package dao import ( "context" "fmt" "time" "order/consts" "order/model/entity" "gitea.com/red-future/common/beans" "gitea.com/red-future/common/db/mongo" "go.mongodb.org/mongo-driver/v2/bson" ) // OrderMonthlyStatisticsDAO 订单月统计DAO type OrderMonthlyStatisticsDAO struct { } var OrderMonthlyStatisticsDAOInstance = &OrderMonthlyStatisticsDAO{} // GenerateStatistics 使用Go代码生成月统计数据 func (dao *OrderMonthlyStatisticsDAO) GenerateStatistics(ctx context.Context, tenantID int64, year int, month int) (*entity.OrderMonthlyStatistics, error) { // 设置时间范围 startDate := time.Date(year, time.Month(month), 1, 0, 0, 0, 0, time.Local) endDate := startDate.AddDate(0, 1, 0) // 查询所有状态的订单数据 var orders []*entity.OrderBase // 查询待支付订单 var pendingOrders []*entity.OrderPending filter := bson.M{ "tenantId": tenantID, "createdAt": bson.M{ "$gte": startDate, "$lt": endDate, }, } err := mongo.DB().Find(ctx, filter, &pendingOrders, consts.OrderPendingCollection) if err != nil { return nil, fmt.Errorf("查询待支付订单数据失败: %v", err) } // 查询已支付订单 var paidOrderEntities []*entity.OrderPaid err = mongo.DB().Find(ctx, filter, &paidOrderEntities, consts.OrderPaidCollection) if err != nil { return nil, fmt.Errorf("查询已支付订单数据失败: %v", err) } // 查询已发货订单 var shippedOrders []*entity.OrderShipped err = mongo.DB().Find(ctx, filter, &shippedOrders, consts.OrderShippedCollection) if err != nil { return nil, fmt.Errorf("查询已发货订单数据失败: %v", err) } // 查询已完成订单 var completedOrderEntities []*entity.OrderCompleted err = mongo.DB().Find(ctx, filter, &completedOrderEntities, consts.OrderCompletedCollection) if err != nil { return nil, fmt.Errorf("查询已完成订单数据失败: %v", err) } // 合并所有订单到基础订单结构 for _, order := range pendingOrders { orders = append(orders, &entity.OrderBase{ MongoBaseDO: order.MongoBaseDO, OrderNo: order.OrderNo, UserID: order.UserID, TotalAmount: order.TotalAmount, PayAmount: order.PayAmount, OrderType: order.OrderType, Subject: order.Subject, Description: order.Description, OrderItems: order.OrderItems, ExpiredAt: order.ExpiredAt, }) } for _, order := range paidOrderEntities { orders = append(orders, &entity.OrderBase{ MongoBaseDO: order.MongoBaseDO, OrderNo: order.OrderNo, UserID: order.UserID, TotalAmount: order.TotalAmount, PayAmount: order.PayAmount, OrderType: order.OrderType, Subject: order.Subject, Description: order.Description, OrderItems: order.OrderItems, ExpiredAt: order.ExpiredAt, }) } for _, order := range shippedOrders { orders = append(orders, &entity.OrderBase{ MongoBaseDO: order.MongoBaseDO, OrderNo: order.OrderNo, UserID: order.UserID, TotalAmount: order.TotalAmount, PayAmount: order.PayAmount, OrderType: order.OrderType, Subject: order.Subject, Description: order.Description, OrderItems: order.OrderItems, ExpiredAt: order.ExpiredAt, }) } for _, order := range completedOrderEntities { orders = append(orders, &entity.OrderBase{ MongoBaseDO: order.MongoBaseDO, OrderNo: order.OrderNo, UserID: order.UserID, TotalAmount: order.TotalAmount, PayAmount: order.PayAmount, OrderType: order.OrderType, Subject: order.Subject, Description: order.Description, OrderItems: order.OrderItems, ExpiredAt: order.ExpiredAt, }) } // 如果没有数据,创建空统计 if len(orders) == 0 { statistics := &entity.OrderMonthlyStatistics{ MongoBaseDO: beans.MongoBaseDO{ TenantId: tenantID, CreatedAt: time.Now(), UpdatedAt: time.Now(), }, ReportDate: startDate, Period: startDate.Format("2006-01"), } return statistics, nil } // 使用Go代码计算统计指标 totalOrders := int64(0) totalAmount := int64(0) completedOrders := int64(0) cancelledOrders := int64(0) paidOrders := int64(0) totalItems := int64(0) uniqueUsers := make(map[int64]bool) uniqueAssets := make(map[string]bool) assetCounts := make(map[string]int64) dailyOrders := make([]int64, 31) dailyAmounts := make([]int64, 31) for _, order := range orders { // 计算总金额 totalAmount += order.TotalAmount // 统计订单状态(基于订单来源集合推断) // 由于订单已按状态拆分,这里可以统计总订单数 totalOrders++ // 统计唯一用户 uniqueUsers[order.UserID] = true // 统计商品信息 if order.OrderItems != nil { totalItems += int64(len(order.OrderItems)) for _, item := range order.OrderItems { uniqueAssets[item.AssetID] = true assetCounts[item.AssetID]++ } } // 统计每日分布 day := order.CreatedAt.Day() if day >= 1 && day <= 31 { dailyOrders[day-1]++ dailyAmounts[day-1] += order.TotalAmount } } // 计算业务指标 var averageOrderValue int64 if totalOrders > 0 { averageOrderValue = totalAmount / totalOrders } var paymentRate, completionRate float64 if totalOrders > 0 { paymentRate = float64(paidOrders) / float64(totalOrders) * 100 completionRate = float64(completedOrders) / float64(totalOrders) * 100 } // 获取热门资产 topAssetID, topAssetName, topAssetCount := dao.findTopAsset(assetCounts, orders) statistics := &entity.OrderMonthlyStatistics{ MongoBaseDO: beans.MongoBaseDO{ TenantId: tenantID, CreatedAt: time.Now(), UpdatedAt: time.Now(), }, ReportDate: startDate, Period: startDate.Format("2006-01"), TotalOrders: totalOrders, CompletedOrders: completedOrders, CancelledOrders: cancelledOrders, PaidOrders: paidOrders, TotalAmount: totalAmount, PaidAmount: totalAmount, NetAmount: totalAmount, AverageOrderValue: averageOrderValue, PaymentRate: paymentRate, CompletionRate: completionRate, UniqueUsers: int64(len(uniqueUsers)), NewUsers: 0, ReturningUsers: 0, TotalItems: totalItems, UniqueAssets: int64(len(uniqueAssets)), TopAssetID: topAssetID, TopAssetName: topAssetName, TopAssetCount: topAssetCount, DailyOrders: dailyOrders, DailyAmounts: dailyAmounts, PeakDay: dao.findPeakDay(dailyOrders), MonthOverMonthGrowth: dao.calculateMonthOverMonthGrowth(ctx, tenantID, startDate, totalOrders), } return statistics, nil } // findTopAsset 找出热门资产 func (dao *OrderMonthlyStatisticsDAO) findTopAsset(assetCounts map[string]int64, orders []*entity.OrderBase) (string, string, int64) { if len(assetCounts) == 0 { return "", "", 0 } var topAssetID string var maxCount int64 for assetID, count := range assetCounts { if count > maxCount { maxCount = count topAssetID = assetID } } // 查找资产名称 var topAssetName string for _, order := range orders { if order.OrderItems != nil { for _, item := range order.OrderItems { if item.AssetID == topAssetID { topAssetName = item.AssetName break } } } if topAssetName != "" { break } } return topAssetID, topAssetName, maxCount } // findPeakDay 找出高峰日 func (dao *OrderMonthlyStatisticsDAO) findPeakDay(dailyOrders []int64) int { maxDay := 1 maxCount := int64(0) for day, count := range dailyOrders { if count > maxCount { maxCount = count maxDay = day + 1 } } return maxDay } // calculateMonthOverMonthGrowth 计算环比增长率 func (dao *OrderMonthlyStatisticsDAO) calculateMonthOverMonthGrowth(ctx context.Context, tenantID int64, currentMonth time.Time, currentOrders int64) float64 { lastMonth := currentMonth.AddDate(0, -1, 0) lastMonthEnd := lastMonth.AddDate(0, 1, -1) // 查询上个月的订单数据 filter := bson.M{ "tenantId": tenantID, "createdAt": bson.M{ "$gte": lastMonth, "$lt": lastMonthEnd, }, } var lastMonthOrders []*entity.OrderBase err := mongo.DB().Find(ctx, filter, &lastMonthOrders, consts.OrderCollection) if err != nil || len(lastMonthOrders) == 0 { return 0.0 } lastMonthOrderCount := int64(len(lastMonthOrders)) if lastMonthOrderCount == 0 { return 0.0 } // 计算环比增长率 growth := (float64(currentOrders) - float64(lastMonthOrderCount)) / float64(lastMonthOrderCount) * 100 return growth } // Save 保存月统计数据 func (dao *OrderMonthlyStatisticsDAO) Save(ctx context.Context, statistics *entity.OrderMonthlyStatistics) error { _, err := mongo.DB().Insert(ctx, []interface{}{statistics}, consts.OrderMonthlyStatisticsCollection) return err } // Update 更新月统计数据 func (dao *OrderMonthlyStatisticsDAO) Update(ctx context.Context, statistics *entity.OrderMonthlyStatistics) error { filter := bson.M{ "tenantId": statistics.TenantId, "reportDate": statistics.ReportDate, } update := bson.M{ "$set": statistics, } _, err := mongo.DB().Update(ctx, filter, update, consts.OrderMonthlyStatisticsCollection) return err } // GetByTenantAndDate 获取指定租户和日期的月统计数据 func (dao *OrderMonthlyStatisticsDAO) GetByTenantAndDate(ctx context.Context, tenantID int64, reportDate time.Time) (*entity.OrderMonthlyStatistics, error) { var result entity.OrderMonthlyStatistics filter := bson.M{ "tenantId": tenantID, "reportDate": reportDate, } err := mongo.DB().FindOne(ctx, filter, &result, consts.OrderMonthlyStatisticsCollection) if err != nil { return nil, err } return &result, nil }