feat: 完善 API Keys 批量删除功能并修复搜索跨选择问题

## 主要改进

### 🔧 核心修复
- 修复搜索时勾选状态无法保存的问题
- 优化全选/取消全选逻辑,支持跨搜索结果保持选择状态
- 改进批量删除的用户体验
- 添加 Unicode 字符处理中间件,提升请求体解析稳定性

### 🎯 具体变更
- **路由修复**: 解决批量删除路由匹配问题,调整路由顺序
- **API客户端**: 修复 DELETE 方法支持请求体数据传输
- **前端逻辑**: 分离筛选和搜索的监听器,搜索时保持已选中状态
- **全选优化**: 取消全选时只移除当前页选中项,保留其他页面选择
- **Unicode处理**: 添加无效 UTF-16 代理对清理和错误处理机制
- **配置管理**: 将 .mcp.json 添加到 .gitignore,避免本地配置被提交

### 🚀 用户体验提升
- 支持跨搜索结果批量选择和删除
- 批量删除按钮显示选中数量
- 智能的全选状态管理
- 更好的 Unicode 字符处理容错性

### 🧪 测试验证
- 验证搜索切换时选择状态保持
- 确认批量删除功能正常工作
- 检查 Redis 数据清理完整性
- 测试 Unicode 字符处理稳定性

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
iRubbish
2025-08-19 09:46:33 +08:00
parent f642fa7095
commit 1ee71ffbc9
7 changed files with 304 additions and 38 deletions

View File

@@ -799,28 +799,20 @@ router.put('/api-keys/:keyId', authenticateAdmin, async (req, res) => {
}
})
// 删除API Key
router.delete('/api-keys/:keyId', authenticateAdmin, async (req, res) => {
try {
const { keyId } = req.params
await apiKeyService.deleteApiKey(keyId)
logger.success(`🗑️ Admin deleted API key: ${keyId}`)
return res.json({ success: true, message: 'API key deleted successfully' })
} catch (error) {
logger.error('❌ Failed to delete API key:', error)
return res.status(500).json({ error: 'Failed to delete API key', message: error.message })
}
})
// 批量删除API Keys
// 批量删除API Keys必须在 :keyId 路由之前定义)
router.delete('/api-keys/batch', authenticateAdmin, async (req, res) => {
try {
const { keyIds } = req.body
// 调试信息
logger.info(`🐛 Batch delete request body: ${JSON.stringify(req.body)}`)
logger.info(`🐛 keyIds type: ${typeof keyIds}, value: ${JSON.stringify(keyIds)}`)
// 参数验证
if (!keyIds || !Array.isArray(keyIds) || keyIds.length === 0) {
logger.warn(
`🚨 Invalid keyIds: ${JSON.stringify({ keyIds, type: typeof keyIds, isArray: Array.isArray(keyIds) })}`
)
return res.status(400).json({
error: 'Invalid request',
message: 'keyIds 必须是一个非空数组'
@@ -843,7 +835,9 @@ router.delete('/api-keys/batch', authenticateAdmin, async (req, res) => {
})
}
logger.info(`🗑️ Admin attempting batch delete of ${keyIds.length} API keys`)
logger.info(
`🗑️ Admin attempting batch delete of ${keyIds.length} API keys: ${JSON.stringify(keyIds)}`
)
const results = {
successCount: 0,
@@ -855,8 +849,8 @@ router.delete('/api-keys/batch', authenticateAdmin, async (req, res) => {
for (const keyId of keyIds) {
try {
// 检查API Key是否存在
const apiKey = await apiKeyService.getApiKey(keyId)
if (!apiKey) {
const apiKey = await redis.getApiKey(keyId)
if (!apiKey || Object.keys(apiKey).length === 0) {
results.failedCount++
results.errors.push({ keyId, error: 'API Key 不存在' })
continue
@@ -903,6 +897,21 @@ router.delete('/api-keys/batch', authenticateAdmin, async (req, res) => {
}
})
// 删除单个API Key必须在批量删除路由之后定义
router.delete('/api-keys/:keyId', authenticateAdmin, async (req, res) => {
try {
const { keyId } = req.params
await apiKeyService.deleteApiKey(keyId)
logger.success(`🗑️ Admin deleted API key: ${keyId}`)
return res.json({ success: true, message: 'API key deleted successfully' })
} catch (error) {
logger.error('❌ Failed to delete API key:', error)
return res.status(500).json({ error: 'Failed to delete API key', message: error.message })
}
})
// 👥 账户分组管理
// 创建账户分组