"""系统设置 API — GET/PUT 分组设置,自动初始化种子数据。""" from typing import List, Dict from fastapi import APIRouter, Depends, HTTPException from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy import select from pydantic import BaseModel from app.core.database import get_db from app.core.security import require_role from app.models.user import User from app.models.system_setting import SystemSetting from app.schemas.system_setting import SettingItem, SettingUpdateItem from app.services.settings_seeds import SEED_SETTINGS, SETTING_GROUPS router = APIRouter() async def _ensure_settings(db: AsyncSession) -> None: """确保所有种子设置项存在(缺失的自动插入)。""" result = await db.execute(select(SystemSetting.key)) existing = {row[0] for row in result.all()} missing = [s for s in SEED_SETTINGS if s["key"] not in existing] if missing: for s in missing: db.add(SystemSetting(**s)) await db.commit() @router.get("", response_model=Dict[str, List[SettingItem]]) async def get_settings( db: AsyncSession = Depends(get_db), current_user: User = Depends(require_role("admin")), ): """获取所有系统设置,按分组返回。""" await _ensure_settings(db) result = await db.execute( select(SystemSetting).order_by(SystemSetting.group, SystemSetting.sort_order) ) settings = result.scalars().all() grouped: Dict[str, List[SettingItem]] = {} for s in settings: group = s.group if group not in grouped: grouped[group] = [] grouped[group].append(SettingItem( key=s.key, value=s.value, value_type=s.value_type, group=s.group, label=s.label, description=s.description, default_value=s.default_value, sort_order=s.sort_order, )) return grouped @router.get("/groups") async def get_setting_groups( db: AsyncSession = Depends(get_db), current_user: User = Depends(require_role("admin")), ): """获取设置分组信息。""" await _ensure_settings(db) return SETTING_GROUPS @router.put("") async def update_settings( items: List[SettingUpdateItem], db: AsyncSession = Depends(get_db), current_user: User = Depends(require_role("admin")), ): """批量更新系统设置。""" await _ensure_settings(db) updated = [] for item in items: result = await db.execute( select(SystemSetting).where(SystemSetting.key == item.key) ) setting = result.scalar_one_or_none() if not setting: continue # 类型校验 if setting.value_type == "boolean" and item.value not in ("true", "false"): raise HTTPException(400, f"设置 '{setting.label}' 需要布尔值 (true/false)") if setting.value_type == "integer": try: int(item.value) except ValueError: raise HTTPException(400, f"设置 '{setting.label}' 需要数字") # 范围校验 if setting.key == "query_max_limit": v = int(item.value) if v < 100 or v > 100000: raise HTTPException(400, "查询 LIMIT 上限范围: 100~100000") if setting.key == "query_timeout": v = int(item.value) if v < 5 or v > 300: raise HTTPException(400, "查询超时范围: 5~300 秒") if setting.key == "query_max_length": v = int(item.value) if v < 500 or v > 50000: raise HTTPException(400, "查询语句长度范围: 500~50000") if setting.key == "security_password_min_length": v = int(item.value) if v < 4 or v > 32: raise HTTPException(400, "密码最小长度范围: 4~32") if setting.key == "security_max_login_attempts": v = int(item.value) if v < 0 or v > 20: raise HTTPException(400, "最大登录失败次数范围: 0~20") setting.value = item.value updated.append(setting.key) await db.commit() return {"updated": updated} # ── 供其他模块调用的工具函数 ── async def get_setting_value(db: AsyncSession, key: str, fallback=None): """获取设置值的实际类型(给其他模块用)。""" result = await db.execute( select(SystemSetting).where(SystemSetting.key == key) ) setting = result.scalar_one_or_none() if setting: return setting.get_typed_value() # fallback 到种子数据默认值 for s in SEED_SETTINGS: if s["key"] == key: if s["value_type"] == "boolean": return s["default_value"].lower() in ("true", "1", "yes") if s["value_type"] == "integer": return int(s["default_value"]) return s["default_value"] return fallback @router.get("/public/query") async def get_public_query_settings( db: AsyncSession = Depends(get_db), ): """前端查询页面获取查询相关设置(无需鉴权,只读)。""" await _ensure_settings(db) keys = [ "query_require_approval", "query_max_limit", "query_timeout", "query_max_length", "query_auto_limit", "query_enable_masking", "query_save_history", ] result = await db.execute( select(SystemSetting).where(SystemSetting.key.in_(keys)) ) settings = result.scalars().all() return {s.key: s.get_typed_value() for s in settings}