feat: complete reporting and filing workflows

This commit is contained in:
hectorzhao
2026-07-28 20:28:47 +08:00
parent 352a6293b4
commit 99c8c7c68b
52 changed files with 3490 additions and 376 deletions
+157 -5
View File
@@ -229,9 +229,24 @@ export type EnterpriseCertification = {
rejectReason?: string | null;
submittedAt: string;
reviewedAt?: string | null;
reviewer?: { id: string; username: string; displayName: string } | null;
tenant?: { id: string; name: string; code: string };
};
export type AuditRecord = {
id: string;
tenantId?: string | null;
targetType: string;
targetId: string;
action: string;
statusBefore?: string | null;
statusAfter: string;
reason?: string | null;
reviewerId?: string | null;
reviewer?: { id: string; username: string; displayName: string } | null;
createdAt: string;
};
export type SmsTemplateAudit = {
id: string;
tenantId: string;
@@ -384,6 +399,47 @@ export type SignatureQualityStat = {
averageArrivalMs?: number | null;
};
export type SignatureChannelCarrierQualityStat = {
signatureId: string;
channelId: string;
channelName: string;
carrier: string;
total: number;
acceptedCount: number;
submitFailureCount: number;
successCount: number;
unknownCount: number;
failureCount: number;
successRate: number;
averageArrivalMs?: number | null;
};
export type SignatureChannelQualityItem = {
signatureId: string;
signatureName: string;
tenantId: string;
tenantName: string;
applicationNames?: string | null;
total: number;
acceptedCount: number;
submitFailureCount: number;
successCount: number;
unknownCount: number;
failureCount: number;
successRate: number;
averageArrivalMs?: number | null;
channelSubmitTotal: number;
breakdowns: SignatureChannelCarrierQualityStat[];
};
export type SignatureChannelQualityResponse = {
date: string;
items: SignatureChannelQualityItem[];
total: number;
page: number;
pageSize: number;
};
export type DailySendSummary = {
total: number;
successCount: number;
@@ -828,6 +884,61 @@ export type ReportMaterialPendingItem = {
application?: ClientSmsApplication | null;
};
export type PagedResult<T> = {
items: T[];
total: number;
page: number;
pageSize: number;
};
export type ReportMaterialBatch = {
id: string;
batchNo: string;
status: string;
selectedCount: number;
channelCount: number;
fileCount: number;
reportTotal: number;
successCount: number;
successRate: number;
createdAt: string;
completedAt?: string | null;
exportFiles: Array<{ id: string; fileObjectId?: string | null; fileName: string; rowCount: number; channelId?: string | null }>;
};
export type ReportImportReviewItem = {
id: string;
rowNumber: number;
reportType: 'signature' | 'drainage';
operation: 'create' | 'update' | 'invalid';
targetId?: string | null;
status: string;
payload: Record<string, unknown>;
originalSnapshot?: Record<string, unknown> | null;
errorMessage?: string | null;
reviewReason?: string | null;
reviewedAt?: string | null;
reviewer?: { id: string; username: string; displayName: string } | null;
};
export type ReportImportReviewBatch = {
id: string;
tenantId: string;
applicationId?: string | null;
fileName: string;
reportType: 'signature' | 'drainage';
status: string;
rowCount: number;
successCount: number;
failedCount: number;
createdAt: string;
reviewedAt?: string | null;
tenant?: { id: string; name: string } | null;
application?: { id: string; name: string } | null;
reviewer?: { id: string; username: string; displayName: string } | null;
items: ReportImportReviewItem[];
};
export type ReportMaterialPreflightTarget = {
id: string;
name: string;
@@ -925,9 +1036,33 @@ export type ReportTask = DictionaryItem & {
reportType?: 'signature' | 'drainage';
drainageItemId?: string | null;
status: string;
signature?: { id: string; name: string; purpose?: string | null; drainageInfo?: Record<string, unknown> | null };
signature?: {
id: string;
name: string;
purpose?: string | null;
drainageInfo?: Record<string, unknown> | null;
tenant?: { id: string; name: string };
application?: { id: string; name: string } | null;
};
drainageInfo?: SmsDrainageInfo | null;
channel?: { id: string; name: string; code: string };
reason?: string | null;
createdAt?: string;
updatedAt?: string;
exportItems?: Array<{
id: string;
rowNumber: number;
exportFile: { id: string; fileObjectId?: string | null; fileName: string; rowCount: number; batchId?: string | null };
batchItem: { id: string; materialVersion: number; batch: { id: string; batchNo: string; createdAt: string } };
}>;
records?: Array<{
id: string;
action: string;
statusBefore?: string | null;
statusAfter: string;
reason?: string | null;
createdAt: string;
}>;
deliveryStats?: {
total: number;
acceptedCount: number;
@@ -1001,6 +1136,8 @@ export type RiskReviewTask = {
rejectReason?: string | null;
createdAt: string;
reviewedAt?: string | null;
tenant?: { id: string; name: string } | null;
application?: { id: string; name: string } | null;
reviewedBy?: { id: string; username: string; displayName: string } | null;
riskHits?: Array<{ id: string; ruleName: string; reason: string }>;
_count?: { messageRecords: number };
@@ -1036,6 +1173,8 @@ export type RiskTaskMessagePage = {
pageSize: number;
};
export type BatchTaskMessagePage = RiskTaskMessagePage;
export type TenantAccount = {
id: string;
tenantId: string;
@@ -1483,6 +1622,8 @@ export type DownstreamRecoveryStatusExportQuery = {
state?: string;
failureCategory?: string;
keyword?: string;
updatedAtFrom?: string;
updatedAtTo?: string;
};
function withQuery(path: string, query: Record<string, string | number | undefined>) {
@@ -1539,6 +1680,8 @@ export const adminApi = {
request<ManagedUser>(`/admin/users/${id}/password`, { method: 'POST', body: JSON.stringify({ password, operatorId }) }),
getDashboard: (tenantId?: string) => request<DashboardResponse>(withQuery('/admin/operations/dashboard/statistics', { tenantId })),
getSendQuality: (date?: string) => request<SendQualityResponse>(withQuery('/admin/operations/send-quality', { date })),
getSignatureQuality: (query: { date?: string; keyword?: string; page?: number; pageSize?: number } = {}) =>
request<SignatureChannelQualityResponse>(withQuery('/admin/operations/signature-quality', query)),
listSystemLogs: (query: { tenantId?: string; keyword?: string; level?: string; module?: string; range?: string; page?: number; pageSize?: number }) =>
request<OperationLogResponse>(withQuery('/admin/system-logs', query)),
listProtocolInteractionLogs: (query: { protocol?: string; direction?: string; eventType?: string; status?: string; keyword?: string; range?: string; page?: number; pageSize?: number }) =>
@@ -1655,6 +1798,8 @@ export const adminApi = {
request<ClientSmsSignature>(`/admin/enterprise-signatures/${id}/status`, { method: 'POST', body: JSON.stringify({ status, reason }) }),
listDrainageInfos: (query: { tenantId?: string; signatureId?: string; keyword?: string; status?: string } = {}) =>
request<SmsDrainageInfo[]>(withQuery('/admin/drainage-infos', query)),
listAuditRecords: (query: { targetType?: string; targetId?: string } = {}) =>
request<AuditRecord[]>(withQuery('/admin/audit-records', query)),
createDrainageInfo: (signatureId: string, body: { siteName: string; url: string; remark?: string; reportValues?: Record<string, unknown> }) =>
request<SmsDrainageInfo>(`/admin/enterprise-signatures/${signatureId}/drainage-infos`, { method: 'POST', body: JSON.stringify(body) }),
updateDrainageInfo: (id: string, body: { siteName?: string; url?: string; remark?: string; reportValues?: Record<string, unknown> }) =>
@@ -1709,8 +1854,8 @@ export const adminApi = {
request<ChannelReportField>('/admin/channel-report-fields', { method: 'POST', body: JSON.stringify(body) }),
replaceChannelReportFields: (channelId: string, reportType: 'signature' | 'drainage', fields: Array<Record<string, unknown>>) =>
request<ChannelReportField[]>(`/admin/channels/${channelId}/report-fields/${reportType}`, { method: 'PUT', body: JSON.stringify({ fields }) }),
listPendingReportMaterials: (query: { reportType?: 'signature' | 'drainage'; tenantId?: string; applicationId?: string } = {}) =>
request<ReportMaterialPendingItem[]>(withQuery('/admin/report-materials/pending', query)),
listPendingReportMaterials: (query: { reportType?: 'signature' | 'drainage'; tenantId?: string; applicationId?: string; keyword?: string; startAt?: string; endAt?: string; page?: number; pageSize?: number } = {}) =>
request<PagedResult<ReportMaterialPendingItem>>(withQuery('/admin/report-materials/pending', query)),
listReportImportProfiles: (reportType?: 'signature' | 'drainage') =>
request<ReportImportProfile[]>(withQuery('/admin/report-materials/import-profiles', { reportType })),
saveReportImportProfile: (body: Omit<ReportImportProfile, 'id'> & { id?: string }) =>
@@ -1724,7 +1869,12 @@ export const adminApi = {
},
commitReportMaterialImport: (id: string, body: { mappings: ReportImportMapping[]; profile?: Omit<ReportImportProfile, 'id'> & { id?: string } }) =>
request<Record<string, unknown>>(`/admin/report-materials/imports/${id}/commit`, { method: 'PUT', body: JSON.stringify(body) }),
listReportMaterialBatches: () => request<Array<Record<string, unknown>>>('/admin/report-materials/batches'),
listReportImportReviewBatches: (query: { reportType?: 'signature' | 'drainage'; status?: string; keyword?: string; startAt?: string; endAt?: string; page?: number; pageSize?: number } = {}) =>
request<PagedResult<ReportImportReviewBatch>>(withQuery('/admin/report-materials/imports/review-batches', query)),
reviewReportImportItems: (id: string, body: { decision: 'approve' | 'reject'; itemIds?: string[]; reason?: string }) =>
request<{ batchId: string; status: string; approvedCount: number; rejectedCount: number; failedCount: number }>(`/admin/report-materials/imports/${id}/review`, { method: 'POST', body: JSON.stringify(body) }),
listReportMaterialBatches: (query: { keyword?: string; startAt?: string; endAt?: string; page?: number; pageSize?: number } = {}) =>
request<PagedResult<ReportMaterialBatch>>(withQuery('/admin/report-materials/batches', query)),
preflightReportMaterialBatch: (body: { items: Array<{ reportType: 'signature' | 'drainage'; signatureId: string; drainageItemId?: string; materialVersion?: number }> }) =>
request<ReportMaterialBatchPreflight>('/admin/report-materials/batches/preflight', { method: 'POST', body: JSON.stringify(body) }),
createReportMaterialBatch: (body: { idempotencyKey: string; items: Array<{ reportType: 'signature' | 'drainage'; signatureId: string; drainageItemId?: string; materialVersion: number }> }) =>
@@ -1741,6 +1891,8 @@ export const adminApi = {
listReportRecords: (query: { taskId?: string; channelId?: string } = {}) => request<ReportRecord[]>(withQuery('/admin/report-records', query)),
listAdminBatchTasks: (query: { tenantId?: string; status?: string } = {}) =>
request<SmsBatchTask[]>(withQuery('/admin/send/batch-tasks', query)),
listAdminBatchTaskMessages: (id: string, query: { phone?: string; page?: number; pageSize?: number } = {}) =>
request<BatchTaskMessagePage>(withQuery(`/admin/send/batch-tasks/${id}/messages`, query)),
terminateAdminBatchTask: (id: string) =>
request<SmsBatchTask>(`/admin/send/batch-tasks/${id}/terminate`, { method: 'POST', body: JSON.stringify({}) }),
listAdminMessages: (query: { tenantId?: string; applicationId?: string; channelId?: string; taskId?: string; phoneNumber?: string; status?: string } = {}) =>
@@ -1761,7 +1913,7 @@ export const adminApi = {
listStatistics: (query: { tenantId?: string; groupBy?: string } = {}) => request<Array<Record<string, unknown>>>(withQuery('/admin/operations/statistics', query)),
getDownstreamDeliveryDashboard: (query: { tenantId?: string; applicationId?: string; deliveryType?: string; createdAtFrom?: string; createdAtTo?: string } = {}) =>
request<DownstreamDeliveryDashboard>(withQuery('/admin/operations/downstream-deliveries/dashboard', query)),
listDownstreamRecoveryStatuses: (query: { tenantId?: string; applicationId?: string; state?: string; failureCategory?: string; keyword?: string; page?: number; pageSize?: number } = {}) =>
listDownstreamRecoveryStatuses: (query: { tenantId?: string; applicationId?: string; state?: string; failureCategory?: string; keyword?: string; updatedAtFrom?: string; updatedAtTo?: string; page?: number; pageSize?: number } = {}) =>
request<DownstreamRecoveryStatusResponse>(withQuery('/admin/operations/downstream-recovery-statuses', query)),
getDownstreamRecoveryStatus: (id: string) =>
request<GatewayDownstreamRecoveryStatus>(`/admin/operations/downstream-recovery-statuses/${id}`),