diff --git a/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Domain.Shared/Enums/AssignmentRequirementTypeEnum.cs b/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Domain.Shared/Enums/AssignmentRequirementTypeEnum.cs index 4c7c9c88..b242feea 100644 --- a/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Domain.Shared/Enums/AssignmentRequirementTypeEnum.cs +++ b/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Domain.Shared/Enums/AssignmentRequirementTypeEnum.cs @@ -5,24 +5,25 @@ public enum AssignmentRequirementTypeEnum /// /// 主题 /// - Discuss=2, - + Discuss = 2, + /// /// 评论 /// - Comment=4, - + Comment = 4, + /// /// 点赞 /// - Agree=8, - - /// - /// 更新个人中心 - /// - UpdateProfile=16 - - + Agree = 8, - + /// + /// 更新昵称 + /// + UpdateNick = 16, + + /// + /// 更新头像 + /// + UpdateIcon = 32, } \ No newline at end of file diff --git a/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Domain/EventHandlers/AssignmentEventHandler.cs b/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Domain/EventHandlers/AssignmentEventHandler.cs index b9c6d4af..d8db8835 100644 --- a/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Domain/EventHandlers/AssignmentEventHandler.cs +++ b/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Domain/EventHandlers/AssignmentEventHandler.cs @@ -33,21 +33,26 @@ public class AssignmentEventHandler : ILocalEventHandler, I case AssignmentRequirementTypeEnum.Discuss: SetCurrentStepNumber(AssignmentRequirementTypeEnum.Discuss, currentAssignmentList); break; - + //发表评论 case AssignmentRequirementTypeEnum.Comment: SetCurrentStepNumber(AssignmentRequirementTypeEnum.Comment, currentAssignmentList); break; - + //点赞 case AssignmentRequirementTypeEnum.Agree: SetCurrentStepNumber(AssignmentRequirementTypeEnum.Agree, currentAssignmentList); break; - - //更新个人信息 - case AssignmentRequirementTypeEnum.UpdateProfile: - //这里还需判断是否更新了 + + //更新昵称 + case AssignmentRequirementTypeEnum.UpdateNick: break; + + //更新头像 + case AssignmentRequirementTypeEnum.UpdateIcon: + SetCurrentStepNumber(AssignmentRequirementTypeEnum.UpdateIcon, currentAssignmentList); + break; + default: throw new ArgumentOutOfRangeException(); } @@ -72,7 +77,7 @@ public class AssignmentEventHandler : ILocalEventHandler, I x.CurrentStepNumber < x.TotalStepNumber) { x.CurrentStepNumber += 1; - if (x.CurrentStepNumber==x.TotalStepNumber) + if (x.CurrentStepNumber == x.TotalStepNumber) { x.AssignmentState = AssignmentStateEnum.Completed; } 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 6b2052f8..f411efb0 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 @@ -1,16 +1,57 @@ -using Yi.Framework.Bbs.Domain.Entities.Assignment; +using SqlSugar; +using Volo.Abp.DependencyInjection; +using Yi.Framework.Bbs.Domain.Entities.Assignment; +using Yi.Framework.Bbs.Domain.Shared.Enums; namespace Yi.Framework.Bbs.Domain.Managers.AssignmentProviders; /// /// 新手任务提供者 /// - public class NoviceProvider : IAssignmentProvider +[ExposeServices(typeof(IAssignmentProvider))] +public class NoviceProvider : IAssignmentProvider { public async Task> GetCanReceiveListAsync(AssignmentContext context) { + List output = new List(); //新手任务是要有前置依赖关系的,链表类型依赖 - throw new NotImplementedException(); - + //先获取到对应任务定义列表,新手任务 + var assignmentDefines = context.AllAssignmentDefine.Where(x => x.AssignmentType == AssignmentTypeEnum.Novice) + .ToList(); + + //根路径 + var rootAssignmentDefine = assignmentDefines.Where(x => x.PreAssignmentId == null).FirstOrDefault(); + //代表没有定义新手任务 + if (rootAssignmentDefine is null) + { + return output; + } + + //1:查询该用户有正在进行的新手任务,如果有跳过 + if (context.CurrentUserAssignments.Any(x => x.AssignmentState == AssignmentStateEnum.Progress)) + { + return output; + } + + //2: 查询该用户是否有完成的新手任务,如果有,则根据链表选择最后的节点 + var assignmentFilterIds = context.CurrentUserAssignments + .Where(x => + //已经完成的 + x.AssignmentState == AssignmentStateEnum.Completed + ) + .Select(x => x.AssignmentDefineId) + .ToList(); + + //该用户接受的最后一个新手任务 + var lastAssignment = assignmentDefines.Where(x => assignmentFilterIds.Contains(x.Id)) + .OrderByDescending(x => x.OrderNum).First(); + + //包含比该用户还要大的任务 + if (assignmentDefines.Any(x => x.OrderNum > lastAssignment.OrderNum)) + { + output.Add(assignmentDefines.FirstOrDefault(x => x.OrderNum == lastAssignment.OrderNum + 1)); + } + + return output; } } \ No newline at end of file diff --git a/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Application.Contracts/Yi.Framework.Rbac.Application.Contracts.csproj b/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Application.Contracts/Yi.Framework.Rbac.Application.Contracts.csproj index d008ce31..ffc8a0b9 100644 --- a/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Application.Contracts/Yi.Framework.Rbac.Application.Contracts.csproj +++ b/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Application.Contracts/Yi.Framework.Rbac.Application.Contracts.csproj @@ -3,6 +3,7 @@ + diff --git a/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Application/Services/AccountService.cs b/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Application/Services/AccountService.cs index ceb452f1..45e80bcc 100644 --- a/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Application/Services/AccountService.cs +++ b/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Application/Services/AccountService.cs @@ -4,15 +4,19 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Caching.Distributed; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using SqlSugar; using Volo.Abp; using Volo.Abp.Application.Services; using Volo.Abp.Authorization; using Volo.Abp.Caching; +using Volo.Abp.EventBus.Local; using Volo.Abp.Guids; using Volo.Abp.Uow; using Volo.Abp.Users; +using Yi.Framework.Bbs.Domain.Shared.Enums; +using Yi.Framework.Bbs.Domain.Shared.Etos; using Yi.Framework.Rbac.Application.Contracts.Dtos.Account; using Yi.Framework.Rbac.Application.Contracts.IServices; using Yi.Framework.Rbac.Domain.Entities; @@ -26,9 +30,9 @@ using Yi.Framework.SqlSugarCore.Abstractions; namespace Yi.Framework.Rbac.Application.Services { - public class AccountService : ApplicationService, IAccountService { + protected ILocalEventBus LocalEventBus => LazyServiceProvider.LazyGetRequiredService(); private IDistributedCache _phoneCache; private readonly ICaptcha _captcha; private readonly IGuidGenerator _guidGenerator; @@ -36,6 +40,7 @@ namespace Yi.Framework.Rbac.Application.Services private readonly IAliyunManger _aliyunManger; private IDistributedCache _userCache; private UserManager _userManager; + public AccountService(IUserRepository userRepository, ICurrentUser currentUser, IAccountManager accountManager, @@ -66,6 +71,7 @@ namespace Yi.Framework.Rbac.Application.Services private ICurrentUser _currentUser; private IAccountManager _accountManager; private ISqlSugarRepository _menuRepository; + /// /// 校验图片登录验证码,无需和账号绑定 /// @@ -83,7 +89,6 @@ namespace Yi.Framework.Rbac.Application.Services } - /// /// 登录 /// @@ -130,7 +135,6 @@ namespace Yi.Framework.Rbac.Application.Services /// 生成验证码 /// /// - [AllowAnonymous] public async Task GetCaptchaImageAsync() { @@ -150,12 +154,11 @@ namespace Yi.Framework.Rbac.Application.Services { throw new UserFriendlyException("手机号码格式错误!请检查"); } + if (await _userRepository.IsAnyAsync(x => x.Phone.ToString() == str_handset)) { throw new UserFriendlyException("该手机号已被注册!"); - } - } @@ -174,6 +177,7 @@ namespace Yi.Framework.Rbac.Application.Services { throw new UserFriendlyException($"{input.Phone}已发送过验证码,10分钟后可重试"); } + //生成一个4位数的验证码 //发送短信,同时生成uuid ////key: 电话号码 value:验证码+uuid @@ -181,7 +185,8 @@ namespace Yi.Framework.Rbac.Application.Services var uuid = Guid.NewGuid(); await _aliyunManger.SendSmsAsync(input.Phone, code); - await _phoneCache.SetAsync(new CaptchaPhoneCacheKey(input.Phone), new CaptchaPhoneCacheItem(code), new DistributedCacheEntryOptions { SlidingExpiration = TimeSpan.FromMinutes(10) }); + await _phoneCache.SetAsync(new CaptchaPhoneCacheKey(input.Phone), new CaptchaPhoneCacheItem(code), + new DistributedCacheEntryOptions { SlidingExpiration = TimeSpan.FromMinutes(10) }); return new { Uuid = uuid @@ -200,6 +205,7 @@ namespace Yi.Framework.Rbac.Application.Services await _phoneCache.RemoveAsync(new CaptchaPhoneCacheKey(input.Phone.ToString())); return; } + throw new UserFriendlyException("验证码错误"); } @@ -216,11 +222,13 @@ namespace Yi.Framework.Rbac.Application.Services { throw new UserFriendlyException("该系统暂未开放注册功能"); } + if (_rbacOptions.EnableCaptcha) { //校验验证码,根据电话号码获取 value,比对验证码已经uuid await ValidationPhoneCaptchaAsync(input); } + //注册领域逻辑 await _accountManager.RegisterAsync(input.UserName, input.Password, input.Phone); } @@ -232,7 +240,6 @@ namespace Yi.Framework.Rbac.Application.Services /// [Route("account")] [Authorize] - public async Task GetAsync() { //通过鉴权jwt获取到用户的id @@ -241,6 +248,7 @@ namespace Yi.Framework.Rbac.Application.Services { throw new UserFriendlyException("用户未登录"); } + //此处优先从缓存中获取 var output = await _userManager.GetInfoAsync(userId.Value); return output; @@ -259,8 +267,8 @@ namespace Yi.Framework.Rbac.Application.Services if (_currentUser.Id is null) { throw new AbpAuthorizationException("用户未登录"); - } + var data = await _userManager.GetInfoAsync(userId!.Value); var menus = data.Menus.ToList(); @@ -269,8 +277,10 @@ namespace Yi.Framework.Rbac.Application.Services { menus = ObjectMapper.Map, List>(await _menuRepository.GetListAsync()); } + //将后端菜单转换成前端路由,组件级别需要过滤 - List routers = ObjectMapper.Map, List>(menus).Vue3RouterBuild(); + List routers = + ObjectMapper.Map, List>(menus).Vue3RouterBuild(); return routers; } @@ -287,6 +297,7 @@ namespace Yi.Framework.Rbac.Application.Services return false; // throw new UserFriendlyException("用户已退出"); } + await _userCache.RemoveAsync(new UserInfoCacheKey(userId.Value)); //Jwt去中心化登出,只需用记录日志即可 return true; @@ -303,11 +314,14 @@ namespace Yi.Framework.Rbac.Application.Services { throw new UserFriendlyException("无效更新!输入的数据,新密码不能与老密码相同"); } + if (_currentUser.Id is null) { throw new UserFriendlyException("用户未登录"); } - await _accountManager.UpdatePasswordAsync(_currentUser.Id ?? Guid.Empty, input.NewPassword, input.OldPassword); + + await _accountManager.UpdatePasswordAsync(_currentUser.Id ?? Guid.Empty, input.NewPassword, + input.OldPassword); return true; } @@ -324,6 +338,7 @@ namespace Yi.Framework.Rbac.Application.Services { throw new UserFriendlyException("重置密码不能为空!"); } + await _accountManager.RestPasswordAsync(userId, input.Password); return true; } @@ -336,10 +351,18 @@ namespace Yi.Framework.Rbac.Application.Services public async Task UpdateIconAsync(UpdateIconDto input) { var entity = await _userRepository.GetByIdAsync(_currentUser.Id); + if (entity.Icon == input.Icon) + { + return false; + } + entity.Icon = input.Icon; await _userRepository.UpdateAsync(entity); + //发布更新头像任务事件 + await this.LocalEventBus.PublishAsync( + new AssignmentEventArgs(AssignmentRequirementTypeEnum.UpdateIcon, _currentUser.GetId()), false); return true; } } -} +} \ No newline at end of file diff --git a/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Application/Services/System/UserService.cs b/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Application/Services/System/UserService.cs index 6c84a32d..6ef827cf 100644 --- a/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Application/Services/System/UserService.cs +++ b/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Application/Services/System/UserService.cs @@ -6,6 +6,8 @@ using Volo.Abp.Application.Dtos; using Volo.Abp.Caching; using Volo.Abp.EventBus.Local; using Volo.Abp.Users; +using Yi.Framework.Bbs.Domain.Shared.Enums; +using Yi.Framework.Bbs.Domain.Shared.Etos; using Yi.Framework.Ddd.Application; using Yi.Framework.Rbac.Application.Contracts.Dtos.User; using Yi.Framework.Rbac.Application.Contracts.IServices; @@ -24,13 +26,20 @@ namespace Yi.Framework.Rbac.Application.Services.System /// /// User服务实现 /// - public class UserService : YiCrudAppService, IUserService + public class UserService : YiCrudAppService, IUserService //IUserService { - public UserService(ISqlSugarRepository repository, UserManager userManager, IUserRepository userRepository, ICurrentUser currentUser, IDeptService deptService, ILocalEventBus localEventBus, IDistributedCache userCache) : base(repository) + protected ILocalEventBus LocalEventBus => LazyServiceProvider.LazyGetRequiredService(); + + public UserService(ISqlSugarRepository repository, UserManager userManager, + IUserRepository userRepository, ICurrentUser currentUser, IDeptService deptService, + ILocalEventBus localEventBus, + IDistributedCache userCache) : base(repository) => - (_userManager, _userRepository, _currentUser, _deptService, _repository, _localEventBus) = - (userManager, userRepository, currentUser, deptService, repository, localEventBus); + (_userManager, _userRepository, _currentUser, _deptService, _repository, _localEventBus) = + (userManager, userRepository, currentUser, deptService, repository, localEventBus); + private UserManager _userManager { get; set; } private ISqlSugarRepository _repository; private IUserRepository _userRepository { get; set; } @@ -39,6 +48,7 @@ namespace Yi.Framework.Rbac.Application.Services.System private ICurrentUser _currentUser { get; set; } private ILocalEventBus _localEventBus; + /// /// 查询用户 /// @@ -56,22 +66,21 @@ namespace Yi.Framework.Rbac.Application.Services.System List ids = input.Ids?.Split(",").Select(x => Guid.Parse(x)).ToList(); - var outPut = await _repository._DbQueryable.WhereIF(!string.IsNullOrEmpty(input.UserName), x => x.UserName.Contains(input.UserName!)) - .WhereIF(input.Phone is not null, x => x.Phone.ToString()!.Contains(input.Phone.ToString()!)) - .WhereIF(!string.IsNullOrEmpty(input.Name), x => x.Name!.Contains(input.Name!)) - .WhereIF(input.State is not null, x => x.State == input.State) - .WhereIF(input.StartTime is not null && input.EndTime is not null, x => x.CreationTime >= input.StartTime && x.CreationTime <= input.EndTime) + var outPut = await _repository._DbQueryable.WhereIF(!string.IsNullOrEmpty(input.UserName), + x => x.UserName.Contains(input.UserName!)) + .WhereIF(input.Phone is not null, x => x.Phone.ToString()!.Contains(input.Phone.ToString()!)) + .WhereIF(!string.IsNullOrEmpty(input.Name), x => x.Name!.Contains(input.Name!)) + .WhereIF(input.State is not null, x => x.State == input.State) + .WhereIF(input.StartTime is not null && input.EndTime is not null, + x => x.CreationTime >= input.StartTime && x.CreationTime <= input.EndTime) - //这个为过滤当前部门,加入数据权限后,将由数据权限控制 - .WhereIF(input.DeptId is not null, x => deptIds.Contains(x.DeptId ?? Guid.Empty)) - - .WhereIF(ids is not null, x => ids.Contains(x.Id)) - - - .LeftJoin((user, dept) => user.DeptId == dept.Id) - .OrderByDescending(user => user.CreationTime) - .Select((user, dept) => new UserGetListOutputDto(), true) - .ToPageListAsync(input.SkipCount, input.MaxResultCount, total); + //这个为过滤当前部门,加入数据权限后,将由数据权限控制 + .WhereIF(input.DeptId is not null, x => deptIds.Contains(x.DeptId ?? Guid.Empty)) + .WhereIF(ids is not null, x => ids.Contains(x.Id)) + .LeftJoin((user, dept) => user.DeptId == dept.Id) + .OrderByDescending(user => user.CreationTime) + .Select((user, dept) => new UserGetListOutputDto(), true) + .ToPageListAsync(input.SkipCount, input.MaxResultCount, total); var result = new PagedResultDto(); result.Items = outPut; @@ -96,7 +105,6 @@ namespace Yi.Framework.Rbac.Application.Services.System [Permission("system:user:add")] public async override Task CreateAsync(UserCreateInputVo input) { - var entitiy = await MapToEntityAsync(input); await _userManager.CreateAsync(entitiy); @@ -122,7 +130,8 @@ namespace Yi.Framework.Rbac.Application.Services.System public override async Task GetAsync(Guid id) { //使用导航树形查询 - var entity = await _repository._DbQueryable.Includes(u => u.Roles).Includes(u => u.Posts).Includes(u => u.Dept).InSingleAsync(id); + var entity = await _repository._DbQueryable.Includes(u => u.Roles).Includes(u => u.Posts) + .Includes(u => u.Dept).InSingleAsync(id); return await MapToGetOutputDtoAsync(entity); } @@ -141,10 +150,12 @@ namespace Yi.Framework.Rbac.Application.Services.System { throw new UserFriendlyException(UserConst.Name_Not_Allowed); } + if (await _repository.IsAnyAsync(u => input.UserName!.Equals(u.UserName) && !id.Equals(u.Id))) { throw new UserFriendlyException("用户已经存在,更新失败"); } + var entity = await _repository.GetByIdAsync(id); //更新密码,特殊处理 if (input.Password is not null) @@ -152,6 +163,7 @@ namespace Yi.Framework.Rbac.Application.Services.System entity.EncryPassword.Password = input.Password; entity.BuildPassword(); } + await MapToEntityAsync(input, entity); var res1 = await _repository.UpdateAsync(entity); @@ -173,6 +185,14 @@ namespace Yi.Framework.Rbac.Application.Services.System await _repository.UpdateAsync(entity); var dto = await MapToGetOutputDtoAsync(entity); + //发布更新昵称任务事件 + if (input.Nick != entity.Icon) + { + await this.LocalEventBus.PublishAsync( + new AssignmentEventArgs(AssignmentRequirementTypeEnum.UpdateNick, _currentUser.GetId(), input.Nick), + false); + } + return dto; } @@ -192,15 +212,16 @@ namespace Yi.Framework.Rbac.Application.Services.System { throw new ApplicationException("用户未存在"); } + entity.State = state; await _repository.UpdateAsync(entity); return await MapToGetOutputDtoAsync(entity); } + [OperLog("删除用户", OperEnum.Delete)] [Permission("system:user:delete")] public override async Task DeleteAsync(Guid id) { - await base.DeleteAsync(id); } @@ -216,4 +237,4 @@ namespace Yi.Framework.Rbac.Application.Services.System return base.PostImportExcelAsync(input); } } -} +} \ No newline at end of file