package server import ( "context" "encoding/json" "net/http" "strings" "chat-agent-system/internal/errcode" "chat-agent-system/internal/httpx" ) func writeJSONResponse(w http.ResponseWriter, status int, data any) { w.Header().Set("Content-Type", "application/json; charset=utf-8") w.WriteHeader(status) _ = json.NewEncoder(w).Encode(data) } func writeSafeError(w http.ResponseWriter, r *http.Request, status int, legacy string) { emitError(w, r, status, errcode.FromLegacy(status, legacy), legacy) } func writeDomainError(w http.ResponseWriter, r *http.Request, status int, err error) { if err == nil { writeSafeError(w, r, status, "") return } if st, ok := domainHTTPStatus(err); ok { def := errcode.FromStatus(st) if d, found := errcode.FromError(err); found { def = d } emitError(w, r, st, def, err.Error()) return } if def, found := errcode.FromError(err); found { // 处理器传入的状态是既有 HTTP 契约。注册表状态只在调用方未给出时使用。 st := status if st == 0 { st = def.Status } if st == 0 { st = http.StatusInternalServerError } emitError(w, r, st, def, err.Error()) return } emitError(w, r, status, errcode.FromLegacy(status, err.Error()), err.Error()) } func emitError(w http.ResponseWriter, r *http.Request, status int, def errcode.Def, legacy string) { if def.ID == "" { def = errcode.FromStatus(status) } details := map[string]any{} if def.Guide != "" { details["guide"] = def.Guide } if def.Module != "" { details["module"] = def.Module } rid := incomingRequestID(w, r) httpx.WriteHTTP(w, r, status, def.ID, legacy, rid, def.Message, details) if rid == "" && w != nil { rid = w.Header().Get(httpx.HeaderRequestID) } reportHTTPError(r, status, def, rid, legacy) } func incomingRequestID(w http.ResponseWriter, r *http.Request) string { if r != nil { if v := strings.TrimSpace(r.Header.Get(httpx.HeaderRequestID)); v != "" { return v } } if w != nil { if v := strings.TrimSpace(w.Header().Get(httpx.HeaderRequestID)); v != "" { return v } } return "" } func reportHTTPError(r *http.Request, status int, def errcode.Def, requestID, legacy string) { ev := errcode.Event{ Code: def.ID, Class: def.Class, HTTPStatus: status, RequestID: requestID, Module: def.Module, Source: "http", Retryable: def.Retryable, UserMessage: def.Message, Upstream: legacy, } ctx := context.Background() if r != nil { ev.Method = r.Method ev.Path = r.URL.Path if sid := strings.TrimSpace(r.URL.Query().Get("session_id")); sid != "" { ev.SessionID = sid } if user := principalFromContext(r.Context()); user != nil { ev.ActorUserID = user.ID } ctx = r.Context() } errcode.Report(ctx, ev) }