1、优化用户留存记录(按天)接口,解决查询阻塞问题;

This commit is contained in:
chenlin 2025-02-18 09:53:56 +08:00
parent 401eaaaaba
commit 5ff7aeab26

View File

@ -1550,8 +1550,28 @@ func (e MiGuDeployService) UserDayRetentionList(c *gin.Context) {
// 使用 goroutine 处理并发查询
var wg sync.WaitGroup
// 创建一个 channel 来接收每一天的查询结果
resultChan := make(chan models.MgUserDayRetention, 100)
//resultChan := make(chan models.MgUserDayRetention, 100)
// 计算预计查询天数(或月份)
var totalDays int
if req.OnlyFirstDay {
// 当前月的1号
now := time.Now()
currentMonthFirst := time.Date(now.Year(), now.Month(), 1, 0, 0, 0, 0, now.Location())
// 计算从 nextMonth 到 currentMonthFirst 包含的1号数量
totalDays = countFirstDays(nextMonth, currentMonthFirst)
} else {
// 按天查询:计算两个日期之间的天数
startDateTime, _ := time.Parse("2006-01-02", startDate)
currentTime, _ := time.Parse("2006-01-02", currentDate)
totalDays = int(currentTime.Sub(startDateTime).Hours()/24) + 1
if totalDays <= 0 {
totalDays = 1
}
}
resultChan := make(chan models.MgUserDayRetention, totalDays)
// 控制并发数的最大值,避免数据库过载
sem := make(chan struct{}, 10) // 同时最多 10 个 goroutine 执行
@ -1577,15 +1597,16 @@ func (e MiGuDeployService) UserDayRetentionList(c *gin.Context) {
var retainedUserCount int64
var unsubOnDay int64
var localErr error // 使用局部变量
// 查询该天的留存用户数
err = e.Orm.Model(&models.MgOrder{}).
localErr = e.Orm.Model(&models.MgOrder{}).
Where("state = 1 AND created_at between ? and ?", currentMonthFirstDay, currentMonthNextFirstDay).
Or("state = 2 AND unsubscribe_time > ? AND created_at between ? and ?", date+" 23:59:59", currentMonthFirstDay, currentMonthNextFirstDay).
Count(&retainedUserCount).Error
// 查询该天的退订用户数
err = e.Orm.Table("mg_order").
localErr = e.Orm.Table("mg_order").
Where("unsubscribe_time >= ?", date+" 00:00:00").
Where("unsubscribe_time <= ?", date+" 23:59:59").
Where("created_at >= ?", currentMonthFirstDay).
@ -1593,8 +1614,8 @@ func (e MiGuDeployService) UserDayRetentionList(c *gin.Context) {
Where("state = 2"). // 状态为2表示退订
Count(&unsubOnDay).Error
if err != nil {
e.Logger.Error(err)
if localErr != nil {
e.Logger.Error(localErr)
return
}
@ -1636,6 +1657,7 @@ func (e MiGuDeployService) UserDayRetentionList(c *gin.Context) {
var retainedUserCount int64
var unsubOnDay int64
var localErr error // 使用局部变量
// 查询该天的留存用户数
err = e.Orm.Model(&models.MgOrder{}).
@ -1643,8 +1665,10 @@ func (e MiGuDeployService) UserDayRetentionList(c *gin.Context) {
Or("state = 2 AND unsubscribe_time > ? AND created_at between ? and ?", date+" 23:59:59", currentMonthFirstDay, currentMonthNextFirstDay).
Count(&retainedUserCount).Error
//retainedUserCount, localErr = getRetentionForDay(date, e.Orm, currentMonthFirstDay, currentMonthNextFirstDay)
// 查询该天的退订用户数
err = e.Orm.Table("mg_order").
localErr = e.Orm.Table("mg_order").
Where("unsubscribe_time >= ?", date+" 00:00:00").
Where("unsubscribe_time <= ?", date+" 23:59:59").
Where("created_at >= ?", currentMonthFirstDay).
@ -1652,8 +1676,8 @@ func (e MiGuDeployService) UserDayRetentionList(c *gin.Context) {
Where("state = 2"). // 状态为2表示退订
Count(&unsubOnDay).Error
if err != nil {
e.Logger.Error(err)
if localErr != nil {
e.Logger.Error(localErr)
return
}
@ -1716,6 +1740,39 @@ func (e MiGuDeployService) UserDayRetentionList(c *gin.Context) {
e.OK(resp, "success")
}
// monthsBetween 返回 start 和 end 之间完整的月份数。
// 如果 end 的日期在 start 的日期之前(不满一个月),则不计入。
func monthsBetween(start, end time.Time) int {
// 保证 start 在前end 在后
if start.After(end) {
start, end = end, start
}
y1, m1, d1 := start.Date()
y2, m2, d2 := end.Date()
// 计算年份和月份的差异
months := (y2-y1)*12 + int(m2-m1)
// 如果 end 的日子小于 start 的日子,说明还没有满一个月
if d2 < d1 {
months--
}
return months
}
// countFirstDays 返回从 start 到 end均为每月1号之间包含的月份数。
// 例如start = 2024-11-01, end = 2025-02-01则返回 4分别是 2024-11-01, 2024-12-01, 2025-01-01, 2025-02-01
func countFirstDays(start, end time.Time) int {
// 如果 start 在 end 之后,返回 0
if start.After(end) {
return 1
}
y1, m1, _ := start.Date()
y2, m2, _ := end.Date()
// 计算月份差,注意此处 start 和 end 都应为当月1号
return (y2-y1)*12 + int(m2-m1) + 1
}
// SysChannelList 查询系统渠道编码
// @Summary 查询系统渠道编码
// @Tags 2024-咪咕-管理后台
@ -2081,25 +2138,25 @@ func (e MiGuDeployService) CalculateRevenueAnalysis(c *gin.Context) {
query = query.Where("channel_code = ?", req.Channel)
}
// 获取每个月的新用户数和有效用户数以下代码是按退订时间超过1天来统计数据
//// 获取每个月的新用户数和有效用户数以下代码是按退订时间超过1天来统计数据
var result []models.MonthlyRetention
err = query.
Select(`DATE_FORMAT(subscribe_time, '%Y-%m') AS month,
COUNT(DISTINCT phone_number) AS new_user_count,
COUNT(DISTINCT CASE
WHEN unsubscribe_time IS NULL OR TIMESTAMPDIFF(HOUR, subscribe_time, unsubscribe_time) > 24
THEN phone_number END) AS valid_users_count`).
COUNT(DISTINCT phone_number) AS new_user_count,
COUNT(DISTINCT CASE
WHEN unsubscribe_time IS NULL OR TIMESTAMPDIFF(HOUR, subscribe_time, unsubscribe_time) > 24
THEN phone_number END) AS valid_users_count`).
Group("month").
Order("month").
Find(&result).Error
// 以下代码是按非1小时退订来统计数据
////以下代码是按非1小时退订来统计数据
//err = query.
// Select(`DATE_FORMAT(subscribe_time, '%Y-%m') AS month,
// COUNT(DISTINCT phone_number) AS new_user_count,
// COUNT(DISTINCT CASE
// WHEN is_one_hour_cancel != 1
// THEN phone_number END) AS valid_users_count`).
// COUNT(DISTINCT phone_number) AS new_user_count,
// COUNT(DISTINCT CASE
// WHEN is_one_hour_cancel != 1
// THEN phone_number END) AS valid_users_count`).
// Group("month").
// Order("month").
// Find(&result).Error