feat: #1775 默认项目/素材库幂等化(DB 唯一约束兜底) #1783

Merged
xiaoxia merged 2 commits from feature/default-project-idempotent-1775 into develop 2026-09-08 08:43:31 +08:00
Owner

背景

小程序端 getOrCreateDefaultProject 在重试、并发、前端重复调用下,仅靠"先查再插"会给同一用户重复创建默认项目和默认素材库。

改动

  • ProjectModel 新增 is_default: bool + 部分唯一索引 uq_projects_owner_defaultWHERE is_default=true,每用户至多一个默认项目)
  • asset_library_repository.get_or_create_default_library 用 IntegrityError 回滚重查,替代先查后插(修复并发 500)
  • project_repository.get_or_create_default_project 同上
  • 新增组合接口 POST /projects/ensure-default,一次拿到 project_id + image/video/voice 三类 library_id
  • 迁移 069:存量回填(每用户最早"默认项目"标 is_default=true)+ 创建部分唯一索引
  • schema 快照增量更新

测试

15 个新测试 + 99 个旧测试全过。覆盖:并发 10 次只建 1 个、跨用户/跨 kind 独立、IntegrityError 回滚重查、字段语义。

数据清理

存量重复数据清单 SQL 见 discussion,禁止自动删除,由主人/灵应确认后执行。

## 背景 小程序端 `getOrCreateDefaultProject` 在重试、并发、前端重复调用下,仅靠"先查再插"会给同一用户重复创建默认项目和默认素材库。 ## 改动 - `ProjectModel` 新增 `is_default: bool` + 部分唯一索引 `uq_projects_owner_default`(`WHERE is_default=true`,每用户至多一个默认项目) - `asset_library_repository.get_or_create_default_library` 用 IntegrityError 回滚重查,替代先查后插(修复并发 500) - `project_repository.get_or_create_default_project` 同上 - 新增组合接口 `POST /projects/ensure-default`,一次拿到 project_id + image/video/voice 三类 library_id - 迁移 069:存量回填(每用户最早"默认项目"标 is_default=true)+ 创建部分唯一索引 - schema 快照增量更新 ## 测试 15 个新测试 + 99 个旧测试全过。覆盖:并发 10 次只建 1 个、跨用户/跨 kind 独立、IntegrityError 回滚重查、字段语义。 ## 数据清理 存量重复数据清单 SQL 见 discussion,**禁止自动删除**,由主人/灵应确认后执行。
xiaoxia added 2 commits 2026-09-08 07:19:20 +08:00
问题:小程序端 getOrCreateDefaultProject 在重试/并发/重复调用下,
仅靠应用层"先查后插"不保证幂等,会给同一用户重复创建默认项目。
素材库虽有唯一约束 uq_asset_libraries_project_kind(迁移058),
但 ensure-default 接口未捕获冲突,并发下直接 500。

改动:
1. projects 表新增 is_default 列 + 部分唯一索引
   uq_projects_owner_default(owner_user_id WHERE is_default=true),
   保证每用户至多一个默认项目(迁移069,存量"默认项目"最早一条回填)
2. Project 实体/Model/Repository 增加 is_default 字段
3. 新增 get_or_create_default_project / find_default_by_owner:
   INSERT 冲突时捕获 IntegrityError 回滚重查,返回已有记录不报500
4. 素材库仓储新增 get_or_create_default_library(同上幂等语义)
5. 新增 POST /projects/ensure-default 组合接口:
   一次请求幂等返回默认项目 + 三类默认素材库ID
6. ensure-default 素材库接口改用幂等仓储方法(修复并发500)
7. in-memory 仓储同步幂等方法

测试:15 个新测试(并发10次只建1个、跨用户独立、IntegrityError恢复、
字段语义),99 个相关旧测试全过。
chore: schema 快照仅增量 is_default 列+部分唯一索引(修正缩进格式)
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m47s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m25s
AI Code Review / AI Code Review (pull_request) Successful in 6m19s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 3m6s
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 1s
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 1s
CI/CD Pipeline / Build Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Frontend Lint (pull_request) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / PR Build Web Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been skipped
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 14s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 14s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 1m25s
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Successful in 1m28s
CI/CD Pipeline / Validate - Style (pull_request) Successful in 1m56s
CI/CD Pipeline / Validate - Security (pull_request) Successful in 3m29s
CI/CD Pipeline / Unit Tests (pull_request) Failing after 6m10s
CI/CD Pipeline / Build Production API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Production (pull_request) Has been skipped
CI/CD Pipeline / Canary Release to Production (pull_request) Has been skipped
CI/CD Pipeline / CI Gate (pull_request) Failing after 3s
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
ACR Cleanup / ACR Image Cleanup (pull_request_target) Successful in 8s
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 22s
f6c18b3aa9

🚀 预览环境已部署

项目 详情
PR号 #1783
预览链接 https://pr-1783.preview.xiaoxiajianji.com
API环境 staging

💡 预览环境使用 staging API 数据,请勿在预览环境中操作重要数据。

🔄 每次提交新代码后预览环境会自动更新。

🗑️ PR 关闭或合并后,预览环境会自动清理。

🚀 **预览环境已部署** | 项目 | 详情 | |------|------| | PR号 | #1783 | | 预览链接 | [https://pr-1783.preview.xiaoxiajianji.com](https://pr-1783.preview.xiaoxiajianji.com) | | API环境 | staging | > 💡 预览环境使用 staging API 数据,请勿在预览环境中操作重要数据。 > > 🔄 每次提交新代码后预览环境会自动更新。 > > 🗑️ PR 关闭或合并后,预览环境会自动清理。
xiaoxia merged commit 9c0474ef9c into develop 2026-09-08 08:43:31 +08:00

🗑️ 预览环境已清理

PR #1783 已关闭或合并,对应的预览环境已被清理。

如有需要,可以重新打开 PR 来重新生成预览环境。

🗑️ **预览环境已清理** PR #1783 已关闭或合并,对应的预览环境已被清理。 > 如有需要,可以重新打开 PR 来重新生成预览环境。
Sign in to join this conversation.