## P1 数据库基础 - zqyy_app: 创建 auth/biz schema、FDW 连接 etl_feiqiu - etl_feiqiu: 创建 app schema RLS 视图、商品库存预警表 - 清理 assistant_abolish 残留数据 ## P2 ETL/DWS 扩展 - 新增 DWS 助教订单贡献度表 (dws.assistant_order_contribution) - 新增 assistant_order_contribution_task 任务及 RLS 视图 - member_consumption 增加充值字段、assistant_daily 增加处罚字段 - 更新 ODS/DWD/DWS 任务文档及业务规则文档 - 更新 consistency_checker、flow_runner、task_registry 等核心模块 ## P3 小程序鉴权系统 - 新增 xcx_auth 路由/schema(微信登录 + JWT) - 新增 wechat/role/matching/application 服务层 - zqyy_app 鉴权表迁移 + 角色权限种子数据 - auth/dependencies.py 支持小程序 JWT 鉴权 ## 文档与审计 - 新增 DOCUMENTATION-MAP 文档导航 - 新增 7 份 BD_Manual 数据库变更文档 - 更新 DDL 基线快照(etl_feiqiu 6 schema + zqyy_app auth) - 新增全栈集成审计记录、部署检查清单更新 - 新增 BACKLOG 路线图、FDW→Core 迁移计划 ## Kiro 工程化 - 新增 5 个 Spec(P1/P2/P3/全栈集成/核心业务) - 新增审计自动化脚本(agent_on_stop/build_audit_context/compliance_prescan) - 新增 6 个 Hook(合规检查/会话日志/提交审计等) - 新增 doc-map steering 文件 ## 运维与测试 - 新增 ops 脚本:迁移验证/API 健康检查/ETL 监控/集成报告 - 新增属性测试:test_dws_contribution / test_auth_system - 清理过期 export 报告文件 - 更新 .gitignore 排除规则
147 lines
4.1 KiB
Python
147 lines
4.1 KiB
Python
# -*- coding: utf-8 -*-
|
||
"""
|
||
在测试库 test_etl_feiqiu 执行 SPI 种子数据脚本。
|
||
|
||
种子脚本:db/etl_feiqiu/seeds/seed_index_parameters.sql
|
||
目标表:dws.cfg_index_parameters(index_type='SPI')
|
||
|
||
使用方式:
|
||
python scripts/ops/run_seed_spi_params.py
|
||
"""
|
||
|
||
import os
|
||
import sys
|
||
from pathlib import Path
|
||
|
||
from dotenv import load_dotenv
|
||
import psycopg2
|
||
|
||
# 加载根 .env
|
||
_ROOT = Path(__file__).resolve().parents[2]
|
||
load_dotenv(_ROOT / ".env", override=False)
|
||
|
||
DSN = os.getenv("TEST_DB_DSN")
|
||
if not DSN:
|
||
print("ERROR: TEST_DB_DSN 未配置,请在根 .env 中设置")
|
||
sys.exit(1)
|
||
|
||
SEED_FILE = _ROOT / "db" / "etl_feiqiu" / "seeds" / "seed_index_parameters.sql"
|
||
|
||
|
||
def execute_seed(conn) -> bool:
|
||
"""执行种子数据脚本,返回是否成功"""
|
||
sql = SEED_FILE.read_text(encoding="utf-8")
|
||
if not sql.strip():
|
||
print("⚠️ 种子脚本为空,跳过")
|
||
return False
|
||
|
||
try:
|
||
cur = conn.cursor()
|
||
cur.execute(sql)
|
||
cur.close()
|
||
conn.commit()
|
||
print("✅ 种子数据脚本执行成功")
|
||
return True
|
||
except Exception as e:
|
||
conn.rollback()
|
||
print(f"❌ 种子数据脚本执行失败: {e}")
|
||
return False
|
||
|
||
|
||
def verify(conn) -> bool:
|
||
"""验证 SPI 参数插入结果"""
|
||
cur = conn.cursor()
|
||
checks = []
|
||
|
||
# 1. SPI 参数总数(应为 28 个)
|
||
cur.execute("""
|
||
SELECT COUNT(*) FROM dws.cfg_index_parameters
|
||
WHERE index_type = 'SPI'
|
||
""")
|
||
spi_count = cur.fetchone()[0]
|
||
checks.append((f"SPI 参数数量 = {spi_count}(期望 28)", spi_count == 28))
|
||
|
||
# 2. 关键参数存在且值正确
|
||
key_params = [
|
||
("weight_level", 0.60),
|
||
("weight_speed", 0.30),
|
||
("weight_stability", 0.10),
|
||
("amount_base_spend_30", 500.0),
|
||
("compression_mode", 1.0),
|
||
]
|
||
for pname, expected in key_params:
|
||
cur.execute("""
|
||
SELECT param_value FROM dws.cfg_index_parameters
|
||
WHERE index_type = 'SPI' AND param_name = %s
|
||
ORDER BY effective_from DESC LIMIT 1
|
||
""", (pname,))
|
||
row = cur.fetchone()
|
||
if row:
|
||
actual = float(row[0])
|
||
ok = abs(actual - expected) < 1e-6
|
||
checks.append((f" {pname} = {actual}(期望 {expected})", ok))
|
||
else:
|
||
checks.append((f" {pname} 缺失", False))
|
||
|
||
# 3. 权重归一化:Level 子分权重之和 = 1.0
|
||
cur.execute("""
|
||
SELECT SUM(param_value) FROM dws.cfg_index_parameters
|
||
WHERE index_type = 'SPI'
|
||
AND param_name IN ('w_level_spend_30', 'w_level_spend_90',
|
||
'w_level_ticket_90', 'w_level_recharge_90')
|
||
""")
|
||
level_sum = float(cur.fetchone()[0] or 0)
|
||
checks.append((f" Level 权重之和 = {level_sum:.2f}(期望 1.00)", abs(level_sum - 1.0) < 1e-6))
|
||
|
||
# 4. 总分权重之和 = 1.0
|
||
cur.execute("""
|
||
SELECT SUM(param_value) FROM dws.cfg_index_parameters
|
||
WHERE index_type = 'SPI'
|
||
AND param_name IN ('weight_level', 'weight_speed', 'weight_stability')
|
||
""")
|
||
total_sum = float(cur.fetchone()[0] or 0)
|
||
checks.append((f" 总分权重之和 = {total_sum:.2f}(期望 1.00)", abs(total_sum - 1.0) < 1e-6))
|
||
|
||
cur.close()
|
||
|
||
print("\n" + "=" * 50)
|
||
print("SPI 种子数据验证结果")
|
||
print("=" * 50)
|
||
all_ok = True
|
||
for name, ok in checks:
|
||
status = "✅" if ok else "❌"
|
||
print(f" {status} {name}")
|
||
if not ok:
|
||
all_ok = False
|
||
|
||
return all_ok
|
||
|
||
|
||
def main():
|
||
dsn_display = DSN.split("@")[1] if "@" in DSN else DSN
|
||
print(f"连接测试库: {dsn_display}")
|
||
print(f"种子脚本: {SEED_FILE.name}\n")
|
||
|
||
if not SEED_FILE.exists():
|
||
print(f"ERROR: 种子脚本不存在: {SEED_FILE}")
|
||
sys.exit(1)
|
||
|
||
conn = psycopg2.connect(DSN)
|
||
|
||
if not execute_seed(conn):
|
||
conn.close()
|
||
sys.exit(1)
|
||
|
||
all_ok = verify(conn)
|
||
conn.close()
|
||
|
||
if all_ok:
|
||
print("\n✅ SPI 种子数据执行完成,所有验证通过")
|
||
else:
|
||
print("\n⚠️ 部分验证未通过,请检查")
|
||
sys.exit(1)
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|