diff --git a/backend/app/api/v1/module_system/dept/service.py b/backend/app/api/v1/module_system/dept/service.py index 9940c4ff..2c61c36f 100644 --- a/backend/app/api/v1/module_system/dept/service.py +++ b/backend/app/api/v1/module_system/dept/service.py @@ -86,9 +86,6 @@ class DeptService: obj = await DeptCRUD(auth).get(code=data.code) if obj: raise CustomException(msg='创建失败,编码已存在') - # 检测循环引用 - if data.parent_id: - await cls._check_circular_reference(auth, data.parent_id, id=None) dept = await DeptCRUD(auth).create(data=data) return DeptOutSchema.model_validate(dept).model_dump() @@ -114,45 +111,9 @@ class DeptService: exist_dept = await DeptCRUD(auth).get(name=data.name) if exist_dept and exist_dept.id != id: raise CustomException(msg='更新失败,部门名称重复') - # 检测循环引用 - if data.parent_id is not None: - await cls._check_circular_reference(auth, data.parent_id, id=id) dept = await DeptCRUD(auth).update(id=id, data=data) return DeptOutSchema.model_validate(dept).model_dump() - @classmethod - async def _check_circular_reference(cls, auth: AuthSchema, parent_id: int, id: int | None = None) -> None: - """ - 检测部门层级循环引用 - - 参数: - - auth (AuthSchema): 认证对象 - - parent_id (int): 父部门ID - - id (int | None): 当前部门ID(用于更新时检测) - - 异常: - - CustomException: 当检测到循环引用时抛出 - """ - if parent_id == id: - raise CustomException(msg='更新失败,不能将部门设置为自己的子部门') - - # 获取所有部门信息用于检测循环引用 - all_depts = await DeptCRUD(auth).get_list_crud() - parent_map = {dept.id: dept.parent_id for dept in all_depts} - - # 检查循环引用 - current_id = parent_id - visited = set() - - while current_id and current_id not in visited: - visited.add(current_id) - if current_id == id: - raise CustomException(msg='更新失败,检测到部门层级循环引用') - current_id = parent_map.get(current_id) - - if current_id == id: - raise CustomException(msg='更新失败,检测到部门层级循环引用') - @classmethod async def delete_dept_service(cls, auth: AuthSchema, ids: list[int]) -> None: """ diff --git a/backend/app/api/v1/module_system/role/model.py b/backend/app/api/v1/module_system/role/model.py index 832d41a1..6bbb8b71 100644 --- a/backend/app/api/v1/module_system/role/model.py +++ b/backend/app/api/v1/module_system/role/model.py @@ -59,13 +59,13 @@ class RoleDeptsModel(MappedBase): ) -class RoleModel(ModelMixin, UserMixin): +class RoleModel(ModelMixin): """ 角色模型 """ __tablename__: str = "sys_role" __table_args__: dict[str, str] = ({'comment': '角色表'}) - __loader_options__: list[str] = ["menus", "depts", "created_by", "updated_by"] + __loader_options__: list[str] = ["menus", "depts"] name: Mapped[str] = mapped_column(String(40), nullable=False, comment="角色名称") code: Mapped[str | None] = mapped_column(String(20), nullable=True, index=True, comment="角色编码") diff --git a/backend/app/api/v1/module_system/role/schema.py b/backend/app/api/v1/module_system/role/schema.py index 1fb0349d..73b27787 100644 --- a/backend/app/api/v1/module_system/role/schema.py +++ b/backend/app/api/v1/module_system/role/schema.py @@ -4,7 +4,7 @@ from fastapi import Query from pydantic import BaseModel, ConfigDict, Field, model_validator, field_validator from app.core.validator import DateTimeStr -from app.core.base_schema import BaseSchema, UserBySchema +from app.core.base_schema import BaseSchema from app.core.validator import role_permission_request_validator from ..dept.schema import DeptOutSchema @@ -50,7 +50,7 @@ class RoleUpdateSchema(RoleCreateSchema): ... -class RoleOutSchema(RoleCreateSchema, BaseSchema, UserBySchema): +class RoleOutSchema(RoleCreateSchema, BaseSchema): """角色信息响应模型""" model_config = ConfigDict(from_attributes=True) @@ -67,16 +67,12 @@ class RoleQueryParam: status: str | None = Query(None, description="是否可用"), created_time: list[DateTimeStr] | None = Query(None, description="创建时间范围", example=["2025-01-01 00:00:00", "2025-12-31 23:59:59"]), updated_time: list[DateTimeStr] | None = Query(None, description="更新时间范围", example=["2025-01-01 00:00:00", "2025-12-31 23:59:59"]), - created_id: int | None = Query(None, description="创建人"), - updated_id: int | None = Query(None, description="更新人"), ) -> None: # 模糊查询字段 self.name = ("like", name) # 精确查询字段 - self.created_id = created_id - self.updated_id = updated_id self.status = status # 时间范围查询 diff --git a/backend/app/api/v1/module_system/user/model.py b/backend/app/api/v1/module_system/user/model.py index 0ac6c17d..0bd4833a 100644 --- a/backend/app/api/v1/module_system/user/model.py +++ b/backend/app/api/v1/module_system/user/model.py @@ -104,3 +104,22 @@ class UserModel(ModelMixin, UserMixin): back_populates="users", lazy="selectin" ) + + # 覆盖 UserMixin 的关系定义,显式指定 foreign_keys 避免自引用混淆 + created_by: Mapped["UserModel | None"] = relationship( + "UserModel", + foreign_keys="UserModel.created_id", + remote_side="UserModel.id", + lazy="selectin", + uselist=False, + viewonly=True # 防止级联操作 + ) + + updated_by: Mapped["UserModel | None"] = relationship( + "UserModel", + foreign_keys="UserModel.updated_id", + remote_side="UserModel.id", + lazy="selectin", + uselist=False, + viewonly=True # 防止级联操作 + ) diff --git a/backend/app/core/base_crud.py b/backend/app/core/base_crud.py index c9e50fa4..2ff36a94 100644 --- a/backend/app/core/base_crud.py +++ b/backend/app/core/base_crud.py @@ -5,7 +5,7 @@ from typing import TypeVar, Sequence, Generic, Dict, Any, List, Optional, Type, from sqlalchemy.sql.elements import ColumnElement from sqlalchemy.orm import selectinload from sqlalchemy.engine import Result -from sqlalchemy import asc, func, select, delete, Select, desc, update, or_, and_ +from sqlalchemy import asc, func, select, delete, Select, desc, update from sqlalchemy import inspect as sa_inspect from app.core.base_model import MappedBase @@ -160,8 +160,16 @@ class CRUDBase(Generic[ModelType, CreateSchemaType, UpdateSchemaType]): sql = sql.options(opt) sql = await self.__filter_permissions(sql) - # 获取总数 - count_sql = select(func.count()).select_from(self.model) + # 优化count查询:使用主键计数而非全表扫描 + mapper = sa_inspect(self.model) + pk_cols = list(getattr(mapper, "primary_key", [])) + if pk_cols: + # 使用主键的第一列进行计数(主键必定非NULL,性能更好) + count_sql = select(func.count(pk_cols[0])).select_from(self.model) + else: + # 降级方案:使用count(*) + count_sql = select(func.count()).select_from(self.model) + if conditions: count_sql = count_sql.where(*conditions) count_sql = await self.__filter_permissions(count_sql) @@ -244,6 +252,14 @@ class CRUDBase(Generic[ModelType, CreateSchemaType, UpdateSchemaType]): await self.auth.db.flush() await self.auth.db.refresh(obj) + + # 权限二次确认:flush后再次验证对象仍在权限范围内 + # 防止并发修改导致的权限逃逸(如其他事务修改了created_id) + verify_obj = await self.get(id=id) + if not verify_obj: + # 对象已被删除或权限已失效 + raise CustomException(msg="更新失败,对象不存在或无权限访问") + return obj except Exception as e: raise CustomException(msg=f"更新失败: {str(e)}") @@ -259,13 +275,27 @@ class CRUDBase(Generic[ModelType, CreateSchemaType, UpdateSchemaType]): - CustomException: 删除失败时抛出异常 """ try: + # 先查询确认权限,避免删除无权限的数据 + objs = await self.list(search={"id": ("in", ids)}) + accessible_ids = [obj.id for obj in objs] + + # 检查是否所有ID都有权限访问 + inaccessible_count = len(ids) - len(accessible_ids) + if inaccessible_count > 0: + raise CustomException(msg=f"无权限删除{inaccessible_count}条数据") + + if not accessible_ids: + return # 没有可删除的数据 + mapper = sa_inspect(self.model) pk_cols = list(getattr(mapper, "primary_key", [])) if not pk_cols: raise CustomException(msg="模型缺少主键,无法删除") if len(pk_cols) > 1: raise CustomException(msg="暂不支持复合主键的批量删除") - sql = delete(self.model).where(pk_cols[0].in_(ids)) + + # 只删除有权限的数据 + sql = delete(self.model).where(pk_cols[0].in_(accessible_ids)) await self.auth.db.execute(sql) await self.auth.db.flush() except Exception as e: @@ -297,15 +327,31 @@ class CRUDBase(Generic[ModelType, CreateSchemaType, UpdateSchemaType]): - CustomException: 更新失败时抛出异常 """ try: + # 先查询确认权限,避免更新无权限的数据 + objs = await self.list(search={"id": ("in", ids)}) + accessible_ids = [obj.id for obj in objs] + + # 检查是否所有ID都有权限访问 + inaccessible_count = len(ids) - len(accessible_ids) + if inaccessible_count > 0: + raise CustomException(msg=f"无权限更新{inaccessible_count}条数据") + + if not accessible_ids: + return # 没有可更新的数据 + mapper = sa_inspect(self.model) pk_cols = list(getattr(mapper, "primary_key", [])) if not pk_cols: raise CustomException(msg="模型缺少主键,无法更新") if len(pk_cols) > 1: raise CustomException(msg="暂不支持复合主键的批量更新") - sql = update(self.model).where(pk_cols[0].in_(ids)).values(**kwargs) + + # 只更新有权限的数据 + sql = update(self.model).where(pk_cols[0].in_(accessible_ids)).values(**kwargs) await self.auth.db.execute(sql) await self.auth.db.flush() + except CustomException: + raise except Exception as e: raise CustomException(msg=f"批量更新失败: {str(e)}") @@ -356,8 +402,16 @@ class CRUDBase(Generic[ModelType, CreateSchemaType, UpdateSchemaType]): conditions.append(attr.between(val[0], val[1])) elif seq == "!=" and val: conditions.append(attr != val) - elif seq in [">", ">=", "<=", "=="] and val: - conditions.append(getattr(attr, seq.replace("==", "__eq__"))(val)) + elif seq == ">" and val: + conditions.append(attr > val) + elif seq == ">=" and val: + conditions.append(attr >= val) + elif seq == "<" and val: + conditions.append(attr < val) + elif seq == "<=" and val: + conditions.append(attr <= val) + elif seq == "==" and val: + conditions.append(attr == val) else: conditions.append(attr == value) return conditions diff --git a/backend/app/core/base_model.py b/backend/app/core/base_model.py index ec614c2e..027acecf 100644 --- a/backend/app/core/base_model.py +++ b/backend/app/core/base_model.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- +from typing import Optional from datetime import datetime from sqlalchemy import DateTime, String, Integer, Text, ForeignKey from sqlalchemy.orm import Mapped, mapped_column, DeclarativeBase, declared_attr, relationship @@ -93,29 +94,25 @@ class UserMixin(MappedBase): ) @declared_attr - def created_by(cls) -> Mapped["UserModel"]: + def created_by(cls) -> Mapped[Optional["UserModel"]]: """ 创建人关联关系(延迟加载,避免循环依赖) """ return relationship( "UserModel", - primaryjoin=f"{cls.__name__}.created_id == UserModel.id", lazy="selectin", - foreign_keys=lambda: [cls.created_id], - viewonly=True, + foreign_keys=lambda: cls.created_id, uselist=False ) @declared_attr - def updated_by(cls) -> Mapped["UserModel"]: + def updated_by(cls) -> Mapped[Optional["UserModel"]]: """ 更新人关联关系(延迟加载,避免循环依赖) """ return relationship( "UserModel", - primaryjoin=f"{cls.__name__}.updated_id == UserModel.id", lazy="selectin", - foreign_keys=lambda: [cls.updated_id], - viewonly=True, + foreign_keys=lambda: cls.updated_id, uselist=False ) diff --git a/backend/app/core/base_schema.py b/backend/app/core/base_schema.py index 24ba16c3..600fa621 100644 --- a/backend/app/core/base_schema.py +++ b/backend/app/core/base_schema.py @@ -9,9 +9,9 @@ class UserInfoSchema(BaseModel): """用户信息模型""" model_config = ConfigDict(from_attributes=True) - id: int = Field(description="用户ID") - name: str = Field(description="用户姓名") - username: str = Field(description="用户名") + id: int | None = Field(default=None, description="用户ID") + name: str | None = Field(default=None, description="用户姓名") + username: str | None = Field(default=None, description="用户名") class CommonSchema(BaseModel): diff --git a/backend/app/core/dependencies.py b/backend/app/core/dependencies.py index b1e8ec1b..2a27cc11 100644 --- a/backend/app/core/dependencies.py +++ b/backend/app/core/dependencies.py @@ -93,7 +93,7 @@ async def get_current_user( username=username, preload=[ "dept", - selectinload(UserModel.roles).selectinload(RoleModel.created_by), + selectinload(UserModel.roles), "positions", "created_by" ] diff --git a/backend/app/core/logger.py b/backend/app/core/logger.py index 8402e0c4..197f6112 100644 --- a/backend/app/core/logger.py +++ b/backend/app/core/logger.py @@ -2,12 +2,16 @@ import logging import sys +import atexit from typing_extensions import override from loguru import logger from app.config.path_conf import LOG_DIR from app.config.setting import settings +# 全局变量记录日志处理器ID +_logger_handlers = [] + class InterceptHandler(logging.Handler): """ @@ -39,6 +43,22 @@ class InterceptHandler(logging.Handler): ) +def cleanup_logging(): + """ + 清理日志资源 + 在程序退出时调用,确保所有日志处理器被正确关闭 + """ + global _logger_handlers + + for handler_id in _logger_handlers: + try: + logger.remove(handler_id) + except Exception: + pass + + _logger_handlers.clear() + + def setup_logging(): """ 配置日志系统 @@ -47,8 +67,10 @@ def setup_logging(): 1. 控制台彩色输出 2. 文件日志轮转 3. 错误日志单独存储 - 4. 异步日志记录 + 4. 智能异步策略:开发环境同步(避免reload资源泄漏),生产环境异步(高性能) """ + global _logger_handlers + # 添加上下文信息 _ = logger.configure(extra={"app_name": "FastapiAdmin"}) # 步骤1:移除默认处理器 @@ -66,16 +88,20 @@ def setup_logging(): "{message}" ) + # 智能选择异步策略:开发环境禁用异步(避免reload时资源泄漏),生产环境启用异步(提升性能) + use_async = not settings.DEBUG + # 步骤3:配置控制台输出 - _ = logger.add( + handler_id = logger.add( sys.stdout, format=log_format, level="DEBUG" if settings.DEBUG else "INFO", - enqueue=True, # 启用异步写入 + enqueue=use_async, # 开发同步,生产异步 backtrace=True, # 显示完整的异常回溯 diagnose=True, # 显示变量值等诊断信息 colorize=True # 启用彩色输出 ) + _logger_handlers.append(handler_id) # 步骤4:创建日志目录 log_dir = LOG_DIR @@ -83,7 +109,7 @@ def setup_logging(): log_dir.mkdir(parents=True, exist_ok=True) # 步骤5:配置常规日志文件 - _ = logger.add( + handler_id = logger.add( str(log_dir / "info.log"), format=log_format, level="INFO", @@ -91,11 +117,12 @@ def setup_logging(): retention=30, # 日志保留天数,超过此天数的日志文件将被自动清理 compression="gz", encoding="utf-8", - enqueue=True + enqueue=use_async # 开发同步,生产异步 ) + _logger_handlers.append(handler_id) # 步骤6:配置错误日志文件 - _ = logger.add( + handler_id = logger.add( str(log_dir / "error.log"), format=log_format, level="ERROR", @@ -103,18 +130,23 @@ def setup_logging(): retention=30, # 日志保留天数,超过此天数的日志文件将被自动清理 compression="gz", encoding="utf-8", - enqueue=True, + enqueue=use_async, # 开发同步,生产异步 backtrace=True, diagnose=True ) + _logger_handlers.append(handler_id) # 步骤7:配置标准库日志 logging.basicConfig(handlers=[InterceptHandler()], level="DEBUG" if settings.DEBUG else "INFO", force=True) logger_name_list = [name for name in logging.root.manager.loggerDict] + # 步骤8:配置第三方库日志 for logger_name in logger_name_list: _logger = logging.getLogger(logger_name) _logger.handlers = [InterceptHandler()] _logger.propagate = False + + # 注册退出清理函数 + atexit.register(cleanup_logging) log = logger \ No newline at end of file diff --git a/backend/app/core/permission.py b/backend/app/core/permission.py index f8308bf1..e86a37e9 100644 --- a/backend/app/core/permission.py +++ b/backend/app/core/permission.py @@ -2,10 +2,9 @@ from typing import Any from sqlalchemy.sql.elements import ColumnElement -from sqlalchemy import select, and_ +from sqlalchemy import select from app.api.v1.module_system.user.model import UserModel from app.api.v1.module_system.dept.model import DeptModel -from app.api.v1.module_system.role.model import RoleModel from app.api.v1.module_system.auth.schema import AuthSchema from app.utils.common_util import get_child_id_map, get_child_recursion @@ -59,7 +58,11 @@ class Permission: 3. 本部门及以下数据权限 - 可以查看本部门及所有子部门的数据 4. 全部数据权限 - 可以查看所有数据 5. 自定义数据权限 - 通过role_dept_relation表定义可访问的部门列表 - 构造权限过滤表达式,返回None表示不限制。 + + 权限处理原则: + - 多个角色的权限取并集(最宽松原则) + - 优先级:全部数据 > 部门权限(2、3、5的并集)> 仅本人 + - 构造权限过滤表达式,返回None表示不限制 """ # 如果不需要检查数据权限,则不限制 if not self.auth.user: @@ -69,89 +72,88 @@ class Permission: if not self.auth.check_data_scope: return None - # 如果模型没有创建人creator_id字段,则不限制 - if not hasattr(self.model, "creator_id"): + # 如果模型没有创建人created_id字段,则不限制 + if not hasattr(self.model, "created_id"): return None # 超级管理员可以查看所有数据 if self.auth.user.is_superuser: return None - # 如果用户没有部门或角色,则只能查看自己的数据 - if not getattr(self.auth.user, "dept_id", None) or not getattr(self.auth.user, "roles", None): - creator_id_attr = getattr(self.model, "creator_id", None) - if creator_id_attr is not None: - return creator_id_attr == self.auth.user.id + # 如果用户没有角色,则只能查看自己的数据 + roles = getattr(self.auth.user, "roles", []) or [] + if not roles: + created_id_attr = getattr(self.model, "created_id", None) + if created_id_attr is not None: + return created_id_attr == self.auth.user.id return None # 获取用户所有角色的权限范围 data_scopes = set() - dept_ids = set() - roles = getattr(self.auth.user, "roles", []) or [] + custom_dept_ids = set() # 自定义权限(data_scope=5)关联的部门ID集合 for role in roles: - # 角色的部门集合 - if hasattr(role, 'depts') and role.depts: - for dept in role.depts: - dept_ids.add(dept.id) data_scopes.add(role.data_scope) + # 收集自定义权限(data_scope=5)关联的部门ID + if role.data_scope == self.DATA_SCOPE_CUSTOM and hasattr(role, 'depts') and role.depts: + for dept in role.depts: + custom_dept_ids.add(dept.id) - # 如果有全部数据权限,直接返回 + # 权限优先级处理:全部数据权限最高优先级 if self.DATA_SCOPE_ALL in data_scopes: - # 全部数据权限 return None - # 如果有自定义数据权限且部门ID存在,优先处理 - if self.DATA_SCOPE_CUSTOM in data_scopes and dept_ids: - # 自定义数据权限 - creator_rel = getattr(self.model, "creator", None) - if hasattr(UserModel, 'dept_id') and creator_rel is not None: - return creator_rel.has(getattr(UserModel, 'dept_id').in_(list(dept_ids))) - else: - creator_id_attr = getattr(self.model, "creator_id", None) - if creator_id_attr is not None: - return creator_id_attr == self.auth.user.id - return None - - # 处理其他数据权限范围 - dept_id_val = getattr(self.auth.user, "dept_id", None) + # 收集所有可访问的部门ID(2、3、5权限的并集) + accessible_dept_ids = set() + user_dept_id = getattr(self.auth.user, "dept_id", None) + # 处理自定义数据权限(5) + if self.DATA_SCOPE_CUSTOM in data_scopes: + accessible_dept_ids.update(custom_dept_ids) + + # 处理本部门数据权限(2) + if self.DATA_SCOPE_DEPT in data_scopes: + if user_dept_id is not None: + accessible_dept_ids.add(user_dept_id) + + # 处理本部门及以下数据权限(3) + if self.DATA_SCOPE_DEPT_AND_CHILD in data_scopes: + if user_dept_id is not None: + try: + # 查询所有部门并递归获取子部门 + dept_sql = select(DeptModel) + dept_result = await self.auth.db.execute(dept_sql) + dept_objs = dept_result.scalars().all() + id_map = get_child_id_map(dept_objs) + # get_child_recursion返回的结果已包含自身ID和所有子部门ID + dept_with_children_ids = get_child_recursion(id=user_dept_id, id_map=id_map) + accessible_dept_ids.update(dept_with_children_ids) + except Exception: + # 查询失败时降级到本部门 + accessible_dept_ids.add(user_dept_id) + + # 如果有部门权限(2、3、5任一),使用部门过滤 + if accessible_dept_ids: + creator_rel = getattr(self.model, "created_by", None) + # 优先使用关系过滤(性能更好) + if creator_rel is not None and hasattr(UserModel, 'dept_id'): + return creator_rel.has(getattr(UserModel, 'dept_id').in_(list(accessible_dept_ids))) + # 降级方案:如果模型没有created_by关系但有created_id,则只能查看自己的数据 + else: + created_id_attr = getattr(self.model, "created_id", None) + if created_id_attr is not None: + return created_id_attr == self.auth.user.id + return None + + # 处理仅本人数据权限(1) if self.DATA_SCOPE_SELF in data_scopes: - # 仅本人数据 - creator_id_attr = getattr(self.model, "creator_id", None) - if creator_id_attr is not None: - return creator_id_attr == self.auth.user.id + created_id_attr = getattr(self.model, "created_id", None) + if created_id_attr is not None: + return created_id_attr == self.auth.user.id return None - if self.DATA_SCOPE_DEPT in data_scopes and dept_id_val is not None: - # 本部门数据 - dept_ids.add(dept_id_val) - - if self.DATA_SCOPE_DEPT_AND_CHILD in data_scopes and dept_id_val is not None: - # 本部门及以下数据(查询所有部门并递归) - dept_sql = select(DeptModel) - dept_result = await self.auth.db.execute(dept_sql) - dept_objs = dept_result.scalars().all() - id_map = get_child_id_map(dept_objs) - dept_child_ids = get_child_recursion(id=dept_id_val, id_map=id_map) - dept_ids.add(dept_id_val) # 包含本部门 - for child_id in dept_child_ids: - dept_ids.add(child_id) - - # 处理2、3汇总的数据权限 - if (self.DATA_SCOPE_DEPT in data_scopes or self.DATA_SCOPE_DEPT_AND_CHILD in data_scopes) and dept_ids: - # 使用关系creator进行筛选(若存在),否则回退到仅本人数据 - creator_rel = getattr(self.model, "creator", None) - if hasattr(UserModel, 'dept_id') and creator_rel is not None and dept_ids: - return creator_rel.has(getattr(UserModel, 'dept_id').in_(list(dept_ids))) - else: - creator_id_attr = getattr(self.model, "creator_id", None) - if creator_id_attr is not None: - return creator_id_attr == self.auth.user.id - return None - - # 默认情况下,只能查看自己的数据 - creator_id_attr = getattr(self.model, "creator_id", None) - if creator_id_attr is not None: - return creator_id_attr == self.auth.user.id + # 默认情况:如果用户有角色但没有任何有效权限范围,只能查看自己的数据 + created_id_attr = getattr(self.model, "created_id", None) + if created_id_attr is not None: + return created_id_attr == self.auth.user.id return None \ No newline at end of file diff --git a/backend/app/scripts/data/sys_role.json b/backend/app/scripts/data/sys_role.json index 50ba4569..e60818e7 100644 --- a/backend/app/scripts/data/sys_role.json +++ b/backend/app/scripts/data/sys_role.json @@ -5,18 +5,6 @@ "status": "0", "order": 1, "data_scope": 4, - "description": "初始化角色", - "created_id": 1, - "updated_id": 1 - }, - { - "name": "普通角色", - "code": "COMMON", - "status": "0", - "order": 1, - "data_scope": 3, - "description": "初始化角色", - "created_id": 1, - "updated_id": 1 + "description": "初始化角色" } ] \ No newline at end of file diff --git a/backend/app/scripts/initialize.py b/backend/app/scripts/initialize.py index 758a3392..3f7ea111 100644 --- a/backend/app/scripts/initialize.py +++ b/backend/app/scripts/initialize.py @@ -32,10 +32,10 @@ class InitializeData: MenuModel, ParamsModel, DeptModel, + RoleModel, DictTypeModel, DictDataModel, UserModel, - RoleModel, UserRolesModel, ] diff --git a/backend/env/.env.dev b/backend/env/.env.dev index 6c4ef152..94987f52 100644 --- a/backend/env/.env.dev +++ b/backend/env/.env.dev @@ -22,7 +22,7 @@ DESCRIPTION = "该项目是一个基于python的web服务框架,基于fastapi DEMO_ENABLE = False # 数据库配置 -DATABASE_TYPE = "postgres" # mysql、postgres +DATABASE_TYPE = "postgres" # mysql、postgres # 数据库配置 DATABASE_HOST = "localhost" diff --git a/backend/main.py b/backend/main.py index 0782cbfa..8e92235a 100755 --- a/backend/main.py +++ b/backend/main.py @@ -84,6 +84,9 @@ def run(env: Annotated[EnvironmentEnum, typer.Option("--env", help="运行环境 display_shutdown_info() except Exception as e: raise + finally: + from app.core.logger import cleanup_logging + cleanup_logging() @fastapiadmin_cli.command(name="revision", help="生成新的 Alembic 迁移脚本, 运行 python main.py revision --env=dev") def revision(env: Annotated[EnvironmentEnum, typer.Option("--env", help="运行环境 (dev, prod)")] = EnvironmentEnum.DEV) -> None: diff --git a/frontend/src/api/module_system/role.ts b/frontend/src/api/module_system/role.ts index fe6dc1fb..48c6c5e9 100644 --- a/frontend/src/api/module_system/role.ts +++ b/frontend/src/api/module_system/role.ts @@ -73,8 +73,6 @@ export default RoleAPI; export interface TablePageQuery extends PageQuery { name?: string; status?: string; - created_id?: number; - updated_id?: number; created_time?: string[]; updated_time?: string[]; } @@ -87,8 +85,6 @@ export interface RoleTable extends BaseType { data_scope?: number; menus?: permissionMenuType[]; depts?: permissionDeptType[]; - created_by?: creatorType; - updated_by?: updatorType; } export interface RoleForm extends BaseFormType { diff --git a/frontend/src/views/module_gencode/demo/index.vue b/frontend/src/views/module_gencode/demo/index.vue index 14332993..757f1baf 100644 --- a/frontend/src/views/module_gencode/demo/index.vue +++ b/frontend/src/views/module_gencode/demo/index.vue @@ -282,6 +282,16 @@ {{ scope.row.created_by?.name }} + + + - - - - - {{ detailFormData.created_by?.name }} - - - {{ detailFormData.updated_by?.name }} - {{ detailFormData.created_time }} @@ -503,7 +490,6 @@ defineOptions({ import { ElMessage, ElMessageBox } from "element-plus"; import RoleAPI, { RoleTable, RoleForm, TablePageQuery } from "@/api/module_system/role"; import { useUserStore } from "@/store"; -import UserTableSelect from "@/views/module_system/user/components/UserTableSelect.vue"; import ExportModal from "@/components/CURD/ExportModal.vue"; import type { IContentConfig } from "@/components/CURD/types"; import { QuestionFilled, ArrowUp, ArrowDown } from "@element-plus/icons-vue"; @@ -551,8 +537,6 @@ const queryFormData = reactive({ page_size: 10, name: "", status: undefined, - created_id: undefined, - updated_id: undefined, created_time: undefined, updated_time: undefined, }); @@ -606,11 +590,6 @@ async function handleQuery() { loadingData(); } -// 选择创建人后触发查询 -function handleConfirm() { - handleQuery(); -} - // 行复选框选中项变化 async function handleSelectionChange(selection: any) { selectIds.value = selection.map((item: any) => item.id);