refactor: optimize cron test support feature

**优化内容:**

1. **验证和安全性加强**
   - 移除cron验证重复,统一使用accountTestSchedulerService.validateCronExpression()方法
   - 添加model参数类型和长度验证(max 256 chars)
   - 限制cronExpression长度至100字符防止DoS攻击
   - 双层验证:service层和route层都进行长度检查

2. **性能优化**
   - 优化_refreshAllTasks()使用Promise.all()并行加载所有平台配置(之前是顺序加载)
   - 改进错误处理,平台加载失败时继续处理其他平台

3. **数据管理改进**
   - 为test config添加1年TTL过期机制(之前没有过期设置)
   - 保证test history已有30天TTL和5条记录限制

4. **错误响应标准化**
   - 统一所有API响应格式,确保error状态都包含message字段
   - 改进错误消息的可读性和上下文信息

5. **用户体验改进**
   - Vue组件使用showToast()替代原生alert()
   - 移除console.error()改用toast通知用户
   - 成功保存时显示成功提示

6. **代码整理**
   - 移除未使用的maxConcurrentTests变量及其getStatus()中的引用
   - 保持代码整洁性

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

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
guoyongchang
2025-12-19 13:39:39 +08:00
parent 9977245d59
commit cd3f51e9e2
4 changed files with 80 additions and 46 deletions

View File

@@ -14,8 +14,6 @@ class AccountTestSchedulerService {
// 定期刷新配置的间隔 (毫秒)
this.refreshIntervalMs = 60 * 1000
this.refreshInterval = null
// 测试并发限制
this.maxConcurrentTests = 3
// 当前正在测试的账户
this.testingAccounts = new Set()
// 是否已启动
@@ -28,6 +26,10 @@ class AccountTestSchedulerService {
* @returns {boolean}
*/
validateCronExpression(cronExpression) {
// 长度检查(防止 DoS
if (!cronExpression || cronExpression.length > 100) {
return false
}
return cron.validate(cronExpression)
}
@@ -85,41 +87,53 @@ class AccountTestSchedulerService {
const platforms = ['claude', 'gemini', 'openai']
const activeAccountKeys = new Set()
for (const platform of platforms) {
const enabledAccounts = await redis.getEnabledTestAccounts(platform)
// 并行加载所有平台的配置
const allEnabledAccounts = await Promise.all(
platforms.map((platform) =>
redis
.getEnabledTestAccounts(platform)
.then((accounts) => accounts.map((acc) => ({ ...acc, platform })))
.catch((error) => {
logger.warn(`⚠️ Failed to load test accounts for platform ${platform}:`, error)
return []
})
)
)
for (const { accountId, cronExpression, model } of enabledAccounts) {
if (!cronExpression) {
logger.warn(
`⚠️ Account ${accountId} (${platform}) has no valid cron expression, skipping`
)
// 展平平台数据
const flatAccounts = allEnabledAccounts.flat()
for (const { accountId, cronExpression, model, platform } of flatAccounts) {
if (!cronExpression) {
logger.warn(
`⚠️ Account ${accountId} (${platform}) has no valid cron expression, skipping`
)
continue
}
const accountKey = `${platform}:${accountId}`
activeAccountKeys.add(accountKey)
// 检查是否需要更新任务
const existingTask = this.scheduledTasks.get(accountKey)
if (existingTask) {
// 如果 cron 表达式和模型都没变,不需要更新
if (existingTask.cronExpression === cronExpression && existingTask.model === model) {
continue
}
const accountKey = `${platform}:${accountId}`
activeAccountKeys.add(accountKey)
// 检查是否需要更新任务
const existingTask = this.scheduledTasks.get(accountKey)
if (existingTask) {
// 如果 cron 表达式和模型都没变,不需要更新
if (existingTask.cronExpression === cronExpression && existingTask.model === model) {
continue
}
// 配置变了,停止旧任务
existingTask.task.stop()
logger.info(
`🔄 Updating cron task for ${accountKey}: ${cronExpression}, model: ${model}`
)
} else {
logger.info(
` Creating cron task for ${accountKey}: ${cronExpression}, model: ${model}`
)
}
// 创建新的 cron 任务
this._createCronTask(accountId, platform, cronExpression, model)
// 配置变了,停止旧任务
existingTask.task.stop()
logger.info(
`🔄 Updating cron task for ${accountKey}: ${cronExpression}, model: ${model}`
)
} else {
logger.info(
` Creating cron task for ${accountKey}: ${cronExpression}, model: ${model}`
)
}
// 创建新的 cron 任务
this._createCronTask(accountId, platform, cronExpression, model)
}
// 清理已删除或禁用的账户任务
@@ -397,7 +411,6 @@ class AccountTestSchedulerService {
return {
running: this.isStarted,
refreshIntervalMs: this.refreshIntervalMs,
maxConcurrentTests: this.maxConcurrentTests,
scheduledTasksCount: this.scheduledTasks.size,
scheduledTasks: tasks,
currentlyTesting: Array.from(this.testingAccounts)