云
10 小时以前 02a1ae81eba7b48223052fb833859f0a65dd7d21
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
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;
 
/**
 * 袋码生成任务执行器
 * <p>
 * 在后台线程内完成分段生成,并负责:回写进度、响应取消、失败/取消后清理半成品。
 */
@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;
    }
 
}