package cn.iocoder.yudao.module.mes.controller.admin.pro.bagcode; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.collection.MapUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; import cn.iocoder.yudao.module.mes.controller.admin.pro.bagcode.vo.MesProBagCodeBindReqVO; import cn.iocoder.yudao.module.mes.controller.admin.pro.bagcode.vo.MesProBagCodeGenerateReqVO; import cn.iocoder.yudao.module.mes.controller.admin.pro.bagcode.vo.MesProBagCodePageReqVO; import cn.iocoder.yudao.module.mes.controller.admin.pro.bagcode.vo.MesProBagCodeRespVO; 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.batch.MesProBatchDO; import cn.iocoder.yudao.module.mes.framework.qrcode.MesProQrLabelBuilder; import cn.iocoder.yudao.module.mes.framework.qrcode.QrCodeImageUtils; import cn.iocoder.yudao.module.mes.framework.qrcode.QrLabelInfo; import cn.iocoder.yudao.module.mes.service.mdm.MesMdmItemService; 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.system.api.user.AdminUserApi; import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameters; 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 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; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; 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; @Tag(name = "管理后台 - MES 袋码赋码(一袋一码)") @RestController @RequestMapping("/mes/pro/bag-code") @Validated public class MesProBagCodeController { @Resource private MesProBagCodeService bagCodeService; @Resource private MesProBatchService batchService; @Resource private AdminUserApi adminUserApi; @Resource private MesMdmItemService mdmItemService; @PostMapping("/generate") @Operation(summary = "生成袋码数据包(一袋一码)") @PreAuthorize("@ss.hasPermission('mes:pro-bag-code:create')") public CommonResult generateBagCodes(@Valid @RequestBody MesProBagCodeGenerateReqVO generateReqVO) { return success(bagCodeService.generateBagCodes(generateReqVO)); } @DeleteMapping("/delete-by-uuid") @Operation(summary = "删除袋码生成组(数据包)") @Parameter(name = "uuid", description = "生成组 UUID", required = true) @PreAuthorize("@ss.hasPermission('mes:pro-bag-code:delete')") public CommonResult deleteBagCodesByUuid(@RequestParam("uuid") String uuid) { bagCodeService.deleteBagCodesByUuid(uuid); return success(true); } @PutMapping("/bind-pallet") @Operation(summary = "袋码绑定托盘") @PreAuthorize("@ss.hasPermission('mes:pro-bag-code:update')") public CommonResult bindPallet(@Valid @RequestBody MesProBagCodeBindReqVO bindReqVO) { bagCodeService.bindPallet(bindReqVO); return success(true); } @PutMapping("/unbind-pallet") @Operation(summary = "袋码解绑托盘") @PreAuthorize("@ss.hasPermission('mes:pro-bag-code:update')") public CommonResult unbindPallet(@Valid @RequestBody MesProBagCodeBindReqVO bindReqVO) { bagCodeService.unbindPallet(bindReqVO); return success(true); } @GetMapping("/get") @Operation(summary = "获得袋码") @Parameter(name = "id", description = "编号", required = true, example = "1024") @PreAuthorize("@ss.hasPermission('mes:pro-bag-code:query')") public CommonResult getBagCode(@RequestParam("id") Long id) { MesProBagCodeDO bagCode = bagCodeService.getBagCode(id); if (bagCode == null) { return success(null); } return success(buildBagCodeRespVOList(Collections.singletonList(bagCode)).get(0)); } @GetMapping("/get-by-code") @Operation(summary = "扫码追溯:根据袋码(二维码内容)反查批次、产线、责任人等信息") @Parameter(name = "code", description = "袋码(二维码内容)", required = true, example = "PC20260818L01001-0001") @PreAuthorize("@ss.hasPermission('mes:pro-bag-code:query')") public CommonResult getBagCodeByCode(@RequestParam("code") String code) { MesProBagCodeDO bagCode = bagCodeService.getBagCodeByCode(code); if (bagCode == null) { throw exception(PRO_BAG_CODE_NOT_EXISTS); } return success(buildBagCodeTraceRespVO(bagCode)); } @GetMapping("/page") @Operation(summary = "获得袋码分页") @PreAuthorize("@ss.hasPermission('mes:pro-bag-code:query')") public CommonResult> getBagCodePage(@Valid MesProBagCodePageReqVO pageReqVO) { PageResult pageResult = bagCodeService.getBagCodePage(pageReqVO); return success(new PageResult<>(buildBagCodeRespVOList(pageResult.getList()), pageResult.getTotal())); } @GetMapping("/list-by-uuid") @Operation(summary = "获得生成组的袋码列表") @Parameter(name = "uuid", description = "生成组 UUID", required = true) @PreAuthorize("@ss.hasPermission('mes:pro-bag-code:query')") public CommonResult> getBagCodeListByUuid(@RequestParam("uuid") String uuid) { List list = bagCodeService.getBagCodeListByUuid(uuid); return success(buildBagCodeRespVOList(list)); } @GetMapping("/export-excel") @Operation(summary = "批量导出袋码 Excel(交付印刷),导出后袋码标记为已导出印刷") @PreAuthorize("@ss.hasPermission('mes:pro-bag-code:export')") @ApiAccessLog(operateType = EXPORT) public void exportBagCodeExcel(@Valid MesProBagCodePageReqVO pageReqVO, HttpServletResponse response) throws IOException { pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); List list = bagCodeService.getBagCodePage(pageReqVO).getList(); List voList = buildBagCodeRespVOList(list); // 导出即交付印刷:标记为已导出印刷并记录时间(仅处理"已生成"状态的袋码) bagCodeService.markBagCodesPrinted(convertList(list, MesProBagCodeDO::getId)); ExcelUtils.write(response, "袋码数据包.xls", "数据", MesProBagCodeRespVO.class, voList); } @GetMapping("/export-qr") @Operation(summary = "批量导出袋码二维码图片 ZIP(图片叠加关键信息文字),供印刷使用") @Parameters({ @Parameter(name = "uuid", description = "生成组 UUID(优先),为空则按 batchId 导出"), @Parameter(name = "batchId", description = "生产批次编号") }) @PreAuthorize("@ss.hasPermission('mes:pro-bag-code:export')") @ApiAccessLog(operateType = EXPORT) public void exportBagCodeQr(@RequestParam(value = "uuid", required = false) String uuid, @RequestParam(value = "batchId", required = false) Long batchId, HttpServletResponse response) throws IOException { List list; String zipName; if (StrUtil.isNotEmpty(uuid)) { list = bagCodeService.getBagCodeListByUuid(uuid); zipName = "袋码二维码-" + uuid; } else if (batchId != null) { list = bagCodeService.getBagCodeListByBatchId(batchId); zipName = "袋码二维码-" + 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); } QrCodeImageUtils.writeZip(response, zipName, buildBagCodeQrLabels(list)); } // ==================== 拼接 VO ==================== private List buildBagCodeRespVOList(List list) { if (CollUtil.isEmpty(list)) { return Collections.emptyList(); } // 1. 获得关联数据(仅批次的产线信息) Map batchMap = convertMap( batchService.getBatchList(convertSet(list, MesProBagCodeDO::getBatchId)), MesProBatchDO::getId); // 2. 构建结果 return BeanUtils.toBean(list, MesProBagCodeRespVO.class, vo -> MapUtils.findAndThen(batchMap, vo.getBatchId(), batch -> vo.setLineCode(batch.getLineCode()).setLineName(batch.getLineName()))); } private MesProBagCodeTraceRespVO buildBagCodeTraceRespVO(MesProBagCodeDO bagCode) { MesProBagCodeTraceRespVO respVO = BeanUtils.toBean(bagCode, MesProBagCodeTraceRespVO.class); // 批次信息 MesProBatchDO batch = batchService.getBatch(bagCode.getBatchId()); if (batch != null) { respVO.setBatchStatus(batch.getStatus()) .setProduceDate(batch.getProduceDate()) .setLineCode(batch.getLineCode()) .setLineName(batch.getLineName()) .setOwnerUserId(batch.getOwnerUserId()) .setItemId(batch.getItemId()); // 责任人 if (batch.getOwnerUserId() != null) { AdminUserRespDTO user = adminUserApi.getUser(batch.getOwnerUserId()); if (user != null) { respVO.setOwnerUserName(user.getNickname()); } } // 品种信息 if (batch.getItemId() != null) { MesMdmItemDO item = mdmItemService.getItem(batch.getItemId()); if (item != null) { respVO.setItemCode(item.getCode()) .setItemName(item.getName()) .setItemSpecification(item.getSpecification()); } } } return respVO; } /** * 构建袋码二维码标签列表(二维码编码纯码值,图片叠加关键信息文字) */ private List buildBagCodeQrLabels(List list) { // 1. 获得关联数据(批次、品种、责任人) Map batchMap = convertMap( batchService.getBatchList(convertSet(list, MesProBagCodeDO::getBatchId)), MesProBatchDO::getId); Map itemMap = mdmItemService.getItemMap( convertSet(batchMap.values(), MesProBatchDO::getItemId)); Map userMap = adminUserApi.getUserMap( convertSet(batchMap.values(), MesProBatchDO::getOwnerUserId)); // 2. 构建标签 List labels = new ArrayList<>(list.size()); for (MesProBagCodeDO bagCode : list) { labels.add(MesProQrLabelBuilder.buildBagCodeLabel( bagCode, batchMap.get(bagCode.getBatchId()), itemMap, userMap)); } return labels; } }