From dde07b734a9053226e09285c973d89dbbf1116d8 Mon Sep 17 00:00:00 2001 From: zhx Date: Fri, 10 Jul 2026 11:59:24 +0800 Subject: [PATCH] fix: acknowledge unsupported broadcast member removal --- .../internal/service/broadcast/service.go | 7 ++++++ .../service/broadcast/service_test.go | 25 ++++++++++++++++++- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/services/activity-service/internal/service/broadcast/service.go b/services/activity-service/internal/service/broadcast/service.go index 91ef5ef0..e77e5e19 100644 --- a/services/activity-service/internal/service/broadcast/service.go +++ b/services/activity-service/internal/service/broadcast/service.go @@ -222,6 +222,13 @@ func (s *Service) RemoveRegionBroadcastMember(ctx context.Context, userID int64, return "", false, xerr.New(xerr.InvalidArgument, err.Error()) } if err := s.publisher.DeleteGroupMember(ctx, groupID, userID); err != nil { + var restErr tencentim.RESTError + if errors.As(err, &restErr) && restErr.Code == 10007 && strings.Contains(strings.ToLower(restErr.Info), "disable kick member") { + // 区域播报群是 AVChatRoom,腾讯 IM 明确不允许服务端踢成员。用户换区依靠客户端 + // 离开旧群并加入新群;这个永久能力拒绝不能重试,否则每条 UserRegionChanged + // 都会重投到 DLQ。removed=false 保留真实结果,nil 让 MQ 确认该读模型操作已降级完成。 + return groupID, false, nil + } return groupID, false, err } // 过渡期老客户端可能仍在 v1 旧区域群里,切区时一并尽力移除; diff --git a/services/activity-service/internal/service/broadcast/service_test.go b/services/activity-service/internal/service/broadcast/service_test.go index 9f8b78c9..5365dc05 100644 --- a/services/activity-service/internal/service/broadcast/service_test.go +++ b/services/activity-service/internal/service/broadcast/service_test.go @@ -333,6 +333,28 @@ func TestRemoveRegionBroadcastMemberDeletesOnlyUserMembership(t *testing.T) { } } +func TestRemoveRegionBroadcastMemberAcknowledgesAVChatKickRestriction(t *testing.T) { + publisher := &fakePublisher{deleteError: tencentim.RESTError{Code: 10007, Info: "this group disable kick member"}} + service := New(Config{NodeID: "node-a"}, newFakeRepository(), publisher, nil) + + groupID, removed, err := service.RemoveRegionBroadcastMember(appcode.WithContext(context.Background(), "lalu"), 42, 1001) + if err != nil { + t.Fatalf("AVChat kick restriction must be acknowledged without retry: %v", err) + } + if removed || groupID != "hy_lalu_bc_r_v2_1001" { + t.Fatalf("AVChat restriction result mismatch: group_id=%s removed=%t", groupID, removed) + } + if len(publisher.deletedGroupIDs) != 1 { + t.Fatalf("unsupported v2 removal must not retry the legacy AVChat group: %v", publisher.deletedGroupIDs) + } + + // 10007 还可能表示其他群业务错误;只能降级腾讯明确返回的 disable-kick 语义。 + publisher.deleteError = tencentim.RESTError{Code: 10007, Info: "another group error"} + if _, _, err := service.RemoveRegionBroadcastMember(appcode.WithContext(context.Background(), "lalu"), 42, 1001); err == nil { + t.Fatal("unrelated Tencent IM 10007 error must remain retryable") + } +} + func TestRegionBroadcastUsesConfiguredGroupPrefix(t *testing.T) { repository := newFakeRepository() publisher := &fakePublisher{} @@ -520,6 +542,7 @@ type fakePublisher struct { ensureErrors map[string]error deletedGroupIDs []string deletedUserID int64 + deleteError error } func (p *fakePublisher) EnsureGroup(_ context.Context, spec tencentim.GroupSpec) error { @@ -535,7 +558,7 @@ func (p *fakePublisher) PublishGroupCustomMessage(_ context.Context, message ten func (p *fakePublisher) DeleteGroupMember(_ context.Context, groupID string, userID int64) error { p.deletedGroupIDs = append(p.deletedGroupIDs, groupID) p.deletedUserID = userID - return nil + return p.deleteError } type fakeRegionSource struct {