@ -11,7 +11,7 @@
{"id":"action:login","type":"Action","name":"login","nameZh":"手机号验证码登录","inputs":["phone","code"],"outputs":["entity:user","entity:store","session_token"],"entrypoints":["POST /api/user/login","UserService#login"],"appliesRules":["rule:BR-AUTH-001","rule:BR-AUTH-002"],"code":"backend/src/main/java/com/petstore/service/UserService.java#login","test":"UserServiceLoginStrategyTest","evidence":"anchored","domain":"auth"}
{"id":"action:login","type":"Action","name":"login","nameZh":"手机号验证码登录","inputs":["phone","code"],"outputs":["entity:user","entity:store","session_token"],"entrypoints":["POST /api/user/login","UserService#login"],"appliesRules":["rule:BR-AUTH-001","rule:BR-AUTH-002"],"code":"backend/src/main/java/com/petstore/service/UserService.java#login","test":"UserServiceLoginStrategyTest","evidence":"anchored","domain":"auth"}
{"id":"action:wx_phone_login","type":"Action","name":"wxPhoneLogin","nameZh":"微信手机号授权登录","inputs":["phoneCode","loginCode"],"outputs":["entity:user","entity:store","session_token"],"entrypoints":["POST /api/user/wx-phone-login","UserController#wxPhoneLogin","UserService#loginByVerifiedPhone"],"appliesRules":["rule:BR-AUTH-001"],"code":"backend/src/main/java/com/petstore/controller/UserController.java#wxPhoneLogin","evidence":"anchored","domain":"auth"}
{"id":"action:wx_phone_login","type":"Action","name":"wxPhoneLogin","nameZh":"微信手机号授权登录","inputs":["phoneCode","loginCode"],"outputs":["entity:user","entity:store","session_token"],"entrypoints":["POST /api/user/wx-phone-login","UserController#wxPhoneLogin","UserService#loginByVerifiedPhone"],"appliesRules":["rule:BR-AUTH-001"],"code":"backend/src/main/java/com/petstore/controller/UserController.java#wxPhoneLogin","evidence":"anchored","domain":"auth"}
{"id":"action:bind_wechat","type":"Action","name":"bindWechat","nameZh":"绑定微信标识","inputs":["userId","openid","unionid"],"outputs":["entity:user"],"entrypoints":["UserService#bindWechatMiniIdentity"],"appliesRules":[],"code":"backend/src/main/java/com/petstore/service/UserService.java#bindWechatMiniIdentity","evidence":"anchored","domain":"auth"}
{"id":"action:bind_wechat","type":"Action","name":"bindWechat","nameZh":"绑定微信标识","inputs":["userId","openid","unionid"],"outputs":["entity:user"],"entrypoints":["UserService#bindWechatMiniIdentity"],"appliesRules":[],"code":"backend/src/main/java/com/petstore/service/UserService.java#bindWechatMiniIdentity","evidence":"anchored","domain":"auth"}
{"id":"action:create_appointment","type":"Action","name":"createAppointment","nameZh":"创建预约","inputs":["petName","petType","serviceType","appointmentTime","storeId","remark","petId","customerUserId","customerPhone","customerName"],"outputs":["entity:appointment"],"entrypoints":["POST /api/appointment/create","AppointmentController#create","AppointmentService#createBooking"],"appliesRules":["rule:BR-APPT-001","rule:BR-APPT-003","rule:BR-APPT-004","rule:BR-SC-001","rule:BR-BE-001"],"code":"backend/src/main/java/com/petstore/controller/AppointmentController.java#create; backend/src/main/java/com/petstore/service/AppointmentService.java#createBooking; backend/src/main/java/com/petstore/service/StoreCustomerService.java#touchBooking; backend/src/main/java/com/petstore/service/BusinessEventService.java#recordAppointmentCreated","test":"AppointmentControllerIdentityTest; AppointmentServiceTest; StoreCustomerServiceTest; BusinessEventServiceTest","evidence":"anchored","domain":"appointment"}
{"id":"action:create_appointment","type":"Action","name":"createAppointment","nameZh":"创建预约","inputs":["petName","petType","serviceType","appointmentTime","storeId","remark","petId","customerUserId","customerPhone","customerName","followUpTaskId" ],"outputs":["entity:appointment","entity:follow_up_task "],"entrypoints":["POST /api/appointment/create","AppointmentController#create","AppointmentService#createBooking"],"appliesRules":["rule:BR-APPT-001","rule:BR-APPT-003","rule:BR-APPT-004","rule:BR-SC-001","rule:BR-BE-001","rule:BR-FU-002" ],"code":"backend/src/main/java/com/petstore/controller/AppointmentController.java#create; backend/src/main/java/com/petstore/service/AppointmentService.java#createBooking; backend/src/main/java/com/petstore/service/FollowUpTaskService.java#prepareRebook; #markRebooked; backend/src/main/java/com/petstore/service/ StoreCustomerService.java#touchBooking; backend/src/main/java/com/petstore/service/BusinessEventService.java#recordAppointmentCreated","test":"AppointmentControllerIdentityTest; AppointmentServiceTest; FollowUpTask ServiceTest; StoreCustomerServiceTest; BusinessEventServiceTest","evidence":"anchored","domain":"appointment"}
{"id":"action:start_service","type":"Action","name":"startService","nameZh":"开始服务","inputs":["appointmentId"],"outputs":["entity:appointment"],"entrypoints":["POST /api/appointment/start","AppointmentController#start","AppointmentService#startService"],"appliesRules":["rule:BR-APPT-001","rule:BR-APPT-002","rule:BR-BE-001"],"code":"backend/src/main/java/com/petstore/controller/AppointmentController.java#start; backend/src/main/java/com/petstore/service/BusinessEventService.java#recordAppointmentStatusChanged","test":"AppointmentServiceTest; BusinessEventServiceTest","evidence":"anchored","domain":"appointment"}
{"id":"action:start_service","type":"Action","name":"startService","nameZh":"开始服务","inputs":["appointmentId"],"outputs":["entity:appointment"],"entrypoints":["POST /api/appointment/start","AppointmentController#start","AppointmentService#startService"],"appliesRules":["rule:BR-APPT-001","rule:BR-APPT-002","rule:BR-BE-001"],"code":"backend/src/main/java/com/petstore/controller/AppointmentController.java#start; backend/src/main/java/com/petstore/service/BusinessEventService.java#recordAppointmentStatusChanged","test":"AppointmentServiceTest; BusinessEventServiceTest","evidence":"anchored","domain":"appointment"}
{"id":"action:transition_appointment_status","type":"Action","name":"transitionAppointmentStatus","nameZh":"更新预约状态","inputs":["id","status"],"outputs":["entity:appointment"],"entrypoints":["PUT /api/appointment/status","AppointmentController#updateStatus","AppointmentService#transitionStatus"],"appliesRules":["rule:BR-APPT-001","rule:BR-APPT-002","rule:BR-APPT-003","rule:BR-BE-001"],"code":"backend/src/main/java/com/petstore/controller/AppointmentController.java#updateStatus; backend/src/main/java/com/petstore/service/BusinessEventService.java#recordAppointmentStatusChanged","test":"AppointmentServiceTest; BusinessEventServiceTest","evidence":"anchored","domain":"appointment"}
{"id":"action:transition_appointment_status","type":"Action","name":"transitionAppointmentStatus","nameZh":"更新预约状态","inputs":["id","status"],"outputs":["entity:appointment"],"entrypoints":["PUT /api/appointment/status","AppointmentController#updateStatus","AppointmentService#transitionStatus"],"appliesRules":["rule:BR-APPT-001","rule:BR-APPT-002","rule:BR-APPT-003","rule:BR-BE-001"],"code":"backend/src/main/java/com/petstore/controller/AppointmentController.java#updateStatus; backend/src/main/java/com/petstore/service/BusinessEventService.java#recordAppointmentStatusChanged","test":"AppointmentServiceTest; BusinessEventServiceTest","evidence":"anchored","domain":"appointment"}
{"id":"action:get_appointment_detail","type":"Action","name":"getAppointmentDetail","nameZh":"预约详情","inputs":["id"],"outputs":["entity:appointment"],"entrypoints":["GET /api/appointment/detail","AppointmentController#detail"],"appliesRules":["rule:BR-APPT-002","rule:BR-APPT-003"],"code":"backend/src/main/java/com/petstore/controller/AppointmentController.java#detail","test":"AppointmentControllerDetailTest","evidence":"anchored","domain":"appointment"}
{"id":"action:get_appointment_detail","type":"Action","name":"getAppointmentDetail","nameZh":"预约详情","inputs":["id"],"outputs":["entity:appointment"],"entrypoints":["GET /api/appointment/detail","AppointmentController#detail"],"appliesRules":["rule:BR-APPT-002","rule:BR-APPT-003"],"code":"backend/src/main/java/com/petstore/controller/AppointmentController.java#detail","test":"AppointmentControllerDetailTest","evidence":"anchored","domain":"appointment"}
@ -21,8 +21,8 @@
{"id":"action:get_report_by_token","type":"Action","name":"getReportByToken","nameZh":"公开报告查询","inputs":["token"],"outputs":["entity:report"],"entrypoints":["GET /api/report/get","ReportController#getByAppointmentId"],"appliesRules":["rule:BR-RPT-006"],"code":"backend/src/main/java/com/petstore/controller/ReportController.java#getByAppointmentId","test":"ReportControllerTest#publicTokenQueryHidesInternalFields","evidence":"anchored","domain":"report"}
{"id":"action:get_report_by_token","type":"Action","name":"getReportByToken","nameZh":"公开报告查询","inputs":["token"],"outputs":["entity:report"],"entrypoints":["GET /api/report/get","ReportController#getByAppointmentId"],"appliesRules":["rule:BR-RPT-006"],"code":"backend/src/main/java/com/petstore/controller/ReportController.java#getByAppointmentId","test":"ReportControllerTest#publicTokenQueryHidesInternalFields","evidence":"anchored","domain":"report"}
{"id":"action:track_report_open","type":"Action","name":"trackReportOpen","nameZh":"报告打开埋点","inputs":["token","visitType"],"outputs":["entity:business_event"],"entrypoints":["POST /api/report/open-track","ReportController#trackReportOpen"],"appliesRules":["rule:BR-RPT-007","rule:BR-BE-001"],"code":"backend/src/main/java/com/petstore/controller/ReportController.java#trackReportOpen; backend/src/main/java/com/petstore/service/BusinessEventService.java#recordReportOpenByToken","test":"ReportControllerTest; BusinessEventServiceTest","evidence":"anchored","domain":"report"}
{"id":"action:track_report_open","type":"Action","name":"trackReportOpen","nameZh":"报告打开埋点","inputs":["token","visitType"],"outputs":["entity:business_event"],"entrypoints":["POST /api/report/open-track","ReportController#trackReportOpen"],"appliesRules":["rule:BR-RPT-007","rule:BR-BE-001"],"code":"backend/src/main/java/com/petstore/controller/ReportController.java#trackReportOpen; backend/src/main/java/com/petstore/service/BusinessEventService.java#recordReportOpenByToken","test":"ReportControllerTest; BusinessEventServiceTest","evidence":"anchored","domain":"report"}
{"id":"action:delete_report","type":"Action","name":"deleteReport","nameZh":"删除报告","inputs":["id"],"outputs":[],"entrypoints":["DELETE /api/report/delete","ReportController#delete"],"appliesRules":["rule:BR-RPT-005"],"code":"backend/src/main/java/com/petstore/controller/ReportController.java#delete","evidence":"anchored","domain":"report"}
{"id":"action:delete_report","type":"Action","name":"deleteReport","nameZh":"删除报告","inputs":["id"],"outputs":[],"entrypoints":["DELETE /api/report/delete","ReportController#delete"],"appliesRules":["rule:BR-RPT-005"],"code":"backend/src/main/java/com/petstore/controller/ReportController.java#delete","evidence":"anchored","domain":"report"}
{"id":"action:submit_lead","type":"Action","name":"submitLead","nameZh":"宠主留资","inputs":["token","phone","consent","loginCode"],"outputs":["leadId","remindDate","unsubscribeToken","wechatBound"],"entrypoints":["POST /api/report/{token}/reminder","ReportLeadController#submitReminder","ReportLeadService#submit"],"appliesRules":["rule:BR-LEAD-001","rule:BR-SC-001","rule:BR-BE-001"],"code":"backend/src/main/java/com/petstore/controller/ReportLeadController.java#submitReminder; backend/src/main/java/com/petstore/service/ReportLeadService.java#submit; backend/src/main/java/com/petstore/service/StoreCustomerService.java#touchLead; backend/src/main/java/com/petstore/service/BusinessEventService.java#recordLeadSubmitted","evidence":"anchored","test":"ReportLeadControllerTest; ReportLeadServiceStoreCustomerTest; StoreCustomerServiceTest; BusinessEventServiceTest","domain":"lead"}
{"id":"action:submit_lead","type":"Action","name":"submitLead","nameZh":"宠主留资","inputs":["token","phone","consent","loginCode"],"outputs":["leadId","remindDate","unsubscribeToken","wechatBound","entity:follow_up_task" ],"entrypoints":["POST /api/report/{token}/reminder","ReportLeadController#submitReminder","ReportLeadService#submit"],"appliesRules":["rule:BR-LEAD-001","rule:BR-SC-001","rule:BR-BE-001","rule:BR-FU-001" ],"code":"backend/src/main/java/com/petstore/controller/ReportLeadController.java#submitReminder; backend/src/main/java/com/petstore/service/ReportLeadService.java#submit; backend/src/main/java/com/petstore/service/FollowUpTaskService.java#ensureForLead; backend/src/main/java/com/petstore/service/ StoreCustomerService.java#touchLead; backend/src/main/java/com/petstore/service/BusinessEventService.java#recordLeadSubmitted","evidence":"anchored","test":"ReportLeadControllerTest; ReportLeadServiceStoreCustomerTest; FollowUpTaskService Test; StoreCustomerServiceTest; BusinessEventServiceTest","domain":"lead"}
{"id":"action:unsubscribe","type":"Action","name":"unsubscribe","nameZh":"一键退订","inputs":["unsubscribeToken"],"outputs":[],"entrypoints":["POST /api/report/unsubscribe","ReportLeadController#unsubscribe"],"appliesRules":["rule:BR-UNSUB-001"],"code":"backend/src/main/java/com/petstore/controller/ReportLeadController.java#unsubscribe","evidence":"anchored","domain":"lead","test":"ReportLeadControllerTest"}
{"id":"action:unsubscribe","type":"Action","name":"unsubscribe","nameZh":"一键退订","inputs":["unsubscribeToken"],"outputs":["entity:follow_up_task" ],"entrypoints":["POST /api/report/unsubscribe","ReportLeadController#unsubscribe"],"appliesRules":["rule:BR-UNSUB-001","rule:BR-FU-001" ],"code":"backend/src/main/java/com/petstore/controller/ReportLeadController.java#unsubscribe; backend/src/main/java/com/petstore/service/FollowUpTaskService.java#cancelForUnsubscribe ","evidence":"anchored","domain":"lead","test":"ReportLeadControllerTest; FollowUpTaskService Test"}
{"id":"action:list_leads","type":"Action","name":"listLeads","nameZh":"回访池列表","inputs":["status"],"outputs":["entity:report_lead"],"entrypoints":["GET /api/report/leads","ReportLeadController#leads"],"appliesRules":["rule:BR-LEAD-002","rule:BR-LEAD-003"],"code":"backend/src/main/java/com/petstore/controller/ReportLeadController.java#leads","evidence":"anchored","test":"ReportLeadControllerTest","domain":"lead"}
{"id":"action:list_leads","type":"Action","name":"listLeads","nameZh":"回访池列表","inputs":["status"],"outputs":["entity:report_lead"],"entrypoints":["GET /api/report/leads","ReportLeadController#leads"],"appliesRules":["rule:BR-LEAD-002","rule:BR-LEAD-003"],"code":"backend/src/main/java/com/petstore/controller/ReportLeadController.java#leads","evidence":"anchored","test":"ReportLeadControllerTest","domain":"lead"}
{"id":"event:store_registered","type":"Event","name":"StoreRegistered","nameZh":"门店入驻","domain":"store","payload":["storeId","bossUserId","occurredAt"],"emittedBy":["action:register_boss"],"code":"backend/src/main/java/com/petstore/service/BusinessEventService.java#recordStoreRegistered","test":"BusinessEventServiceTest","evidence":"anchored","note":"持久化 BusinessEvent 与 AuditLog, 均不含手机号或 legacy inviteCode"}
{"id":"event:store_registered","type":"Event","name":"StoreRegistered","nameZh":"门店入驻","domain":"store","payload":["storeId","bossUserId","occurredAt"],"emittedBy":["action:register_boss"],"code":"backend/src/main/java/com/petstore/service/BusinessEventService.java#recordStoreRegistered","test":"BusinessEventServiceTest","evidence":"anchored","note":"持久化 BusinessEvent 与 AuditLog, 均不含手机号或 legacy inviteCode"}
{"id":"event:session_issued","type":"Event","name":"SessionIssued","nameZh":"签发会话令牌","domain":"auth","payload":["userId","storeId","role","exp"],"emittedBy":["action:register_boss","action:login","action:wx_phone_login","action:accept_staff_invitation"],"code":"backend/src/main/java/com/petstore/auth/SessionTokenService.java#issue","test":"SessionTokenServiceTest","evidence":"anchored"}
{"id":"event:session_issued","type":"Event","name":"SessionIssued","nameZh":"签发会话令牌","domain":"auth","payload":["userId","storeId","role","exp"],"emittedBy":["action:register_boss","action:login","action:wx_phone_login","action:accept_staff_invitation"],"code":"backend/src/main/java/com/petstore/auth/SessionTokenService.java#issue","test":"SessionTokenServiceTest","evidence":"anchored"}
@ -36,7 +36,7 @@
{"id":"event:report_opened","type":"Event","name":"ReportOpened","nameZh":"报告首次打开","domain":"report","payload":["storeId","storeCustomerId","reportId","visitType","occurredAt"],"emittedBy":["action:track_report_open"],"appliesRules":["rule:BR-RPT-007","rule:BR-BE-001"],"code":"backend/src/main/java/com/petstore/controller/ReportController.java#trackReportOpen; backend/src/main/java/com/petstore/service/BusinessEventService.java#recordReportOpenByToken","doc":"docs/本期主线-下一步产品优化点.md 埋点事件 schema","test":"ReportControllerTest; BusinessEventServiceTest","evidence":"anchored"}
{"id":"event:report_opened","type":"Event","name":"ReportOpened","nameZh":"报告首次打开","domain":"report","payload":["storeId","storeCustomerId","reportId","visitType","occurredAt"],"emittedBy":["action:track_report_open"],"appliesRules":["rule:BR-RPT-007","rule:BR-BE-001"],"code":"backend/src/main/java/com/petstore/controller/ReportController.java#trackReportOpen; backend/src/main/java/com/petstore/service/BusinessEventService.java#recordReportOpenByToken","doc":"docs/本期主线-下一步产品优化点.md 埋点事件 schema","test":"ReportControllerTest; BusinessEventServiceTest","evidence":"anchored"}
{"id":"event:report_reopened","type":"Event","name":"ReportReopened","nameZh":"报告再次打开","domain":"report","payload":["storeId","storeCustomerId","reportId","visitType","occurredAt"],"emittedBy":["action:track_report_open"],"appliesRules":["rule:BR-RPT-007","rule:BR-BE-001"],"code":"backend/src/main/java/com/petstore/service/BusinessEventService.java#recordReportOpenByToken","doc":"docs/本期主线-下一步产品优化点.md 埋点事件 schema","test":"BusinessEventServiceTest#reportOpenStoresReportIdButNeverToken","evidence":"anchored"}
{"id":"event:report_reopened","type":"Event","name":"ReportReopened","nameZh":"报告再次打开","domain":"report","payload":["storeId","storeCustomerId","reportId","visitType","occurredAt"],"emittedBy":["action:track_report_open"],"appliesRules":["rule:BR-RPT-007","rule:BR-BE-001"],"code":"backend/src/main/java/com/petstore/service/BusinessEventService.java#recordReportOpenByToken","doc":"docs/本期主线-下一步产品优化点.md 埋点事件 schema","test":"BusinessEventServiceTest#reportOpenStoresReportIdButNeverToken","evidence":"anchored"}
{"id":"event:lead_submitted","type":"Event","name":"LeadSubmitted","nameZh":"留资提交","domain":"lead","payload":["storeId","storeCustomerId","leadId","repeatSubmit","occurredAt"],"emittedBy":["action:submit_lead"],"appliesRules":["rule:BR-LEAD-001","rule:BR-BE-001"],"code":"backend/src/main/java/com/petstore/service/ReportLeadService.java#submit; backend/src/main/java/com/petstore/service/BusinessEventService.java#recordLeadSubmitted","doc":"docs/本期主线-下一步产品优化点.md 埋点事件 schema","test":"ReportLeadServiceStoreCustomerTest; BusinessEventMigrationTest","evidence":"anchored"}
{"id":"event:lead_submitted","type":"Event","name":"LeadSubmitted","nameZh":"留资提交","domain":"lead","payload":["storeId","storeCustomerId","leadId","repeatSubmit","occurredAt"],"emittedBy":["action:submit_lead"],"appliesRules":["rule:BR-LEAD-001","rule:BR-BE-001"],"code":"backend/src/main/java/com/petstore/service/ReportLeadService.java#submit; backend/src/main/java/com/petstore/service/BusinessEventService.java#recordLeadSubmitted","doc":"docs/本期主线-下一步产品优化点.md 埋点事件 schema","test":"ReportLeadServiceStoreCustomerTest; BusinessEventMigrationTest","evidence":"anchored"}
{"id":"rule:BR-AUTH-001","type":"Rule","name":"HMAC session token + 活跃账号范围鉴权","domain":"auth","appliesTo":["action:update_store","action:delete_store","action:get_store_by_invite_code","action:create_appointment","action:start_service","action:transition_appointment_status","action:get_appointment_detail","action:list_appointments","action:submit_report","action:delete_report","action:list_leads","action:get_store_onboarding","action:complete_store_onboarding","action:create_staff_invitation","action:list_staff_invitations","action:revoke_staff_invitation","action:list_staff","action:delete_staff"],"code":"backend/src/main/java/com/petstore/auth/AuthInterceptor.java; SessionTokenService.java; CurrentUserContext.java","test":"SessionTokenServiceTest; AuthInterceptorActiveUserTest","evidence":"anchored","note":"除签名和过期外,每次受保护请求校验账号仍存在且 role/storeId 与 token 一致,门店角色还须归属有效 Store"}
{"id":"rule:BR-AUTH-001","type":"Rule","name":"HMAC session token + 活跃账号范围鉴权","domain":"auth","appliesTo":["action:update_store","action:delete_store","action:get_store_by_invite_code","action:create_appointment","action:start_service","action:transition_appointment_status","action:get_appointment_detail","action:list_appointments","action:submit_report","action:delete_report","action:list_leads","action:admin_list_leads","action:start_follow_up_task","action:reschedule_follow_up_task","action:close_follow_up_task","action: get_store_onboarding","action:complete_store_onboarding","action:create_staff_invitation","action:list_staff_invitations","action:revoke_staff_invitation","action:list_staff","action:delete_staff"],"code":"backend/src/main/java/com/petstore/auth/AuthInterceptor.java; SessionTokenService.java; CurrentUserContext.java","test":"SessionTokenServiceTest; AuthInterceptorActiveUserTest; AdminFollowUpTaskControll erTest","evidence":"anchored","note":"除签名和过期外,每次受保护请求校验账号仍存在且 role/storeId 与 token 一致,门店角色还须归属有效 Store"}
{"id":"rule:BR-AUTH-002","type":"Rule","name":"生产登录策略:仅微信授权","domain":"auth","appliesTo":["action:login","action:wx_phone_login"],"code":"backend/src/main/java/com/petstore/service/UserService.java#login; SmsController.java","test":"UserServiceLoginStrategyTest","evidence":"anchored"}
{"id":"rule:BR-AUTH-002","type":"Rule","name":"生产登录策略:仅微信授权","domain":"auth","appliesTo":["action:login","action:wx_phone_login"],"code":"backend/src/main/java/com/petstore/service/UserService.java#login; SmsController.java","test":"UserServiceLoginStrategyTest","evidence":"anchored"}
{"id":"rule:BR-STORE-001","type":"Rule","name":"店铺更新/删除仅 boss + 同店","domain":"store","appliesTo":["action:update_store","action:delete_store"],"code":"backend/src/main/java/com/petstore/controller/StoreController.java#update; #delete","test":"StoreControllerTest","evidence":"anchored"}
{"id":"rule:BR-STORE-001","type":"Rule","name":"店铺更新/删除仅 boss + 同店","domain":"store","appliesTo":["action:update_store","action:delete_store"],"code":"backend/src/main/java/com/petstore/controller/StoreController.java#update; #delete","test":"StoreControllerTest","evidence":"anchored"}
{"id":"rule:BR-STORE-002","type":"Rule","name":"legacy 永久邀请码禁用","domain":"store","appliesTo":["action:get_store_by_invite_code","action:register_staff"],"code":"backend/src/main/java/com/petstore/controller/StoreController.java#getByInviteCode; backend/src/main/java/com/petstore/controller/UserController.java#registerStaff","test":"StoreControllerTest","evidence":"anchored","note":"legacy invite_code 保留数据库兼容,不返回客户端、不再作为注册凭证"}
{"id":"rule:BR-STORE-002","type":"Rule","name":"legacy 永久邀请码禁用","domain":"store","appliesTo":["action:get_store_by_invite_code","action:register_staff"],"code":"backend/src/main/java/com/petstore/controller/StoreController.java#getByInviteCode; backend/src/main/java/com/petstore/controller/UserController.java#registerStaff","test":"StoreControllerTest","evidence":"anchored","note":"legacy invite_code 保留数据库兼容,不返回客户端、不再作为注册凭证"}
@ -54,7 +54,7 @@
{"id":"rule:BR-RPT-008","type":"Rule","name":"报告发送显式确认且首次回执不可覆盖","domain":"report","appliesTo":["action:confirm_report_sent"],"code":"backend/src/main/java/com/petstore/controller/ReportController.java#confirmSent; backend/src/main/java/com/petstore/service/ReportService.java#confirmSent; backend/src/main/java/com/petstore/mapper/ReportMapper.java#findByIdAndDeletedFalseForUpdate","test":"ReportControllerTest; ReportServiceTest; ReportSendMigrationTest","doc":"docs/架构决策-报告确认发送状态-2026-08-02.md","evidence":"anchored"}
{"id":"rule:BR-RPT-008","type":"Rule","name":"报告发送显式确认且首次回执不可覆盖","domain":"report","appliesTo":["action:confirm_report_sent"],"code":"backend/src/main/java/com/petstore/controller/ReportController.java#confirmSent; backend/src/main/java/com/petstore/service/ReportService.java#confirmSent; backend/src/main/java/com/petstore/mapper/ReportMapper.java#findByIdAndDeletedFalseForUpdate","test":"ReportControllerTest; ReportServiceTest; ReportSendMigrationTest","doc":"docs/架构决策-报告确认发送状态-2026-08-02.md","evidence":"anchored"}
{"id":"rule:BR-LEAD-001","type":"Rule","name":"留资去重","domain":"lead","appliesTo":["action:submit_lead"],"code":"backend/src/main/java/com/petstore/service/ReportLeadService.java#submit","evidence":"anchored","test":"ReportLeadControllerTest#submitLeadRepeatSubmitReturnsAlreadySubmitted"}
{"id":"rule:BR-LEAD-001","type":"Rule","name":"留资去重","domain":"lead","appliesTo":["action:submit_lead"],"code":"backend/src/main/java/com/petstore/service/ReportLeadService.java#submit","evidence":"anchored","test":"ReportLeadControllerTest#submitLeadRepeatSubmitReturnsAlreadySubmitted"}
{"id":"rule:BR-SC-001","type":"Rule","name":"门店客户主档唯一性与合并","domain":"admin","appliesTo":["action:create_appointment","action:submit_lead","action:admin_list_service_customers"],"code":"backend/src/main/java/com/petstore/service/StoreCustomerService.java","doc":"docs/ontology/rules.md#rule:BR-SC-001; docs/架构决策-门店客户主档-2026-08-01.md","test":"StoreCustomerServiceTest; ReportLeadServiceStoreCustomerTest; IdentityOwnershipQueryTest; StoreCustomerTimelineMigrationTest","evidence":"anchored","note":"被吸收投影软删除并指向同店 canonical; 既有 BusinessEvent 不改写"}
{"id":"rule:BR-SC-001","type":"Rule","name":"门店客户主档唯一性与合并","domain":"admin","appliesTo":["action:create_appointment","action:submit_lead","action:admin_list_service_customers"],"code":"backend/src/main/java/com/petstore/service/StoreCustomerService.java","doc":"docs/ontology/rules.md#rule:BR-SC-001; docs/架构决策-门店客户主档-2026-08-01.md","test":"StoreCustomerServiceTest; ReportLeadServiceStoreCustomerTest; IdentityOwnershipQueryTest; StoreCustomerTimelineMigrationTest","evidence":"anchored","note":"被吸收投影软删除并指向同店 canonical; 既有 BusinessEvent 不改写"}
{"id":"rule:BR-BE-001","type":"Rule","name":"业务事件不可变幂等与低敏","domain":"platform","appliesTo":["action:create_appointment","action:start_service","action:transition_appointment_status","action:submit_report","action:track_report_open","action:submit_lead","action:view_workbench"],"code":"backend/src/main/java/com/petstore/service/BusinessEventService.java; backend/src/main/java/com/petstore/service/AdminBusinessEventService.java","doc":"docs/ontology/rules.md#rule:BR-BE-001; docs/架构决策-业务事件落库-2026-08-01.md","test":"BusinessEventServiceTest; BusinessEventMigrationTest; AdminBusinessEventControllerTest","evidence":"anchored"}
{"id":"rule:BR-BE-001","type":"Rule","name":"业务事件不可变幂等与低敏","domain":"platform","appliesTo":["action:create_appointment","action:start_service","action:transition_appointment_status","action:submit_report","action:track_report_open","action:submit_lead","action:start_follow_up_task","action:reschedule_follow_up_task","action:close_follow_up_task","action:unsubscribe","action: view_workbench"],"code":"backend/src/main/java/com/petstore/service/BusinessEventService.java; backend/src/main/java/com/petstore/service/AdminBusinessEventService.java","doc":"docs/ontology/rules.md#rule:BR-BE-001; docs/架构决策-业务事件落库-2026-08-01.md","test":"BusinessEventServiceTest; BusinessEventMigrationTest; AdminBusinessEventControllerTest; FollowUpTaskService Test","evidence":"anchored"}
{"id":"rule:BR-LEAD-002","type":"Rule","name":"留资标识脱敏","domain":"lead","appliesTo":["action:list_leads"],"code":"backend/src/main/java/com/petstore/controller/ReportLeadController.java#maskIdentifier; #leads","frontend":"frontend/src/pages/mine/Leads.vue","evidence":"anchored"}
{"id":"rule:BR-LEAD-002","type":"Rule","name":"留资标识脱敏","domain":"lead","appliesTo":["action:list_leads"],"code":"backend/src/main/java/com/petstore/controller/ReportLeadController.java#maskIdentifier; #leads","frontend":"frontend/src/pages/mine/Leads.vue","evidence":"anchored"}
{"id":"rule:BR-LEAD-003","type":"Rule","name":"回访池跨店 403","domain":"lead","appliesTo":["action:list_leads"],"code":"backend/src/main/java/com/petstore/controller/ReportLeadController.java#leads","evidence":"anchored"}
{"id":"rule:BR-LEAD-003","type":"Rule","name":"回访池跨店 403","domain":"lead","appliesTo":["action:list_leads"],"code":"backend/src/main/java/com/petstore/controller/ReportLeadController.java#leads","evidence":"anchored"}
{"id":"rel:store_has_users","type":"Relation","subject":"entity:store","predicate":"HAS","object":"entity:user","cardinality":"1—N","evidence":"anchored","doc":"User.store_id → Store.id","domain":"store"}
{"id":"rel:store_has_users","type":"Relation","subject":"entity:store","predicate":"HAS","object":"entity:user","cardinality":"1—N","evidence":"anchored","doc":"User.store_id → Store.id","domain":"store"}
@ -139,7 +139,7 @@
{"id":"rule:BR-USER-002","type":"Rule","name":"用户更新仅本人","domain":"user","appliesTo":["action:update_user","action:get_user_info"],"code":"backend/src/main/java/com/petstore/controller/UserController.java#updateUser; #info","evidence":"anchored"}
{"id":"rule:BR-USER-002","type":"Rule","name":"用户更新仅本人","domain":"user","appliesTo":["action:update_user","action:get_user_info"],"code":"backend/src/main/java/com/petstore/controller/UserController.java#updateUser; #info","evidence":"anchored"}
{"id":"rule:BR-CONFIG-001","type":"Rule","name":"生产配置环境变量","domain":"config","appliesTo":[],"code":"backend/src/main/resources/application.yml; application-example.yml","doc":"backend/README.md 环境变量表","evidence":"anchored"}
{"id":"rule:BR-CONFIG-001","type":"Rule","name":"生产配置环境变量","domain":"config","appliesTo":[],"code":"backend/src/main/resources/application.yml; application-example.yml","doc":"backend/README.md 环境变量表","evidence":"anchored"}
{"id":"rule:BR-CONFIG-002","type":"Rule","name":"production profile 强制只读 schema 安全配置","domain":"config","appliesTo":[],"code":"backend/src/main/resources/application.yml production profile; backend/src/main/java/com/petstore/config/ProductionConfigurationValidator.java; backend/src/main/java/com/petstore/PetstoreApplication.java","doc":"backend/README.md production profile","test":"ProductionConfigurationValidatorTest","evidence":"anchored","note":"生产禁用自动建表/show-sql/SMS 万能码/默认数据初始化,并拒绝弱密钥、占位域名和宽 CORS"}
{"id":"rule:BR-CONFIG-002","type":"Rule","name":"production profile 强制只读 schema 安全配置","domain":"config","appliesTo":[],"code":"backend/src/main/resources/application.yml production profile; backend/src/main/java/com/petstore/config/ProductionConfigurationValidator.java; backend/src/main/java/com/petstore/PetstoreApplication.java","doc":"backend/README.md production profile","test":"ProductionConfigurationValidatorTest","evidence":"anchored","note":"生产禁用自动建表/show-sql/SMS 万能码/默认数据初始化,并拒绝弱密钥、占位域名和宽 CORS"}
{"id":"rule:BR-CONFIG-003","type":"Rule","name":"生产发布只读门禁","domain":"config","appliesTo":[],"code":"backend/src/main/java/com/petstore/config/ProductionDatabasePreflightRunner.java; backend/src/main/java/com/petstore/config/PetstoreRuntimeHealthIndicator.java; backend/deploy/release-preflight.sh; backend/deploy/production-smoke.sh","doc":"docs/生产发布与回滚Runbook-2026-08-01.md; docs/生产监控与告警基线-2026-08-01.md","evidence":"anchored","note":"备份和七迁移后运行 schema validate + 26 项只读不变量; readiness 覆盖 DB/磁盘/上传/FFmpeg; 自动 smoke 只读"}
{"id":"rule:BR-CONFIG-003","type":"Rule","name":"生产发布只读门禁","domain":"config","appliesTo":[],"code":"backend/src/main/java/com/petstore/config/ProductionDatabasePreflightRunner.java; backend/src/main/java/com/petstore/config/PetstoreRuntimeHealthIndicator.java; backend/deploy/release-preflight.sh; backend/deploy/production-smoke.sh","doc":"docs/生产发布与回滚Runbook-2026-08-01.md; docs/生产监控与告警基线-2026-08-01.md","evidence":"anchored","note":"备份和八迁移后运行 schema validate + 31 项只读不变量; readiness 覆盖 DB/磁盘/上传/FFmpeg; 自动 smoke 只读"}
{"id":"rel:user_owns_pets","type":"Relation","subject":"entity:user","predicate":"OWNS","object":"entity:pet","cardinality":"1—N","evidence":"anchored","doc":"Pet.owner_user_id → User.id","domain":"user"}
{"id":"rel:user_owns_pets","type":"Relation","subject":"entity:user","predicate":"OWNS","object":"entity:pet","cardinality":"1—N","evidence":"anchored","doc":"Pet.owner_user_id → User.id","domain":"user"}
{"id":"rel:pet_has_appointments","type":"Relation","subject":"entity:pet","predicate":"HAS","object":"entity:appointment","cardinality":"1—N","evidence":"anchored","doc":"Appointment.pet_id( 可空) ","domain":"pet"}
{"id":"rel:pet_has_appointments","type":"Relation","subject":"entity:pet","predicate":"HAS","object":"entity:appointment","cardinality":"1—N","evidence":"anchored","doc":"Appointment.pet_id( 可空) ","domain":"pet"}
{"id":"rel:store_has_service_types","type":"Relation","subject":"entity:store","predicate":"HAS","object":"entity:service_type","cardinality":"1—N","evidence":"anchored","doc":"ServiceType.store_id( NULL=系统默认)","domain":"store"}
{"id":"rel:store_has_service_types","type":"Relation","subject":"entity:store","predicate":"HAS","object":"entity:service_type","cardinality":"1—N","evidence":"anchored","doc":"ServiceType.store_id( NULL=系统默认)","domain":"store"}
@ -206,18 +206,18 @@
{"id":"rel:br_int_001_applies","type":"Relation","subject":"rule:BR-INT-001","predicate":"APPLIES_TO","object":"action:get_report_suggestion","domain":"service_type","evidence":"anchored"}
{"id":"rel:br_int_001_applies","type":"Relation","subject":"rule:BR-INT-001","predicate":"APPLIES_TO","object":"action:get_report_suggestion","domain":"service_type","evidence":"anchored"}
{"id":"rel:br_store_003_applies","type":"Relation","subject":"rule:BR-STORE-003","predicate":"APPLIES_TO","object":"action:get_store","domain":"store","evidence":"anchored"}
{"id":"rel:br_store_003_applies","type":"Relation","subject":"rule:BR-STORE-003","predicate":"APPLIES_TO","object":"action:get_store","domain":"store","evidence":"anchored"}
{"id":"entity:admin_console","type":"Entity","name":"AdminConsole","nameZh":"门店 Web 管理后台","domain":"admin","jpa":false,"fields":["surface","navModules","storeScope","roleGate"],"systems":["Store Admin FE","Backend Core"],"code":"","doc":"docs/ontology/objects.md#entity:admin_console","test":"","evidence":"documented","note":"Phase A 产品预录入;独立 Web 端,与 uni-app 小程序并存。对标宠老板「店铺概况」入口,不做收银/库存。详见 docs/门店管理后台升级方案-对标宠老板.md","status":"planned"}
{"id":"entity:admin_console","type":"Entity","name":"AdminConsole","nameZh":"门店 Web 管理后台","domain":"admin","jpa":false,"fields":["surface","navModules","storeScope","roleGate"],"systems":["Store Admin FE","Backend Core"],"code":"","doc":"docs/ontology/objects.md#entity:admin_console","test":"","evidence":"documented","note":"Phase A 产品预录入;独立 Web 端,与 uni-app 小程序并存。对标宠老板「店铺概况」入口,不做收银/库存。详见 docs/门店管理后台升级方案-对标宠老板.md","status":"planned"}
{"id":"entity:workbench","type":"Entity","name":"Workbench","nameZh":"今日行动工作台","domain":"admin","jpa":false,"fields":["todayAppointmentCount","todayDoneCount","overdueAppointmentCount","inServiceCount","upcomingTodayCount","highlightAnomalyCount","unsentReportCount","dueFollowUpCount","actionGroups","reportFunnel"],"systems":["Store Admin FE","Backend Core","Report Media Backend"],"code":"admin/src/views/WorkbenchView.vue; backend/src/main/java/com/petstore/service/AdminWorkbenchService.java; backend/src/main/java/com/petstore/service/AdminBusinessEventService.java","doc":"docs/ontology/objects.md#entity:workbench","test":"AdminWorkbenchServiceTest; AdminBusinessEventServiceTest","evidence":"anchored","note":"服务端按完整本店数据准确聚合行动;每组最多 3 条低敏预览;新报告待发送有行动组,发送漏斗为真实显式确认,仅转预约未知 。","status":"implemented"}
{"id":"entity:workbench","type":"Entity","name":"Workbench","nameZh":"今日行动工作台","domain":"admin","jpa":false,"fields":["todayAppointmentCount","todayDoneCount","overdueAppointmentCount","inServiceCount","upcomingTodayCount","highlightAnomalyCount","unsentReportCount","dueFollowUpCount","actionGroups","reportFunnel"],"systems":["Store Admin FE","Backend Core","Report Media Backend"],"code":"admin/src/views/WorkbenchView.vue; backend/src/main/java/com/petstore/service/AdminWorkbenchService.java; backend/src/main/java/com/petstore/service/AdminBusinessEventService.java","doc":"docs/ontology/objects.md#entity:workbench","test":"AdminWorkbenchServiceTest; AdminBusinessEventServiceTest","evidence":"anchored","note":"服务端按完整本店数据准确聚合行动;回访待办来自 FollowUpTask; 每组最多 3 条低敏预览;报告发送和回访后真实再次预约均来自持久事件 。","status":"implemented"}
{"id":"entity:workbench_todo_item","type":"Entity","name":"WorkbenchTodoItem","nameZh":"工作台行动组","domain":"admin","jpa":false,"fields":["kind","urgency","count","items","drilldownAction","filter"],"systems":["Store Admin FE","Backend Core"],"code":"backend/src/main/java/com/petstore/service/AdminWorkbenchService.java; admin/src/views/WorkbenchView.vue","doc":"docs/ontology/objects.md#entity:workbench_todo_item","test":"AdminWorkbenchServiceTest","evidence":"anchored","note":"kind: overdue_appointment | in_service | upcoming_today | highlight_anomaly | unsent_report | follow_up_due; items 不含手机号/token/媒体 URL","status":"implemented"}
{"id":"entity:workbench_todo_item","type":"Entity","name":"WorkbenchTodoItem","nameZh":"工作台行动组","domain":"admin","jpa":false,"fields":["kind","urgency","count","items","drilldownAction","filter"],"systems":["Store Admin FE","Backend Core"],"code":"backend/src/main/java/com/petstore/service/AdminWorkbenchService.java; admin/src/views/WorkbenchView.vue","doc":"docs/ontology/objects.md#entity:workbench_todo_item","test":"AdminWorkbenchServiceTest","evidence":"anchored","note":"kind: overdue_appointment | in_service | upcoming_today | highlight_anomaly | unsent_report | follow_up_due; items 不含手机号/token/媒体 URL","status":"implemented"}
{"id":"entity:store_customer","type":"Entity","name":"StoreCustomer","nameZh":"门店客户主档(瘦身)","domain":"admin","jpa":true,"fields":["id","store_id","customer_user_id","phone","display_name","source","first_contact_at","last_contact_at","merged_into_store_customer_id","create_time","update_time","deleted"],"systems":["Store Admin FE","Backend Core","Report Media Backend"],"code":"backend/src/main/java/com/petstore/entity/StoreCustomer.java; backend/src/main/java/com/petstore/service/StoreCustomerService.java; backend/src/main/java/com/petstore/service/AdminServiceCustomerService.java","doc":"docs/ontology/objects.md#entity:store_customer; docs/架构决策-门店客户主档-2026-08-01.md","test":"StoreCustomerServiceTest; AdminServiceCustomerServiceTest; IdentityOwnershipQueryTest; StoreCustomerTimelineMigrationTest","evidence":"anchored","note":"门店与客户的稳定关系实体;被吸收投影以同店 canonical 指针保留不可变事件连续性;不含余额/次卡/积分。","status":"implemented"}
{"id":"entity:store_customer","type":"Entity","name":"StoreCustomer","nameZh":"门店客户主档(瘦身)","domain":"admin","jpa":true,"fields":["id","store_id","customer_user_id","phone","display_name","source","first_contact_at","last_contact_at","merged_into_store_customer_id","create_time","update_time","deleted"],"systems":["Store Admin FE","Backend Core","Report Media Backend"],"code":"backend/src/main/java/com/petstore/entity/StoreCustomer.java; backend/src/main/java/com/petstore/service/StoreCustomerService.java; backend/src/main/java/com/petstore/service/AdminServiceCustomerService.java","doc":"docs/ontology/objects.md#entity:store_customer; docs/架构决策-门店客户主档-2026-08-01.md","test":"StoreCustomerServiceTest; AdminServiceCustomerServiceTest; IdentityOwnershipQueryTest; StoreCustomerTimelineMigrationTest","evidence":"anchored","note":"门店与客户的稳定关系实体;被吸收投影以同店 canonical 指针保留不可变事件连续性;不含余额/次卡/积分。","status":"implemented"}
{"id":"rule:BR-ADMIN-001","type":"Rule","name":"adminRoleGate","nameZh":"门店后台角色门禁( boss/staff 可读写)","domain":"admin","appliesTo":["action:view_workbench","action:admin_list_appointments","action:admin_list_reports","action:admin_list_leads","action:admin_list_service_customers","action:admin_update_settings"],"code":"","doc":"docs/ontology/rules.md#rule:BR-ADMIN-001; docs/门店管理后台-PhaseA-页面清单PRD.md","test":"","evidence":"document ed","note":"boss 与 staff 均可进入 admin_console 并编辑本店数据; customer 禁止。删店/部分设置仍仅 boss( BR-STORE-001/BR-USER-001) 。产品决策 2026-07-10。"}
{"id":"rule:BR-ADMIN-001","type":"Rule","name":"adminRoleGate","nameZh":"门店后台角色门禁( boss/staff 可读写)","domain":"admin","appliesTo":["action:view_workbench","action:admin_list_appointments","action:admin_list_reports","action:admin_list_leads","action:start_follow_up_task","action:reschedule_follow_up_task","action:close_follow_up_task","action: admin_list_service_customers","action:admin_update_settings"],"code":"backend/src/main/java/com/petstore/controller/AdminFollowUpTaskController.java ","doc":"docs/ontology/rules.md#rule:BR-ADMIN-001; docs/门店管理后台-PhaseA-页面清单PRD.md","test":"AdminFollowUpTaskControllerTest","evidence":"anchor ed","note":"boss 与 staff 均可进入 admin_console 并编辑本店数据; customer 禁止。删店/部分设置仍仅 boss( BR-STORE-001/BR-USER-001) 。产品决策 2026-07-10。"}
{"id":"rule:BR-ADMIN-002","type":"Rule","name":"adminStoreScope","nameZh":"后台数据仅本店 storeId","domain":"admin","appliesTo":["action:view_workbench","action:admin_list_appointments","action:admin_list_reports","action:admin_list_leads","action:admin_list_service_customers","action:admin_update_settings"],"code":"","doc":"docs/ontology/rules.md#rule:BR-ADMIN-002","test":"","evidence":"document ed","note":"所有后台查询/写操作强制 current.storeId; 忽略跨店请求参数。连锁同步不在 Phase A。"}
{"id":"rule:BR-ADMIN-002","type":"Rule","name":"adminStoreScope","nameZh":"后台数据仅本店 storeId","domain":"admin","appliesTo":["action:view_workbench","action:admin_list_appointments","action:admin_list_reports","action:admin_list_leads","action:start_follow_up_task","action:reschedule_follow_up_task","action:close_follow_up_task","action: admin_list_service_customers","action:admin_update_settings"],"code":"backend/src/main/java/com/petstore/controller/AdminFollowUpTaskController.java; backend/src/main/java/com/petstore/service/FollowUpTaskService.java ","doc":"docs/ontology/rules.md#rule:BR-ADMIN-002","test":"AdminFollowUpTaskControllerTest; FollowUpTaskServiceTest","evidence":"anchor ed","note":"所有后台查询/写操作强制 current.storeId; 忽略跨店请求参数。连锁同步不在 Phase A。"}
{"id":"rule:BR-ADMIN-003","type":"Rule","name":"adminInternalFieldsAllowed","nameZh":"后台内部字段按用途最小返回","domain":"admin","appliesTo":["action:admin_list_reports","action:admin_update_settings","action:admin_list_service_customers","action:list_staff_invitations"],"code":"","doc":"docs/ontology/rules.md#rule:BR-ADMIN-003","test":"","evidence":"documented","note":"后台可返回报告内部状态; legacy inviteCode 不再返回。员工邀请列表仅返回脱敏手机号且不返回 token/hash, 原始 token 仅创建响应返回一次。"}
{"id":"rule:BR-ADMIN-003","type":"Rule","name":"adminInternalFieldsAllowed","nameZh":"后台内部字段按用途最小返回","domain":"admin","appliesTo":["action:admin_list_reports","action:admin_update_settings","action:admin_list_service_customers","action:list_staff_invitations"],"code":"","doc":"docs/ontology/rules.md#rule:BR-ADMIN-003","test":"","evidence":"documented","note":"后台可返回报告内部状态; legacy inviteCode 不再返回。员工邀请列表仅返回脱敏手机号且不返回 token/hash, 原始 token 仅创建响应返回一次。"}
{"id":"rule:BR-ADMIN-004","type":"Rule","name":"adminPhaseAOutOfScope","nameZh":"Phase A 后台范围闸门","domain":"admin","appliesTo":["entity:admin_console","entity:workbench"],"code":"","doc":"docs/ontology/rules.md#rule:BR-ADMIN-004","test":"","evidence":"documented","note":"禁止将 membership/stored_value/service_package/收银/库存/寄养/押金/点餐/支付宝渠道 写入 Phase A 后台范围或设计稿。上述保持 evidence=gap。"}
{"id":"rule:BR-ADMIN-004","type":"Rule","name":"adminPhaseAOutOfScope","nameZh":"Phase A 后台范围闸门","domain":"admin","appliesTo":["entity:admin_console","entity:workbench"],"code":"","doc":"docs/ontology/rules.md#rule:BR-ADMIN-004","test":"","evidence":"documented","note":"禁止将 membership/stored_value/service_package/收银/库存/寄养/押金/点餐/支付宝渠道 写入 Phase A 后台范围或设计稿。上述保持 evidence=gap。"}
{"id":"action:view_workbench","type":"Action","name":"viewWorkbench","nameZh":"查看今日行动工作台","domain":"admin","inputs":["date","entity:current_user"],"outputs":["entity:workbench","entity:workbench_todo_item"],"entrypoints":["GET /api/admin/workbench/actions","GET /api/admin/workbench/report-funnel","Admin UI /workbench"],"appliesRules":["rule:BR-ADMIN-001","rule:BR-ADMIN-002","rule:BR-ADMIN-004","rule:BR-BE-001"],"code":"backend/src/main/java/com/petstore/controller/AdminBusinessEventController.java#actions; #reportFunnel; backend/src/main/java/com/petstore/service/AdminWorkbenchService.java; backend/src/main/java/com/petstore/service/AdminBusinessEventService.java; admin/src/views/WorkbenchView.vue","doc":"docs/ontology/actions.md#action:view_workbench","test":"AdminBusinessEventControllerTest; AdminWorkbenchServiceTest; AdminBusinessEventServiceTest","evidence":"anchored","note":"按会话 storeId 聚合完整本店行动与低敏预览;事件漏斗按日期汇总。"}
{"id":"action:view_workbench","type":"Action","name":"viewWorkbench","nameZh":"查看今日行动工作台","domain":"admin","inputs":["date","entity:current_user"],"outputs":["entity:workbench","entity:workbench_todo_item"],"entrypoints":["GET /api/admin/workbench/actions","GET /api/admin/workbench/report-funnel","Admin UI /workbench"],"appliesRules":["rule:BR-ADMIN-001","rule:BR-ADMIN-002","rule:BR-ADMIN-004","rule:BR-BE-001"],"code":"backend/src/main/java/com/petstore/controller/AdminBusinessEventController.java#actions; #reportFunnel; backend/src/main/java/com/petstore/service/AdminWorkbenchService.java; backend/src/main/java/com/petstore/service/AdminBusinessEventService.java; admin/src/views/WorkbenchView.vue","doc":"docs/ontology/actions.md#action:view_workbench","test":"AdminBusinessEventControllerTest; AdminWorkbenchServiceTest; AdminBusinessEventServiceTest","evidence":"anchored","note":"按会话 storeId 聚合完整本店行动与低敏预览;事件漏斗按日期汇总。"}
{"id":"action:drilldown_workbench_todo","type":"Action","name":"drilldownWorkbenchTodo","nameZh":"工作台行动下钻","domain":"admin","inputs":["entity:workbench_todo_item"],"outputs":[],"entrypoints":["Admin UI workbench action click"],"appliesRules":["rule:BR-ADMIN-001","rule:BR-ADMIN-002"],"code":"admin/src/views/WorkbenchView.vue; admin/src/views/AppointmentsView.vue","doc":"docs/ontology/actions.md#action:drilldown_workbench_todo","evidence":"anchored","note":"按 kind 跳转 appointments/reports/leads 并带筛选;预约预览可按受保护 ID 详情打开。"}
{"id":"action:drilldown_workbench_todo","type":"Action","name":"drilldownWorkbenchTodo","nameZh":"工作台行动下钻","domain":"admin","inputs":["entity:workbench_todo_item"],"outputs":[],"entrypoints":["Admin UI workbench action click"],"appliesRules":["rule:BR-ADMIN-001","rule:BR-ADMIN-002"],"code":"admin/src/views/WorkbenchView.vue; admin/src/views/AppointmentsView.vue","doc":"docs/ontology/actions.md#action:drilldown_workbench_todo","evidence":"anchored","note":"按 kind 跳转 appointments/reports/leads 并带筛选;预约预览可按受保护 ID 详情打开。"}
{"id":"action:admin_list_appointments","type":"Action","name":"adminListAppointments","nameZh":"后台预约列表","domain":"admin","inputs":["status","dateFrom","dateTo","assignedUserId","page","pageSize"],"outputs":["entity:appointment"],"entrypoints":["Admin UI /appointments (planned)","reuses GET /api/appointment/list"],"appliesRules":["rule:BR-ADMIN-001","rule:BR-ADMIN-002","rule:BR-APPT-001","rule:BR-APPT-002"],"code":"backend/.../AppointmentController.java#list (reuse)","doc":"docs/ontology/actions.md#action:admin_list_appointments","evidence":"documented","note":"Web 列表增强筛选;业务规则复用现有 Appointment*, 不另造状态机。; Web 允许调用 start_service( 产品决策 2026-07-10) "}
{"id":"action:admin_list_appointments","type":"Action","name":"adminListAppointments","nameZh":"后台预约列表","domain":"admin","inputs":["status","dateFrom","dateTo","assignedUserId","page","pageSize"],"outputs":["entity:appointment"],"entrypoints":["Admin UI /appointments (planned)","reuses GET /api/appointment/list"],"appliesRules":["rule:BR-ADMIN-001","rule:BR-ADMIN-002","rule:BR-APPT-001","rule:BR-APPT-002"],"code":"backend/.../AppointmentController.java#list (reuse)","doc":"docs/ontology/actions.md#action:admin_list_appointments","evidence":"documented","note":"Web 列表增强筛选;业务规则复用现有 Appointment*, 不另造状态机。; Web 允许调用 start_service( 产品决策 2026-07-10) "}
{"id":"action:admin_list_reports","type":"Action","name":"adminListReports","nameZh":"后台报告列表","domain":"admin","inputs":["dateFrom","dateTo","highlightStatus","page","pageSize"],"outputs":["entity:report","entity:highlight_video"],"entrypoints":["Admin UI /reports (planned)","reuses GET /api/report/list"],"appliesRules":["rule:BR-ADMIN-001","rule:BR-ADMIN-002","rule:BR-ADMIN-003","rule:BR-RPT-005"],"code":"backend/.../ReportController.java#list (reuse)","doc":"docs/ontology/actions.md#action:admin_list_reports","evidence":"documented","note":"含成片三态、复制公开链接、触发重生成(复用 generate_highlight) 。"}
{"id":"action:admin_list_reports","type":"Action","name":"adminListReports","nameZh":"后台报告列表","domain":"admin","inputs":["dateFrom","dateTo","highlightStatus","page","pageSize"],"outputs":["entity:report","entity:highlight_video"],"entrypoints":["Admin UI /reports (planned)","reuses GET /api/report/list"],"appliesRules":["rule:BR-ADMIN-001","rule:BR-ADMIN-002","rule:BR-ADMIN-003","rule:BR-RPT-005"],"code":"backend/.../ReportController.java#list (reuse)","doc":"docs/ontology/actions.md#action:admin_list_reports","evidence":"documented","note":"含成片三态、复制公开链接、触发重生成(复用 generate_highlight) 。"}
{"id":"action:admin_list_leads","type":"Action","name":"adminListLeads","nameZh":"后台回访线索列表","domain":"admin","inputs":["remindStatus","remindDateTo","page","pageSize"],"outputs":["entity:report_lead"],"entrypoints":["Admin UI /leads (planned)","reuses GET /api/report/leads"],"appliesRules":["rule:BR-ADMIN-001","rule:BR-ADMIN-002","rule:BR-LEAD-002","rule:BR-LEAD-003"],"code":"backend/.../ReportLeadController.java#leads (reuse)","doc":"docs/ontology/actions.md#action:admin_list_leads","evidence":"document ed"}
{"id":"action:admin_list_leads","type":"Action","name":"adminListLeads","nameZh":"后台回访任务池","domain":"admin","inputs":["status","dueDateTo","page","pageSize"],"outputs":["entity:follow_up_task"],"entrypoints":["GET /api/admin/follow-up-tasks","AdminFollowUpTaskController#list","Admin UI /leads"],"appliesRules":["rule:BR-AUTH-001","rule:BR-ADMIN-001","rule:BR-ADMIN-002","rule:BR-FU-001"],"code":"backend/src/main/java/com/petstore/controller/AdminFollowUpTaskController.java#list; backend/src/main/java/com/petstore/service/FollowUpTaskService.java#list; admin/src/views/LeadsView.vue","doc":"docs/ontology/actions.md#action:admin_list_leads; docs/架构决策-FollowUpTask回访状态机与再次预约归因-2026-08-02.md","test":"AdminFollowUpTaskControllerTest; FollowUpTaskServiceTest","evidence":"anchor ed"}
{"id":"action:admin_list_service_customers","type":"Action","name":"adminListServiceCustomers","nameZh":"后台服务客户列表","domain":"admin","inputs":["source","hasPendingLead","lastVisitFrom","lastVisitTo","q","page","pageSize"],"outputs":["entity:store_customer"],"entrypoints":["GET /api/admin/service-customers","Admin UI /customers"],"appliesRules":["rule:BR-ADMIN-001","rule:BR-ADMIN-002","rule:BR-ADMIN-003","rule:BR-ADMIN-004","rule:BR-SC-001"],"code":"backend/src/main/java/com/petstore/controller/AdminServiceCustomerController.java#listServiceCustomers; backend/src/main/java/com/petstore/service/AdminServiceCustomerService.java","doc":"docs/ontology/actions.md#action:admin_list_service_customers; docs/门店管理后台-PhaseA-页面清单PRD.md §7.6","test":"AdminServiceCustomerControllerTest; AdminServiceCustomerServiceTest","evidence":"anchored","note":"以 t_store_customer 稳定主档为入口,动态聚合宠物/到店/报告/留资;返回 storeCustomerId 和脱敏手机号。"}
{"id":"action:admin_list_service_customers","type":"Action","name":"adminListServiceCustomers","nameZh":"后台服务客户列表","domain":"admin","inputs":["source","hasPendingLead","lastVisitFrom","lastVisitTo","q","page","pageSize"],"outputs":["entity:store_customer"],"entrypoints":["GET /api/admin/service-customers","Admin UI /customers"],"appliesRules":["rule:BR-ADMIN-001","rule:BR-ADMIN-002","rule:BR-ADMIN-003","rule:BR-ADMIN-004","rule:BR-SC-001"],"code":"backend/src/main/java/com/petstore/controller/AdminServiceCustomerController.java#listServiceCustomers; backend/src/main/java/com/petstore/service/AdminServiceCustomerService.java","doc":"docs/ontology/actions.md#action:admin_list_service_customers; docs/门店管理后台-PhaseA-页面清单PRD.md §7.6","test":"AdminServiceCustomerControllerTest; AdminServiceCustomerServiceTest","evidence":"anchored","note":"以 t_store_customer 稳定主档为入口,动态聚合宠物/到店/报告/留资;返回 storeCustomerId 和脱敏手机号。"}
{"id":"action:admin_update_settings","type":"Action","name":"adminUpdateSettings","nameZh":"后台门店设置读写","domain":"admin","inputs":["entity:store","entity:service_type","entity:user"],"outputs":["entity:store","entity:service_type","entity:user","entity:audit_log"],"entrypoints":["GET /api/admin/store","Admin UI /settings","PUT /api/store/update"],"appliesRules":["rule:BR-ADMIN-001","rule:BR-ADMIN-002","rule:BR-ADMIN-003","rule:BR-STORE-001","rule:BR-ST-001","rule:BR-USER-001","rule:BR-AUDIT-001"],"code":"","doc":"docs/ontology/actions.md#action:admin_update_settings","evidence":"anchored","note":"后台不再读取 legacy inviteCode; 写资料复用 PUT /api/store/update 并写低敏审计;员工改走限时邀请。","code_refs":["backend/src/main/java/com/petstore/controller/AdminStoreController.java#currentStore","backend/src/main/java/com/petstore/controller/StoreController.java#update"]}
{"id":"action:admin_update_settings","type":"Action","name":"adminUpdateSettings","nameZh":"后台门店设置读写","domain":"admin","inputs":["entity:store","entity:service_type","entity:user"],"outputs":["entity:store","entity:service_type","entity:user","entity:audit_log"],"entrypoints":["GET /api/admin/store","Admin UI /settings","PUT /api/store/update"],"appliesRules":["rule:BR-ADMIN-001","rule:BR-ADMIN-002","rule:BR-ADMIN-003","rule:BR-STORE-001","rule:BR-ST-001","rule:BR-USER-001","rule:BR-AUDIT-001"],"code":"","doc":"docs/ontology/actions.md#action:admin_update_settings","evidence":"anchored","note":"后台不再读取 legacy inviteCode; 写资料复用 PUT /api/store/update 并写低敏审计;员工改走限时邀请。","code_refs":["backend/src/main/java/com/petstore/controller/AdminStoreController.java#currentStore","backend/src/main/java/com/petstore/controller/StoreController.java#update"]}
{"id":"action:admin_manage_schedule","type":"Action","name":"adminManageSchedule","nameZh":"后台排班与占用","domain":"admin","inputs":["date","entity:schedule_block"],"outputs":["entity:schedule_block","entity:appointment"],"entrypoints":["Admin UI /schedule","GET /api/schedule/day","POST /api/schedule/block","DELETE /api/schedule/block"],"appliesRules":["rule:BR-ADMIN-001","rule:BR-ADMIN-002","rule:BR-SCH-001","rule:BR-SCH-002","rule:BR-APPT-004"],"code":"","doc":"docs/ontology/actions.md#action:admin_manage_schedule","evidence":"anchored","note":"Web 排班日视图展示容量占用;增删带时长占用复用 ScheduleController。"}
{"id":"action:admin_manage_schedule","type":"Action","name":"adminManageSchedule","nameZh":"后台排班与占用","domain":"admin","inputs":["date","entity:schedule_block"],"outputs":["entity:schedule_block","entity:appointment"],"entrypoints":["Admin UI /schedule","GET /api/schedule/day","POST /api/schedule/block","DELETE /api/schedule/block"],"appliesRules":["rule:BR-ADMIN-001","rule:BR-ADMIN-002","rule:BR-SCH-001","rule:BR-SCH-002","rule:BR-APPT-004"],"code":"","doc":"docs/ontology/actions.md#action:admin_manage_schedule","evidence":"anchored","note":"Web 排班日视图展示容量占用;增删带时长占用复用 ScheduleController。"}
@ -226,7 +226,7 @@
{"id":"rel:workbench_has_todo_items","type":"Relation","subject":"entity:workbench","predicate":"HAS","object":"entity:workbench_todo_item","domain":"admin","evidence":"anchored","cardinality":"1—N","doc":"按优先级排序的行动组"}
{"id":"rel:workbench_has_todo_items","type":"Relation","subject":"entity:workbench","predicate":"HAS","object":"entity:workbench_todo_item","domain":"admin","evidence":"anchored","cardinality":"1—N","doc":"按优先级排序的行动组"}
{"id":"rel:workbench_reads_appointments","type":"Relation","subject":"entity:workbench","predicate":"AGGREGATES","object":"entity:appointment","domain":"admin","evidence":"documented","cardinality":"1—N","doc":"今日预约指标"}
{"id":"rel:workbench_reads_appointments","type":"Relation","subject":"entity:workbench","predicate":"AGGREGATES","object":"entity:appointment","domain":"admin","evidence":"documented","cardinality":"1—N","doc":"今日预约指标"}
{"id":"rel:workbench_reads_reports","type":"Relation","subject":"entity:workbench","predicate":"AGGREGATES","object":"entity:report","domain":"admin","evidence":"documented","cardinality":"1—N","doc":"待出报告/成片异常"}
{"id":"rel:workbench_reads_reports","type":"Relation","subject":"entity:workbench","predicate":"AGGREGATES","object":"entity:report","domain":"admin","evidence":"documented","cardinality":"1—N","doc":"待出报告/成片异常"}
{"id":"rel:workbench_reads_leads","type":"Relation","subject":"entity:workbench","predicate":"AGGREGATES","object":"entity:report_lead","domain":"admin","evidence":"documented","cardinality":"1—N","doc":"待回访 "}
{"id":"rel:workbench_reads_leads","type":"Relation","subject":"entity:workbench","predicate":"AGGREGATES","object":"entity:follow_up_task","domain":"admin","evidence":"anchored","cardinality":"1—N","doc":"到期开放回访任务 "}
{"id":"rel:workbench_reads_business_events","type":"Relation","subject":"entity:workbench","predicate":"AGGREGATES","object":"entity:business_event","domain":"admin","evidence":"anchored","cardinality":"1—N","doc":"报告提交/打开/留资去重漏斗"}
{"id":"rel:workbench_reads_business_events","type":"Relation","subject":"entity:workbench","predicate":"AGGREGATES","object":"entity:business_event","domain":"admin","evidence":"anchored","cardinality":"1—N","doc":"报告提交/打开/留资去重漏斗"}
{"id":"rel:store_has_service_customers","type":"Relation","subject":"entity:store","predicate":"HAS","object":"entity:store_customer","domain":"admin","evidence":"anchored","cardinality":"1—N","doc":"StoreCustomer.store_id"}
{"id":"rel:store_has_service_customers","type":"Relation","subject":"entity:store","predicate":"HAS","object":"entity:store_customer","domain":"admin","evidence":"anchored","cardinality":"1—N","doc":"StoreCustomer.store_id"}
{"id":"rel:service_customer_is_user","type":"Relation","subject":"entity:store_customer","predicate":"LINKS","object":"entity:user","domain":"admin","evidence":"anchored","cardinality":"N—0..1","doc":"StoreCustomer.customer_user_id; 留资客户可先为空"}
{"id":"rel:service_customer_is_user","type":"Relation","subject":"entity:store_customer","predicate":"LINKS","object":"entity:user","domain":"admin","evidence":"anchored","cardinality":"N—0..1","doc":"StoreCustomer.customer_user_id; 留资客户可先为空"}
@ -237,7 +237,7 @@
{"id":"rel:br_admin_004_gates_console","type":"Relation","subject":"rule:BR-ADMIN-004","predicate":"CONSTRAINS","object":"entity:admin_console","domain":"admin","evidence":"documented","doc":"Phase A 范围闸门"}
{"id":"rel:br_admin_004_gates_console","type":"Relation","subject":"rule:BR-ADMIN-004","predicate":"CONSTRAINS","object":"entity:admin_console","domain":"admin","evidence":"documented","doc":"Phase A 范围闸门"}
{"id":"rel:admin_list_appts_reads_appointment","type":"Relation","subject":"action:admin_list_appointments","predicate":"READS","object":"entity:appointment","domain":"admin","evidence":"documented"}
{"id":"rel:admin_list_appts_reads_appointment","type":"Relation","subject":"action:admin_list_appointments","predicate":"READS","object":"entity:appointment","domain":"admin","evidence":"documented"}
{"id":"rel:admin_list_reports_reads_report","type":"Relation","subject":"action:admin_list_reports","predicate":"READS","object":"entity:report","domain":"admin","evidence":"documented"}
{"id":"rel:admin_list_reports_reads_report","type":"Relation","subject":"action:admin_list_reports","predicate":"READS","object":"entity:report","domain":"admin","evidence":"documented"}
{"id":"rel:admin_list_leads_reads_lead","type":"Relation","subject":"action:admin_list_leads","predicate":"READS","object":"entity:report_lead","domain":"admin","evidence":"document ed"}
{"id":"rel:admin_list_leads_reads_lead","type":"Relation","subject":"action:admin_list_leads","predicate":"READS","object":"entity:follow_up_task","domain":"admin","evidence":"anchor ed"}
{"id":"rel:admin_list_customers_reads_sc","type":"Relation","subject":"action:admin_list_service_customers","predicate":"READS","object":"entity:store_customer","domain":"admin","evidence":"anchored"}
{"id":"rel:admin_list_customers_reads_sc","type":"Relation","subject":"action:admin_list_service_customers","predicate":"READS","object":"entity:store_customer","domain":"admin","evidence":"anchored"}
{"id":"rel:drilldown_todo_to_list_appts","type":"Relation","subject":"action:drilldown_workbench_todo","predicate":"ROUTES_TO","object":"action:admin_list_appointments","domain":"admin","evidence":"anchored","doc":"kind=overdue_appointment|in_service|upcoming_today; 按 ID 或服务端口径筛选"}
{"id":"rel:drilldown_todo_to_list_appts","type":"Relation","subject":"action:drilldown_workbench_todo","predicate":"ROUTES_TO","object":"action:admin_list_appointments","domain":"admin","evidence":"anchored","doc":"kind=overdue_appointment|in_service|upcoming_today; 按 ID 或服务端口径筛选"}
{"id":"entity:staff_invitation","type":"Entity","name":"StaffInvitation","nameZh":"一次性员工邀请","domain":"user","jpa":true,"fields":["id","invitation_id","token_hash","store_id","invited_name","invited_phone","status","expires_at","created_by_user_id","accepted_by_user_id","accepted_at","revoked_by_user_id","revoked_at","create_time","update_time"],"systems":["Backend Core","Store Miniapp FE","Store Admin FE"],"code":"backend/src/main/java/com/petstore/entity/StaffInvitation.java; backend/src/main/java/com/petstore/service/StaffInvitationService.java","doc":"docs/ontology/objects.md#entity:staff_invitation; docs/架构决策-门店开通与员工邀请-2026-08-02.md","test":"StaffInvitationServiceTest; StoreOnboardingMigrationTest","evidence":"anchored","note":"原始 token 只在创建响应返回一次;库内仅 SHA-256; 列表/预览只返回 maskedPhone"}
{"id":"entity:staff_invitation","type":"Entity","name":"StaffInvitation","nameZh":"一次性员工邀请","domain":"user","jpa":true,"fields":["id","invitation_id","token_hash","store_id","invited_name","invited_phone","status","expires_at","created_by_user_id","accepted_by_user_id","accepted_at","revoked_by_user_id","revoked_at","create_time","update_time"],"systems":["Backend Core","Store Miniapp FE","Store Admin FE"],"code":"backend/src/main/java/com/petstore/entity/StaffInvitation.java; backend/src/main/java/com/petstore/service/StaffInvitationService.java","doc":"docs/ontology/objects.md#entity:staff_invitation; docs/架构决策-门店开通与员工邀请-2026-08-02.md","test":"StaffInvitationServiceTest; StoreOnboardingMigrationTest","evidence":"anchored","note":"原始 token 只在创建响应返回一次;库内仅 SHA-256; 列表/预览只返回 maskedPhone"}
@ -256,10 +256,35 @@
{"id":"rel:store_has_staff_invitations","type":"Relation","subject":"entity:store","predicate":"HAS","object":"entity:staff_invitation","cardinality":"1—N","domain":"user","evidence":"anchored","doc":"StaffInvitation.store_id"}
{"id":"rel:store_has_staff_invitations","type":"Relation","subject":"entity:store","predicate":"HAS","object":"entity:staff_invitation","cardinality":"1—N","domain":"user","evidence":"anchored","doc":"StaffInvitation.store_id"}
{"id":"rel:store_has_audit_logs","type":"Relation","subject":"entity:store","predicate":"HAS","object":"entity:audit_log","cardinality":"1—N","domain":"platform","evidence":"anchored","doc":"AuditLog.store_id"}
{"id":"rel:store_has_audit_logs","type":"Relation","subject":"entity:store","predicate":"HAS","object":"entity:audit_log","cardinality":"1—N","domain":"platform","evidence":"anchored","doc":"AuditLog.store_id"}
{"id":"rel:staff_invitation_creates_user","type":"Relation","subject":"action:accept_staff_invitation","predicate":"CREATES","object":"entity:user","domain":"user","evidence":"anchored","doc":"仅微信核验手机号匹配且无账号冲突时创建 staff"}
{"id":"rel:staff_invitation_creates_user","type":"Relation","subject":"action:accept_staff_invitation","predicate":"CREATES","object":"entity:user","domain":"user","evidence":"anchored","doc":"仅微信核验手机号匹配且无账号冲突时创建 staff"}
{"id":"entity:customer_timeline","type":"Entity","name":"CustomerTimeline","nameZh":"门店客户事实时间线","domain":"admin","jpa":false,"fields":["customer","total","page","pageSize","hasMore","list[eventId,eventType,occurredAt,title,description,actorRole,actorName,appointment?,report?,lead?]"],"systems":["Store Admin FE","Backend Core"],"code":"backend/src/main/java/com/petstore/service/StoreCustomerTimelineService.java; admin/src/views/CustomersView.vue","doc":"docs/ontology/objects.md#entity:customer_timeline; docs/架构决策-StoreCustomer事实时间线-2026-08-02.md","test":"StoreCustomerTimelineServiceTest; AdminServiceCustomerControllerTest","evidence":"anchored","note":"由 BusinessEvent 只读投影; 不返回手机号原文、备注、媒体、token 或原始 metadata"}
{"id":"entity:customer_timeline","type":"Entity","name":"CustomerTimeline","nameZh":"门店客户事实时间线","domain":"admin","jpa":false,"fields":["customer","total","page","pageSize","hasMore","list[eventId,eventType,occurredAt,title,description,actorRole,actorName,appointment?,report?,lead?,followUpTask? ]"],"systems":["Store Admin FE","Backend Core"],"code":"backend/src/main/java/com/petstore/service/StoreCustomerTimelineService.java; admin/src/views/CustomersView.vue","doc":"docs/ontology/objects.md#entity:customer_timeline; docs/架构决策-StoreCustomer事实时间线-2026-08-02.md","test":"StoreCustomerTimelineServiceTest; AdminServiceCustomerControllerTest","evidence":"anchored","note":"由 BusinessEvent 只读投影; FollowUpTask 仅低敏摘要; 不返回手机号原文、备注、媒体、token 或原始 metadata"}
{"id":"action:view_store_customer_timeline","type":"Action","name":"viewStoreCustomerTimeline","nameZh":"查看门店客户事实时间线","domain":"admin","inputs":["storeCustomerId","page","pageSize","entity:current_user"],"outputs":["entity:customer_timeline"],"entrypoints":["GET /api/admin/service-customers/timeline","AdminServiceCustomerController#timeline","StoreCustomerTimelineService#timeline","Admin UI /customers drawer"],"appliesRules":["rule:BR-AUTH-001","rule:BR-ADMIN-001","rule:BR-ADMIN-002","rule:BR-ADMIN-003","rule:BR-SC-001","rule:BR-SC-002","rule:BR-BE-001"],"code":"backend/src/main/java/com/petstore/controller/AdminServiceCustomerController.java#timeline; backend/src/main/java/com/petstore/service/StoreCustomerTimelineService.java; admin/src/views/CustomersView.vue","doc":"docs/ontology/actions.md#action:view_store_customer_timeline; docs/架构决策-StoreCustomer事实时间线-2026-08-02.md","test":"StoreCustomerTimelineServiceTest; AdminServiceCustomerControllerTest","evidence":"anchored"}
{"id":"action:view_store_customer_timeline","type":"Action","name":"viewStoreCustomerTimeline","nameZh":"查看门店客户事实时间线","domain":"admin","inputs":["storeCustomerId","page","pageSize","entity:current_user"],"outputs":["entity:customer_timeline"],"entrypoints":["GET /api/admin/service-customers/timeline","AdminServiceCustomerController#timeline","StoreCustomerTimelineService#timeline","Admin UI /customers drawer"],"appliesRules":["rule:BR-AUTH-001","rule:BR-ADMIN-001","rule:BR-ADMIN-002","rule:BR-ADMIN-003","rule:BR-SC-001","rule:BR-SC-002","rule:BR-BE-001"],"code":"backend/src/main/java/com/petstore/controller/AdminServiceCustomerController.java#timeline; backend/src/main/java/com/petstore/service/StoreCustomerTimelineService.java; admin/src/views/CustomersView.vue","doc":"docs/ontology/actions.md#action:view_store_customer_timeline; docs/架构决策-StoreCustomer事实时间线-2026-08-02.md","test":"StoreCustomerTimelineServiceTest; AdminServiceCustomerControllerTest","evidence":"anchored"}
{"id":"rule:BR-SC-002","type":"Rule","name":"lowSensitivityCustomerTimelineProjection","nameZh":"客户时间线同店低敏事实投影","domain":"admin","appliesTo":["action:view_store_customer_timeline"],"code":"backend/src/main/java/com/petstore/service/StoreCustomerTimelineService.java; backend/src/main/java/com/petstore/controller/AdminServiceCustomerController.java","doc":"docs/ontology/rules.md#rule:BR-SC-002; docs/架构决策-StoreCustomer事实时间线-2026-08-02.md","test":"StoreCustomerTimelineServiceTest; AdminServiceCustomerControllerTest; StoreCustomerTimelineMigrationTest","evidence":"anchored","note":"以 current.storeId 校验并解析 canonical + 同店别名; BusinessEvent 按 occurredAt/id 稳定倒序分页;仅白名单摘要,不返回原始 metadata/PII/凭证/备注/媒体"}
{"id":"rule:BR-SC-002","type":"Rule","name":"lowSensitivityCustomerTimelineProjection","nameZh":"客户时间线同店低敏事实投影","domain":"admin","appliesTo":["action:view_store_customer_timeline"],"code":"backend/src/main/java/com/petstore/service/StoreCustomerTimelineService.java; backend/src/main/java/com/petstore/controller/AdminServiceCustomerController.java","doc":"docs/ontology/rules.md#rule:BR-SC-002; docs/架构决策-StoreCustomer事实时间线-2026-08-02.md","test":"StoreCustomerTimelineServiceTest; AdminServiceCustomerControllerTest; StoreCustomerTimelineMigrationTest","evidence":"anchored","note":"以 current.storeId 校验并解析 canonical + 同店别名; BusinessEvent 按 occurredAt/id 稳定倒序分页;仅白名单摘要,不返回原始 metadata/PII/凭证/备注/媒体"}
{"id":"rel:store_customer_has_timeline","type":"Relation","subject":"entity:store_customer","predicate":"HAS_VIEW","object":"entity:customer_timeline","cardinality":"1—1","domain":"admin","evidence":"anchored","doc":"按稳定 StoreCustomer.id 生成只读时间线"}
{"id":"rel:store_customer_has_timeline","type":"Relation","subject":"entity:store_customer","predicate":"HAS_VIEW","object":"entity:customer_timeline","cardinality":"1—1","domain":"admin","evidence":"anchored","doc":"按稳定 StoreCustomer.id 生成只读时间线"}
{"id":"rel:business_events_project_to_customer_timeline","type":"Relation","subject":"entity:business_event","predicate":"PROJECTS_TO","object":"entity:customer_timeline","cardinality":"N—1","domain":"admin","evidence":"anchored","doc":"仅 store_customer_id 归属且通过白名单转换的事实"}
{"id":"rel:business_events_project_to_customer_timeline","type":"Relation","subject":"entity:business_event","predicate":"PROJECTS_TO","object":"entity:customer_timeline","cardinality":"N—1","domain":"admin","evidence":"anchored","doc":"仅 store_customer_id 归属且通过白名单转换的事实"}
{"id":"rel:view_customer_timeline_reads_store_customer","type":"Relation","subject":"action:view_store_customer_timeline","predicate":"READS","object":"entity:store_customer","domain":"admin","evidence":"anchored"}
{"id":"rel:view_customer_timeline_reads_store_customer","type":"Relation","subject":"action:view_store_customer_timeline","predicate":"READS","object":"entity:store_customer","domain":"admin","evidence":"anchored"}
{"id":"rel:view_customer_timeline_reads_business_events","type":"Relation","subject":"action:view_store_customer_timeline","predicate":"READS","object":"entity:business_event","domain":"admin","evidence":"anchored"}
{"id":"rel:view_customer_timeline_reads_business_events","type":"Relation","subject":"action:view_store_customer_timeline","predicate":"READS","object":"entity:business_event","domain":"admin","evidence":"anchored"}
{"id":"entity:follow_up_task","type":"Entity","name":"FollowUpTask","nameZh":"门店回访执行任务","domain":"lead","jpa":true,"fields":["id","task_id","store_id","store_customer_id","source_lead_id","source_report_id","status","outcome","due_date","assigned_user_id","attempt_count","last_attempt_outcome","last_contacted_at","last_contacted_by_user_id","closed_at","closed_by_user_id","rebooked_appointment_id","create_time","update_time","version"],"systems":["Backend Core","Store Admin FE"],"code":"backend/src/main/java/com/petstore/entity/FollowUpTask.java; backend/src/main/java/com/petstore/service/FollowUpTaskService.java; admin/src/views/LeadsView.vue","doc":"docs/ontology/objects.md#entity:follow_up_task; docs/架构决策-FollowUpTask回访状态机与再次预约归因-2026-08-02.md","test":"FollowUpTaskServiceTest; FollowUpTaskMigrationTest; AdminFollowUpTaskControllerTest","evidence":"anchored","note":"ReportLead 保留同意与来源事实;本实体承载领取、改期、终态和真实预约引用;同一 sourceLeadId 至多一个开放任务"}
{"id":"action:start_follow_up_task","type":"Action","name":"startFollowUpTask","nameZh":"领取回访任务","domain":"lead","inputs":["taskId","entity:current_user"],"outputs":["entity:follow_up_task"],"entrypoints":["POST /api/admin/follow-up-tasks/{taskId}/start","AdminFollowUpTaskController#start","FollowUpTaskService#start"],"appliesRules":["rule:BR-AUTH-001","rule:BR-ADMIN-001","rule:BR-ADMIN-002","rule:BR-FU-001","rule:BR-BE-001"],"code":"backend/src/main/java/com/petstore/controller/AdminFollowUpTaskController.java#start; backend/src/main/java/com/petstore/service/FollowUpTaskService.java#start","doc":"docs/ontology/actions.md#action:start_follow_up_task","test":"AdminFollowUpTaskControllerTest; FollowUpTaskServiceTest","evidence":"anchored"}
{"id":"action:reschedule_follow_up_task","type":"Action","name":"rescheduleFollowUpTask","nameZh":"登记回访未完成并改期","domain":"lead","inputs":["taskId","reason","dueDate","entity:current_user"],"outputs":["entity:follow_up_task"],"entrypoints":["POST /api/admin/follow-up-tasks/{taskId}/reschedule","AdminFollowUpTaskController#reschedule","FollowUpTaskService#reschedule"],"appliesRules":["rule:BR-AUTH-001","rule:BR-ADMIN-001","rule:BR-ADMIN-002","rule:BR-FU-001","rule:BR-BE-001"],"code":"backend/src/main/java/com/petstore/controller/AdminFollowUpTaskController.java#reschedule; backend/src/main/java/com/petstore/service/FollowUpTaskService.java#reschedule","doc":"docs/ontology/actions.md#action:reschedule_follow_up_task","test":"AdminFollowUpTaskControllerTest; FollowUpTaskServiceTest","evidence":"anchored"}
{"id":"action:close_follow_up_task","type":"Action","name":"closeFollowUpTask","nameZh":"关闭回访任务","domain":"lead","inputs":["taskId","outcome","entity:current_user"],"outputs":["entity:follow_up_task"],"entrypoints":["POST /api/admin/follow-up-tasks/{taskId}/close","AdminFollowUpTaskController#close","FollowUpTaskService#close"],"appliesRules":["rule:BR-AUTH-001","rule:BR-ADMIN-001","rule:BR-ADMIN-002","rule:BR-FU-001","rule:BR-FU-002","rule:BR-BE-001"],"code":"backend/src/main/java/com/petstore/controller/AdminFollowUpTaskController.java#close; backend/src/main/java/com/petstore/service/FollowUpTaskService.java#close","doc":"docs/ontology/actions.md#action:close_follow_up_task","test":"AdminFollowUpTaskControllerTest; FollowUpTaskServiceTest","evidence":"anchored","note":"仅 not_interested/invalid_contact; rebooked 不可手工选择"}
{"id":"event:follow_up_created","type":"Event","name":"FollowUpCreated","nameZh":"回访任务已创建","domain":"lead","payload":["storeId","storeCustomerId","aggregateId","dueDate","occurredAt"],"emittedBy":["action:submit_lead"],"appliesRules":["rule:BR-FU-001","rule:BR-BE-001"],"code":"backend/src/main/java/com/petstore/service/FollowUpTaskService.java#ensureForLead; backend/src/main/java/com/petstore/service/BusinessEventService.java#recordFollowUpCreated","test":"FollowUpTaskServiceTest; FollowUpTaskMigrationTest; BusinessEventServiceTest","evidence":"anchored"}
{"id":"event:follow_up_started","type":"Event","name":"FollowUpStarted","nameZh":"员工已开始回访","domain":"lead","payload":["storeId","storeCustomerId","aggregateId","actorUserId","actorRole","occurredAt"],"emittedBy":["action:start_follow_up_task"],"appliesRules":["rule:BR-FU-001","rule:BR-BE-001"],"code":"backend/src/main/java/com/petstore/service/FollowUpTaskService.java#start; backend/src/main/java/com/petstore/service/BusinessEventService.java#recordFollowUpStarted","test":"FollowUpTaskServiceTest; BusinessEventServiceTest","evidence":"anchored"}
{"id":"event:follow_up_rescheduled","type":"Event","name":"FollowUpRescheduled","nameZh":"回访已改期","domain":"lead","payload":["storeId","storeCustomerId","aggregateId","attemptOutcome","attemptCount","dueDate","occurredAt"],"emittedBy":["action:reschedule_follow_up_task"],"appliesRules":["rule:BR-FU-001","rule:BR-BE-001"],"code":"backend/src/main/java/com/petstore/service/FollowUpTaskService.java#reschedule; backend/src/main/java/com/petstore/service/BusinessEventService.java#recordFollowUpRescheduled","test":"FollowUpTaskServiceTest; BusinessEventServiceTest","evidence":"anchored"}
{"id":"event:follow_up_completed","type":"Event","name":"FollowUpCompleted","nameZh":"回访任务已完成","domain":"lead","payload":["storeId","storeCustomerId","aggregateId","outcome","attemptCount","occurredAt"],"emittedBy":["action:close_follow_up_task","action:create_appointment"],"appliesRules":["rule:BR-FU-001","rule:BR-FU-002","rule:BR-BE-001"],"code":"backend/src/main/java/com/petstore/service/FollowUpTaskService.java#close; #markRebooked; backend/src/main/java/com/petstore/service/BusinessEventService.java#recordFollowUpCompleted","test":"FollowUpTaskServiceTest; AppointmentServiceTest; BusinessEventServiceTest","evidence":"anchored"}
{"id":"event:follow_up_canceled","type":"Event","name":"FollowUpCanceled","nameZh":"回访任务已取消","domain":"lead","payload":["storeId","storeCustomerId","aggregateId","outcome","occurredAt"],"emittedBy":["action:unsubscribe"],"appliesRules":["rule:BR-FU-001","rule:BR-BE-001"],"code":"backend/src/main/java/com/petstore/service/FollowUpTaskService.java#cancelForUnsubscribe; backend/src/main/java/com/petstore/service/BusinessEventService.java#recordFollowUpCanceled","test":"FollowUpTaskServiceTest; BusinessEventServiceTest","evidence":"anchored"}
{"id":"event:rebook_created","type":"Event","name":"RebookCreated","nameZh":"回访后真实再次预约已创建","domain":"lead","payload":["storeId","storeCustomerId","aggregateId","bookingOrigin","occurredAt"],"emittedBy":["action:create_appointment"],"appliesRules":["rule:BR-FU-002","rule:BR-BE-001"],"code":"backend/src/main/java/com/petstore/service/FollowUpTaskService.java#markRebooked; backend/src/main/java/com/petstore/service/BusinessEventService.java#recordRebookCreated","test":"AppointmentServiceTest; BusinessEventServiceTest; AdminBusinessEventServiceTest","evidence":"anchored"}
{"id":"rule:BR-FU-001","type":"Rule","name":"followUpTaskStateAndOwnership","nameZh":"回访任务状态机与同店领取权","domain":"lead","appliesTo":["action:submit_lead","action:unsubscribe","action:admin_list_leads","action:start_follow_up_task","action:reschedule_follow_up_task","action:close_follow_up_task"],"code":"backend/src/main/java/com/petstore/service/FollowUpTaskService.java; backend/src/main/java/com/petstore/controller/AdminFollowUpTaskController.java","doc":"docs/ontology/rules.md#rule:BR-FU-001; docs/架构决策-FollowUpTask回访状态机与再次预约归因-2026-08-02.md","test":"FollowUpTaskServiceTest; AdminFollowUpTaskControllerTest; FollowUpTaskMigrationTest","evidence":"anchored","note":"pending 无领取人; in_progress 必须有且仅领取人可操作;终态不回退;退订不增加联系次数;跨店 404"}
{"id":"rule:BR-FU-002","type":"Rule","name":"realRebookAttribution","nameZh":"真实再次预约归因","domain":"lead","appliesTo":["action:create_appointment","action:close_follow_up_task"],"code":"backend/src/main/java/com/petstore/service/AppointmentService.java#createBooking; backend/src/main/java/com/petstore/service/FollowUpTaskService.java#prepareRebook; #markRebooked","doc":"docs/ontology/rules.md#rule:BR-FU-002; docs/架构决策-FollowUpTask回访状态机与再次预约归因-2026-08-02.md","test":"AppointmentServiceTest; FollowUpTaskServiceTest; AdminBusinessEventServiceTest","evidence":"anchored","note":"rebooked 不可手工选择;仅同店同 canonical 客户的真实预约保存成功后同事务闭环;容量失败不闭环"}
{"id":"rel:lead_has_follow_up_tasks","type":"Relation","subject":"entity:report_lead","predicate":"HAS","object":"entity:follow_up_task","cardinality":"1—N","domain":"lead","evidence":"anchored","doc":"同一留资可有多个终结轮次,但至多一个开放任务"}
{"id":"rel:store_customer_has_follow_up_tasks","type":"Relation","subject":"entity:store_customer","predicate":"HAS","object":"entity:follow_up_task","cardinality":"1—N","domain":"lead","evidence":"anchored","doc":"FollowUpTask.store_customer_id 指向同店 canonical 主档"}
{"id":"rel:follow_up_task_rebooks_appointment","type":"Relation","subject":"entity:follow_up_task","predicate":"REBOOKS","object":"entity:appointment","cardinality":"N—0..1","domain":"lead","evidence":"anchored","doc":"仅真实预约保存成功后写 rebooked_appointment_id"}
{"id":"rel:submit_lead_creates_follow_up_task","type":"Relation","subject":"action:submit_lead","predicate":"CREATES","object":"entity:follow_up_task","domain":"lead","evidence":"anchored","doc":"开放任务存在时复用;前序终态后创建新轮次"}
{"id":"rel:submit_lead_emits_follow_up_created","type":"Relation","subject":"action:submit_lead","predicate":"EMITS","object":"event:follow_up_created","domain":"lead","evidence":"anchored"}
{"id":"rel:start_follow_up_emits_started","type":"Relation","subject":"action:start_follow_up_task","predicate":"EMITS","object":"event:follow_up_started","domain":"lead","evidence":"anchored"}
{"id":"rel:reschedule_follow_up_emits_rescheduled","type":"Relation","subject":"action:reschedule_follow_up_task","predicate":"EMITS","object":"event:follow_up_rescheduled","domain":"lead","evidence":"anchored"}
{"id":"rel:close_follow_up_emits_completed","type":"Relation","subject":"action:close_follow_up_task","predicate":"EMITS","object":"event:follow_up_completed","domain":"lead","evidence":"anchored"}
{"id":"rel:unsubscribe_emits_follow_up_canceled","type":"Relation","subject":"action:unsubscribe","predicate":"EMITS","object":"event:follow_up_canceled","domain":"lead","evidence":"anchored"}
{"id":"rel:create_appointment_emits_follow_up_completed","type":"Relation","subject":"action:create_appointment","predicate":"EMITS","object":"event:follow_up_completed","domain":"lead","evidence":"anchored","doc":"仅携带已领取任务且预约保存成功"}
{"id":"rel:create_appointment_emits_rebook_created","type":"Relation","subject":"action:create_appointment","predicate":"EMITS","object":"event:rebook_created","domain":"lead","evidence":"anchored"}
{"id":"rel:br_fu_001_applies_follow_up","type":"Relation","subject":"rule:BR-FU-001","predicate":"APPLIES_TO","object":"action:start_follow_up_task","domain":"lead","evidence":"anchored"}
{"id":"rel:br_fu_002_applies_create_appointment","type":"Relation","subject":"rule:BR-FU-002","predicate":"APPLIES_TO","object":"action:create_appointment","domain":"lead","evidence":"anchored"}