From b852254d90e7d1955db31db154193ec8ee84d8b3 Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期三, 09 九月 2026 10:57:59 +0800
Subject: [PATCH] feat(hrm): 新增员工用工性质、班组归属字段及考勤异常处理优化

---
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/barcode/MesWmBarcodeServiceImpl.java |  171 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 files changed, 163 insertions(+), 8 deletions(-)

diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/barcode/MesWmBarcodeServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/barcode/MesWmBarcodeServiceImpl.java
index 2dea524..23bc7d5 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/barcode/MesWmBarcodeServiceImpl.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/barcode/MesWmBarcodeServiceImpl.java
@@ -1,5 +1,6 @@
 package cn.iocoder.yudao.module.mes.service.wm.barcode;
 
+import cn.hutool.core.codec.Base64;
 import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.util.ObjUtil;
 import cn.hutool.core.util.StrUtil;
@@ -11,11 +12,26 @@
 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.barcode.MesWmBarcodeConfigDO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.barcode.MesWmBarcodeDO;
 import cn.iocoder.yudao.module.mes.dal.mysql.wm.barcode.MesWmBarcodeMapper;
+import cn.iocoder.yudao.module.mes.enums.wm.BarcodeFormatEnum;
+import com.google.zxing.BarcodeFormat;
+import com.google.zxing.MultiFormatWriter;
+import com.google.zxing.client.j2se.MatrixToImageWriter;
+import com.google.zxing.common.BitMatrix;
 import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
 import org.springframework.validation.annotation.Validated;
 
+import javax.imageio.ImageIO;
+import java.awt.image.BufferedImage;
+import java.io.ByteArrayOutputStream;
+import java.util.ArrayList;
 import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
 import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;
@@ -27,7 +43,13 @@
  */
 @Service
 @Validated
+@Slf4j
 public class MesWmBarcodeServiceImpl implements MesWmBarcodeService {
+
+    /**
+     * 鍐呭鏍煎紡妯℃澘涓殑鍗犱綅绗﹁瘑鍒鍒�
+     */
+    private static final Pattern PLACEHOLDER_PATTERN = Pattern.compile("\\{[A-Za-z0-9_]+\\}");
 
     @Resource
     private MesWmBarcodeMapper barcodeMapper;
@@ -43,7 +65,8 @@
 
         // 2. 鐢熸垚鏉$爜鍐呭锛屽苟鏍¢獙鍞竴鎬�
         String content = generateAndValidateContent(createReqVO.getId(),
-                createReqVO.getContent(), config.getContentFormat(), createReqVO.getBizCode());
+                createReqVO.getContent(), config.getContentFormat(), createReqVO.getBizCode(),
+                createReqVO.getBizName());
 
         // 3. 淇濆瓨鏉$爜璁板綍
         MesWmBarcodeDO barcode = BeanUtils.toBean(createReqVO, MesWmBarcodeDO.class).setContent(content)
@@ -53,7 +76,7 @@
     }
 
     @Override
-    public String generateBarcodeContent(Integer bizType, String bizCode) {
+    public String generateBarcodeContent(Integer bizType, String bizCode, String bizName) {
         // 1.1 鏍¢獙鍙傛暟
         if (bizType == null) {
             throw exception(BARCODE_BIZ_TYPE_NOT_EXISTS);
@@ -65,7 +88,7 @@
         MesWmBarcodeConfigDO config = barcodeConfigService.validateBarcodeConfigByBizType(bizType);
 
         // 2. 鐢熸垚鏉$爜鍐呭
-        return generateBarcodeContent(config.getContentFormat(), bizCode);
+        return generateBarcodeContent(config.getContentFormat(), bizCode, bizName);
     }
 
     @Override
@@ -78,7 +101,8 @@
 
         // 2. 鐢熸垚鏉$爜鍐呭锛屽苟鏍¢獙鍞竴鎬�
         String content = generateAndValidateContent(updateReqVO.getId(),
-                updateReqVO.getContent(), config.getContentFormat(), updateReqVO.getBizCode());
+                updateReqVO.getContent(), config.getContentFormat(), updateReqVO.getBizCode(),
+                updateReqVO.getBizName());
 
         // 3. 鏇存柊锛堝埛鏂� configId 鍜� format锛岀‘淇濅笌鏈�鏂伴厤缃竴鑷达級
         MesWmBarcodeDO updateObj = BeanUtils.toBean(updateReqVO, MesWmBarcodeDO.class)
@@ -131,10 +155,10 @@
      * @param bizCode 涓氬姟缂栫爜
      * @return 鏈�缁堟潯鐮佸唴瀹�
      */
-    private String generateAndValidateContent(Long id, String content, String contentFormat, String bizCode) {
+    private String generateAndValidateContent(Long id, String content, String contentFormat, String bizCode, String bizName) {
         // 1. 濡傛灉鍓嶇鏈紶閫掑唴瀹癸紝鍒欒嚜鍔ㄧ敓鎴�
         if (StrUtil.isBlank(content)) {
-            content = generateBarcodeContent(contentFormat, bizCode);
+            content = generateBarcodeContent(contentFormat, bizCode, bizName);
         }
         // 2. 鏍¢獙鏉$爜鍐呭鍞竴鎬э紙鎺掗櫎鑷韩锛�
         if (StrUtil.isNotBlank(content)) {
@@ -151,13 +175,144 @@
      *
      * @param contentFormat 鍐呭鏍煎紡妯℃澘
      * @param bizCode 涓氬姟缂栫爜
+     * @param bizName 涓氬姟鍚嶇О
      * @return 鏉$爜鍐呭
      */
-    private String generateBarcodeContent(String contentFormat, String bizCode) {
+    private String generateBarcodeContent(String contentFormat, String bizCode, String bizName) {
         if (StrUtil.isBlank(contentFormat)) {
             return bizCode;
         }
-        return contentFormat.replace(MesWmBarcodeConfigDO.PLACEHOLDER_BUSINESS_CODE, bizCode);
+        // 1. 鏇挎崲宸叉敮鎸佺殑鍗犱綅绗�
+        String content = contentFormat
+                .replace(MesWmBarcodeConfigDO.PLACEHOLDER_BUSINESS_CODE, StrUtil.nullToEmpty(bizCode))
+                .replace(MesWmBarcodeConfigDO.PLACEHOLDER_BUSINESS_NAME, StrUtil.nullToEmpty(bizName));
+        // 2. 娓呯┖妯℃澘涓叾瀹冩湭濉厖鐨勬湭鐭ュ崰浣嶇锛岄伩鍏� {XXX} 娈嬬暀褰卞搷鎵爜
+        return PLACEHOLDER_PATTERN.matcher(content).replaceAll("");
+    }
+
+    @Override
+    public String renderBarcodeImage(String content, Integer format, Integer width, Integer height) {
+        if (StrUtil.isBlank(content)) {
+            throw exception(BARCODE_CONTENT_EMPTY);
+        }
+        int w = width != null && width > 0 ? width : 200;
+        int h = height != null && height > 0 ? height : 200;
+        try {
+            BitMatrix bitMatrix = new MultiFormatWriter().encode(content, toZxingFormat(format), w, h);
+            BufferedImage image = MatrixToImageWriter.toBufferedImage(bitMatrix);
+            ByteArrayOutputStream out = new ByteArrayOutputStream();
+            ImageIO.write(image, "png", out);
+            return "data:image/png;base64," + Base64.encode(out.toByteArray());
+        } catch (Exception e) {
+            log.error("[renderBarcodeImage] 鏉$爜娓叉煋澶辫触锛宑ontent={}, format={}", content, format, e);
+            throw new RuntimeException("鏉$爜娓叉煋澶辫触");
+        }
+    }
+
+    /**
+     * 灏嗛」鐩潯鐮佹牸寮忔灇涓惧�兼槧灏勪负 zxing 鏉$爜鏍煎紡
+     *
+     * @param format 椤圭洰鏉$爜鏍煎紡 {@link BarcodeFormatEnum}
+     * @return zxing 鏉$爜鏍煎紡
+     */
+    private BarcodeFormat toZxingFormat(Integer format) {
+        if (format == null) {
+            return BarcodeFormat.CODE_128;
+        }
+        switch (format) {
+            case 1: // BarcodeFormatEnum.QR_CODE
+                return BarcodeFormat.QR_CODE;
+            case 2: // BarcodeFormatEnum.EAN13
+                return BarcodeFormat.EAN_13;
+            case 3: // BarcodeFormatEnum.CODE39
+                return BarcodeFormat.CODE_39;
+            case 4: // BarcodeFormatEnum.UPC_A
+                return BarcodeFormat.UPC_A;
+            default:
+                throw exception(BARCODE_FORMAT_INVALID);
+        }
+    }
+
+    @Override
+    public Map<String, Object> parseBarcodeContent(String content) {
+        if (StrUtil.isBlank(content)) {
+            throw exception(BARCODE_CONTENT_EMPTY);
+        }
+        // 1. 浼樺厛鎸夋潯鐮佸唴瀹圭簿纭懡涓凡鏈夋潯鐮佽褰�
+        MesWmBarcodeDO barcode = barcodeMapper.selectByContent(content);
+        if (barcode != null) {
+            Map<String, Object> result = new LinkedHashMap<>();
+            result.put("matched", true);
+            result.put("barcodeId", barcode.getId());
+            result.put("configId", barcode.getConfigId());
+            result.put("format", barcode.getFormat());
+            result.put("bizType", barcode.getBizType());
+            result.put("bizId", barcode.getBizId());
+            result.put("bizCode", barcode.getBizCode());
+            result.put("bizName", barcode.getBizName());
+            result.put("content", barcode.getContent());
+            return result;
+        }
+        // 2. 鏈懡涓椂锛岄亶鍘嗗惎鐢ㄧ姸鎬佺殑閰嶇疆鎸夊唴瀹规牸寮忔ā鏉垮弽瑙�
+        List<MesWmBarcodeConfigDO> configs = barcodeConfigService.getEnableBarcodeConfigList();
+        for (MesWmBarcodeConfigDO config : configs) {
+            Map<String, Object> fields = matchByTemplate(config.getContentFormat(), content);
+            if (fields != null) {
+                Map<String, Object> result = new LinkedHashMap<>();
+                result.put("matched", false);
+                result.put("configId", config.getId());
+                result.put("format", config.getFormat());
+                result.put("bizType", config.getBizType());
+                result.put("bizCode", fields.get(MesWmBarcodeConfigDO.PLACEHOLDER_BUSINESS_CODE));
+                result.put("bizName", fields.get(MesWmBarcodeConfigDO.PLACEHOLDER_BUSINESS_NAME));
+                result.put("fields", fields);
+                return result;
+            }
+        }
+        // 3. 鍧囨湭鍛戒腑
+        Map<String, Object> result = new LinkedHashMap<>();
+        result.put("matched", false);
+        result.put("content", content);
+        return result;
+    }
+
+    /**
+     * 鎸夊唴瀹规牸寮忔ā鏉垮弽瑙f潯鐮佸唴瀹�
+     *
+     * @param contentFormat 鍐呭鏍煎紡妯℃澘
+     * @param content 寰呰В鏋愮殑鏉$爜鍐呭
+     * @return 鍗犱綅绗� -> 瀹為檯鍊肩殑鏄犲皠锛涗笉鍖归厤杩斿洖 null
+     */
+    private Map<String, Object> matchByTemplate(String contentFormat, String content) {
+        if (StrUtil.isBlank(contentFormat) || !contentFormat.contains("{")) {
+            return null;
+        }
+        // 1. 灏嗘ā鏉胯浆涓烘鍒欙細瀛楅潰閲忚浆涔夛紝鍗犱綅绗﹁浆涓烘崟鑾风粍
+        List<String> placeholders = new ArrayList<>();
+        StringBuilder regexSb = new StringBuilder();
+        Matcher matcher = PLACEHOLDER_PATTERN.matcher(contentFormat);
+        int lastEnd = 0;
+        while (matcher.find()) {
+            regexSb.append(Pattern.quote(contentFormat.substring(lastEnd, matcher.start())));
+            placeholders.add(matcher.group());
+            regexSb.append("(.+?)");
+            lastEnd = matcher.end();
+        }
+        if (placeholders.isEmpty()) {
+            return null;
+        }
+        regexSb.append(Pattern.quote(contentFormat.substring(lastEnd)));
+
+        // 2. 鍏ㄩ噺鍖归厤骞舵彁鍙栧悇鍗犱綅绗﹀��
+        Matcher contentMatcher = Pattern.compile(regexSb.toString()).matcher(content);
+        if (!contentMatcher.matches()) {
+            return null;
+        }
+        Map<String, Object> fields = new LinkedHashMap<>();
+        for (int i = 0; i < placeholders.size(); i++) {
+            fields.put(placeholders.get(i), contentMatcher.group(i + 1));
+        }
+        return fields;
     }
 
     @Override

--
Gitblit v1.9.3