diff --git a/aspnet-core/modules/account/LINGYUN.Abp.Account.Application.Contracts/LINGYUN/Abp/Account/Dto/ChangePictureInput.cs b/aspnet-core/modules/account/LINGYUN.Abp.Account.Application.Contracts/LINGYUN/Abp/Account/Dto/ChangePictureInput.cs new file mode 100644 index 000000000..5c4d0314d --- /dev/null +++ b/aspnet-core/modules/account/LINGYUN.Abp.Account.Application.Contracts/LINGYUN/Abp/Account/Dto/ChangePictureInput.cs @@ -0,0 +1,12 @@ +using System.ComponentModel.DataAnnotations; +using Volo.Abp.Auditing; +using Volo.Abp.Content; + +namespace LINGYUN.Abp.Account; + +public class ChangePictureInput +{ + [Required] + [DisableAuditing] + public IRemoteStreamContent File { get; set; } +} diff --git a/aspnet-core/modules/account/LINGYUN.Abp.Account.Application.Contracts/LINGYUN/Abp/Account/IAccountAppService.cs b/aspnet-core/modules/account/LINGYUN.Abp.Account.Application.Contracts/LINGYUN/Abp/Account/IAccountAppService.cs index 2ebe2b1ea..be0514b46 100644 --- a/aspnet-core/modules/account/LINGYUN.Abp.Account.Application.Contracts/LINGYUN/Abp/Account/IAccountAppService.cs +++ b/aspnet-core/modules/account/LINGYUN.Abp.Account.Application.Contracts/LINGYUN/Abp/Account/IAccountAppService.cs @@ -1,58 +1,58 @@ -using System.Threading.Tasks; +using System.Threading.Tasks; using Volo.Abp; using Volo.Abp.Application.Dtos; -using Volo.Abp.Application.Services; - -namespace LINGYUN.Abp.Account; - -public interface IAccountAppService : IApplicationService -{ - /// - /// 通过手机号注册用户账户 - /// - /// - /// - Task RegisterAsync(PhoneRegisterDto input); - /// - /// 通过微信小程序注册用户账户 - /// - /// - /// - Task RegisterAsync(WeChatRegisterDto input); - /// - /// 通过手机号重置用户密码 - /// - /// - /// - Task ResetPasswordAsync(PhoneResetPasswordDto input); - /// - /// 发送手机注册验证码短信 - /// - /// - /// - Task SendPhoneRegisterCodeAsync(SendPhoneRegisterCodeDto input); - /// - /// 发送手机登录验证码短信 - /// - /// - /// - Task SendPhoneSigninCodeAsync(SendPhoneSigninCodeDto input); +using Volo.Abp.Application.Services; + +namespace LINGYUN.Abp.Account; + +public interface IAccountAppService : IApplicationService +{ + /// + /// 通过手机号注册用户账户 + /// + /// + /// + Task RegisterAsync(PhoneRegisterDto input); + /// + /// 通过微信小程序注册用户账户 + /// + /// + /// + Task RegisterAsync(WeChatRegisterDto input); + /// + /// 通过手机号重置用户密码 + /// + /// + /// + Task ResetPasswordAsync(PhoneResetPasswordDto input); + /// + /// 发送手机注册验证码短信 + /// + /// + /// + Task SendPhoneRegisterCodeAsync(SendPhoneRegisterCodeDto input); + /// + /// 发送手机登录验证码短信 + /// + /// + /// + Task SendPhoneSigninCodeAsync(SendPhoneSigninCodeDto input); /// /// 发送邮件登录验证码 /// /// - /// - Task SendEmailSigninCodeAsync(SendEmailSigninCodeDto input); - /// - /// 发送手机重置密码验证码短信 - /// - /// - /// - Task SendPhoneResetPasswordCodeAsync(SendPhoneResetPasswordCodeDto input); + /// + Task SendEmailSigninCodeAsync(SendEmailSigninCodeDto input); + /// + /// 发送手机重置密码验证码短信 + /// + /// + /// + Task SendPhoneResetPasswordCodeAsync(SendPhoneResetPasswordCodeDto input); /// /// 获取用户二次认证提供者列表 /// /// - /// - Task> GetTwoFactorProvidersAsync(GetTwoFactorProvidersInput input); -} + /// + Task> GetTwoFactorProvidersAsync(GetTwoFactorProvidersInput input); +} diff --git a/aspnet-core/modules/account/LINGYUN.Abp.Account.Application.Contracts/LINGYUN/Abp/Account/IMyClaimAppService.cs b/aspnet-core/modules/account/LINGYUN.Abp.Account.Application.Contracts/LINGYUN/Abp/Account/IMyClaimAppService.cs index 5706195b3..d4efa8a6d 100644 --- a/aspnet-core/modules/account/LINGYUN.Abp.Account.Application.Contracts/LINGYUN/Abp/Account/IMyClaimAppService.cs +++ b/aspnet-core/modules/account/LINGYUN.Abp.Account.Application.Contracts/LINGYUN/Abp/Account/IMyClaimAppService.cs @@ -1,4 +1,5 @@ -using System.Threading.Tasks; +using System; +using System.Threading.Tasks; using Volo.Abp.Application.Services; namespace LINGYUN.Abp.Account; @@ -10,6 +11,7 @@ public interface IMyClaimAppService : IApplicationService /// /// /// + [Obsolete("请使用 IMyProfileAppService.ChangePictureAsync")] Task ChangeAvatarAsync(ChangeAvatarInput input); /// /// 查询绑定状态 diff --git a/aspnet-core/modules/account/LINGYUN.Abp.Account.Application.Contracts/LINGYUN/Abp/Account/IMyProfileAppService.cs b/aspnet-core/modules/account/LINGYUN.Abp.Account.Application.Contracts/LINGYUN/Abp/Account/IMyProfileAppService.cs index 430090099..05f39b352 100644 --- a/aspnet-core/modules/account/LINGYUN.Abp.Account.Application.Contracts/LINGYUN/Abp/Account/IMyProfileAppService.cs +++ b/aspnet-core/modules/account/LINGYUN.Abp.Account.Application.Contracts/LINGYUN/Abp/Account/IMyProfileAppService.cs @@ -1,7 +1,9 @@ using LINGYUN.Abp.Identity; +using System; using System.Threading.Tasks; using Volo.Abp.Application.Dtos; using Volo.Abp.Application.Services; +using Volo.Abp.Content; namespace LINGYUN.Abp.Account; @@ -73,4 +75,14 @@ public interface IMyProfileAppService : IApplicationService /// /// Task ConfirmEmailAsync(ConfirmEmailInput input); + /// + /// 变更用户头像 + /// + /// + Task ChangePictureAsync(ChangePictureInput input); + /// + /// 获取用户头像 + /// + /// + Task GetPictureAsync(); } diff --git a/aspnet-core/modules/account/LINGYUN.Abp.Account.Application/LINGYUN.Abp.Account.Application.csproj b/aspnet-core/modules/account/LINGYUN.Abp.Account.Application/LINGYUN.Abp.Account.Application.csproj index 1ada339df..eda58b75f 100644 --- a/aspnet-core/modules/account/LINGYUN.Abp.Account.Application/LINGYUN.Abp.Account.Application.csproj +++ b/aspnet-core/modules/account/LINGYUN.Abp.Account.Application/LINGYUN.Abp.Account.Application.csproj @@ -15,6 +15,7 @@ + diff --git a/aspnet-core/modules/account/LINGYUN.Abp.Account.Application/LINGYUN/Abp/Account/AbpAccountApplicationModule.cs b/aspnet-core/modules/account/LINGYUN.Abp.Account.Application/LINGYUN/Abp/Account/AbpAccountApplicationModule.cs index 5544d58be..f5590e8f8 100644 --- a/aspnet-core/modules/account/LINGYUN.Abp.Account.Application/LINGYUN/Abp/Account/AbpAccountApplicationModule.cs +++ b/aspnet-core/modules/account/LINGYUN.Abp.Account.Application/LINGYUN/Abp/Account/AbpAccountApplicationModule.cs @@ -5,6 +5,7 @@ using LINGYUN.Abp.WeChat.MiniProgram; using Microsoft.Extensions.DependencyInjection; using Volo.Abp.Account.Localization; using Volo.Abp.AutoMapper; +using Volo.Abp.BlobStoring; using Volo.Abp.Localization; using Volo.Abp.Modularity; using Volo.Abp.UI.Navigation.Urls; @@ -17,6 +18,7 @@ namespace LINGYUN.Abp.Account; typeof(AbpAccountApplicationContractsModule), typeof(AbpAccountEmailingModule), typeof(AbpIdentityDomainModule), + typeof(AbpBlobStoringModule), typeof(AbpWeChatMiniProgramModule))] public class AbpAccountApplicationModule : AbpModule { diff --git a/aspnet-core/modules/account/LINGYUN.Abp.Account.Application/LINGYUN/Abp/Account/AccountContainer.cs b/aspnet-core/modules/account/LINGYUN.Abp.Account.Application/LINGYUN/Abp/Account/AccountContainer.cs new file mode 100644 index 000000000..bb5295045 --- /dev/null +++ b/aspnet-core/modules/account/LINGYUN.Abp.Account.Application/LINGYUN/Abp/Account/AccountContainer.cs @@ -0,0 +1,8 @@ +using Volo.Abp.BlobStoring; + +namespace LINGYUN.Abp.Account; + +[BlobContainerName("users")] +public class AccountContainer +{ +} diff --git a/aspnet-core/modules/account/LINGYUN.Abp.Account.Application/LINGYUN/Abp/Account/MyClaimAppService.cs b/aspnet-core/modules/account/LINGYUN.Abp.Account.Application/LINGYUN/Abp/Account/MyClaimAppService.cs index 3b040a4d4..21791db40 100644 --- a/aspnet-core/modules/account/LINGYUN.Abp.Account.Application/LINGYUN/Abp/Account/MyClaimAppService.cs +++ b/aspnet-core/modules/account/LINGYUN.Abp.Account.Application/LINGYUN/Abp/Account/MyClaimAppService.cs @@ -1,6 +1,7 @@ using LINGYUN.Abp.Identity; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Identity; +using System; using System.Collections.Generic; using System.Linq; using System.Security.Claims; diff --git a/aspnet-core/modules/account/LINGYUN.Abp.Account.Application/LINGYUN/Abp/Account/MyProfileAppService.cs b/aspnet-core/modules/account/LINGYUN.Abp.Account.Application/LINGYUN/Abp/Account/MyProfileAppService.cs index 42790e5b0..ab1712f4a 100644 --- a/aspnet-core/modules/account/LINGYUN.Abp.Account.Application/LINGYUN/Abp/Account/MyProfileAppService.cs +++ b/aspnet-core/modules/account/LINGYUN.Abp.Account.Application/LINGYUN/Abp/Account/MyProfileAppService.cs @@ -7,17 +7,23 @@ using Microsoft.AspNetCore.Identity; using Microsoft.Extensions.Caching.Distributed; using Microsoft.Extensions.Options; using System; +using System.Collections.Generic; +using System.IO; using System.Linq; using System.Net; +using System.Security.Claims; using System.Text; using System.Threading.Tasks; using System.Web; using Volo.Abp; using Volo.Abp.Account.Localization; using Volo.Abp.Application.Dtos; +using Volo.Abp.BlobStoring; using Volo.Abp.Caching; +using Volo.Abp.Content; using Volo.Abp.Data; using Volo.Abp.Identity; +using Volo.Abp.Security.Claims; using Volo.Abp.Settings; using Volo.Abp.Users; using IIdentitySessionRepository = LINGYUN.Abp.Identity.IIdentitySessionRepository; @@ -36,6 +42,8 @@ public class MyProfileAppService : AccountApplicationServiceBase, IMyProfileAppS protected IIdentitySessionManager IdentitySessionManager => LazyServiceProvider.LazyGetRequiredService(); protected IIdentitySessionRepository IdentitySessionRepository => LazyServiceProvider.LazyGetRequiredService(); + protected IBlobContainer AccountBlobContainer => LazyServiceProvider.LazyGetRequiredService>(); + public MyProfileAppService( Identity.IIdentityUserRepository userRepository, IAccountSmsSecurityCodeSender securityCodeSender, @@ -50,6 +58,66 @@ public class MyProfileAppService : AccountApplicationServiceBase, IMyProfileAppS LocalizationResource = typeof(AccountResource); } + public async virtual Task ChangePictureAsync(ChangePictureInput input) + { + var user = await GetCurrentUserAsync(); + var pictureId = input.File.FileName ?? $"{GuidGenerator.Create():n}.jpg"; + + var avatarClaims = user.Claims.Where(x => x.ClaimType.StartsWith(AbpClaimTypes.Picture)) + .Select(x => x.ToClaim()) + .Skip(0) + .Take(3) + .ToList(); + if (avatarClaims.Any()) + { + // 保留最多3个头像 + if (avatarClaims.Count >= 3) + { + user.RemoveClaim(avatarClaims.First()); + avatarClaims.RemoveAt(0); + } + + // 历史头像加数字标识 + for (var index = 1; index <= avatarClaims.Count; index++) + { + var avatarClaim = avatarClaims[index - 1]; + var findClaim = user.FindClaim(avatarClaim); + if (findClaim != null) + { + findClaim.SetClaim(new Claim( + AbpClaimTypes.Picture + index.ToString(), + findClaim.ClaimValue)); + } + } + } + + user.AddClaim(GuidGenerator, new Claim(AbpClaimTypes.Picture, pictureId)); + + (await UserManager.UpdateAsync(user)).CheckErrors(); + + var pictureName = $"{user.Id:n}/avatar/{pictureId}"; + + await AccountBlobContainer.SaveAsync(pictureName, input.File.GetStream(), true); + + await CurrentUnitOfWork.SaveChangesAsync(); + } + + public async virtual Task GetPictureAsync() + { + var currentUser = await GetCurrentUserAsync(); + var pictureCalim = currentUser.Claims.FirstOrDefault(x => x.ClaimType == AbpClaimTypes.Picture); + if (pictureCalim?.ClaimValue.IsNullOrWhiteSpace() == true) + { + return new RemoteStreamContent(Stream.Null); + } + + var pictureName = $"{CurrentUser.GetId():n}/avatar/{pictureCalim.ClaimValue}"; + + var stream = await AccountBlobContainer.GetOrNullAsync(pictureName); + + return new RemoteStreamContent(stream, contentType: "image/jpeg"); + } + public async virtual Task> GetSessionsAsync(GetMySessionsInput input) { var user = await GetCurrentUserAsync(); diff --git a/aspnet-core/modules/account/LINGYUN.Abp.Account.HttpApi/LINGYUN/Abp/Account/MyProfileController.cs b/aspnet-core/modules/account/LINGYUN.Abp.Account.HttpApi/LINGYUN/Abp/Account/MyProfileController.cs index 90da8191f..7c261bf2b 100644 --- a/aspnet-core/modules/account/LINGYUN.Abp.Account.HttpApi/LINGYUN/Abp/Account/MyProfileController.cs +++ b/aspnet-core/modules/account/LINGYUN.Abp.Account.HttpApi/LINGYUN/Abp/Account/MyProfileController.cs @@ -7,6 +7,7 @@ using Volo.Abp; using Volo.Abp.Account; using Volo.Abp.Application.Dtos; using Volo.Abp.AspNetCore.Mvc; +using Volo.Abp.Content; namespace LINGYUN.Abp.Account; @@ -101,4 +102,18 @@ public class MyProfileController : AbpControllerBase, IMyProfileAppService { return MyProfileAppService.ResetAuthenticatorAsync(); } + + [HttpPost] + [Route("picture")] + public virtual Task ChangePictureAsync([FromForm] ChangePictureInput input) + { + return MyProfileAppService.ChangePictureAsync(input); + } + + [HttpGet] + [Route("picture")] + public virtual Task GetPictureAsync() + { + return MyProfileAppService.GetPictureAsync(); + } }