petstore-docs/ontology/objects.md

417 lines
18 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.

# Objects实体
> 持久名词 + 字段 + 归属系统。本批覆盖 P0 核心闭环的 5 个实体。
## entity:store
**门店**。宠物店主体,所有业务数据的归属范围。
| 字段 | 类型 | 说明 |
|------|------|------|
| id | BIGINT | 主键 |
| name | VARCHAR | 店铺名称 |
| logo | VARCHAR | 店铺 Logo |
| phone | VARCHAR | 联系电话 |
| address | VARCHAR | 地址 |
| latitude | Double | 纬度(公开报告页不返回) |
| longitude | Double | 经度(公开报告页不返回) |
| intro | TEXT | 简介 |
| owner_id | BIGINT | 老板用户 ID |
| invite_code | VARCHAR(8) | 员工邀请码 |
| booking_day_start | LocalTime | 预约首号 |
| booking_last_slot_start | LocalTime | 预约末号 |
| create_time / update_time | DATETIME | 时间戳 |
| deleted | TINYINT(1) | 软删 |
- **归属系统**Backend Core`StoreController` / `StoreService` / `StoreMapper`
- **代码**`backend/src/main/java/com/petstore/entity/Store.java`
- **文档**`docs/产品设计文档.md §三 t_store`
- **测试**`StoreControllerTest`
- **证据**`anchored`
## entity:user
**用户**。三角色:`boss`(老板)/ `staff`(员工)/ `customer`(宠主)。
| 字段 | 类型 | 说明 |
|------|------|------|
| id | BIGINT | 主键 |
| username | VARCHAR | 用户名 |
| password | VARCHAR | 密码(`@JsonIgnore` 不返回;登录成功 `setPassword(null)` |
| name | VARCHAR | 姓名/技师名 |
| phone | VARCHAR | 手机号 |
| avatar | VARCHAR | 头像 |
| store_id | BIGINT | 所属店铺customer 可空) |
| role | VARCHAR | boss / staff / customer |
| wechat_openid | VARCHAR(64) | 微信小程序 openid`@JsonIgnore`,仅服务端使用) |
| wechat_unionid | VARCHAR(64) | 微信 unionid`@JsonIgnore` |
| create_time / update_time | DATETIME | 时间戳 |
| deleted | TINYINT(1) | 软删 |
- **归属系统**Backend Core`UserController` / `UserService` / `UserMapper`
- **代码**`backend/src/main/java/com/petstore/entity/User.java`
- **文档**`docs/产品设计文档.md §三 t_user`、`docs/.agents/common-context.md 术语规则`
- **测试**`UserServiceLoginStrategyTest`
- **证据**`anchored`
## entity:appointment
**预约**。状态机:`new`(待开始)→ `doing`(服务中)→ `done`(已完成)/ `cancel`(已取消)。
| 字段 | 类型 | 说明 |
|------|------|------|
| id | BIGINT | 主键 |
| pet_name | VARCHAR | 宠物名称 |
| pet_type | VARCHAR | 宠物类型(猫/狗/其他) |
| service_type | VARCHAR | 服务类型 |
| appointment_time | DATETIME | 预约时间(半小时档,按营业时间/占用/已过校验) |
| status | VARCHAR | new / doing / done / cancel |
| store_id | BIGINT | 所属门店 |
| customer_user_id | BIGINT | 被服务客户账号;新业务归属主字段 |
| created_by_user_id | BIGINT | 实际创建预约的登录账号customer/boss/staff |
| user_id | BIGINT | legacy 兼容;新数据镜像 customer_user_id |
| assigned_user_id | BIGINT | 服务技师 ID开始服务时赋值API 别名 assignedStaffId |
| pet_id | BIGINT | 宠物档案 ID |
| remark | TEXT | 备注 |
| create_time / update_time | DATETIME | 时间戳 |
| deleted | TINYINT(1) | 软删 |
- **归属系统**Backend Core`AppointmentController` / `AppointmentService` / `AppointmentMapper`
- **代码**`backend/src/main/java/com/petstore/entity/Appointment.java`、`backend/src/main/java/com/petstore/service/AppointmentService.java`
- **文档**`docs/产品设计文档.md §三 t_appointment §5.2 预约管理`
- **测试**`AppointmentServiceTest`、`AppointmentControllerDetailTest`
- **证据**`anchored`
## entity:report
**服务报告**。一约一份,提交后锁死;必须绑定 `appointment_id`
| 字段 | 类型 | 说明 |
|------|------|------|
| id | BIGINT | 主键 |
| appointment_id | BIGINT | 关联预约 ID唯一约束 `uk_report_appointment` |
| remark | TEXT | 备注 |
| customer_user_id | BIGINT | 报告归属客户,从预约派生 |
| author_staff_id | BIGINT | 实际提交报告的门店账号 |
| user_id | BIGINT | legacy 兼容;新数据镜像 author_staff_id |
| store_id | BIGINT | 所属门店 |
| report_token | VARCHAR | 公开访问令牌UUID不带回包给公开页 |
| pet_name | VARCHAR | 宠物名(冗余) |
| service_type | VARCHAR | 服务类型(冗余) |
| appointment_time | DATETIME | 服务时间(冗余) |
| staff_name | VARCHAR | 服务技师名快照(优先取预约 assigned_user_id |
| highlight_video_url | VARCHAR(500) | 成片 MP4 相对 URL |
| highlight_video_status | VARCHAR(32) | processing / done / failed |
| highlight_video_error | VARCHAR(500) | 错误信息 |
| highlight_duration_sec | INT | 成片时长 |
| highlight_compose_mode | VARCHAR(16) | preset / interleave |
| highlight_fail_reason | VARCHAR(16) | material / service / network / unknown |
| highlight_share_cover_url | VARCHAR(500) | 成片首帧封面 |
| create_time / update_time | DATETIME | 时间戳 |
| deleted | TINYINT(1) | 软删 |
- **归属系统**Report Media Backend`ReportController` / `ReportService` / `ReportMapper`
- **代码**`backend/src/main/java/com/petstore/entity/Report.java`、`backend/src/main/java/com/petstore/service/ReportService.java`
- **文档**`docs/产品设计文档.md §三 t_report §5.3 服务报告`、`docs/API-报告成片字段契约.md`
- **测试**`ReportServiceTest`、`ReportControllerTest`
- **证据**`anchored`
## entity:report_lead
**留资线索**。宠主在公开报告页提交手机号,进入门店回访池。
| 字段 | 类型 | 说明 |
|------|------|------|
| id | BIGINT | 主键 |
| report_id | BIGINT | 关联报告 ID |
| store_id | BIGINT | 所属门店(用于回访池范围) |
| phone | VARCHAR | 手机号 |
| pet_name | VARCHAR | 宠物名(冗余) |
| service_type | VARCHAR | 服务类型(冗余) |
| wechat_openid | VARCHAR(64) | 微信 openid脱敏返回 `wechatOpenidMasked` |
| wechat_unionid | VARCHAR(64) | 微信 unionid脱敏返回 `wechatUnionidMasked` |
| reminder_type | VARCHAR | 提醒类型 |
| remind_date | DATE | 下次建议日期 |
| remind_status | VARCHAR | pending / sent / canceled / unsubscribed |
| unsubscribe_token | VARCHAR | 退订令牌 |
| client_ip | VARCHAR | 留资 IP合规留证 |
| create_time / update_time | DATETIME | 时间戳 |
| deleted | TINYINT(1) | 软删 |
- **归属系统**Report Media Backend`ReportLeadController` / `ReportLeadService`
- **代码**`backend/src/main/java/com/petstore/controller/ReportLeadController.java`、`backend/src/main/java/com/petstore/entity/ReportLead.java`
- **文档**`docs/本期主线-下一步产品优化点.md §C 留资`
- **测试**暂无Batch D 未补 `ReportLeadControllerTest`,列为 `documented` gap
- **证据**`anchored`(脱敏与跨店校验已实现并测试覆盖;去重逻辑 `documented`
---
## Batch 2 录入实体
## entity:pet
**宠物档案**。归属某客户(`ownerUserId`);商家/员工通过预约关联查看本店服务过的宠物。
| 字段 | 类型 | 说明 |
|------|------|------|
| id | BIGINT | 主键 |
| name | VARCHAR | 昵称 |
| pet_type | VARCHAR | 猫/狗/其他 |
| breed | VARCHAR | 品种(可选) |
| avatar | VARCHAR | 头像相对路径 |
| owner_user_id | BIGINT | 所属用户(客户) |
| remark | TEXT | 备注 |
| create_time / update_time | DATETIME | 时间戳 |
| deleted | TINYINT(1) | 软删 |
- **归属系统**Backend Core`PetController` / `PetService` / `PetMapper`
- **代码**`backend/src/main/java/com/petstore/entity/Pet.java`、`controller/PetController.java`、`service/PetService.java`
- **文档**`docs/产品设计文档.md §三 t_pet隐含预约含 pet_name/pet_type`
- **测试**:暂无 `PetControllerTest``gap`,建议补)
- **证据**`anchored`(实体与跨用户/跨店校验已实现;测试覆盖为 `gap`
## entity:service_type
**服务类型**。`storeId` 为空表示系统默认(不可改删);非空为门店自定义。
| 字段 | 类型 | 说明 |
|------|------|------|
| id | BIGINT | 主键 |
| store_id | BIGINT | 门店 IDNULL=系统默认) |
| name | VARCHAR | 服务名称 |
| create_time | DATETIME | 创建时间 |
- **归属系统**Backend Core`ServiceTypeController` / `ServiceTypeService` / `ServiceTypeMapper`
- **代码**`backend/src/main/java/com/petstore/entity/ServiceType.java`、`controller/ServiceTypeController.java`
- **文档**`docs/产品设计文档.md §三 t_service_type §5.2 服务类型`
- **测试**`ServiceTypeControllerTest`12 用例)
- **证据**`anchored`
## entity:schedule_block
**门店日程手动占用**。与线上预约一样占用半小时档,用于到店客(`walk_in`)或暂停预约(`blocked`)。
| 字段 | 类型 | 说明 |
|------|------|------|
| id | BIGINT | 主键 |
| store_id | BIGINT | 所属门店(非空) |
| slot_start | DATETIME | 半小时档起始时刻 |
| block_type | VARCHAR(32) | walk_in / blocked |
| note | VARCHAR(500) | 备注 |
| created_by_user_id | BIGINT | 创建人(从上下文派生) |
| create_time / update_time | DATETIME | 时间戳 |
| deleted | TINYINT(1) | 软删 |
- **归属系统**Backend Core`ScheduleController` / `ScheduleService` / `ScheduleBlockMapper`
- **代码**`backend/src/main/java/com/petstore/entity/ScheduleBlock.java`、`controller/ScheduleController.java`、`service/ScheduleService.java`
- **文档**`docs/产品设计文档.md §5.2(隐含,预约时段占用)`
- **测试**`ScheduleControllerTest`7 用例)
- **证据**`anchored`
## entity:report_image
**报告媒体**。`photo_type` 分组:`before`(服务前)/ `after`(服务后)/ `during`(服务过程中)。
| 字段 | 类型 | 说明 |
|------|------|------|
| id | BIGINT | 主键 |
| report_id | BIGINT | 关联报告 ID |
| photo_url | VARCHAR(500) | 媒体 URL |
| photo_type | VARCHAR(20) | before / after / during |
| media_type | VARCHAR(20) | photo / video默认 photo |
| sort_order | INT | 同类型内排序 |
| create_time | DATETIME | 创建时间 |
- **归属系统**Report Media Backend`ReportImageMapper`
- **代码**`backend/src/main/java/com/petstore/entity/ReportImage.java`、`mapper/ReportImageMapper.java`
- **文档**`docs/产品设计文档.md §三 t_report_image`
- **测试**:间接(`ReportServiceTest#createWithImagesSavesEachImage`
- **证据**`anchored`
## entity:highlight_video
**服务回顾短片**。当前作为 `entity:report``highlight_*` 字段承载,非独立 JPA 实体;状态机 `processing` / `done` / `failed`
| 字段 | 类型 | 说明 |
|------|------|------|
| report_id | BIGINT | 关联报告(即 Report.id |
| highlight_video_url | VARCHAR(500) | 成片 MP4 相对 URL |
| highlight_video_status | VARCHAR(32) | processing / done / failed |
| highlight_video_error | VARCHAR(500) | 错误信息 |
| highlight_duration_sec | INT | 成片时长 |
| highlight_compose_mode | VARCHAR(16) | preset / interleave |
| highlight_fail_reason | VARCHAR(16) | material / service / network / unknown |
| highlight_share_cover_url | VARCHAR(500) | 成片首帧封面 |
- **归属系统**Report Media Backend`ReportHighlightVideoService`
- **代码**`backend/src/main/java/com/petstore/entity/Report.java`(字段承载)、`service/ReportHighlightVideoService.java`
- **文档**`docs/API-报告成片字段契约.md`、`docs/洗美报告短视频成片方案.md`
- **测试**:暂无独立测试(`gap`,建议补成片三态用例)
- **证据**`anchored`字段与三态已实现FFmpeg 集成测试为 `gap`
## entity:session_token
**HMAC session token**。非 JPA 实体鉴权上下文数据结构token 格式 `base64url(payload).base64url(sig)`HMAC-SHA256默认 7 天。
| 字段 | 类型 | 说明 |
|------|------|------|
| userId | Long | 用户 ID |
| storeId | Long | 门店 IDcustomer 可空) |
| role | String | boss / staff / customer |
| exp | long | Unix 秒过期时间 |
- **归属系统**Backend Core`SessionTokenService` / `CurrentUser` / `CurrentUserContext` / `AuthInterceptor`
- **代码**`backend/src/main/java/com/petstore/auth/SessionTokenService.java`、`CurrentUser.java`、`CurrentUserContext.java`、`AuthInterceptor.java`
- **文档**`docs/ontology/rules.md#rule:BR-AUTH-001`
- **测试**`SessionTokenServiceTest`7 用例)
- **证据**`anchored`
---
## Batch 2.1 补录实体audit_drift 发现的遗漏)
## entity:report_testimonial
**宠主寄语**。生成分享海报时填写的一句话(口碑素材);同一报告保留一条,重复提交更新内容与时间。
| 字段 | 类型 | 说明 |
|------|------|------|
| id | BIGINT | 主键 |
| report_id | BIGINT | 关联报告(唯一约束,一报告一寄语) |
| store_id | BIGINT | 所属门店 |
| content | VARCHAR(200) | 寄语内容 |
| is_public | TINYINT(1) | 是否公开(默认 1 |
| create_time / update_time | DATETIME | 时间戳 |
- **归属系统**Report Media Backend`ReportTestimonialService`
- **代码**`backend/src/main/java/com/petstore/entity/ReportTestimonial.java`、`service/ReportTestimonialService.java`
- **入口**`POST /api/report/{token}/testimonial` → `ReportController#submitTestimonial`
- **证据**`anchored`
## entity:service_interval
**服务建议周期配置**。用于「下次该洗了」的日期预测;`store_id` 为 null 表示系统默认,老板可针对自家店覆盖。
| 字段 | 类型 | 说明 |
|------|------|------|
| id | BIGINT | 主键 |
| store_id | BIGINT | 门店 IDNULL=系统默认) |
| service_name | VARCHAR(64) | 与 `t_service_type.name` 对齐作为匹配键 |
| pet_type | VARCHAR(16) | 按宠物类型细分dog/cat/otherNULL=全部 |
| interval_days_min | INT | 建议最短间隔(天) |
| interval_days_max | INT | 建议最长间隔(天) |
| create_time | DATETIME | 创建时间 |
- **归属系统**Report Media Backend`ReportLeadService#suggestNext` 读取)
- **代码**`backend/src/main/java/com/petstore/entity/ServiceInterval.java`
- **证据**`anchored`(实体存在;独立 controller 接口 `gap`,目前由 `ReportLeadService` 内部读取)
## entity:highlight_fail_reason
**成片失败原因归类**(非 JPA 枚举)。对宠主展示口径,不含技术堆栈。
| 枚举值 | code | 说明 |
|------|------|------|
| MATERIAL | material | 素材缺失/无法读取/编码失败 |
| SERVICE | service | 合成服务异常ffmpeg/磁盘等) |
| NETWORK | network | 网络类异常(预留) |
| UNKNOWN | unknown | 未归类 |
- **归属系统**Report Media Backend
- **代码**`backend/src/main/java/com/petstore/entity/HighlightFailReason.java`
- **证据**`anchored``jpa: false`
## entity:current_user
**当前登录用户上下文**(非 JPA record。由 `AuthInterceptor``entity:session_token` 解析后写入 ThreadLocal与令牌实体区分。
| 字段 | 类型 | 说明 |
|------|------|------|
| userId | Long | 用户 ID |
| storeId | Long | 所属门店customer 可为 null |
| role | String | boss / staff / customer |
- **归属系统**Backend Coreauth 包)
- **代码**`backend/src/main/java/com/petstore/auth/CurrentUser.java`、`CurrentUserContext.java`、`AuthInterceptor.java`
- **证据**`anchored``jpa: false`
---
## Batch门店 Web 后台预录入Phase A · documented
> 产品依据:`docs/门店管理后台升级方案-对标宠老板.md`。
> **尚未编码**;设计与实现须引用下列 ID。收银/会员资产仍见下方 gap 占位。
## entity:admin_console
**门店 Web 管理后台**(非 JPA。与 uni-app 小程序并存的桌面优先管理端;登录落点为工作台,不做收银/库存首页。
| 字段(概念) | 说明 |
|------|------|
| surface | Web admin |
| navModules | workbench / appointments / reports / customers / leads / schedule / settings |
| storeScope | 单店 `storeId` |
| roleGate | Phase Aboss / staff 均可读写本店customer 禁止 |
- **归属系统**Store Admin FE + Backend Core
- **证据**`documented``jpa: false`
- **约束**`rule:BR-ADMIN-001` … `BR-ADMIN-004`
## entity:workbench
**今日工作台**(非 JPA 聚合视图)。指标卡 + 今日待办 + 报告漏斗摘要;数据来自 appointment/report/lead/highlight**无独立表**。
| 字段(概念) | 说明 |
|------|------|
| todayAppointmentCount / todayDoneCount | 今日预约 / 今日完成 |
| pendingLeadCount / highlightFailedCount | 待回访 / 成片失败 |
| todoItems | `entity:workbench_todo_item[]` |
| reportFunnel | 已发/已打开/已留资/报告转预约(缺埋点时可显示 —) |
| anomalyList | 成片失败或待回访最近 N 条 |
- **证据**`documented`
- **动作**`action:view_workbench`
## entity:workbench_todo_item
**工作台待办项**
| 字段 | 说明 |
|------|------|
| kind | `today_appointments` / `pending_report` / `highlight_anomaly` / `pending_lead` |
| count | 数量 |
| drilldownAction | 下钻到的 admin list action |
| filter | 预置筛选 |
- **证据**`documented`
## entity:service_customer
**服务客户主档(瘦身)**。服务经营视角的客户投影User + Pet + 预约/报告/留资),**不是** `entity:membership`
| 字段 | 说明 |
|------|------|
| userId / displayName / phoneMasked | 身份(手机默认脱敏) |
| pets | 关联宠物摘要 |
| lastVisitAt / lastReportId | 最近到店 / 最近报告 |
| leadStatus / source | 留资状态;来源:预约/报告留资 |
| storeId | 本店范围 |
- **不做**:余额、押金、积分、次卡、会员等级、批量导入持卡会员
- **证据**`documented`(可先无独立表,查询聚合)
- **动作**`action:admin_list_service_customers`
---
## 待录入P1/P2 方向gap 证据)
以下已在 `graph/ontology.jsonl``evidence=gap` + `jpa=false` 占位,**不展开字段****Phase A 门店后台明确不做**`rule:BR-ADMIN-004`
- `entity:membership`(会员卡)— P1/P2 / C 期
- `entity:stored_value`(储值)— P1/P2
- `entity:service_package`(套餐/次卡)— P1/P2
- `entity:report_history`(宠主历史报告汇总)— P2
- `entity:analytics_dashboard`(经营分析看板)— B 期A 期仅工作台轻量指标
后续如进入实现,再补完整字段、动作与规则。