# 宠小它 — P0 研发落地清单 > **本体对齐**:任务条目引用 [`ontology/`](./ontology/) 中的实体/规则 ID(如 `entity:report`、`rule:BR-RPT-002`)。冲突以本体为准。 > > 用途:发给研发拆任务与验收;**仅含 P0**(本期质量与主线闭环必需,不含 P1/P2 增强)。 > 依据:《产品全方位优化说明 v1》《本期主线》《宠主端预约体验优化》《产品设计文档》及当前仓库实现粗对齐。 > 更新:2026-07-05(P0 稳定化轮次:冻结本期范围,重新标记状态并绑定 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 不再信任请求里的身份字段 | 未登录返回 401;A 店用户访问 B 店预约/报告/线索返回 403;customer 不能用任意 `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`;移除启动期 DDL/历史 UPDATE | 仓库不含真实 DB/微信密钥;生产启动只读环境变量 | Backend Ops / System Architect | ✅ 已实现(Batch D) | `rg` 扫描 + 启动验证 | 对应 P0-8;已暴露凭据需轮换 | | **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` | --- ## 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 | 对齐《本期主线》A | | **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 | --- ## 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 登录态打开公开报告页仍显示留资/预约 CTA;boss/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** | **弱网 / 失败重试** | 报告主接口失败:按钮「重新加载」;超时阈值 8~10s 后出失败态 | 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/hash;H5/小程序均上报 | 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** | **backend README RC 清单** | 含 `ffmpeg -version`、`ffprobe -version`、`curl -I $APP_BASE_URL/api/upload/image/`、`mvn test`;写明 `APP_BASE_URL`、`HIGHLIGHT_*`、`PETSTORE_SESSION_SECRET`、上传目录权限、Nginx `client_max_body_size` | Backend Ops | ✅ 已实现(Batch D) | 代码审查 | 对应 P0-10 | | **H2** | **frontend README RC 清单** | 含 `npm install`、`build:h5`、`build:mp-weixin`;写明 `VITE_API_ORIGIN`、`VITE_REPORT_PUBLIC_ORIGIN`、微信 request/download 合法域名、报告二维码链接检查 | Frontend Release Ops | ✅ 已实现(Batch D) | 代码审查 | 对应 P0-10 | | **H3** | **P0 通过条件** | 见下方「P0 通过条件」块 | System Architect / Core Flow QA | 🟡 待人工 smoke 复核 | 见下方 | 对应 P0-10 | --- ## 建议排期顺序(供研发参考) 1. **A1~A3**(后端状态机)— 防脏数据,优先合。 2. **B1**(已完成 Tab)— 前端小改、用户感知强。 3. **B2、D1、D2、E1**(传播与报告体验)— 主线门面。 4. **C1~C5**(宠主)— 与登录/路由并行可排。 5. **E2、E3、F1、F2、G1** — 可并行或插空。 6. **A5~A12**(后端边界、隐私、配置、上传、测试)— Batch B/C/D。 7. **H1~H3**(发布 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/report;customer 不能用任意 `userId` 创建他人宠物/预约。 - 生产配置扫描不含真实 DB/微信密钥,无 `ddl-auto:update`,无宽 CORS。 - 文档口径扫描:`rg "到店不排队|会员储值 \| 路线图 P0|宠主通过微信查看服务报告(v2)|无限制,随便选" docs` 无命中。 --- ## 不在 P0(勿与本清单混排) - 会员卡 / 储值 / 次卡 / 套餐(P1/P2) - 宠主账号内历史报告汇总(P2) - 报告 token 作废重发(中期,见 D3 决策) - 多技师分成、排班深度联动 - 复杂数据看板、漏斗大屏 - 分包优化(可 P1) --- *评审后可将本页 ID 勾进看板;与专文冲突以迭代评审为准。*