feat(migration): ADR-028 migration architecture refactor

- เพิ่ม POST /api/ai/jobs + GET /api/ai/jobs/:jobId endpoints (FR-001, FR-002)
- เพิ่ม BullMQ Worker MigrateDocumentWorker + OCR auto-detect (FR-003, FR-004)
- เพิ่ม cleanup-temp-files + expire-pending-reviews workers (FR-005, FR-005a/b)
- สร้าง SQL deltas: tags, correspondence_tags, alter migration_review_queue (FR-006, ADR-009)
- เพิ่ม MigrationReviewService.commitRecord() + SELECT FOR UPDATE (FR-007, FR-007a)
- เพิ่ม CASL permission migration.commit + MigrationReviewController (FR-007)
- สร้าง TagsModule + TagsService + TagsController (US3)
- สร้าง Migration Review Queue frontend page + ReviewQueueTable (US2)
- อัปเดต n8n guide: deterministic Idempotency-Key + token pre-flight (FR-001a, FR-010a/b)
- สร้าง spec.md, plan.md, tasks.md, data-model.md, contracts/, quickstart.md
- สร้าง ADR-028 document + validation-report.md (PASS 32/32 tasks, 173/173 tests)
This commit is contained in:
2026-05-22 17:10:07 +07:00
parent 990d80e16d
commit a2973be208
55 changed files with 4256 additions and 107 deletions
+39
View File
@@ -53,8 +53,10 @@ import { AiRagQueryDto } from './dto/ai-rag-query.dto';
import { ExtractDocumentDto } from './dto/extract-document.dto';
import { AiCallbackDto } from './dto/ai-callback.dto';
import { CreateAiJobDto } from './dto/create-ai-job.dto';
import { SubmitAiJobDto } from './dto/submit-ai-job.dto';
import { MigrationUpdateDto } from './dto/migration-update.dto';
import { MigrationQueryDto } from './dto/migration-query.dto';
import { ValidationException } from '../../common/exceptions';
import {
ApproveLegacyMigrationDto,
LegacyMigrationIngestDto,
@@ -171,6 +173,43 @@ export class AiController {
return this.aiService.getAiJobStatus(jobId);
}
@Post('jobs')
@UseGuards(JwtAuthGuard, AiEnabledGuard, RbacGuard)
@ApiBearerAuth()
@RequirePermission('ai.suggest')
@HttpCode(HttpStatus.ACCEPTED)
@ApiOperation({
summary: 'Submit AI migration job — ส่งงานย้ายเอกสารให้ AI ประมวลผล',
description:
'รับ tempAttachmentId/documentNumber แล้วส่งงานย้ายเอกสารเข้า BullMQ เพื่อรอการประมวลผล',
})
@ApiHeader({
name: 'Idempotency-Key',
description: 'Unique key เพื่อป้องกัน duplicate AI job',
required: true,
})
async submitMigrationJob(
@Body() dto: SubmitAiJobDto,
@Headers('idempotency-key') idempotencyKey: string
) {
if (!idempotencyKey) {
throw new ValidationException('Idempotency-Key header is required');
}
return this.aiService.submitMigrationJob(dto, idempotencyKey);
}
@Get('jobs/:jobId')
@UseGuards(JwtAuthGuard, RbacGuard)
@ApiBearerAuth()
@RequirePermission('ai.suggest')
@ApiOperation({
summary: 'AI Job Status polling by jobId',
})
@ApiParam({ name: 'jobId', description: 'BullMQ job id' })
async getAiJobStatusById(@Param('jobId') jobId: string) {
return this.aiService.getAiJobStatus(jobId);
}
@Post('extract')
@UseGuards(JwtAuthGuard, AiEnabledGuard, RbacGuard)
@ApiBearerAuth()