diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain.Shared/Consts/AiHubConst.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain.Shared/Consts/AiHubConst.cs new file mode 100644 index 00000000..c1f3c416 --- /dev/null +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain.Shared/Consts/AiHubConst.cs @@ -0,0 +1,6 @@ +namespace Yi.Framework.AiHub.Domain.Shared.Consts; + +public class AiHubConst +{ + public const string VipRole = "YiXinAi-Vip"; +} \ No newline at end of file diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain.Shared/Yi.Framework.AiHub.Domain.Shared.csproj b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain.Shared/Yi.Framework.AiHub.Domain.Shared.csproj index dac23eef..b9a8850e 100644 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain.Shared/Yi.Framework.AiHub.Domain.Shared.csproj +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain.Shared/Yi.Framework.AiHub.Domain.Shared.csproj @@ -6,7 +6,6 @@ - diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Extensions/CurrentExtensions.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Extensions/CurrentExtensions.cs index acab6162..54e152a4 100644 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Extensions/CurrentExtensions.cs +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Extensions/CurrentExtensions.cs @@ -1,5 +1,6 @@ using Microsoft.AspNetCore.Http; using Volo.Abp.Users; +using Yi.Framework.AiHub.Domain.Shared.Consts; namespace Yi.Framework.AiHub.Domain.Extensions; @@ -7,6 +8,6 @@ public static class CurrentExtensions { public static bool IsAiVip(this ICurrentUser currentUser) { - return currentUser.Roles.Contains("YiXinAi-Vip") || currentUser.UserName == "cc"; + return currentUser.Roles.Contains(AiHubConst.VipRole) || currentUser.UserName == "cc"; } } \ 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 new file mode 100644 index 00000000..ee9413fc --- /dev/null +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/AiRechargeManager.cs @@ -0,0 +1,59 @@ +using Microsoft.Extensions.Logging; +using Volo.Abp.Domain.Services; +using Yi.Framework.AiHub.Domain.Entities; +using Yi.Framework.AiHub.Domain.Entities.OpenApi; +using Yi.Framework.AiHub.Domain.Shared.Consts; +using Yi.Framework.Rbac.Application.Contracts.IServices; +using Yi.Framework.SqlSugarCore.Abstractions; + +namespace Yi.Framework.AiHub.Domain.Managers; + +public class AiRechargeManager : DomainService +{ + private readonly ISqlSugarRepository _rechargeRepository; + private readonly IRoleService _roleService; + private readonly ISqlSugarRepository _tokenRepository; + private readonly ILogger _logger; + + public AiRechargeManager(ISqlSugarRepository rechargeRepository, + ISqlSugarRepository tokenRepository, ILogger logger, + IRoleService roleService) + { + _rechargeRepository = rechargeRepository; + _tokenRepository = tokenRepository; + _logger = logger; + _roleService = roleService; + } + + public async Task RemoveVipRoleByExpireAsync() + { + _logger.LogInformation("开始执行VIP过期自动卸载任务"); + + // 获取当前时间 + var currentTime = DateTime.Now; + + // 查找过期的充值记录 + var expiredRecharges = await _rechargeRepository._DbQueryable + .Where(x => x.ExpireDateTime.HasValue && x.ExpireDateTime.Value < currentTime) + .ToListAsync(); + + if (!expiredRecharges.Any()) + { + _logger.LogInformation("没有找到过期的VIP用户"); + return; + } + + // 获取过期用户的ID列表 + var expiredUserIds = expiredRecharges.Select(x => x.UserId).Distinct().ToList(); + _logger.LogInformation($"找到 {expiredUserIds.Count} 个过期的VIP用户"); + + // 获取YiXinAi-Vip角色ID + await _roleService.RemoveUserRoleByRoleCodeAsync(expiredUserIds, AiHubConst.VipRole); + + // 删除过期用户的Token密钥 + var removedTokenCount = await _tokenRepository.DeleteAsync(x => expiredUserIds.Contains(x.UserId)); + + _logger.LogInformation($"成功删除 {removedTokenCount} 个用户的Token密钥"); + _logger.LogInformation($"VIP过期自动卸载任务执行完成,共处理 {expiredUserIds.Count} 个过期用户"); + } +} \ No newline at end of file diff --git a/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Application.Contracts/IServices/IRoleService.cs b/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Application.Contracts/IServices/IRoleService.cs index ac94d752..c3daa732 100644 --- a/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Application.Contracts/IServices/IRoleService.cs +++ b/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Application.Contracts/IServices/IRoleService.cs @@ -9,6 +9,12 @@ namespace Yi.Framework.Rbac.Application.Contracts.IServices /// public interface IRoleService : IYiCrudAppService { - + /// + /// 根据角色名称移除指定用户的角色 + /// + /// + /// + /// + Task RemoveUserRoleByRoleCodeAsync(List userIds, string roleName); } } diff --git a/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Application/Services/System/RoleService.cs b/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Application/Services/System/RoleService.cs index 6188f0ce..643bb786 100644 --- a/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Application/Services/System/RoleService.cs +++ b/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Application/Services/System/RoleService.cs @@ -2,9 +2,7 @@ using Mapster; using Microsoft.AspNetCore.Mvc; using SqlSugar; using Volo.Abp.Application.Dtos; -using Volo.Abp.Application.Services; using Volo.Abp.Domain.Entities; -using Volo.Abp.Uow; using Yi.Framework.Ddd.Application; using Yi.Framework.Rbac.Application.Contracts.Dtos.Role; using Yi.Framework.Rbac.Application.Contracts.Dtos.User; @@ -98,7 +96,8 @@ namespace Yi.Framework.Rbac.Application.Services.System { var entity = await _repository.GetByIdAsync(id); - var isExist = await _repository._DbQueryable.Where(x => x.Id != entity.Id).AnyAsync(x => x.RoleCode == input.RoleCode || x.RoleName == input.RoleName); + var isExist = await _repository._DbQueryable.Where(x => x.Id != entity.Id) + .AnyAsync(x => x.RoleCode == input.RoleCode || x.RoleName == input.RoleName); if (isExist) { throw new UserFriendlyException(RoleConst.Exist); @@ -213,7 +212,18 @@ namespace Yi.Framework.Rbac.Application.Services.System await _userRoleRepository._Db.Deleteable().Where(x => x.RoleId == input.RoleId) .Where(x => input.UserIds.Contains(x.UserId)) .ExecuteCommandAsync(); - ; + } + + /// + /// 根据角色名称移除指定用户的角色 + /// + /// + /// + /// + [RemoteService(isEnabled: false)] + public Task RemoveUserRoleByRoleCodeAsync(List userIds, string roleCode) + { + return _roleManager.RemoveUserRoleByRoleCodeAsync(userIds, roleCode); } } } \ No newline at end of file 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 cd5c2c32..3726eb00 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 @@ -8,10 +8,12 @@ namespace Yi.Framework.Rbac.Domain.Managers { private ISqlSugarRepository _repository; private ISqlSugarRepository _roleMenuRepository; - public RoleManager(ISqlSugarRepository repository, ISqlSugarRepository roleMenuRepository) + private ISqlSugarRepository _userRoleRepository; + public RoleManager(ISqlSugarRepository repository, ISqlSugarRepository roleMenuRepository, ISqlSugarRepository userRoleRepository) { _repository = repository; _roleMenuRepository = roleMenuRepository; + _userRoleRepository = userRoleRepository; } /// @@ -38,5 +40,30 @@ namespace Yi.Framework.Rbac.Domain.Managers } } + + /// + /// 根据角色名称移除指定用户的角色 + /// + /// 用户ID列表 + /// 角色名称 + /// 移除的角色关系数量 + public async Task RemoveUserRoleByRoleCodeAsync(List userIds, string roleName) + { + // 获取角色ID + var role = await _repository._DbQueryable + .Where(x => x.RoleCode == roleName) + .FirstAsync(); + + if (role == null) + { + return 0; + } + + // 移除用户角色关系 + var removedCount = await _userRoleRepository._Db.Deleteable() + .Where(x => userIds.Contains(x.UserId) && x.RoleId == role.Id) + .ExecuteCommandAsync(); + return removedCount; + } } } diff --git a/Yi.Abp.Net8/src/Yi.Abp.Web/Jobs/ai-hub/VipExpireJob.cs b/Yi.Abp.Net8/src/Yi.Abp.Web/Jobs/ai-hub/VipExpireJob.cs new file mode 100644 index 00000000..5576a0d5 --- /dev/null +++ b/Yi.Abp.Net8/src/Yi.Abp.Web/Jobs/ai-hub/VipExpireJob.cs @@ -0,0 +1,31 @@ +using Microsoft.Extensions.Logging; +using SqlSugar; +using Volo.Abp.BackgroundWorkers.Hangfire; +using Yi.Framework.AiHub.Domain.Entities; +using Yi.Framework.AiHub.Domain.Entities.OpenApi; +using Yi.Framework.AiHub.Domain.Managers; +using Yi.Framework.Rbac.Domain.Entities; +using Yi.Framework.SqlSugarCore.Abstractions; + +namespace Yi.Abp.Web.Jobs.ai_hub; + +/// +/// VIP过期自动卸载任务 +/// +public class VipExpireJob : HangfireBackgroundWorkerBase +{ + private readonly AiRechargeManager _aiRechargeManager; + + public VipExpireJob(AiRechargeManager aiRechargeManager) + { + _aiRechargeManager = aiRechargeManager; + RecurringJobId = "VIP过期自动卸载"; + // 每天凌晨0点执行一次 + CronExpression = "0 0 0 * * ?"; + } + + public override async Task DoWorkAsync(CancellationToken cancellationToken = new CancellationToken()) + { + await _aiRechargeManager.RemoveVipRoleByExpireAsync(); + } +} \ No newline at end of file diff --git a/Yi.Abp.Net8/src/Yi.Abp.Web/YiAbpWebModule.cs b/Yi.Abp.Net8/src/Yi.Abp.Web/YiAbpWebModule.cs index d87d54db..26c53a67 100644 --- a/Yi.Abp.Net8/src/Yi.Abp.Web/YiAbpWebModule.cs +++ b/Yi.Abp.Net8/src/Yi.Abp.Web/YiAbpWebModule.cs @@ -77,23 +77,23 @@ namespace Yi.Abp.Web PreConfigure(options => { options.ConventionalControllers.Create(typeof(YiAbpApplicationModule).Assembly, - options => options.RemoteServiceName = "default"); + option => option.RemoteServiceName = "default"); options.ConventionalControllers.Create(typeof(YiFrameworkRbacApplicationModule).Assembly, - options => options.RemoteServiceName = "rbac"); + option => option.RemoteServiceName = "rbac"); options.ConventionalControllers.Create(typeof(YiFrameworkBbsApplicationModule).Assembly, - options => options.RemoteServiceName = "bbs"); + option => option.RemoteServiceName = "bbs"); options.ConventionalControllers.Create(typeof(YiFrameworkChatHubApplicationModule).Assembly, - options => options.RemoteServiceName = "chat-hub"); + option => option.RemoteServiceName = "chat-hub"); options.ConventionalControllers.Create(typeof(YiFrameworkTenantManagementApplicationModule).Assembly, - options => options.RemoteServiceName = "tenant-management"); + option => option.RemoteServiceName = "tenant-management"); options.ConventionalControllers.Create(typeof(YiFrameworkCodeGenApplicationModule).Assembly, - options => options.RemoteServiceName = "code-gen"); + option => option.RemoteServiceName = "code-gen"); options.ConventionalControllers.Create(typeof(YiFrameworkDigitalCollectiblesApplicationModule).Assembly, - options => options.RemoteServiceName = "digital-collectibles"); + option => option.RemoteServiceName = "digital-collectibles"); options.ConventionalControllers.Create(typeof(YiFrameworkStockApplicationModule).Assembly, - options => options.RemoteServiceName = "ai-stock"); + option => option.RemoteServiceName = "ai-stock"); options.ConventionalControllers.Create(typeof(YiFrameworkAiHubApplicationModule).Assembly, - options => options.RemoteServiceName = "ai-hub"); + option => option.RemoteServiceName = "ai-hub"); //统一前缀 options.ConventionalControllers.ConventionalControllerSettings.ForEach(x => x.RootPath = "api/app"); }); @@ -105,12 +105,12 @@ namespace Yi.Abp.Web var host = context.Services.GetHostingEnvironment(); var service = context.Services; - //本地开发环境,禁用作业执行 + //本地开发环境,可以禁用作业执行 if (host.IsDevelopment()) { Configure (options => { - options.IsEnabled = false; + options.IsEnabled = true; }); }