import axios from 'axios' import { unwrapApiResponse } from '@/utils/api-response' import { request } from '@/utils/request' import type { ApiResponse, PageResult } from '@/types/common' import type { BackendCollection, BackendPage, BackendTemplateStatus, CreateTemplateRequest, CreateTemplateVersionRequest, DocumentListResponse, DocumentQuery, DocumentRecord, TemplateResponseDto, TemplateVersionResponseDto, } from './types' const useMockData = import.meta.env.VITE_USE_MOCK !== 'false' export const isDocumentMockEnabled = useMockData const mockRecords: DocumentRecord[] = [ { id: 'doc-001', name: '住院患者知情同意书', category: '住院', version: 'V2.1', status: 'published', updatedBy: '张文静', updatedAt: '2026-08-26 17:20', }, { id: 'doc-002', name: '急诊留观知情同意书', category: '急诊', version: 'V1.3', status: 'published', updatedBy: '张文静', updatedAt: '2026-08-25 10:15', }, ] function formatDateTime(value: string | null | undefined) { if (!value) { return '' } const date = new Date(value) if (Number.isNaN(date.getTime())) { return value .replace('T', ' ') .replace(/\.\d+Z$/, '') .replace(/Z$/, '') } const pad = (part: number) => String(part).padStart(2, '0') return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad( date.getHours(), )}:${pad(date.getMinutes())}` } function normalizeDocumentStatus(status: BackendTemplateStatus | null | undefined) { switch (status) { case 'PUBLISHED': case 'APPROVED': return 'published' as const case 'ARCHIVED': case 'DISABLED': return 'archived' as const case 'PENDING_REVIEW': case 'REJECTED': return 'review' as const case 'DRAFT': default: return 'draft' as const } } function mapDocument(dto: TemplateResponseDto): DocumentRecord { const versionId = dto.currentVersionId ?? undefined const version = dto.versionSequence ? `v${dto.versionSequence}` : '—' return { id: dto.id, name: dto.name, code: dto.templateCode, category: dto.category ?? '未分类', version: String(version), status: normalizeDocumentStatus(dto.status), backendStatus: dto.status ?? undefined, updatedBy: dto.updatedBy ?? dto.createdBy ?? '—', updatedAt: formatDateTime(dto.updatedAt ?? dto.createdAt), versionId, departmentId: dto.departmentId, departmentName: dto.departmentId ? undefined : '全院通用', campusId: dto.campusId, description: dto.description ?? '', } } function normalizePage( data: BackendPage | T[], fallbackPage: number, fallbackPageSize: number, ): PageResult { const records = Array.isArray(data) ? data : (data.records ?? data.items ?? data.content ?? []) const page = Array.isArray(data) ? fallbackPage : (data.page ?? fallbackPage) const pageSize = Array.isArray(data) ? fallbackPageSize : (data.size ?? fallbackPageSize) return { records, total: Array.isArray(data) ? records.length : (data.total ?? records.length), page, pageSize, } } function toBackendQuery(query: DocumentQuery) { const params: Record = { page: Math.max(query.page, 1), size: Math.min(Math.max(query.pageSize, 1), 200), } if (query.keyword?.trim()) { params.keyword = query.keyword.trim() } if (query.status && query.status !== 'all') { const statusMap: Record, string> = { draft: 'DRAFT', published: 'PUBLISHED', archived: 'ARCHIVED', review: 'PENDING_REVIEW', } params.status = statusMap[query.status] } if (query.campusId) { params.campusId = query.campusId } if (query.departmentId) { params.departmentId = query.departmentId } return params } function getRemoteDocuments(path: string, query: DocumentQuery): Promise { return request .get>>(path, { params: toBackendQuery(query), }) .then((response) => { const page = normalizePage(unwrapApiResponse(response), query.page, query.pageSize) return { ...page, records: page.records.map(mapDocument), } }) } export function getDocuments(query: DocumentQuery): Promise { if (!useMockData) { return getRemoteDocuments('/v1/templates', query) } const keyword = query.keyword?.trim().toLowerCase() const filteredRecords = mockRecords.filter((record) => { const matchesKeyword = !keyword || record.name.toLowerCase().includes(keyword) const matchesStatus = !query.status || query.status === 'all' || record.status === query.status return matchesKeyword && matchesStatus }) const page = Math.max(query.page, 1) const pageSize = Math.max(query.pageSize, 1) const start = (page - 1) * pageSize return Promise.resolve({ records: filteredRecords.slice(start, start + pageSize).map((record) => ({ ...record })), total: filteredRecords.length, page, pageSize, }) } export function getTemplatePermissionConfig(query: DocumentQuery): Promise { if (useMockData) { return getDocuments(query) } return getRemoteDocuments('/v1/templates/permission-config', query) } export async function getDocumentDetail(id: string): Promise { if (useMockData) { const record = mockRecords.find((item) => item.id === id) return record ? { ...record } : null } try { const response = await request.get>( `/v1/templates/${encodeURIComponent(id)}`, ) return mapDocument(unwrapApiResponse(response)) } catch (error) { if (axios.isAxiosError(error) && error.response?.status === 404) { return null } throw error } } export async function createTemplate(payload: CreateTemplateRequest): Promise { if (useMockData) { throw new Error('Mock 模式不执行模板写操作') } const response = await request.post>('/v1/templates', payload) return mapDocument(unwrapApiResponse(response)) } export async function createTemplateVersion( templateId: string, payload: CreateTemplateVersionRequest, ): Promise { if (useMockData) { throw new Error('Mock 模式不执行模板写操作') } const response = await request.post>( `/v1/templates/${encodeURIComponent(templateId)}/versions`, payload, ) return unwrapApiResponse(response) } export async function getTemplateVersions( templateId: string, ): Promise { if (useMockData) { return [] } const response = await request.get>>( `/v1/templates/${encodeURIComponent(templateId)}/versions`, ) return normalizePage(unwrapApiResponse(response), 1, 200).records } export async function getTemplateVersionDetail( templateId: string, versionId: string, ): Promise { if (useMockData) { return null } try { const response = await request.get>( `/v1/templates/${encodeURIComponent(templateId)}/versions/${encodeURIComponent(versionId)}`, ) return unwrapApiResponse(response) } catch (error) { if (axios.isAxiosError(error) && error.response?.status === 404) { return null } throw error } } export type TemplateVersionAction = 'submit-review' | 'reject' | 'approve' | 'publish' | 'disable' | 'archive' export function updateTemplateVersionStatus( templateId: string, versionId: string, action: TemplateVersionAction, reason?: string, ): Promise { if (useMockData) { return Promise.reject(new Error('Mock 模式不执行模板版本工作流')) } const requestData = action === 'reject' && reason?.trim() ? { comment: reason.trim() } : undefined return request .post>( `/v1/templates/${encodeURIComponent(templateId)}/versions/${encodeURIComponent(versionId)}/${action}`, requestData, ) .then(unwrapApiResponse) }