22 KiB
宠小它 - 产品设计文档
本体对齐:本文档与
ontology/objects.md对齐(entity:store/entity:user/entity:appointment/entity:report),规则见ontology/rules.md。冲突以本体为准。版本:v2.0 日期:2026-08-02(v2.0:面向 3~5 家独立单店真实试点,补齐门店开通、一次性员工邀请与审计边界)
品牌:宠小它 · 主 Slogan:用心宠小它,温暖伴一生 · 副 Slogan:智慧宠物门店服务系统
Logo / VI:《宠小它-Logo与品牌识别说明》(含概念草案图) 状态:产品设计确认中;本期迭代重点:洗美过程短视频自动成片(竖屏、可变时长、双编排)
相关子文档:
- 洗美报告短视频成片方案 — 本期迭代主方案:过程短视频与自动成片(与 §5.3 服务报告延伸能力对应;时长与编排以专文「与工程实现同步」为准)
- 产品改进建议 — 中长期方向库;§〇 为本期重点说明
- 产品全方位优化说明 v1 — 定位/IA/交互/UI/商业化/小程序体验汇总与排期切片(与专文互补,冲突以评审更新为准)
- P0 研发落地清单 — 本期必做:后端状态机、Tab 筛选、发送报告话术、H5 隐私提示、宠主路径、埋点最小集等(可直接拆 issue)
一、产品定位
平台定位: 面向独立宠物洗护单店的服务管理 SaaS;平台可承载多家独立试点门店,但本期不做连锁多门店组织。
目标用户(v1):
- 宠物店老板 / 员工(管理预约 + 填写报告 + 发送报告 + 回访池)
- 宠主(在线预约、查看公开报告、留资回访、报告分享)
本期 P0 范围(冻结口径):
- 门店端:预约列表、开始服务、填写报告、发送报告、回访池。
- 宠主端:预约填单、公开报告页、下次服务提醒留资、报告打开基础埋点。
- 报告能力:服务前/服务后照片、服务过程素材、短片生成与下载、分享链接/二维码/话术。
不进入 P0:
- 会员、储值、套餐、复杂 CRM。
- 宠主账号内历史报告汇总。
- 完整经营分析看板。
登录方式:
- 生产:微信手机号授权登录;老板入驻和员工接受邀请均必须经微信核验手机号。
- 开发:手机号+演示短信验证码;production profile 默认关闭,不作为生产备用通道。
门店开通流程:
老板微信核验手机号创建门店
→ 补全门店名称/电话/地址
→ 确认预约首末时段与并发容量
→ 确认至少一个服务项目
→ 显式完成开通(邀请员工为选填,支持单人门店)
二、核心功能闭环(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 | legacy 兼容字段;不返回客户端、不再作为注册凭证 |
| booking_day_start | TIME | 每日首个容量桶开始时间 |
| booking_last_slot_start | TIME | 每日最后一个容量桶开始时间 |
| booking_capacity | INT | 门店并发接待数(1~10) |
| onboarding_status | VARCHAR | unknown / in_progress / completed |
| onboarding_completed_at | DATETIME | 老板显式完成开通时间 |
| onboarding_completed_by_user_id | BIGINT | 完成开通的老板用户 |
| create_time | DATETIME | 创建时间 |
| update_time | DATETIME | 更新时间 |
t_user(用户表)
| 字段 | 类型 | 说明 |
|---|---|---|
| id | BIGINT | 主键 |
| username | VARCHAR | 用户名 |
| password | VARCHAR | legacy 服务端兼容字段;生产微信注册只写不可登录哨兵值,不接收或下发密码 |
| name | VARCHAR | 姓名/技师名 |
| phone | VARCHAR | 手机号 |
| avatar | VARCHAR | 头像 |
| store_id | BIGINT | 所属店铺 |
| role | VARCHAR | boss / staff / customer |
| create_time | DATETIME | 创建时间 |
| update_time | DATETIME | 更新时间 |
t_staff_invitation(员工邀请)
| 字段 | 类型 | 说明 |
|---|---|---|
| invitation_id | VARCHAR(36) | 对外随机 ID |
| token_hash | VARCHAR(64) | 原始 256-bit token 的 SHA-256;原文不落库 |
| store_id | BIGINT | 门店 |
| invited_name / invited_phone | VARCHAR | 受邀姓名 / 服务端核验手机号 |
| status | VARCHAR | pending / accepted / revoked / expired |
| expires_at | DATETIME | 1~30 天有效期 |
| accepted_by_user_id / accepted_at | BIGINT / DATETIME | 接受回执 |
| revoked_by_user_id / revoked_at | BIGINT / DATETIME | 撤销回执 |
t_audit_log(不可变操作审计)
记录门店入驻/开通/设置与员工邀请/删除等操作。metadata 仅允许服务端白名单低敏标量,禁止手机号、token、微信标识、密码/密钥、IP、地址坐标和原始请求体。
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 | 店铺ID(NULL表示系统默认) |
| name | VARCHAR | 服务名称 |
| duration_minutes | INT | 预计服务时长(30~480,30 分钟粒度) |
| create_time | DATETIME | 创建时间 |
四、角色权限
| 功能 | 老板(boss) | 员工(staff) |
|---|---|---|
| 预约列表 | ✅ | ✅ |
| 新建预约 | ✅ | ✅ |
| 开始服务/填写报告 | ✅ | ✅ |
| 发送报告 | ✅ | ✅ |
| 员工管理 | ✅ | ❌ |
| 邀请码 | ✅ | ❌ |
| 店铺设置 | ✅ | ❌ |
| 服务类型管理 | ✅ | ❌ |
页面入口
- index.html(统一入口):老板/员工同一套页面,通过「我的」页面区分功能
- 老板「我的」:员工管理 / 服务类型 / 店铺设置 / 我的订单
- 员工「我的」:我的订单
五、核心功能详解
5.1 登录体系
微信授权登录(主)
- 老板/员工均可使用微信授权登录
手机号+短信验证码登录(备用)
员工入职流程:
- 老板按员工本人手机号创建 1~30 天有效的一次性邀请;原始 token 仅创建响应显示一次,数据库只保存 hash
- 员工从小程序卡片进入,核对门店、姓名与脱敏手机号,再用微信核验的同一手机号接受邀请并自动关联门店
- 邀请可在接受前撤销,过期或已使用后不可再次创建账号;不支持永久邀请码、直接建号或随机密码登录
5.2 预约管理
创建预约:
- 老板/员工均可手动创建
- 字段:宠物名字、宠物类型(猫/狗/其他)、服务类型、预约时间、备注
- 建档人 = 当前登录用户
- 号源以服务项目预计时长、门店营业容量时段、门店并发接待数、线上预约与手动占用共同计算;创建时检查服务覆盖的每个半小时桶,并串行化同店容量判定避免超卖
- 线上预约当前不预选技师,容量是门店对外承诺的并发接待数;个人技师日历在具备预约前分配技师能力后再引入
服务类型:
- 系统默认:洗澡、美容、洗澡+美容、剪指甲、驱虫
- 老板可在默认基础上新增/编辑/删除自定义服务类型,并配置 30~480 分钟的预计时长
- 员工只能使用,不能管理
预约状态语义(与界面文案对齐):
- 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)。产品边界、参考分镜、成本与工程机制见子文档 《洗美报告短视频成片方案》。成片以服务端模板化拼接(如 FFmpeg)为主,成片可在报告页/H5 播放与分享。实现对齐备注(2026-04):服务端已落库过程素材,报告查询接口对
duringMedia的回传需与成片任务对齐;highlight_video_*、highlight_duration_sec、highlight_compose_mode等字段承载成片 URL、时长、编排方式与任务状态,本期需完成发起 → 异步生成 → 成功播放 / 失败提示闭环。
5.4 发送报告(v1 核心功能)
操作入口: 报告提交成功后进入「发送给宠主」弹层;后台报告列表也可继续处理待发送报告。
发送方式:
- 复制链接 — 生成报告链接,一键复制到剪贴板,可发给宠主微信
- 当面扫码 — 展示报告二维码,宠主现场扫码打开
- 其他方式 — 仅在员工能够确认宠主实际收到时使用
确认口径:
- 复制链接、复制话术、生成/展示二维码、预览或打开报告,都不自动等于已发送。
- 宠主实际收到后,由本店 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 老板后台(合并到「我的」页面)
员工管理:
- 老板按员工本人微信手机号创建 1~30 天有效的一次性邀请
- 小程序直接转发带受控 path 的邀请卡片;原始口令仅创建后显示一次
- 员工从卡片进入,查看门店/姓名/脱敏手机号,经微信核验后加入
- 老板查看邀请状态、撤销待接受邀请、删除已加入员工
服务类型:
- 新增服务类型
- 删除自定义服务类型(系统默认不可删)
店铺设置:
- 店铺名称
- 店铺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 | 老板后台(开通清单+员工邀请中心) | ✅ Admin 与小程序均已接入 |
| P0 | 老板后台(预约概览) | ✅ 已合并到index.html |
| P1 | 服务类型管理(老板后台) | ✅ |
| P1 | 店铺设置(老板后台) | ✅ |
| P1 | 本地文件上传(替代base64) | ✅ |
| P2 | 宠主端(历史报告汇总) | ⏳ P2(不进入本期 P0) |
八、数据库变更
-- 预约表新增技师字段
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 '店铺ID,NULL表示系统默认',
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)
- 会员、储值、套餐、复杂 CRM(P1/P2,不进入本期 P0)