From bb4b4357d4028b00d75b4552e4b613b8efb7e182 Mon Sep 17 00:00:00 2001 From: smock Date: Fri, 13 Feb 2026 12:15:29 +0800 Subject: [PATCH 1/6] =?UTF-8?q?=E6=94=AF=E6=8C=81=E6=89=8B=E5=8A=A8?= =?UTF-8?q?=E5=AF=BC=E5=85=A5=E8=B7=AF=E7=94=B1=E3=80=81fix=20bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 +- ruoyi-fastapi-backend/.env.dev | 2 ++ ruoyi-fastapi-backend/.env.dockermy | 2 ++ ruoyi-fastapi-backend/.env.dockerpg | 2 ++ ruoyi-fastapi-backend/.env.prod | 2 ++ ruoyi-fastapi-backend/common/router.py | 43 +++++++++++++++++++++++++- ruoyi-fastapi-backend/config/env.py | 1 + 7 files changed, 52 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 220a6961d..70cb8f1d1 100644 --- a/.gitignore +++ b/.gitignore @@ -85,7 +85,7 @@ ipython_config.py # pyenv # For a library or package, you might want to ignore these files since the code is # intended to run in multiple environments; otherwise, check them in: -# .python-version +.python-version # pipenv # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. diff --git a/ruoyi-fastapi-backend/.env.dev b/ruoyi-fastapi-backend/.env.dev index 0f22dfe18..98d8429a5 100644 --- a/ruoyi-fastapi-backend/.env.dev +++ b/ruoyi-fastapi-backend/.env.dev @@ -25,6 +25,8 @@ APP_DEMO_MODE = false APP_DISABLE_SWAGGER = false # 应用是否禁用ReDoc文档 APP_DISABLE_REDOC = false +# 应用是否启用手动导入路由(默认为自动扫描routers目录) +APP_MANUAL_IMPORT_ROUTERS = false # -------- Jwt配置 -------- # Jwt秘钥 diff --git a/ruoyi-fastapi-backend/.env.dockermy b/ruoyi-fastapi-backend/.env.dockermy index 85f1d280e..0c4ec8adc 100644 --- a/ruoyi-fastapi-backend/.env.dockermy +++ b/ruoyi-fastapi-backend/.env.dockermy @@ -25,6 +25,8 @@ APP_DEMO_MODE = false APP_DISABLE_SWAGGER = true # 应用是否禁用ReDoc文档 APP_DISABLE_REDOC = true +# 应用是否启用手动导入路由(默认为自动扫描routers目录) +APP_MANUAL_IMPORT_ROUTERS = false # -------- Jwt配置 -------- # Jwt秘钥 diff --git a/ruoyi-fastapi-backend/.env.dockerpg b/ruoyi-fastapi-backend/.env.dockerpg index 5d531545e..19f8d7a00 100644 --- a/ruoyi-fastapi-backend/.env.dockerpg +++ b/ruoyi-fastapi-backend/.env.dockerpg @@ -25,6 +25,8 @@ APP_SAME_TIME_LOGIN = true APP_DISABLE_SWAGGER = true # 应用是否禁用ReDoc文档 APP_DISABLE_REDOC = true +# 应用是否启用手动导入路由(默认为自动扫描routers目录) +APP_MANUAL_IMPORT_ROUTERS = false # -------- Jwt配置 -------- # Jwt秘钥 diff --git a/ruoyi-fastapi-backend/.env.prod b/ruoyi-fastapi-backend/.env.prod index 9d1bf5ba5..04266f186 100644 --- a/ruoyi-fastapi-backend/.env.prod +++ b/ruoyi-fastapi-backend/.env.prod @@ -25,6 +25,8 @@ APP_DEMO_MODE = false APP_DISABLE_SWAGGER = true # 应用是否禁用ReDoc文档 APP_DISABLE_REDOC = true +# 应用是否启用手动导入路由(默认为自动扫描routers目录) +APP_MANUAL_IMPORT_ROUTERS = false # -------- Jwt配置 -------- # Jwt秘钥 diff --git a/ruoyi-fastapi-backend/common/router.py b/ruoyi-fastapi-backend/common/router.py index f7106c5f9..064443353 100644 --- a/ruoyi-fastapi-backend/common/router.py +++ b/ruoyi-fastapi-backend/common/router.py @@ -16,6 +16,8 @@ from starlette.types import ASGIApp, Lifespan from typing_extensions import deprecated +from config.env import AppConfig + class APIRouterPro(APIRouter): """ @@ -308,7 +310,41 @@ def _find_controller_files(self) -> list[str]: :return: py文件路径列表 """ pattern = os.path.join(self.project_root, '*', 'controller', '[!_]*.py') - return sorted(glob.glob(pattern)) + files = glob.glob(pattern) + # 去重并保持顺序 VSCode 调试模式 + Uvicorn 热重载 环境下,可能返回重复的文件路径。 :os.path.join(self.project_root, '*', 'controller', '[!_]*.py') 中的 * 可能匹配到符号链接指向的同一物理目录,导致同一个文件通过不同路径(真实路径 vs 链接路径)被重复匹配。 + return sorted(dict.fromkeys(files)) + + def _exclude_controller_file_and_manual_import_routers( + self, all_file_names: list[str] + ) -> tuple[list[str], list[tuple[str, APIRouter]]]: + """ + 排除指定的controller文件,并手动导入路由实例(性能比较烂的朋友在debug+reload的时候可以快一点) + + :param file_name: 要排除的controller文件名 + :return: 路由实例列表 + """ + exclude_file_names = [ + 'ai_chat_controller.py', + 'ai_model_controller.py', + ] + + if not exclude_file_names: + return all_file_names, [] + + all_file_names_exclude = [] # 过滤后的文件名列表 + all_file_names_exclude = [ + fname for fname in all_file_names if not any(exclude in fname for exclude in exclude_file_names) + ] + + routers = [] + # 手动导入路由实例 + from module_ai.controller import ai_chat_controller, ai_model_controller # noqa: PLC0415 + + # 这个文件太大了,电脑性能很差的用户动态导入会很久5s+ + routers.append(('module_ai.controller.ai_chat_controller', ai_chat_controller.ai_chat_controller)) + routers.append(('module_ai.controller.ai_model_controller', ai_model_controller.ai_model_controller)) + + return all_file_names_exclude, routers def _import_module_and_get_routers(self, controller_files: list[str]) -> list[tuple[str, APIRouter]]: """ @@ -318,6 +354,11 @@ def _import_module_and_get_routers(self, controller_files: list[str]) -> list[tu :return: 路由实例列表 """ routers = [] + + if AppConfig.app_manual_import_routers: + controller_files, manual_routers = self._exclude_controller_file_and_manual_import_routers(controller_files) + routers.extend(manual_routers) + for file_path in controller_files: # 计算模块路径 relative_path = os.path.relpath(file_path, self.project_root) diff --git a/ruoyi-fastapi-backend/config/env.py b/ruoyi-fastapi-backend/config/env.py index 89baec580..d1d961ab6 100644 --- a/ruoyi-fastapi-backend/config/env.py +++ b/ruoyi-fastapi-backend/config/env.py @@ -27,6 +27,7 @@ class AppSettings(BaseSettings): app_demo_mode: bool = False app_disable_swagger: bool = False app_disable_redoc: bool = False + app_manual_import_routers: bool = False class JwtSettings(BaseSettings): From b9235147a29f2e81a77b48ab5419785cb507ceb3 Mon Sep 17 00:00:00 2001 From: smock Date: Sat, 14 Feb 2026 10:43:58 +0800 Subject: [PATCH 2/6] =?UTF-8?q?Revert=20"=E6=94=AF=E6=8C=81=E6=89=8B?= =?UTF-8?q?=E5=8A=A8=E5=AF=BC=E5=85=A5=E8=B7=AF=E7=94=B1=E3=80=81fix=20bug?= =?UTF-8?q?"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit bb4b4357d4028b00d75b4552e4b613b8efb7e182. --- .gitignore | 2 +- ruoyi-fastapi-backend/.env.dev | 2 -- ruoyi-fastapi-backend/.env.dockermy | 2 -- ruoyi-fastapi-backend/.env.dockerpg | 2 -- ruoyi-fastapi-backend/.env.prod | 2 -- ruoyi-fastapi-backend/common/router.py | 43 +------------------------- ruoyi-fastapi-backend/config/env.py | 1 - 7 files changed, 2 insertions(+), 52 deletions(-) diff --git a/.gitignore b/.gitignore index 70cb8f1d1..220a6961d 100644 --- a/.gitignore +++ b/.gitignore @@ -85,7 +85,7 @@ ipython_config.py # pyenv # For a library or package, you might want to ignore these files since the code is # intended to run in multiple environments; otherwise, check them in: -.python-version +# .python-version # pipenv # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. diff --git a/ruoyi-fastapi-backend/.env.dev b/ruoyi-fastapi-backend/.env.dev index 4c75d15bf..f5feade42 100644 --- a/ruoyi-fastapi-backend/.env.dev +++ b/ruoyi-fastapi-backend/.env.dev @@ -25,8 +25,6 @@ APP_DEMO_MODE = false APP_DISABLE_SWAGGER = false # 应用是否禁用ReDoc文档 APP_DISABLE_REDOC = false -# 应用是否启用手动导入路由(默认为自动扫描routers目录) -APP_MANUAL_IMPORT_ROUTERS = false # -------- Jwt配置 -------- # Jwt秘钥 diff --git a/ruoyi-fastapi-backend/.env.dockermy b/ruoyi-fastapi-backend/.env.dockermy index 52d362c83..a4716822b 100644 --- a/ruoyi-fastapi-backend/.env.dockermy +++ b/ruoyi-fastapi-backend/.env.dockermy @@ -25,8 +25,6 @@ APP_DEMO_MODE = false APP_DISABLE_SWAGGER = true # 应用是否禁用ReDoc文档 APP_DISABLE_REDOC = true -# 应用是否启用手动导入路由(默认为自动扫描routers目录) -APP_MANUAL_IMPORT_ROUTERS = false # -------- Jwt配置 -------- # Jwt秘钥 diff --git a/ruoyi-fastapi-backend/.env.dockerpg b/ruoyi-fastapi-backend/.env.dockerpg index 5f3202aea..5b8619d79 100644 --- a/ruoyi-fastapi-backend/.env.dockerpg +++ b/ruoyi-fastapi-backend/.env.dockerpg @@ -25,8 +25,6 @@ APP_SAME_TIME_LOGIN = true APP_DISABLE_SWAGGER = true # 应用是否禁用ReDoc文档 APP_DISABLE_REDOC = true -# 应用是否启用手动导入路由(默认为自动扫描routers目录) -APP_MANUAL_IMPORT_ROUTERS = false # -------- Jwt配置 -------- # Jwt秘钥 diff --git a/ruoyi-fastapi-backend/.env.prod b/ruoyi-fastapi-backend/.env.prod index fdc7c982a..be624c86c 100644 --- a/ruoyi-fastapi-backend/.env.prod +++ b/ruoyi-fastapi-backend/.env.prod @@ -25,8 +25,6 @@ APP_DEMO_MODE = false APP_DISABLE_SWAGGER = true # 应用是否禁用ReDoc文档 APP_DISABLE_REDOC = true -# 应用是否启用手动导入路由(默认为自动扫描routers目录) -APP_MANUAL_IMPORT_ROUTERS = false # -------- Jwt配置 -------- # Jwt秘钥 diff --git a/ruoyi-fastapi-backend/common/router.py b/ruoyi-fastapi-backend/common/router.py index 064443353..f7106c5f9 100644 --- a/ruoyi-fastapi-backend/common/router.py +++ b/ruoyi-fastapi-backend/common/router.py @@ -16,8 +16,6 @@ from starlette.types import ASGIApp, Lifespan from typing_extensions import deprecated -from config.env import AppConfig - class APIRouterPro(APIRouter): """ @@ -310,41 +308,7 @@ def _find_controller_files(self) -> list[str]: :return: py文件路径列表 """ pattern = os.path.join(self.project_root, '*', 'controller', '[!_]*.py') - files = glob.glob(pattern) - # 去重并保持顺序 VSCode 调试模式 + Uvicorn 热重载 环境下,可能返回重复的文件路径。 :os.path.join(self.project_root, '*', 'controller', '[!_]*.py') 中的 * 可能匹配到符号链接指向的同一物理目录,导致同一个文件通过不同路径(真实路径 vs 链接路径)被重复匹配。 - return sorted(dict.fromkeys(files)) - - def _exclude_controller_file_and_manual_import_routers( - self, all_file_names: list[str] - ) -> tuple[list[str], list[tuple[str, APIRouter]]]: - """ - 排除指定的controller文件,并手动导入路由实例(性能比较烂的朋友在debug+reload的时候可以快一点) - - :param file_name: 要排除的controller文件名 - :return: 路由实例列表 - """ - exclude_file_names = [ - 'ai_chat_controller.py', - 'ai_model_controller.py', - ] - - if not exclude_file_names: - return all_file_names, [] - - all_file_names_exclude = [] # 过滤后的文件名列表 - all_file_names_exclude = [ - fname for fname in all_file_names if not any(exclude in fname for exclude in exclude_file_names) - ] - - routers = [] - # 手动导入路由实例 - from module_ai.controller import ai_chat_controller, ai_model_controller # noqa: PLC0415 - - # 这个文件太大了,电脑性能很差的用户动态导入会很久5s+ - routers.append(('module_ai.controller.ai_chat_controller', ai_chat_controller.ai_chat_controller)) - routers.append(('module_ai.controller.ai_model_controller', ai_model_controller.ai_model_controller)) - - return all_file_names_exclude, routers + return sorted(glob.glob(pattern)) def _import_module_and_get_routers(self, controller_files: list[str]) -> list[tuple[str, APIRouter]]: """ @@ -354,11 +318,6 @@ def _import_module_and_get_routers(self, controller_files: list[str]) -> list[tu :return: 路由实例列表 """ routers = [] - - if AppConfig.app_manual_import_routers: - controller_files, manual_routers = self._exclude_controller_file_and_manual_import_routers(controller_files) - routers.extend(manual_routers) - for file_path in controller_files: # 计算模块路径 relative_path = os.path.relpath(file_path, self.project_root) diff --git a/ruoyi-fastapi-backend/config/env.py b/ruoyi-fastapi-backend/config/env.py index d1d961ab6..89baec580 100644 --- a/ruoyi-fastapi-backend/config/env.py +++ b/ruoyi-fastapi-backend/config/env.py @@ -27,7 +27,6 @@ class AppSettings(BaseSettings): app_demo_mode: bool = False app_disable_swagger: bool = False app_disable_redoc: bool = False - app_manual_import_routers: bool = False class JwtSettings(BaseSettings): From 2b09b3047d0082c0d8c1e0faed653fac13ede31a Mon Sep 17 00:00:00 2001 From: smock Date: Sat, 14 Feb 2026 15:17:23 +0800 Subject: [PATCH 3/6] =?UTF-8?q?get=5Fcurrent=5Fuser=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E7=BC=93=E5=AD=98=E6=94=AF=E6=8C=81=EF=BC=9B=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E5=B0=91=E9=87=8Fbug=EF=BC=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 3 +- README.md | 2 +- ruoyi-fastapi-backend/.env.dev | 2 + ruoyi-fastapi-backend/.env.dev.example | 122 ++++++++++++++++++ ruoyi-fastapi-backend/.env.dockermy | 2 + ruoyi-fastapi-backend/.env.dockerpg | 2 + ruoyi-fastapi-backend/.env.prod | 2 + ruoyi-fastapi-backend/common/enums.py | 1 + ruoyi-fastapi-backend/config/env.py | 1 + .../controller/dept_controller.py | 4 +- .../controller/login_controller.py | 15 ++- .../controller/menu_controller.py | 4 +- .../controller/post_controller.py | 4 +- .../controller/role_controller.py | 16 ++- .../controller/user_controller.py | 16 +-- .../module_admin/service/cache_service.py | 48 +++++++ .../module_admin/service/dept_service.py | 12 +- .../module_admin/service/login_service.py | 88 ++++++++++--- .../module_admin/service/menu_service.py | 12 +- .../module_admin/service/post_service.py | 12 +- .../module_admin/service/role_service.py | 17 ++- .../module_admin/service/user_service.py | 27 +++- ruoyi-fastapi-backend/utils/json_util.py | 32 +++++ 23 files changed, 385 insertions(+), 59 deletions(-) create mode 100644 ruoyi-fastapi-backend/.env.dev.example create mode 100644 ruoyi-fastapi-backend/utils/json_util.py diff --git a/.gitignore b/.gitignore index 220a6961d..d9da83503 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +.env.dev # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] @@ -85,7 +86,7 @@ ipython_config.py # pyenv # For a library or package, you might want to ignore these files since the code is # intended to run in multiple environments; otherwise, check them in: -# .python-version +.python-version # pipenv # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. diff --git a/README.md b/README.md index 705778548..36ccaa609 100644 --- a/README.md +++ b/README.md @@ -246,7 +246,7 @@ pip3 install -r requirements.txt pip3 install -r requirements-pg.txt # 配置环境 -在.env.dev文件中配置开发环境的数据库和redis +把.env.dev.example修改为.env.dev,并在.env.dev文件中配置开发环境的数据库和redis # 运行sql文件 1.新建数据库ruoyi-fastapi(默认,可修改) diff --git a/ruoyi-fastapi-backend/.env.dev b/ruoyi-fastapi-backend/.env.dev index f5feade42..fe9823854 100644 --- a/ruoyi-fastapi-backend/.env.dev +++ b/ruoyi-fastapi-backend/.env.dev @@ -25,6 +25,8 @@ APP_DEMO_MODE = false APP_DISABLE_SWAGGER = false # 应用是否禁用ReDoc文档 APP_DISABLE_REDOC = false +# 启用用户信息缓存 +APP_ENABLE_USER_CACHE = true # -------- Jwt配置 -------- # Jwt秘钥 diff --git a/ruoyi-fastapi-backend/.env.dev.example b/ruoyi-fastapi-backend/.env.dev.example new file mode 100644 index 000000000..fe9823854 --- /dev/null +++ b/ruoyi-fastapi-backend/.env.dev.example @@ -0,0 +1,122 @@ +# -------- 应用配置 -------- +# 应用运行环境 +APP_ENV = 'dev' +# 应用名称 +APP_NAME = 'RuoYi-FastAPI' +# 应用代理路径 +APP_ROOT_PATH = '/dev-api' +# 应用主机 +APP_HOST = '0.0.0.0' +# 应用端口 +APP_PORT = 9099 +# 应用版本 +APP_VERSION= '1.9.0' +# 应用是否开启热重载 +APP_RELOAD = true +# 应用工作进程数 +APP_WORKERS = 1 +# 应用是否开启IP归属区域查询 +APP_IP_LOCATION_QUERY = true +# 应用是否允许账号同时登录 +APP_SAME_TIME_LOGIN = true +# 应用是否为演示模式 +APP_DEMO_MODE = false +# 应用是否禁用Swagger文档 +APP_DISABLE_SWAGGER = false +# 应用是否禁用ReDoc文档 +APP_DISABLE_REDOC = false +# 启用用户信息缓存 +APP_ENABLE_USER_CACHE = true + +# -------- Jwt配置 -------- +# Jwt秘钥 +JWT_SECRET_KEY = 'b01c66dc2c58dc6a0aabfe2144256be36226de378bf87f72c0c795dda67f4d55' +# Jwt算法 +JWT_ALGORITHM = 'HS256' +# 令牌过期时间 +JWT_EXPIRE_MINUTES = 1440 +# redis中令牌过期时间 +JWT_REDIS_EXPIRE_MINUTES = 30 + + +# -------- 数据库配置 -------- +# 数据库类型,可选的有'mysql'、'postgresql',默认为'mysql' +DB_TYPE = 'mysql' +# 数据库主机 +DB_HOST = '127.0.0.1' +# 数据库端口 +DB_PORT = 3306 +# 数据库用户名 +DB_USERNAME = 'root' +# 数据库密码 +DB_PASSWORD = 'mysqlroot' +# 数据库名称 +DB_DATABASE = 'ruoyi-fastapi' +# 是否开启sqlalchemy日志 +DB_ECHO = true +# 允许溢出连接池大小的最大连接数 +DB_MAX_OVERFLOW = 10 +# 连接池大小,0表示连接数无限制 +DB_POOL_SIZE = 50 +# 连接回收时间(单位:秒) +DB_POOL_RECYCLE = 3600 +# 连接池中没有线程可用时,最多等待的时间(单位:秒) +DB_POOL_TIMEOUT = 30 + +# -------- Redis配置 -------- +# Redis主机 +REDIS_HOST = '127.0.0.1' +# Redis端口 +REDIS_PORT = 6379 +# Redis用户名 +REDIS_USERNAME = '' +# Redis密码 +REDIS_PASSWORD = '' +# Redis数据库 +REDIS_DATABASE = 2 + +# -------- 日志配置 -------- +# Redis Stream Key +LOG_STREAM_KEY = 'log:stream' +# Redis Stream 消费组名称 +LOG_STREAM_GROUP = 'log_aggregator' +# Redis Stream 消费者名称前缀 +LOG_STREAM_CONSUMER_PREFIX = 'worker' +# 每次读取的最大消息数量 +LOG_STREAM_BATCH_SIZE = 100 +# 阻塞读取等待时间(毫秒) +LOG_STREAM_BLOCK_MS = 2000 +# Stream 最大长度(近似裁剪) +LOG_STREAM_MAXLEN = 100000 +# Pending 回收最小空闲时间(毫秒) +LOG_STREAM_CLAIM_IDLE_MS = 60000 +# Pending 回收检查间隔(毫秒) +LOG_STREAM_CLAIM_INTERVAL_MS = 5000 +# 每次回收的最大消息数量 +LOG_STREAM_CLAIM_BATCH_SIZE = 100 +# 去重 Key 过期时间(秒) +LOG_STREAM_DEDUP_TTL = 3600 +# 去重 Key 前缀 +LOG_STREAM_DEDUP_PREFIX = 'log:dedup' +# stdout 输出是否为 JSON +LOGURU_JSON = false +# Loguru 最低输出级别 +LOGURU_LEVEL = 'INFO' +# 是否输出到 stdout +LOGURU_STDOUT = true +# 是否启用文件日志 +LOG_FILE_ENABLED = true +# 文件日志根目录 +LOG_FILE_BASE_DIR = 'logs' +# 文件滚动策略 +LOGURU_ROTATION = '50MB' +# 文件保留策略 +LOGURU_RETENTION = '30 days' +# 文件压缩格式 +LOGURU_COMPRESSION = 'zip' +# 实例标识(用于区分实例) +LOG_INSTANCE_ID = 'dev' +# 服务名称(用于统一标识服务) +LOG_SERVICE_NAME = 'ruoyi-fastapi-backend' +# Worker 标识(auto 自动生成) +LOG_WORKER_ID = 'auto' diff --git a/ruoyi-fastapi-backend/.env.dockermy b/ruoyi-fastapi-backend/.env.dockermy index a4716822b..59543eaf4 100644 --- a/ruoyi-fastapi-backend/.env.dockermy +++ b/ruoyi-fastapi-backend/.env.dockermy @@ -25,6 +25,8 @@ APP_DEMO_MODE = false APP_DISABLE_SWAGGER = true # 应用是否禁用ReDoc文档 APP_DISABLE_REDOC = true +# 启用用户信息缓存 +APP_ENABLE_USER_CACHE = true # -------- Jwt配置 -------- # Jwt秘钥 diff --git a/ruoyi-fastapi-backend/.env.dockerpg b/ruoyi-fastapi-backend/.env.dockerpg index 5b8619d79..691378f87 100644 --- a/ruoyi-fastapi-backend/.env.dockerpg +++ b/ruoyi-fastapi-backend/.env.dockerpg @@ -25,6 +25,8 @@ APP_SAME_TIME_LOGIN = true APP_DISABLE_SWAGGER = true # 应用是否禁用ReDoc文档 APP_DISABLE_REDOC = true +# 启用用户信息缓存 +APP_ENABLE_USER_CACHE = true # -------- Jwt配置 -------- # Jwt秘钥 diff --git a/ruoyi-fastapi-backend/.env.prod b/ruoyi-fastapi-backend/.env.prod index be624c86c..a6b20074d 100644 --- a/ruoyi-fastapi-backend/.env.prod +++ b/ruoyi-fastapi-backend/.env.prod @@ -25,6 +25,8 @@ APP_DEMO_MODE = false APP_DISABLE_SWAGGER = true # 应用是否禁用ReDoc文档 APP_DISABLE_REDOC = true +# 启用用户信息缓存 +APP_ENABLE_USER_CACHE = true # -------- Jwt配置 -------- # Jwt秘钥 diff --git a/ruoyi-fastapi-backend/common/enums.py b/ruoyi-fastapi-backend/common/enums.py index 3ce6e320a..d6983c207 100644 --- a/ruoyi-fastapi-backend/common/enums.py +++ b/ruoyi-fastapi-backend/common/enums.py @@ -49,3 +49,4 @@ def remark(self) -> str | None: ACCOUNT_LOCK = {'key': 'account_lock', 'remark': '用户锁定'} PASSWORD_ERROR_COUNT = {'key': 'password_error_count', 'remark': '密码错误次数'} SMS_CODE = {'key': 'sms_code', 'remark': '短信验证码'} + USER_INFO = {'key': 'user', 'remark': '用户信息缓存'} diff --git a/ruoyi-fastapi-backend/config/env.py b/ruoyi-fastapi-backend/config/env.py index 89baec580..8fd344212 100644 --- a/ruoyi-fastapi-backend/config/env.py +++ b/ruoyi-fastapi-backend/config/env.py @@ -27,6 +27,7 @@ class AppSettings(BaseSettings): app_demo_mode: bool = False app_disable_swagger: bool = False app_disable_redoc: bool = False + app_enable_user_cache: bool = True class JwtSettings(BaseSettings): diff --git a/ruoyi-fastapi-backend/module_admin/controller/dept_controller.py b/ruoyi-fastapi-backend/module_admin/controller/dept_controller.py index cbae112ad..c5c9dd301 100644 --- a/ruoyi-fastapi-backend/module_admin/controller/dept_controller.py +++ b/ruoyi-fastapi-backend/module_admin/controller/dept_controller.py @@ -110,7 +110,7 @@ async def edit_system_dept( await DeptService.check_dept_data_scope_services(query_db, edit_dept.dept_id, data_scope_sql) edit_dept.update_by = current_user.user.user_name edit_dept.update_time = datetime.now() - edit_dept_result = await DeptService.edit_dept_services(query_db, edit_dept) + edit_dept_result = await DeptService.edit_dept_services(request, query_db, edit_dept) logger.info(edit_dept_result.message) return ResponseUtil.success(msg=edit_dept_result.message) @@ -139,7 +139,7 @@ async def delete_system_dept( delete_dept = DeleteDeptModel(deptIds=dept_ids) delete_dept.update_by = current_user.user.user_name delete_dept.update_time = datetime.now() - delete_dept_result = await DeptService.delete_dept_services(query_db, delete_dept) + delete_dept_result = await DeptService.delete_dept_services(request, query_db, delete_dept) logger.info(delete_dept_result.message) return ResponseUtil.success(msg=delete_dept_result.message) diff --git a/ruoyi-fastapi-backend/module_admin/controller/login_controller.py b/ruoyi-fastapi-backend/module_admin/controller/login_controller.py index 2143895fb..93741006f 100644 --- a/ruoyi-fastapi-backend/module_admin/controller/login_controller.py +++ b/ruoyi-fastapi-backend/module_admin/controller/login_controller.py @@ -73,7 +73,7 @@ async def login( ex=timedelta(minutes=JwtConfig.jwt_redis_expire_minutes), ) await UserService.edit_user_services( - query_db, EditUserModel(userId=result[0].user_id, loginDate=datetime.now(), type='status') + request, query_db, EditUserModel(userId=result[0].user_id, loginDate=datetime.now(), type='status') ) logger.info('登录成功') # 判断请求是否来自于api文档,如果是返回指定格式的结果,用于修复api文档认证成功后token显示undefined的bug @@ -169,14 +169,19 @@ async def register_user( response_model=ResponseBaseModel, ) async def logout(request: Request, token: Annotated[str | None, Depends(oauth2_scheme)]) -> Response: - payload = jwt.decode( - token, JwtConfig.jwt_secret_key, algorithms=[JwtConfig.jwt_algorithm], options={'verify_exp': False} - ) + try: + payload = jwt.decode( + token, JwtConfig.jwt_secret_key, algorithms=[JwtConfig.jwt_algorithm], options={'verify_exp': False} + ) + except jwt.InvalidSignatureError: + logger.info('Token已过期,无法解析用户信息') + return ResponseUtil.success(msg='退出成功') + if AppConfig.app_same_time_login: token_id: str = payload.get('session_id') else: token_id: str = payload.get('user_id') - await LoginService.logout_services(request, token_id) + await LoginService.logout_services(request, token_id, payload.get('user_id')) logger.info('退出成功') return ResponseUtil.success(msg='退出成功') diff --git a/ruoyi-fastapi-backend/module_admin/controller/menu_controller.py b/ruoyi-fastapi-backend/module_admin/controller/menu_controller.py index d5d8ffced..3219b16d6 100644 --- a/ruoyi-fastapi-backend/module_admin/controller/menu_controller.py +++ b/ruoyi-fastapi-backend/module_admin/controller/menu_controller.py @@ -120,7 +120,7 @@ async def edit_system_menu( ) -> Response: edit_menu.update_by = current_user.user.user_name edit_menu.update_time = datetime.now() - edit_menu_result = await MenuService.edit_menu_services(query_db, edit_menu) + edit_menu_result = await MenuService.edit_menu_services(request, query_db, edit_menu) logger.info(edit_menu_result.message) return ResponseUtil.success(msg=edit_menu_result.message) @@ -140,7 +140,7 @@ async def delete_system_menu( query_db: Annotated[AsyncSession, DBSessionDependency()], ) -> Response: delete_menu = DeleteMenuModel(menuIds=menu_ids) - delete_menu_result = await MenuService.delete_menu_services(query_db, delete_menu) + delete_menu_result = await MenuService.delete_menu_services(request, query_db, delete_menu) logger.info(delete_menu_result.message) return ResponseUtil.success(msg=delete_menu_result.message) diff --git a/ruoyi-fastapi-backend/module_admin/controller/post_controller.py b/ruoyi-fastapi-backend/module_admin/controller/post_controller.py index 6a6312113..845dde3f2 100644 --- a/ruoyi-fastapi-backend/module_admin/controller/post_controller.py +++ b/ruoyi-fastapi-backend/module_admin/controller/post_controller.py @@ -86,7 +86,7 @@ async def edit_system_post( ) -> Response: edit_post.update_by = current_user.user.user_name edit_post.update_time = datetime.now() - edit_post_result = await PostService.edit_post_services(query_db, edit_post) + edit_post_result = await PostService.edit_post_services(request, query_db, edit_post) logger.info(edit_post_result.message) return ResponseUtil.success(msg=edit_post_result.message) @@ -106,7 +106,7 @@ async def delete_system_post( query_db: Annotated[AsyncSession, DBSessionDependency()], ) -> Response: delete_post = DeletePostModel(postIds=post_ids) - delete_post_result = await PostService.delete_post_services(query_db, delete_post) + delete_post_result = await PostService.delete_post_services(request, query_db, delete_post) logger.info(delete_post_result.message) return ResponseUtil.success(msg=delete_post_result.message) diff --git a/ruoyi-fastapi-backend/module_admin/controller/role_controller.py b/ruoyi-fastapi-backend/module_admin/controller/role_controller.py index faefde992..372f2e587 100644 --- a/ruoyi-fastapi-backend/module_admin/controller/role_controller.py +++ b/ruoyi-fastapi-backend/module_admin/controller/role_controller.py @@ -126,7 +126,7 @@ async def edit_system_role( await RoleService.check_role_data_scope_services(query_db, str(edit_role.role_id), data_scope_sql) edit_role.update_by = current_user.user.user_name edit_role.update_time = datetime.now() - edit_role_result = await RoleService.edit_role_services(query_db, edit_role) + edit_role_result = await RoleService.edit_role_services(request, query_db, edit_role) logger.info(edit_role_result.message) return ResponseUtil.success(msg=edit_role_result.message) @@ -158,7 +158,7 @@ async def edit_system_role_datascope( updateBy=current_user.user.user_name, updateTime=datetime.now(), ) - role_data_scope_result = await RoleService.role_datascope_services(query_db, edit_role) + role_data_scope_result = await RoleService.role_datascope_services(request, query_db, edit_role) logger.info(role_data_scope_result.message) return ResponseUtil.success(msg=role_data_scope_result.message) @@ -186,7 +186,7 @@ async def delete_system_role( if not current_user.user.admin: await RoleService.check_role_data_scope_services(query_db, role_id, data_scope_sql) delete_role = DeleteRoleModel(roleIds=role_ids, updateBy=current_user.user.user_name, updateTime=datetime.now()) - delete_role_result = await RoleService.delete_role_services(query_db, delete_role) + delete_role_result = await RoleService.delete_role_services(request, query_db, delete_role) logger.info(delete_role_result.message) return ResponseUtil.success(msg=delete_role_result.message) @@ -271,7 +271,7 @@ async def reset_system_role_status( updateTime=datetime.now(), type='status', ) - edit_role_result = await RoleService.edit_role_services(query_db, edit_role) + edit_role_result = await RoleService.edit_role_services(request, query_db, edit_role) logger.info(edit_role_result.message) return ResponseUtil.success(msg=edit_role_result.message) @@ -336,7 +336,7 @@ async def add_system_role_user( ) -> Response: if not current_user.user.admin: await RoleService.check_role_data_scope_services(query_db, str(add_role_user.role_id), data_scope_sql) - add_role_user_result = await UserService.add_user_role_services(query_db, add_role_user) + add_role_user_result = await UserService.add_user_role_services(request, query_db, add_role_user) logger.info(add_role_user_result.message) return ResponseUtil.success(msg=add_role_user_result.message) @@ -355,7 +355,7 @@ async def cancel_system_role_user( cancel_user_role: CrudUserRoleModel, query_db: Annotated[AsyncSession, DBSessionDependency()], ) -> Response: - cancel_user_role_result = await UserService.delete_user_role_services(query_db, cancel_user_role) + cancel_user_role_result = await UserService.delete_user_role_services(request, query_db, cancel_user_role) logger.info(cancel_user_role_result.message) return ResponseUtil.success(msg=cancel_user_role_result.message) @@ -374,7 +374,9 @@ async def batch_cancel_system_role_user( batch_cancel_user_role: Annotated[CrudUserRoleModel, Query()], query_db: Annotated[AsyncSession, DBSessionDependency()], ) -> Response: - batch_cancel_user_role_result = await UserService.delete_user_role_services(query_db, batch_cancel_user_role) + batch_cancel_user_role_result = await UserService.delete_user_role_services( + request, query_db, batch_cancel_user_role + ) logger.info(batch_cancel_user_role_result.message) return ResponseUtil.success(msg=batch_cancel_user_role_result.message) diff --git a/ruoyi-fastapi-backend/module_admin/controller/user_controller.py b/ruoyi-fastapi-backend/module_admin/controller/user_controller.py index 31fc8dbcc..5133e382e 100644 --- a/ruoyi-fastapi-backend/module_admin/controller/user_controller.py +++ b/ruoyi-fastapi-backend/module_admin/controller/user_controller.py @@ -153,7 +153,7 @@ async def edit_system_user( ) edit_user.update_by = current_user.user.user_name edit_user.update_time = datetime.now() - edit_user_result = await UserService.edit_user_services(query_db, edit_user) + edit_user_result = await UserService.edit_user_services(request, query_db, edit_user) logger.info(edit_user_result.message) return ResponseUtil.success(msg=edit_user_result.message) @@ -185,7 +185,7 @@ async def delete_system_user( if not current_user.user.admin: await UserService.check_user_data_scope_services(query_db, int(user_id), data_scope_sql) delete_user = DeleteUserModel(userIds=user_ids, updateBy=current_user.user.user_name, updateTime=datetime.now()) - delete_user_result = await UserService.delete_user_services(query_db, delete_user) + delete_user_result = await UserService.delete_user_services(request, query_db, delete_user) logger.info(delete_user_result.message) return ResponseUtil.success(msg=delete_user_result.message) @@ -217,7 +217,7 @@ async def reset_system_user_pwd( updateTime=datetime.now(), type='pwd', ) - edit_user_result = await UserService.edit_user_services(query_db, edit_user) + edit_user_result = await UserService.edit_user_services(request, query_db, edit_user) logger.info(edit_user_result.message) return ResponseUtil.success(msg=edit_user_result.message) @@ -248,7 +248,7 @@ async def change_system_user_status( updateTime=datetime.now(), type='status', ) - edit_user_result = await UserService.edit_user_services(query_db, edit_user) + edit_user_result = await UserService.edit_user_services(request, query_db, edit_user) logger.info(edit_user_result.message) return ResponseUtil.success(msg=edit_user_result.message) @@ -333,7 +333,7 @@ async def change_system_user_profile_avatar( updateTime=datetime.now(), type='avatar', ) - edit_user_result = await UserService.edit_user_services(query_db, edit_user) + edit_user_result = await UserService.edit_user_services(request, query_db, edit_user) logger.info(edit_user_result.message) return ResponseUtil.success(model_content=AvatarModel(imgUrl=edit_user.avatar), msg=edit_user_result.message) @@ -363,7 +363,7 @@ async def change_system_user_profile_info( postIds=current_user.user.post_ids.split(',') if current_user.user.post_ids else [], role=current_user.user.role, ) - edit_user_result = await UserService.edit_user_services(query_db, edit_user) + edit_user_result = await UserService.edit_user_services(request, query_db, edit_user) logger.info(edit_user_result.message) return ResponseUtil.success(msg=edit_user_result.message) @@ -390,7 +390,7 @@ async def reset_system_user_password( updateBy=current_user.user.user_name, updateTime=datetime.now(), ) - reset_user_result = await UserService.reset_user_services(query_db, reset_user) + reset_user_result = await UserService.reset_user_services(request, query_db, reset_user) logger.info(reset_user_result.message) return ResponseUtil.success(msg=reset_user_result.message) @@ -519,7 +519,7 @@ async def update_system_role_user( await UserService.check_user_data_scope_services(query_db, user_id, user_data_scope_sql) await RoleService.check_role_data_scope_services(query_db, role_ids, role_data_scope_sql) add_user_role_result = await UserService.add_user_role_services( - query_db, CrudUserRoleModel(userId=user_id, roleIds=role_ids) + request, query_db, CrudUserRoleModel(userId=user_id, roleIds=role_ids) ) logger.info(add_user_role_result.message) diff --git a/ruoyi-fastapi-backend/module_admin/service/cache_service.py b/ruoyi-fastapi-backend/module_admin/service/cache_service.py index 410f6b896..7c958914f 100644 --- a/ruoyi-fastapi-backend/module_admin/service/cache_service.py +++ b/ruoyi-fastapi-backend/module_admin/service/cache_service.py @@ -1,4 +1,5 @@ from fastapi import Request +from config.env import AppConfig from common.enums import RedisInitKeyConfig from common.vo import CrudResponseModel @@ -124,3 +125,50 @@ async def clear_cache_monitor_all_services(cls, request: Request) -> CrudRespons await RedisUtil.init_sys_config(request.app.state.redis) return CrudResponseModel(is_success=True, message='所有缓存清除成功') + + @classmethod + async def clear_usercache_by_id(cls, request: Request, user_id: int): + """ + 根据用户ID清除用户信息缓存service + + :param request: Request对象 + :param user_id: 用户ID + :return: 操作缓存响应信息 + """ + cache_key = f'{RedisInitKeyConfig.USER_INFO.key}:{user_id}' + await request.app.state.redis.delete(cache_key) + + return True + + @classmethod + async def clear_usercache_all(cls, request: Request): + """ + 清除所有用户信息缓存service + + :param request: Request对象 + :return: 操作缓存响应信息 + """ + if not AppConfig.app_enable_user_cache: + return False + + pattern = f'{RedisInitKeyConfig.USER_INFO.key}:*' + batch_size = 1000 + + try: + cursor = 0 + deleted_count = 0 + while True: + cursor, keys = await request.app.state.redis.scan(cursor=cursor, match=pattern, count=batch_size) + if keys: + await request.app.state.redis.unlink(*keys) + deleted_count += len(keys) + # 游标为 0 表示迭代完成 + if cursor == 0: + break + + print(f'✅ 已删除 {deleted_count} 个用户缓存键') + except Exception as e: + print(f'❌ 删除失败: {e}') + return False + + return True diff --git a/ruoyi-fastapi-backend/module_admin/service/dept_service.py b/ruoyi-fastapi-backend/module_admin/service/dept_service.py index da61a2b08..a8252d7e0 100644 --- a/ruoyi-fastapi-backend/module_admin/service/dept_service.py +++ b/ruoyi-fastapi-backend/module_admin/service/dept_service.py @@ -1,6 +1,7 @@ from collections.abc import Sequence from typing import Any +from fastapi import Request from sqlalchemy import ColumnElement from sqlalchemy.ext.asyncio import AsyncSession @@ -11,6 +12,7 @@ from module_admin.entity.do.dept_do import SysDept from module_admin.entity.vo.dept_vo import DeleteDeptModel, DeptModel, DeptTreeModel from utils.common_util import CamelCaseUtil +from module_admin.service.cache_service import CacheService class DeptService: @@ -126,7 +128,9 @@ async def add_dept_services(cls, query_db: AsyncSession, page_object: DeptModel) raise e @classmethod - async def edit_dept_services(cls, query_db: AsyncSession, page_object: DeptModel) -> CrudResponseModel: + async def edit_dept_services( + cls, request: Request, query_db: AsyncSession, page_object: DeptModel + ) -> CrudResponseModel: """ 编辑部门信息service @@ -160,13 +164,16 @@ async def edit_dept_services(cls, query_db: AsyncSession, page_object: DeptModel ): await cls.update_parent_dept_status_normal(query_db, page_object) await query_db.commit() + await CacheService.clear_usercache_all(request) return CrudResponseModel(is_success=True, message='更新成功') except Exception as e: await query_db.rollback() raise e @classmethod - async def delete_dept_services(cls, query_db: AsyncSession, page_object: DeleteDeptModel) -> CrudResponseModel: + async def delete_dept_services( + cls, request: Request, query_db: AsyncSession, page_object: DeleteDeptModel + ) -> CrudResponseModel: """ 删除部门信息service @@ -185,6 +192,7 @@ async def delete_dept_services(cls, query_db: AsyncSession, page_object: DeleteD await DeptDao.delete_dept_dao(query_db, DeptModel(deptId=dept_id)) await query_db.commit() + await CacheService.clear_usercache_all(request) return CrudResponseModel(is_success=True, message='删除成功') except Exception as e: await query_db.rollback() diff --git a/ruoyi-fastapi-backend/module_admin/service/login_service.py b/ruoyi-fastapi-backend/module_admin/service/login_service.py index e7bc1bfd9..e72bdf2a3 100644 --- a/ruoyi-fastapi-backend/module_admin/service/login_service.py +++ b/ruoyi-fastapi-backend/module_admin/service/login_service.py @@ -1,3 +1,4 @@ +import json import random import uuid from datetime import datetime, timedelta, timezone @@ -25,10 +26,12 @@ from module_admin.entity.vo.login_vo import MenuTreeModel, MetaModel, RouterModel, SmsCode, UserLogin, UserRegister from module_admin.entity.vo.user_vo import AddUserModel, CurrentUserModel, ResetUserModel, TokenData, UserInfoModel from module_admin.service.user_service import UserService +from module_admin.service.cache_service import CacheService from utils.common_util import CamelCaseUtil from utils.log_util import logger from utils.message_util import message_service from utils.pwd_util import PwdUtil +from utils.json_util import ComplexEncoder oauth2_scheme = OAuth2PasswordBearer(tokenUrl='login') @@ -168,6 +171,9 @@ async def __check_login_captcha(cls, request: Request, login_user: UserLogin) -> if login_user.code != str(captcha_value): logger.warning('验证码错误') raise LoginException(data='', message='验证码错误') + + # 验证成功/失败后删除验证码缓存 + await request.app.state.redis.delete(f'{RedisInitKeyConfig.CAPTCHA_CODES.key}:{login_user.uuid}') return True @classmethod @@ -188,6 +194,61 @@ async def create_access_token(cls, data: dict, expires_delta: timedelta | None = encoded_jwt = jwt.encode(to_encode, JwtConfig.jwt_secret_key, algorithm=JwtConfig.jwt_algorithm) return encoded_jwt + @classmethod + async def __get_user_by_id_cache(cls, request: Request, db: AsyncSession, user_id: int) -> dict[str, Any]: + """ + 根据user_id获取用户信息(启用缓存) + + :param request: 当前请求对象 + :param db: orm对象 + :param user_id: 用户id + :return: 当前user_id的用户信息对象 + """ + if AppConfig.app_enable_user_cache: + cached_user_info = await request.app.state.redis.get(f'{RedisInitKeyConfig.USER_INFO.key}:{user_id}') + if cached_user_info: + await request.app.state.redis.expire( + f'{RedisInitKeyConfig.USER_INFO.key}:{user_id}', + timedelta(minutes=JwtConfig.jwt_redis_expire_minutes), + ) + user_data = json.loads(cached_user_info) + return user_data + + query_user = await UserDao.get_user_by_id(db, user_id=user_id) + if query_user is None: + return None + + role_id_list = [item.role_id for item in query_user.get('user_role_info')] + if 1 in role_id_list: # noqa: SIM108 + permissions = ['*:*:*'] + else: + permissions = [row.perms for row in query_user.get('user_menu_info') if row.perms] + + post_ids = ','.join([str(row.post_id) for row in query_user.get('user_post_info')]) + role_ids = ','.join([str(row.role_id) for row in query_user.get('user_role_info')]) + roles = [row.role_key for row in query_user.get('user_role_info')] + + query_user['permissions'] = permissions + query_user['post_ids'] = post_ids + query_user['role_ids'] = role_ids + query_user['roles'] = roles + + # 不输出部门信息 + if 'user_menu_info' in query_user: + del query_user['user_menu_info'] + + user_data_info = json.dumps(query_user, cls=ComplexEncoder, ensure_ascii=False, indent=2) + user_data = json.loads(user_data_info) + + if AppConfig.app_enable_user_cache: + await request.app.state.redis.set( + f'{RedisInitKeyConfig.USER_INFO.key}:{user_id}', + user_data_info, + ex=timedelta(minutes=JwtConfig.jwt_redis_expire_minutes), + ) + + return user_data + @classmethod async def get_current_user( cls, request: Request = Request, token: str = Depends(oauth2_scheme), query_db: AsyncSession = Depends(get_db) @@ -217,7 +278,7 @@ async def get_current_user( except InvalidTokenError as e: logger.warning('用户token已失效,请重新登录') raise AuthException(data='', message='用户token已失效,请重新登录') from e - query_user = await UserDao.get_user_by_id(query_db, user_id=token_data.user_id) + query_user = await cls.__get_user_by_id_cache(request, query_db, user_id=token_data.user_id) if query_user.get('user_basic_info') is None: logger.warning('用户token不合法') raise AuthException(data='', message='用户token不合法') @@ -242,28 +303,20 @@ async def get_current_user( ex=timedelta(minutes=JwtConfig.jwt_redis_expire_minutes), ) - role_id_list = [item.role_id for item in query_user.get('user_role_info')] - if 1 in role_id_list: # noqa: SIM108 - permissions = ['*:*:*'] - else: - permissions = [row.perms for row in query_user.get('user_menu_info')] - post_ids = ','.join([str(row.post_id) for row in query_user.get('user_post_info')]) - role_ids = ','.join([str(row.role_id) for row in query_user.get('user_role_info')]) - roles = [row.role_key for row in query_user.get('user_role_info')] is_default_modify_pwd = await cls.__init_password_is_modify( - request, query_user.get('user_basic_info').pwd_update_date + request, query_user.get('user_basic_info').get('pwd_update_date') ) is_password_expired = await cls.__password_is_expired( - request, query_user.get('user_basic_info').pwd_update_date + request, query_user.get('user_basic_info').get('pwd_update_date') ) current_user = CurrentUserModel( - permissions=permissions, - roles=roles, + permissions=query_user.get('permissions'), + roles=query_user.get('roles'), user=UserInfoModel( **CamelCaseUtil.transform_result(query_user.get('user_basic_info')), - postIds=post_ids, - roleIds=role_ids, + postIds=query_user.get('post_ids'), + roleIds=query_user.get('roleIds'), dept=CamelCaseUtil.transform_result(query_user.get('user_dept_info')), role=CamelCaseUtil.transform_result(query_user.get('user_role_info')), ), @@ -504,7 +557,7 @@ async def forget_user_services( if forget_user.sms_code == redis_sms_result: forget_user.password = PwdUtil.get_password_hash(forget_user.password) forget_user.user_id = (await UserDao.get_user_by_name(query_db, forget_user.user_name)).user_id - edit_result = await UserService.reset_user_services(query_db, forget_user) + edit_result = await UserService.reset_user_services(request, query_db, forget_user) result = edit_result.dict() elif not redis_sms_result: result = {'is_success': False, 'message': '短信验证码已过期'} @@ -515,7 +568,7 @@ async def forget_user_services( return CrudResponseModel(**result) @classmethod - async def logout_services(cls, request: Request, token_id: str) -> bool: + async def logout_services(cls, request: Request, token_id: str, user_id: str) -> bool: """ 退出登录services @@ -526,6 +579,7 @@ async def logout_services(cls, request: Request, token_id: str) -> bool: await request.app.state.redis.delete(f'{RedisInitKeyConfig.ACCESS_TOKEN.key}:{token_id}') # await request.app.state.redis.delete(f'{current_user.user.user_id}_access_token') # await request.app.state.redis.delete(f'{current_user.user.user_id}_session_id') + await CacheService.clear_usercache_by_id(request, user_id) return True diff --git a/ruoyi-fastapi-backend/module_admin/service/menu_service.py b/ruoyi-fastapi-backend/module_admin/service/menu_service.py index 734bfe61a..f5c128a11 100644 --- a/ruoyi-fastapi-backend/module_admin/service/menu_service.py +++ b/ruoyi-fastapi-backend/module_admin/service/menu_service.py @@ -1,6 +1,7 @@ from collections.abc import Sequence from typing import Any +from fastapi import Request from sqlalchemy.ext.asyncio import AsyncSession from common.constant import CommonConstant, MenuConstant @@ -14,6 +15,7 @@ from module_admin.entity.vo.user_vo import CurrentUserModel from utils.common_util import CamelCaseUtil from utils.string_util import StringUtil +from module_admin.service.cache_service import CacheService class MenuService: @@ -118,7 +120,9 @@ async def add_menu_services(cls, query_db: AsyncSession, page_object: MenuModel) raise e @classmethod - async def edit_menu_services(cls, query_db: AsyncSession, page_object: MenuModel) -> CrudResponseModel: + async def edit_menu_services( + cls, request: Request, query_db: AsyncSession, page_object: MenuModel + ) -> CrudResponseModel: """ 编辑菜单信息service @@ -138,6 +142,7 @@ async def edit_menu_services(cls, query_db: AsyncSession, page_object: MenuModel try: await MenuDao.edit_menu_dao(query_db, edit_menu) await query_db.commit() + await CacheService.clear_usercache_all(request) return CrudResponseModel(is_success=True, message='更新成功') except Exception as e: await query_db.rollback() @@ -146,7 +151,9 @@ async def edit_menu_services(cls, query_db: AsyncSession, page_object: MenuModel raise ServiceException(message='菜单不存在') @classmethod - async def delete_menu_services(cls, query_db: AsyncSession, page_object: DeleteMenuModel) -> CrudResponseModel: + async def delete_menu_services( + cls, request: Request, query_db: AsyncSession, page_object: DeleteMenuModel + ) -> CrudResponseModel: """ 删除菜单信息service @@ -164,6 +171,7 @@ async def delete_menu_services(cls, query_db: AsyncSession, page_object: DeleteM raise ServiceWarning(message='菜单已分配,不允许删除') await MenuDao.delete_menu_dao(query_db, MenuModel(menuId=menu_id)) await query_db.commit() + await CacheService.clear_usercache_all(request) return CrudResponseModel(is_success=True, message='删除成功') except Exception as e: await query_db.rollback() diff --git a/ruoyi-fastapi-backend/module_admin/service/post_service.py b/ruoyi-fastapi-backend/module_admin/service/post_service.py index 9a11f23c2..195a580a3 100644 --- a/ruoyi-fastapi-backend/module_admin/service/post_service.py +++ b/ruoyi-fastapi-backend/module_admin/service/post_service.py @@ -1,5 +1,6 @@ from typing import Any +from fastapi import Request from sqlalchemy.ext.asyncio import AsyncSession from common.constant import CommonConstant @@ -9,6 +10,7 @@ from module_admin.entity.vo.post_vo import DeletePostModel, PostModel, PostPageQueryModel from utils.common_util import CamelCaseUtil from utils.excel_util import ExcelUtil +from module_admin.service.cache_service import CacheService class PostService: @@ -84,7 +86,9 @@ async def add_post_services(cls, query_db: AsyncSession, page_object: PostModel) raise e @classmethod - async def edit_post_services(cls, query_db: AsyncSession, page_object: PostModel) -> CrudResponseModel: + async def edit_post_services( + cls, request: Request, query_db: AsyncSession, page_object: PostModel + ) -> CrudResponseModel: """ 编辑岗位信息service @@ -102,6 +106,7 @@ async def edit_post_services(cls, query_db: AsyncSession, page_object: PostModel try: await PostDao.edit_post_dao(query_db, edit_post) await query_db.commit() + await CacheService.clear_usercache_all(request) return CrudResponseModel(is_success=True, message='更新成功') except Exception as e: await query_db.rollback() @@ -110,7 +115,9 @@ async def edit_post_services(cls, query_db: AsyncSession, page_object: PostModel raise ServiceException(message='岗位不存在') @classmethod - async def delete_post_services(cls, query_db: AsyncSession, page_object: DeletePostModel) -> CrudResponseModel: + async def delete_post_services( + cls, request: Request, query_db: AsyncSession, page_object: DeletePostModel + ) -> CrudResponseModel: """ 删除岗位信息service @@ -127,6 +134,7 @@ async def delete_post_services(cls, query_db: AsyncSession, page_object: DeleteP raise ServiceException(message=f'{post.post_name}已分配,不能删除') await PostDao.delete_post_dao(query_db, PostModel(postId=post_id)) await query_db.commit() + await CacheService.clear_usercache_all(request) return CrudResponseModel(is_success=True, message='删除成功') except Exception as e: await query_db.rollback() diff --git a/ruoyi-fastapi-backend/module_admin/service/role_service.py b/ruoyi-fastapi-backend/module_admin/service/role_service.py index b7cd0a208..9f14d86d3 100644 --- a/ruoyi-fastapi-backend/module_admin/service/role_service.py +++ b/ruoyi-fastapi-backend/module_admin/service/role_service.py @@ -1,5 +1,6 @@ from typing import Any +from fastapi import Request from sqlalchemy import ColumnElement from sqlalchemy.ext.asyncio import AsyncSession @@ -20,6 +21,7 @@ from module_admin.entity.vo.user_vo import UserInfoModel, UserRolePageQueryModel from utils.common_util import CamelCaseUtil from utils.excel_util import ExcelUtil +from module_admin.service.cache_service import CacheService class RoleService: @@ -167,7 +169,9 @@ async def add_role_services(cls, query_db: AsyncSession, page_object: AddRoleMod raise e @classmethod - async def edit_role_services(cls, query_db: AsyncSession, page_object: AddRoleModel) -> CrudResponseModel: + async def edit_role_services( + cls, request: Request, query_db: AsyncSession, page_object: AddRoleModel + ) -> CrudResponseModel: """ 编辑角色信息service @@ -197,6 +201,7 @@ async def edit_role_services(cls, query_db: AsyncSession, page_object: AddRoleMo query_db, RoleMenuModel(roleId=page_object.role_id, menuId=menu) ) await query_db.commit() + await CacheService.clear_usercache_all(request) return CrudResponseModel(is_success=True, message='更新成功') except Exception as e: await query_db.rollback() @@ -205,7 +210,9 @@ async def edit_role_services(cls, query_db: AsyncSession, page_object: AddRoleMo raise ServiceException(message='角色不存在') @classmethod - async def role_datascope_services(cls, query_db: AsyncSession, page_object: AddRoleModel) -> CrudResponseModel: + async def role_datascope_services( + cls, request: Request, query_db: AsyncSession, page_object: AddRoleModel + ) -> CrudResponseModel: """ 分配角色数据权限service @@ -225,6 +232,7 @@ async def role_datascope_services(cls, query_db: AsyncSession, page_object: AddR query_db, RoleDeptModel(roleId=page_object.role_id, deptId=dept) ) await query_db.commit() + await CacheService.clear_usercache_all(request) return CrudResponseModel(is_success=True, message='分配成功') except Exception as e: await query_db.rollback() @@ -233,7 +241,9 @@ async def role_datascope_services(cls, query_db: AsyncSession, page_object: AddR raise ServiceException(message='角色不存在') @classmethod - async def delete_role_services(cls, query_db: AsyncSession, page_object: DeleteRoleModel) -> CrudResponseModel: + async def delete_role_services( + cls, request: Request, query_db: AsyncSession, page_object: DeleteRoleModel + ) -> CrudResponseModel: """ 删除角色信息service @@ -257,6 +267,7 @@ async def delete_role_services(cls, query_db: AsyncSession, page_object: DeleteR await RoleDao.delete_role_dept_dao(query_db, RoleDeptModel(**role_id_dict)) await RoleDao.delete_role_dao(query_db, RoleModel(**role_id_dict)) await query_db.commit() + await CacheService.clear_usercache_all(request) return CrudResponseModel(is_success=True, message='删除成功') except Exception as e: await query_db.rollback() diff --git a/ruoyi-fastapi-backend/module_admin/service/user_service.py b/ruoyi-fastapi-backend/module_admin/service/user_service.py index 5d35780c3..1f458f354 100644 --- a/ruoyi-fastapi-backend/module_admin/service/user_service.py +++ b/ruoyi-fastapi-backend/module_admin/service/user_service.py @@ -36,6 +36,7 @@ from module_admin.service.dept_service import DeptService from module_admin.service.post_service import PostService from module_admin.service.role_service import RoleService +from module_admin.service.cache_service import CacheService from utils.common_util import CamelCaseUtil from utils.excel_util import ExcelUtil from utils.pwd_util import PwdUtil @@ -200,7 +201,9 @@ def _deal_edit_user(cls, page_object: EditUserModel, edit_user: dict[str, Any]) del edit_user['type'] @classmethod - async def edit_user_services(cls, query_db: AsyncSession, page_object: EditUserModel) -> CrudResponseModel: + async def edit_user_services( + cls, request: Request, query_db: AsyncSession, page_object: EditUserModel + ) -> CrudResponseModel: """ 编辑用户信息service @@ -235,6 +238,7 @@ async def edit_user_services(cls, query_db: AsyncSession, page_object: EditUserM query_db, UserPostModel(userId=page_object.user_id, postId=post) ) await query_db.commit() + await CacheService.clear_usercache_by_id(request, page_object.user_id) return CrudResponseModel(is_success=True, message='更新成功') except Exception as e: await query_db.rollback() @@ -243,7 +247,9 @@ async def edit_user_services(cls, query_db: AsyncSession, page_object: EditUserM raise ServiceException(message='用户不存在') @classmethod - async def delete_user_services(cls, query_db: AsyncSession, page_object: DeleteUserModel) -> CrudResponseModel: + async def delete_user_services( + cls, request: Request, query_db: AsyncSession, page_object: DeleteUserModel + ) -> CrudResponseModel: """ 删除用户信息service @@ -264,6 +270,7 @@ async def delete_user_services(cls, query_db: AsyncSession, page_object: DeleteU await UserDao.delete_user_post_dao(query_db, UserPostModel(**user_id_dict)) await UserDao.delete_user_dao(query_db, UserModel(**user_id_dict)) await query_db.commit() + await CacheService.clear_usercache_by_id(request, page_object.user_id) return CrudResponseModel(is_success=True, message='删除成功') except Exception as e: await query_db.rollback() @@ -333,7 +340,9 @@ async def user_profile_services(cls, query_db: AsyncSession, user_id: int) -> Us ) @classmethod - async def reset_user_services(cls, query_db: AsyncSession, page_object: ResetUserModel) -> CrudResponseModel: + async def reset_user_services( + cls, request: Request, query_db: AsyncSession, page_object: ResetUserModel + ) -> CrudResponseModel: """ 重置用户密码service @@ -356,6 +365,7 @@ async def reset_user_services(cls, query_db: AsyncSession, page_object: ResetUse reset_user['password'] = PwdUtil.get_password_hash(page_object.password) await UserDao.edit_user_dao(query_db, reset_user) await query_db.commit() + await CacheService.clear_usercache_by_id(request, page_object.user_id) return CrudResponseModel(is_success=True, message='重置成功') except Exception as e: await query_db.rollback() @@ -581,7 +591,9 @@ async def get_user_role_allocated_list_services( return result @classmethod - async def add_user_role_services(cls, query_db: AsyncSession, page_object: CrudUserRoleModel) -> CrudResponseModel: + async def add_user_role_services( + cls, request: Request, query_db: AsyncSession, page_object: CrudUserRoleModel + ) -> CrudResponseModel: """ 新增用户关联角色信息service @@ -596,6 +608,7 @@ async def add_user_role_services(cls, query_db: AsyncSession, page_object: CrudU for role_id in role_id_list: await UserDao.add_user_role_dao(query_db, UserRoleModel(userId=page_object.user_id, roleId=role_id)) await query_db.commit() + await CacheService.clear_usercache_by_id(request, page_object.user_id) return CrudResponseModel(is_success=True, message='分配成功') except Exception as e: await query_db.rollback() @@ -604,6 +617,7 @@ async def add_user_role_services(cls, query_db: AsyncSession, page_object: CrudU try: await UserDao.delete_user_role_by_user_and_role_dao(query_db, UserRoleModel(userId=page_object.user_id)) await query_db.commit() + await CacheService.clear_usercache_by_id(request, page_object.user_id) return CrudResponseModel(is_success=True, message='分配成功') except Exception as e: await query_db.rollback() @@ -618,6 +632,7 @@ async def add_user_role_services(cls, query_db: AsyncSession, page_object: CrudU if user_role: continue await UserDao.add_user_role_dao(query_db, UserRoleModel(userId=user_id, roleId=page_object.role_id)) + await CacheService.clear_usercache_by_id(request, user_id) await query_db.commit() return CrudResponseModel(is_success=True, message='新增成功') except Exception as e: @@ -628,7 +643,7 @@ async def add_user_role_services(cls, query_db: AsyncSession, page_object: CrudU @classmethod async def delete_user_role_services( - cls, query_db: AsyncSession, page_object: CrudUserRoleModel + cls, request: Request, query_db: AsyncSession, page_object: CrudUserRoleModel ) -> CrudResponseModel: """ 删除用户关联角色信息service @@ -644,6 +659,7 @@ async def delete_user_role_services( query_db, UserRoleModel(userId=page_object.user_id, roleId=page_object.role_id) ) await query_db.commit() + await CacheService.clear_usercache_by_id(request, page_object.user_id) return CrudResponseModel(is_success=True, message='删除成功') except Exception as e: await query_db.rollback() @@ -655,6 +671,7 @@ async def delete_user_role_services( await UserDao.delete_user_role_by_user_and_role_dao( query_db, UserRoleModel(userId=user_id, roleId=page_object.role_id) ) + await CacheService.clear_usercache_by_id(request, user_id) await query_db.commit() return CrudResponseModel(is_success=True, message='删除成功') except Exception as e: diff --git a/ruoyi-fastapi-backend/utils/json_util.py b/ruoyi-fastapi-backend/utils/json_util.py new file mode 100644 index 000000000..a4672187e --- /dev/null +++ b/ruoyi-fastapi-backend/utils/json_util.py @@ -0,0 +1,32 @@ +import json +from datetime import datetime +from typing import Any + + +# 1. 自定义 JSONEncoder +class ComplexEncoder(json.JSONEncoder): + def default(self, obj: Any) -> Any: + # 处理 datetime 对象 + if isinstance(obj, datetime): + return obj.isoformat() + + # 处理自定义类对象 + if hasattr(obj, '__dict__'): + # 获取对象的字典表示,递归处理嵌套对象 + result = {} + for key, value in obj.__dict__.items(): + # 过滤掉以 _ 开头的私有属性 + if not key.startswith('_'): + # 递归调用 default 处理嵌套对象 + if isinstance(value, (datetime,)): + result[key] = self.default(value) + else: + result[key] = value + return result + + # 处理 dataclass + if hasattr(obj, '__dataclass_fields__'): + return {field: self.default(getattr(obj, field)) for field in obj.__dataclass_fields__} + + # 让基类处理其他类型 + return super().default(obj) From c102aea6bb25999afa74ea467b330f9c01966670 Mon Sep 17 00:00:00 2001 From: smock Date: Sat, 14 Feb 2026 15:21:47 +0800 Subject: [PATCH 4/6] =?UTF-8?q?=E4=B8=8D=E5=90=AF=E7=94=A8=E7=BC=93?= =?UTF-8?q?=E5=AD=98=E6=97=B6=E5=80=99=E4=B8=8D=E6=B8=85=E7=A9=BA=E6=A0=B9?= =?UTF-8?q?=E6=8D=AE=E7=94=A8=E6=88=B7ID=E6=B8=85=E9=99=A4=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E4=BF=A1=E6=81=AF=E7=BC=93=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ruoyi-fastapi-backend/module_admin/service/cache_service.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ruoyi-fastapi-backend/module_admin/service/cache_service.py b/ruoyi-fastapi-backend/module_admin/service/cache_service.py index 7c958914f..55f479961 100644 --- a/ruoyi-fastapi-backend/module_admin/service/cache_service.py +++ b/ruoyi-fastapi-backend/module_admin/service/cache_service.py @@ -135,6 +135,9 @@ async def clear_usercache_by_id(cls, request: Request, user_id: int): :param user_id: 用户ID :return: 操作缓存响应信息 """ + if not AppConfig.app_enable_user_cache: + return False + cache_key = f'{RedisInitKeyConfig.USER_INFO.key}:{user_id}' await request.app.state.redis.delete(cache_key) From 69b089449df5140ea9273aba7878d08920788738 Mon Sep 17 00:00:00 2001 From: smock Date: Sat, 14 Feb 2026 15:32:56 +0800 Subject: [PATCH 5/6] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=BA=86=E6=A0=BC?= =?UTF-8?q?=E5=BC=8F=E8=BF=87ruff?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module_admin/service/cache_service.py | 8 ++++---- .../module_admin/service/dept_service.py | 2 +- .../module_admin/service/login_service.py | 6 +++--- .../module_admin/service/menu_service.py | 2 +- .../module_admin/service/post_service.py | 2 +- .../module_admin/service/role_service.py | 2 +- .../module_admin/service/user_service.py | 2 +- 7 files changed, 12 insertions(+), 12 deletions(-) diff --git a/ruoyi-fastapi-backend/module_admin/service/cache_service.py b/ruoyi-fastapi-backend/module_admin/service/cache_service.py index 55f479961..28960ac06 100644 --- a/ruoyi-fastapi-backend/module_admin/service/cache_service.py +++ b/ruoyi-fastapi-backend/module_admin/service/cache_service.py @@ -1,8 +1,8 @@ from fastapi import Request -from config.env import AppConfig from common.enums import RedisInitKeyConfig from common.vo import CrudResponseModel +from config.env import AppConfig from config.get_redis import RedisUtil from module_admin.entity.vo.cache_vo import CacheInfoModel, CacheMonitorModel @@ -127,7 +127,7 @@ async def clear_cache_monitor_all_services(cls, request: Request) -> CrudRespons return CrudResponseModel(is_success=True, message='所有缓存清除成功') @classmethod - async def clear_usercache_by_id(cls, request: Request, user_id: int): + async def clear_usercache_by_id(cls, request: Request, user_id: int) -> bool: """ 根据用户ID清除用户信息缓存service @@ -137,14 +137,14 @@ async def clear_usercache_by_id(cls, request: Request, user_id: int): """ if not AppConfig.app_enable_user_cache: return False - + cache_key = f'{RedisInitKeyConfig.USER_INFO.key}:{user_id}' await request.app.state.redis.delete(cache_key) return True @classmethod - async def clear_usercache_all(cls, request: Request): + async def clear_usercache_all(cls, request: Request) -> bool: """ 清除所有用户信息缓存service diff --git a/ruoyi-fastapi-backend/module_admin/service/dept_service.py b/ruoyi-fastapi-backend/module_admin/service/dept_service.py index a8252d7e0..29fb87a58 100644 --- a/ruoyi-fastapi-backend/module_admin/service/dept_service.py +++ b/ruoyi-fastapi-backend/module_admin/service/dept_service.py @@ -11,8 +11,8 @@ from module_admin.dao.dept_dao import DeptDao from module_admin.entity.do.dept_do import SysDept from module_admin.entity.vo.dept_vo import DeleteDeptModel, DeptModel, DeptTreeModel -from utils.common_util import CamelCaseUtil from module_admin.service.cache_service import CacheService +from utils.common_util import CamelCaseUtil class DeptService: diff --git a/ruoyi-fastapi-backend/module_admin/service/login_service.py b/ruoyi-fastapi-backend/module_admin/service/login_service.py index e72bdf2a3..48230551e 100644 --- a/ruoyi-fastapi-backend/module_admin/service/login_service.py +++ b/ruoyi-fastapi-backend/module_admin/service/login_service.py @@ -25,13 +25,13 @@ from module_admin.entity.do.user_do import SysUser from module_admin.entity.vo.login_vo import MenuTreeModel, MetaModel, RouterModel, SmsCode, UserLogin, UserRegister from module_admin.entity.vo.user_vo import AddUserModel, CurrentUserModel, ResetUserModel, TokenData, UserInfoModel -from module_admin.service.user_service import UserService from module_admin.service.cache_service import CacheService +from module_admin.service.user_service import UserService from utils.common_util import CamelCaseUtil +from utils.json_util import ComplexEncoder from utils.log_util import logger from utils.message_util import message_service from utils.pwd_util import PwdUtil -from utils.json_util import ComplexEncoder oauth2_scheme = OAuth2PasswordBearer(tokenUrl='login') @@ -219,7 +219,7 @@ async def __get_user_by_id_cache(cls, request: Request, db: AsyncSession, user_i return None role_id_list = [item.role_id for item in query_user.get('user_role_info')] - if 1 in role_id_list: # noqa: SIM108 + if 1 in role_id_list: permissions = ['*:*:*'] else: permissions = [row.perms for row in query_user.get('user_menu_info') if row.perms] diff --git a/ruoyi-fastapi-backend/module_admin/service/menu_service.py b/ruoyi-fastapi-backend/module_admin/service/menu_service.py index f5c128a11..1f992df30 100644 --- a/ruoyi-fastapi-backend/module_admin/service/menu_service.py +++ b/ruoyi-fastapi-backend/module_admin/service/menu_service.py @@ -13,9 +13,9 @@ from module_admin.entity.vo.menu_vo import DeleteMenuModel, MenuModel, MenuQueryModel, MenuTreeModel from module_admin.entity.vo.role_vo import RoleMenuQueryModel from module_admin.entity.vo.user_vo import CurrentUserModel +from module_admin.service.cache_service import CacheService from utils.common_util import CamelCaseUtil from utils.string_util import StringUtil -from module_admin.service.cache_service import CacheService class MenuService: diff --git a/ruoyi-fastapi-backend/module_admin/service/post_service.py b/ruoyi-fastapi-backend/module_admin/service/post_service.py index 195a580a3..01aaba1ac 100644 --- a/ruoyi-fastapi-backend/module_admin/service/post_service.py +++ b/ruoyi-fastapi-backend/module_admin/service/post_service.py @@ -8,9 +8,9 @@ from exceptions.exception import ServiceException from module_admin.dao.post_dao import PostDao from module_admin.entity.vo.post_vo import DeletePostModel, PostModel, PostPageQueryModel +from module_admin.service.cache_service import CacheService from utils.common_util import CamelCaseUtil from utils.excel_util import ExcelUtil -from module_admin.service.cache_service import CacheService class PostService: diff --git a/ruoyi-fastapi-backend/module_admin/service/role_service.py b/ruoyi-fastapi-backend/module_admin/service/role_service.py index 9f14d86d3..ccbd8556e 100644 --- a/ruoyi-fastapi-backend/module_admin/service/role_service.py +++ b/ruoyi-fastapi-backend/module_admin/service/role_service.py @@ -19,9 +19,9 @@ RolePageQueryModel, ) from module_admin.entity.vo.user_vo import UserInfoModel, UserRolePageQueryModel +from module_admin.service.cache_service import CacheService from utils.common_util import CamelCaseUtil from utils.excel_util import ExcelUtil -from module_admin.service.cache_service import CacheService class RoleService: diff --git a/ruoyi-fastapi-backend/module_admin/service/user_service.py b/ruoyi-fastapi-backend/module_admin/service/user_service.py index 1f458f354..9478552d7 100644 --- a/ruoyi-fastapi-backend/module_admin/service/user_service.py +++ b/ruoyi-fastapi-backend/module_admin/service/user_service.py @@ -32,11 +32,11 @@ UserRoleResponseModel, UserRowModel, ) +from module_admin.service.cache_service import CacheService from module_admin.service.config_service import ConfigService from module_admin.service.dept_service import DeptService from module_admin.service.post_service import PostService from module_admin.service.role_service import RoleService -from module_admin.service.cache_service import CacheService from utils.common_util import CamelCaseUtil from utils.excel_util import ExcelUtil from utils.pwd_util import PwdUtil From 0ba70ff7eb360fbe13c8bd3d550c7c310ed7e6ba Mon Sep 17 00:00:00 2001 From: smock Date: Sat, 14 Feb 2026 16:00:59 +0800 Subject: [PATCH 6/6] fix bug --- .gitignore | 2 +- ruoyi-fastapi-backend/module_admin/service/user_service.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index d9da83503..8365d1baf 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -.env.dev +ruoyi-fastapi-backend/.env.dev # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] diff --git a/ruoyi-fastapi-backend/module_admin/service/user_service.py b/ruoyi-fastapi-backend/module_admin/service/user_service.py index 9478552d7..91462dcdd 100644 --- a/ruoyi-fastapi-backend/module_admin/service/user_service.py +++ b/ruoyi-fastapi-backend/module_admin/service/user_service.py @@ -269,8 +269,8 @@ async def delete_user_services( await UserDao.delete_user_role_dao(query_db, UserRoleModel(**user_id_dict)) await UserDao.delete_user_post_dao(query_db, UserPostModel(**user_id_dict)) await UserDao.delete_user_dao(query_db, UserModel(**user_id_dict)) + await CacheService.clear_usercache_by_id(request, user_id) await query_db.commit() - await CacheService.clear_usercache_by_id(request, page_object.user_id) return CrudResponseModel(is_success=True, message='删除成功') except Exception as e: await query_db.rollback()