feat(api): 接入医签通业务接口与类型

This commit is contained in:
yelan
2026-09-01 10:11:49 +08:00
parent c34ef6f79a
commit 2e2d2250dd
12 changed files with 1677 additions and 56 deletions
+229 -10
View File
@@ -1,9 +1,26 @@
import { request } from '@/utils/request'
import axios from 'axios'
import type { DocumentListResponse, DocumentQuery, DocumentRecord } from './types'
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',
@@ -25,11 +42,119 @@ const mockRecords: DocumentRecord[] = [
},
]
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<T>(
data: BackendPage<T> | T[],
fallbackPage: number,
fallbackPageSize: number,
): PageResult<T> {
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<string, string | number> = {
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<Exclude<DocumentQuery['status'], undefined | 'all'>, string> = {
draft: 'DRAFT',
published: 'PUBLISHED',
archived: 'ARCHIVED',
review: 'PENDING_REVIEW',
}
params.status = statusMap[query.status]
}
return params
}
export function getDocuments(query: DocumentQuery): Promise<DocumentListResponse> {
if (!useMockData) {
return request.get<DocumentListResponse>('/management/documents', {
params: query,
})
return request
.get<ApiResponse<BackendCollection<TemplateResponseDto>>>('/v1/templates', {
params: toBackendQuery(query),
})
.then((response) => {
const page = normalizePage(unwrapApiResponse(response), query.page, query.pageSize)
return {
...page,
records: page.records.map(mapDocument),
}
})
}
const keyword = query.keyword?.trim().toLowerCase()
@@ -44,17 +169,111 @@ export function getDocuments(query: DocumentQuery): Promise<DocumentListResponse
const start = (page - 1) * pageSize
return Promise.resolve({
records: filteredRecords.slice(start, start + pageSize),
records: filteredRecords.slice(start, start + pageSize).map((record) => ({ ...record })),
total: filteredRecords.length,
page,
pageSize,
})
}
export function getDocumentDetail(id: string): Promise<DocumentRecord | null> {
if (!useMockData) {
return request.get<DocumentRecord>('/management/documents/' + id)
export async function getDocumentDetail(id: string): Promise<DocumentRecord | null> {
if (useMockData) {
const record = mockRecords.find((item) => item.id === id)
return record ? { ...record } : null
}
return Promise.resolve(mockRecords.find((record) => record.id === id) ?? null)
try {
const response = await request.get<ApiResponse<TemplateResponseDto>>(
`/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<DocumentRecord> {
if (useMockData) {
throw new Error('Mock 模式不执行模板写操作')
}
const response = await request.post<ApiResponse<TemplateResponseDto>>('/v1/templates', payload)
return mapDocument(unwrapApiResponse(response))
}
export async function createTemplateVersion(
templateId: string,
payload: CreateTemplateVersionRequest,
): Promise<TemplateVersionResponseDto> {
if (useMockData) {
throw new Error('Mock 模式不执行模板写操作')
}
const response = await request.post<ApiResponse<TemplateVersionResponseDto>>(
`/v1/templates/${encodeURIComponent(templateId)}/versions`,
payload,
)
return unwrapApiResponse(response)
}
export async function getTemplateVersions(
templateId: string,
): Promise<TemplateVersionResponseDto[]> {
if (useMockData) {
return []
}
const response = await request.get<ApiResponse<BackendCollection<TemplateVersionResponseDto>>>(
`/v1/templates/${encodeURIComponent(templateId)}/versions`,
)
return normalizePage(unwrapApiResponse(response), 1, 200).records
}
export async function getTemplateVersionDetail(
templateId: string,
versionId: string,
): Promise<TemplateVersionResponseDto | null> {
if (useMockData) {
return null
}
try {
const response = await request.get<ApiResponse<TemplateVersionResponseDto>>(
`/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<TemplateVersionResponseDto> {
if (useMockData) {
return Promise.reject(new Error('Mock 模式不执行模板版本工作流'))
}
const requestData = action === 'reject' && reason?.trim() ? { comment: reason.trim() } : undefined
return request
.post<ApiResponse<TemplateVersionResponseDto>>(
`/v1/templates/${encodeURIComponent(templateId)}/versions/${encodeURIComponent(versionId)}/${action}`,
requestData,
)
.then(unwrapApiResponse)
}