import { postgrestGet, postgrestPost, postgrestPut, postgrestDelete, type PostgrestParams } from '../postgrest-client'; import { apiRequest } from '../axios-client'; import { formatDate } from '../../utils'; /** * 从不同格式的 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 RulesQueryParams { page?: number; pageSize?: number; ruleType?: string; // 评查点类型ID (一级分组ID) groupId?: string; // 规则组ID (二级分组ID) risk?: '高' | '中' | '低'; // 风险等级 isActive?: boolean; keyword?: string; area?: string; // 地区过滤 orderBy?: string; orderDirection?: 'asc' | 'desc'; userRole?: string; // 用户角色 token?: string; // JWT token } /** * 评查点列表响应数据 */ export interface RulesListResponse { rules: Rule[]; totalCount: number; } /** * API返回的评查点详情 */ export interface ApiRule { id: number; code: string; name: string; area?: string; // 地区 evaluation_point_groups_id: number | null; evaluation_point_groups_pid?: number | null; // 一级分组ID (评查点类型) risk: string; description: string; is_enabled: boolean; evaluation_point_groups?: { first_name: string; second_name: string; }; // 🆕 PostgREST 双连接查询返回的字段 child_group?: { id: number; name: string; } | null; parent_group?: { id: number; name: string; } | null; // 以下字段仅在详情接口中返回,列表接口可能不包含 references_laws?: Record; extraction_config?: { type: string; fields: string[]; prompt_setting?: { type: string; template: string; }; }; evaluation_config?: { rules: Array<{ id: string; type: string; config: Record; }>; logicType: string; }; pass_message?: string; fail_message?: string; suggestion_message?: string; suggestion_message_type?: string; post_action?: string; action_config?: string; created_at: string; updated_at: string; } /** * 评查点详情(前端模型) */ export interface Rule { id: string; code: string; name: string; ruleType: string; groupId: string; groupName: string; priority: string; description: string; isActive: boolean; createdAt: string; updatedAt: string; } /** * 映射API返回的评查点数据到前端模型 * @param apiRule API返回的评查点数据 * @returns 前端评查点模型 */ function mapApiRuleToFrontendModel(apiRule: ApiRule): Rule { // 风险等级映射到优先级 const priorityMap: Record = { '高': 'high', '中': 'medium', '低': 'low' }; // 🆕 优先使用 PostgREST 双连接查询返回的数据 let ruleType = ''; let groupName = ''; let groupId = ''; if (apiRule.child_group || apiRule.parent_group) { // 有 PostgREST 双连接查询数据(外键约束方式) groupId = apiRule.child_group?.id.toString() || ''; groupName = apiRule.child_group?.name || ''; ruleType = apiRule.parent_group?.name || ''; } else if (apiRule.evaluation_point_groups) { // 兼容旧的查询方式(RPC 函数或手动拼接) const isGroupIdEmpty = !apiRule.evaluation_point_groups_id; ruleType = isGroupIdEmpty ? '' : (apiRule.evaluation_point_groups.first_name || ''); groupName = isGroupIdEmpty ? '' : (apiRule.evaluation_point_groups.second_name || `${apiRule.evaluation_point_groups_id}`); groupId = isGroupIdEmpty ? '' : apiRule.evaluation_point_groups_id?.toString() || ''; } else { // 没有任何分组信息 const isGroupIdEmpty = !apiRule.evaluation_point_groups_id; groupId = isGroupIdEmpty ? '' : apiRule.evaluation_point_groups_id?.toString() || ''; } // 🔑 清洗评查点编码:移除最后一个 '--' 及其后面的字符 // 例如:'code-mis--mz' --> 'code-mis', 'code-mbs--alsi--gz' --> 'code-mbs--alsi' let cleanedCode = apiRule.code || ''; const lastDoubleHyphenIndex = cleanedCode.lastIndexOf('--'); if (lastDoubleHyphenIndex !== -1) { cleanedCode = cleanedCode.substring(0, lastDoubleHyphenIndex); } return { id: apiRule.id ? apiRule.id.toString() : '', code: cleanedCode, name: apiRule.name || '', ruleType: ruleType, groupId: groupId, groupName: groupName, priority: priorityMap[apiRule.risk] || 'medium', description: apiRule.description || '', isActive: apiRule.is_enabled === undefined ? false : apiRule.is_enabled, createdAt: apiRule.created_at, updatedAt: apiRule.updated_at }; } /** * 获取评查点列表 * @param params 查询参数 * @returns 评查点列表、总数和评查点组 */ export async function getRulesList(params: RulesQueryParams): Promise<{data: RulesListResponse; error?: never} | {data?: never; error: string; status?: number}> { try { const { page = 1, pageSize = 10, ruleType, groupId, risk, isActive, keyword, area, userRole, token } = params; // 🔑 如果没有传递 userRole,尝试从 localStorage 中获取 let user_role = userRole || ''; if (!user_role && typeof window !== 'undefined' && window.localStorage) { try { const userInfoStr = localStorage.getItem('user_info'); if (userInfoStr) { const userInfo = JSON.parse(userInfoStr); user_role = userInfo.user_role || userInfo.userRole || ''; } } catch (error) { console.error('❌ [getRulesList] 解析 localStorage 用户信息失败:', error); } } // 🆕 调用后端 FastAPI 接口: GET /api/v3/evaluation-points // 构建查询参数 const queryParams = new URLSearchParams(); queryParams.append('page', page.toString()); queryParams.append('page_size', pageSize.toString()); // 添加一级分组ID(评查点类型) if (ruleType) { queryParams.append('evaluation_point_groups_pid', ruleType); } // 添加二级分组ID(规则组) if (groupId) { queryParams.append('evaluation_point_groups_id', groupId); } // 添加风险等级筛选 if (risk) { queryParams.append('risk', risk); } // 添加启用状态筛选 if (isActive !== undefined) { queryParams.append('is_enabled', isActive.toString()); } // 🔑 添加地区过滤 if (user_role === 'provincial_admin') { queryParams.append('area', '省级'); } else if (area) { queryParams.append('area', area); } // 添加关键词搜索(后端会同时搜索 name 和 code) if (keyword) { queryParams.append('name', keyword); queryParams.append('code', keyword); } // 调用 FastAPI 接口 const response = await apiRequest<{ data: EvaluationPointData[]; total: number; page: number; page_size: number; }>( `/api/v3/evaluation-points?${queryParams.toString()}`, { method: 'GET', headers: { ...(token ? { 'Authorization': `Bearer ${token}` } : {}) } } ); if (response.error) { return { error: response.error, status: response.status }; } if (!response.data || !Array.isArray(response.data.data)) { return { error: '接口返回数据格式不正确', status: 500 }; } console.log('✅ [getRulesList] 成功获取评查点列表,共', response.data.total, '条'); // 🆕 直接映射后端返回的数据到前端 Rule 模型 // 后端已包含 ruleType、groupName、groupId 字段,无需额外处理 const mappedRules: Rule[] = response.data.data.map((point: EvaluationPointData) => { // 风险等级映射到优先级 const priorityMap: Record = { '高': 'high', '中': 'medium', '低': 'low', 'high': 'high', 'medium': 'medium', 'low': 'low' }; // 🔑 清洗评查点编码:移除最后一个 '--' 及其后面的字符 let cleanedCode = point.code || ''; const lastDoubleHyphenIndex = cleanedCode.lastIndexOf('--'); if (lastDoubleHyphenIndex !== -1) { cleanedCode = cleanedCode.substring(0, lastDoubleHyphenIndex); } return { id: point.id?.toString() || '', code: cleanedCode, name: point.name || '', ruleType: point.ruleType || '', // ✅ 后端直接返回 groupId: point.groupId || '', // ✅ 后端直接返回 groupName: point.groupName || '', // ✅ 后端直接返回 priority: priorityMap[point.risk] || 'medium', description: point.description || '', isActive: point.is_enabled, createdAt: formatDate(point.created_at || ''), updatedAt: formatDate(point.updated_at || '') }; }); return { data: { rules: mappedRules, totalCount: response.data.total } }; } catch (error) { console.error('❌ 获取评查点列表出错:', error); return { error: error instanceof Error ? error.message : '获取评查点列表失败', status: 500 }; } } /** * 获取单个评查点详情 * @param id 评查点ID * @param token JWT token (可选) * @returns 评查点详情 */ export async function getRule(id: string, token?: string): Promise<{data: Rule; error?: never} | {data?: never; error: string; status?: number}> { try { // 使用postgrestGet获取单个评查点数据 const postgrestParams: PostgrestParams = { // 使用PostgREST查询参数语法 filter: { 'id': `eq.${id}` }, // 使用PostgREST资源嵌入语法获取关联数据 select: ` id, code, name, evaluation_point_groups_id, risk, description, is_enabled, references_laws, extraction_config, evaluation_config, pass_message, fail_message, suggestion_message, suggestion_message_type, post_action, action_config, created_at, updated_at `, token }; // 获取评查点详情 - 使用正确的PostgREST格式 const response = await postgrestGet<{code: number; msg: string; data: ApiRule} | ApiRule[]>('/api/postgrest/proxy/evaluation_points', postgrestParams); // 检查是否有错误响应 if (response.error) { return { error: response.error, status: response.status }; } // 处理响应数据(PostgREST可能返回数组或包装对象) let apiRule: ApiRule | null = null; if (response.data) { // 如果是数组格式(PostgREST标准响应) if (Array.isArray(response.data)) { apiRule = response.data.length > 0 ? response.data[0] : null; } // 如果是包装对象格式 else if ('data' in response.data && response.data.data) { apiRule = response.data.data as ApiRule; } } if (!apiRule) { return { error: '评查点不存在', status: 404 }; } // 获取分组信息 try { if (apiRule.evaluation_point_groups_id) { const groupParams: PostgrestParams = { select: 'id,name', filter: { 'id': `eq.${apiRule.evaluation_point_groups_id}` }, token }; // 查询评查点分组 const groupResponse = await postgrestGet<{code: number; msg: string; data: {id: number; name: string}[]}>('/api/postgrest/proxy/evaluation_point_groups', groupParams); if (groupResponse.data?.data && groupResponse.data.data.length > 0) { // 将分组信息添加到评查点数据中 const group = groupResponse.data.data[0]; apiRule.evaluation_point_groups = { first_name: group.name, second_name: group.name }; } } } catch (error) { console.error('获取分组信息失败:', error); // 忽略错误,使用默认分组名 } // 将API返回的数据映射到前端模型 const rule = mapApiRuleToFrontendModel(apiRule); // 格式化日期字段 rule.createdAt = formatDate(rule.createdAt); rule.updatedAt = formatDate(rule.updatedAt); return { data: rule }; } catch (error) { console.error('获取评查点详情出错:', error); return { error: error instanceof Error ? error.message : '获取评查点详情失败', status: 500 }; } } /** * 创建新评查点 * @param ruleData 评查点数据 * @param token JWT token (可选) * @returns 创建的评查点 */ export async function createRule(ruleData: Omit, token?: string): Promise<{data: Rule; error?: never} | {data?: never; error: string; status?: number}> { try { // 1. 验证必填字段 if (!ruleData.name || !ruleData.code) { return { error: '评查点名称和编码不能为空', status: 400 }; } // 2. 验证名称长度(1-100字符) const trimmedName = ruleData.name.trim(); if (trimmedName.length === 0 || trimmedName.length > 100) { return { error: '评查点名称长度必须在1-100个字符之间', status: 400 }; } // 3. 验证编码格式(仅允许字母、数字、连字符和下划线) const trimmedCode = ruleData.code.trim(); if (!/^[a-zA-Z0-9-_]+$/.test(trimmedCode)) { return { error: '评查点编码只能包含字母、数字、连字符和下划线', status: 400 }; } // 4. 验证编码唯一性 const existingRulesResponse = await getRulesList({ keyword: trimmedCode, pageSize: 10, token }); if (existingRulesResponse.data && existingRulesResponse.data.rules.length > 0) { // 精确匹配检查(因为keyword是模糊搜索) const exactMatch = existingRulesResponse.data.rules.some(r => r.code === trimmedCode); if (exactMatch) { return { error: '评查点编码已存在,请使用其他编码', status: 409 }; } } // 5. 验证分组ID有效性 if (!ruleData.groupId) { return { error: '必须选择所属规则组', status: 400 }; } // 检查分组是否存在 const groupResponse = await postgrestGet<{code: number; msg: string; data: Array<{id: number; name: string; pid: number}> }>('/api/postgrest/proxy/evaluation_point_groups', { filter: { 'id': `eq.${ruleData.groupId}` }, select: 'id,name,pid', token }); let groupExists = false; if (groupResponse.data && 'code' in groupResponse.data && groupResponse.data.data) { groupExists = Array.isArray(groupResponse.data.data) && groupResponse.data.data.length > 0; } else if (Array.isArray(groupResponse.data)) { groupExists = groupResponse.data.length > 0; } if (!groupExists) { return { error: '所选规则组不存在', status: 404 }; } // 将前端模型转换为API接受的格式 const apiRuleData = { code: trimmedCode, name: trimmedName, evaluation_point_groups_id: parseInt(ruleData.groupId), risk: ruleData.priority === 'high' ? '高' : ruleData.priority === 'medium' ? '中' : '低', description: ruleData.description || '', is_enabled: ruleData.isActive !== undefined ? ruleData.isActive : true, // 以下是默认值,实际应用中需要根据业务逻辑设置 references_laws: {}, extraction_config: { type: "OCR+LLM", fields: [] }, evaluation_config: { rules: [], logicType: "and" }, pass_message: "", fail_message: "", suggestion_message: "", suggestion_message_type: "warning", post_action: "none", action_config: "" }; // 使用postgrestPost创建评查点 const response = await postgrestPost<{code: number; msg: string; data: ApiRule}, typeof apiRuleData>('/api/postgrest/proxy/evaluation_points', apiRuleData, token); // 检查是否有错误响应 if (response.error) { return { error: response.error, status: response.status }; } // 确保响应数据存在且符合预期格式 if (!response.data || !response.data.data) { return { error: '接口返回数据格式不正确', status: 500 }; } // 将API返回的数据映射到前端模型 const rule = mapApiRuleToFrontendModel(response.data.data); return { data: rule }; } catch (error) { console.error('创建评查点出错:', error); return { error: error instanceof Error ? error.message : '创建评查点失败', status: 500 }; } } /** * 更新评查点 * @param id 评查点ID * @param ruleData 评查点数据 * @param token JWT token (可选) * @returns 更新后的评查点 */ export async function updateRule(id: string, ruleData: Partial>, token?: string): Promise<{data: Rule; error?: never} | {data?: never; error: string; status?: number}> { try { // 1. 验证评查点ID有效性 const existingRuleResponse = await getRule(id, token); if (existingRuleResponse.error || !existingRuleResponse.data) { return { error: '评查点不存在', status: 404 }; } // 2. 验证名称长度(如果提供) if (ruleData.name !== undefined) { const trimmedName = ruleData.name.trim(); if (trimmedName.length === 0 || trimmedName.length > 100) { return { error: '评查点名称长度必须在1-100个字符之间', status: 400 }; } } // 3. 验证编码格式和唯一性(如果提供) if (ruleData.code !== undefined) { const trimmedCode = ruleData.code.trim(); // 验证编码格式 if (!/^[a-zA-Z0-9-_]+$/.test(trimmedCode)) { return { error: '评查点编码只能包含字母、数字、连字符和下划线', status: 400 }; } // 验证编码唯一性(排除自身) const existingRulesResponse = await getRulesList({ keyword: trimmedCode, pageSize: 10, token }); if (existingRulesResponse.data && existingRulesResponse.data.rules.length > 0) { // 精确匹配检查,排除当前评查点自身 const exactMatch = existingRulesResponse.data.rules.some(r => r.code === trimmedCode && r.id !== id); if (exactMatch) { return { error: '评查点编码已被其他评查点使用', status: 409 }; } } } // 4. 验证分组ID有效性(如果提供) if (ruleData.groupId !== undefined) { const groupResponse = await postgrestGet<{code: number; msg: string; data: Array<{id: number; name: string; pid: number}> }>('/api/postgrest/proxy/evaluation_point_groups', { filter: { 'id': `eq.${ruleData.groupId}` }, select: 'id,name,pid', token }); let groupExists = false; if (groupResponse.data && 'code' in groupResponse.data && groupResponse.data.data) { groupExists = Array.isArray(groupResponse.data.data) && groupResponse.data.data.length > 0; } else if (Array.isArray(groupResponse.data)) { groupExists = groupResponse.data.length > 0; } if (!groupExists) { return { error: '所选规则组不存在', status: 404 }; } } // 构建API接受的更新数据 const apiRuleData: Record = {}; if (ruleData.code !== undefined) { apiRuleData.code = ruleData.code.trim(); } if (ruleData.name !== undefined) { apiRuleData.name = ruleData.name.trim(); } if (ruleData.groupId !== undefined) { apiRuleData.evaluation_point_groups_id = parseInt(ruleData.groupId); } if (ruleData.priority !== undefined) { apiRuleData.risk = ruleData.priority === 'high' ? '高' : ruleData.priority === 'medium' ? '中' : '低'; } if (ruleData.description !== undefined) { apiRuleData.description = ruleData.description; } if (ruleData.isActive !== undefined) { apiRuleData.is_enabled = ruleData.isActive; } // 使用postgrestPut更新评查点 - 使用正确的PostgREST格式 const response = await postgrestPut<{code: number; msg: string; data: ApiRule} | ApiRule[], typeof apiRuleData>('/api/postgrest/proxy/evaluation_points', apiRuleData, { id: parseInt(id) }, token); // 检查是否有错误响应 if (response.error) { return { error: response.error, status: response.status }; } // 处理响应数据(PostgREST可能返回数组或包装对象) let updatedRule: ApiRule | null = null; if (response.data) { // 如果是数组格式(PostgREST标准响应) if (Array.isArray(response.data)) { updatedRule = response.data.length > 0 ? response.data[0] : null; } // 如果是包装对象格式 else if ('data' in response.data && response.data.data) { updatedRule = response.data.data as ApiRule; } } if (!updatedRule) { return { error: '更新成功但无法获取更新后的数据', status: 500 }; } // 将API返回的数据映射到前端模型 const rule = mapApiRuleToFrontendModel(updatedRule); return { data: rule }; } catch (error) { console.error('更新评查点出错:', error); return { error: error instanceof Error ? error.message : '更新评查点失败', status: 500 }; } } /** * 删除评查点 * @param id 评查点ID * @param token JWT token (可选) * @returns 删除结果 */ export async function deleteRule(id: string, token?: string): Promise<{data: {success: boolean; message: string}; error?: never} | {data?: never; error: string; status?: number}> { try { // 调用后端 FastAPI 接口: DELETE /api/v3/evaluation-points/{id} // 后端会处理所有验证逻辑(检查是否存在、是否有关联数据等) const response = await apiRequest<{success: boolean; message: string}>( `/api/v3/evaluation-points/${id}`, { method: 'DELETE', headers: { ...(token ? { 'Authorization': `Bearer ${token}` } : {}) } } ); if (response.error) { return { error: response.error, status: response.status }; } if (response.data && response.data.success) { console.log('✅ deleteRule 成功:', response.data.message); return { data: response.data }; } return { error: '删除评查点失败', status: 500 }; } catch (error) { console.error('❌ 删除评查点出错:', error); return { error: error instanceof Error ? error.message : '删除评查点失败', status: 500 }; } } /** * 复制评查点 * @param id 评查点ID * @param token JWT token (可选) * @returns 新创建的评查点 */ export async function duplicateRule(id: string, token?: string): Promise<{data: Rule; error?: never} | {data?: never; error: string; status?: number}> { try { // 1. 获取原评查点详情 const ruleResponse = await getRule(id, token); if (ruleResponse.error || !ruleResponse.data) { return { error: ruleResponse.error || '获取评查点详情失败', status: 500 }; } // 2. 准备新评查点数据 const rule = ruleResponse.data; // 创建新评查点对象 const newRuleData = { code: `${rule.code}-COPY`, name: `${rule.name} (复制)`, ruleType: rule.ruleType, groupId: rule.groupId, groupName: rule.groupName, priority: rule.priority, description: rule.description, isActive: rule.isActive }; // 3. 创建新评查点 return createRule(newRuleData, token); } catch (error) { console.error('复制评查点出错:', error); return { error: error instanceof Error ? error.message : '复制评查点失败', status: 500 }; } } /** * 评查点类型 */ export interface RuleType { id: string; name: string; description?: string; isEnabled: boolean; } /** * 规则组 */ export interface RuleGroup { id: string; name: string; description?: string; isEnabled: boolean; typeId?: string; // 关联的评查点类型ID } /** * 获取评查点类型列表 * @param documentTypeIds 文档类型 ID 列表 * @param token JWT token (可选) * @returns 评查点类型列表 */ export async function getRuleTypes(documentTypeIds?: number[], token?: string): Promise<{data: RuleType[]; error?: never} | {data?: never; error: string; status?: number}> { try { // 🔑 如果没有传入 documentTypeIds,返回空数组 if (!documentTypeIds || documentTypeIds.length === 0) { console.warn('getRuleTypes: 未提供 documentTypeIds 参数'); return { data: [] }; } const documentTypesParams: PostgrestParams = { select: 'id, name, evaluation_point_groups_ids', filter: { // 🔑 只查询指定的文档类型 ID 'id': `in.(${documentTypeIds.join(',')})` }, token }; const documentTypesResponse = await postgrestGet<{ code: number; msg: string; data: Array<{ id: number; name: string; evaluation_point_groups_ids: number[]; }> }>('/api/postgrest/proxy/document_types', documentTypesParams); if (documentTypesResponse.error) { return { error: documentTypesResponse.error, status: documentTypesResponse.status }; } // 提取 document_types 数据 let documentTypesData: Array<{ id: number; name: string; evaluation_point_groups_ids: number[]; }> = []; if (documentTypesResponse.data && 'code' in documentTypesResponse.data && documentTypesResponse.data.data) { if (Array.isArray(documentTypesResponse.data.data)) { documentTypesData = documentTypesResponse.data.data; } } else if (Array.isArray(documentTypesResponse.data)) { documentTypesData = documentTypesResponse.data as Array<{ id: number; name: string; evaluation_point_groups_ids: number[]; }>; } if (documentTypesData.length === 0) { console.warn('getRuleTypes: 未找到对应的文档类型数据'); return { data: [] }; } // 2️⃣ 提取并组合所有的 evaluation_point_groups_ids const allGroupIds = new Set(); documentTypesData.forEach(docType => { if (Array.isArray(docType.evaluation_point_groups_ids)) { docType.evaluation_point_groups_ids.forEach(id => allGroupIds.add(id)); } }); if (allGroupIds.size === 0) { console.warn('getRuleTypes: 未找到评查点组 ID'); return { data: [] }; } // console.log('📋 [getRuleTypes] 提取的评查点组 IDs:', Array.from(allGroupIds)); // 3️⃣ 根据组合后的 ID 查询 evaluation_point_groups 表 const groupIdsStr = Array.from(allGroupIds).join(','); const groupsParams: PostgrestParams = { select: ` id, pid, code, name, description, is_enabled `, filter: { 'id': `in.(${groupIdsStr})`, 'pid': 'eq.0' }, token }; const response = await postgrestGet<{ code: number; msg: string; data: Array<{ id: number; pid: number; code: string; name: string; description: string; is_enabled: boolean; }> }>('/api/postgrest/proxy/evaluation_point_groups', groupsParams); // 检查是否有错误响应 if (response.error) { return { error: response.error, status: response.status }; } // 处理响应数据 if (response.data && 'code' in response.data && response.data.data) { if (Array.isArray(response.data.data)) { const ruleTypes = response.data.data.map(item => ({ id: item.id.toString(), pid: item.pid.toString(), code: item.code, name: item.name, description: item.description, isEnabled: item.is_enabled })); // console.log('📋 [getRuleTypes] 返回评查点类型:', ruleTypes); return { data: ruleTypes }; } else { return { data: [] }; } } else if (Array.isArray(response.data)) { const ruleTypes = response.data.map(item => ({ id: item.id.toString(), pid: item.pid.toString(), code: item.code, name: item.name, description: item.description, isEnabled: item.is_enabled })); // console.log('📋 [getRuleTypes] 返回评查点类型:', ruleTypes); return { data: ruleTypes }; } else { return { data: [] }; } } catch (error) { console.error('获取评查点类型出错:', error); return { error: error instanceof Error ? error.message : '获取评查点类型失败', status: 500 }; } } /** * 根据评查点类型ID获取规则组列表 * @param typeId 评查点类型ID * @param token JWT token (可选) * @returns 规则组列表 */ export async function getRuleGroupsByType(typeId: string, token?: string): Promise<{data: RuleGroup[]; error?: never} | {data?: never; error: string; status?: number}> { try { // 如果typeId为空或为"全部",则返回空数组 if (!typeId || typeId === 'all') { return { data: [] }; } // 构建PostgrestParams参数 const postgrestParams: PostgrestParams = { select: ` id, pid, code, name, description, is_enabled `, // 查询指定类型ID的规则组 filter: { 'pid': `eq.${typeId}` }, token }; // 发送请求获取规则组列表 const response = await postgrestGet<{code: number; msg: string; data: Array<{ id: number; pid: number; code: string; name: string; description: string; is_enabled: boolean; }>; }>('/api/postgrest/proxy/evaluation_point_groups', postgrestParams); // 检查是否有错误响应 if (response.error) { return { error: response.error, status: response.status }; } // 确保响应数据存在且符合预期格式 if(response.data && 'code' in response.data && response.data.data){ if(Array.isArray(response.data.data) && response.data.data.length > 0){ // 将API返回的数据映射到前端模型 const ruleGroups = response.data.data.map(item => ({ id: item.id.toString(), name: item.name, description: item.description, isEnabled: item.is_enabled, code: item.code })); return { data: ruleGroups }; }else{ return { error: '9000接口返回数据格式不正确', status: 500 }; } }else if(Array.isArray(response.data) && response.data.length > 0){ // console.log("评查点类型列表",response.data); const ruleGroups = response.data.map(item => ({ id: item.id.toString(), name: item.name, description: item.description, isEnabled: item.is_enabled, code: item.code })); return { data: ruleGroups }; }else{ return { error: '3000接口返回数据格式不正确', status: 500 }; } } catch (error) { console.error('获取规则组出错:', error); return { error: error instanceof Error ? error.message : '获取规则组失败', status: 500 }; } } // 定义提取配置类型 interface ExtractionConfigType { llm: { fields: string[]; prompt_setting: { type: string; template: string; }; }; vlm: { fields: Array; prompt_setting: { type: string; template: string; }; }; regex: { fields: Array<{ field: string; pattern: string }>; }; } // 定义转换后的评查点数据类型 interface FormattedEvaluationPoint { id: number; // 修改为只接受 number 类型 name: string; code: string; risk: string; is_enabled: boolean; description: string; references_laws: Record | null; evaluation_point_groups_pid: number | null; evaluation_point_groups_id: number | null; extraction_config: ExtractionConfigType; evaluation_config: { logicType: string; customLogic: string; rules: Array<{ id: string; type: string; config: Record; }>; }; pass_message: string; fail_message: string; suggestion_message: string; suggestion_message_type: string; post_action: string; action_config: string; score: number; } /** * 将 API 返回的数据格式转换为前端需要的格式 * @param apiRule API 返回的评查点数据 * @returns 转换后的前端格式数据 */ export function convertApiRuleToFormData(apiRule: ApiRule): FormattedEvaluationPoint { // 提取旧格式的字段数据 const extractFields = (): ExtractionConfigType => { const fields: ExtractionConfigType = { llm: { fields: [], prompt_setting: { type: 'system', template: '' } }, vlm: { fields: [], prompt_setting: { type: 'system', template: '' } }, regex: { fields: [] } }; if (!apiRule.extraction_config) return fields; // 处理不同的字段类型 if (apiRule.extraction_config.type === 'OCR+LLM' || apiRule.extraction_config.type === 'LLM') { fields.llm.fields = apiRule.extraction_config.fields || []; if (apiRule.extraction_config.prompt_setting) { fields.llm.prompt_setting = { type: apiRule.extraction_config.prompt_setting.type || 'system', template: apiRule.extraction_config.prompt_setting.template || '' }; } } else if (apiRule.extraction_config.type === 'VLM') { fields.vlm.fields = apiRule.extraction_config.fields || []; if (apiRule.extraction_config.prompt_setting) { fields.vlm.prompt_setting = { type: apiRule.extraction_config.prompt_setting.type || 'system', template: apiRule.extraction_config.prompt_setting.template || '' }; } } else if (apiRule.extraction_config.type === 'REGEX') { // 将字符串字段转换为 regex 格式对象 fields.regex.fields = apiRule.extraction_config.fields.map(field => ({ field, pattern: '' })); } return fields; }; // 转换后的数据 const formattedData: FormattedEvaluationPoint = { id: typeof apiRule.id === 'string' ? parseInt(apiRule.id, 10) : apiRule.id, // 确保 id 是数字 name: apiRule.name, code: apiRule.code, risk: apiRule.risk, is_enabled: apiRule.is_enabled, description: apiRule.description, references_laws: apiRule.references_laws || null, evaluation_point_groups_pid: apiRule.evaluation_point_groups?.first_name ? null : null, evaluation_point_groups_id: apiRule.evaluation_point_groups_id, extraction_config: extractFields(), evaluation_config: { logicType: apiRule.evaluation_config?.logicType || 'and', customLogic: '', rules: apiRule.evaluation_config?.rules || [] }, pass_message: apiRule.pass_message || '', fail_message: apiRule.fail_message || '', suggestion_message: apiRule.suggestion_message || '', suggestion_message_type: apiRule.suggestion_message_type || 'warning', post_action: apiRule.post_action || 'none', action_config: apiRule.action_config || '', score: 0 }; return formattedData; } /** * 获取单个评查点数据 * @param id 评查点ID * @returns 评查点数据 */ /** * 获取格式化的评查点数据(用于列表视图) * @param id 评查点ID * @returns 格式化的评查点数据 */ export async function getFormattedEvaluationPoint(id: number): Promise<{ data?: FormattedEvaluationPoint; error?: string; status?: number; }> { try { // console.log(`获取评查点数据,ID: ${id}`); // 使用 postgrestGet 替代直接调用 fetch const postgrestParams: PostgrestParams = { select: `*`, filter: { 'id': `eq.${id}` } }; const response = await postgrestGet<{code: number; msg: string; data: ApiRule[]} | ApiRule[]>('/api/postgrest/proxy/evaluation_points', postgrestParams); if (response.error) { return { error: response.error, status: response.status }; } // 使用 extractApiData 统一处理响应数据 const extractedData = extractApiData(response.data); if (extractedData && Array.isArray(extractedData) && extractedData.length > 0) { // 转换数据为前端格式 const formattedData = convertApiRuleToFormData(extractedData[0]); return { data: formattedData }; } else { return { error: '获取数据失败: 返回数据为空', status: 404 }; } } catch (error) { console.error('获取评查点数据失败:', error); return { error: error instanceof Error ? error.message : '获取评查点数据失败', status: 500 }; } } /** * 获取评查点组数据 * @returns 评查点组列表 */ export async function getEvaluationPointGroups(): Promise<{ data?: Array<{ id: number; pid: number; code: string; name: string; description: string; is_enabled: boolean; created_at: string; updated_at: string; }>; error?: string; status?: number; }> { try { // console.log("获取评查点组数据"); // 使用 postgrestGet 替代直接调用 fetch const postgrestParams: PostgrestParams = { select: `*` }; // 定义评查点组类型 type EvaluationPointGroupType = { id: number; pid: number; code: string; name: string; description: string; is_enabled: boolean; created_at?: string; updated_at?: string; }; const response = await postgrestGet<{code: number; msg: string; data: EvaluationPointGroupType[]} | EvaluationPointGroupType[]>('/api/postgrest/proxy/evaluation_point_groups', postgrestParams); if (response.error) { return { error: response.error, status: response.status }; } // 使用 extractApiData 统一处理响应数据 const extractedData = extractApiData(response.data); if (extractedData) { // 确保每个组都有 created_at 和 updated_at 字段 const currentTime = new Date().toISOString(); const formattedGroups = extractedData.map(group => ({ ...group, created_at: group.created_at || currentTime, updated_at: group.updated_at || currentTime })); return { data: formattedGroups }; } else { return { error: '获取评查点组数据失败: 返回数据为空', status: 404 }; } } catch (error) { console.error('获取评查点组数据失败:', error); return { error: error instanceof Error ? error.message : '获取评查点组数据失败', status: 500 }; } } // 用于评查点输入的接口 interface EvaluationPointInput { id?: number | string; name?: string; code?: string; risk?: string; is_enabled?: boolean; description?: string; references_laws?: Record | null; evaluation_point_groups_pid?: number | null; evaluation_point_groups_id?: number | null; extraction_config?: { llm?: { fields?: string[]; prompt_setting?: { type?: string; template?: string; }; }; vlm?: { fields?: Array; prompt_setting?: { type?: string; template?: string; }; }; regex?: { fields?: Array<{ field: string, pattern: string }>; }; }; evaluation_config?: { logicType?: string; customLogic?: string; rules?: Array<{ id: string; type: string; config: Record; }>; }; pass_message?: string; fail_message?: string; suggestion_message?: string; suggestion_message_type?: string; post_action?: string; action_config?: string; score?: number; } /** * 保存评查点数据 * @param evaluationPoint 评查点数据 * @param isEditMode 是否为编辑模式 * @returns 保存结果 */ export async function saveEvaluationPoint(evaluationPoint: EvaluationPointInput, isEditMode: boolean): Promise<{ data?: FormattedEvaluationPoint[]; error?: string; status?: number; }> { try { // console.log(`${isEditMode ? '更新' : '创建'}评查点数据`); // 创建一个符合数据库模式的数据副本 const cleanedData = { id: evaluationPoint.id, name: evaluationPoint.name?.trim(), code: evaluationPoint.code?.trim(), risk: evaluationPoint.risk || 'low', is_enabled: evaluationPoint.is_enabled !== undefined ? evaluationPoint.is_enabled : true, description: evaluationPoint.description || '', references_laws: evaluationPoint.references_laws || null, evaluation_point_groups_pid: evaluationPoint.evaluation_point_groups_pid || null, evaluation_point_groups_id: evaluationPoint.evaluation_point_groups_id || null, extraction_config: { llm: { fields: Array.isArray(evaluationPoint.extraction_config?.llm?.fields) ? [...evaluationPoint.extraction_config.llm.fields] : [], prompt_setting: { type: evaluationPoint.extraction_config?.llm?.prompt_setting?.type || 'system', template: evaluationPoint.extraction_config?.llm?.prompt_setting?.template || '' } }, vlm: { fields: Array.isArray(evaluationPoint.extraction_config?.vlm?.fields) ? [...evaluationPoint.extraction_config.vlm.fields] : [], prompt_setting: { type: evaluationPoint.extraction_config?.vlm?.prompt_setting?.type || 'system', template: evaluationPoint.extraction_config?.vlm?.prompt_setting?.template || '' } }, regex: { fields: Array.isArray(evaluationPoint.extraction_config?.regex?.fields) ? [...evaluationPoint.extraction_config.regex.fields] : [] } }, evaluation_config: { logicType: evaluationPoint.evaluation_config?.logicType || 'and', customLogic: evaluationPoint.evaluation_config?.customLogic || '', rules: Array.isArray(evaluationPoint.evaluation_config?.rules) ? evaluationPoint.evaluation_config.rules.map((rule) => ({ id: rule.id || '1', type: rule.type || '', config: rule.config || {} })) : [] }, pass_message: evaluationPoint.pass_message || '文档检查通过,符合规范要求。', fail_message: evaluationPoint.fail_message || '文档存在以下问题,请修改后重新提交。', suggestion_message: evaluationPoint.suggestion_message || '', suggestion_message_type: evaluationPoint.suggestion_message_type || 'warning', post_action: evaluationPoint.post_action || 'none', action_config: evaluationPoint.action_config || '', score: evaluationPoint.score !== undefined ? Number(evaluationPoint.score) : 0 }; // 如果是新建模式,则删除id字段 if (!isEditMode) { delete cleanedData.id; } // 确保配置对象中的规则配置被正确处理 if (cleanedData.evaluation_config && Array.isArray(cleanedData.evaluation_config.rules)) { cleanedData.evaluation_config.rules = cleanedData.evaluation_config.rules .filter(rule => rule && rule.type) // 确保规则有类型 .map(rule => { // 根据规则类型确保config中有必要的字段 const config = { ...rule.config }; // 移除辅助字段(同rules.new.tsx中的逻辑) switch (rule.type) { case 'exists': if (!Array.isArray(config.fields)) config.fields = []; if (!config.logic) config.logic = 'and'; delete config.availableFields; delete config.selectedFields; delete config.existsLogic; break; case 'consistency': if (!Array.isArray(config.pairs)) config.pairs = []; if (!config.logic) config.logic = 'and'; delete config.availableFields; delete config.logicRelation; delete config.initialSourceField; delete config.initialTargetField; delete config.initialCompareMethod; break; case 'format': if (!config.field) config.field = ''; if (!config.formatType) config.formatType = 'date'; if (!config.parameters) config.parameters = ''; delete config.availableFields; delete config.checkField; delete config.formatParams; break; case 'logic': if (!Array.isArray(config.conditions)) config.conditions = []; if (!config.logic) config.logic = 'and'; delete config.availableFields; delete config.logicRelation; delete config.initialField; delete config.initialOperator; delete config.initialValue; break; case 'regex': if (!config.field) config.field = ''; if (!config.pattern) config.pattern = ''; if (!config.matchType) config.matchType = 'match'; delete config.availableFields; delete config.checkField; delete config.regexPattern; break; case 'ai': if (!config.model) config.model = 'qwen14b'; if (typeof config.temperature !== 'number') config.temperature = 0.1; if (!config.prompt) config.prompt = ''; delete config.availableFields; break; case 'code': if (!config.language) config.language = 'javascript'; if (!config.code) config.code = ''; delete config.availableFields; break; } return { id: rule.id, type: rule.type, config }; }); } // console.log("准备发送到API的数据大小:", JSON.stringify(cleanedData).length, "字节"); // 使用 postgrest-client 替代直接 fetch 调用 let response; if (isEditMode) { // 更新操作 response = await postgrestPut<{code: number; msg: string; data: ApiRule} | ApiRule, typeof cleanedData>( `/api/postgrest/proxy/evaluation_points`, cleanedData, {id: cleanedData.id!} ); } else { // 创建操作 response = await postgrestPost<{code: number; msg: string; data: ApiRule} | ApiRule, typeof cleanedData>( '/api/postgrest/proxy/evaluation_points', cleanedData ); } // 处理错误响应 if (response.error) { return { error: response.error, status: response.status }; } // 使用 extractApiData 统一处理响应数据 const extractedData = extractApiData(response.data); if (extractedData) { // 转换数据格式后返回 if (Array.isArray(extractedData)) { return { data: extractedData.map(rule => convertApiRuleToFormData(rule)) }; } else { return { data: [convertApiRuleToFormData(extractedData)] }; } } else { return { error: `${isEditMode ? '更新' : '创建'}评查点数据失败: 返回数据为空`, status: 404 }; } } catch (error) { console.error("保存评查点失败:", error); return { error: error instanceof Error ? error.message : '保存评查点失败', status: 500 }; } } /** * 评查点统计信息 */ export interface RuleStatistics { total_count: number; // 总评查点数 enabled_count: number; // 已启用数量 disabled_count: number; // 已禁用数量 by_risk: { // 按风险等级分组 low: number; // 低风险数量 medium: number; // 中风险数量 high: number; // 高风险数量 }; by_group: Array<{ // 按规则组分组 group_id: number; group_name: string; count: number; }>; } /** * 获取评查点统计信息 * @param token JWT token (可选) * @returns 评查点统计数据 */ export async function getRuleStatistics(token?: string): Promise<{data: RuleStatistics; error?: never} | {data?: never; error: string; status?: number}> { try { // 1. 获取所有评查点基本数据(不需要分页) const postgrestParams: PostgrestParams = { select: 'id,is_enabled,risk,evaluation_point_groups_id', token }; const response = await postgrestGet<{code: number; msg: string; data: Array<{ id: number; is_enabled: boolean; risk: string; evaluation_point_groups_id: number | null; }>}>('/api/postgrest/proxy/evaluation_points', postgrestParams); // 检查是否有错误响应 if (response.error) { return { error: response.error, status: response.status }; } // 提取数据 let evaluationPoints: Array<{ id: number; is_enabled: boolean; risk: string; evaluation_point_groups_id: number | null; }> = []; if (response.data && 'code' in response.data && response.data.data) { if (Array.isArray(response.data.data)) { evaluationPoints = response.data.data; } } else if (Array.isArray(response.data)) { evaluationPoints = response.data; } // 2. 计算基础统计 const totalCount = evaluationPoints.length; const enabledCount = evaluationPoints.filter(p => p.is_enabled).length; const disabledCount = totalCount - enabledCount; // 3. 按风险等级统计 const byRisk = { low: evaluationPoints.filter(p => p.risk === '低').length, medium: evaluationPoints.filter(p => p.risk === '中').length, high: evaluationPoints.filter(p => p.risk === '高').length }; // 4. 按规则组统计 const groupCountMap = new Map(); evaluationPoints.forEach(point => { if (point.evaluation_point_groups_id !== null) { const currentCount = groupCountMap.get(point.evaluation_point_groups_id) || 0; groupCountMap.set(point.evaluation_point_groups_id, currentCount + 1); } }); // 5. 获取规则组名称 const groupIds = Array.from(groupCountMap.keys()); const byGroup: Array<{ group_id: number; group_name: string; count: number; }> = []; if (groupIds.length > 0) { // 批量查询规则组信息 const groupsParams: PostgrestParams = { select: 'id,name', filter: { 'id': `in.(${groupIds.join(',')})` }, token }; const groupsResponse = await postgrestGet<{code: number; msg: string; data: Array<{id: number; name: string}>}>('/api/postgrest/proxy/evaluation_point_groups', groupsParams); let groups: Array<{id: number; name: string}> = []; if (groupsResponse.data && 'code' in groupsResponse.data && groupsResponse.data.data) { if (Array.isArray(groupsResponse.data.data)) { groups = groupsResponse.data.data; } } else if (Array.isArray(groupsResponse.data)) { groups = groupsResponse.data; } // 组合统计数据 groups.forEach(group => { byGroup.push({ group_id: group.id, group_name: group.name, count: groupCountMap.get(group.id) || 0 }); }); // 按数量降序排序 byGroup.sort((a, b) => b.count - a.count); } // 返回统计结果 const statistics: RuleStatistics = { total_count: totalCount, enabled_count: enabledCount, disabled_count: disabledCount, by_risk: byRisk, by_group: byGroup }; return { data: statistics }; } catch (error) { console.error('获取评查点统计信息失败:', error); return { error: error instanceof Error ? error.message : '获取评查点统计信息失败', status: 500 }; } } /** * 批量更新评查点启用状态 * @param ids 评查点ID列表 * @param is_enabled 启用状态 * @param token JWT token (可选) * @returns 批量更新结果 */ export async function batchUpdateRuleStatus( ids: string[], is_enabled: boolean, token?: string ): Promise<{ success: boolean; updated_count: number; failed_ids: string[]; errors?: Array<{ id: string; error: string }>; }> { const failedIds: string[] = []; const errors: Array<{ id: string; error: string }> = []; let updatedCount = 0; // 逐个验证并更新 for (const id of ids) { try { // 验证评查点是否存在 const existingRule = await getRule(id, token); if (existingRule.error || !existingRule.data) { failedIds.push(id); errors.push({ id, error: '评查点不存在' }); continue; } // 执行更新 const updateResult = await updateRule(id, { isActive: is_enabled }, token); if (updateResult.error) { failedIds.push(id); errors.push({ id, error: updateResult.error }); } else { updatedCount++; } } catch (error) { failedIds.push(id); errors.push({ id, error: error instanceof Error ? error.message : '更新失败' }); } } return { success: failedIds.length === 0, updated_count: updatedCount, failed_ids: failedIds, errors: errors.length > 0 ? errors : undefined }; } /** * 批量删除评查点 * @param ids 评查点ID列表 * @param token JWT token (可选) * @returns 批量删除结果 */ export async function batchDeleteRules( ids: string[], token?: string ): Promise<{ success: boolean; deleted_count: number; failed_ids: string[]; errors?: Array<{ id: string; error: string }>; }> { const failedIds: string[] = []; const errors: Array<{ id: string; error: string }> = []; let deletedCount = 0; // 逐个验证并删除 for (const id of ids) { try { // 使用增强的 deleteRule 函数(包含关联检查) const deleteResult = await deleteRule(id, token); if (deleteResult.error) { failedIds.push(id); errors.push({ id, error: deleteResult.error }); } else { deletedCount++; } } catch (error) { failedIds.push(id); errors.push({ id, error: error instanceof Error ? error.message : '删除失败' }); } } return { success: failedIds.length === 0, deleted_count: deletedCount, failed_ids: failedIds, errors: errors.length > 0 ? errors : undefined }; } /** * 完整评查点数据结构(对应前端 EvaluationPoint 类型) */ export interface EvaluationPointData { id?: number; name: string; code: string; risk: string; is_enabled: boolean; description?: string; evaluation_point_groups_id: number | null; evaluation_point_groups_pid: number | null; // 🆕 后端新增的分组名称字段 ruleType?: string; // 评查点类型(一级分组名称) groupName?: string; // 所属规则组(二级分组名称) groupId?: string; // 规则组ID(二级分组ID的字符串形式) references_laws: { name: string; content: string; articles: string[]; }; extraction_config: { llm: { fields: string[]; prompt_setting: { type: string; template: string; }; }; vlm: { fields: Array; prompt_setting: { type: string; template: string; }; }; regex: { fields: Array<{ field: string; pattern: string }>; }; }; evaluation_config: { logicType: string; customLogic: string; rules: Array<{ id: string; type: string; config: Record; }>; }; pass_message: string; fail_message: string; suggestion_message?: string; suggestion_message_type: string; post_action?: string; action_config?: string; score: number; area?: string; created_at?: string; updated_at?: string; } /** * 创建完整评查点(调用后端 FastAPI 接口) * @param evaluationPointData 完整的评查点数据 * @param token JWT token (可选) * @returns 创建的评查点数据 */ export async function createEvaluationPoint( evaluationPointData: Omit, token?: string ): Promise<{data: EvaluationPointData; error?: never} | {data?: never; error: string; status?: number}> { try { // 调用后端 FastAPI 接口: POST /api/v3/evaluation-points const response = await apiRequest( '/api/v3/evaluation-points', { method: 'POST', body: JSON.stringify(evaluationPointData), headers: { 'Content-Type': 'application/json', ...(token ? { 'Authorization': `Bearer ${token}` } : {}) } } ); if (response.error) { return { error: response.error, status: response.status }; } if (response.data) { console.log('✅ createEvaluationPoint 成功:', response.data); return { data: response.data }; } return { error: '创建评查点失败:返回数据格式不正确', status: 500 }; } catch (error) { console.error('❌ 创建评查点出错:', error); return { error: error instanceof Error ? error.message : '创建评查点失败', status: 500 }; } } /** * 更新完整评查点(调用后端 FastAPI 接口) * @param id 评查点ID * @param evaluationPointData 完整的评查点数据(部分更新) * @param token JWT token (可选) * @returns 更新后的评查点数据 */ export async function updateEvaluationPoint( id: string, evaluationPointData: Partial>, token?: string ): Promise<{data: EvaluationPointData; error?: never} | {data?: never; error: string; status?: number}> { try { // 调用后端 FastAPI 接口: PUT /api/v3/evaluation-points/{id} const response = await apiRequest( `/api/v3/evaluation-points/${id}`, { method: 'PUT', body: JSON.stringify(evaluationPointData), headers: { 'Content-Type': 'application/json', ...(token ? { 'Authorization': `Bearer ${token}` } : {}) } } ); if (response.error) { return { error: response.error, status: response.status }; } if (response.data) { console.log('✅ updateEvaluationPoint 成功:', response.data); return { data: response.data }; } return { error: '更新评查点失败:返回数据格式不正确', status: 500 }; } catch (error) { console.error('❌ 更新评查点出错:', error); return { error: error instanceof Error ? error.message : '更新评查点失败', status: 500 }; } } /** * 获取完整评查点详情(用于编辑页面) * @param id 评查点ID * @param token JWT token (可选) * @returns 完整的评查点数据 */ export async function getEvaluationPoint( id: string, token?: string ): Promise<{data: EvaluationPointData; error?: never} | {data?: never; error: string; status?: number}> { try { // 调用后端 FastAPI 接口: GET /api/v3/evaluation-points/{id} const response = await apiRequest( `/api/v3/evaluation-points/${id}`, { method: 'GET', headers: { ...(token ? { 'Authorization': `Bearer ${token}` } : {}) } } ); if (response.error) { return { error: response.error, status: response.status }; } if (!response.data) { return { error: '评查点不存在', status: 404 }; } console.log('✅ getEvaluationPoint 成功:', response.data); return { data: response.data }; } catch (error) { console.error('❌ 获取评查点出错:', error); return { error: error instanceof Error ? error.message : '获取评查点失败', status: 500 }; } }