petstore-docs/ontology/objects.md

503 lines
24 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实体
> 持久名词 + 字段 + 归属系统。以 `graph/ontology.jsonl` 为机器可审计真源。
## 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) | legacy 兼容字段;不返回客户端、不再作为员工注册凭证 |
| booking_day_start | LocalTime | 预约首号 |
| booking_last_slot_start | LocalTime | 预约末号 |
| booking_capacity | INT | 门店承诺的并发接待数110 |
| onboarding_status | VARCHAR(16) | unknown历史不可推断/ in_progress / completed |
| onboarding_completed_at | DATETIME | 老板显式完成开通时间 |
| onboarding_completed_by_user_id | BIGINT | 显式完成开通的老板 |
| 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:staff_invitation
**一次性员工邀请**。老板按员工本人手机号创建130 天有效、可撤销且只能接受一次。
| 字段 | 类型 | 说明 |
|------|------|------|
| invitation_id | VARCHAR(36) | 对外随机 ID管理端撤销使用 |
| token_hash | VARCHAR(64) | 256-bit 原始 token 的 SHA-256原始 token 不落库 |
| store_id | BIGINT | 受邀门店 |
| invited_name / invited_phone | VARCHAR | 受邀姓名 / 服务端核验手机号API 只返回脱敏手机号 |
| status | VARCHAR(16) | pending / accepted / revoked / expired |
| expires_at | DATETIME | 到期时间 |
| created_by_user_id | BIGINT | 创建邀请的老板 |
| accepted_by_user_id / accepted_at | BIGINT / DATETIME | 首次成功接受回执 |
| revoked_by_user_id / revoked_at | BIGINT / DATETIME | 撤销回执 |
| create_time / update_time | DATETIME | 时间戳 |
- **安全边界**:原始 token 仅在创建响应返回一次;列表/预览不返回 `token_hash`、原始 token 或完整手机号。
- **代码**`backend/src/main/java/com/petstore/entity/StaffInvitation.java`、`service/StaffInvitationService.java`
- **测试**`StaffInvitationServiceTest`、`StoreOnboardingMigrationTest`
- **证据**`anchored`
## entity:audit_log
**不可变操作审计**。记录开店、门店配置和员工权限类操作,与可统计经营事实 `BusinessEvent` 分开。
| 字段 | 类型 | 说明 |
|------|------|------|
| audit_id | VARCHAR(36) | 随机审计 ID |
| store_id | BIGINT | 强制门店范围 |
| actor_user_id / actor_role | BIGINT / VARCHAR | 操作人快照 |
| action | VARCHAR(64) | 受控动作名 |
| target_type / target_id | VARCHAR | 目标类型 / 安全引用 ID |
| outcome | VARCHAR(16) | success 等受控结果 |
| metadata_json | TEXT | 服务端白名单低敏标量 JSON |
| occurred_at / create_time | DATETIME | 发生 / 入库时间 |
- **禁止字段**手机号、token/URL、openid/unionid、密码/密钥、IP、地址/坐标、备注/内容和原始请求体。
- **代码**`backend/src/main/java/com/petstore/entity/AuditLog.java`、`service/AuditLogService.java`
- **测试**`AuditLogServiceTest`、`StoreOnboardingMigrationTest`
- **证据**`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 | 预约开始时间(半小时粒度,按服务时长检查跨桶容量) |
| duration_minutes | INT | 下单时的服务时长快照3048030 分钟粒度) |
| 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 |
| send_status | VARCHAR(16) | unknown历史不可确认/ unsent新报告待发送/ sent已显式确认 |
| sent_at | DATETIME | 首次显式确认发送时间 |
| sent_by_user_id | BIGINT | 首次确认发送的门店员工 |
| send_channel | VARCHAR(16) | wechat / qr / other首次确认后不可覆盖 |
| 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`
## entity:business_event
**不可变业务事件**。预约、履约、报告打开与留资等经营事实的统一持久化入口,为报告漏斗和 StoreCustomer 时间线提供可统计事实源。
| 字段 | 类型 | 说明 |
|------|------|------|
| 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 | 发生时间 / 入库时间 |
| metadata_json | TEXT | 服务端白名单低敏标量 JSON |
| idempotency_key | VARCHAR(128) | 可选幂等键 |
- **隐私**:不保存手机号、报告 token/hash、媒体 URL、IP、openid/unionid、备注或原始请求体
- **不变量**:只追加,不更新、不软删;通用事件明细不直接暴露给前端
- **归属系统**Backend Core跨 Report Media Backend 写入点)
- **代码**`backend/src/main/java/com/petstore/entity/BusinessEvent.java`、`BusinessEventService.java`
- **测试**`BusinessEventServiceTest`、`BusinessEventMigrationTest`
- **证据**`anchored`
---
## 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 | 服务名称 |
| duration_minutes | INT | 预计服务时长3048030 分钟粒度) |
| 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 | 半小时档起始时刻 |
| duration_minutes | INT | 连续占用时长3048030 分钟粒度) |
| 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`、`BookingCapacityServiceTest`
- **证据**`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
> 产品依据:`docs/门店管理后台升级方案-对标宠老板.md`。
> 各对象按实际证据标记;收银/会员资产仍见下方 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报告漏斗来自 BusinessEvent**无独立工作台表**。服务端按完整本店数据计算准确总数,不再由前端拉取前 200 条估算。
| 字段(概念) | 说明 |
|------|------|
| todayAppointmentCount / todayDoneCount | 今日预约 / 今日完成 |
| overdueAppointmentCount / inServiceCount / upcomingTodayCount | 逾期未开始 / 服务中 / 今日待开始 |
| dueFollowUpCount / highlightAnomalyCount / unsentReportCount | 到期回访 / 成片失败或处理超时 / 新报告待发送 |
| actionGroups | `entity:workbench_todo_item[]`(每组最多 3 条低敏预览) |
| reportFunnel | 已提交/已确认发送/已打开/已留资/报告转预约;仅转预约仍返回未知 |
| anomalyList | 成片失败或待回访最近 N 条 |
- **代码**`admin/src/views/WorkbenchView.vue`、`AdminBusinessEventController`、`AdminWorkbenchService`、`AdminBusinessEventService`
- **测试**`AdminBusinessEventControllerTest`、`AdminWorkbenchServiceTest`、`AdminBusinessEventServiceTest`
- **证据**`anchored`
- **动作**`action:view_workbench`
## entity:workbench_todo_item
**工作台待办项**
| 字段 | 说明 |
|------|------|
| kind | `overdue_appointment` / `in_service` / `upcoming_today` / `highlight_anomaly` / `unsent_report` / `follow_up_due` |
| urgency | critical / high / normal |
| count | 数量 |
| items | 最多 3 条 resource ID + 宠物/服务/时间低敏预览无手机号、token、媒体 URL |
| drilldownAction / filter | 下钻到的 admin list action 与预置筛选 |
- **代码**`AdminWorkbenchService`、`admin/src/views/WorkbenchView.vue`
- **测试**`AdminWorkbenchServiceTest`
- **证据**`anchored`
## entity:store_customer
**StoreCustomer 门店客户主档(瘦身)**。门店与客户的稳定关系实体,**不是** `entity:membership`。后台读模型以它为入口,再聚合 Pet + Appointment + Report + ReportLead。
| 字段 | 说明 |
|------|------|
| id | 本店客户稳定 IDAPI: storeCustomerId |
| store_id | 本店范围 |
| customer_user_id | 可选全局 customer 账号;留资客户可先为空 |
| phone / display_name | 本店联系手机号与称呼API 手机号默认脱敏 |
| source | 首次来源customer_booking / assisted_booking / report_lead |
| first_contact_at / last_contact_at | 首次/最近接触时间 |
| create_time / update_time / deleted | 审计与软删除 |
API 动态聚合字段:`pets`、`lastVisitAt`、`lastReportId`、`leadStatus`、`source`。`lastVisitAt` 只统计已开始/已完成的历史服务。
- **不做**:余额、押金、积分、次卡、会员等级、批量导入持卡会员
- **证据**`anchored``t_store_customer` + 动态读模型)
- **动作**`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 期仅工作台轻量指标
后续如进入实现,再补完整字段、动作与规则。