refactor: 整合仪表盘功能到监控模块,清理冗余代码

- 移除原监控仪表盘独立模块,将相关功能合并到在线监控模块
- 重构租户配置字段名,统一使用logo_url和name替代tenant_logo/tenant_name
- 优化搜索工具函数,移除重复导入
- 调整参数配置模型字段长度限制,移除config_value的max_length约束
- 清理冗余的常量定义和导入语句
- 修复批量状态设置接口的redis依赖注入
- 增强OAuth登录安全性,添加租户默认归属和state一次性消费
- 优化资源目录缓存逻辑,减少重复计算
- 新增API Token模块基础框架
- 完善用户token版本管理,支持主动失效JWT
- 调整AI模型配置缓存过期时间
- 修复菜单类型字段索引,提升查询性能
- 简化前端刷新token调用逻辑
- 新增滑块验证完成接口和忘记密码验证码校验
- 调整系统配置默认值,添加操作日志保留天数和接口白名单配置
- 限制Mock支付回调仅在开发环境可用
- 重构websocket认证方式,支持更安全的subprotocol传参
This commit is contained in:
zhangtao
2026-07-13 01:14:20 +08:00
parent 6a5f8cf0dd
commit cf88ab8897
102 changed files with 4078 additions and 3777 deletions
@@ -79,11 +79,12 @@ async def delete_param_controller(
@ParamsRouter.patch("/status/batch", summary="批量设置参数状态", response_model=ResponseSchema)
async def batch_set_status_controller(
redis: Annotated[Redis, Depends(redis_getter)],
auth: Annotated[AuthSchema, Security(AuthPermission(["module_system:param:patch"]))],
db: Annotated[AsyncSession, Depends(db_getter)],
data: Annotated[BatchSetAvailable, Body(description="状态设置")],
) -> JSONResponse:
await ParamsService(auth, db).batch_set_status(ids=data.ids, status=data.status)
await ParamsService(auth, db).batch_set_status(redis=redis, ids=data.ids, status=data.status)
return SuccessResponse(msg="批量设置参数状态成功")
@@ -23,7 +23,7 @@ class ParamsModel(ModelMixin, TenantMixin, UserMixin):
config_name: Mapped[str] = mapped_column(String(64), nullable=False, comment="参数名称")
config_key: Mapped[str] = mapped_column(String(500), nullable=False, comment="参数键名")
config_value: Mapped[str | None] = mapped_column(String(500), comment="参数键值")
config_value: Mapped[str | None] = mapped_column(Text, comment="参数键值")
config_type: Mapped[bool] = mapped_column(Boolean, default=False, nullable=True, comment="系统内置(True:是 False:否)", index=True)
status: Mapped[int] = mapped_column(Integer, default=0, nullable=False, comment="状态(0:启动 1:停用)", index=True)
description: Mapped[str | None] = mapped_column(Text, default=None, nullable=True, comment="备注")
@@ -12,7 +12,7 @@ class ParamsCreateSchema(BaseModel):
config_name: str = Field(..., min_length=1, max_length=64, description="参数名称")
config_key: str = Field(..., min_length=1, max_length=500, description="参数键名(小写字母开头,仅允许字母数字_.-)")
config_value: str | None = Field(default=None, max_length=500, description="参数键值")
config_value: str | None = Field(default=None, description="参数键值")
config_type: bool = Field(default=False, description="是否系统内置(True:是 False:否)")
status: int = Field(default=0, ge=0, le=1, description="状态(0:正常 1:停用)")
description: str | None = Field(default=None, max_length=500, description="参数描述")
@@ -1,5 +1,4 @@
import json
import time
from collections.abc import Sequence
from redis.asyncio.client import Redis
@@ -10,7 +9,9 @@ from app.core.base_schema import AuthSchema, PageResultSchema
from app.core.database import async_db_session
from app.core.exceptions import CustomException
from app.core.logger import logger
from app.core.middlewares import invalidate_middleware_config_cache
from app.core.redis_crud import RedisCURD
from app.utils.common_util import search_to_dict
from app.utils.excel_util import ExcelUtil
from .crud import ParamsCRUD
@@ -21,91 +22,6 @@ from .schema import (
ParamsUpdateSchema,
)
# 中间件 / 调度器高频读取的 sys_param 配置键集合。
MIDDLEWARE_CONFIG_KEYS: tuple[str, ...] = (
"demo_enable",
"ip_white_list",
"white_api_list_path",
"ip_black_list",
"operation_log_retention_days",
)
# 内存缓存(按租户隔离)
_MID_CONFIG_TTL: float = 60.0
_mid_config_cache: dict[int, tuple[float, dict]] = {}
def _parse_bool(value: object) -> bool:
"""兼容字符串 / 布尔值 / JSON 布尔值的开关字段解析。
支持的字符串真值:true / 1 / yes / on
支持的字符串假值:false / 0 / no / off(以及空字符串、None
"""
if isinstance(value, bool):
return value
if isinstance(value, str):
normalized = value.strip().lower()
if normalized in {"true", "1", "yes", "on"}:
return True
if normalized in {"false", "0", "no", "off", ""}:
return False
try:
return bool(json.loads(normalized))
except (json.JSONDecodeError, ValueError):
return False
if value is None:
return False
return bool(value)
def _parse_json_list(value: object) -> list:
"""兼容 JSON 字符串 / 列表 / 空值的数组字段解析。"""
if isinstance(value, list):
return value
if isinstance(value, str):
try:
parsed = json.loads(value)
return parsed if isinstance(parsed, list) else []
except (json.JSONDecodeError, ValueError):
return []
return []
def _invalidate_mid_config_cache(tenant_id: int | None = None) -> None:
"""失效中间件内存缓存。tenant_id 为 None 时清空所有租户。"""
if tenant_id is None:
_mid_config_cache.clear()
else:
_mid_config_cache.pop(tenant_id, None)
def _default_for(key: str) -> object:
"""缺省值表:新增 MIDDLEWARE_CONFIG_KEYS 时只需在这里登记默认值。"""
if key in {"ip_white_list", "ip_black_list", "white_api_list_path"}:
return []
if key == "demo_enable":
return False
if key == "operation_log_retention_days":
return 90
return None
def _parse_value(key: str, value: object) -> object:
"""按 key 的语义解析 config_value。"""
if key == "demo_enable":
return _parse_bool(value)
if key in {"ip_white_list", "ip_black_list", "white_api_list_path"}:
return _parse_json_list(value)
if key == "operation_log_retention_days":
if value is None:
return 90
try:
return int(str(value))
except (TypeError, ValueError):
return 90
return value
class ParamsService:
"""参数管理服务
@@ -158,7 +74,7 @@ class ParamsService:
返回:
- list[ParamsOutSchema]: 参数响应模型列表
"""
obj_list = await ParamsCRUD(self.auth, self.db).get_list(search=vars(search) if search else None, order_by=order_by)
obj_list = await ParamsCRUD(self.auth, self.db).get_list(search=search_to_dict(search), order_by=order_by)
return [ParamsOutSchema.model_validate(obj) for obj in obj_list]
async def page(
@@ -184,7 +100,7 @@ class ParamsService:
offset=offset,
limit=page_size,
order_by=order_by or [{"id": "asc"}],
search=vars(search) if search else None,
search=search_to_dict(search),
out_schema=ParamsOutSchema,
)
@@ -268,7 +184,7 @@ class ParamsService:
raise CustomException(msg="同步配置到缓存失败") from e
# 失效中间件内存缓存,让下次请求重新加载
_invalidate_mid_config_cache(user.tenant_id)
invalidate_middleware_config_cache(user.tenant_id)
return out
@@ -282,7 +198,7 @@ class ParamsService:
返回:
- None
"""
if len(ids) < 1:
if not ids:
raise CustomException(msg="删除失败,删除对象不能为空")
# 批量校验参数存在性
objs = await ParamsCRUD(self.auth, self.db).get_list(search={"id": ("in", ids)})
@@ -309,12 +225,13 @@ class ParamsService:
raise CustomException(msg="同步删除缓存失败") from e
# 失效中间件内存缓存
_invalidate_mid_config_cache(user.tenant_id)
invalidate_middleware_config_cache(user.tenant_id)
async def batch_set_status(self, ids: list[int], status: int) -> None:
async def batch_set_status(self, redis: Redis, ids: list[int], status: int) -> None:
"""批量设置系统参数状态
参数:
- redis: Redis 客户端(用于同步缓存)
- ids (list[int]): 系统参数ID列表
- status (int): 状态值
@@ -324,7 +241,17 @@ class ParamsService:
if not ids:
raise CustomException(msg="请选择要操作的数据")
# 先查参数列表获取 config_key 和 tenant_id
params = await ParamsCRUD(self.auth, self.db).get_list(search={"id": ("in", list(ids))})
await ParamsCRUD(self.auth, self.db).set(ids=ids, status=status)
# 同步删除对应 Redis 缓存
for param in params:
redis_key = f"{RedisInitKeyConfig.SYSTEM_CONFIG.key}:{param.tenant_id}:{param.config_key}"
try:
await RedisCURD(redis).delete(redis_key)
except Exception as e:
logger.error(f"同步删除系统配置缓存失败: {e}")
invalidate_middleware_config_cache(None)
@staticmethod
def export(data_list: list[dict]) -> bytes:
@@ -405,56 +332,3 @@ class ParamsService:
if config_obj:
configs = await ParamsService._sync_configs_to_redis(redis, config_obj)
return configs
@staticmethod
async def get_system_config_for_middleware(redis: Redis, tenant_id: int = 1) -> dict:
"""获取中间件 / 调度器所需的系统配置(带 60 秒内存缓存,按租户隔离)。
参数:
- redis (Redis): Redis 客户端实例
- tenant_id (int): 租户 ID
返回:
- dict: 包含 MIDDLEWARE_CONFIG_KEYS 中所有 key 的解析后值。
"""
cached = _mid_config_cache.get(tenant_id)
if cached and time.monotonic() - cached[0] < _MID_CONFIG_TTL:
return cached[1]
config = await ParamsService._fetch_system_config_for_middleware(redis, tenant_id)
_mid_config_cache[tenant_id] = (time.monotonic(), config)
return config
@staticmethod
async def _fetch_system_config_for_middleware(redis: Redis, tenant_id: int = 1) -> dict:
"""从 Redis 批量拉取并解析 MIDDLEWARE_CONFIG_KEYS 中的配置。
停用(status=1)的配置视为未配置,使用默认值。
"""
config_keys = [f"{RedisInitKeyConfig.SYSTEM_CONFIG.key}:{tenant_id}:{key}" for key in MIDDLEWARE_CONFIG_KEYS]
config_values = await RedisCURD(redis).mget(config_keys)
result: dict[str, object] = {}
for key, raw in zip(MIDDLEWARE_CONFIG_KEYS, config_values, strict=True):
if not raw:
result[key] = _default_for(key)
continue
try:
payload = json.loads(raw)
except json.JSONDecodeError:
logger.error("解析系统配置 %s 失败", key)
result[key] = _default_for(key)
continue
if not isinstance(payload, dict):
result[key] = _default_for(key)
continue
# 停用的配置视为未启用,使用默认值
if payload.get("status", 0) != 0:
result[key] = _default_for(key)
continue
result[key] = _parse_value(key, payload.get("config_value"))
return result