BUGFIX:更新订阅计划时根据业务需要对参数进行判断并在需要时更新管理员信息

This commit is contained in:
2026-01-06 17:29:33 +08:00
parent 3beb27e491
commit e64add14af
7 changed files with 255 additions and 59 deletions

View File

@@ -21,6 +21,7 @@ import com.ai.da.service.SubscriptionPlanService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
@@ -28,6 +29,7 @@ import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import java.math.BigDecimal;
@@ -37,11 +39,9 @@ import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.*;
import static com.ai.da.mapper.primary.entity.Account.SystemRole.EDUCATION_SUB;
import static com.ai.da.mapper.primary.entity.SubscriptionPlan.SubscriptionStatus.ACTIVE;
import static com.ai.da.mapper.primary.entity.SubscriptionPlan.SubscriptionStatus.PENDING;
@@ -80,7 +80,7 @@ public class SubscriptionPlanServiceImpl extends ServiceImpl<SubscriptionPlanMap
baseMapper.insert(subscriptionPlan);
if (subscriptionPlan.getStatus().equals(SubscriptionPlan.SubscriptionStatus.ACTIVE.name())) {
// 执行一次激活扫描器
activeSubscriptionPlan();
activeSubscriptionPlan(subscriptionPlan.getId());
}
}
@@ -107,49 +107,203 @@ public class SubscriptionPlanServiceImpl extends ServiceImpl<SubscriptionPlanMap
}
// 更新 到期时间、积分总量、已使用积分量
@Transactional(rollbackFor = Exception.class)
@Override
public void updatePlan(UpdateSubscriptionPlanDTO updateDTO) {
if (Objects.isNull(updateDTO.getId())) {
public void updatePlan(UpdateSubscriptionPlanDTO dto) {
if (dto.getId() == null) {
throw new BusinessException("id.cannot.be.empty");
}
SubscriptionPlan subscriptionPlan = baseMapper.selectById(updateDTO.getId());
if (Objects.isNull(subscriptionPlan)) {
SubscriptionPlan plan = baseMapper.selectById(dto.getId());
if (plan == null) {
throw new BusinessException("unknown.subscription.plan");
}
if (Objects.nonNull(updateDTO.getCurrentPeriodStart()) && !updateDTO.getCurrentPeriodStart().equals(subscriptionPlan.getCurrentPeriodStart())) {
subscriptionPlan.setCurrentPeriodStart(updateDTO.getCurrentPeriodStart());
}
boolean activateToday = false;
if (Objects.nonNull(updateDTO.getCurrentPeriodEnd()) && !updateDTO.getCurrentPeriodEnd().equals(subscriptionPlan.getCurrentPeriodEnd())) {
subscriptionPlan.setCurrentPeriodEnd(updateDTO.getCurrentPeriodEnd());
}
activateToday = handlePeriodStart(dto, plan);
handlePeriodEnd(dto, plan);
handleAccountNum(dto, plan);
handleCreditLimit(dto, plan);
handleBasicInfo(dto, plan);
if (Objects.nonNull(updateDTO.getAccountNum()) && !updateDTO.getAccountNum().equals(subscriptionPlan.getAccountNum())) {
subscriptionPlan.setAccountNum(updateDTO.getAccountNum());
}
if (Objects.nonNull(updateDTO.getCreditLimit()) && !updateDTO.getCreditLimit().equals(subscriptionPlan.getCreditLimit())) {
subscriptionPlan.setCreditLimit(updateDTO.getCreditLimit());
}
if (Objects.nonNull(updateDTO.getAdminAccId()) && !updateDTO.getAdminAccId().equals(subscriptionPlan.getAdminAccId())) {
subscriptionPlan.setAdminAccId(updateDTO.getAdminAccId());
}
if (StringUtils.isNotBlank(updateDTO.getName()) && !updateDTO.getName().equals(subscriptionPlan.getName())) {
subscriptionPlan.setName(updateDTO.getName());
}
subscriptionPlan.setUpdateTime(LocalDateTime.now());
updateById(subscriptionPlan);
plan.setUpdateTime(LocalDateTime.now());
updateById(plan);
postUpdateProcess(plan, activateToday);
}
public void updatePlan() {
// ===================== 字段处理 =====================
/**
* 处理开始时间,返回是否需要当天激活
*/
private boolean handlePeriodStart(UpdateSubscriptionPlanDTO dto, SubscriptionPlan plan) {
Long newStart = dto.getCurrentPeriodStart();
if (newStart == null || newStart.equals(plan.getCurrentPeriodStart())) {
return false;
}
if (ACTIVE.name().equals(plan.getStatus())) {
throw new BusinessException(
"only.subscription.plans.with.a.PENDING.status.can.have.their.start.time.modified"
);
}
plan.setCurrentPeriodStart(newStart);
return isToday(newStart);
}
/**
* 处理结束时间(只能延长)
*/
private void handlePeriodEnd(UpdateSubscriptionPlanDTO dto, SubscriptionPlan plan) {
Long newEnd = dto.getCurrentPeriodEnd();
if (newEnd == null || newEnd.equals(plan.getCurrentPeriodEnd())) {
return;
}
if (newEnd < plan.getCurrentPeriodEnd()) {
throw new BusinessException(
"the.subscription.end.date.can.be.extended.only.not.reduced"
);
}
plan.setCurrentPeriodEnd(newEnd);
}
/**
* 处理账号数量
*/
private void handleAccountNum(UpdateSubscriptionPlanDTO dto, SubscriptionPlan plan) {
Integer newAccountNum = dto.getAccountNum();
if (newAccountNum == null || newAccountNum.equals(plan.getAccountNum())) {
return;
}
if (newAccountNum < plan.getAccountNum()) {
long usedSubAccounts = countExistingSubAccounts(plan.getId());
if (newAccountNum < usedSubAccounts + 1) {
throw new BusinessException(
"total.sub-account.quota.cannot.be.lower.than.existing.sub-accounts"
);
}
}
plan.setAccountNum(newAccountNum);
}
/**
* 处理积分上限
*/
private void handleCreditLimit(UpdateSubscriptionPlanDTO dto, SubscriptionPlan plan) {
BigDecimal newLimit = dto.getCreditLimit();
if (newLimit == null || newLimit.equals(plan.getCreditLimit())) {
return;
}
if (newLimit.compareTo(plan.getCreditUsage()) < 0) {
throw new BusinessException(
"the.credit.limit.set.cannot.be.lower.than.the.amount.of.credits.already.used"
);
}
plan.setCreditLimit(newLimit);
}
/**
* 基础字段
*/
private void handleBasicInfo(UpdateSubscriptionPlanDTO dto, SubscriptionPlan plan) {
if (dto.getAdminAccId() != null
&& !dto.getAdminAccId().equals(plan.getAdminAccId())) {
plan.setAdminAccId(dto.getAdminAccId());
}
if (StringUtils.isNotBlank(dto.getName())
&& !dto.getName().equals(plan.getName())) {
plan.setName(dto.getName());
}
}
// ===================== 更新后处理 =====================
private void postUpdateProcess(SubscriptionPlan plan, boolean activateToday) {
if (ACTIVE.name().equals(plan.getStatus())) {
syncAdminAndSubAccounts(plan);
return;
}
if (activateToday) {
activeSubscriptionPlan(plan.getId());
}
}
// ===================== 账号同步 =====================
private void syncAdminAndSubAccounts(SubscriptionPlan plan) {
Account admin = findActiveAdmin(plan);
if (admin != null) {
syncAdminAccount(admin, plan);
}
syncSubAccounts(plan);
}
private Account findActiveAdmin(SubscriptionPlan plan) {
return accountMapper.selectOne(
new QueryWrapper<Account>().lambda()
.eq(Account::getId, plan.getAdminAccId())
.eq(Account::getSubscriptionPlanId, plan.getId())
);
}
private void syncAdminAccount(Account admin, SubscriptionPlan plan) {
long planEndMillis = toMillis(plan.getCurrentPeriodEnd());
if (!Objects.equals(admin.getValidEndTime(), planEndMillis)) {
admin.setValidEndTime(planEndMillis);
}
if (admin.getCreditsUsageLimit().compareTo(plan.getCreditLimit()) != 0) {
// 这里计算修改前后的差值,上限增长,则差为正,上限下降,则差为负;
BigDecimal delta = plan.getCreditLimit()
.subtract(admin.getCreditsUsageLimit());
// 因为管理员的积分中可能包含自己购买的积分所以这里直接将差值添加到管理员的credit中
admin.setCredits(admin.getCredits().add(delta));
admin.setCreditsUsageLimit(plan.getCreditLimit());
}
accountMapper.updateById(admin);
}
private void syncSubAccounts(SubscriptionPlan plan) {
accountMapper.update(
null,
new UpdateWrapper<Account>().lambda()
.set(Account::getValidEndTime, toMillis(plan.getCurrentPeriodEnd()))
.eq(Account::getSubscriptionPlanId, plan.getId())
.eq(Account::getSystemUser, EDUCATION_SUB.getCode())
);
}
// ===================== 辅助方法 =====================
private long countExistingSubAccounts(Long planId) {
return accountMapper.selectCount(
new QueryWrapper<Account>().lambda()
.eq(Account::getSubscriptionPlanId, planId)
.eq(Account::getSystemUser, EDUCATION_SUB.getCode())
);
}
private boolean isToday(Long timestampSeconds) {
return timestampSeconds >= getTodayStartTimestamp()
&& timestampSeconds < getTodayEndTimestamp();
}
private long toMillis(Long seconds) {
return seconds * 1000;
}
@Override
@@ -407,7 +561,7 @@ public class SubscriptionPlanServiceImpl extends ServiceImpl<SubscriptionPlanMap
}
// 检查是否在有效期内
if (plan.getCurrentPeriodEnd() != null && isExpired(plan.getCurrentPeriodEnd())) {
if (plan.getCurrentPeriodEnd() != null && !isExpired(plan.getCurrentPeriodEnd())) {
throw new BusinessException("valid.subscription.period");
}
}
@@ -427,18 +581,28 @@ public class SubscriptionPlanServiceImpl extends ServiceImpl<SubscriptionPlanMap
return currentPeriodEnd < currentTimestamp;
}
public void activeSubscriptionPlan() {
public void activeSubscriptionPlan(Long planId) {
log.info("开始执行订阅计划生效检查...");
// 1. 扫描所有的订阅计划的开始时间currentPeriodStart找出今天开始生效的计划
List<SubscriptionPlan> todayActivePlans = findTodayActivePlans();
// 支持按id激活
List<SubscriptionPlan> todayActivePlans = new ArrayList<>();
if (Objects.nonNull(planId)) {
SubscriptionPlan subscriptionPlan = baseMapper.selectById(planId);
if (Objects.nonNull(subscriptionPlan)){
todayActivePlans.add(subscriptionPlan);
}
} else {
// 1. 扫描所有的订阅计划的开始时间currentPeriodStart找出今天开始生效的计划
todayActivePlans = findTodayActivePlans();
if (CollectionUtils.isEmpty(todayActivePlans)) {
log.info("今日没有需要生效的订阅计划");
return;
if (CollectionUtils.isEmpty(todayActivePlans)) {
log.info("今日没有需要生效的订阅计划");
return;
}
log.info("发现{}个今日生效的订阅计划", todayActivePlans.size());
}
log.info("发现{}个今日生效的订阅计划", todayActivePlans.size());
// 2. 处理每个今天开始生效的订阅计划
for (SubscriptionPlan plan : todayActivePlans) {