@chenchaolong/plugin-trade-compliance-workbench 1.0.23 → 1.0.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/entities/shared.entity.d.ts +6 -1
- package/dist/lib/entities/shared.entity.d.ts.map +1 -1
- package/dist/lib/entities/shared.entity.js +20 -0
- package/dist/lib/entities/shared.entity.js.map +1 -1
- package/dist/lib/import-jobs/import-job-callback.processor.d.ts.map +1 -1
- package/dist/lib/import-jobs/import-job-callback.processor.js +4 -1
- package/dist/lib/import-jobs/import-job-callback.processor.js.map +1 -1
- package/dist/lib/import-jobs/import-job.coordinator.d.ts +2 -1
- package/dist/lib/import-jobs/import-job.coordinator.d.ts.map +1 -1
- package/dist/lib/import-jobs/import-job.coordinator.js +12 -0
- package/dist/lib/import-jobs/import-job.coordinator.js.map +1 -1
- package/dist/lib/import-jobs/import-job.processor.d.ts.map +1 -1
- package/dist/lib/import-jobs/import-job.processor.js +3 -0
- package/dist/lib/import-jobs/import-job.processor.js.map +1 -1
- package/dist/lib/import-jobs/import-timeout.processor.d.ts.map +1 -1
- package/dist/lib/import-jobs/import-timeout.processor.js +3 -1
- package/dist/lib/import-jobs/import-timeout.processor.js.map +1 -1
- package/dist/lib/public-dto.d.ts +1 -1
- package/dist/lib/public-dto.d.ts.map +1 -1
- package/dist/lib/public-dto.js +2 -0
- package/dist/lib/public-dto.js.map +1 -1
- package/dist/lib/remote-components/trade-compliance-workbench/app.css +1 -1
- package/dist/lib/remote-components/trade-compliance-workbench/app.js +5 -5
- package/dist/lib/remote-ui/action-execution.d.ts.map +1 -1
- package/dist/lib/remote-ui/action-execution.js +1 -0
- package/dist/lib/remote-ui/action-execution.js.map +1 -1
- package/dist/lib/remote-ui/app-source.js +67 -16
- package/dist/lib/remote-ui/app-source.js.map +1 -1
- package/dist/lib/remote-ui/components/task-status.d.ts +3 -0
- package/dist/lib/remote-ui/components/task-status.d.ts.map +1 -1
- package/dist/lib/remote-ui/components/task-status.js +59 -4
- package/dist/lib/remote-ui/components/task-status.js.map +1 -1
- package/dist/lib/remote-ui/contract-import.d.ts +3 -1
- package/dist/lib/remote-ui/contract-import.d.ts.map +1 -1
- package/dist/lib/remote-ui/contract-import.js +2 -0
- package/dist/lib/remote-ui/contract-import.js.map +1 -1
- package/dist/lib/remote-ui/contracts.d.ts +3 -1
- package/dist/lib/remote-ui/contracts.d.ts.map +1 -1
- package/dist/lib/remote-ui/import-task-status.d.ts +35 -0
- package/dist/lib/remote-ui/import-task-status.d.ts.map +1 -1
- package/dist/lib/remote-ui/import-task-status.js +105 -0
- package/dist/lib/remote-ui/import-task-status.js.map +1 -1
- package/dist/lib/remote-ui/styles.css +16 -5
- package/dist/lib/types.d.ts +1 -1
- package/dist/lib/types.d.ts.map +1 -1
- package/dist/lib/types.js +1 -1
- package/dist/lib/types.js.map +1 -1
- package/dist/lib/workbench-action-contract.d.ts +1 -1
- package/dist/lib/workbench-action-contract.d.ts.map +1 -1
- package/dist/lib/workbench-action-contract.js +1 -0
- package/dist/lib/workbench-action-contract.js.map +1 -1
- package/dist/lib/workbench-view.provider.d.ts.map +1 -1
- package/dist/lib/workbench-view.provider.js +19 -2
- package/dist/lib/workbench-view.provider.js.map +1 -1
- package/dist/lib/workbench.service.d.ts +19 -1
- package/dist/lib/workbench.service.d.ts.map +1 -1
- package/dist/lib/workbench.service.js +105 -1
- package/dist/lib/workbench.service.js.map +1 -1
- package/package.json +1 -1
|
@@ -44,7 +44,11 @@ const SKIPPED_REASON_MESSAGES = {
|
|
|
44
44
|
const RUNNING_IMPORT_TASK_STATUSES = ['EXTRACTING', 'AI_PARSING', 'VALIDATING', 'PERSISTING'];
|
|
45
45
|
const RECOVERABLE_IMPORT_TASK_STATUSES = ['QUEUED', 'EXTRACTING', 'AI_PARSING', 'VALIDATING', 'PERSISTING'];
|
|
46
46
|
const RECOVERABLE_REANALYSIS_STATUSES = ['PENDING', 'RUNNING'];
|
|
47
|
-
const TERMINAL_IMPORT_TASK_STATUSES = ['AWAITING_REVIEW', 'SUCCEEDED', 'FAILED', 'TIMED_OUT'];
|
|
47
|
+
const TERMINAL_IMPORT_TASK_STATUSES = ['AWAITING_REVIEW', 'SUCCEEDED', 'FAILED', 'TIMED_OUT', 'CANCELED'];
|
|
48
|
+
const DELETABLE_IMPORT_TASK_STATUSES = [
|
|
49
|
+
'QUEUED', 'EXTRACTING', 'AI_PARSING', 'VALIDATING',
|
|
50
|
+
'AWAITING_REVIEW', 'PERSISTING', 'FAILED', 'TIMED_OUT'
|
|
51
|
+
];
|
|
48
52
|
const CATALOG_RISK_RECOMMENDATIONS = {
|
|
49
53
|
DUAL_USE: '建议核验军民两用物项许可要求,并在出口前完成合规审批',
|
|
50
54
|
OVERSEAS_SANCTION: '建议核验海外制裁限制及交易对手风险,并在交易前完成合规审批'
|
|
@@ -141,6 +145,89 @@ let TradeComplianceWorkbenchService = class TradeComplianceWorkbenchService {
|
|
|
141
145
|
const page = await this.pagedQuery(ImportTask, scope, query, ['sourceFileName', 'documentType', 'status', 'errorMessage'], { documentType: query.parameters?.documentType, status: query.parameters?.status });
|
|
142
146
|
return { ...page, items: page.items.map(publicImportTaskDto) };
|
|
143
147
|
}
|
|
148
|
+
async deleteImportTask(scope, taskId) {
|
|
149
|
+
const transactionResult = await this.dataSource.transaction(async (manager) => {
|
|
150
|
+
const taskRepo = manager.getRepository(ImportTask);
|
|
151
|
+
const task = await required(taskRepo.findOne({
|
|
152
|
+
where: { ...activeScope(scope), id: requiredId(taskId, '解析任务标识') },
|
|
153
|
+
lock: { mode: 'pessimistic_write' }
|
|
154
|
+
}), '解析任务不存在');
|
|
155
|
+
if (task.deletedAt || task.status === 'CANCELED') {
|
|
156
|
+
return {
|
|
157
|
+
taskId: task.id, status: 'CANCELED', deleted: true,
|
|
158
|
+
idempotent: true, sourceFileCleanupRequired: false,
|
|
159
|
+
cancellationTarget: null
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
if (!DELETABLE_IMPORT_TASK_STATUSES.includes(task.status)) {
|
|
163
|
+
throw new DomainError(DomainErrorCodes.INVALID_INPUT, '已完成任务不允许删除');
|
|
164
|
+
}
|
|
165
|
+
const cancellationTarget = {
|
|
166
|
+
importTaskId: task.id,
|
|
167
|
+
claimAttemptToken: task.claimAttemptToken
|
|
168
|
+
};
|
|
169
|
+
const now = new Date();
|
|
170
|
+
task.status = 'CANCELED';
|
|
171
|
+
task.canceledAt = now;
|
|
172
|
+
task.canceledById = scope.userId ?? null;
|
|
173
|
+
task.deletedAt = now;
|
|
174
|
+
task.deletedById = scope.userId ?? null;
|
|
175
|
+
task.deleteOperation = 'USER_DELETE';
|
|
176
|
+
task.claimAttemptToken = null;
|
|
177
|
+
task.resultProcessingClaimId = null;
|
|
178
|
+
task.resultProcessingClaimExpiresAt = null;
|
|
179
|
+
task.completedAt = now;
|
|
180
|
+
await this.deleteImportTaskTransientRows(manager, scope, task);
|
|
181
|
+
await taskRepo.save(task);
|
|
182
|
+
const sourceFileCleanupRequired = !await this.importSourceFileStillReferenced(manager, scope, task);
|
|
183
|
+
return {
|
|
184
|
+
taskId: task.id, status: 'CANCELED', deleted: true,
|
|
185
|
+
idempotent: false, sourceFileCleanupRequired, cancellationTarget,
|
|
186
|
+
sourceFileId: task.sourceFileId
|
|
187
|
+
};
|
|
188
|
+
});
|
|
189
|
+
let sourceFileRemoved = false;
|
|
190
|
+
const sourceFileId = 'sourceFileId' in transactionResult ? transactionResult.sourceFileId : undefined;
|
|
191
|
+
if (transactionResult.sourceFileCleanupRequired && typeof sourceFileId === 'string' && this.storage) {
|
|
192
|
+
try {
|
|
193
|
+
await this.storage.remove(sourceFileId);
|
|
194
|
+
sourceFileRemoved = true;
|
|
195
|
+
}
|
|
196
|
+
catch {
|
|
197
|
+
sourceFileRemoved = false;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
return { ...transactionResult, sourceFileRemoved };
|
|
201
|
+
}
|
|
202
|
+
async deleteImportTaskTransientRows(manager, scope, task) {
|
|
203
|
+
await manager.getRepository(CatalogReviewBlock).delete({ ...activeScope(scope), importTaskId: task.id });
|
|
204
|
+
await this.deleteEmptyImportCatalogBatch(manager, scope, task.id, ControlledCatalogBatch, ControlledGoodsRecord, ControlledCatalogImportIssue);
|
|
205
|
+
await this.deleteEmptyImportCatalogBatch(manager, scope, task.id, SanctionCatalogBatch, SanctionedCompanyRecord, SanctionCatalogImportIssue);
|
|
206
|
+
}
|
|
207
|
+
async deleteEmptyImportCatalogBatch(manager, scope, importTaskId, batchEntity, recordEntity, issueEntity) {
|
|
208
|
+
const batchRepo = manager.getRepository(batchEntity);
|
|
209
|
+
const batch = await batchRepo.findOne({ where: { ...activeScope(scope), importTaskId } });
|
|
210
|
+
if (!batch)
|
|
211
|
+
return;
|
|
212
|
+
const committedRows = await manager.getRepository(recordEntity).count({
|
|
213
|
+
where: { ...activeScope(scope), batchId: batch.id }
|
|
214
|
+
});
|
|
215
|
+
if (committedRows > 0)
|
|
216
|
+
return;
|
|
217
|
+
await manager.getRepository(issueEntity).delete({ ...activeScope(scope), batchId: batch.id });
|
|
218
|
+
await batchRepo.delete({ ...activeScope(scope), id: batch.id });
|
|
219
|
+
}
|
|
220
|
+
async importSourceFileStillReferenced(manager, scope, task) {
|
|
221
|
+
const where = { ...activeScope(scope), sourceFileId: task.sourceFileId };
|
|
222
|
+
const [otherTasks, controlledBatches, sanctionBatches, contracts, orders] = await Promise.all([
|
|
223
|
+
manager.getRepository(ImportTask).count({ where: { ...where, id: Not(task.id) } }),
|
|
224
|
+
manager.getRepository(ControlledCatalogBatch).count({ where }),
|
|
225
|
+
manager.getRepository(SanctionCatalogBatch).count({ where }),
|
|
226
|
+
manager.getRepository(CustomerContract).count({ where }),
|
|
227
|
+
manager.getRepository(PurchaseOrder).count({ where })
|
|
228
|
+
]);
|
|
229
|
+
return otherTasks + controlledBatches + sanctionBatches + contracts + orders > 0;
|
|
230
|
+
}
|
|
144
231
|
async listActiveCustomerContractReanalyses(scope) {
|
|
145
232
|
const [contracts, tasks] = await Promise.all([
|
|
146
233
|
this.dataSource.getRepository(CustomerContract).find({ where: { ...activeScope(scope), currentReanalysisRunId: Not(IsNull()), deletedAt: IsNull() } }),
|
|
@@ -1045,6 +1132,8 @@ let TradeComplianceWorkbenchService = class TradeComplianceWorkbenchService {
|
|
|
1045
1132
|
const task = await required(repo.findOne({
|
|
1046
1133
|
where: { ...activeScope(scope), id: taskId }, lock: { mode: 'pessimistic_write' }
|
|
1047
1134
|
}), '解析任务不存在');
|
|
1135
|
+
if (isCanceledImportTask(task))
|
|
1136
|
+
return task;
|
|
1048
1137
|
if (task.observerConversationId && task.observerConversationId !== normalizedConversationId) {
|
|
1049
1138
|
throw new DomainError(DomainErrorCodes.INVALID_INPUT, '观察会话标识不一致');
|
|
1050
1139
|
}
|
|
@@ -1489,6 +1578,12 @@ let TradeComplianceWorkbenchService = class TradeComplianceWorkbenchService {
|
|
|
1489
1578
|
return repo.save(task);
|
|
1490
1579
|
});
|
|
1491
1580
|
}
|
|
1581
|
+
async isImportTaskCanceled(scope, id) {
|
|
1582
|
+
const task = await this.dataSource.getRepository(ImportTask).findOne({
|
|
1583
|
+
where: { ...activeScope(scope), id }
|
|
1584
|
+
});
|
|
1585
|
+
return !task || isCanceledImportTask(task);
|
|
1586
|
+
}
|
|
1492
1587
|
async markImportCommandDispatched(scope, id) {
|
|
1493
1588
|
return this.dataSource.transaction(async (manager) => {
|
|
1494
1589
|
const repo = manager.getRepository(ImportTask);
|
|
@@ -1530,6 +1625,12 @@ let TradeComplianceWorkbenchService = class TradeComplianceWorkbenchService {
|
|
|
1530
1625
|
});
|
|
1531
1626
|
if (!task)
|
|
1532
1627
|
return { accepted: false, reason: 'scope_mismatch', sequence: 0 };
|
|
1628
|
+
if (isCanceledImportTask(task)) {
|
|
1629
|
+
return {
|
|
1630
|
+
accepted: false, reason: 'task_canceled',
|
|
1631
|
+
sequence: task.handoffCallbackSequence ?? 0
|
|
1632
|
+
};
|
|
1633
|
+
}
|
|
1533
1634
|
if (!handoffCallbackIdentityMatches(input, `trade-compliance-import-task-${task.id}`, task.targetAssistantId))
|
|
1534
1635
|
return { accepted: false, reason: 'identity_mismatch', sequence: task.handoffCallbackSequence ?? 0 };
|
|
1535
1636
|
const currentSequence = task.handoffCallbackSequence ?? 0;
|
|
@@ -3351,6 +3452,9 @@ function assertImportTaskResultWritable(task) {
|
|
|
3351
3452
|
throw new DomainError(DomainErrorCodes.INVALID_INPUT, '解析任务状态不允许保存结果');
|
|
3352
3453
|
}
|
|
3353
3454
|
}
|
|
3455
|
+
function isCanceledImportTask(task) {
|
|
3456
|
+
return task.status === 'CANCELED' || Boolean(task.deletedAt);
|
|
3457
|
+
}
|
|
3354
3458
|
function throwImportTaskTerminalState(task) {
|
|
3355
3459
|
const message = task.status === 'FAILED' || task.status === 'TIMED_OUT'
|
|
3356
3460
|
? task.errorMessage || '解析任务未成功完成'
|