qiangxuan 2 viikkoa sitten
vanhempi
sitoutus
2817fd4b64

+ 30 - 9
zgztBus/jeecg-module-sbm/src/main/java/org/jeecg/modules/billet/billetOriginalProductRecord/controller/BilletOriginalProductRecordController.java

@@ -10,18 +10,23 @@ import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.core.toolkit.IdWorker;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.collections.CollectionUtils;
 import org.apache.commons.lang.StringUtils;
 import org.apache.shiro.SecurityUtils;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
 import org.jeecg.common.api.vo.Result;
+import org.jeecg.common.aspect.annotation.AutoLog;
+import org.jeecg.common.system.base.controller.JeecgController;
 import org.jeecg.common.system.query.QueryGenerator;
-import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
-import com.baomidou.mybatisplus.core.metadata.IPage;
-import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import lombok.extern.slf4j.Slf4j;
 import org.jeecg.common.system.vo.LoginUser;
 import org.jeecg.common.util.DateUtils;
 import org.jeecg.common.util.oConvertUtils;
@@ -32,9 +37,9 @@ import org.jeecg.modules.billet.billetHotsend.service.IBilletHotsendBaseService;
 import org.jeecg.modules.billet.billetHotsendChangeShift.entity.BilletHotsendChangeShift;
 import org.jeecg.modules.billet.billetHotsendChangeShift.service.IBilletHotsendChangeShiftService;
 import org.jeecg.modules.billet.billetOriginalProductRecord.dto.LengthCountQueryDTO;
+import org.jeecg.modules.billet.billetOriginalProductRecord.dto.QualityInspectionQueryDTO;
 import org.jeecg.modules.billet.billetOriginalProductRecord.entity.BilletOriginalProductRecord;
 import org.jeecg.modules.billet.billetOriginalProductRecord.service.IBilletOriginalProductRecordService;
-import org.jeecg.common.system.base.controller.JeecgController;
 import org.jeecg.modules.billet.billetOriginalProductRecord.vo.*;
 import org.jeecg.modules.billet.shiftConfiguration.entity.ShiftConfiguration;
 import org.jeecg.modules.billet.shiftConfiguration.service.IShiftConfigurationService;
@@ -49,10 +54,6 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.servlet.ModelAndView;
-import io.swagger.annotations.Api;
-import io.swagger.annotations.ApiOperation;
-import org.jeecg.common.aspect.annotation.AutoLog;
-import org.apache.shiro.authz.annotation.RequiresPermissions;
 
  /**
  * @Description: 钢坯生成原始记录
@@ -95,6 +96,7 @@ public class BilletOriginalProductRecordController extends JeecgController<Bille
 
 	 @Autowired
 	 private IShiftConfigurationService shiftConfigurationService;
+	 
 	 /**
 	 * 分页列表查询
 	 *
@@ -832,6 +834,25 @@ public class BilletOriginalProductRecordController extends JeecgController<Bille
 		 return Result.OK(resultList);
 	 }
 
+
+	 @ApiOperation("质检记录定尺明细(用于菜单)")
+	 @GetMapping("/qualityInspectionMenu")
+	 public Result<Map<String, Object>> getQualityInspectionMenu(QualityInspectionQueryDTO queryDTO) {
+
+		 Map<String, Object> result = billetOriginalProductRecordService.getQualityInspectionMenu(queryDTO);
+		 return Result.OK(result);
+	 }
+
+
+	 @ApiOperation("质检记录定尺明细(用于推钢室界面)")
+	 @GetMapping("/qualityInspectionScreen")
+	 public Result<Map<String, Object>> getQualityInspectionScreen(QualityInspectionQueryDTO queryDTO) {
+
+		 Map<String, Object> result = billetOriginalProductRecordService.getQualityInspectionScreen(queryDTO);
+		 return Result.OK(result);
+	 }
+
+
 	 /**
 	  * 获取当前班次下所有炉次浇筑数据
 	  * @param ccmNo

+ 51 - 0
zgztBus/jeecg-module-sbm/src/main/java/org/jeecg/modules/billet/billetOriginalProductRecord/dto/QualityInspectionQueryDTO.java

@@ -0,0 +1,51 @@
+package org.jeecg.modules.billet.billetOriginalProductRecord.dto;
+
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.util.Date;
+
+@Data
+public class QualityInspectionQueryDTO {
+
+    /**
+     * 铸机号
+     */
+    private String ccmNo;
+
+    /**
+     * 交班id
+     */
+    private String changeShiftId;
+
+    private String heatNo;
+
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date startTime;
+
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date endTime;
+
+    @DateTimeFormat(pattern = "yyyy-MM-dd")
+    private Date queryDate;
+
+    /**
+     * 班组
+     */
+    private String shiftGroup;
+
+    /**
+     * 定尺
+     */
+    private String size;
+    /**
+     * 牌号
+     */
+    private String brandNum;
+
+    /**
+     * 班别
+     */
+    private String shift;
+
+}

+ 7 - 1
zgztBus/jeecg-module-sbm/src/main/java/org/jeecg/modules/billet/billetOriginalProductRecord/service/IBilletOriginalProductRecordService.java

@@ -2,14 +2,20 @@ package org.jeecg.modules.billet.billetOriginalProductRecord.service;
 
 
 import com.baomidou.mybatisplus.extension.service.IService;
+import org.jeecg.modules.billet.billetOriginalProductRecord.dto.QualityInspectionQueryDTO;
 import org.jeecg.modules.billet.billetOriginalProductRecord.entity.BilletOriginalProductRecord;
 
+import java.util.Map;
+
 /**
  * @Description: 钢坯生成原始记录
  * @Author: jeecg-boot
- * @Date:   2025-06-23
+ * @Date: 2025-06-23
  * @Version: V1.0
  */
 public interface IBilletOriginalProductRecordService extends IService<BilletOriginalProductRecord> {
 
+    Map<String, Object> getQualityInspectionMenu(QualityInspectionQueryDTO queryDTO);
+
+    Map<String, Object> getQualityInspectionScreen(QualityInspectionQueryDTO queryDTO);
 }

+ 523 - 1
zgztBus/jeecg-module-sbm/src/main/java/org/jeecg/modules/billet/billetOriginalProductRecord/service/impl/BilletOriginalProductRecordServiceImpl.java

@@ -1,11 +1,31 @@
 package org.jeecg.modules.billet.billetOriginalProductRecord.service.impl;
 
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.apache.commons.lang.StringUtils;
+import org.jeecg.common.util.DateUtils;
+import org.jeecg.common.util.oConvertUtils;
+import org.jeecg.modules.actualControl.billetActual.billetActual.entity.BilletRulerConfig;
+import org.jeecg.modules.actualControl.billetActual.billetActual.mapper.BilletRulerConfigMapper;
+import org.jeecg.modules.billet.billetHotsendChangeShift.entity.BilletHotsendChangeShift;
+import org.jeecg.modules.billet.billetHotsendChangeShift.service.IBilletHotsendChangeShiftService;
+import org.jeecg.modules.billet.billetOriginalProductRecord.dto.QualityInspectionQueryDTO;
 import org.jeecg.modules.billet.billetOriginalProductRecord.entity.BilletOriginalProductRecord;
 import org.jeecg.modules.billet.billetOriginalProductRecord.mapper.BilletOriginalProductRecordMapper;
 import org.jeecg.modules.billet.billetOriginalProductRecord.service.IBilletOriginalProductRecordService;
+import org.jeecg.modules.billet.billetOriginalProductRecord.vo.QualityInspectionStatisticsVO;
+import org.jeecg.modules.billet.billetOriginalProductRecord.vo.QualityInspectionVO;
+import org.jeecg.modules.carUnit.service.ISysDictService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.stereotype.Service;
 
-import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.util.*;
+import java.util.stream.Collectors;
 
 /**
  * @Description: 钢坯生成原始记录
@@ -16,4 +36,506 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 @Service
 public class BilletOriginalProductRecordServiceImpl extends ServiceImpl<BilletOriginalProductRecordMapper, BilletOriginalProductRecord> implements IBilletOriginalProductRecordService {
 
+
+    @Autowired
+    public RedisTemplate redisTemplate;
+
+    @Autowired
+    private IBilletHotsendChangeShiftService billetHotsendChangeShiftService;
+
+    @Autowired
+    private ISysDictService sysDictService;
+
+    @Autowired
+    private BilletRulerConfigMapper billetRulerConfigMapper;
+
+    @Override
+    public Map<String, Object> getQualityInspectionMenu(QualityInspectionQueryDTO queryDTO) {
+        if (queryDTO == null || StringUtils.isBlank(queryDTO.getCcmNo())) {
+            return Collections.emptyMap();
+        }
+        List<QualityInspectionVO> resultList = new ArrayList<>();
+        LambdaQueryWrapper<BilletOriginalProductRecord> oneQueryWrapper = new LambdaQueryWrapper<>();
+        oneQueryWrapper.eq(BilletOriginalProductRecord::getCcmNo, queryDTO.getCcmNo());
+        Boolean search = true;
+
+        if (oConvertUtils.isNotEmpty(queryDTO.getChangeShiftId())) {
+            LambdaQueryWrapper<BilletHotsendChangeShift> changeQueryWrapper = new LambdaQueryWrapper<>();
+            changeQueryWrapper.eq(BilletHotsendChangeShift::getId, queryDTO.getChangeShiftId()).eq(BilletHotsendChangeShift::getCcmNo, queryDTO.getCcmNo());
+            BilletHotsendChangeShift billetHotsendChangeShift = billetHotsendChangeShiftService.getOne(changeQueryWrapper);
+            if (billetHotsendChangeShift == null) {
+                return Collections.emptyMap(); // 空数据返回
+            }
+            Date startChangeTime = billetHotsendChangeShift.getCreateTime();
+            Date endChangeTime = oConvertUtils.isNotEmpty(billetHotsendChangeShift.getChangeShiftTime()) ? billetHotsendChangeShift.getChangeShiftTime() : new Date();
+            oneQueryWrapper.eq(BilletOriginalProductRecord::getShift, billetHotsendChangeShift.getShift());
+            oneQueryWrapper.eq(BilletOriginalProductRecord::getShiftGroup, billetHotsendChangeShift.getShiftGroup());
+            oneQueryWrapper.between(BilletOriginalProductRecord::getCreateTime, startChangeTime, endChangeTime);
+            search = false;
+        } else if (oConvertUtils.isNotEmpty(queryDTO.getStartTime()) && oConvertUtils.isNotEmpty(queryDTO.getEndTime())) {
+            oneQueryWrapper.between(BilletOriginalProductRecord::getCreateTime, queryDTO.getStartTime(), queryDTO.getEndTime());
+            search = false;
+        }
+
+        if (oConvertUtils.isNotEmpty(queryDTO.getQueryDate()) || search) {
+            if (oConvertUtils.isEmpty(queryDTO.getQueryDate())) {
+                String nowDate = DateUtils.getDate("yyyy-MM-dd");
+                Date startOneTime = DateUtils.getStartOfDayByDate(DateUtils.getStartOfDay(nowDate));
+                LambdaQueryWrapper<BilletHotsendChangeShift> changeQueryWrapper = new LambdaQueryWrapper<>();
+                changeQueryWrapper.eq(BilletHotsendChangeShift::getCcmNo, queryDTO.getCcmNo()).orderByDesc(BilletHotsendChangeShift::getCreateTime).last("limit 1");
+                BilletHotsendChangeShift billetHotsendChangeShift = billetHotsendChangeShiftService.getOne(changeQueryWrapper);
+                oneQueryWrapper.ge(BilletOriginalProductRecord::getCreateTime,
+                        oConvertUtils.isNotEmpty(billetHotsendChangeShift.getCreateTime()) ? billetHotsendChangeShift.getCreateTime() : startOneTime);
+            } else {
+                Date queryDate = queryDTO.getQueryDate();
+                Date startTime = DateUtils.getStartOfDayByDate(DateUtils.getStartOfDay(queryDate));
+                Date endTime = DateUtils.getEndOfDayByDate(startTime);
+                oneQueryWrapper.between(BilletOriginalProductRecord::getCreateTime, startTime, endTime);
+            }
+        }
+
+        if (oConvertUtils.isNotEmpty(queryDTO.getHeatNo())) {
+            oneQueryWrapper.eq(BilletOriginalProductRecord::getHeatNo, queryDTO.getHeatNo());
+        }
+        if (oConvertUtils.isNotEmpty(queryDTO.getSize())) {
+            oneQueryWrapper.like(BilletOriginalProductRecord::getRollClubOneDetails, queryDTO.getSize());
+        }
+        if (oConvertUtils.isNotEmpty(queryDTO.getBrandNum())) {
+            oneQueryWrapper.eq(BilletOriginalProductRecord::getGrade, queryDTO.getBrandNum());
+        }
+        oneQueryWrapper.orderByAsc(BilletOriginalProductRecord::getCreateTime);
+
+        List<BilletOriginalProductRecord> records = this.list(oneQueryWrapper);
+        Set<String> allLengths = new TreeSet<>();
+        ObjectMapper objectMapper = new ObjectMapper();
+
+        // 获取当前班次记录
+        LambdaQueryWrapper<BilletHotsendChangeShift> changeQueryWrapper = new LambdaQueryWrapper<>();
+        changeQueryWrapper.eq(BilletHotsendChangeShift::getCcmNo, queryDTO.getCcmNo())
+                .orderByDesc(BilletHotsendChangeShift::getCreateTime)
+                .last("limit 1");
+        BilletHotsendChangeShift billetHotsendChangeShift = billetHotsendChangeShiftService.getOne(changeQueryWrapper);
+
+        // 设置班次时间范围
+        Date classStartTime = billetHotsendChangeShift != null ? billetHotsendChangeShift.getCreateTime() : null;
+        Date classEndTime = billetHotsendChangeShift != null && billetHotsendChangeShift.getChangeShiftTime() != null
+                ? billetHotsendChangeShift.getChangeShiftTime()
+                : new Date();
+
+        String nowDate = DateUtils.getDate("yyyy-MM-dd");
+
+        Date startCurrentTime = DateUtils.getStartOfDayByDate(DateUtils.getStartOfDay(nowDate));
+        Date endCurrentTime = DateUtils.getEndOfDayByDate(DateUtils.getEndOfDay(nowDate));
+
+        // 汇总统计
+        Map<String, Integer> classLengthCountMap = new TreeMap<>();
+        Map<String, BigDecimal> classLengthWeightMap = new TreeMap<>();
+        Map<String, Integer> dayLengthCountMap = new TreeMap<>();
+        Map<String, BigDecimal> dayLengthWeightMap = new TreeMap<>();
+        int classTotalCount = 0;
+        BigDecimal classTotalWeight = BigDecimal.ZERO;
+        int dayTotalCount = 0;
+        BigDecimal dayTotalWeight = BigDecimal.ZERO;
+
+        for (BilletOriginalProductRecord record : records) {
+            QualityInspectionVO vo = new QualityInspectionVO();
+            vo.setHeatNo(record.getHeatNo());
+            String brandNum = Optional.ofNullable(record.getGrade())
+                    .map(bn -> sysDictService.queryDictTextByKey("billet_spec", bn))
+                    .orElseGet(() -> sysDictService.queryDictTextByKey("billet_spec", "5"));
+            vo.setBrandNum(brandNum);
+            vo.setDeliveryTime(record.getCreateTime());
+
+            Map<String, Integer> mergedCount = new HashMap<>();
+            Map<String, BigDecimal> mergedWeight = new HashMap<>();
+
+            try {
+                // --- rollClubOneDetails ---
+                JsonNode rollDetails = objectMapper.readTree(record.getRollClubOneDetails());
+                JsonNode lengthGroupCount = rollDetails.path("lengthGroupCount");
+                BigDecimal totalWeight = rollDetails.path("directRollingTotalWeight").decimalValue();
+                int totalCount = rollDetails.path("directRollingTotalCount").asInt(1);
+                if (lengthGroupCount.isObject()) {
+                    for (Iterator<Map.Entry<String, JsonNode>> it = lengthGroupCount.fields(); it.hasNext(); ) {
+                        Map.Entry<String, JsonNode> entry = it.next();
+                        String mm = entry.getKey();
+                        int count = entry.getValue().asInt(0);
+
+                        LambdaQueryWrapper<BilletRulerConfig> configQuery = new LambdaQueryWrapper<BilletRulerConfig>()
+                                .eq(BilletRulerConfig::getLength, Integer.valueOf(mm)); // mm 是字符串长度
+
+                        BilletRulerConfig rulerConfig = billetRulerConfigMapper.selectOne(configQuery);
+
+                        BigDecimal weight;
+
+                        if (rulerConfig != null && rulerConfig.getWeight() != null) {
+                            // 方式一:用定尺表配置重量
+                            weight = BigDecimal.valueOf(rulerConfig.getWeight())
+                                    .multiply(BigDecimal.valueOf(count));
+                        } else {
+                            // 方式二:用总重量按支数比例分摊
+                            weight = (totalCount > 0)
+                                    ? totalWeight.divide(BigDecimal.valueOf(totalCount), 4, RoundingMode.HALF_UP)
+                                    .multiply(BigDecimal.valueOf(count))
+                                    : BigDecimal.ZERO;
+                        }
+
+                        mergedCount.merge(mm, count, Integer::sum);
+                        mergedWeight.merge(mm, weight, BigDecimal::add);
+                    }
+                }
+            } catch (Exception ignored) {}
+
+            try {
+                // --- hotChargeLength ---
+                JsonNode hotArray = objectMapper.readTree(record.getHotChargeLength());
+                for (JsonNode node : hotArray) {
+                    String mm = node.path("hotChargeLength").asText();
+                    int count = node.path("totalCount").asInt(0);
+                    BigDecimal weight = new BigDecimal(node.path("totalWeight").asText("0"));
+                    mergedCount.merge(mm, count, Integer::sum);
+                    mergedWeight.merge(mm, weight, BigDecimal::add);
+                }
+            } catch (Exception ignored) {}
+
+            try {
+                // --- stackLength ---
+                JsonNode stackArray = objectMapper.readTree(record.getStackLength());
+                for (JsonNode node : stackArray) {
+                    String mm = node.path("stackingLength").asText();
+                    int count = node.path("stackingCount").asInt(0);
+                    BigDecimal weight = new BigDecimal(node.path("stackingWeight").asText("0"));
+                    mergedCount.merge(mm, count, Integer::sum);
+                    mergedWeight.merge(mm, weight, BigDecimal::add);
+                }
+            } catch (Exception ignored) {}
+
+            Date createTime = record.getCreateTime();
+            boolean isInClassTime = createTime != null && !createTime.before(classStartTime) && !createTime.after(classEndTime);
+            boolean isInDayTime = createTime != null && !createTime.before(startCurrentTime) && !createTime.after(endCurrentTime);
+
+            for (Map.Entry<String, Integer> entry : mergedCount.entrySet()) {
+                String mmStr = entry.getKey();
+                int count = entry.getValue();
+                BigDecimal weight = mergedWeight.getOrDefault(mmStr, BigDecimal.ZERO);
+                String mStr;
+                try {
+                    BigDecimal mm = new BigDecimal(mmStr);
+                    mStr = mm.divide(BigDecimal.valueOf(1000), 2, RoundingMode.HALF_UP).toString();
+                } catch (Exception e) {
+                    mStr = mmStr;
+                }
+                vo.getLengthCountMap().put(mStr, count);
+                allLengths.add(mStr);
+
+                if (isInClassTime) {
+                    classLengthCountMap.merge(mStr, count, Integer::sum);
+                    classLengthWeightMap.merge(mStr, weight, BigDecimal::add);
+                    classTotalCount += count;
+                    classTotalWeight = classTotalWeight.add(weight);
+                }
+                if (isInDayTime) {
+                    dayLengthCountMap.merge(mStr, count, Integer::sum);
+                    dayLengthWeightMap.merge(mStr, weight, BigDecimal::add);
+                    dayTotalCount += count;
+                    dayTotalWeight = dayTotalWeight.add(weight);
+                }
+            }
+
+            resultList.add(vo);
+        }
+
+        // 补齐定尺列
+        for (QualityInspectionVO vo : resultList) {
+            for (String length : allLengths) {
+                vo.getLengthCountMap().putIfAbsent(length, 0);
+            }
+        }
+
+        // 1. 先对定尺重量 map 统一保留4位小数(班次 + 当天)
+        Map<String, BigDecimal> roundedClassWeightMap = classLengthWeightMap.entrySet().stream()
+                .collect(Collectors.toMap(
+                        Map.Entry::getKey,
+                        e -> e.getValue().setScale(4, RoundingMode.HALF_UP)
+                ));
+
+        Map<String, BigDecimal> roundedDayWeightMap = dayLengthWeightMap.entrySet().stream()
+                .collect(Collectors.toMap(
+                        Map.Entry::getKey,
+                        e -> e.getValue().setScale(4, RoundingMode.HALF_UP)
+                ));
+
+        // 2. 总重量使用已四舍五入后的子项重量相加,再保留4位小数
+        BigDecimal roundedClassTotalWeight = roundedClassWeightMap.values().stream()
+                .reduce(BigDecimal.ZERO, BigDecimal::add)
+                .setScale(4, RoundingMode.HALF_UP);
+
+        BigDecimal roundedDayTotalWeight = roundedDayWeightMap.values().stream()
+                .reduce(BigDecimal.ZERO, BigDecimal::add)
+                .setScale(4, RoundingMode.HALF_UP);
+
+        // 3. 封装 VO 返回
+        QualityInspectionStatisticsVO stats = new QualityInspectionStatisticsVO();
+        stats.setClassLengthCountMap(classLengthCountMap);
+        stats.setClassLengthWeightMap(roundedClassWeightMap);
+        stats.setClassTotalCount(classTotalCount);
+        stats.setClassTotalWeight(roundedClassTotalWeight);
+
+        stats.setDayLengthCountMap(dayLengthCountMap);
+        stats.setDayLengthWeightMap(roundedDayWeightMap);
+        stats.setDayTotalCount(dayTotalCount);
+        stats.setDayTotalWeight(roundedDayTotalWeight);
+
+
+        Map<String, Object> result = new HashMap<>();
+        result.put("records", resultList);
+        result.put("statistics", stats);
+
+        return result;
+    }
+
+    @Override
+    public Map<String, Object> getQualityInspectionScreen(QualityInspectionQueryDTO queryDTO) {
+        if (queryDTO == null || StringUtils.isBlank(queryDTO.getCcmNo())) {
+            return Collections.emptyMap();
+        }
+        List<QualityInspectionVO> resultList = new ArrayList<>();
+        LambdaQueryWrapper<BilletOriginalProductRecord> oneQueryWrapper = new LambdaQueryWrapper<>();
+        oneQueryWrapper.eq(BilletOriginalProductRecord::getCcmNo, queryDTO.getCcmNo());
+        Boolean search = true;
+
+        if (oConvertUtils.isNotEmpty(queryDTO.getChangeShiftId())) {
+            LambdaQueryWrapper<BilletHotsendChangeShift> changeQueryWrapper = new LambdaQueryWrapper<>();
+            changeQueryWrapper.eq(BilletHotsendChangeShift::getId, queryDTO.getChangeShiftId()).eq(BilletHotsendChangeShift::getCcmNo, queryDTO.getCcmNo());
+            BilletHotsendChangeShift billetHotsendChangeShift = billetHotsendChangeShiftService.getOne(changeQueryWrapper);
+            if (billetHotsendChangeShift == null) {
+                return Collections.emptyMap(); // 空数据返回
+            }
+            Date startChangeTime = billetHotsendChangeShift.getCreateTime();
+            Date endChangeTime = oConvertUtils.isNotEmpty(billetHotsendChangeShift.getChangeShiftTime()) ? billetHotsendChangeShift.getChangeShiftTime() : new Date();
+            oneQueryWrapper.eq(BilletOriginalProductRecord::getShift, billetHotsendChangeShift.getShift());
+            oneQueryWrapper.eq(BilletOriginalProductRecord::getShiftGroup, billetHotsendChangeShift.getShiftGroup());
+            oneQueryWrapper.between(BilletOriginalProductRecord::getCreateTime, startChangeTime, endChangeTime);
+            search = false;
+        } else if (oConvertUtils.isNotEmpty(queryDTO.getStartTime()) && oConvertUtils.isNotEmpty(queryDTO.getEndTime())) {
+            oneQueryWrapper.between(BilletOriginalProductRecord::getCreateTime, queryDTO.getStartTime(), queryDTO.getEndTime());
+            search = false;
+        }
+
+        if (oConvertUtils.isNotEmpty(queryDTO.getQueryDate()) || search) {
+            if (oConvertUtils.isEmpty(queryDTO.getQueryDate())) {
+                String nowDate = DateUtils.getDate("yyyy-MM-dd");
+                Date startOneTime = DateUtils.getStartOfDayByDate(DateUtils.getStartOfDay(nowDate));
+                LambdaQueryWrapper<BilletHotsendChangeShift> changeQueryWrapper = new LambdaQueryWrapper<>();
+                changeQueryWrapper.eq(BilletHotsendChangeShift::getCcmNo, queryDTO.getCcmNo()).orderByDesc(BilletHotsendChangeShift::getCreateTime).last("limit 1");
+                BilletHotsendChangeShift billetHotsendChangeShift = billetHotsendChangeShiftService.getOne(changeQueryWrapper);
+                oneQueryWrapper.ge(BilletOriginalProductRecord::getCreateTime,
+                        oConvertUtils.isNotEmpty(billetHotsendChangeShift.getCreateTime()) ? billetHotsendChangeShift.getCreateTime() : startOneTime);
+            } else {
+                Date queryDate = queryDTO.getQueryDate();
+                Date startTime = DateUtils.getStartOfDayByDate(DateUtils.getStartOfDay(queryDate));
+                Date endTime = DateUtils.getEndOfDayByDate(startTime);
+                oneQueryWrapper.between(BilletOriginalProductRecord::getCreateTime, startTime, endTime);
+            }
+        }
+
+        if (oConvertUtils.isNotEmpty(queryDTO.getHeatNo())) {
+            oneQueryWrapper.eq(BilletOriginalProductRecord::getHeatNo, queryDTO.getHeatNo());
+        }
+        if (oConvertUtils.isNotEmpty(queryDTO.getSize())) {
+            oneQueryWrapper.like(BilletOriginalProductRecord::getRollClubOneDetails, queryDTO.getSize());
+        }
+        if (oConvertUtils.isNotEmpty(queryDTO.getBrandNum())) {
+            oneQueryWrapper.eq(BilletOriginalProductRecord::getGrade, queryDTO.getBrandNum());
+        }
+        oneQueryWrapper.orderByAsc(BilletOriginalProductRecord::getCreateTime);
+
+        List<BilletOriginalProductRecord> records = this.list(oneQueryWrapper);
+        Set<String> allLengths = new TreeSet<>();
+        ObjectMapper objectMapper = new ObjectMapper();
+
+        // 获取当前班次记录
+        LambdaQueryWrapper<BilletHotsendChangeShift> changeQueryWrapper = new LambdaQueryWrapper<>();
+        changeQueryWrapper.eq(BilletHotsendChangeShift::getCcmNo, queryDTO.getCcmNo())
+                .orderByDesc(BilletHotsendChangeShift::getCreateTime)
+                .last("limit 1");
+        BilletHotsendChangeShift billetHotsendChangeShift = billetHotsendChangeShiftService.getOne(changeQueryWrapper);
+
+        // 设置班次时间范围
+        Date classStartTime = billetHotsendChangeShift != null ? billetHotsendChangeShift.getCreateTime() : null;
+        Date classEndTime = billetHotsendChangeShift != null && billetHotsendChangeShift.getChangeShiftTime() != null
+                ? billetHotsendChangeShift.getChangeShiftTime()
+                : new Date();
+
+        String nowDate = DateUtils.getDate("yyyy-MM-dd");
+
+        Date startCurrentTime = DateUtils.getStartOfDayByDate(DateUtils.getStartOfDay(nowDate));
+        Date endCurrentTime = DateUtils.getEndOfDayByDate(DateUtils.getEndOfDay(nowDate));
+
+        // 汇总统计
+        Map<String, Integer> classLengthCountMap = new TreeMap<>();
+        Map<String, BigDecimal> classLengthWeightMap = new TreeMap<>();
+        Map<String, Integer> dayLengthCountMap = new TreeMap<>();
+        Map<String, BigDecimal> dayLengthWeightMap = new TreeMap<>();
+        int classTotalCount = 0;
+        BigDecimal classTotalWeight = BigDecimal.ZERO;
+        int dayTotalCount = 0;
+        BigDecimal dayTotalWeight = BigDecimal.ZERO;
+
+        for (BilletOriginalProductRecord record : records) {
+            QualityInspectionVO vo = new QualityInspectionVO();
+            vo.setHeatNo(record.getHeatNo());
+            String brandNum = Optional.ofNullable(record.getGrade())
+                    .map(bn -> sysDictService.queryDictTextByKey("billet_spec", bn))
+                    .orElseGet(() -> sysDictService.queryDictTextByKey("billet_spec", "5"));
+            vo.setBrandNum(brandNum);
+            vo.setDeliveryTime(record.getCreateTime());
+
+            Map<String, Integer> mergedCount = new HashMap<>();
+            Map<String, BigDecimal> mergedWeight = new HashMap<>();
+
+            try {
+                // --- rollClubOneDetails ---
+                JsonNode rollDetails = objectMapper.readTree(record.getRollClubOneDetails());
+                JsonNode lengthGroupCount = rollDetails.path("lengthGroupCount");
+                BigDecimal totalWeight = rollDetails.path("directRollingTotalWeight").decimalValue();
+                int totalCount = rollDetails.path("directRollingTotalCount").asInt(1);
+                if (lengthGroupCount.isObject()) {
+                    for (Iterator<Map.Entry<String, JsonNode>> it = lengthGroupCount.fields(); it.hasNext(); ) {
+                        Map.Entry<String, JsonNode> entry = it.next();
+                        String mm = entry.getKey();
+                        int count = entry.getValue().asInt(0);
+
+                        LambdaQueryWrapper<BilletRulerConfig> configQuery = new LambdaQueryWrapper<BilletRulerConfig>()
+                                .eq(BilletRulerConfig::getLength, Integer.valueOf(mm)); // mm 是字符串长度
+
+                        BilletRulerConfig rulerConfig = billetRulerConfigMapper.selectOne(configQuery);
+
+                        BigDecimal weight;
+
+                        if (rulerConfig != null && rulerConfig.getWeight() != null) {
+                            // 方式一:用定尺表配置重量
+                            weight = BigDecimal.valueOf(rulerConfig.getWeight())
+                                    .multiply(BigDecimal.valueOf(count));
+                        } else {
+                            // 方式二:用总重量按支数比例分摊
+                            weight = (totalCount > 0)
+                                    ? totalWeight.divide(BigDecimal.valueOf(totalCount), 4, RoundingMode.HALF_UP)
+                                    .multiply(BigDecimal.valueOf(count))
+                                    : BigDecimal.ZERO;
+                        }
+
+                        mergedCount.merge(mm, count, Integer::sum);
+                        mergedWeight.merge(mm, weight, BigDecimal::add);
+                    }
+                }
+            } catch (Exception ignored) {}
+
+            try {
+                // --- hotChargeLength ---
+                JsonNode hotArray = objectMapper.readTree(record.getHotChargeLength());
+                for (JsonNode node : hotArray) {
+                    String mm = node.path("hotChargeLength").asText();
+                    int count = node.path("totalCount").asInt(0);
+                    BigDecimal weight = new BigDecimal(node.path("totalWeight").asText("0"));
+                    mergedCount.merge(mm, count, Integer::sum);
+                    mergedWeight.merge(mm, weight, BigDecimal::add);
+                }
+            } catch (Exception ignored) {}
+
+            try {
+                // --- stackLength ---
+                JsonNode stackArray = objectMapper.readTree(record.getStackLength());
+                for (JsonNode node : stackArray) {
+                    String mm = node.path("stackingLength").asText();
+                    int count = node.path("stackingCount").asInt(0);
+                    BigDecimal weight = new BigDecimal(node.path("stackingWeight").asText("0"));
+                    mergedCount.merge(mm, count, Integer::sum);
+                    mergedWeight.merge(mm, weight, BigDecimal::add);
+                }
+            } catch (Exception ignored) {}
+
+            Date createTime = record.getCreateTime();
+            boolean isInClassTime = createTime != null && !createTime.before(classStartTime) && !createTime.after(classEndTime);
+            boolean isInDayTime = createTime != null && !createTime.before(startCurrentTime) && !createTime.after(endCurrentTime);
+
+            for (Map.Entry<String, Integer> entry : mergedCount.entrySet()) {
+                String mmStr = entry.getKey();
+                int count = entry.getValue();
+                BigDecimal weight = mergedWeight.getOrDefault(mmStr, BigDecimal.ZERO);
+                String mStr;
+                try {
+                    BigDecimal mm = new BigDecimal(mmStr);
+                    mStr = mm.divide(BigDecimal.valueOf(1000), 2, RoundingMode.HALF_UP).toString();
+                } catch (Exception e) {
+                    mStr = mmStr;
+                }
+                vo.getLengthCountMap().put(mStr, count);
+                allLengths.add(mStr);
+
+                if (isInClassTime) {
+                    classLengthCountMap.merge(mStr, count, Integer::sum);
+                    classLengthWeightMap.merge(mStr, weight, BigDecimal::add);
+                    classTotalCount += count;
+                    classTotalWeight = classTotalWeight.add(weight);
+                }
+                if (isInDayTime) {
+                    dayLengthCountMap.merge(mStr, count, Integer::sum);
+                    dayLengthWeightMap.merge(mStr, weight, BigDecimal::add);
+                    dayTotalCount += count;
+                    dayTotalWeight = dayTotalWeight.add(weight);
+                }
+            }
+
+            resultList.add(vo);
+        }
+
+        // 补齐定尺列
+        for (QualityInspectionVO vo : resultList) {
+            for (String length : allLengths) {
+                vo.getLengthCountMap().putIfAbsent(length, 0);
+            }
+        }
+
+        // 1. 先对定尺重量 map 统一保留4位小数(班次 + 当天)
+        Map<String, BigDecimal> roundedClassWeightMap = classLengthWeightMap.entrySet().stream()
+                .collect(Collectors.toMap(
+                        Map.Entry::getKey,
+                        e -> e.getValue().setScale(4, RoundingMode.HALF_UP)
+                ));
+
+        Map<String, BigDecimal> roundedDayWeightMap = dayLengthWeightMap.entrySet().stream()
+                .collect(Collectors.toMap(
+                        Map.Entry::getKey,
+                        e -> e.getValue().setScale(4, RoundingMode.HALF_UP)
+                ));
+
+        // 2. 总重量使用已四舍五入后的子项重量相加,再保留4位小数
+        BigDecimal roundedClassTotalWeight = roundedClassWeightMap.values().stream()
+                .reduce(BigDecimal.ZERO, BigDecimal::add)
+                .setScale(4, RoundingMode.HALF_UP);
+
+        BigDecimal roundedDayTotalWeight = roundedDayWeightMap.values().stream()
+                .reduce(BigDecimal.ZERO, BigDecimal::add)
+                .setScale(4, RoundingMode.HALF_UP);
+
+        // 3. 封装 VO 返回
+        QualityInspectionStatisticsVO stats = new QualityInspectionStatisticsVO();
+        stats.setClassLengthCountMap(classLengthCountMap);
+        stats.setClassLengthWeightMap(roundedClassWeightMap);
+        stats.setClassTotalCount(classTotalCount);
+        stats.setClassTotalWeight(roundedClassTotalWeight);
+
+        stats.setDayLengthCountMap(dayLengthCountMap);
+        stats.setDayLengthWeightMap(roundedDayWeightMap);
+        stats.setDayTotalCount(dayTotalCount);
+        stats.setDayTotalWeight(roundedDayTotalWeight);
+
+
+        Map<String, Object> result = new HashMap<>();
+        result.put("records", resultList);
+        result.put("statistics", stats);
+
+        return result;
+    }
 }

+ 20 - 0
zgztBus/jeecg-module-sbm/src/main/java/org/jeecg/modules/billet/billetOriginalProductRecord/vo/QualityInspectionStatisticsVO.java

@@ -0,0 +1,20 @@
+package org.jeecg.modules.billet.billetOriginalProductRecord.vo;
+
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.util.Map;
+
+@Data
+public class QualityInspectionStatisticsVO {
+
+    private Map<String, Integer> classLengthCountMap;
+    private Map<String, BigDecimal> classLengthWeightMap;
+    private int classTotalCount;
+    private BigDecimal classTotalWeight;
+
+    private Map<String, Integer> dayLengthCountMap;
+    private Map<String, BigDecimal> dayLengthWeightMap;
+    private int dayTotalCount;
+    private BigDecimal dayTotalWeight;
+}

+ 31 - 0
zgztBus/jeecg-module-sbm/src/main/java/org/jeecg/modules/billet/billetOriginalProductRecord/vo/QualityInspectionVO.java

@@ -0,0 +1,31 @@
+package org.jeecg.modules.billet.billetOriginalProductRecord.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.util.Date;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+@Data
+public class QualityInspectionVO {
+
+    /**
+     * 炉号
+     */
+    private String heatNo;
+    /**
+     * 牌号
+     */
+    private String brandNum;
+    /**
+     * 送样时间
+     */
+    @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date deliveryTime;
+
+    private Map<String, Integer> lengthCountMap = new LinkedHashMap<>();
+
+}