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"
|
echo "[deploy] No failed migrations found"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Sync database schema
|
- name: Apply database migrations
|
||||||
run: |
|
run: |
|
||||||
cd /tmp/api-server
|
cd /tmp/api-server
|
||||||
npx prisma db push --skip-generate
|
npx prisma migrate deploy
|
||||||
|
env:
|
||||||
|
DATABASE_URL: ${{ secrets.DATABASE_URL }}
|
||||||
|
|
||||||
- name: Deploy NestJS API
|
- name: Deploy NestJS API
|
||||||
run: |
|
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
|
userId String
|
||||||
sessionId String?
|
sessionId String?
|
||||||
answerId String?
|
answerId String?
|
||||||
jobType String @db.VarChar(64)
|
jobType String @db.VarChar(32)
|
||||||
// ── M-AI-02-03: 身份与路由 ──
|
// ── M-AI-02-03: 身份与路由 ──
|
||||||
triggerType String @default("api") @db.VarChar(32)
|
triggerType String? @db.VarChar(32)
|
||||||
queueName String @default("ai-interactive") @db.VarChar(64)
|
queueName String @default("ai-interactive") @db.VarChar(64)
|
||||||
priority Int @default(0)
|
priority Int @default(0)
|
||||||
targetType String? @db.VarChar(32)
|
targetType String? @db.VarChar(32)
|
||||||
targetId String? @db.VarChar(255)
|
targetId String? @db.VarChar(255)
|
||||||
parentJobId 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)
|
retriedFromJobId String? @db.VarChar(255)
|
||||||
inputRef String? @db.VarChar(255)
|
inputRef String? @db.VarChar(255)
|
||||||
inputSchemaVersion String? @db.VarChar(100)
|
inputSchemaVersion String? @db.VarChar(100)
|
||||||
@ -607,12 +607,12 @@ model AiJob {
|
|||||||
// ── M-AI-02-04: 错误信息 ──
|
// ── M-AI-02-04: 错误信息 ──
|
||||||
errorCode String? @db.VarChar(100)
|
errorCode String? @db.VarChar(100)
|
||||||
publicErrorMessage String? @db.VarChar(500)
|
publicErrorMessage String? @db.VarChar(500)
|
||||||
internalErrorMessage String? @db.Text
|
internalErrorMessage String? @map("errorMessage") @db.Text
|
||||||
// ── 时间戳 ──
|
// ── 时间戳 ──
|
||||||
progress Int @default(0)
|
progress Int @default(0)
|
||||||
queuedAt DateTime?
|
queuedAt DateTime?
|
||||||
startedAt DateTime?
|
startedAt DateTime?
|
||||||
finishedAt DateTime?
|
finishedAt DateTime? @map("completedAt")
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
|
|
||||||
@ -632,6 +632,7 @@ model AiJob {
|
|||||||
@@index([parentJobId])
|
@@index([parentJobId])
|
||||||
@@index([lifecycleStatus, createdAt])
|
@@index([lifecycleStatus, createdAt])
|
||||||
@@index([queueName, lifecycleStatus, createdAt])
|
@@index([queueName, lifecycleStatus, createdAt])
|
||||||
|
@@unique([userId, jobType, idempotencyKey])
|
||||||
|
|
||||||
@@map("AiAnalysisJob")
|
@@map("AiAnalysisJob")
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,9 +5,10 @@ import { PrismaService } from '../../infrastructure/database/prisma.service';
|
|||||||
export class AiAnalysisRepository {
|
export class AiAnalysisRepository {
|
||||||
constructor(private readonly prisma: PrismaService) {}
|
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> = {
|
private static readonly STATUS_TO_LIFECYCLE: Record<string, string> = {
|
||||||
pending: 'pending',
|
pending: 'queued',
|
||||||
processing: 'running',
|
processing: 'running',
|
||||||
completed: 'succeeded',
|
completed: 'succeeded',
|
||||||
failed: 'failed',
|
failed: 'failed',
|
||||||
@ -23,8 +24,7 @@ export class AiAnalysisRepository {
|
|||||||
status: 'pending',
|
status: 'pending',
|
||||||
queuedAt: new Date(),
|
queuedAt: new Date(),
|
||||||
// ── M-AI-02-10 Shadow Write ──
|
// ── M-AI-02-10 Shadow Write ──
|
||||||
lifecycleStatus: 'pending',
|
lifecycleStatus: 'queued',
|
||||||
triggerType: 'api',
|
|
||||||
queueName: 'ai-interactive',
|
queueName: 'ai-interactive',
|
||||||
inputSchemaVersion: 'legacy-v1',
|
inputSchemaVersion: 'legacy-v1',
|
||||||
attemptCount: 0,
|
attemptCount: 0,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user