Bladeren bron

fix(bin): mask-conf 三处加固——fail-fast / unknown 字段警告 / 单 dash

- load_mask_conf:文件不存在 raise FileNotFoundError,不再
  ConfigParser 静默失败(解决 cwd 错时 mask 默默失效问题)
- main:mask 配置含 PG 表中不存在字段时 stderr 警告(不阻断,
  字段被忽略);为后续 dq schema 探查留检查点雏形
- argparse: --mask-conf 改 -mask-conf,与项目其他 bin 脚本(-ini /
  -dt / -start-date 等)单 dash 多字符风格一致
- 单测加 missing file raises 一条
- kb/25 + kb/93 + conf/templates + 已落地 mask conf 头注释同步改齐
tianyu.chu 1 week geleden
bovenliggende
commit
dfeb479fd2

+ 24 - 9
bin/datax-sync-template-gen.py

@@ -13,16 +13,19 @@ CLI:
   python3 bin/datax-sync-template-gen.py \\
     -ds postgresql/prod-hobby \\
     -t public.card_group_order_info \\
-    [-o [DIR]]
+    [-mask-conf <PATH>] [-o [DIR]]
 
 参数:
-  -ds  数据源 ref,形如 {db_type}/{env}-{实例简称}(同 sync ini 里
-       dataSource 字段格式)。暂只支持 postgresql。
-  -t   schema 限定的表名,形如 schema.table(如 public.card_group_order_info)。
-  -o   输出目录(可选):
-       - 不传:stdout 同时打印 md 表 + ini 模板
-       - 传 -o 不带值:workspace/{yyyymmdd}/,写两个文件 {table}.md + {table}.ini
-       - 传 -o <DIR>:自定义目录,写两个文件
+  -ds         数据源 ref,形如 {db_type}/{env}-{实例简称}(同 sync ini
+              里 dataSource 字段格式)。暂只支持 postgresql。
+  -t          schema 限定的表名(如 public.card_group_order_info)。
+  -mask-conf  mask 配置 ini 路径({table}.mask.ini,可选)。传入时按配置
+              剔除 trim 字段 + 渲染 [mask] 段,md 脱敏类型列填好;不传时
+              全字段输出,md 脱敏类型列空白。**文件不存在直接报错**。
+  -o          输出目录(可选):
+              - 不传:stdout 同时打印 md 表 + ini 模板
+              - 传 -o 不带值:workspace/{yyyymmdd}/,写 {table}.md + .ini
+              - 传 -o <DIR>:自定义目录,写两个文件
 """
 import argparse
 import os
@@ -105,7 +108,11 @@ def load_mask_conf(path):
     method ∈ trim / md5 / month_trunc / mask_middle / keep_first_n / keep_last_n
     - trim:整字段不入 raw(reader column 不查询)
     - 其他:字段入 raw,由 dw_base.datax.mask 在 reader 端脱敏
+
+    文件不存在直接 raise FileNotFoundError(不静默失败)。
     """
+    if not os.path.isfile(path):
+        raise FileNotFoundError('mask 配置不存在: ' + path)
     cp = ConfigParser()
     cp.read(path, encoding='utf-8')
     if not cp.has_section('mask'):
@@ -196,7 +203,7 @@ def main():
                         help='schema 限定的表名(如 public.card_group_order_info)')
     parser.add_argument('-o', nargs='?', const=WORKSPACE_DEFAULT, default=None, metavar='DIR',
                         help='输出目录(不传 stdout 同时打印 md 表 + ini 模板;不带值 workspace/{yyyymmdd}/ 写两文件;带值自定义目录写两文件)')
-    parser.add_argument('--mask-conf', default=None, metavar='PATH',
+    parser.add_argument('-mask-conf', default=None, metavar='PATH', dest='mask_conf',
                         help='mask 配置 ini 路径({table}.mask.ini)。传入时按配置剔除 trim 字段 + 渲染 [mask] 段,md 脱敏类型列填好;不传时全字段输出,md 脱敏类型列空白')
     args = parser.parse_args()
 
@@ -226,6 +233,14 @@ def main():
 
     # full_rows: [(attnum, attname, comment, pg_type, pk_flag), ...]
     mask_dict = load_mask_conf(args.mask_conf) if args.mask_conf else {}
+
+    # mask 配置含表中不存在字段时 stderr 警告(不阻断)
+    pg_field_set = {r[1] for r in full_rows}
+    unknown_fields = [f for f in mask_dict if f not in pg_field_set]
+    if unknown_fields:
+        print('警告:mask 配置含表中不存在字段(已忽略): ' + ', '.join(unknown_fields),
+              file=sys.stderr)
+
     trim_set = {f for f, m in mask_dict.items() if m == 'trim'}
     non_trim_mask = {f: m for f, m in mask_dict.items() if m != 'trim'}
 

+ 1 - 1
conf/templates/datax/mask/mask.template.ini

@@ -2,7 +2,7 @@
 # 落位:jobs/raw/{域}/{table}.mask.ini(与 sync.ini 同级一对一)
 #
 # 用途(双消费):
-#   1. sync 生成器(bin/datax-sync-template-gen.py --mask-conf)
+#   1. sync 生成器(bin/datax-sync-template-gen.py -mask-conf)
 #      → 生成几乎可用 ini:trim 字段从 reader.column 剔除(保持 PG 原顺序);
 #                          其他 method 渲染 [mask] 段供 DataX reader 端脱敏
 #      → md 脱敏类型列填好(kb/24 raw 建模文档手动同步)

+ 1 - 1
jobs/raw/trd/raw_trd_card_group_order_info_inc_d.mask.ini

@@ -5,7 +5,7 @@
 ;
 ; 来源:本次 mask conf 机制上线前,inc_d ini reader.column 已确定为 91 入仓字段
 ;       (PG 全字段 120 砍 29)。本配置按 29 个不入仓字段反推为 trim,
-;       使 sync 生成器(带 --mask-conf)的输出与现已上线 inc_d ini 一致,
+;       使 sync 生成器(带 -mask-conf)的输出与现已上线 inc_d ini 一致,
 ;       为后续表入仓建立标准模板。
 ;
 ; 字段分组按 kb/24 §1 三态决策表(明性 28 + 隐性 1,本配置统一 trim)。

+ 3 - 3
kb/25-raw接入流程.md

@@ -6,7 +6,7 @@
 
 ```
 ┌──────────────────┐
-│ Step 1:跑 sync   │ 不带 --mask-conf
+│ Step 1:跑 sync   │ 不带 -mask-conf
 │ 生成器(初版)    │ → workspace 全字段 ini + md
 └────────┬─────────┘
@@ -24,7 +24,7 @@
 ┌──────────────────┐
-│ Step 4:跑 sync   │ 带 --mask-conf
+│ Step 4:跑 sync   │ 带 -mask-conf
 │ 生成器(终版)    │ → workspace 几乎可用 ini + md(留档)
 └────────┬─────────┘
@@ -94,7 +94,7 @@ field3 = md5
 python3 bin/datax-sync-template-gen.py \
   -ds postgresql/{env}-{instance} \
   -t public.{table_name} \
-  --mask-conf jobs/raw/{域}/{table_name}.mask.ini \
+  -mask-conf jobs/raw/{域}/{table_name}.mask.ini \
   -o
 ```
 

+ 1 - 1
kb/93-架构决策.md

@@ -244,7 +244,7 @@
 
   `jobs/raw/{域}/{table}.mask.ini` 是单一真值——sync 生成器和 dq 模块双消费:
 
-  - sync 生成器:读 mask conf 出几乎可用 ini(已实施,见 ADR-06 / sync 脚本 `--mask-conf`)
+  - sync 生成器:读 mask conf 出几乎可用 ini(已实施,见 ADR-06 / sync 脚本 `-mask-conf`)
   - dq 模块:读 mask conf trim 字段集 + ini reader.column 保留集 = "数仓已知字段全集",与 PG 实时字段集做差,差非空 → schema drift 告警
 
   **二、注册表机制**

+ 5 - 0
tests/unit/datax/test_sync_template_gen.py

@@ -149,6 +149,11 @@ def test_load_mask_conf_no_section_returns_empty(tmp_path):
     assert GEN.load_mask_conf(str(p)) == {}
 
 
+def test_load_mask_conf_missing_file_raises():
+    with pytest.raises(FileNotFoundError, match='mask 配置不存在'):
+        GEN.load_mask_conf('/nonexistent/path/x.mask.ini')
+
+
 def test_render_template_empty_pk():
     out = GEN.render_template(
         ds_ref='postgresql/prod-hobby', database='db', schema='public',