This commit is contained in:
2026-07-08 14:24:50 +08:00
parent 5545af003f
commit 2882d37112
7 changed files with 388 additions and 133 deletions
+106 -7
View File
@@ -218,7 +218,21 @@ async def update_user_menus(
raise HTTPException(status_code=404, detail="用户不存在")
user.allowed_menus = req.allowed_menus
await db.flush()
await log_operation(db, admin.id, admin.username, f"更新菜单权限", "PUT", f"/admin/users/{user_id}/menus")
await log_operation(
db,
admin.id,
admin.username,
f"更新菜单权限",
"PUT",
f"/admin/users/{user_id}/menus",
detail=json.dumps(
{
"user_id": user_id,
"allowed_menus": req.allowed_menus,
},
ensure_ascii=False,
),
)
return {"message": "ok"}
@@ -259,7 +273,22 @@ async def adjust_credits(
f"您的积分已{'增加' if req.amount > 0 else '扣除'}{abs(req.amount)}积分。原因:{req.description}",
"credit",
)
await log_operation(db, admin.id, admin.username, f"调整积分 {'+' if req.amount > 0 else ''}{req.amount}", "POST", f"/admin/users/{user_id}/credits")
await log_operation(
db,
admin.id,
admin.username,
f"调整积分 {'+' if req.amount > 0 else ''}{req.amount}",
"POST",
f"/admin/users/{user_id}/credits",
detail=json.dumps(
{
"user_id": user_id,
"amount": req.amount,
"description": req.description,
},
ensure_ascii=False,
),
)
return {"message": "ok"}
@@ -275,7 +304,21 @@ async def update_user_status(
update(User).where(User.id == user_id).values(is_active=is_active)
)
await db.flush()
await log_operation(db, admin.id, admin.username, f"{'启用' if is_active else '禁用'}用户", "PUT", f"/admin/users/{user_id}/status")
await log_operation(
db,
admin.id,
admin.username,
f"{'启用' if is_active else '禁用'}用户",
"PUT",
f"/admin/users/{user_id}/status",
detail=json.dumps(
{
"user_id": user_id,
"is_active": is_active,
},
ensure_ascii=False,
),
)
return {"message": "ok"}
@@ -295,7 +338,21 @@ async def update_user_admin_status(
raise HTTPException(status_code=400, detail="仅后台用户支持设置超级管理员状态")
user.is_admin = is_admin
await db.flush()
await log_operation(db, admin.id, admin.username, f"{is_admin and '设为' or '取消'}超级管理员", "PUT", f"/admin/users/{user_id}/admin-status")
await log_operation(
db,
admin.id,
admin.username,
f"{is_admin and '设为' or '取消'}超级管理员",
"PUT",
f"/admin/users/{user_id}/admin-status",
detail=json.dumps(
{
"user_id": user_id,
"is_admin": is_admin,
},
ensure_ascii=False,
),
)
return {"message": "ok"}
@@ -314,7 +371,21 @@ async def update_user_frontend_kind(
raise HTTPException(status_code=400, detail="仅前台用户支持设置内部/外部归类")
user.frontend_user_kind = req.frontend_user_kind or FrontendUserKind.EXTERNAL.value
await db.flush()
await log_operation(db, admin.id, admin.username, f"设置前台用户归类为 {user.frontend_user_kind}", "PUT", f"/admin/users/{user_id}/frontend-kind")
await log_operation(
db,
admin.id,
admin.username,
f"设置前台用户归类为 {user.frontend_user_kind}",
"PUT",
f"/admin/users/{user_id}/frontend-kind",
detail=json.dumps(
{
"user_id": user_id,
"frontend_user_kind": user.frontend_user_kind,
},
ensure_ascii=False,
),
)
return user
@@ -354,7 +425,21 @@ async def reset_user_password(
raise HTTPException(status_code=404, detail="用户不存在")
user.hashed_password = hash_password(req.new_password)
await db.flush()
await log_operation(db, admin.id, admin.username, f"重置密码", "PUT", f"/admin/users/{user_id}/reset-password")
await log_operation(
db,
admin.id,
admin.username,
f"重置密码",
"PUT",
f"/admin/users/{user_id}/reset-password",
detail=json.dumps(
{
"user_id": user_id,
"new_password": "***",
},
ensure_ascii=False,
),
)
return {"message": "ok"}
@@ -372,7 +457,21 @@ async def admin_change_password(
raise HTTPException(status_code=400, detail="密码至少6位")
admin.hashed_password = hash_password(new_password)
await db.flush()
await log_operation(db, admin.id, admin.username, "修改密码", "POST", "/admin/change-password")
await log_operation(
db,
admin.id,
admin.username,
"修改密码",
"POST",
"/admin/change-password",
detail=json.dumps(
{
"old_password": "***",
"new_password": "***",
},
ensure_ascii=False,
),
)
return {"message": "密码修改成功"}
+1 -58
View File
@@ -2,11 +2,9 @@ from fastapi import APIRouter, Depends
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from app.dependencies import get_db, get_admin_user, get_backend_user, get_current_user
from app.dependencies import get_db, get_current_user
from app.models.user import User
from app.models.menu_config import MenuConfig
from app.schemas.menu import MenuConfigCreate, MenuConfigOut
from app.utils.id_gen import generate_id
router = APIRouter(tags=["menu-configs"])
@@ -57,58 +55,3 @@ async def public_list_menu_configs(
}
for m in menus
]
@router.get("/admin/menu-configs", response_model=list[MenuConfigOut])
async def admin_list_menu_configs(
admin: User = Depends(get_backend_user),
db: AsyncSession = Depends(get_db),
):
result = await db.execute(select(MenuConfig).order_by(MenuConfig.sort_order))
return result.scalars().all()
@router.post("/admin/menu-configs", response_model=MenuConfigOut)
async def create_menu_config(
req: MenuConfigCreate,
admin: User = Depends(get_admin_user),
db: AsyncSession = Depends(get_db),
):
menu = MenuConfig(id=generate_id(), **req.model_dump())
db.add(menu)
await db.flush()
return menu
@router.put("/admin/menu-configs/{menu_id}", response_model=MenuConfigOut)
async def update_menu_config(
menu_id: str,
req: MenuConfigCreate,
admin: User = Depends(get_admin_user),
db: AsyncSession = Depends(get_db),
):
result = await db.execute(select(MenuConfig).where(MenuConfig.id == menu_id).limit(1))
menu = result.scalar_one_or_none()
if not menu:
from fastapi import HTTPException, status
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="菜单不存在")
for k, v in req.model_dump().items():
setattr(menu, k, v)
await db.flush()
return menu
@router.delete("/admin/menu-configs/{menu_id}")
async def delete_menu_config(
menu_id: str,
admin: User = Depends(get_admin_user),
db: AsyncSession = Depends(get_db),
):
result = await db.execute(select(MenuConfig).where(MenuConfig.id == menu_id).limit(1))
menu = result.scalar_one_or_none()
if not menu:
from fastapi import HTTPException, status
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="菜单不存在")
await db.delete(menu)
await db.flush()
return {"message": "ok"}
+2 -68
View File
@@ -1,16 +1,10 @@
from fastapi import APIRouter, Depends, HTTPException
from fastapi import APIRouter, Depends
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from app.dependencies import get_db, get_admin_user, get_current_user
from app.dependencies import get_db, get_current_user
from app.models.user import User
from app.models.recharge_package import RechargePackage
from app.schemas.recharge_package import (
RechargePackageCreate,
RechargePackageUpdate,
RechargePackageOut,
)
from app.utils.id_gen import generate_id
router = APIRouter(tags=["recharge-packages"])
@@ -43,63 +37,3 @@ async def list_active_packages(
.order_by(RechargePackage.sort_order)
)
return [_to_out(p) for p in result.scalars().all()]
@router.get("/admin/recharge-packages")
async def admin_list_packages(
_admin=Depends(get_admin_user),
db: AsyncSession = Depends(get_db),
):
"""Admin: list all packages."""
result = await db.execute(
select(RechargePackage).order_by(RechargePackage.sort_order)
)
return [_to_out(p) for p in result.scalars().all()]
@router.post("/admin/recharge-packages")
async def create_package(
data: RechargePackageCreate,
_admin=Depends(get_admin_user),
db: AsyncSession = Depends(get_db),
):
pkg = RechargePackage(id=generate_id(), **data.model_dump())
db.add(pkg)
await db.flush()
return _to_out(pkg)
@router.put("/admin/recharge-packages/{pkg_id}")
async def update_package(
pkg_id: str,
data: RechargePackageUpdate,
_admin=Depends(get_admin_user),
db: AsyncSession = Depends(get_db),
):
result = await db.execute(
select(RechargePackage).where(RechargePackage.id == pkg_id).limit(1)
)
pkg = result.scalar_one_or_none()
if not pkg:
raise HTTPException(status_code=404, detail="套餐不存在")
for k, v in data.model_dump(exclude_unset=True).items():
setattr(pkg, k, v)
await db.flush()
return _to_out(pkg)
@router.delete("/admin/recharge-packages/{pkg_id}")
async def delete_package(
pkg_id: str,
_admin=Depends(get_admin_user),
db: AsyncSession = Depends(get_db),
):
result = await db.execute(
select(RechargePackage).where(RechargePackage.id == pkg_id).limit(1)
)
pkg = result.scalar_one_or_none()
if not pkg:
raise HTTPException(status_code=404, detail="套餐不存在")
await db.delete(pkg)
await db.flush()
return {"ok": True}