This commit is contained in:
Neo
2026-02-04 21:39:01 +08:00
parent ee773a9b52
commit a3f4d04335
148 changed files with 31455 additions and 182 deletions

View File

@@ -0,0 +1,222 @@
# -*- coding: utf-8 -*-
"""
测试指数算法任务
"""
import sys
import os
# 添加项目路径
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import logging
from config.settings import AppConfig
from database.connection import DatabaseConnection
from database.operations import DatabaseOperations
from tasks.dws.index import RecallIndexTask, IntimacyIndexTask
# 配置日志
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger('test_index')
def test_recall_index():
"""测试召回指数任务"""
logger.info("=" * 60)
logger.info("测试客户召回指数任务 (DWS_RECALL_INDEX)")
logger.info("=" * 60)
# 加载配置
config = AppConfig.load()
# 连接数据库
db_conn = DatabaseConnection(config.config["db"]["dsn"])
db = DatabaseOperations(db_conn)
try:
# 创建任务实例
task = RecallIndexTask(config, db, None, logger)
# 执行任务
result = task.execute(None)
logger.info("任务执行结果: %s", result)
# 查询结果
if result.get('status') == 'success':
sql = """
SELECT
COUNT(*) as total_count,
ROUND(AVG(display_score)::numeric, 2) as avg_score,
ROUND(MIN(display_score)::numeric, 2) as min_score,
ROUND(MAX(display_score)::numeric, 2) as max_score,
ROUND(AVG(raw_score)::numeric, 4) as avg_raw_score,
ROUND(AVG(score_overdue)::numeric, 4) as avg_overdue,
ROUND(AVG(score_new_bonus)::numeric, 4) as avg_new_bonus,
ROUND(AVG(score_recharge_bonus)::numeric, 4) as avg_recharge_bonus,
ROUND(AVG(score_hot_drop)::numeric, 4) as avg_hot_drop
FROM billiards_dws.dws_member_recall_index
"""
rows = db.query(sql)
if rows:
stats = dict(rows[0])
logger.info("-" * 40)
logger.info("召回指数统计:")
logger.info(" 总记录数: %s", stats['total_count'])
logger.info(" Display Score: 平均=%.2f, 最小=%.2f, 最大=%.2f",
stats['avg_score'] or 0, stats['min_score'] or 0, stats['max_score'] or 0)
logger.info(" Raw Score 平均: %.4f", stats['avg_raw_score'] or 0)
logger.info(" 分项得分平均:")
logger.info(" - 超期紧急性: %.4f", stats['avg_overdue'] or 0)
logger.info(" - 新客户加分: %.4f", stats['avg_new_bonus'] or 0)
logger.info(" - 充值加分: %.4f", stats['avg_recharge_bonus'] or 0)
logger.info(" - 热度断档: %.4f", stats['avg_hot_drop'] or 0)
# 查询Top 5
logger.info("-" * 40)
logger.info("召回优先级 Top 5:")
top_sql = """
SELECT member_id, display_score, raw_score,
days_since_last_visit, visit_interval_median
FROM billiards_dws.dws_member_recall_index
ORDER BY display_score DESC
LIMIT 5
"""
top_rows = db.query(top_sql)
for i, row in enumerate(top_rows or [], 1):
r = dict(row)
logger.info(" %d. 会员%s: %.2f分 (Raw=%.4f, 最近到店=%s天前, 周期=%.1f天)",
i, r['member_id'], r['display_score'] or 0, r['raw_score'] or 0,
r['days_since_last_visit'], r['visit_interval_median'] or 0)
return result
finally:
db_conn.close()
def test_intimacy_index():
"""测试亲密指数任务"""
logger.info("")
logger.info("=" * 60)
logger.info("测试客户-助教亲密指数任务 (DWS_INTIMACY_INDEX)")
logger.info("=" * 60)
# 加载配置
config = AppConfig.load()
# 连接数据库
db_conn = DatabaseConnection(config.config["db"]["dsn"])
db = DatabaseOperations(db_conn)
try:
# 创建任务实例
task = IntimacyIndexTask(config, db, None, logger)
# 执行任务
result = task.execute(None)
logger.info("任务执行结果: %s", result)
# 查询结果
if result.get('status') == 'success':
sql = """
SELECT
COUNT(*) as total_count,
COUNT(DISTINCT member_id) as unique_members,
COUNT(DISTINCT assistant_id) as unique_assistants,
ROUND(AVG(display_score)::numeric, 2) as avg_score,
ROUND(MIN(display_score)::numeric, 2) as min_score,
ROUND(MAX(display_score)::numeric, 2) as max_score,
ROUND(AVG(raw_score)::numeric, 4) as avg_raw_score,
ROUND(AVG(score_frequency)::numeric, 4) as avg_frequency,
ROUND(AVG(score_recency)::numeric, 4) as avg_recency,
ROUND(AVG(score_recharge)::numeric, 4) as avg_recharge,
ROUND(AVG(burst_multiplier)::numeric, 4) as avg_burst
FROM billiards_dws.dws_member_assistant_intimacy
"""
rows = db.query(sql)
if rows:
stats = dict(rows[0])
logger.info("-" * 40)
logger.info("亲密指数统计:")
logger.info(" 总记录数: %s (客户-助教对)", stats['total_count'])
logger.info(" 唯一会员: %s, 唯一助教: %s", stats['unique_members'], stats['unique_assistants'])
logger.info(" Display Score: 平均=%.2f, 最小=%.2f, 最大=%.2f",
stats['avg_score'] or 0, stats['min_score'] or 0, stats['max_score'] or 0)
logger.info(" Raw Score 平均: %.4f", stats['avg_raw_score'] or 0)
logger.info(" 分项得分平均:")
logger.info(" - 频次强度: %.4f", stats['avg_frequency'] or 0)
logger.info(" - 最近温度: %.4f", stats['avg_recency'] or 0)
logger.info(" - 充值强度: %.4f", stats['avg_recharge'] or 0)
logger.info(" - 激增放大: %.4f", stats['avg_burst'] or 0)
# 查询Top亲密关系
logger.info("-" * 40)
logger.info("亲密度 Top 5 客户-助教对:")
top_sql = """
SELECT member_id, assistant_id, display_score, raw_score,
session_count, attributed_recharge_amount
FROM billiards_dws.dws_member_assistant_intimacy
ORDER BY display_score DESC
LIMIT 5
"""
top_rows = db.query(top_sql)
for i, row in enumerate(top_rows or [], 1):
r = dict(row)
logger.info(" %d. 会员%s-助教%s: %.2f分 (会话%d次, 归因充值%.2f元)",
i, r['member_id'], r['assistant_id'],
r['display_score'] or 0, r['session_count'] or 0,
r['attributed_recharge_amount'] or 0)
return result
finally:
db_conn.close()
if __name__ == '__main__':
print("=" * 60)
print("指数算法任务测试")
print("=" * 60)
print()
# 先检查表是否存在
config = AppConfig.load()
db_conn = DatabaseConnection(config.config["db"]["dsn"])
db = DatabaseOperations(db_conn)
check_sql = """
SELECT table_name
FROM information_schema.tables
WHERE table_schema = 'billiards_dws'
AND table_name IN ('dws_member_recall_index', 'dws_member_assistant_intimacy', 'cfg_index_parameters')
"""
tables = db.query(check_sql)
existing_tables = [dict(r)['table_name'] for r in (tables or [])]
if 'cfg_index_parameters' not in existing_tables:
print("警告: cfg_index_parameters 表不存在,请先执行 schema_dws.sql")
print("需要执行的表:")
print(" - cfg_index_parameters")
print(" - dws_member_recall_index")
print(" - dws_member_assistant_intimacy")
print(" - dws_index_percentile_history")
db_conn.close()
sys.exit(1)
db_conn.close()
# 测试召回指数
recall_result = test_recall_index()
# 测试亲密指数
intimacy_result = test_intimacy_index()
print()
print("=" * 60)
print("测试完成")
print("=" * 60)
print(f"召回指数: {recall_result.get('status', 'unknown')}")
print(f"亲密指数: {intimacy_result.get('status', 'unknown')}")