feat: add report material workflows and gateway safeguards

This commit is contained in:
hectorzhao
2026-07-15 18:23:48 +08:00
parent cf9f4ce4cd
commit 7091a8bed4
41 changed files with 3606 additions and 71 deletions
@@ -0,0 +1,134 @@
ALTER TABLE "SmsSignature"
ADD COLUMN "materialVersion" INTEGER NOT NULL DEFAULT 1,
ADD COLUMN "pendingReport" BOOLEAN NOT NULL DEFAULT true,
ADD COLUMN "reportChangedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP;
ALTER TABLE "SmsDrainageInfo"
ADD COLUMN "materialVersion" INTEGER NOT NULL DEFAULT 1,
ADD COLUMN "pendingReport" BOOLEAN NOT NULL DEFAULT true,
ADD COLUMN "reportChangedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP;
ALTER TABLE "ChannelReportField"
ADD COLUMN "exportName" TEXT,
ADD COLUMN "columnWidth" INTEGER NOT NULL DEFAULT 18,
ADD COLUMN "imageWidth" INTEGER NOT NULL DEFAULT 120,
ADD COLUMN "imageHeight" INTEGER NOT NULL DEFAULT 80,
ADD COLUMN "defaultValue" TEXT,
ADD COLUMN "transform" TEXT;
ALTER TABLE "ReportExportFile"
ALTER COLUMN "taskId" DROP NOT NULL,
ADD COLUMN "batchId" TEXT,
ADD COLUMN "channelId" TEXT;
CREATE TABLE "ReportMaterialBatch" (
"id" TEXT NOT NULL,
"batchNo" TEXT NOT NULL,
"status" TEXT NOT NULL DEFAULT 'generating',
"createdById" TEXT,
"selectedCount" INTEGER NOT NULL DEFAULT 0,
"channelCount" INTEGER NOT NULL DEFAULT 0,
"fileCount" INTEGER NOT NULL DEFAULT 0,
"errorMessage" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"completedAt" TIMESTAMP(3),
CONSTRAINT "ReportMaterialBatch_pkey" PRIMARY KEY ("id")
);
CREATE TABLE "ReportMaterialBatchItem" (
"id" TEXT NOT NULL,
"batchId" TEXT NOT NULL,
"signatureId" TEXT NOT NULL,
"drainageItemId" TEXT,
"reportType" TEXT NOT NULL,
"materialVersion" INTEGER NOT NULL,
"snapshot" JSONB NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "ReportMaterialBatchItem_pkey" PRIMARY KEY ("id")
);
CREATE TABLE "ReportExportFileItem" (
"id" TEXT NOT NULL,
"exportFileId" TEXT NOT NULL,
"batchItemId" TEXT NOT NULL,
"taskId" TEXT NOT NULL,
"rowNumber" INTEGER NOT NULL,
CONSTRAINT "ReportExportFileItem_pkey" PRIMARY KEY ("id")
);
CREATE TABLE "ReportMaterialImportProfile" (
"id" TEXT NOT NULL,
"name" TEXT NOT NULL,
"reportType" TEXT NOT NULL,
"tenantId" TEXT,
"applicationId" TEXT,
"sheetName" TEXT,
"headerRowCount" INTEGER NOT NULL DEFAULT 1,
"dataStartRow" INTEGER NOT NULL DEFAULT 2,
"status" TEXT NOT NULL DEFAULT 'active',
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "ReportMaterialImportProfile_pkey" PRIMARY KEY ("id")
);
CREATE TABLE "ReportMaterialImportProfileColumn" (
"id" TEXT NOT NULL,
"profileId" TEXT NOT NULL,
"sourceHeader" TEXT NOT NULL,
"sourceHeaderPath" TEXT,
"sourceColumnIndex" INTEGER NOT NULL,
"targetFieldCode" TEXT NOT NULL,
"targetKind" TEXT NOT NULL DEFAULT 'dynamic',
"fieldType" TEXT NOT NULL DEFAULT 'string',
"required" BOOLEAN NOT NULL DEFAULT false,
"transform" TEXT,
"sortOrder" INTEGER NOT NULL DEFAULT 100,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "ReportMaterialImportProfileColumn_pkey" PRIMARY KEY ("id")
);
CREATE TABLE "ReportMaterialImportBatch" (
"id" TEXT NOT NULL,
"tenantId" TEXT NOT NULL,
"applicationId" TEXT,
"profileId" TEXT,
"fileObjectId" TEXT NOT NULL,
"fileName" TEXT NOT NULL,
"reportType" TEXT NOT NULL,
"status" TEXT NOT NULL DEFAULT 'analyzed',
"sheetName" TEXT NOT NULL,
"headerRowCount" INTEGER NOT NULL DEFAULT 1,
"dataStartRow" INTEGER NOT NULL DEFAULT 2,
"mapping" JSONB NOT NULL,
"preview" JSONB,
"result" JSONB,
"rowCount" INTEGER NOT NULL DEFAULT 0,
"successCount" INTEGER NOT NULL DEFAULT 0,
"failedCount" INTEGER NOT NULL DEFAULT 0,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"completedAt" TIMESTAMP(3),
CONSTRAINT "ReportMaterialImportBatch_pkey" PRIMARY KEY ("id")
);
CREATE UNIQUE INDEX "ReportMaterialBatch_batchNo_key" ON "ReportMaterialBatch"("batchNo");
CREATE INDEX "ReportMaterialBatch_status_createdAt_idx" ON "ReportMaterialBatch"("status", "createdAt");
CREATE INDEX "ReportMaterialBatchItem_batchId_reportType_idx" ON "ReportMaterialBatchItem"("batchId", "reportType");
CREATE INDEX "ReportMaterialBatchItem_signatureId_drainageItemId_idx" ON "ReportMaterialBatchItem"("signatureId", "drainageItemId");
CREATE UNIQUE INDEX "ReportExportFileItem_exportFileId_batchItemId_key" ON "ReportExportFileItem"("exportFileId", "batchItemId");
CREATE INDEX "ReportExportFileItem_taskId_idx" ON "ReportExportFileItem"("taskId");
CREATE INDEX "ReportExportFile_batchId_channelId_idx" ON "ReportExportFile"("batchId", "channelId");
CREATE INDEX "ReportMaterialImportProfile_reportType_status_idx" ON "ReportMaterialImportProfile"("reportType", "status");
CREATE INDEX "ReportMaterialImportProfile_tenantId_applicationId_idx" ON "ReportMaterialImportProfile"("tenantId", "applicationId");
CREATE UNIQUE INDEX "ReportMaterialImportProfileColumn_profileId_sourceColumnIndex_key" ON "ReportMaterialImportProfileColumn"("profileId", "sourceColumnIndex");
CREATE INDEX "ReportMaterialImportProfileColumn_profileId_sortOrder_idx" ON "ReportMaterialImportProfileColumn"("profileId", "sortOrder");
CREATE INDEX "ReportMaterialImportBatch_tenantId_createdAt_idx" ON "ReportMaterialImportBatch"("tenantId", "createdAt");
CREATE INDEX "ReportMaterialImportBatch_status_createdAt_idx" ON "ReportMaterialImportBatch"("status", "createdAt");
ALTER TABLE "ReportExportFile" ADD CONSTRAINT "ReportExportFile_batchId_fkey" FOREIGN KEY ("batchId") REFERENCES "ReportMaterialBatch"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "ReportMaterialBatchItem" ADD CONSTRAINT "ReportMaterialBatchItem_batchId_fkey" FOREIGN KEY ("batchId") REFERENCES "ReportMaterialBatch"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "ReportMaterialBatchItem" ADD CONSTRAINT "ReportMaterialBatchItem_signatureId_fkey" FOREIGN KEY ("signatureId") REFERENCES "SmsSignature"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
ALTER TABLE "ReportMaterialBatchItem" ADD CONSTRAINT "ReportMaterialBatchItem_drainageItemId_fkey" FOREIGN KEY ("drainageItemId") REFERENCES "SmsDrainageInfo"("id") ON DELETE SET NULL ON UPDATE CASCADE;
ALTER TABLE "ReportExportFileItem" ADD CONSTRAINT "ReportExportFileItem_exportFileId_fkey" FOREIGN KEY ("exportFileId") REFERENCES "ReportExportFile"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "ReportExportFileItem" ADD CONSTRAINT "ReportExportFileItem_batchItemId_fkey" FOREIGN KEY ("batchItemId") REFERENCES "ReportMaterialBatchItem"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "ReportExportFileItem" ADD CONSTRAINT "ReportExportFileItem_taskId_fkey" FOREIGN KEY ("taskId") REFERENCES "ChannelSignatureReportTask"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "ReportMaterialImportProfileColumn" ADD CONSTRAINT "ReportMaterialImportProfileColumn_profileId_fkey" FOREIGN KEY ("profileId") REFERENCES "ReportMaterialImportProfile"("id") ON DELETE CASCADE ON UPDATE CASCADE;
@@ -0,0 +1,18 @@
DROP INDEX IF EXISTS "ChannelReportField_channelId_code_key";
INSERT INTO "ChannelReportField" (
"id", "channelId", "drainageFieldId", "reportType", "code", "name", "exportName", "fieldType",
"required", "description", "sortOrder", "columnWidth", "imageWidth", "imageHeight", "defaultValue",
"transform", "status", "createdAt", "updatedAt"
)
SELECT
"id" || '_drainage', "channelId", "drainageFieldId", 'drainage', "code", "name", "exportName", "fieldType",
"required", "description", "sortOrder", "columnWidth", "imageWidth", "imageHeight", "defaultValue",
"transform", "status", "createdAt", "updatedAt"
FROM "ChannelReportField"
WHERE "reportType" = 'both';
UPDATE "ChannelReportField" SET "reportType" = 'signature' WHERE "reportType" = 'both';
CREATE UNIQUE INDEX "ChannelReportField_channelId_code_reportType_key"
ON "ChannelReportField"("channelId", "code", "reportType");
@@ -0,0 +1,4 @@
-- Existing rows predate the pending-material workflow and must not be treated as newly changed.
-- New rows continue to use the schema default (true), while later edits explicitly set true.
UPDATE "SmsSignature" SET "pendingReport" = false;
UPDATE "SmsDrainageInfo" SET "pendingReport" = false;
@@ -0,0 +1 @@
ALTER TABLE "SmsChannelGroupItem" DROP COLUMN "rateLimitPerSecond";
+140 -4
View File
@@ -417,6 +417,9 @@ model SmsSignature {
auditStatus String @default("draft")
reportStatus String @default("waiting_material")
rejectReason String?
materialVersion Int @default(1)
pendingReport Boolean @default(true)
reportChangedAt DateTime @default(now())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@ -429,6 +432,7 @@ model SmsSignature {
drainageItems SmsDrainageInfo[]
reportTasks ChannelSignatureReportTask[]
messageRecords SmsMessageRecord[]
reportBatchItems ReportMaterialBatchItem[]
@@index([tenantId, auditStatus])
@@index([tenantId, reportStatus])
@@ -445,6 +449,9 @@ model SmsDrainageInfo {
reportValues Json?
auditStatus String @default("pending")
rejectReason String?
materialVersion Int @default(1)
pendingReport Boolean @default(true)
reportChangedAt DateTime @default(now())
submittedAt DateTime @default(now())
reviewedAt DateTime?
createdAt DateTime @default(now())
@@ -455,6 +462,7 @@ model SmsDrainageInfo {
application SmsApplication? @relation(fields: [applicationId], references: [id])
reportTasks ChannelSignatureReportTask[]
messageRecords SmsMessageRecord[]
reportBatchItems ReportMaterialBatchItem[]
@@index([tenantId, auditStatus, updatedAt])
@@index([signatureId, auditStatus])
@@ -651,7 +659,6 @@ model SmsChannelGroupItem {
priority Int @default(100)
weight Int @default(1)
isBackup Boolean @default(false)
rateLimitPerSecond Int?
createdAt DateTime @default(now())
group SmsChannelGroup @relation(fields: [groupId], references: [id], onDelete: Cascade)
@@ -707,10 +714,16 @@ model ChannelReportField {
reportType String @default("both")
code String
name String
exportName String?
fieldType String
required Boolean @default(false)
description String?
sortOrder Int @default(100)
columnWidth Int @default(18)
imageWidth Int @default(120)
imageHeight Int @default(80)
defaultValue String?
transform String?
status String @default("active")
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@ -718,7 +731,7 @@ model ChannelReportField {
channel SmsChannel @relation(fields: [channelId], references: [id], onDelete: Cascade)
drainageField DrainageField? @relation(fields: [drainageFieldId], references: [id])
@@unique([channelId, code])
@@unique([channelId, code, reportType])
@@index([drainageFieldId, reportType])
}
@@ -791,6 +804,7 @@ model ChannelSignatureReportTask {
records ChannelSignatureReportRecord[]
exportFiles ReportExportFile[]
receiptImports ReportReceiptImport[]
exportItems ReportExportFileItem[]
@@index([tenantId, status])
@@index([signatureId, channelId])
@@ -819,14 +833,136 @@ model ChannelSignatureReportRecord {
model ReportExportFile {
id String @id @default(cuid())
taskId String
taskId String?
batchId String?
channelId String?
fileObjectId String?
fileName String
rowCount Int @default(0)
status String @default("generated")
createdAt DateTime @default(now())
task ChannelSignatureReportTask @relation(fields: [taskId], references: [id], onDelete: Cascade)
task ChannelSignatureReportTask? @relation(fields: [taskId], references: [id], onDelete: Cascade)
batch ReportMaterialBatch? @relation(fields: [batchId], references: [id], onDelete: Cascade)
items ReportExportFileItem[]
@@index([batchId, channelId])
}
model ReportMaterialBatch {
id String @id @default(cuid())
batchNo String @unique
status String @default("generating")
createdById String?
selectedCount Int @default(0)
channelCount Int @default(0)
fileCount Int @default(0)
errorMessage String?
createdAt DateTime @default(now())
completedAt DateTime?
items ReportMaterialBatchItem[]
exportFiles ReportExportFile[]
@@index([status, createdAt])
}
model ReportMaterialBatchItem {
id String @id @default(cuid())
batchId String
signatureId String
drainageItemId String?
reportType String
materialVersion Int
snapshot Json
createdAt DateTime @default(now())
batch ReportMaterialBatch @relation(fields: [batchId], references: [id], onDelete: Cascade)
signature SmsSignature @relation(fields: [signatureId], references: [id])
drainageInfo SmsDrainageInfo? @relation(fields: [drainageItemId], references: [id])
exportItems ReportExportFileItem[]
@@index([batchId, reportType])
@@index([signatureId, drainageItemId])
}
model ReportExportFileItem {
id String @id @default(cuid())
exportFileId String
batchItemId String
taskId String
rowNumber Int
exportFile ReportExportFile @relation(fields: [exportFileId], references: [id], onDelete: Cascade)
batchItem ReportMaterialBatchItem @relation(fields: [batchItemId], references: [id], onDelete: Cascade)
task ChannelSignatureReportTask @relation(fields: [taskId], references: [id], onDelete: Cascade)
@@unique([exportFileId, batchItemId])
@@index([taskId])
}
model ReportMaterialImportProfile {
id String @id @default(cuid())
name String
reportType String
tenantId String?
applicationId String?
sheetName String?
headerRowCount Int @default(1)
dataStartRow Int @default(2)
status String @default("active")
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
columns ReportMaterialImportProfileColumn[]
@@index([reportType, status])
@@index([tenantId, applicationId])
}
model ReportMaterialImportProfileColumn {
id String @id @default(cuid())
profileId String
sourceHeader String
sourceHeaderPath String?
sourceColumnIndex Int
targetFieldCode String
targetKind String @default("dynamic")
fieldType String @default("string")
required Boolean @default(false)
transform String?
sortOrder Int @default(100)
createdAt DateTime @default(now())
profile ReportMaterialImportProfile @relation(fields: [profileId], references: [id], onDelete: Cascade)
@@unique([profileId, sourceColumnIndex])
@@index([profileId, sortOrder])
}
model ReportMaterialImportBatch {
id String @id @default(cuid())
tenantId String
applicationId String?
profileId String?
fileObjectId String
fileName String
reportType String
status String @default("analyzed")
sheetName String
headerRowCount Int @default(1)
dataStartRow Int @default(2)
mapping Json
preview Json?
result Json?
rowCount Int @default(0)
successCount Int @default(0)
failedCount Int @default(0)
createdAt DateTime @default(now())
completedAt DateTime?
@@index([tenantId, createdAt])
@@index([status, createdAt])
}
model ReportReceiptImport {