feat(ticket): 工单协同后端——升级不搬数据、受理到验收、站内通知

- item.kind='ticket' + ticket_ext 1:1,迁移 0003 同时重写 item 状态约束为任务∪工单并集
- 工单状态机与动作权限:受理/开工/提交限办理人,验收限需求人(办理人不能自验)
- 升级时原任务转 blocked,验收通过后自动回流 todo,事件流两侧都留痕
- item_link(父子/相关)与 notification(只发当事人),三张表均开 RLS
- 抽出 serializers.py 复用任务序列化,修复工单响应缺 tags 导致前端白屏
- 评论与 PATCH 放开给工单;conftest 清库前终止残留事务,避免 TRUNCATE 死等锁
- tests/test_tickets.py 11 个用例,全量 57 passed
This commit is contained in:
dadao
2026-10-28 13:20:07 +08:00
parent 3d02fe845a
commit 30caaaf1d9
11 changed files with 1849 additions and 49 deletions
+11
View File
@@ -55,6 +55,17 @@ async def clean_database(migrated_database: None) -> AsyncIterator[None]:
settings.resolved_migration_database_url(), poolclass=NullPool
)
async with owner_engine.begin() as conn:
# 上一次跑挂掉(Ctrl-C、进程被杀)会留下 idle in transaction 的连接,
# 它握着行锁,TRUNCATE 就会一直等——不是测试慢,是被锁住了。
# 测试库是独占的,直接踢掉除自己以外的会话,再给个 lock_timeout 兜底。
await conn.execute(
text(
"SELECT pg_terminate_backend(pid) FROM pg_stat_activity "
"WHERE datname = current_database() "
"AND pid <> pg_backend_pid() AND state <> 'idle'"
)
)
await conn.execute(text("SET lock_timeout = '15s'"))
await conn.execute(text(f"TRUNCATE {_TABLES} CASCADE"))
await owner_engine.dispose()
reset_store_for_tests()