From 0fcbbb9318bfd034fe728c088bb182722263cd11 Mon Sep 17 00:00:00 2001 From: richjohnrambo Date: Fri, 18 Sep 2026 20:57:50 +0800 Subject: [PATCH] fix: publish member joined after group invitation --- server/im_http.go | 6 ++++++ server/im_http_test.go | 18 ++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/server/im_http.go b/server/im_http.go index f3ff8c0..184682f 100644 --- a/server/im_http.go +++ b/server/im_http.go @@ -4628,6 +4628,8 @@ func serveIMGroupInvitations(wrt http.ResponseWriter, req *http.Request, topic s } if joined { item["status"] = types.GroupInvitationStatusJoined + publishIMGroupRealtimeEvent(principal.TenantID, topic, principal.Uid, "group.member.joined", + map[string]any{"user_id": uid.UserId(), "joined": true, "source": "direct_invitation"}) if imReliableHTTPEventsEnabled(req) { publishIMGroupMemberJoinedSystemNotice(principal.TenantID, topic, principal.Uid, uid, "有成员通过邀请加入了群聊。", @@ -4989,6 +4991,10 @@ func serveIMGroupJoinRequestAction(wrt http.ResponseWriter, req *http.Request, t []imSystemMessageAction{{Type: "group_profile", Label: "查看群聊", TargetID: topic}}, map[string]any{"request_id": updated.RequestID, "source": "approval"}) } + if updated.Status == types.GroupJoinRequestStatusAccepted { + publishIMGroupRealtimeEvent(principal.TenantID, topic, principal.Uid, "group.member.joined", + map[string]any{"request_id": updated.RequestID, "user_id": updated.ApplicantUserID.UserId(), "joined": true, "source": "approval"}) + } publishIMGroupRealtimeEvent(principal.TenantID, topic, principal.Uid, "group.join_request."+updated.Status, map[string]any{"request_id": updated.RequestID, "user_id": updated.ApplicantUserID.UserId(), "status": updated.Status}) writeIMSuccess(wrt, req, groupJoinRequestPayload(updated)) diff --git a/server/im_http_test.go b/server/im_http_test.go index 0283f66..9d63484 100644 --- a/server/im_http_test.go +++ b/server/im_http_test.go @@ -3715,16 +3715,19 @@ func TestServeIMGroupInvitationAddsNonMember(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() expectIMBearer(t, ctrl, "token-1", 7, 42) + oldHub := globals.hub oldSubs := store.Subs oldGroupBusiness := store.GroupBusiness oldSecurity := store.Security subs := mock_store.NewMockSubsPersistenceInterface(ctrl) groupStore := &testGroupBusinessStore{} securityStore := &testUserSecurityStore{} + globals.hub = &Hub{routeSrv: make(chan *ServerComMessage, 2)} store.Subs = subs store.GroupBusiness = groupStore store.Security = securityStore defer func() { + globals.hub = oldHub store.Subs = oldSubs store.GroupBusiness = oldGroupBusiness store.Security = oldSecurity @@ -3758,6 +3761,21 @@ func TestServeIMGroupInvitationAddsNonMember(t *testing.T) { if len(securityStore.notifications) != 0 { t.Fatalf("direct invite should not create approval notification: %#v", securityStore.notifications) } + select { + case msg := <-globals.hub.routeSrv: + if msg.Info == nil || msg.Info.Event != "group.member.joined" || msg.Info.Topic != "grpAlpha" { + t.Fatalf("unexpected realtime event: %#v", msg) + } + var payload map[string]any + if err := json.Unmarshal(msg.Info.Payload, &payload); err != nil { + t.Fatalf("decode payload: %v", err) + } + if payload["user_id"] != target.UserId() || payload["member_sync"] != "single" || payload["source"] != "direct_invitation" { + t.Fatalf("unexpected member joined payload: %#v", payload) + } + default: + t.Fatal("missing group.member.joined realtime event") + } } func TestServeIMGroupInvitationCreatesApprovalRequest(t *testing.T) {