# 字段排查深度调查报告

> 调查时间：2026-02-19
> 调查方式：FACT_MAPPINGS 源码审阅 + DWD DDL 交叉验证 + 数据库实际列/数据查询
> 基于排查脚本报告 `export/SYSTEM/REPORTS/field_audit/field_audit_report.md` 的线索，逐字段深入调查

---

## 调查方法说明

对脚本标记为"缺失"或"对不齐"的每个字段，执行以下调查：
1. 查阅 `FACT_MAPPINGS` 源码（`dwd_load_task.py`），确认是否已以其他名称/表达式映射
2. 查阅 DWD 数据库实际列（`information_schema.columns`），确认是否已有同语义列
3. 查阅 ODS 数据库实际列，确认 ODS 列是否真实写入
4. 结合字段命名规律、上下文语义、业务逻辑进行推测
5. 必要时查询数据库实际数据（`SELECT DISTINCT` / 采样）辅助判断

---

## 1. assistant_accounts_master → dim_assistant_ex（A 类）

### 脚本结论：4 个字段确实缺失，需新增 DWD 列

### 逐字段调查

#### 1.1 `system_role_id`
- **ODS 存在**：✅ `billiards_ods.assistant_accounts_master.system_role_id`（BIGINT）
- **DWD 现有列**：❌ `dim_assistant_ex` 中无 `system_role_id` 列（已通过 `information_schema` 确认）
- **FACT_MAPPINGS**：未配置任何映射
- **采样值**：`10`（系统角色 ID，用于关联角色权限）
- **业务价值**：有。标识助教在系统中的角色（如普通助教、组长等），下游可用于权限分析
- **🔵 最终决策：需新增 DWD 列 + FACT_MAPPINGS**

#### 1.2 `job_num`
- **ODS 存在**：✅ `billiards_ods.assistant_accounts_master.job_num`（TEXT）
- **DWD 现有列**：❌ `dim_assistant_ex` 中无 `job_num` 列
- **FACT_MAPPINGS**：未配置
- **采样值**：全部为 NULL（该门店未启用备用工号）
- **业务价值**：低。当前门店未使用，但作为预留字段有完整性价值
- **🔵 最终决策：需新增 DWD 列 + FACT_MAPPINGS**（保持数据完整性）

#### 1.3 `cx_unit_price`
- **ODS 存在**：✅ `billiards_ods.assistant_accounts_master.cx_unit_price`（NUMERIC(18,2)）
- **DWD 现有列**：❌ `dim_assistant_ex` 中无 `cx_unit_price` 列
- **FACT_MAPPINGS**：未配置
- **采样值**：`0.00`（促销时段单价，本门店未在账号表层面设置）
- **业务价值**：中。虽然当前值为 0，但多门店场景下可能有值，且与助教计费相关
- **🔵 最终决策：需新增 DWD 列 + FACT_MAPPINGS**

#### 1.4 `pd_unit_price`
- **ODS 存在**：✅ `billiards_ods.assistant_accounts_master.pd_unit_price`（NUMERIC(18,2)）
- **DWD 现有列**：❌ `dim_assistant_ex` 中无 `pd_unit_price` 列
- **FACT_MAPPINGS**：未配置
- **采样值**：`0.00`（普通时段单价，实际单价在助教商品或套餐配置中）
- **业务价值**：中。同 cx_unit_price，多门店场景下可能有值
- **🔵 最终决策：需新增 DWD 列 + FACT_MAPPINGS**

### 小结
| ODS 列 | 最终决策 | 操作 |
|--------|---------|------|
| system_role_id | 确实缺失 | 新增 DWD 列 + FACT_MAPPINGS |
| job_num | 确实缺失 | 新增 DWD 列 + FACT_MAPPINGS |
| cx_unit_price | 确实缺失 | 新增 DWD 列 + FACT_MAPPINGS |
| pd_unit_price | 确实缺失 | 新增 DWD 列 + FACT_MAPPINGS |


---

## 2. assistant_service_records → dwd_assistant_service_log / _ex（A 类）

### 脚本结论：site_assistant_id 已自动映射，operator_id/operator_name 缺失

### 逐字段调查

#### 2.1 `site_assistant_id`
- **ODS 存在**：✅ 两个列都存在：`site_assistant_id` 和 `order_assistant_id`
- **DWD 现有列**：✅ `dwd_assistant_service_log.site_assistant_id` 存在
- **FACT_MAPPINGS**：`("site_assistant_id", "order_assistant_id", None)` — 即 DWD 的 `site_assistant_id` 实际映射自 ODS 的 `order_assistant_id`
- **数据库验证**：DWD `site_assistant_id` 的值 = ODS `order_assistant_id` 的值（已通过 JOIN 查询确认）
- **⚠️ 关键发现**：脚本报告"自动映射（同名列）"是**不准确的**。实际上 DWD `site_assistant_id` 通过 FACT_MAPPINGS 映射自 ODS `order_assistant_id`，而非 ODS `site_assistant_id`。ODS 中的 `site_assistant_id`（助教档案 ID）和 `order_assistant_id`（订单助教 ID）是两个不同的字段。
- **业务语义**：ODS `site_assistant_id` 是助教在门店的档案 ID，`order_assistant_id` 是该笔服务记录的订单助教 ID。当前 DWD 存储的是 `order_assistant_id`。
- **🟢 最终决策：无需变更**（当前映射逻辑正确，DWD `site_assistant_id` 存储的是订单维度的助教 ID，符合事实表语义）

#### 2.2 `operator_id`
- **ODS 存在**：✅ `billiards_ods.assistant_service_records.operator_id`（BIGINT）
- **DWD 现有列**：❌ `dwd_assistant_service_log_ex` 中无 `operator_id` 列（已通过 `information_schema` 确认）
- **FACT_MAPPINGS**：未配置
- **采样值**：`2790687322443013`（操作员 ID，如收银员）
- **业务价值**：有。记录谁操作了该助教服务记录，用于操作审计
- **🔵 最终决策：需新增 DWD 列 + FACT_MAPPINGS**（添加到 `dwd_assistant_service_log_ex`）

#### 2.3 `operator_name`
- **ODS 存在**：✅ `billiards_ods.assistant_service_records.operator_name`（TEXT）
- **DWD 现有列**：❌ `dwd_assistant_service_log_ex` 中无 `operator_name` 列
- **FACT_MAPPINGS**：未配置
- **采样值**：`收银员:郑丽珊`
- **业务价值**：有。操作员名称快照，配合 operator_id 使用
- **🔵 最终决策：需新增 DWD 列 + FACT_MAPPINGS**（添加到 `dwd_assistant_service_log_ex`）

### 小结
| ODS 列 | 最终决策 | 操作 |
|--------|---------|------|
| site_assistant_id | 已通过 FACT_MAPPINGS 映射（order_assistant_id → site_assistant_id） | 无需变更 |
| operator_id | 确实缺失 | 新增 DWD 列（_ex 表）+ FACT_MAPPINGS |
| operator_name | 确实缺失 | 新增 DWD 列（_ex 表）+ FACT_MAPPINGS |

---

## 3. assistant_cancellation_records → dwd_assistant_trash_event_ex（A 类）

### 脚本结论：assistanton 缺失，语义待确认

### 逐字段调查

#### 3.1 `assistanton`
- **ODS 存在**：✅ `billiards_ods.assistant_cancellation_records.assistanton`
- **DWD 现有列**：❌ `dwd_assistant_trash_event_ex` 中无 `assistanton` 列
- **FACT_MAPPINGS**：未配置
- **采样值**：`99, 29, 4, 6, 15, 13, 37, 23, 18, 2`（整数值）
- **⚠️ 语义分析**：通过 JOIN 查询 `assistant_accounts_master`，确认 `assistanton` 的值与 `assistant_no`（助教编号）完全一致。例如 `assistanton=33` 对应 `assistant_no='33'`（王嘉琪），`assistanton=7` 对应 `assistant_no='7'`（郑美婷）。
- **结论**：`assistanton` 是 `assistant_no` 的整数形式，即"被废除的助教编号"。
- **DWD 已有列**：`dwd_assistant_trash_event.assistant_no` 已存在（映射自 `assistantname`，但这是助教昵称而非编号）
- **⚠️ 注意**：当前 FACT_MAPPINGS 中 `("assistant_no", "assistantname", None)` 将 ODS `assistantname`（助教昵称）映射到了 DWD `assistant_no`（助教编号），这可能是一个映射错误。`assistanton` 才是真正的助教编号。
- **业务价值**：有。记录被废除的助教编号，用于关联助教档案
- **🔵 最终决策：需新增 DWD 列 + FACT_MAPPINGS**（建议列名 `assistant_no_int`，类型 INTEGER，映射自 `assistanton`）

### 小结
| ODS 列 | 最终决策 | 操作 |
|--------|---------|------|
| assistanton | 确实缺失，语义为助教编号（整数形式） | 新增 DWD 列（_ex 表）+ FACT_MAPPINGS |

---

## 4. store_goods_sales_records → dwd_store_goods_sale（A 类）

### 脚本结论：discount_price 已自动映射

### 逐字段调查

#### 4.1 `discount_price`
- **ODS 存在**：✅ `billiards_ods.store_goods_sales_records.discount_price`
- **DWD 现有列**：✅ `dwd_store_goods_sale.discount_price` 存在（NUMERIC 类型）
- **FACT_MAPPINGS**：`("discount_price", "discount_money", None)` — DWD `discount_price` 映射自 ODS `discount_money`
- **⚠️ 关键发现**：脚本报告"自动映射（同名列）"是**不准确的**。DWD `discount_price` 实际通过 FACT_MAPPINGS 映射自 ODS `discount_money`（折扣金额），而非 ODS `discount_price`（折后单价）。
- **ODS 中两个字段**：`discount_price`（折后单价）和 `discount_money`（折扣金额）是不同字段
- **DWD 中**：`dwd_store_goods_sale.discount_price` 存储的是 `discount_money` 的值
- **DWD `dwd_store_goods_sale_ex`**：已有 `discount_money` 列（自动映射自 ODS `discount_money`）
- **结论**：ODS `discount_price`（折后单价）确实未被映射到 DWD。但 DWD `discount_price` 列名已被占用（存储的是 discount_money 的值）。
- **🟡 最终决策：跳过**（DWD `discount_price` 列名已被 `discount_money` 占用。ODS `discount_price` 的语义是"折后单价"，可通过 `ledger_unit_price` 或计算得出。如需单独映射，需要新建列名如 `unit_discount_price`，但当前业务优先级不高）

### 小结
| ODS 列 | 最终决策 | 操作 |
|--------|---------|------|
| discount_price | 列名已被占用（映射自 discount_money），ODS 原始折后单价未映射 | 跳过（可后续按需新增 `unit_discount_price`） |

---

## 5. member_balance_changes → dwd_member_balance_change_ex（A 类）

### 脚本结论：relate_id 缺失

### 逐字段调查

#### 5.1 `relate_id`
- **ODS 存在**：✅ `billiards_ods.member_balance_changes.relate_id`
- **DWD 现有列**：❌ `dwd_member_balance_change_ex` 中无 `relate_id` 列（已确认）
- **FACT_MAPPINGS**：未配置
- **采样值**：`2993181992014533, 0, 2888539288521221`（关联 ID，0 表示无关联）
- **业务价值**：有。关联充值记录或订单 ID，用于追溯余额变动的业务来源
- **🔵 最终决策：需新增 DWD 列 + FACT_MAPPINGS**（添加到 `dwd_member_balance_change_ex`）

### 附加发现
- 脚本未报告，但调查发现 `dwd_member_balance_change_ex` 已有 `operator_id` 和 `operator_name` 列，且已有数据（自动映射自 ODS 同名列）。这说明 ODS `member_balance_changes` 的 `operator_id`/`operator_name` 已正确流转到 DWD。

### 小结
| ODS 列 | 最终决策 | 操作 |
|--------|---------|------|
| relate_id | 确实缺失 | 新增 DWD 列（_ex 表）+ FACT_MAPPINGS |


---

## 6. tenant_goods_master → dim_tenant_goods_ex（A 类）

### 脚本结论：commoditycode 缺失

### 逐字段调查

#### 6.1 `commoditycode`
- **ODS 存在**：✅ `billiards_ods.tenant_goods_master` 中有两个相关列：`commoditycode` 和 `commodity_code`
- **DWD 现有列**：✅ `dim_tenant_goods_ex` 中有两个列：`commodity_code` 和 `commodity_code_list`
- **FACT_MAPPINGS**：`("commodity_code_list", "commodity_code", None)` — DWD `commodity_code_list` 映射自 ODS `commodity_code`
- **采样值**：ODS `commoditycode` = `{1234530}, {1234321}, {10000028}`（花括号包裹的编码集合）；ODS `commodity_code` = `10000002, 100011`（单个编码）
- **⚠️ 关键发现**：ODS 中 `commoditycode` 和 `commodity_code` 是两个不同字段。`commoditycode` 存储的是花括号包裹的编码集合（如 `{1234530}`），而 `commodity_code` 是单个商品编码。FACT_MAPPINGS 已将 `commodity_code` 映射到 DWD `commodity_code_list`。
- **DWD `commodity_code` 列**：`dim_tenant_goods_ex` 中已有 `commodity_code` 列，数据为 `1234571, 123321` 等（自动映射自 ODS `commodity_code`）
- **结论**：ODS `commoditycode`（花括号编码集合）确实未被映射到 DWD。DWD 已有 `commodity_code`（自动映射）和 `commodity_code_list`（FACT_MAPPINGS 映射），但 `commoditycode` 是第三个不同的字段。
- **业务价值**：低。`commoditycode` 看起来是 `commodity_code` 的数组/集合形式，数据冗余度高
- **🟡 最终决策：跳过**（`commoditycode` 是 `commodity_code` 的集合形式，DWD 已有 `commodity_code` 和 `commodity_code_list` 两列覆盖该语义，无需再映射冗余字段）

### 小结
| ODS 列 | 最终决策 | 操作 |
|--------|---------|------|
| commoditycode | 冗余字段，DWD 已有 commodity_code + commodity_code_list 覆盖 | 跳过 |


---

## 7. site_tables_master → dim_table_ex（A 类）

### 脚本结论：14 个字段全部缺失

### 逐字段调查

DWD `dim_table_ex` 当前仅有 6 个业务列：`table_id, show_status, is_online_reservation, table_cloth_use_time, table_cloth_use_cycle, table_status`（+ 4 个 SCD2 列）。

#### 7.1 `sitename`
- **ODS 存在**：✅ | **DWD 现有列**：❌ | **FACT_MAPPINGS**：未配置
- **采样值**：`朗朗桌球`（门店名称快照）
- **业务价值**：低。门店名称已在 `dim_site` 中维护，此处为冗余快照
- **🔵 最终决策：需新增 DWD 列 + FACT_MAPPINGS**（保持数据完整性，便于台桌维度直接查询门店名）

#### 7.2 `appletqrcodeurl`
- **ODS 存在**：✅ | **DWD 现有列**：❌ | **FACT_MAPPINGS**：未配置
- **采样值**：全部为 NULL
- **业务价值**：低。小程序二维码 URL，当前无数据
- **🔵 最终决策：需新增 DWD 列 + FACT_MAPPINGS**（保持完整性）

#### 7.3 `audit_status`
- **ODS 存在**：✅ | **DWD 现有列**：❌ | **FACT_MAPPINGS**：未配置
- **采样值**：`2`（审核状态枚举）
- **业务价值**：中。台桌审核状态，可能影响台桌可用性
- **🔵 最终决策：需新增 DWD 列 + FACT_MAPPINGS**

#### 7.4 `charge_free`
- **ODS 存在**：✅ | **DWD 现有列**：❌ | **FACT_MAPPINGS**：未配置
- **采样值**：`0`（是否免费，0=否）
- **业务价值**：高。标识台桌是否免费，直接影响计费逻辑
- **🔵 最终决策：需新增 DWD 列 + FACT_MAPPINGS**


#### 7.5 `create_time`
- **ODS 存在**：✅ | **DWD 现有列**：❌ | **FACT_MAPPINGS**：未配置
- **采样值**：`2025-07-16 11:30:20`（台桌创建时间）
- **业务价值**：中。记录台桌何时创建
- **🔵 最终决策：需新增 DWD 列 + FACT_MAPPINGS**

#### 7.6 `delay_lights_time`
- **ODS 存在**：✅ | **DWD 现有列**：❌ | **FACT_MAPPINGS**：未配置
- **采样值**：`0`（延迟关灯时间，秒）
- **业务价值**：中。灯控延迟配置
- **🔵 最终决策：需新增 DWD 列 + FACT_MAPPINGS**

#### 7.7 `is_rest_area`
- **ODS 存在**：✅ | **DWD 现有列**：❌ | **FACT_MAPPINGS**：未配置
- **采样值**：`0`（是否休息区，0=否）
- **业务价值**：中。区分台桌是否为休息区
- **🔵 最终决策：需新增 DWD 列 + FACT_MAPPINGS**

#### 7.8 `light_status`
- **ODS 存在**：✅ | **DWD 现有列**：❌ | **FACT_MAPPINGS**：未配置
- **采样值**：`1, 2`（灯控状态枚举）
- **业务价值**：中。台桌灯控状态
- **🔵 最终决策：需新增 DWD 列 + FACT_MAPPINGS**

#### 7.9 `only_allow_groupon`
- **ODS 存在**：✅ | **DWD 现有列**：❌ | **FACT_MAPPINGS**：未配置
- **采样值**：`2`（是否仅允许团购）
- **业务价值**：中。影响台桌的销售策略
- **🔵 最终决策：需新增 DWD 列 + FACT_MAPPINGS**

#### 7.10 `order_delay_time`
- **ODS 存在**：✅ | **DWD 现有列**：❌ | **FACT_MAPPINGS**：未配置
- **采样值**：`0`（订单延迟时间，秒）
- **业务价值**：中。订单延迟配置
- **🔵 最终决策：需新增 DWD 列 + FACT_MAPPINGS**

#### 7.11 `self_table`
- **ODS 存在**：✅ | **DWD 现有列**：❌ | **FACT_MAPPINGS**：未配置
- **采样值**：`1`（是否自助台桌）
- **业务价值**：中。标识台桌是否支持自助模式
- **🔵 最终决策：需新增 DWD 列 + FACT_MAPPINGS**

#### 7.12 `tablestatusname`
- **ODS 存在**：✅ | **DWD 现有列**：❌ | **FACT_MAPPINGS**：未配置
- **采样值**：`空闲中, 使用中, 暂停中`（台桌状态名称）
- **业务价值**：中。台桌状态的中文描述，配合 `table_status` 使用
- **🔵 最终决策：需新增 DWD 列 + FACT_MAPPINGS**

#### 7.13 `temporary_light_second`
- **ODS 存在**：✅ | **DWD 现有列**：❌ | **FACT_MAPPINGS**：未配置
- **采样值**：`0`（临时灯光秒数）
- **业务价值**：低。灯控相关配置
- **🔵 最终决策：需新增 DWD 列 + FACT_MAPPINGS**

#### 7.14 `virtual_table`
- **ODS 存在**：✅ | **DWD 现有列**：❌ | **FACT_MAPPINGS**：未配置
- **采样值**：`0`（是否虚拟台桌，0=否）
- **业务价值**：中。标识是否为虚拟台桌（如用于测试或特殊场景）
- **🔵 最终决策：需新增 DWD 列 + FACT_MAPPINGS**

### 小结
| ODS 列 | 最终决策 | 操作 |
|--------|---------|------|
| sitename | 确实缺失 | 新增 DWD 列 + FACT_MAPPINGS |
| appletqrcodeurl | 确实缺失 | 新增 DWD 列 + FACT_MAPPINGS |
| audit_status | 确实缺失 | 新增 DWD 列 + FACT_MAPPINGS |
| charge_free | 确实缺失 | 新增 DWD 列 + FACT_MAPPINGS |
| create_time | 确实缺失 | 新增 DWD 列 + FACT_MAPPINGS |
| delay_lights_time | 确实缺失 | 新增 DWD 列 + FACT_MAPPINGS |
| is_rest_area | 确实缺失 | 新增 DWD 列 + FACT_MAPPINGS |
| light_status | 确实缺失 | 新增 DWD 列 + FACT_MAPPINGS |
| only_allow_groupon | 确实缺失 | 新增 DWD 列 + FACT_MAPPINGS |
| order_delay_time | 确实缺失 | 新增 DWD 列 + FACT_MAPPINGS |
| self_table | 确实缺失 | 新增 DWD 列 + FACT_MAPPINGS |
| tablestatusname | 确实缺失 | 新增 DWD 列 + FACT_MAPPINGS |
| temporary_light_second | 确实缺失 | 新增 DWD 列 + FACT_MAPPINGS |
| virtual_table | 确实缺失 | 新增 DWD 列 + FACT_MAPPINGS |


---

## 8. recharge_settlements → dwd_recharge_order（B 类）

### 脚本结论：5 个 ODS→DWD 未映射 + 5 个 DWD 无 ODS 源

### 逐字段调查

#### 8.1-8.5 ODS 驼峰列 → DWD 蛇形列（命名差异导致的映射缺失）

| # | ODS 列（驼峰） | DWD 列（蛇形） | DWD 列存在 | FACT_MAPPINGS | 结论 |
|---|---------------|--------------|-----------|--------------|------|
| 1 | `plcouponsaleamount` | `pl_coupon_sale_amount` | ✅ 已存在于 `dwd_recharge_order` | ❌ 未配置 | 仅补 FACT_MAPPINGS |
| 2 | `mervousalesamount` | `mervou_sales_amount` | ✅ 已存在于 `dwd_recharge_order` | ❌ 未配置 | 仅补 FACT_MAPPINGS |
| 3 | `electricitymoney` | `electricity_money` | ✅ 已存在于 `dwd_recharge_order` | ❌ 未配置 | 仅补 FACT_MAPPINGS |
| 4 | `realelectricitymoney` | `real_electricity_money` | ✅ 已存在于 `dwd_recharge_order` | ❌ 未配置 | 仅补 FACT_MAPPINGS |
| 5 | `electricityadjustmoney` | `electricity_adjust_money` | ✅ 已存在于 `dwd_recharge_order` | ❌ 未配置 | 仅补 FACT_MAPPINGS |

**⚠️ 关键发现**：
- DWD `dwd_recharge_order` 中这 5 个列**已经存在**（DDL 已定义，数据库已创建）
- 但 FACT_MAPPINGS 中**未配置**这 5 个映射条目
- 数据库查询显示 `dwd_recharge_order` 有 485 行，其中 80 行有 `pl_coupon_sale_amount` 和 `electricity_money` 的值（非 NULL）
- 但所有值均为 0.00（无非零值），说明这些列虽有数据但可能是自动映射的默认值
- **对比 `dwd_settlement_head`**：同样的 5 个字段在 `dwd_settlement_head` 的 FACT_MAPPINGS 中**已正确配置**（如 `("electricity_money", "electricitymoney", None)`）
- **根因**：`dwd_recharge_order` 的 FACT_MAPPINGS 遗漏了这 5 个条目，而 `dwd_settlement_head` 已正确配置

**脚本报告的"DWD orphan"（#6-#10）**：这 5 个"DWD 列存在但无 ODS 映射"实际上就是上面 5 个 ODS 列的蛇形命名形式。脚本将它们分别报告为两组问题，但实际上是同一组映射缺失。

- **🟠 最终决策：仅补 FACT_MAPPINGS**（DWD 列已存在，无需 DDL 变更，只需在 `FACT_MAPPINGS["dwd.dwd_recharge_order"]` 中添加 5 个映射条目）

### 小结
| ODS 列 | DWD 列 | 最终决策 | 操作 |
|--------|--------|---------|------|
| plcouponsaleamount | pl_coupon_sale_amount | 映射遗漏 | 仅补 FACT_MAPPINGS |
| mervousalesamount | mervou_sales_amount | 映射遗漏 | 仅补 FACT_MAPPINGS |
| electricitymoney | electricity_money | 映射遗漏 | 仅补 FACT_MAPPINGS |
| realelectricitymoney | real_electricity_money | 映射遗漏 | 仅补 FACT_MAPPINGS |
| electricityadjustmoney | electricity_adjust_money | 映射遗漏 | 仅补 FACT_MAPPINGS |


---

## 9. store_goods_master → dim_store_goods_ex（B 类）

### 脚本结论：time_slot_sale ODS 不存在，batch_stock_quantity 缺失，provisional_total_cost 已自动映射

### 逐字段调查

#### 9.1 `time_slot_sale`
- **ODS 存在**：❌ `billiards_ods.store_goods_master` 中无 `time_slot_sale` 列（已通过 `information_schema` 确认）
- **API 字段**：需确认 API 是否返回该字段。当前 ODS DDL 和数据库中均不存在此列。
- **🟡 最终决策：需确认 API**（ODS 列不存在，需要先获取 API JSON 样本确认字段是否存在。如果 API 返回该字段，需先更新 ODS DDL 和 loader，再映射到 DWD）

#### 9.2 `batch_stock_quantity`
- **ODS 存在**：✅ `billiards_ods.store_goods_master.batch_stock_quantity`（NUMERIC）
- **DWD 现有列**：❌ `dim_store_goods_ex` 中无 `batch_stock_quantity` 列（已确认）
- **FACT_MAPPINGS**：`dim_store_goods` 中有 `("batch_stock_qty", "stock", None)` — 但这是将 ODS `stock` 映射到 DWD `batch_stock_qty`，与 `batch_stock_quantity` 无关
- **采样值**：`35.00, 270.00, 8.00`（批次库存数量）
- **业务价值**：有。批次库存数量，与 `stock`（当前库存）不同
- **🔵 最终决策：需新增 DWD 列 + FACT_MAPPINGS**（添加到 `dim_store_goods_ex`）

#### 9.3 `provisional_total_cost`
- **ODS 存在**：✅ `billiards_ods.store_goods_master.provisional_total_cost`
- **DWD 现有列**：✅ `dim_store_goods_ex.provisional_total_cost` 存在（NUMERIC 类型）
- **FACT_MAPPINGS**：`dim_store_goods_ex` 中有 `("provisional_total_cost", "total_purchase_cost", None)` — DWD `provisional_total_cost` 映射自 ODS `total_purchase_cost`
- **⚠️ 关键发现**：脚本报告"自动映射（同名列）"是**不准确的**。DWD `provisional_total_cost` 实际通过 FACT_MAPPINGS 映射自 ODS `total_purchase_cost`（采购总成本），而非 ODS `provisional_total_cost`（暂估总成本）。ODS 中 `provisional_total_cost` 和 `total_purchase_cost` 是两个不同字段。
- **结论**：ODS `provisional_total_cost`（暂估总成本）确实未被映射到 DWD。DWD `provisional_total_cost` 列名已被占用（存储的是 `total_purchase_cost` 的值）。
- **🟡 最终决策：跳过**（DWD 列名已被占用。如需单独映射 ODS `provisional_total_cost`，需新建列名。当前业务优先级不高）

### 小结
| ODS 列 | 最终决策 | 操作 |
|--------|---------|------|
| time_slot_sale | ODS 列不存在 | 需确认 API 是否返回该字段 |
| batch_stock_quantity | 确实缺失 | 新增 DWD 列（_ex 表）+ FACT_MAPPINGS |
| provisional_total_cost | DWD 列名已被占用（映射自 total_purchase_cost） | 跳过 |


---

## 10. goods_stock_summary（C 类）

### 脚本结论：14 个 ODS 字段，无 DWD 目标表，需新建

### 调查

- **DWD 表存在**：❌ `billiards_dwd` 中无任何 `goods_stock` 相关表（已通过 `information_schema.tables` 确认）
- **TABLE_MAP**：未注册
- **ODS 数据**：14 个字段全部有数据（采样值已在脚本报告中列出）
- **业务价值**：有。库存汇总数据（期初/期末库存、出入库、销售额等），对库存分析和成本核算有价值

### 最终决策
- **🔵 需新建 DWD 表 `dwd_goods_stock_summary`**
- 需要：DDL 创建表 + TABLE_MAP 注册 + FACT_MAPPINGS 配置 + 新建 loader/task 代码
- 14 个 ODS 列全部映射（驼峰 → 蛇形命名转换）

---

## 11. goods_stock_movements（C 类）

### 脚本结论：19 个 ODS 字段，无 DWD 目标表，需新建

### 调查

- **DWD 表存在**：❌ `billiards_dwd` 中无任何 `goods_stock_movement` 相关表
- **TABLE_MAP**：未注册
- **ODS 数据**：19 个字段全部有数据
- **业务价值**：有。库存变动明细（入库、出库、销售、退货等），对库存追踪和审计有价值

### 最终决策
- **🔵 需新建 DWD 表 `dwd_goods_stock_movement`**
- 需要：DDL 创建表 + TABLE_MAP 注册 + FACT_MAPPINGS 配置 + 新建 loader/task 代码
- 19 个 ODS 列全部映射


---

## 12. settlement_ticket_details（C 类）

### 脚本结论：38+3 个字段（含 payload/source_file/source_endpoint/fetched_at/content_hash），无 DWD 目标表

### 调查

- **DWD 表存在**：❌ `billiards_dwd` 中无 `settlement_ticket` 相关表
- **TABLE_MAP**：未注册
- **ODS 数据**：✅ 有 193 行数据（脚本报告中的"采样记录数为 0"可能是旧数据）
- **ODS 实际列**：43 列（含 `ordersettleid, actualpayment, adjustamount, ...` 等 38 个业务列 + `payload, source_file, source_endpoint, fetched_at, content_hash` 5 个元数据列）
- **⚠️ 关键发现**：
  - 脚本报告"38 个无 JSON 源"是因为这些字段是从 API JSON 中解析出来的平层字段，而非直接的 JSON 键名
  - ODS 表中有 `payload` 列（存储原始 JSON），说明数据确实从 API 获取
  - `memberprofile`、`orderitem`、`tenantmembercardlogs` 是嵌套 JSON 对象/数组，存储为 TEXT/JSONB
  - 业务字段包括：结算金额、支付方式、会员信息、退款、配送等完整的结账小票信息

### 字段分类

| 类别 | 字段 | 数量 |
|------|------|------|
| 核心业务字段 | ordersettleid, actualpayment, consumemoney, payamount, paytime, settletype, paymentmethod 等 | ~25 |
| 门店/租户快照 | siteid, sitename, siteaddress, sitebusinesstel, tenantid, tenantname | 6 |
| 嵌套 JSON 字段 | memberprofile, orderitem, tenantmembercardlogs | 3 |
| 元数据字段 | payload, source_file, source_endpoint, fetched_at, content_hash | 5（不映射到 DWD） |
| 低价值/空值字段 | deliveryaddress, deliveryfee, ticketcustomcontent, ticketremark, orderremark | 5 |

### 最终决策
- **🔵 需新建 DWD 表**（建议 `dwd_settlement_ticket` + `dwd_settlement_ticket_ex`）
- 核心业务字段映射到主表，门店快照和低价值字段映射到 _ex 表
- 嵌套 JSON 字段（memberprofile, orderitem, tenantmembercardlogs）暂不展开，保留为 JSONB 类型
- 元数据字段（payload, source_file 等）不映射到 DWD
- 需要：DDL 创建表 + TABLE_MAP 注册 + FACT_MAPPINGS 配置


---

## 全局汇总

### 脚本结论 vs 调查结论对比

| 指标 | 脚本结论 | 调查修正后 | 差异说明 |
|------|---------|-----------|---------|
| 已映射（无需变更） | 3 | 3 | 一致（但 2 个"自动映射"实际是 FACT_MAPPINGS 映射） |
| 映射遗漏（仅补 FACT_MAPPINGS） | 0 | **5** | recharge_settlements 的 5 个电费/券字段 |
| 确实缺失（需新增 DWD 列） | 29 | **24** | 减去：discount_price(跳过)、commoditycode(跳过)、provisional_total_cost(跳过)、time_slot_sale(待确认API)、5个recharge改为仅补映射 |
| 无 DWD 表（需新建） | 74 | 74 | 一致 |
| ODS 列不存在（需确认 API） | 1 | 1 | time_slot_sale |

### 脚本报告中的不准确之处

1. **site_assistant_id（assistant_service_records）**：脚本报告"自动映射（同名列）"，实际是通过 FACT_MAPPINGS 映射自 `order_assistant_id`
2. **discount_price（store_goods_sales_records）**：脚本报告"自动映射（同名列）"，实际是通过 FACT_MAPPINGS 映射自 `discount_money`
3. **provisional_total_cost（store_goods_master）**：脚本报告"自动映射（同名列）"，实际是通过 FACT_MAPPINGS 映射自 `total_purchase_cost`
4. **recharge_settlements 10 个字段**：脚本将 5 个 ODS 列和 5 个 DWD orphan 分别报告，实际是同一组映射缺失（驼峰/蛇形命名差异）
5. **settlement_ticket_details 行数**：脚本可能基于旧数据报告"采样记录数为 0"，实际有 193 行

### 操作汇总表

| 表 | 操作类型 | 字段数 | 具体操作 |
|----|---------|--------|---------|
| assistant_accounts_master | 新增 DWD 列 + FACT_MAPPINGS | 4 | system_role_id, job_num, cx_unit_price, pd_unit_price → dim_assistant_ex |
| assistant_service_records | 新增 DWD 列 + FACT_MAPPINGS | 2 | operator_id, operator_name → dwd_assistant_service_log_ex |
| assistant_cancellation_records | 新增 DWD 列 + FACT_MAPPINGS | 1 | assistanton → dwd_assistant_trash_event_ex（列名 assistant_no_int） |
| store_goods_sales_records | 跳过 | 0 | discount_price 列名已被占用 |
| member_balance_changes | 新增 DWD 列 + FACT_MAPPINGS | 1 | relate_id → dwd_member_balance_change_ex |
| tenant_goods_master | 跳过 | 0 | commoditycode 冗余 |
| site_tables_master | 新增 DWD 列 + FACT_MAPPINGS | 14 | 全部 14 个字段 → dim_table_ex |
| recharge_settlements | 仅补 FACT_MAPPINGS | 5 | 5 个电费/券字段（DWD 列已存在） |
| store_goods_master | 新增 DWD 列 + 确认 API | 1+1 | batch_stock_quantity → dim_store_goods_ex；time_slot_sale 待确认 |
| goods_stock_summary | 新建 DWD 表 | 14 | 全部字段 |
| goods_stock_movements | 新建 DWD 表 | 19 | 全部字段 |
| settlement_ticket_details | 新建 DWD 表 | ~38 | 核心业务字段 + 快照字段 |

### 下一步行动

1. **立即可执行**（无需额外确认）：
   - A 类表 DDL + FACT_MAPPINGS 变更（22 个字段）
   - recharge_settlements 仅补 FACT_MAPPINGS（5 个映射条目）
   - store_goods_master 的 batch_stock_quantity 新增

2. **需确认后执行**：
   - store_goods_master 的 time_slot_sale：需获取 API JSON 样本确认字段是否存在

3. **需设计后执行**：
   - goods_stock_summary 新建 DWD 表（14 列）
   - goods_stock_movements 新建 DWD 表（19 列）
   - settlement_ticket_details 新建 DWD 表（~38 列，含 main/ex 分表设计）

