Merge branch 'main' of https://gitee.com/wg123/video-gen
This commit is contained in:
@@ -5,6 +5,8 @@ from app.api.admin.resource_capacity import router as resource_capacity_router
|
||||
from app.api.admin.team import router as team_router
|
||||
from app.api.admin.home_material import router as home_material_router
|
||||
from app.api.admin.private_portrait import router as private_portrait_router
|
||||
from app.api.admin.recharge_package import router as recharge_package_router
|
||||
from app.api.admin.menu_config import router as menu_config_router
|
||||
|
||||
router = APIRouter()
|
||||
router.include_router(video_prompt_schema_config_router)
|
||||
@@ -12,3 +14,5 @@ router.include_router(resource_capacity_router)
|
||||
router.include_router(team_router)
|
||||
router.include_router(home_material_router)
|
||||
router.include_router(private_portrait_router)
|
||||
router.include_router(recharge_package_router)
|
||||
router.include_router(menu_config_router)
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
import json
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, status
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.dependencies import get_db, get_admin_user, get_backend_user
|
||||
from app.models.user import User
|
||||
from app.models.menu_config import MenuConfig
|
||||
from app.schemas.menu import MenuConfigCreate, MenuConfigOut
|
||||
from app.services.operation_log import log_operation
|
||||
from app.utils.id_gen import generate_id
|
||||
|
||||
router = APIRouter(prefix="/admin/menu-configs", tags=["admin-menu-configs"])
|
||||
|
||||
|
||||
@router.get("", 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("", 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()
|
||||
await log_operation(
|
||||
db,
|
||||
admin.id,
|
||||
admin.username,
|
||||
f"创建菜单 {menu.label}",
|
||||
"POST",
|
||||
"/admin/menu-configs",
|
||||
detail=json.dumps(
|
||||
{
|
||||
"id": menu.id,
|
||||
"label": menu.label,
|
||||
"path": menu.path,
|
||||
"menu_target": menu.menu_target,
|
||||
"menu_type": menu.menu_type,
|
||||
},
|
||||
ensure_ascii=False,
|
||||
),
|
||||
)
|
||||
return menu
|
||||
|
||||
|
||||
@router.put("/{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:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="菜单不存在")
|
||||
before_label = menu.label
|
||||
for k, v in req.model_dump().items():
|
||||
setattr(menu, k, v)
|
||||
await db.flush()
|
||||
await log_operation(
|
||||
db,
|
||||
admin.id,
|
||||
admin.username,
|
||||
f"更新菜单 {before_label}",
|
||||
"PUT",
|
||||
f"/admin/menu-configs/{menu_id}",
|
||||
detail=json.dumps(
|
||||
{
|
||||
"id": menu_id,
|
||||
"before": {"label": before_label},
|
||||
"after": {
|
||||
"label": menu.label,
|
||||
"path": menu.path,
|
||||
"is_active": menu.is_active,
|
||||
"sort_order": menu.sort_order,
|
||||
},
|
||||
},
|
||||
ensure_ascii=False,
|
||||
),
|
||||
)
|
||||
return menu
|
||||
|
||||
|
||||
@router.delete("/{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:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="菜单不存在")
|
||||
menu_label = menu.label
|
||||
await db.delete(menu)
|
||||
await db.flush()
|
||||
await log_operation(
|
||||
db,
|
||||
admin.id,
|
||||
admin.username,
|
||||
f"删除菜单 {menu_label}",
|
||||
"DELETE",
|
||||
f"/admin/menu-configs/{menu_id}",
|
||||
detail=json.dumps(
|
||||
{
|
||||
"id": menu_id,
|
||||
"label": menu_label,
|
||||
"path": menu.path,
|
||||
},
|
||||
ensure_ascii=False,
|
||||
),
|
||||
)
|
||||
return {"message": "ok"}
|
||||
@@ -0,0 +1,145 @@
|
||||
import json
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.dependencies import get_db, get_admin_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.services.operation_log import log_operation
|
||||
from app.utils.id_gen import generate_id
|
||||
|
||||
router = APIRouter(prefix="/admin/recharge-packages", tags=["admin-recharge-packages"])
|
||||
|
||||
|
||||
def _to_out(pkg: RechargePackage) -> dict:
|
||||
return {
|
||||
"id": pkg.id,
|
||||
"name": pkg.name,
|
||||
"credits": round(pkg.credits, 2),
|
||||
"price": round(pkg.price, 2),
|
||||
"bonus_credits": round(pkg.bonus_credits, 2),
|
||||
"total_credits": round(pkg.credits + pkg.bonus_credits, 2),
|
||||
"description": pkg.description,
|
||||
"package_type": pkg.package_type,
|
||||
"is_gift": pkg.is_gift,
|
||||
"is_active": pkg.is_active,
|
||||
"sort_order": pkg.sort_order,
|
||||
}
|
||||
|
||||
|
||||
@router.get("", response_model=list[RechargePackageOut])
|
||||
async def admin_list_packages(
|
||||
admin: User = Depends(get_admin_user),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
result = await db.execute(
|
||||
select(RechargePackage).order_by(RechargePackage.sort_order)
|
||||
)
|
||||
return [_to_out(p) for p in result.scalars().all()]
|
||||
|
||||
|
||||
@router.post("", response_model=RechargePackageOut)
|
||||
async def create_package(
|
||||
data: RechargePackageCreate,
|
||||
admin: User = Depends(get_admin_user),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
pkg = RechargePackage(id=generate_id(), **data.model_dump())
|
||||
db.add(pkg)
|
||||
await db.flush()
|
||||
await log_operation(
|
||||
db,
|
||||
admin.id,
|
||||
admin.username,
|
||||
f"创建充值套餐 {pkg.name}",
|
||||
"POST",
|
||||
"/admin/recharge-packages",
|
||||
detail=json.dumps(
|
||||
{
|
||||
"id": pkg.id,
|
||||
"name": pkg.name,
|
||||
"price": round(pkg.price, 2),
|
||||
"credits": round(pkg.credits, 2),
|
||||
"bonus_credits": round(pkg.bonus_credits, 2),
|
||||
},
|
||||
ensure_ascii=False,
|
||||
),
|
||||
)
|
||||
return _to_out(pkg)
|
||||
|
||||
|
||||
@router.put("/{pkg_id}", response_model=RechargePackageOut)
|
||||
async def update_package(
|
||||
pkg_id: str,
|
||||
data: RechargePackageUpdate,
|
||||
admin: User = 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="套餐不存在")
|
||||
before = _to_out(pkg)
|
||||
for k, v in data.model_dump(exclude_unset=True).items():
|
||||
setattr(pkg, k, v)
|
||||
await db.flush()
|
||||
after = _to_out(pkg)
|
||||
await log_operation(
|
||||
db,
|
||||
admin.id,
|
||||
admin.username,
|
||||
f"更新充值套餐 {pkg.name}",
|
||||
"PUT",
|
||||
f"/admin/recharge-packages/{pkg_id}",
|
||||
detail=json.dumps(
|
||||
{
|
||||
"id": pkg_id,
|
||||
"before": before,
|
||||
"after": after,
|
||||
},
|
||||
ensure_ascii=False,
|
||||
),
|
||||
)
|
||||
return _to_out(pkg)
|
||||
|
||||
|
||||
@router.delete("/{pkg_id}")
|
||||
async def delete_package(
|
||||
pkg_id: str,
|
||||
admin: User = 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="套餐不存在")
|
||||
pkg_name = pkg.name
|
||||
await db.delete(pkg)
|
||||
await db.flush()
|
||||
await log_operation(
|
||||
db,
|
||||
admin.id,
|
||||
admin.username,
|
||||
f"删除充值套餐 {pkg_name}",
|
||||
"DELETE",
|
||||
f"/admin/recharge-packages/{pkg_id}",
|
||||
detail=json.dumps(
|
||||
{
|
||||
"id": pkg_id,
|
||||
"name": pkg_name,
|
||||
},
|
||||
ensure_ascii=False,
|
||||
),
|
||||
)
|
||||
return {"ok": True}
|
||||
@@ -1,5 +1,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
|
||||
from fastapi import APIRouter, Depends
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
@@ -21,6 +23,7 @@ from app.services.video_prompt_schema_config_service import (
|
||||
reset_video_prompt_schema_config,
|
||||
save_video_prompt_schema_config,
|
||||
)
|
||||
from app.services.operation_log import log_operation
|
||||
|
||||
router = APIRouter(prefix="/admin/video-prompt-schema-config", tags=["admin-video-prompt-schema-config"])
|
||||
|
||||
@@ -40,8 +43,22 @@ async def save_config(
|
||||
admin: User = Depends(get_admin_user),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
_ = admin
|
||||
return await save_video_prompt_schema_config(db, data=req.data, is_enabled=req.is_enabled)
|
||||
result = await save_video_prompt_schema_config(db, data=req.data, is_enabled=req.is_enabled)
|
||||
await log_operation(
|
||||
db,
|
||||
admin.id,
|
||||
admin.username,
|
||||
"保存视频提词 Schema 配置",
|
||||
"PUT",
|
||||
"/admin/video-prompt-schema-config",
|
||||
detail=json.dumps(
|
||||
{
|
||||
"is_enabled": req.is_enabled,
|
||||
},
|
||||
ensure_ascii=False,
|
||||
),
|
||||
)
|
||||
return result
|
||||
|
||||
|
||||
@router.post("/reset-default", response_model=VideoPromptSchemaConfigOut, summary="恢复默认视频提词 Schema 配置")
|
||||
@@ -49,8 +66,22 @@ async def reset_default(
|
||||
admin: User = Depends(get_admin_user),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
_ = admin
|
||||
return await reset_video_prompt_schema_config(db)
|
||||
result = await reset_video_prompt_schema_config(db)
|
||||
await log_operation(
|
||||
db,
|
||||
admin.id,
|
||||
admin.username,
|
||||
"恢复默认视频提词 Schema 配置",
|
||||
"POST",
|
||||
"/admin/video-prompt-schema-config/reset-default",
|
||||
detail=json.dumps(
|
||||
{
|
||||
"reset_to_default": True,
|
||||
},
|
||||
ensure_ascii=False,
|
||||
),
|
||||
)
|
||||
return result
|
||||
|
||||
|
||||
@router.get("/export", response_model=VideoPromptSchemaExportOut, summary="导出视频提词 Schema 配置")
|
||||
@@ -68,8 +99,22 @@ async def import_config(
|
||||
admin: User = Depends(get_admin_user),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
_ = admin
|
||||
return await import_video_prompt_schema_config(db, data=req.data, is_enabled=req.is_enabled)
|
||||
result = await import_video_prompt_schema_config(db, data=req.data, is_enabled=req.is_enabled)
|
||||
await log_operation(
|
||||
db,
|
||||
admin.id,
|
||||
admin.username,
|
||||
"导入视频提词 Schema 配置",
|
||||
"POST",
|
||||
"/admin/video-prompt-schema-config/import",
|
||||
detail=json.dumps(
|
||||
{
|
||||
"is_enabled": req.is_enabled,
|
||||
},
|
||||
ensure_ascii=False,
|
||||
),
|
||||
)
|
||||
return result
|
||||
|
||||
|
||||
@router.post("/preview", response_model=VideoPromptSchemaPreviewOut, summary="预览视频提词运行时 Schema")
|
||||
|
||||
@@ -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": "密码修改成功"}
|
||||
|
||||
|
||||
@@ -493,6 +592,22 @@ async def create_admin_notification(
|
||||
db, None, title, content, notif_type, push_ws=True
|
||||
)
|
||||
|
||||
await log_operation(
|
||||
db,
|
||||
admin.id,
|
||||
admin.username,
|
||||
f"{'发送通知给指定用户' if target_user_id else '广播通知'}: {title}",
|
||||
"POST",
|
||||
"/admin/notifications",
|
||||
detail=json.dumps(
|
||||
{
|
||||
"title": title,
|
||||
"target_user_id": target_user_id,
|
||||
"type": notif_type,
|
||||
},
|
||||
ensure_ascii=False,
|
||||
),
|
||||
)
|
||||
return {"message": "ok"}
|
||||
|
||||
|
||||
@@ -513,6 +628,21 @@ async def delete_admin_notification(
|
||||
)
|
||||
await db.delete(notif)
|
||||
await db.flush()
|
||||
await log_operation(
|
||||
db,
|
||||
admin.id,
|
||||
admin.username,
|
||||
f"删除通知: {notif.title}",
|
||||
"DELETE",
|
||||
f"/admin/notifications/{notification_id}",
|
||||
detail=json.dumps(
|
||||
{
|
||||
"notification_id": notification_id,
|
||||
"title": notif.title,
|
||||
},
|
||||
ensure_ascii=False,
|
||||
),
|
||||
)
|
||||
return {"message": "ok"}
|
||||
|
||||
|
||||
@@ -577,6 +707,15 @@ async def batch_update_payment_configs(
|
||||
value=value,
|
||||
))
|
||||
await db.flush()
|
||||
await log_operation(
|
||||
db,
|
||||
admin.id,
|
||||
admin.username,
|
||||
"批量更新支付配置",
|
||||
"PUT",
|
||||
"/admin/payment-configs/batch",
|
||||
detail=json.dumps(list(req.keys()), ensure_ascii=False),
|
||||
)
|
||||
return {"ok": True}
|
||||
|
||||
|
||||
@@ -767,6 +906,22 @@ async def update_payment_config(
|
||||
raise HTTPException(status_code=404, detail="支付配置不存在")
|
||||
config.value = req.value
|
||||
await db.flush()
|
||||
await log_operation(
|
||||
db,
|
||||
admin.id,
|
||||
admin.username,
|
||||
f"更新支付配置: {config.key}",
|
||||
"PUT",
|
||||
f"/admin/payment-configs/{config_id}",
|
||||
detail=json.dumps(
|
||||
{
|
||||
"config_id": config_id,
|
||||
"key": config.key,
|
||||
"value": req.value,
|
||||
},
|
||||
ensure_ascii=False,
|
||||
),
|
||||
)
|
||||
return {
|
||||
"id": config.id,
|
||||
"key": config.key,
|
||||
@@ -785,6 +940,20 @@ async def refund_payment_order(
|
||||
result = await process_refund(db, order_no)
|
||||
if not result.get("success"):
|
||||
raise HTTPException(status_code=400, detail=result.get("message", "退款失败"))
|
||||
await log_operation(
|
||||
db,
|
||||
admin.id,
|
||||
admin.username,
|
||||
f"订单退款: {order_no}",
|
||||
"POST",
|
||||
f"/admin/payment-orders/{order_no}/refund",
|
||||
detail=json.dumps(
|
||||
{
|
||||
"order_no": order_no,
|
||||
},
|
||||
ensure_ascii=False,
|
||||
),
|
||||
)
|
||||
return result
|
||||
|
||||
|
||||
@@ -842,6 +1011,22 @@ async def create_industry_config(
|
||||
)
|
||||
db.add(config)
|
||||
await db.flush()
|
||||
await log_operation(
|
||||
db,
|
||||
admin.id,
|
||||
admin.username,
|
||||
f"创建行业配置: {req.label}",
|
||||
"POST",
|
||||
"/admin/industry-configs",
|
||||
detail=json.dumps(
|
||||
{
|
||||
"config_id": config.id,
|
||||
"key": req.key,
|
||||
"label": req.label,
|
||||
},
|
||||
ensure_ascii=False,
|
||||
),
|
||||
)
|
||||
return _serialize_industry(config)
|
||||
|
||||
|
||||
@@ -866,6 +1051,22 @@ async def update_industry_config(
|
||||
config.is_active = req.is_active
|
||||
config.sort_order = req.sort_order
|
||||
await db.flush()
|
||||
await log_operation(
|
||||
db,
|
||||
admin.id,
|
||||
admin.username,
|
||||
f"更新行业配置: {req.label}",
|
||||
"PUT",
|
||||
f"/admin/industry-configs/{config_id}",
|
||||
detail=json.dumps(
|
||||
{
|
||||
"config_id": config_id,
|
||||
"key": req.key,
|
||||
"label": req.label,
|
||||
},
|
||||
ensure_ascii=False,
|
||||
),
|
||||
)
|
||||
return _serialize_industry(config)
|
||||
|
||||
|
||||
@@ -883,6 +1084,22 @@ async def delete_industry_config(
|
||||
raise HTTPException(status_code=404, detail="行业配置不存在")
|
||||
await db.delete(config)
|
||||
await db.flush()
|
||||
await log_operation(
|
||||
db,
|
||||
admin.id,
|
||||
admin.username,
|
||||
f"删除行业配置: {config.label}",
|
||||
"DELETE",
|
||||
f"/admin/industry-configs/{config_id}",
|
||||
detail=json.dumps(
|
||||
{
|
||||
"config_id": config_id,
|
||||
"key": config.key,
|
||||
"label": config.label,
|
||||
},
|
||||
ensure_ascii=False,
|
||||
),
|
||||
)
|
||||
return {"message": "ok"}
|
||||
|
||||
|
||||
@@ -908,6 +1125,21 @@ async def create_video_engine(
|
||||
engine = VideoEngine(id=generate_id(), **req.model_dump())
|
||||
db.add(engine)
|
||||
await db.flush()
|
||||
await log_operation(
|
||||
db,
|
||||
admin.id,
|
||||
admin.username,
|
||||
f"创建视频引擎: {req.name}",
|
||||
"POST",
|
||||
"/admin/video-engines",
|
||||
detail=json.dumps(
|
||||
{
|
||||
"engine_id": engine.id,
|
||||
"name": req.name,
|
||||
},
|
||||
ensure_ascii=False,
|
||||
),
|
||||
)
|
||||
return engine
|
||||
|
||||
|
||||
@@ -927,6 +1159,21 @@ async def update_video_engine(
|
||||
for k, v in req.model_dump().items():
|
||||
setattr(engine, k, v)
|
||||
await db.flush()
|
||||
await log_operation(
|
||||
db,
|
||||
admin.id,
|
||||
admin.username,
|
||||
f"更新视频引擎: {engine.name}",
|
||||
"PUT",
|
||||
f"/admin/video-engines/{engine_id}",
|
||||
detail=json.dumps(
|
||||
{
|
||||
"engine_id": engine_id,
|
||||
"name": engine.name,
|
||||
},
|
||||
ensure_ascii=False,
|
||||
),
|
||||
)
|
||||
return engine
|
||||
|
||||
|
||||
@@ -944,6 +1191,21 @@ async def delete_video_engine(
|
||||
raise HTTPException(status_code=404, detail="视频引擎不存在")
|
||||
await db.delete(engine)
|
||||
await db.flush()
|
||||
await log_operation(
|
||||
db,
|
||||
admin.id,
|
||||
admin.username,
|
||||
f"删除视频引擎: {engine.name}",
|
||||
"DELETE",
|
||||
f"/admin/video-engines/{engine_id}",
|
||||
detail=json.dumps(
|
||||
{
|
||||
"engine_id": engine_id,
|
||||
"name": engine.name,
|
||||
},
|
||||
ensure_ascii=False,
|
||||
),
|
||||
)
|
||||
return {"message": "ok"}
|
||||
|
||||
|
||||
@@ -969,6 +1231,21 @@ async def create_image_engine(
|
||||
engine = ImageEngine(id=generate_id(), **req.model_dump())
|
||||
db.add(engine)
|
||||
await db.flush()
|
||||
await log_operation(
|
||||
db,
|
||||
admin.id,
|
||||
admin.username,
|
||||
f"创建图片引擎: {req.name}",
|
||||
"POST",
|
||||
"/admin/image-engines",
|
||||
detail=json.dumps(
|
||||
{
|
||||
"engine_id": engine.id,
|
||||
"name": req.name,
|
||||
},
|
||||
ensure_ascii=False,
|
||||
),
|
||||
)
|
||||
return engine
|
||||
|
||||
|
||||
@@ -988,6 +1265,21 @@ async def update_image_engine(
|
||||
for k, v in req.model_dump().items():
|
||||
setattr(engine, k, v)
|
||||
await db.flush()
|
||||
await log_operation(
|
||||
db,
|
||||
admin.id,
|
||||
admin.username,
|
||||
f"更新图片引擎: {engine.name}",
|
||||
"PUT",
|
||||
f"/admin/image-engines/{engine_id}",
|
||||
detail=json.dumps(
|
||||
{
|
||||
"engine_id": engine_id,
|
||||
"name": engine.name,
|
||||
},
|
||||
ensure_ascii=False,
|
||||
),
|
||||
)
|
||||
return engine
|
||||
|
||||
|
||||
@@ -1005,6 +1297,21 @@ async def delete_image_engine(
|
||||
raise HTTPException(status_code=404, detail="图片引擎不存在")
|
||||
await db.delete(engine)
|
||||
await db.flush()
|
||||
await log_operation(
|
||||
db,
|
||||
admin.id,
|
||||
admin.username,
|
||||
f"删除图片引擎: {engine.name}",
|
||||
"DELETE",
|
||||
f"/admin/image-engines/{engine_id}",
|
||||
detail=json.dumps(
|
||||
{
|
||||
"engine_id": engine_id,
|
||||
"name": engine.name,
|
||||
},
|
||||
ensure_ascii=False,
|
||||
),
|
||||
)
|
||||
return {"message": "ok"}
|
||||
|
||||
|
||||
@@ -1056,6 +1363,22 @@ async def create_credit_ratio(
|
||||
ratio = CreditRatio(id=generate_id(), **data)
|
||||
db.add(ratio)
|
||||
await db.flush()
|
||||
await log_operation(
|
||||
db,
|
||||
admin.id,
|
||||
admin.username,
|
||||
f"创建积分规则: {data.get('gen_type', '')} - {data.get('model_config_id', '')}",
|
||||
"POST",
|
||||
"/admin/credit-ratios",
|
||||
detail=json.dumps(
|
||||
{
|
||||
"ratio_id": ratio.id,
|
||||
"gen_type": data.get("gen_type"),
|
||||
"model_config_id": data.get("model_config_id"),
|
||||
},
|
||||
ensure_ascii=False,
|
||||
),
|
||||
)
|
||||
return ratio
|
||||
|
||||
|
||||
@@ -1079,6 +1402,22 @@ async def update_credit_ratio(
|
||||
for k, v in data.items():
|
||||
setattr(ratio, k, v)
|
||||
await db.flush()
|
||||
await log_operation(
|
||||
db,
|
||||
admin.id,
|
||||
admin.username,
|
||||
f"更新积分规则: {data.get('gen_type', '')} - {data.get('model_config_id', '')}",
|
||||
"PUT",
|
||||
f"/admin/credit-ratios/{ratio_id}",
|
||||
detail=json.dumps(
|
||||
{
|
||||
"ratio_id": ratio_id,
|
||||
"gen_type": data.get("gen_type"),
|
||||
"model_config_id": data.get("model_config_id"),
|
||||
},
|
||||
ensure_ascii=False,
|
||||
),
|
||||
)
|
||||
return ratio
|
||||
|
||||
|
||||
@@ -1096,6 +1435,22 @@ async def delete_credit_ratio(
|
||||
raise HTTPException(status_code=404, detail="积分比例不存在")
|
||||
await db.delete(ratio)
|
||||
await db.flush()
|
||||
await log_operation(
|
||||
db,
|
||||
admin.id,
|
||||
admin.username,
|
||||
f"删除积分规则: {ratio.gen_type} - {ratio.model_config_id}",
|
||||
"DELETE",
|
||||
f"/admin/credit-ratios/{ratio_id}",
|
||||
detail=json.dumps(
|
||||
{
|
||||
"ratio_id": ratio_id,
|
||||
"gen_type": ratio.gen_type,
|
||||
"model_config_id": ratio.model_config_id,
|
||||
},
|
||||
ensure_ascii=False,
|
||||
),
|
||||
)
|
||||
return {"message": "ok"}
|
||||
|
||||
|
||||
@@ -1136,6 +1491,21 @@ async def create_model_config(
|
||||
config = ModelConfig(id=generate_id(), **req.model_dump())
|
||||
db.add(config)
|
||||
await db.flush()
|
||||
await log_operation(
|
||||
db,
|
||||
admin.id,
|
||||
admin.username,
|
||||
f"创建模型配置: {req.name}",
|
||||
"POST",
|
||||
"/admin/model-configs",
|
||||
detail=json.dumps(
|
||||
{
|
||||
"config_id": config.id,
|
||||
"name": req.name,
|
||||
},
|
||||
ensure_ascii=False,
|
||||
),
|
||||
)
|
||||
return config
|
||||
|
||||
|
||||
@@ -1153,6 +1523,21 @@ async def update_model_config(
|
||||
for k, v in req.model_dump().items():
|
||||
setattr(config, k, v)
|
||||
await db.flush()
|
||||
await log_operation(
|
||||
db,
|
||||
admin.id,
|
||||
admin.username,
|
||||
f"更新模型配置: {config.name}",
|
||||
"PUT",
|
||||
f"/admin/model-configs/{config_id}",
|
||||
detail=json.dumps(
|
||||
{
|
||||
"config_id": config_id,
|
||||
"name": config.name,
|
||||
},
|
||||
ensure_ascii=False,
|
||||
),
|
||||
)
|
||||
return config
|
||||
|
||||
|
||||
@@ -1168,6 +1553,21 @@ async def delete_model_config(
|
||||
raise HTTPException(status_code=404, detail="配置不存在")
|
||||
await db.delete(config)
|
||||
await db.flush()
|
||||
await log_operation(
|
||||
db,
|
||||
admin.id,
|
||||
admin.username,
|
||||
f"删除模型配置: {config.name}",
|
||||
"DELETE",
|
||||
f"/admin/model-configs/{config_id}",
|
||||
detail=json.dumps(
|
||||
{
|
||||
"config_id": config_id,
|
||||
"name": config.name,
|
||||
},
|
||||
ensure_ascii=False,
|
||||
),
|
||||
)
|
||||
return {"message": "ok"}
|
||||
|
||||
|
||||
@@ -1195,6 +1595,22 @@ async def update_system_config(
|
||||
raise HTTPException(status_code=404, detail="配置不存在")
|
||||
config.value = str(req.value)
|
||||
await db.commit()
|
||||
await log_operation(
|
||||
db,
|
||||
admin.id,
|
||||
admin.username,
|
||||
f"更新系统配置: {config.key}",
|
||||
"PUT",
|
||||
f"/admin/system-configs/{config_id}",
|
||||
detail=json.dumps(
|
||||
{
|
||||
"config_id": config_id,
|
||||
"key": config.key,
|
||||
"value": req.value,
|
||||
},
|
||||
ensure_ascii=False,
|
||||
),
|
||||
)
|
||||
return config
|
||||
|
||||
|
||||
@@ -1557,6 +1973,21 @@ async def admin_update_generation_status(
|
||||
if new_status == "completed":
|
||||
record.generated_at = datetime.now()
|
||||
await db.flush()
|
||||
await log_operation(
|
||||
db,
|
||||
admin.id,
|
||||
admin.username,
|
||||
f"更新生成记录状态: {new_status}",
|
||||
"PUT",
|
||||
f"/admin/generation-records/{record_id}/status",
|
||||
detail=json.dumps(
|
||||
{
|
||||
"record_id": record_id,
|
||||
"new_status": new_status,
|
||||
},
|
||||
ensure_ascii=False,
|
||||
),
|
||||
)
|
||||
return {"message": "ok"}
|
||||
|
||||
|
||||
@@ -1688,6 +2119,22 @@ async def admin_generate_video(
|
||||
)
|
||||
await db.flush()
|
||||
|
||||
await log_operation(
|
||||
db,
|
||||
admin.id,
|
||||
admin.username,
|
||||
f"管理员触发生成{type_str}: {record_id}",
|
||||
"POST",
|
||||
f"/admin/generation-records/{record_id}/generate",
|
||||
detail=json.dumps(
|
||||
{
|
||||
"record_id": record_id,
|
||||
"gen_type": record.gen_type,
|
||||
"project_name": project_name,
|
||||
},
|
||||
ensure_ascii=False,
|
||||
),
|
||||
)
|
||||
return {"message": "ok", "record_id": record_id}
|
||||
|
||||
|
||||
@@ -1739,6 +2186,22 @@ async def upload_pdf(
|
||||
value=url,
|
||||
))
|
||||
await db.commit()
|
||||
await log_operation(
|
||||
db,
|
||||
admin.id,
|
||||
admin.username,
|
||||
f"上传PDF文件: {file.filename}",
|
||||
"POST",
|
||||
"/admin/upload-pdf",
|
||||
detail=json.dumps(
|
||||
{
|
||||
"filename": file.filename,
|
||||
"config_key": config_key,
|
||||
"url": url,
|
||||
},
|
||||
ensure_ascii=False,
|
||||
),
|
||||
)
|
||||
|
||||
return {"url": url}
|
||||
|
||||
@@ -1785,6 +2248,21 @@ async def upload_logo(
|
||||
description="网站Logo图片",
|
||||
))
|
||||
await db.commit()
|
||||
await log_operation(
|
||||
db,
|
||||
admin.id,
|
||||
admin.username,
|
||||
f"上传Logo图片: {file.filename}",
|
||||
"POST",
|
||||
"/admin/upload-logo",
|
||||
detail=json.dumps(
|
||||
{
|
||||
"filename": file.filename,
|
||||
"url": url,
|
||||
},
|
||||
ensure_ascii=False,
|
||||
),
|
||||
)
|
||||
|
||||
return {"url": url}
|
||||
|
||||
|
||||
@@ -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"}
|
||||
|
||||
@@ -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}
|
||||
|
||||
Reference in New Issue
Block a user