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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user