220 lines
19 KiB
Markdown
220 lines
19 KiB
Markdown
# 架构与产品评估报告
|
||
|
||
> **本体对齐**:本报告的 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 的身份边界、报告不变量、隐私脱敏、文档口径和测试基线补齐;补齐后再推进埋点、公众号、经营看板和会员能力,风险会低很多。
|