Răsfoiți Sursa

feat: 重构计划 1 。添加环境配置(prd,test,dev) 2.datax支持多层目录结构,完善同步配置命名模块 3 剔除死代码

tianyu.chu 2 săptămâni în urmă
părinte
comite
3e8131fefc
8 a modificat fișierele cu 252 adăugiri și 53 ștergeri
  1. 2 2
      CLAUDE.md
  2. 2 2
      README.md
  3. 0 0
      conf/sql_style.xml
  4. 1 1
      dw_base/common/template_constants.py
  5. 116 39
      kb/00-项目架构.md
  6. 32 0
      kb/21-命名规范.md
  7. 94 1
      kb/90-重构路线.md
  8. 5 8
      kb/README.md

+ 2 - 2
CLAUDE.md

@@ -1,4 +1,4 @@
-# CLAUDE.md
+a# CLAUDE.md
 
 本项目是数据仓库工程 `poyee-data-warehouse`,当前处于**从老项目 `tendata-warehouse-release` 原地渐进式重构**的阶段。
 
@@ -23,7 +23,7 @@
 - **存储格式**:所有分层一律 `STORED AS ORC` + `orc.compress=NONE`(不压缩)
 - **raw 层契约**:全字段 STRING,同步任务不做类型转换,类型化交给 ods 层(详见 `kb/20-数仓分层与建模.md` §8)。一次性历史导入用 `his` 快照类型 + `o` 周期,表名形如 `raw_xxx_his_o`,不分区,CTAS 一步建表(详见 `kb/00-项目架构.md` §9.3 / §9.3.1)
 - **部署**:项目仓库名 = 部署目录名 = `poyee-data-warehouse`,部署路径 `/home/bigdata/release/poyee-data-warehouse/`,部署用户 `bigdata`
-- **敏感数据源配置**:不入仓库,由运维维护在 `/home/bigdata/release/datasource/{db_type}/{instance}.ini`
+- **敏感数据源配置**:不入仓库,由运维维护在 `/home/bigdata/release/datasource/{db_type}/{env}/{instance}.ini`(按环境 `dev`/`test`/`prod` 分子目录;执行时由命令行 `-env <name>` 或 `conf/env.sh` 默认值注入,ini 里 `dataSource = {db_type}/{instance}` 不含环境)
 
 ## 协作约定
 

+ 2 - 2
README.md

@@ -5,9 +5,9 @@
 ## 技术栈
 
 - **计算引擎**:PySpark 2.4 on YARN (CDH 6.3.2)
-- **数据集成**:DataX(阿里开源)
+- **数据集成**:DataX_v202309
 - **元数据管理**:Hive MetaStore
-- **存储**:HDFS (ORC)、RDS PostgreSQL、Elasticsearch
+- **存储**:HDFS (ORC)、RDS PostgreSQL --> Polar PostgreSQL、Elasticsearch
 - **调度**:DolphinScheduler
 - **告警**:企业微信机器人
 

+ 0 - 0
kb/sql_style.xml → conf/sql_style.xml


+ 1 - 1
dw_base/common/template_constants.py

@@ -13,7 +13,7 @@ SPARK_ODS_DWD_FU_TEMPLATE = 'conf/template/spark/ods-dwd-full-update-template.sq
 SPARK_DWS_ES_TEMPLATE = 'conf/template/spark/dws_es_update_repair_template.sql'
 SPARK_DWS_ES_TEMPLATE_SJT = 'conf/template/spark/dws_es_update_repair_template_sjt.sql'
 SPARK_SOP_ODS_DWD_FU_TEMPLATE = 'conf/template/spark/sop-ods-dwd-full-update-template.sql'
-SPARK_RAW_ODS_V_TEMPLATE = 'conf/template/spark/raw-ods-validat e-template.sql'
+SPARK_RAW_ODS_V_TEMPLATE = 'conf/template/spark/raw-ods-validat。,3e-template.sql'
 SPARK_RAW_SOP_V_TEMPLATE = 'conf/template/spark/raw-sop-template.sql'
 SPARK_STOCK_V_TEMPLATE = 'conf/template/spark/stock-validate-template.sql'
 TFC_TEMPLATE = 'conf/template/hive/tiny-file-combine-template.sql'

+ 116 - 39
kb/00-项目架构.md

@@ -288,15 +288,22 @@ python datax.py generated.json       (DataX 框架执行数据同步)
 
 **HDFS 数据检查**(`check_data_exists()`):当 JSON 配置路径包含 `hdfs-` 时,会自动检查 HDFS reader 路径是否存在且有数据,无数据则跳过执行。
 
-**示例**:
+**示例**(目标态,用 `-env` 切环境;命名见 `21-命名规范.md` §3.9)
 ```bash
-# 使用 ini 配置(自动生成 JSON 后执行)
-bin/datax-single-job-starter.sh -gc conf/datax/config/mysql-hdfs/prod/mysql-hdfs-hobby-orders.ini -start-date 20260415
+# 采集任务(raw 层 ini)
+bin/datax-single-job-starter.sh -gc jobs/raw/trd/raw_trd_order_pay_inc_d.ini -start-date 20260415 -env prod
+
+# 导出任务(ads 层 ini)
+bin/datax-single-job-starter.sh -gc jobs/ads/trd/ads_trd_gmv_d_export.ini -start-date 20260415 -env prod
 
-# 使用已生成的 JSON(跳过生成步骤)
-bin/datax-single-job-starter.sh -c /abs/path/to/generated.json -start-date 20260415
+# 使用已生成的 JSON(跳过生成,env 已嵌入 JSON
+bin/datax-single-job-starter.sh -c /abs/path/to/generated.json
 ```
 
+> **待重构项**(见 `90-重构路线.md` §2.1 DataX 条目):
+> - `-env` 参数目前**尚未实现**,现阶段切环境靠改 `datasource/` 下的实际文件或 `conf/env.sh`(待新建)
+> - `bin/` 下几个 DataX 启动脚本 / 生成器里还残留 `conf/datax/config/` 前缀剥离逻辑(老项目遗留;该目录已迁至 `conf/bak/` 并忽略入库),新项目 ini 放在 `jobs/raw/` / `jobs/ads/` / `manual/`,这段逻辑要清理掉
+
 ---
 
 #### 4.3.2 `datax-multiple-job-starter.sh` —— 通用批量启动
@@ -324,30 +331,32 @@ bin/datax-single-job-starter.sh -c /abs/path/to/generated.json -start-date 20260
 
 **日志路径**:`${LOG_ROOT_DIR}/datax/${src-dst}/${project_layer_env}/${START_DATE}/${START_DATE}-${JOB_NAME}.log`
 
-**示例**:
+**示例**(目标态)
 ```bash
-# 批量执行整个目录下的 ini 配置
-bin/datax-multiple-job-starter.sh -gcd conf/datax/config/mysql-hdfs/prod -start-date 20260415 -parallel
+# 批量执行整个业务域下的 raw 采集 ini
+bin/datax-multiple-job-starter.sh -gcd jobs/raw/trd -start-date 20260415 -env prod -parallel
 
 # 指定多个 ini 文件串行执行
 bin/datax-multiple-job-starter.sh \
-  -gc conf/datax/config/mysql-hdfs/prod/mysql-hdfs-hobby-orders.ini \
-  -gc conf/datax/config/mysql-hdfs/prod/mysql-hdfs-hobby-users.ini \
-  -start-date 20260415
+  -gc jobs/raw/trd/raw_trd_order_pay_inc_d.ini \
+  -gc jobs/raw/usr/raw_usr_user_info_inc_d.ini \
+  -start-date 20260415 -env prod
 ```
 
 ---
 
-#### 4.3.3 `datax-multiple-hive-job-starter.sh` —— MySQL→Hive 专用批量启动
+#### 4.3.3 `datax-multiple-hive-job-starter.sh` —— 带 Hive 分区自动管理的批量启动
 
-**用途**:在 `datax-multiple-job-starter.sh` 之上封装了 **Hive 分区自动管理**。MySQL→Hive 采集场景的推荐入口
+**用途**:在 `datax-multiple-job-starter.sh` 之上封装了 **Hive 分区自动管理**。任何写入 Hive 分区表的 DataX 同步作业(不限于 MySQL→Hive)都可以用它,脚本头注释里"MySQL-Hive 作业"只是历史命名。**日常采集作业的主力入口**
 
 **与 multiple-job-starter 的区别**:
-1. 自动从 ini 配置中解析 Hive 表名和分区路径(`parse_ddl()` 函数)
+1. 自动从 ini 配置中解析 Hive 表名和分区路径(`parse_ddl()` 函数,`grep "path =" <ini>`
 2. 在执行 DataX 前自动执行 `ALTER TABLE ... ADD IF NOT EXISTS PARTITION(dt=...)` 
 3. 支持在脚本内硬编码配置列表(`partitioned_tables`、`generator_config_array` 等数组),适合固定调度场景
 4. 支持 `--override` 参数临时覆盖脚本内硬编码配置
 
+> **自动建分区只对 ini 输入生效**:`parse_ddl()` 读的是 ini 里的 `path = ...` 行。如果走 `-jc` / `-jcd` 传已生成的 JSON,脚本没有 ini 可解析,自动建分区**不触发**,此时要么改用 `-t db.table` 显式声明分区、要么把分区记录在脚本内 `partitioned_tables` 数组。
+
 **额外参数**:
 
 | 参数 | 说明 |
@@ -364,17 +373,17 @@ bin/datax-multiple-job-starter.sh \
 2. 检查路径是否包含 `/dt=${dt}`(分区标识)
 3. 从 HDFS 路径中提取 `{db}.db/{table_name}` → 拼接 `ALTER TABLE {db}.{table} ADD IF NOT EXISTS PARTITION(dt={START_DATE})`
 
-**示例**:
+**示例**(目标态)
 ```bash
-# 执行整个目录 + 自动建分区
+# 执行某业务域下所有 raw 采集 ini + 自动建 Hive 分区
 bin/datax-multiple-hive-job-starter.sh \
-  -gcd conf/datax/config/mysql-hdfs/prod \
-  -start-date 20260415 -parallel
+  -gcd jobs/raw/trd \
+  -start-date 20260415 -env prod -parallel
 
-# 覆盖脚本内配置,只跑指定的失败任务
+# 覆盖脚本内硬编码配置,只跑指定的失败任务
 bin/datax-multiple-hive-job-starter.sh --override \
-  -gc conf/datax/config/mysql-hdfs/prod/mysql-hdfs-hobby-orders.ini \
-  -start-date 20260415
+  -gc jobs/raw/trd/raw_trd_order_pay_inc_d.ini \
+  -start-date 20260415 -env prod
 ```
 
 ---
@@ -394,22 +403,21 @@ bin/datax-multiple-hive-job-starter.sh --override \
 | `-stop-date` | 结束日期,默认今天 |
 | `-o <dir>` | 输出目录(默认 `conf/datax/generated/`) |
 
-**生成路径规则**:输出文件保持 ini 的目录层级,例如:
-```
-输入:conf/datax/config/mysql-hdfs/prod/mysql-hdfs-hobby-orders.ini
-输出:conf/datax/generated/mysql-hdfs/prod/mysql-hdfs-hobby-orders.json
-```
+**生成路径规则**(**当前脚本残留老逻辑,待清理**):脚本里仍保留 `temp = os.path.dirname(gcf).replace(project_root_dir, '').replace('conf/datax/config/', '').split('/')` 这段——老项目的 ini 放在 `conf/datax/config/{src-dst}/{env}/` 下,前缀剥离后能派生出 `src_dst` / `project_layer_env` 拼接输出路径。新项目 ini 已经不走这条路径(`conf/datax/config/` 整体挪到 `conf/bak/` 并 gitignore),但脚本里的 replace 语句仍在执行一次无效剥离,输出会落到 `conf/datax/generated/jobs/raw/trd/xxx.json`——能跑但路径形态不符合新约定。
 
-**示例**:
+重构目标:去掉路径前缀剥离逻辑,输出统一扁平为 `conf/datax/generated/{env}/{目标表名}.json`。登记为硬编码待重构项,见 `90-重构路线.md` §2.1。
+
+**示例**(目标态):
 ```bash
 # 生成单个 ini 对应的 JSON
-python3 bin/datax-job-config-generator.py -c conf/datax/config/mysql-hdfs/prod/mysql-hdfs-hobby-orders.ini
+python3 bin/datax-job-config-generator.py -c jobs/raw/trd/raw_trd_order_pay_inc_d.ini -env prod
 
-# 批量生成整个目录(递归)
-python3 bin/datax-job-config-generator.py -d conf/datax/config/mysql-hdfs/prod -r
+# 批量生成某业务域下所有 ini(递归)
+python3 bin/datax-job-config-generator.py -d jobs/raw/trd -r -env prod
 
 # 指定日期和输出路径
-python3 bin/datax-job-config-generator.py -c some.ini -start-date 20260415 -stop-date 20260416 -o /tmp/datax-out
+python3 bin/datax-job-config-generator.py -c jobs/raw/trd/raw_trd_order_pay_inc_d.ini \
+  -start-date 20260415 -stop-date 20260416 -env prod -o /tmp/datax-out
 ```
 
 ---
@@ -444,16 +452,16 @@ python3 bin/datax-job-config-generator.py -c some.ini -start-date 20260415 -stop
 
 **示例**:
 ```bash
-# 为 MySQL 库中所有表生成 mysql→hdfs 的 ini 配置
+# 为 MySQL 库中所有表生成 mysql→hdfs 的 ini 配置,输出到 raw/trd 业务域
 python3 bin/datax-gc-generator.py --from mysql --to hdfs \
   -h 10.0.0.1 -u reader -p xxx -D hobby_prod \
-  --output conf/datax/config/mysql-hdfs/prod
+  --output jobs/raw/trd
 
 # 只为指定表生成,排除临时表
 python3 bin/datax-gc-generator.py --from mysql --to hdfs \
   -h 10.0.0.1 -u reader -p xxx -D hobby_prod \
   -tr "^order" -er "^tmp_" \
-  --output conf/datax/config/mysql-hdfs/prod
+  --output jobs/raw/trd
 
 # 为 Hive 表生成 hdfs→elasticsearch 的 ini 配置
 python3 bin/datax-gc-generator.py --from hdfs --to elasticsearch \
@@ -535,7 +543,7 @@ jobs/
 
 | 配置类型 | 存放位置 | 是否入仓库 | 维护角色 |
 |----------|---------|-----------|------|
-| 数据源连接(含账密) | `../datasource/{类型}/` | 否 | 运维   |
+| 数据源连接(含账密) | `../datasource/{db_type}/{env}/{instance}.ini` | 否 | 运维   |
 | DataX 同步任务定义 | `jobs/raw/` (采集) 和 `jobs/ads/` (导出) | 是 | 开发   |
 | Spark 默认参数 | `conf/spark-defaults.yaml`(目标态) / `spark_sql.py` 构造函数(现状) | 是 | 开发   |
 | Spark 单作业覆盖 | 对应 `jobs/*.sql` 文件内 `SET spark.x.y=z` | 是 | 开发   |
@@ -569,10 +577,10 @@ userPassword = xxx
 defaultFS = hdfs://nameservice1               # HDFS
 ```
 
-**同步任务定义(jobs/raw/*/*.ini):**
+**同步任务定义(jobs/raw/{domain}/{目标表名}.ini):**
 ```ini
 [reader]
-dataSource = pg-hobby-prod                    # 引用 datasource/pg/hobby-prod.ini
+dataSource = pg/hobby                         # 引用 datasource/pg/${env}/hobby.ini,${env} 运行时注入
 dbName = xxx
 schemaName = public
 tableName = orders
@@ -581,7 +589,7 @@ columnType = col1:bigint,col2:date
 where = update_time >= '${start_date}' AND update_time < '${stop_date}'
 
 [writer]
-dataSource = hdfs-prod
+dataSource = hdfs/default                     # 引用 datasource/hdfs/${env}/default.ini
 column = col1,col2,...
 columnType = col1:bigint,col2:date
 path = /user/hive/warehouse/raw.db/raw_trd_order_pay_inc_d/dt=${dt}
@@ -589,10 +597,79 @@ fileType = orc
 writeMode = truncate
 ```
 
+**关键约定**:
+- `dataSource` 字段只写 `{db_type}/{instance}`,**不含环境**。环境由启动脚本的 `-env` 参数注入
+- 新项目推荐规范见 §6.4;老项目里 `dataSource = pg-hobby-prod` 这种把环境拼进字符串的写法是历史遗留,重构中统一改为上述新形式
+
 **增量/全量区分:**
 - `dt=19700101` 或 `query={}` → 全量
 - `query` 中含 `${start_date}`/`${stop_date}` → 增量
 
+### 6.4 多环境机制与 env 注入
+
+**原则:业务代码一套,环境差异收敛在 `datasource/` 和 `conf/env.sh`,运行时注入。**
+
+**环境集合**:`dev` / `test` / `prod`(由运维在 `datasource/` 下分别维护一套实例配置)。
+
+**注入链路**:
+
+```
+启动脚本(-env prod)
+        │
+        ▼
+ini 里 dataSource = pg/hobby
+        │
+        │  脚本拼接
+        ▼
+实际路径 datasource/pg/prod/hobby.ini
+        │
+        ▼
+DataX Reader/Writer 建立连接
+```
+
+**env 判定优先级**(两级,不引入环境变量,避免污染 shell 历史和 CI 环境):
+
+| 级别 | 来源 | 用途 |
+|------|------|------|
+| L1(最高) | 命令行 `-env <name>` | 调试 / 跨环境临时切换 |
+| L2 | `conf/env.sh` 里的 `DW_ENV` 默认值 | **入仓库**的一份配置,由开发者维护。默认值通常锁死为 `dev`(服务本地调试方便)。DolphinScheduler / 生产脚本总是命令行显式挂 `-env prod` 覆盖。不做任何"按用户/目录"的自动派生 |
+
+**目录示例**:
+
+```
+datasource/                           # 运维维护,不入仓库
+├── pg/
+│   ├── prod/
+│   │   ├── hobby.ini                 # 生产 PG 实例
+│   │   └── crm.ini
+│   ├── test/
+│   │   └── hobby.ini
+│   └── dev/
+│       └── hobby.ini
+├── mysql/
+│   ├── prod/
+│   └── dev/
+├── mongo/
+│   └── ...
+└── hdfs/
+    └── ...
+```
+
+**执行示例**:
+
+```bash
+# 跑生产环境
+bin/datax-multiple-hive-job-starter.sh -gcd jobs/raw/trd -start-date 20260415 -env prod -parallel
+
+# 本地调试(通常省略 -env,走 conf/env.sh 默认值 dev)
+bin/datax-single-job-starter.sh -gc jobs/raw/trd/raw_trd_order_pay_inc_d.ini -start-date 20260415
+
+# 跑测试环境(测试 Hive 集群 + 测试后端库 + 测试服务库都在 datasource/*/test/ 下)
+bin/datax-multiple-hive-job-starter.sh -gcd jobs/raw/trd -start-date 20260415 -env test -parallel
+```
+
+**当前状态**:`-env` 参数、`conf/env.sh`、`datasource/` 的环境子目录**都尚未落地**,是目标态。改造清单见 `90-重构路线.md` §2.1。
+
 ## 7. 部署架构
 
 ```
@@ -918,7 +995,7 @@ jobs/ads/trd/
 | `manual/ddl/` | `.sql` | `{目标表名}.sql`(首次) 或 `{yyyymmdd}_{表名}_{change}.sql`(ALTER) | DDL 唯一来源;首次建表用 `CREATE TABLE IF NOT EXISTS`,后续 ALTER 带日期前缀,执行后归档 |
 | `jobs/raw/{domain}/` | `.ini`(DataX)或 `.sql`(CSV 导入) | `{目标表名}.ini` 或 `{目标表名}.sql` | DataX 采集或 CSV 导入任务定义 |
 | `jobs/{ods\|dwd\|dws\|tdm}/{domain}/` | `.sql` | `{目标表名}.sql` | 每日 `INSERT OVERWRITE` 计算 |
-| `jobs/ads/{domain}/` | `.sql` + `.ini` | `{ads 表名}.sql` + `{ads 表名}_export.ini` | 产出 + 导出 |
+| `jobs/ads/{domain}/` | `.sql` + `.ini` | `{ads 表名}.sql` + `{ads 表名}__{db_type}_{instance}.ini` | 产出 + 导出;同一张 ads 表扇出多下游时各一份 ini(见 `21-命名规范.md` §3.9) |
 | `manual/backfill/` | `.sql` | `{yyyymmdd}_{表名}_history.sql` | 一次性历史回刷脚本 |
 
 ### 9.6 表结构变更流程(migration 模式)

+ 32 - 0
kb/21-命名规范.md

@@ -247,6 +247,35 @@ ads 是面向具体应用场景的输出表(报表、接口、导出),表
 | `tmp` | 临时表 | ETL 中间结果 |
 | `test` | 测试表 | 开发测试用 |
 
+### 3.9 DataX ini 文件命名
+
+按**数据流向**分三类,各自命名规则不同;ini 文件名全局唯一,等于生成的 JSON 文件名(去扩展名)和 JOB_NAME。
+
+| 场景 | 方向 | 命名模板 | 存放位置 | 示例 |
+|------|------|---------|---------|------|
+| 采集 | 外部源 → Hive | `{目标 Hive 表名}.ini` | `jobs/raw/{域}/` | `raw_trd_order_pay_inc_d.ini` |
+| **导出** | **Hive → 外部服务** | `{源 Hive 表名}__{目标 db_type}_{目标 instance}.ini` | `jobs/ads/{域}/` | `ads_trd_gmv_d__mysql_bi.ini`、`ads_trd_gmv_d__pg_api.ini` |
+| 一次性 | 任意 | `{yyyymmdd}_{描述}.ini` | `manual/{子类}/` | `20260415_backfill_hobby_orders.ini` |
+
+**导出类双下划线规则:**
+
+- **`__`(双下划线)是"导出"的视觉标志**,和五段式表名内部的单下划线分层,一眼能识别"这是 Hive 往外写"
+- 前段 = 源 Hive 表名(五段式),后段 = `{目标 db_type}_{目标 instance}`,直接对应 writer 里的 `dataSource = {db_type}/{instance}`,写 ini 时不用二次翻译
+- 同一张 Hive 表扇出多个下游时(如 `ads_trd_gmv_d` 同时导出到 MySQL BI 库和 PG API 库)**不撞名**;撞名的唯一可能是同源表 + 同目标实例,那本来就是同一个任务
+
+**通用约定:**
+
+- **一套代码跑多环境**:不在 ini 文件名里加 `prod` / `dev` / `test` 前缀。环境差异体现在 `datasource/{db_type}/{env}/{instance}.ini`,由脚本根据 `-env` 参数注入
+- ini 内 reader/writer 的 `dataSource` 字段只写 `{db_type}/{instance}`(不含 env),例如 `dataSource = mongo/hobby`
+- DataX 生成引擎(`dw_base/datax/job_config_generator.py`)对文件名**不做校验**,上述命名规则是人/AI 写作约定,靠 code review 保证
+- 命名唯一性保证 JSON 输出目录 `conf/datax-json/{env}/{ini_basename}.json` 不会互相覆盖
+
+**参考样板**:`conf/templates/datax/{raw,ads,manual}/*.template.ini` 提供各类同步场景的字段齐全样本,新开发者和 AI 写新 ini 时抄这里。
+
+**为什么不按老项目的 `{from}-{to}-{db}-{table}.ini` 命名**:
+- 同步方向已经在 ini 的 `[reader]` / `[writer]` section 的 `dataSource` 里体现,文件名再写一遍是冗余
+- 目标/源表名命名能与 `manual/ddl/{表名}.sql` 和 `jobs/{layer}/{域}/{表名}.sql` 一一对应,便于 `grep` 追一张表的完整生命周期
+
 ---
 
 ## 4. 字段命名规则
@@ -393,6 +422,9 @@ STORED AS ORC;
 | dws | `dws_{域}_{实体}_{主题}_{窗口}` | 聚合维度+主题 | 时间窗口后缀 |
 | tdm | `tdm_{域}_{tag\|profile\|crowd_*}_ful_d` | 表类型(长表/宽表/人群包) | `ful_d` |
 | ads | `ads_{域}_{用途描述}` | 应用场景 | 无 |
+| DataX ini 采集 | `{目标 Hive 表名}.ini`(见 §3.9) | 五段式表名 | `.ini` |
+| DataX ini 导出 | `{源 Hive 表名}__{目标 db_type}_{目标 instance}.ini`(见 §3.9) | 双下划线分隔源/目标 | `.ini` |
+| DataX ini 一次性 | `{yyyymmdd}_{描述}.ini`(见 §3.9) | 日期前缀避撞 | `.ini` |
 
 ## 9. 合规 Checklist(建表前自检)
 

+ 94 - 1
kb/90-重构路线.md

@@ -46,9 +46,19 @@
 | `HADOOP_CONF_DIR='/etc/hadoop/conf'` | `__init__.py` | 使用系统环境变量 |
 | `LOG_ROOT_DIR="/opt/data/log"` | `init.sh`、`__init__.py` | 移入 `conf/env.sh` |
 | 钉钉 access_token | `dingtalk_notifier.py` | 移入 `conf/alerter.conf`(敏感项) |
-| 企微 Webhook Key | `alerter_constants.py` | 移入 `conf/alerter.conf`(敏感项) |
+| 企微 Webhook Key | `dw_base/common/alerter_constants.py` | 外移到 `conf/alerter.ini`(**入库**——部署靠 git pull,gitignore 会拉不到;webhook key 不算高敏感,最多被拿去发垃圾消息),Python 侧改 ConfigParser 加载;`alerter_constants.py` 整个删除 |
 | DS API 地址 | `ds/config/base_config.yaml` | 已在 yaml,保持即可 |
 | Spark 默认参数(executor/driver/shuffle/sql.*) | `dw_base/spark/spark_sql.py` 构造函数 + `.config(...)` 链 | 移入 `conf/spark-defaults.yaml`,SQL 文件可用 `SET` 覆盖,见 §2.3 |
+| DataX ini 路径前缀剥离 `conf/datax/config/` | `bin/datax-single-job-starter.sh`(TEMP 处理)、`bin/datax-job-config-generator.py`(`replace('conf/datax/config/', '')`)、`bin/datax-multiple-job-starter.sh`(日志路径派生) | 原目录已整体挪到 `conf/bak/` 并 gitignore,脚本里 replace 现在是 no-op 死逻辑。去除前缀假设,改为靠 ini 文件名(= 任务唯一标识,见 `21-命名规范.md` §3.9)识别用途 |
+| DataX 生成 JSON 输出目录名 `conf/datax/generated` | `bin/datax-job-config-generator.py` 末尾 `default_output_dir`、`bin/datax-single-job-starter.sh` 第 89/118 行、`bin/datax-multiple-job-starter.sh` 第 187 行、`.gitignore` | 目录改名 `conf/datax-json/`;子路径扁平化为 `conf/datax-json/{env}/{ini_basename}.json`(仅按 env 分一级,去掉 src_dst / project_layer_env 等派生层级);`.gitignore` 同步改 |
+| JOB_NAME / JSON 文件名的 `ini→json` 转换逻辑重复实现 | Python 侧 `bin/datax-job-config-generator.py:126`(`os.path.basename(gcf).replace('.ini', '.json')`)+ Bash 侧 `bin/datax-single-job-starter.sh:88`(`basename .ini`) | 合一到 `dw_base.datax.path_utils.job_name_from_ini()`(或类似工具);Bash 侧通过 `python3 -c` 调用或在 `bin/common/init.sh` 定义等价 shell 函数,单一来源 |
+| DataX 脚本不支持 `-env` 参数 | `bin/datax-*.sh` / `bin/datax-job-config-generator.py` 参数解析 | 新增 `-env` 参数,由 ini 内 `dataSource = {db_type}/{instance}` 拼接成 `datasource/{db_type}/{env}/{instance}.ini` 的完整路径;详见 §2.5 |
+| `datasource/` 单层组织(无环境子目录) | `datasource/{db_type}/{instance}.ini` | 改为 `datasource/{db_type}/{env}/{instance}.ini`(运维侧权限隔离 + 支持一套代码跑多环境) |
+| ini 里 `dataSource` 字段拼接环境后缀 | 老项目写法 `dataSource = pg-hobby-prod` | 改为 `dataSource = {db_type}/{instance}`(不含环境),env 由脚本注入 |
+| 导出类 ini 扇出撞名风险 | `jobs/ads/{域}/` 下 ini 若都以源 Hive 表名命名,同一张 ads 表扇出到多个目标库时会重名覆盖 | 命名规则改为 `{源 Hive 表名}__{目标 db_type}_{目标 instance}.ini`(双下划线分隔源/目标),见 `21-命名规范.md` §3.9 |
+| `dw_base/common/template_constants.py` 大量死代码 | 定义了 20+ 个 SQL 模板路径常量,实际只有 2 个(`MYSQL_HIVE_CREATE_TABLE_TEMPLATE` / `MYSQL_HIVE_HBASE_CREATE_TABLE_TEMPLATE`)被引用,其余 18 个零 import | 整个文件删除;连带废弃下一条 |
+| `MySQLReader.generate_hive_ddl()` / `generate_hive_over_hbase_ddl()` 自动建表 DDL 路径 | `dw_base/datax/plugins/reader/mysql_reader.py:195/222`,被 `bin/datax-gc-generator.py:616/728` 调用;且 `conf/template/` 目录在新项目根本不存在,真调用会 FileNotFoundError | 整段路径废弃——与 CLAUDE.md 约定的 `manual/ddl/` 是 DDL 唯一来源相冲突。`datax-gc-generator.py` 仅生成 ini 配置,不再输出 CREATE TABLE DDL;DDL 由开发者按 `21-命名规范.md` 手写到 `manual/ddl/` |
+| 缺少集中的人/AI 参考模板目录 | —(新增) | 已建 `conf/templates/{datasource,datax/{raw,ads,manual},sql,ddl}/`,模板用 `*.template.{ini,sql}` 双扩展名。与上条废弃的运行时模板完全不同:这里的模板不被任何代码读取,只供人/AI 对照写新文件;`kb/README.md` 已加入口 |
 
 ### 2.2 建议的配置结构
 
@@ -191,6 +201,89 @@ conf/alerter.conf
 
 仓库改名 `tendata-warehouse-release` → `poyee-data-warehouse` 时(阶段 1 尾声),`.idea/tendata-warehouse-release.iml` 也要改名为 `.idea/poyee-data-warehouse.iml`,并同步更新 `.idea/modules.xml` 里的引用。这一步不属于 `.gitignore` 的范畴,但和它是同一天会碰到的事,在阶段 1 的仓库改名 checklist 里一起记一笔。
 
+### 2.5 DataX 脚本多环境支持与路径解耦
+
+**现状(脚本残留老逻辑,新项目的业务 ini 未走这条路径):**
+
+1. **脚本里残留路径前缀剥离**:`bin/datax-single-job-starter.sh` 和 `bin/datax-job-config-generator.py` 仍通过剥离 `conf/datax/config/` 前缀从源 ini 路径里派生 `SRC_DST` / `PROJECT_LAYER_ENV`,用于生成 JSON 输出路径和 `datax-multiple-job-starter.sh` 的日志目录。该目录已整体挪到 `conf/bak/` 并 gitignore,新项目 ini 放在 `jobs/raw/{domain}/` / `jobs/ads/{domain}/` / `manual/`,前缀不匹配时剥离变成 no-op,输出会落到形如 `conf/datax/generated/jobs/raw/trd/xxx.json` 的位置——能跑但与新约定不符。代码里这段死逻辑要清理,同时输出根目录也一并改名为 `conf/datax-json/`。
+2. **没有 `-env` 参数**:所有脚本不认 `-env`。
+3. **数据源配置单层组织**:老约定 `datasource/{db_type}/{instance}.ini` 把环境和实例扁平混在一起,要么靠不同的 `{instance}` 名字(如 `hobby-prod` / `hobby-dev`)区分,要么靠部署时替换文件。第一种让 ini 里写 `dataSource = pg-hobby-prod` 这种绑死环境的字符串;第二种让开发侧不知道当前跑的是哪套。
+
+**目标态:一套代码多环境运行**
+
+**三件事联动(顺序重要):**
+
+#### 阶段 1:datasource 改按环境分子目录
+
+- 改为 `datasource/{db_type}/{env}/{instance}.ini`
+- 运维在集群侧按 `prod` / `test` / `dev` 建子目录,各自放一套连接配置
+- 权限隔离更方便:prod 子目录只给生产账号可读
+
+#### 阶段 2:ini 里 `dataSource` 字段去掉环境后缀
+
+- 老写法:`dataSource = pg-hobby-prod`
+- 新写法:`dataSource = pg/hobby`(只写 `{db_type}/{instance}`,不含 env)
+- **变更项目下所有存量 ini**:当前 `conf/bak/` 下的老配置不处理,等业务新 ini 从零写起时就按新规范
+
+#### 阶段 3:DataX 脚本加 `-env` 参数 + env 注入逻辑
+
+**`bin/common/init.sh` 扩展:**
+
+```bash
+# 从命令行参数里挑出 -env,或 fall back 到 conf/env.sh
+# 不读环境变量(用户明确要求不污染 shell 环境)
+# 不做"按用户/部署目录自动派生"——env 必须来自 -env 或 conf/env.sh,二者都没给就退出
+resolve_env() {
+  for arg in "$@"; do
+    case $arg in
+      -env) NEXT_IS_ENV=1 ;;
+      -env=*) DW_ENV="${arg#*=}" ;;
+      *) [ -n "$NEXT_IS_ENV" ] && DW_ENV="$arg" && unset NEXT_IS_ENV ;;
+    esac
+  done
+  if [ -z "$DW_ENV" ]; then
+    # fall back 到 conf/env.sh(入仓库的默认配置)
+    [ -f "${BASE_DIR}/conf/env.sh" ] && . "${BASE_DIR}/conf/env.sh"
+  fi
+  if [ -z "$DW_ENV" ]; then
+    echo "[FATAL] DW_ENV not set: pass -env <dev|test|prod> or define DW_ENV in conf/env.sh" >&2
+    exit 1
+  fi
+  export DW_ENV
+}
+```
+
+**`conf/env.sh` 草案**(**入仓库**,开发者维护,服务本地调试):
+
+```bash
+# 全局环境变量默认值
+# env 判定优先级:命令行 -env > 本文件 DW_ENV
+# 默认锁定为 dev:本地调试开箱即用;DolphinScheduler / 生产脚本总是命令行显式 -env prod 覆盖
+DW_ENV=dev
+# LOG_ROOT_DIR=/opt/data/log
+```
+
+**`dw_base/datax/job_config_generator.py` 改造**:
+
+- `JobConfigGenerator.__init__` 接受新参数 `env`
+- 各 reader/writer 插件在解析 `dataSource = pg/hobby` 时,自动拼成 `datasource/pg/{env}/hobby.ini` 的完整路径再去读连接信息
+
+**`bin/datax-job-config-generator.py` 改造**:
+
+- 新增 `-env` 参数
+- **去掉** `temp = os.path.dirname(gcf).replace('conf/datax/config/', '').split('/')` 这段路径前缀剥离
+- JSON 输出目录从 `conf/datax/generated/` 改名为 `conf/datax-json/`,子路径简化为 `conf/datax-json/{env}/{ini_basename}.json`(扁平,按 env 分一级;`ini_basename` 由 `21-命名规范.md` §3.9 保证全局唯一——采集类 = 目标 Hive 表名、导出类 = `{源}__{db_type}_{instance}`、一次性 = 日期前缀)
+- `.gitignore` 同步把 `conf/datax/generated` 改成 `conf/datax-json`
+
+#### 阶段 4:启动脚本层串起来
+
+- `datax-single-job-starter.sh` 调用 generator 时把 `$DW_ENV` 透传过去
+- `datax-multiple-job-starter.sh` 日志目录改为 `${LOG_ROOT_DIR}/datax/${DW_ENV}/${START_DATE}/${JOB_NAME}.log`(把 `SRC_DST` / `PROJECT_LAYER_ENV` 彻底移除)
+
+**兼容性**:阶段 1-3 期间 `conf/bak/` 下的老 ini 不参与新流程,保留作为老项目历史资产;新业务 ini 全部从零按新规范写。
+
+**参考**:kb/00-项目架构.md §4.3(DataX 脚本详细使用)、§6.4(多环境机制)、kb/21-命名规范.md §3.9(DataX ini 文件命名)。
+
 ## 三、`__init__.py` 瘦身(高优先级)
 
 **现状:** `tendata/__init__.py` 约 120 行,import 即执行以下操作:

+ 5 - 8
kb/README.md

@@ -64,13 +64,10 @@
 4. [21-命名规范](21-命名规范.md) — 熟悉命名规则(★ 最高频参考)
 5. [30-开发规范](30-开发规范.md) — 熟悉开发流程
 
-**AI 辅助开发路径:**
-- 建表 / 写 SQL → `21-命名规范.md` + `20-数仓分层与建模.md`
-- 设计指标 → `22-指标体系.md`
-- 设计标签 → `23-标签体系.md`
-- 重构旧代码 → `90-重构路线.md`
-- 排查权限问题 → `02-权限与账号.md`
 
-## 原始导出
+**查样板:**
+- DataX ini 样板 → `conf/templates/datax/{raw,ads,manual}/*.template.ini`
+- Spark SQL 样板 → `conf/templates/sql/*.template.sql`
+- 建表 DDL 样板 → `conf/templates/ddl/*.template.sql`
+- 数据源连接样板 → `conf/templates/datasource/*.template.ini`
 
-`kb/Bigdata-space-export (5)/` 是公司公共知识库「大数据空间」的导出副本。本目录下的整合文档已从中提取了与 `poyee-data-warehouse` 数仓建设相关的核心信息。如需查看原始资料(含截图),可直接访问该子目录。