- 基准:《善缘丹阳 V1.1 红娘角色权限正式决策与开发规则》(DEC-MATCHMAKER-001~006 全部 LOCKED,A1/B1 已确认) - 审计范围:backend matchmaker/referral/audit/security/auth、Flyway V001~V052、 admin-web 红娘页面、现有测试、治理文档;全部结论带文件:行号证据 - 核心结论:A1 分配关系未建模(分配=工单指派)、claim/followup/complete 无归属校验、 /pending 全局队列对全体红娘可见、无红娘组长角色、模块内越权拒绝不落审计 - P0×5 / P1×6 / P2×6 差距清单 + 可复用模块(CounselingService INV-012 范式、 DataScopeGuard、recordSensitiveAccess)+ ZC-MM-002~007 实施顺序建议 - 首次建立 docs/audits/ 目录
31 KiB
红娘角色权限差距审计(ZC-MM-001)
审计基准:《善缘丹阳 V1.1 红娘角色权限正式决策与开发规则》V1.1(DEC-MATCHMAKER-001
006 全部 LOCKED,A1/B1 已确认) 审计日期:2026-10-09 审计执行:架构师(高见远)· software-danyang-mm 团队 · 只读走查,未修改任何业务代码 审计方式:backend matchmaker/referral/audit/security/auth 模块、Flyway V001V052、admin-web 红娘相关页面、现有测试与治理文档。所有结论均给出文件路径+行号或 SQL/代码证据。
0. 审计范围与方法
- 后端:
backend/src/main/java/com/danyang/match/platform/下matchmaker/(controller 2、service 2、entity 3、mapper 3)、referral/(controller 1、service 1)、audit/、shared/security/、auth/、counseling/(对照 INV-012)、review/(对照敏感访问日志范式)。 - 数据库:
backend/src/main/resources/db/migration/V001~V052 全量中 matchmaker 相关 8 个迁移(V001/V002/V009/V019/V021/V037/V038/V044/V052)。 - 前端:
admin-web/src/modules/matchmaker/MatchmakerManagementView.vue、modules/referral/ReferralManagementView.vue、router/index.ts、layouts/MainLayout.vue。 - 测试:
backend/src/test中 matchmaker/referral/权限相关 6 个测试类。 - 治理文档:
docs/governance/decision-register.md、open-questions.md、docs/PENDING_DECISIONS.md。 - 全局检索:
matchmaker_assignments、ServiceAssignment、matchmaker_leader、ContactAuthorization等关键词。
状态标注:【已实现】/【部分实现】/【缺失】/【无法确定】。
1. 现状总览
一句话结论:当前仓库的"红娘"只是一个挂在 matchmaker_services 服务单上的 assigned_operator_id 工单负责人概念——没有独立的服务对象分配关系、没有红娘组长角色、没有统一的数据范围/字段级隐私策略层;工单维度上已做过多轮越权修补(推荐/转介/详情归属校验),但认领、跟进、完结等写接口仍缺归属校验,未分配队列与全局统计对全体红娘可见,审计与权限回收存在系统性缺口。A1 核心未建模,B1 现状上"侥幸安全"(接口本就不返回联系方式)而非"设计安全"。
决策要求 vs 现状对照表
| # | 决策要求 | 现状 | 状态 | 关键证据 |
|---|---|---|---|---|
| 1 | DEC-002/A1:服务对象由平台授权人员或红娘组长分配,普通红娘仅访问本人有效分配对象 | 分配仅体现在 matchmaker_services.assigned_operator_id(服务单指派),无独立分配实体、无 assigned_by/理由/有效期/结束语义;/pending 全局队列对任何 matchmaker.view 持有者可见 |
缺失(核心) | V009 L5-20;MatchmakerOperatorController L32-36;MatchmakerServiceMapper.findSubmitted() |
| 2 | DEC-002 规则2:禁止普通红娘认领未分配/他人对象 | POST /operator/matchmaker/services/{id}/claim 任何 matchmaker.service 持有者可对任何 assigned 服务接单,无归属校验 |
缺失 | MatchmakerOperatorController L49-56;MatchmakerServiceService.claimService L109-122 |
| 3 | DEC-002 规则3/6:分配、转分配、撤销、结束留痕;状态机覆盖 | 仅有 assign/complete/cancel(cancel 无 HTTP 入口,死代码);无转分配、无撤销、无结束原因;分配人(assigned_by)不记录 | 缺失 | MatchmakerServiceService L87-104、L229-237;V009 L11-14 |
| 4 | DEC-003/B1:默认平台内联系,不暴露完整手机号/微信号/详细地址 | 红娘侧 DTO/VO 与前端页面均无 phone/wechat/address/idNumber 字段;但也不存在任何联系授权机制(无表、无策略类) | 部分实现(结果安全、机制缺失) | MatchmakerServiceService.getServiceDetail L329-348;ReferralService 全文;全局检索 ContactAuthorization 无结果 |
| 5 | DEC-004:红娘不得代替用户 Like/Pass/确认开启聊天/接受活动意向 | 设计与实现均贯彻"推荐≠Like":推荐仅建 pending_view 记录,Like/Pass 由用户本人端点(UserPrincipal+findByIdAndUser 归属校验)触发;转介确认须用户本人 |
已实现 | MatchmakerService L97-142、L213-258;ReferralService.confirmReferral L139-184;测试 MatchmakerReferralPhase13Test L132-296 |
| 6 | DEC-005:最小必要 + 数据范围 + 字段隐私 + 有效分配 | 无 Policy 层;数据范围校验散落且口径不一:recommend/转介有归属校验,claim/followup/complete 无;matchmaker.assign 与 matchmaker.service 语义即"工单指派/工单执行",不是"分配服务对象" |
缺失 | MatchmakerServiceService L142-151(有)vs L109-122、L186-203、L208-224(无) |
| 7 | DEC-006:全程留痕(分配、敏感查看、越权拒绝等) | 有 audit 基础设施且授权拒绝有全局监听;但 claim 不审计、敏感详情查看不记 SensitiveAccessLog、模块内归属拒绝不走 DataScopeGuard、分配审计不记 assigned_by/理由 | 部分实现 | AuthorizationAuditListener L25-49;DataScopeGuard 使用方仅 activity/counseling/service_appointment;MatchmakerServiceService.saveAuditLog L395-403(before/after 恒 null) |
| 8 | 功能权限矩阵:红娘无全站检索/导出/敏感字段 | matchmaker 角色仅授 3 权限(view/service/assign),无 user.view、无 data.export;全仓未发现导出端点 | 已实现(现状合规) | V052 L24-28;grep data.export 使用方为 0 |
| 9 | 红娘组长角色 | 全仓(迁移+代码+前端)无 matchmaker_leader/team_lead | 缺失 | grep 无结果 |
| 10 | 测试基线 MM-AUTH/CONTACT/ACTION/PRIVACY/AUDIT | 仅有"红娘不代替用户操作"(MM-ACTION 方向)与状态白名单守卫测试;无数据范围/联系方式/审计/生命周期场景测试 | 缺失 | MatchmakerReferralPhase13Test、MatchmakerServiceTest(仅常量白名单校验) |
2. 分项审计
2.1 当前红娘角色及权限实现
角色与权限定义【部分实现】:
matchmaker角色定义于 V001:('00000000-0000-0000-0000-000000000005', 'matchmaker', '公益红娘', '提供人工婚恋服务', TRUE)(V001 L145)。- 三条红娘权限定义于 V001 L161-163:
matchmaker.view(020)、matchmaker.assign(021)、matchmaker.service(022)。 - 角色-权限授权缺口曾真实存在:V052 迁移注释自证——"创建红娘运营账号后发现该角色在 role_permissions 中零条授权,登录后台 403"(V052 L4-12);V052 仅补授 3 条权限(L24-28)。
- 其余角色授权同样残缺:V044 注释承认"permissions 共 48 条,role_permissions 里 super_admin 只有 18 条;matchmaker 角色为 0 条"(V044 L5-11),V044 只回补了 super_admin,platform_admin/organization_admin 等角色的红娘权限授予状态无法从迁移中确认【无法确定,需查实际库】。注意
PermissionMatrixTestL57-59 期望 platform_admin 拥有 matchmaker.view/assign/service,但迁移中未见对应 INSERT【测试期望与 DB 授权可能漂移】。 - 红娘组长角色不存在:迁移与代码 grep
leader|team_lead均无结果。
后端接口面【部分实现】:
- 运营端
MatchmakerOperatorController(/operator/matchmaker,类级@PreAuthorize("hasAuthority('matchmaker.view')")L19):dashboard(L25)、pending(L32)、assign(L38,matchmaker.assign)、claim(L49,matchmaker.service)、my-services(L58)、recommendations(L69)、services/{id} 详情(L75)、recommend(L87)、followup(L99)、complete(L112)。 - 用户端
MatchmakerController(/matchmaker):提交申请(L48-58)、我的服务(L60)、推荐列表(L67)、viewed/accept/pass(L73-96)。 - 转介
ReferralController(/referrals):service-station(L33)/activity(L53) 均matchmaker.service;confirm(L70)/my-history(L85) 用户本人;operator/history(L99) 按matchmaker.service。
关键发现:matchmaker.assign 的语义——它是"把服务工单指派给某红娘"(assignService(serviceId, operatorId, organizationId),MatchmakerServiceService L87-104),即工单分派而非决策文档意义上的"服务对象分配"(对用户建立服务关系)。当前系统里"服务对象"就是服务单上的 user_id,分配关系与工单合一,无法表达"同一用户多次/多任务分配、分配历史、到期、撤销"。
已知实现缺陷(非权限但影响验收):
countInProgressByOperator统计status='processing'(MatchmakerServiceMapperL28-30),而 DB CHECK 白名单只有in_service(V009 L30-32),工作台 inProgress 恒为 0。cancelService(MatchmakerServiceServiceL229-237)无任何 Controller 入口,死代码。
2.2 服务对象分配建模
结论:未建模【缺失】。
- 全仓(backend main + admin-web src + 全部迁移)grep
matchmaker_assignments|service_assignment|ServiceAssignment无结果。 - 唯一的"分配"载体是
matchmaker_services.assigned_operator_id(V009 L12),字段只有assigned_operator_id / assigned_at / organization_id,没有 assigned_by、assignment_reason、assignment_scope、expires_at、ended_at、end_reason、status(分配关系状态与服务单状态混用)。 /operator/matchmaker/my-services返回该红娘名下全部服务含历史(MatchmakerServiceMapper.findByOperatorIdL36-38,MatchmakerServiceService.getMyServicesL68-82),即"我的服务对象 = 工单负责人",非独立分配关系。- 认领入口存在:
POST /services/{id}/claim仅需matchmaker.service(MatchmakerOperatorControllerL49-56),claimService只校验状态=assigned,不校验该单是否分配给本人,也不把 assigned_operator_id 改成自己(MatchmakerServiceServiceL109-122)——任何红娘可以把任何红娘的 assigned 单推进到 in_service。直接违反 DEC-002 规则 2(不能自行认领)。 - 转分配/撤销/结束:无 transfer/revoke 接口;
cancelService无入口;completeService无归属校验(L208-224)。
2.3 列表/搜索/详情/导出数据范围一致性
| 接口 | 数据范围 | 判定 |
|---|---|---|
GET /pending(L32-36 → findSubmitted Mapper L17-18) |
全局:任何 matchmaker.view 持有者可见全库 submitted 申请,含申请人 userId + 需求描述(Service L245-258) |
❌ 违反 DEC-002 规则1(不能浏览未分配对象库)、DEC-005 |
GET /dashboard(L25-30 → L260-276) |
pendingCount/todayCount/completedCount 为全局统计,inProgress 按本人 | ❌ 信息泄露(全局量级)+ 状态口径 bug |
GET /my-services(L58-63 → findByOperatorId) |
按本人过滤 | ✅ |
GET /recommendations(L69-73 → findByOperatorId) |
按本人过滤 | ✅ |
GET /services/{id} 详情(L75-85 → getServiceDetail L301-351) |
负责人→完整(含 userProfile 8 字段 + followup 正文);非负责人→仍返回状态、assignedOperatorId、以及该服务的推荐列表(含他人服务的 targetUserId 与推荐理由)(L317-327 对所有调用者装配) | ⚠️ 部分修复:比 Phase 13 D4 前好,但非负责人仍可探测任意 serviceId 的存在性与他人推荐内容(违反"响应不得泄露目标用户是否存在");推荐理由是红娘对候选对象的自由文本,可能含隐私 |
POST /services/{id}/recommend(L87-97 → L142-181) |
归属校验(L148-151)+ 不能推给申请人(L153-155) | ✅(但推荐对象 targetUserId 由红娘手输,无候选来源,见 2.4) |
POST /followup(L99-110 → L186-203) |
无归属校验:任何 matchmaker.service 持有者可对任意 serviceId 写跟进正文 |
❌ 违反 DEC-005;跟进正文为自由文本敏感写面 |
POST /complete(L112-118 → L208-224) |
无归属校验 | ❌ |
POST /claim(L49-56 → L109-122) |
无归属校验 | ❌(见 2.2) |
转介 POST /referrals/* |
assertMatchmakerServesUser(ReferralService L45-52 → findActiveByOperatorAndUser Mapper L48-54)要求存在本人名下、该用户的进行中服务 |
✅(Phase 13 D5 修复,注释自证修复前可代任意用户创建报名/预约) |
| 导出 | 全仓未发现任何导出端点(grep `data.export | export` 在 main 源码无结果) |
按 userId/查询参数越权形态:matchmaker 角色无 user.view(V052 仅 3 权限),无法触达用户管理接口,全站用户检索面关闭;但上述服务单维度参数越权真实存在。
2.4 联系方式暴露(B1)
现状结果层面安全【已实现(被动)】:
- 红娘侧所有返回 DTO 均为手工 Map 装配:服务详情 userProfile 仅 displayName/gender/age/regionName/educationCode/occupationCode/maritalStatus(
MatchmakerServiceServiceL333-343),无手机号/微信号/详细地址/身份证。 - 前端
MatchmakerManagementView.vue表格列仅 userId/serviceType/description/status/createdAt(L7-25)与推荐 userId/targetUserId/reason/status(L29-39);ReferralManagementView.vue无联系方式字段。grep phone/mobile/wechat/身份证/地址 在两个 view 中均无命中。 - 转介活动时的
emergencyContactPhoneEnc是红娘录入的紧急联系人信息(ReferralControllerL62、ReferralServiceL111-112),非读取用户联系方式。 - 无导出接口可绕道获取联系方式(2.3)。
机制层面缺失【缺失】:
- 全仓无 ContactAuthorization/contact_authorization/线下联系 相关表、实体、策略(grep 无结果)——DEC-003 要求的"电话/线下联系须专项授权"完全没有机制承载,目前是"没有功能所以没有漏洞"。若后续有人补电话/线下功能,无任何护栏。
- 平台内联系渠道:现有通知仅是单向 notification(
notificationService.send/sendAsync),无红娘↔用户平台内会话通道;B1 的"平台内服务申请/回应"仅覆盖转介确认流。
功能缺口:推荐弹窗要求红娘手输 targetUserId(前端 L62"输入被推荐对象的用户ID")——红娘既无全站检索权(正确),也没有任何"从用户已提供的择偶需求/公开池中获取候选"的受控途径,主流程实际不可用【部分实现】。
2.5 红娘代替用户操作(DEC-004)
未发现代替用户决策的接口【已实现】:
- 红娘推荐只建
MatchmakerRecommendation(status=pending_view),明确注释"这不是替用户 Like!""红娘点创建 Match → 不允许!"(MatchmakerServiceL97-105),并且有归属校验(L117-121)。 - Like/Pass 仅由用户本人端点触发:
POST /matchmaker/recommendations/{id}/accept|pass(MatchmakerControllerL81-96)→acceptRecommendation用findByIdAndUser(recommendationId, userId)归属校验(MatchmakerServiceL213-222),Pass 同理(L246-255)。 - 红娘无法以用户身份调用:用户端点走
UserPrincipal,运营端走OperatorPrincipal(两套 principal 体系,shared/security/principal/),且测试锁定"红娘无法用别人的用户身份接受推荐"(MatchmakerReferralPhase13TestL158-171)。 - 转介确认(活动/预约)须用户本人
confirmReferral,校验userId.equals(appt.getUserId())(ReferralServiceL145-146、L166-167),非本人确认被拒并有测试(MatchmakerReferralPhase13TestL255-269)。 - 无修改用户择偶条件、同意公开资料的红娘入口(matchmaker 角色无 user 相关权限)。
matchmaker.assign的实际用途 = 指派工单给红娘,不是"代替分配对象做操作";其风险在于指派时organizationId参数恒传 null(Controller L46 传null),且前端弹窗是硬编码假数据(见 2.8)。
⚠️ 唯一灰色点:转介会代替用户创建 activity_registration(status=pending) / service_appointment(status=submitted) 记录(ReferralService L74、L114),但带 source=matchmaker_referral 标记且必须用户本人确认后生效(needsUserConfirmation=true),语义上是"代填草稿、用户确认",与 DEC-004 底线可兼容,但建议在 ZC-MM-004 复核其与"代填关键个人信息"边界的表述。
2.6 审计日志覆盖(DEC-006)
基础设施【已实现】:
audit_logs表 +AuditLog实体(operatorId/organizationId/action/resourceType/resourceId/beforeData/afterData/ip/userAgent)。sensitive_access_logs+SensitiveAccessLog(operatorId/userId/dataType/reason/accessTarget/accessResult/ip)。AuthorizationAuditListener(L25-49):@PreAuthorize拒绝自动写AUTHORIZATION_DENIED(含 IP/UA)→ @PreAuthorize 层的越权拒绝已覆盖。DataScopeGuard.deny(L21-36):写DATA_SCOPE_DENIED后抛 403。
matchmaker 模块实际覆盖【部分实现,缺口明确】:
| 操作 | 审计现状 |
|---|---|
| 分配 assign | ✅ MATCHMAKER_ASSIGN(L102),但 beforeData/afterData 恒 null(saveAuditLog L395-403),不记录分配人以外信息(谁指派=principal.id 即 assignOperatorId=被指派红娘,见 Controller L44-46 的参数命名混乱:body.operatorId 是"被分配红娘",操作者是 principal,审计里存的是前者) |
| 认领 claim | ❌ 无审计(L109-122 无 saveAuditLog) |
| 推荐 recommend | ✅ MATCHMAKER_RECOMMEND(L174) |
| 跟进 followup | ✅ MATCHMAKER_FOLLOWUP(L201) |
| 完成 complete | ✅ MATCHMAKER_COMPLETE 但 operatorId 传 null(L222)——审计丢失操作者 |
| 取消 cancel | ❌ 死代码且无审计 |
| 查看敏感详情 getServiceDetail | ❌ 不写 SensitiveAccessLog、不调 recordSensitiveAccess(对照:ReviewService L470-477 访问本地资格佐证时写 SensitiveAccessLog;AuditLogService.recordSensitiveAccess L74-88 已有现成方法) |
| 模块内归属拒绝(recommend/followup 的 IllegalStateException、ReferralService 的 IllegalStateException) | ❌ 不走 DataScopeGuard,不产生 DATA_SCOPE_DENIED;仅裸抛异常 → 服务层越权拒绝不落审计,违反 MM-AUDIT-002 |
| 缺失字段 | 审计无 purpose_code/service_assignment_id/actor_role_snapshot/request_trace_id(DEC-006 第八节字段要求),AuditLog 实体不含这些列 |
2.7 可复用模块
| 组件 | 位置 | 与 A1/B1 的关系 |
|---|---|---|
| CounselingService 的 INV-012 判定链路 | counseling/service/CounselingService.java L181-200(assign 用条件更新 assignIfPending 防并发)、assertAssigned 模式(L205/232/242 各写接口前置)、GET /counseling/services/operator/assigned(Controller L141-145 → assignedTo) |
与 A1 最接近:"只有被分配人可见/可操作" 的完整范式,含权限矩阵注释(L38-48 定义 6 种角色的列表/脱敏/全文视图)。红娘"本人有效分配对象"可直接套此模式 |
| DataScopeGuard | shared/security/DataScopeGuard.java L21-36 |
拒绝+审计一体,activity/counseling/service_appointment 已用;红娘模块的归属拒绝应统一接入 |
| AuthorizationAuditListener | shared/security/AuthorizationAuditListener.java |
@PreAuthorize 拒绝自动审计,MM-AUDIT-002 的 403 层已覆盖 |
| AuditLogService.recordSensitiveAccess + SensitiveAccessLog | audit/service/AuditLogService.java L74-88;使用范例 review/service/ReviewService.java L470-477 |
受限字段/资料查看审计现成方法(matchmaker 详情接口应调用) |
| MatchmakerServiceMapper.findActiveByOperatorAndUser | matchmaker/mapper/MatchmakerServiceMapper.java L48-54 |
现成的"本人名下+该用户+有效状态"判定 SQL,是未来 ServiceAssignmentPolicy 判定的雏形(状态集含 submitted 待收紧) |
| OperatorAuthService 权限装载 | auth/service/OperatorAuthService.java L98-108(权限集合仅登录时装载进 JWT claims) |
权限事实来源;同时是权限回收的风险点(见 P1-7) |
| 用户端归属校验范式 | MatchmakerRecommendationMapper.findByIdAndUser + MatchmakerService.acceptRecommendation L213-222 |
"资源归属=当前主体"校验样板 |
2.8 前端现状
MatchmakerManagementView.vue:服务请求队列 tab 对所有matchmaker.view用户展示全库 pending(userId+需求说明,L7-25);分配弹窗红娘下拉是硬编码假数据operator_wang/operator_chen(L47-50)——分配功能前端不可真实使用;推荐弹窗需手输 targetUserId(L62)。- 路由/菜单:
router/index.tsL40-41、MainLayout.vueL177-178 权限码已与后端对齐(matchmaker.view / matchmaker.service,V052 注释所述错配已修复)。 ReferralManagementView.vueL35 已改走/referrals/operator/history(契约缺口已修)。
2.9 治理文档现状
docs/governance/decision-register.md:grepDEC-MATCHMAKER|matchmaker无结果【缺失】。docs/governance/open-questions.md:无 matchmaker/电话/线下联系条目【缺失】。docs/PENDING_DECISIONS.md:无 A1/B1/matchmaker 条目【缺失】。docs/audits/目录不存在(本报告即首次建立)。
2.10 现有测试
| 测试 | 覆盖 | 缺口 |
|---|---|---|
MatchmakerReferralPhase13Test |
MM-ACTION 方向较完整:推荐不产生 Like/Match(L132-141)、只有用户能 Like(L143)、红娘不能代用户接受(L158-171)、转介归属校验(L288-296)、用户本人确认(L225-269) | 不含数据范围/联系方式/审计断言 |
MatchmakerServiceTest |
仅校验常量与 DB CHECK 白名单一致(L94-177),纯守卫测试,无权限断言 | — |
PermissionMatrixTest |
静态 Map 自断言(L25-224 + L229 起),不连接 DB/迁移文件,且期望值(matchmaker 有 user.view=true,L119)与 V052 实际授权(无 user.view)漂移 | 矩阵不校验真实授权,是"看起来有保护"的假阳性 |
OperatorEndpointPermissionCoverageTest |
机械守卫:确保运营端 Controller 方法有 @PreAuthorize(L10、L43) | 只查注解存在性,不校验权限码正确性 |
| SecurityTests / V11InvariantsTest | 通用授权拒绝、INV 守卫 | 无 MM-AUTH/CONTACT/PRIVACY/AUDIT/LIFECYCLE 专属场景 |
测试基线覆盖判定:MM-ACTION ≈ 部分覆盖;MM-AUTH/MM-CONTACT/MM-PRIVACY/MM-AUDIT/MM-LIFECYCLE ≈ 全部缺失。
3. 可复用模块清单
(按建议采用优先级)
- CounselingService INV-012 范式(
CounselingServiceL181-200 条件更新分配、assertAssigned 写接口前置、Controller L141-145 assigned 列表)——A1"本人有效分配对象"的直接模板。 - DataScopeGuard(
shared/security/DataScopeGuard.java)——归属拒绝统一走 deny()=审计+403。 - AuthorizationAuditListener——已有,无需新建。
- AuditLogService.recordSensitiveAccess + SensitiveAccessLog(ReviewService L470-477 为使用范例)——受限字段查看审计。
- MatchmakerServiceMapper.findActiveByOperatorAndUser——分配有效性判定 SQL 雏形。
- OperatorAuthService/JwtAuthFilter 权限装载链——若做权限回收需改造此链路(JWT claims 快照问题)。
- 无 MatchmakerPolicy/PrivacyPolicy/ContactAuthorizationPolicy 任何现存实现——需新建。
4. 差距与风险清单(按优先级)
P0(违反 LOCKED 决策,阻断 DoD)
| # | 差距 | 违反 | 影响 | 证据 |
|---|---|---|---|---|
| P0-1 | 服务对象分配未建模:无 matchmaker_assignments/独立分配实体,分配=工单指派且无 assigned_by/理由/有效期/结束语义/分配历史 | DEC-002 规则6/7、5.1 概念模型 | A1 无法落地;分配不可追溯、不可回收、不可过期 | V009 L5-20;全仓 grep 无 assignment 实体 |
| P0-2 | 普通红娘认领入口开放且无归属校验:claim 任意 assigned 单;followup/complete 对任意 serviceId 可写/可完结 | DEC-002 规则1/2;DEC-005;MM-AUTH-004 | 任何红娘可干预他人服务对象、写入跟进内容、变更服务状态 | MatchmakerOperatorController L49-56、L99-118;MatchmakerServiceService L109-122、L186-203、L208-224 |
| P0-3 | 未分配申请队列+全局统计对全体红娘可见(含申请人 userId 与诉求描述) | DEC-002 规则1;DEC-005;MM-AUTH-002/PRIVACY-001 精神 | 普通红娘可浏览"待分配用户库"挑选对象,正面违反 A1 | MatchmakerOperatorController L32-36;MatchmakerServiceService L245-258、L260-276;Mapper findSubmitted L17-18 |
| P0-4 | 无红娘组长角色与团队管理范围概念,assign 权限无范围约束(谁能 assign 只有权限码、无组织/团队边界;organizationId 恒传 null) | DEC-002 规则4/5/8 | 分配权无法收敛到"平台授权人员或组长";跨团队数据范围无依据 | MatchmakerOperatorController L38-47(organizationId=null);全仓无 leader 角色 |
| P0-5 | 服务层归属/范围拒绝不产生审计(不走 DataScopeGuard),claim 无审计、complete 审计丢操作者、分配审计字段残缺 | DEC-006;MM-AUDIT-001/002 | 分配与越权链条不可追溯 | MatchmakerServiceService L102/L109-122/L222/L395-403;DataScopeGuard 使用方不含 matchmaker |
P1(安全机制缺口,DoD 必补)
| # | 差距 | 违反 | 影响 | 证据 |
|---|---|---|---|---|
| P1-6 | 非负责人可读任意服务单脱敏详情+他人推荐列表(targetUserId、推荐理由),泄露存在性与他人服务内容 | DEC-005(响应不得泄露目标用户是否存在);MM-PRIVACY-001 | 横向探测面 | MatchmakerServiceService L301-351(L317-327 对非 owner 装配 recommendations) |
| P1-7 | 运营权限是登录时 JWT claims 快照(JwtAuthFilter L90-99 直接取 claims),停用/调岗/权限回收后旧 token 有效期内仍放行 | DEC-005/006(权限及时回收);MM-LIFECYCLE-001 | 权限回收延迟=token 剩余有效期 | OperatorAuthService L98-108;JwtAuthFilter L90-99 |
| P1-8 | 受限资料查看无 SensitiveAccessLog(服务详情 userProfile 属受限资料但零记录) | DEC-006;MM-AUDIT 方向 | 敏感读取不可溯源 | MatchmakerServiceService L329-348;对照 ReviewService L470-477 |
| P1-9 | 无统一 Policy 层(MatchmakerPolicy/DataScopePolicy/PrivacyPolicy),范围校验散落且口径不一 | DEC-005 判定链要求 | 后续每加一个接口都可能漏校验(现状已证明) | §2.3 表格 |
| P1-10 | B1 联系授权机制完全缺失(无表无策略无审批流),当前安全是"功能不存在"而非"有护栏" | DEC-003 规则7 | 一旦有人新增电话/线下功能即裸奔;DoD "无授权电话/线下联系被拒绝" 无承载 | grep ContactAuthorization 无结果 |
| P1-11 | 测试基线缺口:MM-AUTH/CONTACT/PRIVACY/AUDIT/LIFECYCLE 无场景测试;PermissionMatrixTest 与真实授权漂移且自引用 | 决策文档第十节 | 回归无保护网 | §2.10 |
P2(质量/体验/治理)
| # | 差距 | 影响 | 证据 |
|---|---|---|---|
| P2-12 | 分配弹窗硬编码假红娘(operator_wang/chen),分配功能前端不可用 | 主流程断裂 | MatchmakerManagementView.vue L47-50 |
| P2-13 | 推荐对象需手输 userId,无受控候选来源,推荐主流程实际不可用 | 业务不可用(但不可用方向是安全的) | 同上 L62 |
| P2-14 | dashboard inProgress 统计用 'processing' 与 DB 白名单 'in_service' 不符,恒为 0 | 数据失真 | MatchmakerServiceMapper L28-30 vs V009 L30-32 |
| P2-15 | cancelService 死代码;matchmaker.assign 的 organizationId 参数恒 null | 代码卫生 | MatchmakerServiceService L229-237;Controller L46 |
| P2-16 | 治理文档无 DEC-MATCHMAKER/A1/B1 条目;docs/audits 目录此前不存在 | 治理不同步(ZC-MM-007 范围) | §2.9 |
| P2-17 | PermissionMatrixTest 期望 platform_admin/reviewer 等角色矩阵,但迁移未定义这些角色的 matchmaker 授权,实际库状态无法确定 | 授权事实不清 | V052 仅授 role 005;V044 仅补 super_admin |
5. 仍需后续确认的实现细节(对应决策文档第十一节,仓库现状)
| 待确认项 | 仓库现状 |
|---|---|
| 电话/线下联系例外批准岗位 | 无任何授权机制/审批流/岗位模型(grep 无结果);需产品确认后从零建 |
| 例外授权是否需用户单独同意及记录方式 | 无载体;activity_registration/service_appointment 已有 user_confirmed_at+source=matchmaker_referral 的用户确认范式(ReferralService L151-152)可作参考 |
| 临时授权有效期限 | 无 expires_at/有效期字段于任何红娘相关表(matchmaker_services 也没有);matchmaker_recommendations 无过期机制 |
| 审计日志/服务摘要保留周期 | 迁移中未见 audit_logs 的保留/清理策略;account 模块有 DataRetentionService(account/service/DataRetentionService.java)可参考但未覆盖审计日志【保留周期无法确定】 |
| 红娘组长可分配的组织/团队边界 | organizations 表与 matchmaker_services.organization_id 存在(V009 L11),operator 与组织的关联关系存在于 operator 体系,但"团队/红娘组"概念不存在,边界配置无从谈起 |
| platform_admin/organization_admin 的 matchmaker 权限实际授权 | 迁移不可证【无法确定,需查演示库】 |
| 转介代建 pending 报名/预约与"代填关键信息"边界 | 见 §2.5 灰色点,建议 ZC-MM-004 复核时与产品确认表述 |
6. 对 ZC-MM-002~007 实施顺序的建议(仅顺序与依赖)
- ZC-MM-002(A1 分配)先行且作为一切的地基:必须先落
matchmaker_assignments独立分配关系(含 assigned_by/reason/status/expires/ended)+ 红娘组长角色 + 分配状态机。理由:P0-1~P0-4 全部依赖此建模;后续所有数据范围判定都以"有效分配"为事实源。 - ZC-MM-005(统一数据范围与字段隐私)紧跟 002:把 claim/followup/complete/pending/dashboard/详情统一接入范围判定(复用 CounselingService INV-012 + DataScopeGuard)。依赖 002 的分配事实源;不先做会继续在旧工单模型上打补丁。
- ZC-MM-003(B1 联系规则)与 004(禁止代操作复核)可并行:003 当前主要是"补护栏+固化现状"(关闭暴露面+建立联系授权的拒绝默认),004 主要是复核与测试锁定(现状已基本合规)。两者都不依赖 002 的表结构,但 003 的"平台内服务通知渠道"若要做成会话类功能,建议在 005 稳定后做。
- ZC-MM-006(审计与权限回收)在 002/005 之后收口:分配/转交审计、SensitiveAccessLog 接入、模块内拒绝走 DataScopeGuard 都依赖 002 的新实体与 005 的判定点;JWT 权限回收(P1-7)涉及 auth 链路改造,放在业务语义稳定之后风险最小。
- ZC-MM-007(治理文档)最后:登记 DEC-MATCHMAKER-001~006 与 A1/B1、open-questions(第十一节五项)、本报告归档于 docs/audits。
- 贯穿性建议:每阶段同步补 MM-AUTH/CONTACT/ACTION/PRIVACY/AUDIT/LIFECYCLE 测试基线(当前几乎为零),并把 PermissionMatrixTest 从静态自引用改为对迁移/真实授权的校验——否则 P1-11 的假阳性会掩盖回归。
(审计完毕。本报告由架构师产出、主理人汇编落盘。后续实施需 Owner 授权后按 ZC-MM-002~007 分阶段进行。)