bindbox-game/internal/api/app/app_latest_messages.go
邹方成 1a285f4e23 feat(消息状态): 添加消息已读状态功能
- 新增消息已读状态表结构及模型
- 实现用户端标记消息为已读接口
- 添加管理端获取最新消息记录接口
- 实现微信小程序登录功能
- 更新相关API文档和路由配置
2025-10-18 18:24:42 +08:00

190 lines
6.1 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package app
import (
"fmt"
"net/http"
"time"
"mini-chat/internal/code"
"mini-chat/internal/pkg/core"
"mini-chat/internal/pkg/timeutil"
"mini-chat/internal/pkg/validation"
"mini-chat/internal/repository/mysql/model"
)
type latestMessageByAppIdRequest struct {
AppID string `form:"app_id" binding:"required"` // 小程序ID
Page int `form:"page"` // 当前页码默认1
PageSize int `form:"page_size"` // 每页返回的数据量默认20
}
type latestMessageData struct {
MessageID int32 `json:"message_id"` // 消息ID
SendTime string `json:"send_time"` // 发送时间
SenderID string `json:"sender_id"` // 发送人ID
SenderName string `json:"sender_name"` // 发送人昵称
SenderAvatar string `json:"sender_avatar"` // 发送人头像
ReceiverID string `json:"receiver_id"` // 接收人ID
Content string `json:"content"` // 消息内容
MsgType int32 `json:"msg_type"` // 消息类型(1:文本 2:图片)
IsRead int32 `json:"is_read"` // 是否已读(0:未读 1:已读)
UnreadCount int64 `json:"unread_count"` // 未读数量
}
type latestMessageByAppIdResponse struct {
Page int `json:"page"` // 当前页码
PageSize int `json:"page_size"` // 每页返回的数据量
Total int64 `json:"total"` // 符合查询条件的总记录数
List []latestMessageData `json:"list"`
}
// LatestMessageByAppId 根据appid获取最新消息记录
// @Summary 根据appid获取最新消息记录
// @Description 管理端根据appid获取最新消息记录包含已读未读状态访问时自动标记为已读
// @Tags 管理端.小程序
// @Accept json
// @Produce json
// @Param app_id query string true "小程序ID"
// @Param page query int true "当前页码" default(1)
// @Param page_size query int true "每页返回的数据量,最多 100 条" default(20)
// @Success 200 {object} latestMessageByAppIdResponse
// @Failure 400 {object} code.Failure
// @Router /admin/messages/latest [get]
func (h *handler) LatestMessageByAppId() core.HandlerFunc {
return func(ctx core.Context) {
req := new(latestMessageByAppIdRequest)
res := new(latestMessageByAppIdResponse)
if err := ctx.ShouldBindForm(req); err != nil {
ctx.AbortWithError(core.Error(
http.StatusBadRequest,
code.ParamBindError,
validation.Error(err)),
)
return
}
if req.Page == 0 {
req.Page = 1
}
if req.PageSize == 0 {
req.PageSize = 20
}
if req.PageSize > 100 {
ctx.AbortWithError(core.Error(
http.StatusBadRequest,
code.ListMessageError,
fmt.Sprintf("%s: 一次最多只能查询 100 条", code.Text(code.ListMessageError)),
))
return
}
query := h.readDB.AppMessageLog.WithContext(ctx.RequestContext()).
Where(h.readDB.AppMessageLog.AppID.Eq(req.AppID))
// 查询总数
total, err := query.Count()
if err != nil {
ctx.AbortWithError(core.Error(
http.StatusBadRequest,
code.ListMessageError,
fmt.Sprintf("%s%s", code.Text(code.ListMessageError), err.Error())),
)
return
}
// 分页查询指定小程序的最新消息
resultData, err := query.
Order(h.readDB.AppMessageLog.SendTime.Desc()).
Offset((req.Page - 1) * req.PageSize).
Limit(req.PageSize).
Find()
if err != nil {
ctx.AbortWithError(core.Error(
http.StatusBadRequest,
code.ListMessageError,
fmt.Sprintf("%s%s", code.Text(code.ListMessageError), err.Error())),
)
return
}
// 自动标记该appid下的所有消息为已读管理端访问时
// 这里我们为每个消息的接收者创建或更新已读状态
for _, message := range resultData {
// 检查是否已存在已读状态记录
existingStatus, _ := h.readDB.AppMessageReadStatus.WithContext(ctx.RequestContext()).
Where(h.readDB.AppMessageReadStatus.AppID.Eq(req.AppID)).
Where(h.readDB.AppMessageReadStatus.MessageID.Eq(message.ID)).
Where(h.readDB.AppMessageReadStatus.UserID.Eq(message.ReceiverID)).
First()
if existingStatus == nil {
// 如果不存在,创建新的已读状态记录
now := time.Now()
_ = h.writeDB.AppMessageReadStatus.WithContext(ctx.RequestContext()).Create(&model.AppMessageReadStatus{
AppID: req.AppID,
MessageID: message.ID,
UserID: message.ReceiverID,
IsRead: 1,
ReadTime: &now,
CreatedAt: now,
UpdatedAt: now,
})
} else if existingStatus.IsRead == 0 {
// 如果存在但未读,更新为已读
now := time.Now()
_, _ = h.writeDB.AppMessageReadStatus.WithContext(ctx.RequestContext()).
Where(h.writeDB.AppMessageReadStatus.ID.Eq(existingStatus.ID)).
Updates(map[string]interface{}{
"is_read": 1,
"read_time": &now,
"updated_at": now,
})
}
}
res.Page = req.Page
res.PageSize = req.PageSize
res.Total = total
res.List = make([]latestMessageData, len(resultData))
for k, v := range resultData {
// 查询该消息的已读状态
readStatus, _ := h.readDB.AppMessageReadStatus.WithContext(ctx.RequestContext()).
Where(h.readDB.AppMessageReadStatus.AppID.Eq(req.AppID)).
Where(h.readDB.AppMessageReadStatus.MessageID.Eq(v.ID)).
Where(h.readDB.AppMessageReadStatus.UserID.Eq(v.ReceiverID)).
First()
// 判断是否已读
isRead := int32(0)
if readStatus != nil && readStatus.IsRead == 1 {
isRead = 1
}
// 计算该用户在该应用下的未读消息总数
unreadCount, _ := h.readDB.AppMessageReadStatus.WithContext(ctx.RequestContext()).
Where(h.readDB.AppMessageReadStatus.AppID.Eq(req.AppID)).
Where(h.readDB.AppMessageReadStatus.UserID.Eq(v.ReceiverID)).
Where(h.readDB.AppMessageReadStatus.IsRead.Eq(0)).
Count()
res.List[k] = latestMessageData{
MessageID: v.ID,
SendTime: timeutil.FriendlyTime(v.SendTime),
SenderID: v.SenderID,
SenderName: v.SenderName,
SenderAvatar: "", // TODO: 需要从用户表获取头像信息
ReceiverID: v.ReceiverID,
Content: v.Content,
MsgType: v.MsgType,
IsRead: isRead,
UnreadCount: unreadCount,
}
}
ctx.Payload(res)
}
}