package memory import ( "encoding/json" "net/http" "strconv" "aigateway.local/core/internal/identity" "aigateway.local/core/internal/platform/apiresponse" ) // HTTPHandler 提供门户个人记忆 CRUD 与召回。 type HTTPHandler struct { service *Service identity *identity.Service mux *http.ServeMux } func NewHTTPHandler(service *Service, identityService *identity.Service) *HTTPHandler { h := &HTTPHandler{service: service, identity: identityService, mux: http.NewServeMux()} h.mux.HandleFunc("GET /api/v1/portal/memories", h.list) h.mux.HandleFunc("POST /api/v1/portal/memories", h.save) h.mux.HandleFunc("PUT /api/v1/portal/memories/{id}", h.save) h.mux.HandleFunc("DELETE /api/v1/portal/memories/{id}", h.delete) h.mux.HandleFunc("POST /api/v1/portal/memories/recall", h.recall) return h } func (h *HTTPHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { h.mux.ServeHTTP(w, r) } func (h *HTTPHandler) account(w http.ResponseWriter, r *http.Request) (identity.Account, bool) { account, err := h.identity.Authenticate(r.Context(), identity.KindPortal, r.Header.Get("Authorization")) if err != nil { apiresponse.Error(w, http.StatusUnauthorized, "登录状态无效或已过期") return identity.Account{}, false } return account, true } type memoryInput struct { Category string `json:"category"` Content string `json:"content"` Importance int `json:"importance"` SharedWith []string `json:"shared_with"` Source string `json:"source"` } func (h *HTTPHandler) decode(w http.ResponseWriter, r *http.Request) (memoryInput, bool) { var input memoryInput decoder := json.NewDecoder(http.MaxBytesReader(w, r.Body, 1<<20)) decoder.DisallowUnknownFields() if decoder.Decode(&input) != nil { apiresponse.Error(w, http.StatusBadRequest, "请求格式无效") return input, false } return input, true } func (h *HTTPHandler) list(w http.ResponseWriter, r *http.Request) { a, ok := h.account(w, r) if !ok { return } items, err := h.service.List(r.Context(), OwnerUser, a.ID, 200) if err != nil { apiresponse.Error(w, http.StatusServiceUnavailable, "记忆查询失败") return } apiresponse.OK(w, items) } func (h *HTTPHandler) save(w http.ResponseWriter, r *http.Request) { a, ok := h.account(w, r) if !ok { return } input, ok := h.decode(w, r) if !ok { return } entry, err := h.service.Save(r.Context(), OwnerUser, a.ID, r.PathValue("id"), input.Category, input.Content, input.Source, input.Importance, input.SharedWith, a.ID) if err != nil { apiresponse.Error(w, http.StatusBadRequest, err.Error()) return } apiresponse.OK(w, entry) } func (h *HTTPHandler) delete(w http.ResponseWriter, r *http.Request) { a, ok := h.account(w, r) if !ok { return } entry, err := h.service.Get(r.Context(), r.PathValue("id")) if err != nil { apiresponse.Error(w, http.StatusNotFound, "记忆不存在") return } // 仅本人或共享给本人的可删。 if entry.OwnerKind == OwnerUser && entry.OwnerID != a.ID { apiresponse.Error(w, http.StatusForbidden, "无权删除该记忆") return } if err := h.service.Delete(r.Context(), r.PathValue("id")); err != nil { apiresponse.Error(w, http.StatusBadRequest, err.Error()) return } apiresponse.OK(w, map[string]bool{"deleted": true}) } func (h *HTTPHandler) recall(w http.ResponseWriter, r *http.Request) { a, ok := h.account(w, r) if !ok { return } var input struct { Query string `json:"query"` Limit int `json:"limit"` } decoder := json.NewDecoder(http.MaxBytesReader(w, r.Body, 1<<20)) decoder.DisallowUnknownFields() if decoder.Decode(&input) != nil || input.Query == "" { apiresponse.Error(w, http.StatusBadRequest, "查询内容不能为空") return } if input.Limit <= 0 { input.Limit = 5 } items, err := h.service.Recall(r.Context(), a.ID, a.DepartmentID, input.Query, input.Limit) if err != nil { apiresponse.Error(w, http.StatusServiceUnavailable, "记忆召回失败") return } apiresponse.OK(w, items) } var _ = strconv.Itoa