feat(wechat): 添加发送订阅消息接口并移除登录中的模板消息逻辑

将发送模板消息的逻辑从登录接口中移除,并新增独立的订阅消息发送接口
This commit is contained in:
邹方成 2025-10-29 23:08:16 +08:00
parent 61c517eaf7
commit a910871112
6 changed files with 314 additions and 23 deletions

View File

@ -1401,6 +1401,58 @@ const docTemplate = `{
}
}
},
"/api/wechat/subscribe": {
"post": {
"description": "根据模板ID发送订阅消息",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"微信"
],
"summary": "发送订阅消息",
"parameters": [
{
"description": "请求参数",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/wechat.sendSubscribeMessageRequest"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/wechat.sendSubscribeMessageResponse"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/code.Failure"
}
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/code.Failure"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/code.Failure"
}
}
}
}
},
"/api/wechat/template": {
"post": {
"description": "根据 AppID 获取微信小程序的模板ID",
@ -2528,6 +2580,44 @@ const docTemplate = `{
}
}
},
"wechat.sendSubscribeMessageRequest": {
"type": "object",
"required": [
"app_id",
"app_secret",
"template_id",
"touser"
],
"properties": {
"app_id": {
"description": "微信小程序 AppID",
"type": "string"
},
"app_secret": {
"description": "小程序 AppSecret",
"type": "string"
},
"template_id": {
"description": "模板 ID",
"type": "string"
},
"touser": {
"description": "接收者(用户)的 openid",
"type": "string"
}
}
},
"wechat.sendSubscribeMessageResponse": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"success": {
"type": "boolean"
}
}
},
"wechat.templateRequest": {
"type": "object",
"required": [

View File

@ -1393,6 +1393,58 @@
}
}
},
"/api/wechat/subscribe": {
"post": {
"description": "根据模板ID发送订阅消息",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"微信"
],
"summary": "发送订阅消息",
"parameters": [
{
"description": "请求参数",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/wechat.sendSubscribeMessageRequest"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/wechat.sendSubscribeMessageResponse"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/code.Failure"
}
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/code.Failure"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/code.Failure"
}
}
}
}
},
"/api/wechat/template": {
"post": {
"description": "根据 AppID 获取微信小程序的模板ID",
@ -2520,6 +2572,44 @@
}
}
},
"wechat.sendSubscribeMessageRequest": {
"type": "object",
"required": [
"app_id",
"app_secret",
"template_id",
"touser"
],
"properties": {
"app_id": {
"description": "微信小程序 AppID",
"type": "string"
},
"app_secret": {
"description": "小程序 AppSecret",
"type": "string"
},
"template_id": {
"description": "模板 ID",
"type": "string"
},
"touser": {
"description": "接收者(用户)的 openid",
"type": "string"
}
}
},
"wechat.sendSubscribeMessageResponse": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"success": {
"type": "boolean"
}
}
},
"wechat.templateRequest": {
"type": "object",
"required": [

View File

@ -761,6 +761,33 @@ definitions:
description: 用户昵称
type: string
type: object
wechat.sendSubscribeMessageRequest:
properties:
app_id:
description: 微信小程序 AppID
type: string
app_secret:
description: 小程序 AppSecret
type: string
template_id:
description: 模板 ID
type: string
touser:
description: 接收者(用户)的 openid
type: string
required:
- app_id
- app_secret
- template_id
- touser
type: object
wechat.sendSubscribeMessageResponse:
properties:
message:
type: string
success:
type: boolean
type: object
wechat.templateRequest:
properties:
app_id:
@ -1683,6 +1710,40 @@ paths:
summary: 生成微信小程序二维码
tags:
- 微信
/api/wechat/subscribe:
post:
consumes:
- application/json
description: 根据模板ID发送订阅消息
parameters:
- description: 请求参数
in: body
name: request
required: true
schema:
$ref: '#/definitions/wechat.sendSubscribeMessageRequest'
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/wechat.sendSubscribeMessageResponse'
"400":
description: Bad Request
schema:
$ref: '#/definitions/code.Failure'
"404":
description: Not Found
schema:
$ref: '#/definitions/code.Failure'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/code.Failure'
summary: 发送订阅消息
tags:
- 微信
/api/wechat/template:
post:
consumes:

View File

@ -3,9 +3,11 @@ package wechat
import (
"fmt"
"net/http"
"time"
"mini-chat/internal/code"
"mini-chat/internal/pkg/core"
"mini-chat/internal/pkg/miniprogram"
"mini-chat/internal/pkg/validation"
"gorm.io/gorm"
@ -90,3 +92,73 @@ func (h *handler) GetTemplate() core.HandlerFunc {
ctx.Payload(res)
}
}
type sendSubscribeMessageRequest struct {
AppID string `json:"app_id" binding:"required"` // 微信小程序 AppID
TemplateID string `json:"template_id" binding:"required"` // 模板 ID
AppSecret string `json:"app_secret" binding:"required"` // 小程序 AppSecret
Touser string `json:"touser" binding:"required"` // 接收者(用户)的 openid
}
type sendSubscribeMessageResponse struct {
Success bool `json:"success"`
Message string `json:"message"`
}
// SendSubscribeMessage 发送订阅消息
// @Summary 发送订阅消息
// @Description 根据模板ID发送订阅消息
// @Tags 微信
// @Accept json
// @Produce json
// @Param request body sendSubscribeMessageRequest true "请求参数"
// @Success 200 {object} sendSubscribeMessageResponse
// @Failure 400 {object} code.Failure
// @Failure 404 {object} code.Failure
// @Failure 500 {object} code.Failure
// @Router /api/wechat/subscribe [post]
func (h *handler) SendSubscribeMessage() core.HandlerFunc {
return func(ctx core.Context) {
req := new(sendSubscribeMessageRequest)
res := new(sendSubscribeMessageResponse)
// 参数绑定和验证
if err := ctx.ShouldBindJSON(req); err != nil {
ctx.AbortWithError(core.Error(
http.StatusBadRequest,
code.ParamBindError,
validation.Error(err),
))
return
}
// 发送模版消息
accessToken, err := h.servicesMiniProgram.GetAccessToken(req.AppID, req.AppSecret, ctx)
if err != nil {
h.logger.Error(fmt.Sprintf("获取access_token失败: %s", err.Error()))
} else {
sendSubscribeMessageRequest := new(miniprogram.SendSubscribeMessageRequest)
sendSubscribeMessageRequest.Touser = req.Touser
sendSubscribeMessageRequest.TemplateID = req.TemplateID
sendSubscribeMessageRequest.Page = "pages/contact/index"
sendSubscribeMessageRequest.MiniprogramState = "developer" // 需要改成正式版 目前是体验版 跳转小程序类型developer 为开发版trial为体验版formal为正式版默认为正式版
sendSubscribeMessageRequest.Lang = "zh_CN"
sendSubscribeMessageRequest.Data.Thing1.Value = "留言提醒"
sendSubscribeMessageRequest.Data.Time2.Value = time.Now().Format("2006-01-02 15:04:05")
sendSubscribeMessageRequest.Data.Thing4.Value = "您有一条新的消息..."
sendSubscribeMessageResponse := new(miniprogram.SendSubscribeMessageResponse)
err = miniprogram.SendSubscribeMessage(accessToken, sendSubscribeMessageRequest, sendSubscribeMessageResponse)
if err != nil {
res.Success = false
res.Message = "发送订阅消息失败" + err.Error()
h.logger.Error(fmt.Sprintf("发送模版消息失败: %s", err.Error()))
} else {
res.Success = true
res.Message = "订阅消息发送成功"
}
}
ctx.Payload(res)
}
}

View File

@ -11,7 +11,6 @@ import (
"mini-chat/internal/code"
"mini-chat/internal/pkg/core"
"mini-chat/internal/pkg/httpclient"
"mini-chat/internal/pkg/miniprogram"
"mini-chat/internal/pkg/validation"
"mini-chat/internal/repository/mysql/model"
@ -118,28 +117,6 @@ func (h *handler) MiniprogramLogin() core.HandlerFunc {
return
}
// 发送模版消息
accessToken, err := h.servicesMiniProgram.GetAccessToken(req.AppID, miniProgram.AppSecret, ctx)
if err != nil {
h.logger.Error(fmt.Sprintf("获取access_token失败: %s", err.Error()))
} else {
sendSubscribeMessageRequest := new(miniprogram.SendSubscribeMessageRequest)
sendSubscribeMessageRequest.Touser = openID
sendSubscribeMessageRequest.TemplateID = miniProgram.TemplateID
sendSubscribeMessageRequest.Page = "pages/contact/index"
sendSubscribeMessageRequest.MiniprogramState = "trial"
sendSubscribeMessageRequest.Lang = "zh_CN"
sendSubscribeMessageRequest.Data.Thing1.Value = "测试(CC)"
sendSubscribeMessageRequest.Data.Time2.Value = "2025-10-27"
sendSubscribeMessageRequest.Data.Thing4.Value = "测试(CC)"
sendSubscribeMessageResponse := new(miniprogram.SendSubscribeMessageResponse)
err = miniprogram.SendSubscribeMessage(accessToken, sendSubscribeMessageRequest, sendSubscribeMessageResponse)
if err != nil {
h.logger.Error(fmt.Sprintf("发送模版消息失败: %s", err.Error()))
}
}
res.Success = true
res.Message = "登录成功"
res.Token = token

View File

@ -74,6 +74,7 @@ func NewHTTPMux(logger logger.CustomLogger, db mysql.Repo, cron cron.Server) (co
wechatApiRouter.POST("/qrcode", wechatHandler.GenerateQRCode()) // 生成微信小程序二维码(返回 Base64
wechatApiRouter.POST("/miniprogram/login", wechatHandler.MiniprogramLogin()) // 小程序登录
wechatApiRouter.POST("/template", wechatHandler.GetTemplate()) // 生成微信小程序模板消息
wechatApiRouter.POST("/subscribe", wechatHandler.SendSubscribeMessage()) // 发送订阅消息
}
// 管理端认证接口路由组