using Microsoft.Extensions.Caching.Distributed; using Volo.Abp.Caching; using Volo.Abp.Domain.Services; using Volo.Abp.Settings; using Volo.Abp.Threading; using Yi.Framework.DigitalCollectibles.Domain.Dtos; using Yi.Framework.DigitalCollectibles.Domain.Entities; using Yi.Framework.DigitalCollectibles.Domain.Shared.Consts; using Yi.Framework.DigitalCollectibles.Domain.Shared.Enums; using Yi.Framework.SettingManagement.Domain; using Yi.Framework.SqlSugarCore.Abstractions; namespace Yi.Framework.DigitalCollectibles.Domain.Managers; /// /// 矿池领域服务 /// 处理矿池相关业务,例如挖矿等 /// public class MiningPoolManager : DomainService { private readonly ISqlSugarRepository _collectiblesRepository; private readonly ISqlSugarRepository _onHookRepository; private readonly ISettingProvider _settingProvider; private readonly IDistributedCache _cache; private readonly ISqlSugarRepository _userStoreRepository; public MiningPoolManager(ISettingProvider settingProvider, IDistributedCache cache, ISqlSugarRepository collectiblesRepository, ISqlSugarRepository onHookRepository, ISqlSugarRepository userStoreRepository) { _settingProvider = settingProvider; this._cache = cache; _collectiblesRepository = collectiblesRepository; _onHookRepository = onHookRepository; _userStoreRepository = userStoreRepository; } /// /// 每次挖矿概率,每天根据特定算法计算 /// private static decimal CurrentMiningProbability => AsyncHelper.RunSync(async () => { return await ComputeMiningProbabilityAsync(); }); public async Task GetMiningPoolContentAsync() { var pool = await _cache.GetAsync(CacheConst.MiningPoolContent); return pool; } /// /// 用户进行一次挖矿 /// /// public async Task MiningAsync(Guid userId) { var result = new MiningPoolResult(); //从矿池中开挖,判断矿池是否还有矿 //根据挖矿概率,进行挖掘 //挖到了放到用户仓库即可 var miningMaxLimit = int.Parse(await _settingProvider.GetOrNullAsync("MiningMaxLimit")); var miningMinIntervalSeconds = int.Parse(await _settingProvider.GetOrNullAsync("MiningMinIntervalSeconds")); //如果概率是挖到了矿,再从矿物中随机选择一个稀有度,再在当前稀有度中的矿物列表,随机选择一个具体的矿物 var pool =await GetMiningPoolContentAsync(); if (pool.TotalNumber == 0) { result.Result = MiningResultEnum.PoolIsEmpty; return result; } // 生成一个 0 到 1 之间的随机数 double randomValue = new Random().NextDouble(); //如果随机的概率在当前概率内,成功 if (randomValue.To() < CurrentMiningProbability) { //成功后在藏品中根据稀有度概率必定获取一个 var probabilityArray = RarityEnumExtensions.GetProbabilityArray(); var index = GetRandomIndex(probabilityArray); var rarityType = (RarityEnum)Enum.GetValues(typeof(RarityEnum)).GetValue(index)!; var collectiblesList = await _collectiblesRepository._DbQueryable.Where(x => x.Rarity == rarityType).ToListAsync(); int randomIndex = new Random().Next(collectiblesList.Count); var currentCollectibles = collectiblesList[randomIndex]; result.Result = MiningResultEnum.Success; result.Collectibles = currentCollectibles; //使用结果新增给对应的用户 await _userStoreRepository.InsertAsync(new CollectiblesUserStoreAggregateRoot { UserId = userId, CollectiblesId = result.Collectibles.Id, IsRead = false }); return result; } result.Result = MiningResultEnum.Empty; return result; } /// /// 挂机挖矿 /// public async Task OnHookMiningAsync() { //获取当前激活的挂机挖矿 var currentOnHook = await _onHookRepository._DbQueryable.Where(x => x.IsActive == true) .Where(x => x.EndTime <= DateTime.Now).ToListAsync(); //根据用户对挂机卡hash关系 var userOnHookDic = currentOnHook.GroupBy(x => x.UserId).ToDictionary(x => x.Key, y => y.First()); foreach (var onHookItem in userOnHookDic) { await MiningAsync(onHookItem.Value.UserId); } } private int GetRandomIndex(decimal[] probabilities) { // 生成0到1之间的随机数 Random random = new Random(); decimal randomValue = random.NextDouble().To(); decimal cumulativeProbability = 0.0m; for (int i = 0; i < probabilities.Length; i++) { cumulativeProbability += probabilities[i]; // 判断随机数是否小于当前的累积概率 if (randomValue < cumulativeProbability) { return i; // 返回中标的索引 } } //剩余情况都是普通 return 0; } /// /// 计算当前挖矿概率 /// /// public static Task ComputeMiningProbabilityAsync() { //当前的挖矿期望:当天的所有藏品能被刚好挖完 //保底概率:最大不能高过一个值,百分之10 //手动挖矿:1天可挖10次,每次至少间隔6秒 //自动挖矿:1天可以挖24次 每次间隔60分钟(需要使用自动挖矿卡) //可影响因素:剩余手动挖矿次数+剩余自动挖矿次数 //简单模式,1/15 return Task.FromResult(1m / 15); } /// /// 刷新矿池 /// /// public async Task RefreshMiningPoolAsync() { //获取当前最大的限制 var maximumPoolLimit = int.Parse(await _settingProvider.GetOrNullAsync("MaxPoolLimit")); DateTime startTime = DateTime.Today.AddHours(10); DateTime endTime = startTime.AddDays(1); var probabilityValues = RarityEnumExtensions.GetProbabilityArray(); var result = GenerateDistribution(maximumPoolLimit, probabilityValues); //根据配置,将不同比例的矿,塞入矿池, //矿池,交给redis await _cache.SetAsync(CacheConst.MiningPoolContent, new MiningPoolContent(startTime, endTime) { I0_OrdinaryNumber = result[0], I1_SeniorNumber = result[1], I2_RareNumber = result[2], I3_GemNumber = result[3], I4_LegendNumber = result[4] }, new DistributedCacheEntryOptions { AbsoluteExpiration = endTime }); } /// /// 根据概率生成给对应稀有度藏品 /// /// /// /// /// private int[] GenerateDistribution(int totalCount, decimal[] probabilities) { int[] counts = new int[probabilities.Length]; decimal totalProbability = 0; // 计算概率总和,确保为 1 foreach (var prob in probabilities) { totalProbability += prob; } // 检查概率之和是否为 1 if (totalProbability < 0.99m || totalProbability > 1.01m) { throw new ArgumentException("概率总和必须接近1"); } // 生成分布 for (int i = 0; i < probabilities.Length; i++) { counts[i] = (int)(totalCount * probabilities[i]); } // 处理可能因精度问题导致的总数不足 int sum = 0; foreach (var count in counts) { sum += count; } int difference = totalCount - sum; // 将差值分配给概率最大的一项 if (difference > 0) { int maxIndex = Array.IndexOf(counts, Math.Max(counts[0], counts[1])); counts[maxIndex] += difference; } return counts; } }