import { postgrestGet, postgrestPut } from "../postgrest-client"; import axios from 'axios'; import { API_BASE_URL } from '../../config/api-config'; /** * 从不同格式的 API 响应中提取数据 * @param responseData API 响应数据 * @returns 提取后的数据或 null */ function extractApiData(responseData: unknown): T | null { if (!responseData) return null; // 格式1: { code: number, msg: string, data: T } if (typeof responseData === 'object' && responseData !== null && 'code' in responseData && 'data' in responseData && (responseData as { data: unknown }).data) { return (responseData as { data: T }).data; } // 格式2: 直接是数据对象 return responseData as T; } /** * 提出意见的请求参数接口 */ export interface SubmitOpinionRequest { reviewPointResultId: string | number; documentId: string | number; evaluationPointId: number | null; // 必须是数字ID auditOpinion: string; deductionScore: number; } /** * 提出意见的响应接口 */ export interface SubmitOpinionResponse { success: boolean; message: string; data?: { id: string | number; created_at: string; }; } /** * 交叉评查意见数据接口 */ export interface CrossCheckingOpinion { proposal_id: string | number; evaluation_point_name: string; proposed_score: number; reason: string; proposer: string; votes: Array<{ voter: string; vote_type: string }>; agree_voters: string[]; disagree_voters: string[]; pending_voters: string[]; can_vote: boolean; problem_message: string; proposer_id: number; created_at: string; status: string; } /** * API响应格式 */ export interface ApiResponse { data?: T; error?: string; status?: number; } /** * 安全获取JWT token * @param jwtToken JWT token字符串 * @returns JWT token字符串 */ async function safeGetJWT(jwtToken?: string): Promise { return jwtToken || ''; } /** * 检查用户是否有权确认完成文档评查 * * 🔥 接口文档: auth_doc/交叉评查接口文档.md 接口11 * 📍 API地址: GET /api/v2/cross_review/tasks/{task_id}/can-confirm * * @param taskId 任务ID * @param frontendJWT JWT token * @returns 是否有权确认完成 */ export async function findIsProposer(taskId: string | number, userId: number | undefined, frontendJWT?: string): Promise { try { if (!taskId) { console.error('任务ID不能为空'); return false; } // 调用新的接口检查用户是否有权确认完成 // GET /api/v2/cross_review/tasks/{task_id}/can-confirm const response = await axios.get( `${API_BASE_URL}/api/v2/cross_review/tasks/${taskId}/can-confirm`, { headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${frontendJWT}` } } ); const data = response.data; // console.log('[findIsProposer] 检查权限响应:', data); // 返回 can_confirm 字段,表示是否有权确认完成 // 有权限的用户:任务创建者(assigner_id) 或 主要负责人(principal_user_ids) return data?.can_confirm === true; } catch (error) { console.error('[findIsProposer] 检查权限失败:', error); // 正确处理 axios 错误响应 if (axios.isAxiosError(error) && error.response?.data) { console.error('[findIsProposer] 错误详情:', error.response.data); } return false; } } /** * 提交交叉评查意见 * @param opinionData 意见数据 * @param jwtToken JWT token * @returns 提交结果 */ export async function submitCrossCheckingOpinion( opinionData: SubmitOpinionRequest, jwtToken?: string, userInfo?: { user_id: number } ): Promise> { try { // 获取JWT token console.log('jwtToken', jwtToken) const token = await safeGetJWT(jwtToken); const requestData = { document_id: opinionData.documentId, evaluation_point_id: Number(opinionData.evaluationPointId), // 强制转数字 proposed_score: opinionData.deductionScore, reason: opinionData.auditOpinion, proposer_id: userInfo?.user_id, evaluation_result_id: opinionData.reviewPointResultId }; const response = await axios.post(`${API_BASE_URL}/api/v2/cross_review/proposals`, requestData, { headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` } }); return { data: { success: true, message: '意见提交成功', data: response.data } }; } catch (error) { console.error('提交交叉评查意见失败:', error); // 正确处理 axios 错误响应 let errorMessage = '提交意见失败'; if (axios.isAxiosError(error) && error.response?.data) { // 从 axios 错误响应中提取 msg 字段 errorMessage = error.response.data.msg || errorMessage; } else if (error instanceof Error) { // 处理普通 Error 对象 errorMessage = error.message || errorMessage; } return { error: errorMessage, status: axios.isAxiosError(error) ? error.response?.status || 500 : 500 }; } } /** * 获取交叉评查意见列表(支持分页) * @param documentId 文档ID * @param page 页码 * @param pageSize 每页大小 * @param userId 用户ID,可选,便于后端接口对接 * @param jwtToken JWT token * @returns 意见列表和总数 */ export async function getCrossCheckingOpinions( documentId: string | number, page: number = 1, pageSize: number = 10, userId?: number, // 可选,便于后端接口对接 jwtToken?: string // 改为jwtToken参数 ): Promise> { try { // 获取JWT token const token = await safeGetJWT(jwtToken); // 如果没传userId,默认用1 const realUserId = userId ?? 1; // 实际后端API调用,拼接API_BASE_URL const response = await axios.post(`${API_BASE_URL}/api/v2/cross_review/proposals/document`, { // user_id: realUserId, document_id: documentId, // 如果后端需要document_id可以加上 page, page_size: pageSize }, { headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` } }); const data = response.data; // console.log('最原始的返回data', data); // 处理新的数据结构,支持分页 const responseData = data.data || data; const pagination = data.pagination; // 定义后端返回的数据项类型 interface ProposalItem { proposal_id: string | number; evaluation_point_name: string; proposed_score: number; reason: string; proposer: string; votes?: Array<{ voter: string; vote_type: string }>; agree_voters?: string[]; disagree_voters?: string[]; pending_voters?: string[]; can_vote?: boolean; problem_message?: string; proposer_id: number; created_at: string; status: string; } // 适配后端返回结构,使用新字段 const opinions: CrossCheckingOpinion[] = Array.isArray(responseData) ? responseData.map((item: ProposalItem) => ({ proposal_id: item.proposal_id, evaluation_point_name: item.evaluation_point_name, proposed_score: item.proposed_score, reason: item.reason, proposer: item.proposer, votes: item.votes || [], agree_voters: item.agree_voters || [], disagree_voters: item.disagree_voters || [], pending_voters: item.pending_voters || [], can_vote: item.can_vote ?? false, problem_message: item.problem_message || '', proposer_id: item.proposer_id, created_at: item.created_at, status: item.status })) : []; return { data: { opinions, total: pagination?.total || opinions.length } }; } catch (error) { console.error('获取交叉评查意见失败:', error); // 正确处理 axios 错误响应 let errorMessage = '获取意见列表失败'; if (axios.isAxiosError(error) && error.response?.data) { // 从 axios 错误响应中提取 msg 字段 errorMessage = error.response.data.msg || errorMessage; } else if (error instanceof Error) { // 处理普通 Error 对象 errorMessage = error.message || errorMessage; } return { error: errorMessage, status: axios.isAxiosError(error) ? error.response?.status || 500 : 500 }; } } /** * 意见操作类型 */ export type OpinionActionType = 'agree' | 'disagree' | 'withdraw_vote' | 'withdraw_opinion'; /** * 投票请求参数接口 */ export interface OpinionVoteCreate { vote_type: 'agree' | 'disagree'; } /** * 意见操作请求参数 */ export interface OpinionActionRequest { opinionId: string | number; action: OpinionActionType; } /** * 执行意见操作(赞同、反对、撤销投票、撤销意见) * @param actionData 操作数据 * @param jwtToken JWT token * @returns 操作结果 */ export async function performOpinionAction( actionData: OpinionActionRequest, jwtToken?: string, userInfo?: { user_id: number } ): Promise> { try { const token = await safeGetJWT(jwtToken); let message = ''; let endpoint = ''; let requestBody: Record = {}; switch (actionData.action) { case 'agree': message = '已赞同该意见'; endpoint = `${API_BASE_URL}/api/v2/cross_review/proposals/${actionData.opinionId}/votes`; requestBody = { vote_type: 'agree', voter_id: userInfo?.user_id }; break; case 'disagree': message = '已反对该意见'; endpoint = `${API_BASE_URL}/api/v2/cross_review/proposals/${actionData.opinionId}/votes`; requestBody = { vote_type: 'disagree', voter_id: userInfo?.user_id }; break; case 'withdraw_vote': message = '已撤销投票'; // 撤销投票的接口,根据实际API调整 endpoint = `${API_BASE_URL}/api/v2/cross_review/proposals/${actionData.opinionId}/votes`; requestBody = { vote_type: 'cancel', voter_id: userInfo?.user_id }; break; case 'withdraw_opinion': message = '已撤销意见'; // 撤销意见的接口,根据实际API调整 endpoint = `${API_BASE_URL}/api/v2/cross_review/proposals/${actionData.opinionId}`; requestBody = {}; break; default: throw new Error('无效的操作类型'); } const response = actionData.action === 'withdraw_opinion' ? await axios.delete(endpoint, { headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` } }) : await axios.post(endpoint, requestBody, { headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` } }); const data = response.data; console.log('返回的意见列表数据',data); return { data: { success: true, message: message } }; } catch (error) { console.error('执行意见操作失败:', error); // 正确处理 axios 错误响应 let errorMessage = '操作失败'; if (axios.isAxiosError(error) && error.response?.data) { // 从 axios 错误响应中提取 msg 字段 errorMessage = error.response.data.msg || errorMessage; } else if (error instanceof Error) { // 处理普通 Error 对象 errorMessage = error.message || errorMessage; } return { error: errorMessage, status: axios.isAxiosError(error) ? error.response?.status || 500 : 500 }; } } /** * 完成评查(确认文档审核完成) * @param taskId 任务ID * @param documentId 文档ID * @param frontendJWT JWT token * @returns 完成评查结果 * * 🔥 接口文档: auth_doc/交叉评查接口文档(1).md 接口10 * 📍 API地址: POST /admin/v2/cross_review/tasks/{task_id}/documents/{document_id}/complete */ export async function confirmReviewResults( taskId: string | number, documentId: string | number, frontendJWT?: string ): Promise<{data?: unknown, error?: string, status?: number}> { try { if (!taskId) { return { error: '任务ID不能为空', status: 400 }; } if (!documentId) { return { error: '文档ID不能为空', status: 400 }; } // 调用后端API确认文档审核完成 // 接口: POST /admin/v2/cross_review/tasks/{task_id}/documents/{document_id}/complete const response = await axios.post( `${API_BASE_URL}/admin/v2/cross_review/tasks/${taskId}/documents/${documentId}/complete`, {}, // 无需请求体 { headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${frontendJWT}` } } ); const data = response.data; // 检查响应是否成功 if (data?.success || data?.code === 0) { return { data: { task_id: data.task_id || taskId, document_id: data.document_id || documentId, message: data.message || '文档评查已完成' } }; } // 数据为空或格式不正确 console.error('❌ [confirmReviewResults] API响应数据异常:', data); return { error: data?.message || '确认文档审核失败', status: 500 }; } catch (error) { console.error('完成评查失败:', error); // 正确处理 axios 错误响应 let errorMessage = '完成评查失败'; if (axios.isAxiosError(error) && error.response?.data) { // 从 axios 错误响应中提取 detail 或 msg 字段 errorMessage = error.response.data.detail || error.response.data.msg || errorMessage; } else if (error instanceof Error) { errorMessage = error.message || errorMessage; } return { error: errorMessage, status: axios.isAxiosError(error) ? error.response?.status || 500 : 500 }; } } // 点击完成评查按钮后,调用接口,检查文档下提案是否存在未投票用户 export async function checkProposalVotes( documentId: string | number, jwtToken?: string ): Promise<{data?: unknown, error?: string, status?: number}> { try { // 获取JWT token const token = await safeGetJWT(jwtToken); const requestData = { document_id: documentId }; const response = await axios.post(`${API_BASE_URL}/api/v2/cross_review/proposals/document/check_pending_votes`, requestData, { headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` } }); const data = response.data; console.log("检查投票数据",data); return { data: { success: true, message: '检查成功', data: data } }; } catch (error) { console.error('检查失败:', error); // 正确处理 axios 错误响应 let errorMessage = '检查失败'; if (axios.isAxiosError(error) && error.response?.data) { // 从 axios 错误响应中提取 msg 字段 errorMessage = error.response.data.msg || errorMessage; } else if (error instanceof Error) { // 处理普通 Error 对象 errorMessage = error.message || errorMessage; } return { error: errorMessage, status: axios.isAxiosError(error) ? error.response?.status || 500 : 500 }; } }