文件
hyb-oa/client/src/api/integrations.ts
T
wangsli 7cc6dc3397 R53 A1-A6:ERP 集成运维可视化与业务同步状态接入
- ErpIntegrationPage Tab 化:主数据同步 / Outbox 队列管理 / ID 映射查看器
- Outbox 分页查询、详情、重试与统计端点
- IntegrationBinding 分页查询、routePath 推导与采购/资产深链
- 报销/采购/资产详情区接入 ERP 同步状态卡片
- 单测覆盖 Outbox 与 Binding 端点
2026-04-07 10:33:58 +08:00

291 行
7.5 KiB
TypeScript

import { apiRequestData } from "./http";
export type ErpMasterDataResourceType = "legal_entity" | "cost_center" | "supplier";
export type ErpGateStatus = {
envEnabled: boolean;
effectiveEnabled: boolean;
effectiveReason: string;
provider: string;
masterDataEndpointConfigured: boolean;
journalEndpointConfigured: boolean;
masterDataSyncCron: string | null;
};
export type ErpMasterDataSyncSummary = {
requested: number;
created: number;
updated: number;
archived: number;
skipped: number;
sampleSkipped: Array<{ key: string; reason: string }>;
};
export type ErpMasterDataSyncItem = {
resourceType: ErpMasterDataResourceType;
status: string;
lastStartedAt: string | null;
lastFinishedAt: string | null;
lastSuccessAt: string | null;
lastError: string | null;
lastResult: ErpMasterDataSyncSummary | null;
};
export type ErpMasterDataStatusResponse = {
gate: ErpGateStatus;
items: ErpMasterDataSyncItem[];
triggerMode?: string;
};
export type ReimbursementJournalPreview = {
journal: {
journalType: string;
sourceEventName: string;
entityType: string;
entityId: string;
documentNo: string | null;
currencyCode: string;
totalAmount: string | null;
occurredAt: string | null;
paymentVoucherNo: string | null;
paymentDate: string | null;
dimensions: {
departmentId: string | null;
ownerEmployeeId: string | null;
submitterEmployeeId: string | null;
legalEntityCode: string | null;
costCenterCode: string | null;
};
};
exportState: {
externalId: string | null;
lastOutboxId: string | null;
lastOutboxStatus: string | null;
lastError: string | null;
exportedAt: string | null;
};
};
export type IntegrationOutboxStatus =
| "pending"
| "processing"
| "succeeded"
| "failed"
| "dead_letter";
export type IntegrationOutboxPayloadSummary = {
entityType: string | null;
entityId: string | null;
sourceEventName: string | null;
journalType: string | null;
exportType: string | null;
identityCode: string | null;
};
export type IntegrationOutboxItem = {
id: string;
eventType: string;
status: IntegrationOutboxStatus | string;
payloadSummary: IntegrationOutboxPayloadSummary;
attempts: number;
maxAttempts: number;
nextRetryAt: string | null;
createdAt: string | null;
processedAt: string | null;
deadLetterAt: string | null;
};
export type IntegrationOutboxDetail = IntegrationOutboxItem & {
payload: Record<string, unknown> | null;
lastError: string | null;
};
export type IntegrationOutboxListResponse = {
page: number;
pageSize: number;
total: number;
items: IntegrationOutboxItem[];
};
export type IntegrationOutboxStats = {
pending: number;
processing: number;
succeeded: number;
failed: number;
deadLetter: number;
};
export type IntegrationOutboxQuery = {
page?: number;
pageSize?: number;
status?: IntegrationOutboxStatus;
eventType?: string;
};
export type IntegrationBindingItem = {
id: string;
provider: string;
entityType: string;
localId: string;
externalId: string | null;
externalCode: string | null;
lastSyncedAt: string | null;
routePath: string | null;
};
export type IntegrationBindingListResponse = {
page: number;
pageSize: number;
total: number;
items: IntegrationBindingItem[];
};
export type IntegrationBindingQuery = {
page?: number;
pageSize?: number;
provider?: string;
entityType?: string;
};
function toQueryString(query: Record<string, string | number | boolean | undefined>) {
const params = new URLSearchParams();
for (const [key, value] of Object.entries(query)) {
if (value == null || value === "") {
continue;
}
params.set(key, typeof value === "boolean" ? (value ? "1" : "0") : String(value));
}
const encoded = params.toString();
return encoded ? `?${encoded}` : "";
}
export async function triggerErpSync(reimbursementId: string, runNow = true) {
return apiRequestData<{
outboxId: string;
status: string;
duplicated: boolean;
}>(`/api/integrations/erp/sync/${reimbursementId}?runNow=${runNow ? "1" : "0"}`, {
method: "POST",
body: JSON.stringify({}),
});
}
export async function fetchErpMasterDataStatus() {
return apiRequestData<ErpMasterDataStatusResponse>("/api/integrations/erp/master-data/status");
}
export async function syncErpMasterData(payload: { resources?: ErpMasterDataResourceType[] }) {
return apiRequestData<ErpMasterDataStatusResponse>("/api/integrations/erp/master-data/sync", {
method: "POST",
body: JSON.stringify(payload),
});
}
export async function previewReimbursementJournal(reimbursementId: string) {
return apiRequestData<ReimbursementJournalPreview>(
`/api/integrations/erp/journals/reimbursements/${reimbursementId}/preview`,
);
}
export async function exportReimbursementJournal(reimbursementId: string, runNow = true) {
return apiRequestData<{
outboxId: string;
status: string;
duplicated: boolean;
requeued: boolean;
eventType: string;
}>(`/api/integrations/erp/journals/reimbursements/${reimbursementId}/export?runNow=${runNow ? "1" : "0"}`, {
method: "POST",
body: JSON.stringify({}),
});
}
export async function fetchIntegrationOutbox(query: IntegrationOutboxQuery = {}) {
return apiRequestData<IntegrationOutboxListResponse>(
`/api/integrations/outbox${toQueryString({
page: query.page,
pageSize: query.pageSize,
status: query.status,
event_type: query.eventType,
})}`,
);
}
export async function fetchIntegrationOutboxDetail(outboxId: string) {
const response = await apiRequestData<{ item: IntegrationOutboxDetail }>(`/api/integrations/outbox/${outboxId}`);
return response.item;
}
export async function retryIntegrationOutbox(outboxId: string) {
const response = await apiRequestData<{ item: IntegrationOutboxDetail }>(`/api/integrations/outbox/${outboxId}/retry`, {
method: "POST",
body: JSON.stringify({}),
});
return response.item;
}
export async function fetchIntegrationOutboxStats() {
return apiRequestData<IntegrationOutboxStats>("/api/integrations/outbox/stats");
}
export async function fetchIntegrationBindings(query: IntegrationBindingQuery = {}) {
return apiRequestData<IntegrationBindingListResponse>(
`/api/integrations/bindings${toQueryString({
page: query.page,
pageSize: query.pageSize,
provider: query.provider,
entity_type: query.entityType,
})}`,
);
}
export async function importTravelOrders(payload: {
orders: Array<{
source: string;
externalOrderNo: string;
employeeId: string;
linkedReimbursementId?: string;
occurredAt?: string;
amount?: number;
currency?: string;
}>;
}) {
return apiRequestData<{
requested: number;
imported: number;
skipped: Array<{ source: string; externalOrderNo: string; reason: string }>;
items: Array<{
id: string;
source: string;
externalOrderNo: string;
employeeId: string;
linkedReimbursementId: string | null;
}>;
}>(`/api/travel/orders/import`, {
method: "POST",
body: JSON.stringify(payload),
});
}
export async function testNotificationChannel(payload: {
channelType: string;
name: string;
}) {
return apiRequestData<{
channel: {
id: string;
channelType: string;
name: string;
enabled: boolean;
};
testResult: {
ok: boolean;
code: string;
response?: Record<string, unknown>;
};
}>(`/api/notifications/channels/test`, {
method: "POST",
body: JSON.stringify(payload),
});
}