d3b9403d64
## 主要改进 ### 1. 增强 getRuleGroups 函数 - ✅ 添加完整的分页参数支持 (page, pageSize) - ✅ 添加筛选参数 (name, code, is_enabled, pid) - ✅ 添加排序参数 (orderBy, order) - ✅ 返回总数 (totalCount) - ✅ 支持一级分组和二级分组查询 ### 2. 优化 getChildGroups 函数 - ✅ 内部使用改进后的 getRuleGroups 函数 - ✅ 自动添加评查点数量统计 - ✅ 改进类型安全性 ### 3. 优化 getRuleGroup 函数 - ✅ 确保评查点数量统计准确 - ✅ 改进错误处理 - ✅ 优化类型守卫逻辑 ### 4. 类型定义改进 - ✅ 新增 RuleGroupQueryParams 接口 - ✅ ApiRuleGroup.pid 类型支持 null - ✅ 修复所有 TypeScript 类型错误 ### 5. 创建对接计划文档 - ✅ 详细的 API 对接实施计划 - ✅ 分模块逐步实施策略 - ✅ 验收标准和风险评估 ## 相关文件 - app/api/evaluation_points/rule-groups.ts - docs/evaluation/API对接实施计划.md ## 验收清单 - [x] TypeScript 类型检查通过 - [x] 支持分页、筛选、排序 - [x] 返回评查点数量统计 - [x] 向后兼容现有代码 Co-Authored-By: Claude <noreply@anthropic.com>
1488 lines
33 KiB
Markdown
1488 lines
33 KiB
Markdown
# 评查点系统 API v3 对接实施计划
|
||
|
||
> **制定日期**: 2025-11-25
|
||
> **版本**: v1.0
|
||
> **目标**: 逐步对接评查点分组和评查点管理的 API v3 接口,确保前端与后端完全兼容
|
||
|
||
---
|
||
|
||
## 📋 目录
|
||
|
||
1. [项目概况](#项目概况)
|
||
2. [模块 1:评查点分组管理](#模块-1评查点分组管理)
|
||
3. [模块 2:评查点管理](#模块-2评查点管理)
|
||
4. [验收标准](#验收标准)
|
||
5. [风险与注意事项](#风险与注意事项)
|
||
|
||
---
|
||
|
||
## 📊 项目概况
|
||
|
||
### 涉及文件
|
||
|
||
**文档**:
|
||
- `docs/evaluation/evaluation_point_groups.md` - 分组 API 文档
|
||
- `docs/evaluation/evaluation_points.md` - 评查点 API 文档
|
||
|
||
**API 客户端**:
|
||
- `app/api/evaluation_points/rule-groups.ts`
|
||
- `app/api/evaluation_points/rules.ts`
|
||
|
||
**路由组件**:
|
||
- `app/routes/rule-groups._index.tsx`
|
||
- `app/routes/rule-groups.new.tsx`
|
||
- `app/routes/rules.list.tsx`
|
||
- `app/routes/rules.new.tsx`
|
||
|
||
### 技术栈
|
||
|
||
- **后端**: PostgreSQL + PostgREST
|
||
- **前端**: Remix + React + TypeScript
|
||
- **API 协议**: RESTful API (PostgREST 规范)
|
||
|
||
---
|
||
|
||
## 模块 1:评查点分组管理
|
||
|
||
### 📌 当前状态分析
|
||
|
||
**已实现功能**:
|
||
- ✅ 获取分组列表(含子分组)
|
||
- ✅ 获取单个分组详情
|
||
- ✅ 创建分组
|
||
- ✅ 更新分组
|
||
- ✅ 删除分组
|
||
|
||
**缺失功能**:
|
||
- ❌ 批量启用/禁用分组
|
||
- ❌ 批量删除分组
|
||
- ❌ 统计信息接口
|
||
|
||
---
|
||
|
||
### 阶段 1.1:查询接口对接 ⏱️ 1-2 天
|
||
|
||
#### 任务清单
|
||
|
||
**1. 更新 `getRuleGroups` 函数**
|
||
|
||
**文件**: `app/api/evaluation_points/rule-groups.ts`
|
||
|
||
**当前实现**:
|
||
```typescript
|
||
export async function getRuleGroups(token?: string): Promise<ApiResponse<RuleGroup[]>> {
|
||
const response = await postgrestGet('evaluation_point_groups', {
|
||
filter: { 'pid': 'is.null' },
|
||
select: '*',
|
||
order: { created_at: 'desc' },
|
||
token
|
||
});
|
||
// ...
|
||
}
|
||
```
|
||
|
||
**需要改进**:
|
||
- ✅ 已使用 PostgREST 接口
|
||
- ⚠️ 缺少分页参数支持
|
||
- ⚠️ 缺少筛选条件支持(名称、编码、状态)
|
||
|
||
**改进方案**:
|
||
```typescript
|
||
export interface RuleGroupQueryParams {
|
||
// 分页
|
||
page?: number;
|
||
pageSize?: number;
|
||
|
||
// 筛选
|
||
name?: string; // 模糊搜索
|
||
code?: string; // 模糊搜索
|
||
is_enabled?: boolean;
|
||
pid?: string | null; // 父级ID,null表示一级分组
|
||
|
||
// 排序
|
||
orderBy?: 'created_at' | 'updated_at' | 'name' | 'code';
|
||
order?: 'asc' | 'desc';
|
||
|
||
token?: string;
|
||
}
|
||
|
||
export async function getRuleGroups(
|
||
params?: RuleGroupQueryParams
|
||
): Promise<ApiResponse<RuleGroup[]>> {
|
||
const {
|
||
page = 1,
|
||
pageSize = 50,
|
||
name,
|
||
code,
|
||
is_enabled,
|
||
pid,
|
||
orderBy = 'created_at',
|
||
order = 'desc',
|
||
token
|
||
} = params || {};
|
||
|
||
const filter: Record<string, string> = {};
|
||
|
||
// 构建筛选条件
|
||
if (name) filter['name'] = `ilike.*${name}*`;
|
||
if (code) filter['code'] = `ilike.*${code}*`;
|
||
if (is_enabled !== undefined) filter['is_enabled'] = `eq.${is_enabled}`;
|
||
if (pid === null) {
|
||
filter['pid'] = 'is.null';
|
||
} else if (pid) {
|
||
filter['pid'] = `eq.${pid}`;
|
||
}
|
||
|
||
const response = await postgrestGet('evaluation_point_groups', {
|
||
filter,
|
||
select: '*',
|
||
order: { [orderBy]: order },
|
||
range: { from: (page - 1) * pageSize, to: page * pageSize - 1 },
|
||
token
|
||
});
|
||
|
||
return response;
|
||
}
|
||
```
|
||
|
||
**验收标准**:
|
||
- [ ] 支持分页(page, pageSize)
|
||
- [ ] 支持名称模糊搜索
|
||
- [ ] 支持编码模糊搜索
|
||
- [ ] 支持状态筛选
|
||
- [ ] 支持获取一级分组(pid=null)或二级分组(pid=具体ID)
|
||
- [ ] 返回总数(通过 PostgREST 的 `Prefer: count=exact` header)
|
||
|
||
---
|
||
|
||
**2. 更新 `getChildGroups` 函数**
|
||
|
||
**当前实现**:
|
||
```typescript
|
||
export async function getChildGroups(parentId: string, token?: string): Promise<ApiResponse<RuleGroup[]>> {
|
||
const response = await postgrestGet('evaluation_point_groups', {
|
||
filter: { 'pid': `eq.${parentId}` },
|
||
select: '*',
|
||
order: { created_at: 'desc' },
|
||
token
|
||
});
|
||
// ...
|
||
}
|
||
```
|
||
|
||
**需要改进**:
|
||
- ✅ 已使用 PostgREST 接口
|
||
- ⚠️ 建议合并到 `getRuleGroups` 函数(通过 `pid` 参数区分)
|
||
|
||
**改进方案**:
|
||
```typescript
|
||
// 删除独立的 getChildGroups 函数,统一使用 getRuleGroups
|
||
|
||
// 使用示例:
|
||
// 获取一级分组
|
||
const level1Groups = await getRuleGroups({ pid: null, token });
|
||
|
||
// 获取指定父级的子分组
|
||
const childGroups = await getRuleGroups({ pid: parentId, token });
|
||
```
|
||
|
||
**验收标准**:
|
||
- [ ] `getChildGroups` 函数已废弃
|
||
- [ ] 路由组件已更新为使用 `getRuleGroups({ pid: parentId })`
|
||
|
||
---
|
||
|
||
**3. 更新 `getRuleGroup` 函数(获取单个分组详情)**
|
||
|
||
**当前实现**:
|
||
```typescript
|
||
export async function getRuleGroup(id: string, token?: string): Promise<ApiResponse<RuleGroup>> {
|
||
const response = await postgrestGet('evaluation_point_groups', {
|
||
filter: { 'id': `eq.${id}` },
|
||
select: '*',
|
||
token
|
||
});
|
||
// ...
|
||
}
|
||
```
|
||
|
||
**需要改进**:
|
||
- ✅ 已使用 PostgREST 接口
|
||
- ⚠️ 缺少统计信息(评查点数量)
|
||
|
||
**改进方案**:
|
||
```typescript
|
||
export async function getRuleGroup(id: string, token?: string): Promise<ApiResponse<RuleGroup>> {
|
||
// 方案 1:使用 PostgREST 的关联查询
|
||
const response = await postgrestGet('evaluation_point_groups', {
|
||
filter: { 'id': `eq.${id}` },
|
||
select: '*, evaluation_points(count)',
|
||
token
|
||
});
|
||
|
||
// 方案 2:分两次查询
|
||
// 1. 获取分组信息
|
||
// 2. 查询该分组下的评查点数量
|
||
|
||
return response;
|
||
}
|
||
```
|
||
|
||
**验收标准**:
|
||
- [ ] 返回分组详细信息
|
||
- [ ] 包含该分组下的评查点数量统计
|
||
|
||
---
|
||
|
||
### 阶段 1.2:创建/更新接口对接 ⏱️ 1 天
|
||
|
||
#### 任务清单
|
||
|
||
**1. 更新 `createRuleGroup` 函数**
|
||
|
||
**当前实现**:
|
||
```typescript
|
||
export async function createRuleGroup(
|
||
data: RuleGroupCreateUpdateDto,
|
||
token?: string
|
||
): Promise<ApiResponse<RuleGroup>> {
|
||
const response = await postgrestPost('evaluation_point_groups', data, token);
|
||
// ...
|
||
}
|
||
```
|
||
|
||
**验证清单**:
|
||
- [ ] 必填字段验证(name, code)
|
||
- [ ] 编码唯一性验证
|
||
- [ ] 父级ID验证(如果是二级分组)
|
||
- [ ] 返回新创建的分组完整信息
|
||
|
||
---
|
||
|
||
**2. 更新 `updateRuleGroup` 函数**
|
||
|
||
**当前实现**:
|
||
```typescript
|
||
export async function updateRuleGroup(
|
||
id: string,
|
||
data: RuleGroupCreateUpdateDto,
|
||
token?: string
|
||
): Promise<ApiResponse<RuleGroup>> {
|
||
const response = await postgrestPut(
|
||
'evaluation_point_groups',
|
||
data,
|
||
{ id },
|
||
token
|
||
);
|
||
// ...
|
||
}
|
||
```
|
||
|
||
**验证清单**:
|
||
- [ ] ID 有效性验证
|
||
- [ ] 不允许修改 `pid`(防止分组层级混乱)
|
||
- [ ] 编码唯一性验证(排除自身)
|
||
- [ ] 返回更新后的分组完整信息
|
||
|
||
---
|
||
|
||
### 阶段 1.3:删除接口对接 ⏱️ 0.5 天
|
||
|
||
#### 任务清单
|
||
|
||
**1. 更新 `deleteRuleGroup` 函数**
|
||
|
||
**当前实现**:
|
||
```typescript
|
||
export async function deleteRuleGroup(id: string, token?: string): Promise<ApiResponse<void>> {
|
||
const response = await postgrestDelete('evaluation_point_groups', { id }, token);
|
||
// ...
|
||
}
|
||
```
|
||
|
||
**需要改进**:
|
||
- ⚠️ 缺少级联删除提示
|
||
- ⚠️ 需要检查是否有关联的评查点
|
||
|
||
**改进方案**:
|
||
```typescript
|
||
export async function deleteRuleGroup(id: string, token?: string): Promise<ApiResponse<void>> {
|
||
// 1. 检查是否有子分组
|
||
const childGroups = await getRuleGroups({ pid: id, token });
|
||
if (childGroups.data && childGroups.data.length > 0) {
|
||
return {
|
||
success: false,
|
||
error: '该分组下存在子分组,请先删除子分组',
|
||
status: 400
|
||
};
|
||
}
|
||
|
||
// 2. 检查是否有关联的评查点
|
||
const points = await postgrestGet('evaluation_points', {
|
||
filter: { 'evaluation_point_groups_id': `eq.${id}` },
|
||
select: 'count',
|
||
token
|
||
});
|
||
|
||
if (points.data && points.data.length > 0) {
|
||
return {
|
||
success: false,
|
||
error: '该分组下存在评查点,请先删除或移动评查点',
|
||
status: 400
|
||
};
|
||
}
|
||
|
||
// 3. 执行删除
|
||
const response = await postgrestDelete('evaluation_point_groups', { id }, token);
|
||
return response;
|
||
}
|
||
```
|
||
|
||
**验收标准**:
|
||
- [ ] 删除前检查子分组
|
||
- [ ] 删除前检查关联评查点
|
||
- [ ] 提供清晰的错误提示
|
||
- [ ] 删除成功后返回成功状态
|
||
|
||
---
|
||
|
||
### 阶段 1.4:批量操作接口对接 ⏱️ 1 天
|
||
|
||
#### 任务清单
|
||
|
||
**1. 新增 `batchUpdateRuleGroupStatus` 函数**
|
||
|
||
```typescript
|
||
export interface BatchUpdateStatusDto {
|
||
ids: string[];
|
||
is_enabled: boolean;
|
||
}
|
||
|
||
export async function batchUpdateRuleGroupStatus(
|
||
data: BatchUpdateStatusDto,
|
||
token?: string
|
||
): Promise<ApiResponse<{ updated_count: number }>> {
|
||
const { ids, is_enabled } = data;
|
||
|
||
const response = await postgrestPatch(
|
||
'evaluation_point_groups',
|
||
{ is_enabled },
|
||
{ id: `in.(${ids.join(',')})` },
|
||
token
|
||
);
|
||
|
||
return {
|
||
success: true,
|
||
data: {
|
||
updated_count: response.data?.length || 0
|
||
}
|
||
};
|
||
}
|
||
```
|
||
|
||
**验收标准**:
|
||
- [ ] 支持批量启用/禁用
|
||
- [ ] 返回更新数量
|
||
- [ ] 处理部分失败的情况
|
||
|
||
---
|
||
|
||
**2. 新增 `batchDeleteRuleGroups` 函数**
|
||
|
||
```typescript
|
||
export async function batchDeleteRuleGroups(
|
||
ids: string[],
|
||
token?: string
|
||
): Promise<ApiResponse<{ deleted_count: number; failed_ids: string[] }>> {
|
||
const failedIds: string[] = [];
|
||
let deletedCount = 0;
|
||
|
||
for (const id of ids) {
|
||
const result = await deleteRuleGroup(id, token);
|
||
if (result.success) {
|
||
deletedCount++;
|
||
} else {
|
||
failedIds.push(id);
|
||
}
|
||
}
|
||
|
||
return {
|
||
success: failedIds.length === 0,
|
||
data: {
|
||
deleted_count: deletedCount,
|
||
failed_ids: failedIds
|
||
}
|
||
};
|
||
}
|
||
```
|
||
|
||
**验收标准**:
|
||
- [ ] 支持批量删除
|
||
- [ ] 返回删除成功数量
|
||
- [ ] 返回删除失败的 ID 列表
|
||
- [ ] 提供详细的失败原因
|
||
|
||
---
|
||
|
||
### 阶段 1.5:前端组件更新 ⏱️ 2 天
|
||
|
||
#### 任务清单
|
||
|
||
**1. 更新 `rule-groups._index.tsx`**
|
||
|
||
**需要改进的功能**:
|
||
|
||
a) **分页功能**
|
||
```typescript
|
||
// 当前:无分页
|
||
// 改进:添加分页组件
|
||
|
||
const [pagination, setPagination] = useState({ page: 1, pageSize: 50 });
|
||
|
||
// 在 loader 中使用分页参数
|
||
const response = await getRuleGroups({
|
||
page: pagination.page,
|
||
pageSize: pagination.pageSize,
|
||
...filters,
|
||
token: frontendJWT
|
||
});
|
||
```
|
||
|
||
b) **筛选功能优化**
|
||
```typescript
|
||
// 当前:客户端筛选
|
||
// 改进:服务端筛选
|
||
|
||
const handleFilterChange = (filters: RuleGroupQueryParams) => {
|
||
const newParams = new URLSearchParams();
|
||
if (filters.name) newParams.set('name', filters.name);
|
||
if (filters.code) newParams.set('code', filters.code);
|
||
if (filters.is_enabled !== undefined) {
|
||
newParams.set('is_enabled', filters.is_enabled.toString());
|
||
}
|
||
setSearchParams(newParams);
|
||
};
|
||
```
|
||
|
||
c) **批量操作功能**
|
||
```typescript
|
||
// 新增批量选择状态
|
||
const [selectedIds, setSelectedIds] = useState<string[]>([]);
|
||
|
||
// 批量启用/禁用
|
||
const handleBatchEnable = async (enable: boolean) => {
|
||
const result = await batchUpdateRuleGroupStatus({
|
||
ids: selectedIds,
|
||
is_enabled: enable
|
||
}, frontendJWT);
|
||
|
||
if (result.success) {
|
||
toastService.success(`已${enable ? '启用' : '禁用'} ${result.data.updated_count} 个分组`);
|
||
// 刷新列表
|
||
}
|
||
};
|
||
|
||
// 批量删除
|
||
const handleBatchDelete = async () => {
|
||
messageService.show({
|
||
title: "确认批量删除",
|
||
message: `确认删除选中的 ${selectedIds.length} 个分组吗?`,
|
||
type: "warning",
|
||
onConfirm: async () => {
|
||
const result = await batchDeleteRuleGroups(selectedIds, frontendJWT);
|
||
toastService.success(`成功删除 ${result.data.deleted_count} 个分组`);
|
||
if (result.data.failed_ids.length > 0) {
|
||
toastService.warning(`有 ${result.data.failed_ids.length} 个分组删除失败`);
|
||
}
|
||
// 刷新列表
|
||
}
|
||
});
|
||
};
|
||
```
|
||
|
||
**验收标准**:
|
||
- [ ] 表格支持多选(Checkbox)
|
||
- [ ] 顶部批量操作按钮区域
|
||
- [ ] 批量启用/禁用功能正常
|
||
- [ ] 批量删除功能正常
|
||
- [ ] 分页功能正常
|
||
- [ ] 服务端筛选功能正常
|
||
|
||
---
|
||
|
||
**2. 更新 `rule-groups.new.tsx`**
|
||
|
||
**需要改进的功能**:
|
||
|
||
a) **表单验证增强**
|
||
```typescript
|
||
// 添加异步验证:检查编码唯一性
|
||
const validateCodeUnique = async (code: string, currentId?: string) => {
|
||
const response = await getRuleGroups({ code, token: frontendJWT });
|
||
if (response.data && response.data.length > 0) {
|
||
// 编辑模式下排除自身
|
||
if (currentId && response.data[0].id === currentId) {
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
return true;
|
||
};
|
||
```
|
||
|
||
b) **父级分组选择优化**
|
||
```typescript
|
||
// 当前:手动过滤
|
||
// 改进:使用 API 筛选
|
||
|
||
// 获取一级分组(用于二级分组的父级选择)
|
||
const parentGroupsResponse = await getRuleGroups({
|
||
pid: null,
|
||
is_enabled: true,
|
||
token: frontendJWT
|
||
});
|
||
```
|
||
|
||
**验收标准**:
|
||
- [ ] 编码唯一性实时验证
|
||
- [ ] 父级分组列表仅显示一级分组
|
||
- [ ] 父级分组列表仅显示启用状态的分组
|
||
- [ ] 保存成功后正确跳转
|
||
|
||
---
|
||
|
||
## 模块 2:评查点管理
|
||
|
||
### 📌 当前状态分析
|
||
|
||
**已实现功能**:
|
||
- ✅ 获取评查点列表
|
||
- ✅ 获取单个评查点详情
|
||
- ✅ 创建评查点
|
||
- ✅ 更新评查点
|
||
- ✅ 删除评查点
|
||
- ✅ 复制评查点
|
||
|
||
**缺失功能**:
|
||
- ❌ 批量启用/禁用评查点
|
||
- ❌ 批量删除评查点
|
||
- ❌ 统计信息接口
|
||
- ❌ 评查点使用记录查询
|
||
|
||
---
|
||
|
||
### 阶段 2.1:查询接口对接 ⏱️ 2 天
|
||
|
||
#### 任务清单
|
||
|
||
**1. 更新 `getRulesList` 函数**
|
||
|
||
**文件**: `app/api/evaluation_points/rules.ts`
|
||
|
||
**当前实现**:
|
||
```typescript
|
||
export async function getRulesList(params: {
|
||
ruleType?: string;
|
||
groupId?: string;
|
||
isActive?: boolean;
|
||
keyword?: string;
|
||
area?: string;
|
||
page?: number;
|
||
pageSize?: number;
|
||
token?: string;
|
||
}): Promise<ApiResponse<{ rules: ApiRule[]; totalCount: number }>> {
|
||
// ...
|
||
}
|
||
```
|
||
|
||
**需要改进**:
|
||
- ✅ 已支持分页
|
||
- ✅ 已支持筛选
|
||
- ⚠️ 缺少排序参数
|
||
- ⚠️ 返回格式需要优化
|
||
|
||
**改进方案**:
|
||
```typescript
|
||
export interface RuleQueryParams {
|
||
// 分页
|
||
page?: number;
|
||
pageSize?: number;
|
||
|
||
// 筛选
|
||
keyword?: string; // 名称或编码模糊搜索
|
||
evaluation_point_groups_pid?: string; // 评查点类型(一级分组)
|
||
evaluation_point_groups_id?: string; // 所属规则组(二级分组)
|
||
risk?: 'low' | 'medium' | 'high'; // 风险等级
|
||
is_enabled?: boolean; // 启用状态
|
||
area?: string; // 地区过滤
|
||
|
||
// 排序
|
||
orderBy?: 'created_at' | 'updated_at' | 'name' | 'code' | 'usage_count';
|
||
order?: 'asc' | 'desc';
|
||
|
||
token?: string;
|
||
}
|
||
|
||
export async function getRulesList(
|
||
params?: RuleQueryParams
|
||
): Promise<ApiResponse<{ rules: EvaluationPoint[]; totalCount: number }>> {
|
||
const {
|
||
page = 1,
|
||
pageSize = 10,
|
||
keyword,
|
||
evaluation_point_groups_pid,
|
||
evaluation_point_groups_id,
|
||
risk,
|
||
is_enabled,
|
||
area,
|
||
orderBy = 'created_at',
|
||
order = 'desc',
|
||
token
|
||
} = params || {};
|
||
|
||
const filter: Record<string, string> = {};
|
||
|
||
// 构建筛选条件
|
||
if (keyword) {
|
||
// PostgREST 不支持 OR 条件,需要使用 or 语法
|
||
filter['or'] = `(name.ilike.*${keyword}*,code.ilike.*${keyword}*)`;
|
||
}
|
||
if (evaluation_point_groups_pid) {
|
||
filter['evaluation_point_groups_pid'] = `eq.${evaluation_point_groups_pid}`;
|
||
}
|
||
if (evaluation_point_groups_id) {
|
||
filter['evaluation_point_groups_id'] = `eq.${evaluation_point_groups_id}`;
|
||
}
|
||
if (risk) filter['risk'] = `eq.${risk}`;
|
||
if (is_enabled !== undefined) filter['is_enabled'] = `eq.${is_enabled}`;
|
||
|
||
// 地区过滤(需要通过 code 字段的后缀实现)
|
||
if (area) {
|
||
filter['code'] = `like.*--${area}`;
|
||
}
|
||
|
||
const response = await postgrestGet('evaluation_points', {
|
||
filter,
|
||
select: '*,evaluation_point_groups:evaluation_point_groups_id(*)',
|
||
order: { [orderBy]: order },
|
||
range: { from: (page - 1) * pageSize, to: page * pageSize - 1 },
|
||
count: 'exact',
|
||
token
|
||
});
|
||
|
||
if (response.data) {
|
||
return {
|
||
success: true,
|
||
data: {
|
||
rules: response.data,
|
||
totalCount: response.count || 0
|
||
}
|
||
};
|
||
}
|
||
|
||
return response;
|
||
}
|
||
```
|
||
|
||
**验收标准**:
|
||
- [ ] 支持关键词搜索(名称或编码)
|
||
- [ ] 支持按评查点类型筛选
|
||
- [ ] 支持按规则组筛选
|
||
- [ ] 支持按风险等级筛选
|
||
- [ ] 支持按启用状态筛选
|
||
- [ ] 支持按地区筛选
|
||
- [ ] 支持排序(创建时间、更新时间、使用次数等)
|
||
- [ ] 返回总数(用于分页)
|
||
- [ ] 关联查询规则组信息
|
||
|
||
---
|
||
|
||
**2. 新增 `getRuleStatistics` 函数(统计信息)**
|
||
|
||
```typescript
|
||
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: string;
|
||
group_name: string;
|
||
count: number;
|
||
}>;
|
||
}
|
||
|
||
export async function getRuleStatistics(
|
||
token?: string
|
||
): Promise<ApiResponse<RuleStatistics>> {
|
||
// 使用 PostgREST 的聚合查询
|
||
const response = await postgrestGet('evaluation_points', {
|
||
select: `
|
||
count,
|
||
is_enabled,
|
||
risk,
|
||
evaluation_point_groups_id
|
||
`,
|
||
token
|
||
});
|
||
|
||
// 处理响应数据,计算统计信息
|
||
// ...
|
||
|
||
return {
|
||
success: true,
|
||
data: statistics
|
||
};
|
||
}
|
||
```
|
||
|
||
**验收标准**:
|
||
- [ ] 返回总评查点数
|
||
- [ ] 返回启用/禁用数量
|
||
- [ ] 返回按风险等级分组的数量
|
||
- [ ] 返回按规则组分组的数量
|
||
|
||
---
|
||
|
||
### 阶段 2.2:创建/更新接口对接 ⏱️ 2 天
|
||
|
||
#### 任务清单
|
||
|
||
**1. 更新 `createRule` 函数**
|
||
|
||
**当前实现**:
|
||
```typescript
|
||
// 当前通过 postgrestPost 直接创建
|
||
const response = await postgrestPost('evaluation_points', finalData, frontendJWT);
|
||
```
|
||
|
||
**需要改进**:
|
||
- ⚠️ 缺少数据验证
|
||
- ⚠️ 缺少 JSONB 字段格式验证
|
||
|
||
**改进方案**:
|
||
```typescript
|
||
export interface CreateRuleDto {
|
||
name: string;
|
||
code: string;
|
||
risk: 'low' | 'medium' | 'high';
|
||
is_enabled?: boolean;
|
||
description?: string;
|
||
references_laws?: {
|
||
name: string;
|
||
articles: string[];
|
||
content: string;
|
||
};
|
||
evaluation_point_groups_pid: string;
|
||
evaluation_point_groups_id: 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: 'and' | 'or' | 'custom';
|
||
customLogic?: string;
|
||
rules: Array<{
|
||
id: string;
|
||
type: string;
|
||
config: Record<string, unknown>;
|
||
}>;
|
||
};
|
||
pass_message?: string;
|
||
fail_message?: string;
|
||
suggestion_message?: string;
|
||
suggestion_message_type?: 'info' | 'warning' | 'error';
|
||
post_action?: string;
|
||
action_config?: string;
|
||
score?: number;
|
||
}
|
||
|
||
export async function createRule(
|
||
data: CreateRuleDto,
|
||
token?: string
|
||
): Promise<ApiResponse<EvaluationPoint>> {
|
||
// 1. 验证必填字段
|
||
if (!data.name || !data.code) {
|
||
return {
|
||
success: false,
|
||
error: '名称和编码不能为空',
|
||
status: 400
|
||
};
|
||
}
|
||
|
||
// 2. 验证编码唯一性
|
||
const existingRule = await getRulesList({
|
||
keyword: data.code,
|
||
token
|
||
});
|
||
if (existingRule.data && existingRule.data.rules.length > 0) {
|
||
return {
|
||
success: false,
|
||
error: '评查点编码已存在',
|
||
status: 400
|
||
};
|
||
}
|
||
|
||
// 3. 验证 JSONB 字段格式
|
||
try {
|
||
JSON.parse(JSON.stringify(data.extraction_config));
|
||
JSON.parse(JSON.stringify(data.evaluation_config));
|
||
} catch (error) {
|
||
return {
|
||
success: false,
|
||
error: 'extraction_config 或 evaluation_config 格式无效',
|
||
status: 400
|
||
};
|
||
}
|
||
|
||
// 4. 执行创建
|
||
const response = await postgrestPost('evaluation_points', data, token);
|
||
return response;
|
||
}
|
||
```
|
||
|
||
**验收标准**:
|
||
- [ ] 必填字段验证
|
||
- [ ] 编码唯一性验证
|
||
- [ ] JSONB 字段格式验证
|
||
- [ ] 返回新创建的评查点完整信息
|
||
|
||
---
|
||
|
||
**2. 更新 `updateRule` 函数**
|
||
|
||
**改进方案**:
|
||
```typescript
|
||
export async function updateRule(
|
||
id: string,
|
||
data: Partial<CreateRuleDto>,
|
||
token?: string
|
||
): Promise<ApiResponse<EvaluationPoint>> {
|
||
// 1. 验证 ID 有效性
|
||
const existing = await postgrestGet('evaluation_points', {
|
||
filter: { id: `eq.${id}` },
|
||
token
|
||
});
|
||
|
||
if (!existing.data || existing.data.length === 0) {
|
||
return {
|
||
success: false,
|
||
error: '评查点不存在',
|
||
status: 404
|
||
};
|
||
}
|
||
|
||
// 2. 验证编码唯一性(排除自身)
|
||
if (data.code) {
|
||
const duplicate = await getRulesList({
|
||
keyword: data.code,
|
||
token
|
||
});
|
||
if (duplicate.data && duplicate.data.rules.length > 0) {
|
||
const isDuplicate = duplicate.data.rules.some(r => r.id !== id);
|
||
if (isDuplicate) {
|
||
return {
|
||
success: false,
|
||
error: '评查点编码已存在',
|
||
status: 400
|
||
};
|
||
}
|
||
}
|
||
}
|
||
|
||
// 3. 验证 JSONB 字段格式
|
||
if (data.extraction_config || data.evaluation_config) {
|
||
try {
|
||
if (data.extraction_config) {
|
||
JSON.parse(JSON.stringify(data.extraction_config));
|
||
}
|
||
if (data.evaluation_config) {
|
||
JSON.parse(JSON.stringify(data.evaluation_config));
|
||
}
|
||
} catch (error) {
|
||
return {
|
||
success: false,
|
||
error: 'extraction_config 或 evaluation_config 格式无效',
|
||
status: 400
|
||
};
|
||
}
|
||
}
|
||
|
||
// 4. 执行更新
|
||
const response = await postgrestPut(
|
||
'evaluation_points',
|
||
data,
|
||
{ id },
|
||
token
|
||
);
|
||
|
||
return response;
|
||
}
|
||
```
|
||
|
||
**验收标准**:
|
||
- [ ] ID 有效性验证
|
||
- [ ] 编码唯一性验证(排除自身)
|
||
- [ ] JSONB 字段格式验证
|
||
- [ ] 返回更新后的评查点完整信息
|
||
- [ ] 支持部分字段更新
|
||
|
||
---
|
||
|
||
### 阶段 2.3:复制功能对接 ⏱️ 0.5 天
|
||
|
||
#### 任务清单
|
||
|
||
**1. 验证 `copyRule` 函数**
|
||
|
||
**当前实现**:
|
||
```typescript
|
||
// 在 rules.new.tsx 中处理
|
||
// 通过 URL 参数 mode=copy 触发复制模式
|
||
```
|
||
|
||
**验证清单**:
|
||
- [ ] 复制时移除 `id`, `created_at`, `updated_at`, `usage_count`
|
||
- [ ] 清洗评查点编码(移除地区后缀)
|
||
- [ ] 提示用户修改编码和名称
|
||
- [ ] 保存时验证编码唯一性
|
||
|
||
---
|
||
|
||
### 阶段 2.4:删除接口对接 ⏱️ 0.5 天
|
||
|
||
#### 任务清单
|
||
|
||
**1. 更新 `deleteRule` 函数**
|
||
|
||
**当前实现**:
|
||
```typescript
|
||
export async function deleteRule(id: string, token?: string): Promise<ApiResponse<void>> {
|
||
const response = await postgrestDelete('evaluation_points', { id }, token);
|
||
return response;
|
||
}
|
||
```
|
||
|
||
**需要改进**:
|
||
- ⚠️ 缺少关联检查(评查结果)
|
||
|
||
**改进方案**:
|
||
```typescript
|
||
export async function deleteRule(id: string, token?: string): Promise<ApiResponse<void>> {
|
||
// 1. 检查是否有关联的评查结果
|
||
const results = await postgrestGet('evaluation_results', {
|
||
filter: { 'evaluation_point_id': `eq.${id}` },
|
||
select: 'count',
|
||
token
|
||
});
|
||
|
||
if (results.data && results.data.length > 0) {
|
||
return {
|
||
success: false,
|
||
error: '该评查点已被使用,无法删除。如需停用,请使用禁用功能。',
|
||
status: 400
|
||
};
|
||
}
|
||
|
||
// 2. 执行删除
|
||
const response = await postgrestDelete('evaluation_points', { id }, token);
|
||
return response;
|
||
}
|
||
```
|
||
|
||
**验收标准**:
|
||
- [ ] 删除前检查关联评查结果
|
||
- [ ] 提供清晰的错误提示
|
||
- [ ] 建议使用禁用功能代替删除
|
||
|
||
---
|
||
|
||
### 阶段 2.5:批量操作接口对接 ⏱️ 1 天
|
||
|
||
#### 任务清单
|
||
|
||
**1. 新增 `batchUpdateRuleStatus` 函数**
|
||
|
||
```typescript
|
||
export async function batchUpdateRuleStatus(
|
||
data: { ids: string[]; is_enabled: boolean },
|
||
token?: string
|
||
): Promise<ApiResponse<{ updated_count: number }>> {
|
||
const { ids, is_enabled } = data;
|
||
|
||
const response = await postgrestPatch(
|
||
'evaluation_points',
|
||
{ is_enabled },
|
||
{ id: `in.(${ids.join(',')})` },
|
||
token
|
||
);
|
||
|
||
return {
|
||
success: true,
|
||
data: {
|
||
updated_count: response.data?.length || 0
|
||
}
|
||
};
|
||
}
|
||
```
|
||
|
||
**验收标准**:
|
||
- [ ] 支持批量启用/禁用
|
||
- [ ] 返回更新数量
|
||
|
||
---
|
||
|
||
**2. 新增 `batchDeleteRules` 函数**
|
||
|
||
```typescript
|
||
export async function batchDeleteRules(
|
||
ids: string[],
|
||
token?: string
|
||
): Promise<ApiResponse<{ deleted_count: number; failed_ids: string[] }>> {
|
||
const failedIds: string[] = [];
|
||
let deletedCount = 0;
|
||
|
||
for (const id of ids) {
|
||
const result = await deleteRule(id, token);
|
||
if (result.success) {
|
||
deletedCount++;
|
||
} else {
|
||
failedIds.push(id);
|
||
}
|
||
}
|
||
|
||
return {
|
||
success: failedIds.length === 0,
|
||
data: {
|
||
deleted_count: deletedCount,
|
||
failed_ids: failedIds
|
||
}
|
||
};
|
||
}
|
||
```
|
||
|
||
**验收标准**:
|
||
- [ ] 支持批量删除
|
||
- [ ] 返回删除成功数量
|
||
- [ ] 返回删除失败的 ID 列表
|
||
|
||
---
|
||
|
||
### 阶段 2.6:前端组件更新 ⏱️ 3 天
|
||
|
||
#### 任务清单
|
||
|
||
**1. 更新 `rules.list.tsx`**
|
||
|
||
**需要改进的功能**:
|
||
|
||
a) **批量选择功能**
|
||
```typescript
|
||
const [selectedIds, setSelectedIds] = useState<string[]>([]);
|
||
|
||
const columns = [
|
||
{
|
||
title: <Checkbox checked={allSelected} onChange={handleSelectAll} />,
|
||
key: "selection",
|
||
width: "50px",
|
||
render: (_: unknown, record: Rule) => (
|
||
<Checkbox
|
||
checked={selectedIds.includes(record.id)}
|
||
onChange={() => handleSelectRow(record.id)}
|
||
/>
|
||
)
|
||
},
|
||
// ... 其他列
|
||
];
|
||
```
|
||
|
||
b) **批量操作按钮**
|
||
```tsx
|
||
<div className="batch-actions">
|
||
<Button
|
||
type="default"
|
||
disabled={selectedIds.length === 0}
|
||
onClick={() => handleBatchEnable(true)}
|
||
>
|
||
批量启用
|
||
</Button>
|
||
<Button
|
||
type="default"
|
||
disabled={selectedIds.length === 0}
|
||
onClick={() => handleBatchEnable(false)}
|
||
>
|
||
批量禁用
|
||
</Button>
|
||
<Button
|
||
type="danger"
|
||
disabled={selectedIds.length === 0}
|
||
onClick={handleBatchDelete}
|
||
>
|
||
批量删除
|
||
</Button>
|
||
</div>
|
||
```
|
||
|
||
c) **统计信息展示**
|
||
```typescript
|
||
const { data: statistics } = await getRuleStatistics(frontendJWT);
|
||
|
||
// 在页面顶部展示统计卡片
|
||
<div className="statistics-cards">
|
||
<StatCard title="总评查点" value={statistics.total_count} />
|
||
<StatCard title="已启用" value={statistics.enabled_count} color="success" />
|
||
<StatCard title="已禁用" value={statistics.disabled_count} color="warning" />
|
||
<StatCard title="高风险" value={statistics.by_risk.high} color="error" />
|
||
</div>
|
||
```
|
||
|
||
**验收标准**:
|
||
- [ ] 表格支持多选
|
||
- [ ] 批量操作按钮显示/禁用状态正确
|
||
- [ ] 批量启用/禁用功能正常
|
||
- [ ] 批量删除功能正常
|
||
- [ ] 统计信息展示正常
|
||
|
||
---
|
||
|
||
**2. 更新 `rules.new.tsx`**
|
||
|
||
**需要改进的功能**:
|
||
|
||
a) **异步验证优化**
|
||
```typescript
|
||
// 编码唯一性验证(防抖)
|
||
const validateCodeUnique = useMemo(
|
||
() => debounce(async (code: string, currentId?: string) => {
|
||
const response = await getRulesList({ keyword: code, token: frontendJWT });
|
||
if (response.data && response.data.rules.length > 0) {
|
||
const isDuplicate = response.data.rules.some(r => r.id !== currentId);
|
||
if (isDuplicate) {
|
||
setFormErrors(prev => ({
|
||
...prev,
|
||
code: '评查点编码已存在'
|
||
}));
|
||
}
|
||
}
|
||
}, 500),
|
||
[frontendJWT]
|
||
);
|
||
```
|
||
|
||
b) **保存前验证增强**
|
||
```typescript
|
||
const handleSave = async () => {
|
||
// 1. 基本字段验证
|
||
if (!formData.name?.trim()) {
|
||
toastService.warning("评查点名称不能为空");
|
||
return;
|
||
}
|
||
|
||
// 2. 编码唯一性验证
|
||
const codeValid = await validateCodeUnique(formData.code, formData.id);
|
||
if (!codeValid) {
|
||
toastService.warning("评查点编码已存在");
|
||
return;
|
||
}
|
||
|
||
// 3. JSONB 字段格式验证
|
||
try {
|
||
JSON.parse(JSON.stringify(formData.extraction_config));
|
||
JSON.parse(JSON.stringify(formData.evaluation_config));
|
||
} catch (error) {
|
||
toastService.error("配置格式无效");
|
||
return;
|
||
}
|
||
|
||
// 4. 评查规则完整性验证
|
||
// (已有实现,保持不变)
|
||
|
||
// 5. 执行保存
|
||
const result = isEditMode
|
||
? await updateRule(formData.id!, formData, frontendJWT)
|
||
: await createRule(formData, frontendJWT);
|
||
|
||
if (result.success) {
|
||
toastService.success("保存成功");
|
||
navigate(`/rules/new?id=${result.data.id}`);
|
||
} else {
|
||
toastService.error(result.error || "保存失败");
|
||
}
|
||
};
|
||
```
|
||
|
||
**验收标准**:
|
||
- [ ] 编码唯一性异步验证(带防抖)
|
||
- [ ] JSONB 字段格式验证
|
||
- [ ] 保存前完整性验证
|
||
- [ ] 错误提示清晰明确
|
||
- [ ] 保存成功后正确跳转
|
||
|
||
---
|
||
|
||
## 验收标准
|
||
|
||
### 功能验收
|
||
|
||
#### 模块 1:评查点分组管理
|
||
|
||
- [ ] **查询功能**
|
||
- [ ] 获取一级分组列表
|
||
- [ ] 获取二级分组列表
|
||
- [ ] 按名称筛选
|
||
- [ ] 按编码筛选
|
||
- [ ] 按状态筛选
|
||
- [ ] 分页功能正常
|
||
|
||
- [ ] **创建功能**
|
||
- [ ] 创建一级分组
|
||
- [ ] 创建二级分组
|
||
- [ ] 必填字段验证
|
||
- [ ] 编码唯一性验证
|
||
|
||
- [ ] **更新功能**
|
||
- [ ] 更新分组信息
|
||
- [ ] 不允许修改父级ID
|
||
- [ ] 编码唯一性验证(排除自身)
|
||
|
||
- [ ] **删除功能**
|
||
- [ ] 删除前检查子分组
|
||
- [ ] 删除前检查关联评查点
|
||
- [ ] 级联删除提示
|
||
|
||
- [ ] **批量操作**
|
||
- [ ] 批量启用/禁用
|
||
- [ ] 批量删除
|
||
- [ ] 错误处理
|
||
|
||
#### 模块 2:评查点管理
|
||
|
||
- [ ] **查询功能**
|
||
- [ ] 获取评查点列表
|
||
- [ ] 按关键词搜索
|
||
- [ ] 按评查点类型筛选
|
||
- [ ] 按规则组筛选
|
||
- [ ] 按风险等级筛选
|
||
- [ ] 按状态筛选
|
||
- [ ] 按地区筛选
|
||
- [ ] 排序功能
|
||
- [ ] 分页功能
|
||
- [ ] 统计信息展示
|
||
|
||
- [ ] **创建功能**
|
||
- [ ] 创建评查点
|
||
- [ ] 必填字段验证
|
||
- [ ] 编码唯一性验证
|
||
- [ ] JSONB 字段格式验证
|
||
- [ ] 评查规则完整性验证
|
||
|
||
- [ ] **更新功能**
|
||
- [ ] 更新评查点
|
||
- [ ] 编码唯一性验证(排除自身)
|
||
- [ ] JSONB 字段格式验证
|
||
- [ ] 部分字段更新
|
||
|
||
- [ ] **复制功能**
|
||
- [ ] 复制评查点
|
||
- [ ] 移除不应复制的字段
|
||
- [ ] 清洗编码
|
||
- [ ] 提示用户修改
|
||
|
||
- [ ] **删除功能**
|
||
- [ ] 删除前检查关联评查结果
|
||
- [ ] 建议使用禁用功能
|
||
|
||
- [ ] **批量操作**
|
||
- [ ] 批量启用/禁用
|
||
- [ ] 批量删除
|
||
- [ ] 错误处理
|
||
|
||
---
|
||
|
||
### 性能验收
|
||
|
||
- [ ] 列表页加载时间 < 2 秒
|
||
- [ ] 筛选响应时间 < 1 秒
|
||
- [ ] 保存响应时间 < 2 秒
|
||
- [ ] 批量操作响应时间 < 5 秒
|
||
|
||
---
|
||
|
||
### 代码质量验收
|
||
|
||
- [ ] TypeScript 类型定义完整
|
||
- [ ] 无 ESLint 错误
|
||
- [ ] 无 TypeScript 类型错误
|
||
- [ ] 代码注释清晰
|
||
- [ ] 遵循项目编码规范
|
||
|
||
---
|
||
|
||
## 风险与注意事项
|
||
|
||
### 数据迁移风险
|
||
|
||
**问题**: 现有数据格式可能与 API v3 不完全兼容
|
||
|
||
**解决方案**:
|
||
1. 在开发环境进行充分测试
|
||
2. 编写数据迁移脚本
|
||
3. 备份生产数据
|
||
4. 分批次迁移
|
||
|
||
### 性能风险
|
||
|
||
**问题**: 大数据量情况下查询性能下降
|
||
|
||
**解决方案**:
|
||
1. 添加数据库索引
|
||
2. 优化 PostgREST 查询
|
||
3. 实现分页和懒加载
|
||
4. 使用缓存机制
|
||
|
||
### 兼容性风险
|
||
|
||
**问题**: 旧版本 API 调用可能失败
|
||
|
||
**解决方案**:
|
||
1. 保留旧版本 API(向后兼容)
|
||
2. 使用 API 版本控制
|
||
3. 提供迁移指南
|
||
4. 逐步废弃旧接口
|
||
|
||
### JSONB 字段风险
|
||
|
||
**问题**: JSONB 字段格式复杂,容易出错
|
||
|
||
**解决方案**:
|
||
1. 严格的数据验证
|
||
2. 使用 JSON Schema 验证
|
||
3. 提供默认值和模板
|
||
4. 详细的错误提示
|
||
|
||
---
|
||
|
||
## 实施时间表
|
||
|
||
| 阶段 | 模块 | 工作量 | 起止日期 |
|
||
|------|------|--------|---------|
|
||
| 1.1 | 评查点分组 - 查询接口对接 | 1-2 天 | Day 1-2 |
|
||
| 1.2 | 评查点分组 - 创建/更新接口对接 | 1 天 | Day 3 |
|
||
| 1.3 | 评查点分组 - 删除接口对接 | 0.5 天 | Day 4 上午 |
|
||
| 1.4 | 评查点分组 - 批量操作接口对接 | 1 天 | Day 4 下午 - Day 5 |
|
||
| 1.5 | 评查点分组 - 前端组件更新 | 2 天 | Day 6-7 |
|
||
| 2.1 | 评查点 - 查询接口对接 | 2 天 | Day 8-9 |
|
||
| 2.2 | 评查点 - 创建/更新接口对接 | 2 天 | Day 10-11 |
|
||
| 2.3 | 评查点 - 复制功能对接 | 0.5 天 | Day 12 上午 |
|
||
| 2.4 | 评查点 - 删除接口对接 | 0.5 天 | Day 12 下午 |
|
||
| 2.5 | 评查点 - 批量操作接口对接 | 1 天 | Day 13 |
|
||
| 2.6 | 评查点 - 前端组件更新 | 3 天 | Day 14-16 |
|
||
| 测试 | 集成测试 + 修复 Bug | 2 天 | Day 17-18 |
|
||
| 部署 | 生产环境部署 | 1 天 | Day 19 |
|
||
|
||
**总计**: 约 19 个工作日(约 4 周)
|
||
|
||
---
|
||
|
||
## 附录
|
||
|
||
### PostgREST 查询语法参考
|
||
|
||
```typescript
|
||
// 1. 筛选条件
|
||
filter: {
|
||
'name': 'ilike.*关键词*', // 模糊搜索(不区分大小写)
|
||
'is_enabled': 'eq.true', // 等于
|
||
'risk': 'in.(low,medium)', // 包含
|
||
'created_at': 'gte.2024-01-01', // 大于等于
|
||
'pid': 'is.null' // 为空
|
||
}
|
||
|
||
// 2. OR 条件
|
||
filter: {
|
||
'or': '(name.ilike.*关键词*,code.ilike.*关键词*)'
|
||
}
|
||
|
||
// 3. 关联查询
|
||
select: '*, evaluation_point_groups:evaluation_point_groups_id(*)'
|
||
|
||
// 4. 排序
|
||
order: { 'created_at': 'desc' }
|
||
|
||
// 5. 分页
|
||
range: { from: 0, to: 9 } // 前 10 条
|
||
|
||
// 6. 计数
|
||
count: 'exact'
|
||
```
|
||
|
||
### 类型定义参考
|
||
|
||
```typescript
|
||
// 评查点分组
|
||
export interface RuleGroup {
|
||
id: string;
|
||
name: string;
|
||
code: string;
|
||
pid: string | null;
|
||
description?: string;
|
||
is_enabled: boolean;
|
||
created_at: string;
|
||
updated_at: string;
|
||
children?: RuleGroup[];
|
||
ruleCount?: number;
|
||
}
|
||
|
||
// 评查点
|
||
export interface EvaluationPoint {
|
||
id?: string;
|
||
name: string;
|
||
code: string;
|
||
risk: 'low' | 'medium' | 'high';
|
||
is_enabled: boolean;
|
||
description?: string;
|
||
references_laws?: {
|
||
name: string;
|
||
articles: string[];
|
||
content: string;
|
||
};
|
||
evaluation_point_groups_pid: string;
|
||
evaluation_point_groups_id: string;
|
||
extraction_config: ExtractionConfig;
|
||
evaluation_config: EvaluationConfig;
|
||
pass_message?: string;
|
||
fail_message?: string;
|
||
suggestion_message?: string;
|
||
suggestion_message_type?: 'info' | 'warning' | 'error';
|
||
post_action?: string;
|
||
action_config?: string;
|
||
score?: number;
|
||
usage_count?: number;
|
||
created_at?: string;
|
||
updated_at?: string;
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
**文档结束**
|