feat: 新增VIP过期自动卸载功能
- 新增`AiRechargeManager`类,实现VIP过期用户的自动卸载逻辑。 - 新增`AiHubConst`常量类,统一管理角色名称。 - 在`IRoleService`中添加`RemoveUserRoleByRoleCodeAsync`方法,用于移除指定用户的角色。 - 在`RoleManager`中实现`RemoveUserRoleByRoleCodeAsync`方法。 - 优化`CurrentExtensions`中VIP角色判断逻辑,使用常量替代硬编码。 - 调整`YiAbpWebModule`中部分代码格式,提升可读性。
This commit is contained in:
@@ -0,0 +1,6 @@
|
|||||||
|
namespace Yi.Framework.AiHub.Domain.Shared.Consts;
|
||||||
|
|
||||||
|
public class AiHubConst
|
||||||
|
{
|
||||||
|
public const string VipRole = "YiXinAi-Vip";
|
||||||
|
}
|
||||||
@@ -6,7 +6,6 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Folder Include="Consts\" />
|
|
||||||
<Folder Include="Etos\" />
|
<Folder Include="Etos\" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Volo.Abp.Users;
|
using Volo.Abp.Users;
|
||||||
|
using Yi.Framework.AiHub.Domain.Shared.Consts;
|
||||||
|
|
||||||
namespace Yi.Framework.AiHub.Domain.Extensions;
|
namespace Yi.Framework.AiHub.Domain.Extensions;
|
||||||
|
|
||||||
@@ -7,6 +8,6 @@ public static class CurrentExtensions
|
|||||||
{
|
{
|
||||||
public static bool IsAiVip(this ICurrentUser currentUser)
|
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";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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<AiRechargeAggregateRoot> _rechargeRepository;
|
||||||
|
private readonly IRoleService _roleService;
|
||||||
|
private readonly ISqlSugarRepository<TokenAggregateRoot> _tokenRepository;
|
||||||
|
private readonly ILogger<AiRechargeManager> _logger;
|
||||||
|
|
||||||
|
public AiRechargeManager(ISqlSugarRepository<AiRechargeAggregateRoot> rechargeRepository,
|
||||||
|
ISqlSugarRepository<TokenAggregateRoot> tokenRepository, ILogger<AiRechargeManager> 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} 个过期用户");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,6 +9,12 @@ namespace Yi.Framework.Rbac.Application.Contracts.IServices
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IRoleService : IYiCrudAppService<RoleGetOutputDto, RoleGetListOutputDto, Guid, RoleGetListInputVo, RoleCreateInputVo, RoleUpdateInputVo>
|
public interface IRoleService : IYiCrudAppService<RoleGetOutputDto, RoleGetListOutputDto, Guid, RoleGetListInputVo, RoleCreateInputVo, RoleUpdateInputVo>
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 根据角色名称移除指定用户的角色
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="userIds"></param>
|
||||||
|
/// <param name="roleName"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task<int> RemoveUserRoleByRoleCodeAsync(List<Guid> userIds, string roleName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,7 @@ using Mapster;
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using SqlSugar;
|
using SqlSugar;
|
||||||
using Volo.Abp.Application.Dtos;
|
using Volo.Abp.Application.Dtos;
|
||||||
using Volo.Abp.Application.Services;
|
|
||||||
using Volo.Abp.Domain.Entities;
|
using Volo.Abp.Domain.Entities;
|
||||||
using Volo.Abp.Uow;
|
|
||||||
using Yi.Framework.Ddd.Application;
|
using Yi.Framework.Ddd.Application;
|
||||||
using Yi.Framework.Rbac.Application.Contracts.Dtos.Role;
|
using Yi.Framework.Rbac.Application.Contracts.Dtos.Role;
|
||||||
using Yi.Framework.Rbac.Application.Contracts.Dtos.User;
|
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 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)
|
if (isExist)
|
||||||
{
|
{
|
||||||
throw new UserFriendlyException(RoleConst.Exist);
|
throw new UserFriendlyException(RoleConst.Exist);
|
||||||
@@ -213,7 +212,18 @@ namespace Yi.Framework.Rbac.Application.Services.System
|
|||||||
await _userRoleRepository._Db.Deleteable<UserRoleEntity>().Where(x => x.RoleId == input.RoleId)
|
await _userRoleRepository._Db.Deleteable<UserRoleEntity>().Where(x => x.RoleId == input.RoleId)
|
||||||
.Where(x => input.UserIds.Contains(x.UserId))
|
.Where(x => input.UserIds.Contains(x.UserId))
|
||||||
.ExecuteCommandAsync();
|
.ExecuteCommandAsync();
|
||||||
;
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 根据角色名称移除指定用户的角色
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="userIds"></param>
|
||||||
|
/// <param name="roleCode"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[RemoteService(isEnabled: false)]
|
||||||
|
public Task<int> RemoveUserRoleByRoleCodeAsync(List<Guid> userIds, string roleCode)
|
||||||
|
{
|
||||||
|
return _roleManager.RemoveUserRoleByRoleCodeAsync(userIds, roleCode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -8,10 +8,12 @@ namespace Yi.Framework.Rbac.Domain.Managers
|
|||||||
{
|
{
|
||||||
private ISqlSugarRepository<RoleAggregateRoot> _repository;
|
private ISqlSugarRepository<RoleAggregateRoot> _repository;
|
||||||
private ISqlSugarRepository<RoleMenuEntity> _roleMenuRepository;
|
private ISqlSugarRepository<RoleMenuEntity> _roleMenuRepository;
|
||||||
public RoleManager(ISqlSugarRepository<RoleAggregateRoot> repository, ISqlSugarRepository<RoleMenuEntity> roleMenuRepository)
|
private ISqlSugarRepository<UserRoleEntity> _userRoleRepository;
|
||||||
|
public RoleManager(ISqlSugarRepository<RoleAggregateRoot> repository, ISqlSugarRepository<RoleMenuEntity> roleMenuRepository, ISqlSugarRepository<UserRoleEntity> userRoleRepository)
|
||||||
{
|
{
|
||||||
_repository = repository;
|
_repository = repository;
|
||||||
_roleMenuRepository = roleMenuRepository;
|
_roleMenuRepository = roleMenuRepository;
|
||||||
|
_userRoleRepository = userRoleRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -38,5 +40,30 @@ namespace Yi.Framework.Rbac.Domain.Managers
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 根据角色名称移除指定用户的角色
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="userIds">用户ID列表</param>
|
||||||
|
/// <param name="roleName">角色名称</param>
|
||||||
|
/// <returns>移除的角色关系数量</returns>
|
||||||
|
public async Task<int> RemoveUserRoleByRoleCodeAsync(List<Guid> 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<UserRoleEntity>()
|
||||||
|
.Where(x => userIds.Contains(x.UserId) && x.RoleId == role.Id)
|
||||||
|
.ExecuteCommandAsync();
|
||||||
|
return removedCount;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
31
Yi.Abp.Net8/src/Yi.Abp.Web/Jobs/ai-hub/VipExpireJob.cs
Normal file
31
Yi.Abp.Net8/src/Yi.Abp.Web/Jobs/ai-hub/VipExpireJob.cs
Normal file
@@ -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;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// VIP过期自动卸载任务
|
||||||
|
/// </summary>
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -77,23 +77,23 @@ namespace Yi.Abp.Web
|
|||||||
PreConfigure<AbpAspNetCoreMvcOptions>(options =>
|
PreConfigure<AbpAspNetCoreMvcOptions>(options =>
|
||||||
{
|
{
|
||||||
options.ConventionalControllers.Create(typeof(YiAbpApplicationModule).Assembly,
|
options.ConventionalControllers.Create(typeof(YiAbpApplicationModule).Assembly,
|
||||||
options => options.RemoteServiceName = "default");
|
option => option.RemoteServiceName = "default");
|
||||||
options.ConventionalControllers.Create(typeof(YiFrameworkRbacApplicationModule).Assembly,
|
options.ConventionalControllers.Create(typeof(YiFrameworkRbacApplicationModule).Assembly,
|
||||||
options => options.RemoteServiceName = "rbac");
|
option => option.RemoteServiceName = "rbac");
|
||||||
options.ConventionalControllers.Create(typeof(YiFrameworkBbsApplicationModule).Assembly,
|
options.ConventionalControllers.Create(typeof(YiFrameworkBbsApplicationModule).Assembly,
|
||||||
options => options.RemoteServiceName = "bbs");
|
option => option.RemoteServiceName = "bbs");
|
||||||
options.ConventionalControllers.Create(typeof(YiFrameworkChatHubApplicationModule).Assembly,
|
options.ConventionalControllers.Create(typeof(YiFrameworkChatHubApplicationModule).Assembly,
|
||||||
options => options.RemoteServiceName = "chat-hub");
|
option => option.RemoteServiceName = "chat-hub");
|
||||||
options.ConventionalControllers.Create(typeof(YiFrameworkTenantManagementApplicationModule).Assembly,
|
options.ConventionalControllers.Create(typeof(YiFrameworkTenantManagementApplicationModule).Assembly,
|
||||||
options => options.RemoteServiceName = "tenant-management");
|
option => option.RemoteServiceName = "tenant-management");
|
||||||
options.ConventionalControllers.Create(typeof(YiFrameworkCodeGenApplicationModule).Assembly,
|
options.ConventionalControllers.Create(typeof(YiFrameworkCodeGenApplicationModule).Assembly,
|
||||||
options => options.RemoteServiceName = "code-gen");
|
option => option.RemoteServiceName = "code-gen");
|
||||||
options.ConventionalControllers.Create(typeof(YiFrameworkDigitalCollectiblesApplicationModule).Assembly,
|
options.ConventionalControllers.Create(typeof(YiFrameworkDigitalCollectiblesApplicationModule).Assembly,
|
||||||
options => options.RemoteServiceName = "digital-collectibles");
|
option => option.RemoteServiceName = "digital-collectibles");
|
||||||
options.ConventionalControllers.Create(typeof(YiFrameworkStockApplicationModule).Assembly,
|
options.ConventionalControllers.Create(typeof(YiFrameworkStockApplicationModule).Assembly,
|
||||||
options => options.RemoteServiceName = "ai-stock");
|
option => option.RemoteServiceName = "ai-stock");
|
||||||
options.ConventionalControllers.Create(typeof(YiFrameworkAiHubApplicationModule).Assembly,
|
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");
|
options.ConventionalControllers.ConventionalControllerSettings.ForEach(x => x.RootPath = "api/app");
|
||||||
});
|
});
|
||||||
@@ -105,12 +105,12 @@ namespace Yi.Abp.Web
|
|||||||
var host = context.Services.GetHostingEnvironment();
|
var host = context.Services.GetHostingEnvironment();
|
||||||
var service = context.Services;
|
var service = context.Services;
|
||||||
|
|
||||||
//本地开发环境,禁用作业执行
|
//本地开发环境,可以禁用作业执行
|
||||||
if (host.IsDevelopment())
|
if (host.IsDevelopment())
|
||||||
{
|
{
|
||||||
Configure<AbpBackgroundWorkerOptions> (options =>
|
Configure<AbpBackgroundWorkerOptions> (options =>
|
||||||
{
|
{
|
||||||
options.IsEnabled = false;
|
options.IsEnabled = true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user