feat(ai): unify AI architecture, implement RAG and legacy migration
CI / CD Pipeline / build (push) Failing after 5m36s
CI / CD Pipeline / deploy (push) Has been skipped

This commit is contained in:
2026-05-15 11:10:44 +07:00
parent 0240d80da5
commit 6cb3ae10ee
56 changed files with 6051 additions and 304 deletions
@@ -0,0 +1,21 @@
// File: src/modules/ai/dto/ai-rag-query.dto.ts
// Change Log
// - 2026-05-14: เพิ่ม DTO สำหรับ BullMQ RAG Query ตาม ADR-023 Phase 4.
import { IsNotEmpty, IsString, IsUUID, MaxLength } from 'class-validator';
import { ApiProperty } from '@nestjs/swagger';
/** DTO สำหรับส่ง RAG query เข้า BullMQ queue (FR-009, FR-010) */
export class AiRagQueryDto {
@ApiProperty({
description: 'คำถามสำหรับ RAG ไม่เกิน 500 ตัวอักษร',
maxLength: 500,
})
@IsString()
@IsNotEmpty()
@MaxLength(500)
question!: string;
@ApiProperty({ description: 'publicId ของโครงการ (ADR-019) เพื่อ isolation' })
@IsUUID()
projectPublicId!: string;
}
@@ -0,0 +1,26 @@
// File: src/modules/ai/dto/delete-audit-logs.dto.ts
// Change Log
// - 2026-05-14: ย้าย DeleteAuditLogsQueryDto จาก ai.controller.ts เข้า dto/ folder (🟢 LOW-2).
import { IsInt, IsOptional, IsUUID, Max, Min } from 'class-validator';
import { Type } from 'class-transformer';
import { ApiPropertyOptional } from '@nestjs/swagger';
/** Query params สำหรับ DELETE /ai/audit-logs (T026) */
export class DeleteAuditLogsQueryDto {
@ApiPropertyOptional({ description: 'UUID ของเอกสารที่ต้องการลบ log' })
@IsOptional()
@IsUUID()
documentPublicId?: string;
@ApiPropertyOptional({
description: 'ลบ log ที่เก่ากว่า N วัน (1-365)',
minimum: 1,
maximum: 365,
})
@IsOptional()
@Type(() => Number)
@IsInt()
@Min(1)
@Max(365)
olderThanDays?: number;
}
@@ -0,0 +1,122 @@
// File: src/modules/ai/dto/legacy-migration.dto.ts
// Change Log
// - 2026-05-14: เพิ่ม DTO สำหรับ ADR-023 legacy migration staging endpoints.
import {
IsEnum,
IsNotEmpty,
IsNumber,
IsObject,
IsOptional,
IsString,
IsUUID,
Max,
Min,
} from 'class-validator';
import { Transform } from 'class-transformer';
import { MigrationReviewRecordStatus } from '../entities/migration-review.entity';
export class LegacyMigrationRecordDto {
@IsString()
@IsOptional()
originalFileName?: string;
@IsObject()
@IsOptional()
extractedMetadata?: Record<string, unknown>;
@Transform(({ value }: { value: unknown }) =>
value === undefined || value === null || value === ''
? undefined
: Number(value)
)
@IsNumber()
@Min(0)
@Max(1)
@IsOptional()
confidenceScore?: number;
@IsEnum(MigrationReviewRecordStatus)
@IsOptional()
status?: MigrationReviewRecordStatus;
@IsString()
@IsOptional()
errorReason?: string;
}
export class LegacyMigrationIngestDto {
@IsString()
@IsNotEmpty()
batchId!: string;
@IsString()
@IsOptional()
source?: 'api' | 'folder-watcher';
@IsOptional()
records?: LegacyMigrationRecordDto[] | string;
}
export class LegacyMigrationQueueQueryDto {
@Transform(({ value }: { value: unknown }) =>
value === undefined ? 1 : Number(value)
)
@IsNumber()
@Min(1)
@IsOptional()
page?: number;
@Transform(({ value }: { value: unknown }) =>
value === undefined ? 20 : Number(value)
)
@IsNumber()
@Min(1)
@Max(100)
@IsOptional()
limit?: number;
@IsEnum(MigrationReviewRecordStatus)
@IsOptional()
status?: MigrationReviewRecordStatus;
}
export class ApproveLegacyMigrationDto {
@IsString()
@IsNotEmpty()
documentNumber!: string;
@IsString()
@IsNotEmpty()
subject!: string;
@IsString()
@IsNotEmpty()
categoryCode!: string;
@IsUUID()
projectPublicId!: string;
@IsUUID()
@IsOptional()
senderOrganizationPublicId?: string;
@IsUUID()
@IsOptional()
receiverOrganizationPublicId?: string;
@IsString()
@IsOptional()
issuedDate?: string;
@IsString()
@IsOptional()
receivedDate?: string;
@IsString()
@IsOptional()
body?: string;
@IsObject()
@IsOptional()
finalMetadata?: Record<string, unknown>;
}