@chenchaolong/plugin-trade-compliance-workbench 1.0.103 → 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.
@@ -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) => asImportCompletion(unwrapResponse(await bridge.requestData({ parameters: { collection: 'importTask', 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) => asContractCompletion(unwrapResponse(await bridge.requestData({ parameters: { collection: 'importTask', 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
- const entry = current[task.documentType];
723
- if (!entry || entry.task.id !== task.id)
724
- return current;
725
- return { ...current, [task.documentType]: null };
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
- const entry = current[task.documentType];
729
- if (!entry || entry.id !== task.id)
730
- return current;
731
- return { ...current, [task.documentType]: null };
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 (task.documentType === 'PURCHASE_CONTRACT' && purchaseImportTask?.id === task.id) {
804
+ if (purchaseImportTask?.id === task.id) {
734
805
  setPurchaseImportTask(null);
735
806
  setPurchaseReviewDraft(null);
736
807
  }
737
- if (task.documentType === 'CUSTOMER_CONTRACT' && customerImportState.status !== 'IDLE' && customerImportState.importTaskId === task.id) {
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]);