10 KiB
BD_Manual:dws_assistant_order_contribution(助教订单流水四项统计)
DWS 表:
dws.dws_assistant_order_contributionDWD 数据源:dwd.dwd_settlement_head(结算主表)、dwd.dwd_table_fee_log(台费明细)、dwd.dwd_assistant_service_log(助教服务记录) 任务代码:DWS_ASSISTANT_ORDER_CONTRIBUTION代码位置:apps/etl/connectors/feiqiu/tasks/dws/assistant_order_contribution_task.pyDDL 位置:docs/database/ddl/etl_feiqiu__dws.sql迁移脚本:db/etl_feiqiu/migrations/2025-02-24__create_dws_assistant_order_contribution.sqlRLS 视图:db/etl_feiqiu/migrations/2025-02-24__create_rls_view_assistant_order_contribution.sqlFDW 映射:db/zqyy_app/migrations/2025-02-24__add_fdw_dws_extensions.sql
1. 表结构
| 列名 | 类型 | 默认值 | 业务含义 | 取值范围/示例 |
|---|---|---|---|---|
contribution_id |
BIGINT (SERIAL) | nextval 序列 | 自增主键(PK) | 自增 |
site_id |
BIGINT NOT NULL | — | 门店 ID | 飞球门店 ID |
tenant_id |
BIGINT NOT NULL | — | 租户 ID | 飞球租户 ID |
assistant_id |
BIGINT NOT NULL | — | 助教 ID | 飞球助教 ID |
assistant_nickname |
VARCHAR(100) | NULL | 助教昵称 | 中文昵称 |
stat_date |
DATE NOT NULL | — | 统计日期 | 2025-01-15 |
order_gross_revenue |
NUMERIC(14,2) | 0 | 订单总流水 = 台费 + 酒水食品 + 所有助教服务费 | 0.00 ~ 金额值 |
order_net_revenue |
NUMERIC(14,2) | 0 | 订单净流水 = 订单总流水 - 所有助教服务分成 | 0.00 ~ 金额值 |
time_weighted_revenue |
NUMERIC(14,2) | 0 | 时效贡献流水 = 台费按时长分摊 + 个人服务费 + 酒水食品按时长比例 | 0.00 ~ 金额值 |
time_weighted_net_revenue |
NUMERIC(14,2) | 0 | 时效净贡献 = 时效贡献流水 - 个人服务分成 | 0.00 ~ 金额值 |
order_count |
INTEGER | 0 | 当日参与订单数 | 0 ~ 正整数 |
total_service_seconds |
INTEGER | 0 | 当日总服务时长(秒) | 0 ~ 正整数 |
created_at |
TIMESTAMPTZ | NOW() | 记录创建时间 | ISO 时间戳 |
updated_at |
TIMESTAMPTZ | NOW() | 记录最后更新时间 | ISO 时间戳 |
2. 主键与索引
| 名称 | 类型 | 列 | 说明 |
|---|---|---|---|
dws_assistant_order_contribution_pkey |
PRIMARY KEY | contribution_id |
物理主键(自增序列) |
idx_aoc_site_assistant_date |
UNIQUE INDEX | (site_id, assistant_id, stat_date) |
业务主键:每个门店每个助教每天唯一一条记录 |
idx_aoc_stat_date |
INDEX | (site_id, stat_date) |
按门店+日期查询,支持日度报表 |
3. 数据写入策略
- delete-before-insert:每次执行按
site_id+ 日期窗口全量刷新DELETE FROM dws.dws_assistant_order_contribution WHERE site_id = %s AND stat_date BETWEEN %s AND %s- 批量
INSERT新计算结果
- 继承
BaseDwsTask默认 load 实现,幂等可重跑
4. 算法概要
4.1 数据来源
| 来源表 | 筛选条件 | 提取内容 |
|---|---|---|
dwd.dwd_settlement_head |
日期窗口内,settle_type IN (1, 3) |
结算单信息、酒水食品金额 |
dwd.dwd_table_fee_log |
关联结算单 | 台桌使用时长、台费金额、区域 |
dwd.dwd_assistant_service_log |
关联结算单 | 助教服务时长、服务流水、分成、课程类型 |
4.2 四项统计公式
订单总流水(order_gross_revenue)
order_gross_revenue = total_table_fee + total_goods_amount + SUM(所有助教 ledger_amount)
每个参与助教获得相同值。
订单净流水(order_net_revenue)
order_net_revenue = order_gross_revenue - SUM(所有助教 commission)
每个参与助教获得相同值。
时效贡献流水(time_weighted_revenue)
对于台桌 t:
billable_seconds = MAX(SUM(助教服务时长), 台桌使用时长)
台费分摊_a = table_fee_t × (service_seconds_a / billable_seconds)
酒水食品分摊_a = total_goods_amount × (助教 a 总服务时长 / 所有助教总服务时长)
time_weighted_revenue_a = SUM(各台桌台费分摊_a) + ledger_amount_a + 酒水食品分摊_a
时效净贡献(time_weighted_net_revenue)
time_weighted_net_revenue_a = time_weighted_revenue_a - commission_a
4.3 超休/打赏课特殊处理
当 course_type = BONUS 时,四项统计均等于个人服务流水和分成,不参与订单级分摊。
5. 前置依赖
- 任务依赖:
DWD_LOAD_FROM_ODS(需先完成 DWD 层数据加载) - 数据源表:
dwd.dwd_settlement_head、dwd.dwd_table_fee_log、dwd.dwd_assistant_service_log必须已有数据
6. 验证 SQL
6.1 检查表是否存在且有数据
SELECT
COUNT(*) AS total_rows,
COUNT(DISTINCT site_id) AS site_count,
COUNT(DISTINCT assistant_id) AS assistant_count,
MIN(stat_date) AS earliest_date,
MAX(stat_date) AS latest_date
FROM dws.dws_assistant_order_contribution;
6.2 检查业务主键唯一性(不应有重复)
SELECT site_id, assistant_id, stat_date, COUNT(*) AS cnt
FROM dws.dws_assistant_order_contribution
GROUP BY site_id, assistant_id, stat_date
HAVING COUNT(*) > 1;
-- 预期:无结果返回
6.3 检查四项统计数值合理性(非负)
SELECT
COUNT(*) FILTER (WHERE order_gross_revenue < 0) AS neg_gross,
COUNT(*) FILTER (WHERE order_net_revenue < 0) AS neg_net,
COUNT(*) FILTER (WHERE time_weighted_revenue < 0) AS neg_twr,
COUNT(*) FILTER (WHERE time_weighted_net_revenue < 0) AS neg_twnr
FROM dws.dws_assistant_order_contribution;
-- 预期:所有列均为 0
6.4 按门店查看统计概况
SELECT
site_id,
COUNT(*) AS record_count,
SUM(order_count) AS total_orders,
ROUND(AVG(order_gross_revenue), 2) AS avg_gross,
ROUND(AVG(order_net_revenue), 2) AS avg_net,
ROUND(AVG(time_weighted_revenue), 2) AS avg_twr,
ROUND(AVG(time_weighted_net_revenue), 2) AS avg_twnr
FROM dws.dws_assistant_order_contribution
GROUP BY site_id
ORDER BY site_id;
7. RLS 视图与 FDW 映射
7.1 RLS 视图(ETL 库 app schema)
-- 视图名:app.v_dws_assistant_order_contribution
CREATE OR REPLACE VIEW app.v_dws_assistant_order_contribution AS
SELECT * FROM dws.dws_assistant_order_contribution
WHERE site_id = current_setting('app.current_site_id')::bigint;
7.2 FDW 外部表(业务库 fdw_etl schema)
-- 外部表名:fdw_etl.v_dws_assistant_order_contribution
-- 通过 app schema RLS 视图访问,非直接访问 dws schema
CREATE FOREIGN TABLE fdw_etl.v_dws_assistant_order_contribution (...)
SERVER etl_server
OPTIONS (schema_name 'app', table_name 'v_dws_assistant_order_contribution');
8. 兼容性说明
| 影响范围 | 说明 |
|---|---|
| ETL 任务 | 新增任务 DWS_ASSISTANT_ORDER_CONTRIBUTION,依赖 DWD_LOAD_FROM_ODS。不影响现有 DWS 任务 |
| 后端 API | 当前无 API 直接读取此表。后续小程序助教看板需新增接口 |
| 管理后台 | 当前无前端页面展示。后续可在助教详情页新增流水统计展示 |
| 小程序 | 小程序助教端将通过后端 API 读取此表数据展示四项统计 |
| 其他 DWS 表 | 独立于现有 dws_assistant_daily_detail,不修改任何已有表或任务逻辑 |
9. 回滚策略
9.1 删除数据(保留表结构)
DELETE FROM dws.dws_assistant_order_contribution;
9.2 完整回滚(删除表 + 视图 + FDW)
-- 1. 删除 FDW 外部表(业务库)
DROP FOREIGN TABLE IF EXISTS fdw_etl.v_dws_assistant_order_contribution;
-- 2. 删除 RLS 视图(ETL 库)
DROP VIEW IF EXISTS app.v_dws_assistant_order_contribution;
-- 3. 删除表和索引(ETL 库)
DROP INDEX IF EXISTS dws.idx_aoc_stat_date;
DROP INDEX IF EXISTS dws.idx_aoc_site_assistant_date;
DROP TABLE IF EXISTS dws.dws_assistant_order_contribution;
9.3 回滚任务注册
从 orchestration/task_registry.py 中移除 DWS_ASSISTANT_ORDER_CONTRIBUTION 注册行,并从 tasks/dws/__init__.py 中移除 AssistantOrderContributionTask 导出。
10. 代码引用
- 任务类:
tasks/dws/assistant_order_contribution_task.py→AssistantOrderContributionTask - 数据结构:
TableUsage、AssistantService、OrderData(同文件) - 继承:
BaseDwsTask - 任务注册:
orchestration/task_registry.py→DWS_ASSISTANT_ORDER_CONTRIBUTION - 属性测试:
tests/test_dws_contribution_properties.py - 迁移脚本:
db/etl_feiqiu/migrations/2025-02-24__create_dws_assistant_order_contribution.sql - RLS 视图:
db/etl_feiqiu/migrations/2025-02-24__create_rls_view_assistant_order_contribution.sql - FDW 映射:
db/zqyy_app/migrations/2025-02-24__add_fdw_dws_extensions.sql - 验证脚本:
apps/etl/connectors/feiqiu/scripts/verify_dws_extensions.py
11. 关联扩展字段说明
本次 Spec(02-etl-dws-miniapp-extensions)同时扩展了两张已有表的字段,简要说明如下:
11.1 dws_member_consumption_summary 新增字段
| 列名 | 类型 | 默认值 | 业务含义 |
|---|---|---|---|
recharge_count_30d |
INTEGER | 0 | 近 30 天充值次数 |
recharge_count_60d |
INTEGER | 0 | 近 60 天充值次数 |
recharge_count_90d |
INTEGER | 0 | 近 90 天充值次数 |
recharge_amount_30d |
NUMERIC(14,2) | 0 | 近 30 天充值金额 |
recharge_amount_60d |
NUMERIC(14,2) | 0 | 近 60 天充值金额 |
recharge_amount_90d |
NUMERIC(14,2) | 0 | 近 90 天充值金额 |
avg_ticket_amount |
NUMERIC(14,2) | 0 | 次均消费 = total_consume_amount / MAX(total_visit_count, 1) |
迁移脚本:db/etl_feiqiu/migrations/2025-02-24__alter_member_consumption_add_recharge_fields.sql
11.2 dws_assistant_daily_detail 新增字段
| 列名 | 类型 | 默认值 | 业务含义 |
|---|---|---|---|
penalty_minutes |
NUMERIC(10,2) | 0 | 定档折算惩罚分钟数,无惩罚时为 0 |
penalty_reason |
TEXT | NULL | 惩罚原因描述,无惩罚时为 NULL |
is_exempt |
BOOLEAN | FALSE | 是否豁免惩罚 |
per_hour_contribution |
NUMERIC(14,2) | NULL | 单人每小时贡献流水 = 台费每小时单价 / 助教人数 |
迁移脚本:db/etl_feiqiu/migrations/2025-02-24__alter_assistant_daily_add_penalty_fields.sql