HaniBlindBox/server/HoneyBox/src/HoneyBox.Core/Services/RankService.cs
2026-01-04 01:47:02 +08:00

628 lines
21 KiB
C#
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.

using HoneyBox.Core.Interfaces;
using HoneyBox.Model.Data;
using HoneyBox.Model.Models.Rank;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
namespace HoneyBox.Core.Services;
/// <summary>
/// 排行榜服务实现
/// </summary>
public class RankService : IRankService
{
private readonly HoneyBoxDbContext _dbContext;
private readonly ILogger<RankService> _logger;
/// <summary>
/// 有效的排行榜类型
/// </summary>
private static readonly HashSet<string> ValidRankTypes = new(StringComparer.OrdinalIgnoreCase)
{
"diamond", "integral", "dadajuan", "invite", "loss"
};
public RankService(HoneyBoxDbContext dbContext, ILogger<RankService> logger)
{
_dbContext = dbContext;
_logger = logger;
}
/// <inheritdoc />
public async Task<RankResponse> GetWeekRankAsync(int userId)
{
// 获取当前用户信息
var user = await _dbContext.Users
.Where(u => u.Id == userId)
.Select(u => new { u.Nickname, u.HeadImg })
.FirstOrDefaultAsync();
if (user == null)
{
throw new InvalidOperationException("用户不存在");
}
// 计算本周时间范围(周一到周日)
var (weekStart, weekEnd) = GetWeekRange(DateTime.Now);
var weekStartTimestamp = ToUnixTimestamp(weekStart);
var weekEndTimestamp = ToUnixTimestamp(weekEnd);
// 查询当前用户本周消费总额
var myOrderTotal = await _dbContext.Orders
.Where(o => o.UserId == userId
&& o.Addtime >= weekStartTimestamp
&& o.Addtime <= weekEndTimestamp
&& o.Status == 1
&& o.OrderType < 5)
.SumAsync(o => (decimal?)o.OrderTotal) ?? 0;
// 查询排行榜前30名
var rankData = await _dbContext.Orders
.Where(o => o.Addtime >= weekStartTimestamp
&& o.Addtime <= weekEndTimestamp
&& o.Status == 1
&& o.OrderType < 5)
.GroupBy(o => o.UserId)
.Select(g => new
{
UserId = g.Key,
OrderTotal = g.Sum(o => o.OrderTotal)
})
.OrderByDescending(x => x.OrderTotal)
.ThenBy(x => x.UserId)
.Take(30)
.ToListAsync();
// 获取用户信息
var userIds = rankData.Select(r => r.UserId).ToList();
var users = await _dbContext.Users
.Where(u => userIds.Contains(u.Id))
.Select(u => new { u.Id, u.Nickname, u.HeadImg })
.ToDictionaryAsync(u => u.Id);
// 获取周榜奖品信息 (goods_id = -1 表示周榜奖品)
var prizes = await _dbContext.GoodsItems
.Where(g => g.GoodsId == -1 && g.Rank >= 1 && g.Rank <= 30)
.Select(g => new { g.Rank, g.Title, g.ImgUrl })
.ToDictionaryAsync(g => g.Rank);
// 构建排行榜数据
var data = new List<RankItemDto>();
object myRank = "暂未上榜";
object myPrizeTitle = 0;
object myPrizeImgurl = 0;
for (int i = 0; i < rankData.Count; i++)
{
var item = rankData[i];
var rank = i + 1;
var userInfo = users.GetValueOrDefault(item.UserId);
var prizeInfo = prizes.GetValueOrDefault(rank);
var rankItem = new RankItemDto
{
Rank = rank,
UserId = item.UserId,
Nickname = userInfo?.Nickname ?? "",
Headimg = userInfo?.HeadImg ?? "",
OrderTotal = item.OrderTotal,
PrizeTitle = prizeInfo?.Title ?? "",
PrizeImgurl = prizeInfo?.ImgUrl ?? ""
};
data.Add(rankItem);
// 判断当前用户是否在排名中
if (item.UserId == userId)
{
myRank = rank;
myPrizeTitle = prizeInfo?.Title ?? "";
myPrizeImgurl = prizeInfo?.ImgUrl ?? "";
}
}
return new RankResponse
{
Date = $"{weekStart:MM月dd日}-{weekEnd:MM月dd日}",
EndDate = weekEndTimestamp,
MyRank = new MyRankDto
{
MyRank = myRank,
MyPrizeTitle = myPrizeTitle,
MyPrizeImgurl = myPrizeImgurl,
MyOrderTotal = myOrderTotal,
MyNickname = user.Nickname,
MyHeadimg = user.HeadImg
},
Data = data
};
}
/// <inheritdoc />
public async Task<RankResponse> GetMonthRankAsync(int userId)
{
// 获取当前用户信息
var user = await _dbContext.Users
.Where(u => u.Id == userId)
.Select(u => new { u.Nickname, u.HeadImg })
.FirstOrDefaultAsync();
if (user == null)
{
throw new InvalidOperationException("用户不存在");
}
// 计算本月时间范围
var (monthStart, monthEnd) = GetMonthRange(DateTime.Now);
var monthStartTimestamp = ToUnixTimestamp(monthStart);
var monthEndTimestamp = ToUnixTimestamp(monthEnd);
// 查询当前用户本月消费总额
var myOrderTotal = await _dbContext.Orders
.Where(o => o.UserId == userId
&& o.Addtime >= monthStartTimestamp
&& o.Addtime <= monthEndTimestamp
&& o.Status == 1
&& o.OrderType < 5)
.SumAsync(o => (decimal?)o.OrderTotal) ?? 0;
// 查询排行榜前30名
var rankData = await _dbContext.Orders
.Where(o => o.Addtime >= monthStartTimestamp
&& o.Addtime <= monthEndTimestamp
&& o.Status == 1
&& o.OrderType < 5)
.GroupBy(o => o.UserId)
.Select(g => new
{
UserId = g.Key,
OrderTotal = g.Sum(o => o.OrderTotal)
})
.OrderByDescending(x => x.OrderTotal)
.ThenBy(x => x.UserId)
.Take(30)
.ToListAsync();
// 获取用户信息
var userIds = rankData.Select(r => r.UserId).ToList();
var users = await _dbContext.Users
.Where(u => userIds.Contains(u.Id))
.Select(u => new { u.Id, u.Nickname, u.HeadImg })
.ToDictionaryAsync(u => u.Id);
// 获取月榜奖品信息 (goods_id = -2 表示月榜奖品)
var prizes = await _dbContext.GoodsItems
.Where(g => g.GoodsId == -2 && g.Rank >= 1 && g.Rank <= 30)
.Select(g => new { g.Rank, g.Title, g.ImgUrl })
.ToDictionaryAsync(g => g.Rank);
// 构建排行榜数据
var data = new List<RankItemDto>();
object myRank = "暂未上榜";
object myPrizeTitle = 0;
object myPrizeImgurl = 0;
for (int i = 0; i < rankData.Count; i++)
{
var item = rankData[i];
var rank = i + 1;
var userInfo = users.GetValueOrDefault(item.UserId);
var prizeInfo = prizes.GetValueOrDefault(rank);
var rankItem = new RankItemDto
{
Rank = rank,
UserId = item.UserId,
Nickname = userInfo?.Nickname ?? "",
Headimg = userInfo?.HeadImg ?? "",
OrderTotal = item.OrderTotal,
PrizeTitle = prizeInfo?.Title ?? "",
PrizeImgurl = prizeInfo?.ImgUrl ?? ""
};
data.Add(rankItem);
// 判断当前用户是否在排名中
if (item.UserId == userId)
{
myRank = rank;
myPrizeTitle = prizeInfo?.Title ?? "";
myPrizeImgurl = prizeInfo?.ImgUrl ?? "";
}
}
return new RankResponse
{
Date = $"{monthStart:MM月dd日}-{monthEnd:MM月dd日}",
EndDate = monthEndTimestamp,
MyRank = new MyRankDto
{
MyRank = myRank,
MyPrizeTitle = myPrizeTitle,
MyPrizeImgurl = myPrizeImgurl,
MyOrderTotal = myOrderTotal,
MyNickname = user.Nickname,
MyHeadimg = user.HeadImg
},
Data = data
};
}
/// <summary>
/// 获取本周时间范围周一00:00:00到周日23:59:59
/// </summary>
private static (DateTime start, DateTime end) GetWeekRange(DateTime date)
{
// 获取当前是周几 (0=周日, 1=周一, ..., 6=周六)
var dayOfWeek = (int)date.DayOfWeek;
// 计算到周一的天数差
var daysToMonday = dayOfWeek == 0 ? 6 : dayOfWeek - 1;
// 本周一00:00:00
var weekStart = date.Date.AddDays(-daysToMonday);
// 本周日23:59:59
var weekEnd = weekStart.AddDays(6).AddHours(23).AddMinutes(59).AddSeconds(59);
return (weekStart, weekEnd);
}
/// <summary>
/// 获取本月时间范围1号00:00:00到月末23:59:59
/// </summary>
private static (DateTime start, DateTime end) GetMonthRange(DateTime date)
{
// 本月1号00:00:00
var monthStart = new DateTime(date.Year, date.Month, 1, 0, 0, 0);
// 本月最后一天23:59:59
var daysInMonth = DateTime.DaysInMonth(date.Year, date.Month);
var monthEnd = new DateTime(date.Year, date.Month, daysInMonth, 23, 59, 59);
return (monthStart, monthEnd);
}
/// <summary>
/// 将DateTime转换为Unix时间戳
/// </summary>
private static int ToUnixTimestamp(DateTime dateTime)
{
return (int)(dateTime.ToUniversalTime() - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds;
}
/// <inheritdoc />
public async Task<List<GenericRankItemDto>> GetRankListAsync(string type, int page = 1, int limit = 10)
{
if (!ValidRankTypes.Contains(type))
{
throw new InvalidOperationException("无效的排行榜类型");
}
return type.ToLower() switch
{
"diamond" => await GetDiamondRankAsync(page, limit),
"integral" => await GetIntegralRankAsync(page, limit),
"dadajuan" => await GetDadajuanRankAsync(page, limit),
"invite" => await GetInviteRankAsync(page, limit),
"loss" => await GetLossRankAsync(page, limit),
_ => new List<GenericRankItemDto>()
};
}
/// <inheritdoc />
public async Task<ConsumeRecordResponse> GetConsumeRecordAsync(int page = 1, int limit = 10)
{
// 获取本月时间范围
var (monthStart, monthEnd) = GetMonthRange(DateTime.Now);
var monthStartTimestamp = ToUnixTimestamp(monthStart);
var monthEndTimestamp = ToUnixTimestamp(monthEnd);
// 查询本月消费排行榜
var rankData = await _dbContext.Orders
.Where(o => o.Status == 1
&& o.Price > 0
&& o.Addtime >= monthStartTimestamp
&& o.Addtime <= monthEndTimestamp)
.GroupBy(o => o.UserId)
.Select(g => new
{
UserId = g.Key,
Price = g.Sum(o => o.Price)
})
.OrderByDescending(x => x.Price)
.Skip((page - 1) * limit)
.Take(limit)
.ToListAsync();
// 获取用户信息
var userIds = rankData.Select(r => r.UserId).ToList();
var users = await _dbContext.Users
.Where(u => userIds.Contains(u.Id))
.Select(u => new { u.Id, u.Nickname, u.HeadImg, u.Mobile })
.ToDictionaryAsync(u => u.Id);
// 构建响应数据
var list = rankData.Select(item =>
{
var userInfo = users.GetValueOrDefault(item.UserId);
return new ConsumeRecordItemDto
{
UserId = item.UserId,
Nickname = userInfo?.Nickname ?? "",
Headimg = userInfo?.HeadImg ?? "",
Mobile = userInfo?.Mobile ?? "",
Price = item.Price
};
}).ToList();
return new ConsumeRecordResponse { List = list };
}
#region Private Rank Methods
/// <summary>
/// 获取钻石排行榜
/// </summary>
private async Task<List<GenericRankItemDto>> GetDiamondRankAsync(int page, int limit)
{
// 获取测试用户ID列表
var testUserIds = await _dbContext.Users
.Where(u => u.IsTest > 0 && u.Status == 1)
.Select(u => u.Id)
.ToListAsync();
// 查询钻石消费排行
var rankData = await _dbContext.Orders
.Where(o => o.Status == 1
&& o.UseMoney > 0
&& !testUserIds.Contains(o.UserId))
.GroupBy(o => o.UserId)
.Select(g => new
{
UserId = g.Key,
Value = g.Sum(o => o.UseMoney)
})
.OrderByDescending(x => x.Value)
.Skip((page - 1) * limit)
.Take(limit)
.ToListAsync();
return await BuildRankListAsync(rankData, page, limit, "钻石");
}
/// <summary>
/// 获取UU币排行榜
/// </summary>
private async Task<List<GenericRankItemDto>> GetIntegralRankAsync(int page, int limit)
{
// 获取测试用户ID列表
var testUserIds = await _dbContext.Users
.Where(u => u.IsTest > 0 && u.Status == 1)
.Select(u => u.Id)
.ToListAsync();
// 查询积分消费排行
var rankData = await _dbContext.Orders
.Where(o => o.Status == 1
&& o.UseIntegral > 0
&& !testUserIds.Contains(o.UserId))
.GroupBy(o => o.UserId)
.Select(g => new
{
UserId = g.Key,
Value = g.Sum(o => o.UseIntegral)
})
.OrderByDescending(x => x.Value)
.Skip((page - 1) * limit)
.Take(limit)
.ToListAsync();
return await BuildRankListAsync(rankData, page, limit, "UU币");
}
/// <summary>
/// 获取达达卷排行榜
/// </summary>
private async Task<List<GenericRankItemDto>> GetDadajuanRankAsync(int page, int limit)
{
// 达达卷排行榜默认显示更多数据
var actualLimit = limit == 10 ? 200 : limit;
// 获取测试用户ID列表
var testUserIds = await _dbContext.Users
.Where(u => u.IsTest > 0 && u.Status == 1)
.Select(u => u.Id)
.ToListAsync();
// 查询达达卷回收排行
var rankData = await _dbContext.OrderItemsRecoveries
.Where(o => o.Money > 0
&& !testUserIds.Contains(o.UserId))
.GroupBy(o => o.UserId)
.Select(g => new
{
UserId = g.Key,
Value = g.Sum(o => o.Money) * 100 // 转换为达达卷单位
})
.OrderByDescending(x => x.Value)
.Skip((page - 1) * actualLimit)
.Take(actualLimit)
.ToListAsync();
return await BuildRankListAsync(rankData, page, actualLimit, "达达卷");
}
/// <summary>
/// 获取邀请排行榜
/// </summary>
private async Task<List<GenericRankItemDto>> GetInviteRankAsync(int page, int limit)
{
// 邀请排行榜默认显示更多数据
var actualLimit = limit == 10 ? 50 : limit;
// 查询邀请排行
var rankData = await _dbContext.Users
.Where(u => u.Pid > 0 && u.IsTest == 0 && u.Status == 1)
.GroupBy(u => u.Pid)
.Select(g => new
{
UserId = g.Key,
Value = (decimal)g.Count()
})
.Where(x => x.Value > 0)
.OrderByDescending(x => x.Value)
.Skip((page - 1) * actualLimit)
.Take(actualLimit)
.ToListAsync();
return await BuildRankListAsync(rankData, page, actualLimit, "人");
}
/// <summary>
/// 获取亏损排行榜
/// </summary>
private async Task<List<GenericRankItemDto>> GetLossRankAsync(int page, int limit)
{
// 获取测试用户ID列表
var testUserIds = await _dbContext.Users
.Where(u => u.IsTest > 0)
.Select(u => u.Id)
.ToListAsync();
// 查询订单消费数据
var orderData = await _dbContext.Orders
.Where(o => o.Status == 1 && !testUserIds.Contains(o.UserId))
.GroupBy(o => o.UserId)
.Select(g => new
{
UserId = g.Key,
ConsumeMoney = g.Sum(o => o.Price)
})
.ToListAsync();
// 获取所有相关用户ID
var userIds = orderData.Select(o => o.UserId).ToList();
// 查询出货金额
var outputData = await _dbContext.OrderItems
.Where(o => userIds.Contains(o.UserId))
.GroupBy(o => o.UserId)
.Select(g => new
{
UserId = g.Key,
OutputMoney = g.Sum(o => o.GoodslistMoney)
})
.ToDictionaryAsync(x => x.UserId, x => x.OutputMoney);
// 查询达达卷金额
var dadaData = await _dbContext.OrderItemsRecoveries
.Where(o => userIds.Contains(o.UserId))
.GroupBy(o => o.UserId)
.Select(g => new
{
UserId = g.Key,
DadaMoney = g.Sum(o => o.Money)
})
.ToDictionaryAsync(x => x.UserId, x => x.DadaMoney);
// 计算亏损并排序
var lossData = orderData
.Select(o =>
{
var outputMoney = outputData.GetValueOrDefault(o.UserId, 0);
var dadaMoney = dadaData.GetValueOrDefault(o.UserId, 0);
var lossMoney = outputMoney - (o.ConsumeMoney + dadaMoney);
return new { o.UserId, LossMoney = lossMoney };
})
.Where(x => x.LossMoney < 0) // 只显示亏损用户
.OrderBy(x => x.LossMoney) // 亏损越多排名越前
.Skip((page - 1) * limit)
.Take(limit)
.ToList();
// 获取用户信息
var lossUserIds = lossData.Select(x => x.UserId).ToList();
var users = await _dbContext.Users
.Where(u => lossUserIds.Contains(u.Id))
.Select(u => new { u.Id, u.Nickname, u.HeadImg })
.ToDictionaryAsync(u => u.Id);
// 构建排行榜数据
var result = new List<GenericRankItemDto>();
var offset = (page - 1) * limit;
for (int i = 0; i < lossData.Count; i++)
{
var item = lossData[i];
var userInfo = users.GetValueOrDefault(item.UserId);
if (userInfo != null)
{
result.Add(new GenericRankItemDto
{
Rank = offset + i + 1,
UserId = item.UserId,
Nickname = userInfo.Nickname ?? "",
Headimg = userInfo.HeadImg ?? "",
Value = Math.Abs(item.LossMoney), // 显示亏损金额的绝对值
Unit = "元"
});
}
}
return result;
}
/// <summary>
/// 构建排行榜列表
/// </summary>
private async Task<List<GenericRankItemDto>> BuildRankListAsync<T>(
List<T> rankData,
int page,
int limit,
string unit) where T : class
{
// 使用反射获取UserId和Value属性
var userIdProp = typeof(T).GetProperty("UserId");
var valueProp = typeof(T).GetProperty("Value");
if (userIdProp == null || valueProp == null)
{
return new List<GenericRankItemDto>();
}
var userIds = rankData.Select(r => (int)userIdProp.GetValue(r)!).ToList();
var users = await _dbContext.Users
.Where(u => userIds.Contains(u.Id))
.Select(u => new { u.Id, u.Nickname, u.HeadImg })
.ToDictionaryAsync(u => u.Id);
var result = new List<GenericRankItemDto>();
var offset = (page - 1) * limit;
for (int i = 0; i < rankData.Count; i++)
{
var item = rankData[i];
var userId = (int)userIdProp.GetValue(item)!;
var value = Convert.ToDecimal(valueProp.GetValue(item));
var userInfo = users.GetValueOrDefault(userId);
if (userInfo != null)
{
result.Add(new GenericRankItemDto
{
Rank = offset + i + 1,
UserId = userId,
Nickname = userInfo.Nickname ?? "",
Headimg = userInfo.HeadImg ?? "",
Value = value,
Unit = unit
});
}
}
return result;
}
#endregion
}