feat(meeting): Task 13 完成 meeting_invite 通知卡片对接
## 后端 - MeetingService.InviteUsers 的 PushPayload.Extra 补齐设计 §10.1 要求字段: inviter_id / inviter_name / inviter_avatar / expired_at(Unix 秒, 与 Redis TTL MeetingInviteTokenTTL=600s 对齐) ## 前端 - constants/notify.js:supportsInlineAction 扩展支持 meeting_invite; 新增 NOTIFY_INLINE_ACTION_LABEL 映射(meeting_invite → 立即加入/稍后) + NOTIFY_INLINE_ACTION_DEFAULT 兜底 - components/notify/NotifyItem.vue: * 按 type 动态渲染按钮文案(actionLabel 计算属性) * isExpired 计算属性:meeting_invite 比对 extra.expired_at * 1000 * 过期态合并为单个 disabled 的"邀请已过期"按钮 * 新增 .notify-btn--expired 灰显样式 - pages/notify/index.vue: * handleAccept / handleReject / _navigateByNotify 增加 meeting_invite 分支 * 新增 _navigateToMeetingInvite 辅助:跳 /pages/meeting/preview?mode=join&code=xxx * 过期时 toast "邀请已过期"不跳转,"稍后"仅 markRead 不发接口 ## 文档 - docs/plans/2026-04-21-phase2e-2-implementation.plan.md:Task 13 标 ✅ + 详细产出 - docs/progress/CURRENT_STATUS.md:新增 Task 13 专节(关键技术点 / 验证记录 / 下一步) - .cursor/rules/project-context.mdc:2e-2 进度刷 Task 0-13 ✅ ## 验证 后端 curl 双角色端到端(testuser1 创会 → 邀请 testuser2 → 拉通知列表): - type=meeting_invite,extra 8 字段齐全 - expired_at = invited_at + 600,与 Redis TTL 对齐 Made-with: Cursor
This commit is contained in:
@@ -693,6 +693,9 @@ func (s *MeetingService) InviteUsers(ctx context.Context, inviterID int64, code
|
||||
return 0, 0, err
|
||||
}
|
||||
|
||||
// 邀请人展示信息:extra 里一并带上,前端卡片不必再查 actor_* 字段就能渲染
|
||||
inviterName, inviterAvatar := s.resolveUserDisplay(ctx, inviterID)
|
||||
|
||||
pushed := 0
|
||||
skipped := 0
|
||||
seen := make(map[int64]struct{}, len(inviteeIDs))
|
||||
@@ -720,11 +723,13 @@ func (s *MeetingService) InviteUsers(ctx context.Context, inviterID int64, code
|
||||
skipped++
|
||||
continue
|
||||
}
|
||||
now := time.Now()
|
||||
expiredAt := now.Add(time.Duration(constants.MeetingInviteTokenTTL) * time.Second).Unix()
|
||||
payload := invitePayload{
|
||||
RoomCode: code,
|
||||
InviterID: inviterID,
|
||||
InviteeID: invitee,
|
||||
CreatedAt: time.Now().Unix(),
|
||||
CreatedAt: now.Unix(),
|
||||
}
|
||||
buf, _ := json.Marshal(payload)
|
||||
if err := s.redis.Set(ctx, redisKeyInvitePrefix+token, string(buf),
|
||||
@@ -736,11 +741,19 @@ func (s *MeetingService) InviteUsers(ctx context.Context, inviterID int64, code
|
||||
|
||||
roomID := room.ID
|
||||
actor := inviterID
|
||||
// extra 字段约定(Phase 2e-2 Task 13 / design §10.1):
|
||||
// room_code / invite_token / room_title / has_password:进会所需参数
|
||||
// inviter_id / inviter_name / inviter_avatar:前端卡片渲染"XX 邀请你加入..."
|
||||
// expired_at:Unix 秒,与 Redis TTL 同步,前端据此把按钮灰显
|
||||
extra := map[string]interface{}{
|
||||
"room_code": code,
|
||||
"invite_token": token,
|
||||
"room_title": room.Title,
|
||||
"has_password": room.PasswordHash != nil,
|
||||
"room_code": code,
|
||||
"invite_token": token,
|
||||
"room_title": room.Title,
|
||||
"has_password": room.PasswordHash != nil,
|
||||
"inviter_id": inviterID,
|
||||
"inviter_name": inviterName,
|
||||
"inviter_avatar": inviterAvatar,
|
||||
"expired_at": expiredAt,
|
||||
}
|
||||
payloads = append(payloads, ¬ifyService.PushPayload{
|
||||
UserID: invitee,
|
||||
|
||||
Reference in New Issue
Block a user