// Package caseapi ist die HTTP-Bindung fuer das Akte/Vorgang-Objektmodell // (VOR-01). Nutzeridentitaet kommt uebergangsweise aus X-User-Id, wie // bereits bei approvalapi (APR-01) etabliert. package caseapi import ( "encoding/json" "errors" "net/http" "gitea.perlbach24.de/scripte/nexarch/workflow/internal/caseobj" ) type Server struct { svc *caseobj.Service } func NewServer(svc *caseobj.Service) *Server { return &Server{svc: svc} } func (s *Server) RegisterRoutes(mux *http.ServeMux) { mux.HandleFunc("POST /cases", s.requireUser(s.handleCreate)) mux.HandleFunc("GET /cases/{id}", s.requireUser(s.handleGet)) mux.HandleFunc("POST /cases/{id}/transition", s.requireUser(s.handleTransition)) } func (s *Server) requireUser(next func(w http.ResponseWriter, r *http.Request, userID string)) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { userID := r.Header.Get("X-User-Id") if userID == "" { writeError(w, http.StatusUnauthorized, "X-User-Id fehlt") return } next(w, r, userID) } } type createRequest struct { CaseNumber string `json:"case_number"` Responsible string `json:"responsible"` References []caseobj.Reference `json:"references"` } func (s *Server) handleCreate(w http.ResponseWriter, r *http.Request, userID string) { var req createRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { writeError(w, http.StatusBadRequest, "ungueltiger anfrage-koerper") return } c, err := s.svc.Create(r.Context(), req.CaseNumber, req.Responsible, userID, req.References) switch { case err == nil: writeJSON(w, http.StatusCreated, c) case errors.Is(err, caseobj.ErrDuplicateCaseNumber): writeError(w, http.StatusConflict, "aktenzeichen bereits vergeben") default: writeError(w, http.StatusInternalServerError, "akte konnte nicht angelegt werden") } } func (s *Server) handleGet(w http.ResponseWriter, r *http.Request, _ string) { id := r.PathValue("id") c, err := s.svc.Get(r.Context(), id) switch { case err == nil: writeJSON(w, http.StatusOK, c) case errors.Is(err, caseobj.ErrNotFound): writeError(w, http.StatusNotFound, "akte nicht gefunden") default: writeError(w, http.StatusInternalServerError, "akte konnte nicht gelesen werden") } } type transitionRequest struct { To string `json:"to"` Reason string `json:"reason"` } func (s *Server) handleTransition(w http.ResponseWriter, r *http.Request, userID string) { id := r.PathValue("id") var req transitionRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { writeError(w, http.StatusBadRequest, "ungueltiger anfrage-koerper") return } err := s.svc.Transition(r.Context(), id, req.To, userID, req.Reason) switch { case err == nil: c, getErr := s.svc.Get(r.Context(), id) if getErr != nil { writeError(w, http.StatusInternalServerError, "status geaendert, lesen fehlgeschlagen") return } writeJSON(w, http.StatusOK, c) case errors.Is(err, caseobj.ErrNotFound): writeError(w, http.StatusNotFound, "akte nicht gefunden") default: writeError(w, http.StatusUnprocessableEntity, "statuswechsel nicht zulaessig") } } func writeJSON(w http.ResponseWriter, status int, v any) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(status) _ = json.NewEncoder(w).Encode(v) } func writeError(w http.ResponseWriter, status int, message string) { writeJSON(w, status, map[string]string{"error": message}) }