ソースを参照

refactor(tests): 冒烟 ini 切到 [mask] 声明式脱敏 + kb 同步

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
tianyu.chu 1 週間 前
コミット
8e4012bce8

+ 2 - 2
kb/90-重构路线.md

@@ -198,13 +198,13 @@ L3   SparkSQL(...) 显式传参  +  extra_spark_config  +  命令行 -sc
 
 **高优先级**(正式 raw 库上线前需要):
 
-- **DataX 字段级脱敏(合规硬约束:敏感值必须不出业务库)**:规范做法 = ini 里 `querySql` 写 SQL 表达式(如 `TO_CHAR(cert_birthday, 'YYYY-MM')`),PG 端执行后返回已脱敏值,敏感原值从未离开业务库。当前已生产可用,`tests/integration/datax/hive_import/app_user_cert_info.ini` 就是该模式。痛点:`querySql` 把列名、WHERE、脱敏规则揉一起,可读性差;改造见低优先级"ini 配置化脱敏"项。**不走** DataX 层 transformer / reader `transform =` 方案——这些路径是原值先进 DataX JVM 再变换,等同"已出业务库再脱",合规退步,本项目不采纳
+- ✅ 2026-04-23 **DataX 字段级脱敏(合规硬约束:敏感值必须不出业务库)**:规范做法 = ini 里 `[mask]` 声明式段(`cert_birthday = month_trunc` 等),代码侧 `dw_base/datax/mask.py` 翻译为 PG `querySql` 内 SQL 表达式下发源库执行,敏感原值从未离开业务库。PG 方言当前支持 5 种脱敏(静态:`month_trunc` / `md5` / `mask_middle`;动态:`keep_first_{n}` / `keep_last_{n}`)。优先级 `手写 querySql > [mask] > table`,手写用于复杂场景 override。**不走** DataX 层 transformer / reader `transform =` 方案——这些路径是原值先进 DataX JVM 再变换,等同"已出业务库再脱",合规退步,本项目不采纳。MySQL / ClickHouse 等其他 reader 按需扩展 `mask.py` 顶层字典
 
 **低优先级**(有明确批量回刷场景再做):
 
 - 日期范围自动按日展开 + N 个 json 分发多 worker(`-start-date 20260401 -stop-date 20260410` → 自动切 10 份 json,每份独立选 worker)
 - **`plugin_factory` 解耦**:当前工厂模块顶层 eager import 所有 reader/writer(mysql/pg/mongo/hdfs/es/kafka/hbase/ch/...),任一 plugin 的 top-level import 破损就连累整个 DataX 链路(2026-04-23 已踩:`dw_base/database/` 删除后 `mysql_reader` 的 `MySQLColumn` import 挂,挂一扯一串)。改造方向二选一:(a) lazy import:`importlib.import_module(f"dw_base.datax.plugins.reader.{db_type}_reader")` 按需加载;(b) registry pattern:每个 reader 用 `@register("postgresql")` 装饰器自注册,工厂解耦于具体 plugin 列表
-- **ini 配置化脱敏(querySql 自动生成)**:ini 层加声明式 `[mask]` 段(`cert_birthday = month_trunc` / `phone = mask_middle` / `id_card = md5` 等),代码侧把声明翻译为 PG `querySql` 里的 SQL 表达式下发,仍走高优先级那条的"PG 端完成脱敏"合规路径(敏感不出库约束不变),开发者免手写冗长 querySql。解决现有 querySql 模式的可读性痛点
+- ✅ 2026-04-23 **ini 配置化脱敏** 已落地 `dw_base/datax/mask.py`(PG 5 种脱敏),详见高优先级"DataX 字段级脱敏"条
 
 **第三条命令 `datax-gc-generator`(ini 元生成器)独立保留**:用户已确认。职责是"从 PG 扫 schema 生成 ini 参考模板",和"执行 ini"不是一回事,不收口到上面两条里。详见 §2.7。
 

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

@@ -194,3 +194,4 @@
 | 2026-04-23 | **kb/00 改名项目导览 + kb/93 补 ADR 模板 + kb/91 入库**:(a) `kb/00-项目架构.md` → `kb/00-项目导览.md`(git mv),文档定位收敛为"新人 / 外部协作者入口";级联改 README(索引行 + 阅读建议 #1)、CLAUDE.md 冷启动必读、kb/02 §4 + kb/30 §6 + kb/90 §1.2 跨文档引用、kb/92 阶段 0 checklist;历史 changelog 里的 `00-项目架构.md` 字面量保留为 snapshot 不改;(b) kb/93 按业界主流(Michael Nygard)补 5 段 ADR 模板(Context / Decision / Consequences / Alternatives / Reversal Trigger);(c) `kb/91-重构备忘.md` 从 untracked 入库(保留现有 DataX 脚本使用说明内容,作为重构期间的独立备忘文件) | — |
 | 2026-04-23 | **kb/90 §2.6 两入口收口按老入口平迁收敛 + 新增 §九 待讨论议题**:(a) §2.6 命名锁定 `datax-hive-import-starter.sh` / `datax-hdfs-export-starter.sh`(对齐 `-starter` 风格);参数表删 6 项新设计(`-dt` / `-skip-exist` / `-force-overwrite` / `-skip-partitions` / `-src-check` / `-env`),改为老参数平迁集,其中 `-env` 本轮不做且未来不规划(datasource 已扁平化);`-c` / `-cd` / `-jc` / `-jcd` + `--override` + 5 个 `xxx_array=()` 不平迁(后两者在老脚本从未激活使用,2026-04-23 查证);实现建议改为 6 个新模块落 `dw_base/datax/` 包内(放宽 B2 前置)、老脚本冒烟 2 通过后整体删不留转发封装;后延 ADR 锚点 1 项(日期范围展开);(b) 新增 §九 待讨论议题锚点,先登记"分布式任务分发"(DS worker × 新入口内部实现)一项 | — |
 | 2026-04-23 | **聚簇 A.2 `conf/workers.ini` 外配 + 新集群 hostname 适配**:`bin/common/init.sh` 原硬编码 `RELEASE_HOST="m3"` + `DATAX_WORKERS=(m3 d1 d2 d3 d4)` + `DATAX_WORKERS_WEIGHTS` map + 队列展开 for 循环(L13-28),整体外移到 `conf/workers.ini`(`[release] host` + `[weights]` 两 section,ini 格式入库);init.sh 改为纯 bash 读 ini(case + BASH_REMATCH 正则,不调 Python,避免 bash/py 双源漂移)。新集群 hostname 从老短名 m3/d1-d4 换到真实短名 cdhmaster02 / cdhnode01-03(用户 2026-04-23 提供),权重从 1/2/2/3/3 改为 1/3/3/3(master 1、3 个 worker 均权 3)。联动 kb/90 §2.1 行标 ✅ + §八 A 聚簇状态表行标 ✅ | — |
+| 2026-04-23 | **DataX 字段级脱敏配置化落地(插入本轮 scope 完成)**:新建 `dw_base/datax/mask.py` —— PG 方言 5 种脱敏(3 静态 `month_trunc` / `md5` / `mask_middle` + 2 动态 `keep_first_{n}` / `keep_last_{n}`,正则捕获数字参数),列名白名单 `[A-Za-z_][A-Za-z0-9_]*` + mask_type 校验防 SQL 注入;`postgresql_reader.py:load_others` 检测 `[mask]` 段自动调 mask 模块生成 querySql,优先级"手写 querySql > [mask] > table"。新建 `tests/unit/datax/test_mask.py` 9 条单测覆盖静态/动态/综合/各校验分支。`tests/integration/datax/hive_import/app_user_cert_info.ini` 从手写 querySql 切到 `[mask] cert_birthday = month_trunc` 声明式。联动 kb/90 §2.6 高优先级"DataX 字段级脱敏"改写为 [mask] 为规范 + querySql 为手写后备,低优先级"ini 配置化脱敏"条标 ✅ 压缩 | — |

+ 7 - 4
tests/integration/datax/hive_import/app_user_cert_info.ini

@@ -4,8 +4,8 @@
 ; 目标 datasource:datasource/hdfs/prd-hdfs-ha.ini(HA 集群,nameservice1)
 ; 时间锚点:create_time
 ; 敏感字段:
-;   - user_cert_data / cert_no / cert_real_name:column/querySql 都不查
-;   - cert_birthday:querySql 里 TO_CHAR 截到月粒度,PG 端执行完成脱敏(敏感原值不出业务库)
+;   - user_cert_data / cert_no / cert_real_name:column 不列(不查)
+;   - cert_birthday:[mask] 声明 month_trunc,代码生成 PG TO_CHAR 脱敏(敏感原值不出业务库)
 ;
 ; 注:key = value 不对齐——parse_ddl 的 grep "path =" 要求字面子串,多空格对齐会破匹配
 
@@ -15,11 +15,14 @@ database = hs_sync_data
 table = public.app_user_cert_info
 column = id,user_id,cert_birthday,cert_sex,cert_province,cert_city,version,status,del_flag,create_time,update_time
 columnType =
-where =
-querySql = SELECT id, user_id, TO_CHAR(cert_birthday, 'YYYY-MM') AS cert_birthday, cert_sex, cert_province, cert_city, version, status, del_flag, create_time, update_time FROM public.app_user_cert_info WHERE create_time >= '${start_date}' AND create_time < '${stop_date}'
+where = create_time >= '${start_date}' AND create_time < '${stop_date}'
+querySql =
 splitPk = id
 fetchSize = 1000
 
+[mask]
+cert_birthday = month_trunc
+
 [writer]
 dataSource = hdfs/prd-hdfs-ha
 path = /user/hive/warehouse/test.db/raw_usr_app_user_cert_info_inc_d/dt=${dt}/