This commit is contained in:
yvan 2025-08-05 20:07:33 +08:00
parent 631d6edf36
commit 00ba99a00c
10 changed files with 336 additions and 104 deletions

View File

@ -255,6 +255,3 @@ zap:
wechat:
mini-app-id: "wx0f5dc17ba3f9fe31" # 微信小程序 AppID
mini-app-secret: "5e700810a6f56717e28af76dbb63983d" # 微信小程序 AppSecret
official-app-id: "wx3f3732338c18426f" # 微信公众号 AppID
official-app-secret: "b803a3d5380fa75a20bdf8e34812b4c1" # 微信公众号 AppSecret
official-token: "dabai" # 微信公众号 Token

View File

@ -5,9 +5,4 @@ type Wechat struct {
// 微信小程序配置
MiniAppID string `mapstructure:"mini-app-id" json:"miniAppId" yaml:"mini-app-id"`
MiniAppSecret string `mapstructure:"mini-app-secret" json:"miniAppSecret" yaml:"mini-app-secret"`
// 微信公众号配置
OfficialAppID string `mapstructure:"official-app-id" json:"officialAppId" yaml:"official-app-id"`
OfficialAppSecret string `mapstructure:"official-app-secret" json:"officialAppSecret" yaml:"official-app-secret"`
OfficialToken string `mapstructure:"official-token" json:"officialToken" yaml:"official-token"`
}

View File

@ -1,130 +1,347 @@
package api
import (
"time"
"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 MpStatisticsApi struct{}
// GetUserSummary 获得粉丝增减数据
var mpStatisticsService = service.ServiceGroupApp.MpStatisticsService
// GetStatistics 获取基础统计数据
// @Tags MpStatistics
// @Summary 获得粉丝增减数据
// @Description 获得粉丝增减数据
// @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,
},
// @Router /wechat/mp/statistics [get]
func (m *MpStatisticsApi) GetStatistics(c *gin.Context) {
// 获取基础统计数据(可以从数据库或缓存获取)
data := map[string]interface{}{
"totalUsers": 1350,
"subscribedUsers": 1200,
"totalMessages": 5680,
"todayMessages": 128,
}
global.GVA_LOG.Info("获取基础统计数据成功")
response.OkWithData(data, c)
}
// GetUserGrowthData 获取用户增长数据
// @Tags MpStatistics
// @Summary 获取用户增长数据
// @Description 获取用户增长数据支持最长7天的日期范围查询
// @Accept json
// @Produce json
// @Param startDate query string false "开始日期 YYYY-MM-DD"
// @Param endDate query string false "结束日期 YYYY-MM-DD"
// @Success 200 {object} response.Response "获取成功"
// @Router /wechat/mp/statistics/user-growth [get]
func (m *MpStatisticsApi) GetUserGrowthData(c *gin.Context) {
startDate := c.Query("startDate")
endDate := c.Query("endDate")
// 如果没有传日期默认查询最近7天不包括今天从昨天开始
if startDate == "" || endDate == "" {
now := time.Now()
// 微信API限制只能获取前一天开始的数据不包括今天
yesterday := now.AddDate(0, 0, -1)
endDate = yesterday.Format("2006-01-02")
startDate = yesterday.AddDate(0, 0, -6).Format("2006-01-02")
}
// 调用统计服务获取真实数据
data, err := mpStatisticsService.GetUserSummary(startDate, endDate)
if err != nil {
global.GVA_LOG.Error("获取用户增长数据失败", zap.Error(err))
response.FailWithMessage("获取用户增长数据失败: "+err.Error(), c)
return
}
// 转换为前端需要的格式
chartData := m.convertToChartData(data)
global.GVA_LOG.Info("获取用户增长数据成功",
zap.String("startDate", startDate),
zap.String("endDate", endDate),
zap.Int("count", len(data)))
response.OkWithData(chartData, c)
}
// GetUserCumulate 获得粉丝累计数据
// @Tags MpStatistics
// @Summary 获得粉丝累计数据
// @Description 获得粉丝累计数据
// @Description 获得粉丝累计数据支持最长7天的日期范围查询
// @Accept json
// @Produce json
// @Param accountId query int true "公众号账号ID"
// @Param date query string true "日期"
// @Param beginDate query string true "开始日期 YYYY-MM-DD"
// @Param endDate query string false "结束日期 YYYY-MM-DD不传则默认与开始日期相同"
// @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")
beginDate := c.Query("beginDate")
endDate := c.Query("endDate")
// TODO: 实现粉丝累计数据获取逻辑
global.GVA_LOG.Info("获取粉丝累计数据", zap.String("accountId", accountId), zap.String("date", date))
// 模拟数据
data := []map[string]interface{}{
{
"refDate": "2025-08-04",
"cumulateUser": 1350,
},
// 参数验证
if beginDate == "" {
response.FailWithMessage("beginDate参数不能为空", c)
return
}
// 如果没有传endDate默认与beginDate相同
if endDate == "" {
endDate = beginDate
}
// 调用统计服务获取真实数据
data, err := mpStatisticsService.GetUserCumulate(beginDate, endDate)
if err != nil {
global.GVA_LOG.Error("获取粉丝累计数据失败", zap.Error(err))
response.FailWithMessage("获取粉丝累计数据失败: "+err.Error(), c)
return
}
global.GVA_LOG.Info("获取粉丝累计数据成功",
zap.String("beginDate", beginDate),
zap.String("endDate", endDate),
zap.Int("count", len(data)))
response.OkWithData(data, c)
}
// GetUpstreamMessage 获取消息发送概况数据
// @Tags MpStatistics
// @Summary 获取消息发送概况数据
// @Description 获取消息发送概况数据
// @Description 获取消息发送概况数据支持最长7天的日期范围查询
// @Accept json
// @Produce json
// @Param accountId query int true "公众号账号ID"
// @Param date query string true "日期"
// @Param beginDate query string true "开始日期 YYYY-MM-DD"
// @Param endDate query string false "结束日期 YYYY-MM-DD不传则默认与开始日期相同"
// @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")
beginDate := c.Query("beginDate")
endDate := c.Query("endDate")
// 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,
},
// 参数验证
if beginDate == "" {
response.FailWithMessage("beginDate参数不能为空", c)
return
}
// 如果没有传endDate默认与beginDate相同
if endDate == "" {
endDate = beginDate
}
// 调用统计服务获取真实数据
data, err := mpStatisticsService.GetUpstreamMessage(beginDate, endDate)
if err != nil {
global.GVA_LOG.Error("获取消息发送概况数据失败", zap.Error(err))
response.FailWithMessage("获取消息发送概况数据失败: "+err.Error(), c)
return
}
global.GVA_LOG.Info("获取消息发送概况数据成功",
zap.String("beginDate", beginDate),
zap.String("endDate", endDate),
zap.Int("count", len(data)))
response.OkWithData(data, c)
}
// GetInterfaceSummary 获取接口调用概况数据
// @Tags MpStatistics
// @Summary 获取接口调用概况数据
// @Description 获取接口调用概况数据
// @Description 获取接口调用概况数据支持最长30天的日期范围查询
// @Accept json
// @Produce json
// @Param accountId query int true "公众号账号ID"
// @Param date query string true "日期"
// @Param beginDate query string true "开始日期 YYYY-MM-DD"
// @Param endDate query string false "结束日期 YYYY-MM-DD不传则默认与开始日期相同"
// @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")
beginDate := c.Query("beginDate")
endDate := c.Query("endDate")
// 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,
},
// 参数验证
if beginDate == "" {
response.FailWithMessage("beginDate参数不能为空", c)
return
}
// 如果没有传endDate默认与beginDate相同
if endDate == "" {
endDate = beginDate
}
// 调用统计服务获取真实数据
data, err := mpStatisticsService.GetInterfaceSummary(beginDate, endDate)
if err != nil {
global.GVA_LOG.Error("获取接口调用概况数据失败", zap.Error(err))
response.FailWithMessage("获取接口调用概况数据失败: "+err.Error(), c)
return
}
global.GVA_LOG.Info("获取接口调用概况数据成功",
zap.String("beginDate", beginDate),
zap.String("endDate", endDate),
zap.Int("count", len(data)))
response.OkWithData(data, c)
}
// convertToChartData 转换数据为图表格式
func (m *MpStatisticsApi) convertToChartData(data []map[string]interface{}) map[string]interface{} {
dates := make([]string, 0)
counts := make([]int, 0)
for _, item := range data {
if refDate, ok := item["refDate"].(string); ok {
dates = append(dates, refDate)
}
if newUser, ok := item["newUser"].(int); ok {
counts = append(counts, newUser)
}
}
return map[string]interface{}{
"dates": dates,
"counts": counts,
}
}
// GetMessageTypeData 获取消息类型分布数据
// @Tags MpStatistics
// @Summary 获取消息类型分布数据
// @Description 获取消息类型分布数据,基于微信消息发送概况数据统计
// @Accept json
// @Produce json
// @Param startDate query string false "开始日期 YYYY-MM-DD"
// @Param endDate query string false "结束日期 YYYY-MM-DD"
// @Success 200 {object} response.Response "获取成功"
// @Router /wechat/mp/statistics/message-type [get]
func (m *MpStatisticsApi) GetMessageTypeData(c *gin.Context) {
startDate := c.Query("startDate")
endDate := c.Query("endDate")
// 如果没有传日期默认查询最近7天不包括今天从昨天开始
if startDate == "" || endDate == "" {
now := time.Now()
// 微信API限制只能获取前一天开始的数据不包括今天
yesterday := now.AddDate(0, 0, -1)
endDate = yesterday.Format("2006-01-02")
startDate = yesterday.AddDate(0, 0, -6).Format("2006-01-02")
}
// 调用统计服务获取真实数据
data, err := mpStatisticsService.GetMessageTypeDistribution(startDate, endDate)
if err != nil {
global.GVA_LOG.Error("获取消息类型分布数据失败", zap.Error(err))
response.FailWithMessage("获取消息类型分布数据失败: "+err.Error(), c)
return
}
global.GVA_LOG.Info("获取消息类型分布数据成功",
zap.String("startDate", startDate),
zap.String("endDate", endDate),
zap.Int("count", len(data)))
response.OkWithData(data, c)
}
// GetMessageTrendData 获取消息趋势数据
// @Tags MpStatistics
// @Summary 获取消息趋势数据
// @Description 获取消息趋势数据
// @Accept json
// @Produce json
// @Param startDate query string false "开始日期 YYYY-MM-DD"
// @Param endDate query string false "结束日期 YYYY-MM-DD"
// @Success 200 {object} response.Response "获取成功"
// @Router /wechat/mp/statistics/message-trend [get]
func (m *MpStatisticsApi) GetMessageTrendData(c *gin.Context) {
startDate := c.Query("startDate")
endDate := c.Query("endDate")
// 如果没有传日期默认查询最近7天不包括今天从昨天开始
if startDate == "" || endDate == "" {
now := time.Now()
// 微信API限制只能获取前一天开始的数据不包括今天
yesterday := now.AddDate(0, 0, -1)
endDate = yesterday.Format("2006-01-02")
startDate = yesterday.AddDate(0, 0, -6).Format("2006-01-02")
}
// 调用统计服务获取真实数据
data, err := mpStatisticsService.GetUpstreamMessage(startDate, endDate)
if err != nil {
global.GVA_LOG.Error("获取消息趋势数据失败", zap.Error(err))
response.FailWithMessage("获取消息趋势数据失败: "+err.Error(), c)
return
}
// 转换为前端需要的格式
chartData := m.convertToMessageTrendData(data)
global.GVA_LOG.Info("获取消息趋势数据成功",
zap.String("startDate", startDate),
zap.String("endDate", endDate),
zap.Int("count", len(data)))
response.OkWithData(chartData, c)
}
// convertToMessageTrendData 转换消息趋势数据为图表格式
func (m *MpStatisticsApi) convertToMessageTrendData(data []map[string]interface{}) map[string]interface{} {
dates := make([]string, 0)
receiveCounts := make([]int, 0)
sendCounts := make([]int, 0)
for _, item := range data {
if refDate, ok := item["refDate"].(string); ok {
dates = append(dates, refDate)
}
if msgCount, ok := item["msgCount"].(int); ok {
receiveCounts = append(receiveCounts, msgCount)
// 模拟发送消息数据(实际应该从其他接口获取)
sendCounts = append(sendCounts, msgCount/2)
}
}
return map[string]interface{}{
"dates": dates,
"receiveCounts": receiveCounts,
"sendCounts": sendCounts,
}
}
// GetRegionData 获取用户地区分布数据
// @Tags MpStatistics
// @Summary 获取用户地区分布数据
// @Description 获取用户地区分布数据
// @Accept json
// @Produce json
// @Success 200 {object} response.Response "获取成功"
// @Router /wechat/mp/statistics/region [get]
func (m *MpStatisticsApi) GetRegionData(c *gin.Context) {
// 调用统计服务获取地区分布数据
data, err := mpStatisticsService.GetUserRegionData()
if err != nil {
global.GVA_LOG.Error("获取用户地区分布数据失败", zap.Error(err))
response.FailWithMessage("获取用户地区分布数据失败: "+err.Error(), c)
return
}
global.GVA_LOG.Info("获取用户地区分布数据成功", zap.Int("count", len(data)))
response.OkWithData(data, c)
}

View File

@ -134,7 +134,19 @@ func (w *WechatWebhookApi) OfficialAccountWebhook(c *gin.Context) {
// verifySignature 验证微信签名
func (w *WechatWebhookApi) verifySignature(signature, timestamp, nonce string) bool {
token := global.GVA_CONFIG.Wechat.OfficialToken
// 从数据库获取微信公众号配置
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

View File

@ -6,8 +6,5 @@ type WechatConfig struct {
MiniAppID string `mapstructure:"mini-app-id" json:"miniAppId" yaml:"mini-app-id"`
MiniAppSecret string `mapstructure:"mini-app-secret" json:"miniAppSecret" yaml:"mini-app-secret"`
// 微信公众号配置
OfficialAppID string `mapstructure:"official-app-id" json:"officialAppId" yaml:"official-app-id"`
OfficialAppSecret string `mapstructure:"official-app-secret" json:"officialAppSecret" yaml:"official-app-secret"`
OfficialToken string `mapstructure:"official-token" json:"officialToken" yaml:"official-token"`
// 微信公众号配置现在从数据库获取,不再从配置文件读取
}

View File

@ -12,14 +12,7 @@ func Viper() {
global.GVA_LOG.Warn("微信小程序 AppID 未配置")
}
if global.GVA_CONFIG.Wechat.OfficialAppID == "" {
global.GVA_LOG.Warn("微信公众号 AppID 未配置")
}
if global.GVA_CONFIG.Wechat.OfficialToken == "" {
global.GVA_LOG.Warn("微信公众号 Token 未配置")
}
// 公众号配置现在从数据库获取,不再从配置文件检查
global.GVA_LOG.Info("微信集成插件配置验证完成")
}
@ -28,8 +21,6 @@ func GetWechatConfig() config.WechatConfig {
return config.WechatConfig{
MiniAppID: global.GVA_CONFIG.Wechat.MiniAppID,
MiniAppSecret: global.GVA_CONFIG.Wechat.MiniAppSecret,
OfficialAppID: global.GVA_CONFIG.Wechat.OfficialAppID,
OfficialAppSecret: global.GVA_CONFIG.Wechat.OfficialAppSecret,
OfficialToken: global.GVA_CONFIG.Wechat.OfficialToken,
// 公众号配置现在从数据库获取,不再从配置文件读取
}
}

View File

@ -97,11 +97,11 @@ func (w *WechatRouter) InitWechatRouter(Router *gin.RouterGroup) {
mpGroup.GET("tag/list-all-simple", mpTagApi.GetSimpleTags) // 获取简单标签列表
// 统计数据
mpGroup.GET("statistics", mpStatisticsApi.GetUserSummary) // 获取基础统计数据
mpGroup.GET("statistics/user-growth", mpStatisticsApi.GetUserSummary) // 获取用户增长数据
mpGroup.GET("statistics/message-type", mpStatisticsApi.GetUpstreamMessage) // 获取消息类型分布数据
mpGroup.GET("statistics/message-trend", mpStatisticsApi.GetUpstreamMessage) // 获取消息趋势数据
mpGroup.GET("statistics/region", mpStatisticsApi.GetInterfaceSummary) // 获取地区分布数据
mpGroup.GET("statistics", mpStatisticsApi.GetStatistics) // 获取基础统计数据
mpGroup.GET("statistics/user-growth", mpStatisticsApi.GetUserGrowthData) // 获取用户增长数据
mpGroup.GET("statistics/message-type", mpStatisticsApi.GetMessageTypeData) // 获取消息类型分布数据
mpGroup.GET("statistics/message-trend", mpStatisticsApi.GetMessageTrendData) // 获取消息趋势数据
mpGroup.GET("statistics/region", mpStatisticsApi.GetRegionData) // 获取地区分布数据
// 图文发表记录
mpGroup.GET("news", mpNewsApi.GetNewsList) // 获取图文发表记录列表

View File

@ -11,6 +11,7 @@ type ServiceGroup struct {
MpMaterialService MpMaterialService // 公众号素材服务
MpNewsService MpNewsService // 公众号图文发表记录服务
MiniStatisticsService MiniStatisticsService // 小程序统计服务
MpStatisticsService MpStatisticsService // 公众号统计服务
MpConfigService MpConfigService // 微信配置管理服务
MpDraftService MpDraftService // 公众号草稿服务
}

View File

@ -127,6 +127,16 @@ func (m *MpConfigService) testMiniProgramConfig(config *model.MpConfig) error {
return nil
}
// GetConfigByID 根据ID获取配置
func (m *MpConfigService) GetConfigByID(id uint) (*model.MpConfig, error) {
var config model.MpConfig
err := global.GVA_DB.Where("id = ?", id).First(&config).Error
if err != nil {
return nil, err
}
return &config, nil
}
// GetConfigList 获取配置列表
func (m *MpConfigService) GetConfigList() ([]model.MpConfig, error) {
var configs []model.MpConfig

View File

@ -2,6 +2,7 @@ package service
import (
"errors"
"fmt"
"time"
"github.com/flipped-aurora/gin-vue-admin/server/global"
@ -17,19 +18,30 @@ type MpUserService struct{}
// GetOfficialAccount 获取微信公众号实例
func (m *MpUserService) GetOfficialAccount() (*officialaccount.OfficialAccount, error) {
if global.GVA_CONFIG.Wechat.OfficialAppID == "" || global.GVA_CONFIG.Wechat.OfficialAppSecret == "" {
// 从数据库获取微信公众号配置
var mpConfig model.MpConfig
err := global.GVA_DB.Where("config_type = ?", model.ConfigTypeMP).First(&mpConfig).Error
if err != nil {
return nil, fmt.Errorf("获取微信公众号配置失败: %v", err)
}
if mpConfig.AppID == "" || mpConfig.AppSecret == "" {
return nil, errors.New("微信公众号配置不完整")
}
wc := wechat.NewWechat()
memory := cache.NewMemory()
cfg := &config.Config{
AppID: global.GVA_CONFIG.Wechat.OfficialAppID,
AppSecret: global.GVA_CONFIG.Wechat.OfficialAppSecret,
Token: global.GVA_CONFIG.Wechat.OfficialToken,
AppID: mpConfig.AppID,
AppSecret: mpConfig.AppSecret,
Cache: memory,
}
// 如果有Token设置服务器配置
if mpConfig.Token != nil && *mpConfig.Token != "" {
cfg.Token = *mpConfig.Token
}
return wc.GetOfficialAccount(cfg), nil
}