From 8d0e76c6949beda7e0ccb700c6637395e2955134 Mon Sep 17 00:00:00 2001 From: zhangtao <9480807882@qq.com> Date: Mon, 13 Jul 2026 01:38:51 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E7=BB=9F=E4=B8=80=E5=88=9D?= =?UTF-8?q?=E5=A7=8B=E5=8C=96=E6=95=B0=E6=8D=AE=E6=96=87=E4=BB=B6=E5=AD=98?= =?UTF-8?q?=E6=94=BE=E8=B7=AF=E5=BE=84=E5=B9=B6=E6=B8=85=E7=90=86=E5=86=97?= =?UTF-8?q?=E4=BD=99=E8=84=9A=E6=9C=AC=E6=95=B0=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 将所有初始化数据文件从app/scripts/data迁移至backend/sql/data目录 2. 更新path_conf.py中的SCRIPT_DIR配置指向新路径 3. 移除app/scripts/data下的冗余初始化数据文件 4. 清理权限缓存相关的进程级缓存逻辑 5. 调整部分代码导入顺序和冗余依赖 6. 简化用户菜单获取逻辑,移除菜单树缓存 7. 调整套餐菜单设置接口,移除redis参数传递 --- .../v1/module_generator/gencode/service.py | 4 +-- .../v1/module_platform/package/controller.py | 5 ++- .../api/v1/module_platform/package/service.py | 34 +------------------ .../api/v1/module_platform/tenant/service.py | 2 +- .../api/v1/module_system/params/service.py | 1 + .../app/api/v1/module_system/user/service.py | 17 ++-------- backend/app/config/path_conf.py | 2 +- backend/app/core/dependencies.py | 17 ++-------- backend/app/core/middlewares.py | 7 ++-- backend/app/core/permission.py | 2 +- .../scripts => sql}/data/platform_menu.json | 0 .../data/platform_package.json | 0 .../data/platform_package_menu.json | 0 .../scripts => sql}/data/platform_tenant.json | 0 .../data/platform_user_tenant.json | 0 .../{app/scripts => sql}/data/sys_dept.json | 0 .../scripts => sql}/data/sys_dict_data.json | 0 .../scripts => sql}/data/sys_dict_type.json | 0 .../{app/scripts => sql}/data/sys_param.json | 0 .../{app/scripts => sql}/data/sys_role.json | 0 .../{app/scripts => sql}/data/sys_user.json | 0 .../scripts => sql}/data/sys_user_roles.json | 0 .../scripts => sql}/data/sys_version.json | 0 23 files changed, 16 insertions(+), 75 deletions(-) rename backend/{app/scripts => sql}/data/platform_menu.json (100%) rename backend/{app/scripts => sql}/data/platform_package.json (100%) rename backend/{app/scripts => sql}/data/platform_package_menu.json (100%) rename backend/{app/scripts => sql}/data/platform_tenant.json (100%) rename backend/{app/scripts => sql}/data/platform_user_tenant.json (100%) rename backend/{app/scripts => sql}/data/sys_dept.json (100%) rename backend/{app/scripts => sql}/data/sys_dict_data.json (100%) rename backend/{app/scripts => sql}/data/sys_dict_type.json (100%) rename backend/{app/scripts => sql}/data/sys_param.json (100%) rename backend/{app/scripts => sql}/data/sys_role.json (100%) rename backend/{app/scripts => sql}/data/sys_user.json (100%) rename backend/{app/scripts => sql}/data/sys_user_roles.json (100%) rename backend/{app/scripts => sql}/data/sys_version.json (100%) diff --git a/backend/app/api/v1/module_generator/gencode/service.py b/backend/app/api/v1/module_generator/gencode/service.py index f8468ba0..9d9f300f 100644 --- a/backend/app/api/v1/module_generator/gencode/service.py +++ b/backend/app/api/v1/module_generator/gencode/service.py @@ -30,10 +30,10 @@ from app.core.base_schema import AuthSchema, PageResultSchema from app.core.exceptions import CustomException from app.core.logger import logger from app.utils.common_util import CamelCaseUtil, compute_menu_route_first_segment, search_to_dict -from .gen_util import GenUtils -from .jinja2_template_util import Jinja2TemplateUtil from .crud import GenTableColumnCRUD, GenTableCRUD +from .gen_util import GenUtils +from .jinja2_template_util import Jinja2TemplateUtil from .schema import ( GenSyncColumnChange, GenSyncPreviewSchema, diff --git a/backend/app/api/v1/module_platform/package/controller.py b/backend/app/api/v1/module_platform/package/controller.py index cd9fd7ef..474a85dd 100644 --- a/backend/app/api/v1/module_platform/package/controller.py +++ b/backend/app/api/v1/module_platform/package/controller.py @@ -1,6 +1,6 @@ from typing import Annotated -from fastapi import APIRouter, Body, Depends, Path, Query, Request, Security, status +from fastapi import APIRouter, Body, Depends, Path, Query, Security, status from fastapi.responses import JSONResponse from fastapi_cache import FastAPICache from fastapi_cache.decorator import cache @@ -113,11 +113,10 @@ async def get_menus_controller( @PackageRouter.post("/menus/{package_id}/set", summary="设置套餐菜单", response_model=ResponseSchema) async def set_menus_controller( - request: Request, auth: Annotated[AuthSchema, Security(AuthPermission(["module_package:package:update"]))], db: Annotated[AsyncSession, Depends(db_getter)], package_id: Annotated[int, Path(description="套餐ID", ge=1)], data: Annotated[PackageMenuSetSchema, Body(description="菜单列表")], ) -> JSONResponse: - await PackageService(auth, db).set_menus(package_id=package_id, data=data, redis=request.app.state.redis) + await PackageService(auth, db).set_menus(package_id=package_id, data=data) return SuccessResponse(msg="设置成功") diff --git a/backend/app/api/v1/module_platform/package/service.py b/backend/app/api/v1/module_platform/package/service.py index 9b21ae3b..b6a249e6 100644 --- a/backend/app/api/v1/module_platform/package/service.py +++ b/backend/app/api/v1/module_platform/package/service.py @@ -1,7 +1,6 @@ from typing import Any import sqlalchemy as sa -from redis.asyncio.client import Redis from sqlalchemy import func, select from sqlalchemy.ext.asyncio import AsyncSession @@ -10,8 +9,6 @@ from app.api.v1.module_platform.tenant.model import TenantModel from app.core.base_schema import AuthSchema, PageResultSchema from app.core.exceptions import CustomException, require_superadmin from app.core.logger import logger -from app.core.dependencies import _package_menu_cache -from app.core.redis_crud import RedisCURD from app.utils.common_util import search_to_dict from .crud import PackageCRUD @@ -142,41 +139,12 @@ class PackageService: result = await self.db.execute(stmt) return [row[0] for row in result.all()] - async def set_menus(self, package_id: int, data: PackageMenuSetSchema, redis: Redis | None = None) -> None: + async def set_menus(self, package_id: int, data: PackageMenuSetSchema) -> None: await self.db.execute(sa.delete(PackageMenuModel).where(PackageMenuModel.package_id == package_id)) for menu_id in data.menu_ids: self.db.add(PackageMenuModel(package_id=package_id, menu_id=menu_id)) await self.db.flush() logger.info(f"套餐[{package_id}]菜单权限已设置, count={len(data.menu_ids)}") - # 失效缓存:使所有 worker / 进程 立刻重新查询套餐菜单(最长 60s 旧值生效 -> 立即生效) - await self._invalidate_package_menu_cache(package_id=package_id, redis=redis) - - async def _invalidate_package_menu_cache(self, package_id: int, redis: Redis | None = None) -> None: - """套餐菜单变更后失效缓存(进程级 + 跨进程广播)。 - - 进程级:清空本进程的 ``_package_menu_cache`` 中所有引用此套餐的租户(这里用全局清空)。 - 跨进程:通过 Redis pub/sub 通知所有 worker 进程清空各自的进程缓存。 - - 缓存具体的 location 在 ``_get_cached_tenant_menu_ids`` (dependencies.py)。 - """ - try: - _package_menu_cache.clear() - except Exception: - pass - - if redis is None: - return - - try: - import json - - await RedisCURD(redis).publish( - "cache:invalidate:package_menus", - json.dumps({"package_id": package_id}), - ) - logger.info(f"已广播套餐[{package_id}]菜单缓存失效") - except Exception as e: - logger.warning(f"广播缓存失效失败(仅影响跨 worker 延迟生效): {e!s}") async def get_package_menu_ids(self, package_id: int) -> list[int]: stmt = select(PackageMenuModel.menu_id).where(PackageMenuModel.package_id == package_id) diff --git a/backend/app/api/v1/module_platform/tenant/service.py b/backend/app/api/v1/module_platform/tenant/service.py index 302220ff..48dc7923 100644 --- a/backend/app/api/v1/module_platform/tenant/service.py +++ b/backend/app/api/v1/module_platform/tenant/service.py @@ -1052,7 +1052,7 @@ class TenantService: amount = pkg.price - from app.api.v1.module_platform.order.service import _generate_order_no, PaymentService + from app.api.v1.module_platform.order.service import PaymentService, _generate_order_no order = await OrderCRUD(auth, db).create( OrderCreateInternalSchema( diff --git a/backend/app/api/v1/module_system/params/service.py b/backend/app/api/v1/module_system/params/service.py index ada881e4..3730964b 100644 --- a/backend/app/api/v1/module_system/params/service.py +++ b/backend/app/api/v1/module_system/params/service.py @@ -22,6 +22,7 @@ from .schema import ( ParamsUpdateSchema, ) + class ParamsService: """参数管理服务 diff --git a/backend/app/api/v1/module_system/user/service.py b/backend/app/api/v1/module_system/user/service.py index 8129e48b..c269c3cd 100644 --- a/backend/app/api/v1/module_system/user/service.py +++ b/backend/app/api/v1/module_system/user/service.py @@ -1,8 +1,6 @@ -import json from typing import Any from fastapi import UploadFile -from fastapi_cache import FastAPICache from sqlalchemy.ext.asyncio import AsyncSession from app.api.v1.module_platform.menu.crud import MenuCRUD @@ -205,17 +203,7 @@ class UserService: user_dict.tenant_by = CommonSchema(id=user.tenant_by.id, name=user.tenant_by.name, status=user.tenant_by.status) user_dict.is_impersonate = self.auth.is_impersonate - # 菜单树缓存(菜单变更极低频,Redis 60s 过期) _pc_only = {"client": "pc"} - _MENU_TREE_TTL = 60 - _prefix = FastAPICache.get_prefix() - cache_key = f"{_prefix}:menu:menu_tree:{self.auth.user.tenant_id or 'platform'}" if self.auth.user.is_superuser else f"{_prefix}:menu:menu_tree:user:{self.auth.user.id}" - _backend = FastAPICache.get_backend() - cached = await _backend.get(cache_key) - if cached: - user_dict.menus = json.loads(cached.decode()) - return user_dict - if self.auth.user.is_superuser: scope_filter = {"scope": "tenant"} if self.auth.user.tenant_id else {"scope": "platform"} menu_all = await MenuCRUD(self.auth, self.db).tree_list( @@ -242,9 +230,8 @@ class UserService: if menu_ids else [] ) - menu_tree = traversal_to_tree([menu.model_dump(mode="json") for menu in menus_raw]) - await _backend.set(cache_key, json.dumps(menu_tree).encode(), expire=_MENU_TREE_TTL) - user_dict.menus = [MenuTreeOutSchema(**item) for item in menu_tree] + menu_tree = [MenuTreeOutSchema(**item) for item in traversal_to_tree([menu.model_dump(mode="json") for menu in menus_raw])] + user_dict.menus = menu_tree return user_dict async def update_current_info(self, data: CurrentUserUpdateSchema) -> UserOutSchema: diff --git a/backend/app/config/path_conf.py b/backend/app/config/path_conf.py index 5f2d0a9e..7c74ae74 100644 --- a/backend/app/config/path_conf.py +++ b/backend/app/config/path_conf.py @@ -25,7 +25,7 @@ INVOICE_DIR = STATIC_DIR / "invoice" ENV_DIR = BASE_DIR / "env" # 初始化脚本 -SCRIPT_DIR: Path = BASE_DIR / "app" / "scripts" / "data" +SCRIPT_DIR: Path = BASE_DIR / "sql" / "data" # 模版文件配置(统一管理代码生成模板 + HTML 模板) TEMPLATE_DIR: Path = BASE_DIR / "templates" diff --git a/backend/app/core/dependencies.py b/backend/app/core/dependencies.py index a8a0f484..49f4076e 100644 --- a/backend/app/core/dependencies.py +++ b/backend/app/core/dependencies.py @@ -1,5 +1,4 @@ import json -import time from collections.abc import AsyncGenerator from dataclasses import replace from typing import Any @@ -17,12 +16,8 @@ from app.core.exceptions import CustomException from app.core.logger import logger from app.core.redis_crud import RedisCURD from app.core.request_context import RequestContext - from app.core.security import OAuth2Schema, decode_access_token -# 套餐可用菜单 ID 进程级缓存(package/service.py 也引用,共享同一 dict) -_package_menu_cache: dict[int, tuple[float, list[int]]] = {} - async def db_getter() -> AsyncGenerator[AsyncSession, None]: """数据库会话 — 请求级生命周期管理。 @@ -248,15 +243,9 @@ class AuthPermission: raise CustomException(msg="无权限操作", code=RET.FORBIDDEN.code, status_code=403) if user.tenant_id: - # 获取租户可用菜单 ID(带 60s 进程级缓存) - cached = _package_menu_cache.get(user.tenant_id) - if cached and time.time() - cached[0] < 60: - allowed_ids = set[int](cached[1]) - else: - from app.api.v1.module_platform.package.service import PackageService - result = await PackageService(auth, db).get_tenant_available_menu_ids(user.tenant_id) - _package_menu_cache[user.tenant_id] = (time.time(), result) - allowed_ids = set[int](result) + from app.api.v1.module_platform.package.service import PackageService + result = await PackageService(auth, db).get_tenant_available_menu_ids(user.tenant_id) + allowed_ids = set[int](result) user_permissions = {p for p, mid in auth.permissions_with_menu.items() if mid in allowed_ids} if not any(perm in user_permissions for perm in self.permissions): diff --git a/backend/app/core/middlewares.py b/backend/app/core/middlewares.py index afeac28f..741ed628 100644 --- a/backend/app/core/middlewares.py +++ b/backend/app/core/middlewares.py @@ -3,7 +3,9 @@ import time import uuid from dataclasses import replace from types import MappingProxyType +from typing import Any +from redis.asyncio.client import Redis from starlette.middleware.base import BaseHTTPMiddleware, RequestResponseEndpoint from starlette.middleware.cors import CORSMiddleware from starlette.middleware.gzip import GZipMiddleware @@ -13,10 +15,6 @@ from starlette.requests import Request from starlette.responses import Response from starlette.types import ASGIApp -from typing import Any - -from redis.asyncio.client import Redis - from app.common.enums import RedisInitKeyConfig from app.common.response import ErrorResponse from app.config.setting import settings @@ -27,7 +25,6 @@ from app.core.request_context import RequestContext, clear_current_tenant, reset from app.core.security import decode_access_token from app.utils.ip_local_util import get_client_ip - # ── 中间件配置(Redis 缓存读取) ────────────────────────────── # 中间件高频读取的 sys_param 配置键集合 MIDDLEWARE_CONFIG_KEYS: tuple[str, ...] = ( diff --git a/backend/app/core/permission.py b/backend/app/core/permission.py index ba6f76a0..6efaa173 100644 --- a/backend/app/core/permission.py +++ b/backend/app/core/permission.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Any +from typing import Any from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession diff --git a/backend/app/scripts/data/platform_menu.json b/backend/sql/data/platform_menu.json similarity index 100% rename from backend/app/scripts/data/platform_menu.json rename to backend/sql/data/platform_menu.json diff --git a/backend/app/scripts/data/platform_package.json b/backend/sql/data/platform_package.json similarity index 100% rename from backend/app/scripts/data/platform_package.json rename to backend/sql/data/platform_package.json diff --git a/backend/app/scripts/data/platform_package_menu.json b/backend/sql/data/platform_package_menu.json similarity index 100% rename from backend/app/scripts/data/platform_package_menu.json rename to backend/sql/data/platform_package_menu.json diff --git a/backend/app/scripts/data/platform_tenant.json b/backend/sql/data/platform_tenant.json similarity index 100% rename from backend/app/scripts/data/platform_tenant.json rename to backend/sql/data/platform_tenant.json diff --git a/backend/app/scripts/data/platform_user_tenant.json b/backend/sql/data/platform_user_tenant.json similarity index 100% rename from backend/app/scripts/data/platform_user_tenant.json rename to backend/sql/data/platform_user_tenant.json diff --git a/backend/app/scripts/data/sys_dept.json b/backend/sql/data/sys_dept.json similarity index 100% rename from backend/app/scripts/data/sys_dept.json rename to backend/sql/data/sys_dept.json diff --git a/backend/app/scripts/data/sys_dict_data.json b/backend/sql/data/sys_dict_data.json similarity index 100% rename from backend/app/scripts/data/sys_dict_data.json rename to backend/sql/data/sys_dict_data.json diff --git a/backend/app/scripts/data/sys_dict_type.json b/backend/sql/data/sys_dict_type.json similarity index 100% rename from backend/app/scripts/data/sys_dict_type.json rename to backend/sql/data/sys_dict_type.json diff --git a/backend/app/scripts/data/sys_param.json b/backend/sql/data/sys_param.json similarity index 100% rename from backend/app/scripts/data/sys_param.json rename to backend/sql/data/sys_param.json diff --git a/backend/app/scripts/data/sys_role.json b/backend/sql/data/sys_role.json similarity index 100% rename from backend/app/scripts/data/sys_role.json rename to backend/sql/data/sys_role.json diff --git a/backend/app/scripts/data/sys_user.json b/backend/sql/data/sys_user.json similarity index 100% rename from backend/app/scripts/data/sys_user.json rename to backend/sql/data/sys_user.json diff --git a/backend/app/scripts/data/sys_user_roles.json b/backend/sql/data/sys_user_roles.json similarity index 100% rename from backend/app/scripts/data/sys_user_roles.json rename to backend/sql/data/sys_user_roles.json diff --git a/backend/app/scripts/data/sys_version.json b/backend/sql/data/sys_version.json similarity index 100% rename from backend/app/scripts/data/sys_version.json rename to backend/sql/data/sys_version.json