diff --git a/app/routes/rules-new.tsx b/app/routes/rules-new.tsx index 2515587..72d0792 100644 --- a/app/routes/rules-new.tsx +++ b/app/routes/rules-new.tsx @@ -36,14 +36,19 @@ import rulesStyles from "~/styles/rules.css?url"; import { useNavigate, useLocation } from "@remix-run/react"; // 导入评查点模型定义和常量 import type { - EvaluationPoint + EvaluationPoint, + LogicOperator, + CompareMethod, + FormatType, + ComparisonOperator, + MatchType, + ProgrammingLanguage } from "~/models/evaluation_points"; import { EVALUATION_OPTIONS } from "~/models/evaluation_points"; import type { EvaluationPointGroup } from "~/models/evaluation_point_groups"; // 导入RuleContext上下文 import { RuleContext } from "~/contexts/RuleContext"; -// 导入API函数 -import { getEvaluationPoint, getEvaluationPointGroups, saveEvaluationPoint } from "~/api/evaluation_points/rules"; +import { postgrestGet, postgrestPost, postgrestPut } from "~/api/postgrest-client"; export const meta: MetaFunction = () => { return [ @@ -55,14 +60,76 @@ export const meta: MetaFunction = () => { ]; }; -export const handle = { - breadcrumb: "评查点管理" -}; - export function links() { return [{ rel: "stylesheet", href: rulesStyles }]; } +export const handle = { + breadcrumb: "评查点管理" +}; + +// 添加规则配置接口 +interface BaseRuleConfig { + availableFields?: string[]; +} + +interface ExistsRuleConfig extends BaseRuleConfig { + fields: string[]; + logic: LogicOperator; + selectedFields?: string[]; + existsLogic?: string; +} + +interface ConsistencyRuleConfig extends BaseRuleConfig { + pairs: Array<{sourceField: string; targetField: string; compareMethod: CompareMethod}>; + logic: LogicOperator; + logicRelation?: string; + initialSourceField?: string; + initialTargetField?: string; + initialCompareMethod?: string; +} + +interface FormatRuleConfig extends BaseRuleConfig { + field: string; + formatType: FormatType; + parameters: string; + checkField?: string; + formatParams?: string; +} + +interface LogicRuleConfig extends BaseRuleConfig { + conditions: { + field: string; + operator: ComparisonOperator; + value: string; + }[]; + logic: LogicOperator; + logicRelation?: string; + initialField?: string; + initialOperator?: string; + initialValue?: string; +} + +interface RegexRuleConfig extends BaseRuleConfig { + field: string; + pattern: string; + matchType: MatchType; + checkField?: string; + regexPattern?: string; +} + +interface AIRuleConfig extends BaseRuleConfig { + model: string; + temperature: number; + prompt: string; +} + +interface CodeRuleConfig extends BaseRuleConfig { + language: ProgrammingLanguage; + code: string; +} + +type RuleConfig = ExistsRuleConfig | ConsistencyRuleConfig | FormatRuleConfig | LogicRuleConfig | RegexRuleConfig | AIRuleConfig | CodeRuleConfig; export default function RuleNew() { const navigate = useNavigate(); @@ -169,31 +236,34 @@ export default function RuleNew() { try { setIsLoading(true); console.log(`获取评查点数据,ID: ${id}`); - - const response = await getEvaluationPoint(id); - - if (response.error) { - console.error('获取评查点数据失败:', response.error); - alert(`获取评查点数据失败: ${response.error}`); - resetFormData(); - navigate('/rules'); - return; + // 使用 postgrestGet 替代直接调用 fetch + const postgrestParams = { + filter: { + 'id': `eq.${id}` } - - if (response.data) { - setFormData(response.data as EvaluationPoint); + }; + const response = await postgrestGet('evaluation_points', postgrestParams); + + if (response.data && Array.isArray(response.data) && response.data[0]) { - // 初始化extractionFields - const extractedFields = extractFieldsFromFormData(response.data as EvaluationPoint); - setExtractionFields(extractedFields); - - // 设置编辑模式的实例键 - setInstanceKey(`edit_${id}_${Date.now()}`); + if (response.data.length > 0) { + const data = response.data[0] + setFormData(data); + + // 初始化extractionFields + const extractedFields = extractFieldsFromFormData(data); + setExtractionFields(extractedFields); + + // 设置编辑模式的实例键 + setInstanceKey(`edit_${id}_${Date.now()}`); + } else { + console.error('获取数据失败: 返回数据为空'); + alert('获取数据失败: 返回数据为空'); + resetFormData(); + navigate('/rules'); + } } else { - console.error('获取数据失败: 返回数据为空'); - alert('获取数据失败: 返回数据为空'); - resetFormData(); - navigate('/rules'); + throw new Error(`响应状态: ${response.error}`); } } catch (error) { console.error('获取评查点数据失败:', error); @@ -213,20 +283,10 @@ export default function RuleNew() { const fetchEvaluationPointGroups = useCallback(async () => { try { console.log("获取评查点组数据"); - - const response = await getEvaluationPointGroups(); - - if (response.error) { - console.error('获取评查点组数据失败:', response.error); - alert(`获取评查点组数据失败: ${response.error}\n将使用默认数据`); - return; - } - - if (response.data) { + const response = await postgrestGet('evaluation_point_groups'); + + if (response.data && Array.isArray(response.data) && response.data.length > 0) { setEvaluationPointGroups(response.data); - } else { - console.error('获取评查点组数据失败: 返回数据为空'); - alert('获取评查点组数据失败: 返回数据为空\n将使用默认数据'); } } catch (error) { console.error('获取评查点组数据失败:', error); @@ -235,7 +295,7 @@ export default function RuleNew() { } }, []); - const handleSave = () => { + const handleSave = async () => { console.log("保存评查点", formData); // 验证必填字段 @@ -252,42 +312,202 @@ export default function RuleNew() { // 显示保存中状态 setIsLoading(true); - // 调用API保存数据 - saveEvaluationPoint(formData, isEditMode) - .then(response => { - if (response.error) { - console.error("保存评查点失败:", response.error); - alert(`保存评查点失败: ${response.error}`); - return; - } - - if (response.data) { - // 获取新创建或更新的评查点ID - const savedPointId = response.data[0]?.id; - - if (savedPointId) { - // 显示成功消息 - alert(`评查点${isEditMode ? '更新' : '创建'}成功!`); - - // 保存成功后跳转到编辑页面,加载刚保存的数据 - navigate(`/rules/new?id=${savedPointId}`); - } else { - // 无法获取ID的情况 - alert(`评查点${isEditMode ? '更新' : '创建'}成功,但无法获取ID。正在返回列表页面。`); - navigate('/rules'); + try { + // 创建一个符合数据库模式的数据副本 + const cleanedData = { + id: formData.id, + name: formData.name?.trim(), + code: formData.code?.trim(), + risk: formData.risk || 'low', + is_enabled: formData.is_enabled !== undefined ? formData.is_enabled : true, + description: formData.description || '', + references_laws: formData.references_laws || null, + evaluation_point_groups_pid: formData.evaluation_point_groups_pid || null, + evaluation_point_groups_id: formData.evaluation_point_groups_id || null, + extraction_config: { + llm: { + fields: Array.isArray(formData.extraction_config?.llm?.fields) ? + [...formData.extraction_config.llm.fields] : [], + prompt_setting: { + type: formData.extraction_config?.llm?.prompt_setting?.type || 'system', + template: formData.extraction_config?.llm?.prompt_setting?.template || '' + } + }, + vlm: { + fields: Array.isArray(formData.extraction_config?.vlm?.fields) ? + [...formData.extraction_config.vlm.fields] : [], + prompt_setting: { + type: formData.extraction_config?.vlm?.prompt_setting?.type || 'system', + template: formData.extraction_config?.vlm?.prompt_setting?.template || '' + } + }, + regex: { + fields: Array.isArray(formData.extraction_config?.regex?.fields) ? + [...formData.extraction_config.regex.fields] : [] } + }, + evaluation_config: { + logicType: formData.evaluation_config?.logicType || 'and', + customLogic: formData.evaluation_config?.customLogic || '', + rules: Array.isArray(formData.evaluation_config?.rules) ? + formData.evaluation_config.rules.map(rule => ({ + id: rule.id || '1', + type: rule.type || '', + config: rule.config || {} + })) : [] + }, + pass_message: formData.pass_message || '文档检查通过,符合规范要求。', + fail_message: formData.fail_message || '文档存在以下问题,请修改后重新提交。', + suggestion_message: formData.suggestion_message || '', + suggestion_message_type: formData.suggestion_message_type || 'warning', + post_action: formData.post_action || 'none', + action_config: formData.action_config || '', + score: formData.score !== undefined ? Number(formData.score) : 0 + }; + + // 确保rules中的每个配置对象都被正确处理 + 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 } as RuleConfig; + + switch (rule.type) { + case 'exists': + if (!Array.isArray((config as ExistsRuleConfig).fields)) (config as ExistsRuleConfig).fields = []; + if (!(config as ExistsRuleConfig).logic) (config as ExistsRuleConfig).logic = 'and'; + // 删除不必要的字段 + delete (config as ExistsRuleConfig & {availableFields?: string}).availableFields; + delete (config as ExistsRuleConfig).selectedFields; + delete (config as ExistsRuleConfig).existsLogic; + break; + + case 'consistency': + if (!Array.isArray((config as ConsistencyRuleConfig).pairs)) (config as ConsistencyRuleConfig).pairs = []; + if (!(config as ConsistencyRuleConfig).logic) (config as ConsistencyRuleConfig).logic = 'and'; + delete (config as ConsistencyRuleConfig & {availableFields?: string}).availableFields; + delete (config as ConsistencyRuleConfig).logicRelation; + delete (config as ConsistencyRuleConfig).initialSourceField; + delete (config as ConsistencyRuleConfig).initialTargetField; + delete (config as ConsistencyRuleConfig).initialCompareMethod; + break; + + case 'format': + if (!(config as FormatRuleConfig).field) (config as FormatRuleConfig).field = ''; + if (!(config as FormatRuleConfig).formatType) (config as FormatRuleConfig).formatType = 'date'; + if (!(config as FormatRuleConfig).parameters) (config as FormatRuleConfig).parameters = ''; + delete (config as FormatRuleConfig & {availableFields?: string}).availableFields; + delete (config as FormatRuleConfig).checkField; + delete (config as FormatRuleConfig).formatParams; + break; + + case 'logic': + if (!Array.isArray((config as LogicRuleConfig).conditions)) (config as LogicRuleConfig).conditions = []; + if (!(config as LogicRuleConfig).logic) (config as LogicRuleConfig).logic = 'and'; + delete (config as LogicRuleConfig & {availableFields?: string}).availableFields; + delete (config as LogicRuleConfig).logicRelation; + delete (config as LogicRuleConfig).initialField; + delete (config as LogicRuleConfig).initialOperator; + delete (config as LogicRuleConfig).initialValue; + break; + + case 'regex': + if (!(config as RegexRuleConfig).field) (config as RegexRuleConfig).field = ''; + if (!(config as RegexRuleConfig).pattern) (config as RegexRuleConfig).pattern = ''; + if (!(config as RegexRuleConfig).matchType) (config as RegexRuleConfig).matchType = 'match'; + delete (config as RegexRuleConfig & {availableFields?: string}).availableFields; + delete (config as RegexRuleConfig).checkField; + delete (config as RegexRuleConfig).regexPattern; + break; + + case 'ai': + if (!(config as AIRuleConfig).model) (config as AIRuleConfig).model = 'qwen14b'; + if (typeof (config as AIRuleConfig).temperature !== 'number') (config as AIRuleConfig).temperature = 0.1; + if (!(config as AIRuleConfig).prompt) (config as AIRuleConfig).prompt = ''; + delete (config as AIRuleConfig & {availableFields?: string}).availableFields; + break; + + case 'code': + if (!(config as CodeRuleConfig).language) (config as CodeRuleConfig).language = 'javascript'; + if (!(config as CodeRuleConfig).code) (config as CodeRuleConfig).code = ''; + delete (config as CodeRuleConfig & {availableFields?: string}).availableFields; + break; + } + + return { + id: rule.id, + type: rule.type, + config + }; + }); + } + + // 如果是新建模式,则删除id字段 + if (!isEditMode) { + delete cleanedData.id; + } + + // 确保extraction_config和evaluation_config是有效的JSON对象 + // 通过先序列化再解析来验证 + try { + JSON.parse(JSON.stringify(cleanedData.extraction_config)); + } catch (e) { + throw new Error("extraction_config 格式无效"); + } + + try { + JSON.parse(JSON.stringify(cleanedData.evaluation_config)); + } catch (e) { + throw new Error("evaluation_config 格式无效"); + } + + // 检查JSON字符串长度,如果太长可能会被截断 + const jsonData = JSON.stringify(cleanedData); + const maxLength = 65536; // 通常PostgreSQL的jsonb列可以存储的最大长度 + + if (jsonData.length > maxLength) { + throw new Error(`数据大小超过限制 (${jsonData.length} > ${maxLength})`); + } + + console.log("准备提交到API的数据:", cleanedData); + console.log("JSON数据长度:", jsonData.length); + + let response; + + if (isEditMode) { + response = await postgrestPut('evaluation_points', cleanedData, {id: formData.id!}); + } else { + response = await postgrestPost('evaluation_points', cleanedData); + } + + if (response.error) { + alert(`系统繁忙: ${response.error}`); + } else if (response.data && Array.isArray(response.data) && response.data.length > 0) { + // 获取新创建或更新的评查点ID + const savedPointId = response.data[0]?.id; + + if (savedPointId) { + // 显示成功消息 + alert(`评查点${isEditMode ? '更新' : '创建'}成功!`); + + // 保存成功后跳转到编辑页面并重新加载数据 + navigate(`/rules-new?id=${savedPointId}`, { replace: true }); + // 重新获取评查点数据 + await fetchEvaluationPoint(savedPointId); } else { - alert(`保存成功,但返回数据为空。正在返回列表页面。`); + // 无法获取ID的情况 + alert(`评查点${isEditMode ? '更新' : '创建'}成功,但无法获取ID。正在返回列表页面。`); navigate('/rules'); } - }) - .catch(error => { - console.error("保存评查点出错:", error); - alert(`保存评查点出错: ${error instanceof Error ? error.message : '未知错误'}`); - }) - .finally(() => { - setIsLoading(false); - }); + } else { + alert(`系统繁忙`); + } + } catch (error) { + console.error("数据处理错误:", error); + alert(`数据处理错误: ${error instanceof Error ? error.message : '未知错误'}`); + setIsLoading(false); + } } const handleSaveDraft = () => {