petstore-docs/产品设计文档.md

438 lines
20 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.

# 宠小它 - 产品设计文档
> **本体对齐**:本文档与 [`ontology/objects.md`](./ontology/objects.md) 对齐(`entity:store`/`entity:user`/`entity:appointment`/`entity:report`),规则见 [`ontology/rules.md`](./ontology/rules.md)。冲突以本体为准。
>
> 版本v1.9
> 日期2026-04-18v1.9:对外品牌统一为「宠小它」及主/副 Sloganv1.8:成片「可变时长 + 双编排」与 §5.3 对齐专文 §0
> **品牌**:宠小它 · **主 Slogan**:用心宠小它,温暖伴一生 · **副 Slogan**:智慧宠物门店服务系统
> **Logo / VI**[《宠小它-Logo与品牌识别说明》](./宠小它-Logo与品牌识别说明.md)(含概念草案图)
> 状态:产品设计确认中;**本期迭代重点:洗美过程短视频自动成片(竖屏、可变时长、双编排)**
**相关子文档:**
- [洗美报告短视频成片方案](./洗美报告短视频成片方案.md) — **本期迭代主方案**:过程短视频与自动成片(与 §5.3 服务报告延伸能力对应;时长与编排以专文「与工程实现同步」为准)
- [产品改进建议](./产品改进建议.md) — 中长期方向库;**§〇** 为本期重点说明
- [产品全方位优化说明 v1](./产品全方位优化说明-v1.md) — 定位/IA/交互/UI/商业化/小程序体验汇总与排期切片(**与专文互补**,冲突以评审更新为准)
- [**P0 研发落地清单**](./P0-研发落地清单.md) — **本期必做**后端状态机、Tab 筛选、发送报告话术、H5 隐私提示、宠主路径、埋点最小集等(**可直接拆 issue**
---
## 一、产品定位
**平台定位:** 宠物店服务管理 SaaS 平台,支持多家宠物店入驻
**目标用户v1**
- 宠物店老板 / 员工(管理预约 + 填写报告 + 发送报告 + 回访池)
- 宠主(在线预约、查看公开报告、留资回访、报告分享)
> **本期 P0 范围(冻结口径):**
> - 门店端:预约列表、开始服务、填写报告、发送报告、回访池。
> - 宠主端:预约填单、公开报告页、下次服务提醒留资、报告打开基础埋点。
> - 报告能力:服务前/服务后照片、服务过程素材、短片生成与下载、分享链接/二维码/话术。
>
> **不进入 P0**
> - 会员、储值、套餐、复杂 CRM。
> - 宠主账号内历史报告汇总。
> - 完整经营分析看板。
**登录方式:**
- 微信授权登录(老板/员工主要登录方式)
- 手机号+短信验证码(备用)
---
## 二、核心功能闭环v1
```
预约创建(宠主/老板/员工,下单即生效)→ 待开始(new) → 开始服务(剪毛/正式开做时点击,谁点谁技师)→ 进行中(doing) → 提交报告并完成(done) → 发送给宠主 → 显式确认发送
↘ 已取消(cancel) → 在「已完成」Tab 与完成单并列展示
```
**报告分享流程v1 重点):**
```
填写并提交报告(照片+备注)→ 选择传播方式 → 宠主实际收到 → 员工「确认已发送」
├── 复制链接/话术(发给宠主微信)
├── 当面展示二维码(现场扫码)
└── 其他可核实方式
```
---
## 三、数据模型
### t_store店铺表
| 字段 | 类型 | 说明 |
|------|------|------|
| id | BIGINT | 主键 |
| name | VARCHAR | 店铺名称 |
| logo | VARCHAR | 店铺Logo |
| phone | VARCHAR | 联系电话 |
| address | VARCHAR | 地址 |
| intro | TEXT | 简介 |
| owner_id | BIGINT | 老板用户ID |
| invite_code | VARCHAR | 员工邀请码8位 |
| booking_day_start | TIME | 每日首个容量桶开始时间 |
| booking_last_slot_start | TIME | 每日最后一个容量桶开始时间 |
| booking_capacity | INT | 门店并发接待数110 |
| create_time | DATETIME | 创建时间 |
| update_time | DATETIME | 更新时间 |
### t_user用户表
| 字段 | 类型 | 说明 |
|------|------|------|
| id | BIGINT | 主键 |
| username | VARCHAR | 用户名 |
| password | VARCHAR | 密码 |
| name | VARCHAR | 姓名/技师名 |
| phone | VARCHAR | 手机号 |
| avatar | VARCHAR | 头像 |
| store_id | BIGINT | 所属店铺 |
| role | VARCHAR | boss / staff / customer |
| create_time | DATETIME | 创建时间 |
| update_time | DATETIME | 更新时间 |
### t_store_customer门店客户主档
| 字段 | 类型 | 说明 |
|------|------|------|
| id | BIGINT | 本店客户稳定主键 |
| store_id | BIGINT | 门店 ID |
| customer_user_id | BIGINT | 全局 customer 账号,留资客户可空 |
| phone | VARCHAR(20) | 本店最后确认手机号API 默认脱敏 |
| display_name | VARCHAR(64) | 门店视角客户称呼 |
| source | VARCHAR(32) | customer_booking / assisted_booking / report_lead |
| first_contact_at / last_contact_at | DATETIME | 首次/最近接触时间 |
| create_time / update_time | DATETIME | 创建/更新时间 |
| deleted | TINYINT | 软删除标记 |
### t_business_event不可变业务事件
| 字段 | 类型 | 说明 |
|------|------|------|
| id / event_id | BIGINT / VARCHAR(36) | 内部主键 / 随机事件 ID |
| event_type / event_version | VARCHAR(64) / INT | 过去时事件类型 / schema 版本 |
| store_id / store_customer_id | BIGINT | 门店范围 / 可选客户关系 |
| aggregate_type / aggregate_id | VARCHAR(32) / BIGINT | 关联预约、报告或留资 |
| actor_user_id / actor_role | BIGINT / VARCHAR(16) | 操作人快照,匿名事件可空 |
| source | VARCHAR(32) | customer / admin / public_report / system / migration |
| occurred_at / create_time | DATETIME(3) | 业务发生 / 入库时间 |
| metadata_json | TEXT | 服务端白名单低敏维度,不存原始请求 |
| idempotency_key | VARCHAR(128) | 可选稳定幂等键 |
### t_appointment预约表
| 字段 | 类型 | 说明 |
|------|------|------|
| id | BIGINT | 主键 |
| pet_name | VARCHAR | 宠物名称 |
| pet_type | VARCHAR | 宠物类型(猫/狗/其他) |
| service_type | VARCHAR | 服务类型 |
| appointment_time | DATETIME | 预约开始时间(半小时粒度) |
| duration_minutes | INT | 下单时服务时长快照;修改服务项目不影响历史预约 |
| status | VARCHAR | new待开始/ doing服务中/ done已完成/ cancel已取消 |
| store_id | BIGINT | 店铺ID |
| customer_user_id | BIGINT | 被服务客户账号 |
| created_by_user_id | BIGINT | 实际创建预约的账号 |
| user_id | BIGINT | legacy 兼容,新数据镜像 customer_user_id |
| assigned_user_id | BIGINT | 服务技师ID开始服务时赋值API 别名 assignedStaffId |
| remark | TEXT | 备注 |
| create_time | DATETIME | 创建时间 |
| update_time | DATETIME | 更新时间 |
### t_report报告表
| 字段 | 类型 | 说明 |
|------|------|------|
| id | BIGINT | 主键 |
| appointment_id | BIGINT | 预约ID |
| remark | TEXT | 备注 |
| customer_user_id | BIGINT | 报告归属客户,从预约派生 |
| author_staff_id | BIGINT | 实际提交报告的门店账号 |
| user_id | BIGINT | legacy 兼容,新数据镜像 author_staff_id |
| store_id | BIGINT | 店铺ID |
| report_token | VARCHAR | 报告访问令牌UUID永久有效 |
| pet_name | VARCHAR | 宠物名(冗余) |
| service_type | VARCHAR | 服务类型(冗余) |
| appointment_time | DATETIME | 服务时间(冗余) |
| staff_name | VARCHAR | 服务技师名快照(冗余) |
| send_status | VARCHAR(16) | unknown历史不可确认/ unsent新报告待发送/ sent已显式确认 |
| sent_at | DATETIME | 首次显式确认发送时间 |
| sent_by_user_id | BIGINT | 首次确认发送员工 |
| send_channel | VARCHAR(16) | wechat / qr / other首次确认后不可覆盖 |
| create_time | DATETIME | 创建时间 |
| update_time | DATETIME | 更新时间 |
### t_report_image报告媒体表
| 字段 | 类型 | 说明 |
|------|------|------|
| id | BIGINT | 主键 |
| report_id | BIGINT | 关联报告ID |
| photo_url | VARCHAR(500) | 媒体URL |
| photo_type | VARCHAR(20) | before / after / during |
| sort_order | INT | 同类型内排序序号 |
| create_time | DATETIME | 创建时间 |
> 说明:当前后端已按 `photo_type` 存储前后图与过程素材;`media_type`photo/video为前端语义后端字段化会在后续迭代补齐。
### t_service_type服务类型表
| 字段 | 类型 | 说明 |
|------|------|------|
| id | BIGINT | 主键 |
| store_id | BIGINT | 店铺IDNULL表示系统默认 |
| name | VARCHAR | 服务名称 |
| duration_minutes | INT | 预计服务时长3048030 分钟粒度) |
| create_time | DATETIME | 创建时间 |
---
## 四、角色权限
| 功能 | 老板boss | 员工staff |
|------|-------------|---------------|
| 预约列表 | ✅ | ✅ |
| 新建预约 | ✅ | ✅ |
| 开始服务/填写报告 | ✅ | ✅ |
| 发送报告 | ✅ | ✅ |
| 员工管理 | ✅ | ❌ |
| 邀请码 | ✅ | ❌ |
| 店铺设置 | ✅ | ❌ |
| 服务类型管理 | ✅ | ❌ |
### 页面入口
- **index.html统一入口**:老板/员工同一套页面,通过「我的」页面区分功能
- 老板「我的」:员工管理 / 服务类型 / 店铺设置 / 我的订单
- 员工「我的」:我的订单
---
## 五、核心功能详解
### 5.1 登录体系
**微信授权登录**(主)
- 老板/员工均可使用微信授权登录
**手机号+短信验证码登录**(备用)
**员工入职流程:**
- 老板后台生成邀请码 → 员工点链接注册 → 自动关联店铺
- 老板后台直接新增员工 → 系统生成随机6位密码 → 员工用手机号+密码登录
### 5.2 预约管理
**创建预约:**
- 老板/员工均可手动创建
- 字段:宠物名字、宠物类型(猫/狗/其他)、服务类型、预约时间、备注
- 建档人 = 当前登录用户
- 号源以服务项目预计时长、门店营业容量时段、门店并发接待数、线上预约与手动占用共同计算;创建时检查服务覆盖的每个半小时桶,并串行化同店容量判定避免超卖
- 线上预约当前不预选技师,容量是门店对外承诺的并发接待数;个人技师日历在具备预约前分配技师能力后再引入
**服务类型:**
- 系统默认:洗澡、美容、洗澡+美容、剪指甲、驱虫
- 老板可在默认基础上新增/编辑/删除自定义服务类型,并配置 30480 分钟的预计时长
- 员工只能使用,不能管理
**预约状态语义(与界面文案对齐):**
- **new = 待开始**:下单即占号生效,**无**门店单独「确认接单」环节;到店、洗吹等若尚未到「开剪/正式开做」,仍属待开始。
- **doing = 服务中**:店员在**剪毛/正式开做**时点击「开始服务」进入本状态。
- **done = 已完成**:提交服务报告时,后端将关联预约置为已完成(见 ReportService
- **cancel = 已取消**取消后在前端「已完成」Tab 中与完成单并列展示(含历史取消)。
**开始服务:**
- 点击「开始服务」→ 状态变为「进行中」
- assigned_user_id = 当前登录用户(谁点谁服务)
#### 预约状态迁移矩阵(产品规则 · 供开发/测试验收)
| 当前状态 | 目标状态 | 触发 | 操作角色 | 备注 |
|----------|----------|------|----------|------|
| (创建) | new | 成功创建预约 | 宠主/老板/员工 | 下单即生效,占号 |
| new | doing | 点击「开始服务」 | boss / staff | 建议在剪毛/正式开做时点击 |
| new | cancel | 取消预约 | 宠主C 端)/ 门店侧 | 业务可约定开始前可取消 |
| doing | done | 提交服务报告(创建 Report | boss / staff | 后端 `ReportService.create` 内将预约置 done |
| doing | cancel | 取消(若允许) | 视业务 | **建议**:仅允许店长或限定条件,当前接口需防滥用 |
| done | — | 终态 | — | 不回退 |
| cancel | — | 终态 | — | 不回退 |
**期望约束(建议在接口层逐步收紧):** `new → doing` 仅当当前为 `new``doing → done` 由报告提交触发;取消仅允许自 `new` / `doing`(按产品最终规则)。当前部分接口未校验前置状态,以实际上线前补校验为准。
### 5.3 服务报告
**填写报告(进行中状态可操作):**
**报告提交必填项:**
- 宠物名称
- 服务类型
- 服务时间
- 服务前照片至少 1 张
- 服务后照片至少 1 张
服务过程中照片/视频、备注为选填。
- 技师 = 开始服务时分配的用户
- 提交后锁死,不可再修改
- 一约一份报告(同一 `appointmentId` 第二次提交返回 `REPORT_ALREADY_EXISTS`
- 报告必须绑定预约 `appointmentId`;无预约报告不作为本期主路径
> **本期迭代重点:** 洗美报告「过程短视频 + 自动生成 **竖屏成片9:16**」,**成片时长随素材量变化**(配置级总时长上限),支持 **预设顺序 / 穿插** 两种编排(`highlight_compose_mode`)。产品边界、参考分镜、成本与工程机制见子文档 [《洗美报告短视频成片方案》](./洗美报告短视频成片方案.md)。成片以服务端模板化拼接(如 FFmpeg为主成片可在报告页/H5 播放与分享。
>
> **实现对齐备注2026-04**:服务端已落库过程素材,报告查询接口对 `duringMedia` 的回传需与成片任务对齐;`highlight_video_*`、`highlight_duration_sec`、`highlight_compose_mode` 等字段承载成片 URL、时长、编排方式与任务状态本期需完成**发起 → 异步生成 → 成功播放 / 失败提示**闭环。
### 5.4 发送报告v1 核心功能)
**操作入口:** 报告提交成功后进入「发送给宠主」弹层;后台报告列表也可继续处理待发送报告。
**发送方式:**
1. **复制链接** — 生成报告链接,一键复制到剪贴板,可发给宠主微信
2. **当面扫码** — 展示报告二维码,宠主现场扫码打开
3. **其他方式** — 仅在员工能够确认宠主实际收到时使用
**确认口径:**
- 复制链接、复制话术、生成/展示二维码、预览或打开报告,**都不自动等于已发送**。
- 宠主实际收到后,由本店 boss/staff 选择真实渠道并点击「确认已发送」。
- 首次确认写入 `sent_at/sent_by_user_id/send_channel``report_sent` 事件;重复点击幂等成功,不覆盖首次回执。
- 历史报告没有可靠发送证据,迁移后统一为 `unknown`;只有迁移后新报告默认 `unsent` 并进入工作台待办。
**报告链接:**
- URL`https://域名/report.html?token=xxx`
- Token = UUID永久有效
- 宠主打开无需登录v1 免登录)
**报告二维码:**
- 扫码直接打开报告页
- 打印出来可贴在墙上/前台,方便宠主扫码
### 5.5 H5 报告页
**访问方式:** 链接或二维码扫码,免登录
**报告页内容:**
- 品牌头部:店铺 Logo + 店铺名称 + 联系电话 + 地址
- 服务信息卡片:宠物名、服务项目、服务时间、技师名
- 前后对比照片(并排展示)
- 服务过程中素材区(照片/短视频)
- 技师备注
- 底部品牌信息(即使链接被转发,店铺信息持续透出)
**分享能力:**
- 长按图片可保存
- 链接可复制转发微信
### 5.6 老板后台(合并到「我的」页面)
**员工管理:**
- 员工邀请码(复制分享)
- 新增员工(姓名+手机号,系统生成密码)
- 删除员工
**服务类型:**
- 新增服务类型
- 删除自定义服务类型(系统默认不可删)
**店铺设置:**
- 店铺名称
- 店铺Logo
- 联系电话
- 地址
- 简介
---
## 六、技术方案
| 端 | 技术 |
|----|------|
| APP前端 | uniapp + Vue3 |
| H5报告页 | Vue3独立部署 |
| 后端 | Spring Boot 3.2 + JPA |
| 数据库 | MySQL |
| 图片存储 | 本地存储后续可平滑切换OSS |
---
## 七、开发进度
| 优先级 | 功能 | 状态 |
|--------|------|------|
| P0 | 老板入驻/登录 | ✅ |
| P0 | 员工邀请码注册 | ✅ |
| P0 | 员工登录(微信+验证码) | ✅ |
| P0 | 预约列表 | ✅ |
| P0 | 预约创建 | ✅ |
| P0 | 服务时长 + 门店并发预约容量 + 连续占用 | ✅ |
| P0 | 开始服务(分配技师) | ✅ |
| P0 | 报告填写 | ✅ |
| P0 | 报告过程素材上传(照片/短视频) | ✅ 基础能力已接入 |
| P0 | 发送报告(复制链接+下载二维码) | 🔨 本次 |
| P0 | H5报告页品牌信息强化 | 🔨 本次 |
| P0 | H5报告页链接+二维码) | ✅ |
| P1 | 报告过程素材回传duringMedia | 🔨 进行中 |
| **P0** | **洗美过程短视频自动成片(可变时长、双编排、模板拼接)** | **🔨 本期迭代重点** |
| P0 | 老板后台(员工管理+邀请码) | ✅ 已合并到index.html |
| P0 | 老板后台(预约概览) | ✅ 已合并到index.html |
| P1 | 服务类型管理(老板后台) | ✅ |
| P1 | 店铺设置(老板后台) | ✅ |
| P1 | 本地文件上传替代base64 | ✅ |
| P2 | 宠主端(历史报告汇总) | ⏳ P2不进入本期 P0 |
---
## 八、数据库变更
```sql
-- 预约表新增技师字段
ALTER TABLE t_appointment ADD COLUMN assigned_user_id BIGINT COMMENT '技师ID';
-- 店铺表新增简介字段
ALTER TABLE t_store ADD COLUMN intro TEXT COMMENT '简介';
-- 服务类型表
CREATE TABLE t_service_type (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
store_id BIGINT COMMENT '店铺IDNULL表示系统默认',
name VARCHAR(50) NOT NULL,
duration_minutes INT NOT NULL DEFAULT 60,
create_time DATETIME
);
-- 系统默认服务类型
INSERT INTO t_service_type (store_id, name, duration_minutes, create_time) VALUES
(NULL, '洗澡', 60, NOW()),
(NULL, '美容', 120, NOW()),
(NULL, '洗澡+美容', 150, NOW()),
(NULL, '剪指甲', 30, NOW()),
(NULL, '驱虫', 30, NOW());
-- 真实预约容量字段(完整回填与验证见 backend/db/migrations/20260801_create_booking_capacity.sql
ALTER TABLE t_appointment ADD COLUMN duration_minutes INT NOT NULL DEFAULT 60;
ALTER TABLE t_schedule_block ADD COLUMN duration_minutes INT NOT NULL DEFAULT 30;
ALTER TABLE t_store ADD COLUMN booking_capacity INT NOT NULL DEFAULT 1;
-- 报告显式确认发送字段(完整历史兼容与验证见 backend/db/migrations/20260801_create_report_send_status.sql
ALTER TABLE t_report ADD COLUMN send_status VARCHAR(16) NOT NULL DEFAULT 'unsent';
ALTER TABLE t_report ADD COLUMN sent_at DATETIME NULL;
ALTER TABLE t_report ADD COLUMN sent_by_user_id BIGINT NULL;
ALTER TABLE t_report ADD COLUMN send_channel VARCHAR(16) NULL;
-- 报告媒体表(前后图 + 过程素材)
CREATE TABLE t_report_image (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
report_id BIGINT NOT NULL COMMENT '关联报告ID',
photo_url VARCHAR(500) NOT NULL COMMENT '媒体URL',
photo_type VARCHAR(20) NOT NULL COMMENT 'before/after/during',
sort_order INT DEFAULT 0 COMMENT '同类型排序',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
INDEX idx_report_image_report_id (report_id),
INDEX idx_report_image_report_type_sort (report_id, photo_type, sort_order)
);
```
---
## 九、后续事项(开放清单)
- [ ] 宠小它 Logo 素材
- [ ] 图片存储方案(本地 / OSS
- [ ] 短信验证码服务商接入
- [ ] 微信授权登录 AppID/AppSecret
- [ ] H5页面域名及部署方案
- [ ] 宠主账号内历史报告汇总P2不进入本期 P0
- [ ] 会员、储值、套餐、复杂 CRMP1/P2不进入本期 P0