云
8 小时以前 02a1ae81eba7b48223052fb833859f0a65dd7d21
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/bagcode/MesProBagCodeController.java
@@ -13,6 +13,7 @@
import cn.iocoder.yudao.module.mes.controller.admin.pro.bagcode.vo.MesProBagCodeBindReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.pro.bagcode.vo.MesProBagCodeGenerateRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.pro.bagcode.vo.MesProBagCodeGenerateReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.pro.bagcode.vo.MesProBagCodeGenerateTaskRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.pro.bagcode.vo.MesProBagCodePageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.pro.bagcode.vo.MesProBagCodeRawGenerateReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.pro.bagcode.vo.MesProBagCodeReplaceReqVO;
@@ -21,13 +22,16 @@
import cn.iocoder.yudao.module.mes.controller.admin.pro.bagcode.vo.MesProBagCodeTraceRespVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.mdm.MesMdmItemDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.bagcode.MesProBagCodeDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.bagcode.MesProBagCodeGenerateTaskDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.batch.MesProBatchDO;
import cn.iocoder.yudao.module.mes.framework.qrcode.MesProQrLabelBuilder;
import cn.iocoder.yudao.module.mes.enums.pro.MesProBagCodeGenerateTaskStatusEnum;
import cn.iocoder.yudao.module.mes.framework.qrcode.QrCodeImageUtils;
import cn.iocoder.yudao.module.mes.framework.qrcode.QrLabelInfo;
import cn.iocoder.yudao.module.mes.framework.qrexport.MesProQrExportProperties;
import cn.iocoder.yudao.module.mes.service.mdm.MesMdmItemService;
import cn.iocoder.yudao.module.mes.service.pro.bagcode.MesProBagCodeGenerateTaskService;
import cn.iocoder.yudao.module.mes.service.pro.bagcode.MesProBagCodeService;
import cn.iocoder.yudao.module.mes.service.pro.batch.MesProBatchService;
import cn.iocoder.yudao.module.mes.service.pro.qrexport.MesProQrLabelAssembler;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import io.swagger.v3.oas.annotations.Operation;
@@ -42,11 +46,9 @@
import org.springframework.web.bind.annotation.*;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@@ -56,7 +58,7 @@
import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PRO_BAG_CODE_NOT_EXISTS;
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PRO_QR_EXPORT_PARAMS_INVALID;
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PRO_QR_EXPORT_TOO_MANY;
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PRO_QR_EXPORT_TOO_MANY_FOR_SYNC;
@Tag(name = "管理后台 - MES 袋码赋码(一袋一码)")
@RestController
@@ -67,11 +69,17 @@
    @Resource
    private MesProBagCodeService bagCodeService;
    @Resource
    private MesProBagCodeGenerateTaskService generateTaskService;
    @Resource
    private MesProBatchService batchService;
    @Resource
    private AdminUserApi adminUserApi;
    @Resource
    private MesMdmItemService mdmItemService;
    @Resource
    private MesProQrLabelAssembler labelAssembler;
    @Resource
    private MesProQrExportProperties qrExportProperties;
    @PostMapping("/generate")
    @Operation(summary = "生成袋码数据包(一袋一码),累计达到阈值时自动生成托盘码")
@@ -86,6 +94,23 @@
    public CommonResult<MesProBagCodeGenerateRespVO> generateRawBagCodes(
            @Valid @RequestBody MesProBagCodeRawGenerateReqVO rawGenerateReqVO) {
        return success(bagCodeService.generateRawBagCodes(rawGenerateReqVO));
    }
    @GetMapping("/generate-task/get")
    @Operation(summary = "获得袋码生成任务(大批量生成的进度轮询)")
    @Parameter(name = "id", description = "任务编号", required = true, example = "1024")
    @PreAuthorize("@ss.hasPermission('mes:pro-bag-code:query')")
    public CommonResult<MesProBagCodeGenerateTaskRespVO> getGenerateTask(@RequestParam("id") Long id) {
        return success(buildGenerateTaskRespVO(generateTaskService.getTask(id)));
    }
    @PostMapping("/generate-task/cancel")
    @Operation(summary = "取消袋码生成任务(停止生成并清理半成品)")
    @Parameter(name = "id", description = "任务编号", required = true, example = "1024")
    @PreAuthorize("@ss.hasPermission('mes:pro-bag-code:create')")
    public CommonResult<Boolean> cancelGenerateTask(@RequestParam("id") Long id) {
        generateTaskService.cancel(id);
        return success(true);
    }
    @PostMapping("/replace")
@@ -162,6 +187,13 @@
        return success(new PageResult<>(buildBagCodeRespVOList(pageResult.getList()), pageResult.getTotal()));
    }
    @GetMapping("/unbound-ton-bags")
    @Operation(summary = "获得可绑定生产批次的吨袋")
    @PreAuthorize("@ss.hasAnyPermissions('mes:pro-batch:create', 'mes:pro-batch:update', 'mes:pro-bag-code:query')")
    public CommonResult<List<MesProBagCodeRespVO>> getUnboundTonBags() {
        return success(buildBagCodeRespVOList(bagCodeService.getUnboundTonBags()));
    }
    @GetMapping("/list-by-uuid")
    @Operation(summary = "获得生成组的袋码列表")
    @Parameter(name = "uuid", description = "生成组 UUID", required = true)
@@ -211,10 +243,8 @@
        if (CollUtil.isEmpty(list)) {
            throw exception(PRO_BAG_CODE_NOT_EXISTS);
        }
        if (list.size() > QrCodeImageUtils.MAX_QR_EXPORT) {
            throw exception(PRO_QR_EXPORT_TOO_MANY);
        }
        QrCodeImageUtils.writeZip(response, zipName, buildBagCodeQrLabels(list));
        assertSyncExportAllowed(list.size());
        QrCodeImageUtils.writeZip(response, zipName, labelAssembler.buildBagCodeLabels(list));
    }
    // ==================== 拼接 VO ====================
@@ -239,9 +269,27 @@
        else if (batchId != null) list = bagCodeService.getPrintableBagCodeListByBatchId(batchId);
        else throw exception(PRO_QR_EXPORT_PARAMS_INVALID);
        if (CollUtil.isEmpty(list)) throw exception(PRO_BAG_CODE_NOT_EXISTS);
        if (list.size() > QrCodeImageUtils.MAX_QR_EXPORT) throw exception(PRO_QR_EXPORT_TOO_MANY);
        List<QrLabelInfo> labels = list.stream().map(MesProQrLabelBuilder::buildBagBusinessCodeLabel).toList();
        QrCodeImageUtils.writeZip(response, "袋码业务二维码-" + (StrUtil.isNotBlank(uuid) ? uuid : batchId), labels);
        assertSyncExportAllowed(list.size());
        QrCodeImageUtils.writeZip(response, "袋码业务二维码-" + (StrUtil.isNotBlank(uuid) ? uuid : batchId),
                labelAssembler.buildBagBusinessLabels(list));
    }
    private MesProBagCodeGenerateTaskRespVO buildGenerateTaskRespVO(MesProBagCodeGenerateTaskDO task) {
        MesProBagCodeGenerateTaskRespVO respVO = BeanUtils.toBean(task, MesProBagCodeGenerateTaskRespVO.class);
        for (MesProBagCodeGenerateTaskStatusEnum value : MesProBagCodeGenerateTaskStatusEnum.values()) {
            if (value.getStatus().equals(task.getStatus())) {
                respVO.setStatusName(value.getName());
                break;
            }
        }
        Integer total = task.getTotalCount();
        if (total == null || total <= 0) {
            respVO.setProgress(0);
        } else {
            int finished = task.getFinishedCount() == null ? 0 : task.getFinishedCount();
            respVO.setProgress((int) Math.min(100L, (long) finished * 100 / total));
        }
        return respVO;
    }
    private List<MesProBagCodeRespVO> buildBagCodeRespVOList(List<MesProBagCodeDO> list) {
@@ -303,24 +351,15 @@
    }
    /**
     * 构建袋码二维码标签列表(二维码编码纯码值,图片叠加关键信息文字)
     * 同步导出只承接小批量
     * <p>
     * 数量超过分界线时必须走后台导出任务:同步响应会随张数线性变长(上万张可达数分钟、响应体上 GB),
     * 必然被网关或浏览器截断,用户看到的将是「下载失败」而不是可操作的提示。
     */
    private List<QrLabelInfo> buildBagCodeQrLabels(List<MesProBagCodeDO> list) {
        // 1. 获得关联数据(批次、品种、责任人);品种集合含袋码自身品种与历史批次品种
        Map<Long, MesProBatchDO> batchMap = convertMap(
                batchService.getBatchList(convertSet(list, MesProBagCodeDO::getBatchId)), MesProBatchDO::getId);
        Set<Long> itemIds = convertSet(list, MesProBagCodeDO::getItemId);
        itemIds.addAll(convertSet(batchMap.values(), MesProBatchDO::getItemId));
        Map<Long, MesMdmItemDO> itemMap = mdmItemService.getItemMap(itemIds);
        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(
                convertSet(batchMap.values(), MesProBatchDO::getOwnerUserId));
        // 2. 构建标签
        List<QrLabelInfo> labels = new ArrayList<>(list.size());
        for (MesProBagCodeDO bagCode : list) {
            labels.add(MesProQrLabelBuilder.buildBagCodeLabel(
                    bagCode, batchMap.get(bagCode.getBatchId()), itemMap, userMap));
    private void assertSyncExportAllowed(int count) {
        if (count > qrExportProperties.getSyncThreshold()) {
            throw exception(PRO_QR_EXPORT_TOO_MANY_FOR_SYNC, count, qrExportProperties.getSyncThreshold());
        }
        return labels;
    }
}