From ef20ef701446e96909f44ff16b1f7ffa71cf3071 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A9=99=E5=AD=90?= <454313500@qq.com> Date: Wed, 16 Oct 2024 22:47:04 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=8C=E6=88=90=E6=90=AD=E5=BB=BA?= =?UTF-8?q?=E5=9F=BA=E7=A1=80=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Jobs/AutoRefreshMiningPoolJob.cs | 3 + .../{CacheConst.cs => MiningCacheConst.cs} | 6 +- .../DigitalCollectiblesSettingProvider.cs | 9 +- .../Dtos/MiningPoolResult.cs | 2 - .../Dtos/UserMiningLimitCacheDto.cs | 14 +++ .../Managers/MiningPoolManager.cs | 104 ++++++++++++++---- ...ramework.DigitalCollectibles.Domain.csproj | 1 + 7 files changed, 112 insertions(+), 27 deletions(-) rename Yi.Abp.Net8/module/digital-collectibles/Yi.Framework.DigitalCollectibles.Domain.Shared/Consts/{CacheConst.cs => MiningCacheConst.cs} (51%) create mode 100644 Yi.Abp.Net8/module/digital-collectibles/Yi.Framework.DigitalCollectibles.Domain/Dtos/UserMiningLimitCacheDto.cs diff --git a/Yi.Abp.Net8/module/digital-collectibles/Yi.Framework.DigitalCollectibles.Application/Jobs/AutoRefreshMiningPoolJob.cs b/Yi.Abp.Net8/module/digital-collectibles/Yi.Framework.DigitalCollectibles.Application/Jobs/AutoRefreshMiningPoolJob.cs index 1563a531..fe349a8f 100644 --- a/Yi.Abp.Net8/module/digital-collectibles/Yi.Framework.DigitalCollectibles.Application/Jobs/AutoRefreshMiningPoolJob.cs +++ b/Yi.Abp.Net8/module/digital-collectibles/Yi.Framework.DigitalCollectibles.Application/Jobs/AutoRefreshMiningPoolJob.cs @@ -24,6 +24,9 @@ public class AutoRefreshMiningPoolJob : QuartzBackgroundWorkerBase public override async Task Execute(IJobExecutionContext context) { + //刷新矿池 await _miningPoolManager.RefreshMiningPoolAsync(); + //刷新用户限制 + await _miningPoolManager.RefreshMiningUserLimitAsync(); } } \ No newline at end of file diff --git a/Yi.Abp.Net8/module/digital-collectibles/Yi.Framework.DigitalCollectibles.Domain.Shared/Consts/CacheConst.cs b/Yi.Abp.Net8/module/digital-collectibles/Yi.Framework.DigitalCollectibles.Domain.Shared/Consts/MiningCacheConst.cs similarity index 51% rename from Yi.Abp.Net8/module/digital-collectibles/Yi.Framework.DigitalCollectibles.Domain.Shared/Consts/CacheConst.cs rename to Yi.Abp.Net8/module/digital-collectibles/Yi.Framework.DigitalCollectibles.Domain.Shared/Consts/MiningCacheConst.cs index 015111e3..f1c633ef 100644 --- a/Yi.Abp.Net8/module/digital-collectibles/Yi.Framework.DigitalCollectibles.Domain.Shared/Consts/CacheConst.cs +++ b/Yi.Abp.Net8/module/digital-collectibles/Yi.Framework.DigitalCollectibles.Domain.Shared/Consts/MiningCacheConst.cs @@ -1,9 +1,13 @@ namespace Yi.Framework.DigitalCollectibles.Domain.Shared.Consts; -public class CacheConst +public class MiningCacheConst { /// /// 矿池 /// public const string MiningPoolContent = "MiningPool"; + + public const string UserMiningLimit = "UserMiningLimit"; + + public const string UserMiningLimitLock = "UserMiningLimitLock"; } \ No newline at end of file diff --git a/Yi.Abp.Net8/module/digital-collectibles/Yi.Framework.DigitalCollectibles.Domain.Shared/Settings/DigitalCollectiblesSettingProvider.cs b/Yi.Abp.Net8/module/digital-collectibles/Yi.Framework.DigitalCollectibles.Domain.Shared/Settings/DigitalCollectiblesSettingProvider.cs index 29491c38..46ca1a5b 100644 --- a/Yi.Abp.Net8/module/digital-collectibles/Yi.Framework.DigitalCollectibles.Domain.Shared/Settings/DigitalCollectiblesSettingProvider.cs +++ b/Yi.Abp.Net8/module/digital-collectibles/Yi.Framework.DigitalCollectibles.Domain.Shared/Settings/DigitalCollectiblesSettingProvider.cs @@ -18,11 +18,14 @@ namespace Yi.Abp.Domain.Shared.Settings //每日矿池最大上限 new SettingDefinition("MaxPoolLimit", "100"), - //每日手动挖矿最大上限 - new SettingDefinition("MiningMaxLimit", "10"), + //每日挖矿最大上限 + new SettingDefinition("MiningMaxLimit", "36"), //每次挖矿最小间隔(秒) - new SettingDefinition("MiningMinIntervalSeconds", "5") + new SettingDefinition("MiningMinIntervalSeconds", "5"), + + //每次挖到矿的概率 + new SettingDefinition("MiningMinProbability", "0.06") ); } } diff --git a/Yi.Abp.Net8/module/digital-collectibles/Yi.Framework.DigitalCollectibles.Domain/Dtos/MiningPoolResult.cs b/Yi.Abp.Net8/module/digital-collectibles/Yi.Framework.DigitalCollectibles.Domain/Dtos/MiningPoolResult.cs index 27be92c5..833f81a9 100644 --- a/Yi.Abp.Net8/module/digital-collectibles/Yi.Framework.DigitalCollectibles.Domain/Dtos/MiningPoolResult.cs +++ b/Yi.Abp.Net8/module/digital-collectibles/Yi.Framework.DigitalCollectibles.Domain/Dtos/MiningPoolResult.cs @@ -5,7 +5,5 @@ namespace Yi.Framework.DigitalCollectibles.Domain.Dtos; public class MiningPoolResult { - public MiningResultEnum Result { get; set; } - public CollectiblesAggregateRoot? Collectibles { get; set; } } \ No newline at end of file diff --git a/Yi.Abp.Net8/module/digital-collectibles/Yi.Framework.DigitalCollectibles.Domain/Dtos/UserMiningLimitCacheDto.cs b/Yi.Abp.Net8/module/digital-collectibles/Yi.Framework.DigitalCollectibles.Domain/Dtos/UserMiningLimitCacheDto.cs new file mode 100644 index 00000000..7f000a55 --- /dev/null +++ b/Yi.Abp.Net8/module/digital-collectibles/Yi.Framework.DigitalCollectibles.Domain/Dtos/UserMiningLimitCacheDto.cs @@ -0,0 +1,14 @@ +namespace Yi.Framework.DigitalCollectibles.Domain.Dtos; + +public class UserMiningLimitCacheDto +{ + /// + /// 当前已挖矿次数 + /// + public int Number { get; set; } + + /// + /// 上次挖矿时间 + /// + public DateTime? LastMiningTime{ get; set; } +} \ No newline at end of file diff --git a/Yi.Abp.Net8/module/digital-collectibles/Yi.Framework.DigitalCollectibles.Domain/Managers/MiningPoolManager.cs b/Yi.Abp.Net8/module/digital-collectibles/Yi.Framework.DigitalCollectibles.Domain/Managers/MiningPoolManager.cs index 68790cb8..07b6ac08 100644 --- a/Yi.Abp.Net8/module/digital-collectibles/Yi.Framework.DigitalCollectibles.Domain/Managers/MiningPoolManager.cs +++ b/Yi.Abp.Net8/module/digital-collectibles/Yi.Framework.DigitalCollectibles.Domain/Managers/MiningPoolManager.cs @@ -1,4 +1,5 @@ -using Microsoft.Extensions.Caching.Distributed; +using FreeRedis; +using Microsoft.Extensions.Caching.Distributed; using Volo.Abp.Caching; using Volo.Abp.Domain.Services; using Volo.Abp.Settings; @@ -21,57 +22,110 @@ public class MiningPoolManager : DomainService private readonly ISqlSugarRepository _collectiblesRepository; private readonly ISqlSugarRepository _onHookRepository; private readonly ISettingProvider _settingProvider; - private readonly IDistributedCache _cache; + private readonly IDistributedCache _miningPoolCache; + private readonly IDistributedCache _userMiningLimitCache; private readonly ISqlSugarRepository _userStoreRepository; + private IRedisClient RedisClient => LazyServiceProvider.LazyGetRequiredService(); - public MiningPoolManager(ISettingProvider settingProvider, IDistributedCache cache, + public MiningPoolManager(ISettingProvider settingProvider, IDistributedCache miningPoolCache, ISqlSugarRepository collectiblesRepository, ISqlSugarRepository onHookRepository, - ISqlSugarRepository userStoreRepository) + ISqlSugarRepository userStoreRepository, + IDistributedCache userMiningLimitCache) { _settingProvider = settingProvider; - this._cache = cache; + this._miningPoolCache = miningPoolCache; _collectiblesRepository = collectiblesRepository; _onHookRepository = onHookRepository; _userStoreRepository = userStoreRepository; + _userMiningLimitCache = userMiningLimitCache; } /// /// 每次挖矿概率,每天根据特定算法计算 /// - private static decimal CurrentMiningProbability => AsyncHelper.RunSync(async () => + private decimal CurrentMiningProbability => AsyncHelper.RunSync(async () => { return await ComputeMiningProbabilityAsync(); }); public async Task GetMiningPoolContentAsync() { - var pool = await _cache.GetAsync(CacheConst.MiningPoolContent); + var pool = await _miningPoolCache.GetAsync(MiningCacheConst.MiningPoolContent); return pool; } + + /// + /// 校验挖矿限制 + /// + /// + private async Task VerifyMiningLimitAsync(Guid userId) + { + //每天最大次数限制 + var miningMaxLimit = int.Parse(await _settingProvider.GetOrNullAsync("MiningMaxLimit")); + //每次间隔 + var miningMinIntervalSeconds = int.Parse(await _settingProvider.GetOrNullAsync("MiningMinIntervalSeconds")); + + var currentNumber = 1; + + //根据用户进行上锁 + if (await RedisClient.SetNxAsync($"UserMiningLimitLock:{userId}", true, + TimeSpan.FromSeconds(miningMinIntervalSeconds))) + { + var userLimit = await _userMiningLimitCache.GetAsync($"{MiningCacheConst.UserMiningLimit}:{userId}"); + + if (userLimit is not null) + { + //符合条件,成功挖矿 + if (userLimit.Number < miningMaxLimit) + { + currentNumber = userLimit.Number + 1; + } + else + { + throw new UserFriendlyException($"失败,你已达当轮矿池最大限制,给其他人留点吧"); + } + } + + //没有缓存过,必定成功,直接走 + await _userMiningLimitCache.SetAsync($"{MiningCacheConst.UserMiningLimit}:{userId}", + new UserMiningLimitCacheDto + { + Number = currentNumber, + LastMiningTime = DateTime.Now, + }, + new DistributedCacheEntryOptions() + { + //虽然新增的是一天,但是每次刷新是早上10点,矿池刷新时,还需要清除限制 + AbsoluteExpirationRelativeToNow = TimeSpan.FromDays(1) + }); + } + + //已上过锁,并且没有到限制时间,必定失败 + //不符合条件,直接走 + throw new UserFriendlyException($"失败,你都挖的冒烟了,请稍后再挖"); + } + /// /// 用户进行一次挖矿 /// /// public async Task MiningAsync(Guid userId) { - + //检验限制 + await VerifyMiningLimitAsync(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(); + var pool = await GetMiningPoolContentAsync(); if (pool.TotalNumber == 0) { - result.Result = MiningResultEnum.PoolIsEmpty; - return result; + throw new UserFriendlyException($"失败,矿池已经被掏空了,请等矿池刷新后再来"); } // 生成一个 0 到 1 之间的随机数 @@ -87,7 +141,7 @@ public class MiningPoolManager : DomainService 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; //使用结果新增给对应的用户 @@ -102,8 +156,7 @@ public class MiningPoolManager : DomainService return result; } - result.Result = MiningResultEnum.Empty; - return result; + throw new UserFriendlyException($"恭喜!空空如也,挖了个寂寞"); } /// @@ -150,7 +203,7 @@ public class MiningPoolManager : DomainService /// 计算当前挖矿概率 /// /// - public static Task ComputeMiningProbabilityAsync() + public async Task ComputeMiningProbabilityAsync() { //当前的挖矿期望:当天的所有藏品能被刚好挖完 @@ -160,7 +213,8 @@ public class MiningPoolManager : DomainService //可影响因素:剩余手动挖矿次数+剩余自动挖矿次数 //简单模式,1/15 - return Task.FromResult(1m / 15); + var miningMaxLimit = decimal.Parse(await _settingProvider.GetOrNullAsync("MiningMinProbability")); + return miningMaxLimit; } /// @@ -179,7 +233,7 @@ public class MiningPoolManager : DomainService //根据配置,将不同比例的矿,塞入矿池, //矿池,交给redis - await _cache.SetAsync(CacheConst.MiningPoolContent, new MiningPoolContent(startTime, endTime) + await _miningPoolCache.SetAsync(MiningCacheConst.MiningPoolContent, new MiningPoolContent(startTime, endTime) { I0_OrdinaryNumber = result[0], I1_SeniorNumber = result[1], @@ -192,6 +246,14 @@ public class MiningPoolManager : DomainService }); } + /// + /// 刷新用户挖矿限制 + /// + public async Task RefreshMiningUserLimitAsync() + { + await RedisClient.DelAsync($"{MiningCacheConst.UserMiningLimit}*"); + } + /// /// 根据概率生成给对应稀有度藏品 /// diff --git a/Yi.Abp.Net8/module/digital-collectibles/Yi.Framework.DigitalCollectibles.Domain/Yi.Framework.DigitalCollectibles.Domain.csproj b/Yi.Abp.Net8/module/digital-collectibles/Yi.Framework.DigitalCollectibles.Domain/Yi.Framework.DigitalCollectibles.Domain.csproj index b8434d1a..431565d5 100644 --- a/Yi.Abp.Net8/module/digital-collectibles/Yi.Framework.DigitalCollectibles.Domain/Yi.Framework.DigitalCollectibles.Domain.csproj +++ b/Yi.Abp.Net8/module/digital-collectibles/Yi.Framework.DigitalCollectibles.Domain/Yi.Framework.DigitalCollectibles.Domain.csproj @@ -7,6 +7,7 @@ +