feat(platform): 首页门面三统计 + 运营后台展示模式配置

- 新表 platform_stat_display:每个指标可选 real/manual/random 三种
  展示模式,含建表 + anchor_minutes 两个 alembic migration
- 公开接口 GET /api/v1/platform/stats(无鉴权门面数字,登录前可读)
- 运营后台 GET/PATCH /admin/api/dashboard-display 配置展示模式
- 配套 model/repository/schema,注册 router(app/main+admin/main),
  导出 model,补 docs(api/database 索引及 3 篇详情)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
OuYingJun1024
2026-06-06 22:45:07 +08:00
parent f5d1a1a20d
commit cfeacb4bab
16 changed files with 726 additions and 1 deletions
+2
View File
@@ -18,6 +18,7 @@ from app.admin.routers.audit import router as audit_router
from app.admin.routers.auth import router as auth_router
from app.admin.routers.config import router as config_router
from app.admin.routers.dashboard import router as dashboard_router
from app.admin.routers.dashboard_display import router as dashboard_display_router
from app.admin.routers.feedback import router as feedback_router
from app.admin.routers.users import router as users_router
from app.admin.routers.wallet import router as wallet_router
@@ -73,6 +74,7 @@ def health() -> dict[str, str]:
admin_app.include_router(auth_router)
admin_app.include_router(dashboard_router)
admin_app.include_router(dashboard_display_router)
admin_app.include_router(users_router)
admin_app.include_router(wallet_router)
admin_app.include_router(withdraw_router)
+69
View File
@@ -0,0 +1,69 @@
"""admin 首页三统计展示配置:列出三指标配置(+ 真实值预览)+ 改某指标(带审计 + 校验)。
每指标可独立选 real/manual/random。计算逻辑见 app/repositories/platform_stat.py。
权限:operator 可改(运营调门面数字),super 恒可。
"""
from __future__ import annotations
from typing import Annotated
from fastapi import APIRouter, Depends, HTTPException, Request
from app.admin.audit import write_audit
from app.admin.deps import AdminDb, get_client_ip, get_current_admin, require_role
from app.admin.schemas.dashboard_display import (
PlatformStatItemOut,
PlatformStatUpdateRequest,
)
from app.models.admin import AdminUser
from app.repositories import platform_stat
router = APIRouter(
prefix="/admin/api/dashboard-display",
tags=["admin-dashboard-display"],
dependencies=[Depends(get_current_admin)],
)
@router.get("", response_model=list[PlatformStatItemOut], summary="首页三统计配置 + 真实值预览")
def list_display(db: AdminDb) -> list[PlatformStatItemOut]:
return [PlatformStatItemOut(**item) for item in platform_stat.get_config(db)]
@router.patch(
"/{metric}", response_model=PlatformStatItemOut, summary="改某指标展示配置(带审计)"
)
def update_display(
metric: str,
body: PlatformStatUpdateRequest,
request: Request,
admin: Annotated[AdminUser, Depends(require_role("operator"))],
db: AdminDb,
) -> PlatformStatItemOut:
try:
before, after = platform_stat.update_config(
db,
metric,
mode=body.mode,
manual_value=body.manual_value,
random_mult_min=body.random_mult_min,
random_mult_max=body.random_mult_max,
random_tick_seconds=body.random_tick_seconds,
random_anchor_minutes=body.random_anchor_minutes,
random_initial=body.random_initial,
apply_now=body.apply_now,
admin_id=admin.id,
commit=False,
)
except ValueError as e:
raise HTTPException(status_code=400, detail=str(e)) from e
write_audit(
db, admin, action="dashboard_display.set", target_type="platform_stat",
target_id=metric, detail={"before": before, "after": after},
ip=get_client_ip(request), commit=False,
)
db.commit()
item = next(it for it in platform_stat.get_config(db) if it["metric"] == metric)
return PlatformStatItemOut(**item)
+38
View File
@@ -0,0 +1,38 @@
"""admin 首页三统计展示配置 schemas。
倍率用千分比整数(1.000→1000);total_saved 的 manual_value / random_current 单位是分。
"""
from __future__ import annotations
from pydantic import BaseModel
class PlatformStatItemOut(BaseModel):
metric: str # help_users / total_compares / total_saved
label: str # 帮助用户 / 完成比价 / 累计节省
unit: str # 人 / 次 / 分
mode: str # real / manual / random
manual_value: int | None = None
random_mult_min: int
random_mult_max: int
random_tick_seconds: int
random_anchor_minutes: int
random_current: int | None = None
random_last_tick_at: str | None = None
real_value: int # 当前真实值(给运营对比参考)
updated_at: str | None = None
class PlatformStatUpdateRequest(BaseModel):
"""改某指标配置(均可选,只改传了的字段)。"""
mode: str | None = None
manual_value: int | None = None
random_mult_min: int | None = None
random_mult_max: int | None = None
random_tick_seconds: int | None = None
random_anchor_minutes: int | None = None
# 切 random 时的初始基数;不传则用当前真实值播种
random_initial: int | None = None
# 立即更新:不等更新钟点,保存后马上把展示值刷新一次(real 快照/manual 生效/random ×一档)
apply_now: bool = False