From fe8db21391c7bc26044084ee4f0aa970994b56c7 Mon Sep 17 00:00:00 2001 From: zhuguifei <zhuguifei@zhuguifeideiMac.local> Date: 星期四, 11 九月 2025 13:45:03 +0800 Subject: [PATCH] 新增预测性维护两个页面部分接口 --- ruoyi-modules/lb-qms/src/main/java/cn/shlanbao/qms/service/impl/LbBatchServiceImpl.java | 25 ++ ruoyi-modules/lb-qms/src/main/java/cn/shlanbao/qms/service/ILbQualityService.java | 32 ++++ ruoyi-modules/lb-qms/src/main/java/cn/shlanbao/qms/domain/vo/LbBatchVo.java | 5 ruoyi-modules/lb-qms/src/main/java/cn/shlanbao/qms/domain/LbBatch.java | 4 ruoyi-modules/lb-qms/src/main/java/cn/shlanbao/qms/controller/LbQualityController.java | 66 ++++++++ ruoyi-modules/lb-qms/src/main/java/cn/shlanbao/qms/service/impl/LbQualityServiceImpl.java | 309 ++++++++++++++++++++++++++++++++++++++ 6 files changed, 439 insertions(+), 2 deletions(-) diff --git a/ruoyi-modules/lb-qms/src/main/java/cn/shlanbao/qms/controller/LbQualityController.java b/ruoyi-modules/lb-qms/src/main/java/cn/shlanbao/qms/controller/LbQualityController.java new file mode 100644 index 0000000..357558d --- /dev/null +++ b/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(); + } + + + /** + * 鏌ヨ杩戜竴鏈圢G top5 + * @return + */ + @GetMapping("/ngrank") + public Map<String,Object> ngrank() { + return qualityService.queryNgrankList(); + } + + + + + +} diff --git a/ruoyi-modules/lb-qms/src/main/java/cn/shlanbao/qms/domain/LbBatch.java b/ruoyi-modules/lb-qms/src/main/java/cn/shlanbao/qms/domain/LbBatch.java index dbcb742..b680945 100644 --- a/ruoyi-modules/lb-qms/src/main/java/cn/shlanbao/qms/domain/LbBatch.java +++ b/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; /** * 浜у搧鍨嬪彿 diff --git a/ruoyi-modules/lb-qms/src/main/java/cn/shlanbao/qms/domain/vo/LbBatchVo.java b/ruoyi-modules/lb-qms/src/main/java/cn/shlanbao/qms/domain/vo/LbBatchVo.java index dbfbfff..c2cd3b3 100644 --- a/ruoyi-modules/lb-qms/src/main/java/cn/shlanbao/qms/domain/vo/LbBatchVo.java +++ b/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; + + /** * 浜у搧鍨嬪彿 */ diff --git a/ruoyi-modules/lb-qms/src/main/java/cn/shlanbao/qms/service/ILbQualityService.java b/ruoyi-modules/lb-qms/src/main/java/cn/shlanbao/qms/service/ILbQualityService.java new file mode 100644 index 0000000..42457b2 --- /dev/null +++ b/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(); + + /** + * 鏌ヨ杩戜竴鏈坣grank + * @return + */ + Map<String, Object> queryNgrankList(); +} diff --git a/ruoyi-modules/lb-qms/src/main/java/cn/shlanbao/qms/service/impl/LbBatchServiceImpl.java b/ruoyi-modules/lb-qms/src/main/java/cn/shlanbao/qms/service/impl/LbBatchServiceImpl.java index ce9ed02..20ecf84 100644 --- a/ruoyi-modules/lb-qms/src/main/java/cn/shlanbao/qms/service/impl/LbBatchServiceImpl.java +++ b/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")&¶ms.containsKey("endTime")) { + lqw.between(LbBatch::getBatchTime,params.get("startTime"),params.get("endTime")); + } return lqw; } diff --git a/ruoyi-modules/lb-qms/src/main/java/cn/shlanbao/qms/service/impl/LbQualityServiceImpl.java b/ruoyi-modules/lb-qms/src/main/java/cn/shlanbao/qms/service/impl/LbQualityServiceImpl.java new file mode 100644 index 0000000..4fd5302 --- /dev/null +++ b/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); + + // 涓�娆℃�х粺璁K鍜屾�绘暟 + 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); // 杩欓噷鏀逛负姝e簭锛岀‘淇濇暟鎹寜鏃ユ湡椤哄簭 + + 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); + + // 璁$畻姣忎釜鏃ユ湡鐨凮K姣斾緥 + 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()) // 鏀逛负姝e簭鎺掑簭 + .collect(Collectors.toMap( + Map.Entry::getKey, + Map.Entry::getValue, + (e1, e2) -> e1, + LinkedHashMap::new + )); + + // 杩囨护鎺墆Axis灏忎簬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. 鎸塼estItem鍒嗙粍骞剁粺璁℃暟閲忥紙姣忚璁′负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鍜寉Axis鏁扮粍 + 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; + } + +} -- Gitblit v1.9.3