Bladeren bron

docs(kb/33+23+29+93+92): 1 期 tdm 概念修正 + ADR-10 跨层下钻 + 字典权威源分离

kb/33 §1-§4: "偏好" → "消费明细统计",命名 usr_pref_trade_* → usr_trade_*,
   数据源 dws → dwd + WHERE order_type='group' + cnt 改 COUNT(DISTINCT);
kb/23 §2: 删 103 行 tag_code 摘要表 + 改链 kb/34;§5 加 §5.1 字典维护规约;
kb/29 §2.1/§2.5: dws 主题去"偏好",标"通用日聚合 1 期暂无下游";
kb/93: 加 ADR-10 TDM 跨层下钻 DWD(业界三方案 A/B/C 对比 + 4 项反悔条件);
kb/92: 加 5/12 changelog 一条.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
tianyu.chu 4 weken geleden
bovenliggende
commit
5345b828bc
5 gewijzigde bestanden met toevoegingen van 89 en 35 verwijderingen
  1. 17 11
      kb/23-标签体系.md
  2. 4 4
      kb/29-dws建模.md
  3. 24 20
      kb/33-tdm建模.md
  4. 0 0
      kb/92-重构进度.md
  5. 44 0
      kb/93-架构决策.md

+ 17 - 11
kb/23-标签体系.md

@@ -72,14 +72,7 @@ DWD / DWS(事实 + 维度)
 - 所有标签统一口径管理,便于建设标签字典元数据
 - 支持按 `tag_code` 灵活查询任意标签组合
 
-**1 期落地标签清单**(用户域共 103 个 tag_code,详见 kb/33 §2.4 + §3.4):
-
-| 表 | 类别 | tag_code 数 | 内容 |
-|---|---|---|---|
-| `tdm_usr_tag_d` | attr 属性 | 7 | usr_level / usr_is_cert / usr_sex / usr_city / usr_register_time / usr_birth_month / usr_generation |
-| `tdm_usr_tag_d` | stat 偏好 30d | 32 | 16 品类 × {amt, cnt},`usr_pref_trade_{category}_{amt,cnt}_30d` |
-| `tdm_usr_tag_d` | stat 偏好 y{当年} | 32 | 16 品类 × {amt, cnt},`usr_pref_trade_{category}_{amt,cnt}_y{当年}`(1 期当年=2026)|
-| `tdm_usr_tag_o` | stat 偏好 y{往年} | 32 / 凝固年 | 16 品类 × {amt, cnt},`usr_pref_trade_{category}_{amt,cnt}_y{往年}`(1 期凝固 y2025)|
+**1 期落地标签清单**:用户域共 103 个 tag_code(7 attr + 64 stat 日更 + 32 stat 25 年凝固),详细 tag_code + 业务价值 + 计算口径 + 示例值见 [kb/34-标签字典.md](34-标签字典.md)。
 
 ## 3. 核心标签宽表
 
@@ -146,19 +139,32 @@ GROUP BY entity_id
 
 所有 `tag_code` 必须在标签字典中注册后才能写入长表,确保编码唯一、口径明确。
 
+**字典权威源**:[kb/34-标签字典.md](34-标签字典.md)(103 个 tag_code 全量 + 编码规则 + 业务价值 + 示例值 + 圈选 SQL 示例)。
+
+**字典元数据 schema(业务字典服务化时可参考)**:
+
 | 字段 | 说明 |
 |------|------|
-| `tag_code` | 全局唯一编码,详细命名约定见 kb/33 §4。属性 `usr_{属性名}`(如 `usr_sex` / `usr_city`);偏好 `usr_pref_{主题}_{category}_{metric}_{window}`(如 `usr_pref_trade_basketball_amt_30d`)|
+| `tag_code` | 全局唯一编码,详细命名约定见 kb/33 §4。属性 `usr_{属性名}`(如 `usr_sex`);消费明细统计 `usr_{主题}_{category}_{metric}_{window}`(如 `usr_trade_Basketball_amt_30d`)|
 | `tag_name` | 中文名称 |
 | `entity_type` | `usr` / `prd` / `shp` |
-| `tag_type` | `categorical` / `numeric` / `boolean` / `json` |
+| `tag_type` | `attr` / `stat` / `rule`(预留 `algo`)|
 | `compute_logic` | 计算口径(SQL 或自然语言) |
 | `source_table` | 上游依赖表 |
 | `update_cycle` | 更新周期 |
 | `owner` | 负责人 |
 | `is_core` | 是否进入宽表 |
 
-> 标签字典前期用 Docmost / 文档维护,后期可建元数据管理模块。
+### 5.1 字典维护规约(kb/34 ↔ kb/33 ↔ SQL 联动)
+
+字典 kb/34 是业务字典权威源,kb/33 是建模约定 + 命名规则。两者按变化频率分层维护:
+
+| 字典改动类型 | 联动改动范围 |
+|---|---|
+| 改命名规则(如 `usr_pref_` → `usr_`)| kb/33 §4 命名约定 + kb/34 全表 + 所有相关 SQL(jobs/tdm/ + manual/backfill/)|
+| 加 / 删 tag_code | kb/34 字典行 + 相关 SQL UNION ALL 段 + kb/23 §2 总数字(若变化)|
+| 改口径 / 业务价值 / 示例值 / 业务校准 | 只改 kb/34(kb/33 不动)|
+| 改 EAV schema 或 _d / _o 分表策略 | kb/33 §1.2 / §1.3 + 重写 SQL + kb/34 提示运营字典刷新 |
 
 ## 6. 标签服务演进路线
 

+ 4 - 4
kb/29-dws建模.md

@@ -47,7 +47,7 @@ DWS 只做 `_1d` 日聚合主题宽表,不爆窗口表(不建 `_30d` / `_y{
 
 ### 2.1 主题
 
-用户 × 品类粒度的日交易聚合,承载偏好标签(金额 / 次数 / 多窗口)的聚合基础
+用户 × 品类粒度的日交易聚合(通用语义,不内嵌业务过滤如 `order_type`)。1 期上层暂无消费方(TDM 跨层下钻 DWD 直接取数,见 kb/93 ADR-10),保留通用聚合语义留作未来 BI / 2 期接入
 
 ### 2.2 粒度
 
@@ -69,9 +69,9 @@ DWD 已完成维度退化(`category` 来自 `dim_trd_card_group_ful_d`,DIM 
 
 **口径要点**:
 
-- `pay_amt_cny`(Net Revenue)= **偏好标签金额口径**。DWD 已内置 `mer_act%` 修正规则(mer_act% 时取 `point/100`,否则取 `actual_payment`),详见 kb/27 §2.5
-- `payable_amt_cny`(GMV)= **业务通用 GMV 口径**,与偏好标签解耦(业务侧 SQL 常用)
-- `pay_order_cnt = COUNT(DISTINCT order_id)` = **偏好标签次数口径**(A2 锁定,不用份数
+- `pay_amt_cny`(Net Revenue)= **消费明细统计金额口径**。DWD 已内置 `mer_act%` 修正规则(mer_act% 时取 `point/100`,否则取 `actual_payment`),详见 kb/27 §2.5
+- `payable_amt_cny`(GMV)= **业务通用 GMV 口径**,与统计标签解耦(业务侧 SQL 常用)
+- `pay_order_cnt = COUNT(DISTINCT order_id)` = **消费明细统计次数口径**(A2 锁定,不用份数;1 期 TDM 跨层下钻 DWD 后用 `COUNT(DISTINCT order_id)` 直接计算
 - `purchase_cnt`(份数)保留作 2 期备用 + 审计,1 期标签不用
 
 **砍掉的字段**(违反"字段要全"原则但 1 期 scope 不扩张优先):

+ 24 - 20
kb/33-tdm建模.md

@@ -26,7 +26,7 @@
 
 **收益**:schema 不绑定标签维度组合;新增维度(如 channel / device 等)只需新 tag_code 命名,**不需 ALTER**。
 
-**代价**:维度切片需 `tag_code LIKE 'usr_pref_trade_%_amt_30d'` 模式匹配;数值标签聚合 / 排序需 `CAST(tag_value AS DECIMAL)`。
+**代价**:维度切片需 `tag_code LIKE 'usr_trade_%_amt_30d'` 模式匹配;数值标签聚合 / 排序需 `CAST(tag_value AS DECIMAL)`。
 
 ### 1.3 表分类:_d 日更 / _o 一次性凝固
 
@@ -34,8 +34,8 @@
 
 | 后缀 | 写入策略 | 含哪些标签 |
 |---|---|---|
-| `_d` | 每天 INSERT OVERWRITE 单分区 dt=T-1 | 属性 snap + 偏好滚动窗口(30d)+ 偏好当年累计(y{当年})|
-| `_o` | 一次性写入永远不动 | 偏好往年总额(y{往年},跨年凝固时新建一张)|
+| `_d` | 每天 INSERT OVERWRITE 单分区 dt=T-1 | 属性 snap + 消费明细滚动窗口(30d)+ 消费明细当年累计(y{当年})|
+| `_o` | 一次性写入永远不动 | 消费明细往年总额(y{往年},同表多 dt 分区凝固,不新建表)|
 
 **为什么分两类**:
 
@@ -51,7 +51,7 @@
 
 ### 1.5 调度依赖
 
-- `_d` sche:DS DEPENDENT 同 dt `dim_usr_user_ful_d`(属性源)+ 同 dt `dws_usr_user_trade_1d`(偏好源
+- `_d` sche:DS DEPENDENT 同 dt `dim_usr_user_ful_d`(属性源)+ 同 dt `dwd_trd_order_pay_apd_d`(消费明细源,1 期跨层下钻 kb/93 ADR-10
 - `_o`:不挂日调度,手跑 once
 
 ---
@@ -60,7 +60,7 @@
 
 ### 2.1 用途
 
-承载 1 期用户标签的日更部分:7 个属性 + 16 品类 × 4 偏好窗口(30d 金额/次数 + y{当年} 金额/次数)。
+承载 1 期用户标签的日更部分:7 个属性 + 16 品类 × 4 消费明细统计窗口(30d 金额/次数 + y{当年} 金额/次数,1 期拼团粒度 `order_type='group'`)。
 
 ### 2.2 粒度
 
@@ -69,8 +69,8 @@
 ### 2.3 来源
 
 - 属性 7 个 ← `dim_usr_user_ful_d WHERE dt='${dt}'`
-- 偏好 30d 金额/次数 ← `dws_usr_user_trade_1d WHERE dt BETWEEN $[yyyyMMdd-30] AND ${dt}` 滚动聚合
-- 偏好 y{当年} 金额/次数 ← `dws_usr_user_trade_1d WHERE dt BETWEEN '{当年}0101' AND ${dt}` 滚动覆盖(1 期当年 = 2026)
+- 消费明细 30d 金额/次数 ← `dwd_trd_order_pay_apd_d WHERE dt BETWEEN $[yyyyMMdd-30] AND ${dt} AND order_type='group' AND category IS NOT NULL` 跨层下钻按 (user_id, category) 重 GROUP BY(kb/93 ADR-10)
+- 消费明细 y{当年} 金额/次数 ← `dwd_trd_order_pay_apd_d WHERE dt BETWEEN '{当年}0101' AND ${dt} AND order_type='group' AND category IS NOT NULL` 同口径滚动覆盖(1 期当年 = 2026)
 
 ### 2.4 tag_code 清单
 
@@ -86,20 +86,22 @@
 | `usr_birth_month` | 生日年月 | dim_usr_user.birthday_cert 派生(未实名 NULL)| `yyyyMM` |
 | `usr_generation` | 出生世代 | dim_usr_user.birthday_cert 派生(按 §6 切片)| 世代名("90后" / "00后" 等)|
 
-#### 偏好类(tag_type=stat,16 品类 × 4 窗口 = 64 个)
+#### 消费明细统计类(tag_type=stat,16 品类 × 4 窗口 = 64 个,1 期拼团粒度
 
 | tag_code 模板 | 描述 | 来源 |
 |---|---|---|
-| `usr_pref_trade_{category}_amt_30d` | 某品类近 30 天金额 | `SUM(pay_amt_cny)` WHERE dt IN [T-30, T-1] |
-| `usr_pref_trade_{category}_cnt_30d` | 某品类近 30 天次数 | `SUM(pay_order_cnt)` 同口径 |
-| `usr_pref_trade_{category}_amt_y2026` | 某品类 26 年累计金额 | `SUM(pay_amt_cny)` WHERE dt IN [20260101, T-1](每天滚动覆盖)|
-| `usr_pref_trade_{category}_cnt_y2026` | 某品类 26 年累计次数 | `SUM(pay_order_cnt)` 同口径 |
+| `usr_trade_{category}_amt_30d` | 某品类近 30 天金额 | `SUM(pay_amt_cny)` from dwd WHERE dt IN [T-30, T-1] + order_type='group' |
+| `usr_trade_{category}_cnt_30d` | 某品类近 30 天次数 | `COUNT(DISTINCT order_id)` 同口径 |
+| `usr_trade_{category}_amt_y2026` | 某品类 26 年累计金额 | `SUM(pay_amt_cny)` from dwd WHERE dt IN [20260101, T-1] + order_type='group'(每天滚动覆盖)|
+| `usr_trade_{category}_cnt_y2026` | 某品类 26 年累计次数 | `COUNT(DISTINCT order_id)` 同口径 |
 
 `{category}` 是 16 品类占位(kb/28 §3.2 DIM 已清洗):Basketball / Soccer / MLB / TCG / PTCG / 影视收藏 / 综合体育 / 综合收藏 / NFL / NHL / UFC / WWE / Tennis / Esports / F1 / Golf。
 
-**金额口径**:`SUM(pay_amt_cny)` = Net Revenue 偏好金额(A3 锁 1 期不扣减退款;GMV = `payable_amt_cny`,偏好 amt 用 pay_amt_cny,见 kb/27 §2.5 / kb/29 §2.5)。
+**金额口径**:`SUM(pay_amt_cny)` = Net Revenue(A3 锁 1 期不扣减退款;GMV = `payable_amt_cny`,统计 amt 用 pay_amt_cny,见 kb/27 §2.5 / kb/29 §2.5)。
 
-**次数口径**:`SUM(pay_order_cnt)` = `COUNT(DISTINCT order_id)`(A2 锁定,1 期不用份数)。
+**次数口径**:`COUNT(DISTINCT order_id)`(A2 锁定,1 期不用份数;dwd 订单粒度需 DISTINCT 而非 SUM)。
+
+> 详细 tag_code 字典 + 业务价值 + 圈选示例见 [kb/34-标签字典.md](34-标签字典.md)。
 
 ### 2.5 字段表
 
@@ -125,16 +127,16 @@
 
 ### 3.3 来源
 
-`dws_usr_user_trade_1d WHERE dt BETWEEN '{凝固年}0101' AND '{凝固年}1231'` 一次性聚合。1 期凝固 25 年用 `[20250101, 20251231]`。
+`dwd_trd_order_pay_apd_d WHERE dt BETWEEN '{凝固年}0101' AND '{凝固年}1231' AND order_type='group' AND category IS NOT NULL` 一次性跨层下钻聚合(kb/93 ADR-10)。1 期凝固 25 年用 `[20250101, 20251231]`。
 
 ### 3.4 tag_code 清单(tag_type=stat,每凝固年 32 个)
 
 | tag_code 模板 | 描述 | 来源 |
 |---|---|---|
-| `usr_pref_trade_{category}_amt_y{凝固年}` | 某品类该年总额 | `SUM(pay_amt_cny)` WHERE dt IN [该年 01-01, 12-31] |
-| `usr_pref_trade_{category}_cnt_y{凝固年}` | 某品类该年总次数 | `SUM(pay_order_cnt)` 同口径 |
+| `usr_trade_{category}_amt_y{凝固年}` | 某品类该年总额 | `SUM(pay_amt_cny)` from dwd WHERE dt IN [该年 01-01, 12-31] + order_type='group' |
+| `usr_trade_{category}_cnt_y{凝固年}` | 某品类该年总次数 | `COUNT(DISTINCT order_id)` 同口径 |
 
-`{category}` 同 §2.4 16 品类。1 期 `{凝固年}=2025`,落 `usr_pref_trade_basketball_amt_y2025` 等 32 个 tag_code。
+`{category}` 同 §2.4 16 品类。1 期 `{凝固年}=2025`,落 `usr_trade_Basketball_amt_y2025` 等 32 个 tag_code。
 
 ### 3.5 dt 锚点 + 写入
 
@@ -154,9 +156,11 @@
 | 类型 | 模板 | 示例 |
 |---|---|---|
 | 属性 | `usr_{属性名}` | `usr_sex` / `usr_city` / `usr_generation` |
-| 偏好 | `usr_pref_{主题}_{category}_{metric}_{window}` | `usr_pref_trade_basketball_amt_30d` / `usr_pref_trade_mlb_cnt_y2025` |
+| 消费明细统计 | `usr_{主题}_{category}_{metric}_{window}` | `usr_trade_Basketball_amt_30d` / `usr_trade_MLB_cnt_y2025` |
+
+**所有维度(品类 / 窗口 / 度量)全部 encode 到 tag_code**。下游按维度切片用 `tag_code LIKE 'usr_trade_%_amt_30d'` 等模式匹配。
 
-**所有维度(品类 / 窗口 / 度量)全部 encode 到 tag_code**。下游按维度切片用 `tag_code LIKE 'usr_pref_trade_%_amt_30d'` 等模式匹配。
+> 详细字段说明 + 业务价值 + 示例值见 [kb/34-标签字典.md](34-标签字典.md)
 
 `{window}` 取值约定:
 

File diff suppressed because it is too large
+ 0 - 0
kb/92-重构进度.md


+ 44 - 0
kb/93-架构决策.md

@@ -395,3 +395,47 @@
   - 业务库延迟 > 1 天的漂移场景频繁出现(如系统升级 / 故障恢复多日数据回流),N 上调到 3 或更大
   - 迁 Spark 3+ / Iceberg / Hudi / Delta Lake 后改 MERGE INTO(更优)
   - 业务侧能严格保证 OLTP 应用层契约(update_time 与事件时间同步刷新),可降到 N=1(实际很难保证)
+
+### ADR-10 TDM 跨层下钻 DWD(1 期专用,dws 单下游场景)
+
+- **状态**:已采纳
+
+  1 期 TDM 用户标签的消费明细统计需按拼团订单(`order_type='group'`)× 消费品类粒度聚合。上游 `dws_usr_user_trade_1d` 是通用日聚合宽表(粒度 `user × category × dt`,按 OneData 范式不内嵌业务过滤 `order_type`),聚合粒度内已混合所有 `order_type`,TDM 层无法 WHERE 反向过滤(信息已聚合丢失)。
+
+  业界主流三种解法:
+
+  - **A. 多维粒度宽表**(OneData / 阿里主流):dws 加 `order_type` 进粒度 = `(user, category, order_type, dt)`,下游各自 WHERE
+  - **B. 分粒度事实**(Kimball-ish):每业务过程一张 dws 表(`dws_usr_user_grouptrade_1d` 等)
+  - **C. 应用层跨层下钻**(OneData 不推但实务常见):dws 不动,应用层从 dwd 二次聚合
+
+  1 期 dws 上层无 BI / ADS 消费方,tdm 是 dws 唯一下游,A/B 的"通用宽表 / 分粒度"价值不成立。
+
+- **决策**:1 期 tdm_usr_tag_d / tdm_usr_tag_o 的 stat 段跨层取数 `dwd_trd_order_pay_apd_d`:
+  - 直接从 dwd 订单粒度按 `(user_id, category)` 重新 GROUP BY
+  - WHERE 显式加 `order_type='group'` + `category IS NOT NULL`
+  - 金额 `SUM(pay_amt_cny)`;次数 `COUNT(DISTINCT order_id)`(dwd 订单粒度,需 DISTINCT 而非 SUM)
+  - `dws_usr_user_trade_1d` 不动,保通用日聚合语义,留作未来 BI / 2 期接入
+
+- **后果**:
+
+  - 正面:
+    - dws 通用聚合语义不污染(不写死 `order_type='group'`),未来多消费方接入零反悔成本
+    - tdm SQL 显式 WHERE 业务过滤,意图清晰
+    - 单下游场景下不为虚构的"通用价值"付多维粒度的复杂度(kb/29 §1.2 1 期数据量级 Spark 单年扫描可接受)
+  - 负面:
+    - 重复聚合(dws 已 GROUP BY 一次,tdm 又 GROUP BY 一次)Spark IO 倍增;1 期数据量级可接受
+    - 长期看是 1 期权宜,演进到下游多消费方时反悔到 A 多维粒度需 dws 重跑全量 + tdm SQL 改回拉 dws
+
+- **候选方案**:
+
+  - **A 多维粒度宽表**(dws 加 order_type 维度):OneData 主流;1 期单下游,dws 行数翻几倍 + dws 重跑全量;Spark IO 反而比跨层下钻多——否决但为反悔目标
+  - **B 分粒度事实**(建 `dws_usr_user_grouptrade_1d` 等多张专表):Kimball-ish;1 期单业务过程无表数爆炸合理性——否决
+  - **dws WHERE order_type='group' 写死**(早期草案):dws 名为通用却按业务过滤,语义错位 + 未来 BI 想看全量订单时反悔——否决
+  - **保持 tdm 从 dws 拉但混所有 order_type**(不加过滤):1 期业务规约是拼团粒度,混合 order_type 数据语义错——否决
+
+- **反悔条件**:
+
+  - BI / ADS 接入 dws 需多 order_type 切片 → 演进到 A 多维粒度宽表,tdm 改回从 dws 拉
+  - dwd 30 天 / 全年扫描 Spark 性能不可接受(1 期数据量级判定失效)
+  - 2 期单买 / 限时 / 活动等其他 `order_type` 同样需要标签 → 演进到 A 多维粒度
+  - 多下游接入 dws 形成"重复聚合 × N"的代价上升 → 演进到 A

Some files were not shown because too many files changed in this diff