feat: 支持基于 crontab 的定时账号测试

每个测试计划绑定一个账号和一个模型,按 cron 表达式定期执行测试,
保存历史结果并在前端账号管理页面中提供完整的增删改查和结果查看功能。

主要变更:
- 新增 scheduled_test_plans / scheduled_test_results 两张表及迁移
- 后端 service 层:CRUD 服务 + 后台 cron runner(每分钟扫描到期计划并发执行)
- RunTestBackground 方法通过 httptest 在内存中执行账号测试并解析 SSE 输出
- Redis leader lock + pg_try_advisory_lock 双重保障多实例部署只执行一次
- REST API:5 个管理端点(计划 CRUD + 结果查询)
- 前端 ScheduledTestsPanel 组件:计划管理、启用开关、内联编辑、结果展开查看
- 中英文 i18n 支持

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
guoyongchang
2026-03-05 16:06:05 +08:00
parent 7076717b20
commit 3a089242f8
23 changed files with 1677 additions and 9 deletions

View File

@@ -22,6 +22,7 @@ import opsAPI from './ops'
import errorPassthroughAPI from './errorPassthrough'
import dataManagementAPI from './dataManagement'
import apiKeysAPI from './apiKeys'
import scheduledTestsAPI from './scheduledTests'
/**
* Unified admin API object for convenient access
@@ -45,7 +46,8 @@ export const adminAPI = {
ops: opsAPI,
errorPassthrough: errorPassthroughAPI,
dataManagement: dataManagementAPI,
apiKeys: apiKeysAPI
apiKeys: apiKeysAPI,
scheduledTests: scheduledTestsAPI
}
export {
@@ -67,7 +69,8 @@ export {
opsAPI,
errorPassthroughAPI,
dataManagementAPI,
apiKeysAPI
apiKeysAPI,
scheduledTestsAPI
}
export default adminAPI

View File

@@ -0,0 +1,85 @@
/**
* Admin Scheduled Tests API endpoints
* Handles scheduled test plan management for account connectivity monitoring
*/
import { apiClient } from '../client'
import type {
ScheduledTestPlan,
ScheduledTestResult,
CreateScheduledTestPlanRequest,
UpdateScheduledTestPlanRequest
} from '@/types'
/**
* List all scheduled test plans for an account
* @param accountId - Account ID
* @returns List of scheduled test plans
*/
export async function listByAccount(accountId: number): Promise<ScheduledTestPlan[]> {
const { data } = await apiClient.get<ScheduledTestPlan[]>(
`/admin/accounts/${accountId}/scheduled-test-plans`
)
return data
}
/**
* Create a new scheduled test plan
* @param req - Plan creation request
* @returns Created plan
*/
export async function create(req: CreateScheduledTestPlanRequest): Promise<ScheduledTestPlan> {
const { data } = await apiClient.post<ScheduledTestPlan>(
'/admin/scheduled-test-plans',
req
)
return data
}
/**
* Update an existing scheduled test plan
* @param id - Plan ID
* @param req - Fields to update
* @returns Updated plan
*/
export async function update(id: number, req: UpdateScheduledTestPlanRequest): Promise<ScheduledTestPlan> {
const { data } = await apiClient.put<ScheduledTestPlan>(
`/admin/scheduled-test-plans/${id}`,
req
)
return data
}
/**
* Delete a scheduled test plan
* @param id - Plan ID
*/
export async function deletePlan(id: number): Promise<void> {
await apiClient.delete(`/admin/scheduled-test-plans/${id}`)
}
/**
* List test results for a plan
* @param planId - Plan ID
* @param limit - Optional max number of results to return
* @returns List of test results
*/
export async function listResults(planId: number, limit?: number): Promise<ScheduledTestResult[]> {
const { data } = await apiClient.get<ScheduledTestResult[]>(
`/admin/scheduled-test-plans/${planId}/results`,
{
params: limit ? { limit } : undefined
}
)
return data
}
export const scheduledTestsAPI = {
listByAccount,
create,
update,
delete: deletePlan,
listResults
}
export default scheduledTestsAPI