package handler import ( "bytes" "encoding/json" "fmt" "html" "io" "git.yeij.top/AskaEth/Cyrene/pkg/logger" "net/http" "time" "github.com/gin-gonic/gin" "git.yeij.top/AskaEth/Cyrene/gateway/internal/middleware" ) // MemoryHandler 记忆查询处理器 — 代理到 Memory-Service type MemoryHandler struct { memoryServiceURL string client *http.Client } // NewMemoryHandler 创建记忆处理器 func NewMemoryHandler(memoryServiceURL string) *MemoryHandler { return &MemoryHandler{ memoryServiceURL: memoryServiceURL, client: &http.Client{ Timeout: 15 * time.Second, }, } } // Query 搜索用户记忆 — 代理 POST /api/v1/memories/query // 管理员可通过 user_id 查询参数查询任意用户的记忆 func (h *MemoryHandler) Query(c *gin.Context) { authUserID := middleware.GetUserID(c) userID := c.Query("user_id") // 非管理员只能查询自己的记忆;管理员可通过查询参数指定目标用户 if authUserID != "admin" || userID == "" { userID = authUserID } query := c.Query("q") if query == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "查询参数q不能为空"}) return } // 使用 memory-service 的 POST /api/v1/memories/query 端点 reqBody, _ := json.Marshal(map[string]interface{}{ "user_id": userID, "query_text": query, "limit": 10, }) url := fmt.Sprintf("%s/api/v1/memories/query", h.memoryServiceURL) httpReq, err := http.NewRequest("POST", url, bytes.NewReader(reqBody)) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "构建请求失败"}) return } httpReq.Header.Set("Content-Type", "application/json") resp, err := h.client.Do(httpReq) if err != nil { logger.Printf("[memory] Memory-Service 不可达 (Query): %v", err) c.JSON(http.StatusBadGateway, gin.H{ "error": fmt.Sprintf("Memory-Service 不可达: %v", err), "errorType": "memory_service_unreachable", "hint": "Memory-Service 服务未启动或不可达,请先在「服务管理」面板中启动 Memory-Service", }) return } defer resp.Body.Close() body, _ := io.ReadAll(resp.Body) var result interface{} json.Unmarshal(body, &result) c.JSON(resp.StatusCode, result) } // List 列出用户所有记忆 — 代理 GET /api/v1/memories?user_id=... // 管理员可通过 user_id 查询参数查询任意用户的记忆 func (h *MemoryHandler) List(c *gin.Context) { authUserID := middleware.GetUserID(c) userID := c.Query("user_id") // 非管理员只能查询自己的记忆;管理员可通过查询参数指定目标用户 if authUserID != "admin" || userID == "" { userID = authUserID } limit := c.Query("limit") offset := c.Query("offset") url := fmt.Sprintf("%s/api/v1/memories?user_id=%s", h.memoryServiceURL, userID) if limit != "" { url += "&limit=" + limit } if offset != "" { url += "&offset=" + offset } resp, err := h.client.Get(url) if err != nil { logger.Printf("[memory] Memory-Service 不可达 (List): %v", err) c.JSON(http.StatusBadGateway, gin.H{ "error": fmt.Sprintf("Memory-Service 不可达: %v", err), "errorType": "memory_service_unreachable", "hint": "Memory-Service 服务未启动或不可达,请先在「服务管理」面板中启动 Memory-Service", }) return } defer resp.Body.Close() body, _ := io.ReadAll(resp.Body) var result interface{} json.Unmarshal(body, &result) c.JSON(resp.StatusCode, result) } // Add 手动添加记忆 — 代理 POST /api/v1/memories // 管理员可通过请求体中的 user_id 字段为任意用户添加记忆 func (h *MemoryHandler) Add(c *gin.Context) { authUserID := middleware.GetUserID(c) var req struct { UserID string `json:"user_id"` Content string `json:"content" binding:"required"` Category string `json:"category"` Priority int `json:"priority"` } if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "请求参数无效"}) return } if req.Category == "" { req.Category = "other" } if req.Priority <= 0 { req.Priority = 1 } // 管理员可通过请求体指定目标用户,否则使用认证用户 userID := authUserID if authUserID == "admin" && req.UserID != "" { userID = req.UserID } // 转发到 Memory-Service(对用户输入进行 HTML 转义防 XSS) memReq := map[string]interface{}{ "user_id": userID, "content": html.EscapeString(req.Content), "category": html.EscapeString(req.Category), "priority": req.Priority, } reqBody, _ := json.Marshal(memReq) url := fmt.Sprintf("%s/api/v1/memories", h.memoryServiceURL) httpReq, err := http.NewRequest("POST", url, bytes.NewReader(reqBody)) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "构建请求失败"}) return } httpReq.Header.Set("Content-Type", "application/json") resp, err := h.client.Do(httpReq) if err != nil { logger.Printf("[memory] Memory-Service 不可达 (Add): %v", err) c.JSON(http.StatusBadGateway, gin.H{ "error": fmt.Sprintf("Memory-Service 不可达: %v", err), "errorType": "memory_service_unreachable", "hint": "Memory-Service 服务未启动或不可达,请先在「服务管理」面板中启动 Memory-Service", }) return } defer resp.Body.Close() body, _ := io.ReadAll(resp.Body) var result interface{} json.Unmarshal(body, &result) c.JSON(resp.StatusCode, result) } // Delete 删除单条记忆 — 代理 DELETE /api/v1/memories/:id func (h *MemoryHandler) Delete(c *gin.Context) { memoryID := c.Query("id") if memoryID == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "缺少 id 参数"}) return } url := fmt.Sprintf("%s/api/v1/memories/%s", h.memoryServiceURL, memoryID) req, err := http.NewRequest("DELETE", url, nil) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "构建请求失败"}) return } resp, err := h.client.Do(req) if err != nil { logger.Printf("[memory] Memory-Service 不可达 (Delete): %v", err) c.JSON(http.StatusBadGateway, gin.H{ "error": fmt.Sprintf("Memory-Service 不可达: %v", err), "errorType": "memory_service_unreachable", "hint": "Memory-Service 服务未启动或不可达,请先在「服务管理」面板中启动 Memory-Service", }) return } defer resp.Body.Close() body, _ := io.ReadAll(resp.Body) var result interface{} json.Unmarshal(body, &result) c.JSON(resp.StatusCode, result) }