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}) }