Files
KCGL/inventory-backend/app/services/permission_service.py

204 lines
7.4 KiB
Python
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from app.models.system import SysMenu, SysElement, SysRolePermission
from app.extensions import db
from sqlalchemy.exc import SQLAlchemyError
class PermissionService:
@staticmethod
def get_permission_tree():
"""
获取完整的权限树(菜单嵌套菜单 + 菜单包含元素)
供前端权限配置页面展示
"""
# 1. 获取所有菜单 (按 parent_id 和 sort_order 排序,保证父子处理顺序)
menus = SysMenu.query.order_by(SysMenu.parent_id, SysMenu.sort_order).all()
# 2. 获取所有元素
elements = SysElement.query.all()
# --- 核心逻辑:构建树形结构 ---
# 3. 创建一个 lookup 字典,方便通过 ID 查找菜单节点
# 同时将 SQLAlchemy 对象转为字典,方便后续操作
menu_map = {}
for m in menus:
m_dict = m.to_dict()
m_dict['children'] = [] # 初始化 children
menu_map[m.id] = m_dict
# 4. 创建 code 到 id 的映射,用于把 element 挂载到 menu 上
# 因为 SysElement 关联的是 menu_code而不是 menu_id
code_to_id = {m.code: m.id for m in menus}
# 5. 将元素 (Elements) 挂载到对应的菜单 (Menu) 下
for el in elements:
# 找到该元素所属菜单的 ID
parent_menu_id = code_to_id.get(el.menu_code)
if parent_menu_id and parent_menu_id in menu_map:
el_dict = el.to_dict()
# 标记类型为 element前端 transformData 需要用到
el_dict['type'] = 'element'
menu_map[parent_menu_id]['children'].append(el_dict)
# 6. 将子菜单挂载到父菜单下,并构建最终的树
tree_data = []
for m in menus:
current_node = menu_map[m.id]
if m.parent_id == 0 or m.parent_id is None:
# 如果是顶级菜单,直接放入结果集
tree_data.append(current_node)
else:
# 如果是子菜单,找到它的父级,把它塞进父级的 children 里
if m.parent_id in menu_map:
menu_map[m.parent_id]['children'].append(current_node)
else:
# 如果找不到父级(比如父级被删了),为了防止数据丢失,暂时作为顶级显示
tree_data.append(current_node)
return tree_data
@staticmethod
def get_role_permissions(role_code):
"""获取指定角色拥有的所有权限Code"""
try:
# === 新增逻辑:超级管理员上帝模式 ===
if role_code == 'SUPER_ADMIN':
# 直接获取所有菜单和元素,无视配置表
all_menus = [m.code for m in SysMenu.query.all()]
all_elements = [e.code for e in SysElement.query.all()]
return {
'menus': all_menus,
'elements': all_elements
}
# =================================
perms = SysRolePermission.query.filter_by(role_code=role_code).all()
menu_codes = []
element_codes = []
for p in perms:
# 这里假设你的数据库存的是 target_code
if p.type == 'menu':
menu_codes.append(p.target_code)
else:
element_codes.append(p.target_code)
# 前端 handleRoleSelect 会合并这两个数组,所以分开返回没问题
return {
'menus': menu_codes,
'elements': element_codes
}
except Exception as e:
# 记录日志或处理错误
print(f"Error fetching role permissions: {e}")
return {'menus': [], 'elements': []}
@staticmethod
def assign_permissions(role_code, permissions):
"""
保存角色的权限
permissions: 前端传来的 list混合了 menu_code 和 element_code
"""
if not role_code:
raise ValueError("角色代码不能为空")
session = db.session
try:
# 1. 开启事务 (Flask-SQLAlchemy 自动管理,但明确逻辑更好)
# 2. 删除该角色旧的所有权限
SysRolePermission.query.filter_by(role_code=role_code).delete()
# 3. 准备新数据
if permissions:
# 3.1 去重
unique_codes = set(permissions)
# 3.2 预加载所有 Menu Code用于区分是 Menu 还是 Element
# 这一步很重要,因为 SysRolePermission 表需要 type 字段
all_menu_codes = {res[0] for res in session.query(SysMenu.code).all()}
new_records = []
for code in unique_codes:
if not code: continue
# 判断类型:如果 code 存在于菜单表中,就是 menu否则就是 element
p_type = 'menu' if code in all_menu_codes else 'element'
new_records.append(SysRolePermission(
role_code=role_code,
target_code=code,
type=p_type
))
# 3.3 批量插入
if new_records:
session.add_all(new_records)
# 4. 提交
session.commit()
return True
except SQLAlchemyError as e:
session.rollback()
raise e
except Exception as e:
session.rollback()
raise e
@staticmethod
def init_audit_menu():
"""
初始化审计日志菜单和超级管理员权限
防重复:只插入不存在的记录
"""
try:
# 1. 检查并创建审计日志菜单
menu_code = 'system_audit'
existing_menu = SysMenu.query.filter_by(code=menu_code).first()
if not existing_menu:
new_menu = SysMenu(
parent_id=0,
name='审计日志',
code=menu_code,
path='/system/audit',
sort_order=110,
is_visible=True
)
db.session.add(new_menu)
db.session.flush() # 获取新插入的 ID
print(f"✅ 审计日志菜单已创建 (code: {menu_code})")
else:
print(f" 审计日志菜单已存在 (code: {menu_code})")
# 2. 为超级管理员赋予审计日志菜单权限
role_code = 'SUPER_ADMIN'
existing_perm = SysRolePermission.query.filter_by(
role_code=role_code,
target_code=menu_code
).first()
if not existing_perm:
new_perm = SysRolePermission(
role_code=role_code,
target_code=menu_code,
type='menu'
)
db.session.add(new_perm)
print(f"✅ 超级管理员已赋予审计日志权限")
else:
print(f" 超级管理员已拥有审计日志权限")
# 3. 提交
db.session.commit()
return True
except Exception as e:
db.session.rollback()
print(f"❌ 初始化审计日志菜单失败: {str(e)}")
raise e