new
This commit is contained in:
@@ -2,6 +2,7 @@ package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
"tyapi-server/internal/domains/api/entities"
|
||||
"tyapi-server/internal/domains/api/repositories"
|
||||
@@ -228,6 +229,61 @@ func (r *GormApiCallRepository) CountByUserId(ctx context.Context, userId string
|
||||
return r.CountWhere(ctx, &entities.ApiCall{}, "user_id = ?", userId)
|
||||
}
|
||||
|
||||
// CountByUserIdAndDateRange 按用户ID和日期范围统计API调用次数
|
||||
func (r *GormApiCallRepository) CountByUserIdAndDateRange(ctx context.Context, userId string, startDate, endDate time.Time) (int64, error) {
|
||||
return r.CountWhere(ctx, &entities.ApiCall{}, "user_id = ? AND created_at >= ? AND created_at < ?", userId, startDate, endDate)
|
||||
}
|
||||
|
||||
// GetDailyStatsByUserId 获取用户每日API调用统计
|
||||
func (r *GormApiCallRepository) GetDailyStatsByUserId(ctx context.Context, userId string, startDate, endDate time.Time) ([]map[string]interface{}, error) {
|
||||
var results []map[string]interface{}
|
||||
|
||||
// 构建SQL查询 - 使用PostgreSQL语法,使用具体的日期范围
|
||||
sql := `
|
||||
SELECT
|
||||
DATE(created_at) as date,
|
||||
COUNT(*) as calls
|
||||
FROM api_calls
|
||||
WHERE user_id = $1
|
||||
AND DATE(created_at) >= $2
|
||||
AND DATE(created_at) <= $3
|
||||
GROUP BY DATE(created_at)
|
||||
ORDER BY date ASC
|
||||
`
|
||||
|
||||
err := r.GetDB(ctx).Raw(sql, userId, startDate.Format("2006-01-02"), endDate.Format("2006-01-02")).Scan(&results).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
||||
// GetMonthlyStatsByUserId 获取用户每月API调用统计
|
||||
func (r *GormApiCallRepository) GetMonthlyStatsByUserId(ctx context.Context, userId string, startDate, endDate time.Time) ([]map[string]interface{}, error) {
|
||||
var results []map[string]interface{}
|
||||
|
||||
// 构建SQL查询 - 使用PostgreSQL语法,使用具体的日期范围
|
||||
sql := `
|
||||
SELECT
|
||||
TO_CHAR(created_at, 'YYYY-MM') as month,
|
||||
COUNT(*) as calls
|
||||
FROM api_calls
|
||||
WHERE user_id = $1
|
||||
AND created_at >= $2
|
||||
AND created_at <= $3
|
||||
GROUP BY TO_CHAR(created_at, 'YYYY-MM')
|
||||
ORDER BY month ASC
|
||||
`
|
||||
|
||||
err := r.GetDB(ctx).Raw(sql, userId, startDate, endDate).Scan(&results).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func (r *GormApiCallRepository) FindByTransactionId(ctx context.Context, transactionId string) (*entities.ApiCall, error) {
|
||||
var call entities.ApiCall
|
||||
err := r.FindOne(ctx, &call, "transaction_id = ?", transactionId)
|
||||
@@ -329,4 +385,135 @@ func (r *GormApiCallRepository) ListWithFiltersAndProductName(ctx context.Contex
|
||||
}
|
||||
|
||||
return productNameMap, calls, total, nil
|
||||
}
|
||||
|
||||
// GetSystemTotalCalls 获取系统总API调用次数
|
||||
func (r *GormApiCallRepository) GetSystemTotalCalls(ctx context.Context) (int64, error) {
|
||||
var count int64
|
||||
err := r.GetDB(ctx).Model(&entities.ApiCall{}).Count(&count).Error
|
||||
return count, err
|
||||
}
|
||||
|
||||
// GetSystemCallsByDateRange 获取系统指定时间范围内的API调用次数
|
||||
func (r *GormApiCallRepository) GetSystemCallsByDateRange(ctx context.Context, startDate, endDate time.Time) (int64, error) {
|
||||
var count int64
|
||||
err := r.GetDB(ctx).Model(&entities.ApiCall{}).
|
||||
Where("created_at >= ? AND created_at <= ?", startDate, endDate).
|
||||
Count(&count).Error
|
||||
return count, err
|
||||
}
|
||||
|
||||
// GetSystemDailyStats 获取系统每日API调用统计
|
||||
func (r *GormApiCallRepository) GetSystemDailyStats(ctx context.Context, startDate, endDate time.Time) ([]map[string]interface{}, error) {
|
||||
var results []map[string]interface{}
|
||||
|
||||
sql := `
|
||||
SELECT
|
||||
DATE(created_at) as date,
|
||||
COUNT(*) as calls
|
||||
FROM api_calls
|
||||
WHERE DATE(created_at) >= $1
|
||||
AND DATE(created_at) <= $2
|
||||
GROUP BY DATE(created_at)
|
||||
ORDER BY date ASC
|
||||
`
|
||||
|
||||
err := r.GetDB(ctx).Raw(sql, startDate.Format("2006-01-02"), endDate.Format("2006-01-02")).Scan(&results).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
||||
// GetSystemMonthlyStats 获取系统每月API调用统计
|
||||
func (r *GormApiCallRepository) GetSystemMonthlyStats(ctx context.Context, startDate, endDate time.Time) ([]map[string]interface{}, error) {
|
||||
var results []map[string]interface{}
|
||||
|
||||
sql := `
|
||||
SELECT
|
||||
TO_CHAR(created_at, 'YYYY-MM') as month,
|
||||
COUNT(*) as calls
|
||||
FROM api_calls
|
||||
WHERE created_at >= $1
|
||||
AND created_at <= $2
|
||||
GROUP BY TO_CHAR(created_at, 'YYYY-MM')
|
||||
ORDER BY month ASC
|
||||
`
|
||||
|
||||
err := r.GetDB(ctx).Raw(sql, startDate, endDate).Scan(&results).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
||||
// GetApiPopularityRanking 获取API受欢迎程度排行榜
|
||||
func (r *GormApiCallRepository) GetApiPopularityRanking(ctx context.Context, period string, limit int) ([]map[string]interface{}, error) {
|
||||
var sql string
|
||||
var args []interface{}
|
||||
|
||||
switch period {
|
||||
case "today":
|
||||
sql = `
|
||||
SELECT
|
||||
p.id as product_id,
|
||||
p.name as api_name,
|
||||
p.description as api_description,
|
||||
COUNT(ac.id) as call_count
|
||||
FROM product p
|
||||
LEFT JOIN api_calls ac ON p.id = ac.product_id
|
||||
AND DATE(ac.created_at) = CURRENT_DATE
|
||||
WHERE p.deleted_at IS NULL
|
||||
GROUP BY p.id, p.name, p.description
|
||||
HAVING COUNT(ac.id) > 0
|
||||
ORDER BY call_count DESC
|
||||
LIMIT $1
|
||||
`
|
||||
args = []interface{}{limit}
|
||||
case "month":
|
||||
sql = `
|
||||
SELECT
|
||||
p.id as product_id,
|
||||
p.name as api_name,
|
||||
p.description as api_description,
|
||||
COUNT(ac.id) as call_count
|
||||
FROM product p
|
||||
LEFT JOIN api_calls ac ON p.id = ac.product_id
|
||||
AND DATE_TRUNC('month', ac.created_at) = DATE_TRUNC('month', CURRENT_DATE)
|
||||
WHERE p.deleted_at IS NULL
|
||||
GROUP BY p.id, p.name, p.description
|
||||
HAVING COUNT(ac.id) > 0
|
||||
ORDER BY call_count DESC
|
||||
LIMIT $1
|
||||
`
|
||||
args = []interface{}{limit}
|
||||
case "total":
|
||||
sql = `
|
||||
SELECT
|
||||
p.id as product_id,
|
||||
p.name as api_name,
|
||||
p.description as api_description,
|
||||
COUNT(ac.id) as call_count
|
||||
FROM product p
|
||||
LEFT JOIN api_calls ac ON p.id = ac.product_id
|
||||
WHERE p.deleted_at IS NULL
|
||||
GROUP BY p.id, p.name, p.description
|
||||
HAVING COUNT(ac.id) > 0
|
||||
ORDER BY call_count DESC
|
||||
LIMIT $1
|
||||
`
|
||||
args = []interface{}{limit}
|
||||
default:
|
||||
return nil, fmt.Errorf("不支持的时间周期: %s", period)
|
||||
}
|
||||
|
||||
var results []map[string]interface{}
|
||||
err := r.GetDB(ctx).Raw(sql, args...).Scan(&results).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
@@ -3,6 +3,8 @@ package repositories
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"strings"
|
||||
"time"
|
||||
"tyapi-server/internal/domains/finance/entities"
|
||||
domain_finance_repo "tyapi-server/internal/domains/finance/repositories"
|
||||
"tyapi-server/internal/shared/database"
|
||||
@@ -110,7 +112,14 @@ func (r *GormRechargeRecordRepository) List(ctx context.Context, options interfa
|
||||
|
||||
if options.Filters != nil {
|
||||
for key, value := range options.Filters {
|
||||
query = query.Where(key+" = ?", value)
|
||||
// 特殊处理 user_ids 过滤器
|
||||
if key == "user_ids" {
|
||||
if userIds, ok := value.(string); ok && userIds != "" {
|
||||
query = query.Where("user_id IN ?", strings.Split(userIds, ","))
|
||||
}
|
||||
} else {
|
||||
query = query.Where(key+" = ?", value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,4 +184,144 @@ func (r *GormRechargeRecordRepository) SoftDelete(ctx context.Context, id string
|
||||
|
||||
func (r *GormRechargeRecordRepository) Restore(ctx context.Context, id string) error {
|
||||
return r.RestoreEntity(ctx, id, &entities.RechargeRecord{})
|
||||
}
|
||||
}
|
||||
|
||||
// GetTotalAmountByUserId 获取用户总充值金额
|
||||
func (r *GormRechargeRecordRepository) GetTotalAmountByUserId(ctx context.Context, userId string) (float64, error) {
|
||||
var total float64
|
||||
err := r.GetDB(ctx).Model(&entities.RechargeRecord{}).
|
||||
Select("COALESCE(SUM(amount), 0)").
|
||||
Where("user_id = ? AND status = ?", userId, entities.RechargeStatusSuccess).
|
||||
Scan(&total).Error
|
||||
return total, err
|
||||
}
|
||||
|
||||
// GetTotalAmountByUserIdAndDateRange 按用户ID和日期范围获取总充值金额
|
||||
func (r *GormRechargeRecordRepository) GetTotalAmountByUserIdAndDateRange(ctx context.Context, userId string, startDate, endDate time.Time) (float64, error) {
|
||||
var total float64
|
||||
err := r.GetDB(ctx).Model(&entities.RechargeRecord{}).
|
||||
Select("COALESCE(SUM(amount), 0)").
|
||||
Where("user_id = ? AND status = ? AND created_at >= ? AND created_at < ?", userId, entities.RechargeStatusSuccess, startDate, endDate).
|
||||
Scan(&total).Error
|
||||
return total, err
|
||||
}
|
||||
|
||||
// GetDailyStatsByUserId 获取用户每日充值统计
|
||||
func (r *GormRechargeRecordRepository) GetDailyStatsByUserId(ctx context.Context, userId string, startDate, endDate time.Time) ([]map[string]interface{}, error) {
|
||||
var results []map[string]interface{}
|
||||
|
||||
// 构建SQL查询 - 使用PostgreSQL语法,使用具体的日期范围
|
||||
sql := `
|
||||
SELECT
|
||||
DATE(created_at) as date,
|
||||
COALESCE(SUM(amount), 0) as amount
|
||||
FROM recharge_records
|
||||
WHERE user_id = $1
|
||||
AND status = $2
|
||||
AND DATE(created_at) >= $3
|
||||
AND DATE(created_at) <= $4
|
||||
GROUP BY DATE(created_at)
|
||||
ORDER BY date ASC
|
||||
`
|
||||
|
||||
err := r.GetDB(ctx).Raw(sql, userId, entities.RechargeStatusSuccess, startDate.Format("2006-01-02"), endDate.Format("2006-01-02")).Scan(&results).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
||||
// GetMonthlyStatsByUserId 获取用户每月充值统计
|
||||
func (r *GormRechargeRecordRepository) GetMonthlyStatsByUserId(ctx context.Context, userId string, startDate, endDate time.Time) ([]map[string]interface{}, error) {
|
||||
var results []map[string]interface{}
|
||||
|
||||
// 构建SQL查询 - 使用PostgreSQL语法,使用具体的日期范围
|
||||
sql := `
|
||||
SELECT
|
||||
TO_CHAR(created_at, 'YYYY-MM') as month,
|
||||
COALESCE(SUM(amount), 0) as amount
|
||||
FROM recharge_records
|
||||
WHERE user_id = $1
|
||||
AND status = $2
|
||||
AND created_at >= $3
|
||||
AND created_at <= $4
|
||||
GROUP BY TO_CHAR(created_at, 'YYYY-MM')
|
||||
ORDER BY month ASC
|
||||
`
|
||||
|
||||
err := r.GetDB(ctx).Raw(sql, userId, entities.RechargeStatusSuccess, startDate, endDate).Scan(&results).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
||||
// GetSystemTotalAmount 获取系统总充值金额
|
||||
func (r *GormRechargeRecordRepository) GetSystemTotalAmount(ctx context.Context) (float64, error) {
|
||||
var total float64
|
||||
err := r.GetDB(ctx).Model(&entities.RechargeRecord{}).
|
||||
Where("status = ?", entities.RechargeStatusSuccess).
|
||||
Select("COALESCE(SUM(amount), 0)").
|
||||
Scan(&total).Error
|
||||
return total, err
|
||||
}
|
||||
|
||||
// GetSystemAmountByDateRange 获取系统指定时间范围内的充值金额
|
||||
func (r *GormRechargeRecordRepository) GetSystemAmountByDateRange(ctx context.Context, startDate, endDate time.Time) (float64, error) {
|
||||
var total float64
|
||||
err := r.GetDB(ctx).Model(&entities.RechargeRecord{}).
|
||||
Where("status = ? AND created_at >= ? AND created_at <= ?", entities.RechargeStatusSuccess, startDate, endDate).
|
||||
Select("COALESCE(SUM(amount), 0)").
|
||||
Scan(&total).Error
|
||||
return total, err
|
||||
}
|
||||
|
||||
// GetSystemDailyStats 获取系统每日充值统计
|
||||
func (r *GormRechargeRecordRepository) GetSystemDailyStats(ctx context.Context, startDate, endDate time.Time) ([]map[string]interface{}, error) {
|
||||
var results []map[string]interface{}
|
||||
|
||||
sql := `
|
||||
SELECT
|
||||
DATE(created_at) as date,
|
||||
COALESCE(SUM(amount), 0) as amount
|
||||
FROM recharge_records
|
||||
WHERE status = $1
|
||||
AND DATE(created_at) >= $2
|
||||
AND DATE(created_at) <= $3
|
||||
GROUP BY DATE(created_at)
|
||||
ORDER BY date ASC
|
||||
`
|
||||
|
||||
err := r.GetDB(ctx).Raw(sql, entities.RechargeStatusSuccess, startDate.Format("2006-01-02"), endDate.Format("2006-01-02")).Scan(&results).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
||||
// GetSystemMonthlyStats 获取系统每月充值统计
|
||||
func (r *GormRechargeRecordRepository) GetSystemMonthlyStats(ctx context.Context, startDate, endDate time.Time) ([]map[string]interface{}, error) {
|
||||
var results []map[string]interface{}
|
||||
|
||||
sql := `
|
||||
SELECT
|
||||
TO_CHAR(created_at, 'YYYY-MM') as month,
|
||||
COALESCE(SUM(amount), 0) as amount
|
||||
FROM recharge_records
|
||||
WHERE status = $1
|
||||
AND created_at >= $2
|
||||
AND created_at <= $3
|
||||
GROUP BY TO_CHAR(created_at, 'YYYY-MM')
|
||||
ORDER BY month ASC
|
||||
`
|
||||
|
||||
err := r.GetDB(ctx).Raw(sql, entities.RechargeStatusSuccess, startDate, endDate).Scan(&results).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
"tyapi-server/internal/domains/finance/entities"
|
||||
domain_finance_repo "tyapi-server/internal/domains/finance/repositories"
|
||||
"tyapi-server/internal/shared/database"
|
||||
@@ -184,31 +185,102 @@ func (r *GormWalletRepository) GetByUserID(ctx context.Context, userID string) (
|
||||
return &wallet, nil
|
||||
}
|
||||
|
||||
// UpdateBalanceWithVersionRetry 乐观锁自动重试,最大重试maxRetry次
|
||||
func (r *GormWalletRepository) UpdateBalanceWithVersion(ctx context.Context, walletID string, newBalance string, oldVersion int64) (bool, error) {
|
||||
// UpdateBalanceWithVersion 乐观锁自动重试,最大重试maxRetry次
|
||||
func (r *GormWalletRepository) UpdateBalanceWithVersion(ctx context.Context, walletID string, amount decimal.Decimal, operation string) (bool, error) {
|
||||
maxRetry := 10
|
||||
for i := 0; i < maxRetry; i++ {
|
||||
result := r.GetDB(ctx).Model(&entities.Wallet{}).
|
||||
Where("id = ? AND version = ?", walletID, oldVersion).
|
||||
Updates(map[string]interface{}{
|
||||
"balance": newBalance,
|
||||
"version": oldVersion + 1,
|
||||
})
|
||||
if result.Error != nil {
|
||||
return false, result.Error
|
||||
}
|
||||
if result.RowsAffected == 1 {
|
||||
return true, nil
|
||||
}
|
||||
// 并发冲突,重试前重新查version
|
||||
// 每次重试都重新获取最新的钱包信息
|
||||
var wallet entities.Wallet
|
||||
err := r.GetDB(ctx).Where("id = ?", walletID).First(&wallet).Error
|
||||
if err != nil {
|
||||
return false, err
|
||||
return false, fmt.Errorf("获取钱包信息失败: %w", err)
|
||||
}
|
||||
oldVersion = wallet.Version
|
||||
|
||||
// 重新计算新余额
|
||||
var newBalance decimal.Decimal
|
||||
switch operation {
|
||||
case "add":
|
||||
newBalance = wallet.Balance.Add(amount)
|
||||
case "subtract":
|
||||
newBalance = wallet.Balance.Sub(amount)
|
||||
default:
|
||||
return false, fmt.Errorf("不支持的操作类型: %s", operation)
|
||||
}
|
||||
|
||||
// 乐观锁更新
|
||||
result := r.GetDB(ctx).Model(&entities.Wallet{}).
|
||||
Where("id = ? AND version = ?", walletID, wallet.Version).
|
||||
Updates(map[string]interface{}{
|
||||
"balance": newBalance.String(),
|
||||
"version": wallet.Version + 1,
|
||||
})
|
||||
|
||||
if result.Error != nil {
|
||||
return false, fmt.Errorf("更新钱包余额失败: %w", result.Error)
|
||||
}
|
||||
|
||||
if result.RowsAffected == 1 {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// 乐观锁冲突,继续重试
|
||||
// 注意:这里可以添加日志记录,但需要确保logger可用
|
||||
}
|
||||
return false, fmt.Errorf("高并发下余额变动失败,请重试")
|
||||
|
||||
return false, fmt.Errorf("高并发下余额变动失败,已达到最大重试次数 %d", maxRetry)
|
||||
}
|
||||
|
||||
// UpdateBalanceByUserID 乐观锁更新(通过用户ID直接更新,使用原生SQL)
|
||||
func (r *GormWalletRepository) UpdateBalanceByUserID(ctx context.Context, userID string, amount decimal.Decimal, operation string) (bool, error) {
|
||||
maxRetry := 20 // 增加重试次数
|
||||
baseDelay := 1 // 基础延迟毫秒
|
||||
|
||||
for i := 0; i < maxRetry; i++ {
|
||||
// 每次重试都重新获取最新的钱包信息
|
||||
var wallet entities.Wallet
|
||||
err := r.GetDB(ctx).Where("user_id = ?", userID).First(&wallet).Error
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("获取钱包信息失败: %w", err)
|
||||
}
|
||||
|
||||
// 重新计算新余额
|
||||
var newBalance decimal.Decimal
|
||||
switch operation {
|
||||
case "add":
|
||||
newBalance = wallet.Balance.Add(amount)
|
||||
case "subtract":
|
||||
newBalance = wallet.Balance.Sub(amount)
|
||||
default:
|
||||
return false, fmt.Errorf("不支持的操作类型: %s", operation)
|
||||
}
|
||||
|
||||
// 使用原生SQL进行乐观锁更新
|
||||
newVersion := wallet.Version + 1
|
||||
result := r.GetDB(ctx).Exec(`
|
||||
UPDATE wallets
|
||||
SET balance = ?, version = ?, updated_at = NOW()
|
||||
WHERE user_id = ? AND version = ?
|
||||
`, newBalance.String(), newVersion, userID, wallet.Version)
|
||||
|
||||
if result.Error != nil {
|
||||
return false, fmt.Errorf("更新钱包余额失败: %w", result.Error)
|
||||
}
|
||||
|
||||
if result.RowsAffected == 1 {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// 乐观锁冲突,添加指数退避延迟
|
||||
if i < maxRetry-1 {
|
||||
delay := baseDelay * (1 << i) // 指数退避: 1ms, 2ms, 4ms, 8ms...
|
||||
if delay > 50 {
|
||||
delay = 50 // 最大延迟50ms
|
||||
}
|
||||
time.Sleep(time.Duration(delay) * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
return false, fmt.Errorf("高并发下余额变动失败,已达到最大重试次数 %d", maxRetry)
|
||||
}
|
||||
|
||||
func (r *GormWalletRepository) UpdateBalance(ctx context.Context, walletID string, balance string) error {
|
||||
|
||||
@@ -2,6 +2,7 @@ package repositories
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"time"
|
||||
"tyapi-server/internal/domains/finance/entities"
|
||||
domain_finance_repo "tyapi-server/internal/domains/finance/repositories"
|
||||
@@ -150,6 +151,81 @@ func (r *GormWalletTransactionRepository) CountByUserId(ctx context.Context, use
|
||||
return r.CountWhere(ctx, &entities.WalletTransaction{}, "user_id = ?", userId)
|
||||
}
|
||||
|
||||
// CountByUserIdAndDateRange 按用户ID和日期范围统计钱包交易次数
|
||||
func (r *GormWalletTransactionRepository) CountByUserIdAndDateRange(ctx context.Context, userId string, startDate, endDate time.Time) (int64, error) {
|
||||
return r.CountWhere(ctx, &entities.WalletTransaction{}, "user_id = ? AND created_at >= ? AND created_at < ?", userId, startDate, endDate)
|
||||
}
|
||||
|
||||
// GetTotalAmountByUserId 获取用户总消费金额
|
||||
func (r *GormWalletTransactionRepository) GetTotalAmountByUserId(ctx context.Context, userId string) (float64, error) {
|
||||
var total float64
|
||||
err := r.GetDB(ctx).Model(&entities.WalletTransaction{}).
|
||||
Select("COALESCE(SUM(amount), 0)").
|
||||
Where("user_id = ?", userId).
|
||||
Scan(&total).Error
|
||||
return total, err
|
||||
}
|
||||
|
||||
// GetTotalAmountByUserIdAndDateRange 按用户ID和日期范围获取总消费金额
|
||||
func (r *GormWalletTransactionRepository) GetTotalAmountByUserIdAndDateRange(ctx context.Context, userId string, startDate, endDate time.Time) (float64, error) {
|
||||
var total float64
|
||||
err := r.GetDB(ctx).Model(&entities.WalletTransaction{}).
|
||||
Select("COALESCE(SUM(amount), 0)").
|
||||
Where("user_id = ? AND created_at >= ? AND created_at < ?", userId, startDate, endDate).
|
||||
Scan(&total).Error
|
||||
return total, err
|
||||
}
|
||||
|
||||
// GetDailyStatsByUserId 获取用户每日消费统计
|
||||
func (r *GormWalletTransactionRepository) GetDailyStatsByUserId(ctx context.Context, userId string, startDate, endDate time.Time) ([]map[string]interface{}, error) {
|
||||
var results []map[string]interface{}
|
||||
|
||||
// 构建SQL查询 - 使用PostgreSQL语法,使用具体的日期范围
|
||||
sql := `
|
||||
SELECT
|
||||
DATE(created_at) as date,
|
||||
COALESCE(SUM(amount), 0) as amount
|
||||
FROM wallet_transactions
|
||||
WHERE user_id = $1
|
||||
AND DATE(created_at) >= $2
|
||||
AND DATE(created_at) <= $3
|
||||
GROUP BY DATE(created_at)
|
||||
ORDER BY date ASC
|
||||
`
|
||||
|
||||
err := r.GetDB(ctx).Raw(sql, userId, startDate.Format("2006-01-02"), endDate.Format("2006-01-02")).Scan(&results).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
||||
// GetMonthlyStatsByUserId 获取用户每月消费统计
|
||||
func (r *GormWalletTransactionRepository) GetMonthlyStatsByUserId(ctx context.Context, userId string, startDate, endDate time.Time) ([]map[string]interface{}, error) {
|
||||
var results []map[string]interface{}
|
||||
|
||||
// 构建SQL查询 - 使用PostgreSQL语法,使用具体的日期范围
|
||||
sql := `
|
||||
SELECT
|
||||
TO_CHAR(created_at, 'YYYY-MM') as month,
|
||||
COALESCE(SUM(amount), 0) as amount
|
||||
FROM wallet_transactions
|
||||
WHERE user_id = $1
|
||||
AND created_at >= $2
|
||||
AND created_at <= $3
|
||||
GROUP BY TO_CHAR(created_at, 'YYYY-MM')
|
||||
ORDER BY month ASC
|
||||
`
|
||||
|
||||
err := r.GetDB(ctx).Raw(sql, userId, startDate, endDate).Scan(&results).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
||||
// 实现interfaces.Repository接口的其他方法
|
||||
func (r *GormWalletTransactionRepository) Delete(ctx context.Context, id string) error {
|
||||
return r.DeleteEntity(ctx, id, &entities.WalletTransaction{})
|
||||
@@ -391,4 +467,153 @@ func (r *GormWalletTransactionRepository) ListWithFiltersAndProductName(ctx cont
|
||||
}
|
||||
|
||||
return productNameMap, transactions, total, nil
|
||||
}
|
||||
}
|
||||
|
||||
// ExportWithFiltersAndProductName 导出钱包交易记录(包含产品名称和企业信息)
|
||||
func (r *GormWalletTransactionRepository) ExportWithFiltersAndProductName(ctx context.Context, filters map[string]interface{}) ([]*entities.WalletTransaction, error) {
|
||||
var transactionsWithProduct []WalletTransactionWithProduct
|
||||
|
||||
// 构建查询
|
||||
query := r.GetDB(ctx).Table("wallet_transactions wt").
|
||||
Select("wt.*, p.name as product_name").
|
||||
Joins("LEFT JOIN product p ON wt.product_id = p.id")
|
||||
|
||||
// 构建WHERE条件
|
||||
var whereConditions []string
|
||||
var whereArgs []interface{}
|
||||
|
||||
// 用户ID筛选
|
||||
if userIds, ok := filters["user_ids"].(string); ok && userIds != "" {
|
||||
whereConditions = append(whereConditions, "wt.user_id IN (?)")
|
||||
whereArgs = append(whereArgs, strings.Split(userIds, ","))
|
||||
} else if userId, ok := filters["user_id"].(string); ok && userId != "" {
|
||||
whereConditions = append(whereConditions, "wt.user_id = ?")
|
||||
whereArgs = append(whereArgs, userId)
|
||||
}
|
||||
|
||||
// 时间范围筛选
|
||||
if startTime, ok := filters["start_time"].(time.Time); ok {
|
||||
whereConditions = append(whereConditions, "wt.created_at >= ?")
|
||||
whereArgs = append(whereArgs, startTime)
|
||||
}
|
||||
if endTime, ok := filters["end_time"].(time.Time); ok {
|
||||
whereConditions = append(whereConditions, "wt.created_at <= ?")
|
||||
whereArgs = append(whereArgs, endTime)
|
||||
}
|
||||
|
||||
// 交易ID筛选
|
||||
if transactionId, ok := filters["transaction_id"].(string); ok && transactionId != "" {
|
||||
whereConditions = append(whereConditions, "wt.transaction_id LIKE ?")
|
||||
whereArgs = append(whereArgs, "%"+transactionId+"%")
|
||||
}
|
||||
|
||||
// 产品名称筛选
|
||||
if productName, ok := filters["product_name"].(string); ok && productName != "" {
|
||||
whereConditions = append(whereConditions, "p.name LIKE ?")
|
||||
whereArgs = append(whereArgs, "%"+productName+"%")
|
||||
}
|
||||
|
||||
// 产品ID列表筛选
|
||||
if productIds, ok := filters["product_ids"].(string); ok && productIds != "" {
|
||||
whereConditions = append(whereConditions, "wt.product_id IN (?)")
|
||||
whereArgs = append(whereArgs, strings.Split(productIds, ","))
|
||||
}
|
||||
|
||||
// 金额范围筛选
|
||||
if minAmount, ok := filters["min_amount"].(string); ok && minAmount != "" {
|
||||
whereConditions = append(whereConditions, "wt.amount >= ?")
|
||||
whereArgs = append(whereArgs, minAmount)
|
||||
}
|
||||
if maxAmount, ok := filters["max_amount"].(string); ok && maxAmount != "" {
|
||||
whereConditions = append(whereConditions, "wt.amount <= ?")
|
||||
whereArgs = append(whereArgs, maxAmount)
|
||||
}
|
||||
|
||||
// 应用WHERE条件
|
||||
if len(whereConditions) > 0 {
|
||||
query = query.Where(strings.Join(whereConditions, " AND "), whereArgs...)
|
||||
}
|
||||
|
||||
// 排序
|
||||
query = query.Order("wt.created_at DESC")
|
||||
|
||||
// 执行查询
|
||||
err := query.Find(&transactionsWithProduct).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 转换为entities.WalletTransaction
|
||||
var transactions []*entities.WalletTransaction
|
||||
for _, t := range transactionsWithProduct {
|
||||
transaction := t.WalletTransaction
|
||||
transactions = append(transactions, &transaction)
|
||||
}
|
||||
|
||||
return transactions, nil
|
||||
}
|
||||
|
||||
// GetSystemTotalAmount 获取系统总消费金额
|
||||
func (r *GormWalletTransactionRepository) GetSystemTotalAmount(ctx context.Context) (float64, error) {
|
||||
var total float64
|
||||
err := r.GetDB(ctx).Model(&entities.WalletTransaction{}).
|
||||
Select("COALESCE(SUM(amount), 0)").
|
||||
Scan(&total).Error
|
||||
return total, err
|
||||
}
|
||||
|
||||
// GetSystemAmountByDateRange 获取系统指定时间范围内的消费金额
|
||||
func (r *GormWalletTransactionRepository) GetSystemAmountByDateRange(ctx context.Context, startDate, endDate time.Time) (float64, error) {
|
||||
var total float64
|
||||
err := r.GetDB(ctx).Model(&entities.WalletTransaction{}).
|
||||
Where("created_at >= ? AND created_at <= ?", startDate, endDate).
|
||||
Select("COALESCE(SUM(amount), 0)").
|
||||
Scan(&total).Error
|
||||
return total, err
|
||||
}
|
||||
|
||||
// GetSystemDailyStats 获取系统每日消费统计
|
||||
func (r *GormWalletTransactionRepository) GetSystemDailyStats(ctx context.Context, startDate, endDate time.Time) ([]map[string]interface{}, error) {
|
||||
var results []map[string]interface{}
|
||||
|
||||
sql := `
|
||||
SELECT
|
||||
DATE(created_at) as date,
|
||||
COALESCE(SUM(amount), 0) as amount
|
||||
FROM wallet_transactions
|
||||
WHERE DATE(created_at) >= $1
|
||||
AND DATE(created_at) <= $2
|
||||
GROUP BY DATE(created_at)
|
||||
ORDER BY date ASC
|
||||
`
|
||||
|
||||
err := r.GetDB(ctx).Raw(sql, startDate.Format("2006-01-02"), endDate.Format("2006-01-02")).Scan(&results).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
||||
// GetSystemMonthlyStats 获取系统每月消费统计
|
||||
func (r *GormWalletTransactionRepository) GetSystemMonthlyStats(ctx context.Context, startDate, endDate time.Time) ([]map[string]interface{}, error) {
|
||||
var results []map[string]interface{}
|
||||
|
||||
sql := `
|
||||
SELECT
|
||||
TO_CHAR(created_at, 'YYYY-MM') as month,
|
||||
COALESCE(SUM(amount), 0) as amount
|
||||
FROM wallet_transactions
|
||||
WHERE created_at >= $1
|
||||
AND created_at <= $2
|
||||
GROUP BY TO_CHAR(created_at, 'YYYY-MM')
|
||||
ORDER BY month ASC
|
||||
`
|
||||
|
||||
err := r.GetDB(ctx).Raw(sql, startDate, endDate).Scan(&results).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
@@ -0,0 +1,461 @@
|
||||
package statistics
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"gorm.io/gorm"
|
||||
|
||||
"tyapi-server/internal/domains/statistics/entities"
|
||||
"tyapi-server/internal/domains/statistics/repositories"
|
||||
)
|
||||
|
||||
// GormStatisticsDashboardRepository GORM统计仪表板仓储实现
|
||||
type GormStatisticsDashboardRepository struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
// NewGormStatisticsDashboardRepository 创建GORM统计仪表板仓储
|
||||
func NewGormStatisticsDashboardRepository(db *gorm.DB) repositories.StatisticsDashboardRepository {
|
||||
return &GormStatisticsDashboardRepository{
|
||||
db: db,
|
||||
}
|
||||
}
|
||||
|
||||
// Save 保存统计仪表板
|
||||
func (r *GormStatisticsDashboardRepository) Save(ctx context.Context, dashboard *entities.StatisticsDashboard) error {
|
||||
if dashboard == nil {
|
||||
return fmt.Errorf("统计仪表板不能为空")
|
||||
}
|
||||
|
||||
// 验证仪表板
|
||||
if err := dashboard.Validate(); err != nil {
|
||||
return fmt.Errorf("统计仪表板验证失败: %w", err)
|
||||
}
|
||||
|
||||
// 保存到数据库
|
||||
result := r.db.WithContext(ctx).Save(dashboard)
|
||||
if result.Error != nil {
|
||||
return fmt.Errorf("保存统计仪表板失败: %w", result.Error)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// FindByID 根据ID查找统计仪表板
|
||||
func (r *GormStatisticsDashboardRepository) FindByID(ctx context.Context, id string) (*entities.StatisticsDashboard, error) {
|
||||
if id == "" {
|
||||
return nil, fmt.Errorf("仪表板ID不能为空")
|
||||
}
|
||||
|
||||
var dashboard entities.StatisticsDashboard
|
||||
result := r.db.WithContext(ctx).Where("id = ?", id).First(&dashboard)
|
||||
if result.Error != nil {
|
||||
if result.Error == gorm.ErrRecordNotFound {
|
||||
return nil, fmt.Errorf("统计仪表板不存在")
|
||||
}
|
||||
return nil, fmt.Errorf("查询统计仪表板失败: %w", result.Error)
|
||||
}
|
||||
|
||||
return &dashboard, nil
|
||||
}
|
||||
|
||||
// FindByUser 根据用户查找统计仪表板
|
||||
func (r *GormStatisticsDashboardRepository) FindByUser(ctx context.Context, userID string, limit, offset int) ([]*entities.StatisticsDashboard, error) {
|
||||
if userID == "" {
|
||||
return nil, fmt.Errorf("用户ID不能为空")
|
||||
}
|
||||
|
||||
var dashboards []*entities.StatisticsDashboard
|
||||
query := r.db.WithContext(ctx).Where("created_by = ?", userID)
|
||||
|
||||
if limit > 0 {
|
||||
query = query.Limit(limit)
|
||||
}
|
||||
if offset > 0 {
|
||||
query = query.Offset(offset)
|
||||
}
|
||||
|
||||
result := query.Order("created_at DESC").Find(&dashboards)
|
||||
if result.Error != nil {
|
||||
return nil, fmt.Errorf("查询统计仪表板失败: %w", result.Error)
|
||||
}
|
||||
|
||||
return dashboards, nil
|
||||
}
|
||||
|
||||
// FindByUserRole 根据用户角色查找统计仪表板
|
||||
func (r *GormStatisticsDashboardRepository) FindByUserRole(ctx context.Context, userRole string, limit, offset int) ([]*entities.StatisticsDashboard, error) {
|
||||
if userRole == "" {
|
||||
return nil, fmt.Errorf("用户角色不能为空")
|
||||
}
|
||||
|
||||
var dashboards []*entities.StatisticsDashboard
|
||||
query := r.db.WithContext(ctx).Where("user_role = ?", userRole)
|
||||
|
||||
if limit > 0 {
|
||||
query = query.Limit(limit)
|
||||
}
|
||||
if offset > 0 {
|
||||
query = query.Offset(offset)
|
||||
}
|
||||
|
||||
result := query.Order("created_at DESC").Find(&dashboards)
|
||||
if result.Error != nil {
|
||||
return nil, fmt.Errorf("查询统计仪表板失败: %w", result.Error)
|
||||
}
|
||||
|
||||
return dashboards, nil
|
||||
}
|
||||
|
||||
// Update 更新统计仪表板
|
||||
func (r *GormStatisticsDashboardRepository) Update(ctx context.Context, dashboard *entities.StatisticsDashboard) error {
|
||||
if dashboard == nil {
|
||||
return fmt.Errorf("统计仪表板不能为空")
|
||||
}
|
||||
|
||||
if dashboard.ID == "" {
|
||||
return fmt.Errorf("仪表板ID不能为空")
|
||||
}
|
||||
|
||||
// 验证仪表板
|
||||
if err := dashboard.Validate(); err != nil {
|
||||
return fmt.Errorf("统计仪表板验证失败: %w", err)
|
||||
}
|
||||
|
||||
// 更新数据库
|
||||
result := r.db.WithContext(ctx).Save(dashboard)
|
||||
if result.Error != nil {
|
||||
return fmt.Errorf("更新统计仪表板失败: %w", result.Error)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Delete 删除统计仪表板
|
||||
func (r *GormStatisticsDashboardRepository) Delete(ctx context.Context, id string) error {
|
||||
if id == "" {
|
||||
return fmt.Errorf("仪表板ID不能为空")
|
||||
}
|
||||
|
||||
result := r.db.WithContext(ctx).Delete(&entities.StatisticsDashboard{}, "id = ?", id)
|
||||
if result.Error != nil {
|
||||
return fmt.Errorf("删除统计仪表板失败: %w", result.Error)
|
||||
}
|
||||
|
||||
if result.RowsAffected == 0 {
|
||||
return fmt.Errorf("统计仪表板不存在")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// FindByRole 根据角色查找统计仪表板
|
||||
func (r *GormStatisticsDashboardRepository) FindByRole(ctx context.Context, userRole string, limit, offset int) ([]*entities.StatisticsDashboard, error) {
|
||||
if userRole == "" {
|
||||
return nil, fmt.Errorf("用户角色不能为空")
|
||||
}
|
||||
|
||||
var dashboards []*entities.StatisticsDashboard
|
||||
query := r.db.WithContext(ctx).Where("user_role = ?", userRole)
|
||||
|
||||
if limit > 0 {
|
||||
query = query.Limit(limit)
|
||||
}
|
||||
if offset > 0 {
|
||||
query = query.Offset(offset)
|
||||
}
|
||||
|
||||
result := query.Order("created_at DESC").Find(&dashboards)
|
||||
if result.Error != nil {
|
||||
return nil, fmt.Errorf("查询统计仪表板失败: %w", result.Error)
|
||||
}
|
||||
|
||||
return dashboards, nil
|
||||
}
|
||||
|
||||
// FindDefaultByRole 根据角色查找默认统计仪表板
|
||||
func (r *GormStatisticsDashboardRepository) FindDefaultByRole(ctx context.Context, userRole string) (*entities.StatisticsDashboard, error) {
|
||||
if userRole == "" {
|
||||
return nil, fmt.Errorf("用户角色不能为空")
|
||||
}
|
||||
|
||||
var dashboard entities.StatisticsDashboard
|
||||
result := r.db.WithContext(ctx).
|
||||
Where("user_role = ? AND is_default = ? AND is_active = ?", userRole, true, true).
|
||||
First(&dashboard)
|
||||
|
||||
if result.Error != nil {
|
||||
if result.Error == gorm.ErrRecordNotFound {
|
||||
return nil, fmt.Errorf("默认统计仪表板不存在")
|
||||
}
|
||||
return nil, fmt.Errorf("查询默认统计仪表板失败: %w", result.Error)
|
||||
}
|
||||
|
||||
return &dashboard, nil
|
||||
}
|
||||
|
||||
// FindActiveByRole 根据角色查找激活的统计仪表板
|
||||
func (r *GormStatisticsDashboardRepository) FindActiveByRole(ctx context.Context, userRole string, limit, offset int) ([]*entities.StatisticsDashboard, error) {
|
||||
if userRole == "" {
|
||||
return nil, fmt.Errorf("用户角色不能为空")
|
||||
}
|
||||
|
||||
var dashboards []*entities.StatisticsDashboard
|
||||
query := r.db.WithContext(ctx).
|
||||
Where("user_role = ? AND is_active = ?", userRole, true)
|
||||
|
||||
if limit > 0 {
|
||||
query = query.Limit(limit)
|
||||
}
|
||||
if offset > 0 {
|
||||
query = query.Offset(offset)
|
||||
}
|
||||
|
||||
result := query.Order("created_at DESC").Find(&dashboards)
|
||||
if result.Error != nil {
|
||||
return nil, fmt.Errorf("查询激活统计仪表板失败: %w", result.Error)
|
||||
}
|
||||
|
||||
return dashboards, nil
|
||||
}
|
||||
|
||||
// FindByStatus 根据状态查找统计仪表板
|
||||
func (r *GormStatisticsDashboardRepository) FindByStatus(ctx context.Context, isActive bool, limit, offset int) ([]*entities.StatisticsDashboard, error) {
|
||||
var dashboards []*entities.StatisticsDashboard
|
||||
query := r.db.WithContext(ctx).Where("is_active = ?", isActive)
|
||||
|
||||
if limit > 0 {
|
||||
query = query.Limit(limit)
|
||||
}
|
||||
if offset > 0 {
|
||||
query = query.Offset(offset)
|
||||
}
|
||||
|
||||
result := query.Order("created_at DESC").Find(&dashboards)
|
||||
if result.Error != nil {
|
||||
return nil, fmt.Errorf("查询统计仪表板失败: %w", result.Error)
|
||||
}
|
||||
|
||||
return dashboards, nil
|
||||
}
|
||||
|
||||
// FindByAccessLevel 根据访问级别查找统计仪表板
|
||||
func (r *GormStatisticsDashboardRepository) FindByAccessLevel(ctx context.Context, accessLevel string, limit, offset int) ([]*entities.StatisticsDashboard, error) {
|
||||
if accessLevel == "" {
|
||||
return nil, fmt.Errorf("访问级别不能为空")
|
||||
}
|
||||
|
||||
var dashboards []*entities.StatisticsDashboard
|
||||
query := r.db.WithContext(ctx).Where("access_level = ?", accessLevel)
|
||||
|
||||
if limit > 0 {
|
||||
query = query.Limit(limit)
|
||||
}
|
||||
if offset > 0 {
|
||||
query = query.Offset(offset)
|
||||
}
|
||||
|
||||
result := query.Order("created_at DESC").Find(&dashboards)
|
||||
if result.Error != nil {
|
||||
return nil, fmt.Errorf("查询统计仪表板失败: %w", result.Error)
|
||||
}
|
||||
|
||||
return dashboards, nil
|
||||
}
|
||||
|
||||
// CountByUser 根据用户统计数量
|
||||
func (r *GormStatisticsDashboardRepository) CountByUser(ctx context.Context, userID string) (int64, error) {
|
||||
if userID == "" {
|
||||
return 0, fmt.Errorf("用户ID不能为空")
|
||||
}
|
||||
|
||||
var count int64
|
||||
result := r.db.WithContext(ctx).
|
||||
Model(&entities.StatisticsDashboard{}).
|
||||
Where("created_by = ?", userID).
|
||||
Count(&count)
|
||||
|
||||
if result.Error != nil {
|
||||
return 0, fmt.Errorf("统计仪表板数量失败: %w", result.Error)
|
||||
}
|
||||
|
||||
return count, nil
|
||||
}
|
||||
|
||||
// CountByRole 根据角色统计数量
|
||||
func (r *GormStatisticsDashboardRepository) CountByRole(ctx context.Context, userRole string) (int64, error) {
|
||||
if userRole == "" {
|
||||
return 0, fmt.Errorf("用户角色不能为空")
|
||||
}
|
||||
|
||||
var count int64
|
||||
result := r.db.WithContext(ctx).
|
||||
Model(&entities.StatisticsDashboard{}).
|
||||
Where("user_role = ?", userRole).
|
||||
Count(&count)
|
||||
|
||||
if result.Error != nil {
|
||||
return 0, fmt.Errorf("统计仪表板数量失败: %w", result.Error)
|
||||
}
|
||||
|
||||
return count, nil
|
||||
}
|
||||
|
||||
// CountByStatus 根据状态统计数量
|
||||
func (r *GormStatisticsDashboardRepository) CountByStatus(ctx context.Context, isActive bool) (int64, error) {
|
||||
var count int64
|
||||
result := r.db.WithContext(ctx).
|
||||
Model(&entities.StatisticsDashboard{}).
|
||||
Where("is_active = ?", isActive).
|
||||
Count(&count)
|
||||
|
||||
if result.Error != nil {
|
||||
return 0, fmt.Errorf("统计仪表板数量失败: %w", result.Error)
|
||||
}
|
||||
|
||||
return count, nil
|
||||
}
|
||||
|
||||
// BatchSave 批量保存统计仪表板
|
||||
func (r *GormStatisticsDashboardRepository) BatchSave(ctx context.Context, dashboards []*entities.StatisticsDashboard) error {
|
||||
if len(dashboards) == 0 {
|
||||
return fmt.Errorf("统计仪表板列表不能为空")
|
||||
}
|
||||
|
||||
// 验证所有仪表板
|
||||
for _, dashboard := range dashboards {
|
||||
if err := dashboard.Validate(); err != nil {
|
||||
return fmt.Errorf("统计仪表板验证失败: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// 批量保存
|
||||
result := r.db.WithContext(ctx).CreateInBatches(dashboards, 100)
|
||||
if result.Error != nil {
|
||||
return fmt.Errorf("批量保存统计仪表板失败: %w", result.Error)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// BatchDelete 批量删除统计仪表板
|
||||
func (r *GormStatisticsDashboardRepository) BatchDelete(ctx context.Context, ids []string) error {
|
||||
if len(ids) == 0 {
|
||||
return fmt.Errorf("仪表板ID列表不能为空")
|
||||
}
|
||||
|
||||
result := r.db.WithContext(ctx).Delete(&entities.StatisticsDashboard{}, "id IN ?", ids)
|
||||
if result.Error != nil {
|
||||
return fmt.Errorf("批量删除统计仪表板失败: %w", result.Error)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetDefaultDashboard 设置默认仪表板
|
||||
func (r *GormStatisticsDashboardRepository) SetDefaultDashboard(ctx context.Context, dashboardID string) error {
|
||||
if dashboardID == "" {
|
||||
return fmt.Errorf("仪表板ID不能为空")
|
||||
}
|
||||
|
||||
// 开始事务
|
||||
tx := r.db.WithContext(ctx).Begin()
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
tx.Rollback()
|
||||
}
|
||||
}()
|
||||
|
||||
// 先取消同角色的所有默认状态
|
||||
var dashboard entities.StatisticsDashboard
|
||||
if err := tx.Where("id = ?", dashboardID).First(&dashboard).Error; err != nil {
|
||||
tx.Rollback()
|
||||
return fmt.Errorf("查询仪表板失败: %w", err)
|
||||
}
|
||||
|
||||
// 取消同角色的所有默认状态
|
||||
if err := tx.Model(&entities.StatisticsDashboard{}).
|
||||
Where("user_role = ? AND is_default = ?", dashboard.UserRole, true).
|
||||
Update("is_default", false).Error; err != nil {
|
||||
tx.Rollback()
|
||||
return fmt.Errorf("取消默认状态失败: %w", err)
|
||||
}
|
||||
|
||||
// 设置新的默认状态
|
||||
if err := tx.Model(&entities.StatisticsDashboard{}).
|
||||
Where("id = ?", dashboardID).
|
||||
Update("is_default", true).Error; err != nil {
|
||||
tx.Rollback()
|
||||
return fmt.Errorf("设置默认状态失败: %w", err)
|
||||
}
|
||||
|
||||
// 提交事务
|
||||
if err := tx.Commit().Error; err != nil {
|
||||
return fmt.Errorf("提交事务失败: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoveDefaultDashboard 移除默认仪表板
|
||||
func (r *GormStatisticsDashboardRepository) RemoveDefaultDashboard(ctx context.Context, userRole string) error {
|
||||
if userRole == "" {
|
||||
return fmt.Errorf("用户角色不能为空")
|
||||
}
|
||||
|
||||
result := r.db.WithContext(ctx).
|
||||
Model(&entities.StatisticsDashboard{}).
|
||||
Where("user_role = ? AND is_default = ?", userRole, true).
|
||||
Update("is_default", false)
|
||||
|
||||
if result.Error != nil {
|
||||
return fmt.Errorf("移除默认仪表板失败: %w", result.Error)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ActivateDashboard 激活仪表板
|
||||
func (r *GormStatisticsDashboardRepository) ActivateDashboard(ctx context.Context, dashboardID string) error {
|
||||
if dashboardID == "" {
|
||||
return fmt.Errorf("仪表板ID不能为空")
|
||||
}
|
||||
|
||||
result := r.db.WithContext(ctx).
|
||||
Model(&entities.StatisticsDashboard{}).
|
||||
Where("id = ?", dashboardID).
|
||||
Update("is_active", true)
|
||||
|
||||
if result.Error != nil {
|
||||
return fmt.Errorf("激活仪表板失败: %w", result.Error)
|
||||
}
|
||||
|
||||
if result.RowsAffected == 0 {
|
||||
return fmt.Errorf("仪表板不存在")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeactivateDashboard 停用仪表板
|
||||
func (r *GormStatisticsDashboardRepository) DeactivateDashboard(ctx context.Context, dashboardID string) error {
|
||||
if dashboardID == "" {
|
||||
return fmt.Errorf("仪表板ID不能为空")
|
||||
}
|
||||
|
||||
result := r.db.WithContext(ctx).
|
||||
Model(&entities.StatisticsDashboard{}).
|
||||
Where("id = ?", dashboardID).
|
||||
Update("is_active", false)
|
||||
|
||||
if result.Error != nil {
|
||||
return fmt.Errorf("停用仪表板失败: %w", result.Error)
|
||||
}
|
||||
|
||||
if result.RowsAffected == 0 {
|
||||
return fmt.Errorf("仪表板不存在")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,377 @@
|
||||
package statistics
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
|
||||
"tyapi-server/internal/domains/statistics/entities"
|
||||
"tyapi-server/internal/domains/statistics/repositories"
|
||||
)
|
||||
|
||||
// GormStatisticsReportRepository GORM统计报告仓储实现
|
||||
type GormStatisticsReportRepository struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
// NewGormStatisticsReportRepository 创建GORM统计报告仓储
|
||||
func NewGormStatisticsReportRepository(db *gorm.DB) repositories.StatisticsReportRepository {
|
||||
return &GormStatisticsReportRepository{
|
||||
db: db,
|
||||
}
|
||||
}
|
||||
|
||||
// Save 保存统计报告
|
||||
func (r *GormStatisticsReportRepository) Save(ctx context.Context, report *entities.StatisticsReport) error {
|
||||
if report == nil {
|
||||
return fmt.Errorf("统计报告不能为空")
|
||||
}
|
||||
|
||||
// 验证报告
|
||||
if err := report.Validate(); err != nil {
|
||||
return fmt.Errorf("统计报告验证失败: %w", err)
|
||||
}
|
||||
|
||||
// 保存到数据库
|
||||
result := r.db.WithContext(ctx).Save(report)
|
||||
if result.Error != nil {
|
||||
return fmt.Errorf("保存统计报告失败: %w", result.Error)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// FindByID 根据ID查找统计报告
|
||||
func (r *GormStatisticsReportRepository) FindByID(ctx context.Context, id string) (*entities.StatisticsReport, error) {
|
||||
if id == "" {
|
||||
return nil, fmt.Errorf("报告ID不能为空")
|
||||
}
|
||||
|
||||
var report entities.StatisticsReport
|
||||
result := r.db.WithContext(ctx).Where("id = ?", id).First(&report)
|
||||
if result.Error != nil {
|
||||
if result.Error == gorm.ErrRecordNotFound {
|
||||
return nil, fmt.Errorf("统计报告不存在")
|
||||
}
|
||||
return nil, fmt.Errorf("查询统计报告失败: %w", result.Error)
|
||||
}
|
||||
|
||||
return &report, nil
|
||||
}
|
||||
|
||||
// FindByUser 根据用户查找统计报告
|
||||
func (r *GormStatisticsReportRepository) FindByUser(ctx context.Context, userID string, limit, offset int) ([]*entities.StatisticsReport, error) {
|
||||
if userID == "" {
|
||||
return nil, fmt.Errorf("用户ID不能为空")
|
||||
}
|
||||
|
||||
var reports []*entities.StatisticsReport
|
||||
query := r.db.WithContext(ctx).Where("generated_by = ?", userID)
|
||||
|
||||
if limit > 0 {
|
||||
query = query.Limit(limit)
|
||||
}
|
||||
if offset > 0 {
|
||||
query = query.Offset(offset)
|
||||
}
|
||||
|
||||
result := query.Order("created_at DESC").Find(&reports)
|
||||
if result.Error != nil {
|
||||
return nil, fmt.Errorf("查询统计报告失败: %w", result.Error)
|
||||
}
|
||||
|
||||
return reports, nil
|
||||
}
|
||||
|
||||
// FindByStatus 根据状态查找统计报告
|
||||
func (r *GormStatisticsReportRepository) FindByStatus(ctx context.Context, status string) ([]*entities.StatisticsReport, error) {
|
||||
if status == "" {
|
||||
return nil, fmt.Errorf("报告状态不能为空")
|
||||
}
|
||||
|
||||
var reports []*entities.StatisticsReport
|
||||
result := r.db.WithContext(ctx).
|
||||
Where("status = ?", status).
|
||||
Order("created_at DESC").
|
||||
Find(&reports)
|
||||
|
||||
if result.Error != nil {
|
||||
return nil, fmt.Errorf("查询统计报告失败: %w", result.Error)
|
||||
}
|
||||
|
||||
return reports, nil
|
||||
}
|
||||
|
||||
// Update 更新统计报告
|
||||
func (r *GormStatisticsReportRepository) Update(ctx context.Context, report *entities.StatisticsReport) error {
|
||||
if report == nil {
|
||||
return fmt.Errorf("统计报告不能为空")
|
||||
}
|
||||
|
||||
if report.ID == "" {
|
||||
return fmt.Errorf("报告ID不能为空")
|
||||
}
|
||||
|
||||
// 验证报告
|
||||
if err := report.Validate(); err != nil {
|
||||
return fmt.Errorf("统计报告验证失败: %w", err)
|
||||
}
|
||||
|
||||
// 更新数据库
|
||||
result := r.db.WithContext(ctx).Save(report)
|
||||
if result.Error != nil {
|
||||
return fmt.Errorf("更新统计报告失败: %w", result.Error)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Delete 删除统计报告
|
||||
func (r *GormStatisticsReportRepository) Delete(ctx context.Context, id string) error {
|
||||
if id == "" {
|
||||
return fmt.Errorf("报告ID不能为空")
|
||||
}
|
||||
|
||||
result := r.db.WithContext(ctx).Delete(&entities.StatisticsReport{}, "id = ?", id)
|
||||
if result.Error != nil {
|
||||
return fmt.Errorf("删除统计报告失败: %w", result.Error)
|
||||
}
|
||||
|
||||
if result.RowsAffected == 0 {
|
||||
return fmt.Errorf("统计报告不存在")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// FindByType 根据类型查找统计报告
|
||||
func (r *GormStatisticsReportRepository) FindByType(ctx context.Context, reportType string, limit, offset int) ([]*entities.StatisticsReport, error) {
|
||||
if reportType == "" {
|
||||
return nil, fmt.Errorf("报告类型不能为空")
|
||||
}
|
||||
|
||||
var reports []*entities.StatisticsReport
|
||||
query := r.db.WithContext(ctx).Where("report_type = ?", reportType)
|
||||
|
||||
if limit > 0 {
|
||||
query = query.Limit(limit)
|
||||
}
|
||||
if offset > 0 {
|
||||
query = query.Offset(offset)
|
||||
}
|
||||
|
||||
result := query.Order("created_at DESC").Find(&reports)
|
||||
if result.Error != nil {
|
||||
return nil, fmt.Errorf("查询统计报告失败: %w", result.Error)
|
||||
}
|
||||
|
||||
return reports, nil
|
||||
}
|
||||
|
||||
// FindByTypeAndPeriod 根据类型和周期查找统计报告
|
||||
func (r *GormStatisticsReportRepository) FindByTypeAndPeriod(ctx context.Context, reportType, period string, limit, offset int) ([]*entities.StatisticsReport, error) {
|
||||
if reportType == "" {
|
||||
return nil, fmt.Errorf("报告类型不能为空")
|
||||
}
|
||||
|
||||
if period == "" {
|
||||
return nil, fmt.Errorf("统计周期不能为空")
|
||||
}
|
||||
|
||||
var reports []*entities.StatisticsReport
|
||||
query := r.db.WithContext(ctx).
|
||||
Where("report_type = ? AND period = ?", reportType, period)
|
||||
|
||||
if limit > 0 {
|
||||
query = query.Limit(limit)
|
||||
}
|
||||
if offset > 0 {
|
||||
query = query.Offset(offset)
|
||||
}
|
||||
|
||||
result := query.Order("created_at DESC").Find(&reports)
|
||||
if result.Error != nil {
|
||||
return nil, fmt.Errorf("查询统计报告失败: %w", result.Error)
|
||||
}
|
||||
|
||||
return reports, nil
|
||||
}
|
||||
|
||||
// FindByDateRange 根据日期范围查找统计报告
|
||||
func (r *GormStatisticsReportRepository) FindByDateRange(ctx context.Context, startDate, endDate time.Time, limit, offset int) ([]*entities.StatisticsReport, error) {
|
||||
if startDate.IsZero() || endDate.IsZero() {
|
||||
return nil, fmt.Errorf("开始日期和结束日期不能为空")
|
||||
}
|
||||
|
||||
var reports []*entities.StatisticsReport
|
||||
query := r.db.WithContext(ctx).
|
||||
Where("created_at >= ? AND created_at < ?", startDate, endDate)
|
||||
|
||||
if limit > 0 {
|
||||
query = query.Limit(limit)
|
||||
}
|
||||
if offset > 0 {
|
||||
query = query.Offset(offset)
|
||||
}
|
||||
|
||||
result := query.Order("created_at DESC").Find(&reports)
|
||||
if result.Error != nil {
|
||||
return nil, fmt.Errorf("查询统计报告失败: %w", result.Error)
|
||||
}
|
||||
|
||||
return reports, nil
|
||||
}
|
||||
|
||||
// FindByUserAndDateRange 根据用户和日期范围查找统计报告
|
||||
func (r *GormStatisticsReportRepository) FindByUserAndDateRange(ctx context.Context, userID string, startDate, endDate time.Time, limit, offset int) ([]*entities.StatisticsReport, error) {
|
||||
if userID == "" {
|
||||
return nil, fmt.Errorf("用户ID不能为空")
|
||||
}
|
||||
|
||||
if startDate.IsZero() || endDate.IsZero() {
|
||||
return nil, fmt.Errorf("开始日期和结束日期不能为空")
|
||||
}
|
||||
|
||||
var reports []*entities.StatisticsReport
|
||||
query := r.db.WithContext(ctx).
|
||||
Where("generated_by = ? AND created_at >= ? AND created_at < ?", userID, startDate, endDate)
|
||||
|
||||
if limit > 0 {
|
||||
query = query.Limit(limit)
|
||||
}
|
||||
if offset > 0 {
|
||||
query = query.Offset(offset)
|
||||
}
|
||||
|
||||
result := query.Order("created_at DESC").Find(&reports)
|
||||
if result.Error != nil {
|
||||
return nil, fmt.Errorf("查询统计报告失败: %w", result.Error)
|
||||
}
|
||||
|
||||
return reports, nil
|
||||
}
|
||||
|
||||
// CountByUser 根据用户统计数量
|
||||
func (r *GormStatisticsReportRepository) CountByUser(ctx context.Context, userID string) (int64, error) {
|
||||
if userID == "" {
|
||||
return 0, fmt.Errorf("用户ID不能为空")
|
||||
}
|
||||
|
||||
var count int64
|
||||
result := r.db.WithContext(ctx).
|
||||
Model(&entities.StatisticsReport{}).
|
||||
Where("generated_by = ?", userID).
|
||||
Count(&count)
|
||||
|
||||
if result.Error != nil {
|
||||
return 0, fmt.Errorf("统计报告数量失败: %w", result.Error)
|
||||
}
|
||||
|
||||
return count, nil
|
||||
}
|
||||
|
||||
// CountByType 根据类型统计数量
|
||||
func (r *GormStatisticsReportRepository) CountByType(ctx context.Context, reportType string) (int64, error) {
|
||||
if reportType == "" {
|
||||
return 0, fmt.Errorf("报告类型不能为空")
|
||||
}
|
||||
|
||||
var count int64
|
||||
result := r.db.WithContext(ctx).
|
||||
Model(&entities.StatisticsReport{}).
|
||||
Where("report_type = ?", reportType).
|
||||
Count(&count)
|
||||
|
||||
if result.Error != nil {
|
||||
return 0, fmt.Errorf("统计报告数量失败: %w", result.Error)
|
||||
}
|
||||
|
||||
return count, nil
|
||||
}
|
||||
|
||||
// CountByStatus 根据状态统计数量
|
||||
func (r *GormStatisticsReportRepository) CountByStatus(ctx context.Context, status string) (int64, error) {
|
||||
if status == "" {
|
||||
return 0, fmt.Errorf("报告状态不能为空")
|
||||
}
|
||||
|
||||
var count int64
|
||||
result := r.db.WithContext(ctx).
|
||||
Model(&entities.StatisticsReport{}).
|
||||
Where("status = ?", status).
|
||||
Count(&count)
|
||||
|
||||
if result.Error != nil {
|
||||
return 0, fmt.Errorf("统计报告数量失败: %w", result.Error)
|
||||
}
|
||||
|
||||
return count, nil
|
||||
}
|
||||
|
||||
// BatchSave 批量保存统计报告
|
||||
func (r *GormStatisticsReportRepository) BatchSave(ctx context.Context, reports []*entities.StatisticsReport) error {
|
||||
if len(reports) == 0 {
|
||||
return fmt.Errorf("统计报告列表不能为空")
|
||||
}
|
||||
|
||||
// 验证所有报告
|
||||
for _, report := range reports {
|
||||
if err := report.Validate(); err != nil {
|
||||
return fmt.Errorf("统计报告验证失败: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// 批量保存
|
||||
result := r.db.WithContext(ctx).CreateInBatches(reports, 100)
|
||||
if result.Error != nil {
|
||||
return fmt.Errorf("批量保存统计报告失败: %w", result.Error)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// BatchDelete 批量删除统计报告
|
||||
func (r *GormStatisticsReportRepository) BatchDelete(ctx context.Context, ids []string) error {
|
||||
if len(ids) == 0 {
|
||||
return fmt.Errorf("报告ID列表不能为空")
|
||||
}
|
||||
|
||||
result := r.db.WithContext(ctx).Delete(&entities.StatisticsReport{}, "id IN ?", ids)
|
||||
if result.Error != nil {
|
||||
return fmt.Errorf("批量删除统计报告失败: %w", result.Error)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteExpiredReports 删除过期报告
|
||||
func (r *GormStatisticsReportRepository) DeleteExpiredReports(ctx context.Context, expiredBefore time.Time) error {
|
||||
if expiredBefore.IsZero() {
|
||||
return fmt.Errorf("过期时间不能为空")
|
||||
}
|
||||
|
||||
result := r.db.WithContext(ctx).
|
||||
Delete(&entities.StatisticsReport{}, "expires_at IS NOT NULL AND expires_at < ?", expiredBefore)
|
||||
if result.Error != nil {
|
||||
return fmt.Errorf("删除过期报告失败: %w", result.Error)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteByStatus 根据状态删除统计报告
|
||||
func (r *GormStatisticsReportRepository) DeleteByStatus(ctx context.Context, status string) error {
|
||||
if status == "" {
|
||||
return fmt.Errorf("报告状态不能为空")
|
||||
}
|
||||
|
||||
result := r.db.WithContext(ctx).
|
||||
Delete(&entities.StatisticsReport{}, "status = ?", status)
|
||||
if result.Error != nil {
|
||||
return fmt.Errorf("根据状态删除统计报告失败: %w", result.Error)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,381 @@
|
||||
package statistics
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
|
||||
"tyapi-server/internal/domains/statistics/entities"
|
||||
"tyapi-server/internal/domains/statistics/repositories"
|
||||
)
|
||||
|
||||
// GormStatisticsRepository GORM统计指标仓储实现
|
||||
type GormStatisticsRepository struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
// NewGormStatisticsRepository 创建GORM统计指标仓储
|
||||
func NewGormStatisticsRepository(db *gorm.DB) repositories.StatisticsRepository {
|
||||
return &GormStatisticsRepository{
|
||||
db: db,
|
||||
}
|
||||
}
|
||||
|
||||
// Save 保存统计指标
|
||||
func (r *GormStatisticsRepository) Save(ctx context.Context, metric *entities.StatisticsMetric) error {
|
||||
if metric == nil {
|
||||
return fmt.Errorf("统计指标不能为空")
|
||||
}
|
||||
|
||||
// 验证指标
|
||||
if err := metric.Validate(); err != nil {
|
||||
return fmt.Errorf("统计指标验证失败: %w", err)
|
||||
}
|
||||
|
||||
// 保存到数据库
|
||||
result := r.db.WithContext(ctx).Create(metric)
|
||||
if result.Error != nil {
|
||||
return fmt.Errorf("保存统计指标失败: %w", result.Error)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// FindByID 根据ID查找统计指标
|
||||
func (r *GormStatisticsRepository) FindByID(ctx context.Context, id string) (*entities.StatisticsMetric, error) {
|
||||
if id == "" {
|
||||
return nil, fmt.Errorf("指标ID不能为空")
|
||||
}
|
||||
|
||||
var metric entities.StatisticsMetric
|
||||
result := r.db.WithContext(ctx).Where("id = ?", id).First(&metric)
|
||||
if result.Error != nil {
|
||||
if result.Error == gorm.ErrRecordNotFound {
|
||||
return nil, fmt.Errorf("统计指标不存在")
|
||||
}
|
||||
return nil, fmt.Errorf("查询统计指标失败: %w", result.Error)
|
||||
}
|
||||
|
||||
return &metric, nil
|
||||
}
|
||||
|
||||
// FindByType 根据类型查找统计指标
|
||||
func (r *GormStatisticsRepository) FindByType(ctx context.Context, metricType string, limit, offset int) ([]*entities.StatisticsMetric, error) {
|
||||
if metricType == "" {
|
||||
return nil, fmt.Errorf("指标类型不能为空")
|
||||
}
|
||||
|
||||
var metrics []*entities.StatisticsMetric
|
||||
query := r.db.WithContext(ctx).Where("metric_type = ?", metricType)
|
||||
|
||||
if limit > 0 {
|
||||
query = query.Limit(limit)
|
||||
}
|
||||
if offset > 0 {
|
||||
query = query.Offset(offset)
|
||||
}
|
||||
|
||||
result := query.Order("created_at DESC").Find(&metrics)
|
||||
if result.Error != nil {
|
||||
return nil, fmt.Errorf("查询统计指标失败: %w", result.Error)
|
||||
}
|
||||
|
||||
return metrics, nil
|
||||
}
|
||||
|
||||
// Update 更新统计指标
|
||||
func (r *GormStatisticsRepository) Update(ctx context.Context, metric *entities.StatisticsMetric) error {
|
||||
if metric == nil {
|
||||
return fmt.Errorf("统计指标不能为空")
|
||||
}
|
||||
|
||||
if metric.ID == "" {
|
||||
return fmt.Errorf("指标ID不能为空")
|
||||
}
|
||||
|
||||
// 验证指标
|
||||
if err := metric.Validate(); err != nil {
|
||||
return fmt.Errorf("统计指标验证失败: %w", err)
|
||||
}
|
||||
|
||||
// 更新数据库
|
||||
result := r.db.WithContext(ctx).Save(metric)
|
||||
if result.Error != nil {
|
||||
return fmt.Errorf("更新统计指标失败: %w", result.Error)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Delete 删除统计指标
|
||||
func (r *GormStatisticsRepository) Delete(ctx context.Context, id string) error {
|
||||
if id == "" {
|
||||
return fmt.Errorf("指标ID不能为空")
|
||||
}
|
||||
|
||||
result := r.db.WithContext(ctx).Delete(&entities.StatisticsMetric{}, "id = ?", id)
|
||||
if result.Error != nil {
|
||||
return fmt.Errorf("删除统计指标失败: %w", result.Error)
|
||||
}
|
||||
|
||||
if result.RowsAffected == 0 {
|
||||
return fmt.Errorf("统计指标不存在")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// FindByTypeAndDateRange 根据类型和日期范围查找统计指标
|
||||
func (r *GormStatisticsRepository) FindByTypeAndDateRange(ctx context.Context, metricType string, startDate, endDate time.Time) ([]*entities.StatisticsMetric, error) {
|
||||
if metricType == "" {
|
||||
return nil, fmt.Errorf("指标类型不能为空")
|
||||
}
|
||||
|
||||
if startDate.IsZero() || endDate.IsZero() {
|
||||
return nil, fmt.Errorf("开始日期和结束日期不能为空")
|
||||
}
|
||||
|
||||
var metrics []*entities.StatisticsMetric
|
||||
result := r.db.WithContext(ctx).
|
||||
Where("metric_type = ? AND date >= ? AND date < ?", metricType, startDate, endDate).
|
||||
Order("date ASC").
|
||||
Find(&metrics)
|
||||
|
||||
if result.Error != nil {
|
||||
return nil, fmt.Errorf("查询统计指标失败: %w", result.Error)
|
||||
}
|
||||
|
||||
return metrics, nil
|
||||
}
|
||||
|
||||
// FindByTypeDimensionAndDateRange 根据类型、维度和日期范围查找统计指标
|
||||
func (r *GormStatisticsRepository) FindByTypeDimensionAndDateRange(ctx context.Context, metricType, dimension string, startDate, endDate time.Time) ([]*entities.StatisticsMetric, error) {
|
||||
if metricType == "" {
|
||||
return nil, fmt.Errorf("指标类型不能为空")
|
||||
}
|
||||
|
||||
if startDate.IsZero() || endDate.IsZero() {
|
||||
return nil, fmt.Errorf("开始日期和结束日期不能为空")
|
||||
}
|
||||
|
||||
var metrics []*entities.StatisticsMetric
|
||||
query := r.db.WithContext(ctx).
|
||||
Where("metric_type = ? AND date >= ? AND date < ?", metricType, startDate, endDate)
|
||||
|
||||
if dimension != "" {
|
||||
query = query.Where("dimension = ?", dimension)
|
||||
}
|
||||
|
||||
result := query.Order("date ASC").Find(&metrics)
|
||||
if result.Error != nil {
|
||||
return nil, fmt.Errorf("查询统计指标失败: %w", result.Error)
|
||||
}
|
||||
|
||||
return metrics, nil
|
||||
}
|
||||
|
||||
// FindByTypeNameAndDateRange 根据类型、名称和日期范围查找统计指标
|
||||
func (r *GormStatisticsRepository) FindByTypeNameAndDateRange(ctx context.Context, metricType, metricName string, startDate, endDate time.Time) ([]*entities.StatisticsMetric, error) {
|
||||
if metricType == "" {
|
||||
return nil, fmt.Errorf("指标类型不能为空")
|
||||
}
|
||||
|
||||
if metricName == "" {
|
||||
return nil, fmt.Errorf("指标名称不能为空")
|
||||
}
|
||||
|
||||
if startDate.IsZero() || endDate.IsZero() {
|
||||
return nil, fmt.Errorf("开始日期和结束日期不能为空")
|
||||
}
|
||||
|
||||
var metrics []*entities.StatisticsMetric
|
||||
result := r.db.WithContext(ctx).
|
||||
Where("metric_type = ? AND metric_name = ? AND date >= ? AND date < ?",
|
||||
metricType, metricName, startDate, endDate).
|
||||
Order("date ASC").
|
||||
Find(&metrics)
|
||||
|
||||
if result.Error != nil {
|
||||
return nil, fmt.Errorf("查询统计指标失败: %w", result.Error)
|
||||
}
|
||||
|
||||
return metrics, nil
|
||||
}
|
||||
|
||||
// GetAggregatedMetrics 获取聚合指标
|
||||
func (r *GormStatisticsRepository) GetAggregatedMetrics(ctx context.Context, metricType, dimension string, startDate, endDate time.Time) (map[string]float64, error) {
|
||||
if metricType == "" {
|
||||
return nil, fmt.Errorf("指标类型不能为空")
|
||||
}
|
||||
|
||||
if startDate.IsZero() || endDate.IsZero() {
|
||||
return nil, fmt.Errorf("开始日期和结束日期不能为空")
|
||||
}
|
||||
|
||||
type AggregatedResult struct {
|
||||
MetricName string `json:"metric_name"`
|
||||
TotalValue float64 `json:"total_value"`
|
||||
}
|
||||
|
||||
var results []AggregatedResult
|
||||
query := r.db.WithContext(ctx).
|
||||
Model(&entities.StatisticsMetric{}).
|
||||
Select("metric_name, SUM(value) as total_value").
|
||||
Where("metric_type = ? AND date >= ? AND date < ?", metricType, startDate, endDate).
|
||||
Group("metric_name")
|
||||
|
||||
if dimension != "" {
|
||||
query = query.Where("dimension = ?", dimension)
|
||||
}
|
||||
|
||||
result := query.Find(&results)
|
||||
if result.Error != nil {
|
||||
return nil, fmt.Errorf("查询聚合指标失败: %w", result.Error)
|
||||
}
|
||||
|
||||
// 转换为map
|
||||
aggregated := make(map[string]float64)
|
||||
for _, res := range results {
|
||||
aggregated[res.MetricName] = res.TotalValue
|
||||
}
|
||||
|
||||
return aggregated, nil
|
||||
}
|
||||
|
||||
// GetMetricsByDimension 根据维度获取指标
|
||||
func (r *GormStatisticsRepository) GetMetricsByDimension(ctx context.Context, dimension string, startDate, endDate time.Time) ([]*entities.StatisticsMetric, error) {
|
||||
if dimension == "" {
|
||||
return nil, fmt.Errorf("统计维度不能为空")
|
||||
}
|
||||
|
||||
if startDate.IsZero() || endDate.IsZero() {
|
||||
return nil, fmt.Errorf("开始日期和结束日期不能为空")
|
||||
}
|
||||
|
||||
var metrics []*entities.StatisticsMetric
|
||||
result := r.db.WithContext(ctx).
|
||||
Where("dimension = ? AND date >= ? AND date < ?", dimension, startDate, endDate).
|
||||
Order("date ASC").
|
||||
Find(&metrics)
|
||||
|
||||
if result.Error != nil {
|
||||
return nil, fmt.Errorf("查询统计指标失败: %w", result.Error)
|
||||
}
|
||||
|
||||
return metrics, nil
|
||||
}
|
||||
|
||||
// CountByType 根据类型统计数量
|
||||
func (r *GormStatisticsRepository) CountByType(ctx context.Context, metricType string) (int64, error) {
|
||||
if metricType == "" {
|
||||
return 0, fmt.Errorf("指标类型不能为空")
|
||||
}
|
||||
|
||||
var count int64
|
||||
result := r.db.WithContext(ctx).
|
||||
Model(&entities.StatisticsMetric{}).
|
||||
Where("metric_type = ?", metricType).
|
||||
Count(&count)
|
||||
|
||||
if result.Error != nil {
|
||||
return 0, fmt.Errorf("统计指标数量失败: %w", result.Error)
|
||||
}
|
||||
|
||||
return count, nil
|
||||
}
|
||||
|
||||
// CountByTypeAndDateRange 根据类型和日期范围统计数量
|
||||
func (r *GormStatisticsRepository) CountByTypeAndDateRange(ctx context.Context, metricType string, startDate, endDate time.Time) (int64, error) {
|
||||
if metricType == "" {
|
||||
return 0, fmt.Errorf("指标类型不能为空")
|
||||
}
|
||||
|
||||
if startDate.IsZero() || endDate.IsZero() {
|
||||
return 0, fmt.Errorf("开始日期和结束日期不能为空")
|
||||
}
|
||||
|
||||
var count int64
|
||||
result := r.db.WithContext(ctx).
|
||||
Model(&entities.StatisticsMetric{}).
|
||||
Where("metric_type = ? AND date >= ? AND date < ?", metricType, startDate, endDate).
|
||||
Count(&count)
|
||||
|
||||
if result.Error != nil {
|
||||
return 0, fmt.Errorf("统计指标数量失败: %w", result.Error)
|
||||
}
|
||||
|
||||
return count, nil
|
||||
}
|
||||
|
||||
// BatchSave 批量保存统计指标
|
||||
func (r *GormStatisticsRepository) BatchSave(ctx context.Context, metrics []*entities.StatisticsMetric) error {
|
||||
if len(metrics) == 0 {
|
||||
return fmt.Errorf("统计指标列表不能为空")
|
||||
}
|
||||
|
||||
// 验证所有指标
|
||||
for _, metric := range metrics {
|
||||
if err := metric.Validate(); err != nil {
|
||||
return fmt.Errorf("统计指标验证失败: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// 批量保存
|
||||
result := r.db.WithContext(ctx).CreateInBatches(metrics, 100)
|
||||
if result.Error != nil {
|
||||
return fmt.Errorf("批量保存统计指标失败: %w", result.Error)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// BatchDelete 批量删除统计指标
|
||||
func (r *GormStatisticsRepository) BatchDelete(ctx context.Context, ids []string) error {
|
||||
if len(ids) == 0 {
|
||||
return fmt.Errorf("指标ID列表不能为空")
|
||||
}
|
||||
|
||||
result := r.db.WithContext(ctx).Delete(&entities.StatisticsMetric{}, "id IN ?", ids)
|
||||
if result.Error != nil {
|
||||
return fmt.Errorf("批量删除统计指标失败: %w", result.Error)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteByDateRange 根据日期范围删除统计指标
|
||||
func (r *GormStatisticsRepository) DeleteByDateRange(ctx context.Context, startDate, endDate time.Time) error {
|
||||
if startDate.IsZero() || endDate.IsZero() {
|
||||
return fmt.Errorf("开始日期和结束日期不能为空")
|
||||
}
|
||||
|
||||
result := r.db.WithContext(ctx).
|
||||
Delete(&entities.StatisticsMetric{}, "date >= ? AND date < ?", startDate, endDate)
|
||||
if result.Error != nil {
|
||||
return fmt.Errorf("根据日期范围删除统计指标失败: %w", result.Error)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteByTypeAndDateRange 根据类型和日期范围删除统计指标
|
||||
func (r *GormStatisticsRepository) DeleteByTypeAndDateRange(ctx context.Context, metricType string, startDate, endDate time.Time) error {
|
||||
if metricType == "" {
|
||||
return fmt.Errorf("指标类型不能为空")
|
||||
}
|
||||
|
||||
if startDate.IsZero() || endDate.IsZero() {
|
||||
return fmt.Errorf("开始日期和结束日期不能为空")
|
||||
}
|
||||
|
||||
result := r.db.WithContext(ctx).
|
||||
Delete(&entities.StatisticsMetric{}, "metric_type = ? AND date >= ? AND date < ?",
|
||||
metricType, startDate, endDate)
|
||||
if result.Error != nil {
|
||||
return fmt.Errorf("根据类型和日期范围删除统计指标失败: %w", result.Error)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -6,6 +6,7 @@ package repositories
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"go.uber.org/zap"
|
||||
@@ -71,6 +72,20 @@ func (r *GormUserRepository) GetByIDWithEnterpriseInfo(ctx context.Context, id s
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func (r *GormUserRepository) BatchGetByIDsWithEnterpriseInfo(ctx context.Context, ids []string) ([]*entities.User, error) {
|
||||
if len(ids) == 0 {
|
||||
return []*entities.User{}, nil
|
||||
}
|
||||
|
||||
var users []*entities.User
|
||||
if err := r.GetDB(ctx).Preload("EnterpriseInfo").Where("id IN ?", ids).Find(&users).Error; err != nil {
|
||||
r.GetLogger().Error("批量查询用户失败", zap.Error(err), zap.Strings("ids", ids))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return users, nil
|
||||
}
|
||||
|
||||
func (r *GormUserRepository) ExistsByUnifiedSocialCode(ctx context.Context, unifiedSocialCode string, excludeUserID string) (bool, error) {
|
||||
var count int64
|
||||
query := r.GetDB(ctx).Model(&entities.User{}).
|
||||
@@ -337,3 +352,315 @@ func (r *GormUserRepository) GetStatsByDateRange(ctx context.Context, startDate,
|
||||
|
||||
return &stats, nil
|
||||
}
|
||||
|
||||
// GetSystemUserStats 获取系统用户统计信息
|
||||
func (r *GormUserRepository) GetSystemUserStats(ctx context.Context) (*repositories.UserStats, error) {
|
||||
var stats repositories.UserStats
|
||||
|
||||
db := r.GetDB(ctx)
|
||||
|
||||
// 总用户数
|
||||
if err := db.Model(&entities.User{}).Count(&stats.TotalUsers).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 活跃用户数(最近30天有登录)
|
||||
thirtyDaysAgo := time.Now().AddDate(0, 0, -30)
|
||||
if err := db.Model(&entities.User{}).Where("last_login_at >= ?", thirtyDaysAgo).Count(&stats.ActiveUsers).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 已认证用户数
|
||||
if err := db.Model(&entities.User{}).Where("is_certified = ?", true).Count(&stats.CertifiedUsers).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 今日注册数
|
||||
today := time.Now().Truncate(24 * time.Hour)
|
||||
if err := db.Model(&entities.User{}).Where("created_at >= ?", today).Count(&stats.TodayRegistrations).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 今日登录数
|
||||
if err := db.Model(&entities.User{}).Where("last_login_at >= ?", today).Count(&stats.TodayLogins).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &stats, nil
|
||||
}
|
||||
|
||||
// GetSystemUserStatsByDateRange 获取系统指定时间范围内的用户统计信息
|
||||
func (r *GormUserRepository) GetSystemUserStatsByDateRange(ctx context.Context, startDate, endDate time.Time) (*repositories.UserStats, error) {
|
||||
var stats repositories.UserStats
|
||||
|
||||
db := r.GetDB(ctx)
|
||||
|
||||
// 指定时间范围内的注册数
|
||||
if err := db.Model(&entities.User{}).
|
||||
Where("created_at >= ? AND created_at <= ?", startDate, endDate).
|
||||
Count(&stats.TodayRegistrations).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 指定时间范围内的登录数
|
||||
if err := db.Model(&entities.User{}).
|
||||
Where("last_login_at >= ? AND last_login_at <= ?", startDate, endDate).
|
||||
Count(&stats.TodayLogins).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &stats, nil
|
||||
}
|
||||
|
||||
// GetSystemDailyUserStats 获取系统每日用户统计
|
||||
func (r *GormUserRepository) GetSystemDailyUserStats(ctx context.Context, startDate, endDate time.Time) ([]map[string]interface{}, error) {
|
||||
var results []map[string]interface{}
|
||||
|
||||
sql := `
|
||||
SELECT
|
||||
DATE(created_at) as date,
|
||||
COUNT(*) as count
|
||||
FROM users
|
||||
WHERE DATE(created_at) >= $1
|
||||
AND DATE(created_at) <= $2
|
||||
GROUP BY DATE(created_at)
|
||||
ORDER BY date ASC
|
||||
`
|
||||
|
||||
err := r.GetDB(ctx).Raw(sql, startDate.Format("2006-01-02"), endDate.Format("2006-01-02")).Scan(&results).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
||||
// GetSystemMonthlyUserStats 获取系统每月用户统计
|
||||
func (r *GormUserRepository) GetSystemMonthlyUserStats(ctx context.Context, startDate, endDate time.Time) ([]map[string]interface{}, error) {
|
||||
var results []map[string]interface{}
|
||||
|
||||
sql := `
|
||||
SELECT
|
||||
TO_CHAR(created_at, 'YYYY-MM') as month,
|
||||
COUNT(*) as count
|
||||
FROM users
|
||||
WHERE created_at >= $1
|
||||
AND created_at <= $2
|
||||
GROUP BY TO_CHAR(created_at, 'YYYY-MM')
|
||||
ORDER BY month ASC
|
||||
`
|
||||
|
||||
err := r.GetDB(ctx).Raw(sql, startDate, endDate).Scan(&results).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
||||
// GetUserCallRankingByCalls 按调用次数获取用户排行
|
||||
func (r *GormUserRepository) GetUserCallRankingByCalls(ctx context.Context, period string, limit int) ([]map[string]interface{}, error) {
|
||||
var sql string
|
||||
var args []interface{}
|
||||
|
||||
switch period {
|
||||
case "today":
|
||||
sql = `
|
||||
SELECT
|
||||
u.id as user_id,
|
||||
COALESCE(ei.company_name, u.username, u.phone) as username,
|
||||
COUNT(ac.id) as calls
|
||||
FROM users u
|
||||
LEFT JOIN enterprise_infos ei ON u.id = ei.user_id
|
||||
LEFT JOIN api_calls ac ON u.id = ac.user_id
|
||||
AND DATE(ac.created_at) = CURRENT_DATE
|
||||
WHERE u.deleted_at IS NULL
|
||||
GROUP BY u.id, ei.company_name, u.username, u.phone
|
||||
HAVING COUNT(ac.id) > 0
|
||||
ORDER BY calls DESC
|
||||
LIMIT $1
|
||||
`
|
||||
args = []interface{}{limit}
|
||||
case "month":
|
||||
sql = `
|
||||
SELECT
|
||||
u.id as user_id,
|
||||
COALESCE(ei.company_name, u.username, u.phone) as username,
|
||||
COUNT(ac.id) as calls
|
||||
FROM users u
|
||||
LEFT JOIN enterprise_infos ei ON u.id = ei.user_id
|
||||
LEFT JOIN api_calls ac ON u.id = ac.user_id
|
||||
AND DATE_TRUNC('month', ac.created_at) = DATE_TRUNC('month', CURRENT_DATE)
|
||||
WHERE u.deleted_at IS NULL
|
||||
GROUP BY u.id, ei.company_name, u.username, u.phone
|
||||
HAVING COUNT(ac.id) > 0
|
||||
ORDER BY calls DESC
|
||||
LIMIT $1
|
||||
`
|
||||
args = []interface{}{limit}
|
||||
case "total":
|
||||
sql = `
|
||||
SELECT
|
||||
u.id as user_id,
|
||||
COALESCE(ei.company_name, u.username, u.phone) as username,
|
||||
COUNT(ac.id) as calls
|
||||
FROM users u
|
||||
LEFT JOIN enterprise_infos ei ON u.id = ei.user_id
|
||||
LEFT JOIN api_calls ac ON u.id = ac.user_id
|
||||
WHERE u.deleted_at IS NULL
|
||||
GROUP BY u.id, ei.company_name, u.username, u.phone
|
||||
HAVING COUNT(ac.id) > 0
|
||||
ORDER BY calls DESC
|
||||
LIMIT $1
|
||||
`
|
||||
args = []interface{}{limit}
|
||||
default:
|
||||
return nil, fmt.Errorf("不支持的时间周期: %s", period)
|
||||
}
|
||||
|
||||
var results []map[string]interface{}
|
||||
err := r.GetDB(ctx).Raw(sql, args...).Scan(&results).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
||||
// GetUserCallRankingByConsumption 按消费金额获取用户排行
|
||||
func (r *GormUserRepository) GetUserCallRankingByConsumption(ctx context.Context, period string, limit int) ([]map[string]interface{}, error) {
|
||||
var sql string
|
||||
var args []interface{}
|
||||
|
||||
switch period {
|
||||
case "today":
|
||||
sql = `
|
||||
SELECT
|
||||
u.id as user_id,
|
||||
COALESCE(ei.company_name, u.username, u.phone) as username,
|
||||
COALESCE(SUM(wt.amount), 0) as consumption
|
||||
FROM users u
|
||||
LEFT JOIN enterprise_infos ei ON u.id = ei.user_id
|
||||
LEFT JOIN wallet_transactions wt ON u.id = wt.user_id
|
||||
AND DATE(wt.created_at) = CURRENT_DATE
|
||||
WHERE u.deleted_at IS NULL
|
||||
GROUP BY u.id, ei.company_name, u.username, u.phone
|
||||
HAVING COALESCE(SUM(wt.amount), 0) > 0
|
||||
ORDER BY consumption DESC
|
||||
LIMIT $1
|
||||
`
|
||||
args = []interface{}{limit}
|
||||
case "month":
|
||||
sql = `
|
||||
SELECT
|
||||
u.id as user_id,
|
||||
COALESCE(ei.company_name, u.username, u.phone) as username,
|
||||
COALESCE(SUM(wt.amount), 0) as consumption
|
||||
FROM users u
|
||||
LEFT JOIN enterprise_infos ei ON u.id = ei.user_id
|
||||
LEFT JOIN wallet_transactions wt ON u.id = wt.user_id
|
||||
AND DATE_TRUNC('month', wt.created_at) = DATE_TRUNC('month', CURRENT_DATE)
|
||||
WHERE u.deleted_at IS NULL
|
||||
GROUP BY u.id, ei.company_name, u.username, u.phone
|
||||
HAVING COALESCE(SUM(wt.amount), 0) > 0
|
||||
ORDER BY consumption DESC
|
||||
LIMIT $1
|
||||
`
|
||||
args = []interface{}{limit}
|
||||
case "total":
|
||||
sql = `
|
||||
SELECT
|
||||
u.id as user_id,
|
||||
COALESCE(ei.company_name, u.username, u.phone) as username,
|
||||
COALESCE(SUM(wt.amount), 0) as consumption
|
||||
FROM users u
|
||||
LEFT JOIN enterprise_infos ei ON u.id = ei.user_id
|
||||
LEFT JOIN wallet_transactions wt ON u.id = wt.user_id
|
||||
WHERE u.deleted_at IS NULL
|
||||
GROUP BY u.id, ei.company_name, u.username, u.phone
|
||||
HAVING COALESCE(SUM(wt.amount), 0) > 0
|
||||
ORDER BY consumption DESC
|
||||
LIMIT $1
|
||||
`
|
||||
args = []interface{}{limit}
|
||||
default:
|
||||
return nil, fmt.Errorf("不支持的时间周期: %s", period)
|
||||
}
|
||||
|
||||
var results []map[string]interface{}
|
||||
err := r.GetDB(ctx).Raw(sql, args...).Scan(&results).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
||||
// GetRechargeRanking 获取充值排行
|
||||
func (r *GormUserRepository) GetRechargeRanking(ctx context.Context, period string, limit int) ([]map[string]interface{}, error) {
|
||||
var sql string
|
||||
var args []interface{}
|
||||
|
||||
switch period {
|
||||
case "today":
|
||||
sql = `
|
||||
SELECT
|
||||
u.id as user_id,
|
||||
COALESCE(ei.company_name, u.username, u.phone) as username,
|
||||
COALESCE(SUM(rr.amount), 0) as amount
|
||||
FROM users u
|
||||
LEFT JOIN enterprise_infos ei ON u.id = ei.user_id
|
||||
LEFT JOIN recharge_records rr ON u.id = rr.user_id
|
||||
AND DATE(rr.created_at) = CURRENT_DATE
|
||||
WHERE u.deleted_at IS NULL
|
||||
GROUP BY u.id, ei.company_name, u.username, u.phone
|
||||
HAVING COALESCE(SUM(rr.amount), 0) > 0
|
||||
ORDER BY amount DESC
|
||||
LIMIT $1
|
||||
`
|
||||
args = []interface{}{limit}
|
||||
case "month":
|
||||
sql = `
|
||||
SELECT
|
||||
u.id as user_id,
|
||||
COALESCE(ei.company_name, u.username, u.phone) as username,
|
||||
COALESCE(SUM(rr.amount), 0) as amount
|
||||
FROM users u
|
||||
LEFT JOIN enterprise_infos ei ON u.id = ei.user_id
|
||||
LEFT JOIN recharge_records rr ON u.id = rr.user_id
|
||||
AND DATE_TRUNC('month', rr.created_at) = DATE_TRUNC('month', CURRENT_DATE)
|
||||
WHERE u.deleted_at IS NULL
|
||||
GROUP BY u.id, ei.company_name, u.username, u.phone
|
||||
HAVING COALESCE(SUM(rr.amount), 0) > 0
|
||||
ORDER BY amount DESC
|
||||
LIMIT $1
|
||||
`
|
||||
args = []interface{}{limit}
|
||||
case "total":
|
||||
sql = `
|
||||
SELECT
|
||||
u.id as user_id,
|
||||
COALESCE(ei.company_name, u.username, u.phone) as username,
|
||||
COALESCE(SUM(rr.amount), 0) as amount
|
||||
FROM users u
|
||||
LEFT JOIN enterprise_infos ei ON u.id = ei.user_id
|
||||
LEFT JOIN recharge_records rr ON u.id = rr.user_id
|
||||
WHERE u.deleted_at IS NULL
|
||||
GROUP BY u.id, ei.company_name, u.username, u.phone
|
||||
HAVING COALESCE(SUM(rr.amount), 0) > 0
|
||||
ORDER BY amount DESC
|
||||
LIMIT $1
|
||||
`
|
||||
args = []interface{}{limit}
|
||||
default:
|
||||
return nil, fmt.Errorf("不支持的时间周期: %s", period)
|
||||
}
|
||||
|
||||
var results []map[string]interface{}
|
||||
err := r.GetDB(ctx).Raw(sql, args...).Scan(&results).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
Reference in New Issue
Block a user