'use client' import { chunkUpload } from '@/lib/chunk-upload' import { preparePerformanceSurveyImagesInBrowser } from '@/lib/performance-survey-image-browser' import type { WorkReviewBoardData, WorkReviewProcessedImage, WorkReviewProject, WorkReviewReviewerOption, WorkReviewReviewInput, WorkReviewStatus, WorkReviewSubmissionInput, } from '@/types/work-review' const API_BASE = '/api/work-review' export class WorkReviewRequestError extends Error { status: number constructor(message: string, status: number) { super(message) this.status = status } } async function workReviewRequest(path: string, options?: RequestInit): Promise { let response: Response try { response = await fetch(`${API_BASE}${path}`, { ...options, cache: options?.method && options.method !== 'GET' ? options.cache : 'no-store', headers: { ...(options?.body instanceof FormData ? {} : { 'Content-Type': 'application/json' }), ...(options?.headers || {}), }, }) } catch (error) { if (error instanceof DOMException && error.name === 'AbortError') throw error throw new WorkReviewRequestError('网络连接失败,请检查网络后重试', 0) } const contentType = response.headers.get('content-type') || '' const payload = contentType.includes('application/json') ? await response.json().catch(() => null) : await response.text().catch(() => '') if (!response.ok || (payload && typeof payload === 'object' && payload.success === false)) { const message = payload && typeof payload === 'object' ? String(payload.error || payload.message || '请求失败') : String(payload || '请求失败') throw new WorkReviewRequestError(message, response.status) } return (payload && typeof payload === 'object' && 'data' in payload ? payload.data : payload) as T } export function getWorkReviewBoard( input: { query?: string status?: WorkReviewStatus | '' project?: string page?: number pageSize?: number }, signal?: AbortSignal, ) { const params = new URLSearchParams() if (input.query?.trim()) params.set('query', input.query.trim()) if (input.status) params.set('status', input.status) if (input.project?.trim()) params.set('project', input.project.trim()) params.set('page', String(input.page || 1)) params.set('pageSize', String(input.pageSize || 20)) return workReviewRequest(`?${params.toString()}`, { signal }) } export function getWorkReviewReviewerOptions(signal?: AbortSignal) { return workReviewRequest('/reviewers', { signal }) } export function createWorkReviewProject(input: WorkReviewSubmissionInput) { return workReviewRequest('/projects', { method: 'POST', body: JSON.stringify(input), }) } export function createWorkReviewVersion(projectId: string, input: WorkReviewSubmissionInput) { return workReviewRequest(`/projects/${encodeURIComponent(projectId)}/versions`, { method: 'POST', body: JSON.stringify(input), }) } export function deleteWorkReviewProject(projectId: string) { return workReviewRequest<{ id: string; title: string }>(`/projects/${encodeURIComponent(projectId)}`, { method: 'DELETE', }) } export function submitWorkReview(versionId: string, input: WorkReviewReviewInput) { return workReviewRequest(`/versions/${encodeURIComponent(versionId)}/reviews`, { method: 'POST', body: JSON.stringify(input), }) } export async function uploadWorkReviewImages( files: File[], onProgress?: (message: string, percent?: number) => void, ): Promise { if (files.length === 0) throw new Error('请至少选择 1 张作品图片') const processed = await preparePerformanceSurveyImagesInBrowser( files, (message) => { onProgress?.(message) }, { profile: 'work_review' }, ) const uploaded: WorkReviewProcessedImage[] = [] for (let index = 0; index < processed.length; index += 1) { const image = processed[index] onProgress?.(`正在上传图片 ${index + 1}/${processed.length}`, Math.round((index / processed.length) * 100)) const result = await chunkUpload( image.file, { materialType: '作品过稿看板', fileName: image.file.name, }, { skipComplete: true, onProgress: (fileProgress) => { const totalProgress = ((index + fileProgress / 100) / processed.length) * 100 onProgress?.(`正在上传图片 ${index + 1}/${processed.length}`, Math.round(totalProgress)) }, }, ) if (!result?.uploadId) throw new Error(`图片「${image.sourceName}」上传失败,请重试`) uploaded.push({ uploadId: result.uploadId, mimeType: image.mimeType, width: image.width, height: image.height, kind: image.kind, sourceName: image.sourceName, sourceIndex: image.sourceIndex, sliceIndex: image.sliceIndex, sliceCount: image.sliceCount, }) } onProgress?.('图片上传完成,正在保存作品', 100) return uploaded }