import type { AxiosResponse } from 'axios'; import { request } from '@/utils/request'; import { commandHeaders } from './command'; export const ARCHIVE_API_BASE = '/archives'; const array = (value: T[] | null | undefined): T[] => (Array.isArray(value) ? value : []); export interface ArchivePageMeta { page: number; size: number; totalElements: number; totalPages: number; } export interface ArchiveReference { publicId: string; businessNo: string; name: string; } export interface ArchivePackageItem { publicId: string; itemType: string; itemName: string; objectType?: string | null; objectPublicId?: string | null; filePublicId?: string | null; fileName?: string | null; fileSha256?: string | null; required: boolean; status: string; missingReason?: string | null; returnReason?: string | null; notApplicableReason?: string | null; notApplicableRequestedByName?: string | null; notApplicableRequestedAt?: string | null; notApplicableReviewedByName?: string | null; notApplicableReviewedAt?: string | null; notApplicableReviewOpinion?: string | null; allowedActions: string[]; } export interface ArchivePackageAction { sequenceNo: number; actionCode: string; fromStatus?: string | null; toStatus?: string | null; opinion?: string | null; actorName?: string | null; occurredAt: string; manifestSha256?: string | null; } export interface ArchivePackageObjectSnapshot { publicId: string; itemType: string; objectType: string; objectPublicId: string; objectBusinessNo?: string | null; objectName?: string | null; objectStatus: string; objectVersion?: number | null; objectFormType?: string | null; sourceVersionPublicId?: string | null; snapshotSha256: string; capturedAt: string; } export interface ArchivePackage { publicId: string; businessNo: string; company: ArchiveReference; project: ArchiveReference; packageVersion: number; rootPackagePublicId?: string | null; supersedesPackagePublicId?: string | null; revisionReason?: string | null; ruleVersion: string; completeness: string; missingCount: number; retentionYears: number; retentionExpiresOn?: string | null; retentionStatus: 'PENDING' | 'ACTIVE' | 'DUE_SOON' | 'EXPIRED' | 'FROZEN'; physicalLocation?: string | null; frozen: boolean; status: string; manifestSha256?: string | null; submittedByName?: string | null; reviewedByName?: string | null; submittedAt?: string | null; archivedAt?: string | null; updatedAt: string; version: number; allowedActions: string[]; items: ArchivePackageItem[]; objectSnapshots: ArchivePackageObjectSnapshot[]; actions: ArchivePackageAction[]; } export interface ArchiveFile { publicId: string; displayName: string; mediaType: string; originalType?: string | null; sizeBytes: number; sha256: string; scanStatus: string; companyPublicId: string; companyName: string; projectPublicId: string; projectBusinessNo: string; projectName: string; contractPublicId?: string | null; counterpartyPublicId?: string | null; formType?: string | null; documentType: string; seriesPublicId: string; versionNo: number; archiveStatus: string; borrowStatus?: string | null; packagePublicId?: string | null; packageStatus?: string | null; uploadedBy: string; uploadedAt: string; sensitive: boolean; allowedActions: string[]; } export interface ArchiveFileDetail { file: ArchiveFile; versions: ArchiveFile[]; borrows: ArchiveBorrow[]; } export interface ArchiveBorrow { publicId: string; businessNo: string; filePublicId: string; fileName: string; projectPublicId: string; projectName: string; purpose: string; dueAt: string; status: string; applicantName: string; approvedByName?: string | null; approvedAt?: string | null; reviewOpinion?: string | null; returnCondition?: string | null; returnedByName?: string | null; returnedAt?: string | null; version: number; allowedActions: string[]; } export interface ArchivePackageQuery { view?: 'prepare' | 'check' | 'returned' | 'archived'; companyId?: string; projectId?: string; status?: string; completeness?: string; frozen?: boolean; keyword?: string; sort?: string; page: number; size: number; } export interface ArchiveFileQuery { companyId?: string; projectId?: string; contractId?: string; counterpartyId?: string; formType?: string; fileName?: string; uploadedBy?: string; uploadedDateFrom?: string; uploadedDateTo?: string; originalType?: string; archiveStatus?: string; scanStatus?: string; sort?: string; page: number; size: number; } export interface ArchiveBorrowQuery { status?: string; keyword?: string; page: number; size: number; } export interface ArchivePackageCreatePayload { projectId: string; retentionYears?: number; physicalLocation?: string; } export interface ArchivePackageCommandPayload { version: number; opinion?: string; returnItemIds?: string[]; } export interface ArchiveFreezePayload { version: number; reason: string; } export interface ArchivePackageRevisionPayload { version: number; reason: string; } export interface ArchiveNotApplicablePayload { version: number; reason: string; } export interface ArchiveNotApplicableReviewPayload { version: number; approved: boolean; opinion: string; } export interface ArchiveFileMetadata { projectId: string; packageId?: string; packageItemId?: string; contractId?: string; counterpartyId?: string; formType?: string; fileType: string; originalType?: string; replacesFileId?: string; replacementReason?: string; notApplicableReason?: string; } export interface ArchiveBorrowCreatePayload { fileId: string; purpose: string; dueAt: string; } export interface ArchiveBorrowCommandPayload { version: number; opinion?: string; returnCondition?: string; } interface PageEnvelope { data: T[]; meta: ArchivePageMeta; requestId: string; } export function normalizeArchivePackageItem(item: Partial | null | undefined): ArchivePackageItem { return { ...(item || {}), allowedActions: array(item?.allowedActions) } as ArchivePackageItem; } export function normalizeArchivePackage(value: Partial | null | undefined): ArchivePackage { const item = value || {}; return { ...item, allowedActions: array(item.allowedActions), items: array(item.items).map(normalizeArchivePackageItem), objectSnapshots: array(item.objectSnapshots), actions: array(item.actions), } as ArchivePackage; } export function normalizeArchiveFile(value: Partial | null | undefined): ArchiveFile { return { ...(value || {}), allowedActions: array(value?.allowedActions) } as ArchiveFile; } export function normalizeArchiveBorrow(value: Partial | null | undefined): ArchiveBorrow { return { ...(value || {}), allowedActions: array(value?.allowedActions) } as ArchiveBorrow; } export function normalizeArchiveFileDetail(value: Partial | null | undefined): ArchiveFileDetail { return { ...(value || {}), file: normalizeArchiveFile(value?.file), versions: array(value?.versions).map(normalizeArchiveFile), borrows: array(value?.borrows).map(normalizeArchiveBorrow), } as ArchiveFileDetail; } export function archiveErrorText(error: unknown, fallback: string): string { const value = error as Error & { status?: number; code?: string; fieldErrors?: Record }; const fields = value.fieldErrors ? Object.values(value.fieldErrors).join(';') : ''; if (value.status === 401) return '登录会话已失效,请重新登录后重试。'; if (value.status === 403) return '当前身份没有访问该数据或执行该操作的权限。'; if (value.status === 404) return '记录不存在、已被删除或当前身份无权查看。'; if (value.code === 'VERSION_CONFLICT' || value.status === 409) return value.message || '数据已发生变化,请刷新后重试。'; if (value.status === 422) { return fields ? `${value.message || '当前条件不满足业务规则'}:${fields}` : value.message || '当前条件不满足业务规则,请检查后重试。'; } if (value.status && value.status >= 500) return value.message || '服务处理失败,请稍后重试。'; return fields ? `${value.message || fallback}:${fields}` : value.message || fallback; } async function page(url: string, params: Record) { const envelope = await request.get>({ url, params }, { isTransformResponse: false }); return { items: array(envelope.data), meta: envelope.meta || { page: Number(params.page) || 1, size: Number(params.size) || 20, totalElements: 0, totalPages: 0, }, requestId: envelope.requestId, }; } export function getArchivePackages(params: ArchivePackageQuery) { return page(`${ARCHIVE_API_BASE}/packages`, params as unknown as Record).then( (result) => ({ ...result, items: result.items.map(normalizeArchivePackage), }), ); } export function getArchivePackage(publicId: string) { return request.get({ url: `${ARCHIVE_API_BASE}/packages/${publicId}` }).then(normalizeArchivePackage); } export function createArchivePackage(payload: ArchivePackageCreatePayload, idempotencyKey?: string) { return request .post({ url: `${ARCHIVE_API_BASE}/packages`, data: payload, ...commandHeaders('archive-package-create', idempotencyKey), }) .then(normalizeArchivePackage); } export function checkArchivePackage(publicId: string, payload: ArchivePackageCommandPayload, idempotencyKey?: string) { return request .post({ url: `${ARCHIVE_API_BASE}/packages/${publicId}/check`, data: payload, ...commandHeaders('archive-package-check', idempotencyKey), }) .then(normalizeArchivePackage); } export function submitArchivePackage(publicId: string, payload: ArchivePackageCommandPayload, idempotencyKey?: string) { return request .post({ url: `${ARCHIVE_API_BASE}/packages/${publicId}/submit`, data: payload, ...commandHeaders('archive-package-submit', idempotencyKey), }) .then(normalizeArchivePackage); } export function returnArchivePackage(publicId: string, payload: ArchivePackageCommandPayload, idempotencyKey?: string) { return request .post({ url: `${ARCHIVE_API_BASE}/packages/${publicId}/return`, data: payload, ...commandHeaders('archive-package-return', idempotencyKey), }) .then(normalizeArchivePackage); } export function reviseArchivePackage( publicId: string, payload: ArchivePackageRevisionPayload, idempotencyKey?: string, ) { return request .post({ url: `${ARCHIVE_API_BASE}/packages/${publicId}/revise`, data: payload, ...commandHeaders('archive-package-revise', idempotencyKey), }) .then(normalizeArchivePackage); } export function archiveArchivePackage( publicId: string, payload: ArchivePackageCommandPayload, idempotencyKey?: string, ) { return request .post({ url: `${ARCHIVE_API_BASE}/packages/${publicId}/archive`, data: payload, ...commandHeaders('archive-package-archive', idempotencyKey), }) .then(normalizeArchivePackage); } export function freezeArchivePackage(publicId: string, payload: ArchiveFreezePayload, idempotencyKey?: string) { return request .post({ url: `${ARCHIVE_API_BASE}/packages/${publicId}/freeze`, data: payload, ...commandHeaders('archive-package-freeze', idempotencyKey), }) .then(normalizeArchivePackage); } export function unfreezeArchivePackage( publicId: string, payload: ArchivePackageCommandPayload, idempotencyKey?: string, ) { return request .post({ url: `${ARCHIVE_API_BASE}/packages/${publicId}/unfreeze`, data: payload, ...commandHeaders('archive-package-unfreeze', idempotencyKey), }) .then(normalizeArchivePackage); } export function markArchiveItemNotApplicable( packageId: string, itemId: string, payload: ArchiveNotApplicablePayload, idempotencyKey?: string, ) { return request .post({ url: `${ARCHIVE_API_BASE}/packages/${packageId}/items/${itemId}/not-applicable`, data: payload, ...commandHeaders('archive-item-not-applicable', idempotencyKey), }) .then(normalizeArchivePackage); } export function reviewArchiveItemNotApplicable( packageId: string, itemId: string, payload: ArchiveNotApplicableReviewPayload, idempotencyKey?: string, ) { return request .post({ url: `${ARCHIVE_API_BASE}/packages/${packageId}/items/${itemId}/not-applicable/review`, data: payload, ...commandHeaders('archive-item-not-applicable-review', idempotencyKey), }) .then(normalizeArchivePackage); } export function getArchiveFiles(params: ArchiveFileQuery) { return page(`${ARCHIVE_API_BASE}/files`, params as unknown as Record).then( (result) => ({ ...result, items: result.items.map(normalizeArchiveFile), }), ); } export function getArchiveFile(publicId: string) { return request .get({ url: `${ARCHIVE_API_BASE}/files/${publicId}` }) .then(normalizeArchiveFileDetail); } export function rescanArchiveFile(publicId: string, idempotencyKey?: string) { return request .post({ url: `${ARCHIVE_API_BASE}/files/${publicId}/rescan`, data: {}, ...commandHeaders('archive-file-rescan', idempotencyKey), }) .then(normalizeArchiveFile); } export function getArchiveFileContent(publicId: string, preview = false) { return request.get>( { url: `${ARCHIVE_API_BASE}/files/${publicId}/content`, params: { preview }, responseType: 'blob' }, { isReturnNativeResponse: true }, ); } export function uploadArchiveFile(metadata: ArchiveFileMetadata, file: File, idempotencyKey?: string) { const formData = new FormData(); formData.append('metadata', new Blob([JSON.stringify(metadata)], { type: 'application/json' })); formData.append('file', file, file.name); return request .post({ url: `${ARCHIVE_API_BASE}/files`, data: formData, headers: { 'Content-Type': 'multipart/form-data', ...commandHeaders('archive-file-upload', idempotencyKey).headers, }, }) .then(normalizeArchiveFile); } export function getArchiveBorrows(params: ArchiveBorrowQuery) { return page(`${ARCHIVE_API_BASE}/borrows`, params as unknown as Record).then( (result) => ({ ...result, items: result.items.map(normalizeArchiveBorrow), }), ); } export function createArchiveBorrow(payload: ArchiveBorrowCreatePayload, idempotencyKey?: string) { return request .post({ url: `${ARCHIVE_API_BASE}/borrows`, data: payload, ...commandHeaders('archive-borrow-create', idempotencyKey), }) .then(normalizeArchiveBorrow); } export function approveArchiveBorrow(publicId: string, payload: ArchiveBorrowCommandPayload, idempotencyKey?: string) { return request .post({ url: `${ARCHIVE_API_BASE}/borrows/${publicId}/approve`, data: payload, ...commandHeaders('archive-borrow-approve', idempotencyKey), }) .then(normalizeArchiveBorrow); } export function rejectArchiveBorrow(publicId: string, payload: ArchiveBorrowCommandPayload, idempotencyKey?: string) { return request .post({ url: `${ARCHIVE_API_BASE}/borrows/${publicId}/reject`, data: payload, ...commandHeaders('archive-borrow-reject', idempotencyKey), }) .then(normalizeArchiveBorrow); } export function returnArchiveBorrow(publicId: string, payload: ArchiveBorrowCommandPayload, idempotencyKey?: string) { return request .post({ url: `${ARCHIVE_API_BASE}/borrows/${publicId}/return`, data: payload, ...commandHeaders('archive-borrow-return', idempotencyKey), }) .then(normalizeArchiveBorrow); }