群阅后即焚

This commit is contained in:
hawklin2017 2026-05-20 17:45:35 +08:00
parent 89d65f0d2b
commit 35152a2832
5 changed files with 56 additions and 47 deletions

View File

@ -298,8 +298,8 @@ func (o *GroupApi) GetEditSetting(c *gin.Context) {
// SetMsgBurnDuration 设置群消息阅后即焚时长burnDuration=0 表示关闭。 // SetMsgBurnDuration 设置群消息阅后即焚时长burnDuration=0 表示关闭。
func (o *GroupApi) SetMsgBurnDuration(c *gin.Context) { func (o *GroupApi) SetMsgBurnDuration(c *gin.Context) {
var req struct { var req struct {
GroupID string `json:"groupID"` GroupID string `json:"groupID"`
BurnDuration int32 `json:"burnDuration"` BurnDuration int32 `json:"burnDuration"`
} }
if err := c.ShouldBindJSON(&req); err != nil { if err := c.ShouldBindJSON(&req); err != nil {
apiresp.GinError(c, errs.ErrArgs.WrapMsg(err.Error())) apiresp.GinError(c, errs.ErrArgs.WrapMsg(err.Error()))
@ -324,6 +324,40 @@ func (o *GroupApi) SetMsgBurnDuration(c *gin.Context) {
apiresp.GinSuccess(c, resp) apiresp.GinSuccess(c, resp)
} }
// GetMsgBurnDuration 返回当前群消息阅后即焚时长0 表示关闭(与 get_groups_info 中字段一致)。
func (o *GroupApi) GetMsgBurnDuration(c *gin.Context) {
var req struct {
GroupID string `json:"groupID"`
}
if err := c.ShouldBindJSON(&req); err != nil {
apiresp.GinError(c, errs.ErrArgs.WrapMsg(err.Error()))
return
}
if req.GroupID == "" {
apiresp.GinError(c, errs.ErrArgs.WrapMsg("groupID is empty"))
return
}
resp, err := o.Client.GetGroupsInfo(c.Request.Context(), &group.GetGroupsInfoReq{
GroupIDs: []string{req.GroupID},
})
if err != nil {
apiresp.GinError(c, err)
return
}
if len(resp.GroupInfos) == 0 {
apiresp.GinError(c, errs.ErrRecordNotFound.WrapMsg("group not found", "groupID", req.GroupID))
return
}
gi := resp.GroupInfos[0]
apiresp.GinSuccess(c, struct {
GroupID string `json:"groupID"`
BurnDuration int32 `json:"burnDuration"`
}{
GroupID: gi.GroupID,
BurnDuration: gi.GetMsgBurnDuration(),
})
}
func (o *GroupApi) JoinGroup(c *gin.Context) { func (o *GroupApi) JoinGroup(c *gin.Context) {
a2r.Call(c, group.GroupClient.JoinGroup, o.Client) a2r.Call(c, group.GroupClient.JoinGroup, o.Client)
} }

View File

@ -242,6 +242,7 @@ func newGinRouter(ctx context.Context, client discovery.SvcDiscoveryRegistry, co
groupRouterGroup.POST("/set_edit_setting", g.SetEditSetting) groupRouterGroup.POST("/set_edit_setting", g.SetEditSetting)
groupRouterGroup.POST("/get_edit_setting", g.GetEditSetting) groupRouterGroup.POST("/get_edit_setting", g.GetEditSetting)
groupRouterGroup.POST("/set_msg_burn_duration", g.SetMsgBurnDuration) groupRouterGroup.POST("/set_msg_burn_duration", g.SetMsgBurnDuration)
groupRouterGroup.POST("/get_msg_burn_duration", g.GetMsgBurnDuration)
groupRouterGroup.POST("/join_group", g.JoinGroup) groupRouterGroup.POST("/join_group", g.JoinGroup)
groupRouterGroup.POST("/quit_group", g.QuitGroup) groupRouterGroup.POST("/quit_group", g.QuitGroup)
groupRouterGroup.POST("/group_application_response", g.ApplicationGroupResponse) groupRouterGroup.POST("/group_application_response", g.ApplicationGroupResponse)

View File

@ -902,7 +902,7 @@ func (c *conversationServer) ClearBurnExpiredMsgs(ctx context.Context, req *pbco
// 删除焚毁消息并同步通知阅读方客户端best-effort失败不中断流程 // 删除焚毁消息并同步通知阅读方客户端best-effort失败不中断流程
if err := c.msgClient.DeleteMsgs(ctx, g.UserID, g.ConversationID, g.Seqs, &msg.DeleteSyncOpt{ if err := c.msgClient.DeleteMsgs(ctx, g.UserID, g.ConversationID, g.Seqs, &msg.DeleteSyncOpt{
IsSyncSelf: true, IsSyncOther: true,
}); err != nil { }); err != nil {
log.ZError(ctx, "ClearBurnExpiredMsgs DeleteMsgs failed", err, log.ZError(ctx, "ClearBurnExpiredMsgs DeleteMsgs failed", err,
"userID", g.UserID, "conversationID", g.ConversationID, "seqs", g.Seqs) "userID", g.UserID, "conversationID", g.ConversationID, "seqs", g.Seqs)
@ -920,12 +920,10 @@ func (c *conversationServer) ClearBurnExpiredMsgs(ctx context.Context, req *pbco
return &pbconversation.ClearBurnExpiredMsgsResp{Count: processed}, nil return &pbconversation.ClearBurnExpiredMsgsResp{Count: processed}, nil
} }
// ClearGroupBurnExpiredMsgs 处理群消息「阅后即焚」到期记录: // ClearGroupBurnExpiredMsgs 处理群消息「阅后即焚」到期记录:
// 1. 查询满足 read_count >= member_count 且 burn_end_time 过期的记录(按 group_id 聚合)。 // 1. 查询满足 read_count >= member_count 且 burn_end_time 过期的记录(按 group_id 聚合)。
// 2. 对每个群,获取所有成员 ID批量推进他们在群会话上的 min_seq。 // 2. 对每个群调用 msg.DeleteMsgsIsSyncOther物理删除群会话消息并下发 DeleteMsgsNotification
// 3. 更新每个成员的会话 min_seq 并下发 ConversationChangeNotification。 // 3. 删除已处理的 group_msg_burn_record 记录。
// 4. 删除已处理的 group_msg_burn_record 记录。
func (c *conversationServer) ClearGroupBurnExpiredMsgs(ctx context.Context, req *pbconversation.ClearGroupBurnExpiredMsgsReq) (*pbconversation.ClearGroupBurnExpiredMsgsResp, error) { func (c *conversationServer) ClearGroupBurnExpiredMsgs(ctx context.Context, req *pbconversation.ClearGroupBurnExpiredMsgsReq) (*pbconversation.ClearGroupBurnExpiredMsgsResp, error) {
if c.groupMsgBurnRecordDB == nil { if c.groupMsgBurnRecordDB == nil {
return &pbconversation.ClearGroupBurnExpiredMsgsResp{Count: 0}, nil return &pbconversation.ClearGroupBurnExpiredMsgsResp{Count: 0}, nil
@ -944,35 +942,20 @@ func (c *conversationServer) ClearGroupBurnExpiredMsgs(ctx context.Context, req
continue continue
} }
conversationID := msgprocessor.GetConversationIDBySessionType(constant.ReadGroupChatType, g.GroupID) conversationID := msgprocessor.GetConversationIDBySessionType(constant.ReadGroupChatType, g.GroupID)
newMinSeq := g.MaxSeq + 1
// 获取群所有成员 ID // 与 ClearBurnExpiredMsgs 一致物理删除并同步客户端best-effort
memberIDs, err := c.groupClient.GetGroupMemberUserIDs(ctx, g.GroupID) var deleteAsUserID string
if err != nil { if len(c.config.Share.IMAdminUserID) > 0 {
log.ZError(ctx, "ClearGroupBurnExpiredMsgs GetGroupMemberUserIDs failed", err, deleteAsUserID = c.config.Share.IMAdminUserID[0]
"groupID", g.GroupID)
continue
} }
if len(memberIDs) == 0 { if deleteAsUserID == "" {
continue log.ZWarn(ctx, "ClearGroupBurnExpiredMsgs: IMAdminUserID empty, skip DeleteMsgs", nil,
} "groupID", g.GroupID, "conversationID", conversationID, "seqs", g.Seqs)
} else if err := c.msgClient.DeleteMsgs(ctx, deleteAsUserID, conversationID, g.Seqs, &msg.DeleteSyncOpt{
// 批量推进所有成员的 min_seqseq 层) IsSyncOther: true,
if err := c.msgClient.SetUserConversationMin(ctx, conversationID, memberIDs, newMinSeq); err != nil { }); err != nil {
log.ZError(ctx, "ClearGroupBurnExpiredMsgs SetUserConversationMin failed", err, log.ZError(ctx, "ClearGroupBurnExpiredMsgs DeleteMsgs failed", err,
"groupID", g.GroupID, "conversationID", conversationID, "minSeq", newMinSeq) "groupID", g.GroupID, "conversationID", conversationID, "seqs", g.Seqs)
continue
}
// 更新每个成员会话文档中的 min_seq 并发送通知
if err := c.conversationDatabase.UpdateUsersConversationField(ctx, memberIDs, conversationID,
map[string]any{"min_seq": newMinSeq}); err != nil {
log.ZError(ctx, "ClearGroupBurnExpiredMsgs UpdateUsersConversationField failed", err,
"groupID", g.GroupID, "conversationID", conversationID, "minSeq", newMinSeq)
continue
}
for _, memberID := range memberIDs {
c.conversationNotificationSender.ConversationChangeNotification(ctx, memberID, []string{conversationID})
} }
// 删除已处理记录 // 删除已处理记录
@ -980,9 +963,9 @@ func (c *conversationServer) ClearGroupBurnExpiredMsgs(ctx context.Context, req
log.ZError(ctx, "ClearGroupBurnExpiredMsgs DeleteByGroupSeqs failed", err, log.ZError(ctx, "ClearGroupBurnExpiredMsgs DeleteByGroupSeqs failed", err,
"groupID", g.GroupID, "seqs", g.Seqs) "groupID", g.GroupID, "seqs", g.Seqs)
} }
log.ZDebug(ctx, "ClearGroupBurnExpiredMsgs advanced min_seq for group", log.ZDebug(ctx, "ClearGroupBurnExpiredMsgs processed group burn batch",
"groupID", g.GroupID, "conversationID", conversationID, "groupID", g.GroupID, "conversationID", conversationID,
"minSeq", newMinSeq, "memberCount", len(memberIDs), "seqs", g.Seqs) "seqs", g.Seqs)
processed++ processed++
} }
return &pbconversation.ClearGroupBurnExpiredMsgsResp{Count: processed}, nil return &pbconversation.ClearGroupBurnExpiredMsgsResp{Count: processed}, nil

View File

@ -266,16 +266,7 @@ func (m *msgServer) recordBurnDeadlines(ctx context.Context, conv *conversation.
PeerID: peerID, PeerID: peerID,
DeadlineMs: deadline, DeadlineMs: deadline,
CreateTime: now, CreateTime: now,
}, })
&model.MsgBurnDeadline{
UserID: peerID,
ConversationID: conv.ConversationID,
Seq: seq,
PeerID: readerUserID,
DeadlineMs: deadline,
CreateTime: now,
},
)
} }
if err := m.msgBurnDeadlineDB.UpsertIfAbsent(ctx, items); err != nil { if err := m.msgBurnDeadlineDB.UpsertIfAbsent(ctx, items); err != nil {
log.ZError(ctx, "recordBurnDeadlines UpsertIfAbsent failed", err, log.ZError(ctx, "recordBurnDeadlines UpsertIfAbsent failed", err,

@ -1 +1 @@
Subproject commit 7bd25d8024043c6f8ebd4c6d473a89e7034e639d Subproject commit c5adb72b8d02244a5d3d7d814e607e9b6256fdd6