feat: 累积功能变更 — 聊天集成、租户管理、小程序更新、ETL 增强、迁移脚本

包含多个会话的累积代码变更:
- backend: AI 聊天服务、触发器调度、认证增强、WebSocket、调度器最小间隔
- admin-web: ETL 状态页、任务管理、调度配置、登录优化
- miniprogram: 看板页面、聊天集成、UI 组件、导航更新
- etl: DWS 新任务(finance_area_daily/board_cache)、连接器增强
- tenant-admin: 项目初始化
- db: 19 个迁移脚本(etl_feiqiu 11 + zqyy_app 8)
- packages/shared: 枚举和工具函数更新
- tools: 数据库工具、报表生成、健康检查
- docs: PRD/架构/部署/合约文档更新

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Neo
2026-04-06 00:03:48 +08:00
parent 70324d8542
commit 6f8f12314f
515 changed files with 76604 additions and 7456 deletions

View File

@@ -0,0 +1,160 @@
"""
DDL 迁移验证脚本 — admin-web-enhancement spec (Task 17.2)
验证 NS4.1 + P16 迁移在 test_zqyy_app 上的正确性:
1. biz.connectors / biz.tenants / biz.sites / biz.site_code_history 四张表存在且字段正确
2. scheduled_tasks 新增 3 个字段
3. auth._archived_site_code_mapping 存在
"""
import os
import sys
from dotenv import load_dotenv
load_dotenv()
dsn = os.environ.get("APP_DB_DSN", "")
if not dsn:
print("ERROR: APP_DB_DSN 未设置")
sys.exit(1)
if "test_" not in dsn:
print(f"ERROR: APP_DB_DSN 不包含 'test_',拒绝连接非测试库: {dsn}")
sys.exit(1)
import psycopg2 # noqa: E402
conn = psycopg2.connect(dsn)
cur = conn.cursor()
results: list[tuple[str, bool, str]] = []
def check(name: str, sql: str, expected_fn):
"""执行 SQL 并用 expected_fn 判断结果,记录 pass/fail。"""
try:
cur.execute(sql)
rows = cur.fetchall()
ok, detail = expected_fn(rows)
results.append((name, ok, detail))
except Exception as e:
results.append((name, False, f"异常: {e}"))
# ── 1. biz.connectors 表字段 ──
check(
"biz.connectors 表存在且字段正确",
"""
SELECT column_name FROM information_schema.columns
WHERE table_schema = 'biz' AND table_name = 'connectors'
ORDER BY ordinal_position
""",
lambda rows: (
(cols := [r[0] for r in rows])
and set(cols) == {"id", "connector_key", "display_name", "is_active", "created_at"},
f"字段: {cols}",
),
)
# ── 2. biz.tenants 表字段 ──
check(
"biz.tenants 表存在且字段正确",
"""
SELECT column_name FROM information_schema.columns
WHERE table_schema = 'biz' AND table_name = 'tenants'
ORDER BY ordinal_position
""",
lambda rows: (
(cols := [r[0] for r in rows])
and set(cols)
== {"id", "connector_id", "tenant_id", "tenant_name", "is_active", "created_at", "updated_at"},
f"字段: {cols}",
),
)
# ── 3. biz.sites 表字段 ──
check(
"biz.sites 表存在且字段正确",
"""
SELECT column_name FROM information_schema.columns
WHERE table_schema = 'biz' AND table_name = 'sites'
ORDER BY ordinal_position
""",
lambda rows: (
(cols := [r[0] for r in rows])
and set(cols)
== {
"id", "tenant_id", "site_id", "site_name", "site_code",
"site_label", "is_active", "created_at", "updated_at",
},
f"字段: {cols}",
),
)
# ── 4. biz.site_code_history 表字段 ──
check(
"biz.site_code_history 表存在且字段正确",
"""
SELECT column_name FROM information_schema.columns
WHERE table_schema = 'biz' AND table_name = 'site_code_history'
ORDER BY ordinal_position
""",
lambda rows: (
(cols := [r[0] for r in rows])
and set(cols) == {"id", "site_id", "site_code", "is_current", "created_at", "retired_at"},
f"字段: {cols}",
),
)
# ── 5. scheduled_tasks 新增 3 个字段 ──
check(
"scheduled_tasks 新增字段存在",
"""
SELECT column_name, data_type, column_default
FROM information_schema.columns
WHERE table_schema = 'public' AND table_name = 'scheduled_tasks'
AND column_name IN ('min_run_interval_value', 'min_run_interval_unit', 'last_success_at')
ORDER BY ordinal_position
""",
lambda rows: (
len(rows) == 3,
f"找到 {len(rows)} 个字段: {[r[0] for r in rows]}",
),
)
# ── 6. auth._archived_site_code_mapping 存在 ──
check(
"auth._archived_site_code_mapping 存在",
"""
SELECT table_name FROM information_schema.tables
WHERE table_schema = 'auth' AND table_name = '_archived_site_code_mapping'
""",
lambda rows: (
len(rows) == 1,
f"找到 {len(rows)} 张表",
),
)
cur.close()
conn.close()
# ── 打印结果 ──
print("\n" + "=" * 60)
print("DDL 迁移验证结果 — admin-web-enhancement")
print("=" * 60)
passed = 0
failed = 0
for name, ok, detail in results:
status = "✅ PASS" if ok else "❌ FAIL"
print(f" {status} {name}")
print(f" {detail}")
if ok:
passed += 1
else:
failed += 1
print(f"\n总计: {passed} 通过, {failed} 失败")
if failed > 0:
sys.exit(1)
print("全部通过 ✅")