5efe624340
- 反馈:新增 GET /admin/api/feedbacks/summary(待审核/已采纳/未采纳/合计计数); 列表联表带出完整手机号 + 昵称(供 admin 点手机号查该用户全部反馈) - 低价审核:列表加 sort_by/sort_order(提交时间排序);联表带出手机号/昵称, 并按 comparison_record_id 关联比价记录带出 trace_id/trace_url + 机型/ROM/Android/app 版本 - feedback 表新增 app_version/device_model/rom_name/android_version 四列 + alembic 迁移; /api/v1/feedback 提交接口接收并落库,供 admin 反馈页展示「提交版本号」「机型OS版本」 (字段可空,旧端不传不影响提交) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: zzhyyyyy <2685922758@qq.com> Reviewed-on: #94 Co-authored-by: zhuzihao <zhuzihao@wonderable.ai> Co-committed-by: zhuzihao <zhuzihao@wonderable.ai>
66 lines
2.2 KiB
Python
66 lines
2.2 KiB
Python
"""admin 上报更低价审核 schemas。"""
|
|
from __future__ import annotations
|
|
|
|
from datetime import datetime
|
|
|
|
from pydantic import BaseModel, ConfigDict, Field, field_validator
|
|
|
|
|
|
class PriceReportOut(BaseModel):
|
|
"""admin 列表项:price_report 全字段(审核要看的快照 + 截图 + 状态 + 奖励)。"""
|
|
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
id: int
|
|
user_id: int
|
|
comparison_record_id: int | None = None
|
|
# 选中比价记录的快照
|
|
store_name: str | None = None
|
|
dish_summary: str | None = None
|
|
original_platform_id: str | None = None
|
|
original_platform_name: str | None = None
|
|
original_price_cents: int | None = None
|
|
# 用户上报的更低价
|
|
reported_platform_id: str
|
|
reported_platform_name: str
|
|
reported_price_cents: int
|
|
images: list[str] = []
|
|
# 审核
|
|
status: str
|
|
reject_reason: str | None = None
|
|
reward_coins: int | None = None
|
|
reviewed_at: datetime | None = None
|
|
created_at: datetime
|
|
# 联表瞬态字段:phone/nickname 由 _attach_user_info 挂(展示完整手机号、点击查该用户全部上报);
|
|
# 其余由 _attach_price_report_comparison 从关联比价记录挂:trace_*(Trace 列点跳调试链接)、
|
|
# device_model/rom_name/android_version(机型OS版本列)、app_version(提交版本号列)
|
|
phone: str | None = None
|
|
nickname: str | None = None
|
|
trace_id: str | None = None
|
|
trace_url: str | None = None
|
|
device_model: str | None = None
|
|
rom_name: str | None = None
|
|
android_version: str | None = None
|
|
app_version: str | None = None
|
|
|
|
|
|
class PriceReportRejectRequest(BaseModel):
|
|
reason: str = Field(min_length=1, max_length=256, description="拒绝理由,用户端记录页会看到")
|
|
|
|
@field_validator("reason")
|
|
@classmethod
|
|
def _reason_not_blank(cls, v: str) -> str:
|
|
# min_length=1 放过纯空白(" "),trim 后再校验非空,避免审计/用户端记录到空理由
|
|
if not v.strip():
|
|
raise ValueError("拒绝理由不能为空")
|
|
return v.strip()
|
|
|
|
|
|
class PriceReportSummary(BaseModel):
|
|
"""审核台顶部各状态计数。"""
|
|
|
|
pending: int
|
|
approved: int
|
|
rejected: int
|
|
total: int
|