package cn.iocoder.yudao.module.mes.framework.bagcode; import jakarta.annotation.PostConstruct; import jakarta.annotation.Resource; import org.springframework.stereotype.Component; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Semaphore; import java.util.concurrent.atomic.AtomicBoolean; /** * 袋码生成任务运行态注册表 *

* 保存「哪些任务正在生成」与「是否已请求取消」。取消标记放内存而非数据库, * 是因为生成线程需要每段落检查一次取消,走数据库会产生大量无谓查询;本项目为单机部署,两者同进程。 *

* 进程重启会让所有运行态丢失,此时由清理任务把残留的非终态任务回收为失败。 */ @Component public class MesProBagCodeGenerateTaskRegistry { @Resource private MesProBagCodeGenerateProperties properties; /** * 正在运行的任务:任务编号 → 取消标记 */ private final Map runningTasks = new ConcurrentHashMap<>(); /** * 并发许可:约束后台生成任务数 */ private Semaphore permits; @PostConstruct public void init() { permits = new Semaphore(Math.max(1, properties.getMaxConcurrentTasks())); } /** * 尝试占用一个运行名额 */ public boolean tryAcquire() { return permits.tryAcquire(); } /** * 释放运行名额(完成、失败、取消、提交失败均需调用) */ public void release() { permits.release(); } /** * 当前由本进程持有的任务数(含刚建好、尚未被线程接管的) */ public int getRunningCount() { return runningTasks.size(); } /** * 注册任务并返回其取消标记 *

* 必须在任务落库后、投递线程池前调用,避免清理任务把尚未注册的记录误判为重启残留。 */ public AtomicBoolean register(Long taskId) { AtomicBoolean flag = new AtomicBoolean(false); runningTasks.put(taskId, flag); return flag; } /** * 获取任务的取消标记;任务不属于本进程时返回 null */ public AtomicBoolean get(Long taskId) { return runningTasks.get(taskId); } /** * 任务是否由本进程持有 */ public boolean isRunning(Long taskId) { return runningTasks.containsKey(taskId); } /** * 注销任务 */ public void unregister(Long taskId) { runningTasks.remove(taskId); } /** * 请求取消:任务在跑则置位取消标记 * * @return true 表示取消请求已送达;false 表示任务不在本进程运行 */ public boolean requestCancel(Long taskId) { AtomicBoolean flag = runningTasks.get(taskId); if (flag == null) { return false; } flag.set(true); return true; } }