Просмотр исходного кода

docs(kb/27): dwd_pay 金额派生通式 + 业界全名命名(_amt_cny)+ 删 8 个状态/异步字段

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
tianyu.chu 4 дней назад
Родитель
Сommit
4d106f3081
2 измененных файлов с 60 добавлено и 32 удалено
  1. 59 32
      kb/27-dwd建模.md
  2. 1 0
      kb/92-重构进度.md

+ 59 - 32
kb/27-dwd建模.md

@@ -16,7 +16,7 @@
 
 
 ### 1.3 维度退化策略
 ### 1.3 维度退化策略
 
 
-DWD 直引 DIM 层已清洗字段,**不在 DWD 二次清洗、不做空值兜底**。如 `cgi.sport` 脏数据归一在 `dim_trd_card_group_ful_d` 完成(见 `28-dim建模.md §3.2`),DWD pay 维度退化时直引 DIM 已清洗的 `sport`。
+DWD 直引 DIM 层已清洗字段,**不在 DWD 二次清洗、不做空值兜底**。如 `cgi.sport` 脏数据归一在 `dim_trd_card_group_ful_d` 完成(见 `28-dim建模.md §3.2`),DWD pay 维度退化时直引 DIM 已清洗的 `category`。
 
 
 ### 1.4 分区与写入
 ### 1.4 分区与写入
 
 
@@ -56,18 +56,51 @@ WHERE order_type = 'group'
 
 
 DWD 不二次清洗 category(`dim_trd_card_group_ful_d` 已归一脏数据)。**不带大类 `main_category`**(业务侧定:拼团大类 lv1 业务库原名 `first_sport`,1 期不引,2 期再讨论)。
 DWD 不二次清洗 category(`dim_trd_card_group_ful_d` 已归一脏数据)。**不带大类 `main_category`**(业务侧定:拼团大类 lv1 业务库原名 `first_sport`,1 期不引,2 期再讨论)。
 
 
-### 2.5 accounts_payable_cny 派生规则
+### 2.5 金额字段 mer_act% 修正规则
 
 
-业务库分/元单位混存,按 `point_type` 区分换算:
+业务库 `card_group_order_info` 部分金额字段在 `point_type LIKE 'mer_act%'` 时为分单位(积分支付场景),其他时为元单位。DWD 按下表派生统一为元单位(`_amt_cny` 后缀)。
+
+**通式**:
+
+```sql
+CASE
+    WHEN cgoi.point_type LIKE 'mer_act%' THEN ROUND(cgoi.{业务库字段} / 100.00, 2)
+    ELSE cgoi.{业务库字段}
+END AS {dwd 字段}
+```
+
+**派生映射**:
+
+| 业务库字段 | DWD 字段 | 公式 | 业务含义 |
+|---|---|---|---|
+| accounts_payable | payable_amt_cny | 通式 | **GMV** |
+| actual_payment / point | pay_amt_cny | 特例(见下)| **Net Revenue(购买偏好口径)**|
+| trade_amount | trade_amt_cny | 通式 | 订单交易金额 |
+| settlement_amount | settle_amt_cny | 通式 | 结算金额(支付那一刻 = 实付,_apd_d 不更新)|
+| card_price | card_price_cny | 通式 | 单价应付 |
+| act_price | act_price_cny | 通式 | 单价实付 |
+| discount | discount_amt_cny | 通式 | 商家优惠券抵扣 |
+| platform_discount | platform_discount_amt_cny | 通式 | 平台券抵扣 |
+| act_discount | act_discount_amt_cny | 通式 | 活动折扣(首单等)|
+| member_discount | member_discount_amt_cny | 通式 | 会员折扣 |
+| point_deduct | point_deduct_amt_cny | 通式 | 积分抵扣金额 |
+
+**Net Revenue 特例**(mer_act% 时换字段,不是同字段除 100):
 
 
 ```sql
 ```sql
 CASE
 CASE
-    WHEN cgoi.point_type LIKE 'mer_act%' THEN ROUND(cgoi.accounts_payable / 100.00, 2)
-    ELSE cgoi.accounts_payable
-END AS accounts_payable_cny
+    WHEN cgoi.point_type LIKE 'mer_act%' THEN ROUND(cgoi.point / 100.00, 2)
+    ELSE cgoi.actual_payment
+END AS pay_amt_cny
 ```
 ```
 
 
-`point_type LIKE 'mer_act%'` 时除 100,其他直取。**DWD 内置该规则,下游不再处理**。GMV 口径走该字段。
+mer_act% 时用 `point/100`(积分支付场景:积分本质是分单位价格),其他时用 `actual_payment`。
+
+**直取不派生**:`shipping_cost` → `shipping_amt_cny`、`shipping_free_amount` → `shipping_free_amt_cny`(业务侧未给修正逻辑,按元单位直取)。
+
+**待业务确认**:`discount_amount` 业务库注释仅"折扣金额",与 `discount` 语义区别 + 是否需 mer_act% 修正 + 最终命名待业务答复。
+
+DWD 内置以上派生规则,下游不再处理。
 
 
 ### 2.6 字段表
 ### 2.6 字段表
 
 
@@ -88,22 +121,22 @@ END AS accounts_payable_cny
 | 拼团维度退化 | sets | STRING | dim_trd_card_group | 系列 |
 | 拼团维度退化 | sets | STRING | dim_trd_card_group | 系列 |
 | 拼团维度退化 | year | STRING | dim_trd_card_group | 年份(赛季)|
 | 拼团维度退化 | year | STRING | dim_trd_card_group | 年份(赛季)|
 | 收货 | shipping_address_id | BIGINT | order | |
 | 收货 | shipping_address_id | BIGINT | order | |
-| 数量 | purchase_count | BIGINT | order | 购买份数 |
-| 数量 | give_num | BIGINT | order | 赠送个数 |
-| 金额 | accounts_payable_cny | DECIMAL(20,4) | 派生(见 §2.5)| **GMV 字段** |
-| 金额 | actual_payment_cny | DECIMAL(20,4) | order.actual_payment | 实付款(GMV 不用此字段,保留作冗余)|
-| 金额 | trade_amount_cny | DECIMAL(20,4) | order | 订单交易金额 |
-| 金额 | settlement_amount_cny | DECIMAL(20,4) | order | 结算金额(实付-退款)|
-| 金额 | card_price_cny | DECIMAL(20,4) | order | 应付款(拼团时点价)|
-| 金额 | act_price_cny | DECIMAL(20,4) | order | 应付款(活动价)|
-| 金额 | discount_cny | DECIMAL(20,4) | order | 折扣 |
-| 金额 | discount_amount_cny | DECIMAL(20,4) | order | 折扣金额 |
-| 金额 | platform_discount_cny | DECIMAL(20,4) | order | 平台券折扣 |
-| 金额 | member_discount_cny | DECIMAL(20,4) | order | 会员折扣 |
-| 金额 | act_discount_cny | DECIMAL(20,4) | order | 活动折扣 |
-| 金额 | point_deduct_cny | DECIMAL(20,4) | order | 积分抵扣金额 |
-| 金额 | shipping_cost_cny | DECIMAL(20,4) | order | 运费 |
-| 金额 | shipping_free_amount_cny | DECIMAL(20,4) | order | 运费券金额 |
+| 数量 | purchase_cnt | BIGINT | order.purchase_count | 购买份数 |
+| 数量 | give_cnt | BIGINT | order.give_num | 赠送个数 |
+| 金额 | payable_amt_cny | DECIMAL(20,4) | 派生 accounts_payable(见 §2.5)| **GMV** |
+| 金额 | pay_amt_cny | DECIMAL(20,4) | 派生 actual_payment / point(见 §2.5)| **Net Revenue(购买偏好口径)**|
+| 金额 | trade_amt_cny | DECIMAL(20,4) | 派生 trade_amount | 订单交易金额 |
+| 金额 | settle_amt_cny | DECIMAL(20,4) | 派生 settlement_amount | 结算金额(支付那一刻=实付,_apd_d 不更新)|
+| 金额 | card_price_cny | DECIMAL(20,4) | 派生 card_price | 单价应付 |
+| 金额 | act_price_cny | DECIMAL(20,4) | 派生 act_price | 单价实付 |
+| 金额 | discount_amt_cny | DECIMAL(20,4) | 派生 discount | 商家优惠券抵扣 |
+| 金额 | platform_discount_amt_cny | DECIMAL(20,4) | 派生 platform_discount | 平台券抵扣 |
+| 金额 | member_discount_amt_cny | DECIMAL(20,4) | 派生 member_discount | 会员折扣 |
+| 金额 | act_discount_amt_cny | DECIMAL(20,4) | 派生 act_discount | 活动折扣 |
+| 金额 | point_deduct_amt_cny | DECIMAL(20,4) | 派生 point_deduct | 积分抵扣金额 |
+| 金额 | shipping_amt_cny | DECIMAL(20,4) | order.shipping_cost | 运费(直取不派生)|
+| 金额 | shipping_free_amt_cny | DECIMAL(20,4) | order.shipping_free_amount | 运费券金额(直取不派生)|
+| 金额 | discount_amount ❓ | DECIMAL(20,4) | order.discount_amount | 折扣金额(业务库注释仅"折扣金额"未明确,与 discount 语义区别 + 是否派生 + 最终命名待业务答复)|
 | 积分 | point | BIGINT | order | 消耗积分 |
 | 积分 | point | BIGINT | order | 消耗积分 |
 | 积分 | discount_point | BIGINT | order | 折扣积分 |
 | 积分 | discount_point | BIGINT | order | 折扣积分 |
 | 优惠券 | coupon | BIGINT | order | 优惠券 id |
 | 优惠券 | coupon | BIGINT | order | 优惠券 id |
@@ -119,20 +152,14 @@ END AS accounts_payable_cny
 | 业务标记 | order_type | STRING | order | 订单类型 |
 | 业务标记 | order_type | STRING | order | 订单类型 |
 | 业务标记 | order_sub_type | STRING | order | 订单子类型(含赠与)|
 | 业务标记 | order_sub_type | STRING | order | 订单子类型(含赠与)|
 | 业务标记 | give_user_code | STRING | order | 赠与人 |
 | 业务标记 | give_user_code | STRING | order | 赠与人 |
-| 业务标记 | status | BIGINT | order | 订单状态 |
-| 业务标记 | serve_status | BIGINT | order | 业务状态 |
-| 业务标记 | refuse_status | TINYINT | order | 是否接受累积发货 |
-| 业务标记 | invoice_id | BIGINT | order | 发票记录 id |
 | 业务标记 | anonymous | BOOLEAN | order | 是否匿名 |
 | 业务标记 | anonymous | BOOLEAN | order | 是否匿名 |
 | 业务标记 | pick_up_type | STRING | order | 提货方式 |
 | 业务标记 | pick_up_type | STRING | order | 提货方式 |
 | 业务标记 | point_type | STRING | order | 使用积分类型 |
 | 业务标记 | point_type | STRING | order | 使用积分类型 |
-| 业务标记 | open_self | TINYINT | order | 是否商家代拆 |
-| 业务标记 | goods_allocate | TINYINT | order | 卡密是否分配 |
-| 风控 | waring_type | STRING | order | 风险异常类型 |
-| 风控 | waring_status | TINYINT | order | 风险异常状态 |
+| 业务标记 | open_self | TINYINT | order | 是否商家代拆(默认 0,1=商家待拆)|
 | 时间 | order_create_time | TIMESTAMP | order.create_time | 下单时间 |
 | 时间 | order_create_time | TIMESTAMP | order.create_time | 下单时间 |
-| 时间 | order_update_time | TIMESTAMP | order.update_time | 订单最近更新时间 |
 | 时间 | expire_time | TIMESTAMP | order | 过期时间 |
 | 时间 | expire_time | TIMESTAMP | order | 过期时间 |
 | 软删 | is_deleted | BOOLEAN | order | 软删归一 |
 | 软删 | is_deleted | BOOLEAN | order | 软删归一 |
 | 框架 | etl_time | TIMESTAMP | 派生 | ETL 处理时间 |
 | 框架 | etl_time | TIMESTAMP | 派生 | ETL 处理时间 |
 | 分区 | dt | STRING | `DATE(payment_success_time)` | **业务时间分区**|
 | 分区 | dt | STRING | `DATE(payment_success_time)` | **业务时间分区**|
+
+> **未纳入字段**(按事件不可变设计,归 `dim_trd_order_zip_d` 拉链或后续业务事件表):`status` / `serve_status` / `update_time` / `refuse_status` / `waring_type` / `waring_status` / `goods_allocate` / `invoice_id`。

+ 1 - 0
kb/92-重构进度.md

@@ -179,6 +179,7 @@
 | 2026-04-22 | **勘误:移除 "SQL SET 资源类参数不生效" 的错误表述**:A.4 落地时误把 "`SparkSession.conf.set()` 对已启动 session 不改变资源" 这条 Spark 固有行为类推到"SQL 里的 SET"。实际 `spark_sql.py:query()` L433-440 对 SQL 内 `SET spark.*` 做预扫描,在 session 启动前塞进 `_final_spark_config`,`__init_spark_session` 通过 `builder.config()` 写入后才 `getOrCreate()`——**资源类 SET 同样生效**。清理 4 处错误:`dw_base/spark/spark_sql.py:155` 注释、`conf/spark-tuning.conf` L6-7 注意段、`kb/90 §2.3` "落地坑"段 + L2 行括号、本文 L74 基于错误前提的验证项 | — |
 | 2026-04-22 | **勘误:移除 "SQL SET 资源类参数不生效" 的错误表述**:A.4 落地时误把 "`SparkSession.conf.set()` 对已启动 session 不改变资源" 这条 Spark 固有行为类推到"SQL 里的 SET"。实际 `spark_sql.py:query()` L433-440 对 SQL 内 `SET spark.*` 做预扫描,在 session 启动前塞进 `_final_spark_config`,`__init_spark_session` 通过 `builder.config()` 写入后才 `getOrCreate()`——**资源类 SET 同样生效**。清理 4 处错误:`dw_base/spark/spark_sql.py:155` 注释、`conf/spark-tuning.conf` L6-7 注意段、`kb/90 §2.3` "落地坑"段 + L2 行括号、本文 L74 基于错误前提的验证项 | — |
 | 2026-05-09 | **kb/27-dwd建模.md + kb/28-dim建模.md 新建(用户标签 1 期建模落地)**:基于 tdm.md 标签清单(用户属性 7 个 + 16 品类 × 3 窗口 × 2 度量)倒推得 3 张表。kb/27 含 `dwd_trd_order_pay_apd_d`(订单支付明细,业务过程定义 / 粒度 / `payment_success_time` 业务时间分区 / 维度退化字段来源 / `accounts_payable_cny` 含 `point_type LIKE 'mer_act%'` 时除 100 的换算规则 / 字段表);kb/28 含 `dim_usr_user_ful_d`(base_user LEFT JOIN cert_info,性别/生日仅取 cert)与 `dim_trd_card_group_ful_d`(拼团维度,`sport` 内置脏数据清洗 mlb→MLB / Boxing→UFC / other→NULL,下游直引)。通用约定:DWD 单分区 `dt=T-1` 不回算(ODS 漂移已在 ODS 层归位);DIM 跑批初始化扫全量 + ROW_NUMBER 取最新行、日常增量"昨日 dim + 今日 ods"按 pk 合并去重;脏数据清洗位置统一放 DIM 层。**关键决策点**:1 期品类直取 `cgi.sport`(推翻早先"panini 是品类权威源"的草稿结论),不引产品表(panini / checklist / version_config 1 期不建对应 dim 表),业务依据是拼团可单品/组合品跨类,按拼团大类聚合贴近业务侧分析口径;C3 字段圈定 `id_card`(已 md5)/ `code` 纳入,`live_anonymous` / 通知三件套 / `live_config_json` 不纳入。**不入 kb 的**:1 期不实施的 `dwd_trd_order_refund_apd_d`(A3 锁不做退款扣减,2 期实施时再补 kb 章节)+ `dim_prd_checklist_ful_d`。设计稿留 `workspace/20260508/dwd_dim_draft.md`(含 1 期倒推链路 / refund 字段草稿 / 待办依赖等过程性内容,2 期实施时回查)。README §2x 数仓建模索引加 27 / 28 两行 | — |
 | 2026-05-09 | **kb/27-dwd建模.md + kb/28-dim建模.md 新建(用户标签 1 期建模落地)**:基于 tdm.md 标签清单(用户属性 7 个 + 16 品类 × 3 窗口 × 2 度量)倒推得 3 张表。kb/27 含 `dwd_trd_order_pay_apd_d`(订单支付明细,业务过程定义 / 粒度 / `payment_success_time` 业务时间分区 / 维度退化字段来源 / `accounts_payable_cny` 含 `point_type LIKE 'mer_act%'` 时除 100 的换算规则 / 字段表);kb/28 含 `dim_usr_user_ful_d`(base_user LEFT JOIN cert_info,性别/生日仅取 cert)与 `dim_trd_card_group_ful_d`(拼团维度,`sport` 内置脏数据清洗 mlb→MLB / Boxing→UFC / other→NULL,下游直引)。通用约定:DWD 单分区 `dt=T-1` 不回算(ODS 漂移已在 ODS 层归位);DIM 跑批初始化扫全量 + ROW_NUMBER 取最新行、日常增量"昨日 dim + 今日 ods"按 pk 合并去重;脏数据清洗位置统一放 DIM 层。**关键决策点**:1 期品类直取 `cgi.sport`(推翻早先"panini 是品类权威源"的草稿结论),不引产品表(panini / checklist / version_config 1 期不建对应 dim 表),业务依据是拼团可单品/组合品跨类,按拼团大类聚合贴近业务侧分析口径;C3 字段圈定 `id_card`(已 md5)/ `code` 纳入,`live_anonymous` / 通知三件套 / `live_config_json` 不纳入。**不入 kb 的**:1 期不实施的 `dwd_trd_order_refund_apd_d`(A3 锁不做退款扣减,2 期实施时再补 kb 章节)+ `dim_prd_checklist_ful_d`。设计稿留 `workspace/20260508/dwd_dim_draft.md`(含 1 期倒推链路 / refund 字段草稿 / 待办依赖等过程性内容,2 期实施时回查)。README §2x 数仓建模索引加 27 / 28 两行 | — |
 | 2026-05-09 | **kb/27 + kb/28 sport→category(lv2 品类字段命名拍板)+ workspace 三份工作稿标已部分作废**:(a) 品类字段命名按 kb/21 §1 一词一义原则,业务库 sport 实际多义(16 值里"影视收藏 / 综合收藏 / 综合体育"等非 sport 语义),数仓字段改用中性的 `category`;lv1 大类(业务库 `first_sport`,1 期不引)未来引入命名 `main_category`;改动点 kb/28 §3.2(命名说明 + 清洗规则措辞)+ §3.3 字段表 sport 行 + kb/27 §2.4 维度退化表 + 末段 + §2.6 字段表 sport 行。(b) `workspace/20260508/` 下 `dws_tdm_draft.md` / `dwd_modeling_questions.md` / `industry_practice_review.md` 顶部加"已部分作废 2026-05-09"段,建模结论以 kb/27 + kb/28 为准;workspace 在 .gitignore 不入 commit,本地档案保留作设计推理过程档案。 | — |
 | 2026-05-09 | **kb/27 + kb/28 sport→category(lv2 品类字段命名拍板)+ workspace 三份工作稿标已部分作废**:(a) 品类字段命名按 kb/21 §1 一词一义原则,业务库 sport 实际多义(16 值里"影视收藏 / 综合收藏 / 综合体育"等非 sport 语义),数仓字段改用中性的 `category`;lv1 大类(业务库 `first_sport`,1 期不引)未来引入命名 `main_category`;改动点 kb/28 §3.2(命名说明 + 清洗规则措辞)+ §3.3 字段表 sport 行 + kb/27 §2.4 维度退化表 + 末段 + §2.6 字段表 sport 行。(b) `workspace/20260508/` 下 `dws_tdm_draft.md` / `dwd_modeling_questions.md` / `industry_practice_review.md` 顶部加"已部分作废 2026-05-09"段,建模结论以 kb/27 + kb/28 为准;workspace 在 .gitignore 不入 commit,本地档案保留作设计推理过程档案。 | — |
+| 2026-05-09 | **kb/27 §2.5 + §2.6 大改:dwd_pay 金额派生通式 + 业界全名命名 + 删 8 个状态/异步字段**:(a) §2.5 改"金额字段 mer_act% 修正规则":通式 `CASE WHEN point_type LIKE 'mer_act%' THEN ROUND(/100.00, 2) ELSE 直取 END` + 11 字段派生映射(accounts_payable→payable_amt_cny GMV / actual_payment+point→pay_amt_cny Net Revenue 特例 / trade_amount / settlement_amount / card_price / act_price / discount / platform_discount / act_discount / member_discount / point_deduct)+ Net Revenue 特例(mer_act% 时换字段用 point/100,非同字段除 100)+ 2 字段直取(shipping_cost / shipping_free_amount)+ 1 字段待业务确认(discount_amount 与 discount 区别)。(b) §2.6 字段表按业界全名 + `_amt_cny` 后缀重命名(pay_amt_cny / payable_amt_cny / settle_amt_cny / discount_amt_cny / platform_discount_amt_cny / member_discount_amt_cny / act_discount_amt_cny / point_deduct_amt_cny / shipping_amt_cny / shipping_free_amt_cny / trade_amt_cny),数量改 cnt 词根(purchase_cnt / give_cnt);按 Kimball / OneData "DWD 事件表只装事件本身的事实快照"原则删 8 字段(status / serve_status / update_time / refuse_status / waring_type / waring_status / goods_allocate / invoice_id),归 dim_trd_order_zip_d 拉链或后续业务事件表;保留 open_self(业务侧待确认归处,本期暂留);settlement_amount 保留并派生为 settle_amt_cny(_apd_d 不被退款改写)。 | — |
 | 2026-04-22 | **仓库改名 `tendata-warehouse-release` → `poyee-data-warehouse` 收尾**:项目根目录由用户手动改名完成;代码侧 `dw_base/utils/file_utils.py:9` + `dw_base/utils/hdfs_merge_small_file.py:7` 两处 `re.sub(r"tendata-warehouse.*", ...)` 字面量同步更新。`.idea/*.iml` / `modules.xml` / `workspace.xml` 因 `.idea` + `*.iml` 在 `.gitignore`,属本地 IDE 状态,不入库亦不影响运行(老 `tendata-warehouse-release.iml` + modules.xml / workspace.xml 里的 module name 残留不处理)。联动 kb/90 §1.1 L88 表格行打勾 + §2.3 末尾"与仓库改名的联动"段压缩为一行记录 | — |
 | 2026-04-22 | **仓库改名 `tendata-warehouse-release` → `poyee-data-warehouse` 收尾**:项目根目录由用户手动改名完成;代码侧 `dw_base/utils/file_utils.py:9` + `dw_base/utils/hdfs_merge_small_file.py:7` 两处 `re.sub(r"tendata-warehouse.*", ...)` 字面量同步更新。`.idea/*.iml` / `modules.xml` / `workspace.xml` 因 `.idea` + `*.iml` 在 `.gitignore`,属本地 IDE 状态,不入库亦不影响运行(老 `tendata-warehouse-release.iml` + modules.xml / workspace.xml 里的 module name 残留不处理)。联动 kb/90 §1.1 L88 表格行打勾 + §2.3 末尾"与仓库改名的联动"段压缩为一行记录 | — |
 | 2026-04-21 | **聚簇 A.4 Spark 参数外配 + `spark_sql.py` 三级覆盖**:按业务调整频率拆两文件入库 —— `conf/spark-defaults.conf`(12 条底层行为/开关类,初始化后少改:`spark.sql.adaptive/broadcastTimeout/codegen/arrow*/files/statistics.*` + `spark.dynamicAllocation.enabled` + `spark.files.ignoreCorruptFiles` + `spark.debug.maxToStringFields` + `spark.port.maxRetries` + `hive.exec.orc.default.block.size`)+ `conf/spark-tuning.conf`(10 条资源/并行度/队列,业务早期常改:`spark.{driver,executor}.{memory,cores}` + `spark.executor.instances` + `spark.executor.memoryOverhead` + `spark.driver.maxResultSize` + `spark.default.parallelism` + `spark.sql.shuffle.partitions` + `spark.yarn.queue`)。`dw_base/spark/spark_sql.py` 改造:(a) 模块级新增 `_load_spark_conf_file(path)`,读 Spark 原生 `key value` 格式,支持 `#` 注释与空行,文件缺失返回 `{}` 容错单测;(b) `__init__` 10 个 tuning 相关构造参数默认值 `'2g' / 200 / ...` → `Optional[...] = None` sentinel,不破坏既有调用点显式传参;(c) `__init_spark_session` 原 22 条硬编码 `.config(...)` 链替换为三段:L1 先 `spark-defaults.conf` 后 `spark-tuning.conf`(相同 key tuning 覆盖 defaults)→ L2 `self._final_spark_config`(SQL 内 SET)→ L3 构造参数非 None 项 + `extra_spark_config`(L3 内 extra 覆盖 named),保持原"extra > SQL SET > named" 的向后兼容;日志分层打 `L1/L2/L3` 前缀便于排查。联动:`kb/90 §2.2` conf 结构加 `spark-tuning.conf` + `§2.3` 改写为两文件模型(去单文件草案)+ 删"坑 2"(B1 → A2 依赖边)+ 聚簇 L59 依赖边删 | — |
 | 2026-04-21 | **聚簇 A.4 Spark 参数外配 + `spark_sql.py` 三级覆盖**:按业务调整频率拆两文件入库 —— `conf/spark-defaults.conf`(12 条底层行为/开关类,初始化后少改:`spark.sql.adaptive/broadcastTimeout/codegen/arrow*/files/statistics.*` + `spark.dynamicAllocation.enabled` + `spark.files.ignoreCorruptFiles` + `spark.debug.maxToStringFields` + `spark.port.maxRetries` + `hive.exec.orc.default.block.size`)+ `conf/spark-tuning.conf`(10 条资源/并行度/队列,业务早期常改:`spark.{driver,executor}.{memory,cores}` + `spark.executor.instances` + `spark.executor.memoryOverhead` + `spark.driver.maxResultSize` + `spark.default.parallelism` + `spark.sql.shuffle.partitions` + `spark.yarn.queue`)。`dw_base/spark/spark_sql.py` 改造:(a) 模块级新增 `_load_spark_conf_file(path)`,读 Spark 原生 `key value` 格式,支持 `#` 注释与空行,文件缺失返回 `{}` 容错单测;(b) `__init__` 10 个 tuning 相关构造参数默认值 `'2g' / 200 / ...` → `Optional[...] = None` sentinel,不破坏既有调用点显式传参;(c) `__init_spark_session` 原 22 条硬编码 `.config(...)` 链替换为三段:L1 先 `spark-defaults.conf` 后 `spark-tuning.conf`(相同 key tuning 覆盖 defaults)→ L2 `self._final_spark_config`(SQL 内 SET)→ L3 构造参数非 None 项 + `extra_spark_config`(L3 内 extra 覆盖 named),保持原"extra > SQL SET > named" 的向后兼容;日志分层打 `L1/L2/L3` 前缀便于排查。联动:`kb/90 §2.2` conf 结构加 `spark-tuning.conf` + `§2.3` 改写为两文件模型(去单文件草案)+ 删"坑 2"(B1 → A2 依赖边)+ 聚簇 L59 依赖边删 | — |
 | 2026-04-22 | **datasource 扁平化 + db_type 按父目录段判定(方向反转)**:反转 2026-04-15 起立项的"一套代码跑多环境"设计(kb/90 原 §2.5:`-env` 参数 + `datasource/{db_type}/{env}/{instance}.ini` 分层 + env 注入)。实际前期跨环境同步是常态(test 业务库 → prod HDFS),"全局 env"概念不成立。新方案:(a) source ini 落位 `datasource/{db_type}/{env}-{实例简称}.ini`,env 写进文件名(env ∈ dev/test/prod),扁平组织;(b) sync ini 内 `dataSource = {db_type}/{env}-{实例简称}`(强制带 db_type 前缀;旧裸名 `hdfs-ha` 不再支持);(c) 代码侧 `dw_base/datax/plugins/plugin.py:37` + `plugin_factory.py:34` 的 db_type 提取从"文件名按 `-` 切首词"改为"按 `/` 切首段"(父目录名)—— 旧算法在新文件名出现 `-` 时会把 `env` 误判为 db_type,必须改。联动:kb/00 §1 目录树重绘(去 env 子目录,加样例 `prod-hobby.ini` / `test-hobby.ini` / `prod-ha.ini` 等 + sync ini 引用形式说明段)+ §6.1 配置分类表落位描述、kb/02 §4 同步、kb/21 §3.9 过时 3 行修正(废 `-env` 注入表述;引用形式 `{db_type}/{env}-{实例简称}`;JSON 输出路径去 `{env}/` 层)、kb/90 §2.1 硬编码表合并 3 行为 1 行 + §2.5 大幅压缩(三阶段设计整段删,保留路径解耦部分)+ §2.8 末尾表行更新 + §八 状态表合并、kb/92 L90 checklist 落位改扁平。conf/templates/datasource/\*.template.ini × 3 头注释补"落位"+"dataSource 引用"两行。`bin/datax-job-config-generator.py` L5/L114/L115 的路径注释讲的是另一个 `conf/datax/config/` 作业分组层级,与本次 datasource 扁平化无关,不动;`dw_base/datax/plugins/reader/mysql_reader.py:178` `{group}/mysql-{database}` 生成逻辑是非活跃代码(批量采集未启动),留到 §2.7 重构时对齐 | — |
 | 2026-04-22 | **datasource 扁平化 + db_type 按父目录段判定(方向反转)**:反转 2026-04-15 起立项的"一套代码跑多环境"设计(kb/90 原 §2.5:`-env` 参数 + `datasource/{db_type}/{env}/{instance}.ini` 分层 + env 注入)。实际前期跨环境同步是常态(test 业务库 → prod HDFS),"全局 env"概念不成立。新方案:(a) source ini 落位 `datasource/{db_type}/{env}-{实例简称}.ini`,env 写进文件名(env ∈ dev/test/prod),扁平组织;(b) sync ini 内 `dataSource = {db_type}/{env}-{实例简称}`(强制带 db_type 前缀;旧裸名 `hdfs-ha` 不再支持);(c) 代码侧 `dw_base/datax/plugins/plugin.py:37` + `plugin_factory.py:34` 的 db_type 提取从"文件名按 `-` 切首词"改为"按 `/` 切首段"(父目录名)—— 旧算法在新文件名出现 `-` 时会把 `env` 误判为 db_type,必须改。联动:kb/00 §1 目录树重绘(去 env 子目录,加样例 `prod-hobby.ini` / `test-hobby.ini` / `prod-ha.ini` 等 + sync ini 引用形式说明段)+ §6.1 配置分类表落位描述、kb/02 §4 同步、kb/21 §3.9 过时 3 行修正(废 `-env` 注入表述;引用形式 `{db_type}/{env}-{实例简称}`;JSON 输出路径去 `{env}/` 层)、kb/90 §2.1 硬编码表合并 3 行为 1 行 + §2.5 大幅压缩(三阶段设计整段删,保留路径解耦部分)+ §2.8 末尾表行更新 + §八 状态表合并、kb/92 L90 checklist 落位改扁平。conf/templates/datasource/\*.template.ini × 3 头注释补"落位"+"dataSource 引用"两行。`bin/datax-job-config-generator.py` L5/L114/L115 的路径注释讲的是另一个 `conf/datax/config/` 作业分组层级,与本次 datasource 扁平化无关,不动;`dw_base/datax/plugins/reader/mysql_reader.py:178` `{group}/mysql-{database}` 生成逻辑是非活跃代码(批量采集未启动),留到 §2.7 重构时对齐 | — |