Files
danyang-match/docs/Phase-20-V11-Recovery-Completion-Report-2026-09-17.md
T
WorkBuddy 22c778b111 fix(analytics): close Phase 20 KEEP-VERIFY — 20 defects (fake metric definitions, hardcoded zeros, systemic soft-delete leak, module-wide 403, fake-green tests); add MetricCatalog + unified calculator + V043 grants + 51 guard tests; full regression 648 green
口径造假:
- D1 实名人数用 user_profiles.profile_status != 'rejected' 冒充 → in_progress/need_edit 被算成已实名
- D3 Chat Started 直接数会话行数 → system/risk_notice 系统通知计入
- D4 Match 不分来源 → 活动 10 对互选被当 10 对成功引用(Gate)
- D6 Banner 曝光/点击硬编码 0 → 改返回 null + NOT_INSTRUMENTED 标记

死代码 / 占位:
- D5 活动漏斗全部硬编码 0 + 一行把"用户数"当"活动数"的死代码
- D20 getDailySummary(days) 无边界校验

系统性软删缺陷(D12):
- BaseEntity.deletedAt 无 @TableLogic 且全局无逻辑删除配置
  → selectCount 把软删数据全部计入统计且无任何报错
- 修复 45 处统计调用显式 deleted_at IS NULL(2 轮复验,首轮漏扫 5 处次级路径:
  服务用户数分域明细 / 服务站统计 / 性别与年龄分布)

结构类:
- D15 Dashboard/用户漏斗/专项页各实现一遍同名指标且互不相同 → 抽 AnalyticsMetricsCalculator 统一口径

权限(Gate ⑩):
- data_viewer 在 V001 定义却全仓从未授权(空壳角色)
- platform_admin 的 V015 扩展条件 id >= '20000000-…' 漏掉 V1.0 老权限段
  → 整个 Analytics 模块除 super_admin 外全员 403
- V043 授予两角色;刻意不授予 organization_admin(相关表无组织维度,无范围全量即越权)

假绿测试:
- 删除 AnalyticsServiceTest(5× assertTrue(true),零断言)

新增:
- 指标字典 MetricCatalog(36 指标 × 12 字段)+ MetricStatus/MetricDefinition + docs/ANALYTICS_METRICS.md
- 守卫 51 用例:MetricCatalogGuardTest(11) / AnalyticsMetricsPhase20IntegrationTest(21)
  / AnalyticsSoftDeleteGuardTest(4,结构性) / AnalyticsPermissionGrantTest(6)
  / LocalEligibilityStatusVocabularyGuardTest(9)

跨阶段修复(缺陷本体属账号生命周期模块,按"发现即修"处理):
- X1(Critical)注销写入 user_identities 状态域的 'failed' → 违反生产 V017 chk_elig_verify_status
  → 真实 PG 上 closeAccount 事务回滚、账号注销 100% 不可用(负向验证实测 DataIntegrityViolation)
- X2 checkCanWithdraw 资格拦截用 submitted/pending(该表永不可能出现)→ 死代码
- X3 测试库补 chk_elig_type/chk_elig_verify_status,暴露 ActivityPhase14Test 夹具违规

验证:
- 负向验证两轮共 7 处注入 → 7 个用例精准变红且无无关失败(一处被源码结构+运行时行为两层捕获)
- 还原后 md5sum 逐字节一致,NEG-VAL 残留 0
- 后端全量 648 tests / 0 failures / 0 errors / BUILD SUCCESS;admin-web vue-tsc EXIT=0
2026-09-17 23:25:31 +08:00

22 KiB
Raw Blame History

Phase 20 V1.1 Recovery Completion Report — Analytics 数据统计与管理驾驶舱

日期:2026-09-17 阶段:Phase 20(Analytics — EXTEND) 分支:main | Tag:phase20-recovery-2026-09-17 Bundle:.workbuddy/backup/phase20-recovery-2026-09-17.bundle(complete history,回读通过) 结论:CLOSED(Gate 全项通过)


一、计划目标与 Gate

官方计划(WorkBuddy首轮启动提示词 + Phase 01~Phase 26 Jira级任务清单 + 每阶段验收条件.md 第 1777 行起) 定义 EPIC-P20 数据统计与管理驾驶舱,任务 P20-T01 ~ P20-T14(指标字典 / User / Review / Recommendation / Match / Chat Started / Matchmaker / Activity / Risk Metrics / Daily Summary / Rebuild / Platform Dashboard / Organization Dashboard / Analytics 权限)。

Gate 逐项结论

Gate 要求 结论 证据
Gate ① 每个指标登记名称、定义、分子分母、去重键、时间字段、时区、版本、负责人 ✅ MetricCatalog(36 指标 × 12 字段)+ docs/ANALYTICS_METRICS.md;MetricCatalogGuardTest 11 用例双向校验
Gate ② 固定数据集「注册10 / 实名7 / 审核通过4」显示一致 ✅ AnalyticsMetricsPhase20IntegrationTest.gateUserFunnelCountsThreeDistinctConcepts
Gate ③ 2 个 Like = 1 个 Mutual Match ✅ gateTwoLikesProduceOneMutualMatch
Gate ④ System Message 不算 Chat Started ✅ gateSystemMessageDoesNotCountAsChatStarted / riskNoticeDoesNotCountAsChatStarted
Gate ⑤ 活动 10 对互选 ≠ 10 对 Success ✅ gateMutualConfirmationDoesNotEqualSuccess(互选 10 / 成功 3,且显式 assertNotEquals)
Gate ⑥ 红娘看 20 候选、发 3 个 → Recommendation = 3 ✅ gateMatchmakerRecommendationCountsOnlySent
Gate ⑦ Dashboard 与专项页相同指标必须一致 ✅ gateDashboardAgreesWithDetailViews;且由统一口径层从结构上保证(见 §三 D15)
Gate ⑧ 服务用户数与服务人次分离 ✅ gateServiceUsersDeduplicatedButVisitsAreNot(1 人 / 4 人次)
Gate ⑨ 有效匹配在口径确认前标 provisional,禁止对外宣传 ✅ MetricCatalog 标 PROVISIONAL;响应带 effectiveMatchesProvisional + 说明;effectiveConnectionCarriesProvisionalFlag
Gate ⑩ 所有统计接口受 analytics.view 与 DataScope 约束,低权限 403 ✅ V043 授权 + AnalyticsPermissionGrantTest 6 用例(含低权限清单、organization_admin 刻意排除)
Gate ⑪ 相同用户多次服务不污染去重人数 ✅ gateServiceUsersDeduplicatedButVisitsAreNot
Gate ⑫ 软删数据不得计入统计 ✅ AnalyticsSoftDeleteGuardTest(结构性)+ 5 个行为用例

二、复验方法(KEEP-VERIFY)

本轮不采信任何「历史 ✅」。Phase 20 在 STATUS.md 里早已标注「✅ 已完成」,但复验起点是: AnalyticsController 15 个端点、AnalyticsService 546 行确实存在, 其中却同时含有硬编码 0、占位死代码、字母级口径造假、全员 403、假绿测试。

逐项核对顺序:

  1. 运行时路径:Controller → Service → Mapper → DDL 逐跳确认无断点、无死路。
  2. 生产约束:核对迁移里的 NOT NULL / CHECK / UNIQUE,确认代码写入值合法。
  3. 测试库保真度:测试库是否与生产同约束(缺约束 = 该非法路径零覆盖 = 假绿)。
  4. 守卫有效性:守卫是否解析真实 DDL/真实 SQL、是否会精准变红。
  5. 前端落点:后端能力是否有真实前端入口。

三、复验查出的真实缺陷与修复

3.1 口径造假类(数字是"算"出来的,但算错了概念)

编号 指标 缺陷 修复
D1 实名认证通过用户数 用 user_profiles.profile_status != 'rejected'("资料没被退回")当作"已实名"。取值域里的 in_progress(资料还在填)、need_edit(被退回待改)全被算成已实名 UserIdentityMapper.countVerifiedUsers():COUNT(DISTINCT user_id) FROM user_identities WHERE verification_status='verified'
D3 开启聊天会话数 直接数会话行数,从不看消息类型 —— 一条系统通知(「你们已互相喜欢」)就让会话计入 MessageMapper.countConversationsWithRealMessages():COUNT(DISTINCT conversation_id) WHERE message_type NOT IN ('system','risk_notice')
D4 Match 数 全来源混成一个数字,用户自主匹配 / 活动互选 / 红娘促成不可分。Gate「10 对互选 ≠ 10 对成功」在数据上无法体现 按 source 分离:match.source.user_like / .activity_mutual / .matchmaker,响应增 matchesBySource
D6 Banner 曝光量 / 点击量 硬编码 0。0 被读成"投了没人点",真相是"项目里根本没有记录点击的地方" 返回 impressions: null + impressionsAvailable: false + 原因说明;MetricCatalog 标 NOT_INSTRUMENTED

D1 是最危险的一条:对外汇报"实名 X 人"时,把"资料还在填"的人也算进去了,数字系统性偏高且无法自查。

3.2 死代码 / 占位类(后台永远显示 0)

编号 缺陷 修复
D5 活动漏斗所有指标硬编码为 0,并留着一行把「用户数」当「活动数」的死代码。后台"活动漏斗"不是没数据,是根本没有查询 接入真实查询:totalActivities / totalRegistrations / approvedRegistrations / checkedInCount,并三分离 mutualBothConfirmed / mutualMatches / mutualSuccessful
D20 getDailySummary(days) 无入参边界,越界静默返回 1~365 校验 → BusinessException(VALIDATION_ERROR)

3.3 系统性缺陷(D12:软删数据全部计入统计)

根因:BaseEntity.deletedAt 没有 @TableLogic 注解,项目也没有配置 MyBatis-Plus 的全局逻辑删除字段。 因此 selectCount(new LambdaQueryWrapper<T>()) 生成的 SQL 是 SELECT COUNT(*) FROM t —— 软删除的数据被完整计入统计,且不产生任何报错或警告。

影响 说明
累计注册用户数 用户删除后只增不减
Banner / 活动 / 服务记录 软删后仍计入总数
每日汇总 "新增用户/新增 Match"把已删记录继续算进当天

修复:AnalyticsService + AnalyticsMetricsCalculator 内全部统计查询显式 deleted_at IS NULL(共 45 处直接 Mapper 统计调用,0 处遗漏)。

补扫(本轮第 2 轮复验发现,属 D12 未扫净):首轮只修了"统一口径层"这条主路径,以下 5 处次级路径仍在漏:

位置 后果
getUniqueServiceUsersStats → programUniqueUsers 分域明细与已过滤的总数同屏展示,各部分之和对不上总数
getUniqueServiceUsersStats → eligibilityUniqueUsers 同上
getServiceStationStats → totalAppointments / activeStations 服务站点统计偏高
getUserFunnel → genderRatio 分布与 totalRegistered(已过滤)对不上账
getUserFunnel → ageDistribution 同上

3.4 结构类(三套实现 → 无单一事实来源)

编号 缺陷 修复
D15 Dashboard / 用户漏斗 / V11 专项仪表盘各自实现了一遍同名指标,且互不相同;字段名还不一致(verifiedUsers vs verifiedCount)。只要有一处漏改就永久不一致,且没有任何测试能发现 抽出 AnalyticsMetricsCalculator 作为唯一口径来源;AnalyticsService 全面委托,只保留组装与前端字段名兼容

3.5 权限授予脱节(Gate ⑩:全员 403)

缺陷 证据 修复
V001 第 149 行定义了专看统计的角色 data_viewer(描述:"仅查看统计数据"),但全仓迁移从未给它授予任何权限 —— 是个空壳角色 角色存在 ≠ 角色可用 V043 授予 analytics.view
platform_admin 的权限扩展在 V015 以 id >= '20000000-...'(V1.1 新增段)为条件,而 analytics.view 的 id 是 10000000-...060(V1.0 老权限段)→ 被漏掉 权限段划分造成的静默遗漏 V043 一并授予
净效果:整个 Analytics 模块(15 端点)除 super_admin 外全员 403,包括专门为它设计的 data_viewer。前端"数据统计"菜单对谁都是点了报错 — 见下

两个既有守卫(AnalyticsController 的 @PreAuthorize、OperatorEndpointPermissionCoverageTest)都是绿的 —— 前者检查"端点有没有声明权限",后者检查"端点有没有权限注解",都不检查"这个权限有没有授予任何角色"。 这是"守卫覆盖不到的组合缺陷",故新增 AnalyticsPermissionGrantTest 补上「权限 → 角色」这一环。

刻意不授予 organization_admin:平台级统计接口所依赖的表(users / matches / messages)没有 organization_id 维度, 无法把机构管理员的数据范围限制在"本机构"。在没有范围限制的情况下授予全局统计权限 = 越权可见全平台数据。 宁可返回 403,也不给无范围的全量数据;待统计口径支持组织维度后再评估。

3.6 测试假绿(本轮替换)

缺陷 说明
AnalyticsServiceTest 由 5 个 assertTrue(true) 组成 —— 没有任何断言,却让"Phase 20 已完成"看起来成立。测试通过不代表功能存在,只代表测试什么都没检查

3.7 前端落点

缺陷 修复
AnalyticsView.vue 未接 /v11-dashboard 综合端点;公益服务数据区、活动互选vs成功、按来源分离 Match、provisional / 未埋点标注全部缺失 补接端点 + 新增上述区块 + 对 PROVISIONAL / NOT_INSTRUMENTED 显式标注(避免运营把 provisional 当正式口径引用)

3.8 其它修正

项 说明
CounselingServiceRecord 注释过时 注释写 counseling / premarital,实际服务类型是 relationship_guidance / emotional_support / premarital_guidance / anti_fraud_guidance
MigrationValidationTest 基线 LATEST_MIGRATION 42 → 43(新增 V043)

四、跨阶段发现(复验 Phase 20 时发现,缺陷本体属账号生命周期模块)

按纪律「发现真实缺陷即修」,以下问题在核对生产约束 vs 测试库时暴露。其模块归属为 Phase 21,故单独列出。

X1(Critical):账号注销写入了越界状态 → 生产必然 500

AccountLifecycleService.invalidateLocalEligibility 写入 verification_status = 'failed'(Constants.VERIFICATION_STATUS_FAILED)。

但生产 V017 chk_elig_verify_status 只允许 declared / needs_evidence / pending_review / verified / rejected —— 没有 failed, 且没有任何后续迁移放宽它(已全量核对迁移目录)。

failed 实际属于 user_identities 的状态域 —— 生产 V002 的 CHECK 里才有它, 且 IdentityVerificationService 也正是这样用的。这是跨状态域常量误用。

后果:真实 PostgreSQL 上 CHECK 违约 → closeAccount 整个事务回滚 → 账号注销 100% 不可用(报 500)。而测试库此前没有这两条 CHECK,所以该必崩缺陷在测试期完全不可见。

负向验证实证:把该行改回 failed 后,守卫报出的异常正是 DataIntegrityViolation —— 即数据库层真实拒绝了这次写入,直接证实了生产的失败模式。

修复:改用 VERIFICATION_STATUS_REJECTED(白名单内,语义为"资格失效")。 未选择"新增迁移把 failed 加进白名单" —— 那是把一个跨域误用的值固化进契约, 与本项目 Phase 19 D16「收敛权限膨胀」的处理原则一致:优先收敛,不扩契约。

X2:checkCanWithdraw 的资格拦截是死代码

该方法用 'submitted' / 'pending' 判断"本地资格审核进行中", 但这两个值在 user_local_eligibilities 里永不可能出现(同 X1 的状态域混用) → 拦截条件恒为 false,从未生效过:审核中的用户可以直接注销。

修复:改用 needs_evidence / pending_review(抽为 LOCAL_ELIGIBILITY_IN_REVIEW 常量并注明取值域来源)。

X3:测试库约束漂移

测试库缺 chk_elig_type / chk_elig_verify_status。 补齐后立即暴露出一处既有夹具违规:ActivityPhase14Test 使用 eligibility_type = "household"(正确值是 household_registration)—— 已修正。

X4 / X5:Phase 21 遗留(本轮未修,仅记录)

项 说明
X4 测试假绿 AccountLifecycleServiceTest 由 7 个 assertTrue(true) 组成(仅有的真实断言是几个常量字符串)。零真实覆盖,这正是 X1/X2 长期隐形的原因之一
X5 未实现 notifyActiveMatches / notifyMatchmaker / cancelPendingActivityRegistrations 均为 TODO 桩(只写日志,无任何查询);checkCanWithdraw 的 activeMatchmakerServices 硬编码 0(注释:"需要 MatchmakerServiceMapper")→ 该阻塞项永不生效

X4/X5 属 Phase 21 交付范围(需产品确认通知语义与红娘服务口径),不在本轮静默扩权修复,留待 Phase 21 处理。


五、本轮新增能力(此前完全不存在)

5.1 指标字典代码化

文件 作用
MetricStatus OFFICIAL / PROVISIONAL / NOT_INSTRUMENTED —— 把"口径是否可用"本身建模
MetricDefinition 12 字段 record:code / name / definition / numerator / denominator / dedupeKey / timeField / timezone / version / owner / status / sourceTable
MetricCatalog 36 个指标注册表;of() 未登记即抛异常;provisional() / notInstrumented() 检索
docs/ANALYTICS_METRICS.md 指标字典文档,与代码逐条对应;含软删强制约定、变更流程、已知待办

为什么状态本身要建模:0 与「不可用」对决策的含义完全相反。 「Banner 点击 0」读起来是"投了没人点",真相是"没有任何地方记录点击"。

5.2 统一口径计算层

AnalyticsMetricsCalculator —— 消除三套独立实现,所有查询显式过滤软删, 方法上的 javadoc 标注其对应 MetricCatalog 指标编码。

5.3 新增指标与方法

层 新增
UserIdentityMapper countVerifiedUsers()
MessageMapper countConversationsWithRealMessages()
MatchMapper countAllMatches / countBySource / countBySourceAndStatus / countByStatus
RecommendationRecordMapper countShown / countShownBySource / countBySourceAndAction / countByAction
ActivityRegistrationMapper countAllRegistrations / countRegistrationsByStatus / countCheckedIn / countRegistrationsBySource / countDistinctRegisteredUsers
ActivityMutualConsentMapper countBothConfirmed / countOneSidedAccepted
V043 analytics.view → data_viewer + platform_admin

六、守卫与测试保真度(本轮新增)

守卫 用例数 作用
MetricCatalogGuardTest 11 代码 ↔ 文档双向一致;12 项元数据完整;状态诚实性(provisional/not-instrumented 必须真的被标注);去重键差异;未登记编码抛异常
AnalyticsMetricsPhase20IntegrationTest 21 Gate 全部 8 类 + 软删 5 例 + 入参校验 + 可用性诚实性。真实 DB、真实 SQL,非 mock
AnalyticsSoftDeleteGuardTest 4 结构性约束:统计包内每处 *Mapper.selectCount/selectList 实参必须含 isNull(..)。含"防假绿"下界(扫不到足够调用点即失败)
AnalyticsPermissionGrantTest 6 「权限 → 角色」授权链;低权限角色清单;organization_admin 刻意排除;授予的权限码必须真实存在;super_admin 兜底路径
LocalEligibilityStatusVocabularyGuardTest 9 X1/X2 守卫:生产 DDL 白名单解析、测试库一致性(防漂移)、越界值被 DB 真实拒绝、注销后状态落在白名单内、审核中确实能拦截注销

AnalyticsServiceTest(5×assertTrue(true))在本轮被清除; AnalyticsSoftDeleteGuardTest 的"防假绿下界"与 MetricCatalogGuardTest 的解析自检, 共同保证守卫不会因为解析失效而静默变绿。


七、负向验证(证明守卫是承重的,而非装饰)

一次性注入缺陷(均以 NEG-VAL 标记),跑定向回归,确认精准变红且无任何无关失败,再还原。

第 1 轮

注入缺陷 精准变红的用例 断言信息
把实名口径改回 user_profiles.profile_status <> 'rejected' realNameIsNotProfileStatusProxy expected: <0> but was: <5>
移除 registeredUsers() 的 isNull(DeletedAt) softDeletedUserIsExcludedFromRegisteredCount expected: <0> but was: <1>
从 V043 移除 data_viewer 授权 dataViewerRoleIsActuallyUsable 当前实际授予: []

MetricCatalogGuardTest 11/11 保持绿 —— 口径守卫在 DB 层,目录守卫正确地不误报。

第 2 轮(针对本轮新增守卫)

注入缺陷 精准变红的用例 断言信息
invalidateLocalEligibility 改回 failed closeAccountLeavesEligibilityStatusWithinWhitelist DataIntegrityViolation —— DB 真实拒绝
checkCanWithdraw 改回 submitted/pending pendingEligibilityReviewBlocksWithdrawal expected: <false> but was: <true>
移除 programUsers 的 isNull(DeletedAt) everyAnalyticsMapperQueryFiltersSoftDeletes(结构性) 列出未过滤的调用点
同上 softDeletedServiceRowsAreExcludedFromServiceMetrics(行为) expected: <1> but was: <2>

同一注入被两层守卫同时捕获(源码结构 + 运行时行为),说明结构性守卫对新代码同样生效 —— 这正是它存在的意义:数字断言只能覆盖"被构造出来的固定数据集"。

还原后:grep -rc "NEG-VAL" backend/src = 0;md5sum 与注入前逐字节一致。


八、回归证据

后端全量:Tests run: 648, Failures: 0, Errors: 0, Skipped: 3 / BUILD SUCCESS  (约 6.5 分)
  (复验起点 602 → 本轮 648,净 +46;其中新增守卫 9+4+6+11 = 30,集成用例 21)
前端 admin-web:vue-tsc --noEmit EXIT=0
负向验证:2 轮、7 处注入 → 7 个用例精准变红;还原后 0 残留

九、明确记录的设计决策(非缺陷,但需知情)

  1. organization_admin 不授予全局统计权限(见 §3.5)。要在机构维度开放统计, 必须先给 users / matches / messages 等表建立组织维度,否则任何授予都是越权。
  2. match.effective 标 PROVISIONAL:「有效」是否要求线下见面 / 持续 N 天,业务方未裁决。 当前是保守近似(真人会话数与 Match 数取较小值)。禁止作为正式成功率或写入对外宣传材料。
  3. Banner 曝光/点击保持 NOT_INSTRUMENTED:不返回 0。要启用需先建事件表 + 上报端点 + 小程序埋点。
  4. 软删过滤是每个指标的隐含前置条件:BaseEntity.deletedAt 无 @TableLogic, 全局也无逻辑删除配置。本项目不建议为此加全局配置(会改变所有既有查询语义、风险外溢), 而是用结构性守卫强制显式过滤。
  5. 功能开关关闭时的注册口径:user.registered.total 统计全部未软删用户, 不按 account_status 过滤 —— 「注销用户」不是「从未注册用户」,两者对"累计注册"含义不同。

十、遗留与下一阶段

项 状态
X4 AccountLifecycleServiceTest 的 7 个假绿断言 ⏳ Phase 21(本轮已就资格失效路径补真实覆盖)
X5 通知/活动报名取消的 TODO 桩、activeMatchmakerServices 硬编码 0 ⏳ Phase 21,需产品确认语义
Banner 曝光/点击埋点闭环 ⏳ 需新建事件表 + 上报端点
match.effective 口径裁决 ⏳ 需业务方确认
service.user.unique 等跨域去重改为 DB 侧聚合 ⏳ 数据量达十万级时
组织 / 站点维度统计拆分 ⏳ 依赖组织维度建模
下一阶段 Phase 21(等用户确认)

附:本轮改动文件

后端(新增) analytics/metric/MetricStatus.java、analytics/metric/MetricDefinition.java、 analytics/metric/MetricCatalog.java、analytics/metric/AnalyticsMetricsCalculator.java、 db/migration/V043__grant_analytics_view_to_data_roles.sql

后端(修改) analytics/AnalyticsService.java(重写为委托模式)、 account/service/AccountLifecycleService.java(X1/X2)、 verification/mapper/UserIdentityMapper.java、chat/mapper/MessageMapper.java、 match/mapper/MatchMapper.java、match/mapper/RecommendationRecordMapper.java、 activity/mapper/ActivityRegistrationMapper.java、activity_mutual/mapper/ActivityMutualConsentMapper.java、 counseling/entity/CounselingServiceRecord.java

前端 admin-web/src/modules/analytics/AnalyticsView.vue

测试 analytics/AnalyticsMetricsPhase20IntegrationTest.java(新增,21)、 analytics/MetricCatalogGuardTest.java(新增,11)、 analytics/AnalyticsSoftDeleteGuardTest.java(新增,4)、 security/AnalyticsPermissionGrantTest.java(新增,6)、 account/LocalEligibilityStatusVocabularyGuardTest.java(新增,9)、 activity/ActivityPhase14Test.java、deployment/MigrationValidationTest.java、 src/test/resources/schema-h2.sql、analytics/AnalyticsServiceTest.java(删除)

文档 docs/ANALYTICS_METRICS.md(新增)