Add meeting display fields

This commit is contained in:
richjohnrambo
2026-08-29 19:28:10 +08:00
parent 866222cb39
commit c6a243ee59
2 changed files with 122 additions and 5 deletions
+104 -4
View File
@@ -1926,8 +1926,9 @@ func serveIMMeetingList(wrt http.ResponseWriter, req *http.Request) {
return
}
items := make([]map[string]any, 0, len(meetings))
userSummaries := meetingUserSummaries(principal.TenantID, meetings)
for i := range meetings {
items = append(items, meetingPayload(&meetings[i], principal.Uid))
items = append(items, meetingPayloadWithUserSummaries(&meetings[i], principal.Uid, userSummaries))
}
writeIMSuccess(wrt, req, map[string]any{
"items": items,
@@ -7273,22 +7274,37 @@ func organizationSearchPayloads(items []types.OrganizationSearchItem) []map[stri
}
func meetingPayload(meeting *types.Meeting, viewer types.Uid) map[string]any {
participants := meetingParticipantPayloads(meeting.Participants)
return meetingPayloadWithUserSummaries(meeting, viewer, nil)
}
func meetingPayloadWithUserSummaries(meeting *types.Meeting, viewer types.Uid, userSummaries map[types.Uid]map[string]any) map[string]any {
participants := meetingParticipantPayloadsWithSummaries(meeting.Participants, userSummaries)
organizer := meetingUserSummary(meeting.OrganizerID, userSummaries)
startAt := meeting.StartAt.UTC()
endAt := meeting.EndAt.UTC()
data := map[string]any{
"meeting_id": meeting.MeetingID,
"title": meeting.Title,
"organizer_id": meeting.OrganizerID.UserId(),
"start_at": meeting.StartAt.UTC().Format(time.RFC3339Nano),
"end_at": meeting.EndAt.UTC().Format(time.RFC3339Nano),
"organizer": organizer,
"start_at": startAt.Format(time.RFC3339Nano),
"end_at": endAt.Format(time.RFC3339Nano),
"meeting_time": meetingTimeRange(startAt, endAt),
"time_range": meetingTimeRange(startAt, endAt),
"location": meeting.Location,
"state": meeting.State,
"reminder_minutes": meeting.ReminderMinutes,
"version": meeting.Version,
"participants": participants,
"participant_count": len(participants),
"participant_names": meetingParticipantNames(participants),
"permissions": meetingPermissionsPayload(meeting, viewer),
"created_at": meeting.CreatedAt.UTC().Format(time.RFC3339Nano),
"updated_at": meeting.UpdatedAt.UTC().Format(time.RFC3339Nano),
}
if displayName, ok := organizer["display_name"]; ok {
data["organizer_display_name"] = displayName
}
if meeting.CanceledAt != nil {
data["canceled_at"] = meeting.CanceledAt.UTC().Format(time.RFC3339Nano)
}
@@ -7302,12 +7318,24 @@ func meetingPayload(meeting *types.Meeting, viewer types.Uid) map[string]any {
}
func meetingParticipantPayloads(participants []types.MeetingParticipant) []map[string]any {
return meetingParticipantPayloadsWithSummaries(participants, nil)
}
func meetingParticipantPayloadsWithSummaries(participants []types.MeetingParticipant, userSummaries map[types.Uid]map[string]any) []map[string]any {
items := make([]map[string]any, 0, len(participants))
for _, participant := range participants {
item := map[string]any{
"user_id": participant.UserID.UserId(),
"response": participant.Response,
}
if summary := meetingUserSummary(participant.UserID, userSummaries); summary != nil {
if displayName, ok := summary["display_name"]; ok {
item["display_name"] = displayName
}
if avatar, ok := summary["avatar_file_id"]; ok {
item["avatar_file_id"] = avatar
}
}
if participant.ResponseAt != nil {
item["response_at"] = participant.ResponseAt.UTC().Format(time.RFC3339Nano)
}
@@ -7316,6 +7344,78 @@ func meetingParticipantPayloads(participants []types.MeetingParticipant) []map[s
return items
}
func meetingUserSummaries(tenantID types.TenantID, meetings []types.Meeting) map[types.Uid]map[string]any {
if store.Users == nil {
return nil
}
seen := make(map[types.Uid]bool)
uids := make([]types.Uid, 0, len(meetings)*2)
for _, meeting := range meetings {
if !meeting.OrganizerID.IsZero() && !seen[meeting.OrganizerID] {
seen[meeting.OrganizerID] = true
uids = append(uids, meeting.OrganizerID)
}
for _, participant := range meeting.Participants {
if !participant.UserID.IsZero() && !seen[participant.UserID] {
seen[participant.UserID] = true
uids = append(uids, participant.UserID)
}
}
}
if len(uids) == 0 {
return nil
}
users, err := store.Users.GetAll(tenantID, uids...)
if err != nil {
logs.Warn.Printf("im http: failed to load meeting 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 meetingUserSummary(uid types.Uid, summaries map[types.Uid]map[string]any) map[string]any {
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 meetingParticipantNames(participants []map[string]any) []string {
names := make([]string, 0, len(participants))
for _, participant := range participants {
if name := strings.TrimSpace(fmt.Sprint(participant["display_name"])); name != "" {
names = append(names, name)
}
}
return names
}
func meetingTimeRange(startAt, endAt time.Time) string {
if startAt.IsZero() || endAt.IsZero() {
return ""
}
if startAt.Format("2006-01-02") == endAt.Format("2006-01-02") {
return startAt.Format("2006-01-02 15:04") + "-" + endAt.Format("15:04")
}
return startAt.Format("2006-01-02 15:04") + "-" + endAt.Format("2006-01-02 15:04")
}
func meetingParticipantUIDs(meeting *types.Meeting) []types.Uid {
if meeting == nil {
return nil
+18 -1
View File
@@ -2374,6 +2374,7 @@ func TestServeIMMeetingsList(t *testing.T) {
viewer := types.Uid(42)
expectIMBearer(t, ctrl, "token-1", 7, viewer)
oldMeetings := store.Meetings
oldUsers := store.Users
now := time.Now().UTC().Round(time.Millisecond)
meetings := &testMeetingsStore{list: []types.Meeting{{
MeetingID: "mtg_test",
@@ -2391,7 +2392,15 @@ func TestServeIMMeetingsList(t *testing.T) {
},
}}}
store.Meetings = meetings
defer func() { store.Meetings = oldMeetings }()
users := mock_store.NewMockUsersPersistenceInterface(ctrl)
organizer := types.User{Public: map[string]any{"fn": "mzb"}}
organizer.SetUid(viewer)
users.EXPECT().GetAll(types.TenantID(7), viewer).Return([]types.User{organizer}, nil)
store.Users = users
defer func() {
store.Meetings = oldMeetings
store.Users = oldUsers
}()
req := httptest.NewRequest(http.MethodGet, "/v1/im/meetings?limit=20&cursor=c1", nil)
req.Header.Set("Authorization", "Bearer token-1")
@@ -2409,6 +2418,14 @@ func TestServeIMMeetingsList(t *testing.T) {
if items[0].(map[string]any)["meeting_id"] != "mtg_test" || data["has_more"] != true {
t.Fatalf("unexpected meeting payload: %#v", data)
}
item := items[0].(map[string]any)
if item["organizer_display_name"] != "mzb" || item["participant_count"].(float64) != 1 {
t.Fatalf("missing meeting display fields: %#v", item)
}
participants := item["participants"].([]any)
if participants[0].(map[string]any)["display_name"] != "mzb" {
t.Fatalf("missing participant display name: %#v", participants)
}
}
func TestServeIMMeetingMyResponse(t *testing.T) {