# 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 | 门店承诺的并发接待数(1~10) | | 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 **一次性员工邀请**。老板按员工本人手机号创建,1~30 天有效、可撤销且只能接受一次。 | 字段 | 类型 | 说明 | |------|------|------| | 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 | 下单时的服务时长快照(30~480,30 分钟粒度) | | 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 | 门店 ID(NULL=系统默认) | | name | VARCHAR | 服务名称 | | duration_minutes | INT | 预计服务时长(30~480,30 分钟粒度) | | 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 | 连续占用时长(30~480,30 分钟粒度) | | 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 | 门店 ID(customer 可空) | | 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 | 门店 ID(NULL=系统默认) | | service_name | VARCHAR(64) | 与 `t_service_type.name` 对齐作为匹配键 | | pet_type | VARCHAR(16) | 按宠物类型细分(dog/cat/other),NULL=全部 | | 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:customer_timeline **门店客户事实时间线**(非 JPA 只读模型)。以 canonical `StoreCustomer.id` 为稳定客户关系键,把已经持久化的 `BusinessEvent` 投影为门店可理解的预约、履约、报告发送/打开和回访意向记录;被合并投影的事件通过别名指针一并读取,不改写历史事件。 | 字段 | 说明 | |------|------| | customer | 客户称呼、脱敏手机号、首次来源、首次/最近接触时间 | | total / page / pageSize / hasMore | 稳定倒序分页信息 | | list | `eventId/eventType/occurredAt/title/description` 与可选预约/报告/回访低敏摘要 | - **归属系统**:Backend Core / Store Admin FE - **代码**:`backend/src/main/java/com/petstore/service/StoreCustomerTimelineService.java`、`admin/src/views/CustomersView.vue` - **隐私边界**:不返回手机号原文、备注、媒体、报告/邀请 token、openid/unionid、IP 或原始 `metadata_json` - **证据**:`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 Core(auth 包) - **代码**:`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 A:boss / 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 | 本店客户稳定 ID(API: storeCustomerId) | | store_id | 本店范围 | | customer_user_id | 可选全局 customer 账号;留资客户可先为空 | | phone / display_name | 本店联系手机号与称呼;API 手机号默认脱敏 | | source | 首次来源:customer_booking / assisted_booking / report_lead | | first_contact_at / last_contact_at | 首次/最近接触时间 | | merged_into_store_customer_id | 仅软删除投影使用;指向同店有效 canonical 主档,保持不可变事件连续性 | | 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 期仅工作台轻量指标 后续如进入实现,再补完整字段、动作与规则。