TASK:教育版子账号积分刷新策略更新
This commit is contained in:
@@ -27,7 +27,7 @@ public class AccountTask {
|
||||
// @Scheduled(cron = "59 59 23 * * ?")
|
||||
// @Scheduled(cron = "0 0 0 1 * ?")
|
||||
public void refreshCreditsMonthly() {
|
||||
log.info("每月1号0点 将年费用户积分重置为 6000");
|
||||
log.info("每月1号0点 重置教育版子账号为默认积分");
|
||||
accountService.refreshCreditsMonthly();
|
||||
}
|
||||
|
||||
|
||||
@@ -528,7 +528,7 @@ public class RedisUtil {
|
||||
return JSON.parseObject(json, ProgressDTO.class);
|
||||
} catch (Exception e) {
|
||||
log.warn("任务进度解析失败 key={}, json={}", key, json);
|
||||
return new ProgressDTO(0, 0, false);
|
||||
return new ProgressDTO(0, 0, false, null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -660,7 +660,7 @@ public class RedisUtilEnhance {
|
||||
return JSON.parseObject(json, ProgressDTO.class);
|
||||
} catch (Exception e) {
|
||||
log.warn("任务进度解析失败 key={}, json={}", key, json);
|
||||
return new ProgressDTO(0, 0, false);
|
||||
return new ProgressDTO(0, 0, false, null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -385,5 +385,11 @@ public class AccountController {
|
||||
return Response.success("success");
|
||||
}*/
|
||||
|
||||
@GetMapping("/refreshCreditsMonthly")
|
||||
@ApiOperation(value = "刷新子账号积分")
|
||||
public void refreshCreditsMonthly() {
|
||||
accountService.refreshCreditsMonthly();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@@ -11,5 +13,6 @@ public class ProgressDTO {
|
||||
private int total;
|
||||
private int current;
|
||||
private boolean error;
|
||||
private LocalDateTime computeTime;
|
||||
}
|
||||
|
||||
|
||||
@@ -46,6 +46,7 @@ import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
@@ -1600,17 +1601,199 @@ public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> impl
|
||||
}
|
||||
|
||||
/**
|
||||
* 为年费用户每月更新积分
|
||||
* 为教育版子账号用户每月更新积分
|
||||
*/
|
||||
public void refreshCreditsWeekly(){
|
||||
UpdateWrapper<Account> accountUpdateWrapper = new UpdateWrapper<>();
|
||||
// 刷新账号有效期截止之前的年付用户的积分
|
||||
long epochMilli = Instant.now().toEpochMilli();
|
||||
accountUpdateWrapper.lambda().set(Account::getCredits, CreditsEventsEnum.RESET_YEAR_CREDITS.getValue())
|
||||
.eq(Account::getSystemUser,1)
|
||||
// .or().eq(Account::getSystemUser,2)
|
||||
.gt(Account::getValidEndTime, epochMilli);
|
||||
baseMapper.update(null,accountUpdateWrapper);
|
||||
/**
|
||||
* 每月刷新教育版子账号积分
|
||||
* 只刷新教育版管理员(role=7)的子账号(role=8)
|
||||
*/
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void refreshCreditsMonthly() {
|
||||
long startTime = System.currentTimeMillis();
|
||||
log.info("开始执行月度积分刷新任务");
|
||||
|
||||
try {
|
||||
long currentEpochMilli = Instant.now().toEpochMilli();
|
||||
|
||||
// 1. 查询所有有效的教育版管理员账号
|
||||
List<Account> adminAccounts = getValidAdminAccounts(currentEpochMilli);
|
||||
|
||||
if (CollectionUtils.isEmpty(adminAccounts)) {
|
||||
log.info("未找到有效的教育版管理员账号");
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. 批量处理所有子账号积分刷新
|
||||
int totalProcessed = refreshAllSubAccountsCredits(adminAccounts);
|
||||
|
||||
log.info("月度积分刷新任务完成,共处理 {} 个子账号,耗时 {} ms",
|
||||
totalProcessed, System.currentTimeMillis() - startTime);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("积分刷新任务执行失败", e);
|
||||
throw new BusinessException("积分刷新任务执行失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取有效的教育版管理员账号
|
||||
*/
|
||||
private List<Account> getValidAdminAccounts(long currentEpochMilli) {
|
||||
QueryWrapper<Account> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.lambda()
|
||||
.select(Account::getId, Account::getOrganizationName, Account::getCreditsUsageLimit)
|
||||
.eq(Account::getSystemUser, 7) // 教育版管理员
|
||||
.gt(Account::getValidEndTime, currentEpochMilli); // 账号有效期内
|
||||
|
||||
return baseMapper.selectList(queryWrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量刷新所有子账号积分
|
||||
*/
|
||||
private int refreshAllSubAccountsCredits(List<Account> adminAccounts) {
|
||||
int totalProcessed = 0;
|
||||
|
||||
for (Account adminAccount : adminAccounts) {
|
||||
// 获取该管理员的所有子账号
|
||||
List<Account> subAccounts = getSubAccountsByAdmin(adminAccount);
|
||||
|
||||
if (CollectionUtils.isEmpty(subAccounts)) {
|
||||
log.debug("管理员 {} 没有子账号", adminAccount.getId());
|
||||
continue;
|
||||
}
|
||||
|
||||
// 批量更新子账号积分
|
||||
int processedCount = batchUpdateSubAccountsCredits(subAccounts);
|
||||
totalProcessed += processedCount;
|
||||
|
||||
adminAccount.setCreditsUsage(new BigDecimal(3500).multiply(new BigDecimal(processedCount)));
|
||||
adminAccount.setCredits(adminAccount.getCreditsUsageLimit().subtract(adminAccount.getCreditsUsage()));
|
||||
adminAccount.setUpdateDate(new Date());
|
||||
updateById(adminAccount);
|
||||
|
||||
log.info("管理员 {} 的子账号积分刷新完成,共处理 {} 个子账号",
|
||||
adminAccount.getId(), processedCount);
|
||||
|
||||
}
|
||||
|
||||
return totalProcessed;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定管理员的所有子账号
|
||||
*/
|
||||
private List<Account> getSubAccountsByAdmin(Account adminAccount) {
|
||||
QueryWrapper<Account> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.lambda()
|
||||
.eq(Account::getOrganizationName, adminAccount.getOrganizationName())
|
||||
.eq(Account::getParentId, adminAccount.getId())
|
||||
.eq(Account::getSystemUser, 8); // 教育版子账号
|
||||
|
||||
return baseMapper.selectList(queryWrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量更新子账号积分
|
||||
*/
|
||||
private int batchUpdateSubAccountsCredits(List<Account> subAccounts) {
|
||||
List<Account> accountsToUpdate = new ArrayList<>();
|
||||
Date now = new Date();
|
||||
|
||||
for (Account subAcc : subAccounts) {
|
||||
try {
|
||||
Account updatedAccount = calculateNewCredits(subAcc, now);
|
||||
accountsToUpdate.add(updatedAccount);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("计算子账号 {} 积分时发生错误", subAcc.getId(), e);
|
||||
}
|
||||
}
|
||||
|
||||
if (!accountsToUpdate.isEmpty()) {
|
||||
// 使用批量更新提高性能
|
||||
return batchUpdateByIds(accountsToUpdate);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算新的积分值
|
||||
*/
|
||||
private Account calculateNewCredits(Account subAcc, Date updateTime) {
|
||||
BigDecimal creditsUsageLimit = subAcc.getCreditsUsageLimit();
|
||||
// 使用 Optional 替代 ObjectUtils.defaultIfNull
|
||||
BigDecimal creditsUsage = Optional.ofNullable(subAcc.getCreditsUsage()).orElse(BigDecimal.ZERO);
|
||||
BigDecimal currentCredits = Optional.ofNullable(subAcc.getCredits()).orElse(BigDecimal.ZERO);
|
||||
|
||||
// 计算学校分配积分的剩余量
|
||||
BigDecimal schoolCreditRemaining = creditsUsageLimit.subtract(creditsUsage);
|
||||
|
||||
// 计算个人充值的积分(总积分减去学校分配的剩余积分)
|
||||
BigDecimal personalCredits = currentCredits.subtract(
|
||||
schoolCreditRemaining.compareTo(BigDecimal.ZERO) > 0 ?
|
||||
schoolCreditRemaining : BigDecimal.ZERO
|
||||
);
|
||||
|
||||
// 确保个人积分不为负数
|
||||
personalCredits = personalCredits.max(BigDecimal.ZERO);
|
||||
|
||||
// 新的总积分 = 个人积分 + 学校分配的新积分额度 todo 重新分配的积分是使用上个月分配的积分还是默认积分,暂时使用默认积分 3500
|
||||
BigDecimal newTotalCredits = personalCredits.add(new BigDecimal(3500));
|
||||
|
||||
// 记录积分变更日志(可选)
|
||||
logCreditChange(subAcc, currentCredits, newTotalCredits, creditsUsage);
|
||||
|
||||
// 创建更新对象
|
||||
Account updatedAccount = new Account();
|
||||
updatedAccount.setId(subAcc.getId());
|
||||
updatedAccount.setCredits(newTotalCredits);
|
||||
updatedAccount.setCreditsUsage(BigDecimal.ZERO); // 重置已使用积分
|
||||
updatedAccount.setCreditsUsageLimit(new BigDecimal(3500)); // 重置为默认积分
|
||||
updatedAccount.setUpdateDate(updateTime);
|
||||
|
||||
return updatedAccount;
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录积分变更日志
|
||||
*/
|
||||
private void logCreditChange(Account subAcc, BigDecimal oldCredits,
|
||||
BigDecimal newCredits, BigDecimal creditsUsage) {
|
||||
if (creditsUsage.compareTo(subAcc.getCreditsUsageLimit()) > 0) {
|
||||
log.warn("用户 {} 积分使用量 {} 大于积分使用上限 {}",
|
||||
subAcc.getId(), creditsUsage, subAcc.getCreditsUsageLimit());
|
||||
}
|
||||
|
||||
log.debug("更新子账号 {} 积分: 旧值={}, 已使用={}, 新值={}",
|
||||
subAcc.getId(), oldCredits,creditsUsage, newCredits);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量更新账号信息
|
||||
*/
|
||||
private int batchUpdateByIds(List<Account> accountsToUpdate) {
|
||||
// 使用MyBatis Plus的批量更新方法
|
||||
// 如果没有批量更新方法,可以分批次更新以避免大数据量问题
|
||||
int batchSize = 1000; // 每批更新1000条
|
||||
int totalUpdated = 0;
|
||||
|
||||
for (int i = 0; i < accountsToUpdate.size(); i += batchSize) {
|
||||
int end = Math.min(i + batchSize, accountsToUpdate.size());
|
||||
List<Account> batch = accountsToUpdate.subList(i, end);
|
||||
|
||||
// 使用MyBatis Plus的批量更新
|
||||
// 或者实现自己的批量更新逻辑
|
||||
for (Account account : batch) {
|
||||
baseMapper.updateById(account);
|
||||
}
|
||||
|
||||
totalUpdated += batch.size();
|
||||
log.debug("已批量更新 {} 个子账号积分", batch.size());
|
||||
}
|
||||
|
||||
return totalUpdated;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -25,6 +25,7 @@ import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
@@ -57,7 +58,7 @@ public class ProductImageServiceImpl implements ProductImageService {
|
||||
System.out.println(">>> [asyncInitialize] 当前线程:" + Thread.currentThread().getName());
|
||||
|
||||
String progressKey = String.valueOf(brandId);
|
||||
ProgressDTO progressDTO = new ProgressDTO(0, 0, false);
|
||||
ProgressDTO progressDTO = new ProgressDTO(0, 0, false, null);
|
||||
redisUtil.setTaskProgressDTO(progressKey, progressDTO);
|
||||
|
||||
try {
|
||||
@@ -113,13 +114,14 @@ public class ProductImageServiceImpl implements ProductImageService {
|
||||
// 更新当前进度
|
||||
current++;
|
||||
progressDTO.setCurrent(current);
|
||||
progressDTO.setComputeTime(LocalDateTime.now());
|
||||
redisUtil.setTaskProgressDTO(progressKey, progressDTO);
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage());
|
||||
// log.error("初始化失败", e);
|
||||
redisUtil.setTaskProgressDTO(progressKey, new ProgressDTO(0, 0, true));
|
||||
redisUtil.setTaskProgressDTO(progressKey, new ProgressDTO(0, 0, true, null));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user