@chenchaolong/plugin-trade-compliance-workbench 1.0.102 → 1.0.104
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/import-jobs/import-command.builder.js +1 -1
- package/dist/lib/import-jobs/import-command.builder.js.map +1 -1
- package/dist/lib/remote-components/trade-compliance-workbench/app.js +15 -15
- package/dist/lib/remote-ui/app-source.js +92 -18
- package/dist/lib/remote-ui/app-source.js.map +1 -1
- package/dist/lib/remote-ui/pages/catalogs.d.ts.map +1 -1
- package/dist/lib/remote-ui/pages/catalogs.js +3 -1
- package/dist/lib/remote-ui/pages/catalogs.js.map +1 -1
- package/dist/lib/remote-ui/polling.d.ts +5 -0
- package/dist/lib/remote-ui/polling.d.ts.map +1 -1
- package/dist/lib/remote-ui/polling.js +12 -0
- package/dist/lib/remote-ui/polling.js.map +1 -1
- package/dist/lib/workbench.service.d.ts +2 -0
- package/dist/lib/workbench.service.d.ts.map +1 -1
- package/dist/lib/workbench.service.js +85 -4
- package/dist/lib/workbench.service.js.map +1 -1
- package/package.json +1 -1
|
@@ -194,6 +194,49 @@ function App() {
|
|
|
194
194
|
};
|
|
195
195
|
});
|
|
196
196
|
}, []);
|
|
197
|
+
const clearMissingImportTask = React.useCallback((taskId) => {
|
|
198
|
+
deletedImportTaskIdsRef.current.add(taskId);
|
|
199
|
+
catalogTaskTrackerRef.current.abort(taskId);
|
|
200
|
+
contractTaskTrackerRef.current.abort(taskId);
|
|
201
|
+
removeLocalImportTask(taskId);
|
|
202
|
+
setImportCompletions(current => {
|
|
203
|
+
let changed = false;
|
|
204
|
+
const next = { ...current };
|
|
205
|
+
for (const key of Object.keys(next)) {
|
|
206
|
+
if (next[key]?.task.id !== taskId)
|
|
207
|
+
continue;
|
|
208
|
+
next[key] = null;
|
|
209
|
+
changed = true;
|
|
210
|
+
}
|
|
211
|
+
return changed ? next : current;
|
|
212
|
+
});
|
|
213
|
+
setImportFailures(current => {
|
|
214
|
+
let changed = false;
|
|
215
|
+
const next = { ...current };
|
|
216
|
+
for (const key of Object.keys(next)) {
|
|
217
|
+
if (current[key]?.id !== taskId)
|
|
218
|
+
continue;
|
|
219
|
+
next[key] = null;
|
|
220
|
+
changed = true;
|
|
221
|
+
}
|
|
222
|
+
return changed ? next : current;
|
|
223
|
+
});
|
|
224
|
+
if (purchaseImportTask?.id === taskId) {
|
|
225
|
+
setPurchaseImportTask(null);
|
|
226
|
+
setPurchaseReviewDraft(null);
|
|
227
|
+
}
|
|
228
|
+
if (customerImportState.status !== 'IDLE' && customerImportState.importTaskId === taskId) {
|
|
229
|
+
setCustomerImportState({ status: 'IDLE' });
|
|
230
|
+
}
|
|
231
|
+
if (taskChatSynchronizerRef.current?.isSelected(taskId)) {
|
|
232
|
+
taskChatSynchronizerRef.current.clear();
|
|
233
|
+
}
|
|
234
|
+
}, [customerImportState, purchaseImportTask?.id, removeLocalImportTask]);
|
|
235
|
+
function handleMissingTrackedImportTask(taskId) {
|
|
236
|
+
clearMissingImportTask(taskId);
|
|
237
|
+
setToast({ message: '导入任务已消失,已停止跟踪', tone: 'info' });
|
|
238
|
+
return loadTaskState().catch(() => undefined);
|
|
239
|
+
}
|
|
197
240
|
const selectImportTaskForChat = React.useCallback(async (task) => {
|
|
198
241
|
const synchronizer = taskChatSynchronizerRef.current;
|
|
199
242
|
const chatSelectionIntent = synchronizer.beginSelection(task.id);
|
|
@@ -465,7 +508,12 @@ function App() {
|
|
|
465
508
|
try {
|
|
466
509
|
const completion = await runCatalogImportOrchestration({
|
|
467
510
|
start,
|
|
468
|
-
pollTask: async (taskId) =>
|
|
511
|
+
pollTask: async (taskId) => {
|
|
512
|
+
const response = asRecord(unwrapResponse(await bridge.requestData({ parameters: { collection: 'importTask', taskId } })));
|
|
513
|
+
if (!response.task || typeof response.task !== 'object')
|
|
514
|
+
throw new Error(`轮询响应缺少导入任务:${taskId}`);
|
|
515
|
+
return asImportCompletion(response);
|
|
516
|
+
},
|
|
469
517
|
refresh: loadTaskState,
|
|
470
518
|
signal,
|
|
471
519
|
onTransition: (previous, next) => {
|
|
@@ -484,6 +532,10 @@ function App() {
|
|
|
484
532
|
catch (error) {
|
|
485
533
|
if (isAbortError(error))
|
|
486
534
|
throw error;
|
|
535
|
+
if (error instanceof Error && error.message.startsWith('轮询响应缺少导入任务:')) {
|
|
536
|
+
await handleMissingTrackedImportTask(error.message.slice('轮询响应缺少导入任务:'.length));
|
|
537
|
+
return { task: start.data.task, batch: undefined };
|
|
538
|
+
}
|
|
487
539
|
if (isPollingObservationTimeout(error)) {
|
|
488
540
|
setToast({ message: '任务仍在后台处理中,将继续跟踪', tone: 'info' });
|
|
489
541
|
scheduleTaskStateRecovery();
|
|
@@ -497,7 +549,7 @@ function App() {
|
|
|
497
549
|
setToast({ message: error instanceof Error ? error.message : '目录解析失败', tone: 'error' });
|
|
498
550
|
throw error;
|
|
499
551
|
}
|
|
500
|
-
}, [loadTaskState, scheduleTaskStateRecovery, upsertLocalImportTask]);
|
|
552
|
+
}, [handleMissingTrackedImportTask, loadTaskState, scheduleTaskStateRecovery, upsertLocalImportTask]);
|
|
501
553
|
const orchestrateContractImport = React.useCallback(async (start, signal) => {
|
|
502
554
|
const task = start.data?.task;
|
|
503
555
|
if (!task)
|
|
@@ -515,7 +567,12 @@ function App() {
|
|
|
515
567
|
const completion = await runContractImportOrchestration({
|
|
516
568
|
start,
|
|
517
569
|
signal,
|
|
518
|
-
pollTask: async (taskId) =>
|
|
570
|
+
pollTask: async (taskId) => {
|
|
571
|
+
const response = asRecord(unwrapResponse(await bridge.requestData({ parameters: { collection: 'importTask', taskId } })));
|
|
572
|
+
if (!response.task || typeof response.task !== 'object')
|
|
573
|
+
throw new Error(`轮询响应缺少导入任务:${taskId}`);
|
|
574
|
+
return asContractCompletion(response);
|
|
575
|
+
},
|
|
519
576
|
onTransition: (previous, next) => {
|
|
520
577
|
upsertLocalImportTask(next);
|
|
521
578
|
const notice = taskTransition(previous, next);
|
|
@@ -534,6 +591,10 @@ function App() {
|
|
|
534
591
|
catch (error) {
|
|
535
592
|
if (isAbortError(error))
|
|
536
593
|
throw error;
|
|
594
|
+
if (error instanceof Error && error.message.startsWith('轮询响应缺少导入任务:')) {
|
|
595
|
+
await handleMissingTrackedImportTask(error.message.slice('轮询响应缺少导入任务:'.length));
|
|
596
|
+
return { task };
|
|
597
|
+
}
|
|
537
598
|
if (isPollingObservationTimeout(error)) {
|
|
538
599
|
setToast({ message: '任务仍在后台处理中,将继续跟踪', tone: 'info' });
|
|
539
600
|
scheduleTaskStateRecovery();
|
|
@@ -551,7 +612,7 @@ function App() {
|
|
|
551
612
|
setToast({ message: error instanceof Error ? error.message : '合同解析失败', tone: 'error' });
|
|
552
613
|
throw error;
|
|
553
614
|
}
|
|
554
|
-
}, [enqueuePendingReview, scheduleTaskStateRecovery, upsertLocalImportTask]);
|
|
615
|
+
}, [enqueuePendingReview, handleMissingTrackedImportTask, scheduleTaskStateRecovery, upsertLocalImportTask]);
|
|
555
616
|
const startTrackingUploadedImportTask = React.useCallback((task) => {
|
|
556
617
|
if (task.documentType === 'PURCHASE_CONTRACT' || task.documentType === 'CUSTOMER_CONTRACT') {
|
|
557
618
|
const running = contractTaskTrackerRef.current.run(task.id, signal => orchestrateContractImport({ success: true, data: { task: task } }, signal));
|
|
@@ -719,22 +780,32 @@ function App() {
|
|
|
719
780
|
contractTaskTrackerRef.current.abort(task.id);
|
|
720
781
|
removeLocalImportTask(task.id);
|
|
721
782
|
setImportCompletions(current => {
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
783
|
+
let changed = false;
|
|
784
|
+
const next = { ...current };
|
|
785
|
+
for (const key of Object.keys(next)) {
|
|
786
|
+
if (next[key]?.task.id !== task.id)
|
|
787
|
+
continue;
|
|
788
|
+
next[key] = null;
|
|
789
|
+
changed = true;
|
|
790
|
+
}
|
|
791
|
+
return changed ? next : current;
|
|
726
792
|
});
|
|
727
793
|
setImportFailures(current => {
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
794
|
+
let changed = false;
|
|
795
|
+
const next = { ...current };
|
|
796
|
+
for (const key of Object.keys(next)) {
|
|
797
|
+
if (current[key]?.id !== task.id)
|
|
798
|
+
continue;
|
|
799
|
+
next[key] = null;
|
|
800
|
+
changed = true;
|
|
801
|
+
}
|
|
802
|
+
return changed ? next : current;
|
|
732
803
|
});
|
|
733
|
-
if (
|
|
804
|
+
if (purchaseImportTask?.id === task.id) {
|
|
734
805
|
setPurchaseImportTask(null);
|
|
735
806
|
setPurchaseReviewDraft(null);
|
|
736
807
|
}
|
|
737
|
-
if (
|
|
808
|
+
if (customerImportState.status !== 'IDLE' && customerImportState.importTaskId === task.id) {
|
|
738
809
|
setCustomerImportState({ status: 'IDLE' });
|
|
739
810
|
}
|
|
740
811
|
if (taskChatSynchronizerRef.current?.isSelected(task.id)) {
|
|
@@ -750,7 +821,7 @@ function App() {
|
|
|
750
821
|
if (options.refresh)
|
|
751
822
|
await loadTaskState();
|
|
752
823
|
});
|
|
753
|
-
}, [customerImportState, loadTaskState, purchaseImportTask, removeLocalImportTask, runMutation]);
|
|
824
|
+
}, [customerImportState, loadTaskState, purchaseImportTask?.id, removeLocalImportTask, runMutation]);
|
|
754
825
|
const deleteImportTask = React.useCallback(async (task) => {
|
|
755
826
|
await deleteImportTaskRecord(task, { refresh: true });
|
|
756
827
|
}, [deleteImportTaskRecord]);
|
|
@@ -1425,7 +1496,7 @@ function sameWorkbenchRoute(left, right) {
|
|
|
1425
1496
|
}
|
|
1426
1497
|
function buildTaskStatusItems(tasks, onOpen, onSelect, onDelete) {
|
|
1427
1498
|
return [...tasks]
|
|
1428
|
-
.filter(task => ['QUEUED', 'EXTRACTING', 'AI_PARSING', 'VALIDATING', 'PERSISTING', 'SUCCEEDED', 'FAILED', 'TIMED_OUT'].includes(task.status))
|
|
1499
|
+
.filter(task => ['QUEUED', 'EXTRACTING', 'AI_PARSING', 'VALIDATING', 'PERSISTING', 'SUCCEEDED', 'FAILED', 'TIMED_OUT'].includes(task.status) || isCatalogAwaitingReviewTask(task))
|
|
1429
1500
|
.sort((left, right) => String(right.updatedAt ?? right.queuedAt ?? right.createdAt ?? '').localeCompare(String(left.updatedAt ?? left.queuedAt ?? left.createdAt ?? '')))
|
|
1430
1501
|
.map(task => {
|
|
1431
1502
|
const tone = taskTone(task.status);
|
|
@@ -1450,16 +1521,19 @@ function taskTone(status) {
|
|
|
1450
1521
|
return 'success';
|
|
1451
1522
|
if (status === 'FAILED' || status === 'TIMED_OUT')
|
|
1452
1523
|
return 'failed';
|
|
1453
|
-
if (status === 'QUEUED' || status === 'EXTRACTING' || status === 'AI_PARSING' || status === 'VALIDATING' || status === 'PERSISTING')
|
|
1524
|
+
if (status === 'QUEUED' || status === 'EXTRACTING' || status === 'AI_PARSING' || status === 'VALIDATING' || status === 'PERSISTING' || status === 'AWAITING_REVIEW')
|
|
1454
1525
|
return 'running';
|
|
1455
1526
|
return 'idle';
|
|
1456
1527
|
}
|
|
1457
1528
|
function taskStatusLabel(status) {
|
|
1458
1529
|
const labels = {
|
|
1459
|
-
QUEUED: '排队中', EXTRACTING: '提取中', AI_PARSING: 'AI 解析中', VALIDATING: '校验中', PERSISTING: '入库中', SUCCEEDED: '成功', FAILED: '失败', TIMED_OUT: '超时', CANCELED: '已取消'
|
|
1530
|
+
QUEUED: '排队中', EXTRACTING: '提取中', AI_PARSING: 'AI 解析中', VALIDATING: '校验中', PERSISTING: '入库中', AWAITING_REVIEW: '自动复核中', SUCCEEDED: '成功', FAILED: '失败', TIMED_OUT: '超时', CANCELED: '已取消'
|
|
1460
1531
|
};
|
|
1461
1532
|
return labels[status] ?? status;
|
|
1462
1533
|
}
|
|
1534
|
+
function isCatalogAwaitingReviewTask(task) {
|
|
1535
|
+
return task.status === 'AWAITING_REVIEW' && (task.documentType === 'CONTROLLED_CATALOG' || task.documentType === 'SANCTION_CATALOG');
|
|
1536
|
+
}
|
|
1463
1537
|
function catalogTaskTypeLabel(task) {
|
|
1464
1538
|
const base = documentTypeLabel(task.documentType);
|
|
1465
1539
|
if (task.documentType !== 'CONTROLLED_CATALOG')
|