2025-12-12 16:20:47 +08:00
|
|
|
|
package dao
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
|
"context"
|
|
|
|
|
|
"fmt"
|
|
|
|
|
|
"time"
|
|
|
|
|
|
|
|
|
|
|
|
"order/consts"
|
|
|
|
|
|
"order/model/entity"
|
|
|
|
|
|
|
2025-12-31 11:18:52 +08:00
|
|
|
|
"gitee.com/red-future---jilin-g/common/beans"
|
2025-12-30 11:03:11 +08:00
|
|
|
|
"gitee.com/red-future---jilin-g/common/mongo"
|
|
|
|
|
|
|
2025-12-12 16:20:47 +08:00
|
|
|
|
"go.mongodb.org/mongo-driver/v2/bson"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
// OrderQuarterlyStatisticsDAO 订单季度统计DAO
|
2025-12-29 15:58:48 +08:00
|
|
|
|
type OrderQuarterlyStatisticsDAO struct {
|
|
|
|
|
|
}
|
2025-12-12 16:20:47 +08:00
|
|
|
|
|
2025-12-30 11:03:11 +08:00
|
|
|
|
var OrderQuarterlyStatisticsDAOInstance = &OrderQuarterlyStatisticsDAO{}
|
2025-12-12 16:20:47 +08:00
|
|
|
|
|
|
|
|
|
|
// 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
|
2025-12-30 11:03:11 +08:00
|
|
|
|
err := mongo.DB().Find(ctx, filter, &orders, consts.OrderCollection)
|
2025-12-12 16:20:47 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, fmt.Errorf("查询订单数据失败: %v", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 如果没有数据,创建空统计
|
|
|
|
|
|
if len(orders) == 0 {
|
|
|
|
|
|
statistics := &entity.OrderQuarterlyStatistics{
|
2025-12-31 11:18:52 +08:00
|
|
|
|
MongoBaseDO: beans.MongoBaseDO{
|
2025-12-12 16:20:47 +08:00
|
|
|
|
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{
|
2025-12-31 11:18:52 +08:00
|
|
|
|
MongoBaseDO: beans.MongoBaseDO{
|
2025-12-12 16:20:47 +08:00
|
|
|
|
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
|
2025-12-30 11:03:11 +08:00
|
|
|
|
err := mongo.DB().Find(ctx, filter, &lastQuarterOrders, consts.OrderCollection)
|
2025-12-12 16:20:47 +08:00
|
|
|
|
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
|
2025-12-30 11:03:11 +08:00
|
|
|
|
err := mongo.DB().Find(ctx, filter, &lastYearOrders, consts.OrderCollection)
|
2025-12-12 16:20:47 +08:00
|
|
|
|
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 {
|
2025-12-30 11:03:11 +08:00
|
|
|
|
_, err := mongo.DB().Insert(ctx, []interface{}{statistics}, consts.OrderQuarterlyStatisticsCollection)
|
2025-12-12 16:20:47 +08:00
|
|
|
|
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,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-30 11:03:11 +08:00
|
|
|
|
_, err := mongo.DB().Update(ctx, filter, update, consts.OrderQuarterlyStatisticsCollection)
|
2025-12-12 16:20:47 +08:00
|
|
|
|
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,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-30 11:03:11 +08:00
|
|
|
|
err := mongo.DB().FindOne(ctx, filter, &result, consts.OrderQuarterlyStatisticsCollection)
|
2025-12-12 16:20:47 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return &result, nil
|
|
|
|
|
|
}
|