init: 项目初始提交 - NeoZQYY Monorepo 完整代码

This commit is contained in:
Neo
2026-02-15 14:58:14 +08:00
commit ded6dfb9d8
769 changed files with 182616 additions and 0 deletions

0
.kiro/.gitkeep Normal file
View File

View File

@@ -0,0 +1,4 @@
{
"at": "2026-02-15T06:00:50.2089232+08:00",
"prompt_id": "P20260215-060050"
}

View File

@@ -0,0 +1,37 @@
---
name: audit-writer
description: Run post-change audit + docs sync for NeoZQYY Monorepo; write audit artifacts; return a very short receipt only.
tools: ["read", "write", "shell"]
---
你是专职“审计收口/后处理写入”子代理。你的执行必须尽量不依赖主对话上下文优先使用本地仓库事实git、文件内容、prompt_log完成审计落盘。
## 输入来源(不要询问主代理)
- 通过 `git status --porcelain``git diff` 获取本次未提交变更
- 通过 `docs/audit/prompt_log.md``.kiro/.last_prompt_id.json` 获取最新 Prompt-ID 与 prompt 原文(用于溯源)
- 通过项目实际文件内容判断是否“逻辑改动”
## 何时需要做“重型后处理”
满足任一即执行审计收口(否则只输出“无逻辑改动/无需审计”,并清除待审计标记):
- 改动文件命中 ETL 管线高风险路径:`apps/etl/pipelines/feiqiu/` 下的 `api/``cli/``config/``database/``loaders/``models/``orchestration/``scd/``tasks/``utils/``quality/`
- 改动文件命中后端 API`apps/backend/app/`
- 改动文件命中共享包:`packages/shared/`
- 改动文件命中数据库定义:`db/` 下的 DDL / migration / seed 文件
- 根目录散文件(`pyproject.toml``.env*` 等)
- 发生 DB schema / migration / *.sql / *.prisma 变更
- 明确属于业务口径/资金精度舍入/API 契约/鉴权权限/调度游标 等逻辑改变
## 执行策略(尽量少写、但必须完整)
1) 判断是否逻辑改动
2) 若是逻辑改动:
- 按需调用 skill
- steering-readme-maintainer同步 product/tech/structure-lite/README
- change-annotation-audit写 docs/audit/changes/... + AI_CHANGELOG + CHANGE 注释)
- bd-manual-db-docs仅当 DB schema 变更)
3) 完成后把 `.kiro/.audit_state.json``audit_required` 置为 false或清空 reasons/changed_files/last_reminded_at
## 输出(强制极短回执)
你最终只允许输出 3 段信息:
- done: yes/no
- files_written: <按行列出相对路径>
- next_step: <若失败给 1~2 条;成功则写 “commit when ready”>

View File

@@ -0,0 +1,15 @@
{
"enabled": true,
"name": "Audit Flagger (Prompt Submit)",
"description": "每次提交 prompt 时,基于 git status 判断是否存在高风险改动;若需要审计则写入 .kiro/.audit_state.json无 stdout。",
"version": "1",
"when": {
"type": "promptSubmit"
},
"then": {
"type": "runCommand",
"command": "powershell -NoProfile -ExecutionPolicy Bypass -File .kiro/scripts/audit_flagger.ps1"
},
"workspaceFolderName": "NeoZQYY",
"shortName": "audit-flagger"
}

View File

@@ -0,0 +1,15 @@
{
"enabled": true,
"name": "Audit Reminder (Agent Stop, 15min)",
"description": "若检测到高风险改动且未审计,则在 agentStop 以 stderr+非0 形式提醒15 分钟限频;不写 stdout。",
"version": "1",
"when": {
"type": "agentStop"
},
"then": {
"type": "runCommand",
"command": "powershell -NoProfile -ExecutionPolicy Bypass -File .kiro/scripts/audit_reminder.ps1"
},
"workspaceFolderName": "NeoZQYY",
"shortName": "audit-reminder"
}

View File

@@ -0,0 +1,15 @@
{
"enabled": false,
"name": "change-impact-reviewSteering + README",
"description": "每次 agent 执行结束后,评估本轮代码变更是否需要同步更新 product/tech/structure steering 文档及 README必要时自动更新并输出审计摘要。已禁用改为手动 /audit 子代理流程)",
"version": "1",
"when": {
"type": "agentStop"
},
"then": {
"type": "askAgent",
"prompt": "你必须对本轮执行进行「变更影响审查」。\n\n第一步判断本轮是否引入了「逻辑改动」业务规则、数据处理/ETL 逻辑、API 行为、鉴权/权限、小程序交互逻辑)。如果没有逻辑改动(仅格式化/注释/拼写修正),输出「无逻辑改动」并结束。\n\n第二步如果存在逻辑改动逐一评估以下文档是否需要更新需要则立即更新\n- .kiro/steering/product.md产品定位、业务规则/定义)\n- .kiro/steering/tech.md技术栈/约束、部署/运行时假设)\n- .kiro/steering/structure.md目录结构、关键模块边界\n- README.md运行方式、环境变量、接口、本地部署、集成说明\n- gui/README.md\tGUI 的独立文档,需要说明各子目录用途和常用命令\n- docs/ 文档目录索引,帮助找到正确的子目录\n- scripts/ 脚本较多且分子目录,需要说明各子目录用途和常用命令\n- tasks/ 任务开发约定(如何新增任务、注册流程)\n- database/ Schema 约定、迁移规范\n- tests/ 测试运行方式、FakeDB/FakeAPI 用法\n\n第三步输出审计友好的摘要\n- 变更范围:涉及的模块/接口/数据库对象\n- 变更原因:为什么改\n- 风险评估:回归范围 + 建议运行的测试/验证\n- 文档同步:已更新的文档列表(或明确说明无需更新的理由)\n\n第4步) 变更标注与审计落盘(强制执行):\n创建或更新审计记录文件docs/audit/changes/<YYYY-MM-DD>__<slug>.md内容必须包含\n- 日期/时间Asia/Shanghai\n- 原始用户 Prompt原文或引用 Prompt-ID + 不超过 5 行的摘录)\n- 直接原因AI 分析后“为何必须改” + “修改方案简介”\n- 修改文件清单Files changed list\n- 风险点、回滚要点、验证步骤(至少包含可执行的验证方式)\n对每一个被修改的文件必须在文件内新增或更新 AI_CHANGELOG 记录项,至少包含:\n- 日期\n- PromptPrompt-ID + 摘录)\n- 直接原因(必要性 + 方案简介)\n- 变更摘要(改了什么:模块/函数/接口/字段等)\n- 风险与验证(回归范围 + 验证方法/测试点/SQL/联调步骤)\n对每一处“逻辑变更”的代码块必须在变更附近添加内联 CHANGE 标记注释,至少说明:\n- 变更意图intent\n- 关键假设assumptions\n- 边界条件/资金口径/精度与舍入规则(若相关)\n- 关联 PromptPrompt-ID 或摘录)以及必要的验证提示\n\n硬性规则如果涉及数据库 schema 或表结构变更,必须同步更新 docs/database/ 下对应的表结构文档。"
},
"workspaceFolderName": "NeoZQYY",
"shortName": "change-impact-review"
}

View File

@@ -0,0 +1,15 @@
{
"enabled": true,
"name": "Manual: DB 文档全量同步",
"description": "按需触发:对比 Postgres 实际 schema 与 docs/database/ 下的文档,自动补全或更新缺失/过时的表结构说明,并输出变更摘要。",
"version": "1",
"when": {
"type": "userTriggered"
},
"then": {
"type": "askAgent",
"prompt": "执行一次按需的数据库文档全量同步。\n\n步骤\n1) 检查当前 Postgres schema使用环境中可用的工具/命令,例如 pg_dump --schema-only 或查询 information_schema。\n2) 与 docs/database 下现有文档进行对比。\n3) 更新缺失或过时的 schema/表结构文档。\n4) 输出对账摘要:哪些文档被修改了、修改原因。输出路径遵循.env路径定义。\n\n注意如果需要执行 shell 命令,请通过 agent 的 shell 工具调用。"
},
"workspaceFolderName": "NeoZQYY",
"shortName": "db-docs-sync"
}

View File

@@ -0,0 +1,22 @@
{
"enabled": false,
"name": "DB Schema 文档执行 (bd_manual)",
"description": "当数据库 schema/migration 相关文件被保存时,检查是否有表结构变更,并自动更新 docs/database/ 下对应的表结构文档。(已禁用:由 /audit 流程统一处理 DB 文档)",
"version": "1",
"when": {
"type": "fileEdited",
"patterns": [
"**/migrations/**/*.*",
"**/*.sql",
"**/*ddl*.*",
"**/*schema*.*",
"**/*.prisma"
]
},
"then": {
"type": "askAgent",
"prompt": "一个数据库相关文件刚被保存。你必须检查是否发生了 schema/表结构变更。\n\n如果发生了表结构变更你必须更新以下目录中的文档\ndocs/database/\n\n最低输出要求必须写入对应 schema 目录 + 表结构文档):\n1) 变更内容:表/字段/类型/可空性/默认值/约束/索引/外键的具体变化\n2) 变更原因:业务背景与动机\n3) 影响范围ETL 管线、后端 API 契约、小程序字段等\n4) 回滚策略:如何回退 + 数据回填注意事项\n5) 验证 SQL至少 3 条查询语句用于验证变更正确性\n6) 溯源留痕日期Asia/ShanghaiYYYY-MM-DDPromptPrompt-ID + ≤5 行摘录或原文Direct cause必要性 + 修改方案简介)\n\n如果没有发生表结构变更例如仅修改注释在变更日志文档中写一条简短说明\"无结构性变更\"(同样要带日期 + Prompt-ID。"
},
"workspaceFolderName": "NeoZQYY",
"shortName": "db-schema-doc-enforcer"
}

View File

@@ -0,0 +1,15 @@
{
"enabled": true,
"name": "Prompt Audit Log (Shell)",
"description": "每次提交 prompt 时,用本地 Shell 在 docs/audit/prompt_logs/ 生成独立日志文件(按时间戳命名);不触发 LLM避免上下文膨胀。",
"version": "3",
"when": {
"type": "promptSubmit"
},
"then": {
"type": "runCommand",
"command": "powershell -NoProfile -ExecutionPolicy Bypass -File .kiro/scripts/prompt_audit_log.ps1"
},
"workspaceFolderName": "NeoZQYY",
"shortName": "prompt-audit-log"
}

View File

@@ -0,0 +1,15 @@
{
"enabled": true,
"name": "Manual: Run /audit (via audit-writer subagent)",
"description": "按需触发:启动 audit-writer 子代理执行变更影响审查+文档同步+审计落盘,完成后自动刷新审计一览表,并仅回传极短回执。",
"version": "2",
"when": {
"type": "userTriggered"
},
"then": {
"type": "askAgent",
"prompt": "立刻启动名为 audit-writer 的子代理来执行「后处理写入/审计收口」流程。\n\n约束\n- 子代理自行使用 git status/diff 与 .kiro/.last_prompt_id.json 中最新 Prompt-ID 作为溯源;不要依赖主对话上下文。\n- 子代理必须按需调用 skillsteering-readme-maintainer、change-annotation-audit、bd-manual-db-docs仅在满足触发条件时。\n- 子代理结束后,必须把 .kiro/.audit_state.json 中 audit_required 置为 false或清空文件以停止后续提醒。\n- 审计落盘完成后,必须执行 `python scripts/gen_audit_dashboard.py` 刷新审计一览表docs/audit/audit_dashboard.md。\n- 你的最终回复必须是「极短回执」,只包含:\n 1) 是否完成yes/no\n 2) 写了哪些文件(文件列表)\n 3) 如果失败下一步怎么做1~2 条)"
},
"workspaceFolderName": "NeoZQYY",
"shortName": "audit"
}

View File

@@ -0,0 +1,128 @@
# audit_flagger.ps1 — 判断 git 工作区是否存在高风险改动
# 兼容 Windows PowerShell 5.1(避免 try{} 内嵌套脚本块的解析器 bug
$ErrorActionPreference = "SilentlyContinue"
function Get-TaipeiNow {
$tz = [TimeZoneInfo]::FindSystemTimeZoneById("Taipei Standard Time")
if ($tz) {
return [TimeZoneInfo]::ConvertTime([DateTimeOffset]::Now, $tz)
}
return [DateTimeOffset]::Now
}
function Sha1Hex([string]$s) {
$sha1 = [System.Security.Cryptography.SHA1]::Create()
$bytes = [System.Text.Encoding]::UTF8.GetBytes($s)
$hash = $sha1.ComputeHash($bytes)
return ([BitConverter]::ToString($hash) -replace "-", "").ToLowerInvariant()
}
function Get-ChangedFiles {
$status = git status --porcelain 2>$null
if ($LASTEXITCODE -ne 0) { return @() }
$result = @()
foreach ($line in $status) {
if ([string]::IsNullOrWhiteSpace($line)) { continue }
$pathPart = $line.Substring([Math]::Min(3, $line.Length - 1)).Trim()
if ($pathPart -match " -> ") { $pathPart = ($pathPart -split " -> ")[-1] }
if ([string]::IsNullOrWhiteSpace($pathPart)) { continue }
$result += $pathPart.Replace("\", "/").Trim()
}
return $result
}
function Test-NoiseFile([string]$f) {
if ($f -match "^docs/audit/") { return $true }
if ($f -match "^\.kiro/\.audit_state\.json$") { return $true }
if ($f -match "^\.kiro/\.last_prompt_id\.json$") { return $true }
if ($f -match "^\.kiro/scripts/") { return $true }
return $false
}
function Write-AuditState([string]$json) {
$null = New-Item -ItemType Directory -Force -Path ".kiro" 2>$null
Set-Content -Path ".kiro/.audit_state.json" -Value $json -Encoding UTF8
}
# --- 主逻辑 ---
# 非 git 仓库直接退出
$null = git rev-parse --is-inside-work-tree 2>$null
if ($LASTEXITCODE -ne 0) { exit 0 }
$allFiles = Get-ChangedFiles
# 过滤噪声
$files = @()
foreach ($f in $allFiles) {
if (-not (Test-NoiseFile $f)) { $files += $f }
}
$files = $files | Sort-Object -Unique
$now = Get-TaipeiNow
if ($files.Count -eq 0) {
$json = '{"audit_required":false,"db_docs_required":false,"reasons":[],"changed_files":[],"change_fingerprint":"","marked_at":"' + $now.ToString("o") + '","last_reminded_at":null}'
Write-AuditState $json
exit 0
}
# 高风险路径("regex|label" 格式,避免 @{} 哈希表在 PS 5.1 的解析问题)
$riskRules = @(
"^apps/etl/pipelines/feiqiu/(api|cli|config|database|loaders|models|orchestration|scd|tasks|utils|quality)/|etl",
"^apps/backend/app/|backend",
"^packages/shared/|shared",
"^db/|db"
)
$reasons = @()
$auditRequired = $false
$dbDocsRequired = $false
foreach ($f in $files) {
foreach ($rule in $riskRules) {
$idx = $rule.LastIndexOf("|")
$pat = $rule.Substring(0, $idx)
$lbl = $rule.Substring($idx + 1)
if ($f -match $pat) {
$auditRequired = $true
$tag = "dir:" + $lbl
if ($reasons -notcontains $tag) { $reasons += $tag }
}
}
if ($f -notmatch "/") {
$auditRequired = $true
if ($reasons -notcontains "root-file") { $reasons += "root-file" }
}
if ($f -match "^db/" -or $f -match "/migrations/" -or $f -match "\.sql$" -or $f -match "\.prisma$") {
$dbDocsRequired = $true
if ($reasons -notcontains "db-schema-change") { $reasons += "db-schema-change" }
}
}
$fp = Sha1Hex(($files -join "`n"))
# 读取已有状态以保留 last_reminded_at
$lastReminded = $null
if (Test-Path ".kiro/.audit_state.json") {
$raw = Get-Content ".kiro/.audit_state.json" -Raw 2>$null
if ($raw) {
$existing = $raw | ConvertFrom-Json 2>$null
if ($existing -and $existing.change_fingerprint -eq $fp) {
$lastReminded = $existing.last_reminded_at
}
}
}
$stateObj = [ordered]@{
audit_required = [bool]$auditRequired
db_docs_required = [bool]$dbDocsRequired
reasons = $reasons
changed_files = $files | Select-Object -First 50
change_fingerprint = $fp
marked_at = $now.ToString("o")
last_reminded_at = $lastReminded
}
Write-AuditState ($stateObj | ConvertTo-Json -Depth 6)
exit 0

View File

@@ -0,0 +1,72 @@
$ErrorActionPreference = "Stop"
function Get-TaipeiNow {
try {
$tz = [TimeZoneInfo]::FindSystemTimeZoneById("Taipei Standard Time")
return [TimeZoneInfo]::ConvertTime([DateTimeOffset]::Now, $tz)
} catch {
return [DateTimeOffset]::Now
}
}
try {
$statePath = ".kiro/.audit_state.json"
if (-not (Test-Path $statePath)) { exit 0 }
$state = $null
try { $state = Get-Content $statePath -Raw | ConvertFrom-Json } catch { exit 0 }
if (-not $state) { exit 0 }
# If no pending audit, do nothing
if (-not $state.audit_required) { exit 0 }
# If working tree is clean (ignoring logs/state), stop reminding
$null = git rev-parse --is-inside-work-tree 2>$null
if ($LASTEXITCODE -ne 0) { exit 0 }
$status = git status --porcelain
$files = @()
foreach ($line in $status) {
if ([string]::IsNullOrWhiteSpace($line)) { continue }
$pathPart = $line.Substring([Math]::Min(3, $line.Length-1)).Trim()
if ($pathPart -match " -> ") { $pathPart = ($pathPart -split " -> ")[-1] }
$p = $pathPart.Replace("\", "/").Trim()
if ($p -and $p -notmatch "^docs/audit/" -and $p -notmatch "^\.kiro/") { $files += $p }
}
if (($files | Sort-Object -Unique).Count -eq 0) {
$state.audit_required = $false
$state.reasons = @()
$state.changed_files = @()
$state.last_reminded_at = $null
($state | ConvertTo-Json -Depth 6) | Set-Content -Path $statePath -Encoding UTF8
exit 0
}
$now = Get-TaipeiNow
$minInterval = [TimeSpan]::FromMinutes(15)
$last = $null
if ($state.last_reminded_at) {
try { $last = [DateTimeOffset]::Parse($state.last_reminded_at) } catch { $last = $null }
}
$shouldRemind = $true
if ($last) {
$elapsed = $now - $last
if ($elapsed -lt $minInterval) { $shouldRemind = $false }
}
if (-not $shouldRemind) { exit 0 }
# Update last_reminded_at (persist even if user ignores)
$state.last_reminded_at = $now.ToString("o")
($state | ConvertTo-Json -Depth 6) | Set-Content -Path $statePath -Encoding UTF8
$reasons = @()
if ($state.reasons) { $reasons = @($state.reasons) }
$reasonText = if ($reasons.Count -gt 0) { ($reasons -join ", ") } else { "high-risk paths changed" }
[Console]::Error.WriteLine("[AUDIT REMINDER] Pending audit detected ($reasonText). Run /audit (Manual: Run /audit hook) to sync docs & write audit artifacts. (rate limit: 15min)")
exit 1
} catch {
exit 0
}

View File

@@ -0,0 +1,61 @@
$ErrorActionPreference = "Stop"
function Get-TaipeiNow {
try {
$tz = [TimeZoneInfo]::FindSystemTimeZoneById("Taipei Standard Time")
return [TimeZoneInfo]::ConvertTime([DateTimeOffset]::Now, $tz)
} catch {
return [DateTimeOffset]::Now
}
}
try {
$now = Get-TaipeiNow
$promptId = "P{0}" -f $now.ToString("yyyyMMdd-HHmmss")
$promptRaw = $env:USER_PROMPT
if ($null -eq $promptRaw) { $promptRaw = "" }
# 截断过长的 prompt避免意外记录展开的 #context
if ($promptRaw.Length -gt 20000) {
$promptRaw = $promptRaw.Substring(0, 5000) + "`n[TRUNCATED: prompt too long; possible expanded #context]"
}
$summary = ($promptRaw -replace "\s+", " ").Trim()
if ($summary.Length -gt 120) { $summary = $summary.Substring(0, 120) + "" }
if ([string]::IsNullOrWhiteSpace($summary)) { $summary = "(empty prompt)" }
# CHANGE [2026-02-15] intent: 简化为每次直接写独立文件到 prompt_logs/,不再维护 prompt_log.md 中间文件
$logDir = Join-Path "docs" "audit" "prompt_logs"
$null = New-Item -ItemType Directory -Force -Path $logDir 2>$null
$filename = "prompt_log_{0}.md" -f $now.ToString("yyyyMMdd_HHmmss")
$targetLog = Join-Path $logDir $filename
$timestamp = $now.ToString("yyyy-MM-dd HH:mm:ss zzz")
$entry = @"
- [$promptId] $timestamp
- summary: $summary
- prompt:
``````text
$promptRaw
``````
"@
Set-Content -Path $targetLog -Value $entry -Encoding UTF8
# 保存 last prompt id 供下游 /audit 溯源
$stateDir = ".kiro"
$null = New-Item -ItemType Directory -Force -Path $stateDir 2>$null
$lastPrompt = @{
prompt_id = $promptId
at = $now.ToString("o")
} | ConvertTo-Json -Depth 4
Set-Content -Path (Join-Path $stateDir ".last_prompt_id.json") -Value $lastPrompt -Encoding UTF8
exit 0
} catch {
# 不阻塞 prompt 提交
exit 0
}

4
.kiro/settings/mcp.json Normal file
View File

@@ -0,0 +1,4 @@
{
"mcpServers": {
}
}

View File

@@ -0,0 +1,41 @@
---
name: bd-manual-db-docs
description: 当 PostgreSQL schema/表结构发生变化时,用于将变更以审计友好的方式落盘到 docs/database/(含变更原因、影响、回滚与验证 SQL
---
# 目的
保证数据库结构变化可追溯、可审计、可回滚,并与 ETL/后端/小程序字段映射保持一致。
# 触发条件
- 迁移脚本/DDL 修改(新增/删除/改表、字段、类型、默认值、非空、约束、索引、外键)
- ORM/Schema 定义变更导致实际 DB 结构变化
- 手工执行 DDL需用 manualTrigger hook 或本 Skill 补齐文档)
# 输出要求(必须全部满足)
所有输出必须落盘到:`docs/database/`
至少包含:
1) Schema Change Log变更日志条目
2) Table Structure Doc涉及表的结构文档更新
3) Rollback & Verification回滚要点 + 至少 3 条验证 SQL
4) 溯源:日期 + Prompt-ID/Prompt 摘录 + Direct cause必要性 + 方案简介)
# 工作流
## 1) 识别结构性变化
- 列出新增/修改/删除的对象schema/table/column/index/constraint/fk
- 明确变更前后差异before/after
## 2) 更新变更日志Schema Change Log
- 在对应 schema 目录下追加一条变更记录(模板见 assets/schema-changelog-template.md
## 3) 更新表结构文档Table Structure Doc
- 每张受影响的表都要更新(模板见 assets/table-structure-template.md
- 同步字段含义/口径说明,尤其是金额类字段:精度、币种、舍入
## 4) 回滚与验证
- 写清楚 DDL 回滚路径(必要时提供反向迁移)
- 写至少 3 条验证 SQL含约束/索引/关键字段检查)
# 模板
- `assets/schema-changelog-template.md`
- `assets/table-structure-template.md`

View File

@@ -0,0 +1,27 @@
# Schema 变更日志Schema Change Log
- 日期Asia/ShanghaiYYYY-MM-DD
- Prompt-ID
- 原始原因Prompt 摘录/原文):
- 直接原因(必要性 + 方案简介):
- 影响的 Schema
- 变更摘要(一句话):
## 变更明细
- 新增:
- 修改:
- 删除:
## 影响范围
- ETL
- 后端 API
- 小程序:
## 回滚要点
- DDL 回滚:
- 数据回填/迁移注意事项:
## 验证 SQL至少 3 条)
1)
2)
3)

View File

@@ -0,0 +1,22 @@
# <schema>.<table>
## 表用途Purpose
- 该表代表什么业务对象/过程
## 字段Columns
| 字段名 | 类型 | 可空 | 默认值 | 约束/键 | 说明(含口径) |
|---|---|---:|---|---|---|
> 金额类字段必须注明:币种、精度、舍入/截断规则、是否允许负数。
## 索引Indexes
- 索引名 / 字段 / 是否唯一 / 备注
## 约束与外键Constraints & FKs
- 约束名 / 定义 / 备注
## 数据不变量Invariants
- 例如:状态机枚举范围、唯一性、跨字段一致性约束(如有)
## 变更历史Change History
- YYYY-MM-DD | Prompt-ID | 直接原因 | 变更摘要

View File

@@ -0,0 +1,37 @@
---
name: change-annotation-audit
description: 对每次修改强制生成审计记录docs/audit/changes/...),并在每个被改文件写 AI_CHANGELOG、在逻辑变更处写 CHANGE 标记注释包含日期、Prompt 与直接原因)。
---
# 目的
把“为什么改、怎么改、怎么验”固化到可审计产物中,满足资金相关项目的严谨性要求。
# 触发条件
- 任何对代码或文档的实质修改(非纯格式化)
- 特别是逻辑改动、资金口径改动、接口契约改动、DB 结构改动
# 必须产物(缺一不可)
1) `docs/audit/changes/<YYYY-MM-DD>__<slug>.md`
2) 每个被修改文件内的 `AI_CHANGELOG` 条目
3) 每个逻辑变更附近的 `CHANGE` 标记注释
# 工作流
## 1) Prompt 溯源
- 确认本次修改有 Prompt-ID来自 prompt_log.md
- 若没有,先补写 Prompt-ID再继续
## 2) 写审计记录Per-change
使用模板:`assets/audit-record-template.md`
- 必须写原始原因Prompt、直接原因、改动方案简介、文件清单、风险/回滚/验证
## 3) 写文件内 AI_CHANGELOGPer-file
- 对每个修改的文件追加一条 AI_CHANGELOG
- 选择适合语言/文件类型的注释风格(模板见 assets/file-changelog-templates.md
## 4) 写 CHANGE 标记Block-level
- 对每处逻辑变更,必须在附近写 CHANGE 标记
- 必须包含intent、assumptions、边界条件金额/舍入/精度)、验证提示
# 模板
- `assets/audit-record-template.md`
- `assets/file-changelog-templates.md`

View File

@@ -0,0 +1,19 @@
# 变更审计记录Change Audit Record
- 日期/时间Asia/Shanghai
- Prompt-ID
- 原始原因Prompt 原文或 ≤5 行摘录):
- 直接原因(必要性 + 修改方案简介):
## 变更范围Changed
- 模块/接口/表/关键文件:
## 风险与回滚Risk & Rollback
- 风险点:
- 回滚要点:
## 验证Verification
- 至少 1 条可执行验证方式(测试/SQL/联调):
## 文件清单Files changed
- ...

View File

@@ -0,0 +1,48 @@
# 文件内 AI_CHANGELOG 与 CHANGE 标记模板
## 通用 AI_CHANGELOG建议放在文件头部或“变更记录”小节
- 2026-02-13 | Prompt: P20260213-101530摘录...| Direct cause... | Summary... | Verify...
---
## Markdown / 文档(放在文档末尾或“变更记录”小节)
### AI_CHANGELOG
- YYYY-MM-DD | Prompt: P...(摘录:...| Direct cause... | Summary... | Verify...
---
## JS/TS块注释
/*
AI_CHANGELOG
- YYYY-MM-DD | Prompt: P...(摘录:...| Direct cause... | Summary... | Verify...
*/
// [CHANGE P...] intent: ...
// assumptions: ...
// edge cases / money semantics: ...
// verify: ...
---
## Pythondocstring/块注释)
"""
AI_CHANGELOG
- YYYY-MM-DD | Prompt: P...(摘录:...| Direct cause... | Summary... | Verify...
"""
# [CHANGE P...] intent: ...
# assumptions: ...
# edge cases / money semantics: ...
# verify: ...
---
## SQL块注释 + 行注释)
/*
AI_CHANGELOG
- YYYY-MM-DD | Prompt: P...(摘录:...| Direct cause... | Summary... | Verify...
*/
-- [CHANGE P...] intent: ...
-- assumptions: ...
-- money semantics: precision/rounding/currency ...
-- verify: ...

View File

@@ -0,0 +1,38 @@
---
name: steering-readme-maintainer
description: 当发生业务/ETL/API/鉴权/小程序交互等逻辑改动时,用于执行变更影响审查并同步更新 product/tech/structure/README 与审计记录。
---
# 目的
将“逻辑改动→文档同步→审计留痕”流程标准化,减少漏更与口径漂移风险(资金相关场景优先保证可追溯与可复算)。
# 触发条件(何时调用本 Skill
- 修改了业务规则/计算口径/资金处理(精度、舍入、阈值等)
- 修改了 ETL/SQL 清洗聚合映射逻辑
- 修改了 API 行为(返回结构、错误码、鉴权/权限)
- 修改了小程序关键交互流程(校验、状态机、关键字段)
# 工作流(必须按顺序执行)
## 1) 分类:是否属于“逻辑改动”
- 若不是逻辑改动:写明“无逻辑改动”,并说明为何(例如仅格式化/拼写修正/注释调整)。
- 若是逻辑改动:进入下一步。
## 2) Steering 与 README 同步(逐项评估)
- `.kiro/steering/product.md`:业务定义/口径/资金规则是否变化?
- `.kiro/steering/tech.md`:技术栈/运行方式/依赖/部署假设是否变化?
- `.kiro/steering/structure-lite.md摘要/ .kiro/steering/structure.md仅在目录树/边界变化时)`:目录/模块边界/职责是否变化?
- `README.md`:运行方式、配置、环境变量、接口契约、联调步骤是否变化?
> 规则:如果“对读者理解系统行为”有帮助,就应更新;不要为了追求“少改文档”而拒绝同步。
## 3) 输出审计友好摘要(对话回复/审计记录都需要)
- Changed改了哪些模块/接口/表/关键文件
- Why原始原因Prompt-ID + 摘录)与直接原因(必要性 + 方案简介)
- Risk风险点与回归范围
- Verify建议的验证步骤测试/SQL/联调)
## 4) 联动硬规则检查
- 如果涉及 DB schema/表结构变化:必须同步更新 `docs/database/`(见 skill `bd-manual-db-docs`)。
# 资产(可复制模板/清单)
见:`assets/steering-update-checklist.md`

View File

@@ -0,0 +1,23 @@
# Steering & README 同步清单(逻辑改动必查)
## product.md产品/口径)
- 业务定义/指标口径/字段含义是否改变?
- 涉及金额的精度/舍入/阈值规则是否改变?
- 角色/权限模型是否改变?
## tech.md技术/运行)
- 新增/变更依赖(框架、库、驱动)?
- 配置项/环境变量/端口/服务启动方式是否改变?
- 数据访问边界ETL 库 vs 业务库)是否改变?
- 性能/一致性/幂等/重试策略是否改变?
## structure.md结构/职责)
- 新增目录/模块?
- 模块职责或边界是否重新划分?
- 新增集成点(队列、定时任务、外部系统)?
## README.md使用/联调)
- 本地启动步骤是否改变?
- 新增/变更配置项(.env 等)?
- API 契约是否变化(路径、参数、返回、错误码)?
- 小程序联调步骤是否变化?

View File

@@ -0,0 +1 @@
{"generationMode": "requirements-first"}

View File

@@ -0,0 +1,311 @@
# 设计文档:数据库文档整理与补全
## 概述
本特性对 `docs/bd_manual/` 目录进行系统性整理和补全,涵盖四个核心工作流:
1. **目录结构规范化** — 统一各层目录布局,新增 `ETL_Admin/` 层和根目录 `README.md` 索引
2. **DDL 对比同步** — 编写 Python 脚本对比四个 schema 的 DDL 文件与数据库实际状态,以数据库为准修正 DDL
3. **ODS 表级文档补全** — 为 `billiards` schema 下所有 ODS 表生成 Markdown 表级文档
4. **API→ODS 字段映射文档** — 建立 API JSON 响应到 ODS 表字段的映射关系文档
本特性以文档和 DDL 维护为主不涉及业务代码逻辑变更。DDL 修正属于 `database/` 高风险路径,完成后需触发 `/audit`
## 架构
```mermaid
graph TD
subgraph 信息来源
DB[(PostgreSQL 数据库)]
DDL[database/schema_*.sql]
API_REF[docs/api-reference/]
PARSERS[models/parsers.py]
COMMENTS[DDL COMMENT ON 注释]
end
subgraph 对比脚本
COMPARE[scripts/compare_ddl_db.py]
end
subgraph 文档产出
README[docs/bd_manual/README.md]
ODS_DOCS[docs/bd_manual/ODS/main/*.md]
ODS_MAP[docs/bd_manual/ODS/mappings/*.md]
ODS_DICT[docs/dictionary/ods_tables_dictionary.md]
ETL_DOCS[docs/bd_manual/ETL_Admin/main/*.md]
CHANGES[docs/bd_manual/*/changes/*.md]
DDL_FIX[database/schema_*.sql 修正]
end
DB -->|information_schema 查询| COMPARE
DDL -->|解析 CREATE TABLE| COMPARE
COMPARE -->|差异报告| CHANGES
COMPARE -->|修正| DDL_FIX
DB -->|表结构 + COMMENT| ODS_DOCS
COMMENTS -->|字段说明| ODS_DOCS
API_REF -->|端点信息| ODS_MAP
PARSERS -->|转换逻辑| ODS_MAP
DB -->|表概览| ODS_DICT
DB -->|表结构| ETL_DOCS
```
## 组件与接口
### 1. DDL 对比脚本 (`scripts/compare_ddl_db.py`)
一个独立的 Python 脚本,用于对比 DDL 文件与数据库实际状态。
**输入**
- DDL 文件路径(`database/schema_*.sql`
- 数据库连接(通过 `PG_DSN` 环境变量或 `--pg-dsn` 参数)
**输出**
- 控制台差异报告(表级、字段级、类型级)
- 可选:`--fix` 模式直接修正 DDL 文件
**对比逻辑**
-`information_schema.columns` 查询数据库实际表结构
- 解析 DDL 文件中的 `CREATE TABLE` 语句提取表名和字段定义
- 逐表逐字段对比:表是否存在、字段是否存在、字段类型是否一致、约束是否一致
- 差异分类:`MISSING_TABLE`DDL 缺表)、`EXTRA_TABLE`DDL 多表)、`MISSING_COLUMN``EXTRA_COLUMN``TYPE_MISMATCH``NULLABLE_MISMATCH`
**接口**
```python
def compare_schema(ddl_path: str, schema_name: str, pg_dsn: str) -> list[SchemaDiff]
```
### 2. ODS 表级文档生成器
手动编写(非自动生成脚本),参考以下信息来源:
- 数据库 `information_schema.columns` 获取字段名、类型、可空性
- DDL 文件中的 `COMMENT ON` 注释获取字段说明、示例值、JSON 字段映射
- 现有 DWD/DWS 表级文档格式作为模板
### 3. API→ODS 映射文档
手动编写,参考以下信息来源:
- `docs/api-reference/endpoints/*.md` — API 端点路径、请求参数、响应字段
- `docs/api-reference/samples/*.json` — JSON 响应样本
- `models/parsers.py``TypeParser` 类中的类型转换方法
- DDL 文件中的 `COMMENT ON` 注释中的 `【JSON字段】` 标注
### 4. 目录结构与索引
**新增目录**
- `docs/bd_manual/ETL_Admin/main/`
- `docs/bd_manual/ETL_Admin/changes/`
- `docs/bd_manual/ODS/mappings/`
**新增文件**
- `docs/bd_manual/README.md` — 根索引,列出目录结构和各层文档清单
## 数据模型
本特性不引入新的数据模型。涉及的现有 schema 如下:
| Schema | DDL 文件 | 用途 | 预估表数 |
|--------|----------|------|----------|
| `billiards_ods` | `database/schema_ODS_doc.sql` | 原始数据存储 | ~22 张 |
| `billiards_dwd` | `database/schema_dwd_doc.sql` | 明细数据层 | ~22 张(含 Ex |
| `billiards_dws` | `database/schema_dws.sql` | 数据服务层 | ~30 张 |
| `etl_admin` | `database/schema_etl_admin.sql` | ETL 管理元数据 | ~5 张 |
### 文档模板格式
**ODS 表级文档模板**(与 DWD/DWS 保持一致):
```markdown
# {表名} {中文说明}
> 生成时间YYYY-MM-DD
## 表信息
| 属性 | 值 |
|------|-----|
| Schema | billiards |
| 表名 | {表名} |
| 主键 | {主键字段} |
| 数据来源 | {API 端点 / JSON 文件} |
| 说明 | {表说明} |
## 字段说明
| 序号 | 字段名 | 类型 | 可空 | 说明 |
|------|--------|------|------|------|
| 1 | ... | ... | ... | ... |
## 使用说明
{SQL 示例}
## 可回溯性
| 项目 | 说明 |
|------|------|
| 可回溯 | ✅ 完全可回溯(保留 payload 原始 JSON |
| 数据来源 | {API 端点路径} |
```
**API→ODS 映射文档模板**
```markdown
# {API端点名} → {ODS表名} 字段映射
> 生成时间YYYY-MM-DD
## 端点信息
| 属性 | 值 |
|------|-----|
| 接口路径 | {路径} |
| 请求方法 | POST |
| ODS 对应表 | {表名} |
| JSON 数据路径 | {如 data.tenantMemberInfos} |
## 字段映射
| JSON 字段 | ODS 列名 | 类型转换 | 说明 |
|-----------|----------|----------|------|
| id | id | int→BIGINT | 主键 |
| ... | ... | ... | ... |
## ETL 补充字段
| ODS 列名 | 生成逻辑 |
|-----------|----------|
| content_hash | 对业务字段计算 SHA256 |
| source_file | 固定值:{文件名}.json |
| source_endpoint | API 端点路径 |
| fetched_at | 入库时间戳 |
| payload | 完整原始 JSON 记录 |
## 类型转换规则
- 时间戳:通过 `TypeParser.parse_timestamp()` 转换,支持字符串和 Unix 毫秒时间戳
- 金额:通过 `TypeParser.parse_decimal(value, scale=2)` 转换ROUND_HALF_UP
- 整数:通过 `TypeParser.parse_int()` 转换
```
## 正确性属性
*属性是系统在所有有效执行中都应保持为真的特征或行为——本质上是关于系统应该做什么的形式化陈述。属性是人类可读规格说明与机器可验证正确性保证之间的桥梁。*
### Property 1: 数据层目录结构一致性
*For any* 数据层目录ODS、DWD、DWS、ETL_Admin该目录下都应包含 `main/``changes/` 两个子目录。
**Validates: Requirements 1.2**
### Property 2: DDL 对比脚本差异检测完整性
*For any* schema 和对应的 DDL 文件,当数据库中存在 DDL 文件未定义的表或字段时,对比脚本应将其报告为 `MISSING_TABLE``MISSING_COLUMN`;当 DDL 文件中存在数据库没有的表或字段时,应报告为 `EXTRA_TABLE``EXTRA_COLUMN`;当字段类型不一致时,应报告为 `TYPE_MISMATCH`
**Validates: Requirements 2.1, 2.2, 2.3, 2.4**
### Property 3: DDL 修正后零差异(不动点)
*For any* schema在以数据库实际状态修正 DDL 文件后,再次运行对比脚本,差异列表应为空。
**Validates: Requirements 2.5**
### Property 4: ODS 表级文档覆盖率
*For any* `billiards` schema 中的 ODS 表,在 `docs/bd_manual/ODS/main/` 目录下都应存在一份对应的 Markdown 文档。
**Validates: Requirements 3.1**
### Property 5: ODS 表级文档格式完整性
*For any* ODS 表级文档,都应包含以下章节:表信息(含 Schema、表名、主键、数据来源、说明、字段说明表格、使用说明含 SQL 示例)、可回溯性信息,以及 ETL 元数据字段content_hash、source_file、source_endpoint、fetched_at、payload的说明。
**Validates: Requirements 3.2, 3.4, 3.5**
### Property 6: ODS 表级文档命名规范
*For any* ODS 表级文档文件,其文件名应匹配 `BD_manual_{表名}.md` 格式。
**Validates: Requirements 3.6**
### Property 7: 映射文档覆盖率
*For any* 有对应 ODS 表的 API 端点,在 `docs/bd_manual/ODS/mappings/` 目录下都应存在一份对应的映射文档。
**Validates: Requirements 4.1**
### Property 8: 映射文档内容完整性
*For any* 映射文档都应包含以下信息API 端点路径、ODS 表名、JSON 数据路径、字段映射表格,以及 ETL 补充字段content_hash、source_file、source_endpoint、fetched_at、payload的生成逻辑。
**Validates: Requirements 4.2, 4.4**
### Property 9: 映射文档命名规范
*For any* 映射文档文件,其文件名应匹配 `mapping_{API端点名}_{ODS表名}.md` 格式。
**Validates: Requirements 4.6**
### Property 10: ODS 数据字典覆盖率
*For any* `billiards` schema 中的 ODS 表ODS 数据字典中都应有对应的条目,包含表名、中文说明、主键、数据来源信息。
**Validates: Requirements 5.2**
## 错误处理
### DDL 对比脚本
| 场景 | 处理方式 |
|------|----------|
| 数据库连接失败 | 输出错误信息并退出,返回非零退出码 |
| DDL 文件不存在 | 输出错误信息并跳过该 schema |
| DDL 文件解析失败 | 输出解析错误位置和原因,尽可能继续解析其余部分 |
| schema 在数据库中不存在 | 输出警告并跳过 |
### 文档生成
| 场景 | 处理方式 |
|------|----------|
| 表无 COMMENT 注释 | 字段说明列填写"(待补充)" |
| API 端点文档缺失 | 映射文档中标注"端点文档待补充",仅基于 DDL COMMENT 生成 |
| 字段类型无法识别 | 保留数据库原始类型字符串 |
## 测试策略
### 单元测试
针对 DDL 对比脚本的核心逻辑编写单元测试(`tests/unit/test_compare_ddl.py`
- 测试 DDL 解析器能正确提取表名、字段名、字段类型、约束
- 测试差异检测逻辑能识别各类差异(缺失表、多余表、字段差异、类型差异)
- 测试边界情况:空 DDL 文件、无表的 schema、COMMENT 中含特殊字符
### 属性测试
使用 `hypothesis`Python 属性测试框架)。
- **Property 2 测试**:生成随机的"DDL 表定义"和"数据库表定义",注入已知差异,验证对比函数能检测到所有差异
- **Feature: bd-manual-docs-consolidation, Property 2: DDL 对比脚本差异检测完整性**
- 最少 100 次迭代
- **Property 3 测试**:生成随机的数据库表定义,用其生成 DDL再运行对比验证差异为零
- **Feature: bd-manual-docs-consolidation, Property 3: DDL 修正后零差异(不动点)**
- 最少 100 次迭代
### 集成验证
文档覆盖率和格式验证通过 Python 脚本实现(`scripts/validate_bd_manual.py`),可在 CI 中运行:
- 验证 Property 1目录结构、Property 4-10文档覆盖率、格式、命名
- 输入:文件系统 + 数据库 `information_schema` 查询
- 输出:通过/失败报告,列出缺失或不合规的文档
### 测试配置
- 属性测试库:`hypothesis`(需添加到开发依赖)
- 单元测试:`pytest tests/unit/test_compare_ddl.py`
- 集成验证:`python scripts/validate_bd_manual.py --pg-dsn "$PG_DSN"`
- 每个属性测试最少 100 次迭代
- 每个测试需注释引用对应的设计属性编号

View File

@@ -0,0 +1,80 @@
# 需求文档
## 简介
整理和补全飞球 ETL 系统的数据库文档体系(`docs/bd_manual/`包括目录结构规范化、DDL 与数据库实际状态的对比同步、ODS 层表级文档补全、以及 API JSON → ODS 字段映射文档的建立。本特性不涉及代码逻辑变更,仅涉及文档和 DDL 文件的维护。
## 术语表
- **BD_Manual**: 数据库手册目录(`docs/bd_manual/`),存放各层表级文档、变更记录等
- **ODS**: 操作数据存储层Operational Data Store`billiards` schema保留 API 原始数据
- **DWD**: 明细数据层Data Warehouse Detail`billiards_dwd` schema清洗后的维度和事实表
- **DWS**: 数据服务层Data Warehouse Service`billiards_dws` schema汇总宽表和配置表
- **DDL**: 数据定义语言文件(`database/schema_*.sql`),定义表结构
- **表级文档**: 以 Markdown 格式编写的单表说明文件,包含表信息、字段说明、使用示例等
- **字段映射文档**: 记录 API JSON 响应字段到 ODS 表字段的对应关系和转换逻辑
- **SCD2**: 缓慢变化维度类型 2用于 DWD 维度表的历史版本管理
- **ETL_Admin**: ETL 管理 schema`etl_admin`),存放调度、游标、运行记录等元数据
## 需求
### 需求 1规范化 BD_Manual 目录结构
**用户故事:** 作为数据开发人员,我希望 BD_Manual 目录结构统一规范,以便快速定位各层各类型的数据库文档。
#### 验收标准
1. THE BD_Manual SHALL 包含以下顶层子目录:`ODS/``DWD/``DWS/``ETL_Admin/`
2. WHEN 某一数据层目录被访问时THE BD_Manual SHALL 在该层目录下提供 `main/`(表级文档)和 `changes/`(变更记录)两个子目录
3. THE DWD 目录 SHALL 额外保留 `Ex/` 子目录用于存放扩展表文档
4. THE BD_Manual SHALL 在根目录提供一个 `README.md` 索引文件,列出目录结构说明和各层文档清单
5. WHEN ETL_Admin schema 存在表时THE BD_Manual SHALL 在 `ETL_Admin/main/` 下为每张表提供表级文档
### 需求 2DDL 文件与数据库实际状态对比同步
**用户故事:** 作为数据开发人员,我希望 DDL 文件与数据库实际表结构保持一致,以便 DDL 文件可作为可信的 schema 参考。
#### 验收标准
1. WHEN 对比 ODS 层 DDL 文件(`database/schema_ODS_doc.sql`)与数据库 `billiards_ods` schema 实际表结构时THE 对比脚本 SHALL 输出所有差异项(缺失表、多余表、字段差异、类型差异、约束差异)
2. WHEN 对比 DWD 层 DDL 文件(`database/schema_dwd_doc.sql`)与数据库 `billiards_dwd` schema 实际表结构时THE 对比脚本 SHALL 输出所有差异项
3. WHEN 对比 DWS 层 DDL 文件(`database/schema_dws.sql`)与数据库 `billiards_dws` schema 实际表结构时THE 对比脚本 SHALL 输出所有差异项
4. WHEN 对比 ETL_Admin 层 DDL 文件(`database/schema_etl_admin.sql`)与数据库 `etl_admin` schema 实际表结构时THE 对比脚本 SHALL 输出所有差异项
5. WHEN 发现差异时THE DDL 文件 SHALL 以数据库实际状态为准进行修正
6. WHEN DDL 文件被修正后THE 变更记录 SHALL 在对应层的 `changes/` 目录下生成一份差异说明文档
### 需求 3补全 ODS 层表级文档
**用户故事:** 作为数据开发人员,我希望 ODS 层每张表都有完整的表级文档,以便理解原始数据结构和来源。
#### 验收标准
1. THE ODS 表级文档 SHALL 为 `billiards_ods` schema 中的每张 ODS 表生成一份 Markdown 文档,存放于 `docs/bd_manual/ODS/main/`
2. THE ODS 表级文档 SHALL 遵循与 DWD/DWS 表级文档一致的格式,包含:表信息表格、字段说明表格、使用说明(含 SQL 示例)、可回溯性信息
3. WHEN ODS 表的字段含有 COMMENT 注释时THE 表级文档 SHALL 将 COMMENT 中的说明、示例、JSON 字段映射信息提取并填入字段说明
4. THE ODS 表级文档的表信息 SHALL 包含 Schema、表名、主键、数据来源API 端点或文件)、说明
5. THE ODS 表级文档 SHALL 包含 ETL 元数据字段(`content_hash``source_file``source_endpoint``fetched_at``payload`)的统一说明
6. THE ODS 表级文档的文件命名 SHALL 遵循 `BD_manual_{表名}.md` 格式
### 需求 4建立 API JSON → ODS 字段映射文档
**用户故事:** 作为数据开发人员,我希望有一份清晰的 API 响应字段到 ODS 表字段的映射文档,以便理解数据从 API 到 ODS 的转换逻辑。
#### 验收标准
1. THE 映射文档 SHALL 为每个 API 端点与其对应的 ODS 表建立一份映射文件,存放于 `docs/bd_manual/ODS/mappings/`
2. THE 映射文档 SHALL 包含以下信息API 端点路径、对应 ODS 表名、JSON 响应路径(如 `data.tenantMemberInfos`)、每个字段的 JSON 路径到 ODS 列名的映射
3. WHEN 字段存在类型转换或值处理逻辑时THE 映射文档 SHALL 记录转换规则(如时间格式转换、枚举值映射、金额精度处理)
4. THE 映射文档 SHALL 标注 ETL 补充字段(`content_hash``source_file``source_endpoint``fetched_at``payload`)的生成逻辑
5. THE 映射文档 SHALL 参考 `models/parsers.py` 中的解析逻辑和 `docs/api-reference/` 中的端点文档作为信息来源
6. THE 映射文档的文件命名 SHALL 遵循 `mapping_{API端点名}_{ODS表名}.md` 格式
### 需求 5建立 ODS 数据字典
**用户故事:** 作为数据开发人员,我希望有一份 ODS 层的数据字典汇总文档,以便快速查阅所有 ODS 表的概览信息。
#### 验收标准
1. THE ODS 数据字典 SHALL 创建于 `docs/dictionary/ods_tables_dictionary.md`
2. THE ODS 数据字典 SHALL 列出所有 ODS 表的概览信息,包含:表名、中文说明、主键、记录数、数据来源
3. THE ODS 数据字典 SHALL 遵循与现有 DWD/DWS 数据字典一致的格式

View File

@@ -0,0 +1,111 @@
# 实施计划:数据库文档整理与补全
## 概述
按照"目录结构 → DDL 对比脚本 → DDL 同步 → ODS 文档 → 映射文档 → 数据字典 → 索引"的顺序逐步完成文档体系的整理和补全。DDL 对比脚本先编写并测试,再用它驱动实际的 DDL 同步工作。
## 任务
- [x] 1. 规范化 BD_Manual 目录结构
- 创建 `docs/bd_manual/ETL_Admin/main/``docs/bd_manual/ETL_Admin/changes/` 目录
- 创建 `docs/bd_manual/ODS/mappings/` 目录
- 确认 ODS/DWD/DWS 各层均有 `main/``changes/` 子目录
- _Requirements: 1.1, 1.2, 1.3_
- [ ] 2. 编写 DDL 对比脚本
- [x] 2.1 实现 DDL 解析器和对比核心逻辑 (`scripts/compare_ddl_db.py`)
- 解析 `CREATE TABLE` 语句提取表名、字段名、字段类型、约束、主键
- 查询 `information_schema.columns` 获取数据库实际表结构
- 实现逐表逐字段对比输出差异分类MISSING_TABLE、EXTRA_TABLE、MISSING_COLUMN、EXTRA_COLUMN、TYPE_MISMATCH、NULLABLE_MISMATCH
- 支持 `--pg-dsn``--schema``--ddl-path` 参数
- _Requirements: 2.1, 2.2, 2.3, 2.4_
- [x] 2.2 编写 DDL 解析器单元测试 (`tests/unit/test_compare_ddl.py`)
- 测试 DDL 解析器正确提取表名、字段、类型、约束
- 测试差异检测逻辑识别各类差异
- 测试边界情况空文件、COMMENT 含特殊字符
- _Requirements: 2.1_
- [x] 2.3 编写 DDL 对比属性测试
- **Property 2: DDL 对比脚本差异检测完整性**
- **Validates: Requirements 2.1, 2.2, 2.3, 2.4**
- [x] 2.4 编写 DDL 修正不动点属性测试
- **Property 3: DDL 修正后零差异(不动点)**
- **Validates: Requirements 2.5**
- [x] 3. 检查点 — 确认对比脚本可用
- 确保所有测试通过,如有问题请向用户确认。
- [x] 4. 执行 DDL 对比并同步
- [x] 4.1 运行对比脚本对比四个 schemaODS、DWD、DWS、ETL_Admin
- 执行 `scripts/compare_ddl_db.py` 对比每个 schema
- 记录所有差异项
- _Requirements: 2.1, 2.2, 2.3, 2.4_
- [x] 4.2 修正 DDL 文件以匹配数据库实际状态
- 以数据库为准修正 `database/schema_ODS_doc.sql``database/schema_dwd_doc.sql``database/schema_dws.sql``database/schema_etl_admin.sql`
- _Requirements: 2.5_
- [x] 4.3 生成 DDL 变更记录
- 在对应层的 `changes/` 目录下生成差异说明文档(日期前缀命名)
- 包含变更说明、兼容性影响、回滚策略、验证 SQL至少 3 条)
- _Requirements: 2.6_
- [x] 5. 检查点 — 确认 DDL 同步完成
- 确保所有测试通过,如有问题请向用户确认。
- [x] 6. 补全 ODS 层表级文档
- [x] 6.1 为每张 ODS 表编写表级文档 (`docs/bd_manual/ODS/main/BD_manual_{表名}.md`)
- 从数据库 `information_schema.columns` 获取字段信息
- 从 DDL `COMMENT ON` 注释提取字段说明、示例值、JSON 字段映射
- 遵循 DWD/DWS 文档格式:表信息、字段说明、使用说明(含 SQL、可回溯性
- 包含 ETL 元数据字段统一说明
- _Requirements: 3.1, 3.2, 3.3, 3.4, 3.5, 3.6_
- [x] 7. 建立 API→ODS 字段映射文档
- [x] 7.1 为每个 API 端点编写映射文档 (`docs/bd_manual/ODS/mappings/mapping_{端点名}_{表名}.md`)
- 参考 `docs/api-reference/endpoints/*.md` 获取端点信息和响应字段
- 参考 DDL `COMMENT ON` 中的 `【JSON字段】` 标注获取映射关系
- 参考 `models/parsers.py``TypeParser` 的转换方法记录类型转换规则
- 包含 ETL 补充字段生成逻辑说明
- _Requirements: 4.1, 4.2, 4.3, 4.4, 4.5, 4.6_
- [x] 8. 建立 ODS 数据字典和 ETL_Admin 文档
- [x] 8.1 创建 ODS 数据字典 (`docs/dictionary/ods_tables_dictionary.md`)
- 列出所有 ODS 表概览:表名、中文说明、主键、记录数、数据来源
- 遵循现有 DWD/DWS 数据字典格式
- _Requirements: 5.1, 5.2, 5.3_
- [x] 8.2 为 ETL_Admin 表编写表级文档 (`docs/bd_manual/ETL_Admin/main/BD_manual_{表名}.md`)
- 从数据库获取 `etl_admin` schema 表结构
- 遵循统一文档格式
- _Requirements: 1.5_
- [x] 9. 编写 BD_Manual 根目录 README.md 索引
- 创建 `docs/bd_manual/README.md`
- 列出目录结构说明、各层文档清单、文档命名规范
- _Requirements: 1.4_
- [x] 10. 编写文档验证脚本
- [x] 10.1 实现文档覆盖率和格式验证脚本 (`scripts/validate_bd_manual.py`)
- 验证目录结构一致性Property 1
- 验证 ODS 文档覆盖率和命名规范Property 4, 6
- 验证 ODS 文档格式完整性Property 5
- 验证映射文档覆盖率和命名规范Property 7, 9
- 验证映射文档内容完整性Property 8
- 验证数据字典覆盖率Property 10
- 支持 `--pg-dsn` 参数连接数据库获取表清单
- _Requirements: 1.2, 3.1, 3.2, 3.6, 4.1, 4.2, 4.6, 5.2_
- [x] 11. 最终检查点 — 确认所有文档完整
- 运行 `scripts/validate_bd_manual.py` 确认所有验证通过
- 确保所有测试通过,如有问题请向用户确认。
## 备注
- 标记 `*` 的子任务为可选,可跳过以加速 MVP
- 每个任务引用了具体的需求编号以便追溯
- DDL 修正涉及 `database/` 高风险路径,完成后需触发 `/audit`
- 属性测试验证对比脚本的通用正确性,集成验证脚本验证文档体系的完整性
- ODS 表级文档和映射文档为手动编写(非自动生成),需逐表参考数据库和 API 文档

View File

@@ -0,0 +1 @@
{"generationMode": "requirements-first"}

View File

@@ -0,0 +1,229 @@
# 设计文档:文档体系整理与优化
## 概述
本设计针对飞球 ETL 系统的 `docs/` 目录进行结构性优化,包含三个核心工作流:
1. **文档覆盖度补充**:新增 `architecture/``business-rules/``operations/` 三个缺失目录及骨架文档,新增项目级 `CHANGELOG.md`,更新文档总索引
2. **审计一览表生成**:编写 Python 脚本解析 `docs/audit/changes/` 下的审计源记录,生成结构化的 `audit_dashboard.md` 汇总视图
3. **业务规则文档迁移**:将 `index_algorithm_cn.md``docs/database/DWS/` 迁移至 `docs/business-rules/`,原位置保留重定向说明
## 架构
### 文档目录结构(目标状态)
```
docs/
├── README.md ← 文档总索引(更新)
├── CHANGELOG.md ← 新增:项目级变更日志
├── architecture/ ← 新增:架构设计文档
│ ├── README.md ← 目录索引
│ ├── system_overview.md ← 系统整体架构
│ └── data_flow.md ← 数据流向详解
├── business-rules/ ← 新增:业务规则文档
│ ├── README.md ← 目录索引
│ ├── index_algorithm_cn.md ← 迁移自 database/DWS/
│ ├── dws_metrics.md ← DWS 口径定义(骨架)
│ └── scd2_rules.md ← SCD2 处理规则(骨架)
├── operations/ ← 新增:运维文档
│ ├── README.md ← 目录索引
│ ├── environment_setup.md ← 环境搭建指南
│ ├── scheduling.md ← 调度配置说明
│ └── troubleshooting.md ← 故障排查手册
├── audit/
│ ├── audit_dashboard.md ← 新增:审计一览表
│ ├── changes/ ← 审计源记录(不变)
│ └── ...
├── api-reference/ ← 不变
├── database/ ← 不变(移除 index_algorithm_cn.md
├── etl_tasks/ ← 不变
├── reports/ ← 不变
└── requirements/ ← 不变
```
### 审计一览表生成流程
```mermaid
graph LR
A["docs/audit/changes/*.md"] -->|Python 脚本解析| B["结构化数据列表"]
B -->|按时间倒序排列| C["时间线视图"]
B -->|按模块分类| D["模块索引视图"]
C --> E["audit_dashboard.md"]
D --> E
```
## 组件与接口
### 组件 1审计一览表生成脚本
- 路径:`scripts/gen_audit_dashboard.py`
- 职责:扫描 `docs/audit/changes/` 目录,解析每个 Markdown 文件,提取结构化信息,生成 `docs/audit/audit_dashboard.md`
- 入口:`python scripts/gen_audit_dashboard.py`
#### 解析逻辑
脚本需要从审计源记录中提取以下字段:
| 字段 | 提取方式 |
|------|----------|
| 日期 | 从文件名前缀 `YYYY-MM-DD` 提取 |
| 标题/需求摘要 | 从 Markdown 一级标题 `# ...` 提取 |
| slug | 从文件名 `__` 后的部分提取 |
| 修改文件列表 | 从"修改文件清单"或"文件清单"章节的表格/列表中提取 |
| 影响模块 | 根据修改文件路径推断所属模块api/、tasks/、docs/ 等) |
| 变更类型 | 从文件内容中提取bugfix/新增/修改/删除/文档) |
| 风险等级 | 从"风险点"章节推断(高/中/低/极低) |
#### 模块分类规则
根据修改文件路径前缀映射到功能模块:
```python
MODULE_MAP = {
"api/": "API 层",
"tasks/ods": "ODS 层",
"tasks/dwd": "DWD 层",
"tasks/dws": "DWS 层",
"tasks/index": "指数算法",
"loaders/": "数据装载",
"database/": "数据库",
"orchestration/": "调度",
"config/": "配置",
"cli/": "CLI",
"models/": "模型",
"scd/": "SCD2",
"docs/": "文档",
"scripts/": "脚本工具",
"tests/": "测试",
"quality/": "质量校验",
"gui/": "GUI",
"utils/": "工具库",
}
```
### 组件 2静态文档文件
纯 Markdown 文件,无代码逻辑。包括:
- 架构文档(`docs/architecture/`
- 业务规则文档(`docs/business-rules/`
- 运维文档(`docs/operations/`
- 变更日志(`docs/CHANGELOG.md`
- 更新后的文档总索引(`docs/README.md`
### 组件 3文件迁移与重定向
-`docs/database/DWS/index_algorithm_cn.md` 内容迁移至 `docs/business-rules/index_algorithm_cn.md`
- 在原位置 `docs/database/DWS/index_algorithm_cn.md` 替换为重定向说明
## 数据模型
### 审计记录解析结构
```python
@dataclass
class AuditEntry:
"""从单个审计源记录文件解析出的结构化数据"""
date: str # YYYY-MM-DD从文件名提取
slug: str # 文件名中 __ 后的标识符
title: str # Markdown 一级标题
filename: str # 源文件名(不含路径)
changed_files: list[str] # 修改的文件路径列表
modules: set[str] # 影响的功能模块集合
risk_level: str # 风险等级:高/中/低/极低
change_type: str # 变更类型bugfix/功能/文档/重构/清理
```
### 审计一览表输出格式
`audit_dashboard.md` 包含两个视图:
1. **时间线视图**(按日期倒序):
```markdown
| 日期 | 需求摘要 | 变更类型 | 影响模块 | 风险 | 详情 |
|------|----------|----------|----------|------|------|
| 2026-02-15 | docs/database 合并 | 重构 | 文档, 脚本工具 | 极低 | [链接](changes/...) |
```
2. **模块索引视图**(按模块分组):
```markdown
### API 层
| 日期 | 需求摘要 | 变更类型 | 风险 | 详情 |
...
### DWS 层
| 日期 | 需求摘要 | 变更类型 | 风险 | 详情 |
...
```
## 正确性属性
*正确性属性是一种在系统所有合法执行路径上都应成立的特征或行为——本质上是对"系统应该做什么"的形式化陈述。属性是连接人类可读规格说明与机器可验证正确性保证之间的桥梁。*
本特性中,大部分需求涉及静态文档文件的创建和目录组织,属于例子级别的验证(文件是否存在、内容是否包含特定条目)。可提取为通用属性的集中在审计一览表生成脚本的解析、分类和排序逻辑。
### Property 1审计记录解析-渲染完整性
*For any* 格式合规的审计源记录 Markdown 文件(包含一级标题、日期行、修改文件清单章节),解析后生成的表格行应包含:日期、需求摘要、变更类型、影响模块和源文件链接。
**Validates: Requirements 2.1, 2.2**
### Property 2文件路径模块分类正确性
*For any* 文件路径字符串模块分类函数的返回值应属于预定义的模块名称集合API 层、ODS 层、DWD 层、DWS 层、指数算法、数据装载、数据库、调度、配置、CLI、模型、SCD2、文档、脚本工具、测试、质量校验、GUI、工具库、其他
**Validates: Requirements 2.3**
### Property 3审计条目时间倒序排列
*For any* 一组审计条目列表,经过排序函数处理后,输出列表中每个条目的日期应大于等于其后续条目的日期(严格非递增序)。
**Validates: Requirements 2.4**
## 错误处理
### 审计记录解析容错
| 场景 | 处理方式 |
|------|----------|
| 审计文件缺少一级标题 | 使用文件名 slug 作为标题替代 |
| 审计文件缺少"修改文件清单"章节 | `changed_files` 返回空列表,`modules` 标记为 `{"其他"}` |
| 审计文件名不符合 `YYYY-MM-DD__slug.md` 格式 | 跳过该文件,输出警告日志 |
| 审计文件编码非 UTF-8 | 尝试 UTF-8 解码,失败则跳过并警告 |
| `docs/audit/changes/` 目录为空 | 生成空的 dashboard 文件,包含"暂无审计记录"提示 |
### 文件迁移容错
| 场景 | 处理方式 |
|------|----------|
| `index_algorithm_cn.md` 源文件不存在 | 脚本报错退出,提示文件路径 |
| 目标目录 `docs/business-rules/` 已存在同名文件 | 提示用户确认是否覆盖 |
## 测试策略
### 测试框架
- 单元测试:`pytest`
- 属性测试:`hypothesis`Python 属性测试库)
- 测试目录:`tests/unit/test_gen_audit_dashboard.py`
### 属性测试
每个正确性属性对应一个 `hypothesis` 属性测试,最少运行 100 次迭代:
- **Property 1**:生成随机的审计 Markdown 内容(包含标题、日期、文件清单),验证解析+渲染后的表格行包含所有必要字段
- Tag: `Feature: docs-optimization, Property 1: 审计记录解析-渲染完整性`
- **Property 2**:生成随机的文件路径字符串,验证分类结果属于预定义模块集合
- Tag: `Feature: docs-optimization, Property 2: 文件路径模块分类正确性`
- **Property 3**:生成随机的日期列表构造审计条目,验证排序后严格非递增
- Tag: `Feature: docs-optimization, Property 3: 审计条目时间倒序排列`
### 单元测试
- 解析真实审计记录文件的具体例子(使用项目中已有的审计文件作为测试输入)
- 边界情况:空目录、格式异常文件、缺少章节的文件
- 文件存在性检查:验证所有新增目录和文件已创建
- 文档总索引完整性:验证 `docs/README.md` 包含所有一级目录条目
- 重定向文件验证:验证原 `index_algorithm_cn.md` 位置包含重定向说明

View File

@@ -0,0 +1,56 @@
# 需求文档:文档体系整理与优化
## 简介
对飞球 ETL 系统etl-billiards`docs/` 目录进行文档体系整理与优化,涵盖三个核心诉求:文档覆盖度评估与缺失类别补充、审计一览表生成、业务规则文档独立目录建设。目标是让项目文档从宏观架构到微观实现形成完整闭环,同时提供可快速检索的审计变更视图。
## 术语表
- **文档总索引Docs_Index**`docs/README.md`,项目文档的统一入口与导航页
- **审计一览表Audit_Dashboard**:基于 `docs/audit/changes/` 源数据生成的汇总视图文件
- **审计源记录Audit_Record**`docs/audit/changes/` 目录下的单个审计 Markdown 文件
- **业务规则文档目录Business_Rules_Dir**`docs/business-rules/`存放指数算法、DWS 口径定义、SCD2 规则等业务逻辑文档
- **架构设计文档目录Architecture_Dir**`docs/architecture/`,存放系统整体架构、数据流、模块交互等设计文档
- **运维文档目录Operations_Dir**`docs/operations/`,存放环境搭建、调度配置、故障排查等运维指南
- **变更日志Changelog**`docs/CHANGELOG.md`,项目级版本变更历史记录
- **指数算法文档Index_Algorithm_Doc**:当前位于 `docs/database/DWS/index_algorithm_cn.md` 的指数算法说明文件
## 需求
### 需求 1文档覆盖度评估与缺失类别补充
**用户故事:** 作为项目开发者,我希望文档体系涵盖从宏观架构到微观实现的完整说明,以便新成员快速上手、现有成员高效查阅。
#### 验收标准
1. THE Docs_Index SHALL 包含指向所有一级文档目录的链接和简要说明
2. WHEN 新增文档目录时THE Docs_Index SHALL 同步更新对应的目录条目和说明
3. THE Architecture_Dir SHALL 包含系统整体架构文档涵盖数据流向图ODS→DWD→DWS、模块交互关系和技术栈说明
4. THE Business_Rules_Dir SHALL 包含独立的业务规则与算法文档目录,与数据库表结构文档分离
5. THE Operations_Dir SHALL 包含环境搭建指南、调度配置说明和常见故障排查手册
6. THE Changelog SHALL 记录项目级版本变更历史,包含日期、变更摘要和影响范围
### 需求 2审计一览表生成
**用户故事:** 作为项目管理者,我希望基于审计源记录生成一个汇总视图,以便一眼了解项目的修改痕迹并按功能模块检索。
#### 验收标准
1. THE Audit_Dashboard SHALL 从 Audit_Record 文件中提取日期、需求摘要、修改内容和影响范围信息
2. THE Audit_Dashboard SHALL 以表格形式展示每条审计记录的时间日期、用户需求摘要、修改/新增/删除的内容概要和对项目的影响
3. THE Audit_Dashboard SHALL 提供按功能模块分类的索引(如 API 层、ODS 层、DWD 层、DWS 层、文档、基础设施)
4. THE Audit_Dashboard SHALL 提供按时间倒序排列的完整变更列表
5. WHEN 新的 Audit_Record 被添加到 `docs/audit/changes/`THE Audit_Dashboard SHALL 通过手动重新生成的方式保持同步
6. THE Audit_Dashboard SHALL 存放于 `docs/audit/` 目录下,文件名为 `audit_dashboard.md`
### 需求 3业务规则文档独立目录
**用户故事:** 作为项目开发者,我希望业务规则和算法文档有独立的存放目录,以便与数据库表结构文档清晰分离,方便查阅和维护。
#### 验收标准
1. THE Business_Rules_Dir SHALL 作为独立目录存在于 `docs/business-rules/` 路径下
2. WHEN Index_Algorithm_Doc 从 `docs/database/DWS/` 迁移至 Business_Rules_Dir 时THE 原路径 SHALL 保留一个指向新位置的重定向说明
3. THE Business_Rules_Dir SHALL 包含目录级 README 文件,列出所有业务规则文档的索引
4. THE Business_Rules_Dir SHALL 按业务域组织文档如指数算法、DWS 口径定义、SCD2 规则、薪酬计算规则)
5. THE Docs_Index SHALL 包含 Business_Rules_Dir 的条目和说明

View File

@@ -0,0 +1,100 @@
# 实施计划:文档体系整理与优化
## 概述
基于设计文档,将实施分为四个阶段:新增文档目录与骨架文件、业务规则文档迁移、审计一览表生成脚本开发、文档总索引更新。所有任务聚焦于文件创建/修改和 Python 脚本编写。
## 任务
- [x] 1. 新增文档目录与骨架文件
- [x] 1.1 创建 `docs/architecture/` 目录及文档
- 创建 `docs/architecture/README.md`(目录索引)
- 创建 `docs/architecture/system_overview.md`(系统整体架构:数据流向图、模块交互、技术栈)
- 创建 `docs/architecture/data_flow.md`ODS→DWD→DWS 数据流向详解)
- 从根 `README.md``.kiro/steering/` 中提取架构信息填充内容
- _Requirements: 1.3_
- [x] 1.2 创建 `docs/operations/` 目录及文档
- 创建 `docs/operations/README.md`(目录索引)
- 创建 `docs/operations/environment_setup.md`环境搭建指南Python、PostgreSQL、依赖安装
- 创建 `docs/operations/scheduling.md`调度配置说明CLI 参数、定时任务、管道模式)
- 创建 `docs/operations/troubleshooting.md`(故障排查手册:常见错误与解决方案)
- _Requirements: 1.5_
- [x] 1.3 创建 `docs/CHANGELOG.md`
- 基于 `docs/audit/changes/` 中的审计记录,整理项目级版本变更历史
- 包含日期、变更摘要和影响范围
- _Requirements: 1.6_
- [x] 2. 业务规则文档迁移与目录建设
- [x] 2.1 创建 `docs/business-rules/` 目录并迁移指数算法文档
- 创建 `docs/business-rules/README.md`(目录索引,按业务域列出文档)
-`docs/database/DWS/index_algorithm_cn.md` 内容复制到 `docs/business-rules/index_algorithm_cn.md`
- 将原 `docs/database/DWS/index_algorithm_cn.md` 替换为重定向说明
- _Requirements: 3.1, 3.2, 3.3, 3.4_
- [x] 2.2 创建业务规则骨架文档
- 创建 `docs/business-rules/dws_metrics.md`DWS 口径定义骨架)
- 创建 `docs/business-rules/scd2_rules.md`SCD2 处理规则骨架)
- _Requirements: 3.4_
- [x] 3. 检查点 — 确认文档目录结构正确
- 确认所有新增目录和文件已创建,如有问题请提出。
- [x] 4. 审计一览表生成脚本
- [x] 4.1 实现审计记录解析模块
-`scripts/gen_audit_dashboard.py` 中实现 `AuditEntry` 数据类
- 实现 `parse_audit_file(filepath)` 函数:从文件名提取日期/slug从内容提取标题/修改文件/风险等级
- 实现 `classify_module(filepath)` 函数:根据 MODULE_MAP 将文件路径映射到功能模块
- 实现 `scan_audit_dir(dirpath)` 函数:扫描目录并返回 AuditEntry 列表
- _Requirements: 2.1, 2.3_
- [x] 4.2 编写属性测试:审计记录解析-渲染完整性
- **Property 1: 审计记录解析-渲染完整性**
- 使用 hypothesis 生成随机审计 Markdown 内容,验证解析+渲染后表格行包含所有必要字段
- **Validates: Requirements 2.1, 2.2**
- [x] 4.3 编写属性测试:文件路径模块分类正确性
- **Property 2: 文件路径模块分类正确性**
- 使用 hypothesis 生成随机文件路径,验证分类结果属于预定义模块集合
- **Validates: Requirements 2.3**
- [x] 4.4 实现审计一览表渲染模块
- 实现 `render_timeline_table(entries)` 函数:按时间倒序生成 Markdown 表格
- 实现 `render_module_index(entries)` 函数:按模块分组生成 Markdown 章节
- 实现 `render_dashboard(entries)` 函数:组合时间线和模块索引生成完整 dashboard
- _Requirements: 2.2, 2.3, 2.4_
- [x] 4.5 编写属性测试:审计条目时间倒序排列
- **Property 3: 审计条目时间倒序排列**
- 使用 hypothesis 生成随机日期列表,验证排序后严格非递增
- **Validates: Requirements 2.4**
- [x] 4.6 编写单元测试
- 使用真实审计文件作为测试输入验证解析正确性
- 测试边界情况:空目录、格式异常文件、缺少章节的文件
- _Requirements: 2.1, 2.3_
- [x] 4.7 实现主入口并生成 audit_dashboard.md
- 实现 `main()` 函数:扫描 → 解析 → 渲染 → 写入 `docs/audit/audit_dashboard.md`
- 运行脚本生成实际的 audit_dashboard.md 文件
- _Requirements: 2.5, 2.6_
- [x] 5. 更新文档总索引
- [x] 5.1 更新 `docs/README.md`
- 添加 `architecture/``business-rules/``operations/` 三个新目录的条目和说明
- 添加 `CHANGELOG.md` 条目
- 添加 `audit/audit_dashboard.md` 条目
- 移除过时条目(如 `data_exports/``templates/``test-json-doc/` 如果不存在)
- 确保所有一级目录都有对应链接
- _Requirements: 1.1, 1.2, 3.5_
- [x] 6. 最终检查点 — 确认所有文件完整
- 确认所有测试通过,所有文档文件已创建,审计一览表已生成,如有问题请提出。
## 备注
- 标记 `*` 的任务为可选测试任务,可跳过以加速 MVP
- 每个任务引用了具体的需求编号以便追溯
- 检查点用于增量验证
- 属性测试验证通用正确性属性,单元测试验证具体例子和边界情况

View File

@@ -0,0 +1 @@
{"generationMode": "requirements-first"}

View File

@@ -0,0 +1,285 @@
# 设计文档ETL 任务说明文档
## 概述
本设计描述如何为飞球 ETL 系统生成一套完整的任务说明文档,放置于 `docs/etl_tasks/` 目录下。文档以 Markdown 格式编写按数据层ODS / DWD / DWS / INDEX / Utility分文件组织并提供一个总览 README 作为入口。
文档的目标读者是开发者和运维人员,需要覆盖:
- 每个任务的代码标识、Python 类、数据来源与目标
- Extract / Transform / Load 各阶段的处理逻辑
- CLI 参数与管道执行方式
- BaseTask 公共机制
## 架构
文档为纯静态 Markdown 文件,不涉及运行时代码变更。整体结构如下:
```
docs/etl_tasks/
├── README.md # 总览:任务清单 + 跳转链接 + 执行方式
├── ods_tasks.md # ODS 层任务详解
├── dwd_tasks.md # DWD 层任务详解
├── dws_tasks.md # DWS 层任务详解
├── index_tasks.md # INDEX 层任务详解
├── utility_tasks.md # 工具类任务详解
└── base_task_mechanism.md # BaseTask 公共机制与执行参数
```
```mermaid
graph TD
README["README.md<br/>总览入口"] --> ODS["ods_tasks.md"]
README --> DWD["dwd_tasks.md"]
README --> DWS["dws_tasks.md"]
README --> IDX["index_tasks.md"]
README --> UTL["utility_tasks.md"]
README --> BASE["base_task_mechanism.md"]
```
## 组件与接口
本 spec 不涉及代码组件。产出物为 7 个 Markdown 文件,各文件的内容范围如下:
### README.md总览
| 章节 | 内容 |
|------|------|
| 系统简介 | 飞球 ETL 系统概述、数据流向API → ODS → DWD → DWS |
| 任务清单 | 按层分组的表格任务代码、Python 类、简要说明、跳转链接 |
| 管道类型 | 7 种管道api_ods / api_ods_dwd / api_full / ods_dwd / dwd_dws / dwd_dws_index / dwd_index的层组合 |
| 处理模式 | increment_only / verify_only / increment_verify 的区别 |
| 数据源模式 | online / offline / hybrid 的区别 |
| CLI 参数速查 | 所有 CLI 参数的表格(参数名、类型、默认值、说明) |
| 常见命令示例 | 典型使用场景的命令行示例 |
### ods_tasks.mdODS 层)
每个 ODS 任务一个小节,包含:
- 任务代码与 Python 类
- API 端点与请求参数
- 字段解析逻辑transform 阶段)
- 目标 ODS 表与写入策略
- 特殊说明如分页、去重、content_hash 等)
需区分两种 ODS 任务模式:
1. 独立任务类(如 OrdersTask、MembersTask继承 BaseTask有独立的 E/T/L 实现
2. 通用 ODS 任务(由 `ods_tasks.py` 中 OdsTaskSpec + BaseOdsTask 动态生成):通过声明式配置定义端点、列映射等
已注册的 ODS 任务14 个独立 + N 个通用):
| 任务代码 | Python 类 | API 端点 | 目标表 |
|----------|-----------|----------|--------|
| ORDERS | OrdersTask | /Site/GetAllOrderSettleList | billiards_ods.fact_order |
| PAYMENTS | PaymentsTask | 支付相关端点 | billiards_ods.fact_payment |
| MEMBERS | MembersTask | /MemberProfile/GetTenantMemberList | billiards_ods.dim_member |
| PRODUCTS | ProductsTask | 商品相关端点 | billiards_ods 商品表 |
| TABLES | TablesTask | 台桌相关端点 | billiards_ods 台桌表 |
| ASSISTANTS | AssistantsTask | 助教相关端点 | billiards_ods 助教表 |
| PACKAGES_DEF | PackagesDefTask | 套餐相关端点 | billiards_ods 套餐表 |
| REFUNDS | RefundsTask | 退款相关端点 | billiards_ods 退款表 |
| COUPON_USAGE | CouponUsageTask | 优惠券相关端点 | billiards_ods 优惠券表 |
| INVENTORY_CHANGE | InventoryChangeTask | 库存变动端点 | billiards_ods 库存表 |
| TOPUPS | TopupsTask | 充值相关端点 | billiards_ods 充值表 |
| TABLE_DISCOUNT | TableDiscountTask | 台费折扣端点 | billiards_ods 折扣表 |
| ASSISTANT_ABOLISH | AssistantAbolishTask | 助教取消端点 | billiards_ods 取消表 |
| LEDGER | LedgerTask | 台账端点 | billiards_ods 台账表 |
通用 ODS 任务由 `ODS_TASK_CLASSES` 字典动态注册,每个任务通过 `OdsTaskSpec` 声明:
- `endpoint`API 端点路径
- `table`:目标 ODS 表名
- `columns`列定义列表ColumnSpec
- `page_size``data_path``list_key`:分页参数
- `pk_columns`:主键列
- `snapshot_mode`快照模式content_hash 去重)
### dwd_tasks.mdDWD 层)
DWD 层有 5 个已注册任务:
| 任务代码 | Python 类 | 说明 |
|----------|-----------|------|
| DWD_LOAD_FROM_ODS | DwdLoadTask | 核心装载任务:遍历 TABLE_MAP维度走 SCD2事实走增量 |
| TICKET_DWD | TicketDwdTask | 结账小票明细 → fact_order / fact_order_goods / fact_table_usage / fact_assistant_service |
| PAYMENTS_DWD | PaymentsDwdTask | ODS 支付记录 → fact_payment |
| MEMBERS_DWD | MembersDwdTask | ODS 会员记录 → dim_member |
| DWD_QUALITY_CHECK | DwdQualityTask | ODS 与 DWD 行数/金额核对,输出 JSON 报表 |
核心任务 DWD_LOAD_FROM_ODS 的处理逻辑:
- TABLE_MAP 定义了 40+ 对 DWD→ODS 表映射
- 维度表dim_*):检测 SCD2 列是否存在,有则执行 SCD2 合并(关闭旧版+插入新版),无则执行 Type1 Upsert
- 事实表dwd_*、fact_*):按 fetched_at 水位线增量插入,支持 upsert 或 insert-only
- FACT_MAPPINGS 定义了列名映射ODS 驼峰命名 → DWD 下划线命名)
- 每张表独立事务,单表失败不影响后续表
SCD2 处理流程:
1. 从 ODS 取最新快照DISTINCT ON 按业务主键 + fetched_at DESC
2. 与 DWD 当前版本scd2_is_current=1逐列对比
3. 有变更关闭旧版scd2_end_time=now, scd2_is_current=0+ 插入新版version+1
4. 无变更:跳过
### dws_tasks.mdDWS 层)
DWS 层有 15 个已注册任务,按业务域分组:
**助教业绩域:**
| 任务代码 | Python 类 | 目标表 | 粒度 |
|----------|-----------|--------|------|
| DWS_ASSISTANT_DAILY | AssistantDailyTask | dws_assistant_daily_detail | 日期+助教 |
| DWS_ASSISTANT_MONTHLY | AssistantMonthlyTask | dws_assistant_monthly_summary | 月份+助教 |
| DWS_ASSISTANT_CUSTOMER | AssistantCustomerTask | dws_assistant_customer_stats | 日期+助教+会员 |
| DWS_ASSISTANT_SALARY | AssistantSalaryTask | dws_assistant_salary_calc | 月份+助教 |
| DWS_ASSISTANT_FINANCE | AssistantFinanceTask | dws_assistant_finance_analysis | 日期+助教 |
**会员分析域:**
| 任务代码 | Python 类 | 目标表 | 粒度 |
|----------|-----------|--------|------|
| DWS_MEMBER_CONSUMPTION | MemberConsumptionTask | dws_member_consumption_summary | 日期+会员 |
| DWS_MEMBER_VISIT | MemberVisitTask | dws_member_visit_detail | 日期+会员+结账单 |
**财务统计域:**
| 任务代码 | Python 类 | 目标表 | 粒度 |
|----------|-----------|--------|------|
| DWS_FINANCE_DAILY | FinanceDailyTask | dws_finance_daily_summary | 日期 |
| DWS_FINANCE_RECHARGE | FinanceRechargeTask | dws_finance_recharge_summary | 日期 |
| DWS_FINANCE_INCOME_STRUCTURE | FinanceIncomeStructureTask | dws_finance_income_structure | 日期+收入类型 |
| DWS_FINANCE_DISCOUNT_DETAIL | FinanceDiscountDetailTask | dws_finance_discount_detail | 日期+折扣类型 |
**运维任务:**
| 任务代码 | Python 类 | 说明 |
|----------|-----------|------|
| DWS_BUILD_ORDER_SUMMARY | DwsBuildOrderSummaryTask | 构建订单汇总中间表 |
| DWS_RETENTION_CLEANUP | DwsRetentionCleanupTask | 按时间分层清理历史数据 |
| DWS_MV_REFRESH_FINANCE_DAILY | DwsMvRefreshFinanceDailyTask | 刷新财务日报物化视图 |
| DWS_MV_REFRESH_ASSISTANT_DAILY | DwsMvRefreshAssistantDailyTask | 刷新助教日报物化视图 |
所有 DWS 任务继承 BaseDwsTask共享以下机制
- 时间分层范围计算TimeLayer: LAST_2_DAYS / LAST_1_MONTH / LAST_3_MONTHS / LAST_6_MONTHS / ALL
- 配置缓存ConfigCache业绩档位、等级价格、奖金规则、区域分类、技能类型
- delete-before-insert 更新策略(按日期范围先删后插,保证幂等)
- bulk_insert / upsert 写入方法
### index_tasks.mdINDEX 层)
INDEX 层有 4 个已注册任务:
| 任务代码 | Python 类 | 目标表 | 指数类型 |
|----------|-----------|--------|----------|
| DWS_WINBACK_INDEX | WinbackIndexTask | dws_member_winback_index | WBI回流指数 |
| DWS_NEWCONV_INDEX | NewconvIndexTask | dws_member_newconv_index | NCI新客转化指数 |
| DWS_RELATION_INDEX | RelationIndexTask | dws_relation_index | RS关系指数 |
| DWS_ML_MANUAL_IMPORT | MlManualImportTask | dws_ml_manual_ledger | ML手动台账导入 |
所有指数任务继承 BaseIndexTask共享
- 参数从 `billiards_dws.cfg_index_parameters` 表加载
- 百分位历史记录PercentileHistory
- 标准化的指数计算流程
### utility_tasks.md工具类
| 任务代码 | Python 类 | 用途 |
|----------|-----------|------|
| INIT_ODS_SCHEMA | InitOdsSchemaTask | 执行 ODS + etl_admin DDL创建必要目录 |
| INIT_DWD_SCHEMA | InitDwdSchemaTask | 执行 DWD DDL |
| INIT_DWS_SCHEMA | InitDwsSchemaTask | 执行 DWS DDL |
| MANUAL_INGEST | ManualIngestTask | 从本地 JSON 文件手动入库到 ODS |
| ODS_JSON_ARCHIVE | OdsJsonArchiveTask | 归档 ODS JSON 文件 |
| CHECK_CUTOFF | CheckCutoffTask | 检查数据截止时间 |
| SEED_DWS_CONFIG | SeedDwsConfigTask | 初始化 DWS 配置种子数据 |
| DATA_INTEGRITY_CHECK | DataIntegrityTask | 数据完整性校验 |
### base_task_mechanism.md公共机制
覆盖内容:
- BaseTask 模板方法流程execute → build_context → [分段] → extract → transform → load → commit
- TaskContext 字段说明
- 时间窗口计算逻辑(优先级:手动覆盖 > 游标 > 闲忙时段默认值)
- 窗口分段build_window_segments
- TaskRegistry 注册方式与元数据TaskMeta: task_class, requires_db_config, layer, task_type
- PipelineRunner 管道执行流程
- 校验框架Verifier概述
## 数据模型
本 spec 不涉及数据模型变更。文档中引用的数据模型均为现有系统中的表结构,包括:
**ODS 层表**billiards_ods schema
- settlement_records, table_fee_transactions, assistant_service_records, member_profiles, payment_transactions, refund_transactions 等 20+ 表
**DWD 层表**billiards_dwd schema
- 维度表dim_site, dim_table, dim_assistant, dim_member, dim_member_card_account, dim_tenant_goods, dim_store_goods, dim_goods_category, dim_groupbuy_package各含 _ex 扩展表)
- 事实表dwd_settlement_head, dwd_table_fee_log, dwd_table_fee_adjust, dwd_store_goods_sale, dwd_assistant_service_log, dwd_assistant_trash_event, dwd_member_balance_change, dwd_groupbuy_redemption, dwd_platform_coupon_redemption, dwd_recharge_order, dwd_payment, dwd_refund各含 _ex 扩展表)
**DWS 层表**billiards_dws schema
- 助教域dws_assistant_daily_detail, dws_assistant_monthly_summary, dws_assistant_customer_stats, dws_assistant_salary_calc, dws_assistant_finance_analysis
- 会员域dws_member_consumption_summary, dws_member_visit_detail
- 财务域dws_finance_daily_summary, dws_finance_recharge_summary, dws_finance_income_structure, dws_finance_discount_detail
- 指数域dws_member_winback_index, dws_member_newconv_index, dws_relation_index, dws_ml_manual_ledger
- 配置表cfg_index_parameters, cfg_skill_type, cfg_performance_tier, cfg_level_price, cfg_bonus_rule, cfg_area_category
## 正确性属性
*正确性属性是一种在系统所有有效执行中都应成立的特征或行为——本质上是关于系统应该做什么的形式化陈述。属性是人类可读规范与机器可验证正确性保证之间的桥梁。*
由于本 spec 的产出物是文档Markdown 文件),而非运行时代码,正确性属性主要关注文档的完整性和一致性——即文档是否覆盖了所有已注册任务。
Property 1: ODS 任务文档覆盖完整性
*对于所有*在 `task_registry.py` 中注册且 layer="ODS" 的任务代码,`ods_tasks.md` 中应包含该任务代码的说明章节,并列出其目标表。
**Validates: Requirements 2.1, 2.4**
Property 2: DWD 任务文档覆盖完整性
*对于所有*在 `task_registry.py` 中注册且 layer="DWD" 的任务代码,`dwd_tasks.md` 中应包含该任务代码的说明章节,并列出其源表和目标表。
**Validates: Requirements 3.1**
Property 3: DWS 任务文档覆盖完整性
*对于所有*在 `task_registry.py` 中注册且 layer="DWS" 的任务代码,`dws_tasks.md` 中应包含该任务代码的说明章节,并标注其更新策略。
**Validates: Requirements 4.1, 4.4**
Property 4: INDEX 任务文档覆盖完整性
*对于所有*在 `task_registry.py` 中注册且 layer="INDEX" 的任务代码,`index_tasks.md` 中应包含该任务代码的说明章节。
**Validates: Requirements 5.1**
Property 5: Utility 任务文档覆盖完整性
*对于所有*在 `task_registry.py` 中注册且 task_type="utility" 的任务代码,`utility_tasks.md` 中应包含该任务代码的说明章节。
**Validates: Requirements 6.1**
Property 6: CLI 参数文档覆盖完整性
*对于所有*在 `cli/main.py``parse_args()` 中定义的 CLI 参数,`README.md``base_task_mechanism.md` 中应包含该参数的说明。
**Validates: Requirements 7.1**
Property 7: 管道类型文档覆盖完整性
*对于所有*在 `PipelineRunner.PIPELINE_LAYERS` 中定义的管道类型,`README.md` 中应包含该管道类型的层组合说明。
**Validates: Requirements 7.2**
## 错误处理
本 spec 为文档生成任务,不涉及运行时错误处理。文档编写过程中需注意:
1. 若源代码中的任务类或注册信息发生变更,文档可能过时——应在 README.md 中注明"最后更新日期"和"基于代码版本"
2. 若某个任务的 API 端点或参数无法从代码中直接读取(如动态配置),应在文档中标注"参见配置文件"
## 测试策略
**单元测试(示例验证):**
- 验证所有 7 个 Markdown 文件存在于 `docs/etl_tasks/` 目录下
- 验证 README.md 包含指向其他 6 个文件的链接
- 验证每个分层文件中包含对应层的所有已注册任务代码
**属性测试(覆盖完整性验证):**
- 使用 pytest 编写脚本,从 `task_registry.py` 动态读取已注册任务列表
- 解析对应的 Markdown 文件,检查每个任务代码是否出现在文档中
-`cli/main.py` 解析 CLI 参数列表,检查文档中是否覆盖
- 属性测试库pytest本项目已使用配合 parametrize 实现参数化验证
- 每个属性测试标注对应的设计属性编号
**测试标注格式:**
```python
# Feature: etl-task-documentation, Property 1: ODS 任务文档覆盖完整性
def test_ods_task_coverage():
...
```
由于本 spec 的产出物是静态文档,属性测试的核心价值在于确保文档与代码的一致性,防止文档遗漏任务。测试应在文档生成后运行一次即可,无需持续集成。

View File

@@ -0,0 +1,119 @@
# 需求文档ETL 任务说明文档
## 简介
为飞球 ETL 系统etl-billiards生成一份完整的任务说明文档覆盖 ODS、DWD、DWS、INDEX 四层所有已注册任务的逻辑、执行方式、参数含义及处理流程。文档面向开发者和运维人员,放置于 `docs/etl_tasks/` 目录下。
## 术语表
- **ETL_System**:飞球 ETL 系统,负责从上游 API 抽取数据并经 ODS → DWD → DWS 三层处理
- **Task_Document**:本次生成的 ETL 任务说明文档
- **ODS**操作数据存储层Operational Data Store保留 API 原始 payload
- **DWD**明细数据层Data Warehouse Detail清洗后的维度表和事实表
- **DWS**数据服务层Data Warehouse Service汇总统计表
- **INDEX**:指数算法层,基于 DWS 数据计算自定义业务指数
- **BaseTask**:所有 ETL 任务的基类,提供 Extract → Transform → Load 模板方法
- **TaskRegistry**:任务注册表,维护任务代码与任务类的映射关系
- **TaskContext**:运行期上下文,包含 store_id、时间窗口等信息
- **Pipeline**:管道,定义多层任务的执行顺序(如 api_ods、api_full、dwd_dws 等)
- **Loader**加载器负责将转换后的数据写入目标表upsert/insert
## 需求
### 需求 1文档结构与组织
**用户故事:** 作为开发者,我希望文档按数据层分章节组织,以便快速定位特定层的任务说明。
#### 验收标准
1. THE Task_Document SHALL 包含一个总览文件(`README.md`),列出所有层及其任务清单,并提供跳转链接
2. THE Task_Document SHALL 按 ODS、DWD、DWS、INDEX、Utility 五个分类分别生成独立的 Markdown 文件
3. THE Task_Document SHALL 放置于 `docs/etl_tasks/` 目录下
4. WHEN 新增或删除任务时THE Task_Document SHALL 通过总览文件的任务清单反映当前已注册任务的完整列表
### 需求 2ODS 层任务说明
**用户故事:** 作为开发者,我希望了解每个 ODS 任务的 API 端点、参数、解析逻辑和目标表,以便排查数据抓取问题。
#### 验收标准
1. THE Task_Document SHALL 为每个 ODS 任务列出任务代码、对应的 Python 类、源 API 端点
2. THE Task_Document SHALL 说明每个 ODS 任务的 extract 阶段调用的 API 参数及其含义
3. THE Task_Document SHALL 说明每个 ODS 任务的 transform 阶段的字段解析和类型转换逻辑
4. THE Task_Document SHALL 说明每个 ODS 任务的 load 阶段的目标表名和写入策略upsert/insert
5. THE Task_Document SHALL 区分"独立 ODS 任务"(如 OrdersTask和"通用 ODS 任务"(由 ODS_TASK_CLASSES 动态生成)两种模式
6. THE Task_Document SHALL 说明通用 ODS 任务的 OdsTaskSpec 配置结构(端点、表名、列映射、分页参数等)
### 需求 3DWD 层任务说明
**用户故事:** 作为开发者,我希望了解 DWD 层任务如何从 ODS 读取数据并清洗装载到维度表和事实表,以便理解数据血缘。
#### 验收标准
1. THE Task_Document SHALL 为每个 DWD 任务列出任务代码、Python 类、源 ODS 表和目标 DWD 表
2. THE Task_Document SHALL 说明 DWD_LOAD_FROM_ODS 任务的 TABLE_MAP 映射关系及维度/事实分流逻辑
3. THE Task_Document SHALL 说明维度表的 SCD2 处理方式(生效区间、变更检测、历史版本管理)
4. THE Task_Document SHALL 说明事实表的增量装载方式(水位线、去重、冲突处理)
5. THE Task_Document SHALL 说明 DWD_QUALITY_CHECK 任务的行数/金额核对逻辑和报表输出格式
6. THE Task_Document SHALL 说明 TICKET_DWD、PAYMENTS_DWD、MEMBERS_DWD 三个独立 DWD 任务各自的处理特点
### 需求 4DWS 层任务说明
**用户故事:** 作为开发者,我希望了解 DWS 层每个汇总任务的业务含义、数据来源和计算规则,以便验证业务报表的正确性。
#### 验收标准
1. THE Task_Document SHALL 为每个 DWS 任务列出任务代码、Python 类、目标表、主键和统计粒度
2. THE Task_Document SHALL 说明每个 DWS 任务的数据来源表DWD 层的哪些表)
3. THE Task_Document SHALL 说明每个 DWS 任务的核心业务计算规则(如工资计算公式、业绩档位、排名逻辑等)
4. THE Task_Document SHALL 说明每个 DWS 任务的更新策略delete-before-insert 或 upsert
5. THE Task_Document SHALL 说明物化视图刷新任务MV_REFRESH的分层刷新机制和配置方式
6. THE Task_Document SHALL 说明数据保留清理任务RETENTION_CLEANUP的时间分层策略和配置参数
### 需求 5INDEX 层任务说明
**用户故事:** 作为开发者,我希望了解指数算法任务的计算逻辑和参数含义,以便调优指数模型。
#### 验收标准
1. THE Task_Document SHALL 为每个 INDEX 任务列出任务代码、Python 类、目标表和指数类型
2. THE Task_Document SHALL 说明每个指数的计算公式或算法概要WBI/NCI/RS/ML
3. THE Task_Document SHALL 说明指数参数的配置来源cfg_index_parameters 表)和参数含义
4. THE Task_Document SHALL 说明 ML_MANUAL_IMPORT 任务的 Excel 导入逻辑和模板格式
### 需求 6工具类任务说明
**用户故事:** 作为运维人员,我希望了解 Schema 初始化、手动入库等工具类任务的用途和使用方式。
#### 验收标准
1. THE Task_Document SHALL 为每个工具类任务列出任务代码、Python 类和用途说明
2. THE Task_Document SHALL 说明 INIT_ODS_SCHEMA、INIT_DWD_SCHEMA、INIT_DWS_SCHEMA 三个初始化任务执行的 DDL 文件和创建的目录
3. THE Task_Document SHALL 说明 MANUAL_INGEST 任务的文件匹配规则、JSON 解析逻辑和入库流程
4. THE Task_Document SHALL 说明 ODS_JSON_ARCHIVE 任务的归档策略
5. THE Task_Document SHALL 说明 CHECK_CUTOFF 和 DATA_INTEGRITY_CHECK 任务的校验逻辑
### 需求 7执行方式与参数说明
**用户故事:** 作为运维人员,我希望了解如何通过 CLI 和管道模式执行任务,以及各参数的含义。
#### 验收标准
1. THE Task_Document SHALL 说明 CLI 入口(`python -m cli.main`)的所有参数及其含义
2. THE Task_Document SHALL 说明管道类型api_ods、api_ods_dwd、api_full、ods_dwd、dwd_dws、dwd_dws_index、dwd_index各自包含的层和执行顺序
3. THE Task_Document SHALL 说明处理模式increment_only、verify_only、increment_verify的区别和适用场景
4. THE Task_Document SHALL 说明时间窗口参数window-start、window-end、window-split、lookback-hours、overlap-seconds的计算逻辑
5. THE Task_Document SHALL 说明数据源模式online、offline、hybrid的区别
6. THE Task_Document SHALL 提供常见使用场景的命令示例
### 需求 8BaseTask 与公共机制说明
**用户故事:** 作为开发者,我希望了解任务基类的模板方法和公共机制,以便开发新任务时遵循统一模式。
#### 验收标准
1. THE Task_Document SHALL 说明 BaseTask 的 Execute → Extract → Transform → Load 模板方法流程
2. THE Task_Document SHALL 说明 TaskContext 的字段含义store_id、window_start、window_end、window_minutes、cursor
3. THE Task_Document SHALL 说明时间窗口的计算逻辑(游标优先、闲忙时段、手动覆盖)
4. THE Task_Document SHALL 说明窗口分段build_window_segments的切分策略
5. THE Task_Document SHALL 说明任务注册表TaskRegistry的注册方式和元数据结构layer、task_type、requires_db_config

View File

@@ -0,0 +1,125 @@
# 实施计划ETL 任务说明文档
## 概述
基于对 `tasks/``loaders/``orchestration/``cli/` 目录下源代码的分析,生成 7 个 Markdown 文档文件,放置于 `docs/etl_tasks/` 目录下。每个任务按照设计文档中定义的结构和内容范围编写。
## 任务
- [x] 1. 创建 `docs/etl_tasks/base_task_mechanism.md`
- 说明 BaseTask 的 execute → extract → transform → load 模板方法流程
- 说明 TaskContext 字段含义store_id、window_start、window_end、window_minutes、cursor
- 说明时间窗口计算逻辑(手动覆盖 > 游标 > 闲忙时段默认值)
- 说明窗口分段build_window_segments切分策略
- 说明 TaskRegistry 注册方式与 TaskMeta 元数据结构layer、task_type、requires_db_config
- 说明 PipelineRunner 管道执行流程与校验框架概述
- _Requirements: 8.1, 8.2, 8.3, 8.4, 8.5_
- [x] 2. 创建 `docs/etl_tasks/ods_tasks.md`
- [x] 2.1 编写独立 ODS 任务说明14 个任务ORDERS、PAYMENTS、MEMBERS、PRODUCTS、TABLES、ASSISTANTS、PACKAGES_DEF、REFUNDS、COUPON_USAGE、INVENTORY_CHANGE、TOPUPS、TABLE_DISCOUNT、ASSISTANT_ABOLISH、LEDGER
- 每个任务列出任务代码、Python 类、API 端点、请求参数、字段解析逻辑、目标表、写入策略
- _Requirements: 2.1, 2.2, 2.3, 2.4_
- [x] 2.2 编写通用 ODS 任务说明BaseOdsTask + OdsTaskSpec 模式)
- 说明 OdsTaskSpec 配置结构endpoint、table、columns、pk_columns、snapshot_mode 等)
- 说明 BaseOdsTask 的通用 execute 流程API 调用、schema-aware 插入、content_hash 去重、软删除标记)
- 列出由 ODS_TASK_CLASSES 动态注册的所有任务
- _Requirements: 2.5, 2.6_
- [x] 3. 创建 `docs/etl_tasks/dwd_tasks.md`
- [x] 3.1 编写 DWD_LOAD_FROM_ODS 核心任务说明
- 列出完整的 TABLE_MAP 映射表DWD 表 → ODS 表)
- 说明维度/事实分流逻辑dim_* 走 SCD2 或 Type1 Upsert其余走增量插入
- 说明 SCD2 处理流程(最新快照选取、变更检测、版本关闭与新建)
- 说明事实表增量装载fetched_at 水位线、upsert/insert-only、FACT_MAPPINGS 列映射)
- _Requirements: 3.2, 3.3, 3.4_
- [x] 3.2 编写独立 DWD 任务说明TICKET_DWD、PAYMENTS_DWD、MEMBERS_DWD
- 每个任务列出:源 ODS 表、目标 DWD 表、处理特点
- _Requirements: 3.6_
- [x] 3.3 编写 DWD_QUALITY_CHECK 任务说明
- 说明行数/金额核对逻辑、金额列自动扫描规则、JSON 报表输出格式
- _Requirements: 3.5_
- [x] 4. 创建 `docs/etl_tasks/dws_tasks.md`
- [x] 4.1 编写 BaseDwsTask 公共机制说明
- 说明时间分层TimeLayer、配置缓存ConfigCache、delete-before-insert 策略、bulk_insert/upsert 方法
- _Requirements: 4.1_
- [x] 4.2 编写助教业绩域任务说明5 个任务)
- DWS_ASSISTANT_DAILY日度服务明细聚合数据来源、聚合维度、输出字段
- DWS_ASSISTANT_MONTHLY月度汇总业绩档位计算、排名逻辑、新人封顶规则
- DWS_ASSISTANT_CUSTOMER助教-客户关系统计
- DWS_ASSISTANT_SALARY工资计算公式基础工资+提成+奖金+扣款)
- DWS_ASSISTANT_FINANCE助教收支分析收入 vs 日均成本、毛利率)
- _Requirements: 4.2, 4.3, 4.4_
- [x] 4.3 编写会员分析域任务说明2 个任务)
- DWS_MEMBER_CONSUMPTION会员消费汇总、客户分层
- DWS_MEMBER_VISIT会员到店明细、服务时长、折扣计算
- _Requirements: 4.2, 4.3, 4.4_
- [x] 4.4 编写财务统计域任务说明4 个任务)
- DWS_FINANCE_DAILY财务日报结算汇总、团购、充值、赠卡消费、费用、平台
- DWS_FINANCE_RECHARGE充值统计首充/续充、现金/赠送、卡余额)
- DWS_FINANCE_INCOME_STRUCTURE收入结构分析按类型、按区域
- DWS_FINANCE_DISCOUNT_DETAIL折扣明细统计
- _Requirements: 4.2, 4.3, 4.4_
- [x] 4.5 编写运维任务说明4 个任务)
- DWS_BUILD_ORDER_SUMMARY订单汇总中间表构建
- DWS_RETENTION_CLEANUP时间分层清理策略、配置参数enabled、layer、tables、table_layers
- DWS_MV_REFRESH_FINANCE_DAILY / DWS_MV_REFRESH_ASSISTANT_DAILY物化视图分层刷新机制、L1-L4 层级、配置方式
- _Requirements: 4.5, 4.6_
- [x] 5. 创建 `docs/etl_tasks/index_tasks.md`
- 编写 BaseIndexTask 公共机制(参数加载、百分位历史)
- 编写 4 个指数任务说明WBI回流指数、NCI新客转化指数、RS关系指数、ML手动台账导入
- 说明 cfg_index_parameters 配置表结构和参数含义
- 说明 ML_MANUAL_IMPORT 的 Excel 模板格式和导入逻辑
- _Requirements: 5.1, 5.2, 5.3, 5.4_
- [x] 6. 创建 `docs/etl_tasks/utility_tasks.md`
- 编写 8 个工具类任务说明INIT_ODS_SCHEMA、INIT_DWD_SCHEMA、INIT_DWS_SCHEMA、MANUAL_INGEST、ODS_JSON_ARCHIVE、CHECK_CUTOFF、SEED_DWS_CONFIG、DATA_INTEGRITY_CHECK
- 每个任务列出:用途、执行的 DDL/操作、配置参数
- 重点说明 MANUAL_INGEST 的文件匹配规则和入库流程
- _Requirements: 6.1, 6.2, 6.3, 6.4, 6.5_
- [x] 7. 创建 `docs/etl_tasks/README.md`(总览)
- 编写系统简介和数据流向图API → ODS → DWD → DWS
- 编写按层分组的任务清单表格任务代码、Python 类、简要说明、跳转链接)
- 编写管道类型说明7 种管道的层组合)
- 编写处理模式说明increment_only / verify_only / increment_verify
- 编写数据源模式说明online / offline / hybrid
- 编写 CLI 参数速查表(所有参数的表格)
- 编写常见命令示例
- _Requirements: 1.1, 1.2, 1.3, 7.1, 7.2, 7.3, 7.5, 7.6_
- [x] 8. 检查点 - 验证文档完整性
- 确认 7 个文件全部存在于 `docs/etl_tasks/` 目录下
- 确认 README.md 中的任务清单覆盖所有已注册任务
- 确认各分层文件中的任务代码与 task_registry.py 一致
- Ensure all files are valid Markdown, ask the user if questions arise.
- [x] 9. 编写文档覆盖完整性验证脚本
- [x] 9.1 编写 ODS 任务覆盖验证
- **Property 1: ODS 任务文档覆盖完整性**
- **Validates: Requirements 2.1, 2.4**
- [x] 9.2 编写 DWD 任务覆盖验证
- **Property 2: DWD 任务文档覆盖完整性**
- **Validates: Requirements 3.1**
- [x] 9.3 编写 DWS 任务覆盖验证
- **Property 3: DWS 任务文档覆盖完整性**
- **Validates: Requirements 4.1, 4.4**
- [x] 9.4 编写 INDEX 和 Utility 任务覆盖验证
- **Property 4: INDEX 任务文档覆盖完整性**
- **Property 5: Utility 任务文档覆盖完整性**
- **Validates: Requirements 5.1, 6.1**
- [x] 9.5 编写 CLI 参数和管道类型覆盖验证
- **Property 6: CLI 参数文档覆盖完整性**
- **Property 7: 管道类型文档覆盖完整性**
- **Validates: Requirements 7.1, 7.2**
- [x] 10. 最终检查点
- Ensure all tests pass, ask the user if questions arise.
## 说明
- 任务标记 `*` 的为可选项,可跳过以加快 MVP 进度
- 每个任务引用了具体的需求编号以便追溯
- 检查点确保增量验证
- 文档编写顺序先写公共机制task 1再按层写各任务task 2-6最后写总览task 7

View File

@@ -0,0 +1 @@
{"generationMode": "requirements-first"}

View File

@@ -0,0 +1,553 @@
# 设计文档Monorepo 迁移
## 概述
本设计将现有单一 ETL 仓库(`FQ-ETL`)迁移为 Monorepo 单体仓库(`NeoZQYY`),采用一次性搬迁策略。核心设计原则:
1. **最小破坏性**ETL 整体平移,保持内部结构不变,仅调整外部引用
2. **分层隔离**:通过 uv workspace 实现 Python 包依赖隔离,通过 `.env` 分层实现配置隔离
3. **数据库重组**:从现有 4 个 schemabilliards_ods/billiards_dwd/billiards_dws/etl_admin重组为 6 层 schemameta/ods/dwd/core/dws/app
4. **渐进式扩展**:第一阶段只建必要骨架,未来扩展点记录在 Roadmap 中
## 架构
### 整体架构
```mermaid
graph TB
subgraph "NeoZQYY Monorepo"
subgraph "apps/"
ETL["apps/etl/pipelines/feiqiu/"]
Backend["apps/backend/ (FastAPI)"]
Mini["apps/miniprogram/ (Donut+TDesign)"]
Admin["apps/admin-web/ (未来)"]
end
subgraph "packages/"
Shared["packages/shared/"]
end
subgraph "gui/"
GUI["gui/ (PySide6过渡期)"]
end
subgraph "db/"
ETLDB["db/etl_feiqiu/"]
AppDB["db/zqyy_app/"]
FDW["db/fdw/"]
end
end
ETL --> Shared
Backend --> Shared
GUI --> Shared
Backend --> AppDB
ETL --> ETLDB
AppDB -.->|postgres_fdw 只读| ETLDB
```
### 数据流架构
```mermaid
graph LR
API["上游 SaaS API"] --> ODS["ods (原始数据)"]
ODS --> DWD["dwd (main+EX 明细)"]
DWD --> Core["core (统一最小字段集)"]
DWD --> DWS["dws (汇总/工资)"]
Core --> DWS
DWS --> App["app (视图+RLS)"]
App -.->|FDW 只读映射| ZqyyApp["zqyy_app DB"]
ZqyyApp --> FastAPI["FastAPI 后端"]
FastAPI --> MiniApp["微信小程序"]
subgraph "etl_feiqiu DB"
Meta["meta (调度/游标)"]
ODS
DWD
Core
DWS
App
end
```
## 组件与接口
### 1. 目录结构生成器Scaffold
负责创建 Monorepo 完整目录结构和基础配置文件。
**输入**:目标路径 `C:\NeoZQYY\`
**输出**:完整目录树 + README.md + 配置文件
**关键行为**
- 创建所有一级和二级目录
- 为每个一级目录生成 README.md作用 + 结构 + Roadmap
- 生成 `.gitignore``.kiroignore``.env.template`
- 初始化 Git 仓库
### 2. uv Workspace 配置
**根 `pyproject.toml`**
```toml
[project]
name = "neozqyy"
version = "0.1.0"
requires-python = ">=3.10"
[tool.uv.workspace]
members = [
"apps/etl/pipelines/feiqiu",
"apps/backend",
"packages/shared",
"gui",
]
```
**子项目 `pyproject.toml` 模式**(以 ETL 为例):
```toml
[project]
name = "etl-feiqiu"
version = "0.1.0"
requires-python = ">=3.10"
dependencies = [
"psycopg2-binary>=2.9.0",
"requests>=2.28.0",
"python-dateutil>=2.8.0",
"tzdata>=2023.0",
"python-dotenv",
"openpyxl>=3.1.0",
"neozqyy-shared",
]
[tool.uv.sources]
neozqyy-shared = { workspace = true }
```
### 3. 配置隔离机制
**分层加载顺序**
```
根 .env公共配置→ 应用 .env.local私有覆盖→ 环境变量 → CLI 参数
```
**实现方式**
- 现有 `AppConfig``DEFAULTS < ENV < CLI` 模式保持不变
- 新增:在 `load_env_overrides()` 中先加载根 `.env`,再加载应用级 `.env.local`
- 冲突策略:应用级优先(后加载覆盖先加载)
- 缺失检测:在 `_validate()` 中检查必需项,报告缺失项名称
### 4. ETL 平移策略
**平移范围**
| 源路径 | 目标路径 | 说明 |
|--------|----------|------|
| `api/` | `apps/etl/pipelines/feiqiu/api/` | API 客户端 |
| `cli/` | `apps/etl/pipelines/feiqiu/cli/` | CLI 入口 |
| `config/` | `apps/etl/pipelines/feiqiu/config/` | 配置 |
| `loaders/` | `apps/etl/pipelines/feiqiu/loaders/` | 加载器 |
| `models/` | `apps/etl/pipelines/feiqiu/models/` | 模型 |
| `orchestration/` | `apps/etl/pipelines/feiqiu/orchestration/` | 调度 |
| `scd/` | `apps/etl/pipelines/feiqiu/scd/` | SCD2 |
| `tasks/` | `apps/etl/pipelines/feiqiu/tasks/` | 任务 |
| `utils/` | `apps/etl/pipelines/feiqiu/utils/` | 工具 |
| `quality/` | `apps/etl/pipelines/feiqiu/quality/` | 质量检查 |
| `tests/` | `apps/etl/pipelines/feiqiu/tests/` | 测试 |
| `database/*.sql` | `db/etl_feiqiu/schemas/` | DDL |
| `database/migrations/` | `db/etl_feiqiu/migrations/` | 迁移脚本 |
| `database/seed_*.sql` | `db/etl_feiqiu/seeds/` | 种子数据 |
| `gui/` | `gui/` | GUI顶层 |
**import 路径策略**
- ETL 内部使用相对 import`from .config.settings import AppConfig`)或保持现有绝对 import
- `pyproject.toml` 中设置 `pythonpath`,使 `apps/etl/pipelines/feiqiu/` 为 Python 路径根
- `pytest.ini` 同步更新 `pythonpath = .`
- 目标ETL 内部代码零修改或最小修改
### 5. 小程序平移策略
**平移范围**
| 源路径 | 目标路径 |
|--------|----------|
| `C:\ZQYY\XCX\`(除 Prototype | `apps/miniprogram/` |
| `C:\ZQYY\XCX\Prototype\` | `docs/h5_ui/` |
小程序为独立前端项目Donut + TDesign不涉及 Python 依赖管理,直接复制即可。
### 6. 数据库 Schema 重组etl_feiqiu
**现有 → 新 schema 映射**
| 现有 Schema | 新 Schema | 说明 |
|-------------|-----------|------|
| `etl_admin` | `meta` | 调度、游标、运行记录 |
| `billiards_ods` | `ods` | ODS 原始数据,结构不变 |
| `billiards_dwd` | `dwd` | DWD 明细,保留 main+EX 拆分 |
| (新增) | `core` | 统一维度/事实最小字段集 |
| `billiards_dws` | `dws` | DWS 汇总,结构不变 |
| (新增) | `app` | 面向外部的视图/函数 + RLS |
**core schema 设计原则**
- 仅包含跨系统共享的最小字段集(如会员 ID、姓名、手机号、状态
- 维度表从 DWD 维度表提取核心字段
- 事实表从 DWD 事实表提取核心度量
- 第一版保持精简,后续按需扩展
**app schema 设计原则**
- 以视图VIEW封装 DWS/Core 层数据
- 所有视图启用 RLS`site_id` 过滤
- 提供函数接口供 FDW 映射使用
- 不存储实际数据,仅做访问层
**RLS 实现方案**
```sql
-- 创建应用角色
CREATE ROLE app_reader;
-- 在 app schema 的视图上启用 RLS
ALTER TABLE app.v_member_summary ENABLE ROW LEVEL SECURITY;
-- 创建策略:根据会话变量 app.current_site_id 过滤
CREATE POLICY site_isolation ON app.v_member_summary
FOR SELECT TO app_reader
USING (site_id = current_setting('app.current_site_id')::bigint);
```
### 7. 业务数据库设计zqyy_app
**核心表**
- `users`:用户账户(微信 OpenID、手机号、角色
- `roles` / `permissions`RBAC 权限模型
- `user_roles`:用户-角色关联
- `tasks`:任务管理(审批流)
- `approvals`:审批记录
**FDW 映射**
```sql
-- 在 zqyy_app 中创建外部服务器
CREATE SERVER etl_feiqiu_server
FOREIGN DATA WRAPPER postgres_fdw
OPTIONS (host 'localhost', dbname 'etl_feiqiu', port '5432');
-- 创建用户映射
CREATE USER MAPPING FOR app_user
SERVER etl_feiqiu_server
OPTIONS (user 'app_reader', password '***');
-- 导入 app schema 的外部表
IMPORT FOREIGN SCHEMA app
FROM SERVER etl_feiqiu_server
INTO fdw_etl;
```
**约束**FDW 映射为只读,`zqyy_app` 不存储 ETL 数据副本。
### 8. FastAPI 后端骨架
**项目结构**
```
apps/backend/
├── app/
│ ├── __init__.py
│ ├── main.py # FastAPI 入口
│ ├── config.py # 配置加载
│ ├── database.py # 数据库连接
│ ├── routers/ # 路由模块
│ │ └── __init__.py
│ ├── middleware/ # 中间件
│ │ └── __init__.py
│ └── schemas/ # Pydantic 模型
│ └── __init__.py
├── tests/
│ └── __init__.py
├── pyproject.toml
└── README.md
```
**关键配置**
- 连接 `zqyy_app` 数据库(通过 FDW 访问 ETL 数据)
- OpenAPI 文档自动生成FastAPI 内置)
- 依赖 `packages/shared` 获取通用工具
### 9. 共享包packages/shared
**模块划分**
```
packages/shared/
├── src/
│ └── neozqyy_shared/
│ ├── __init__.py
│ ├── enums.py # 字段枚举定义
│ ├── money.py # 金额精度工具CNY, numeric(2)
│ └── datetime_utils.py # 时间处理工具
├── tests/
│ └── __init__.py
├── pyproject.toml
└── README.md
```
**提取来源**
- `enums.py`:从 ETL 的 `models/` 中提取通用枚举
- `money.py`:金额四舍五入、格式化(`Decimal` + `ROUND_HALF_UP`scale=2
- `datetime_utils.py`:时区转换、日期范围计算(从 `utils/` 提取)
### 10. .kiro 迁移
**迁移内容**
- 复制 `.kiro/steering/` 到 Monorepo
- 更新 `product.md`:从单一 ETL 视角扩展为 Monorepo 全局视角
- 更新 `tech.md`:新增 FastAPI、uv workspace、Donut+TDesign 等技术栈
- 更新 `structure-lite.md`:反映 Monorepo 目录结构和模块边界
- 更新路径引用:所有 steering 文件中的路径适配新结构
## 数据模型
### etl_feiqiu 数据库(六层 Schema
```mermaid
erDiagram
META {
bigint run_id PK
text task_code
timestamptz started_at
timestamptz ended_at
text status
jsonb result_summary
}
META ||--o{ ODS : "调度触发"
ODS {
bigint id PK
text content_hash PK
jsonb payload
text source_endpoint
timestamptz fetched_at
}
ODS ||--o{ DWD : "清洗装载"
DWD {
bigint id PK
timestamptz scd2_start_time
timestamptz scd2_end_time
int scd2_is_current
int scd2_version
}
DWD ||--o{ CORE : "提取最小字段集"
CORE {
bigint id PK
text name
bigint site_id
}
DWD ||--o{ DWS : "汇总聚合"
CORE ||--o{ DWS : "汇总聚合"
DWS {
bigint id PK
date stat_date
numeric amount
bigint site_id
}
DWS ||--o{ APP : "视图封装"
APP {
text view_name
text rls_policy
}
```
### zqyy_app 数据库
```mermaid
erDiagram
USERS {
bigint id PK
text wx_openid UK
text mobile
text nickname
int status
timestamptz created_at
}
ROLES {
int id PK
text name UK
text description
}
USER_ROLES {
bigint user_id FK
int role_id FK
}
PERMISSIONS {
int id PK
text resource
text action
}
ROLE_PERMISSIONS {
int role_id FK
int permission_id FK
}
USERS ||--o{ USER_ROLES : "拥有"
ROLES ||--o{ USER_ROLES : "分配给"
ROLES ||--o{ ROLE_PERMISSIONS : "包含"
PERMISSIONS ||--o{ ROLE_PERMISSIONS : "授予"
FDW_ETL_VIEWS {
text foreign_table_name
text source_schema
text mapping_type
}
```
### 配置分层模型
```
优先级(低 → 高):
┌─────────────────────────────┐
│ 根 .env公共配置模板 │ DB_HOST, DB_PORT, TIMEZONE
├─────────────────────────────┤
│ 应用 .env.local私有覆盖 │ DB_NAME, DB_PASSWORD, API_TOKEN
├─────────────────────────────┤
│ 环境变量 │ 运行时覆盖
├─────────────────────────────┤
│ CLI 参数 │ 最高优先级
└─────────────────────────────┘
```
## 正确性属性
*属性是系统在所有有效执行中都应保持为真的特征或行为——本质上是关于系统应该做什么的形式化陈述。属性是人类可读规格与机器可验证正确性保证之间的桥梁。*
### Property 1: README.md 结构完整性
*对于任意* Monorepo 一级目录,其 README.md 文件应存在且包含"作用说明"、"结构描述"和"Roadmap"三个段落。
**Validates: Requirements 1.5**
### Property 2: Python 子项目配置完整性
*对于任意* uv workspace 声明的 Python 子项目成员,该子项目目录下应存在独立的 `pyproject.toml` 文件,且文件中包含 `[project]` 段落。
**Validates: Requirements 3.2**
### Property 3: 配置优先级 - .env.local 覆盖
*对于任意*配置项名称和两个不同的值,当根 `.env` 和应用 `.env.local` 都定义了该配置项时,配置加载器返回的值应等于 `.env.local` 中的值。
**Validates: Requirements 4.3**
### Property 4: 必需配置缺失检测
*对于任意*必需配置项,当所有配置层级(.env、.env.local、环境变量、CLI均未提供该项时配置加载器应抛出错误且错误信息中包含该缺失配置项的名称。
**Validates: Requirements 4.4**
### Property 5: 文件迁移完整性
*对于任意*源-目标目录映射关系ETL 业务代码、database 文件、tests 目录),源目录中的每个文件在目标目录的对应位置都应存在且内容一致。
**Validates: Requirements 5.1, 5.2, 5.3**
### Property 6: Schema 表定义迁移完整性
*对于任意*现有数据库 schemabilliards_ods、billiards_dws中的表新 schemaods、dws的 DDL 文件中应包含该表的 CREATE TABLE 定义。
**Validates: Requirements 7.3, 7.6**
### Property 7: Core schema 最小字段集
*对于任意* core schema 中的表,其字段数量应严格少于对应 dwd schema 中同名(或对应)表的字段数量。
**Validates: Requirements 7.5**
### Property 8: 测试数据库结构一致性
*对于任意*生产数据库etl_feiqiu、zqyy_app中的 schema 和表定义对应的测试数据库test_etl_feiqiu、test_zqyy_app中应存在相同的 schema 和表结构。
**Validates: Requirements 9.1, 9.2**
### Property 9: Steering 文件路径更新
*对于任意* `.kiro/steering/` 目录下的文件,文件内容中不应包含旧仓库路径引用(如 `FQ-ETL``C:\ZQYY\FQ-ETL`)。
**Validates: Requirements 10.2**
### Property 10: 业务表 site_id 字段存在性
*对于任意* app schema 中的业务视图和 dws/core schema 中的业务表,其定义中应包含 `site_id` 字段。
**Validates: Requirements 13.1**
### Property 11: RLS 按 site_id 隔离
*对于任意* app schema 中启用了 RLS 的视图,当会话变量 `app.current_site_id` 设置为某个门店 ID 时,查询结果应仅包含该 `site_id` 的数据行。
**Validates: Requirements 13.2**
## 错误处理
### 配置错误
- **缺失必需配置**:启动时立即报错,列出所有缺失项名称,不启动服务
- **配置值格式错误**:报告具体的配置项路径和期望格式
- **.env 文件不存在**:使用默认值继续,不报错(.env.template 仅为模板)
### 迁移错误
- **源文件不存在**:记录警告日志,继续迁移其他文件,最终汇总报告缺失文件列表
- **目标目录已存在**:提示用户确认是否覆盖,默认不覆盖
- **import 路径修复失败**:记录错误日志,标记需要手动修复的文件
### 数据库错误
- **Schema 创建失败**:回滚当前 schema 的所有 DDL报告失败原因
- **FDW 连接失败**:记录错误日志,不影响本地表的正常使用
- **RLS 策略创建失败**:回滚策略创建,报告受影响的表
### 测试数据库错误
- **结构不一致**:提供 diff 工具比较生产与测试库结构差异
- **数据迁移失败**:回滚到迁移前状态,报告失败的表和原因
## 测试策略
### 测试框架
- **单元测试**`pytest`Python 子项目)
- **属性测试**`hypothesis`Python 属性测试库)
- 每个属性测试配置最少 100 次迭代
### 单元测试覆盖
1. **Scaffold 测试**:验证目录创建、文件生成的具体示例
2. **配置加载器测试**:验证分层加载、冲突处理、缺失检测的边界情况
3. **迁移脚本测试**:验证文件复制、路径映射的具体场景
4. **DDL 语法测试**:验证生成的 SQL 语法正确性
### 属性测试覆盖
每个属性测试必须引用设计文档中的属性编号:
- **Feature: monorepo-migration, Property 1: README.md 结构完整性** — 验证所有一级目录 README 包含必需段落
- **Feature: monorepo-migration, Property 2: Python 子项目配置完整性** — 验证所有 workspace 成员有 pyproject.toml
- **Feature: monorepo-migration, Property 3: 配置优先级** — 生成随机配置项,验证 .env.local 覆盖行为
- **Feature: monorepo-migration, Property 4: 必需配置缺失检测** — 生成随机必需项组合,验证缺失报错
- **Feature: monorepo-migration, Property 5: 文件迁移完整性** — 验证源-目标文件映射的完整性
- **Feature: monorepo-migration, Property 6: Schema 表定义迁移完整性** — 验证现有表在新 DDL 中存在
- **Feature: monorepo-migration, Property 7: Core schema 最小字段集** — 验证 core 表字段数少于 dwd
- **Feature: monorepo-migration, Property 8: 测试数据库结构一致性** — 验证测试库与生产库结构相同
- **Feature: monorepo-migration, Property 9: Steering 文件路径更新** — 验证无旧路径残留
- **Feature: monorepo-migration, Property 10: 业务表 site_id 存在性** — 验证业务表包含 site_id
- **Feature: monorepo-migration, Property 11: RLS 隔离** — 验证 RLS 按 site_id 过滤(集成测试)
### 集成测试
- **ETL 运行验证**:在新目录结构下运行 `pytest tests/unit`,确保所有现有测试通过
- **数据库 Schema 验证**:在测试数据库上执行 DDL验证 schema 创建成功
- **FDW 连接验证**:验证 zqyy_app 通过 FDW 可读取 etl_feiqiu 的 app schema 数据
- **uv workspace 验证**:运行 `uv sync`,验证所有子项目依赖正确解析

View File

@@ -0,0 +1,186 @@
# 需求文档Monorepo 迁移
## 简介
将现有台球厅运营助手项目从单一 ETL 仓库(`FQ-ETL`)扩展为 Monorepo 单体仓库(`NeoZQYY`),整合 ETL 管线、微信小程序后端、小程序前端、管理后台等多个子项目。迁移采用一次性搬迁策略,不保留 Git 历史,所有架构决策已在前期讨论中确认。
## 术语表
- **Monorepo**:单体仓库,多个子项目共存于同一 Git 仓库中
- **ETL_Pipeline**:数据抽取-转换-加载管线,负责从上游 SaaS API 抓取数据并逐层处理
- **ODS**操作数据存储层Operational Data Store保留源 payload
- **DWD**明细数据层Data Warehouse Detail清洗后的明细数据
- **DWS**数据服务层Data Warehouse Service汇总与聚合数据
- **Core**:统一维度/事实最小字段集层,位于 DWD 与 DWS 之间
- **App_Schema**:应用层 schema提供视图/函数 + RLS 供外部访问
- **Meta_Schema**:元数据层 schema存储 ETL 调度、游标、运行记录
- **FDW**PostgreSQL 外部数据包装器Foreign Data Wrapper用于跨库只读映射
- **uv_Workspace**Python 包管理工具 uv 的 workspace 模式,管理多包依赖
- **RLS**行级安全策略Row Level Security用于多门店数据隔离
- **SCD2**:缓慢变化维度类型 2Slowly Changing Dimension Type 2维度历史追踪
- **etl_feiqiu**:飞球平台 ETL 数据库实例名
- **zqyy_app**:业务应用数据库实例名(用户/权限/任务/审批)
- **site_id**:门店标识字段,用于多门店数据隔离
- **Scaffold**项目骨架包含目录结构、配置文件、README 等基础设施
## 需求
### 需求 1Monorepo 骨架搭建
**用户故事:** 作为开发者,我希望在 `C:\NeoZQYY\` 创建完整的 Monorepo 目录结构和基础配置,以便所有子项目有统一的组织方式和开发规范。
#### 验收标准
1. WHEN Scaffold 初始化执行时THE Scaffold SHALL 在 `C:\NeoZQYY\` 下创建以下一级目录:`apps/``gui/``packages/``db/``docs/``infra/``scripts/``samples/``tests/``tmp/``.kiro/`
2. WHEN Scaffold 初始化执行时THE Scaffold SHALL 在 `apps/` 下创建子目录:`etl/`(含 `pipelines/feiqiu/`)、`backend/``miniprogram/``admin-web/`
3. WHEN Scaffold 初始化执行时THE Scaffold SHALL 在 `db/` 下创建子目录:`etl_feiqiu/`(含 `schemas/``migrations/``seeds/`)、`zqyy_app/`(含 `schemas/``migrations/``seeds/`)、`fdw/`
4. WHEN Scaffold 初始化执行时THE Scaffold SHALL 在 `docs/` 下创建子目录:`prd/``contracts/`(含 `openapi/``schemas/``data_dictionary/`)、`permission_matrix/``architecture/``database/``h5_ui/``ops/``audit/``roadmap/`
5. THE Scaffold SHALL 为每个一级目录生成 `README.md` 文件,包含该目录的作用说明、内部结构描述和 Roadmap 段落
6. WHEN 某个功能"暂不实施但未来必须做"时THE Scaffold SHALL 将该内容记录在对应目录 `README.md` 的 Roadmap 段落中
### 需求 2Git 仓库与版本控制配置
**用户故事:** 作为开发者,我希望新 Monorepo 有正确的 Git 配置,以便代码版本管理规范且安全。
#### 验收标准
1. WHEN Git 仓库初始化时THE Scaffold SHALL 创建新的 Git 仓库,不迁移旧仓库历史
2. THE Scaffold SHALL 生成 `.gitignore` 文件,排除 `tmp/``__pycache__/``.env`(非模板)、`*.pyc``.hypothesis/``.pytest_cache/``logs/``node_modules/`、虚拟环境目录等
3. THE Scaffold SHALL 生成 `.kiroignore` 文件,排除不需要 Kiro 索引的目录
### 需求 3Python 包管理与 uv Workspace 配置
**用户故事:** 作为开发者,我希望使用 `pyproject.toml` + `uv` workspace 管理多包依赖,以便各子项目的依赖隔离且可统一管理。
#### 验收标准
1. THE Scaffold SHALL 在 Monorepo 根目录生成 `pyproject.toml`,配置 uv workspace 并声明所有 Python 子项目成员
2. THE Scaffold SHALL 为每个 Python 子项目(`apps/etl/pipelines/feiqiu/``apps/backend/``packages/shared/``gui/`)生成独立的 `pyproject.toml`
3. WHEN 子项目声明对 `packages/shared` 的依赖时THE uv_Workspace SHALL 通过 workspace 路径引用解析该依赖
### 需求 4环境配置隔离
**用户故事:** 作为开发者,我希望公共配置和各应用私有配置分层管理,以便敏感信息不泄露且配置不冲突。
#### 验收标准
1. THE Scaffold SHALL 在 Monorepo 根目录生成 `.env.template` 文件,包含公共配置项(数据库主机、端口等非敏感信息)的模板
2. WHEN 各应用需要私有配置时THE Scaffold SHALL 支持在应用目录下放置 `.env.local` 文件覆盖公共配置
3. IF 公共 `.env` 与应用 `.env.local` 存在同名配置项且值冲突THEN THE 配置加载器 SHALL 以应用级 `.env.local` 的值为准
4. IF 必需的配置项在所有层级均缺失THEN THE 配置加载器 SHALL 在启动时报告明确的错误信息,指出缺失的配置项名称
### 需求 5ETL 项目平移
**用户故事:** 作为开发者,我希望将现有 ETL 项目整体平移到 Monorepo 中,以便 ETL 功能在新仓库中正常运行。
#### 验收标准
1. WHEN ETL 平移执行时THE 迁移脚本 SHALL 将 `C:\ZQYY\FQ-ETL` 的业务代码(`api/``cli/``config/``loaders/``models/``orchestration/``scd/``tasks/``utils/``quality/`)复制到 `apps/etl/pipelines/feiqiu/`
2. WHEN ETL 平移执行时THE 迁移脚本 SHALL 将 `database/` 目录的 DDL、seed、migration 文件迁移到 `db/etl_feiqiu/` 对应子目录
3. WHEN ETL 平移执行时THE 迁移脚本 SHALL 将 `tests/` 目录复制到 `apps/etl/pipelines/feiqiu/tests/`
4. WHEN ETL 平移完成后THE ETL_Pipeline SHALL 通过 `pytest tests/unit` 验证所有单元测试通过
5. IF ETL 内部存在需要调整的 import 路径THEN THE 迁移脚本 SHALL 更新这些路径以适配新目录结构
6. WHEN ETL 平移执行时THE 迁移脚本 SHALL 将现有 `gui/` 目录迁移到 Monorepo 顶层 `gui/`
### 需求 6小程序前端平移
**用户故事:** 作为开发者,我希望将微信小程序项目迁移到 Monorepo 中,以便前端代码与后端统一管理。
#### 验收标准
1. WHEN 小程序平移执行时THE 迁移脚本 SHALL 将 `C:\ZQYY\XCX` 的项目文件复制到 `apps/miniprogram/`
2. WHEN 小程序平移执行时THE 迁移脚本 SHALL 将 `C:\ZQYY\XCX\Prototype` 目录复制到 `docs/h5_ui/`
3. WHEN 小程序平移完成后THE 小程序项目 SHALL 保持原有的 Donut + TDesign 技术栈配置不变
### 需求 7数据库 Schema 重组etl_feiqiu
**用户故事:** 作为数据工程师,我希望将 ETL 数据库重组为六层 schema 架构,以便数据分层清晰、职责明确。
#### 验收标准
1. THE 数据库迁移 SHALL 为 `etl_feiqiu` 数据库创建六个 schema`meta``ods``dwd``core``dws``app`
2. WHEN `meta` schema 创建时THE DDL SHALL 包含 ETL 调度、游标、运行记录相关表(从现有 `etl_admin` schema 迁移)
3. WHEN `ods` schema 创建时THE DDL SHALL 包含现有 `billiards_ods` 的所有表定义
4. WHEN `dwd` schema 创建时THE DDL SHALL 保留现有 main + EX 拆分模式(因字段量大)
5. WHEN `core` schema 创建时THE DDL SHALL 仅包含统一维度表和事实表的最小字段集
6. WHEN `dws` schema 创建时THE DDL SHALL 包含现有 `billiards_dws` 的汇总表定义(助教业绩、财务日报、工资计算等)
7. WHEN `app` schema 创建时THE DDL SHALL 创建面向外部访问的视图和函数,并配置 RLS 策略以 `site_id` 隔离多门店数据
8. THE 数据库迁移 SHALL 将所有 DDL 文件存放在 `db/etl_feiqiu/schemas/` 目录下,每个 schema 一个独立文件
### 需求 8业务数据库设计zqyy_app
**用户故事:** 作为后端开发者,我希望有独立的业务数据库存储用户、权限、任务、审批等应用数据,以便业务逻辑与 ETL 数据解耦。
#### 验收标准
1. THE 数据库迁移 SHALL 为 `zqyy_app` 数据库创建用户管理、权限控制、任务管理、审批流程相关的表结构
2. THE 数据库迁移 SHALL 将 `zqyy_app` 的 DDL 文件存放在 `db/zqyy_app/schemas/` 目录下
3. WHEN `zqyy_app` 需要访问 ETL 数据时THE FDW 配置 SHALL 通过 `postgres_fdw``etl_feiqiu``app` schema 映射为 `zqyy_app` 中的外部表
4. THE FDW 配置 SHALL 以只读方式映射,`zqyy_app` 不存储 ETL 数据的副本
5. THE FDW 配置文件 SHALL 存放在 `db/fdw/` 目录下
### 需求 9测试数据库镜像
**用户故事:** 作为开发者,我希望有与生产结构完全一致的测试数据库,以便在不影响生产数据的情况下进行开发和测试。
#### 验收标准
1. THE 数据库迁移 SHALL 创建 `test_etl_feiqiu` 数据库,其 schema 结构与 `etl_feiqiu` 完全一致
2. THE 数据库迁移 SHALL 创建 `test_zqyy_app` 数据库,其 schema 结构与 `zqyy_app` 完全一致
3. WHEN 测试数据库创建完成后THE 迁移脚本 SHALL 提供从现有 `LLZQ-test` 数据库迁移测试数据到新结构的脚本
4. WHEN 生产数据库 schema 发生变更时THE 测试数据库 SHALL 同步应用相同的迁移脚本以保持结构一致
### 需求 10.kiro 配置迁移与 Steering 更新
**用户故事:** 作为开发者,我希望 Kiro IDE 的配置和 steering 文件适配 Monorepo 结构,以便 AI 辅助开发在新仓库中正常工作。
#### 验收标准
1. WHEN .kiro 迁移执行时THE 迁移脚本 SHALL 将现有 `.kiro/steering/` 文件复制到 Monorepo 的 `.kiro/steering/`
2. WHEN .kiro 迁移完成后THE Steering 文件 SHALL 更新所有路径引用以反映 Monorepo 目录结构
3. WHEN .kiro 迁移完成后THE Steering 文件 SHALL 更新 `product.md``tech.md``structure.md` 为 Monorepo 视角的内容
### 需求 11FastAPI 后端骨架
**用户故事:** 作为后端开发者,我希望有 FastAPI 项目骨架,以便快速开始小程序后端 API 的开发。
#### 验收标准
1. WHEN 后端骨架创建时THE Scaffold SHALL 在 `apps/backend/` 下生成 FastAPI 项目结构,包含入口文件、路由目录、中间件目录、配置文件
2. THE 后端骨架 SHALL 配置 OpenAPI 文档自动生成
3. THE 后端骨架 SHALL 配置数据库连接模块,支持连接 `zqyy_app` 数据库
4. THE 后端骨架 SHALL 包含独立的 `pyproject.toml`,声明 FastAPI 及相关依赖
### 需求 12共享包基础结构
**用户故事:** 作为开发者,我希望有统一的共享包存放跨项目复用的工具代码,以便避免代码重复。
#### 验收标准
1. WHEN 共享包创建时THE Scaffold SHALL 在 `packages/shared/` 下生成 Python 包结构,包含 `__init__.py``pyproject.toml`
2. THE 共享包 SHALL 提取并包含以下通用工具模块字段枚举定义、金额精度处理工具CNYnumeric(2))、时间处理工具
3. WHEN ETL 或后端项目引用共享包时THE uv_Workspace SHALL 通过 workspace 路径依赖解析 `packages/shared`
### 需求 13多门店数据隔离
**用户故事:** 作为系统架构师,我希望在同一数据库内通过 RLS 实现多门店数据隔离,以便未来扩展到多门店场景。
#### 验收标准
1. THE 数据库设计 SHALL 在所有业务表中包含 `site_id` 字段标识门店归属
2. WHEN RLS 策略启用时THE 数据库 SHALL 根据当前会话的 `site_id` 参数自动过滤查询结果,仅返回该门店的数据
3. WHEN 每个门店运行 ETL 时THE ETL_Pipeline SHALL 作为独立进程执行,使用该门店的 `site_id` 标识数据
### 需求 14基础设施配置管理
**用户故事:** 作为运维人员,我希望基础设施配置纳入版本控制,以便环境配置可追溯、可复现。
### 需求15避免影响kiro性能完成Monorepo后根据文件和目录结构。编辑.kiroignore
验收标准:完善
#### 验收标准
1. THE Scaffold SHALL 在 `infra/` 下创建 `jump_proxy/``tailscale/``firewall/` 子目录
2. THE infra 目录 SHALL 纳入 Git 版本控制
3. IF infra 目录中包含敏感配置文件THEN THE `.gitignore` SHALL 排除这些敏感文件,同时保留非敏感的配置模板

View File

@@ -0,0 +1,215 @@
# 实施计划Monorepo 迁移
## 概述
将现有 ETL 仓库迁移为 Monorepo 单体仓库,分 7 个阶段执行。每个阶段包含具体的代码/文件操作任务,按依赖顺序排列。
## 任务
- [x] 1. Monorepo 骨架搭建
- [x] 1.1 在 `C:\NeoZQYY\` 创建完整目录结构
- 创建所有一级目录:`apps/``gui/``packages/``db/``docs/``infra/``scripts/``samples/``tests/``tmp/``.kiro/`
- 创建 `apps/` 子目录:`etl/pipelines/feiqiu/``backend/``miniprogram/``admin-web/`
- 创建 `db/` 子目录:`etl_feiqiu/schemas/``etl_feiqiu/migrations/``etl_feiqiu/seeds/``zqyy_app/schemas/``zqyy_app/migrations/``zqyy_app/seeds/``fdw/`
- 创建 `docs/` 子目录:`prd/``contracts/openapi/``contracts/schemas/``contracts/data_dictionary/``permission_matrix/``architecture/``database/``h5_ui/``ops/``audit/``roadmap/`
- 创建 `infra/` 子目录:`jump_proxy/``tailscale/``firewall/`
- _Requirements: 1.1, 1.2, 1.3, 1.4, 14.1_
- [x] 1.2 生成所有一级目录的 README.md
- 每个 README 包含作用说明、内部结构描述、Roadmap 段落
- 一级目录列表:`apps/``gui/``packages/``db/``docs/``infra/``scripts/``samples/``tests/`
- `apps/etl/README.md` 的 Roadmap 记录未来 sdk/connectors 拆分计划
- `packages/README.md` 的 Roadmap 记录 etl_sdk、authz、data_contracts 候选
- `db/README.md` 的 Roadmap 记录 FDW 演进计划
- _Requirements: 1.5, 1.6_
- [x] 1.3 编写 README 结构完整性属性测试
- **Property 1: README.md 结构完整性**
- **Validates: Requirements 1.5**
- [x] 1.4 初始化 Git 仓库并生成版本控制配置
-`C:\NeoZQYY\` 执行 `git init`
- 生成 `.gitignore`:排除 `tmp/``__pycache__/``.env``*.pyc``.hypothesis/``.pytest_cache/``logs/``node_modules/`、虚拟环境目录、`infra/` 下的敏感文件
- 生成 `.kiroignore`
- _Requirements: 2.1, 2.2, 2.3, 14.2, 14.3_
- [x] 1.5 配置 pyproject.toml 和 uv workspace
- 生成根 `pyproject.toml`,声明 workspace 成员:`apps/etl/pipelines/feiqiu``apps/backend``packages/shared``gui`
- 为每个 Python 子项目生成独立 `pyproject.toml`
- _Requirements: 3.1, 3.2_
- [x] 1.6 编写 Python 子项目配置完整性属性测试
- **Property 2: Python 子项目配置完整性**
- **Validates: Requirements 3.2**
- [x] 1.7 生成环境配置模板
- 生成根 `.env.template`包含公共配置项模板DB_HOST、DB_PORT、TIMEZONE 等)
- _Requirements: 4.1_
- [x] 2. 检查点 - 骨架验证
- 确保所有目录和文件已创建ask the user if questions arise.
- [x] 3. ETL 项目平移
- [x] 3.1 复制 ETL 业务代码到 Monorepo
-`C:\ZQYY\FQ-ETL``api/``cli/``config/``loaders/``models/``orchestration/``scd/``tasks/``utils/``quality/` 复制到 `apps/etl/pipelines/feiqiu/`
-`tests/` 复制到 `apps/etl/pipelines/feiqiu/tests/`
-`requirements.txt``pytest.ini``run_etl.bat``run_etl.sh` 复制到 `apps/etl/pipelines/feiqiu/`
- _Requirements: 5.1, 5.3_
- [x] 3.2 迁移数据库文件到 db/etl_feiqiu/
-`database/schema_*.sql` 复制到 `db/etl_feiqiu/schemas/`
-`database/migrations/` 复制到 `db/etl_feiqiu/migrations/`
-`database/seed_*.sql` 复制到 `db/etl_feiqiu/seeds/`
-`database/connection.py``database/operations.py``database/base.py` 保留在 ETL 内部(`apps/etl/pipelines/feiqiu/database/`
- _Requirements: 5.2_
- [x] 3.3 迁移 GUI 到顶层
-`C:\ZQYY\FQ-ETL\gui/` 复制到 `C:\NeoZQYY\gui/`
- 生成 `gui/pyproject.toml`,声明 PySide6 依赖
- _Requirements: 5.6_
- [x] 3.4 调整 ETL 的 pyproject.toml 和 pytest.ini
- 更新 `apps/etl/pipelines/feiqiu/pyproject.toml`,从 `requirements.txt` 提取依赖
- 更新 `apps/etl/pipelines/feiqiu/pytest.ini`,设置 `pythonpath = .`
- _Requirements: 5.4, 5.5_
- [x] 3.5 编写文件迁移完整性属性测试
- **Property 5: 文件迁移完整性**
- **Validates: Requirements 5.1, 5.2, 5.3**
- [x] 4. 检查点 - ETL 平移验证
-`apps/etl/pipelines/feiqiu/` 下运行 `pytest tests/unit`,确保所有单元测试通过
- Ensure all tests pass, ask the user if questions arise.
- [x] 5. 小程序前端平移
- [x] 5.1 复制小程序项目到 Monorepo
-`C:\ZQYY\XCX\`(除 Prototype 目录)复制到 `apps/miniprogram/`
-`C:\ZQYY\XCX\Prototype\` 复制到 `docs/h5_ui/`
- 生成 `apps/miniprogram/README.md`
- _Requirements: 6.1, 6.2, 6.3_
- [x] 6. 数据库 Schema 重组
- [x] 6.1 编写 etl_feiqiu 六层 Schema DDL
- 创建 `db/etl_feiqiu/schemas/meta.sql`:从现有 `etl_admin` schema 迁移调度、游标、运行记录表
- 创建 `db/etl_feiqiu/schemas/ods.sql`:从现有 `billiards_ods` 迁移所有表定义schema 名改为 `ods`
- 创建 `db/etl_feiqiu/schemas/dwd.sql`:从现有 `billiards_dwd` 迁移,保留 main+EX 拆分
- 创建 `db/etl_feiqiu/schemas/core.sql`:设计统一维度/事实最小字段集表
- 创建 `db/etl_feiqiu/schemas/dws.sql`:从现有 `billiards_dws` 迁移汇总表
- 创建 `db/etl_feiqiu/schemas/app.sql`:创建面向外部的视图 + RLS 策略(以 `site_id` 隔离)
- _Requirements: 7.1, 7.2, 7.3, 7.4, 7.5, 7.6, 7.7, 7.8_
- [x] 6.2 编写 Schema 表定义迁移完整性属性测试
- **Property 6: Schema 表定义迁移完整性**
- **Validates: Requirements 7.3, 7.6**
- [x] 6.3 编写 Core schema 最小字段集属性测试
- **Property 7: Core schema 最小字段集**
- **Validates: Requirements 7.5**
- [x] 6.4 编写 zqyy_app 数据库 Schema DDL
- 创建 `db/zqyy_app/schemas/init.sql`:用户表、角色表、权限表、用户角色关联表、任务表、审批表
- 所有业务表包含 `site_id` 字段
- _Requirements: 8.1, 8.2, 13.1_
- [x] 6.5 编写 FDW 映射配置
- 创建 `db/fdw/setup_fdw.sql`CREATE SERVER、CREATE USER MAPPING只读角色、IMPORT FOREIGN SCHEMA
- _Requirements: 8.3, 8.4, 8.5_
- [x] 6.6 编写业务表 site_id 存在性属性测试
- **Property 10: 业务表 site_id 字段存在性**
- **Validates: Requirements 13.1**
- [x] 6.7 编写测试数据库创建脚本
- 创建 `db/etl_feiqiu/scripts/create_test_db.sql`:创建 `test_etl_feiqiu`,复用生产 DDL
- 创建 `db/zqyy_app/scripts/create_test_db.sql`:创建 `test_zqyy_app`,复用生产 DDL
- 创建 `db/scripts/migrate_test_data.sql`:从 `LLZQ-test` 迁移测试数据的脚本
- _Requirements: 9.1, 9.2, 9.3_
- [x] 6.8 编写测试数据库结构一致性属性测试
- **Property 8: 测试数据库结构一致性**
- **Validates: Requirements 9.1, 9.2**
- [x] 7. 检查点 - 数据库 Schema 验证
- 确保所有 DDL 文件语法正确ask the user if questions arise.
- [ ] 8. .kiro 迁移与 Steering 更新
- [-] 8.1 复制 .kiro/steering/ 到 Monorepo
-`C:\ZQYY\FQ-ETL\.kiro\steering\` 所有文件复制到 `C:\NeoZQYY\.kiro\steering\`
-`C:\ZQYY\FQ-ETL\.kiro\specs\` 复制到 `C:\NeoZQYY\.kiro\specs\`(包含本 spec
- _Requirements: 10.1_
- [~] 8.2 更新 Steering 文件为 Monorepo 视角
- 更新 `product.md`:从单一 ETL 扩展为 Monorepo 全局视角ETL + 后端 + 小程序 + GUI
- 更新 `tech.md`:新增 FastAPI、uv workspace、Donut+TDesign 技术栈
- 更新 `structure-lite.md`:反映 Monorepo 目录结构和模块边界
- 更新所有 steering 文件中的路径引用,移除旧仓库路径(`FQ-ETL``C:\ZQYY\FQ-ETL`
- _Requirements: 10.2, 10.3_
- [~] 8.3 编写 Steering 文件路径更新属性测试
- **Property 9: Steering 文件路径更新**
- **Validates: Requirements 10.2**
- [ ] 9. FastAPI 后端骨架
- [~] 9.1 创建 FastAPI 项目结构
- 创建 `apps/backend/app/__init__.py``main.py``config.py``database.py`
- 创建 `apps/backend/app/routers/__init__.py`
- 创建 `apps/backend/app/middleware/__init__.py`
- 创建 `apps/backend/app/schemas/__init__.py`
- 创建 `apps/backend/tests/__init__.py`
- `main.py` 中配置 FastAPI 实例,启用 OpenAPI 文档自动生成
- `database.py` 中配置 `zqyy_app` 数据库连接
- _Requirements: 11.1, 11.2, 11.3_
- [~] 9.2 生成 apps/backend/pyproject.toml
- 声明 FastAPI、uvicorn、psycopg2-binary、neozqyy-shared 等依赖
- 配置 uv workspace 源引用 `neozqyy-shared`
- _Requirements: 11.4_
- [~] 9.3 生成 apps/backend/README.md
- 包含作用说明、项目结构、启动方式、Roadmap
- _Requirements: 1.5_
- [ ] 10. 共享包搭建
- [~] 10.1 创建 packages/shared 包结构
- 创建 `packages/shared/src/neozqyy_shared/__init__.py`
- 创建 `packages/shared/src/neozqyy_shared/enums.py`:字段枚举定义(从 ETL models/ 提取通用枚举)
- 创建 `packages/shared/src/neozqyy_shared/money.py`金额精度工具Decimal + ROUND_HALF_UPscale=2
- 创建 `packages/shared/src/neozqyy_shared/datetime_utils.py`:时区转换、日期范围计算
- 创建 `packages/shared/tests/__init__.py`
- _Requirements: 12.1, 12.2_
- [~] 10.2 生成 packages/shared/pyproject.toml
- 声明包名 `neozqyy-shared`最小依赖python-dateutil、tzdata
- _Requirements: 12.3_
- [~] 10.3 编写配置优先级属性测试
- **Property 3: 配置优先级 - .env.local 覆盖**
- **Validates: Requirements 4.3**
- [~] 10.4 编写必需配置缺失检测属性测试
- **Property 4: 必需配置缺失检测**
- **Validates: Requirements 4.4**
- [ ] 11. 检查点 - 全局验证
- 验证 uv workspace 依赖解析:在根目录运行 `uv sync`
- 验证 ETL 单元测试:在 `apps/etl/pipelines/feiqiu/` 下运行 `pytest tests/unit`
- Ensure all tests pass, ask the user if questions arise.
- [ ] 12. RLS 与多门店隔离验证
- [~] 12.1 编写 RLS 按 site_id 隔离属性测试
- **Property 11: RLS 按 site_id 隔离**
- **Validates: Requirements 13.2**
- 需要集成测试环境test_etl_feiqiu 数据库)
- [ ] 13. 最终检查点
- 确保所有文件已创建、所有 README 已编写、所有 DDL 语法正确
- Ensure all tests pass, ask the user if questions arise.
## 备注
- 标记 `*` 的任务为可选测试任务,可跳过以加速 MVP
- 每个任务引用具体需求编号,确保可追溯
- 检查点确保增量验证,避免问题累积
- 属性测试验证通用正确性,单元测试验证具体边界情况
- 文件复制操作需要用户在终端手动执行涉及跨目录操作Kiro 负责生成目标文件内容

View File

@@ -0,0 +1 @@
{"generationMode": "requirements-first"}

View File

@@ -0,0 +1,424 @@
# 设计文档:仓库治理只读审计
## 概述
本设计描述三个 Python 审计脚本的实现方案,用于对 etl-billiards 仓库进行只读分析并生成三份 Markdown 报告。脚本仅读取文件系统和源代码,不连接数据库、不修改任何现有文件,仅在 `docs/audit/repo/` 目录下输出报告。
审计脚本采用模块化设计:一个共享的仓库扫描器负责遍历文件系统,三个独立的分析器分别生成文件清单、流程树和文档对齐报告。
## 架构
```mermaid
graph TD
A[scripts/audit/run_audit.py<br/>审计主入口] --> B[scripts/audit/scanner.py<br/>仓库扫描器]
A --> C[scripts/audit/inventory_analyzer.py<br/>文件清单分析器]
A --> D[scripts/audit/flow_analyzer.py<br/>流程树分析器]
A --> E[scripts/audit/doc_alignment_analyzer.py<br/>文档对齐分析器]
B --> F[文件系统<br/>只读遍历]
C --> G[docs/audit/repo/file_inventory.md]
D --> H[docs/audit/repo/flow_tree.md]
E --> I[docs/audit/repo/doc_alignment.md]
C --> B
D --> B
E --> B
```
### 执行流程
1. `run_audit.py` 作为主入口,初始化扫描器并依次调用三个分析器
2. `scanner.py` 递归遍历仓库,构建文件元信息列表(路径、大小、类型)
3. 各分析器接收扫描结果,执行各自的分析逻辑,输出 Markdown 报告
4. 所有报告写入 `docs/audit/repo/` 目录
## 组件与接口
### 1. 仓库扫描器 (`scripts/audit/scanner.py`)
负责递归遍历仓库文件系统,返回结构化的文件元信息。
```python
@dataclass
class FileEntry:
"""单个文件/目录的元信息"""
rel_path: str # 相对于仓库根目录的路径
is_dir: bool # 是否为目录
size_bytes: int # 文件大小(目录为 0
extension: str # 文件扩展名(小写,含点号)
is_empty_dir: bool # 是否为空目录
EXCLUDED_PATTERNS: list[str] = [
".git", "__pycache__", ".pytest_cache",
"*.pyc", ".kiro",
]
def scan_repo(root: Path, exclude: list[str] = EXCLUDED_PATTERNS) -> list[FileEntry]:
"""递归扫描仓库,返回所有文件和目录的元信息列表"""
...
```
### 2. 文件清单分析器 (`scripts/audit/inventory_analyzer.py`)
对扫描结果进行用途分类和处置标签分配。
```python
# 用途分类枚举
class Category(str, Enum):
CORE_CODE = "核心代码"
CONFIG = "配置"
DATABASE_DEF = "数据库定义"
TEST = "测试"
DOCS = "文档"
SCRIPTS = "脚本工具"
GUI = "GUI"
BUILD_DEPLOY = "构建与部署"
LOG_OUTPUT = "日志与输出"
TEMP_DEBUG = "临时与调试"
OTHER = "其他"
# 处置标签枚举
class Disposition(str, Enum):
KEEP = "保留"
CANDIDATE_DELETE = "候选删除"
CANDIDATE_ARCHIVE = "候选归档"
NEEDS_REVIEW = "待确认"
@dataclass
class InventoryItem:
"""清单条目"""
rel_path: str
category: Category
disposition: Disposition
description: str
def classify(entry: FileEntry) -> InventoryItem:
"""根据路径、扩展名等规则对单个文件/目录进行分类和标签分配"""
...
def build_inventory(entries: list[FileEntry]) -> list[InventoryItem]:
"""批量分类所有文件条目"""
...
def render_inventory_report(items: list[InventoryItem], repo_root: str) -> str:
"""生成 Markdown 格式的文件清单报告"""
...
```
**分类规则(按优先级从高到低)**
| 路径模式 | 用途分类 | 默认处置 |
|---------|---------|---------|
| `tmp/` 下所有文件 | 临时与调试 | 候选删除/候选归档 |
| `logs/``export/` 下的运行时产出 | 日志与输出 | 候选归档 |
| `*.lnk``*.rar` 文件 | 其他 | 候选删除 |
| 空目录(如 `Deleded & backup/` | 其他 | 候选删除 |
| `tasks/``loaders/``scd/``orchestration/``quality/``models/``utils/``api/` | 核心代码 | 保留 |
| `config/` | 配置 | 保留 |
| `database/*.sql``database/migrations/` | 数据库定义 | 保留 |
| `database/*.py` | 核心代码 | 保留 |
| `tests/` | 测试 | 保留 |
| `docs/` | 文档 | 保留 |
| `scripts/` 下的 `.py` 文件 | 脚本工具 | 保留/待确认 |
| `gui/` | GUI | 保留 |
| `setup.py``build_exe.py``*.bat``*.sh``*.ps1` | 构建与部署 | 保留 |
| 根目录散落文件(`Prompt用.md``Untitled``fix_symbols.py` 等) | 其他 | 待确认 |
### 3. 流程树分析器 (`scripts/audit/flow_analyzer.py`)
通过静态分析 Python 源码的 `import` 语句和类继承关系,构建从入口到末端模块的调用树。
```python
@dataclass
class FlowNode:
"""流程树节点"""
name: str # 节点名称(模块名/类名/函数名)
source_file: str # 所在源文件路径
node_type: str # 类型entry/module/class/function
children: list["FlowNode"]
def parse_imports(filepath: Path) -> list[str]:
"""使用 ast 模块解析 Python 文件的 import 语句,返回被导入的本地模块列表"""
...
def build_flow_tree(repo_root: Path, entry_file: str) -> FlowNode:
"""从指定入口文件出发,递归追踪 import 链,构建流程树"""
...
def find_orphan_modules(repo_root: Path, all_entries: list[FileEntry], reachable: set[str]) -> list[str]:
"""找出未被任何入口直接或间接引用的 Python 模块"""
...
def render_flow_report(trees: list[FlowNode], orphans: list[str], repo_root: str) -> str:
"""生成 Markdown 格式的流程树报告(含 Mermaid 图和缩进文本)"""
...
```
**入口点识别**
- CLI 入口:`cli/main.py``main()` 函数
- GUI 入口:`gui/main.py``main()` 函数
- 批处理入口:`run_etl.bat``run_gui.bat``run_ods.bat` → 解析其中的 `python` 命令
- 运维脚本:`scripts/*.py` → 各自的 `if __name__ == "__main__"`
**静态分析策略**
- 使用 Python `ast` 模块解析源文件,提取 `import``from ... import` 语句
- 仅追踪项目内部模块(排除标准库和第三方包)
- 通过 `orchestration/task_registry.py` 的注册语句识别所有任务类及其源文件
- 通过类继承关系(`BaseTask``BaseLoader``BaseDwsTask` 等)识别任务和加载器层级
### 4. 文档对齐分析器 (`scripts/audit/doc_alignment_analyzer.py`)
检查文档与代码之间的映射关系、过期点、冲突点和缺失点。
```python
@dataclass
class DocMapping:
"""文档与代码的映射关系"""
doc_path: str # 文档文件路径
doc_topic: str # 文档主题
related_code: list[str] # 关联的代码文件/模块
status: str # 状态aligned/stale/conflict/orphan
@dataclass
class AlignmentIssue:
"""对齐问题"""
doc_path: str
issue_type: str # stale/conflict/missing
description: str
related_code: str
def scan_docs(repo_root: Path) -> list[str]:
"""扫描所有文档文件路径"""
...
def extract_code_references(doc_path: Path) -> list[str]:
"""从文档中提取代码引用(文件路径、类名、函数名、表名等)"""
...
def check_reference_validity(ref: str, repo_root: Path) -> bool:
"""检查文档中的代码引用是否仍然有效"""
...
def find_undocumented_modules(repo_root: Path, documented: set[str]) -> list[str]:
"""找出缺少文档的核心代码模块"""
...
def check_ddl_vs_dictionary(repo_root: Path) -> list[AlignmentIssue]:
"""比对 DDL 文件与数据字典文档的覆盖度"""
...
def check_api_samples_vs_parsers(repo_root: Path) -> list[AlignmentIssue]:
"""比对 API 响应样本与 ODS 表结构/解析器的一致性"""
...
def render_alignment_report(mappings: list[DocMapping], issues: list[AlignmentIssue], repo_root: str) -> str:
"""生成 Markdown 格式的文档对齐报告"""
...
```
**文档来源识别**
- `docs/` 目录下的 `.md``.txt``.csv` 文件
- 根目录的 `README.md`
- `开发笔记/` 目录
- 各模块内的 `README.md``gui/README.md``fetch-test/README.md`
- `.kiro/steering/` 下的引导文件
- `docs/test-json-doc/` 下的 API 响应样本及分析文档
**对齐检查策略**
- 过期点检测:文档中引用的文件路径、类名、函数名在代码中已不存在
- 冲突点检测DDL 中的表/字段定义与数据字典文档不一致API 样本字段与解析器不匹配
- 缺失点检测:核心代码模块(`tasks/``loaders/``orchestration/` 等)缺少对应文档
### 5. 审计主入口 (`scripts/audit/run_audit.py`)
```python
def run_audit(repo_root: Path | None = None) -> None:
"""执行完整审计流程,生成三份报告到 docs/audit/"""
...
if __name__ == "__main__":
run_audit()
```
## 数据模型
### FileEntry文件元信息
| 字段 | 类型 | 说明 |
|------|------|------|
| `rel_path` | `str` | 相对路径 |
| `is_dir` | `bool` | 是否为目录 |
| `size_bytes` | `int` | 文件大小 |
| `extension` | `str` | 扩展名 |
| `is_empty_dir` | `bool` | 是否为空目录 |
### InventoryItem清单条目
| 字段 | 类型 | 说明 |
|------|------|------|
| `rel_path` | `str` | 相对路径 |
| `category` | `Category` | 用途分类 |
| `disposition` | `Disposition` | 处置标签 |
| `description` | `str` | 简要说明 |
### FlowNode流程树节点
| 字段 | 类型 | 说明 |
|------|------|------|
| `name` | `str` | 节点名称 |
| `source_file` | `str` | 源文件路径 |
| `node_type` | `str` | 节点类型 |
| `children` | `list[FlowNode]` | 子节点列表 |
### DocMapping / AlignmentIssue文档对齐
| 字段 | 类型 | 说明 |
|------|------|------|
| `doc_path` | `str` | 文档路径 |
| `doc_topic` / `issue_type` | `str` | 主题/问题类型 |
| `related_code` | `list[str]` / `str` | 关联代码 |
| `status` / `description` | `str` | 状态/描述 |
## 正确性属性
*属性Property是指在系统所有合法执行路径上都应成立的特征或行为——本质上是对"系统应该做什么"的形式化陈述。属性是连接人类可读规格说明与机器可验证正确性保证之间的桥梁。*
### Property 1: classify 完整性
*对于任意* `FileEntry``classify` 函数返回的 `InventoryItem``category` 字段应属于 `Category` 枚举,`disposition` 字段应属于 `Disposition` 枚举,且 `description` 字段为非空字符串。
**Validates: Requirements 1.2, 1.3**
### Property 2: 清单渲染完整性
*对于任意* `InventoryItem` 列表,`render_inventory_report` 生成的 Markdown 文本中,每个条目对应的行应包含该条目的 `rel_path``category.value``disposition.value``description` 四个字段。
**Validates: Requirements 1.4**
### Property 3: 空目录标记为候选删除
*对于任意* `is_empty_dir=True``FileEntry``classify` 返回的 `disposition` 应为 `Disposition.CANDIDATE_DELETE`
**Validates: Requirements 1.5**
### Property 4: .lnk/.rar 文件标记为候选删除
*对于任意* 扩展名为 `.lnk``.rar``FileEntry``classify` 返回的 `disposition` 应为 `Disposition.CANDIDATE_DELETE`
**Validates: Requirements 1.6**
### Property 5: tmp/ 下文件处置范围
*对于任意* `rel_path``tmp/` 开头的 `FileEntry``classify` 返回的 `disposition` 应为 `Disposition.CANDIDATE_DELETE``Disposition.CANDIDATE_ARCHIVE` 之一。
**Validates: Requirements 1.7**
### Property 6: 运行时产出目录标记为候选归档
*对于任意* `rel_path``logs/``export/` 开头且非 `__init__.py``FileEntry``classify` 返回的 `disposition` 应为 `Disposition.CANDIDATE_ARCHIVE`
**Validates: Requirements 1.8**
### Property 7: 扫描器排除规则
*对于任意* 文件树,`scan_repo` 返回的 `FileEntry` 列表中不应包含 `rel_path` 匹配排除模式(`.git``__pycache__``.pytest_cache`)的条目。
**Validates: Requirements 1.1**
### Property 8: 清单按分类分组
*对于任意* `InventoryItem` 列表,`render_inventory_report` 生成的 Markdown 中,同一 `Category` 的条目应连续出现(即按分类分组排列)。
**Validates: Requirements 1.10**
### Property 9: 流程树节点 source_file 有效性
*对于任意* `FlowNode` 树中的节点,`source_file` 字段应为非空字符串,且对应的文件在仓库中实际存在。
**Validates: Requirements 2.7**
### Property 10: 孤立模块检测正确性
*对于任意* 文件集合和可达模块集合,`find_orphan_modules` 返回的孤立模块列表中的每个模块都不应出现在可达集合中,且可达集合中的每个模块都不应出现在孤立列表中。
**Validates: Requirements 2.8**
### Property 11: 过期引用检测
*对于任意* 文档中提取的代码引用,若该引用指向的文件路径在仓库中不存在,则 `check_reference_validity` 应返回 `False`
**Validates: Requirements 3.3**
### Property 12: 缺失文档检测
*对于任意* 核心代码模块集合和已文档化模块集合,`find_undocumented_modules` 返回的缺失列表应恰好等于核心模块集合与已文档化集合的差集。
**Validates: Requirements 3.5**
### Property 13: 统计摘要一致性
*对于任意* 报告的统计摘要,各分类/标签的计数之和应等于对应条目列表的总长度。
**Validates: Requirements 4.5, 4.6, 4.7**
### Property 14: 报告头部元信息
*对于任意* 报告输出,头部应包含一个符合 ISO 格式的时间戳字符串和仓库根目录路径字符串。
**Validates: Requirements 4.2**
### Property 15: 写操作仅限 docs/audit/
*对于任意* 审计执行过程,所有文件写操作的目标路径应以 `docs/audit/repo/` 为前缀。
**Validates: Requirements 5.2**
### Property 16: 文档对齐报告分区完整性
*对于任意* `render_alignment_report` 的输出Markdown 文本应包含"映射关系"、"过期点"、"冲突点"、"缺失点"四个分区标题。
**Validates: Requirements 3.8**
## 错误处理
| 场景 | 处理方式 |
|------|---------|
| 文件读取权限不足 | 记录警告到报告的"错误"分区,跳过该文件,继续处理 |
| Python 源文件语法错误(`ast.parse` 失败) | 记录警告,将该文件标记为"待确认",不中断流程树构建 |
| 文档中的代码引用格式无法解析 | 跳过该引用,不产生误报 |
| DDL 文件 SQL 语法不规范 | 使用正则提取 `CREATE TABLE` 和列定义,容忍非标准语法 |
| `docs/audit/repo/` 目录创建失败 | 抛出异常并终止,因为无法输出报告 |
| 编码问题(非 UTF-8 文件) | 尝试 `utf-8``gbk``latin-1` 回退读取,记录编码警告 |
## 测试策略
### 测试框架
- 单元测试与属性测试均使用 `pytest`
- 属性测试库:`hypothesis`Python 生态最成熟的属性测试框架)
- 测试文件位于 `tests/unit/test_audit_*.py`
### 单元测试
针对具体示例和边界情况:
- 扫描器对实际仓库子集的遍历结果
- classify 对已知文件路径的分类正确性(如 `tmp/hebing.py` → 临时与调试/候选删除)
- 入口点识别对实际仓库的结果
- DDL 与数据字典的比对结果
- 文件读取失败时的容错行为
- `docs/audit/repo/` 目录不存在时的自动创建
### 属性测试
每个正确性属性对应一个属性测试,使用 `hypothesis` 生成随机输入:
- 每个属性测试至少运行 100 次迭代
- 每个测试用注释标注对应的设计属性编号
- 标注格式:**Feature: repo-audit, Property {N}: {属性标题}**
**生成器策略**
- `FileEntry` 生成器:随机路径(含各种扩展名、目录层级)、随机大小、随机 is_dir/is_empty_dir
- `InventoryItem` 生成器:随机 Category/Disposition 组合、随机描述文本
- `FlowNode` 生成器:随机树结构(限制深度和宽度)
- 文件树生成器:构造临时目录结构用于扫描器测试

View File

@@ -0,0 +1,90 @@
# 需求文档:仓库治理只读审计
## 简介
对飞球 ETL 系统 (etl-billiards) 仓库进行全面的只读审计分析,产出三份结构化报告:文件/目录清单(含处置建议)、项目流程树(从入口到末端逻辑)、文档对齐报告(文档与代码的映射关系)。本阶段不修改任何文件,所有处置决策留待用户逐一确认后再执行。
## 术语表
- **审计脚本 (Audit_Script)**:执行只读分析并生成报告的 Python 脚本集合
- **文件清单 (File_Inventory)**:按用途归类的仓库文件与目录列表,每项附带处置标签
- **处置标签 (Disposition_Tag)**:对文件/目录的处置建议,取值为:保留、候选删除、候选归档、待确认
- **流程树 (Flow_Tree)**:从程序入口出发,沿调用链展开到各子模块/子逻辑的树状结构
- **文档对齐报告 (Doc_Alignment_Report)**:文档与代码之间映射关系的分析报告,包含过期点、冲突点、缺失点
- **入口 (Entry_Point)**:程序的顶层启动点,如 `cli/main.py``gui/main.py``scripts/*.py`
- **ODS/DWD/DWS**:数据仓库三层架构——操作数据存储层/明细数据层/数据服务层
- **SCD2**:缓慢变化维度类型 2维度表的历史版本管理策略
## 需求
### 需求 1文件与目录清单生成
**用户故事:** 作为项目维护者,我希望获得一份按用途归类的仓库文件与目录清单,以便了解每个文件的角色并决定其去留。
#### 验收标准
1. WHEN 审计脚本扫描仓库根目录时THE Audit_Script SHALL 递归遍历所有文件和目录(排除 `.git/``__pycache__/``.pytest_cache/` 等运行时缓存目录)
2. WHEN 审计脚本处理每个文件或目录时THE Audit_Script SHALL 将其归入以下用途分类之一核心代码、配置、数据库定义、测试、文档、脚本工具、GUI、构建与部署、日志与输出、临时与调试、其他
3. WHEN 审计脚本完成归类后THE Audit_Script SHALL 为每个条目分配一个处置标签(保留/候选删除/候选归档/待确认)
4. WHEN 审计脚本生成清单时THE File_Inventory SHALL 包含以下字段:相对路径、用途分类、处置标签、简要说明
5. WHEN 审计脚本遇到空目录(如 `database/Deleded & backup/``scripts/Deleded & backup/`THE Audit_Script SHALL 将其标记为"候选删除"
6. WHEN 审计脚本遇到 `.lnk` 快捷方式文件或 `.rar` 压缩包时THE Audit_Script SHALL 将其标记为"候选删除"
7. WHEN 审计脚本遇到 `tmp/` 目录下的文件时THE Audit_Script SHALL 逐一评估并标记为"候选删除"或"候选归档"
8. WHEN 审计脚本遇到 `logs/``export/` 目录下的运行时产出文件时THE Audit_Script SHALL 将其标记为"候选归档"
9. IF 审计脚本无法确定某文件的用途分类THEN THE Audit_Script SHALL 将其标记为"待确认"并在说明中注明原因
10. WHEN 审计脚本完成清单生成后THE File_Inventory SHALL 以 Markdown 表格格式输出,按用途分类分组排列
### 需求 2项目流程树生成
**用户故事:** 作为项目维护者,我希望获得一份从入口到各子模块的调用流程树,以便理解系统的执行路径和模块依赖关系。
#### 验收标准
1. WHEN 审计脚本分析项目入口时THE Audit_Script SHALL 识别以下入口点:`cli/main.py`CLI 主入口)、`gui/main.py`GUI 主入口)、`scripts/*.py`(运维脚本)、批处理文件(`run_etl.bat``run_gui.bat``run_ods.bat` 等)
2. WHEN 审计脚本从 CLI 入口展开时THE Flow_Tree SHALL 追踪以下调用链CLI 参数解析 → 配置加载 → 调度器初始化 → 任务注册表查询 → 任务执行Extract → Transform → Load→ 加载器调用 → 数据库操作
3. WHEN 审计脚本从 GUI 入口展开时THE Flow_Tree SHALL 追踪以下调用链GUI 主窗口初始化 → 各面板/组件加载 → 后台工作线程 → CLI 命令构建 → 任务执行
4. WHEN 审计脚本分析任务模块时THE Flow_Tree SHALL 区分以下任务类型ODS 抓取任务、DWD 加载任务、DWS 汇总任务、校验任务、Schema 初始化任务
5. WHEN 审计脚本分析加载器模块时THE Flow_Tree SHALL 区分以下加载器类型ODS 通用加载器、维度加载器SCD2、事实表加载器
6. WHEN 审计脚本生成流程树时THE Flow_Tree SHALL 以缩进文本或 Mermaid 图的形式输出,层级深度至少达到函数/方法级别
7. WHEN 审计脚本分析模块依赖时THE Flow_Tree SHALL 标注每个节点所在的源文件路径
8. IF 审计脚本发现存在孤立模块未被任何入口直接或间接引用的代码文件THEN THE Flow_Tree SHALL 在报告末尾单独列出这些孤立模块
### 需求 3文档对齐报告生成
**用户故事:** 作为项目维护者,我希望了解现有文档与代码之间的对齐状况,以便识别过期、冲突和缺失的文档。
#### 验收标准
1. WHEN 审计脚本扫描文档目录时THE Audit_Script SHALL 识别以下文档来源:`docs/` 目录、`README.md``开发笔记/`、各模块内的 `README.md`(如 `gui/README.md``fetch-test/README.md`)、`.kiro/steering/` 下的引导文件
2. WHEN 审计脚本分析每份文档时THE Doc_Alignment_Report SHALL 建立文档与代码模块之间的映射关系
3. WHEN 审计脚本检测到文档引用了已不存在的代码实体函数、类、文件路径THE Doc_Alignment_Report SHALL 将该引用标记为"过期点"
4. WHEN 审计脚本检测到文档描述与代码实际行为不一致时THE Doc_Alignment_Report SHALL 将该处标记为"冲突点"
5. WHEN 审计脚本检测到核心代码模块缺少对应文档时THE Doc_Alignment_Report SHALL 将该模块标记为"缺失点"
6. WHEN 审计脚本分析 DDL 文件(`database/schema_*.sql`THE Doc_Alignment_Report SHALL 检查数据字典文档(`docs/dwd_main_tables_dictionary.md``docs/dws_tables_dictionary.md`)是否覆盖了所有表和字段
7. WHEN 审计脚本分析 `docs/test-json-doc/` 下的 API 响应样本时THE Doc_Alignment_Report SHALL 检查样本字段是否与 ODS 表结构和解析器(`models/parsers.py`)一致
8. WHEN 审计脚本完成分析后THE Doc_Alignment_Report SHALL 以 Markdown 格式输出,包含以下分区:映射关系表、过期点列表、冲突点列表、缺失点列表
### 需求 4报告输出与格式规范
**用户故事:** 作为项目维护者,我希望审计报告以统一、可读的格式输出,以便后续逐项决策和执行。
#### 验收标准
1. THE Audit_Script SHALL 将三份报告输出到 `docs/audit/repo/` 目录下,文件名分别为 `file_inventory.md``flow_tree.md``doc_alignment.md`
2. THE Audit_Script SHALL 在每份报告的头部包含生成时间戳和仓库根目录路径
3. WHEN 报告引用代码标识符类名、函数名、变量名、文件路径THE Audit_Script SHALL 保留英文原文,使用行内代码格式(反引号)
4. WHEN 报告包含说明性文字时THE Audit_Script SHALL 使用简体中文
5. THE Audit_Script SHALL 在文件清单报告末尾附加统计摘要:各用途分类的文件数量、各处置标签的文件数量
6. THE Audit_Script SHALL 在流程树报告末尾附加统计摘要:入口点数量、任务数量、加载器数量、孤立模块数量
7. THE Audit_Script SHALL 在文档对齐报告末尾附加统计摘要:过期点数量、冲突点数量、缺失点数量
### 需求 5只读安全保障
**用户故事:** 作为项目维护者,我希望审计过程不会修改仓库中的任何文件,以确保分析阶段的安全性。
#### 验收标准
1. THE Audit_Script SHALL 仅执行文件系统的读取操作(读取文件内容、列出目录、获取文件元信息)
2. THE Audit_Script SHALL 仅在 `docs/audit/repo/` 目录下创建新文件,该目录为报告专用输出目录
3. IF 审计脚本在执行过程中遇到权限错误或文件读取失败THEN THE Audit_Script SHALL 在报告中记录该错误并继续处理其余文件
4. THE Audit_Script SHALL 在运行前检查 `docs/audit/repo/` 目录是否存在,若不存在则创建该目录

View File

@@ -0,0 +1,118 @@
# 实施计划:仓库治理只读审计
## 概述
将设计文档中的审计脚本拆分为增量式编码任务。每个任务构建在前一个任务之上,最终产出可运行的审计工具集。所有脚本位于 `scripts/audit/` 目录,报告输出到 `docs/audit/repo/`
## 任务
- [x] 1. 搭建审计脚本骨架和数据模型
- [x] 1.1 创建 `scripts/audit/__init__.py` 和数据模型定义
- 定义 `FileEntry` dataclass`rel_path`, `is_dir`, `size_bytes`, `extension`, `is_empty_dir`
- 定义 `Category``Disposition` 枚举
- 定义 `InventoryItem` dataclass
- 定义 `FlowNode` dataclass
- 定义 `DocMapping``AlignmentIssue` dataclass
- _Requirements: 1.2, 1.3, 1.4, 2.7, 3.2, 3.3_
- [x] 1.2 编写 classify 完整性属性测试
- **Property 1: classify 完整性**
- **Validates: Requirements 1.2, 1.3**
- [x] 2. 实现仓库扫描器
- [x] 2.1 创建 `scripts/audit/scanner.py`
- 实现 `EXCLUDED_PATTERNS` 常量和排除匹配逻辑
- 实现 `scan_repo(root, exclude)` 函数:递归遍历文件系统,返回 `list[FileEntry]`
- 处理空目录检测(`is_empty_dir`
- 处理文件读取权限错误(跳过并记录)
- _Requirements: 1.1, 5.1, 5.3_
- [x] 2.2 编写扫描器排除规则属性测试
- **Property 7: 扫描器排除规则**
- **Validates: Requirements 1.1**
- [x] 3. 实现文件清单分析器
- [x] 3.1 创建 `scripts/audit/inventory_analyzer.py`
- 实现 `classify(entry: FileEntry) -> InventoryItem` 函数,包含完整分类规则表
- 实现 `build_inventory(entries) -> list[InventoryItem]` 批量分类函数
- 实现 `render_inventory_report(items, repo_root) -> str` Markdown 渲染函数
- 包含统计摘要生成(各分类/标签计数)
- 注意:需求 1.8 仅覆盖 `logs/``export/` 目录(不含 `reports/`
- _Requirements: 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 1.10, 4.2, 4.5_
- [x] 3.2 编写 classify 分类规则属性测试
- **Property 3: 空目录标记为候选删除**
- **Property 4: .lnk/.rar 文件标记为候选删除**
- **Property 5: tmp/ 下文件处置范围**
- **Property 6: 运行时产出目录标记为候选归档**(仅 `logs/``export/`
- **Validates: Requirements 1.5, 1.6, 1.7, 1.8**
- [x] 3.3 编写清单渲染属性测试
- **Property 2: 清单渲染完整性**
- **Property 8: 清单按分类分组**
- **Validates: Requirements 1.4, 1.10**
- [x] 4. 检查点 - 确保文件清单模块测试通过
- 确保所有测试通过,如有疑问请向用户确认。
- [x] 5. 实现流程树分析器
- [x] 5.1 创建 `scripts/audit/flow_analyzer.py`
- 实现 `parse_imports(filepath)` 函数:使用 `ast` 模块解析 Python 文件的 import 语句
- 实现 `build_flow_tree(repo_root, entry_file)` 函数:从入口递归追踪 import 链
- 实现 `find_orphan_modules(repo_root, all_entries, reachable)` 函数
- 实现 `render_flow_report(trees, orphans, repo_root)` 函数:生成 Mermaid 图和缩进文本
- 包含入口点识别逻辑CLI、GUI、批处理、运维脚本
- 包含任务类型和加载器类型区分逻辑
- 包含统计摘要生成
- _Requirements: 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 2.7, 2.8, 4.6_
- [x] 5.2 编写流程树属性测试
- **Property 9: 流程树节点 source_file 有效性**
- **Property 10: 孤立模块检测正确性**
- **Validates: Requirements 2.7, 2.8**
- [x] 6. 实现文档对齐分析器
- [x] 6.1 创建 `scripts/audit/doc_alignment_analyzer.py`
- 实现 `scan_docs(repo_root)` 函数:扫描所有文档来源
- 实现 `extract_code_references(doc_path)` 函数:从文档提取代码引用
- 实现 `check_reference_validity(ref, repo_root)` 函数
- 实现 `find_undocumented_modules(repo_root, documented)` 函数
- 实现 `check_ddl_vs_dictionary(repo_root)` 函数DDL 与数据字典比对
- 实现 `check_api_samples_vs_parsers(repo_root)` 函数API 样本与解析器比对
- 实现 `render_alignment_report(mappings, issues, repo_root)` 函数
- 包含统计摘要生成
- _Requirements: 3.1, 3.2, 3.3, 3.4, 3.5, 3.6, 3.7, 3.8, 4.7_
- [x] 6.2 编写文档对齐属性测试
- **Property 11: 过期引用检测**
- **Property 12: 缺失文档检测**
- **Property 16: 文档对齐报告分区完整性**
- **Validates: Requirements 3.3, 3.5, 3.8**
- [x] 7. 检查点 - 确保流程树和文档对齐模块测试通过
- 确保所有测试通过,如有疑问请向用户确认。
- [x] 8. 实现审计主入口和报告输出
- [x] 8.1 创建 `scripts/audit/run_audit.py`
- 实现 `run_audit(repo_root)` 主函数:依次调用扫描器和三个分析器
- 实现 `docs/audit/repo/` 目录检查与创建逻辑
- 实现报告头部元信息(时间戳、仓库路径)注入
- 实现三份报告的文件写入
- 添加 `if __name__ == "__main__"` 入口
- _Requirements: 4.1, 4.2, 4.3, 4.4, 5.2, 5.4_
- [x] 8.2 编写报告输出属性测试
- **Property 13: 统计摘要一致性**
- **Property 14: 报告头部元信息**
- **Property 15: 写操作仅限 docs/audit/**
- **Validates: Requirements 4.2, 4.5, 4.6, 4.7, 5.2**
- [x] 9. 最终检查点 - 确保所有测试通过
- 确保所有测试通过,如有疑问请向用户确认。
## 备注
- 标记 `*` 的子任务为可选,可跳过以加速 MVP 交付
- 每个任务引用了具体的需求编号,便于追溯
- 属性测试使用 `hypothesis` 库,每个测试至少 100 次迭代
- 单元测试验证具体示例和边界情况,属性测试验证通用正确性

View File

@@ -0,0 +1 @@
{"generationMode": "requirements-first"}

View File

@@ -0,0 +1,462 @@
# 设计文档ETL 调度器重构
## 概述
本次重构将 `ETLScheduler`(约 900 行,职责混乱的"上帝类")拆分为三层清晰的架构:
1. **CLI 层**`cli/main.py`):参数解析、配置加载、资源创建与释放
2. **PipelineRunner**`orchestration/pipeline_runner.py`):管道定义、层→任务映射、校验编排
3. **TaskExecutor**`orchestration/task_executor.py`):单任务执行、游标管理、运行记录
核心设计原则:**单个任务是最小执行单元,管道模式只是"调度拼接"**。每层通过依赖注入接收协作对象,不自行创建资源,便于独立测试。
## 架构
### 分层架构图
```mermaid
graph TD
CLI["CLI 层<br/>cli/main.py<br/>参数解析 · 配置加载 · 资源管理"]
PR["PipelineRunner<br/>orchestration/pipeline_runner.py<br/>管道定义 · 层→任务映射 · 校验编排"]
TE["TaskExecutor<br/>orchestration/task_executor.py<br/>单任务执行 · 游标管理 · 运行记录"]
TR["TaskRegistry<br/>orchestration/task_registry.py<br/>任务注册 · 元数据查询"]
CM["CursorManager"]
RT["RunTracker"]
DB["DatabaseConnection"]
API["APIClient"]
CLI -->|"创建并注入"| PR
CLI -->|"创建并注入"| TE
CLI -->|"管理生命周期"| DB
CLI -->|"管理生命周期"| API
PR -->|"委托执行"| TE
PR -->|"查询任务"| TR
TE -->|"查询元数据"| TR
TE -->|"管理游标"| CM
TE -->|"记录运行"| RT
TE -->|"使用"| DB
TE -->|"使用"| API
```
### 调用流程
**传统模式**`--tasks`
```
CLI → TaskExecutor.run_tasks([task_codes]) → TaskExecutor._run_single_task() × N
```
**管道模式**`--pipeline`
```
CLI → PipelineRunner.run(pipeline, processing_mode, ...)
→ PipelineRunner._resolve_tasks(layers)
→ TaskExecutor.run_tasks([resolved_tasks])
→ [可选] PipelineRunner._run_verification(layers, ...)
```
## 组件与接口
### TaskExecutor
负责单任务执行的完整生命周期。从原 `ETLScheduler` 中提取 `_run_single_task``_execute_fetch``_execute_ingest``_execute_ods_record_and_load``_run_utility_task` 等方法。
```python
class TaskExecutor:
def __init__(
self,
config: AppConfig,
db_ops: DatabaseOperations,
api_client: APIClient,
cursor_mgr: CursorManager,
run_tracker: RunTracker,
task_registry: TaskRegistry,
logger: logging.Logger,
):
...
def run_tasks(
self,
task_codes: list[str],
data_source: str = "hybrid", # online / offline / hybrid
) -> list[dict[str, Any]]:
"""批量执行任务列表,返回每个任务的结果。"""
...
def run_single_task(
self,
task_code: str,
run_uuid: str,
store_id: int,
data_source: str = "hybrid",
) -> dict[str, Any]:
"""执行单个任务的完整生命周期。"""
...
```
关键变化:
- `data_source` 作为显式参数传入,不再读取 `self.pipeline_flow` 全局状态
- 工具类任务判断通过 `TaskRegistry.get_metadata(task_code)` 查询,不再硬编码
- 不自行创建 `DatabaseConnection``APIClient`
### PipelineRunner
负责管道编排。从原 `ETLScheduler` 中提取 `run_pipeline_with_verification``_run_layer_verification``_get_tasks_for_layers` 等方法。
```python
class PipelineRunner:
# 管道定义(从 scheduler.py 模块级常量迁移至此)
PIPELINE_LAYERS: dict[str, list[str]] = {
"api_ods": ["ODS"],
"api_ods_dwd": ["ODS", "DWD"],
"api_full": ["ODS", "DWD", "DWS", "INDEX"],
"ods_dwd": ["DWD"],
"dwd_dws": ["DWS"],
"dwd_dws_index": ["DWS", "INDEX"],
"dwd_index": ["INDEX"],
}
def __init__(
self,
config: AppConfig,
task_executor: TaskExecutor,
task_registry: TaskRegistry,
db_conn: DatabaseConnection,
api_client: APIClient,
logger: logging.Logger,
):
...
def run(
self,
pipeline: str,
processing_mode: str = "increment_only",
data_source: str = "hybrid",
window_start: datetime | None = None,
window_end: datetime | None = None,
window_split: str | None = None,
task_codes: list[str] | None = None,
fetch_before_verify: bool = False,
verify_tables: list[str] | None = None,
) -> dict[str, Any]:
"""执行管道,返回汇总结果。"""
...
def _resolve_tasks(self, layers: list[str]) -> list[str]:
"""根据层列表解析任务代码,优先查询 TaskRegistry 元数据。"""
...
def _run_verification(self, layers, window_start, window_end, ...):
"""执行后置校验(从原 _run_layer_verification 迁移)。"""
...
```
### TaskRegistry增强
在现有注册功能基础上增加元数据支持。
```python
@dataclass
class TaskMeta:
"""任务元数据"""
task_class: type
requires_db_config: bool = True
layer: str | None = None # "ODS" / "DWD" / "DWS" / "INDEX" / None
task_type: str = "etl" # "etl" / "utility" / "verification"
class TaskRegistry:
def __init__(self):
self._tasks: dict[str, TaskMeta] = {}
def register(
self,
task_code: str,
task_class: type,
requires_db_config: bool = True,
layer: str | None = None,
task_type: str = "etl",
):
"""注册任务类及其元数据。"""
self._tasks[task_code.upper()] = TaskMeta(
task_class=task_class,
requires_db_config=requires_db_config,
layer=layer,
task_type=task_type,
)
def create_task(self, task_code, config, db_connection, api_client, logger):
"""创建任务实例(保持原有接口不变)。"""
...
def get_metadata(self, task_code: str) -> TaskMeta | None:
"""查询任务元数据。"""
...
def get_tasks_by_layer(self, layer: str) -> list[str]:
"""获取指定层的所有任务代码。"""
...
def is_utility_task(self, task_code: str) -> bool:
"""判断是否为工具类任务(不需要游标/运行记录)。"""
meta = self.get_metadata(task_code)
return meta is not None and not meta.requires_db_config
def get_all_task_codes(self) -> list[str]:
"""获取所有已注册的任务代码(保持原有接口)。"""
...
```
### CLI 层重构
```python
# cli/main.py 核心流程伪代码
def main():
args = parse_args()
config = AppConfig.load(build_cli_overrides(args))
# 资源创建
db_conn = DatabaseConnection(...)
api_client = APIClient(...)
try:
# 组装依赖
db_ops = DatabaseOperations(db_conn)
cursor_mgr = CursorManager(db_conn)
run_tracker = RunTracker(db_conn)
registry = default_registry
executor = TaskExecutor(config, db_ops, api_client, cursor_mgr, run_tracker, registry, logger)
if args.pipeline:
runner = PipelineRunner(config, executor, registry, db_conn, api_client, logger)
runner.run(
pipeline=args.pipeline,
processing_mode=args.processing_mode,
data_source=resolve_data_source(args),
...
)
else:
task_codes = config.get("run.tasks")
data_source = resolve_data_source(args)
executor.run_tasks(task_codes, data_source=data_source)
finally:
db_conn.close()
```
### 参数映射
| 旧参数 | 旧值 | 新参数 | 新值 | 说明 |
|--------|------|--------|------|------|
| `--pipeline-flow` | `FULL` | `--data-source` | `hybrid` | 在线抓取 + 本地入库 |
| `--pipeline-flow` | `FETCH_ONLY` | `--data-source` | `online` | 仅在线抓取落盘 |
| `--pipeline-flow` | `INGEST_ONLY` | `--data-source` | `offline` | 仅本地清洗入库 |
### 静态方法归位
| 方法 | 原位置 | 新位置 | 理由 |
|------|--------|--------|------|
| `_map_run_status` | `ETLScheduler` | `RunTracker` | 状态映射是运行记录的职责 |
| `_filter_verify_tables` | `ETLScheduler` | `tasks/verification/` 模块 | 校验表过滤是校验模块的职责 |
## 数据模型
### TaskMeta新增
```python
@dataclass
class TaskMeta:
task_class: type # 任务类引用
requires_db_config: bool = True # 是否需要数据库任务配置(游标/运行记录)
layer: str | None = None # 所属层:"ODS"/"DWD"/"DWS"/"INDEX"/None
task_type: str = "etl" # 任务类型:"etl"/"utility"/"verification"
```
### DataSource 枚举
```python
class DataSource(str, Enum):
ONLINE = "online" # 仅在线抓取(原 FETCH_ONLY
OFFLINE = "offline" # 仅本地入库(原 INGEST_ONLY
HYBRID = "hybrid" # 抓取 + 入库(原 FULL
```
### 配置键映射
| 旧键 | 新键 | 默认值 |
|------|------|--------|
| `app.timezone` | `app.timezone` | `Asia/Shanghai`(原 `Asia/Shanghai` |
| `pipeline.flow` | `run.data_source` | `hybrid` |
| `pipeline.fetch_root` | `io.fetch_root` | `export/JSON` |
| `pipeline.ingest_source_dir` | `io.ingest_source_dir` | `""` |
### 任务执行结果(不变)
```python
# 单任务结果
{
"task_code": str,
"status": str, # "SUCCESS" / "FAIL" / "SKIP"
"counts": {
"fetched": int,
"inserted": int,
"updated": int,
"skipped": int,
"errors": int,
},
"window": {"start": datetime, "end": datetime, "minutes": int} | None,
"dump_dir": str | None,
}
# 管道结果
{
"status": str,
"pipeline": str,
"layers": list[str],
"results": list[dict], # 各任务结果
"verification_summary": dict | None, # 校验汇总
}
```
## 正确性属性
*正确性属性是一种在系统所有有效执行中都应成立的特征或行为——本质上是对系统应做什么的形式化陈述。属性是人类可读规格与机器可验证正确性保证之间的桥梁。*
### Property 1data_source 参数决定执行路径
*对于任意* 任务代码和任意 `data_source`online/offline/hybridTaskExecutor 执行该任务时,抓取阶段执行当且仅当 `data_source``online``hybrid`,入库阶段执行当且仅当 `data_source``offline``hybrid`
**验证:需求 1.2**
### Property 2成功任务推进游标
*对于任意* 非工具类任务,当任务执行成功且返回包含有效 `window`(含 `start``end`的结果时CursorManager.advance 应被调用且参数与返回的窗口一致。
**验证:需求 1.3**
### Property 3失败任务标记 FAIL 并重新抛出
*对于任意* 非工具类任务当任务执行过程中抛出异常时RunTracker 应被更新为 FAIL 状态,且该异常应被重新抛出给调用方。
**验证:需求 1.4**
### Property 4工具类任务由元数据决定
*对于任意* 任务代码TaskExecutor 是否跳过游标管理和运行记录,取决于 TaskRegistry 中该任务的 `requires_db_config` 元数据。当 `requires_db_config=False` 时跳过,否则执行完整生命周期。
**验证:需求 1.6, 4.2**
### Property 5管道名称→层列表映射
*对于任意* 有效的管道名称PipelineRunner 解析出的层列表应与 `PIPELINE_LAYERS` 字典中的定义完全一致。
**验证:需求 2.1**
### Property 6processing_mode 控制执行流程
*对于任意* processing_mode 值,增量 ETL 执行当且仅当模式包含 `increment`(即 `increment_only``increment_verify`),校验流程执行当且仅当模式包含 `verify`(即 `verify_only``increment_verify`)。
**验证:需求 2.3, 2.4**
### Property 7管道结果汇总完整性
*对于任意* 一组任务执行结果PipelineRunner 返回的汇总字典应包含 `status``pipeline``layers``results` 字段,且 `results` 列表长度等于实际执行的任务数。
**验证:需求 2.6**
### Property 8TaskRegistry 元数据 round-trip
*对于任意* 任务代码、任务类和元数据组合requires_db_config、layer、task_type注册后通过 `get_metadata` 查询应返回相同的元数据值。
**验证:需求 4.1**
### Property 9TaskRegistry 向后兼容默认值
*对于任意* 使用旧接口(仅 task_code 和 task_class注册的任务查询元数据应返回 `requires_db_config=True``layer=None``task_type="etl"`
**验证:需求 4.4**
### Property 10按层查询任务
*对于任意* 注册了 `layer` 元数据的任务集合,`get_tasks_by_layer(layer)` 返回的任务代码集合应等于所有 `layer` 匹配的已注册任务代码集合。
**验证:需求 4.3**
### Property 11pipeline_flow → data_source 映射一致性
*对于任意*`pipeline_flow`FULL/FETCH_ONLY/INGEST_ONLY映射到 `data_source` 的结果应与预定义映射表一致FULL→hybrid、FETCH_ONLY→online、INGEST_ONLY→offline。同样配置键 `pipeline.flow` 应自动映射到 `run.data_source`
**验证:需求 8.1, 8.2, 8.3, 5.2, 8.4**
## 错误处理
### TaskExecutor 错误处理
- 任务执行异常:更新 RunTracker 状态为 FAIL含 error_message然后重新抛出异常
- 游标推进失败:记录错误日志,不影响任务结果(任务本身已成功)
- 任务配置不存在:返回 `{"status": "SKIP"}` 结果,不抛异常
### PipelineRunner 错误处理
- 单个任务失败:记录错误,继续执行后续任务(与当前行为一致)
- 校验框架未安装:返回 `{"status": "SKIPPED"}` 并记录警告
- 无效管道名称:抛出 `ValueError`
### CLI 错误处理
- 配置加载失败:`SystemExit` 并输出错误信息
- 资源创建失败:`SystemExit` 并输出错误信息
- 执行过程异常:记录错误日志,`finally` 块确保资源释放,返回非零退出码
### 弃用警告
- 使用 Python `warnings.warn(DeprecationWarning)` 发出弃用警告
- 同时在日志中记录映射详情,便于运维排查
## 测试策略
### 单元测试
使用 `pytest` + 现有的 `FakeDB`/`FakeAPI` 测试工具(`tests/unit/task_test_utils.py`)。
**TaskExecutor 测试**
- 注入 mock 依赖FakeDB、FakeAPI、mock CursorManager、mock RunTracker
- 验证成功/失败/跳过三种路径
- 验证工具类任务不触发游标/运行记录
- 验证 data_source 参数正确控制抓取/入库阶段
**PipelineRunner 测试**
- 注入 mock TaskExecutor
- 验证不同 processing_mode 下的执行流程
- 验证管道→层→任务的解析链
**TaskRegistry 测试**
- 验证元数据注册和查询
- 验证向后兼容(无元数据注册)
- 验证按层查询
**配置兼容性测试**
- 验证旧键→新键映射
- 验证优先级规则
- 验证默认值变更
### 属性测试
使用 `hypothesis` 库进行属性测试,每个属性至少运行 100 次迭代。
每个属性测试必须用注释标注对应的设计属性编号:
```python
# Feature: scheduler-refactor, Property 8: TaskRegistry 元数据 round-trip
```
**属性测试覆盖**
- Property 1: data_source 参数决定执行路径
- Property 2: 成功任务推进游标
- Property 3: 失败任务标记 FAIL 并重新抛出
- Property 4: 工具类任务由元数据决定
- Property 5: 管道名称→层列表映射
- Property 6: processing_mode 控制执行流程
- Property 7: 管道结果汇总完整性
- Property 8: TaskRegistry 元数据 round-trip
- Property 9: TaskRegistry 向后兼容默认值
- Property 10: 按层查询任务
- Property 11: pipeline_flow → data_source 映射一致性

View File

@@ -0,0 +1,123 @@
# 需求文档ETL 调度器重构
## 简介
当前 `orchestration/scheduler.py`(约 900 行)中的 `ETLScheduler` 类承担了过多职责单任务执行、管道编排、资源管理。CLI 参数命名混乱(`--pipeline` vs `--pipeline-flow` vs `--processing-mode`全局状态耦合严重配置键语义重叠。本次重构将调度器拆分为三层架构CLI → PipelineRunner → TaskExecutor重新设计参数命名消除全局状态依赖使每层可独立测试。
## 术语表
- **TaskExecutor**:任务执行器,负责单个 ETL 任务的执行、游标管理和运行记录
- **PipelineRunner**:管道运行器,负责管道定义、层→任务映射、校验编排
- **TaskRegistry**:任务注册表,管理所有已注册的任务类及其元数据
- **DataSource**:数据源模式,取代原 `pipeline.flow`,表示数据来自在线 API`online`)、本地 JSON`offline`)或混合模式(`hybrid`
- **ProcessingMode**:处理模式,控制 ETL 执行策略(仅增量 / 仅校验 / 增量+校验)
- **Pipeline**:管道,定义一组按层顺序执行的 ETL 任务集合(如 `api_full` = ODS → DWD → DWS → INDEX
- **CursorManager**:游标管理器,管理任务的时间水位(上次处理到哪里)
- **RunTracker**:运行记录器,在 `etl_admin` Schema 中记录每次任务执行的状态和统计
## 需求
### 需求 1架构分层 — TaskExecutor执行层
**用户故事:** 作为开发者,我希望单任务执行逻辑独立封装在 TaskExecutor 中,以便可以脱离管道上下文独立测试和复用。
#### 验收标准
1. THE TaskExecutor SHALL 封装单个任务的完整执行生命周期:创建运行记录、执行任务、更新游标、记录结果
2. WHEN TaskExecutor 执行一个任务时THE TaskExecutor SHALL 接收显式的 `data_source` 参数,而非读取全局状态
3. WHEN 任务执行成功且返回有效时间窗口时THE TaskExecutor SHALL 推进该任务的游标水位
4. WHEN 任务执行过程中发生异常时THE TaskExecutor SHALL 将运行记录状态更新为 FAIL 并重新抛出异常
5. THE TaskExecutor SHALL 通过构造函数接收 `db_ops``api_client``cursor_manager``run_tracker``task_registry` 等依赖,而非自行创建
6. WHEN 执行工具类任务(如 INIT_ODS_SCHEMATHE TaskExecutor SHALL 跳过游标管理和运行记录,直接执行任务
### 需求 2架构分层 — PipelineRunner编排层
**用户故事:** 作为开发者,我希望管道编排逻辑独立封装在 PipelineRunner 中,以便管道定义和校验流程可以独立演进。
#### 验收标准
1. THE PipelineRunner SHALL 根据管道名称解析出需要执行的层列表(如 `api_full``["ODS", "DWD", "DWS", "INDEX"]`
2. WHEN PipelineRunner 执行管道时THE PipelineRunner SHALL 委托 TaskExecutor 逐个执行任务,而非直接操作数据库或 API
3. WHEN 处理模式为 `verify_only`THE PipelineRunner SHALL 跳过增量 ETL仅执行校验流程
4. WHEN 处理模式为 `increment_verify`THE PipelineRunner SHALL 先执行增量 ETL再执行校验流程
5. THE PipelineRunner SHALL 根据层列表自动选择对应的任务代码,支持配置覆盖
6. WHEN 管道执行完成时THE PipelineRunner SHALL 汇总所有任务的执行结果并返回统一的结果字典
### 需求 3架构分层 — CLI 层重构
**用户故事:** 作为运维人员,我希望 CLI 参数命名清晰、语义无歧义,以便快速理解和正确使用各种执行模式。
#### 验收标准
1. THE CLI SHALL 将 `--pipeline-flow`FULL/FETCH_ONLY/INGEST_ONLY重命名为 `--data-source`online/offline/hybrid并保留旧名称作为别名
2. THE CLI SHALL 保留 `--pipeline` 参数用于管道模式,保留 `--tasks` 参数用于传统模式
3. WHEN 用户同时指定 `--pipeline``--tasks`THE CLI SHALL 将 `--tasks` 作为管道内的任务过滤器
4. THE CLI SHALL 保留 `--processing-mode`increment_only/verify_only/increment_verify参数不变
5. WHEN 用户使用旧参数名 `--pipeline-flow`THE CLI SHALL 发出弃用警告并将值映射到新的 `--data-source` 参数
6. THE CLI SHALL 仅负责参数解析和配置加载,将执行逻辑委托给 PipelineRunner 或 TaskExecutor
### 需求 4任务分类元数据化
**用户故事:** 作为开发者,我希望任务的分类信息(是否需要数据库配置、所属层等)由任务注册表管理,而非硬编码在调度器中。
#### 验收标准
1. THE TaskRegistry SHALL 支持在注册任务时附带元数据(`requires_db_config``layer``task_type`
2. WHEN TaskExecutor 需要判断任务是否为工具类任务时THE TaskExecutor SHALL 查询 TaskRegistry 的元数据,而非检查硬编码集合
3. WHEN PipelineRunner 需要根据层获取任务列表时THE PipelineRunner SHALL 查询 TaskRegistry 的 `layer` 元数据
4. THE TaskRegistry SHALL 保持向后兼容,无元数据的任务默认为 `requires_db_config=True``layer=None`
### 需求 5配置键重构
**用户故事:** 作为运维人员,我希望配置键命名合理、语义清晰,以便正确配置 ETL 系统的运行参数。
#### 验收标准
1. THE AppConfig SHALL 将 `app.timezone` 默认值从 `Asia/Shanghai` 改为 `Asia/Shanghai`
2. THE AppConfig SHALL 将 `pipeline.flow` 配置键重命名为 `run.data_source`,并保留旧键作为兼容别名
3. WHEN 配置中同时存在旧键 `pipeline.flow` 和新键 `run.data_source`THE AppConfig SHALL 优先使用新键的值
4. THE AppConfig SHALL 将 `pipeline.fetch_root``pipeline.ingest_source_dir` 移至 `io` 命名空间下(`io.fetch_root``io.ingest_source_dir`
### 需求 6资源管理与生命周期
**用户故事:** 作为开发者,我希望数据库连接和 API 客户端的创建与关闭由 CLI 层统一管理,以便确保资源正确释放。
#### 验收标准
1. THE CLI SHALL 在 `finally` 块中关闭数据库连接和 API 客户端,确保异常情况下资源也能释放
2. THE TaskExecutor SHALL 通过依赖注入接收已创建的数据库连接和 API 客户端,而非自行创建
3. THE PipelineRunner SHALL 通过依赖注入接收已创建的数据库连接和 API 客户端,而非自行创建
4. WHEN CLI 创建资源时THE CLI SHALL 使用 Python 上下文管理器(`with` 语句)或 `try/finally` 模式管理生命周期
### 需求 7静态方法归位
**用户故事:** 作为开发者,我希望与调度器无关的静态工具方法移至合适的模块,以便保持类的职责单一。
#### 验收标准
1. THE `_map_run_status` 方法 SHALL 从 ETLScheduler 移至 RunTracker 或独立的工具模块
2. THE `_filter_verify_tables` 方法 SHALL 从 ETLScheduler 移至校验相关模块
3. WHEN 静态方法被移动后THE 原调用方 SHALL 更新导入路径以引用新位置
### 需求 8向后兼容与过渡
**用户故事:** 作为运维人员,我希望重构后的系统在过渡期内兼容旧的 CLI 参数和配置键,以便平滑迁移。
#### 验收标准
1. WHEN 用户使用旧参数 `--pipeline-flow FULL`THE CLI SHALL 将其等价映射为 `--data-source hybrid` 并发出弃用警告
2. WHEN 用户使用旧参数 `--pipeline-flow FETCH_ONLY`THE CLI SHALL 将其等价映射为 `--data-source online` 并发出弃用警告
3. WHEN 用户使用旧参数 `--pipeline-flow INGEST_ONLY`THE CLI SHALL 将其等价映射为 `--data-source offline` 并发出弃用警告
4. WHEN 配置文件中使用旧键 `pipeline.flow`THE AppConfig SHALL 自动映射到新键 `run.data_source`
5. THE 系统 SHALL 在日志中记录所有弃用映射,便于运维人员逐步迁移
### 需求 9可测试性
**用户故事:** 作为开发者,我希望重构后的每一层都可以独立进行单元测试,以便快速验证逻辑正确性。
#### 验收标准
1. THE TaskExecutor SHALL 支持通过注入 mock 依赖FakeDB、FakeAPI进行单元测试无需真实数据库
2. THE PipelineRunner SHALL 支持通过注入 mock TaskExecutor 进行单元测试,无需执行真实任务
3. THE TaskRegistry SHALL 支持在测试中创建独立实例,不依赖全局 `default_registry`
4. WHEN 运行单元测试时THE 测试 SHALL 验证各层之间的交互契约(调用参数、返回值格式)

View File

@@ -0,0 +1,147 @@
# 实现计划ETL 调度器重构
## 概述
`ETLScheduler`~900 行)拆分为 TaskExecutor执行层、PipelineRunner编排层、增强版 TaskRegistry元数据重构 CLI 参数和配置键,保持向后兼容。采用自底向上的实现顺序:先基础组件,再上层编排,最后 CLI 集成。
## 任务
- [x] 1. 增强 TaskRegistry支持元数据注册与查询
- [x] 1.1 扩展 TaskRegistry 类,添加 TaskMeta 数据类和元数据相关方法
-`orchestration/task_registry.py` 中添加 `TaskMeta` dataclass`task_class``requires_db_config``layer``task_type`
- 修改 `register()` 方法签名,增加可选的 `requires_db_config``layer``task_type` 参数
- 添加 `get_metadata()``get_tasks_by_layer()``is_utility_task()` 方法
- 保持 `create_task()``get_all_task_codes()` 接口不变
- _需求: 4.1, 4.4_
- [x] 1.2 更新所有任务注册调用,添加元数据
- 将原 `NO_DB_CONFIG_TASKS` 硬编码集合中的任务标记为 `requires_db_config=False`
- 为 ODS 任务添加 `layer="ODS"`DWD 任务添加 `layer="DWD"`DWS 任务添加 `layer="DWS"`INDEX 任务添加 `layer="INDEX"`
- 工具类任务标记 `task_type="utility"`,校验类任务标记 `task_type="verification"`
- _需求: 4.1, 4.2, 4.3_
- [x] 1.3 编写 TaskRegistry 属性测试
- **Property 8: TaskRegistry 元数据 round-trip**
- **验证: 需求 4.1**
- [x] 1.4 编写 TaskRegistry 向后兼容和按层查询属性测试
- **Property 9: TaskRegistry 向后兼容默认值**
- **Property 10: 按层查询任务**
- **验证: 需求 4.4, 4.3**
- [x] 2. 配置键重构与向后兼容
- [x] 2.1 修改 `config/defaults.py` 默认值
-`app.timezone` 默认值从 `Asia/Shanghai` 改为 `Asia/Shanghai`
-`db.session.timezone` 默认值从 `Asia/Shanghai` 改为 `Asia/Shanghai`
- 添加 `run.data_source` 键(默认 `hybrid`
-`pipeline.fetch_root``pipeline.ingest_source_dir` 复制到 `io.fetch_root``io.ingest_source_dir`(保留旧键兼容)
- _需求: 5.1, 5.2, 5.4_
- [x] 2.2 在 `config/settings.py``_normalize()` 中添加兼容映射逻辑
- 旧键 `pipeline.flow` → 新键 `run.data_source`值映射FULL→hybrid, FETCH_ONLY→online, INGEST_ONLY→offline
- 旧键 `pipeline.fetch_root``io.fetch_root``pipeline.ingest_source_dir``io.ingest_source_dir`
- 新键优先:当新旧键同时存在时,使用新键的值
- 记录弃用警告日志
- _需求: 5.2, 5.3, 5.4, 8.4, 8.5_
- [x] 2.3 编写配置映射属性测试
- **Property 11: pipeline_flow → data_source 映射一致性**
- **验证: 需求 8.1, 8.2, 8.3, 5.2, 8.4**
- [x] 3. 静态方法归位
- [x] 3.1 将 `_map_run_status` 移至 RunTracker
-`orchestration/run_tracker.py` 中添加 `map_run_status()` 静态方法(从 `ETLScheduler._map_run_status` 复制)
- _需求: 7.1_
- [x] 3.2 将 `_filter_verify_tables` 移至校验模块
-`tasks/verification/` 下合适的模块中添加 `filter_verify_tables()` 函数
- _需求: 7.2_
- [x] 4. 检查点 — 确保所有测试通过
- 运行 `pytest tests/unit`,确保所有测试通过,如有问题请询问用户。
- [x] 5. 实现 TaskExecutor执行层
- [x] 5.1 创建 `orchestration/task_executor.py`
- 实现 `TaskExecutor` 类,构造函数接收 `config``db_ops``api_client``cursor_mgr``run_tracker``task_registry``logger`
-`ETLScheduler` 迁移以下方法:`run_tasks``_run_single_task``_execute_fetch``_execute_ingest``_execute_ods_record_and_load``_run_utility_task``_build_fetch_dir``_resolve_ingest_source``_counts_from_fetch``_load_task_config``_maybe_run_integrity_check``_attach_run_file_logger`
-`data_source` 改为方法参数(替代原 `self.pipeline_flow` 全局状态)
- 使用 `self.task_registry.is_utility_task()` 替代硬编码的 `NO_DB_CONFIG_TASKS`
- 使用 `RunTracker.map_run_status()` 替代 `self._map_run_status()`
- 添加 `DataSource` 枚举类(`online`/`offline`/`hybrid`
- _需求: 1.1, 1.2, 1.3, 1.4, 1.5, 1.6_
- [x] 5.2 编写 TaskExecutor 属性测试
- **Property 1: data_source 参数决定执行路径**
- **Property 2: 成功任务推进游标**
- **Property 3: 失败任务标记 FAIL 并重新抛出**
- **Property 4: 工具类任务由元数据决定**
- **验证: 需求 1.2, 1.3, 1.4, 1.6, 4.2**
- [x] 6. 实现 PipelineRunner编排层
- [x] 6.1 创建 `orchestration/pipeline_runner.py`
- 实现 `PipelineRunner` 类,构造函数接收 `config``task_executor``task_registry``db_conn``api_client``logger`
-`PIPELINE_LAYERS` 常量从 `scheduler.py` 迁移至此
-`ETLScheduler` 迁移以下方法:`run_pipeline_with_verification`(重命名为 `run`)、`_run_layer_verification`(重命名为 `_run_verification`)、`_get_tasks_for_layers`(重命名为 `_resolve_tasks`
- 使用 `filter_verify_tables()`(已移至校验模块)替代原内联静态方法
- 使用 `task_registry.get_tasks_by_layer()` 作为默认任务解析,配置覆盖优先
- _需求: 2.1, 2.2, 2.3, 2.4, 2.5, 2.6_
- [x] 6.2 编写 PipelineRunner 属性测试
- **Property 5: 管道名称→层列表映射**
- **Property 6: processing_mode 控制执行流程**
- **Property 7: 管道结果汇总完整性**
- **验证: 需求 2.1, 2.3, 2.4, 2.6**
- [x] 7. 检查点 — 确保所有测试通过
- 运行 `pytest tests/unit`,确保所有测试通过,如有问题请询问用户。
- [x] 8. 重构 CLI 层
- [x] 8.1 重构 `cli/main.py` 参数解析
- 添加 `--data-source` 参数choices: online/offline/hybrid默认 hybrid
- 保留 `--pipeline-flow` 作为弃用别名,使用时发出 `DeprecationWarning` 并映射到 `--data-source`
- 更新 `build_cli_overrides()``--data-source` 写入 `run.data_source` 配置键
- _需求: 3.1, 3.5, 8.1, 8.2, 8.3_
- [x] 8.2 重构 `cli/main.py``main()` 函数
-`try/finally` 块中管理 `DatabaseConnection``APIClient` 的生命周期
-`try` 块内组装 `TaskExecutor``PipelineRunner`(依赖注入)
- 管道模式委托 `PipelineRunner.run()`,传统模式委托 `TaskExecutor.run_tasks()`
- 添加 `resolve_data_source(args)` 辅助函数处理新旧参数映射
- _需求: 3.2, 3.3, 3.4, 3.6, 6.1, 6.4_
- [x] 8.3 编写 CLI 参数解析单元测试
- 测试 `--data-source` 新参数正确解析
- 测试 `--pipeline-flow` 旧参数弃用映射
- 测试 `--pipeline` + `--tasks` 同时使用时的行为
- _需求: 3.1, 3.3, 3.5_
- [x] 9. 清理旧代码与集成
- [x] 9.1 重构 `orchestration/scheduler.py` 为薄包装层
-`ETLScheduler` 改为薄包装,内部委托 `TaskExecutor``PipelineRunner`
- 保留 `ETLScheduler` 类名和 `run_tasks()``run_pipeline_with_verification()``close()` 公共接口,标记为弃用
- 确保 GUI 层(`gui/workers/`)等现有调用方无需立即修改
- _需求: 8.1, 8.4_
- [x] 9.2 更新 GUI 工作线程中的调度器引用
- 检查 `gui/workers/` 中对 `ETLScheduler` 的使用
- 如有直接引用内部方法,更新为使用新的公共接口
- _需求: 7.3_
- [x] 9.3 编写集成测试验证端到端流程
- 使用 FakeDB/FakeAPI 验证 CLI → PipelineRunner → TaskExecutor 完整调用链
- 验证传统模式和管道模式均正常工作
- _需求: 9.4_
- [x] 10. 最终检查点 — 确保所有测试通过
- 运行 `pytest tests/unit`,确保所有测试通过,如有问题请询问用户。
## 备注
- 标记 `*` 的子任务为可选测试任务,可跳过以加速 MVP
- 每个任务引用了具体的需求编号,确保可追溯性
- 检查点确保增量验证,避免问题累积
- 属性测试使用 `hypothesis` 库,验证通用正确性属性
- 单元测试验证具体示例和边界条件
- `ETLScheduler` 保留为薄包装层,确保 GUI 等现有调用方平滑过渡

22
.kiro/steering/db-docs.md Normal file
View File

@@ -0,0 +1,22 @@
---
inclusion: fileMatch
fileMatchPattern:
- "**/migrations/**/*.*"
- "**/*.sql"
- "**/*schema*.*"
- "**/*ddl*.*"
- "**/*.prisma"
---
# Database Schema Documentation Rules
当你修改任何可能影响 PostgreSQL schema/表结构的内容时(迁移脚本/DDL/表定义/ORM 模型):
1) 必须同步更新 BD 手册目录:
docs/database
2) 文档最低要求:
- 变更说明:新增/修改/删除的表、字段、约束、索引
- 兼容性:对 ETL、后端 API、小程序字段映射的影响
- 回滚策略如何撤销DDL 回滚 / 数据回填)
- 验证步骤:最少包含 3 条校验 SQL

View File

@@ -0,0 +1,27 @@
---
inclusion: always
---
# GovernanceLite
## 目的
在“上下文压缩很致命”的前提下,保留最小硬约束:**任何逻辑改动必须可追溯、可验证、可回滚**。
## 何时必须审计Audit Required
满足任一即视为“高风险”,必须运行 `/audit`
- 改动文件命中:`api/``cli/``config/``database/``loaders/``models/``orchestration/``scd/``tasks/``utils/` 或根目录散文件
- 出现 DB schema / migration / `*.sql` / `*.prisma` 结构性变更
- 业务口径/资金精度与舍入/数据清洗聚合映射/API 契约/鉴权权限/调度游标逻辑发生变化
## 执行方式(自动判定 + 半自动执行)
- 系统会在你提交 prompt 时自动判定“是否待审计”,并在与 Kiro 交互结束时提醒15 分钟限频)
- 用户将手动触发 `/audit`Manual: Run /audit hook由 **audit-writer 子代理**执行重型写入
- 主对话只接收“极短回执”done + files_written + next_step避免审计细节淹没上下文
## 审计产物(由 /audit 生成)
- `docs/audit/changes/<YYYY-MM-DD>__<slug>.md`
- 每个被改文件内:`AI_CHANGELOG`
- 每处逻辑变更附近:`CHANGE` 注释
- DB schema 变更:同步 `docs/database/`
(详细模板/清单/流程见 skills`steering-readme-maintainer``change-annotation-audit``bd-manual-db-docs`

View File

@@ -0,0 +1,18 @@
---
inclusion: always
---
# 语言与编码规范(强制)
## 输出语言
- 默认所有“说明性文字”一律使用简体中文对话回复、文档内容、代码注释、README/ADR/变更说明等)。
- 允许保留英文的部分:
- 代码标识符(类名/函数名/变量名/接口名/库名/命令名)不翻译
- 第三方工具的原始 CLI 输出/报错原文不篡改(可在原文后补充中文解释)
## 文档与注释
- 新增/修改的文档必须与代码变更同步更新
- 注释只写“为什么/边界/假设”,避免复述代码
## 编码与字符集
- 仓库内所有文本文件统一 UTF-8无 BOM。
- 禁止出现 GBK/Big5 混用;若发现历史文件,先转码再重构

22
.kiro/steering/product.md Normal file
View File

@@ -0,0 +1,22 @@
# 产品概述
飞球 ETL 系统 (etl-billiards) — 面向台球门店业务的数据仓库 ETL 管线。
## 功能
- 从上游 SaaS API 抽取运营数据(订单、支付、会员、助教、库存等)
- 原始数据落地 **ODS**(操作数据存储层),保留源 payload 便于回溯
- 清洗装载至 **DWD**(明细数据层),维度走 SCD2事实按时间增量
- 汇总至 **DWS**数据服务层助教业绩、财务日报、会员分析、工资计算、自定义指数算法WBI/NCI/RS/OS/MS/ML
- 提供 **PySide6 桌面 GUI**,支持任务管理、调度配置
- 支持在线API 抓取和离线JSON 回放)两种模式
## 业务上下文
- 单租户:一家台球门店(由 `STORE_ID` 标识)
- 核心实体:会员(客户)、助教(教练)、台桌、订单、支付、退款、团购套餐、库存
- 领域语言以中文为主代码注释、文档、UI 文案均为中文
- 货币人民币CNY金额以 numeric(2) 存储
## 主要入口
- CLI`python -m cli.main`(主入口)
- GUI`python -m gui.main`
- 批处理脚本:`run_etl.bat``run_gui.bat`(根目录)、`scripts/run_ods.bat`

View File

@@ -0,0 +1,17 @@
---
inclusion: manual
---
# 变更影响审查与文档同步(手动参考)
说明:本文件用于“按需加载”的快速参考(可作为 /slash command详细流程请优先使用 skill
- steering-readme-maintainer
## 何时使用
- 发生业务/资金口径/ETL/接口/鉴权/小程序交互等“逻辑改动”时
## 快速清单
- 是否需要更新 product.md / tech.md / structure.md / README.md / (各子目录下README.md)
- 是否需要补齐审计记录 docs/audit/changes/<date>__<slug>.md
- 是否需要在每个修改文件写入 AI_CHANGELOG
- 是否需要在逻辑变更处加 CHANGE 标记注释

View File

@@ -0,0 +1,33 @@
---
inclusion: always
---
# 项目结构Lite
目标:在不注入大段目录树的前提下,让 Agent 快速理解“模块边界 + 高风险区”。
## 关键模块边界(高风险路径 = 变更默认需要审计)
- `cli/`:命令行入口与参数/运行模式(影响一键增量、调度参数等)
- `config/`默认值、环境变量解析、AppConfig、调度任务配置影响运行时假设
- `api/`:外部接口客户端与端点路由(影响抓取/契约/回放)
- `database/`连接、DDL/schema、seed、migrations影响数据结构与回滚
- `tasks/`ETL 任务ODS/DWD/DWS/指数/校验),业务规则主要落在这里
- `loaders/`upsert 与维度/事实装载(影响落库与冲突处理)
- `scd/`SCD2 处理(影响维度历史与生效区间)
- `orchestration/`:调度/注册/游标/运行记录(影响增量水位与可重复性)
- `models/`:解析与验证器(影响字段校验与转换)
- `utils/`日志、JSON 存储、窗口切分等通用工具(影响全局行为)
- 根目录散文件:`.env*``pyproject.toml``requirements*``Makefile``README.md` 等(影响运行/依赖/发布)
## 架构要点(摘要)
- 任务模式:每个 ETL 任务继承 `BaseTask`Extract → Transform → Load并在 `orchestration/task_registry.py` 注册
- 加载器模式:每张目标表一个 Loader维度/事实分目录;核心是 `upsert()` 与冲突处理策略
- 配置分层DEFAULTS → `.env` → CLI 覆盖;通过 `AppConfig.get("dotted.path")` 访问
- 管线流程:`FULL` / `FETCH_ONLY` / `INGEST_ONLY` 由 CLI 或环境变量控制
- 调度器:负责游标(水位)与运行记录(增量正确性关键)
## 编码/命名约定(摘要)
- 文件编码UTF-8
- SQL纯 SQL非 ORM迁移脚本放 `database/migrations/`,推荐“日期前缀”命名
- 任务:大写蛇形命名(例如 `DWD_LOAD_FROM_ODS`
- 日志:统一经由 `utils/logging_utils.py`

124
.kiro/steering/structure.md Normal file
View File

@@ -0,0 +1,124 @@
---
inclusion: auto
name: structure-full
description: Full directory tree + architecture patterns. Load only for large refactors, module moves, or changes spanning multiple subsystems.
---
# 项目结构
```
NeoZQYY/ # Monorepo 工作区根目录C:\NeoZQYY
├── cli/ # CLI 入口main.py
├── config/ # 配置默认值、环境变量解析、AppConfig、调度任务配置
│ └── scheduled_tasks.json
├── api/ # API 客户端HTTP、本地 JSON 回放、录制)
│ └── endpoint_routing.py # 端点路由映射
├── database/ # 数据库连接、操作、DDL Schema、种子脚本、迁移
│ ├── migrations/ # 迁移脚本(纯 SQL日期前缀命名
│ ├── schema_*.sql # DDL 定义
│ └── seed_*.sql # 种子数据
├── tasks/ # ETL 任务实现(按数据层分目录)
│ ├── base_task.py # BaseTask 基类,提供 Extract/Transform/Load 模板
│ ├── ods/ # ODS 层抓取任务16 个业务实体 + ods_tasks 工厂)
│ ├── dwd/ # DWD 层装载任务base_dwd_task、维度/事实装载、质量检查)
│ ├── dws/ # DWS 汇总与指数任务
│ │ └── index/ # 指数计算任务(亲密度、新客转化、召回、关系、赢回)
│ ├── utility/ # 工具类任务Schema 初始化、手动入库、完整性检查、DWS 构建等)
│ └── verification/ # ETL 后置校验任务ODS/DWD/DWS/指数校验器)
├── loaders/ # 数据加载器ODS、维度、事实
│ ├── base_loader.py # BaseLoader 基类,定义 upsert 接口
│ ├── ods/ # 通用 ODS 加载器
│ ├── dimensions/ # SCD2 维度加载器(会员、助教、商品、台桌、套餐)
│ └── facts/ # 事实表加载器(订单、支付、退款、小票、充值等)
├── scd/ # SCD2缓慢变化维度处理器
├── orchestration/ # 调度器、任务注册表、游标管理、运行记录
│ ├── pipeline_runner.py # 管线运行器
│ ├── task_executor.py # 任务执行器
│ ├── task_registry.py # 任务注册表
│ ├── scheduler.py # ETL 调度器
│ ├── cursor_manager.py # 游标(水位)管理
│ └── run_tracker.py # 运行记录追踪
├── quality/ # 数据质量检查器(余额一致性、完整性)
│ └── integrity_service.py # 完整性检查服务
├── models/ # 解析器与验证器
├── utils/ # 工具函数日志、JSON 存储、报告、窗口切分
├── gui/ # PySide6 桌面 GUI
│ ├── main_window.py
│ ├── widgets/ # UI 面板与组件
│ ├── workers/ # 后台工作线程
│ ├── models/ # GUI 数据模型(任务、调度)
│ ├── utils/ # GUI 专用工具设置、CLI 构建器)
│ └── resources/ # 样式表
├── scripts/ # 运维/工具脚本
│ ├── run_update.py # 一键增量更新入口ODS → DWD → DWS
│ ├── run_ods.bat # ODS 批处理入口
│ ├── audit/ # 仓库审计脚本(扫描器、分析器、报告生成)
│ ├── check/ # 数据检查脚本完整性、ODS 缺口、DWD 服务、内容哈希等)
│ ├── db_admin/ # 数据库管理脚本Excel 导入)
│ ├── export/ # 数据导出脚本(指数、团购、亲密度、会员明细等)
│ ├── rebuild/ # 数据重建脚本(全量 ODS→DWD 重建)
│ └── repair/ # 数据修复脚本回填、去重、hash 修复、维度修复、索引调优)
├── tests/ # 测试套件
│ ├── unit/ # 单元测试FakeDB/FakeAPI无需真实数据库
│ └── integration/ # 集成测试(需要 TEST_DB_DSN 或真实数据库)
├── docs/ # 文档
│ ├── CHANGELOG.md # 项目级版本变更历史
│ ├── audit/ # 审计产物
│ │ ├── changes/ # AI 逐次变更审计记录
│ │ ├── repo/ # 仓库审计报告(自动生成)
│ │ ├── prompt_logs/ # Prompt 日志(每次 prompt 一个独立文件,按时间戳命名)
│ │ └── audit_dashboard.md # 审计一览表(/audit 自动刷新)
│ ├── architecture/ # 架构设计文档(系统概览、数据流向)
│ ├── business-rules/ # 业务规则文档指数算法、DWS 口径、SCD2 规则)
│ ├── operations/ # 运维文档(环境搭建、调度配置、故障排查)
│ ├── database/ # 数据库文档统一目录ODS/DWD/DWS/ETL_Admin 表手册 + 概览索引)
│ │ ├── overview/ # 层级概览 / 速查索引
│ │ ├── ODS/ # ODS 层表手册main/mappings/changes
│ │ ├── DWD/ # DWD 层表手册main + Ex 扩展)
│ │ ├── DWS/ # DWS 层表手册
│ │ └── ETL_Admin/ # ETL 管理层表手册
│ ├── etl_tasks/ # ETL 任务文档
│ ├── requirements/ # 需求文档(功能需求、口径补充、指数 PRD
│ ├── reports/ # 分析报告
│ ├── api-reference/ # API 参考文档(标准化)
│ │ ├── api_registry.json # API 注册表25 个端点定义)
│ │ ├── summary/ # 每个 API 一个精简版 .md25 个)
│ │ ├── endpoints/ # 每个 API 一个详细版 .md 文档24 个)
│ │ └── samples/ # 最新响应样本JSON
├── reports/ # 质检输出JSON已 gitignore
├── export/ # JSON 落盘与日志(已 gitignore
├── logs/ # 运行日志(已 gitignore
└── .Deleted/ # 已归档/废弃文件(隐藏目录,已 gitignore
```
## 架构模式
- **任务模式**:每个 ETL 任务继承 `BaseTask`Extract → Transform → Load 模板方法),在 `orchestration/task_registry.py` 中注册。
- **加载器模式**:每张目标表对应一个加载器,继承 `BaseLoader` 并实现 `upsert()` 方法。维度加载器在 `loaders/dimensions/`,事实加载器在 `loaders/facts/`
- **配置分层**`DEFAULTS` 字典 → `.env` 覆盖 → CLI 参数覆盖。通过 `AppConfig.get("dotted.path")` 访问。
- **管线流程**`FULL`(抓取 + 入库)、`FETCH_ONLY`(仅抓取)、`INGEST_ONLY`(仅入库)。由 `--pipeline-flow` CLI 参数或 `PIPELINE_FLOW` 环境变量控制。
- **调度器**`ETLScheduler` 编排任务执行,管理游标(水位),在 `etl_admin` Schema 中记录运行状态。
- **API 抽象**`APIClient`HTTP`LocalJsonClient`(离线回放)、`RecordingAPIClient`(抓取 + 落盘)共享相同接口,任务代码无需关心数据来源。
## 编码约定
- 文件编码UTF-8文件头加 `# -*- coding: utf-8 -*-`
- 日志格式:通过 `utils/logging_utils.py` 统一
- 任务代码:大写蛇形命名(如 `DWD_LOAD_FROM_ODS``DWS_ASSISTANT_DAILY`
- SQL 文件:纯 SQL不使用 ORM通过 `psycopg2` 执行
- 数据库操作:批量 upsert + 冲突处理,显式 commit/rollback
- 中文注释和文档字符串是正常且预期的
<!--
AI_CHANGELOG:
- 日期: 2026-02-13
- Prompt: P20260213-171500 — "继续"Task 3 API 文档全面重构续接)
- 直接原因: 新增 docs/api-reference/ 目录替代旧 test-json-doc需在项目结构文档中反映
- 变更摘要: docs/ 树中新增 api-reference/(含 api_registry.json、endpoints/、samples/test-json-doc 标记为 [已废弃]
- 风险与验证: 纯文档结构描述变更,无运行时影响;验证方式:对比实际目录 `ls docs/api-reference/` 确认一致
- 日期: 2026-02-14
- Prompt: P20260214-130000 — 25 个 API 文档归档至 summary/ + 字段分组修正
- 直接原因: 新增 summary/ 子目录存放精简版 API 文档,需在项目结构中反映
- 变更摘要: api-reference/ 树中新增 summary/25 个精简版 .mdendpoints/ 说明从"25 个"更正为"24 个"
- 风险与验证: 纯文档结构描述变更,无运行时影响
-->

60
.kiro/steering/tech.md Normal file
View File

@@ -0,0 +1,60 @@
# 技术栈与构建
## 语言与运行时
- Python 3.10+(测试缓存中观察到 3.13
- 未提交虚拟环境;用户自行管理
## 核心依赖requirements.txt
- `psycopg2-binary>=2.9.0` — PostgreSQL 驱动
- `requests>=2.28.0` — 上游 API 的 HTTP 客户端
- `python-dateutil>=2.8.0` / `tzdata>=2023.0` — 日期解析与时区处理
- `python-dotenv``.env` 文件加载
- `openpyxl>=3.1.0` — Excel 导入导出DWS 数据)
- `PySide6>=6.5.0` — Qt 桌面 GUI 框架
- `flask>=2.3` — 可选 Web API
- `pyinstaller>=6.0.0` — 可选,仅打包 EXE 时需要
## 数据库
- PostgreSQL连接远程实例
- Schema`billiards_ods`ODS 原始数据)、`billiards_dwd`(明细数据)、`billiards_dws`(汇总数据)、`etl_admin`(调度/运行记录)
- DDL 文件位于 `database/schema_*.sql`,种子脚本位于 `database/seed_*.sql`
- 迁移脚本位于 `database/migrations/`(纯 SQL日期前缀命名
## 测试
- 框架:`pytest`(未固定在 requirements 中,需单独安装)
- 配置:`pytest.ini` 设置 `pythonpath = .`
- 结构:`tests/unit/`(基于 mock无需数据库`tests/integration/`(需要 `TEST_DB_DSN`
- 测试工具:`tests/unit/task_test_utils.py` 提供 FakeDB/FakeAPI 辅助类
## 常用命令
```bash
# 安装依赖
pip install -r requirements.txt
# 在线全流程 ETL抓取 + 入库)
python -m cli.main --pg-dsn "$PG_DSN" --store-id "$STORE_ID" --api-token "$API_TOKEN"
# 运行指定任务
python -m cli.main --tasks INIT_ODS_SCHEMA,MANUAL_INGEST --data-source offline
# 试运行(不写库)
python -m cli.main --dry-run --tasks DWD_LOAD_FROM_ODS
# 单元测试
pytest tests/unit
# 集成测试(需要数据库)
TEST_DB_DSN="postgresql://..." pytest tests/integration
# 启动 GUI
python -m gui.main
```
## 配置体系
- 分层叠加:`config/defaults.py` < `.env` / 环境变量 < CLI 参数
- 配置类:`config.settings.AppConfig`,支持点号路径访问(`config.get("db.dsn")`
- 敏感值DSN、API Token放在 `.env` 中,禁止提交
## 打包
- 已移除 EXE 打包支持(`build_exe.py``setup.py` 已归档至 `.Deleted/`
- 直接通过 `python -m cli.main``python -m gui.main` 运行