385 lines
15 KiB
Python
385 lines
15 KiB
Python
"""
|
||
将 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,
|
||
'麻将棋牌:M7(2台)'),
|
||
('M8', NULL, 'MAHJONG', '🀄 麻将/棋牌', '🀄 麻将/棋牌', '🀄', 'EXACT', 10, TRUE,
|
||
'麻将棋牌:M8(1台)'),
|
||
('666', NULL, 'MAHJONG', '🀄 麻将/棋牌', '🀄 麻将/棋牌', '🀄', 'EXACT', 10, TRUE,
|
||
'麻将棋牌:666(2台)'),
|
||
('发财', 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歌娱乐:幸会158(2台)'),
|
||
-- 特殊区域
|
||
('补时长', 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()
|