fix(tenant): 新增爬虫过期时间字段并重构定时任务

- TenantDO、Req/Resp VO 增加 crawlExpireTime 字段
- 禁用任务改为仅依据 crawlExpireTime 判断过期
- 支持批量更新租户下所有爬虫用户状态
This commit is contained in:
2026-02-04 15:32:18 +08:00
parent a685e4b5cb
commit eb936ce677
7 changed files with 89 additions and 45 deletions

View File

@@ -45,6 +45,10 @@ public class TenantPageReqVO extends PageParam {
@Schema(description = "爬大哥到期时间") @Schema(description = "爬大哥到期时间")
private LocalDateTime[] brotherExpireTime; private LocalDateTime[] brotherExpireTime;
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
@Schema(description = "爬主播到期时间")
private LocalDateTime[] crawlExpireTime;
@Schema @Schema
private String remark; private String remark;

View File

@@ -7,10 +7,13 @@ import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty; import com.alibaba.excel.annotation.ExcelProperty;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data; import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import javax.validation.constraints.NotNull; import javax.validation.constraints.NotNull;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 租户 Response VO") @Schema(description = "管理后台 - 租户 Response VO")
@Data @Data
@ExcelIgnoreUnannotated @ExcelIgnoreUnannotated
@@ -53,6 +56,8 @@ public class TenantRespVO {
@Schema(description = "大哥过期时间", requiredMode = Schema.RequiredMode.REQUIRED) @Schema(description = "大哥过期时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime brotherExpireTime; private LocalDateTime brotherExpireTime;
@Schema(description = "爬主播到期时间")
private LocalDateTime crawlExpireTime;
@Schema(description = "账号数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") @Schema(description = "账号数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Integer accountCount; private Integer accountCount;

View File

@@ -5,6 +5,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data; import lombok.Data;
import org.hibernate.validator.constraints.Length; import org.hibernate.validator.constraints.Length;
import org.springframework.format.annotation.DateTimeFormat;
import javax.validation.constraints.AssertTrue; import javax.validation.constraints.AssertTrue;
import javax.validation.constraints.NotNull; import javax.validation.constraints.NotNull;
@@ -12,6 +13,8 @@ import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size; import javax.validation.constraints.Size;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 租户创建/修改 Request VO") @Schema(description = "管理后台 - 租户创建/修改 Request VO")
@Data @Data
public class TenantSaveReqVO { public class TenantSaveReqVO {
@@ -51,6 +54,8 @@ public class TenantSaveReqVO {
@Schema(description = "大哥过期时间") @Schema(description = "大哥过期时间")
private LocalDateTime brotherExpireTime; private LocalDateTime brotherExpireTime;
@Schema(description = "爬主播到期时间")
private LocalDateTime crawlExpireTime;
@Schema(description = "账号数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") @Schema(description = "账号数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@NotNull(message = "账号数量不能为空") @NotNull(message = "账号数量不能为空")

View File

@@ -84,6 +84,12 @@ public class TenantDO extends BaseDO {
* 大哥过期时间 * 大哥过期时间
*/ */
private LocalDateTime brotherExpireTime; private LocalDateTime brotherExpireTime;
/**
* 爬虫到期时间
*/
private LocalDateTime crawlExpireTime;
/** /**
* 账号数量 * 账号数量
*/ */

View File

@@ -1,6 +1,5 @@
package cn.iocoder.yudao.module.system.job; package cn.iocoder.yudao.module.system.job;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler; import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler;
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
import cn.iocoder.yudao.framework.tenant.core.job.TenantJob; import cn.iocoder.yudao.framework.tenant.core.job.TenantJob;
@@ -13,7 +12,6 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.time.Duration;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.List; import java.util.List;
@@ -64,25 +62,39 @@ public class DisableCrawlExpiredAccount implements JobHandler{
public String execute(String param) throws Exception { public String execute(String param) throws Exception {
Long tenantId = TenantContextHolder.getTenantId(); Long tenantId = TenantContextHolder.getTenantId();
TenantDO tenant = tenantMapper.selectById(tenantId); TenantDO tenant = tenantMapper.selectById(tenantId);
if (tenant.getExpireTime()!=null) {
Duration brotherDuration = LocalDateTimeUtil.between(tenant.getExpireTime(), LocalDateTime.now()); // 检查租户是否配置了爬虫过期时间
long minutes = brotherDuration.toMinutes(); if (tenant.getCrawlExpireTime() == null) {
LambdaQueryWrapper<AdminUserDO> aiUserQueryWrapper = new LambdaQueryWrapper<>(); return "租户未配置爬虫过期时间";
aiUserQueryWrapper.eq(AdminUserDO::getTenantId, tenantId); }
aiUserQueryWrapper.eq(AdminUserDO::getCrawl, 1);
List<AdminUserDO> aiUserList = userMapper.selectList(aiUserQueryWrapper); // 判断是否已过期(当前时间 >= 过期时间)
int aiAccountNum = 0 ; if (LocalDateTime.now().isBefore(tenant.getCrawlExpireTime())) {
if (minutes >= 0) { return "爬虫权限未过期,无需处理";
for (AdminUserDO adminUserDO : aiUserList) { }
adminUserDO.setCrawl((byte) 0);
userMapper.updateById(adminUserDO); // 查询当前租户下所有启用爬虫权限的用户ID
aiAccountNum++; LambdaQueryWrapper<AdminUserDO> queryWrapper = new LambdaQueryWrapper<>();
log.info("禁用过期爬虫账号账号ID{}", adminUserDO.getId()); queryWrapper.eq(AdminUserDO::getTenantId, tenantId)
} .eq(AdminUserDO::getCrawl, 1)
} .select(AdminUserDO::getId);
// 返回操作结果包含禁用的AI账号和大哥账号数量统计 List<AdminUserDO> crawlUserList = userMapper.selectList(queryWrapper);
return "禁用过期账号成功,禁用了 " + aiAccountNum + " 个 爬虫 账号。";
} if (crawlUserList.isEmpty()) {
return "租户未配置过期时间"; return "无需禁用的爬虫账号";
}
// 批量更新:禁用爬虫权限
AdminUserDO updateEntity = new AdminUserDO();
updateEntity.setCrawl((byte) 0);
LambdaQueryWrapper<AdminUserDO> updateWrapper = new LambdaQueryWrapper<>();
updateWrapper.eq(AdminUserDO::getTenantId, tenantId)
.eq(AdminUserDO::getCrawl, 1);
int updatedCount = userMapper.update(updateEntity, updateWrapper);
// 记录被禁用的账号ID
crawlUserList.forEach(user -> log.info("禁用过期爬虫账号账号ID{}", user.getId()));
return "禁用过期爬虫账号成功,共禁用 " + updatedCount + " 个账号";
} }
} }

View File

@@ -1,6 +1,5 @@
package cn.iocoder.yudao.module.system.job; package cn.iocoder.yudao.module.system.job;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler; import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler;
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
import cn.iocoder.yudao.framework.tenant.core.job.TenantJob; import cn.iocoder.yudao.framework.tenant.core.job.TenantJob;
@@ -13,7 +12,6 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.time.Duration;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.List;/* import java.util.List;/*
* @author: ziin * @author: ziin
@@ -43,26 +41,40 @@ public class DisableWebAIExpiredAccount implements JobHandler{
public String execute(String param) throws Exception { public String execute(String param) throws Exception {
Long tenantId = TenantContextHolder.getTenantId(); Long tenantId = TenantContextHolder.getTenantId();
TenantDO tenant = tenantMapper.selectById(tenantId); TenantDO tenant = tenantMapper.selectById(tenantId);
if (tenant.getAiExpireTime()!=null) {
Duration brotherDuration = LocalDateTimeUtil.between(tenant.getAiExpireTime(), LocalDateTime.now()); // 检查租户是否配置了AI过期时间
long minutes = brotherDuration.toMinutes(); if (tenant.getAiExpireTime() == null) {
LambdaQueryWrapper<AdminUserDO> aiUserQueryWrapper = new LambdaQueryWrapper<>(); return "租户未配置AI过期时间";
aiUserQueryWrapper.eq(AdminUserDO::getTenantId, tenantId);
aiUserQueryWrapper.eq(AdminUserDO::getWebAi, 1);
List<AdminUserDO> aiUserList = userMapper.selectList(aiUserQueryWrapper);
int aiAccountNum = 0 ;
if (minutes >= 0) {
for (AdminUserDO adminUserDO : aiUserList) {
adminUserDO.setWebAi((byte) 0);
userMapper.updateById(adminUserDO);
aiAccountNum++;
log.info("禁用过期WebAI账号账号ID{}", adminUserDO.getId());
} }
// 判断是否已过期(当前时间 >= 过期时间)
if (LocalDateTime.now().isBefore(tenant.getAiExpireTime())) {
return "WebAI权限未过期无需处理";
} }
// 返回操作结果包含禁用的AI账号和大哥账号数量统计
return "禁用过期账号成功,禁用了 " + aiAccountNum + " 个 WebAI 账号。"; // 查询当前租户下所有启用WebAI权限的用户ID
LambdaQueryWrapper<AdminUserDO> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(AdminUserDO::getTenantId, tenantId)
.eq(AdminUserDO::getWebAi, 1)
.select(AdminUserDO::getId);
List<AdminUserDO> webAiUserList = userMapper.selectList(queryWrapper);
if (webAiUserList.isEmpty()) {
return "无需禁用的WebAI账号";
} }
return "租户未配置过期时间";
// 批量更新禁用WebAI权限
AdminUserDO updateEntity = new AdminUserDO();
updateEntity.setWebAi((byte) 0);
LambdaQueryWrapper<AdminUserDO> updateWrapper = new LambdaQueryWrapper<>();
updateWrapper.eq(AdminUserDO::getTenantId, tenantId)
.eq(AdminUserDO::getWebAi, 1);
int updatedCount = userMapper.update(updateEntity, updateWrapper);
// 记录被禁用的账号ID
webAiUserList.forEach(user -> log.info("禁用过期WebAI账号账号ID{}", user.getId()));
return "禁用过期WebAI账号成功共禁用 " + updatedCount + " 个账号";
} }
} }

View File

@@ -170,12 +170,12 @@ public class TenantServiceImpl implements TenantService {
log.info("代理: {} 续费租户:{} 成功,套餐 Id:{}", currentTenantId,targetTenant.getId(),updateReqVO.getPackageId()); log.info("代理: {} 续费租户:{} 成功,套餐 Id:{}", currentTenantId,targetTenant.getId(),updateReqVO.getPackageId());
} }
if (targetTenant.getExpireTime().isBefore(LocalDateTime.now())){ if (targetTenant.getCrawlExpireTime().isBefore(LocalDateTime.now())){
targetTenant.setExpireTime(LocalDateTime.now().plusDays(tenantAgencyPackage.getDays())); targetTenant.setCrawlExpireTime(LocalDateTime.now().plusDays(tenantAgencyPackage.getDays()));
targetTenant.setStatus(CommonStatusEnum.ENABLE.getStatus()); targetTenant.setStatus(CommonStatusEnum.ENABLE.getStatus());
targetTenantUser.setCrawl((byte) 1); targetTenantUser.setCrawl((byte) 1);
}else { }else {
targetTenant.setExpireTime(targetTenant.getExpireTime().plusDays(tenantAgencyPackage.getDays())); targetTenant.setCrawlExpireTime(targetTenant.getCrawlExpireTime().plusDays(tenantAgencyPackage.getDays()));
} }
if (tenantAgencyPackage.getAiClient()==1){ if (tenantAgencyPackage.getAiClient()==1){