fix: 隔离日计划策略并发初始化事务

This commit is contained in:
zhouz
2026-08-14 16:20:07 +08:00
parent f3c1f61dbd
commit d5876c5812
3 changed files with 32 additions and 14 deletions

View File

@@ -1,10 +1,7 @@
package cn.code.nl.module.lms.controller.admin.dailyplan.vo;
import cn.code.nl.module.system.api.user.AdminUserApi;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
import org.dromara.core.trans.anno.Trans;
import org.dromara.core.trans.constant.TransType;
import org.dromara.core.trans.vo.VO;
import java.time.LocalDateTime;
@@ -18,7 +15,6 @@ public class DailyPlanStrategyRespVO implements VO {
private Integer strategyMode;
private Long cursorDailyPlanId;
private Integer version;
@Trans(type = TransType.AUTO_TRANS, key = AdminUserApi.PREFIX, fields = "nickname", ref = "updaterName")
private String updater;
private String updaterName;
private LocalDateTime updateTime;

View File

@@ -309,6 +309,7 @@ public class DailyPlanServiceImpl implements DailyPlanService {
DailyPlanStrategyDO strategy = strategyInitializer.getOrCreate(type);
result.add(convertStrategy(strategy));
}
resolveStrategyUpdaterNames(result);
return result;
}
@@ -736,6 +737,14 @@ public class DailyPlanServiceImpl implements DailyPlanService {
plans.forEach(plan -> plan.setUpdaterName(resolveOperatorName(plan.getUpdater(), userMap)));
}
/** 批量解析策略修改人,避免转换组件把系统调度标识当作用户 ID。 */
private void resolveStrategyUpdaterNames(List<DailyPlanStrategyRespVO> strategies) {
Set<Long> userIds = strategies.stream().map(DailyPlanStrategyRespVO::getUpdater)
.map(DailyPlanServiceImpl::parseOperatorId).filter(Objects::nonNull).collect(Collectors.toSet());
Map<Long, AdminUserRespDTO> userMap = userIds.isEmpty() ? Map.of() : adminUserApi.getUserMap(userIds);
strategies.forEach(strategy -> strategy.setUpdaterName(resolveOperatorName(strategy.getUpdater(), userMap)));
}
/** 将日计划数据对象逐字段转换为响应对象。 */
private DailyPlanRespVO convertPlan(DailyPlanDO plan) {
DailyPlanRespVO response = new DailyPlanRespVO();

View File

@@ -8,6 +8,9 @@ import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import jakarta.annotation.Resource;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Component;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.support.TransactionTemplate;
import static cn.code.nl.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.code.nl.module.lms.enums.ErrorCodeConstants.DAILY_PLAN_STRATEGY_NOT_EXISTS;
@@ -20,8 +23,26 @@ public class DailyPlanStrategyInitializer {
@Resource
private DailyPlanStrategyMapper strategyMapper;
@Resource
private PlatformTransactionManager transactionManager;
public DailyPlanStrategyDO getOrCreate(DailyPlanStrategyTypeEnum type) {
TransactionTemplate transaction = new TransactionTemplate(transactionManager);
transaction.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
try {
return transaction.execute(status -> loadOrInsert(type));
} catch (DuplicateKeyException ignored) {
// 冲突事务已经完整回滚;新事务读取可见的并发提交,规避 MySQL RR 旧快照。
DailyPlanStrategyDO concurrent = transaction.execute(
status -> strategyMapper.selectByStrategyType(type.getCode()));
if (concurrent != null) {
return concurrent;
}
throw exception(DAILY_PLAN_STRATEGY_NOT_EXISTS);
}
}
private DailyPlanStrategyDO loadOrInsert(DailyPlanStrategyTypeEnum type) {
DailyPlanStrategyDO current = strategyMapper.selectByStrategyType(type.getCode());
if (current != null) {
return current;
@@ -37,15 +58,7 @@ public class DailyPlanStrategyInitializer {
strategy.setVersion(0);
strategy.setCreator(SYSTEM_OPERATOR);
strategy.setUpdater(SYSTEM_OPERATOR);
try {
strategyMapper.insert(strategy);
return strategy;
} catch (DuplicateKeyException ignored) {
DailyPlanStrategyDO concurrent = strategyMapper.selectByStrategyType(type.getCode());
if (concurrent != null) {
return concurrent;
}
throw exception(DAILY_PLAN_STRATEGY_NOT_EXISTS);
}
}
}