liu
6 天以前 f8ccc5ab312ab7c5d6c7eb7fac4e37924f513869
fix: 完善质量检验接口与质检员分配
已添加5个文件
已修改12个文件
636 ■■■■■ 文件已修改
doc/20261103_enable_process_maintenance_menu.sql 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
doc/20261103_quality_inspect_user_assignment.sql 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/project/system/mapper/SysUserMapper.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/quality/controller/QualityInspectController.java 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/quality/controller/QualityInspectParamController.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/quality/controller/QualityTestStandardController.java 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/quality/mapper/QualityTestStandardMapper.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/quality/pojo/QualityInspect.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/quality/service/IQualityTestStandardService.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/quality/service/impl/QualityTestStandardServiceImpl.java 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/quality/util/QualityInspectionValueValidator.java 135 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-dev.yml 223 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-tide.yml 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/quality/QualityTestStandardMapper.xml 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/system/SysUserMapper.xml 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/test/java/com/ruoyi/quality/controller/QualityInspectControllerTest.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/test/java/com/ruoyi/quality/controller/QualityInspectParamControllerTest.java 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
doc/20261103_enable_process_maintenance_menu.sql
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,56 @@
-- æ¢å¤â€œå·¥åºç»´æŠ¤â€èœå•(MySQL,可重复执行)。
-- è‹¥ä¾çº¦å®šï¼švisible='0' è¡¨ç¤ºæ˜¾ç¤ºï¼Œstatus='0' è¡¨ç¤ºæ­£å¸¸ã€‚
-- 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 = '工序维护';
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);
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);
    /**
     * æ ¹æ®æ¡ä»¶åˆ†é¡µæŸ¥è¯¢å·²é…ç”¨æˆ·è§’色列表
     *
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("检验记录ID不能为空");
        }
        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));
    }
    /** ä»…返回当前租户中正常、未删除的系统用户,供质检员分配使用。 */
    @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;
    }
}
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("检验记录ID不能为空");
        }
        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));
    }
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));
    }
    /**
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);
}
src/main/java/com/ruoyi/quality/pojo/QualityInspect.java
@@ -70,6 +70,11 @@
    private String checkName;
    /**
     * æ£€éªŒå‘˜å¯¹åº”的系统用户ID
     */
    private Long checkUserId;
    /**
     * å…³è”产品id
     */
    @NotBlank(message = "产品id不能为空")
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);
}
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);
    }
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 + "),不能按合格保存";
    }
}
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,Linux配置 /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的URI编码
    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
          # æ…¢SQL记录
          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:
  # æ˜¯å¦å¼€å¯swagger
  enabled: false
  # è¯·æ±‚前缀
  pathMapping: /dev-api
# é˜²æ­¢XSS攻击
xss:
  # è¿‡æ»¤å¼€å…³
  enabled: true
  # æŽ’除链接(多个用逗号分隔)
  excludes: /system/notice
  # åŒ¹é…é“¾æŽ¥
  urlPatterns: /system/*,/monitor/*,/tool/*
# ä»£ç ç”Ÿæˆ
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"
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"
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>
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
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);
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());
    }
}