初始提交:飞球 ETL 系统全量代码
This commit is contained in:
15
.kiro/hooks/change-impact-review.kiro.hook
Normal file
15
.kiro/hooks/change-impact-review.kiro.hook
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"enabled": true,
|
||||
"name": "change-impact-review(Steering + README)",
|
||||
"description": "每次 agent 执行结束后,评估本轮代码变更是否需要同步更新 product/tech/structure steering 文档及 README,必要时自动更新并输出审计摘要。",
|
||||
"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/ai_audit/changes/<YYYY-MM-DD>__<slug>.md,内容必须包含:\n- 日期/时间(Asia/Taipei)\n- 原始用户 Prompt(原文;或引用 Prompt-ID + 不超过 5 行的摘录)\n- 直接原因:AI 分析后“为何必须改” + “修改方案简介”\n- 修改文件清单(Files changed list)\n- 风险点、回滚要点、验证步骤(至少包含可执行的验证方式)\n对每一个被修改的文件,必须在文件内新增或更新 AI_CHANGELOG 记录项,至少包含:\n- 日期\n- Prompt(Prompt-ID + 摘录)\n- 直接原因(必要性 + 方案简介)\n- 变更摘要(改了什么:模块/函数/接口/字段等)\n- 风险与验证(回归范围 + 验证方法/测试点/SQL/联调步骤)\n对每一处“逻辑变更”的代码块,必须在变更附近添加内联 CHANGE 标记注释,至少说明:\n- 变更意图(intent)\n- 关键假设(assumptions)\n- 边界条件/资金口径/精度与舍入规则(若相关)\n- 关联 Prompt(Prompt-ID 或摘录)以及必要的验证提示\n\n硬性规则:如果涉及数据库 schema 或表结构变更,必须同步更新 C:\\ZQYY\\FQ-ETL\\docs\\bd_manual\\ 下对应的表结构文档。"
|
||||
},
|
||||
"workspaceFolderName": "FQ-ETL",
|
||||
"shortName": "change-impact-review"
|
||||
}
|
||||
15
.kiro/hooks/db-docs-sync.kiro.hook
Normal file
15
.kiro/hooks/db-docs-sync.kiro.hook
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"enabled": true,
|
||||
"name": "Manual: DB 文档全量同步",
|
||||
"description": "按需触发:对比 Postgres 实际 schema 与 docs/bd_manual/ 下的文档,自动补全或更新缺失/过时的表结构说明,并输出变更摘要。",
|
||||
"version": "1",
|
||||
"when": {
|
||||
"type": "userTriggered"
|
||||
},
|
||||
"then": {
|
||||
"type": "askAgent",
|
||||
"prompt": "执行一次按需的数据库文档全量同步。\n\n步骤:\n1) 检查当前 Postgres schema(使用环境中可用的工具/命令,例如 pg_dump --schema-only 或查询 information_schema)。\n2) 与 docs/bd_manual 下现有文档进行对比。\n3) 更新缺失或过时的 schema/表结构文档。\n4) 输出对账摘要:哪些文档被修改了、修改原因。输出路径遵循.env路径定义。\n\n注意:如果需要执行 shell 命令,请通过 agent 的 shell 工具调用。"
|
||||
},
|
||||
"workspaceFolderName": "FQ-ETL",
|
||||
"shortName": "db-docs-sync"
|
||||
}
|
||||
22
.kiro/hooks/db-schema-doc-enforcer.kiro.hook
Normal file
22
.kiro/hooks/db-schema-doc-enforcer.kiro.hook
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"enabled": true,
|
||||
"name": "DB Schema 文档执行 (bd_manual)",
|
||||
"description": "当数据库 schema/migration 相关文件被保存时,检查是否有表结构变更,并自动更新 docs/bd_manual/ 下对应的表结构文档。",
|
||||
"version": "1",
|
||||
"when": {
|
||||
"type": "fileEdited",
|
||||
"patterns": [
|
||||
"**/migrations/**/*.*",
|
||||
"**/*.sql",
|
||||
"**/*ddl*.*",
|
||||
"**/*schema*.*",
|
||||
"**/*.prisma"
|
||||
]
|
||||
},
|
||||
"then": {
|
||||
"type": "askAgent",
|
||||
"prompt": "一个数据库相关文件刚被保存。你必须检查是否发生了 schema/表结构变更。\n\n如果发生了表结构变更,你必须更新以下目录中的文档:\ndocs/bd_manual/\n\n最低输出要求(必须写入对应 schema 目录 + 表结构文档):\n1) 变更内容:表/字段/类型/可空性/默认值/约束/索引/外键的具体变化\n2) 变更原因:业务背景与动机\n3) 影响范围:ETL 管线、后端 API 契约、小程序字段等\n4) 回滚策略:如何回退 + 数据回填注意事项\n5) 验证 SQL:至少 3 条查询语句用于验证变更正确性\n6) 溯源留痕日期(Asia/Taipei,YYYY-MM-DD);Prompt(Prompt-ID + ≤5 行摘录或原文);Direct cause(必要性 + 修改方案简介)\n\n如果没有发生表结构变更(例如仅修改注释),在变更日志文档中写一条简短说明:\"无结构性变更\"(同样要带日期 + Prompt-ID)。"
|
||||
},
|
||||
"workspaceFolderName": "FQ-ETL",
|
||||
"shortName": "db-schema-doc-enforcer"
|
||||
}
|
||||
13
.kiro/hooks/prompt-audit-log.kiro.hook
Normal file
13
.kiro/hooks/prompt-audit-log.kiro.hook
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"enabled": true,
|
||||
"name": "Prompt Audit Log",
|
||||
"description": "每次提交 prompt 时,自动将原始用户 prompt 追加到 docs/ai_audit/prompt_log.md,包含日期时间和 Prompt-ID。",
|
||||
"version": "1",
|
||||
"when": {
|
||||
"type": "promptSubmit"
|
||||
},
|
||||
"then": {
|
||||
"type": "askAgent",
|
||||
"prompt": "将本次用户 Prompt 追加写入 docs/ai_audit/prompt_log.md。\n\n要求:\n- 使用 Asia/Taipei 日期时间。\n- 生成 Prompt-ID:P<YYYYMMDD-HHMMSS>(例如 P20260213-101530)。\n- 记录 Prompt 原文(如包含敏感信息则用 [REDACTED] 脱敏,并说明已脱敏)。\n- 记录一行摘要(≤120 字),用于后续快速检索。\n\n最后一行必须输出:Prompt-ID: <id>"
|
||||
}
|
||||
}
|
||||
41
.kiro/skills/bd-manual-db-docs/SKILL.md
Normal file
41
.kiro/skills/bd-manual-db-docs/SKILL.md
Normal file
@@ -0,0 +1,41 @@
|
||||
---
|
||||
name: bd-manual-db-docs
|
||||
description: 当 PostgreSQL schema/表结构发生变化时,用于将变更以审计友好的方式落盘到 docs/bd_manual/(含变更原因、影响、回滚与验证 SQL)。
|
||||
---
|
||||
|
||||
# 目的
|
||||
保证数据库结构变化可追溯、可审计、可回滚,并与 ETL/后端/小程序字段映射保持一致。
|
||||
|
||||
# 触发条件
|
||||
- 迁移脚本/DDL 修改(新增/删除/改表、字段、类型、默认值、非空、约束、索引、外键)
|
||||
- ORM/Schema 定义变更导致实际 DB 结构变化
|
||||
- 手工执行 DDL(需用 manualTrigger hook 或本 Skill 补齐文档)
|
||||
|
||||
# 输出要求(必须全部满足)
|
||||
所有输出必须落盘到:`docs/bd_manual/`
|
||||
|
||||
至少包含:
|
||||
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`
|
||||
@@ -0,0 +1,27 @@
|
||||
# Schema 变更日志(Schema Change Log)
|
||||
|
||||
- 日期(Asia/Taipei,YYYY-MM-DD):
|
||||
- Prompt-ID:
|
||||
- 原始原因(Prompt 摘录/原文):
|
||||
- 直接原因(必要性 + 方案简介):
|
||||
- 影响的 Schema:
|
||||
- 变更摘要(一句话):
|
||||
|
||||
## 变更明细
|
||||
- 新增:
|
||||
- 修改:
|
||||
- 删除:
|
||||
|
||||
## 影响范围
|
||||
- ETL:
|
||||
- 后端 API:
|
||||
- 小程序:
|
||||
|
||||
## 回滚要点
|
||||
- DDL 回滚:
|
||||
- 数据回填/迁移注意事项:
|
||||
|
||||
## 验证 SQL(至少 3 条)
|
||||
1)
|
||||
2)
|
||||
3)
|
||||
@@ -0,0 +1,22 @@
|
||||
# <schema>.<table>
|
||||
|
||||
## 表用途(Purpose)
|
||||
- 该表代表什么业务对象/过程
|
||||
|
||||
## 字段(Columns)
|
||||
| 字段名 | 类型 | 可空 | 默认值 | 约束/键 | 说明(含口径) |
|
||||
|---|---|---:|---|---|---|
|
||||
|
||||
> 金额类字段必须注明:币种、精度、舍入/截断规则、是否允许负数。
|
||||
|
||||
## 索引(Indexes)
|
||||
- 索引名 / 字段 / 是否唯一 / 备注
|
||||
|
||||
## 约束与外键(Constraints & FKs)
|
||||
- 约束名 / 定义 / 备注
|
||||
|
||||
## 数据不变量(Invariants)
|
||||
- 例如:状态机枚举范围、唯一性、跨字段一致性约束(如有)
|
||||
|
||||
## 变更历史(Change History)
|
||||
- YYYY-MM-DD | Prompt-ID | 直接原因 | 变更摘要
|
||||
37
.kiro/skills/change-annotation-audit/SKILL.md
Normal file
37
.kiro/skills/change-annotation-audit/SKILL.md
Normal file
@@ -0,0 +1,37 @@
|
||||
---
|
||||
name: change-annotation-audit
|
||||
description: 对每次修改强制生成审计记录(docs/ai_audit/changes/...),并在每个被改文件写 AI_CHANGELOG、在逻辑变更处写 CHANGE 标记注释(包含日期、Prompt 与直接原因)。
|
||||
---
|
||||
|
||||
# 目的
|
||||
把“为什么改、怎么改、怎么验”固化到可审计产物中,满足资金相关项目的严谨性要求。
|
||||
|
||||
# 触发条件
|
||||
- 任何对代码或文档的实质修改(非纯格式化)
|
||||
- 特别是:逻辑改动、资金口径改动、接口契约改动、DB 结构改动
|
||||
|
||||
# 必须产物(缺一不可)
|
||||
1) `docs/ai_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_CHANGELOG(Per-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`
|
||||
@@ -0,0 +1,19 @@
|
||||
# 变更审计记录(Change Audit Record)
|
||||
|
||||
- 日期/时间(Asia/Taipei):
|
||||
- Prompt-ID:
|
||||
- 原始原因(Prompt 原文或 ≤5 行摘录):
|
||||
- 直接原因(必要性 + 修改方案简介):
|
||||
|
||||
## 变更范围(Changed)
|
||||
- 模块/接口/表/关键文件:
|
||||
|
||||
## 风险与回滚(Risk & Rollback)
|
||||
- 风险点:
|
||||
- 回滚要点:
|
||||
|
||||
## 验证(Verification)
|
||||
- 至少 1 条可执行验证方式(测试/SQL/联调):
|
||||
|
||||
## 文件清单(Files changed)
|
||||
- ...
|
||||
@@ -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: ...
|
||||
|
||||
---
|
||||
|
||||
## Python(docstring/块注释)
|
||||
"""
|
||||
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: ...
|
||||
38
.kiro/skills/steering-readme-maintainer/SKILL.md
Normal file
38
.kiro/skills/steering-readme-maintainer/SKILL.md
Normal 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.md`:目录/模块边界/职责是否变化?
|
||||
- `README.md`:运行方式、配置、环境变量、接口契约、联调步骤是否变化?
|
||||
|
||||
> 规则:如果“对读者理解系统行为”有帮助,就应更新;不要为了追求“少改文档”而拒绝同步。
|
||||
|
||||
## 3) 输出审计友好摘要(对话回复/审计记录都需要)
|
||||
- Changed:改了哪些模块/接口/表/关键文件
|
||||
- Why:原始原因(Prompt-ID + 摘录)与直接原因(必要性 + 方案简介)
|
||||
- Risk:风险点与回归范围
|
||||
- Verify:建议的验证步骤(测试/SQL/联调)
|
||||
|
||||
## 4) 联动硬规则检查
|
||||
- 如果涉及 DB schema/表结构变化:必须同步更新 `docs/bd_manual/`(见 skill `bd-manual-db-docs`)。
|
||||
|
||||
# 资产(可复制模板/清单)
|
||||
见:`assets/steering-update-checklist.md`
|
||||
@@ -0,0 +1,23 @@
|
||||
# Steering & README 同步清单(逻辑改动必查)
|
||||
|
||||
## product.md(产品/口径)
|
||||
- 业务定义/指标口径/字段含义是否改变?
|
||||
- 涉及金额的精度/舍入/阈值规则是否改变?
|
||||
- 角色/权限模型是否改变?
|
||||
|
||||
## tech.md(技术/运行)
|
||||
- 新增/变更依赖(框架、库、驱动)?
|
||||
- 配置项/环境变量/端口/服务启动方式是否改变?
|
||||
- 数据访问边界(ETL 库 vs 业务库)是否改变?
|
||||
- 性能/一致性/幂等/重试策略是否改变?
|
||||
|
||||
## structure.md(结构/职责)
|
||||
- 新增目录/模块?
|
||||
- 模块职责或边界是否重新划分?
|
||||
- 新增集成点(队列、定时任务、外部系统)?
|
||||
|
||||
## README.md(使用/联调)
|
||||
- 本地启动步骤是否改变?
|
||||
- 新增/变更配置项(.env 等)?
|
||||
- API 契约是否变化(路径、参数、返回、错误码)?
|
||||
- 小程序联调步骤是否变化?
|
||||
1
.kiro/specs/repo-audit/.config.kiro
Normal file
1
.kiro/specs/repo-audit/.config.kiro
Normal file
@@ -0,0 +1 @@
|
||||
{"generationMode": "requirements-first"}
|
||||
424
.kiro/specs/repo-audit/design.md
Normal file
424
.kiro/specs/repo-audit/design.md
Normal file
@@ -0,0 +1,424 @@
|
||||
# 设计文档:仓库治理只读审计
|
||||
|
||||
## 概述
|
||||
|
||||
本设计描述三个 Python 审计脚本的实现方案,用于对 etl-billiards 仓库进行只读分析并生成三份 Markdown 报告。脚本仅读取文件系统和源代码,不连接数据库、不修改任何现有文件,仅在 `docs/audit/` 目录下输出报告。
|
||||
|
||||
审计脚本采用模块化设计:一个共享的仓库扫描器负责遍历文件系统,三个独立的分析器分别生成文件清单、流程树和文档对齐报告。
|
||||
|
||||
## 架构
|
||||
|
||||
```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/file_inventory.md]
|
||||
D --> H[docs/audit/flow_tree.md]
|
||||
E --> I[docs/audit/doc_alignment.md]
|
||||
|
||||
C --> B
|
||||
D --> B
|
||||
E --> B
|
||||
```
|
||||
|
||||
### 执行流程
|
||||
|
||||
1. `run_audit.py` 作为主入口,初始化扫描器并依次调用三个分析器
|
||||
2. `scanner.py` 递归遍历仓库,构建文件元信息列表(路径、大小、类型)
|
||||
3. 各分析器接收扫描结果,执行各自的分析逻辑,输出 Markdown 报告
|
||||
4. 所有报告写入 `docs/audit/` 目录
|
||||
|
||||
## 组件与接口
|
||||
|
||||
### 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/` 为前缀。
|
||||
|
||||
**Validates: Requirements 5.2**
|
||||
|
||||
### Property 16: 文档对齐报告分区完整性
|
||||
|
||||
*对于任意* `render_alignment_report` 的输出,Markdown 文本应包含"映射关系"、"过期点"、"冲突点"、"缺失点"四个分区标题。
|
||||
|
||||
**Validates: Requirements 3.8**
|
||||
|
||||
## 错误处理
|
||||
|
||||
| 场景 | 处理方式 |
|
||||
|------|---------|
|
||||
| 文件读取权限不足 | 记录警告到报告的"错误"分区,跳过该文件,继续处理 |
|
||||
| Python 源文件语法错误(`ast.parse` 失败) | 记录警告,将该文件标记为"待确认",不中断流程树构建 |
|
||||
| 文档中的代码引用格式无法解析 | 跳过该引用,不产生误报 |
|
||||
| DDL 文件 SQL 语法不规范 | 使用正则提取 `CREATE TABLE` 和列定义,容忍非标准语法 |
|
||||
| `docs/audit/` 目录创建失败 | 抛出异常并终止,因为无法输出报告 |
|
||||
| 编码问题(非 UTF-8 文件) | 尝试 `utf-8` → `gbk` → `latin-1` 回退读取,记录编码警告 |
|
||||
|
||||
## 测试策略
|
||||
|
||||
### 测试框架
|
||||
|
||||
- 单元测试与属性测试均使用 `pytest`
|
||||
- 属性测试库:`hypothesis`(Python 生态最成熟的属性测试框架)
|
||||
- 测试文件位于 `tests/unit/test_audit_*.py`
|
||||
|
||||
### 单元测试
|
||||
|
||||
针对具体示例和边界情况:
|
||||
- 扫描器对实际仓库子集的遍历结果
|
||||
- classify 对已知文件路径的分类正确性(如 `tmp/hebing.py` → 临时与调试/候选删除)
|
||||
- 入口点识别对实际仓库的结果
|
||||
- DDL 与数据字典的比对结果
|
||||
- 文件读取失败时的容错行为
|
||||
- `docs/audit/` 目录不存在时的自动创建
|
||||
|
||||
### 属性测试
|
||||
|
||||
每个正确性属性对应一个属性测试,使用 `hypothesis` 生成随机输入:
|
||||
|
||||
- 每个属性测试至少运行 100 次迭代
|
||||
- 每个测试用注释标注对应的设计属性编号
|
||||
- 标注格式:**Feature: repo-audit, Property {N}: {属性标题}**
|
||||
|
||||
**生成器策略**:
|
||||
- `FileEntry` 生成器:随机路径(含各种扩展名、目录层级)、随机大小、随机 is_dir/is_empty_dir
|
||||
- `InventoryItem` 生成器:随机 Category/Disposition 组合、随机描述文本
|
||||
- `FlowNode` 生成器:随机树结构(限制深度和宽度)
|
||||
- 文件树生成器:构造临时目录结构用于扫描器测试
|
||||
90
.kiro/specs/repo-audit/requirements.md
Normal file
90
.kiro/specs/repo-audit/requirements.md
Normal 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/` 目录下,文件名分别为 `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/` 目录下创建新文件,该目录为报告专用输出目录
|
||||
3. IF 审计脚本在执行过程中遇到权限错误或文件读取失败,THEN THE Audit_Script SHALL 在报告中记录该错误并继续处理其余文件
|
||||
4. THE Audit_Script SHALL 在运行前检查 `docs/audit/` 目录是否存在,若不存在则创建该目录
|
||||
118
.kiro/specs/repo-audit/tasks.md
Normal file
118
.kiro/specs/repo-audit/tasks.md
Normal file
@@ -0,0 +1,118 @@
|
||||
# 实施计划:仓库治理只读审计
|
||||
|
||||
## 概述
|
||||
|
||||
将设计文档中的审计脚本拆分为增量式编码任务。每个任务构建在前一个任务之上,最终产出可运行的审计工具集。所有脚本位于 `scripts/audit/` 目录,报告输出到 `docs/audit/`。
|
||||
|
||||
## 任务
|
||||
|
||||
- [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/` 目录检查与创建逻辑
|
||||
- 实现报告头部元信息(时间戳、仓库路径)注入
|
||||
- 实现三份报告的文件写入
|
||||
- 添加 `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 次迭代
|
||||
- 单元测试验证具体示例和边界情况,属性测试验证通用正确性
|
||||
1
.kiro/specs/scheduler-refactor/.config.kiro
Normal file
1
.kiro/specs/scheduler-refactor/.config.kiro
Normal file
@@ -0,0 +1 @@
|
||||
{"generationMode": "requirements-first"}
|
||||
462
.kiro/specs/scheduler-refactor/design.md
Normal file
462
.kiro/specs/scheduler-refactor/design.md
Normal 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/Taipei`) |
|
||||
| `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 1:data_source 参数决定执行路径
|
||||
|
||||
*对于任意* 任务代码和任意 `data_source` 值(online/offline/hybrid),TaskExecutor 执行该任务时,抓取阶段执行当且仅当 `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 6:processing_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 8:TaskRegistry 元数据 round-trip
|
||||
|
||||
*对于任意* 任务代码、任务类和元数据组合(requires_db_config、layer、task_type),注册后通过 `get_metadata` 查询应返回相同的元数据值。
|
||||
|
||||
**验证:需求 4.1**
|
||||
|
||||
### Property 9:TaskRegistry 向后兼容默认值
|
||||
|
||||
*对于任意* 使用旧接口(仅 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 11:pipeline_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 映射一致性
|
||||
123
.kiro/specs/scheduler-refactor/requirements.md
Normal file
123
.kiro/specs/scheduler-refactor/requirements.md
Normal 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_SCHEMA)时,THE 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/Taipei` 改为 `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 验证各层之间的交互契约(调用参数、返回值格式)
|
||||
147
.kiro/specs/scheduler-refactor/tasks.md
Normal file
147
.kiro/specs/scheduler-refactor/tasks.md
Normal 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/Taipei` 改为 `Asia/Shanghai`
|
||||
- 将 `db.session.timezone` 默认值从 `Asia/Taipei` 改为 `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
22
.kiro/steering/db-docs.md
Normal file
@@ -0,0 +1,22 @@
|
||||
---
|
||||
inclusion: fileMatch
|
||||
fileMatchPattern:
|
||||
- "**/migrations/**/*.*"
|
||||
- "**/*.sql"
|
||||
- "**/*schema*.*"
|
||||
- "**/*ddl*.*"
|
||||
- "**/*.prisma"
|
||||
---
|
||||
|
||||
# Database Schema Documentation Rules
|
||||
|
||||
当你修改任何可能影响 PostgreSQL schema/表结构的内容时(迁移脚本/DDL/表定义/ORM 模型):
|
||||
|
||||
1) 必须同步更新 BD 手册目录:
|
||||
docs/bd_manual
|
||||
|
||||
2) 文档最低要求:
|
||||
- 变更说明:新增/修改/删除的表、字段、约束、索引
|
||||
- 兼容性:对 ETL、后端 API、小程序字段映射的影响
|
||||
- 回滚策略:如何撤销(DDL 回滚 / 数据回填)
|
||||
- 验证步骤:最少包含 3 条校验 SQL
|
||||
59
.kiro/steering/governance.md
Normal file
59
.kiro/steering/governance.md
Normal file
@@ -0,0 +1,59 @@
|
||||
---
|
||||
inclusion: always
|
||||
---
|
||||
|
||||
# Governance / Engineering Rigour
|
||||
|
||||
## Hard Rules(必须遵守)
|
||||
|
||||
### 1) Logic Change → Change Impact Review + Doc Updates
|
||||
任何**逻辑改动**必须做 Change Impact Review,并评估/必要时更新:
|
||||
- .kiro/steering/product.md
|
||||
- .kiro/steering/structure.md
|
||||
- .kiro/steering/tech.md
|
||||
- README.md
|
||||
|
||||
**逻辑改动**包括(不限于):
|
||||
- 业务规则/计算口径/资金处理(精度、舍入、阈值等)
|
||||
- 数据处理与 ETL 逻辑(含 SQL 逻辑、清洗/聚合/映射)
|
||||
- API 行为(返回结构、错误码、鉴权/权限)
|
||||
- 小程序交互逻辑(校验、关键流程状态机)
|
||||
|
||||
**通常不视为逻辑改动**(仍需判断是否影响结构文档/运行方式):
|
||||
- 纯格式化、拼写/文案微调、仅注释调整、无行为变化的重命名
|
||||
|
||||
### 2) DB Schema / Table Structure Change → Must Update BD Manual
|
||||
任何数据库 schema / 表结构变化(DDL/迁移/字段类型/默认值/非空/约束/索引/外键等),必须同步到:
|
||||
- `docs/bd_manual` 下对应 schema 目录与表结构文档
|
||||
|
||||
文档必须包含:变更原因、影响范围、回滚策略、数据迁移注意事项、验证 SQL。
|
||||
|
||||
---
|
||||
|
||||
## Audit & Annotation Requirements(审计与标注)
|
||||
|
||||
### A) Per-change Audit Artifact(一次 Prompt 一份记录)
|
||||
每次修改(以一次用户 Prompt 驱动为单位)必须创建/追加:
|
||||
- `docs/ai_audit/changes/<YYYY-MM-DD>__<slug>.md`
|
||||
|
||||
内容至少包含:
|
||||
- 日期(Asia/Taipei,YYYY-MM-DD)
|
||||
- 原始原因:用户 Prompt(原文或 ≤5 行摘录,需可追溯完整 Prompt)
|
||||
- 直接原因:为什么必须改 + 修改方案简介
|
||||
- Changed:涉及模块/接口/表(或关键文件)
|
||||
- Risk/Verify:风险点、回归范围、验证步骤
|
||||
- 如涉及 DB 结构:回滚要点 + 验证 SQL
|
||||
|
||||
### B) Per-file AI_CHANGELOG(每个被修改文件必须可追溯)
|
||||
每个被修改的代码/文档文件必须追加/更新 **AI_CHANGELOG** 条目,至少包含:
|
||||
- 日期(Asia/Taipei,YYYY-MM-DD)
|
||||
- Prompt(原文或引用 Prompt-ID + 摘录)
|
||||
- 直接原因(必要性 + 方案简介)
|
||||
- 变更摘要(改了什么)
|
||||
- 风险与验证(至少 1 条验证方式)
|
||||
|
||||
### C) Inline CHANGE Markers(逻辑变更处必须可读)
|
||||
对“逻辑变更”的代码块,在变更附近增加 **CHANGE 标记注释**,包含:
|
||||
- intent(变更意图)
|
||||
- assumptions(前置假设)
|
||||
- edge cases / money semantics(边界条件与资金口径:精度/舍入等)
|
||||
18
.kiro/steering/language-zh.md
Normal file
18
.kiro/steering/language-zh.md
Normal file
@@ -0,0 +1,18 @@
|
||||
---
|
||||
inclusion: always
|
||||
---
|
||||
# 语言与编码规范(强制)
|
||||
|
||||
## 输出语言
|
||||
- 默认:所有“说明性文字”一律使用简体中文(对话回复、文档内容、代码注释、README/ADR/变更说明等)。
|
||||
- 允许保留英文的部分:
|
||||
- 代码标识符(类名/函数名/变量名/接口名/库名/命令名)不翻译
|
||||
- 第三方工具的原始 CLI 输出/报错原文不篡改(可在原文后补充中文解释)
|
||||
|
||||
## 文档与注释
|
||||
- 新增/修改的文档必须与代码变更同步更新
|
||||
- 注释只写“为什么/边界/假设”,避免复述代码
|
||||
|
||||
## 编码与字符集
|
||||
- 仓库内所有文本文件统一 UTF-8(建议无 BOM)
|
||||
- 禁止出现 GBK/Big5 混用;若发现历史文件,先转码再重构
|
||||
22
.kiro/steering/product.md
Normal file
22
.kiro/steering/product.md
Normal 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`
|
||||
17
.kiro/steering/steering-readme-maintainer.md
Normal file
17
.kiro/steering/steering-readme-maintainer.md
Normal 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/ai_audit/changes/<date>__<slug>.md
|
||||
- 是否需要在每个修改文件写入 AI_CHANGELOG
|
||||
- 是否需要在逻辑变更处加 CHANGE 标记注释
|
||||
105
.kiro/steering/structure.md
Normal file
105
.kiro/steering/structure.md
Normal file
@@ -0,0 +1,105 @@
|
||||
# 项目结构
|
||||
|
||||
```
|
||||
FQ-ETL/ # 工作区根目录(C:\ZQYY\FQ-ETL)
|
||||
├── 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/ # 文档
|
||||
│ ├── audit/ # 仓库审计报告(自动生成)
|
||||
│ ├── bd_manual/ # 业务数据手册(DWD/DWS 表说明)
|
||||
│ │ ├── DWD/ # DWD 层表手册(main + Ex 扩展)
|
||||
│ │ └── dws/ # DWS 层表手册
|
||||
│ ├── dictionary/ # 数据字典
|
||||
│ ├── index/ # 指数算法文档
|
||||
│ ├── requirements/ # 需求文档
|
||||
│ ├── reports/ # 分析报告
|
||||
│ ├── data_exports/ # 数据导出文档与 CSV
|
||||
│ ├── templates/ # 模板文件(Excel 等)
|
||||
│ ├── api-reference/ # API 参考文档(标准化,替代 test-json-doc)
|
||||
│ │ ├── api_registry.json # API 注册表(25 个端点定义)
|
||||
│ │ ├── endpoints/ # 每个 API 一个 .md 文档(25 个)
|
||||
│ │ └── samples/ # 最新响应样本(JSON)
|
||||
│ ├── test-json-doc/ # [已废弃] 旧版 API 测试 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/` 确认一致
|
||||
-->
|
||||
60
.kiro/steering/tech.md
Normal file
60
.kiro/steering/tech.md
Normal 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`(OLTP/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 --pipeline-flow INGEST_ONLY
|
||||
|
||||
# 试运行(不写库)
|
||||
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` 运行
|
||||
Reference in New Issue
Block a user