fix(server): create member chat sessions on invite and allow manager upsert

Project managers can register sess-p{N}-u{uid} for other members; adding a
member also ensures their private session so the sidebar is never empty.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
weichen
2026-09-20 09:11:36 +00:00
co-authored by Cursor
parent 28478bdca5
commit aef3d6d5cb
2 changed files with 45 additions and 2 deletions
+26 -2
View File
@@ -1067,6 +1067,7 @@ type upsertChatSessionRequest struct {
}
// handleUpsertChatSession 注册或更新会话元数据(立项/成员加入时同步)。
// 默认归属认证用户本人;项目管理员可为其他成员代注册(防水平越权)。
func (s *Server) handleUpsertChatSession(w http.ResponseWriter, r *http.Request) {
if s.ctxMgr == nil {
writeSafeError(w, r, http.StatusServiceUnavailable, "context manager not initialized")
@@ -1086,9 +1087,32 @@ func (s *Server) handleUpsertChatSession(w http.ResponseWriter, r *http.Request)
writeSafeError(w, r, http.StatusBadRequest, "missing session_id")
return
}
req.UserID = user.ID
summary, err := s.ctxMgr.EnsureChatSessionOwned(r.Context(), req.SessionID, req.UserID, req.ProjectID, req.Title)
ownerID := user.ID
if req.UserID > 0 && req.UserID != user.ID {
if req.ProjectID <= 0 {
writeJSONResponse(w, http.StatusBadRequest, map[string]string{
"error_code": "ERR_INVALID_PARAM",
"message": "代注册他人会话时必须指定 project_id",
})
return
}
can, err := s.authzSvc.CanManageProjectMembers(r.Context(), req.ProjectID, user.ID, user.RoleID)
if err != nil {
writeSafeError(w, r, http.StatusInternalServerError, err.Error())
return
}
if !can {
writeJSONResponse(w, http.StatusForbidden, map[string]string{
"error_code": "ERR_FORBIDDEN",
"message": "无权为其他用户注册项目会话",
})
return
}
ownerID = req.UserID
}
summary, err := s.ctxMgr.EnsureChatSessionOwned(r.Context(), req.SessionID, ownerID, req.ProjectID, req.Title)
if err != nil {
if errors.Is(err, ctxmgr.ErrSessionOwnerMismatch) {
writeJSONResponse(w, http.StatusForbidden, map[string]string{
@@ -4,6 +4,7 @@ import (
"context"
"encoding/json"
"errors"
"fmt"
"log"
"net/http"
"strconv"
@@ -128,9 +129,27 @@ func (s *Server) handleAddProjectMember(w http.ResponseWriter, r *http.Request,
writeDutySyncError(w)
return
}
s.ensureMemberChatSession(r.Context(), projectID, req.UserID)
writeJSONResponse(w, http.StatusCreated, map[string]bool{"success": true})
}
// ensureMemberChatSession 为项目成员补齐私有会话(sess-p{id}-u{uid}),失败仅记日志。
func (s *Server) ensureMemberChatSession(ctx context.Context, projectID, memberUserID int64) {
if s == nil || s.ctxMgr == nil || projectID <= 0 || memberUserID <= 0 {
return
}
title := ""
if s.pwSvc != nil {
if proj, err := s.pwSvc.GetProject(ctx, projectID, 0); err == nil && proj != nil {
title = proj.Name
}
}
sessionID := fmt.Sprintf("sess-p%d-u%d", projectID, memberUserID)
if _, err := s.ctxMgr.EnsureChatSessionOwned(ctx, sessionID, memberUserID, projectID, title); err != nil {
log.Printf("[members] ensure chat session for project=%d user=%d failed: %v", projectID, memberUserID, err)
}
}
func (s *Server) handleUpdateProjectMember(w http.ResponseWriter, r *http.Request, user *auth.UserAccount, projectID int64) {
if !s.requireProjectMemberManage(w, r, user, projectID) {
return