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; /** * 二维码大批量导出任务接口 *

* 数量不超过分界线的导出不走这里(调用方直接调原下载接口,秒级返回、不排队); * 超过分界线才提交任务、轮询进度、完成后下载。 *

* 说明:下载与取消未额外限制「只能操作自己创建的任务」。能进入本接口的用户已持有对应模块的导出权限, * 也就已经可以直接调用同步导出接口拿到同样的 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 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 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 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 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); } /** * 校验业务类型对应的模块导出权限 *

* 单靠「任一导出权限」会让只有托盘导出权限的用户也能导出袋码,故按类型再校验一次。 */ 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; } }