# Events（事件）

> 动作或状态变更后发生的事实（过去时）。用于埋点、状态机迁移、领域事件。

## Store 相关

### event:store_registered

微信核验手机号后的老板入驻成功，`Store` + `boss` `User` 创建完成，`session_token` 签发；同时以 BusinessEvent 和 AuditLog 持久化。

- **触发动作**：`action:register_boss`
- **payload**：`storeId`、`bossUserId`、`occurredAt`；不含手机号或 legacy inviteCode
- **代码**：`BusinessEventService#recordStoreRegistered`、`MerchantOnboardingService#registerVerifiedBoss`
- **证据**：`anchored`

## User / 鉴权相关

### event:session_issued

登录/注册成功后签发 HMAC session token。

- **触发动作**：`action:register_boss`、`action:login`、`action:wx_phone_login`、`action:accept_staff_invitation`
- **payload**：`userId`、`storeId`、`role`、`exp`
- **代码**：`backend/src/main/java/com/petstore/auth/SessionTokenService.java#issue`
- **测试**：`SessionTokenServiceTest`
- **证据**：`anchored`

### event:wx_bound

微信 openid/unionid 绑定到用户。

- **触发动作**：`action:bind_wechat`（`action:wx_phone_login` 内）
- **payload**：`userId`、`openid`、`unionid?`
- **代码**：`backend/src/main/java/com/petstore/service/UserService.java#bindWechatMiniIdentity`
- **证据**：`anchored`

## Appointment 相关

### event:appointment_created

预约创建成功（`status=new`，按服务时长跨容量桶占号生效）；低敏 metadata 保存 `bookingOrigin` 与 `durationMinutes` 快照。

- **触发动作**：`action:create_appointment`
- **持久字段**：`storeId`、`storeCustomerId`、`aggregateType=appointment`、`aggregateId`、`actorUserId/role`、`bookingOrigin`
- **代码**：`backend/src/main/java/com/petstore/service/AppointmentService.java#createBooking`、`BusinessEventService#recordAppointmentCreated`
- **测试**：`AppointmentServiceTest`、`BusinessEventServiceTest`
- **证据**：`anchored`

### event:appointment_status_changed

预约状态迁移（`new→doing`、`new→cancel`、`doing→done`、`doing→cancel`）。

- **触发动作**：`action:start_service`（`new→doing`）、`action:transition_appointment_status`、`action:submit_report`（`doing→done`）
- **持久字段**：`aggregateId=appointmentId`、`fromStatus`、`toStatus`、`actorUserId/role`、`storeCustomerId?`
- **适用规则**：`rule:BR-APPT-001`
- **代码**：`AppointmentService#startService`/`#transitionStatus`、`ReportService#create`、`BusinessEventService#recordAppointmentStatusChanged`
- **测试**：`AppointmentServiceTest`、`ReportServiceTest#createDoingAppointmentMarksDone`
- **证据**：`anchored`

### event:service_started

服务正式开始（`new -> doing`），是履约时长与服务漏斗的开始事实。

- **触发动作**：`action:start_service`
- **持久字段**：`aggregateId=appointmentId`、`storeId`、`storeCustomerId?`、`actorUserId/role`、`occurredAt`
- **幂等键**：`service_started:{appointmentId}`
- **代码**：`backend/src/main/java/com/petstore/service/BusinessEventService.java#recordAppointmentStatusChanged`
- **测试**：`BusinessEventServiceTest#statusToDoingRecordsGenericAndServiceStartedFacts`
- **证据**：`anchored`

### event:service_completed

服务完成（`doing -> done`），可由报告提交或合法手工完成触发。

- **触发动作**：`action:transition_appointment_status`、`action:submit_report`
- **持久字段**：同 `event:service_started`
- **幂等键**：`service_completed:{appointmentId}`
- **代码**：`BusinessEventService#recordAppointmentStatusChanged`、`ReportService#create`
- **测试**：`AppointmentServiceTest`、`ReportServiceTest`
- **证据**：`anchored`

## Report 相关

### event:report_submitted

报告提交成功，关联预约置 `done`。

- **触发动作**：`action:submit_report`
- **持久字段**：`aggregateId=reportId`、`storeId`、`storeCustomerId?`、`actorUserId/role`；不保存 report token/hash
- **适用规则**：`rule:BR-RPT-001`、`rule:BR-RPT-002`、`rule:BR-RPT-004`
- **代码**：`backend/src/main/java/com/petstore/service/ReportService.java#create`、`BusinessEventService#recordReportSubmitted`
- **测试**：`ReportServiceTest`
- **证据**：`anchored`

### event:report_opened

报告页首次打开（本地 storage 标记首次）。

- **触发动作**：`action:track_report_open`
- **持久字段**：`aggregateId=reportId`、`storeId`、`storeCustomerId?`、`visitType=first`、`occurredAt`；事件表不保存 token/hash
- **适用规则**：`rule:BR-RPT-007`（token hash 日志）
- **代码**：`ReportController#trackReportOpen`、`BusinessEventService#recordReportOpenByToken`
- **测试**：`ReportControllerTest#reportOpenPersistsLowSensitivityBusinessEvent`、`BusinessEventServiceTest#reportOpenStoresReportIdButNeverToken`
- **文档**：`docs/本期主线-下一步产品优化点.md 埋点事件 schema`
- **证据**：`anchored`（事件已落库并进入工作台去重统计；日志仅保留短 hash）

### event:report_sent

门店员工首次显式确认宠主已经收到报告。历史报告不猜测、不回填该事件。

- **触发动作**：`action:confirm_report_sent`
- **持久字段**：`aggregateId=reportId`、`storeId`、`storeCustomerId?`、`actorUserId/role`、`occurredAt=sentAt`、`channel`；不保存 report token、手机号或 URL
- **幂等键**：`report_sent:{reportId}`
- **适用规则**：`rule:BR-RPT-008`、`rule:BR-BE-001`
- **代码**：`ReportService#confirmSent`、`BusinessEventService#recordReportSent`
- **测试**：`ReportServiceTest#firstConfirmSentPersistsImmutableReceiptAndEvent`、`BusinessEventServiceTest#reportSentIsIdempotentAndStoresOnlyChannelMetadata`
- **证据**：`anchored`

### event:report_reopened

报告页再次打开（非首次）。

- **触发动作**：`action:track_report_open`
- **持久字段**：同 `event:report_opened`，`visitType=repeat`
- **代码/测试**：同 `event:report_opened`
- **证据**：`anchored`

## ReportLead 相关

### event:lead_submitted

宠主留资提交成功（含重复提交幂等更新）。

- **触发动作**：`action:submit_lead`
- **持久字段**：`aggregateId=leadId`、`storeId`、`storeCustomerId?`、`repeatSubmit`；不复制手机号、IP、openid 或 token
- **适用规则**：`rule:BR-LEAD-001`
- **代码**：`ReportLeadService#submit`、`BusinessEventService#recordLeadSubmitted`
- **测试**：`ReportLeadServiceStoreCustomerTest`、`BusinessEventMigrationTest`
- **文档**：`docs/本期主线-下一步产品优化点.md 埋点事件 schema`
- **证据**：`anchored`

### event:follow_up_created

回访任务已创建；由新留资或可靠历史待办回填触发。

- **触发动作**：`action:submit_lead`
- **payload**：`storeId`、`storeCustomerId`、`aggregateId=taskInternalId`、`dueDate`、`occurredAt`
- **证据**：`anchored`

### event:follow_up_started

门店员工已领取并开始处理回访。

- **触发动作**：`action:start_follow_up_task`
- **payload**：`storeId`、`storeCustomerId`、`aggregateId=taskInternalId`、`actorUserId`、`actorRole`
- **证据**：`anchored`

### event:follow_up_rescheduled

本次联系未形成终态并已设置下次回访日。

- **触发动作**：`action:reschedule_follow_up_task`
- **payload**：`attemptOutcome`、`attemptCount`、`dueDate`；不含手机号或自由文本
- **证据**：`anchored`

### event:follow_up_completed

回访任务以员工处理结果或真实再次预约完成。

- **触发动作**：`action:close_follow_up_task`、`action:create_appointment`
- **payload**：`outcome`、`attemptCount`
- **证据**：`anchored`

### event:follow_up_canceled

宠主退订导致开放任务取消；不伪装成员工联系。

- **触发动作**：`action:unsubscribe`
- **payload**：`outcome=unsubscribed`
- **证据**：`anchored`

### event:rebook_created

回访任务关联的真实同店预约已保存成功。

- **触发动作**：`action:create_appointment`
- **payload**：`storeId`、`storeCustomerId`、`aggregateId=appointmentId`、`bookingOrigin=follow_up`
- **证据**：`anchored`

---

## Batch 2 录入事件

## Highlight 相关

### event:highlight_succeeded

成片生成成功（`highlight_video_status` 置 `done`）。

- **触发动作**：`action:generate_highlight`（异步回调）
- **payload**：`reportTokenHash`、`appointmentId`、`durationSec`、`composeMode`、`ts`
- **适用规则**：`rule:BR-HL-002`
- **代码**：`backend/src/main/java/com/petstore/service/ReportHighlightVideoService.java`
- **文档**：`docs/本期主线-下一步产品优化点.md 埋点事件 schema`
- **证据**：`documented`（schema 已冻结；事件上报与统计落地 `gap`）

### event:highlight_failed

成片生成失败（`highlight_video_status` 置 `failed`，`highlight_fail_reason` 归类）。

- **触发动作**：`action:generate_highlight`（异步回调）
- **payload**：`reportTokenHash`、`appointmentId`、`failReason`（material/service/network/unknown）、`ts`
- **适用规则**：`rule:BR-HL-002`
- **代码**：`backend/src/main/java/com/petstore/service/ReportHighlightVideoService.java`
- **文档**：`docs/本期主线-下一步产品优化点.md 埋点事件 schema`
- **证据**：`documented`

### event:video_play

报告页成片点击播放。

- **触发动作**：`action:track_video_play`（前端客户端）
- **payload**：`reportTokenHash`、`ts`、`source`
- **文档**：`docs/本期主线-下一步产品优化点.md 埋点事件 schema`
- **证据**：`documented`（schema 已冻结；上报未实现 `gap`）

### event:video_save

报告页成片保存到相册。

- **触发动作**：`action:track_video_save`（前端客户端）
- **payload**：`reportTokenHash`、`ts`
- **文档**：`docs/本期主线-下一步产品优化点.md 埋点事件 schema`
- **证据**：`documented`（schema 已冻结；上报未实现 `gap`）

## Upload 相关

### event:upload_completed

上传图片/视频完成。

- **触发动作**：`action:upload_image`
- **payload**：`url`、`mediaType`、`size`、`ts`
- **代码**：`backend/src/main/java/com/petstore/controller/FileController.java#uploadImage`
- **证据**：`implemented`（接口返回 url，事件未单独上报）

## Schedule 相关

### event:schedule_block_created

手动占用时段创建成功。

- **触发动作**：`action:create_schedule_block`
- **payload**：`blockId`、`storeId`、`slotStart`、`blockType`、`createdByUserId`
- **代码**：`backend/src/main/java/com/petstore/service/ScheduleService.java#createBlock`
- **证据**：`anchored`

### event:schedule_block_deleted

手动占用时段取消。

- **触发动作**：`action:delete_schedule_block`
- **payload**：`blockId`、`storeId`
- **代码**：`backend/src/main/java/com/petstore/service/ScheduleService.java#deleteBlock`
- **证据**：`anchored`

## ServiceType / Pet / Staff 相关

### event:service_type_created

服务类型创建（仅门店自定义）。

- **触发动作**：`action:create_service_type`
- **payload**：`serviceTypeId`、`storeId`、`name`
- **代码**：`backend/src/main/java/com/petstore/service/ServiceTypeService.java#create`
- **证据**：`anchored`

### event:service_type_deleted

服务类型删除（仅门店自定义）。

- **触发动作**：`action:delete_service_type`
- **payload**：`serviceTypeId`、`storeId`
- **代码**：`backend/src/main/java/com/petstore/service/ServiceTypeService.java#delete`
- **证据**：`anchored`

### event:pet_created

宠物档案创建。

- **触发动作**：`action:create_pet`
- **payload**：`petId`、`ownerUserId`、`name`、`petType`
- **代码**：`backend/src/main/java/com/petstore/service/PetService.java#create`
- **证据**：`anchored`

### event:staff_invitation_accepted

受邀员工通过微信手机号核验后首次接受邀请并创建 staff 账号；以 AuditLog 持久化。

- **触发动作**：`action:accept_staff_invitation`
- **payload**：`storeId`、`staffUserId`、`invitationId`、`verificationMethod`、`occurredAt`；不含手机号、token 或微信标识
- **代码**：`StaffInvitationService#acceptVerified`、`AuditLogService#record`
- **测试**：`StaffInvitationServiceTest`
- **证据**：`anchored`

### event:user_updated

当前登录用户信息更新（头像/姓名/手机号）。

- **触发动作**：`action:update_user`
- **payload**：`userId`、`updatedFields`
- **代码**：`backend/src/main/java/com/petstore/service/UserService.java#updateUser`
- **证据**：`anchored`

### event:testimonial_saved

宠主寄语已保存（一报告一寄语，重复提交为更新）。

- **触发动作**：`action:submit_testimonial`
- **payload**：`reportTokenHash`、`testimonialId`、`isPublic`、`ts`
- **适用规则**：`rule:BR-TST-001`
- **代码**：`backend/src/main/java/com/petstore/service/ReportTestimonialService.java`
- **证据**：`documented`（写库已实现；独立事件上报/统计未落地）

### event:session_revoked

会话吊销（服务端 logout）。

- **触发动作**：`action:logout`
- **payload**：`userId`、`ts`
- **证据**：`gap`（当前无服务端吊销实现）

### event:workbench_viewed

门店后台工作台已打开（可选埋点）。

- **触发动作**：`action:view_workbench`
- **payload**：`storeId`、`userId`、`ts`
- **证据**：`gap`（A 期可不实现上报）

## 智能预约助手 M0（Batch 0 · documented）

### event:booking_agent_started

智能预约 session 创建成功的首次事实。

- **触发动作**：`action:create_booking_agent_session`
- **幂等键**：`booking_agent_started:{sessionDbId}`
- **BusinessEvent**：`storeId=session.storeId`、`aggregateType=booking_agent_session`、`aggregateId=session.id`
- **metadata 白名单**：`entrySource`
- **证据**：`documented`

### event:booking_agent_draft_ready

session 首次进入 `confirmable` 的事实，不表示预约已创建。

- **触发动作**：`action:submit_booking_agent_message`
- **幂等键**：`booking_agent_draft_ready:{sessionDbId}`
- **metadata 白名单**：`inputModality`
- **证据**：`documented`

### event:booking_agent_fallback

用户带草稿返回普通表单，或 LLM 失败后进入降级的首次事实。

- **触发动作**：`action:handoff_booking_agent_draft`
- **幂等键**：`booking_agent_fallback:{sessionDbId}`
- **metadata 白名单**：`reason=user/llm_unavailable`
- **证据**：`documented`

M0 三个事件均禁止写入输入原文、转写文本、备注、宠物名、服务名、供应商 request ID 或错误原文；M0 不定义 `booking_agent_confirmed`。
