import { API_BASE_URL } from '../../config/api-config'; import { postgrestPut, postgrestGet } from '../postgrest-client'; import axios from 'axios'; export { uploadDocumentToTask } from './cross-files-upload'; // 交叉评查任务状态枚举 export enum CrossCheckingTaskStatus { PENDING = 'pending', IN_PROGRESS = 'in_progress', COMPLETED = 'completed' } // 交叉评查任务类型枚举 export enum CrossCheckingTaskType { CITY = 'CITY', DISTRICT = 'DISTRICT' } // 案卷类型枚举 export enum CrossCheckingDocType { PENALTY = 'penalty', // 行政处罚 PERMIT = 'permit' // 行政许可 } // 文档类型接口(用于交叉评查案卷类型选项) export interface DocumentType { id: number; name: string; code: string; evaluation_point_groups_ids?: number[]; } // 交叉评查任务接口 export interface CrossCheckingTask { id: number; sequence: number; taskName: string; startDate: string; taskType: CrossCheckingTaskType; docType: string; // 改为直接使用返回的 doc_type 字符串 evaluationRegion: string[]; progress: number; status: string; // 改为直接使用返回的 task_status 字符串 score: number; operation: string; documents: UserTaskDocument[]; // 改为 documents 数组,包含完整的文档信息 totalDocuments?: number; // 新增:任务包含的文档总数 } // 用户任务文档接口类型定义 export interface UserTaskDocument { document_id: number; document_name: string; document_type_id: number; document_type_name: string; } // 新的用户任务信息接口(根据新的API格式) export interface UserTaskInfo { task_id: number; task_name?: string; task_status: string; doc_type?: string; task_created_at?: string; evaluation_region?: string[]; task_type?: string; progress?: number; total_documents?: number; // 新增:任务包含的文档总数 } // 用户任务API响应格式(新格式) export interface UserTaskApiResponse { total: number; page: number; page_size: number; items: UserTaskInfo[]; } // 任务文档接口类型定义(任务详情页兼容结构) export interface TaskDocument { document_id: number; file_name: string; status: string; path: string; file_code: string; file_type_name: string; file_type_id: number; file_size: number; upload_time: string; created_at: string; evaluations_status: number; audit_status: number; created_by_user_id: number; issues: Array<{ severity: string; message: string; }>; final_score: number; score_summary: string ; score_percent?: number | null; pass_count: number; warning_count: number; fail_count: number; manual_count: number; } // ==================== 新版接口类型定义(支持版本归纳)==================== /** * 历史版本信息 * 每个历史版本都有独立的评查统计、消息列表、分数信息 */ export interface CrossReviewHistoryVersion { /** 历史版本的文档ID */ id: number; /** 版本号(从1开始,数字越小越旧) */ version_number: number; /** 创建时间(ISO 8601格式) */ created_at: string; /** 文件大小(字节) */ file_size: number; /** 文件存储路径 */ path: string; /** 文档处理状态 */ status: "Waiting" | "Cutting" | "Extractioning" | "Evaluationing" | "Failed" | "Processed"; /** 文书号/文档编号(可为null) */ document_number: string | null; /** 文档类型ID */ type_id: number; /** 文档类型名称 */ type_name: string; /** 上传时间(ISO 8601格式) */ upload_time: string; /** 任务内评查完成状态:0=未评查, 1=已评查 */ audit_status: 0 | 1; /** 总评查点数 */ total_evaluation_points: number; /** 通过的评查点数量 */ pass_count: number; /** 警告的评查点数量 */ warning_count: number; /** 错误的评查点数量 */ error_count: number; /** 需人工审核的评查点数量 */ manual_count: number; /** 问题总数 */ issue_count: number; /** 警告消息列表 */ warning_messages: string[]; /** 错误消息列表 */ error_messages: string[]; /** 问题消息列表(综合:警告+错误) */ issue_messages: string[]; /** 需人工确认的消息列表 */ manual_messages: string[]; /** 最终得分 */ final_score: number; /** 满分 */ full_score: number; /** 得分摘要(如 "85.5/100") */ score_summary: string; /** 得分百分比(0-100) */ score_percent: number; } /** * 文档信息(含版本和评查统计)- 新版接口 */ export interface CrossReviewDocumentWithVersion { // ========== 基本信息 ========== /** 当前版本的文档ID */ id: number; /** 文档名称 */ name: string; /** 文件存储路径 */ path: string; /** 当前版本号(最大值,从1开始) */ version_number: number; /** 创建时间(ISO 8601格式) */ created_at: string; /** 文档处理状态 */ status: "Waiting" | "Cutting" | "Extractioning" | "Evaluationing" | "Failed" | "Processed"; /** 文件大小(字节) */ file_size: number; /** 文书号/文档编号(可为null) */ document_number: string | null; /** 文档类型ID */ type_id: number; /** 文档类型名称 */ type_name: string; /** 上传时间(ISO 8601格式) */ upload_time: string; // ========== 任务内评查状态 ========== /** 任务内评查完成状态:0=未评查, 1=已评查 */ audit_status: 0 | 1; // ========== 评查统计 ========== /** 总评查点数 */ total_evaluation_points: number; /** 通过的评查点数量 */ pass_count: number; /** 警告的评查点数量 */ warning_count: number; /** 错误的评查点数量 */ error_count: number; /** 需人工审核的评查点数量 */ manual_count: number; /** 问题总数 */ issue_count: number; // ========== 评查消息列表 ========== /** 警告消息列表 */ warning_messages: string[]; /** 错误消息列表 */ error_messages: string[]; /** 问题消息列表(综合) */ issue_messages: string[]; /** 需人工确认的消息列表 */ manual_messages: string[]; // ========== 交叉评查特有:分数信息 ========== /** 最终得分 */ final_score: number; /** 满分 */ full_score: number; /** 得分摘要(如 "85.5/100") */ score_summary: string; /** 得分百分比(0-100) */ score_percent: number; // ========== 版本信息 ========== /** 总版本数 */ total_versions: number; /** 历史版本列表(按created_at降序,不包含当前版本) */ history_versions: CrossReviewHistoryVersion[]; // ========== 前端扩展字段 ========== /** 是否已展开历史版本(前端状态) */ isExpanded?: boolean; } /** * 交叉评查任务文档列表响应 */ export interface CrossReviewDocumentListResponse { /** 总文档数(按版本分组后的唯一文档数) */ total: number; /** 当前页码 */ page: number; /** 每页数量 */ page_size: number; /** 总页数 */ total_pages: number; /** 文档列表 */ documents: CrossReviewDocumentWithVersion[]; } /** * 获取任务文档列表请求参数(支持版本归纳) */ export interface GetTaskDocumentsWithVersionsParams { /** 任务ID */ taskId: number; /** 页码(从1开始) */ page?: number; /** 每页数量(最大100) */ pageSize?: number; /** 模糊搜索关键字(匹配文件名称或文档编号) */ keyword?: string; /** JWT token */ jwtToken?: string; } // 任务文档API响应格式(新增) export interface TaskDocumentApiResponse { total: number; page: number; page_size: number; items: TaskDocument[]; } // API响应格式 export interface ApiResponse { success: boolean; data?: T; error?: string; message?: string; status?: number; } interface ResultEnvelope { code?: number; msg?: string; data?: T; } interface V3UserTaskItem { taskId: number; taskName: string; taskType: string; docTypeId?: number | null; docTypeCode?: string | null; status: string; progress?: number; totalDocuments?: number; completedDocuments?: number; createdAt?: string; } interface V3UserTaskPage { total: number; page: number; pageSize: number; items: V3UserTaskItem[]; } interface V3TaskDocumentItem { documentId: number; name: string; documentNumber?: string | null; typeId?: number | null; processingStatus?: string | null; versionNo?: number; isLatestVersion?: boolean; auditStatus?: number; createdAt?: string; } interface V3TaskDocumentPage { taskId: number; total: number; page: number; pageSize: number; items: V3TaskDocumentItem[]; } function unwrapResultEnvelope(payload: unknown): T { if (payload && typeof payload === 'object' && 'data' in (payload as ResultEnvelope)) { return ((payload as ResultEnvelope).data ?? null) as T; } return payload as T; } function mapProcessingStatus(status?: string | null): CrossReviewDocumentWithVersion['status'] { switch (status) { case 'processed': case 'Processed': return 'Processed'; case 'failed': case 'Failed': return 'Failed'; case 'cutting': case 'Cutting': return 'Cutting'; case 'extracting': case 'Extractioning': return 'Extractioning'; case 'evaluating': case 'Evaluationing': return 'Evaluationing'; case 'waiting': case 'Waiting': default: return 'Waiting'; } } function mapV3TaskToUserTaskInfo(item: V3UserTaskItem): UserTaskInfo { return { task_id: item.taskId, task_name: item.taskName, task_status: item.status, doc_type: item.docTypeCode || undefined, task_created_at: item.createdAt, task_type: item.taskType, progress: item.progress, total_documents: item.totalDocuments }; } function mapV3DocumentToTaskDocument(item: V3TaskDocumentItem): TaskDocument { return { document_id: item.documentId, file_name: item.name || '', status: mapProcessingStatus(item.processingStatus), path: '', file_code: item.documentNumber || '', file_type_name: item.typeId ? `类型${item.typeId}` : '', file_type_id: item.typeId || 0, file_size: 0, upload_time: item.createdAt || '', created_at: item.createdAt || '', evaluations_status: 0, audit_status: item.auditStatus || 0, created_by_user_id: 0, issues: [], final_score: 0, score_summary: '', score_percent: null, pass_count: 0, warning_count: 0, fail_count: 0, manual_count: 0 }; } function mapV3DocumentToVersionedDocument(item: V3TaskDocumentItem): CrossReviewDocumentWithVersion { const typeName = item.typeId ? `类型${item.typeId}` : '未知类型'; return { id: item.documentId, name: item.name || '', path: '', version_number: item.versionNo || 1, created_at: item.createdAt || '', status: mapProcessingStatus(item.processingStatus), file_size: 0, document_number: item.documentNumber || null, type_id: item.typeId || 0, type_name: typeName, upload_time: item.createdAt || '', audit_status: (item.auditStatus || 0) as 0 | 1, total_evaluation_points: 0, pass_count: 0, warning_count: 0, error_count: 0, manual_count: 0, issue_count: 0, warning_messages: [], error_messages: [], issue_messages: [], manual_messages: [], final_score: 0, full_score: 100, score_summary: '', score_percent: 0, total_versions: 1, history_versions: [] }; } // 任务列表查询参数 export interface TaskListParams { page?: number; pageSize?: number; taskType?: string; docType?: string; status?: string; keyword?: string; dateFrom?: string; dateTo?: string; } // 任务列表响应数据 export interface TaskListResponse { tasks: CrossCheckingTask[]; totalCount: number; currentPage: number; pageSize: number; totalPages: number; } /** * 获取交叉评查任务列表 * @param params 查询参数 * @param userInfo 用户信息 * @param jwtToken JWT token * @returns 任务列表响应 */ export async function getCrossCheckingTasks(params: TaskListParams = {}, userInfo?: { user_id?: number; [key: string]: unknown }, jwtToken?: string): Promise> { try { const userTasksResponse = await getUserTaskDocuments(params, jwtToken); // console.log('getUserTaskDocuments响应:', JSON.stringify(userTasksResponse,null,2)); if (!userTasksResponse.success || !userTasksResponse.data) { console.error('获取用户任务失败:', userTasksResponse.error); return { success: false, error: userTasksResponse.error || '获取用户任务失败' }; } // 将用户任务数据转换为CrossCheckingTask格式 const userTasks = userTasksResponse.data.items; const convertedTasks: CrossCheckingTask[] = userTasks.map((userTask: UserTaskInfo, index: number) => { // 从用户任务中提取任务信息,使用API返回的实际数据 const task: CrossCheckingTask = { id: userTask.task_id, sequence: index + 1, taskName: userTask.task_name || `任务 ${userTask.task_id}`, // 使用API返回的任务名称 startDate: userTask.task_created_at ? new Date(userTask.task_created_at).toISOString().split('T')[0] : new Date().toISOString().split('T')[0], taskType: userTask.task_type, // 保持默认任务类型 docType: userTask.doc_type || '未知类型', // 使用API返回的文档类型 evaluationRegion: userTask.evaluation_region || [], progress: userTask.progress || 0, // 使用API返回的进度 status: userTask.task_status || 'pending', // 使用API返回的任务状态 score: userTask.task_status === 'completed' ? 85 : 0, // 默认分数 operation: userTask.task_status === 'completed' ? '查看结果' : userTask.task_status === 'in_progress' ? '进行中' : '去评查', documents: [], // 暂时为空数组,因为新API格式中任务列表不包含具体文档信息 totalDocuments: userTask.total_documents || 0 // 使用API返回的文档总数 }; return task; }); const { taskType, docType, status, keyword, dateFrom, dateTo } = params; // 筛选数据 let filteredTasks = [...convertedTasks]; // 按任务类型筛选 if (taskType && taskType !== 'all') { filteredTasks = filteredTasks.filter(task => task.taskType === taskType); } // 按案卷类型筛选 if (docType && docType !== 'all') { filteredTasks = filteredTasks.filter(task => task.docType === docType); } // 按状态筛选 if (status && status !== 'all') { filteredTasks = filteredTasks.filter(task => task.status === status); } // 按关键词搜索 if (keyword) { const lowerKeyword = keyword.toLowerCase(); filteredTasks = filteredTasks.filter(task => task.taskName.toLowerCase().includes(lowerKeyword) || task.evaluationRegion.join(',').toLowerCase().includes(lowerKeyword) ); } // 按日期范围筛选 if (dateFrom || dateTo) { filteredTasks = filteredTasks.filter(task => { const taskDate = new Date(task.startDate); if (dateFrom && new Date(dateFrom) > taskDate) return false; if (dateTo && new Date(dateTo) < taskDate) return false; return true; }); } return { success: true, data: { tasks: filteredTasks, totalCount: userTasksResponse.data.total, currentPage: userTasksResponse.data.page, pageSize: userTasksResponse.data.page_size, totalPages: Math.ceil(userTasksResponse.data.total / userTasksResponse.data.page_size) } }; } catch (error) { console.error('获取交叉评查任务列表失败:', error); return { success: false, error: error instanceof Error ? error.message : '获取任务列表失败' }; } } /** * 删除交叉评查任务 * @param taskId 任务ID * @returns 删除结果 */ export async function deleteCrossCheckingTask(taskId: number): Promise> { try { // 模拟API延迟 await new Promise(resolve => setTimeout(resolve, 500)); // 这里应该调用实际的API来删除任务 // 目前暂时返回成功,因为没有实际的删除API console.log(`尝试删除任务ID: ${taskId}`); return { success: true, data: true, message: '删除任务成功' }; } catch (error) { console.error('删除交叉评查任务失败:', error); return { success: false, error: error instanceof Error ? error.message : '删除任务失败' }; } } /** * 获取任务详情及相关文档 * @param taskId 任务ID * @param page 页码,默认为1 * @param pageSize 每页大小,默认为10 * @param jwtToken JWT token * @returns 任务详情和文档列表 */ export async function getCrossCheckingTaskDetail( taskId: number, page: number = 1, pageSize: number = 10, jwtToken?: string ): Promise> { try { // console.log('开始调用getCrossCheckingTaskDetail,参数:', { taskId, page, pageSize }); // 获取任务的文档列表 const taskDocumentsResponse = await getTaskDocuments(taskId, page, pageSize, jwtToken); if (!taskDocumentsResponse.success || !taskDocumentsResponse.data) { console.error('获取任务文档失败:', taskDocumentsResponse.error); return { success: false, error: taskDocumentsResponse.error || '获取任务文档失败' }; } const documentsData = taskDocumentsResponse.data; const result = { success: true, data: { task: null, // 暂时不返回任务详情,因为新接口主要关注文档列表 files: documentsData.items, total: documentsData.total, currentPage: documentsData.page, pageSize: documentsData.page_size } }; return result; } catch (error) { console.error('获取任务详情失败:', error); return { success: false, error: error instanceof Error ? error.message : '获取任务详情失败' }; } } /** * 获取统计数据 * @param userInfo 用户信息 * @param jwtToken JWT token * @returns 统计数据 */ export async function getCrossCheckingStats(userInfo?: { user_id?: number; [key: string]: unknown }, jwtToken?: string): Promise> { try { console.log('开始调用getCrossCheckingStats'); // 获取用户任务数据来计算统计(默认获取第一页数据进行统计) const userTasksResponse = await getUserTaskDocuments({ page: 1, pageSize: 100 }, jwtToken); if (!userTasksResponse.success || !userTasksResponse.data) { console.error('获取用户任务失败:', userTasksResponse.error); return { success: false, error: userTasksResponse.error || '获取用户任务失败' }; } const userTasks = userTasksResponse.data.items; const totalTasks = userTasksResponse.data.total; const pendingTasks = userTasks.filter(t => t.task_status === 'pending').length; const inProgressTasks = userTasks.filter(t => t.task_status === 'in_progress').length; const completedTasks = userTasks.filter(t => t.task_status === 'completed').length; return { success: true, data: { totalTasks, pendingTasks, inProgressTasks, completedTasks } }; } catch (error) { console.error('获取统计数据失败:', error); return { success: false, error: error instanceof Error ? error.message : '获取统计数据失败' }; } } // ==================== 更新:用户任务文档相关接口 ==================== /** * 获取用户参与的所有任务列表(更新为新的API格式) * @param page 页码 * @param pageSize 每页大小 * @param jwtToken JWT token * @returns 用户任务列表 */ export async function getUserTaskDocuments(params: TaskListParams = {}, jwtToken?: string): Promise> { try { const base = API_BASE_URL.endsWith('/') ? API_BASE_URL.slice(0, -1) : API_BASE_URL; const url = `${base}/api/v3/cross-review/tasks/query`; const page = params.page || 1; const pageSize = params.pageSize || 10; const requestBody: Record = { page, pageSize }; if (params.keyword?.trim()) { requestBody.keyword = params.keyword.trim(); } if (params.status && params.status !== 'all') { requestBody.status = params.status; } if (params.taskType && params.taskType !== 'all') { requestBody.taskType = params.taskType; } if (params.docType && params.docType !== 'all') { requestBody.docTypeCode = params.docType; } const response = await axios.post>(url, requestBody, { headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${jwtToken || ''}` } }); const pageData = unwrapResultEnvelope(response.data); const items = Array.isArray(pageData?.items) ? pageData.items.map(mapV3TaskToUserTaskInfo) : []; return { success: true, data: { total: pageData?.total || 0, page: pageData?.page || page, page_size: pageData?.pageSize || pageSize, items } }; } catch (error) { if (axios.isAxiosError(error)) { return { success: false, error: `HTTP ${error.response?.status}: ${error.response?.statusText || error.message}` }; } return { success: false, error: error instanceof Error ? error.message : '获取用户任务列表失败' }; } } /** * 获取指定任务的文档列表(兼容任务详情使用) * @param taskId 任务ID * @param page 页码 * @param pageSize 每页大小 * @param jwtToken JWT token * @returns 任务文档列表 */ export async function getTaskDocuments(taskId: number, page: number = 1, pageSize: number = 10, jwtToken?: string): Promise> { try { const base = API_BASE_URL.endsWith('/') ? API_BASE_URL.slice(0, -1) : API_BASE_URL; const url = `${base}/api/v3/cross-review/tasks/${taskId}/documents`; const response = await axios.get>(url, { params: { page, pageSize }, headers: { 'Authorization': `Bearer ${jwtToken || ''}` } }); const pageData = unwrapResultEnvelope(response.data); const items = Array.isArray(pageData?.items) ? pageData.items.map(mapV3DocumentToTaskDocument) : []; return { success: true, data: { total: pageData?.total || 0, page: pageData?.page || page, page_size: pageData?.pageSize || pageSize, items } }; } catch (error) { if (axios.isAxiosError(error)) { return { success: false, error: `HTTP ${error.response?.status}: ${error.response?.statusText || error.message}` }; } return { success: false, error: error instanceof Error ? error.message : '获取任务文档列表失败' }; } } /** * 获取任务下文档列表(支持版本归纳) * * GET /api/v3/cross-review/tasks/{task_id}/documents * * 同一任务内同名且同类型的文档会被归纳为版本组,最新上传的为当前版本,其余为历史版本。 * * @param params 请求参数 * @returns 文档列表响应(含版本信息) */ export async function getTaskDocumentsWithVersions( params: GetTaskDocumentsWithVersionsParams ): Promise> { const { taskId, page = 1, pageSize = 10, keyword, jwtToken } = params; try { const base = API_BASE_URL.endsWith('/') ? API_BASE_URL.slice(0, -1) : API_BASE_URL; const url = `${base}/api/v3/cross-review/tasks/${taskId}/documents`; const queryParams: { page: number; pageSize: number; keyword?: string; } = { page, pageSize }; // 只有当 keyword 有值时才添加 if (keyword && keyword.trim()) { queryParams.keyword = keyword.trim(); } const response = await axios.get>(url, { params: queryParams, headers: { 'Authorization': `Bearer ${jwtToken || ''}` } }); const pageData = unwrapResultEnvelope(response.data); const documents = Array.isArray(pageData?.items) ? pageData.items.map(mapV3DocumentToVersionedDocument) : []; return { success: true, data: { total: pageData?.total || 0, page: pageData?.page || page, page_size: pageData?.pageSize || pageSize, total_pages: Math.ceil((pageData?.total || 0) / (pageData?.pageSize || pageSize || 1)), documents } }; } catch (error) { if (axios.isAxiosError(error)) { // 处理特定错误码 if (error.response?.status === 403) { return { success: false, error: '无权访问任务:您不是该任务的参与者', status: 403 }; } return { success: false, error: `HTTP ${error.response?.status}: ${error.response?.statusText || error.message}`, status: error.response?.status }; } return { success: false, error: error instanceof Error ? error.message : '获取任务文档列表失败' }; } } /** * 更新文件的审核状态 * @param id 文件ID * @param auditStatus 审核状态 * @returns 更新结果 */ export async function updateDocumentAuditStatus(id: string, auditStatus: number, frontendJWT?: string): Promise<{ success?: boolean; error?: string; status?: number; }> { try { if (!id) { return { error: '文件ID不能为空', status: 400 }; } const response = await postgrestPut>( '/api/postgrest/proxy/documents', { audit_status: auditStatus }, { id: parseInt(id) }, frontendJWT ); if (response.error) { return { error: response.error, status: response.status }; } return { success: true }; } catch (error) { console.error('更新文件审核状态失败:', error); return { error: error instanceof Error ? error.message : '更新文件审核状态失败', status: 500 }; } } /** * 获取可用于交叉评查的文档类型列表 * 条件:evaluation_point_groups_ids 不为空 * @param jwtToken JWT token * @returns 文档类型列表 */ export async function getCrossCheckingDocumentTypes(jwtToken?: string): Promise> { try { const response = await axios.get<{ data?: Array<{ id: number; name: string; code: string; isEnabled?: boolean; ruleSetIds?: number[]; }> }>(`${API_BASE_URL}/api/document-types`, { headers: jwtToken ? { Authorization: `Bearer ${jwtToken}` } : undefined, }); const rawItems = Array.isArray(response.data?.data) ? response.data.data : []; const filteredData = rawItems .filter((item) => item && item.isEnabled !== false) .map((item) => ({ id: item.id, name: item.name, code: item.code, })) .sort((a, b) => a.name.localeCompare(b.name, 'zh-CN')); return { success: true, data: filteredData }; } catch (error) { console.error('[getCrossCheckingDocumentTypes] 获取交叉评查文档类型失败:', error); return { success: false, error: error instanceof Error ? error.message : '获取文档类型失败' }; } } // ==================== 追加附件 API ==================== /** * 追加附件响应接口 */ export interface AppendAttachmentsResponse { success: boolean; result?: { original_document_id: number; new_document_id: number; new_version_number: number; task_id: number; message: string; background_processing: boolean; }; error?: string; } /** * 追加附件参数接口 */ export interface AppendAttachmentsParams { /** 任务ID */ taskId: number; /** 原文档ID */ documentId: number; /** 附件文件列表 */ files: File[]; /** 备注说明(可选) */ remark?: string; /** Word附件是否使用Markdown处理(可选,默认false) */ useMarkdown?: boolean; /** JWT Token */ jwtToken?: string; } /** * 为交叉评查任务文档追加附件 * * POST /api/v2/cross_review/tasks/{task_id}/documents/{document_id}/append_attachments * * 追加附件后创建新文档(新版本),原文档保留。 * 新文档自动关联到当前任务,audit_status = 0(需重新评查) * * @param params 追加附件参数 * @returns 追加结果 */ export async function appendTaskDocumentAttachments( params: AppendAttachmentsParams ): Promise> { const { taskId, documentId, files, remark, useMarkdown = false, jwtToken } = params; try { if (!taskId || taskId <= 0) { return { success: false, error: '任务ID无效' }; } if (!documentId || documentId <= 0) { return { success: false, error: '文档ID无效' }; } if (!files || files.length === 0) { return { success: false, error: '请选择附件文件' }; } // 构建 FormData const formData = new FormData(); files.forEach(file => { formData.append('files', file); }); if (remark) { formData.append('remark', remark); } formData.append('use_markdown', useMarkdown.toString()); // 构建请求 URL const base = API_BASE_URL.endsWith('/') ? API_BASE_URL.slice(0, -1) : API_BASE_URL; const url = `${base}/api/v2/cross_review/tasks/${taskId}/documents/${documentId}/append_attachments`; const response = await axios.post(url, formData, { headers: { 'Content-Type': 'multipart/form-data', 'Authorization': `Bearer ${jwtToken || ''}` } }); return { success: true, data: response.data }; } catch (error) { if (axios.isAxiosError(error)) { if (error.response?.status === 403) { return { success: false, error: '无权追加附件:您不是任务创建者或负责人', status: 403 }; } if (error.response?.status === 400) { return { success: false, error: error.response.data?.error || '请求参数错误', status: 400 }; } return { success: false, error: `HTTP ${error.response?.status}: ${error.response?.statusText || error.message}`, status: error.response?.status }; } return { success: false, error: error instanceof Error ? error.message : '追加附件失败' }; } } // ==================== 上传模板 API ==================== /** * 上传模板响应接口(文档级别) */ export interface UploadDocumentTemplateResponse { success: boolean; result?: { document_id: number; comparison_id: number; template_name: string; template_path: string; status: string; message: string; }; error?: string; } /** * 上传模板参数接口(文档级别) */ export interface UploadDocumentTemplateParams { /** 文档ID */ documentId: number; /** 模板文件 */ file: File; /** 对比记录ID(可选,用于更新已有模板) */ comparisonId?: number; /** JWT Token */ jwtToken?: string; } /** * 为交叉评查任务中的文档上传模板(文档级别) * * 复用现有的 /upload_contract_template 接口,与 files-upload.ts 中的 uploadContractTemplate 保持一致 * * @param params 上传模板参数 * @returns 上传结果 */ export async function uploadCrossReviewDocumentTemplate( params: UploadDocumentTemplateParams ): Promise> { const { documentId, file, comparisonId, jwtToken } = params; try { if (!documentId || documentId <= 0) { return { success: false, error: '文档ID无效' }; } if (!file) { return { success: false, error: '请选择模板文件' }; } // 构建 FormData,与 files-upload.ts 中的 uploadContractTemplate 保持一致 const formData = new FormData(); formData.append('file', file); // upload_info 作为 JSON 字符串 const uploadInfo: { document_id: number; comparison_id?: number } = { document_id: documentId }; if (comparisonId) { uploadInfo.comparison_id = comparisonId; } formData.append('upload_info', JSON.stringify(uploadInfo)); // 使用与 files-upload.ts 相同的上传接口 const { UPLOAD_URL } = await import('~/config/api-config'); const url = `${UPLOAD_URL}/upload_contract_template`; const response = await axios.post(url, formData, { headers: { 'Accept': 'application/json', 'Authorization': `Bearer ${jwtToken || ''}` } }); return { success: true, data: response.data }; } catch (error) { if (axios.isAxiosError(error)) { if (error.response?.status === 403) { return { success: false, error: '无权上传模板', status: 403 }; } if (error.response?.status === 400) { return { success: false, error: error.response.data?.error || '请求参数错误', status: 400 }; } return { success: false, error: `HTTP ${error.response?.status}: ${error.response?.statusText || error.message}`, status: error.response?.status }; } return { success: false, error: error instanceof Error ? error.message : '上传模板失败' }; } }