Files
DjangoAdmin_Tornado_EleVue/src/common/permission.py
T
gitadmin fda0fe1a53
CI / lint (push) Canceled after 0s
CI / test (push) Canceled after 0s
签入版本
2026-09-16 10:08:07 +08:00

126 lines
4.8 KiB
Python

# +======================================================================
# | 模块: 权限节点解析层
# | 说明: 跨模块共享的权限解析,供访问控制中间件与个人中心使用,
# | 带 Redis 缓存与版本号即时失效
# +======================================================================
"""权限节点解析(跨模块共享,供访问控制中间件与个人中心使用)。
权限节点存储于 menu 表(type=1),非独立业务模块:
- get_permissions_list:按用户解析权限节点列表(带 Redis 缓存)
- invalidate_permission_cache:角色/菜单/用户角色变更时使权限缓存失效
"""
# ----------------------------------------------------------------------
# 导入依赖
# 涵盖:JSON 序列化、SQLAlchemy 条件表达式、数据库会话、日志器、
# Redis 客户端、菜单/角色/用户模型
# ----------------------------------------------------------------------
import json
from sqlalchemy import and_, or_
from core.database import db
from core.logger import logger
from core.redis import redis
from modules.menu.models import Menu
from modules.role.models import Role
from modules.role_menu.models import RoleMenu
from modules.user.models import User
from modules.user_role.models import UserRole
# ============================================================
# 缓存配置
# ============================================================
# 权限缓存 TTL(秒):角色/菜单变更走版本号即时失效,TTL 仅兜底
_PERM_CACHE_TTL = 300
# 权限缓存版本号 key:自增即表示"所有用户的权限缓存失效"
_PERM_VERSION_KEY = "perm:version"
# ======================================================================
# 读取权限缓存版本号
# ======================================================================
def _get_perm_version() -> int:
"""读取权限缓存版本号(Redis 异常时回退 1,仅按 TTL 失效)。
:return: 当前权限缓存版本号
"""
try:
version = redis.get(_PERM_VERSION_KEY)
return int(version) if version else 1
except Exception:
return 1
# ======================================================================
# 失效权限缓存
# ======================================================================
def invalidate_permission_cache() -> None:
"""角色/菜单/用户角色变更后调用:自增版本号使所有权限缓存立即失效。"""
try:
redis.incr(_PERM_VERSION_KEY)
except Exception:
logger.warning("权限缓存失效失败(将按 TTL 自动过期)")
# ======================================================================
# 获取用户权限节点列表
# ======================================================================
def get_permissions_list(user_id) -> list:
"""获取用户权限节点列表(带 Redis 缓存)。超级管理员(id=1)拥有全部节点。
:param user_id: 用户 ID
:return: 权限节点标识列表
"""
user_id = int(user_id)
version = _get_perm_version()
cache_key = f"perm:list:{version}:{user_id}"
# 读缓存
try:
cached = redis.get(cache_key)
if cached is not None:
return json.loads(cached)
except Exception:
pass # 缓存读取异常降级为实时查询
# 实时查询
try:
if user_id == 1:
# 超级管理员:全部权限节点
nodes = db.query(Menu).filter(and_(Menu.is_delete == 0, Menu.type == 1)).all()
else:
# 其他用户:通过角色关联查询权限节点(过滤软删的用户/角色/菜单关联,
# 且仅取状态正常的用户,停用的用户不再授权)
nodes = (
db.query(Menu)
.join(RoleMenu, RoleMenu.menu_id == Menu.id)
.join(UserRole, UserRole.role_id == RoleMenu.role_id)
.join(Role, Role.id == UserRole.role_id)
.join(User, User.id == UserRole.user_id)
.filter(
UserRole.user_id == user_id,
UserRole.is_delete == 0,
RoleMenu.is_delete == 0,
Role.is_delete == 0,
User.is_delete == 0,
User.status == 1,
or_(Menu.type == 1, and_(Menu.type == 0, Menu.permission != "")),
Menu.status == 0,
Menu.is_delete == 0,
)
.distinct()
.all()
)
# 提取权限标识,过滤空值
result = [item.permission for item in nodes if item.permission]
finally:
db.close()
# 写缓存(Redis 异常时静默,仅损失缓存收益)
try:
redis.set(cache_key, json.dumps(result), ex=_PERM_CACHE_TTL)
except Exception:
pass
return result