微信小程序页面迁移校验之前 P5任务处理之前

This commit is contained in:
Neo
2026-03-09 01:19:21 +08:00
parent 263bf96035
commit 6e20987d2f
1112 changed files with 153824 additions and 219694 deletions

View File

@@ -51,7 +51,7 @@ graph LR
| 文档 | 说明 |
|------|------|
| [BaseTask 公共机制](base_task_mechanism.md) | 任务基类模板方法、TaskContext、时间窗口、注册表、Flow 执行 |
| [ODS 层任务](ods_tasks.md) | 23 个通用 ODS 任务的架构、配置结构、API 端点、目标表 |
| [ODS 层任务](ods_tasks.md) | 22 个通用 ODS 任务的架构、配置结构、API 端点、目标表 |
| [DWD 层任务](dwd_tasks.md) | DWD_LOAD_FROM_ODS 核心装载、SCD2 处理、质量校验 |
| [DWS 层任务](dws_tasks.md) | 助教业绩、会员分析、财务统计、库存汇总、运维任务共 17 个 DWS 任务 |
| [INDEX 层任务](index_tasks.md) | WBI/NCI/RS/SPI 指数算法 + ML 手动台账导入 |
@@ -69,10 +69,9 @@ graph LR
|----------|-----------|--------|----------|------|
| `ODS_ASSISTANT_ACCOUNT` | `OdsAssistantAccountsTask` | `ods.assistant_accounts_master` | 助教账号档案 | [查看](ods_tasks.md) |
| `ODS_ASSISTANT_LEDGER` | `OdsAssistantLedgerTask` | `ods.assistant_service_records` | 助教服务流水 | [查看](ods_tasks.md) |
| `ODS_ASSISTANT_ABOLISH` | `OdsAssistantAbolishTask` | `ods.assistant_cancellation_records` | 助教废除记录 | [查看](ods_tasks.md) |
| `ODS_INVENTORY_CHANGE` | `OdsInventoryChangeTask` | `ods.goods_stock_movements` | 库存变化记录 | [查看](ods_tasks.md) |
| `ODS_INVENTORY_STOCK` | `OdsInventoryStockTask` | `ods.goods_stock_summary` | 库存汇总 | [查看](ods_tasks.md) |
| `ODS_GROUP_PACKAGE` | `OdsPackageTask` | `ods.group_buy_packages` | 团购套餐定义 | [查看](ods_tasks.md) |
| `ODS_GROUP_PACKAGE` | `OdsPackageTask` | `ods.group_buy_packages` | 团购套餐定义 + 详情子流程(通过 `detail_endpoint` 串行调用 `QueryPackageCouponInfo` 获取每个团购的详情数据,写入 `ods.group_buy_package_details` | [查看](ods_tasks.md) |
| `ODS_GROUP_BUY_REDEMPTION` | `OdsGroupBuyRedemptionTask` | `ods.group_buy_redemption_records` | 团购套餐核销 | [查看](ods_tasks.md) |
| `ODS_MEMBER` | `OdsMemberTask` | `ods.member_profiles` | 会员档案 | [查看](ods_tasks.md) |
| `ODS_MEMBER_BALANCE` | `OdsMemberBalanceTask` | `ods.member_balance_changes` | 会员余额变动 | [查看](ods_tasks.md) |

View File

@@ -77,13 +77,15 @@ load(extracted, context) → 遍历 TABLE_MAP
| `dwd.dim_goods_category` | `ods.stock_goods_category_tree` | 商品分类维度(含子类展开) |
| `dwd.dim_groupbuy_package` | `ods.group_buy_packages` | 团购套餐维度 |
| `dwd.dim_groupbuy_package_ex` | `ods.group_buy_packages` | 团购套餐扩展 |
| `dwd.dim_staff` | `ods.staff_info_master` | 员工维度 |
| `dwd.dim_staff_ex` | `ods.staff_info_master` | 员工扩展 |
#### 事实表映射
| DWD 表 | ODS 源表 | 说明 |
|--------|----------|------|
| `dwd.dwd_settlement_head` | `ods.settlement_records` | 结算头(订单结算主记录) |
| `dwd.dwd_settlement_head` | `ods.settlement_records` | 结算头(订单结算主记录)— 详见下方「结算头关键字段口径」 |
| `dwd.dwd_settlement_head_ex` | `ods.settlement_records` | 结算头扩展(支付方式、撤单、促销等) |
| `dwd.dwd_table_fee_log` | `ods.table_fee_transactions` | 台费流水 |
| `dwd.dwd_table_fee_log_ex` | `ods.table_fee_transactions` | 台费流水扩展(销售员、消费类型等) |
@@ -93,8 +95,8 @@ load(extracted, context) → 遍历 TABLE_MAP
| `dwd.dwd_store_goods_sale_ex` | `ods.store_goods_sales_records` | 商品销售扩展 |
| `dwd.dwd_assistant_service_log` | `ods.assistant_service_records` | 助教服务记录 |
| `dwd.dwd_assistant_service_log_ex` | `ods.assistant_service_records` | 助教服务扩展 |
| `dwd.dwd_assistant_trash_event` | `ods.assistant_cancellation_records` | 助教取消/废单事件 |
| `dwd.dwd_assistant_trash_event_ex` | `ods.assistant_cancellation_records` | 助教取消扩展 |
| ~~`dwd.dwd_assistant_trash_event`~~ | ~~`ods.assistant_cancellation_records`~~ | ~~助教取消/废单事件2026-02-22 DROP2026-03-01 清理残留)~~ |
| ~~`dwd.dwd_assistant_trash_event_ex`~~ | ~~`ods.assistant_cancellation_records`~~ | ~~助教取消扩展2026-02-22 DROP2026-03-01 清理残留)~~ |
| `dwd.dwd_member_balance_change` | `ods.member_balance_changes` | 会员余额变动 |
| `dwd.dwd_member_balance_change_ex` | `ods.member_balance_changes` | 会员余额变动扩展 |
| `dwd.dwd_groupbuy_redemption` | `ods.group_buy_redemption_records` | 团购核销记录 |
@@ -106,8 +108,48 @@ load(extracted, context) → 遍历 TABLE_MAP
| `dwd.dwd_payment` | `ods.payment_transactions` | 支付记录 |
| `dwd.dwd_refund` | `ods.refund_transactions` | 退款记录 |
| `dwd.dwd_refund_ex` | `ods.refund_transactions` | 退款扩展 |
| `dwd.dwd_goods_stock_summary` | `ods.goods_stock_summary` | 库存汇总 |
| `dwd.dwd_goods_stock_movement` | `ods.goods_stock_movements` | 库存变动 |
> 共计 **17 对维度映射**(含 `_ex`+ **23 对事实映射**(含 `_ex`= **40 对**映射。
> 共计 **19 对维度映射**(含 `_ex`+ **23 对事实映射**(含 `_ex`,已排除 2026-02-22 DROP 的 assistant_trash_event= **42 对**有效映射。
---
### 结算头关键字段口径
`dwd_settlement_head` 是核心交易事实表,以下字段在下游消费时需特别注意:
#### settle_type 枚举
| 值 | 含义 | 说明 |
|----|------|------|
| 1 | 台桌结账 | 正常台桌消费结账 |
| 3 | 商城订单 | 商品零售订单 |
| 6 | 退货订单 | 商品退货 |
| 7 | 退款订单 | 金额退款 |
> DWS 层计算发生额、收入等指标时,通常只取 `settle_type IN (1, 3)`(正向交易),排除退货/退款。
> 本表无 `is_delete` 字段,不可用 `is_delete` 过滤。
#### consume_money 口径警告
`consume_money` 存在三种历史口径A/B/C**DWS 层不应直接使用**。
应使用 `items_sum` 口径:
```
items_sum = table_charge_money + goods_money + assistant_pd_money + assistant_cx_money + electricity_money
```
> 详见 [consume_money 口径校准文档](../../../../docs/reports/DWD-DOC/consume/consume-money-caliber.md)
> 及 [BD 手册 dwd_settlement_head](../database/DWD/main/BD_manual_dwd_settlement_head.md)
#### 支付渠道恒等式
```
balance_amount = recharge_card_amount + gift_card_amount -- 储值卡 = 充值卡 + 礼品卡
```
> `balance_amount` 是独立支付渠道,`recharge_card_amount` / `gift_card_amount` 是其分账明细,三者不可重复计算。
---

View File

@@ -8,7 +8,7 @@
## 概述
DWS 层共有 17 个已注册任务(含 DWS_MAINTENANCE按业务域分为组:
DWS 层共有 19 个已注册任务(含 DWS_MAINTENANCE按业务域分为组:
### 助教业绩域6 个)
@@ -28,6 +28,13 @@ DWS 层共有 17 个已注册任务(含 DWS_MAINTENANCE按业务域分
| `DWS_MEMBER_CONSUMPTION` | `MemberConsumptionTask` | `dws_member_consumption_summary` | 日期+会员 | delete-before-insert |
| `DWS_MEMBER_VISIT` | `MemberVisitTask` | `dws_member_visit_detail` | 日期+会员+结账单 | delete-before-insert |
### 项目标签域2 个)
| 任务代码 | Python 类 | 目标表 | 粒度 | 更新策略 |
|----------|-----------|--------|------|----------|
| `DWS_ASSISTANT_PROJECT_TAG` | `AssistantProjectTagTask` | `dws_assistant_project_tag` | 助教+时间窗口+项目 | 全量删除重建(按 site_id |
| `DWS_MEMBER_PROJECT_TAG` | `MemberProjectTagTask` | `dws_member_project_tag` | 会员+时间窗口+项目 | 全量删除重建(按 site_id |
### 财务统计域4 个)
| 任务代码 | Python 类 | 目标表 | 粒度 | 更新策略 |
@@ -373,7 +380,7 @@ DWS 汇总计算涉及历史月份时,不能直接使用维度表的"当前版
```
dwd_assistant_service_log ──┬──► DWS_ASSISTANT_DAILY日度明细
dwd_assistant_trash_event ──┘ │
dwd_assistant_service_log_ex ┘ │
DWS_ASSISTANT_MONTHLY月度汇总+档位+排名)
@@ -448,7 +455,7 @@ dwd_assistant_service_log ────► DWS_ASSISTANT_CUSTOMER客户关系
| 来源表 | Schema | 用途 |
|--------|--------|------|
| `dwd_assistant_service_log` | `dwd` | 助教服务流水(主数据源) |
| `dwd_assistant_trash_event` | `dwd` | 废除记录(排除无效业绩 |
| `dwd_assistant_service_log_ex` | `dwd` | 扩展表(`is_trash` 标记废除记录) |
| `dim_assistant` | `dwd` | 助教维度SCD2获取当日等级 |
| `cfg_skill_type` | `dws` | 技能 → 课程类型映射 |
@@ -459,21 +466,23 @@ dwd_assistant_service_log ────► DWS_ASSISTANT_CUSTOMER客户关系
| 字段分组 | 字段 | 说明 |
|----------|------|------|
| 标识 | `site_id`, `tenant_id`, `assistant_id`, `assistant_nickname`, `stat_date` | 门店、助教、日期 |
| 等级 | `assistant_level_code`, `assistant_level_name` | SCD2 as-of 取值,取统计日当日生效的等级 |
| 等级 | `assistant_level_code`, `assistant_level_name` | SCD2 as-of 取值`level_code``level_name` 由 code 静态映射得出 |
| 服务次数 | `total_service_count`, `base_service_count`, `bonus_service_count`, `room_service_count` | 总/基础课/附加课/包厢课 |
| 计费秒数 | `total_seconds`, `base_seconds`, `bonus_seconds`, `room_seconds` | 原始秒数 |
| 计费小时 | `total_hours`, `base_hours`, `bonus_hours`, `room_hours` | 秒数 ÷ 3600`Decimal` 精度 |
| 计费金额 | `total_ledger_amount`, `base_ledger_amount`, `bonus_ledger_amount`, `room_ledger_amount` | 台账金额 |
| 去重统计 | `unique_customers`, `unique_tables` | 去重客户数(排除散客)、去重台桌数 |
| 废除统计 | `trashed_seconds`, `trashed_count` | 被废除的秒数和次数 |
| 惩罚检测 | `penalty_minutes`, `penalty_reason`, `is_exempt`, `per_hour_contribution` | 惩罚分钟数(公式:`actual_minutes × (1 - per_hour_contribution / 24)`)、惩罚原因、是否豁免、每小时贡献金额(= `base_ledger_amount / base_hours / overlap_count` |
#### 核心业务逻辑
1. **课程类型分类**:通过 `skill_id` 查询 `cfg_skill_type` 映射,分为 `BASE`(基础课)、`BONUS`(附加课)、`ROOM`(包厢课),未匹配默认 `BASE`
2. **废除记录排除**`assistant_service_id` 为键构建废除索引,被废除的服务记录不计入有效业绩(服务次数、时长、金额),但单独统计 `trashed_seconds``trashed_count`
2. **废除记录排除**通过 JOIN `dwd_assistant_service_log_ex``is_trash = 1` 标记识别废除记录(`dwd_assistant_trash_event` 已于 2026-02-22 废弃),被废除的服务记录不计入有效业绩(服务次数、时长、金额),但单独统计 `trashed_seconds``trashed_count`
3. **助教等级 SCD2 取值**:调用 `get_assistant_level_asof(assistant_id, service_date)` 获取统计日当日生效的等级版本,而非当前最新版本
4. **散客过滤**`unique_customers` 统计时排除 `member_id` 为 0 或 None 的散客
5. **客户/台桌去重**:无论服务记录是否被废除,客户和台桌均参与去重统计
6. **定档折算惩罚检测**聚合完成后检测同一台桌多名助教重叠挂台的违规情况规则2。计算 `per_hour_contribution = base_ledger_amount / base_hours / overlap_count`,若低于阈值(默认 24 元/小时)则按比例扣减 `penalty_minutes`。豁免助教(`is_exempt = True`)不参与惩罚计算。
---
@@ -813,6 +822,9 @@ dim_table ────────────────────┘
| 全量累计 | `first_consume_date`, `last_consume_date`, `total_visit_count`, `total_consume_amount`, `total_recharge_amount`, `total_table_fee`, `total_goods_amount`, `total_assistant_amount` | 首次/最近消费日期、累计到店次数、累计消费金额、累计充值金额、累计台费、累计商品金额、累计助教费用 |
| 滚动窗口(次数) | `visit_count_7d`, `visit_count_10d`, `visit_count_15d`, `visit_count_30d`, `visit_count_60d`, `visit_count_90d` | 各窗口到店次数 |
| 滚动窗口(金额) | `consume_amount_7d`, `consume_amount_10d`, `consume_amount_15d`, `consume_amount_30d`, `consume_amount_60d`, `consume_amount_90d` | 各窗口消费金额 |
| 充值窗口(笔数) | `recharge_count_30d`, `recharge_count_60d`, `recharge_count_90d` | 近 30/60/90 天充值笔数来源dwd_recharge_order |
| 充值窗口(金额) | `recharge_amount_30d`, `recharge_amount_60d`, `recharge_amount_90d` | 近 30/60/90 天充值金额(仅 `pay_amount` 现金部分,不含 `point_amount` 赠送) |
| 次均消费 | `avg_ticket_amount` | total_consume_amount / MAX(total_visit_count, 1) |
| 卡余额 | `cash_card_balance`, `gift_card_balance`, `total_card_balance` | 储值卡(现金卡)余额、赠送卡余额、总余额 |
| 活跃度 | `days_since_last`, `is_active_7d`, `is_active_30d`, `is_active_90d` | 距最近消费天数、近 7/30/90 天是否活跃 |
| 客户分层 | `customer_tier` | 分层标签(高价值/中等/低活跃/流失) |
@@ -821,15 +833,17 @@ dim_table ────────────────────┘
**1. 散客排除**
`member_id` 为 0 或 None 的散客不进入此表统计。SQL 层面和 transform 阶段均做过滤。
`member_id` 为 0 或 None 的散客不进入此表统计。SQL 层面和 transform 阶段均做过滤,同时通过 `settle_type IN (1, 3)` 仅保留台桌结账和商城订单(排除退货/退款)
**2. 消费统计来源**
`dwd_settlement_head``member_id` 聚合,消费金额拆分为:
- `consume_money`:总消费金额
`dwd_settlement_head``member_id` 聚合,消费金额使用 `items_sum` 口径拆分为:
- `items_sum`:消费项目合计(= `table_charge_money + goods_money + assistant_pd_money + assistant_cx_money + electricity_money`
- `table_charge_money`:台费
- `goods_money`:商品金额
- `assistant_pd_money + assistant_cx_money`:助教费用(专业课 + 陪练课合计)
- `assistant_pd_money + assistant_cx_money`:助教费用(陪打 + 超休合计)
> ⚠️ 不使用 `consume_money`(三种历史口径混合),详见 `docs/reports/DWD-DOC/consume/consume-money-caliber.md`
**3. 滚动窗口**
@@ -909,7 +923,7 @@ dim_table ────────────────────┘
| 会员信息 | `member_nickname`, `member_mobile`, `member_birthday` | 昵称、脱敏手机号、生日 |
| 台桌信息 | `table_id`, `table_name`, `area_name`, `area_category` | 台桌 ID、台桌名称、区域名称、区域分类 |
| 消费金额 | `table_fee`, `goods_amount`, `assistant_amount`, `total_consume`, `total_discount`, `actual_pay` | 台费、商品金额、助教费用、总消费、总优惠、实付金额 |
| 支付方式 | `cash_pay`, `cash_card_pay`, `gift_card_pay`, `groupbuy_pay` | 现金/在线支付、储值卡支付、赠送卡支付、团购券支付 |
| 支付方式 | `cash_pay`, `balance_pay`, `recharge_card_pay`, `gift_card_pay`, `groupbuy_pay` | 现金/在线支付、储值卡总支付、现金充值卡支付、赠送卡支付、团购券支付 |
| 时长 | `table_duration_min`, `assistant_duration_min` | 台桌使用时长(分钟)、助教服务时长(分钟) |
| 助教服务 | `assistant_services` | JSON 格式的助教服务明细 |
@@ -917,15 +931,15 @@ dim_table ────────────────────┘
**1. 散客排除**
SQL 层面通过 `member_id IS NOT NULL AND member_id != 0` 过滤transform 阶段通过 `is_guest()` 二次过滤。
SQL 层面通过 `member_id IS NOT NULL AND member_id != 0` 过滤,同时通过 `settle_type IN (1, 3)` 仅保留台桌结账和商城订单(排除退货/退款),transform 阶段通过 `is_guest()` 二次过滤。
**2. 消费金额拆分**
`dwd_settlement_head` 直接读取各金额字段:
- `table_fee``table_charge_money`(台费)
- `goods_amount``goods_money`(商品金额)
- `assistant_amount``assistant_pd_money + assistant_cx_money`专业课 + 陪练课助教费用合计)
- `total_consume``consume_money`(总消费金额
- `assistant_amount``assistant_pd_money + assistant_cx_money`陪打 + 超休助教费用合计)
- `total_consume``items_sum`= `table_charge_money + goods_money + assistant_pd_money + assistant_cx_money + electricity_money`,不使用 `consume_money`
- `actual_pay``pay_amount`(实付金额)
**3. 总优惠计算**
@@ -943,7 +957,8 @@ total_discount = adjust_amount + member_discount_amount + rounding_amount
| 字段 | 来源字段 | 说明 |
|------|----------|------|
| `cash_pay` | `pay_amount` | 现金/在线支付 |
| `cash_card_pay` | `balance_amount` | 储值卡(现金卡)支付 |
| `balance_pay` | `balance_amount` | 储值卡总支付(= recharge_card_pay + gift_card_pay |
| `recharge_card_pay` | `recharge_card_amount` | 现金充值卡支付balance_pay 的子项) |
| `gift_card_pay` | `gift_card_amount` | 赠送卡支付 |
| `groupbuy_pay` | `coupon_amount` | 团购券支付 |
@@ -1046,12 +1061,12 @@ dwd_member_balance_change ────┘
|----------|------|------|
| 标识 | `site_id`, `tenant_id`, `stat_date` | 门店、统计日期 |
| 发生额 | `gross_amount`, `table_fee_amount`, `goods_amount`, `assistant_pd_amount`, `assistant_cx_amount` | 正价总额及按类型拆分(台费/商品/专业课/陪练课) |
| 优惠 | `discount_total`, `discount_groupbuy`, `discount_vip`, `discount_gift_card`, `discount_manual`, `discount_rounding`, `discount_other` | 优惠合计及按类型拆分 |
| 优惠 | `discount_total`, `discount_groupbuy`, `discount_vip`, `discount_gift_card`, `discount_manual`, `discount_rounding`, `discount_other` | 优惠合计及按类型拆分discount_manual=大客户优惠discount_other=其他手动调整,两者互斥) |
| 确认收入 | `confirmed_income` | 发生额 - 优惠合计 |
| 现金流入 | `cash_inflow_total`, `cash_pay_amount`, `groupbuy_pay_amount`, `platform_settlement_amount`, `recharge_cash_inflow` | 现金流入合计及来源拆分 |
| 现金流出 | `cash_outflow_total`, `platform_fee_amount` | 现金流出合计(支出 + 平台费用) |
| 现金净变动 | `cash_balance_change` | 流入 - 流出 |
| 卡消费 | `card_consume_total`, `cash_card_consume`, `gift_card_consume` | 值卡消费 + 赠送卡消费 |
| 卡消费 | `card_consume_total`, `recharge_card_consume`, `gift_card_consume` | 现金充值卡消费= `recharge_card_amount`+ 赠送卡消费 |
| 充值统计 | `recharge_count`, `recharge_total`, `recharge_cash`, `recharge_gift`, `first_recharge_count`, `first_recharge_amount`, `renewal_count`, `renewal_amount` | 充值笔数/金额、首充/续充拆分 |
| 订单统计 | `order_count`, `member_order_count`, `guest_order_count`, `avg_order_amount` | 总订单数、会员/散客订单数、客单价 |
@@ -1063,7 +1078,9 @@ dwd_member_balance_change ────┘
gross_amount = table_charge_money + goods_money + assistant_pd_money + assistant_cx_money
```
`dwd_settlement_head``DATE(pay_time)` 聚合分别统计台费、商品、专业课PD、陪练课CX四类收入
> 注意:`gross_amount` 为发生额(正价四项),不含 `electricity_money`。完整消费项目合计(`items_sum`)还需加上 `electricity_money`
`dwd_settlement_head``biz_date(pay_time)` 聚合,通过 `settle_type IN (1, 3)` 仅保留台桌结账和商城订单(排除退货/退款分别统计台费、商品、陪打PD、超休CX四类收入。
**2. 团购优惠计算**
@@ -1096,10 +1113,12 @@ discount_other = adjust_amount - big_customer_amount (负值置 0
**5. 优惠合计与确认收入**
```
discount_total = discount_groupbuy + discount_vip + discount_gift_card + discount_manual + discount_rounding
discount_total = discount_groupbuy + discount_vip + discount_gift_card + discount_manual + discount_rounding + discount_other
confirmed_income = gross_amount - discount_total
```
> `discount_manual` = 大客户优惠,`discount_other` = 其他手动调整,两者互斥,之和 = adjust_amount。
**6. 现金流计算**
```
@@ -1123,11 +1142,13 @@ daily_expense = expense_amount / days_in_month
**8. 卡消费统计**
```
cash_card_consume = recharge_card_amount + balance_amount 值卡支付)
recharge_card_consume = recharge_card_amount 现金充值卡支付部分
gift_card_consume = 赠送卡消费总额 (来自余额变动)
card_consume_total = cash_card_consume + gift_card_consume
card_consume_total = recharge_card_consume + gift_card_consume
```
> 注意:`balance_amount = recharge_card_amount + gift_card_amount`(恒等式),因此 `recharge_card_consume` 只取 `recharge_card_amount`,不可再加 `balance_amount`,否则重复计算。
---
### DWS_FINANCE_RECHARGE — 充值统计
@@ -1170,7 +1191,7 @@ card_consume_total = cash_card_consume + gift_card_consume
每笔充值金额拆分为:
```
充值总额 = pay_money(现金部分)+ gift_money(赠送部分)
充值总额 = pay_amount(现金部分)+ point_amount(赠送部分)
```
**2. 会员去重统计**
@@ -1236,14 +1257,14 @@ total_card_balance = cash_card_balance + gift_card_balance
**维度 1按收入类型`structure_type = 'INCOME_TYPE'`**
`dwd_settlement_head``pay_time::DATE` 聚合,仅统计已结账订单(`settle_status = 1`),每日展开为 4 条记录:
`dwd_settlement_head``pay_time::DATE` 聚合,仅统计已结账订单(`settle_type IN (1, 3)`),每日展开为 4 条记录:
| category_code | category_name | 来源字段 | 说明 |
|---------------|---------------|----------|------|
| `TABLE_FEE` | 台费收入 | `table_charge_money` | 台桌使用费 |
| `GOODS` | 商品收入 | `goods_money` | 商品销售 |
| `ASSISTANT_BASE` | 助教基础课 | `assistant_pd_money` | 专业课PD=陪打) |
| `ASSISTANT_BONUS` | 助教附加课 | `assistant_cx_money` | 附加课CX=超休/促销) |
| `ASSISTANT_PD` | 助教陪打 | `assistant_pd_money` | 陪打收入 |
| `ASSISTANT_CX` | 助教超休 | `assistant_cx_money` | 超休收入 |
占比计算:`income_ratio = 该类型金额 / 当日四类收入总和`
@@ -1327,7 +1348,7 @@ total_card_balance = cash_card_balance + gift_card_balance
团购优惠 = coupon_amount - 团购实付
```
仅统计 `coupon_amount > 0` 的已结账订单(`settle_status = 1`)。
仅统计 `coupon_amount > 0` 的已结账订单(`settle_type IN (1, 3)`)。
**2. 赠送卡消费拆分**
@@ -1406,10 +1427,10 @@ dws_*(所有 DWS 汇总表)──────► DWS_MAINTENANCE统一维
| 商品 | `item_count`, `total_item_quantity` | 商品种类数、商品总数量 |
| 费用明细 | `table_fee_amount`, `assistant_service_amount`, `goods_amount`, `group_amount` | 台费、助教费、商品金额、团购金额 |
| 优惠 | `total_coupon_deduction`, `member_discount_amount`, `manual_discount_amount` | 团购抵扣、会员折扣、手动调整 |
| 金额汇总 | `order_original_amount`, `order_final_amount` | 订单原价、实付金额 |
| 支付方式 | `stored_card_deduct`, `external_paid_amount`, `total_paid_amount` | 储值卡抵扣、外部支付、总支付 |
| 金额汇总 | `order_original_amount`, `order_final_amount` | 订单原价= `total_paid_amount + total_coupon_deduction + member_discount_amount + manual_discount_amount`、实付金额 |
| 支付方式 | `stored_card_deduct`, `external_paid_amount`, `total_paid_amount` | 储值卡抵扣= `balance_amount`、外部支付、总支付 |
| 台账流水 | `book_table_flow`, `book_assistant_flow`, `book_goods_flow`, `book_group_flow`, `book_order_flow` | 台费/助教/商品/团购/订单台账流水 |
| 有效消费 | `order_effective_consume_cash`, `order_effective_recharge_cash`, `order_effective_flow` | 有效消费现金、有效充值现金、有效流水 |
| 有效消费 | `order_effective_consume_cash`, `order_effective_recharge_cash`, `order_effective_flow` | 有效消费现金、有效充值现金(当前硬编码为 0占位、有效流水 |
| 退款 | `refund_amount`, `net_income` | 退款金额、净收入 |
#### 核心业务逻辑
@@ -1463,7 +1484,7 @@ net_income = total_paid_amount - refund_amount
recharge_order_flag = (consume_money = 0 AND pay_amount > 0)
```
消费金额为 0 但有支付金额的订单标记为充值订单。
消费金额为 0 但有支付金额的订单标记为充值订单。此处 `consume_money` 仅用于零值判断(三种口径在 =0 时等价),不涉及金额聚合。
#### 配置参数
@@ -1638,3 +1659,116 @@ dwd_goods_stock_summary ──┬──► DWS_GOODS_STOCK_DAILY日度汇总
- `range_start_stock` 取该月第一条记录的值(期初快照)
- `range_end_stock` / `current_stock` 取该月最后一条记录的值(期末快照)
- `stat_period = 'monthly'`
---
## 项目标签域
项目标签域包含 2 个任务按时间窗口计算助教和客户在四大项目类型BILLIARD/SNOOKER/MAHJONG/KTV的时长占比占比≥25% 则分配标签。数据流向为:
```
dwd_assistant_service_log (income_seconds) ──┐
├──► dim_table (site_table_id JOIN)
dwd_table_fee_log (ledger_count) ────────────┘ │
cfg_area_category (get_area_category)
┌──────────────────┴──────────────────┐
▼ ▼
DWS_ASSISTANT_PROJECT_TAG DWS_MEMBER_PROJECT_TAG
助教项目标签6 个时间窗口) 客户项目标签2 个时间窗口)
```
### 公共逻辑
1. 数据链路走 `dim_table`(通过 `site_table_id` JOIN`scd2_is_current=1`),获取 `area_name``table_name`
2. 通过 `get_area_category(area_name, table_name)` 映射到 `category_code`
3. 只保留四大项目BILLIARD/SNOOKER/MAHJONG/KTV排除 SPECIAL/OTHER
4. 标签阈值:`TAG_THRESHOLD = 0.25`25%
5. 更新策略:全量删除重建(按 `site_id` 删除后重新插入所有时间窗口)
---
### DWS_ASSISTANT_PROJECT_TAG — 助教项目标签
| 属性 | 值 |
|------|-----|
| 任务代码 | `DWS_ASSISTANT_PROJECT_TAG` |
| Python 类 | `AssistantProjectTagTask``tasks/dws/assistant_project_tag_task.py` |
| 目标表 | `dws.dws_assistant_project_tag` |
| 主键 | `site_id`, `assistant_id`, `time_window`, `category_code` |
| 粒度 | 助教 + 时间窗口 + 项目类型 |
| 更新策略 | 全量删除重建(按 site_id |
| 更新频率 | 每日更新 |
| 依赖 | `DWD_LOAD_FROM_ODS` |
#### 数据来源
| 来源表 | Schema | 用途 |
|--------|--------|------|
| `dwd_assistant_service_log` | `dwd` | 助教服务流水(`income_seconds` 工作时长) |
| `dim_table` | `dwd` | 台桌维度SCD2 当前版本,`area_name` + `table_name` |
| `cfg_area_category` | `dws` | 区域分类映射(通过 ConfigCache 加载) |
#### 时间窗口
| 枚举值 | 说明 |
|--------|------|
| `THIS_MONTH` | 本月(月初 ~ 今天) |
| `THIS_QUARTER` | 本季度季度首月1日 ~ 今天) |
| `LAST_MONTH` | 上月(上月初 ~ 上月末) |
| `LAST_3_MONTHS_EXCL_CURRENT` | 前3个月不含本月 |
| `LAST_QUARTER` | 上季度 |
| `LAST_6_MONTHS` | 最近半年(不含本月) |
#### 核心业务逻辑
1.`dwd_assistant_service_log``(site_assistant_id, site_table_id)` 聚合 `income_seconds`
2. 通过 `dim_table` JOIN 获取台桌的 `area_name``table_name`
3. 调用 `get_area_category(area_name, table_name)` 映射到 `category_code`
4.`(assistant_id, category_code)` 汇总各项目时长
5. 计算占比:`percentage = duration_seconds / total_seconds`(四位小数)
6. 占比 ≥ 0.25 标记 `is_tagged = TRUE`
7. 过滤条件:`is_delete = 0`,营业日切点通过 `biz_date_sql_expr` 处理
---
### DWS_MEMBER_PROJECT_TAG — 客户项目标签
| 属性 | 值 |
|------|-----|
| 任务代码 | `DWS_MEMBER_PROJECT_TAG` |
| Python 类 | `MemberProjectTagTask``tasks/dws/member_project_tag_task.py` |
| 目标表 | `dws.dws_member_project_tag` |
| 主键 | `site_id`, `member_id`, `time_window`, `category_code` |
| 粒度 | 会员 + 时间窗口 + 项目类型 |
| 更新策略 | 全量删除重建(按 site_id |
| 更新频率 | 每日更新 |
| 依赖 | `DWD_LOAD_FROM_ODS` |
#### 数据来源
| 来源表 | Schema | 用途 |
|--------|--------|------|
| `dwd_table_fee_log` | `dwd` | 台费流水(`ledger_count` 计费时长) |
| `dim_table` | `dwd` | 台桌维度SCD2 当前版本,`area_name` + `table_name` |
| `cfg_area_category` | `dws` | 区域分类映射(通过 ConfigCache 加载) |
#### 时间窗口
| 枚举值 | 说明 |
|--------|------|
| `LAST_30_DAYS` | 近30天含今天base_date-29天 ~ base_date |
| `LAST_60_DAYS` | 近60天含今天base_date-59天 ~ base_date |
#### 核心业务逻辑
1.`dwd_table_fee_log``(member_id, site_table_id)` 聚合 `ledger_count`
2. 散客排除:`member_id IS NOT NULL AND member_id != 0`
3. 通过 `dim_table` JOIN 获取台桌的 `area_name``table_name`
4. 调用 `get_area_category(area_name, table_name)` 映射到 `category_code`
5.`(member_id, category_code)` 汇总各项目时长
6. 计算占比:`percentage = duration_seconds / total_seconds`(四位小数)
7. 占比 ≥ 0.25 标记 `is_tagged = TRUE`
8. 过滤条件:`COALESCE(is_delete, 0) = 0`,营业日切点通过 `biz_date_sql_expr` 处理

View File

@@ -78,7 +78,6 @@ API 返回的 JSON 响应通过两级路径定位数据:先按 `data_path`
| `ODS_SETTLEMENT_RECORDS` | ✅ | `(rangeStartTime, rangeEndTime)` | ❌ | ✅ | ✅ | `NONE` | — |
| `ODS_TABLE_USE` | ❌ | 默认 | ❌ | ✅ | ✅ | `WINDOW` | `create_time` |
| `ODS_ASSISTANT_LEDGER` | ✅ | 默认 | ❌ | ✅ | ✅ | `WINDOW` | `create_time` |
| `ODS_ASSISTANT_ABOLISH` | ✅ | 默认 | ❌ | ✅ | ✅ | `NONE` | — |
| `ODS_STORE_GOODS_SALES` | ❌ | 默认 | ❌ | ✅ | ✅ | `WINDOW` | `create_time` |
| `ODS_PAYMENT` | ❌ | 默认 | ❌ | ✅ | ✅ | `NONE` | — |
| `ODS_REFUND` | ❌ | 默认 | ❌ | ✅ | ✅ | `WINDOW` | `pay_time` |
@@ -88,6 +87,8 @@ API 返回的 JSON 响应通过两级路径定位数据:先按 `data_path`
| `ODS_MEMBER_BALANCE` | ❌ | 默认 | ❌ | ✅ | ✅ | `WINDOW` | `create_time` |
| `ODS_RECHARGE_SETTLE` | ✅ | `(rangeStartTime, rangeEndTime)` | ✅ | ❌ | ✅ | `NONE` | — |
| `ODS_GROUP_PACKAGE` | ❌ | 默认 | ❌ | ✅ | ✅ | `FULL_TABLE` | — |
> `ODS_GROUP_PACKAGE` 额外配置了 `detail_endpoint`,在主流程完成后串行调用 `QueryPackageCouponInfo` 获取每个团购的详情数据,写入 `ods.group_buy_package_details`。
| `ODS_GROUP_BUY_REDEMPTION` | ❌ | 默认 | ❌ | ✅ | ✅ | `WINDOW` | `create_time` |
| `ODS_INVENTORY_STOCK` | ❌ | 默认 | ❌ | ✅ | ✅ | `NONE` | — |
| `ODS_INVENTORY_CHANGE` | ✅ | 默认 | ❌ | ✅ | ✅ | `NONE` | — |

View File

@@ -7,7 +7,7 @@
## 概述
ODS 层采用**声明式配置**驱动的通用任务模式:由 `BaseOdsTask` + `OdsTaskSpec` 配置驱动,通过 `ODS_TASK_CLASSES` 字典动态注册,共 23 个任务。
ODS 层采用**声明式配置**驱动的通用任务模式:由 `BaseOdsTask` + `OdsTaskSpec` 配置驱动,通过 `ODS_TASK_CLASSES` 字典动态注册,共 22 个任务。
所有 ODS 任务写入 `ods.*` 表,原始 API 响应以 JSON 格式存入 `payload` 列,元数据列(`fetched_at``source_file``content_hash` 等)自动填充。
@@ -22,7 +22,6 @@ ODS 层采用**声明式配置**驱动的通用任务模式:由 `BaseOdsTask`
| `ODS_SETTLEMENT_RECORDS` | `OdsOrderSettleTask` | `/Site/GetAllOrderSettleList` | `settlement_records` | 结账记录 |
| `ODS_TABLE_USE` | `OdsTableUseTask` | `/Site/GetSiteTableOrderDetails` | `table_fee_transactions` | 台费计费流水 |
| `ODS_ASSISTANT_LEDGER` | `OdsAssistantLedgerTask` | `/AssistantPerformance/GetOrderAssistantDetails` | `assistant_service_records` | 助教服务流水 |
| `ODS_ASSISTANT_ABOLISH` | `OdsAssistantAbolishTask` | `/AssistantPerformance/GetAbolitionAssistant` | `assistant_cancellation_records` | 助教废除记录 |
| `ODS_STORE_GOODS_SALES` | `OdsGoodsLedgerTask` | `/TenantGoods/GetGoodsSalesList` | `store_goods_sales_records` | 门店商品销售流水 |
| `ODS_PAYMENT` | `OdsPaymentTask` | `/PayLog/GetPayLogListPage` | `payment_transactions` | 支付流水 |
| `ODS_REFUND` | `OdsRefundTask` | `/Order/GetRefundPayLogList` | `refund_transactions` | 退款流水 |
@@ -31,7 +30,7 @@ ODS 层采用**声明式配置**驱动的通用任务模式:由 `BaseOdsTask`
| `ODS_MEMBER_CARD` | `OdsMemberCardTask` | `/MemberProfile/GetTenantMemberCardList` | `member_stored_value_cards` | 会员储值卡 |
| `ODS_MEMBER_BALANCE` | `OdsMemberBalanceTask` | `/MemberProfile/GetMemberCardBalanceChange` | `member_balance_changes` | 会员余额变动 |
| `ODS_RECHARGE_SETTLE` | `OdsRechargeSettleTask` | `/Site/GetRechargeSettleList` | `recharge_settlements` | 充值结算 |
| `ODS_GROUP_PACKAGE` | `OdsPackageTask` | `/PackageCoupon/QueryPackageCouponList` | `group_buy_packages` | 团购套餐定义 |
| `ODS_GROUP_PACKAGE` | `OdsPackageTask` | `/PackageCoupon/QueryPackageCouponList` | `group_buy_packages` | 团购套餐定义(含详情子流程,见下方说明) |
| `ODS_GROUP_BUY_REDEMPTION` | `OdsGroupBuyRedemptionTask` | `/Site/GetSiteTableUseDetails` | `group_buy_redemption_records` | 团购套餐核销 |
| `ODS_INVENTORY_STOCK` | `OdsInventoryStockTask` | `/TenantGoods/GetGoodsStockReport` | `goods_stock_summary` | 库存汇总 |
| `ODS_INVENTORY_CHANGE` | `OdsInventoryChangeTask` | `/GoodsStockManage/QueryGoodsOutboundReceipt` | `goods_stock_movements` | 库存变化记录 |
@@ -44,6 +43,26 @@ ODS 层采用**声明式配置**驱动的通用任务模式:由 `BaseOdsTask`
> 所有目标表均位于 `ods` schema 下。
### ODS_GROUP_PACKAGE 详情子流程
`ODS_GROUP_PACKAGE` 任务通过 `detail_endpoint` 配置启用了二级详情拉取:
| 配置项 | 值 |
|--------|-----|
| `detail_endpoint` | `/PackageCoupon/QueryPackageCouponInfo` |
| `detail_target_table` | `ods.group_buy_package_details` |
| `detail_param_builder` | `lambda rec: {"couponId": rec["id"]}` |
| `detail_data_path` | `("data",)` |
| `detail_id_column` | `id` |
执行流程:
1. 主流程从 `QueryPackageCouponList` 拉取团购列表 → 写入 `ods.group_buy_packages`
2. 子流程从 `ods.group_buy_packages` 提取所有 `id`
3. 串行调用 `QueryPackageCouponInfo`(通过 `UnifiedPipeline` + `RateLimiter`),获取每个团购的详情
4. 详情数据经字段提取后写入 `ods.group_buy_package_details`全量快照UPSERT on `coupon_id`
详情表字段映射见 `docs/database/ODS/mappings/mapping_QueryPackageCouponInfo_group_buy_package_details.md`
---
## 通用 ODS 任务架构BaseOdsTask + OdsTaskSpec 模式)
@@ -228,7 +247,7 @@ execute(cursor_data)
### content_hash 去重机制
`content_hash` 是通用 ODS 任务的核心去重手段,所有 23 个任务默认开启(`skip_unchanged=True`)。
`content_hash` 是通用 ODS 任务的核心去重手段,所有 22 个任务默认开启(`skip_unchanged=True`)。
#### 计算方式
@@ -277,8 +296,7 @@ ORDER BY id, fetched_at DESC;
| `ODS_SETTLEMENT_RECORDS` | 是 | `NONE` | — | 结账记录,按时间窗口增量抓取 |
| `ODS_TABLE_USE` | 否 | `WINDOW` | `create_time` | 台费计费流水 |
| `ODS_ASSISTANT_LEDGER` | 是 | `WINDOW` | `create_time` | 助教服务流水 |
| `ODS_ASSISTANT_ABOLISH` | 是 | `NONE` | — | 助教废除记录 |
| `ODS_STORE_GOODS_SALES` | 否 | `WINDOW` | `create_time` | 门店商品销售流水 |
| `ODS_STORE_GOODS_SALES` | 是 | `WINDOW` | `create_time` | 门店商品销售流水2026-03-01 修复:`requires_window``False` 改为 `True`,新增 `time_fields=("startTime", "endTime")` |
| `ODS_PAYMENT` | 否 | `NONE` | — | 支付流水 |
| `ODS_REFUND` | 否 | `WINDOW` | `pay_time` | 退款流水 |
| `ODS_PLATFORM_COUPON` | 否 | `WINDOW` | `consume_time` | 平台/团购券核销 |
@@ -286,7 +304,7 @@ ORDER BY id, fetched_at DESC;
| `ODS_MEMBER_CARD` | 否 | `FULL_TABLE` | — | 会员储值卡 |
| `ODS_MEMBER_BALANCE` | 否 | `WINDOW` | `create_time` | 会员余额变动 |
| `ODS_RECHARGE_SETTLE` | 是 | `NONE` | — | 充值结算 |
| `ODS_GROUP_PACKAGE` | 否 | `FULL_TABLE` | — | 团购套餐定义 |
| `ODS_GROUP_PACKAGE` | 否 | `FULL_TABLE` | — | 团购套餐定义 + 详情子流程(`detail_endpoint` |
| `ODS_GROUP_BUY_REDEMPTION` | 否 | `WINDOW` | `create_time` | 团购套餐核销 |
| `ODS_INVENTORY_STOCK` | 否 | `NONE` | — | 库存汇总 |
| `ODS_INVENTORY_CHANGE` | 是 | `NONE` | — | 库存变化记录 |
@@ -297,4 +315,4 @@ ORDER BY id, fetched_at DESC;
| `ODS_TENANT_GOODS` | 否 | `FULL_TABLE` | — | 租户商品档案 |
| `ODS_STAFF_INFO` | 否 | `FULL_TABLE` | — | 员工档案,全量快照 |
> 所有 23 个任务默认 `skip_unchanged=True`(去重开启)。
> 所有 22 个任务默认 `skip_unchanged=True`(去重开启)。

View File

@@ -283,7 +283,6 @@ execute()
| `member_stored_value_cards` | `ods.member_stored_value_cards` |
| `recharge_settlements` | `ods.recharge_settlements` |
| `settlement_records` | `ods.settlement_records` |
| `assistant_cancellation_records` | `ods.assistant_cancellation_records` |
| `assistant_accounts_master` | `ods.assistant_accounts_master` |
| `assistant_service_records` | `ods.assistant_service_records` |
| `site_tables_master` | `ods.site_tables_master` |