feat: 完成搭建基础功能

This commit is contained in:
橙子
2024-10-16 22:47:04 +08:00
parent b88cad7b80
commit ef20ef7014
7 changed files with 112 additions and 27 deletions

View File

@@ -24,6 +24,9 @@ public class AutoRefreshMiningPoolJob : QuartzBackgroundWorkerBase
public override async Task Execute(IJobExecutionContext context) public override async Task Execute(IJobExecutionContext context)
{ {
//刷新矿池
await _miningPoolManager.RefreshMiningPoolAsync(); await _miningPoolManager.RefreshMiningPoolAsync();
//刷新用户限制
await _miningPoolManager.RefreshMiningUserLimitAsync();
} }
} }

View File

@@ -1,9 +1,13 @@
namespace Yi.Framework.DigitalCollectibles.Domain.Shared.Consts; namespace Yi.Framework.DigitalCollectibles.Domain.Shared.Consts;
public class CacheConst public class MiningCacheConst
{ {
/// <summary> /// <summary>
/// 矿池 /// 矿池
/// </summary> /// </summary>
public const string MiningPoolContent = "MiningPool"; public const string MiningPoolContent = "MiningPool";
public const string UserMiningLimit = "UserMiningLimit";
public const string UserMiningLimitLock = "UserMiningLimitLock";
} }

View File

@@ -18,11 +18,14 @@ namespace Yi.Abp.Domain.Shared.Settings
//每日矿池最大上限 //每日矿池最大上限
new SettingDefinition("MaxPoolLimit", "100"), 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")
); );
} }
} }

View File

@@ -5,7 +5,5 @@ namespace Yi.Framework.DigitalCollectibles.Domain.Dtos;
public class MiningPoolResult public class MiningPoolResult
{ {
public MiningResultEnum Result { get; set; }
public CollectiblesAggregateRoot? Collectibles { get; set; } public CollectiblesAggregateRoot? Collectibles { get; set; }
} }

View File

@@ -0,0 +1,14 @@
namespace Yi.Framework.DigitalCollectibles.Domain.Dtos;
public class UserMiningLimitCacheDto
{
/// <summary>
/// 当前已挖矿次数
/// </summary>
public int Number { get; set; }
/// <summary>
/// 上次挖矿时间
/// </summary>
public DateTime? LastMiningTime{ get; set; }
}

View File

@@ -1,4 +1,5 @@
using Microsoft.Extensions.Caching.Distributed; using FreeRedis;
using Microsoft.Extensions.Caching.Distributed;
using Volo.Abp.Caching; using Volo.Abp.Caching;
using Volo.Abp.Domain.Services; using Volo.Abp.Domain.Services;
using Volo.Abp.Settings; using Volo.Abp.Settings;
@@ -21,57 +22,110 @@ public class MiningPoolManager : DomainService
private readonly ISqlSugarRepository<CollectiblesAggregateRoot> _collectiblesRepository; private readonly ISqlSugarRepository<CollectiblesAggregateRoot> _collectiblesRepository;
private readonly ISqlSugarRepository<OnHookAggregateRoot> _onHookRepository; private readonly ISqlSugarRepository<OnHookAggregateRoot> _onHookRepository;
private readonly ISettingProvider _settingProvider; private readonly ISettingProvider _settingProvider;
private readonly IDistributedCache<MiningPoolContent> _cache; private readonly IDistributedCache<MiningPoolContent?> _miningPoolCache;
private readonly IDistributedCache<UserMiningLimitCacheDto?> _userMiningLimitCache;
private readonly ISqlSugarRepository<CollectiblesUserStoreAggregateRoot> _userStoreRepository; private readonly ISqlSugarRepository<CollectiblesUserStoreAggregateRoot> _userStoreRepository;
private IRedisClient RedisClient => LazyServiceProvider.LazyGetRequiredService<IRedisClient>();
public MiningPoolManager(ISettingProvider settingProvider, IDistributedCache<MiningPoolContent> cache, public MiningPoolManager(ISettingProvider settingProvider, IDistributedCache<MiningPoolContent> miningPoolCache,
ISqlSugarRepository<CollectiblesAggregateRoot> collectiblesRepository, ISqlSugarRepository<CollectiblesAggregateRoot> collectiblesRepository,
ISqlSugarRepository<OnHookAggregateRoot> onHookRepository, ISqlSugarRepository<OnHookAggregateRoot> onHookRepository,
ISqlSugarRepository<CollectiblesUserStoreAggregateRoot> userStoreRepository) ISqlSugarRepository<CollectiblesUserStoreAggregateRoot> userStoreRepository,
IDistributedCache<UserMiningLimitCacheDto> userMiningLimitCache)
{ {
_settingProvider = settingProvider; _settingProvider = settingProvider;
this._cache = cache; this._miningPoolCache = miningPoolCache;
_collectiblesRepository = collectiblesRepository; _collectiblesRepository = collectiblesRepository;
_onHookRepository = onHookRepository; _onHookRepository = onHookRepository;
_userStoreRepository = userStoreRepository; _userStoreRepository = userStoreRepository;
_userMiningLimitCache = userMiningLimitCache;
} }
/// <summary> /// <summary>
/// 每次挖矿概率,每天根据特定算法计算 /// 每次挖矿概率,每天根据特定算法计算
/// </summary> /// </summary>
private static decimal CurrentMiningProbability => AsyncHelper.RunSync(async () => private decimal CurrentMiningProbability => AsyncHelper.RunSync(async () =>
{ {
return await ComputeMiningProbabilityAsync(); return await ComputeMiningProbabilityAsync();
}); });
public async Task<MiningPoolContent> GetMiningPoolContentAsync() public async Task<MiningPoolContent> GetMiningPoolContentAsync()
{ {
var pool = await _cache.GetAsync(CacheConst.MiningPoolContent); var pool = await _miningPoolCache.GetAsync(MiningCacheConst.MiningPoolContent);
return pool; return pool;
} }
/// <summary>
/// 校验挖矿限制
/// </summary>
/// <param name="userId"></param>
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($"失败,你都挖的冒烟了,请稍后再挖");
}
/// <summary> /// <summary>
/// 用户进行一次挖矿 /// 用户进行一次挖矿
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public async Task<MiningPoolResult> MiningAsync(Guid userId) public async Task<MiningPoolResult> MiningAsync(Guid userId)
{ {
//检验限制
await VerifyMiningLimitAsync(userId);
var result = new MiningPoolResult(); 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) if (pool.TotalNumber == 0)
{ {
result.Result = MiningResultEnum.PoolIsEmpty; throw new UserFriendlyException($"失败,矿池已经被掏空了,请等矿池刷新后再来");
return result;
} }
// 生成一个 0 到 1 之间的随机数 // 生成一个 0 到 1 之间的随机数
@@ -87,7 +141,7 @@ public class MiningPoolManager : DomainService
await _collectiblesRepository._DbQueryable.Where(x => x.Rarity == rarityType).ToListAsync(); await _collectiblesRepository._DbQueryable.Where(x => x.Rarity == rarityType).ToListAsync();
int randomIndex = new Random().Next(collectiblesList.Count); int randomIndex = new Random().Next(collectiblesList.Count);
var currentCollectibles = collectiblesList[randomIndex]; var currentCollectibles = collectiblesList[randomIndex];
result.Result = MiningResultEnum.Success;
result.Collectibles = currentCollectibles; result.Collectibles = currentCollectibles;
//使用结果新增给对应的用户 //使用结果新增给对应的用户
@@ -102,8 +156,7 @@ public class MiningPoolManager : DomainService
return result; return result;
} }
result.Result = MiningResultEnum.Empty; throw new UserFriendlyException($"恭喜!空空如也,挖了个寂寞");
return result;
} }
/// <summary> /// <summary>
@@ -150,7 +203,7 @@ public class MiningPoolManager : DomainService
/// 计算当前挖矿概率 /// 计算当前挖矿概率
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public static Task<decimal> ComputeMiningProbabilityAsync() public async Task<decimal> ComputeMiningProbabilityAsync()
{ {
//当前的挖矿期望:当天的所有藏品能被刚好挖完 //当前的挖矿期望:当天的所有藏品能被刚好挖完
@@ -160,7 +213,8 @@ public class MiningPoolManager : DomainService
//可影响因素:剩余手动挖矿次数+剩余自动挖矿次数 //可影响因素:剩余手动挖矿次数+剩余自动挖矿次数
//简单模式1/15 //简单模式1/15
return Task.FromResult(1m / 15); var miningMaxLimit = decimal.Parse(await _settingProvider.GetOrNullAsync("MiningMinProbability"));
return miningMaxLimit;
} }
/// <summary> /// <summary>
@@ -179,7 +233,7 @@ public class MiningPoolManager : DomainService
//根据配置,将不同比例的矿,塞入矿池, //根据配置,将不同比例的矿,塞入矿池,
//矿池交给redis //矿池交给redis
await _cache.SetAsync(CacheConst.MiningPoolContent, new MiningPoolContent(startTime, endTime) await _miningPoolCache.SetAsync(MiningCacheConst.MiningPoolContent, new MiningPoolContent(startTime, endTime)
{ {
I0_OrdinaryNumber = result[0], I0_OrdinaryNumber = result[0],
I1_SeniorNumber = result[1], I1_SeniorNumber = result[1],
@@ -192,6 +246,14 @@ public class MiningPoolManager : DomainService
}); });
} }
/// <summary>
/// 刷新用户挖矿限制
/// </summary>
public async Task RefreshMiningUserLimitAsync()
{
await RedisClient.DelAsync($"{MiningCacheConst.UserMiningLimit}*");
}
/// <summary> /// <summary>
/// 根据概率生成给对应稀有度藏品 /// 根据概率生成给对应稀有度藏品
/// </summary> /// </summary>

View File

@@ -7,6 +7,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\..\framework\Yi.Framework.Caching.FreeRedis\Yi.Framework.Caching.FreeRedis.csproj" />
<ProjectReference Include="..\..\..\framework\Yi.Framework.Mapster\Yi.Framework.Mapster.csproj" /> <ProjectReference Include="..\..\..\framework\Yi.Framework.Mapster\Yi.Framework.Mapster.csproj" />
<ProjectReference Include="..\..\..\framework\Yi.Framework.SqlSugarCore.Abstractions\Yi.Framework.SqlSugarCore.Abstractions.csproj" /> <ProjectReference Include="..\..\..\framework\Yi.Framework.SqlSugarCore.Abstractions\Yi.Framework.SqlSugarCore.Abstractions.csproj" />
<ProjectReference Include="..\..\setting-management\Yi.Framework.SettingManagement.Domain\Yi.Framework.SettingManagement.Domain.csproj" /> <ProjectReference Include="..\..\setting-management\Yi.Framework.SettingManagement.Domain\Yi.Framework.SettingManagement.Domain.csproj" />