质量门禁技术债追踪¶
本文件记录所有门禁豁免及其原因、负责人、预计消除时间、修复方式。任何新增豁免(check_file_length.py 的 SKIP_REL_PATHS、eslint.config.js 的 max-lines overrides、# noqa、# nosec、# type: ignore 等)必须同步更新本文件,否则视为违规。
配套文档:Pre-commit 质量门禁手册 自动清单生成:
poetry run python scripts/check_file_length.py --list-debt业务待审核债(非门禁豁免):线下分账待审核技术债
一、文件长度豁免(check_file_length.py / eslint max-lines)¶
历史 6 个 admin-system 大文件已全部拆分至 500 有效行以内,双份豁免清单已清空(仅保留 seed_data 配置类豁免)。check_file_length.py 的 SKIP_REL_PATHS 与 eslint.config.js 的 max-lines overrides 现已无业务文件条目,实现单一来源。
| 文件 | 拆分前 | 拆分后 | 拆分策略 | 状态 |
|---|---|---|---|---|
src/admin-system/src/shared/api/adminApi.ts |
1783 | ~10(barrel) | barrel index → 15 域文件 | ✅ |
src/admin-system/src/features/system-config/pages/SystemConfigPage.tsx |
571 | ~50 | 3 Tab 组件 | ✅ |
src/admin-system/src/features/product-management/components/ProductTree.tsx |
698 | ~250 | 主体 + EditModal + utils + SubTreeNodes + SidebarBar | ✅ |
src/admin-system/src/features/product-management/components/ProductManagePanels.tsx |
615 | ~300 | ChildrenPanel + FabricTypePanel + FabricMaterialPanel + panelShared | ✅ |
src/admin-system/src/features/content/ContentManagePage.tsx |
567 | ~460 | 主页 + CategoryImageManagerModal + flattenContentTree | ✅ |
src/admin-system/src/features/ai-models/pages/AiModelsPage.tsx |
658 | 187 | 主页 + AiModelFormPanel + AiModelEditRow + ApiKeyManagerRow + maskKey | ✅ |
配置类豁免(永久)¶
| 路径 | 原因 |
|---|---|
src/backend/scripts/seed_data |
种子/配置数据,非业务代码 |
拆分手法库(供后续拆分参考)¶
| 手法 | 适用场景 | 步骤 |
|---|---|---|
| barrel index | 单文件聚合多个域(如 adminApi.ts) |
按域拆成 domain.ts → 建 index.ts re-export → 原文件退化为 export * from './admin/index' → 外部 import 路径不变 |
| 子组件抽取 | 单大组件含多个独立区块(如 AiModelsPage) |
识别独立区块 → 抽 props 接口 → 状态下沉到子组件(自管理)或上提到父组件(受控)→ 拆完逐个 typecheck |
| Tab 拆分 | 多 Tab 切换页面 | 每个 Tab 抽独立组件,主页只管 tab 状态与切换壳 |
| utils 抽取 | 纯函数混在组件文件 | 移到 utils/ 下纯 .ts 文件,显式标注参数与返回类型 |
| shared 常量 | 多组件共用图标/标题等 | 抽 panelShared.ts 等,集中导出 |
关键注意事项:
1. 拆分时优先保持外部 import 路径不变(barrel re-export),避免大面积改 import
2. 同名类型跨域冲突需重命名(如 TemplateImage → MarketingTemplateImage)
3. 每步拆分后立即跑 typecheck + lint + format-check + arch,确认无回归再继续
4. 拆分暴露的隐式 any(原文件内联能推断,跨文件后丢失)需补显式类型标注
二、提交信息规范(commitlint)¶
仓库引入 @commitlint/config-conventional(见 commitlint.config.cjs),约束 feat / fix / refactor / chore / docs / style / test / perf / build / ci / revert 前缀。
历史迁移说明:仓库早期使用 [FIX] / [FEAT] 方括号风格,自 commitlint 引入起统一为 conventional。历史提交不回填,新提交须符合规范,否则 commit-msg 阶段拒绝。
三、覆盖率门禁¶
| 范围 | 阈值 | 配置位置 | 覆盖范围 |
|---|---|---|---|
| 后端 | 80% | pyproject.toml [tool.pytest.ini_options] addopts --cov-fail-under=80 |
backend / messaging / storage / commons |
| agent-system | 80% | src/agent-system/vitest.config.ts |
src/shared + src/app |
| admin-system | 80% | src/admin-system/vitest.config.ts |
src/shared + src/app + src/features/**/utils/** |
| infinitecanvas | 80% | src/infinitecanvas/vitest.config.ts |
13 个已稳定的 src/utils 纯函数(见下方登记) |
后端当前基线(2026-07-04):746 单元测试全过;行覆盖率 80.38%,分支覆盖率 54.46%(branch=true 时 pytest 综合指标约 76%)。本轮批量新增约 120+ 测,覆盖计费 Service/Repository、支付适配器、Dypns 短信、Word/库模板 Admin API、用户任务/计费 API、消息 Stream、storage 对象 API 等。
当前状态:adminApi barrel 拆分后,新增的 src/shared/api/admin/*.ts 已通过 coverage.exclude 排除(与原 adminApi.ts 一致),覆盖率门禁维持。infinitecanvas 从零搭起测试基建,首批 13 个 utils 纯函数覆盖率达 97.46%。
收紧路径:后续应逐步为 admin/*.ts 补单测,并在 coverage.include 精确包含已测文件,逐步把覆盖率统计范围从"排除整个 api 目录"放开到"包含已测 api 文件"。
3.1 前端 coverage exclude 登记¶
下列文件 / 目录经评估依赖网络、复杂状态或运行时渲染,单测成本高,暂通过 coverage.exclude 排除。后续按"修复方式"逐步纳入。
| 前端 | 排除项 | 原因 | 修复方式 | 优先级 |
|---|---|---|---|---|
| agent-system | src/shared/api/agentApi.ts |
薄封装 HTTP 客户端,依赖鉴权与后端 | 用 MSW mock 鉴权后补 url 构造测试 | 低 |
| agent-system | src/shared/api/settlementApi.ts |
同上,结算接口薄封装 | 同上 | 低 |
| agent-system | src/shared/components/** |
受控 UI 组件,需 RTL + 交互测试 | 优先补关键交互组件(Alert、Modal) | 中 |
| agent-system | src/app/components/Layout.tsx、Sidebar.tsx |
含路由与权限分支的布局组件 | 集成测试场景覆盖 | 低 |
| admin-system | src/shared/api/admin/**、adminApi.ts |
按 domain 拆分的 API 客户端,依赖网络 | MSW mock 后逐个补 | 中 |
| admin-system | src/shared/api/billingApi.ts、adminHttpClient.ts、wordBatchTestStream.ts |
计费 API + 网络传输层(fetch / SSE / FormData) | MSW mock 鉴权后补 | 中 |
| admin-system | src/shared/utils/dualImageUpload.ts |
双图上传,依赖 FileReader / FormData | 抽纯函数 + MSW | 中 |
| admin-system | src/shared/hooks/useFabricMaterialCatalog.ts |
React Query 网络请求 hook | 集成测试 + MSW | 中 |
| admin-system | src/shared/components/** |
受控 UI 组件库 | 抽 hook / 纯逻辑到 utils 单测 | 中 |
| admin-system | src/app/components/Layout.tsx、Sidebar.tsx、Topbar.tsx |
含路由与权限分支的布局组件 | 集成测试场景覆盖 | 低 |
| infinitecanvas | src/utils 中 API 客户端类(canvas-api-client、runninghub-api、image-generation-api、canvas-project-api) |
HTTP 客户端 | MSW mock 后补 | 中 |
| infinitecanvas | src/utils 中状态/序列化类(canvas-persistence、canvas-document-builder、selection-group、parent-node-refs、image-tool-processor、image-upload-compress、group-workflow、asset-upload、asset-resolver、canvas-asset-items、ai-input-node、ai-prompt-highlight) |
依赖画布状态/IndexedDB/文件 IO | 抽纯函数 + 集成测试 | 中 |
四、维护规则¶
- 新增豁免 → 在对应章节登记(文件、原因、负责人、预计消除时间、修复方式)
- 消除豁免 → 删除条目并同步清空代码侧的豁免配置(
SKIP_REL_PATHS/ eslint overrides /# noqa等) - 每月核对 → 由
--list-debt输出核对,偏差须在此说明 - PR 审查 → 新增豁免的 PR 必须在描述中说明理由与消除计划,reviewer 有权拒绝无理豁免
五、已知的预先存在测试债(非门禁豁免,但需修复)¶
| 测试文件 | 问题 | 影响 | 修复方式 | 状态 |
|---|---|---|---|---|
src/admin-system/tests/unit/app/providers/AuthProvider.test.tsx |
mock authApi.login/getMe(agent-system 接口),但 admin-system 的 AuthProvider 实际调用 adminLogin(adminApi) |
✅ 已修复 | 重写为 mock adminLogin,localStorage key 改为 admin_token/admin_user,并在源码 JSON.parse 处加 try/catch 防御损坏数据 |
✅ 2026-07 完成 |
tests/unit/backend/api/test_misc.py 的 TestStorageApi.test_upload_success |
用 httpx.MockTransport + respx 双重 mock,且 respx 试图拦截 ASGITransport 的 client,导致全量 pytest 卡死 |
✅ 已修复 | 改用 monkeypatch.setattr(StorageHttpClient, 'upload_bytes', fake) 直接 mock 类方法,彻底避免 HTTP |
✅ 2026-07 完成 |
六、架构规则修正记录(dependency-cruiser)¶
本次治理修正了两处规则正则,使其与实际代码结构对齐:
features-no-cross-import:原(?!\\1)反向引用在 dependency-cruiser 中无效,改为pathNot: '^src/features/$1/',正确允许同 feature 内子目录互导、仅禁止跨 featureno-direct-axios:原(?!shared/api/httpClient)仅匹配httpClient,扩展为(?:httpClient|adminHttpClient)覆盖 admin-system 的adminHttpClient
七、行内豁免速查¶
代码中常见的"行内豁免"机制,使用时必须在本文件登记:
| 机制 | 适用 | 示例 | 登记要求 |
|---|---|---|---|
# noqa: <code> |
Ruff lint | import os # noqa: F401 # 注册副作用 |
本文件登记 code 与原因 |
# nosec |
Bandit 安全 | subprocess.run(...) # nosec # 输入已白名单 |
本文件登记 B 规则与原因 |
# type: ignore[<code>] |
mypy | import x # type: ignore[import-untyped] # 第三方无类型 |
本文件登记 module 与原因 |
# alembic:allow-danger: 原因 |
Alembic 迁移 | op.drop_column(...) # alembic:allow-danger: 列已废弃,无数据 |
行尾必须写原因 |
SKIP_REL_PATHS |
文件长度 | scripts/check_file_length.py |
本文件第一节登记 |
eslint overrides |
ESLint 规则 | eslint.config.js |
本文件第一节登记 |
原则:行内豁免是最后的手段,优先重构消除。每条豁免都应有明确的消除路径或充分的永久理由。
7.1 Bandit # nosec 登记¶
| 文件 | 规则 | 原因 | 消除计划 |
|---|---|---|---|
src/backend/core/services/model_pool_router.py |
B311 | 模型池同优先级账号轮询、层内加权随机选模型,属负载均衡非加密 | 永久(业务需要伪随机分流) |
src/messaging/streams/active_user_registry.py |
B311 | 加权轮盘赌抽取高优用户做任务调度,属公平调度非安全场景 | 永久(业务需要伪随机分流) |
src/backend/core/services/queue_sim_engine.py |
B311 | 队列仿真引擎会员抽样/延迟抖动/失败率/Key 分配,非加密 | 永久(仿真专用伪随机) |
八、配置漂移校验(待办 / 进行中)¶
| 项 | 说明 | 负责人 | 状态 |
|---|---|---|---|
| env ↔ Settings ↔ ConfigMap | scripts/check_env_settings_sync.py;CI job Env / Settings Sync |
后端负责人 | ✅ 已落地 |
| 公共基座保护路径 | scripts/check_protected_paths.py + .github/CODEOWNERS;PR job Protected Paths |
后端负责人 | ✅ 已落地 |
| 规范文档 | docs/standards/config-governance.md |
后端负责人 | ✅ 已落地 |
新增配置 KEY 时:先改对应 settings/<domain>.py 与 env/.env.<domain>.example,再改 ConfigMap 域文件,最后跑上述脚本。
九、核对命令速查¶
# 文件长度豁免清单 + 当前行数
poetry run python scripts/check_file_length.py --list-debt
# 全量文件长度检查(零 FAIL 为达标)
poetry run python scripts/check_file_length.py
# 全量 pre-commit 自检
poetry run pre-commit run --all-files
# 单前端全量门禁(含 test + build)
make admin-check
make agent-check
# 后端全量(含 test + cov)
make check