feat(1.0):调整未读列表
This commit is contained in:
parent
a6ac558680
commit
ba0630b2da
25
docs/docs.go
25
docs/docs.go
@ -924,6 +924,11 @@ const docTemplate = `{
|
|||||||
},
|
},
|
||||||
"/admin/messages/latest": {
|
"/admin/messages/latest": {
|
||||||
"get": {
|
"get": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"LoginVerifyToken": []
|
||||||
|
}
|
||||||
|
],
|
||||||
"description": "管理端根据appid获取最新消息记录,包含已读未读状态,访问时自动标记为已读",
|
"description": "管理端根据appid获取最新消息记录,包含已读未读状态,访问时自动标记为已读",
|
||||||
"consumes": [
|
"consumes": [
|
||||||
"application/json"
|
"application/json"
|
||||||
@ -1788,26 +1793,6 @@ const docTemplate = `{
|
|||||||
"app.latestMessageData": {
|
"app.latestMessageData": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"content": {
|
|
||||||
"description": "消息内容",
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"is_read": {
|
|
||||||
"description": "是否已读(0:未读 1:已读)",
|
|
||||||
"type": "integer"
|
|
||||||
},
|
|
||||||
"message_id": {
|
|
||||||
"description": "消息ID",
|
|
||||||
"type": "integer"
|
|
||||||
},
|
|
||||||
"msg_type": {
|
|
||||||
"description": "消息类型(1:文本 2:图片)",
|
|
||||||
"type": "integer"
|
|
||||||
},
|
|
||||||
"receiver_id": {
|
|
||||||
"description": "接收人ID",
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"send_time": {
|
"send_time": {
|
||||||
"description": "发送时间",
|
"description": "发送时间",
|
||||||
"type": "string"
|
"type": "string"
|
||||||
|
|||||||
@ -916,6 +916,11 @@
|
|||||||
},
|
},
|
||||||
"/admin/messages/latest": {
|
"/admin/messages/latest": {
|
||||||
"get": {
|
"get": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"LoginVerifyToken": []
|
||||||
|
}
|
||||||
|
],
|
||||||
"description": "管理端根据appid获取最新消息记录,包含已读未读状态,访问时自动标记为已读",
|
"description": "管理端根据appid获取最新消息记录,包含已读未读状态,访问时自动标记为已读",
|
||||||
"consumes": [
|
"consumes": [
|
||||||
"application/json"
|
"application/json"
|
||||||
@ -1780,26 +1785,6 @@
|
|||||||
"app.latestMessageData": {
|
"app.latestMessageData": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"content": {
|
|
||||||
"description": "消息内容",
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"is_read": {
|
|
||||||
"description": "是否已读(0:未读 1:已读)",
|
|
||||||
"type": "integer"
|
|
||||||
},
|
|
||||||
"message_id": {
|
|
||||||
"description": "消息ID",
|
|
||||||
"type": "integer"
|
|
||||||
},
|
|
||||||
"msg_type": {
|
|
||||||
"description": "消息类型(1:文本 2:图片)",
|
|
||||||
"type": "integer"
|
|
||||||
},
|
|
||||||
"receiver_id": {
|
|
||||||
"description": "接收人ID",
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"send_time": {
|
"send_time": {
|
||||||
"description": "发送时间",
|
"description": "发送时间",
|
||||||
"type": "string"
|
"type": "string"
|
||||||
|
|||||||
@ -275,21 +275,6 @@ definitions:
|
|||||||
type: object
|
type: object
|
||||||
app.latestMessageData:
|
app.latestMessageData:
|
||||||
properties:
|
properties:
|
||||||
content:
|
|
||||||
description: 消息内容
|
|
||||||
type: string
|
|
||||||
is_read:
|
|
||||||
description: 是否已读(0:未读 1:已读)
|
|
||||||
type: integer
|
|
||||||
message_id:
|
|
||||||
description: 消息ID
|
|
||||||
type: integer
|
|
||||||
msg_type:
|
|
||||||
description: 消息类型(1:文本 2:图片)
|
|
||||||
type: integer
|
|
||||||
receiver_id:
|
|
||||||
description: 接收人ID
|
|
||||||
type: string
|
|
||||||
send_time:
|
send_time:
|
||||||
description: 发送时间
|
description: 发送时间
|
||||||
type: string
|
type: string
|
||||||
@ -1414,6 +1399,8 @@ paths:
|
|||||||
description: Bad Request
|
description: Bad Request
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/definitions/code.Failure'
|
$ref: '#/definitions/code.Failure'
|
||||||
|
security:
|
||||||
|
- LoginVerifyToken: []
|
||||||
summary: 根据appid获取最新消息记录
|
summary: 根据appid获取最新消息记录
|
||||||
tags:
|
tags:
|
||||||
- 管理端.小程序
|
- 管理端.小程序
|
||||||
|
|||||||
@ -10,6 +10,7 @@ type handler struct {
|
|||||||
logger logger.CustomLogger
|
logger logger.CustomLogger
|
||||||
writeDB *dao.Query
|
writeDB *dao.Query
|
||||||
readDB *dao.Query
|
readDB *dao.Query
|
||||||
|
db mysql.Repo
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(logger logger.CustomLogger, db mysql.Repo) *handler {
|
func New(logger logger.CustomLogger, db mysql.Repo) *handler {
|
||||||
@ -17,5 +18,6 @@ func New(logger logger.CustomLogger, db mysql.Repo) *handler {
|
|||||||
logger: logger,
|
logger: logger,
|
||||||
writeDB: dao.Use(db.GetDbW()),
|
writeDB: dao.Use(db.GetDbW()),
|
||||||
readDB: dao.Use(db.GetDbR()),
|
readDB: dao.Use(db.GetDbR()),
|
||||||
|
db: db,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,6 +3,7 @@ package app
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
"mini-chat/internal/code"
|
"mini-chat/internal/code"
|
||||||
"mini-chat/internal/pkg/core"
|
"mini-chat/internal/pkg/core"
|
||||||
@ -17,15 +18,10 @@ type latestMessageByAppIdRequest struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type latestMessageData struct {
|
type latestMessageData struct {
|
||||||
MessageID int32 `json:"message_id"` // 消息ID
|
|
||||||
SendTime string `json:"send_time"` // 发送时间
|
SendTime string `json:"send_time"` // 发送时间
|
||||||
SenderID string `json:"sender_id"` // 发送人ID
|
SenderID string `json:"sender_id"` // 发送人ID
|
||||||
SenderName string `json:"sender_name"` // 发送人昵称
|
SenderName string `json:"sender_name"` // 发送人昵称
|
||||||
SenderAvatar string `json:"sender_avatar"` // 发送人头像
|
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"` // 未读数量
|
UnreadCount int64 `json:"unread_count"` // 未读数量
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,6 +44,7 @@ type latestMessageByAppIdResponse struct {
|
|||||||
// @Success 200 {object} latestMessageByAppIdResponse
|
// @Success 200 {object} latestMessageByAppIdResponse
|
||||||
// @Failure 400 {object} code.Failure
|
// @Failure 400 {object} code.Failure
|
||||||
// @Router /admin/messages/latest [get]
|
// @Router /admin/messages/latest [get]
|
||||||
|
// @Security LoginVerifyToken
|
||||||
func (h *handler) LatestMessageByAppId() core.HandlerFunc {
|
func (h *handler) LatestMessageByAppId() core.HandlerFunc {
|
||||||
return func(ctx core.Context) {
|
return func(ctx core.Context) {
|
||||||
req := new(latestMessageByAppIdRequest)
|
req := new(latestMessageByAppIdRequest)
|
||||||
@ -78,37 +75,54 @@ func (h *handler) LatestMessageByAppId() core.HandlerFunc {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
query := h.readDB.AppMessageLog.WithContext(ctx.RequestContext()).
|
type unreadMessageResult struct {
|
||||||
Where(h.readDB.AppMessageLog.AppID.Eq(req.AppID))
|
SenderID string `json:"sender_id"`
|
||||||
|
SenderName string `json:"sender_name"`
|
||||||
|
SendTime time.Time `json:"send_time"`
|
||||||
|
AvatarURL string `json:"avatar_url"`
|
||||||
|
UnreadCount int64 `json:"unread_count"`
|
||||||
|
}
|
||||||
|
|
||||||
// 查询总数
|
var results []unreadMessageResult
|
||||||
total, err := query.Count()
|
var total int64
|
||||||
if err != nil {
|
|
||||||
|
countErr := h.db.GetDbR().Table("app_message_log m").
|
||||||
|
Select("m.send_time, m.sender_id, m.sender_name, u.user_avatar as avatar_url, COUNT(*) as unread_count").
|
||||||
|
Joins("LEFT JOIN app_user u ON m.sender_id = u.user_id").
|
||||||
|
Where("m.app_id = ? AND m.sender_id != ? AND m.is_read = 1", req.AppID, "888888").
|
||||||
|
Group("m.sender_id").
|
||||||
|
Count(&total).
|
||||||
|
Error
|
||||||
|
if countErr != nil {
|
||||||
ctx.AbortWithError(core.Error(
|
ctx.AbortWithError(core.Error(
|
||||||
http.StatusBadRequest,
|
http.StatusBadRequest,
|
||||||
code.ListMessageError,
|
code.ListMessageError,
|
||||||
fmt.Sprintf("%s:%s", code.Text(code.ListMessageError), err.Error())),
|
fmt.Sprintf("%s:%s", code.Text(code.ListMessageError), countErr.Error())),
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 分页查询指定小程序的最新消息
|
resultErr := h.db.GetDbR().Table("app_message_log m").
|
||||||
resultData, err := query.
|
Select("max(m.send_time) as send_time, m.sender_id, max(m.sender_name) as sender_name, max(u.user_avatar) as avatar_url, COUNT(*) as unread_count").
|
||||||
Order(h.readDB.AppMessageLog.SendTime.Asc()).
|
Joins("LEFT JOIN app_user u ON m.sender_id = u.user_id").
|
||||||
|
Where("m.app_id = ? AND m.sender_id != ? AND m.is_read = 1", req.AppID, "888888").
|
||||||
|
Group("m.sender_id").
|
||||||
|
Order("unread_count DESC").
|
||||||
Offset((req.Page - 1) * req.PageSize).
|
Offset((req.Page - 1) * req.PageSize).
|
||||||
Limit(req.PageSize).
|
Limit(req.PageSize).
|
||||||
Find()
|
Find(&results).
|
||||||
if err != nil {
|
Error
|
||||||
|
if resultErr != nil {
|
||||||
ctx.AbortWithError(core.Error(
|
ctx.AbortWithError(core.Error(
|
||||||
http.StatusBadRequest,
|
http.StatusBadRequest,
|
||||||
code.ListMessageError,
|
code.ListMessageError,
|
||||||
fmt.Sprintf("%s:%s", code.Text(code.ListMessageError), err.Error())),
|
fmt.Sprintf("%s:%s", code.Text(code.ListMessageError), resultErr.Error())),
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 自动标记该appid下的所有消息为已读(管理端访问时)
|
// 自动标记该appid下的所有消息为已读(管理端访问时)
|
||||||
_, err = h.writeDB.AppMessageLog.WithContext(ctx.RequestContext()).
|
_, err := h.writeDB.AppMessageLog.WithContext(ctx.RequestContext()).
|
||||||
Where(h.writeDB.AppMessageLog.AppID.Eq(req.AppID)).
|
Where(h.writeDB.AppMessageLog.AppID.Eq(req.AppID)).
|
||||||
Where(h.writeDB.AppMessageLog.IsRead.Eq(0)).
|
Where(h.writeDB.AppMessageLog.IsRead.Eq(0)).
|
||||||
Update(h.writeDB.AppMessageLog.IsRead, 1)
|
Update(h.writeDB.AppMessageLog.IsRead, 1)
|
||||||
@ -120,27 +134,15 @@ func (h *handler) LatestMessageByAppId() core.HandlerFunc {
|
|||||||
res.Page = req.Page
|
res.Page = req.Page
|
||||||
res.PageSize = req.PageSize
|
res.PageSize = req.PageSize
|
||||||
res.Total = total
|
res.Total = total
|
||||||
res.List = make([]latestMessageData, len(resultData))
|
res.List = make([]latestMessageData, len(results))
|
||||||
|
|
||||||
for k, v := range resultData {
|
|
||||||
// 计算该用户在该应用下的未读消息总数
|
|
||||||
unreadCount, _ := h.readDB.AppMessageLog.WithContext(ctx.RequestContext()).
|
|
||||||
Where(h.readDB.AppMessageLog.AppID.Eq(req.AppID)).
|
|
||||||
Where(h.readDB.AppMessageLog.ReceiverID.Eq(v.ReceiverID)).
|
|
||||||
Where(h.readDB.AppMessageLog.IsRead.Eq(0)).
|
|
||||||
Count()
|
|
||||||
|
|
||||||
|
for k, v := range results {
|
||||||
res.List[k] = latestMessageData{
|
res.List[k] = latestMessageData{
|
||||||
MessageID: v.ID,
|
|
||||||
SendTime: timeutil.FriendlyTime(v.SendTime),
|
SendTime: timeutil.FriendlyTime(v.SendTime),
|
||||||
SenderID: v.SenderID,
|
SenderID: v.SenderID,
|
||||||
SenderName: v.SenderName,
|
SenderName: v.SenderName,
|
||||||
SenderAvatar: "", // TODO: 需要从用户表获取头像信息
|
SenderAvatar: v.AvatarURL,
|
||||||
ReceiverID: v.ReceiverID,
|
UnreadCount: v.UnreadCount,
|
||||||
Content: v.Content,
|
|
||||||
MsgType: v.MsgType,
|
|
||||||
IsRead: v.IsRead, // 直接使用消息表中的 is_read 字段
|
|
||||||
UnreadCount: unreadCount,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -8,13 +8,13 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/DanPlayer/randomname"
|
|
||||||
|
|
||||||
"mini-chat/internal/code"
|
"mini-chat/internal/code"
|
||||||
"mini-chat/internal/pkg/core"
|
"mini-chat/internal/pkg/core"
|
||||||
"mini-chat/internal/pkg/httpclient"
|
"mini-chat/internal/pkg/httpclient"
|
||||||
"mini-chat/internal/pkg/validation"
|
"mini-chat/internal/pkg/validation"
|
||||||
"mini-chat/internal/repository/mysql/model"
|
"mini-chat/internal/repository/mysql/model"
|
||||||
|
|
||||||
|
"github.com/DanPlayer/randomname"
|
||||||
)
|
)
|
||||||
|
|
||||||
type miniprogramLoginRequest struct {
|
type miniprogramLoginRequest struct {
|
||||||
|
|||||||
@ -18,4 +18,4 @@ func New(logger logger.CustomLogger, db mysql.Repo) *handler {
|
|||||||
writeDB: dao.Use(db.GetDbW()),
|
writeDB: dao.Use(db.GetDbW()),
|
||||||
readDB: dao.Use(db.GetDbR()),
|
readDB: dao.Use(db.GetDbR()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -114,7 +114,7 @@ func VerifySignature(rawData, signature, sessionKey string) bool {
|
|||||||
h := sha1.New()
|
h := sha1.New()
|
||||||
h.Write([]byte(rawData + sessionKey))
|
h.Write([]byte(rawData + sessionKey))
|
||||||
expectedSignature := hex.EncodeToString(h.Sum(nil))
|
expectedSignature := hex.EncodeToString(h.Sum(nil))
|
||||||
|
|
||||||
return expectedSignature == signature
|
return expectedSignature == signature
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,7 +126,7 @@ func pkcs7Unpad(data []byte) ([]byte, error) {
|
|||||||
|
|
||||||
// 获取填充长度
|
// 获取填充长度
|
||||||
padding := int(data[len(data)-1])
|
padding := int(data[len(data)-1])
|
||||||
|
|
||||||
// 验证填充长度
|
// 验证填充长度
|
||||||
if padding > len(data) || padding == 0 {
|
if padding > len(data) || padding == 0 {
|
||||||
return nil, fmt.Errorf("无效的填充长度: %d", padding)
|
return nil, fmt.Errorf("无效的填充长度: %d", padding)
|
||||||
@ -140,4 +140,4 @@ func pkcs7Unpad(data []byte) ([]byte, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return data[:len(data)-padding], nil
|
return data[:len(data)-padding], nil
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,11 +4,12 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"mini-chat/internal/pkg/core"
|
|
||||||
"mini-chat/internal/pkg/httpclient"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"mini-chat/internal/pkg/core"
|
||||||
|
"mini-chat/internal/pkg/httpclient"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AccessTokenRequest 获取 access_token 请求参数
|
// AccessTokenRequest 获取 access_token 请求参数
|
||||||
|
|||||||
@ -63,15 +63,15 @@ func NewHTTPMux(logger logger.CustomLogger, db mysql.Repo, cron cron.Server) (co
|
|||||||
|
|
||||||
appNonAuthApiRouter := mux.Group("/app")
|
appNonAuthApiRouter := mux.Group("/app")
|
||||||
{
|
{
|
||||||
appNonAuthApiRouter.POST("/user/create", appHandler.CreateAppUser()) // 新增小程序用户
|
appNonAuthApiRouter.POST("/user/create", appHandler.CreateAppUser()) // 新增小程序用户
|
||||||
appNonAuthApiRouter.GET("/messages", messageHandler.AppMessagePageList()) // 消息列表
|
appNonAuthApiRouter.GET("/messages", messageHandler.AppMessagePageList()) // 消息列表
|
||||||
appNonAuthApiRouter.POST("/send_message", messageHandler.UserSendMessage()) // 发送消息
|
appNonAuthApiRouter.POST("/send_message", messageHandler.UserSendMessage()) // 发送消息
|
||||||
}
|
}
|
||||||
|
|
||||||
// 微信 API 路由组
|
// 微信 API 路由组
|
||||||
wechatApiRouter := mux.Group("/api/wechat")
|
wechatApiRouter := mux.Group("/api/wechat")
|
||||||
{
|
{
|
||||||
wechatApiRouter.POST("/qrcode", wechatHandler.GenerateQRCode()) // 生成微信小程序二维码(返回 Base64)
|
wechatApiRouter.POST("/qrcode", wechatHandler.GenerateQRCode()) // 生成微信小程序二维码(返回 Base64)
|
||||||
wechatApiRouter.POST("/miniprogram/login", wechatHandler.MiniprogramLogin()) // 小程序登录
|
wechatApiRouter.POST("/miniprogram/login", wechatHandler.MiniprogramLogin()) // 小程序登录
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,9 +97,9 @@ func NewHTTPMux(logger logger.CustomLogger, db mysql.Repo, cron cron.Server) (co
|
|||||||
adminAuthApiRouter.PUT("/app/keyword/material/:id", keywordHandler.ModifyKeywordMaterial()) // 修改意图关键字素材
|
adminAuthApiRouter.PUT("/app/keyword/material/:id", keywordHandler.ModifyKeywordMaterial()) // 修改意图关键字素材
|
||||||
adminAuthApiRouter.GET("/app/keyword/materials", keywordHandler.KeywordMaterialPageList()) // 获取意图关键字素材列表
|
adminAuthApiRouter.GET("/app/keyword/materials", keywordHandler.KeywordMaterialPageList()) // 获取意图关键字素材列表
|
||||||
|
|
||||||
adminAuthApiRouter.GET("/app/users", appHandler.UserPageList()) // 获取小程序用户列表
|
adminAuthApiRouter.GET("/app/users", appHandler.UserPageList()) // 获取小程序用户列表
|
||||||
adminAuthApiRouter.POST("/send_message", appHandler.AdminSendMessage()) // 发送消息
|
adminAuthApiRouter.POST("/send_message", appHandler.AdminSendMessage()) // 发送消息
|
||||||
adminAuthApiRouter.GET("/messages", appHandler.AppMessagePageList()) // 获取小程序用户消息列表
|
adminAuthApiRouter.GET("/messages", appHandler.AppMessagePageList()) // 获取小程序用户消息列表
|
||||||
adminAuthApiRouter.GET("/messages/latest", appHandler.LatestMessageByAppId()) // 根据appid获取最新消息记录
|
adminAuthApiRouter.GET("/messages/latest", appHandler.LatestMessageByAppId()) // 根据appid获取最新消息记录
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user