添加项目文件。
This commit is contained in:
@@ -0,0 +1,253 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using MongoRepository;
|
||||
using MongoDB.Driver;
|
||||
using CommonDomain;
|
||||
using UserDomain.Entity;
|
||||
using UserService.User;
|
||||
using DotNetCore.CAP;
|
||||
using UserService.GameConfigs;
|
||||
|
||||
namespace UserService
|
||||
{
|
||||
public class LotteryService : ILotteryService
|
||||
{
|
||||
|
||||
|
||||
private readonly IMongoBaseRepository<LotteryRecord> _recordRepo;
|
||||
|
||||
private readonly IMongoBaseRepository<LotteryUserStatus> _userStatusRepo;
|
||||
|
||||
private readonly IUserService _userService;
|
||||
|
||||
private readonly ICapPublisher _capPublisher;
|
||||
private readonly IActivityConfigService _activityConfig;
|
||||
|
||||
public LotteryService(
|
||||
IMongoBaseRepository<LotteryRecord> recordRepo,
|
||||
IMongoBaseRepository<LotteryUserStatus> userStatusRepo,
|
||||
IUserService userService,
|
||||
ICapPublisher capPublisher, IActivityConfigService activityConfig)
|
||||
{
|
||||
|
||||
_recordRepo = recordRepo;
|
||||
_userStatusRepo = userStatusRepo;
|
||||
_userService = userService;
|
||||
_capPublisher = capPublisher;
|
||||
_activityConfig = activityConfig;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
public async Task<LotteryUserStatus?> GetUserStatus(string userId)
|
||||
{
|
||||
var activityConfig = await _activityConfig.Get();
|
||||
var activity = activityConfig?.LotteryActivityConfig;
|
||||
var cycleKey = BuildCycleKey(activity);
|
||||
var status = await _userStatusRepo.FindOneAsync(s => s.UserId == userId );
|
||||
if (status == null)
|
||||
{
|
||||
status = new LotteryUserStatus
|
||||
{
|
||||
UserId = userId,
|
||||
ActivityCycleKey = cycleKey,
|
||||
ActivityStartTime = activity?.StartTime ?? 0,
|
||||
ActivityEndTime = activity?.EndTime ?? 0
|
||||
};
|
||||
await _userStatusRepo.InsertOneAsync(status);
|
||||
}
|
||||
else if (string.IsNullOrWhiteSpace(status.ActivityCycleKey))
|
||||
{
|
||||
status.ActivityCycleKey = cycleKey;
|
||||
status.ActivityStartTime = activity?.StartTime ?? 0;
|
||||
status.ActivityEndTime = activity?.EndTime ?? 0;
|
||||
await _userStatusRepo.ReplaceOneAsync(status);
|
||||
}
|
||||
else if (status.ActivityCycleKey != cycleKey)
|
||||
{
|
||||
status.ActivityCycleKey = cycleKey;
|
||||
status.ActivityStartTime = activity?.StartTime ?? 0;
|
||||
status.ActivityEndTime = activity?.EndTime ?? 0;
|
||||
status.DrawnPrizeIds = new List<string>();
|
||||
status.TotalDrawCount = 0;
|
||||
status.DrawCountByPool = new Dictionary<int, int>();
|
||||
status.IsFinished = false;
|
||||
await _userStatusRepo.ReplaceOneAsync(status);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
public async Task<(bool success, string message, LotteryPrize? prize, long cost)> Draw(string userId, int poolType = 0)
|
||||
{
|
||||
if (poolType < 0 || poolType > 3)
|
||||
return (false, "奖池类型错误", null, 0);
|
||||
|
||||
var activityConfig = await _activityConfig.Get();
|
||||
if (activityConfig?.LotteryActivityConfig == null)
|
||||
return (false, "活动不存在或未开启", null, 0);
|
||||
var activity = activityConfig.LotteryActivityConfig;
|
||||
|
||||
var now = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
||||
if (activity.EndTime > 0 && now >= activity.EndTime)
|
||||
return (false, "活动已结束", null, 0);
|
||||
if (!activity.IsOpen)
|
||||
return (false, "活动不存在或未开启", null, 0);
|
||||
if (activity.StartTime > 0 && now < activity.StartTime)
|
||||
return (false, "活动未开始", null, 0);
|
||||
|
||||
var effectivePoolType = poolType == 0 ? 1 : poolType;
|
||||
var selectedPools = activity.Pools.Where(p => p.PoolType == effectivePoolType).ToList();
|
||||
if (selectedPools.Count == 0)
|
||||
return (false, "未配置对应奖池", null, 0);
|
||||
|
||||
var userStatus = await GetUserStatus(userId );
|
||||
if (userStatus.IsFinished)
|
||||
return (false, "您已抽完所有奖项,活动结束", null, 0);
|
||||
|
||||
if (activity.UserMaxDraw > 0 && userStatus.TotalDrawCount >= activity.UserMaxDraw)
|
||||
return (false, "已达最大抽奖次数", null, 0);
|
||||
|
||||
if (activity.CostsByPool?.TryGetValue(effectivePoolType, out var configuredCosts) != true
|
||||
|| configuredCosts == null || configuredCosts.Count == 0)
|
||||
return (false, "未配置当前奖池抽奖消耗", null, 0);
|
||||
userStatus.DrawCountByPool ??= new Dictionary<int, int>();
|
||||
var poolDrawCount = userStatus.DrawCountByPool.GetValueOrDefault(effectivePoolType);
|
||||
var costIndex = Math.Min(poolDrawCount, configuredCosts.Count - 1);
|
||||
var cost = configuredCosts[costIndex];
|
||||
|
||||
var user = await _userService.GetUser(userId);
|
||||
if (user == null)
|
||||
return (false, "用户不存在", null, 0);
|
||||
if (user.Assets.Gold < cost)
|
||||
return (false, "金币不足", null, 0);
|
||||
|
||||
var level = DrawLevel(selectedPools);
|
||||
if (level == null)
|
||||
return (false, "奖池已空", null, 0);
|
||||
|
||||
var prize = DrawPrize(level, userStatus.DrawnPrizeIds);
|
||||
if (prize == null)
|
||||
{
|
||||
prize = DrawPrizeFromAll(selectedPools, userStatus.DrawnPrizeIds);
|
||||
if (prize == null)
|
||||
return (false, "所有奖项已抽完", null, 0);
|
||||
level = selectedPools.FirstOrDefault(p => p.PrizeList.Any(x => x.PrizeId == prize.PrizeId));
|
||||
}
|
||||
|
||||
if (prize.TotalCount > 0 && prize.DrawnCount >= prize.TotalCount)
|
||||
return (false, "该奖项已抽完", null, 0);
|
||||
|
||||
user.Assets.Gold -= cost;
|
||||
await _userService.UpdateUser(user);
|
||||
|
||||
var record = new LotteryRecord
|
||||
{
|
||||
ActivityCycleKey = BuildCycleKey(activity),
|
||||
UserId = userId,
|
||||
PrizeId = prize.PrizeId,
|
||||
Level = level.Level,
|
||||
Cost = cost,
|
||||
DrawTime = now
|
||||
};
|
||||
await _recordRepo.InsertOneAsync(record);
|
||||
|
||||
userStatus.DrawnPrizeIds.Add(prize.PrizeId);
|
||||
userStatus.TotalDrawCount++;
|
||||
userStatus.DrawCountByPool[effectivePoolType] = poolDrawCount + 1;
|
||||
|
||||
var allPrizeIds = activity.Pools.SelectMany(p => p.PrizeList).Select(x => x.PrizeId).ToList();
|
||||
if (allPrizeIds.All(id => userStatus.DrawnPrizeIds.Contains(id)))
|
||||
{
|
||||
userStatus.IsFinished = true;
|
||||
}
|
||||
await _userStatusRepo.ReplaceOneAsync(userStatus);
|
||||
|
||||
prize.DrawnCount++;
|
||||
|
||||
_capPublisher.Publish("Lottery.DrawPrize", new LotteryDrawMessage
|
||||
{
|
||||
UserId = userId,
|
||||
PrizeId = prize.PrizeId,
|
||||
PrizeName = prize.Name,
|
||||
Type = prize.Type,
|
||||
Count = prize.Count,
|
||||
OtherId = prize.OtherId
|
||||
});
|
||||
|
||||
return (true, "抽奖成功", prize, cost);
|
||||
}
|
||||
|
||||
private LotteryPoolLevel? DrawLevel(List<LotteryPoolLevel> pools)
|
||||
{
|
||||
var availablePools = pools.Where(p =>
|
||||
p.PrizeList.Any(x => x.TotalCount == 0 || x.DrawnCount < x.TotalCount)).ToList();
|
||||
if (!availablePools.Any())
|
||||
return null;
|
||||
|
||||
long totalWeight = availablePools.Sum(p => (long)p.Weight);
|
||||
if (totalWeight <= 0)
|
||||
return null;
|
||||
var random = new Random();
|
||||
long randomValue = random.NextInt64(0, totalWeight);
|
||||
long currentWeightSum = 0;
|
||||
|
||||
foreach (var pool in availablePools)
|
||||
{
|
||||
currentWeightSum += pool.Weight;
|
||||
if (randomValue < currentWeightSum)
|
||||
return pool;
|
||||
}
|
||||
return availablePools.LastOrDefault();
|
||||
}
|
||||
|
||||
private LotteryPrize? DrawPrize(LotteryPoolLevel level, List<string> drawnPrizeIds)
|
||||
{
|
||||
var availablePrizes = level.PrizeList
|
||||
.Where(p => !drawnPrizeIds.Contains(p.PrizeId) &&
|
||||
(p.TotalCount == 0 || p.DrawnCount < p.TotalCount))
|
||||
.ToList();
|
||||
if (!availablePrizes.Any())
|
||||
return null;
|
||||
|
||||
int totalWeight = availablePrizes.Sum(p => p.Weight);
|
||||
if (totalWeight <= 0)
|
||||
return null;
|
||||
var random = new Random();
|
||||
int randomValue = random.Next(0, totalWeight);
|
||||
int currentWeightSum = 0;
|
||||
|
||||
foreach (var prize in availablePrizes)
|
||||
{
|
||||
currentWeightSum += prize.Weight;
|
||||
if (randomValue < currentWeightSum)
|
||||
return prize;
|
||||
}
|
||||
return availablePrizes.LastOrDefault();
|
||||
}
|
||||
|
||||
private LotteryPrize? DrawPrizeFromAll(List<LotteryPoolLevel> pools, List<string> drawnPrizeIds)
|
||||
{
|
||||
foreach (var pool in pools.OrderByDescending(p => p.Weight))
|
||||
{
|
||||
var prize = DrawPrize(pool, drawnPrizeIds);
|
||||
if (prize != null)
|
||||
return prize;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public async Task<List<string>> GetUserDrawnPrizes(string userId)
|
||||
{
|
||||
var status = await GetUserStatus(userId);
|
||||
return status?.DrawnPrizeIds ?? new List<string>();
|
||||
}
|
||||
|
||||
private static string BuildCycleKey(LotteryActivityConfig? activity)
|
||||
{
|
||||
return activity == null ? "0:0" : $"{activity.StartTime}:{activity.EndTime}";
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user