This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
package api
|
||||
|
||||
// ApiGroup 微信集成API组
|
||||
type ApiGroup struct {
|
||||
WechatMiniApi WechatMiniApi // 微信小程序API
|
||||
MpUserApi MpUserApi // 公众号用户API
|
||||
MpMessageApi MpMessageApi // 公众号消息API
|
||||
MpAutoReplyApi MpAutoReplyApi // 公众号自动回复API
|
||||
MpMenuApi MpMenuApi // 公众号菜单API
|
||||
MpMaterialApi MpMaterialApi // 公众号素材API
|
||||
WechatWebhookApi WechatWebhookApi // 微信Webhook API
|
||||
}
|
||||
|
||||
// ApiGroupApp API组实例
|
||||
var ApiGroupApp = new(ApiGroup)
|
||||
@@ -0,0 +1,132 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/plugin/wechat-integration/service"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type MiniStatisticsApi struct{}
|
||||
|
||||
var miniStatisticsService = service.ServiceGroupApp.MiniStatisticsService
|
||||
|
||||
// GetMiniStatistics 获取小程序基础统计数据
|
||||
// @Tags MiniStatistics
|
||||
// @Summary 获取小程序基础统计数据
|
||||
// @Description 获取小程序基础统计数据
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {object} response.Response{data=response.MiniStatisticsResponse} "获取成功"
|
||||
// @Router /wechat/mini/statistics [get]
|
||||
func (m *MiniStatisticsApi) GetMiniStatistics(c *gin.Context) {
|
||||
statistics, err := miniStatisticsService.GetMiniStatistics()
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取小程序基础统计数据失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithData(statistics, c)
|
||||
}
|
||||
|
||||
// GetVisitTrendData 获取访问趋势数据
|
||||
// @Tags MiniStatistics
|
||||
// @Summary 获取访问趋势数据
|
||||
// @Description 获取小程序访问趋势数据
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param startDate query string false "开始日期"
|
||||
// @Param endDate query string false "结束日期"
|
||||
// @Success 200 {object} response.Response{data=[]response.VisitTrendData} "获取成功"
|
||||
// @Router /wechat/mini/statistics/visit-trend [get]
|
||||
func (m *MiniStatisticsApi) GetVisitTrendData(c *gin.Context) {
|
||||
startDate := c.Query("startDate")
|
||||
endDate := c.Query("endDate")
|
||||
|
||||
trendData, err := miniStatisticsService.GetVisitTrendData(startDate, endDate)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取访问趋势数据失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithData(trendData, c)
|
||||
}
|
||||
|
||||
// GetSourceData 获取用户来源分布数据
|
||||
// @Tags MiniStatistics
|
||||
// @Summary 获取用户来源分布数据
|
||||
// @Description 获取小程序用户来源分布数据
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {object} response.Response{data=[]response.SourceData} "获取成功"
|
||||
// @Router /wechat/mini/statistics/source [get]
|
||||
func (m *MiniStatisticsApi) GetSourceData(c *gin.Context) {
|
||||
sourceData, err := miniStatisticsService.GetSourceData()
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取用户来源分布数据失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithData(sourceData, c)
|
||||
}
|
||||
|
||||
// GetPageData 获取页面访问数据
|
||||
// @Tags MiniStatistics
|
||||
// @Summary 获取页面访问数据
|
||||
// @Description 获取小程序页面访问数据
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {object} response.Response{data=[]response.PageData} "获取成功"
|
||||
// @Router /wechat/mini/statistics/page [get]
|
||||
func (m *MiniStatisticsApi) GetPageData(c *gin.Context) {
|
||||
pageData, err := miniStatisticsService.GetPageData()
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取页面访问数据失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithData(pageData, c)
|
||||
}
|
||||
|
||||
// GetRetentionData 获取用户留存数据
|
||||
// @Tags MiniStatistics
|
||||
// @Summary 获取用户留存数据
|
||||
// @Description 获取小程序用户留存数据
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {object} response.Response{data=[]response.RetentionData} "获取成功"
|
||||
// @Router /wechat/mini/statistics/retention [get]
|
||||
func (m *MiniStatisticsApi) GetRetentionData(c *gin.Context) {
|
||||
retentionData, err := miniStatisticsService.GetRetentionData()
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取用户留存数据失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithData(retentionData, c)
|
||||
}
|
||||
|
||||
// GetDeviceData 获取设备分布数据
|
||||
// @Tags MiniStatistics
|
||||
// @Summary 获取设备分布数据
|
||||
// @Description 获取小程序设备分布数据
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {object} response.Response{data=[]response.DeviceData} "获取成功"
|
||||
// @Router /wechat/mini/statistics/device [get]
|
||||
func (m *MiniStatisticsApi) GetDeviceData(c *gin.Context) {
|
||||
deviceData, err := miniStatisticsService.GetDeviceData()
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取设备分布数据失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithData(deviceData, c)
|
||||
}
|
||||
@@ -0,0 +1,294 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/plugin/wechat-integration/model"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/plugin/wechat-integration/model/request"
|
||||
wechatResponse "github.com/flipped-aurora/gin-vue-admin/server/plugin/wechat-integration/model/response"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/plugin/wechat-integration/service"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type MpAutoReplyApi struct{}
|
||||
|
||||
var mpAutoReplyService = service.ServiceGroupApp.MpAutoReplyService
|
||||
|
||||
// GetAutoReplyList 获取自动回复列表
|
||||
// @Tags MpAutoReply
|
||||
// @Summary 获取自动回复列表
|
||||
// @Description 获取公众号自动回复规则列表
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param page query int false "页码"
|
||||
// @Param pageSize query int false "每页数量"
|
||||
// @Param replyType query int false "回复类型"
|
||||
// @Success 200 {object} response.Response{data=response.PageResult} "获取成功"
|
||||
// @Router /wechat/mp/auto-reply [get]
|
||||
func (m *MpAutoReplyApi) GetAutoReplyList(c *gin.Context) {
|
||||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||
pageSize, _ := strconv.Atoi(c.DefaultQuery("pageSize", "10"))
|
||||
|
||||
var replyType *int
|
||||
if rt := c.Query("replyType"); rt != "" {
|
||||
if t, err := strconv.Atoi(rt); err == nil {
|
||||
replyType = &t
|
||||
}
|
||||
}
|
||||
|
||||
if page <= 0 {
|
||||
page = 1
|
||||
}
|
||||
if pageSize <= 0 || pageSize > 100 {
|
||||
pageSize = 10
|
||||
}
|
||||
|
||||
replies, total, err := mpAutoReplyService.GetAutoReplyList(page, pageSize, replyType)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取自动回复列表失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithDetailed(wechatResponse.PageResult{
|
||||
List: replies,
|
||||
Total: total,
|
||||
Page: page,
|
||||
PageSize: pageSize,
|
||||
}, "获取成功", c)
|
||||
}
|
||||
|
||||
// GetAutoReplyByID 获取自动回复详情
|
||||
// @Tags MpAutoReply
|
||||
// @Summary 获取自动回复详情
|
||||
// @Description 根据ID获取自动回复详细信息
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path int true "自动回复ID"
|
||||
// @Success 200 {object} response.Response{data=model.MpAutoReply} "获取成功"
|
||||
// @Router /wechat/mp/auto-reply/{id} [get]
|
||||
func (m *MpAutoReplyApi) GetAutoReplyByID(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 32)
|
||||
if err != nil {
|
||||
response.FailWithMessage("无效的自动回复ID", c)
|
||||
return
|
||||
}
|
||||
|
||||
reply, err := mpAutoReplyService.GetAutoReplyByID(uint(id))
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取自动回复详情失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithData(reply, c)
|
||||
}
|
||||
|
||||
// CreateAutoReply 创建自动回复
|
||||
// @Tags MpAutoReply
|
||||
// @Summary 创建自动回复
|
||||
// @Description 创建新的自动回复规则
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param data body request.CreateAutoReplyRequest true "创建参数"
|
||||
// @Success 200 {object} response.Response{data=model.MpAutoReply} "创建成功"
|
||||
// @Router /wechat/mp/auto-reply [post]
|
||||
func (m *MpAutoReplyApi) CreateAutoReply(c *gin.Context) {
|
||||
var req request.CreateAutoReplyRequest
|
||||
err := c.ShouldBindJSON(&req)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
// 验证必填字段
|
||||
if req.Type == 0 || req.ResponseMessageType == "" || req.ResponseContent == "" {
|
||||
response.FailWithMessage("回复类型、响应消息类型和响应内容不能为空", c)
|
||||
return
|
||||
}
|
||||
|
||||
// 构建自动回复对象
|
||||
autoReply := &model.MpAutoReply{
|
||||
Type: req.Type,
|
||||
ResponseMessageType: req.ResponseMessageType,
|
||||
ResponseContent: &req.ResponseContent,
|
||||
}
|
||||
|
||||
// 根据类型设置特定字段
|
||||
if req.Type == model.AutoReplyTypeKeyword {
|
||||
if req.RequestKeyword == "" {
|
||||
response.FailWithMessage("关键字回复必须设置关键字", c)
|
||||
return
|
||||
}
|
||||
autoReply.RequestKeyword = &req.RequestKeyword
|
||||
if req.RequestMatch != 0 {
|
||||
autoReply.RequestMatch = &req.RequestMatch
|
||||
}
|
||||
} else if req.Type == model.AutoReplyTypeMessage {
|
||||
if req.RequestMessageType == "" {
|
||||
response.FailWithMessage("消息回复必须设置请求消息类型", c)
|
||||
return
|
||||
}
|
||||
autoReply.RequestMessageType = &req.RequestMessageType
|
||||
}
|
||||
|
||||
err = mpAutoReplyService.CreateAutoReply(autoReply)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("创建自动回复失败!", zap.Error(err))
|
||||
response.FailWithMessage("创建失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithDetailed(autoReply, "创建成功", c)
|
||||
}
|
||||
|
||||
// UpdateAutoReply 更新自动回复
|
||||
// @Tags MpAutoReply
|
||||
// @Summary 更新自动回复
|
||||
// @Description 更新自动回复规则
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param data body request.UpdateAutoReplyRequest true "更新参数"
|
||||
// @Success 200 {object} response.Response "更新成功"
|
||||
// @Router /wechat/mp/auto-reply [put]
|
||||
func (m *MpAutoReplyApi) UpdateAutoReply(c *gin.Context) {
|
||||
var req request.UpdateAutoReplyRequest
|
||||
err := c.ShouldBindJSON(&req)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
if req.ID == 0 {
|
||||
response.FailWithMessage("ID不能为空", c)
|
||||
return
|
||||
}
|
||||
|
||||
// 获取现有记录
|
||||
existingReply, err := mpAutoReplyService.GetAutoReplyByID(req.ID)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取自动回复失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
// 更新字段
|
||||
if req.ResponseContent != "" {
|
||||
existingReply.ResponseContent = &req.ResponseContent
|
||||
}
|
||||
if req.RequestKeyword != "" {
|
||||
existingReply.RequestKeyword = &req.RequestKeyword
|
||||
}
|
||||
if req.RequestMatch != 0 {
|
||||
existingReply.RequestMatch = &req.RequestMatch
|
||||
}
|
||||
|
||||
err = mpAutoReplyService.UpdateAutoReply(existingReply)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("更新自动回复失败!", zap.Error(err))
|
||||
response.FailWithMessage("更新失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithMessage("更新成功", c)
|
||||
}
|
||||
|
||||
// DeleteAutoReply 删除自动回复
|
||||
// @Tags MpAutoReply
|
||||
// @Summary 删除自动回复
|
||||
// @Description 删除指定的自动回复规则
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path int true "自动回复ID"
|
||||
// @Success 200 {object} response.Response "删除成功"
|
||||
// @Router /wechat/mp/auto-reply/{id} [delete]
|
||||
func (m *MpAutoReplyApi) DeleteAutoReply(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 32)
|
||||
if err != nil {
|
||||
response.FailWithMessage("无效的自动回复ID", c)
|
||||
return
|
||||
}
|
||||
|
||||
err = mpAutoReplyService.DeleteAutoReply(uint(id))
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("删除自动回复失败!", zap.Error(err))
|
||||
response.FailWithMessage("删除失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithMessage("删除成功", c)
|
||||
}
|
||||
|
||||
// TestAutoReply 测试自动回复
|
||||
// @Tags MpAutoReply
|
||||
// @Summary 测试自动回复
|
||||
// @Description 测试关键字是否能触发自动回复
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param data body request.TestAutoReplyRequest true "测试参数"
|
||||
// @Success 200 {object} response.Response{data=model.MpAutoReply} "测试成功"
|
||||
// @Router /wechat/mp/auto-reply/test [post]
|
||||
func (m *MpAutoReplyApi) TestAutoReply(c *gin.Context) {
|
||||
var req request.TestAutoReplyRequest
|
||||
err := c.ShouldBindJSON(&req)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
if req.Keyword == "" {
|
||||
response.FailWithMessage("测试关键字不能为空", c)
|
||||
return
|
||||
}
|
||||
|
||||
reply, err := mpAutoReplyService.TestAutoReply(req.Keyword)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("测试自动回复失败!", zap.Error(err))
|
||||
response.FailWithMessage("测试失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
if reply == nil {
|
||||
response.OkWithDetailed(nil, "没有匹配的自动回复", c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithDetailed(reply, "找到匹配的自动回复", c)
|
||||
}
|
||||
|
||||
// SetSubscribeReply 设置关注回复
|
||||
// @Tags MpAutoReply
|
||||
// @Summary 设置关注回复
|
||||
// @Description 设置用户关注时的自动回复
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param data body request.SetSubscribeReplyRequest true "设置参数"
|
||||
// @Success 200 {object} response.Response "设置成功"
|
||||
// @Router /wechat/mp/auto-reply/subscribe [post]
|
||||
func (m *MpAutoReplyApi) SetSubscribeReply(c *gin.Context) {
|
||||
var req request.SetSubscribeReplyRequest
|
||||
err := c.ShouldBindJSON(&req)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
if req.Content == "" {
|
||||
response.FailWithMessage("回复内容不能为空", c)
|
||||
return
|
||||
}
|
||||
|
||||
err = mpAutoReplyService.SetSubscribeReply(req.Content)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("设置关注回复失败!", zap.Error(err))
|
||||
response.FailWithMessage("设置失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithMessage("设置成功", c)
|
||||
}
|
||||
@@ -0,0 +1,369 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/plugin/wechat-integration/model"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/plugin/wechat-integration/model/request"
|
||||
wechatResponse "github.com/flipped-aurora/gin-vue-admin/server/plugin/wechat-integration/model/response"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/plugin/wechat-integration/service"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type MpConfigApi struct{}
|
||||
|
||||
var mpConfigService = service.ServiceGroupApp.MpConfigService
|
||||
|
||||
// GetWechatConfig 获取微信配置
|
||||
// @Tags MpConfig
|
||||
// @Summary 获取微信配置
|
||||
// @Description 获取微信配置信息
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param type query string false "配置类型 mp/mini"
|
||||
// @Success 200 {object} response.Response{data=model.MpConfig} "获取成功"
|
||||
// @Router /wechat/mp/config [get]
|
||||
func (m *MpConfigApi) GetWechatConfig(c *gin.Context) {
|
||||
configType := c.DefaultQuery("type", "mp")
|
||||
|
||||
config, err := mpConfigService.GetWechatConfig(configType)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取微信配置失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithData(config, c)
|
||||
}
|
||||
|
||||
// SaveWechatConfig 保存微信配置
|
||||
// @Tags MpConfig
|
||||
// @Summary 保存微信配置
|
||||
// @Description 保存微信配置信息
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param data body request.SaveMpConfigRequest true "配置信息"
|
||||
// @Success 200 {object} response.Response "保存成功"
|
||||
// @Router /wechat/mp/config [post]
|
||||
func (m *MpConfigApi) SaveWechatConfig(c *gin.Context) {
|
||||
var req request.SaveMpConfigRequest
|
||||
err := c.ShouldBindJSON(&req)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
// 将DTO转换为数据库实体
|
||||
config := &model.MpConfig{
|
||||
ConfigType: req.ConfigType,
|
||||
AppID: req.AppID,
|
||||
AppSecret: req.AppSecret,
|
||||
Token: req.Token,
|
||||
EncodingAESKey: req.EncodingAESKey,
|
||||
ServerURL: req.ServerURL,
|
||||
WebhookURL: req.WebhookURL,
|
||||
MiniAppName: req.MiniAppName,
|
||||
MiniAppDesc: req.MiniAppDesc,
|
||||
Status: req.Status,
|
||||
ExtraConfig: req.ExtraConfig,
|
||||
Remark: req.Remark,
|
||||
}
|
||||
|
||||
// 验证配置
|
||||
err = mpConfigService.ValidateConfig(config)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
err = mpConfigService.SaveWechatConfig(config)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("保存微信配置失败!", zap.Error(err))
|
||||
response.FailWithMessage("保存失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithData(config, c)
|
||||
}
|
||||
|
||||
// TestWechatConfig 测试微信配置
|
||||
// @Tags MpConfig
|
||||
// @Summary 测试微信配置
|
||||
// @Description 测试微信配置是否有效
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param data body request.TestMpConfigRequest true "配置信息"
|
||||
// @Success 200 {object} response.Response "测试成功"
|
||||
// @Router /wechat/mp/config/test [post]
|
||||
func (m *MpConfigApi) TestWechatConfig(c *gin.Context) {
|
||||
var req request.TestMpConfigRequest
|
||||
err := c.ShouldBindJSON(&req)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
// 将DTO转换为数据库实体
|
||||
config := &model.MpConfig{
|
||||
ConfigType: req.ConfigType,
|
||||
AppID: req.AppID,
|
||||
AppSecret: req.AppSecret,
|
||||
Token: req.Token,
|
||||
EncodingAESKey: req.EncodingAESKey,
|
||||
ServerURL: req.ServerURL,
|
||||
WebhookURL: req.WebhookURL,
|
||||
MiniAppName: req.MiniAppName,
|
||||
MiniAppDesc: req.MiniAppDesc,
|
||||
}
|
||||
|
||||
err = mpConfigService.TestWechatConfig(config)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("测试微信配置失败!", zap.Error(err))
|
||||
response.FailWithMessage("测试失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithMessage("测试成功", c)
|
||||
}
|
||||
|
||||
// GetConfigList 获取配置列表
|
||||
// @Tags MpConfig
|
||||
// @Summary 获取配置列表
|
||||
// @Description 获取所有微信配置列表
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {object} response.Response{data=[]model.MpConfig} "获取成功"
|
||||
// @Router /wechat/mp/config/list [get]
|
||||
func (m *MpConfigApi) GetConfigList(c *gin.Context) {
|
||||
configs, err := mpConfigService.GetConfigList()
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取配置列表失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithData(configs, c)
|
||||
}
|
||||
|
||||
// DeleteConfig 删除配置
|
||||
// @Tags MpConfig
|
||||
// @Summary 删除配置
|
||||
// @Description 删除微信配置
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path int true "配置ID"
|
||||
// @Success 200 {object} response.Response "删除成功"
|
||||
// @Router /wechat/mp/config/{id} [delete]
|
||||
func (m *MpConfigApi) DeleteConfig(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 32)
|
||||
if err != nil {
|
||||
response.FailWithMessage("无效的配置ID", c)
|
||||
return
|
||||
}
|
||||
|
||||
err = mpConfigService.DeleteConfig(uint(id))
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("删除配置失败!", zap.Error(err))
|
||||
response.FailWithMessage("删除失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithMessage("删除成功", c)
|
||||
}
|
||||
|
||||
// GetWebhookLogs 获取Webhook日志
|
||||
// @Tags MpConfig
|
||||
// @Summary 获取Webhook日志
|
||||
// @Description 获取微信Webhook日志列表
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param page query int false "页码"
|
||||
// @Param pageSize query int false "每页数量"
|
||||
// @Param configId query int false "配置ID"
|
||||
// @Success 200 {object} response.Response{data=response.PageResult} "获取成功"
|
||||
// @Router /wechat/mp/webhook/logs [get]
|
||||
func (m *MpConfigApi) GetWebhookLogs(c *gin.Context) {
|
||||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||
pageSize, _ := strconv.Atoi(c.DefaultQuery("pageSize", "10"))
|
||||
|
||||
var configID *uint
|
||||
if configIDStr := c.Query("configId"); configIDStr != "" {
|
||||
if id, err := strconv.ParseUint(configIDStr, 10, 32); err == nil {
|
||||
configIDUint := uint(id)
|
||||
configID = &configIDUint
|
||||
}
|
||||
}
|
||||
|
||||
if page <= 0 {
|
||||
page = 1
|
||||
}
|
||||
if pageSize <= 0 || pageSize > 100 {
|
||||
pageSize = 10
|
||||
}
|
||||
|
||||
logs, total, err := mpConfigService.GetWebhookLogs(page, pageSize, configID)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取Webhook日志失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithDetailed(wechatResponse.PageResult{
|
||||
List: logs,
|
||||
Total: total,
|
||||
Page: page,
|
||||
PageSize: pageSize,
|
||||
}, "获取成功", c)
|
||||
}
|
||||
|
||||
// GetWebhookLogDetail 获取Webhook日志详情
|
||||
// @Tags MpConfig
|
||||
// @Summary 获取Webhook日志详情
|
||||
// @Description 获取微信Webhook日志详情
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path int true "日志ID"
|
||||
// @Success 200 {object} response.Response{data=model.MpWebhookLog} "获取成功"
|
||||
// @Router /wechat/mp/webhook/logs/{id} [get]
|
||||
func (m *MpConfigApi) GetWebhookLogDetail(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 32)
|
||||
if err != nil {
|
||||
response.FailWithMessage("无效的日志ID", c)
|
||||
return
|
||||
}
|
||||
|
||||
log, err := mpConfigService.GetWebhookLogByID(uint(id))
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取Webhook日志详情失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithData(log, c)
|
||||
}
|
||||
|
||||
// ClearWebhookLogs 清空Webhook日志
|
||||
// @Tags MpConfig
|
||||
// @Summary 清空Webhook日志
|
||||
// @Description 清空微信Webhook日志
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param configId query int false "配置ID"
|
||||
// @Success 200 {object} response.Response "清空成功"
|
||||
// @Router /wechat/mp/webhook/logs/clear [post]
|
||||
func (m *MpConfigApi) ClearWebhookLogs(c *gin.Context) {
|
||||
var configID *uint
|
||||
if configIDStr := c.Query("configId"); configIDStr != "" {
|
||||
if id, err := strconv.ParseUint(configIDStr, 10, 32); err == nil {
|
||||
configIDUint := uint(id)
|
||||
configID = &configIDUint
|
||||
}
|
||||
}
|
||||
|
||||
err := mpConfigService.ClearWebhookLogs(configID)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("清空Webhook日志失败!", zap.Error(err))
|
||||
response.FailWithMessage("清空失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithMessage("清空成功", c)
|
||||
}
|
||||
|
||||
// ValidateConfig 验证微信配置
|
||||
// @Tags MpConfig
|
||||
// @Summary 验证微信配置
|
||||
// @Description 验证微信配置是否有效
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {object} response.Response "验证成功"
|
||||
// @Router /wechat/mp/config/validate [post]
|
||||
func (m *MpConfigApi) ValidateConfig(c *gin.Context) {
|
||||
// 获取当前配置
|
||||
config, err := mpConfigService.GetWechatConfig("mp")
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取微信配置失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取配置失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
// 验证配置
|
||||
err = mpConfigService.ValidateConfig(config)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("验证微信配置失败!", zap.Error(err))
|
||||
response.FailWithMessage("验证失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
// 使用微信公众号服务验证配置
|
||||
wechatMpService := service.ServiceGroupApp.WechatMpService
|
||||
err = wechatMpService.ValidateConfig(config)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("配置连通性测试失败!", zap.Error(err))
|
||||
response.FailWithMessage("配置连通性测试失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithMessage("验证成功", c)
|
||||
}
|
||||
|
||||
// GenerateQrCode 生成公众号二维码
|
||||
// @Tags MpConfig
|
||||
// @Summary 生成公众号二维码
|
||||
// @Description 生成公众号二维码
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {object} response.Response{data=map[string]string} "生成成功"
|
||||
// @Router /wechat/mp/qrcode/generate [post]
|
||||
func (m *MpConfigApi) GenerateQrCode(c *gin.Context) {
|
||||
// 获取当前配置
|
||||
config, err := mpConfigService.GetWechatConfig("mp")
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取微信配置失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取配置失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
// 生成二维码
|
||||
qrCodeUrl, err := mpConfigService.GenerateQrCode(config)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("生成二维码失败!", zap.Error(err))
|
||||
response.FailWithMessage("生成失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithData(map[string]string{"qrCodeUrl": qrCodeUrl}, c)
|
||||
}
|
||||
|
||||
// ClearQuota 清空API配额
|
||||
// @Tags MpConfig
|
||||
// @Summary 清空API配额
|
||||
// @Description 清空微信公众号API配额
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {object} response.Response "清空成功"
|
||||
// @Router /wechat/mp/quota/clear [post]
|
||||
func (m *MpConfigApi) ClearQuota(c *gin.Context) {
|
||||
// 获取当前配置
|
||||
config, err := mpConfigService.GetWechatConfig("mp")
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取微信配置失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取配置失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
// 清空配额
|
||||
err = mpConfigService.ClearQuota(config)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("清空API配额失败!", zap.Error(err))
|
||||
response.FailWithMessage("清空失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithMessage("清空成功", c)
|
||||
}
|
||||
@@ -0,0 +1,226 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
|
||||
wechatResponse "github.com/flipped-aurora/gin-vue-admin/server/plugin/wechat-integration/model/response"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/plugin/wechat-integration/service"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type MpDraftApi struct{}
|
||||
|
||||
var mpDraftService = service.ServiceGroupApp.MpDraftService
|
||||
|
||||
// GetDraftList 获取草稿列表
|
||||
// @Tags MpDraft
|
||||
// @Summary 获取草稿列表
|
||||
// @Description 获取公众号草稿列表
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param page query int false "页码"
|
||||
// @Param pageSize query int false "每页数量"
|
||||
// @Param title query string false "标题"
|
||||
// @Param author query string false "作者"
|
||||
// @Success 200 {object} response.Response{data=response.PageResult} "获取成功"
|
||||
// @Router /wechat/mp/draft [get]
|
||||
func (m *MpDraftApi) GetDraftList(c *gin.Context) {
|
||||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||
pageSize, _ := strconv.Atoi(c.DefaultQuery("pageSize", "10"))
|
||||
title := c.Query("title")
|
||||
author := c.Query("author")
|
||||
|
||||
if page <= 0 {
|
||||
page = 1
|
||||
}
|
||||
if pageSize <= 0 || pageSize > 100 {
|
||||
pageSize = 10
|
||||
}
|
||||
|
||||
draftResponse, err := mpDraftService.GetDraftList(page, pageSize, title, author)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取草稿列表失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithDetailed(wechatResponse.PageResult{
|
||||
List: draftResponse.Items,
|
||||
Total: int64(draftResponse.TotalCount),
|
||||
Page: page,
|
||||
PageSize: pageSize,
|
||||
}, "获取成功", c)
|
||||
}
|
||||
|
||||
// GetDraftInfo 获取草稿详情
|
||||
// @Tags MpDraft
|
||||
// @Summary 获取草稿详情
|
||||
// @Description 获取公众号草稿详情
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path string true "草稿MediaID"
|
||||
// @Success 200 {object} response.Response{data=service.DraftItem} "获取成功"
|
||||
// @Router /wechat/mp/draft/{id} [get]
|
||||
func (m *MpDraftApi) GetDraftInfo(c *gin.Context) {
|
||||
mediaID := c.Param("id")
|
||||
if mediaID == "" {
|
||||
response.FailWithMessage("草稿MediaID不能为空", c)
|
||||
return
|
||||
}
|
||||
|
||||
draft, err := mpDraftService.GetDraftInfo(mediaID)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取草稿详情失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithData(draft, c)
|
||||
}
|
||||
|
||||
// CreateDraft 创建草稿
|
||||
// @Tags MpDraft
|
||||
// @Summary 创建草稿
|
||||
// @Description 创建公众号草稿
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param data body service.CreateDraftRequest true "草稿信息"
|
||||
// @Success 200 {object} response.Response "创建成功"
|
||||
// @Router /wechat/mp/draft [post]
|
||||
func (m *MpDraftApi) CreateDraft(c *gin.Context) {
|
||||
var req service.CreateDraftRequest
|
||||
err := c.ShouldBindJSON(&req)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
mediaID, err := mpDraftService.CreateDraft(&req)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("创建草稿失败!", zap.Error(err))
|
||||
response.FailWithMessage("创建失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithData(map[string]string{"mediaId": mediaID}, c)
|
||||
}
|
||||
|
||||
// UpdateDraft 更新草稿
|
||||
// @Tags MpDraft
|
||||
// @Summary 更新草稿
|
||||
// @Description 更新公众号草稿
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param data body service.UpdateDraftRequest true "草稿信息"
|
||||
// @Success 200 {object} response.Response "更新成功"
|
||||
// @Router /wechat/mp/draft [put]
|
||||
func (m *MpDraftApi) UpdateDraft(c *gin.Context) {
|
||||
var req service.UpdateDraftRequest
|
||||
err := c.ShouldBindJSON(&req)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
err = mpDraftService.UpdateDraft(&req)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("更新草稿失败!", zap.Error(err))
|
||||
response.FailWithMessage("更新失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithMessage("更新成功", c)
|
||||
}
|
||||
|
||||
// DeleteDraft 删除草稿
|
||||
// @Tags MpDraft
|
||||
// @Summary 删除草稿
|
||||
// @Description 删除公众号草稿
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path string true "草稿MediaID"
|
||||
// @Success 200 {object} response.Response "删除成功"
|
||||
// @Router /wechat/mp/draft/{id} [delete]
|
||||
func (m *MpDraftApi) DeleteDraft(c *gin.Context) {
|
||||
mediaID := c.Param("id")
|
||||
if mediaID == "" {
|
||||
response.FailWithMessage("草稿MediaID不能为空", c)
|
||||
return
|
||||
}
|
||||
|
||||
err := mpDraftService.DeleteDraft(mediaID)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("删除草稿失败!", zap.Error(err))
|
||||
response.FailWithMessage("删除失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithMessage("删除成功", c)
|
||||
}
|
||||
|
||||
// PublishDraft 发布草稿
|
||||
// @Tags MpDraft
|
||||
// @Summary 发布草稿
|
||||
// @Description 发布公众号草稿
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path string true "草稿MediaID"
|
||||
// @Success 200 {object} response.Response "发布成功"
|
||||
// @Router /wechat/mp/draft/{id}/publish [post]
|
||||
func (m *MpDraftApi) PublishDraft(c *gin.Context) {
|
||||
mediaID := c.Param("id")
|
||||
if mediaID == "" {
|
||||
response.FailWithMessage("草稿MediaID不能为空", c)
|
||||
return
|
||||
}
|
||||
|
||||
err := mpDraftService.PublishDraft(mediaID)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("发布草稿失败!", zap.Error(err))
|
||||
response.FailWithMessage("发布失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithMessage("发布成功", c)
|
||||
}
|
||||
|
||||
// SyncDrafts 同步草稿
|
||||
// @Tags MpDraft
|
||||
// @Summary 同步草稿
|
||||
// @Description 同步公众号草稿
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {object} response.Response "同步成功"
|
||||
// @Router /wechat/mp/draft/sync [post]
|
||||
func (m *MpDraftApi) SyncDrafts(c *gin.Context) {
|
||||
err := mpDraftService.SyncDrafts()
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("同步草稿失败!", zap.Error(err))
|
||||
response.FailWithMessage("同步失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithMessage("同步成功", c)
|
||||
}
|
||||
|
||||
// GetDraftCount 获取草稿数量统计
|
||||
// @Tags MpDraft
|
||||
// @Summary 获取草稿数量统计
|
||||
// @Description 获取草稿数量统计信息
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {object} response.Response{data=map[string]int64} "获取成功"
|
||||
// @Router /wechat/mp/draft/count [get]
|
||||
func (m *MpDraftApi) GetDraftCount(c *gin.Context) {
|
||||
count, err := mpDraftService.GetDraftCount()
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取草稿数量统计失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithData(count, c)
|
||||
}
|
||||
@@ -0,0 +1,293 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"io"
|
||||
"strconv"
|
||||
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
|
||||
wechatResponse "github.com/flipped-aurora/gin-vue-admin/server/plugin/wechat-integration/model/response"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/plugin/wechat-integration/service"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type MpMaterialApi struct{}
|
||||
|
||||
var mpMaterialService = service.ServiceGroupApp.MpMaterialService
|
||||
|
||||
// GetMaterialList 获取素材列表
|
||||
// @Tags MpMaterial
|
||||
// @Summary 获取素材列表
|
||||
// @Description 获取公众号素材列表
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param page query int false "页码"
|
||||
// @Param pageSize query int false "每页数量"
|
||||
// @Param materialType query string false "素材类型"
|
||||
// @Param permanent query bool false "是否永久素材"
|
||||
// @Success 200 {object} response.Response{data=response.PageResult} "获取成功"
|
||||
// @Router /wechat/mp/material [get]
|
||||
func (m *MpMaterialApi) GetMaterialList(c *gin.Context) {
|
||||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||
pageSize, _ := strconv.Atoi(c.DefaultQuery("pageSize", "10"))
|
||||
materialType := c.Query("materialType")
|
||||
|
||||
var permanent *bool
|
||||
if p := c.Query("permanent"); p != "" {
|
||||
if perm, err := strconv.ParseBool(p); err == nil {
|
||||
permanent = &perm
|
||||
}
|
||||
}
|
||||
|
||||
if page <= 0 {
|
||||
page = 1
|
||||
}
|
||||
if pageSize <= 0 || pageSize > 100 {
|
||||
pageSize = 10
|
||||
}
|
||||
|
||||
materials, total, err := mpMaterialService.GetMaterialList(page, pageSize, materialType, permanent)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取素材列表失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithDetailed(wechatResponse.PageResult{
|
||||
List: materials,
|
||||
Total: total,
|
||||
Page: page,
|
||||
PageSize: pageSize,
|
||||
}, "获取成功", c)
|
||||
}
|
||||
|
||||
// GetMaterialByID 获取素材详情
|
||||
// @Tags MpMaterial
|
||||
// @Summary 获取素材详情
|
||||
// @Description 根据ID获取素材详细信息
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path int true "素材ID"
|
||||
// @Success 200 {object} response.Response{data=model.MpMaterial} "获取成功"
|
||||
// @Router /wechat/mp/material/{id} [get]
|
||||
func (m *MpMaterialApi) GetMaterialByID(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 32)
|
||||
if err != nil {
|
||||
response.FailWithMessage("无效的素材ID", c)
|
||||
return
|
||||
}
|
||||
|
||||
material, err := mpMaterialService.GetMaterialByID(uint(id))
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取素材详情失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithData(material, c)
|
||||
}
|
||||
|
||||
// UploadTempMaterial 上传临时素材
|
||||
// @Tags MpMaterial
|
||||
// @Summary 上传临时素材
|
||||
// @Description 上传临时素材到微信服务器
|
||||
// @Accept multipart/form-data
|
||||
// @Produce json
|
||||
// @Param file formData file true "素材文件"
|
||||
// @Param type formData string true "素材类型:image、voice、video、thumb"
|
||||
// @Success 200 {object} response.Response{data=model.MpMaterial} "上传成功"
|
||||
// @Router /wechat/mp/material/temp [post]
|
||||
func (m *MpMaterialApi) UploadTempMaterial(c *gin.Context) {
|
||||
// 获取上传的文件
|
||||
file, header, err := c.Request.FormFile("file")
|
||||
if err != nil {
|
||||
response.FailWithMessage("获取文件失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
materialType := c.PostForm("type")
|
||||
if materialType == "" {
|
||||
response.FailWithMessage("素材类型不能为空", c)
|
||||
return
|
||||
}
|
||||
|
||||
// 验证素材类型
|
||||
validTypes := map[string]bool{
|
||||
"image": true,
|
||||
"voice": true,
|
||||
"video": true,
|
||||
"thumb": true,
|
||||
}
|
||||
if !validTypes[materialType] {
|
||||
response.FailWithMessage("无效的素材类型", c)
|
||||
return
|
||||
}
|
||||
|
||||
material, err := mpMaterialService.UploadTempMaterial(file, header.Filename, materialType)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("上传临时素材失败!", zap.Error(err))
|
||||
response.FailWithMessage("上传失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithDetailed(material, "上传成功", c)
|
||||
}
|
||||
|
||||
// UploadPermanentMaterial 上传永久素材
|
||||
// @Tags MpMaterial
|
||||
// @Summary 上传永久素材
|
||||
// @Description 上传永久素材到微信服务器
|
||||
// @Accept multipart/form-data
|
||||
// @Produce json
|
||||
// @Param file formData file true "素材文件"
|
||||
// @Param type formData string true "素材类型:image、voice、video、thumb"
|
||||
// @Success 200 {object} response.Response{data=model.MpMaterial} "上传成功"
|
||||
// @Router /wechat/mp/material/permanent [post]
|
||||
func (m *MpMaterialApi) UploadPermanentMaterial(c *gin.Context) {
|
||||
// 获取上传的文件
|
||||
file, header, err := c.Request.FormFile("file")
|
||||
if err != nil {
|
||||
response.FailWithMessage("获取文件失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
materialType := c.PostForm("type")
|
||||
if materialType == "" {
|
||||
response.FailWithMessage("素材类型不能为空", c)
|
||||
return
|
||||
}
|
||||
|
||||
// 验证素材类型
|
||||
validTypes := map[string]bool{
|
||||
"image": true,
|
||||
"voice": true,
|
||||
"video": true,
|
||||
"thumb": true,
|
||||
}
|
||||
if !validTypes[materialType] {
|
||||
response.FailWithMessage("无效的素材类型", c)
|
||||
return
|
||||
}
|
||||
|
||||
material, err := mpMaterialService.UploadPermanentMaterial(file, header.Filename, materialType)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("上传永久素材失败!", zap.Error(err))
|
||||
response.FailWithMessage("上传失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithDetailed(material, "上传成功", c)
|
||||
}
|
||||
|
||||
// DeleteMaterial 删除素材
|
||||
// @Tags MpMaterial
|
||||
// @Summary 删除素材
|
||||
// @Description 删除指定的素材
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path int true "素材ID"
|
||||
// @Success 200 {object} response.Response "删除成功"
|
||||
// @Router /wechat/mp/material/{id} [delete]
|
||||
func (m *MpMaterialApi) DeleteMaterial(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 32)
|
||||
if err != nil {
|
||||
response.FailWithMessage("无效的素材ID", c)
|
||||
return
|
||||
}
|
||||
|
||||
err = mpMaterialService.DeleteMaterial(uint(id))
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("删除素材失败!", zap.Error(err))
|
||||
response.FailWithMessage("删除失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithMessage("删除成功", c)
|
||||
}
|
||||
|
||||
// GetMaterialByMediaID 根据MediaID获取素材
|
||||
// @Tags MpMaterial
|
||||
// @Summary 根据MediaID获取素材
|
||||
// @Description 根据微信MediaID获取素材信息
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param mediaId query string true "微信MediaID"
|
||||
// @Success 200 {object} response.Response{data=model.MpMaterial} "获取成功"
|
||||
// @Router /wechat/mp/material/media [get]
|
||||
func (m *MpMaterialApi) GetMaterialByMediaID(c *gin.Context) {
|
||||
mediaID := c.Query("mediaId")
|
||||
if mediaID == "" {
|
||||
response.FailWithMessage("MediaID不能为空", c)
|
||||
return
|
||||
}
|
||||
|
||||
material, err := mpMaterialService.GetMaterialByMediaID(mediaID)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取素材失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithData(material, c)
|
||||
}
|
||||
|
||||
// DownloadMaterial 下载素材
|
||||
// @Tags MpMaterial
|
||||
// @Summary 下载素材
|
||||
// @Description 从微信服务器下载素材
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param mediaId query string true "素材MediaID"
|
||||
// @Success 200 {object} response.Response "下载成功"
|
||||
// @Router /wechat/mp/material/download [get]
|
||||
func (m *MpMaterialApi) DownloadMaterial(c *gin.Context) {
|
||||
mediaID := c.Query("mediaId")
|
||||
if mediaID == "" {
|
||||
response.FailWithMessage("MediaID不能为空", c)
|
||||
return
|
||||
}
|
||||
|
||||
reader, filename, err := mpMaterialService.DownloadMaterial(mediaID)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("下载素材失败!", zap.Error(err))
|
||||
response.FailWithMessage("下载失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
defer reader.Close()
|
||||
|
||||
// 设置响应头
|
||||
c.Header("Content-Disposition", "attachment; filename="+filename)
|
||||
c.Header("Content-Type", "application/octet-stream")
|
||||
|
||||
// 将文件内容写入响应
|
||||
_, err = io.Copy(c.Writer, reader)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("写入响应失败!", zap.Error(err))
|
||||
response.FailWithMessage("下载失败", c)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// SyncMaterials 同步素材
|
||||
// @Tags MpMaterial
|
||||
// @Summary 同步素材
|
||||
// @Description 从微信服务器同步素材列表
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {object} response.Response "同步成功"
|
||||
// @Router /wechat/mp/material/sync [post]
|
||||
func (m *MpMaterialApi) SyncMaterials(c *gin.Context) {
|
||||
err := mpMaterialService.SyncMaterialsFromWechat()
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("同步素材失败!", zap.Error(err))
|
||||
response.FailWithMessage("同步失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithMessage("同步成功", c)
|
||||
}
|
||||
@@ -0,0 +1,247 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/plugin/wechat-integration/model"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/plugin/wechat-integration/model/request"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/plugin/wechat-integration/service"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type MpMenuApi struct{}
|
||||
|
||||
var mpMenuService = service.ServiceGroupApp.MpMenuService
|
||||
|
||||
// GetMenuList 获取菜单列表
|
||||
// @Tags MpMenu
|
||||
// @Summary 获取菜单列表
|
||||
// @Description 获取公众号菜单配置列表
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {object} response.Response{data=[]model.MpMenu} "获取成功"
|
||||
// @Router /wechat/mp/menu [get]
|
||||
func (m *MpMenuApi) GetMenuList(c *gin.Context) {
|
||||
menus, err := mpMenuService.GetMenuList()
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取菜单列表失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithDetailed(menus, "获取成功", c)
|
||||
}
|
||||
|
||||
// GetMenuByID 获取菜单详情
|
||||
// @Tags MpMenu
|
||||
// @Summary 获取菜单详情
|
||||
// @Description 根据ID获取菜单详细信息
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path int true "菜单ID"
|
||||
// @Success 200 {object} response.Response{data=model.MpMenu} "获取成功"
|
||||
// @Router /wechat/mp/menu/{id} [get]
|
||||
func (m *MpMenuApi) GetMenuByID(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 32)
|
||||
if err != nil {
|
||||
response.FailWithMessage("无效的菜单ID", c)
|
||||
return
|
||||
}
|
||||
|
||||
menu, err := mpMenuService.GetMenuByID(uint(id))
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取菜单详情失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithData(menu, c)
|
||||
}
|
||||
|
||||
// CreateMenu 创建菜单
|
||||
// @Tags MpMenu
|
||||
// @Summary 创建菜单
|
||||
// @Description 创建新的菜单项
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param data body request.CreateMenuRequest true "创建参数"
|
||||
// @Success 200 {object} response.Response{data=model.MpMenu} "创建成功"
|
||||
// @Router /wechat/mp/menu [post]
|
||||
func (m *MpMenuApi) CreateMenu(c *gin.Context) {
|
||||
var req request.CreateMenuRequest
|
||||
err := c.ShouldBindJSON(&req)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
// 验证必填字段
|
||||
if req.Name == "" || req.Type == "" {
|
||||
response.FailWithMessage("菜单名称和类型不能为空", c)
|
||||
return
|
||||
}
|
||||
|
||||
// 构建菜单对象
|
||||
menu := &model.MpMenu{
|
||||
Name: &req.Name,
|
||||
Type: req.Type,
|
||||
}
|
||||
|
||||
// 设置可选字段
|
||||
if req.ParentID != "" {
|
||||
menu.ParentID = &req.ParentID
|
||||
}
|
||||
if req.MenuKey != "" {
|
||||
menu.MenuKey = &req.MenuKey
|
||||
}
|
||||
if req.URL != "" {
|
||||
menu.URL = &req.URL
|
||||
}
|
||||
if req.MiniProgramAppID != "" {
|
||||
menu.MiniProgramAppID = &req.MiniProgramAppID
|
||||
}
|
||||
if req.MiniProgramPagePath != "" {
|
||||
menu.MiniProgramPagePath = &req.MiniProgramPagePath
|
||||
}
|
||||
if req.ReplyMessageType != "" {
|
||||
menu.ReplyMessageType = &req.ReplyMessageType
|
||||
}
|
||||
if req.ReplyContent != "" {
|
||||
menu.ReplyContent = &req.ReplyContent
|
||||
}
|
||||
|
||||
err = mpMenuService.CreateMenu(menu)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("创建菜单失败!", zap.Error(err))
|
||||
response.FailWithMessage("创建失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithDetailed(menu, "创建成功", c)
|
||||
}
|
||||
|
||||
// UpdateMenu 更新菜单
|
||||
// @Tags MpMenu
|
||||
// @Summary 更新菜单
|
||||
// @Description 更新菜单项
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param data body request.UpdateMenuRequest true "更新参数"
|
||||
// @Success 200 {object} response.Response "更新成功"
|
||||
// @Router /wechat/mp/menu [put]
|
||||
func (m *MpMenuApi) UpdateMenu(c *gin.Context) {
|
||||
var req request.UpdateMenuRequest
|
||||
err := c.ShouldBindJSON(&req)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
if req.ID == 0 {
|
||||
response.FailWithMessage("ID不能为空", c)
|
||||
return
|
||||
}
|
||||
|
||||
// 获取现有记录
|
||||
existingMenu, err := mpMenuService.GetMenuByID(req.ID)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取菜单失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
// 更新字段
|
||||
if req.Name != "" {
|
||||
existingMenu.Name = &req.Name
|
||||
}
|
||||
if req.Type != "" {
|
||||
existingMenu.Type = req.Type
|
||||
}
|
||||
if req.MenuKey != "" {
|
||||
existingMenu.MenuKey = &req.MenuKey
|
||||
}
|
||||
if req.URL != "" {
|
||||
existingMenu.URL = &req.URL
|
||||
}
|
||||
if req.ReplyContent != "" {
|
||||
existingMenu.ReplyContent = &req.ReplyContent
|
||||
}
|
||||
|
||||
err = mpMenuService.UpdateMenu(existingMenu)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("更新菜单失败!", zap.Error(err))
|
||||
response.FailWithMessage("更新失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithMessage("更新成功", c)
|
||||
}
|
||||
|
||||
// DeleteMenu 删除菜单
|
||||
// @Tags MpMenu
|
||||
// @Summary 删除菜单
|
||||
// @Description 删除指定的菜单项
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path int true "菜单ID"
|
||||
// @Success 200 {object} response.Response "删除成功"
|
||||
// @Router /wechat/mp/menu/{id} [delete]
|
||||
func (m *MpMenuApi) DeleteMenu(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 32)
|
||||
if err != nil {
|
||||
response.FailWithMessage("无效的菜单ID", c)
|
||||
return
|
||||
}
|
||||
|
||||
err = mpMenuService.DeleteMenu(uint(id))
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("删除菜单失败!", zap.Error(err))
|
||||
response.FailWithMessage("删除失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithMessage("删除成功", c)
|
||||
}
|
||||
|
||||
// PublishMenu 发布菜单
|
||||
// @Tags MpMenu
|
||||
// @Summary 发布菜单
|
||||
// @Description 将菜单配置发布到微信服务器
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {object} response.Response "发布成功"
|
||||
// @Router /wechat/mp/menu/publish [post]
|
||||
func (m *MpMenuApi) PublishMenu(c *gin.Context) {
|
||||
err := mpMenuService.PublishMenu()
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("发布菜单失败!", zap.Error(err))
|
||||
response.FailWithMessage("发布失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithMessage("发布成功", c)
|
||||
}
|
||||
|
||||
// DeleteWechatMenu 删除微信菜单
|
||||
// @Tags MpMenu
|
||||
// @Summary 删除微信菜单
|
||||
// @Description 删除微信服务器上的菜单
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {object} response.Response "删除成功"
|
||||
// @Router /wechat/mp/menu/wechat [delete]
|
||||
func (m *MpMenuApi) DeleteWechatMenu(c *gin.Context) {
|
||||
err := mpMenuService.DeleteWechatMenu()
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("删除微信菜单失败!", zap.Error(err))
|
||||
response.FailWithMessage("删除失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithMessage("删除成功", c)
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/plugin/wechat-integration/model/request"
|
||||
wechatResponse "github.com/flipped-aurora/gin-vue-admin/server/plugin/wechat-integration/model/response"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/plugin/wechat-integration/service"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type MpMessageApi struct{}
|
||||
|
||||
var mpMessageService = service.ServiceGroupApp.MpMessageService
|
||||
|
||||
// GetMessageList 获取消息列表
|
||||
// @Tags MpMessage
|
||||
// @Summary 获取消息列表
|
||||
// @Description 获取公众号消息记录列表
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param page query int false "页码"
|
||||
// @Param pageSize query int false "每页数量"
|
||||
// @Param msgType query string false "消息类型"
|
||||
// @Param openid query string false "用户openid"
|
||||
// @Success 200 {object} response.Response{data=response.PageResult} "获取成功"
|
||||
// @Router /wechat/mp/messages [get]
|
||||
func (m *MpMessageApi) GetMessageList(c *gin.Context) {
|
||||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||
pageSize, _ := strconv.Atoi(c.DefaultQuery("pageSize", "10"))
|
||||
msgType := c.Query("msgType")
|
||||
openid := c.Query("openid")
|
||||
|
||||
if page <= 0 {
|
||||
page = 1
|
||||
}
|
||||
if pageSize <= 0 || pageSize > 100 {
|
||||
pageSize = 10
|
||||
}
|
||||
|
||||
messages, total, err := mpMessageService.GetMessageList(page, pageSize, msgType, openid)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取消息列表失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithDetailed(wechatResponse.PageResult{
|
||||
List: messages,
|
||||
Total: total,
|
||||
Page: page,
|
||||
PageSize: pageSize,
|
||||
}, "获取成功", c)
|
||||
}
|
||||
|
||||
// SendTextMessage 发送文本消息
|
||||
// @Tags MpMessage
|
||||
// @Summary 发送文本消息
|
||||
// @Description 向指定用户发送文本消息
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param data body request.SendTextMessageRequest true "发送参数"
|
||||
// @Success 200 {object} response.Response "发送成功"
|
||||
// @Router /wechat/mp/message/text [post]
|
||||
func (m *MpMessageApi) SendTextMessage(c *gin.Context) {
|
||||
var req request.SendTextMessageRequest
|
||||
err := c.ShouldBindJSON(&req)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
if req.OpenID == "" || req.Content == "" {
|
||||
response.FailWithMessage("openid和消息内容不能为空", c)
|
||||
return
|
||||
}
|
||||
|
||||
err = mpMessageService.SendTextMessage(req.OpenID, req.Content)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("发送文本消息失败!", zap.Error(err))
|
||||
response.FailWithMessage("发送失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithMessage("发送成功", c)
|
||||
}
|
||||
|
||||
// SendImageMessage 发送图片消息
|
||||
// @Tags MpMessage
|
||||
// @Summary 发送图片消息
|
||||
// @Description 向指定用户发送图片消息
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param data body request.SendImageMessageRequest true "发送参数"
|
||||
// @Success 200 {object} response.Response "发送成功"
|
||||
// @Router /wechat/mp/message/image [post]
|
||||
func (m *MpMessageApi) SendImageMessage(c *gin.Context) {
|
||||
var req request.SendImageMessageRequest
|
||||
err := c.ShouldBindJSON(&req)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
if req.OpenID == "" || req.MediaID == "" {
|
||||
response.FailWithMessage("openid和媒体ID不能为空", c)
|
||||
return
|
||||
}
|
||||
|
||||
err = mpMessageService.SendImageMessage(req.OpenID, req.MediaID)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("发送图片消息失败!", zap.Error(err))
|
||||
response.FailWithMessage("发送失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithMessage("发送成功", c)
|
||||
}
|
||||
|
||||
// GetMessageByID 获取消息详情
|
||||
// @Tags MpMessage
|
||||
// @Summary 获取消息详情
|
||||
// @Description 根据ID获取消息详细信息
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path int true "消息ID"
|
||||
// @Success 200 {object} response.Response{data=model.MpMessage} "获取成功"
|
||||
// @Router /wechat/mp/message/{id} [get]
|
||||
func (m *MpMessageApi) GetMessageByID(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 32)
|
||||
if err != nil {
|
||||
response.FailWithMessage("无效的消息ID", c)
|
||||
return
|
||||
}
|
||||
|
||||
message, err := mpMessageService.GetMessageByID(uint(id))
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取消息详情失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithData(message, c)
|
||||
}
|
||||
|
||||
// DeleteMessage 删除消息
|
||||
// @Tags MpMessage
|
||||
// @Summary 删除消息
|
||||
// @Description 删除指定的消息记录
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path int true "消息ID"
|
||||
// @Success 200 {object} response.Response "删除成功"
|
||||
// @Router /wechat/mp/message/{id} [delete]
|
||||
func (m *MpMessageApi) DeleteMessage(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 32)
|
||||
if err != nil {
|
||||
response.FailWithMessage("无效的消息ID", c)
|
||||
return
|
||||
}
|
||||
|
||||
err = mpMessageService.DeleteMessage(uint(id))
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("删除消息失败!", zap.Error(err))
|
||||
response.FailWithMessage("删除失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithMessage("删除成功", c)
|
||||
}
|
||||
@@ -0,0 +1,259 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/plugin/wechat-integration/model"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/plugin/wechat-integration/model/request"
|
||||
wechatResponse "github.com/flipped-aurora/gin-vue-admin/server/plugin/wechat-integration/model/response"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/plugin/wechat-integration/service"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type MpNewsApi struct{}
|
||||
|
||||
var mpNewsService = service.ServiceGroupApp.MpNewsService
|
||||
|
||||
// GetNewsList 获取图文发表记录列表
|
||||
// @Tags MpNews
|
||||
// @Summary 获取图文发表记录列表
|
||||
// @Description 获取公众号图文发表记录列表
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param page query int false "页码"
|
||||
// @Param pageSize query int false "每页数量"
|
||||
// @Param status query string false "发布状态"
|
||||
// @Success 200 {object} response.Response{data=response.PageResult} "获取成功"
|
||||
// @Router /wechat/mp/news [get]
|
||||
func (m *MpNewsApi) GetNewsList(c *gin.Context) {
|
||||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||
pageSize, _ := strconv.Atoi(c.DefaultQuery("pageSize", "10"))
|
||||
status := c.Query("status")
|
||||
|
||||
if page <= 0 {
|
||||
page = 1
|
||||
}
|
||||
if pageSize <= 0 || pageSize > 100 {
|
||||
pageSize = 10
|
||||
}
|
||||
|
||||
newsList, total, err := mpNewsService.GetNewsList(page, pageSize, status)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取图文发表记录列表失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithDetailed(wechatResponse.PageResult{
|
||||
List: newsList,
|
||||
Total: total,
|
||||
Page: page,
|
||||
PageSize: pageSize,
|
||||
}, "获取成功", c)
|
||||
}
|
||||
|
||||
// GetNewsInfo 获取图文详情
|
||||
// @Tags MpNews
|
||||
// @Summary 获取图文详情
|
||||
// @Description 获取公众号图文详情
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path int true "图文ID"
|
||||
// @Success 200 {object} response.Response{data=model.MpNews} "获取成功"
|
||||
// @Router /wechat/mp/news/{id} [get]
|
||||
func (m *MpNewsApi) GetNewsInfo(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 32)
|
||||
if err != nil {
|
||||
response.FailWithMessage("无效的图文ID", c)
|
||||
return
|
||||
}
|
||||
|
||||
news, err := mpNewsService.GetNewsInfo(uint(id))
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取图文详情失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithData(news, c)
|
||||
}
|
||||
|
||||
// CreateNews 创建图文记录
|
||||
// @Tags MpNews
|
||||
// @Summary 创建图文记录
|
||||
// @Description 创建公众号图文记录
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param data body request.CreateMpNewsRequest true "图文信息"
|
||||
// @Success 200 {object} response.Response "创建成功"
|
||||
// @Router /wechat/mp/news [post]
|
||||
func (m *MpNewsApi) CreateNews(c *gin.Context) {
|
||||
var req request.CreateMpNewsRequest
|
||||
err := c.ShouldBindJSON(&req)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
// 将DTO转换为数据库实体
|
||||
news := &model.MpNews{
|
||||
MediaID: req.MediaID,
|
||||
Title: req.Title,
|
||||
Author: req.Author,
|
||||
Digest: req.Digest,
|
||||
Content: req.Content,
|
||||
ContentURL: req.ContentURL,
|
||||
SourceURL: req.SourceURL,
|
||||
ThumbMediaID: req.ThumbMediaID,
|
||||
ThumbURL: req.ThumbURL,
|
||||
ShowCover: req.ShowCover,
|
||||
NeedOpenComment: req.NeedOpenComment,
|
||||
OnlyFansCanComment: req.OnlyFansCanComment,
|
||||
PublishStatus: req.PublishStatus,
|
||||
WechatURL: req.WechatURL,
|
||||
WechatMsgID: req.WechatMsgID,
|
||||
}
|
||||
|
||||
err = mpNewsService.CreateNews(news)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("创建图文记录失败!", zap.Error(err))
|
||||
response.FailWithMessage("创建失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithData(news, c)
|
||||
}
|
||||
|
||||
// UpdateNews 更新图文记录
|
||||
// @Tags MpNews
|
||||
// @Summary 更新图文记录
|
||||
// @Description 更新公众号图文记录
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param data body request.UpdateMpNewsRequest true "图文信息"
|
||||
// @Success 200 {object} response.Response "更新成功"
|
||||
// @Router /wechat/mp/news [put]
|
||||
func (m *MpNewsApi) UpdateNews(c *gin.Context) {
|
||||
var req request.UpdateMpNewsRequest
|
||||
err := c.ShouldBindJSON(&req)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
if req.ID == 0 {
|
||||
response.FailWithMessage("ID不能为空", c)
|
||||
return
|
||||
}
|
||||
|
||||
// 获取现有记录以保留时间字段
|
||||
existingNews, err := mpNewsService.GetNewsInfo(req.ID)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取图文记录失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
// 更新字段,保留原有的时间字段
|
||||
existingNews.MediaID = req.MediaID
|
||||
existingNews.Title = req.Title
|
||||
existingNews.Author = req.Author
|
||||
existingNews.Digest = req.Digest
|
||||
existingNews.Content = req.Content
|
||||
existingNews.ContentURL = req.ContentURL
|
||||
existingNews.SourceURL = req.SourceURL
|
||||
existingNews.ThumbMediaID = req.ThumbMediaID
|
||||
existingNews.ThumbURL = req.ThumbURL
|
||||
existingNews.ShowCover = req.ShowCover
|
||||
existingNews.NeedOpenComment = req.NeedOpenComment
|
||||
existingNews.OnlyFansCanComment = req.OnlyFansCanComment
|
||||
existingNews.PublishStatus = req.PublishStatus
|
||||
existingNews.WechatURL = req.WechatURL
|
||||
existingNews.WechatMsgID = req.WechatMsgID
|
||||
|
||||
err = mpNewsService.UpdateNews(existingNews)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("更新图文记录失败!", zap.Error(err))
|
||||
response.FailWithMessage("更新失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithMessage("更新成功", c)
|
||||
}
|
||||
|
||||
// DeleteNews 删除图文记录
|
||||
// @Tags MpNews
|
||||
// @Summary 删除图文记录
|
||||
// @Description 删除公众号图文记录
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path int true "图文ID"
|
||||
// @Success 200 {object} response.Response "删除成功"
|
||||
// @Router /wechat/mp/news/{id} [delete]
|
||||
func (m *MpNewsApi) DeleteNews(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 32)
|
||||
if err != nil {
|
||||
response.FailWithMessage("无效的图文ID", c)
|
||||
return
|
||||
}
|
||||
|
||||
err = mpNewsService.DeleteNews(uint(id))
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("删除图文记录失败!", zap.Error(err))
|
||||
response.FailWithMessage("删除失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithMessage("删除成功", c)
|
||||
}
|
||||
|
||||
// GetNewsStatistics 获取图文数据统计
|
||||
// @Tags MpNews
|
||||
// @Summary 获取图文数据统计
|
||||
// @Description 获取公众号图文数据统计
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path int true "图文ID"
|
||||
// @Success 200 {object} response.Response{data=map[string]interface{}} "获取成功"
|
||||
// @Router /wechat/mp/news/{id}/statistics [get]
|
||||
func (m *MpNewsApi) GetNewsStatistics(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 32)
|
||||
if err != nil {
|
||||
response.FailWithMessage("无效的图文ID", c)
|
||||
return
|
||||
}
|
||||
|
||||
statistics, err := mpNewsService.GetNewsStatistics(uint(id))
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取图文数据统计失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithData(statistics, c)
|
||||
}
|
||||
|
||||
// SyncNews 同步图文发表记录
|
||||
// @Tags MpNews
|
||||
// @Summary 同步图文发表记录
|
||||
// @Description 同步公众号图文发表记录
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {object} response.Response "同步成功"
|
||||
// @Router /wechat/mp/news/sync [post]
|
||||
func (m *MpNewsApi) SyncNews(c *gin.Context) {
|
||||
err := mpNewsService.SyncNews()
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("同步图文发表记录失败!", zap.Error(err))
|
||||
response.FailWithMessage("同步失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithMessage("同步成功", c)
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type MpStatisticsApi struct{}
|
||||
|
||||
// GetUserSummary 获得粉丝增减数据
|
||||
// @Tags MpStatistics
|
||||
// @Summary 获得粉丝增减数据
|
||||
// @Description 获得粉丝增减数据
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param accountId query int true "公众号账号ID"
|
||||
// @Param date query string true "日期"
|
||||
// @Success 200 {object} response.Response "获取成功"
|
||||
// @Router /wechat/mp/statistics/user-summary [get]
|
||||
func (m *MpStatisticsApi) GetUserSummary(c *gin.Context) {
|
||||
accountId := c.Query("accountId")
|
||||
date := c.Query("date")
|
||||
|
||||
// TODO: 实现粉丝增减数据获取逻辑
|
||||
global.GVA_LOG.Info("获取粉丝增减数据", zap.String("accountId", accountId), zap.String("date", date))
|
||||
|
||||
// 模拟数据
|
||||
data := []map[string]interface{}{
|
||||
{
|
||||
"refDate": "2025-08-04",
|
||||
"userSource": 0,
|
||||
"newUser": 12,
|
||||
"cancelUser": 3,
|
||||
"cumulateUser": 1350,
|
||||
},
|
||||
}
|
||||
|
||||
response.OkWithData(data, c)
|
||||
}
|
||||
|
||||
// GetUserCumulate 获得粉丝累计数据
|
||||
// @Tags MpStatistics
|
||||
// @Summary 获得粉丝累计数据
|
||||
// @Description 获得粉丝累计数据
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param accountId query int true "公众号账号ID"
|
||||
// @Param date query string true "日期"
|
||||
// @Success 200 {object} response.Response "获取成功"
|
||||
// @Router /wechat/mp/statistics/user-cumulate [get]
|
||||
func (m *MpStatisticsApi) GetUserCumulate(c *gin.Context) {
|
||||
accountId := c.Query("accountId")
|
||||
date := c.Query("date")
|
||||
|
||||
// TODO: 实现粉丝累计数据获取逻辑
|
||||
global.GVA_LOG.Info("获取粉丝累计数据", zap.String("accountId", accountId), zap.String("date", date))
|
||||
|
||||
// 模拟数据
|
||||
data := []map[string]interface{}{
|
||||
{
|
||||
"refDate": "2025-08-04",
|
||||
"cumulateUser": 1350,
|
||||
},
|
||||
}
|
||||
|
||||
response.OkWithData(data, c)
|
||||
}
|
||||
|
||||
// GetUpstreamMessage 获取消息发送概况数据
|
||||
// @Tags MpStatistics
|
||||
// @Summary 获取消息发送概况数据
|
||||
// @Description 获取消息发送概况数据
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param accountId query int true "公众号账号ID"
|
||||
// @Param date query string true "日期"
|
||||
// @Success 200 {object} response.Response "获取成功"
|
||||
// @Router /wechat/mp/statistics/upstream-message [get]
|
||||
func (m *MpStatisticsApi) GetUpstreamMessage(c *gin.Context) {
|
||||
accountId := c.Query("accountId")
|
||||
date := c.Query("date")
|
||||
|
||||
// TODO: 实现消息发送概况数据获取逻辑
|
||||
global.GVA_LOG.Info("获取消息发送概况数据", zap.String("accountId", accountId), zap.String("date", date))
|
||||
|
||||
// 模拟数据
|
||||
data := []map[string]interface{}{
|
||||
{
|
||||
"refDate": "2025-08-04",
|
||||
"msgType": 1,
|
||||
"msgUser": 45,
|
||||
"msgCount": 128,
|
||||
},
|
||||
}
|
||||
|
||||
response.OkWithData(data, c)
|
||||
}
|
||||
|
||||
// GetInterfaceSummary 获取接口调用概况数据
|
||||
// @Tags MpStatistics
|
||||
// @Summary 获取接口调用概况数据
|
||||
// @Description 获取接口调用概况数据
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param accountId query int true "公众号账号ID"
|
||||
// @Param date query string true "日期"
|
||||
// @Success 200 {object} response.Response "获取成功"
|
||||
// @Router /wechat/mp/statistics/interface-summary [get]
|
||||
func (m *MpStatisticsApi) GetInterfaceSummary(c *gin.Context) {
|
||||
accountId := c.Query("accountId")
|
||||
date := c.Query("date")
|
||||
|
||||
// TODO: 实现接口调用概况数据获取逻辑
|
||||
global.GVA_LOG.Info("获取接口调用概况数据", zap.String("accountId", accountId), zap.String("date", date))
|
||||
|
||||
// 模拟数据
|
||||
data := []map[string]interface{}{
|
||||
{
|
||||
"refDate": "2025-08-04",
|
||||
"callbackCount": 256,
|
||||
"failCount": 2,
|
||||
"totalTimeCost": 1280,
|
||||
"maxTimeCost": 45,
|
||||
},
|
||||
}
|
||||
|
||||
response.OkWithData(data, c)
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
|
||||
wechatResponse "github.com/flipped-aurora/gin-vue-admin/server/plugin/wechat-integration/model/response"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type MpTagApi struct{}
|
||||
|
||||
// CreateTag 创建公众号标签
|
||||
// @Tags MpTag
|
||||
// @Summary 创建公众号标签
|
||||
// @Description 创建公众号标签
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param data body map[string]interface{} true "标签信息"
|
||||
// @Success 200 {object} response.Response "创建成功"
|
||||
// @Router /wechat/mp/tag/create [post]
|
||||
func (m *MpTagApi) CreateTag(c *gin.Context) {
|
||||
var req map[string]interface{}
|
||||
err := c.ShouldBindJSON(&req)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: 实现标签创建逻辑
|
||||
global.GVA_LOG.Info("创建标签请求", zap.Any("data", req))
|
||||
response.OkWithMessage("标签创建功能待实现", c)
|
||||
}
|
||||
|
||||
// UpdateTag 更新公众号标签
|
||||
// @Tags MpTag
|
||||
// @Summary 更新公众号标签
|
||||
// @Description 更新公众号标签
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param data body map[string]interface{} true "标签信息"
|
||||
// @Success 200 {object} response.Response "更新成功"
|
||||
// @Router /wechat/mp/tag/update [put]
|
||||
func (m *MpTagApi) UpdateTag(c *gin.Context) {
|
||||
var req map[string]interface{}
|
||||
err := c.ShouldBindJSON(&req)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: 实现标签更新逻辑
|
||||
global.GVA_LOG.Info("更新标签请求", zap.Any("data", req))
|
||||
response.OkWithMessage("标签更新功能待实现", c)
|
||||
}
|
||||
|
||||
// DeleteTag 删除公众号标签
|
||||
// @Tags MpTag
|
||||
// @Summary 删除公众号标签
|
||||
// @Description 删除公众号标签
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id query int true "标签ID"
|
||||
// @Success 200 {object} response.Response "删除成功"
|
||||
// @Router /wechat/mp/tag/delete [delete]
|
||||
func (m *MpTagApi) DeleteTag(c *gin.Context) {
|
||||
idStr := c.Query("id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 32)
|
||||
if err != nil {
|
||||
response.FailWithMessage("无效的标签ID", c)
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: 实现标签删除逻辑
|
||||
global.GVA_LOG.Info("删除标签请求", zap.Uint64("id", id))
|
||||
response.OkWithMessage("标签删除功能待实现", c)
|
||||
}
|
||||
|
||||
// GetTag 获取公众号标签详情
|
||||
// @Tags MpTag
|
||||
// @Summary 获取公众号标签详情
|
||||
// @Description 获取公众号标签详情
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id query int true "标签ID"
|
||||
// @Success 200 {object} response.Response "获取成功"
|
||||
// @Router /wechat/mp/tag/get [get]
|
||||
func (m *MpTagApi) GetTag(c *gin.Context) {
|
||||
idStr := c.Query("id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 32)
|
||||
if err != nil {
|
||||
response.FailWithMessage("无效的标签ID", c)
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: 实现获取标签详情逻辑
|
||||
global.GVA_LOG.Info("获取标签详情请求", zap.Uint64("id", id))
|
||||
response.OkWithMessage("获取标签详情功能待实现", c)
|
||||
}
|
||||
|
||||
// GetTagPage 获取公众号标签分页
|
||||
// @Tags MpTag
|
||||
// @Summary 获取公众号标签分页
|
||||
// @Description 获取公众号标签分页
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param page query int false "页码"
|
||||
// @Param pageSize query int false "每页数量"
|
||||
// @Success 200 {object} response.Response{data=response.PageResult} "获取成功"
|
||||
// @Router /wechat/mp/tag/page [get]
|
||||
func (m *MpTagApi) GetTagPage(c *gin.Context) {
|
||||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||
pageSize, _ := strconv.Atoi(c.DefaultQuery("pageSize", "10"))
|
||||
|
||||
if page <= 0 {
|
||||
page = 1
|
||||
}
|
||||
if pageSize <= 0 || pageSize > 100 {
|
||||
pageSize = 10
|
||||
}
|
||||
|
||||
// 模拟数据
|
||||
tags := []map[string]interface{}{
|
||||
{
|
||||
"id": 1,
|
||||
"name": "VIP用户",
|
||||
"count": 150,
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"name": "普通用户",
|
||||
"count": 1200,
|
||||
},
|
||||
}
|
||||
|
||||
response.OkWithDetailed(wechatResponse.PageResult{
|
||||
List: tags,
|
||||
Total: int64(len(tags)),
|
||||
Page: page,
|
||||
PageSize: pageSize,
|
||||
}, "获取成功", c)
|
||||
}
|
||||
|
||||
// GetSimpleTags 获取公众号标签精简信息列表
|
||||
// @Tags MpTag
|
||||
// @Summary 获取公众号标签精简信息列表
|
||||
// @Description 获取公众号标签精简信息列表
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {object} response.Response "获取成功"
|
||||
// @Router /wechat/mp/tag/list-all-simple [get]
|
||||
func (m *MpTagApi) GetSimpleTags(c *gin.Context) {
|
||||
// TODO: 实现获取简单标签列表逻辑
|
||||
global.GVA_LOG.Info("获取简单标签列表请求")
|
||||
|
||||
// 模拟数据
|
||||
tags := []map[string]interface{}{
|
||||
{"id": 1, "name": "VIP用户"},
|
||||
{"id": 2, "name": "普通用户"},
|
||||
}
|
||||
|
||||
response.OkWithData(tags, c)
|
||||
}
|
||||
|
||||
// SyncTag 同步公众号标签
|
||||
// @Tags MpTag
|
||||
// @Summary 同步公众号标签
|
||||
// @Description 同步公众号标签
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param accountId query int true "公众号账号ID"
|
||||
// @Success 200 {object} response.Response "同步成功"
|
||||
// @Router /wechat/mp/tag/sync [post]
|
||||
func (m *MpTagApi) SyncTag(c *gin.Context) {
|
||||
accountIdStr := c.Query("accountId")
|
||||
accountId, err := strconv.ParseUint(accountIdStr, 10, 32)
|
||||
if err != nil {
|
||||
response.FailWithMessage("无效的账号ID", c)
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: 实现标签同步逻辑
|
||||
global.GVA_LOG.Info("同步标签请求", zap.Uint64("accountId", accountId))
|
||||
response.OkWithMessage("标签同步功能待实现", c)
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/plugin/wechat-integration/model/request"
|
||||
wechatResponse "github.com/flipped-aurora/gin-vue-admin/server/plugin/wechat-integration/model/response"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/plugin/wechat-integration/service"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type MpUserApi struct{}
|
||||
|
||||
var mpUserService = service.ServiceGroupApp.MpUserService
|
||||
|
||||
// GetUserList 获取公众号用户列表
|
||||
// @Tags MpUser
|
||||
// @Summary 获取公众号用户列表
|
||||
// @Description 获取微信公众号粉丝列表
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param page query int false "页码"
|
||||
// @Param pageSize query int false "每页数量"
|
||||
// @Param subscribeStatus query int false "关注状态"
|
||||
// @Success 200 {object} response.Response{data=response.PageResult} "获取成功"
|
||||
// @Router /wechat/mp/users [get]
|
||||
func (m *MpUserApi) GetUserList(c *gin.Context) {
|
||||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||
pageSize, _ := strconv.Atoi(c.DefaultQuery("pageSize", "10"))
|
||||
|
||||
var subscribeStatus *int
|
||||
if status := c.Query("subscribeStatus"); status != "" {
|
||||
if s, err := strconv.Atoi(status); err == nil {
|
||||
subscribeStatus = &s
|
||||
}
|
||||
}
|
||||
|
||||
if page <= 0 {
|
||||
page = 1
|
||||
}
|
||||
if pageSize <= 0 || pageSize > 100 {
|
||||
pageSize = 10
|
||||
}
|
||||
|
||||
users, total, err := mpUserService.GetUserList(page, pageSize, subscribeStatus)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取公众号用户列表失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithDetailed(wechatResponse.PageResult{
|
||||
List: users,
|
||||
Total: total,
|
||||
Page: page,
|
||||
PageSize: pageSize,
|
||||
}, "获取成功", c)
|
||||
}
|
||||
|
||||
// GetUserInfo 获取公众号用户信息
|
||||
// @Tags MpUser
|
||||
// @Summary 获取公众号用户信息
|
||||
// @Description 根据openid获取公众号用户详细信息
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param openid query string true "用户openid"
|
||||
// @Success 200 {object} response.Response{data=model.MpUser} "获取成功"
|
||||
// @Router /wechat/mp/user [get]
|
||||
func (m *MpUserApi) GetUserInfo(c *gin.Context) {
|
||||
openid := c.Query("openid")
|
||||
if openid == "" {
|
||||
response.FailWithMessage("openid不能为空", c)
|
||||
return
|
||||
}
|
||||
|
||||
user, err := mpUserService.GetUserByOpenID(openid)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取公众号用户信息失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithData(user, c)
|
||||
}
|
||||
|
||||
// SyncUser 同步用户信息
|
||||
// @Tags MpUser
|
||||
// @Summary 同步用户信息
|
||||
// @Description 从微信服务器同步用户信息
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param data body request.SyncUserRequest true "同步参数"
|
||||
// @Success 200 {object} response.Response{data=model.MpUser} "同步成功"
|
||||
// @Router /wechat/mp/user/sync [post]
|
||||
func (m *MpUserApi) SyncUser(c *gin.Context) {
|
||||
var req request.SyncUserRequest
|
||||
err := c.ShouldBindJSON(&req)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
if req.OpenID == "" {
|
||||
response.FailWithMessage("openid不能为空", c)
|
||||
return
|
||||
}
|
||||
|
||||
user, err := mpUserService.SyncUser(req.OpenID)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("同步用户信息失败!", zap.Error(err))
|
||||
response.FailWithMessage("同步失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithDetailed(user, "同步成功", c)
|
||||
}
|
||||
|
||||
// UpdateUserRemark 更新用户备注
|
||||
// @Tags MpUser
|
||||
// @Summary 更新用户备注
|
||||
// @Description 更新公众号用户备注
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param data body request.UpdateRemarkRequest true "更新参数"
|
||||
// @Success 200 {object} response.Response "更新成功"
|
||||
// @Router /wechat/mp/user/remark [put]
|
||||
func (m *MpUserApi) UpdateUserRemark(c *gin.Context) {
|
||||
var req request.UpdateRemarkRequest
|
||||
err := c.ShouldBindJSON(&req)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
if req.OpenID == "" {
|
||||
response.FailWithMessage("openid不能为空", c)
|
||||
return
|
||||
}
|
||||
|
||||
err = mpUserService.UpdateUserRemark(req.OpenID, req.Remark)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("更新用户备注失败!", zap.Error(err))
|
||||
response.FailWithMessage("更新失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithMessage("更新成功", c)
|
||||
}
|
||||
@@ -0,0 +1,212 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/plugin/wechat-integration/model/request"
|
||||
wechatResponse "github.com/flipped-aurora/gin-vue-admin/server/plugin/wechat-integration/model/response"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/plugin/wechat-integration/service"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type WechatMiniApi struct{}
|
||||
|
||||
var wechatMiniService = service.ServiceGroupApp.WechatMiniService
|
||||
|
||||
// Login 小程序登录
|
||||
// @Tags WechatMini
|
||||
// @Summary 小程序登录
|
||||
// @Description 微信小程序登录接口
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param data body request.MiniLoginRequest true "登录参数"
|
||||
// @Success 200 {object} response.Response{data=response.MiniLoginResponse} "登录成功"
|
||||
// @Router /wechat/mini/login [post]
|
||||
func (w *WechatMiniApi) Login(c *gin.Context) {
|
||||
var req request.MiniLoginRequest
|
||||
err := c.ShouldBindJSON(&req)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
if req.Code == "" {
|
||||
response.FailWithMessage("code不能为空", c)
|
||||
return
|
||||
}
|
||||
|
||||
user, err := wechatMiniService.Code2Session(req.Code)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("小程序登录失败!", zap.Error(err))
|
||||
response.FailWithMessage("登录失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
// 这里可以生成JWT token或其他认证信息
|
||||
resp := wechatResponse.MiniLoginResponse{
|
||||
OpenID: user.OpenID,
|
||||
UnionID: user.UnionID,
|
||||
UserID: user.UserID,
|
||||
}
|
||||
|
||||
response.OkWithDetailed(resp, "登录成功", c)
|
||||
}
|
||||
|
||||
// GetUserInfo 获取用户信息
|
||||
// @Tags WechatMini
|
||||
// @Summary 获取用户信息
|
||||
// @Description 获取小程序用户信息
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param openid query string true "用户openid"
|
||||
// @Success 200 {object} response.Response{data=model.WechatMiniUser} "获取成功"
|
||||
// @Router /wechat/mini/userinfo [get]
|
||||
func (w *WechatMiniApi) GetUserInfo(c *gin.Context) {
|
||||
openid := c.Query("openid")
|
||||
if openid == "" {
|
||||
response.FailWithMessage("openid不能为空", c)
|
||||
return
|
||||
}
|
||||
|
||||
user, err := wechatMiniService.GetUserInfo(openid)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取用户信息失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithData(user, c)
|
||||
}
|
||||
|
||||
// UpdateUserInfo 更新用户信息
|
||||
// @Tags WechatMini
|
||||
// @Summary 更新用户信息
|
||||
// @Description 更新小程序用户信息
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param data body request.UpdateUserInfoRequest true "用户信息"
|
||||
// @Success 200 {object} response.Response "更新成功"
|
||||
// @Router /wechat/mini/userinfo [put]
|
||||
func (w *WechatMiniApi) UpdateUserInfo(c *gin.Context) {
|
||||
var req request.UpdateUserInfoRequest
|
||||
err := c.ShouldBindJSON(&req)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
if req.OpenID == "" {
|
||||
response.FailWithMessage("openid不能为空", c)
|
||||
return
|
||||
}
|
||||
|
||||
err = wechatMiniService.UpdateUserInfo(req.OpenID, req.UserInfo)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("更新用户信息失败!", zap.Error(err))
|
||||
response.FailWithMessage("更新失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithMessage("更新成功", c)
|
||||
}
|
||||
|
||||
// BindPhone 绑定手机号
|
||||
// @Tags WechatMini
|
||||
// @Summary 绑定手机号
|
||||
// @Description 绑定手机号并关联系统用户
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param data body request.BindPhoneRequest true "绑定参数"
|
||||
// @Success 200 {object} response.Response{data=model.WechatMiniUser} "绑定成功"
|
||||
// @Router /wechat/mini/bind-phone [post]
|
||||
func (w *WechatMiniApi) BindPhone(c *gin.Context) {
|
||||
var req request.BindPhoneRequest
|
||||
err := c.ShouldBindJSON(&req)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
if req.OpenID == "" || req.Phone == "" {
|
||||
response.FailWithMessage("openid和手机号不能为空", c)
|
||||
return
|
||||
}
|
||||
|
||||
user, err := wechatMiniService.BindPhoneAndLinkUser(req.OpenID, req.Phone)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("绑定手机号失败!", zap.Error(err))
|
||||
response.FailWithMessage("绑定失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithDetailed(user, "绑定成功", c)
|
||||
}
|
||||
|
||||
// GetUserList 获取用户列表
|
||||
// @Tags WechatMini
|
||||
// @Summary 获取用户列表
|
||||
// @Description 获取小程序用户列表(管理员接口)
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param page query int false "页码"
|
||||
// @Param pageSize query int false "每页数量"
|
||||
// @Success 200 {object} response.Response{data=response.PageResult} "获取成功"
|
||||
// @Router /wechat/mini/users [get]
|
||||
func (w *WechatMiniApi) GetUserList(c *gin.Context) {
|
||||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||
pageSize, _ := strconv.Atoi(c.DefaultQuery("pageSize", "10"))
|
||||
|
||||
if page <= 0 {
|
||||
page = 1
|
||||
}
|
||||
if pageSize <= 0 || pageSize > 100 {
|
||||
pageSize = 10
|
||||
}
|
||||
|
||||
users, total, err := wechatMiniService.GetUserList(page, pageSize)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取用户列表失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithDetailed(wechatResponse.PageResult{
|
||||
List: users,
|
||||
Total: total,
|
||||
Page: page,
|
||||
PageSize: pageSize,
|
||||
}, "获取成功", c)
|
||||
}
|
||||
|
||||
// CheckUnionID 检查UnionID是否存在(为APP登录预留)
|
||||
// @Tags WechatMini
|
||||
// @Summary 检查UnionID
|
||||
// @Description 检查UnionID是否已存在用户
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param unionid query string true "UnionID"
|
||||
// @Success 200 {object} response.Response{data=model.WechatMiniUser} "检查成功"
|
||||
// @Router /wechat/mini/check-unionid [get]
|
||||
func (w *WechatMiniApi) CheckUnionID(c *gin.Context) {
|
||||
unionid := c.Query("unionid")
|
||||
if unionid == "" {
|
||||
response.FailWithMessage("unionid不能为空", c)
|
||||
return
|
||||
}
|
||||
|
||||
user, err := wechatMiniService.CheckUnionIDExists(unionid)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("检查UnionID失败!", zap.Error(err))
|
||||
response.FailWithMessage("检查失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
if user == nil {
|
||||
response.OkWithDetailed(nil, "UnionID不存在", c)
|
||||
} else {
|
||||
response.OkWithDetailed(user, "UnionID已存在", c)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"crypto/sha1"
|
||||
"fmt"
|
||||
"io"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/plugin/wechat-integration/service"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type WechatWebhookApi 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 *WechatWebhookApi) OfficialAccountWebhook(c *gin.Context) {
|
||||
// 获取参数
|
||||
signature := c.Query("signature")
|
||||
timestamp := c.Query("timestamp")
|
||||
nonce := c.Query("nonce")
|
||||
echostr := c.Query("echostr")
|
||||
|
||||
// 验证签名
|
||||
if !w.verifySignature(signature, timestamp, nonce) {
|
||||
global.GVA_LOG.Error("微信签名验证失败")
|
||||
c.String(403, "签名验证失败")
|
||||
return
|
||||
}
|
||||
|
||||
// GET请求用于验证服务器配置
|
||||
if c.Request.Method == "GET" {
|
||||
c.String(200, echostr)
|
||||
return
|
||||
}
|
||||
|
||||
// POST请求处理消息
|
||||
body, err := io.ReadAll(c.Request.Body)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("读取请求体失败", zap.Error(err))
|
||||
c.String(400, "读取请求体失败")
|
||||
return
|
||||
}
|
||||
|
||||
// 简化消息处理 - 暂时只记录日志
|
||||
global.GVA_LOG.Info("收到微信消息", zap.String("body", string(body)))
|
||||
|
||||
// TODO: 实现完整的消息解析和处理
|
||||
// 由于微信SDK API变化,暂时简化处理
|
||||
|
||||
c.String(200, "success")
|
||||
}
|
||||
|
||||
// verifySignature 验证微信签名
|
||||
func (w *WechatWebhookApi) verifySignature(signature, timestamp, nonce string) bool {
|
||||
token := global.GVA_CONFIG.Wechat.OfficialToken
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user