| | |
| | | package org.dromara.qa.analy.service.impl; |
| | | |
| | | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; |
| | | import com.baomidou.mybatisplus.core.toolkit.BeanUtils; |
| | | import com.fasterxml.jackson.databind.ObjectMapper; |
| | | import com.baomidou.dynamic.datasource.annotation.DS; |
| | | import org.dromara.common.core.utils.MapstructUtils; |
| | | import org.dromara.common.core.utils.StringUtils; |
| | | import org.dromara.common.mybatis.core.page.TableDataInfo; |
| | |
| | | import org.dromara.qa.analy.domain.PackerTimeData; |
| | | import org.dromara.qa.analy.domain.RollerTimeData; |
| | | import org.dromara.qa.analy.domain.vo.StoreSilkDetailVo; |
| | | import org.dromara.qa.analy.mapper.RollerTimeDataMapper; |
| | | import org.dromara.qa.analy.service.IRollerTimeDataService; |
| | | import org.dromara.qa.analy.service.IPackerTimeDataService; |
| | | import org.dromara.qa.md.domain.MdShift; |
| | | import org.dromara.qa.md.domain.bo.MdShiftBo; |
| | | import org.dromara.qa.md.mapper.MdShiftMapper; |
| | | import org.dromara.qa.md.service.OracleShiftReader; |
| | | import org.springframework.stereotype.Service; |
| | | import org.dromara.qa.analy.domain.bo.StoreSilkInfoBo; |
| | |
| | | import org.dromara.qa.analy.domain.StoreSilkInfo; |
| | | import org.dromara.qa.analy.mapper.StoreSilkInfoMapper; |
| | | import org.dromara.qa.analy.service.IStoreSilkInfoService; |
| | | import org.dromara.qa.analy.mapper.FeedmatchTimeDataMapper; |
| | | |
| | | import org.dromara.qa.analy.service.IFeedmatchTimeDataService; |
| | | import java.lang.reflect.Field; |
| | | import java.sql.Timestamp; |
| | | import java.text.ParseException; |
| | | import java.text.SimpleDateFormat; |
| | | import java.util.*; |
| | | import java.time.LocalDate; |
| | | import java.time.LocalDateTime; |
| | |
| | | * @author zhuguifei |
| | | * @date 2026-03-02 |
| | | */ |
| | | @DS("oracle_zs") |
| | | @Slf4j |
| | | @RequiredArgsConstructor |
| | | @Service |
| | | public class StoreSilkInfoServiceImpl implements IStoreSilkInfoService { |
| | | |
| | | private final StoreSilkInfoMapper baseMapper; |
| | | private final FeedmatchTimeDataMapper feedmatchTimeDataMapper; |
| | | private final RollerTimeDataMapper rollerTimeDataMapper; |
| | | private final org.dromara.qa.analy.mapper.PackerTimeDataMapper packerTimeDataMapper; |
| | | private final MdShiftMapper mdShiftMapper; |
| | | private final IFeedmatchTimeDataService feedmatchTimeDataService; |
| | | private final IRollerTimeDataService rollerTimeDataService; |
| | | private final IPackerTimeDataService packerTimeDataService; |
| | | private final OracleShiftReader oracleShiftReader; |
| | | |
| | | /** |
| | |
| | | * - 查询上界使用 effectiveDistEnd:如果出料还没结束,就以当前时间作为上界,仍然能取到对应关系。 |
| | | */ |
| | | Timestamp targetTime = new Timestamp(distimebegin.getTime() + 10 * 60 * 1000); // 出料开始后10分钟 |
| | | String containerStr = StringUtils.isEmpty(containerNum) ? "" : containerNum.trim(); |
| | | //小于10的柜号补0 |
| | | if (containerStr.length() == 1) { |
| | | containerStr = "0" + containerStr; |
| | | } |
| | | LambdaQueryWrapper<FeedmatchTimeData> lqw = new LambdaQueryWrapper<>(); |
| | | String finalContainerStr = containerStr; |
| | | lqw.ge(FeedmatchTimeData::getTime, targetTime) |
| | | .le(FeedmatchTimeData::getTime, effectiveDistEnd) // 不能大于出料结束时间(出料中则使用当前时间) |
| | | .le(FeedmatchTimeData::getTime, effectiveDistEnd) |
| | | .and(wrapper -> wrapper |
| | | .like(FeedmatchTimeData::getFs11, finalContainerStr) |
| | | .or() |
| | | .like(FeedmatchTimeData::getFs12, finalContainerStr) |
| | | .or() |
| | | .like(FeedmatchTimeData::getFs21, finalContainerStr) |
| | | .or() |
| | | .like(FeedmatchTimeData::getFs22, finalContainerStr) |
| | | .or() |
| | | .like(FeedmatchTimeData::getFs31, finalContainerStr) |
| | | .or() |
| | | .like(FeedmatchTimeData::getFs32, finalContainerStr) |
| | | .or() |
| | | .like(FeedmatchTimeData::getFs41, finalContainerStr) |
| | | .or() |
| | | .like(FeedmatchTimeData::getFs42, finalContainerStr) |
| | | ) |
| | | .orderByAsc(FeedmatchTimeData::getTime) |
| | | .last("LIMIT 1"); |
| | | FeedmatchTimeData feedMatch = feedmatchTimeDataMapper.selectOne(lqw); |
| | | |
| | | FeedmatchTimeData feedMatch = feedmatchTimeDataService.selectOne(lqw); |
| | | if (feedMatch == null) { |
| | | // TODO 添加提示 |
| | | continue; |
| | | } |
| | | |
| | | // feedMatch 转map:通过反向映射快速定位“该柜对应哪个喂丝机”、“该机组对应哪个管道” |
| | | // fsRevMap:key=储丝柜号末位(如 1/2/3),value=字段名(如 fs11/fs12...) |
| | | // fsRevMap:key=储丝柜号后两位(如 01/09),value=字段名(如 fs11/fs12...) |
| | | Map<String, String> fsRevMap = new HashMap<>(); |
| | | // pipeRevMap:key=喂丝机号+管道组合(如 1x / 2x...),value=字段名(如 pipe01/pipe02...) |
| | | Map<String, String> pipeRevMap = new HashMap<>(); |
| | |
| | | throw new RuntimeException(e); |
| | | } |
| | | if (field.getName().startsWith("fs") && value != null) { |
| | | fsRevMap.put(value.toString(), field.getName()); |
| | | String key = value.toString().trim(); |
| | | if (key.length() == 1) { |
| | | key = "0" + key; |
| | | } else if (key.length() > 2) { |
| | | key = key.substring(key.length() - 2); |
| | | } |
| | | fsRevMap.put(key, field.getName()); |
| | | } else if (field.getName().startsWith("pipe") && value != null) { |
| | | pipeRevMap.put(value.toString(), field.getName()); |
| | | pipeMap.put(field.getName(),value.toString()); |
| | | } |
| | | } |
| | | // 根据储丝柜号获取喂丝机号 |
| | | String fsNum = fsRevMap.get(containerNum); |
| | | String containerKey = StringUtils.isEmpty(containerNum) ? "" : containerNum.trim(); |
| | | if (containerKey.length() == 1) { |
| | | containerKey = "0" + containerKey; |
| | | } else if (containerKey.length() > 2) { |
| | | containerKey = containerKey.substring(containerKey.length() - 2); |
| | | } |
| | | String fsNum = fsRevMap.get(containerKey); |
| | | if (StringUtils.isEmpty(fsNum)) { |
| | | // TODO 喂丝机号空返回信息 |
| | | continue; |
| | |
| | | // 卷包产量统计 |
| | | Double rollerOutput = 0.0; |
| | | Double packerOutput = 0.0; |
| | | // 明细列表 |
| | | // 明细列表(仅存最终正数结果) |
| | | List<StoreSilkDetailVo> rollerDetailList = new ArrayList<>(); |
| | | List<StoreSilkDetailVo> packerDetailList = new ArrayList<>(); |
| | | // 操作记录列表(存所有增减过程) |
| | | List<StoreSilkDetailVo> rollerRecordList = new ArrayList<>(); |
| | | List<StoreSilkDetailVo> packerRecordList = new ArrayList<>(); |
| | | |
| | | for (int s = 0; s < distShiftList.size(); s++) { |
| | | MdShiftBo shiftBo = distShiftList.get(s); |
| | |
| | | .isNotNull(RollerTimeData::getQty) |
| | | .gt(RollerTimeData::getQty, 0) |
| | | .last("LIMIT 1"); |
| | | |
| | | RollerTimeData rData = rollerTimeDataMapper.selectOne(rlqw); |
| | | RollerTimeData rData = rollerTimeDataService.selectOne(rlqw); |
| | | if (rData != null) { |
| | | // 先把统计结束时刻累计值加进来:current = Qty(calcEnd) |
| | | currentRollerOutput += rData.getQty(); |
| | | |
| | | // 记录过程:班次截止累计 |
| | | StoreSilkDetailVo endRecord = new StoreSilkDetailVo(); |
| | | endRecord.setFsNum(fsNum.substring(2, 3)); |
| | | endRecord.setSiloNum(containerNum); |
| | | endRecord.setPipeNum(channel); |
| | | endRecord.setEquNo(equNo); |
| | | endRecord.setShiftCode(shift); |
| | | endRecord.setShiftStartTime(calcStartDate); |
| | | endRecord.setShiftEndTime(calcEndDate); |
| | | endRecord.setOutput(rData.getQty()); |
| | | endRecord.setCalcType("班次截止累计"); |
| | | endRecord.setHitTime(rData.getTime()); |
| | | rollerRecordList.add(endRecord); |
| | | |
| | | // 2) 扣“头”:如果统计开始时刻晚于班次开始,则减去 Qty(calcStart) |
| | | if (calcStartDate.after(stimDate)) { |
| | |
| | | .gt(RollerTimeData::getQty, 0) |
| | | .last("LIMIT 1"); |
| | | |
| | | RollerTimeData rBeginData = rollerTimeDataMapper.selectOne(beginRlqw); |
| | | RollerTimeData rBeginData = rollerTimeDataService.selectOne(beginRlqw); |
| | | if (rBeginData != null) { |
| | | currentRollerOutput -= rBeginData.getQty(); |
| | | |
| | | // 记录过程:扣除头部产量 |
| | | StoreSilkDetailVo beginRecord = new StoreSilkDetailVo(); |
| | | beginRecord.setFsNum(fsNum.substring(2, 3)); |
| | | beginRecord.setSiloNum(containerNum); |
| | | beginRecord.setPipeNum(channel); |
| | | beginRecord.setEquNo(equNo); |
| | | beginRecord.setShiftCode(shift); |
| | | beginRecord.setShiftStartTime(calcStartDate); |
| | | beginRecord.setShiftEndTime(calcEndDate); |
| | | beginRecord.setOutput(-rBeginData.getQty()); // 负数表示扣除 |
| | | beginRecord.setCalcType("扣除出料前累计"); |
| | | beginRecord.setHitTime(rBeginData.getTime()); |
| | | rollerRecordList.add(beginRecord); |
| | | } |
| | | } |
| | | } |
| | |
| | | .eq(PackerTimeData::getKey, packerKey) |
| | | .ge(PackerTimeData::getTime, tenMinBeforeCalcEnd) |
| | | .orderByDesc(PackerTimeData::getTime) |
| | | .isNotNull(PackerTimeData::getTsQty) |
| | | .gt(PackerTimeData::getTsQty, 0) |
| | | .isNotNull(PackerTimeData::getQty) |
| | | .gt(PackerTimeData::getQty, 0) |
| | | .last("LIMIT 1"); |
| | | |
| | | PackerTimeData pData = packerTimeDataMapper.selectOne(plqw); |
| | | PackerTimeData pData = packerTimeDataService.selectOne(plqw); |
| | | if (pData != null) { |
| | | // 先把统计结束时刻累计值加进来:current = Qty(calcEnd) |
| | | currentPackerOutput += pData.getTsQty(); |
| | | currentPackerOutput += pData.getQty(); |
| | | |
| | | // 记录过程:班次截止累计 |
| | | StoreSilkDetailVo endRecord = new StoreSilkDetailVo(); |
| | | endRecord.setFsNum(fsNum.substring(2, 3)); |
| | | endRecord.setSiloNum(containerNum); |
| | | endRecord.setPipeNum(channel); |
| | | endRecord.setEquNo(equNo); |
| | | endRecord.setShiftCode(shift); |
| | | endRecord.setShiftStartTime(calcStartDate); |
| | | endRecord.setShiftEndTime(calcEndDate); |
| | | endRecord.setOutput(pData.getQty()); |
| | | endRecord.setCalcType("班次截止累计"); |
| | | endRecord.setHitTime(pData.getTime()); |
| | | packerRecordList.add(endRecord); |
| | | |
| | | // 2) 扣“头”:如果统计开始时刻晚于班次开始,则减去 Qty(calcStart) |
| | | if (calcStartDate.after(stimDate)) { |
| | |
| | | .eq(PackerTimeData::getKey, packerKey) |
| | | .ge(PackerTimeData::getTime, tenMinBeforeCalcStart) |
| | | .orderByDesc(PackerTimeData::getTime) |
| | | .isNotNull(PackerTimeData::getTsQty) |
| | | .gt(PackerTimeData::getTsQty, 0) |
| | | .isNotNull(PackerTimeData::getQty) |
| | | .gt(PackerTimeData::getQty, 0) |
| | | .last("LIMIT 1"); |
| | | |
| | | PackerTimeData pBeginData = packerTimeDataMapper.selectOne(beginPlqw); |
| | | PackerTimeData pBeginData = packerTimeDataService.selectOne(beginPlqw); |
| | | if (pBeginData != null) { |
| | | currentPackerOutput -= pBeginData.getTsQty(); |
| | | currentPackerOutput -= pBeginData.getQty(); |
| | | |
| | | // 记录过程:扣除头部产量 |
| | | StoreSilkDetailVo beginRecord = new StoreSilkDetailVo(); |
| | | beginRecord.setFsNum(fsNum.substring(2, 3)); |
| | | beginRecord.setSiloNum(containerNum); |
| | | beginRecord.setPipeNum(channel); |
| | | beginRecord.setEquNo(equNo); |
| | | beginRecord.setShiftCode(shift); |
| | | beginRecord.setShiftStartTime(calcStartDate); |
| | | beginRecord.setShiftEndTime(calcEndDate); |
| | | beginRecord.setOutput(-pBeginData.getQty()); // 负数表示扣除 |
| | | beginRecord.setCalcType("扣除出料前累计"); |
| | | beginRecord.setHitTime(pBeginData.getTime()); |
| | | packerRecordList.add(beginRecord); |
| | | } |
| | | } |
| | | } |
| | |
| | | storeSilkInfoVo.setPackerOutput(packerOutput); |
| | | storeSilkInfoVo.setRollerDetailList(rollerDetailList); |
| | | storeSilkInfoVo.setPackerDetailList(packerDetailList); |
| | | storeSilkInfoVo.setRollerRecordList(rollerRecordList); |
| | | storeSilkInfoVo.setPackerRecordList(packerRecordList); |
| | | |
| | | } |
| | | |
| | |
| | | private LambdaQueryWrapper<StoreSilkInfo> buildQueryWrapper(StoreSilkInfoBo bo) { |
| | | Map<String, Object> params = bo.getParams(); |
| | | LambdaQueryWrapper<StoreSilkInfo> lqw = Wrappers.lambdaQuery(); |
| | | lqw.orderByAsc(StoreSilkInfo::getId); |
| | | lqw.like(StringUtils.isNotBlank(bo.getMaterialname()), StoreSilkInfo::getMaterialname, bo.getMaterialname()); |
| | | lqw.eq(StringUtils.isNotBlank(bo.getBatchcode()), StoreSilkInfo::getBatchcode, bo.getBatchcode()); |
| | | lqw.eq(bo.getActualstarttime() != null, StoreSilkInfo::getActualstarttime, bo.getActualstarttime()); |
| | | lqw.like(StringUtils.isNotBlank(bo.getBatchcode()), StoreSilkInfo::getBatchcode, bo.getBatchcode()); |
| | | if (bo.getActualstarttime() != null) { |
| | | ZoneId zone = ZoneId.systemDefault(); |
| | | LocalDate day = bo.getActualstarttime().toInstant().atZone(zone).toLocalDate(); |
| | | Date dayStart = Date.from(day.atStartOfDay(zone).toInstant()); |
| | | Date nextDayStart = Date.from(day.plusDays(1).atStartOfDay(zone).toInstant()); |
| | | lqw.ge(StoreSilkInfo::getActualstarttime, dayStart); |
| | | lqw.lt(StoreSilkInfo::getActualstarttime, nextDayStart); |
| | | } |
| | | lqw.eq(bo.getDistimebegin() != null, StoreSilkInfo::getDistimebegin, bo.getDistimebegin()); |
| | | lqw.eq(bo.getDistimeend() != null, StoreSilkInfo::getDistimeend, bo.getDistimeend()); |
| | | lqw.eq(StringUtils.isNotBlank(bo.getSiloid()), StoreSilkInfo::getSiloid, bo.getSiloid()); |
| | | lqw.between(params.get("beginTime") != null && params.get("endTime") != null, |
| | | StoreSilkInfo::getDistimeend, params.get("beginTime"), params.get("endTime")); |
| | | if (StringUtils.isNotBlank(bo.getSiloid())) { |
| | | // 支持多个柜号查询,以逗号分隔 |
| | | String[] siloids = bo.getSiloid().split(","); |
| | | lqw.and(wrapper -> { |
| | | for (String val : siloids) { |
| | | val = val.trim(); |
| | | if (StringUtils.isBlank(val)) { |
| | | continue; |
| | | } |
| | | String finalVal = val; |
| | | // 使用 OR 连接多个柜号条件 |
| | | wrapper.or(w -> { |
| | | try { |
| | | int num = Integer.parseInt(finalVal); |
| | | String padded = String.format("%02d", num); |
| | | // 匹配 _1 或 _01 (兼容不补零和补零的情况) |
| | | w.likeLeft(StoreSilkInfo::getSiloid, "_" + num) |
| | | .or() |
| | | .likeLeft(StoreSilkInfo::getSiloid, "_" + padded); |
| | | } catch (NumberFormatException e) { |
| | | // 非数字则按原值匹配 |
| | | w.eq(StoreSilkInfo::getSiloid, finalVal); |
| | | } |
| | | }); |
| | | } |
| | | }); |
| | | } |
| | | if (params.get("beginTime") != null && params.get("endTime") != null) { |
| | | lqw.apply("distimeend BETWEEN TO_DATE({0}, 'YYYY-MM-DD HH24:MI:SS') AND TO_DATE({1}, 'YYYY-MM-DD HH24:MI:SS')", |
| | | params.get("beginTime"), |
| | | params.get("endTime")); |
| | | } |
| | | return lqw; |
| | | } |
| | | |
| | |
| | | validEntityBeforeSave(add); |
| | | boolean flag = baseMapper.insert(add) > 0; |
| | | if (flag) { |
| | | bo.setId(add.getId()); |
| | | } |
| | | return flag; |
| | | } |