from dataclasses import asdict from apiflask import APIBlueprint from flask_jwt_extended import jwt_required from iti.applications.common.utils.schema import pagination_fields from iti.applications.extensions import db, sys_log from iti.applications.models.sys_menu import SysMenu from .schemas.role import RoleCreateRequest, RoleQuery, RoleUpdateRequest from iti.applications.models import Role, RoleSchema, sys_user_role, sys_role_menu from iti.applications.common.utils import success, page_schema, page from iti.applications.common.enums import LogType from iti.applications.common import ModelFilter from iti.applications.common.exceptions.biz_exp import BizException from sqlalchemy import select, delete, exists from iti.applications.common.events import RoleEvents, RoleRelEvents from iti.applications.extensions import eventbus from iti.applications.common import permission bp = APIBlueprint("sys_role", __name__, url_prefix="/role", tag="系统.角色管理") @bp.get("/list") @jwt_required() @permission("system:role:list") @bp.input(RoleQuery.Schema(exclude=pagination_fields), location="query") @bp.output(RoleSchema(exclude=["users"], many=True)) def list_role(query_data: RoleQuery): """ 获取角色列表 """ return success(get_list_or_page(query_data)) def get_list_or_page(query_data: RoleQuery): """ 获取角色列表或分页 """ query = select(Role).order_by(Role.sort.asc()) if query_data.name: query = query.filter_by(name=ModelFilter.escape_like(query_data.name)) if query_data.code: query = query.filter_by(code=ModelFilter.escape_like(query_data.code)) if query_data.status: query = query.filter_by(status=query_data.status) if query_data.createdAt and len(query_data.createdAt) >= 2: query = query.filter( Role.created_at.between(query_data.createdAt[0], query_data.createdAt[1]) ) if query_data.updatedAt and len(query_data.updatedAt) >= 2: query = query.filter( Role.updated_at.between(query_data.updatedAt[0], query_data.updatedAt[1]) ) if query_data.page and query_data.size: return db.paginate(query, page=query_data.page, per_page=query_data.size) else: return db.session.scalars(query).all() @bp.get("/page") @jwt_required() @permission("system:role:list") @bp.input(RoleQuery.Schema, location="query") @bp.output(page_schema(RoleSchema(exclude=["users"]))) def page_role(query_data: RoleQuery): """ 分页获取角色列表 """ return page(get_list_or_page(query_data)) @bp.post("") @jwt_required() @permission("system:role:create") @bp.input(RoleCreateRequest.Schema, location="json") @bp.output(RoleSchema) @sys_log( name="创建角色", desc="创建角色", type=LogType.OPERATION, save_db=True, execute_time=True, ) def create_role(json_data: RoleCreateRequest): """ 创建角色 """ role = Role(**asdict(json_data)) db.session.add(role) db.session.commit() return success(role) @bp.put("/") @jwt_required() @permission("system:role:edit") @bp.input(RoleUpdateRequest.Schema(partial=True), location="json") @sys_log( name="更新角色", desc="更新角色", type=LogType.OPERATION, save_db=True, execute_time=True, ) def update_role(id: str, json_data: RoleUpdateRequest): """ 更新角色 """ role = db.session.scalar(select(Role).filter_by(id=id)) if not role: raise BizException("角色不存在") # Code不能重复,排除自己 if db.session.scalar( select(exists().where(Role.code == json_data.code, Role.id != id)) ): raise BizException("角色编码已存在") # 传入则更新,否则不变 menus_updated = False old_menus = role.menus for key, value in asdict(json_data).items(): if key == "permissions" and value is not None: role.menus = db.session.scalars( select(SysMenu) .filter(SysMenu.id.in_(value)) .options(db.noload(SysMenu.children), db.noload(SysMenu.parent)) ).all() menus_updated = True continue if value is not None: setattr(role, key, value) # 提交事务 db.session.commit() # 触发角色事件 eventbus.emit(RoleEvents.ROLE_UPDATED.value, role) if menus_updated: eventbus.emit(RoleRelEvents.ROLE_MENUS_UPDATED.value, role, old_menus) return success() @bp.delete("/") @jwt_required() @permission("system:role:delete") @sys_log( name="删除角色", desc="删除角色", type=LogType.OPERATION, save_db=True, execute_time=True, ) def delete_role(id: str): """ 删除角色 """ role = db.session.scalar(select(Role).filter_by(id=id)) if not role: raise BizException("角色不存在") try: # 删除角色关联关系 db.session.execute(delete(sys_user_role).filter_by(role_id=id)) db.session.execute(delete(sys_role_menu).filter_by(role_id=id)) # 删除角色 db.session.delete(role) db.session.commit() # 触发角色删除事件 eventbus.emit(RoleEvents.ROLE_DELETED.value, role) except Exception as e: db.session.rollback() raise BizException(f"删除角色失败: {e}") return success()