package cn.iocoder.yudao.module.mes.service.pro.bagcode; import cn.iocoder.yudao.module.mes.dal.mysql.pro.bagcode.MesProBagCodeGenerateTaskMapper; import cn.iocoder.yudao.module.mes.enums.pro.MesProBagCodeGenerateTaskStatusEnum; import cn.iocoder.yudao.module.mes.framework.bagcode.MesProBagCodeGenerateTaskRegistry; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import java.time.LocalDateTime; import java.util.concurrent.atomic.AtomicBoolean; /** * 袋码生成任务执行器 *

* 在后台线程内完成分段生成,并负责:回写进度、响应取消、失败/取消后清理半成品。 */ @Slf4j @Component public class MesProBagCodeGenerateRunner { @Resource private MesProBagCodeGenerateTaskMapper taskMapper; @Resource private MesProBagCodeBatchGenerator generator; @Resource private MesProBagCodeGenerateCleaner cleaner; @Resource private MesProBagCodeGenerateTaskRegistry registry; /** * 执行生成任务(由线程池调用,勿在请求线程内直接调用) * * @param taskId 任务编号 * @param spec 生成参数 */ public void run(Long taskId, MesProBagCodeGenerateSpec spec) { // 取消标记在提交时已注册,这里只取用;取不到说明注册环节被绕过,补一个以保证取消可用 AtomicBoolean cancelFlag = registry.get(taskId); if (cancelFlag == null) { cancelFlag = registry.register(taskId); } try { taskMapper.updateStatus(taskId, MesProBagCodeGenerateTaskStatusEnum.RUNNING.getStatus(), null, null); int finished = generator.generate(spec, taskId, cancelFlag).finishedCount(); // 最后一段写入后、任务收尾前才收到取消:仍按取消处理,保证"取消后无半成品" if (cancelFlag.get()) { throw new MesProBagCodeBatchGenerator.CancelledException(); } taskMapper.updateProgress(taskId, finished); taskMapper.updateStatus(taskId, MesProBagCodeGenerateTaskStatusEnum.FINISHED.getStatus(), null, LocalDateTime.now()); log.info("[run][生成任务 {} 完成,共 {} 条袋码]", taskId, finished); } catch (MesProBagCodeBatchGenerator.CancelledException e) { cleanup(taskId, spec.uuid()); taskMapper.updateStatus(taskId, MesProBagCodeGenerateTaskStatusEnum.CANCELED.getStatus(), null, LocalDateTime.now()); log.info("[run][生成任务 {} 已取消]", taskId); } catch (Exception e) { cleanup(taskId, spec.uuid()); taskMapper.updateStatus(taskId, MesProBagCodeGenerateTaskStatusEnum.FAILED.getStatus(), truncateError(e), LocalDateTime.now()); log.error("[run][生成任务 {} 生成失败]", taskId, e); } finally { registry.unregister(taskId); registry.release(); } } /** * 清理半成品:清理失败只记录日志,不能影响任务终态回写,否则任务会一直挂在"生成中" */ private void cleanup(Long taskId, String uuid) { try { cleaner.cleanup(uuid); } catch (Exception e) { log.error("[cleanup][生成任务 {} 半成品清理失败,uuid={}]", taskId, uuid, e); } } private String truncateError(Exception e) { String message = e.getMessage(); if (message == null || message.isBlank()) { message = e.getClass().getSimpleName(); } return message.length() > 500 ? message.substring(0, 500) : message; } }