From 6954d375b58dd60e6b1399ecf921dc970898a53a Mon Sep 17 00:00:00 2001 From: Your Name Date: Tue, 20 Oct 2026 21:42:43 +0800 Subject: [PATCH] fix: select support identity by IM tenant --- internal/store/support.go | 9 +++++---- internal/support/service.go | 10 ++-------- internal/support/service_test.go | 32 ++++++++++++++++++++++++++++---- 3 files changed, 35 insertions(+), 16 deletions(-) diff --git a/internal/store/support.go b/internal/store/support.go index fb348f8..c477d23 100644 --- a/internal/store/support.go +++ b/internal/store/support.go @@ -37,13 +37,14 @@ func (s *Store) CustomerServiceByID(ctx context.Context, tenantID, serviceID int return &row, err } -func (s *Store) MemberIMIdentity(ctx context.Context, tenantID, memberID int64) (*MemberIMIdentity, error) { +func (s *Store) MemberIMIdentityForTenant(ctx context.Context, tenantID, memberID int64, imTenantCode string) (*MemberIMIdentity, error) { if s == nil || s.db == nil { return nil, errors.New("store is unavailable") } var row MemberIMIdentity err := s.db.GetContext(ctx, &row, `SELECT id,tenant_id,member_id,im_tenant_code,im_user_id,im_username,im_password_cipher - FROM hg_app_member_im_identity WHERE tenant_id=? AND member_id=? LIMIT 1`, tenantID, memberID) + FROM hg_app_member_im_identity WHERE tenant_id=? AND member_id=? AND im_tenant_code=? LIMIT 1`, + tenantID, memberID, imTenantCode) if errors.Is(err, sql.ErrNoRows) { return nil, ErrResourceNotFound } @@ -77,10 +78,10 @@ func (s *Store) SaveMemberIMIdentity(ctx context.Context, identity *MemberIMIden im_password_cipher=VALUES(im_password_cipher),sync_status=1,last_error='',updated_at=UTC_TIMESTAMP(3)`, identity.TenantID, identity.MemberID, identity.IMTenantCode, identity.IMUserID, identity.IMUsername, identity.IMPasswordCipher) if err == nil { - return s.MemberIMIdentity(ctx, identity.TenantID, identity.MemberID) + return s.MemberIMIdentityForTenant(ctx, identity.TenantID, identity.MemberID, identity.IMTenantCode) } // A concurrent request may have completed the same deterministic mapping. - existing, findErr := s.MemberIMIdentity(ctx, identity.TenantID, identity.MemberID) + existing, findErr := s.MemberIMIdentityForTenant(ctx, identity.TenantID, identity.MemberID, identity.IMTenantCode) if findErr == nil { return existing, nil } diff --git a/internal/support/service.go b/internal/support/service.go index bdaf1f9..03cfe5d 100644 --- a/internal/support/service.go +++ b/internal/support/service.go @@ -20,7 +20,7 @@ import ( type Repository interface { CustomerServiceByID(context.Context, int64, int64) (*store.CustomerService, error) - MemberIMIdentity(context.Context, int64, int64) (*store.MemberIMIdentity, error) + MemberIMIdentityForTenant(context.Context, int64, int64, string) (*store.MemberIMIdentity, error) MemberIMProfile(context.Context, int64, int64) (*store.MemberIMProfile, error) SaveMemberIMIdentity(context.Context, *store.MemberIMIdentity) (*store.MemberIMIdentity, error) } @@ -188,14 +188,8 @@ func (s *Service) OpenSession(ctx context.Context, tenantID, memberID, serviceID } func (s *Service) ensureIdentity(ctx context.Context, tenantID, memberID int64, imTenantCode string) (*store.MemberIMIdentity, error) { - identity, err := s.repository.MemberIMIdentity(ctx, tenantID, memberID) + identity, err := s.repository.MemberIMIdentityForTenant(ctx, tenantID, memberID, imTenantCode) if err == nil { - if !strings.EqualFold(identity.IMTenantCode, imTenantCode) { - // A tenant switch creates a distinct IM user. Keep the old tenant's IM - // account intact, but replace this member's local mapping with a newly - // provisioned account in the active tenant. - return s.createIdentity(ctx, tenantID, memberID, imTenantCode) - } if identity.IMUserID == "" || identity.IMUsername == "" { return nil, ErrIMUnavailable } diff --git a/internal/support/service_test.go b/internal/support/service_test.go index 78831af..30847f8 100644 --- a/internal/support/service_test.go +++ b/internal/support/service_test.go @@ -13,15 +13,20 @@ import ( ) type repositoryStub struct { - agent *store.CustomerService - identity *store.MemberIMIdentity - profile *store.MemberIMProfile + agent *store.CustomerService + identity *store.MemberIMIdentity + profile *store.MemberIMProfile + requestedIMTenantCode string } func (r *repositoryStub) CustomerServiceByID(context.Context, int64, int64) (*store.CustomerService, error) { return r.agent, nil } -func (r *repositoryStub) MemberIMIdentity(context.Context, int64, int64) (*store.MemberIMIdentity, error) { +func (r *repositoryStub) MemberIMIdentityForTenant(_ context.Context, _, _ int64, imTenantCode string) (*store.MemberIMIdentity, error) { + r.requestedIMTenantCode = imTenantCode + if r.identity == nil || !strings.EqualFold(r.identity.IMTenantCode, imTenantCode) { + return nil, store.ErrResourceNotFound + } return r.identity, nil } func (r *repositoryStub) MemberIMProfile(context.Context, int64, int64) (*store.MemberIMProfile, error) { @@ -207,3 +212,22 @@ func TestEnsureIdentityReprovisionsMemberWhenIMTenantChanges(t *testing.T) { t.Fatalf("provisioning login attempts = %d, want 1", im.loginAttempts) } } + +func TestEnsureIdentityQueriesTheActiveIMTenantMapping(t *testing.T) { + repository := &repositoryStub{ + identity: &store.MemberIMIdentity{TenantID: 0, MemberID: 9, IMTenantCode: "allrich", IMUserID: "usr_old", IMUsername: "olduser"}, + profile: &store.MemberIMProfile{MemberID: 9, Username: "member01"}, + } + im := &imStub{} + service := New(repository, im, config.AuthConfig{FinanceMasterKey: strings.Repeat("k", 32)}) + identity, err := service.ensureIdentity(context.Background(), 0, 9, "default") + if err != nil { + t.Fatal(err) + } + if repository.requestedIMTenantCode != "default" { + t.Fatalf("queried IM tenant = %q, want default", repository.requestedIMTenantCode) + } + if identity.IMTenantCode != "default" { + t.Fatalf("identity tenant = %q, want default", identity.IMTenantCode) + } +}