package server // chat_unread_api.go — P3 / Step 2 双轨制未读消息与位点推进 API // // GET /api/v1/chat/unread?session_id=...&since_message_id=... // POST /api/v1/chat/read // // user_id 只取自登录 Principal,忽略 query/body 中的身份字段。 import ( "encoding/json" "net/http" "strconv" "chat-agent-system/internal/app/chatflow" "chat-agent-system/internal/ctxmgr" ) func (s *Server) handleChatUnread(w http.ResponseWriter, r *http.Request) { user, ok := s.requireAuthenticatedUser(w, r) if !ok { return } sessionID := r.URL.Query().Get("session_id") if sessionID == "" { writeSafeError(w, r, http.StatusBadRequest, "missing session_id") return } var sinceID int64 if sinceStr := r.URL.Query().Get("since_message_id"); sinceStr != "" { if id, err := strconv.ParseInt(sinceStr, 10, 64); err == nil { sinceID = id } } if s.chatLog == nil { writeJSONResponse(w, http.StatusOK, ctxmgr.ChatUnreadSummary{ Total: 0, NextMessageID: 0, UnreadMessageIDs: []int64{}, Items: []ctxmgr.UnreadDayItem{}, }) return } summary, err := s.chatLog.Unread(r.Context(), s.chatPrincipal(user, chatflow.ActionUnread), chatflow.UnreadQuery{ SessionID: sessionID, SinceMessageID: sinceID, }) if err != nil { s.writeChatLogError(w, err) return } if summary == nil { summary = &ctxmgr.ChatUnreadSummary{ UnreadMessageIDs: []int64{}, Items: []ctxmgr.UnreadDayItem{}, } } writeJSONResponse(w, http.StatusOK, summary) } func (s *Server) handleChatRead(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { writeSafeError(w, r, http.StatusMethodNotAllowed, "method not allowed") return } user, ok := s.requireAuthenticatedUser(w, r) if !ok { return } var req ctxmgr.MarkChatReadRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { writeSafeError(w, r, http.StatusBadRequest, "invalid request body: "+err.Error()) return } if req.SessionID == "" { writeSafeError(w, r, http.StatusBadRequest, "missing session_id") return } if s.chatLog == nil { writeSafeError(w, r, http.StatusInternalServerError, "context manager not initialized") return } pos, err := s.chatLog.MarkRead(r.Context(), s.chatPrincipal(user, chatflow.ActionUnread), req) if err != nil { s.writeChatLogError(w, err) return } writeJSONResponse(w, http.StatusOK, map[string]any{ "ok": true, "session_id": pos.SessionID, "user_id": pos.UserID, "last_read_message_id": pos.LastReadMessageID, "dismissed_unread_ids": pos.DismissedUnreadIDs, "updated_at": pos.UpdatedAt, }) }