1、历史汇总查询接口优化,非当日数据存入数据库,每1小时更新一次;

This commit is contained in:
chenlin 2025-04-24 18:22:40 +08:00
parent 2eb326eb0a
commit 9cb7995515
5 changed files with 240 additions and 3 deletions

View File

@ -3150,3 +3150,107 @@ func (e MiGuDeployService) queryHistoricalDataByHour(startTime, endTime string,
return paginatedData, summaryData, len(filteredData), nil
}
func (e MiGuDeployService) HistoricalSummaryListNewByCatch(c *gin.Context) {
fmt.Println("HistoricalSummaryList")
err := e.MakeContext(c).MakeOrm().Errors
if err != nil {
fmt.Println("MakeContext err:", err)
e.Logger.Error(err)
return
}
// 请求参数绑定
req := &models.HistoricalSummaryListReq{}
if c.ShouldBindJSON(req) != nil {
logger.Errorf("para err")
response.Error(c, http.StatusBadRequest, errors.New("para err"), "参数错误")
return
}
// 分页处理
if req.PageNum < 1 {
req.PageNum = 1
}
if req.PageSize == 0 {
req.PageSize = 10
}
var summaries []models.MgHistoricalSummary
today := time.Now().Format("2006-01-02")
// 1. 先查缓存表(非今日)
var cacheQuery *gorm.DB
// 如果 req.StartTime 和 req.EndTime 不为空,应用时间条件
if req.StartTime != "" && req.EndTime != "" {
startDate := req.StartTime[:10] // 取前 10 位 YYYY-MM-DD
endDate := req.EndTime[:10] // 同上
cacheQuery = e.Orm.Table("mg_historical_summary").
Where("date >= ? AND date <= ?", startDate, endDate)
} else {
// 如果为空,则查询所有数据
cacheQuery = e.Orm.Table("mg_historical_summary")
}
if req.SkuCode != 0 {
cacheQuery = cacheQuery.Where("product_id = ?", req.SkuCode)
}
if req.Channel != "" {
cacheQuery = cacheQuery.Where("channel_code = ?", req.Channel)
}
var cacheData []models.MgHistoricalSummary
if err := cacheQuery.Find(&cacheData).Error; err != nil {
response.Error(c, http.StatusInternalServerError, err, "查询失败")
}
summaries = append(summaries, cacheData...)
// 2. 如果包含今日,查实时数据
if req.EndTime >= today || req.EndTime == "" {
// 计算今天的开始时间和结束时间
now := time.Now()
// 格式化时间为 string 类型,格式为 "YYYY-MM-DD"
start := now.Truncate(24 * time.Hour).Format("2006-01-02") // 今天的零点,字符串格式
end := now.Truncate(24 * time.Hour).Add(24*time.Hour - time.Second).Format("2006-01-02") // 今天的23:59:59字符串格式
realTimeData, err := models.CalculateDailySummaryFromRealtime(e.Orm, start, end, req.Channel, req.SkuCode)
if err != nil {
response.Error(c, http.StatusInternalServerError, err, "查询失败")
}
for _, item := range realTimeData {
summaries = append(summaries, item)
}
}
// 3. 排序:默认按日期倒序
sort.SliceStable(summaries, func(i, j int) bool {
return summaries[i].Date > summaries[j].Date
})
// 4. 分页
total := len(summaries)
start := (req.PageNum - 1) * req.PageSize
end := start + req.PageSize
if start > total {
start = total
}
if end > total {
end = total
}
pageList := summaries[start:end]
resp := &models.HistoricalSummaryListResp{
List: pageList,
Count: total,
PageNum: req.PageNum,
PageSize: req.PageSize,
}
e.OK(resp, "")
}

View File

@ -139,6 +139,8 @@ type MgHourSummary struct {
// MgHistoricalSummary 历史汇总查询表对应的结构体
type MgHistoricalSummary struct {
Model
Date string `json:"date"` // 日期
ProductID int64 `json:"product_id"` // 产品ID
ChannelCode string `gorm:"size:255" json:"channel_code"` // 渠道编码
@ -2140,3 +2142,126 @@ func GetMonthlyEffectiveUserStats(db *gorm.DB, retentionMonth string, skuCode in
return &stats, nil
}
func UpdateHistoricalSummaryCache() {
logger.Info("****** UpdateHistoricalSummaryCache start ******")
fmt.Println("****** UpdateHistoricalSummaryCache start ******")
if database.Db == nil {
logger.Error("Database connection is nil")
fmt.Println("Database connection is nil")
return
}
startTime := "1970-01-01 00:00:00"
endTime := time.Now().AddDate(0, 0, -1).Format("2006-01-02") + " 23:59:59"
// 1. 查询某天的完整实时数据
summaries, err := CalculateDailySummaryFromRealtime(database.Db, startTime, endTime, "", 0)
if err != nil {
log.Printf("calculateDailySummaryFromRealtime failed: %v", err)
return
}
if len(summaries) == 0 {
return
}
tx := database.Db.Begin()
defer func() {
if r := recover(); r != nil {
tx.Rollback()
}
}()
// 2. 清空表中的所有数据
if err = tx.Exec("TRUNCATE TABLE mg_historical_summary").Error; err != nil {
logger.Error("TRUNCATE TABLE mg_historical_summary error:", err)
fmt.Println("TRUNCATE TABLE mg_historical_summary error")
tx.Rollback()
return
}
// 3. 插入新数据到缓存表
if err = tx.Create(&summaries).Error; err != nil {
logger.Error("UpdateHistoricalSummaryCache Create error:", err)
fmt.Println("UpdateHistoricalSummaryCache Create error")
tx.Rollback()
return
}
err = tx.Commit().Error
if err != nil {
logger.Error("UpdateHistoricalSummaryCache Commit error:", err)
fmt.Println("UpdateHistoricalSummaryCache Commit error")
return
}
return
}
func CalculateDailySummaryFromRealtime(db *gorm.DB, startTime, endTime, channelCode string, productID int) ([]MgHistoricalSummary, error) {
var results []MgHistoricalSummary
qs := db.Model(&MgOrder{}).
Select(`
DATE_FORMAT(mg_order.subscribe_time, '%Y-%m-%d') AS date,
mg_order.product_id,
mg_order.channel_code,
IFNULL(submission_count.submission_count, 0) AS submission_count,
COUNT(CASE WHEN mg_order.is_one_hour_cancel = 1 THEN 1 END) AS new_user_unsub_within_hour,
COUNT(CASE WHEN mg_order.state = 2 AND DATE(mg_order.unsubscribe_time) = DATE(mg_order.subscribe_time) THEN 1 END) AS new_user_unsub_on_day,
COUNT(CASE WHEN mg_order.state = 2 AND TIMESTAMPDIFF(HOUR, mg_order.subscribe_time, mg_order.unsubscribe_time) <= 24 THEN 1 END) AS new_user_unsub_within_24h,
COUNT(*) AS new_user_count,
SUM(CASE WHEN mg_order.state = 2 THEN 1 ELSE 0 END) AS total_new_user_unsub,
CONCAT(ROUND(COUNT(CASE WHEN mg_order.is_one_hour_cancel = 1 THEN 1 END) * 100.0 / NULLIF(COUNT(*), 0), 2), '%') AS new_user_unsub_within_hour_rate,
CONCAT(ROUND(COUNT(CASE WHEN mg_order.state = 2 AND DATE(mg_order.unsubscribe_time) = DATE(mg_order.subscribe_time) THEN 1 END) * 100.0 / NULLIF(COUNT(*), 0), 2), '%') AS new_user_unsub_on_day_rate,
CONCAT(ROUND(COUNT(CASE WHEN mg_order.state = 2 AND TIMESTAMPDIFF(HOUR, mg_order.subscribe_time, mg_order.unsubscribe_time) <= 24 THEN 1 END) * 100.0 / NULLIF(COUNT(*), 0), 2), '%') AS new_user_unsub_within_24h_rate,
CONCAT(ROUND(SUM(CASE WHEN mg_order.state = 2 THEN 1 ELSE 0 END) * 100.0 / NULLIF(COUNT(*), 0), 2), '%') AS total_new_user_unsub_rate,
IFNULL(
CONCAT(
LEAST(
ROUND(COUNT(*) * 100.0 / NULLIF(submission_count.submission_count, 0), 2),
100.00
),
'%'
),
'0.00%'
) AS submission_success_rate
`).
// 使用 Joins 来替代 LeftJoin 进行左连接
Joins(`
LEFT JOIN (
SELECT
channel_code,
DATE(created_at) AS created_date,
COUNT(*) AS submission_count
FROM mg_transaction_log
WHERE verification_code != ''
GROUP BY channel_code, created_date
) AS submission_count
ON submission_count.channel_code = mg_order.channel_code
AND submission_count.created_date = DATE(mg_order.subscribe_time)
`).
Where("mg_order.subscribe_time BETWEEN ? AND ?", startTime, endTime).
Group("DATE(mg_order.subscribe_time), mg_order.product_id, mg_order.channel_code").
Order("mg_order.subscribe_time DESC")
// 🔍 条件筛选:渠道
if channelCode != "" {
qs = qs.Where("mg_order.channel_code = ?", channelCode)
}
// 🔍 条件筛选:产品
if productID != 0 {
qs = qs.Where("mg_order.product_id = ?", productID)
}
err := qs.Find(&results).Error
if err != nil {
return nil, err
}
return results, nil
}

View File

@ -32,9 +32,11 @@ func registerMiGuControlManageRouter(v1 *gin.RouterGroup, authMiddleware *jwt.Gi
api.POST("home/data", apiMiGu.HomepageDataSummary) // 查询首页汇总数据
api.POST("home/revenue_analysis", apiMiGu.CalculateRevenueAnalysis) // 营收分析
api.POST("historical_summary/list", apiMiGu.HistoricalSummaryListOld) // 历史汇总查询
//api.POST("historical_summary/list", apiMiGu.HistoricalSummaryListOld) // 历史汇总查询
//api.POST("order/import", apiMiGu.ImportExcelToMgOrderHandler) // 通过excel导入订单数据
//api.POST("order/import_update", apiMiGu.ImportExcelToMgOrderHandlerUpdate) // 通过excel导入订单退订数据
api.POST("historical_summary/list", apiMiGu.HistoricalSummaryListNewByCatch) // 历史汇总查询(缓存版本)
}
}

View File

@ -100,8 +100,14 @@ func run() error {
fmt.Println("err:", err)
}
} else {
// 每小时更新1次历史汇总数据
err := s.Every(1).Hour().Do(models.UpdateHistoricalSummaryCache)
if err != nil {
fmt.Println("err:", err)
}
// 统一每天定时任务
err := s.Every(1).Day().At("01:30").Do(func() {
err = s.Every(1).Day().At("01:30").Do(func() {
if time.Now().Day() == 1 {
// 每月1号的逻辑
fmt.Println("****每月1号 检测未退订 任务延迟到03:30执行****")

View File

@ -30,7 +30,7 @@ settings:
# 数据库类型 mysql, sqlite3, postgres, sqlserver
driver: mysql
# 数据库连接字符串 mysql 缺省信息 charset=utf8&parseTime=True&loc=Local&timeout=1000ms
source:
source: migu_pro:RhDShkE4WabxXazb@tcp(112.33.14.191:3306)/migu_pro?charset=utf8&parseTime=True&loc=Local&timeout=1000ms
gen:
# 代码生成读取的数据库名称
dbname: dbname