Files
Neo-ZQYY/scripts/ops/seed_dws_config.py

385 lines
15 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
将 seed_dws_config.sql 中的种子数据写入 DWS 配置表test_etl_feiqiu
包含:
1. 原始种子数据cfg_performance_tier / cfg_assistant_level_price / cfg_bonus_rules / cfg_area_category / cfg_skill_type
2. 新增 2025-01-01~2026-02-28 统一提成档位基础课18元/小时打赏课40%
3. 新增 GUARANTEE 保底奖金规则(按等级区分)
执行目标库TEST_DB_DSN 或 PG_DSN.env 中配置)
"""
import os
import sys
from pathlib import Path
from dotenv import load_dotenv
import psycopg2
# 加载根 .env
load_dotenv(Path(__file__).resolve().parents[2] / ".env")
dsn = os.environ.get("PG_DSN")
if not dsn:
print("ERROR: PG_DSN 未在 .env 中配置,终止执行")
sys.exit(1)
# 确认连接的是测试库
if "test_etl_feiqiu" not in dsn:
print(f"WARNING: DSN 指向 {dsn},不是 test_etl_feiqiu请确认")
resp = input("继续执行?(y/N): ").strip().lower()
if resp != "y":
print("已取消")
sys.exit(0)
print(f"连接数据库: {dsn.split('@')[1] if '@' in dsn else dsn}")
# ============================================================================
# SQL 语句定义
# ============================================================================
# 清空并重建配置数据
SQL_STATEMENTS = []
# --- 1. cfg_performance_tier ---
SQL_STATEMENTS.append("""
TRUNCATE TABLE dws.cfg_performance_tier RESTART IDENTITY CASCADE;
""")
# 2025-01-01 ~ 2026-02-28: 统一提成档位(不分档,所有助教统一规则)
# 基础课球房提成 18 元/小时,打赏课球房提成 40%
SQL_STATEMENTS.append("""
INSERT INTO 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
-- 2025-01-01 ~ 2026-02-28: 统一提成(不分档)
('T0', '统一档', 0,
0, NULL,
18.00, 0.40, 0, FALSE,
FALSE, '2025-01-01', '2026-02-28',
'2025-01-01~2026-02-28统一规则基础课球房提成18元/小时打赏课球房提成40%,不分档位'),
-- 旧方案至2024-12-31保留历史口径
('T0', '0档-淘汰压力', 0,
0, 100,
28.00, 0.50, 3, FALSE,
FALSE, '2000-01-01', '2024-12-31',
'历史口径H<100专业课抽成28元/小时打赏课抽成50%休假3天'),
('T1', '1档-及格档', 1,
100, 130,
18.00, 0.40, 4, FALSE,
FALSE, '2000-01-01', '2024-12-31',
'历史口径100≤H<130专业课抽成18元/小时打赏课抽成40%休假4天'),
('T2', '2档-良好档', 2,
130, 160,
15.00, 0.38, 4, FALSE,
FALSE, '2000-01-01', '2024-12-31',
'历史口径130≤H<160专业课抽成15元/小时打赏课抽成38%休假4天'),
('T3', '3档-优秀档', 3,
160, 190,
13.00, 0.35, 5, FALSE,
FALSE, '2000-01-01', '2024-12-31',
'历史口径160≤H<190专业课抽成13元/小时打赏课抽成35%休假5天'),
('T4', '4档-卓越加速档', 4,
190, 220,
10.00, 0.33, 6, FALSE,
FALSE, '2000-01-01', '2024-12-31',
'历史口径190≤H<220专业课抽成10元/小时打赏课抽成33%休假6天'),
('T5', '5档-冠军加速档', 5,
220, NULL,
8.00, 0.30, 0, TRUE,
FALSE, '2000-01-01', '2024-12-31',
'历史口径H≥220专业课抽成8元/小时打赏课抽成30%,休假自由'),
-- 新方案2026-03-01起恢复分档
('T0', '0档-淘汰压力', 0,
0, 120,
28.00, 0.50, 3, FALSE,
FALSE, '2026-03-01', '9999-12-31',
'新方案H<120专业课抽成28元/小时打赏课抽成50%休假3天'),
('T1', '1档-及格档', 1,
120, 150,
18.00, 0.40, 4, FALSE,
FALSE, '2026-03-01', '9999-12-31',
'新方案120≤H<150专业课抽成18元/小时打赏课抽成40%休假4天'),
('T2', '2档-良好档', 2,
150, 180,
13.00, 0.35, 5, FALSE,
FALSE, '2026-03-01', '9999-12-31',
'新方案150≤H<180专业课抽成13元/小时打赏课抽成35%休假5天'),
('T3', '3档-优秀档', 3,
180, 210,
10.00, 0.30, 6, FALSE,
FALSE, '2026-03-01', '9999-12-31',
'新方案180≤H<210专业课抽成10元/小时打赏课抽成30%休假6天'),
('T4', '4档-销冠竞争', 4,
210, NULL,
8.00, 0.25, 0, TRUE,
FALSE, '2026-03-01', '9999-12-31',
'新方案H≥210专业课抽成8元/小时打赏课抽成25%,休假自由');
""")
# --- 2. cfg_assistant_level_price ---
SQL_STATEMENTS.append("""
TRUNCATE TABLE dws.cfg_assistant_level_price RESTART IDENTITY CASCADE;
""")
SQL_STATEMENTS.append("""
INSERT INTO dws.cfg_assistant_level_price (
level_code, level_name,
base_course_price, bonus_course_price,
effective_from, effective_to, description
) VALUES
(10, '初级',
98.00, 190.00,
'2000-01-01', '9999-12-31',
'初级助教基础课98元/时附加课190元/时(客户支付价格)'),
(20, '中级',
108.00, 190.00,
'2000-01-01', '9999-12-31',
'中级助教基础课108元/时附加课190元/时(客户支付价格)'),
(30, '高级',
118.00, 190.00,
'2000-01-01', '9999-12-31',
'高级助教基础课118元/时附加课190元/时(客户支付价格)'),
(40, '星级',
138.00, 190.00,
'2000-01-01', '9999-12-31',
'星级助教基础课138元/时附加课190元/时(客户支付价格)'),
(8, '助教管理',
98.00, 190.00,
'2000-01-01', '9999-12-31',
'助教管理:不参与客户服务计费,默认按初级价格');
""")
# --- 3. cfg_bonus_rules ---
SQL_STATEMENTS.append("""
TRUNCATE TABLE dws.cfg_bonus_rules RESTART IDENTITY CASCADE;
""")
SQL_STATEMENTS.append("""
INSERT INTO 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
-- 冲刺奖金历史口径至2024-12-31
('SPRINT', 'SPRINT_190', '冲刺奖金190',
190.00, NULL, 300.00,
FALSE, 1,
'2000-01-01', '2024-12-31',
'历史口径业绩≥190小时获得300元冲刺奖金不累计'),
('SPRINT', 'SPRINT_220', '冲刺奖金220',
220.00, NULL, 800.00,
FALSE, 2,
'2000-01-01', '2024-12-31',
'历史口径业绩≥220小时获得800元冲刺奖金覆盖190档'),
-- 保底奖金2025-01-01 ~ 2026-02-28
-- 按助教等级区分,需同时满足总课时和打赏课最低时数
-- level_code: 10=初级, 20=中级, 30=高级, 40=星级
('GUARANTEE', 'GUAR_LV10', '初级保底奖金',
130.00, NULL, 12000.00,
FALSE, 10,
'2025-01-01', '2026-02-28',
'初级保底完成130小时课程含≥10小时打赏课保底月薪线12000元实发=MAX(课时收入+奖金, 12000)'),
('GUARANTEE', 'GUAR_LV20', '中级保底奖金',
150.00, NULL, 16000.00,
FALSE, 20,
'2025-01-01', '2026-02-28',
'中级保底完成150小时课程含≥10小时打赏课保底月薪线16000元实发=MAX(课时收入+奖金, 16000)'),
('GUARANTEE', 'GUAR_LV30', '高级保底奖金',
160.00, NULL, 18000.00,
FALSE, 30,
'2025-01-01', '2026-02-28',
'高级保底完成160小时课程含≥10小时打赏课保底月薪线18000元实发=MAX(课时收入+奖金, 18000)'),
('GUARANTEE', 'GUAR_LV40', '星级保底奖金',
170.00, NULL, 23000.00,
FALSE, 40,
'2025-01-01', '2026-02-28',
'星级保底完成170小时课程含≥10小时打赏课保底月薪线23000元实发=MAX(课时收入+奖金, 23000)'),
-- Top排名奖金2026-03-01起
('TOP_RANK', 'TOP_1', 'Top1排名奖金',
NULL, 1, 1000.00,
FALSE, 0,
'2026-03-01', '9999-12-31',
'月度排名第一获得1000元并列都算'),
('TOP_RANK', 'TOP_2', 'Top2排名奖金',
NULL, 2, 600.00,
FALSE, 0,
'2026-03-01', '9999-12-31',
'月度排名第二获得600元并列都算'),
('TOP_RANK', 'TOP_3', 'Top3排名奖金',
NULL, 3, 400.00,
FALSE, 0,
'2026-03-01', '9999-12-31',
'月度排名第三获得400元并列都算');
""")
# --- 4. cfg_area_category ---
SQL_STATEMENTS.append("""
TRUNCATE TABLE dws.cfg_area_category RESTART IDENTITY CASCADE;
""")
SQL_STATEMENTS.append("""
INSERT INTO dws.cfg_area_category (
source_area_name, source_table_name, category_code, category_name,
display_name, short_name,
match_type, match_priority, is_active, description
) VALUES
-- VIP包厢台桌级映射优先级最高
('VIP包厢', 'V5', 'SNOOKER', '斯诺克', '斯诺克', '', 'EXACT', 5, TRUE,
'VIP包厢V5台→斯诺克台桌级精确匹配优先级高于区域级'),
-- 台球散台(精确匹配)
('A区', NULL, 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE,
'台球散台A区18台- 中八/追分'),
('B区', NULL, 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE,
'台球散台B区15台- 中八/追分'),
('C区', NULL, 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE,
'台球散台C区6台- 中八/追分'),
('TV台', NULL, 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE,
'台球散台TV台1台- 中八/追分'),
-- VIP包厢区域级V1-V4 归入中式/追分)
('VIP包厢', NULL, 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE,
'台球VIP包厢V1-V4中八→ 归入中式/追分'),
-- 斯诺克区
('斯诺克区', NULL, 'SNOOKER', '斯诺克', '斯诺克', '', 'EXACT', 10, TRUE,
'斯诺克斯诺克区4台'),
-- 麻将区
('麻将房', NULL, 'MAHJONG', '🀄 麻将/棋牌', '🀄 麻将/棋牌', '🀄', 'EXACT', 10, TRUE,
'麻将棋牌麻将房5台'),
('M7', NULL, 'MAHJONG', '🀄 麻将/棋牌', '🀄 麻将/棋牌', '🀄', 'EXACT', 10, TRUE,
'麻将棋牌M72台'),
('M8', NULL, 'MAHJONG', '🀄 麻将/棋牌', '🀄 麻将/棋牌', '🀄', 'EXACT', 10, TRUE,
'麻将棋牌M81台'),
('666', NULL, 'MAHJONG', '🀄 麻将/棋牌', '🀄 麻将/棋牌', '🀄', 'EXACT', 10, TRUE,
'麻将棋牌6662台'),
('发财', NULL, 'MAHJONG', '🀄 麻将/棋牌', '🀄 麻将/棋牌', '🀄', 'EXACT', 10, TRUE,
'麻将棋牌发财1台'),
-- KTV/K包
('K包', NULL, 'KTV', '🎤 团建/K歌', '🎤 团建/K歌', '🎤', 'EXACT', 10, TRUE,
'K歌娱乐K包4台'),
('k包活动区', NULL, 'KTV', '🎤 团建/K歌', '🎤 团建/K歌', '🎤', 'EXACT', 10, TRUE,
'K歌娱乐k包活动区2台'),
('幸会158', NULL, 'KTV', '🎤 团建/K歌', '🎤 团建/K歌', '🎤', 'EXACT', 10, TRUE,
'K歌娱乐幸会1582台'),
-- 特殊区域
('补时长', NULL, 'SPECIAL', '补时长', '补时长', '', 'EXACT', 10, TRUE,
'特殊补时长7台- 用于时长补录'),
-- 模糊匹配规则
('%VIP%', NULL, 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'LIKE', 50, TRUE,
'模糊匹配:包含"VIP"的区域→归入中式/追分'),
('%斯诺克%', NULL, 'SNOOKER', '斯诺克', '斯诺克', '', 'LIKE', 50, TRUE,
'模糊匹配:包含"斯诺克"的区域'),
('%麻将%', NULL, 'MAHJONG', '🀄 麻将/棋牌', '🀄 麻将/棋牌', '🀄', 'LIKE', 50, TRUE,
'模糊匹配:包含"麻将"的区域'),
('%K包%', NULL, 'KTV', '🎤 团建/K歌', '🎤 团建/K歌', '🎤', 'LIKE', 50, TRUE,
'模糊匹配:包含"K包"的区域'),
('%KTV%', NULL, 'KTV', '🎤 团建/K歌', '🎤 团建/K歌', '🎤', 'LIKE', 50, TRUE,
'模糊匹配:包含"KTV"的区域'),
-- 默认兜底
('DEFAULT', NULL, 'OTHER', '其他', '其他', '', 'DEFAULT', 999, TRUE,
'兜底规则:无法匹配的区域归入其他');
""")
# --- 5. cfg_skill_type ---
SQL_STATEMENTS.append("""
TRUNCATE TABLE dws.cfg_skill_type RESTART IDENTITY CASCADE;
""")
SQL_STATEMENTS.append("""
INSERT INTO dws.cfg_skill_type (
skill_id, skill_name,
course_type_code, course_type_name,
is_active, description
) VALUES
(2791903611396869, '台球基础陪打',
'BASE', '基础课',
TRUE, '基础课:陪打服务,按助教等级计价'),
(2807440316432197, '台球超休服务',
'BONUS', '附加课',
TRUE, '附加课:超休/激励课固定190元/小时'),
(2807440316432198, '包厢服务',
'BASE', '基础课',
TRUE, '包厢服务归入基础课统计统一按138元/小时计价');
""")
# --- 验证 SQL ---
SQL_VERIFY = """
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 dws.cfg_performance_tier;
SELECT COUNT(*) INTO v_price_count FROM dws.cfg_assistant_level_price;
SELECT COUNT(*) INTO v_bonus_count FROM dws.cfg_bonus_rules;
SELECT COUNT(*) INTO v_area_count FROM dws.cfg_area_category;
SELECT COUNT(*) INTO v_skill_count FROM 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;
$$;
"""
# ============================================================================
# 执行
# ============================================================================
def main():
conn = psycopg2.connect(dsn)
conn.autocommit = False
cur = conn.cursor()
try:
for i, sql in enumerate(SQL_STATEMENTS):
cur.execute(sql)
print(f" 步骤 {i+1}/{len(SQL_STATEMENTS)} 完成")
# 验证
cur.execute(SQL_VERIFY)
# 额外查询验证
checks = [
("cfg_performance_tier", "SELECT tier_code, tier_name, effective_from, effective_to, base_deduction, bonus_deduction_ratio FROM dws.cfg_performance_tier ORDER BY effective_from, tier_level"),
("cfg_bonus_rules", "SELECT rule_type, rule_code, rule_name, threshold_hours, bonus_amount, effective_from, effective_to FROM dws.cfg_bonus_rules ORDER BY effective_from, rule_type, priority"),
("cfg_assistant_level_price", "SELECT level_code, level_name, base_course_price, bonus_course_price FROM dws.cfg_assistant_level_price ORDER BY level_code"),
("cfg_area_category", "SELECT COUNT(*) as cnt FROM dws.cfg_area_category"),
("cfg_skill_type", "SELECT skill_id, skill_name, course_type_code FROM dws.cfg_skill_type"),
]
for table_name, sql in checks:
cur.execute(sql)
rows = cur.fetchall()
cols = [desc[0] for desc in cur.description]
print(f"\n=== {table_name} ===")
print(f" 列: {cols}")
for row in rows:
print(f" {row}")
conn.commit()
print("\n✅ 所有配置数据已成功写入 dws schema")
except Exception as e:
conn.rollback()
print(f"\n❌ 执行失败,已回滚: {e}")
raise
finally:
cur.close()
conn.close()
if __name__ == "__main__":
main()