修复用户反馈机型与系统版本补全 #181

Merged
guke merged 1 commits from codex/feedback-device-details into main 2026-07-27 13:33:57 +08:00
Member

改动说明

  • 反馈列表按同一用户、同一设备编码和反馈提交时间,补全可读机型、厂商及 ROM 大版本
  • 补充常见线上机型编码映射
  • 更新本地 mock 数据并增加时间边界回归测试

验证

  • 反馈相关测试:3 项通过
  • Ruff 检查通过
## 改动说明 - 反馈列表按同一用户、同一设备编码和反馈提交时间,补全可读机型、厂商及 ROM 大版本 - 补充常见线上机型编码映射 - 更新本地 mock 数据并增加时间边界回归测试 ## 验证 - 反馈相关测试:3 项通过 - Ruff 检查通过
linkeyu added 1 commit 2026-07-27 10:56:01 +08:00
反馈列表按同一用户、同一设备和提交时间补全可读机型及厂商系统版本,并更新 mock 数据与回归测试。
Member

🤖 review-pr 深审结论(后端 · app-server #181)

配套前端 admin-web #85。本仓有本地克隆,走完整仓 worktree 深审 + 实跑测试。

总评 🟢 可合:字段契约与前端对齐,时间窗匹配正确、时区一致、单条窗口查询无 N+1,13 项反馈测试全过、新增回归测试通过。置信度 ~0.9。

值得改(低危 · 健壮性/一致性)

  • low-med queries.py_attach_feedback_device_details:三个瞬态字段只在 candidates(有 device_model 的反馈)上赋值,历史反馈(device_model=None,线上真实存在)从不赋这三个属性。实测(Pydantic 2.12.5 + from_attributes不会 500,但 model_validate 对缺失属性既不报错也不填默认值,model_dump_json整个省略这三个 key(不是 null,是键缺失);前端因防御式写法当前不受影响。但这偏离了本仓约定——_attach_user_info 是对所有 record 无条件挂 phone/nickname,正为保证 from_attributes 永远读得到。建议把 = None 默认赋值移到所有 items(而非仅 candidates),响应契约更干净、也不依赖前端恰好防御到位。

Nits(非阻塞)

  • low _DEVICE_MARKETING_NAMES 仅 6 条硬编码机型映射,多数线上机型会退回展示原始 Build.MODEL 编码;后续可考虑挪到数据表/配置。
  • low approve/reject 接口对单条 fb 直接 model_validate 不走补全(新字段被省略),但它本就没挂 phone/nickname,新字段只是沿用既有模式,影响可忽略。
  • low 新增测试只覆盖「有 device_model 且命中比价记录」的正路,缺「有 device_model 但无匹配」「历史 device_model=None」两个边界断言。

写得好 👍

  • 补全用单条窗口查询row_number() over (partition_by feedback_id order by created_at desc, id desc))取反馈时间点或之前最近一条比价记录,无 N+1,分页后调用、集合有界。
  • 时间窗 created_at <= Feedback.created_at 两侧均 DateTime(timezone=True)无时区错配;join 带 user_id 相等,而 ComparisonRecord.user_id 可空、Feedback.user_id NOT NULL,SQL 里天然排除孤儿比价行,不会误配。
  • 测试特意造「未来 5 分钟的同机型升级记录」验证不被选中,边界意识到位。

—— 自动深审,仅供参考,具体以人工判断为准。

## 🤖 review-pr 深审结论(后端 · app-server #181) 配套前端 admin-web #85。本仓有本地克隆,走完整仓 worktree 深审 + 实跑测试。 **总评 🟢 可合**:字段契约与前端对齐,时间窗匹配正确、时区一致、单条窗口查询无 N+1,13 项反馈测试全过、新增回归测试通过。置信度 ~0.9。 ### 值得改(低危 · 健壮性/一致性) - **low-med** `queries.py` 的 `_attach_feedback_device_details`:三个瞬态字段只在 candidates(有 `device_model` 的反馈)上赋值,历史反馈(`device_model=None`,线上真实存在)从不赋这三个属性。实测(Pydantic 2.12.5 + `from_attributes`)**不会 500**,但 `model_validate` 对缺失属性既不报错也不填默认值,`model_dump_json` 会**整个省略这三个 key**(不是 `null`,是键缺失);前端因防御式写法当前不受影响。但这偏离了本仓约定——`_attach_user_info` 是对**所有** record 无条件挂 `phone`/`nickname`,正为保证 `from_attributes` 永远读得到。建议把 `= None` 默认赋值移到**所有 items**(而非仅 candidates),响应契约更干净、也不依赖前端恰好防御到位。 ### Nits(非阻塞) - **low** `_DEVICE_MARKETING_NAMES` 仅 6 条硬编码机型映射,多数线上机型会退回展示原始 `Build.MODEL` 编码;后续可考虑挪到数据表/配置。 - **low** approve/reject 接口对单条 `fb` 直接 `model_validate` 不走补全(新字段被省略),但它本就没挂 `phone`/`nickname`,新字段只是沿用既有模式,影响可忽略。 - **low** 新增测试只覆盖「有 device_model 且命中比价记录」的正路,缺「有 device_model 但无匹配」「历史 device_model=None」两个边界断言。 ### 写得好 👍 - 补全用**单条窗口查询**(`row_number() over (partition_by feedback_id order by created_at desc, id desc)`)取反馈时间点或之前最近一条比价记录,**无 N+1**,分页后调用、集合有界。 - 时间窗 `created_at <= Feedback.created_at` 两侧均 `DateTime(timezone=True)`,**无时区错配**;join 带 `user_id` 相等,而 `ComparisonRecord.user_id` 可空、`Feedback.user_id` NOT NULL,SQL 里天然排除孤儿比价行,不会误配。 - 测试特意造「未来 5 分钟的同机型升级记录」验证不被选中,边界意识到位。 —— 自动深审,仅供参考,具体以人工判断为准。
guke merged commit 22a1105000 into main 2026-07-27 13:33:57 +08:00
Sign in to join this conversation.
No Reviewers
No Label
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: WonderableAI/shaguabijia-app-server#181