feat(evaluation): 模块1.4 - 新增评查点分组批量操作接口

## 主要改进

### 1. 新增 batchUpdateRuleGroupStatus 函数(批量启用/禁用)
-  参数验证(ID列表不为空,每个ID有效)
-  逐个验证分组存在性
-  逐个执行更新操作
-  返回详细的操作结果
  - updated_count: 成功更新的数量
  - failed_ids: 失败的ID列表
  - errors: 详细的错误信息(包含ID和错误原因)

### 2. 新增 batchDeleteRuleGroups 函数(批量删除)
-  参数验证(ID列表不为空,每个ID有效)
-  采用安全的阻止删除策略
-  逐个检查并删除分组
-  返回详细的操作结果和错误信息
  - deleted_count: 成功删除的数量
  - failed_ids: 失败的ID列表
  - errors: 详细的错误信息(包含子分组/评查点检查结果)

### 3. 批量操作特性
-  **逐个处理**:确保每个分组都能被正确处理
-  **部分成功支持**:即使部分分组操作失败,成功的也会被处理
-  **详细的错误追踪**:记录每个失败的ID及其失败原因
-  **安全性优先**:批量删除继承单个删除的安全检查

### 4. 返回值结构

```typescript
// 批量更新状态
{
  success: boolean;          // 是否全部成功
  updated_count: number;     // 成功更新的数量
  failed_ids: string[];      // 失败的ID列表
  errors?: Array<{           // 详细错误(可选)
    id: string;
    error: string;
  }>;
}

// 批量删除
{
  success: boolean;          // 是否全部成功
  deleted_count: number;     // 成功删除的数量
  failed_ids: string[];      // 失败的ID列表
  errors?: Array<{           // 详细错误(可选)
    id: string;
    error: string;
    details?: {              // 删除失败详情
      hasChildren?: boolean;
      hasPoints?: boolean;
    };
  }>;
}
```

## 使用示例

### 批量启用分组
```typescript
const result = await batchUpdateRuleGroupStatus(
  ['1', '2', '3'],
  true,
  token
);

if (result.success) {
  console.log(`成功启用 ${result.updated_count} 个分组`);
} else {
  console.log(`成功 ${result.updated_count} 个,失败 ${result.failed_ids.length} 个`);
  result.errors?.forEach(err => {
    console.log(`分组 ${err.id}: ${err.error}`);
  });
}
```

### 批量删除分组
```typescript
const result = await batchDeleteRuleGroups(['1', '2'], token);

if (result.success) {
  console.log(`成功删除 ${result.deleted_count} 个分组`);
} else {
  result.errors?.forEach(err => {
    if (err.details?.hasChildren) {
      console.log(`分组 ${err.id} 有子分组,无法删除`);
    }
    if (err.details?.hasPoints) {
      console.log(`分组 ${err.id} 有评查点,无法删除`);
    }
  });
}
```

## 相关文件
- app/api/evaluation_points/rule-groups.ts

## 验收清单
- [x] TypeScript 类型检查通过
- [x] 完整的参数验证
- [x] 支持部分成功场景
- [x] 详细的错误追踪
- [x] 安全的删除策略

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-25 12:16:46 +08:00
parent 89b1d2e5f5
commit 7f1a05107f
+197 -2
View File
@@ -937,9 +937,204 @@ async function deleteEvaluationPointsByGroupId(groupId: string, token?: string):
return { success: true }; return { success: true };
} catch (error) { } catch (error) {
console.error('删除评查点失败:', error); console.error('删除评查点失败:', error);
return { return {
success: false, success: false,
error: error instanceof Error ? error.message : '删除评查点失败' error: error instanceof Error ? error.message : '删除评查点失败'
}; };
} }
}
// ==================== 批量操作接口 ====================
/**
* 批量更新分组状态(启用/禁用)
* @param ids 分组ID列表
* @param is_enabled 目标状态
* @param token JWT token (可选)
* @returns 更新结果
*/
export async function batchUpdateRuleGroupStatus(
ids: string[],
is_enabled: boolean,
token?: string
): Promise<{
success: boolean;
updated_count: number;
failed_ids: string[];
errors?: Array<{ id: string; error: string }>;
}> {
try {
// ========== 1. 参数验证 ==========
if (!Array.isArray(ids) || ids.length === 0) {
return {
success: false,
updated_count: 0,
failed_ids: [],
errors: [{ id: 'validation', error: 'ID列表不能为空' }]
};
}
// 验证每个ID的有效性
const invalidIds = ids.filter(id => !id || id.trim() === '');
if (invalidIds.length > 0) {
return {
success: false,
updated_count: 0,
failed_ids: ids,
errors: [{ id: 'validation', error: '存在无效的分组ID' }]
};
}
// ========== 2. 逐个更新(确保每个分组都能被正确处理) ==========
const failedIds: string[] = [];
const errors: Array<{ id: string; error: string }> = [];
let updatedCount = 0;
for (const id of ids) {
try {
// 验证分组是否存在
const groupResponse = await getRuleGroup(id, token);
if (groupResponse.error || !groupResponse.data) {
failedIds.push(id);
errors.push({ id, error: '分组不存在或无法访问' });
continue;
}
// 执行更新
const updateResponse = await postgrestPut<ApiResponse<RuleGroup> | RuleGroup, Partial<ApiRuleGroup>>(
'evaluation_point_groups',
{ is_enabled },
{ id },
token
);
if (updateResponse.error) {
failedIds.push(id);
errors.push({ id, error: updateResponse.error });
} else {
updatedCount++;
}
} catch (error) {
failedIds.push(id);
errors.push({
id,
error: error instanceof Error ? error.message : '更新失败'
});
}
}
// ========== 3. 返回结果 ==========
return {
success: failedIds.length === 0,
updated_count: updatedCount,
failed_ids: failedIds,
errors: errors.length > 0 ? errors : undefined
};
} catch (error) {
console.error('批量更新分组状态失败:', error);
return {
success: false,
updated_count: 0,
failed_ids: ids,
errors: [{
id: 'batch',
error: error instanceof Error ? error.message : '批量更新失败'
}]
};
}
}
/**
* 批量删除分组(安全的阻止删除策略)
* @param ids 分组ID列表
* @param token JWT token (可选)
* @returns 删除结果
*/
export async function batchDeleteRuleGroups(
ids: string[],
token?: string
): Promise<{
success: boolean;
deleted_count: number;
failed_ids: string[];
errors?: Array<{ id: string; error: string; details?: { hasChildren?: boolean; hasPoints?: boolean } }>;
}> {
try {
// ========== 1. 参数验证 ==========
if (!Array.isArray(ids) || ids.length === 0) {
return {
success: false,
deleted_count: 0,
failed_ids: [],
errors: [{ id: 'validation', error: 'ID列表不能为空' }]
};
}
// 验证每个ID的有效性
const invalidIds = ids.filter(id => !id || id.trim() === '');
if (invalidIds.length > 0) {
return {
success: false,
deleted_count: 0,
failed_ids: ids,
errors: [{ id: 'validation', error: '存在无效的分组ID' }]
};
}
// ========== 2. 逐个删除(使用安全的阻止删除策略) ==========
const failedIds: string[] = [];
const errors: Array<{ id: string; error: string; details?: { hasChildren?: boolean; hasPoints?: boolean } }> = [];
let deletedCount = 0;
for (const id of ids) {
try {
const deleteResult = await deleteRuleGroup(id, token);
if (!deleteResult.success) {
failedIds.push(id);
errors.push({
id,
error: deleteResult.error || '删除失败',
details: deleteResult.details ? {
hasChildren: deleteResult.details.hasChildren,
hasPoints: deleteResult.details.hasPoints
} : undefined
});
} else {
deletedCount++;
}
} catch (error) {
failedIds.push(id);
errors.push({
id,
error: error instanceof Error ? error.message : '删除失败'
});
}
}
// ========== 3. 返回结果 ==========
return {
success: failedIds.length === 0,
deleted_count: deletedCount,
failed_ids: failedIds,
errors: errors.length > 0 ? errors : undefined
};
} catch (error) {
console.error('批量删除分组失败:', error);
return {
success: false,
deleted_count: 0,
failed_ids: ids,
errors: [{
id: 'batch',
error: error instanceof Error ? error.message : '批量删除失败'
}]
};
}
} }