petstore-docs/P0-研发落地清单.md

185 lines
22 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 宠小它 — P0 研发落地清单
> **本体对齐**:任务条目引用 [`ontology/`](./ontology/) 中的实体/规则 ID如 `entity:report`、`rule:BR-RPT-002`)。冲突以本体为准。
>
> 用途:发给研发拆任务与验收;**仅含 P0**(本期质量与主线闭环必需,不含 P1/P2 增强)。
> 依据:《产品全方位优化说明 v1》《本期主线》《宠主端预约体验优化》《产品设计文档》及当前仓库实现粗对齐。
> 更新2026-07-05P0 稳定化轮次:冻结本期范围,重新标记状态并绑定 Owner 与验收命令)
**关联** [产品全方位优化说明-v1](./产品全方位优化说明-v1.md) · [本期主线-下一步产品优化点](./本期主线-下一步产品优化点.md) · [宠主端预约体验优化-产品说明](./宠主端预约体验优化-产品说明.md) · [架构与产品评估报告-2026-07-05](./架构与产品评估报告-2026-07-05.md) · [Cursor P0 Handoff](./superpowers/plans/2026-07-05-cursor-p0-handoff.md)
---
## 本期 P0 范围(冻结口径)
本期 P0 范围:
- 门店端:预约列表、开始服务、填写报告、发送报告、回访池。
- 宠主端:预约填单、公开报告页、下次服务提醒留资、报告打开基础埋点。
- 报告能力:服务前/服务后照片、服务过程素材、短片生成与下载、分享链接/二维码/话术。
不进入 P0
- 会员、储值、套餐、复杂 CRM。
- 宠主账号内历史报告汇总。
- 完整经营分析看板。
- 即使已有预约容量模型,也没有排队/叫号与准点履约保证;对外仍不承诺「到店不排队」,统一文案为「在线选时段,到店更省心」。
---
## 状态标记说明
- ✅ 已实现:仓库已实现且通过本次构建/验收命令。
- 🟡 部分实现:主路径已落,但仍有 gap已列出需按验收命令闭环。
- ❌ 未实现:本期必须补齐。
- 🧪 待测试:实现可能已存在,但缺自动化测试或人工 smoke 证据。
---
## 优先级说明
- **P0**:本迭代必须交付;阻塞对外口碑、数据脏读、合规提示缺失、或主线(传播/成片/留资)不可用。
- 已部分实现的项:**仍保留**,以下方「验收标准」为准做 gap 关闭。
---
## A. 后端Spring Boot
| ID | 需求 | 验收标准 | Owner | 状态 | 验收命令 | 备注 |
|----|------|----------|-------|------|----------|------|
| **A1** | **预约状态机校验**`PUT /appointment/status`(及任意改状态入口)禁止非法迁移 | 仅允许:`new→doing`、`new→cancel`、`doing→done`、`doing→cancel`(与产品最终规则一致);非法迁移返回 **4xx + 明确业务码**(如 `INVALID_STATUS`、`CANCEL_NOT_ALLOWED` | Backend Core | 🟡 部分实现:`AppointmentService.updateStatus`/`startService` 已集中校验,但缺自动化测试 | `mvn -f backend/pom.xml test`(待补 `AppointmentServiceTest` | 与「谁点谁服务」一致Batch D 补测试 |
| **A2** | **开始服务**仅当 `status=new` | `startService``new` 时拒绝,勿静默改库 | Backend Core | 🟡 部分实现:服务层已校验,需补测试 | 同 A1 | 与 A1 同一套状态机 |
| **A3** | **取消预约**校验:仅允许目标状态为 `cancel` 且前置状态符合产品规则 | 与 A1 同一套状态机;前端取消按钮仅 `new` 时展示则后端仍要防篡改 | Backend Core | 🟡 部分实现:状态机已收口,需补测试 | 同 A1 | 防恶意请求 |
| **A4** | **留资去重**:同一 `report_token` + 同一手机号 | 二次提交返回成功态或「已提交」语义,不重复插库 | Report Media Backend | 🟡 部分实现:去重逻辑已存在,需补测试用例 | `mvn -f backend/pom.xml test`(待补 `ReportLeadControllerTest` | 口径见《本期主线》C |
| **A5** | **公开报告字段裁剪**`GET /api/report/get?token=` 不返回 top-level `userId/storeId/reportToken`;日志只记 token hash | 公开 token 请求只返回页面必要字段store 对象可返回 `id` 用于预约跳转 | System Architect / Report Media Backend | ✅ 已实现Batch B | 手工 curl + `mvn test` | 对应 P0-2 |
| **A6** | **报告一约一份**`Report.appointment_id` 唯一约束;服务层拒绝同预约重复提交 | 同一 `appointmentId` 第二次提交返回 409 / `REPORT_ALREADY_EXISTS`;非 `doing` 预约不能创建报告;无预约提交返回 `APPOINTMENT_REQUIRED` | System Architect / Data Model / Report Media Backend | ✅ 已实现Batch B/D`ReportServiceTest`/`ReportControllerTest` | `mvn -f backend/pom.xml test` | 对应 P0-3上线前需清理重复数据 |
| **A7** | **报告前后照片必填后端兜底** | 缺 before 返回 `BEFORE_PHOTO_REQUIRED`;缺 after 返回 `AFTER_PHOTO_REQUIRED` | Report Media Backend | ✅ 已实现Batch B`ReportControllerTest` | `mvn -f backend/pom.xml test` | 与前端 B5 同步落地 |
| **A8** | **统一鉴权上下文**HMAC session token + `CurrentUserContext`protected API 不再信任请求里的身份字段 | 未登录返回 401A 店用户访问 B 店预约/报告/线索返回 403customer 不能用任意 `userId` 创建他人宠物/预约 | System Architect / Backend Core | ✅ 已实现Batch C`SessionTokenServiceTest` | `mvn -f backend/pom.xml test` + 跨店/跨用户接口验证 | 对应 P0-1不引入完整 Spring Security |
| **A9** | **线索脱敏**`/report/leads` 默认不返回完整 `wechatOpenid/wechatUnionid` | 回包只有 `wechatBound`、`wechatOpenidMasked`、`wechatUnionidMasked` | Report Media Backend | ✅ 已实现Batch B | 手工 curl + `mvn test` | 对应 P0-2 |
| **A10** | **配置安全与启动迁移收口**`application.yml` 不保留真实敏感值;生产 profile 禁用 `ddl-auto:update`/`show-sql`、万能验证码和宽 CORS生产启动不写默认业务数据 | 仓库不含真实 DB/微信密钥;最终配置不安全时启动失败;有 liveness/readiness、只读 schema/数据预检和上线 smoke | Backend Ops / System Architect | ✅ 代码与脚本已实现;待真实环境留证 | `mvn test` + `deploy/release-preflight.sh` + `deploy/production-smoke.sh` | 对应 P0-8生产域名/凭据/迁移/线上 smoke 归 H3已暴露凭据需轮换 |
| **A11** | **上传路径与类型保护**`FileController` 归一化路径 + 扩展名/MIME 校验 | `/api/upload/image/../...`、伪装扩展名、超大文件均被拒绝 | Backend Core / System Architect | ✅ 已实现Batch D | 手工 curl + `mvn test` | 对应 P0-9 |
| **A12** | **最小测试基线**:补 `SessionTokenServiceTest`、`ReportServiceTest`、`ReportControllerTest`、`AppointmentServiceTest` | `mvn test` 不再输出 `No tests to run`,新增用例全部通过 | Core Flow QA / System Architect | ✅ 已实现Batch D | `mvn -f backend/pom.xml test` | 对应 P0-10持续回归数量以 Surefire 当次统计为准 |
| **A13** | **预约/报告身份语义拆分**:客户、创建人、服务技师、报告作者不得复用同一语义字段 | customer 自约身份从 session 派生门店代约必须提供真实宠主宠主报告按预约客户归属查询legacy 字段只做兼容 | System Architect / Backend Core / Report Media Backend | ✅ 已实现Phase 0 RC1 后) | `mvn -f backend/pom.xml test` + H5/小程序构建 | 迁移见 `backend/db/migrations/20260801_split_service_identity.sql` |
| **A14** | **StoreCustomer 门店客户主档**:客户列表不再临时拼成无 ID 投影 | 预约/留资自动建档同店账号与手机号唯一API 返回 storeCustomerId 且手机脱敏;历史数据可回填 | Backend Core / Report Media Backend / Data Model | ✅ 已实现(待生产迁移) | `mvn -f backend/pom.xml test` + `npm --prefix admin run build` | 迁移见 `backend/db/migrations/20260801_create_store_customer.sql` |
| **A15** | **BusinessEvent 业务事件落库**:经营漏斗不再依赖日志或前端猜测 | 首批 7 类事实统一落库稳定事件幂等metadata 不含 token/手机号/IP工作台已打开可统计历史可靠事实可回填 | Backend Core / Report Media Backend / Data Model | ✅ 已实现(待生产迁移) | `mvn -f backend/pom.xml test` + `npm --prefix admin run build` + 本体漂移审计 | 迁移见 `backend/db/migrations/20260801_create_business_event.sql` |
| **A16** | **真实预约容量与连续排班占用**:服务时长、门店并发容量和预约时长快照共同决定号源 | 长服务跨半小时桶占号;`walk_in` 消耗一份容量,`blocked` 关闭全部;并发创建不超卖 | Backend Core / Store Admin FE / Data Model | ✅ 已实现(待生产迁移) | backend 全测 + admin/H5/小程序构建 + 本体漂移审计 | 迁移见 `backend/db/migrations/20260801_create_booking_capacity.sql`;不等同排队/准点承诺 |
| **A17** | **报告显式确认发送状态**:传播动作与发送事实分离 | 历史为 `unknown`;新报告为 `unsent`;首次确认写不可覆盖回执与幂等 `report_sent`;工作台仅提醒新报告待发送 | Report Media Backend / Store Miniapp FE / Store Admin FE / Data Model | ✅ 已实现(待生产迁移) | backend 全测 + admin/H5/小程序构建 + 本体漂移审计 | 迁移见 `backend/db/migrations/20260801_create_report_send_status.sql`;不宣称微信投递成功 |
| **A18** | **门店开通 + 一次性员工邀请 + AuditLog** | 老板入驻须微信核验;历史开通状态 unknown完成清单需资料/容量/服务;邀请 token 只显一次且库内仅 hash手机号微信核验、限时/一次/可撤销;删员工立即失效旧会话 | Backend Core / Store Miniapp FE / Store Admin FE / Data Model | ✅ 代码完成(待生产迁移与微信真机验收) | backend 全测 + admin/H5/小程序构建 + 本体漂移审计 | 迁移见 `backend/db/migrations/20260802_create_store_onboarding.sql`;单角色账号冲突明确拒绝 |
| **A19** | **StoreCustomer 事实时间线**:客户详情不再只显示当前聚合快照 | 以 canonical `storeCustomerId` 同店校验BusinessEvent 按发生时间稳定分页;合并前别名事件不断档;预约/报告/回访提供低敏摘要和跳转不返回手机号原文、备注、媒体、token 或原始 metadata | Backend Core / Store Admin FE | ✅ 代码完成(待生产迁移) | backend 全测 + admin build + 本体漂移审计 | 迁移见 `backend/db/migrations/20260802_create_store_customer_timeline.sql`;既有事件不改写 |
---
## B. 小程序 — 门店端(老板/员工)
| ID | 需求 | 验收标准 | Owner | 状态 | 验收命令 | 备注 |
|----|------|----------|-------|------|----------|------|
| **B1** | **「已完成」Tab 列表默认不含已取消** | Tab「已完成」下默认仅展示 `status===done``cancel` 通过次级筛选或单独子 TAB 实现 | Store Miniapp FE | 🟡 部分实现(以现状为准做回归) | `npm --prefix frontend run build:h5` + 手工 smoke | 当前 `Home.vue` 需对照 |
| **B2** | **发送报告成功页**:同屏 报告链接 + 二维码 + 两条可复制话术 + 显式确认发送 | 话术支持变量;复制/预览不自动确认;宠主实际收到后选择微信/扫码/其他并确认 | Store Miniapp FE | ✅ 已实现(`ReportShareModal.vue` | `npm --prefix frontend run build:h5` + 手工 smoke | 对齐 A17不得把剪贴板动作冒充发送 |
| **B3** | **报告提交锁死提示** | 提交前弹窗固定文案:「提交后不可修改,请确认照片与备注无误」 | Store Miniapp FE | 🟡 部分实现:核对文案与位置 | 手工 smoke | 与产品 v1 锁死策略一致 |
| **B4** | **「我的」老板端分组标题** | 至少两组:`店铺与经营` / `团队与账号`;高频在上;使用 `.module-title` | Store Miniapp FE | ✅ 已实现 | `npm --prefix frontend run build:h5` + 手工 smoke | 见《产品全方位优化说明》§2.2 |
| **B5** | **报告前后照片必填前端校验** | 无 before 提示「请至少上传1张服务前照片」无 after 提示「请至少上传1张服务后照片」 | Store Miniapp FE / Customer Experience FE | ✅ 已实现Batch A | `npm --prefix frontend run build:h5` + `npm --prefix frontend run build:mp-weixin` | 对应 A7、P0-4 |
| **B6** | **小程序开通向导与员工邀请卡片** | 老板可查看/完成开通清单;创建邀请后原始口令只显示一次,可直接 `open-type=share` 转发;员工预览脱敏信息并经微信手机号核验加入 | Store Miniapp FE / Backend Core | ✅ 代码完成,待微信真机 smoke | H5 + mp-weixin 构建;老板/员工双微信真机验收 | H5 只预览,不绕过微信核验 |
---
## C. 小程序 — 宠主端customer
| ID | 需求 | 验收标准 | Owner | 状态 | 验收命令 | 备注 |
|----|------|----------|-------|------|----------|------|
| **C1** | **单一主预约路径** | 首屏仅保留一个主级「预约服务」入口;不再同屏强并存「大按钮 + 空态大按钮 + FAB +」等效重复 | Customer Experience FE | 🟡 部分实现:需对照当前 `Home.vue` 复核 | 手工 smoke | 见《宠主端预约体验优化》§9 |
| **C2** | **扫码 / 分享落地 `storeId`**(含小程序码 `scene` 解码) | 普通 `storeId` query、小程序码 `scene=storeId%3D1`、分享 path/query 均预填门店且可「更换门店」 | Customer Experience FE | ✅ 已实现Batch A | `npm --prefix frontend run build:h5` + `npm --prefix frontend run build:mp-weixin` + 手工 smoke | 对应 P0-6、P1-1 |
| **C3** | **登录时机与草稿**(冻结策略:提交时登录 + guest 草稿恢复) | 未登录可填单,提交时跳登录,登录后草稿恢复;草稿 key 不依赖 `userInfo.id`;不会产生 `userId: undefined` | Customer Experience FE / Backend Core | ✅ 已实现Batch A | 手工 smoke未登录填写 → 提交跳登录 → 草稿恢复 | 与 `Login.vue` redirect 联动 |
| **C4** | **异常可理解** | 无可用时段、网络失败、未登录:均有 toast / 空态 / 重试,禁止静默失败 | Customer Experience FE | 🟡 部分实现:需逐场景回归 | 手工 smoke | 专文 §7 |
| **C5** | **报告页角色判断**`isStaff` 改为角色判断boss/staff | customer 登录态打开公开报告页仍显示留资/预约 CTAboss/staff 才显示内部分享/保存 | Customer Experience FE | ✅ 已实现Batch A | 手工 smoke | 对应 P0-7 |
---
## D. H5 报告页(`report-view` / 独立 H5
| ID | 需求 | 验收标准 | Owner | 状态 | 验收命令 | 备注 |
|----|------|----------|-------|------|----------|------|
| **D1** | **隐私提示(短期策略)** | 首屏或固定条:「本页含宠物服务影像,请勿公开转发或发群」 | Customer Experience FE / Product Design | 🟡 部分实现:核对文案与位置 | 手工 smoke | 对齐《本期主线》A token 风险 |
| **D2** | **短片三态与文案对齐小程序** | `processing / failed / success``ReportHighlightBlock` 一致;失败展示归类中文,不暴露堆栈 | Customer Experience FE | 🟡 部分实现:需对照三态文案 | 手工 smoke | H5 与小程序报告页行为一致 |
| **D3** | **弱网 / 失败重试** | 报告主接口失败:按钮「重新加载」;超时阈值 810s 后出失败态 | Customer Experience FE | 🟡 部分实现 | 手工 smoke | 与《产品全方位优化说明》§3.3 |
| **D4** | **公开报告 token 使用方式**:不依赖回包 `reportToken`,只用路由 token 做留资/分享 | 公开报告页 `reminderToken` = 路由 token不读 `reportData.reportToken` | Customer Experience FE | ✅ 已实现Batch A | 手工 smoke + 代码审查 | 与 A5 联动 |
---
## E. 传播与埋点P0 最小集)
| ID | 需求 | 验收标准 | Owner | 状态 | 验收命令 | 备注 |
|----|------|----------|-------|------|----------|------|
| **E1** | **分享卡片元信息规则** | 小程序 `onShareAppMessage`:标题「宠物名 + 服务 + 门店」;封面优先级 成片封面 > 服务后首图 > Logo | Customer Experience FE | ✅ 已实现(`reportShare.js` | 手工 smoke + 代码审查 | 见 `frontend/src/utils/reportShare.js` |
| **E2** | **埋点:报告打开** | 前端提交 token 定位报告;事件表仅存 report ID、首次/再次和时间,不存 token/hashH5/小程序均上报 | Report Share QA / Backend Ops | ✅ 已落 BusinessEvent 并进入工作台去重统计(待生产迁移) | `BusinessEventServiceTest` + admin build | 对应 P0-10、P1-4 |
| **E3** | **埋点事件 schema**(最小集) | 定义 `report_opened`、`report_reopened`、`highlight_succeeded`、`highlight_failed`、`video_play`、`video_save`、`lead_submitted` 字段、来源、统计窗口 | Report Share QA / Product Design | ✅ 已冻结;核心履约/打开/留资已落库,成片与客户端播放事件待接入 | 见《本期主线-下一步产品优化点》埋点 schema 表 | 对应 P1-4 |
---
## F. 视觉与规范(工程债)
| ID | 需求 | 验收标准 | Owner | 状态 | 验收命令 | 备注 |
|----|------|----------|-------|------|----------|------|
| **F1** | **禁止业务页硬编码主色** | 主色/品牌浅底统一 `var(--c-brand)` 等;`rg "#16a34a|#2db96d"` 业务 vue 内为 0`global.css`/极少量渐变定义) | Store Miniapp FE / Customer Experience FE | 🟡 部分实现 | `rg "#16a34a|#2db96d" frontend/src/pages` | 《前端 UI 规范》已对齐 `--c-*` |
| **F2** | **空态禁用 emoji 作主视觉** | 宠主空态等用 `AppIcon` 或插图占位,不用 📋 等作为主视觉 | Customer Experience FE | 🟡 部分实现 | 代码审查 | 见 `Home.vue` 空态 slot |
---
## G. 文案与对外承诺(配置级)
| ID | 需求 | 验收标准 | Owner | 状态 | 验收命令 | 备注 |
|----|------|----------|-------|------|----------|------|
| **G1** | **无排队/叫号能力不承诺「不排队」** | 全局检索「排队」「不排队」;宠主副标题等改为「在线选时段,到店更省心」 | Product Design / Customer Experience FE | ✅ 已实现Batch A 文档冻结,容量模型上线后仍成立) | `rg "到店不排队|不排队" docs frontend/src` | 与《产品全方位优化说明》D2 一致 |
| **G2** | **对内录单表述(可选)** | 门店端新建预约入口文案是否改为「代客预约」由产品拍板;若改则全局统一 | Product Design | 🟡 待产品拍板 | — | 降低「预约」歧义 |
---
## H. 发布 RC 门禁Batch D 收口)
| ID | 需求 | 验收标准 | Owner | 状态 | 验收命令 | 备注 |
|----|------|----------|-------|------|----------|------|
| **H1** | **后端可重复发布门禁** | README + production profile + 配置校验 + schema/26 项只读预检 + liveness/readiness + 只读 smoke + systemd/Nginx 模板 | Backend Ops | ✅ 仓库能力已实现;待真实环境执行 | backend 全测/打包 + `bash -n deploy/*.sh` + Runbook | 不把模板/本地通过写成已上线 |
| **H2** | **前端可重复发布门禁** | admin/H5/小程序真实域名配置先门禁再构建;仓库占位值不能进入正式包;微信合法域名、体验版、静态回滚路径明确 | Frontend Release Ops | ✅ 仓库能力已实现;待真实环境执行 | admin/frontend preflight + 三端构建 + Runbook | 真实配置不得提交 |
| **H3** | **P0 通过条件** | 见下方「P0 通过条件」块 | System Architect / Core Flow QA | 🟡 待人工 smoke 复核 | 见下方 | 对应 P0-10 |
---
## 建议排期顺序(供研发参考)
1. **A1A3**(后端状态机)— 防脏数据,优先合。
2. **B1**(已完成 Tab— 前端小改、用户感知强。
3. **B2、D1、D2、E1**(传播与报告体验)— 主线门面。
4. **C1C5**(宠主)— 与登录/路由并行可排。
5. **E2、E3、F1、F2、G1** — 可并行或插空。
6. **A5A12**(后端边界、隐私、配置、上传、测试)— Batch B/C/D。
7. **H1H3**(发布 RC 门禁)— Batch D 收口。
---
## P0 通过条件(最终门禁)
- 后端 `mvn -f backend/pom.xml test` 有实际测试用例并通过(不再 `No tests to run`)。
- 前端 `npm --prefix frontend run build:h5``npm --prefix frontend run build:mp-weixin` 均通过。
- 公开报告页 token 匿名可打开,且不返回 top-level `userId/storeId/reportToken`
- boss/staff 登录后可生成报告、发送报告、查看回访池。
- customer 登录态打开报告仍显示留资和预约 CTA。
- 同一 `appointmentId` 第二次提交报告返回 `REPORT_ALREADY_EXISTS`
- 报告前后照片缺失返回对应业务码。
- A 店 boss/staff 不能查 B 店 leads/list/reportcustomer 不能用任意 `userId` 创建他人宠物/预约。
- 生产配置扫描不含真实 DB/微信密钥,最终 profile 为 `ddl-auto=validate`CORS 仅允许显式生产 HTTPS 源;只读生产预检通过。
- 已按顺序完成身份、StoreCustomer、BusinessEvent、预约容量四个生产迁移并归档备份与验证证据。
- liveness/readiness、只读 smoke 与 boss/staff/customer 三角色真实环境 smoke 通过Gitea/GitLab 未受 Petstore 配置影响。
- 文档口径扫描:`rg "到店不排队|会员储值 \| 路线图 P0|宠主通过微信查看服务报告v2|无限制,随便选" docs` 无命中。
---
## 不在 P0勿与本清单混排
- 会员卡 / 储值 / 次卡 / 套餐P1/P2
- 宠主账号内历史报告汇总P2
- 报告 token 作废重发(中期,见 D3 决策)
- 多技师分成、排班深度联动
- 复杂数据看板、漏斗大屏
- 分包优化(可 P1
---
*评审后可将本页 ID 勾进看板;与专文冲突以迭代评审为准。*