初始提交:飞球 ETL 系统全量代码
This commit is contained in:
140
docs/开发笔记/test_inventory.md
Normal file
140
docs/开发笔记/test_inventory.md
Normal file
@@ -0,0 +1,140 @@
|
||||
# 单元测试清单(280 passed / 1 skipped)
|
||||
|
||||
> 最后更新:2026-02-12,基于 `pytest tests/unit -v` 输出。
|
||||
|
||||
## 概览
|
||||
|
||||
| 分类 | 测试文件 | 测试数 | 说明 |
|
||||
|------|---------|--------|------|
|
||||
| ETL 任务(在线) | `test_etl_tasks_online.py` | 14 | FakeAPI 模拟在线抓取,验证 14 个 ODS 任务 E/T/L |
|
||||
| ETL 任务(离线) | `test_etl_tasks_offline.py` | 14 | 本地 JSON 回放,验证离线入库链路 |
|
||||
| ETL 任务(分阶段) | `test_etl_tasks_stages.py` | 42 | 14 个任务 × 3 阶段(Extract/Transform/Load) |
|
||||
| ODS 通用任务 | `test_ods_tasks.py` | ~20+ | ODS 通用加载器任务测试 |
|
||||
| 解析器 | `test_parsers.py` | ~10+ | 数据类型解析(日期/金额/枚举) |
|
||||
| 配置管理 | `test_config.py` | ~10+ | AppConfig 加载、点号路径、分层覆盖 |
|
||||
| 接口路由 | `test_endpoint_routing.py` | ~5+ | 近期/历史接口路由规则 |
|
||||
| 报告工具 | `test_reporting.py` | ~5+ | 汇总格式化工具 |
|
||||
| 审计扫描 | `test_audit_*.py`(6 个文件) | ~40+ | 仓库审计:文件清单、流程树、文档对齐、报告属性 |
|
||||
| 关系指数 | `test_relation_index_base.py` | ~5+ | RS/OS/MS/ML 指数基础逻辑 |
|
||||
| **调度器重构(新增)** | 见下方 | **51** | TaskRegistry / TaskExecutor / PipelineRunner / CLI / E2E |
|
||||
|
||||
## 调度器重构新增测试(51 个)
|
||||
|
||||
### `test_task_registry.py` — TaskRegistry 单元测试(16 个)
|
||||
|
||||
| 测试类 | 测试方法 | 验证内容 |
|
||||
|--------|---------|---------|
|
||||
| `TestRegisterAndMetadata` | `test_register_with_defaults` | 仅传 task_code + task_class 时使用默认元数据 |
|
||||
| | `test_register_with_full_metadata` | 完整元数据注册(layer/task_type) |
|
||||
| | `test_register_utility_task` | 工具类任务 requires_db_config=False |
|
||||
| | `test_case_insensitive_lookup` | task_code 大小写不敏感 |
|
||||
| | `test_get_metadata_unknown_returns_none` | 未注册任务返回 None |
|
||||
| `TestCreateTask` | `test_create_task_returns_instance` | 创建任务实例(接口不变) |
|
||||
| | `test_create_task_unknown_raises` | 未知任务抛 ValueError |
|
||||
| `TestGetTasksByLayer` | `test_returns_matching_tasks` | 按层查询返回匹配任务 |
|
||||
| | `test_case_insensitive_layer` | 层名大小写不敏感 |
|
||||
| | `test_no_match_returns_empty` | 无匹配返回空列表 |
|
||||
| | `test_none_layer_excluded` | layer=None 不被任何层查询返回 |
|
||||
| `TestIsUtilityTask` | `test_utility_task` | requires_db_config=False → True |
|
||||
| | `test_normal_task` | requires_db_config=True → False |
|
||||
| | `test_unknown_task` | 未注册任务 → False |
|
||||
| `TestGetAllTaskCodes` | `test_returns_all_codes` | 返回所有已注册代码 |
|
||||
| | `test_empty_registry` | 空注册表返回空列表 |
|
||||
|
||||
|
||||
### `test_task_registry_properties.py` — TaskRegistry 属性测试(3 个类,~300 次迭代)
|
||||
|
||||
| 测试类 | 测试方法 | Property | 验证内容 | 迭代次数 |
|
||||
|--------|---------|----------|---------|---------|
|
||||
| `TestProperty8MetadataRoundTrip` | `test_metadata_round_trip` | P8 | 任意 task_code/requires_db/layer/task_type 组合注册后,get_metadata 返回完全相同的值 | 100 |
|
||||
| `TestProperty9BackwardCompatibleDefaults` | `test_legacy_register_uses_defaults` | P9 | 仅传 task_code + task_class 时,默认 requires_db_config=True、layer=None、task_type="etl" | 100 |
|
||||
| `TestProperty10GetTasksByLayer` | `test_get_tasks_by_layer_matches_manual_filter` | P10 | 注册一组任务后,按层查询结果与手动过滤完全一致 | 100 |
|
||||
|
||||
### `test_config_properties.py` — 配置映射属性测试(1 个类,100 次迭代)
|
||||
|
||||
| 测试类 | 测试方法 | Property | 验证内容 | 迭代次数 |
|
||||
|--------|---------|----------|---------|---------|
|
||||
| `TestProperty11FlowToDataSourceMapping` | `test_pipeline_flow_maps_to_data_source` | P11 | pipeline_flow(FULL/FETCH_ONLY/INGEST_ONLY)→ data_source(hybrid/online/offline)映射一致 | 100 |
|
||||
|
||||
### `test_task_executor_properties.py` — TaskExecutor 属性测试(4 个类,7 个方法,~700 次迭代)
|
||||
|
||||
| 测试类 | 测试方法 | Property | 验证内容 | 迭代次数 |
|
||||
|--------|---------|----------|---------|---------|
|
||||
| `TestProperty1DataSourceDeterminesPath` | `test_flow_includes_fetch` | P1 | data_source 为 online/hybrid 时 fetch=True,offline 时 fetch=False | 100 |
|
||||
| | `test_flow_includes_ingest` | P1 | data_source 为 offline/hybrid 时 ingest=True,online 时 ingest=False | 100 |
|
||||
| | `test_fetch_and_ingest_consistency` | P1 | hybrid 两者皆 True,online 仅 fetch,offline 仅 ingest | 100 |
|
||||
| `TestProperty2SuccessAdvancesCursor` | `test_success_with_window_advances_cursor` | P2 | 成功任务调用 cursor_mgr.advance,传入正确的 window_start/window_end | 100 |
|
||||
| `TestProperty3FailureMarksFailAndReraises` | `test_exception_marks_fail_and_reraises` | P3 | 异常时 run_tracker.update_run(status="FAIL") 并重新抛出原始异常 | 100 |
|
||||
| `TestProperty4UtilityTaskDeterminedByMetadata` | `test_utility_task_skips_cursor_and_run_tracker` | P4 | 工具类任务(requires_db_config=False)跳过游标和运行记录 | 100 |
|
||||
| | `test_non_utility_task_uses_cursor_and_run_tracker` | P4 | 非工具类任务使用游标和运行记录 | 100 |
|
||||
|
||||
### `test_pipeline_runner_properties.py` — PipelineRunner 属性测试(3 个类,8 个方法,~800 次迭代)
|
||||
|
||||
| 测试类 | 测试方法 | Property | 验证内容 | 迭代次数 |
|
||||
|--------|---------|----------|---------|---------|
|
||||
| `TestProperty5PipelineNameToLayers` | `test_layers_match_pipeline_definition` | P5 | run() 返回的 layers 与 PIPELINE_LAYERS[pipeline] 完全一致 | 100 |
|
||||
| | `test_resolve_tasks_called_with_correct_layers` | P5 | _resolve_tasks 接收的层列表与定义一致 | 100 |
|
||||
| `TestProperty6ProcessingModeControlsFlow` | `test_increment_executes_iff_mode_contains_increment` | P6 | 增量 ETL 执行当且仅当 mode 包含 "increment" | 100 |
|
||||
| | `test_verification_executes_iff_mode_contains_verify` | P6 | 校验流程执行当且仅当 mode 包含 "verify" | 100 |
|
||||
| `TestProperty7PipelineSummaryCompleteness` | `test_summary_has_required_fields` | P7 | 返回字典包含 status/pipeline/layers/results/verification_summary | 100 |
|
||||
| | `test_results_length_equals_executed_tasks` | P7 | results 长度等于实际执行的任务数 | 100 |
|
||||
| | `test_pipeline_and_layers_match_input` | P7 | 返回的 pipeline 和 layers 与输入一致 | 100 |
|
||||
| | `test_increment_only_has_no_verification` | P7 | increment_only 模式下 verification_summary 为 None | 100 |
|
||||
|
||||
### `test_filter_verify_tables.py` — 校验表过滤单元测试(9 个)
|
||||
|
||||
| 测试类 | 测试方法 | 验证内容 |
|
||||
|--------|---------|---------|
|
||||
| `TestFilterVerifyTables` | `test_none_input_returns_none` | 输入 None 返回 None |
|
||||
| | `test_empty_list_returns_none` | 空列表返回 None |
|
||||
| | `test_dwd_layer_filters_correctly` | DWD 层过滤:保留 dwd_/dim_/fact_ 前缀 |
|
||||
| | `test_dws_layer_filters_correctly` | DWS 层过滤:保留 dws_ 前缀 |
|
||||
| | `test_index_layer_filters_correctly` | INDEX 层过滤:保留 v_/wbi_/nci_ 等前缀 |
|
||||
| | `test_ods_layer_filters_correctly` | ODS 层过滤:保留 ods_ 前缀 |
|
||||
| | `test_unknown_layer_returns_normalized` | 未知层返回归一化后的全部表名 |
|
||||
| | `test_layer_case_insensitive` | 层名大小写不敏感 |
|
||||
| | `test_whitespace_and_empty_entries_stripped` | 空白和空条目被过滤 |
|
||||
|
||||
### `test_cli_args.py` — CLI 参数解析单元测试(14 个)
|
||||
|
||||
| 测试类 | 测试方法 | 验证内容 |
|
||||
|--------|---------|---------|
|
||||
| `TestDataSourceArg` | `test_data_source_valid_values` (×3) | --data-source 接受 online/offline/hybrid |
|
||||
| | `test_data_source_default_is_none` | 未指定时默认 None |
|
||||
| `TestResolveDataSource` | `test_explicit_data_source_returns_directly` | 显式 --data-source 直接返回 |
|
||||
| | `test_data_source_takes_priority_over_pipeline_flow` | --data-source 优先于 --pipeline-flow |
|
||||
| | `test_pipeline_flow_maps_with_deprecation_warning` (×3) | 旧参数映射 + 弃用警告 |
|
||||
| | `test_neither_arg_defaults_to_hybrid` | 两者都未指定时默认 hybrid |
|
||||
| `TestBuildCliOverrides` | `test_data_source_online_sets_run_key` | --data-source 写入 run.data_source |
|
||||
| | `test_pipeline_flow_sets_both_keys` | 旧参数同时写入 pipeline.flow 和 run.data_source |
|
||||
| | `test_default_data_source_is_hybrid` | 默认 run.data_source 为 hybrid |
|
||||
| `TestPipelineAndTasks` | `test_pipeline_and_tasks_both_parsed` | --pipeline + --tasks 同时解析 |
|
||||
|
||||
### `test_e2e_flow.py` — 端到端流程集成测试(4 个)
|
||||
|
||||
| 测试类 | 测试方法 | 验证内容 |
|
||||
|--------|---------|---------|
|
||||
| `TestTraditionalModeE2E` | `test_run_tasks_executes_utility_task_and_returns_results` | TaskExecutor.run_tasks 工具类任务端到端 |
|
||||
| `TestPipelineModeE2E` | `test_pipeline_delegates_to_executor_and_returns_structure` | PipelineRunner → TaskExecutor 委托 + 返回结构 |
|
||||
| | `test_pipeline_verify_only_skips_increment` | verify_only 模式跳过增量 ETL |
|
||||
| `TestSchedulerThinWrapper` | `test_scheduler_delegates_run_tasks` | ETLScheduler 薄包装层正确委托 TaskExecutor/PipelineRunner |
|
||||
|
||||
---
|
||||
|
||||
## 属性测试(PBT)汇总
|
||||
|
||||
| Property | 所属组件 | 验证需求 | 迭代次数 |
|
||||
|----------|---------|---------|---------|
|
||||
| P1 | TaskExecutor | data_source 参数决定执行路径(Req 1.2) | 300 |
|
||||
| P2 | TaskExecutor | 成功任务推进游标(Req 1.3) | 100 |
|
||||
| P3 | TaskExecutor | 失败任务标记 FAIL 并重新抛出(Req 1.4) | 100 |
|
||||
| P4 | TaskExecutor | 工具类任务由元数据决定(Req 1.6, 4.2) | 200 |
|
||||
| P5 | PipelineRunner | 管道名称→层列表映射(Req 2.1) | 200 |
|
||||
| P6 | PipelineRunner | processing_mode 控制执行流程(Req 2.3, 2.4) | 200 |
|
||||
| P7 | PipelineRunner | 管道结果汇总完整性(Req 2.6) | 400 |
|
||||
| P8 | TaskRegistry | 元数据 round-trip(Req 4.1) | 100 |
|
||||
| P9 | TaskRegistry | 向后兼容默认值(Req 4.4) | 100 |
|
||||
| P10 | TaskRegistry | 按层查询任务(Req 4.3) | 100 |
|
||||
| P11 | AppConfig | pipeline_flow → data_source 映射一致性(Req 8.1-8.4, 5.2) | 100 |
|
||||
|
||||
总计:11 个属性,~1900 次迭代。
|
||||
34
docs/开发笔记/在线抓取,更新ODS ,然后将更新的ODS内容,对应到DWD的更新。.md
Normal file
34
docs/开发笔记/在线抓取,更新ODS ,然后将更新的ODS内容,对应到DWD的更新。.md
Normal file
@@ -0,0 +1,34 @@
|
||||
在线抓取,更新ODS ,然后将更新的ODS内容,对应到DWD的更新。
|
||||
|
||||
|
||||
可以按“两段定时”跑:先在线抓取+入库更新 ODS,再跑 DWD_LOAD_FROM_ODS 把新增/变更同步到 DWD。CLI 用 python -m etl_billiards.cli.main。
|
||||
|
||||
1) ODS:在线抓取 + 入库(FULL)
|
||||
python -m etl_billiards.cli.main ^
|
||||
--pipeline-flow FULL ^
|
||||
--tasks PRODUCTS,TABLES,MEMBERS,ASSISTANTS,PACKAGES_DEF,ORDERS,PAYMENTS,REFUNDS,COUPON_USAGE,INVENTORY_CHANGE,TOPUPS,TABLE_DISCOUNT,ASSISTANT_ABOLISH,LEDGER ^
|
||||
--pg-dsn "%PG_DSN%" ^
|
||||
--store-id %STORE_ID% ^
|
||||
--api-token "%API_TOKEN%"
|
||||
(可选)指定落盘目录:加 --fetch-root "export/JSON";美化 JSON:--write-pretty-json
|
||||
|
||||
2) DWD:ODS → DWD
|
||||
python -m etl_billiards.cli.main ^
|
||||
--pipeline-flow INGEST_ONLY ^
|
||||
--tasks DWD_LOAD_FROM_ODS ^
|
||||
--pg-dsn "%PG_DSN%" ^
|
||||
--store-id %STORE_ID%
|
||||
推荐的环境变量
|
||||
PG_DSN=postgresql://user:pwd@host:5432/db
|
||||
STORE_ID=...
|
||||
API_TOKEN=...
|
||||
(可选)JSON_FETCH_ROOT=... / FETCH_ROOT=...,LOG_ROOT=...
|
||||
如果你希望“一条命令顺序跑完 ODS+DWD”,也可以直接:
|
||||
|
||||
python -m etl_billiards.cli.main ^
|
||||
--pipeline-flow FULL ^
|
||||
--tasks PRODUCTS,TABLES,MEMBERS,ASSISTANTS,PACKAGES_DEF,ORDERS,PAYMENTS,REFUNDS,COUPON_USAGE,INVENTORY_CHANGE,TOPUPS,TABLE_DISCOUNT,ASSISTANT_ABOLISH,LEDGER,DWD_LOAD_FROM_ODS ^
|
||||
--pg-dsn "%PG_DSN%" ^
|
||||
--store-id %STORE_ID% ^
|
||||
--api-token "%API_TOKEN%"
|
||||
(这会对前半段任务走在线抓取+入库,对 DWD_LOAD_FROM_ODS 只做入库阶段,因为它没有抓取逻辑。)
|
||||
455
docs/开发笔记/更新关系指数.txt
Normal file
455
docs/开发笔记/更新关系指数.txt
Normal file
@@ -0,0 +1,455 @@
|
||||
PRD:保留 NCI / WBI,删除亲密指数,新增 RS / OS / MS / ML 指数体系
|
||||
|
||||
生效目标:用**客户级(NCI/WBI)负责“触达优先级”,用关系级(RS/OS/MS/ML)**负责“归属与执行”,替代原 INTIMACY 的混合口径,提升可解释性与可运营性。
|
||||
统计与映射方法沿用现有“时间衰减 + 分位截断 + 0–10 映射 + 可选 EWMA 平滑”的工程框架(减少改造风险)。
|
||||
|
||||
1. 背景与问题
|
||||
1.1 背景
|
||||
|
||||
当前体系已有两类客户级指数:
|
||||
|
||||
NCI(新客转化指数):用于新客欢迎与转化排序
|
||||
|
||||
WBI(老客挽回指数):用于老客召回排序
|
||||
|
||||
同时存在一个关系级的 亲密指数(INTIMACY),但它把“关系强度、归属、升温、付费关联”混在一个分数里,导致:
|
||||
|
||||
一个分数承担多个运营目的,解释困难,策略难稳定
|
||||
|
||||
动量(burst)乘法放大会掩盖“真实关系强度”
|
||||
|
||||
充值归因可靠性要求高,一旦归因口径瑕疵会放大偏差
|
||||
|
||||
1.2 目标
|
||||
|
||||
保留 NCI / WBI 不变(持续作为客户级“触达优先级”)
|
||||
|
||||
删除 INTIMACY(停止计算/停止被消费)
|
||||
|
||||
新增四个关系级指数:RS / OS / MS / ML
|
||||
|
||||
RS:关系强度(熟不熟)
|
||||
|
||||
OS:归属份额(主要归谁)
|
||||
|
||||
MS:动量升温(最近是否回暖/升温)
|
||||
|
||||
ML:付费关联(推增值/储值由谁推更可能成)
|
||||
|
||||
2. 术语与设计依据(对齐工程约定)
|
||||
2.1 时间衰减(半衰期)
|
||||
|
||||
统一采用半衰期形式的指数衰减,保证“越近越重要”的可解释性。
|
||||
|
||||
2.2 0–10 映射(展示分)
|
||||
|
||||
统一采用:
|
||||
|
||||
P5/P95 分位截断(Winsorize)降低极端值影响
|
||||
|
||||
可选压缩(log1p/asinh)
|
||||
|
||||
MinMax 映射到 0–10
|
||||
|
||||
可选 EWMA 平滑减少跨批次抖动
|
||||
|
||||
2.3 价值/触达优先级方法论(NCI/WBI 保留原因)
|
||||
|
||||
RFM(Recency/Frequency/Monetary)作为客户分层与运营触达优先级的主流方法,与你的 WBI/NCI 结构一致(尤其是 recency + frequency + monetary 信号)。
|
||||
其中 NCI/WBI 继续承担“客户级排序”,关系级指数负责“落人/归属/推荐成功率”。
|
||||
|
||||
3. 用户与核心运营场景
|
||||
3.1 角色
|
||||
|
||||
店长/运营:配置策略、看板、复盘
|
||||
|
||||
助教主管:分派任务、监控撞单/共管
|
||||
|
||||
助教:执行跟进、召回、增值推荐
|
||||
|
||||
3.2 场景总览(决策逻辑分层)
|
||||
|
||||
客户级:要不要触达、先触达谁 → NCI / WBI
|
||||
|
||||
关系级:由谁触达、怎么触达 → OS(定责)+ RS/MS/ML(定策略)
|
||||
|
||||
4. 新指标定义(删除 INTIMACY,新增 RS/OS/MS/ML)
|
||||
|
||||
粒度说明:RS/OS/MS/ML 均为 (site_id, member_id, assistant_id) 关系对粒度。
|
||||
数据基础与会话合并逻辑沿用原 INTIMACY 的服务日志抽取与 session merge(减少工程变动)。
|
||||
NCI/WBI 完全保留原逻辑与输出。
|
||||
|
||||
4.1 RS:关系强度(Relationship Strength)
|
||||
|
||||
用途:判断“这位助教与该客户是否真的熟、关系是否牢”。
|
||||
核心输入:
|
||||
|
||||
合并会话后的:次数、时长、课型权重(基础/附加)
|
||||
|
||||
距今天数(会话结束时间)
|
||||
|
||||
最近一次服务距今天数
|
||||
|
||||
计算(建议口径):
|
||||
|
||||
会话权重:τ_i = 1.0(基础) 或 incentive_weight(附加)
|
||||
|
||||
会话衰减:decay(d; h) = exp(-ln(2)*d/h)
|
||||
|
||||
频次项:F = Σ ( τ_i * decay(d_i; h_session) )
|
||||
|
||||
时长项:D = Σ ( sqrt(dur_hours_i) * τ_i * decay(d_i; h_session) )
|
||||
|
||||
最近门控:R = decay(days_since_last; h_last)
|
||||
|
||||
RS_raw:
|
||||
|
||||
base = w_F*F + w_D*D
|
||||
|
||||
gate = R^(gate_alpha)
|
||||
|
||||
RS_raw = base * gate
|
||||
|
||||
输出:
|
||||
|
||||
RS_raw
|
||||
|
||||
RS_display(0–10,沿用通用映射与可选 EWMA)
|
||||
|
||||
4.2 OS:归属份额(Ownership Share)
|
||||
|
||||
用途:解决“客户到底归谁主跟,避免多人撞单”。
|
||||
核心输入:同一客户在所有助教上的 RS_raw。
|
||||
|
||||
计算:
|
||||
|
||||
对每个 member_id:OS = RS_raw_i / (Σ RS_raw_all_assistants + eps)
|
||||
|
||||
加入噪声门槛:
|
||||
|
||||
若 RS_raw_i < min_rs_raw_for_ownership,则 OS 视为 0(不参与归属)
|
||||
|
||||
若 Σ RS_raw_all_assistants < min_total_rs_raw,则该客户视为“未形成稳定归属”
|
||||
|
||||
输出:
|
||||
|
||||
OS_share(0–1,推荐 UI 显示百分比)
|
||||
|
||||
OS_label(主责/共管/公海):
|
||||
|
||||
主责:OS_share >= ownership_main_threshold
|
||||
|
||||
共管:ownership_comanage_threshold <= OS_share < ownership_main_threshold
|
||||
|
||||
公海:OS_share < ownership_comanage_threshold
|
||||
|
||||
OS 不建议做分位映射(它是份额值,天然可解释)。如必须 0–10,只做 OS_share*10 的线性映射。
|
||||
|
||||
4.3 MS:动量升温(Momentum)
|
||||
|
||||
用途:判断“最近是否升温/回流”,用于跟进紧急程度(而不是关系强度)。
|
||||
核心输入:短期与长期的加权频次(含衰减与课型权重)。
|
||||
|
||||
计算:
|
||||
|
||||
F_short = Σ( τ_i * decay(d_i; h_short) )
|
||||
|
||||
F_long = Σ( τ_i * decay(d_i; h_long) )
|
||||
|
||||
ratio = (F_short + eps) / (F_long + eps)
|
||||
|
||||
MS_raw(只保留升温部分):
|
||||
|
||||
MS_raw = max(0, ln(ratio))
|
||||
|
||||
输出:
|
||||
|
||||
MS_raw
|
||||
|
||||
MS_display(0–10,通用映射与可选 EWMA)
|
||||
|
||||
4.4 ML:付费关联(Monetization Link)
|
||||
|
||||
用途:判断“推储值/增值由谁推更可能成”,用于选择执行人/协同人。
|
||||
核心输入:服务后短窗口内的充值归因、金额、时间衰减。
|
||||
|
||||
关键前提(必须做的口径修复):
|
||||
|
||||
单笔充值只归因一个助教:采用“last-touch”原则
|
||||
|
||||
对每笔充值:在归因窗口内,找 session_end <= pay_time 且最接近 pay_time 的那条会话对应的助教归因
|
||||
|
||||
计算:
|
||||
|
||||
对每笔归因充值 r:
|
||||
|
||||
金额压缩:ln(1 + amt / amount_base)
|
||||
|
||||
时间衰减:decay(days_ago_r; h_recharge)
|
||||
|
||||
ML_raw = Σ( ln(1 + amt/amount_base) * decay(days_ago_r; h_recharge) )
|
||||
|
||||
输出:
|
||||
|
||||
ML_raw
|
||||
|
||||
ML_display(0–10,通用映射与可选 EWMA)
|
||||
|
||||
5. 数据依赖与产出表
|
||||
5.1 输入数据(沿用原 INTIMACY 的服务/充值口径)
|
||||
|
||||
服务日志:billiards_dwd.dwd_assistant_service_log + billiards_dwd.dim_assistant
|
||||
|
||||
充值订单:billiards_dwd.dwd_recharge_order(settle_type=5)
|
||||
|
||||
课型映射:cfg_skill_type(决定 BONUS/BASE 权重)
|
||||
|
||||
5.2 输出表(新增)
|
||||
|
||||
建议新增统一关系表(替代原 dws_member_assistant_intimacy):
|
||||
|
||||
表名:billiards_dws.dws_member_assistant_relation_index
|
||||
主键:(site_id, member_id, assistant_id)
|
||||
核心字段:
|
||||
|
||||
基础特征(用于解释与排查):
|
||||
session_count, total_duration_minutes, basic_session_count, incentive_session_count, days_since_last_session
|
||||
attributed_recharge_count, attributed_recharge_amount
|
||||
|
||||
指数:
|
||||
rs_raw, rs_display
|
||||
os_share, os_label, os_rank
|
||||
ms_raw, ms_display
|
||||
ml_raw, ml_display
|
||||
|
||||
时间:calc_time, created_at, updated_at
|
||||
|
||||
NCI/WBI 输出表保持不变。
|
||||
|
||||
5.3 删除/下线(原 INTIMACY)
|
||||
|
||||
停止写入:billiards_dws.dws_member_assistant_intimacy
|
||||
|
||||
兼容期保留表但不再更新,前端与运营入口不再读取
|
||||
|
||||
6. cfg_index_parameters 配置体系更新(你要求的“更新”核心)
|
||||
6.1 现有配置表结构(保持不变)
|
||||
|
||||
cfg_index_parameters 继续使用现有字段:
|
||||
(param_id, index_type, param_name, param_value, description, effective_from, effective_to, created_at, updated_at)
|
||||
|
||||
6.2 index_type 更新范围
|
||||
|
||||
保留:NCI, WBI(不改参数,不改逻辑)
|
||||
|
||||
删除:INTIMACY(通过 effective_to 失效,不再读取)
|
||||
|
||||
新增:RS, OS, MS, ML
|
||||
|
||||
其它 index_type(如 RECALL)本 PRD 不涉及,保持现状。
|
||||
|
||||
7. 新增参数清单(默认值建议)
|
||||
|
||||
说明:参数名尽量沿用现有风格(snake_case),并把“展示映射参数”复用到 RS/MS/ML 三个需要 0–10 映射的指数上。Winsorize 与 EWMA 的合理性见设计依据。
|
||||
|
||||
7.1 RS 参数(index_type = RS)
|
||||
param_name 默认值 说明
|
||||
lookback_days 60 回看窗口(天)
|
||||
session_merge_hours 4 会话合并间隔(小时)
|
||||
incentive_weight 1.5 附加课权重
|
||||
halflife_session 14 会话衰减半衰期(天)
|
||||
halflife_last 10 最近服务衰减半衰期(天)
|
||||
weight_F 1.0 频次项权重
|
||||
weight_D 0.7 时长项权重
|
||||
gate_alpha 0.6 最近门控幂次(越大越强调“必须最近”)
|
||||
percentile_lower 5 映射下分位
|
||||
percentile_upper 95 映射上分位
|
||||
compression_mode 1 0=none,1=log1p,2=asinh
|
||||
use_smoothing 1 是否启用 EWMA 平滑
|
||||
ewma_alpha 0.2 EWMA α
|
||||
7.2 OS 参数(index_type = OS)
|
||||
param_name 默认值 说明
|
||||
min_rs_raw_for_ownership 0.05 归属噪声门槛(低于此 RS_raw 不参与 OS)
|
||||
min_total_rs_raw 0.10 客户总体关系强度过低则视为“未形成归属”
|
||||
ownership_main_threshold 0.60 OS 主责阈值
|
||||
ownership_comanage_threshold 0.35 OS 共管阈值
|
||||
eps 1e-6 分母保护
|
||||
7.3 MS 参数(index_type = MS)
|
||||
param_name 默认值 说明
|
||||
lookback_days 60 回看窗口(天)
|
||||
session_merge_hours 4 会话合并间隔(小时)
|
||||
incentive_weight 1.5 附加课权重
|
||||
halflife_short 7 短期半衰期(天)
|
||||
halflife_long 30 长期半衰期(天)
|
||||
eps 1e-6 比值保护
|
||||
percentile_lower 5 映射下分位
|
||||
percentile_upper 95 映射上分位
|
||||
compression_mode 1 0=none,1=log1p,2=asinh
|
||||
use_smoothing 1 是否启用 EWMA 平滑
|
||||
ewma_alpha 0.2 EWMA α
|
||||
7.4 ML 参数(index_type = ML)
|
||||
param_name 默认值 说明
|
||||
lookback_days 60 回看窗口(天)
|
||||
recharge_attribute_hours 1 充值归因窗口(小时)
|
||||
attribution_mode 1 1=last_touch(单笔充值只归因一个助教)
|
||||
amount_base 500 金额压缩基数
|
||||
halflife_recharge 21 充值衰减半衰期(天)
|
||||
percentile_lower 5 映射下分位
|
||||
percentile_upper 95 映射上分位
|
||||
compression_mode 1 0=none,1=log1p,2=asinh
|
||||
use_smoothing 1 是否启用 EWMA 平滑
|
||||
ewma_alpha 0.2 EWMA α
|
||||
8. 配置迁移策略(cfg_index_parameters 具体更新方式)
|
||||
8.1 INTIMACY 下线
|
||||
|
||||
将 index_type = 'INTIMACY' 的现行有效参数统一设置 effective_to = 下线日(建议为新版本生效日前一天)
|
||||
|
||||
代码层:不再加载/执行 INTIMACY 任务
|
||||
|
||||
8.2 新增 RS/OS/MS/ML 参数
|
||||
|
||||
插入上述四个 index_type 的参数行,设置 effective_from = 新版本生效日
|
||||
|
||||
param_id 由数据库自增生成(不在 PRD 固定)
|
||||
|
||||
8.3 生效日期建议
|
||||
|
||||
当前日期为 2026-02-08(台北时区),建议:
|
||||
|
||||
新增四类参数 effective_from = 2026-02-09
|
||||
|
||||
INTIMACY effective_to = 2026-02-08
|
||||
|
||||
9. 任务与工程改造范围
|
||||
9.1 ETL 任务
|
||||
|
||||
保留:NCI/WBI 原任务
|
||||
|
||||
新增:RelationIndexTask(或拆为 RS/MS/ML/OS 四个任务,但建议一个任务产出一张关系表)
|
||||
|
||||
删除/停用:原 IntimacyIndexTask
|
||||
|
||||
9.2 关键工程点(必须实现)
|
||||
|
||||
复用 session merge(降低风险)
|
||||
|
||||
充值归因改为 last_touch 单归因(ML 可靠性的硬前提)
|
||||
|
||||
RS/MS/ML 的 display 映射复用 BaseIndexTask(一致性与可调参性)
|
||||
|
||||
OS 份额化与标签化(防撞单的唯一有效方式)
|
||||
|
||||
10. 运营使用方式(落地规则)
|
||||
10.1 任务队列(建议固定四条队列)
|
||||
|
||||
新客欢迎:按 NCI_welcome 排序
|
||||
|
||||
新客转化:按 NCI_convert 排序
|
||||
|
||||
老客召回:按 WBI 排序
|
||||
|
||||
活跃升温承接:按 MS 排序
|
||||
|
||||
10.2 “落人”规则(所有队列通用)
|
||||
|
||||
有明确归属:按 OS_label=主责 的助教派单
|
||||
|
||||
共管:只派给主责助教,协同人由 ML 或 RS 次高者确定
|
||||
|
||||
公海:派给当班/新客官/运营池
|
||||
|
||||
10.3 增值推荐(谁推更可能成)
|
||||
|
||||
选客户:以 NCI/WBI 中的价值/充值未回访信号做筛选
|
||||
|
||||
选执行人:以 ML 高者为主,OS 作为责任边界,RS 决定话术深度
|
||||
|
||||
11. 验收标准(可测试、可回归)
|
||||
11.1 数据正确性
|
||||
|
||||
OS:同一 member_id 下所有 assistant_id 的 OS_share(参与归属者)求和≈1
|
||||
|
||||
RS:新增会话、会话更近、时长更长 → RS_raw 单调上升(统计抽样验证)
|
||||
|
||||
MS:短期频次明显高于长期 → MS_raw>0;否则 MS_raw=0
|
||||
|
||||
ML:充值发生在归因窗口内且越近越大 → ML_raw 越高;且单笔充值只归因一个助教
|
||||
|
||||
11.2 运营可用性
|
||||
|
||||
至少能稳定支持:
|
||||
|
||||
客户分配(OS)
|
||||
|
||||
跟进紧急程度(MS)
|
||||
|
||||
召回优先级(WBI)
|
||||
|
||||
新客欢迎/转化(NCI)
|
||||
|
||||
推增值选人(ML)
|
||||
|
||||
12. 风险与对策
|
||||
|
||||
OS 噪声归属:低互动关系也被算份额
|
||||
→ 用 min_rs_raw_for_ownership 与 min_total_rs_raw 双门槛
|
||||
|
||||
ML 偏差:归因口径不稳定导致误导选人
|
||||
→ 强制 last_touch 单归因;窗口可调;上线初期做抽样对账
|
||||
|
||||
display 分数跨批次漂移(相对分固有属性)
|
||||
→ 开启 EWMA 平滑降低短期抖动
|
||||
|
||||
13. 上线与灰度建议
|
||||
|
||||
第 1 阶段(影子跑数):RS/OS/MS/ML 与 INTIMACY 并行计算但不对外展示(1–2 周)
|
||||
|
||||
第 2 阶段(切读):前端/运营策略只读 RS/OS/MS/ML;INTIMACY 停止消费
|
||||
|
||||
第 3 阶段(下线):停更 INTIMACY 表,保留历史查询周期后再清理
|
||||
|
||||
14. 交付物清单
|
||||
|
||||
新增关系表:dws_member_assistant_relation_index
|
||||
|
||||
新增指数任务:RelationIndexTask(含 RS/OS/MS/ML)
|
||||
|
||||
cfg_index_parameters:
|
||||
|
||||
INTIMACY 参数失效(effective_to)
|
||||
|
||||
新增 RS/OS/MS/ML 参数(effective_from)
|
||||
|
||||
运营端:
|
||||
|
||||
队列:新客欢迎/新客转化/老客召回/升温承接
|
||||
|
||||
客户详情:展示 NCI、WBI、以及每位助教的 RS/OS/MS/ML
|
||||
|
||||
15. 实施定稿补充(2026-02-08)
|
||||
|
||||
1. ML 数据源定稿为“人工台账唯一真源”:
|
||||
- `dws_ml_manual_order_alloc` 为 ML 主口径输入;
|
||||
- 无台账时 `ML_raw=0`;
|
||||
- `dwd_recharge_order` 的 last-touch 仅保留备用代码路径(默认关闭,`ML.source_mode=0`)。
|
||||
|
||||
2. 台账规则定稿:
|
||||
- 一单可归多个助教,默认均分;
|
||||
- `external_id` 作为订单ID,必填;
|
||||
- 同一 `(site_id, external_id, assistant_id)` 重复导入时覆盖;
|
||||
- 覆盖边界:
|
||||
- 30天内:按 `site_id + biz_date` 日覆盖;
|
||||
- 超过30天:按固定纪元 `2026-01-01` 的 30 天桶覆盖。
|
||||
|
||||
3. 关系指数任务形态:
|
||||
- 单任务 `RelationIndexTask` 一次产出 RS/OS/MS/ML;
|
||||
- 输出表:`dws_member_assistant_relation_index`;
|
||||
- `RS/MS/ML` 分位映射与 EWMA 历史按 `index_type` 隔离。
|
||||
|
||||
4. 参数补充:
|
||||
- `index_type=OS` 新增 `ownership_gap_threshold`(默认 0.15);
|
||||
- `index_type=ML` 新增 `source_mode`(默认 0,manual_only)。
|
||||
|
||||
5. 上线策略修订:
|
||||
- 当前未正式上线,直接切换读新表;
|
||||
- 影子期不再作为强制步骤。
|
||||
11
docs/开发笔记/现在进行ETL全流程测试。.txt
Normal file
11
docs/开发笔记/现在进行ETL全流程测试。.txt
Normal file
@@ -0,0 +1,11 @@
|
||||
现在进行ETL全流程测试。
|
||||
按以下要求后台运行此任务。
|
||||
你每隔1-10分析,获取一遍输出,及时DEBUG。
|
||||
并对完成的没有报错的内容,在数据来源侧和数据落回测进行数据比对。
|
||||
|
||||
[09:56:35] [环境变量] WINDOW_SPLIT_UNIT=month
|
||||
[09:56:35] [环境变量] INDEX_LOOKBACK_DAYS=180
|
||||
[09:56:35] [环境变量] VERIFY_SKIP_ODS_ON_FETCH=true
|
||||
[09:56:35] [环境变量] VERIFY_ODS_LOCAL_JSON=true
|
||||
[09:56:35] [工作目录] C:\dev\LLTQ\ETL\feiqiu-ETL\etl_billiards
|
||||
[09:56:35] [执行命令] python -m cli.main --pipeline api_full --processing-mode verify_only --fetch-before-verify --window-start 2025-07-07 09:55:21 --window-end 2026-02-09 09:55:21 --window-split month --tasks ODS_MEMBER_BALANCE,ODS_MEMBER,ODS_MEMBER_CARD,ODS_SETTLEMENT_RECORDS,ODS_PAYMENT,ODS_RECHARGE_SETTLE,ODS_REFUND,ODS_SETTLEMENT_TICKET,ODS_ASSISTANT_LEDGER,ODS_ASSISTANT_ABOLISH,ODS_ASSISTANT_ACCOUNT,ODS_TENANT_GOODS,ODS_STORE_GOODS,ODS_STORE_GOODS_SALES,ODS_GOODS_CATEGORY,ODS_TABLES,ODS_TABLE_USE,ODS_TABLE_FEE_DISCOUNT,ODS_GROUP_BUY_REDEMPTION,ODS_GROUP_PACKAGE,ODS_PLATFORM_COUPON,ODS_INVENTORY_STOCK,ODS_INVENTORY_CHANGE,DWD_LOAD_FROM_ODS,PAYMENTS_DWD,MEMBERS_DWD,DWS_BUILD_ORDER_SUMMARY,DWS_ASSISTANT_DAILY,DWS_ASSISTANT_MONTHLY,DWS_ASSISTANT_CUSTOMER,DWS_ASSISTANT_SALARY,DWS_ASSISTANT_FINANCE,DWS_MEMBER_CONSUMPTION,DWS_MEMBER_VISIT,DWS_FINANCE_DAILY,DWS_FINANCE_RECHARGE,DWS_FINANCE_INCOME_STRUCTURE,DWS_FINANCE_DISCOUNT_DETAIL,DWS_MV_REFRESH_FINANCE_DAILY,DWS_MV_REFRESH_ASSISTANT_DAILY,DWS_RETENTION_CLEANUP,DWS_WINBACK_INDEX,DWS_NEWCONV_INDEX,DWS_RELATION_INDEX
|
||||
314
docs/开发笔记/补充-2.md
Normal file
314
docs/开发笔记/补充-2.md
Normal file
@@ -0,0 +1,314 @@
|
||||
时间分层机制:需求明确“四层时间分层(近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 的客户是散客。不进入客户维度统计。
|
||||
|
||||
门店/租户范围:现在只有一个门店,一个租户。
|
||||
|
||||
|
||||
|
||||
|
||||
我想让你帮我基于DWS层的数据(也可使用DWD层),设计2个指数算法:
|
||||
- 客户召回指数:根据客户的到店时间,计算由助教或工作人员进行召回的必要性和紧急程度。算法不仅尊重每个客户的到店周期习惯,还要对新客户和刚充值客户进行一定的召回倾向。
|
||||
- 客户与助教亲密程度:根据单个客户与单个助教发生的服务关系,为助教的约课精力分配,约课成功率进行推算参考。计算2个对象的亲密程度。附加课(激励/超休)权重是基础课的1.5倍。重要的指标包括服务频次,为其充值(服务开始到结束后的1个小时内发生的充值即算做为其充值。此逻辑仅在指数算法有效),最后一次服务发生到现在的间隔等。次重要的是每次服务的时长(同一个客人,对某一个助教的服务,间隔小于4小时,则算作同次服务)。若一段时间内频次,频率出现激增,则加重权重。
|
||||
注意:指数算法需要符合人性直觉,对时间间隔周期敏感。指数会周期更新(1小时到1天不等,根据实际业务进行调整),计算的分数会直接覆盖旧分数,是一个动态的实时的分数。我建议算法没有最高分,是一个线性的分数,更新完一轮后,将最高分映射为满分上限10.0分。在0.0-10.0区间内,映射所有分数。
|
||||
|
||||
参考资料如下,告诉我你的实施计划。
|
||||
|
||||
# 指数算法方案(假设所有输入特征已具备)
|
||||
|
||||
> 统一约定:以“天”为时间单位;回溯窗口最多 60 天;指数每轮重算并覆盖旧分数。
|
||||
> 所有指数先计算 **Raw Score(无上限)**,再映射为 **Display Score(0.0–10.0)** 用于展示与排序。
|
||||
|
||||
---
|
||||
|
||||
## 0. 通用函数与参数
|
||||
|
||||
### 0.1 时间衰减函数(半衰期模型)
|
||||
设事件距今天数为 \(d \ge 0\),半衰期为 \(h>0\)(天):
|
||||
|
||||
\[
|
||||
\mathrm{decay}(d;h)=\exp\left(-\ln(2)\cdot \frac{d}{h}\right)
|
||||
\]
|
||||
|
||||
解释:当 \(d=h\) 时权重衰减到 0.5;越近权重越大,符合“近两周更重要”的直觉。
|
||||
|
||||
### 0.2 更新窗口
|
||||
- 回溯:最近 \(W=60\) 天内的到店/服务/充值事件
|
||||
- 近期重点:半衰期通常取 \(7\sim 14\) 天
|
||||
|
||||
---
|
||||
|
||||
## 1) 客户召回指数(Recall Index, RI)
|
||||
|
||||
### 1.1 目标
|
||||
衡量“是否需要召回”及“紧急程度”。
|
||||
同时尊重客户个人到店周期,并对 **新客户**、**刚充值客户** 增加召回倾向。
|
||||
|
||||
### 1.2 假设输入特征(概念层,不细化字段)
|
||||
对每个客户 \(c\),假设已具备:
|
||||
- \(t\): 距离最近一次到店(或最近一次服务结束)已过去的天数
|
||||
- \(\mu\): 客户过去 60 天到店间隔的“典型周期”(建议中位数)
|
||||
- \(\sigma\): 客户到店间隔波动尺度(建议 MAD / IQR 等稳健尺度)
|
||||
- \(d_{\text{first}}\): 距离首访的天数(若无首访则视为很大)
|
||||
- \(d_{\text{re}}\): 距离最近一次充值的天数(若无充值则视为很大)
|
||||
- \(n_{14}, n_{60}\): 近 14 天/60 天到店(或会话)次数
|
||||
|
||||
> 注:若 \(t>60\),可按 \(t=60\) 截断用于衰减计算,避免“太久不来”占用资源。
|
||||
|
||||
### 1.3 参数(可调默认值)
|
||||
- \(\sigma_0=2\):波动下限(天),避免 \(\sigma\) 过小导致超期过敏
|
||||
- 新客半衰期 \(h_{\text{new}}=7\)
|
||||
- 刚充值半衰期 \(h_{\text{re}}=10\)
|
||||
- 召回各分量权重:
|
||||
- \(w_{\text{over}}=3.0\)(超期紧急性,主导)
|
||||
- \(w_{\text{new}}=1.0\)
|
||||
- \(w_{\text{re}}=1.0\)
|
||||
- \(w_{\text{hot}}=1.0\)(近期活跃后断档)
|
||||
- 防除零:\(\epsilon=10^{-6}\)
|
||||
|
||||
### 1.4 计算步骤(Raw Score)
|
||||
|
||||
#### (1) 超期紧急性(尊重个人周期)
|
||||
先做稳健标准化超期量:
|
||||
\[
|
||||
\sigma'=\max(\sigma,\sigma_0)
|
||||
\]
|
||||
\[
|
||||
z=\max\left(0,\frac{t-\mu}{\sigma'}\right)
|
||||
\]
|
||||
将其映射到 \(0\sim 1\) 的“超期强度”(越超期越接近 1):
|
||||
\[
|
||||
\mathrm{overdue}=1-\exp(-z)
|
||||
\]
|
||||
|
||||
#### (2) 新客户召回倾向(快衰减)
|
||||
设“新客”条件为 \(d_{\text{first}}\le 60\):
|
||||
\[
|
||||
\mathrm{new\_bonus}=
|
||||
\begin{cases}
|
||||
\mathrm{decay}(d_{\text{first}};h_{\text{new}}), & d_{\text{first}}\le 60\\
|
||||
0, & \text{否则}
|
||||
\end{cases}
|
||||
\]
|
||||
|
||||
#### (3) 刚充值召回倾向(快衰减)
|
||||
设“近期充值”条件为 \(d_{\text{re}}\le 60\):
|
||||
\[
|
||||
\mathrm{re\_bonus}=
|
||||
\begin{cases}
|
||||
\mathrm{decay}(d_{\text{re}};h_{\text{re}}), & d_{\text{re}}\le 60\\
|
||||
0, & \text{否则}
|
||||
\end{cases}
|
||||
\]
|
||||
|
||||
#### (4) 近期活跃后断档加重(“热了又断”更值得召回)
|
||||
定义短期/长期活跃率:
|
||||
\[
|
||||
r_{14}=\frac{n_{14}}{14},\quad r_{60}=\frac{n_{60}+1}{60}
|
||||
\]
|
||||
活跃比:
|
||||
\[
|
||||
\mathrm{hot\_ratio}=\frac{r_{14}}{r_{60}+\epsilon}
|
||||
\]
|
||||
将“高于常态”的部分做对数压缩(避免爆炸):
|
||||
\[
|
||||
\mathrm{hot\_drop}=\max\left(0,\ln\left(1+(\mathrm{hot\_ratio}-1)\right)\right)
|
||||
\]
|
||||
|
||||
#### (5) 汇总 Raw Score(无上限)
|
||||
\[
|
||||
RI_{\text{raw}}=
|
||||
w_{\text{over}}\cdot \mathrm{overdue}
|
||||
+w_{\text{new}}\cdot \mathrm{new\_bonus}
|
||||
+w_{\text{re}}\cdot \mathrm{re\_bonus}
|
||||
+w_{\text{hot}}\cdot \mathrm{hot\_drop}
|
||||
\]
|
||||
|
||||
---
|
||||
|
||||
## 2) 客户-助教亲密指数(Intimacy Index, II)
|
||||
|
||||
### 2.1 目标
|
||||
衡量“客户 \(c\) 与助教 \(a\) 的关系强度与近期温度”,用于:
|
||||
- 助教约课精力分配
|
||||
- 约课成功率的先验参考
|
||||
|
||||
强调:
|
||||
- **附加课权重 = 基础课的 1.5 倍**
|
||||
- 指标重要性:频次、归因充值、最近一次间隔 > 时长
|
||||
- 若短期频率激增,权重要加重
|
||||
|
||||
### 2.2 假设输入特征(概念层)
|
||||
对每个客户-助教对 \((c,a)\),假设已具备(近 60 天):
|
||||
- 会话集合 \(i\in S\),每个会话有:
|
||||
- 距今天数 \(\Delta d_i\)
|
||||
- 时长(分钟)\(\mathrm{dur}_i\)
|
||||
- 课型权重 \(\tau_i\in\{1.0,1.5\}\)(基础/附加)
|
||||
- 最近一次会话距今天数:\(d_{\text{last}}\)
|
||||
- 归因充值集合 \(j\in R\),每笔充值有:
|
||||
- 金额 \(\mathrm{amt}_j\)
|
||||
- 距今天数 \(\Delta d^{(r)}_j\)
|
||||
|
||||
### 2.3 参数(可调默认值)
|
||||
- 会话衰减半衰期 \(h_{\text{sess}}=14\)
|
||||
- 最近一次衰减半衰期 \(h_{\text{last}}=10\)
|
||||
- 充值衰减半衰期 \(h_{\text{pay}}=21\)
|
||||
- 金额压缩基准 \(A_0=500\)(选门店常见充值档位)
|
||||
- Burst(激增)检测半衰期:
|
||||
- 短期 \(h_{\text{short}}=7\)
|
||||
- 长期 \(h_{\text{long}}=30\)
|
||||
- 汇总权重:
|
||||
- \(w_F=2.0\)(频次)
|
||||
- \(w_R=1.5\)(最近一次)
|
||||
- \(w_M=2.0\)(归因充值)
|
||||
- \(w_D=0.5\)(时长)
|
||||
- 激增放大系数 \(\gamma=0.6\)
|
||||
- 防除零:\(\epsilon=10^{-6}\)
|
||||
|
||||
### 2.4 计算步骤(Raw Score)
|
||||
|
||||
#### (1) 频次强度(课型加权 + 近因加权)
|
||||
\[
|
||||
F=\sum_{i\in S}\tau_i\cdot \mathrm{decay}(\Delta d_i;h_{\text{sess}})
|
||||
\]
|
||||
|
||||
#### (2) 最近一次温度(单独建模,直觉更强)
|
||||
\[
|
||||
R=\mathrm{decay}(d_{\text{last}};h_{\text{last}})
|
||||
\]
|
||||
|
||||
#### (3) 归因充值强度(金额压缩 + 时间衰减)
|
||||
金额压缩采用对数(抑制长尾):
|
||||
\[
|
||||
m(\mathrm{amt})=\ln\left(1+\frac{\mathrm{amt}}{A_0}\right)
|
||||
\]
|
||||
\[
|
||||
M=\sum_{j\in R} m(\mathrm{amt}_j)\cdot \mathrm{decay}(\Delta d^{(r)}_j;h_{\text{pay}})
|
||||
\]
|
||||
|
||||
#### (4) 时长贡献(次要:温和加分,避免一局超长碾压)
|
||||
\[
|
||||
D=\sum_{i\in S}\sqrt{\frac{\mathrm{dur}_i}{60}}\cdot \tau_i\cdot \mathrm{decay}(\Delta d_i;h_{\text{sess}})
|
||||
\]
|
||||
|
||||
#### (5) 频率激增(Burst)放大
|
||||
分别计算短期/长期的“近因频次”:
|
||||
\[
|
||||
F_{\text{short}}=\sum_{i\in S}\tau_i\cdot \mathrm{decay}(\Delta d_i;h_{\text{short}})
|
||||
\]
|
||||
\[
|
||||
F_{\text{long}}=\sum_{i\in S}\tau_i\cdot \mathrm{decay}(\Delta d_i;h_{\text{long}})
|
||||
\]
|
||||
激增幅度(仅放大“高于常态”的部分,并做对数压缩):
|
||||
\[
|
||||
\mathrm{burst}=\max\left(0,\ln\left(1+\left(\frac{F_{\text{short}}}{F_{\text{long}}+\epsilon}-1\right)\right)\right)
|
||||
\]
|
||||
放大因子:
|
||||
\[
|
||||
\mathrm{mult}=1+\gamma\cdot \mathrm{burst}
|
||||
\]
|
||||
|
||||
#### (6) 汇总 Raw Score(无上限)
|
||||
\[
|
||||
II_{\text{raw}}=\left(w_F F + w_R R + w_M M + w_D D\right)\cdot \mathrm{mult}
|
||||
\]
|
||||
|
||||
---
|
||||
|
||||
## 3) 统一的 0.0–10.0 映射方案(稳健替代“按最大值等比”)
|
||||
|
||||
> 目标:既能保持“高分更紧急/更亲密”的排序,又不被极端离群点劫持整体区间。
|
||||
> 该映射对 RI 与 II 通用,分别在各自对象集合上计算分位点。
|
||||
|
||||
### 3.1 每轮映射步骤(推荐:分位截断 + 可选压缩 + MinMax)
|
||||
|
||||
对某个指数的全体 Raw 分数集合 \(\{x_k\}\)(如所有客户的 \(RI_{\text{raw}}\) 或所有 pair 的 \(II_{\text{raw}}\)):
|
||||
|
||||
1) 计算稳健锚点分位数(建议):
|
||||
- 下锚:\(Q_L = P05(x)\)(5 分位)
|
||||
- 上锚:\(Q_U = P95(x)\)(95 分位)
|
||||
|
||||
2) 分位截断(Winsorize):
|
||||
\[
|
||||
x'_k = \min\left(\max(x_k,Q_L),Q_U\right)
|
||||
\]
|
||||
|
||||
3) (可选)非线性压缩(当仍呈长尾时启用;II 通常更适合启用)
|
||||
两种任选一种即可:
|
||||
- \[
|
||||
g(x)=\ln(1+x)
|
||||
\]
|
||||
- \[
|
||||
g(x)=\mathrm{asinh}(x)
|
||||
\]
|
||||
得到:
|
||||
\[
|
||||
y_k=g(x'_k)
|
||||
\]
|
||||
|
||||
4) 映射到 \([0,10]\):
|
||||
\[
|
||||
\text{score}_k=
|
||||
10\cdot \frac{y_k-\min(y)}{\max(y)-\min(y)+\epsilon}
|
||||
\]
|
||||
|
||||
### 3.2 防抖(可选但强烈建议):分位点做 EWMA 平滑
|
||||
若你发现“每轮分布轻微变化导致全员分数跳动”,可对 \(Q_L, Q_U\) 做平滑:
|
||||
|
||||
\[
|
||||
Q_{U,t}=(1-\alpha)Q_{U,t-1}+\alpha Q_{U,t}^{\text{now}},\quad \alpha=0.2
|
||||
\]
|
||||
\[
|
||||
Q_{L,t}=(1-\alpha)Q_{L,t-1}+\alpha Q_{L,t}^{\text{now}}
|
||||
\]
|
||||
|
||||
> 更新频率越高(例如每小时),越推荐启用平滑;每日更新可不启用或取更大 \(\alpha\)。
|
||||
|
||||
### 3.3 极端边界处理
|
||||
- 若出现 \(\max(y)-\min(y)\) 很小(几乎全员相同),则直接置:
|
||||
- \(\text{score}=5.0\) 或按业务设定为 0.0/固定值
|
||||
- 对 \(t>60\) 的客户,可视业务做“召回上限策略”:例如将其召回分数封顶在 8~9(避免永远占据第一优先级),但 Raw 分数仍可保留用于分析。
|
||||
|
||||
---
|
||||
|
||||
## 4) 实施建议(不涉及字段级)
|
||||
- 先按默认参数跑 1~2 周,观察:
|
||||
- 召回指数 TopN 是否符合店内直觉(人工抽检)
|
||||
- 亲密指数在助教维度是否形成合理“主客群”
|
||||
- 分数跨轮次是否抖动(决定是否启用分位点 EWMA)
|
||||
- 权重调整优先级:
|
||||
1) 映射稳定性(分位截断阈值、是否启用压缩/平滑)
|
||||
2) RI:\(w_{\text{over}}\) 与 \(h_{\text{new}},h_{\text{re}}\)
|
||||
3) II:\(w_F,w_M\) 与 \(h_{\text{sess}},h_{\text{pay}}\),以及 \(\gamma\)
|
||||
167
docs/开发笔记/补充更多信息.md
Normal file
167
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 的客户是散客。不进入客户维度统计。
|
||||
|
||||
门店/租户范围:现在只有一个门店,一个租户。
|
||||
4
docs/开发笔记/记录.md
Normal file
4
docs/开发笔记/记录.md
Normal file
@@ -0,0 +1,4 @@
|
||||
|
||||
|
||||
|
||||
API遵循 RESTful API 规范
|
||||
9294
docs/开发笔记/记录1.md
Normal file
9294
docs/开发笔记/记录1.md
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user