feat: 完成

This commit is contained in:
橙子
2024-10-17 00:27:20 +08:00
parent cb1bac25a3
commit 260d87c97e
15 changed files with 361 additions and 59 deletions

View File

@@ -0,0 +1,47 @@
using Volo.Abp.Application.Dtos;
using Yi.Framework.DigitalCollectibles.Domain.Shared.Consts;
namespace Yi.Framework.DigitalCollectibles.Application.Contracts.Dtos.Collectibles;
public class CollectiblesDto : EntityDto<Guid>
{
/// <summary>
/// 藏品编号
/// </summary>
public string Code { get; set; }
/// <summary>
/// 藏品名称
/// </summary>
public string Name { get; set; }
/// <summary>
/// 藏品描述
/// </summary>
public string? Describe { get; set; }
/// <summary>
/// 价值数
/// </summary>
public decimal ValueNumber { get; set; }
/// <summary>
/// 藏品地址
/// </summary>
public string Url { get; set; }
/// <summary>
/// 稀有度
/// </summary>
public RarityEnum Rarity{ get; set; }
/// <summary>
/// 总共出现次数
/// </summary>
public int FindTotal { get; set; }
/// <summary>
/// 排序
/// </summary>
public int OrderNum { get; set; }
}

View File

@@ -2,7 +2,15 @@
namespace Yi.Framework.DigitalCollectibles.Application.Contracts.Dtos.Collectibles;
public class CollectiblesUserGetOutputDto:EntityDto<Guid>
public class CollectiblesUserGetOutputDto : EntityDto<Guid>
{
/// <summary>
/// 藏品
/// </summary>
public CollectiblesDto Collectibles{ get; set; }
/// <summary>
/// 数量
/// </summary>
public int Number { get; set; }
}

View File

@@ -1,4 +1,5 @@
using Volo.Abp.Application.Dtos;
using Yi.Framework.DigitalCollectibles.Application.Contracts.Dtos.Collectibles;
using Yi.Framework.DigitalCollectibles.Domain.Shared.Consts;
namespace Yi.Framework.DigitalCollectibles.Application.Contracts.Dtos.Market;
@@ -6,42 +7,24 @@ namespace Yi.Framework.DigitalCollectibles.Application.Contracts.Dtos.Market;
public class MarketGetListOutputDto:EntityDto<Guid>
{
/// <summary>
/// 藏品编号
/// 上架时间
/// </summary>
public string Code { get; set; }
public DateTime CreationTime{ get; set; }
/// <summary>
/// 藏品名称
/// 出售者用户id
/// </summary>
public string Name { get; set; }
public Guid SellUserId { get; set; }
/// <summary>
/// 藏品描述
/// 出售数量
/// </summary>
public string? Describe { get; set; }
/// <summary>
/// 价值数
/// </summary>
public decimal ValueNumber { get; set; }
/// <summary>
/// 藏品地址
/// </summary>
public string Url { get; set; }
public int SellNumber{ get; set; }
/// <summary>
/// 稀有度
/// 出售单价
/// </summary>
public RarityEnum Rarity{ get; set; }
public decimal UnitPrice{ get; set; }
/// <summary>
/// 总共出现次数
/// 藏品
/// </summary>
public int FindTotal { get; set; }
/// <summary>
/// 排序
/// </summary>
public int OrderNum { get; set; }
public CollectiblesDto Collectibles{ get; set; }
}

View File

@@ -0,0 +1,8 @@
namespace Yi.Framework.DigitalCollectibles.Application.Contracts.Dtos.Market;
public class PurchaseGoodsDto
{
public Guid MarketGoodsId{ get; set; }
public int Number{ get; set; }
}

View File

@@ -0,0 +1,10 @@
namespace Yi.Framework.DigitalCollectibles.Application.Contracts.Dtos.Market;
public class ShelvedGoodsDto
{
public Guid CollectiblesId { get; set; }
public int Number { get; set; }
public decimal Mmoney { get; set; }
}

View File

@@ -5,7 +5,6 @@ namespace Yi.Framework.DigitalCollectibles.Application.Contracts.Dtos.MiningPool
public class MiningResultOutput
{
public MiningResultEnum Result { get; set; }
public CollectiblesUserGetOutputDto? Collectibles { get; set; }
}

View File

@@ -7,6 +7,7 @@ using Volo.Abp.Application.Services;
using Yi.Framework.DigitalCollectibles.Application.Contracts.Dtos.Collectibles;
using Yi.Framework.DigitalCollectibles.Application.Contracts.Dtos.Market;
using Yi.Framework.DigitalCollectibles.Domain.Entities;
using Yi.Framework.DigitalCollectibles.Domain.Shared.Consts;
using Yi.Framework.SqlSugarCore.Abstractions;
namespace Yi.Framework.DigitalCollectibles.Application.Services;
@@ -35,12 +36,31 @@ public class CollectiblesService : ApplicationService
CollectiblesUserGetInput input)
{
RefAsync<int> total = 0;
var entities = await _collectiblesUserStoreRepository._DbQueryable.WhereIF(
var output = await _collectiblesUserStoreRepository._DbQueryable.WhereIF(
input.StartTime is not null && input.EndTime is not null,
x => x.CreationTime >= input.StartTime && x.CreationTime <= input.EndTime)
.OrderByDescending(x => x.CreationTime)
u => u.CreationTime >= input.StartTime && u.CreationTime <= input.EndTime)
.LeftJoin<CollectiblesAggregateRoot>((u, c) => u.CollectiblesId == c.Id)
.GroupBy((u, c) => u.CollectiblesId)
.Select((u, c) =>
new CollectiblesUserGetOutputDto
{
Id = c.Id,
Collectibles = new CollectiblesDto
{
Id = c.Id,
Code = c.Code,
Name = c.Name,
Describe = c.Describe,
ValueNumber = c.ValueNumber,
Url = c.Url,
Rarity = c.Rarity,
FindTotal = c.FindTotal,
OrderNum = c.OrderNum
},
Number = SqlFunc.AggregateCount(u.CollectiblesId)
})
.OrderBy(dto => dto.Collectibles.OrderNum)
.ToPageListAsync(input.SkipCount, input.MaxResultCount, total);
var output = entities.Adapt<List<CollectiblesUserGetOutputDto>>();
return new PagedResultDto<CollectiblesUserGetOutputDto>(total, output);
}
}

View File

@@ -1,23 +1,33 @@
using Mapster;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using SqlSugar;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
using Volo.Abp.Users;
using Yi.Framework.DigitalCollectibles.Application.Contracts.Dtos.Collectibles;
using Yi.Framework.DigitalCollectibles.Application.Contracts.Dtos.Market;
using Yi.Framework.DigitalCollectibles.Domain.Entities;
using Yi.Framework.DigitalCollectibles.Domain.Managers;
using Yi.Framework.DigitalCollectibles.Domain.Shared.Consts;
using Yi.Framework.SqlSugarCore.Abstractions;
namespace Yi.Framework.DigitalCollectibles.Application.Services;
/// <summary>
/// 市场应用服务
/// 交易市场应用服务
/// </summary>
public class MarketService:ApplicationService
public class MarketService : ApplicationService
{
private readonly ISqlSugarRepository<MarketGoodsAggregateRoot> _marketGoodsRepository;
public MarketService(ISqlSugarRepository<MarketGoodsAggregateRoot> marketGoodsRepository)
private readonly MarketManager _marketManager;
public MarketService(ISqlSugarRepository<MarketGoodsAggregateRoot> marketGoodsRepository,
MarketManager marketManager)
{
_marketGoodsRepository = marketGoodsRepository;
_marketManager = marketManager;
}
/// <summary>
@@ -28,12 +38,56 @@ public class MarketService:ApplicationService
public async Task<PagedResultDto<MarketGetListOutputDto>> GetListAsync(MarketGetListInput input)
{
RefAsync<int> total = 0;
var entities = await _marketGoodsRepository._DbQueryable.WhereIF(
var output = await _marketGoodsRepository._DbQueryable.WhereIF(
input.StartTime is not null && input.EndTime is not null,
x => x.CreationTime >= input.StartTime && x.CreationTime <= input.EndTime)
.OrderByDescending(x => x.CreationTime)
m => m.CreationTime >= input.StartTime && m.CreationTime <= input.EndTime)
.LeftJoin<CollectiblesAggregateRoot>((m, c) => m.CollectiblesId == c.Id)
.Select((m, c) =>
new MarketGetListOutputDto
{
Id = m.Id,
CreationTime = m.CreationTime,
SellUserId = m.SellUserId,
SellNumber = m.SellNumber,
Collectibles = new CollectiblesDto
{
Id = c.Id,
Code = c.Code,
Name = c.Name,
Describe = c.Describe,
ValueNumber = c.ValueNumber,
Url = c.Url,
Rarity =c.Rarity,
FindTotal = c.FindTotal,
OrderNum = c.OrderNum
}
}
)
.OrderByDescending(dto => dto.CreationTime)
.ToPageListAsync(input.SkipCount, input.MaxResultCount, total);
var output = entities.Adapt<List<MarketGetListOutputDto>>();
return new PagedResultDto<MarketGetListOutputDto>(total, output);
}
/// <summary>
/// 上架商品
/// </summary>
[HttpPost("shelved")]
[Authorize]
public async Task ShelvedGoodsAsync(ShelvedGoodsDto input)
{
await _marketManager.ShelvedGoodsAsync(CurrentUser.GetId(), input.CollectiblesId, input.Number, input.Mmoney);
}
/// <summary>
/// 购买商品
/// </summary>
[HttpPut("purchase")]
[Authorize]
public async Task PurchaseGoodsAsync(PurchaseGoodsDto input)
{
await _marketManager.PurchaseGoodsAsync(CurrentUser.GetId(),input.MarketGoodsId, input.Number);
}
}

View File

@@ -8,10 +8,22 @@ using Yi.Framework.DigitalCollectibles.Domain.Managers;
namespace Yi.Framework.DigitalCollectibles.Application.Services;
/// <summary>
/// 矿池应用服务
/// </summary>
public class MiningPoolService : ApplicationService
{
private readonly MiningPoolManager _manager;
/// <summary>
/// 内测-白嫖-获取自动挖矿卡
/// </summary>
[HttpPost("mining-pool/on-hook")]
public async Task GetOnHookAsync()
{
await _manager.GetOnHookAsync(CurrentUser.GetId());
}
/// <summary>
/// 获取矿池状态
/// </summary>

View File

@@ -15,17 +15,20 @@ namespace Yi.Abp.Domain.Shared.Settings
public override void Define(ISettingDefinitionContext context)
{
context.Add(
//每日矿池最大上限
//每日矿池最大上限--控制矿池膨胀率
new SettingDefinition("MaxPoolLimit", "50"),
//每日挖矿最大上限
//每日挖矿最大上限--控制无限挖矿
new SettingDefinition("MiningMaxLimit", "36"),
//每次挖矿最小间隔(秒)
//每次挖矿最小间隔(秒)--控制暴力挖矿
new SettingDefinition("MiningMinIntervalSeconds", "5"),
//每次挖到矿的概率
new SettingDefinition("MiningMinProbability", "0.06")
//每次挖到矿的概率--控制爆率
new SettingDefinition("MiningMinProbability", "0.06"),
//交易税率--控制频繁交易
new SettingDefinition("MarketTaxRate", "0.2")
);
}
}

View File

@@ -6,7 +6,7 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\bbs\Yi.Framework.Bbs.Domain.Shared\Yi.Framework.Bbs.Domain.Shared.csproj" />
</ItemGroup>
<ItemGroup>

View File

@@ -8,7 +8,7 @@ namespace Yi.Framework.DigitalCollectibles.Domain.Entities;
/// 表示用户与藏品的库存关系
/// </summary>
[SugarTable("DC_CollectiblesUserStore")]
public class CollectiblesUserStoreAggregateRoot:FullAuditedAggregateRoot<Guid>
public class CollectiblesUserStoreAggregateRoot : FullAuditedAggregateRoot<Guid>
{
/// <summary>
/// 用户id
@@ -24,4 +24,27 @@ public class CollectiblesUserStoreAggregateRoot:FullAuditedAggregateRoot<Guid>
/// 用户是否已读
/// </summary>
public bool IsRead { get; set; }
/// <summary>
/// 是否正在市场交易
/// </summary>
public bool IsAtMarketing { get; set; }
/// <summary>
/// 上架货物
/// </summary>
public void ShelvedMarket()
{
IsAtMarketing = true;
}
/// <summary>
/// 交易货物
/// </summary>
public void PurchaseMarket(Guid userId)
{
UserId = userId;
IsAtMarketing = false;
}
}

View File

@@ -9,8 +9,22 @@ namespace Yi.Framework.DigitalCollectibles.Domain.Entities;
/// 用于定时任务处理自动挖矿
/// </summary>
[SugarTable("DC_OnHook")]
public class OnHookAggregateRoot:FullAuditedAggregateRoot<Guid>
public class OnHookAggregateRoot : FullAuditedAggregateRoot<Guid>
{
public OnHookAggregateRoot()
{
}
public OnHookAggregateRoot(Guid userId, int effectiveHours)
{
UserId = userId;
EffectiveHours = effectiveHours;
StarTime = DateTime.Now;
EndTime = DateTime.Now.AddHours(effectiveHours);
IsActive = true;
}
/// <summary>
/// 用户id
/// </summary>
@@ -20,19 +34,19 @@ public class OnHookAggregateRoot:FullAuditedAggregateRoot<Guid>
/// 开始时间
/// </summary>
public DateTime? StarTime { get; set; }
/// <summary>
/// 结束时间
/// </summary>
public DateTime? EndTime { get; set; }
/// <summary>
/// 有效小时数
/// </summary>
public int EffectiveHours{ get; set; }
public int EffectiveHours { get; set; }
/// <summary>
/// 是否激活
/// </summary>
public bool IsActive{ get; set; }
public bool IsActive { get; set; }
}

View File

@@ -1,4 +1,9 @@
using Volo.Abp.Domain.Services;
using Volo.Abp.EventBus.Local;
using Volo.Abp.Settings;
using Yi.Framework.Bbs.Domain.Shared.Etos;
using Yi.Framework.DigitalCollectibles.Domain.Entities;
using Yi.Framework.SqlSugarCore.Abstractions;
namespace Yi.Framework.DigitalCollectibles.Domain.Managers;
@@ -6,7 +11,109 @@ namespace Yi.Framework.DigitalCollectibles.Domain.Managers;
/// 市场领域服务
/// 处理交易市场相关业务,例如交易等
/// </summary>
public class MarketManager:DomainService
public class MarketManager : DomainService
{
private readonly ISqlSugarRepository<CollectiblesUserStoreAggregateRoot> _collectiblesUserStoreRepository;
private readonly ISqlSugarRepository<MarketGoodsAggregateRoot> _marketGoodsRepository;
private readonly ILocalEventBus _localEventBus;
public readonly ISettingProvider _settingProvider;
public MarketManager(ISqlSugarRepository<CollectiblesUserStoreAggregateRoot> collectiblesUserStoreRepository,
ISqlSugarRepository<MarketGoodsAggregateRoot> marketGoodsRepository, ILocalEventBus localEventBus, ISettingProvider settingProvider)
{
_collectiblesUserStoreRepository = collectiblesUserStoreRepository;
_marketGoodsRepository = marketGoodsRepository;
_localEventBus = localEventBus;
_settingProvider = settingProvider;
}
/// <summary>
/// 上架货物
/// </summary>
/// <param name="userId">上架者</param>
/// <param name="collectiblesId">上架的收藏品id</param>
/// <param name="number">上架数量</param>
/// <param name="money">上架单价</param>
/// <returns></returns>
public async Task ShelvedGoodsAsync(Guid userId, Guid collectiblesId, int number, decimal money)
{
var collectiblesList = await _collectiblesUserStoreRepository._DbQueryable.Where(x => x.IsAtMarketing == false)
.Where(x => x.CollectiblesId == collectiblesId).ToListAsync();
if (collectiblesList.Count < number)
{
throw new UserFriendlyException($"您的藏品不足{number}个,上架失败");
}
//上架收藏品
var shelvedcollectibles = collectiblesList.Take(2);
foreach (var store in shelvedcollectibles)
{
store.ShelvedMarket();
}
await _collectiblesUserStoreRepository.UpdateRangeAsync(shelvedcollectibles.ToList());
await _marketGoodsRepository.InsertAsync(new MarketGoodsAggregateRoot
{
SellUserId = userId,
CollectiblesId = collectiblesId,
SellNumber = number,
UnitPrice = money
});
}
/// <summary>
/// 购买商品
/// </summary>
/// <param name="userId">购买者用户</param>
/// <param name="marketGoodsId">商品id</param>
/// <param name="number">购买数量</param>
/// <returns></returns>
public async Task PurchaseGoodsAsync(Guid userId, Guid marketGoodsId, int number)
{
//1-市场扣减或者关闭该商品
//2-出售者新增钱,购买者扣钱
//3-出售者删除对应库存,购买者新增对应库存
var marketGoods = await _marketGoodsRepository.GetAsync(x => x.Id == marketGoodsId);
//1-市场扣减或者关闭该商品
if (marketGoods.SellNumber == number)
{
await _marketGoodsRepository.DeleteAsync(x => x.Id == marketGoodsId);
}
else if (marketGoods.SellNumber >= number)
{
marketGoods.SellNumber -= number;
await _marketGoodsRepository.UpdateAsync(marketGoods);
}
else
{
throw new UserFriendlyException($"交易失败,当前交易市场库存不足");
}
//2-出售者新增钱,购买者扣钱
//发布一个其他领域的事件-购买者扣钱
await _localEventBus.PublishAsync(new MoneyChangeEventArgs() { UserId = userId, Number = -number },false);
//发布一个其他领域的事件-出售者加钱,同时扣税
var marketTaxRate = decimal.Parse(await _settingProvider.GetOrNullAsync("MarketTaxRate"));
await _localEventBus.PublishAsync(new MoneyChangeEventArgs() { UserId = userId, Number = number*(1-marketTaxRate) },false);
//3-出售者删除对应库存,购买者新增对应库存
var collectiblesList = await _collectiblesUserStoreRepository._DbQueryable.Where(x => x.IsAtMarketing == true)
.Where(x => x.UserId == marketGoods.SellUserId)
.Where(x => x.CollectiblesId == marketGoods.CollectiblesId)
.ToListAsync();
if (collectiblesList.Count < number)
{
throw new UserFriendlyException($"交易失败,当前出售者库存不足");
}
var updateStore = collectiblesList.Take(number);
foreach (var userStore in updateStore)
{
userStore.PurchaseMarket(userId);
}
await _collectiblesUserStoreRepository.UpdateRangeAsync(updateStore.ToList());
}
}

View File

@@ -55,6 +55,20 @@ public class MiningPoolManager : DomainService
return pool;
}
public async Task GetOnHookAsync(Guid userId)
{
var onHook = await _onHookRepository._DbQueryable.Where(x => x.UserId == userId)
.Where(x => x.IsActive == true)
.Where(x => x.EndTime <= DateTime.Now)
.FirstAsync();
if (onHook is not null)
{
throw new UserFriendlyException($"当前你正在进行自动挂机,结束时间:{onHook.EndTime.Value.ToString("MM月dd日HH分mm秒")})");
}
await _onHookRepository.InsertAsync(new OnHookAggregateRoot(userId, 24));
}
/// <summary>
/// 校验挖矿限制