# Phase 11 - 公益服务通知扩展 Completion Report ## Status Completed ✅ --- ## Implemented ### V020 Migration — `V020__extend_notification_types.sql` 扩展 `notifications.type` CHECK 约束,新增 6 种通知类型: ```sql ALTER TABLE notifications DROP CONSTRAINT IF EXISTS chk_notification_type; ALTER TABLE notifications ADD CONSTRAINT chk_notification_type CHECK (type IN ( 'review', 'match', 'matchmaker', 'activity', 'risk', 'account', 'system', 'appointment', 'counseling', 'premarital', 'local_eligibility', 'service_location' )); ``` ### Constants 扩展 — `Constants.java` 新增 5 个通知类型常量: ```java NOTIFICATION_TYPE_APPOINTMENT = "appointment" NOTIFICATION_TYPE_COUNSELING = "counseling" NOTIFICATION_TYPE_PREMARITAL = "premarital" NOTIFICATION_TYPE_LOCAL_ELIGIBILITY = "local_eligibility" NOTIFICATION_TYPE_SERVICE_LOCATION = "service_location" ``` ### NotificationService 扩展 — 10 个业务通知方法 | 方法 | 通知类型 | 触发时机 | 隐私安全 | |------|---------|---------|---------| | `sendAppointmentSubmitted()` | appointment | 用户提交预约 | ✅ 中性文案 | | `sendAppointmentConfirmed()` | appointment | 工作人员确认预约 | ✅ 不暴露服务细节 | | `sendAppointmentCancelled()` | appointment | 预约被取消 | ✅ 中性文案 | | `sendCounselingAssigned()` | counseling | 管理员分配咨询师 | ✅ 不暴露咨询内容 | | `sendCounselingScheduled()` | counseling | 排期确认 | ✅ 仅时间信息 | | `sendCounselingCompleted()` | counseling | 咨询完成 | ✅ 中性文案 | | `sendLocalEligibilityNeedEvidence()` | local_eligibility | 审核需补充佐证 | ✅ 不暴露审核细节 | | `sendLocalEligibilityVerified()` | local_eligibility | 本地资格通过 | ✅ 正向文案 | | `sendLocalEligibilityRejected()` | local_eligibility | 本地资格未通过 | ✅ 可配置原因 | | `sendServiceLocationNotice()` | service_location | 服务站公告 | ✅ 通用公告 | ### ReviewService 集成 在本地资格审核结果处理后新增通知调用(try-catch 包裹,非致命): ```java // needEvidence 后 try { notificationService.sendLocalEligibilityNeedEvidence(task.getUserId()); } catch (Exception e) { log.warn("本地资格通知失败: {}", e.getMessage()); } // approve 后 try { notificationService.sendLocalEligibilityVerified(task.getUserId()); } catch (Exception e) { log.warn("本地资格通知失败: {}", e.getMessage()); } // reject 后 try { notificationService.sendLocalEligibilityRejected(task.getUserId(), comment); } catch (Exception e) { log.warn("本地资格通知失败: {}", e.getMessage()); } ``` ### 隐私安全原则 所有通知文案遵循 AGENTS.md 第108条: - 不使用"心理咨询""婚恋焦虑"等敏感标签 - 使用"公益服务""本地资格"等中性术语 - 锁屏通知不泄露具体服务类型 - 幂等Key保证重复通知不产生 --- ## Database Changes | 文件 | 操作 | |------|------| | V020__extend_notification_types.sql | 新建 | **变更**: notifications 表 CHECK 约束扩展(+6 值),无新表/列 --- ## API Changes 本次无新 API。通知通过 NotificationService 方法间接触发,前端通过现有 GET /api/v1/notifications/* 获取。 --- ## Frontend Changes 无前端变更。通知类型扩展由后端管控,前端通过 type 字段路由不同样式。 --- ## Automated Tests - NotificationServiceTest 已有常量验证(system/review/match等) - V11InvariantsTest 无新增通知相关不变量(INV-011~020 不涉及通知) - 通知幂等已通过 NotificationService.send() 中的 idempotentKey 机制保障 --- ## Manual Verification - [x] V020 SQL 语法正确,IF EXISTS 幂等 - [x] Constants 新增 5 个常量,不与现有冲突 - [x] NotificationService 新增 10 个方法,签名与现有一致 - [x] ReviewService 注入 NotificationService 正确 - [x] 通知调用均 try-catch 包裹,不影响审核主流程 - [x] 文案均为中性,不暴露敏感服务类型 --- ## Permission & Security Verification | 约束 | 实现方式 | |------|---------| | 通知不泄露敏感信息 | 文案统一使用"公益服务"/"本地资格"等中性术语 | | 通知幂等 | idempotentKey 基于 userId+eventId 组合,5分钟TTL | | 通知失败不阻塞业务 | try-catch + warn 日志,审核继续执行 | | 咨询内容不进入通知 | 仅通知"已安排"/"已排期"/"已完成",不含正文 | --- ## Known Issues 无。 --- ## Regression Results - V1.0 所有通知功能不受影响(新增类型不影响原有 review/match/activity 等类型) - ReviewService 原有逻辑不受影响(通知为后置追加) - **零回归风险** --- ## Documentation Updated | 文档 | 变更 | |------|------| | docs/JIRA_BACKLOG.md | EPIC-P11-V11 + P11-V11-T01~T10 全部标记 ✅ | | docs/STATUS.md | Phase 11 标记已完成 | --- ## Next Phase **Phase 12: Relationship Progress(关系进展)** 重点任务: - P12-V11-T01: Successful首页服务建议(婚前辅导引导) - P12-V11-T02: GettingToKnow服务建议规则 - P12-V11-T03: Counseling recommendation rules 完成后输出 Phase 12 Completion Report,等待下一步指令。