pet-ai/server/plugin/wechat-integration/api/webhook_api.go

178 lines
4.8 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 api
import (
"crypto/sha1"
"encoding/json"
"fmt"
"io"
"sort"
"strings"
"time"
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/plugin/wechat-integration/model"
"github.com/flipped-aurora/gin-vue-admin/server/plugin/wechat-integration/service"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)
type WebhookApi struct{}
var webhookMessageService = service.ServiceGroupApp.MpMessageService
// OfficialAccountWebhook 公众号消息接收
// @Tags WechatWebhook
// @Summary 公众号消息接收
// @Description 接收微信公众号推送的消息和事件
// @Accept xml
// @Produce xml
// @Param signature query string true "微信加密签名"
// @Param timestamp query string true "时间戳"
// @Param nonce query string true "随机数"
// @Param echostr query string false "随机字符串"
// @Success 200 {string} string "success"
// @Router /wechat/official/webhook [get,post]
func (w *WebhookApi) OfficialAccountWebhook(c *gin.Context) {
startTime := time.Now()
// 获取参数
signature := c.Query("signature")
timestamp := c.Query("timestamp")
nonce := c.Query("nonce")
echostr := c.Query("echostr")
// 准备webhook日志记录
webhookLog := &model.MpWebhookLog{
ConfigID: 1, // 默认配置ID实际应该根据具体配置获取
RequestURL: c.Request.URL.String(),
RequestMethod: c.Request.Method,
Signature: &signature,
Timestamp: &timestamp,
Nonce: &nonce,
Echostr: &echostr,
}
// 记录请求头
if headers, err := json.Marshal(c.Request.Header); err == nil {
headersStr := string(headers)
webhookLog.RequestHeaders = &headersStr
}
// 验证签名
if !w.verifySignature(signature, timestamp, nonce) {
global.GVA_LOG.Error("微信签名验证失败")
// 记录失败信息
webhookLog.ResponseStatus = 403
responseBody := "签名验证失败"
webhookLog.ResponseBody = &responseBody
errorMsg := "签名验证失败"
webhookLog.ErrorMessage = &errorMsg
webhookLog.ProcessTime = int(time.Since(startTime).Milliseconds())
// 保存到数据库
w.saveWebhookLog(webhookLog)
c.String(403, "签名验证失败")
return
}
// GET请求用于验证服务器配置
if c.Request.Method == "GET" {
webhookLog.ResponseStatus = 200
webhookLog.ResponseBody = &echostr
webhookLog.ProcessTime = int(time.Since(startTime).Milliseconds())
// 保存到数据库
w.saveWebhookLog(webhookLog)
c.String(200, echostr)
return
}
// POST请求处理消息
body, err := io.ReadAll(c.Request.Body)
if err != nil {
global.GVA_LOG.Error("读取请求体失败", zap.Error(err))
// 记录失败信息
webhookLog.ResponseStatus = 400
responseBody := "读取请求体失败"
webhookLog.ResponseBody = &responseBody
errorMsg := err.Error()
webhookLog.ErrorMessage = &errorMsg
webhookLog.ProcessTime = int(time.Since(startTime).Milliseconds())
// 保存到数据库
w.saveWebhookLog(webhookLog)
c.String(400, "读取请求体失败")
return
}
// 记录请求体
bodyStr := string(body)
webhookLog.RequestBody = &bodyStr
// 简化消息处理 - 暂时只记录日志
global.GVA_LOG.Info("收到微信消息", zap.String("body", bodyStr))
// TODO: 实现完整的消息解析和处理
// 由于微信SDK API变化暂时简化处理
// 记录成功响应
webhookLog.ResponseStatus = 200
responseBody := "success"
webhookLog.ResponseBody = &responseBody
webhookLog.ProcessTime = int(time.Since(startTime).Milliseconds())
// 保存到数据库
w.saveWebhookLog(webhookLog)
c.String(200, "success")
}
// verifySignature 验证微信签名
func (w *WebhookApi) verifySignature(signature, timestamp, nonce string) bool {
// 从数据库获取微信公众号配置
var mpConfig model.MpConfig
err := global.GVA_DB.Where("config_type = ?", model.ConfigTypeMP).First(&mpConfig).Error
if err != nil {
global.GVA_LOG.Error("获取微信公众号配置失败", zap.Error(err))
return false
}
token := ""
if mpConfig.Token != nil {
token = *mpConfig.Token
}
if token == "" {
global.GVA_LOG.Error("微信公众号Token未配置")
return false
}
// 将token、timestamp、nonce三个参数进行字典序排序
strs := []string{token, timestamp, nonce}
sort.Strings(strs)
// 将三个参数字符串拼接成一个字符串进行sha1加密
str := strings.Join(strs, "")
h := sha1.New()
h.Write([]byte(str))
encrypted := fmt.Sprintf("%x", h.Sum(nil))
// 将加密后的字符串与signature对比
return encrypted == signature
}
// saveWebhookLog 保存Webhook日志到数据库
func (w *WebhookApi) saveWebhookLog(log *model.MpWebhookLog) {
if err := global.GVA_DB.Create(log).Error; err != nil {
global.GVA_LOG.Error("保存Webhook日志失败", zap.Error(err))
// 数据库记录失败不影响webhook正常响应
} else {
global.GVA_LOG.Debug("Webhook日志保存成功", zap.Uint("id", log.ID))
}
}