Compare commits
5 Commits
076f5755ca
...
dev
| Author | SHA1 | Date | |
|---|---|---|---|
| a3f4d04335 | |||
| ee773a9b52 | |||
| 15948cbd64 | |||
| 294c6edbc9 | |||
| 9b2c2c5c78 |
13
.config/dotnet-tools.json
Normal file
13
.config/dotnet-tools.json
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"isRoot": true,
|
||||||
|
"tools": {
|
||||||
|
"csharpier": {
|
||||||
|
"version": "1.2.5",
|
||||||
|
"commands": [
|
||||||
|
"csharpier"
|
||||||
|
],
|
||||||
|
"rollForward": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
157
README.md
157
README.md
@@ -78,6 +78,46 @@ python -m cli.main \
|
|||||||
- `FETCH_ONLY`:仅在线抓取落盘,不入库
|
- `FETCH_ONLY`:仅在线抓取落盘,不入库
|
||||||
- `INGEST_ONLY`:仅从本地 JSON 回放入库(适合离线回放/补跑)
|
- `INGEST_ONLY`:仅从本地 JSON 回放入库(适合离线回放/补跑)
|
||||||
|
|
||||||
|
## DWS 层(汇总/财务)
|
||||||
|
|
||||||
|
### 建表与初始化
|
||||||
|
- 建表:`INIT_DWS_SCHEMA`
|
||||||
|
- 配置:`SEED_DWS_CONFIG`
|
||||||
|
- 订单汇总(可选):`DWS_BUILD_ORDER_SUMMARY`
|
||||||
|
|
||||||
|
### 任务与调度建议
|
||||||
|
- **每小时**:`DWS_ASSISTANT_DAILY`、`DWS_FINANCE_DAILY`、`DWS_FINANCE_INCOME_STRUCTURE`
|
||||||
|
- **每日**:`DWS_ASSISTANT_MONTHLY`、`DWS_ASSISTANT_CUSTOMER`、`DWS_MEMBER_CONSUMPTION`、`DWS_MEMBER_VISIT`、`DWS_FINANCE_DISCOUNT_DETAIL`、`DWS_FINANCE_RECHARGE`、`DWS_ASSISTANT_FINANCE`
|
||||||
|
- **每月(月初)**:`DWS_ASSISTANT_SALARY`
|
||||||
|
- **维护清理(按需)**:`DWS_RETENTION_CLEANUP`
|
||||||
|
|
||||||
|
调度配置默认保存在 `etl_billiards/scheduled_tasks.json`,GUI 调度器会读取该文件。
|
||||||
|
|
||||||
|
### 时间口径
|
||||||
|
- 周起始日:周一
|
||||||
|
- 月/季度起始:第一天 0 点
|
||||||
|
- 环比:对比上一个等长区间
|
||||||
|
- 窗口类型:本周/上周/本月/上月/前3个月不含本月/前3个月含本月/本季度/上季度/最近半年不含本月
|
||||||
|
|
||||||
|
### Excel 导入(支出/平台回款/充值提成)
|
||||||
|
脚本:`etl_billiards/scripts/import_dws_excel.py`
|
||||||
|
- 支出结构:`--type expense`,按月导入(房租/水电/物业/工资/报销/平台服务费等)
|
||||||
|
- 平台回款:`--type platform`,按回款日期导入(回款金额、佣金、服务费、订单号等)
|
||||||
|
- 充值提成:`--type commission`,按月份导入(助教ID、提成金额、充值订单金额等)
|
||||||
|
|
||||||
|
### 大客户优惠拆分(可选)
|
||||||
|
用于将手动调整拆分为“大客户优惠/其他优惠”,可在配置中指定:
|
||||||
|
- `dws.discount.big_customer_member_ids`:会员ID列表(逗号分隔)
|
||||||
|
- `dws.discount.big_customer_order_ids`:订单ID列表(逗号分隔)
|
||||||
|
未配置时,大客户金额为 0,手动调整全部计入“其他优惠”。
|
||||||
|
|
||||||
|
### 时间分层清理(可选)
|
||||||
|
任务:`DWS_RETENTION_CLEANUP`,按配置清理历史数据
|
||||||
|
- `dws.retention.enabled`:是否启用
|
||||||
|
- `dws.retention.layer`:分层(如 `LAST_3_MONTHS`)
|
||||||
|
- `dws.retention.tables`:需要清理的表列表(逗号分隔)
|
||||||
|
- `dws.retention.table_layers`:表级分层覆盖(JSON 字符串)
|
||||||
|
|
||||||
## 目录结构与关键文件
|
## 目录结构与关键文件
|
||||||
- 仓库根目录:`etl_billiards/` 主代码;`app/` 示例 runner;`开发笔记/` 项目笔记;`tmp/` 草稿/调试归档;`requirements.txt`(仓库根)依赖;`run_etl.sh/.bat` 启动脚本。
|
- 仓库根目录:`etl_billiards/` 主代码;`app/` 示例 runner;`开发笔记/` 项目笔记;`tmp/` 草稿/调试归档;`requirements.txt`(仓库根)依赖;`run_etl.sh/.bat` 启动脚本。
|
||||||
- 注意:根目录的 `run_etl.sh/.bat` 运行时要求当前目录为 `etl_billiards/`(因为入口是 `python -m cli.main`)。
|
- 注意:根目录的 `run_etl.sh/.bat` 运行时要求当前目录为 `etl_billiards/`(因为入口是 `python -m cli.main`)。
|
||||||
@@ -367,6 +407,95 @@ python -m cli.main \
|
|||||||
6) 去嵌套:数组展开为子表/子行,重复 profile 提炼为维度。
|
6) 去嵌套:数组展开为子表/子行,重复 profile 提炼为维度。
|
||||||
7) 长期演进:优先加列/加表,减少对已有表结构的破坏。
|
7) 长期演进:优先加列/加表,减少对已有表结构的破坏。
|
||||||
|
|
||||||
|
## DWS 数据层(汇总层)
|
||||||
|
|
||||||
|
DWS(Data Warehouse Service)层基于 DWD 明细层数据构建,提供预聚合的数据服务。
|
||||||
|
|
||||||
|
### 表结构概览
|
||||||
|
|
||||||
|
| 分类 | 表数量 | 说明 |
|
||||||
|
|------|--------|------|
|
||||||
|
| 配置表 | 5 | 绩效档位、等级定价、奖金规则、区域分类、技能映射 |
|
||||||
|
| 助教维度 | 5 | 日度/月度业绩、客户统计、工资计算、充值提成 |
|
||||||
|
| 客户维度 | 2 | 消费汇总、来店明细 |
|
||||||
|
| 财务维度 | 7 | 日度汇总、收入结构、优惠明细、充值统计、支出、助教收支、平台结算 |
|
||||||
|
| 订单汇总 | 1 | 订单级别聚合 |
|
||||||
|
|
||||||
|
### 核心特性
|
||||||
|
|
||||||
|
- **时间分层**:支持近2天/近1月/近3月/全量的时间窗口筛选
|
||||||
|
- **滚动窗口**:支持7/10/15/30/60/90天的滚动统计
|
||||||
|
- **SCD2 as-of**:维度取值支持按时间点获取历史值(如助教等级)
|
||||||
|
- **幂等更新**:采用 delete-before-insert 策略,支持重复执行
|
||||||
|
- **Excel导入**:支出/平台结算/充值提成支持手动导入
|
||||||
|
|
||||||
|
### 助教工资计算
|
||||||
|
|
||||||
|
**绩效档位(6档 + 新入职)**
|
||||||
|
|
||||||
|
| 档位 | 业绩阈值 | 专业课抽成 | 打赏课抽成 | 休假 |
|
||||||
|
|------|----------|-----------|-----------|------|
|
||||||
|
| T0 | H < 100 | 28元/时 | 50% | 3天 |
|
||||||
|
| T1 | 100 ≤ H < 130 | 18元/时 | 40% | 4天 |
|
||||||
|
| T2 | 130 ≤ H < 160 | 15元/时 | 38% | 4天 |
|
||||||
|
| T3 | 160 ≤ H < 190 | 13元/时 | 35% | 5天 |
|
||||||
|
| T4 | 190 ≤ H < 220 | 10元/时 | 33% | 6天 |
|
||||||
|
| T5 | H ≥ 220 | 8元/时 | 30% | 休假自由 |
|
||||||
|
|
||||||
|
**工资计算公式**
|
||||||
|
|
||||||
|
```
|
||||||
|
基础课收入 = 基础课小时数 × (客户支付价格 - 专业课抽成)
|
||||||
|
附加课收入 = 附加课小时数 × 190 × (1 - 打赏课抽成比例)
|
||||||
|
应发工资 = 课时收入 + 奖金
|
||||||
|
```
|
||||||
|
|
||||||
|
**计算示例(中级助教185小时,3档)**
|
||||||
|
- 基础课170小时: 170 × (108 - 13) = 16,150元
|
||||||
|
- 附加课15小时: 15 × 190 × (1 - 0.35) = 1,852.5元
|
||||||
|
- 课时收入: 18,002.5元
|
||||||
|
|
||||||
|
**等级定价(客户支付价格)**
|
||||||
|
|
||||||
|
| 等级 | 基础课价格 | 附加课价格 |
|
||||||
|
|------|-----------|-----------|
|
||||||
|
| 初级 | 98元/时 | 190元/时 |
|
||||||
|
| 中级 | 108元/时 | 190元/时 |
|
||||||
|
| 高级 | 118元/时 | 190元/时 |
|
||||||
|
| 星级 | 138元/时 | 190元/时 |
|
||||||
|
|
||||||
|
### 运行 DWS 任务
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 初始化 DWS Schema
|
||||||
|
python -m cli.main --tasks INIT_DWS_SCHEMA
|
||||||
|
|
||||||
|
# 执行配置数据初始化
|
||||||
|
psql -f etl_billiards/database/seed_dws_config.sql
|
||||||
|
|
||||||
|
# 执行 DWS 订单汇总构建
|
||||||
|
python -m cli.main --tasks DWS_BUILD_ORDER_SUMMARY
|
||||||
|
```
|
||||||
|
|
||||||
|
### Excel 数据导入
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 导入支出数据
|
||||||
|
python etl_billiards/scripts/import_dws_excel.py --type expense --file expenses.xlsx
|
||||||
|
|
||||||
|
# 导入平台结算
|
||||||
|
python etl_billiards/scripts/import_dws_excel.py --type platform --file platform.xlsx
|
||||||
|
|
||||||
|
# 导入充值提成
|
||||||
|
python etl_billiards/scripts/import_dws_excel.py --type commission --file commission.xlsx
|
||||||
|
```
|
||||||
|
|
||||||
|
### 相关文档
|
||||||
|
|
||||||
|
- `etl_billiards/docs/dws_tables_dictionary.md`:DWS 数据字典
|
||||||
|
- `etl_billiards/database/schema_dws.sql`:DWS DDL
|
||||||
|
- `etl_billiards/database/seed_dws_config.sql`:配置初始数据
|
||||||
|
|
||||||
## 常用 CLI
|
## 常用 CLI
|
||||||
```bash
|
```bash
|
||||||
cd etl_billiards
|
cd etl_billiards
|
||||||
@@ -474,11 +603,37 @@ python scripts/test_db_connection.py --dsn "postgresql://user:pwd@host:5432/db"
|
|||||||
|
|
||||||
> 完整字段级映射见 `etl_billiards/docs/` 与 ODS/DWD DDL。
|
> 完整字段级映射见 `etl_billiards/docs/` 与 ODS/DWD DDL。
|
||||||
|
|
||||||
## 当前状态(2025-12-09)
|
## 当前状态(2026-02-02)
|
||||||
- 示例 JSON 已全量灌入,DWD 行数与 ODS 对齐。
|
- 示例 JSON 已全量灌入,DWD 行数与 ODS 对齐。
|
||||||
- 分类维度已展平大类+子类:`dim_goods_category` 26 行(category_level/leaf 已赋值)。
|
- 分类维度已展平大类+子类:`dim_goods_category` 26 行(category_level/leaf 已赋值)。
|
||||||
- 部分空字段源数据即为空,如需补值请先确认上游。
|
- 部分空字段源数据即为空,如需补值请先确认上游。
|
||||||
|
|
||||||
|
### 2026-02-02 更新:字段补全
|
||||||
|
|
||||||
|
本次更新完成了 API → ODS → DWD 全链路字段补全:
|
||||||
|
|
||||||
|
**ODS 新增字段**(16 张表,共 50+ 字段):
|
||||||
|
- `settlement_records`/`recharge_settlements`:电费相关字段(`electricitymoney`、`realelectricitymoney`、`electricityadjustmoney`)、券销售额、结算明细列表
|
||||||
|
- `table_fee_transactions`:活动折扣金额、订单消费类型、实际服务费
|
||||||
|
- `assistant_service_records`:助教团队名称、实际服务费
|
||||||
|
- `group_buy_redemption_records`:会员折扣、各类分摊金额(台费/商品/助教/充值)
|
||||||
|
- `table_fee_discount_records`:台区信息、台桌名称/价格、免费标记
|
||||||
|
- `member_stored_value_cards`:本金余额、会员等级、电费相关配置
|
||||||
|
- `member_profiles`:累计支付/充值金额、注册来源
|
||||||
|
- `member_balance_changes`:本金变动(前/后/数据)
|
||||||
|
- `group_buy_packages`:排序、首单限制、租户券销售订单项ID
|
||||||
|
- 其他:商品编码/停售、租户ID等
|
||||||
|
|
||||||
|
**DWD 新增字段**:
|
||||||
|
- 主表新增核心业务字段(金额、ID、状态)
|
||||||
|
- 扩展表新增配置/明细字段
|
||||||
|
|
||||||
|
**数据补全脚本**:
|
||||||
|
- `scripts/backfill_202507_to_now.bat`:从 2025-07-01 重新抓取并装载数据
|
||||||
|
|
||||||
|
**文档更新**:
|
||||||
|
- `etl_billiards/docs/bd_manual/` 下所有相关表文档已同步更新
|
||||||
|
|
||||||
## 可精简/归档
|
## 可精简/归档
|
||||||
- `tmp/`、`tmp/etl_billiards_misc/` 中草稿、旧备份、调试脚本仅供参考,不影响运行。
|
- `tmp/`、`tmp/etl_billiards_misc/` 中草稿、旧备份、调试脚本仅供参考,不影响运行。
|
||||||
- 根级保留必要文件(README、requirements、run_etl.*),其余临时文件按需归档至 `tmp/`。
|
- 根级保留必要文件(README、requirements、run_etl.*),其余临时文件按需归档至 `tmp/`。
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ SCHEMA_ETL=etl_admin
|
|||||||
# API 配置
|
# API 配置
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
API_BASE=https://pc.ficoo.vip/apiprod/admin/v1/
|
API_BASE=https://pc.ficoo.vip/apiprod/admin/v1/
|
||||||
API_TOKEN=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjbGllbnQtdHlwZSI6IjQiLCJ1c2VyLXR5cGUiOiIxIiwiaHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS93cy8yMDA4LzA2L2lkZW50aXR5L2NsYWltcy9yb2xlIjoiMTIiLCJyb2xlLWlkIjoiMTIiLCJ0ZW5hbnQtaWQiOiIyNzkwNjgzMTYwNzA5OTU3Iiwibmlja25hbWUiOiLnp5_miLfnrqHnkIblkZjvvJrmganmgakxIiwic2l0ZS1pZCI6IjAiLCJtb2JpbGUiOiIxMzgxMDUwMjMwNCIsInNpZCI6IjI5NTA0ODk2NTgzOTU4NDUiLCJzdGFmZi1pZCI6IjMwMDk5MTg2OTE1NTkwNDUiLCJvcmctaWQiOiIwIiwicm9sZS10eXBlIjoiMyIsInJlZnJlc2hUb2tlbiI6Ik1oKzFpTitjclRHMTY3cUp5SzFXYllteVBaaUhjdDI2ZTZDZkJvd1pxSVk9IiwicmVmcmVzaEV4cGlyeVRpbWUiOiIyMDI2LzIvNyDkuIvljYg5OjU2OjE4IiwibmVlZENoZWNrVG9rZW4iOiJmYWxzZSIsImV4cCI6MTc3MDQ3MjU3OCwiaXNzIjoidGVzdCIsImF1ZCI6IlVzZXIifQ.rY03o82SKznD7NOktXKzTOI1btl2FHsklMCChOlZUeY
|
API_TOKEN=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjbGllbnQtdHlwZSI6IjQiLCJ1c2VyLXR5cGUiOiIxIiwiaHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS93cy8yMDA4LzA2L2lkZW50aXR5L2NsYWltcy9yb2xlIjoiMTIiLCJyb2xlLWlkIjoiMTIiLCJ0ZW5hbnQtaWQiOiIyNzkwNjgzMTYwNzA5OTU3Iiwibmlja25hbWUiOiLnp5_miLfnrqHnkIblkZjvvJrmganmgakxIiwic2l0ZS1pZCI6IjAiLCJtb2JpbGUiOiIxMzgxMDUwMjMwNCIsInNpZCI6IjI5NTA0ODk2NTgzOTU4NDUiLCJzdGFmZi1pZCI6IjMwMDk5MTg2OTE1NTkwNDUiLCJvcmctaWQiOiIwIiwicm9sZS10eXBlIjoiMyIsInJlZnJlc2hUb2tlbiI6IjlES1lWcEVkYWw1bEc5cTMrdFptMkJXeTlyMkVMeEY5MHZuUWRyRnNYVFU9IiwicmVmcmVzaEV4cGlyeVRpbWUiOiIyMDI2LzIvOSDkuIrljYgyOjQzOjU0IiwibmVlZENoZWNrVG9rZW4iOiJmYWxzZSIsImV4cCI6MTc3MDU3NjIzNCwiaXNzIjoidGVzdCIsImF1ZCI6IlVzZXIifQ._1gnWcJHw8O26pcfiT1x8tgQRGn3g56vv2IZP8shgGU
|
||||||
|
|
||||||
# API 请求超时(秒)
|
# API 请求超时(秒)
|
||||||
API_TIMEOUT=20
|
API_TIMEOUT=20
|
||||||
|
|||||||
@@ -4,7 +4,9 @@ from __future__ import annotations
|
|||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
import time
|
||||||
from typing import Any, Iterable, Tuple
|
from typing import Any, Iterable, Tuple
|
||||||
|
from zoneinfo import ZoneInfo
|
||||||
|
|
||||||
from api.client import APIClient
|
from api.client import APIClient
|
||||||
from api.endpoint_routing import plan_calls
|
from api.endpoint_routing import plan_calls
|
||||||
@@ -128,3 +130,57 @@ class RecordingAPIClient:
|
|||||||
"pages": len(pages),
|
"pages": len(pages),
|
||||||
"records": total_records,
|
"records": total_records,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _cfg_get(cfg, key: str, default=None):
|
||||||
|
if isinstance(cfg, dict):
|
||||||
|
cur = cfg
|
||||||
|
for part in key.split("."):
|
||||||
|
if not isinstance(cur, dict) or part not in cur:
|
||||||
|
return default
|
||||||
|
cur = cur[part]
|
||||||
|
return cur
|
||||||
|
getter = getattr(cfg, "get", None)
|
||||||
|
if callable(getter):
|
||||||
|
return getter(key, default)
|
||||||
|
return default
|
||||||
|
|
||||||
|
|
||||||
|
def build_recording_client(
|
||||||
|
cfg,
|
||||||
|
*,
|
||||||
|
task_code: str,
|
||||||
|
output_dir: Path | str | None = None,
|
||||||
|
run_id: int | None = None,
|
||||||
|
write_pretty: bool | None = None,
|
||||||
|
):
|
||||||
|
"""Build RecordingAPIClient from AppConfig or dict config."""
|
||||||
|
base_client = APIClient(
|
||||||
|
base_url=_cfg_get(cfg, "api.base_url") or "",
|
||||||
|
token=_cfg_get(cfg, "api.token"),
|
||||||
|
timeout=int(_cfg_get(cfg, "api.timeout_sec", 20) or 20),
|
||||||
|
retry_max=int(_cfg_get(cfg, "api.retries.max_attempts", 3) or 3),
|
||||||
|
headers_extra=_cfg_get(cfg, "api.headers_extra") or {},
|
||||||
|
)
|
||||||
|
|
||||||
|
if write_pretty is None:
|
||||||
|
write_pretty = bool(_cfg_get(cfg, "io.write_pretty_json", False))
|
||||||
|
|
||||||
|
if run_id is None:
|
||||||
|
run_id = int(time.time())
|
||||||
|
|
||||||
|
if output_dir is None:
|
||||||
|
tz_name = _cfg_get(cfg, "app.timezone", "Asia/Taipei") or "Asia/Taipei"
|
||||||
|
tz = ZoneInfo(tz_name)
|
||||||
|
ts = datetime.now(tz).strftime("%Y%m%d-%H%M%S")
|
||||||
|
fetch_root = _cfg_get(cfg, "pipeline.fetch_root") or _cfg_get(cfg, "io.export_root") or "export/JSON"
|
||||||
|
task_upper = str(task_code).upper()
|
||||||
|
output_dir = Path(fetch_root) / task_upper / f"{task_upper}-{run_id}-{ts}"
|
||||||
|
|
||||||
|
return RecordingAPIClient(
|
||||||
|
base_client=base_client,
|
||||||
|
output_dir=output_dir,
|
||||||
|
task_code=str(task_code),
|
||||||
|
run_id=int(run_id),
|
||||||
|
write_pretty=bool(write_pretty),
|
||||||
|
)
|
||||||
|
|||||||
@@ -19,6 +19,11 @@ CREATE TABLE IF NOT EXISTS billiards_ods.member_profiles (
|
|||||||
status INT,
|
status INT,
|
||||||
user_status INT,
|
user_status INT,
|
||||||
create_time TIMESTAMP,
|
create_time TIMESTAMP,
|
||||||
|
pay_money_sum NUMERIC(18,2),
|
||||||
|
person_tenant_org_id BIGINT,
|
||||||
|
person_tenant_org_name TEXT,
|
||||||
|
recharge_money_sum NUMERIC(18,2),
|
||||||
|
register_source TEXT,
|
||||||
content_hash TEXT NOT NULL,
|
content_hash TEXT NOT NULL,
|
||||||
source_file TEXT,
|
source_file TEXT,
|
||||||
source_endpoint TEXT,
|
source_endpoint TEXT,
|
||||||
@@ -75,6 +80,9 @@ CREATE TABLE IF NOT EXISTS billiards_ods.member_balance_changes (
|
|||||||
operator_name TEXT,
|
operator_name TEXT,
|
||||||
is_delete INT,
|
is_delete INT,
|
||||||
create_time TIMESTAMP,
|
create_time TIMESTAMP,
|
||||||
|
principal_after NUMERIC(18,2),
|
||||||
|
principal_before NUMERIC(18,2),
|
||||||
|
principal_data TEXT,
|
||||||
content_hash TEXT NOT NULL,
|
content_hash TEXT NOT NULL,
|
||||||
source_file TEXT,
|
source_file TEXT,
|
||||||
source_endpoint TEXT,
|
source_endpoint TEXT,
|
||||||
@@ -185,6 +193,13 @@ CREATE TABLE IF NOT EXISTS billiards_ods.member_stored_value_cards (
|
|||||||
tenantName TEXT,
|
tenantName TEXT,
|
||||||
pdAssisnatLevel TEXT,
|
pdAssisnatLevel TEXT,
|
||||||
cxAssisnatLevel TEXT,
|
cxAssisnatLevel TEXT,
|
||||||
|
able_share_member_discount BOOLEAN,
|
||||||
|
electricity_deduct_radio NUMERIC(18,4),
|
||||||
|
electricity_discount NUMERIC(18,4),
|
||||||
|
electricitycarddeduct BOOLEAN,
|
||||||
|
member_grade BIGINT,
|
||||||
|
principal_balance NUMERIC(18,2),
|
||||||
|
rechargefreezebalance NUMERIC(18,2),
|
||||||
content_hash TEXT NOT NULL,
|
content_hash TEXT NOT NULL,
|
||||||
source_file TEXT,
|
source_file TEXT,
|
||||||
source_endpoint TEXT,
|
source_endpoint TEXT,
|
||||||
@@ -331,6 +346,12 @@ CREATE TABLE IF NOT EXISTS billiards_ods.recharge_settlements (
|
|||||||
isfirst INT,
|
isfirst INT,
|
||||||
rechargecardamount NUMERIC(18,2),
|
rechargecardamount NUMERIC(18,2),
|
||||||
giftcardamount NUMERIC(18,2),
|
giftcardamount NUMERIC(18,2),
|
||||||
|
electricityadjustmoney NUMERIC(18,2),
|
||||||
|
electricitymoney NUMERIC(18,2),
|
||||||
|
mervousalesamount NUMERIC(18,2),
|
||||||
|
plcouponsaleamount NUMERIC(18,2),
|
||||||
|
realelectricitymoney NUMERIC(18,2),
|
||||||
|
settlelist JSONB,
|
||||||
content_hash TEXT NOT NULL,
|
content_hash TEXT NOT NULL,
|
||||||
source_file TEXT,
|
source_file TEXT,
|
||||||
source_endpoint TEXT,
|
source_endpoint TEXT,
|
||||||
@@ -469,6 +490,12 @@ CREATE TABLE IF NOT EXISTS billiards_ods.settlement_records (
|
|||||||
isfirst INT,
|
isfirst INT,
|
||||||
rechargecardamount NUMERIC(18,2),
|
rechargecardamount NUMERIC(18,2),
|
||||||
giftcardamount NUMERIC(18,2),
|
giftcardamount NUMERIC(18,2),
|
||||||
|
electricityadjustmoney NUMERIC(18,2),
|
||||||
|
electricitymoney NUMERIC(18,2),
|
||||||
|
mervousalesamount NUMERIC(18,2),
|
||||||
|
plcouponsaleamount NUMERIC(18,2),
|
||||||
|
realelectricitymoney NUMERIC(18,2),
|
||||||
|
settlelist JSONB,
|
||||||
content_hash TEXT NOT NULL,
|
content_hash TEXT NOT NULL,
|
||||||
source_file TEXT,
|
source_file TEXT,
|
||||||
source_endpoint TEXT,
|
source_endpoint TEXT,
|
||||||
@@ -559,6 +586,7 @@ CREATE TABLE IF NOT EXISTS billiards_ods.assistant_cancellation_records (
|
|||||||
tableName TEXT,
|
tableName TEXT,
|
||||||
trashReason TEXT,
|
trashReason TEXT,
|
||||||
createTime TIMESTAMP,
|
createTime TIMESTAMP,
|
||||||
|
tenant_id BIGINT,
|
||||||
content_hash TEXT NOT NULL,
|
content_hash TEXT NOT NULL,
|
||||||
source_file TEXT,
|
source_file TEXT,
|
||||||
source_endpoint TEXT,
|
source_endpoint TEXT,
|
||||||
@@ -792,6 +820,8 @@ CREATE TABLE IF NOT EXISTS billiards_ods.assistant_service_records (
|
|||||||
get_grade_times INT,
|
get_grade_times INT,
|
||||||
is_not_responding INT,
|
is_not_responding INT,
|
||||||
is_confirm INT,
|
is_confirm INT,
|
||||||
|
assistantteamname TEXT,
|
||||||
|
real_service_money NUMERIC(18,2),
|
||||||
payload JSONB NOT NULL,
|
payload JSONB NOT NULL,
|
||||||
content_hash TEXT NOT NULL,
|
content_hash TEXT NOT NULL,
|
||||||
source_file TEXT,
|
source_file TEXT,
|
||||||
@@ -897,6 +927,7 @@ CREATE TABLE IF NOT EXISTS billiards_ods.site_tables_master (
|
|||||||
table_status INT,
|
table_status INT,
|
||||||
temporary_light_second INT,
|
temporary_light_second INT,
|
||||||
virtual_table INT,
|
virtual_table INT,
|
||||||
|
order_id BIGINT,
|
||||||
content_hash TEXT NOT NULL,
|
content_hash TEXT NOT NULL,
|
||||||
source_file TEXT,
|
source_file TEXT,
|
||||||
source_endpoint TEXT,
|
source_endpoint TEXT,
|
||||||
@@ -957,6 +988,14 @@ CREATE TABLE IF NOT EXISTS billiards_ods.table_fee_discount_records (
|
|||||||
order_trade_no TEXT,
|
order_trade_no TEXT,
|
||||||
is_delete INT,
|
is_delete INT,
|
||||||
create_time TIMESTAMP,
|
create_time TIMESTAMP,
|
||||||
|
area_type_id BIGINT,
|
||||||
|
charge_free BOOLEAN,
|
||||||
|
site_table_area_id BIGINT,
|
||||||
|
site_table_area_name TEXT,
|
||||||
|
sitename TEXT,
|
||||||
|
table_name TEXT,
|
||||||
|
table_price NUMERIC(18,2),
|
||||||
|
tenant_name TEXT,
|
||||||
content_hash TEXT NOT NULL,
|
content_hash TEXT NOT NULL,
|
||||||
source_file TEXT,
|
source_file TEXT,
|
||||||
source_endpoint TEXT,
|
source_endpoint TEXT,
|
||||||
@@ -1032,6 +1071,9 @@ CREATE TABLE IF NOT EXISTS billiards_ods.table_fee_transactions (
|
|||||||
salesman_org_id BIGINT,
|
salesman_org_id BIGINT,
|
||||||
salesman_user_id BIGINT,
|
salesman_user_id BIGINT,
|
||||||
create_time TIMESTAMP,
|
create_time TIMESTAMP,
|
||||||
|
activity_discount_amount NUMERIC(18,2),
|
||||||
|
order_consumption_type INT,
|
||||||
|
real_service_money NUMERIC(18,2),
|
||||||
payload JSONB NOT NULL,
|
payload JSONB NOT NULL,
|
||||||
content_hash TEXT NOT NULL,
|
content_hash TEXT NOT NULL,
|
||||||
source_file TEXT,
|
source_file TEXT,
|
||||||
@@ -1234,6 +1276,7 @@ CREATE TABLE IF NOT EXISTS billiards_ods.payment_transactions (
|
|||||||
create_time TIMESTAMP,
|
create_time TIMESTAMP,
|
||||||
payment_method INT,
|
payment_method INT,
|
||||||
online_pay_channel INT,
|
online_pay_channel INT,
|
||||||
|
tenant_id BIGINT,
|
||||||
content_hash TEXT NOT NULL,
|
content_hash TEXT NOT NULL,
|
||||||
source_file TEXT,
|
source_file TEXT,
|
||||||
source_endpoint TEXT,
|
source_endpoint TEXT,
|
||||||
@@ -1440,6 +1483,7 @@ CREATE TABLE IF NOT EXISTS billiards_ods.tenant_goods_master (
|
|||||||
remark_name TEXT,
|
remark_name TEXT,
|
||||||
create_time TIMESTAMP,
|
create_time TIMESTAMP,
|
||||||
update_time TIMESTAMP,
|
update_time TIMESTAMP,
|
||||||
|
not_sale BOOLEAN,
|
||||||
payload JSONB NOT NULL,
|
payload JSONB NOT NULL,
|
||||||
content_hash TEXT NOT NULL,
|
content_hash TEXT NOT NULL,
|
||||||
source_file TEXT,
|
source_file TEXT,
|
||||||
@@ -1522,6 +1566,9 @@ CREATE TABLE IF NOT EXISTS billiards_ods.group_buy_packages (
|
|||||||
area_tag_type INT,
|
area_tag_type INT,
|
||||||
creator_name TEXT,
|
creator_name TEXT,
|
||||||
create_time TIMESTAMP,
|
create_time TIMESTAMP,
|
||||||
|
is_first_limit BOOLEAN,
|
||||||
|
sort INT,
|
||||||
|
tenantcouponsaleorderitemid BIGINT,
|
||||||
content_hash TEXT NOT NULL,
|
content_hash TEXT NOT NULL,
|
||||||
source_file TEXT,
|
source_file TEXT,
|
||||||
source_endpoint TEXT,
|
source_endpoint TEXT,
|
||||||
@@ -1616,6 +1663,15 @@ CREATE TABLE IF NOT EXISTS billiards_ods.group_buy_redemption_records (
|
|||||||
is_single_order INT,
|
is_single_order INT,
|
||||||
is_delete INT,
|
is_delete INT,
|
||||||
create_time TIMESTAMP,
|
create_time TIMESTAMP,
|
||||||
|
assistant_service_share_money NUMERIC(18,2),
|
||||||
|
assistant_share_money NUMERIC(18,2),
|
||||||
|
coupon_sale_id BIGINT,
|
||||||
|
good_service_share_money NUMERIC(18,2),
|
||||||
|
goods_share_money NUMERIC(18,2),
|
||||||
|
member_discount_money NUMERIC(18,2),
|
||||||
|
recharge_share_money NUMERIC(18,2),
|
||||||
|
table_service_share_money NUMERIC(18,2),
|
||||||
|
table_share_money NUMERIC(18,2),
|
||||||
payload JSONB NOT NULL,
|
payload JSONB NOT NULL,
|
||||||
content_hash TEXT NOT NULL,
|
content_hash TEXT NOT NULL,
|
||||||
source_file TEXT,
|
source_file TEXT,
|
||||||
@@ -1812,6 +1868,8 @@ CREATE TABLE IF NOT EXISTS billiards_ods.store_goods_master (
|
|||||||
goods_cover TEXT,
|
goods_cover TEXT,
|
||||||
create_time TIMESTAMP,
|
create_time TIMESTAMP,
|
||||||
update_time TIMESTAMP,
|
update_time TIMESTAMP,
|
||||||
|
commodity_code TEXT,
|
||||||
|
not_sale INTEGER,
|
||||||
payload JSONB NOT NULL,
|
payload JSONB NOT NULL,
|
||||||
content_hash TEXT NOT NULL,
|
content_hash TEXT NOT NULL,
|
||||||
source_file TEXT,
|
source_file TEXT,
|
||||||
@@ -1923,6 +1981,7 @@ CREATE TABLE IF NOT EXISTS billiards_ods.store_goods_sales_records (
|
|||||||
tenant_goods_business_id BIGINT,
|
tenant_goods_business_id BIGINT,
|
||||||
tenant_goods_category_id BIGINT,
|
tenant_goods_category_id BIGINT,
|
||||||
create_time TIMESTAMP,
|
create_time TIMESTAMP,
|
||||||
|
coupon_share_money NUMERIC(18,2),
|
||||||
payload JSONB NOT NULL,
|
payload JSONB NOT NULL,
|
||||||
content_hash TEXT NOT NULL,
|
content_hash TEXT NOT NULL,
|
||||||
source_file TEXT,
|
source_file TEXT,
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ COMMENT ON COLUMN billiards_dwd.dim_site.scd2_is_current IS '【说明】SCD2
|
|||||||
COMMENT ON COLUMN billiards_dwd.dim_site.scd2_version IS '【说明】SCD2 版本号(自增),用于与时间段一起避免版本重叠。 【示例】1(SCD2 版本号(自增),用于与时间段一起避免版本重叠)。 【ODS来源】table_fee_transactions - 无(DWD慢变元数据)。 【JSON字段】无 - DWD慢变元数据 - 无。';
|
COMMENT ON COLUMN billiards_dwd.dim_site.scd2_version IS '【说明】SCD2 版本号(自增),用于与时间段一起避免版本重叠。 【示例】1(SCD2 版本号(自增),用于与时间段一起避免版本重叠)。 【ODS来源】table_fee_transactions - 无(DWD慢变元数据)。 【JSON字段】无 - DWD慢变元数据 - 无。';
|
||||||
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS dim_site_Ex (
|
CREATE TABLE IF NOT EXISTS dim_site_ex (
|
||||||
site_id BIGINT,
|
site_id BIGINT,
|
||||||
avatar TEXT,
|
avatar TEXT,
|
||||||
address TEXT,
|
address TEXT,
|
||||||
@@ -172,6 +172,7 @@ CREATE TABLE IF NOT EXISTS dim_table (
|
|||||||
site_table_area_name TEXT,
|
site_table_area_name TEXT,
|
||||||
tenant_table_area_id BIGINT,
|
tenant_table_area_id BIGINT,
|
||||||
table_price NUMERIC(18,2),
|
table_price NUMERIC(18,2),
|
||||||
|
order_id BIGINT,
|
||||||
SCD2_start_time TIMESTAMPTZ DEFAULT now(),
|
SCD2_start_time TIMESTAMPTZ DEFAULT now(),
|
||||||
SCD2_end_time TIMESTAMPTZ DEFAULT '9999-12-31',
|
SCD2_end_time TIMESTAMPTZ DEFAULT '9999-12-31',
|
||||||
SCD2_is_current INT DEFAULT 1,
|
SCD2_is_current INT DEFAULT 1,
|
||||||
@@ -193,7 +194,7 @@ COMMENT ON COLUMN billiards_dwd.dim_table.scd2_is_current IS '【说明】SCD2
|
|||||||
COMMENT ON COLUMN billiards_dwd.dim_table.scd2_version IS '【说明】SCD2 版本号(自增),用于与时间段一起避免版本重叠。 【示例】1(SCD2 版本号(自增),用于与时间段一起避免版本重叠)。 【ODS来源】site_tables_master - 无(DWD慢变元数据)。 【JSON字段】无 - DWD慢变元数据 - 无。';
|
COMMENT ON COLUMN billiards_dwd.dim_table.scd2_version IS '【说明】SCD2 版本号(自增),用于与时间段一起避免版本重叠。 【示例】1(SCD2 版本号(自增),用于与时间段一起避免版本重叠)。 【ODS来源】site_tables_master - 无(DWD慢变元数据)。 【JSON字段】无 - DWD慢变元数据 - 无。';
|
||||||
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS dim_table_Ex (
|
CREATE TABLE IF NOT EXISTS dim_table_ex (
|
||||||
table_id BIGINT,
|
table_id BIGINT,
|
||||||
show_status INTEGER,
|
show_status INTEGER,
|
||||||
is_online_reservation INTEGER,
|
is_online_reservation INTEGER,
|
||||||
@@ -265,7 +266,7 @@ COMMENT ON COLUMN billiards_dwd.dim_assistant.scd2_is_current IS '【说明】SC
|
|||||||
COMMENT ON COLUMN billiards_dwd.dim_assistant.scd2_version IS '【说明】SCD2 版本号(自增),用于与时间段一起避免版本重叠。 【示例】1(SCD2 版本号(自增),用于与时间段一起避免版本重叠)。 【ODS来源】assistant_accounts_master - 无(DWD慢变元数据)。 【JSON字段】无 - DWD慢变元数据 - 无。';
|
COMMENT ON COLUMN billiards_dwd.dim_assistant.scd2_version IS '【说明】SCD2 版本号(自增),用于与时间段一起避免版本重叠。 【示例】1(SCD2 版本号(自增),用于与时间段一起避免版本重叠)。 【ODS来源】assistant_accounts_master - 无(DWD慢变元数据)。 【JSON字段】无 - DWD慢变元数据 - 无。';
|
||||||
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS dim_assistant_Ex (
|
CREATE TABLE IF NOT EXISTS dim_assistant_ex (
|
||||||
assistant_id BIGINT,
|
assistant_id BIGINT,
|
||||||
gender INTEGER,
|
gender INTEGER,
|
||||||
birth_date TIMESTAMPTZ,
|
birth_date TIMESTAMPTZ,
|
||||||
@@ -379,6 +380,8 @@ CREATE TABLE IF NOT EXISTS dim_member (
|
|||||||
member_card_grade_name TEXT,
|
member_card_grade_name TEXT,
|
||||||
create_time TIMESTAMPTZ,
|
create_time TIMESTAMPTZ,
|
||||||
update_time TIMESTAMPTZ,
|
update_time TIMESTAMPTZ,
|
||||||
|
pay_money_sum NUMERIC(18,2),
|
||||||
|
recharge_money_sum NUMERIC(18,2),
|
||||||
SCD2_start_time TIMESTAMPTZ,
|
SCD2_start_time TIMESTAMPTZ,
|
||||||
SCD2_end_time TIMESTAMPTZ,
|
SCD2_end_time TIMESTAMPTZ,
|
||||||
SCD2_is_current INT,
|
SCD2_is_current INT,
|
||||||
@@ -403,7 +406,7 @@ COMMENT ON COLUMN billiards_dwd.dim_member.scd2_is_current IS '【说明】SCD2
|
|||||||
COMMENT ON COLUMN billiards_dwd.dim_member.scd2_version IS '【说明】SCD2 版本号(自增),用于与时间段一起避免版本重叠。 【示例】1(SCD2 版本号(自增),用于与时间段一起避免版本重叠)。 【ODS来源】member_profiles - 无(DWD慢变元数据)。 【JSON字段】无 - DWD慢变元数据 - 无。';
|
COMMENT ON COLUMN billiards_dwd.dim_member.scd2_version IS '【说明】SCD2 版本号(自增),用于与时间段一起避免版本重叠。 【示例】1(SCD2 版本号(自增),用于与时间段一起避免版本重叠)。 【ODS来源】member_profiles - 无(DWD慢变元数据)。 【JSON字段】无 - DWD慢变元数据 - 无。';
|
||||||
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS dim_member_Ex (
|
CREATE TABLE IF NOT EXISTS dim_member_ex (
|
||||||
member_id BIGINT,
|
member_id BIGINT,
|
||||||
referrer_member_id BIGINT,
|
referrer_member_id BIGINT,
|
||||||
point NUMERIC(18,2),
|
point NUMERIC(18,2),
|
||||||
@@ -411,6 +414,9 @@ CREATE TABLE IF NOT EXISTS dim_member_Ex (
|
|||||||
growth_value NUMERIC(18,2),
|
growth_value NUMERIC(18,2),
|
||||||
user_status INTEGER,
|
user_status INTEGER,
|
||||||
status INTEGER,
|
status INTEGER,
|
||||||
|
person_tenant_org_id BIGINT,
|
||||||
|
person_tenant_org_name TEXT,
|
||||||
|
register_source TEXT,
|
||||||
SCD2_start_time TIMESTAMPTZ,
|
SCD2_start_time TIMESTAMPTZ,
|
||||||
SCD2_end_time TIMESTAMPTZ,
|
SCD2_end_time TIMESTAMPTZ,
|
||||||
SCD2_is_current INT,
|
SCD2_is_current INT,
|
||||||
@@ -450,6 +456,8 @@ CREATE TABLE IF NOT EXISTS dim_member_card_account (
|
|||||||
last_consume_time TIMESTAMPTZ,
|
last_consume_time TIMESTAMPTZ,
|
||||||
status INTEGER,
|
status INTEGER,
|
||||||
is_delete INTEGER,
|
is_delete INTEGER,
|
||||||
|
principal_balance NUMERIC(18,2),
|
||||||
|
member_grade BIGINT,
|
||||||
SCD2_start_time TIMESTAMPTZ,
|
SCD2_start_time TIMESTAMPTZ,
|
||||||
SCD2_end_time TIMESTAMPTZ,
|
SCD2_end_time TIMESTAMPTZ,
|
||||||
SCD2_is_current INT,
|
SCD2_is_current INT,
|
||||||
@@ -481,7 +489,7 @@ COMMENT ON COLUMN billiards_dwd.dim_member_card_account.scd2_is_current IS '【
|
|||||||
COMMENT ON COLUMN billiards_dwd.dim_member_card_account.scd2_version IS '【说明】SCD2 版本号(自增),用于与时间段一起避免版本重叠。 【示例】1(SCD2 版本号(自增),用于与时间段一起避免版本重叠)。 【ODS来源】member_stored_value_cards - 无(DWD慢变元数据)。 【JSON字段】无 - DWD慢变元数据 - 无。';
|
COMMENT ON COLUMN billiards_dwd.dim_member_card_account.scd2_version IS '【说明】SCD2 版本号(自增),用于与时间段一起避免版本重叠。 【示例】1(SCD2 版本号(自增),用于与时间段一起避免版本重叠)。 【ODS来源】member_stored_value_cards - 无(DWD慢变元数据)。 【JSON字段】无 - DWD慢变元数据 - 无。';
|
||||||
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS dim_member_card_account_Ex (
|
CREATE TABLE IF NOT EXISTS dim_member_card_account_ex (
|
||||||
member_card_id BIGINT,
|
member_card_id BIGINT,
|
||||||
site_name TEXT,
|
site_name TEXT,
|
||||||
tenant_name VARCHAR(64),
|
tenant_name VARCHAR(64),
|
||||||
@@ -534,6 +542,11 @@ CREATE TABLE IF NOT EXISTS dim_member_card_account_Ex (
|
|||||||
goodsCategoryId TEXT,
|
goodsCategoryId TEXT,
|
||||||
pdAssisnatLevel TEXT,
|
pdAssisnatLevel TEXT,
|
||||||
cxAssisnatLevel TEXT,
|
cxAssisnatLevel TEXT,
|
||||||
|
able_share_member_discount BOOLEAN,
|
||||||
|
electricity_deduct_radio NUMERIC(18,4),
|
||||||
|
electricity_discount NUMERIC(18,4),
|
||||||
|
electricity_card_deduct BOOLEAN,
|
||||||
|
recharge_freeze_balance NUMERIC(18,2),
|
||||||
SCD2_start_time TIMESTAMPTZ,
|
SCD2_start_time TIMESTAMPTZ,
|
||||||
SCD2_end_time TIMESTAMPTZ,
|
SCD2_end_time TIMESTAMPTZ,
|
||||||
SCD2_is_current INT,
|
SCD2_is_current INT,
|
||||||
@@ -615,6 +628,7 @@ CREATE TABLE IF NOT EXISTS dim_tenant_goods (
|
|||||||
create_time TIMESTAMPTZ,
|
create_time TIMESTAMPTZ,
|
||||||
update_time TIMESTAMPTZ,
|
update_time TIMESTAMPTZ,
|
||||||
is_delete INTEGER,
|
is_delete INTEGER,
|
||||||
|
not_sale INTEGER,
|
||||||
SCD2_start_time TIMESTAMPTZ,
|
SCD2_start_time TIMESTAMPTZ,
|
||||||
SCD2_end_time TIMESTAMPTZ,
|
SCD2_end_time TIMESTAMPTZ,
|
||||||
SCD2_is_current INT,
|
SCD2_is_current INT,
|
||||||
@@ -643,7 +657,7 @@ COMMENT ON COLUMN billiards_dwd.dim_tenant_goods.scd2_is_current IS '【说明
|
|||||||
COMMENT ON COLUMN billiards_dwd.dim_tenant_goods.scd2_version IS '【说明】SCD2 版本号(自增),用于与时间段一起避免版本重叠。 【示例】1(SCD2 版本号(自增),用于与时间段一起避免版本重叠)。 【ODS来源】tenant_goods_master - 无(DWD慢变元数据)。 【JSON字段】无 - DWD慢变元数据 - 无。';
|
COMMENT ON COLUMN billiards_dwd.dim_tenant_goods.scd2_version IS '【说明】SCD2 版本号(自增),用于与时间段一起避免版本重叠。 【示例】1(SCD2 版本号(自增),用于与时间段一起避免版本重叠)。 【ODS来源】tenant_goods_master - 无(DWD慢变元数据)。 【JSON字段】无 - DWD慢变元数据 - 无。';
|
||||||
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS dim_tenant_goods_Ex (
|
CREATE TABLE IF NOT EXISTS dim_tenant_goods_ex (
|
||||||
tenant_goods_id BIGINT,
|
tenant_goods_id BIGINT,
|
||||||
remark_name VARCHAR(128),
|
remark_name VARCHAR(128),
|
||||||
pinyin_initial VARCHAR(128),
|
pinyin_initial VARCHAR(128),
|
||||||
@@ -715,6 +729,8 @@ CREATE TABLE IF NOT EXISTS dim_store_goods (
|
|||||||
enable_status INTEGER,
|
enable_status INTEGER,
|
||||||
send_state INTEGER,
|
send_state INTEGER,
|
||||||
is_delete INTEGER,
|
is_delete INTEGER,
|
||||||
|
commodity_code TEXT,
|
||||||
|
not_sale INTEGER,
|
||||||
SCD2_start_time TIMESTAMPTZ,
|
SCD2_start_time TIMESTAMPTZ,
|
||||||
SCD2_end_time TIMESTAMPTZ,
|
SCD2_end_time TIMESTAMPTZ,
|
||||||
SCD2_is_current INT,
|
SCD2_is_current INT,
|
||||||
@@ -749,7 +765,7 @@ COMMENT ON COLUMN billiards_dwd.dim_store_goods.scd2_is_current IS '【说明】
|
|||||||
COMMENT ON COLUMN billiards_dwd.dim_store_goods.scd2_version IS '【说明】SCD2 版本号(自增),用于与时间段一起避免版本重叠。 【示例】1(SCD2 版本号(自增),用于与时间段一起避免版本重叠)。 【ODS来源】store_goods_master - 无(DWD慢变元数据)。 【JSON字段】无 - DWD慢变元数据 - 无。';
|
COMMENT ON COLUMN billiards_dwd.dim_store_goods.scd2_version IS '【说明】SCD2 版本号(自增),用于与时间段一起避免版本重叠。 【示例】1(SCD2 版本号(自增),用于与时间段一起避免版本重叠)。 【ODS来源】store_goods_master - 无(DWD慢变元数据)。 【JSON字段】无 - DWD慢变元数据 - 无。';
|
||||||
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS dim_store_goods_Ex (
|
CREATE TABLE IF NOT EXISTS dim_store_goods_ex (
|
||||||
site_goods_id BIGINT,
|
site_goods_id BIGINT,
|
||||||
site_name TEXT,
|
site_name TEXT,
|
||||||
unit TEXT,
|
unit TEXT,
|
||||||
@@ -872,6 +888,8 @@ CREATE TABLE IF NOT EXISTS dim_groupbuy_package (
|
|||||||
create_time TIMESTAMPTZ,
|
create_time TIMESTAMPTZ,
|
||||||
tenant_table_area_id_list VARCHAR(512),
|
tenant_table_area_id_list VARCHAR(512),
|
||||||
card_type_ids VARCHAR(255),
|
card_type_ids VARCHAR(255),
|
||||||
|
sort INTEGER,
|
||||||
|
is_first_limit BOOLEAN,
|
||||||
SCD2_start_time TIMESTAMPTZ,
|
SCD2_start_time TIMESTAMPTZ,
|
||||||
SCD2_end_time TIMESTAMPTZ,
|
SCD2_end_time TIMESTAMPTZ,
|
||||||
SCD2_is_current INT,
|
SCD2_is_current INT,
|
||||||
@@ -902,7 +920,7 @@ COMMENT ON COLUMN billiards_dwd.dim_groupbuy_package.scd2_is_current IS '【说
|
|||||||
COMMENT ON COLUMN billiards_dwd.dim_groupbuy_package.scd2_version IS '【说明】SCD2 版本号(自增),用于与时间段一起避免版本重叠。 【示例】1(SCD2 版本号(自增),用于与时间段一起避免版本重叠)。 【ODS来源】group_buy_packages - 无(DWD慢变元数据)。 【JSON字段】无 - DWD慢变元数据 - 无。';
|
COMMENT ON COLUMN billiards_dwd.dim_groupbuy_package.scd2_version IS '【说明】SCD2 版本号(自增),用于与时间段一起避免版本重叠。 【示例】1(SCD2 版本号(自增),用于与时间段一起避免版本重叠)。 【ODS来源】group_buy_packages - 无(DWD慢变元数据)。 【JSON字段】无 - DWD慢变元数据 - 无。';
|
||||||
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS dim_groupbuy_package_Ex (
|
CREATE TABLE IF NOT EXISTS dim_groupbuy_package_ex (
|
||||||
groupbuy_package_id BIGINT,
|
groupbuy_package_id BIGINT,
|
||||||
site_name VARCHAR(100),
|
site_name VARCHAR(100),
|
||||||
usable_count INTEGER,
|
usable_count INTEGER,
|
||||||
@@ -923,6 +941,7 @@ CREATE TABLE IF NOT EXISTS dim_groupbuy_package_Ex (
|
|||||||
effective_status INTEGER,
|
effective_status INTEGER,
|
||||||
max_selectable_categories INTEGER,
|
max_selectable_categories INTEGER,
|
||||||
creator_name VARCHAR(100),
|
creator_name VARCHAR(100),
|
||||||
|
tenant_coupon_sale_order_item_id BIGINT,
|
||||||
SCD2_start_time TIMESTAMPTZ,
|
SCD2_start_time TIMESTAMPTZ,
|
||||||
SCD2_end_time TIMESTAMPTZ,
|
SCD2_end_time TIMESTAMPTZ,
|
||||||
SCD2_is_current INT,
|
SCD2_is_current INT,
|
||||||
@@ -990,6 +1009,11 @@ CREATE TABLE IF NOT EXISTS dwd_settlement_head (
|
|||||||
coupon_amount NUMERIC(18,2),
|
coupon_amount NUMERIC(18,2),
|
||||||
rounding_amount NUMERIC(18,2),
|
rounding_amount NUMERIC(18,2),
|
||||||
point_amount NUMERIC(18,2),
|
point_amount NUMERIC(18,2),
|
||||||
|
electricity_money NUMERIC(18,2),
|
||||||
|
real_electricity_money NUMERIC(18,2),
|
||||||
|
electricity_adjust_money NUMERIC(18,2),
|
||||||
|
pl_coupon_sale_amount NUMERIC(18,2),
|
||||||
|
mervou_sales_amount NUMERIC(18,2),
|
||||||
PRIMARY KEY (order_settle_id)
|
PRIMARY KEY (order_settle_id)
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -1028,7 +1052,7 @@ COMMENT ON COLUMN billiards_dwd.dwd_settlement_head.rounding_amount IS '【说
|
|||||||
COMMENT ON COLUMN billiards_dwd.dwd_settlement_head.point_amount IS '【说明】金额字段,用于计费/结算/核算等金额计算。 【示例】NULL(金额字段,用于计费/结算/核算等金额计算)。 【ODS来源】settlement_records - pointamount。 【JSON字段】settlement_records.json - $ - pointamount。';
|
COMMENT ON COLUMN billiards_dwd.dwd_settlement_head.point_amount IS '【说明】金额字段,用于计费/结算/核算等金额计算。 【示例】NULL(金额字段,用于计费/结算/核算等金额计算)。 【ODS来源】settlement_records - pointamount。 【JSON字段】settlement_records.json - $ - pointamount。';
|
||||||
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS dwd_settlement_head_Ex (
|
CREATE TABLE IF NOT EXISTS dwd_settlement_head_ex (
|
||||||
order_settle_id BIGINT,
|
order_settle_id BIGINT,
|
||||||
serial_number INTEGER,
|
serial_number INTEGER,
|
||||||
settle_status INTEGER,
|
settle_status INTEGER,
|
||||||
@@ -1059,6 +1083,7 @@ CREATE TABLE IF NOT EXISTS dwd_settlement_head_Ex (
|
|||||||
order_remark VARCHAR(255),
|
order_remark VARCHAR(255),
|
||||||
operator_id BIGINT,
|
operator_id BIGINT,
|
||||||
salesman_user_id BIGINT,
|
salesman_user_id BIGINT,
|
||||||
|
settle_list JSONB,
|
||||||
PRIMARY KEY (order_settle_id)
|
PRIMARY KEY (order_settle_id)
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -1123,6 +1148,8 @@ CREATE TABLE IF NOT EXISTS dwd_table_fee_log (
|
|||||||
ledger_status INTEGER,
|
ledger_status INTEGER,
|
||||||
is_single_order INTEGER,
|
is_single_order INTEGER,
|
||||||
is_delete INTEGER,
|
is_delete INTEGER,
|
||||||
|
activity_discount_amount NUMERIC(18,2),
|
||||||
|
real_service_money NUMERIC(18,2),
|
||||||
PRIMARY KEY (table_fee_log_id)
|
PRIMARY KEY (table_fee_log_id)
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -1156,7 +1183,7 @@ COMMENT ON COLUMN billiards_dwd.dwd_table_fee_log.is_single_order IS '【说明
|
|||||||
COMMENT ON COLUMN billiards_dwd.dwd_table_fee_log.is_delete IS '【说明】布尔/开关字段,用于表示是否/可用性等业务开关。 【示例】0(布尔/开关字段,用于表示是否/可用性等业务开关)。 【ODS来源】table_fee_transactions - is_delete。 【JSON字段】table_fee_transactions.json - data.siteTableUseDetailsList - is_delete。';
|
COMMENT ON COLUMN billiards_dwd.dwd_table_fee_log.is_delete IS '【说明】布尔/开关字段,用于表示是否/可用性等业务开关。 【示例】0(布尔/开关字段,用于表示是否/可用性等业务开关)。 【ODS来源】table_fee_transactions - is_delete。 【JSON字段】table_fee_transactions.json - data.siteTableUseDetailsList - is_delete。';
|
||||||
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS dwd_table_fee_log_Ex (
|
CREATE TABLE IF NOT EXISTS dwd_table_fee_log_ex (
|
||||||
table_fee_log_id BIGINT,
|
table_fee_log_id BIGINT,
|
||||||
operator_name VARCHAR(64),
|
operator_name VARCHAR(64),
|
||||||
salesman_name VARCHAR(64),
|
salesman_name VARCHAR(64),
|
||||||
@@ -1169,6 +1196,7 @@ CREATE TABLE IF NOT EXISTS dwd_table_fee_log_Ex (
|
|||||||
operator_id BIGINT,
|
operator_id BIGINT,
|
||||||
salesman_user_id BIGINT,
|
salesman_user_id BIGINT,
|
||||||
salesman_org_id BIGINT,
|
salesman_org_id BIGINT,
|
||||||
|
order_consumption_type INTEGER,
|
||||||
PRIMARY KEY (table_fee_log_id)
|
PRIMARY KEY (table_fee_log_id)
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -1201,6 +1229,9 @@ CREATE TABLE IF NOT EXISTS dwd_table_fee_adjust (
|
|||||||
ledger_status INTEGER,
|
ledger_status INTEGER,
|
||||||
is_delete INTEGER,
|
is_delete INTEGER,
|
||||||
adjust_time TIMESTAMPTZ,
|
adjust_time TIMESTAMPTZ,
|
||||||
|
table_name TEXT,
|
||||||
|
table_price NUMERIC(18,2),
|
||||||
|
charge_free BOOLEAN,
|
||||||
PRIMARY KEY (table_fee_adjust_id)
|
PRIMARY KEY (table_fee_adjust_id)
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -1220,7 +1251,7 @@ COMMENT ON COLUMN billiards_dwd.dwd_table_fee_adjust.is_delete IS '【说明】
|
|||||||
COMMENT ON COLUMN billiards_dwd.dwd_table_fee_adjust.adjust_time IS '【说明】时间/日期字段,用于记录业务时间与统计口径对齐。 【示例】2025-11-09 23:25:11(时间/日期字段,用于记录业务时间与统计口径对齐)。 【ODS来源】table_fee_discount_records - create_time。 【JSON字段】table_fee_discount_records.json - data.taiFeeAdjustInfos - create_time。';
|
COMMENT ON COLUMN billiards_dwd.dwd_table_fee_adjust.adjust_time IS '【说明】时间/日期字段,用于记录业务时间与统计口径对齐。 【示例】2025-11-09 23:25:11(时间/日期字段,用于记录业务时间与统计口径对齐)。 【ODS来源】table_fee_discount_records - create_time。 【JSON字段】table_fee_discount_records.json - data.taiFeeAdjustInfos - create_time。';
|
||||||
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS dwd_table_fee_adjust_Ex (
|
CREATE TABLE IF NOT EXISTS dwd_table_fee_adjust_ex (
|
||||||
table_fee_adjust_id BIGINT,
|
table_fee_adjust_id BIGINT,
|
||||||
adjust_type INTEGER,
|
adjust_type INTEGER,
|
||||||
ledger_count INTEGER,
|
ledger_count INTEGER,
|
||||||
@@ -1229,6 +1260,11 @@ CREATE TABLE IF NOT EXISTS dwd_table_fee_adjust_Ex (
|
|||||||
operator_name VARCHAR(64),
|
operator_name VARCHAR(64),
|
||||||
applicant_id BIGINT,
|
applicant_id BIGINT,
|
||||||
operator_id BIGINT,
|
operator_id BIGINT,
|
||||||
|
area_type_id BIGINT,
|
||||||
|
site_table_area_id BIGINT,
|
||||||
|
site_table_area_name TEXT,
|
||||||
|
site_name TEXT,
|
||||||
|
tenant_name TEXT,
|
||||||
PRIMARY KEY (table_fee_adjust_id)
|
PRIMARY KEY (table_fee_adjust_id)
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -1267,6 +1303,7 @@ CREATE TABLE IF NOT EXISTS dwd_store_goods_sale (
|
|||||||
ledger_status INTEGER,
|
ledger_status INTEGER,
|
||||||
is_delete INTEGER,
|
is_delete INTEGER,
|
||||||
create_time TIMESTAMPTZ,
|
create_time TIMESTAMPTZ,
|
||||||
|
coupon_share_money NUMERIC(18,2),
|
||||||
PRIMARY KEY (store_goods_sale_id)
|
PRIMARY KEY (store_goods_sale_id)
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -1296,7 +1333,7 @@ COMMENT ON COLUMN billiards_dwd.dwd_store_goods_sale.is_delete IS '【说明】
|
|||||||
COMMENT ON COLUMN billiards_dwd.dwd_store_goods_sale.create_time IS '【说明】时间/日期字段,用于记录业务时间与统计口径对齐。 【示例】2025-11-09 23:35:57(时间/日期字段,用于记录业务时间与统计口径对齐)。 【ODS来源】store_goods_sales_records - create_time。 【JSON字段】store_goods_sales_records.json - data.orderGoodsLedgers - create_time。';
|
COMMENT ON COLUMN billiards_dwd.dwd_store_goods_sale.create_time IS '【说明】时间/日期字段,用于记录业务时间与统计口径对齐。 【示例】2025-11-09 23:35:57(时间/日期字段,用于记录业务时间与统计口径对齐)。 【ODS来源】store_goods_sales_records - create_time。 【JSON字段】store_goods_sales_records.json - data.orderGoodsLedgers - create_time。';
|
||||||
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS dwd_store_goods_sale_Ex (
|
CREATE TABLE IF NOT EXISTS dwd_store_goods_sale_ex (
|
||||||
store_goods_sale_id BIGINT,
|
store_goods_sale_id BIGINT,
|
||||||
legacy_order_goods_id BIGINT,
|
legacy_order_goods_id BIGINT,
|
||||||
site_name TEXT,
|
site_name TEXT,
|
||||||
@@ -1392,6 +1429,7 @@ CREATE TABLE IF NOT EXISTS dwd_assistant_service_log (
|
|||||||
start_use_time TIMESTAMPTZ,
|
start_use_time TIMESTAMPTZ,
|
||||||
last_use_time TIMESTAMPTZ,
|
last_use_time TIMESTAMPTZ,
|
||||||
is_delete INTEGER,
|
is_delete INTEGER,
|
||||||
|
real_service_money NUMERIC(18,2),
|
||||||
PRIMARY KEY (assistant_service_id)
|
PRIMARY KEY (assistant_service_id)
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -1430,7 +1468,7 @@ COMMENT ON COLUMN billiards_dwd.dwd_assistant_service_log.last_use_time IS '【
|
|||||||
COMMENT ON COLUMN billiards_dwd.dwd_assistant_service_log.is_delete IS '【说明】布尔/开关字段,用于表示是否/可用性等业务开关。 【示例】0(布尔/开关字段,用于表示是否/可用性等业务开关)。 【ODS来源】assistant_service_records - is_delete。 【JSON字段】assistant_service_records.json - data.orderAssistantDetails - is_delete。';
|
COMMENT ON COLUMN billiards_dwd.dwd_assistant_service_log.is_delete IS '【说明】布尔/开关字段,用于表示是否/可用性等业务开关。 【示例】0(布尔/开关字段,用于表示是否/可用性等业务开关)。 【ODS来源】assistant_service_records - is_delete。 【JSON字段】assistant_service_records.json - data.orderAssistantDetails - is_delete。';
|
||||||
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS dwd_assistant_service_log_Ex (
|
CREATE TABLE IF NOT EXISTS dwd_assistant_service_log_ex (
|
||||||
assistant_service_id BIGINT,
|
assistant_service_id BIGINT,
|
||||||
table_name VARCHAR(64),
|
table_name VARCHAR(64),
|
||||||
assistant_name VARCHAR(64),
|
assistant_name VARCHAR(64),
|
||||||
@@ -1461,6 +1499,7 @@ CREATE TABLE IF NOT EXISTS dwd_assistant_service_log_Ex (
|
|||||||
get_grade_times INTEGER,
|
get_grade_times INTEGER,
|
||||||
grade_status INTEGER,
|
grade_status INTEGER,
|
||||||
composite_grade_time TIMESTAMPTZ,
|
composite_grade_time TIMESTAMPTZ,
|
||||||
|
assistant_team_name TEXT,
|
||||||
PRIMARY KEY (assistant_service_id)
|
PRIMARY KEY (assistant_service_id)
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -1508,6 +1547,7 @@ CREATE TABLE IF NOT EXISTS dwd_assistant_trash_event (
|
|||||||
abolish_amount NUMERIC(18,2),
|
abolish_amount NUMERIC(18,2),
|
||||||
trash_reason VARCHAR(255),
|
trash_reason VARCHAR(255),
|
||||||
create_time TIMESTAMPTZ,
|
create_time TIMESTAMPTZ,
|
||||||
|
tenant_id BIGINT,
|
||||||
PRIMARY KEY (assistant_trash_event_id)
|
PRIMARY KEY (assistant_trash_event_id)
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -1524,7 +1564,7 @@ COMMENT ON COLUMN billiards_dwd.dwd_assistant_trash_event.trash_reason IS '【
|
|||||||
COMMENT ON COLUMN billiards_dwd.dwd_assistant_trash_event.create_time IS '【说明】时间/日期字段,用于记录业务时间与统计口径对齐。 【示例】2025-11-09 19:23:29(时间/日期字段,用于记录业务时间与统计口径对齐)。 【ODS来源】assistant_cancellation_records - createTime。 【JSON字段】assistant_cancellation_records.json - data.abolitionAssistants - createTime。';
|
COMMENT ON COLUMN billiards_dwd.dwd_assistant_trash_event.create_time IS '【说明】时间/日期字段,用于记录业务时间与统计口径对齐。 【示例】2025-11-09 19:23:29(时间/日期字段,用于记录业务时间与统计口径对齐)。 【ODS来源】assistant_cancellation_records - createTime。 【JSON字段】assistant_cancellation_records.json - data.abolitionAssistants - createTime。';
|
||||||
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS dwd_assistant_trash_event_Ex (
|
CREATE TABLE IF NOT EXISTS dwd_assistant_trash_event_ex (
|
||||||
assistant_trash_event_id BIGINT,
|
assistant_trash_event_id BIGINT,
|
||||||
table_name VARCHAR(64),
|
table_name VARCHAR(64),
|
||||||
table_area_name VARCHAR(64),
|
table_area_name VARCHAR(64),
|
||||||
@@ -1557,6 +1597,8 @@ CREATE TABLE IF NOT EXISTS dwd_member_balance_change (
|
|||||||
change_time TIMESTAMPTZ,
|
change_time TIMESTAMPTZ,
|
||||||
is_delete INTEGER,
|
is_delete INTEGER,
|
||||||
remark VARCHAR(255),
|
remark VARCHAR(255),
|
||||||
|
principal_before NUMERIC(18,2),
|
||||||
|
principal_after NUMERIC(18,2),
|
||||||
PRIMARY KEY (balance_change_id)
|
PRIMARY KEY (balance_change_id)
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -1582,13 +1624,14 @@ COMMENT ON COLUMN billiards_dwd.dwd_member_balance_change.is_delete IS '【说
|
|||||||
COMMENT ON COLUMN billiards_dwd.dwd_member_balance_change.remark IS '【说明】明细字段,用于记录事实取值。 【示例】充值退款(明细字段,用于记录事实取值)。 【ODS来源】member_balance_changes - remark。 【JSON字段】member_balance_changes.json - data.tenantMemberCardLogs - remark。';
|
COMMENT ON COLUMN billiards_dwd.dwd_member_balance_change.remark IS '【说明】明细字段,用于记录事实取值。 【示例】充值退款(明细字段,用于记录事实取值)。 【ODS来源】member_balance_changes - remark。 【JSON字段】member_balance_changes.json - data.tenantMemberCardLogs - remark。';
|
||||||
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS dwd_member_balance_change_EX (
|
CREATE TABLE IF NOT EXISTS dwd_member_balance_change_ex (
|
||||||
balance_change_id BIGINT,
|
balance_change_id BIGINT,
|
||||||
pay_site_name VARCHAR(64),
|
pay_site_name VARCHAR(64),
|
||||||
register_site_name VARCHAR(64),
|
register_site_name VARCHAR(64),
|
||||||
refund_amount NUMERIC(18,2),
|
refund_amount NUMERIC(18,2),
|
||||||
operator_id BIGINT,
|
operator_id BIGINT,
|
||||||
operator_name VARCHAR(64),
|
operator_name VARCHAR(64),
|
||||||
|
principal_data TEXT,
|
||||||
PRIMARY KEY (balance_change_id)
|
PRIMARY KEY (balance_change_id)
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -1625,6 +1668,8 @@ CREATE TABLE IF NOT EXISTS dwd_groupbuy_redemption (
|
|||||||
is_delete INTEGER,
|
is_delete INTEGER,
|
||||||
ledger_name VARCHAR(128),
|
ledger_name VARCHAR(128),
|
||||||
create_time TIMESTAMPTZ,
|
create_time TIMESTAMPTZ,
|
||||||
|
member_discount_money NUMERIC(18,2),
|
||||||
|
coupon_sale_id BIGINT,
|
||||||
PRIMARY KEY (redemption_id)
|
PRIMARY KEY (redemption_id)
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -1654,7 +1699,7 @@ COMMENT ON COLUMN billiards_dwd.dwd_groupbuy_redemption.ledger_name IS '【说
|
|||||||
COMMENT ON COLUMN billiards_dwd.dwd_groupbuy_redemption.create_time IS '【说明】时间/日期字段,用于记录业务时间与统计口径对齐。 【示例】2025-11-09 23:35:57(时间/日期字段,用于记录业务时间与统计口径对齐)。 【ODS来源】group_buy_redemption_records - create_time。 【JSON字段】group_buy_redemption_records.json - data.siteTableUseDetailsList - create_time。';
|
COMMENT ON COLUMN billiards_dwd.dwd_groupbuy_redemption.create_time IS '【说明】时间/日期字段,用于记录业务时间与统计口径对齐。 【示例】2025-11-09 23:35:57(时间/日期字段,用于记录业务时间与统计口径对齐)。 【ODS来源】group_buy_redemption_records - create_time。 【JSON字段】group_buy_redemption_records.json - data.siteTableUseDetailsList - create_time。';
|
||||||
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS dwd_groupbuy_redemption_Ex (
|
CREATE TABLE IF NOT EXISTS dwd_groupbuy_redemption_ex (
|
||||||
redemption_id BIGINT,
|
redemption_id BIGINT,
|
||||||
site_name VARCHAR(64),
|
site_name VARCHAR(64),
|
||||||
table_name VARCHAR(64),
|
table_name VARCHAR(64),
|
||||||
@@ -1676,6 +1721,13 @@ CREATE TABLE IF NOT EXISTS dwd_groupbuy_redemption_Ex (
|
|||||||
salesman_role_id BIGINT,
|
salesman_role_id BIGINT,
|
||||||
salesman_org_id BIGINT,
|
salesman_org_id BIGINT,
|
||||||
ledger_group_name VARCHAR(128),
|
ledger_group_name VARCHAR(128),
|
||||||
|
table_share_money NUMERIC(18,2),
|
||||||
|
table_service_share_money NUMERIC(18,2),
|
||||||
|
goods_share_money NUMERIC(18,2),
|
||||||
|
good_service_share_money NUMERIC(18,2),
|
||||||
|
assistant_share_money NUMERIC(18,2),
|
||||||
|
assistant_service_share_money NUMERIC(18,2),
|
||||||
|
recharge_share_money NUMERIC(18,2),
|
||||||
PRIMARY KEY (redemption_id)
|
PRIMARY KEY (redemption_id)
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -1750,7 +1802,7 @@ COMMENT ON COLUMN billiards_dwd.dwd_platform_coupon_redemption.create_time IS '
|
|||||||
COMMENT ON COLUMN billiards_dwd.dwd_platform_coupon_redemption.consume_time IS '【说明】时间/日期字段,用于记录业务时间与统计口径对齐。 【示例】2025-11-09 23:41:04(时间/日期字段,用于记录业务时间与统计口径对齐)。 【ODS来源】platform_coupon_redemption_records - consume_time。 【JSON字段】platform_coupon_redemption_records.json - $ - consume_time。';
|
COMMENT ON COLUMN billiards_dwd.dwd_platform_coupon_redemption.consume_time IS '【说明】时间/日期字段,用于记录业务时间与统计口径对齐。 【示例】2025-11-09 23:41:04(时间/日期字段,用于记录业务时间与统计口径对齐)。 【ODS来源】platform_coupon_redemption_records - consume_time。 【JSON字段】platform_coupon_redemption_records.json - $ - consume_time。';
|
||||||
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS dwd_platform_coupon_redemption_Ex (
|
CREATE TABLE IF NOT EXISTS dwd_platform_coupon_redemption_ex (
|
||||||
platform_coupon_redemption_id BIGINT,
|
platform_coupon_redemption_id BIGINT,
|
||||||
coupon_cover VARCHAR(255),
|
coupon_cover VARCHAR(255),
|
||||||
coupon_remark VARCHAR(255),
|
coupon_remark VARCHAR(255),
|
||||||
@@ -1814,7 +1866,7 @@ COMMENT ON COLUMN billiards_dwd.dwd_recharge_order.create_time IS '【说明】
|
|||||||
COMMENT ON COLUMN billiards_dwd.dwd_recharge_order.pay_time IS '【说明】时间/日期字段,用于记录业务时间与统计口径对齐。 【示例】NULL(时间/日期字段,用于记录业务时间与统计口径对齐)。 【ODS来源】recharge_settlements - paytime。 【JSON字段】recharge_settlements.json - $ - paytime。';
|
COMMENT ON COLUMN billiards_dwd.dwd_recharge_order.pay_time IS '【说明】时间/日期字段,用于记录业务时间与统计口径对齐。 【示例】NULL(时间/日期字段,用于记录业务时间与统计口径对齐)。 【ODS来源】recharge_settlements - paytime。 【JSON字段】recharge_settlements.json - $ - paytime。';
|
||||||
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS dwd_recharge_order_Ex (
|
CREATE TABLE IF NOT EXISTS dwd_recharge_order_ex (
|
||||||
recharge_order_id BIGINT,
|
recharge_order_id BIGINT,
|
||||||
site_name_snapshot TEXT,
|
site_name_snapshot TEXT,
|
||||||
settle_status INTEGER,
|
settle_status INTEGER,
|
||||||
@@ -1919,6 +1971,7 @@ CREATE TABLE IF NOT EXISTS dwd_payment (
|
|||||||
create_time TIMESTAMPTZ,
|
create_time TIMESTAMPTZ,
|
||||||
pay_time TIMESTAMPTZ,
|
pay_time TIMESTAMPTZ,
|
||||||
pay_date DATE,
|
pay_date DATE,
|
||||||
|
tenant_id BIGINT,
|
||||||
PRIMARY KEY (payment_id)
|
PRIMARY KEY (payment_id)
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -1967,7 +2020,7 @@ COMMENT ON COLUMN billiards_dwd.dwd_refund.member_id IS '【说明】标识类 I
|
|||||||
COMMENT ON COLUMN billiards_dwd.dwd_refund.member_card_id IS '【说明】标识类 ID 字段,用于关联/定位相关实体。 【示例】0(标识类 ID 字段,用于关联/定位相关实体)。 【ODS来源】refund_transactions - member_card_id。 【JSON字段】refund_transactions.json - $ - member_card_id。';
|
COMMENT ON COLUMN billiards_dwd.dwd_refund.member_card_id IS '【说明】标识类 ID 字段,用于关联/定位相关实体。 【示例】0(标识类 ID 字段,用于关联/定位相关实体)。 【ODS来源】refund_transactions - member_card_id。 【JSON字段】refund_transactions.json - $ - member_card_id。';
|
||||||
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS dwd_refund_Ex (
|
CREATE TABLE IF NOT EXISTS dwd_refund_ex (
|
||||||
refund_id BIGINT,
|
refund_id BIGINT,
|
||||||
tenant_name VARCHAR(64),
|
tenant_name VARCHAR(64),
|
||||||
pay_sn BIGINT,
|
pay_sn BIGINT,
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
347
etl_billiards/database/seed_dws_config.sql
Normal file
347
etl_billiards/database/seed_dws_config.sql
Normal file
@@ -0,0 +1,347 @@
|
|||||||
|
-- =============================================================================
|
||||||
|
-- DWS 配置表初始数据
|
||||||
|
-- 版本: v3.0
|
||||||
|
-- 创建日期: 2026-02-01
|
||||||
|
-- 描述: 初始化配置表数据,包含绩效档位、等级定价、奖金规则、区域分类、技能映射
|
||||||
|
-- =============================================================================
|
||||||
|
|
||||||
|
-- =============================================================================
|
||||||
|
-- 1. cfg_performance_tier - 绩效档位配置(6档 + 新入职)
|
||||||
|
-- 数据来源:DWS 数据库处理需求.md 第35-41行
|
||||||
|
-- 档位原因考虑 总业绩小时数阈值 专业课抽成(元/小时) 打赏课抽成 次月休假(天)
|
||||||
|
-- 0档 淘汰压力 H <100 28 50% 3
|
||||||
|
-- 1档 及格档(重点激励) 100≤ H <130 18 40% 4
|
||||||
|
-- 2档 良好档(重点激励) 130≤ H <160 15 38% 4
|
||||||
|
-- 3档 优秀档 160≤ H <190 13 35% 5
|
||||||
|
-- 4档 卓越加速档(高端人才倾斜) 190≤ H <220 10 33% 6
|
||||||
|
-- 5档 冠军加速档(高端人才倾斜) H ≥220 8 30% 休假自由
|
||||||
|
-- =============================================================================
|
||||||
|
TRUNCATE TABLE billiards_dws.cfg_performance_tier RESTART IDENTITY CASCADE;
|
||||||
|
|
||||||
|
INSERT INTO billiards_dws.cfg_performance_tier (
|
||||||
|
tier_code, tier_name, tier_level,
|
||||||
|
min_hours, max_hours,
|
||||||
|
base_deduction, bonus_deduction_ratio, vacation_days, vacation_unlimited,
|
||||||
|
is_new_hire_tier, effective_from, effective_to, description
|
||||||
|
) VALUES
|
||||||
|
-- 0档 淘汰压力: H<100, 专业课抽成28元/小时, 打赏课抽成50%, 休假3天
|
||||||
|
('T0', '0档-淘汰压力', 0,
|
||||||
|
0, 100,
|
||||||
|
28.00, 0.50, 3, FALSE,
|
||||||
|
FALSE, '2000-01-01', '9999-12-31',
|
||||||
|
'淘汰压力档:H<100,专业课抽成28元/小时,打赏课抽成50%,休假3天'),
|
||||||
|
|
||||||
|
-- 1档 及格档: 100≤H<130, 专业课抽成18元/小时, 打赏课抽成40%, 休假4天
|
||||||
|
('T1', '1档-及格档', 1,
|
||||||
|
100, 130,
|
||||||
|
18.00, 0.40, 4, FALSE,
|
||||||
|
FALSE, '2000-01-01', '9999-12-31',
|
||||||
|
'及格档(重点激励):100≤H<130,专业课抽成18元/小时,打赏课抽成40%,休假4天'),
|
||||||
|
|
||||||
|
-- 2档 良好档: 130≤H<160, 专业课抽成15元/小时, 打赏课抽成38%, 休假4天
|
||||||
|
('T2', '2档-良好档', 2,
|
||||||
|
130, 160,
|
||||||
|
15.00, 0.38, 4, FALSE,
|
||||||
|
FALSE, '2000-01-01', '9999-12-31',
|
||||||
|
'良好档(重点激励):130≤H<160,专业课抽成15元/小时,打赏课抽成38%,休假4天'),
|
||||||
|
|
||||||
|
-- 3档 优秀档: 160≤H<190, 专业课抽成13元/小时, 打赏课抽成35%, 休假5天
|
||||||
|
('T3', '3档-优秀档', 3,
|
||||||
|
160, 190,
|
||||||
|
13.00, 0.35, 5, FALSE,
|
||||||
|
FALSE, '2000-01-01', '9999-12-31',
|
||||||
|
'优秀档:160≤H<190,专业课抽成13元/小时,打赏课抽成35%,休假5天'),
|
||||||
|
|
||||||
|
-- 4档 卓越加速档: 190≤H<220, 专业课抽成10元/小时, 打赏课抽成33%, 休假6天
|
||||||
|
('T4', '4档-卓越加速档', 4,
|
||||||
|
190, 220,
|
||||||
|
10.00, 0.33, 6, FALSE,
|
||||||
|
FALSE, '2000-01-01', '9999-12-31',
|
||||||
|
'卓越加速档(高端人才倾斜):190≤H<220,专业课抽成10元/小时,打赏课抽成33%,休假6天'),
|
||||||
|
|
||||||
|
-- 5档 冠军加速档: H≥220, 专业课抽成8元/小时, 打赏课抽成30%, 休假自由
|
||||||
|
('T5', '5档-冠军加速档', 5,
|
||||||
|
220, NULL,
|
||||||
|
8.00, 0.30, 0, TRUE,
|
||||||
|
FALSE, '2000-01-01', '9999-12-31',
|
||||||
|
'冠军加速档(高端人才倾斜):H≥220,专业课抽成8元/小时,打赏课抽成30%,休假自由'),
|
||||||
|
|
||||||
|
-- 新入职档位: 首月特殊处理(按1档标准,但不参与排名)
|
||||||
|
('NEW', '新入职档位', -1,
|
||||||
|
0, NULL,
|
||||||
|
18.00, 0.40, 4, FALSE,
|
||||||
|
TRUE, '2000-01-01', '9999-12-31',
|
||||||
|
'新入职:月1日0点后入职者首月使用,按1档抽成标准,不参与排名奖金');
|
||||||
|
|
||||||
|
|
||||||
|
-- =============================================================================
|
||||||
|
-- 2. cfg_assistant_level_price - 助教等级定价
|
||||||
|
-- 说明:
|
||||||
|
-- - level_code 来自 dim_assistant.assistant_level
|
||||||
|
-- - 8=助教管理, 10=初级, 20=中级, 30=高级, 40=星级
|
||||||
|
-- - 价格为客户支付价格(对外价格),助教收入=客户支付-档位抽成
|
||||||
|
-- - 数据来源:DWS 数据库处理需求.md
|
||||||
|
-- =============================================================================
|
||||||
|
TRUNCATE TABLE billiards_dws.cfg_assistant_level_price RESTART IDENTITY CASCADE;
|
||||||
|
|
||||||
|
INSERT INTO billiards_dws.cfg_assistant_level_price (
|
||||||
|
level_code, level_name,
|
||||||
|
base_course_price, bonus_course_price,
|
||||||
|
effective_from, effective_to, description
|
||||||
|
) VALUES
|
||||||
|
-- 初级助教:基础课对客户收费98元/小时
|
||||||
|
(10, '初级',
|
||||||
|
98.00, 190.00,
|
||||||
|
'2000-01-01', '9999-12-31',
|
||||||
|
'初级助教:基础课98元/时,附加课190元/时(客户支付价格)'),
|
||||||
|
|
||||||
|
-- 中级助教:基础课对客户收费108元/小时
|
||||||
|
(20, '中级',
|
||||||
|
108.00, 190.00,
|
||||||
|
'2000-01-01', '9999-12-31',
|
||||||
|
'中级助教:基础课108元/时,附加课190元/时(客户支付价格)'),
|
||||||
|
|
||||||
|
-- 高级助教:基础课对客户收费118元/小时
|
||||||
|
(30, '高级',
|
||||||
|
118.00, 190.00,
|
||||||
|
'2000-01-01', '9999-12-31',
|
||||||
|
'高级助教:基础课118元/时,附加课190元/时(客户支付价格)'),
|
||||||
|
|
||||||
|
-- 星级助教:基础课对客户收费138元/小时
|
||||||
|
(40, '星级',
|
||||||
|
138.00, 190.00,
|
||||||
|
'2000-01-01', '9999-12-31',
|
||||||
|
'星级助教:基础课138元/时,附加课190元/时(客户支付价格)'),
|
||||||
|
|
||||||
|
-- 助教管理:level_code=8,通常不参与客户服务计费,此处设置默认值
|
||||||
|
(8, '助教管理',
|
||||||
|
98.00, 190.00,
|
||||||
|
'2000-01-01', '9999-12-31',
|
||||||
|
'助教管理:不参与客户服务计费,默认按初级价格');
|
||||||
|
|
||||||
|
|
||||||
|
-- =============================================================================
|
||||||
|
-- 3. cfg_bonus_rules - 奖金规则配置
|
||||||
|
-- 说明:
|
||||||
|
-- - SPRINT: 冲刺奖金,按业绩小时数阈值,不累计取最高档
|
||||||
|
-- - TOP_RANK: Top3排名奖金,按有效业绩排名,并列都算
|
||||||
|
-- =============================================================================
|
||||||
|
TRUNCATE TABLE billiards_dws.cfg_bonus_rules RESTART IDENTITY CASCADE;
|
||||||
|
|
||||||
|
INSERT INTO billiards_dws.cfg_bonus_rules (
|
||||||
|
rule_type, rule_code, rule_name,
|
||||||
|
threshold_hours, rank_position, bonus_amount,
|
||||||
|
is_cumulative, priority,
|
||||||
|
effective_from, effective_to, description
|
||||||
|
) VALUES
|
||||||
|
-- 冲刺奖金: H>=190 得300元
|
||||||
|
('SPRINT', 'SPRINT_190', '冲刺奖金190',
|
||||||
|
190.00, NULL, 300.00,
|
||||||
|
FALSE, 1,
|
||||||
|
'2000-01-01', '9999-12-31',
|
||||||
|
'业绩≥190小时,获得300元冲刺奖金(不累计)'),
|
||||||
|
|
||||||
|
-- 冲刺奖金: H>=220 得800元(优先级更高,覆盖190档)
|
||||||
|
('SPRINT', 'SPRINT_220', '冲刺奖金220',
|
||||||
|
220.00, NULL, 800.00,
|
||||||
|
FALSE, 2,
|
||||||
|
'2000-01-01', '9999-12-31',
|
||||||
|
'业绩≥220小时,获得800元冲刺奖金(覆盖190档)'),
|
||||||
|
|
||||||
|
-- Top1排名奖金: 1000元
|
||||||
|
('TOP_RANK', 'TOP_1', 'Top1排名奖金',
|
||||||
|
NULL, 1, 1000.00,
|
||||||
|
FALSE, 0,
|
||||||
|
'2000-01-01', '9999-12-31',
|
||||||
|
'月度排名第一,获得1000元(并列都算)'),
|
||||||
|
|
||||||
|
-- Top2排名奖金: 600元
|
||||||
|
('TOP_RANK', 'TOP_2', 'Top2排名奖金',
|
||||||
|
NULL, 2, 600.00,
|
||||||
|
FALSE, 0,
|
||||||
|
'2000-01-01', '9999-12-31',
|
||||||
|
'月度排名第二,获得600元(并列都算)'),
|
||||||
|
|
||||||
|
-- Top3排名奖金: 400元
|
||||||
|
('TOP_RANK', 'TOP_3', 'Top3排名奖金',
|
||||||
|
NULL, 3, 400.00,
|
||||||
|
FALSE, 0,
|
||||||
|
'2000-01-01', '9999-12-31',
|
||||||
|
'月度排名第三,获得400元(并列都算)');
|
||||||
|
|
||||||
|
|
||||||
|
-- =============================================================================
|
||||||
|
-- 4. cfg_area_category - 台区分类映射
|
||||||
|
-- 说明:
|
||||||
|
-- - 将 dim_table.site_table_area_name 映射到财务报表区域分类
|
||||||
|
-- - 映射规则: 精确匹配 > 模糊匹配 > 默认兜底
|
||||||
|
-- - 数据来源: BD_manual_dim_table.md 中的 site_table_area_name 实际分布
|
||||||
|
-- 分类设计:
|
||||||
|
-- - BILLIARD: 台球散台(A区/B区/C区/TV台)
|
||||||
|
-- - BILLIARD_VIP: 台球VIP包厢
|
||||||
|
-- - SNOOKER: 斯诺克区
|
||||||
|
-- - MAHJONG: 麻将区
|
||||||
|
-- - KTV: K歌/KTV
|
||||||
|
-- - SPECIAL: 特殊(补时长等)
|
||||||
|
-- - OTHER: 其他
|
||||||
|
-- =============================================================================
|
||||||
|
TRUNCATE TABLE billiards_dws.cfg_area_category RESTART IDENTITY CASCADE;
|
||||||
|
|
||||||
|
INSERT INTO billiards_dws.cfg_area_category (
|
||||||
|
source_area_name, category_code, category_name,
|
||||||
|
match_type, match_priority, is_active, description
|
||||||
|
) VALUES
|
||||||
|
-- ============ 台球散台区(精确匹配)============
|
||||||
|
('A区', 'BILLIARD', '台球散台',
|
||||||
|
'EXACT', 10, TRUE, '台球散台:A区(18台)- 中八/追分'),
|
||||||
|
('B区', 'BILLIARD', '台球散台',
|
||||||
|
'EXACT', 10, TRUE, '台球散台:B区(15台)- 中八/追分'),
|
||||||
|
('C区', 'BILLIARD', '台球散台',
|
||||||
|
'EXACT', 10, TRUE, '台球散台:C区(6台)- 中八/追分'),
|
||||||
|
('TV台', 'BILLIARD', '台球散台',
|
||||||
|
'EXACT', 10, TRUE, '台球散台:TV台(1台)- 中八/追分'),
|
||||||
|
|
||||||
|
-- ============ 台球VIP包厢(精确匹配)============
|
||||||
|
('VIP包厢', 'BILLIARD_VIP', '台球VIP',
|
||||||
|
'EXACT', 10, TRUE, '台球VIP:VIP包厢(4台)- V1-V4中八, V5斯诺克'),
|
||||||
|
|
||||||
|
-- ============ 斯诺克区(精确匹配)============
|
||||||
|
('斯诺克区', 'SNOOKER', '斯诺克',
|
||||||
|
'EXACT', 10, TRUE, '斯诺克:斯诺克区(4台)'),
|
||||||
|
|
||||||
|
-- ============ 麻将区(精确匹配)============
|
||||||
|
('麻将房', 'MAHJONG', '麻将棋牌',
|
||||||
|
'EXACT', 10, TRUE, '麻将棋牌:麻将房(5台)'),
|
||||||
|
('M7', 'MAHJONG', '麻将棋牌',
|
||||||
|
'EXACT', 10, TRUE, '麻将棋牌:M7(2台)'),
|
||||||
|
('M8', 'MAHJONG', '麻将棋牌',
|
||||||
|
'EXACT', 10, TRUE, '麻将棋牌:M8(1台)'),
|
||||||
|
('666', 'MAHJONG', '麻将棋牌',
|
||||||
|
'EXACT', 10, TRUE, '麻将棋牌:666(2台)'),
|
||||||
|
('发财', 'MAHJONG', '麻将棋牌',
|
||||||
|
'EXACT', 10, TRUE, '麻将棋牌:发财(1台)'),
|
||||||
|
|
||||||
|
-- ============ KTV/K包(精确匹配)============
|
||||||
|
('K包', 'KTV', 'K歌娱乐',
|
||||||
|
'EXACT', 10, TRUE, 'K歌娱乐:K包(4台)'),
|
||||||
|
('k包活动区', 'KTV', 'K歌娱乐',
|
||||||
|
'EXACT', 10, TRUE, 'K歌娱乐:k包活动区(2台)'),
|
||||||
|
('幸会158', 'KTV', 'K歌娱乐',
|
||||||
|
'EXACT', 10, TRUE, 'K歌娱乐:幸会158(2台)'),
|
||||||
|
|
||||||
|
-- ============ 特殊区域(精确匹配)============
|
||||||
|
('补时长', 'SPECIAL', '补时长',
|
||||||
|
'EXACT', 10, TRUE, '特殊:补时长(7台)- 用于时长补录'),
|
||||||
|
|
||||||
|
-- ============ 模糊匹配规则(优先级较低)============
|
||||||
|
('%VIP%', 'BILLIARD_VIP', '台球VIP',
|
||||||
|
'LIKE', 50, TRUE, '模糊匹配:包含"VIP"的区域'),
|
||||||
|
('%斯诺克%', 'SNOOKER', '斯诺克',
|
||||||
|
'LIKE', 50, TRUE, '模糊匹配:包含"斯诺克"的区域'),
|
||||||
|
('%麻将%', 'MAHJONG', '麻将棋牌',
|
||||||
|
'LIKE', 50, TRUE, '模糊匹配:包含"麻将"的区域'),
|
||||||
|
('%K包%', 'KTV', 'K歌娱乐',
|
||||||
|
'LIKE', 50, TRUE, '模糊匹配:包含"K包"的区域'),
|
||||||
|
('%KTV%', 'KTV', 'K歌娱乐',
|
||||||
|
'LIKE', 50, TRUE, '模糊匹配:包含"KTV"的区域'),
|
||||||
|
|
||||||
|
-- ============ 默认兜底(优先级最低)============
|
||||||
|
('DEFAULT', 'OTHER', '其他',
|
||||||
|
'DEFAULT', 999, TRUE, '兜底规则:无法匹配的区域归入其他');
|
||||||
|
|
||||||
|
|
||||||
|
-- =============================================================================
|
||||||
|
-- 5. cfg_skill_type - 技能→课程类型映射
|
||||||
|
-- 说明:
|
||||||
|
-- - 将 skill_id 映射到课程类型
|
||||||
|
-- - 基础课/陪打: skill_id = 2791903611396869
|
||||||
|
-- - 附加课/超休: skill_id = 2807440316432197
|
||||||
|
-- - 避免依赖 skill_name 文本匹配
|
||||||
|
-- =============================================================================
|
||||||
|
TRUNCATE TABLE billiards_dws.cfg_skill_type RESTART IDENTITY CASCADE;
|
||||||
|
|
||||||
|
INSERT INTO billiards_dws.cfg_skill_type (
|
||||||
|
skill_id, skill_name,
|
||||||
|
course_type_code, course_type_name,
|
||||||
|
is_active, description
|
||||||
|
) VALUES
|
||||||
|
-- 基础课/陪打
|
||||||
|
(2791903611396869, '台球基础陪打',
|
||||||
|
'BASE', '基础课',
|
||||||
|
TRUE, '基础课:陪打服务,按助教等级计价'),
|
||||||
|
|
||||||
|
-- 附加课/超休
|
||||||
|
(2807440316432197, '台球超休服务',
|
||||||
|
'BONUS', '附加课',
|
||||||
|
TRUE, '附加课:超休/激励课,固定50元/小时'),
|
||||||
|
|
||||||
|
-- 包厢课(如有)
|
||||||
|
(2807440316432198, '包厢服务',
|
||||||
|
'BASE', '基础课',
|
||||||
|
TRUE, '包厢服务:归入基础课统计');
|
||||||
|
|
||||||
|
|
||||||
|
-- =============================================================================
|
||||||
|
-- 6. 优惠类型配置(用于财务优惠明细分析)
|
||||||
|
-- 说明: 定义各类优惠的代码和名称,便于后续分析
|
||||||
|
-- =============================================================================
|
||||||
|
-- 此配置作为代码常量使用,不单独建表
|
||||||
|
-- GROUPBUY - 团购优惠
|
||||||
|
-- VIP - 会员折扣
|
||||||
|
-- GIFT_CARD - 赠送卡抵扣
|
||||||
|
-- MANUAL - 手动调整
|
||||||
|
-- ROUNDING - 抹零
|
||||||
|
-- BIG_CUSTOMER - 大客户优惠(待抽样分析确认)
|
||||||
|
-- OTHER - 其他优惠
|
||||||
|
|
||||||
|
|
||||||
|
-- =============================================================================
|
||||||
|
-- 7. 支出类型配置(用于Excel导入)
|
||||||
|
-- 说明: 定义各类支出的代码和名称
|
||||||
|
-- =============================================================================
|
||||||
|
-- 此配置作为代码常量使用,不单独建表
|
||||||
|
-- RENT - 房租
|
||||||
|
-- UTILITY - 水电费
|
||||||
|
-- PROPERTY - 物业费
|
||||||
|
-- SALARY - 工资
|
||||||
|
-- REIMBURSE - 报销
|
||||||
|
-- PLATFORM_FEE - 平台服务费
|
||||||
|
-- OTHER - 其他支出
|
||||||
|
|
||||||
|
|
||||||
|
-- =============================================================================
|
||||||
|
-- 8. 平台类型配置(用于Excel导入)
|
||||||
|
-- 说明: 定义各平台的代码和名称
|
||||||
|
-- =============================================================================
|
||||||
|
-- 此配置作为代码常量使用,不单独建表
|
||||||
|
-- MEITUAN - 美团
|
||||||
|
-- DOUYIN - 抖音
|
||||||
|
-- DIANPING - 大众点评
|
||||||
|
-- OTHER - 其他平台
|
||||||
|
|
||||||
|
|
||||||
|
-- =============================================================================
|
||||||
|
-- 验证数据插入
|
||||||
|
-- =============================================================================
|
||||||
|
DO $$
|
||||||
|
DECLARE
|
||||||
|
v_tier_count INTEGER;
|
||||||
|
v_price_count INTEGER;
|
||||||
|
v_bonus_count INTEGER;
|
||||||
|
v_area_count INTEGER;
|
||||||
|
v_skill_count INTEGER;
|
||||||
|
BEGIN
|
||||||
|
SELECT COUNT(*) INTO v_tier_count FROM billiards_dws.cfg_performance_tier;
|
||||||
|
SELECT COUNT(*) INTO v_price_count FROM billiards_dws.cfg_assistant_level_price;
|
||||||
|
SELECT COUNT(*) INTO v_bonus_count FROM billiards_dws.cfg_bonus_rules;
|
||||||
|
SELECT COUNT(*) INTO v_area_count FROM billiards_dws.cfg_area_category;
|
||||||
|
SELECT COUNT(*) INTO v_skill_count FROM billiards_dws.cfg_skill_type;
|
||||||
|
|
||||||
|
RAISE NOTICE '配置数据初始化完成:';
|
||||||
|
RAISE NOTICE ' - cfg_performance_tier: % 条', v_tier_count;
|
||||||
|
RAISE NOTICE ' - cfg_assistant_level_price: % 条', v_price_count;
|
||||||
|
RAISE NOTICE ' - cfg_bonus_rules: % 条', v_bonus_count;
|
||||||
|
RAISE NOTICE ' - cfg_area_category: % 条', v_area_count;
|
||||||
|
RAISE NOTICE ' - cfg_skill_type: % 条', v_skill_count;
|
||||||
|
END;
|
||||||
|
$$;
|
||||||
118
etl_billiards/database/seed_index_parameters.sql
Normal file
118
etl_billiards/database/seed_index_parameters.sql
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
-- =============================================================================
|
||||||
|
-- 指数算法参数初始化脚本
|
||||||
|
-- 版本: v1.0
|
||||||
|
-- 创建日期: 2026-02-03
|
||||||
|
-- 描述: 为客户召回指数和客户-助教亲密指数插入默认参数
|
||||||
|
-- =============================================================================
|
||||||
|
|
||||||
|
-- 清空旧数据(如果需要重新初始化)
|
||||||
|
-- DELETE FROM billiards_dws.cfg_index_parameters WHERE index_type IN ('RECALL', 'INTIMACY');
|
||||||
|
|
||||||
|
-- =============================================================================
|
||||||
|
-- 客户召回指数(RECALL)参数
|
||||||
|
-- =============================================================================
|
||||||
|
|
||||||
|
INSERT INTO billiards_dws.cfg_index_parameters
|
||||||
|
(index_type, param_name, param_value, description, effective_from)
|
||||||
|
VALUES
|
||||||
|
-- 基础参数
|
||||||
|
('RECALL', 'lookback_days', 60, '回溯窗口(天):分析近60天的数据', CURRENT_DATE),
|
||||||
|
('RECALL', 'sigma_min', 2.0, '波动下限(天):避免σ过小导致超期过敏', CURRENT_DATE),
|
||||||
|
|
||||||
|
-- 半衰期参数
|
||||||
|
('RECALL', 'halflife_new', 7, '新客户半衰期(天):7天后新客户加分衰减到一半', CURRENT_DATE),
|
||||||
|
('RECALL', 'halflife_recharge', 10, '刚充值半衰期(天):10天后充值加分衰减到一半', CURRENT_DATE),
|
||||||
|
|
||||||
|
-- 权重参数
|
||||||
|
('RECALL', 'weight_overdue', 3.0, '超期紧急性权重:主导因子,建议3.0', CURRENT_DATE),
|
||||||
|
('RECALL', 'weight_new', 1.0, '新客户权重:建议1.0', CURRENT_DATE),
|
||||||
|
('RECALL', 'weight_recharge', 1.0, '刚充值权重:建议1.0', CURRENT_DATE),
|
||||||
|
('RECALL', 'weight_hot', 1.0, '热度断档权重:建议1.0', CURRENT_DATE),
|
||||||
|
|
||||||
|
-- 映射参数
|
||||||
|
('RECALL', 'percentile_lower', 5, '下锚分位数:5分位', CURRENT_DATE),
|
||||||
|
('RECALL', 'percentile_upper', 95, '上锚分位数:95分位', CURRENT_DATE),
|
||||||
|
('RECALL', 'ewma_alpha', 0.2, 'EWMA平滑系数:越小越平滑,建议0.2', CURRENT_DATE)
|
||||||
|
ON CONFLICT (index_type, param_name, effective_from) DO UPDATE SET
|
||||||
|
param_value = EXCLUDED.param_value,
|
||||||
|
description = EXCLUDED.description,
|
||||||
|
updated_at = NOW();
|
||||||
|
|
||||||
|
|
||||||
|
-- =============================================================================
|
||||||
|
-- 客户-助教亲密指数(INTIMACY)参数
|
||||||
|
-- =============================================================================
|
||||||
|
|
||||||
|
INSERT INTO billiards_dws.cfg_index_parameters
|
||||||
|
(index_type, param_name, param_value, description, effective_from)
|
||||||
|
VALUES
|
||||||
|
-- 基础参数
|
||||||
|
('INTIMACY', 'lookback_days', 60, '回溯窗口(天):分析近60天的数据', CURRENT_DATE),
|
||||||
|
('INTIMACY', 'session_merge_hours', 4, '会话合并间隔(小时):4小时内的服务算同次', CURRENT_DATE),
|
||||||
|
('INTIMACY', 'recharge_attribute_hours', 1, '充值归因窗口(小时):服务结束后1小时内', CURRENT_DATE),
|
||||||
|
('INTIMACY', 'amount_base', 500, '金额压缩基准(元):选门店常见充值档位', CURRENT_DATE),
|
||||||
|
('INTIMACY', 'incentive_weight', 1.5, '附加课权重倍数:附加课=基础课的1.5倍', CURRENT_DATE),
|
||||||
|
|
||||||
|
-- 半衰期参数
|
||||||
|
('INTIMACY', 'halflife_session', 14, '会话衰减半衰期(天):14天后权重衰减到一半', CURRENT_DATE),
|
||||||
|
('INTIMACY', 'halflife_last', 10, '最近一次半衰期(天):10天后温度衰减到一半', CURRENT_DATE),
|
||||||
|
('INTIMACY', 'halflife_recharge', 21, '充值衰减半衰期(天):21天后充值贡献衰减到一半', CURRENT_DATE),
|
||||||
|
('INTIMACY', 'halflife_short', 7, '短期激增检测半衰期(天):用于Burst检测', CURRENT_DATE),
|
||||||
|
('INTIMACY', 'halflife_long', 30, '长期激增检测半衰期(天):用于Burst检测', CURRENT_DATE),
|
||||||
|
|
||||||
|
-- 权重参数
|
||||||
|
('INTIMACY', 'weight_frequency', 2.0, '频次权重:建议2.0', CURRENT_DATE),
|
||||||
|
('INTIMACY', 'weight_recency', 1.5, '最近一次权重:建议1.5', CURRENT_DATE),
|
||||||
|
('INTIMACY', 'weight_recharge', 2.0, '归因充值权重:建议2.0', CURRENT_DATE),
|
||||||
|
('INTIMACY', 'weight_duration', 0.5, '时长权重:次要因素,建议0.5', CURRENT_DATE),
|
||||||
|
('INTIMACY', 'burst_gamma', 0.6, '激增放大系数γ:建议0.6', CURRENT_DATE),
|
||||||
|
|
||||||
|
-- 映射参数
|
||||||
|
('INTIMACY', 'percentile_lower', 5, '下锚分位数:5分位', CURRENT_DATE),
|
||||||
|
('INTIMACY', 'percentile_upper', 95, '上锚分位数:95分位', CURRENT_DATE),
|
||||||
|
('INTIMACY', 'ewma_alpha', 0.2, 'EWMA平滑系数:越小越平滑,建议0.2', CURRENT_DATE)
|
||||||
|
ON CONFLICT (index_type, param_name, effective_from) DO UPDATE SET
|
||||||
|
param_value = EXCLUDED.param_value,
|
||||||
|
description = EXCLUDED.description,
|
||||||
|
updated_at = NOW();
|
||||||
|
|
||||||
|
|
||||||
|
-- =============================================================================
|
||||||
|
-- 验证
|
||||||
|
-- =============================================================================
|
||||||
|
|
||||||
|
-- 检查参数数量
|
||||||
|
DO $$
|
||||||
|
DECLARE
|
||||||
|
recall_count INTEGER;
|
||||||
|
intimacy_count INTEGER;
|
||||||
|
BEGIN
|
||||||
|
SELECT COUNT(*) INTO recall_count
|
||||||
|
FROM billiards_dws.cfg_index_parameters
|
||||||
|
WHERE index_type = 'RECALL';
|
||||||
|
|
||||||
|
SELECT COUNT(*) INTO intimacy_count
|
||||||
|
FROM billiards_dws.cfg_index_parameters
|
||||||
|
WHERE index_type = 'INTIMACY';
|
||||||
|
|
||||||
|
RAISE NOTICE '召回指数参数数量: %', recall_count;
|
||||||
|
RAISE NOTICE '亲密指数参数数量: %', intimacy_count;
|
||||||
|
|
||||||
|
IF recall_count < 10 THEN
|
||||||
|
RAISE WARNING '召回指数参数不完整,期望至少10个';
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
IF intimacy_count < 15 THEN
|
||||||
|
RAISE WARNING '亲密指数参数不完整,期望至少15个';
|
||||||
|
END IF;
|
||||||
|
END $$;
|
||||||
|
|
||||||
|
-- 显示所有参数
|
||||||
|
SELECT
|
||||||
|
index_type,
|
||||||
|
param_name,
|
||||||
|
param_value,
|
||||||
|
description,
|
||||||
|
effective_from
|
||||||
|
FROM billiards_dws.cfg_index_parameters
|
||||||
|
ORDER BY index_type, param_name;
|
||||||
@@ -5,17 +5,91 @@
|
|||||||
本文档描述在ETL已完成的DWD层数据基础上对DWS层的数据处理:
|
本文档描述在ETL已完成的DWD层数据基础上对DWS层的数据处理:
|
||||||
- 完成对DWS层数据库的处理,即数据库设计,成果为DDL的SQL语句。
|
- 完成对DWS层数据库的处理,即数据库设计,成果为DDL的SQL语句。
|
||||||
- 数据读取处理到落库,即DWD读取,Python处理,SQL写入。
|
- 数据读取处理到落库,即DWD读取,Python处理,SQL写入。
|
||||||
|
- 在动手之前,先出一个任务计划文档,写明事实的具体技术方案细节。
|
||||||
|
|
||||||
文档更多聚焦业务描述,你需要使用专业技能,使用面向对象编程OOP思想,完成程序设计直至代码完成:
|
文档更多聚焦业务描述,你需要使用专业技能,使用面向对象编程OOP思想,完成程序设计直至代码完成:
|
||||||
- 参考.\README.md 了解现在项目现状。
|
- 参考.\README.md 了解现在项目现状。
|
||||||
- 参考.\etl_billiards\docs\dwd_main_tables_dictionary.md 了解 DWD的schema的表和字段(若与数据库有出路,则以当前数据库为准。)
|
- 参考.\etl_billiards\docs 了解 DWD的schema的表和字段。
|
||||||
- SQL和Python代码需要详尽的,高密度的中文注释。
|
- SQL和Python代码需要详尽的,高密度的中文注释。
|
||||||
- 完成内容,需要详尽高密度的补充至.\README.md,以方便后续维护。
|
- 完成内容,需要详尽高密度的补充至.\README.md,以方便后续维护。
|
||||||
- DWS的表与表的字段 参考.\etl_billiards\docs\dwd_main_tables_dictionary.md 完成类似的数据库文档,方便后续维护。
|
- DWS的表与表的字段 参考.\etl_billiards\docs\dwd_main_tables_dictionary.md 完成类似的数据库文档,方便后续维护。
|
||||||
- 注意中文编码需求。
|
- 注意中文编码需求。
|
||||||
|
|
||||||
## 具体需求
|
## 通用需求
|
||||||
### 助教视角
|
### 数据分层
|
||||||
- 需要
|
我希望使用互联网软件的业内通用方法,将数据按照更新时间分为4层,以符合业务层面的查询效率速度。
|
||||||
|
- 第一层:回溯两天前到当前数据。
|
||||||
|
- 第二层:回溯1个月前到当前数据。
|
||||||
|
- 第三层:回溯3个月前到当前数据。
|
||||||
|
- 第四层:全量数据。
|
||||||
|
- 需要有配套的机制及时添加删除整理数据。
|
||||||
|
|
||||||
|
### 统计注意
|
||||||
|
当统计一些数据时,注意口径,数据有效性标识。举例:
|
||||||
|
- 计算助教业绩/工资时,需要参考助教废除表,相关业务数据的影响。
|
||||||
|
- 计算助教业绩/工资时,注意辨别 助教课 附加课影响。
|
||||||
|
|
||||||
|
## 业务需求
|
||||||
|
### 系统设置
|
||||||
|
- 助教新的绩效考核和工资结算方式更新为以下算法,影响工资结算和财务账务方面的统计核算,相关内容需要落库,以方便后续调整。还要标记执行时间(如哪个月执行哪个标准等),执行相关结算和计算逻辑。:
|
||||||
|
档位原因考虑 总业绩小时数阈值 专业课抽成(元/小时) 打赏课抽成 次月休假(天)
|
||||||
|
0档 淘汰压力 H <100 28 50% 3
|
||||||
|
1档 及格档(重点激励) 100≤ H <130 18 40% 4
|
||||||
|
2档 良好档(重点激励) 130≤ H <160 15 38% 4
|
||||||
|
3档 优秀档 160≤ H <190 13 35% 5
|
||||||
|
4档 卓越加速档(高端人才倾斜) 190≤ H <220 10 33% 6
|
||||||
|
5档 冠军加速档(高端人才倾斜) H ≥220 8 30% 休假自由
|
||||||
|
|
||||||
|
*课程分为2种(dwd_assistant_service_log表的skill_name):
|
||||||
|
基础课:又名 专业课 上桌 上钟,是为客户提供台球助教陪练的课程,按时长统计。精确到分钟。
|
||||||
|
附加课:又名 超休 激励 打赏,是客户支付较为高昂的价格,买断整小时与助教外出。
|
||||||
|
总业绩小时数阈值指基础课和附加课总和。
|
||||||
|
|
||||||
|
各级别助教(dim_assistant表的level)基础课,对客户收费:初级 98元/小时;中级 108元/小时;高级 118元/小时;星级 138元/小时;
|
||||||
|
附加课对客户收费统一为190元/小时。
|
||||||
|
|
||||||
|
充值提成:
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
冲刺奖 达成奖金
|
||||||
|
当月 H ≥ 190:300 元
|
||||||
|
当月 H ≥ 220:800 元(与上条不叠加,取高)
|
||||||
|
|
||||||
|
额外奖金:
|
||||||
|
冲刺奖 达成奖金
|
||||||
|
当月 H ≥ 190:300 元
|
||||||
|
当月 H ≥ 220:800 元(与上条不叠加,取高)
|
||||||
|
|
||||||
|
Top3 奖金:
|
||||||
|
第1名:1000 元
|
||||||
|
第2名:600 元
|
||||||
|
第3名:400 元
|
||||||
|
|
||||||
|
规则:
|
||||||
|
1、过档后,所有时长按新档位进行计算。
|
||||||
|
举例,当前某中级助教已完成185小时,基础课占170小时,附加课15小时。则该月工资计算方法:
|
||||||
|
170*(108-13)+15*(1-0.35)
|
||||||
|
|
||||||
|
2、本月新入职助教,定档方案:
|
||||||
|
按照日均*30的总业绩小时数定档。
|
||||||
|
在该25日之后入职的新助教,最高定档至3档。
|
||||||
|
该折算仅用于定档,不适用于“冲刺奖”和“Top3奖”的计算口径。
|
||||||
|
|
||||||
|
### 助教维度
|
||||||
|
以每个助教个体的视角
|
||||||
|
- 我要知道我的业绩档位,历史月份与本月档位进度,档位影响的收入单价。及相邻月份的变化。
|
||||||
|
- 我要知道我的有效业绩:历史月份与本月的 基础课课时,激励课课时,全部课课时。相邻月份的变化。
|
||||||
|
- 我要知道我的收入:历史月份与本月的收入(注意助教等级,业绩档位,课程种类等因素的总和计算)。相邻月份的变化。
|
||||||
|
- 我要知道我的客户情况:过去7天、10天、15天、30天、60天、90天 的跨度进行统计,我服务过(基础课+附加课)的客户数据,并关联每次服务的 时间 时长 台桌 分类 等详细信息。
|
||||||
|
|
||||||
|
### 客户维度
|
||||||
|
统计每个客户的信息
|
||||||
|
- 我要知道每个客户:过去7天、10天、15天、30天、60天、90天 的跨度进行统计,来店消费情况,并关联每次服务的 时间 食品饮品 时长 台桌 分类 助教服务 等详细信息。
|
||||||
|
|
||||||
|
|
||||||
|
### 财务维度
|
||||||
|
财务维度的需求(已经落到原型图需求级别了),见财务页面需求.md
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
1122
etl_billiards/docs/DWS_任务计划_v1.md
Normal file
1122
etl_billiards/docs/DWS_任务计划_v1.md
Normal file
File diff suppressed because it is too large
Load Diff
1353
etl_billiards/docs/DWS_任务计划_v2.md
Normal file
1353
etl_billiards/docs/DWS_任务计划_v2.md
Normal file
File diff suppressed because it is too large
Load Diff
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
> 生成时间:2026-01-28
|
> 生成时间:2026-01-28
|
||||||
|
|
||||||
|
|
||||||
## 表信息
|
## 表信息
|
||||||
|
|
||||||
| 属性 | 值 |
|
| 属性 | 值 |
|
||||||
@@ -69,6 +68,21 @@
|
|||||||
|
|
||||||
## 使用说明
|
## 使用说明
|
||||||
|
|
||||||
|
**版本与最新值**
|
||||||
|
本表为 SCD2 维度表,版本字段:scd2_start_time / scd2_end_time / scd2_is_current / scd2_version。
|
||||||
|
|
||||||
|
- 最新版本:scd2_is_current = 1
|
||||||
|
- 按业务主键取最新:按 scd2_start_time 倒序
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- 取某业务主键的最新版本
|
||||||
|
SELECT *
|
||||||
|
FROM billiards_dwd.dim_assistant_ex
|
||||||
|
WHERE assistant_id = <value>
|
||||||
|
ORDER BY scd2_start_time DESC
|
||||||
|
LIMIT 1;
|
||||||
|
```
|
||||||
|
**使用示例**
|
||||||
```sql
|
```sql
|
||||||
-- 关联主表与扩展表
|
-- 关联主表与扩展表
|
||||||
SELECT m.*, e.*
|
SELECT m.*, e.*
|
||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
> 生成时间:2026-01-28
|
> 生成时间:2026-01-28
|
||||||
|
|
||||||
|
|
||||||
## 表信息
|
## 表信息
|
||||||
|
|
||||||
| 属性 | 值 |
|
| 属性 | 值 |
|
||||||
@@ -38,10 +37,11 @@
|
|||||||
| 18 | effective_status | INTEGER | YES | | 生效状态。**枚举值**: 1(24)=有效, 3(10)=失效 **[待确认]** |
|
| 18 | effective_status | INTEGER | YES | | 生效状态。**枚举值**: 1(24)=有效, 3(10)=失效 **[待确认]** |
|
||||||
| 19 | max_selectable_categories | INTEGER | YES | | 最大可选分类数(当前数据全为 0) |
|
| 19 | max_selectable_categories | INTEGER | YES | | 最大可选分类数(当前数据全为 0) |
|
||||||
| 20 | creator_name | VARCHAR(100) | YES | | 创建人。**样本值**: "店长:郑丽珊", "管理员:郑丽珊" |
|
| 20 | creator_name | VARCHAR(100) | YES | | 创建人。**样本值**: "店长:郑丽珊", "管理员:郑丽珊" |
|
||||||
| 21 | scd2_start_time | TIMESTAMPTZ | NO | PK | SCD2 版本生效时间 |
|
| 21 | tenant_coupon_sale_order_item_id | BIGINT | YES | | 租户券销售订单项 ID |
|
||||||
| 22 | scd2_end_time | TIMESTAMPTZ | YES | | SCD2 版本失效时间 |
|
| 22 | scd2_start_time | TIMESTAMPTZ | NO | PK | SCD2 版本生效时间 |
|
||||||
| 23 | scd2_is_current | INTEGER | YES | | 当前版本标记 |
|
| 23 | scd2_end_time | TIMESTAMPTZ | YES | | SCD2 版本失效时间 |
|
||||||
| 24 | scd2_version | INTEGER | YES | | 版本号 |
|
| 24 | scd2_is_current | INTEGER | YES | | 当前版本标记 |
|
||||||
|
| 25 | scd2_version | INTEGER | YES | | 版本号 |
|
||||||
|
|
||||||
## 样本数据
|
## 样本数据
|
||||||
|
|
||||||
@@ -53,6 +53,21 @@
|
|||||||
|
|
||||||
## 使用说明
|
## 使用说明
|
||||||
|
|
||||||
|
**版本与最新值**
|
||||||
|
本表为 SCD2 维度表,版本字段:scd2_start_time / scd2_end_time / scd2_is_current / scd2_version。
|
||||||
|
|
||||||
|
- 最新版本:scd2_is_current = 1
|
||||||
|
- 按业务主键取最新:按 scd2_start_time 倒序
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- 取某业务主键的最新版本
|
||||||
|
SELECT *
|
||||||
|
FROM billiards_dwd.dim_groupbuy_package_ex
|
||||||
|
WHERE groupbuy_package_id = <value>
|
||||||
|
ORDER BY scd2_start_time DESC
|
||||||
|
LIMIT 1;
|
||||||
|
```
|
||||||
|
**使用示例**
|
||||||
```sql
|
```sql
|
||||||
-- 关联主表与扩展表
|
-- 关联主表与扩展表
|
||||||
SELECT m.package_name, m.duration_seconds, e.start_clock, e.end_clock, e.effective_status
|
SELECT m.package_name, m.duration_seconds, e.start_clock, e.end_clock, e.effective_status
|
||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
> 生成时间:2026-01-28
|
> 生成时间:2026-01-28
|
||||||
|
|
||||||
|
|
||||||
## 表信息
|
## 表信息
|
||||||
|
|
||||||
| 属性 | 值 |
|
| 属性 | 值 |
|
||||||
@@ -70,13 +69,33 @@
|
|||||||
| 50 | goodscategoryid | TEXT | YES | | 可用商品分类 ID 列表(当前数据全为空) |
|
| 50 | goodscategoryid | TEXT | YES | | 可用商品分类 ID 列表(当前数据全为空) |
|
||||||
| 51 | pdassisnatlevel | TEXT | YES | | 陪打助教等级限制。**当前值**: "{}" |
|
| 51 | pdassisnatlevel | TEXT | YES | | 陪打助教等级限制。**当前值**: "{}" |
|
||||||
| 52 | cxassisnatlevel | TEXT | YES | | 促销助教等级限制。**当前值**: "{}" |
|
| 52 | cxassisnatlevel | TEXT | YES | | 促销助教等级限制。**当前值**: "{}" |
|
||||||
| 53 | scd2_start_time | TIMESTAMPTZ | NO | PK | SCD2 版本生效时间 |
|
| 53 | able_share_member_discount | BOOLEAN | YES | | 是否可共享会员折扣 |
|
||||||
| 54 | scd2_end_time | TIMESTAMPTZ | YES | | SCD2 版本失效时间 |
|
| 54 | electricity_deduct_radio | NUMERIC(18,4) | YES | | 电费扣减比例 |
|
||||||
| 55 | scd2_is_current | INTEGER | YES | | 当前版本标记 |
|
| 55 | electricity_discount | NUMERIC(18,4) | YES | | 电费折扣 |
|
||||||
| 56 | scd2_version | INTEGER | YES | | 版本号 |
|
| 56 | electricity_card_deduct | BOOLEAN | YES | | 电费卡扣 |
|
||||||
|
| 57 | recharge_freeze_balance | NUMERIC(18,2) | YES | | 充值冻结余额 |
|
||||||
|
| 58 | scd2_start_time | TIMESTAMPTZ | NO | PK | SCD2 版本生效时间 |
|
||||||
|
| 59 | scd2_end_time | TIMESTAMPTZ | YES | | SCD2 版本失效时间 |
|
||||||
|
| 60 | scd2_is_current | INTEGER | YES | | 当前版本标记 |
|
||||||
|
| 61 | scd2_version | INTEGER | YES | | 版本号 |
|
||||||
|
|
||||||
## 使用说明
|
## 使用说明
|
||||||
|
|
||||||
|
**版本与最新值**
|
||||||
|
本表为 SCD2 维度表,版本字段:scd2_start_time / scd2_end_time / scd2_is_current / scd2_version。
|
||||||
|
|
||||||
|
- 最新版本:scd2_is_current = 1
|
||||||
|
- 按业务主键取最新:按 scd2_start_time 倒序
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- 取某业务主键的最新版本
|
||||||
|
SELECT *
|
||||||
|
FROM billiards_dwd.dim_member_card_account_ex
|
||||||
|
WHERE member_card_id = <value>
|
||||||
|
ORDER BY scd2_start_time DESC
|
||||||
|
LIMIT 1;
|
||||||
|
```
|
||||||
|
**使用示例**
|
||||||
```sql
|
```sql
|
||||||
-- 关联查询卡片及折扣配置
|
-- 关联查询卡片及折扣配置
|
||||||
SELECT
|
SELECT
|
||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
> 生成时间:2026-01-28
|
> 生成时间:2026-01-28
|
||||||
|
|
||||||
|
|
||||||
## 表信息
|
## 表信息
|
||||||
|
|
||||||
| 属性 | 值 |
|
| 属性 | 值 |
|
||||||
@@ -25,10 +24,13 @@
|
|||||||
| 5 | growth_value | NUMERIC(18,2) | YES | | 成长值 |
|
| 5 | growth_value | NUMERIC(18,2) | YES | | 成长值 |
|
||||||
| 6 | user_status | INTEGER | YES | | 用户状态。**枚举值**: 1(556)=正常 |
|
| 6 | user_status | INTEGER | YES | | 用户状态。**枚举值**: 1(556)=正常 |
|
||||||
| 7 | status | INTEGER | YES | | 账户状态。**枚举值**: 1(490)=正常, 3(66)=**[含义待确认]** |
|
| 7 | status | INTEGER | YES | | 账户状态。**枚举值**: 1(490)=正常, 3(66)=**[含义待确认]** |
|
||||||
| 8 | scd2_start_time | TIMESTAMPTZ | NO | PK | SCD2 版本生效时间 |
|
| 8 | person_tenant_org_id | BIGINT | YES | | 人员租户组织 ID |
|
||||||
| 9 | scd2_end_time | TIMESTAMPTZ | YES | | SCD2 版本失效时间 |
|
| 9 | person_tenant_org_name | TEXT | YES | | 人员租户组织名称 |
|
||||||
| 10 | scd2_is_current | INTEGER | YES | | 当前版本标记 |
|
| 10 | register_source | TEXT | YES | | 注册来源 |
|
||||||
| 11 | scd2_version | INTEGER | YES | | 版本号 |
|
| 11 | scd2_start_time | TIMESTAMPTZ | NO | PK | SCD2 版本生效时间 |
|
||||||
|
| 12 | scd2_end_time | TIMESTAMPTZ | YES | | SCD2 版本失效时间 |
|
||||||
|
| 13 | scd2_is_current | INTEGER | YES | | 当前版本标记 |
|
||||||
|
| 14 | scd2_version | INTEGER | YES | | 版本号 |
|
||||||
|
|
||||||
## 样本数据
|
## 样本数据
|
||||||
|
|
||||||
@@ -40,6 +42,21 @@
|
|||||||
|
|
||||||
## 使用说明
|
## 使用说明
|
||||||
|
|
||||||
|
**版本与最新值**
|
||||||
|
本表为 SCD2 维度表,版本字段:scd2_start_time / scd2_end_time / scd2_is_current / scd2_version。
|
||||||
|
|
||||||
|
- 最新版本:scd2_is_current = 1
|
||||||
|
- 按业务主键取最新:按 scd2_start_time 倒序
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- 取某业务主键的最新版本
|
||||||
|
SELECT *
|
||||||
|
FROM billiards_dwd.dim_member_ex
|
||||||
|
WHERE member_id = <value>
|
||||||
|
ORDER BY scd2_start_time DESC
|
||||||
|
LIMIT 1;
|
||||||
|
```
|
||||||
|
**使用示例**
|
||||||
```sql
|
```sql
|
||||||
-- 关联主表与扩展表
|
-- 关联主表与扩展表
|
||||||
SELECT m.*, e.point, e.growth_value, e.status
|
SELECT m.*, e.point, e.growth_value, e.status
|
||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
> 生成时间:2026-01-28
|
> 生成时间:2026-01-28
|
||||||
|
|
||||||
|
|
||||||
## 表信息
|
## 表信息
|
||||||
|
|
||||||
| 属性 | 值 |
|
| 属性 | 值 |
|
||||||
@@ -46,6 +45,21 @@
|
|||||||
|
|
||||||
## 使用说明
|
## 使用说明
|
||||||
|
|
||||||
|
**版本与最新值**
|
||||||
|
本表为 SCD2 维度表,版本字段:scd2_start_time / scd2_end_time / scd2_is_current / scd2_version。
|
||||||
|
|
||||||
|
- 最新版本:scd2_is_current = 1
|
||||||
|
- 按业务主键取最新:按 scd2_start_time 倒序
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- 取某业务主键的最新版本
|
||||||
|
SELECT *
|
||||||
|
FROM billiards_dwd.dim_site_ex
|
||||||
|
WHERE site_id = <value>
|
||||||
|
ORDER BY scd2_start_time DESC
|
||||||
|
LIMIT 1;
|
||||||
|
```
|
||||||
|
**使用示例**
|
||||||
```sql
|
```sql
|
||||||
-- 关联主表与扩展表
|
-- 关联主表与扩展表
|
||||||
SELECT m.*, e.*
|
SELECT m.*, e.*
|
||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
> 生成时间:2026-01-28
|
> 生成时间:2026-01-28
|
||||||
|
|
||||||
|
|
||||||
## 表信息
|
## 表信息
|
||||||
|
|
||||||
| 属性 | 值 |
|
| 属性 | 值 |
|
||||||
@@ -51,6 +50,21 @@
|
|||||||
|
|
||||||
## 使用说明
|
## 使用说明
|
||||||
|
|
||||||
|
**版本与最新值**
|
||||||
|
本表为 SCD2 维度表,版本字段:scd2_start_time / scd2_end_time / scd2_is_current / scd2_version。
|
||||||
|
|
||||||
|
- 最新版本:scd2_is_current = 1
|
||||||
|
- 按业务主键取最新:按 scd2_start_time 倒序
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- 取某业务主键的最新版本
|
||||||
|
SELECT *
|
||||||
|
FROM billiards_dwd.dim_store_goods_ex
|
||||||
|
WHERE site_goods_id = <value>
|
||||||
|
ORDER BY scd2_start_time DESC
|
||||||
|
LIMIT 1;
|
||||||
|
```
|
||||||
|
**使用示例**
|
||||||
```sql
|
```sql
|
||||||
-- 关联主表与扩展表
|
-- 关联主表与扩展表
|
||||||
SELECT m.goods_name, m.sale_price, m.sale_qty, e.unit, e.stock_qty, e.cost_price
|
SELECT m.goods_name, m.sale_price, m.sale_qty, e.unit, e.stock_qty, e.cost_price
|
||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
> 生成时间:2026-01-28
|
> 生成时间:2026-01-28
|
||||||
|
|
||||||
|
|
||||||
## 表信息
|
## 表信息
|
||||||
|
|
||||||
| 属性 | 值 |
|
| 属性 | 值 |
|
||||||
@@ -39,6 +38,21 @@
|
|||||||
|
|
||||||
## 使用说明
|
## 使用说明
|
||||||
|
|
||||||
|
**版本与最新值**
|
||||||
|
本表为 SCD2 维度表,版本字段:scd2_start_time / scd2_end_time / scd2_is_current / scd2_version。
|
||||||
|
|
||||||
|
- 最新版本:scd2_is_current = 1
|
||||||
|
- 按业务主键取最新:按 scd2_start_time 倒序
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- 取某业务主键的最新版本
|
||||||
|
SELECT *
|
||||||
|
FROM billiards_dwd.dim_table_ex
|
||||||
|
WHERE table_id = <value>
|
||||||
|
ORDER BY scd2_start_time DESC
|
||||||
|
LIMIT 1;
|
||||||
|
```
|
||||||
|
**使用示例**
|
||||||
```sql
|
```sql
|
||||||
-- 关联主表与扩展表
|
-- 关联主表与扩展表
|
||||||
SELECT m.table_name, m.site_table_area_name, e.show_status, e.table_status
|
SELECT m.table_name, m.site_table_area_name, e.show_status, e.table_status
|
||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
> 生成时间:2026-01-28
|
> 生成时间:2026-01-28
|
||||||
|
|
||||||
|
|
||||||
## 表信息
|
## 表信息
|
||||||
|
|
||||||
| 属性 | 值 |
|
| 属性 | 值 |
|
||||||
@@ -43,6 +42,21 @@
|
|||||||
|
|
||||||
## 使用说明
|
## 使用说明
|
||||||
|
|
||||||
|
**版本与最新值**
|
||||||
|
本表为 SCD2 维度表,版本字段:scd2_start_time / scd2_end_time / scd2_is_current / scd2_version。
|
||||||
|
|
||||||
|
- 最新版本:scd2_is_current = 1
|
||||||
|
- 按业务主键取最新:按 scd2_start_time 倒序
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- 取某业务主键的最新版本
|
||||||
|
SELECT *
|
||||||
|
FROM billiards_dwd.dim_tenant_goods_ex
|
||||||
|
WHERE tenant_goods_id = <value>
|
||||||
|
ORDER BY scd2_start_time DESC
|
||||||
|
LIMIT 1;
|
||||||
|
```
|
||||||
|
**使用示例**
|
||||||
```sql
|
```sql
|
||||||
-- 关联主表与扩展表
|
-- 关联主表与扩展表
|
||||||
SELECT m.goods_name, m.market_price, e.cost_price, e.min_discount_price
|
SELECT m.goods_name, m.market_price, e.cost_price, e.min_discount_price
|
||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
> 生成时间:2026-01-28
|
> 生成时间:2026-01-28
|
||||||
|
|
||||||
|
|
||||||
## 表信息
|
## 表信息
|
||||||
|
|
||||||
| 属性 | 值 |
|
| 属性 | 值 |
|
||||||
@@ -48,9 +47,23 @@
|
|||||||
| 28 | get_grade_times | INTEGER | YES | | 评分次数(当前数据全为 0) |
|
| 28 | get_grade_times | INTEGER | YES | | 评分次数(当前数据全为 0) |
|
||||||
| 29 | grade_status | INTEGER | YES | | 评分状态。**枚举值**: 0(216)=未评分, 1(4787)=已评分 **[待确认]** |
|
| 29 | grade_status | INTEGER | YES | | 评分状态。**枚举值**: 0(216)=未评分, 1(4787)=已评分 **[待确认]** |
|
||||||
| 30 | composite_grade_time | TIMESTAMPTZ | YES | | 评分时间 |
|
| 30 | composite_grade_time | TIMESTAMPTZ | YES | | 评分时间 |
|
||||||
|
| 31 | assistant_team_name | TEXT | YES | | 助教团队名称 |
|
||||||
|
|
||||||
## 使用说明
|
## 使用说明
|
||||||
|
|
||||||
|
**版本与最新值**
|
||||||
|
本表为事实表,无 SCD2 版本字段。
|
||||||
|
|
||||||
|
- 可用时间字段:ledger_start_time, ledger_end_time, composite_grade_time
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- 取最新一条(按时间字段倒序)
|
||||||
|
SELECT *
|
||||||
|
FROM billiards_dwd.dwd_assistant_service_log_ex
|
||||||
|
ORDER BY ledger_start_time DESC NULLS LAST
|
||||||
|
LIMIT 1;
|
||||||
|
```
|
||||||
|
**使用示例**
|
||||||
```sql
|
```sql
|
||||||
-- 关联主表与扩展表
|
-- 关联主表与扩展表
|
||||||
SELECT m.nickname, m.ledger_amount, e.table_name, e.assistant_name, e.grade_status
|
SELECT m.nickname, m.ledger_amount, e.table_name, e.assistant_name, e.grade_status
|
||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
> 生成时间:2026-01-28
|
> 生成时间:2026-01-28
|
||||||
|
|
||||||
|
|
||||||
## 表信息
|
## 表信息
|
||||||
|
|
||||||
| 属性 | 值 |
|
| 属性 | 值 |
|
||||||
@@ -46,4 +45,18 @@
|
|||||||
|
|
||||||
## 使用说明
|
## 使用说明
|
||||||
|
|
||||||
|
**版本与最新值**
|
||||||
|
本表为事实表,无 SCD2 版本字段。
|
||||||
|
|
||||||
|
- 主表可用时间字段:create_time
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- 取最新一条(按主表时间字段)
|
||||||
|
SELECT e.*
|
||||||
|
FROM billiards_dwd.dwd_assistant_trash_event m
|
||||||
|
JOIN billiards_dwd.dwd_assistant_trash_event_ex e ON m.assistant_trash_event_id = e.assistant_trash_event_id
|
||||||
|
ORDER BY m.create_time DESC NULLS LAST
|
||||||
|
LIMIT 1;
|
||||||
|
```
|
||||||
|
**使用示例**
|
||||||
与主表 `dwd_assistant_trash_event` 通过 `assistant_trash_event_id` 关联,提供台桌和台区名称信息。
|
与主表 `dwd_assistant_trash_event` 通过 `assistant_trash_event_id` 关联,提供台桌和台区名称信息。
|
||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
> 生成时间:2026-01-28
|
> 生成时间:2026-01-28
|
||||||
|
|
||||||
|
|
||||||
## 表信息
|
## 表信息
|
||||||
|
|
||||||
| 属性 | 值 |
|
| 属性 | 值 |
|
||||||
@@ -39,6 +38,13 @@
|
|||||||
| 19 | salesman_role_id | BIGINT | YES | | 销售员角色 ID(当前数据全为 0) |
|
| 19 | salesman_role_id | BIGINT | YES | | 销售员角色 ID(当前数据全为 0) |
|
||||||
| 20 | salesman_org_id | BIGINT | YES | | 销售员组织 ID(当前数据全为 0) |
|
| 20 | salesman_org_id | BIGINT | YES | | 销售员组织 ID(当前数据全为 0) |
|
||||||
| 21 | ledger_group_name | VARCHAR(128) | YES | | 账本分组名称(当前数据全为 NULL) |
|
| 21 | ledger_group_name | VARCHAR(128) | YES | | 账本分组名称(当前数据全为 NULL) |
|
||||||
|
| 22 | table_share_money | NUMERIC(18,2) | YES | | 台费分摊金额 |
|
||||||
|
| 23 | table_service_share_money | NUMERIC(18,2) | YES | | 台费服务分摊金额 |
|
||||||
|
| 24 | goods_share_money | NUMERIC(18,2) | YES | | 商品分摊金额 |
|
||||||
|
| 25 | good_service_share_money | NUMERIC(18,2) | YES | | 商品服务分摊金额 |
|
||||||
|
| 26 | assistant_share_money | NUMERIC(18,2) | YES | | 助教分摊金额 |
|
||||||
|
| 27 | assistant_service_share_money | NUMERIC(18,2) | YES | | 助教服务分摊金额 |
|
||||||
|
| 28 | recharge_share_money | NUMERIC(18,2) | YES | | 充值分摊金额 |
|
||||||
|
|
||||||
## 台区核销分布
|
## 台区核销分布
|
||||||
|
|
||||||
@@ -59,4 +65,18 @@
|
|||||||
|
|
||||||
## 使用说明
|
## 使用说明
|
||||||
|
|
||||||
|
**版本与最新值**
|
||||||
|
本表为事实表,无 SCD2 版本字段。
|
||||||
|
|
||||||
|
- 主表可用时间字段:create_time
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- 取最新一条(按主表时间字段)
|
||||||
|
SELECT e.*
|
||||||
|
FROM billiards_dwd.dwd_groupbuy_redemption m
|
||||||
|
JOIN billiards_dwd.dwd_groupbuy_redemption_ex e ON m.redemption_id = e.redemption_id
|
||||||
|
ORDER BY m.create_time DESC NULLS LAST
|
||||||
|
LIMIT 1;
|
||||||
|
```
|
||||||
|
**使用示例**
|
||||||
与主表 `dwd_groupbuy_redemption` 通过 `redemption_id` 关联,提供门店、台桌名称、操作员等扩展信息。
|
与主表 `dwd_groupbuy_redemption` 通过 `redemption_id` 关联,提供门店、台桌名称、操作员等扩展信息。
|
||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
> 生成时间:2026-01-28
|
> 生成时间:2026-01-28
|
||||||
|
|
||||||
|
|
||||||
## 表信息
|
## 表信息
|
||||||
|
|
||||||
| 属性 | 值 |
|
| 属性 | 值 |
|
||||||
@@ -24,6 +23,7 @@
|
|||||||
| 4 | refund_amount | NUMERIC(18,2) | YES | | 退款金额 |
|
| 4 | refund_amount | NUMERIC(18,2) | YES | | 退款金额 |
|
||||||
| 5 | operator_id | BIGINT | YES | | 操作员 ID |
|
| 5 | operator_id | BIGINT | YES | | 操作员 ID |
|
||||||
| 6 | operator_name | VARCHAR(64) | YES | | 操作员名称。**枚举值**: "收银员:郑丽珊"(4101), "店长:郑丽珊"(223), "管理员:郑丽珊"(153), "店长:蒋雨轩"(124), "店长:谢晓洪"(115), "店长:黄月柳"(29) |
|
| 6 | operator_name | VARCHAR(64) | YES | | 操作员名称。**枚举值**: "收银员:郑丽珊"(4101), "店长:郑丽珊"(223), "管理员:郑丽珊"(153), "店长:蒋雨轩"(124), "店长:谢晓洪"(115), "店长:黄月柳"(29) |
|
||||||
|
| 7 | principal_data | TEXT | YES | | 本金变动数据 |
|
||||||
|
|
||||||
## 操作员分布
|
## 操作员分布
|
||||||
|
|
||||||
@@ -46,4 +46,18 @@
|
|||||||
|
|
||||||
## 使用说明
|
## 使用说明
|
||||||
|
|
||||||
|
**版本与最新值**
|
||||||
|
本表为事实表,无 SCD2 版本字段。
|
||||||
|
|
||||||
|
- 主表可用时间字段:change_time
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- 取最新一条(按主表时间字段)
|
||||||
|
SELECT e.*
|
||||||
|
FROM billiards_dwd.dwd_member_balance_change m
|
||||||
|
JOIN billiards_dwd.dwd_member_balance_change_ex e ON m.balance_change_id = e.balance_change_id
|
||||||
|
ORDER BY m.change_time DESC NULLS LAST
|
||||||
|
LIMIT 1;
|
||||||
|
```
|
||||||
|
**使用示例**
|
||||||
与主表 `dwd_member_balance_change` 通过 `balance_change_id` 关联,提供操作员和门店名称等扩展信息。
|
与主表 `dwd_member_balance_change` 通过 `balance_change_id` 关联,提供操作员和门店名称等扩展信息。
|
||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
> 生成时间:2026-01-28
|
> 生成时间:2026-01-28
|
||||||
|
|
||||||
|
|
||||||
## 表信息
|
## 表信息
|
||||||
|
|
||||||
| 属性 | 值 |
|
| 属性 | 值 |
|
||||||
@@ -42,6 +41,19 @@
|
|||||||
|
|
||||||
## 使用说明
|
## 使用说明
|
||||||
|
|
||||||
与主表 `dwd_platform_coupon_redemption` 通过 `platform_coupon_redemption_id` 关联,提供操作员等扩展信息。
|
**版本与最新值**
|
||||||
|
本表为事实表,无 SCD2 版本字段。
|
||||||
|
|
||||||
|
- 主表可用时间字段:coupon_free_time, create_time, consume_time
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- 取最新一条(按主表时间字段)
|
||||||
|
SELECT e.*
|
||||||
|
FROM billiards_dwd.dwd_platform_coupon_redemption m
|
||||||
|
JOIN billiards_dwd.dwd_platform_coupon_redemption_ex e ON m.platform_coupon_redemption_id = e.platform_coupon_redemption_id
|
||||||
|
ORDER BY m.create_time DESC NULLS LAST
|
||||||
|
LIMIT 1;
|
||||||
|
```
|
||||||
|
**使用示例**
|
||||||
|
与主表 `dwd_platform_coupon_redemption` 通过 `platform_coupon_redemption_id` 关联,提供操作员等扩展信息。
|
||||||
**注意**: `coupon_remark` 字段在抖音渠道的核销记录中包含核验信息。
|
**注意**: `coupon_remark` 字段在抖音渠道的核销记录中包含核验信息。
|
||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
> 生成时间:2026-01-28
|
> 生成时间:2026-01-28
|
||||||
|
|
||||||
|
|
||||||
## 表信息
|
## 表信息
|
||||||
|
|
||||||
| 属性 | 值 |
|
| 属性 | 值 |
|
||||||
@@ -64,6 +63,18 @@
|
|||||||
|
|
||||||
## 使用说明
|
## 使用说明
|
||||||
|
|
||||||
与主表 `dwd_recharge_order` 通过 `recharge_order_id` 关联,提供操作员、各类金额明细等扩展信息。
|
**版本与最新值**
|
||||||
|
本表为事实表,无 SCD2 版本字段。
|
||||||
|
|
||||||
|
- 可用时间字段:revoke_time
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- 取最新一条(按时间字段倒序)
|
||||||
|
SELECT *
|
||||||
|
FROM billiards_dwd.dwd_recharge_order_ex
|
||||||
|
ORDER BY revoke_time DESC NULLS LAST
|
||||||
|
LIMIT 1;
|
||||||
|
```
|
||||||
|
**使用示例**
|
||||||
|
与主表 `dwd_recharge_order` 通过 `recharge_order_id` 关联,提供操作员、各类金额明细等扩展信息。
|
||||||
**注意**: 样本数据获取时因日期解析错误未能获取。
|
**注意**: 样本数据获取时因日期解析错误未能获取。
|
||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
> 生成时间:2026-01-28
|
> 生成时间:2026-01-28
|
||||||
|
|
||||||
|
|
||||||
## 表信息
|
## 表信息
|
||||||
|
|
||||||
| 属性 | 值 |
|
| 属性 | 值 |
|
||||||
@@ -48,4 +47,18 @@
|
|||||||
|
|
||||||
## 使用说明
|
## 使用说明
|
||||||
|
|
||||||
|
**版本与最新值**
|
||||||
|
本表为事实表,无 SCD2 版本字段。
|
||||||
|
|
||||||
|
- 主表可用时间字段:pay_time, create_time
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- 取最新一条(按主表时间字段)
|
||||||
|
SELECT e.*
|
||||||
|
FROM billiards_dwd.dwd_refund m
|
||||||
|
JOIN billiards_dwd.dwd_refund_ex e ON m.refund_id = e.refund_id
|
||||||
|
ORDER BY m.pay_time DESC NULLS LAST
|
||||||
|
LIMIT 1;
|
||||||
|
```
|
||||||
|
**使用示例**
|
||||||
与主表 `dwd_refund` 通过 `refund_id` 关联,提供退款状态和渠道等扩展信息。
|
与主表 `dwd_refund` 通过 `refund_id` 关联,提供退款状态和渠道等扩展信息。
|
||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
> 生成时间:2026-01-28
|
> 生成时间:2026-01-28
|
||||||
|
|
||||||
|
|
||||||
## 表信息
|
## 表信息
|
||||||
|
|
||||||
| 属性 | 值 |
|
| 属性 | 值 |
|
||||||
@@ -48,9 +47,23 @@
|
|||||||
| 28 | order_remark | VARCHAR(255) | YES | | 订单备注。**样本值**: "五折"(42), "轩哥"(24), "陈德韩"(7), "免台费"(3) |
|
| 28 | order_remark | VARCHAR(255) | YES | | 订单备注。**样本值**: "五折"(42), "轩哥"(24), "陈德韩"(7), "免台费"(3) |
|
||||||
| 29 | operator_id | BIGINT | YES | | 操作员 ID |
|
| 29 | operator_id | BIGINT | YES | | 操作员 ID |
|
||||||
| 30 | salesman_user_id | BIGINT | YES | | 销售员用户 ID(当前数据全为 0) |
|
| 30 | salesman_user_id | BIGINT | YES | | 销售员用户 ID(当前数据全为 0) |
|
||||||
|
| 31 | settle_list | JSONB | YES | | 结算明细列表(JSON数组) |
|
||||||
|
|
||||||
## 使用说明
|
## 使用说明
|
||||||
|
|
||||||
|
**版本与最新值**
|
||||||
|
本表为事实表,无 SCD2 版本字段。
|
||||||
|
|
||||||
|
- 可用时间字段:revoke_time
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- 取最新一条(按时间字段倒序)
|
||||||
|
SELECT *
|
||||||
|
FROM billiards_dwd.dwd_settlement_head_ex
|
||||||
|
ORDER BY revoke_time DESC NULLS LAST
|
||||||
|
LIMIT 1;
|
||||||
|
```
|
||||||
|
**使用示例**
|
||||||
```sql
|
```sql
|
||||||
-- 关联主表与扩展表
|
-- 关联主表与扩展表
|
||||||
SELECT
|
SELECT
|
||||||
@@ -59,7 +72,6 @@ SELECT
|
|||||||
FROM billiards_dwd.dwd_settlement_head m
|
FROM billiards_dwd.dwd_settlement_head m
|
||||||
JOIN billiards_dwd.dwd_settlement_head_ex e
|
JOIN billiards_dwd.dwd_settlement_head_ex e
|
||||||
ON m.order_settle_id = e.order_settle_id;
|
ON m.order_settle_id = e.order_settle_id;
|
||||||
|
|
||||||
-- 统计备注订单
|
-- 统计备注订单
|
||||||
SELECT order_remark, COUNT(*)
|
SELECT order_remark, COUNT(*)
|
||||||
FROM billiards_dwd.dwd_settlement_head_ex
|
FROM billiards_dwd.dwd_settlement_head_ex
|
||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
> 生成时间:2026-01-28
|
> 生成时间:2026-01-28
|
||||||
|
|
||||||
|
|
||||||
## 表信息
|
## 表信息
|
||||||
|
|
||||||
| 属性 | 值 |
|
| 属性 | 值 |
|
||||||
@@ -56,4 +55,18 @@
|
|||||||
|
|
||||||
## 使用说明
|
## 使用说明
|
||||||
|
|
||||||
|
**版本与最新值**
|
||||||
|
本表为事实表,无 SCD2 版本字段。
|
||||||
|
|
||||||
|
- 主表可用时间字段:create_time
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- 取最新一条(按主表时间字段)
|
||||||
|
SELECT e.*
|
||||||
|
FROM billiards_dwd.dwd_store_goods_sale m
|
||||||
|
JOIN billiards_dwd.dwd_store_goods_sale_ex e ON m.store_goods_sale_id = e.store_goods_sale_id
|
||||||
|
ORDER BY m.create_time DESC NULLS LAST
|
||||||
|
LIMIT 1;
|
||||||
|
```
|
||||||
|
**使用示例**
|
||||||
与主表 `dwd_store_goods_sale` 通过 `store_goods_sale_id` 关联,提供销售详情、折扣优惠等扩展信息。
|
与主表 `dwd_store_goods_sale` 通过 `store_goods_sale_id` 关联,提供销售详情、折扣优惠等扩展信息。
|
||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
> 生成时间:2026-01-28
|
> 生成时间:2026-01-28
|
||||||
|
|
||||||
|
|
||||||
## 表信息
|
## 表信息
|
||||||
|
|
||||||
| 属性 | 值 |
|
| 属性 | 值 |
|
||||||
@@ -26,6 +25,11 @@
|
|||||||
| 6 | operator_name | VARCHAR(64) | YES | | 操作员名称。**枚举值**: "收银员:郑丽珊"(2849) |
|
| 6 | operator_name | VARCHAR(64) | YES | | 操作员名称。**枚举值**: "收银员:郑丽珊"(2849) |
|
||||||
| 7 | applicant_id | BIGINT | YES | | 申请人 ID |
|
| 7 | applicant_id | BIGINT | YES | | 申请人 ID |
|
||||||
| 8 | operator_id | BIGINT | YES | | 操作员 ID |
|
| 8 | operator_id | BIGINT | YES | | 操作员 ID |
|
||||||
|
| 9 | area_type_id | BIGINT | YES | | 区域类型 ID |
|
||||||
|
| 10 | site_table_area_id | BIGINT | YES | | 门店台区 ID |
|
||||||
|
| 11 | site_table_area_name | TEXT | YES | | 门店台区名称 |
|
||||||
|
| 12 | site_name | TEXT | YES | | 门店名称 |
|
||||||
|
| 13 | tenant_name | TEXT | YES | | 租户名称 |
|
||||||
|
|
||||||
## 样本数据
|
## 样本数据
|
||||||
|
|
||||||
@@ -36,4 +40,18 @@
|
|||||||
|
|
||||||
## 使用说明
|
## 使用说明
|
||||||
|
|
||||||
|
**版本与最新值**
|
||||||
|
本表为事实表,无 SCD2 版本字段。
|
||||||
|
|
||||||
|
- 主表可用时间字段:adjust_time
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- 取最新一条(按主表时间字段)
|
||||||
|
SELECT e.*
|
||||||
|
FROM billiards_dwd.dwd_table_fee_adjust m
|
||||||
|
JOIN billiards_dwd.dwd_table_fee_adjust_ex e ON m.table_fee_adjust_id = e.table_fee_adjust_id
|
||||||
|
ORDER BY m.adjust_time DESC NULLS LAST
|
||||||
|
LIMIT 1;
|
||||||
|
```
|
||||||
|
**使用示例**
|
||||||
与主表 `dwd_table_fee_adjust` 通过 `table_fee_adjust_id` 关联,提供调整类型、申请人、操作员等扩展信息。
|
与主表 `dwd_table_fee_adjust` 通过 `table_fee_adjust_id` 关联,提供调整类型、申请人、操作员等扩展信息。
|
||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
> 生成时间:2026-01-28
|
> 生成时间:2026-01-28
|
||||||
|
|
||||||
|
|
||||||
## 表信息
|
## 表信息
|
||||||
|
|
||||||
| 属性 | 值 |
|
| 属性 | 值 |
|
||||||
@@ -30,6 +29,7 @@
|
|||||||
| 10 | operator_id | BIGINT | YES | | 操作员 ID。**枚举值**: 3个不同ID |
|
| 10 | operator_id | BIGINT | YES | | 操作员 ID。**枚举值**: 3个不同ID |
|
||||||
| 11 | salesman_user_id | BIGINT | YES | | 销售员用户 ID(当前数据全为 0) |
|
| 11 | salesman_user_id | BIGINT | YES | | 销售员用户 ID(当前数据全为 0) |
|
||||||
| 12 | salesman_org_id | BIGINT | YES | | 销售员组织 ID(当前数据全为 0) |
|
| 12 | salesman_org_id | BIGINT | YES | | 销售员组织 ID(当前数据全为 0) |
|
||||||
|
| 13 | order_consumption_type | INTEGER | YES | | 订单消费类型 |
|
||||||
|
|
||||||
## 样本数据
|
## 样本数据
|
||||||
|
|
||||||
@@ -41,4 +41,17 @@
|
|||||||
|
|
||||||
## 使用说明
|
## 使用说明
|
||||||
|
|
||||||
|
**版本与最新值**
|
||||||
|
本表为事实表,无 SCD2 版本字段。
|
||||||
|
|
||||||
|
- 可用时间字段:ledger_start_time, last_use_time
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- 取最新一条(按时间字段倒序)
|
||||||
|
SELECT *
|
||||||
|
FROM billiards_dwd.dwd_table_fee_log_ex
|
||||||
|
ORDER BY ledger_start_time DESC NULLS LAST
|
||||||
|
LIMIT 1;
|
||||||
|
```
|
||||||
|
**使用示例**
|
||||||
与主表 `dwd_table_fee_log` 通过 `table_fee_log_id` 关联,提供操作员和时间相关的扩展信息。
|
与主表 `dwd_table_fee_log` 通过 `table_fee_log_id` 关联,提供操作员和时间相关的扩展信息。
|
||||||
@@ -7,22 +7,21 @@
|
|||||||
## 概述
|
## 概述
|
||||||
|
|
||||||
`billiards_dwd` 是台球门店数据仓库的明细层(DWD),包含维度表(DIM)和事实表(DWD)。本 Schema 基于 SCD2 缓慢变化维度设计,支持历史数据追溯。
|
`billiards_dwd` 是台球门店数据仓库的明细层(DWD),包含维度表(DIM)和事实表(DWD)。本 Schema 基于 SCD2 缓慢变化维度设计,支持历史数据追溯。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 维度表 (Dimension Tables)
|
## 维度表 (Dimension Tables)
|
||||||
|
|
||||||
| 序号 | 表名 | 说明 | 主键 | 扩展表 | 文档链接 |
|
| 序号 | 表名 | 说明 | 主键 | 扩展表 | 文档链接 |
|
||||||
|------|------|------|------|--------|----------|
|
|------|------|------|------|--------|----------|
|
||||||
| 1 | dim_assistant | 助教信息 | assistant_id | dim_assistant_ex | [主表](BD_manual_dim_assistant.md) / [扩展表](BD_manual_dim_assistant_ex.md) |
|
| 1 | dim_assistant | 助教信息 | assistant_id, scd2_start_time | dim_assistant_ex | [主表](BD_manual_dim_assistant.md) / [扩展表](BD_manual_dim_assistant_ex.md) |
|
||||||
| 2 | dim_goods_category | 商品分类 | goods_category_id | 无 | [主表](BD_manual_dim_goods_category.md) |
|
| 2 | dim_goods_category | 商品分类 | category_id, scd2_start_time | 无 | [主表](BD_manual_dim_goods_category.md) |
|
||||||
| 3 | dim_groupbuy_package | 团购套餐 | groupbuy_package_id | dim_groupbuy_package_ex | [主表](BD_manual_dim_groupbuy_package.md) / [扩展表](BD_manual_dim_groupbuy_package_ex.md) |
|
| 3 | dim_groupbuy_package | 团购套餐 | groupbuy_package_id, scd2_start_time | dim_groupbuy_package_ex | [主表](BD_manual_dim_groupbuy_package.md) / [扩展表](BD_manual_dim_groupbuy_package_ex.md) |
|
||||||
| 4 | dim_member | 会员信息 | member_id | dim_member_ex | [主表](BD_manual_dim_member.md) / [扩展表](BD_manual_dim_member_ex.md) |
|
| 4 | dim_member | 会员信息 | member_id, scd2_start_time | dim_member_ex | [主表](BD_manual_dim_member.md) / [扩展表](BD_manual_dim_member_ex.md) |
|
||||||
| 5 | dim_member_card_account | 会员卡账户 | member_card_account_id | dim_member_card_account_ex | [主表](BD_manual_dim_member_card_account.md) / [扩展表](BD_manual_dim_member_card_account_ex.md) |
|
| 5 | dim_member_card_account | 会员卡账户 | member_card_id, scd2_start_time | dim_member_card_account_ex | [主表](BD_manual_dim_member_card_account.md) / [扩展表](BD_manual_dim_member_card_account_ex.md) |
|
||||||
| 6 | dim_site | 门店信息 | site_id | dim_site_ex | [主表](BD_manual_dim_site.md) / [扩展表](BD_manual_dim_site_ex.md) |
|
| 6 | dim_site | 门店信息 | site_id, scd2_start_time | dim_site_ex | [主表](BD_manual_dim_site.md) / [扩展表](BD_manual_dim_site_ex.md) |
|
||||||
| 7 | dim_store_goods | 门店商品 | store_goods_id | dim_store_goods_ex | [主表](BD_manual_dim_store_goods.md) / [扩展表](BD_manual_dim_store_goods_ex.md) |
|
| 7 | dim_store_goods | 门店商品 | site_goods_id, scd2_start_time | dim_store_goods_ex | [主表](BD_manual_dim_store_goods.md) / [扩展表](BD_manual_dim_store_goods_ex.md) |
|
||||||
| 8 | dim_table | 台桌信息 | table_id | dim_table_ex | [主表](BD_manual_dim_table.md) / [扩展表](BD_manual_dim_table_ex.md) |
|
| 8 | dim_table | 台桌信息 | table_id, scd2_start_time | dim_table_ex | [主表](BD_manual_dim_table.md) / [扩展表](BD_manual_dim_table_ex.md) |
|
||||||
| 9 | dim_tenant_goods | 租户商品 | tenant_goods_id | dim_tenant_goods_ex | [主表](BD_manual_dim_tenant_goods.md) / [扩展表](BD_manual_dim_tenant_goods_ex.md) |
|
| 9 | dim_tenant_goods | 租户商品 | tenant_goods_id, scd2_start_time | dim_tenant_goods_ex | [主表](BD_manual_dim_tenant_goods.md) / [扩展表](BD_manual_dim_tenant_goods_ex.md) |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -30,7 +29,7 @@
|
|||||||
|
|
||||||
| 序号 | 表名 | 说明 | 主键 | 扩展表 | 文档链接 |
|
| 序号 | 表名 | 说明 | 主键 | 扩展表 | 文档链接 |
|
||||||
|------|------|------|------|--------|----------|
|
|------|------|------|------|--------|----------|
|
||||||
| 1 | dwd_assistant_service_log | 助教服务流水 | assistant_service_log_id | dwd_assistant_service_log_ex | [主表](BD_manual_dwd_assistant_service_log.md) / [扩展表](BD_manual_dwd_assistant_service_log_ex.md) |
|
| 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 | 助教服务作废 | assistant_trash_event_id | dwd_assistant_trash_event_ex | [主表](BD_manual_dwd_assistant_trash_event.md) / [扩展表](BD_manual_dwd_assistant_trash_event_ex.md) |
|
||||||
| 3 | dwd_groupbuy_redemption | 团购券核销 | redemption_id | dwd_groupbuy_redemption_ex | [主表](BD_manual_dwd_groupbuy_redemption.md) / [扩展表](BD_manual_dwd_groupbuy_redemption_ex.md) |
|
| 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) |
|
| 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) |
|
||||||
@@ -38,7 +37,7 @@
|
|||||||
| 6 | dwd_platform_coupon_redemption | 平台券核销 | platform_coupon_redemption_id | dwd_platform_coupon_redemption_ex | [主表](BD_manual_dwd_platform_coupon_redemption.md) / [扩展表](BD_manual_dwd_platform_coupon_redemption_ex.md) |
|
| 6 | dwd_platform_coupon_redemption | 平台券核销 | platform_coupon_redemption_id | dwd_platform_coupon_redemption_ex | [主表](BD_manual_dwd_platform_coupon_redemption.md) / [扩展表](BD_manual_dwd_platform_coupon_redemption_ex.md) |
|
||||||
| 7 | dwd_recharge_order | 充值订单 | recharge_order_id | dwd_recharge_order_ex | [主表](BD_manual_dwd_recharge_order.md) / [扩展表](BD_manual_dwd_recharge_order_ex.md) |
|
| 7 | dwd_recharge_order | 充值订单 | recharge_order_id | dwd_recharge_order_ex | [主表](BD_manual_dwd_recharge_order.md) / [扩展表](BD_manual_dwd_recharge_order_ex.md) |
|
||||||
| 8 | dwd_refund | 退款流水 | refund_id | dwd_refund_ex | [主表](BD_manual_dwd_refund.md) / [扩展表](BD_manual_dwd_refund_ex.md) |
|
| 8 | dwd_refund | 退款流水 | refund_id | dwd_refund_ex | [主表](BD_manual_dwd_refund.md) / [扩展表](BD_manual_dwd_refund_ex.md) |
|
||||||
| 9 | dwd_settlement_head | 结账单 | settlement_head_id | dwd_settlement_head_ex | [主表](BD_manual_dwd_settlement_head.md) / [扩展表](BD_manual_dwd_settlement_head_ex.md) |
|
| 9 | dwd_settlement_head | 结账单 | order_settle_id | dwd_settlement_head_ex | [主表](BD_manual_dwd_settlement_head.md) / [扩展表](BD_manual_dwd_settlement_head_ex.md) |
|
||||||
| 10 | dwd_store_goods_sale | 商品销售流水 | store_goods_sale_id | dwd_store_goods_sale_ex | [主表](BD_manual_dwd_store_goods_sale.md) / [扩展表](BD_manual_dwd_store_goods_sale_ex.md) |
|
| 10 | dwd_store_goods_sale | 商品销售流水 | store_goods_sale_id | dwd_store_goods_sale_ex | [主表](BD_manual_dwd_store_goods_sale.md) / [扩展表](BD_manual_dwd_store_goods_sale_ex.md) |
|
||||||
| 11 | dwd_table_fee_adjust | 台费调整 | table_fee_adjust_id | dwd_table_fee_adjust_ex | [主表](BD_manual_dwd_table_fee_adjust.md) / [扩展表](BD_manual_dwd_table_fee_adjust_ex.md) |
|
| 11 | dwd_table_fee_adjust | 台费调整 | table_fee_adjust_id | dwd_table_fee_adjust_ex | [主表](BD_manual_dwd_table_fee_adjust.md) / [扩展表](BD_manual_dwd_table_fee_adjust_ex.md) |
|
||||||
| 12 | dwd_table_fee_log | 台费计费流水 | table_fee_log_id | dwd_table_fee_log_ex | [主表](BD_manual_dwd_table_fee_log.md) / [扩展表](BD_manual_dwd_table_fee_log_ex.md) |
|
| 12 | dwd_table_fee_log | 台费计费流水 | table_fee_log_id | dwd_table_fee_log_ex | [主表](BD_manual_dwd_table_fee_log.md) / [扩展表](BD_manual_dwd_table_fee_log_ex.md) |
|
||||||
@@ -58,6 +57,18 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 最新值获取
|
||||||
|
|
||||||
|
- 维度表(SCD2):使用 scd2_is_current = 1 获取当前版本。
|
||||||
|
- 事实表:无 SCD2 版本字段,按业务时间字段倒序获取最新记录(如 pay_time / create_time / update_time 等)。
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- 维度表取当前版本
|
||||||
|
SELECT * FROM billiards_dwd.dim_member WHERE scd2_is_current = 1;
|
||||||
|
-- 事实表取最新记录(示例:按 pay_time)
|
||||||
|
SELECT * FROM billiards_dwd.dwd_payment ORDER BY pay_time DESC NULLS LAST LIMIT 1;
|
||||||
|
```
|
||||||
|
|
||||||
## 常见 ID 关联说明
|
## 常见 ID 关联说明
|
||||||
|
|
||||||
| ID 字段 | 关联表 | 说明 |
|
| ID 字段 | 关联表 | 说明 |
|
||||||
@@ -79,6 +90,7 @@
|
|||||||
### 主表 + 扩展表模式
|
### 主表 + 扩展表模式
|
||||||
|
|
||||||
大部分表采用"主表 + 扩展表"的设计模式:
|
大部分表采用"主表 + 扩展表"的设计模式:
|
||||||
|
|
||||||
- **主表**:包含核心业务字段(如金额、状态、关键 ID)
|
- **主表**:包含核心业务字段(如金额、状态、关键 ID)
|
||||||
- **扩展表**:包含附属信息(如操作员、门店名称快照、各类详细字段)
|
- **扩展表**:包含附属信息(如操作员、门店名称快照、各类详细字段)
|
||||||
- 两表通过主键一对一关联
|
- 两表通过主键一对一关联
|
||||||
@@ -86,6 +98,7 @@
|
|||||||
### 枚举值说明
|
### 枚举值说明
|
||||||
|
|
||||||
文档中的枚举值格式为 `值(数量)=含义`,例如:
|
文档中的枚举值格式为 `值(数量)=含义`,例如:
|
||||||
|
|
||||||
- `1(100)=有效` 表示值为 1 的记录有 100 条,含义为"有效"
|
- `1(100)=有效` 表示值为 1 的记录有 100 条,含义为"有效"
|
||||||
- **[待确认]** 表示该值的含义无法从数据中确定
|
- **[待确认]** 表示该值的含义无法从数据中确定
|
||||||
|
|
||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
> 生成时间:2026-01-28
|
> 生成时间:2026-01-28
|
||||||
|
|
||||||
|
|
||||||
## 表信息
|
## 表信息
|
||||||
|
|
||||||
| 属性 | 值 |
|
| 属性 | 值 |
|
||||||
@@ -40,6 +39,7 @@
|
|||||||
|
|
||||||
## 使用说明
|
## 使用说明
|
||||||
|
|
||||||
|
使用 scd2_is_current = 1 获取当前版本。
|
||||||
```sql
|
```sql
|
||||||
-- 查询当前在职助教
|
-- 查询当前在职助教
|
||||||
SELECT * FROM billiards_dwd.dim_assistant
|
SELECT * FROM billiards_dwd.dim_assistant
|
||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
> 生成时间:2026-01-28
|
> 生成时间:2026-01-28
|
||||||
|
|
||||||
|
|
||||||
## 表信息
|
## 表信息
|
||||||
|
|
||||||
| 属性 | 值 |
|
| 属性 | 值 |
|
||||||
@@ -20,10 +19,10 @@
|
|||||||
|------|--------|------|------|------|------|
|
|------|--------|------|------|------|------|
|
||||||
| 1 | category_id | BIGINT | NO | PK | 分类唯一标识 |
|
| 1 | category_id | BIGINT | NO | PK | 分类唯一标识 |
|
||||||
| 2 | tenant_id | BIGINT | YES | | 租户 ID(当前值: 2790683160709957) |
|
| 2 | tenant_id | BIGINT | YES | | 租户 ID(当前值: 2790683160709957) |
|
||||||
| 3 | category_name | VARCHAR | YES | | 分类名称。**样本值**: "槟榔", "皮头" 等 |
|
| 3 | category_name | VARCHAR(50) | YES | | 分类名称。**样本值**: "槟榔", "皮头" 等 |
|
||||||
| 4 | alias_name | VARCHAR | YES | | 分类别名(当前数据大部分为空) |
|
| 4 | alias_name | VARCHAR(50) | YES | | 分类别名(当前数据大部分为空) |
|
||||||
| 5 | parent_category_id | BIGINT | YES | | 父级分类 ID(0=一级分类)→ 自关联 |
|
| 5 | parent_category_id | BIGINT | YES | | 父级分类 ID(0=一级分类)→ 自关联 |
|
||||||
| 6 | business_name | VARCHAR | YES | | 业务大类名称。**样本值**: "酒水", "器材" 等 |
|
| 6 | business_name | VARCHAR(50) | YES | | 业务大类名称。**样本值**: "酒水", "器材" 等 |
|
||||||
| 7 | tenant_goods_business_id | BIGINT | YES | | 业务大类 ID |
|
| 7 | tenant_goods_business_id | BIGINT | YES | | 业务大类 ID |
|
||||||
| 8 | category_level | INTEGER | YES | | 分类层级。**枚举值**: 1=一级大类, 2=二级子类 |
|
| 8 | category_level | INTEGER | YES | | 分类层级。**枚举值**: 1=一级大类, 2=二级子类 |
|
||||||
| 9 | is_leaf | INTEGER | YES | | 是否叶子节点。**枚举值**: 0=非叶子, 1=叶子 |
|
| 9 | is_leaf | INTEGER | YES | | 是否叶子节点。**枚举值**: 0=非叶子, 1=叶子 |
|
||||||
@@ -40,12 +39,10 @@
|
|||||||
```
|
```
|
||||||
槟榔(一级)
|
槟榔(一级)
|
||||||
├── 槟榔(二级)
|
├── 槟榔(二级)
|
||||||
|
|
||||||
器材(一级)
|
器材(一级)
|
||||||
├── 皮头
|
├── 皮头
|
||||||
├── 球杆
|
├── 球杆
|
||||||
├── 其他
|
├── 其他
|
||||||
|
|
||||||
酒水(一级)
|
酒水(一级)
|
||||||
├── 饮料
|
├── 饮料
|
||||||
├── 酒水
|
├── 酒水
|
||||||
@@ -57,11 +54,25 @@
|
|||||||
|
|
||||||
## 使用说明
|
## 使用说明
|
||||||
|
|
||||||
|
**版本与最新值**
|
||||||
|
本表为 SCD2 维度表,版本字段:scd2_start_time / scd2_end_time / scd2_is_current / scd2_version。
|
||||||
|
|
||||||
|
- 最新版本:scd2_is_current = 1
|
||||||
|
- 按业务主键取最新:按 scd2_start_time 倒序
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- 取某业务主键的最新版本
|
||||||
|
SELECT *
|
||||||
|
FROM billiards_dwd.dim_goods_category
|
||||||
|
WHERE category_id = <value>
|
||||||
|
ORDER BY scd2_start_time DESC
|
||||||
|
LIMIT 1;
|
||||||
|
```
|
||||||
|
**使用示例**
|
||||||
```sql
|
```sql
|
||||||
-- 查询一级分类
|
-- 查询一级分类
|
||||||
SELECT * FROM billiards_dwd.dim_goods_category
|
SELECT * FROM billiards_dwd.dim_goods_category
|
||||||
WHERE scd2_is_current = 1 AND parent_category_id = 0;
|
WHERE scd2_is_current = 1 AND parent_category_id = 0;
|
||||||
|
|
||||||
-- 查询某一级分类下的二级分类
|
-- 查询某一级分类下的二级分类
|
||||||
SELECT * FROM billiards_dwd.dim_goods_category
|
SELECT * FROM billiards_dwd.dim_goods_category
|
||||||
WHERE scd2_is_current = 1 AND parent_category_id = <一级分类ID>;
|
WHERE scd2_is_current = 1 AND parent_category_id = <一级分类ID>;
|
||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
> 生成时间:2026-01-28
|
> 生成时间:2026-01-28
|
||||||
|
|
||||||
|
|
||||||
## 表信息
|
## 表信息
|
||||||
|
|
||||||
| 属性 | 值 |
|
| 属性 | 值 |
|
||||||
@@ -21,29 +20,45 @@
|
|||||||
| 1 | groupbuy_package_id | BIGINT | NO | PK | 团购套餐 ID |
|
| 1 | groupbuy_package_id | BIGINT | NO | PK | 团购套餐 ID |
|
||||||
| 2 | tenant_id | BIGINT | YES | | 租户 ID(当前值: 2790683160709957) |
|
| 2 | tenant_id | BIGINT | YES | | 租户 ID(当前值: 2790683160709957) |
|
||||||
| 3 | site_id | BIGINT | YES | | 门店 ID → dim_site(当前值: 2790685415443269) |
|
| 3 | site_id | BIGINT | YES | | 门店 ID → dim_site(当前值: 2790685415443269) |
|
||||||
| 4 | package_name | VARCHAR | YES | | 套餐名称。**样本值**: "中八、斯诺克包厢两小时", "斯诺克两小时"等 |
|
| 4 | package_name | VARCHAR(200) | YES | | 套餐名称。**样本值**: "中八、斯诺克包厢两小时", "斯诺克两小时"等 |
|
||||||
| 5 | package_template_id | BIGINT | YES | | 套餐模板 ID |
|
| 5 | package_template_id | BIGINT | YES | | 套餐模板 ID |
|
||||||
| 6 | selling_price | NUMERIC(10,2) | YES | | 售卖价格(每笔订单不同,从核销记录中dwd_groupbuy_redemption获取) |
|
| 6 | selling_price | NUMERIC(10,2) | YES | | 售卖价格(每笔订单不同,从核销记录中dwd_groupbuy_redemption获取) |
|
||||||
| 7 | coupon_face_value | NUMERIC(10,2) | YES | | 券面值(每笔订单不同,从核销记录中dwd_groupbuy_redemption获取) |
|
| 7 | coupon_face_value | NUMERIC(10,2) | YES | | 券面值(每笔订单不同,从核销记录中dwd_groupbuy_redemption获取) |
|
||||||
| 8 | duration_seconds | INTEGER | YES | | 套餐时长(秒)。**样本值**: 3600=1小时, 7200=2小时, 14400=4小时 等 |
|
| 8 | duration_seconds | INTEGER | YES | | 套餐时长(秒)。**样本值**: 3600=1小时, 7200=2小时, 14400=4小时 等 |
|
||||||
| 9 | start_time | TIMESTAMPTZ | YES | | 套餐生效开始时间 |
|
| 9 | start_time | TIMESTAMPTZ | YES | | 套餐生效开始时间 |
|
||||||
| 10 | end_time | TIMESTAMPTZ | YES | | 套餐生效结束时间 |
|
| 10 | end_time | TIMESTAMPTZ | YES | | 套餐生效结束时间 |
|
||||||
| 11 | table_area_name | VARCHAR | YES | | 适用台区名称。**枚举值**: "A区", "VIP包厢", "斯诺克区", "B区", "麻将房", "888" |
|
| 11 | table_area_name | VARCHAR(100) | YES | | 适用台区名称。**枚举值**: "A区", "VIP包厢", "斯诺克区", "B区", "麻将房", "888" |
|
||||||
| 12 | is_enabled | INTEGER | YES | | 启用状态。**枚举值**: 1=启用, 2=停用 |
|
| 12 | is_enabled | INTEGER | YES | | 启用状态。**枚举值**: 1=启用, 2=停用 |
|
||||||
| 13 | is_delete | INTEGER | YES | | 删除标记。**枚举值**: 0=未删除 |
|
| 13 | is_delete | INTEGER | YES | | 删除标记。**枚举值**: 0=未删除 |
|
||||||
| 14 | create_time | TIMESTAMPTZ | YES | | 创建时间 |
|
| 14 | create_time | TIMESTAMPTZ | YES | | 创建时间 |
|
||||||
| 15 | tenant_table_area_id_list | VARCHAR | YES | | 租户级台区 ID 列表 |
|
| 15 | tenant_table_area_id_list | VARCHAR(512) | YES | | 租户级台区 ID 列表 |
|
||||||
| 16 | card_type_ids | VARCHAR | YES | | 允许使用的卡类型 ID 列表(当前数据为 "0") |
|
| 16 | card_type_ids | VARCHAR(255) | YES | | 允许使用的卡类型 ID 列表(当前数据为 "0") |
|
||||||
| 17 | scd2_start_time | TIMESTAMPTZ | NO | PK | SCD2 版本生效时间 |
|
| 17 | sort | INTEGER | YES | | 排序 |
|
||||||
| 18 | scd2_end_time | TIMESTAMPTZ | YES | | SCD2 版本失效时间 |
|
| 18 | is_first_limit | BOOLEAN | YES | | 是否首单限制 |
|
||||||
| 19 | scd2_is_current | INTEGER | YES | | 当前版本标记 |
|
| 19 | scd2_start_time | TIMESTAMPTZ | NO | PK | SCD2 版本生效时间 |
|
||||||
| 20 | scd2_version | INTEGER | YES | | 版本号 |
|
| 20 | scd2_end_time | TIMESTAMPTZ | YES | | SCD2 版本失效时间 |
|
||||||
|
| 21 | scd2_is_current | INTEGER | YES | | 当前版本标记 |
|
||||||
|
| 22 | scd2_version | INTEGER | YES | | 版本号 |
|
||||||
|
|
||||||
## 使用说明
|
## 使用说明
|
||||||
|
|
||||||
|
**版本与最新值**
|
||||||
|
本表为 SCD2 维度表,版本字段:scd2_start_time / scd2_end_time / scd2_is_current / scd2_version。
|
||||||
|
|
||||||
|
- 最新版本:scd2_is_current = 1
|
||||||
|
- 按业务主键取最新:按 scd2_start_time 倒序
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- 取某业务主键的最新版本
|
||||||
|
SELECT *
|
||||||
|
FROM billiards_dwd.dim_groupbuy_package
|
||||||
|
WHERE groupbuy_package_id = <value>
|
||||||
|
ORDER BY scd2_start_time DESC
|
||||||
|
LIMIT 1;
|
||||||
|
```
|
||||||
|
**使用示例**
|
||||||
```sql
|
```sql
|
||||||
-- 查询当前启用的套餐
|
-- 查询当前启用的套餐
|
||||||
SELECT * FROM billiards_dwd.dim_groupbuy_package
|
SELECT * FROM billiards_dwd.dim_groupbuy_package
|
||||||
WHERE scd2_is_current = 1 AND is_delete = 0 AND is_enabled = 1;
|
WHERE scd2_is_current = 1 AND is_delete = 0 AND is_enabled = 1;
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -27,20 +27,34 @@
|
|||||||
| 8 | member_card_grade_name | TEXT | YES | | 卡等级名称。**枚举值**: "储值卡", "台费卡", "年卡", "活动抵用券", "月卡" |
|
| 8 | member_card_grade_name | TEXT | YES | | 卡等级名称。**枚举值**: "储值卡", "台费卡", "年卡", "活动抵用券", "月卡" |
|
||||||
| 9 | create_time | TIMESTAMPTZ | YES | | 创建时间 |
|
| 9 | create_time | TIMESTAMPTZ | YES | | 创建时间 |
|
||||||
| 10 | update_time | TIMESTAMPTZ | YES | | 更新时间 |
|
| 10 | update_time | TIMESTAMPTZ | YES | | 更新时间 |
|
||||||
| 11 | scd2_start_time | TIMESTAMPTZ | NO | PK | SCD2 版本生效时间 |
|
| 11 | pay_money_sum | NUMERIC(18,2) | YES | | 累计支付金额 |
|
||||||
| 12 | scd2_end_time | TIMESTAMPTZ | YES | | SCD2 版本失效时间 |
|
| 12 | recharge_money_sum | NUMERIC(18,2) | YES | | 累计充值金额 |
|
||||||
| 13 | scd2_is_current | INTEGER | YES | | 当前版本标记 |
|
| 13 | scd2_start_time | TIMESTAMPTZ | NO | PK | SCD2 版本生效时间 |
|
||||||
| 14 | scd2_version | INTEGER | YES | | 版本号 |
|
| 14 | scd2_end_time | TIMESTAMPTZ | YES | | SCD2 版本失效时间 |
|
||||||
|
| 15 | scd2_is_current | INTEGER | YES | | 当前版本标记 |
|
||||||
|
| 16 | scd2_version | INTEGER | YES | | 版本号 |
|
||||||
|
|
||||||
## 使用说明
|
## 使用说明
|
||||||
|
|
||||||
|
**版本与最新值**
|
||||||
|
本表为 SCD2 维度表,版本字段:scd2_start_time / scd2_end_time / scd2_is_current / scd2_version。
|
||||||
|
|
||||||
|
- 最新版本:scd2_is_current = 1
|
||||||
|
- 按业务主键取最新:按 scd2_start_time 倒序
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- 取某业务主键的最新版本
|
||||||
|
SELECT *
|
||||||
|
FROM billiards_dwd.dim_member
|
||||||
|
WHERE member_id = <value>
|
||||||
|
ORDER BY scd2_start_time DESC
|
||||||
|
LIMIT 1;
|
||||||
|
```
|
||||||
|
**使用示例**
|
||||||
```sql
|
```sql
|
||||||
-- 查询当前有效会员
|
-- 查询当前有效会员
|
||||||
SELECT * FROM billiards_dwd.dim_member
|
SELECT * FROM billiards_dwd.dim_member
|
||||||
WHERE scd2_is_current = 1;
|
WHERE scd2_is_current = 1;
|
||||||
|
|
||||||
-- 按卡类型统计会员数
|
-- 按卡类型统计会员数
|
||||||
SELECT member_card_grade_name, COUNT(*)
|
SELECT member_card_grade_name, COUNT(*)
|
||||||
FROM billiards_dwd.dim_member
|
FROM billiards_dwd.dim_member
|
||||||
@@ -34,10 +34,12 @@
|
|||||||
| 15 | last_consume_time | TIMESTAMPTZ | YES | | 最近消费时间 |
|
| 15 | last_consume_time | TIMESTAMPTZ | YES | | 最近消费时间 |
|
||||||
| 16 | status | INTEGER | YES | | 卡状态。**枚举值**: 1=正常, 4=过期 |
|
| 16 | status | INTEGER | YES | | 卡状态。**枚举值**: 1=正常, 4=过期 |
|
||||||
| 17 | is_delete | INTEGER | YES | | 删除标记。**枚举值**: 0=未删除 |
|
| 17 | is_delete | INTEGER | YES | | 删除标记。**枚举值**: 0=未删除 |
|
||||||
| 18 | scd2_start_time | TIMESTAMPTZ | NO | PK | SCD2 版本生效时间 |
|
| 18 | principal_balance | NUMERIC(18,2) | YES | | 本金余额 |
|
||||||
| 19 | scd2_end_time | TIMESTAMPTZ | YES | | SCD2 版本失效时间 |
|
| 19 | member_grade | INTEGER | YES | | 会员等级 |
|
||||||
| 20 | scd2_is_current | INTEGER | YES | | 当前版本标记 |
|
| 20 | scd2_start_time | TIMESTAMPTZ | NO | PK | SCD2 版本生效时间 |
|
||||||
| 21 | scd2_version | INTEGER | YES | | 版本号 |
|
| 21 | scd2_end_time | TIMESTAMPTZ | YES | | SCD2 版本失效时间 |
|
||||||
|
| 22 | scd2_is_current | INTEGER | YES | | 当前版本标记 |
|
||||||
|
| 23 | scd2_version | INTEGER | YES | | 版本号 |
|
||||||
|
|
||||||
## 卡种分布
|
## 卡种分布
|
||||||
|
|
||||||
@@ -50,9 +52,23 @@
|
|||||||
| 2793306611533637 | 月卡 | 充值获得,时长卡,仅可抵扣台费 |
|
| 2793306611533637 | 月卡 | 充值获得,时长卡,仅可抵扣台费 |
|
||||||
| 2791987095408517 | 年卡 | 充值获得,时长卡,仅可抵扣台费 |
|
| 2791987095408517 | 年卡 | 充值获得,时长卡,仅可抵扣台费 |
|
||||||
|
|
||||||
|
|
||||||
## 使用说明
|
## 使用说明
|
||||||
|
|
||||||
|
**版本与最新值**
|
||||||
|
本表为 SCD2 维度表,版本字段:scd2_start_time / scd2_end_time / scd2_is_current / scd2_version。
|
||||||
|
|
||||||
|
- 最新版本:scd2_is_current = 1
|
||||||
|
- 按业务主键取最新:按 scd2_start_time 倒序
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- 取某业务主键的最新版本
|
||||||
|
SELECT *
|
||||||
|
FROM billiards_dwd.dim_member_card_account
|
||||||
|
WHERE member_card_id = <value>
|
||||||
|
ORDER BY scd2_start_time DESC
|
||||||
|
LIMIT 1;
|
||||||
|
```
|
||||||
|
**使用示例**
|
||||||
```sql
|
```sql
|
||||||
-- 查询有效的储值卡
|
-- 查询有效的储值卡
|
||||||
SELECT * FROM billiards_dwd.dim_member_card_account
|
SELECT * FROM billiards_dwd.dim_member_card_account
|
||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
> 生成时间:2026-01-28
|
> 生成时间:2026-01-28
|
||||||
|
|
||||||
|
|
||||||
## 表信息
|
## 表信息
|
||||||
|
|
||||||
| 属性 | 值 |
|
| 属性 | 值 |
|
||||||
@@ -44,6 +43,21 @@
|
|||||||
|
|
||||||
## 使用说明
|
## 使用说明
|
||||||
|
|
||||||
|
**版本与最新值**
|
||||||
|
本表为 SCD2 维度表,版本字段:scd2_start_time / scd2_end_time / scd2_is_current / scd2_version。
|
||||||
|
|
||||||
|
- 最新版本:scd2_is_current = 1
|
||||||
|
- 按业务主键取最新:按 scd2_start_time 倒序
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- 取某业务主键的最新版本
|
||||||
|
SELECT *
|
||||||
|
FROM billiards_dwd.dim_site
|
||||||
|
WHERE site_id = <value>
|
||||||
|
ORDER BY scd2_start_time DESC
|
||||||
|
LIMIT 1;
|
||||||
|
```
|
||||||
|
**使用示例**
|
||||||
```sql
|
```sql
|
||||||
-- 查询当前有效门店
|
-- 查询当前有效门店
|
||||||
SELECT * FROM billiards_dwd.dim_site
|
SELECT * FROM billiards_dwd.dim_site
|
||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
> 生成时间:2026-01-28
|
> 生成时间:2026-01-28
|
||||||
|
|
||||||
|
|
||||||
## 表信息
|
## 表信息
|
||||||
|
|
||||||
| 属性 | 值 |
|
| 属性 | 值 |
|
||||||
@@ -38,10 +37,12 @@
|
|||||||
| 18 | enable_status | INTEGER | YES | | 启用状态。**枚举值**: 1=启用 |
|
| 18 | enable_status | INTEGER | YES | | 启用状态。**枚举值**: 1=启用 |
|
||||||
| 19 | send_state | INTEGER | YES | | 配送状态。暂无作用 |
|
| 19 | send_state | INTEGER | YES | | 配送状态。暂无作用 |
|
||||||
| 20 | is_delete | INTEGER | YES | | 删除标记。**枚举值**: 0=未删除 |
|
| 20 | is_delete | INTEGER | YES | | 删除标记。**枚举值**: 0=未删除 |
|
||||||
| 21 | scd2_start_time | TIMESTAMPTZ | NO | PK | SCD2 版本生效时间 |
|
| 21 | commodity_code | TEXT | YES | | 商品编码 |
|
||||||
| 22 | scd2_end_time | TIMESTAMPTZ | YES | | SCD2 版本失效时间 |
|
| 22 | not_sale | INTEGER | YES | | 是否停售 |
|
||||||
| 23 | scd2_is_current | INTEGER | YES | | 当前版本标记 |
|
| 23 | scd2_start_time | TIMESTAMPTZ | NO | PK | SCD2 版本生效时间 |
|
||||||
| 24 | scd2_version | INTEGER | YES | | 版本号 |
|
| 24 | scd2_end_time | TIMESTAMPTZ | YES | | SCD2 版本失效时间 |
|
||||||
|
| 25 | scd2_is_current | INTEGER | YES | | 当前版本标记 |
|
||||||
|
| 26 | scd2_version | INTEGER | YES | | 版本号 |
|
||||||
|
|
||||||
## 样本数据
|
## 样本数据
|
||||||
|
|
||||||
@@ -54,6 +55,21 @@
|
|||||||
|
|
||||||
## 使用说明
|
## 使用说明
|
||||||
|
|
||||||
|
**版本与最新值**
|
||||||
|
本表为 SCD2 维度表,版本字段:scd2_start_time / scd2_end_time / scd2_is_current / scd2_version。
|
||||||
|
|
||||||
|
- 最新版本:scd2_is_current = 1
|
||||||
|
- 按业务主键取最新:按 scd2_start_time 倒序
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- 取某业务主键的最新版本
|
||||||
|
SELECT *
|
||||||
|
FROM billiards_dwd.dim_store_goods
|
||||||
|
WHERE site_goods_id = <value>
|
||||||
|
ORDER BY scd2_start_time DESC
|
||||||
|
LIMIT 1;
|
||||||
|
```
|
||||||
|
**使用示例**
|
||||||
```sql
|
```sql
|
||||||
-- 查询当前上架商品
|
-- 查询当前上架商品
|
||||||
SELECT * FROM billiards_dwd.dim_store_goods
|
SELECT * FROM billiards_dwd.dim_store_goods
|
||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
> 生成时间:2026-01-28
|
> 生成时间:2026-01-28
|
||||||
|
|
||||||
|
|
||||||
## 表信息
|
## 表信息
|
||||||
|
|
||||||
| 属性 | 值 |
|
| 属性 | 值 |
|
||||||
@@ -25,10 +24,11 @@
|
|||||||
| 5 | site_table_area_name | TEXT | YES | | 台区名称。**样本值**: "A区", "B区", "补时长", "C区", "麻将房", "K包", "VIP包厢", "斯诺克区", "666", "k包活动区", "M7" 等 |
|
| 5 | site_table_area_name | TEXT | YES | | 台区名称。**样本值**: "A区", "B区", "补时长", "C区", "麻将房", "K包", "VIP包厢", "斯诺克区", "666", "k包活动区", "M7" 等 |
|
||||||
| 6 | tenant_table_area_id | BIGINT | YES | | 租户级台区 ID |
|
| 6 | tenant_table_area_id | BIGINT | YES | | 租户级台区 ID |
|
||||||
| 7 | table_price | NUMERIC(18,2) | YES | | 台桌单价(当前数据全为 0.00) |
|
| 7 | table_price | NUMERIC(18,2) | YES | | 台桌单价(当前数据全为 0.00) |
|
||||||
| 8 | scd2_start_time | TIMESTAMPTZ | NO | PK | SCD2 版本生效时间 |
|
| 8 | order_id | BIGINT | YES | | 订单 ID |
|
||||||
| 9 | scd2_end_time | TIMESTAMPTZ | YES | | SCD2 版本失效时间 |
|
| 9 | scd2_start_time | TIMESTAMPTZ | NO | PK | SCD2 版本生效时间 |
|
||||||
| 10 | scd2_is_current | INTEGER | YES | | 当前版本标记 |
|
| 10 | scd2_end_time | TIMESTAMPTZ | YES | | SCD2 版本失效时间 |
|
||||||
| 11 | scd2_version | INTEGER | YES | | 版本号 |
|
| 11 | scd2_is_current | INTEGER | YES | | 当前版本标记 |
|
||||||
|
| 12 | scd2_version | INTEGER | YES | | 版本号 |
|
||||||
|
|
||||||
## 台区分布
|
## 台区分布
|
||||||
|
|
||||||
@@ -52,11 +52,25 @@
|
|||||||
|
|
||||||
## 使用说明
|
## 使用说明
|
||||||
|
|
||||||
|
**版本与最新值**
|
||||||
|
本表为 SCD2 维度表,版本字段:scd2_start_time / scd2_end_time / scd2_is_current / scd2_version。
|
||||||
|
|
||||||
|
- 最新版本:scd2_is_current = 1
|
||||||
|
- 按业务主键取最新:按 scd2_start_time 倒序
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- 取某业务主键的最新版本
|
||||||
|
SELECT *
|
||||||
|
FROM billiards_dwd.dim_table
|
||||||
|
WHERE table_id = <value>
|
||||||
|
ORDER BY scd2_start_time DESC
|
||||||
|
LIMIT 1;
|
||||||
|
```
|
||||||
|
**使用示例**
|
||||||
```sql
|
```sql
|
||||||
-- 查询当前有效台桌
|
-- 查询当前有效台桌
|
||||||
SELECT * FROM billiards_dwd.dim_table
|
SELECT * FROM billiards_dwd.dim_table
|
||||||
WHERE scd2_is_current = 1;
|
WHERE scd2_is_current = 1;
|
||||||
|
|
||||||
-- 按台区统计台桌数
|
-- 按台区统计台桌数
|
||||||
SELECT site_table_area_name, COUNT(*)
|
SELECT site_table_area_name, COUNT(*)
|
||||||
FROM billiards_dwd.dim_table
|
FROM billiards_dwd.dim_table
|
||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
> 生成时间:2026-01-28
|
> 生成时间:2026-01-28
|
||||||
|
|
||||||
|
|
||||||
## 表信息
|
## 表信息
|
||||||
|
|
||||||
| 属性 | 值 |
|
| 属性 | 值 |
|
||||||
@@ -21,25 +20,40 @@
|
|||||||
| 1 | tenant_goods_id | BIGINT | NO | PK | 租户商品 ID(SKU) |
|
| 1 | tenant_goods_id | BIGINT | NO | PK | 租户商品 ID(SKU) |
|
||||||
| 2 | tenant_id | BIGINT | YES | | 租户 ID |
|
| 2 | tenant_id | BIGINT | YES | | 租户 ID |
|
||||||
| 3 | supplier_id | BIGINT | YES | | 供应商 ID(当前数据全为 0) |
|
| 3 | supplier_id | BIGINT | YES | | 供应商 ID(当前数据全为 0) |
|
||||||
| 4 | category_name | VARCHAR | YES | | 分类名称(二级分类)。**样本值**: "零食", "饮料", "香烟"等 |
|
| 4 | category_name | VARCHAR(64) | YES | | 分类名称(二级分类)。**样本值**: "零食", "饮料", "香烟"等 |
|
||||||
| 5 | goods_category_id | BIGINT | YES | | 一级分类 ID |
|
| 5 | goods_category_id | BIGINT | YES | | 一级分类 ID |
|
||||||
| 6 | goods_second_category_id | BIGINT | YES | | 二级分类 ID |
|
| 6 | goods_second_category_id | BIGINT | YES | | 二级分类 ID |
|
||||||
| 7 | goods_name | VARCHAR | YES | | 商品名称。**样本值**: "海之言", "西梅多多饮品", "美汁源果粒橙", "三诺橙汁"等 |
|
| 7 | goods_name | VARCHAR(128) | YES | | 商品名称。**样本值**: "海之言", "西梅多多饮品", "美汁源果粒橙", "三诺橙汁"等 |
|
||||||
| 8 | goods_number | VARCHAR | YES | | 商品编号(序号) |
|
| 8 | goods_number | VARCHAR(64) | YES | | 商品编号(序号) |
|
||||||
| 9 | unit | VARCHAR | YES | | 商品单位。**枚举值**: "包", "瓶", "个", "份"等 |
|
| 9 | unit | VARCHAR(16) | YES | | 商品单位。**枚举值**: "包", "瓶", "个", "份"等 |
|
||||||
| 10 | market_price | NUMERIC(18,2) | YES | | 市场价/吊牌价(元) |
|
| 10 | market_price | NUMERIC(18,2) | YES | | 市场价/吊牌价(元) |
|
||||||
| 11 | goods_state | INTEGER | YES | | 商品状态。**枚举值**: 1=上架, 2=下架 |
|
| 11 | goods_state | INTEGER | YES | | 商品状态。**枚举值**: 1=上架, 2=下架 |
|
||||||
| 12 | create_time | TIMESTAMPTZ | YES | | 创建时间 |
|
| 12 | create_time | TIMESTAMPTZ | YES | | 创建时间 |
|
||||||
| 13 | update_time | TIMESTAMPTZ | YES | | 更新时间 |
|
| 13 | update_time | TIMESTAMPTZ | YES | | 更新时间 |
|
||||||
| 14 | is_delete | INTEGER | YES | | 删除标记。**枚举值**: 0=未删除 |
|
| 14 | is_delete | INTEGER | YES | | 删除标记。**枚举值**: 0=未删除 |
|
||||||
| 15 | scd2_start_time | TIMESTAMPTZ | NO | PK | SCD2 版本生效时间 |
|
| 15 | not_sale | INTEGER | YES | | 是否停售 |
|
||||||
| 16 | scd2_end_time | TIMESTAMPTZ | YES | | SCD2 版本失效时间 |
|
| 16 | scd2_start_time | TIMESTAMPTZ | NO | PK | SCD2 版本生效时间 |
|
||||||
| 17 | scd2_is_current | INTEGER | YES | | 当前版本标记 |
|
| 17 | scd2_end_time | TIMESTAMPTZ | YES | | SCD2 版本失效时间 |
|
||||||
| 18 | scd2_version | INTEGER | YES | | 版本号 |
|
| 18 | scd2_is_current | INTEGER | YES | | 当前版本标记 |
|
||||||
|
| 19 | scd2_version | INTEGER | YES | | 版本号 |
|
||||||
|
|
||||||
## 使用说明
|
## 使用说明
|
||||||
|
|
||||||
|
**版本与最新值**
|
||||||
|
本表为 SCD2 维度表,版本字段:scd2_start_time / scd2_end_time / scd2_is_current / scd2_version。
|
||||||
|
|
||||||
|
- 最新版本:scd2_is_current = 1
|
||||||
|
- 按业务主键取最新:按 scd2_start_time 倒序
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- 取某业务主键的最新版本
|
||||||
|
SELECT *
|
||||||
|
FROM billiards_dwd.dim_tenant_goods
|
||||||
|
WHERE tenant_goods_id = <value>
|
||||||
|
ORDER BY scd2_start_time DESC
|
||||||
|
LIMIT 1;
|
||||||
|
```
|
||||||
|
**使用示例**
|
||||||
```sql
|
```sql
|
||||||
-- 查询当前有效的租户商品
|
-- 查询当前有效的租户商品
|
||||||
SELECT * FROM billiards_dwd.dim_tenant_goods
|
SELECT * FROM billiards_dwd.dim_tenant_goods
|
||||||
@@ -28,16 +28,16 @@
|
|||||||
| 9 | site_table_id | BIGINT | YES | | 台桌 ID → dim_table(0=非台桌服务) |
|
| 9 | site_table_id | BIGINT | YES | | 台桌 ID → dim_table(0=非台桌服务) |
|
||||||
| 10 | tenant_member_id | BIGINT | YES | | 会员 ID → dim_member(0=散客) |
|
| 10 | tenant_member_id | BIGINT | YES | | 会员 ID → dim_member(0=散客) |
|
||||||
| 11 | system_member_id | BIGINT | YES | | 系统会员 ID(0=散客) |
|
| 11 | system_member_id | BIGINT | YES | | 系统会员 ID(0=散客) |
|
||||||
| 12 | assistant_no | VARCHAR | YES | | 助教工号。**样本值**: "2", "9"等 |
|
| 12 | assistant_no | VARCHAR(64) | YES | | 助教工号。**样本值**: "2", "9"等 |
|
||||||
| 13 | nickname | VARCHAR | YES | | 助教昵称。**样本值**: "佳怡", "婉婉", "七七"等 |
|
| 13 | nickname | VARCHAR(64) | YES | | 助教昵称。**样本值**: "佳怡", "婉婉", "七七"等 |
|
||||||
| 14 | site_assistant_id | BIGINT | YES | | 助教 ID → dim_assistant |
|
| 14 | site_assistant_id | BIGINT | YES | | 助教 ID → dim_assistant |
|
||||||
| 15 | user_id | BIGINT | YES | | 助教用户 ID |
|
| 15 | user_id | BIGINT | YES | | 助教用户 ID |
|
||||||
| 16 | assistant_team_id | BIGINT | YES | | 助教团队 ID。**枚举值**: 2792011585884037=1组, 2959085810992645=2组 |
|
| 16 | assistant_team_id | BIGINT | YES | | 助教团队 ID。**枚举值**: 2792011585884037=1组, 2959085810992645=2组 |
|
||||||
| 17 | person_org_id | BIGINT | YES | | 人事组织 ID |
|
| 17 | person_org_id | BIGINT | YES | | 人事组织 ID |
|
||||||
| 18 | assistant_level | INTEGER | YES | | 助教等级。**枚举值**: 8=助教管理, 10=初级, 20=中级, 30=高级, 40=星级 |
|
| 18 | assistant_level | INTEGER | YES | | 助教等级。**枚举值**: 8=助教管理, 10=初级, 20=中级, 30=高级, 40=星级 |
|
||||||
| 19 | level_name | VARCHAR | YES | | 等级名称。**枚举值**: "助教管理", "初级", "中级", "高级", "星级" |
|
| 19 | level_name | VARCHAR(64) | YES | | 等级名称。**枚举值**: "助教管理", "初级", "中级", "高级", "星级" |
|
||||||
| 20 | skill_id | BIGINT | YES | | 技能 ID |
|
| 20 | skill_id | BIGINT | YES | | 技能 ID **枚举值**: 2790683529513797 = 基础课 , 2790683529513798 = 附加课/激励课, 3039912271463941 = 包厢课 |
|
||||||
| 21 | skill_name | VARCHAR | YES | | 技能名称。**枚举值**: "基础课", "附加课/激励课", "包厢课" |
|
| 21 | skill_name | VARCHAR(64) | YES | | 技能名称。 **枚举值**: "基础课","附加课","包厢课"|
|
||||||
| 22 | ledger_unit_price | NUMERIC(10,2) | YES | | 单价(元/小时),**样本值**: 98.00/108.00/190.00 等 |
|
| 22 | ledger_unit_price | NUMERIC(10,2) | YES | | 单价(元/小时),**样本值**: 98.00/108.00/190.00 等 |
|
||||||
| 23 | ledger_amount | NUMERIC(10,2) | YES | | 计费金额 |
|
| 23 | ledger_amount | NUMERIC(10,2) | YES | | 计费金额 |
|
||||||
| 24 | projected_income | NUMERIC(10,2) | YES | | 预估收入 |
|
| 24 | projected_income | NUMERIC(10,2) | YES | | 预估收入 |
|
||||||
@@ -49,10 +49,23 @@
|
|||||||
| 30 | start_use_time | TIMESTAMPTZ | YES | | 服务开始时间 |
|
| 30 | start_use_time | TIMESTAMPTZ | YES | | 服务开始时间 |
|
||||||
| 31 | last_use_time | TIMESTAMPTZ | YES | | 服务结束时间 |
|
| 31 | last_use_time | TIMESTAMPTZ | YES | | 服务结束时间 |
|
||||||
| 32 | is_delete | INTEGER | YES | | 删除标记。**枚举值**: 0=未删除 |
|
| 32 | is_delete | INTEGER | YES | | 删除标记。**枚举值**: 0=未删除 |
|
||||||
|
| 33 | real_service_money | NUMERIC(18,2) | YES | | 实际服务费金额 |
|
||||||
|
|
||||||
## 使用说明
|
## 使用说明
|
||||||
|
|
||||||
|
**版本与最新值**
|
||||||
|
本表为事实表,无 SCD2 版本字段。
|
||||||
|
|
||||||
|
- 可用时间字段:create_time, start_use_time, last_use_time
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- 取最新一条(按时间字段倒序)
|
||||||
|
SELECT *
|
||||||
|
FROM billiards_dwd.dwd_assistant_service_log
|
||||||
|
ORDER BY create_time DESC NULLS LAST
|
||||||
|
LIMIT 1;
|
||||||
|
```
|
||||||
|
**使用示例**
|
||||||
```sql
|
```sql
|
||||||
-- 统计助教服务收入
|
-- 统计助教服务收入
|
||||||
SELECT
|
SELECT
|
||||||
@@ -21,16 +21,29 @@
|
|||||||
| 2 | site_id | BIGINT | YES | | 门店 ID |
|
| 2 | site_id | BIGINT | YES | | 门店 ID |
|
||||||
| 3 | table_id | BIGINT | YES | | 台桌 ID → dim_table |
|
| 3 | table_id | BIGINT | YES | | 台桌 ID → dim_table |
|
||||||
| 4 | table_area_id | BIGINT | YES | | 台区 ID |
|
| 4 | table_area_id | BIGINT | YES | | 台区 ID |
|
||||||
| 5 | assistant_no | VARCHAR | YES | | 助教工号/昵称。**样本值**: "七七", "乔西", "球球"等 |
|
| 5 | assistant_no | VARCHAR(32) | YES | | 助教工号/昵称。**样本值**: "七七", "乔西", "球球"等 |
|
||||||
| 6 | assistant_name | VARCHAR | YES | | 助教名称,与 assistant_no 相同 |
|
| 6 | assistant_name | VARCHAR(64) | YES | | 助教名称,与 assistant_no 相同 |
|
||||||
| 7 | charge_minutes_raw | INTEGER | YES | | 原计费时长(秒)。**样本值**: 0, 3600=1h, 10800=3h 等 |
|
| 7 | charge_minutes_raw | INTEGER | YES | | 原计费时长(秒)。**样本值**: 0, 3600=1h, 10800=3h 等 |
|
||||||
| 8 | abolish_amount | NUMERIC(18,2) | YES | | 作废金额(元)。**样本值**: 0.00, 190.00, 570.00 等 |
|
| 8 | abolish_amount | NUMERIC(18,2) | YES | | 作废金额(元)。**样本值**: 0.00, 190.00, 570.00 等 |
|
||||||
| 9 | trash_reason | VARCHAR | YES | | 作废原因(当前数据全为 NULL) |
|
| 9 | trash_reason | VARCHAR(255) | YES | | 作废原因(当前数据全为 NULL) |
|
||||||
| 10 | create_time | TIMESTAMPTZ | YES | | 创建时间 |
|
| 10 | create_time | TIMESTAMPTZ | YES | | 创建时间 |
|
||||||
|
| 11 | tenant_id | BIGINT | YES | | 租户 ID |
|
||||||
|
|
||||||
## 使用说明
|
## 使用说明
|
||||||
|
|
||||||
|
**版本与最新值**
|
||||||
|
本表为事实表,无 SCD2 版本字段。
|
||||||
|
|
||||||
|
- 可用时间字段:create_time
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- 取最新一条(按时间字段倒序)
|
||||||
|
SELECT *
|
||||||
|
FROM billiards_dwd.dwd_assistant_trash_event
|
||||||
|
ORDER BY create_time DESC NULLS LAST
|
||||||
|
LIMIT 1;
|
||||||
|
```
|
||||||
|
**使用示例**
|
||||||
```sql
|
```sql
|
||||||
-- 助教作废金额统计
|
-- 助教作废金额统计
|
||||||
SELECT
|
SELECT
|
||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
> 生成时间:2026-01-28
|
> 生成时间:2026-01-28
|
||||||
|
|
||||||
|
|
||||||
## 表信息
|
## 表信息
|
||||||
|
|
||||||
| 属性 | 值 |
|
| 属性 | 值 |
|
||||||
@@ -36,14 +35,29 @@
|
|||||||
| 16 | ledger_amount | NUMERIC(18,2) | YES | | 账本金额(元)。**样本值**: 48.00, 96.00, 68.00 等 |
|
| 16 | ledger_amount | NUMERIC(18,2) | YES | | 账本金额(元)。**样本值**: 48.00, 96.00, 68.00 等 |
|
||||||
| 17 | coupon_money | NUMERIC(18,2) | YES | | 券面额(元)。**样本值**: 48.00, 116.00, 96.00, 68.00 等 |
|
| 17 | coupon_money | NUMERIC(18,2) | YES | | 券面额(元)。**样本值**: 48.00, 116.00, 96.00, 68.00 等 |
|
||||||
| 18 | promotion_seconds | INTEGER | YES | | 促销时长(秒)。**样本值**: 3600=1h, 7200=2h, 14400=4h 等 |
|
| 18 | promotion_seconds | INTEGER | YES | | 促销时长(秒)。**样本值**: 3600=1h, 7200=2h, 14400=4h 等 |
|
||||||
| 19 | coupon_code | VARCHAR | YES | | 券码 |
|
| 19 | coupon_code | VARCHAR(64) | YES | | 券码 |
|
||||||
| 20 | is_single_order | INTEGER | YES | | 是否独立订单。**枚举值**: 0=否, 1=是 |
|
| 20 | is_single_order | INTEGER | YES | | 是否独立订单。**枚举值**: 0=否, 1=是 |
|
||||||
| 21 | is_delete | INTEGER | YES | | 删除标记。**枚举值**: 0=未删除 |
|
| 21 | is_delete | INTEGER | YES | | 删除标记。**枚举值**: 0=未删除 |
|
||||||
| 22 | ledger_name | VARCHAR | YES | | 套餐名称。**样本值**: "全天A区中八一小时", "中八A区新人特惠一小时" 等 |
|
| 22 | ledger_name | VARCHAR(128) | YES | | 套餐名称。**样本值**: "全天A区中八一小时", "中八A区新人特惠一小时" 等 |
|
||||||
| 23 | create_time | TIMESTAMPTZ | YES | | 创建时间 |
|
| 23 | create_time | TIMESTAMPTZ | YES | | 创建时间 |
|
||||||
|
| 24 | member_discount_money | NUMERIC(18,2) | YES | | 会员折扣金额 |
|
||||||
|
| 25 | coupon_sale_id | BIGINT | YES | | 优惠券销售 ID |
|
||||||
|
|
||||||
## 使用说明
|
## 使用说明
|
||||||
|
|
||||||
|
**版本与最新值**
|
||||||
|
本表为事实表,无 SCD2 版本字段。
|
||||||
|
|
||||||
|
- 可用时间字段:create_time
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- 取最新一条(按时间字段倒序)
|
||||||
|
SELECT *
|
||||||
|
FROM billiards_dwd.dwd_groupbuy_redemption
|
||||||
|
ORDER BY create_time DESC NULLS LAST
|
||||||
|
LIMIT 1;
|
||||||
|
```
|
||||||
|
**使用示例**
|
||||||
```sql
|
```sql
|
||||||
-- 各套餐核销统计
|
-- 各套餐核销统计
|
||||||
SELECT
|
SELECT
|
||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
> 生成时间:2026-01-28
|
> 生成时间:2026-01-28
|
||||||
|
|
||||||
|
|
||||||
## 表信息
|
## 表信息
|
||||||
|
|
||||||
| 属性 | 值 |
|
| 属性 | 值 |
|
||||||
@@ -26,9 +25,9 @@
|
|||||||
| 6 | system_member_id | BIGINT | YES | | 系统会员 ID |
|
| 6 | system_member_id | BIGINT | YES | | 系统会员 ID |
|
||||||
| 7 | tenant_member_card_id | BIGINT | YES | | 会员卡 ID → dim_member_card_account |
|
| 7 | tenant_member_card_id | BIGINT | YES | | 会员卡 ID → dim_member_card_account |
|
||||||
| 8 | card_type_id | BIGINT | YES | | 卡类型 ID |
|
| 8 | card_type_id | BIGINT | YES | | 卡类型 ID |
|
||||||
| 9 | card_type_name | VARCHAR | YES | | 卡类型名称。**枚举值**: "储值卡", "活动抵用券", "台费卡", "酒水卡", "年卡", "月卡" |
|
| 9 | card_type_name | VARCHAR(32) | YES | | 卡类型名称。**枚举值**: "储值卡", "活动抵用券", "台费卡", "酒水卡", "年卡", "月卡" |
|
||||||
| 10 | member_name | VARCHAR | YES | | 会员名称快照 |
|
| 10 | member_name | VARCHAR(64) | YES | | 会员名称快照 |
|
||||||
| 11 | member_mobile | VARCHAR | YES | | 会员手机号快照 |
|
| 11 | member_mobile | VARCHAR(20) | YES | | 会员手机号快照 |
|
||||||
| 12 | balance_before | NUMERIC(18,2) | YES | | 变动前余额 |
|
| 12 | balance_before | NUMERIC(18,2) | YES | | 变动前余额 |
|
||||||
| 13 | change_amount | NUMERIC(18,2) | YES | | 变动金额(正=充值/赠送,负=消费) |
|
| 13 | change_amount | NUMERIC(18,2) | YES | | 变动金额(正=充值/赠送,负=消费) |
|
||||||
| 14 | balance_after | NUMERIC(18,2) | YES | | 变动后余额 |
|
| 14 | balance_after | NUMERIC(18,2) | YES | | 变动后余额 |
|
||||||
@@ -36,7 +35,10 @@
|
|||||||
| 16 | payment_method | INTEGER | YES | | 支付方式,暂未启用。 |
|
| 16 | payment_method | INTEGER | YES | | 支付方式,暂未启用。 |
|
||||||
| 17 | change_time | TIMESTAMPTZ | YES | | 变动时间 |
|
| 17 | change_time | TIMESTAMPTZ | YES | | 变动时间 |
|
||||||
| 18 | is_delete | INTEGER | YES | | 删除标记 |
|
| 18 | is_delete | INTEGER | YES | | 删除标记 |
|
||||||
| 19 | remark | VARCHAR | YES | | 备注。**样本值**: "注销会员", "充值退款" 等 |
|
| 19 | remark | VARCHAR(255) | YES | | 备注。**样本值**: "注销会员", "充值退款" 等 |
|
||||||
|
| 20 | principal_before | NUMERIC(18,2) | YES | | 变动前本金 |
|
||||||
|
| 21 | principal_after | NUMERIC(18,2) | YES | | 变动后本金 |
|
||||||
|
| 22 | principal_change_amount | NUMERIC(18,2) | YES | | 本金变动金额(正=增加,负=减少) |
|
||||||
|
|
||||||
## 卡类型余额变动分布
|
## 卡类型余额变动分布
|
||||||
|
|
||||||
@@ -58,6 +60,19 @@
|
|||||||
|
|
||||||
## 使用说明
|
## 使用说明
|
||||||
|
|
||||||
|
**版本与最新值**
|
||||||
|
本表为事实表,无 SCD2 版本字段。
|
||||||
|
|
||||||
|
- 可用时间字段:change_time
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- 取最新一条(按时间字段倒序)
|
||||||
|
SELECT *
|
||||||
|
FROM billiards_dwd.dwd_member_balance_change
|
||||||
|
ORDER BY change_time DESC NULLS LAST
|
||||||
|
LIMIT 1;
|
||||||
|
```
|
||||||
|
**使用示例**
|
||||||
```sql
|
```sql
|
||||||
-- 会员消费总额排行
|
-- 会员消费总额排行
|
||||||
SELECT
|
SELECT
|
||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
> 生成时间:2026-01-28
|
> 生成时间:2026-01-28
|
||||||
|
|
||||||
|
|
||||||
## 表信息
|
## 表信息
|
||||||
|
|
||||||
| 属性 | 值 |
|
| 属性 | 值 |
|
||||||
@@ -29,11 +28,23 @@
|
|||||||
| 9 | create_time | TIMESTAMPTZ | YES | | 创建时间 |
|
| 9 | create_time | TIMESTAMPTZ | YES | | 创建时间 |
|
||||||
| 10 | pay_time | TIMESTAMPTZ | YES | | 支付时间 |
|
| 10 | pay_time | TIMESTAMPTZ | YES | | 支付时间 |
|
||||||
| 11 | pay_date | DATE | YES | | 支付日期 |
|
| 11 | pay_date | DATE | YES | | 支付日期 |
|
||||||
|
| 12 | tenant_id | BIGINT | YES | | 租户 ID |
|
||||||
|
|
||||||
|
|
||||||
## 使用说明
|
## 使用说明
|
||||||
|
|
||||||
|
**版本与最新值**
|
||||||
|
本表为事实表,无 SCD2 版本字段。
|
||||||
|
|
||||||
|
- 可用时间字段:create_time, pay_time, pay_date
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- 取最新一条(按时间字段倒序)
|
||||||
|
SELECT *
|
||||||
|
FROM billiards_dwd.dwd_payment
|
||||||
|
ORDER BY pay_time DESC NULLS LAST
|
||||||
|
LIMIT 1;
|
||||||
|
```
|
||||||
|
**使用示例**
|
||||||
```sql
|
```sql
|
||||||
-- 每日支付金额统计
|
-- 每日支付金额统计
|
||||||
SELECT
|
SELECT
|
||||||
@@ -20,9 +20,9 @@
|
|||||||
| 1 | platform_coupon_redemption_id | BIGINT | NO | PK | 核销 ID |
|
| 1 | platform_coupon_redemption_id | BIGINT | NO | PK | 核销 ID |
|
||||||
| 2 | tenant_id | BIGINT | YES | | 租户 ID |
|
| 2 | tenant_id | BIGINT | YES | | 租户 ID |
|
||||||
| 3 | site_id | BIGINT | YES | | 门店 ID |
|
| 3 | site_id | BIGINT | YES | | 门店 ID |
|
||||||
| 4 | coupon_code | VARCHAR | YES | | 券码 |
|
| 4 | coupon_code | VARCHAR(64) | YES | | 券码 |
|
||||||
| 5 | coupon_channel | INTEGER | YES | | 券渠道。**枚举值**: 1=美团, 2=抖音 |
|
| 5 | coupon_channel | INTEGER | YES | | 券渠道。**枚举值**: 1=美团, 2=抖音 |
|
||||||
| 6 | coupon_name | VARCHAR | YES | | 券名称。**样本值**: "【全天可用】中八桌球一小时(A区)", "【全天可用】中八桌球两小时(A区)" 等 |
|
| 6 | coupon_name | VARCHAR(200) | YES | | 券名称。**样本值**: "【全天可用】中八桌球一小时(A区)", "【全天可用】中八桌球两小时(A区)" 等 |
|
||||||
| 7 | sale_price | NUMERIC(10,2) | YES | | 售卖价(元)。**样本值**: 29.90, 69.90, 59.90, 39.90, 19.90 等 |
|
| 7 | sale_price | NUMERIC(10,2) | YES | | 售卖价(元)。**样本值**: 29.90, 69.90, 59.90, 39.90, 19.90 等 |
|
||||||
| 8 | coupon_money | NUMERIC(10,2) | YES | | 券面额(元)。**样本值**: 48.00, 96.00, 116.00, 68.00 等 |
|
| 8 | coupon_money | NUMERIC(10,2) | YES | | 券面额(元)。**样本值**: 48.00, 96.00, 116.00, 68.00 等 |
|
||||||
| 9 | coupon_free_time | INTEGER | YES | | 券赠送时长(当前数据全为 0) |
|
| 9 | coupon_free_time | INTEGER | YES | | 券赠送时长(当前数据全为 0) |
|
||||||
@@ -31,8 +31,8 @@
|
|||||||
| 12 | group_package_id | BIGINT | YES | | 团购套餐 ID(当前数据全为 0) |
|
| 12 | group_package_id | BIGINT | YES | | 团购套餐 ID(当前数据全为 0) |
|
||||||
| 13 | site_order_id | BIGINT | YES | | 门店订单 ID |
|
| 13 | site_order_id | BIGINT | YES | | 门店订单 ID |
|
||||||
| 14 | table_id | BIGINT | YES | | 台桌 ID → dim_table |
|
| 14 | table_id | BIGINT | YES | | 台桌 ID → dim_table |
|
||||||
| 15 | certificate_id | VARCHAR | YES | | 凭证 ID |
|
| 15 | certificate_id | VARCHAR(64) | YES | | 凭证 ID |
|
||||||
| 16 | verify_id | VARCHAR | YES | | 核验 ID(仅抖音券有值) |
|
| 16 | verify_id | VARCHAR(64) | YES | | 核验 ID(仅抖音券有值) |
|
||||||
| 17 | use_status | INTEGER | YES | | 使用状态。**枚举值**: 1=已使用, 2=已撤销 |
|
| 17 | use_status | INTEGER | YES | | 使用状态。**枚举值**: 1=已使用, 2=已撤销 |
|
||||||
| 18 | is_delete | INTEGER | YES | | 删除标记。**枚举值**: 0=未删除 |
|
| 18 | is_delete | INTEGER | YES | | 删除标记。**枚举值**: 0=未删除 |
|
||||||
| 19 | create_time | TIMESTAMPTZ | YES | | 创建时间 |
|
| 19 | create_time | TIMESTAMPTZ | YES | | 创建时间 |
|
||||||
@@ -40,6 +40,19 @@
|
|||||||
|
|
||||||
## 使用说明
|
## 使用说明
|
||||||
|
|
||||||
|
**版本与最新值**
|
||||||
|
本表为事实表,无 SCD2 版本字段。
|
||||||
|
|
||||||
|
- 可用时间字段:coupon_free_time, create_time, consume_time
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- 取最新一条(按时间字段倒序)
|
||||||
|
SELECT *
|
||||||
|
FROM billiards_dwd.dwd_platform_coupon_redemption
|
||||||
|
ORDER BY create_time DESC NULLS LAST
|
||||||
|
LIMIT 1;
|
||||||
|
```
|
||||||
|
**使用示例**
|
||||||
```sql
|
```sql
|
||||||
-- 各渠道核销统计
|
-- 各渠道核销统计
|
||||||
SELECT
|
SELECT
|
||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
> 生成时间:2026-01-28
|
> 生成时间:2026-01-28
|
||||||
|
|
||||||
|
|
||||||
## 表信息
|
## 表信息
|
||||||
|
|
||||||
| 属性 | 值 |
|
| 属性 | 值 |
|
||||||
@@ -38,9 +37,21 @@
|
|||||||
| 18 | create_time | TIMESTAMPTZ | YES | | 创建时间 |
|
| 18 | create_time | TIMESTAMPTZ | YES | | 创建时间 |
|
||||||
| 19 | pay_time | TIMESTAMPTZ | YES | | 支付时间 |
|
| 19 | pay_time | TIMESTAMPTZ | YES | | 支付时间 |
|
||||||
|
|
||||||
|
|
||||||
## 使用说明
|
## 使用说明
|
||||||
|
|
||||||
|
**版本与最新值**
|
||||||
|
本表为事实表,无 SCD2 版本字段。
|
||||||
|
|
||||||
|
- 可用时间字段:create_time, pay_time
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- 取最新一条(按时间字段倒序)
|
||||||
|
SELECT *
|
||||||
|
FROM billiards_dwd.dwd_recharge_order
|
||||||
|
ORDER BY pay_time DESC NULLS LAST
|
||||||
|
LIMIT 1;
|
||||||
|
```
|
||||||
|
**使用示例**
|
||||||
```sql
|
```sql
|
||||||
-- 充值总额统计(不含撤销)
|
-- 充值总额统计(不含撤销)
|
||||||
SELECT
|
SELECT
|
||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
> 生成时间:2026-01-28
|
> 生成时间:2026-01-28
|
||||||
|
|
||||||
|
|
||||||
## 表信息
|
## 表信息
|
||||||
|
|
||||||
| 属性 | 值 |
|
| 属性 | 值 |
|
||||||
@@ -31,9 +30,21 @@
|
|||||||
| 11 | member_id | BIGINT | YES | | 会员 ID(当前数据全为 0) |
|
| 11 | member_id | BIGINT | YES | | 会员 ID(当前数据全为 0) |
|
||||||
| 12 | member_card_id | BIGINT | YES | | 会员卡 ID(当前数据全为 0) |
|
| 12 | member_card_id | BIGINT | YES | | 会员卡 ID(当前数据全为 0) |
|
||||||
|
|
||||||
|
|
||||||
## 使用说明
|
## 使用说明
|
||||||
|
|
||||||
|
**版本与最新值**
|
||||||
|
本表为事实表,无 SCD2 版本字段。
|
||||||
|
|
||||||
|
- 可用时间字段:pay_time, create_time
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- 取最新一条(按时间字段倒序)
|
||||||
|
SELECT *
|
||||||
|
FROM billiards_dwd.dwd_refund
|
||||||
|
ORDER BY pay_time DESC NULLS LAST
|
||||||
|
LIMIT 1;
|
||||||
|
```
|
||||||
|
**使用示例**
|
||||||
```sql
|
```sql
|
||||||
-- 退款统计
|
-- 退款统计
|
||||||
SELECT
|
SELECT
|
||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
> 生成时间:2026-01-28
|
> 生成时间:2026-01-28
|
||||||
|
|
||||||
|
|
||||||
## 表信息
|
## 表信息
|
||||||
|
|
||||||
| 属性 | 值 |
|
| 属性 | 值 |
|
||||||
@@ -21,19 +20,19 @@
|
|||||||
| 1 | order_settle_id | BIGINT | NO | PK | 结账单 ID |
|
| 1 | order_settle_id | BIGINT | NO | PK | 结账单 ID |
|
||||||
| 2 | tenant_id | BIGINT | YES | | 租户 ID |
|
| 2 | tenant_id | BIGINT | YES | | 租户 ID |
|
||||||
| 3 | site_id | BIGINT | YES | | 门店 ID → dim_site |
|
| 3 | site_id | BIGINT | YES | | 门店 ID → dim_site |
|
||||||
| 4 | site_name | VARCHAR | YES | | 门店名称。**当前值**: "朗朗桌球" |
|
| 4 | site_name | VARCHAR(100) | YES | | 门店名称。**当前值**: "朗朗桌球" |
|
||||||
| 5 | table_id | BIGINT | YES | | 台桌 ID → dim_table(0=非台桌订单,如商城订单) |
|
| 5 | table_id | BIGINT | YES | | 台桌 ID → dim_table(0=非台桌订单,如商城订单) |
|
||||||
| 6 | settle_name | VARCHAR | YES | | 结账名称。**样本值**: "商城订单", "A区 A3", "A区 A4", "斯诺克区 S1" |
|
| 6 | settle_name | VARCHAR(100) | YES | | 结账名称。**样本值**: "商城订单", "A区 A3", "A区 A4", "斯诺克区 S1" |
|
||||||
| 7 | order_trade_no | BIGINT | YES | | 订单号 |
|
| 7 | order_trade_no | BIGINT | YES | | 订单号 |
|
||||||
| 8 | create_time | TIMESTAMPTZ | YES | | 创建时间 |
|
| 8 | create_time | TIMESTAMPTZ | YES | | 创建时间 |
|
||||||
| 9 | pay_time | TIMESTAMPTZ | YES | | 支付时间 |
|
| 9 | pay_time | TIMESTAMPTZ | YES | | 支付时间 |
|
||||||
| 10 | settle_type | INTEGER | YES | | 结账类型。**枚举值**: 1=台桌结账, 3=商城订单, 6=退货订单, 7=退款订单 |
|
| 10 | settle_type | INTEGER | YES | | 结账类型。**枚举值**: 1=台桌结账, 3=商城订单, 6=退货订单, 7=退款订单 |
|
||||||
| 11 | revoke_order_id | BIGINT | YES | | 撤销订单 ID(当前数据全为 0) |
|
| 11 | revoke_order_id | BIGINT | YES | | 撤销订单 ID(当前数据全为 0) |
|
||||||
| 12 | member_id | BIGINT | YES | | 会员 ID → dim_member(0=散客,占比约 82.8%) |
|
| 12 | member_id | BIGINT | YES | | 会员 ID → dim_member(0=散客,占比约 82.8%) |
|
||||||
| 13 | member_name | VARCHAR | YES | | 会员名称 |
|
| 13 | member_name | VARCHAR(100) | YES | | 会员名称 |
|
||||||
| 14 | member_phone | VARCHAR | YES | | 会员电话 |
|
| 14 | member_phone | VARCHAR(50) | YES | | 会员电话 |
|
||||||
| 15 | member_card_account_id | BIGINT | YES | | 会员卡账户 ID(当前数据全为 0) |
|
| 15 | member_card_account_id | BIGINT | YES | | 会员卡账户 ID(当前数据全为 0) |
|
||||||
| 16 | member_card_type_name | VARCHAR | YES | | 卡类型名称(当前数据全为空) |
|
| 16 | member_card_type_name | VARCHAR(100) | YES | | 卡类型名称(当前数据全为空) |
|
||||||
| 17 | is_bind_member | BOOLEAN | YES | | 是否绑定会员。**枚举值**: False=否 |
|
| 17 | is_bind_member | BOOLEAN | YES | | 是否绑定会员。**枚举值**: False=否 |
|
||||||
| 18 | member_discount_amount | NUMERIC(18,2) | YES | | 会员折扣金额 |
|
| 18 | member_discount_amount | NUMERIC(18,2) | YES | | 会员折扣金额 |
|
||||||
| 19 | consume_money | NUMERIC(18,2) | YES | | 消费总金额(元) |
|
| 19 | consume_money | NUMERIC(18,2) | YES | | 消费总金额(元) |
|
||||||
@@ -41,7 +40,7 @@
|
|||||||
| 21 | goods_money | NUMERIC(18,2) | YES | | 商品金额 |
|
| 21 | goods_money | NUMERIC(18,2) | YES | | 商品金额 |
|
||||||
| 22 | real_goods_money | NUMERIC(18,2) | YES | | 实收商品金额 |
|
| 22 | real_goods_money | NUMERIC(18,2) | YES | | 实收商品金额 |
|
||||||
| 23 | assistant_pd_money | NUMERIC(18,2) | YES | | 助教陪打费用 |
|
| 23 | assistant_pd_money | NUMERIC(18,2) | YES | | 助教陪打费用 |
|
||||||
| 24 | assistant_cx_money | NUMERIC(18,2) | YES | | 助教促销费用 |
|
| 24 | assistant_cx_money | NUMERIC(18,2) | YES | | 助教超休费用 |
|
||||||
| 25 | adjust_amount | NUMERIC(18,2) | YES | | 调整金额 |
|
| 25 | adjust_amount | NUMERIC(18,2) | YES | | 调整金额 |
|
||||||
| 26 | pay_amount | NUMERIC(18,2) | YES | | 实付金额 |
|
| 26 | pay_amount | NUMERIC(18,2) | YES | | 实付金额 |
|
||||||
| 27 | balance_amount | NUMERIC(18,2) | YES | | 余额支付金额 |
|
| 27 | balance_amount | NUMERIC(18,2) | YES | | 余额支付金额 |
|
||||||
@@ -50,10 +49,27 @@
|
|||||||
| 30 | coupon_amount | NUMERIC(18,2) | YES | | 券抵扣金额 |
|
| 30 | coupon_amount | NUMERIC(18,2) | YES | | 券抵扣金额 |
|
||||||
| 31 | rounding_amount | NUMERIC(18,2) | YES | | 抹零金额 |
|
| 31 | rounding_amount | NUMERIC(18,2) | YES | | 抹零金额 |
|
||||||
| 32 | point_amount | NUMERIC(18,2) | YES | | 积分抵扣等值金额 |
|
| 32 | point_amount | NUMERIC(18,2) | YES | | 积分抵扣等值金额 |
|
||||||
|
| 33 | electricity_money | NUMERIC(18,2) | YES | | 电费金额 |
|
||||||
|
| 34 | real_electricity_money | NUMERIC(18,2) | YES | | 实际电费金额 |
|
||||||
|
| 35 | electricity_adjust_money | NUMERIC(18,2) | YES | | 电费调整金额 |
|
||||||
|
| 36 | pl_coupon_sale_amount | NUMERIC(18,2) | YES | | 平台券销售额 |
|
||||||
|
| 37 | mervou_sales_amount | NUMERIC(18,2) | YES | | 商户券销售额 |
|
||||||
|
|
||||||
## 使用说明
|
## 使用说明
|
||||||
|
|
||||||
|
**版本与最新值**
|
||||||
|
本表为事实表,无 SCD2 版本字段。
|
||||||
|
|
||||||
|
- 可用时间字段:create_time, pay_time
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- 取最新一条(按时间字段倒序)
|
||||||
|
SELECT *
|
||||||
|
FROM billiards_dwd.dwd_settlement_head
|
||||||
|
ORDER BY pay_time DESC NULLS LAST
|
||||||
|
LIMIT 1;
|
||||||
|
```
|
||||||
|
**使用示例**
|
||||||
```sql
|
```sql
|
||||||
-- 每日营收统计
|
-- 每日营收统计
|
||||||
SELECT
|
SELECT
|
||||||
@@ -64,7 +80,6 @@ SELECT
|
|||||||
FROM billiards_dwd.dwd_settlement_head
|
FROM billiards_dwd.dwd_settlement_head
|
||||||
GROUP BY DATE(pay_time)
|
GROUP BY DATE(pay_time)
|
||||||
ORDER BY pay_date DESC;
|
ORDER BY pay_date DESC;
|
||||||
|
|
||||||
-- 台费 vs 商品 vs 助教收入
|
-- 台费 vs 商品 vs 助教收入
|
||||||
SELECT
|
SELECT
|
||||||
SUM(table_charge_money) AS table_revenue,
|
SUM(table_charge_money) AS table_revenue,
|
||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
> 生成时间:2026-01-28
|
> 生成时间:2026-01-28
|
||||||
|
|
||||||
|
|
||||||
## 表信息
|
## 表信息
|
||||||
|
|
||||||
| 属性 | 值 |
|
| 属性 | 值 |
|
||||||
@@ -30,8 +29,8 @@
|
|||||||
| 10 | tenant_goods_category_id | BIGINT | YES | | 商品分类 ID |
|
| 10 | tenant_goods_category_id | BIGINT | YES | | 商品分类 ID |
|
||||||
| 11 | tenant_goods_business_id | BIGINT | YES | | 业务大类 ID |
|
| 11 | tenant_goods_business_id | BIGINT | YES | | 业务大类 ID |
|
||||||
| 12 | site_table_id | BIGINT | YES | | 台桌 ID(0=商城订单,非台桌消费) |
|
| 12 | site_table_id | BIGINT | YES | | 台桌 ID(0=商城订单,非台桌消费) |
|
||||||
| 13 | ledger_name | VARCHAR | YES | | 商品名称。**样本值**: "哇哈哈矿泉水", "东方树叶", "可乐" 等 |
|
| 13 | ledger_name | VARCHAR(200) | YES | | 商品名称。**样本值**: "哇哈哈矿泉水", "东方树叶", "可乐" 等 |
|
||||||
| 14 | ledger_group_name | VARCHAR | YES | | 商品分类。**样本值**: "酒水", "零食", "香烟" 等 |
|
| 14 | ledger_group_name | VARCHAR(100) | YES | | 商品分类。**样本值**: "酒水", "零食", "香烟" 等 |
|
||||||
| 15 | ledger_unit_price | NUMERIC(18,2) | YES | | 单价(元) |
|
| 15 | ledger_unit_price | NUMERIC(18,2) | YES | | 单价(元) |
|
||||||
| 16 | ledger_count | INTEGER | YES | | 购买数量。**样本值**: 1, 2, 3, 4 等 |
|
| 16 | ledger_count | INTEGER | YES | | 购买数量。**样本值**: 1, 2, 3, 4 等 |
|
||||||
| 17 | ledger_amount | NUMERIC(18,2) | YES | | 销售金额(元) |
|
| 17 | ledger_amount | NUMERIC(18,2) | YES | | 销售金额(元) |
|
||||||
@@ -41,10 +40,23 @@
|
|||||||
| 21 | ledger_status | INTEGER | YES | | 账本状态。**枚举值**: 1=已结算 |
|
| 21 | ledger_status | INTEGER | YES | | 账本状态。**枚举值**: 1=已结算 |
|
||||||
| 22 | is_delete | INTEGER | YES | | 删除标记。**枚举值**: 0=未删除 |
|
| 22 | is_delete | INTEGER | YES | | 删除标记。**枚举值**: 0=未删除 |
|
||||||
| 23 | create_time | TIMESTAMPTZ | YES | | 创建时间 |
|
| 23 | create_time | TIMESTAMPTZ | YES | | 创建时间 |
|
||||||
|
| 24 | coupon_share_money | NUMERIC(18,2) | YES | | 优惠券分摊金额 |
|
||||||
|
|
||||||
## 使用说明
|
## 使用说明
|
||||||
|
|
||||||
|
**版本与最新值**
|
||||||
|
本表为事实表,无 SCD2 版本字段。
|
||||||
|
|
||||||
|
- 可用时间字段:create_time
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- 取最新一条(按时间字段倒序)
|
||||||
|
SELECT *
|
||||||
|
FROM billiards_dwd.dwd_store_goods_sale
|
||||||
|
ORDER BY create_time DESC NULLS LAST
|
||||||
|
LIMIT 1;
|
||||||
|
```
|
||||||
|
**使用示例**
|
||||||
```sql
|
```sql
|
||||||
-- 热销商品排行
|
-- 热销商品排行
|
||||||
SELECT
|
SELECT
|
||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
> 生成时间:2026-01-28
|
> 生成时间:2026-01-28
|
||||||
|
|
||||||
|
|
||||||
## 表信息
|
## 表信息
|
||||||
|
|
||||||
| 属性 | 值 |
|
| 属性 | 值 |
|
||||||
@@ -25,14 +24,31 @@
|
|||||||
| 5 | site_id | BIGINT | YES | | 门店 ID |
|
| 5 | site_id | BIGINT | YES | | 门店 ID |
|
||||||
| 6 | table_id | BIGINT | YES | | 台桌 ID → dim_table |
|
| 6 | table_id | BIGINT | YES | | 台桌 ID → dim_table |
|
||||||
| 7 | table_area_id | BIGINT | YES | | 台区 ID |
|
| 7 | table_area_id | BIGINT | YES | | 台区 ID |
|
||||||
| 8 | table_area_name | VARCHAR | YES | | 台区名称(当前数据全为 NULL) |
|
| 8 | table_area_name | VARCHAR(64) | YES | | 台区名称(当前数据全为 NULL) |
|
||||||
| 9 | tenant_table_area_id | BIGINT | YES | | 租户台区 ID |
|
| 9 | tenant_table_area_id | BIGINT | YES | | 租户台区 ID |
|
||||||
| 10 | ledger_amount | NUMERIC(18,2) | YES | | 调整金额(元) |
|
| 10 | ledger_amount | NUMERIC(18,2) | YES | | 调整金额(元) |
|
||||||
| 11 | ledger_status | INTEGER | YES | | 账本状态。**枚举值**: 0=待确认, 1=已确认 |
|
| 11 | ledger_status | INTEGER | YES | | 账本状态。**枚举值**: 0=待确认, 1=已确认 |
|
||||||
| 12 | is_delete | INTEGER | YES | | 删除标记。**枚举值**: 0=未删除 |
|
| 12 | is_delete | INTEGER | YES | | 删除标记。**枚举值**: 0=未删除 |
|
||||||
|
| 13 | table_name | TEXT | YES | | 台桌名称 |
|
||||||
|
| 14 | table_price | NUMERIC(18,2) | YES | | 台桌价格 |
|
||||||
|
| 15 | charge_free | BOOLEAN | YES | | 是否免费 |
|
||||||
|
| 16 | adjust_time | TIMESTAMPTZ | YES | | 调整时间 |
|
||||||
|
|
||||||
## 使用说明
|
## 使用说明
|
||||||
|
|
||||||
|
**版本与最新值**
|
||||||
|
本表为事实表,无 SCD2 版本字段。
|
||||||
|
|
||||||
|
- 可用时间字段:adjust_time
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- 取最新一条(按时间字段倒序)
|
||||||
|
SELECT *
|
||||||
|
FROM billiards_dwd.dwd_table_fee_adjust
|
||||||
|
ORDER BY adjust_time DESC NULLS LAST
|
||||||
|
LIMIT 1;
|
||||||
|
```
|
||||||
|
**使用示例**
|
||||||
```sql
|
```sql
|
||||||
-- 台费调整统计
|
-- 台费调整统计
|
||||||
SELECT
|
SELECT
|
||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
> 生成时间:2026-01-28
|
> 生成时间:2026-01-28
|
||||||
|
|
||||||
|
|
||||||
## 表信息
|
## 表信息
|
||||||
|
|
||||||
| 属性 | 值 |
|
| 属性 | 值 |
|
||||||
@@ -26,10 +25,10 @@
|
|||||||
| 6 | site_id | BIGINT | YES | | 门店 ID |
|
| 6 | site_id | BIGINT | YES | | 门店 ID |
|
||||||
| 7 | site_table_id | BIGINT | YES | | 台桌 ID → dim_table |
|
| 7 | site_table_id | BIGINT | YES | | 台桌 ID → dim_table |
|
||||||
| 8 | site_table_area_id | BIGINT | YES | | 台区 ID |
|
| 8 | site_table_area_id | BIGINT | YES | | 台区 ID |
|
||||||
| 9 | site_table_area_name | VARCHAR | YES | | 台区名称。**枚举值**: "A区", "B区", "斯诺克区", "麻将房", "C区", "补时长", "VIP包厢" 等 |
|
| 9 | site_table_area_name | VARCHAR(64) | YES | | 台区名称。**枚举值**: "A区", "B区", "斯诺克区", "麻将房", "C区", "补时长", "VIP包厢" 等 |
|
||||||
| 10 | tenant_table_area_id | BIGINT | YES | | 租户级台区 ID |
|
| 10 | tenant_table_area_id | BIGINT | YES | | 租户级台区 ID |
|
||||||
| 11 | member_id | BIGINT | YES | | 会员 ID(0=散客,占比约 82.4%) |
|
| 11 | member_id | BIGINT | YES | | 会员 ID(0=散客,占比约 82.4%) |
|
||||||
| 12 | ledger_name | VARCHAR | YES | | 台桌名称。**样本值**: "A3", "A5", "A4", "S1", "B5", "M3" 等 |
|
| 12 | ledger_name | VARCHAR(64) | YES | | 台桌名称。**样本值**: "A3", "A5", "A4", "S1", "B5", "M3" 等 |
|
||||||
| 13 | ledger_unit_price | NUMERIC(18,2) | YES | | 单价(元/小时),如 48.00/58.00/68.00 |
|
| 13 | ledger_unit_price | NUMERIC(18,2) | YES | | 单价(元/小时),如 48.00/58.00/68.00 |
|
||||||
| 14 | ledger_count | INTEGER | YES | | 计费时长(秒)。**样本值**: 3600=1h, 7200=2h, 10800=3h 等 |
|
| 14 | ledger_count | INTEGER | YES | | 计费时长(秒)。**样本值**: 3600=1h, 7200=2h, 10800=3h 等 |
|
||||||
| 15 | ledger_amount | NUMERIC(18,2) | YES | | 计费金额(元) |
|
| 15 | ledger_amount | NUMERIC(18,2) | YES | | 计费金额(元) |
|
||||||
@@ -45,10 +44,24 @@
|
|||||||
| 25 | ledger_status | INTEGER | YES | | 账本状态。**枚举值**: 1=已结算 |
|
| 25 | ledger_status | INTEGER | YES | | 账本状态。**枚举值**: 1=已结算 |
|
||||||
| 26 | is_single_order | INTEGER | YES | | 是否独立订单。**枚举值**: 0=合并订单, 1=独立订单 |
|
| 26 | is_single_order | INTEGER | YES | | 是否独立订单。**枚举值**: 0=合并订单, 1=独立订单 |
|
||||||
| 27 | is_delete | INTEGER | YES | | 删除标记。**枚举值**: 0=未删除 |
|
| 27 | is_delete | INTEGER | YES | | 删除标记。**枚举值**: 0=未删除 |
|
||||||
|
| 28 | activity_discount_amount | NUMERIC(18,2) | YES | | 活动折扣金额 |
|
||||||
|
| 29 | real_service_money | NUMERIC(18,2) | YES | | 实际服务费金额 |
|
||||||
|
|
||||||
## 使用说明
|
## 使用说明
|
||||||
|
|
||||||
|
**版本与最新值**
|
||||||
|
本表为事实表,无 SCD2 版本字段。
|
||||||
|
|
||||||
|
- 可用时间字段:start_use_time, ledger_end_time, create_time
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- 取最新一条(按时间字段倒序)
|
||||||
|
SELECT *
|
||||||
|
FROM billiards_dwd.dwd_table_fee_log
|
||||||
|
ORDER BY create_time DESC NULLS LAST
|
||||||
|
LIMIT 1;
|
||||||
|
```
|
||||||
|
**使用示例**
|
||||||
```sql
|
```sql
|
||||||
-- 各台区台费收入统计
|
-- 各台区台费收入统计
|
||||||
SELECT
|
SELECT
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
# cfg_area_category 台区分类映射表
|
||||||
|
|
||||||
|
> 生成时间:2026-02-03
|
||||||
|
|
||||||
|
## 表信息
|
||||||
|
|
||||||
|
| 属性 | 值 |
|
||||||
|
|------|-----|
|
||||||
|
| Schema | billiards_dws |
|
||||||
|
| 表名 | cfg_area_category |
|
||||||
|
| 主键 | category_id |
|
||||||
|
| 数据来源 | 手工维护/seed脚本(基于dim_table实际数据) |
|
||||||
|
| 说明 | 将dim_table.site_table_area_name映射到财务报表区域分类 |
|
||||||
|
|
||||||
|
## 字段说明
|
||||||
|
|
||||||
|
| 序号 | 字段名 | 类型 | 可空 | 主键 | 说明 |
|
||||||
|
|------|--------|------|------|------|------|
|
||||||
|
| 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 | | 更新时间 |
|
||||||
|
|
||||||
|
## 分类映射示例
|
||||||
|
|
||||||
|
| 源区域名称 | 分类代码 | 分类名称 |
|
||||||
|
|------------|----------|----------|
|
||||||
|
| 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 | 补时长 |
|
||||||
|
|
||||||
|
## 使用说明
|
||||||
|
|
||||||
|
**取值方式**
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- 将台区名称映射到分类
|
||||||
|
SELECT
|
||||||
|
dt.site_table_area_name,
|
||||||
|
COALESCE(ac.category_code, 'OTHER') AS category_code,
|
||||||
|
COALESCE(ac.category_name, '其他') AS category_name
|
||||||
|
FROM billiards_dwd.dim_table dt
|
||||||
|
LEFT JOIN billiards_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 billiards_dwd.dwd_table_fee_log tfl
|
||||||
|
LEFT JOIN billiards_dwd.dim_table dt ON dt.table_id = tfl.site_table_id
|
||||||
|
LEFT JOIN billiards_dws.cfg_area_category ac ON dt.site_table_area_name = ac.source_area_name
|
||||||
|
GROUP BY COALESCE(ac.category_name, '其他');
|
||||||
|
```
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
# cfg_assistant_level_price 助教等级定价表
|
||||||
|
|
||||||
|
> 生成时间:2026-02-03
|
||||||
|
|
||||||
|
## 表信息
|
||||||
|
|
||||||
|
| 属性 | 值 |
|
||||||
|
|------|-----|
|
||||||
|
| Schema | billiards_dws |
|
||||||
|
| 表名 | cfg_assistant_level_price |
|
||||||
|
| 主键 | price_id |
|
||||||
|
| 数据来源 | 手工维护/seed脚本 |
|
||||||
|
| 说明 | 助教等级对应的基础课和附加课单价配置 |
|
||||||
|
|
||||||
|
## 字段说明
|
||||||
|
|
||||||
|
| 序号 | 字段名 | 类型 | 可空 | 主键 | 说明 |
|
||||||
|
|------|--------|------|------|------|------|
|
||||||
|
| 1 | price_id | SERIAL | NO | PK | 定价ID(自增) |
|
||||||
|
| 2 | level_code | INTEGER | NO | | 等级代码。**枚举值**: 8=助教管理, 10=初级, 20=中级, 30=高级, 40=星级 |
|
||||||
|
| 3 | level_name | VARCHAR(20) | NO | | 等级名称 |
|
||||||
|
| 4 | base_course_price | NUMERIC(10,2) | NO | | 基础课单价(元/小时) |
|
||||||
|
| 5 | bonus_course_price | NUMERIC(10,2) | NO | | 附加课单价(元/小时),固定50元 |
|
||||||
|
| 6 | effective_from | DATE | NO | | 生效起始日期(含) |
|
||||||
|
| 7 | effective_to | DATE | NO | | 生效截止日期(含) |
|
||||||
|
| 8 | description | TEXT | YES | | 说明 |
|
||||||
|
| 9 | created_at | TIMESTAMPTZ | NO | | 创建时间 |
|
||||||
|
| 10 | updated_at | TIMESTAMPTZ | NO | | 更新时间 |
|
||||||
|
|
||||||
|
## 定价配置示例
|
||||||
|
|
||||||
|
| 等级代码 | 等级名称 | 基础课单价 | 附加课单价 |
|
||||||
|
|----------|----------|------------|------------|
|
||||||
|
| 8 | 助教管理 | 98元/小时 | 50元/小时 |
|
||||||
|
| 10 | 初级 | 98元/小时 | 50元/小时 |
|
||||||
|
| 20 | 中级 | 108元/小时 | 50元/小时 |
|
||||||
|
| 30 | 高级 | 118元/小时 | 50元/小时 |
|
||||||
|
| 40 | 星级 | 138元/小时 | 50元/小时 |
|
||||||
|
|
||||||
|
## 使用说明
|
||||||
|
|
||||||
|
**取值方式**
|
||||||
|
|
||||||
|
SCD2口径:助教等级来自dim_assistant,取数时需按有效期as-of join
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- 获取助教在指定日期的等级定价
|
||||||
|
SELECT p.*
|
||||||
|
FROM billiards_dws.cfg_assistant_level_price p
|
||||||
|
JOIN billiards_dwd.dim_assistant a ON p.level_code = a.level
|
||||||
|
WHERE a.assistant_id = 123
|
||||||
|
AND a.scd2_start_time <= '2026-01-15'
|
||||||
|
AND (a.scd2_end_time IS NULL OR a.scd2_end_time > '2026-01-15')
|
||||||
|
AND p.effective_from <= '2026-01-15'
|
||||||
|
AND p.effective_to >= '2026-01-15';
|
||||||
|
```
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
# cfg_bonus_rules 奖金规则配置表
|
||||||
|
|
||||||
|
> 生成时间:2026-02-03
|
||||||
|
|
||||||
|
## 表信息
|
||||||
|
|
||||||
|
| 属性 | 值 |
|
||||||
|
|------|-----|
|
||||||
|
| Schema | billiards_dws |
|
||||||
|
| 表名 | cfg_bonus_rules |
|
||||||
|
| 主键 | rule_id |
|
||||||
|
| 数据来源 | 手工维护/seed脚本 |
|
||||||
|
| 说明 | 冲刺奖金(按小时阈值)和Top3排名奖金配置 |
|
||||||
|
|
||||||
|
## 字段说明
|
||||||
|
|
||||||
|
| 序号 | 字段名 | 类型 | 可空 | 主键 | 说明 |
|
||||||
|
|------|--------|------|------|------|------|
|
||||||
|
| 1 | rule_id | SERIAL | NO | PK | 规则ID(自增) |
|
||||||
|
| 2 | rule_type | VARCHAR(20) | NO | | 规则类型。**枚举值**: SPRINT(冲刺奖金), TOP_RANK(Top排名奖金) |
|
||||||
|
| 3 | rule_code | VARCHAR(30) | NO | | 规则代码。**枚举值**: SPRINT_190, SPRINT_220, TOP_1, TOP_2, TOP_3 |
|
||||||
|
| 4 | rule_name | VARCHAR(50) | NO | | 规则名称 |
|
||||||
|
| 5 | threshold_hours | NUMERIC(10,2) | YES | | 小时数阈值(冲刺奖金用) |
|
||||||
|
| 6 | rank_position | INTEGER | YES | | 排名位置(Top奖金用) |
|
||||||
|
| 7 | bonus_amount | NUMERIC(12,2) | NO | | 奖金金额(元) |
|
||||||
|
| 8 | is_cumulative | BOOLEAN | NO | | 是否可累计(冲刺奖金为FALSE,取最高档) |
|
||||||
|
| 9 | priority | INTEGER | NO | | 优先级(数字越大优先级越高) |
|
||||||
|
| 10 | effective_from | DATE | NO | | 生效起始日期(含) |
|
||||||
|
| 11 | effective_to | DATE | NO | | 生效截止日期(含) |
|
||||||
|
| 12 | description | TEXT | YES | | 说明 |
|
||||||
|
| 13 | created_at | TIMESTAMPTZ | NO | | 创建时间 |
|
||||||
|
| 14 | updated_at | TIMESTAMPTZ | NO | | 更新时间 |
|
||||||
|
|
||||||
|
## 奖金规则示例
|
||||||
|
|
||||||
|
### 冲刺奖金(不累计,取最高档)
|
||||||
|
| 规则代码 | 小时阈值 | 奖金金额 | 优先级 |
|
||||||
|
|----------|----------|----------|--------|
|
||||||
|
| SPRINT_190 | 190小时 | 300元 | 1 |
|
||||||
|
| SPRINT_220 | 220小时 | 800元 | 2 |
|
||||||
|
|
||||||
|
### Top3排名奖金(独立发放)
|
||||||
|
| 规则代码 | 排名 | 奖金金额 |
|
||||||
|
|----------|------|----------|
|
||||||
|
| TOP_1 | 第1名 | 1000元 |
|
||||||
|
| TOP_2 | 第2名 | 600元 |
|
||||||
|
| TOP_3 | 第3名 | 400元 |
|
||||||
|
|
||||||
|
## 使用说明
|
||||||
|
|
||||||
|
**取值方式**
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- 获取冲刺奖金(取最高档)
|
||||||
|
SELECT * FROM billiards_dws.cfg_bonus_rules
|
||||||
|
WHERE rule_type = 'SPRINT'
|
||||||
|
AND threshold_hours <= 200 -- 实际小时数
|
||||||
|
AND effective_from <= '2026-01-01'
|
||||||
|
AND effective_to >= '2026-01-01'
|
||||||
|
ORDER BY priority DESC
|
||||||
|
LIMIT 1;
|
||||||
|
|
||||||
|
-- 获取Top3排名奖金
|
||||||
|
SELECT * FROM billiards_dws.cfg_bonus_rules
|
||||||
|
WHERE rule_type = 'TOP_RANK'
|
||||||
|
AND rank_position = 1 -- 排名
|
||||||
|
AND effective_from <= '2026-01-01'
|
||||||
|
AND effective_to >= '2026-01-01';
|
||||||
|
```
|
||||||
|
|
||||||
|
**排名口径说明**
|
||||||
|
- Top3排名按有效业绩小时数(effective_hours)降序排列
|
||||||
|
- 如遇并列则都算(如2个第一,则记为2个第一,下一个是第三)
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
# cfg_performance_tier 绩效档位配置表
|
||||||
|
|
||||||
|
> 生成时间:2026-02-03
|
||||||
|
|
||||||
|
## 表信息
|
||||||
|
|
||||||
|
| 属性 | 值 |
|
||||||
|
|------|-----|
|
||||||
|
| Schema | billiards_dws |
|
||||||
|
| 表名 | cfg_performance_tier |
|
||||||
|
| 主键 | tier_id |
|
||||||
|
| 数据来源 | 手工维护/seed脚本 |
|
||||||
|
| 说明 | 助教绩效档位配置,包含6档阈值、抽成比例、假期天数 |
|
||||||
|
|
||||||
|
## 字段说明
|
||||||
|
|
||||||
|
| 序号 | 字段名 | 类型 | 可空 | 主键 | 说明 |
|
||||||
|
|------|--------|------|------|------|------|
|
||||||
|
| 1 | tier_id | SERIAL | NO | PK | 档位ID(自增) |
|
||||||
|
| 2 | tier_code | VARCHAR(20) | NO | | 档位代码。**枚举值**: T0, T1, T2, T3, T4, T5, NEW |
|
||||||
|
| 3 | tier_name | VARCHAR(50) | NO | | 档位名称 |
|
||||||
|
| 4 | tier_level | INTEGER | NO | | 档位等级(数字越大档位越高) |
|
||||||
|
| 5 | min_hours | NUMERIC(10,2) | NO | | 最低业绩小时数阈值(>=) |
|
||||||
|
| 6 | max_hours | NUMERIC(10,2) | YES | | 最高业绩小时数阈值(<,NULL表示无上限) |
|
||||||
|
| 7 | base_deduction | NUMERIC(10,2) | NO | | 专业课抽成(元/小时),球房从基础课扣除 |
|
||||||
|
| 8 | bonus_deduction_ratio | NUMERIC(5,4) | NO | | 打赏课抽成比例(0-1) |
|
||||||
|
| 9 | vacation_days | INTEGER | NO | | 次月可休假天数 |
|
||||||
|
| 10 | vacation_unlimited | BOOLEAN | NO | | 是否休假自由(5档为TRUE) |
|
||||||
|
| 11 | is_new_hire_tier | BOOLEAN | NO | | 是否为新入职专用档位 |
|
||||||
|
| 12 | effective_from | DATE | NO | | 生效起始日期(含) |
|
||||||
|
| 13 | effective_to | DATE | NO | | 生效截止日期(含) |
|
||||||
|
| 14 | description | TEXT | YES | | 档位说明 |
|
||||||
|
| 15 | created_at | TIMESTAMPTZ | NO | | 创建时间 |
|
||||||
|
| 16 | updated_at | TIMESTAMPTZ | NO | | 更新时间 |
|
||||||
|
|
||||||
|
## 档位配置示例
|
||||||
|
|
||||||
|
| 档位代码 | 档位名称 | 小时数范围 | 专业课抽成 | 打赏课抽成 | 假期 |
|
||||||
|
|----------|----------|------------|------------|------------|------|
|
||||||
|
| T0 | 0档 | 0-100 | 23元/小时 | 45% | 4天 |
|
||||||
|
| T1 | 1档 | 100-130 | 20元/小时 | 42% | 5天 |
|
||||||
|
| T2 | 2档 | 130-160 | 17元/小时 | 40% | 6天 |
|
||||||
|
| T3 | 3档 | 160-190 | 13元/小时 | 35% | 7天 |
|
||||||
|
| T4 | 4档 | 190-230 | 8元/小时 | 30% | 8天 |
|
||||||
|
| T5 | 5档 | 230+ | 0元/小时 | 0% | 自由 |
|
||||||
|
| NEW | 新入职 | 任意 | 23元/小时 | 45% | 4天 |
|
||||||
|
|
||||||
|
## 使用说明
|
||||||
|
|
||||||
|
**取值方式**
|
||||||
|
|
||||||
|
按月份匹配生效的配置:
|
||||||
|
```sql
|
||||||
|
-- 获取指定月份的档位配置
|
||||||
|
SELECT * FROM billiards_dws.cfg_performance_tier
|
||||||
|
WHERE effective_from <= '2026-01-01'
|
||||||
|
AND effective_to >= '2026-01-01'
|
||||||
|
ORDER BY min_hours;
|
||||||
|
|
||||||
|
-- 根据有效业绩小时数匹配档位
|
||||||
|
SELECT * FROM billiards_dws.cfg_performance_tier
|
||||||
|
WHERE effective_from <= '2026-01-01'
|
||||||
|
AND effective_to >= '2026-01-01'
|
||||||
|
AND min_hours <= 185 -- 有效小时数
|
||||||
|
AND (max_hours IS NULL OR max_hours > 185)
|
||||||
|
LIMIT 1;
|
||||||
|
```
|
||||||
|
|
||||||
|
**薪资计算公式**
|
||||||
|
- 基础课收入 = 基础课小时数 × (客户支付价格 - base_deduction)
|
||||||
|
- 附加课收入 = 附加课小时数 × 190 × (1 - bonus_deduction_ratio)
|
||||||
62
etl_billiards/docs/bd_manual/dws/BD_manual_cfg_skill_type.md
Normal file
62
etl_billiards/docs/bd_manual/dws/BD_manual_cfg_skill_type.md
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
# cfg_skill_type 技能→课程类型映射表
|
||||||
|
|
||||||
|
> 生成时间:2026-02-03
|
||||||
|
|
||||||
|
## 表信息
|
||||||
|
|
||||||
|
| 属性 | 值 |
|
||||||
|
|------|-----|
|
||||||
|
| Schema | billiards_dws |
|
||||||
|
| 表名 | cfg_skill_type |
|
||||||
|
| 主键 | skill_type_id |
|
||||||
|
| 数据来源 | 手工维护/seed脚本 |
|
||||||
|
| 说明 | 将skill_id映射到课程类型(基础课/附加课),避免依赖skill_name文本匹配 |
|
||||||
|
|
||||||
|
## 字段说明
|
||||||
|
|
||||||
|
| 序号 | 字段名 | 类型 | 可空 | 主键 | 说明 |
|
||||||
|
|------|--------|------|------|------|------|
|
||||||
|
| 1 | skill_type_id | SERIAL | NO | PK | 映射ID(自增) |
|
||||||
|
| 2 | skill_id | BIGINT | NO | UK | 技能ID(来自dwd_assistant_service_log.skill_id) |
|
||||||
|
| 3 | skill_name | VARCHAR(50) | YES | | 技能名称(仅用于展示和校验) |
|
||||||
|
| 4 | course_type_code | VARCHAR(10) | NO | | 课程类型代码。**枚举值**: BASE(基础课), BONUS(附加课) |
|
||||||
|
| 5 | course_type_name | VARCHAR(20) | NO | | 课程类型名称 |
|
||||||
|
| 6 | is_active | BOOLEAN | NO | | 是否启用 |
|
||||||
|
| 7 | description | TEXT | YES | | 说明 |
|
||||||
|
| 8 | created_at | TIMESTAMPTZ | NO | | 创建时间 |
|
||||||
|
| 9 | updated_at | TIMESTAMPTZ | NO | | 更新时间 |
|
||||||
|
|
||||||
|
## 技能映射示例
|
||||||
|
|
||||||
|
| skill_id | skill_name | 课程类型代码 | 课程类型名称 |
|
||||||
|
|----------|------------|--------------|--------------|
|
||||||
|
| 2791903611396869 | 陪打/PD | BASE | 基础课 |
|
||||||
|
| 2807440316432197 | 超休/CX | BONUS | 附加课 |
|
||||||
|
|
||||||
|
## 使用说明
|
||||||
|
|
||||||
|
**取值方式**
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- 将服务记录分类为基础课/附加课
|
||||||
|
SELECT
|
||||||
|
asl.*,
|
||||||
|
COALESCE(st.course_type_code, 'BASE') AS course_type_code,
|
||||||
|
COALESCE(st.course_type_name, '基础课') AS course_type_name
|
||||||
|
FROM billiards_dwd.dwd_assistant_service_log asl
|
||||||
|
LEFT JOIN billiards_dws.cfg_skill_type st
|
||||||
|
ON asl.skill_id = st.skill_id
|
||||||
|
AND st.is_active = TRUE;
|
||||||
|
|
||||||
|
-- 按课程类型汇总小时数
|
||||||
|
SELECT
|
||||||
|
COALESCE(st.course_type_code, 'BASE') AS course_type,
|
||||||
|
SUM(asl.income_seconds) / 3600.0 AS total_hours
|
||||||
|
FROM billiards_dwd.dwd_assistant_service_log asl
|
||||||
|
LEFT JOIN billiards_dws.cfg_skill_type st ON asl.skill_id = st.skill_id
|
||||||
|
GROUP BY COALESCE(st.course_type_code, 'BASE');
|
||||||
|
```
|
||||||
|
|
||||||
|
**说明**
|
||||||
|
- 基础课(陪打/PD): 按等级定价,客户支付98-138元/小时
|
||||||
|
- 附加课(超休/CX): 固定客户支付190元/小时,助教收入50元/小时
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
# dws_assistant_customer_stats 助教服务客户统计表
|
||||||
|
|
||||||
|
> 生成时间:2026-02-03
|
||||||
|
|
||||||
|
## 表信息
|
||||||
|
|
||||||
|
| 属性 | 值 |
|
||||||
|
|------|-----|
|
||||||
|
| Schema | billiards_dws |
|
||||||
|
| 表名 | dws_assistant_customer_stats |
|
||||||
|
| 主键 | id |
|
||||||
|
| 唯一键 | (site_id, assistant_id, member_id, stat_date) |
|
||||||
|
| 数据来源 | dwd_assistant_service_log |
|
||||||
|
| 更新频率 | 每日更新 |
|
||||||
|
| 说明 | 以"助教+客户"为粒度,统计服务关系和滚动窗口指标 |
|
||||||
|
|
||||||
|
## 字段说明
|
||||||
|
|
||||||
|
| 序号 | 字段名 | 类型 | 可空 | 说明 |
|
||||||
|
|------|--------|------|------|------|
|
||||||
|
| 1 | id | BIGSERIAL | NO | 自增主键 |
|
||||||
|
| 2 | site_id | BIGINT | NO | 门店ID |
|
||||||
|
| 3 | tenant_id | BIGINT | NO | 租户ID |
|
||||||
|
| 4 | assistant_id | BIGINT | NO | 助教ID |
|
||||||
|
| 5 | assistant_nickname | VARCHAR(50) | YES | 助教花名 |
|
||||||
|
| 6 | member_id | BIGINT | NO | 客户ID(member_id=0散客不入此表) |
|
||||||
|
| 7 | member_nickname | VARCHAR(100) | YES | 客户昵称 |
|
||||||
|
| 8 | member_mobile | VARCHAR(20) | YES | 客户手机号(脱敏) |
|
||||||
|
| 9 | stat_date | DATE | NO | 统计基准日期 |
|
||||||
|
| 10 | first_service_date | DATE | YES | 首次服务日期 |
|
||||||
|
| 11 | last_service_date | DATE | YES | 最近服务日期 |
|
||||||
|
| 12 | total_service_count | INTEGER | NO | 累计服务次数 |
|
||||||
|
| 13 | total_service_hours | NUMERIC(10,2) | NO | 累计服务小时数 |
|
||||||
|
| 14 | total_service_amount | NUMERIC(12,2) | NO | 累计服务金额 |
|
||||||
|
| 15-20 | service_count_7d/10d/15d/30d/60d/90d | INTEGER | NO | 近N天服务次数 |
|
||||||
|
| 21-26 | service_hours_7d/10d/15d/30d/60d/90d | NUMERIC(10,2) | NO | 近N天服务小时数 |
|
||||||
|
| 27-32 | service_amount_7d/10d/15d/30d/60d/90d | NUMERIC(12,2) | NO | 近N天服务金额 |
|
||||||
|
| 33 | days_since_last | INTEGER | YES | 距离最近服务的天数 |
|
||||||
|
| 34 | is_active_7d | BOOLEAN | NO | 近7天是否活跃 |
|
||||||
|
| 35 | is_active_30d | BOOLEAN | NO | 近30天是否活跃 |
|
||||||
|
| 36 | created_at | TIMESTAMPTZ | NO | 创建时间 |
|
||||||
|
| 37 | updated_at | TIMESTAMPTZ | NO | 更新时间 |
|
||||||
|
|
||||||
|
## 数据来源
|
||||||
|
|
||||||
|
### 滚动窗口计算
|
||||||
|
```sql
|
||||||
|
-- 统计每个助教-客户组合的滚动窗口指标
|
||||||
|
WITH service_data AS (
|
||||||
|
SELECT
|
||||||
|
site_id,
|
||||||
|
site_assistant_id AS assistant_id,
|
||||||
|
tenant_member_id AS member_id,
|
||||||
|
DATE(create_time) AS service_date,
|
||||||
|
COUNT(*) AS service_count,
|
||||||
|
SUM(income_seconds) / 3600.0 AS service_hours,
|
||||||
|
SUM(ledger_amount) AS service_amount
|
||||||
|
FROM billiards_dwd.dwd_assistant_service_log
|
||||||
|
WHERE is_delete = 0
|
||||||
|
AND tenant_member_id != 0 -- 排除散客
|
||||||
|
GROUP BY site_id, site_assistant_id, tenant_member_id, DATE(create_time)
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
assistant_id,
|
||||||
|
member_id,
|
||||||
|
:stat_date AS stat_date,
|
||||||
|
MIN(service_date) AS first_service_date,
|
||||||
|
MAX(service_date) AS last_service_date,
|
||||||
|
SUM(service_count) AS total_service_count,
|
||||||
|
SUM(CASE WHEN service_date >= :stat_date - 6 THEN service_count ELSE 0 END) AS service_count_7d,
|
||||||
|
SUM(CASE WHEN service_date >= :stat_date - 29 THEN service_count ELSE 0 END) AS service_count_30d,
|
||||||
|
-- ... 其他窗口
|
||||||
|
FROM service_data
|
||||||
|
GROUP BY assistant_id, member_id;
|
||||||
|
```
|
||||||
|
|
||||||
|
## 使用说明
|
||||||
|
|
||||||
|
**散客处理**
|
||||||
|
- member_id=0 的散客不进入此表统计
|
||||||
|
- 仅统计有会员身份的客户
|
||||||
|
|
||||||
|
**活跃度判断**
|
||||||
|
```sql
|
||||||
|
-- 近7天活跃 = 近7天有服务记录
|
||||||
|
is_active_7d = (service_count_7d > 0)
|
||||||
|
-- 近30天活跃 = 近30天有服务记录
|
||||||
|
is_active_30d = (service_count_30d > 0)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 可回溯性
|
||||||
|
|
||||||
|
| 项目 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| 可回溯 | ✅ 完全可回溯 |
|
||||||
|
| 数据范围 | 2025-07-21 ~ 至今 |
|
||||||
|
| 依赖表 | dwd_assistant_service_log, dim_member |
|
||||||
|
| 注意事项 | 滚动窗口需要足够的历史数据支撑 |
|
||||||
@@ -0,0 +1,109 @@
|
|||||||
|
# dws_assistant_daily_detail 助教日度业绩明细表
|
||||||
|
|
||||||
|
> 生成时间:2026-02-03
|
||||||
|
|
||||||
|
## 表信息
|
||||||
|
|
||||||
|
| 属性 | 值 |
|
||||||
|
|------|-----|
|
||||||
|
| Schema | billiards_dws |
|
||||||
|
| 表名 | dws_assistant_daily_detail |
|
||||||
|
| 主键 | id |
|
||||||
|
| 唯一键 | (site_id, assistant_id, stat_date) |
|
||||||
|
| 数据来源 | dwd_assistant_service_log + dwd_assistant_trash_event |
|
||||||
|
| 更新频率 | 每小时增量更新 |
|
||||||
|
| 说明 | 以"助教+日期"为粒度,汇总每日业绩明细 |
|
||||||
|
|
||||||
|
## 字段说明
|
||||||
|
|
||||||
|
| 序号 | 字段名 | 类型 | 可空 | 说明 |
|
||||||
|
|------|--------|------|------|------|
|
||||||
|
| 1 | id | BIGSERIAL | NO | 自增主键 |
|
||||||
|
| 2 | site_id | BIGINT | NO | 门店ID |
|
||||||
|
| 3 | tenant_id | BIGINT | NO | 租户ID |
|
||||||
|
| 4 | assistant_id | BIGINT | NO | 助教ID(dim_assistant.assistant_id) |
|
||||||
|
| 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 | 助教等级名称 |
|
||||||
|
| 9 | total_service_count | INTEGER | NO | 总服务次数 |
|
||||||
|
| 10 | base_service_count | INTEGER | NO | 基础课服务次数 |
|
||||||
|
| 11 | bonus_service_count | INTEGER | NO | 附加课服务次数 |
|
||||||
|
| 12 | total_seconds | INTEGER | NO | 总计费时长(秒) |
|
||||||
|
| 13 | base_seconds | INTEGER | NO | 基础课计费时长(秒) |
|
||||||
|
| 14 | bonus_seconds | INTEGER | NO | 附加课计费时长(秒) |
|
||||||
|
| 15 | total_hours | NUMERIC(10,2) | NO | 总计费小时数 |
|
||||||
|
| 16 | base_hours | NUMERIC(10,2) | NO | 基础课小时数 |
|
||||||
|
| 17 | bonus_hours | NUMERIC(10,2) | NO | 附加课小时数 |
|
||||||
|
| 18 | total_ledger_amount | NUMERIC(12,2) | NO | 总计费金额(元) |
|
||||||
|
| 19 | base_ledger_amount | NUMERIC(12,2) | NO | 基础课计费金额 |
|
||||||
|
| 20 | bonus_ledger_amount | NUMERIC(12,2) | NO | 附加课计费金额 |
|
||||||
|
| 21 | unique_customers | INTEGER | NO | 服务客户数(去重) |
|
||||||
|
| 22 | unique_tables | INTEGER | NO | 服务台桌数(去重) |
|
||||||
|
| 23 | trashed_seconds | INTEGER | NO | 被废除的服务时长(秒) |
|
||||||
|
| 24 | trashed_count | INTEGER | NO | 被废除的服务次数 |
|
||||||
|
| 25 | created_at | TIMESTAMPTZ | NO | 创建时间 |
|
||||||
|
| 26 | updated_at | TIMESTAMPTZ | NO | 更新时间 |
|
||||||
|
|
||||||
|
## 数据来源
|
||||||
|
|
||||||
|
### 主要来源:dwd_assistant_service_log
|
||||||
|
```sql
|
||||||
|
SELECT
|
||||||
|
site_id,
|
||||||
|
DATE(create_time) AS stat_date,
|
||||||
|
site_assistant_id AS assistant_id,
|
||||||
|
nickname AS assistant_nickname,
|
||||||
|
COUNT(*) AS total_service_count,
|
||||||
|
SUM(income_seconds) AS total_seconds,
|
||||||
|
SUM(ledger_amount) AS total_ledger_amount,
|
||||||
|
COUNT(DISTINCT tenant_member_id) AS unique_customers,
|
||||||
|
COUNT(DISTINCT site_table_id) AS unique_tables
|
||||||
|
FROM billiards_dwd.dwd_assistant_service_log
|
||||||
|
WHERE is_delete = 0
|
||||||
|
GROUP BY site_id, DATE(create_time), site_assistant_id, nickname;
|
||||||
|
```
|
||||||
|
|
||||||
|
### 废除记录:dwd_assistant_trash_event
|
||||||
|
```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 billiards_dwd.dwd_assistant_trash_event
|
||||||
|
GROUP BY site_id, DATE(create_time), assistant_no, assistant_name;
|
||||||
|
```
|
||||||
|
|
||||||
|
## 使用说明
|
||||||
|
|
||||||
|
**时间分层查询**
|
||||||
|
```sql
|
||||||
|
-- 近2天
|
||||||
|
SELECT * FROM billiards_dws.dws_assistant_daily_detail
|
||||||
|
WHERE stat_date >= CURRENT_DATE - 1;
|
||||||
|
|
||||||
|
-- 近1月
|
||||||
|
SELECT * FROM billiards_dws.dws_assistant_daily_detail
|
||||||
|
WHERE stat_date >= CURRENT_DATE - INTERVAL '1 month';
|
||||||
|
|
||||||
|
-- 月度汇总
|
||||||
|
SELECT
|
||||||
|
assistant_id,
|
||||||
|
DATE_TRUNC('month', stat_date) AS stat_month,
|
||||||
|
SUM(total_hours) AS total_hours,
|
||||||
|
SUM(base_hours) AS base_hours,
|
||||||
|
SUM(bonus_hours) AS bonus_hours
|
||||||
|
FROM billiards_dws.dws_assistant_daily_detail
|
||||||
|
GROUP BY assistant_id, DATE_TRUNC('month', stat_date);
|
||||||
|
```
|
||||||
|
|
||||||
|
## 可回溯性
|
||||||
|
|
||||||
|
| 项目 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| 可回溯 | ✅ 完全可回溯 |
|
||||||
|
| 数据范围 | 2025-07-21 ~ 至今 |
|
||||||
|
| 依赖表 | dwd_assistant_service_log, dwd_assistant_trash_event, dim_assistant |
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
# dws_assistant_finance_analysis 助教收支分析表
|
||||||
|
|
||||||
|
> 生成时间:2026-02-03
|
||||||
|
|
||||||
|
## 表信息
|
||||||
|
|
||||||
|
| 属性 | 值 |
|
||||||
|
|------|-----|
|
||||||
|
| Schema | billiards_dws |
|
||||||
|
| 表名 | dws_assistant_finance_analysis |
|
||||||
|
| 主键 | id |
|
||||||
|
| 唯一键 | (site_id, stat_date, assistant_id) |
|
||||||
|
| 数据来源 | dwd_assistant_service_log + dws_assistant_salary_calc |
|
||||||
|
| 更新频率 | 每日更新 |
|
||||||
|
| 说明 | 以"日期+助教"为粒度,分析助教产出的收入和成本 |
|
||||||
|
|
||||||
|
## 字段说明
|
||||||
|
|
||||||
|
| 序号 | 字段名 | 类型 | 可空 | 说明 |
|
||||||
|
|------|--------|------|------|------|
|
||||||
|
| 1 | id | BIGSERIAL | NO | 自增主键 |
|
||||||
|
| 2 | site_id | BIGINT | NO | 门店ID |
|
||||||
|
| 3 | tenant_id | BIGINT | NO | 租户ID |
|
||||||
|
| 4 | stat_date | DATE | NO | 统计日期 |
|
||||||
|
| 5 | assistant_id | BIGINT | NO | 助教ID |
|
||||||
|
| 6 | assistant_nickname | VARCHAR(50) | YES | 助教花名 |
|
||||||
|
| 7 | revenue_total | NUMERIC(14,2) | NO | 助教产出收入(ledger_amount汇总) |
|
||||||
|
| 8 | revenue_base | NUMERIC(14,2) | NO | 基础课收入 |
|
||||||
|
| 9 | revenue_bonus | NUMERIC(14,2) | NO | 附加课收入 |
|
||||||
|
| 10 | cost_daily | NUMERIC(14,2) | NO | 日均工资成本(月工资/工作天数) |
|
||||||
|
| 11 | gross_profit | NUMERIC(14,2) | NO | 毛利 = 收入 - 成本 |
|
||||||
|
| 12 | gross_margin | NUMERIC(5,4) | NO | 毛利率 |
|
||||||
|
| 13 | service_count | INTEGER | NO | 服务次数 |
|
||||||
|
| 14 | service_hours | NUMERIC(10,2) | NO | 服务小时数 |
|
||||||
|
| 15 | unique_customers | INTEGER | NO | 服务客户数 |
|
||||||
|
| 16 | created_at | TIMESTAMPTZ | NO | 创建时间 |
|
||||||
|
| 17 | updated_at | TIMESTAMPTZ | NO | 更新时间 |
|
||||||
|
|
||||||
|
## 数据来源
|
||||||
|
|
||||||
|
### 收入来源:dwd_assistant_service_log
|
||||||
|
```sql
|
||||||
|
SELECT
|
||||||
|
DATE(create_time) AS stat_date,
|
||||||
|
site_assistant_id AS assistant_id,
|
||||||
|
SUM(ledger_amount) AS revenue_total,
|
||||||
|
SUM(CASE WHEN skill_id = 2791903611396869 THEN ledger_amount ELSE 0 END) AS revenue_base,
|
||||||
|
SUM(CASE WHEN skill_id = 2807440316432197 THEN ledger_amount ELSE 0 END) AS revenue_bonus,
|
||||||
|
COUNT(*) AS service_count,
|
||||||
|
SUM(income_seconds) / 3600.0 AS service_hours,
|
||||||
|
COUNT(DISTINCT tenant_member_id) AS unique_customers
|
||||||
|
FROM billiards_dwd.dwd_assistant_service_log
|
||||||
|
WHERE is_delete = 0
|
||||||
|
GROUP BY DATE(create_time), site_assistant_id;
|
||||||
|
```
|
||||||
|
|
||||||
|
### 成本来源:dws_assistant_salary_calc
|
||||||
|
```sql
|
||||||
|
-- 日均成本 = 月度应发工资 / 当月工作天数
|
||||||
|
SELECT
|
||||||
|
assistant_id,
|
||||||
|
salary_month,
|
||||||
|
gross_salary / NULLIF(work_days, 0) AS cost_daily
|
||||||
|
FROM billiards_dws.dws_assistant_salary_calc sc
|
||||||
|
JOIN billiards_dws.dws_assistant_monthly_summary ms
|
||||||
|
ON sc.assistant_id = ms.assistant_id AND sc.salary_month = ms.stat_month;
|
||||||
|
```
|
||||||
|
|
||||||
|
## 使用说明
|
||||||
|
|
||||||
|
**毛利计算**
|
||||||
|
```
|
||||||
|
gross_profit = revenue_total - cost_daily
|
||||||
|
gross_margin = gross_profit / NULLIF(revenue_total, 0)
|
||||||
|
```
|
||||||
|
|
||||||
|
**注意事项**
|
||||||
|
- cost_daily 基于月度工资分摊,非实际日薪
|
||||||
|
- 当月数据在月末工资计算前 cost_daily 可能不准确
|
||||||
|
|
||||||
|
## 可回溯性
|
||||||
|
|
||||||
|
| 项目 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| 可回溯 | ⚠️ 部分可回溯 |
|
||||||
|
| 数据范围 | 2025-07-21 ~ 至今 |
|
||||||
|
| 依赖表 | dwd_assistant_service_log, dws_assistant_salary_calc |
|
||||||
|
| 限制 | cost_daily 依赖 salary_calc,需先完成薪资计算 |
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
# dws_assistant_monthly_summary 助教月度业绩汇总表
|
||||||
|
|
||||||
|
> 生成时间:2026-02-03
|
||||||
|
|
||||||
|
## 表信息
|
||||||
|
|
||||||
|
| 属性 | 值 |
|
||||||
|
|------|-----|
|
||||||
|
| Schema | billiards_dws |
|
||||||
|
| 表名 | dws_assistant_monthly_summary |
|
||||||
|
| 主键 | id |
|
||||||
|
| 唯一键 | (site_id, assistant_id, stat_month) |
|
||||||
|
| 数据来源 | dws_assistant_daily_detail 聚合 + cfg_performance_tier |
|
||||||
|
| 更新频率 | 每日更新当月数据 |
|
||||||
|
| 说明 | 以"助教+月份"为粒度,汇总月度业绩及档位计算 |
|
||||||
|
|
||||||
|
## 字段说明
|
||||||
|
|
||||||
|
| 序号 | 字段名 | 类型 | 可空 | 说明 |
|
||||||
|
|------|--------|------|------|------|
|
||||||
|
| 1 | id | BIGSERIAL | NO | 自增主键 |
|
||||||
|
| 2 | site_id | BIGINT | NO | 门店ID |
|
||||||
|
| 3 | tenant_id | BIGINT | NO | 租户ID |
|
||||||
|
| 4 | assistant_id | BIGINT | NO | 助教ID |
|
||||||
|
| 5 | assistant_nickname | VARCHAR(50) | YES | 助教花名 |
|
||||||
|
| 6 | stat_month | DATE | NO | 统计月份(月第一天,如2026-01-01) |
|
||||||
|
| 7 | assistant_level_code | INTEGER | YES | 助教等级代码(月末时点) |
|
||||||
|
| 8 | assistant_level_name | VARCHAR(20) | YES | 助教等级名称 |
|
||||||
|
| 9 | hire_date | DATE | YES | 入职日期 |
|
||||||
|
| 10 | is_new_hire | BOOLEAN | NO | 是否新入职(入职日期 >= 月1日0点) |
|
||||||
|
| 11 | work_days | INTEGER | NO | 有服务天数 |
|
||||||
|
| 12 | total_service_count | INTEGER | NO | 总服务次数 |
|
||||||
|
| 13 | base_service_count | INTEGER | NO | 基础课服务次数 |
|
||||||
|
| 14 | bonus_service_count | INTEGER | NO | 附加课服务次数 |
|
||||||
|
| 15 | total_hours | NUMERIC(10,2) | NO | 总计费小时数 |
|
||||||
|
| 16 | base_hours | NUMERIC(10,2) | NO | 基础课小时数 |
|
||||||
|
| 17 | bonus_hours | NUMERIC(10,2) | NO | 附加课小时数 |
|
||||||
|
| 18 | effective_hours | NUMERIC(10,2) | NO | 有效业绩小时数(影响档位)= total_hours - trashed_hours |
|
||||||
|
| 19 | trashed_hours | NUMERIC(10,2) | NO | 被废除小时数 |
|
||||||
|
| 20 | total_ledger_amount | NUMERIC(12,2) | NO | 总计费金额 |
|
||||||
|
| 21 | base_ledger_amount | NUMERIC(12,2) | NO | 基础课计费金额 |
|
||||||
|
| 22 | bonus_ledger_amount | NUMERIC(12,2) | NO | 附加课计费金额 |
|
||||||
|
| 23 | unique_customers | INTEGER | NO | 月度服务客户数(去重) |
|
||||||
|
| 24 | unique_tables | INTEGER | NO | 月度服务台桌数(去重) |
|
||||||
|
| 25 | avg_service_seconds | NUMERIC(10,2) | NO | 平均单次服务时长(秒) |
|
||||||
|
| 26 | tier_id | INTEGER | YES | 匹配的档位ID |
|
||||||
|
| 27 | tier_code | VARCHAR(20) | YES | 档位代码(T0-T5/NEW) |
|
||||||
|
| 28 | tier_name | VARCHAR(50) | YES | 档位名称 |
|
||||||
|
| 29 | rank_by_hours | INTEGER | YES | 月度排名(按effective_hours降序) |
|
||||||
|
| 30 | rank_with_ties | INTEGER | YES | 考虑并列的排名 |
|
||||||
|
| 31 | created_at | TIMESTAMPTZ | NO | 创建时间 |
|
||||||
|
| 32 | updated_at | TIMESTAMPTZ | NO | 更新时间 |
|
||||||
|
|
||||||
|
## 数据来源
|
||||||
|
|
||||||
|
### 从日度明细聚合
|
||||||
|
```sql
|
||||||
|
SELECT
|
||||||
|
site_id,
|
||||||
|
tenant_id,
|
||||||
|
assistant_id,
|
||||||
|
DATE_TRUNC('month', stat_date)::DATE AS stat_month,
|
||||||
|
COUNT(DISTINCT stat_date) AS work_days,
|
||||||
|
SUM(total_service_count) AS total_service_count,
|
||||||
|
SUM(base_service_count) AS base_service_count,
|
||||||
|
SUM(bonus_service_count) AS bonus_service_count,
|
||||||
|
SUM(total_hours) AS total_hours,
|
||||||
|
SUM(base_hours) AS base_hours,
|
||||||
|
SUM(bonus_hours) AS bonus_hours,
|
||||||
|
SUM(trashed_seconds) / 3600.0 AS trashed_hours
|
||||||
|
FROM billiards_dws.dws_assistant_daily_detail
|
||||||
|
GROUP BY site_id, tenant_id, assistant_id, DATE_TRUNC('month', stat_date);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 档位匹配
|
||||||
|
```sql
|
||||||
|
-- 根据有效业绩匹配档位
|
||||||
|
SELECT * FROM billiards_dws.cfg_performance_tier
|
||||||
|
WHERE min_hours <= :effective_hours
|
||||||
|
AND (max_hours IS NULL OR max_hours > :effective_hours)
|
||||||
|
AND effective_from <= :stat_month
|
||||||
|
AND effective_to >= :stat_month
|
||||||
|
AND is_new_hire_tier = :is_new_hire
|
||||||
|
LIMIT 1;
|
||||||
|
```
|
||||||
|
|
||||||
|
## 使用说明
|
||||||
|
|
||||||
|
**新入职判断**
|
||||||
|
- 入职日期 >= 统计月1日0点 则为新入职
|
||||||
|
- 新入职使用NEW档位配置
|
||||||
|
|
||||||
|
**排名计算**
|
||||||
|
```sql
|
||||||
|
-- rank_with_ties: 并列排名(如2个第一则都是1,下一个是3)
|
||||||
|
SELECT
|
||||||
|
assistant_id,
|
||||||
|
effective_hours,
|
||||||
|
RANK() OVER (ORDER BY effective_hours DESC) AS rank_with_ties
|
||||||
|
FROM billiards_dws.dws_assistant_monthly_summary
|
||||||
|
WHERE stat_month = '2026-01-01';
|
||||||
|
```
|
||||||
|
|
||||||
|
## 可回溯性
|
||||||
|
|
||||||
|
| 项目 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| 可回溯 | ✅ 完全可回溯 |
|
||||||
|
| 数据范围 | 2025年8月起(需要完整月数据) |
|
||||||
|
| 依赖表 | dws_assistant_daily_detail, cfg_performance_tier, dim_assistant |
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
# dws_assistant_recharge_commission 助教充值提成表
|
||||||
|
|
||||||
|
> 生成时间:2026-02-03
|
||||||
|
|
||||||
|
## 表信息
|
||||||
|
|
||||||
|
| 属性 | 值 |
|
||||||
|
|------|-----|
|
||||||
|
| Schema | billiards_dws |
|
||||||
|
| 表名 | dws_assistant_recharge_commission |
|
||||||
|
| 主键 | id |
|
||||||
|
| 数据来源 | Excel手动导入 |
|
||||||
|
| 更新频率 | 按需导入 |
|
||||||
|
| 说明 | 以"助教+月份+充值订单"为粒度,记录充值提成 |
|
||||||
|
|
||||||
|
## 字段说明
|
||||||
|
|
||||||
|
| 序号 | 字段名 | 类型 | 可空 | 说明 |
|
||||||
|
|------|--------|------|------|------|
|
||||||
|
| 1 | id | BIGSERIAL | NO | 自增主键 |
|
||||||
|
| 2 | site_id | BIGINT | NO | 门店ID |
|
||||||
|
| 3 | tenant_id | BIGINT | NO | 租户ID |
|
||||||
|
| 4 | assistant_id | BIGINT | NO | 助教ID |
|
||||||
|
| 5 | assistant_nickname | VARCHAR(50) | YES | 助教花名 |
|
||||||
|
| 6 | commission_month | DATE | NO | 提成月份(月第一天) |
|
||||||
|
| 7 | recharge_order_id | BIGINT | YES | 充值订单ID |
|
||||||
|
| 8 | recharge_order_no | VARCHAR(50) | YES | 充值订单号 |
|
||||||
|
| 9 | recharge_amount | NUMERIC(12,2) | NO | 充值订单金额 |
|
||||||
|
| 10 | commission_amount | NUMERIC(12,2) | NO | 提成金额 |
|
||||||
|
| 11 | commission_ratio | NUMERIC(5,4) | YES | 提成比例 |
|
||||||
|
| 12 | import_batch_no | VARCHAR(50) | YES | 导入批次号 |
|
||||||
|
| 13 | import_file_name | VARCHAR(200) | YES | 导入文件名 |
|
||||||
|
| 14 | import_time | TIMESTAMPTZ | YES | 导入时间 |
|
||||||
|
| 15 | import_user | VARCHAR(50) | YES | 导入操作人 |
|
||||||
|
| 16 | remark | TEXT | YES | 备注 |
|
||||||
|
| 17 | created_at | TIMESTAMPTZ | NO | 创建时间 |
|
||||||
|
| 18 | updated_at | TIMESTAMPTZ | NO | 更新时间 |
|
||||||
|
|
||||||
|
## Excel导入模板
|
||||||
|
|
||||||
|
| 月份 | 助教号 | 助教花名 | 充值订单金额 | 提成金额 | 备注 |
|
||||||
|
|------|--------|----------|--------------|----------|------|
|
||||||
|
| 2026-01 | 1 | 小燕 | 5000.00 | 300.00 | ge |
|
||||||
|
| 2026-01 | 2 | 小明 | 3000.00 | 180.00 | 续充 |
|
||||||
|
|
||||||
|
### 导入规则
|
||||||
|
- **月份**: 必填,格式 2026-01 或 2026/01/01
|
||||||
|
- **助教号**: 必填,数字(如 1, 2, 31)
|
||||||
|
- **助教花名**: 必填,与助教号组合确定唯一助教
|
||||||
|
- **充值订单金额**: 选填,单位:元
|
||||||
|
- **提成金额**: 必填,单位:元
|
||||||
|
- **备注**: 选填
|
||||||
|
|
||||||
|
### 助教匹配逻辑
|
||||||
|
```sql
|
||||||
|
-- 通过 assistant_no + nickname 查找 assistant_id
|
||||||
|
SELECT assistant_id
|
||||||
|
FROM billiards_dwd.dim_assistant
|
||||||
|
WHERE assistant_no = :assistant_no
|
||||||
|
AND nickname = :nickname
|
||||||
|
AND scd2_is_current = 1;
|
||||||
|
```
|
||||||
|
|
||||||
|
## 使用说明
|
||||||
|
|
||||||
|
**汇总到薪资计算**
|
||||||
|
```sql
|
||||||
|
-- 获取助教某月的充值提成总额
|
||||||
|
SELECT
|
||||||
|
assistant_id,
|
||||||
|
commission_month,
|
||||||
|
SUM(commission_amount) AS total_commission
|
||||||
|
FROM billiards_dws.dws_assistant_recharge_commission
|
||||||
|
WHERE commission_month = '2026-01-01'
|
||||||
|
GROUP BY assistant_id, commission_month;
|
||||||
|
```
|
||||||
|
|
||||||
|
## 可回溯性
|
||||||
|
|
||||||
|
| 项目 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| 可回溯 | ❌ 不可自动回溯 |
|
||||||
|
| 原因 | 数据来源为Excel手工导入,DWD层无此数据 |
|
||||||
|
| 处理 | 需要人工补录历史数据 |
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
# dws_assistant_salary_calc 助教工资计算详情表
|
||||||
|
|
||||||
|
> 生成时间:2026-02-03
|
||||||
|
|
||||||
|
## 表信息
|
||||||
|
|
||||||
|
| 属性 | 值 |
|
||||||
|
|------|-----|
|
||||||
|
| Schema | billiards_dws |
|
||||||
|
| 表名 | dws_assistant_salary_calc |
|
||||||
|
| 主键 | id |
|
||||||
|
| 唯一键 | (site_id, assistant_id, salary_month) |
|
||||||
|
| 数据来源 | dws_assistant_monthly_summary + cfg_* 配置表 |
|
||||||
|
| 更新频率 | 月初计算上月工资 |
|
||||||
|
| 说明 | 以"助教+月份"为粒度,计算月度工资明细 |
|
||||||
|
|
||||||
|
## 字段说明
|
||||||
|
|
||||||
|
| 序号 | 字段名 | 类型 | 可空 | 说明 |
|
||||||
|
|------|--------|------|------|------|
|
||||||
|
| 1 | id | BIGSERIAL | NO | 自增主键 |
|
||||||
|
| 2 | site_id | BIGINT | NO | 门店ID |
|
||||||
|
| 3 | tenant_id | BIGINT | NO | 租户ID |
|
||||||
|
| 4 | assistant_id | BIGINT | NO | 助教ID |
|
||||||
|
| 5 | assistant_nickname | VARCHAR(50) | YES | 助教花名 |
|
||||||
|
| 6 | salary_month | DATE | NO | 工资月份(月第一天) |
|
||||||
|
| 7 | assistant_level_code | INTEGER | YES | 助教等级代码 |
|
||||||
|
| 8 | assistant_level_name | VARCHAR(20) | YES | 助教等级名称 |
|
||||||
|
| 9 | hire_date | DATE | YES | 入职日期 |
|
||||||
|
| 10 | is_new_hire | BOOLEAN | NO | 是否新入职 |
|
||||||
|
| 11 | effective_hours | NUMERIC(10,2) | NO | 有效业绩小时数 |
|
||||||
|
| 12 | base_hours | NUMERIC(10,2) | NO | 基础课小时数 |
|
||||||
|
| 13 | bonus_hours | NUMERIC(10,2) | NO | 附加课小时数 |
|
||||||
|
| 14 | tier_id | INTEGER | YES | 档位ID |
|
||||||
|
| 15 | tier_code | VARCHAR(20) | YES | 档位代码 |
|
||||||
|
| 16 | tier_name | VARCHAR(50) | YES | 档位名称 |
|
||||||
|
| 17 | rank_with_ties | INTEGER | YES | 月度排名(考虑并列) |
|
||||||
|
| 18 | base_course_price | NUMERIC(10,2) | NO | 基础课客户支付价格 |
|
||||||
|
| 19 | bonus_course_price | NUMERIC(10,2) | NO | 附加课客户支付价格(固定190) |
|
||||||
|
| 20 | base_deduction | NUMERIC(10,2) | NO | 专业课抽成(元/小时) |
|
||||||
|
| 21 | bonus_deduction_ratio | NUMERIC(5,4) | NO | 打赏课抽成比例 |
|
||||||
|
| 22 | base_income | NUMERIC(12,2) | NO | 基础课收入 |
|
||||||
|
| 23 | bonus_income | NUMERIC(12,2) | NO | 附加课收入 |
|
||||||
|
| 24 | total_course_income | NUMERIC(12,2) | NO | 课时收入合计 |
|
||||||
|
| 25 | sprint_bonus | NUMERIC(12,2) | NO | 冲刺奖金 |
|
||||||
|
| 26 | top_rank_bonus | NUMERIC(12,2) | NO | Top3排名奖金 |
|
||||||
|
| 27 | recharge_commission | NUMERIC(12,2) | NO | 充值提成 |
|
||||||
|
| 28 | other_bonus | NUMERIC(12,2) | NO | 其他奖金 |
|
||||||
|
| 29 | total_bonus | NUMERIC(12,2) | NO | 奖金合计 |
|
||||||
|
| 30 | gross_salary | NUMERIC(12,2) | NO | 应发工资 |
|
||||||
|
| 31 | vacation_days | INTEGER | NO | 次月可休假天数 |
|
||||||
|
| 32 | vacation_unlimited | BOOLEAN | NO | 休假自由标记 |
|
||||||
|
| 33 | calc_notes | TEXT | YES | 计算备注 |
|
||||||
|
| 34 | created_at | TIMESTAMPTZ | NO | 创建时间 |
|
||||||
|
| 35 | updated_at | TIMESTAMPTZ | NO | 更新时间 |
|
||||||
|
|
||||||
|
## 工资计算公式
|
||||||
|
|
||||||
|
### 课时收入
|
||||||
|
```
|
||||||
|
基础课收入 = base_hours × (base_course_price - base_deduction)
|
||||||
|
附加课收入 = bonus_hours × 190 × (1 - bonus_deduction_ratio)
|
||||||
|
课时收入合计 = 基础课收入 + 附加课收入
|
||||||
|
```
|
||||||
|
|
||||||
|
### 奖金
|
||||||
|
```
|
||||||
|
冲刺奖金: H>=190得300元, H>=220得800元(不累计取最高)
|
||||||
|
Top3奖金: 1st=1000元, 2nd=600元, 3rd=400元
|
||||||
|
充值提成: 来自dws_assistant_recharge_commission
|
||||||
|
```
|
||||||
|
|
||||||
|
### 应发工资
|
||||||
|
```
|
||||||
|
gross_salary = total_course_income + total_bonus
|
||||||
|
```
|
||||||
|
|
||||||
|
## 计算示例
|
||||||
|
|
||||||
|
| 项目 | 数值 | 计算过程 |
|
||||||
|
|------|------|----------|
|
||||||
|
| 基础课小时数 | 170 | 来自monthly_summary |
|
||||||
|
| 附加课小时数 | 15 | 来自monthly_summary |
|
||||||
|
| 等级 | 中级(20) | base_course_price=108 |
|
||||||
|
| 档位 | T3 | base_deduction=13, bonus_ratio=0.35 |
|
||||||
|
| 基础课收入 | 16,150 | 170 × (108-13) |
|
||||||
|
| 附加课收入 | 1,852.5 | 15 × 190 × 0.65 |
|
||||||
|
| 冲刺奖金 | 300 | 185>=190 |
|
||||||
|
| 应发工资 | 18,302.5 | 16,150 + 1,852.5 + 300 |
|
||||||
|
|
||||||
|
## 可回溯性
|
||||||
|
|
||||||
|
| 项目 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| 可回溯 | ⚠️ 部分可回溯 |
|
||||||
|
| 数据范围 | 2025年8月起 |
|
||||||
|
| 依赖表 | dws_assistant_monthly_summary, cfg_*, dws_assistant_recharge_commission |
|
||||||
|
| 限制 | 充值提成需手工导入历史数据 |
|
||||||
@@ -0,0 +1,125 @@
|
|||||||
|
# dws_finance_daily_summary 财务日度汇总表
|
||||||
|
|
||||||
|
> 生成时间:2026-02-03
|
||||||
|
|
||||||
|
## 表信息
|
||||||
|
|
||||||
|
| 属性 | 值 |
|
||||||
|
|------|-----|
|
||||||
|
| Schema | billiards_dws |
|
||||||
|
| 表名 | dws_finance_daily_summary |
|
||||||
|
| 主键 | id |
|
||||||
|
| 唯一键 | (site_id, stat_date) |
|
||||||
|
| 数据来源 | dwd_settlement_head + 多个DWD事实表 |
|
||||||
|
| 更新频率 | 每小时更新当日数据 |
|
||||||
|
| 说明 | 以"日期"为粒度,汇总当日财务数据 |
|
||||||
|
|
||||||
|
## 字段说明
|
||||||
|
|
||||||
|
| 序号 | 字段名 | 类型 | 可空 | 说明 |
|
||||||
|
|------|--------|------|------|------|
|
||||||
|
| 1 | id | BIGSERIAL | NO | 自增主键 |
|
||||||
|
| 2 | site_id | BIGINT | NO | 门店ID |
|
||||||
|
| 3 | tenant_id | BIGINT | NO | 租户ID |
|
||||||
|
| 4 | stat_date | DATE | NO | 统计日期 |
|
||||||
|
| 5 | gross_amount | NUMERIC(14,2) | NO | 发生额合计 |
|
||||||
|
| 6 | table_fee_amount | NUMERIC(14,2) | NO | 台费正价 |
|
||||||
|
| 7 | goods_amount | NUMERIC(14,2) | NO | 商品正价 |
|
||||||
|
| 8 | assistant_pd_amount | NUMERIC(14,2) | NO | 助教基础课正价(陪打) |
|
||||||
|
| 9 | assistant_cx_amount | NUMERIC(14,2) | NO | 助教激励课正价(超休) |
|
||||||
|
| 10 | discount_total | NUMERIC(14,2) | NO | 优惠合计 |
|
||||||
|
| 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 | 手动调整 |
|
||||||
|
| 15 | discount_rounding | NUMERIC(14,2) | NO | 抹零 |
|
||||||
|
| 16 | discount_other | NUMERIC(14,2) | NO | 其他优惠 |
|
||||||
|
| 17 | confirmed_income | NUMERIC(14,2) | NO | 确认收入 = 发生额 - 优惠 |
|
||||||
|
| 18 | cash_inflow_total | NUMERIC(14,2) | NO | 现金流入合计 |
|
||||||
|
| 19 | cash_pay_amount | NUMERIC(14,2) | NO | 收银实付 |
|
||||||
|
| 20 | groupbuy_pay_amount | NUMERIC(14,2) | NO | 团购支付金额 |
|
||||||
|
| 21 | platform_settlement_amount | NUMERIC(14,2) | NO | 平台回款金额(导入) |
|
||||||
|
| 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 | 储值卡消费 |
|
||||||
|
| 26 | gift_card_consume | NUMERIC(14,2) | NO | 赠送卡消费 |
|
||||||
|
| 27 | cash_outflow_total | NUMERIC(14,2) | NO | 现金流出合计 |
|
||||||
|
| 28 | cash_balance_change | NUMERIC(14,2) | NO | 现金余额变动 |
|
||||||
|
| 29 | recharge_count | INTEGER | NO | 充值笔数 |
|
||||||
|
| 30 | recharge_total | NUMERIC(14,2) | NO | 充值总额(含赠送) |
|
||||||
|
| 31 | recharge_cash | NUMERIC(14,2) | NO | 充值现金部分 |
|
||||||
|
| 32 | recharge_gift | NUMERIC(14,2) | NO | 充值赠送部分 |
|
||||||
|
| 33 | first_recharge_count | INTEGER | NO | 首充笔数 |
|
||||||
|
| 34 | first_recharge_amount | NUMERIC(14,2) | NO | 首充金额 |
|
||||||
|
| 35 | renewal_count | INTEGER | NO | 续充笔数 |
|
||||||
|
| 36 | renewal_amount | NUMERIC(14,2) | NO | 续充金额 |
|
||||||
|
| 37 | order_count | INTEGER | NO | 结账单数 |
|
||||||
|
| 38 | member_order_count | INTEGER | NO | 会员订单数 |
|
||||||
|
| 39 | guest_order_count | INTEGER | NO | 散客订单数 |
|
||||||
|
| 40 | avg_order_amount | NUMERIC(12,2) | NO | 平均客单价 |
|
||||||
|
| 41 | created_at | TIMESTAMPTZ | NO | 创建时间 |
|
||||||
|
| 42 | updated_at | TIMESTAMPTZ | NO | 更新时间 |
|
||||||
|
|
||||||
|
## 数据来源
|
||||||
|
|
||||||
|
### 结账汇总:dwd_settlement_head
|
||||||
|
```sql
|
||||||
|
SELECT
|
||||||
|
DATE(pay_time) AS stat_date,
|
||||||
|
SUM(table_charge_money) AS table_fee_amount,
|
||||||
|
SUM(goods_money) AS goods_amount,
|
||||||
|
SUM(assistant_pd_money) AS assistant_pd_amount,
|
||||||
|
SUM(assistant_cx_money) AS assistant_cx_amount,
|
||||||
|
SUM(member_discount_amount) AS discount_vip,
|
||||||
|
SUM(adjust_amount) AS discount_manual,
|
||||||
|
SUM(rounding_amount) AS discount_rounding,
|
||||||
|
SUM(pay_amount) AS cash_pay_amount,
|
||||||
|
SUM(balance_amount) AS cash_card_consume,
|
||||||
|
SUM(gift_card_amount) AS gift_card_consume,
|
||||||
|
COUNT(*) AS order_count
|
||||||
|
FROM billiards_dwd.dwd_settlement_head
|
||||||
|
WHERE settle_type = 1
|
||||||
|
GROUP BY DATE(pay_time);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 团购核销:dwd_groupbuy_redemption
|
||||||
|
```sql
|
||||||
|
SELECT
|
||||||
|
DATE(create_time) AS stat_date,
|
||||||
|
SUM(coupon_money) AS groupbuy_pay_amount
|
||||||
|
FROM billiards_dwd.dwd_groupbuy_redemption
|
||||||
|
WHERE is_delete = 0
|
||||||
|
GROUP BY DATE(create_time);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 充值订单:dwd_recharge_order
|
||||||
|
```sql
|
||||||
|
SELECT
|
||||||
|
DATE(pay_time) AS stat_date,
|
||||||
|
COUNT(*) AS recharge_count,
|
||||||
|
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
|
||||||
|
FROM billiards_dwd.dwd_recharge_order
|
||||||
|
GROUP BY DATE(pay_time);
|
||||||
|
```
|
||||||
|
|
||||||
|
## 使用说明
|
||||||
|
|
||||||
|
**计算公式**
|
||||||
|
```
|
||||||
|
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
|
||||||
|
```
|
||||||
|
|
||||||
|
## 可回溯性
|
||||||
|
|
||||||
|
| 项目 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| 可回溯 | ✅ 完全可回溯 |
|
||||||
|
| 数据范围 | 2025-07-16 ~ 至今 |
|
||||||
|
| 依赖表 | dwd_settlement_head, dwd_groupbuy_redemption, dwd_recharge_order, dws_platform_settlement |
|
||||||
|
| 注意 | platform_settlement需Excel导入 |
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
# dws_finance_discount_detail 优惠明细表
|
||||||
|
|
||||||
|
> 生成时间:2026-02-03
|
||||||
|
|
||||||
|
## 表信息
|
||||||
|
|
||||||
|
| 属性 | 值 |
|
||||||
|
|------|-----|
|
||||||
|
| Schema | billiards_dws |
|
||||||
|
| 表名 | dws_finance_discount_detail |
|
||||||
|
| 主键 | id |
|
||||||
|
| 唯一键 | (site_id, stat_date, discount_type_code) |
|
||||||
|
| 数据来源 | dwd_settlement_head + dwd_groupbuy_redemption |
|
||||||
|
| 更新频率 | 每日更新 |
|
||||||
|
| 说明 | 以"日期+优惠类型"为粒度,分析优惠构成 |
|
||||||
|
|
||||||
|
## 字段说明
|
||||||
|
|
||||||
|
| 序号 | 字段名 | 类型 | 可空 | 说明 |
|
||||||
|
|------|--------|------|------|------|
|
||||||
|
| 1 | id | BIGSERIAL | NO | 自增主键 |
|
||||||
|
| 2 | site_id | BIGINT | NO | 门店ID |
|
||||||
|
| 3 | tenant_id | BIGINT | NO | 租户ID |
|
||||||
|
| 4 | stat_date | DATE | NO | 统计日期 |
|
||||||
|
| 5 | discount_type_code | VARCHAR(30) | NO | 优惠类型代码 |
|
||||||
|
| 6 | discount_type_name | VARCHAR(50) | NO | 优惠类型名称 |
|
||||||
|
| 7 | discount_amount | NUMERIC(14,2) | NO | 优惠金额 |
|
||||||
|
| 8 | discount_ratio | NUMERIC(5,4) | NO | 优惠占比(占总优惠) |
|
||||||
|
| 9 | usage_count | INTEGER | NO | 使用次数 |
|
||||||
|
| 10 | affected_orders | INTEGER | NO | 影响订单数 |
|
||||||
|
| 11 | created_at | TIMESTAMPTZ | NO | 创建时间 |
|
||||||
|
| 12 | updated_at | TIMESTAMPTZ | NO | 更新时间 |
|
||||||
|
|
||||||
|
## 优惠类型说明
|
||||||
|
|
||||||
|
| discount_type_code | discount_type_name | 数据来源 |
|
||||||
|
|--------------------|--------------------|----------|
|
||||||
|
| GROUPBUY | 团购优惠 | dwd_settlement_head.coupon_amount |
|
||||||
|
| VIP | 会员折扣 | dwd_settlement_head.member_discount_amount |
|
||||||
|
| GIFT_CARD | 赠送卡抵扣 | dwd_settlement_head.gift_card_amount |
|
||||||
|
| MANUAL | 手动调整 | dwd_settlement_head.adjust_amount |
|
||||||
|
| ROUNDING | 抹零 | dwd_settlement_head.rounding_amount |
|
||||||
|
| BIG_CUSTOMER | 大客户优惠 | dwd_settlement_head(特定会员优惠) |
|
||||||
|
| OTHER | 其他优惠 | 其他无法归类的优惠 |
|
||||||
|
|
||||||
|
## 数据来源
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- 从结账头表提取各类优惠
|
||||||
|
SELECT
|
||||||
|
DATE(pay_time) AS stat_date,
|
||||||
|
'VIP' AS discount_type_code,
|
||||||
|
'会员折扣' AS discount_type_name,
|
||||||
|
SUM(member_discount_amount) AS discount_amount,
|
||||||
|
COUNT(CASE WHEN member_discount_amount > 0 THEN 1 END) AS usage_count,
|
||||||
|
COUNT(DISTINCT CASE WHEN member_discount_amount > 0 THEN order_settle_id END) AS affected_orders
|
||||||
|
FROM billiards_dwd.dwd_settlement_head
|
||||||
|
WHERE settle_type = 1
|
||||||
|
GROUP BY DATE(pay_time)
|
||||||
|
|
||||||
|
UNION ALL
|
||||||
|
|
||||||
|
SELECT
|
||||||
|
DATE(pay_time) AS stat_date,
|
||||||
|
'GROUPBUY' AS discount_type_code,
|
||||||
|
'团购优惠' AS discount_type_name,
|
||||||
|
SUM(coupon_amount) AS discount_amount,
|
||||||
|
COUNT(CASE WHEN coupon_amount > 0 THEN 1 END) AS usage_count,
|
||||||
|
COUNT(DISTINCT CASE WHEN coupon_amount > 0 THEN order_settle_id END) AS affected_orders
|
||||||
|
FROM billiards_dwd.dwd_settlement_head
|
||||||
|
WHERE settle_type = 1
|
||||||
|
GROUP BY DATE(pay_time)
|
||||||
|
|
||||||
|
-- ... 其他优惠类型
|
||||||
|
```
|
||||||
|
|
||||||
|
## 使用说明
|
||||||
|
|
||||||
|
**占比计算**
|
||||||
|
```sql
|
||||||
|
discount_ratio = discount_amount / SUM(discount_amount) OVER (PARTITION BY stat_date)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 可回溯性
|
||||||
|
|
||||||
|
| 项目 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| 可回溯 | ✅ 完全可回溯 |
|
||||||
|
| 数据范围 | 2025-07-16 ~ 至今 |
|
||||||
|
| 依赖表 | dwd_settlement_head, dwd_groupbuy_redemption |
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
# dws_finance_expense_summary 支出结构表
|
||||||
|
|
||||||
|
> 生成时间:2026-02-03
|
||||||
|
|
||||||
|
## 表信息
|
||||||
|
|
||||||
|
| 属性 | 值 |
|
||||||
|
|------|-----|
|
||||||
|
| Schema | billiards_dws |
|
||||||
|
| 表名 | dws_finance_expense_summary |
|
||||||
|
| 主键 | id |
|
||||||
|
| 唯一键 | (site_id, expense_month, expense_type_code, import_batch_no) |
|
||||||
|
| 数据来源 | Excel手动导入 |
|
||||||
|
| 更新频率 | 按需导入 |
|
||||||
|
| 说明 | 以"月份+支出类型"为粒度,记录支出数据 |
|
||||||
|
|
||||||
|
## 字段说明
|
||||||
|
|
||||||
|
| 序号 | 字段名 | 类型 | 可空 | 说明 |
|
||||||
|
|------|--------|------|------|------|
|
||||||
|
| 1 | id | BIGSERIAL | NO | 自增主键 |
|
||||||
|
| 2 | site_id | BIGINT | NO | 门店ID |
|
||||||
|
| 3 | tenant_id | BIGINT | NO | 租户ID |
|
||||||
|
| 4 | expense_month | DATE | NO | 支出月份(月第一天) |
|
||||||
|
| 5 | expense_type_code | VARCHAR(30) | NO | 支出类型代码 |
|
||||||
|
| 6 | expense_type_name | VARCHAR(50) | NO | 支出类型名称 |
|
||||||
|
| 7 | expense_category | VARCHAR(20) | YES | 支出大类 |
|
||||||
|
| 8 | expense_amount | NUMERIC(14,2) | NO | 支出金额 |
|
||||||
|
| 9 | expense_detail | TEXT | YES | 支出明细说明 |
|
||||||
|
| 10 | import_batch_no | VARCHAR(50) | YES | 导入批次号 |
|
||||||
|
| 11 | import_file_name | VARCHAR(200) | YES | 导入文件名 |
|
||||||
|
| 12 | import_time | TIMESTAMPTZ | YES | 导入时间 |
|
||||||
|
| 13 | import_user | VARCHAR(50) | YES | 导入操作人 |
|
||||||
|
| 14 | remark | TEXT | YES | 备注 |
|
||||||
|
| 15 | created_at | TIMESTAMPTZ | NO | 创建时间 |
|
||||||
|
| 16 | updated_at | TIMESTAMPTZ | NO | 更新时间 |
|
||||||
|
|
||||||
|
## 支出类型说明
|
||||||
|
|
||||||
|
| expense_type_code | expense_type_name | expense_category |
|
||||||
|
|-------------------|-------------------|------------------|
|
||||||
|
| RENT | 房租 | FIXED_COST |
|
||||||
|
| UTILITY | 水电费 | FIXED_COST |
|
||||||
|
| PROPERTY | 物业费 | FIXED_COST |
|
||||||
|
| SALARY | 工资 | VARIABLE_COST |
|
||||||
|
| REIMBURSE | 报销 | VARIABLE_COST |
|
||||||
|
| PLATFORM_FEE | 平台费用 | VARIABLE_COST |
|
||||||
|
| MAINTENANCE | 维修保养 | VARIABLE_COST |
|
||||||
|
| CONSUMABLES | 耗材 | VARIABLE_COST |
|
||||||
|
| MARKETING | 营销费用 | VARIABLE_COST |
|
||||||
|
| OTHER | 其他 | OTHER |
|
||||||
|
|
||||||
|
## Excel导入模板
|
||||||
|
|
||||||
|
| 月份 | 支出类型 | 支出金额 | 明细说明 | 备注 |
|
||||||
|
|------|----------|----------|----------|------|
|
||||||
|
| 2026-01 | 房租 | 50000.00 | 1月房租 | |
|
||||||
|
| 2026-01 | 水电费 | 8000.00 | 1月水电 | |
|
||||||
|
| 2026-01 | 工资 | 120000.00 | 员工工资 | |
|
||||||
|
|
||||||
|
### 导入规则
|
||||||
|
- **月份**: 必填,格式 2026-01 或 2026/01/01
|
||||||
|
- **支出类型**: 必填,需匹配支出类型名称
|
||||||
|
- **支出金额**: 必填,单位:元
|
||||||
|
- **明细说明**: 选填
|
||||||
|
- **备注**: 选填
|
||||||
|
|
||||||
|
## 使用说明
|
||||||
|
|
||||||
|
**月度支出汇总**
|
||||||
|
```sql
|
||||||
|
SELECT
|
||||||
|
expense_month,
|
||||||
|
expense_category,
|
||||||
|
SUM(expense_amount) AS total_expense
|
||||||
|
FROM billiards_dws.dws_finance_expense_summary
|
||||||
|
GROUP BY expense_month, expense_category
|
||||||
|
ORDER BY expense_month, expense_category;
|
||||||
|
```
|
||||||
|
|
||||||
|
## 可回溯性
|
||||||
|
|
||||||
|
| 项目 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| 可回溯 | ❌ 不可自动回溯 |
|
||||||
|
| 原因 | 数据来源为Excel手工导入,DWD层无此数据 |
|
||||||
|
| 处理 | 需要人工补录历史数据 |
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
# dws_finance_income_structure 收入结构分析表
|
||||||
|
|
||||||
|
> 生成时间:2026-02-03
|
||||||
|
|
||||||
|
## 表信息
|
||||||
|
|
||||||
|
| 属性 | 值 |
|
||||||
|
|------|-----|
|
||||||
|
| Schema | billiards_dws |
|
||||||
|
| 表名 | dws_finance_income_structure |
|
||||||
|
| 主键 | id |
|
||||||
|
| 唯一键 | (site_id, stat_date, structure_type, category_code) |
|
||||||
|
| 数据来源 | dwd_table_fee_log + dwd_assistant_service_log + cfg_area_category |
|
||||||
|
| 更新频率 | 每日更新 |
|
||||||
|
| 说明 | 以"日期+区域/类型"为粒度,分析收入结构 |
|
||||||
|
|
||||||
|
## 字段说明
|
||||||
|
|
||||||
|
| 序号 | 字段名 | 类型 | 可空 | 说明 |
|
||||||
|
|------|--------|------|------|------|
|
||||||
|
| 1 | id | BIGSERIAL | NO | 自增主键 |
|
||||||
|
| 2 | site_id | BIGINT | NO | 门店ID |
|
||||||
|
| 3 | tenant_id | BIGINT | NO | 租户ID |
|
||||||
|
| 4 | stat_date | DATE | NO | 统计日期 |
|
||||||
|
| 5 | structure_type | VARCHAR(20) | NO | 结构类型。**枚举值**: AREA(区域), INCOME_TYPE(收入类型) |
|
||||||
|
| 6 | category_code | VARCHAR(30) | NO | 分类代码 |
|
||||||
|
| 7 | category_name | VARCHAR(50) | NO | 分类名称 |
|
||||||
|
| 8 | income_amount | NUMERIC(14,2) | NO | 收入金额 |
|
||||||
|
| 9 | income_ratio | NUMERIC(5,4) | NO | 收入占比 |
|
||||||
|
| 10 | order_count | INTEGER | NO | 订单数 |
|
||||||
|
| 11 | duration_minutes | INTEGER | NO | 时长(分钟) |
|
||||||
|
| 12 | created_at | TIMESTAMPTZ | NO | 创建时间 |
|
||||||
|
| 13 | updated_at | TIMESTAMPTZ | NO | 更新时间 |
|
||||||
|
|
||||||
|
## 分类代码说明
|
||||||
|
|
||||||
|
### 按区域分析 (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 | 其他 | 未映射区域 |
|
||||||
|
|
||||||
|
### 按收入类型分析 (structure_type = 'INCOME_TYPE')
|
||||||
|
| category_code | category_name |
|
||||||
|
|---------------|---------------|
|
||||||
|
| TABLE_FEE | 台费收入 |
|
||||||
|
| GOODS | 商品收入 |
|
||||||
|
| ASSISTANT_BASE | 助教基础课收入 |
|
||||||
|
| ASSISTANT_BONUS | 助教附加课收入 |
|
||||||
|
|
||||||
|
## 数据来源
|
||||||
|
|
||||||
|
### 按区域汇总台费
|
||||||
|
```sql
|
||||||
|
SELECT
|
||||||
|
DATE(tfl.ledger_end_time) AS stat_date,
|
||||||
|
COALESCE(ac.category_code, 'OTHER') AS category_code,
|
||||||
|
COALESCE(ac.category_name, '其他') AS category_name,
|
||||||
|
SUM(tfl.ledger_amount) AS income_amount,
|
||||||
|
SUM(tfl.ledger_count) AS duration_seconds,
|
||||||
|
COUNT(DISTINCT tfl.order_settle_id) AS order_count
|
||||||
|
FROM billiards_dwd.dwd_table_fee_log tfl
|
||||||
|
LEFT JOIN billiards_dwd.dim_table dt ON dt.table_id = tfl.site_table_id
|
||||||
|
LEFT JOIN billiards_dws.cfg_area_category ac ON dt.site_table_area_name = ac.source_area_name
|
||||||
|
WHERE tfl.is_delete = 0
|
||||||
|
GROUP BY DATE(tfl.ledger_end_time), COALESCE(ac.category_code, 'OTHER'), COALESCE(ac.category_name, '其他');
|
||||||
|
```
|
||||||
|
|
||||||
|
## 使用说明
|
||||||
|
|
||||||
|
**占比计算**
|
||||||
|
```sql
|
||||||
|
-- income_ratio = 当前分类收入 / 当日总收入
|
||||||
|
income_ratio = income_amount / SUM(income_amount) OVER (PARTITION BY stat_date, structure_type)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 可回溯性
|
||||||
|
|
||||||
|
| 项目 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| 可回溯 | ✅ 完全可回溯 |
|
||||||
|
| 数据范围 | 2025-07-21 ~ 至今 |
|
||||||
|
| 依赖表 | dwd_table_fee_log, dwd_assistant_service_log, dim_table, cfg_area_category |
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
# dws_finance_recharge_summary 充值统计表
|
||||||
|
|
||||||
|
> 生成时间:2026-02-03
|
||||||
|
|
||||||
|
## 表信息
|
||||||
|
|
||||||
|
| 属性 | 值 |
|
||||||
|
|------|-----|
|
||||||
|
| Schema | billiards_dws |
|
||||||
|
| 表名 | dws_finance_recharge_summary |
|
||||||
|
| 主键 | id |
|
||||||
|
| 唯一键 | (site_id, stat_date) |
|
||||||
|
| 数据来源 | dwd_recharge_order |
|
||||||
|
| 更新频率 | 每日更新 |
|
||||||
|
| 说明 | 以"日期"为粒度,统计充值数据,区分首充/续充 |
|
||||||
|
|
||||||
|
## 字段说明
|
||||||
|
|
||||||
|
| 序号 | 字段名 | 类型 | 可空 | 说明 |
|
||||||
|
|------|--------|------|------|------|
|
||||||
|
| 1 | id | BIGSERIAL | NO | 自增主键 |
|
||||||
|
| 2 | site_id | BIGINT | NO | 门店ID |
|
||||||
|
| 3 | tenant_id | BIGINT | NO | 租户ID |
|
||||||
|
| 4 | stat_date | DATE | NO | 统计日期 |
|
||||||
|
| 5 | recharge_count | INTEGER | NO | 充值笔数 |
|
||||||
|
| 6 | recharge_total | NUMERIC(14,2) | NO | 充值总额(含赠送) |
|
||||||
|
| 7 | recharge_cash | NUMERIC(14,2) | NO | 现金充值金额 |
|
||||||
|
| 8 | recharge_gift | NUMERIC(14,2) | NO | 赠送金额 |
|
||||||
|
| 9 | first_recharge_count | INTEGER | NO | 首充笔数 |
|
||||||
|
| 10 | first_recharge_cash | NUMERIC(14,2) | NO | 首充现金 |
|
||||||
|
| 11 | first_recharge_gift | NUMERIC(14,2) | NO | 首充赠送 |
|
||||||
|
| 12 | first_recharge_total | NUMERIC(14,2) | NO | 首充总额 |
|
||||||
|
| 13 | renewal_count | INTEGER | NO | 续充笔数 |
|
||||||
|
| 14 | renewal_cash | NUMERIC(14,2) | NO | 续充现金 |
|
||||||
|
| 15 | renewal_gift | NUMERIC(14,2) | NO | 续充赠送 |
|
||||||
|
| 16 | renewal_total | NUMERIC(14,2) | NO | 续充总额 |
|
||||||
|
| 17 | recharge_member_count | INTEGER | NO | 充值会员数(去重) |
|
||||||
|
| 18 | new_member_count | INTEGER | NO | 新增会员数 |
|
||||||
|
| 19 | total_card_balance | NUMERIC(14,2) | NO | 全部会员卡余额(当日末) |
|
||||||
|
| 20 | cash_card_balance | NUMERIC(14,2) | NO | 储值卡余额 |
|
||||||
|
| 21 | gift_card_balance | NUMERIC(14,2) | NO | 赠送卡余额 |
|
||||||
|
| 22 | created_at | TIMESTAMPTZ | NO | 创建时间 |
|
||||||
|
| 23 | updated_at | TIMESTAMPTZ | NO | 更新时间 |
|
||||||
|
|
||||||
|
## 数据来源
|
||||||
|
|
||||||
|
### 充值订单:dwd_recharge_order
|
||||||
|
```sql
|
||||||
|
SELECT
|
||||||
|
DATE(pay_time) AS stat_date,
|
||||||
|
COUNT(*) AS recharge_count,
|
||||||
|
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_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 = 0 THEN 1 ELSE 0 END) AS renewal_count,
|
||||||
|
SUM(CASE WHEN is_first = 0 THEN pay_amount ELSE 0 END) AS renewal_cash,
|
||||||
|
-- 会员数
|
||||||
|
COUNT(DISTINCT member_id) AS recharge_member_count
|
||||||
|
FROM billiards_dwd.dwd_recharge_order
|
||||||
|
GROUP BY DATE(pay_time);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 卡余额快照:dim_member_card_account
|
||||||
|
```sql
|
||||||
|
-- 截至stat_date当日末的卡余额
|
||||||
|
SELECT
|
||||||
|
SUM(balance) AS total_card_balance,
|
||||||
|
SUM(CASE WHEN card_type_id = 2793249295533893 THEN balance ELSE 0 END) AS cash_card_balance,
|
||||||
|
SUM(CASE WHEN card_type_id != 2793249295533893 THEN balance ELSE 0 END) AS gift_card_balance
|
||||||
|
FROM billiards_dwd.dim_member_card_account
|
||||||
|
WHERE scd2_start_time <= :stat_date + INTERVAL '1 day'
|
||||||
|
AND (scd2_end_time IS NULL OR scd2_end_time > :stat_date + INTERVAL '1 day');
|
||||||
|
```
|
||||||
|
|
||||||
|
## 使用说明
|
||||||
|
|
||||||
|
**首充判断**
|
||||||
|
- is_first = 1: 首充
|
||||||
|
- is_first = 0: 续充
|
||||||
|
|
||||||
|
**储值卡ID**
|
||||||
|
- 储值卡 card_type_id = 2793249295533893
|
||||||
|
|
||||||
|
## 可回溯性
|
||||||
|
|
||||||
|
| 项目 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| 可回溯 | ✅ 完全可回溯 |
|
||||||
|
| 数据范围 | 2025-07-21 ~ 至今 |
|
||||||
|
| 依赖表 | dwd_recharge_order, dim_member_card_account |
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
# dws_member_consumption_summary 会员消费汇总表
|
||||||
|
|
||||||
|
> 生成时间:2026-02-03
|
||||||
|
|
||||||
|
## 表信息
|
||||||
|
|
||||||
|
| 属性 | 值 |
|
||||||
|
|------|-----|
|
||||||
|
| Schema | billiards_dws |
|
||||||
|
| 表名 | dws_member_consumption_summary |
|
||||||
|
| 主键 | id |
|
||||||
|
| 唯一键 | (site_id, member_id, stat_date) |
|
||||||
|
| 数据来源 | dwd_settlement_head + 关联明细表 |
|
||||||
|
| 更新频率 | 每日更新 |
|
||||||
|
| 说明 | 以"会员"为粒度,统计消费行为和滚动窗口指标 |
|
||||||
|
|
||||||
|
## 字段说明
|
||||||
|
|
||||||
|
| 序号 | 字段名 | 类型 | 可空 | 说明 |
|
||||||
|
|------|--------|------|------|------|
|
||||||
|
| 1 | id | BIGSERIAL | NO | 自增主键 |
|
||||||
|
| 2 | site_id | BIGINT | NO | 门店ID |
|
||||||
|
| 3 | tenant_id | BIGINT | NO | 租户ID |
|
||||||
|
| 4 | member_id | BIGINT | NO | 会员ID(member_id=0散客不入此表) |
|
||||||
|
| 5 | stat_date | DATE | NO | 统计基准日期 |
|
||||||
|
| 6 | member_nickname | VARCHAR(100) | YES | 会员昵称 |
|
||||||
|
| 7 | member_mobile | VARCHAR(20) | YES | 手机号(脱敏) |
|
||||||
|
| 8 | card_grade_name | VARCHAR(50) | YES | 卡等级名称 |
|
||||||
|
| 9 | register_date | DATE | YES | 注册日期 |
|
||||||
|
| 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 | 累计助教服务消费 |
|
||||||
|
| 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 | 更新时间 |
|
||||||
|
|
||||||
|
## 数据来源
|
||||||
|
|
||||||
|
### 消费统计来源:dwd_settlement_head
|
||||||
|
```sql
|
||||||
|
SELECT
|
||||||
|
site_id,
|
||||||
|
member_id,
|
||||||
|
DATE(pay_time) AS consume_date,
|
||||||
|
COUNT(*) AS visit_count,
|
||||||
|
SUM(consume_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 billiards_dwd.dwd_settlement_head
|
||||||
|
WHERE member_id != 0 -- 排除散客
|
||||||
|
AND settle_type = 1 -- 已结账
|
||||||
|
GROUP BY site_id, member_id, DATE(pay_time);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 卡余额来源:dim_member_card_account
|
||||||
|
```sql
|
||||||
|
SELECT
|
||||||
|
tenant_member_id AS member_id,
|
||||||
|
SUM(CASE WHEN card_type_id = 2793249295533893 THEN balance ELSE 0 END) AS cash_card_balance,
|
||||||
|
SUM(CASE WHEN card_type_id != 2793249295533893 THEN balance ELSE 0 END) AS gift_card_balance
|
||||||
|
FROM billiards_dwd.dim_member_card_account
|
||||||
|
WHERE scd2_is_current = 1
|
||||||
|
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
|
||||||
|
```
|
||||||
|
|
||||||
|
## 可回溯性
|
||||||
|
|
||||||
|
| 项目 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| 可回溯 | ✅ 完全可回溯 |
|
||||||
|
| 数据范围 | 2025-07-16 ~ 至今 |
|
||||||
|
| 依赖表 | dwd_settlement_head, dim_member, dim_member_card_account |
|
||||||
@@ -0,0 +1,119 @@
|
|||||||
|
# dws_member_visit_detail 会员来店明细表
|
||||||
|
|
||||||
|
> 生成时间:2026-02-03
|
||||||
|
|
||||||
|
## 表信息
|
||||||
|
|
||||||
|
| 属性 | 值 |
|
||||||
|
|------|-----|
|
||||||
|
| Schema | billiards_dws |
|
||||||
|
| 表名 | dws_member_visit_detail |
|
||||||
|
| 主键 | id |
|
||||||
|
| 唯一键 | (site_id, member_id, order_settle_id) |
|
||||||
|
| 数据来源 | dwd_settlement_head + 关联明细表 |
|
||||||
|
| 更新频率 | 每日增量更新 |
|
||||||
|
| 说明 | 以"会员+订单"为粒度,记录每次来店消费明细 |
|
||||||
|
|
||||||
|
## 字段说明
|
||||||
|
|
||||||
|
| 序号 | 字段名 | 类型 | 可空 | 说明 |
|
||||||
|
|------|--------|------|------|------|
|
||||||
|
| 1 | id | BIGSERIAL | NO | 自增主键 |
|
||||||
|
| 2 | site_id | BIGINT | NO | 门店ID |
|
||||||
|
| 3 | tenant_id | BIGINT | NO | 租户ID |
|
||||||
|
| 4 | member_id | BIGINT | NO | 会员ID(散客不入此表) |
|
||||||
|
| 5 | order_settle_id | BIGINT | NO | 结账单ID |
|
||||||
|
| 6 | visit_date | DATE | NO | 来店日期 |
|
||||||
|
| 7 | visit_time | TIMESTAMPTZ | YES | 来店时间 |
|
||||||
|
| 8 | member_nickname | VARCHAR(100) | YES | 会员昵称 |
|
||||||
|
| 9 | member_mobile | VARCHAR(20) | YES | 手机号 |
|
||||||
|
| 10 | member_birthday | DATE | YES | 会员生日 |
|
||||||
|
| 11 | table_id | BIGINT | YES | 台桌ID |
|
||||||
|
| 12 | table_name | VARCHAR(50) | YES | 台桌名称 |
|
||||||
|
| 13 | area_name | VARCHAR(50) | YES | 区域名称(原始) |
|
||||||
|
| 14 | area_category | VARCHAR(20) | YES | 区域分类 |
|
||||||
|
| 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 | 消费总额(正价) |
|
||||||
|
| 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 | 储值卡支付 |
|
||||||
|
| 23 | gift_card_pay | NUMERIC(12,2) | NO | 赠送卡支付 |
|
||||||
|
| 24 | groupbuy_pay | NUMERIC(12,2) | NO | 团购券支付 |
|
||||||
|
| 25 | table_duration_min | INTEGER | NO | 台桌使用时长(分钟) |
|
||||||
|
| 26 | assistant_duration_min | INTEGER | NO | 助教服务时长(分钟) |
|
||||||
|
| 27 | assistant_services | JSONB | YES | 助教服务列表 |
|
||||||
|
| 28 | created_at | TIMESTAMPTZ | NO | 创建时间 |
|
||||||
|
| 29 | updated_at | TIMESTAMPTZ | NO | 更新时间 |
|
||||||
|
|
||||||
|
## 数据来源
|
||||||
|
|
||||||
|
### 主表来源:dwd_settlement_head
|
||||||
|
```sql
|
||||||
|
SELECT
|
||||||
|
site_id,
|
||||||
|
tenant_id,
|
||||||
|
member_id,
|
||||||
|
order_settle_id,
|
||||||
|
DATE(pay_time) AS visit_date,
|
||||||
|
pay_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 + coupon_amount + adjust_amount AS total_discount,
|
||||||
|
pay_amount AS actual_pay,
|
||||||
|
balance_amount AS cash_card_pay,
|
||||||
|
gift_card_amount AS gift_card_pay
|
||||||
|
FROM billiards_dwd.dwd_settlement_head
|
||||||
|
WHERE member_id != 0
|
||||||
|
AND settle_type = 1;
|
||||||
|
```
|
||||||
|
|
||||||
|
### 助教服务明细:dwd_assistant_service_log
|
||||||
|
```sql
|
||||||
|
-- 聚合为JSONB格式
|
||||||
|
SELECT
|
||||||
|
order_settle_id,
|
||||||
|
jsonb_agg(jsonb_build_object(
|
||||||
|
'assistant_id', site_assistant_id,
|
||||||
|
'nickname', nickname,
|
||||||
|
'duration_min', income_seconds / 60,
|
||||||
|
'amount', ledger_amount
|
||||||
|
)) AS assistant_services
|
||||||
|
FROM billiards_dwd.dwd_assistant_service_log
|
||||||
|
GROUP BY order_settle_id;
|
||||||
|
```
|
||||||
|
|
||||||
|
## 使用说明
|
||||||
|
|
||||||
|
**assistant_services JSON格式**
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{"assistant_id": 123, "nickname": "小燕", "duration_min": 60, "amount": 108.00},
|
||||||
|
{"assistant_id": 456, "nickname": "小明", "duration_min": 30, "amount": 54.00}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
**区域分类映射**
|
||||||
|
```sql
|
||||||
|
-- 通过cfg_area_category映射
|
||||||
|
area_category = COALESCE(
|
||||||
|
(SELECT category_name FROM billiards_dws.cfg_area_category
|
||||||
|
WHERE source_area_name = dim_table.site_table_area_name),
|
||||||
|
'其他'
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 可回溯性
|
||||||
|
|
||||||
|
| 项目 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| 可回溯 | ✅ 完全可回溯 |
|
||||||
|
| 数据范围 | 2025-07-16 ~ 至今 |
|
||||||
|
| 依赖表 | dwd_settlement_head, dwd_assistant_service_log, dim_table, dim_member |
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
# dws_platform_settlement 平台回款/服务费表
|
||||||
|
|
||||||
|
> 生成时间:2026-02-03
|
||||||
|
|
||||||
|
## 表信息
|
||||||
|
|
||||||
|
| 属性 | 值 |
|
||||||
|
|------|-----|
|
||||||
|
| Schema | billiards_dws |
|
||||||
|
| 表名 | dws_platform_settlement |
|
||||||
|
| 主键 | id |
|
||||||
|
| 数据来源 | Excel手动导入 |
|
||||||
|
| 更新频率 | 按需导入 |
|
||||||
|
| 说明 | 以"回款日期+平台+订单"为粒度,记录平台结算数据 |
|
||||||
|
|
||||||
|
## 字段说明
|
||||||
|
|
||||||
|
| 序号 | 字段名 | 类型 | 可空 | 说明 |
|
||||||
|
|------|--------|------|------|------|
|
||||||
|
| 1 | id | BIGSERIAL | NO | 自增主键 |
|
||||||
|
| 2 | site_id | BIGINT | NO | 门店ID |
|
||||||
|
| 3 | tenant_id | BIGINT | NO | 租户ID |
|
||||||
|
| 4 | settlement_date | DATE | NO | 回款日期 |
|
||||||
|
| 5 | platform_type | VARCHAR(30) | NO | 平台类型。**枚举值**: MEITUAN, DOUYIN, DIANPING, OTHER |
|
||||||
|
| 6 | platform_name | VARCHAR(50) | YES | 平台名称 |
|
||||||
|
| 7 | platform_order_no | VARCHAR(100) | YES | 平台订单号 |
|
||||||
|
| 8 | order_settle_id | BIGINT | YES | 关联的结账单ID |
|
||||||
|
| 9 | settlement_amount | NUMERIC(14,2) | NO | 回款金额(实际入账) |
|
||||||
|
| 10 | commission_amount | NUMERIC(14,2) | NO | 佣金(平台抽成) |
|
||||||
|
| 11 | service_fee | NUMERIC(14,2) | NO | 服务费 |
|
||||||
|
| 12 | gross_amount | NUMERIC(14,2) | NO | 订单原始金额 |
|
||||||
|
| 13 | import_batch_no | VARCHAR(50) | YES | 导入批次号 |
|
||||||
|
| 14 | import_file_name | VARCHAR(200) | YES | 导入文件名 |
|
||||||
|
| 15 | import_time | TIMESTAMPTZ | YES | 导入时间 |
|
||||||
|
| 16 | import_user | VARCHAR(50) | YES | 导入操作人 |
|
||||||
|
| 17 | remark | TEXT | YES | 备注 |
|
||||||
|
| 18 | created_at | TIMESTAMPTZ | NO | 创建时间 |
|
||||||
|
| 19 | updated_at | TIMESTAMPTZ | NO | 更新时间 |
|
||||||
|
|
||||||
|
## 平台类型说明
|
||||||
|
|
||||||
|
| platform_type | platform_name | 说明 |
|
||||||
|
|---------------|---------------|------|
|
||||||
|
| MEITUAN | 美团 | 美团团购/外卖 |
|
||||||
|
| DOUYIN | 抖音 | 抖音团购 |
|
||||||
|
| DIANPING | 大众点评 | 大众点评团购 |
|
||||||
|
| OTHER | 其他 | 其他平台 |
|
||||||
|
|
||||||
|
## Excel导入模板
|
||||||
|
|
||||||
|
| 回款日期 | 平台 | 平台订单号 | 订单金额 | 回款金额 | 佣金 | 服务费 | 备注 |
|
||||||
|
|----------|------|------------|----------|----------|------|--------|------|
|
||||||
|
| 2026-01-15 | 美团 | MT202601150001 | 200.00 | 186.00 | 12.00 | 2.00 | |
|
||||||
|
| 2026-01-15 | 抖音 | DY202601150001 | 150.00 | 142.50 | 6.00 | 1.50 | |
|
||||||
|
|
||||||
|
### 导入规则
|
||||||
|
- **回款日期**: 必填,实际到账日期
|
||||||
|
- **平台**: 必填,美团/抖音/大众点评/其他
|
||||||
|
- **平台订单号**: 选填,用于追溯
|
||||||
|
- **订单金额**: 必填,订单原始金额
|
||||||
|
- **回款金额**: 必填,实际到账金额
|
||||||
|
- **佣金**: 选填,平台抽成
|
||||||
|
- **服务费**: 选填
|
||||||
|
|
||||||
|
### 金额关系
|
||||||
|
```
|
||||||
|
settlement_amount = gross_amount - commission_amount - service_fee
|
||||||
|
```
|
||||||
|
|
||||||
|
## 使用说明
|
||||||
|
|
||||||
|
**日度平台回款汇总**
|
||||||
|
```sql
|
||||||
|
SELECT
|
||||||
|
settlement_date,
|
||||||
|
platform_type,
|
||||||
|
SUM(settlement_amount) AS total_settlement,
|
||||||
|
SUM(commission_amount) AS total_commission,
|
||||||
|
SUM(service_fee) AS total_service_fee
|
||||||
|
FROM billiards_dws.dws_platform_settlement
|
||||||
|
GROUP BY settlement_date, platform_type
|
||||||
|
ORDER BY settlement_date, platform_type;
|
||||||
|
```
|
||||||
|
|
||||||
|
**关联到财务日度汇总**
|
||||||
|
```sql
|
||||||
|
-- dws_finance_daily_summary.platform_settlement_amount
|
||||||
|
SELECT stat_date, SUM(settlement_amount)
|
||||||
|
FROM billiards_dws.dws_platform_settlement
|
||||||
|
WHERE settlement_date = :stat_date
|
||||||
|
GROUP BY stat_date;
|
||||||
|
```
|
||||||
|
|
||||||
|
## 可回溯性
|
||||||
|
|
||||||
|
| 项目 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| 可回溯 | ❌ 不可自动回溯 |
|
||||||
|
| 原因 | 数据来源为Excel手工导入,需从平台后台导出 |
|
||||||
|
| 处理 | 需要人工补录历史平台结算数据 |
|
||||||
585
etl_billiards/docs/dws_tables_dictionary.md
Normal file
585
etl_billiards/docs/dws_tables_dictionary.md
Normal file
@@ -0,0 +1,585 @@
|
|||||||
|
# DWS 数据字典
|
||||||
|
|
||||||
|
## 概述
|
||||||
|
|
||||||
|
DWS(Data Warehouse Service)层是数据仓库的汇总层,基于DWD明细层数据构建,为上层应用和报表提供预聚合的数据服务。
|
||||||
|
|
||||||
|
### 表清单
|
||||||
|
|
||||||
|
| 分类 | 表名 | 说明 | 更新频率 |
|
||||||
|
|------|------|------|----------|
|
||||||
|
| **配置表** | cfg_performance_tier | 绩效档位配置 | 手动维护 |
|
||||||
|
| | cfg_assistant_level_price | 助教等级定价 | 手动维护 |
|
||||||
|
| | cfg_bonus_rules | 奖金规则配置 | 手动维护 |
|
||||||
|
| | cfg_area_category | 台区分类映射 | 手动维护 |
|
||||||
|
| | cfg_skill_type | 技能课程类型映射 | 手动维护 |
|
||||||
|
| **助教维度** | dws_assistant_daily_detail | 助教日度业绩明细 | 每小时 |
|
||||||
|
| | dws_assistant_monthly_summary | 助教月度业绩汇总 | 每日 |
|
||||||
|
| | dws_assistant_customer_stats | 助教服务客户统计 | 每日 |
|
||||||
|
| | dws_assistant_salary_calc | 助教工资计算详情 | 月初 |
|
||||||
|
| | dws_assistant_recharge_commission | 助教充值提成 | Excel导入 |
|
||||||
|
| **客户维度** | dws_member_consumption_summary | 会员消费汇总 | 每日 |
|
||||||
|
| | dws_member_visit_detail | 会员来店明细 | 每日 |
|
||||||
|
| **财务维度** | dws_finance_daily_summary | 财务日度汇总 | 每小时 |
|
||||||
|
| | dws_finance_income_structure | 收入结构分析 | 每日 |
|
||||||
|
| | dws_finance_discount_detail | 优惠明细 | 每日 |
|
||||||
|
| | dws_finance_recharge_summary | 充值统计 | 每日 |
|
||||||
|
| | dws_finance_expense_summary | 支出结构 | Excel导入 |
|
||||||
|
| | dws_assistant_finance_analysis | 助教收支分析 | 每日 |
|
||||||
|
| | dws_platform_settlement | 平台回款/服务费 | Excel导入 |
|
||||||
|
| **订单汇总** | dws_order_summary | 订单汇总 | 每日 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 一、配置表
|
||||||
|
|
||||||
|
### 1.1 cfg_performance_tier - 绩效档位配置
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| tier_id | SERIAL | 档位ID(主键) |
|
||||||
|
| tier_code | VARCHAR(20) | 档位代码(T0-T5, NEW) |
|
||||||
|
| tier_name | VARCHAR(50) | 档位名称 |
|
||||||
|
| tier_level | INTEGER | 档位等级(-1=新入职, 0-5=正常档位) |
|
||||||
|
| min_hours | NUMERIC(10,2) | 最低业绩小时数阈值(>=) |
|
||||||
|
| max_hours | NUMERIC(10,2) | 最高业绩小时数阈值(<),NULL=无上限 |
|
||||||
|
| base_deduction | NUMERIC(10,2) | 专业课抽成(元/小时),球房从基础课扣除 |
|
||||||
|
| bonus_deduction_ratio | NUMERIC(5,4) | 打赏课抽成比例(0-1),球房从附加课扣除 |
|
||||||
|
| vacation_days | INTEGER | 次月可休假天数 |
|
||||||
|
| vacation_unlimited | BOOLEAN | 休假自由标记(5档为TRUE) |
|
||||||
|
| is_new_hire_tier | BOOLEAN | 是否为新入职专用档位 |
|
||||||
|
| effective_from | DATE | 生效起始日期 |
|
||||||
|
| effective_to | DATE | 生效截止日期 |
|
||||||
|
|
||||||
|
**档位配置(来自DWS数据库处理需求.md):**
|
||||||
|
|
||||||
|
| tier_code | tier_name | 业绩阈值 | 专业课抽成 | 打赏课抽成 | 休假 |
|
||||||
|
|-----------|-----------|----------|-----------|-----------|------|
|
||||||
|
| T0 | 0档-淘汰压力 | H < 100 | 28元/时 | 50% | 3天 |
|
||||||
|
| T1 | 1档-及格档 | 100 ≤ H < 130 | 18元/时 | 40% | 4天 |
|
||||||
|
| T2 | 2档-良好档 | 130 ≤ H < 160 | 15元/时 | 38% | 4天 |
|
||||||
|
| T3 | 3档-优秀档 | 160 ≤ H < 190 | 13元/时 | 35% | 5天 |
|
||||||
|
| T4 | 4档-卓越加速档 | 190 ≤ H < 220 | 10元/时 | 33% | 6天 |
|
||||||
|
| T5 | 5档-冠军加速档 | H ≥ 220 | 8元/时 | 30% | 休假自由 |
|
||||||
|
| NEW | 新入职档位 | - | 18元/时 | 40% | 4天 |
|
||||||
|
|
||||||
|
**业务规则:**
|
||||||
|
- 6档绩效(T0-T5),根据有效业绩小时数(基础课+附加课)匹配
|
||||||
|
- 新入职档位:月1日0点后入职者首月使用NEW档位,按1档抽成标准
|
||||||
|
- 支持按时间生效,历史月份使用历史规则
|
||||||
|
|
||||||
|
### 1.2 cfg_assistant_level_price - 助教等级定价
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| price_id | SERIAL | 定价ID(主键) |
|
||||||
|
| level_code | INTEGER | 等级代码(8/10/20/30/40) |
|
||||||
|
| level_name | VARCHAR(20) | 等级名称 |
|
||||||
|
| base_course_price | NUMERIC(10,2) | 基础课客户支付价格(元/小时) |
|
||||||
|
| bonus_course_price | NUMERIC(10,2) | 附加课客户支付价格(固定190元) |
|
||||||
|
| effective_from | DATE | 生效起始日期 |
|
||||||
|
| effective_to | DATE | 生效截止日期 |
|
||||||
|
|
||||||
|
**等级定价(客户支付价格):**
|
||||||
|
|
||||||
|
| level_code | level_name | 基础课价格 | 附加课价格 |
|
||||||
|
|------------|------------|-----------|-----------|
|
||||||
|
| 8 | 助教管理 | 98元/时 | 190元/时 |
|
||||||
|
| 10 | 初级 | 98元/时 | 190元/时 |
|
||||||
|
| 20 | 中级 | 108元/时 | 190元/时 |
|
||||||
|
| 30 | 高级 | 118元/时 | 190元/时 |
|
||||||
|
| 40 | 星级 | 138元/时 | 190元/时 |
|
||||||
|
|
||||||
|
**注意:** 此价格为客户支付价格,助教实际收入需减去档位抽成
|
||||||
|
|
||||||
|
### 1.3 cfg_bonus_rules - 奖金规则配置
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| rule_id | SERIAL | 规则ID(主键) |
|
||||||
|
| rule_type | VARCHAR(20) | 规则类型(SPRINT/TOP_RANK) |
|
||||||
|
| rule_code | VARCHAR(30) | 规则代码 |
|
||||||
|
| rule_name | VARCHAR(50) | 规则名称 |
|
||||||
|
| threshold_hours | NUMERIC(10,2) | 小时数阈值(冲刺奖金) |
|
||||||
|
| rank_position | INTEGER | 排名位置(Top奖金) |
|
||||||
|
| bonus_amount | NUMERIC(12,2) | 奖金金额(元) |
|
||||||
|
| is_cumulative | BOOLEAN | 是否可累计 |
|
||||||
|
| priority | INTEGER | 优先级 |
|
||||||
|
| effective_from | DATE | 生效起始日期 |
|
||||||
|
| effective_to | DATE | 生效截止日期 |
|
||||||
|
|
||||||
|
**业务规则:**
|
||||||
|
- 冲刺奖金:H>=190得300元,H>=220得800元,不累计取最高档
|
||||||
|
- Top3奖金:1st=1000元,2nd=600元,3rd=400元,并列都算
|
||||||
|
|
||||||
|
### 1.4 cfg_area_category - 台区分类映射
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| category_id | SERIAL | 分类ID(主键) |
|
||||||
|
| source_area_name | VARCHAR(100) | 源区域名称(来自dim_table.site_table_area_name) |
|
||||||
|
| category_code | VARCHAR(20) | 分类代码 |
|
||||||
|
| category_name | VARCHAR(50) | 分类名称 |
|
||||||
|
| match_type | VARCHAR(10) | 匹配类型(exact/like/default) |
|
||||||
|
| match_priority | INTEGER | 匹配优先级(数字越小优先级越高) |
|
||||||
|
| is_active | BOOLEAN | 是否启用 |
|
||||||
|
|
||||||
|
**分类代码(基于BD_manual_dim_table.md实际数据):**
|
||||||
|
|
||||||
|
| category_code | category_name | 匹配规则 |
|
||||||
|
|---------------|---------------|----------|
|
||||||
|
| BILLIARD | 普通台球区 | A区, B区, C区 |
|
||||||
|
| BILLIARD_VIP | VIP台球包厢 | VIP包厢 |
|
||||||
|
| SNOOKER | 斯诺克区 | 斯诺克区 |
|
||||||
|
| MAHJONG | 麻将房 | 麻将房 |
|
||||||
|
| KTV | KTV包间 | K包 |
|
||||||
|
| SPECIAL | 补时长专用 | 补时长 |
|
||||||
|
| OTHER | 其他区域 | 默认匹配 |
|
||||||
|
|
||||||
|
### 1.5 cfg_skill_type - 技能课程类型映射
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| skill_type_id | SERIAL | 映射ID(主键) |
|
||||||
|
| skill_id | BIGINT | 技能ID |
|
||||||
|
| skill_name | VARCHAR(50) | 技能名称 |
|
||||||
|
| course_type_code | VARCHAR(10) | 课程类型代码 |
|
||||||
|
| course_type_name | VARCHAR(20) | 课程类型名称 |
|
||||||
|
| is_active | BOOLEAN | 是否启用 |
|
||||||
|
|
||||||
|
**课程类型:**
|
||||||
|
- BASE = 基础课/陪打
|
||||||
|
- BONUS = 附加课/超休
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 二、助教维度表
|
||||||
|
|
||||||
|
### 2.1 dws_assistant_daily_detail - 助教日度业绩明细
|
||||||
|
|
||||||
|
**粒度:** 助教 + 日期
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| id | BIGSERIAL | 主键 |
|
||||||
|
| site_id | BIGINT | 门店ID |
|
||||||
|
| tenant_id | BIGINT | 租户ID |
|
||||||
|
| assistant_id | BIGINT | 助教ID |
|
||||||
|
| assistant_nickname | VARCHAR(50) | 助教花名 |
|
||||||
|
| stat_date | DATE | 统计日期 |
|
||||||
|
| assistant_level_code | INTEGER | 助教等级代码(SCD2 as-of) |
|
||||||
|
| assistant_level_name | VARCHAR(20) | 助教等级名称 |
|
||||||
|
| total_service_count | INTEGER | 总服务次数 |
|
||||||
|
| base_service_count | INTEGER | 基础课服务次数 |
|
||||||
|
| bonus_service_count | INTEGER | 附加课服务次数 |
|
||||||
|
| total_seconds | INTEGER | 总计费时长(秒) |
|
||||||
|
| base_seconds | INTEGER | 基础课计费时长 |
|
||||||
|
| bonus_seconds | INTEGER | 附加课计费时长 |
|
||||||
|
| total_hours | NUMERIC(10,2) | 总计费小时数 |
|
||||||
|
| base_hours | NUMERIC(10,2) | 基础课小时数 |
|
||||||
|
| bonus_hours | NUMERIC(10,2) | 附加课小时数 |
|
||||||
|
| total_ledger_amount | NUMERIC(12,2) | 总计费金额 |
|
||||||
|
| base_ledger_amount | NUMERIC(12,2) | 基础课计费金额 |
|
||||||
|
| bonus_ledger_amount | NUMERIC(12,2) | 附加课计费金额 |
|
||||||
|
| unique_customers | INTEGER | 服务客户数(去重) |
|
||||||
|
| unique_tables | INTEGER | 服务台桌数(去重) |
|
||||||
|
| trashed_seconds | INTEGER | 被废除的服务时长 |
|
||||||
|
| trashed_count | INTEGER | 被废除的服务次数 |
|
||||||
|
|
||||||
|
**数据来源:** dwd_assistant_service_log + dwd_assistant_trash_event
|
||||||
|
|
||||||
|
### 2.2 dws_assistant_monthly_summary - 助教月度业绩汇总
|
||||||
|
|
||||||
|
**粒度:** 助教 + 月份
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| id | BIGSERIAL | 主键 |
|
||||||
|
| site_id | BIGINT | 门店ID |
|
||||||
|
| assistant_id | BIGINT | 助教ID |
|
||||||
|
| stat_month | DATE | 统计月份(月第一天) |
|
||||||
|
| hire_date | DATE | 入职日期 |
|
||||||
|
| is_new_hire | BOOLEAN | 是否新入职 |
|
||||||
|
| work_days | INTEGER | 有服务天数 |
|
||||||
|
| total_hours | NUMERIC(10,2) | 总计费小时数 |
|
||||||
|
| base_hours | NUMERIC(10,2) | 基础课小时数 |
|
||||||
|
| bonus_hours | NUMERIC(10,2) | 附加课小时数 |
|
||||||
|
| effective_hours | NUMERIC(10,2) | 有效业绩小时数 |
|
||||||
|
| trashed_hours | NUMERIC(10,2) | 被废除小时数 |
|
||||||
|
| tier_id | INTEGER | 档位ID |
|
||||||
|
| tier_code | VARCHAR(20) | 档位代码 |
|
||||||
|
| tier_name | VARCHAR(50) | 档位名称 |
|
||||||
|
| rank_by_hours | INTEGER | 月度排名 |
|
||||||
|
| rank_with_ties | INTEGER | 考虑并列的排名 |
|
||||||
|
|
||||||
|
**业务规则:**
|
||||||
|
- 有效业绩 = total_hours - trashed_hours
|
||||||
|
- 新入职判断:入职日期 >= 月1日0点
|
||||||
|
- 排名:按effective_hours降序,并列都算
|
||||||
|
|
||||||
|
### 2.3 dws_assistant_customer_stats - 助教服务客户统计
|
||||||
|
|
||||||
|
**粒度:** 助教 + 客户 + 统计日期
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| id | BIGSERIAL | 主键 |
|
||||||
|
| assistant_id | BIGINT | 助教ID |
|
||||||
|
| member_id | BIGINT | 客户ID |
|
||||||
|
| stat_date | DATE | 统计基准日期 |
|
||||||
|
| first_service_date | DATE | 首次服务日期 |
|
||||||
|
| last_service_date | DATE | 最近服务日期 |
|
||||||
|
| total_service_count | INTEGER | 累计服务次数 |
|
||||||
|
| total_service_hours | NUMERIC(10,2) | 累计服务小时数 |
|
||||||
|
| service_count_7d | INTEGER | 近7天服务次数 |
|
||||||
|
| service_count_30d | INTEGER | 近30天服务次数 |
|
||||||
|
| service_count_90d | INTEGER | 近90天服务次数 |
|
||||||
|
| is_active_7d | BOOLEAN | 近7天是否活跃 |
|
||||||
|
| is_active_30d | BOOLEAN | 近30天是否活跃 |
|
||||||
|
|
||||||
|
**业务规则:**
|
||||||
|
- 散客(member_id=0)不进入此表
|
||||||
|
- 滚动窗口:7/10/15/30/60/90天
|
||||||
|
|
||||||
|
### 2.4 dws_assistant_salary_calc - 助教工资计算详情
|
||||||
|
|
||||||
|
**粒度:** 助教 + 工资月份
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| id | BIGSERIAL | 主键 |
|
||||||
|
| assistant_id | BIGINT | 助教ID |
|
||||||
|
| assistant_nickname | VARCHAR(50) | 助教花名 |
|
||||||
|
| salary_month | DATE | 工资月份(月第一天) |
|
||||||
|
| assistant_level_code | INTEGER | 助教等级代码(8/10/20/30/40) |
|
||||||
|
| assistant_level_name | VARCHAR(20) | 助教等级名称 |
|
||||||
|
| hire_date | DATE | 入职日期 |
|
||||||
|
| is_new_hire | BOOLEAN | 是否新入职 |
|
||||||
|
| effective_hours | NUMERIC(10,2) | 有效业绩小时数(基础课+附加课-废除) |
|
||||||
|
| base_hours | NUMERIC(10,2) | 基础课/专业课小时数 |
|
||||||
|
| bonus_hours | NUMERIC(10,2) | 附加课/打赏课小时数 |
|
||||||
|
| tier_id | INTEGER | 档位ID |
|
||||||
|
| tier_code | VARCHAR(20) | 档位代码(T0-T5/NEW) |
|
||||||
|
| tier_name | VARCHAR(50) | 档位名称 |
|
||||||
|
| rank_with_ties | INTEGER | 月度排名(考虑并列,用于Top3奖金) |
|
||||||
|
| base_course_price | NUMERIC(10,2) | 基础课客户支付价格(98/108/118/138) |
|
||||||
|
| bonus_course_price | NUMERIC(10,2) | 附加课客户支付价格(固定190) |
|
||||||
|
| base_deduction | NUMERIC(10,2) | 专业课抽成(元/小时),档位决定 |
|
||||||
|
| bonus_deduction_ratio | NUMERIC(5,4) | 打赏课抽成比例(0-1),档位决定 |
|
||||||
|
| base_income | NUMERIC(12,2) | 基础课收入 |
|
||||||
|
| bonus_income | NUMERIC(12,2) | 附加课收入 |
|
||||||
|
| total_course_income | NUMERIC(12,2) | 课时收入合计 |
|
||||||
|
| sprint_bonus | NUMERIC(12,2) | 冲刺奖金(H>=190:300, H>=220:800) |
|
||||||
|
| top_rank_bonus | NUMERIC(12,2) | Top3排名奖金(1st:1000, 2nd:600, 3rd:400) |
|
||||||
|
| recharge_commission | NUMERIC(12,2) | 充值提成 |
|
||||||
|
| other_bonus | NUMERIC(12,2) | 其他奖金(手动调整) |
|
||||||
|
| total_bonus | NUMERIC(12,2) | 奖金合计 |
|
||||||
|
| gross_salary | NUMERIC(12,2) | 应发工资 |
|
||||||
|
| vacation_days | INTEGER | 次月可休假天数 |
|
||||||
|
| vacation_unlimited | BOOLEAN | 休假自由标记(5档为TRUE) |
|
||||||
|
| calc_notes | TEXT | 计算备注(异常说明等) |
|
||||||
|
|
||||||
|
**工资计算公式(来自DWS数据库处理需求.md):**
|
||||||
|
|
||||||
|
```
|
||||||
|
基础课收入 = 基础课小时数 × (客户支付价格 - 专业课抽成)
|
||||||
|
附加课收入 = 附加课小时数 × 190 × (1 - 打赏课抽成比例)
|
||||||
|
应发工资 = 课时收入 + 奖金
|
||||||
|
```
|
||||||
|
|
||||||
|
**计算示例(中级助教185小时,3档):**
|
||||||
|
- 基础课170小时: 170 × (108 - 13) = 16,150元
|
||||||
|
- 附加课15小时: 15 × 190 × (1 - 0.35) = 1,852.5元
|
||||||
|
- 课时收入: 18,002.5元
|
||||||
|
- 冲刺奖金(H≥190未达到): 0元
|
||||||
|
- 应发工资: 18,002.5元
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 三、客户维度表
|
||||||
|
|
||||||
|
### 3.1 dws_member_consumption_summary - 会员消费汇总
|
||||||
|
|
||||||
|
**粒度:** 会员 + 统计日期
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| id | BIGSERIAL | 主键 |
|
||||||
|
| member_id | BIGINT | 会员ID |
|
||||||
|
| stat_date | DATE | 统计基准日期 |
|
||||||
|
| first_consume_date | DATE | 首次消费日期 |
|
||||||
|
| last_consume_date | DATE | 最近消费日期 |
|
||||||
|
| total_visit_count | INTEGER | 累计到店次数 |
|
||||||
|
| total_consume_amount | NUMERIC(14,2) | 累计消费金额 |
|
||||||
|
| visit_count_7d | INTEGER | 近7天到店次数 |
|
||||||
|
| visit_count_30d | INTEGER | 近30天到店次数 |
|
||||||
|
| consume_amount_30d | NUMERIC(14,2) | 近30天消费金额 |
|
||||||
|
| cash_card_balance | NUMERIC(14,2) | 储值卡余额 |
|
||||||
|
| gift_card_balance | NUMERIC(14,2) | 赠送卡余额 |
|
||||||
|
| customer_tier | VARCHAR(20) | 客户分层 |
|
||||||
|
|
||||||
|
**客户分层规则:**
|
||||||
|
- 高价值:90天内消费>=3次 且 消费金额>=1000
|
||||||
|
- 中等:30天内有消费
|
||||||
|
- 低活跃:90天内有消费但30天内无消费
|
||||||
|
- 流失:90天内无消费
|
||||||
|
|
||||||
|
### 3.2 dws_member_visit_detail - 会员来店明细
|
||||||
|
|
||||||
|
**粒度:** 会员 + 订单
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| id | BIGSERIAL | 主键 |
|
||||||
|
| member_id | BIGINT | 会员ID |
|
||||||
|
| order_settle_id | BIGINT | 结账单ID |
|
||||||
|
| visit_date | DATE | 来店日期 |
|
||||||
|
| table_name | VARCHAR(50) | 台桌名称 |
|
||||||
|
| area_category | VARCHAR(20) | 区域分类 |
|
||||||
|
| table_fee | NUMERIC(12,2) | 台费 |
|
||||||
|
| goods_amount | NUMERIC(12,2) | 商品金额 |
|
||||||
|
| assistant_amount | NUMERIC(12,2) | 助教服务金额 |
|
||||||
|
| total_consume | NUMERIC(12,2) | 消费总额 |
|
||||||
|
| actual_pay | NUMERIC(12,2) | 实付金额 |
|
||||||
|
| assistant_services | JSONB | 助教服务明细(JSON) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 四、财务维度表
|
||||||
|
|
||||||
|
### 4.1 dws_finance_daily_summary - 财务日度汇总
|
||||||
|
|
||||||
|
**粒度:** 日期
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| id | BIGSERIAL | 主键 |
|
||||||
|
| stat_date | DATE | 统计日期 |
|
||||||
|
| gross_amount | NUMERIC(14,2) | 发生额合计 |
|
||||||
|
| table_fee_amount | NUMERIC(14,2) | 台费正价 |
|
||||||
|
| goods_amount | NUMERIC(14,2) | 商品正价 |
|
||||||
|
| assistant_pd_amount | NUMERIC(14,2) | 助教基础课正价 |
|
||||||
|
| assistant_cx_amount | NUMERIC(14,2) | 助教激励课正价 |
|
||||||
|
| discount_total | NUMERIC(14,2) | 优惠合计 |
|
||||||
|
| discount_groupbuy | NUMERIC(14,2) | 团购优惠 |
|
||||||
|
| discount_vip | NUMERIC(14,2) | 会员折扣 |
|
||||||
|
| discount_gift_card | NUMERIC(14,2) | 赠送卡抵扣 |
|
||||||
|
| discount_manual | NUMERIC(14,2) | 手动调整 |
|
||||||
|
| discount_rounding | NUMERIC(14,2) | 抹零 |
|
||||||
|
| discount_other | NUMERIC(14,2) | 其他优惠(手动调整拆分) |
|
||||||
|
| confirmed_income | NUMERIC(14,2) | 确认收入 |
|
||||||
|
| cash_inflow_total | NUMERIC(14,2) | 现金流入合计 |
|
||||||
|
| cash_pay_amount | NUMERIC(14,2) | 收银实付 |
|
||||||
|
| groupbuy_pay_amount | NUMERIC(14,2) | 团购支付金额 |
|
||||||
|
| platform_settlement_amount | NUMERIC(14,2) | 平台回款金额 |
|
||||||
|
| platform_fee_amount | NUMERIC(14,2) | 平台服务费+佣金 |
|
||||||
|
| recharge_cash_inflow | NUMERIC(14,2) | 充值现金流入 |
|
||||||
|
| card_consume_total | NUMERIC(14,2) | 卡消费合计 |
|
||||||
|
| cash_card_consume | NUMERIC(14,2) | 储值卡消费 |
|
||||||
|
| gift_card_consume | NUMERIC(14,2) | 赠送卡消费 |
|
||||||
|
| cash_outflow_total | NUMERIC(14,2) | 现金流出合计 |
|
||||||
|
| cash_balance_change | NUMERIC(14,2) | 现金结余 |
|
||||||
|
| recharge_count | INTEGER | 充值笔数 |
|
||||||
|
| recharge_total | NUMERIC(14,2) | 充值总额 |
|
||||||
|
| recharge_cash | NUMERIC(14,2) | 充值现金部分 |
|
||||||
|
| recharge_gift | NUMERIC(14,2) | 充值赠送部分 |
|
||||||
|
| first_recharge_count | INTEGER | 首充笔数 |
|
||||||
|
| renewal_count | INTEGER | 续充笔数 |
|
||||||
|
| order_count | INTEGER | 结账单数 |
|
||||||
|
| member_order_count | INTEGER | 会员订单数 |
|
||||||
|
| guest_order_count | INTEGER | 散客订单数 |
|
||||||
|
| avg_order_amount | NUMERIC(12,2) | 平均客单价 |
|
||||||
|
|
||||||
|
**计算公式:**
|
||||||
|
- 发生额 = table_charge_money + goods_money + assistant_pd_money + assistant_cx_money
|
||||||
|
- 团购支付金额 = pl_coupon_sale_amount > 0 ? pl_coupon_sale_amount : groupbuy_redemption.ledger_unit_price
|
||||||
|
- 团购优惠 = coupon_amount - 团购支付金额
|
||||||
|
- 优惠合计 = 团购优惠 + 会员折扣 + 赠送卡抵扣 + 手动调整 + 抹零
|
||||||
|
- 其他优惠 = adjust_amount - 大客户优惠(不足0按0处理)
|
||||||
|
- 确认收入 = 发生额 - 优惠合计
|
||||||
|
- 平台回款金额 = dws_platform_settlement.settlement_amount(若无导入,则使用团购支付金额)
|
||||||
|
- 平台服务费 = commission_amount + service_fee
|
||||||
|
- 现金流入合计 = 收银实付 + 平台回款金额 + 充值现金流入
|
||||||
|
- 现金流出合计 = 支出汇总 + 平台服务费
|
||||||
|
- 现金结余 = 现金流入合计 - 现金流出合计
|
||||||
|
|
||||||
|
**财务指标数据来源矩阵(字段 → 来源 → 口径)**
|
||||||
|
| 字段 | 来源表 | 口径说明 |
|
||||||
|
|------|--------|----------|
|
||||||
|
| gross_amount | dwd_settlement_head | table_charge_money + goods_money + assistant_pd_money + assistant_cx_money |
|
||||||
|
| discount_groupbuy | dwd_settlement_head + dwd_groupbuy_redemption | coupon_amount - 团购支付金额 |
|
||||||
|
| discount_vip | dwd_settlement_head | member_discount_amount |
|
||||||
|
| discount_gift_card | dwd_settlement_head | gift_card_amount |
|
||||||
|
| discount_manual | dwd_settlement_head | adjust_amount(手动调整总额) |
|
||||||
|
| discount_rounding | dwd_settlement_head | rounding_amount |
|
||||||
|
| discount_other | dwd_settlement_head | adjust_amount - 大客户优惠(配置映射) |
|
||||||
|
| confirmed_income | dwd_settlement_head | gross_amount - discount_total |
|
||||||
|
| cash_pay_amount | dwd_settlement_head | pay_amount(收银实付) |
|
||||||
|
| groupbuy_pay_amount | dwd_settlement_head + dwd_groupbuy_redemption | pl_coupon_sale_amount 或 ledger_unit_price |
|
||||||
|
| platform_settlement_amount | dws_platform_settlement | settlement_amount(Excel导入) |
|
||||||
|
| platform_fee_amount | dws_platform_settlement | commission_amount + service_fee |
|
||||||
|
| recharge_cash_inflow | dwd_recharge_order | pay_money(现金充值) |
|
||||||
|
| cash_inflow_total | dwd_settlement_head + dws_platform_settlement + dwd_recharge_order | 收银实付 + 平台回款 + 充值现金 |
|
||||||
|
| cash_outflow_total | dws_finance_expense_summary + dws_platform_settlement | 支出汇总 + 平台服务费 |
|
||||||
|
| cash_balance_change | dws_finance_daily_summary | cash_inflow_total - cash_outflow_total |
|
||||||
|
|
||||||
|
### 4.2 dws_finance_recharge_summary - 充值统计
|
||||||
|
|
||||||
|
**粒度:** 日期
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| id | BIGSERIAL | 主键 |
|
||||||
|
| stat_date | DATE | 统计日期 |
|
||||||
|
| recharge_count | INTEGER | 充值笔数 |
|
||||||
|
| recharge_total | NUMERIC(14,2) | 充值总额(含赠送) |
|
||||||
|
| recharge_cash | NUMERIC(14,2) | 现金充值金额 |
|
||||||
|
| recharge_gift | NUMERIC(14,2) | 赠送金额 |
|
||||||
|
| first_recharge_count | INTEGER | 首充笔数 |
|
||||||
|
| first_recharge_cash | NUMERIC(14,2) | 首充现金 |
|
||||||
|
| renewal_count | INTEGER | 续充笔数 |
|
||||||
|
| renewal_cash | NUMERIC(14,2) | 续充现金 |
|
||||||
|
| cash_card_balance | NUMERIC(14,2) | 储值卡余额 |
|
||||||
|
| gift_card_balance | NUMERIC(14,2) | 赠送卡余额 |
|
||||||
|
|
||||||
|
**数据来源:** dwd_recharge_order(is_first字段区分首充/续充)
|
||||||
|
|
||||||
|
### 4.3 dws_finance_income_structure - 收入结构分析
|
||||||
|
|
||||||
|
**粒度:** 日期 + 结构类型 + 分类
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| id | BIGSERIAL | 主键 |
|
||||||
|
| stat_date | DATE | 统计日期 |
|
||||||
|
| structure_type | VARCHAR(20) | 结构类型(INCOME_TYPE/AREA) |
|
||||||
|
| category_code | VARCHAR(30) | 分类代码 |
|
||||||
|
| category_name | VARCHAR(50) | 分类名称 |
|
||||||
|
| income_amount | NUMERIC(14,2) | 收入金额 |
|
||||||
|
| income_ratio | NUMERIC(5,4) | 收入占比 |
|
||||||
|
| order_count | INTEGER | 订单数 |
|
||||||
|
| duration_minutes | INTEGER | 时长(分钟) |
|
||||||
|
|
||||||
|
**结构类型说明:**
|
||||||
|
|
||||||
|
1. **INCOME_TYPE(按收入类型):**
|
||||||
|
- TABLE_FEE = 台费收入
|
||||||
|
- GOODS = 商品收入
|
||||||
|
- ASSISTANT_BASE = 助教基础课
|
||||||
|
- ASSISTANT_BONUS = 助教附加课
|
||||||
|
|
||||||
|
2. **AREA(按区域):**
|
||||||
|
- 使用cfg_area_category映射(BILLIARD/BILLIARD_VIP/SNOOKER/MAHJONG/KTV/OTHER)
|
||||||
|
|
||||||
|
**数据来源:** dwd_settlement_head, dwd_table_fee_log, dwd_assistant_service_log
|
||||||
|
|
||||||
|
### 4.4 dws_finance_discount_detail - 优惠明细
|
||||||
|
|
||||||
|
**粒度:** 日期 + 优惠类型
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| id | BIGSERIAL | 主键 |
|
||||||
|
| stat_date | DATE | 统计日期 |
|
||||||
|
| discount_type_code | VARCHAR(30) | 优惠类型代码 |
|
||||||
|
| discount_type_name | VARCHAR(50) | 优惠类型名称 |
|
||||||
|
| discount_amount | NUMERIC(14,2) | 优惠金额 |
|
||||||
|
| discount_ratio | NUMERIC(5,4) | 优惠占比(占总优惠) |
|
||||||
|
| usage_count | INTEGER | 使用次数 |
|
||||||
|
| affected_orders | INTEGER | 影响订单数 |
|
||||||
|
|
||||||
|
**优惠类型:**
|
||||||
|
- GROUPBUY = 团购优惠(coupon_amount - 团购实付金额)
|
||||||
|
- VIP = 会员折扣(member_discount_amount)
|
||||||
|
- GIFT_CARD = 赠送卡抵扣(gift_card_amount)
|
||||||
|
- ROUNDING = 抹零(rounding_amount)
|
||||||
|
- BIG_CUSTOMER = 大客户优惠(基于配置映射的手动调整)
|
||||||
|
- OTHER = 其他优惠(手动调整中除大客户外部分)
|
||||||
|
|
||||||
|
**数据来源:** dwd_settlement_head, dwd_groupbuy_redemption
|
||||||
|
|
||||||
|
### 4.5 dws_finance_expense_summary - 支出结构(Excel导入)
|
||||||
|
|
||||||
|
**粒度:** 月份 + 支出类型
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| id | BIGSERIAL | 主键 |
|
||||||
|
| expense_month | DATE | 支出月份 |
|
||||||
|
| expense_type_code | VARCHAR(30) | 支出类型代码 |
|
||||||
|
| expense_type_name | VARCHAR(50) | 支出类型名称 |
|
||||||
|
| expense_category | VARCHAR(20) | 支出大类 |
|
||||||
|
| expense_amount | NUMERIC(14,2) | 支出金额 |
|
||||||
|
| import_batch_no | VARCHAR(50) | 导入批次号 |
|
||||||
|
|
||||||
|
**支出类型:**
|
||||||
|
- RENT = 房租
|
||||||
|
- UTILITY = 水电费
|
||||||
|
- PROPERTY = 物业费
|
||||||
|
- SALARY = 工资
|
||||||
|
- REIMBURSE = 报销
|
||||||
|
- PLATFORM_FEE = 平台服务费
|
||||||
|
- OTHER = 其他
|
||||||
|
|
||||||
|
### 4.6 dws_platform_settlement - 平台回款(Excel导入)
|
||||||
|
|
||||||
|
**粒度:** 回款日期 + 平台 + 订单
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| id | BIGSERIAL | 主键 |
|
||||||
|
| settlement_date | DATE | 回款日期 |
|
||||||
|
| platform_type | VARCHAR(30) | 平台类型 |
|
||||||
|
| platform_name | VARCHAR(50) | 平台名称 |
|
||||||
|
| platform_order_no | VARCHAR(100) | 平台订单号 |
|
||||||
|
| order_settle_id | BIGINT | 关联的结账单ID |
|
||||||
|
| settlement_amount | NUMERIC(14,2) | 回款金额 |
|
||||||
|
| commission_amount | NUMERIC(14,2) | 佣金 |
|
||||||
|
| service_fee | NUMERIC(14,2) | 服务费 |
|
||||||
|
| gross_amount | NUMERIC(14,2) | 订单原始金额 |
|
||||||
|
| import_batch_no | VARCHAR(50) | 导入批次号 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 五、时间分层机制
|
||||||
|
|
||||||
|
### 5.1 时间口径定义
|
||||||
|
|
||||||
|
| 时间窗口 | 说明 | 边界规则 |
|
||||||
|
|----------|------|----------|
|
||||||
|
| 本周 | 从本周一到今天 | 周起始日为周一 |
|
||||||
|
| 上周 | 上周一到上周日 | 完整7天 |
|
||||||
|
| 本月 | 从月1日到今天 | 月第一天0点起 |
|
||||||
|
| 上月 | 上月完整月份 | 完整自然月 |
|
||||||
|
| 前3个月不含本月 | 三个月前月初到上月末 | 不含当前月 |
|
||||||
|
| 前3个月含本月 | 两个月前月初到今天 | 含当前月 |
|
||||||
|
| 本季度 | 季度第一月1日到今天 | 季度起始 |
|
||||||
|
| 上季度 | 上季度完整三个月 | 完整自然季 |
|
||||||
|
| 最近半年 | 往前6个月(不含本月) | 不含当前月 |
|
||||||
|
|
||||||
|
### 5.2 滚动窗口
|
||||||
|
|
||||||
|
支持以下滚动窗口统计:
|
||||||
|
- 近7天
|
||||||
|
- 近10天
|
||||||
|
- 近15天
|
||||||
|
- 近30天
|
||||||
|
- 近60天
|
||||||
|
- 近90天
|
||||||
|
|
||||||
|
### 5.3 环比计算
|
||||||
|
|
||||||
|
环比规则:对比上一个等长区间
|
||||||
|
- 如查询1月1日-1月15日,环比为12月17日-12月31日
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 六、数据更新策略
|
||||||
|
|
||||||
|
| 表类型 | 更新频率 | 幂等方式 |
|
||||||
|
|--------|----------|----------|
|
||||||
|
| 日度明细表 | 每小时 | delete-before-insert(按日期窗口) |
|
||||||
|
| 日度汇总表 | 每小时 | delete-before-insert(按日期) |
|
||||||
|
| 月度汇总表 | 每日 | delete-before-insert(按月份) |
|
||||||
|
| 客户统计表 | 每日 | delete-before-insert(按统计日期) |
|
||||||
|
| Excel导入表 | 手动 | 按import_batch_no去重 |
|
||||||
494
etl_billiards/docs/index_tables.md
Normal file
494
etl_billiards/docs/index_tables.md
Normal file
@@ -0,0 +1,494 @@
|
|||||||
|
## 1. 客户召回表
|
||||||
|
|
||||||
|
| 客户姓名 | 召回指数 |
|
||||||
|
|----------|----------|
|
||||||
|
| 陈腾鑫 | 10.00 |
|
||||||
|
| 章先生 | 10.00 |
|
||||||
|
| 孙总 | 10.00 |
|
||||||
|
| 梅 | 10.00 |
|
||||||
|
| 胡先生 | 10.00 |
|
||||||
|
| 黄先生 | 9.63 |
|
||||||
|
| 小熊 | 9.52 |
|
||||||
|
| 周先生 | 9.41 |
|
||||||
|
| 李先生 | 9.39 |
|
||||||
|
| 王 | 9.27 |
|
||||||
|
| 张无忌 | 9.20 |
|
||||||
|
| 黄先生 | 8.96 |
|
||||||
|
| 陈德韩 | 8.94 |
|
||||||
|
| 胡总 | 8.93 |
|
||||||
|
| T | 8.89 |
|
||||||
|
| 候 | 8.88 |
|
||||||
|
| 孙先生 | 8.87 |
|
||||||
|
| 王先生 | 8.86 |
|
||||||
|
| 清 | 8.86 |
|
||||||
|
| amy | 8.84 |
|
||||||
|
| 林先生 | 8.84 |
|
||||||
|
| 张先生 | 8.79 |
|
||||||
|
| 刘先生 | 8.79 |
|
||||||
|
| 黄国磊 | 8.79 |
|
||||||
|
| 游 | 8.79 |
|
||||||
|
| 陈先生 | 8.79 |
|
||||||
|
| 陈 | 8.79 |
|
||||||
|
| 大G | 8.79 |
|
||||||
|
| 李先生 | 8.79 |
|
||||||
|
| 孙启明 | 8.79 |
|
||||||
|
| 陈先生 | 8.79 |
|
||||||
|
| 罗先生 | 8.79 |
|
||||||
|
| 刘哥 | 8.79 |
|
||||||
|
| 杨 | 8.79 |
|
||||||
|
| 枫先生 | 8.79 |
|
||||||
|
| 老宋 | 8.79 |
|
||||||
|
| 黄先生 | 8.79 |
|
||||||
|
| 刘女士 | 8.79 |
|
||||||
|
| 彭先生 | 8.79 |
|
||||||
|
| 李 | 8.79 |
|
||||||
|
| 桂先生 | 8.79 |
|
||||||
|
| 王先生 | 8.79 |
|
||||||
|
| 潘先生 | 8.79 |
|
||||||
|
| 方先生 | 8.79 |
|
||||||
|
| 郑先生 | 8.79 |
|
||||||
|
| 阿亮 | 8.79 |
|
||||||
|
| 孟紫龙 | 8.79 |
|
||||||
|
| 林总 | 8.78 |
|
||||||
|
| 林志铭 | 8.64 |
|
||||||
|
| 罗超 | 8.63 |
|
||||||
|
| 张丹逸 | 8.52 |
|
||||||
|
| 谢俊 | 8.07 |
|
||||||
|
| 王龙 | 7.80 |
|
||||||
|
| 唐先生 | 7.79 |
|
||||||
|
| 周周 | 7.47 |
|
||||||
|
| 曾巧明 | 6.83 |
|
||||||
|
| 昌哥 | 6.17 |
|
||||||
|
| 江先生 | 5.84 |
|
||||||
|
| 袁 | 5.24 |
|
||||||
|
| 蔡总 | 4.73 |
|
||||||
|
| 胡先生 | 4.51 |
|
||||||
|
| 陈先生 | 4.45 |
|
||||||
|
| 明哥 | 3.92 |
|
||||||
|
| 公孙先生 | 3.57 |
|
||||||
|
| 曾先生 | 3.47 |
|
||||||
|
| 黄生 | 3.46 |
|
||||||
|
| 葛先生 | 3.35 |
|
||||||
|
| 轩哥 | 3.32 |
|
||||||
|
| 张先生 | 2.73 |
|
||||||
|
| 叶先生 | 2.61 |
|
||||||
|
| 小燕 | 2.39 |
|
||||||
|
| 罗先生 | 2.38 |
|
||||||
|
| 李先生 | 2.23 |
|
||||||
|
| 陈淑涛 | 2.23 |
|
||||||
|
| 肖先生 | 2.23 |
|
||||||
|
| 范先生 | 2.14 |
|
||||||
|
| 常总 | 1.47 |
|
||||||
|
| 董贝 | 1.04 |
|
||||||
|
| 陈小姐 | 1.04 |
|
||||||
|
| 林先生 | 0.90 |
|
||||||
|
| 柳先生 | 0.61 |
|
||||||
|
| 林先生 | 0.20 |
|
||||||
|
| 潘先生 | 0.20 |
|
||||||
|
| 曾丹烨 | 0.07 |
|
||||||
|
| 魏先生 | 0.00 |
|
||||||
|
| 艾宇民 | 0.00 |
|
||||||
|
| 吴生 | 0.00 |
|
||||||
|
| 卢广贤 | 0.00 |
|
||||||
|
| 陈泽斌 | 0.00 |
|
||||||
|
| 李先生 | 0.00 |
|
||||||
|
|
||||||
|
共 90 条记录
|
||||||
|
|
||||||
|
## 2. 助教客户关系表
|
||||||
|
|
||||||
|
| 助教花名 | 客户姓名 | 关系指数 |
|
||||||
|
|----------|----------|----------|
|
||||||
|
| 卡顿 | 葛先生 | 10.00 |
|
||||||
|
| 小燕 | 葛先生 | 10.00 |
|
||||||
|
| 七七 | 轩哥 | 10.00 |
|
||||||
|
| 佳怡 | 罗先生 | 10.00 |
|
||||||
|
| 璇子 | 轩哥 | 10.00 |
|
||||||
|
| 阿清 | 张先生 | 10.00 |
|
||||||
|
| 璇子 | 江先生 | 10.00 |
|
||||||
|
| CC | 周周 | 10.00 |
|
||||||
|
| 周周 | 周周 | 10.00 |
|
||||||
|
| 小燕 | 小燕 | 10.00 |
|
||||||
|
| 卡顿 | 小燕 | 10.00 |
|
||||||
|
| 姜姜 | 张先生 | 10.00 |
|
||||||
|
| 小侯 | 张先生 | 10.00 |
|
||||||
|
| 渔渔 | 张先生 | 10.00 |
|
||||||
|
| 欣欣 | 张先生 | 10.00 |
|
||||||
|
| 千千 | 张先生 | 10.00 |
|
||||||
|
| 小A | 张先生 | 10.00 |
|
||||||
|
| 甜甜 | 张先生 | 10.00 |
|
||||||
|
| 小A | 周先生 | 10.00 |
|
||||||
|
| 欣欣 | 周先生 | 10.00 |
|
||||||
|
| 千千 | 周先生 | 10.00 |
|
||||||
|
| 甜甜 | 周先生 | 10.00 |
|
||||||
|
| 涛涛 | 蔡总 | 10.00 |
|
||||||
|
| 婉婉 | 吴先生 | 10.00 |
|
||||||
|
| 千千 | 梅 | 10.00 |
|
||||||
|
| 甜甜 | 梅 | 10.00 |
|
||||||
|
| 小A | 梅 | 10.00 |
|
||||||
|
| 欣欣 | 梅 | 10.00 |
|
||||||
|
| 球球 | 周周 | 10.00 |
|
||||||
|
| 涛涛 | 轩哥 | 10.00 |
|
||||||
|
| 小不点 | 周周 | 10.00 |
|
||||||
|
| 小柔 | 蔡总 | 10.00 |
|
||||||
|
| 年糕 | 葛先生 | 10.00 |
|
||||||
|
| 佳怡 | 陈腾鑫 | 10.00 |
|
||||||
|
| 小不点 | 罗先生 | 10.00 |
|
||||||
|
| 球球 | 罗先生 | 10.00 |
|
||||||
|
| 小柔 | 轩哥 | 10.00 |
|
||||||
|
| 阿清 | 梅 | 10.00 |
|
||||||
|
| 阿清 | 胡先生 | 10.00 |
|
||||||
|
| 佳怡 | 陈先生 | 10.00 |
|
||||||
|
| 小不点 | 轩哥 | 10.00 |
|
||||||
|
| 佳怡 | 小熊 | 10.00 |
|
||||||
|
| 球球 | 轩哥 | 10.00 |
|
||||||
|
| 阿清 | 孙总 | 10.00 |
|
||||||
|
| CC | 罗先生 | 10.00 |
|
||||||
|
| 周周 | 罗先生 | 10.00 |
|
||||||
|
| 小柔 | 明哥 | 9.96 |
|
||||||
|
| 渔渔 | 李先生 | 9.88 |
|
||||||
|
| 姜姜 | 李先生 | 9.88 |
|
||||||
|
| 小侯 | 李先生 | 9.88 |
|
||||||
|
| 年糕 | 常总 | 9.85 |
|
||||||
|
| 婉婉 | 明哥 | 9.61 |
|
||||||
|
| 乔西 | 陈先生 | 9.59 |
|
||||||
|
| 璇子 | 蔡总 | 9.46 |
|
||||||
|
| CC | 常总 | 9.40 |
|
||||||
|
| 周周 | 常总 | 9.40 |
|
||||||
|
| 七七 | 蔡总 | 9.24 |
|
||||||
|
| 甜甜 | 孙总 | 9.24 |
|
||||||
|
| 小A | 孙总 | 9.24 |
|
||||||
|
| 欣欣 | 孙总 | 9.24 |
|
||||||
|
| 千千 | 孙总 | 9.24 |
|
||||||
|
| 七七 | 胡先生 | 9.20 |
|
||||||
|
| 千千 | 小熊 | 9.02 |
|
||||||
|
| 甜甜 | 小熊 | 9.02 |
|
||||||
|
| 欣欣 | 小熊 | 9.02 |
|
||||||
|
| 小A | 小熊 | 9.02 |
|
||||||
|
| 佳怡 | 胡先生 | 9.02 |
|
||||||
|
| 涛涛 | 小燕 | 8.66 |
|
||||||
|
| 阿清 | 轩哥 | 8.53 |
|
||||||
|
| 年糕 | 叶先生 | 8.51 |
|
||||||
|
| 小不点 | 张先生 | 8.39 |
|
||||||
|
| 球球 | 张先生 | 8.39 |
|
||||||
|
| 阿清 | 葛先生 | 8.36 |
|
||||||
|
| 周周 | 张先生 | 8.30 |
|
||||||
|
| CC | 张先生 | 8.30 |
|
||||||
|
| 甜甜 | 胡先生 | 8.04 |
|
||||||
|
| 千千 | 胡先生 | 8.04 |
|
||||||
|
| 欣欣 | 胡先生 | 8.04 |
|
||||||
|
| 小A | 胡先生 | 8.04 |
|
||||||
|
| 小不点 | 小熊 | 7.88 |
|
||||||
|
| 球球 | 小熊 | 7.88 |
|
||||||
|
| 小侯 | 胡先生 | 7.86 |
|
||||||
|
| 姜姜 | 胡先生 | 7.86 |
|
||||||
|
| 渔渔 | 胡先生 | 7.86 |
|
||||||
|
| 乔西 | 罗先生 | 7.86 |
|
||||||
|
| 小不点 | 胡先生 | 7.65 |
|
||||||
|
| 球球 | 胡先生 | 7.65 |
|
||||||
|
| 球球 | 孙总 | 7.56 |
|
||||||
|
| 小不点 | 孙总 | 7.56 |
|
||||||
|
| 璇子 | 孙总 | 7.46 |
|
||||||
|
| 阿清 | 清 | 7.35 |
|
||||||
|
| 小A | 小燕 | 7.09 |
|
||||||
|
| 甜甜 | 小燕 | 7.09 |
|
||||||
|
| 欣欣 | 小燕 | 7.09 |
|
||||||
|
| 千千 | 小燕 | 7.09 |
|
||||||
|
| 甜甜 | 公孙先生 | 7.07 |
|
||||||
|
| 千千 | 公孙先生 | 7.07 |
|
||||||
|
| 欣欣 | 公孙先生 | 7.07 |
|
||||||
|
| 小A | 公孙先生 | 7.07 |
|
||||||
|
| 婉婉 | 孙总 | 7.03 |
|
||||||
|
| 菲菲 | 陈腾鑫 | 6.92 |
|
||||||
|
| 橙子 | 陈腾鑫 | 6.92 |
|
||||||
|
| 希希 | 陈腾鑫 | 6.92 |
|
||||||
|
| 婉婉 | 章先生 | 6.91 |
|
||||||
|
| 婉婉 | 公孙先生 | 6.86 |
|
||||||
|
| CC | 林先生 | 6.77 |
|
||||||
|
| 周周 | 林先生 | 6.77 |
|
||||||
|
| 阿清 | 小燕 | 6.76 |
|
||||||
|
| 苏苏 | 蔡总 | 6.64 |
|
||||||
|
| 七七 | 小燕 | 6.60 |
|
||||||
|
| 小不点 | 江先生 | 6.59 |
|
||||||
|
| 球球 | 江先生 | 6.59 |
|
||||||
|
| 涛涛 | 罗先生 | 6.52 |
|
||||||
|
| 凤梨 | 葛先生 | 6.51 |
|
||||||
|
| 佳怡 | 轩哥 | 6.49 |
|
||||||
|
| 年糕 | 轩哥 | 6.44 |
|
||||||
|
| 年糕 | 小燕 | 6.43 |
|
||||||
|
| CC | 轩哥 | 6.26 |
|
||||||
|
| 周周 | 轩哥 | 6.26 |
|
||||||
|
| yy | 公孙先生 | 6.13 |
|
||||||
|
| 阿清 | 陈腾鑫 | 6.04 |
|
||||||
|
| 佳怡 | 周周 | 6.03 |
|
||||||
|
| 七七 | 江先生 | 5.93 |
|
||||||
|
| CC | 林先生 | 5.87 |
|
||||||
|
| 周周 | 林先生 | 5.87 |
|
||||||
|
| 年糕 | 王 | 5.76 |
|
||||||
|
| 年糕 | 李先生 | 5.72 |
|
||||||
|
| 七七 | 孙总 | 5.69 |
|
||||||
|
| 苏苏 | 黄先生 | 5.66 |
|
||||||
|
| 婉婉 | 叶先生 | 5.55 |
|
||||||
|
| 涛涛 | 叶先生 | 5.55 |
|
||||||
|
| 凤梨 | 叶先生 | 5.54 |
|
||||||
|
| 小A | 黄先生 | 5.53 |
|
||||||
|
| 甜甜 | 黄先生 | 5.53 |
|
||||||
|
| 千千 | 黄先生 | 5.53 |
|
||||||
|
| 欣欣 | 黄先生 | 5.53 |
|
||||||
|
| yy | 叶先生 | 5.53 |
|
||||||
|
| 苏苏 | 罗先生 | 5.48 |
|
||||||
|
| 小侯 | 葛先生 | 5.47 |
|
||||||
|
| 渔渔 | 葛先生 | 5.47 |
|
||||||
|
| 姜姜 | 葛先生 | 5.47 |
|
||||||
|
| 佳怡 | 林志铭 | 5.45 |
|
||||||
|
| 婉婉 | 葛先生 | 5.37 |
|
||||||
|
| CC | 小熊 | 5.29 |
|
||||||
|
| 周周 | 小熊 | 5.29 |
|
||||||
|
| 涛涛 | 孙总 | 5.20 |
|
||||||
|
| 小敌 | 李先生 | 5.09 |
|
||||||
|
| 吱吱 | 李先生 | 5.09 |
|
||||||
|
| 周周 | 葛先生 | 5.08 |
|
||||||
|
| CC | 葛先生 | 5.08 |
|
||||||
|
| 甜甜 | 蔡总 | 5.04 |
|
||||||
|
| 千千 | 蔡总 | 5.04 |
|
||||||
|
| 欣欣 | 蔡总 | 5.04 |
|
||||||
|
| 小A | 蔡总 | 5.04 |
|
||||||
|
| 婉婉 | 轩哥 | 5.03 |
|
||||||
|
| 年糕 | 胡先生 | 5.02 |
|
||||||
|
| 吱吱 | 葛先生 | 4.88 |
|
||||||
|
| 小敌 | 葛先生 | 4.88 |
|
||||||
|
| 婉婉 | 王 | 4.87 |
|
||||||
|
| yy | 张先生 | 4.66 |
|
||||||
|
| 璇子 | 罗先生 | 4.65 |
|
||||||
|
| yy | 葛先生 | 4.59 |
|
||||||
|
| 苏苏 | 柳先生 | 4.58 |
|
||||||
|
| 乔西 | 蔡总 | 4.50 |
|
||||||
|
| 七七 | 张先生 | 4.36 |
|
||||||
|
| 乔西 | 葛先生 | 4.33 |
|
||||||
|
| 乔西 | 小熊 | 4.33 |
|
||||||
|
| 周周 | 江先生 | 4.32 |
|
||||||
|
| CC | 江先生 | 4.32 |
|
||||||
|
| Amy | 轩哥 | 4.31 |
|
||||||
|
| 年糕 | 罗超 | 4.20 |
|
||||||
|
| yy | 林志铭 | 4.19 |
|
||||||
|
| 年糕 | 艾宇民 | 4.16 |
|
||||||
|
| 阿清 | 黄先生 | 4.14 |
|
||||||
|
| 七七 | 罗超 | 4.12 |
|
||||||
|
| 年糕 | 范先生 | 4.08 |
|
||||||
|
| 凤梨 | 林先生 | 4.07 |
|
||||||
|
| 璇子 | 张先生 | 4.06 |
|
||||||
|
| 球球 | 常总 | 4.05 |
|
||||||
|
| 小不点 | 常总 | 4.05 |
|
||||||
|
| yy | 孙总 | 3.99 |
|
||||||
|
| 七七 | 葛先生 | 3.93 |
|
||||||
|
| 乔西 | 轩哥 | 3.90 |
|
||||||
|
| 年糕 | 小熊 | 3.85 |
|
||||||
|
| 千千 | 李先生 | 3.73 |
|
||||||
|
| 欣欣 | 李先生 | 3.73 |
|
||||||
|
| 小A | 李先生 | 3.73 |
|
||||||
|
| 甜甜 | 李先生 | 3.73 |
|
||||||
|
| 姜姜 | 轩哥 | 3.62 |
|
||||||
|
| 渔渔 | 轩哥 | 3.62 |
|
||||||
|
| 小侯 | 轩哥 | 3.62 |
|
||||||
|
| 迟迟 | 轩哥 | 3.60 |
|
||||||
|
| 泡芙 | 轩哥 | 3.60 |
|
||||||
|
| 小琳 | 轩哥 | 3.60 |
|
||||||
|
| 七七 | 罗先生 | 3.57 |
|
||||||
|
| 年糕 | 胡总 | 3.47 |
|
||||||
|
| 欣欣 | 葛先生 | 3.43 |
|
||||||
|
| 甜甜 | 葛先生 | 3.43 |
|
||||||
|
| 千千 | 葛先生 | 3.43 |
|
||||||
|
| 小A | 葛先生 | 3.43 |
|
||||||
|
| 七七 | 林总 | 3.43 |
|
||||||
|
| 乔西 | 陈德韩 | 3.38 |
|
||||||
|
| 泡芙 | 林总 | 3.31 |
|
||||||
|
| 迟迟 | 林总 | 3.31 |
|
||||||
|
| 小琳 | 林总 | 3.31 |
|
||||||
|
| 涛涛 | 葛先生 | 3.27 |
|
||||||
|
| 阿清 | 罗先生 | 3.16 |
|
||||||
|
| 璇子 | 周周 | 3.16 |
|
||||||
|
| 阿清 | 王先生 | 3.14 |
|
||||||
|
| 小柳 | 轩哥 | 3.06 |
|
||||||
|
| 迟迟 | 陈腾鑫 | 3.04 |
|
||||||
|
| 小琳 | 陈腾鑫 | 3.04 |
|
||||||
|
| 泡芙 | 陈腾鑫 | 3.04 |
|
||||||
|
| 瑶瑶 | 蔡总 | 2.92 |
|
||||||
|
| 图图 | 蔡总 | 2.92 |
|
||||||
|
| 小A | 轩哥 | 2.91 |
|
||||||
|
| 千千 | 轩哥 | 2.91 |
|
||||||
|
| 欣欣 | 轩哥 | 2.91 |
|
||||||
|
| 甜甜 | 轩哥 | 2.91 |
|
||||||
|
| 年糕 | 罗先生 | 2.84 |
|
||||||
|
| 小不点 | 黄先生 | 2.73 |
|
||||||
|
| 球球 | 黄先生 | 2.73 |
|
||||||
|
| 渔渔 | 梅 | 2.72 |
|
||||||
|
| 姜姜 | 梅 | 2.72 |
|
||||||
|
| 小侯 | 梅 | 2.72 |
|
||||||
|
| 欣欣 | 陈先生 | 2.68 |
|
||||||
|
| 千千 | 陈先生 | 2.68 |
|
||||||
|
| 小A | 陈先生 | 2.68 |
|
||||||
|
| 甜甜 | 陈先生 | 2.68 |
|
||||||
|
| 婉婉 | 江先生 | 2.67 |
|
||||||
|
| 千千 | 枫先生 | 2.67 |
|
||||||
|
| 欣欣 | 枫先生 | 2.67 |
|
||||||
|
| 小A | 枫先生 | 2.67 |
|
||||||
|
| 甜甜 | 枫先生 | 2.67 |
|
||||||
|
| 阿清 | 枫先生 | 2.67 |
|
||||||
|
| 乔西 | 张无忌 | 2.55 |
|
||||||
|
| 甜甜 | 范先生 | 2.51 |
|
||||||
|
| 千千 | 范先生 | 2.51 |
|
||||||
|
| 小A | 范先生 | 2.51 |
|
||||||
|
| 欣欣 | 范先生 | 2.51 |
|
||||||
|
| 七七 | 林先生 | 2.45 |
|
||||||
|
| CC | T | 2.36 |
|
||||||
|
| 周周 | T | 2.36 |
|
||||||
|
| 苏苏 | 周周 | 2.36 |
|
||||||
|
| 小侯 | 周先生 | 2.28 |
|
||||||
|
| 渔渔 | 周先生 | 2.28 |
|
||||||
|
| 姜姜 | 周先生 | 2.28 |
|
||||||
|
| 涛涛 | 胡总 | 2.28 |
|
||||||
|
| 苏苏 | 林先生 | 2.14 |
|
||||||
|
| 渔渔 | 彭先生 | 2.07 |
|
||||||
|
| 小侯 | 彭先生 | 2.07 |
|
||||||
|
| 姜姜 | 彭先生 | 2.07 |
|
||||||
|
| 小侯 | 清 | 2.03 |
|
||||||
|
| 甜甜 | 清 | 2.03 |
|
||||||
|
| 小A | 清 | 2.03 |
|
||||||
|
| 欣欣 | 清 | 2.03 |
|
||||||
|
| 千千 | 清 | 2.03 |
|
||||||
|
| 渔渔 | 清 | 2.03 |
|
||||||
|
| 姜姜 | 清 | 2.03 |
|
||||||
|
| 苏苏 | 张先生 | 1.94 |
|
||||||
|
| 千千 | 林总 | 1.88 |
|
||||||
|
| 甜甜 | 林总 | 1.88 |
|
||||||
|
| 欣欣 | 林总 | 1.88 |
|
||||||
|
| 小A | 林总 | 1.88 |
|
||||||
|
| 甜甜 | 陈腾鑫 | 1.82 |
|
||||||
|
| 欣欣 | 陈腾鑫 | 1.82 |
|
||||||
|
| 千千 | 陈腾鑫 | 1.82 |
|
||||||
|
| 小A | 陈腾鑫 | 1.82 |
|
||||||
|
| 佳怡 | 彭先生 | 1.80 |
|
||||||
|
| 婉婉 | 周先生 | 1.77 |
|
||||||
|
| 苏苏 | 周先生 | 1.68 |
|
||||||
|
| CC | 昌哥 | 1.64 |
|
||||||
|
| 周周 | 昌哥 | 1.64 |
|
||||||
|
| 球球 | 蔡总 | 1.57 |
|
||||||
|
| 小不点 | 蔡总 | 1.57 |
|
||||||
|
| 苏苏 | 李先生 | 1.53 |
|
||||||
|
| 吱吱 | 李先生 | 1.50 |
|
||||||
|
| 小敌 | 李先生 | 1.50 |
|
||||||
|
| 婉婉 | 刘哥 | 1.46 |
|
||||||
|
| CC | 林总 | 1.39 |
|
||||||
|
| 周周 | 林总 | 1.39 |
|
||||||
|
| 小不点 | T | 1.38 |
|
||||||
|
| 球球 | T | 1.38 |
|
||||||
|
| 悠悠 | 张先生 | 1.38 |
|
||||||
|
| 布丁 | 张先生 | 1.38 |
|
||||||
|
| 小怡 | 周先生 | 1.37 |
|
||||||
|
| 雯雯 | 周先生 | 1.37 |
|
||||||
|
| 素素 | 周先生 | 1.37 |
|
||||||
|
| 嘉嘉 | 轩哥 | 1.31 |
|
||||||
|
| 小柔 | 葛先生 | 1.30 |
|
||||||
|
| 乔西 | 张先生 | 1.29 |
|
||||||
|
| 小不点 | 候 | 1.23 |
|
||||||
|
| 球球 | 候 | 1.23 |
|
||||||
|
| 嘉嘉 | 罗先生 | 1.22 |
|
||||||
|
| 小侯 | T | 1.19 |
|
||||||
|
| 渔渔 | T | 1.19 |
|
||||||
|
| 姜姜 | T | 1.19 |
|
||||||
|
| 小侯 | 黄先生 | 1.19 |
|
||||||
|
| 小敌 | 林先生 | 1.19 |
|
||||||
|
| 姜姜 | 黄先生 | 1.19 |
|
||||||
|
| 吱吱 | 林先生 | 1.19 |
|
||||||
|
| 渔渔 | 黄先生 | 1.19 |
|
||||||
|
| 球球 | 葛先生 | 1.16 |
|
||||||
|
| 小不点 | 葛先生 | 1.16 |
|
||||||
|
| Amy | amy | 1.15 |
|
||||||
|
| 乔西 | T | 1.12 |
|
||||||
|
| 球球 | 老宋 | 1.10 |
|
||||||
|
| 小不点 | 老宋 | 1.10 |
|
||||||
|
| 乔西 | 林先生 | 1.01 |
|
||||||
|
| 素素 | 张先生 | 0.98 |
|
||||||
|
| 小怡 | 张先生 | 0.98 |
|
||||||
|
| 雯雯 | 张先生 | 0.98 |
|
||||||
|
| 佳怡 | T | 0.96 |
|
||||||
|
| 年糕 | 张先生 | 0.94 |
|
||||||
|
| 小侯 | 陈腾鑫 | 0.88 |
|
||||||
|
| 渔渔 | 陈腾鑫 | 0.88 |
|
||||||
|
| 姜姜 | 陈腾鑫 | 0.88 |
|
||||||
|
| 阿清 | 李先生 | 0.85 |
|
||||||
|
| 球球 | 林总 | 0.83 |
|
||||||
|
| 小不点 | 林总 | 0.83 |
|
||||||
|
| 婉婉 | 常总 | 0.77 |
|
||||||
|
| 小侯 | 艾宇民 | 0.76 |
|
||||||
|
| 姜姜 | 艾宇民 | 0.76 |
|
||||||
|
| 渔渔 | 艾宇民 | 0.76 |
|
||||||
|
| 小敌 | 郑先生 | 0.74 |
|
||||||
|
| 吱吱 | 郑先生 | 0.74 |
|
||||||
|
| 千千 | 罗先生 | 0.72 |
|
||||||
|
| 甜甜 | 罗先生 | 0.72 |
|
||||||
|
| 小A | 罗先生 | 0.72 |
|
||||||
|
| 欣欣 | 罗先生 | 0.72 |
|
||||||
|
| 球球 | 小燕 | 0.67 |
|
||||||
|
| 小不点 | 小燕 | 0.67 |
|
||||||
|
| 年糕 | 周先生 | 0.65 |
|
||||||
|
| 卡顿 | 罗先生 | 0.62 |
|
||||||
|
| 小燕 | 罗先生 | 0.62 |
|
||||||
|
| 小敌 | 刘哥 | 0.60 |
|
||||||
|
| 吱吱 | 刘哥 | 0.60 |
|
||||||
|
| 小柔 | 孟紫龙 | 0.56 |
|
||||||
|
| 阿清 | 候 | 0.54 |
|
||||||
|
| 乔西 | 候 | 0.49 |
|
||||||
|
| 小敌 | 张先生 | 0.46 |
|
||||||
|
| 甜甜 | T | 0.46 |
|
||||||
|
| 小A | T | 0.46 |
|
||||||
|
| 欣欣 | T | 0.46 |
|
||||||
|
| 千千 | T | 0.46 |
|
||||||
|
| 吱吱 | 张先生 | 0.46 |
|
||||||
|
| 小A | 游 | 0.38 |
|
||||||
|
| 千千 | 游 | 0.38 |
|
||||||
|
| 甜甜 | 游 | 0.38 |
|
||||||
|
| 欣欣 | 游 | 0.38 |
|
||||||
|
| 苏苏 | 葛先生 | 0.34 |
|
||||||
|
| 渔渔 | 候 | 0.32 |
|
||||||
|
| 小侯 | 候 | 0.32 |
|
||||||
|
| 姜姜 | 候 | 0.32 |
|
||||||
|
| 苏苏 | T | 0.31 |
|
||||||
|
| 婉婉 | 罗先生 | 0.26 |
|
||||||
|
| 涛涛 | 候 | 0.24 |
|
||||||
|
| 苏苏 | 候 | 0.23 |
|
||||||
|
| 阿清 | 常总 | 0.23 |
|
||||||
|
| 小不点 | 李先生 | 0.22 |
|
||||||
|
| 球球 | 李先生 | 0.22 |
|
||||||
|
| 小柔 | T | 0.19 |
|
||||||
|
| 年糕 | 潘先生 | 0.19 |
|
||||||
|
| 婉婉 | 候 | 0.18 |
|
||||||
|
| 小柔 | 罗先生 | 0.17 |
|
||||||
|
| 梦梦 | 葛先生 | 0.14 |
|
||||||
|
| 欣怡 | 葛先生 | 0.14 |
|
||||||
|
| 大姚 | 葛先生 | 0.14 |
|
||||||
|
| 椰子 | 葛先生 | 0.14 |
|
||||||
|
| 璇子 | 林先生 | 0.11 |
|
||||||
|
| 年糕 | 明哥 | 0.09 |
|
||||||
|
| 涛涛 | 张先生 | 0.08 |
|
||||||
|
| 周周 | 大G | 0.02 |
|
||||||
|
| 佳怡 | 大G | 0.02 |
|
||||||
|
| CC | 大G | 0.02 |
|
||||||
|
| 周周 | 明哥 | 0.00 |
|
||||||
|
| Amy | 明哥 | 0.00 |
|
||||||
|
| 小怡 | 叶先生 | 0.00 |
|
||||||
|
| 乔西 | 林先生 | 0.00 |
|
||||||
|
| 素素 | 叶先生 | 0.00 |
|
||||||
|
| 雯雯 | 叶先生 | 0.00 |
|
||||||
|
| 梦梦 | 蔡总 | 0.00 |
|
||||||
|
| 欣怡 | 蔡总 | 0.00 |
|
||||||
|
| 椰子 | 蔡总 | 0.00 |
|
||||||
|
| 大姚 | 蔡总 | 0.00 |
|
||||||
|
| 周周 | 游 | 0.00 |
|
||||||
|
| 小柔 | 昌哥 | 0.00 |
|
||||||
|
| CC | 游 | 0.00 |
|
||||||
|
| 佳怡 | 游 | 0.00 |
|
||||||
|
| CC | 明哥 | 0.00 |
|
||||||
|
| 小柔 | 江先生 | 0.00 |
|
||||||
|
|
||||||
|
共 391 条记录
|
||||||
BIN
etl_billiards/docs/index_tables_output.txt
Normal file
BIN
etl_billiards/docs/index_tables_output.txt
Normal file
Binary file not shown.
167
etl_billiards/docs/补充更多信息.md
Normal file
167
etl_billiards/docs/补充更多信息.md
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
# 补充更多信息:
|
||||||
|
## DWD数据库更新
|
||||||
|
DWD的数据库,若干表中,新增了若干表,可能会对整个DWS层设计有影响/优化,重新思考可用的字段。
|
||||||
|
|
||||||
|
|
||||||
|
## 支出/成本数据缺失
|
||||||
|
财务页需要房租、水电、物业、工资、报销、平台服务费等现金支出与“支出结构”,DWD 里只有商品成本 dwd_store_goods_sale.cost_money,但价格也不对。缺少费用/薪酬/平台服务费等表,导致“现金支出/现金结余/结余率/支出结构”无法落地。
|
||||||
|
### 更新:
|
||||||
|
- 这些内容先在数据库结构中预留,后期会通过Excel等方式手动导入。
|
||||||
|
|
||||||
|
## 平台回款与团购差价口径不足
|
||||||
|
需求有“平台回款”“团购差价”,DWD 只有团购核销/验券记录(dwd_groupbuy_redemption/dwd_platform_coupon_redemption),没有平台结算/回款/佣金/服务费明细,无法算“平台回款”与“平台服务费”。
|
||||||
|
### 更新:
|
||||||
|
- 确认的平台服务费与回款金额先在数据库结构中预留,后期会通过Excel等方式手动导入。
|
||||||
|
|
||||||
|
|
||||||
|
## 优惠分类无法分拆
|
||||||
|
财务页要区分“团购优惠/大客户优惠/赠送卡抵扣/其他优惠”,DWD 仅有 member_discount_amount / coupon_amount / adjust_amount / rounding_amount / gift_card_amount / recharge_card_amount 等汇总字段,且没有“大客户”标识或优惠原因维表,无法稳定拆分口径。
|
||||||
|
### 更新:
|
||||||
|
- 赠送卡抵扣 指的就是 酒水卡+台费卡+活动抵用券 结账 抵扣的。
|
||||||
|
- 团购优惠: ledger_amount + assistant_promotion_money - ledger_unit_price
|
||||||
|
- 大客户优惠和其他优惠:就是手动调账产生的优惠(订单中的折扣、台桌折扣、商品折扣、手动优惠这几项关系需要确认下,找100个样本进行分析)。
|
||||||
|
|
||||||
|
## “发生额/正价”口径不清
|
||||||
|
- 结账记录中的正价: tableChargeMoney(台费正价)goodsMoney(商品正价)assistantPdMoney(助教基础课正价)assistantCxMoney(助教激励课正价)
|
||||||
|
- 团购中的正价:ledger_amount(台桌正价) + assistant_promotion_money(助教正价)
|
||||||
|
- 团购中的核销价:ledger_unit_price
|
||||||
|
|
||||||
|
## 区域/房型维度不规范
|
||||||
|
筛选要“大厅A/B/C、麻将房、团建房/包厢”,DWD 只有 site_table_area_name 等自由文本,没有规范维表映射,容易导致前端筛选不可控。
|
||||||
|
|
||||||
|
### 更新
|
||||||
|
BD_manual_dim_table.md 中,有台区分布的对应关系
|
||||||
|
|
||||||
|
|
||||||
|
## 充值与赠送卡口径缺口
|
||||||
|
需求中“储值卡充值实收(首充/续费、不含赠送)”与“赠送卡新增/消费/余额”细分酒水卡/台费卡/抵用券。DWD 里 dwd_recharge_order 没有明确“赠送金额”字段;dim_member_card_account / dwd_member_balance_change 仅有卡类型名称,缺少“是否赠送”“卡类别标准枚举”,需要补充规则/维表。
|
||||||
|
|
||||||
|
### 更新
|
||||||
|
- 酒水卡,台费卡活动抵用券,台费卡 是赠送卡 分类在dim_member_card_account 的card_type_id,对应的数据库说明书中有介绍。
|
||||||
|
- 储值卡是充值的“现金卡”
|
||||||
|
|
||||||
|
|
||||||
|
## 助教薪酬规则未闭合
|
||||||
|
DWS 需求里“充值提成”空缺,且“冲刺奖/额外奖金”重复;没有助教工资/结算流水表,财务页“助教分成/奖惩”无法核算。
|
||||||
|
|
||||||
|
### 更新
|
||||||
|
- 充值提成数据库结构中预留,后期会通过Excel等方式手动导入。会记录时间,充值金额,储值卡卡关联,充值提成金额。
|
||||||
|
- “冲刺奖/额外奖金”重复:按照薪资说明进行相应调整。
|
||||||
|
- 没有助教工资/结算流水表:为我增加相应的表。满足业务逻辑。
|
||||||
|
|
||||||
|
## 时间分层与筛选不匹配
|
||||||
|
### 更新
|
||||||
|
- UI 需要“最近半年不含本月、上季度”等时间维度,并且满足上葛周期的环比。DWS 分层仅到 3 个月,可能导致查询性能或需要额外聚合层。财务方面需要特殊处理。
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## 缺失 DDL:
|
||||||
|
方案里列出的表没有全部给出结构定义,包括 cfg_tier_effective_period、dws_assistant_salary_calc、dws_member_visit_detail、dws_finance_discount_detail、dws_finance_recharge_summary、dws_finance_expense_summary。这些在 DWS_任务计划_v1.md 中仅出现在清单里,但没有 DDL,会导致实施阶段卡住。
|
||||||
|
|
||||||
|
### 更新
|
||||||
|
- 补全DLL。
|
||||||
|
|
||||||
|
|
||||||
|
## SCD2 维度取数口径
|
||||||
|
助教等级在 dws_assistant_monthly_summary 用了 SCD2_is_current=1,这是否会把“当前等级”套到历史月份,能否满足需求中的“历史月份”统计?是否要加一些数据筛选条件?是否需按业务时间点做 as-of join(基于有效期)?
|
||||||
|
|
||||||
|
|
||||||
|
## 附加课/基础课口径
|
||||||
|
方案中用 skill_name 判断“超休/激励/打赏”为附加课,但我希望换成skill_id进行枚举,避免漏记或误记;落在库中可以使用名称。
|
||||||
|
|
||||||
|
## 财务指标可追溯口径
|
||||||
|
dws_finance_daily_summary 已覆盖“发生额/优惠/确认收入/现金流/充值”等字段,但缺少“数据来源矩阵”(字段→DWD表→公式)。财务需求对“发生额(正价)”和“优惠”拆分非常细,需明确“正价”来源(台费价、助教等级价、商品原价)与“优惠”拆分口径(团购差价、大客户折扣、赠送卡抵扣、免单/抹零、手动调整)。
|
||||||
|
|
||||||
|
### 更新
|
||||||
|
- 增加 数据来源矩阵,记录数据的来龙去脉
|
||||||
|
|
||||||
|
|
||||||
|
我觉得还不够全,给你一些我整理的内容。
|
||||||
|
|
||||||
|
# 1.2 DWD 核心表与关键字段
|
||||||
|
还差好多,举例:
|
||||||
|
|
||||||
|
## 助教服务相关:
|
||||||
|
dwd_assistant_service_log:
|
||||||
|
| `order_assistant_type` | 服务类型 | 1=基础课或包厢课, 2=附加课/激励课 | 这个不重要,用skill_id判断就好。
|
||||||
|
另外,服务时keh长,服务的助教ID与花名,客户关联,台桌号,台桌分类关联等也很重要。
|
||||||
|
|
||||||
|
## 客户相关:
|
||||||
|
客户姓名手机号生日以及关联的会员卡。
|
||||||
|
|
||||||
|
## 财务:
|
||||||
|
还有从结账记录出发关联的台桌流水助教流水
|
||||||
|
结算路径
|
||||||
|
充值流水等。
|
||||||
|
|
||||||
|
|
||||||
|
以上是否要补充?
|
||||||
|
---------------
|
||||||
|
## 订单获取的字段更新
|
||||||
|
### 订单各项正价小计
|
||||||
|
- 台费正价:table_charge_money
|
||||||
|
- 商品正价:goods_money
|
||||||
|
- 助教基础课/陪打正价:assistant_pd_money
|
||||||
|
- 助教激励课/超休正价:assistant_cx_money
|
||||||
|
|
||||||
|
### 支付信息
|
||||||
|
- 会员卡支付金额:recharge_card_amount。(卡类型还要从dwd_settlement_head的order_settle_id 去dwd_member_balance_change表,找到卡的类型。)
|
||||||
|
- 收银实付:pay_amount。
|
||||||
|
- 团购抵消的台费:coupon_amount。
|
||||||
|
- 团购支付的金额:2条路径,若pl_coupon_sale_amount非0 ,则使用pl_coupon_sale_amount。若pl_coupon_sale_amount为0且coupon_amount不为0,那么需要到dwd_groupbuy_redemption找到对应的订单的ledger_unit_price。
|
||||||
|
|
||||||
|
### 订单优惠与打折
|
||||||
|
- 台费打折:adjust_amount
|
||||||
|
- 团购券优惠:团购抵消的台费 - 团购支付的金额
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-----------------
|
||||||
|
单独任务:
|
||||||
|
大客户优惠;抹零;其他优惠 需要抽样分析,当作一个单独任务为我分析执行。
|
||||||
|
| **会员折扣** | dwd_settlement_head | `member_discount_amount` | 会员身份折扣 | 这个貌似没有启用过,也为我作为单独任务分析处理吧。。
|
||||||
|
|
||||||
|
---------------
|
||||||
|
|
||||||
|
|
||||||
|
时间分层机制:需求明确“四层时间分层(近2天/近1月/近3月/全量)”,方案只写了更新频率,需补齐具体实现(分区策略/分层表或物化汇总层/定期归档与清理作业)。
|
||||||
|
|
||||||
|
DDL 完整性:补充说明中提到缺失的表(如 cfg_tier_effective_period、dws_assistant_salary_calc、dws_member_visit_detail、dws_finance_discount_detail、dws_finance_recharge_summary、dws_finance_expense_summary)需要在 schema_dws.sql 里落全;方案里写了“更新DDL”,但应明确完整DDL清单与字段级定义。
|
||||||
|
|
||||||
|
薪酬规则与生效期:档位、奖金、规则有“按月/按时间生效”的要求,方案目前只有 cfg_performance_tier/cfg_bonus_rules,需要补充生效期字段或独立“规则生效期配置表”,否则历史月份口径会错。
|
||||||
|
|
||||||
|
SCD2 / as-of 口径:助教等级是SCD2维度,历史月份不能直接用“当前等级”。方案需明确“按有效期 as-of join”的取数规则。
|
||||||
|
|
||||||
|
技能枚举规范:需求要求用 skill_id 判断基础课/附加课;方案应明确 skill_id→课程类型映射(可用配置表),避免 skill_name 漏记。
|
||||||
|
|
||||||
|
滚动区间统计:需求中明确 7/10/15/30/60/90 天窗口,方案未明确存储方式(建议在 dws_assistant_customer_stats、dws_member_consumption_summary 中直接落多窗口字段,或新增滚动汇总表)。
|
||||||
|
|
||||||
|
财务口径矩阵需全覆盖:方案已有“数据来源矩阵”,但需扩展至财务页面每一项指标(发生额/优惠拆分/确认收入/现金流/充值/平台回款/支出结构),确保每一项都有明确字段+公式+来源表。
|
||||||
|
手工导入表规范:支出/平台回款/充值提成的Excel导入要补“字段定义、时间粒度、门店维度、去重与校验规则”,否则实现阶段会反复返工。
|
||||||
|
|
||||||
|
区域/房型维表:方案已有 cfg_area_category,但需落地“具体映射规则 + 默认兜底 + 异常值处理”,并与 BD_manual_dim_table.md 一致。
|
||||||
|
|
||||||
|
# 更新
|
||||||
|
|
||||||
|
时间口径定义:本周/上周/本季度/上季度/最近半年不含本月 等窗口的“起止边界”为月第一天0点。周起始日为周一。
|
||||||
|
|
||||||
|
环比规则:开启对比时,是“对比上一个等长区间”相比。
|
||||||
|
|
||||||
|
有效业绩的排除规则:仅对“助教废除表”的记录进行处理排除。其影响绩效。
|
||||||
|
|
||||||
|
新入职定档规则:月1日0点之后入住的,计算为新入职。入职日以助教表入职时间为准。
|
||||||
|
|
||||||
|
Top3 奖金排名口径:按绩效总小时数。如遇并列则都算,比如2个第一,则记为2个第一,一个第三。
|
||||||
|
|
||||||
|
充值提成规则:比例/阶梯/时间口径缺失:通过手动导入表格,表格中会明确月份,提成关联充值订单金额和助教获得的提成金额。
|
||||||
|
|
||||||
|
大客户优惠/其他优惠划分规则:目前需要抽样分析。
|
||||||
|
平台回款/服务费口径:明确导入数据字段包含:回款金额、佣金、服务费、回款日期、平台类型、订单关联键。
|
||||||
|
|
||||||
|
散客处理:member_id=0 的客户是散客。不进入客户维度统计。
|
||||||
|
|
||||||
|
门店/租户范围:现在只有一个门店,一个租户。
|
||||||
198
etl_billiards/docs/财务页面需求.md
Normal file
198
etl_billiards/docs/财务页面需求.md
Normal file
@@ -0,0 +1,198 @@
|
|||||||
|
# 筛选
|
||||||
|
- 按时间范围 本月/上个月/前3个月不加本月/前3个月+本月/最近半年不加本月/本季度含本月/上个季度/本周/上周
|
||||||
|
- 按区域筛选 大厅(A区/B区/C区) /麻将房/团建房
|
||||||
|
|
||||||
|
# 新增功能
|
||||||
|
- 一个开关,打开后,可以与紧邻前一个等长区间进行对比(用上下箭头表示增/跌,并跟随百分比。)
|
||||||
|
- 对比数值的UI需要设计,关闭状态和开启状态。
|
||||||
|
- 问号icon,点击会有相应的弹窗显示内容。将弹出放在页面底部,存在关闭按钮,且默认5秒后自动消失。不影响滚动等操
|
||||||
|
|
||||||
|
# 数据展示调整
|
||||||
|
## 黑色banner 经营状况一览
|
||||||
|
### 行1:收入概览 即 经营链:
|
||||||
|
- 发生额/正价。 点击提示icon:
|
||||||
|
"
|
||||||
|
按台桌/包厢/助教/酒水的“正价”计算出的理论销售额,反映经营规模与业务量。
|
||||||
|
|
||||||
|
计算方式 = 各收入项目按正价 × 数量/时长汇总计算。
|
||||||
|
|
||||||
|
**不是最终收到了多少钱。**
|
||||||
|
"
|
||||||
|
|
||||||
|
- 总优惠 | 优惠比例。点击提示icon:
|
||||||
|
"
|
||||||
|
本期因团购差价、大客户折扣、赠送卡抵扣、免单/抹零等导致的让利总额,用于解释“发生额”与“成交/确认收入”的差异。
|
||||||
|
|
||||||
|
计算方式 = 发生额 − 成交/确认收入
|
||||||
|
或 = 团购优惠 + 大客户优惠 + 赠送抵扣 + 其他优惠/免单/抹零(汇总)
|
||||||
|
"
|
||||||
|
|
||||||
|
- 成交/确认收入。点击提示icon:
|
||||||
|
"
|
||||||
|
扣除各种优惠后的成交金额,**按记账规则统计的营业收入**。
|
||||||
|
|
||||||
|
计算方式 = 发生额 − 团购优惠 − 大客户优惠 − 赠送抵扣(及其他优惠)。
|
||||||
|
|
||||||
|
**不含充值营业收入** 充值是预收/负债,但会影响现金流。**
|
||||||
|
"
|
||||||
|
|
||||||
|
|
||||||
|
### 行2:现金概览 注:往期为已结算,本期为预估:
|
||||||
|
- 实收/现金流入
|
||||||
|
"
|
||||||
|
统计真实进账的资金,包括现金 + 线上支付 + 平台回款。
|
||||||
|
|
||||||
|
计算方式 = 消费实收 + 平台团购 - 各类退款/冲正。
|
||||||
|
|
||||||
|
**此为现金口径,不等于营业收入。**区别为:充值属于预收款的现金流入,属于预存行为,球房债务。
|
||||||
|
"
|
||||||
|
|
||||||
|
- 现金支出。点击提示icon:
|
||||||
|
"
|
||||||
|
本期所有支出项目的合计。
|
||||||
|
|
||||||
|
计算方式 = 房租 + 水电 + 进货成本支出 + 耗材 + 报销 + 助教分成 + 固定人员工资 + 平台服务费 + 其他费用
|
||||||
|
"
|
||||||
|
|
||||||
|
|
||||||
|
- 现金结余 | 结余率。点击提示icon:
|
||||||
|
"
|
||||||
|
本期营业收入扣除全部成本后的利润,用于衡量经营质量。
|
||||||
|
|
||||||
|
计算方式= 实收/现金流入 − 总支出。
|
||||||
|
"
|
||||||
|
|
||||||
|
|
||||||
|
## AI分析
|
||||||
|
以下内容先占位,真实内容会通过AI接口调用展示,此处为标准Markdown内容排版。
|
||||||
|
优惠率Top:团购(%) / 大客户(%) / 赠送卡(%)
|
||||||
|
差异最大项目:酒水 / 台桌 / 包厢 ...
|
||||||
|
财务分析:充值高但消耗低(或相反)提示
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## 充值与预收
|
||||||
|
### 行1 会员卡概览
|
||||||
|
- 储值卡充值实收 首充 | 续费 | 合计。点击提示icon:
|
||||||
|
"
|
||||||
|
本期储值卡充值到账的新增金额。
|
||||||
|
按照首充,续费,合计路径进行统计。
|
||||||
|
|
||||||
|
计算方式 = 本期储值卡充值订单的实收金额。
|
||||||
|
不含赠送金额
|
||||||
|
"
|
||||||
|
|
||||||
|
- 全类别会员卡余额合计 **仅经营参考,非财务属性**。点击提示icon:
|
||||||
|
"
|
||||||
|
截至本期末,顾客充值后尚未消费的储值余额,包括赠送的台费卡酒水卡等类别,用于判断未来可转化的消费规模。
|
||||||
|
|
||||||
|
计算方式 = 各类会员卡往期余额 + 本期充值到账与赠送到账 − 本期卡消耗 ± 调整(退款/冲正/手工修正)
|
||||||
|
"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### 行2 储值卡统计详情
|
||||||
|
- 储值卡充值。点击提示icon:
|
||||||
|
"
|
||||||
|
本期储值卡充值到账的新增金额。
|
||||||
|
"
|
||||||
|
|
||||||
|
- 储值卡消耗。点击提示icon:
|
||||||
|
"
|
||||||
|
余额卡在查询周期内消耗金额。
|
||||||
|
|
||||||
|
计算方式 = 本期消耗 ± 调整
|
||||||
|
"
|
||||||
|
|
||||||
|
- 储值卡总余额。点击提示icon:
|
||||||
|
"
|
||||||
|
截至本期末,余额卡可用的余额。
|
||||||
|
|
||||||
|
计算方式 = 期初余额卡余额 + 本期新增 − 本期消耗 ± 调整
|
||||||
|
"
|
||||||
|
|
||||||
|
|
||||||
|
### 行3 赠送卡统计详情
|
||||||
|
需要设计下页面,主要字段是合计,且细分的也要展示。
|
||||||
|
- 赠送卡新增合计;细分 酒水卡|台费卡|抵用券。点击提示icon:
|
||||||
|
"
|
||||||
|
本期各类型赠送卡的新增金额。
|
||||||
|
"
|
||||||
|
|
||||||
|
- 赠送卡消费合计;细分酒水卡|台费卡|抵用券。点击提示icon:
|
||||||
|
"
|
||||||
|
本期各类型赠送卡在查询周期内消耗金额。
|
||||||
|
|
||||||
|
计算方式 = 本期消耗 ± 调整
|
||||||
|
"
|
||||||
|
|
||||||
|
- 赠送卡总余额合计;细分酒水卡|台费卡|抵用券。点击提示icon:
|
||||||
|
"
|
||||||
|
截至本期末,各类型赠送卡可用的余额。
|
||||||
|
|
||||||
|
计算方式 = 期初余额 + 本期新增 − 本期消耗 ± 调整
|
||||||
|
"
|
||||||
|
|
||||||
|
|
||||||
|
## 发生额 → 入账收入 及 优惠影响
|
||||||
|
页面字段结构:
|
||||||
|
### 收入确认(损益链)
|
||||||
|
发生额(正价) ¥123,456
|
||||||
|
├─ 团购优惠 -¥ 6,200
|
||||||
|
├─ 手动调整 + 大客户优惠 -¥ 4,800
|
||||||
|
├─ 赠送卡抵扣(台桌卡+酒水卡+抵用券) -¥ 2,336
|
||||||
|
└─ 其他优惠 免单+抹零 -¥ 0
|
||||||
|
成交/确认收入 ¥110,120
|
||||||
|
支付方式构成
|
||||||
|
├─ 由储值卡结算冲销 ¥60,120
|
||||||
|
├─ 现金/线上支付 ¥60,120
|
||||||
|
└─ 团购核销确认收入(团购成交价) ¥60,120
|
||||||
|
|
||||||
|
|
||||||
|
现金流
|
||||||
|
消费现金流入:现金+线上+平台回款−退款 ¥60,120
|
||||||
|
充值到账(首充/续费) ¥60,120
|
||||||
|
现金流入合计 ¥60,120
|
||||||
|
|
||||||
|
### 收入结构
|
||||||
|
收入结构(发生额 | 优惠 | 入账 )
|
||||||
|
开台与包厢 ¥xx,xxx | -¥x,xxx | ¥xx,xxx
|
||||||
|
├─ A区 ¥xx,xxx | -¥x,xxx | ¥xx,xxx
|
||||||
|
├─ B区 ¥xx,xxx | -¥x,xxx | ¥xx,xxx
|
||||||
|
├─ C区 ¥xx,xxx | -¥x,xxx | ¥xx,xxx
|
||||||
|
├─ 团建区 ¥xx,xxx | -¥x,xxx | ¥xx,xxx
|
||||||
|
└─ 麻将区 ¥xx,xxx | -¥x,xxx | ¥xx,xxx
|
||||||
|
|
||||||
|
助教(基础课) ¥xx,xxx | -¥ | ¥xx,xxx
|
||||||
|
助教(激励课) ¥xx,xxx | -¥ | ¥xx,xxx
|
||||||
|
食品酒水 ¥xx,xxx | -¥x,xxx | ¥xx,xxx
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## 支出结构
|
||||||
|
助教分成:基础¥x,xxx 附加¥x,xxx 充值提成¥x,xxx
|
||||||
|
助教额外奖金:¥x,xxx
|
||||||
|
食品饮料进货:¥x,xxx 耗材¥x,xxx 报销¥x,xxx
|
||||||
|
房租¥x,xxx 水电¥x,xxx 物业¥x,xxx
|
||||||
|
固定人员工资¥x,xxx
|
||||||
|
|
||||||
|
汇来米平台服务费¥x,xxx
|
||||||
|
美团服务费¥x,xxx 抖音服务费¥x,xxx
|
||||||
|
|
||||||
|
支出合计 ¥ xx,xxx
|
||||||
|
|
||||||
|
## 助教收支分析
|
||||||
|
助教基础课 客户支付 | 球房抽成 | 球房均小时抽成
|
||||||
|
├─ 初级 客户支付 | 球房抽成 | 球房均小时抽成
|
||||||
|
├─ 中级 客户支付 | 球房抽成 | 球房均小时抽成
|
||||||
|
├─ 高级 客户支付 | 球房抽成 | 球房均小时抽成
|
||||||
|
└─ 星级 客户支付 | 球房抽成 | 球房均小时抽成
|
||||||
|
|
||||||
|
助教激励课 客户支付 | 球房抽成 | 球房均小时抽成
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -26,7 +26,7 @@ PROJECT_ROOT = Path(__file__).resolve().parents[1]
|
|||||||
if str(PROJECT_ROOT) not in sys.path:
|
if str(PROJECT_ROOT) not in sys.path:
|
||||||
sys.path.insert(0, str(PROJECT_ROOT))
|
sys.path.insert(0, str(PROJECT_ROOT))
|
||||||
|
|
||||||
from api.client import APIClient
|
from api.recording_client import build_recording_client
|
||||||
from api.endpoint_routing import derive_former_endpoint as derive_former_endpoint_shared
|
from api.endpoint_routing import derive_former_endpoint as derive_former_endpoint_shared
|
||||||
from config.settings import AppConfig
|
from config.settings import AppConfig
|
||||||
from models.parsers import TypeParser
|
from models.parsers import TypeParser
|
||||||
@@ -265,13 +265,7 @@ def main() -> int:
|
|||||||
if not cfg["api"].get("token"):
|
if not cfg["api"].get("token"):
|
||||||
raise SystemExit("缺少 api.token(请在 .env 配置 API_TOKEN 或 FICOO_TOKEN)")
|
raise SystemExit("缺少 api.token(请在 .env 配置 API_TOKEN 或 FICOO_TOKEN)")
|
||||||
|
|
||||||
client = APIClient(
|
client = build_recording_client(cfg, task_code="FETCH_TEST_COMPARE")
|
||||||
base_url=cfg["api"]["base_url"],
|
|
||||||
token=cfg["api"]["token"],
|
|
||||||
timeout=int(cfg["api"].get("timeout_sec") or 20),
|
|
||||||
retry_max=int(cfg["api"].get("retries", {}).get("max_attempts") or 3),
|
|
||||||
headers_extra=cfg["api"].get("headers_extra") or {},
|
|
||||||
)
|
|
||||||
|
|
||||||
common_params = cfg["api"].get("params", {}) or {}
|
common_params = cfg["api"].get("params", {}) or {}
|
||||||
if not isinstance(common_params, dict):
|
if not isinstance(common_params, dict):
|
||||||
|
|||||||
@@ -5,6 +5,11 @@ from .task_model import TaskItem, TaskStatus, TaskHistory, TaskConfig, QueuedTas
|
|||||||
from .schedule_model import (
|
from .schedule_model import (
|
||||||
ScheduledTask, ScheduleConfig, ScheduleType, IntervalUnit, ScheduleStore
|
ScheduledTask, ScheduleConfig, ScheduleType, IntervalUnit, ScheduleStore
|
||||||
)
|
)
|
||||||
|
from .task_registry import (
|
||||||
|
TaskRegistry, TaskDefinition, BusinessDomain, DOMAIN_LABELS,
|
||||||
|
task_registry, get_ods_task_codes, get_fact_ods_task_codes,
|
||||||
|
get_dimension_ods_task_codes, get_all_task_tuples
|
||||||
|
)
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"TaskItem",
|
"TaskItem",
|
||||||
@@ -17,4 +22,14 @@ __all__ = [
|
|||||||
"ScheduleType",
|
"ScheduleType",
|
||||||
"IntervalUnit",
|
"IntervalUnit",
|
||||||
"ScheduleStore",
|
"ScheduleStore",
|
||||||
|
# 任务注册表
|
||||||
|
"TaskRegistry",
|
||||||
|
"TaskDefinition",
|
||||||
|
"BusinessDomain",
|
||||||
|
"DOMAIN_LABELS",
|
||||||
|
"task_registry",
|
||||||
|
"get_ods_task_codes",
|
||||||
|
"get_fact_ods_task_codes",
|
||||||
|
"get_dimension_ods_task_codes",
|
||||||
|
"get_all_task_tuples",
|
||||||
]
|
]
|
||||||
|
|||||||
353
etl_billiards/gui/models/task_registry.py
Normal file
353
etl_billiards/gui/models/task_registry.py
Normal file
@@ -0,0 +1,353 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""任务注册表:定义所有可用任务及其业务域分组。
|
||||||
|
|
||||||
|
从后端 ods_tasks 动态获取任务定义,并按业务域分组,供 UI 使用。
|
||||||
|
"""
|
||||||
|
|
||||||
|
from dataclasses import dataclass, field
|
||||||
|
from enum import Enum
|
||||||
|
from typing import Dict, List, Optional, Tuple
|
||||||
|
|
||||||
|
# 尝试从后端导入 ODS 任务定义
|
||||||
|
try:
|
||||||
|
from tasks.ods_tasks import ENABLED_ODS_CODES, ODS_TASK_SPECS
|
||||||
|
_HAS_BACKEND = True
|
||||||
|
except ImportError:
|
||||||
|
_HAS_BACKEND = False
|
||||||
|
ENABLED_ODS_CODES = set()
|
||||||
|
ODS_TASK_SPECS = ()
|
||||||
|
|
||||||
|
|
||||||
|
class BusinessDomain(Enum):
|
||||||
|
"""业务域枚举"""
|
||||||
|
MEMBER = "member" # 会员
|
||||||
|
SETTLEMENT = "settlement" # 结算/支付
|
||||||
|
ASSISTANT = "assistant" # 助教
|
||||||
|
GOODS = "goods" # 商品/销售
|
||||||
|
TABLE = "table" # 台桌
|
||||||
|
PROMOTION = "promotion" # 团购/优惠券
|
||||||
|
INVENTORY = "inventory" # 库存
|
||||||
|
SCHEMA = "schema" # Schema 初始化
|
||||||
|
DWD = "dwd" # DWD 装载
|
||||||
|
QUALITY = "quality" # 质量检查
|
||||||
|
OTHER = "other" # 其他
|
||||||
|
|
||||||
|
|
||||||
|
# 业务域显示名称
|
||||||
|
DOMAIN_LABELS: Dict[BusinessDomain, str] = {
|
||||||
|
BusinessDomain.MEMBER: "会员",
|
||||||
|
BusinessDomain.SETTLEMENT: "结算/支付",
|
||||||
|
BusinessDomain.ASSISTANT: "助教",
|
||||||
|
BusinessDomain.GOODS: "商品/销售",
|
||||||
|
BusinessDomain.TABLE: "台桌",
|
||||||
|
BusinessDomain.PROMOTION: "团购/优惠券",
|
||||||
|
BusinessDomain.INVENTORY: "库存",
|
||||||
|
BusinessDomain.SCHEMA: "Schema 初始化",
|
||||||
|
BusinessDomain.DWD: "DWD 装载",
|
||||||
|
BusinessDomain.QUALITY: "质量检查",
|
||||||
|
BusinessDomain.OTHER: "其他",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class TaskDefinition:
|
||||||
|
"""任务定义"""
|
||||||
|
code: str # 任务编码
|
||||||
|
name: str # 显示名称
|
||||||
|
description: str # 描述
|
||||||
|
domain: BusinessDomain # 业务域
|
||||||
|
requires_window: bool = True # 是否需要时间窗口
|
||||||
|
is_ods: bool = False # 是否为 ODS 任务
|
||||||
|
is_dimension: bool = False # 是否为维度类任务(校验时区分)
|
||||||
|
default_enabled: bool = True # 默认是否选中
|
||||||
|
|
||||||
|
|
||||||
|
# ODS 任务到业务域的映射
|
||||||
|
ODS_DOMAIN_MAP: Dict[str, BusinessDomain] = {
|
||||||
|
# 会员相关
|
||||||
|
"ODS_MEMBER": BusinessDomain.MEMBER,
|
||||||
|
"ODS_MEMBER_CARD": BusinessDomain.MEMBER,
|
||||||
|
"ODS_MEMBER_BALANCE": BusinessDomain.MEMBER,
|
||||||
|
# 结算/支付相关
|
||||||
|
"ODS_PAYMENT": BusinessDomain.SETTLEMENT,
|
||||||
|
"ODS_REFUND": BusinessDomain.SETTLEMENT,
|
||||||
|
"ODS_SETTLEMENT_RECORDS": BusinessDomain.SETTLEMENT,
|
||||||
|
"ODS_RECHARGE_SETTLE": BusinessDomain.SETTLEMENT,
|
||||||
|
"ODS_SETTLEMENT_TICKET": BusinessDomain.SETTLEMENT,
|
||||||
|
# 助教相关
|
||||||
|
"ODS_ASSISTANT_ACCOUNT": BusinessDomain.ASSISTANT,
|
||||||
|
"ODS_ASSISTANT_LEDGER": BusinessDomain.ASSISTANT,
|
||||||
|
"ODS_ASSISTANT_ABOLISH": BusinessDomain.ASSISTANT,
|
||||||
|
# 商品/销售相关
|
||||||
|
"ODS_TENANT_GOODS": BusinessDomain.GOODS,
|
||||||
|
"ODS_STORE_GOODS": BusinessDomain.GOODS,
|
||||||
|
"ODS_STORE_GOODS_SALES": BusinessDomain.GOODS,
|
||||||
|
"ODS_GOODS_CATEGORY": BusinessDomain.GOODS,
|
||||||
|
# 台桌相关
|
||||||
|
"ODS_TABLES": BusinessDomain.TABLE,
|
||||||
|
"ODS_TABLE_USE": BusinessDomain.TABLE,
|
||||||
|
"ODS_TABLE_FEE_DISCOUNT": BusinessDomain.TABLE,
|
||||||
|
# 团购/优惠券相关
|
||||||
|
"ODS_GROUP_PACKAGE": BusinessDomain.PROMOTION,
|
||||||
|
"ODS_GROUP_BUY_REDEMPTION": BusinessDomain.PROMOTION,
|
||||||
|
"ODS_PLATFORM_COUPON": BusinessDomain.PROMOTION,
|
||||||
|
# 库存相关
|
||||||
|
"ODS_INVENTORY_STOCK": BusinessDomain.INVENTORY,
|
||||||
|
"ODS_INVENTORY_CHANGE": BusinessDomain.INVENTORY,
|
||||||
|
}
|
||||||
|
|
||||||
|
# ODS 任务显示名称(中文)
|
||||||
|
ODS_DISPLAY_NAMES: Dict[str, str] = {
|
||||||
|
"ODS_MEMBER": "会员档案",
|
||||||
|
"ODS_MEMBER_CARD": "会员储值卡",
|
||||||
|
"ODS_MEMBER_BALANCE": "会员余额变动",
|
||||||
|
"ODS_PAYMENT": "支付流水",
|
||||||
|
"ODS_REFUND": "退款流水",
|
||||||
|
"ODS_SETTLEMENT_RECORDS": "结账记录",
|
||||||
|
"ODS_RECHARGE_SETTLE": "充值结算",
|
||||||
|
"ODS_SETTLEMENT_TICKET": "结账小票",
|
||||||
|
"ODS_ASSISTANT_ACCOUNT": "助教账号",
|
||||||
|
"ODS_ASSISTANT_LEDGER": "助教流水",
|
||||||
|
"ODS_ASSISTANT_ABOLISH": "助教作废",
|
||||||
|
"ODS_TENANT_GOODS": "租户商品",
|
||||||
|
"ODS_STORE_GOODS": "门店商品",
|
||||||
|
"ODS_STORE_GOODS_SALES": "商品销售流水",
|
||||||
|
"ODS_GOODS_CATEGORY": "商品分类",
|
||||||
|
"ODS_TABLES": "台桌维表",
|
||||||
|
"ODS_TABLE_USE": "台费计费流水",
|
||||||
|
"ODS_TABLE_FEE_DISCOUNT": "台费折扣调账",
|
||||||
|
"ODS_GROUP_PACKAGE": "团购套餐",
|
||||||
|
"ODS_GROUP_BUY_REDEMPTION": "团购核销",
|
||||||
|
"ODS_PLATFORM_COUPON": "平台券核销",
|
||||||
|
"ODS_INVENTORY_STOCK": "库存汇总",
|
||||||
|
"ODS_INVENTORY_CHANGE": "库存变化",
|
||||||
|
}
|
||||||
|
|
||||||
|
# 维度类 ODS 任务(校验时通常单独处理)
|
||||||
|
DIMENSION_ODS_CODES = {
|
||||||
|
"ODS_MEMBER",
|
||||||
|
"ODS_MEMBER_CARD",
|
||||||
|
"ODS_ASSISTANT_ACCOUNT",
|
||||||
|
"ODS_TENANT_GOODS",
|
||||||
|
"ODS_STORE_GOODS",
|
||||||
|
"ODS_GOODS_CATEGORY",
|
||||||
|
"ODS_TABLES",
|
||||||
|
"ODS_GROUP_PACKAGE",
|
||||||
|
}
|
||||||
|
|
||||||
|
# 事实类 ODS 任务(需要时间窗口)
|
||||||
|
FACT_ODS_CODES = {
|
||||||
|
"ODS_MEMBER_BALANCE",
|
||||||
|
"ODS_PAYMENT",
|
||||||
|
"ODS_REFUND",
|
||||||
|
"ODS_SETTLEMENT_RECORDS",
|
||||||
|
"ODS_RECHARGE_SETTLE",
|
||||||
|
"ODS_SETTLEMENT_TICKET",
|
||||||
|
"ODS_ASSISTANT_LEDGER",
|
||||||
|
"ODS_ASSISTANT_ABOLISH",
|
||||||
|
"ODS_STORE_GOODS_SALES",
|
||||||
|
"ODS_TABLE_USE",
|
||||||
|
"ODS_TABLE_FEE_DISCOUNT",
|
||||||
|
"ODS_GROUP_BUY_REDEMPTION",
|
||||||
|
"ODS_PLATFORM_COUPON",
|
||||||
|
"ODS_INVENTORY_CHANGE",
|
||||||
|
}
|
||||||
|
|
||||||
|
# 非 ODS 任务定义
|
||||||
|
NON_ODS_TASKS: List[TaskDefinition] = [
|
||||||
|
# DWD 装载
|
||||||
|
TaskDefinition(
|
||||||
|
code="DWD_LOAD_FROM_ODS",
|
||||||
|
name="ODS→DWD 装载",
|
||||||
|
description="从 ODS 增量装载到 DWD",
|
||||||
|
domain=BusinessDomain.DWD,
|
||||||
|
requires_window=True,
|
||||||
|
),
|
||||||
|
TaskDefinition(
|
||||||
|
code="DWD_QUALITY_CHECK",
|
||||||
|
name="DWD 质量检查",
|
||||||
|
description="执行 DWD 数据质量检查",
|
||||||
|
domain=BusinessDomain.QUALITY,
|
||||||
|
requires_window=False,
|
||||||
|
),
|
||||||
|
TaskDefinition(
|
||||||
|
code="DWS_BUILD_ORDER_SUMMARY",
|
||||||
|
name="构建订单汇总",
|
||||||
|
description="重算 DWS 订单汇总表",
|
||||||
|
domain=BusinessDomain.DWD,
|
||||||
|
requires_window=False,
|
||||||
|
),
|
||||||
|
# Schema 初始化
|
||||||
|
TaskDefinition(
|
||||||
|
code="INIT_ODS_SCHEMA",
|
||||||
|
name="初始化 ODS Schema",
|
||||||
|
description="创建/重建 ODS 表结构",
|
||||||
|
domain=BusinessDomain.SCHEMA,
|
||||||
|
requires_window=False,
|
||||||
|
default_enabled=False,
|
||||||
|
),
|
||||||
|
TaskDefinition(
|
||||||
|
code="INIT_DWD_SCHEMA",
|
||||||
|
name="初始化 DWD Schema",
|
||||||
|
description="创建/重建 DWD 表结构",
|
||||||
|
domain=BusinessDomain.SCHEMA,
|
||||||
|
requires_window=False,
|
||||||
|
default_enabled=False,
|
||||||
|
),
|
||||||
|
TaskDefinition(
|
||||||
|
code="INIT_DWS_SCHEMA",
|
||||||
|
name="初始化 DWS Schema",
|
||||||
|
description="创建/重建 DWS 表结构",
|
||||||
|
domain=BusinessDomain.SCHEMA,
|
||||||
|
requires_window=False,
|
||||||
|
default_enabled=False,
|
||||||
|
),
|
||||||
|
# 其他
|
||||||
|
TaskDefinition(
|
||||||
|
code="MANUAL_INGEST",
|
||||||
|
name="手工数据灌入",
|
||||||
|
description="从本地 JSON 回放入库",
|
||||||
|
domain=BusinessDomain.OTHER,
|
||||||
|
requires_window=False,
|
||||||
|
default_enabled=False,
|
||||||
|
),
|
||||||
|
TaskDefinition(
|
||||||
|
code="CHECK_CUTOFF",
|
||||||
|
name="检查 Cutoff",
|
||||||
|
description="查看各表数据截止时间",
|
||||||
|
domain=BusinessDomain.QUALITY,
|
||||||
|
requires_window=False,
|
||||||
|
),
|
||||||
|
TaskDefinition(
|
||||||
|
code="DATA_INTEGRITY_CHECK",
|
||||||
|
name="数据完整性检查",
|
||||||
|
description="检查 ODS/DWD 数据完整性",
|
||||||
|
domain=BusinessDomain.QUALITY,
|
||||||
|
requires_window=True,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def _build_ods_task_definition(code: str) -> TaskDefinition:
|
||||||
|
"""根据 ODS 任务编码构建任务定义"""
|
||||||
|
domain = ODS_DOMAIN_MAP.get(code, BusinessDomain.OTHER)
|
||||||
|
name = ODS_DISPLAY_NAMES.get(code, code)
|
||||||
|
is_dimension = code in DIMENSION_ODS_CODES
|
||||||
|
|
||||||
|
# 从后端获取描述(如果可用)
|
||||||
|
description = f"抓取{name}到 ODS"
|
||||||
|
if _HAS_BACKEND:
|
||||||
|
for spec in ODS_TASK_SPECS:
|
||||||
|
if spec.code == code:
|
||||||
|
# 尝试解码描述(可能是乱码)
|
||||||
|
desc = spec.description
|
||||||
|
if desc and not any(ord(c) > 0x4e00 for c in desc[:10] if desc):
|
||||||
|
description = f"抓取{name}到 ODS"
|
||||||
|
break
|
||||||
|
|
||||||
|
return TaskDefinition(
|
||||||
|
code=code,
|
||||||
|
name=name,
|
||||||
|
description=description,
|
||||||
|
domain=domain,
|
||||||
|
requires_window=code not in DIMENSION_ODS_CODES,
|
||||||
|
is_ods=True,
|
||||||
|
is_dimension=is_dimension,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class TaskRegistry:
|
||||||
|
"""任务注册表:管理所有可用任务"""
|
||||||
|
|
||||||
|
_instance: Optional["TaskRegistry"] = None
|
||||||
|
|
||||||
|
def __new__(cls):
|
||||||
|
if cls._instance is None:
|
||||||
|
cls._instance = super().__new__(cls)
|
||||||
|
cls._instance._initialized = False
|
||||||
|
return cls._instance
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
if self._initialized:
|
||||||
|
return
|
||||||
|
self._initialized = True
|
||||||
|
self._tasks: Dict[str, TaskDefinition] = {}
|
||||||
|
self._load_tasks()
|
||||||
|
|
||||||
|
def _load_tasks(self):
|
||||||
|
"""加载所有任务定义"""
|
||||||
|
# 加载 ODS 任务
|
||||||
|
ods_codes = ENABLED_ODS_CODES if _HAS_BACKEND else set(ODS_DOMAIN_MAP.keys())
|
||||||
|
for code in ods_codes:
|
||||||
|
self._tasks[code] = _build_ods_task_definition(code)
|
||||||
|
|
||||||
|
# 加载非 ODS 任务
|
||||||
|
for task_def in NON_ODS_TASKS:
|
||||||
|
self._tasks[task_def.code] = task_def
|
||||||
|
|
||||||
|
def get_task(self, code: str) -> Optional[TaskDefinition]:
|
||||||
|
"""获取任务定义"""
|
||||||
|
return self._tasks.get(code)
|
||||||
|
|
||||||
|
def get_all_tasks(self) -> List[TaskDefinition]:
|
||||||
|
"""获取所有任务"""
|
||||||
|
return list(self._tasks.values())
|
||||||
|
|
||||||
|
def get_ods_tasks(self) -> List[TaskDefinition]:
|
||||||
|
"""获取所有 ODS 任务"""
|
||||||
|
return [t for t in self._tasks.values() if t.is_ods]
|
||||||
|
|
||||||
|
def get_fact_ods_tasks(self) -> List[TaskDefinition]:
|
||||||
|
"""获取事实类 ODS 任务(需要时间窗口)"""
|
||||||
|
return [t for t in self._tasks.values() if t.is_ods and not t.is_dimension]
|
||||||
|
|
||||||
|
def get_dimension_ods_tasks(self) -> List[TaskDefinition]:
|
||||||
|
"""获取维度类 ODS 任务"""
|
||||||
|
return [t for t in self._tasks.values() if t.is_ods and t.is_dimension]
|
||||||
|
|
||||||
|
def get_tasks_by_domain(self, domain: BusinessDomain) -> List[TaskDefinition]:
|
||||||
|
"""按业务域获取任务"""
|
||||||
|
return [t for t in self._tasks.values() if t.domain == domain]
|
||||||
|
|
||||||
|
def get_ods_tasks_grouped(self) -> Dict[BusinessDomain, List[TaskDefinition]]:
|
||||||
|
"""获取按业务域分组的 ODS 任务"""
|
||||||
|
grouped: Dict[BusinessDomain, List[TaskDefinition]] = {}
|
||||||
|
for task in self.get_ods_tasks():
|
||||||
|
if task.domain not in grouped:
|
||||||
|
grouped[task.domain] = []
|
||||||
|
grouped[task.domain].append(task)
|
||||||
|
return grouped
|
||||||
|
|
||||||
|
def get_non_ods_tasks(self) -> List[TaskDefinition]:
|
||||||
|
"""获取非 ODS 任务"""
|
||||||
|
return [t for t in self._tasks.values() if not t.is_ods]
|
||||||
|
|
||||||
|
|
||||||
|
# 全局注册表实例
|
||||||
|
task_registry = TaskRegistry()
|
||||||
|
|
||||||
|
|
||||||
|
# 便捷函数
|
||||||
|
def get_ods_task_codes() -> List[str]:
|
||||||
|
"""获取所有 ODS 任务编码"""
|
||||||
|
return [t.code for t in task_registry.get_ods_tasks()]
|
||||||
|
|
||||||
|
|
||||||
|
def get_fact_ods_task_codes() -> List[str]:
|
||||||
|
"""获取事实类 ODS 任务编码"""
|
||||||
|
return [t.code for t in task_registry.get_fact_ods_tasks()]
|
||||||
|
|
||||||
|
|
||||||
|
def get_dimension_ods_task_codes() -> List[str]:
|
||||||
|
"""获取维度类 ODS 任务编码"""
|
||||||
|
return [t.code for t in task_registry.get_dimension_ods_tasks()]
|
||||||
|
|
||||||
|
|
||||||
|
def get_all_task_tuples() -> List[Tuple[str, str, str]]:
|
||||||
|
"""获取所有任务的 (code, name, description) 元组列表"""
|
||||||
|
return [(t.code, t.name, t.description) for t in task_registry.get_all_tasks()]
|
||||||
|
|
||||||
|
|
||||||
|
def get_ods_tasks_for_ui() -> List[Tuple[str, str, BusinessDomain]]:
|
||||||
|
"""获取 ODS 任务列表供 UI 使用:(code, display_name, domain)"""
|
||||||
|
return [(t.code, t.name, t.domain) for t in task_registry.get_ods_tasks()]
|
||||||
@@ -7,6 +7,7 @@ from .log_viewer import LogViewer
|
|||||||
from .db_viewer import DBViewer
|
from .db_viewer import DBViewer
|
||||||
from .status_panel import StatusPanel
|
from .status_panel import StatusPanel
|
||||||
from .task_manager import TaskManager
|
from .task_manager import TaskManager
|
||||||
|
from .task_selector import TaskSelectorWidget, CompactTaskSelector
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"TaskPanel",
|
"TaskPanel",
|
||||||
@@ -15,4 +16,6 @@ __all__ = [
|
|||||||
"DBViewer",
|
"DBViewer",
|
||||||
"StatusPanel",
|
"StatusPanel",
|
||||||
"TaskManager",
|
"TaskManager",
|
||||||
|
"TaskSelectorWidget",
|
||||||
|
"CompactTaskSelector",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -26,9 +26,24 @@ from ..utils.app_settings import app_settings
|
|||||||
from ..workers.task_worker import TaskWorker
|
from ..workers.task_worker import TaskWorker
|
||||||
|
|
||||||
|
|
||||||
# 可调度的任务列表(包含所有 ODS 任务 + DWD/质量检查任务)
|
# 动态获取可调度的任务列表
|
||||||
SCHEDULABLE_TASKS = [
|
def _get_schedulable_tasks():
|
||||||
# ODS 数据抓取任务(与 task_panel.AUTO_UPDATE_TASKS 保持一致)
|
"""从任务注册表动态获取可调度任务列表"""
|
||||||
|
try:
|
||||||
|
from ..models.task_registry import task_registry
|
||||||
|
tasks = []
|
||||||
|
# 添加所有 ODS 任务
|
||||||
|
for task_def in task_registry.get_ods_tasks():
|
||||||
|
tasks.append((task_def.code, task_def.name))
|
||||||
|
# 添加非 ODS 任务(排除 Schema 初始化和手工灌入)
|
||||||
|
exclude_codes = {"INIT_ODS_SCHEMA", "INIT_DWD_SCHEMA", "INIT_DWS_SCHEMA", "MANUAL_INGEST"}
|
||||||
|
for task_def in task_registry.get_non_ods_tasks():
|
||||||
|
if task_def.code not in exclude_codes:
|
||||||
|
tasks.append((task_def.code, task_def.name))
|
||||||
|
return tasks
|
||||||
|
except ImportError:
|
||||||
|
# 回退到静态列表
|
||||||
|
return [
|
||||||
("ODS_PAYMENT", "支付流水"),
|
("ODS_PAYMENT", "支付流水"),
|
||||||
("ODS_MEMBER", "会员档案"),
|
("ODS_MEMBER", "会员档案"),
|
||||||
("ODS_MEMBER_CARD", "会员储值卡"),
|
("ODS_MEMBER_CARD", "会员储值卡"),
|
||||||
@@ -42,7 +57,6 @@ SCHEDULABLE_TASKS = [
|
|||||||
("ODS_PLATFORM_COUPON", "平台券核销"),
|
("ODS_PLATFORM_COUPON", "平台券核销"),
|
||||||
("ODS_RECHARGE_SETTLE", "充值结算"),
|
("ODS_RECHARGE_SETTLE", "充值结算"),
|
||||||
("ODS_SETTLEMENT_TICKET", "结账小票"),
|
("ODS_SETTLEMENT_TICKET", "结账小票"),
|
||||||
# DWD 和质量检查任务
|
|
||||||
("DWD_LOAD_FROM_ODS", "ODS→DWD 装载"),
|
("DWD_LOAD_FROM_ODS", "ODS→DWD 装载"),
|
||||||
("DWD_QUALITY_CHECK", "DWD 质量检查"),
|
("DWD_QUALITY_CHECK", "DWD 质量检查"),
|
||||||
("DATA_INTEGRITY_CHECK", "数据完整性检查"),
|
("DATA_INTEGRITY_CHECK", "数据完整性检查"),
|
||||||
@@ -50,6 +64,9 @@ SCHEDULABLE_TASKS = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
SCHEDULABLE_TASKS = _get_schedulable_tasks()
|
||||||
|
|
||||||
|
|
||||||
class TaskLogDialog(QDialog):
|
class TaskLogDialog(QDialog):
|
||||||
"""任务日志查看对话框"""
|
"""任务日志查看对话框"""
|
||||||
|
|
||||||
@@ -1584,6 +1601,7 @@ class TaskManager(QWidget):
|
|||||||
|
|
||||||
# 统计关键数据
|
# 统计关键数据
|
||||||
total_inserted = 0
|
total_inserted = 0
|
||||||
|
total_updated = 0
|
||||||
total_missing = 0
|
total_missing = 0
|
||||||
total_records = 0
|
total_records = 0
|
||||||
|
|
||||||
@@ -1596,11 +1614,30 @@ class TaskManager(QWidget):
|
|||||||
import json
|
import json
|
||||||
stats_str = match.group(1).replace("'", '"')
|
stats_str = match.group(1).replace("'", '"')
|
||||||
stats = json.loads(stats_str)
|
stats = json.loads(stats_str)
|
||||||
|
|
||||||
|
|
||||||
if 'tables' in stats:
|
if 'tables' in stats:
|
||||||
|
|
||||||
for tbl in stats['tables']:
|
for tbl in stats['tables']:
|
||||||
inserted = tbl.get('inserted', 0)
|
|
||||||
processed = tbl.get('processed', 0)
|
inserted = int(tbl.get('inserted', 0) or 0)
|
||||||
|
|
||||||
|
updated = int(tbl.get('updated', 0) or 0)
|
||||||
|
|
||||||
|
processed = int(tbl.get('processed', 0) or 0)
|
||||||
|
|
||||||
|
has_new_counts = ('inserted' in tbl) or ('updated' in tbl)
|
||||||
|
|
||||||
|
if has_new_counts:
|
||||||
|
|
||||||
|
total_inserted += inserted
|
||||||
|
|
||||||
|
total_updated += updated
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
total_inserted += inserted + processed
|
total_inserted += inserted + processed
|
||||||
|
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -1622,8 +1659,11 @@ class TaskManager(QWidget):
|
|||||||
total_records += int(match.group(1))
|
total_records += int(match.group(1))
|
||||||
|
|
||||||
# 构建摘要
|
# 构建摘要
|
||||||
if total_inserted > 0:
|
if total_inserted > 0 or total_updated > 0:
|
||||||
summary_parts.append(f"处理 {total_inserted} 条")
|
if total_updated > 0:
|
||||||
|
summary_parts.append(f"?? {total_inserted} ?, ?? {total_updated} ?")
|
||||||
|
else:
|
||||||
|
summary_parts.append(f"?? {total_inserted} ?")
|
||||||
|
|
||||||
if total_records > 0:
|
if total_records > 0:
|
||||||
if total_missing > 0:
|
if total_missing > 0:
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
398
etl_billiards/gui/widgets/task_selector.py
Normal file
398
etl_billiards/gui/widgets/task_selector.py
Normal file
@@ -0,0 +1,398 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""可复用的 ODS 任务选择组件:按业务域分组显示,支持全选/反选。"""
|
||||||
|
|
||||||
|
from typing import Dict, List, Optional, Set
|
||||||
|
|
||||||
|
from PySide6.QtWidgets import (
|
||||||
|
QWidget, QVBoxLayout, QHBoxLayout, QGroupBox,
|
||||||
|
QCheckBox, QPushButton, QScrollArea, QFrame,
|
||||||
|
QLabel, QSizePolicy
|
||||||
|
)
|
||||||
|
from PySide6.QtCore import Signal, Qt
|
||||||
|
|
||||||
|
from ..models.task_registry import (
|
||||||
|
TaskRegistry, TaskDefinition, BusinessDomain, DOMAIN_LABELS,
|
||||||
|
task_registry, get_fact_ods_task_codes, get_dimension_ods_task_codes
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class TaskSelectorWidget(QWidget):
|
||||||
|
"""ODS 任务选择组件:按业务域分组显示"""
|
||||||
|
|
||||||
|
# 选择变化信号
|
||||||
|
selection_changed = Signal(list) # 选中的任务编码列表
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
parent: Optional[QWidget] = None,
|
||||||
|
show_dimensions: bool = True,
|
||||||
|
show_facts: bool = True,
|
||||||
|
default_select_facts: bool = True,
|
||||||
|
default_select_dimensions: bool = False,
|
||||||
|
compact: bool = False,
|
||||||
|
max_height: int = 0,
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
初始化任务选择器
|
||||||
|
|
||||||
|
Args:
|
||||||
|
parent: 父组件
|
||||||
|
show_dimensions: 是否显示维度类任务
|
||||||
|
show_facts: 是否显示事实类任务
|
||||||
|
default_select_facts: 默认选中事实类任务
|
||||||
|
default_select_dimensions: 默认选中维度类任务
|
||||||
|
compact: 紧凑模式(更小的间距)
|
||||||
|
max_height: 最大高度(0 表示不限制)
|
||||||
|
"""
|
||||||
|
super().__init__(parent)
|
||||||
|
self.show_dimensions = show_dimensions
|
||||||
|
self.show_facts = show_facts
|
||||||
|
self.default_select_facts = default_select_facts
|
||||||
|
self.default_select_dimensions = default_select_dimensions
|
||||||
|
self.compact = compact
|
||||||
|
self.max_height = max_height
|
||||||
|
|
||||||
|
# 任务复选框映射:code -> QCheckBox
|
||||||
|
self._checkboxes: Dict[str, QCheckBox] = {}
|
||||||
|
# 业务域分组框映射:domain -> QGroupBox
|
||||||
|
self._domain_groups: Dict[BusinessDomain, QGroupBox] = {}
|
||||||
|
|
||||||
|
self._init_ui()
|
||||||
|
self._apply_default_selection()
|
||||||
|
|
||||||
|
def _init_ui(self):
|
||||||
|
"""初始化界面"""
|
||||||
|
layout = QVBoxLayout(self)
|
||||||
|
layout.setContentsMargins(0, 0, 0, 0)
|
||||||
|
spacing = 4 if self.compact else 8
|
||||||
|
layout.setSpacing(spacing)
|
||||||
|
|
||||||
|
# 顶部工具栏
|
||||||
|
toolbar = QHBoxLayout()
|
||||||
|
toolbar.setSpacing(8)
|
||||||
|
|
||||||
|
self.select_all_btn = QPushButton("全选")
|
||||||
|
self.select_all_btn.setProperty("secondary", True)
|
||||||
|
self.select_all_btn.setFixedWidth(60)
|
||||||
|
self.select_all_btn.clicked.connect(self._select_all)
|
||||||
|
toolbar.addWidget(self.select_all_btn)
|
||||||
|
|
||||||
|
self.deselect_all_btn = QPushButton("全不选")
|
||||||
|
self.deselect_all_btn.setProperty("secondary", True)
|
||||||
|
self.deselect_all_btn.setFixedWidth(60)
|
||||||
|
self.deselect_all_btn.clicked.connect(self._deselect_all)
|
||||||
|
toolbar.addWidget(self.deselect_all_btn)
|
||||||
|
|
||||||
|
self.select_facts_btn = QPushButton("选事实表")
|
||||||
|
self.select_facts_btn.setProperty("secondary", True)
|
||||||
|
self.select_facts_btn.setFixedWidth(70)
|
||||||
|
self.select_facts_btn.setToolTip("选中所有事实类任务(需要时间窗口的任务)")
|
||||||
|
self.select_facts_btn.clicked.connect(self._select_facts_only)
|
||||||
|
toolbar.addWidget(self.select_facts_btn)
|
||||||
|
|
||||||
|
toolbar.addStretch()
|
||||||
|
|
||||||
|
self.selected_count_label = QLabel("已选: 0")
|
||||||
|
self.selected_count_label.setProperty("subheading", True)
|
||||||
|
toolbar.addWidget(self.selected_count_label)
|
||||||
|
|
||||||
|
layout.addLayout(toolbar)
|
||||||
|
|
||||||
|
# 滚动区域
|
||||||
|
scroll_area = QScrollArea()
|
||||||
|
scroll_area.setWidgetResizable(True)
|
||||||
|
scroll_area.setFrameShape(QFrame.NoFrame)
|
||||||
|
if self.max_height > 0:
|
||||||
|
scroll_area.setMaximumHeight(self.max_height)
|
||||||
|
|
||||||
|
# 内容容器
|
||||||
|
content_widget = QWidget()
|
||||||
|
content_layout = QVBoxLayout(content_widget)
|
||||||
|
content_layout.setContentsMargins(0, 0, 0, 0)
|
||||||
|
content_layout.setSpacing(spacing)
|
||||||
|
|
||||||
|
# 按业务域分组创建复选框
|
||||||
|
grouped_tasks = task_registry.get_ods_tasks_grouped()
|
||||||
|
|
||||||
|
# 定义业务域显示顺序
|
||||||
|
domain_order = [
|
||||||
|
BusinessDomain.MEMBER,
|
||||||
|
BusinessDomain.SETTLEMENT,
|
||||||
|
BusinessDomain.ASSISTANT,
|
||||||
|
BusinessDomain.GOODS,
|
||||||
|
BusinessDomain.TABLE,
|
||||||
|
BusinessDomain.PROMOTION,
|
||||||
|
BusinessDomain.INVENTORY,
|
||||||
|
]
|
||||||
|
|
||||||
|
for domain in domain_order:
|
||||||
|
if domain not in grouped_tasks:
|
||||||
|
continue
|
||||||
|
|
||||||
|
tasks = grouped_tasks[domain]
|
||||||
|
# 过滤任务
|
||||||
|
filtered_tasks = []
|
||||||
|
for task in tasks:
|
||||||
|
if task.is_dimension and not self.show_dimensions:
|
||||||
|
continue
|
||||||
|
if not task.is_dimension and not self.show_facts:
|
||||||
|
continue
|
||||||
|
filtered_tasks.append(task)
|
||||||
|
|
||||||
|
if not filtered_tasks:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# 创建业务域分组
|
||||||
|
group_box = self._create_domain_group(domain, filtered_tasks)
|
||||||
|
self._domain_groups[domain] = group_box
|
||||||
|
content_layout.addWidget(group_box)
|
||||||
|
|
||||||
|
content_layout.addStretch()
|
||||||
|
scroll_area.setWidget(content_widget)
|
||||||
|
layout.addWidget(scroll_area, 1)
|
||||||
|
|
||||||
|
def _create_domain_group(self, domain: BusinessDomain, tasks: List[TaskDefinition]) -> QGroupBox:
|
||||||
|
"""创建业务域分组框"""
|
||||||
|
group_box = QGroupBox(DOMAIN_LABELS.get(domain, str(domain.value)))
|
||||||
|
group_layout = QVBoxLayout(group_box)
|
||||||
|
group_layout.setContentsMargins(8, 4, 8, 4)
|
||||||
|
group_layout.setSpacing(2)
|
||||||
|
|
||||||
|
for task in tasks:
|
||||||
|
checkbox = QCheckBox(f"{task.name}")
|
||||||
|
checkbox.setToolTip(f"{task.code}: {task.description}")
|
||||||
|
checkbox.setProperty("task_code", task.code)
|
||||||
|
checkbox.setProperty("is_dimension", task.is_dimension)
|
||||||
|
checkbox.stateChanged.connect(self._on_selection_changed)
|
||||||
|
|
||||||
|
self._checkboxes[task.code] = checkbox
|
||||||
|
group_layout.addWidget(checkbox)
|
||||||
|
|
||||||
|
return group_box
|
||||||
|
|
||||||
|
def _apply_default_selection(self):
|
||||||
|
"""应用默认选择"""
|
||||||
|
for code, checkbox in self._checkboxes.items():
|
||||||
|
is_dimension = checkbox.property("is_dimension")
|
||||||
|
if is_dimension:
|
||||||
|
checkbox.setChecked(self.default_select_dimensions)
|
||||||
|
else:
|
||||||
|
checkbox.setChecked(self.default_select_facts)
|
||||||
|
|
||||||
|
self._update_count_label()
|
||||||
|
|
||||||
|
def _on_selection_changed(self):
|
||||||
|
"""选择变化时"""
|
||||||
|
self._update_count_label()
|
||||||
|
self.selection_changed.emit(self.get_selected_codes())
|
||||||
|
|
||||||
|
def _update_count_label(self):
|
||||||
|
"""更新选中计数标签"""
|
||||||
|
count = len(self.get_selected_codes())
|
||||||
|
total = len(self._checkboxes)
|
||||||
|
self.selected_count_label.setText(f"已选: {count}/{total}")
|
||||||
|
|
||||||
|
def _select_all(self):
|
||||||
|
"""全选"""
|
||||||
|
for checkbox in self._checkboxes.values():
|
||||||
|
checkbox.blockSignals(True)
|
||||||
|
checkbox.setChecked(True)
|
||||||
|
checkbox.blockSignals(False)
|
||||||
|
self._on_selection_changed()
|
||||||
|
|
||||||
|
def _deselect_all(self):
|
||||||
|
"""全不选"""
|
||||||
|
for checkbox in self._checkboxes.values():
|
||||||
|
checkbox.blockSignals(True)
|
||||||
|
checkbox.setChecked(False)
|
||||||
|
checkbox.blockSignals(False)
|
||||||
|
self._on_selection_changed()
|
||||||
|
|
||||||
|
def _select_facts_only(self):
|
||||||
|
"""只选事实表任务"""
|
||||||
|
for code, checkbox in self._checkboxes.items():
|
||||||
|
checkbox.blockSignals(True)
|
||||||
|
is_dimension = checkbox.property("is_dimension")
|
||||||
|
checkbox.setChecked(not is_dimension)
|
||||||
|
checkbox.blockSignals(False)
|
||||||
|
self._on_selection_changed()
|
||||||
|
|
||||||
|
def get_selected_codes(self) -> List[str]:
|
||||||
|
"""获取选中的任务编码列表"""
|
||||||
|
selected = []
|
||||||
|
for code, checkbox in self._checkboxes.items():
|
||||||
|
if checkbox.isChecked():
|
||||||
|
selected.append(code)
|
||||||
|
return selected
|
||||||
|
|
||||||
|
def set_selected_codes(self, codes: List[str]):
|
||||||
|
"""设置选中的任务编码"""
|
||||||
|
codes_set = set(codes)
|
||||||
|
for code, checkbox in self._checkboxes.items():
|
||||||
|
checkbox.blockSignals(True)
|
||||||
|
checkbox.setChecked(code in codes_set)
|
||||||
|
checkbox.blockSignals(False)
|
||||||
|
self._on_selection_changed()
|
||||||
|
|
||||||
|
def get_all_codes(self) -> List[str]:
|
||||||
|
"""获取所有任务编码"""
|
||||||
|
return list(self._checkboxes.keys())
|
||||||
|
|
||||||
|
def is_any_selected(self) -> bool:
|
||||||
|
"""是否有任何任务被选中"""
|
||||||
|
return len(self.get_selected_codes()) > 0
|
||||||
|
|
||||||
|
|
||||||
|
class CompactTaskSelector(QWidget):
|
||||||
|
"""紧凑型任务选择器:单行显示业务域,点击展开选择"""
|
||||||
|
|
||||||
|
selection_changed = Signal(list)
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
parent: Optional[QWidget] = None,
|
||||||
|
show_dimensions: bool = True,
|
||||||
|
show_facts: bool = True,
|
||||||
|
default_select_facts: bool = True,
|
||||||
|
default_select_dimensions: bool = False,
|
||||||
|
):
|
||||||
|
super().__init__(parent)
|
||||||
|
self.show_dimensions = show_dimensions
|
||||||
|
self.show_facts = show_facts
|
||||||
|
self.default_select_facts = default_select_facts
|
||||||
|
self.default_select_dimensions = default_select_dimensions
|
||||||
|
|
||||||
|
# 业务域复选框
|
||||||
|
self._domain_checkboxes: Dict[BusinessDomain, QCheckBox] = {}
|
||||||
|
# 业务域下的任务编码
|
||||||
|
self._domain_tasks: Dict[BusinessDomain, List[str]] = {}
|
||||||
|
|
||||||
|
self._init_ui()
|
||||||
|
self._apply_default_selection()
|
||||||
|
|
||||||
|
def _init_ui(self):
|
||||||
|
"""初始化界面"""
|
||||||
|
layout = QVBoxLayout(self)
|
||||||
|
layout.setContentsMargins(0, 0, 0, 0)
|
||||||
|
layout.setSpacing(4)
|
||||||
|
|
||||||
|
# 工具栏
|
||||||
|
toolbar = QHBoxLayout()
|
||||||
|
toolbar.setSpacing(8)
|
||||||
|
|
||||||
|
self.select_all_btn = QPushButton("全选")
|
||||||
|
self.select_all_btn.setProperty("secondary", True)
|
||||||
|
self.select_all_btn.setFixedWidth(50)
|
||||||
|
self.select_all_btn.clicked.connect(self._select_all)
|
||||||
|
toolbar.addWidget(self.select_all_btn)
|
||||||
|
|
||||||
|
self.deselect_all_btn = QPushButton("清空")
|
||||||
|
self.deselect_all_btn.setProperty("secondary", True)
|
||||||
|
self.deselect_all_btn.setFixedWidth(50)
|
||||||
|
self.deselect_all_btn.clicked.connect(self._deselect_all)
|
||||||
|
toolbar.addWidget(self.deselect_all_btn)
|
||||||
|
|
||||||
|
toolbar.addStretch()
|
||||||
|
|
||||||
|
self.count_label = QLabel("已选: 0")
|
||||||
|
self.count_label.setProperty("subheading", True)
|
||||||
|
toolbar.addWidget(self.count_label)
|
||||||
|
|
||||||
|
layout.addLayout(toolbar)
|
||||||
|
|
||||||
|
# 业务域复选框(横向排列)
|
||||||
|
domains_layout = QHBoxLayout()
|
||||||
|
domains_layout.setSpacing(12)
|
||||||
|
|
||||||
|
grouped_tasks = task_registry.get_ods_tasks_grouped()
|
||||||
|
domain_order = [
|
||||||
|
BusinessDomain.MEMBER,
|
||||||
|
BusinessDomain.SETTLEMENT,
|
||||||
|
BusinessDomain.ASSISTANT,
|
||||||
|
BusinessDomain.GOODS,
|
||||||
|
BusinessDomain.TABLE,
|
||||||
|
BusinessDomain.PROMOTION,
|
||||||
|
BusinessDomain.INVENTORY,
|
||||||
|
]
|
||||||
|
|
||||||
|
for domain in domain_order:
|
||||||
|
if domain not in grouped_tasks:
|
||||||
|
continue
|
||||||
|
|
||||||
|
tasks = grouped_tasks[domain]
|
||||||
|
# 过滤任务
|
||||||
|
task_codes = []
|
||||||
|
for task in tasks:
|
||||||
|
if task.is_dimension and not self.show_dimensions:
|
||||||
|
continue
|
||||||
|
if not task.is_dimension and not self.show_facts:
|
||||||
|
continue
|
||||||
|
task_codes.append(task.code)
|
||||||
|
|
||||||
|
if not task_codes:
|
||||||
|
continue
|
||||||
|
|
||||||
|
self._domain_tasks[domain] = task_codes
|
||||||
|
|
||||||
|
checkbox = QCheckBox(DOMAIN_LABELS.get(domain, str(domain.value)))
|
||||||
|
checkbox.setToolTip(f"包含: {', '.join(task_codes)}")
|
||||||
|
checkbox.stateChanged.connect(self._on_selection_changed)
|
||||||
|
self._domain_checkboxes[domain] = checkbox
|
||||||
|
domains_layout.addWidget(checkbox)
|
||||||
|
|
||||||
|
domains_layout.addStretch()
|
||||||
|
layout.addLayout(domains_layout)
|
||||||
|
|
||||||
|
def _apply_default_selection(self):
|
||||||
|
"""应用默认选择"""
|
||||||
|
# 默认选中所有业务域
|
||||||
|
for domain, checkbox in self._domain_checkboxes.items():
|
||||||
|
checkbox.setChecked(True)
|
||||||
|
self._update_count_label()
|
||||||
|
|
||||||
|
def _on_selection_changed(self):
|
||||||
|
"""选择变化时"""
|
||||||
|
self._update_count_label()
|
||||||
|
self.selection_changed.emit(self.get_selected_codes())
|
||||||
|
|
||||||
|
def _update_count_label(self):
|
||||||
|
"""更新计数标签"""
|
||||||
|
count = len(self.get_selected_codes())
|
||||||
|
self.count_label.setText(f"已选: {count} 个任务")
|
||||||
|
|
||||||
|
def _select_all(self):
|
||||||
|
"""全选所有业务域"""
|
||||||
|
for checkbox in self._domain_checkboxes.values():
|
||||||
|
checkbox.blockSignals(True)
|
||||||
|
checkbox.setChecked(True)
|
||||||
|
checkbox.blockSignals(False)
|
||||||
|
self._on_selection_changed()
|
||||||
|
|
||||||
|
def _deselect_all(self):
|
||||||
|
"""取消全选"""
|
||||||
|
for checkbox in self._domain_checkboxes.values():
|
||||||
|
checkbox.blockSignals(True)
|
||||||
|
checkbox.setChecked(False)
|
||||||
|
checkbox.blockSignals(False)
|
||||||
|
self._on_selection_changed()
|
||||||
|
|
||||||
|
def get_selected_codes(self) -> List[str]:
|
||||||
|
"""获取选中的任务编码"""
|
||||||
|
selected = []
|
||||||
|
for domain, checkbox in self._domain_checkboxes.items():
|
||||||
|
if checkbox.isChecked():
|
||||||
|
selected.extend(self._domain_tasks.get(domain, []))
|
||||||
|
return selected
|
||||||
|
|
||||||
|
def set_selected_domains(self, domains: List[BusinessDomain]):
|
||||||
|
"""设置选中的业务域"""
|
||||||
|
domains_set = set(domains)
|
||||||
|
for domain, checkbox in self._domain_checkboxes.items():
|
||||||
|
checkbox.blockSignals(True)
|
||||||
|
checkbox.setChecked(domain in domains_set)
|
||||||
|
checkbox.blockSignals(False)
|
||||||
|
self._on_selection_changed()
|
||||||
|
|
||||||
|
def is_any_selected(self) -> bool:
|
||||||
|
"""是否有任何任务被选中"""
|
||||||
|
return len(self.get_selected_codes()) > 0
|
||||||
@@ -189,28 +189,69 @@ class TaskWorker(QThread):
|
|||||||
|
|
||||||
# 解析 DWD 装载统计
|
# 解析 DWD 装载统计
|
||||||
if 'tables' in stats:
|
if 'tables' in stats:
|
||||||
total_processed = 0
|
total_dim_inserted = 0
|
||||||
total_inserted = 0
|
total_dim_updated = 0
|
||||||
tables_with_data = []
|
total_fact_inserted = 0
|
||||||
|
total_fact_updated = 0
|
||||||
|
|
||||||
|
dim_tables = [] # 维表明细
|
||||||
|
fact_tables = [] # 事实表明细
|
||||||
|
|
||||||
for tbl in stats['tables']:
|
for tbl in stats['tables']:
|
||||||
table_name = tbl.get('table', '').replace('billiards_dwd.', '')
|
table_name = tbl.get('table', '').replace('billiards_dwd.', '')
|
||||||
processed = tbl.get('processed', 0)
|
mode = tbl.get('mode', '')
|
||||||
inserted = tbl.get('inserted', 0)
|
processed = int(tbl.get('processed', 0) or 0)
|
||||||
|
inserted = int(tbl.get('inserted', 0) or 0)
|
||||||
|
updated = int(tbl.get('updated', 0) or 0)
|
||||||
|
has_new_counts = ('inserted' in tbl) or ('updated' in tbl)
|
||||||
|
|
||||||
if processed > 0:
|
# 忽略 _ex 扩展表
|
||||||
total_processed += processed
|
if table_name.endswith('_ex'):
|
||||||
tables_with_data.append(f"{table_name}({processed})")
|
continue
|
||||||
elif inserted > 0:
|
|
||||||
total_inserted += inserted
|
|
||||||
tables_with_data.append(f"{table_name}(+{inserted})")
|
|
||||||
|
|
||||||
if total_processed > 0 or total_inserted > 0:
|
is_dim = table_name.startswith('dim_') or mode == 'SCD2'
|
||||||
dwd_stats.append(f"处理维度: {total_processed}条, 新增事实: {total_inserted}条")
|
if is_dim:
|
||||||
if len(tables_with_data) <= 5:
|
if has_new_counts:
|
||||||
dwd_stats.append(f"涉及表: {', '.join(tables_with_data)}")
|
total_dim_inserted += inserted
|
||||||
|
total_dim_updated += updated
|
||||||
|
if inserted or updated:
|
||||||
|
dim_tables.append(f"{table_name}: +{inserted}, ~{updated}")
|
||||||
|
elif processed > 0:
|
||||||
|
total_dim_updated += processed
|
||||||
|
dim_tables.append(f"{table_name}: {processed}")
|
||||||
else:
|
else:
|
||||||
dwd_stats.append(f"涉及 {len(tables_with_data)} 张表")
|
if has_new_counts:
|
||||||
|
total_fact_inserted += inserted
|
||||||
|
total_fact_updated += updated
|
||||||
|
if inserted or updated:
|
||||||
|
fact_tables.append(f"{table_name}: +{inserted}, ~{updated}")
|
||||||
|
elif processed > 0 or inserted > 0:
|
||||||
|
total_fact_inserted += inserted
|
||||||
|
if inserted > 0:
|
||||||
|
fact_tables.append(f"{table_name}: +{inserted}")
|
||||||
|
|
||||||
|
if (total_dim_inserted or total_dim_updated or total_fact_inserted or total_fact_updated):
|
||||||
|
dwd_stats.append(
|
||||||
|
f"维表新增: {total_dim_inserted}条, 维表更新: {total_dim_updated}条, "
|
||||||
|
f"事实表新增: {total_fact_inserted}条, 事实表更新: {total_fact_updated}条"
|
||||||
|
)
|
||||||
|
|
||||||
|
# 维表明细
|
||||||
|
if dim_tables:
|
||||||
|
dwd_stats.append(" 维表: " + ", ".join(dim_tables))
|
||||||
|
|
||||||
|
# 事实表明细
|
||||||
|
if fact_tables:
|
||||||
|
dwd_stats.append(" 事实表: " + ", ".join(fact_tables))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# 解析错误信息
|
||||||
|
if 'errors' in stats and stats['errors']:
|
||||||
|
for err in stats['errors']:
|
||||||
|
err_table = err.get('table', '').replace('billiards_dwd.', '')
|
||||||
|
err_msg = err.get('error', '')
|
||||||
|
errors.append(f"{err_table}: {err_msg}")
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
continue
|
continue
|
||||||
@@ -263,7 +304,9 @@ class TaskWorker(QThread):
|
|||||||
summary_parts[-1] += f" 等{len(ods_stats)}项"
|
summary_parts[-1] += f" 等{len(ods_stats)}项"
|
||||||
|
|
||||||
if dwd_stats:
|
if dwd_stats:
|
||||||
summary_parts.append("【DWD 装载】" + "; ".join(dwd_stats))
|
summary_parts.append("【DWD 装载】" + dwd_stats[0]) # 第一行是汇总
|
||||||
|
for detail in dwd_stats[1:]: # 后面是详情
|
||||||
|
summary_parts.append(detail)
|
||||||
|
|
||||||
if integrity_stats:
|
if integrity_stats:
|
||||||
total_missing = integrity_stats.get('final_missing', integrity_stats.get('total_missing', 0))
|
total_missing = integrity_stats.get('final_missing', integrity_stats.get('total_missing', 0))
|
||||||
|
|||||||
@@ -306,7 +306,8 @@ class ETLScheduler:
|
|||||||
|
|
||||||
def _build_fetch_dir(self, task_code: str, run_id: int) -> Path:
|
def _build_fetch_dir(self, task_code: str, run_id: int) -> Path:
|
||||||
ts = datetime.now(self.tz).strftime("%Y%m%d-%H%M%S")
|
ts = datetime.now(self.tz).strftime("%Y%m%d-%H%M%S")
|
||||||
return Path(self.fetch_root) / f"{task_code.upper()}-{run_id}-{ts}"
|
task_code = str(task_code or "").upper()
|
||||||
|
return Path(self.fetch_root) / task_code / f"{task_code}-{run_id}-{ts}"
|
||||||
|
|
||||||
def _resolve_ingest_source(self, fetch_dir: Path, fetch_stats: dict | None) -> Path:
|
def _resolve_ingest_source(self, fetch_dir: Path, fetch_stats: dict | None) -> Path:
|
||||||
if fetch_stats and fetch_dir.exists():
|
if fetch_stats and fetch_dir.exists():
|
||||||
@@ -357,8 +358,20 @@ class ETLScheduler:
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
# 创建任务实例(不需要 API client,使用 None)
|
# 创建任务实例(不需要 API client,使用 None)
|
||||||
|
api_client = None
|
||||||
|
if task_code == "ODS_JSON_ARCHIVE":
|
||||||
|
run_id = int(datetime.now(self.tz).timestamp())
|
||||||
|
fetch_dir = self._build_fetch_dir(task_code, run_id)
|
||||||
|
api_client = RecordingAPIClient(
|
||||||
|
base_client=self.api_client,
|
||||||
|
output_dir=fetch_dir,
|
||||||
|
task_code=task_code,
|
||||||
|
run_id=run_id,
|
||||||
|
write_pretty=self.write_pretty_json,
|
||||||
|
)
|
||||||
|
|
||||||
task = self.task_registry.create_task(
|
task = self.task_registry.create_task(
|
||||||
task_code, self.config, self.db_ops, None, self.logger
|
task_code, self.config, self.db_ops, api_client, self.logger
|
||||||
)
|
)
|
||||||
|
|
||||||
# 执行任务(工具类任务通常不需要 cursor_data)
|
# 执行任务(工具类任务通常不需要 cursor_data)
|
||||||
|
|||||||
@@ -28,6 +28,26 @@ from tasks.check_cutoff_task import CheckCutoffTask
|
|||||||
from tasks.init_dws_schema_task import InitDwsSchemaTask
|
from tasks.init_dws_schema_task import InitDwsSchemaTask
|
||||||
from tasks.dws_build_order_summary_task import DwsBuildOrderSummaryTask
|
from tasks.dws_build_order_summary_task import DwsBuildOrderSummaryTask
|
||||||
from tasks.data_integrity_task import DataIntegrityTask
|
from tasks.data_integrity_task import DataIntegrityTask
|
||||||
|
from tasks.seed_dws_config_task import SeedDwsConfigTask
|
||||||
|
|
||||||
|
# DWS 层任务导入
|
||||||
|
from tasks.dws import (
|
||||||
|
AssistantDailyTask,
|
||||||
|
AssistantMonthlyTask,
|
||||||
|
AssistantCustomerTask,
|
||||||
|
AssistantSalaryTask,
|
||||||
|
AssistantFinanceTask,
|
||||||
|
MemberConsumptionTask,
|
||||||
|
MemberVisitTask,
|
||||||
|
FinanceDailyTask,
|
||||||
|
FinanceRechargeTask,
|
||||||
|
FinanceIncomeStructureTask,
|
||||||
|
FinanceDiscountDetailTask,
|
||||||
|
DwsRetentionCleanupTask,
|
||||||
|
# 指数算法任务
|
||||||
|
RecallIndexTask,
|
||||||
|
IntimacyIndexTask,
|
||||||
|
)
|
||||||
|
|
||||||
class TaskRegistry:
|
class TaskRegistry:
|
||||||
"""任务注册和工厂"""
|
"""任务注册和工厂"""
|
||||||
@@ -81,6 +101,26 @@ default_registry.register("ODS_JSON_ARCHIVE", OdsJsonArchiveTask)
|
|||||||
default_registry.register("CHECK_CUTOFF", CheckCutoffTask)
|
default_registry.register("CHECK_CUTOFF", CheckCutoffTask)
|
||||||
default_registry.register("DATA_INTEGRITY_CHECK", DataIntegrityTask)
|
default_registry.register("DATA_INTEGRITY_CHECK", DataIntegrityTask)
|
||||||
default_registry.register("INIT_DWS_SCHEMA", InitDwsSchemaTask)
|
default_registry.register("INIT_DWS_SCHEMA", InitDwsSchemaTask)
|
||||||
|
default_registry.register("SEED_DWS_CONFIG", SeedDwsConfigTask)
|
||||||
default_registry.register("DWS_BUILD_ORDER_SUMMARY", DwsBuildOrderSummaryTask)
|
default_registry.register("DWS_BUILD_ORDER_SUMMARY", DwsBuildOrderSummaryTask)
|
||||||
|
|
||||||
|
# DWS 层业务任务
|
||||||
|
default_registry.register("DWS_ASSISTANT_DAILY", AssistantDailyTask)
|
||||||
|
default_registry.register("DWS_ASSISTANT_MONTHLY", AssistantMonthlyTask)
|
||||||
|
default_registry.register("DWS_ASSISTANT_CUSTOMER", AssistantCustomerTask)
|
||||||
|
default_registry.register("DWS_ASSISTANT_SALARY", AssistantSalaryTask)
|
||||||
|
default_registry.register("DWS_ASSISTANT_FINANCE", AssistantFinanceTask)
|
||||||
|
default_registry.register("DWS_MEMBER_CONSUMPTION", MemberConsumptionTask)
|
||||||
|
default_registry.register("DWS_MEMBER_VISIT", MemberVisitTask)
|
||||||
|
default_registry.register("DWS_FINANCE_DAILY", FinanceDailyTask)
|
||||||
|
default_registry.register("DWS_FINANCE_RECHARGE", FinanceRechargeTask)
|
||||||
|
default_registry.register("DWS_FINANCE_INCOME_STRUCTURE", FinanceIncomeStructureTask)
|
||||||
|
default_registry.register("DWS_FINANCE_DISCOUNT_DETAIL", FinanceDiscountDetailTask)
|
||||||
|
default_registry.register("DWS_RETENTION_CLEANUP", DwsRetentionCleanupTask)
|
||||||
|
|
||||||
|
# DWS 指数算法任务
|
||||||
|
default_registry.register("DWS_RECALL_INDEX", RecallIndexTask)
|
||||||
|
default_registry.register("DWS_INTIMACY_INDEX", IntimacyIndexTask)
|
||||||
|
|
||||||
for code, task_cls in ODS_TASK_CLASSES.items():
|
for code, task_cls in ODS_TASK_CLASSES.items():
|
||||||
default_registry.register(code, task_cls)
|
default_registry.register(code, task_cls)
|
||||||
|
|||||||
29540
etl_billiards/reports/data_integrity_window_20260201_175048.json
Normal file
29540
etl_billiards/reports/data_integrity_window_20260201_175048.json
Normal file
File diff suppressed because it is too large
Load Diff
331
etl_billiards/scheduled_tasks.json
Normal file
331
etl_billiards/scheduled_tasks.json
Normal file
File diff suppressed because one or more lines are too long
636
etl_billiards/scripts/analyze_discount_patterns.py
Normal file
636
etl_billiards/scripts/analyze_discount_patterns.py
Normal file
@@ -0,0 +1,636 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
优惠口径抽样分析脚本
|
||||||
|
|
||||||
|
功能说明:
|
||||||
|
从dwd_settlement_head表抽样100单,分析以下优惠字段的使用情况:
|
||||||
|
- adjust_amount: 台费打折/调整(可能包含大客户优惠、其他优惠)
|
||||||
|
- member_discount_amount: 会员折扣
|
||||||
|
- rounding_amount: 抹零金额
|
||||||
|
- coupon_amount: 团购抵消台费
|
||||||
|
- gift_card_amount: 赠送卡支付
|
||||||
|
|
||||||
|
分析目标:
|
||||||
|
1. 大客户优惠:是否存在"大客户"标识?如何与普通调整区分?
|
||||||
|
2. 会员折扣:是否有非零值?使用场景是什么?
|
||||||
|
3. 抹零:抹零规则?与adjust_amount的关系?
|
||||||
|
4. 其他优惠:adjust_amount中还包含哪些优惠类型?
|
||||||
|
|
||||||
|
输出:
|
||||||
|
- 控制台打印分析报告
|
||||||
|
- 生成 docs/analysis_discount_patterns.md 报告文件
|
||||||
|
|
||||||
|
作者:ETL团队
|
||||||
|
创建日期:2026-02-01
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
from datetime import datetime
|
||||||
|
from decimal import Decimal
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Any, Dict, List, Optional, Tuple
|
||||||
|
|
||||||
|
# 添加项目根目录到Python路径
|
||||||
|
project_root = Path(__file__).parent.parent.parent
|
||||||
|
sys.path.insert(0, str(project_root))
|
||||||
|
|
||||||
|
from etl_billiards.utils.config import Config
|
||||||
|
from etl_billiards.utils.db import DatabaseConnection
|
||||||
|
|
||||||
|
|
||||||
|
def analyze_discount_patterns():
|
||||||
|
"""
|
||||||
|
执行优惠口径抽样分析
|
||||||
|
"""
|
||||||
|
print("=" * 80)
|
||||||
|
print("优惠口径抽样分析")
|
||||||
|
print("=" * 80)
|
||||||
|
print()
|
||||||
|
|
||||||
|
# 加载配置和数据库连接
|
||||||
|
config = Config()
|
||||||
|
db = DatabaseConnection(config)
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 1. 获取总体统计
|
||||||
|
print("【1. 总体统计】")
|
||||||
|
print("-" * 40)
|
||||||
|
overall_stats = get_overall_stats(db)
|
||||||
|
print_overall_stats(overall_stats)
|
||||||
|
print()
|
||||||
|
|
||||||
|
# 2. 抽样分析优惠订单
|
||||||
|
print("【2. 有优惠的订单抽样分析(100单)】")
|
||||||
|
print("-" * 40)
|
||||||
|
sample_orders = get_sample_orders_with_discount(db, limit=100)
|
||||||
|
discount_analysis = analyze_sample_orders(sample_orders)
|
||||||
|
print_discount_analysis(discount_analysis)
|
||||||
|
print()
|
||||||
|
|
||||||
|
# 3. adjust_amount详细分析
|
||||||
|
print("【3. adjust_amount (台费打折/调整) 详细分析】")
|
||||||
|
print("-" * 40)
|
||||||
|
adjust_analysis = analyze_adjust_amount(db)
|
||||||
|
print_adjust_analysis(adjust_analysis)
|
||||||
|
print()
|
||||||
|
|
||||||
|
# 4. 会员折扣使用分析
|
||||||
|
print("【4. member_discount_amount (会员折扣) 使用分析】")
|
||||||
|
print("-" * 40)
|
||||||
|
member_discount_analysis = analyze_member_discount(db)
|
||||||
|
print_member_discount_analysis(member_discount_analysis)
|
||||||
|
print()
|
||||||
|
|
||||||
|
# 5. 抹零规则分析
|
||||||
|
print("【5. rounding_amount (抹零) 规则分析】")
|
||||||
|
print("-" * 40)
|
||||||
|
rounding_analysis = analyze_rounding(db)
|
||||||
|
print_rounding_analysis(rounding_analysis)
|
||||||
|
print()
|
||||||
|
|
||||||
|
# 6. 团购优惠分析
|
||||||
|
print("【6. 团购优惠分析】")
|
||||||
|
print("-" * 40)
|
||||||
|
groupbuy_analysis = analyze_groupbuy(db)
|
||||||
|
print_groupbuy_analysis(groupbuy_analysis)
|
||||||
|
print()
|
||||||
|
|
||||||
|
# 7. 生成分析报告
|
||||||
|
print("【7. 生成分析报告】")
|
||||||
|
print("-" * 40)
|
||||||
|
report = generate_report(
|
||||||
|
overall_stats,
|
||||||
|
discount_analysis,
|
||||||
|
adjust_analysis,
|
||||||
|
member_discount_analysis,
|
||||||
|
rounding_analysis,
|
||||||
|
groupbuy_analysis
|
||||||
|
)
|
||||||
|
|
||||||
|
# 保存报告
|
||||||
|
report_path = project_root / "etl_billiards" / "docs" / "analysis_discount_patterns.md"
|
||||||
|
with open(report_path, 'w', encoding='utf-8') as f:
|
||||||
|
f.write(report)
|
||||||
|
print(f"报告已保存到: {report_path}")
|
||||||
|
|
||||||
|
finally:
|
||||||
|
db.close()
|
||||||
|
|
||||||
|
|
||||||
|
def get_overall_stats(db: DatabaseConnection) -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
获取总体统计数据
|
||||||
|
"""
|
||||||
|
sql = """
|
||||||
|
SELECT
|
||||||
|
COUNT(*) AS total_orders,
|
||||||
|
COUNT(CASE WHEN adjust_amount != 0 THEN 1 END) AS orders_with_adjust,
|
||||||
|
COUNT(CASE WHEN member_discount_amount != 0 THEN 1 END) AS orders_with_member_discount,
|
||||||
|
COUNT(CASE WHEN rounding_amount != 0 THEN 1 END) AS orders_with_rounding,
|
||||||
|
COUNT(CASE WHEN coupon_amount != 0 THEN 1 END) AS orders_with_coupon,
|
||||||
|
COUNT(CASE WHEN gift_card_amount != 0 THEN 1 END) AS orders_with_gift_card,
|
||||||
|
SUM(adjust_amount) AS total_adjust,
|
||||||
|
SUM(member_discount_amount) AS total_member_discount,
|
||||||
|
SUM(rounding_amount) AS total_rounding,
|
||||||
|
SUM(coupon_amount) AS total_coupon,
|
||||||
|
SUM(gift_card_amount) AS total_gift_card,
|
||||||
|
SUM(consume_money) AS total_consume,
|
||||||
|
SUM(pay_amount) AS total_pay
|
||||||
|
FROM billiards_dwd.dwd_settlement_head
|
||||||
|
"""
|
||||||
|
rows = db.query(sql)
|
||||||
|
return dict(rows[0]) if rows else {}
|
||||||
|
|
||||||
|
|
||||||
|
def get_sample_orders_with_discount(
|
||||||
|
db: DatabaseConnection,
|
||||||
|
limit: int = 100
|
||||||
|
) -> List[Dict[str, Any]]:
|
||||||
|
"""
|
||||||
|
抽样获取有优惠的订单
|
||||||
|
"""
|
||||||
|
sql = """
|
||||||
|
SELECT
|
||||||
|
order_settle_id,
|
||||||
|
order_trade_no,
|
||||||
|
create_time,
|
||||||
|
consume_money,
|
||||||
|
pay_amount,
|
||||||
|
adjust_amount,
|
||||||
|
member_discount_amount,
|
||||||
|
rounding_amount,
|
||||||
|
coupon_amount,
|
||||||
|
gift_card_amount,
|
||||||
|
balance_amount,
|
||||||
|
recharge_card_amount,
|
||||||
|
pl_coupon_sale_amount,
|
||||||
|
table_charge_money,
|
||||||
|
goods_money,
|
||||||
|
assistant_pd_money,
|
||||||
|
assistant_cx_money,
|
||||||
|
consume_money - pay_amount - COALESCE(recharge_card_amount, 0)
|
||||||
|
- COALESCE(gift_card_amount, 0) - COALESCE(balance_amount, 0) AS calculated_discount
|
||||||
|
FROM billiards_dwd.dwd_settlement_head
|
||||||
|
WHERE adjust_amount != 0
|
||||||
|
OR member_discount_amount != 0
|
||||||
|
OR rounding_amount != 0
|
||||||
|
OR coupon_amount != 0
|
||||||
|
OR gift_card_amount != 0
|
||||||
|
ORDER BY RANDOM()
|
||||||
|
LIMIT %s
|
||||||
|
"""
|
||||||
|
rows = db.query(sql, (limit,))
|
||||||
|
return [dict(row) for row in rows] if rows else []
|
||||||
|
|
||||||
|
|
||||||
|
def analyze_sample_orders(orders: List[Dict[str, Any]]) -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
分析抽样订单
|
||||||
|
"""
|
||||||
|
analysis = {
|
||||||
|
'total_sampled': len(orders),
|
||||||
|
'with_adjust': 0,
|
||||||
|
'with_member_discount': 0,
|
||||||
|
'with_rounding': 0,
|
||||||
|
'with_coupon': 0,
|
||||||
|
'with_gift_card': 0,
|
||||||
|
'adjust_values': [],
|
||||||
|
'member_discount_values': [],
|
||||||
|
'rounding_values': [],
|
||||||
|
'coupon_values': [],
|
||||||
|
'gift_card_values': [],
|
||||||
|
}
|
||||||
|
|
||||||
|
for order in orders:
|
||||||
|
adjust = Decimal(str(order.get('adjust_amount', 0)))
|
||||||
|
member_discount = Decimal(str(order.get('member_discount_amount', 0)))
|
||||||
|
rounding = Decimal(str(order.get('rounding_amount', 0)))
|
||||||
|
coupon = Decimal(str(order.get('coupon_amount', 0)))
|
||||||
|
gift_card = Decimal(str(order.get('gift_card_amount', 0)))
|
||||||
|
|
||||||
|
if adjust != 0:
|
||||||
|
analysis['with_adjust'] += 1
|
||||||
|
analysis['adjust_values'].append(float(adjust))
|
||||||
|
if member_discount != 0:
|
||||||
|
analysis['with_member_discount'] += 1
|
||||||
|
analysis['member_discount_values'].append(float(member_discount))
|
||||||
|
if rounding != 0:
|
||||||
|
analysis['with_rounding'] += 1
|
||||||
|
analysis['rounding_values'].append(float(rounding))
|
||||||
|
if coupon != 0:
|
||||||
|
analysis['with_coupon'] += 1
|
||||||
|
analysis['coupon_values'].append(float(coupon))
|
||||||
|
if gift_card != 0:
|
||||||
|
analysis['with_gift_card'] += 1
|
||||||
|
analysis['gift_card_values'].append(float(gift_card))
|
||||||
|
|
||||||
|
return analysis
|
||||||
|
|
||||||
|
|
||||||
|
def analyze_adjust_amount(db: DatabaseConnection) -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
分析adjust_amount字段的分布和模式
|
||||||
|
"""
|
||||||
|
# 1. 值分布
|
||||||
|
sql_distribution = """
|
||||||
|
SELECT
|
||||||
|
CASE
|
||||||
|
WHEN adjust_amount = 0 THEN '0'
|
||||||
|
WHEN adjust_amount > 0 AND adjust_amount <= 10 THEN '0-10'
|
||||||
|
WHEN adjust_amount > 10 AND adjust_amount <= 50 THEN '10-50'
|
||||||
|
WHEN adjust_amount > 50 AND adjust_amount <= 100 THEN '50-100'
|
||||||
|
WHEN adjust_amount > 100 AND adjust_amount <= 500 THEN '100-500'
|
||||||
|
WHEN adjust_amount > 500 THEN '>500'
|
||||||
|
WHEN adjust_amount < 0 AND adjust_amount >= -10 THEN '-10-0'
|
||||||
|
WHEN adjust_amount < -10 AND adjust_amount >= -50 THEN '-50--10'
|
||||||
|
WHEN adjust_amount < -50 AND adjust_amount >= -100 THEN '-100--50'
|
||||||
|
WHEN adjust_amount < -100 THEN '<-100'
|
||||||
|
END AS range,
|
||||||
|
COUNT(*) AS count,
|
||||||
|
SUM(adjust_amount) AS total_amount
|
||||||
|
FROM billiards_dwd.dwd_settlement_head
|
||||||
|
WHERE adjust_amount != 0
|
||||||
|
GROUP BY range
|
||||||
|
ORDER BY range
|
||||||
|
"""
|
||||||
|
distribution = db.query(sql_distribution)
|
||||||
|
|
||||||
|
# 2. 与消费金额的关系
|
||||||
|
sql_ratio = """
|
||||||
|
SELECT
|
||||||
|
ROUND(adjust_amount / NULLIF(consume_money, 0) * 100, 2) AS discount_ratio,
|
||||||
|
COUNT(*) AS count
|
||||||
|
FROM billiards_dwd.dwd_settlement_head
|
||||||
|
WHERE adjust_amount != 0 AND consume_money > 0
|
||||||
|
GROUP BY discount_ratio
|
||||||
|
ORDER BY count DESC
|
||||||
|
LIMIT 20
|
||||||
|
"""
|
||||||
|
ratio_distribution = db.query(sql_ratio)
|
||||||
|
|
||||||
|
# 3. 典型样本
|
||||||
|
sql_samples = """
|
||||||
|
SELECT
|
||||||
|
order_settle_id,
|
||||||
|
consume_money,
|
||||||
|
adjust_amount,
|
||||||
|
ROUND(adjust_amount / NULLIF(consume_money, 0) * 100, 2) AS ratio
|
||||||
|
FROM billiards_dwd.dwd_settlement_head
|
||||||
|
WHERE adjust_amount != 0
|
||||||
|
ORDER BY ABS(adjust_amount) DESC
|
||||||
|
LIMIT 10
|
||||||
|
"""
|
||||||
|
samples = db.query(sql_samples)
|
||||||
|
|
||||||
|
return {
|
||||||
|
'distribution': [dict(r) for r in distribution] if distribution else [],
|
||||||
|
'ratio_distribution': [dict(r) for r in ratio_distribution] if ratio_distribution else [],
|
||||||
|
'top_samples': [dict(r) for r in samples] if samples else []
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def analyze_member_discount(db: DatabaseConnection) -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
分析member_discount_amount字段的使用情况
|
||||||
|
"""
|
||||||
|
sql = """
|
||||||
|
SELECT
|
||||||
|
COUNT(*) AS total_orders,
|
||||||
|
COUNT(CASE WHEN member_discount_amount != 0 THEN 1 END) AS with_discount,
|
||||||
|
SUM(member_discount_amount) AS total_discount,
|
||||||
|
AVG(CASE WHEN member_discount_amount != 0 THEN member_discount_amount END) AS avg_discount,
|
||||||
|
MAX(member_discount_amount) AS max_discount,
|
||||||
|
MIN(CASE WHEN member_discount_amount != 0 THEN member_discount_amount END) AS min_discount
|
||||||
|
FROM billiards_dwd.dwd_settlement_head
|
||||||
|
"""
|
||||||
|
rows = db.query(sql)
|
||||||
|
stats = dict(rows[0]) if rows else {}
|
||||||
|
|
||||||
|
# 抽样有会员折扣的订单
|
||||||
|
sql_samples = """
|
||||||
|
SELECT
|
||||||
|
order_settle_id,
|
||||||
|
member_id,
|
||||||
|
consume_money,
|
||||||
|
member_discount_amount,
|
||||||
|
ROUND(member_discount_amount / NULLIF(consume_money, 0) * 100, 2) AS ratio
|
||||||
|
FROM billiards_dwd.dwd_settlement_head
|
||||||
|
WHERE member_discount_amount != 0
|
||||||
|
LIMIT 20
|
||||||
|
"""
|
||||||
|
samples = db.query(sql_samples)
|
||||||
|
|
||||||
|
return {
|
||||||
|
'stats': stats,
|
||||||
|
'samples': [dict(r) for r in samples] if samples else []
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def analyze_rounding(db: DatabaseConnection) -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
分析rounding_amount字段的规则
|
||||||
|
"""
|
||||||
|
# 1. 抹零金额分布
|
||||||
|
sql_distribution = """
|
||||||
|
SELECT
|
||||||
|
rounding_amount,
|
||||||
|
COUNT(*) AS count
|
||||||
|
FROM billiards_dwd.dwd_settlement_head
|
||||||
|
WHERE rounding_amount != 0
|
||||||
|
GROUP BY rounding_amount
|
||||||
|
ORDER BY count DESC
|
||||||
|
LIMIT 20
|
||||||
|
"""
|
||||||
|
distribution = db.query(sql_distribution)
|
||||||
|
|
||||||
|
# 2. 抹零与实付金额的关系
|
||||||
|
sql_pattern = """
|
||||||
|
SELECT
|
||||||
|
pay_amount,
|
||||||
|
rounding_amount,
|
||||||
|
pay_amount + rounding_amount AS before_rounding,
|
||||||
|
MOD(CAST((pay_amount + rounding_amount) * 100 AS INTEGER), 100) AS cents
|
||||||
|
FROM billiards_dwd.dwd_settlement_head
|
||||||
|
WHERE rounding_amount != 0
|
||||||
|
LIMIT 20
|
||||||
|
"""
|
||||||
|
patterns = db.query(sql_pattern)
|
||||||
|
|
||||||
|
return {
|
||||||
|
'distribution': [dict(r) for r in distribution] if distribution else [],
|
||||||
|
'patterns': [dict(r) for r in patterns] if patterns else []
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def analyze_groupbuy(db: DatabaseConnection) -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
分析团购优惠
|
||||||
|
"""
|
||||||
|
# 1. 团购使用统计
|
||||||
|
sql_stats = """
|
||||||
|
SELECT
|
||||||
|
COUNT(*) AS total_orders,
|
||||||
|
COUNT(CASE WHEN coupon_amount != 0 THEN 1 END) AS with_coupon,
|
||||||
|
COUNT(CASE WHEN pl_coupon_sale_amount != 0 THEN 1 END) AS with_pl_coupon,
|
||||||
|
SUM(coupon_amount) AS total_coupon_amount,
|
||||||
|
SUM(pl_coupon_sale_amount) AS total_pl_coupon_sale
|
||||||
|
FROM billiards_dwd.dwd_settlement_head
|
||||||
|
"""
|
||||||
|
stats = db.query(sql_stats)
|
||||||
|
|
||||||
|
# 2. 团购订单样本
|
||||||
|
sql_samples = """
|
||||||
|
SELECT
|
||||||
|
sh.order_settle_id,
|
||||||
|
sh.coupon_amount,
|
||||||
|
sh.pl_coupon_sale_amount,
|
||||||
|
gr.ledger_amount AS groupbuy_ledger_amount,
|
||||||
|
gr.ledger_unit_price AS groupbuy_unit_price
|
||||||
|
FROM billiards_dwd.dwd_settlement_head sh
|
||||||
|
LEFT JOIN billiards_dwd.dwd_groupbuy_redemption gr
|
||||||
|
ON sh.order_settle_id = gr.order_settle_id
|
||||||
|
WHERE sh.coupon_amount != 0
|
||||||
|
LIMIT 20
|
||||||
|
"""
|
||||||
|
samples = db.query(sql_samples)
|
||||||
|
|
||||||
|
return {
|
||||||
|
'stats': dict(stats[0]) if stats else {},
|
||||||
|
'samples': [dict(r) for r in samples] if samples else []
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def print_overall_stats(stats: Dict[str, Any]):
|
||||||
|
"""打印总体统计"""
|
||||||
|
total = stats.get('total_orders', 0)
|
||||||
|
print(f"总订单数: {total:,}")
|
||||||
|
print(f"有adjust_amount的订单: {stats.get('orders_with_adjust', 0):,} ({stats.get('orders_with_adjust', 0)/total*100:.2f}%)")
|
||||||
|
print(f"有member_discount的订单: {stats.get('orders_with_member_discount', 0):,} ({stats.get('orders_with_member_discount', 0)/total*100:.2f}%)")
|
||||||
|
print(f"有rounding的订单: {stats.get('orders_with_rounding', 0):,} ({stats.get('orders_with_rounding', 0)/total*100:.2f}%)")
|
||||||
|
print(f"有coupon的订单: {stats.get('orders_with_coupon', 0):,} ({stats.get('orders_with_coupon', 0)/total*100:.2f}%)")
|
||||||
|
print(f"有gift_card的订单: {stats.get('orders_with_gift_card', 0):,} ({stats.get('orders_with_gift_card', 0)/total*100:.2f}%)")
|
||||||
|
print()
|
||||||
|
print(f"adjust_amount总额: {stats.get('total_adjust', 0):,.2f}")
|
||||||
|
print(f"member_discount总额: {stats.get('total_member_discount', 0):,.2f}")
|
||||||
|
print(f"rounding总额: {stats.get('total_rounding', 0):,.2f}")
|
||||||
|
print(f"coupon总额: {stats.get('total_coupon', 0):,.2f}")
|
||||||
|
print(f"gift_card总额: {stats.get('total_gift_card', 0):,.2f}")
|
||||||
|
|
||||||
|
|
||||||
|
def print_discount_analysis(analysis: Dict[str, Any]):
|
||||||
|
"""打印抽样分析结果"""
|
||||||
|
print(f"抽样订单数: {analysis['total_sampled']}")
|
||||||
|
print(f" - 有adjust_amount: {analysis['with_adjust']}")
|
||||||
|
print(f" - 有member_discount: {analysis['with_member_discount']}")
|
||||||
|
print(f" - 有rounding: {analysis['with_rounding']}")
|
||||||
|
print(f" - 有coupon: {analysis['with_coupon']}")
|
||||||
|
print(f" - 有gift_card: {analysis['with_gift_card']}")
|
||||||
|
|
||||||
|
|
||||||
|
def print_adjust_analysis(analysis: Dict[str, Any]):
|
||||||
|
"""打印adjust_amount分析结果"""
|
||||||
|
print("值分布:")
|
||||||
|
for item in analysis.get('distribution', []):
|
||||||
|
print(f" {item.get('range', 'N/A')}: {item.get('count', 0):,} 单, 总额 {item.get('total_amount', 0):,.2f}")
|
||||||
|
|
||||||
|
print("\n折扣比例分布 (Top 10):")
|
||||||
|
for item in analysis.get('ratio_distribution', [])[:10]:
|
||||||
|
print(f" {item.get('discount_ratio', 0)}%: {item.get('count', 0):,} 单")
|
||||||
|
|
||||||
|
print("\n大额调整样本 (Top 10):")
|
||||||
|
for item in analysis.get('top_samples', []):
|
||||||
|
print(f" 订单{item.get('order_settle_id')}: 消费{item.get('consume_money', 0):,.2f}, 调整{item.get('adjust_amount', 0):,.2f} ({item.get('ratio', 0)}%)")
|
||||||
|
|
||||||
|
|
||||||
|
def print_member_discount_analysis(analysis: Dict[str, Any]):
|
||||||
|
"""打印会员折扣分析结果"""
|
||||||
|
stats = analysis.get('stats', {})
|
||||||
|
print(f"总订单数: {stats.get('total_orders', 0):,}")
|
||||||
|
print(f"有会员折扣的订单: {stats.get('with_discount', 0):,}")
|
||||||
|
print(f"会员折扣总额: {stats.get('total_discount', 0):,.2f}")
|
||||||
|
print(f"平均折扣: {stats.get('avg_discount', 0):,.2f}")
|
||||||
|
print(f"最大折扣: {stats.get('max_discount', 0):,.2f}")
|
||||||
|
|
||||||
|
samples = analysis.get('samples', [])
|
||||||
|
if samples:
|
||||||
|
print("\n样本订单:")
|
||||||
|
for item in samples[:5]:
|
||||||
|
print(f" 订单{item.get('order_settle_id')}: 会员{item.get('member_id')}, 消费{item.get('consume_money', 0):,.2f}, 折扣{item.get('member_discount_amount', 0):,.2f} ({item.get('ratio', 0)}%)")
|
||||||
|
else:
|
||||||
|
print("\n[!] 未发现使用会员折扣的订单,该字段可能未启用")
|
||||||
|
|
||||||
|
|
||||||
|
def print_rounding_analysis(analysis: Dict[str, Any]):
|
||||||
|
"""打印抹零分析结果"""
|
||||||
|
print("抹零金额分布:")
|
||||||
|
for item in analysis.get('distribution', []):
|
||||||
|
print(f" {item.get('rounding_amount', 0):,.2f}: {item.get('count', 0):,} 单")
|
||||||
|
|
||||||
|
print("\n抹零模式样本:")
|
||||||
|
for item in analysis.get('patterns', [])[:5]:
|
||||||
|
print(f" 实付{item.get('pay_amount', 0):,.2f} + 抹零{item.get('rounding_amount', 0):,.2f} = {item.get('before_rounding', 0):,.2f}")
|
||||||
|
|
||||||
|
|
||||||
|
def print_groupbuy_analysis(analysis: Dict[str, Any]):
|
||||||
|
"""打印团购分析结果"""
|
||||||
|
stats = analysis.get('stats', {})
|
||||||
|
print(f"总订单数: {stats.get('total_orders', 0):,}")
|
||||||
|
print(f"有coupon_amount的订单: {stats.get('with_coupon', 0):,}")
|
||||||
|
print(f"有pl_coupon_sale_amount的订单: {stats.get('with_pl_coupon', 0):,}")
|
||||||
|
print(f"coupon_amount总额: {stats.get('total_coupon_amount', 0):,.2f}")
|
||||||
|
print(f"pl_coupon_sale_amount总额: {stats.get('total_pl_coupon_sale', 0):,.2f}")
|
||||||
|
|
||||||
|
print("\n团购订单样本:")
|
||||||
|
for item in analysis.get('samples', [])[:5]:
|
||||||
|
print(f" 订单{item.get('order_settle_id')}: coupon={item.get('coupon_amount', 0):,.2f}, pl_coupon={item.get('pl_coupon_sale_amount', 0):,.2f}, groupbuy_price={item.get('groupbuy_unit_price', 'N/A')}")
|
||||||
|
|
||||||
|
|
||||||
|
def generate_report(
|
||||||
|
overall_stats: Dict[str, Any],
|
||||||
|
discount_analysis: Dict[str, Any],
|
||||||
|
adjust_analysis: Dict[str, Any],
|
||||||
|
member_discount_analysis: Dict[str, Any],
|
||||||
|
rounding_analysis: Dict[str, Any],
|
||||||
|
groupbuy_analysis: Dict[str, Any]
|
||||||
|
) -> str:
|
||||||
|
"""
|
||||||
|
生成Markdown格式的分析报告
|
||||||
|
"""
|
||||||
|
now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||||
|
total = overall_stats.get('total_orders', 1)
|
||||||
|
|
||||||
|
report = f"""# 优惠口径抽样分析报告
|
||||||
|
|
||||||
|
**生成时间**: {now}
|
||||||
|
|
||||||
|
## 一、总体统计
|
||||||
|
|
||||||
|
| 指标 | 数值 | 占比 |
|
||||||
|
|------|------|------|
|
||||||
|
| 总订单数 | {overall_stats.get('total_orders', 0):,} | 100% |
|
||||||
|
| 有adjust_amount的订单 | {overall_stats.get('orders_with_adjust', 0):,} | {overall_stats.get('orders_with_adjust', 0)/total*100:.2f}% |
|
||||||
|
| 有member_discount的订单 | {overall_stats.get('orders_with_member_discount', 0):,} | {overall_stats.get('orders_with_member_discount', 0)/total*100:.2f}% |
|
||||||
|
| 有rounding的订单 | {overall_stats.get('orders_with_rounding', 0):,} | {overall_stats.get('orders_with_rounding', 0)/total*100:.2f}% |
|
||||||
|
| 有coupon的订单 | {overall_stats.get('orders_with_coupon', 0):,} | {overall_stats.get('orders_with_coupon', 0)/total*100:.2f}% |
|
||||||
|
| 有gift_card的订单 | {overall_stats.get('orders_with_gift_card', 0):,} | {overall_stats.get('orders_with_gift_card', 0)/total*100:.2f}% |
|
||||||
|
|
||||||
|
### 金额统计
|
||||||
|
|
||||||
|
| 优惠类型 | 总额 |
|
||||||
|
|----------|------|
|
||||||
|
| adjust_amount (台费打折/调整) | {overall_stats.get('total_adjust', 0):,.2f} |
|
||||||
|
| member_discount_amount (会员折扣) | {overall_stats.get('total_member_discount', 0):,.2f} |
|
||||||
|
| rounding_amount (抹零) | {overall_stats.get('total_rounding', 0):,.2f} |
|
||||||
|
| coupon_amount (团购抵消台费) | {overall_stats.get('total_coupon', 0):,.2f} |
|
||||||
|
| gift_card_amount (赠送卡支付) | {overall_stats.get('total_gift_card', 0):,.2f} |
|
||||||
|
|
||||||
|
## 二、adjust_amount (台费打折/调整) 分析
|
||||||
|
|
||||||
|
### 值分布
|
||||||
|
|
||||||
|
| 区间 | 订单数 | 总额 |
|
||||||
|
|------|--------|------|
|
||||||
|
"""
|
||||||
|
|
||||||
|
for item in adjust_analysis.get('distribution', []):
|
||||||
|
report += f"| {item.get('range', 'N/A')} | {item.get('count', 0):,} | {item.get('total_amount', 0):,.2f} |\n"
|
||||||
|
|
||||||
|
report += """
|
||||||
|
### 分析结论
|
||||||
|
|
||||||
|
- **是否包含大客户优惠**: 需要进一步分析adjust_amount的业务来源
|
||||||
|
- **与普通调整的区分**: 建议查看是否有备注字段或关联的优惠活动表
|
||||||
|
|
||||||
|
## 三、member_discount_amount (会员折扣) 分析
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
member_stats = member_discount_analysis.get('stats', {})
|
||||||
|
with_discount = member_stats.get('with_discount', 0)
|
||||||
|
|
||||||
|
if with_discount == 0:
|
||||||
|
report += """### 结论
|
||||||
|
|
||||||
|
**[!] 该字段未发现任何非零值,会员折扣功能可能未启用。**
|
||||||
|
|
||||||
|
建议:在DWS财务统计中,可以暂时忽略此字段,或将其标记为"待启用"。
|
||||||
|
"""
|
||||||
|
else:
|
||||||
|
report += f"""### 使用统计
|
||||||
|
|
||||||
|
| 指标 | 数值 |
|
||||||
|
|------|------|
|
||||||
|
| 有会员折扣的订单 | {with_discount:,} |
|
||||||
|
| 会员折扣总额 | {member_stats.get('total_discount', 0):,.2f} |
|
||||||
|
| 平均折扣 | {member_stats.get('avg_discount', 0):,.2f} |
|
||||||
|
| 最大折扣 | {member_stats.get('max_discount', 0):,.2f} |
|
||||||
|
"""
|
||||||
|
|
||||||
|
report += """
|
||||||
|
## 四、rounding_amount (抹零) 分析
|
||||||
|
|
||||||
|
### 抹零金额分布
|
||||||
|
|
||||||
|
| 抹零金额 | 订单数 |
|
||||||
|
|----------|--------|
|
||||||
|
"""
|
||||||
|
|
||||||
|
for item in rounding_analysis.get('distribution', [])[:10]:
|
||||||
|
report += f"| {item.get('rounding_amount', 0):,.2f} | {item.get('count', 0):,} |\n"
|
||||||
|
|
||||||
|
report += """
|
||||||
|
### 抹零规则推断
|
||||||
|
|
||||||
|
根据抹零金额分布,推断抹零规则为:
|
||||||
|
- 抹零到整元(去除角分)
|
||||||
|
- 或抹零到特定尾数
|
||||||
|
|
||||||
|
## 五、团购优惠分析
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
groupbuy_stats = groupbuy_analysis.get('stats', {})
|
||||||
|
report += f"""### 使用统计
|
||||||
|
|
||||||
|
| 指标 | 数值 |
|
||||||
|
|------|------|
|
||||||
|
| 有coupon_amount的订单 | {groupbuy_stats.get('with_coupon', 0):,} |
|
||||||
|
| 有pl_coupon_sale_amount的订单 | {groupbuy_stats.get('with_pl_coupon', 0):,} |
|
||||||
|
| coupon_amount总额 | {groupbuy_stats.get('total_coupon_amount', 0):,.2f} |
|
||||||
|
| pl_coupon_sale_amount总额 | {groupbuy_stats.get('total_pl_coupon_sale', 0):,.2f} |
|
||||||
|
|
||||||
|
### 团购支付金额计算路径
|
||||||
|
|
||||||
|
根据分析,团购支付金额应按以下路径计算:
|
||||||
|
1. 若 `pl_coupon_sale_amount ≠ 0` → 使用 `pl_coupon_sale_amount`
|
||||||
|
2. 若 `pl_coupon_sale_amount = 0` 且 `coupon_amount ≠ 0` → 通过 `order_settle_id` 关联 `dwd_groupbuy_redemption` 获取 `ledger_unit_price`
|
||||||
|
|
||||||
|
团购优惠金额 = coupon_amount - 团购支付金额
|
||||||
|
|
||||||
|
## 六、建议与结论
|
||||||
|
|
||||||
|
### 优惠口径定义建议
|
||||||
|
|
||||||
|
| 优惠类型 | 字段来源 | 计算公式 | 状态 |
|
||||||
|
|----------|----------|----------|------|
|
||||||
|
| 团购优惠 | settlement + groupbuy | coupon_amount - 团购支付金额 | 可用 |
|
||||||
|
| 会员折扣 | settlement.member_discount_amount | 直接取值 | 待确认 |
|
||||||
|
| 赠送卡抵扣 | settlement.gift_card_amount | 直接取值 | 可用 |
|
||||||
|
| 手动调整 | settlement.adjust_amount | 直接取值 | 可用 |
|
||||||
|
| 抹零 | settlement.rounding_amount | 直接取值 | 可用 |
|
||||||
|
| 大客户优惠 | 待分析 | 需要业务确认 | 待定义 |
|
||||||
|
| 其他优惠 | 待分析 | 需要业务确认 | 待定义 |
|
||||||
|
|
||||||
|
### 下一步行动
|
||||||
|
|
||||||
|
1. **确认会员折扣是否启用**: 与业务确认member_discount_amount的使用场景
|
||||||
|
2. **大客户优惠识别规则**: 与业务确认如何从adjust_amount中识别大客户优惠
|
||||||
|
3. **其他优惠分类**: 与业务确认adjust_amount中还包含哪些优惠类型
|
||||||
|
"""
|
||||||
|
|
||||||
|
return report
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
analyze_discount_patterns()
|
||||||
287
etl_billiards/scripts/analyze_member_discount_usage.py
Normal file
287
etl_billiards/scripts/analyze_member_discount_usage.py
Normal file
@@ -0,0 +1,287 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
会员折扣启用分析脚本
|
||||||
|
|
||||||
|
功能说明:
|
||||||
|
确认 dwd_settlement_head.member_discount_amount 字段是否已启用
|
||||||
|
|
||||||
|
分析内容:
|
||||||
|
1. 统计非零记录数
|
||||||
|
2. 按时间分布分析
|
||||||
|
3. 按会员类型分析
|
||||||
|
4. 与其他字段的关联分析
|
||||||
|
|
||||||
|
输出:
|
||||||
|
- 控制台打印分析结果
|
||||||
|
- 结论:字段是否已启用,使用场景
|
||||||
|
|
||||||
|
作者:ETL团队
|
||||||
|
创建日期:2026-02-01
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
from datetime import datetime
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Any, Dict, List
|
||||||
|
|
||||||
|
# 添加项目根目录到Python路径
|
||||||
|
project_root = Path(__file__).parent.parent.parent
|
||||||
|
sys.path.insert(0, str(project_root))
|
||||||
|
|
||||||
|
from etl_billiards.utils.config import Config
|
||||||
|
from etl_billiards.utils.db import DatabaseConnection
|
||||||
|
|
||||||
|
|
||||||
|
def analyze_member_discount_usage():
|
||||||
|
"""
|
||||||
|
执行会员折扣启用分析
|
||||||
|
"""
|
||||||
|
print("=" * 80)
|
||||||
|
print("会员折扣启用分析 (member_discount_amount)")
|
||||||
|
print("=" * 80)
|
||||||
|
print()
|
||||||
|
|
||||||
|
# 加载配置和数据库连接
|
||||||
|
config = Config()
|
||||||
|
db = DatabaseConnection(config)
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 1. 基础统计
|
||||||
|
print("【1. 基础统计】")
|
||||||
|
print("-" * 40)
|
||||||
|
basic_stats = get_basic_stats(db)
|
||||||
|
print_basic_stats(basic_stats)
|
||||||
|
print()
|
||||||
|
|
||||||
|
# 2. 时间分布分析
|
||||||
|
print("【2. 时间分布分析】")
|
||||||
|
print("-" * 40)
|
||||||
|
time_distribution = get_time_distribution(db)
|
||||||
|
print_time_distribution(time_distribution)
|
||||||
|
print()
|
||||||
|
|
||||||
|
# 3. 会员类型分析
|
||||||
|
print("【3. 与会员的关联分析】")
|
||||||
|
print("-" * 40)
|
||||||
|
member_analysis = get_member_analysis(db)
|
||||||
|
print_member_analysis(member_analysis)
|
||||||
|
print()
|
||||||
|
|
||||||
|
# 4. 样本数据
|
||||||
|
print("【4. 样本数据】")
|
||||||
|
print("-" * 40)
|
||||||
|
samples = get_sample_data(db)
|
||||||
|
print_samples(samples)
|
||||||
|
print()
|
||||||
|
|
||||||
|
# 5. 结论
|
||||||
|
print("【5. 分析结论】")
|
||||||
|
print("-" * 40)
|
||||||
|
print_conclusion(basic_stats)
|
||||||
|
|
||||||
|
finally:
|
||||||
|
db.close()
|
||||||
|
|
||||||
|
|
||||||
|
def get_basic_stats(db: DatabaseConnection) -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
获取基础统计数据
|
||||||
|
"""
|
||||||
|
sql = """
|
||||||
|
SELECT
|
||||||
|
COUNT(*) AS total_orders,
|
||||||
|
COUNT(CASE WHEN member_discount_amount != 0 THEN 1 END) AS with_member_discount,
|
||||||
|
COUNT(CASE WHEN member_discount_amount > 0 THEN 1 END) AS positive_discount,
|
||||||
|
COUNT(CASE WHEN member_discount_amount < 0 THEN 1 END) AS negative_discount,
|
||||||
|
SUM(member_discount_amount) AS total_member_discount,
|
||||||
|
AVG(CASE WHEN member_discount_amount != 0 THEN member_discount_amount END) AS avg_discount,
|
||||||
|
MAX(member_discount_amount) AS max_discount,
|
||||||
|
MIN(member_discount_amount) AS min_discount,
|
||||||
|
STDDEV(CASE WHEN member_discount_amount != 0 THEN member_discount_amount END) AS stddev_discount
|
||||||
|
FROM billiards_dwd.dwd_settlement_head
|
||||||
|
"""
|
||||||
|
rows = db.query(sql)
|
||||||
|
return dict(rows[0]) if rows else {}
|
||||||
|
|
||||||
|
|
||||||
|
def get_time_distribution(db: DatabaseConnection) -> List[Dict[str, Any]]:
|
||||||
|
"""
|
||||||
|
获取按月份的时间分布
|
||||||
|
"""
|
||||||
|
sql = """
|
||||||
|
SELECT
|
||||||
|
DATE_TRUNC('month', create_time)::DATE AS month,
|
||||||
|
COUNT(*) AS total_orders,
|
||||||
|
COUNT(CASE WHEN member_discount_amount != 0 THEN 1 END) AS with_discount,
|
||||||
|
SUM(member_discount_amount) AS total_discount
|
||||||
|
FROM billiards_dwd.dwd_settlement_head
|
||||||
|
GROUP BY DATE_TRUNC('month', create_time)
|
||||||
|
ORDER BY month DESC
|
||||||
|
LIMIT 12
|
||||||
|
"""
|
||||||
|
rows = db.query(sql)
|
||||||
|
return [dict(row) for row in rows] if rows else []
|
||||||
|
|
||||||
|
|
||||||
|
def get_member_analysis(db: DatabaseConnection) -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
分析与会员的关联
|
||||||
|
"""
|
||||||
|
# 会员vs非会员
|
||||||
|
sql_member_vs_guest = """
|
||||||
|
SELECT
|
||||||
|
CASE WHEN member_id = 0 THEN '散客' ELSE '会员' END AS customer_type,
|
||||||
|
COUNT(*) AS total_orders,
|
||||||
|
COUNT(CASE WHEN member_discount_amount != 0 THEN 1 END) AS with_discount,
|
||||||
|
SUM(member_discount_amount) AS total_discount
|
||||||
|
FROM billiards_dwd.dwd_settlement_head
|
||||||
|
GROUP BY CASE WHEN member_id = 0 THEN '散客' ELSE '会员' END
|
||||||
|
"""
|
||||||
|
member_vs_guest = db.query(sql_member_vs_guest)
|
||||||
|
|
||||||
|
# 按会员卡等级
|
||||||
|
sql_by_grade = """
|
||||||
|
SELECT
|
||||||
|
COALESCE(m.member_card_grade_name, '未知') AS grade_name,
|
||||||
|
COUNT(*) AS total_orders,
|
||||||
|
COUNT(CASE WHEN sh.member_discount_amount != 0 THEN 1 END) AS with_discount,
|
||||||
|
SUM(sh.member_discount_amount) AS total_discount
|
||||||
|
FROM billiards_dwd.dwd_settlement_head sh
|
||||||
|
LEFT JOIN billiards_dwd.dim_member m ON sh.member_id = m.member_id
|
||||||
|
WHERE sh.member_id != 0
|
||||||
|
GROUP BY COALESCE(m.member_card_grade_name, '未知')
|
||||||
|
ORDER BY total_orders DESC
|
||||||
|
"""
|
||||||
|
by_grade = db.query(sql_by_grade)
|
||||||
|
|
||||||
|
return {
|
||||||
|
'member_vs_guest': [dict(row) for row in member_vs_guest] if member_vs_guest else [],
|
||||||
|
'by_grade': [dict(row) for row in by_grade] if by_grade else []
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def get_sample_data(db: DatabaseConnection) -> List[Dict[str, Any]]:
|
||||||
|
"""
|
||||||
|
获取有会员折扣的样本数据
|
||||||
|
"""
|
||||||
|
sql = """
|
||||||
|
SELECT
|
||||||
|
sh.order_settle_id,
|
||||||
|
sh.order_trade_no,
|
||||||
|
sh.create_time,
|
||||||
|
sh.member_id,
|
||||||
|
m.nickname AS member_name,
|
||||||
|
m.member_card_grade_name,
|
||||||
|
sh.consume_money,
|
||||||
|
sh.pay_amount,
|
||||||
|
sh.member_discount_amount,
|
||||||
|
ROUND(sh.member_discount_amount / NULLIF(sh.consume_money, 0) * 100, 2) AS discount_ratio
|
||||||
|
FROM billiards_dwd.dwd_settlement_head sh
|
||||||
|
LEFT JOIN billiards_dwd.dim_member m ON sh.member_id = m.member_id
|
||||||
|
WHERE sh.member_discount_amount != 0
|
||||||
|
ORDER BY sh.create_time DESC
|
||||||
|
LIMIT 20
|
||||||
|
"""
|
||||||
|
rows = db.query(sql)
|
||||||
|
return [dict(row) for row in rows] if rows else []
|
||||||
|
|
||||||
|
|
||||||
|
def print_basic_stats(stats: Dict[str, Any]):
|
||||||
|
"""打印基础统计"""
|
||||||
|
total = stats.get('total_orders', 1)
|
||||||
|
with_discount = stats.get('with_member_discount', 0)
|
||||||
|
|
||||||
|
print(f"总订单数: {total:,}")
|
||||||
|
print(f"有会员折扣的订单: {with_discount:,} ({with_discount/total*100:.4f}%)")
|
||||||
|
print(f" - 正值(折扣): {stats.get('positive_discount', 0):,}")
|
||||||
|
print(f" - 负值(加价?): {stats.get('negative_discount', 0):,}")
|
||||||
|
print()
|
||||||
|
print(f"会员折扣总额: {stats.get('total_member_discount', 0):,.2f}")
|
||||||
|
print(f"平均折扣: {stats.get('avg_discount', 0) or 0:,.2f}")
|
||||||
|
print(f"最大折扣: {stats.get('max_discount', 0):,.2f}")
|
||||||
|
print(f"最小折扣: {stats.get('min_discount', 0):,.2f}")
|
||||||
|
|
||||||
|
|
||||||
|
def print_time_distribution(distribution: List[Dict[str, Any]]):
|
||||||
|
"""打印时间分布"""
|
||||||
|
if not distribution:
|
||||||
|
print("无数据")
|
||||||
|
return
|
||||||
|
|
||||||
|
print(f"{'月份':<12} {'总订单':>10} {'有折扣':>10} {'折扣总额':>15}")
|
||||||
|
print("-" * 50)
|
||||||
|
for item in distribution:
|
||||||
|
month = str(item.get('month', 'N/A'))[:7]
|
||||||
|
total = item.get('total_orders', 0)
|
||||||
|
with_discount = item.get('with_discount', 0)
|
||||||
|
total_discount = item.get('total_discount', 0)
|
||||||
|
print(f"{month:<12} {total:>10,} {with_discount:>10,} {total_discount:>15,.2f}")
|
||||||
|
|
||||||
|
|
||||||
|
def print_member_analysis(analysis: Dict[str, Any]):
|
||||||
|
"""打印会员分析"""
|
||||||
|
print("会员 vs 散客:")
|
||||||
|
for item in analysis.get('member_vs_guest', []):
|
||||||
|
print(f" {item.get('customer_type', 'N/A')}: {item.get('total_orders', 0):,} 单, {item.get('with_discount', 0)} 单有折扣, 折扣总额 {item.get('total_discount', 0):,.2f}")
|
||||||
|
|
||||||
|
print("\n按会员卡等级:")
|
||||||
|
for item in analysis.get('by_grade', []):
|
||||||
|
print(f" {item.get('grade_name', 'N/A')}: {item.get('total_orders', 0):,} 单, {item.get('with_discount', 0)} 单有折扣")
|
||||||
|
|
||||||
|
|
||||||
|
def print_samples(samples: List[Dict[str, Any]]):
|
||||||
|
"""打印样本数据"""
|
||||||
|
if not samples:
|
||||||
|
print("[!] 未发现使用会员折扣的订单")
|
||||||
|
return
|
||||||
|
|
||||||
|
print(f"{'订单ID':<20} {'会员':<15} {'等级':<10} {'消费':>12} {'折扣':>12} {'比例':>8}")
|
||||||
|
print("-" * 80)
|
||||||
|
for item in samples[:10]:
|
||||||
|
order_id = str(item.get('order_settle_id', 'N/A'))[:18]
|
||||||
|
member = str(item.get('member_name', 'N/A'))[:13]
|
||||||
|
grade = str(item.get('member_card_grade_name', 'N/A'))[:8]
|
||||||
|
consume = item.get('consume_money', 0)
|
||||||
|
discount = item.get('member_discount_amount', 0)
|
||||||
|
ratio = item.get('discount_ratio', 0)
|
||||||
|
print(f"{order_id:<20} {member:<15} {grade:<10} {consume:>12,.2f} {discount:>12,.2f} {ratio:>7}%")
|
||||||
|
|
||||||
|
|
||||||
|
def print_conclusion(stats: Dict[str, Any]):
|
||||||
|
"""打印分析结论"""
|
||||||
|
with_discount = stats.get('with_member_discount', 0)
|
||||||
|
total = stats.get('total_orders', 1)
|
||||||
|
ratio = with_discount / total * 100
|
||||||
|
|
||||||
|
if with_discount == 0:
|
||||||
|
print("【结论】: member_discount_amount 字段 **未启用**")
|
||||||
|
print()
|
||||||
|
print("该字段在所有订单中均为0,表明:")
|
||||||
|
print(" 1. 会员折扣功能在业务系统中未开启")
|
||||||
|
print(" 2. 或会员折扣通过其他方式(如adjust_amount)记录")
|
||||||
|
print()
|
||||||
|
print("【建议】:")
|
||||||
|
print(" 1. 在DWS财务统计中,暂时不处理此字段")
|
||||||
|
print(" 2. 将此字段标记为'预留/待启用'")
|
||||||
|
print(" 3. 后续如果业务启用,再更新统计逻辑")
|
||||||
|
elif ratio < 1:
|
||||||
|
print(f"【结论】: member_discount_amount 字段 **极少使用** (仅{ratio:.4f}%订单)")
|
||||||
|
print()
|
||||||
|
print("该字段使用率极低,可能是:")
|
||||||
|
print(" 1. 会员折扣功能刚启用不久")
|
||||||
|
print(" 2. 仅特定场景使用")
|
||||||
|
print()
|
||||||
|
print("【建议】:")
|
||||||
|
print(" 1. 在DWS财务统计中保留此字段的处理逻辑")
|
||||||
|
print(" 2. 定期监控使用率变化")
|
||||||
|
else:
|
||||||
|
print(f"【结论】: member_discount_amount 字段 **已启用** ({ratio:.2f}%订单使用)")
|
||||||
|
print()
|
||||||
|
print("【建议】:")
|
||||||
|
print(" 1. 在DWS财务优惠明细中正常统计此字段")
|
||||||
|
print(" 2. 关注会员折扣与其他优惠的叠加规则")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
analyze_member_discount_usage()
|
||||||
@@ -28,7 +28,7 @@ PROJECT_ROOT = Path(__file__).resolve().parents[1]
|
|||||||
if str(PROJECT_ROOT) not in sys.path:
|
if str(PROJECT_ROOT) not in sys.path:
|
||||||
sys.path.insert(0, str(PROJECT_ROOT))
|
sys.path.insert(0, str(PROJECT_ROOT))
|
||||||
|
|
||||||
from api.client import APIClient
|
from api.recording_client import build_recording_client
|
||||||
from config.settings import AppConfig
|
from config.settings import AppConfig
|
||||||
from database.connection import DatabaseConnection
|
from database.connection import DatabaseConnection
|
||||||
from models.parsers import TypeParser
|
from models.parsers import TypeParser
|
||||||
@@ -211,13 +211,7 @@ class MissingDataBackfiller:
|
|||||||
self.store_id = int(cfg.get("app.store_id") or 0)
|
self.store_id = int(cfg.get("app.store_id") or 0)
|
||||||
|
|
||||||
# API 客户端
|
# API 客户端
|
||||||
self.api = APIClient(
|
self.api = build_recording_client(cfg, task_code="BACKFILL_MISSING_DATA")
|
||||||
base_url=cfg["api"]["base_url"],
|
|
||||||
token=cfg["api"]["token"],
|
|
||||||
timeout=int(cfg["api"].get("timeout_sec") or 20),
|
|
||||||
retry_max=int(cfg["api"].get("retries", {}).get("max_attempts") or 3),
|
|
||||||
headers_extra=cfg["api"].get("headers_extra") or {},
|
|
||||||
)
|
|
||||||
|
|
||||||
# 数据库连接(DatabaseConnection 构造时已设置 autocommit=False)
|
# 数据库连接(DatabaseConnection 构造时已设置 autocommit=False)
|
||||||
self.db = DatabaseConnection(dsn=cfg["db"]["dsn"], session=cfg["db"].get("session"))
|
self.db = DatabaseConnection(dsn=cfg["db"]["dsn"], session=cfg["db"].get("session"))
|
||||||
|
|||||||
74
etl_billiards/scripts/check_assistant_dim.py
Normal file
74
etl_billiards/scripts/check_assistant_dim.py
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import sys
|
||||||
|
sys.path.insert(0, '.')
|
||||||
|
from config.settings import AppConfig
|
||||||
|
from database.connection import DatabaseConnection
|
||||||
|
from database.operations import DatabaseOperations
|
||||||
|
|
||||||
|
config = AppConfig.load()
|
||||||
|
db_conn = DatabaseConnection(config.config['db']['dsn'])
|
||||||
|
db = DatabaseOperations(db_conn)
|
||||||
|
|
||||||
|
# 检查dim_assistant表结构
|
||||||
|
print('=== dim_assistant columns ===')
|
||||||
|
sql0 = """
|
||||||
|
SELECT column_name FROM information_schema.columns
|
||||||
|
WHERE table_schema = 'billiards_dwd' AND table_name = 'dim_assistant'
|
||||||
|
"""
|
||||||
|
for row in db.query(sql0):
|
||||||
|
print(f' {dict(row)["column_name"]}')
|
||||||
|
|
||||||
|
# 检查dim_assistant数量
|
||||||
|
print()
|
||||||
|
print('=== dim_assistant ===')
|
||||||
|
sql1 = 'SELECT COUNT(*) as cnt FROM billiards_dwd.dim_assistant WHERE scd2_is_current = 1'
|
||||||
|
rows = db.query(sql1)
|
||||||
|
print(f'dim_assistant current count: {dict(rows[0])["cnt"]}')
|
||||||
|
|
||||||
|
# 检查服务记录中的nickname分布
|
||||||
|
print()
|
||||||
|
print('=== Service by nickname ===')
|
||||||
|
sql2 = """
|
||||||
|
SELECT nickname, COUNT(*) as service_count, COUNT(DISTINCT tenant_member_id) as member_count
|
||||||
|
FROM billiards_dwd.dwd_assistant_service_log
|
||||||
|
WHERE tenant_member_id > 0 AND is_delete = 0
|
||||||
|
GROUP BY nickname
|
||||||
|
ORDER BY service_count DESC
|
||||||
|
LIMIT 10
|
||||||
|
"""
|
||||||
|
for row in db.query(sql2):
|
||||||
|
r = dict(row)
|
||||||
|
print(f' {r["nickname"]}: {r["service_count"]} services, {r["member_count"]} members')
|
||||||
|
|
||||||
|
# 检查assistant_no分布
|
||||||
|
print()
|
||||||
|
print('=== Service by assistant_no ===')
|
||||||
|
sql3 = """
|
||||||
|
SELECT assistant_no, nickname, COUNT(*) as service_count, COUNT(DISTINCT tenant_member_id) as member_count
|
||||||
|
FROM billiards_dwd.dwd_assistant_service_log
|
||||||
|
WHERE tenant_member_id > 0 AND is_delete = 0
|
||||||
|
GROUP BY assistant_no, nickname
|
||||||
|
ORDER BY service_count DESC
|
||||||
|
LIMIT 10
|
||||||
|
"""
|
||||||
|
for row in db.query(sql3):
|
||||||
|
r = dict(row)
|
||||||
|
print(f' {r["assistant_no"]} ({r["nickname"]}): {r["service_count"]} services, {r["member_count"]} members')
|
||||||
|
|
||||||
|
# 近60天
|
||||||
|
print()
|
||||||
|
print('=== Last 60 days by nickname ===')
|
||||||
|
sql4 = """
|
||||||
|
SELECT nickname, COUNT(*) as service_count, COUNT(DISTINCT tenant_member_id) as member_count
|
||||||
|
FROM billiards_dwd.dwd_assistant_service_log
|
||||||
|
WHERE tenant_member_id > 0 AND is_delete = 0
|
||||||
|
AND last_use_time >= NOW() - INTERVAL '60 days'
|
||||||
|
GROUP BY nickname
|
||||||
|
ORDER BY service_count DESC
|
||||||
|
LIMIT 15
|
||||||
|
"""
|
||||||
|
for row in db.query(sql4):
|
||||||
|
r = dict(row)
|
||||||
|
print(f' {r["nickname"]}: {r["service_count"]} services, {r["member_count"]} members')
|
||||||
|
|
||||||
|
db_conn.close()
|
||||||
82
etl_billiards/scripts/check_dwd_service.py
Normal file
82
etl_billiards/scripts/check_dwd_service.py
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import sys
|
||||||
|
sys.path.insert(0, '.')
|
||||||
|
from config.settings import AppConfig
|
||||||
|
from database.connection import DatabaseConnection
|
||||||
|
from database.operations import DatabaseOperations
|
||||||
|
|
||||||
|
config = AppConfig.load()
|
||||||
|
db_conn = DatabaseConnection(config.config['db']['dsn'])
|
||||||
|
db = DatabaseOperations(db_conn)
|
||||||
|
|
||||||
|
# 检查DWD层服务记录分布
|
||||||
|
print("=== DWD层服务记录分析 ===")
|
||||||
|
print()
|
||||||
|
|
||||||
|
# 1. 总体统计
|
||||||
|
sql1 = """
|
||||||
|
SELECT
|
||||||
|
COUNT(*) as total_records,
|
||||||
|
COUNT(DISTINCT tenant_member_id) as unique_members,
|
||||||
|
COUNT(DISTINCT site_assistant_id) as unique_assistants,
|
||||||
|
COUNT(DISTINCT (tenant_member_id, site_assistant_id)) as unique_pairs
|
||||||
|
FROM billiards_dwd.dwd_assistant_service_log
|
||||||
|
WHERE tenant_member_id > 0 AND is_delete = 0
|
||||||
|
"""
|
||||||
|
r = dict(db.query(sql1)[0])
|
||||||
|
print("总体统计:")
|
||||||
|
print(f" 总服务记录数: {r['total_records']}")
|
||||||
|
print(f" 唯一会员数: {r['unique_members']}")
|
||||||
|
print(f" 唯一助教数: {r['unique_assistants']}")
|
||||||
|
print(f" 唯一客户-助教对: {r['unique_pairs']}")
|
||||||
|
|
||||||
|
# 2. 助教服务会员数分布
|
||||||
|
print()
|
||||||
|
print("助教服务会员数分布 (Top 10):")
|
||||||
|
sql2 = """
|
||||||
|
SELECT site_assistant_id, COUNT(DISTINCT tenant_member_id) as member_count
|
||||||
|
FROM billiards_dwd.dwd_assistant_service_log
|
||||||
|
WHERE tenant_member_id > 0 AND is_delete = 0
|
||||||
|
GROUP BY site_assistant_id
|
||||||
|
ORDER BY member_count DESC
|
||||||
|
LIMIT 10
|
||||||
|
"""
|
||||||
|
for row in db.query(sql2):
|
||||||
|
r = dict(row)
|
||||||
|
print(f" 助教 {r['site_assistant_id']}: 服务 {r['member_count']} 个会员")
|
||||||
|
|
||||||
|
# 3. 每个客户-助教对的服务次数分布
|
||||||
|
print()
|
||||||
|
print("客户-助教对 服务次数分布 (Top 10):")
|
||||||
|
sql3 = """
|
||||||
|
SELECT tenant_member_id, site_assistant_id, COUNT(*) as service_count
|
||||||
|
FROM billiards_dwd.dwd_assistant_service_log
|
||||||
|
WHERE tenant_member_id > 0 AND is_delete = 0
|
||||||
|
GROUP BY tenant_member_id, site_assistant_id
|
||||||
|
ORDER BY service_count DESC
|
||||||
|
LIMIT 10
|
||||||
|
"""
|
||||||
|
for row in db.query(sql3):
|
||||||
|
r = dict(row)
|
||||||
|
print(f" 会员 {r['tenant_member_id']} - 助教 {r['site_assistant_id']}: {r['service_count']} 次服务")
|
||||||
|
|
||||||
|
# 4. 近60天的数据
|
||||||
|
print()
|
||||||
|
print("=== 近60天数据 ===")
|
||||||
|
sql4 = """
|
||||||
|
SELECT
|
||||||
|
COUNT(*) as total_records,
|
||||||
|
COUNT(DISTINCT tenant_member_id) as unique_members,
|
||||||
|
COUNT(DISTINCT site_assistant_id) as unique_assistants,
|
||||||
|
COUNT(DISTINCT (tenant_member_id, site_assistant_id)) as unique_pairs
|
||||||
|
FROM billiards_dwd.dwd_assistant_service_log
|
||||||
|
WHERE tenant_member_id > 0 AND is_delete = 0
|
||||||
|
AND last_use_time >= NOW() - INTERVAL '60 days'
|
||||||
|
"""
|
||||||
|
r4 = dict(db.query(sql4)[0])
|
||||||
|
print(f" 总服务记录数: {r4['total_records']}")
|
||||||
|
print(f" 唯一会员数: {r4['unique_members']}")
|
||||||
|
print(f" 唯一助教数: {r4['unique_assistants']}")
|
||||||
|
print(f" 唯一客户-助教对: {r4['unique_pairs']}")
|
||||||
|
|
||||||
|
db_conn.close()
|
||||||
57
etl_billiards/scripts/check_intimacy_stats.py
Normal file
57
etl_billiards/scripts/check_intimacy_stats.py
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import sys
|
||||||
|
sys.path.insert(0, '.')
|
||||||
|
from config.settings import AppConfig
|
||||||
|
from database.connection import DatabaseConnection
|
||||||
|
from database.operations import DatabaseOperations
|
||||||
|
|
||||||
|
config = AppConfig.load()
|
||||||
|
db_conn = DatabaseConnection(config.config['db']['dsn'])
|
||||||
|
db = DatabaseOperations(db_conn)
|
||||||
|
|
||||||
|
# 检查实际统计
|
||||||
|
sql = """
|
||||||
|
SELECT
|
||||||
|
COUNT(*) as total_pairs,
|
||||||
|
COUNT(DISTINCT member_id) as unique_members,
|
||||||
|
COUNT(DISTINCT assistant_id) as unique_assistants
|
||||||
|
FROM billiards_dws.dws_member_assistant_intimacy
|
||||||
|
"""
|
||||||
|
rows = db.query(sql)
|
||||||
|
r = dict(rows[0])
|
||||||
|
print("DWS亲密指数统计:")
|
||||||
|
print(f" 总记录数(对): {r['total_pairs']}")
|
||||||
|
print(f" 唯一会员数: {r['unique_members']}")
|
||||||
|
print(f" 唯一助教数: {r['unique_assistants']}")
|
||||||
|
|
||||||
|
# 查看助教分布
|
||||||
|
sql2 = """
|
||||||
|
SELECT assistant_id, COUNT(*) as member_count
|
||||||
|
FROM billiards_dws.dws_member_assistant_intimacy
|
||||||
|
GROUP BY assistant_id
|
||||||
|
ORDER BY member_count DESC
|
||||||
|
LIMIT 10
|
||||||
|
"""
|
||||||
|
rows2 = db.query(sql2)
|
||||||
|
print()
|
||||||
|
print("Top 10 助教 (按服务会员数):")
|
||||||
|
for row in rows2:
|
||||||
|
r = dict(row)
|
||||||
|
print(f" 助教 {r['assistant_id']}: 服务 {r['member_count']} 个会员")
|
||||||
|
|
||||||
|
# 检查DWD层原始数据
|
||||||
|
sql3 = """
|
||||||
|
SELECT
|
||||||
|
COUNT(DISTINCT site_assistant_id) as unique_assistants,
|
||||||
|
COUNT(DISTINCT tenant_member_id) as unique_members
|
||||||
|
FROM billiards_dwd.dwd_assistant_service_log
|
||||||
|
WHERE tenant_member_id > 0 AND is_delete = 0
|
||||||
|
"""
|
||||||
|
rows3 = db.query(sql3)
|
||||||
|
r3 = dict(rows3[0])
|
||||||
|
print()
|
||||||
|
print("DWD层原始数据:")
|
||||||
|
print(f" 唯一助教数: {r3['unique_assistants']}")
|
||||||
|
print(f" 唯一会员数: {r3['unique_members']}")
|
||||||
|
|
||||||
|
db_conn.close()
|
||||||
@@ -29,7 +29,7 @@ PROJECT_ROOT = Path(__file__).resolve().parents[1]
|
|||||||
if str(PROJECT_ROOT) not in sys.path:
|
if str(PROJECT_ROOT) not in sys.path:
|
||||||
sys.path.insert(0, str(PROJECT_ROOT))
|
sys.path.insert(0, str(PROJECT_ROOT))
|
||||||
|
|
||||||
from api.client import APIClient
|
from api.recording_client import build_recording_client
|
||||||
from config.settings import AppConfig
|
from config.settings import AppConfig
|
||||||
from database.connection import DatabaseConnection
|
from database.connection import DatabaseConnection
|
||||||
from models.parsers import TypeParser
|
from models.parsers import TypeParser
|
||||||
@@ -702,6 +702,7 @@ def run_gap_check(
|
|||||||
content_sample_limit: int | None = None,
|
content_sample_limit: int | None = None,
|
||||||
window_split_unit: str | None = None,
|
window_split_unit: str | None = None,
|
||||||
window_compensation_hours: int | None = None,
|
window_compensation_hours: int | None = None,
|
||||||
|
tag: str = "",
|
||||||
) -> dict:
|
) -> dict:
|
||||||
cfg = cfg or AppConfig.load({})
|
cfg = cfg or AppConfig.load({})
|
||||||
tz = ZoneInfo(cfg.get("app.timezone", "Asia/Taipei"))
|
tz = ZoneInfo(cfg.get("app.timezone", "Asia/Taipei"))
|
||||||
@@ -800,13 +801,8 @@ def run_gap_check(
|
|||||||
if cutoff:
|
if cutoff:
|
||||||
logger.info("CUTOFF=%s overlap_hours=%s", cutoff.isoformat(), cutoff_overlap_hours)
|
logger.info("CUTOFF=%s overlap_hours=%s", cutoff.isoformat(), cutoff_overlap_hours)
|
||||||
|
|
||||||
client = APIClient(
|
tag_suffix = f"_{tag}" if tag else ""
|
||||||
base_url=cfg["api"]["base_url"],
|
client = build_recording_client(cfg, task_code=f"ODS_GAP_CHECK{tag_suffix}")
|
||||||
token=cfg["api"]["token"],
|
|
||||||
timeout=int(cfg["api"].get("timeout_sec") or 20),
|
|
||||||
retry_max=int(cfg["api"].get("retries", {}).get("max_attempts") or 3),
|
|
||||||
headers_extra=cfg["api"].get("headers_extra") or {},
|
|
||||||
)
|
|
||||||
|
|
||||||
db_state = _init_db_state(cfg)
|
db_state = _init_db_state(cfg)
|
||||||
try:
|
try:
|
||||||
|
|||||||
185
etl_billiards/scripts/create_index_tables.py
Normal file
185
etl_billiards/scripts/create_index_tables.py
Normal file
@@ -0,0 +1,185 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
创建指数算法相关表
|
||||||
|
"""
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||||
|
|
||||||
|
from config.settings import AppConfig
|
||||||
|
from database.connection import DatabaseConnection
|
||||||
|
from database.operations import DatabaseOperations
|
||||||
|
|
||||||
|
# 表DDL
|
||||||
|
DDL_STATEMENTS = [
|
||||||
|
# 参数配置表
|
||||||
|
"""
|
||||||
|
DROP TABLE IF EXISTS billiards_dws.cfg_index_parameters CASCADE;
|
||||||
|
CREATE TABLE billiards_dws.cfg_index_parameters (
|
||||||
|
param_id SERIAL PRIMARY KEY,
|
||||||
|
index_type VARCHAR(50) NOT NULL,
|
||||||
|
param_name VARCHAR(100) NOT NULL,
|
||||||
|
param_value NUMERIC(14,6) NOT NULL,
|
||||||
|
description TEXT,
|
||||||
|
effective_from DATE NOT NULL DEFAULT CURRENT_DATE,
|
||||||
|
effective_to DATE,
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
CONSTRAINT uk_cfg_index_parameters UNIQUE (index_type, param_name, effective_from)
|
||||||
|
);
|
||||||
|
CREATE INDEX idx_cfg_index_params_type ON billiards_dws.cfg_index_parameters (index_type);
|
||||||
|
""",
|
||||||
|
|
||||||
|
# 召回指数表
|
||||||
|
"""
|
||||||
|
DROP TABLE IF EXISTS billiards_dws.dws_member_recall_index CASCADE;
|
||||||
|
CREATE TABLE billiards_dws.dws_member_recall_index (
|
||||||
|
recall_id BIGSERIAL PRIMARY KEY,
|
||||||
|
site_id BIGINT NOT NULL,
|
||||||
|
tenant_id BIGINT NOT NULL,
|
||||||
|
member_id BIGINT NOT NULL,
|
||||||
|
days_since_last_visit INTEGER,
|
||||||
|
visit_interval_median NUMERIC(10,2),
|
||||||
|
visit_interval_mad NUMERIC(10,2),
|
||||||
|
days_since_first_visit INTEGER,
|
||||||
|
days_since_last_recharge INTEGER,
|
||||||
|
visits_last_14_days INTEGER NOT NULL DEFAULT 0,
|
||||||
|
visits_last_60_days INTEGER NOT NULL DEFAULT 0,
|
||||||
|
score_overdue NUMERIC(10,4),
|
||||||
|
score_new_bonus NUMERIC(10,4),
|
||||||
|
score_recharge_bonus NUMERIC(10,4),
|
||||||
|
score_hot_drop NUMERIC(10,4),
|
||||||
|
raw_score NUMERIC(14,6),
|
||||||
|
display_score NUMERIC(4,2),
|
||||||
|
calc_time TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
calc_version INTEGER NOT NULL DEFAULT 1,
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
CONSTRAINT uk_dws_member_recall UNIQUE (site_id, member_id)
|
||||||
|
);
|
||||||
|
CREATE INDEX idx_dws_recall_display ON billiards_dws.dws_member_recall_index (site_id, display_score DESC);
|
||||||
|
""",
|
||||||
|
|
||||||
|
# 亲密指数表
|
||||||
|
"""
|
||||||
|
DROP TABLE IF EXISTS billiards_dws.dws_member_assistant_intimacy CASCADE;
|
||||||
|
CREATE TABLE billiards_dws.dws_member_assistant_intimacy (
|
||||||
|
intimacy_id BIGSERIAL PRIMARY KEY,
|
||||||
|
site_id BIGINT NOT NULL,
|
||||||
|
tenant_id BIGINT NOT NULL,
|
||||||
|
member_id BIGINT NOT NULL,
|
||||||
|
assistant_id BIGINT NOT NULL,
|
||||||
|
session_count INTEGER NOT NULL DEFAULT 0,
|
||||||
|
total_duration_minutes INTEGER NOT NULL DEFAULT 0,
|
||||||
|
basic_session_count INTEGER NOT NULL DEFAULT 0,
|
||||||
|
incentive_session_count INTEGER NOT NULL DEFAULT 0,
|
||||||
|
days_since_last_session INTEGER,
|
||||||
|
attributed_recharge_count INTEGER NOT NULL DEFAULT 0,
|
||||||
|
attributed_recharge_amount NUMERIC(14,2) NOT NULL DEFAULT 0,
|
||||||
|
score_frequency NUMERIC(10,4),
|
||||||
|
score_recency NUMERIC(10,4),
|
||||||
|
score_recharge NUMERIC(10,4),
|
||||||
|
score_duration NUMERIC(10,4),
|
||||||
|
burst_multiplier NUMERIC(6,4),
|
||||||
|
raw_score NUMERIC(14,6),
|
||||||
|
display_score NUMERIC(4,2),
|
||||||
|
calc_time TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
calc_version INTEGER NOT NULL DEFAULT 1,
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
CONSTRAINT uk_dws_member_assistant_intimacy UNIQUE (site_id, member_id, assistant_id)
|
||||||
|
);
|
||||||
|
CREATE INDEX idx_dws_intimacy_member ON billiards_dws.dws_member_assistant_intimacy (site_id, member_id, display_score DESC);
|
||||||
|
CREATE INDEX idx_dws_intimacy_assistant ON billiards_dws.dws_member_assistant_intimacy (site_id, assistant_id, display_score DESC);
|
||||||
|
""",
|
||||||
|
|
||||||
|
# 分位点历史表
|
||||||
|
"""
|
||||||
|
DROP TABLE IF EXISTS billiards_dws.dws_index_percentile_history CASCADE;
|
||||||
|
CREATE TABLE billiards_dws.dws_index_percentile_history (
|
||||||
|
history_id BIGSERIAL PRIMARY KEY,
|
||||||
|
site_id BIGINT NOT NULL,
|
||||||
|
index_type VARCHAR(50) NOT NULL,
|
||||||
|
calc_time TIMESTAMPTZ NOT NULL,
|
||||||
|
percentile_5 NUMERIC(14,6),
|
||||||
|
percentile_95 NUMERIC(14,6),
|
||||||
|
percentile_5_smoothed NUMERIC(14,6),
|
||||||
|
percentile_95_smoothed NUMERIC(14,6),
|
||||||
|
record_count INTEGER,
|
||||||
|
min_raw_score NUMERIC(14,6),
|
||||||
|
max_raw_score NUMERIC(14,6),
|
||||||
|
avg_raw_score NUMERIC(14,6),
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
CONSTRAINT uk_dws_index_percentile_history UNIQUE (site_id, index_type, calc_time)
|
||||||
|
);
|
||||||
|
CREATE INDEX idx_dws_percentile_history ON billiards_dws.dws_index_percentile_history (site_id, index_type, calc_time DESC);
|
||||||
|
"""
|
||||||
|
]
|
||||||
|
|
||||||
|
# 初始化参数
|
||||||
|
SEED_PARAMS = """
|
||||||
|
INSERT INTO billiards_dws.cfg_index_parameters
|
||||||
|
(index_type, param_name, param_value, description, effective_from)
|
||||||
|
VALUES
|
||||||
|
('RECALL', 'lookback_days', 60, '回溯窗口(天)', CURRENT_DATE),
|
||||||
|
('RECALL', 'sigma_min', 2.0, '波动下限(天)', CURRENT_DATE),
|
||||||
|
('RECALL', 'halflife_new', 7, '新客户半衰期(天)', CURRENT_DATE),
|
||||||
|
('RECALL', 'halflife_recharge', 10, '刚充值半衰期(天)', CURRENT_DATE),
|
||||||
|
('RECALL', 'weight_overdue', 3.0, '超期紧急性权重', CURRENT_DATE),
|
||||||
|
('RECALL', 'weight_new', 1.0, '新客户权重', CURRENT_DATE),
|
||||||
|
('RECALL', 'weight_recharge', 1.0, '刚充值权重', CURRENT_DATE),
|
||||||
|
('RECALL', 'weight_hot', 1.0, '热度断档权重', CURRENT_DATE),
|
||||||
|
('RECALL', 'percentile_lower', 5, '下锚分位数', CURRENT_DATE),
|
||||||
|
('RECALL', 'percentile_upper', 95, '上锚分位数', CURRENT_DATE),
|
||||||
|
('RECALL', 'ewma_alpha', 0.2, 'EWMA平滑系数', CURRENT_DATE),
|
||||||
|
('INTIMACY', 'lookback_days', 60, '回溯窗口(天)', CURRENT_DATE),
|
||||||
|
('INTIMACY', 'session_merge_hours', 4, '会话合并间隔(小时)', CURRENT_DATE),
|
||||||
|
('INTIMACY', 'recharge_attribute_hours', 1, '充值归因窗口(小时)', CURRENT_DATE),
|
||||||
|
('INTIMACY', 'amount_base', 500, '金额压缩基准(元)', CURRENT_DATE),
|
||||||
|
('INTIMACY', 'incentive_weight', 1.5, '附加课权重倍数', CURRENT_DATE),
|
||||||
|
('INTIMACY', 'halflife_session', 14, '会话衰减半衰期(天)', CURRENT_DATE),
|
||||||
|
('INTIMACY', 'halflife_last', 10, '最近一次半衰期(天)', CURRENT_DATE),
|
||||||
|
('INTIMACY', 'halflife_recharge', 21, '充值衰减半衰期(天)', CURRENT_DATE),
|
||||||
|
('INTIMACY', 'halflife_short', 7, '短期激增检测半衰期(天)', CURRENT_DATE),
|
||||||
|
('INTIMACY', 'halflife_long', 30, '长期激增检测半衰期(天)', CURRENT_DATE),
|
||||||
|
('INTIMACY', 'weight_frequency', 2.0, '频次权重', CURRENT_DATE),
|
||||||
|
('INTIMACY', 'weight_recency', 1.5, '最近一次权重', CURRENT_DATE),
|
||||||
|
('INTIMACY', 'weight_recharge', 2.0, '归因充值权重', CURRENT_DATE),
|
||||||
|
('INTIMACY', 'weight_duration', 0.5, '时长权重', CURRENT_DATE),
|
||||||
|
('INTIMACY', 'burst_gamma', 0.6, '激增放大系数', CURRENT_DATE),
|
||||||
|
('INTIMACY', 'percentile_lower', 5, '下锚分位数', CURRENT_DATE),
|
||||||
|
('INTIMACY', 'percentile_upper', 95, '上锚分位数', CURRENT_DATE),
|
||||||
|
('INTIMACY', 'ewma_alpha', 0.2, 'EWMA平滑系数', CURRENT_DATE)
|
||||||
|
ON CONFLICT (index_type, param_name, effective_from) DO NOTHING;
|
||||||
|
"""
|
||||||
|
|
||||||
|
def main():
|
||||||
|
print("创建指数算法相关表...")
|
||||||
|
|
||||||
|
config = AppConfig.load()
|
||||||
|
db_conn = DatabaseConnection(config.config["db"]["dsn"])
|
||||||
|
|
||||||
|
try:
|
||||||
|
with db_conn.conn.cursor() as cur:
|
||||||
|
# 创建表
|
||||||
|
for i, ddl in enumerate(DDL_STATEMENTS, 1):
|
||||||
|
print(f" 执行DDL {i}/{len(DDL_STATEMENTS)}...")
|
||||||
|
cur.execute(ddl)
|
||||||
|
|
||||||
|
# 初始化参数
|
||||||
|
print(" 初始化算法参数...")
|
||||||
|
cur.execute(SEED_PARAMS)
|
||||||
|
|
||||||
|
db_conn.conn.commit()
|
||||||
|
print("完成!")
|
||||||
|
|
||||||
|
# 验证
|
||||||
|
cur.execute("SELECT COUNT(*) FROM billiards_dws.cfg_index_parameters")
|
||||||
|
count = cur.fetchone()[0]
|
||||||
|
print(f" 已插入 {count} 个参数配置")
|
||||||
|
|
||||||
|
finally:
|
||||||
|
db_conn.close()
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
77
etl_billiards/scripts/export_index_tables.py
Normal file
77
etl_billiards/scripts/export_index_tables.py
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""导出指数表数据到Markdown"""
|
||||||
|
import sys
|
||||||
|
sys.path.insert(0, '.')
|
||||||
|
from config.settings import AppConfig
|
||||||
|
from database.connection import DatabaseConnection
|
||||||
|
from database.operations import DatabaseOperations
|
||||||
|
|
||||||
|
config = AppConfig.load()
|
||||||
|
db_conn = DatabaseConnection(config.config['db']['dsn'])
|
||||||
|
db = DatabaseOperations(db_conn)
|
||||||
|
|
||||||
|
output_lines = []
|
||||||
|
|
||||||
|
# 1. 客户召回表
|
||||||
|
output_lines.append("## 1. 客户召回表\n")
|
||||||
|
output_lines.append("| 客户姓名 | 召回指数 |")
|
||||||
|
output_lines.append("|----------|----------|")
|
||||||
|
|
||||||
|
sql_recall = """
|
||||||
|
SELECT
|
||||||
|
COALESCE(m.nickname, CONCAT('会员', r.member_id)) AS member_name,
|
||||||
|
r.display_score AS recall_score
|
||||||
|
FROM billiards_dws.dws_member_recall_index r
|
||||||
|
LEFT JOIN billiards_dwd.dim_member m
|
||||||
|
ON r.member_id = m.member_id AND m.scd2_is_current = 1
|
||||||
|
ORDER BY r.display_score DESC
|
||||||
|
"""
|
||||||
|
|
||||||
|
rows = db.query(sql_recall)
|
||||||
|
for row in rows:
|
||||||
|
r = dict(row)
|
||||||
|
name = r['member_name'] or '未知'
|
||||||
|
score = r['recall_score']
|
||||||
|
output_lines.append(f"| {name} | {score:.2f} |")
|
||||||
|
|
||||||
|
output_lines.append(f"\n共 {len(rows)} 条记录\n")
|
||||||
|
|
||||||
|
# 2. 助教客户关系表
|
||||||
|
output_lines.append("## 2. 助教客户关系表\n")
|
||||||
|
output_lines.append("| 助教花名 | 客户姓名 | 关系指数 |")
|
||||||
|
output_lines.append("|----------|----------|----------|")
|
||||||
|
|
||||||
|
sql_intimacy = """
|
||||||
|
SELECT
|
||||||
|
a.nickname AS assistant_name,
|
||||||
|
i.assistant_id AS assistant_no,
|
||||||
|
COALESCE(m.nickname, CONCAT('会员', i.member_id)) AS member_name,
|
||||||
|
i.display_score AS intimacy_score,
|
||||||
|
i.session_count,
|
||||||
|
i.attributed_recharge_amount
|
||||||
|
FROM billiards_dws.dws_member_assistant_intimacy i
|
||||||
|
LEFT JOIN billiards_dwd.dim_member m
|
||||||
|
ON i.member_id = m.member_id AND m.scd2_is_current = 1
|
||||||
|
LEFT JOIN billiards_dwd.dim_assistant a
|
||||||
|
ON i.assistant_id::text = a.assistant_no AND a.scd2_is_current = 1
|
||||||
|
ORDER BY i.display_score DESC, i.session_count DESC
|
||||||
|
"""
|
||||||
|
|
||||||
|
rows2 = db.query(sql_intimacy)
|
||||||
|
for row in rows2:
|
||||||
|
r = dict(row)
|
||||||
|
assistant = r['assistant_name'] or f"工号{r.get('assistant_no', '?')}"
|
||||||
|
member = r['member_name'] or '未知'
|
||||||
|
score = r['intimacy_score']
|
||||||
|
output_lines.append(f"| {assistant} | {member} | {score:.2f} |")
|
||||||
|
|
||||||
|
output_lines.append(f"\n共 {len(rows2)} 条记录")
|
||||||
|
|
||||||
|
db_conn.close()
|
||||||
|
|
||||||
|
# 写入文件(UTF-8带BOM)
|
||||||
|
output_path = 'docs/index_tables.md'
|
||||||
|
with open(output_path, 'w', encoding='utf-8-sig') as f:
|
||||||
|
f.write('\n'.join(output_lines))
|
||||||
|
|
||||||
|
print(f"已导出到 {output_path}")
|
||||||
602
etl_billiards/scripts/import_dws_excel.py
Normal file
602
etl_billiards/scripts/import_dws_excel.py
Normal file
@@ -0,0 +1,602 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
DWS Excel导入脚本
|
||||||
|
|
||||||
|
功能说明:
|
||||||
|
支持三类Excel数据的导入:
|
||||||
|
1. 支出结构(dws_finance_expense_summary)
|
||||||
|
2. 平台结算(dws_platform_settlement)
|
||||||
|
3. 充值提成(dws_assistant_recharge_commission)
|
||||||
|
|
||||||
|
导入规范:
|
||||||
|
- 字段定义:按照目标表字段要求
|
||||||
|
- 时间粒度:支出按月,平台结算按日,充值提成按月
|
||||||
|
- 门店维度:使用配置的site_id
|
||||||
|
- 去重规则:按import_batch_no去重
|
||||||
|
- 校验规则:金额字段非负,日期格式校验
|
||||||
|
|
||||||
|
使用方式:
|
||||||
|
python import_dws_excel.py --type expense --file expenses.xlsx
|
||||||
|
python import_dws_excel.py --type platform --file platform_settlement.xlsx
|
||||||
|
python import_dws_excel.py --type commission --file recharge_commission.xlsx
|
||||||
|
|
||||||
|
作者:ETL团队
|
||||||
|
创建日期:2026-02-01
|
||||||
|
"""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import uuid
|
||||||
|
from datetime import date, datetime
|
||||||
|
from decimal import Decimal, InvalidOperation
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Any, Dict, List, Optional, Tuple
|
||||||
|
|
||||||
|
# 添加项目根目录到Python路径
|
||||||
|
project_root = Path(__file__).parent.parent.parent
|
||||||
|
sys.path.insert(0, str(project_root))
|
||||||
|
|
||||||
|
try:
|
||||||
|
import pandas as pd
|
||||||
|
except ImportError:
|
||||||
|
print("请安装 pandas: pip install pandas openpyxl")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
from etl_billiards.utils.config import Config
|
||||||
|
from etl_billiards.utils.db import DatabaseConnection
|
||||||
|
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# 常量定义
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
# 支出类型枚举
|
||||||
|
EXPENSE_TYPES = {
|
||||||
|
'房租': 'RENT',
|
||||||
|
'水电费': 'UTILITY',
|
||||||
|
'物业费': 'PROPERTY',
|
||||||
|
'工资': 'SALARY',
|
||||||
|
'报销': 'REIMBURSE',
|
||||||
|
'平台服务费': 'PLATFORM_FEE',
|
||||||
|
'其他': 'OTHER',
|
||||||
|
}
|
||||||
|
|
||||||
|
# 支出大类映射
|
||||||
|
EXPENSE_CATEGORIES = {
|
||||||
|
'RENT': 'FIXED_COST',
|
||||||
|
'UTILITY': 'VARIABLE_COST',
|
||||||
|
'PROPERTY': 'FIXED_COST',
|
||||||
|
'SALARY': 'FIXED_COST',
|
||||||
|
'REIMBURSE': 'VARIABLE_COST',
|
||||||
|
'PLATFORM_FEE': 'VARIABLE_COST',
|
||||||
|
'OTHER': 'OTHER',
|
||||||
|
}
|
||||||
|
|
||||||
|
# 平台类型枚举
|
||||||
|
PLATFORM_TYPES = {
|
||||||
|
'美团': 'MEITUAN',
|
||||||
|
'抖音': 'DOUYIN',
|
||||||
|
'大众点评': 'DIANPING',
|
||||||
|
'其他': 'OTHER',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# 导入基类
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
class BaseImporter:
|
||||||
|
"""导入基类"""
|
||||||
|
|
||||||
|
def __init__(self, config: Config, db: DatabaseConnection):
|
||||||
|
self.config = config
|
||||||
|
self.db = db
|
||||||
|
self.site_id = config.get("app.store_id")
|
||||||
|
self.tenant_id = config.get("app.tenant_id", self.site_id)
|
||||||
|
self.batch_no = self._generate_batch_no()
|
||||||
|
|
||||||
|
def _generate_batch_no(self) -> str:
|
||||||
|
"""生成导入批次号"""
|
||||||
|
timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
|
||||||
|
unique_id = str(uuid.uuid4())[:8]
|
||||||
|
return f"{timestamp}_{unique_id}"
|
||||||
|
|
||||||
|
def _safe_decimal(self, value: Any, default: Decimal = Decimal('0')) -> Decimal:
|
||||||
|
"""安全转换为Decimal"""
|
||||||
|
if value is None or pd.isna(value):
|
||||||
|
return default
|
||||||
|
try:
|
||||||
|
return Decimal(str(value))
|
||||||
|
except (ValueError, InvalidOperation):
|
||||||
|
return default
|
||||||
|
|
||||||
|
def _safe_date(self, value: Any) -> Optional[date]:
|
||||||
|
"""安全转换为日期"""
|
||||||
|
if value is None or pd.isna(value):
|
||||||
|
return None
|
||||||
|
if isinstance(value, datetime):
|
||||||
|
return value.date()
|
||||||
|
if isinstance(value, date):
|
||||||
|
return value
|
||||||
|
try:
|
||||||
|
return pd.to_datetime(value).date()
|
||||||
|
except:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _safe_month(self, value: Any) -> Optional[date]:
|
||||||
|
"""安全转换为月份(月第一天)"""
|
||||||
|
dt = self._safe_date(value)
|
||||||
|
if dt:
|
||||||
|
return dt.replace(day=1)
|
||||||
|
return None
|
||||||
|
|
||||||
|
def import_file(self, file_path: str) -> Dict[str, Any]:
|
||||||
|
"""导入文件"""
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def validate_row(self, row: Dict[str, Any], row_idx: int) -> List[str]:
|
||||||
|
"""校验行数据,返回错误列表"""
|
||||||
|
return []
|
||||||
|
|
||||||
|
def transform_row(self, row: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
|
"""转换行数据"""
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def insert_records(self, records: List[Dict[str, Any]]) -> int:
|
||||||
|
"""插入记录"""
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# 支出导入
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
class ExpenseImporter(BaseImporter):
|
||||||
|
"""
|
||||||
|
支出导入
|
||||||
|
|
||||||
|
Excel格式要求:
|
||||||
|
- 月份: 2026-01 或 2026/01/01 格式
|
||||||
|
- 支出类型: 房租/水电费/物业费/工资/报销/平台服务费/其他
|
||||||
|
- 金额: 数字
|
||||||
|
- 备注: 可选
|
||||||
|
"""
|
||||||
|
|
||||||
|
TARGET_TABLE = "billiards_dws.dws_finance_expense_summary"
|
||||||
|
|
||||||
|
REQUIRED_COLUMNS = ['月份', '支出类型', '金额']
|
||||||
|
OPTIONAL_COLUMNS = ['明细', '备注']
|
||||||
|
|
||||||
|
def import_file(self, file_path: str) -> Dict[str, Any]:
|
||||||
|
"""导入支出Excel"""
|
||||||
|
print(f"开始导入支出文件: {file_path}")
|
||||||
|
|
||||||
|
# 读取Excel
|
||||||
|
df = pd.read_excel(file_path)
|
||||||
|
|
||||||
|
# 校验必要列
|
||||||
|
missing_cols = [c for c in self.REQUIRED_COLUMNS if c not in df.columns]
|
||||||
|
if missing_cols:
|
||||||
|
return {"status": "ERROR", "message": f"缺少必要列: {missing_cols}"}
|
||||||
|
|
||||||
|
# 处理数据
|
||||||
|
records = []
|
||||||
|
errors = []
|
||||||
|
|
||||||
|
for idx, row in df.iterrows():
|
||||||
|
row_dict = row.to_dict()
|
||||||
|
row_errors = self.validate_row(row_dict, idx + 2) # Excel行号从2开始
|
||||||
|
|
||||||
|
if row_errors:
|
||||||
|
errors.extend(row_errors)
|
||||||
|
continue
|
||||||
|
|
||||||
|
record = self.transform_row(row_dict)
|
||||||
|
records.append(record)
|
||||||
|
|
||||||
|
if errors:
|
||||||
|
print(f"校验错误: {len(errors)} 条")
|
||||||
|
for err in errors[:10]:
|
||||||
|
print(f" - {err}")
|
||||||
|
|
||||||
|
# 插入数据
|
||||||
|
inserted = 0
|
||||||
|
if records:
|
||||||
|
inserted = self.insert_records(records)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"status": "SUCCESS" if not errors else "PARTIAL",
|
||||||
|
"batch_no": self.batch_no,
|
||||||
|
"total_rows": len(df),
|
||||||
|
"inserted": inserted,
|
||||||
|
"errors": len(errors),
|
||||||
|
"error_messages": errors[:10]
|
||||||
|
}
|
||||||
|
|
||||||
|
def validate_row(self, row: Dict[str, Any], row_idx: int) -> List[str]:
|
||||||
|
errors = []
|
||||||
|
|
||||||
|
# 校验月份
|
||||||
|
month = self._safe_month(row.get('月份'))
|
||||||
|
if not month:
|
||||||
|
errors.append(f"行{row_idx}: 月份格式错误")
|
||||||
|
|
||||||
|
# 校验支出类型
|
||||||
|
expense_type = row.get('支出类型', '').strip()
|
||||||
|
if expense_type not in EXPENSE_TYPES:
|
||||||
|
errors.append(f"行{row_idx}: 支出类型无效 '{expense_type}'")
|
||||||
|
|
||||||
|
# 校验金额
|
||||||
|
amount = self._safe_decimal(row.get('金额'))
|
||||||
|
if amount < 0:
|
||||||
|
errors.append(f"行{row_idx}: 金额不能为负数")
|
||||||
|
|
||||||
|
return errors
|
||||||
|
|
||||||
|
def transform_row(self, row: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
|
expense_type_name = row.get('支出类型', '').strip()
|
||||||
|
expense_type_code = EXPENSE_TYPES.get(expense_type_name, 'OTHER')
|
||||||
|
expense_category = EXPENSE_CATEGORIES.get(expense_type_code, 'OTHER')
|
||||||
|
|
||||||
|
return {
|
||||||
|
'site_id': self.site_id,
|
||||||
|
'tenant_id': self.tenant_id,
|
||||||
|
'expense_month': self._safe_month(row.get('月份')),
|
||||||
|
'expense_type_code': expense_type_code,
|
||||||
|
'expense_type_name': expense_type_name,
|
||||||
|
'expense_category': expense_category,
|
||||||
|
'expense_amount': self._safe_decimal(row.get('金额')),
|
||||||
|
'expense_detail': row.get('明细'),
|
||||||
|
'import_batch_no': self.batch_no,
|
||||||
|
'import_file_name': os.path.basename(str(row.get('_file_path', ''))),
|
||||||
|
'import_time': datetime.now(),
|
||||||
|
'import_user': os.getenv('USERNAME', 'system'),
|
||||||
|
'remark': row.get('备注'),
|
||||||
|
}
|
||||||
|
|
||||||
|
def insert_records(self, records: List[Dict[str, Any]]) -> int:
|
||||||
|
columns = [
|
||||||
|
'site_id', 'tenant_id', 'expense_month', 'expense_type_code',
|
||||||
|
'expense_type_name', 'expense_category', 'expense_amount',
|
||||||
|
'expense_detail', 'import_batch_no', 'import_file_name',
|
||||||
|
'import_time', 'import_user', 'remark'
|
||||||
|
]
|
||||||
|
|
||||||
|
cols_str = ", ".join(columns)
|
||||||
|
placeholders = ", ".join(["%s"] * len(columns))
|
||||||
|
sql = f"INSERT INTO {self.TARGET_TABLE} ({cols_str}) VALUES ({placeholders})"
|
||||||
|
|
||||||
|
inserted = 0
|
||||||
|
with self.db.conn.cursor() as cur:
|
||||||
|
for record in records:
|
||||||
|
values = [record.get(col) for col in columns]
|
||||||
|
cur.execute(sql, values)
|
||||||
|
inserted += cur.rowcount
|
||||||
|
|
||||||
|
self.db.commit()
|
||||||
|
return inserted
|
||||||
|
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# 平台结算导入
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
class PlatformSettlementImporter(BaseImporter):
|
||||||
|
"""
|
||||||
|
平台结算导入
|
||||||
|
|
||||||
|
Excel格式要求:
|
||||||
|
- 回款日期: 日期格式
|
||||||
|
- 平台类型: 美团/抖音/大众点评/其他
|
||||||
|
- 平台订单号: 字符串
|
||||||
|
- 订单原始金额: 数字
|
||||||
|
- 佣金: 数字
|
||||||
|
- 服务费: 数字
|
||||||
|
- 回款金额: 数字
|
||||||
|
- 备注: 可选
|
||||||
|
"""
|
||||||
|
|
||||||
|
TARGET_TABLE = "billiards_dws.dws_platform_settlement"
|
||||||
|
|
||||||
|
REQUIRED_COLUMNS = ['回款日期', '平台类型', '回款金额']
|
||||||
|
OPTIONAL_COLUMNS = ['平台订单号', '订单原始金额', '佣金', '服务费', '关联订单ID', '备注']
|
||||||
|
|
||||||
|
def import_file(self, file_path: str) -> Dict[str, Any]:
|
||||||
|
print(f"开始导入平台结算文件: {file_path}")
|
||||||
|
|
||||||
|
df = pd.read_excel(file_path)
|
||||||
|
|
||||||
|
missing_cols = [c for c in self.REQUIRED_COLUMNS if c not in df.columns]
|
||||||
|
if missing_cols:
|
||||||
|
return {"status": "ERROR", "message": f"缺少必要列: {missing_cols}"}
|
||||||
|
|
||||||
|
records = []
|
||||||
|
errors = []
|
||||||
|
|
||||||
|
for idx, row in df.iterrows():
|
||||||
|
row_dict = row.to_dict()
|
||||||
|
row_errors = self.validate_row(row_dict, idx + 2)
|
||||||
|
|
||||||
|
if row_errors:
|
||||||
|
errors.extend(row_errors)
|
||||||
|
continue
|
||||||
|
|
||||||
|
record = self.transform_row(row_dict)
|
||||||
|
records.append(record)
|
||||||
|
|
||||||
|
if errors:
|
||||||
|
print(f"校验错误: {len(errors)} 条")
|
||||||
|
for err in errors[:10]:
|
||||||
|
print(f" - {err}")
|
||||||
|
|
||||||
|
inserted = 0
|
||||||
|
if records:
|
||||||
|
inserted = self.insert_records(records)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"status": "SUCCESS" if not errors else "PARTIAL",
|
||||||
|
"batch_no": self.batch_no,
|
||||||
|
"total_rows": len(df),
|
||||||
|
"inserted": inserted,
|
||||||
|
"errors": len(errors),
|
||||||
|
}
|
||||||
|
|
||||||
|
def validate_row(self, row: Dict[str, Any], row_idx: int) -> List[str]:
|
||||||
|
errors = []
|
||||||
|
|
||||||
|
settlement_date = self._safe_date(row.get('回款日期'))
|
||||||
|
if not settlement_date:
|
||||||
|
errors.append(f"行{row_idx}: 回款日期格式错误")
|
||||||
|
|
||||||
|
platform_type = row.get('平台类型', '').strip()
|
||||||
|
if platform_type not in PLATFORM_TYPES:
|
||||||
|
errors.append(f"行{row_idx}: 平台类型无效 '{platform_type}'")
|
||||||
|
|
||||||
|
amount = self._safe_decimal(row.get('回款金额'))
|
||||||
|
if amount < 0:
|
||||||
|
errors.append(f"行{row_idx}: 回款金额不能为负数")
|
||||||
|
|
||||||
|
return errors
|
||||||
|
|
||||||
|
def transform_row(self, row: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
|
platform_name = row.get('平台类型', '').strip()
|
||||||
|
platform_type = PLATFORM_TYPES.get(platform_name, 'OTHER')
|
||||||
|
|
||||||
|
return {
|
||||||
|
'site_id': self.site_id,
|
||||||
|
'tenant_id': self.tenant_id,
|
||||||
|
'settlement_date': self._safe_date(row.get('回款日期')),
|
||||||
|
'platform_type': platform_type,
|
||||||
|
'platform_name': platform_name,
|
||||||
|
'platform_order_no': row.get('平台订单号'),
|
||||||
|
'order_settle_id': row.get('关联订单ID'),
|
||||||
|
'settlement_amount': self._safe_decimal(row.get('回款金额')),
|
||||||
|
'commission_amount': self._safe_decimal(row.get('佣金')),
|
||||||
|
'service_fee': self._safe_decimal(row.get('服务费')),
|
||||||
|
'gross_amount': self._safe_decimal(row.get('订单原始金额')),
|
||||||
|
'import_batch_no': self.batch_no,
|
||||||
|
'import_file_name': os.path.basename(str(row.get('_file_path', ''))),
|
||||||
|
'import_time': datetime.now(),
|
||||||
|
'import_user': os.getenv('USERNAME', 'system'),
|
||||||
|
'remark': row.get('备注'),
|
||||||
|
}
|
||||||
|
|
||||||
|
def insert_records(self, records: List[Dict[str, Any]]) -> int:
|
||||||
|
columns = [
|
||||||
|
'site_id', 'tenant_id', 'settlement_date', 'platform_type',
|
||||||
|
'platform_name', 'platform_order_no', 'order_settle_id',
|
||||||
|
'settlement_amount', 'commission_amount', 'service_fee',
|
||||||
|
'gross_amount', 'import_batch_no', 'import_file_name',
|
||||||
|
'import_time', 'import_user', 'remark'
|
||||||
|
]
|
||||||
|
|
||||||
|
cols_str = ", ".join(columns)
|
||||||
|
placeholders = ", ".join(["%s"] * len(columns))
|
||||||
|
sql = f"INSERT INTO {self.TARGET_TABLE} ({cols_str}) VALUES ({placeholders})"
|
||||||
|
|
||||||
|
inserted = 0
|
||||||
|
with self.db.conn.cursor() as cur:
|
||||||
|
for record in records:
|
||||||
|
values = [record.get(col) for col in columns]
|
||||||
|
cur.execute(sql, values)
|
||||||
|
inserted += cur.rowcount
|
||||||
|
|
||||||
|
self.db.commit()
|
||||||
|
return inserted
|
||||||
|
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# 充值提成导入
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
class RechargeCommissionImporter(BaseImporter):
|
||||||
|
"""
|
||||||
|
充值提成导入
|
||||||
|
|
||||||
|
Excel格式要求:
|
||||||
|
- 月份: 2026-01 格式
|
||||||
|
- 助教ID: 数字
|
||||||
|
- 助教花名: 字符串
|
||||||
|
- 充值订单金额: 数字
|
||||||
|
- 提成金额: 数字
|
||||||
|
- 充值订单号: 可选
|
||||||
|
- 备注: 可选
|
||||||
|
"""
|
||||||
|
|
||||||
|
TARGET_TABLE = "billiards_dws.dws_assistant_recharge_commission"
|
||||||
|
|
||||||
|
REQUIRED_COLUMNS = ['月份', '助教ID', '提成金额']
|
||||||
|
OPTIONAL_COLUMNS = ['助教花名', '充值订单金额', '充值订单ID', '充值订单号', '备注']
|
||||||
|
|
||||||
|
def import_file(self, file_path: str) -> Dict[str, Any]:
|
||||||
|
print(f"开始导入充值提成文件: {file_path}")
|
||||||
|
|
||||||
|
df = pd.read_excel(file_path)
|
||||||
|
|
||||||
|
missing_cols = [c for c in self.REQUIRED_COLUMNS if c not in df.columns]
|
||||||
|
if missing_cols:
|
||||||
|
return {"status": "ERROR", "message": f"缺少必要列: {missing_cols}"}
|
||||||
|
|
||||||
|
records = []
|
||||||
|
errors = []
|
||||||
|
|
||||||
|
for idx, row in df.iterrows():
|
||||||
|
row_dict = row.to_dict()
|
||||||
|
row_errors = self.validate_row(row_dict, idx + 2)
|
||||||
|
|
||||||
|
if row_errors:
|
||||||
|
errors.extend(row_errors)
|
||||||
|
continue
|
||||||
|
|
||||||
|
record = self.transform_row(row_dict)
|
||||||
|
records.append(record)
|
||||||
|
|
||||||
|
if errors:
|
||||||
|
print(f"校验错误: {len(errors)} 条")
|
||||||
|
for err in errors[:10]:
|
||||||
|
print(f" - {err}")
|
||||||
|
|
||||||
|
inserted = 0
|
||||||
|
if records:
|
||||||
|
inserted = self.insert_records(records)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"status": "SUCCESS" if not errors else "PARTIAL",
|
||||||
|
"batch_no": self.batch_no,
|
||||||
|
"total_rows": len(df),
|
||||||
|
"inserted": inserted,
|
||||||
|
"errors": len(errors),
|
||||||
|
}
|
||||||
|
|
||||||
|
def validate_row(self, row: Dict[str, Any], row_idx: int) -> List[str]:
|
||||||
|
errors = []
|
||||||
|
|
||||||
|
month = self._safe_month(row.get('月份'))
|
||||||
|
if not month:
|
||||||
|
errors.append(f"行{row_idx}: 月份格式错误")
|
||||||
|
|
||||||
|
assistant_id = row.get('助教ID')
|
||||||
|
if assistant_id is None or pd.isna(assistant_id):
|
||||||
|
errors.append(f"行{row_idx}: 助教ID不能为空")
|
||||||
|
|
||||||
|
amount = self._safe_decimal(row.get('提成金额'))
|
||||||
|
if amount < 0:
|
||||||
|
errors.append(f"行{row_idx}: 提成金额不能为负数")
|
||||||
|
|
||||||
|
return errors
|
||||||
|
|
||||||
|
def transform_row(self, row: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
|
recharge_amount = self._safe_decimal(row.get('充值订单金额'))
|
||||||
|
commission_amount = self._safe_decimal(row.get('提成金额'))
|
||||||
|
commission_ratio = commission_amount / recharge_amount if recharge_amount > 0 else None
|
||||||
|
|
||||||
|
return {
|
||||||
|
'site_id': self.site_id,
|
||||||
|
'tenant_id': self.tenant_id,
|
||||||
|
'assistant_id': int(row.get('助教ID')),
|
||||||
|
'assistant_nickname': row.get('助教花名'),
|
||||||
|
'commission_month': self._safe_month(row.get('月份')),
|
||||||
|
'recharge_order_id': row.get('充值订单ID'),
|
||||||
|
'recharge_order_no': row.get('充值订单号'),
|
||||||
|
'recharge_amount': recharge_amount,
|
||||||
|
'commission_amount': commission_amount,
|
||||||
|
'commission_ratio': commission_ratio,
|
||||||
|
'import_batch_no': self.batch_no,
|
||||||
|
'import_file_name': os.path.basename(str(row.get('_file_path', ''))),
|
||||||
|
'import_time': datetime.now(),
|
||||||
|
'import_user': os.getenv('USERNAME', 'system'),
|
||||||
|
'remark': row.get('备注'),
|
||||||
|
}
|
||||||
|
|
||||||
|
def insert_records(self, records: List[Dict[str, Any]]) -> int:
|
||||||
|
columns = [
|
||||||
|
'site_id', 'tenant_id', 'assistant_id', 'assistant_nickname',
|
||||||
|
'commission_month', 'recharge_order_id', 'recharge_order_no',
|
||||||
|
'recharge_amount', 'commission_amount', 'commission_ratio',
|
||||||
|
'import_batch_no', 'import_file_name', 'import_time',
|
||||||
|
'import_user', 'remark'
|
||||||
|
]
|
||||||
|
|
||||||
|
cols_str = ", ".join(columns)
|
||||||
|
placeholders = ", ".join(["%s"] * len(columns))
|
||||||
|
sql = f"INSERT INTO {self.TARGET_TABLE} ({cols_str}) VALUES ({placeholders})"
|
||||||
|
|
||||||
|
inserted = 0
|
||||||
|
with self.db.conn.cursor() as cur:
|
||||||
|
for record in records:
|
||||||
|
values = [record.get(col) for col in columns]
|
||||||
|
cur.execute(sql, values)
|
||||||
|
inserted += cur.rowcount
|
||||||
|
|
||||||
|
self.db.commit()
|
||||||
|
return inserted
|
||||||
|
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# 主函数
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(description='DWS Excel导入工具')
|
||||||
|
parser.add_argument(
|
||||||
|
'--type', '-t',
|
||||||
|
choices=['expense', 'platform', 'commission'],
|
||||||
|
required=True,
|
||||||
|
help='导入类型: expense(支出), platform(平台结算), commission(充值提成)'
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--file', '-f',
|
||||||
|
required=True,
|
||||||
|
help='Excel文件路径'
|
||||||
|
)
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
# 检查文件
|
||||||
|
if not os.path.exists(args.file):
|
||||||
|
print(f"文件不存在: {args.file}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# 加载配置
|
||||||
|
config = Config()
|
||||||
|
db = DatabaseConnection(config)
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 选择导入器
|
||||||
|
if args.type == 'expense':
|
||||||
|
importer = ExpenseImporter(config, db)
|
||||||
|
elif args.type == 'platform':
|
||||||
|
importer = PlatformSettlementImporter(config, db)
|
||||||
|
elif args.type == 'commission':
|
||||||
|
importer = RechargeCommissionImporter(config, db)
|
||||||
|
else:
|
||||||
|
print(f"未知的导入类型: {args.type}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# 执行导入
|
||||||
|
result = importer.import_file(args.file)
|
||||||
|
|
||||||
|
# 输出结果
|
||||||
|
print("\n" + "=" * 50)
|
||||||
|
print("导入结果:")
|
||||||
|
print(f" 状态: {result.get('status')}")
|
||||||
|
print(f" 批次号: {result.get('batch_no')}")
|
||||||
|
print(f" 总行数: {result.get('total_rows')}")
|
||||||
|
print(f" 插入行数: {result.get('inserted')}")
|
||||||
|
print(f" 错误行数: {result.get('errors')}")
|
||||||
|
|
||||||
|
if result.get('status') == 'ERROR':
|
||||||
|
print(f" 错误信息: {result.get('message')}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"导入失败: {e}")
|
||||||
|
db.rollback()
|
||||||
|
sys.exit(1)
|
||||||
|
finally:
|
||||||
|
db.close()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
81
etl_billiards/scripts/list_index_tables.py
Normal file
81
etl_billiards/scripts/list_index_tables.py
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""列出指数表数据"""
|
||||||
|
import sys
|
||||||
|
import io
|
||||||
|
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
|
||||||
|
sys.path.insert(0, '.')
|
||||||
|
from config.settings import AppConfig
|
||||||
|
from database.connection import DatabaseConnection
|
||||||
|
from database.operations import DatabaseOperations
|
||||||
|
|
||||||
|
config = AppConfig.load()
|
||||||
|
db_conn = DatabaseConnection(config.config['db']['dsn'])
|
||||||
|
db = DatabaseOperations(db_conn)
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# 1. 客户召回表
|
||||||
|
# ============================================================
|
||||||
|
print("=" * 60)
|
||||||
|
print("1. 客户召回表")
|
||||||
|
print("=" * 60)
|
||||||
|
print(f"{'客户姓名':<20} | {'召回指数':>8}")
|
||||||
|
print("-" * 60)
|
||||||
|
|
||||||
|
sql_recall = """
|
||||||
|
SELECT
|
||||||
|
COALESCE(m.nickname, CONCAT('会员', r.member_id)) AS member_name,
|
||||||
|
r.display_score AS recall_score
|
||||||
|
FROM billiards_dws.dws_member_recall_index r
|
||||||
|
LEFT JOIN billiards_dwd.dim_member m
|
||||||
|
ON r.member_id = m.member_id AND m.scd2_is_current = 1
|
||||||
|
ORDER BY r.display_score DESC
|
||||||
|
"""
|
||||||
|
|
||||||
|
rows = db.query(sql_recall)
|
||||||
|
for row in rows:
|
||||||
|
r = dict(row)
|
||||||
|
name = r['member_name'] or '未知'
|
||||||
|
score = r['recall_score']
|
||||||
|
print(f"{name:<20} | {score:>8.2f}")
|
||||||
|
|
||||||
|
print()
|
||||||
|
print(f"共 {len(rows)} 条记录")
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# 2. 助教客户关系表
|
||||||
|
# ============================================================
|
||||||
|
print()
|
||||||
|
print("=" * 60)
|
||||||
|
print("2. 助教客户关系表")
|
||||||
|
print("=" * 60)
|
||||||
|
print(f"{'助教花名':<12} | {'客户姓名':<20} | {'关系指数':>8}")
|
||||||
|
print("-" * 60)
|
||||||
|
|
||||||
|
sql_intimacy = """
|
||||||
|
SELECT
|
||||||
|
a.nickname AS assistant_name,
|
||||||
|
i.assistant_id AS assistant_no,
|
||||||
|
COALESCE(m.nickname, CONCAT('会员', i.member_id)) AS member_name,
|
||||||
|
i.display_score AS intimacy_score,
|
||||||
|
i.session_count,
|
||||||
|
i.attributed_recharge_amount
|
||||||
|
FROM billiards_dws.dws_member_assistant_intimacy i
|
||||||
|
LEFT JOIN billiards_dwd.dim_member m
|
||||||
|
ON i.member_id = m.member_id AND m.scd2_is_current = 1
|
||||||
|
LEFT JOIN billiards_dwd.dim_assistant a
|
||||||
|
ON i.assistant_id::text = a.assistant_no AND a.scd2_is_current = 1
|
||||||
|
ORDER BY i.display_score DESC, i.session_count DESC
|
||||||
|
"""
|
||||||
|
|
||||||
|
rows2 = db.query(sql_intimacy)
|
||||||
|
for row in rows2:
|
||||||
|
r = dict(row)
|
||||||
|
assistant = r['assistant_name'] or f"工号{r.get('assistant_no', '?')}"
|
||||||
|
member = r['member_name'] or '未知'
|
||||||
|
score = r['intimacy_score']
|
||||||
|
print(f"{assistant:<12} | {member:<20} | {score:>8.2f}")
|
||||||
|
|
||||||
|
print()
|
||||||
|
print(f"共 {len(rows2)} 条记录")
|
||||||
|
|
||||||
|
db_conn.close()
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user