diff --git a/ruoyi-fastapi-backend/common/annotation/cache_annotation.py b/ruoyi-fastapi-backend/common/annotation/cache_annotation.py new file mode 100644 index 0000000..b7851b5 --- /dev/null +++ b/ruoyi-fastapi-backend/common/annotation/cache_annotation.py @@ -0,0 +1,563 @@ +import hashlib +import inspect +import json +from collections.abc import Awaitable, Callable, Sequence +from datetime import datetime +from functools import wraps +from typing import Any, TypeVar + +from fastapi import Request +from fastapi.encoders import jsonable_encoder +from fastapi.responses import JSONResponse, ORJSONResponse, Response, StreamingResponse, UJSONResponse +from redis import asyncio as aioredis +from typing_extensions import ParamSpec + +from common.constant import HttpStatusConstant +from common.context import RequestContext +from common.enums import RedisInitKeyConfig +from exceptions.exception import LoginException +from utils.log_util import logger + +P = ParamSpec('P') +R = TypeVar('R') + + +class ApiCacheManager: + """ + 接口缓存键与命名空间管理工具类 + """ + + @classmethod + async def clear_namespace(cls, redis: aioredis.Redis, namespace: str) -> int: + """ + 清理指定命名空间下的接口缓存 + + :param redis: Redis连接对象 + :param namespace: 缓存命名空间 + :return: 删除的缓存数量 + """ + return await cls._clear_by_pattern(redis, cls.build_namespace_pattern(namespace)) + + @classmethod + async def clear_all(cls, redis: aioredis.Redis) -> int: + """ + 清理所有接口缓存 + + :param redis: Redis连接对象 + :return: 删除的缓存数量 + """ + return await cls._clear_by_pattern(redis, cls.build_namespace_pattern('*')) + + @classmethod + async def clear_namespaces(cls, redis: aioredis.Redis, namespaces: list[str] | tuple[str, ...] | set[str]) -> int: + """ + 批量清理多个命名空间下的接口缓存 + + :param redis: Redis连接对象 + :param namespaces: 需要清理的缓存命名空间列表 + :return: 删除的缓存数量 + """ + deleted_count = 0 + for namespace in set(namespaces): + deleted_count += await cls.clear_namespace(redis, namespace) + + return deleted_count + + @classmethod + async def clear_namespace_prefix(cls, redis: aioredis.Redis, namespace_prefix: str) -> int: + """ + 按命名空间前缀清理接口缓存 + + :param redis: Redis连接对象 + :param namespace_prefix: 缓存命名空间前缀 + :return: 删除的缓存数量 + """ + return await cls._clear_by_pattern(redis, cls.build_namespace_prefix_pattern(namespace_prefix)) + + @classmethod + async def clear_namespace_prefixes( + cls, + redis: aioredis.Redis, + namespace_prefixes: list[str] | tuple[str, ...] | set[str], + ) -> int: + """ + 批量按命名空间前缀清理接口缓存 + + :param redis: Redis连接对象 + :param namespace_prefixes: 需要清理的缓存命名空间前缀列表 + :return: 删除的缓存数量 + """ + deleted_count = 0 + for namespace_prefix in set(namespace_prefixes): + deleted_count += await cls.clear_namespace_prefix(redis, namespace_prefix) + + return deleted_count + + @classmethod + def build_namespace_pattern(cls, namespace: str) -> str: + """ + 生成命名空间扫描表达式 + + :param namespace: 缓存命名空间 + :return: 缓存键扫描匹配模式 + """ + return f'{RedisInitKeyConfig.API_CACHE.key}:{namespace}:*' + + @classmethod + def build_namespace_prefix_pattern(cls, namespace_prefix: str) -> str: + """ + 生成命名空间前缀扫描表达式 + + :param namespace_prefix: 缓存命名空间前缀 + :return: 缓存键扫描匹配模式 + """ + return f'{RedisInitKeyConfig.API_CACHE.key}:{namespace_prefix}*' + + @classmethod + async def _clear_by_pattern(cls, redis: aioredis.Redis, pattern: str) -> int: + """ + 根据扫描表达式清理匹配的接口缓存 + + :param redis: Redis连接对象 + :param pattern: 缓存键扫描匹配模式 + :return: 删除的缓存数量 + """ + cache_keys = [key async for key in redis.scan_iter(match=pattern)] + if not cache_keys: + return 0 + + return await redis.delete(*cache_keys) + + +class _ApiCacheSupport: + """ + 接口缓存装饰器共用工具基类 + """ + + def _get_request(self, func: Callable[P, Awaitable[R]], *args: P.args, **kwargs: P.kwargs) -> Request | None: + """ + 从被装饰函数的入参中提取Request对象 + + :param func: 被装饰的异步接口函数 + :param args: 位置参数 + :param kwargs: 关键字参数 + :return: Request对象,未找到时返回None + """ + signature = inspect.signature(func) + bound_arguments = signature.bind_partial(*args, **kwargs) + for argument in bound_arguments.arguments.values(): + if isinstance(argument, Request): + return argument + + return None + + def _get_redis_client(self, request: Request, skip_message: str) -> aioredis.Redis | None: + """ + 从应用状态中获取Redis连接 + + :param request: 当前请求对象 + :param skip_message: 未初始化Redis连接时的日志信息 + :return: Redis连接对象,未初始化时返回None + """ + redis = getattr(request.app.state, 'redis', None) + if redis is None: + logger.warning(skip_message) + + return redis + + def _resolve_request_redis( + self, + func: Callable[P, Awaitable[R]], + skip_message: str, + *args: P.args, + **kwargs: P.kwargs, + ) -> tuple[Request | None, aioredis.Redis | None]: + """ + 从被装饰函数入参中同时解析Request与Redis连接 + + :param func: 被装饰的异步接口函数 + :param skip_message: 未初始化Redis连接时的日志信息 + :param args: 位置参数 + :param kwargs: 关键字参数 + :return: Request对象与Redis连接对象组成的元组 + """ + request = self._get_request(func, *args, **kwargs) + if request is None: + return None, None + + return request, self._get_redis_client(request, skip_message) + + def _load_json_content(self, response_body: str) -> Any | None: + """ + 解析JSON响应体内容 + + :param response_body: JSON字符串响应体 + :return: 解析后的Python对象,解析失败时返回None + """ + try: + return json.loads(response_body) + except json.JSONDecodeError: + return None + + def _extract_json_response_content(self, result: Any) -> Any | None: + """ + 提取响应中的JSON内容 + + :param result: 原始接口返回结果 + :return: 解析后的JSON内容,无法提取时返回None + """ + if result is None or isinstance(result, StreamingResponse): + return None + + if isinstance(result, (JSONResponse, ORJSONResponse, UJSONResponse)): + return self._load_json_content(result.body.decode('utf-8')) + + if isinstance(result, Response): + return None + + return jsonable_encoder(result) + + def _match_response_codes(self, response_content: Any, response_codes: set[int]) -> bool: + """ + 判断响应内容中的业务响应码是否匹配 + + :param response_content: JSON响应内容 + :param response_codes: 允许的业务响应码集合 + :return: 是否匹配 + """ + if not isinstance(response_content, dict): + return True + + response_code = response_content.get('code') + if response_code is None: + return True + + return response_code in response_codes + + def _get_matched_response_content(self, result: Any, response_codes: set[int]) -> Any | None: + """ + 提取并校验满足业务响应码要求的JSON响应内容 + + :param result: 原始接口返回结果 + :param response_codes: 允许的业务响应码集合 + :return: 匹配成功的JSON响应内容,不匹配时返回None + """ + response_content = self._extract_json_response_content(result) + if response_content is None or not self._match_response_codes(response_content, response_codes): + return None + + return response_content + + +class ApiCache(_ApiCacheSupport): + """ + 接口缓存装饰器,仅用于幂等且返回JSON的接口 + """ + + def __init__( + self, + namespace: str, + expire_seconds: int = 10, + vary_by_user: bool = True, + methods: Sequence[str] | None = None, + cache_response_codes: set[int] | None = None, + ) -> None: + """ + 初始化接口缓存装饰器 + + :param namespace: 缓存命名空间,用于区分不同接口类型 + :param expire_seconds: 缓存过期时间,单位秒 + :param vary_by_user: 是否按当前登录用户隔离缓存 + :param methods: 允许启用缓存的HTTP方法列表,为None时默认仅缓存GET请求 + :param cache_response_codes: 允许缓存的业务响应码,为None时默认仅缓存成功响应 + """ + self.namespace = namespace + self.expire_seconds = expire_seconds + self.vary_by_user = vary_by_user + self.methods = tuple(dict.fromkeys(method.upper() for method in methods)) if methods else ('GET',) + self.cache_response_codes = ( + cache_response_codes if cache_response_codes is not None else {HttpStatusConstant.SUCCESS} + ) + + def __call__(self, func: Callable[P, Awaitable[R]]) -> Callable[P, Awaitable[R]]: + """ + 为目标异步接口函数增加接口缓存能力 + + :param func: 需要被缓存的异步接口函数 + :return: 包装后的异步接口函数 + """ + + @wraps(func) + async def wrapper(*args: P.args, **kwargs: P.kwargs) -> R: + request, redis = self._resolve_request_redis( + func, '当前应用未初始化Redis连接,跳过接口缓存', *args, **kwargs + ) + if request is None or redis is None: + return await func(*args, **kwargs) + if not self._is_request_method_allowed(request): + return await func(*args, **kwargs) + + cache_key = await self._build_cache_key(request) + cached_response = await redis.get(cache_key) + if cached_response: + return self._build_cached_response(cached_response) # type: ignore[return-value] + + result = await func(*args, **kwargs) + await self._cache_response(redis, cache_key, result) + self._append_cache_header(result, cache_status='MISS') + + return result + + return wrapper + + def _is_request_method_allowed(self, request: Request) -> bool: + """ + 判断当前请求方法是否允许启用接口缓存 + + :param request: 当前请求对象 + :return: 是否允许缓存 + """ + return request.method.upper() in self.methods + + async def _build_cache_key(self, request: Request) -> str: + """ + 根据当前请求生成稳定缓存键 + + :param request: 当前请求对象 + :return: 接口缓存键 + """ + request_body = await request.body() + user_scope = self._get_user_scope(request) if self.vary_by_user else '' + key_material = { + 'method': request.method, + 'path': request.url.path, + 'path_params': dict(sorted(request.path_params.items())), + 'query_params': sorted(request.query_params.multi_items()), + 'body_digest': hashlib.sha256(request_body).hexdigest() if request_body else '', + 'user_scope': user_scope, + } + key_digest = hashlib.sha256( + json.dumps( + key_material, + ensure_ascii=False, + sort_keys=True, + separators=(',', ':'), + default=str, + ).encode('utf-8') + ).hexdigest() + + return f'{RedisInitKeyConfig.API_CACHE.key}:{self.namespace}:{key_digest}' + + def _get_user_scope(self, request: Request) -> str: + """ + 获取用户隔离维度 + + 优先使用当前登录用户ID,未登录时回退到Authorization摘要。 + + :param request: 当前请求对象 + :return: 用户隔离字符串 + """ + try: + current_user = RequestContext.get_current_user() + return str(current_user.user.user_id) + except LoginException: + authorization = request.headers.get('Authorization', '') + return hashlib.sha256(authorization.encode('utf-8')).hexdigest() if authorization else '' + + def _serialize_response(self, result: Any) -> str | None: + """ + 将响应对象序列化为可存入缓存的字符串 + + :param result: 原始接口返回结果 + :return: 序列化后的缓存内容,不可缓存时返回None + """ + response_payload = self._extract_response_payload(result) + if response_payload is None: + return None + + return json.dumps(response_payload, ensure_ascii=False) + + def _extract_response_payload(self, result: Any) -> dict[str, Any] | None: + """ + 提取可缓存的响应载荷 + + :param result: 原始接口返回结果 + :return: 可缓存的响应载荷,不可缓存时返回None + """ + if isinstance(result, StreamingResponse): + return None + + if isinstance(result, (JSONResponse, ORJSONResponse, UJSONResponse)): + # 命中缓存时无法安全复放后台任务,因此带有后台任务的响应不参与缓存 + if getattr(result, 'background', None) is not None: + return None + + content = self._get_matched_response_content(result, self.cache_response_codes) + if content is None: + return None + + return { + 'content': content, + 'status_code': result.status_code, + 'media_type': result.media_type, + 'headers': self._filter_response_headers(result.headers), + } + + if isinstance(result, Response): + return None + + json_content = self._get_matched_response_content(result, self.cache_response_codes) + if json_content is None: + return None + + return { + 'content': json_content, + 'status_code': HttpStatusConstant.SUCCESS, + 'media_type': 'application/json', + 'headers': {}, + } + + def _build_cached_response(self, cached_response: str) -> JSONResponse: + """ + 根据缓存内容重建JSON响应对象 + + :param cached_response: 缓存中读取到的响应字符串 + :return: 重建后的JSON响应对象 + """ + cached_payload = json.loads(cached_response) + cached_content = self._refresh_response_time(cached_payload['content']) + response = JSONResponse( + status_code=cached_payload['status_code'], + content=jsonable_encoder(cached_content), + headers=cached_payload.get('headers'), + media_type=cached_payload.get('media_type'), + ) + response.headers['X-Api-Cache'] = 'HIT' + + return response + + async def _cache_response(self, redis: aioredis.Redis, cache_key: str, result: Any) -> None: + """ + 将接口响应写入接口缓存 + + :param redis: Redis连接对象 + :param cache_key: 接口缓存键 + :param result: 原始接口返回结果 + :return: None + """ + serialized_response = self._serialize_response(result) + if serialized_response is None: + return + + await redis.set(cache_key, serialized_response, ex=self.expire_seconds) + logger.debug(f'接口缓存写入成功: {cache_key}') + + def _append_cache_header(self, result: Any, cache_status: str) -> None: + """ + 为响应对象追加接口缓存命中状态响应头 + + :param result: 原始接口返回结果 + :param cache_status: 缓存状态标识 + :return: None + """ + if isinstance(result, Response): + result.headers['X-Api-Cache'] = cache_status + + def _filter_response_headers(self, headers: dict[str, str]) -> dict[str, str]: + """ + 过滤不适合直接回放的响应头 + + :param headers: 原始响应头 + :return: 过滤后的响应头 + """ + excluded_headers = {'content-length', 'content-type', 'set-cookie'} + return {key: value for key, value in headers.items() if key.lower() not in excluded_headers} + + def _refresh_response_time(self, response_content: Any) -> Any: + """ + 刷新项目统一响应体中的time字段 + + :param response_content: JSON响应内容 + :return: 刷新time后的响应内容 + """ + if not isinstance(response_content, dict): + return response_content + + if {'code', 'msg', 'success', 'time'}.issubset(response_content): + refreshed_content = response_content.copy() + refreshed_content['time'] = datetime.now() + return refreshed_content + + return response_content + + +class ApiCacheEvict(_ApiCacheSupport): + """ + 接口缓存失效装饰器,用于在写操作成功后统一清理相关缓存 + """ + + def __init__( + self, + namespaces: Sequence[str] | None = None, + namespace_prefixes: Sequence[str] | None = None, + evict_response_codes: set[int] | None = None, + ) -> None: + """ + 初始化接口缓存失效装饰器 + + :param namespaces: 需要失效的缓存命名空间列表 + :param namespace_prefixes: 需要失效的缓存命名空间前缀列表 + :param evict_response_codes: 允许触发失效的业务响应码,默认为仅成功响应触发 + """ + self.namespaces = tuple(dict.fromkeys(namespaces or ())) + self.namespace_prefixes = tuple(dict.fromkeys(namespace_prefixes or ())) + if not self.namespaces and not self.namespace_prefixes: + raise ValueError('ApiCacheEvict至少需要指定namespaces或namespace_prefixes') + + self.evict_response_codes = ( + evict_response_codes if evict_response_codes is not None else {HttpStatusConstant.SUCCESS} + ) + + def __call__(self, func: Callable[P, Awaitable[R]]) -> Callable[P, Awaitable[R]]: + """ + 为目标异步接口函数增加缓存失效能力 + + :param func: 需要在成功后触发缓存失效的异步接口函数 + :return: 包装后的异步接口函数 + """ + + @wraps(func) + async def wrapper(*args: P.args, **kwargs: P.kwargs) -> R: + result = await func(*args, **kwargs) + _, redis = self._resolve_request_redis(func, '当前应用未初始化Redis连接,跳过接口缓存失效', *args, **kwargs) + if redis is None: + return result + + if self._should_evict(result): + await self._evict_cache(redis) + + return result + + return wrapper + + async def _evict_cache(self, redis: aioredis.Redis) -> None: + """ + 根据配置统一执行接口缓存失效 + + :param redis: Redis连接对象 + :return: None + """ + if self.namespaces: + await ApiCacheManager.clear_namespaces(redis, self.namespaces) + + if self.namespace_prefixes: + await ApiCacheManager.clear_namespace_prefixes(redis, self.namespace_prefixes) + + def _should_evict(self, result: Any) -> bool: + """ + 判断当前响应是否应触发缓存失效 + + :param result: 原始接口返回结果 + :return: 是否触发缓存失效 + """ + return self._get_matched_response_content(result, self.evict_response_codes) is not None diff --git a/ruoyi-fastapi-backend/common/constant.py b/ruoyi-fastapi-backend/common/constant.py index 8323c6a..932b01b 100644 --- a/ruoyi-fastapi-backend/common/constant.py +++ b/ruoyi-fastapi-backend/common/constant.py @@ -143,6 +143,268 @@ class LockConstant: LOCK_RENEWAL_INTERVAL = 20 +class CacheNamespace: + """ + 接口缓存命名空间常量 + + MONITOR_SERVER_INFO: 服务监控信息缓存 + MONITOR_JOB_LIST: 定时任务分页列表缓存 + MONITOR_JOB_DETAIL: 定时任务详情缓存 + LOGIN_USER_INFO: 登录用户信息缓存 + LOGIN_USER_ROUTERS: 登录用户路由缓存 + SYSTEM_MENU_TREE: 菜单树缓存 + SYSTEM_MENU_ROLE_TREE: 角色菜单树缓存 + SYSTEM_MENU_LIST: 菜单分页列表缓存 + SYSTEM_MENU_DETAIL: 菜单详情缓存 + SYSTEM_DEPT_EDIT_TREE: 部门编辑树缓存 + SYSTEM_DEPT_LIST: 部门列表缓存 + SYSTEM_DEPT_DETAIL: 部门详情缓存 + SYSTEM_POST_LIST: 岗位列表缓存 + SYSTEM_POST_DETAIL: 岗位详情缓存 + SYSTEM_NOTICE_LIST: 通知公告列表缓存 + SYSTEM_NOTICE_DETAIL: 通知公告详情缓存 + SYSTEM_ROLE_DEPT_TREE: 角色部门树缓存 + SYSTEM_ROLE_LIST: 角色列表缓存 + SYSTEM_ROLE_DETAIL: 角色详情缓存 + SYSTEM_ROLE_ALLOCATED_USER_LIST: 已分配用户角色列表缓存 + SYSTEM_ROLE_UNALLOCATED_USER_LIST: 未分配用户角色列表缓存 + SYSTEM_USER_DEPT_TREE: 用户部门树缓存 + SYSTEM_USER_LIST: 用户列表缓存 + SYSTEM_USER_PROFILE: 用户个人信息缓存 + SYSTEM_USER_DETAIL: 用户详情缓存 + SYSTEM_CONFIG_LIST: 参数配置列表缓存 + SYSTEM_CONFIG_DETAIL: 参数配置详情缓存 + SYSTEM_DICT_TYPE_LIST: 字典类型列表缓存 + SYSTEM_DICT_TYPE_OPTIONS: 字典类型选项缓存 + SYSTEM_DICT_TYPE_DETAIL: 字典类型详情缓存 + SYSTEM_DICT_DATA_LIST: 字典数据列表缓存 + SYSTEM_DICT_DATA_DETAIL: 字典数据详情缓存 + AI_MODEL_LIST: AI模型列表缓存 + AI_MODEL_ALL: AI模型全量列表缓存 + AI_MODEL_DETAIL: AI模型详情缓存 + AI_CHAT_CONFIG: AI对话配置缓存 + TOOL_GEN_LIST: 代码生成列表缓存 + TOOL_GEN_DB_LIST: 代码生成数据源列表缓存 + TOOL_GEN_DETAIL: 代码生成详情缓存 + TOOL_GEN_PREVIEW: 代码生成预览缓存 + """ + + MONITOR_SERVER_INFO = 'monitor:server:info' + MONITOR_JOB_LIST = 'monitor:job:list' + MONITOR_JOB_DETAIL = 'monitor:job:detail' + + LOGIN_USER_INFO = 'login:user:info' + LOGIN_USER_ROUTERS = 'login:user:routers' + + SYSTEM_MENU_TREE = 'system:menu:tree' + SYSTEM_MENU_ROLE_TREE = 'system:menu:role-tree' + SYSTEM_MENU_LIST = 'system:menu:list' + SYSTEM_MENU_DETAIL = 'system:menu:detail' + + SYSTEM_DEPT_EDIT_TREE = 'system:dept:edit-tree' + SYSTEM_DEPT_LIST = 'system:dept:list' + SYSTEM_DEPT_DETAIL = 'system:dept:detail' + + SYSTEM_POST_LIST = 'system:post:list' + SYSTEM_POST_DETAIL = 'system:post:detail' + + SYSTEM_NOTICE_LIST = 'system:notice:list' + SYSTEM_NOTICE_DETAIL = 'system:notice:detail' + + SYSTEM_ROLE_DEPT_TREE = 'system:role:dept-tree' + SYSTEM_ROLE_LIST = 'system:role:list' + SYSTEM_ROLE_DETAIL = 'system:role:detail' + SYSTEM_ROLE_ALLOCATED_USER_LIST = 'system:role:allocated-user-list' + SYSTEM_ROLE_UNALLOCATED_USER_LIST = 'system:role:unallocated-user-list' + + SYSTEM_USER_DEPT_TREE = 'system:user:dept-tree' + SYSTEM_USER_LIST = 'system:user:list' + SYSTEM_USER_PROFILE = 'system:user:profile' + SYSTEM_USER_DETAIL = 'system:user:detail' + + SYSTEM_CONFIG_LIST = 'system:config:list' + SYSTEM_CONFIG_DETAIL = 'system:config:detail' + + SYSTEM_DICT_TYPE_LIST = 'system:dict:type-list' + SYSTEM_DICT_TYPE_OPTIONS = 'system:dict:type-options' + SYSTEM_DICT_TYPE_DETAIL = 'system:dict:type-detail' + SYSTEM_DICT_DATA_LIST = 'system:dict:data-list' + SYSTEM_DICT_DATA_DETAIL = 'system:dict:data-detail' + + AI_MODEL_LIST = 'ai:model:list' + AI_MODEL_ALL = 'ai:model:all' + AI_MODEL_DETAIL = 'ai:model:detail' + AI_CHAT_CONFIG = 'ai:chat:config' + + TOOL_GEN_LIST = 'tool:gen:list' + TOOL_GEN_DB_LIST = 'tool:gen:db-list' + TOOL_GEN_DETAIL = 'tool:gen:detail' + TOOL_GEN_PREVIEW = 'tool:gen:preview' + + +class CacheGroup: + """ + 接口缓存失效分组常量 + + PERMISSION_MUTATION: 权限及菜单视图关联缓存失效基础分组 + DATA_SCOPE_MUTATION: 数据范围相关视图关联缓存失效基础分组 + MENU_MUTATION: 菜单写操作关联缓存失效分组 + JOB_MUTATION: 定时任务写操作关联缓存失效分组 + POST_MUTATION: 岗位写操作关联缓存失效分组 + NOTICE_MUTATION: 通知公告写操作关联缓存失效分组 + ROLE_ENTITY_MUTATION: 角色实体信息变更关联缓存失效分组 + ROLE_PERMISSION_MUTATION: 角色权限变更关联缓存失效分组 + ROLE_MUTATION: 角色通用写操作关联缓存失效组合分组 + USER_ENTITY_MUTATION: 用户实体信息变更关联缓存失效分组 + USER_PERMISSION_MUTATION: 用户权限变更关联缓存失效分组 + USER_INFO_MUTATION: 用户资料与安全相关写操作关联缓存失效分组 + LOGIN_SUCCESS_MUTATION: 登录成功后关联缓存失效分组 + LOGOUT_MUTATION: 登出后关联缓存失效分组 + CONFIG_MUTATION: 参数配置写操作关联缓存失效分组 + DICT_TYPE_MUTATION: 字典类型写操作关联缓存失效分组 + DICT_DATA_MUTATION: 字典数据写操作关联缓存失效分组 + AI_MODEL_MUTATION: AI模型写操作关联缓存失效分组 + AI_CHAT_CONFIG_MUTATION: AI对话配置写操作关联缓存失效分组 + GEN_MUTATION: 代码生成写操作关联缓存失效分组 + """ + + PERMISSION_MUTATION = ( + CacheNamespace.LOGIN_USER_INFO, + CacheNamespace.LOGIN_USER_ROUTERS, + CacheNamespace.SYSTEM_MENU_TREE, + CacheNamespace.SYSTEM_MENU_ROLE_TREE, + CacheNamespace.SYSTEM_MENU_LIST, + ) + + DATA_SCOPE_MUTATION = ( + CacheNamespace.LOGIN_USER_INFO, + CacheNamespace.SYSTEM_DEPT_EDIT_TREE, + CacheNamespace.SYSTEM_DEPT_LIST, + CacheNamespace.SYSTEM_DEPT_DETAIL, + CacheNamespace.SYSTEM_ROLE_DEPT_TREE, + CacheNamespace.SYSTEM_ROLE_LIST, + CacheNamespace.SYSTEM_ROLE_DETAIL, + CacheNamespace.SYSTEM_ROLE_ALLOCATED_USER_LIST, + CacheNamespace.SYSTEM_ROLE_UNALLOCATED_USER_LIST, + CacheNamespace.SYSTEM_USER_DEPT_TREE, + CacheNamespace.SYSTEM_USER_LIST, + CacheNamespace.SYSTEM_USER_DETAIL, + CacheNamespace.SYSTEM_USER_PROFILE, + CacheNamespace.AI_MODEL_LIST, + CacheNamespace.AI_MODEL_ALL, + CacheNamespace.AI_MODEL_DETAIL, + ) + + MENU_MUTATION = ( + *PERMISSION_MUTATION, + CacheNamespace.SYSTEM_MENU_DETAIL, + ) + + JOB_MUTATION = ( + CacheNamespace.MONITOR_JOB_LIST, + CacheNamespace.MONITOR_JOB_DETAIL, + ) + + POST_MUTATION = ( + CacheNamespace.SYSTEM_POST_LIST, + CacheNamespace.SYSTEM_POST_DETAIL, + CacheNamespace.SYSTEM_USER_DETAIL, + CacheNamespace.SYSTEM_USER_PROFILE, + ) + + NOTICE_MUTATION = ( + CacheNamespace.SYSTEM_NOTICE_LIST, + CacheNamespace.SYSTEM_NOTICE_DETAIL, + ) + + ROLE_ENTITY_MUTATION = ( + CacheNamespace.SYSTEM_ROLE_DEPT_TREE, + CacheNamespace.SYSTEM_ROLE_LIST, + CacheNamespace.SYSTEM_ROLE_DETAIL, + CacheNamespace.SYSTEM_ROLE_ALLOCATED_USER_LIST, + CacheNamespace.SYSTEM_ROLE_UNALLOCATED_USER_LIST, + CacheNamespace.SYSTEM_MENU_ROLE_TREE, + CacheNamespace.SYSTEM_USER_DETAIL, + ) + + ROLE_PERMISSION_MUTATION = ( + *ROLE_ENTITY_MUTATION, + CacheNamespace.SYSTEM_USER_PROFILE, + CacheNamespace.LOGIN_USER_INFO, + *PERMISSION_MUTATION, + ) + + ROLE_MUTATION = ( + *ROLE_PERMISSION_MUTATION, + *DATA_SCOPE_MUTATION, + ) + + USER_ENTITY_MUTATION = ( + CacheNamespace.SYSTEM_USER_LIST, + CacheNamespace.SYSTEM_USER_DETAIL, + CacheNamespace.SYSTEM_ROLE_ALLOCATED_USER_LIST, + CacheNamespace.SYSTEM_ROLE_UNALLOCATED_USER_LIST, + ) + + USER_PERMISSION_MUTATION = ( + *DATA_SCOPE_MUTATION, + *PERMISSION_MUTATION, + ) + + USER_INFO_MUTATION = ( + CacheNamespace.SYSTEM_USER_LIST, + CacheNamespace.SYSTEM_USER_DETAIL, + CacheNamespace.SYSTEM_USER_PROFILE, + CacheNamespace.LOGIN_USER_INFO, + ) + + LOGIN_SUCCESS_MUTATION = ( + CacheNamespace.SYSTEM_USER_LIST, + CacheNamespace.LOGIN_USER_INFO, + CacheNamespace.LOGIN_USER_ROUTERS, + CacheNamespace.SYSTEM_USER_PROFILE, + CacheNamespace.SYSTEM_USER_DETAIL, + ) + + LOGOUT_MUTATION = ( + CacheNamespace.LOGIN_USER_INFO, + CacheNamespace.LOGIN_USER_ROUTERS, + ) + + CONFIG_MUTATION = ( + CacheNamespace.SYSTEM_CONFIG_LIST, + CacheNamespace.SYSTEM_CONFIG_DETAIL, + ) + + DICT_TYPE_MUTATION = ( + CacheNamespace.SYSTEM_DICT_TYPE_LIST, + CacheNamespace.SYSTEM_DICT_TYPE_OPTIONS, + CacheNamespace.SYSTEM_DICT_TYPE_DETAIL, + CacheNamespace.SYSTEM_DICT_DATA_LIST, + CacheNamespace.SYSTEM_DICT_DATA_DETAIL, + ) + + DICT_DATA_MUTATION = ( + CacheNamespace.SYSTEM_DICT_DATA_LIST, + CacheNamespace.SYSTEM_DICT_DATA_DETAIL, + ) + + AI_MODEL_MUTATION = ( + CacheNamespace.AI_MODEL_LIST, + CacheNamespace.AI_MODEL_ALL, + CacheNamespace.AI_MODEL_DETAIL, + ) + + AI_CHAT_CONFIG_MUTATION = (CacheNamespace.AI_CHAT_CONFIG,) + + GEN_MUTATION = ( + CacheNamespace.TOOL_GEN_LIST, + CacheNamespace.TOOL_GEN_DB_LIST, + CacheNamespace.TOOL_GEN_DETAIL, + CacheNamespace.TOOL_GEN_PREVIEW, + ) + + class MenuConstant: """ 菜单常量 diff --git a/ruoyi-fastapi-backend/common/enums.py b/ruoyi-fastapi-backend/common/enums.py index 3ce6e32..1e0868c 100644 --- a/ruoyi-fastapi-backend/common/enums.py +++ b/ruoyi-fastapi-backend/common/enums.py @@ -45,6 +45,7 @@ def remark(self) -> str | None: ACCESS_TOKEN = {'key': 'access_token', 'remark': '登录令牌信息'} SYS_DICT = {'key': 'sys_dict', 'remark': '数据字典'} SYS_CONFIG = {'key': 'sys_config', 'remark': '配置信息'} + API_CACHE = {'key': 'api_cache', 'remark': '接口响应缓存'} CAPTCHA_CODES = {'key': 'captcha_codes', 'remark': '图片验证码'} ACCOUNT_LOCK = {'key': 'account_lock', 'remark': '用户锁定'} PASSWORD_ERROR_COUNT = {'key': 'password_error_count', 'remark': '密码错误次数'} diff --git a/ruoyi-fastapi-backend/module_admin/controller/config_controller.py b/ruoyi-fastapi-backend/module_admin/controller/config_controller.py index dddd29b..7da4c80 100644 --- a/ruoyi-fastapi-backend/module_admin/controller/config_controller.py +++ b/ruoyi-fastapi-backend/module_admin/controller/config_controller.py @@ -6,10 +6,12 @@ from pydantic_validation_decorator import ValidateFields from sqlalchemy.ext.asyncio import AsyncSession +from common.annotation.cache_annotation import ApiCache, ApiCacheEvict from common.annotation.log_annotation import Log from common.aspect.db_seesion import DBSessionDependency from common.aspect.interface_auth import UserInterfaceAuthDependency from common.aspect.pre_auth import CurrentUserDependency, PreAuthDependency +from common.constant import CacheGroup, CacheNamespace from common.enums import BusinessType from common.router import APIRouterPro from common.vo import DataResponseModel, PageResponseModel, ResponseBaseModel @@ -32,6 +34,7 @@ response_model=PageResponseModel[ConfigModel], dependencies=[UserInterfaceAuthDependency('system:config:list')], ) +@ApiCache(namespace=CacheNamespace.SYSTEM_CONFIG_LIST) async def get_system_config_list( request: Request, config_page_query: Annotated[ConfigPageQueryModel, Query()], @@ -52,6 +55,7 @@ async def get_system_config_list( dependencies=[UserInterfaceAuthDependency('system:config:add')], ) @ValidateFields(validate_model='add_config') +@ApiCacheEvict(namespaces=CacheGroup.CONFIG_MUTATION) @Log(title='参数管理', business_type=BusinessType.INSERT) async def add_system_config( request: Request, @@ -77,6 +81,7 @@ async def add_system_config( dependencies=[UserInterfaceAuthDependency('system:config:edit')], ) @ValidateFields(validate_model='edit_config') +@ApiCacheEvict(namespaces=CacheGroup.CONFIG_MUTATION) @Log(title='参数管理', business_type=BusinessType.UPDATE) async def edit_system_config( request: Request, @@ -117,6 +122,7 @@ async def refresh_system_config( response_model=ResponseBaseModel, dependencies=[UserInterfaceAuthDependency('system:config:remove')], ) +@ApiCacheEvict(namespaces=CacheGroup.CONFIG_MUTATION) @Log(title='参数管理', business_type=BusinessType.DELETE) async def delete_system_config( request: Request, @@ -137,6 +143,7 @@ async def delete_system_config( response_model=DataResponseModel[ConfigModel], dependencies=[UserInterfaceAuthDependency('system:config:query')], ) +@ApiCache(namespace=CacheNamespace.SYSTEM_CONFIG_DETAIL) async def query_detail_system_config( request: Request, config_id: Annotated[int, Path(description='参数主键')], diff --git a/ruoyi-fastapi-backend/module_admin/controller/dept_controller.py b/ruoyi-fastapi-backend/module_admin/controller/dept_controller.py index cbae112..942e326 100644 --- a/ruoyi-fastapi-backend/module_admin/controller/dept_controller.py +++ b/ruoyi-fastapi-backend/module_admin/controller/dept_controller.py @@ -6,11 +6,13 @@ from sqlalchemy import ColumnElement from sqlalchemy.ext.asyncio import AsyncSession +from common.annotation.cache_annotation import ApiCache, ApiCacheEvict from common.annotation.log_annotation import Log from common.aspect.data_scope import DataScopeDependency from common.aspect.db_seesion import DBSessionDependency from common.aspect.interface_auth import UserInterfaceAuthDependency from common.aspect.pre_auth import CurrentUserDependency, PreAuthDependency +from common.constant import CacheGroup, CacheNamespace from common.enums import BusinessType from common.router import APIRouterPro from common.vo import DataResponseModel, ResponseBaseModel @@ -33,6 +35,7 @@ response_model=DataResponseModel[list[DeptModel]], dependencies=[UserInterfaceAuthDependency('system:dept:list')], ) +@ApiCache(namespace=CacheNamespace.SYSTEM_DEPT_EDIT_TREE) async def get_system_dept_tree_for_edit_option( request: Request, dept_id: Annotated[int, Path(description='部门id')], @@ -53,6 +56,7 @@ async def get_system_dept_tree_for_edit_option( response_model=DataResponseModel[list[DeptModel]], dependencies=[UserInterfaceAuthDependency('system:dept:list')], ) +@ApiCache(namespace=CacheNamespace.SYSTEM_DEPT_LIST) async def get_system_dept_list( request: Request, dept_query: Annotated[DeptQueryModel, Query()], @@ -73,6 +77,7 @@ async def get_system_dept_list( dependencies=[UserInterfaceAuthDependency('system:dept:add')], ) @ValidateFields(validate_model='add_dept') +@ApiCacheEvict(namespaces=CacheGroup.DATA_SCOPE_MUTATION) @Log(title='部门管理', business_type=BusinessType.INSERT) async def add_system_dept( request: Request, @@ -98,6 +103,7 @@ async def add_system_dept( dependencies=[UserInterfaceAuthDependency('system:dept:edit')], ) @ValidateFields(validate_model='edit_dept') +@ApiCacheEvict(namespaces=CacheGroup.DATA_SCOPE_MUTATION) @Log(title='部门管理', business_type=BusinessType.UPDATE) async def edit_system_dept( request: Request, @@ -123,6 +129,7 @@ async def edit_system_dept( response_model=ResponseBaseModel, dependencies=[UserInterfaceAuthDependency('system:dept:remove')], ) +@ApiCacheEvict(namespaces=CacheGroup.DATA_SCOPE_MUTATION) @Log(title='部门管理', business_type=BusinessType.DELETE) async def delete_system_dept( request: Request, @@ -152,6 +159,7 @@ async def delete_system_dept( response_model=DataResponseModel[DeptModel], dependencies=[UserInterfaceAuthDependency('system:dept:query')], ) +@ApiCache(namespace=CacheNamespace.SYSTEM_DEPT_DETAIL) async def query_detail_system_dept( request: Request, dept_id: Annotated[int, Path(description='部门id')], diff --git a/ruoyi-fastapi-backend/module_admin/controller/dict_controller.py b/ruoyi-fastapi-backend/module_admin/controller/dict_controller.py index f5b0297..2098902 100644 --- a/ruoyi-fastapi-backend/module_admin/controller/dict_controller.py +++ b/ruoyi-fastapi-backend/module_admin/controller/dict_controller.py @@ -6,10 +6,12 @@ from pydantic_validation_decorator import ValidateFields from sqlalchemy.ext.asyncio import AsyncSession +from common.annotation.cache_annotation import ApiCache, ApiCacheEvict from common.annotation.log_annotation import Log from common.aspect.db_seesion import DBSessionDependency from common.aspect.interface_auth import UserInterfaceAuthDependency from common.aspect.pre_auth import CurrentUserDependency, PreAuthDependency +from common.constant import CacheGroup, CacheNamespace from common.enums import BusinessType from common.router import APIRouterPro from common.vo import DataResponseModel, PageResponseModel, ResponseBaseModel @@ -39,6 +41,7 @@ response_model=PageResponseModel[DictTypeModel], dependencies=[UserInterfaceAuthDependency('system:dict:list')], ) +@ApiCache(namespace=CacheNamespace.SYSTEM_DICT_TYPE_LIST) async def get_system_dict_type_list( request: Request, dict_type_page_query: Annotated[DictTypePageQueryModel, Query()], @@ -61,6 +64,7 @@ async def get_system_dict_type_list( dependencies=[UserInterfaceAuthDependency('system:dict:add')], ) @ValidateFields(validate_model='add_dict_type') +@ApiCacheEvict(namespaces=CacheGroup.DICT_TYPE_MUTATION) @Log(title='字典类型', business_type=BusinessType.INSERT) async def add_system_dict_type( request: Request, @@ -86,6 +90,7 @@ async def add_system_dict_type( dependencies=[UserInterfaceAuthDependency('system:dict:edit')], ) @ValidateFields(validate_model='edit_dict_type') +@ApiCacheEvict(namespaces=CacheGroup.DICT_TYPE_MUTATION) @Log(title='字典类型', business_type=BusinessType.UPDATE) async def edit_system_dict_type( request: Request, @@ -123,6 +128,7 @@ async def refresh_system_dict(request: Request, query_db: Annotated[AsyncSession response_model=ResponseBaseModel, dependencies=[UserInterfaceAuthDependency('system:dict:remove')], ) +@ApiCacheEvict(namespaces=CacheGroup.DICT_TYPE_MUTATION) @Log(title='字典类型', business_type=BusinessType.DELETE) async def delete_system_dict_type( request: Request, @@ -142,6 +148,7 @@ async def delete_system_dict_type( description='用于获取字典类型下拉列表', response_model=DataResponseModel[list[DictTypeModel]], ) +@ApiCache(namespace=CacheNamespace.SYSTEM_DICT_TYPE_OPTIONS) async def query_system_dict_type_options( request: Request, query_db: Annotated[AsyncSession, DBSessionDependency()] ) -> Response: @@ -160,6 +167,7 @@ async def query_system_dict_type_options( response_model=DataResponseModel[DictTypeModel], dependencies=[UserInterfaceAuthDependency('system:dict:query')], ) +@ApiCache(namespace=CacheNamespace.SYSTEM_DICT_TYPE_DETAIL) async def query_detail_system_dict_type( request: Request, dict_id: Annotated[int, Path(description='字典主键')], @@ -229,6 +237,7 @@ async def query_system_dict_type_data( response_model=PageResponseModel[DictDataModel], dependencies=[UserInterfaceAuthDependency('system:dict:list')], ) +@ApiCache(namespace=CacheNamespace.SYSTEM_DICT_DATA_LIST) async def get_system_dict_data_list( request: Request, dict_data_page_query: Annotated[DictDataPageQueryModel, Query()], @@ -251,6 +260,7 @@ async def get_system_dict_data_list( dependencies=[UserInterfaceAuthDependency('system:dict:add')], ) @ValidateFields(validate_model='add_dict_data') +@ApiCacheEvict(namespaces=CacheGroup.DICT_DATA_MUTATION) @Log(title='字典数据', business_type=BusinessType.INSERT) async def add_system_dict_data( request: Request, @@ -276,6 +286,7 @@ async def add_system_dict_data( dependencies=[UserInterfaceAuthDependency('system:dict:edit')], ) @ValidateFields(validate_model='edit_dict_data') +@ApiCacheEvict(namespaces=CacheGroup.DICT_DATA_MUTATION) @Log(title='字典数据', business_type=BusinessType.UPDATE) async def edit_system_dict_data( request: Request, @@ -298,6 +309,7 @@ async def edit_system_dict_data( response_model=ResponseBaseModel, dependencies=[UserInterfaceAuthDependency('system:dict:remove')], ) +@ApiCacheEvict(namespaces=CacheGroup.DICT_DATA_MUTATION) @Log(title='字典数据', business_type=BusinessType.DELETE) async def delete_system_dict_data( request: Request, @@ -318,6 +330,7 @@ async def delete_system_dict_data( response_model=DataResponseModel[DictDataModel], dependencies=[UserInterfaceAuthDependency('system:dict:query')], ) +@ApiCache(namespace=CacheNamespace.SYSTEM_DICT_DATA_DETAIL) async def query_detail_system_dict_data( request: Request, dict_code: Annotated[int, Path(description='字典编码')], diff --git a/ruoyi-fastapi-backend/module_admin/controller/job_controller.py b/ruoyi-fastapi-backend/module_admin/controller/job_controller.py index 95891e6..b7dc32d 100644 --- a/ruoyi-fastapi-backend/module_admin/controller/job_controller.py +++ b/ruoyi-fastapi-backend/module_admin/controller/job_controller.py @@ -6,10 +6,12 @@ from pydantic_validation_decorator import ValidateFields from sqlalchemy.ext.asyncio import AsyncSession +from common.annotation.cache_annotation import ApiCache, ApiCacheEvict from common.annotation.log_annotation import Log from common.aspect.db_seesion import DBSessionDependency from common.aspect.interface_auth import UserInterfaceAuthDependency from common.aspect.pre_auth import CurrentUserDependency, PreAuthDependency +from common.constant import CacheGroup, CacheNamespace from common.enums import BusinessType from common.router import APIRouterPro from common.vo import DataResponseModel, PageResponseModel, ResponseBaseModel @@ -41,6 +43,7 @@ response_model=PageResponseModel[JobModel], dependencies=[UserInterfaceAuthDependency('monitor:job:list')], ) +@ApiCache(namespace=CacheNamespace.MONITOR_JOB_LIST) async def get_system_job_list( request: Request, job_page_query: Annotated[JobPageQueryModel, Query()], @@ -61,6 +64,7 @@ async def get_system_job_list( dependencies=[UserInterfaceAuthDependency('monitor:job:add')], ) @ValidateFields(validate_model='add_job') +@ApiCacheEvict(namespaces=CacheGroup.JOB_MUTATION) @Log(title='定时任务', business_type=BusinessType.INSERT) async def add_system_job( request: Request, @@ -86,6 +90,7 @@ async def add_system_job( dependencies=[UserInterfaceAuthDependency('monitor:job:edit')], ) @ValidateFields(validate_model='edit_job') +@ApiCacheEvict(namespaces=CacheGroup.JOB_MUTATION) @Log(title='定时任务', business_type=BusinessType.UPDATE) async def edit_system_job( request: Request, @@ -108,6 +113,7 @@ async def edit_system_job( response_model=ResponseBaseModel, dependencies=[UserInterfaceAuthDependency('monitor:job:changeStatus')], ) +@ApiCacheEvict(namespaces=CacheGroup.JOB_MUTATION) @Log(title='定时任务', business_type=BusinessType.UPDATE) async def change_system_job_status( request: Request, @@ -135,6 +141,7 @@ async def change_system_job_status( response_model=ResponseBaseModel, dependencies=[UserInterfaceAuthDependency('monitor:job:changeStatus')], ) +@ApiCacheEvict(namespaces=CacheGroup.JOB_MUTATION) @Log(title='定时任务', business_type=BusinessType.UPDATE) async def execute_system_job( request: Request, @@ -154,6 +161,7 @@ async def execute_system_job( response_model=ResponseBaseModel, dependencies=[UserInterfaceAuthDependency('monitor:job:remove')], ) +@ApiCacheEvict(namespaces=CacheGroup.JOB_MUTATION) @Log(title='定时任务', business_type=BusinessType.DELETE) async def delete_system_job( request: Request, @@ -174,6 +182,7 @@ async def delete_system_job( response_model=DataResponseModel[JobModel], dependencies=[UserInterfaceAuthDependency('monitor:job:query')], ) +@ApiCache(namespace=CacheNamespace.MONITOR_JOB_DETAIL) async def query_detail_system_job( request: Request, job_id: Annotated[int, Path(description='任务ID')], diff --git a/ruoyi-fastapi-backend/module_admin/controller/login_controller.py b/ruoyi-fastapi-backend/module_admin/controller/login_controller.py index 2143895..b7c6d17 100644 --- a/ruoyi-fastapi-backend/module_admin/controller/login_controller.py +++ b/ruoyi-fastapi-backend/module_admin/controller/login_controller.py @@ -6,9 +6,11 @@ from fastapi import Depends, Request, Response from sqlalchemy.ext.asyncio import AsyncSession +from common.annotation.cache_annotation import ApiCache, ApiCacheEvict from common.annotation.log_annotation import Log from common.aspect.db_seesion import DBSessionDependency from common.aspect.pre_auth import CurrentUserDependency +from common.constant import CacheGroup, CacheNamespace from common.enums import BusinessType, RedisInitKeyConfig from common.router import APIRouterPro from common.vo import CrudResponseModel, DataResponseModel, DynamicResponseModel, ResponseBaseModel @@ -29,6 +31,7 @@ description='用于用户登录', response_model=DynamicResponseModel[LoginToken] | Token, ) +@ApiCacheEvict(namespaces=CacheGroup.LOGIN_SUCCESS_MUTATION) @Log(title='用户登录', business_type=BusinessType.OTHER, log_type='login') async def login( request: Request, @@ -90,6 +93,7 @@ async def login( description='用于获取当前登录用户的信息', response_model=DynamicResponseModel[CurrentUserModel], ) +@ApiCache(namespace=CacheNamespace.LOGIN_USER_INFO) async def get_login_user_info( request: Request, current_user: Annotated[CurrentUserModel, CurrentUserDependency()] ) -> Response: @@ -104,6 +108,7 @@ async def get_login_user_info( description='用于获取当前登录用户的路由信息', response_model=DataResponseModel[list[RouterModel]], ) +@ApiCache(namespace=CacheNamespace.LOGIN_USER_ROUTERS) async def get_login_user_routers( request: Request, current_user: Annotated[CurrentUserModel, CurrentUserDependency()], @@ -121,6 +126,7 @@ async def get_login_user_routers( description='用于用户注册', response_model=DataResponseModel[CrudResponseModel], ) +@ApiCacheEvict(namespaces=CacheGroup.USER_ENTITY_MUTATION) async def register_user( request: Request, user_register: UserRegister, @@ -168,6 +174,7 @@ async def register_user( description='用于用户退出登录', response_model=ResponseBaseModel, ) +@ApiCacheEvict(namespaces=CacheGroup.LOGOUT_MUTATION) 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} diff --git a/ruoyi-fastapi-backend/module_admin/controller/menu_controller.py b/ruoyi-fastapi-backend/module_admin/controller/menu_controller.py index d5d8ffc..80b9dd1 100644 --- a/ruoyi-fastapi-backend/module_admin/controller/menu_controller.py +++ b/ruoyi-fastapi-backend/module_admin/controller/menu_controller.py @@ -5,10 +5,12 @@ from pydantic_validation_decorator import ValidateFields from sqlalchemy.ext.asyncio import AsyncSession +from common.annotation.cache_annotation import ApiCache, ApiCacheEvict from common.annotation.log_annotation import Log from common.aspect.db_seesion import DBSessionDependency from common.aspect.interface_auth import UserInterfaceAuthDependency from common.aspect.pre_auth import CurrentUserDependency, PreAuthDependency +from common.constant import CacheGroup, CacheNamespace from common.enums import BusinessType from common.router import APIRouterPro from common.vo import DataResponseModel, DynamicResponseModel, ResponseBaseModel @@ -30,6 +32,7 @@ description='用于获取当前用户可见的菜单树', response_model=DataResponseModel[list[MenuTreeModel]], ) +@ApiCache(namespace=CacheNamespace.SYSTEM_MENU_TREE) async def get_system_menu_tree( request: Request, query_db: Annotated[AsyncSession, DBSessionDependency()], @@ -47,6 +50,7 @@ async def get_system_menu_tree( description='用于获取指定角色可见的菜单树', response_model=DynamicResponseModel[RoleMenuQueryModel], ) +@ApiCache(namespace=CacheNamespace.SYSTEM_MENU_ROLE_TREE) async def get_system_role_menu_tree( request: Request, role_id: Annotated[int, Path(description='角色ID')], @@ -66,6 +70,7 @@ async def get_system_role_menu_tree( response_model=DataResponseModel[list[MenuModel]], dependencies=[UserInterfaceAuthDependency('system:menu:list')], ) +@ApiCache(namespace=CacheNamespace.SYSTEM_MENU_LIST) async def get_system_menu_list( request: Request, menu_query: Annotated[MenuQueryModel, Query()], @@ -86,6 +91,7 @@ async def get_system_menu_list( dependencies=[UserInterfaceAuthDependency('system:menu:add')], ) @ValidateFields(validate_model='add_menu') +@ApiCacheEvict(namespaces=CacheGroup.MENU_MUTATION) @Log(title='菜单管理', business_type=BusinessType.INSERT) async def add_system_menu( request: Request, @@ -111,6 +117,7 @@ async def add_system_menu( dependencies=[UserInterfaceAuthDependency('system:menu:edit')], ) @ValidateFields(validate_model='edit_menu') +@ApiCacheEvict(namespaces=CacheGroup.MENU_MUTATION) @Log(title='菜单管理', business_type=BusinessType.UPDATE) async def edit_system_menu( request: Request, @@ -133,6 +140,7 @@ async def edit_system_menu( response_model=ResponseBaseModel, dependencies=[UserInterfaceAuthDependency('system:menu:remove')], ) +@ApiCacheEvict(namespaces=CacheGroup.MENU_MUTATION) @Log(title='菜单管理', business_type=BusinessType.DELETE) async def delete_system_menu( request: Request, @@ -153,6 +161,7 @@ async def delete_system_menu( response_model=DataResponseModel[MenuModel], dependencies=[UserInterfaceAuthDependency('system:menu:query')], ) +@ApiCache(namespace=CacheNamespace.SYSTEM_MENU_DETAIL) async def query_detail_system_menu( request: Request, menu_id: Annotated[int, Path(description='菜单ID')], diff --git a/ruoyi-fastapi-backend/module_admin/controller/notice_controller.py b/ruoyi-fastapi-backend/module_admin/controller/notice_controller.py index ff904a6..4a536a7 100644 --- a/ruoyi-fastapi-backend/module_admin/controller/notice_controller.py +++ b/ruoyi-fastapi-backend/module_admin/controller/notice_controller.py @@ -5,10 +5,12 @@ from pydantic_validation_decorator import ValidateFields from sqlalchemy.ext.asyncio import AsyncSession +from common.annotation.cache_annotation import ApiCache, ApiCacheEvict from common.annotation.log_annotation import Log from common.aspect.db_seesion import DBSessionDependency from common.aspect.interface_auth import UserInterfaceAuthDependency from common.aspect.pre_auth import CurrentUserDependency, PreAuthDependency +from common.constant import CacheGroup, CacheNamespace from common.enums import BusinessType from common.router import APIRouterPro from common.vo import DataResponseModel, PageResponseModel, ResponseBaseModel @@ -30,6 +32,7 @@ response_model=PageResponseModel[NoticeModel], dependencies=[UserInterfaceAuthDependency('system:notice:list')], ) +@ApiCache(namespace=CacheNamespace.SYSTEM_NOTICE_LIST) async def get_system_notice_list( request: Request, notice_page_query: Annotated[NoticePageQueryModel, Query()], @@ -50,6 +53,7 @@ async def get_system_notice_list( dependencies=[UserInterfaceAuthDependency('system:notice:add')], ) @ValidateFields(validate_model='add_notice') +@ApiCacheEvict(namespaces=CacheGroup.NOTICE_MUTATION) @Log(title='通知公告', business_type=BusinessType.INSERT) async def add_system_notice( request: Request, @@ -75,6 +79,7 @@ async def add_system_notice( dependencies=[UserInterfaceAuthDependency('system:notice:edit')], ) @ValidateFields(validate_model='edit_notice') +@ApiCacheEvict(namespaces=CacheGroup.NOTICE_MUTATION) @Log(title='通知公告', business_type=BusinessType.UPDATE) async def edit_system_notice( request: Request, @@ -97,6 +102,7 @@ async def edit_system_notice( response_model=ResponseBaseModel, dependencies=[UserInterfaceAuthDependency('system:notice:remove')], ) +@ApiCacheEvict(namespaces=CacheGroup.NOTICE_MUTATION) @Log(title='通知公告', business_type=BusinessType.DELETE) async def delete_system_notice( request: Request, @@ -117,6 +123,7 @@ async def delete_system_notice( response_model=DataResponseModel[NoticeModel], dependencies=[UserInterfaceAuthDependency('system:notice:query')], ) +@ApiCache(namespace=CacheNamespace.SYSTEM_NOTICE_DETAIL) async def query_detail_system_post( request: Request, notice_id: Annotated[int, Path(description='公告ID')], diff --git a/ruoyi-fastapi-backend/module_admin/controller/post_controller.py b/ruoyi-fastapi-backend/module_admin/controller/post_controller.py index 6a63121..391527c 100644 --- a/ruoyi-fastapi-backend/module_admin/controller/post_controller.py +++ b/ruoyi-fastapi-backend/module_admin/controller/post_controller.py @@ -6,10 +6,12 @@ from pydantic_validation_decorator import ValidateFields from sqlalchemy.ext.asyncio import AsyncSession +from common.annotation.cache_annotation import ApiCache, ApiCacheEvict from common.annotation.log_annotation import Log from common.aspect.db_seesion import DBSessionDependency from common.aspect.interface_auth import UserInterfaceAuthDependency from common.aspect.pre_auth import CurrentUserDependency, PreAuthDependency +from common.constant import CacheGroup, CacheNamespace from common.enums import BusinessType from common.router import APIRouterPro from common.vo import DataResponseModel, PageResponseModel, ResponseBaseModel @@ -32,6 +34,7 @@ response_model=PageResponseModel[PostModel], dependencies=[UserInterfaceAuthDependency('system:post:list')], ) +@ApiCache(namespace=CacheNamespace.SYSTEM_POST_LIST) async def get_system_post_list( request: Request, post_page_query: Annotated[PostPageQueryModel, Query()], @@ -52,6 +55,7 @@ async def get_system_post_list( dependencies=[UserInterfaceAuthDependency('system:post:add')], ) @ValidateFields(validate_model='add_post') +@ApiCacheEvict(namespaces=CacheGroup.POST_MUTATION) @Log(title='岗位管理', business_type=BusinessType.INSERT) async def add_system_post( request: Request, @@ -77,6 +81,7 @@ async def add_system_post( dependencies=[UserInterfaceAuthDependency('system:post:edit')], ) @ValidateFields(validate_model='edit_post') +@ApiCacheEvict(namespaces=CacheGroup.POST_MUTATION) @Log(title='岗位管理', business_type=BusinessType.UPDATE) async def edit_system_post( request: Request, @@ -99,6 +104,7 @@ async def edit_system_post( response_model=ResponseBaseModel, dependencies=[UserInterfaceAuthDependency('system:post:remove')], ) +@ApiCacheEvict(namespaces=CacheGroup.POST_MUTATION) @Log(title='岗位管理', business_type=BusinessType.DELETE) async def delete_system_post( request: Request, @@ -119,6 +125,7 @@ async def delete_system_post( response_model=DataResponseModel[PostModel], dependencies=[UserInterfaceAuthDependency('system:post:query')], ) +@ApiCache(namespace=CacheNamespace.SYSTEM_POST_DETAIL) async def query_detail_system_post( request: Request, post_id: Annotated[int, Path(description='岗位ID')], diff --git a/ruoyi-fastapi-backend/module_admin/controller/role_controller.py b/ruoyi-fastapi-backend/module_admin/controller/role_controller.py index faefde9..c908560 100644 --- a/ruoyi-fastapi-backend/module_admin/controller/role_controller.py +++ b/ruoyi-fastapi-backend/module_admin/controller/role_controller.py @@ -7,11 +7,13 @@ from sqlalchemy import ColumnElement from sqlalchemy.ext.asyncio import AsyncSession +from common.annotation.cache_annotation import ApiCache, ApiCacheEvict from common.annotation.log_annotation import Log from common.aspect.data_scope import DataScopeDependency from common.aspect.db_seesion import DBSessionDependency from common.aspect.interface_auth import UserInterfaceAuthDependency from common.aspect.pre_auth import CurrentUserDependency, PreAuthDependency +from common.constant import CacheGroup, CacheNamespace from common.enums import BusinessType from common.router import APIRouterPro from common.vo import DataResponseModel, DynamicResponseModel, PageResponseModel, ResponseBaseModel @@ -45,6 +47,7 @@ response_model=DynamicResponseModel[RoleDeptQueryModel], dependencies=[UserInterfaceAuthDependency('system:role:query')], ) +@ApiCache(namespace=CacheNamespace.SYSTEM_ROLE_DEPT_TREE) async def get_system_role_dept_tree( request: Request, role_id: Annotated[int, Path(description='角色ID')], @@ -66,6 +69,7 @@ async def get_system_role_dept_tree( response_model=PageResponseModel[RoleModel], dependencies=[UserInterfaceAuthDependency('system:role:list')], ) +@ApiCache(namespace=CacheNamespace.SYSTEM_ROLE_LIST) async def get_system_role_list( request: Request, role_page_query: Annotated[RolePageQueryModel, Query()], @@ -88,6 +92,7 @@ async def get_system_role_list( dependencies=[UserInterfaceAuthDependency('system:role:add')], ) @ValidateFields(validate_model='add_role') +@ApiCacheEvict(namespaces=CacheGroup.ROLE_ENTITY_MUTATION) @Log(title='角色管理', business_type=BusinessType.INSERT) async def add_system_role( request: Request, @@ -113,6 +118,7 @@ async def add_system_role( dependencies=[UserInterfaceAuthDependency('system:role:edit')], ) @ValidateFields(validate_model='edit_role') +@ApiCacheEvict(namespaces=CacheGroup.ROLE_PERMISSION_MUTATION) @Log(title='角色管理', business_type=BusinessType.UPDATE) async def edit_system_role( request: Request, @@ -139,6 +145,7 @@ async def edit_system_role( response_model=ResponseBaseModel, dependencies=[UserInterfaceAuthDependency('system:role:edit')], ) +@ApiCacheEvict(namespaces=CacheGroup.DATA_SCOPE_MUTATION) @Log(title='角色管理', business_type=BusinessType.GRANT) async def edit_system_role_datascope( request: Request, @@ -171,6 +178,7 @@ async def edit_system_role_datascope( response_model=ResponseBaseModel, dependencies=[UserInterfaceAuthDependency('system:role:remove')], ) +@ApiCacheEvict(namespaces=CacheGroup.ROLE_ENTITY_MUTATION) @Log(title='角色管理', business_type=BusinessType.DELETE) async def delete_system_role( request: Request, @@ -199,6 +207,7 @@ async def delete_system_role( response_model=DataResponseModel[RoleModel], dependencies=[UserInterfaceAuthDependency('system:role:query')], ) +@ApiCache(namespace=CacheNamespace.SYSTEM_ROLE_DETAIL) async def query_detail_system_role( request: Request, role_id: Annotated[int, Path(description='角色ID')], @@ -253,6 +262,7 @@ async def export_system_role_list( response_model=ResponseBaseModel, dependencies=[UserInterfaceAuthDependency('system:role:edit')], ) +@ApiCacheEvict(namespaces=CacheGroup.ROLE_MUTATION) @Log(title='角色管理', business_type=BusinessType.UPDATE) async def reset_system_role_status( request: Request, @@ -284,6 +294,7 @@ async def reset_system_role_status( response_model=PageResponseModel[UserInfoModel], dependencies=[UserInterfaceAuthDependency('system:role:list')], ) +@ApiCache(namespace=CacheNamespace.SYSTEM_ROLE_ALLOCATED_USER_LIST) async def get_system_allocated_user_list( request: Request, user_role: Annotated[UserRolePageQueryModel, Query()], @@ -305,6 +316,7 @@ async def get_system_allocated_user_list( response_model=PageResponseModel[UserInfoModel], dependencies=[UserInterfaceAuthDependency('system:role:list')], ) +@ApiCache(namespace=CacheNamespace.SYSTEM_ROLE_UNALLOCATED_USER_LIST) async def get_system_unallocated_user_list( request: Request, user_role: Annotated[UserRolePageQueryModel, Query()], @@ -326,6 +338,7 @@ async def get_system_unallocated_user_list( response_model=ResponseBaseModel, dependencies=[UserInterfaceAuthDependency('system:role:edit')], ) +@ApiCacheEvict(namespaces=CacheGroup.ROLE_MUTATION) @Log(title='角色管理', business_type=BusinessType.GRANT) async def add_system_role_user( request: Request, @@ -349,6 +362,7 @@ async def add_system_role_user( response_model=ResponseBaseModel, dependencies=[UserInterfaceAuthDependency('system:role:edit')], ) +@ApiCacheEvict(namespaces=CacheGroup.ROLE_MUTATION) @Log(title='角色管理', business_type=BusinessType.GRANT) async def cancel_system_role_user( request: Request, @@ -368,6 +382,7 @@ async def cancel_system_role_user( response_model=ResponseBaseModel, dependencies=[UserInterfaceAuthDependency('system:role:edit')], ) +@ApiCacheEvict(namespaces=CacheGroup.ROLE_MUTATION) @Log(title='角色管理', business_type=BusinessType.GRANT) async def batch_cancel_system_role_user( request: Request, diff --git a/ruoyi-fastapi-backend/module_admin/controller/server_controller.py b/ruoyi-fastapi-backend/module_admin/controller/server_controller.py index c754689..8f24816 100644 --- a/ruoyi-fastapi-backend/module_admin/controller/server_controller.py +++ b/ruoyi-fastapi-backend/module_admin/controller/server_controller.py @@ -1,7 +1,9 @@ from fastapi import Request, Response +from common.annotation.cache_annotation import ApiCache from common.aspect.interface_auth import UserInterfaceAuthDependency from common.aspect.pre_auth import PreAuthDependency +from common.constant import CacheNamespace from common.router import APIRouterPro from common.vo import DataResponseModel from module_admin.entity.vo.server_vo import ServerMonitorModel @@ -21,6 +23,7 @@ response_model=DataResponseModel[ServerMonitorModel], dependencies=[UserInterfaceAuthDependency('monitor:server:list')], ) +@ApiCache(namespace=CacheNamespace.MONITOR_SERVER_INFO) async def get_monitor_server_info(request: Request) -> Response: # 获取全量数据 server_info_query_result = await ServerService.get_server_monitor_info() diff --git a/ruoyi-fastapi-backend/module_admin/controller/user_controller.py b/ruoyi-fastapi-backend/module_admin/controller/user_controller.py index 31fc8db..91649af 100644 --- a/ruoyi-fastapi-backend/module_admin/controller/user_controller.py +++ b/ruoyi-fastapi-backend/module_admin/controller/user_controller.py @@ -9,11 +9,13 @@ from sqlalchemy import ColumnElement from sqlalchemy.ext.asyncio import AsyncSession +from common.annotation.cache_annotation import ApiCache, ApiCacheEvict from common.annotation.log_annotation import Log from common.aspect.data_scope import DataScopeDependency from common.aspect.db_seesion import DBSessionDependency from common.aspect.interface_auth import UserInterfaceAuthDependency from common.aspect.pre_auth import CurrentUserDependency, PreAuthDependency +from common.constant import CacheGroup, CacheNamespace from common.enums import BusinessType from common.router import APIRouterPro from common.vo import DataResponseModel, DynamicResponseModel, PageResponseModel, ResponseBaseModel @@ -60,6 +62,7 @@ response_model=DataResponseModel[list[DeptTreeModel]], dependencies=[UserInterfaceAuthDependency('system:user:list')], ) +@ApiCache(namespace=CacheNamespace.SYSTEM_USER_DEPT_TREE) async def get_system_dept_tree( request: Request, query_db: Annotated[AsyncSession, DBSessionDependency()], @@ -78,6 +81,7 @@ async def get_system_dept_tree( response_model=PageResponseModel[UserRowModel], dependencies=[UserInterfaceAuthDependency('system:user:list')], ) +@ApiCache(namespace=CacheNamespace.SYSTEM_USER_LIST) async def get_system_user_list( request: Request, user_page_query: Annotated[UserPageQueryModel, Query()], @@ -101,6 +105,7 @@ async def get_system_user_list( dependencies=[UserInterfaceAuthDependency('system:user:add')], ) @ValidateFields(validate_model='add_user') +@ApiCacheEvict(namespaces=CacheGroup.USER_ENTITY_MUTATION) @Log(title='用户管理', business_type=BusinessType.INSERT) async def add_system_user( request: Request, @@ -134,6 +139,7 @@ async def add_system_user( dependencies=[UserInterfaceAuthDependency('system:user:edit')], ) @ValidateFields(validate_model='edit_user') +@ApiCacheEvict(namespaces=CacheGroup.USER_PERMISSION_MUTATION) @Log(title='用户管理', business_type=BusinessType.UPDATE) async def edit_system_user( request: Request, @@ -166,6 +172,7 @@ async def edit_system_user( response_model=ResponseBaseModel, dependencies=[UserInterfaceAuthDependency('system:user:remove')], ) +@ApiCacheEvict(namespaces=CacheGroup.USER_ENTITY_MUTATION) @Log(title='用户管理', business_type=BusinessType.DELETE) async def delete_system_user( request: Request, @@ -198,6 +205,7 @@ async def delete_system_user( response_model=ResponseBaseModel, dependencies=[UserInterfaceAuthDependency('system:user:resetPwd')], ) +@ApiCacheEvict(namespaces=CacheGroup.USER_INFO_MUTATION) @Log(title='用户管理', business_type=BusinessType.UPDATE) async def reset_system_user_pwd( request: Request, @@ -230,6 +238,7 @@ async def reset_system_user_pwd( response_model=ResponseBaseModel, dependencies=[UserInterfaceAuthDependency('system:user:edit')], ) +@ApiCacheEvict(namespaces=CacheGroup.USER_INFO_MUTATION) @Log(title='用户管理', business_type=BusinessType.UPDATE) async def change_system_user_status( request: Request, @@ -260,6 +269,7 @@ async def change_system_user_status( description='用于获取当前登录用户的个人信息', response_model=DynamicResponseModel[UserProfileModel], ) +@ApiCache(namespace=CacheNamespace.SYSTEM_USER_PROFILE) async def query_detail_system_user_profile( request: Request, query_db: Annotated[AsyncSession, DBSessionDependency()], @@ -285,6 +295,7 @@ async def query_detail_system_user_profile( response_model=DynamicResponseModel[UserDetailModel], dependencies=[UserInterfaceAuthDependency('system:user:query')], ) +@ApiCache(namespace=CacheNamespace.SYSTEM_USER_DETAIL) async def query_detail_system_user( request: Request, query_db: Annotated[AsyncSession, DBSessionDependency()], @@ -306,6 +317,7 @@ async def query_detail_system_user( description='用于修改当前登录用户的头像', response_model=DynamicResponseModel[AvatarModel], ) +@ApiCacheEvict(namespaces=CacheGroup.USER_INFO_MUTATION) @Log(title='个人信息', business_type=BusinessType.UPDATE) async def change_system_user_profile_avatar( request: Request, @@ -346,6 +358,7 @@ async def change_system_user_profile_avatar( description='用于修改当前登录用户的个人信息', response_model=ResponseBaseModel, ) +@ApiCacheEvict(namespaces=CacheGroup.USER_INFO_MUTATION) @Log(title='个人信息', business_type=BusinessType.UPDATE) async def change_system_user_profile_info( request: Request, @@ -375,6 +388,7 @@ async def change_system_user_profile_info( description='用于修改当前登录用户的密码', response_model=ResponseBaseModel, ) +@ApiCacheEvict(namespaces=CacheGroup.USER_INFO_MUTATION) @Log(title='个人信息', business_type=BusinessType.UPDATE) async def reset_system_user_password( request: Request, @@ -403,6 +417,7 @@ async def reset_system_user_password( response_model=ResponseBaseModel, dependencies=[UserInterfaceAuthDependency('system:user:import')], ) +@ApiCacheEvict(namespaces=CacheGroup.DATA_SCOPE_MUTATION) @Log(title='用户管理', business_type=BusinessType.IMPORT) async def batch_import_system_user( request: Request, @@ -505,6 +520,7 @@ async def get_system_allocated_role_list( response_model=ResponseBaseModel, dependencies=[UserInterfaceAuthDependency('system:user:edit')], ) +@ApiCacheEvict(namespaces=CacheGroup.USER_PERMISSION_MUTATION) @Log(title='用户管理', business_type=BusinessType.GRANT) async def update_system_role_user( request: Request, diff --git a/ruoyi-fastapi-backend/module_ai/controller/ai_chat_controller.py b/ruoyi-fastapi-backend/module_ai/controller/ai_chat_controller.py index d6d0ab9..c23a0a4 100644 --- a/ruoyi-fastapi-backend/module_ai/controller/ai_chat_controller.py +++ b/ruoyi-fastapi-backend/module_ai/controller/ai_chat_controller.py @@ -4,9 +4,11 @@ from fastapi.responses import StreamingResponse from sqlalchemy.ext.asyncio import AsyncSession +from common.annotation.cache_annotation import ApiCache, ApiCacheEvict from common.annotation.log_annotation import Log from common.aspect.db_seesion import DBSessionDependency from common.aspect.pre_auth import CurrentUserDependency, PreAuthDependency +from common.constant import CacheGroup, CacheNamespace from common.enums import BusinessType from common.router import APIRouterPro from common.vo import DataResponseModel, ResponseBaseModel @@ -59,6 +61,7 @@ async def send_chat_message( description='获取当前用户的AI对话配置', response_model=DataResponseModel[AiChatConfigModel], ) +@ApiCache(namespace=CacheNamespace.AI_CHAT_CONFIG) async def get_user_chat_config( request: Request, query_db: Annotated[AsyncSession, DBSessionDependency()], @@ -77,6 +80,7 @@ async def get_user_chat_config( description='保存当前用户的AI对话配置', response_model=DataResponseModel[AiChatConfigModel], ) +@ApiCacheEvict(namespaces=CacheGroup.AI_CHAT_CONFIG_MUTATION) @Log(title='AI对话配置管理', business_type=BusinessType.INSERT) async def save_user_chat_config( request: Request, diff --git a/ruoyi-fastapi-backend/module_ai/controller/ai_model_controller.py b/ruoyi-fastapi-backend/module_ai/controller/ai_model_controller.py index 2d158c1..efd89eb 100644 --- a/ruoyi-fastapi-backend/module_ai/controller/ai_model_controller.py +++ b/ruoyi-fastapi-backend/module_ai/controller/ai_model_controller.py @@ -6,11 +6,13 @@ from sqlalchemy import ColumnElement from sqlalchemy.ext.asyncio import AsyncSession +from common.annotation.cache_annotation import ApiCache, ApiCacheEvict from common.annotation.log_annotation import Log from common.aspect.data_scope import DataScopeDependency from common.aspect.db_seesion import DBSessionDependency from common.aspect.interface_auth import UserInterfaceAuthDependency from common.aspect.pre_auth import CurrentUserDependency, PreAuthDependency +from common.constant import CacheGroup, CacheNamespace from common.enums import BusinessType from common.router import APIRouterPro from common.vo import DataResponseModel, PageResponseModel, ResponseBaseModel @@ -33,6 +35,7 @@ response_model=PageResponseModel[AiModelModel], dependencies=[UserInterfaceAuthDependency('ai:model:list')], ) +@ApiCache(namespace=CacheNamespace.AI_MODEL_LIST) async def get_ai_model_list( request: Request, ai_model_page_query: Annotated[AiModelPageQueryModel, Query()], @@ -54,6 +57,7 @@ async def get_ai_model_list( description='用于获取AI模型不分页列表', response_model=DataResponseModel[AiModelModel], ) +@ApiCache(namespace=CacheNamespace.AI_MODEL_ALL) async def get_ai_model_all( request: Request, query_db: Annotated[AsyncSession, DBSessionDependency()], @@ -77,6 +81,7 @@ async def get_ai_model_all( dependencies=[UserInterfaceAuthDependency('ai:model:add')], ) @ValidateFields(validate_model='add_ai_model') +@ApiCacheEvict(namespaces=CacheGroup.AI_MODEL_MUTATION) @Log(title='AI模型管理', business_type=BusinessType.INSERT) async def add_ai_model( request: Request, @@ -104,6 +109,7 @@ async def add_ai_model( dependencies=[UserInterfaceAuthDependency('ai:model:edit')], ) @ValidateFields(validate_model='edit_ai_model') +@ApiCacheEvict(namespaces=CacheGroup.AI_MODEL_MUTATION) @Log(title='AI模型管理', business_type=BusinessType.UPDATE) async def edit_ai_model( request: Request, @@ -129,6 +135,7 @@ async def edit_ai_model( response_model=ResponseBaseModel, dependencies=[UserInterfaceAuthDependency('ai:model:remove')], ) +@ApiCacheEvict(namespaces=CacheGroup.AI_MODEL_MUTATION) @Log(title='AI模型管理', business_type=BusinessType.DELETE) async def delete_ai_model( request: Request, @@ -155,6 +162,7 @@ async def delete_ai_model( response_model=DataResponseModel[AiModelModel], dependencies=[UserInterfaceAuthDependency('ai:model:query')], ) +@ApiCache(namespace=CacheNamespace.AI_MODEL_DETAIL) async def get_ai_model_detail( request: Request, model_id: Annotated[int, Path(description='模型ID')], diff --git a/ruoyi-fastapi-backend/module_generator/controller/gen_controller.py b/ruoyi-fastapi-backend/module_generator/controller/gen_controller.py index 997428a..63ee838 100644 --- a/ruoyi-fastapi-backend/module_generator/controller/gen_controller.py +++ b/ruoyi-fastapi-backend/module_generator/controller/gen_controller.py @@ -6,10 +6,12 @@ from pydantic_validation_decorator import ValidateFields from sqlalchemy.ext.asyncio import AsyncSession +from common.annotation.cache_annotation import ApiCache, ApiCacheEvict from common.annotation.log_annotation import Log from common.aspect.db_seesion import DBSessionDependency from common.aspect.interface_auth import RoleInterfaceAuthDependency, UserInterfaceAuthDependency from common.aspect.pre_auth import CurrentUserDependency, PreAuthDependency +from common.constant import CacheGroup, CacheNamespace from common.enums import BusinessType from common.router import APIRouterPro from common.vo import DataResponseModel, PageResponseModel, ResponseBaseModel @@ -38,6 +40,7 @@ response_model=PageResponseModel[GenTableRowModel], dependencies=[UserInterfaceAuthDependency('tool:gen:list')], ) +@ApiCache(namespace=CacheNamespace.TOOL_GEN_LIST) async def get_gen_table_list( request: Request, gen_page_query: Annotated[GenTablePageQueryModel, Query()], @@ -57,6 +60,7 @@ async def get_gen_table_list( response_model=PageResponseModel[GenTableDbRowModel], dependencies=[UserInterfaceAuthDependency('tool:gen:list')], ) +@ApiCache(namespace=CacheNamespace.TOOL_GEN_DB_LIST) async def get_gen_db_table_list( request: Request, gen_page_query: Annotated[GenTablePageQueryModel, Query()], @@ -76,6 +80,7 @@ async def get_gen_db_table_list( response_model=ResponseBaseModel, dependencies=[UserInterfaceAuthDependency('tool:gen:import')], ) +@ApiCacheEvict(namespaces=CacheGroup.GEN_MUTATION) @Log(title='代码生成', business_type=BusinessType.IMPORT) async def import_gen_table( request: Request, @@ -99,6 +104,7 @@ async def import_gen_table( dependencies=[UserInterfaceAuthDependency('tool:gen:edit')], ) @ValidateFields(validate_model='edit_gen_table') +@ApiCacheEvict(namespaces=CacheGroup.GEN_MUTATION) @Log(title='代码生成', business_type=BusinessType.UPDATE) async def edit_gen_table( request: Request, @@ -122,6 +128,7 @@ async def edit_gen_table( response_model=ResponseBaseModel, dependencies=[UserInterfaceAuthDependency('tool:gen:remove')], ) +@ApiCacheEvict(namespaces=CacheGroup.GEN_MUTATION) @Log(title='代码生成', business_type=BusinessType.DELETE) async def delete_gen_table( request: Request, @@ -142,6 +149,7 @@ async def delete_gen_table( response_model=ResponseBaseModel, dependencies=[RoleInterfaceAuthDependency('admin')], ) +@ApiCacheEvict(namespaces=CacheGroup.GEN_MUTATION) @Log(title='创建表', business_type=BusinessType.OTHER) async def create_table( request: Request, @@ -190,6 +198,7 @@ async def batch_gen_code( response_model=ResponseBaseModel, dependencies=[UserInterfaceAuthDependency('tool:gen:code')], ) +@ApiCacheEvict(namespaces=CacheGroup.GEN_MUTATION) @Log(title='代码生成', business_type=BusinessType.GENCODE) async def gen_code_local( request: Request, @@ -212,6 +221,7 @@ async def gen_code_local( response_model=DataResponseModel[GenTableDetailModel], dependencies=[UserInterfaceAuthDependency('tool:gen:query')], ) +@ApiCache(namespace=CacheNamespace.TOOL_GEN_DETAIL) async def query_detail_gen_table( request: Request, table_id: Annotated[int, Path(description='表编号')], @@ -233,6 +243,7 @@ async def query_detail_gen_table( response_model=DataResponseModel[dict[str, str]], dependencies=[UserInterfaceAuthDependency('tool:gen:preview')], ) +@ApiCache(namespace=CacheNamespace.TOOL_GEN_PREVIEW) async def preview_code( request: Request, table_id: Annotated[int, Path(description='表编号')], @@ -251,6 +262,7 @@ async def preview_code( response_model=DataResponseModel[str], dependencies=[UserInterfaceAuthDependency('tool:gen:edit')], ) +@ApiCacheEvict(namespaces=CacheGroup.GEN_MUTATION) @Log(title='代码生成', business_type=BusinessType.UPDATE) async def sync_db( request: Request, diff --git a/ruoyi-fastapi-backend/utils/gen_util.py b/ruoyi-fastapi-backend/utils/gen_util.py index 9549546..dc8e881 100644 --- a/ruoyi-fastapi-backend/utils/gen_util.py +++ b/ruoyi-fastapi-backend/utils/gen_util.py @@ -198,7 +198,7 @@ def get_column_length(cls, column_type: str) -> int: :return: 字段长度 """ if '(' in column_type: - length = len(column_type.split('(')[1].split(')')[0]) + length = len(column_type.split('(')[1].split(')', maxsplit=1)[0]) return length return 0 @@ -211,7 +211,7 @@ def split_column_type(cls, column_type: str) -> list[str]: :return: 拆分结果 """ if '(' in column_type and ')' in column_type: - return column_type.split('(')[1].split(')')[0].split(',') + return column_type.split('(')[1].split(')', maxsplit=1)[0].split(',') return [] @classmethod diff --git a/ruoyi-fastapi-backend/utils/upload_util.py b/ruoyi-fastapi-backend/utils/upload_util.py index c979a14..b751dde 100644 --- a/ruoyi-fastapi-backend/utils/upload_util.py +++ b/ruoyi-fastapi-backend/utils/upload_util.py @@ -55,7 +55,9 @@ def check_file_timestamp(cls, filename: str) -> bool: :param filename: 文件名称 :return: 校验结果 """ - timestamp = filename.rsplit('.', 1)[0].split('_')[-1].split(UploadConfig.UPLOAD_MACHINE)[0] + timestamp = ( + filename.rsplit('.', 1)[0].rsplit('_', maxsplit=1)[-1].split(UploadConfig.UPLOAD_MACHINE, maxsplit=1)[0] + ) try: datetime.strptime(timestamp, '%Y%m%d%H%M%S') return True