fix(M-AI-02-GATE): restore physical column names, composite UNIQUE, lifecycle spec, migrate deploy
Some checks failed
Deploy API Server / build-and-deploy (push) Failing after 23s
Some checks failed
Deploy API Server / build-and-deploy (push) Failing after 23s
Section 2: @map("errorMessage") + @map("completedAt") decouple logical from physical names.
Forward-fix migration reverts physical columns to original names.
Section 3: jobType VARCHAR(64) → VARCHAR(32) restored.
Section 4: lifecycleStatus mapping restored to spec (pending→queued, processing→running,
completed→succeeded, failed→failed). Enum: created|queued|running|retrying|succeeded|
failed|cancel_requested|cancelled.
Section 5: idempotencyKey UNIQUE changed from single-column to composite
(userId, jobType, idempotencyKey). MySQL allows multiple NULLs.
Section 6: triggerType backfill now documents evidence basis per record.
Section 7: CI deploy.yml uses prisma migrate deploy (not db push).
Migration baseline established (28 rows in _prisma_migrations).
prisma migrate diff: No difference detected.
332 tests pass.
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
2437cc0aa3
commit
98e442e666
@ -59,10 +59,12 @@ jobs:
|
||||
echo "[deploy] No failed migrations found"
|
||||
fi
|
||||
|
||||
- name: Sync database schema
|
||||
- name: Apply database migrations
|
||||
run: |
|
||||
cd /tmp/api-server
|
||||
npx prisma db push --skip-generate
|
||||
npx prisma migrate deploy
|
||||
env:
|
||||
DATABASE_URL: ${{ secrets.DATABASE_URL }}
|
||||
|
||||
- name: Deploy NestJS API
|
||||
run: |
|
||||
|
||||
@ -0,0 +1,20 @@
|
||||
-- M-AI-02-GATE-FIX: 恢复物理列名、jobType 类型、idempotencyKey 复合唯一约束
|
||||
|
||||
-- 1. 恢复物理列名:internalErrorMessage → errorMessage(保留内部字段名 internalErrorMessage 为 Prisma 逻辑名)
|
||||
ALTER TABLE `AiAnalysisJob`
|
||||
CHANGE COLUMN `internalErrorMessage` `errorMessage` TEXT NULL;
|
||||
|
||||
-- 2. 恢复物理列名:finishedAt → completedAt(保留内部字段名 finishedAt 为 Prisma 逻辑名)
|
||||
ALTER TABLE `AiAnalysisJob`
|
||||
CHANGE COLUMN `finishedAt` `completedAt` DATETIME(3) NULL;
|
||||
|
||||
-- 3. 恢复 jobType VARCHAR(64) → VARCHAR(32)
|
||||
ALTER TABLE `AiAnalysisJob`
|
||||
MODIFY COLUMN `jobType` VARCHAR(32) NOT NULL;
|
||||
|
||||
-- 4. 修复 idempotencyKey 唯一约束:单列 → 复合 (userId, jobType, idempotencyKey)
|
||||
ALTER TABLE `AiAnalysisJob`
|
||||
DROP INDEX `AiAnalysisJob_idempotencyKey_key`;
|
||||
|
||||
ALTER TABLE `AiAnalysisJob`
|
||||
ADD UNIQUE INDEX `AiAnalysisJob_userId_jobType_idempotencyKey_key` (`userId`, `jobType`, `idempotencyKey`);
|
||||
@ -569,15 +569,15 @@ model AiJob {
|
||||
userId String
|
||||
sessionId String?
|
||||
answerId String?
|
||||
jobType String @db.VarChar(64)
|
||||
jobType String @db.VarChar(32)
|
||||
// ── M-AI-02-03: 身份与路由 ──
|
||||
triggerType String @default("api") @db.VarChar(32)
|
||||
triggerType String? @db.VarChar(32)
|
||||
queueName String @default("ai-interactive") @db.VarChar(64)
|
||||
priority Int @default(0)
|
||||
targetType String? @db.VarChar(32)
|
||||
targetId String? @db.VarChar(255)
|
||||
parentJobId String? @db.VarChar(255)
|
||||
idempotencyKey String? @unique @db.VarChar(255)
|
||||
idempotencyKey String? @db.VarChar(255)
|
||||
retriedFromJobId String? @db.VarChar(255)
|
||||
inputRef String? @db.VarChar(255)
|
||||
inputSchemaVersion String? @db.VarChar(100)
|
||||
@ -607,12 +607,12 @@ model AiJob {
|
||||
// ── M-AI-02-04: 错误信息 ──
|
||||
errorCode String? @db.VarChar(100)
|
||||
publicErrorMessage String? @db.VarChar(500)
|
||||
internalErrorMessage String? @db.Text
|
||||
internalErrorMessage String? @map("errorMessage") @db.Text
|
||||
// ── 时间戳 ──
|
||||
progress Int @default(0)
|
||||
queuedAt DateTime?
|
||||
startedAt DateTime?
|
||||
finishedAt DateTime?
|
||||
finishedAt DateTime? @map("completedAt")
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@ -632,6 +632,7 @@ model AiJob {
|
||||
@@index([parentJobId])
|
||||
@@index([lifecycleStatus, createdAt])
|
||||
@@index([queueName, lifecycleStatus, createdAt])
|
||||
@@unique([userId, jobType, idempotencyKey])
|
||||
|
||||
@@map("AiAnalysisJob")
|
||||
}
|
||||
|
||||
@ -5,9 +5,10 @@ import { PrismaService } from '../../infrastructure/database/prisma.service';
|
||||
export class AiAnalysisRepository {
|
||||
constructor(private readonly prisma: PrismaService) {}
|
||||
|
||||
// ── M-AI-02-10: 旧状态 → 新 lifecycleStatus 映射 ──
|
||||
// ── M-AI-02-10: 旧 status → 新 lifecycleStatus 映射 ──
|
||||
// Spec: created|queued|running|retrying|succeeded|failed|cancel_requested|cancelled
|
||||
private static readonly STATUS_TO_LIFECYCLE: Record<string, string> = {
|
||||
pending: 'pending',
|
||||
pending: 'queued',
|
||||
processing: 'running',
|
||||
completed: 'succeeded',
|
||||
failed: 'failed',
|
||||
@ -23,8 +24,7 @@ export class AiAnalysisRepository {
|
||||
status: 'pending',
|
||||
queuedAt: new Date(),
|
||||
// ── M-AI-02-10 Shadow Write ──
|
||||
lifecycleStatus: 'pending',
|
||||
triggerType: 'api',
|
||||
lifecycleStatus: 'queued',
|
||||
queueName: 'ai-interactive',
|
||||
inputSchemaVersion: 'legacy-v1',
|
||||
attemptCount: 0,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user