Add sender summaries to forward bundles

This commit is contained in:
richjohnrambo
2026-09-07 10:39:42 +08:00
parent 6330c592cf
commit a56efd4454
3 changed files with 97 additions and 9 deletions
+77 -9
View File
@@ -9380,10 +9380,10 @@ func buildForwardBundle(principal *imPrincipal, sources []struct {
Topic string `json:"topic"`
Seq imForwardSeq `json:"seq"`
}, target string, sourceReadable map[string]error, comment string) (map[string]any, []map[string]any) {
items := make([]map[string]any, 0, len(sources))
preview := make([]map[string]any, 0, min(len(sources), 3))
sourceRefs := make([]map[string]any, 0, len(sources))
failures := make([]map[string]any, 0)
sourceMessages := make([]*types.Message, 0, len(sources))
sourceTopics := make([]string, 0, len(sources))
sourceSeqs := make([]int64, 0, len(sources))
for _, source := range sources {
sourceTopic, sourceOK := normalizeIMForwardTopic(principal.Uid, source.Topic)
if sourceTopic == "" || int64(source.Seq) <= 0 || !sourceOK {
@@ -9412,22 +9412,38 @@ func buildForwardBundle(principal *imPrincipal, sources []struct {
failures = append(failures, forwardFailurePayload(sourceTopic, int64(source.Seq), target, "MESSAGE_UNAVAILABLE"))
continue
}
sourceMessages = append(sourceMessages, msg)
sourceTopics = append(sourceTopics, sourceTopic)
sourceSeqs = append(sourceSeqs, int64(source.Seq))
}
if len(failures) > 0 {
return nil, failures
}
userSummaries := forwardMessageUserSummaries(principal.TenantID, sourceMessages)
items := make([]map[string]any, 0, len(sourceMessages))
preview := make([]map[string]any, 0, min(len(sourceMessages), 3))
sourceRefs := make([]map[string]any, 0, len(sourceMessages))
for i, msg := range sourceMessages {
snapshot := favoriteSnapshot(msg)
if fromUser := forwardMessageUserSummary(msg.From, userSummaries); fromUser != nil {
snapshot["from_user"] = fromUser
}
items = append(items, snapshot)
ref := map[string]any{"topic": sourceTopic, "seq": int64(source.Seq)}
ref := map[string]any{"topic": sourceTopics[i], "seq": sourceSeqs[i]}
sourceRefs = append(sourceRefs, ref)
if len(preview) < 3 {
preview = append(preview, map[string]any{
previewItem := map[string]any{
"from": msg.From,
"type": imMessageType(msg),
"summary": textSnippet(messageText(*msg), ""),
"ts": msg.CreatedAt.UTC().Format(time.RFC3339Nano),
})
}
if fromUser := forwardMessageUserSummary(msg.From, userSummaries); fromUser != nil {
previewItem["from_user"] = fromUser
}
preview = append(preview, previewItem)
}
}
if len(failures) > 0 {
return nil, failures
}
bundleID := "fbundle_" + store.Store.GetUidString()
return map[string]any{
"bundle_id": bundleID,
@@ -9441,6 +9457,58 @@ func buildForwardBundle(principal *imPrincipal, sources []struct {
}, nil
}
func forwardMessageUserSummaries(tenantID types.TenantID, messages []*types.Message) map[types.Uid]map[string]any {
if store.Users == nil || len(messages) == 0 {
return nil
}
seen := make(map[types.Uid]bool)
uids := make([]types.Uid, 0, len(messages))
for _, msg := range messages {
if msg == nil {
continue
}
uid := types.ParseUid(msg.From)
if uid.IsZero() || seen[uid] {
continue
}
seen[uid] = true
uids = append(uids, uid)
}
if len(uids) == 0 {
return nil
}
users, err := store.Users.GetAll(tenantID, uids...)
if err != nil {
logs.Warn.Printf("im http: failed to load forward user summaries: %v", err)
return nil
}
summaries := make(map[types.Uid]map[string]any, len(users))
for i := range users {
uid := users[i].Uid()
if uid.IsZero() {
uid = types.ParseUid(users[i].Id)
}
if !uid.IsZero() {
summaries[uid] = userSummaryPayload(&users[i], "")
}
}
return summaries
}
func forwardMessageUserSummary(from string, summaries map[types.Uid]map[string]any) map[string]any {
uid := types.ParseUid(from)
if uid.IsZero() {
return nil
}
if summary, ok := summaries[uid]; ok && summary != nil {
return summary
}
return map[string]any{
"user_id": uid.UserId(),
"display_name": uid.UserId(),
}
}
func prepareHTTPForwardMessage(tenantID types.TenantID, msg *types.Message) error {
if msg == nil || msg.TenantID != tenantID || strings.TrimSpace(msg.Topic) == "" {
return types.ErrMalformed
+19
View File
@@ -5362,13 +5362,17 @@ func TestServeIMMessageForwardBundleSavesForwardBundle(t *testing.T) {
oldSubs := store.Subs
oldMessages := store.Messages
oldUsers := store.Users
subs := mock_store.NewMockSubsPersistenceInterface(ctrl)
messages := mock_store.NewMockMessagesPersistenceInterface(ctrl)
users := mock_store.NewMockUsersPersistenceInterface(ctrl)
store.Subs = subs
store.Messages = messages
store.Users = users
t.Cleanup(func() {
store.Subs = oldSubs
store.Messages = oldMessages
store.Users = oldUsers
})
subs.EXPECT().Get(types.TenantID(7), "grpTarget", types.Uid(42), false).Return(&types.Subscription{
@@ -5396,6 +5400,11 @@ func TestServeIMMessageForwardBundleSavesForwardBundle(t *testing.T) {
CreatedAt: time.Date(2026, 8, 13, 9, 0, 0, 0, time.UTC),
},
}, nil)
users.EXPECT().GetAll(types.TenantID(7), types.Uid(99)).Return([]types.User{{
ObjHeader: types.ObjHeader{Id: types.Uid(99).String()},
TenantID: 7,
Public: types.KVMap{"display_name": "后端-potter", "avatar_file_id": "avatar-99"},
}}, nil)
messages.EXPECT().Save(types.TenantID(7), gomock.Any(), gomock.Nil(), true).DoAndReturn(
func(_ types.TenantID, msg *types.Message, _ []string, _ bool) (*types.MessageSaveResult, error) {
if msg.Topic != "grpTarget" || msg.Head["x-im-type"] != "forward_bundle" {
@@ -5405,6 +5414,16 @@ func TestServeIMMessageForwardBundleSavesForwardBundle(t *testing.T) {
if content["bundle_id"] != "fbundle_bundle-1" || content["item_count"] != 1 {
t.Fatalf("unexpected bundle content: %#v", content)
}
bundleItems := content["items"].([]map[string]any)
itemUser := bundleItems[0]["from_user"].(map[string]any)
if itemUser["user_id"] != types.Uid(99).UserId() || itemUser["display_name"] != "后端-potter" || itemUser["avatar_file_id"] != "avatar-99" {
t.Fatalf("unexpected item sender summary: %#v", itemUser)
}
previewItems := content["preview"].([]map[string]any)
previewUser := previewItems[0]["from_user"].(map[string]any)
if previewUser["user_id"] != types.Uid(99).UserId() || previewUser["display_name"] != "后端-potter" || previewUser["avatar_file_id"] != "avatar-99" {
t.Fatalf("unexpected preview sender summary: %#v", previewUser)
}
return &types.MessageSaveResult{SeqId: 23, MarkedReadBySender: true}, nil
})