diff --git a/app/admin/apis/migumanage/migu_admin.go b/app/admin/apis/migumanage/migu_admin.go index 192873a..fc004d9 100644 --- a/app/admin/apis/migumanage/migu_admin.go +++ b/app/admin/apis/migumanage/migu_admin.go @@ -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