diff --git a/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Application/Jobs/AssignmentExpireTimeOutJob.cs b/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Application/Jobs/AssignmentExpireTimeOutJob.cs new file mode 100644 index 00000000..937fb689 --- /dev/null +++ b/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Application/Jobs/AssignmentExpireTimeOutJob.cs @@ -0,0 +1,27 @@ +using Quartz; +using Volo.Abp.BackgroundWorkers.Quartz; +using Yi.Framework.Bbs.Domain.Managers; + +namespace Yi.Framework.Bbs.Application.Jobs; + +/// +/// 每日任务job +/// +public class AssignmentExpireTimeOutJob : QuartzBackgroundWorkerBase +{ + private readonly AssignmentManager _assignmentManager; + + public AssignmentExpireTimeOutJob(AssignmentManager assignmentManager) + { + _assignmentManager = assignmentManager; + JobDetail = JobBuilder.Create().WithIdentity(nameof(AssignmentExpireTimeOutJob)).Build(); + //每个小时整点执行一次 + Trigger = TriggerBuilder.Create().WithIdentity(nameof(AssignmentExpireTimeOutJob)).WithCronSchedule("0 0 * * * ?") + .Build(); + } + + public override async Task Execute(IJobExecutionContext context) + { + await _assignmentManager.ExpireTimeoutAsync(); + } +} \ No newline at end of file diff --git a/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Domain.Shared/Enums/AssignmentTypeEnum.cs b/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Domain.Shared/Enums/AssignmentTypeEnum.cs index d0665771..84e00060 100644 --- a/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Domain.Shared/Enums/AssignmentTypeEnum.cs +++ b/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Domain.Shared/Enums/AssignmentTypeEnum.cs @@ -6,14 +6,60 @@ public enum AssignmentTypeEnum /// 新手任务 /// Novice, - + /// /// 每日任务 /// Daily, - + /// /// 每周任务 /// Weekly +} + +public static class AssignmentTypeExtension +{ + public static DateTime? GetExpireTime(this AssignmentTypeEnum assignmentType) + { + switch (assignmentType) + { + case AssignmentTypeEnum.Novice: + return null; + case AssignmentTypeEnum.Daily: + return DateTime.Now.Date.AddDays(1); + case AssignmentTypeEnum.Weekly: + DateTime today = DateTime.Now; // 获取当前日期和时间 + int daysUntilNextMonday = ((int)DayOfWeek.Monday - (int)today.DayOfWeek + 7) % 7 + 7; + DateTime nextMonday = today.AddDays(daysUntilNextMonday).Date; // 添加天数并将时间设为 0 点 + return nextMonday; + default: + throw new ArgumentOutOfRangeException(nameof(assignmentType), assignmentType, null); + } + } + + public static bool IsExpire(this AssignmentTypeEnum assignmentType,DateTime time) + { + switch (assignmentType) + { + case AssignmentTypeEnum.Novice: + return false; + case AssignmentTypeEnum.Daily: + //昨天之前发的,算过期 + return time.Date < DateTime.Now.Date; + case AssignmentTypeEnum.Weekly: + // 获取当前日期 + DateTime now = DateTime.Now; + // 计算本周一的日期 + int daysToSubtract = (int)now.DayOfWeek - (int)DayOfWeek.Monday; + if (daysToSubtract < 0) daysToSubtract += 7; // 如果今天是周日,则需要调整 + DateTime startOfWeek = now.AddDays(-daysToSubtract).Date; + // 获取本周一的凌晨 00:00 + DateTime mondayMidnight = startOfWeek; // .Date 默认为 00:00 + //本周一之前发的 + return time, IHasCreationTime, IO /// public Guid UserId { get; set; } - /// - /// 总共步骤数 - /// - public int TotalStepNumber { get; set; } - /// /// 当前步骤数 /// public int CurrentStepNumber { get; set; } + /// + /// 总共步骤数 + /// + public int TotalStepNumber { get; set; } /// /// 任务状态 /// public AssignmentStateEnum AssignmentState { get; set; } - + /// + /// 任务奖励的钱钱数量 + /// + public decimal RewardsMoneyNumber { get; set; } /// /// 任务过期时间 /// public DateTime? ExpireTime { get; set; } + + public DateTime? CompleteTime { get; set; } + public DateTime CreationTime { get; } public int OrderNum { get; set; } public DateTime? LastModificationTime { get; } + + + public bool IsAllowCompleted() + { + return AssignmentState == AssignmentStateEnum.Progress && this.CurrentStepNumber == this.TotalStepNumber; + } + + public bool TrySetExpire() + { + if (ExpireTime<=DateTime.Now) + { + AssignmentState = AssignmentStateEnum.Expired; + return false; + } + + return true; + } } \ No newline at end of file diff --git a/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Domain/Entities/Assignment/AssignmentDefineAggregateRoot.cs b/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Domain/Entities/Assignment/AssignmentDefineAggregateRoot.cs index 5573e76a..72d44e8d 100644 --- a/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Domain/Entities/Assignment/AssignmentDefineAggregateRoot.cs +++ b/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Domain/Entities/Assignment/AssignmentDefineAggregateRoot.cs @@ -31,7 +31,11 @@ public class AssignmentDefineAggregateRoot: AggregateRoot, IHasCreationTim /// public AssignmentTypeEnum AssignmentType{ get; set; } - + /// + /// 总共步骤数 + /// + public int TotalStepNumber { get; set; } + /// /// 前置任务id /// diff --git a/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Domain/Managers/AssignmentManager.cs b/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Domain/Managers/AssignmentManager.cs index e37d92a8..8ab3e89c 100644 --- a/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Domain/Managers/AssignmentManager.cs +++ b/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Domain/Managers/AssignmentManager.cs @@ -1,5 +1,12 @@ -using Volo.Abp.Domain.Services; +using SqlSugar; +using Volo.Abp.Domain.Services; +using Volo.Abp.EventBus.Local; +using Volo.Abp.Users; using Yi.Framework.Bbs.Domain.Entities.Assignment; +using Yi.Framework.Bbs.Domain.Managers.AssignmentProviders; +using Yi.Framework.Bbs.Domain.Shared.Enums; +using Yi.Framework.Bbs.Domain.Shared.Etos; +using Yi.Framework.SqlSugarCore.Abstractions; namespace Yi.Framework.Bbs.Domain.Managers; @@ -8,36 +15,125 @@ namespace Yi.Framework.Bbs.Domain.Managers; /// public class AssignmentManager : DomainService { + private readonly IEnumerable _assignmentProviders; + private readonly ISqlSugarRepository _assignmentRepository; + private readonly ISqlSugarRepository _assignmentDefineRepository; + private readonly ILocalEventBus _localEventBus; + + public AssignmentManager(IEnumerable assignmentProviders, + ISqlSugarRepository assignmentRepository, + ISqlSugarRepository assignmentDefineRepository, ILocalEventBus localEventBus) + { + this._assignmentProviders = assignmentProviders; + _assignmentRepository = assignmentRepository; + _assignmentDefineRepository = assignmentDefineRepository; + _localEventBus = localEventBus; + } + /// /// 接受任务 /// /// 领取用户 /// 任务定义id /// - public Task AcceptAsync(Guid userId, Guid asignmentDefineId) + public async Task AcceptAsync(Guid userId, Guid asignmentDefineId) { - throw new NotImplementedException(); + var canReceiveList = await GetCanReceiveListAsync(userId); + + //如果要接收的任务id在可领取的任务列表中,就可以接收任务 + if (canReceiveList.Select(x => x.Id).Contains(asignmentDefineId)) + { + var assignmentDefine = await _assignmentDefineRepository.GetByIdAsync(asignmentDefineId); + + var entity = new AssignmentAggregateRoot(); + entity.AssignmentDefineId = asignmentDefineId; + entity.UserId = userId; + entity.AssignmentState = AssignmentStateEnum.Progress; + entity.CurrentStepNumber = 0; + entity.TotalStepNumber = assignmentDefine.TotalStepNumber; + entity.RewardsMoneyNumber = assignmentDefine.RewardsMoneyNumber; + entity.ExpireTime = assignmentDefine.AssignmentType.GetExpireTime(); + await _assignmentRepository.InsertAsync(entity); + } } - + /// /// 领取任务奖励 /// /// 任务id /// /// - public Task ReceiveRewardsAsync(Guid asignmentId) + public async Task ReceiveRewardsAsync(Guid asignmentId) { - throw new NotImplementedException(); + var assignment = await _assignmentRepository.GetByIdAsync(asignmentId); + if (assignment.IsAllowCompleted()) + { + //设置已完成,并领取奖励,钱钱 + assignment.AssignmentState = AssignmentStateEnum.Completed; + //加钱加钱 + await _localEventBus.PublishAsync( + new MoneyChangeEventArgs { UserId = assignment.UserId, Number = assignment.RewardsMoneyNumber }, false); + } } - - + + /// /// 根据用户id获取能够领取的任务列表 /// /// 用户id /// - public Task> GetCanReceiveListAsync(Guid userId) + public async Task> GetCanReceiveListAsync(Guid userId) { + var context = await GetAssignmentContext(userId); + var output = new List(); + foreach (var assignmentProvider in _assignmentProviders) + { + output.AddRange(await assignmentProvider.GetCanReceiveListAsync(context)); + } + + output.DistinctBy(x => x.Id); throw new NotImplementedException(); } + + + /// + /// 获取任务的上下文 + /// + /// + /// + private async Task GetAssignmentContext(Guid userId) + { + var allAssignmentDefine = await _assignmentDefineRepository.GetListAsync(); + + var currentUserAssignment = await _assignmentRepository.GetListAsync(x => x.UserId == userId); + + var context = new AssignmentContext(userId, allAssignmentDefine, currentUserAssignment); + return context; + } + + + /// + /// 过期超时的任务,定时任务去处理即可 + /// + public async Task ExpireTimeoutAsync() + { + var progressEntities = await _assignmentRepository._DbQueryable + .Where(x => x.AssignmentState == AssignmentStateEnum.Progress) + .ToListAsync(); + + var needUpdateEntities = new List(); + foreach (var progressEntity in progressEntities) + { + if (progressEntity.TrySetExpire()) + { + needUpdateEntities.Add(progressEntity); + } + } + + if (needUpdateEntities.Any()) + { + await _assignmentRepository._Db.Updateable(needUpdateEntities).ExecuteCommandAsync(); + } + + } } \ No newline at end of file diff --git a/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Domain/Managers/AssignmentProviders/AssignmentContext.cs b/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Domain/Managers/AssignmentProviders/AssignmentContext.cs index 4f1f6048..5ed79e80 100644 --- a/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Domain/Managers/AssignmentProviders/AssignmentContext.cs +++ b/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Domain/Managers/AssignmentProviders/AssignmentContext.cs @@ -4,6 +4,13 @@ namespace Yi.Framework.Bbs.Domain.Managers.AssignmentProviders; public class AssignmentContext { + public AssignmentContext( Guid currentUserId,List allAssignmentDefine, List currentUserAssignments) + { + AllAssignmentDefine = allAssignmentDefine; + CurrentUserAssignments = currentUserAssignments; + CurrentUserId = currentUserId; + } + /// /// 全部的任务定义 /// @@ -17,5 +24,5 @@ public class AssignmentContext /// /// 当前用户id /// - public Guid CurrentUserId { get; set; } + public Guid CurrentUserId { get; } } \ No newline at end of file diff --git a/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Domain/Managers/AssignmentProviders/IAssignmentProvider.cs b/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Domain/Managers/AssignmentProviders/IAssignmentProvider.cs index 9f718414..0cffaff7 100644 --- a/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Domain/Managers/AssignmentProviders/IAssignmentProvider.cs +++ b/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Domain/Managers/AssignmentProviders/IAssignmentProvider.cs @@ -14,11 +14,4 @@ public interface IAssignmentProvider : ITransientDependency /// /// Task> GetCanReceiveListAsync(AssignmentContext context); - - /// - /// 校验是否能够被领取,该方法还需工厂进行代理执行一次 - /// - /// - /// - Task VerifyCanAcceptAsync(AssignmentContext context); } \ No newline at end of file diff --git a/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Domain/Managers/AssignmentProviders/Impl/DailyProvider.cs b/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Domain/Managers/AssignmentProviders/Impl/DailyProvider.cs index 56ce0057..a51477a3 100644 --- a/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Domain/Managers/AssignmentProviders/Impl/DailyProvider.cs +++ b/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Domain/Managers/AssignmentProviders/Impl/DailyProvider.cs @@ -1,9 +1,11 @@ -namespace Yi.Framework.Bbs.Domain.Managers.AssignmentProviders; +using Yi.Framework.Bbs.Domain.Shared.Enums; + +namespace Yi.Framework.Bbs.Domain.Managers.AssignmentProviders; /// /// 每日任务提供者 /// public class DailyProvider : TimerProvider { - protected override TimeSpan TimeCycle => TimeSpan.FromDays(1); + protected override AssignmentTypeEnum AssignmentType => AssignmentTypeEnum.Daily; } \ No newline at end of file diff --git a/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Domain/Managers/AssignmentProviders/Impl/NoviceProvider.cs b/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Domain/Managers/AssignmentProviders/Impl/NoviceProvider.cs index 24f2062c..d4ff0a02 100644 --- a/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Domain/Managers/AssignmentProviders/Impl/NoviceProvider.cs +++ b/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Domain/Managers/AssignmentProviders/Impl/NoviceProvider.cs @@ -9,11 +9,7 @@ public class NoviceProvider : IAssignmentProvider { public Task> GetCanReceiveListAsync(AssignmentContext context) { - throw new NotImplementedException(); - } - - public Task VerifyCanAcceptAsync(AssignmentContext context) - { + //新手任务是要有前置依赖关系的,链表类型依赖 throw new NotImplementedException(); } } \ No newline at end of file diff --git a/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Domain/Managers/AssignmentProviders/Impl/WeeklyProvider.cs b/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Domain/Managers/AssignmentProviders/Impl/WeeklyProvider.cs index 4a39e7a7..52d264d8 100644 --- a/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Domain/Managers/AssignmentProviders/Impl/WeeklyProvider.cs +++ b/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Domain/Managers/AssignmentProviders/Impl/WeeklyProvider.cs @@ -1,9 +1,11 @@ -namespace Yi.Framework.Bbs.Domain.Managers.AssignmentProviders; +using Yi.Framework.Bbs.Domain.Shared.Enums; + +namespace Yi.Framework.Bbs.Domain.Managers.AssignmentProviders; /// /// 每周任务提供者 /// public class WeeklyProvider : TimerProvider { - protected override TimeSpan TimeCycle => TimeSpan.FromDays(7); + protected override AssignmentTypeEnum AssignmentType => AssignmentTypeEnum.Weekly; } \ No newline at end of file diff --git a/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Domain/Managers/AssignmentProviders/TimerAbstractProvider.cs b/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Domain/Managers/AssignmentProviders/TimerAbstractProvider.cs index a22a8637..527d659b 100644 --- a/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Domain/Managers/AssignmentProviders/TimerAbstractProvider.cs +++ b/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Domain/Managers/AssignmentProviders/TimerAbstractProvider.cs @@ -1,24 +1,41 @@ -using Yi.Framework.Bbs.Domain.Entities.Assignment; +using SqlSugar; +using Yi.Framework.Bbs.Domain.Entities.Assignment; +using Yi.Framework.Bbs.Domain.Shared.Enums; namespace Yi.Framework.Bbs.Domain.Managers.AssignmentProviders; /// -/// 定时任务提供者 +/// 循环任务提供者 /// public abstract class TimerProvider : IAssignmentProvider { /// - /// 时间周期 + /// 任务类型 /// - protected abstract TimeSpan TimeCycle { get; } + protected abstract AssignmentTypeEnum AssignmentType { get; } public Task> GetCanReceiveListAsync(AssignmentContext context) { - throw new NotImplementedException(); - } + //先获取到对应任务定义列表 + var assignmentDefines = context.AllAssignmentDefine.Where(x => x.AssignmentType == AssignmentType).ToList(); - public Task VerifyCanAcceptAsync(AssignmentContext context) - { - throw new NotImplementedException(); + //满足以下1个条件 + //1:没有正在运行的 + //2: 存在已完成,但是完成时间是过期的 + var assignmentFilterIds = context.CurrentUserAssignments + .Where(x => + //正在进行的,要去掉 + x.AssignmentState == AssignmentStateEnum.Progress|| + //已完成,但是还没过期,要去掉 + (x.AssignmentState == AssignmentStateEnum.Completed&&!AssignmentType.IsExpire(x.CompleteTime!.Value))) + .Select(x => x.AssignmentDefineId) + .ToList(); + + + + //出去不可接收的任务,就是可接收任务 + var output = assignmentDefines.Where(x => !assignmentFilterIds.Contains(x.Id)).ToList(); + return Task.FromResult(output); } + } \ No newline at end of file