From ba0630b2dadd8f6d0f570ec6f5792fec215972a7 Mon Sep 17 00:00:00 2001 From: summer <> Date: Mon, 20 Oct 2025 14:11:27 +0800 Subject: [PATCH] =?UTF-8?q?feat(1.0):=E8=B0=83=E6=95=B4=E6=9C=AA=E8=AF=BB?= =?UTF-8?q?=E5=88=97=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/docs.go | 25 ++------- docs/swagger.json | 25 ++------- docs/swagger.yaml | 17 +----- internal/api/app/app.go | 2 + internal/api/app/app_latest_messages.go | 70 ++++++++++++------------ internal/api/wechat/miniprogram_login.go | 4 +- internal/api/wechat/wechat.go | 2 +- internal/pkg/wechat/decrypt.go | 6 +- internal/pkg/wechat/qrcode.go | 5 +- internal/router/router.go | 12 ++-- 10 files changed, 65 insertions(+), 103 deletions(-) diff --git a/docs/docs.go b/docs/docs.go index d1789a3..c820453 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -924,6 +924,11 @@ const docTemplate = `{ }, "/admin/messages/latest": { "get": { + "security": [ + { + "LoginVerifyToken": [] + } + ], "description": "管理端根据appid获取最新消息记录,包含已读未读状态,访问时自动标记为已读", "consumes": [ "application/json" @@ -1788,26 +1793,6 @@ const docTemplate = `{ "app.latestMessageData": { "type": "object", "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": { "description": "发送时间", "type": "string" diff --git a/docs/swagger.json b/docs/swagger.json index 52a3983..ca5b3de 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -916,6 +916,11 @@ }, "/admin/messages/latest": { "get": { + "security": [ + { + "LoginVerifyToken": [] + } + ], "description": "管理端根据appid获取最新消息记录,包含已读未读状态,访问时自动标记为已读", "consumes": [ "application/json" @@ -1780,26 +1785,6 @@ "app.latestMessageData": { "type": "object", "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": { "description": "发送时间", "type": "string" diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 235320a..67fae55 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -275,21 +275,6 @@ definitions: type: object app.latestMessageData: 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: description: 发送时间 type: string @@ -1414,6 +1399,8 @@ paths: description: Bad Request schema: $ref: '#/definitions/code.Failure' + security: + - LoginVerifyToken: [] summary: 根据appid获取最新消息记录 tags: - 管理端.小程序 diff --git a/internal/api/app/app.go b/internal/api/app/app.go index 15f012f..5dd4795 100644 --- a/internal/api/app/app.go +++ b/internal/api/app/app.go @@ -10,6 +10,7 @@ type handler struct { logger logger.CustomLogger writeDB *dao.Query readDB *dao.Query + db mysql.Repo } func New(logger logger.CustomLogger, db mysql.Repo) *handler { @@ -17,5 +18,6 @@ func New(logger logger.CustomLogger, db mysql.Repo) *handler { logger: logger, writeDB: dao.Use(db.GetDbW()), readDB: dao.Use(db.GetDbR()), + db: db, } } diff --git a/internal/api/app/app_latest_messages.go b/internal/api/app/app_latest_messages.go index fd191a5..28f005a 100644 --- a/internal/api/app/app_latest_messages.go +++ b/internal/api/app/app_latest_messages.go @@ -3,6 +3,7 @@ package app import ( "fmt" "net/http" + "time" "mini-chat/internal/code" "mini-chat/internal/pkg/core" @@ -17,15 +18,10 @@ type latestMessageByAppIdRequest struct { } 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"` // 未读数量 } @@ -48,6 +44,7 @@ type latestMessageByAppIdResponse struct { // @Success 200 {object} latestMessageByAppIdResponse // @Failure 400 {object} code.Failure // @Router /admin/messages/latest [get] +// @Security LoginVerifyToken func (h *handler) LatestMessageByAppId() core.HandlerFunc { return func(ctx core.Context) { req := new(latestMessageByAppIdRequest) @@ -78,37 +75,54 @@ func (h *handler) LatestMessageByAppId() core.HandlerFunc { return } - query := h.readDB.AppMessageLog.WithContext(ctx.RequestContext()). - Where(h.readDB.AppMessageLog.AppID.Eq(req.AppID)) + type unreadMessageResult struct { + 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"` + } - // 查询总数 - total, err := query.Count() - if err != nil { + var results []unreadMessageResult + var total int64 + + 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( http.StatusBadRequest, code.ListMessageError, - fmt.Sprintf("%s:%s", code.Text(code.ListMessageError), err.Error())), + fmt.Sprintf("%s:%s", code.Text(code.ListMessageError), countErr.Error())), ) return } - // 分页查询指定小程序的最新消息 - resultData, err := query. - Order(h.readDB.AppMessageLog.SendTime.Asc()). + resultErr := h.db.GetDbR().Table("app_message_log m"). + 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"). + 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). Limit(req.PageSize). - Find() - if err != nil { + Find(&results). + Error + if resultErr != nil { ctx.AbortWithError(core.Error( http.StatusBadRequest, code.ListMessageError, - fmt.Sprintf("%s:%s", code.Text(code.ListMessageError), err.Error())), + fmt.Sprintf("%s:%s", code.Text(code.ListMessageError), resultErr.Error())), ) return } // 自动标记该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.IsRead.Eq(0)). Update(h.writeDB.AppMessageLog.IsRead, 1) @@ -120,27 +134,15 @@ func (h *handler) LatestMessageByAppId() core.HandlerFunc { res.Page = req.Page res.PageSize = req.PageSize res.Total = total - res.List = make([]latestMessageData, len(resultData)) - - 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() + res.List = make([]latestMessageData, len(results)) + for k, v := range results { 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: v.IsRead, // 直接使用消息表中的 is_read 字段 - UnreadCount: unreadCount, + SenderAvatar: v.AvatarURL, + UnreadCount: v.UnreadCount, } } diff --git a/internal/api/wechat/miniprogram_login.go b/internal/api/wechat/miniprogram_login.go index 11fa167..66b39da 100644 --- a/internal/api/wechat/miniprogram_login.go +++ b/internal/api/wechat/miniprogram_login.go @@ -8,13 +8,13 @@ import ( "net/http" "time" - "github.com/DanPlayer/randomname" - "mini-chat/internal/code" "mini-chat/internal/pkg/core" "mini-chat/internal/pkg/httpclient" "mini-chat/internal/pkg/validation" "mini-chat/internal/repository/mysql/model" + + "github.com/DanPlayer/randomname" ) type miniprogramLoginRequest struct { diff --git a/internal/api/wechat/wechat.go b/internal/api/wechat/wechat.go index c720313..4545072 100644 --- a/internal/api/wechat/wechat.go +++ b/internal/api/wechat/wechat.go @@ -18,4 +18,4 @@ func New(logger logger.CustomLogger, db mysql.Repo) *handler { writeDB: dao.Use(db.GetDbW()), readDB: dao.Use(db.GetDbR()), } -} \ No newline at end of file +} diff --git a/internal/pkg/wechat/decrypt.go b/internal/pkg/wechat/decrypt.go index 192d786..a8ba90b 100644 --- a/internal/pkg/wechat/decrypt.go +++ b/internal/pkg/wechat/decrypt.go @@ -114,7 +114,7 @@ func VerifySignature(rawData, signature, sessionKey string) bool { h := sha1.New() h.Write([]byte(rawData + sessionKey)) expectedSignature := hex.EncodeToString(h.Sum(nil)) - + return expectedSignature == signature } @@ -126,7 +126,7 @@ func pkcs7Unpad(data []byte) ([]byte, error) { // 获取填充长度 padding := int(data[len(data)-1]) - + // 验证填充长度 if padding > len(data) || padding == 0 { return nil, fmt.Errorf("无效的填充长度: %d", padding) @@ -140,4 +140,4 @@ func pkcs7Unpad(data []byte) ([]byte, error) { } return data[:len(data)-padding], nil -} \ No newline at end of file +} diff --git a/internal/pkg/wechat/qrcode.go b/internal/pkg/wechat/qrcode.go index 56ad3e2..e8d63b7 100644 --- a/internal/pkg/wechat/qrcode.go +++ b/internal/pkg/wechat/qrcode.go @@ -4,11 +4,12 @@ import ( "bytes" "encoding/json" "fmt" - "mini-chat/internal/pkg/core" - "mini-chat/internal/pkg/httpclient" "net/http" "sync" "time" + + "mini-chat/internal/pkg/core" + "mini-chat/internal/pkg/httpclient" ) // AccessTokenRequest 获取 access_token 请求参数 diff --git a/internal/router/router.go b/internal/router/router.go index dd41e84..833ae11 100644 --- a/internal/router/router.go +++ b/internal/router/router.go @@ -63,15 +63,15 @@ func NewHTTPMux(logger logger.CustomLogger, db mysql.Repo, cron cron.Server) (co appNonAuthApiRouter := mux.Group("/app") { - appNonAuthApiRouter.POST("/user/create", appHandler.CreateAppUser()) // 新增小程序用户 - appNonAuthApiRouter.GET("/messages", messageHandler.AppMessagePageList()) // 消息列表 + appNonAuthApiRouter.POST("/user/create", appHandler.CreateAppUser()) // 新增小程序用户 + appNonAuthApiRouter.GET("/messages", messageHandler.AppMessagePageList()) // 消息列表 appNonAuthApiRouter.POST("/send_message", messageHandler.UserSendMessage()) // 发送消息 } // 微信 API 路由组 wechatApiRouter := mux.Group("/api/wechat") { - wechatApiRouter.POST("/qrcode", wechatHandler.GenerateQRCode()) // 生成微信小程序二维码(返回 Base64) + wechatApiRouter.POST("/qrcode", wechatHandler.GenerateQRCode()) // 生成微信小程序二维码(返回 Base64) 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.GET("/app/keyword/materials", keywordHandler.KeywordMaterialPageList()) // 获取意图关键字素材列表 - adminAuthApiRouter.GET("/app/users", appHandler.UserPageList()) // 获取小程序用户列表 - adminAuthApiRouter.POST("/send_message", appHandler.AdminSendMessage()) // 发送消息 - adminAuthApiRouter.GET("/messages", appHandler.AppMessagePageList()) // 获取小程序用户消息列表 + adminAuthApiRouter.GET("/app/users", appHandler.UserPageList()) // 获取小程序用户列表 + adminAuthApiRouter.POST("/send_message", appHandler.AdminSendMessage()) // 发送消息 + adminAuthApiRouter.GET("/messages", appHandler.AppMessagePageList()) // 获取小程序用户消息列表 adminAuthApiRouter.GET("/messages/latest", appHandler.LatestMessageByAppId()) // 根据appid获取最新消息记录 }