From e235532469fae7d7a704fae39bf4c33049bcc33e Mon Sep 17 00:00:00 2001 From: awen Date: Thu, 10 Apr 2025 02:23:55 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9A=82=E5=AD=982?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/components/rules/new/ReviewSettings.tsx | 229 +++++++++---- app/routes/rules.new.tsx | 352 ++++++++++++++++---- 2 files changed, 454 insertions(+), 127 deletions(-) diff --git a/app/components/rules/new/ReviewSettings.tsx b/app/components/rules/new/ReviewSettings.tsx index f277db9..56fdeb7 100644 --- a/app/components/rules/new/ReviewSettings.tsx +++ b/app/components/rules/new/ReviewSettings.tsx @@ -1532,76 +1532,165 @@ export function ReviewSettings({ const generateEvaluationConfig = useCallback(() => { // 创建符合数据库模式的evaluation_config对象 const evaluationConfig = { - logicType: combinationLogic, - customLogic: combinationLogic === 'custom' ? customLogic : '', - rules: rules.map(rule => { - // 创建一个深拷贝以避免修改原始对象 - const processedConfig = JSON.parse(JSON.stringify(rule.config || {})); - - // 根据规则类型处理特定的字段映射 - switch(rule.type) { - case 'exists': - // 将UI字段名映射为API字段名 - if (processedConfig.selectedFields) { - processedConfig.fields = processedConfig.selectedFields; - delete processedConfig.selectedFields; - } - if (processedConfig.existsLogic) { - processedConfig.logic = processedConfig.existsLogic; - delete processedConfig.existsLogic; - } - break; - - case 'consistency': - case 'logic': - // 将UI字段名映射为API字段名 - if (processedConfig.logicRelation) { - processedConfig.logic = processedConfig.logicRelation; - delete processedConfig.logicRelation; - } - // 删除用于UI的临时字段 - delete processedConfig.initialSourceField; - delete processedConfig.initialTargetField; - delete processedConfig.initialCompareMethod; - delete processedConfig.initialField; - delete processedConfig.initialOperator; - delete processedConfig.initialValue; - break; - - case 'format': - // 确保field字段正确设置 - if (processedConfig.checkField) { - processedConfig.field = processedConfig.checkField; - delete processedConfig.checkField; - } - if (processedConfig.formatParams) { - processedConfig.parameters = processedConfig.formatParams; - delete processedConfig.formatParams; - } - break; - - case 'regex': - // 确保field和pattern字段正确设置 - if (processedConfig.checkField) { - processedConfig.field = processedConfig.checkField; - delete processedConfig.checkField; - } - if (processedConfig.regexPattern) { - processedConfig.pattern = processedConfig.regexPattern; - delete processedConfig.regexPattern; - } - break; - } - - // 移除辅助用的UI字段 - delete processedConfig.availableFields; - - return { - id: rule.id, - type: rule.type, - config: processedConfig - }; - }).filter(rule => rule.type && rule.type.trim() !== '') + logicType: combinationLogic || 'and', + customLogic: combinationLogic === 'custom' ? (customLogic || '') : '', + rules: rules + .filter(rule => rule.type && rule.type.trim() !== '') + .map(rule => { + // 创建一个深拷贝以避免修改原始对象 + const processedConfig = JSON.parse(JSON.stringify(rule.config || {})); + + // 根据规则类型处理特定的字段映射 + switch(rule.type) { + case 'exists': + // 将UI字段名映射为API字段名 + if (Array.isArray(processedConfig.selectedFields)) { + processedConfig.fields = processedConfig.selectedFields; + delete processedConfig.selectedFields; + } + + // 确保fields字段存在且是数组 + if (!Array.isArray(processedConfig.fields)) { + processedConfig.fields = []; + } + + if (processedConfig.existsLogic) { + processedConfig.logic = processedConfig.existsLogic; + delete processedConfig.existsLogic; + } + + // 确保logic字段有默认值 + if (!processedConfig.logic) { + processedConfig.logic = 'and'; + } + break; + + case 'consistency': + case 'logic': + // 将UI字段名映射为API字段名 + if (processedConfig.logicRelation) { + processedConfig.logic = processedConfig.logicRelation; + delete processedConfig.logicRelation; + } + + // 确保logic字段有默认值 + if (!processedConfig.logic) { + processedConfig.logic = 'and'; + } + + // 确保pairs/conditions字段是数组 + if (rule.type === 'consistency' && !Array.isArray(processedConfig.pairs)) { + processedConfig.pairs = []; + } + + if (rule.type === 'logic' && !Array.isArray(processedConfig.conditions)) { + processedConfig.conditions = []; + } + + // 删除用于UI的临时字段 + delete processedConfig.initialSourceField; + delete processedConfig.initialTargetField; + delete processedConfig.initialCompareMethod; + delete processedConfig.initialField; + delete processedConfig.initialOperator; + delete processedConfig.initialValue; + break; + + case 'format': + // 确保field字段正确设置 + if (processedConfig.checkField) { + processedConfig.field = processedConfig.checkField; + delete processedConfig.checkField; + } + + // 确保field字段有值 + if (!processedConfig.field) { + processedConfig.field = ''; + } + + if (processedConfig.formatParams) { + processedConfig.parameters = processedConfig.formatParams; + delete processedConfig.formatParams; + } + + // 确保formatType字段有值 + if (!processedConfig.formatType) { + processedConfig.formatType = ''; + } + + // 确保parameters字段有值 + if (!processedConfig.parameters) { + processedConfig.parameters = ''; + } + break; + + case 'regex': + // 确保field和pattern字段正确设置 + if (processedConfig.checkField) { + processedConfig.field = processedConfig.checkField; + delete processedConfig.checkField; + } + + // 确保field字段有值 + if (!processedConfig.field) { + processedConfig.field = ''; + } + + if (processedConfig.regexPattern) { + processedConfig.pattern = processedConfig.regexPattern; + delete processedConfig.regexPattern; + } + + // 确保pattern字段有值 + if (!processedConfig.pattern) { + processedConfig.pattern = ''; + } + + // 确保matchType字段有值 + if (!processedConfig.matchType) { + processedConfig.matchType = 'match'; + } + break; + + case 'ai': + // 确保model字段有值 + if (!processedConfig.model) { + processedConfig.model = 'qwen14b'; + } + + // 确保temperature字段是数字 + if (typeof processedConfig.temperature !== 'number') { + processedConfig.temperature = 0.1; + } + + // 确保prompt字段有值 + if (!processedConfig.prompt) { + processedConfig.prompt = ''; + } + break; + + case 'code': + // 确保language字段有值 + if (!processedConfig.language) { + processedConfig.language = 'javascript'; + } + + // 确保code字段有值 + if (!processedConfig.code) { + processedConfig.code = ''; + } + break; + } + + // 移除辅助用的UI字段 + delete processedConfig.availableFields; + + return { + id: rule.id, + type: rule.type, + config: processedConfig + }; + }) }; // 使用setTimeout避免连锁更新 diff --git a/app/routes/rules.new.tsx b/app/routes/rules.new.tsx index 30924cc..d3ed68e 100644 --- a/app/routes/rules.new.tsx +++ b/app/routes/rules.new.tsx @@ -36,7 +36,13 @@ 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"; @@ -61,6 +67,68 @@ 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(); @@ -192,63 +260,208 @@ export default function RuleNew() { ? `http://127.0.0.1:9000/admin/evaluation_points?id=eq.${formData.id}` : 'http://127.0.0.1:9000/admin/evaluation_points'; - // 创建一个符合数据库模式的数据副本 - const cleanedData = { - id: formData.id, - name: formData.name, - code: formData.code, - risk: formData.risk, - is_enabled: formData.is_enabled !== undefined ? formData.is_enabled : true, - description: formData.description || '', - references_laws: formData.references_laws, - evaluation_point_groups_pid: formData.evaluation_point_groups_pid, - evaluation_point_groups_id: formData.evaluation_point_groups_id, - extraction_config: formData.extraction_config, - evaluation_config: formData.evaluation_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 ? formData.score : 0 - }; - - // 如果是新建模式,则删除id字段 - if (!isEditMode) { - delete cleanedData.id; - } - - console.log("准备提交到API的数据:", cleanedData); - - // 发送数据到API - fetch(apiUrl, { - method: apiMethod, - headers: { - 'Accept': 'application/json', - 'Content-Type': 'application/json' - }, - body: JSON.stringify(cleanedData) - }) - .then(response => { - if (!response.ok) { - throw new Error(`API错误: ${response.status}`); + 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 + }; + }); } - return response.json(); - }) - .then(data => { - console.log("保存成功:", data); - alert(`评查点${isEditMode ? '更新' : '创建'}成功!`); - // 保存成功后返回列表页 - navigate('/rules'); - }) - .catch(error => { - console.error("保存失败:", error); - alert(`保存失败: ${error.message}`); - }) - .finally(() => { + + // 如果是新建模式,则删除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); + + // 发送数据到API + fetch(apiUrl, { + method: apiMethod, + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json' + }, + body: JSON.stringify(cleanedData) + }) + .then(async response => { + if (!response.ok) { + // 尝试获取详细错误信息 + let errorText = ''; + try { + const errorData = await response.json(); + errorText = JSON.stringify(errorData); + } catch (e) { + errorText = await response.text(); + } + throw new Error(`API错误 (${response.status}): ${errorText}`); + } + return response.json(); + }) + .then(data => { + console.log("保存成功:", data); + alert(`评查点${isEditMode ? '更新' : '创建'}成功!`); + // 保存成功后返回列表页 + navigate('/rules'); + }) + .catch(error => { + console.error("保存失败:", error); + alert(`保存失败: ${error.message}`); + }) + .finally(() => { + setIsLoading(false); + }); + } catch (error) { + console.error("数据处理错误:", error); + alert(`数据处理错误: ${error instanceof Error ? error.message : '未知错误'}`); setIsLoading(false); - }); + } } const handleSaveDraft = () => { @@ -293,7 +506,32 @@ export default function RuleNew() { }; const handleReviewSettingsChange = (data: Record) => { - setFormData(prev => ({ ...prev, ...data })); + console.log("评查设置变更:", data); + + // 检查数据中是否包含evaluation_config对象 + if (data.evaluation_config) { + // 确保formData.evaluation_config存在并具有必要的默认属性 + const currentConfig = formData.evaluation_config || { + logicType: 'and', + customLogic: '', + rules: [] + }; + + // 合并评查配置数据 + const mergedConfig = { + ...currentConfig, + ...(data.evaluation_config as object) + }; + + // 更新表单数据 + setFormData(prev => ({ + ...prev, + evaluation_config: mergedConfig + })); + } else { + // 处理其他普通字段 + setFormData(prev => ({ ...prev, ...data })); + } } /**