fix: preserve pending support state on private profiles
Deploy olons-server production / deploy (push) Successful in 2m42s

This commit is contained in:
Your Name
2026-10-20 21:31:44 +08:00
parent 5f85472757
commit 975a2a2ed2
3 changed files with 47 additions and 34 deletions
+6
View File
@@ -388,6 +388,12 @@ func IsConflict(err error) bool {
return errors.As(err, &target) && target.Status == http.StatusConflict return errors.As(err, &target) && target.Status == http.StatusConflict
} }
func IsFriendRequestConflict(err error) bool {
var target *Error
return errors.As(err, &target) && target.Status == http.StatusConflict &&
strings.EqualFold(strings.TrimSpace(target.Reason), "FRIEND_REQUEST_CONFLICT")
}
func IsPermissionDenied(err error) bool { func IsPermissionDenied(err error) bool {
var target *Error var target *Error
return errors.As(err, &target) && target.Status == http.StatusForbidden return errors.As(err, &target) && target.Status == http.StatusForbidden
+17 -19
View File
@@ -125,42 +125,42 @@ func (s *Service) OpenSession(ctx context.Context, tenantID, memberID, serviceID
if !strings.EqualFold(strings.TrimSpace(imSession.UserID), strings.TrimSpace(identity.IMUserID)) { if !strings.EqualFold(strings.TrimSpace(imSession.UserID), strings.TrimSpace(identity.IMUserID)) {
return nil, ErrIMUnavailable return nil, ErrIMUnavailable
} }
var conversation *imclient.Conversation
profile, err := s.im.UserProfile(ctx, imSession.Token, agent.IMUserID) profile, err := s.im.UserProfile(ctx, imSession.Token, agent.IMUserID)
relationship := ""
if err != nil { if err != nil {
if !imclient.IsPermissionDenied(err) { if !imclient.IsPermissionDenied(err) {
return nil, fmt.Errorf("query customer service relationship: %w", err) return nil, fmt.Errorf("query customer service relationship: %w", err)
} }
conversation, err = s.im.EnsureP2P(ctx, imSession.Token, agent.IMUserID, key := fmt.Sprintf("customer-service:%d:%d:%d", tenantID, memberID, serviceID)
fmt.Sprintf("p2p:%s:%s", identity.IMUserID, agent.IMUserID)) request, requestErr := s.im.CreateFriendRequest(ctx, imSession.Token, agent.IMUserID, key)
if err != nil { if imclient.IsFriendRequestConflict(requestErr) {
return nil, fmt.Errorf("query customer service relationship: %w", err) return nil, ErrRelationPending
} }
if requestErr != nil {
if imclient.IsConflict(requestErr) {
return nil, ErrRelationDenied
} }
relationship := "" return nil, fmt.Errorf("create customer service friend request: %w", requestErr)
if profile != nil { }
relationship = strings.TrimSpace(profile.Relationship) relationship = strings.TrimSpace(request.Status)
} else { } else {
relationship = strings.TrimSpace(conversation.Relationship) relationship = strings.TrimSpace(profile.Relationship)
} }
switch relationship { switch relationship {
case "friend": case "friend":
case "none", "": case "none", "":
key := fmt.Sprintf("customer-service:%d:%d:%d", tenantID, memberID, serviceID) key := fmt.Sprintf("customer-service:%d:%d:%d", tenantID, memberID, serviceID)
request, requestErr := s.im.CreateFriendRequest(ctx, imSession.Token, agent.IMUserID, key) request, requestErr := s.im.CreateFriendRequest(ctx, imSession.Token, agent.IMUserID, key)
if imclient.IsFriendRequestConflict(requestErr) {
return nil, ErrRelationPending
}
if requestErr != nil && !imclient.IsConflict(requestErr) { if requestErr != nil && !imclient.IsConflict(requestErr) {
return nil, fmt.Errorf("create customer service friend request: %w", requestErr) return nil, fmt.Errorf("create customer service friend request: %w", requestErr)
} }
if requestErr != nil { if requestErr != nil {
conversation, err = s.im.EnsureP2P(ctx, imSession.Token, agent.IMUserID, return nil, ErrRelationDenied
fmt.Sprintf("p2p:%s:%s", identity.IMUserID, agent.IMUserID))
if err != nil {
return nil, fmt.Errorf("refresh customer service relationship: %w", err)
}
relationship = strings.TrimSpace(conversation.Relationship)
} else { } else {
relationship = strings.TrimSpace(request.Status) relationship = strings.TrimSpace(request.Status)
conversation = nil
} }
if relationship != "accepted" && relationship != "friend" { if relationship != "accepted" && relationship != "friend" {
return nil, ErrRelationPending return nil, ErrRelationPending
@@ -170,13 +170,11 @@ func (s *Service) OpenSession(ctx context.Context, tenantID, memberID, serviceID
default: default:
return nil, ErrRelationDenied return nil, ErrRelationDenied
} }
if conversation == nil { conversation, err := s.im.EnsureP2P(ctx, imSession.Token, agent.IMUserID,
conversation, err = s.im.EnsureP2P(ctx, imSession.Token, agent.IMUserID,
fmt.Sprintf("p2p:%s:%s", identity.IMUserID, agent.IMUserID)) fmt.Sprintf("p2p:%s:%s", identity.IMUserID, agent.IMUserID))
if err != nil { if err != nil {
return nil, fmt.Errorf("ensure customer service conversation: %w", err) return nil, fmt.Errorf("ensure customer service conversation: %w", err)
} }
}
if strings.TrimSpace(conversation.Topic) == "" || (conversation.Relationship != "" && conversation.Relationship != "friend") || !conversation.CanSend { if strings.TrimSpace(conversation.Topic) == "" || (conversation.Relationship != "" && conversation.Relationship != "friend") || !conversation.CanSend {
return nil, ErrRelationDenied return nil, ErrRelationDenied
} }
+12 -3
View File
@@ -42,7 +42,9 @@ type imStub struct {
resetPasswords int resetPasswords int
loginError error loginError error
profileError error profileError error
friendError error
conversation *imclient.Conversation conversation *imclient.Conversation
conversationErr error
ensureP2PCalls int ensureP2PCalls int
} }
@@ -78,10 +80,16 @@ func (i *imStub) UserProfile(context.Context, string, string) (*imclient.UserPro
} }
func (i *imStub) CreateFriendRequest(context.Context, string, string, string) (*imclient.FriendRequest, error) { func (i *imStub) CreateFriendRequest(context.Context, string, string, string) (*imclient.FriendRequest, error) {
i.friendRequests++ i.friendRequests++
if i.friendError != nil {
return nil, i.friendError
}
return &imclient.FriendRequest{RequestID: "frq_1", Status: "accepted"}, nil return &imclient.FriendRequest{RequestID: "frq_1", Status: "accepted"}, nil
} }
func (i *imStub) EnsureP2P(context.Context, string, string, string) (*imclient.Conversation, error) { func (i *imStub) EnsureP2P(context.Context, string, string, string) (*imclient.Conversation, error) {
i.ensureP2PCalls++ i.ensureP2PCalls++
if i.conversationErr != nil {
return nil, i.conversationErr
}
if i.conversation != nil { if i.conversation != nil {
return i.conversation, nil return i.conversation, nil
} }
@@ -133,7 +141,8 @@ func TestOpenSessionReturnsPendingWhenPrivateProfileHidesPendingRelationship(t *
} }
im := &imStub{ im := &imStub{
profileError: &imclient.Error{Status: 403, Reason: "PERMISSION_DENIED"}, profileError: &imclient.Error{Status: 403, Reason: "PERMISSION_DENIED"},
conversation: &imclient.Conversation{Topic: "p2p-topic", Relationship: "pending_outbound", CanSend: false}, friendError: &imclient.Error{Status: 409, Reason: "FRIEND_REQUEST_CONFLICT"},
conversationErr: &imclient.Error{Status: 403, Reason: "PERMISSION_DENIED"},
} }
service := New(repository, im, config.AuthConfig{FinanceMasterKey: strings.Repeat("k", 32)}) service := New(repository, im, config.AuthConfig{FinanceMasterKey: strings.Repeat("k", 32)})
repository.identity.IMPasswordCipher, _ = service.encryptCredential("password") repository.identity.IMPasswordCipher, _ = service.encryptCredential("password")
@@ -142,8 +151,8 @@ func TestOpenSessionReturnsPendingWhenPrivateProfileHidesPendingRelationship(t *
if !errors.Is(err, ErrRelationPending) { if !errors.Is(err, ErrRelationPending) {
t.Fatalf("error = %v, want ErrRelationPending", err) t.Fatalf("error = %v, want ErrRelationPending", err)
} }
if im.ensureP2PCalls != 1 || im.friendRequests != 0 { if im.ensureP2PCalls != 0 || im.friendRequests != 1 {
t.Fatalf("ensure p2p calls=%d friend requests=%d, want 1 and 0", im.ensureP2PCalls, im.friendRequests) t.Fatalf("ensure p2p calls=%d friend requests=%d, want 0 and 1", im.ensureP2PCalls, im.friendRequests)
} }
} }