package cn.iocoder.yudao.module.mes.framework.qrcode;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.StrUtil;
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.dal.dataobject.pro.pallet.MesProPalletDO;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import java.nio.charset.StandardCharsets;
import java.net.URLEncoder;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* MES 二维码标签构建器:统一"二维码编码内容 + 图片叠加可读文本"的文本组装规则,
* 保证批次、托盘、袋码三种标签的关键信息保留一致。
*
* 配置了 {@code mes.trace-base-url} 时,二维码内容为 H5 溯源地址(带 code 参数),否则回退为纯码值(兼容旧码)
*/
public final class MesProQrLabelBuilder {
private MesProQrLabelBuilder() {
}
/**
* H5 溯源页面基础地址,由 {@code MesQrCodeConfig} 启动时注入;为空时二维码编码纯码值
*/
private static volatile String traceBaseUrl = "";
public static void setTraceBaseUrl(String traceBaseUrl) {
MesProQrLabelBuilder.traceBaseUrl = StrUtil.blankToDefault(traceBaseUrl, "");
}
/**
* 组装二维码内容:配置了溯源地址则拼 URL,否则返回纯码值
*/
private static String buildQrCodeContent(String code) {
if (StrUtil.isEmpty(traceBaseUrl)) {
return code;
}
return traceBaseUrl + "?code=" + URLEncoder.encode(code, StandardCharsets.UTF_8);
}
/**
* 批次二维码标签
*/
public static QrLabelInfo buildBatchLabel(MesProBatchDO batch,
Map itemMap,
Map userMap) {
// 未补录业务信息时用临时批次码作为二维码内容,并明确提示状态;品种/产线等信息缺失时不拼接空文本
String code = StrUtil.blankToDefault(batch.getCode(), batch.getTempCode());
LinkedHashMap keyValues = new LinkedHashMap<>();
keyValues.put("批次号", code);
if (StrUtil.isEmpty(batch.getCode())) {
keyValues.put("状态", "待补录");
}
MesMdmItemDO item = itemMap.get(batch.getItemId());
if (item != null) {
keyValues.put("品种", item.getName());
keyValues.put("规格", item.getSpecification());
}
if (StrUtil.isNotEmpty(batch.getLineName()) || StrUtil.isNotEmpty(batch.getLineCode())) {
keyValues.put("产线", StrUtil.join(" / ", batch.getLineName(), batch.getLineCode()));
}
if (batch.getProduceDate() != null) {
keyValues.put("生产日期", LocalDateTimeUtil.format(batch.getProduceDate(), "yyyy-MM-dd HH:mm"));
}
if (batch.getPlanQuantity() != null) {
keyValues.put("计划数量", String.valueOf(batch.getPlanQuantity()));
}
AdminUserRespDTO user = userMap.get(batch.getOwnerUserId());
if (user != null) {
keyValues.put("责任人", user.getNickname());
}
return QrLabelInfo.builder().fileName(code).code(buildQrCodeContent(code)).keyValues(keyValues).build();
}
/**
* 托盘二维码标签
*/
public static QrLabelInfo buildPalletLabel(MesProPalletDO pallet,
MesProBatchDO batch,
Map itemMap,
Map userMap) {
// 未绑定批次(临时托盘码)时用临时码作为二维码内容并明确提示状态
String code = StrUtil.blankToDefault(pallet.getCode(), pallet.getTempCode());
LinkedHashMap keyValues = new LinkedHashMap<>();
keyValues.put("托盘码", code);
if (StrUtil.isEmpty(pallet.getCode())) {
keyValues.put("状态", "未绑定批次");
}
keyValues.put("托盘序号", pallet.getPalletNo() == null ? "" : String.valueOf(pallet.getPalletNo()));
if (StrUtil.isNotEmpty(pallet.getBatchCode())) {
keyValues.put("批次号", pallet.getBatchCode());
}
if (pallet.getBagCount() != null) {
keyValues.put("已装袋数", String.valueOf(pallet.getBagCount()));
}
if (batch != null) {
fillBatchKeyValues(keyValues, batch, itemMap, userMap);
}
return QrLabelInfo.builder().fileName(code).code(buildQrCodeContent(code)).keyValues(keyValues).build();
}
/**
* 袋码二维码标签
*/
public static QrLabelInfo buildBagCodeLabel(MesProBagCodeDO bagCode,
MesProBatchDO batch,
Map itemMap,
Map userMap) {
LinkedHashMap keyValues = new LinkedHashMap<>();
keyValues.put("袋码", bagCode.getCode());
keyValues.put("袋序号", bagCode.getBagNo() == null ? "" : String.valueOf(bagCode.getBagNo()));
keyValues.put("批次号", bagCode.getBatchCode());
if (StrUtil.isNotEmpty(bagCode.getPalletCode())) {
keyValues.put("绑定托盘", bagCode.getPalletCode());
}
if (batch != null) {
fillBatchKeyValues(keyValues, batch, itemMap, userMap);
}
return QrLabelInfo.builder().fileName(bagCode.getCode()).code(buildQrCodeContent(bagCode.getCode())).keyValues(keyValues).build();
}
/**
* 填充批次公共可读文本(品种、规格、产线、生产日期、责任人)
*/
private static void fillBatchKeyValues(LinkedHashMap keyValues, MesProBatchDO batch,
Map itemMap, Map userMap) {
MesMdmItemDO item = itemMap.get(batch.getItemId());
if (item != null) {
keyValues.put("品种", item.getName());
keyValues.put("规格", item.getSpecification());
}
keyValues.put("产线", StrUtil.join(" / ", batch.getLineName(), batch.getLineCode()));
keyValues.put("生产日期", batch.getProduceDate() == null ? ""
: LocalDateTimeUtil.format(batch.getProduceDate(), "yyyy-MM-dd HH:mm"));
AdminUserRespDTO user = userMap.get(batch.getOwnerUserId());
if (user != null) {
keyValues.put("责任人", user.getNickname());
}
}
}