package workbench import ( "encoding/json" "net/http" "aigateway.local/core/internal/identity" "aigateway.local/core/internal/platform/apiresponse" ) // InboxAdminHTTPHandler 向管理端暴露站内消息:消息中心、未读数、广播与读回执。 type InboxAdminHTTPHandler struct { inbox *InboxService identity *identity.Service mux *http.ServeMux } func NewInboxAdminHTTPHandler(inbox *InboxService, identityService *identity.Service) *InboxAdminHTTPHandler { h := &InboxAdminHTTPHandler{inbox: inbox, identity: identityService, mux: http.NewServeMux()} h.mux.HandleFunc("GET /api/v1/admin/inbox", h.list) h.mux.HandleFunc("GET /api/v1/admin/inbox/unread", h.unread) h.mux.HandleFunc("POST /api/v1/admin/inbox/broadcast", h.broadcast) h.mux.HandleFunc("POST /api/v1/admin/inbox/read-all", h.readAll) h.mux.HandleFunc("POST /api/v1/admin/inbox/{id}/read", h.read) return h } func (h *InboxAdminHTTPHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { h.mux.ServeHTTP(w, r) } func (h *InboxAdminHTTPHandler) require(w http.ResponseWriter, r *http.Request, permission string) (identity.Account, bool) { account, err := h.identity.Authenticate(r.Context(), identity.KindAdmin, r.Header.Get("Authorization")) if err != nil { apiresponse.Error(w, http.StatusUnauthorized, "登录状态无效") return identity.Account{}, false } if !identity.HasPermission(account, permission) { apiresponse.Error(w, http.StatusForbidden, "缺少站内消息权限") return identity.Account{}, false } return account, true } func (h *InboxAdminHTTPHandler) list(w http.ResponseWriter, r *http.Request) { account, ok := h.require(w, r, identity.PermissionInboxRead) if !ok { return } items, err := h.inbox.AdminList(r.Context(), account.ID, r.URL.Query().Get("scope"), 100) if err != nil { apiresponse.Error(w, http.StatusBadRequest, err.Error()) return } apiresponse.OK(w, items) } func (h *InboxAdminHTTPHandler) unread(w http.ResponseWriter, r *http.Request) { account, ok := h.require(w, r, identity.PermissionInboxRead) if !ok { return } count, err := h.inbox.UnreadCount(r.Context(), "admin", account.ID) if err != nil { apiresponse.Error(w, http.StatusBadRequest, err.Error()) return } apiresponse.OK(w, map[string]int{"unread": count}) } func (h *InboxAdminHTTPHandler) broadcast(w http.ResponseWriter, r *http.Request) { if _, ok := h.require(w, r, identity.PermissionInboxManage); !ok { return } var input struct { RecipientKind string `json:"recipient_kind"` DepartmentIDs []string `json:"department_ids"` Category string `json:"category"` Title string `json:"title"` Body string `json:"body"` Link string `json:"link"` Payload json.RawMessage `json:"payload,omitempty"` } if !decodeAsset(w, r, &input) { return } count, err := h.inbox.Broadcast(r.Context(), InboxInput{ RecipientKind: input.RecipientKind, Category: input.Category, Title: input.Title, Body: input.Body, Link: input.Link, Payload: input.Payload, }, input.DepartmentIDs, "") if err != nil { apiresponse.Error(w, http.StatusBadRequest, err.Error()) return } apiresponse.OK(w, map[string]any{"sent": count, "ok": true}) } func (h *InboxAdminHTTPHandler) read(w http.ResponseWriter, r *http.Request) { account, ok := h.require(w, r, identity.PermissionInboxRead) if !ok { return } changed, err := h.inbox.MarkRead(r.Context(), r.PathValue("id"), "admin", account.ID) if err != nil { apiresponse.Error(w, http.StatusBadRequest, err.Error()) return } apiresponse.OK(w, map[string]bool{"read": changed}) } func (h *InboxAdminHTTPHandler) readAll(w http.ResponseWriter, r *http.Request) { account, ok := h.require(w, r, identity.PermissionInboxRead) if !ok { return } count, err := h.inbox.MarkAllRead(r.Context(), "admin", account.ID) if err != nil { apiresponse.Error(w, http.StatusBadRequest, err.Error()) return } apiresponse.OK(w, map[string]any{"read_all": count}) }