package service import ( "fmt" "gorm.io/gorm" "psi/database" "psi/models" "psi/utils" "time" ) type StatistTaskService struct{} // GenerateDailyStat 生成每日统计数据(定时任务调用) // GenerateDailyStat 生成每日统计数据(定时任务调用) // GenerateDailyStat 生成每日统计数据(定时任务调用) func (s *StatistTaskService) GenerateDailyStat(db ...*gorm.DB) error { now := time.Now() yesterday := now.AddDate(0, 0, -1) statDateStr := yesterday.Format("20060102") var statDate int64 if _, err := fmt.Sscanf(statDateStr, "%d", &statDate); err != nil { return fmt.Errorf("日期格式化失败: %v", err) } yesterdayStart := time.Date(yesterday.Year(), yesterday.Month(), yesterday.Day(), 0, 0, 0, 0, yesterday.Location()).Unix() yesterdayEnd := time.Date(yesterday.Year(), yesterday.Month(), yesterday.Day(), 23, 59, 59, 0, yesterday.Location()).Unix() mainDB := database.DB if len(db) > 0 && db[0] != nil { mainDB = db[0] } var aboutIDs []int64 if err := mainDB.Model(&models.Employee{}).Where("deleted_at = ?", 0).Distinct().Pluck("about_id", &aboutIDs).Error; err != nil { return fmt.Errorf("查询租户列表失败: %v", err) } if len(aboutIDs) == 0 { utils.InfoLog("work", map[string]interface{}{ "source": "定时任务-生成每日统计", "stat_date": statDate, "message": "没有找到任何租户", }) return nil } for _, aboutID := range aboutIDs { if aboutID == 0 { continue } tenantDB, err := database.GetTenantDB(aboutID) if err != nil { utils.ErrorLog("work", map[string]interface{}{ "source": "定时任务-生成每日统计", "about_id": aboutID, "error": fmt.Sprintf("获取租户数据库失败: %v", err), }) continue } tx := tenantDB.Begin() func() { defer func() { if r := recover(); r != nil { tx.Rollback() } }() _, err := s.generateDashboardDailyStat(tx, statDate, yesterdayStart, yesterdayEnd) if err != nil { tx.Rollback() utils.ErrorLog("work", map[string]interface{}{ "source": "定时任务-生成每日统计", "about_id": aboutID, "stat_date": statDate, "error": fmt.Sprintf("生成全局统计失败: %v", err), }) return } if err := s.generateUserDailyStat(tx, aboutID, statDate, yesterdayStart, yesterdayEnd); err != nil { tx.Rollback() utils.ErrorLog("work", map[string]interface{}{ "source": "定时任务-生成每日统计", "about_id": aboutID, "stat_date": statDate, "error": fmt.Sprintf("生成用户统计失败: %v", err), }) return } if err := tx.Commit().Error; err != nil { utils.ErrorLog("work", map[string]interface{}{ "source": "定时任务-生成每日统计", "about_id": aboutID, "stat_date": statDate, "error": fmt.Sprintf("提交事务失败: %v", err), }) return } utils.InfoLog("work", map[string]interface{}{ "source": "定时任务-生成每日统计", "about_id": aboutID, "stat_date": statDate, "message": fmt.Sprintf("成功生成%s的统计数据", yesterday.Format("2006-01-02")), }) }() } return nil } // generateDashboardDailyStat 生成全局每日统计 func (s *StatistTaskService) generateDashboardDailyStat(tx *gorm.DB, statDate int64, startDate, endDate int64) (*models.DashboardDailyStat, error) { var existingStat models.DashboardDailyStat err := tx.Where("stat_date = ? AND is_del = ?", statDate, 0).First(&existingStat).Error if err == nil { if err := tx.Model(&models.DashboardDailyStat{}).Where("id = ?", existingStat.ID).Update("is_del", 1).Error; err != nil { return nil, err } } var totalReceivingNum, totalOutboundNum int64 tx.Model(&models.Statist{}). Where("stat_date <= ? AND is_del = ?", endDate, 0). Select("COALESCE(SUM(receiving_num), 0), COALESCE(SUM(outbound_num), 0)"). Row().Scan(&totalReceivingNum, &totalOutboundNum) var todayInbound, todayOutbound int64 tx.Model(&models.Statist{}). Where("stat_date >= ? AND stat_date <= ? AND is_del = ?", startDate, endDate, 0). Select("COALESCE(SUM(receiving_num), 0), COALESCE(SUM(outbound_num), 0)"). Row().Scan(&todayInbound, &todayOutbound) beforeYesterdayStart := time.Date(time.Unix(startDate, 0).Year(), time.Unix(startDate, 0).Month(), time.Unix(startDate, 0).Day()-1, 0, 0, 0, 0, time.Unix(startDate, 0).Location()).Unix() beforeYesterdayEnd := time.Date(time.Unix(startDate, 0).Year(), time.Unix(startDate, 0).Month(), time.Unix(startDate, 0).Day()-1, 23, 59, 59, 0, time.Unix(startDate, 0).Location()).Unix() var yesterdayInbound, yesterdayOutbound int64 tx.Model(&models.Statist{}). Where("stat_date >= ? AND stat_date <= ? AND is_del = ?", beforeYesterdayStart, beforeYesterdayEnd, 0). Select("COALESCE(SUM(receiving_num), 0), COALESCE(SUM(outbound_num), 0)"). Row().Scan(&yesterdayInbound, &yesterdayOutbound) var totalSalesCount int64 tx.Model(&models.SalesOrder{}). Where("created_at <= ? AND is_del = ?", endDate, 0). Count(&totalSalesCount) var todaySalesCount int64 tx.Model(&models.SalesOrder{}). Where("created_at >= ? AND created_at <= ? AND is_del = ?", startDate, endDate, 0). Count(&todaySalesCount) var yesterdaySalesCount int64 tx.Model(&models.SalesOrder{}). Where("created_at >= ? AND created_at <= ? AND is_del = ?", beforeYesterdayStart, beforeYesterdayEnd, 0). Count(&yesterdaySalesCount) var productTotal int64 tx.Model(&models.Product{}). Where("is_del = ?", 0). Count(&productTotal) var inventoryTotal int64 tx.Model(&models.Inventory{}). Where("is_del = ?", 0). Select("COALESCE(SUM(quantity), 0)"). Row().Scan(&inventoryTotal) now := time.Now().Unix() dashboardStat := &models.DashboardDailyStat{ StatDate: statDate, TotalReceivingNum: totalReceivingNum, TotalOutboundNum: totalOutboundNum, TotalSalesCount: totalSalesCount, ProductTotal: productTotal, InventoryTotal: inventoryTotal, TodayInbound: todayInbound, TodayOutbound: todayOutbound, YesterdayInbound: yesterdayInbound, YesterdayOutbound: yesterdayOutbound, TodaySalesCount: todaySalesCount, YesterdaySalesCount: yesterdaySalesCount, CreatedAt: now, UpdatedAt: now, IsDel: 0, } if err := tx.Create(dashboardStat).Error; err != nil { return nil, err } return dashboardStat, nil } // generateUserDailyStat 生成用户每日统计 func (s *StatistTaskService) generateUserDailyStat(tx *gorm.DB, aboutID int64, statDate int64, startDate, endDate int64) error { type UserStatGroup struct { UserID int64 `gorm:"column:user_id"` UserName string `gorm:"column:user_name"` ReceivingNum int64 `gorm:"column:receiving_num"` OutboundNum int64 `gorm:"column:outbound_num"` SalesCount int64 `gorm:"column:sales_count"` } var userStats []UserStatGroup tx.Table("employee"). Select(` employee.id as user_id, employee.username as user_name, COALESCE((SELECT SUM(receiving_num) FROM statist WHERE create_by = employee.id AND stat_date >= ? AND stat_date <= ? AND is_del = ?), 0) as receiving_num, COALESCE((SELECT SUM(outbound_num) FROM statist WHERE create_by = employee.id AND stat_date >= ? AND stat_date <= ? AND is_del = ?), 0) as outbound_num, COALESCE((SELECT COUNT(*) FROM sales_order WHERE create_by = employee.id AND created_at >= ? AND created_at <= ? AND is_del = ?), 0) as sales_count `, startDate, endDate, 0, startDate, endDate, 0, startDate, endDate, 0). Where("employee.about_id = ? AND employee.is_del = ?", aboutID, 0). Scan(&userStats) if len(userStats) == 0 { return nil } now := time.Now().Unix() for _, userStat := range userStats { var existingStat models.UserDailyStat err := tx.Where("user_id = ? AND stat_date = ? AND is_del = ?", userStat.UserID, statDate, 0).First(&existingStat).Error if err == nil { if err := tx.Model(&models.UserDailyStat{}).Where("id = ?", existingStat.ID).Update("is_del", 1).Error; err != nil { continue } } newStat := &models.UserDailyStat{ UserID: userStat.UserID, UserName: userStat.UserName, StatDate: statDate, ReceivingNum: userStat.ReceivingNum, OutboundNum: userStat.OutboundNum, SalesCount: userStat.SalesCount, CreatedAt: now, UpdatedAt: now, IsDel: 0, } if err := tx.Create(newStat).Error; err != nil { utils.ErrorLog("work", map[string]interface{}{ "source": "生成用户统计", "user_id": userStat.UserID, "error": fmt.Sprintf("创建用户统计记录失败: %v", err), }) } } return nil }