BookSystem/backend/auth/apps/settings/urls.py
jayhgq 82cf0192e8 refactor: 重构系统权限与设置管理,新增多类型配置能力
1.  重构权限码规范,将系统权限前缀从perm:system改为system:
2.  扩展系统设置模型,支持string/image/select/boolean四种配置类型
3.  新增设置项选项配置、图片上传功能
4.  重构初始化权限数据,拆分权限与系统设置权限结构
5.  重写前端系统设置页面,支持按类型渲染不同配置表单
6.  添加python-multipart依赖支持文件上传

遗留问题:
1.系统设置页面的添加设置按钮无法使用
2026-05-27 15:39:13 +08:00

178 lines
5.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from fastapi import APIRouter, Depends, HTTPException, File, UploadFile
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, and_
from fastcrud import FastCRUD
from pathlib import Path
import os
import shutil
from datetime import datetime
from database import get_db
from models import SystemSetting, User
from schemas import (
SystemSettingCreate,
SystemSettingUpdate,
SystemSettingResponse,
)
from middleware import get_current_user
from utils.like_filter import ilike_contains
router = APIRouter(prefix="/settings", tags=["settings"])
# 创建FastCRUD实例
setting_crud = FastCRUD(SystemSetting, SystemSettingResponse)
# 图片存储目录
UPLOAD_DIR = Path(__file__).parent.parent.parent / "uploads"
UPLOAD_DIR.mkdir(exist_ok=True)
@router.post("/", response_model=SystemSettingResponse)
async def create_setting(
setting_data: SystemSettingCreate,
db: AsyncSession = Depends(get_db),
current_user: User = Depends(get_current_user),
):
"""创建设置项"""
# 检查设置键名是否已存在
result = await db.execute(select(SystemSetting).filter(SystemSetting.key == setting_data.key))
existing_setting = result.scalar_one_or_none()
if existing_setting:
raise HTTPException(status_code=400, detail="设置键名已存在")
# 创建设置实例
db_setting = SystemSetting(
key=setting_data.key,
value=setting_data.value,
type=setting_data.type or "string",
options=setting_data.options,
description=setting_data.description,
)
db.add(db_setting)
await db.commit()
await db.refresh(db_setting)
return db_setting
@router.get("/", response_model=list[SystemSettingResponse])
async def get_settings(
db: AsyncSession = Depends(get_db),
current_user: User = Depends(get_current_user),
):
"""获取所有设置项"""
result = await db.execute(select(SystemSetting))
settings = result.scalars().all()
return [SystemSettingResponse.model_validate(s) for s in settings]
@router.get("/{setting_id}", response_model=SystemSettingResponse)
async def get_setting(
setting_id: int,
db: AsyncSession = Depends(get_db),
current_user: User = Depends(get_current_user),
):
"""获取单个设置项"""
setting = await setting_crud.get(db, id=setting_id)
if not setting:
raise HTTPException(status_code=404, detail="设置项不存在")
return setting
@router.get("/key/{key}", response_model=SystemSettingResponse)
async def get_setting_by_key(
key: str,
db: AsyncSession = Depends(get_db),
current_user: User = Depends(get_current_user),
):
"""根据键名获取设置项"""
result = await db.execute(select(SystemSetting).filter(SystemSetting.key == key))
setting = result.scalar_one_or_none()
if not setting:
raise HTTPException(status_code=404, detail="设置项不存在")
return setting
@router.put("/{setting_id}", response_model=SystemSettingResponse)
async def update_setting(
setting_id: int,
setting_data: SystemSettingUpdate,
db: AsyncSession = Depends(get_db),
current_user: User = Depends(get_current_user),
):
"""更新设置项"""
# 检查设置键名是否被其他设置使用
if setting_data.key:
result = await db.execute(
select(SystemSetting).filter(SystemSetting.key == setting_data.key, SystemSetting.id != setting_id)
)
existing_setting = result.scalar_one_or_none()
if existing_setting:
raise HTTPException(status_code=400, detail="设置键名已存在")
# 准备更新数据
update_data = setting_data.model_dump(exclude_unset=True)
# 使用FastCRUD更新设置
updated_setting = await setting_crud.update(
db, update_data, id=setting_id, schema_to_select=SystemSettingResponse, return_as_model=True
)
if not updated_setting:
raise HTTPException(status_code=404, detail="设置项不存在")
return updated_setting
@router.delete("/{setting_id}")
async def delete_setting(
setting_id: int,
db: AsyncSession = Depends(get_db),
current_user: User = Depends(get_current_user),
):
"""删除设置项"""
# 检查设置是否存在
result = await db.execute(select(SystemSetting).filter(SystemSetting.id == setting_id))
existing_setting = result.scalar_one_or_none()
if not existing_setting:
raise HTTPException(status_code=404, detail="设置项不存在")
# 使用FastCRUD删除设置
deleted = await setting_crud.delete(db, id=setting_id)
return {"message": "设置项删除成功"}
@router.post("/upload")
async def upload_image(
file: UploadFile = File(...),
current_user: User = Depends(get_current_user),
):
"""上传图片"""
# 检查文件类型
if not file.filename or not file.filename.lower().endswith(('.png', '.jpg', '.jpeg', '.gif', '.webp')):
raise HTTPException(status_code=400, detail="只支持图片格式png, jpg, jpeg, gif, webp")
# 检查文件大小2MB
file_size = 0
chunk = await file.read(1024)
while chunk:
file_size += len(chunk)
if file_size > 2 * 1024 * 1024:
raise HTTPException(status_code=400, detail="文件大小不能超过2MB")
chunk = await file.read(1024)
# 重置文件指针
await file.seek(0)
# 生成文件名
timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
ext = file.filename.split('.')[-1]
filename = f"setting_{timestamp}.{ext}"
file_path = UPLOAD_DIR / filename
# 保存文件
with open(file_path, "wb") as buffer:
shutil.copyfileobj(file.file, buffer)
# 返回文件路径
return {"url": f"/uploads/{filename}"}