fix: 修复 3 个 P0 端点 500 错误根因(async/sync 不匹配 + edit_plan_id 残留) #114

Merged
xiaoxia merged 2 commits from fix/p0-endpoint-500-root-cause into develop 2026-06-29 20:28:26 +08:00
Owner

问题

自动化测试 v0.1.97 显示 3 个 P0 端点仍返回 500:

  • GET /api/v1/dashboard/overview
  • GET /api/v1/assets?library_id=xxx
  • POST /api/v1/generation/tasks

根因分析

Bug 1:素材列表 500

asset_repository 所有方法为 async def,但调用方(ListAssetsUseCase.execute()、路由层)均为同步上下文,未 await 导致返回 coroutine 对象而非实际数据 → TypeError。

Bug 2:创建生成任务 500

GenerationTaskModel 仍定义 edit_plan_id 列,但 Alembic 迁移 011 已从数据库删除该列 → INSERT 报 "column does not exist"。

Bug 3:Dashboard 概览 500

generation_task_repository._to_domain() 访问 model.edit_plan_id,该列已被迁移 011 删除 → AttributeError。

修复内容

文件 修改
packages/adapters/sqlalchemy_impl/asset_repository.py 所有 async defdef
packages/ports/asset_repository.py 接口 async defdef
packages/adapters/sqlalchemy_impl/models.py 移除 edit_plan_id 列定义
packages/adapters/sqlalchemy_impl/generation_task_repository.py 移除 _to_domain/create/update 中的 edit_plan_id
packages/domain/generation_task.py 移除 edit_plan_id 字段及 create() 参数
packages/application/generation_tasks.py 移除 Command 中的 edit_plan_id 及 UseCase 传参
apps/api/app/api/routes/generation_tasks.py async defdef,移除 await,移除 edit_plan_id
apps/api/app/api/routes/task_center.py 移除两处 retry 中的 edit_plan_id

测试建议

部署后重新运行 v0.1.97 自动化测试,验证 3 个 P0 端点返回非 500。

## 问题 自动化测试 v0.1.97 显示 3 个 P0 端点仍返回 500: - `GET /api/v1/dashboard/overview` - `GET /api/v1/assets?library_id=xxx` - `POST /api/v1/generation/tasks` ## 根因分析 ### Bug 1:素材列表 500 `asset_repository` 所有方法为 `async def`,但调用方(`ListAssetsUseCase.execute()`、路由层)均为同步上下文,未 `await` 导致返回 coroutine 对象而非实际数据 → TypeError。 ### Bug 2:创建生成任务 500 `GenerationTaskModel` 仍定义 `edit_plan_id` 列,但 Alembic 迁移 011 已从数据库删除该列 → INSERT 报 "column does not exist"。 ### Bug 3:Dashboard 概览 500 `generation_task_repository._to_domain()` 访问 `model.edit_plan_id`,该列已被迁移 011 删除 → AttributeError。 ## 修复内容 | 文件 | 修改 | |------|------| | `packages/adapters/sqlalchemy_impl/asset_repository.py` | 所有 `async def` → `def` | | `packages/ports/asset_repository.py` | 接口 `async def` → `def` | | `packages/adapters/sqlalchemy_impl/models.py` | 移除 `edit_plan_id` 列定义 | | `packages/adapters/sqlalchemy_impl/generation_task_repository.py` | 移除 `_to_domain`/`create`/`update` 中的 `edit_plan_id` | | `packages/domain/generation_task.py` | 移除 `edit_plan_id` 字段及 `create()` 参数 | | `packages/application/generation_tasks.py` | 移除 Command 中的 `edit_plan_id` 及 UseCase 传参 | | `apps/api/app/api/routes/generation_tasks.py` | `async def` → `def`,移除 `await`,移除 `edit_plan_id` | | `apps/api/app/api/routes/task_center.py` | 移除两处 retry 中的 `edit_plan_id` | ## 测试建议 部署后重新运行 v0.1.97 自动化测试,验证 3 个 P0 端点返回非 500。
xiaoxia added 1 commit 2026-06-29 20:11:10 +08:00
fix: 修复 3 个 P0 端点 500 错误的真正根因
CI/CD Pipeline / Validate Code Quality And Tests (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Lint (pull_request) Has been cancelled
f9e8d6efdc
Bug 1 — GET /api/v1/assets → 500:
  asset_repository 所有方法为 async def,但调用方为同步上下文,
  未 await 导致返回 coroutine 对象而非实际数据。
  修复:全部改为 def(同步),与 SQLAlchemy 同步 Session 一致。

Bug 2 — POST /api/v1/generation/tasks → 500:
  GenerationTaskModel 仍定义 edit_plan_id 列,但迁移 011 已从数据库
  删除该列,INSERT 时报 column does not exist。
  修复:从 model、repository、domain entity、application use case、
  所有路由中彻底移除 edit_plan_id 引用。

Bug 3 — GET /api/v1/dashboard/overview → 500:
  generation_task_repository._to_domain() 访问 model.edit_plan_id,
  该列已被迁移 011 删除,触发 AttributeError。
  修复:同上,移除 edit_plan_id 引用。

涉及文件:
  - packages/adapters/sqlalchemy_impl/asset_repository.py (async→sync)
  - packages/ports/asset_repository.py (async→sync 接口)
  - packages/adapters/sqlalchemy_impl/models.py (移除 edit_plan_id 列)
  - packages/adapters/sqlalchemy_impl/generation_task_repository.py
  - packages/domain/generation_task.py
  - packages/application/generation_tasks.py
  - apps/api/app/api/routes/generation_tasks.py
  - apps/api/app/api/routes/task_center.py
Author
Owner

🔍 代码审查报告 — PR #114

分支: fix/p0-endpoint-500-root-causedevelop
变更范围: 8 个文件,+20/-32
Head SHA: f9e8d6ef


修复验证通过

Bug 1:素材列表 500 — async/sync 不匹配

  • packages/ports/asset_repository.py:7 个方法全部从 async def 改为 def
  • packages/adapters/sqlalchemy_impl/asset_repository.py:7 个方法全部从 async def 改为 def
  • apps/api/app/api/routes/generation_tasks.pycreate_generation_task_resolve_project_and_libraryasync def 改为 def,移除 await 调用
  • apps/api/app/api/routes/assets.py:已确认为同步路由,无 async/await 残留
  • Port 与 Adapter 签名一致

Bug 2:创建生成任务 500 — edit_plan_id 残留

  • 域实体 GenerationTask:已移除 edit_plan_id 字段(dataclass 属性 + create() 参数)
  • 应用层 CreateGenerationTaskCommand:已移除 edit_plan_id 字段
  • SQLAlchemy Model GenerationTaskModel:已移除 edit_plan_id
  • Adapter _to_domain/create/update:已移除 edit_plan_id 映射
  • 路由层 generation_tasks.py retry:移除 edit_plan_id=task.edit_plan_id
  • 路由层 task_center.py 两处 retry:移除 edit_plan_id=task.edit_plan_id
  • 全仓库无 edit_plan_id 代码残留

Bug 3:Dashboard 概览 500

  • 根因同 Bug 2(_to_domain 引用已删除的列),修复后一并解决

P1:迁移 015 重新添加了已删除的 edit_plan_id

alembic/versions/015_add_generation_task_extensions.pyupgrade() 中包含:

op.add_column("generation_tasks", sa.Column("edit_plan_id", sa.String(32), nullable=False, server_default=""))

但迁移 011 已经明确删除了该列:

conn.execute(sa.text("ALTER TABLE generation_tasks DROP COLUMN IF EXISTS edit_plan_id"))

本 PR 又从代码层面彻底移除了 edit_plan_id。这意味着:

  • 代码中无任何地方使用 edit_plan_id
  • 但数据库会因迁移 015 重新拥有这个孤立列

建议修复:

  • 从迁移 015 的 upgrade() 中删除 add_column("edit_plan_id", ...)
  • 从迁移 015 的 downgrade() 中删除对应的 drop_column("edit_plan_id")

总结

级别 数量 说明
P0 0
P1 1 迁移 015 不应重新添加 edit_plan_id
P2 0

结论:🟡 需修复 P1 迁移问题后可合并。 代码层面的 async/sync 对齐和 edit_plan_id 清理都正确且完整。

## 🔍 代码审查报告 — PR #114 **分支:** `fix/p0-endpoint-500-root-cause` → `develop` **变更范围:** 8 个文件,+20/-32 **Head SHA:** `f9e8d6ef` --- ### ✅ 修复验证通过 **Bug 1:素材列表 500 — async/sync 不匹配** - `packages/ports/asset_repository.py`:7 个方法全部从 `async def` 改为 `def` ✅ - `packages/adapters/sqlalchemy_impl/asset_repository.py`:7 个方法全部从 `async def` 改为 `def` ✅ - `apps/api/app/api/routes/generation_tasks.py`:`create_generation_task` 和 `_resolve_project_and_library` 从 `async def` 改为 `def`,移除 `await` 调用 ✅ - `apps/api/app/api/routes/assets.py`:已确认为同步路由,无 `async`/`await` 残留 ✅ - Port 与 Adapter 签名一致 ✅ **Bug 2:创建生成任务 500 — edit_plan_id 残留** - 域实体 `GenerationTask`:已移除 `edit_plan_id` 字段(dataclass 属性 + `create()` 参数)✅ - 应用层 `CreateGenerationTaskCommand`:已移除 `edit_plan_id` 字段 ✅ - SQLAlchemy Model `GenerationTaskModel`:已移除 `edit_plan_id` 列 ✅ - Adapter `_to_domain`/`create`/`update`:已移除 `edit_plan_id` 映射 ✅ - 路由层 `generation_tasks.py` retry:移除 `edit_plan_id=task.edit_plan_id` ✅ - 路由层 `task_center.py` 两处 retry:移除 `edit_plan_id=task.edit_plan_id` ✅ - 全仓库无 `edit_plan_id` 代码残留 ✅ **Bug 3:Dashboard 概览 500** - 根因同 Bug 2(`_to_domain` 引用已删除的列),修复后一并解决 ✅ --- ### ❌ P1:迁移 015 重新添加了已删除的 `edit_plan_id` 列 `alembic/versions/015_add_generation_task_extensions.py` 的 `upgrade()` 中包含: ```python op.add_column("generation_tasks", sa.Column("edit_plan_id", sa.String(32), nullable=False, server_default="")) ``` 但迁移 011 已经明确删除了该列: ```python conn.execute(sa.text("ALTER TABLE generation_tasks DROP COLUMN IF EXISTS edit_plan_id")) ``` 本 PR 又从代码层面彻底移除了 `edit_plan_id`。这意味着: - 代码中无任何地方使用 `edit_plan_id` - 但数据库会因迁移 015 重新拥有这个**孤立列** **建议修复:** - 从迁移 015 的 `upgrade()` 中删除 `add_column("edit_plan_id", ...)` - 从迁移 015 的 `downgrade()` 中删除对应的 `drop_column("edit_plan_id")` --- ### 总结 | 级别 | 数量 | 说明 | |------|------|------| | P0 | 0 | — | | P1 | 1 | 迁移 015 不应重新添加 `edit_plan_id` 列 | | P2 | 0 | — | **结论:🟡 需修复 P1 迁移问题后可合并。** 代码层面的 async/sync 对齐和 `edit_plan_id` 清理都正确且完整。
xiaoxia added 1 commit 2026-06-29 20:23:25 +08:00
fix: 从迁移 015 中移除残留的 edit_plan_id(代码审计 P1)
CI/CD Pipeline / Validate Code Quality And Tests (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Lint (pull_request) Has been cancelled
7783f36172
迁移 011 已删除 edit_plan_id 列,迁移 015 不应再 add_column。
同时从 downgrade() 中移除对应的 drop_column。
Author
Owner

复审通过

Head SHA: 7783f361 | 9 文件 +20/-34

P1 修复验证

问题 修复状态
迁移 015 重新添加 edit_plan_id 已从 upgrade/downgrade 移除

全量验证结果

Bug 1:async/sync 不匹配

  • packages/ports/asset_repository.py:7 个方法全部 async defdef
  • packages/adapters/sqlalchemy_impl/asset_repository.py:7 个方法同步对齐
  • routes/generation_tasks.pycreate_generation_task + _resolve_project_and_library 改为 def,移除所有 await
  • routes/assets.py:已全部为同步,无残留
  • routes/task_center.py:无 async/await 残留

Bug 2:edit_plan_id 残留

  • 域实体 GenerationTask:已移除
  • 应用层 CreateGenerationTaskCommand:已移除
  • ORM 模型 GenerationTaskModel:已移除
  • Adapter _to_domain/create/update:已移除
  • 路由层 retry 函数(2 处):已移除
  • 迁移 015:已从 upgrade/downgrade 移除
  • Schema 层无残留

Bug 3:Dashboard 500

  • 根因同 Bug 2,修复后一并解决

迁移链验证

011(DROP edit_plan_id)→ 012-014 → 015(不再 ADD edit_plan_id) 一致


结论: 通过,可以合并。 3 个 P0 端点 500 的根因均已正确修复。

## ✅ 复审通过 **Head SHA:** `7783f361` | **9 文件** +20/-34 ### P1 修复验证 | 问题 | 修复状态 | |------|----------| | 迁移 015 重新添加 `edit_plan_id` 列 | ✅ 已从 upgrade/downgrade 移除 | ### 全量验证结果 **Bug 1:async/sync 不匹配** ✅ - `packages/ports/asset_repository.py`:7 个方法全部 `async def` → `def` - `packages/adapters/sqlalchemy_impl/asset_repository.py`:7 个方法同步对齐 - `routes/generation_tasks.py`:`create_generation_task` + `_resolve_project_and_library` 改为 `def`,移除所有 `await` - `routes/assets.py`:已全部为同步,无残留 - `routes/task_center.py`:无 async/await 残留 **Bug 2:edit_plan_id 残留** ✅ - 域实体 `GenerationTask`:已移除 ✅ - 应用层 `CreateGenerationTaskCommand`:已移除 ✅ - ORM 模型 `GenerationTaskModel`:已移除 ✅ - Adapter `_to_domain`/`create`/`update`:已移除 ✅ - 路由层 retry 函数(2 处):已移除 ✅ - 迁移 015:已从 upgrade/downgrade 移除 ✅ - Schema 层无残留 ✅ **Bug 3:Dashboard 500** ✅ - 根因同 Bug 2,修复后一并解决 ### 迁移链验证 011(DROP edit_plan_id)→ 012-014 → 015(不再 ADD edit_plan_id)✅ 一致 --- **结论:✅ 通过,可以合并。** 3 个 P0 端点 500 的根因均已正确修复。
xiaoxia merged commit 6b5959bafd into develop 2026-06-29 20:28:26 +08:00
Author
Owner

🔴 三轮审查 — 发现真正的 P0 根因

Head SHA: 7b08029e(PR 分支有 1 个新 commit 未合并到 develop)


P0:develop 分支 AssetModel 存在幽灵列 storage_key

问题: develop 分支的 AssetModelpackages/adapters/sqlalchemy_impl/models.py)定义了:

storage_key = Column(String(255), nullable=False)

但数据库 assets 表中不存在 storage_key 列(实际列名是 file_url)。

影响: SQLAlchemy 查询 assets 表时会 SELECT 一个不存在的列,PostgreSQL 抛出 OperationalError,触发 general_exception_handler → 500。

受影响的端点:

  • GET /api/v1/assets — 直接查 assets 表 → 500
  • GET /api/v1/dashboard/overviewsum_storage_by_project_idscount_by_project_ids 查 assets 表 → 500
  • POST /api/v1/generation/tasks_resolve_project_and_libraryasset_repository.find_by_id() 查 assets 表 → 500

修复已在 PR 分支(commit 7b08029e):

  • PR 分支的 models.py 已移除 storage_key 列定义
  • asset_repository.py 正确使用 model.file_urlasset.storage_key 映射

但此 commit 未合并到 develop!

📊 根因链

PR #114 合并到 develop (20:28)
  ↓ 但只合并了 async/sync + edit_plan_id 修复
  ↓ develop 仍保留 AssetModel.storage_key 幽灵列
  ↓ v0.1.98 部署 (20:34)
  ↓ SQLAlchemy SELECT storage_key → 列不存在 → OperationalError
  ↓ 3 个 P0 端点全部 500

结论

PR #114 的 async/sync 和 edit_plan_id 修复是正确的。但 500 的真正根因是 AssetModel.storage_key 幽灵列。

紧急操作: 需要将 commit 7b08029e(删除 storage_key 幽灵列)合并到 develop 并重新部署。

## 🔴 三轮审查 — 发现真正的 P0 根因 **Head SHA:** `7b08029e`(PR 分支有 1 个新 commit 未合并到 develop) --- ### ❌ P0:develop 分支 `AssetModel` 存在幽灵列 `storage_key` **问题:** develop 分支的 `AssetModel`(`packages/adapters/sqlalchemy_impl/models.py`)定义了: ```python storage_key = Column(String(255), nullable=False) ``` 但数据库 `assets` 表中**不存在** `storage_key` 列(实际列名是 `file_url`)。 **影响:** SQLAlchemy 查询 `assets` 表时会 SELECT 一个不存在的列,PostgreSQL 抛出 `OperationalError`,触发 `general_exception_handler` → 500。 **受影响的端点:** - `GET /api/v1/assets` — 直接查 assets 表 → 500 - `GET /api/v1/dashboard/overview` — `sum_storage_by_project_ids` 和 `count_by_project_ids` 查 assets 表 → 500 - `POST /api/v1/generation/tasks` — `_resolve_project_and_library` 中 `asset_repository.find_by_id()` 查 assets 表 → 500 **修复已在 PR 分支(commit `7b08029e`):** - PR 分支的 `models.py` 已移除 `storage_key` 列定义 ✅ - `asset_repository.py` 正确使用 `model.file_url` ↔ `asset.storage_key` 映射 ✅ **但此 commit 未合并到 develop!** ### 📊 根因链 ``` PR #114 合并到 develop (20:28) ↓ 但只合并了 async/sync + edit_plan_id 修复 ↓ develop 仍保留 AssetModel.storage_key 幽灵列 ↓ v0.1.98 部署 (20:34) ↓ SQLAlchemy SELECT storage_key → 列不存在 → OperationalError ↓ 3 个 P0 端点全部 500 ``` ### 结论 PR #114 的 async/sync 和 edit_plan_id 修复是正确的。但 500 的真正根因是 `AssetModel.storage_key` 幽灵列。 **紧急操作:** 需要将 commit `7b08029e`(删除 `storage_key` 幽灵列)合并到 develop 并重新部署。
Sign in to join this conversation.