@chenchaolong/plugin-trade-compliance-workbench 1.0.118 → 1.0.120
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/adapters/pdf-page-renderer.d.ts.map +1 -1
- package/dist/lib/adapters/pdf-page-renderer.js +33 -90
- package/dist/lib/adapters/pdf-page-renderer.js.map +1 -1
- package/dist/lib/constants.d.ts +2 -0
- package/dist/lib/constants.d.ts.map +1 -1
- package/dist/lib/constants.js +2 -0
- package/dist/lib/constants.js.map +1 -1
- 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.css +1 -1
- package/dist/lib/remote-components/trade-compliance-workbench/app.js +8 -8
- package/dist/lib/remote-components/trade-compliance-workbench/preview-source.mjs +25 -0
- package/dist/lib/remote-components/trade-compliance-workbench/preview.js +341 -0
- package/dist/lib/remote-ui/app-source.d.ts.map +1 -1
- package/dist/lib/remote-ui/app-source.js +38 -31
- package/dist/lib/remote-ui/app-source.js.map +1 -1
- package/dist/lib/remote-ui/components/source-file-preview.d.ts +37 -0
- package/dist/lib/remote-ui/components/source-file-preview.d.ts.map +1 -0
- package/dist/lib/remote-ui/components/source-file-preview.js +115 -0
- package/dist/lib/remote-ui/components/source-file-preview.js.map +1 -0
- package/dist/lib/remote-ui/contract-import.d.ts +1 -0
- 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 +1 -0
- package/dist/lib/remote-ui/contracts.d.ts.map +1 -1
- package/dist/lib/remote-ui/customer-contract-contracts.d.ts +7 -0
- package/dist/lib/remote-ui/customer-contract-contracts.d.ts.map +1 -1
- package/dist/lib/remote-ui/pages/catalogs.d.ts +1 -1
- package/dist/lib/remote-ui/pages/catalogs.d.ts.map +1 -1
- package/dist/lib/remote-ui/pages/catalogs.js +2 -0
- package/dist/lib/remote-ui/pages/catalogs.js.map +1 -1
- package/dist/lib/remote-ui/pages/contracts.d.ts.map +1 -1
- package/dist/lib/remote-ui/pages/contracts.js +2 -1
- package/dist/lib/remote-ui/pages/contracts.js.map +1 -1
- package/dist/lib/remote-ui/pages/procurement.d.ts +2 -1
- package/dist/lib/remote-ui/pages/procurement.d.ts.map +1 -1
- package/dist/lib/remote-ui/pages/procurement.js +32 -9
- package/dist/lib/remote-ui/pages/procurement.js.map +1 -1
- package/dist/lib/remote-ui/procurement-contracts.d.ts +10 -7
- package/dist/lib/remote-ui/procurement-contracts.d.ts.map +1 -1
- package/dist/lib/remote-ui/styles.css +21 -0
- package/dist/lib/workbench-view.provider.d.ts +1 -29
- package/dist/lib/workbench-view.provider.d.ts.map +1 -1
- package/dist/lib/workbench-view.provider.js +8 -27
- package/dist/lib/workbench-view.provider.js.map +1 -1
- package/dist/lib/workbench.middleware.d.ts.map +1 -1
- package/dist/lib/workbench.middleware.js +38 -22
- package/dist/lib/workbench.middleware.js.map +1 -1
- package/dist/lib/workbench.service.d.ts +12 -0
- package/dist/lib/workbench.service.d.ts.map +1 -1
- package/dist/lib/workbench.service.js +70 -17
- package/dist/lib/workbench.service.js.map +1 -1
- package/package.json +2 -2
|
@@ -11,7 +11,7 @@ import { join } from 'node:path';
|
|
|
11
11
|
import { finished } from 'node:stream/promises';
|
|
12
12
|
import { createHash, randomUUID } from 'node:crypto';
|
|
13
13
|
import { BankAccountSettings, CatalogReviewBlock, CompanySanctionMatch, CompanySettings, ControlledCatalogBatch, ControlledCatalogImportIssue, ControlledGoodsRecord, Customer, CustomerContract, CustomerContractAnalysisRun, CustomerContractClause, CustomerContractClauseAnalysis, DerivedSalesOrderManualData, GeneratedSalesFile, ImportTask, ManagedAsset, MonthlySafeExchangeRate, ProductComplianceMatch, ProfitRuleSettings, PurchaseOrder, PurchaseOrderLine, SanctionCatalogBatch, SanctionCatalogImportIssue, SanctionedCompanyAlias, SanctionedCompanyRecord, SettingsHistory, Supplier } from './entities/index.js';
|
|
14
|
-
import { CATALOG_REVIEW_BATCH_LIMIT } from './constants.js';
|
|
14
|
+
import { CATALOG_REVIEW_BATCH_LIMIT, CATALOG_REVIEW_LEASE_MS, CATALOG_TRANSLATION_REVIEW_LEASE_MS } from './constants.js';
|
|
15
15
|
import { CatalogTypes, ClauseTypes, CustomerProfitRateStatuses, DomainError, DomainErrorCodes, RefundValidationMethods, RiskLevels, SalesPaymentStatuses, SanctionHitStatuses, StalledCatalogImportDecisions } from './types.js';
|
|
16
16
|
import { deriveSalesLine, assertReceivedAmount } from './domain/finance.js';
|
|
17
17
|
import { parseCustomerContractNo, parsePurchaseContractNo } from './domain/contract-number.js';
|
|
@@ -694,7 +694,12 @@ let TradeComplianceWorkbenchService = class TradeComplianceWorkbenchService {
|
|
|
694
694
|
}
|
|
695
695
|
async getImportTaskResult(scope, taskId) {
|
|
696
696
|
await this.expireStaleImportTasks(scope);
|
|
697
|
-
const
|
|
697
|
+
const taskRepo = this.dataSource.getRepository(ImportTask);
|
|
698
|
+
let task = await taskRepo.findOne({ where: { ...activeScope(scope), id: taskId, deletedAt: IsNull() } });
|
|
699
|
+
task ??= await taskRepo.findOne({ where: { ...activeScope(scope), id: taskId } });
|
|
700
|
+
if (!task)
|
|
701
|
+
throw new DomainError(DomainErrorCodes.NOT_FOUND, '解析任务不存在');
|
|
702
|
+
task = await this.recoverExpiredCatalogReviewLeases(scope, task) ?? task;
|
|
698
703
|
const publicTask = (await this.publicImportTaskDtos([task]))[0];
|
|
699
704
|
if (!task.resultEntityId)
|
|
700
705
|
return { task: publicTask };
|
|
@@ -1375,7 +1380,7 @@ let TradeComplianceWorkbenchService = class TradeComplianceWorkbenchService {
|
|
|
1375
1380
|
if (blocks.length === 0)
|
|
1376
1381
|
return null;
|
|
1377
1382
|
const leaseToken = randomUUID();
|
|
1378
|
-
const leaseExpiresAt = new Date(Date.now() +
|
|
1383
|
+
const leaseExpiresAt = new Date(Date.now() + CATALOG_TRANSLATION_REVIEW_LEASE_MS);
|
|
1379
1384
|
for (const block of blocks) {
|
|
1380
1385
|
block.status = 'LEASED';
|
|
1381
1386
|
block.leaseToken = leaseToken;
|
|
@@ -1403,12 +1408,45 @@ let TradeComplianceWorkbenchService = class TradeComplianceWorkbenchService {
|
|
|
1403
1408
|
async countCatalogReviewBlocks(scope, taskId) {
|
|
1404
1409
|
return this.dataSource.getRepository(CatalogReviewBlock).count({ where: { ...activeScope(scope), importTaskId: taskId, status: In(['PENDING', 'LEASED']) } });
|
|
1405
1410
|
}
|
|
1411
|
+
async recoverExpiredCatalogReviewLeases(scope, task) {
|
|
1412
|
+
if ((task.documentType !== 'CONTROLLED_CATALOG' && task.documentType !== 'SANCTION_CATALOG') ||
|
|
1413
|
+
task.status !== 'AWAITING_REVIEW' || !task.progress)
|
|
1414
|
+
return null;
|
|
1415
|
+
return this.dataSource.transaction(async (manager) => {
|
|
1416
|
+
const taskRepo = manager.getRepository(ImportTask);
|
|
1417
|
+
const locked = await taskRepo.findOne({
|
|
1418
|
+
where: { ...activeScope(scope), id: task.id, deletedAt: IsNull() },
|
|
1419
|
+
lock: { mode: 'pessimistic_write' }
|
|
1420
|
+
});
|
|
1421
|
+
if (!locked || locked.status !== 'AWAITING_REVIEW')
|
|
1422
|
+
return null;
|
|
1423
|
+
await this.recoverExpiredCatalogReviewLeasesInTransaction(manager, scope, locked);
|
|
1424
|
+
return locked;
|
|
1425
|
+
});
|
|
1426
|
+
}
|
|
1427
|
+
async recoverExpiredCatalogReviewLeasesInTransaction(manager, scope, task) {
|
|
1428
|
+
if (task.documentType !== 'CONTROLLED_CATALOG' && task.documentType !== 'SANCTION_CATALOG')
|
|
1429
|
+
return task.progress?.reviewPendingCount ?? 0;
|
|
1430
|
+
const repo = manager.getRepository(CatalogReviewBlock);
|
|
1431
|
+
await repo.update({
|
|
1432
|
+
...activeScope(scope), importTaskId: task.id, status: 'LEASED', leaseExpiresAt: LessThanOrEqual(new Date())
|
|
1433
|
+
}, { status: 'PENDING', leaseToken: null, leaseExpiresAt: null });
|
|
1434
|
+
const previousPendingCount = task.progress?.reviewPendingCount;
|
|
1435
|
+
const pendingCount = await this.reconcileCatalogReviewPendingCount(repo, scope, task);
|
|
1436
|
+
if (task.progress && previousPendingCount !== pendingCount) {
|
|
1437
|
+
task.progress.progressVersion += 1;
|
|
1438
|
+
task.progress.lastProgressAt = new Date().toISOString();
|
|
1439
|
+
await manager.getRepository(ImportTask).save(task);
|
|
1440
|
+
}
|
|
1441
|
+
return pendingCount;
|
|
1442
|
+
}
|
|
1406
1443
|
async waitImportProgress(scope, taskId, afterProgressVersion = 0, waitMs = 10_000) {
|
|
1407
1444
|
const deadline = Date.now() + Math.max(0, Math.min(waitMs, 10_000));
|
|
1408
1445
|
while (true) {
|
|
1409
|
-
|
|
1446
|
+
let task = await required(this.dataSource.getRepository(ImportTask).findOne({
|
|
1410
1447
|
where: { ...activeScope(scope), id: taskId }
|
|
1411
1448
|
}), '解析任务不存在');
|
|
1449
|
+
task = await this.recoverExpiredCatalogReviewLeases(scope, task) ?? task;
|
|
1412
1450
|
const version = task.progress?.progressVersion ?? 0;
|
|
1413
1451
|
if (version > afterProgressVersion || TERMINAL_IMPORT_TASK_STATUSES.includes(task.status)) {
|
|
1414
1452
|
return { task: publicImportTaskDto(task) };
|
|
@@ -1429,16 +1467,7 @@ let TradeComplianceWorkbenchService = class TradeComplianceWorkbenchService {
|
|
|
1429
1467
|
throw new DomainError(DomainErrorCodes.INVALID_INPUT, '仅目录任务包含复核块');
|
|
1430
1468
|
}
|
|
1431
1469
|
const repo = manager.getRepository(CatalogReviewBlock);
|
|
1432
|
-
await
|
|
1433
|
-
...activeScope(scope), importTaskId: taskId, status: 'LEASED', leaseExpiresAt: LessThanOrEqual(new Date())
|
|
1434
|
-
}, { status: 'PENDING', leaseToken: null, leaseExpiresAt: null });
|
|
1435
|
-
const previousPendingCount = task.progress?.reviewPendingCount;
|
|
1436
|
-
const pendingCount = await this.reconcileCatalogReviewPendingCount(repo, scope, task);
|
|
1437
|
-
if (task.progress && previousPendingCount !== pendingCount) {
|
|
1438
|
-
task.progress.progressVersion += 1;
|
|
1439
|
-
task.progress.lastProgressAt = new Date().toISOString();
|
|
1440
|
-
await manager.getRepository(ImportTask).save(task);
|
|
1441
|
-
}
|
|
1470
|
+
await this.recoverExpiredCatalogReviewLeasesInTransaction(manager, scope, task);
|
|
1442
1471
|
const blocks = await repo.createQueryBuilder('block')
|
|
1443
1472
|
.setLock('pessimistic_write')
|
|
1444
1473
|
.setOnLocked('skip_locked')
|
|
@@ -1449,10 +1478,34 @@ let TradeComplianceWorkbenchService = class TradeComplianceWorkbenchService {
|
|
|
1449
1478
|
.addOrderBy('block.id', 'ASC')
|
|
1450
1479
|
.limit(limit)
|
|
1451
1480
|
.getMany();
|
|
1452
|
-
if (blocks.length === 0)
|
|
1453
|
-
|
|
1481
|
+
if (blocks.length === 0) {
|
|
1482
|
+
const leased = await repo.createQueryBuilder('block')
|
|
1483
|
+
.setLock('pessimistic_write')
|
|
1484
|
+
.setOnLocked('skip_locked')
|
|
1485
|
+
.where('block."tenantId" = :tenantId AND block."organizationId" = :organizationId AND block."importTaskId" = :taskId AND block.status = :status', {
|
|
1486
|
+
...activeScope(scope), taskId, status: 'LEASED'
|
|
1487
|
+
})
|
|
1488
|
+
.andWhere('block."leaseExpiresAt" IS NOT NULL')
|
|
1489
|
+
.orderBy('block."leaseExpiresAt"', 'ASC')
|
|
1490
|
+
.addOrderBy('block.ordinal', 'ASC')
|
|
1491
|
+
.addOrderBy('block.id', 'ASC')
|
|
1492
|
+
.limit(1)
|
|
1493
|
+
.getOne();
|
|
1494
|
+
if (!leased)
|
|
1495
|
+
return { leaseToken: null, items: [] };
|
|
1496
|
+
const leasedUntil = leased.leaseExpiresAt instanceof Date ? leased.leaseExpiresAt : null;
|
|
1497
|
+
const leaseExpiresAt = leasedUntil?.toISOString() ?? null;
|
|
1498
|
+
const retryAfterMs = leasedUntil ? Math.max(1000, leasedUntil.getTime() - Date.now()) : 5000;
|
|
1499
|
+
return {
|
|
1500
|
+
leaseToken: null,
|
|
1501
|
+
items: [],
|
|
1502
|
+
leaseExpiresAt,
|
|
1503
|
+
retryAfterMs,
|
|
1504
|
+
instruction: '当前待复核块仍被占用,请等待 retryAfterMs 后再调用 nextReviewBatch。'
|
|
1505
|
+
};
|
|
1506
|
+
}
|
|
1454
1507
|
const leaseToken = randomUUID();
|
|
1455
|
-
const leaseExpiresAt = new Date(Date.now() +
|
|
1508
|
+
const leaseExpiresAt = new Date(Date.now() + CATALOG_REVIEW_LEASE_MS);
|
|
1456
1509
|
for (const block of blocks) {
|
|
1457
1510
|
block.status = 'LEASED';
|
|
1458
1511
|
block.leaseToken = leaseToken;
|