liu
13 小时以前 66517edf1de22c8203daa256abffddae43bae704
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
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
package cn.iocoder.yudao.module.mes.controller.admin.pro.qrexport;
 
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.security.core.service.SecurityFrameworkService;
import cn.iocoder.yudao.module.mes.controller.admin.pro.qrexport.vo.MesProQrExportCountRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.pro.qrexport.vo.MesProQrExportTaskRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.pro.qrexport.vo.MesProQrExportTaskSubmitReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.qrexport.MesProQrExportTaskDO;
import cn.iocoder.yudao.module.mes.enums.pro.MesProQrExportBizTypeEnum;
import cn.iocoder.yudao.module.mes.enums.pro.MesProQrExportTaskStatusEnum;
import cn.iocoder.yudao.module.mes.framework.qrexport.MesProQrExportProperties;
import cn.iocoder.yudao.module.mes.service.pro.qrexport.MesProQrExportTaskService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
 
import java.io.IOException;
 
import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.FORBIDDEN;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PRO_QR_EXPORT_PARAMS_INVALID;
 
/**
 * 二维码大批量导出任务接口
 * <p>
 * 数量不超过分界线的导出不走这里(调用方直接调原下载接口,秒级返回、不排队);
 * 超过分界线才提交任务、轮询进度、完成后下载。
 * <p>
 * 说明:下载与取消未额外限制「只能操作自己创建的任务」。能进入本接口的用户已持有对应模块的导出权限,
 * 也就已经可以直接调用同步导出接口拿到同样的 ZIP 内容,再加一层创建人校验并不能提供实际保护。
 */
@Tag(name = "管理后台 - MES 二维码导出任务")
@RestController
@RequestMapping("/mes/pro/qr-export")
@Validated
public class MesProQrExportTaskController {
 
    /**
     * 任一模块的导出权限即可访问本接口;具体业务类型再逐项校验
     */
    private static final String ANY_EXPORT_PERMISSION =
            "@ss.hasAnyPermissions('mes:pro-bag-code:export','mes:pro-pallet:export','mes:pro-batch:export')";
 
    @Resource
    private MesProQrExportTaskService taskService;
    @Resource
    private MesProQrExportProperties properties;
    @Resource
    private SecurityFrameworkService securityFrameworkService;
 
    @GetMapping("/count")
    @Operation(summary = "统计二维码导出张数并给出导出方式(SYNC 直接下载 / ASYNC 后台任务)")
    @PreAuthorize(ANY_EXPORT_PERMISSION)
    public CommonResult<MesProQrExportCountRespVO> count(@RequestParam("bizType") String bizType,
                                                         @RequestParam(value = "uuid", required = false) String uuid,
                                                         @RequestParam(value = "batchId", required = false) Long batchId) {
        MesProQrExportBizTypeEnum bizTypeEnum = resolveBizType(bizType);
        assertBizPermission(bizTypeEnum);
        long total = taskService.countLabels(bizTypeEnum, uuid, batchId);
        MesProQrExportCountRespVO respVO = new MesProQrExportCountRespVO();
        respVO.setTotalCount(total);
        respVO.setSyncThreshold(properties.getSyncThreshold());
        respVO.setMode(total > properties.getSyncThreshold()
                ? MesProQrExportCountRespVO.MODE_ASYNC : MesProQrExportCountRespVO.MODE_SYNC);
        return success(respVO);
    }
 
    @PostMapping("/task")
    @Operation(summary = "提交二维码后台导出任务(大批量)")
    @PreAuthorize(ANY_EXPORT_PERMISSION)
    public CommonResult<MesProQrExportTaskRespVO> submit(@Valid @RequestBody MesProQrExportTaskSubmitReqVO reqVO) {
        MesProQrExportBizTypeEnum bizTypeEnum = resolveBizType(reqVO.getBizType());
        assertBizPermission(bizTypeEnum);
        MesProQrExportTaskDO task = taskService.submit(bizTypeEnum, reqVO.getUuid(), reqVO.getBatchId());
        return success(convert(task));
    }
 
    @GetMapping("/task/get")
    @Operation(summary = "获得二维码导出任务(用于轮询进度)")
    @Parameter(name = "id", description = "任务编号", required = true, example = "1024")
    @PreAuthorize(ANY_EXPORT_PERMISSION)
    public CommonResult<MesProQrExportTaskRespVO> get(@RequestParam("id") Long id) {
        return success(convert(taskService.getTask(id)));
    }
 
    @PostMapping("/task/cancel")
    @Operation(summary = "取消二维码导出任务")
    @Parameter(name = "id", description = "任务编号", required = true, example = "1024")
    @PreAuthorize(ANY_EXPORT_PERMISSION)
    public CommonResult<Boolean> cancel(@RequestParam("id") Long id) {
        taskService.cancel(id);
        return success(true);
    }
 
    @GetMapping("/task/download")
    @Operation(summary = "下载二维码导出产物(下载完成后系统立即删除临时文件)")
    @Parameter(name = "id", description = "任务编号", required = true, example = "1024")
    @PreAuthorize(ANY_EXPORT_PERMISSION)
    public void download(@RequestParam("id") Long id, HttpServletResponse response) throws IOException {
        taskService.download(id, response);
    }
 
    // ==================== 私有方法 ====================
 
    private MesProQrExportBizTypeEnum resolveBizType(String bizType) {
        for (MesProQrExportBizTypeEnum value : MesProQrExportBizTypeEnum.values()) {
            if (value.getType().equals(bizType)) {
                return value;
            }
        }
        throw exception(PRO_QR_EXPORT_PARAMS_INVALID);
    }
 
    /**
     * 校验业务类型对应的模块导出权限
     * <p>
     * 单靠「任一导出权限」会让只有托盘导出权限的用户也能导出袋码,故按类型再校验一次。
     */
    private void assertBizPermission(MesProQrExportBizTypeEnum bizType) {
        String permission = switch (bizType) {
            case BAG_LABEL, BAG_BUSINESS -> "mes:pro-bag-code:export";
            case PALLET_LABEL, PALLET_BUSINESS -> "mes:pro-pallet:export";
            case BATCH_LABEL, BATCH_BUSINESS -> "mes:pro-batch:export";
        };
        if (!securityFrameworkService.hasAnyPermissions(permission)) {
            throw exception(FORBIDDEN);
        }
    }
 
    private MesProQrExportTaskRespVO convert(MesProQrExportTaskDO task) {
        MesProQrExportTaskRespVO respVO = new MesProQrExportTaskRespVO();
        respVO.setId(task.getId());
        respVO.setBizType(task.getBizType());
        respVO.setTaskName(task.getTaskName());
        respVO.setStatus(task.getStatus());
        MesProQrExportTaskStatusEnum statusEnum = parseStatus(task.getStatus());
        respVO.setStatusName(statusEnum == null ? null : statusEnum.getName());
        respVO.setTotalCount(task.getTotalCount());
        respVO.setFinishedCount(task.getFinishedCount());
        respVO.setProgress(calcProgress(task));
        respVO.setFileSize(task.getFileSize());
        respVO.setErrorMsg(task.getErrorMsg());
        respVO.setDownloadable(MesProQrExportTaskStatusEnum.FINISHED.getStatus().equals(task.getStatus())
                && task.getFilePath() != null && !task.getFilePath().isBlank());
        respVO.setCreateTime(task.getCreateTime());
        respVO.setFinishTime(task.getFinishTime());
        return respVO;
    }
 
    private Integer calcProgress(MesProQrExportTaskDO task) {
        Integer total = task.getTotalCount();
        if (total == null || total <= 0) {
            return 0;
        }
        int finished = task.getFinishedCount() == null ? 0 : task.getFinishedCount();
        return (int) Math.min(100L, (long) finished * 100 / total);
    }
 
    private MesProQrExportTaskStatusEnum parseStatus(Integer status) {
        if (status == null) {
            return null;
        }
        for (MesProQrExportTaskStatusEnum value : MesProQrExportTaskStatusEnum.values()) {
            if (value.getStatus().equals(status)) {
                return value;
            }
        }
        return null;
    }
 
}