616f059f1e
2. 修复交叉评查的任务中的文档列表的历史文档的查看跳转路径。 3. 修复评查点新增中评查点类型只能显示当前文档类型绑定的这几个一级分组。评查点类型=一级分组。 4. 修复文档列表关于pdf的下载失败的问题。
1220 lines
35 KiB
TypeScript
1220 lines
35 KiB
TypeScript
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<T>(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<string, unknown>;
|
|
extraction_config?: {
|
|
type: string;
|
|
fields: string[];
|
|
prompt_setting?: {
|
|
type: string;
|
|
template: string;
|
|
};
|
|
};
|
|
evaluation_config?: {
|
|
rules: Array<{
|
|
id: string;
|
|
type: string;
|
|
config: Record<string, unknown>;
|
|
}>;
|
|
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<string, string> = {
|
|
'高': '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);
|
|
}
|
|
|
|
// console.log('🔍 [getRulesList] 查询参数:', queryParams.toString());
|
|
|
|
// 调用 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<string, string> = {
|
|
'高': '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 || ''),
|
|
area: point.area || ''
|
|
};
|
|
});
|
|
// console.log('✅ [getRulesList] 成功映射评查点列表数据', response.data.data[0]);
|
|
|
|
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 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
|
|
};
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* 评查点类型
|
|
*/
|
|
export interface RuleType {
|
|
id: string;
|
|
pid?: string;
|
|
code?: 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<number>();
|
|
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返回的数据映射到前端模型
|
|
// console.log("评查点类型列表",response.data);
|
|
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<string | { name: string; type: string }>;
|
|
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<string, unknown> | 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<string, unknown>;
|
|
}>;
|
|
};
|
|
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;
|
|
}
|
|
|
|
|
|
// 用于评查点输入的接口
|
|
interface EvaluationPointInput {
|
|
id?: number | string;
|
|
name?: string;
|
|
code?: string;
|
|
risk?: string;
|
|
is_enabled?: boolean;
|
|
description?: string;
|
|
references_laws?: Record<string, unknown> | 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<string | { name: string, type: string }>;
|
|
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<string, unknown>;
|
|
}>;
|
|
};
|
|
pass_message?: string;
|
|
fail_message?: string;
|
|
suggestion_message?: string;
|
|
suggestion_message_type?: string;
|
|
post_action?: string;
|
|
action_config?: string;
|
|
score?: number;
|
|
}
|
|
|
|
|
|
/**
|
|
* 评查点统计信息
|
|
*/
|
|
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 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 {
|
|
// 执行更新 - 使用 updateEvaluationPoint,只传入 is_enabled 字段
|
|
const updateResult = await updateEvaluationPoint(id, { 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<string | { name: string; type: string }>;
|
|
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<string, unknown>;
|
|
}>;
|
|
};
|
|
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<EvaluationPointData, 'id' | 'created_at' | 'updated_at'>,
|
|
token?: string
|
|
): Promise<{data: EvaluationPointData; error?: never} | {data?: never; error: string; status?: number}> {
|
|
try {
|
|
// 调用后端 FastAPI 接口: POST /api/v3/evaluation-points
|
|
const response = await apiRequest<EvaluationPointData>(
|
|
'/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<Omit<EvaluationPointData, 'created_at' | 'updated_at'>>,
|
|
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<EvaluationPointData>(
|
|
`/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<EvaluationPointData>(
|
|
`/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
|
|
};
|
|
}
|
|
} |