27f76918b2
本 PR 汇合三块运营后台改动(原 #50 仅含其中「加固」一块,已并入本 PR 并关闭)。 ## 1. review 加固 (436b2a3) - 超管防自锁:降级/禁用最后一个 active super_admin 前校验,杜绝零超管死局 - 时间筛选统一 tz-aware(列为 timestamptz),比较绝对时刻、不依赖 DB 会话时区 - 上报审核 / 调余额(set·扣减)加行锁,防并发/连点重复发钱 - ad_audit 复算排序补 id 次级键;health-check 限 finance;调账/拒绝 reason 去空白校验 ## 2. 反馈改版 (5a18dbb) - contact 可选、截图≤6;admin 反馈列表筛选/排序;admin·wallet 接口调整 + docs ## 3. 列表页码分页 (1a7a624) - CursorPage 加 total;新增 offset_paginate(count 与分页同源) - 上报/审计日志从 id 游标改 offset 分页(支持跳页) - 用户 / 提现 / 上报 / 审计日志 四页接入页码分页 测试:admin 套件 47 passed。前端配套改动见 shaguabijia-admin-web。 --------- Co-authored-by: OuYingJun1024 <1034284404@qq.com> Reviewed-on: #51 Co-authored-by: ouzhou <ouzhou@wonderable.ai> Co-committed-by: ouzhou <ouzhou@wonderable.ai>
63 lines
2.2 KiB
Python
63 lines
2.2 KiB
Python
"""帮助与反馈 endpoint。
|
|
|
|
路由前缀 `/api/v1/feedback`,需 Bearer 鉴权(反馈绑到登录用户,便于回访)。
|
|
POST / 提交反馈(multipart:content 必填;contact 可选(原型改版后客户端已不再采集);images 可选 ≤6 张)
|
|
|
|
截图复用 [app.core.media] 落盘到 /media/feedback/。
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
|
|
from fastapi import APIRouter, File, Form, HTTPException, UploadFile
|
|
|
|
from app.api.deps import CurrentUser, DbSession
|
|
from app.core import media
|
|
from app.repositories import feedback as feedback_repo
|
|
from app.schemas.feedback import FeedbackOut
|
|
|
|
logger = logging.getLogger("shagua.feedback")
|
|
|
|
router = APIRouter(prefix="/api/v1/feedback", tags=["feedback"])
|
|
|
|
_MAX_IMAGES = 6
|
|
_CONTENT_MAX = 2000
|
|
_CONTACT_MAX = 128
|
|
|
|
|
|
@router.post("", response_model=FeedbackOut, summary="提交反馈")
|
|
async def submit_feedback(
|
|
user: CurrentUser,
|
|
db: DbSession,
|
|
content: str = Form(...),
|
|
# 原型改版后客户端不再采集联系方式;保留字段以兼容旧端 + 后续可能复用,默认空串。
|
|
contact: str = Form(default=""),
|
|
images: list[UploadFile] = File(default=[]),
|
|
) -> FeedbackOut:
|
|
content = content.strip()
|
|
contact = contact.strip()
|
|
if not content:
|
|
raise HTTPException(status_code=400, detail="反馈内容不能为空")
|
|
if len(content) > _CONTENT_MAX:
|
|
raise HTTPException(status_code=400, detail="反馈内容过长")
|
|
if len(contact) > _CONTACT_MAX:
|
|
raise HTTPException(status_code=400, detail="联系方式过长")
|
|
|
|
files = [f for f in (images or []) if f is not None and f.filename]
|
|
if len(files) > _MAX_IMAGES:
|
|
raise HTTPException(status_code=400, detail=f"最多上传 {_MAX_IMAGES} 张图片")
|
|
|
|
urls: list[str] = []
|
|
for f in files:
|
|
data = await f.read()
|
|
try:
|
|
urls.append(media.save_feedback_image(user.id, data))
|
|
except media.MediaError as e:
|
|
raise HTTPException(status_code=400, detail=str(e)) from e
|
|
|
|
fb = feedback_repo.create_feedback(
|
|
db, user_id=user.id, content=content, contact=contact, images=urls,
|
|
)
|
|
logger.info("feedback id=%d user_id=%d images=%d", fb.id, user.id, len(urls))
|
|
return FeedbackOut.model_validate(fb)
|