Files
order/dao/order_quarterly_statistics_dao.go

335 lines
9.2 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package dao
import (
"context"
"fmt"
"time"
"order/consts"
"order/model/entity"
"gitee.com/red-future---jilin-g/common/beans"
"gitee.com/red-future---jilin-g/common/mongo"
"go.mongodb.org/mongo-driver/v2/bson"
)
// OrderQuarterlyStatisticsDAO 订单季度统计DAO
type OrderQuarterlyStatisticsDAO struct {
}
var OrderQuarterlyStatisticsDAOInstance = &OrderQuarterlyStatisticsDAO{}
// GenerateStatistics 使用Go代码生成季度统计数据
func (dao *OrderQuarterlyStatisticsDAO) GenerateStatistics(ctx context.Context, tenantID int64, year int, quarter int) (*entity.OrderQuarterlyStatistics, error) {
// 设置时间范围
var startDate time.Time
switch quarter {
case 1:
startDate = time.Date(year, 1, 1, 0, 0, 0, 0, time.Local)
case 2:
startDate = time.Date(year, 4, 1, 0, 0, 0, 0, time.Local)
case 3:
startDate = time.Date(year, 7, 1, 0, 0, 0, 0, time.Local)
case 4:
startDate = time.Date(year, 10, 1, 0, 0, 0, 0, time.Local)
default:
return nil, fmt.Errorf("无效的季度: %d", quarter)
}
endDate := startDate.AddDate(0, 3, 0)
// 查询订单数据
filter := bson.M{
"tenantId": tenantID,
"createdAt": bson.M{
"$gte": startDate,
"$lt": endDate,
},
}
var orders []*entity.OrderBase
err := mongo.DB().Find(ctx, filter, &orders, consts.OrderCollection)
if err != nil {
return nil, fmt.Errorf("查询订单数据失败: %v", err)
}
// 如果没有数据,创建空统计
if len(orders) == 0 {
statistics := &entity.OrderQuarterlyStatistics{
MongoBaseDO: beans.MongoBaseDO{
TenantId: tenantID,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
},
ReportDate: startDate,
Period: fmt.Sprintf("%d-Q%d", year, quarter),
Quarter: quarter,
}
return statistics, nil
}
// 使用Go代码计算统计指标
totalOrders := int64(len(orders))
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)
monthlyOrders := make([]int64, 3)
monthlyAmounts := make([]int64, 3)
for _, order := range orders {
// 计算总金额
totalAmount += order.TotalAmount
// 注意OrderBase结构体没有Status字段状态统计需要通过其他方式获取
// 统计唯一用户
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]++
}
}
// 统计月度分布
month := int(order.CreatedAt.Month())
quarterStartMonth := int(startDate.Month())
if month >= quarterStartMonth && month < quarterStartMonth+3 {
index := month - quarterStartMonth
if index >= 0 && index < 3 {
monthlyOrders[index]++
monthlyAmounts[index] += 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.OrderQuarterlyStatistics{
MongoBaseDO: beans.MongoBaseDO{
TenantId: tenantID,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
},
ReportDate: startDate,
Period: fmt.Sprintf("%d-Q%d", year, quarter),
Quarter: quarter,
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,
MonthlyOrders: monthlyOrders,
MonthlyAmounts: monthlyAmounts,
PeakMonth: dao.findPeakMonth(monthlyOrders),
QuarterOverQuarterGrowth: dao.calculateQuarterOverQuarterGrowth(ctx, tenantID, year, quarter, totalOrders),
YearOverYearGrowth: dao.calculateYearOverYearGrowth(ctx, tenantID, year, totalOrders),
}
return statistics, nil
}
// findTopAsset 找出热门资产
func (dao *OrderQuarterlyStatisticsDAO) 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
}
// findPeakMonth 找出高峰月份
func (dao *OrderQuarterlyStatisticsDAO) findPeakMonth(monthlyOrders []int64) int {
maxMonth := 1
maxCount := int64(0)
for i, count := range monthlyOrders {
if count > maxCount {
maxCount = count
maxMonth = i + 1
}
}
return maxMonth
}
// calculateQuarterOverQuarterGrowth 计算环比增长率
func (dao *OrderQuarterlyStatisticsDAO) calculateQuarterOverQuarterGrowth(ctx context.Context, tenantID int64, year, quarter int, currentOrders int64) float64 {
// 计算上个季度
lastYear := year
lastQuarter := quarter - 1
if lastQuarter == 0 {
lastYear = year - 1
lastQuarter = 4
}
// 查询上个季度的订单数据
var lastQuarterStartDate time.Time
switch lastQuarter {
case 1:
lastQuarterStartDate = time.Date(lastYear, 1, 1, 0, 0, 0, 0, time.Local)
case 2:
lastQuarterStartDate = time.Date(lastYear, 4, 1, 0, 0, 0, 0, time.Local)
case 3:
lastQuarterStartDate = time.Date(lastYear, 7, 1, 0, 0, 0, 0, time.Local)
case 4:
lastQuarterStartDate = time.Date(lastYear, 10, 1, 0, 0, 0, 0, time.Local)
default:
return 0.0
}
lastQuarterEndDate := lastQuarterStartDate.AddDate(0, 3, 0)
filter := bson.M{
"tenantId": tenantID,
"createdAt": bson.M{
"$gte": lastQuarterStartDate,
"$lt": lastQuarterEndDate,
},
}
var lastQuarterOrders []*entity.OrderBase
err := mongo.DB().Find(ctx, filter, &lastQuarterOrders, consts.OrderCollection)
if err != nil || len(lastQuarterOrders) == 0 {
return 0.0
}
lastQuarterOrderCount := int64(len(lastQuarterOrders))
if lastQuarterOrderCount == 0 {
return 0.0
}
// 计算环比增长率
growth := (float64(currentOrders) - float64(lastQuarterOrderCount)) / float64(lastQuarterOrderCount) * 100
return growth
}
// calculateYearOverYearGrowth 计算同比增长率
func (dao *OrderQuarterlyStatisticsDAO) calculateYearOverYearGrowth(ctx context.Context, tenantID int64, year int, currentOrders int64) float64 {
// 查询去年同期的订单数据
lastYear := year - 1
lastYearStartDate := time.Date(lastYear, 1, 1, 0, 0, 0, 0, time.Local)
lastYearEndDate := lastYearStartDate.AddDate(1, 0, 0)
filter := bson.M{
"tenantId": tenantID,
"createdAt": bson.M{
"$gte": lastYearStartDate,
"$lt": lastYearEndDate,
},
}
var lastYearOrders []*entity.OrderBase
err := mongo.DB().Find(ctx, filter, &lastYearOrders, consts.OrderCollection)
if err != nil || len(lastYearOrders) == 0 {
return 0.0
}
lastYearOrderCount := int64(len(lastYearOrders))
if lastYearOrderCount == 0 {
return 0.0
}
// 计算同比增长率
growth := (float64(currentOrders) - float64(lastYearOrderCount)) / float64(lastYearOrderCount) * 100
return growth
}
// Save 保存季度统计数据
func (dao *OrderQuarterlyStatisticsDAO) Save(ctx context.Context, statistics *entity.OrderQuarterlyStatistics) error {
_, err := mongo.DB().Insert(ctx, []interface{}{statistics}, consts.OrderQuarterlyStatisticsCollection)
return err
}
// Update 更新季度统计数据
func (dao *OrderQuarterlyStatisticsDAO) Update(ctx context.Context, statistics *entity.OrderQuarterlyStatistics) error {
filter := bson.M{
"tenantId": statistics.TenantId,
"reportDate": statistics.ReportDate,
}
update := bson.M{
"$set": statistics,
}
_, err := mongo.DB().Update(ctx, filter, update, consts.OrderQuarterlyStatisticsCollection)
return err
}
// GetByTenantAndDate 获取指定租户和日期的季度统计数据
func (dao *OrderQuarterlyStatisticsDAO) GetByTenantAndDate(ctx context.Context, tenantID int64, reportDate time.Time) (*entity.OrderQuarterlyStatistics, error) {
var result entity.OrderQuarterlyStatistics
filter := bson.M{
"tenantId": tenantID,
"reportDate": reportDate,
}
err := mongo.DB().FindOne(ctx, filter, &result, consts.OrderQuarterlyStatisticsCollection)
if err != nil {
return nil, err
}
return &result, nil
}