using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using MongoRepository; using MongoDB.Driver; using CommonDomain; using UserDomain.Entity; namespace CommonService { public class LotteryService : ILotteryService { private readonly IMongoBaseRepository _recordRepo; private readonly IMongoBaseRepository _userStatusRepo; private readonly IUserService _userService; private readonly ICapPublisher _capPublisher; public LotteryService( IMongoBaseRepository activityRepo, IMongoBaseRepository recordRepo, IMongoBaseRepository userStatusRepo, IUserService userService, ICapPublisher capPublisher) { _activityRepo = activityRepo; _recordRepo = recordRepo; _userStatusRepo = userStatusRepo; _userService = userService; _capPublisher = capPublisher; } public async Task GetUserStatus(string userId) { var status = await _userStatusRepo.FindOneAsync(s => s.UserId == userId && s.ActivityId == activityId); if (status == null) { status = new LotteryUserStatus { UserId = userId }; await _userStatusRepo.InsertOneAsync(status); } return status; } public async Task<(bool success, string message, LotteryPrize? prize, long cost)> Draw(string userId) { var activity = await GetActivity(activityId); if (activity == null) return (false, "活动不存在或未开启", null, 0); var now = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); if (now < activity.StartTime) return (false, "活动未开始", null, 0); if (now > activity.EndTime) return (false, "活动已结束", null, 0); var userStatus = await GetUserStatus(userId, activityId); if (userStatus.IsFinished) return (false, "您已抽完所有奖项,活动结束", null, 0); if (activity.UserMaxDraw > 0 && userStatus.TotalDrawCount >= activity.UserMaxDraw) return (false, "已达最大抽奖次数", null, 0); var costIndex = Math.Min(userStatus.TotalDrawCount, activity.Costs.Count - 1); var cost = activity.Costs[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(activity.Pools); if (level == null) return (false, "奖池已空", null, 0); var prize = DrawPrize(level, userStatus.DrawnPrizeIds); if (prize == null) { prize = DrawPrizeFromAll(activity.Pools, userStatus.DrawnPrizeIds); if (prize == null) return (false, "所有奖项已抽完", null, 0); level = activity.Pools.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 { UserId = userId, ActivityId = activityId, PrizeId = prize.PrizeId, Level = level.Level, Cost = cost, DrawTime = now }; await _recordRepo.InsertOneAsync(record); userStatus.DrawnPrizeIds.Add(prize.PrizeId); userStatus.TotalDrawCount++; 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++; await _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 pools) { var availablePools = pools.Where(p => p.PrizeList.Any(x => !x.TotalCount.Equals(0) && x.DrawnCount < x.TotalCount)).ToList(); if (!availablePools.Any()) return null; long totalWeight = availablePools.Sum(p => (long)p.Weight); if (totalWeight <= 0) return null; long randomValue = 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 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; int randomValue = 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 pools, List 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> GetUserDrawnPrizes(string userId, string activityId) { var status = await GetUserStatus(userId, activityId); return status?.DrawnPrizeIds ?? new List(); } } }