Files
baiyue-firstleg/scripts/synchronous-dependency-baseline.mjs
T

273 lines
21 KiB
JavaScript

export const CONTEXTS = Object.freeze(['REQUEST', 'BACKGROUND', 'INFRASTRUCTURE', 'EXTERNAL'])
export const ACCESS_MODES = Object.freeze(['READ', 'WRITE', 'READ_WRITE'])
export const IDEMPOTENCY = Object.freeze([
'SAFE_READ', 'IDEMPOTENCY_KEY', 'BUSINESS_KEY', 'EVENT_ID', 'STATE_CONVERGENT', 'NONE', 'MIXED'
])
const feign = (path, source, target, contexts, access, idempotency, note) => ({
id: `FEIGN:${path}`,
transport: 'FEIGN', path, source, target, contexts, access, idempotency, note
})
const http = (path, source, target, contexts, access, idempotency, note) => ({
id: `HTTP:${path}`,
transport: 'HTTP', path, source, target, contexts, access, idempotency, note
})
export const SYNCHRONOUS_CLIENTS = Object.freeze([
feign('auth/src/main/java/com/baiyue/auth/client/IamAuthorizationClient.java',
'auth-service', 'iam-service', ['REQUEST'], 'READ', 'SAFE_READ', 'POST 读取授权快照,不写 IAM'),
feign('auth/src/main/java/com/baiyue/auth/client/IamOnboardingClient.java',
'auth-service', 'iam-service', ['REQUEST'], 'WRITE', 'BUSINESS_KEY', '注册/补偿以 authUserId 收敛,不自动重试'),
feign('file/src/main/java/com/baiyue/file/client/FinanceExportProviderClient.java',
'file-service', 'finance-service', ['BACKGROUND'], 'READ', 'SAFE_READ', '导出 Worker 分批读取'),
feign('file/src/main/java/com/baiyue/file/client/FirstlegExportProviderClient.java',
'file-service', 'firstleg-service', ['BACKGROUND'], 'READ', 'SAFE_READ', '导出 Worker 分批读取'),
feign('file/src/main/java/com/baiyue/file/client/IamExportProviderClient.java',
'file-service', 'iam-service', ['BACKGROUND'], 'READ', 'SAFE_READ', '导出 Worker 分批读取'),
feign('file/src/main/java/com/baiyue/file/client/IamQualificationClient.java',
'file-service', 'iam-service', ['REQUEST'], 'WRITE', 'STATE_CONVERGENT', '资质登记/替换按客户与资质类型收敛,无通用幂等头'),
feign('file/src/main/java/com/baiyue/file/client/OrderExportProviderClient.java',
'file-service', 'order-service', ['BACKGROUND'], 'READ', 'SAFE_READ', '导出 Worker 分批读取'),
feign('file/src/main/java/com/baiyue/file/client/WarehouseExportProviderClient.java',
'file-service', 'warehouse-service', ['BACKGROUND'], 'READ', 'SAFE_READ', '导出 Worker 分批读取'),
feign('file/src/main/java/com/baiyue/file/client/WeightOcrClient.java',
'file-service', 'integration-service', ['REQUEST'], 'READ', 'NONE', '上传线程内调用 OCR;第三方调用不可假定免费幂等'),
feign('finance/src/main/java/com/baiyue/finance/client/FinanceFileClient.java',
'finance-service', 'file-service', ['REQUEST'], 'READ_WRITE', 'MIXED', '元数据 GET 可重复,凭证上传无幂等键'),
feign('finance/src/main/java/com/baiyue/finance/client/FirstlegCostSnapshotClient.java',
'finance-service', 'firstleg-service', ['REQUEST'], 'READ', 'SAFE_READ', '读取整柜成本分摊快照'),
feign('finance/src/main/java/com/baiyue/finance/client/FirstlegFeeSourceClient.java',
'finance-service', 'firstleg-service', ['REQUEST', 'BACKGROUND'], 'READ_WRITE', 'MIXED',
'事件消费回读快照;平台作废命令携带 Idempotency-Key 与请求哈希'),
feign('finance/src/main/java/com/baiyue/finance/client/IamCustomerClient.java',
'finance-service', 'iam-service', ['REQUEST'], 'READ', 'SAFE_READ', '读取客户主档快照'),
feign('finance/src/main/java/com/baiyue/finance/client/WalletResultEventBridgeClient.java',
'finance-service', 'event-bridge-service', ['BACKGROUND'], 'WRITE', 'EVENT_ID', '钱包结果 Outbox 按 eventId 回传海外业务'),
feign('finance/src/main/java/com/baiyue/finance/client/WarehouseSupplierClient.java',
'finance-service', 'warehouse-service', ['REQUEST'], 'READ', 'SAFE_READ', '读取并校验供应商快照'),
feign('firstleg/src/main/java/com/baiyue/firstleg/client/CarrierScheduleIntegrationClient.java',
'firstleg-service', 'integration-service', ['BACKGROUND'], 'READ', 'SAFE_READ', '船节点定时任务查询,不属于用户请求'),
feign('firstleg/src/main/java/com/baiyue/firstleg/client/EventBridgeClient.java',
'firstleg-service', 'event-bridge-service', ['BACKGROUND'], 'WRITE', 'EVENT_ID', '装柜/船节点 Outbox 按 eventId 投递'),
feign('firstleg/src/main/java/com/baiyue/firstleg/client/FeePaymentVoucherFileClient.java',
'firstleg-service', 'file-service', ['REQUEST'], 'READ', 'SAFE_READ', '校验付款凭证元数据'),
feign('firstleg/src/main/java/com/baiyue/firstleg/client/InboundHoldFileClient.java',
'firstleg-service', 'file-service', ['REQUEST'], 'READ_WRITE', 'MIXED', '扣货凭证上传无幂等键,元数据与下载可重复'),
feign('firstleg/src/main/java/com/baiyue/firstleg/client/FinanceFeeSourceEventClient.java',
'firstleg-service', 'finance-service', ['BACKGROUND'], 'WRITE', 'EVENT_ID', '费用 Outbox 按事件幂等键投递'),
feign('firstleg/src/main/java/com/baiyue/firstleg/client/IamCustomerSnapshotClient.java',
'firstleg-service', 'iam-service', ['REQUEST'], 'READ', 'SAFE_READ', '读取客户名称与状态快照'),
feign('firstleg/src/main/java/com/baiyue/firstleg/client/LoadingDocumentFileClient.java',
'firstleg-service', 'file-service', ['REQUEST'], 'READ_WRITE', 'MIXED', '上传无幂等键,元数据/下载可重复'),
feign('firstleg/src/main/java/com/baiyue/firstleg/client/LoadingEvidenceFileClient.java',
'firstleg-service', 'file-service', ['REQUEST'], 'READ', 'SAFE_READ', '读取装柜证据元数据/内容'),
feign('firstleg/src/main/java/com/baiyue/firstleg/client/MeasurementImageClient.java',
'firstleg-service', 'file-service', ['REQUEST'], 'READ_WRITE', 'MIXED', '图片上传/作废非安全重放,查询可重复'),
feign('firstleg/src/main/java/com/baiyue/firstleg/client/OceanBookingFileClient.java',
'firstleg-service', 'file-service', ['REQUEST'], 'READ_WRITE', 'MIXED', '订舱附件上传无幂等键,元数据与下载可重复'),
feign('firstleg/src/main/java/com/baiyue/firstleg/client/OrderCargoClient.java',
'firstleg-service', 'order-service', ['REQUEST', 'BACKGROUND'], 'READ', 'SAFE_READ',
'在线测量/箱号/排柜读取;Order 审核 Outbox 创建 Firstleg 入库及箱号时也会后台回读订单货物'),
feign('firstleg/src/main/java/com/baiyue/firstleg/client/OrderFulfillmentSplitClient.java',
'firstleg-service', 'order-service', ['REQUEST'], 'WRITE', 'IDEMPOTENCY_KEY', 'PDA 结算拆单携带 idempotencyKey/operationId'),
feign('firstleg/src/main/java/com/baiyue/firstleg/client/PendingReviewOrderClient.java',
'firstleg-service', 'order-service', ['REQUEST'], 'READ', 'SAFE_READ', '智能排柜分页读取待审核订单投影'),
feign('firstleg/src/main/java/com/baiyue/firstleg/client/PriceBookFileClient.java',
'firstleg-service', 'file-service', ['REQUEST'], 'READ_WRITE', 'MIXED', '价目文件上传无幂等键,读取可重复'),
feign('firstleg/src/main/java/com/baiyue/firstleg/client/PriceBookWarehouseClient.java',
'firstleg-service', 'warehouse-service', ['REQUEST'], 'READ', 'SAFE_READ', '读取仓库/渠道主数据'),
feign('firstleg/src/main/java/com/baiyue/firstleg/client/PrintableOrderInboundClient.java',
'firstleg-service', 'order-service', ['REQUEST'], 'WRITE', 'BUSINESS_KEY',
'首次打印按客户与订单预留稳定入仓码,重复调用复用已冻结快照'),
feign('firstleg/src/main/java/com/baiyue/firstleg/client/PrintPdfClient.java',
'firstleg-service', 'print-service', ['REQUEST'], 'READ', 'SAFE_READ', '无状态 PDF 渲染,POST 为计算型读取'),
feign('firstleg/src/main/java/com/baiyue/firstleg/client/TruckAppointmentFileClient.java',
'firstleg-service', 'file-service', ['REQUEST'], 'READ_WRITE', 'MIXED', '附件上传无幂等键,元数据读取可重复'),
feign('firstleg/src/main/java/com/baiyue/firstleg/client/WarehouseMasterDataClient.java',
'firstleg-service', 'warehouse-service', ['REQUEST'], 'READ', 'SAFE_READ', '读取仓库主数据'),
feign('firstleg/src/main/java/com/baiyue/firstleg/client/WarehousePalletLocationClient.java',
'firstleg-service', 'warehouse-service', ['REQUEST'], 'READ_WRITE', 'MIXED',
'查询可重复;预占/选择释放/转移/结算使用业务幂等键,普通确认/释放按状态收敛'),
feign('firstleg/src/main/java/com/baiyue/firstleg/client/WarehousePartnerClient.java',
'firstleg-service', 'warehouse-service', ['REQUEST'], 'READ', 'SAFE_READ', '读取启用往来单位快照'),
feign('firstleg/src/main/java/com/baiyue/firstleg/client/WarehouseSupplierClient.java',
'firstleg-service', 'warehouse-service', ['REQUEST'], 'READ', 'SAFE_READ', '读取供应商快照'),
feign('firstleg/src/main/java/com/baiyue/firstleg/customs/client/CustomsFileClient.java',
'firstleg-service', 'file-service', ['REQUEST'], 'READ_WRITE', 'MIXED', '报关文件上传无幂等键,读取可重复'),
feign('iam/src/main/java/com/baiyue/iam/client/AuthAccountClient.java',
'iam-service', 'auth-service', ['REQUEST'], 'READ_WRITE', 'MIXED', '账号查询可重复,写操作按账号/命令业务键收敛'),
feign('iam/src/main/java/com/baiyue/iam/client/CustomerDimensionRuleClient.java',
'iam-service', 'firstleg-service', ['BACKGROUND'], 'WRITE', 'BUSINESS_KEY',
'IAM 持久化任务租约投递;初始化/补偿复用稳定 initializationKey,不在客户创建请求内调用'),
feign('iam/src/main/java/com/baiyue/iam/client/OverseasStorageFeeClient.java',
'iam-service', 'warehouse-service', ['REQUEST'], 'WRITE', 'BUSINESS_KEY', '初始化/补偿使用 initializationKey'),
feign('iam/src/main/java/com/baiyue/iam/client/SecurityEventBridgeClient.java',
'iam-service', 'event-bridge-service', ['BACKGROUND'], 'WRITE', 'EVENT_ID', 'IAM 安全 Outbox 按 eventId 投递'),
feign('lastleg-service/src/main/java/com/baiyue/lastleg/client/EventBridgeClient.java',
'lastleg-service', 'event-bridge-service', ['BACKGROUND'], 'WRITE', 'EVENT_ID', '卸柜完成 Outbox 按 eventId 投递'),
feign('lastleg-service/src/main/java/com/baiyue/lastleg/client/DomesticWarehouseDirectoryClient.java',
'lastleg-service', 'warehouse-service', ['REQUEST'], 'READ', 'SAFE_READ', '读取国内共享仓库权威目录'),
feign('lastleg-service/src/main/java/com/baiyue/lastleg/masterdata/quote/client/DomesticPartnerWarehouseClient.java',
'lastleg-service', 'warehouse-service', ['REQUEST'], 'READ', 'SAFE_READ', '报价创建时读取国内合作仓快照'),
feign('warehouse-overseas-service/src/main/java/com/baiyue/warehouse/client/DomesticWarehouseDirectoryClient.java',
'warehouse-overseas-service', 'warehouse-service', ['REQUEST'], 'READ', 'SAFE_READ',
'海外库位、托盘和资源锁写入前读取国内共享收货仓权威目录'),
feign('order/src/main/java/com/baiyue/order/client/FirstlegColoadGroupClient.java',
'order-service', 'firstleg-service', ['REQUEST'], 'READ', 'SAFE_READ', '批量读取同柜不可拆组聚合'),
feign('order/src/main/java/com/baiyue/order/client/FinanceWalletClient.java',
'order-service', 'finance-service', ['REQUEST'], 'READ', 'SAFE_READ', '提交订单前读取客户钱包余额与有效授信'),
feign('order/src/main/java/com/baiyue/order/client/FirstlegInboundClient.java',
'order-service', 'firstleg-service', ['REQUEST', 'BACKGROUND'], 'READ_WRITE', 'BUSINESS_KEY',
'列表查询/缺失修复在线执行;审核建单由 Outbox 投递,按 orderId/inboundCode 收敛'),
feign('order/src/main/java/com/baiyue/order/client/FirstlegPricingClient.java',
'order-service', 'firstleg-service', ['REQUEST'], 'READ', 'SAFE_READ', 'POST 校验计价渠道,不写履约状态'),
feign('order/src/main/java/com/baiyue/order/client/FirstlegUnifiedOrderClient.java',
'order-service', 'firstleg-service', ['REQUEST'], 'READ', 'SAFE_READ', 'POST 查询统一订单投影'),
feign('order/src/main/java/com/baiyue/order/client/MerchantPricingCatalogClient.java',
'order-service', 'firstleg-service', ['REQUEST'], 'READ', 'SAFE_READ', '批量下单读取客户可用物流渠道'),
feign('order/src/main/java/com/baiyue/order/client/MerchantWarehouseCatalogClient.java',
'order-service', 'warehouse-service', ['REQUEST'], 'READ', 'SAFE_READ', '批量下单分页读取启用仓库'),
feign('order/src/main/java/com/baiyue/order/client/OrderBatchFileClient.java',
'order-service', 'file-service', ['REQUEST'], 'WRITE', 'NONE', '批量导入文件上传无通用幂等键'),
feign('order/src/main/java/com/baiyue/order/client/ProductImageClient.java',
'order-service', 'file-service', ['REQUEST'], 'READ', 'SAFE_READ', '读取商品图片元数据'),
feign('order/src/main/java/com/baiyue/order/client/TenantEligibilityClient.java',
'order-service', 'iam-service', ['REQUEST'], 'READ', 'SAFE_READ', '读取客户下单资格'),
feign('order/src/main/java/com/baiyue/order/client/WarehouseClient.java',
'order-service', 'warehouse-service', ['REQUEST'], 'READ', 'SAFE_READ', '读取仓库主数据'),
http('common-audit/src/main/java/com/baiyue/common/audit/HttpAuditSink.java',
'common-audit', 'audit-service', ['INFRASTRUCTURE'], 'WRITE', 'NONE', '同步审计投递失败当前仅记录告警'),
http('event-bridge-service/src/main/java/com/baiyue/eventbridge/service/HttpFinanceEventClient.java',
'event-bridge-service', 'finance-service', ['BACKGROUND'], 'WRITE', 'EVENT_ID', '跨区费用 Inbox 按 eventId 投递 Finance'),
http('event-bridge-service/src/main/java/com/baiyue/eventbridge/service/HttpFirstlegEventClient.java',
'event-bridge-service', 'firstleg-service', ['BACKGROUND'], 'WRITE', 'EVENT_ID', 'Inbox 租约投递,目标按 eventId 幂等'),
http('event-bridge-service/src/main/java/com/baiyue/eventbridge/service/HttpLastlegEventClient.java',
'event-bridge-service', 'lastleg-service', ['BACKGROUND'], 'WRITE', 'EVENT_ID', 'Inbox 租约投递,目标按 eventId 幂等'),
http('event-bridge-service/src/main/java/com/baiyue/eventbridge/service/HttpSecurityProjectionEventClient.java',
'event-bridge-service', 'lastleg-service', ['BACKGROUND'], 'WRITE', 'EVENT_ID', 'IAM 安全事件投影按 eventId/版本单调幂等'),
http('gateway-overseas/src/main/java/com/baiyue/gateway/filter/HttpOverseasSecurityValidationClient.java',
'gateway-overseas', 'lastleg-service', ['INFRASTRUCTURE'], 'READ', 'SAFE_READ', 'POST 实时校验,失败关闭'),
http('gateway-overseas/src/main/java/com/baiyue/gateway/filter/HttpPdaSessionValidationClient.java',
'gateway-overseas', 'auth-service', ['INFRASTRUCTURE'], 'READ', 'SAFE_READ', 'POST PDA 会话校验,失败关闭'),
http('lastleg-service/src/main/java/com/baiyue/lastleg/customervisit/gateway/HttpCustomerVisitFileGateway.java',
'lastleg-service', 'file-service', ['REQUEST'], 'READ_WRITE', 'MIXED', '回访附件上传无幂等键,归属校验可重复'),
http('lastleg-service/src/main/java/com/baiyue/lastleg/delivery/gateway/HttpDeliveryDocumentFileGateway.java',
'lastleg-service', 'file-service', ['REQUEST'], 'READ_WRITE', 'MIXED', '派送资料上传无幂等键,元数据按客户/派送单/类别校验,当前与历史下载只读'),
http('lastleg-service/src/main/java/com/baiyue/lastleg/exceptionorder/gateway/HttpExceptionEvidenceFileGateway.java',
'lastleg-service', 'file-service', ['REQUEST'], 'READ_WRITE', 'MIXED', '异常处理证据上传无幂等键,归属校验可重复'),
http('lastleg-service/src/main/java/com/baiyue/lastleg/inboundplan/pda/gateway/HttpUnloadPhotoFileGateway.java',
'lastleg-service', 'file-service', ['REQUEST'], 'READ_WRITE', 'MIXED', '上传无幂等键,元数据校验可重复'),
http('lastleg-service/src/main/java/com/baiyue/lastleg/inboundplan/pda/gateway/HttpWarehouseResourceGateway.java',
'lastleg-service', 'warehouse-overseas-service', ['REQUEST'], 'READ_WRITE', 'MIXED', '查询可重复;写操作按 business/reservation key 或状态收敛'),
http('lastleg-service/src/main/java/com/baiyue/lastleg/palletops/gateway/HttpPalletPhysicalGateway.java',
'lastleg-service', 'warehouse-overseas-service', ['REQUEST'], 'READ_WRITE', 'IDEMPOTENCY_KEY', '托盘变更/补偿使用 operationKey 与请求哈希'),
http('lastleg-service/src/main/java/com/baiyue/lastleg/procurement/gateway/HttpProcurementFileGateway.java',
'lastleg-service', 'file-service', ['REQUEST'], 'WRITE', 'NONE', '采购凭证和签收文件上传无幂等键,禁止客户端自动重试'),
http('lastleg-service/src/main/java/com/baiyue/lastleg/relabel/gateway/HttpRelabelDocumentFileGateway.java',
'lastleg-service', 'file-service', ['REQUEST'], 'READ_WRITE', 'MIXED', '换标PDF上传无幂等键,按客户/业务类型/业务单归属下载只读,传输层不自动重试'),
http('lastleg-service/src/main/java/com/baiyue/lastleg/relabel/pda/gateway/HttpRelabelPhotoFileGateway.java',
'lastleg-service', 'file-service', ['REQUEST'], 'READ_WRITE', 'MIXED', '上传无幂等键,元数据校验可重复'),
http('integration-service/src/main/java/com/baiyue/integration/provider/EvergreenTrackingClient.java',
'integration-service', 'external-evergreen', ['EXTERNAL'], 'READ', 'SAFE_READ', '船司轨迹查询;遵守外部限流'),
http('integration-service/src/main/java/com/baiyue/integration/provider/HedeTrackingClient.java',
'integration-service', 'external-hede', ['EXTERNAL'], 'READ', 'SAFE_READ', '船司轨迹查询;遵守外部限流'),
http('integration-service/src/main/java/com/baiyue/integration/provider/MatsonTrackingClient.java',
'integration-service', 'external-matson', ['EXTERNAL'], 'READ', 'SAFE_READ', '船司轨迹查询;遵守外部限流'),
http('integration-service/src/main/java/com/baiyue/integration/provider/SmlTrackingClient.java',
'integration-service', 'external-sml', ['EXTERNAL'], 'READ', 'SAFE_READ', '船司轨迹查询;遵守外部限流'),
http('integration-service/src/main/java/com/baiyue/integration/provider/ZimTrackingClient.java',
'integration-service', 'external-zim', ['EXTERNAL'], 'READ', 'SAFE_READ', '令牌获取与轨迹查询不改变平台业务事实'),
http('integration-service/src/main/java/com/baiyue/integration/provider/ocr/BaiduMeterClient.java',
'integration-service', 'external-baidu-ocr', ['EXTERNAL'], 'READ', 'NONE', 'OCR 可能计费,不由 HTTP 层自动重试')
])
export const USER_ENTRIES = Object.freeze([
{ name: '认证 API', service: 'auth-service' },
{ name: 'IAM API', service: 'iam-service' },
{ name: '订单 API', service: 'order-service' },
{ name: '头程 API/PDA', service: 'firstleg-service' },
{ name: '财务 API', service: 'finance-service' },
{ name: '文件 API', service: 'file-service' },
{ name: '海外尾程 API/PDA', service: 'lastleg-service' },
{ name: '国内仓储 API', service: 'warehouse-service' },
{ name: '海外仓储 API', service: 'warehouse-overseas-service' }
])
export function graphEdges(context) {
return [...new Set(SYNCHRONOUS_CLIENTS
.filter(client => context === 'ALL' || client.contexts.includes(context))
.map(client => `${client.source}->${client.target}`))].sort()
}
export function stronglyConnectedComponents(edgeNames) {
const adjacency = new Map()
for (const edge of edgeNames) {
const [from, to] = edge.split('->')
if (!adjacency.has(from)) adjacency.set(from, new Set())
if (!adjacency.has(to)) adjacency.set(to, new Set())
adjacency.get(from).add(to)
}
let index = 0
const indexes = new Map()
const lowLinks = new Map()
const stack = []
const onStack = new Set()
const components = []
function connect(node) {
indexes.set(node, index)
lowLinks.set(node, index++)
stack.push(node)
onStack.add(node)
for (const target of adjacency.get(node)) {
if (!indexes.has(target)) {
connect(target)
lowLinks.set(node, Math.min(lowLinks.get(node), lowLinks.get(target)))
} else if (onStack.has(target)) lowLinks.set(node, Math.min(lowLinks.get(node), indexes.get(target)))
}
if (lowLinks.get(node) !== indexes.get(node)) return
const component = []
let current
do {
current = stack.pop()
onStack.delete(current)
component.push(current)
} while (current !== node)
components.push(component.sort())
}
for (const node of adjacency.keys()) if (!indexes.has(node)) connect(node)
return components.filter(component => component.length > 1)
.sort((left, right) => left.join().localeCompare(right.join()))
}
export function longestSimplePaths(entryService, edgeNames = graphEdges('REQUEST')) {
const adjacency = new Map()
for (const edge of edgeNames) {
const [from, to] = edge.split('->')
if (!adjacency.has(from)) adjacency.set(from, new Set())
adjacency.get(from).add(to)
}
let longest = []
function visit(node, path) {
const targets = [...(adjacency.get(node) ?? [])].sort()
let extended = false
for (const target of targets) {
if (path.includes(target)) continue
extended = true
visit(target, [...path, target])
}
if (extended) return
if (longest.length === 0 || path.length > longest[0].length) longest = [path]
else if (path.length === longest[0].length) longest.push(path)
}
visit(entryService, [entryService])
return longest.sort((left, right) => left.join('->').localeCompare(right.join('->')))
}