Files
Agent-System/internal/server/admin_workflow_handlers.go
weichen f3f23a3018 feat(errcode): 用稳定错误码记录 HTTP 与异步失败
失败响应此前不写日志,request_id 无法对照原因。注册表不覆盖处理器已经给出的 HTTP 状态。
2026-09-28 02:27:16 +00:00

195 lines
5.7 KiB
Go

package server
import (
"context"
"encoding/json"
"errors"
"net/http"
"strconv"
"chat-agent-system/internal/auth"
"chat-agent-system/internal/workflowdef"
)
func (s *Server) requireWorkflowAdmin(w http.ResponseWriter, r *http.Request) (*auth.UserAccount, bool) {
user, ok := s.requireSystemAdmin(w, r)
if !ok {
return nil, false
}
if s.wfDefSvc() == nil || !s.wfDefSvc().Available() {
writeSafeError(w, r, http.StatusServiceUnavailable, "工作流定义服务需要数据库 IAM")
return nil, false
}
return user, true
}
func (s *Server) handleAdminWorkflows(w http.ResponseWriter, r *http.Request) {
user, ok := s.requireWorkflowAdmin(w, r)
if !ok {
return
}
switch r.Method {
case http.MethodGet:
defs, err := s.wfDefSvc().ListDefinitions(r.Context())
if err != nil {
writeDomainError(w, r, http.StatusInternalServerError, err)
return
}
writeJSONResponse(w, http.StatusOK, map[string]any{"workflows": defs})
case http.MethodPost:
var req workflowdef.CreateDefinitionInput
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
writeSafeError(w, r, http.StatusBadRequest, "无效的 JSON")
return
}
def, ver, err := s.wfDefSvc().CreateDefinition(r.Context(), req, user.ID)
if err != nil {
status := http.StatusBadRequest
if errors.Is(err, workflowdef.ErrValidationFailed) {
status = http.StatusUnprocessableEntity
}
writeDomainError(w, r, status, err)
return
}
writeJSONResponse(w, http.StatusCreated, map[string]any{"definition": def, "version": ver})
default:
writeSafeError(w, r, http.StatusMethodNotAllowed, "method not allowed")
}
}
func (s *Server) handleAdminWorkflowByID(w http.ResponseWriter, r *http.Request) {
if _, ok := s.requireWorkflowAdmin(w, r); !ok {
return
}
id, err := strconv.ParseInt(r.PathValue("id"), 10, 64)
if err != nil || id <= 0 {
writeSafeError(w, r, http.StatusBadRequest, "invalid workflow id")
return
}
if r.Method != http.MethodGet {
writeSafeError(w, r, http.StatusMethodNotAllowed, "method not allowed")
return
}
def, err := s.wfDefSvc().GetDefinition(r.Context(), id)
if err != nil {
writeDomainError(w, r, http.StatusNotFound, err)
return
}
versions, err := s.wfDefSvc().ListVersions(r.Context(), id)
if err != nil {
writeDomainError(w, r, http.StatusInternalServerError, err)
return
}
writeJSONResponse(w, http.StatusOK, map[string]any{"definition": def, "versions": versions})
}
func (s *Server) handleAdminWorkflowVersion(w http.ResponseWriter, r *http.Request) {
user, ok := s.requireWorkflowAdmin(w, r)
if !ok {
return
}
workflowID, err := strconv.ParseInt(r.PathValue("id"), 10, 64)
if err != nil || workflowID <= 0 {
writeSafeError(w, r, http.StatusBadRequest, "invalid workflow id")
return
}
version, err := strconv.Atoi(r.PathValue("version"))
if err != nil || version <= 0 {
writeSafeError(w, r, http.StatusBadRequest, "invalid version")
return
}
switch r.Method {
case http.MethodPatch, http.MethodPut:
var body struct {
Definition workflowdef.VersionDefinition `json:"definition"`
}
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
writeSafeError(w, r, http.StatusBadRequest, "invalid json")
return
}
updated, err := s.wfDefSvc().UpdateDraft(r.Context(), workflowID, version, body.Definition)
if err != nil {
status := http.StatusBadRequest
if errors.Is(err, workflowdef.ErrNotDraft) {
status = http.StatusConflict
}
writeDomainError(w, r, status, err)
return
}
writeJSONResponse(w, http.StatusOK, updated)
default:
writeSafeError(w, r, http.StatusMethodNotAllowed, "method not allowed")
}
_ = user
}
func (s *Server) handleAdminWorkflowValidate(w http.ResponseWriter, r *http.Request) {
if _, ok := s.requireWorkflowAdmin(w, r); !ok {
return
}
if r.Method != http.MethodPost {
writeSafeError(w, r, http.StatusMethodNotAllowed, "method not allowed")
return
}
workflowID, _ := strconv.ParseInt(r.PathValue("id"), 10, 64)
version, _ := strconv.Atoi(r.PathValue("version"))
res, err := s.wfDefSvc().ValidateVersion(r.Context(), workflowID, version)
if err != nil {
writeDomainError(w, r, http.StatusNotFound, err)
return
}
writeJSONResponse(w, http.StatusOK, res)
}
func (s *Server) handleAdminWorkflowPublish(w http.ResponseWriter, r *http.Request) {
user, ok := s.requireWorkflowAdmin(w, r)
if !ok {
return
}
if r.Method != http.MethodPost {
writeSafeError(w, r, http.StatusMethodNotAllowed, "method not allowed")
return
}
workflowID, _ := strconv.ParseInt(r.PathValue("id"), 10, 64)
version, _ := strconv.Atoi(r.PathValue("version"))
ver, err := s.wfDefSvc().PublishVersion(r.Context(), workflowID, version, user.ID)
if err != nil {
status := http.StatusBadRequest
if errors.Is(err, workflowdef.ErrValidationFailed) {
status = http.StatusUnprocessableEntity
}
writeDomainError(w, r, status, err)
return
}
if refresher, ok := s.pwSvc().Catalog().(interface {
Refresh(context.Context) error
}); ok {
_ = refresher.Refresh(r.Context())
}
writeJSONResponse(w, http.StatusOK, ver)
}
func (s *Server) handleAdminWorkflowRetire(w http.ResponseWriter, r *http.Request) {
if _, ok := s.requireWorkflowAdmin(w, r); !ok {
return
}
if r.Method != http.MethodPost {
writeSafeError(w, r, http.StatusMethodNotAllowed, "method not allowed")
return
}
workflowID, _ := strconv.ParseInt(r.PathValue("id"), 10, 64)
version, _ := strconv.Atoi(r.PathValue("version"))
ver, err := s.wfDefSvc().RetireVersion(r.Context(), workflowID, version)
if err != nil {
writeDomainError(w, r, http.StatusBadRequest, err)
return
}
if refresher, ok := s.pwSvc().Catalog().(interface {
Refresh(context.Context) error
}); ok {
_ = refresher.Refresh(r.Context())
}
writeJSONResponse(w, http.StatusOK, ver)
}