feat: harden CMPP delivery and platform workflows
This commit is contained in:
+21
-3
@@ -1,4 +1,5 @@
|
||||
import { clearSession, dispatchSessionEvent, getSessionTenantId, hasRecentUserActivity, readSession, requestReauthentication, type LoginSession } from './session';
|
||||
import { assertUploadFileSize } from '@/utils/fileUpload';
|
||||
|
||||
type RequestOptions = RequestInit & {
|
||||
tenantId?: string;
|
||||
@@ -346,8 +347,10 @@ export type ClientSmsSignature = {
|
||||
};
|
||||
|
||||
export type ClientSmsSignatureView = Pick<ClientSmsSignature,
|
||||
'id' | 'tenantId' | 'applicationId' | 'name' | 'purpose' | 'auditStatus' | 'rejectReason' | 'createdAt' | 'updatedAt' | 'materials'
|
||||
'id' | 'tenantId' | 'applicationId' | 'name' | 'purpose' | 'auditStatus' | 'reportStatus' | 'rejectReason' | 'createdAt' | 'updatedAt' | 'materials'
|
||||
> & {
|
||||
pendingReport?: boolean;
|
||||
reportChangedAt?: string;
|
||||
application?: Pick<ClientSmsApplication, 'id' | 'name' | 'status'> | null;
|
||||
submittedMaterialCount: number;
|
||||
reportValues: Record<string, unknown>;
|
||||
@@ -474,6 +477,8 @@ export type SmsMessageRecord = {
|
||||
carrier?: string | null;
|
||||
province?: string | null;
|
||||
content: string;
|
||||
clientSrcId?: string | null;
|
||||
applicationExtension?: string | null;
|
||||
billingUnits: number;
|
||||
amountCents: number;
|
||||
status: string;
|
||||
@@ -794,6 +799,7 @@ export type RiskReviewTask = {
|
||||
rejectReason?: string | null;
|
||||
createdAt: string;
|
||||
reviewedAt?: string | null;
|
||||
reviewedBy?: { id: string; username: string; displayName: string } | null;
|
||||
riskHits?: Array<{ id: string; ruleName: string; reason: string }>;
|
||||
_count?: { messageRecords: number };
|
||||
};
|
||||
@@ -845,6 +851,7 @@ export type DailyReconciliationReport = {
|
||||
applicationName: string;
|
||||
sentUnits: number;
|
||||
successUnits: number;
|
||||
failedUnits: number;
|
||||
generatedAt: string;
|
||||
updatedAt: string;
|
||||
};
|
||||
@@ -861,7 +868,9 @@ export type DailyProfitReport = {
|
||||
channelId?: string | null;
|
||||
sentUnits: number;
|
||||
successUnits: number;
|
||||
failedUnits: number;
|
||||
revenueCents: number;
|
||||
refundCents: number;
|
||||
costCents: number;
|
||||
profitCents: number;
|
||||
profitRateBps: number;
|
||||
@@ -883,6 +892,7 @@ export type DailyQualityReport = {
|
||||
drainageInfoId?: string | null;
|
||||
sentUnits: number;
|
||||
successUnits: number;
|
||||
failedUnits: number;
|
||||
successRateBps: number;
|
||||
avgArrivalMs?: number | null;
|
||||
generatedAt: string;
|
||||
@@ -1351,6 +1361,7 @@ export const adminApi = {
|
||||
saveReportImportProfile: (body: Omit<ReportImportProfile, 'id'> & { id?: string }) =>
|
||||
request<ReportImportProfile>('/admin/report-materials/import-profiles', { method: 'POST', body: JSON.stringify(body) }),
|
||||
analyzeReportMaterialImport: (file: File, body: { tenantId: string; applicationId?: string; reportType: 'signature' | 'drainage'; sheetName?: string; headerRowCount?: number; dataStartRow?: number; profileId?: string }) => {
|
||||
assertUploadFileSize(file);
|
||||
const form = new FormData();
|
||||
form.set('file', file);
|
||||
Object.entries(body).forEach(([key, value]) => { if (value !== undefined) form.set(key, String(value)); });
|
||||
@@ -1433,6 +1444,7 @@ export const adminApi = {
|
||||
request<{ items: DictionaryItem[]; total: number; page: number; pageSize: number }>(withQuery('/admin/dictionaries/phone-carrier-rules', query)),
|
||||
createPhoneCarrierRule: (body: { carrier: string; pattern: string; priority?: number; status?: string; remark?: string }) =>
|
||||
request<DictionaryItem>('/admin/dictionaries/phone-carrier-rules', { method: 'POST', body: JSON.stringify(body) }),
|
||||
deletePhoneCarrierRule: (id: string) => request<DictionaryItem>(`/admin/dictionaries/phone-carrier-rules/${id}`, { method: 'DELETE' }),
|
||||
listDrainageFields: () => request<DictionaryItem[]>('/admin/dictionaries/drainage-fields'),
|
||||
createDrainageField: (body: { code: string; name: string; fieldType: 'string' | 'image' | 'file'; required?: boolean; status?: string; description?: string }) =>
|
||||
request<DictionaryItem>('/admin/dictionaries/drainage-fields', { method: 'POST', body: JSON.stringify(body) }),
|
||||
@@ -1442,6 +1454,7 @@ export const adminApi = {
|
||||
request<CommonReportField>('/admin/dictionaries/common-report-fields', { method: 'POST', body: JSON.stringify(body) }),
|
||||
deleteCommonReportField: (id: string) => request<CommonReportField>(`/admin/dictionaries/common-report-fields/${id}`, { method: 'DELETE' }),
|
||||
uploadFileObject: async (file: File, body: { purpose: string; prefix?: string }, tenantId?: string) => {
|
||||
assertUploadFileSize(file);
|
||||
const form = new FormData();
|
||||
form.set('file', file);
|
||||
form.set('purpose', body.purpose);
|
||||
@@ -1539,8 +1552,12 @@ export const clientApi = {
|
||||
request<SmsDrainageInfo>(`/client/drainage-infos/${id}`, { method: 'PUT', tenantId, body: JSON.stringify(body) }),
|
||||
changeDrainageInfoStatus: (id: string, status: string, tenantId = getSessionTenantId() ?? DEFAULT_CLIENT_TENANT_ID) =>
|
||||
request<SmsDrainageInfo>(`/client/drainage-infos/${id}/status`, { method: 'POST', tenantId, body: JSON.stringify({ status }) }),
|
||||
listTemplates: (query: { status?: string; keyword?: string } = {}, tenantId = getSessionTenantId() ?? DEFAULT_CLIENT_TENANT_ID) =>
|
||||
request<ClientSmsTemplate[]>(withQuery('/client/templates', query), { tenantId }),
|
||||
listTemplates: (query: { status?: string; keyword?: string; includeHistory?: boolean } = {}, tenantId = getSessionTenantId() ?? DEFAULT_CLIENT_TENANT_ID) =>
|
||||
request<ClientSmsTemplate[]>(withQuery('/client/templates', {
|
||||
status: query.status,
|
||||
keyword: query.keyword,
|
||||
includeHistory: query.includeHistory === undefined ? undefined : String(query.includeHistory),
|
||||
}), { tenantId }),
|
||||
createTemplate: (body: { tenantId?: string; applicationId: string; signatureId?: string; name: string; content: string; category?: string; variables?: Array<{ name: string; example?: string; required?: boolean }> }, tenantId = getSessionTenantId() ?? DEFAULT_CLIENT_TENANT_ID) =>
|
||||
request<ClientSmsTemplate>('/client/templates', { method: 'POST', tenantId, body: JSON.stringify({ ...body, tenantId }) }),
|
||||
updateTemplate: (id: string, body: { applicationId?: string; signatureId?: string | null; name?: string; content?: string; category?: string; auditStatus?: string; variables?: Array<{ name: string; example?: string; required?: boolean }> }, tenantId = getSessionTenantId() ?? DEFAULT_CLIENT_TENANT_ID) =>
|
||||
@@ -1568,6 +1585,7 @@ export const clientApi = {
|
||||
createFileObject: (body: { bucket?: string; objectKey: string; fileName: string; contentType: string; sizeBytes: number; purpose: string }, tenantId = getSessionTenantId() ?? DEFAULT_CLIENT_TENANT_ID) =>
|
||||
request<FileObject>('/admin/files', { method: 'POST', tenantId, body: JSON.stringify({ ...body, tenantId, bucket: body.bucket ?? 'cmpp-platform' }) }),
|
||||
uploadFileObject: async (file: File, body: { purpose: string; prefix?: string }, tenantId = getSessionTenantId() ?? DEFAULT_CLIENT_TENANT_ID) => {
|
||||
assertUploadFileSize(file);
|
||||
const form = new FormData();
|
||||
form.set('file', file);
|
||||
form.set('purpose', body.purpose);
|
||||
|
||||
Reference in New Issue
Block a user