diff --git a/app/api/evaluation_points/rules.ts b/app/api/evaluation_points/rules.ts index 73d01a8..5db0c44 100644 --- a/app/api/evaluation_points/rules.ts +++ b/app/api/evaluation_points/rules.ts @@ -509,14 +509,69 @@ export async function getRule(id: string, token?: string): Promise<{data: Rule; */ 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}> }>('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: ruleData.code, - name: ruleData.name, + 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, + description: ruleData.description || '', + is_enabled: ruleData.isActive !== undefined ? ruleData.isActive : true, // 以下是默认值,实际应用中需要根据业务逻辑设置 references_laws: {}, extraction_config: { @@ -534,27 +589,27 @@ export async function createRule(ruleData: Omit('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 { + return { error: error instanceof Error ? error.message : '创建评查点失败', status: 500 }; @@ -570,53 +625,112 @@ export async function createRule(ruleData: Omit>, 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}> }>('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; + apiRuleData.code = ruleData.code.trim(); } - + if (ruleData.name !== undefined) { - apiRuleData.name = ruleData.name; + 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更新评查点 const response = await postgrestPut<{code: number; msg: string; data: ApiRule}, typeof apiRuleData>(`evaluation_points/${id}`, apiRuleData, undefined, 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 { + return { error: error instanceof Error ? error.message : '更新评查点失败', status: 500 };