zhuguifei
18 小时以前 fe8db21391c7bc26044084ee4f0aa970994b56c7
新增预测性维护两个页面部分接口
已修改3个文件
已添加3个文件
441 ■■■■■ 文件已修改
ruoyi-modules/lb-qms/src/main/java/cn/shlanbao/qms/controller/LbQualityController.java 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/lb-qms/src/main/java/cn/shlanbao/qms/domain/LbBatch.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/lb-qms/src/main/java/cn/shlanbao/qms/domain/vo/LbBatchVo.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/lb-qms/src/main/java/cn/shlanbao/qms/service/ILbQualityService.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/lb-qms/src/main/java/cn/shlanbao/qms/service/impl/LbBatchServiceImpl.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/lb-qms/src/main/java/cn/shlanbao/qms/service/impl/LbQualityServiceImpl.java 309 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/lb-qms/src/main/java/cn/shlanbao/qms/controller/LbQualityController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,66 @@
package cn.shlanbao.qms.controller;
import cn.shlanbao.qms.domain.bo.LbBatchBo;
import cn.shlanbao.qms.domain.vo.LbBatchVo;
import cn.shlanbao.qms.service.ILbQualityService;
import lombok.RequiredArgsConstructor;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.Map;
/**
 * AI质量分析-预测性维护
 */
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/qms/quality")
public class LbQualityController {
    private final ILbQualityService qualityService;
    /**
     * å¹´åº¦è´¨é‡å¥åº·çއ
     */
    @GetMapping("/health")
    public Map<String, Object> health() {
        return qualityService.queryQualityHealth();
    }
    /**
     * ä»Šæ—¥æ‰¹æ¬¡
     */
    @GetMapping("/batch")
    public TableDataInfo<LbBatchVo> queryBatchList(LbBatchBo bo, PageQuery pageQuery) {
        return qualityService.queryBatchList(bo, pageQuery);
    }
    /**
     * æŸ¥è¯¢è¿‘一月批次趋势
     * @return
     */
    @GetMapping("/pwbatch")
    public Map<String,Object> queryPwbatchList() {
        return qualityService.queryPwbatchList();
    }
    /**
     * æŸ¥è¯¢è¿‘一月NG top5
     * @return
     */
    @GetMapping("/ngrank")
    public Map<String,Object> ngrank() {
        return qualityService.queryNgrankList();
    }
}
ruoyi-modules/lb-qms/src/main/java/cn/shlanbao/qms/domain/LbBatch.java
@@ -1,11 +1,13 @@
package cn.shlanbao.qms.domain;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serial;
import java.util.Date;
/**
 * æ‰¹æ¬¡ç®¡ç†å¯¹è±¡ lb_batch
@@ -31,6 +33,8 @@
     * æ‰¹æ¬¡å·
     */
    private String batchCode;
    @JsonFormat(pattern = "yyyy-MM-dd hh:mm:ss")
    private Date batchTime;
    /**
     * äº§å“åž‹å·
ruoyi-modules/lb-qms/src/main/java/cn/shlanbao/qms/domain/vo/LbBatchVo.java
@@ -3,6 +3,7 @@
import cn.shlanbao.qms.domain.LbBatch;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.dromara.common.excel.annotation.ExcelDictFormat;
import org.dromara.common.excel.convert.ExcelDictConvert;
import io.github.linpeilie.annotations.AutoMapper;
@@ -40,6 +41,10 @@
    @ExcelProperty(value = "批次号")
    private String batchCode;
    @JsonFormat(pattern = "yyyy-MM-dd hh:mm:ss")
    private Date batchTime;
    /**
     * äº§å“åž‹å·
     */
ruoyi-modules/lb-qms/src/main/java/cn/shlanbao/qms/service/ILbQualityService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,32 @@
package cn.shlanbao.qms.service;
import cn.shlanbao.qms.domain.bo.LbBatchBo;
import cn.shlanbao.qms.domain.vo.LbBatchVo;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import java.util.Map;
public interface ILbQualityService {
    /**
     * æŸ¥è¯¢å¹´åº¦è´¨é‡å¥åº·åº¦
     */
    Map<String, Object> queryQualityHealth();
    /**
     * æŸ¥è¯¢ä»Šå¤©æ‰¹æ¬¡
     * @return
     */
    TableDataInfo<LbBatchVo> queryBatchList(LbBatchBo bo, PageQuery pageQuery);
    /**
     * æŸ¥è¯¢è¿‘一月批次趋势
     * @return
     */
    Map<String, Object> queryPwbatchList();
    /**
     * æŸ¥è¯¢è¿‘一月ngrank
     * @return
     */
    Map<String, Object> queryNgrankList();
}
ruoyi-modules/lb-qms/src/main/java/cn/shlanbao/qms/service/impl/LbBatchServiceImpl.java
@@ -1,5 +1,8 @@
package cn.shlanbao.qms.service.impl;
import cn.shlanbao.qms.domain.LbSensorResult;
import cn.shlanbao.qms.domain.vo.LbSensorResultVo;
import cn.shlanbao.qms.mapper.LbSensorResultMapper;
import cn.shlanbao.qms.service.ILbTestResultService;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
@@ -31,7 +34,7 @@
public class LbBatchServiceImpl implements ILbBatchService {
    private final LbBatchMapper baseMapper;
    private final LbSensorResultMapper sensorResultMapper;
    private final ILbTestResultService testResultService;
    /**
@@ -42,7 +45,22 @@
     */
    @Override
    public LbBatchVo queryById(Long id){
        return baseMapper.selectVoById(id);
        LbBatchVo lbBatchVo = baseMapper.selectVoById(id);
        if(lbBatchVo!=null){
            LambdaQueryWrapper<LbSensorResult> sensorResultWrapper = new LambdaQueryWrapper<>();
            sensorResultWrapper.eq(LbSensorResult::getBatchCode, lbBatchVo.getBatchCode());
            List<LbSensorResultVo> sensorResultVoList = sensorResultMapper.selectVoList(sensorResultWrapper);
            long okCount = sensorResultVoList.stream()
                .filter(vo -> "OK".equals(vo.getJudgeResult()))
                .count();
            long ngCount = sensorResultVoList.stream()
                .filter(vo -> "NG".equals(vo.getJudgeResult()))
                .count();
            lbBatchVo.setOkNum(okCount);
            lbBatchVo.setNgNum(ngCount);
        }
        return lbBatchVo;
    }
    /**
@@ -83,6 +101,9 @@
        lqw.eq(bo.getUserId() != null, LbBatch::getUserId, bo.getUserId());
        lqw.eq(bo.getNum() != null, LbBatch::getNum, bo.getNum());
        lqw.orderByDesc(LbBatch::getBatchCode);
        if(params!=null && params.containsKey("startTime")&&params.containsKey("endTime")) {
            lqw.between(LbBatch::getBatchTime,params.get("startTime"),params.get("endTime"));
        }
        return lqw;
    }
ruoyi-modules/lb-qms/src/main/java/cn/shlanbao/qms/service/impl/LbQualityServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,309 @@
package cn.shlanbao.qms.service.impl;
import cn.hutool.core.date.DateUtil;
import cn.shlanbao.qms.domain.LbBatch;
import cn.shlanbao.qms.domain.LbSensorResult;
import cn.shlanbao.qms.domain.LbTestResult;
import cn.shlanbao.qms.domain.bo.LbBatchBo;
import cn.shlanbao.qms.domain.vo.LbBatchVo;
import cn.shlanbao.qms.domain.vo.LbSensorResultVo;
import cn.shlanbao.qms.domain.vo.LbTestResultVo;
import cn.shlanbao.qms.mapper.LbBatchMapper;
import cn.shlanbao.qms.mapper.LbSensorResultMapper;
import cn.shlanbao.qms.mapper.LbTestResultMapper;
import cn.shlanbao.qms.service.ILbQualityService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;
@RequiredArgsConstructor
@Service
public class LbQualityServiceImpl implements ILbQualityService {
    private final LbBatchMapper batchMapper;
    private final LbSensorResultMapper sensorResultMapper;
    private final LbTestResultMapper testResultMapper;
    @Override
    public Map<String, Object> queryQualityHealth() {
        Map<String, Object> result = new HashMap<>(2);
        // èŽ·å–å½“å‰å¹´ä»½
        int currentYear = DateUtil.year(DateUtil.date());
        // æž„建查询条件:查询当前年份的数据
        QueryWrapper<LbSensorResult> queryWrapper = new QueryWrapper<>();
        queryWrapper.likeRight("batch_code", String.valueOf(currentYear));
        // æŸ¥è¯¢æ•°æ®
        List<LbSensorResultVo> sensorResultList = sensorResultMapper.selectVoList(queryWrapper);
        if (CollectionUtils.isEmpty(sensorResultList)) {
            result.put("xAxis", Collections.emptyList());
            result.put("yAxis", Collections.emptyList());
            return result;
        }
        // æŒ‰å¹´æœˆåˆ†ç»„(batch_code前6位)
        Map<String, List<LbSensorResultVo>> monthlyGroup = sensorResultList.stream()
            .filter(vo -> vo != null && StringUtils.isNotBlank(vo.getBatchCode()))
            .collect(Collectors.groupingBy(vo -> vo.getBatchCode().substring(0, 6)));
        // å‡†å¤‡x轴和y轴数据
        List<String> xAxis = new ArrayList<>();
        List<Map<String, Object>> yAxis = new ArrayList<>();
        // å¤„理每个月的数据
        monthlyGroup.entrySet().stream()
            .sorted(Map.Entry.comparingByKey())
            .forEach(entry -> {
                String monthKey = entry.getKey();
                List<LbSensorResultVo> monthlyData = entry.getValue();
                if (CollectionUtils.isEmpty(monthlyData)) {
                    return;
                }
                // æ·»åŠ x轴数据
                xAxis.add(monthKey.substring(4, 6) + "月");
                // è®¡ç®—合格率
                long passCount = monthlyData.stream()
                    .filter(vo -> "OK".equalsIgnoreCase(vo.getJudgeResult()))
                    .count();
                double passRate = (double) passCount / monthlyData.size();
                // æ·»åŠ y轴数据
                Map<String, Object> monthData = new HashMap<>(1);
                monthData.put("value", String.format("%.1f", (passRate * 10)));
                yAxis.add(monthData);
            });
        result.put("xAxis", xAxis);
        result.put("yAxis", yAxis);
        return result;
    }
    @Override
    public TableDataInfo<LbBatchVo> queryBatchList(LbBatchBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<LbBatch> lqw = buildQueryWrapper(bo);
        Page<LbBatchVo> result = batchMapper.selectVoPage(pageQuery.build(), lqw);
        List<LbBatchVo> records = result.getRecords();
        if (records.isEmpty()) {
            return TableDataInfo.build(result);
        }
        List<String> batchCodes = records.stream()
            .map(LbBatchVo::getBatchCode)
            .collect(Collectors.toList());
        LambdaQueryWrapper<LbSensorResult> sensorResultWrapper = new LambdaQueryWrapper<>();
        sensorResultWrapper.in(LbSensorResult::getBatchCode, batchCodes);
        List<LbSensorResultVo> sensorResultVoList = sensorResultMapper.selectVoList(sensorResultWrapper);
        // ä¸€æ¬¡æ€§ç»Ÿè®¡OK和总数
        Map<String, BatchStats> statsMap = sensorResultVoList.stream()
            .collect(Collectors.groupingBy(
                LbSensorResultVo::getBatchCode,
                Collectors.collectingAndThen(
                    Collectors.toList(),
                    list -> {
                        long okCount = list.stream()
                            .filter(vo -> "OK".equals(vo.getJudgeResult()))
                            .count();
                        return new BatchStats(okCount, list.size());
                    }
                )
            ));
        // æ‰¹é‡è®¾ç½®ç»Ÿè®¡ç»“æžœ
        records.forEach(item -> {
            BatchStats stats = statsMap.get(item.getBatchCode());
            if (stats != null) {
                item.setOkNum(stats.okCount);
                item.setNgNum(stats.totalCount - stats.okCount);
            } else {
                // å¦‚果没有相关检测结果,设置为0
                item.setOkNum(0L);
                item.setNgNum(item.getNum());
            }
        });
        return TableDataInfo.build(result);
    }
    @Override
    public Map<String, Object> queryPwbatchList() {
        Map<String, Object> res = new HashMap<>(2);
        // å‡†å¤‡x轴和y轴数据
        List<String> xAxis = new ArrayList<>();
        List<Object> yAxis = new ArrayList<>();
        LambdaQueryWrapper<LbBatch> dateQuery = Wrappers.lambdaQuery();
        dateQuery.select(LbBatch::getBatchTime)
            .groupBy(LbBatch::getBatchTime)
            .orderByDesc(LbBatch::getBatchTime)  // ä¿æŒå€’序查询最近7天
            .last("LIMIT 30");
        List<LbBatchVo> dateVos = batchMapper.selectVoList(dateQuery);
        // ä»Ž Vo å¯¹è±¡ä¸­æå– batchTime
        List<Date> recentDates = dateVos.stream()
            .map(LbBatchVo::getBatchTime)
            .filter(Objects::nonNull)
            .collect(Collectors.toList());
        LambdaQueryWrapper<LbBatch> lqw = Wrappers.lambdaQuery();
        lqw.in(LbBatch::getBatchTime, recentDates)
            .orderByAsc(LbBatch::getBatchTime);  // è¿™é‡Œæ”¹ä¸ºæ­£åºï¼Œç¡®ä¿æ•°æ®æŒ‰æ—¥æœŸé¡ºåº
        List<LbBatchVo> records = batchMapper.selectVoList(lqw);
        List<String> batchCodes = records.stream()
            .map(LbBatchVo::getBatchCode)
            .collect(Collectors.toList());
        LambdaQueryWrapper<LbSensorResult> sensorResultWrapper = new LambdaQueryWrapper<>();
        sensorResultWrapper.in(LbSensorResult::getBatchCode, batchCodes);
        List<LbSensorResultVo> sensorResultVoList = sensorResultMapper.selectVoList(sensorResultWrapper);
        // è®¡ç®—每个日期的OK比例
        Map<Date, Double> dateOkRatioMap = sensorResultVoList.stream()
            .collect(Collectors.groupingBy(
                result -> {
                    String batchCode = result.getBatchCode();
                    return records.stream()
                        .filter(record -> record.getBatchCode().equals(batchCode))
                        .findFirst()
                        .map(LbBatchVo::getBatchTime)
                        .orElse(null);
                },
                Collectors.collectingAndThen(
                    Collectors.toList(),
                    list -> {
                        long okCount = list.stream()
                            .filter(result -> "ok".equalsIgnoreCase(result.getJudgeResult()))
                            .count();
                        return list.size() > 0 ? (double) okCount / list.size() : 0.0;
                    }
                )
            ))
            .entrySet().stream()
            .filter(entry -> entry.getKey() != null)
            .sorted(Map.Entry.<Date, Double>comparingByKey())  // æ”¹ä¸ºæ­£åºæŽ’序
            .collect(Collectors.toMap(
                Map.Entry::getKey,
                Map.Entry::getValue,
                (e1, e2) -> e1,
                LinkedHashMap::new
            ));
        // è¿‡æ»¤æŽ‰yAxis小于80的数据
        dateOkRatioMap.entrySet().stream()
            .filter(entry -> {
                double ratio = entry.getValue() * 100;  // è½¬æ¢ä¸ºç™¾åˆ†æ¯”
                return ratio >= 80;  // åªä¿ç•™å¤§äºŽç­‰äºŽ80的数据
            })
            .forEach(entry -> {
                Date date = entry.getKey();
                double ratio = entry.getValue() * 100;  // è½¬æ¢ä¸ºç™¾åˆ†æ¯”
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
                xAxis.add(sdf.format(date));
                yAxis.add(String.format("%.1f", ratio));
            });
        res.put("xAxis", xAxis);
        res.put("yAxis", yAxis);
        return res;
    }
    @Override
    public Map<String, Object> queryNgrankList() {
        Map<String, Object> res = new HashMap<>(2);
        LambdaQueryWrapper<LbTestResult> lqwGroup = Wrappers.lambdaQuery();
        lqwGroup.select(LbTestResult::getBatchCode)
            .groupBy(LbTestResult::getBatchCode)
            .orderByDesc(LbTestResult::getBatchCode)
            .last("LIMIT 30");
        List<LbTestResultVo> groupResultVos = testResultMapper.selectVoList(lqwGroup);
        List<String> batchCodeList = groupResultVos.stream()
            .map(LbTestResultVo::getBatchCode)
            .collect(Collectors.toList());
        LambdaQueryWrapper<LbTestResult> lqw = Wrappers.lambdaQuery();
        lqw.in(LbTestResult::getBatchCode, batchCodeList);
        lqw.eq(LbTestResult::getTestResult, "NG");
        List<LbTestResultVo> testResultVos = testResultMapper.selectVoList(lqw);
        // 1. æŒ‰testItem分组并统计数量(每行计为1)
        Map<String, Long> countByTestItem = testResultVos.stream()
            .filter(result -> "NG".equalsIgnoreCase(result.getTestResult()))
            .collect(Collectors.groupingBy(
                LbTestResultVo::getTestItem,
                Collectors.counting()
            ));
        // 2. æŒ‰æ•°é‡é™åºæŽ’序并取前5大
        List<Map.Entry<String, Long>> top5Entries = countByTestItem.entrySet().stream()
            .sorted(Map.Entry.<String, Long>comparingByValue().reversed())
            .limit(5)
            .collect(Collectors.toList());
        // 3. ç”ŸæˆxAxis和yAxis数组
        String[] xAxis = top5Entries.stream()
            .map(Map.Entry::getKey)
            .toArray(String[]::new);
        Long[] yAxis = top5Entries.stream()
            .map(Map.Entry::getValue)
            .toArray(Long[]::new);
        res.put("xAxis", xAxis);
        res.put("yAxis", yAxis);
        return res;
    }
    // å†…部统计类
    private static class BatchStats {
        long okCount;
        long totalCount;
        BatchStats(long okCount, long totalCount) {
            this.okCount = okCount;
            this.totalCount = totalCount;
        }
    }
    private LambdaQueryWrapper<LbBatch> buildQueryWrapper(LbBatchBo bo) {
        Map<String, Object> params = bo.getParams();
        LambdaQueryWrapper<LbBatch> lqw = Wrappers.lambdaQuery();
        lqw.eq(StringUtils.isNotBlank(bo.getBatchCode()), LbBatch::getBatchCode, bo.getBatchCode());
        lqw.eq(StringUtils.isNotBlank(bo.getProdModel()), LbBatch::getProdModel, bo.getProdModel());
        lqw.eq(StringUtils.isNotBlank(bo.getDeviceCode()), LbBatch::getDeviceCode, bo.getDeviceCode());
        lqw.eq(bo.getUserId() != null, LbBatch::getUserId, bo.getUserId());
        lqw.eq(bo.getNum() != null, LbBatch::getNum, bo.getNum());
        lqw.orderByDesc(LbBatch::getBatchCode);
        if (params != null && params.containsKey("startTime") && params.containsKey("endTime")) {
            lqw.between(LbBatch::getBatchTime, params.get("startTime"), params.get("endTime"));
        }
        return lqw;
    }
}