From 7038d31c538aa18d2c09fcf7733214c0086b63e1 Mon Sep 17 00:00:00 2001 From: ccnetcore Date: Sun, 10 Aug 2025 11:53:28 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9EVIP=E5=85=85=E5=80=BC?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E5=B9=B6=E6=94=AF=E6=8C=81=E9=80=9A=E8=BF=87?= =?UTF-8?q?=E8=A7=92=E8=89=B2=E4=BB=A3=E7=A0=81=E4=B8=BA=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E5=88=86=E9=85=8D=E8=A7=92=E8=89=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Dtos/Recharge/RechargeCreateInput.cs | 43 ++++++++++ .../Services/RechargeService.cs | 86 +++++++++++++------ .../Managers/AiRechargeManager.cs | 33 +++++-- .../IServices/IUserService.cs | 7 ++ .../Services/System/UserService.cs | 24 ++++++ .../Managers/FileManager.cs | 5 +- .../Managers/RoleManager.cs | 11 +-- .../Managers/UserManager.cs | 30 +++---- 8 files changed, 181 insertions(+), 58 deletions(-) create mode 100644 Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Recharge/RechargeCreateInput.cs diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Recharge/RechargeCreateInput.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Recharge/RechargeCreateInput.cs new file mode 100644 index 00000000..a8354fb7 --- /dev/null +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Recharge/RechargeCreateInput.cs @@ -0,0 +1,43 @@ +using System.ComponentModel.DataAnnotations; + +namespace Yi.Framework.AiHub.Application.Contracts.Dtos.Recharge; + +public class RechargeCreateInput +{ + /// + /// 用户ID + /// + [Required] + public Guid UserId { get; set; } + + /// + /// 充值金额 + /// + [Required] + [Range(0.01, double.MaxValue, ErrorMessage = "充值金额必须大于0")] + public decimal RechargeAmount { get; set; } + + /// + /// 充值内容 + /// + [Required] + [StringLength(500, ErrorMessage = "充值内容不能超过500个字符")] + public string Content { get; set; } = string.Empty; + + /// + /// 到期时间(为空表示永久VIP) + /// + public DateTime? ExpireDateTime { get; set; } + + /// + /// 备注 + /// + [StringLength(1000, ErrorMessage = "备注不能超过1000个字符")] + public string? Remark { get; set; } + + /// + /// 联系方式 + /// + [StringLength(200, ErrorMessage = "联系方式不能超过200个字符")] + public string? ContactInfo { get; set; } +} \ No newline at end of file diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/RechargeService.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/RechargeService.cs index 5cd8247b..913ae912 100644 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/RechargeService.cs +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/RechargeService.cs @@ -1,41 +1,73 @@ using Mapster; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.DependencyInjection; using Volo.Abp.Application.Services; +using Volo.Abp.Users; using Yi.Framework.AiHub.Application.Contracts.Dtos.Recharge; using Yi.Framework.AiHub.Domain.Entities; +using Yi.Framework.AiHub.Domain.Shared.Consts; using Yi.Framework.Rbac.Application.Contracts.IServices; -using Yi.Framework.Rbac.Domain.Shared.Dtos; using Yi.Framework.SqlSugarCore.Abstractions; -namespace Yi.Framework.AiHub.Application.Services; - -/// -/// ai 充值表 -/// -public class RechargeService : ApplicationService +namespace Yi.Framework.AiHub.Application.Services { - private readonly ISqlSugarRepository _repository; - - public RechargeService(ISqlSugarRepository repository) + public class RechargeService : ApplicationService { - _repository = repository; - } + private readonly ISqlSugarRepository _repository; + private readonly ICurrentUser _currentUser; + private readonly IUserService _userService; - /// - /// 查询已登录的账户充值记录 - /// - /// - [Route("recharge/account")] - [Authorize] - public async Task> GetListByAccountAsync() - { - var userId = CurrentUser.Id; - var entities = await _repository._DbQueryable.Where(x => x.UserId == userId) - .OrderByDescending(x => x.CreationTime) - .ToListAsync(); - var output = entities.Adapt>(); - return output; + public RechargeService( + ISqlSugarRepository repository, + ICurrentUser currentUser, + IUserService userService) + { + _repository = repository; + _currentUser = currentUser; + _userService = userService; + } + + /// + /// 查询已登录的账户充值记录 + /// + /// + [Route("recharge/account")] + [Authorize] + public async Task> GetListByAccountAsync() + { + var userId = CurrentUser.Id; + var entities = await _repository._DbQueryable.Where(x => x.UserId == userId) + .OrderByDescending(x => x.CreationTime) + .ToListAsync(); + var output = entities.Adapt>(); + return output; + } + + /// + /// 给用户充值VIP + /// + /// 充值输入参数 + /// + [HttpPost("recharge/vip")] + public async Task RechargeVipAsync(RechargeCreateInput input) + { + // 创建充值记录 + var rechargeRecord = new AiRechargeAggregateRoot + { + UserId = input.UserId, + RechargeAmount = input.RechargeAmount, + Content = input.Content, + ExpireDateTime = input.ExpireDateTime, + Remark = input.Remark, + ContactInfo = input.ContactInfo + }; + + // 保存充值记录到数据库 + await _repository.InsertAsync(rechargeRecord); + + // 使用UserService给用户添加VIP角色 + await _userService.AddUserRoleByRoleCodeAsync(input.UserId, + new List() { AiHubConst.VipRole, "default" }); + } } } \ No newline at end of file diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/AiRechargeManager.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/AiRechargeManager.cs index ee9413fc..7f8a4fdc 100644 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/AiRechargeManager.cs +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/AiRechargeManager.cs @@ -32,19 +32,40 @@ public class AiRechargeManager : DomainService // 获取当前时间 var currentTime = DateTime.Now; - // 查找过期的充值记录 - var expiredRecharges = await _rechargeRepository._DbQueryable - .Where(x => x.ExpireDateTime.HasValue && x.ExpireDateTime.Value < currentTime) + // 查找所有充值记录,按用户分组 + var allRecharges = await _rechargeRepository._DbQueryable .ToListAsync(); - if (!expiredRecharges.Any()) + if (!allRecharges.Any()) + { + _logger.LogInformation("没有找到任何充值记录"); + return; + } + + // 按用户分组,找出真正过期的用户 + var expiredUserIds = allRecharges + .GroupBy(x => x.UserId) + .Where(group => + { + // 如果用户有任何一个过期时间为空的记录,说明是永久VIP,不过期 + if (group.Any(x => !x.ExpireDateTime.HasValue)) + return false; + + // 找到用户最大的过期时间 + var maxExpireTime = group.Max(x => x.ExpireDateTime); + + // 如果最大过期时间小于当前时间,说明用户已过期 + return maxExpireTime.HasValue && maxExpireTime.Value < currentTime; + }) + .Select(group => group.Key) + .ToList(); + + if (!expiredUserIds.Any()) { _logger.LogInformation("没有找到过期的VIP用户"); return; } - // 获取过期用户的ID列表 - var expiredUserIds = expiredRecharges.Select(x => x.UserId).Distinct().ToList(); _logger.LogInformation($"找到 {expiredUserIds.Count} 个过期的VIP用户"); // 获取YiXinAi-Vip角色ID diff --git a/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Application.Contracts/IServices/IUserService.cs b/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Application.Contracts/IServices/IUserService.cs index 343765e4..315fdb13 100644 --- a/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Application.Contracts/IServices/IUserService.cs +++ b/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Application.Contracts/IServices/IUserService.cs @@ -8,5 +8,12 @@ namespace Yi.Framework.Rbac.Application.Contracts.IServices /// public interface IUserService : IYiCrudAppService { + /// + /// 通过角色代码给用户添加角色 + /// + /// 用户ID + /// 角色代码 + /// + Task AddUserRoleByRoleCodeAsync(Guid userId, List roleCodes); } } 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 33d97ddf..a4f5cc55 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 @@ -241,5 +241,29 @@ namespace Yi.Framework.Rbac.Application.Services.System { return base.PostImportExcelAsync(input); } + + /// + /// 通过角色代码给用户添加角色 + /// + /// 用户ID + /// + /// + public async Task AddUserRoleByRoleCodeAsync(Guid userId, List roleCodes) + { + // 根据角色代码查找角色ID + var roleRepository = LazyServiceProvider.LazyGetRequiredService>(); + var roleIds = await roleRepository._DbQueryable + .Where(r => roleCodes.Contains(r.RoleCode) && r.State == true) + .Select(r=>r.Id) + .ToListAsync(); + + if (!roleIds.Any()) + { + return; + } + + // 使用UserManager给用户设置角色 + await _userManager.GiveUserSetRoleAsync(new List { userId }, roleIds); + } } } \ No newline at end of file diff --git a/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Domain/Managers/FileManager.cs b/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Domain/Managers/FileManager.cs index d82112d5..40f542cd 100644 --- a/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Domain/Managers/FileManager.cs +++ b/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Domain/Managers/FileManager.cs @@ -32,14 +32,15 @@ public class FileManager : DomainService, IFileManager /// public async Task> CreateAsync(IEnumerable files) { - if (files.Count() == 0) + var formFiles = files as IFormFile[] ?? files.ToArray(); + if (!formFiles.Any()) { throw new ArgumentException("文件上传为空!"); } //批量插入 List entities = new(); - foreach (var file in files) + foreach (var file in formFiles) { FileAggregateRoot data = new(_guidGenerator.Create(), file.FileName, (decimal)file.Length / 1024); data.CheckDirectoryOrCreate(); diff --git a/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Domain/Managers/RoleManager.cs b/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Domain/Managers/RoleManager.cs index 3726eb00..9df14100 100644 --- a/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Domain/Managers/RoleManager.cs +++ b/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Domain/Managers/RoleManager.cs @@ -26,19 +26,20 @@ namespace Yi.Framework.Rbac.Domain.Managers { //这个是需要事务的,在service中进行工作单元 await _roleMenuRepository.DeleteAsync(u => roleIds.Contains(u.RoleId)); + //添加新的关系 + List roleMenuEntity = new(); //遍历用户 foreach (var roleId in roleIds) { - //添加新的关系 - List roleMenuEntity = new(); + foreach (var menu in menuIds) { roleMenuEntity.Add(new RoleMenuEntity() { RoleId = roleId, MenuId = menu }); } - //一次性批量添加 - await _roleMenuRepository.InsertRangeAsync(roleMenuEntity); + } - + //一次性批量添加 + await _roleMenuRepository.InsertRangeAsync(roleMenuEntity); } /// diff --git a/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Domain/Managers/UserManager.cs b/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Domain/Managers/UserManager.cs index 46ace5d8..ac8a4c87 100644 --- a/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Domain/Managers/UserManager.cs +++ b/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Domain/Managers/UserManager.cs @@ -46,27 +46,25 @@ namespace Yi.Framework.Rbac.Domain.Managers /// /// /// - public async Task GiveUserSetRoleAsync(List userIds, List roleIds) + public async Task GiveUserSetRoleAsync(List userIds, List? roleIds) { - //删除用户之前所有的用户角色关系(物理删除,没有恢复的必要) + //删除用户之前所有的用户角色关系 await _repositoryUserRole.DeleteAsync(u => userIds.Contains(u.UserId)); if (roleIds is not null) { + //添加新的关系 + List userRoleEntities = new(); //遍历用户 foreach (var userId in userIds) { - //添加新的关系 - List userRoleEntities = new(); - foreach (var roleId in roleIds) { userRoleEntities.Add(new UserRoleEntity() { UserId = userId, RoleId = roleId }); } - - //一次性批量添加 - await _repositoryUserRole.InsertRangeAsync(userRoleEntities); } + //一次性批量添加 + await _repositoryUserRole.InsertRangeAsync(userRoleEntities); } } @@ -77,25 +75,24 @@ namespace Yi.Framework.Rbac.Domain.Managers /// /// /// - public async Task GiveUserSetPostAsync(List userIds, List postIds) + public async Task GiveUserSetPostAsync(List userIds, List? postIds) { //删除用户之前所有的用户角色关系(物理删除,没有恢复的必要) await _repositoryUserPost.DeleteAsync(u => userIds.Contains(u.UserId)); if (postIds is not null) { + //添加新的关系 + List userPostEntities = new(); //遍历用户 foreach (var userId in userIds) { - //添加新的关系 - List userPostEntities = new(); foreach (var post in postIds) { userPostEntities.Add(new UserPostEntity() { UserId = userId, PostId = post }); } - - //一次性批量添加 - await _repositoryUserPost.InsertRangeAsync(userPostEntities); } + //一次性批量添加 + await _repositoryUserPost.InsertRangeAsync(userPostEntities); } } @@ -137,10 +134,7 @@ namespace Yi.Framework.Rbac.Domain.Managers public async Task SetDefautRoleAsync(Guid userId) { var role = await _roleRepository.GetFirstAsync(x => x.RoleCode == UserConst.DefaultRoleCode); - if (role is not null) - { - await GiveUserSetRoleAsync(new List { userId }, new List { role.Id }); - } + await GiveUserSetRoleAsync(new List { userId }, new List { role.Id }); } private void ValidateUserName(UserAggregateRoot input)