petstore-docs/架构与产品评估报告-2026-07-05.md

220 lines
19 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/) 的规则条目对齐(如 P0-1→`rule:BR-AUTH-001`、P0-2→`rule:BR-RPT-006`/`rule:BR-LEAD-002`、P0-3→`rule:BR-RPT-002`、P0-4→`rule:BR-RPT-003`)。冲突以本体为准。
日期2026-07-05
评估范围:
- 文档:`docs/产品设计文档.md`、`docs/P0-研发落地清单.md`、`docs/本期主线-下一步产品优化点.md`、报告成片、宠主预约、公众号协同、品牌与 UI 规范等。
- 后端:`backend/src/main/java/com/petstore` 下的预约、报告、成片、留资、宠物、用户、文件上传、配置与启动逻辑。
- 前端:`frontend/src` 下的首页、预约创建、报告填写、公开报告页、报告分享弹层、留资卡片、线索页、我的页、API 与工具函数。
方法:
- 架构师 agent 从边界、状态机、API 契约、数据模型、隐私安全、发布运维、测试可观测性做只读评估。
- 产品 agent 从定位、角色、主链路、报告分享、宠主预约、留资转化、文案品牌、验收标准做只读评估。
- 主线程补充核对关键代码路径,并执行基础验证命令。
## 总结
当前系统已经有一条可运行的主线:门店预约、开始服务、报告填写、报告发送、公开报告页、成片生成、宠主留资、回访池这些关键环节都已出现实现。真正阻碍进入稳定验收的,不是主流程缺失,而是四类风险仍未收口:
1. 身份与数据边界仍主要依赖前端传参,后端缺统一鉴权上下文。
2. 产品文档与代码状态不同步,部分 P0 已实现但文档仍写待改,部分文档承诺代码还未闭环。
3. 报告、线索、公开 token、上传与配置存在隐私和发布风险。
4. 自动化测试、迁移脚本、发布门禁和埋点统计基线不足,导致回归和上线依赖人工经验。
建议先按 P0 修“边界、隐私、报告不变量、产品口径、验证基线”,再做 P1 的增长与可观测性增强。
## 健康度评估
| 维度 | 结论 | 主要依据 |
| --- | --- | --- |
| 产品定位 | 基本健康但文档滞后 | B 端门店 SaaS + C 端报告/预约/留资闭环已形成;但 `产品设计文档.md` 仍把宠主查看报告写作 v2与 P0 清单和现有代码冲突。 |
| 主流程 | 中等偏好 | 预约、状态 Tab、开始服务、报告提交、发送弹层、报告页、短片三态都有实现。 |
| 状态机 | 较好但缺测试 | `AppointmentService` 已集中处理 `new -> doing -> done`、取消规则和非法迁移,但文档还保留旧口径,自动化测试缺失。 |
| API 边界 | 中等偏弱 | 前端 API 已集中,但后端多处仍从请求参数读取 `userId/storeId/role` 控制范围。 |
| 数据模型 | 中等偏弱 | `Report`、`ReportLead` 字段逐步补齐,但“一约一报告”、报告 token 生命周期、成片任务队列和迁移脚本仍不完整。 |
| 隐私安全 | 弱 | 公开报告回包和线索接口暴露面偏大登录、CORS、配置密钥、上传路径需要生产化。 |
| 发布运维 | 弱 | FFmpeg、上传大小、H5 public origin、微信合法域名、生产 profile、迁移步骤缺一份 RC 门禁清单。 |
| 测试与可观测 | 弱 | 后端 `mvn test` 可执行但无测试用例;前端缺依赖和可执行测试;报告打开埋点仍偏日志化。 |
## 当前可以保留的基础
1. 前端角色分流和主流程页面已经成型,`Home.vue`、`Report.vue`、`ReportShareModal.vue`、`reportView.vue` 能串起核心体验。
2. 预约状态机已经从“直接赋值”收口到服务层校验,文档应更新为“待补测试和验收证据”,不是“待重写”。
3. 报告成片字段契约和前后端字段名基本一致,`highlightStatus`、`highlightVideoUrl`、`highlightFailReason` 等字段具备继续扩展的基础。
4. 留资链路已有 token、手机号、同报告同手机号去重、同意状态等基础不需要推倒重做。
5. P0 清单本身结构可用,适合继续作为验收总入口;问题是状态需要重新冻结。
## P0 必补项
| 编号 | 问题 | Owner | 涉及路径 | 建议 | 验收口径 |
| --- | --- | --- | --- | --- | --- |
| P0-1 | 后端身份与店铺边界未收口 | System Architect、Backend Core、Report Media Backend | `AppointmentController`、`ReportController`、`ReportLeadController`、`PetController`、`StoreController`、`UserController` | 建立统一登录态和角色上下文;所有 boss/staff/customer 接口从上下文派生 `userId/storeId/role`,不再信任请求参数;公开报告 token 接口单独白名单。 | A 店用户访问 B 店预约、报告列表、线索接口返回 403宠主不能用任意 `userId` 创建/查询他人宠物和预约。 |
| P0-2 | 公开报告与线索隐私暴露面偏大 | System Architect、Product Design、Report Media Backend、Customer Experience FE | `ReportController.get`、`ReportLeadController.leads`、`Leads.vue`、`reportView.vue` | 公开报告回包只返回页面必要字段;线索列表默认不返回完整 openid/unionid手机号按角色决定展示/脱敏;日志只记录不可逆 token hash。 | `GET /api/report/get?token=` 不含内部 `userId/storeId/reportToken`;员工态不显示完整 openid/unionid日志不可反推出 token。 |
| P0-3 | 报告“一约一份”和提交不变量缺数据库与服务层兜底 | System Architect、Report Media Backend、Data Model | `ReportService.create`、`Report.java`、`ReportController.create`、`Report.vue` | 增加 `appointment_id` 唯一约束;服务层拒绝同预约重复提交;明确是否允许无预约报告,若不允许则前后端同时拦截。 | 同一 `appointmentId` 第二次提交返回 409 或业务码;非 `doing` 预约不能创建报告;无预约提交符合产品确认后的唯一规则。 |
| P0-4 | 报告前后照片必填口径未落实 | Product Design、Store Miniapp FE、Report Media Backend | `产品设计文档.md`、`Report.vue`、报告创建接口 | 文档、前端、后端统一:服务前、服务后至少各 1 张,过程图可选。 | 0 张 before 或 0 张 after 时提交失败,提示“请至少上传 1 张服务前/服务后照片”。 |
| P0-5 | 产品文档与代码状态冲突 | Product Design、PM Assistant、Docs | `产品设计文档.md`、`P0-研发落地清单.md`、`宠主端预约体验优化-产品说明.md`、`产品全方位优化说明-v1.md` | 冻结本期范围:宠主预约/公开报告/留资/基础埋点属于本期;历史报告汇总、复杂会员储值、完整 CRM 放到 P1/P2删除“到店不排队”等无容量模型承诺。 | `rg "到店不排队|会员储值 \\| 路线图 P0|宠主通过微信查看服务报告v2" docs` 不再命中冲突口径。 |
| P0-6 | 宠主预约登录与草稿策略未闭环 | Product Design、Customer Experience FE、Backend Core | `P0-研发落地清单.md`、`CustAppointmentCreate.vue`、`frontend/README.md` | 二选一冻结策略:进入预约先登录,或提交时登录并恢复 guest 草稿。若选择提交时登录,草稿 key 不能依赖 `userInfo.id`。 | 未登录填写后提交不会产生 `userId: undefined`;登录返回后门店、宠物、服务、时段、备注恢复。 |
| P0-7 | 报告页角色判断会把已登录 customer 当员工态 | Product Design、Customer Experience FE | `reportView.vue`、`auth/session` 工具 | 把 `isStaff` 从“是否登录”改为“角色是 boss/staff”customer 登录态仍看到留资/预约 CTA。 | customer 登录态打开报告页显示 `ReminderCard` 和“我也要预约”boss/staff 才显示内部操作。 |
| P0-8 | 配置、密钥与生产 profile 风险 | System Architect、Backend Ops、Frontend Release Ops | `backend/src/main/resources/application.yml`、`application-example.yml`、`.env.*` | 移除真实环境配置和明文敏感项,改为环境变量;已暴露凭据按安全流程轮换;生产 profile 禁用 `ddl-auto:update`、`show-sql`、万能验证码和宽 CORS。 | 仓库不含真实 DB/微信密钥;生产启动只读环境变量;安全扫描不再命中敏感配置。 |
| P0-9 | 上传接口缺路径与类型保护 | System Architect、Backend Core | `FileController`、上传反代配置 | 对上传文件做扩展名、MIME、大小、路径归一化校验下载/访问路径拒绝 `..`、绝对路径和非媒体类型。 | 构造 `/api/upload/image/../...`、伪装扩展名、超大文件均被拒绝;正常图片/视频仍可上传访问。 |
| P0-10 | 验证基线不足 | Core Flow QA、Report Share QA、System Architect | `backend/src/test`、前端 smoke、CI | 补最小自动化:预约状态机、报告创建、留资去重、公开报告字段、跨店访问拒绝、前端报告打开/分享/预约深链 smoke。 | `mvn test` 至少跑上述服务/控制器测试;前端构建和 smoke 能在本地或 CI 复现。 |
## P1 补强项
| 编号 | 问题 | Owner | 建议 | 验收口径 |
| --- | --- | --- | --- | --- |
| P1-1 | `storeId` 全入口不完整 | Customer Experience FE、Product Design | 支持普通 query、小程序码 `scene`、报告页“我也要预约”、公众号菜单、分享 path/query写清参数协议。 | 三种入口均可预填门店,并允许用户更换门店。 |
| P1-2 | 留资主档规则待定 | Product Design、Report Media Backend | 明确手机号、openid、unionid 的主键、合并、冲突、解绑、展示和权限策略。 | 同手机号重复提交合并不同微信同手机号有明确处理Leads 页面展示符合权限。 |
| P1-3 | 成片任务缺成本与队列治理 | Report Media Backend、Backend Ops | 增加重试次数、频控、任务表或可观测队列、失败分类聚合、FFmpeg 健康检查。 | 同一报告短时间重复生成被限制失败原因可统计RC 可检测 FFmpeg/ffprobe。 |
| P1-4 | 埋点从日志升级为可统计事件 | Report Share QA、Progress Digest、Backend Ops | 定义 `report_open`、`report_reopen`、`highlight_success`、`highlight_failed`、`video_play`、`video_save`、`lead_submit`。 | 老板端或日报能看到打开率、成片成功率、留资转化。 |
| P1-5 | 启动期 DDL/修复逻辑不可审计 | System Architect、Data Model、Backend Ops | 把 `PetstoreApplication` 中的临时 DDL/历史 UPDATE 移入迁移脚本,建立版本化迁移。 | 新环境和老环境都通过迁移脚本升级;应用启动不再改表结构。 |
| P1-6 | 共享空态和品牌细节不统一 | Product Design、Customer Experience FE、Store Miniapp FE | `AppPageState` 默认改用 `AppIcon` 或显式 icon prop清理主视觉 emoji统一品牌词和按钮文案。 | 主流程空态不出现 emoji 主视觉品牌名、Slogan、主色使用规范一致。 |
| P1-7 | 公众号配置不是代码能证明的 P0 | PM Assistant、Frontend Release Ops、Product Design | 将公众号菜单、欢迎语、自动回复列为运营配置验收项,和代码页面存在性分开。 | 公众号后台截图/配置单可证明菜单可跳小程序,自动回复路径有效。 |
## P2 方向库
| 编号 | 方向 | 说明 |
| --- | --- | --- |
| P2-1 | 报告 token 作废、重发、有效期 | 支持老板重新生成分享链接、旧链接失效、异常频次提醒。 |
| P2-2 | 宠主历史报告汇总 | 在本期公开报告页稳定后,再做宠主账号内历史记录。 |
| P2-3 | 会员、储值、套餐 | 不进入当前 P0应等待预约/报告/留资闭环和数据口径稳定后再设计。 |
| P2-4 | 公众号模板消息和小程序订阅去重 | 避免报告通知、成片通知、小程序订阅消息重复触达。 |
| P2-5 | 经营分析看板 | 基于 P1 埋点和订单数据,做报告打开、预约转化、回访转化、服务复购。 |
## 文档修订清单
| 文档 | 当前问题 | 建议改法 |
| --- | --- | --- |
| `产品设计文档.md` | 写“宠主查看服务报告 v2”预约时间写“无限制随便选”取消单展示和状态矩阵与代码/P0 不一致;数据模型缺新字段。 | 改为本期包含宠主预约、公开报告、留资;更新预约时间为可预约窗口和占用校验;状态矩阵对齐代码;补 `ReportLead`、成片字段、预约窗口等模型。 |
| `P0-研发落地清单.md` | 部分“当前需改”已过时,如状态机、报告分享弹层、我的页分组;部分缺口还未标清 owner。 | 重新标记 `已实现 / 部分实现 / 未实现 / 待测试`,并把每项绑定 owner 和验收命令。 |
| `宠主端预约体验优化-产品说明.md` | 仍有“到店不排队”示例;旧入口描述和当前主 CTA 不一致;登录策略未冻结。 | 删除容量承诺,统一为“在线选时段,到店更省心”;写清登录策略和深链协议。 |
| `产品全方位优化说明-v1.md` | 会员储值被写进 P0 路线图,与 common context 和 P0 清单冲突。 | 降级到 P1/P2 方向库,当前只保留预约/报告/留资闭环。 |
| `本期主线-下一步产品优化点.md` | 指标建议好,但缺事件 schema 和数据来源。 | 增加埋点事件、字段、统计窗口、owner、看板或日报落地方式。 |
| `洗美报告短视频成片方案.md` | 成片方案大体吻合代码,但重试频控、队列状态、成本指标没有落到验收。 | 增加成片任务状态机、失败重试次数、频控和 RC 检查。 |
| `公众号协同与全链路触达-产品说明.md` | P0 中包含运营后台配置,代码无法自动证明。 | 拆成“代码可验收”和“运营配置可验收”两栏。 |
| `backend/README.md`、`frontend/README.md` | 发布依赖、环境变量、微信合法域名、FFmpeg、上传大小、H5 origin 不完整。 | 增加本地开发、RC、生产上线三段式运行手册。 |
## 代码修订清单
### 后端
1. 建立统一鉴权中间层或拦截器,生成 `CurrentUserContext`
- `userId`
- `role`
- `storeId`
- `isCustomer`
- `isStaff`
- `isBoss`
2. 控制器改造原则:
- 管理接口不接受客户端传入的身份字段作为权限依据。
- 查询列表时由上下文派生作用域。
- 公开报告接口只允许 token 和必要参数。
- 线索、宠物、报告、预约接口都做跨店/跨用户校验。
3. 数据约束:
- `report.appointment_id` 加唯一约束。
- `report_lead(report_id, phone)` 已有方向,但需迁移脚本确认。
- 报告 token 增加状态、作废时间或过期策略的预留字段。
- 成片任务若继续异步化,建议从 report 字段扩展到任务表或任务日志。
4. 配置与发布:
- 移除仓库内真实环境敏感配置,使用环境变量和 profile。
- 关闭生产 `ddl-auto:update` 和 SQL 明文输出。
- 收敛 CORS 白名单。
- 上传路径做归一化和类型校验。
### 前端
1. `reportView.vue`
- `isStaff` 使用角色判断,不使用 `isLoggedIn()`
- customer 登录态仍显示留资/预约入口。
- 公开报告页不要依赖后端回包里的内部 token 字段。
2. `CustAppointmentCreate.vue`
- 支持 `scene` 解码。
- 冻结登录策略并实现 guest 草稿或进入页登录。
- 提交前确保 `userInfo.id` 一定存在或后端可识别匿名转登录上下文。
3. `Report.vue`
- 前后照片必填。
- 与后端一致处理重复报告、非 `doing` 预约和无预约报告。
4. `Leads.vue`
- 默认隐藏或脱敏 openid/unionid。
- 手机号展示按角色和操作场景收敛。
5. `ReportShareModal.vue`、`reportPublicUrl.js`
-`VITE_REPORT_PUBLIC_ORIGIN` 写入发布手册和 RC 检查。
- 链接、二维码、话术、封面规则做 smoke。
6. 共享 UI
- `AppPageState` 用品牌 icon 作为默认主视觉。
- 清理主流程空态中的 emoji 主视觉。
## 建议实施顺序
1. 第 1 批:修文档口径、报告页角色判断、照片必填、`storeId` 深链协议、公开报告字段裁剪。
2. 第 2 批:统一鉴权上下文、跨店/跨用户校验、线索脱敏、报告唯一约束。
3. 第 3 批迁移脚本、生产配置、上传安全、FFmpeg/上传/微信域名 RC 门禁。
4. 第 4 批:后端最小测试、前端构建与 smoke、埋点事件入库或可统计日志。
5. 第 5 批token 作废、历史报告、会员储值、经营看板等 P2 方向。
## 最小验收集
### 后端
| 场景 | 期望 |
| --- | --- |
| `new -> doing` | 成功 |
| `doing -> done` | 成功 |
| `done -> doing` | 失败并返回明确业务码 |
| `done -> cancel` | 失败并返回明确业务码 |
| 重复 `startService` | 第二次失败 |
| 非本店用户查预约/线索/报告列表 | 403 |
| 公开 token 查报告 | 只返回公开页必要字段 |
| 同一预约重复创建报告 | 第二次失败 |
| 报告前图或后图为空 | 创建失败 |
| 同报告同手机号重复留资 | 幂等更新,不产生重复记录 |
| 上传路径带 `..` | 拒绝 |
### 前端
| 场景 | 期望 |
| --- | --- |
| customer 首页 | 只有一个主预约入口,空态不出现第二个主按钮 |
| 预约页普通 `storeId` query | 预填门店 |
| 预约页小程序码 `scene` | 预填门店 |
| 未登录填写预约 | 按冻结策略登录并恢复草稿,或进入前即登录 |
| customer 登录态打开报告页 | 仍显示留资/预约 CTA |
| boss/staff 打开报告页 | 显示内部分享/保存操作 |
| 报告提交无前图或后图 | 阻止提交并提示 |
| 报告提交成功 | 链接、二维码、两条话术同屏 |
| 成片处理中/失败/完成 | 三态文案和按钮正确 |
| 线索页 | openid/unionid 不默认明文展示 |
### 发布 RC
| 检查 | 期望 |
| --- | --- |
| `APP_BASE_URL` | 后端可生成可访问媒体 URL |
| `VITE_REPORT_PUBLIC_ORIGIN` | 报告链接和二维码非空且可访问 |
| `ffmpeg -version`、`ffprobe -version` | 服务端可执行 |
| 上传 50MB 视频 | 不被 Nginx 或后端误拦截 |
| 微信 request/download 合法域名 | 小程序可请求 API 并下载短片 |
| 生产配置扫描 | 无真实 DB/微信密钥、无 `ddl-auto:update`、无宽 CORS |
## 本次验证结果
| 命令 | 结果 | 说明 |
| --- | --- | --- |
| `mvn test``backend` | 成功 | Maven 构建通过,但输出 `No tests to run`,不能证明业务逻辑有自动化覆盖。 |
| `npm --prefix frontend run build:h5` | 失败 | `frontend/node_modules` 不存在,`uni: command not found`;这是依赖/工具链缺失,不是源码编译通过或失败的业务结论。 |
| `git -C docs diff --check` | 成功 | 文档变更无空白检查错误。 |
## 结论
这套文档和代码的方向是对的:先做门店服务闭环,再把报告页作为宠主触达与复购入口。但现在不适合继续叠会员、储值或更复杂增长功能。下一轮应把 P0 的身份边界、报告不变量、隐私脱敏、文档口径和测试基线补齐;补齐后再推进埋点、公众号、经营看板和会员能力,风险会低很多。