Files
DysonNetwork/DysonNetwork.Pass/Lotteries/LotteryService.cs
2026-02-03 02:18:02 +08:00

193 lines
6.3 KiB
C#

using DysonNetwork.Shared.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using NodaTime;
namespace DysonNetwork.Pass.Lotteries;
public class LotteryOrderMetaData
{
public Guid AccountId { get; set; }
public List<int> RegionOneNumbers { get; set; } = new();
public int RegionTwoNumber { get; set; }
public int Multiplier { get; set; } = 1;
}
public class LotteryService(
AppDatabase db,
ILogger<LotteryService> logger)
{
private static bool ValidateNumbers(List<int> region1, int region2)
{
if (region1.Count != 5 || region1.Distinct().Count() != 5)
return false;
if (region1.Any(n => n < 0 || n > 99))
return false;
if (region2 < 0 || region2 > 99)
return false;
return true;
}
public async Task<SnLottery> CreateTicketAsync(Guid accountId, List<int> region1, int region2, int multiplier = 1)
{
if (!ValidateNumbers(region1, region2))
throw new ArgumentException("Invalid lottery numbers");
var lottery = new SnLottery
{
AccountId = accountId,
RegionOneNumbers = region1,
RegionTwoNumber = region2,
Multiplier = multiplier
};
db.Lotteries.Add(lottery);
await db.SaveChangesAsync();
return lottery;
}
public async Task<List<SnLottery>> GetUserTicketsAsync(Guid accountId, int offset = 0, int limit = 20)
{
return await db.Lotteries
.Where(l => l.AccountId == accountId)
.OrderByDescending(l => l.CreatedAt)
.Skip(offset)
.Take(limit)
.ToListAsync();
}
public async Task<SnLottery?> GetTicketAsync(Guid id)
{
return await db.Lotteries.FirstOrDefaultAsync(l => l.Id == id);
}
public async Task<int> GetUserTicketCountAsync(Guid accountId)
{
return await db.Lotteries.CountAsync(l => l.AccountId == accountId);
}
private static decimal CalculateLotteryPrice(int multiplier)
{
return 10 + (multiplier - 1) * 10;
}
private static int CalculateReward(int region1Matches, bool region2Match)
{
var reward = region1Matches switch
{
0 => 0,
1 => 10,
2 => 100,
3 => 500,
4 => 1000,
5 => 10000,
_ => 0
};
if (region2Match) reward *= 10;
return reward;
}
private static List<int> GenerateUniqueRandomNumbers(int count, int min, int max)
{
var numbers = new List<int>();
var random = new Random();
while (numbers.Count < count)
{
var num = random.Next(min, max + 1);
if (!numbers.Contains(num)) numbers.Add(num);
}
return numbers.OrderBy(n => n).ToList();
}
private int CountMatches(List<int> playerNumbers, List<int> winningNumbers)
{
return playerNumbers.Intersect(winningNumbers).Count();
}
public async Task DrawLotteries()
{
try
{
logger.LogInformation("Starting drawing lotteries...");
var now = SystemClock.Instance.GetCurrentInstant();
// All pending lottery tickets
var tickets = await db.Lotteries
.Where(l => l.DrawStatus == LotteryDrawStatus.Pending)
.ToListAsync();
if (tickets.Count == 0)
{
logger.LogInformation("No pending lottery tickets");
return;
}
logger.LogInformation("Found {Count} pending lottery tickets for draw", tickets.Count);
// Generate winning numbers
var winningRegion1 = GenerateUniqueRandomNumbers(5, 0, 99);
var winningRegion2 = GenerateUniqueRandomNumbers(1, 0, 99)[0];
logger.LogInformation("Winning numbers generated: Region1 [{Region1}], Region2 [{Region2}]",
string.Join(",", winningRegion1), winningRegion2);
var drawDate = Instant.FromDateTimeUtc(new DateTime(DateTime.UtcNow.Year, DateTime.UtcNow.Month,
DateTime.UtcNow.Day, 0, 0, 0, DateTimeKind.Utc).AddDays(-1)); // Yesterday's date
var totalPrizesAwarded = 0;
long totalPrizeAmount = 0;
// Process each ticket
foreach (var ticket in tickets)
{
var region1Matches = CountMatches(ticket.RegionOneNumbers, winningRegion1);
var region2Match = ticket.RegionTwoNumber == winningRegion2;
var reward = CalculateReward(region1Matches, region2Match);
// Record match results
ticket.MatchedRegionOneNumbers = ticket.RegionOneNumbers.Intersect(winningRegion1).ToList();
ticket.MatchedRegionTwoNumber = region2Match ? (int?)winningRegion2 : null;
if (reward > 0)
{
// Note: Prize awarding is now handled by the Wallet service
// The Wallet service will process lottery results and award prizes
logger.LogInformation(
"Lottery prize of {Amount} to account {AccountId} for {Matches} matches needs to be awarded via Wallet service",
reward, ticket.AccountId, region1Matches);
totalPrizesAwarded++;
totalPrizeAmount += reward;
}
ticket.DrawStatus = LotteryDrawStatus.Drawn;
ticket.DrawDate = drawDate;
}
// Save the draw record
var lotteryRecord = new SnLotteryRecord
{
DrawDate = drawDate,
WinningRegionOneNumbers = winningRegion1,
WinningRegionTwoNumber = winningRegion2,
TotalTickets = tickets.Count,
TotalPrizesAwarded = totalPrizesAwarded,
TotalPrizeAmount = totalPrizeAmount
};
db.LotteryRecords.Add(lotteryRecord);
await db.SaveChangesAsync();
logger.LogInformation("Daily lottery draw completed: {Prizes} prizes awarded, total amount {Amount}",
totalPrizesAwarded, totalPrizeAmount);
}
catch (Exception ex)
{
logger.LogError(ex, "An error occurred during the daily lottery draw");
throw;
}
}
}