From f8ccc5ab312ab7c5d6c7eb7fac4e37924f513869 Mon Sep 17 00:00:00 2001
From: liu <2021943741@qq.com>
Date: 星期二, 03 十一月 2026 19:25:29 +0800
Subject: [PATCH] fix: 完善质量检验接口与质检员分配

---
 doc/20261103_enable_process_maintenance_menu.sql                                  |   56 +++++
 src/main/java/com/ruoyi/quality/controller/QualityTestStandardController.java     |    4 
 src/main/java/com/ruoyi/quality/mapper/QualityTestStandardMapper.java             |    2 
 src/main/java/com/ruoyi/quality/pojo/QualityInspect.java                          |    5 
 src/test/java/com/ruoyi/quality/controller/QualityInspectControllerTest.java      |   29 ++
 src/main/java/com/ruoyi/quality/service/IQualityTestStandardService.java          |    2 
 src/main/java/com/ruoyi/quality/util/QualityInspectionValueValidator.java         |  135 ++++++++++++
 src/main/java/com/ruoyi/project/system/mapper/SysUserMapper.java                  |    3 
 src/main/java/com/ruoyi/quality/controller/QualityInspectController.java          |   60 +++++
 src/main/java/com/ruoyi/quality/controller/QualityInspectParamController.java     |   30 ++
 src/test/java/com/ruoyi/quality/controller/QualityInspectParamControllerTest.java |   49 ++++
 src/main/resources/mapper/quality/QualityTestStandardMapper.xml                   |    5 
 src/main/resources/mapper/system/SysUserMapper.xml                                |   19 +
 src/main/resources/application-dev.yml                                            |  223 ++++++++++++++++++++
 src/main/java/com/ruoyi/quality/service/impl/QualityTestStandardServiceImpl.java  |    4 
 doc/20261103_quality_inspect_user_assignment.sql                                  |    5 
 src/main/resources/application-tide.yml                                           |    5 
 17 files changed, 627 insertions(+), 9 deletions(-)

diff --git a/doc/20261103_enable_process_maintenance_menu.sql b/doc/20261103_enable_process_maintenance_menu.sql
new file mode 100644
index 0000000..f0a1566
--- /dev/null
+++ b/doc/20261103_enable_process_maintenance_menu.sql
@@ -0,0 +1,56 @@
+-- 鎭㈠鈥滃伐搴忕淮鎶も�濊彍鍗曪紙MySQL锛屽彲閲嶅鎵ц锛夈��
+-- 鑻ヤ緷绾﹀畾锛歷isible='0' 琛ㄧず鏄剧ず锛宻tatus='0' 琛ㄧず姝e父銆�
+
+-- 1. 鎭㈠鑿滃崟鏈韩锛屽苟淇鍓嶇缁勪欢鍦板潃銆�
+UPDATE sys_menu
+SET visible = '0',
+    status = '0',
+    menu_type = 'C',
+    component = 'productionManagement/productionProcess/index',
+    update_by = 'admin',
+    update_time = NOW()
+WHERE menu_name = '宸ュ簭缁存姢'
+   OR component = 'productionManagement/productionProcess/index';
+
+-- 2. 纭繚鐩存帴鐖惰彍鍗曞浜庢樉绀恒�佹甯哥姸鎬併��
+UPDATE sys_menu parent
+INNER JOIN sys_menu child ON child.parent_id = parent.menu_id
+SET parent.visible = '0',
+    parent.status = '0',
+    parent.update_by = 'admin',
+    parent.update_time = NOW()
+WHERE child.menu_name = '宸ュ簭缁存姢';
+
+-- 3. 缁欑鐞嗗憳瑙掕壊琛ラ綈鑿滃崟鍏崇郴锛堝凡瀛樺湪鏃朵笉閲嶅鎻掑叆锛夈��
+INSERT INTO sys_role_menu (role_id, menu_id)
+SELECT role.role_id, menu.menu_id
+FROM sys_role role
+INNER JOIN sys_menu menu ON menu.menu_name = '宸ュ簭缁存姢'
+WHERE role.role_key = 'admin'
+  AND NOT EXISTS (
+      SELECT 1
+      FROM sys_role_menu relation
+      WHERE relation.role_id = role.role_id
+        AND relation.menu_id = menu.menu_id
+  );
+
+-- 4. 鎵ц鍚庢鏌ヨ彍鍗曘�佺埗鑿滃崟鍜岃鑹叉巿鏉冪粨鏋溿��
+SELECT menu.menu_id,
+       menu.parent_id,
+       menu.menu_name,
+       menu.path,
+       menu.component,
+       menu.visible,
+       menu.status,
+       parent.menu_name AS parent_name,
+       parent.visible AS parent_visible,
+       parent.status AS parent_status
+FROM sys_menu menu
+LEFT JOIN sys_menu parent ON parent.menu_id = menu.parent_id
+WHERE menu.menu_name = '宸ュ簭缁存姢';
+
+SELECT role.role_name, role.role_key, relation.menu_id
+FROM sys_role_menu relation
+INNER JOIN sys_role role ON role.role_id = relation.role_id
+INNER JOIN sys_menu menu ON menu.menu_id = relation.menu_id
+WHERE menu.menu_name = '宸ュ簭缁存姢';
diff --git a/doc/20261103_quality_inspect_user_assignment.sql b/doc/20261103_quality_inspect_user_assignment.sql
new file mode 100644
index 0000000..ea0df51
--- /dev/null
+++ b/doc/20261103_quality_inspect_user_assignment.sql
@@ -0,0 +1,5 @@
+ALTER TABLE quality_inspect
+    ADD COLUMN check_user_id BIGINT NULL COMMENT '妫�楠屽憳绯荤粺鐢ㄦ埛ID' AFTER check_name;
+
+CREATE INDEX idx_quality_inspect_check_user_id
+    ON quality_inspect (check_user_id);
diff --git a/src/main/java/com/ruoyi/project/system/mapper/SysUserMapper.java b/src/main/java/com/ruoyi/project/system/mapper/SysUserMapper.java
index 5507b3f..f9de981 100644
--- a/src/main/java/com/ruoyi/project/system/mapper/SysUserMapper.java
+++ b/src/main/java/com/ruoyi/project/system/mapper/SysUserMapper.java
@@ -27,6 +27,9 @@
      */
     public List<SysUser> selectUserList(SysUser sysUser);
 
+    /** 褰撳墠绉熸埛涓彲鐢ㄤ簬涓氬姟鍒嗛厤鐨勬甯哥郴缁熺敤鎴枫�� */
+    public List<SysUser> selectActiveUsers(@Param("tenantId") Long tenantId);
+
     /**
      * 鏍规嵁鏉′欢鍒嗛〉鏌ヨ宸查厤鐢ㄦ埛瑙掕壊鍒楄〃
      *
diff --git a/src/main/java/com/ruoyi/quality/controller/QualityInspectController.java b/src/main/java/com/ruoyi/quality/controller/QualityInspectController.java
index 1702c2a..3d9a177 100644
--- a/src/main/java/com/ruoyi/quality/controller/QualityInspectController.java
+++ b/src/main/java/com/ruoyi/quality/controller/QualityInspectController.java
@@ -2,7 +2,11 @@
 
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.common.utils.SecurityUtils;
 import com.ruoyi.framework.web.domain.AjaxResult;
+import com.ruoyi.project.system.domain.SysUser;
+import com.ruoyi.project.system.mapper.SysUserMapper;
+import com.ruoyi.project.system.service.ISysUserService;
 import com.ruoyi.procurementrecord.pojo.ProcurementRecordStorage;
 import com.ruoyi.procurementrecord.service.ProcurementRecordService;
 import com.ruoyi.procurementrecord.service.impl.ProcurementRecordServiceImpl;
@@ -15,6 +19,7 @@
 import com.ruoyi.quality.service.IQualityInspectParamService;
 import com.ruoyi.quality.service.IQualityInspectService;
 import com.ruoyi.quality.service.IQualityUnqualifiedService;
+import com.ruoyi.quality.util.QualityInspectionValueValidator;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.util.CollectionUtils;
@@ -23,6 +28,7 @@
 import javax.annotation.Resource;
 import javax.servlet.http.HttpServletResponse;
 import java.util.List;
+import java.util.Objects;
 
 /**
  * 鍘熸潗鏂欐楠�/杩囩▼妫�楠�/鍑哄巶妫�楠�
@@ -41,6 +47,12 @@
     private IQualityInspectFileService qualityInspectFileService;
     @Autowired
     private ProcurementRecordService procurementRecordService;
+
+    @Resource
+    private ISysUserService sysUserService;
+
+    @Resource
+    private SysUserMapper sysUserMapper;
 
 
     /**
@@ -108,6 +120,48 @@
             return AjaxResult.error("宸叉彁浜ょ殑妫�楠岃褰曚笉鑳戒慨鏀�");
         }
         return AjaxResult.success(qualityInspectService.updateQualityInspect(qualityInspectDto));
+    }
+
+    /** 浠呭垎閰嶆楠屽憳锛岄伩鍏嶈Е鍙戞暣寮犳楠屽崟鐨勫繀濉牎楠屻�� */
+    @PostMapping("/assignInspector")
+    public AjaxResult assignInspector(@RequestBody QualityInspect inspect) {
+        if (inspect == null || inspect.getId() == null) {
+            return AjaxResult.error("妫�楠岃褰旾D涓嶈兘涓虹┖");
+        }
+        if (inspect.getCheckUserId() == null) {
+            return AjaxResult.error("璇烽�夋嫨妫�楠屽憳");
+        }
+        QualityInspect stored = qualityInspectService.getById(inspect.getId());
+        if (stored == null) {
+            return AjaxResult.error("妫�楠岃褰曚笉瀛樺湪");
+        }
+        if (Integer.valueOf(1).equals(stored.getInspectState())) {
+            return AjaxResult.error("宸叉彁浜ょ殑妫�楠岃褰曚笉鑳介噸鏂板垎閰嶆楠屽憳");
+        }
+        SysUser inspector = sysUserService.selectUserById(inspect.getCheckUserId());
+        if (inspector == null || "2".equals(inspector.getDelFlag())) {
+            return AjaxResult.error("鎵�閫夌敤鎴蜂笉瀛樺湪鎴栧凡鍒犻櫎");
+        }
+        if (!SecurityUtils.isAdmin(SecurityUtils.getUserId())
+                && !Objects.equals(SecurityUtils.getLoginUser().getTenantId(), inspector.getTenantId())) {
+            return AjaxResult.error("鍙兘鍒嗛厤缁欏綋鍓嶅叕鍙哥敤鎴�");
+        }
+        if (!"0".equals(inspector.getStatus())) {
+            return AjaxResult.error("鎵�閫夌敤鎴峰凡鍋滅敤");
+        }
+        QualityInspect update = new QualityInspect();
+        update.setId(inspect.getId());
+        update.setCheckUserId(inspector.getUserId());
+        update.setCheckName(StringUtils.defaultIfBlank(inspector.getNickName(), inspector.getUserName()));
+        return AjaxResult.success(qualityInspectService.updateById(update));
+    }
+
+    /** 浠呰繑鍥炲綋鍓嶇鎴蜂腑姝e父銆佹湭鍒犻櫎鐨勭郴缁熺敤鎴凤紝渚涜川妫�鍛樺垎閰嶄娇鐢ㄣ�� */
+    @GetMapping("/inspectorCandidates")
+    public AjaxResult inspectorCandidates() {
+        Long tenantId = SecurityUtils.isAdmin(SecurityUtils.getUserId())
+                ? null : SecurityUtils.getLoginUser().getTenantId();
+        return AjaxResult.success(sysUserMapper.selectActiveUsers(tenantId));
     }
 
     /**
@@ -187,6 +241,12 @@
                 return "璇峰~鍐欌��" + parameter.getParameterItem() + "鈥濈殑妫�楠屽��";
             }
         }
+        if ("鍚堟牸".equals(inspect.getCheckResult())) {
+            String valueMessage = QualityInspectionValueValidator.validateQualified(inspect.getQualityInspectParams());
+            if (valueMessage != null) {
+                return valueMessage;
+            }
+        }
         return null;
     }
 }
diff --git a/src/main/java/com/ruoyi/quality/controller/QualityInspectParamController.java b/src/main/java/com/ruoyi/quality/controller/QualityInspectParamController.java
index 3d6df0c..78fb8ee 100644
--- a/src/main/java/com/ruoyi/quality/controller/QualityInspectParamController.java
+++ b/src/main/java/com/ruoyi/quality/controller/QualityInspectParamController.java
@@ -9,6 +9,8 @@
 import com.ruoyi.quality.service.IQualityInspectFileService;
 import com.ruoyi.quality.service.IQualityInspectParamService;
 import com.ruoyi.quality.service.IQualityInspectService;
+import com.ruoyi.quality.util.QualityInspectionValueValidator;
+import org.apache.commons.lang3.StringUtils;
 import org.springframework.util.CollectionUtils;
 import org.springframework.web.bind.annotation.*;
 
@@ -25,6 +27,9 @@
 
     @Resource
     private IQualityInspectParamService qualityInspectParamService;
+
+    @Resource
+    private IQualityInspectService qualityInspectService;
 
     /**
      * 璇︽儏
@@ -44,6 +49,31 @@
      */
     @PostMapping("/update")
     public AjaxResult update(@RequestBody List<QualityInspectParam> qualityInspectParams) {
+        if (CollectionUtils.isEmpty(qualityInspectParams)) {
+            return AjaxResult.error("妫�楠岄」鐩笉鑳戒负绌�");
+        }
+        Long inspectId = qualityInspectParams.get(0).getInspectId();
+        if (inspectId == null) {
+            return AjaxResult.error("妫�楠岃褰旾D涓嶈兘涓虹┖");
+        }
+        QualityInspect inspect = qualityInspectService.getById(inspectId);
+        if (inspect == null) {
+            return AjaxResult.error("妫�楠岃褰曚笉瀛樺湪");
+        }
+        if (Integer.valueOf(1).equals(inspect.getInspectState())) {
+            return AjaxResult.error("宸叉彁浜ょ殑妫�楠岃褰曚笉鑳戒慨鏀�");
+        }
+        for (QualityInspectParam parameter : qualityInspectParams) {
+            if (StringUtils.isBlank(parameter.getTestValue())) {
+                return AjaxResult.error("璇峰~鍐欌��" + parameter.getParameterItem() + "鈥濈殑妫�楠屽��");
+            }
+        }
+        if ("鍚堟牸".equals(inspect.getCheckResult())) {
+            String valueMessage = QualityInspectionValueValidator.validateQualified(qualityInspectParams);
+            if (valueMessage != null) {
+                return AjaxResult.error(valueMessage);
+            }
+        }
         return AjaxResult.success(qualityInspectParamService.updateBatchById(qualityInspectParams));
     }
 
diff --git a/src/main/java/com/ruoyi/quality/controller/QualityTestStandardController.java b/src/main/java/com/ruoyi/quality/controller/QualityTestStandardController.java
index c018bce..32f0489 100644
--- a/src/main/java/com/ruoyi/quality/controller/QualityTestStandardController.java
+++ b/src/main/java/com/ruoyi/quality/controller/QualityTestStandardController.java
@@ -101,8 +101,8 @@
      * @return
      */
     @GetMapping("/getQualityTestStandardByProductId")
-    public AjaxResult getQualityTestStandardByProductId(@Nonnull Long productId, @Nonnull Integer inspectType, String process) {
-        return AjaxResult.success(qualityTestStandardService.getQualityTestStandardByProductId(productId,inspectType,process));
+    public AjaxResult getQualityTestStandardByProductId(@Nonnull Long productId, @Nonnull Integer inspectType, Long processId) {
+        return AjaxResult.success(qualityTestStandardService.getQualityTestStandardByProductId(productId, inspectType, processId));
     }
 
     /**
diff --git a/src/main/java/com/ruoyi/quality/mapper/QualityTestStandardMapper.java b/src/main/java/com/ruoyi/quality/mapper/QualityTestStandardMapper.java
index a394b86..01ddb7a 100644
--- a/src/main/java/com/ruoyi/quality/mapper/QualityTestStandardMapper.java
+++ b/src/main/java/com/ruoyi/quality/mapper/QualityTestStandardMapper.java
@@ -22,7 +22,7 @@
 
     IPage<QualityTestStandard> qualityTestStandardListPage(Page page, @Param("c") QualityTestStandard qualityTestStandard);
 
-    List<QualityTestStandard> getQualityTestStandardByProductId(@Param("productId") Long productId, @Param("inspectType") Integer inspectType, @Param("process") String process);
+    List<QualityTestStandard> getQualityTestStandardByProductId(@Param("productId") Long productId, @Param("inspectType") Integer inspectType, @Param("processId") Long processId);
 
 
 }
diff --git a/src/main/java/com/ruoyi/quality/pojo/QualityInspect.java b/src/main/java/com/ruoyi/quality/pojo/QualityInspect.java
index 5216cd6..2b86d71 100644
--- a/src/main/java/com/ruoyi/quality/pojo/QualityInspect.java
+++ b/src/main/java/com/ruoyi/quality/pojo/QualityInspect.java
@@ -70,6 +70,11 @@
     private String checkName;
 
     /**
+     * 妫�楠屽憳瀵瑰簲鐨勭郴缁熺敤鎴稩D
+     */
+    private Long checkUserId;
+
+    /**
      * 鍏宠仈浜у搧id
      */
     @NotBlank(message = "浜у搧id涓嶈兘涓虹┖")
diff --git a/src/main/java/com/ruoyi/quality/service/IQualityTestStandardService.java b/src/main/java/com/ruoyi/quality/service/IQualityTestStandardService.java
index a580765..ac2fbaa 100644
--- a/src/main/java/com/ruoyi/quality/service/IQualityTestStandardService.java
+++ b/src/main/java/com/ruoyi/quality/service/IQualityTestStandardService.java
@@ -21,6 +21,6 @@
 
     int copyParam(QualityTestStandard qualityTestStandard);
 
-    List<QualityTestStandard> getQualityTestStandardByProductId(Long productId, Integer inspectType, String process);
+    List<QualityTestStandard> getQualityTestStandardByProductId(Long productId, Integer inspectType, Long processId);
 
 }
diff --git a/src/main/java/com/ruoyi/quality/service/impl/QualityTestStandardServiceImpl.java b/src/main/java/com/ruoyi/quality/service/impl/QualityTestStandardServiceImpl.java
index 339a10d..431736b 100644
--- a/src/main/java/com/ruoyi/quality/service/impl/QualityTestStandardServiceImpl.java
+++ b/src/main/java/com/ruoyi/quality/service/impl/QualityTestStandardServiceImpl.java
@@ -60,8 +60,8 @@
     }
 
     @Override
-    public List<QualityTestStandard> getQualityTestStandardByProductId(Long productId, Integer inspectType, String process) {
-        return qualityTestStandardMapper.getQualityTestStandardByProductId(productId, inspectType, process);
+    public List<QualityTestStandard> getQualityTestStandardByProductId(Long productId, Integer inspectType, Long processId) {
+        return qualityTestStandardMapper.getQualityTestStandardByProductId(productId, inspectType, processId);
     }
 
 
diff --git a/src/main/java/com/ruoyi/quality/util/QualityInspectionValueValidator.java b/src/main/java/com/ruoyi/quality/util/QualityInspectionValueValidator.java
new file mode 100644
index 0000000..8d3f8f5
--- /dev/null
+++ b/src/main/java/com/ruoyi/quality/util/QualityInspectionValueValidator.java
@@ -0,0 +1,135 @@
+package com.ruoyi.quality.util;
+
+import com.ruoyi.quality.pojo.QualityInspectParam;
+import org.apache.commons.lang3.StringUtils;
+
+import java.math.BigDecimal;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * 妫�楠屽�间笌鏍囧噯/鍐呮帶鑼冨洿鏍¢獙銆�
+ * 鏀寔鍖洪棿銆佹璐熷亸宸�佸ぇ灏忔瘮杈冨拰绮剧‘鏁板�硷紝渚嬪锛�1.95~2.05銆�2.00卤0.10銆佲墹0.02銆佲墺100銆�
+ */
+public final class QualityInspectionValueValidator {
+
+    private static final String NUMBER = "(-?\\d+(?:\\.\\d+)?)";
+    private static final Pattern RANGE = Pattern.compile("^" + NUMBER + "(?:~|锝瀨鑷硘鈥攟-)" + NUMBER + "$");
+    private static final Pattern PLUS_MINUS = Pattern.compile("^" + NUMBER + "(?:卤|\\+/-)" + NUMBER + "$");
+    private static final Pattern TOLERANCE_ONLY = Pattern.compile("^(?:卤|\\+/-)" + NUMBER + "$");
+    private static final Pattern COMPARE = Pattern.compile("^(<=|>=|<|>|鈮鈮�)" + NUMBER + "$");
+    private static final Pattern EXACT = Pattern.compile("^" + NUMBER + "$");
+    private static final Pattern OBVIOUS_ABNORMAL_TEXT = Pattern.compile(".*(寮傚父|涓嶅悎鏍紎澶辫触|涓嶉�氳繃|瓒呮爣|缂洪櫡).*", Pattern.CASE_INSENSITIVE);
+
+    private QualityInspectionValueValidator() {
+    }
+
+    public static String validateQualified(List<QualityInspectParam> parameters) {
+        if (parameters == null) {
+            return null;
+        }
+        for (QualityInspectParam parameter : parameters) {
+            String message = validateParameter(parameter);
+            if (message != null) {
+                return message;
+            }
+        }
+        return null;
+    }
+
+    private static String validateParameter(QualityInspectParam parameter) {
+        if (parameter == null || StringUtils.isBlank(parameter.getTestValue())) {
+            return null;
+        }
+        String testText = normalize(parameter.getTestValue());
+        if (OBVIOUS_ABNORMAL_TEXT.matcher(testText).matches()) {
+            return error(parameter, effectiveRule(parameter));
+        }
+
+        BigDecimal testValue = decimal(testText);
+        String rule = firstNumericRule(parameter.getControlValue(), parameter.getStandardValue());
+        if (rule == null) {
+            return null;
+        }
+        if (testValue == null || !matches(testValue, rule)) {
+            return error(parameter, rule);
+        }
+        return null;
+    }
+
+    private static String firstNumericRule(String controlValue, String standardValue) {
+        String control = normalize(controlValue);
+        if (isSupportedRule(control)) {
+            return control;
+        }
+        String standard = normalize(standardValue);
+        return isSupportedRule(standard) ? standard : null;
+    }
+
+    private static boolean isSupportedRule(String rule) {
+        return rule != null && (RANGE.matcher(rule).matches()
+                || PLUS_MINUS.matcher(rule).matches()
+                || TOLERANCE_ONLY.matcher(rule).matches()
+                || COMPARE.matcher(rule).matches()
+                || EXACT.matcher(rule).matches());
+    }
+
+    private static boolean matches(BigDecimal value, String rule) {
+        Matcher matcher = RANGE.matcher(rule);
+        if (matcher.matches()) {
+            BigDecimal first = new BigDecimal(matcher.group(1));
+            BigDecimal second = new BigDecimal(matcher.group(2));
+            BigDecimal min = first.min(second);
+            BigDecimal max = first.max(second);
+            return value.compareTo(min) >= 0 && value.compareTo(max) <= 0;
+        }
+        matcher = PLUS_MINUS.matcher(rule);
+        if (matcher.matches()) {
+            BigDecimal center = new BigDecimal(matcher.group(1));
+            BigDecimal tolerance = new BigDecimal(matcher.group(2)).abs();
+            return value.compareTo(center.subtract(tolerance)) >= 0
+                    && value.compareTo(center.add(tolerance)) <= 0;
+        }
+        Matcher toleranceOnly = TOLERANCE_ONLY.matcher(rule);
+        if (toleranceOnly.matches()) {
+            BigDecimal tolerance = new BigDecimal(toleranceOnly.group(1)).abs();
+            return value.abs().compareTo(tolerance) <= 0;
+        }
+        matcher = COMPARE.matcher(rule);
+        if (matcher.matches()) {
+            int compared = value.compareTo(new BigDecimal(matcher.group(2)));
+            String operator = matcher.group(1);
+            if ("<".equals(operator)) return compared < 0;
+            if (">".equals(operator)) return compared > 0;
+            if ("<=".equals(operator) || "鈮�".equals(operator)) return compared <= 0;
+            return compared >= 0;
+        }
+        matcher = EXACT.matcher(rule);
+        return matcher.matches() && value.compareTo(new BigDecimal(matcher.group(1))) == 0;
+    }
+
+    private static BigDecimal decimal(String value) {
+        try {
+            return StringUtils.isBlank(value) ? null : new BigDecimal(value);
+        } catch (NumberFormatException ignored) {
+            return null;
+        }
+    }
+
+    private static String normalize(String value) {
+        return StringUtils.isBlank(value) ? null : value.trim().replaceAll("\\s+", "").replace(",", "");
+    }
+
+    private static String effectiveRule(QualityInspectParam parameter) {
+        return StringUtils.isNotBlank(parameter.getControlValue())
+                ? parameter.getControlValue() : parameter.getStandardValue();
+    }
+
+    private static String error(QualityInspectParam parameter, String rule) {
+        String item = StringUtils.defaultIfBlank(parameter.getParameterItem(), "鏈懡鍚嶆寚鏍�");
+        String standard = StringUtils.defaultIfBlank(rule, "宸查厤缃爣鍑�");
+        return "妫�楠岄」鐩��" + item + "鈥濈殑妫�楠屽�尖��" + parameter.getTestValue()
+                + "鈥濅笉绗﹀悎鏍囧噯锛�" + standard + "锛夛紝涓嶈兘鎸夊悎鏍间繚瀛�";
+    }
+}
diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml
new file mode 100644
index 0000000..99f6727
--- /dev/null
+++ b/src/main/resources/application-dev.yml
@@ -0,0 +1,223 @@
+# 鑺-浠撳偍鐗╂祦绯荤粺椤圭洰鐩稿叧閰嶇疆
+inspur:
+  appId: 1211004787244400640
+  appSecret: qZw3MjAwMTIwMjYxNTAxNDYyOTEy0U
+ruoyi:
+  # 鍚嶇О
+  name: RuoYi
+  # 鐗堟湰
+  version: 3.8.9
+  # 鐗堟潈骞翠唤
+  copyrightYear: 2025
+  # 鏂囦欢璺緞 绀轰緥锛� Windows閰嶇疆D:/ruoyi/uploadPath锛孡inux閰嶇疆 /home/ruoyi/uploadPath锛�
+  profile: /center-lims/mis/file
+
+  # 鑾峰彇ip鍦板潃寮�鍏�
+  addressEnabled: false
+  # 楠岃瘉鐮佺被鍨� math 鏁板瓧璁$畻 char 瀛楃楠岃瘉
+  captchaType: math
+
+# 寮�鍙戠幆澧冮厤缃�
+server:
+  # 鏈嶅姟鍣ㄧ殑HTTP绔彛锛岄粯璁や负8080
+  port: 8034
+  servlet:
+    # 搴旂敤鐨勮闂矾寰�
+    context-path: /
+  tomcat:
+    # tomcat鐨刄RI缂栫爜
+    uri-encoding: UTF-8
+    # 杩炴帴鏁版弧鍚庣殑鎺掗槦鏁帮紝榛樿涓�100
+    accept-count: 1000
+    threads:
+      # tomcat鏈�澶х嚎绋嬫暟锛岄粯璁や负200
+      max: 800
+      # Tomcat鍚姩鍒濆鍖栫殑绾跨▼鏁帮紝榛樿鍊�10
+      min-spare: 100
+
+# 鏃ュ織閰嶇疆
+logging:
+  level:
+    com.ruoyi: warn
+    org.springframework: warn
+
+minio:
+  endpoint: http://114.132.189.42/
+  port: 7019
+  secure: false
+  accessKey: admin
+  secretKey: 12345678
+  preview-expiry: 24 # 棰勮鍦板潃榛樿24灏忔椂
+  default-bucket: uploadPath
+# 鐢ㄦ埛閰嶇疆
+user:
+  password:
+    # 瀵嗙爜鏈�澶ч敊璇鏁�
+    maxRetryCount: 5
+    # 瀵嗙爜閿佸畾鏃堕棿锛堥粯璁�10鍒嗛挓锛�
+    lockTime: 10
+
+# Spring閰嶇疆
+spring:
+  datasource:
+    type: com.alibaba.druid.pool.DruidDataSource
+    driverClassName: com.mysql.cj.jdbc.Driver
+    druid:
+      # 涓诲簱鏁版嵁婧�
+      master:
+        url: jdbc:mysql://10.136.65.135:3306/zlglxt-ruoyi-xinlan?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
+        username: root
+        password: zttZTT123!
+      # 浠庡簱鏁版嵁婧�
+      slave:
+        # 浠庢暟鎹簮寮�鍏�/榛樿鍏抽棴
+        enabled: false
+        url:
+        username:
+        password:
+      # 鍒濆杩炴帴鏁�
+      initialSize: 5
+      # 鏈�灏忚繛鎺ユ睜鏁伴噺
+      minIdle: 10
+      # 鏈�澶ц繛鎺ユ睜鏁伴噺
+      maxActive: 20
+      # 閰嶇疆鑾峰彇杩炴帴绛夊緟瓒呮椂鐨勬椂闂�
+      maxWait: 60000
+      # 閰嶇疆杩炴帴瓒呮椂鏃堕棿
+      connectTimeout: 30000
+      # 閰嶇疆缃戠粶瓒呮椂鏃堕棿
+      socketTimeout: 60000
+      # 閰嶇疆闂撮殧澶氫箙鎵嶈繘琛屼竴娆℃娴嬶紝妫�娴嬮渶瑕佸叧闂殑绌洪棽杩炴帴锛屽崟浣嶆槸姣
+      timeBetweenEvictionRunsMillis: 60000
+      # 閰嶇疆涓�涓繛鎺ュ湪姹犱腑鏈�灏忕敓瀛樼殑鏃堕棿锛屽崟浣嶆槸姣
+      minEvictableIdleTimeMillis: 300000
+      # 閰嶇疆涓�涓繛鎺ュ湪姹犱腑鏈�澶х敓瀛樼殑鏃堕棿锛屽崟浣嶆槸姣
+      maxEvictableIdleTimeMillis: 900000
+      # 閰嶇疆妫�娴嬭繛鎺ユ槸鍚︽湁鏁�
+      validationQuery: SELECT 1 FROM DUAL
+      testWhileIdle: true
+      testOnBorrow: false
+      testOnReturn: false
+      webStatFilter:
+        enabled: true
+      statViewServlet:
+        enabled: true
+        # 璁剧疆鐧藉悕鍗曪紝涓嶅~鍒欏厑璁告墍鏈夎闂�
+        allow:
+        url-pattern: /druid/*
+        # 鎺у埗鍙扮鐞嗙敤鎴峰悕鍜屽瘑鐮�
+        login-username: ruoyi
+        login-password: 123456
+      filter:
+        stat:
+          enabled: true
+          # 鎱QL璁板綍
+          log-slow-sql: true
+          slow-sql-millis: 1000
+          merge-sql: true
+        wall:
+          config:
+            multi-statement-allow: true
+  # 璧勬簮淇℃伅
+  messages:
+    # 鍥介檯鍖栬祫婧愭枃浠惰矾寰�
+    basename: i18n/messages
+  # 鏂囦欢涓婁紶
+  servlet:
+    multipart:
+      # 鍗曚釜鏂囦欢澶у皬
+      max-file-size: 1GB
+      # 璁剧疆鎬讳笂浼犵殑鏂囦欢澶у皬
+      max-request-size: 2GB
+  # 鏈嶅姟妯″潡
+  devtools:
+    restart:
+      # 鐑儴缃插紑鍏�
+      enabled: false
+  # redis 閰嶇疆
+  redis:
+    # 鍦板潃
+    #    host: 127.0.0.1
+    host: 10.136.65.135
+    # 绔彛锛岄粯璁や负6379
+    port: 6379
+    # 鏁版嵁搴撶储寮�
+    database: 0
+    # 瀵嗙爜
+    password: zttZTT123!
+    #    password: 123456
+
+    # 杩炴帴瓒呮椂鏃堕棿
+    timeout: 10s
+    lettuce:
+      pool:
+        # 杩炴帴姹犱腑鐨勬渶灏忕┖闂茶繛鎺�
+        min-idle: 0
+        # 杩炴帴姹犱腑鐨勬渶澶х┖闂茶繛鎺�
+        max-idle: 8
+        # 杩炴帴姹犵殑鏈�澶ф暟鎹簱杩炴帴鏁�
+        max-active: 8
+        # #杩炴帴姹犳渶澶ч樆濉炵瓑寰呮椂闂达紙浣跨敤璐熷�艰〃绀烘病鏈夐檺鍒讹級
+        max-wait: -1ms
+
+# token閰嶇疆
+token:
+  # 浠ょ墝鑷畾涔夋爣璇�
+  header: Authorization
+  # 浠ょ墝瀵嗛挜
+  secret: abcdefghijklmnopqrstuvwxyz
+  # 浠ょ墝鏈夋晥鏈燂紙榛樿30鍒嗛挓锛�
+  expireTime: 450
+
+# MyBatis Plus閰嶇疆
+mybatis-plus:
+  # 鎼滅储鎸囧畾鍖呭埆鍚�   鏍规嵁鑷繁鐨勯」鐩潵
+  typeAliasesPackage: com.ruoyi.**.pojo
+  # 閰嶇疆mapper鐨勬壂鎻忥紝鎵惧埌鎵�鏈夌殑mapper.xml鏄犲皠鏂囦欢
+  mapperLocations: classpath*:mapper/**/*Mapper.xml
+  # 鍔犺浇鍏ㄥ眬鐨勯厤缃枃浠�
+  configLocation: classpath:mybatis/mybatis-config.xml
+  global-config:
+    enable-sql-runner: true
+    db-config:
+      id-type: auto
+
+# PageHelper鍒嗛〉鎻掍欢
+pagehelper:
+  helperDialect: mysql
+  supportMethodsArguments: true
+  params: count=countSql
+
+# Swagger閰嶇疆
+swagger:
+  # 鏄惁寮�鍚痵wagger
+  enabled: false
+  # 璇锋眰鍓嶇紑
+  pathMapping: /dev-api
+
+# 闃叉XSS鏀诲嚮
+xss:
+  # 杩囨护寮�鍏�
+  enabled: true
+  # 鎺掗櫎閾炬帴锛堝涓敤閫楀彿鍒嗛殧锛�
+  excludes: /system/notice
+  # 鍖归厤閾炬帴
+  urlPatterns: /system/*,/monitor/*,/tool/*
+
+# 浠g爜鐢熸垚
+gen:
+  # 浣滆��
+  author: ruoyi
+  # 榛樿鐢熸垚鍖呰矾寰� system 闇�鏀规垚鑷繁鐨勬ā鍧楀悕绉� 濡� system monitor tool
+  packageName: com.ruoyi.project.system
+  # 鑷姩鍘婚櫎琛ㄥ墠缂�锛岄粯璁ゆ槸true
+  autoRemovePre: false
+  # 琛ㄥ墠缂�锛堢敓鎴愮被鍚嶄笉浼氬寘鍚〃鍓嶇紑锛屽涓敤閫楀彿鍒嗛殧锛�
+  tablePrefix: sys_
+  # 鏄惁鍏佽鐢熸垚鏂囦欢瑕嗙洊鍒版湰鍦帮紙鑷畾涔夎矾寰勶級锛岄粯璁や笉鍏佽
+  allowOverwrite: false
+
+# 鏈湴寮�鍙戞枃浠剁洰褰�
+file:
+  temp-dir: "L:/20260813xxinruan/浜岀骇椤圭洰娴疆寮�鍙�/璐ㄩ噺绠$悊妫�娴嬬郴缁�/.tmp/uploads/temp"
+  upload-dir: "L:/20260813xxinruan/浜岀骇椤圭洰娴疆寮�鍙�/璐ㄩ噺绠$悊妫�娴嬬郴缁�/.tmp/uploads/files"
diff --git a/src/main/resources/application-tide.yml b/src/main/resources/application-tide.yml
index fc862e8..48013c5 100644
--- a/src/main/resources/application-tide.yml
+++ b/src/main/resources/application-tide.yml
@@ -216,3 +216,8 @@
   tablePrefix: sys_
   # 鏄惁鍏佽鐢熸垚鏂囦欢瑕嗙洊鍒版湰鍦帮紙鑷畾涔夎矾寰勶級锛岄粯璁や笉鍏佽
   allowOverwrite: false
+
+# 鏈湴寮�鍙戞枃浠剁洰褰�
+file:
+  temp-dir: "L:/20260813xxinruan/浜岀骇椤圭洰娴疆寮�鍙�/璐ㄩ噺绠$悊妫�娴嬬郴缁�/.tmp/uploads/temp"
+  upload-dir: "L:/20260813xxinruan/浜岀骇椤圭洰娴疆寮�鍙�/璐ㄩ噺绠$悊妫�娴嬬郴缁�/.tmp/uploads/files"
diff --git a/src/main/resources/mapper/quality/QualityTestStandardMapper.xml b/src/main/resources/mapper/quality/QualityTestStandardMapper.xml
index a17ac3e..16d4ffd 100644
--- a/src/main/resources/mapper/quality/QualityTestStandardMapper.xml
+++ b/src/main/resources/mapper/quality/QualityTestStandardMapper.xml
@@ -24,11 +24,10 @@
         SELECT qts.*
         FROM quality_test_standard qts
         left join quality_test_standard_binding qtsb on qtsb.test_standard_id = qts.id
-        left join product_process pp on qts.process_id = pp.id
         WHERE qtsb.product_id = #{productId}
         AND qts.inspect_type = #{inspectType}
-        <if test="process!='' and process!=null">
-            and pp.name = #{process}
+        <if test="processId != null">
+            and qts.process_id = #{processId}
         </if>
         order by qts.id desc
     </select>
diff --git a/src/main/resources/mapper/system/SysUserMapper.xml b/src/main/resources/mapper/system/SysUserMapper.xml
index 5ac9a4d..6ffc9de 100644
--- a/src/main/resources/mapper/system/SysUserMapper.xml
+++ b/src/main/resources/mapper/system/SysUserMapper.xml
@@ -98,6 +98,25 @@
 		${params.dataScope}
 	</select>
 
+	<select id="selectActiveUsers" resultMap="SysUserResult">
+		SELECT u.user_id,
+		       u.user_name,
+		       COALESCE(NULLIF(u.nick_name, ''), u.user_name) AS nick_name,
+		       u.status,
+		       u.tenant_id,
+		       GROUP_CONCAT(DISTINCT d.dept_name ORDER BY d.dept_name SEPARATOR ', ') AS dept_names
+		FROM sys_user u
+		LEFT JOIN sys_user_dept ud ON ud.user_id = u.user_id
+		LEFT JOIN sys_dept d ON d.dept_id = ud.dept_id
+		WHERE u.status = '0'
+		  AND u.del_flag = '0'
+		<if test="tenantId != null">
+		  AND u.tenant_id = #{tenantId}
+		</if>
+		GROUP BY u.user_id, u.user_name, u.nick_name, u.status, u.tenant_id
+		ORDER BY nick_name, u.user_name
+	</select>
+
 	<select id="selectAllocatedList" parameterType="com.ruoyi.project.system.domain.SysUser" resultMap="SysUserResult">
 	    select distinct u.user_id,  u.user_name, u.nick_name, u.email, u.phonenumber, u.status, u.create_time
 	    from sys_user u
diff --git a/src/test/java/com/ruoyi/quality/controller/QualityInspectControllerTest.java b/src/test/java/com/ruoyi/quality/controller/QualityInspectControllerTest.java
index 9b781b4..5b34a72 100644
--- a/src/test/java/com/ruoyi/quality/controller/QualityInspectControllerTest.java
+++ b/src/test/java/com/ruoyi/quality/controller/QualityInspectControllerTest.java
@@ -70,6 +70,35 @@
     }
 
     @Test
+    void addRejectsOutOfRangeValueWhenMarkedQualified() {
+        QualityInspectDto dto = validInspection(0);
+        dto.getQualityInspectParams().get(0).setTestValue("80");
+
+        AjaxResult result = controller.add(dto);
+
+        assertTrue(result.isError());
+        assertTrue(String.valueOf(result.get(AjaxResult.MSG_TAG)).contains("涓嶈兘鎸夊悎鏍间繚瀛�"));
+        verify(inspectService, never()).add(any());
+    }
+
+    @Test
+    void assignInspectorOnlyUpdatesInspectorField() {
+        QualityInspect request = new QualityInspect();
+        request.setId(9L);
+        request.setCheckName("寮犳楠�");
+        QualityInspect stored = new QualityInspect();
+        stored.setId(9L);
+        stored.setInspectState(0);
+        when(inspectService.getById(9L)).thenReturn(stored);
+        when(inspectService.updateById(any(QualityInspect.class))).thenReturn(true);
+
+        AjaxResult result = controller.assignInspector(request);
+
+        assertTrue(result.isSuccess());
+        verify(inspectService).updateById(any(QualityInspect.class));
+    }
+
+    @Test
     void processInspectionRequiresProcess() {
         QualityInspectDto dto = validInspection(1);
         dto.setProcess(null);
diff --git a/src/test/java/com/ruoyi/quality/controller/QualityInspectParamControllerTest.java b/src/test/java/com/ruoyi/quality/controller/QualityInspectParamControllerTest.java
new file mode 100644
index 0000000..09e36e1
--- /dev/null
+++ b/src/test/java/com/ruoyi/quality/controller/QualityInspectParamControllerTest.java
@@ -0,0 +1,49 @@
+package com.ruoyi.quality.controller;
+
+import com.ruoyi.framework.web.domain.AjaxResult;
+import com.ruoyi.quality.pojo.QualityInspect;
+import com.ruoyi.quality.pojo.QualityInspectParam;
+import com.ruoyi.quality.service.IQualityInspectParamService;
+import com.ruoyi.quality.service.IQualityInspectService;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import java.util.Collections;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.anyList;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@ExtendWith(MockitoExtension.class)
+class QualityInspectParamControllerTest {
+
+    @Mock private IQualityInspectParamService paramService;
+    @Mock private IQualityInspectService inspectService;
+    @InjectMocks private QualityInspectParamController controller;
+
+    @Test
+    void updateRejectsOutOfRangeValueForQualifiedInspection() {
+        QualityInspect inspect = new QualityInspect();
+        inspect.setId(3L);
+        inspect.setInspectState(0);
+        inspect.setCheckResult("鍚堟牸");
+        when(inspectService.getById(3L)).thenReturn(inspect);
+
+        QualityInspectParam parameter = new QualityInspectParam();
+        parameter.setId(5L);
+        parameter.setInspectId(3L);
+        parameter.setParameterItem("鍘氬害");
+        parameter.setControlValue("1.95~2.05");
+        parameter.setTestValue("99");
+
+        AjaxResult result = controller.update(Collections.singletonList(parameter));
+
+        assertTrue(result.isError());
+        verify(paramService, never()).updateBatchById(anyList());
+    }
+}

--
Gitblit v1.9.3