主题
运营数据指标与归因体系 · 技术实现方案
版本:v2.1(去归因,聚焦自动化 EDM) | 更新:2026-07-09 配套:
运营数据指标与归因体系.mdv2.1(口径)、eladmin-运营看板原型.html(产品形态,一期/二期已分组) v2.1 变化:砍掉归因(t_ops_action_result 表、对照组/A-B 分组、归因任务)。依据:生命周期触发邮件是金融行业基本功,不是实验对象;当前体量下 uplift 统计上测不出(检测 2pp 差异需每组数千样本)。保留 action_log(频控依据+不可回算)、实时响应率(运营故障报警,非因果证明)、EDM 事件(渠道健康+合规硬要求)。恢复条件:接入真金白银激励需算 ROI 时。 v2.0 变化:新表 11 → 6 张(v2.1 后为 5 张),定时任务 6 → 3 个(v2.1 后为 2 个),实时查询为主。
0. 关键前提(已核对代码)
0.1 大小写已定论
生产 CardStatisticsMapper.xml 实际使用小写 intent='charge' / status='posted'。本方案所有交易 SQL 统一小写。上线前跑 SELECT DISTINCT status,intent FROM t_user_card_transaction_utgl LIMIT 20 终确认。ATM 识别:authorization_obj LIKE '%"type":"ATM"%'。
0.2 工程约定(新代码必须遵循)
| 层 | 位置 | 命名 / 注解 |
|---|---|---|
| 定时任务 | wallet-front-api/.../svc/schedule/ops/ | XxxTask.kt,@Component + @Scheduled(cron)(@EnableScheduling 只在 FrontApiApplication) |
| 实体/Mapper/DAL | wallet-front-dal/.../modular/ops/ | @TableName+@TableId(AUTO);extends BaseMapper;复杂聚合走 XML <select>;IXxxDalService/Impl |
| eladmin 接口 | eladmin-wallet/.../modules/ops/rest/ | /api/eladmin/ops/*,@PreAuthorize("@el.check('ops:x')") + @Log(经 wallet-rpc→wallet-front-dal 传递依赖复用 DAL) |
| 公网追踪接口 | wallet-front-api | 收件人点击来自公网,不能放 eladmin;复用 GlobalRateLimitFilter |
| DDL | 手动执行(无 Flyway) | 建表脚本纳入 docs/sql/(stat_*.sql / ops_*.sql,按表前缀命名) 版本管理 |
1. 数据流 v2.0(实时为主,三条路径)
源业务表(已存在):t_user_info / t_user_identity_audit_master /
t_user_card / t_user_card_transaction_utgl / t_agency / t_user_asset
│
┌─────────┼──────────────────────┬───────────────────────┐
│(A) 实时查询【主路径】 │(B) 日更计数表【仅1张】 │(C) 动作与EDM【准实时】
│漏斗(cohort口径)/六张行动名单/ │t_stat_agent_daily │t_ops_action_log
│留存cohort/沉睡分层/高余额低频 │(只存分子分母计数) │t_ops_email_template/event/suppression
│——全部可回算,直查源表 │排行/趋势/沉睡代理检测 │响应率/邮件事件均实时查
└─────────┴──────────────────────┴───────────────────────┘
▼
eladmin 查询接口(/api/eladmin/ops/*)+ wallet-front-api 公网追踪端点- (A) 是主路径:留存/沉睡/漏斗全部由时间戳回算(0.4 见指标文档),当前体量直查源表足够快,不建表。查询变慢再物化(附录恢复条件)。
- (B) 只建一张代理日计数表:排行/趋势要按天对比,且"当日归属代理"是快照性质(用户可能换代理)。
- (C) 动作日志不可回算(不记就永远没有),第一天就建;响应率 = action_log ⋈ 源表实时算,不建结果表、不跑归因任务。
2. 表设计(5 张 DDL)
前缀按性质区分:数据统计类用 t_stat_(与既有 t_stat_card 同族,如 t_stat_agent_daily);运营动作/EDM 类用 t_ops_(action_log / email 三表)。同库,日期字段 INT YYYYMMDD。
2.1 t_stat_agent_daily — 代理每日计数(唯一的聚合表)
只存分子分母计数,不存比率——比率在查询时按所选窗口 SUM(分子)/SUM(分母) 现算(日比率不能平均;小代理单日比率无意义)。
sql
CREATE TABLE t_stat_agent_daily (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
stat_date INT NOT NULL COMMENT '统计日 YYYYMMDD',
agency_id BIGINT NOT NULL COMMENT '0=全平台汇总',
-- 规模计数(当日新增)
reg_cnt INT DEFAULT 0 COMMENT '新增注册(人)',
kyc_submit_cnt INT DEFAULT 0 COMMENT '新增KYC提交(人)',
kyc_approve_cnt INT DEFAULT 0 COMMENT '新增KYC通过(人)',
card_apply_cnt INT DEFAULT 0 COMMENT '新增申请卡',
card_produce_cnt INT DEFAULT 0 COMMENT '新增制卡',
card_active_cnt INT DEFAULT 0 COMMENT '新增激活',
card_firstuse_cnt INT DEFAULT 0 COMMENT '新增首刷',
-- 价值(当日)
charge_amt DECIMAL(20,4) DEFAULT 0 COMMENT '消费GMV(posted charge,北极星)',
charge_cnt INT DEFAULT 0,
topup_amt DECIMAL(20,4) DEFAULT 0 COMMENT '充值(先行指标,不并入GMV)',
withdraw_amt DECIMAL(20,4) DEFAULT 0,
active_card_cnt INT DEFAULT 0 COMMENT '当日有消费的卡数',
-- 风控计数(比率查询时算,分母<30不评估)
declined_cnt INT DEFAULT 0,
txn_cnt INT DEFAULT 0 COMMENT '总笔数(拒付率分母)',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uk_date_agency (stat_date, agency_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='代理每日计数(只存分子分母)';2.2 t_ops_action_log — 运营动作日志(频控依据 + 不可回算,第一天就建)
sql
CREATE TABLE t_ops_action_log (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
card_id BIGINT COMMENT '作用的卡',
user_id BIGINT,
agency_id BIGINT COMMENT '归属代理(快照,防换代理)',
action_type VARCHAR(32) NOT NULL COMMENT 'ACTIVATE_NUDGE/FIRST_USE_NUDGE/REACTIVATE/KYC_ASSIST/RESUBMIT_PUSH/OPEN_CARD_NUDGE',
channel VARCHAR(20) COMMENT 'EMAIL/SMS/TELEGRAM/MANUAL',
operator VARCHAR(64) COMMENT '执行人/系统/代理账号',
batch_id VARCHAR(64) COMMENT '活动批次',
template_id BIGINT COMMENT '使用的模板版本',
send_status VARCHAR(16) DEFAULT 'SENT' COMMENT 'SENT/SKIPPED(频控拦截)/FAILED',
incentive_type VARCHAR(32) COMMENT '预留:激励类型(券/费率减免)',
incentive_cost DECIMAL(20,4) COMMENT '预留:激励成本(日后接激励算ROI用)',
target_metric VARCHAR(32) COMMENT 'ACTIVATION/FIRST_USE/RETENTION(决定响应率判定口径)',
card_status_before VARCHAR(32) COMMENT '动作时卡状态快照',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '响应率窗口起点',
KEY idx_card (card_id),
KEY idx_batch (batch_id),
KEY idx_type_time (action_type, create_time)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='运营动作事件日志';v2.1 已移除
group_type(对照组分组)列——不做 A/B,全部 100% 发送。日后接入付费激励需要 uplift 时ALTER TABLE加回即可。
2.3 EDM 三表(模板 / 事件 / 压制)
sql
CREATE TABLE t_ops_email_template (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
template_code VARCHAR(64) NOT NULL,
action_type VARCHAR(32) NOT NULL,
lang VARCHAR(8) DEFAULT 'zh' COMMENT 'zh/en',
version INT DEFAULT 1,
subject VARCHAR(255) NOT NULL,
body_html MEDIUMTEXT COMMENT '占位符 ${cardTailNo}/${balance}/${activateUrl}',
enabled TINYINT DEFAULT 1,
create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uk_code_lang_ver (template_code, lang, version)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='EDM邮件模板';
CREATE TABLE t_ops_email_event (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
action_id BIGINT NOT NULL COMMENT '关联 t_ops_action_log.id',
event_type VARCHAR(16) NOT NULL COMMENT 'OPENED/CLICKED/BOUNCED/UNSUBSCRIBED',
event_time DATETIME DEFAULT CURRENT_TIMESTAMP,
ua VARCHAR(255) COMMENT '过滤预取虚增',
KEY idx_action (action_id),
KEY idx_type_time (event_type, event_time)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='EDM邮件事件';
CREATE TABLE t_ops_email_suppression (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
email VARCHAR(128) NOT NULL,
reason VARCHAR(32) COMMENT 'UNSUBSCRIBE/COMPLAINT/HARD_BOUNCE',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY uk_email (email)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='EDM压制名单';2.4 后置表(本期不建,日后启用时的变更要点)
| 表 | 后置原因 | 启用时必须的改动 |
|---|---|---|
t_ops_action_result + 对照组机制 | 生命周期邮件是基本功不做实验;当前体量 uplift 统计上测不出 | 恢复条件=接入付费激励(券/费率减免)需算 ROI;届时 action_log 加回 group_type 列、恢复 hash 分组与归因任务 |
t_stat_card_daily | 沉睡/留存可回算,实时查够用 | 瘦身为只存不可回算字段:balance / card_status / agency_id 快照 |
t_stat_funnel_daily | 漏斗实时查;KYC 积压趋势暂用当前快照 | 改存当日新增数而非累计(累计可加总得出,反之不行);补 KYC 各状态积压快照列 |
t_stat_retention_cohort | 完全可回算 | 仅当查询慢时作为物化缓存 |
t_ops_anomaly_rule/hit | 代理少,人工看记分卡即可 | 规则表加 min_denominator 分母保护列;auto_action 禁止 FREEZE_COMMISSION,仅允许 NUDGE 类 |
3. 计算逻辑
3.1 实时接口 SQL 要点(路径 A,不建表)
- cohort 漏斗(考核口径):按申请周分组,
激活率 = COUNT(active_time <= create_time + 14d) / COUNT(*),只统计申请时间早于今天−14 天的"已成熟"cohort;快照累计口径(状态≥阶段)仅存量盘点接口用 - 六张行动名单:直查
t_user_card+ 反连接交易表;沉睡分层用MAX(posted charge create_time)距今天数;剔除 LOCKED/SUSPENDED 单列;分页在 SQL 内完成(修掉老接口先分页后过滤的缺陷) - 留存 cohort / 活跃趋势:按
active_time分月 cohort × 窗口内 distinct 有消费卡——全历史可回算,上线即有完整热力图 - 高余额×低频:
t_user_card ⋈ t_user_asset,balance > 阈值 AND 近 30 天 charge < 2 笔,按 balance 降序
3.2 t_stat_agent_daily(每日 upsert)
- 按
agency_id聚合当日新增各计数 + 当日交易金额(posted charge / topup / withdraw 分列) INSERT ... ON DUPLICATE KEY UPDATE(沿用 CardStatistics 惯例),另落一行 agency_id=0 全平台汇总- 排行接口读此表按窗口 SUM;沉睡代理检测 = 连续 4 周
SUM(reg_cnt)=0的代理;新代理激活期 = t_agency.create_time 起算首个 reg/首个 active 的间隔(实时查)
3.3 响应率(实时查,无表无任务)
- 定位:运营故障报警,不是因果证明——发了 500 封催激活窗口内 0 人激活 = 链接挂了/进垃圾箱/落地页撞登录墙,这才是它要抓的问题
- 按 batch_id / action_type / template_id 分组:
响应率 = 窗口内达成数 / SENT 数(ACTIVATION=active_time 落窗口;FIRST_USE/RETENTION=窗口内 posted charge),action_log ⋈ 源表实时算 - 分母只取
send_status='SENT'(SKIPPED/FAILED 不进分母) - ⚠️ 展示处固定标注"响应率含自然转化,不代表动作净效果",防止被当成 ROI 汇报
3.4 EDM 指标
- 打开率/点击率/退订率分母 =
channel='EMAIL' AND send_status='SENT' - 事件采集见 §6.3;与响应率并排展示:点击高但响应低 → 落地承接有问题;点击低 → 文案/送达有问题
- 退订率/硬退信率是合规监控红线:单批退订 >1% 或硬退信 >2% 立即停发排查(发件域名声誉殃及验证码邮件)
3.5 对账与水位线(OpsReconcileTask)
- 日对账:agent_daily 各计数总和 vs 源表按日现算,偏差 >1% 告警——指标被抓到一次不准,信任就崩
- 内审积压水位线:
COUNT(card_account_status='PENDING_REVIEW')> X 或较昨日 +50% → 消息中心告警内部审核负责人(防雪崩,非考核)
4. 定时任务(2 个,wallet-front-api/svc/schedule/ops/)
| 任务 | Cron | 职责 |
|---|---|---|
OpsAgentDailyTask | 0 20 1 * * ?(1:20,避开 CardStatisticsTask 1:00) | upsert t_stat_agent_daily |
OpsReconcileTask | 0 40 2 * * ? | 日对账 + 内审积压水位线告警 |
骨架沿用 CardStatisticsTask:@Component + @Scheduled(cron),复杂聚合放 Mapper XML。
5. 接口清单
5.1 eladmin 查询接口(modules/ops/rest/,权限点 ops:*)
| 接口 | 数据源 | 类型 |
|---|---|---|
/api/eladmin/ops/funnel | 实时 cohort 漏斗 + KYC 两段快照 | A 实时 |
/api/eladmin/ops/list/{type} | 六张行动名单(分页 SQL 内完成) | A 实时 |
/api/eladmin/ops/retention | 留存 cohort + 活跃趋势(回算) | A 实时 |
/api/eladmin/ops/agent/rank | 分层排行(甲/乙联赛,窗口 SUM 现算比率) | B |
/api/eladmin/ops/agent/lifecycle | 新代理激活期名单 + 沉睡代理名单 + 集中度 | A+B |
/api/eladmin/ops/action/log (POST) | 人工动作/一键触达写日志 | C |
/api/eladmin/ops/action/stats | 响应率(batch/action_type/模板维度,实时算) | A 实时 |
/api/eladmin/ops/email/template (CRUD) | 模板管理(版本化+启停) | CRUD |
/api/eladmin/ops/email/stats | 批次打开/点击/退订率 | B |
5.2 公网追踪端点(wallet-front-api,HMAC 验签防遍历刷数据)
| 端点 | 行为 |
|---|---|
GET /t?aid=&sig= | 验签 → 写 CLICKED → 302 到落地页(激活页/用卡指引,深链+短时效免密会话,别让登录墙杀掉转化) |
GET /e/o/{aid}.png | 写 OPENED → 返回 1×1 透明 PNG |
GET /e/unsub?aid=&sig= | 写 suppression + UNSUBSCRIBED → 退订成功页 |
6. 触达接入点
6.1 发送前置校验 canSend(cardId, actionType)(全部查已有表,零额外成本)
- 收件地址在 suppression → 拒发
- 该用户 7 天内已有 SENT 运营邮件 → 拒发(全局频次)
- 同卡同 action_type 30 天内已发 → 拒发(冷却)
- 该卡 7 天内已被代理触达 → 平台不发(权属去重,代理动作也须写 action_log)
- 同卡命中多名单 → 只发最高优先级(补件>激活>首刷>唤醒)
- 卡 LOCKED/SUSPENDED/风控审查中 → 拒发 拦截的记
send_status='SKIPPED'入 action_log(留痕,不进响应率分母)。
6.2 不做 A/B 分组(v2.1 决策)
- 所有动作 100% 发送,不留对照组、代码不做 hash 分组——生命周期触发邮件是基本功,不是实验对象;当前体量 uplift 也测不出统计显著
- 文案好坏用同 action_type 不同模板版本的响应率/点击率对比粗判(非严格实验,够用)
- 恢复条件见 2.4:接入付费激励需算 ROI 时再加回
6.3 写入位置
- 系统触达:调
IMessageSendCenterService处同步 INSERT action_log(渲染模板记 template_id) - 人工/代理动作:eladmin 与 wallet-pc 的"触达/标记回访"按钮 → POST /ops/action/log
- 触达权属:t_agency 维度配置触达策略(平台代发/代理自发/双发仲裁),上线前与头部代理通气
7. 分阶段路线(与指标文档 v2.0 第 5 章一致)
| 阶段 | 交付 | 表 | 任务 | 接口 |
|---|---|---|---|---|
| P0 | 大小写终确认 | — | — | — |
| P1 | 实时漏斗 + 六张行动名单 | 无 | 无 | funnel, list/{type}, retention |
| P2 | EDM 触达闭环 + 响应率监控 | action_log, email×3 | 无 | action/log, action/stats, email/*, 公网×3 |
| P3 | 代理记分卡 + 排行 + 生命周期 | agent_daily | OpsAgentDailyTask | agent/rank, agent/lifecycle |
| P4 | 对账 + 水位线 | — | OpsReconcileTask | —(告警走消息中心) |
| 后期 | 归因(接激励时)/异常引擎/趋势表/卡快照/RFM/wallet-pc | 见 2.4 | 视体量 | 复用+agencyId 过滤 |
8. 风险与注意事项
- 大小写:上线前实测一次,错则所有交易指标归零。
- 成熟期偏差:cohort 接口必须只返回"已成熟"的 cohort(申请日 ≤ 今天−窗口),否则最近一周永远"看起来在恶化"。
- 小样本:比率类展示时分母 <30 标灰不参与排名/告警。
- 响应率误读:响应率含自然转化,只作运营健康监控,不得当"动作净贡献/ROI"汇报——展示处固定标注。
- GMV 双计:charge 与 topup 分列,任何"总额"展示不得相加;北极星只认 charge。
- 触达权属:平台直触前必须过代理策略配置——这是渠道政治问题,技术挡不住投诉。
- EDM 合规:退订链接硬底线;运营邮件被举报会殃及同域名的验证码/通知邮件;像素打开率只看趋势;送达痛点直接换 ESP 不自建。
- 银盛:上线前交易 SQL 扩为 UTGL UNION 银盛表,预留 channel 维度。
- agency 快照:action_log 与 agent_daily 都存当时的 agency_id,防换代理导致历史错乱。
- 时区:stat_date 与交易 create_time 口径统一(现有任务用服务器本地日期)。
- DDL:无 Flyway,脚本入
docs/sql/(stat_*.sql / ops_*.sql,按表前缀命名),发布手动执行并记录。
9. 交付物汇总 v2.1
- 新增表 5 张:t_stat_agent_daily(只存计数)、t_ops_action_log(含 template/send_status/incentive 列,无对照组列)、t_ops_email_template、t_ops_email_event、t_ops_email_suppression
- 定时任务 2 个:OpsAgentDailyTask / OpsReconcileTask(对账+水位线)
- 实时接口为主:漏斗/名单/留存/响应率全部直查源表(可回算),不建预聚合表、不建归因结果表
- 公网追踪 3 端点:/t 点击 302、/e/o 像素、/e/unsub 退订(wallet-front-api,HMAC 验签)
- 接入:canSend 六条前置校验(含代理触达去重)+ 模板渲染,所有动作 100% 发送
- 已砍/后置:action_result+对照组/归因(恢复条件=接付费激励)、card_daily、funnel_daily、retention_cohort、anomaly 两表、RFM、时延 P90(详见 2.4 与指标文档 6.1)
本方案贴合现有代码约定,可直接拆分为 P1-P4 工程任务排期。