Files
leaudit-platform-frontend/app/api/evaluation_points/rules.ts
T

1250 lines
36 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { postgrestGet, postgrestPost, postgrestPut, postgrestDelete, type PostgrestParams } from '../postgrest-client';
import { apiRequest } from '../axios-client';
import { formatDate } from '../../utils';
import {
getEvaluationPointGroup,
getEvaluationPointGroupChildren,
getEvaluationPointGroupsByDocumentTypes
} from './rule-groups';
/**
* 从不同格式的 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; // 地区过滤
documentAttributeType?: 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;
area?: string;
documentAttributeType?: string;
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
};
}
async function enrichRuleGroupRelation(apiRule: ApiRule, token?: string): Promise<ApiRule> {
if (!apiRule.evaluation_point_groups_id || apiRule.child_group || apiRule.parent_group) {
return apiRule;
}
const groupResponse = await getEvaluationPointGroup(String(apiRule.evaluation_point_groups_id), false, token);
if (groupResponse.error || !groupResponse.data) {
return apiRule;
}
const childGroup = groupResponse.data;
const parentGroupId = childGroup.pid && childGroup.pid !== '0' ? Number(childGroup.pid) : null;
let parentGroup: ApiRule['parent_group'] = null;
if (parentGroupId) {
const parentResponse = await getEvaluationPointGroup(String(parentGroupId), false, token);
if (!parentResponse.error && parentResponse.data) {
parentGroup = {
id: Number(parentResponse.data.id),
name: parentResponse.data.name
};
}
}
return {
...apiRule,
evaluation_point_groups_pid: parentGroupId ?? apiRule.evaluation_point_groups_pid ?? null,
child_group: {
id: Number(childGroup.id),
name: childGroup.name
},
parent_group: parentGroup
};
}
async function enrichEvaluationPointGroupRelation(
point: EvaluationPointData,
token?: string
): Promise<EvaluationPointData> {
if (!point.evaluation_point_groups_id) {
return point;
}
const childResponse = await getEvaluationPointGroup(String(point.evaluation_point_groups_id), false, token);
if (childResponse.error || !childResponse.data) {
return point;
}
const childGroup = childResponse.data;
const parentGroupId = childGroup.pid && childGroup.pid !== '0' ? Number(childGroup.pid) : null;
let ruleType = point.ruleType || '';
if (parentGroupId) {
const parentResponse = await getEvaluationPointGroup(String(parentGroupId), false, token);
if (!parentResponse.error && parentResponse.data) {
ruleType = parentResponse.data.name;
}
}
return {
...point,
evaluation_point_groups_pid: parentGroupId ?? point.evaluation_point_groups_pid ?? null,
groupId: String(childGroup.id),
groupName: childGroup.name,
ruleType
};
}
/**
* 获取评查点列表
* @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,
documentAttributeType,
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());
}
// 添加子类型过滤(原 document_attribute_type
if (documentAttributeType) {
queryParams.append('document_attribute_type', documentAttributeType);
}
// 🔑 添加地区过滤
// 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) {
if (response.status === 404) {
return await getRulesListFromPostgrest(params);
}
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 || '',
documentAttributeType: point.document_attribute_type || ''
};
});
// console.log('✅ [getRulesList] 成功映射评查点列表数据', response.data.data[0]);
return {
data: {
rules: mappedRules,
totalCount: response.data.total
}
};
} catch (error) {
console.error('❌ 获取评查点列表出错:', error);
if (error instanceof Error && error.message.includes('404')) {
return await getRulesListFromPostgrest(params);
}
return {
error: error instanceof Error ? error.message : '获取评查点列表失败',
status: 500
};
}
}
async function getRulesListFromPostgrest(
params: RulesQueryParams
): Promise<{data: RulesListResponse; error?: never} | {data?: never; error: string; status?: number}> {
const {
page = 1,
pageSize = 10,
ruleType,
groupId,
risk,
isActive,
keyword,
area,
documentAttributeType,
token
} = params;
const postgrestParams: PostgrestParams = {
select: 'id,code,name,area,evaluation_point_groups_id,evaluation_point_groups_pid,risk,description,is_enabled,document_attribute_type,created_at,updated_at,child_group:evaluation_point_groups!fk_evaluation_points_group(id,name),parent_group:evaluation_point_groups!fk_evaluation_points_parent_group(id,name)',
order: 'updated_at.desc',
limit: pageSize,
offset: (page - 1) * pageSize,
token,
filter: {}
};
if (ruleType) postgrestParams.filter!.evaluation_point_groups_pid = `eq.${ruleType}`;
if (groupId) postgrestParams.filter!.evaluation_point_groups_id = `eq.${groupId}`;
if (risk) postgrestParams.filter!.risk = `eq.${risk}`;
if (isActive !== undefined) postgrestParams.filter!.is_enabled = `eq.${isActive}`;
if (area) postgrestParams.filter!.area = `eq.${area}`;
if (documentAttributeType) postgrestParams.filter!.document_attribute_type = `eq.${documentAttributeType}`;
if (keyword?.trim()) {
const safeKeyword = keyword.trim().replace(/,/g, ' ');
postgrestParams.or = `name.ilike.*${safeKeyword}*,code.ilike.*${safeKeyword}*`;
}
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 };
}
let rows: ApiRule[] = [];
if (Array.isArray(response.data)) {
rows = response.data;
} else if (response.data && 'data' in response.data && Array.isArray(response.data.data)) {
rows = response.data.data;
}
return {
data: {
rules: rows.map(mapApiRuleToFrontendModel),
totalCount: rows.length
}
};
}
/**
* 获取单个评查点详情
* @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 {
apiRule = await enrichRuleGroupRelation(apiRule, token);
} 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 {
if (!documentTypeIds || documentTypeIds.length === 0) {
return { data: [] };
}
const response = await getEvaluationPointGroupsByDocumentTypes(documentTypeIds, token);
if (response.error) {
return { error: response.error, status: response.status };
}
return {
data: (response.data || []).map(item => ({
id: item.id,
pid: item.pid,
code: item.code || '',
name: item.name,
description: item.description || '',
isEnabled: item.is_enabled
}))
};
} 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 {
if (!typeId || typeId === 'all') {
return { data: [] };
}
const response = await getEvaluationPointGroupChildren(typeId, { pageSize: 500 }, token);
if (response.error) {
if (response.status === 404) {
return {
data: [
{ code: '通用', label: '通用' }
]
};
}
return { error: response.error, status: response.status };
}
const ruleGroups = (response.data || []).map(item => ({
id: item.id,
name: item.name,
description: item.description,
isEnabled: item.is_enabled,
code: item.code
}));
return { data: ruleGroups };
} 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.parent_group?.id || apiRule.evaluation_point_groups_pid || 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的字符串形式)
document_attribute_type?: string; // 文档属性类型
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 };
}
const enrichedData = await enrichEvaluationPointGroupRelation(response.data, token);
console.log('✅ getEvaluationPoint 成功:', enrichedData);
return { data: enrichedData };
} catch (error) {
console.error('❌ 获取评查点出错:', error);
return {
error: error instanceof Error ? error.message : '获取评查点失败',
status: 500
};
}
}
/**
* 适用属性类型选项
*/
export interface AttributeTypeOption {
code: string;
label: string;
}
/**
* 获取适用属性类型列表
* 从后端获取当前评查点表中所有已使用的 document_attribute_type 去重列表
* @param token JWT token (可选)
* @returns 适用属性类型列表
*/
export async function getAttributeTypes(
token?: string
): Promise<{data: AttributeTypeOption[]; error?: never} | {data?: never; error: string; status?: number}> {
try {
// 调用后端 FastAPI 接口: GET /api/v3/evaluation-points/attribute-types
const response = await apiRequest<{
types: AttributeTypeOption[];
}>(
'/api/v3/evaluation-points/attribute-types',
{
method: 'GET',
headers: {
...(token ? { 'Authorization': `Bearer ${token}` } : {})
}
}
);
if (response.error) {
return { error: response.error, status: response.status };
}
if (!response.data || !Array.isArray(response.data.types)) {
// 返回默认的属性类型列表
return {
data: [
{ code: 'ALL', label: '通用' }
]
};
}
console.log('✅ getAttributeTypes 成功:', response.data.types);
return { data: response.data.types };
} catch (error) {
console.error('❌ 获取适用属性类型列表出错:', error);
// 出错时返回默认列表,不阻塞用户操作
return {
data: [
{ code: 'ALL', label: '通用' }
]
};
}
}