Files
knowledge-governance/internal/httpapi/monitor_tasks.go
姚章浩 1b09f7d35b
build / build (push) Successful in 1m17s
feat(monitor): 卡片概况与统一文件处理表
2026-10-27 12:14:19 +08:00

103 lines
3.4 KiB
Go

package httpapi
import (
"context"
"errors"
postgresstore "knowledge-governance/internal/platform/postgres"
"net/http"
"strconv"
"strings"
"time"
)
type monitorRemovalRetrier interface {
RetryMonitorRemoval(context.Context, string, string, time.Time) error
}
func retryMonitorRemoval(w http.ResponseWriter, r *http.Request, config Config) {
identity, ok := requireSessionIdentity(w, r)
if !ok {
return
}
if identity.Role != roleEngineer && identity.Role != roleAdmin {
writeJSON(w, 403, map[string]string{"error": "forbidden"})
return
}
if r.Header.Get("X-Governance-Action") != "monitor-removal-retry" {
writeJSON(w, 403, map[string]string{"error": "write intent required"})
return
}
id := r.PathValue("id")
if id == "" || len(id) > 128 {
writeJSON(w, 400, map[string]string{"error": "invalid publication"})
return
}
store, ok := config.Store.(monitorRemovalRetrier)
if !ok {
writeJSON(w, 501, map[string]string{"error": "removal retry not configured"})
return
}
err := store.RetryMonitorRemoval(r.Context(), id, actorFor(r), time.Now().UTC())
if errors.Is(err, postgresstore.ErrPublicationStateConflict) {
writeJSON(w, 409, map[string]string{"error": "状态或治理规则已变化,不能重复提交"})
return
}
if err != nil {
writeJSON(w, 500, map[string]string{"error": "测试库移除重试失败"})
return
}
writeJSON(w, 202, map[string]string{"status": "accepted"})
}
type monitorTaskReader interface {
ListMonitorTasks(context.Context, postgresstore.MonitorTaskFilter) (postgresstore.MonitorTaskPage, error)
}
func listMonitorTasks(w http.ResponseWriter, r *http.Request, config Config) {
if _, ok := requireSessionIdentity(w, r); !ok {
return
}
q := r.URL.Query()
f := postgresstore.MonitorTaskFilter{Search: q.Get("q"), Source: q.Get("source"), Scope: q.Get("scope"), Action: q.Get("action"), Status: q.Get("status"), Stage: q.Get("stage"), ID: q.Get("id"), Target: q.Get("target"), Since: q.Get("since"), Until: q.Get("until"), Limit: 20}
for k, p := range map[string]*int{"limit": &f.Limit, "offset": &f.Offset} {
if q.Get(k) != "" {
n, e := strconv.Atoi(q.Get(k))
if e != nil {
writeJSON(w, 400, map[string]string{"error": "invalid pagination"})
return
}
*p = n
}
}
if f.Limit < 1 || f.Limit > 100 || f.Offset < 0 || len(f.Search) > 512 || len(f.Source) > 128 || len(f.ID) > 128 {
writeJSON(w, 400, map[string]string{"error": "invalid filter"})
return
}
for _, pair := range [][2]string{{f.Scope, "|internal|external"}, {f.Action, "|add|update|remove"}, {f.Status, "|active|waiting|running|retrying|attention|completed|cancelled"}, {f.Stage, "|waiting|fetch|upload|parsing|indexing|removal|finished"}} {
if !strings.Contains("|"+pair[1]+"|", "|"+pair[0]+"|") {
writeJSON(w, 400, map[string]string{"error": "invalid filter"})
return
}
}
for _, v := range []string{f.Since, f.Until} {
if v != "" {
if _, e := time.Parse(time.RFC3339, v); e != nil {
writeJSON(w, 400, map[string]string{"error": "invalid date"})
return
}
}
}
store, ok := config.Store.(monitorTaskReader)
if !ok {
writeJSON(w, 501, map[string]string{"error": "monitor tasks not configured"})
return
}
page, e := store.ListMonitorTasks(r.Context(), f)
if e != nil {
writeJSON(w, 500, map[string]string{"error": "无法读取文件处理状态"})
return
}
w.Header().Set("Cache-Control", "no-store")
writeJSON(w, 200, map[string]any{"data": page})
}