feat: Implement core backend services for team owner management, SQLite persistence, and logging, alongside frontend monitoring and record views.

This commit is contained in:
2026-01-30 18:15:50 +08:00
parent e61430b60d
commit 165c6d69b9
7 changed files with 844 additions and 125 deletions

View File

@@ -92,6 +92,7 @@ func startServer(cfg *config.Config) {
// 日志 API
mux.HandleFunc("/api/logs", api.CORS(handleGetLogs))
mux.HandleFunc("/api/logs/clear", api.CORS(handleClearLogs))
mux.HandleFunc("/api/logs/stream", handleLogStream) // SSE 实时日志
// S2A 代理 API
mux.HandleFunc("/api/s2a/test", api.CORS(handleS2ATest))
@@ -117,6 +118,10 @@ func startServer(cfg *config.Config) {
mux.HandleFunc("/api/team/status", api.CORS(api.HandleTeamProcessStatus))
mux.HandleFunc("/api/team/stop", api.CORS(api.HandleTeamProcessStop))
// 批次记录 API
mux.HandleFunc("/api/batch/runs", api.CORS(handleBatchRuns))
mux.HandleFunc("/api/batch/stats", api.CORS(handleBatchStats))
// 监控设置 API
mux.HandleFunc("/api/monitor/settings", api.CORS(api.HandleGetMonitorSettings))
mux.HandleFunc("/api/monitor/settings/save", api.CORS(api.HandleSaveMonitorSettings))
@@ -252,6 +257,84 @@ func handleClearLogs(w http.ResponseWriter, r *http.Request) {
api.Success(w, map[string]string{"message": "日志已清空"})
}
// handleBatchRuns 获取批次运行记录
func handleBatchRuns(w http.ResponseWriter, r *http.Request) {
if database.Instance == nil {
api.Error(w, http.StatusInternalServerError, "数据库未初始化")
return
}
runs, err := database.Instance.GetBatchRuns(50)
if err != nil {
api.Error(w, http.StatusInternalServerError, fmt.Sprintf("获取记录失败: %v", err))
return
}
api.Success(w, runs)
}
// handleBatchStats 获取批次统计
func handleBatchStats(w http.ResponseWriter, r *http.Request) {
if database.Instance == nil {
api.Error(w, http.StatusInternalServerError, "数据库未初始化")
return
}
stats := database.Instance.GetBatchRunStats()
api.Success(w, stats)
}
// handleLogStream SSE 实时日志流
func handleLogStream(w http.ResponseWriter, r *http.Request) {
// 设置 SSE 响应头
w.Header().Set("Content-Type", "text/event-stream")
w.Header().Set("Cache-Control", "no-cache")
w.Header().Set("Connection", "keep-alive")
w.Header().Set("Access-Control-Allow-Origin", "*")
// 生成唯一 ID
listenerID := fmt.Sprintf("client-%d", time.Now().UnixNano())
// 订阅日志
logChan := logger.AddListener(listenerID)
defer logger.RemoveListener(listenerID)
// 获取 flusher
flusher, ok := w.(http.Flusher)
if !ok {
http.Error(w, "SSE not supported", http.StatusInternalServerError)
return
}
// 发送初始连接确认
fmt.Fprintf(w, "data: {\"type\":\"connected\",\"id\":\"%s\"}\n\n", listenerID)
flusher.Flush()
// 监听日志和连接关闭
ctx := r.Context()
for {
select {
case <-ctx.Done():
return
case entry, ok := <-logChan:
if !ok {
return
}
// 发送日志条目
data, _ := json.Marshal(map[string]interface{}{
"type": "log",
"timestamp": entry.Timestamp.Format("15:04:05"),
"level": entry.Level,
"message": entry.Message,
"email": entry.Email,
"module": entry.Module,
})
fmt.Fprintf(w, "data: %s\n\n", data)
flusher.Flush()
}
}
}
func handleS2ATest(w http.ResponseWriter, r *http.Request) {
if config.Global == nil || config.Global.S2AApiBase == "" {
api.Error(w, http.StatusBadRequest, "S2A 配置未设置")