微信小程序页面迁移校验之前 P5任务处理之前
This commit is contained in:
@@ -12,7 +12,7 @@
|
||||
### 1.1 助教日报(dws_assistant_daily_detail)
|
||||
|
||||
- 目标表:`dws.dws_assistant_daily_detail`
|
||||
- 数据来源:`dwd_assistant_service_log`、`dwd_assistant_trash_event`、`dim_assistant`(SCD2)
|
||||
- 数据来源:`dwd_assistant_service_log`、`dwd_assistant_service_log_ex`(提供 `is_trash` 标记)、`dim_assistant`(SCD2)
|
||||
- 粒度:门店 × 助教 × 日期
|
||||
- 核心指标:服务次数(总/基础课/附加课/包厢课)、计费秒数与小时数、台账金额、去重客户数与台桌数、废除统计
|
||||
- 课程类型分类:通过 `cfg_skill_type` 映射 `skill_id` → `BASE`/`BONUS`/`ROOM`
|
||||
|
||||
@@ -42,6 +42,12 @@
|
||||
| 23 | scd2_end_time | TIMESTAMPTZ | YES | | SCD2 版本失效时间 |
|
||||
| 24 | scd2_is_current | INTEGER | YES | | 当前版本标记 |
|
||||
| 25 | scd2_version | INTEGER | YES | | 版本号 |
|
||||
| 26 | table_area_ids | JSONB | YES | | 可用台区 ID 列表(来自详情接口 tableAreaId) |
|
||||
| 27 | table_area_names | JSONB | YES | | 可用台区名称列表(来自详情接口 tableAreaNameList) |
|
||||
| 28 | assistant_services | JSONB | YES | | 助教服务关联数组(来自详情接口 packageCouponAssistants) |
|
||||
| 29 | groupon_site_infos | JSONB | YES | | 关联门店信息数组(来自详情接口 grouponSiteInfos) |
|
||||
|
||||
> 字段 26-29 由迁移脚本 `db/etl_feiqiu/migrations/2026-03-05__add_detail_fields_to_dim_groupbuy_package_ex.sql` 新增,数据来源为 `ods.group_buy_package_details`(通过 LEFT JOIN `coupon_id = groupbuy_package_id` 合并)。
|
||||
|
||||
## 样本数据
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
| 14 | is_confirm | INTEGER | YES | | 是否确认。**枚举值**: 2(5003)=**[待确认]** |
|
||||
| 15 | is_single_order | INTEGER | YES | | 是否独立订单。**枚举值**: 1(5003)=是 |
|
||||
| 16 | is_not_responding | INTEGER | YES | | 无响应。**枚举值**: 0(5003)=正常 |
|
||||
| 17 | is_trash | INTEGER | YES | | 是否废单。**枚举值**: 0(5003)=正常 |
|
||||
| 17 | is_trash | INTEGER | YES | | 是否废单。**枚举值**: 0=正常, 1=已作废。⚠️ 此字段是判断助教服务是否作废的唯一依据,替代已废弃的 `dwd_assistant_trash_event` 表(2026-02-22 DROP)。DWS 层助教日报等任务通过此字段过滤废单统计。 |
|
||||
| 18 | trash_applicant_id | BIGINT | YES | | 废单申请人 ID(当前数据全为 0) |
|
||||
| 19 | trash_applicant_name | VARCHAR(64) | YES | | 废单申请人姓名(当前数据全为空) |
|
||||
| 20 | trash_reason | VARCHAR(255) | YES | | 废单原因(当前数据全为空) |
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
| 序号 | 表名 | 说明 | 主键 | 扩展表 | 文档链接 |
|
||||
|------|------|------|------|--------|----------|
|
||||
| 1 | dwd_assistant_service_log | 助教服务流水 | assistant_service_id | dwd_assistant_service_log_ex | [主表](BD_manual_dwd_assistant_service_log.md) / [扩展表](BD_manual_dwd_assistant_service_log_ex.md) |
|
||||
| 2 | dwd_assistant_trash_event | 助教服务作废 | assistant_trash_event_id | dwd_assistant_trash_event_ex | [主表](BD_manual_dwd_assistant_trash_event.md) / [扩展表](BD_manual_dwd_assistant_trash_event_ex.md) |
|
||||
| 2 | ~~dwd_assistant_trash_event~~ | ~~助教服务作废~~ | — | — | ⚠️ 已于 2026-02-22 废弃,作废判断改用 `dwd_assistant_service_log_ex.is_trash` |
|
||||
| 3 | dwd_groupbuy_redemption | 团购券核销 | redemption_id | dwd_groupbuy_redemption_ex | [主表](BD_manual_dwd_groupbuy_redemption.md) / [扩展表](BD_manual_dwd_groupbuy_redemption_ex.md) |
|
||||
| 4 | dwd_member_balance_change | 会员余额变动 | balance_change_id | dwd_member_balance_change_ex | [主表](BD_manual_dwd_member_balance_change.md) / [扩展表](BD_manual_dwd_member_balance_change_ex.md) |
|
||||
| 5 | dwd_payment | 支付流水 | payment_id | 无 | [主表](BD_manual_dwd_payment.md) |
|
||||
@@ -118,7 +118,7 @@ SELECT * FROM dwd.dwd_payment ORDER BY pay_time DESC NULLS LAST LIMIT 1;
|
||||
| dwd_table_fee_adjust | 2,849 |
|
||||
| dwd_assistant_service_log | 1,090 |
|
||||
| dwd_recharge_order | 455 |
|
||||
| dwd_assistant_trash_event | 98 |
|
||||
| ~~dwd_assistant_trash_event~~ | ~~98~~ | ⚠️ 已废弃(2026-02-22) |
|
||||
| dwd_refund | 45 |
|
||||
|
||||
---
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
| M7 | 2 | 麻将/麻将棋牌 |
|
||||
| M8 | 1 | 麻将/麻将棋牌 |
|
||||
| K包 | 4 | K包/K歌/KTV |
|
||||
| VIP包厢 | 4 | 台球/打球/中八/追分 (V5为 台球/打球/斯诺克) |
|
||||
| VIP包厢 | 4 | 🎱 中式/追分 (V1-V4)、斯诺克 (V5) |
|
||||
| 斯诺克区 | 4 | 台球/打球/斯诺克 |
|
||||
| 666 | 2 | 麻将/麻将棋牌 |
|
||||
| TV台 | 1 | 台球/打球/中八/追分 |
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
| 16 | member_card_type_name | VARCHAR(100) | YES | | 卡类型名称(当前数据全为空) |
|
||||
| 17 | is_bind_member | BOOLEAN | YES | | 是否绑定会员。**枚举值**: False=否 |
|
||||
| 18 | member_discount_amount | NUMERIC(18,2) | YES | | 会员折扣金额 |
|
||||
| 19 | consume_money | NUMERIC(18,2) | YES | | 消费总金额(元) |
|
||||
| 19 | consume_money | NUMERIC(18,2) | YES | | 消费总金额(元)。⚠️ **口径不稳定**:存在三种历史口径(A/B/C),DWS 层不应直接使用,应使用 `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) |
|
||||
| 20 | table_charge_money | NUMERIC(18,2) | YES | | 台费金额 |
|
||||
| 21 | goods_money | NUMERIC(18,2) | YES | | 商品金额 |
|
||||
| 22 | real_goods_money | NUMERIC(18,2) | YES | | 实收商品金额 |
|
||||
@@ -71,19 +71,30 @@ LIMIT 1;
|
||||
```
|
||||
**使用示例**
|
||||
```sql
|
||||
-- 每日营收统计
|
||||
-- 每日营收统计(使用 items_sum 口径,不使用 consume_money)
|
||||
SELECT
|
||||
DATE(pay_time) AS pay_date,
|
||||
COUNT(*) AS order_count,
|
||||
SUM(consume_money) AS total_consume,
|
||||
SUM(table_charge_money + goods_money + assistant_pd_money
|
||||
+ assistant_cx_money + electricity_money) AS total_items_sum,
|
||||
SUM(pay_amount) AS total_pay
|
||||
FROM dwd.dwd_settlement_head
|
||||
WHERE settle_type IN (1, 3)
|
||||
GROUP BY DATE(pay_time)
|
||||
ORDER BY pay_date DESC;
|
||||
-- 台费 vs 商品 vs 助教收入
|
||||
SELECT
|
||||
SUM(table_charge_money) AS table_revenue,
|
||||
SUM(goods_money) AS goods_revenue,
|
||||
SUM(assistant_pd_money + assistant_cx_money) AS assistant_revenue
|
||||
FROM dwd.dwd_settlement_head;
|
||||
SUM(assistant_pd_money) AS assistant_pd_revenue,
|
||||
SUM(assistant_cx_money) AS assistant_cx_revenue
|
||||
FROM dwd.dwd_settlement_head
|
||||
WHERE settle_type IN (1, 3);
|
||||
```
|
||||
|
||||
**支付渠道恒等式(100% 成立)**
|
||||
```
|
||||
balance_amount = recharge_card_amount + gift_card_amount -- 储值卡 = 充值卡 + 礼品卡
|
||||
pay_amount = point_amount + cash_amount -- 实付 = 积分 + 现金(互斥)
|
||||
```
|
||||
> `balance_amount` 是独立支付渠道,`recharge_card_amount`/`gift_card_amount` 是其分账明细,不可重复计算。
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# cfg_area_category 台区分类映射表
|
||||
|
||||
> 生成时间:2026-02-03
|
||||
> 生成时间:2026-02-03 | 更新时间:2026-03-07
|
||||
|
||||
## 表信息
|
||||
|
||||
@@ -9,8 +9,9 @@
|
||||
| Schema | dws |
|
||||
| 表名 | cfg_area_category |
|
||||
| 主键 | category_id |
|
||||
| 唯一约束 | (source_area_name, COALESCE(source_table_name, '')) |
|
||||
| 数据来源 | 手工维护/seed脚本(基于dim_table实际数据) |
|
||||
| 说明 | 将dim_table.site_table_area_name映射到财务报表区域分类 |
|
||||
| 说明 | 将dim_table的台区/台桌映射到项目分类,支持台桌级细分 |
|
||||
|
||||
## 字段说明
|
||||
|
||||
@@ -18,57 +19,47 @@
|
||||
|------|--------|------|------|------|------|
|
||||
| 1 | category_id | SERIAL | NO | PK | 分类ID(自增) |
|
||||
| 2 | source_area_name | VARCHAR(100) | NO | UK | 源区域名称(来自dim_table.site_table_area_name) |
|
||||
| 3 | category_code | VARCHAR(20) | NO | | 分类代码。**枚举值**: BILLIARD, BILLIARD_VIP, SNOOKER, MAHJONG, KTV, SPECIAL, OTHER |
|
||||
| 4 | category_name | VARCHAR(50) | NO | | 分类名称 |
|
||||
| 5 | match_type | VARCHAR(10) | NO | | 匹配类型。**枚举值**: EXACT(精确), LIKE(模糊), DEFAULT(兜底) |
|
||||
| 6 | match_priority | INTEGER | NO | | 匹配优先级(数字越小优先级越高) |
|
||||
| 7 | is_active | BOOLEAN | NO | | 是否启用 |
|
||||
| 8 | description | TEXT | YES | | 说明 |
|
||||
| 9 | created_at | TIMESTAMPTZ | NO | | 创建时间 |
|
||||
| 10 | updated_at | TIMESTAMPTZ | NO | | 更新时间 |
|
||||
| 3 | source_table_name | VARCHAR(100) | YES | UK | 源台桌名称(来自dim_table.table_name),NULL表示区域级映射 |
|
||||
| 4 | category_code | VARCHAR(20) | NO | | 分类代码。**枚举值**: BILLIARD, SNOOKER, MAHJONG, KTV, SPECIAL, OTHER |
|
||||
| 5 | category_name | VARCHAR(50) | NO | | 分类名称(含emoji) |
|
||||
| 6 | display_name | VARCHAR(50) | YES | | 显示名称(用于筛选器) |
|
||||
| 7 | short_name | VARCHAR(20) | YES | | 简写(用于列表标签) |
|
||||
| 8 | match_type | VARCHAR(10) | NO | | 匹配类型。**枚举值**: EXACT(精确), LIKE(模糊), DEFAULT(兜底) |
|
||||
| 9 | match_priority | INTEGER | NO | | 匹配优先级(数字越小优先级越高) |
|
||||
| 10 | is_active | BOOLEAN | NO | | 是否启用 |
|
||||
| 11 | description | TEXT | YES | | 说明 |
|
||||
| 12 | created_at | TIMESTAMPTZ | NO | | 创建时间 |
|
||||
| 13 | updated_at | TIMESTAMPTZ | NO | | 更新时间 |
|
||||
|
||||
## 分类映射示例
|
||||
## 变更说明(2026-03-07)
|
||||
|
||||
| 源区域名称 | 分类代码 | 分类名称 |
|
||||
|------------|----------|----------|
|
||||
| A区 | BILLIARD | 台球散台 |
|
||||
| B区 | BILLIARD | 台球散台 |
|
||||
| C区 | BILLIARD | 台球散台 |
|
||||
| TV台 | BILLIARD | 台球散台 |
|
||||
| VIP包厢 | BILLIARD_VIP | 台球VIP |
|
||||
| 斯诺克区 | SNOOKER | 斯诺克 |
|
||||
| 麻将房 | MAHJONG | 麻将棋牌 |
|
||||
| M7 | MAHJONG | 麻将棋牌 |
|
||||
| M8 | MAHJONG | 麻将棋牌 |
|
||||
| 666 | MAHJONG | 麻将棋牌 |
|
||||
| 发财 | MAHJONG | 麻将棋牌 |
|
||||
| K包 | KTV | K歌娱乐 |
|
||||
| k包活动区 | KTV | K歌娱乐 |
|
||||
| 幸会158 | KTV | K歌娱乐 |
|
||||
| 补时长 | SPECIAL | 补时长 |
|
||||
### 新增字段
|
||||
- `source_table_name`:支持台桌级细分映射(如 VIP包厢 V5 → SNOOKER)
|
||||
- `display_name`:前端筛选器显示名称
|
||||
- `short_name`:列表中的简写标签
|
||||
|
||||
## 使用说明
|
||||
### 删除类型
|
||||
- `BILLIARD_VIP` 已废弃,VIP包厢 V1-V4 归入 `BILLIARD`,V5 归入 `SNOOKER`
|
||||
|
||||
**取值方式**
|
||||
### 唯一约束变更
|
||||
- 从 `(source_area_name)` 改为 `(source_area_name, COALESCE(source_table_name, ''))`
|
||||
|
||||
```sql
|
||||
-- 将台区名称映射到分类
|
||||
SELECT
|
||||
dt.site_table_area_name,
|
||||
COALESCE(ac.category_code, 'OTHER') AS category_code,
|
||||
COALESCE(ac.category_name, '其他') AS category_name
|
||||
FROM dwd.dim_table dt
|
||||
LEFT JOIN dws.cfg_area_category ac
|
||||
ON dt.site_table_area_name = ac.source_area_name
|
||||
AND ac.is_active = TRUE
|
||||
WHERE dt.scd2_is_current = 1;
|
||||
## 匹配优先级
|
||||
|
||||
-- 按分类汇总收入
|
||||
SELECT
|
||||
COALESCE(ac.category_name, '其他') AS category_name,
|
||||
SUM(tfl.ledger_amount) AS total_amount
|
||||
FROM dwd.dwd_table_fee_log tfl
|
||||
LEFT JOIN dwd.dim_table dt ON dt.table_id = tfl.site_table_id
|
||||
LEFT JOIN dws.cfg_area_category ac ON dt.site_table_area_name = ac.source_area_name
|
||||
GROUP BY COALESCE(ac.category_name, '其他');
|
||||
```
|
||||
| 优先级 | 匹配方式 | 说明 |
|
||||
|--------|---------|------|
|
||||
| 5 | 台桌级精确 | source_area_name + source_table_name 都匹配 |
|
||||
| 10 | 区域级精确 | source_area_name 匹配,source_table_name 为 NULL |
|
||||
| 50 | 模糊匹配 | source_area_name 包含模式匹配 |
|
||||
| 999 | 兜底 | 无法匹配的区域归入 OTHER |
|
||||
|
||||
## 分类映射
|
||||
|
||||
| 分类代码 | 显示名称 | 简写 | 源区域 |
|
||||
|----------|---------|------|--------|
|
||||
| BILLIARD | 🎱 中式/追分 | 🎱 | A区、B区、C区、TV台、VIP包厢(V1-V4) |
|
||||
| SNOOKER | 斯诺克 | 斯 | 斯诺克区、VIP包厢(V5) |
|
||||
| MAHJONG | 🀄 麻将/棋牌 | 🀄 | 麻将房、M7、M8、666、发财 |
|
||||
| KTV | 🎤 团建/K歌 | 🎤 | K包、k包活动区、幸会158 |
|
||||
| SPECIAL | 补时长 | 补 | 补时长 |
|
||||
| OTHER | 其他 | 他 | 兜底 |
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
| 表名 | dws_assistant_daily_detail |
|
||||
| 主键 | id |
|
||||
| 唯一键 | (site_id, assistant_id, stat_date) |
|
||||
| 数据来源 | dwd_assistant_service_log + dwd_assistant_trash_event |
|
||||
| 数据来源 | dwd_assistant_service_log + dwd_assistant_service_log_ex |
|
||||
| 更新频率 | 每小时增量更新 |
|
||||
| 说明 | 以"助教+日期"为粒度,汇总每日业绩明细 |
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
| 5 | assistant_nickname | VARCHAR(50) | YES | 助教花名(冗余,便于查询展示) |
|
||||
| 6 | stat_date | DATE | NO | 统计日期 |
|
||||
| 7 | assistant_level_code | INTEGER | YES | 助教等级代码(SCD2口径:取stat_date当日生效的等级) |
|
||||
| 8 | assistant_level_name | VARCHAR(20) | YES | 助教等级名称 |
|
||||
| 8 | assistant_level_name | VARCHAR(20) | YES | 助教等级名称(由 `level_code` 静态映射得出,不依赖 SCD2 返回值) |
|
||||
| 9 | total_service_count | INTEGER | NO | 总服务次数 |
|
||||
| 10 | base_service_count | INTEGER | NO | 基础课服务次数 |
|
||||
| 11 | bonus_service_count | INTEGER | NO | 附加课服务次数 |
|
||||
@@ -46,8 +46,12 @@
|
||||
| 26 | unique_tables | INTEGER | NO | 服务台桌数(去重) |
|
||||
| 27 | trashed_seconds | INTEGER | NO | 被废除的服务时长(秒) |
|
||||
| 28 | trashed_count | INTEGER | NO | 被废除的服务次数 |
|
||||
| 29 | created_at | TIMESTAMPTZ | NO | 创建时间 |
|
||||
| 30 | updated_at | TIMESTAMPTZ | NO | 更新时间 |
|
||||
| 29 | penalty_minutes | NUMERIC(10,2) | YES | 惩罚分钟数(定档折算)。公式:`actual_minutes × (1 - per_hour_contribution / 24)`,per_hour_contribution ≥ 24 时为 0 |
|
||||
| 30 | penalty_reason | TEXT | YES | 惩罚原因描述(NULL=无违规) |
|
||||
| 31 | is_exempt | BOOLEAN | NO | 是否豁免惩罚(豁免助教不计算惩罚) |
|
||||
| 32 | per_hour_contribution | NUMERIC(10,2) | YES | 每小时贡献金额(= `base_ledger_amount / base_hours / overlap_count`,NULL=无违规或豁免) |
|
||||
| 33 | created_at | TIMESTAMPTZ | NO | 创建时间 |
|
||||
| 34 | updated_at | TIMESTAMPTZ | NO | 更新时间 |
|
||||
|
||||
## 数据来源
|
||||
|
||||
@@ -68,17 +72,21 @@ WHERE is_delete = 0
|
||||
GROUP BY site_id, DATE(start_use_time), site_assistant_id, nickname;
|
||||
```
|
||||
|
||||
### 废除记录:dwd_assistant_trash_event
|
||||
### 废除记录:dwd_assistant_service_log_ex
|
||||
|
||||
> ⚠️ `dwd_assistant_trash_event` 已于 2026-02-22 废弃,作废判断改用 `dwd_assistant_service_log_ex.is_trash`(0=正常,1=作废)。
|
||||
|
||||
```sql
|
||||
SELECT
|
||||
site_id,
|
||||
DATE(create_time) AS stat_date,
|
||||
assistant_no,
|
||||
assistant_name,
|
||||
SUM(charge_minutes_raw * 60) AS trashed_seconds,
|
||||
COUNT(*) AS trashed_count
|
||||
FROM dwd.dwd_assistant_trash_event
|
||||
GROUP BY site_id, DATE(create_time), assistant_no, assistant_name;
|
||||
s.site_id,
|
||||
DATE(s.start_use_time) AS stat_date,
|
||||
s.site_assistant_id AS assistant_id,
|
||||
SUM(CASE WHEN ex.is_trash = 1 THEN s.income_seconds ELSE 0 END) AS trashed_seconds,
|
||||
COUNT(CASE WHEN ex.is_trash = 1 THEN 1 END) AS trashed_count
|
||||
FROM dwd.dwd_assistant_service_log s
|
||||
LEFT JOIN dwd.dwd_assistant_service_log_ex ex ON s.assistant_service_id = ex.assistant_service_id
|
||||
WHERE s.is_delete = 0
|
||||
GROUP BY s.site_id, DATE(s.start_use_time), s.site_assistant_id;
|
||||
```
|
||||
|
||||
## 使用说明
|
||||
@@ -115,4 +123,4 @@ GROUP BY assistant_id, DATE_TRUNC('month', stat_date);
|
||||
|------|------|
|
||||
| 可回溯 | ✅ 完全可回溯 |
|
||||
| 数据范围 | 2025-07-21 ~ 至今 |
|
||||
| 依赖表 | dwd_assistant_service_log, dwd_assistant_trash_event, dim_assistant |
|
||||
| 依赖表 | dwd_assistant_service_log, dwd_assistant_service_log_ex, dim_assistant, dim_table, cfg_skill_type |
|
||||
|
||||
@@ -0,0 +1,136 @@
|
||||
# dws_assistant_project_tag 助教项目标签表
|
||||
|
||||
> 生成时间:2026-03-07
|
||||
|
||||
## 表信息
|
||||
|
||||
| 属性 | 值 |
|
||||
|------|-----|
|
||||
| Schema | dws |
|
||||
| 表名 | dws_assistant_project_tag |
|
||||
| 主键 | id |
|
||||
| 唯一键 | (site_id, assistant_id, time_window, category_code) |
|
||||
| 数据来源 | dwd_assistant_service_log + dim_table + cfg_area_category |
|
||||
| 更新频率 | 每日全量重建(按 site_id 删除后重新插入) |
|
||||
| 说明 | 按时间窗口计算助教在四大项目的工作时长占比,≥25% 分配标签 |
|
||||
|
||||
## 字段说明
|
||||
|
||||
| 序号 | 字段名 | 类型 | 可空 | 说明 |
|
||||
|------|--------|------|------|------|
|
||||
| 1 | id | BIGSERIAL | NO | 自增主键 |
|
||||
| 2 | site_id | BIGINT | NO | 门店ID |
|
||||
| 3 | tenant_id | BIGINT | NO | 租户ID |
|
||||
| 4 | assistant_id | BIGINT | NO | 助教ID |
|
||||
| 5 | time_window | VARCHAR(40) | NO | 时间窗口枚举值 |
|
||||
| 6 | category_code | VARCHAR(30) | NO | 项目分类代码(BILLIARD/SNOOKER/MAHJONG/KTV) |
|
||||
| 7 | category_name | VARCHAR(50) | NO | 项目显示名称(如 🎱 中式/追分) |
|
||||
| 8 | short_name | VARCHAR(10) | NO | 项目简写(如 🎱) |
|
||||
| 9 | duration_seconds | BIGINT | NO | 该项目总工作时长(秒) |
|
||||
| 10 | total_seconds | BIGINT | NO | 所有四大项目总时长(秒) |
|
||||
| 11 | percentage | NUMERIC(5,4) | NO | 占比(0~1,四位小数) |
|
||||
| 12 | is_tagged | BOOLEAN | NO | 占比≥0.25 时为 TRUE |
|
||||
| 13 | computed_at | TIMESTAMPTZ | NO | 计算时间 |
|
||||
| 14 | created_at | TIMESTAMPTZ | NO | 创建时间 |
|
||||
| 15 | updated_at | TIMESTAMPTZ | NO | 更新时间 |
|
||||
|
||||
## 时间窗口
|
||||
|
||||
助教看板使用 6 个时间窗口:
|
||||
|
||||
| 枚举值 | 说明 |
|
||||
|--------|------|
|
||||
| THIS_MONTH | 本月(月初 ~ 今天) |
|
||||
| THIS_QUARTER | 本季度(季度首月1日 ~ 今天) |
|
||||
| LAST_MONTH | 上月(上月初 ~ 上月末) |
|
||||
| LAST_3_MONTHS_EXCL_CURRENT | 前3个月不含本月 |
|
||||
| LAST_QUARTER | 上季度 |
|
||||
| LAST_6_MONTHS | 最近半年(不含本月) |
|
||||
|
||||
## 索引
|
||||
|
||||
| 索引名 | 字段 | 类型 | 说明 |
|
||||
|--------|------|------|------|
|
||||
| pk_dws_assistant_project_tag | id | 主键 | 自增主键 |
|
||||
| uk_dws_assistant_project_tag | (site_id, assistant_id, time_window, category_code) | 唯一 | 业务唯一键 |
|
||||
| idx_apt_site_window_tagged | (site_id, time_window) WHERE is_tagged=TRUE | 部分索引 | 加速看板查询 |
|
||||
|
||||
|
||||
## 数据链路
|
||||
|
||||
```
|
||||
dwd.dwd_assistant_service_log (income_seconds, site_table_id)
|
||||
→ JOIN dwd.dim_table (site_table_id → table_id, scd2_is_current=1)
|
||||
→ get_area_category(area_name, table_name) -- 通过 cfg_area_category 映射
|
||||
→ 只保留 BILLIARD/SNOOKER/MAHJONG/KTV
|
||||
→ 按 (assistant_id, category_code) 汇总 income_seconds
|
||||
→ 计算占比 percentage = duration_seconds / total_seconds
|
||||
→ ≥0.25 标记 is_tagged=TRUE
|
||||
→ 写入 dws.dws_assistant_project_tag
|
||||
```
|
||||
|
||||
### 关键规则
|
||||
|
||||
1. 数据链路走 `dim_table`(通过 `site_table_id` JOIN),不直接用事实表的 `site_table_area_name`
|
||||
2. 只计算四大项目(BILLIARD/SNOOKER/MAHJONG/KTV),SPECIAL/OTHER 不参与
|
||||
3. 标签阈值 25%(`TAG_THRESHOLD = 0.25`)
|
||||
4. 全量删除重建策略:按 `site_id` 删除后重新插入所有时间窗口
|
||||
5. `is_delete = 0` 过滤已删除的服务记录
|
||||
|
||||
## ETL 任务
|
||||
|
||||
| 属性 | 值 |
|
||||
|------|-----|
|
||||
| 任务代码 | DWS_ASSISTANT_PROJECT_TAG |
|
||||
| Python 类 | AssistantProjectTagTask |
|
||||
| 文件 | tasks/dws/assistant_project_tag_task.py |
|
||||
| 依赖 | DWD_LOAD_FROM_ODS |
|
||||
|
||||
## 变更记录
|
||||
|
||||
| 日期 | 变更 | 说明 |
|
||||
|------|------|------|
|
||||
| 2026-03-07 | 新建表 | 支持助教看板按项目类型筛选 |
|
||||
|
||||
## 验证 SQL
|
||||
|
||||
```sql
|
||||
-- 1. 确认表存在且有数据
|
||||
SELECT COUNT(*) AS row_count,
|
||||
COUNT(DISTINCT assistant_id) AS assistant_count,
|
||||
COUNT(DISTINCT time_window) AS window_count
|
||||
FROM dws.dws_assistant_project_tag;
|
||||
|
||||
-- 2. 确认 category_code 只有四大项目
|
||||
SELECT DISTINCT category_code
|
||||
FROM dws.dws_assistant_project_tag
|
||||
ORDER BY category_code;
|
||||
-- 期望:BILLIARD, KTV, MAHJONG, SNOOKER
|
||||
|
||||
-- 3. 确认占比计算正确(duration_seconds / total_seconds ≈ percentage)
|
||||
SELECT site_id, assistant_id, time_window, category_code,
|
||||
duration_seconds, total_seconds, percentage,
|
||||
ROUND(duration_seconds::numeric / NULLIF(total_seconds, 0), 4) AS calc_pct,
|
||||
is_tagged,
|
||||
(percentage >= 0.25) AS should_be_tagged
|
||||
FROM dws.dws_assistant_project_tag
|
||||
WHERE percentage >= 0.25 AND is_tagged = FALSE
|
||||
LIMIT 10;
|
||||
-- 期望:0 行(所有 ≥25% 的都应标记为 TRUE)
|
||||
|
||||
-- 4. 确认唯一键无重复
|
||||
SELECT site_id, assistant_id, time_window, category_code, COUNT(*)
|
||||
FROM dws.dws_assistant_project_tag
|
||||
GROUP BY site_id, assistant_id, time_window, category_code
|
||||
HAVING COUNT(*) > 1;
|
||||
-- 期望:0 行
|
||||
```
|
||||
|
||||
## 回滚策略
|
||||
|
||||
```sql
|
||||
-- 删除表(不影响其他表)
|
||||
DROP TABLE IF EXISTS dws.dws_assistant_project_tag CASCADE;
|
||||
-- 从 task_registry.py 移除 DWS_ASSISTANT_PROJECT_TAG 注册
|
||||
-- 从 maintenance_task.py DEFAULT_RETENTION_TABLES 移除对应条目
|
||||
```
|
||||
@@ -22,7 +22,7 @@
|
||||
| 2 | site_id | BIGINT | NO | 门店ID |
|
||||
| 3 | tenant_id | BIGINT | NO | 租户ID |
|
||||
| 4 | stat_date | DATE | NO | 统计日期 |
|
||||
| 5 | gross_amount | NUMERIC(14,2) | NO | 发生额合计 |
|
||||
| 5 | gross_amount | NUMERIC(14,2) | NO | 发生额合计(= 四项正价之和:table_fee + goods + assistant_pd + assistant_cx,不含 electricity_money,不使用 `consume_money`) |
|
||||
| 6 | table_fee_amount | NUMERIC(14,2) | NO | 台费正价 |
|
||||
| 7 | goods_amount | NUMERIC(14,2) | NO | 商品正价 |
|
||||
| 8 | assistant_pd_amount | NUMERIC(14,2) | NO | 助教基础课正价(陪打) |
|
||||
@@ -31,9 +31,9 @@
|
||||
| 11 | discount_groupbuy | NUMERIC(14,2) | NO | 团购优惠 |
|
||||
| 12 | discount_vip | NUMERIC(14,2) | NO | 会员折扣 |
|
||||
| 13 | discount_gift_card | NUMERIC(14,2) | NO | 赠送卡抵扣(余额变动) |
|
||||
| 14 | discount_manual | NUMERIC(14,2) | NO | 手动调整 |
|
||||
| 14 | discount_manual | NUMERIC(14,2) | NO | 大客户优惠(从 adjust_amount 中按配置拆出) |
|
||||
| 15 | discount_rounding | NUMERIC(14,2) | NO | 抹零 |
|
||||
| 16 | discount_other | NUMERIC(14,2) | NO | 其他优惠 |
|
||||
| 16 | discount_other | NUMERIC(14,2) | NO | 其他优惠(adjust_amount - 大客户优惠) |
|
||||
| 17 | confirmed_income | NUMERIC(14,2) | NO | 确认收入 = 发生额 - 优惠 |
|
||||
| 18 | cash_inflow_total | NUMERIC(14,2) | NO | 现金流入合计 |
|
||||
| 19 | cash_pay_amount | NUMERIC(14,2) | NO | 收银实付 |
|
||||
@@ -42,7 +42,7 @@
|
||||
| 22 | platform_fee_amount | NUMERIC(14,2) | NO | 平台佣金+服务费(导入) |
|
||||
| 23 | recharge_cash_inflow | NUMERIC(14,2) | NO | 充值现金流入 |
|
||||
| 24 | card_consume_total | NUMERIC(14,2) | NO | 卡消费合计 |
|
||||
| 25 | cash_card_consume | NUMERIC(14,2) | NO | 储值卡消费 |
|
||||
| 25 | recharge_card_consume | NUMERIC(14,2) | NO | 现金充值卡消费(= `recharge_card_amount`,仅现金充值卡支付部分,不含赠送卡) |
|
||||
| 26 | gift_card_consume | NUMERIC(14,2) | NO | 赠送卡消费 |
|
||||
| 27 | cash_outflow_total | NUMERIC(14,2) | NO | 现金流出合计 |
|
||||
| 28 | cash_balance_change | NUMERIC(14,2) | NO | 现金余额变动 |
|
||||
@@ -63,7 +63,14 @@
|
||||
|
||||
## 数据来源
|
||||
|
||||
> ⚠️ **consume_money 口径警告**:飞球上游 `consume_money` 在不同时期存在三种口径(A/B/C),DWS 层不应直接使用。
|
||||
> 应使用 `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)。
|
||||
|
||||
### 结账汇总:dwd_settlement_head
|
||||
|
||||
> ⚠️ 以下示例 SQL 使用 `DATE(pay_time)` 简化展示。实际代码使用 `biz_date_sql_expr(pay_time, cutoff_hour)` 进行营业日归属(跨日订单归前一天)。
|
||||
|
||||
```sql
|
||||
SELECT
|
||||
DATE(pay_time) AS stat_date,
|
||||
@@ -82,6 +89,7 @@ SELECT
|
||||
SUM(pl_coupon_sale_amount) AS pl_coupon_sale_amount
|
||||
FROM dwd.dwd_settlement_head
|
||||
WHERE site_id = :site_id
|
||||
AND settle_type IN (1, 3) -- 仅台桌结账+商城订单,排除退货(6)/退款(7)
|
||||
GROUP BY DATE(pay_time);
|
||||
```
|
||||
|
||||
@@ -137,12 +145,49 @@ GROUP BY change_time::DATE;
|
||||
|
||||
**计算公式**
|
||||
```
|
||||
-- gross_amount 基于 items_sum 各分项(全时期一致),不使用 consume_money
|
||||
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
|
||||
confirmed_income = gross_amount - discount_total
|
||||
cash_inflow_total = cash_pay_amount + groupbuy_pay_amount + platform_settlement_amount + recharge_cash_inflow
|
||||
cash_inflow_total = cash_pay_amount + platform_inflow + recharge_cash_inflow
|
||||
-- platform_inflow:优先取 platform_settlement_amount(平台回款),为 0 时取 groupbuy_pay_amount(团购支付)
|
||||
-- 两者互斥,不可同时计入
|
||||
```
|
||||
|
||||
> ⚠️ `discount_manual` 存储大客户优惠(从 adjust_amount 中按配置的会员ID/订单ID拆出),`discount_other` 存储其他手动调整(= adjust_amount - 大客户优惠)。两者互斥,之和 = adjust_amount。
|
||||
|
||||
**支付渠道恒等式**
|
||||
```
|
||||
-- 以下恒等式 100% 成立(DWD-DOC 校准确认)
|
||||
balance_amount = recharge_card_amount + gift_card_amount -- 储值卡 = 充值卡 + 礼品卡
|
||||
pay_amount = point_amount + cash_amount -- 实付 = 积分 + 现金(互斥)
|
||||
```
|
||||
|
||||
> ⚠️ `balance_amount`(储值卡支付)是独立支付渠道,`recharge_card_amount` 和 `gift_card_amount` 是其分账明细,不可与 `balance_amount` 重复计算。
|
||||
|
||||
**团购券三层价格体系**
|
||||
```
|
||||
顾客支付价(PCR.sale_price)→ 平台结算价(SH.pl_coupon_sale_amount)→ 门店抵扣价(SH.coupon_amount)
|
||||
门店补贴 = coupon_amount - pl_coupon_sale_amount
|
||||
```
|
||||
- `pl_coupon_sale_amount = SUM(GR.ledger_unit_price)` ✅ 100%
|
||||
- `coupon_amount = SUM(GR.ledger_amount)` ✅ 100%
|
||||
- P1 期间(2025-07~10)`pl_coupon_sale_amount` 恒为 0
|
||||
|
||||
**F2 收支平衡公式(三期差异)**
|
||||
```
|
||||
P1/P2(< 2026-01-15 12:45:59):
|
||||
consume = coupon + pay + balance - rounding + adjust + member_disc + prepay(ex)
|
||||
|
||||
B 类过渡期(2026-01-15 12:46~18:44,约 40 笔):
|
||||
consume = 2*coupon + pay + balance - rounding + adjust + member_disc + prepay(ex)
|
||||
|
||||
P3(≥ 2026-01-15 18:45,当前生效):
|
||||
consume = coupon + pl_coupon + pay + balance - rounding + adjust + member_disc + prepay(ex)
|
||||
```
|
||||
- 通过率:P1/P2 99.24% | B 95.00% | P3 99.87%
|
||||
- 详见 [F2 收支平衡专项](../../../../docs/reports/DWD-DOC/05-f2-balance-audit.md)
|
||||
|
||||
**物化汇总层(可选)**
|
||||
- L1~L4 物化视图:`mv_dws_finance_daily_summary_l1` / `l2` / `l3` / `l4`
|
||||
- 刷新任务:`DWS_MV_REFRESH_FINANCE_DAILY`
|
||||
|
||||
@@ -37,16 +37,17 @@
|
||||
|--------------------|--------------------|----------|
|
||||
| GROUPBUY | 团购优惠 | dwd_settlement_head.coupon_amount - 团购实付 |
|
||||
| VIP | 会员折扣 | dwd_settlement_head.member_discount_amount |
|
||||
| GIFT_CARD_TABLE | 台费卡抵扣 | dwd_member_balance_change |
|
||||
| GIFT_CARD_DRINK | 酒水卡抵扣 | dwd_member_balance_change |
|
||||
| GIFT_CARD_COUPON | 活动抵用券抵扣 | dwd_member_balance_change |
|
||||
| MANUAL | 手动调整 | dwd_settlement_head.adjust_amount |
|
||||
| GIFT_CARD_TABLE | 台费卡抵扣 | dwd_member_balance_change(`card_type_id = 2791990152417157`) |
|
||||
| GIFT_CARD_DRINK | 酒水卡抵扣 | dwd_member_balance_change(`card_type_id = 2794699703437125`) |
|
||||
| GIFT_CARD_COUPON | 活动抵用券抵扣 | dwd_member_balance_change(`card_type_id = 2793266846533445`) |
|
||||
| BIG_CUSTOMER | 大客户优惠 | dwd_settlement_head(big_customer_amount,从 adjust_amount 拆分) |
|
||||
| OTHER | 其他优惠 | adjust_amount - big_customer_amount(其他无法归类的手动调整) |
|
||||
| ROUNDING | 抹零 | dwd_settlement_head.rounding_amount |
|
||||
| BIG_CUSTOMER | 大客户优惠 | dwd_settlement_head(特定会员优惠) |
|
||||
| OTHER | 其他优惠 | 其他无法归类的优惠 |
|
||||
|
||||
## 数据来源
|
||||
|
||||
> ⚠️ 以下示例 SQL 使用 `pay_time::DATE` 简化展示。实际代码使用 `biz_date_sql_expr(pay_time, cutoff_hour)` 进行营业日归属(跨日订单归前一天),详见 ETL 配置 `app.business_day_start_hour`。
|
||||
|
||||
```sql
|
||||
-- 从结账头表提取优惠汇总
|
||||
SELECT
|
||||
@@ -62,7 +63,7 @@ SELECT
|
||||
COUNT(CASE WHEN rounding_amount != 0 THEN 1 END) AS rounding_order_count
|
||||
FROM dwd.dwd_settlement_head
|
||||
WHERE site_id = :site_id
|
||||
AND settle_status = 1
|
||||
AND settle_type IN (1, 3)
|
||||
GROUP BY pay_time::DATE;
|
||||
```
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
| 表名 | dws_finance_income_structure |
|
||||
| 主键 | id |
|
||||
| 唯一键 | (site_id, stat_date, structure_type, category_code) |
|
||||
| 数据来源 | dwd_table_fee_log + dwd_assistant_service_log + cfg_area_category |
|
||||
| 数据来源 | dwd_settlement_head + dwd_table_fee_log + dwd_assistant_service_log + cfg_area_category |
|
||||
| 更新频率 | 每日更新 |
|
||||
| 说明 | 以"日期+区域/类型"为粒度,分析收入结构 |
|
||||
|
||||
@@ -35,23 +35,28 @@
|
||||
## 分类代码说明
|
||||
|
||||
### 按区域分析 (structure_type = 'AREA')
|
||||
| category_code | category_name | 来源 |
|
||||
|---------------|---------------|------|
|
||||
| BILLIARD | 台球散台 | A区/B区/C区/TV台 |
|
||||
| BILLIARD_VIP | 台球VIP | VIP包厢 |
|
||||
| SNOOKER | 斯诺克 | 斯诺克区 |
|
||||
| MAHJONG | 麻将棋牌 | 麻将房/M7/M8/666/发财 |
|
||||
| KTV | K歌娱乐 | K包/k包活动区/幸会158 |
|
||||
| SPECIAL | 补时长 | 补时长 |
|
||||
| OTHER | 其他 | 未映射区域 |
|
||||
| category_code | category_name | display_name | 来源 |
|
||||
|---------------|---------------|--------------|------|
|
||||
| BILLIARD | 🎱 中式/追分 | 🎱 中式/追分 | A区/B区/C区/TV台/VIP包厢(V1-V4) |
|
||||
| SNOOKER | 斯诺克 | 斯诺克 | 斯诺克区/VIP包厢(V5) |
|
||||
| MAHJONG | 🀄 麻将/棋牌 | 🀄 麻将/棋牌 | 麻将房/M7/M8/666/发财 |
|
||||
| KTV | 🎤 团建/K歌 | 🎤 团建/K歌 | K包/k包活动区/幸会158 |
|
||||
| SPECIAL | 补时长 | 补时长 | 补时长 |
|
||||
| OTHER | 其他 | 其他 | 未映射区域 |
|
||||
|
||||
> ⚠️ `BILLIARD_VIP` 已于 2026-03-07 废弃,VIP包厢按台桌级映射拆分至 BILLIARD(V1-V4) 和 SNOOKER(V5)。
|
||||
|
||||
### 按收入类型分析 (structure_type = 'INCOME_TYPE')
|
||||
| category_code | category_name |
|
||||
|---------------|---------------|
|
||||
| TABLE_FEE | 台费收入 |
|
||||
| GOODS | 商品收入 |
|
||||
| ASSISTANT_BASE | 助教基础课收入 |
|
||||
| ASSISTANT_BONUS | 助教附加课收入 |
|
||||
| category_code | category_name | 数据来源字段 |
|
||||
|---------------|---------------|-------------|
|
||||
| TABLE_FEE | 台费收入 | `settlement_head.table_charge_money` |
|
||||
| GOODS | 商品收入 | `settlement_head.goods_money` |
|
||||
| ASSISTANT_PD | 助教陪打收入 | `settlement_head.assistant_pd_money` |
|
||||
| ASSISTANT_CX | 助教超休收入 | `settlement_head.assistant_cx_money` |
|
||||
|
||||
> ⚠️ 历史版本曾使用 `ASSISTANT_BASE`/`ASSISTANT_BONUS`,已更正为 `ASSISTANT_PD`(陪打)/`ASSISTANT_CX`(超休),与 DWD 结算单字段对齐。
|
||||
> 收入金额取自 `items_sum` 各分项(`table_charge_money + goods_money + assistant_pd_money + assistant_cx_money + electricity_money`),
|
||||
> 不使用 `consume_money`(存在三种历史口径混合,详见 [consume_money 口径](../../../../docs/reports/DWD-DOC/consume/consume-money-caliber.md))。
|
||||
|
||||
## 数据来源
|
||||
|
||||
@@ -85,4 +90,4 @@ income_ratio = income_amount / SUM(income_amount) OVER (PARTITION BY stat_date,
|
||||
|------|------|
|
||||
| 可回溯 | ✅ 完全可回溯 |
|
||||
| 数据范围 | 2025-07-21 ~ 至今 |
|
||||
| 依赖表 | dwd_table_fee_log, dwd_assistant_service_log, dim_table, cfg_area_category |
|
||||
| 依赖表 | dwd_settlement_head, dwd_table_fee_log, dwd_assistant_service_log, dim_table, cfg_area_category |
|
||||
|
||||
@@ -49,16 +49,16 @@
|
||||
SELECT
|
||||
DATE(pay_time) AS stat_date,
|
||||
COUNT(*) AS recharge_count,
|
||||
SUM(pay_money + gift_money) AS recharge_total,
|
||||
SUM(pay_money) AS recharge_cash,
|
||||
SUM(gift_money) AS recharge_gift,
|
||||
SUM(pay_amount + point_amount) AS recharge_total,
|
||||
SUM(pay_amount) AS recharge_cash,
|
||||
SUM(point_amount) AS recharge_gift,
|
||||
-- 首充
|
||||
SUM(CASE WHEN is_first = 1 THEN 1 ELSE 0 END) AS first_recharge_count,
|
||||
SUM(CASE WHEN is_first = 1 THEN pay_money ELSE 0 END) AS first_recharge_cash,
|
||||
SUM(CASE WHEN is_first = 1 THEN gift_money ELSE 0 END) AS first_recharge_gift,
|
||||
SUM(CASE WHEN is_first = 1 THEN pay_amount ELSE 0 END) AS first_recharge_cash,
|
||||
SUM(CASE WHEN is_first = 1 THEN point_amount ELSE 0 END) AS first_recharge_gift,
|
||||
-- 续充
|
||||
SUM(CASE WHEN is_first != 1 OR is_first IS NULL THEN 1 ELSE 0 END) AS renewal_count,
|
||||
SUM(CASE WHEN is_first != 1 OR is_first IS NULL THEN pay_money ELSE 0 END) AS renewal_cash,
|
||||
SUM(CASE WHEN is_first != 1 OR is_first IS NULL THEN pay_amount ELSE 0 END) AS renewal_cash,
|
||||
-- 会员数
|
||||
COUNT(DISTINCT member_id) AS recharge_member_count
|
||||
FROM dwd.dwd_recharge_order
|
||||
|
||||
@@ -30,40 +30,50 @@
|
||||
| 10 | first_consume_date | DATE | YES | 首次消费日期 |
|
||||
| 11 | last_consume_date | DATE | YES | 最近消费日期 |
|
||||
| 12 | total_visit_count | INTEGER | NO | 累计到店次数 |
|
||||
| 13 | total_consume_amount | NUMERIC(14,2) | NO | 累计消费金额 |
|
||||
| 14 | total_recharge_amount | NUMERIC(14,2) | NO | 累计充值金额 |
|
||||
| 15 | total_table_fee | NUMERIC(14,2) | NO | 累计台费 |
|
||||
| 16 | total_goods_amount | NUMERIC(14,2) | NO | 累计商品消费 |
|
||||
| 17 | total_assistant_amount | NUMERIC(14,2) | NO | 累计助教服务消费 |
|
||||
| 13 | total_consume_amount | NUMERIC(14,2) | NO | 累计消费金额(基于 `items_sum` 口径,见下方说明) |
|
||||
| 14 | total_recharge_amount | NUMERIC(14,2) | NO | 累计充值金额(来源:`dim_member.recharge_money_sum`,上游 API 同步值) |
|
||||
| 15 | total_table_fee | NUMERIC(14,2) | NO | 累计台费(`table_charge_money`) |
|
||||
| 16 | total_goods_amount | NUMERIC(14,2) | NO | 累计商品消费(`goods_money`) |
|
||||
| 17 | total_assistant_amount | NUMERIC(14,2) | NO | 累计助教服务消费(= `assistant_pd_money` + `assistant_cx_money`) |
|
||||
| 18-23 | visit_count_7d/10d/15d/30d/60d/90d | INTEGER | NO | 近N天到店次数 |
|
||||
| 24-29 | consume_amount_7d/10d/15d/30d/60d/90d | NUMERIC(14,2) | NO | 近N天消费金额 |
|
||||
| 30 | cash_card_balance | NUMERIC(14,2) | NO | 储值卡余额 |
|
||||
| 31 | gift_card_balance | NUMERIC(14,2) | NO | 赠送卡余额 |
|
||||
| 32 | total_card_balance | NUMERIC(14,2) | NO | 总卡余额 |
|
||||
| 33 | days_since_last | INTEGER | YES | 距离最近消费的天数 |
|
||||
| 34 | is_active_7d | BOOLEAN | NO | 近7天是否活跃 |
|
||||
| 35 | is_active_30d | BOOLEAN | NO | 近30天是否活跃 |
|
||||
| 36 | is_active_90d | BOOLEAN | NO | 近90天是否活跃 |
|
||||
| 37 | customer_tier | VARCHAR(20) | YES | 客户分层(高价值/中等/低活跃/流失) |
|
||||
| 38 | created_at | TIMESTAMPTZ | NO | 创建时间 |
|
||||
| 39 | updated_at | TIMESTAMPTZ | NO | 更新时间 |
|
||||
| 30-32 | recharge_count_30d/60d/90d | INTEGER | NO | 近N天充值笔数(来源:dwd_recharge_order) |
|
||||
| 33-35 | recharge_amount_30d/60d/90d | NUMERIC(14,2) | NO | 近N天充值金额(仅 `pay_amount` 现金部分,不含 `point_amount` 赠送,来源:dwd_recharge_order) |
|
||||
| 36 | avg_ticket_amount | NUMERIC(14,2) | NO | 次均消费(= total_consume_amount / MAX(total_visit_count, 1)) |
|
||||
| 37 | cash_card_balance | NUMERIC(14,2) | NO | 储值卡余额 |
|
||||
| 38 | gift_card_balance | NUMERIC(14,2) | NO | 赠送卡余额 |
|
||||
| 39 | total_card_balance | NUMERIC(14,2) | NO | 总卡余额 |
|
||||
| 40 | days_since_last | INTEGER | YES | 距离最近消费的天数 |
|
||||
| 41 | is_active_7d | BOOLEAN | NO | 近7天是否活跃 |
|
||||
| 42 | is_active_30d | BOOLEAN | NO | 近30天是否活跃 |
|
||||
| 43 | is_active_90d | BOOLEAN | NO | 近90天是否活跃 |
|
||||
| 44 | customer_tier | VARCHAR(20) | YES | 客户分层(高价值/中等/低活跃/流失) |
|
||||
| 45 | created_at | TIMESTAMPTZ | NO | 创建时间 |
|
||||
| 46 | updated_at | TIMESTAMPTZ | NO | 更新时间 |
|
||||
|
||||
## 数据来源
|
||||
|
||||
### 消费统计来源:dwd_settlement_head
|
||||
|
||||
> ⚠️ **consume_money 口径警告**:`consume_money` 在不同时期存在三种口径(A/B/C),DWS 层不应直接使用。
|
||||
> 应使用 `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)。
|
||||
|
||||
```sql
|
||||
SELECT
|
||||
site_id,
|
||||
member_id,
|
||||
DATE(pay_time) AS consume_date,
|
||||
COUNT(*) AS visit_count,
|
||||
SUM(consume_money) AS consume_amount,
|
||||
-- ✅ 使用 items_sum 口径(全时期一致),不使用 consume_money
|
||||
SUM(table_charge_money + goods_money + assistant_pd_money
|
||||
+ assistant_cx_money + electricity_money) AS consume_amount,
|
||||
SUM(table_charge_money) AS table_fee,
|
||||
SUM(goods_money) AS goods_amount,
|
||||
SUM(assistant_pd_money + assistant_cx_money) AS assistant_amount
|
||||
FROM dwd.dwd_settlement_head
|
||||
WHERE member_id != 0 -- 排除散客
|
||||
AND settle_type = 1 -- 已结账
|
||||
AND settle_type IN (1, 3) -- 已结账订单(台桌结账 + 快捷结账)
|
||||
GROUP BY site_id, member_id, DATE(pay_time);
|
||||
```
|
||||
|
||||
@@ -84,19 +94,27 @@ GROUP BY tenant_member_id;
|
||||
- member_id=0 的散客不进入此表统计
|
||||
|
||||
**客户分层规则**
|
||||
```sql
|
||||
customer_tier = CASE
|
||||
WHEN consume_amount_30d >= 1000 THEN '高价值'
|
||||
WHEN consume_amount_30d >= 300 THEN '中等'
|
||||
WHEN is_active_30d THEN '低活跃'
|
||||
ELSE '流失'
|
||||
END
|
||||
```python
|
||||
# 基于 90 天消费次数+金额组合判断(代码实际逻辑)
|
||||
if visit_count_90d >= 3 and consume_amount_90d >= 1000:
|
||||
customer_tier = '高价值'
|
||||
elif visit_count_30d > 0:
|
||||
customer_tier = '中等'
|
||||
elif visit_count_90d > 0:
|
||||
customer_tier = '低活跃'
|
||||
else:
|
||||
customer_tier = '流失'
|
||||
```
|
||||
|
||||
**金额口径说明**
|
||||
- `total_consume_amount` 及各滚动窗口 `consume_amount_*d` 均基于 `items_sum` 口径
|
||||
- `items_sum = table_charge_money + goods_money + assistant_pd_money + assistant_cx_money + electricity_money`
|
||||
- `total_assistant_amount` = `assistant_pd_money`(陪打)+ `assistant_cx_money`(超休),不使用笼统的 `service_fee`
|
||||
|
||||
## 可回溯性
|
||||
|
||||
| 项目 | 说明 |
|
||||
|------|------|
|
||||
| 可回溯 | ✅ 完全可回溯 |
|
||||
| 数据范围 | 2025-07-16 ~ 至今 |
|
||||
| 依赖表 | dwd_settlement_head, dim_member, dim_member_card_account |
|
||||
| 依赖表 | dwd_settlement_head, dwd_recharge_order, dim_member, dim_member_card_account |
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
# dws_member_project_tag 客户项目标签表
|
||||
|
||||
> 生成时间:2026-03-07
|
||||
|
||||
## 表信息
|
||||
|
||||
| 属性 | 值 |
|
||||
|------|-----|
|
||||
| Schema | dws |
|
||||
| 表名 | dws_member_project_tag |
|
||||
| 主键 | id |
|
||||
| 唯一键 | (site_id, member_id, time_window, category_code) |
|
||||
| 数据来源 | dwd_table_fee_log + dim_table + cfg_area_category |
|
||||
| 更新频率 | 每日全量重建(按 site_id 删除后重新插入) |
|
||||
| 说明 | 按时间窗口计算客户在四大项目的消费时长占比,≥25% 分配标签。散客不参与。 |
|
||||
|
||||
## 字段说明
|
||||
|
||||
| 序号 | 字段名 | 类型 | 可空 | 说明 |
|
||||
|------|--------|------|------|------|
|
||||
| 1 | id | BIGSERIAL | NO | 自增主键 |
|
||||
| 2 | site_id | BIGINT | NO | 门店ID |
|
||||
| 3 | tenant_id | BIGINT | NO | 租户ID |
|
||||
| 4 | member_id | BIGINT | NO | 会员ID(散客不入此表) |
|
||||
| 5 | time_window | VARCHAR(40) | NO | 时间窗口枚举值 |
|
||||
| 6 | category_code | VARCHAR(30) | NO | 项目分类代码(BILLIARD/SNOOKER/MAHJONG/KTV) |
|
||||
| 7 | category_name | VARCHAR(50) | NO | 项目显示名称(如 🎱 中式/追分) |
|
||||
| 8 | short_name | VARCHAR(10) | NO | 项目简写(如 🎱) |
|
||||
| 9 | duration_seconds | BIGINT | NO | 该项目总计费时长(秒,来源 ledger_count) |
|
||||
| 10 | total_seconds | BIGINT | NO | 所有四大项目总时长(秒) |
|
||||
| 11 | percentage | NUMERIC(5,4) | NO | 占比(0~1,四位小数) |
|
||||
| 12 | is_tagged | BOOLEAN | NO | 占比≥0.25 时为 TRUE |
|
||||
| 13 | computed_at | TIMESTAMPTZ | NO | 计算时间 |
|
||||
| 14 | created_at | TIMESTAMPTZ | NO | 创建时间 |
|
||||
| 15 | updated_at | TIMESTAMPTZ | NO | 更新时间 |
|
||||
|
||||
## 时间窗口
|
||||
|
||||
客户看板使用 2 个时间窗口:
|
||||
|
||||
| 枚举值 | 说明 |
|
||||
|--------|------|
|
||||
| LAST_30_DAYS | 近30天(含今天,base_date-29天 ~ base_date) |
|
||||
| LAST_60_DAYS | 近60天(含今天,base_date-59天 ~ base_date) |
|
||||
|
||||
## 索引
|
||||
|
||||
| 索引名 | 字段 | 类型 | 说明 |
|
||||
|--------|------|------|------|
|
||||
| pk_dws_member_project_tag | id | 主键 | 自增主键 |
|
||||
| uk_dws_member_project_tag | (site_id, member_id, time_window, category_code) | 唯一 | 业务唯一键 |
|
||||
| idx_mpt_site_window_tagged | (site_id, time_window) WHERE is_tagged=TRUE | 部分索引 | 加速看板查询 |
|
||||
|
||||
|
||||
## 数据链路
|
||||
|
||||
```
|
||||
dwd.dwd_table_fee_log (ledger_count, site_table_id)
|
||||
→ JOIN dwd.dim_table (site_table_id → table_id, scd2_is_current=1)
|
||||
→ get_area_category(area_name, table_name) -- 通过 cfg_area_category 映射
|
||||
→ 只保留 BILLIARD/SNOOKER/MAHJONG/KTV
|
||||
→ 排除散客(member_id IS NULL 或 = 0)
|
||||
→ 按 (member_id, category_code) 汇总 ledger_count
|
||||
→ 计算占比 percentage = duration_seconds / total_seconds
|
||||
→ ≥0.25 标记 is_tagged=TRUE
|
||||
→ 写入 dws.dws_member_project_tag
|
||||
```
|
||||
|
||||
### 关键规则
|
||||
|
||||
1. 数据链路走 `dim_table`(通过 `site_table_id` JOIN),不直接用事实表的 `site_table_area_name`
|
||||
2. 客户时长使用 `ledger_count`(计费时长),不使用 `income_seconds`(那是助教工作时长)
|
||||
3. 散客(member_id=0 或 NULL)不参与标签计算
|
||||
4. 只计算四大项目(BILLIARD/SNOOKER/MAHJONG/KTV)
|
||||
5. 标签阈值 25%(`TAG_THRESHOLD = 0.25`)
|
||||
6. 全量删除重建策略:按 `site_id` 删除后重新插入所有时间窗口
|
||||
7. `COALESCE(is_delete, 0) = 0` 过滤已删除的台费记录
|
||||
|
||||
## ETL 任务
|
||||
|
||||
| 属性 | 值 |
|
||||
|------|-----|
|
||||
| 任务代码 | DWS_MEMBER_PROJECT_TAG |
|
||||
| Python 类 | MemberProjectTagTask |
|
||||
| 文件 | tasks/dws/member_project_tag_task.py |
|
||||
| 依赖 | DWD_LOAD_FROM_ODS |
|
||||
|
||||
## 变更记录
|
||||
|
||||
| 日期 | 变更 | 说明 |
|
||||
|------|------|------|
|
||||
| 2026-03-07 | 新建表 | 支持客户看板按项目类型筛选 |
|
||||
|
||||
## 验证 SQL
|
||||
|
||||
```sql
|
||||
-- 1. 确认表存在且有数据
|
||||
SELECT COUNT(*) AS row_count,
|
||||
COUNT(DISTINCT member_id) AS member_count,
|
||||
COUNT(DISTINCT time_window) AS window_count
|
||||
FROM dws.dws_member_project_tag;
|
||||
|
||||
-- 2. 确认无散客数据
|
||||
SELECT COUNT(*) FROM dws.dws_member_project_tag WHERE member_id = 0 OR member_id IS NULL;
|
||||
-- 期望:0
|
||||
|
||||
-- 3. 确认占比计算正确
|
||||
SELECT site_id, member_id, time_window, category_code,
|
||||
duration_seconds, total_seconds, percentage,
|
||||
ROUND(duration_seconds::numeric / NULLIF(total_seconds, 0), 4) AS calc_pct,
|
||||
is_tagged,
|
||||
(percentage >= 0.25) AS should_be_tagged
|
||||
FROM dws.dws_member_project_tag
|
||||
WHERE percentage >= 0.25 AND is_tagged = FALSE
|
||||
LIMIT 10;
|
||||
-- 期望:0 行
|
||||
|
||||
-- 4. 确认唯一键无重复
|
||||
SELECT site_id, member_id, time_window, category_code, COUNT(*)
|
||||
FROM dws.dws_member_project_tag
|
||||
GROUP BY site_id, member_id, time_window, category_code
|
||||
HAVING COUNT(*) > 1;
|
||||
-- 期望:0 行
|
||||
```
|
||||
|
||||
## 回滚策略
|
||||
|
||||
```sql
|
||||
-- 删除表(不影响其他表)
|
||||
DROP TABLE IF EXISTS dws.dws_member_project_tag CASCADE;
|
||||
-- 从 task_registry.py 移除 DWS_MEMBER_PROJECT_TAG 注册
|
||||
-- 从 maintenance_task.py DEFAULT_RETENTION_TABLES 移除对应条目
|
||||
```
|
||||
@@ -35,13 +35,14 @@
|
||||
| 15 | table_fee | NUMERIC(12,2) | NO | 台费 |
|
||||
| 16 | goods_amount | NUMERIC(12,2) | NO | 商品金额 |
|
||||
| 17 | assistant_amount | NUMERIC(12,2) | NO | 助教服务金额 |
|
||||
| 18 | total_consume | NUMERIC(12,2) | NO | 消费总额(正价) |
|
||||
| 18 | total_consume | NUMERIC(12,2) | NO | 消费总额(基于 `items_sum` 口径,= tc + goods + pd + cx + electricity) |
|
||||
| 19 | total_discount | NUMERIC(12,2) | NO | 优惠总额 |
|
||||
| 20 | actual_pay | NUMERIC(12,2) | NO | 实付金额 |
|
||||
| 21 | cash_pay | NUMERIC(12,2) | NO | 现金/刷卡支付 |
|
||||
| 22 | cash_card_pay | NUMERIC(12,2) | NO | 储值卡支付 |
|
||||
| 21 | cash_pay | NUMERIC(12,2) | NO | 收银实付(= `pay_amount`,与 actual_pay 同值) |
|
||||
| 22 | balance_pay | NUMERIC(12,2) | NO | 储值卡总支付(= recharge_card_pay + gift_card_pay) |
|
||||
| 22a | recharge_card_pay | NUMERIC(12,2) | NO | 现金充值卡支付(balance_pay 的子项) |
|
||||
| 23 | gift_card_pay | NUMERIC(12,2) | NO | 赠送卡支付 |
|
||||
| 24 | groupbuy_pay | NUMERIC(12,2) | NO | 团购券支付 |
|
||||
| 24 | groupbuy_pay | NUMERIC(12,2) | NO | 团购抵消台费金额(= `coupon_amount`) |
|
||||
| 25 | table_duration_min | INTEGER | NO | 台桌使用时长(分钟,来自台费流水真实秒数) |
|
||||
| 26 | assistant_duration_min | INTEGER | NO | 助教服务时长(分钟) |
|
||||
| 27 | assistant_services | JSONB | YES | 助教服务列表 |
|
||||
@@ -51,28 +52,36 @@
|
||||
## 数据来源
|
||||
|
||||
### 主表来源:dwd_settlement_head
|
||||
|
||||
> ⚠️ `total_consume` 使用 `items_sum` 口径(全时期一致),不使用 `consume_money`(存在三种历史口径混合)。
|
||||
|
||||
```sql
|
||||
SELECT
|
||||
site_id,
|
||||
tenant_id,
|
||||
member_id,
|
||||
order_settle_id,
|
||||
DATE(pay_time) AS visit_date,
|
||||
create_time AS visit_time,
|
||||
member_name AS member_nickname,
|
||||
member_phone AS member_mobile,
|
||||
table_id,
|
||||
table_charge_money AS table_fee,
|
||||
goods_money AS goods_amount,
|
||||
assistant_pd_money + assistant_cx_money AS assistant_amount,
|
||||
consume_money AS total_consume,
|
||||
member_discount_amount + adjust_amount + rounding_amount AS total_discount,
|
||||
pay_amount AS actual_pay,
|
||||
balance_amount AS cash_card_pay,
|
||||
gift_card_amount AS gift_card_pay
|
||||
FROM dwd.dwd_settlement_head
|
||||
WHERE member_id != 0
|
||||
AND settle_type = 1;
|
||||
sh.site_id,
|
||||
sh.tenant_id,
|
||||
sh.member_id,
|
||||
sh.order_settle_id,
|
||||
DATE(sh.pay_time) AS visit_date,
|
||||
sh.create_time AS visit_time,
|
||||
-- ⚠️ member_nickname/member_mobile 实际从 dim_member 关联获取(nickname/mobile),非结算头表字段
|
||||
dm.nickname AS member_nickname,
|
||||
dm.mobile AS member_mobile,
|
||||
sh.table_id,
|
||||
sh.table_charge_money AS table_fee,
|
||||
sh.goods_money AS goods_amount,
|
||||
sh.assistant_pd_money + sh.assistant_cx_money AS assistant_amount,
|
||||
-- ✅ 使用 items_sum 口径,不使用 consume_money
|
||||
sh.table_charge_money + sh.goods_money + sh.assistant_pd_money
|
||||
+ sh.assistant_cx_money + sh.electricity_money AS total_consume,
|
||||
sh.member_discount_amount + sh.adjust_amount + sh.rounding_amount AS total_discount,
|
||||
sh.pay_amount AS actual_pay,
|
||||
sh.balance_amount AS balance_pay,
|
||||
sh.recharge_card_amount AS recharge_card_pay,
|
||||
sh.gift_card_amount AS gift_card_pay
|
||||
FROM dwd.dwd_settlement_head sh
|
||||
JOIN dwd.dim_member dm ON sh.tenant_member_id = dm.tenant_member_id AND dm.scd2_is_current = 1
|
||||
WHERE sh.member_id IS NOT NULL AND sh.member_id != 0
|
||||
AND sh.settle_type IN (1, 3); -- 仅台桌结账+商城订单,排除退货(6)/退款(7)
|
||||
```
|
||||
|
||||
### 助教服务明细:dwd_assistant_service_log
|
||||
@@ -127,4 +136,4 @@ area_category = COALESCE(
|
||||
|------|------|
|
||||
| 可回溯 | ✅ 完全可回溯 |
|
||||
| 数据范围 | 2025-07-16 ~ 至今 |
|
||||
| 依赖表 | dwd_settlement_head, dwd_assistant_service_log, dwd_table_fee_log, dim_table, dim_member |
|
||||
| 依赖表 | dwd_settlement_head, dwd_assistant_service_log, dwd_table_fee_log, dim_table, dim_member, cfg_area_category |
|
||||
|
||||
@@ -23,20 +23,20 @@
|
||||
| 4 | order_date | DATE | NO | 订单日期(优先 pay_time,其次 create_time) |
|
||||
| 5 | tenant_id | BIGINT | NO | 租户ID |
|
||||
| 6 | member_id | BIGINT | YES | 会员ID(NULL 或 0 为散客) |
|
||||
| 7 | member_flag | BOOLEAN | NO | 是否会员订单 |
|
||||
| 8 | recharge_order_flag | BOOLEAN | NO | 充值订单标记(消费金额=0 且实付>0) |
|
||||
| 7 | member_flag | BOOLEAN | NO | 是否会员订单(来源:`is_bind_member`) |
|
||||
| 8 | recharge_order_flag | BOOLEAN | NO | 充值订单标记(`consume_money = 0` 且实付>0,此处 consume_money 仅用于零值判断,不参与金额计算) |
|
||||
| 9 | item_count | INTEGER | NO | 订单项数 |
|
||||
| 10 | total_item_quantity | INTEGER | NO | 订单项总数量 |
|
||||
| 11 | table_fee_amount | NUMERIC | NO | 台费金额 |
|
||||
| 12 | assistant_service_amount | NUMERIC | NO | 助教服务金额 |
|
||||
| 12 | assistant_service_amount | NUMERIC | NO | 助教服务金额(= `assistant_pd_money` + `assistant_cx_money`) |
|
||||
| 13 | goods_amount | NUMERIC | NO | 商品金额 |
|
||||
| 14 | group_amount | NUMERIC | NO | 团购金额 |
|
||||
| 15 | total_coupon_deduction | NUMERIC | NO | 优惠券抵扣总额 |
|
||||
| 16 | member_discount_amount | NUMERIC | NO | 会员折扣金额 |
|
||||
| 17 | manual_discount_amount | NUMERIC | NO | 手动折扣金额 |
|
||||
| 18 | order_original_amount | NUMERIC | NO | 原价估算(实付+优惠/抵扣) |
|
||||
| 18 | order_original_amount | NUMERIC | NO | 原价估算(= `total_paid_amount + total_coupon_deduction + member_discount_amount + manual_discount_amount`) |
|
||||
| 19 | order_final_amount | NUMERIC | NO | 最终应付金额 |
|
||||
| 20 | stored_card_deduct | NUMERIC | NO | 储值卡抵扣金额 |
|
||||
| 20 | stored_card_deduct | NUMERIC | NO | 储值卡抵扣金额(= `balance_amount`,即 `recharge_card_amount + gift_card_amount`) |
|
||||
| 21 | external_paid_amount | NUMERIC | NO | 外部支付金额(实付-卡类抵扣) |
|
||||
| 22 | total_paid_amount | NUMERIC | NO | 总实付金额 |
|
||||
| 23 | book_table_flow | NUMERIC | NO | 台费流水 |
|
||||
@@ -44,9 +44,9 @@
|
||||
| 25 | book_goods_flow | NUMERIC | NO | 商品流水 |
|
||||
| 26 | book_group_flow | NUMERIC | NO | 团购流水 |
|
||||
| 27 | book_order_flow | NUMERIC | NO | 订单总流水(台费+助教+商品+团购) |
|
||||
| 28 | order_effective_consume_cash | NUMERIC | NO | 有效消费现金 |
|
||||
| 29 | order_effective_recharge_cash | NUMERIC | NO | 有效充值现金 |
|
||||
| 30 | order_effective_flow | NUMERIC | NO | 有效流水 |
|
||||
| 28 | order_effective_consume_cash | NUMERIC | NO | 有效消费现金(= `GREATEST(total_paid_amount - stored_card_deduct, 0)`,即外部支付金额) |
|
||||
| 29 | order_effective_recharge_cash | NUMERIC | NO | 有效充值现金(当前硬编码为 0,占位字段,待后续实现) |
|
||||
| 30 | order_effective_flow | NUMERIC | NO | 有效流水(当前 = `total_paid_amount`) |
|
||||
| 31 | refund_amount | NUMERIC | NO | 退款金额 |
|
||||
| 32 | net_income | NUMERIC | NO | 净收入(实付-退款) |
|
||||
| 33 | created_at | TIMESTAMPTZ | NO | 创建时间 |
|
||||
@@ -54,10 +54,18 @@
|
||||
|
||||
## 业务口径
|
||||
|
||||
> ⚠️ 本表金额字段基于 `items_sum` 各分项(`table_charge_money + goods_money + assistant_pd_money + assistant_cx_money + electricity_money`),
|
||||
> 不使用 `consume_money`(存在三种历史口径混合)。
|
||||
|
||||
- order_date 优先取 pay_time,其次 create_time
|
||||
- recharge_order_flag:消费金额=0 且实付>0 时标记为充值订单
|
||||
- order_original_amount = 实付 + 优惠/抵扣
|
||||
- recharge_order_flag:`consume_money = 0` 且实付>0 时标记为充值订单(此处 consume_money 仅用于零值判断,不参与金额计算)
|
||||
- stored_card_deduct = `balance_amount`(恒等式:`balance_amount = recharge_card_amount + gift_card_amount`,三者不可相加)
|
||||
- order_original_amount = `total_paid_amount + total_coupon_deduction + member_discount_amount + manual_discount_amount`(实付 + 团购抵扣 + 会员折扣 + 手动调整)
|
||||
- external_paid_amount = total_paid_amount - stored_card_deduct(外部支付 = 实付 - 储值卡抵扣)
|
||||
- book_order_flow = 台费 + 助教 + 商品 + 团购
|
||||
- order_effective_recharge_cash:当前硬编码为 0,占位字段
|
||||
- order_effective_consume_cash = `GREATEST(total_paid_amount - stored_card_deduct, 0)`(与 external_paid_amount 同值)
|
||||
- order_effective_flow = `total_paid_amount`(当前实现)
|
||||
- net_income = total_paid_amount - refund_amount
|
||||
|
||||
## 使用说明
|
||||
@@ -81,4 +89,4 @@ ORDER BY order_date DESC;
|
||||
| 项目 | 说明 |
|
||||
|------|------|
|
||||
| 可回溯 | ✅ 完全可回溯 |
|
||||
| 依赖表 | dwd_settlement_head, dwd_table_fee_log, dwd_assistant_service_log, dwd_store_goods_sale, dwd_groupbuy_redemption, dwd_payment, dwd_refund |
|
||||
| 依赖表 | dwd_settlement_head, dwd_table_fee_log, dwd_assistant_service_log, dwd_store_goods_sale, dwd_groupbuy_redemption, dwd_refund, dwd_refund_ex |
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
# group_buy_package_details 团购套餐详情
|
||||
|
||||
> 生成时间:2026-03-05
|
||||
|
||||
## 表信息
|
||||
|
||||
| 属性 | 值 |
|
||||
|------|-----|
|
||||
| Schema | ods |
|
||||
| 表名 | group_buy_package_details |
|
||||
| 主键 | coupon_id |
|
||||
| 数据来源 | `QueryPackageCouponInfo` 详情接口(二级拉取) |
|
||||
| DDL 路径 | `db/etl_feiqiu/ods/group_buy_package_details.sql` |
|
||||
| 说明 | 团购套餐详情 ODS 层,存储每个 couponId 的详情原始数据 |
|
||||
|
||||
## 数据获取方式
|
||||
|
||||
本表数据通过 `ODS_GROUP_PACKAGE` 任务的 **detail_endpoint 二级详情拉取** 子流程获取:
|
||||
1. 主流程先从 `QueryPackageCouponList` 拉取团购列表写入 `ods.group_buy_packages`
|
||||
2. 子流程遍历列表中每个 `id`,串行调用 `QueryPackageCouponInfo` 获取详情
|
||||
3. 详情数据写入本表,采用全量快照模式(`SnapshotMode.FULL_TABLE`),UPSERT on `coupon_id`
|
||||
|
||||
## 字段说明
|
||||
|
||||
| 序号 | 字段名 | 类型 | 可空 | 说明 |
|
||||
|------|--------|------|------|------|
|
||||
| 1 | coupon_id | BIGINT | NO(PK) | 团购套餐 ID(= groupPurchasePackage.id) |
|
||||
| 2 | package_name | TEXT | YES | 团购套餐名称 |
|
||||
| 3 | duration | INTEGER | YES | 台费计时时长(秒) |
|
||||
| 4 | start_time | TIMESTAMPTZ | YES | 可用日期开始 |
|
||||
| 5 | end_time | TIMESTAMPTZ | YES | 可用日期结束 |
|
||||
| 6 | add_start_clock | TEXT | YES | 可用时段开始(如 "00:00:00") |
|
||||
| 7 | add_end_clock | TEXT | YES | 可用时段结束(如 "1.00:00:00") |
|
||||
| 8 | is_enabled | INTEGER | YES | 是否启用(1=启用, 0=禁用) |
|
||||
| 9 | is_delete | INTEGER | YES | 是否已删除(1=已删除, 0=正常) |
|
||||
| 10 | site_id | BIGINT | YES | 店铺 ID |
|
||||
| 11 | tenant_id | BIGINT | YES | 租户 ID |
|
||||
| 12 | create_time | TIMESTAMPTZ | YES | 创建时间 |
|
||||
| 13 | creator_name | TEXT | YES | 创建人 |
|
||||
| 14 | table_area_ids | JSONB | YES | 可用台区 ID 列表(来自 groupPurchasePackage.tableAreaId) |
|
||||
| 15 | table_area_names | JSONB | YES | 可用台区名称列表(来自 groupPurchasePackage.tableAreaNameList) |
|
||||
| 16 | assistant_services | JSONB | YES | 助教服务关联数组(来自 packageCouponAssistants) |
|
||||
| 17 | groupon_site_infos | JSONB | YES | 关联门店信息数组(来自 grouponSiteInfos) |
|
||||
| 18 | package_services | JSONB | YES | 套餐服务数组(来自 packagePackageService,待调研) |
|
||||
| 19 | coupon_details_list | JSONB | YES | 券明细数组(来自 packageCouponDetailsList,待调研) |
|
||||
| 20 | content_hash | TEXT | YES | 业务字段内容哈希,用于变更检测 |
|
||||
| 21 | payload | JSONB | YES | 详情接口完整原始 JSON 响应 |
|
||||
| 22 | fetched_at | TIMESTAMPTZ | YES | ETL 拉取时间戳 |
|
||||
|
||||
## 与列表表的关系
|
||||
|
||||
```
|
||||
ods.group_buy_packages (列表)
|
||||
└── ods.group_buy_package_details (详情)
|
||||
关联字段:group_buy_packages.id = group_buy_package_details.coupon_id
|
||||
关系:1:1(每个列表记录对应一条详情)
|
||||
```
|
||||
|
||||
## 下游消费
|
||||
|
||||
DWD 层 `dwd.dim_groupbuy_package_ex` 在加载时通过 LEFT JOIN 本表,将 `table_area_ids`、`table_area_names`、`assistant_services`、`groupon_site_infos` 四个 JSONB 字段合并到扩展表。
|
||||
|
||||
## 使用说明
|
||||
|
||||
```sql
|
||||
-- 查询最新入库的详情记录
|
||||
SELECT coupon_id, package_name, table_area_names, assistant_services
|
||||
FROM ods.group_buy_package_details
|
||||
ORDER BY fetched_at DESC
|
||||
LIMIT 10;
|
||||
```
|
||||
|
||||
```sql
|
||||
-- 关联列表表查看完整信息
|
||||
SELECT p.id, p.package_name, p.selling_price,
|
||||
d.table_area_names, d.assistant_services, d.groupon_site_infos
|
||||
FROM ods.group_buy_packages p
|
||||
LEFT JOIN ods.group_buy_package_details d ON p.id = d.coupon_id
|
||||
WHERE p.is_delete IS DISTINCT FROM 1;
|
||||
```
|
||||
|
||||
## 可回溯性
|
||||
|
||||
| 项目 | 说明 |
|
||||
|------|------|
|
||||
| 可回溯 | ✅ 完全可回溯(保留 payload 原始 JSON) |
|
||||
| 数据来源 | `PackageCoupon/QueryPackageCouponInfo` API |
|
||||
@@ -0,0 +1,89 @@
|
||||
# 团购套餐详情(QueryPackageCouponInfo) → group_buy_package_details 字段映射
|
||||
|
||||
> 生成时间:2026-03-05
|
||||
|
||||
## 端点信息
|
||||
|
||||
| 属性 | 值 |
|
||||
|------|-----|
|
||||
| 接口路径 | `PackageCoupon/QueryPackageCouponInfo` |
|
||||
| 请求方法 | POST |
|
||||
| 请求参数 | `{ "couponId": <id> }`(从 `ods.group_buy_packages.id` 获取) |
|
||||
| ODS 对应表 | `ods.group_buy_package_details` |
|
||||
| JSON 数据路径 | `data` |
|
||||
| 调用方式 | 二级详情拉取(`ODS_GROUP_PACKAGE` 任务的 `detail_endpoint` 子流程) |
|
||||
|
||||
## 响应结构
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"groupPurchasePackage": {
|
||||
"id": 123,
|
||||
"packageName": "...",
|
||||
"duration": 3600,
|
||||
"startTime": "...",
|
||||
"endTime": "...",
|
||||
"addStartClock": "00:00:00",
|
||||
"addEndClock": "1.00:00:00",
|
||||
"isEnabled": 1,
|
||||
"isDelete": 0,
|
||||
"siteId": 456,
|
||||
"tenantId": 789,
|
||||
"createTime": "...",
|
||||
"creatorName": "...",
|
||||
"tableAreaId": [1, 2, 3],
|
||||
"tableAreaNameList": ["A区", "B区"]
|
||||
},
|
||||
"packageCouponAssistants": [...],
|
||||
"grouponSiteInfos": [...],
|
||||
"packagePackageService": [...],
|
||||
"packageCouponDetailsList": [...]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 字段映射
|
||||
|
||||
### 结构化字段(来自 data.groupPurchasePackage)
|
||||
|
||||
| JSON 路径 | ODS 列名 | 类型转换 | 说明 |
|
||||
|-----------|----------|----------|------|
|
||||
| data.groupPurchasePackage.id | coupon_id | int→BIGINT | 团购套餐 ID,主键 |
|
||||
| data.groupPurchasePackage.packageName | package_name | string→TEXT | 套餐名称 |
|
||||
| data.groupPurchasePackage.duration | duration | int→INTEGER | 台费计时时长(秒) |
|
||||
| data.groupPurchasePackage.startTime | start_time | string→TIMESTAMPTZ | 可用日期开始 |
|
||||
| data.groupPurchasePackage.endTime | end_time | string→TIMESTAMPTZ | 可用日期结束 |
|
||||
| data.groupPurchasePackage.addStartClock | add_start_clock | string→TEXT | 可用时段开始 |
|
||||
| data.groupPurchasePackage.addEndClock | add_end_clock | string→TEXT | 可用时段结束 |
|
||||
| data.groupPurchasePackage.isEnabled | is_enabled | int→INTEGER | 是否启用 |
|
||||
| data.groupPurchasePackage.isDelete | is_delete | int→INTEGER | 是否已删除 |
|
||||
| data.groupPurchasePackage.siteId | site_id | int→BIGINT | 店铺 ID |
|
||||
| data.groupPurchasePackage.tenantId | tenant_id | int→BIGINT | 租户 ID |
|
||||
| data.groupPurchasePackage.createTime | create_time | string→TIMESTAMPTZ | 创建时间 |
|
||||
| data.groupPurchasePackage.creatorName | creator_name | string→TEXT | 创建人 |
|
||||
|
||||
### JSONB 数组字段
|
||||
|
||||
| JSON 路径 | ODS 列名 | 类型转换 | 说明 |
|
||||
|-----------|----------|----------|------|
|
||||
| data.groupPurchasePackage.tableAreaId | table_area_ids | array→JSONB | 可用台区 ID 列表 |
|
||||
| data.groupPurchasePackage.tableAreaNameList | table_area_names | array→JSONB | 可用台区名称列表 |
|
||||
| data.packageCouponAssistants | assistant_services | array→JSONB | 助教服务关联(含 skillId/assistantLevel/assistantDuration) |
|
||||
| data.grouponSiteInfos | groupon_site_infos | array→JSONB | 关联门店信息(含 siteId/siteName) |
|
||||
| data.packagePackageService | package_services | array→JSONB | 套餐服务数组(待调研,可能为空) |
|
||||
| data.packageCouponDetailsList | coupon_details_list | array→JSONB | 券明细数组(待调研,可能为空) |
|
||||
|
||||
## ETL 补充字段
|
||||
|
||||
| ODS 列名 | 生成逻辑 |
|
||||
|-----------|----------|
|
||||
| content_hash | 基于原始 payload + is_delete 计算 SHA-256 |
|
||||
| payload | 完整原始 JSON 响应(`data` 节点) |
|
||||
| fetched_at | ETL 拉取时间戳(`DEFAULT now()`) |
|
||||
|
||||
## 写入策略
|
||||
|
||||
- 全量快照模式(`SnapshotMode.FULL_TABLE`)
|
||||
- UPSERT on `coupon_id`,每次运行覆盖全部记录
|
||||
- 通过 `content_hash` 去重,内容未变则跳过写入
|
||||
@@ -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) |
|
||||
|
||||
@@ -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 DROP,2026-03-01 清理残留)~~ |
|
||||
| ~~`dwd.dwd_assistant_trash_event_ex`~~ | ~~`ods.assistant_cancellation_records`~~ | ~~助教取消扩展(2026-02-22 DROP,2026-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` 是其分账明细,三者不可重复计算。
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -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` 处理
|
||||
|
||||
@@ -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` | — |
|
||||
|
||||
@@ -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`(去重开启)。
|
||||
|
||||
@@ -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` |
|
||||
|
||||
Reference in New Issue
Block a user