| | |
| | | Date distimebegin = storeSilkInfoVo.getDistimebegin(); |
| | | //出料结束时间 |
| | | Date distimeend = storeSilkInfoVo.getDistimeend(); |
| | | if (distimebegin == null) { |
| | | continue; |
| | | } |
| | | /** |
| | | * 统计“出料进行中”的关键点: |
| | | * - 出料结束时间 distimeend 可能为空(仍在出料中) |
| | | * - 也可能已经有结束时间(出料已结束) |
| | | * |
| | | * 因此这里统一计算一个“本次统计的有效结束时间” effectiveDistEnd: |
| | | * 1) distimeend == null :说明还在出料中,本次统计截止到当前时间 now |
| | | * 2) distimeend != null :说明已出料结束,截止到 distimeend |
| | | * 3) 若 distimeend 在未来(脏数据/时钟误差),也按 now 截止,避免统计到未来 |
| | | * |
| | | * 后续所有查询、班次切分、扣减都基于 effectiveDistEnd, |
| | | * 这样同一套逻辑同时适配“出料已结束”和“出料未结束”两种场景。 |
| | | */ |
| | | Date now = new Date(); |
| | | Date effectiveDistEnd; |
| | | if (distimeend == null) { |
| | | effectiveDistEnd = now; |
| | | } else { |
| | | effectiveDistEnd = distimeend.after(now) ? now : distimeend; |
| | | } |
| | | if (!effectiveDistEnd.after(distimebegin)) { |
| | | continue; |
| | | } |
| | | |
| | | //储丝柜柜号 |
| | | String siloid = storeSilkInfoVo.getSiloid(); |
| | |
| | | String containerNum = siloid.substring(lastIndex + 1); |
| | | if (StringUtils.isEmpty(containerNum)) continue; |
| | | |
| | | //根据出料开始时间查询喂丝机->储丝柜->机台对应关系(feedmatch_time_data) |
| | | Timestamp targetTime = new Timestamp(distimebegin.getTime() + 10 * 60 * 1000); // 查询出料10分钟后的第一条记录,保证数据准确性 |
| | | /** |
| | | * 根据出料开始时间查询“喂丝机 -> 储丝柜 -> 机台(管道)”对应关系(feedmatch_time_data) |
| | | * |
| | | * 说明: |
| | | * - 这里不是直接用 distimebegin,而是取出料开始后 10 分钟的第一条记录, |
| | | * 目的是避开出料刚开始时的波动/映射未稳定问题,保证映射准确性。 |
| | | * - 查询上界使用 effectiveDistEnd:如果出料还没结束,就以当前时间作为上界,仍然能取到对应关系。 |
| | | */ |
| | | Timestamp targetTime = new Timestamp(distimebegin.getTime() + 10 * 60 * 1000); // 出料开始后10分钟 |
| | | LambdaQueryWrapper<FeedmatchTimeData> lqw = new LambdaQueryWrapper<>(); |
| | | lqw.ge(FeedmatchTimeData::getTime, targetTime) |
| | | .le(FeedmatchTimeData::getTime, distimeend) // 不能大于出料结束时间 |
| | | .le(FeedmatchTimeData::getTime, effectiveDistEnd) // 不能大于出料结束时间(出料中则使用当前时间) |
| | | .orderByAsc(FeedmatchTimeData::getTime) |
| | | .last("LIMIT 1"); |
| | | FeedmatchTimeData feedMatch = feedmatchTimeDataMapper.selectOne(lqw); |
| | |
| | | continue; |
| | | } |
| | | |
| | | // feedMatch 转map TODO 逆转map需验证key是否会重复 |
| | | //fsRevMap是逆转map key->喂丝机对应的储丝柜号 value-> fs + 序号 |
| | | // feedMatch 转map:通过反向映射快速定位“该柜对应哪个喂丝机”、“该机组对应哪个管道” |
| | | // fsRevMap:key=储丝柜号末位(如 1/2/3),value=字段名(如 fs11/fs12...) |
| | | Map<String, String> fsRevMap = new HashMap<>(); |
| | | //pipeRevMap是逆转map key->机组对应的喂丝机和管道 value-> pipe + 序号 |
| | | // pipeRevMap:key=喂丝机号+管道组合(如 1x / 2x...),value=字段名(如 pipe01/pipe02...) |
| | | Map<String, String> pipeRevMap = new HashMap<>(); |
| | | // pipeMap:key=字段名(pipe01/pipe02...),value=字段值(用于后续解析具体管道号) |
| | | Map<String, String> pipeMap = new HashMap<>(); |
| | | Field[] fields = feedMatch.getClass().getDeclaredFields(); |
| | | for (Field field : fields) { |
| | |
| | | // TODO 管道号空返回信息 |
| | | continue; |
| | | } |
| | | // List存-> 喂丝机对应的机组(如 pipe01 pipe02 代表1#、2#卷接机组) |
| | | /** |
| | | * pipeList 存放“该喂丝机对应的机组字段名” |
| | | * - 例如 pipe01、pipe02 代表 1#、2#卷接/包装机组 |
| | | * - 后续会从 pipe01/pipe02 提取机组号 equNo,并用于拼接 key 查询卷接/包装表 |
| | | */ |
| | | List<String> pipeList = new ArrayList<>(); |
| | | for (Map.Entry<String, String> entry : pipeRevMap.entrySet()) { |
| | | //fsNum第三位是喂丝机序号 |
| | |
| | | continue; |
| | | } |
| | | |
| | | // 根据出料开始结束时间,查询该柜料在哪几个班次生产 |
| | | List<MdShiftBo> distShiftList = calcShiftSpans(distimebegin, distimeend, mdShifts); |
| | | /** |
| | | * 根据 [distimebegin, effectiveDistEnd] 计算涉及到的班次列表 |
| | | * - 出料已结束:effectiveDistEnd=distimeend |
| | | * - 出料未结束:effectiveDistEnd=now |
| | | */ |
| | | List<MdShiftBo> distShiftList = calcShiftSpans(distimebegin, effectiveDistEnd, mdShifts); |
| | | storeSilkInfoVo.setDistShiftList(distShiftList); |
| | | if (distShiftList.isEmpty()) 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); |
| | |
| | | String shift = shiftBo.getCode(); |
| | | |
| | | // 解析班次时间 |
| | | // 1. 获取班次配置的开始和结束时间字符串 (格式如 "07:30:00") |
| | | // 1) 获取班次配置的开始/结束时间 (可能是 "HH:mm" 或 "HH:mm:ss") |
| | | String stimStr = shiftBo.getStim(); |
| | | String etimStr = shiftBo.getEtim(); |
| | | if (StringUtils.isEmpty(stimStr) || StringUtils.isEmpty(etimStr)) { |
| | | continue; |
| | | } |
| | | // 2. 补全秒数(如果配置仅为 HH:mm) |
| | | // 2) 补全秒数(如果配置仅为 HH:mm) |
| | | if (stimStr.length() == 5) stimStr += ":00"; |
| | | if (etimStr.length() == 5) etimStr += ":00"; |
| | | |
| | | // 3. 结合日期解析为 LocalDateTime |
| | | // 注意:shiftBo.getDay() 是该班次的归属日期 |
| | | // 3) 结合日期(shiftBo.getDay() 为班次归属日)解析为 LocalDateTime |
| | | String dateStr = shiftBo.getDay().toInstant().atZone(zone).toLocalDate().toString(); |
| | | DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); |
| | | LocalDateTime shiftStart = LocalDateTime.parse(dateStr + " " + stimStr, formatter); |
| | | LocalDateTime shiftEnd = LocalDateTime.parse(dateStr + " " + etimStr, formatter); |
| | | |
| | | // 4. 处理跨天班次:如果 结束时间 <= 开始时间,说明跨天,结束时间需 +1 天 |
| | | // 4) 处理跨天班次:如果 结束时间 <= 开始时间,说明跨天,结束时间 +1 天 |
| | | if (!shiftEnd.isAfter(shiftStart)) { |
| | | shiftEnd = shiftEnd.plusDays(1); |
| | | } |
| | |
| | | Date stimDate = Date.from(shiftStart.atZone(zone).toInstant()); |
| | | Date etimDate = Date.from(shiftEnd.atZone(zone).toInstant()); |
| | | |
| | | // 计算班次结束前10分钟的时间点 |
| | | String shiftEndStr = shiftEnd.format(formatter); |
| | | String tenMinBeforeShiftEnd = shiftEnd.minusMinutes(10).format(formatter); |
| | | /** |
| | | * 为了同时支持“出料未结束”,这里引入一个“本班次的统计窗口”: |
| | | * |
| | | * 统计结束时刻 calcEnd = min(班次结束, effectiveDistEnd) |
| | | * - 出料已结束:effectiveDistEnd=distimeend,可能早于班次结束,calcEnd=distimeend |
| | | * - 出料未结束:effectiveDistEnd=now,calcEnd=now 或 班次结束(取较小) |
| | | * |
| | | * 统计开始时刻 calcStart = max(班次开始, distimebegin) |
| | | * - 若出料开始晚于班次开始,则从出料开始统计 |
| | | * - 若出料开始早于班次开始,则从班次开始统计 |
| | | * |
| | | * 最终本班次有效产量口径为: |
| | | * output = Qty(calcEnd) - Qty(calcStart) |
| | | * |
| | | * 说明:这个口径已经“隐含了扣尾” |
| | | * - 如果 calcEnd < 班次结束(例如出料已结束或统计到当前时刻),尾巴自然不会被统计进去 |
| | | * - 因此不需要再额外写一个“扣尾 if(...)”分支,逻辑更统一、更不容易出错 |
| | | */ |
| | | LocalDateTime effectiveEndLdt = LocalDateTime.ofInstant(effectiveDistEnd.toInstant(), zone); |
| | | LocalDateTime calcEnd = shiftEnd.isBefore(effectiveEndLdt) ? shiftEnd : effectiveEndLdt; |
| | | Date calcEndDate = Date.from(calcEnd.atZone(zone).toInstant()); |
| | | if (!calcEndDate.after(stimDate)) { |
| | | continue; |
| | | } |
| | | Date calcStartDate = distimebegin.after(stimDate) ? distimebegin : stimDate; |
| | | if (!calcEndDate.after(calcStartDate)) { |
| | | continue; |
| | | } |
| | | String calcEndStr = calcEnd.format(formatter); |
| | | String tenMinBeforeCalcEnd = calcEnd.minusMinutes(10).format(formatter); |
| | | |
| | | // 根据卷接机组和班次获取产量(pipeList = 多少个机组) |
| | | for (int j = 0; j < pipeList.size(); j++) { |
| | | String pipe = pipeList.get(j); |
| | | // 提取机组号 |
| | | String equNo = pipe.replaceAll("\\D+", ""); |
| | | //管道号 |
| | | // 管道号(用于明细展示):从 feedMatch 的 pipe01/pipe02... 字段值里取末位 |
| | | String channel = pipeMap.get("pipe" + equNo); |
| | | if (channel != null && channel.length() > 0) { |
| | | channel = channel.substring(channel.length() - 1); |
| | |
| | | channel = ""; |
| | | } |
| | | |
| | | //注意4号管道以后对应的机组需要+1,机组4号空缺跳到5号 |
| | | /** |
| | | * 机组号映射规则(业务约定): |
| | | * - pipe04 对应的机组号在系统里是空缺的,需要跳到 5 号 |
| | | * - 因此当管道号 >= 4 时,机组号需要 +1 |
| | | * - 同时这里补齐为 2 位数字,便于拼 key(例如 01/02/05...) |
| | | */ |
| | | try { |
| | | int equ = Integer.parseInt(equNo); |
| | | equNo = String.format("%02d", equ >= 4 ? equ + 1 : equ); |
| | |
| | | } |
| | | |
| | | |
| | | // 拼接卷接机的key,中间 "1" 代表卷接机,格式:班次 + "1" + 机组号 |
| | | // key 拼接规则: |
| | | // - 卷接机:班次 + "1" + 机组号(例如 101、105...) |
| | | String key = shift + "1" + equNo; |
| | | // 拼接包装机的key,中间 "2" 代表包装机,格式:班次 + "2" + 机组号 |
| | | // - 包装机:班次 + "2" + 机组号(例如 201、205...) |
| | | String packerKey = shift + "2" + equNo; |
| | | |
| | | // ================= 卷接机产量统计 ================= |
| | | Double currentRollerOutput = 0.0; |
| | | // 1. 查询班次结束时的产量快照 |
| | | /** |
| | | * 取数策略说明(卷接/包装一致): |
| | | * - 表内的 qty 视为累计值(同一 key 下单调递增) |
| | | * - 由于数据是采样上报,某个时刻不一定刚好有记录 |
| | | * - 因此采用“目标时刻前 10 分钟内,离目标最近的一条记录”作为快照值 |
| | | * |
| | | * 注意:10分钟是经验窗口,如果现场采样更稀疏,可考虑放宽窗口 |
| | | */ |
| | | // 1) 查询统计结束时刻 calcEnd 的快照值 Qty(calcEnd) |
| | | LambdaQueryWrapper<RollerTimeData> rlqw = new LambdaQueryWrapper<>(); |
| | | rlqw.le(RollerTimeData::getTime, shiftEndStr) |
| | | rlqw.le(RollerTimeData::getTime, calcEndStr) |
| | | .eq(RollerTimeData::getKey, key) |
| | | .ge(RollerTimeData::getTime, tenMinBeforeShiftEnd) |
| | | .ge(RollerTimeData::getTime, tenMinBeforeCalcEnd) |
| | | .orderByDesc(RollerTimeData::getTime) |
| | | .isNotNull(RollerTimeData::getQty) |
| | | .gt(RollerTimeData::getQty, 0) |
| | |
| | | |
| | | RollerTimeData rData = rollerTimeDataMapper.selectOne(rlqw); |
| | | if (rData != null) { |
| | | // 初始产量设为班次结束时的累计值 |
| | | // 先把统计结束时刻累计值加进来:current = Qty(calcEnd) |
| | | currentRollerOutput += rData.getQty(); |
| | | |
| | | // 2. 处理“扣头”:出料开始时间 > 班次开始时间 |
| | | if (distimebegin.after(stimDate)) { |
| | | LocalDateTime distBeginTime = LocalDateTime.ofInstant(distimebegin.toInstant(), zone); |
| | | String distBeginStr = distBeginTime.format(formatter); |
| | | String tenMinBeforeDistBegin = distBeginTime.minusMinutes(10).format(formatter); |
| | | // 记录过程:班次截止累计 |
| | | 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)) { |
| | | LocalDateTime calcStartLdt = LocalDateTime.ofInstant(calcStartDate.toInstant(), zone); |
| | | String calcStartStr = calcStartLdt.format(formatter); |
| | | String tenMinBeforeCalcStart = calcStartLdt.minusMinutes(10).format(formatter); |
| | | |
| | | LambdaQueryWrapper<RollerTimeData> beginRlqw = new LambdaQueryWrapper<>(); |
| | | beginRlqw.le(RollerTimeData::getTime, distBeginStr) |
| | | .eq(RollerTimeData::getKey, key) |
| | | .ge(RollerTimeData::getTime, tenMinBeforeDistBegin) |
| | | .orderByDesc(RollerTimeData::getTime) |
| | | .isNotNull(RollerTimeData::getQty) |
| | | .gt(RollerTimeData::getQty, 0) |
| | | .last("LIMIT 1"); |
| | | beginRlqw.le(RollerTimeData::getTime, calcStartStr) |
| | | .eq(RollerTimeData::getKey, key) |
| | | .ge(RollerTimeData::getTime, tenMinBeforeCalcStart) |
| | | .orderByDesc(RollerTimeData::getTime) |
| | | .isNotNull(RollerTimeData::getQty) |
| | | .gt(RollerTimeData::getQty, 0) |
| | | .last("LIMIT 1"); |
| | | |
| | | RollerTimeData rBeginData = rollerTimeDataMapper.selectOne(beginRlqw); |
| | | if (rBeginData != null) { |
| | | currentRollerOutput -= rBeginData.getQty(); |
| | | } |
| | | } |
| | | |
| | | // 3. 处理“扣尾”:出料结束时间 < 班次结束时间 |
| | | if (etimDate.after(distimeend)) { |
| | | LocalDateTime distEndTime = LocalDateTime.ofInstant(distimeend.toInstant(), zone); |
| | | String distEndStr = distEndTime.format(formatter); |
| | | String tenMinBeforeDistEnd = distEndTime.minusMinutes(10).format(formatter); |
| | | |
| | | LambdaQueryWrapper<RollerTimeData> endRlqw = new LambdaQueryWrapper<>(); |
| | | endRlqw.le(RollerTimeData::getTime, distEndStr) |
| | | .eq(RollerTimeData::getKey, key) |
| | | .ge(RollerTimeData::getTime, tenMinBeforeDistEnd) |
| | | .orderByDesc(RollerTimeData::getTime) |
| | | .isNotNull(RollerTimeData::getQty) |
| | | .gt(RollerTimeData::getQty, 0) |
| | | .last("LIMIT 1"); |
| | | |
| | | RollerTimeData rEndData = rollerTimeDataMapper.selectOne(endRlqw); |
| | | if (rEndData != null) { |
| | | double qtyDelta = rData.getQty() - rEndData.getQty(); |
| | | if (qtyDelta > 0) { |
| | | currentRollerOutput -= qtyDelta; |
| | | } |
| | | // 记录过程:扣除头部产量 |
| | | 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); |
| | | } |
| | | } |
| | | } |
| | |
| | | detail.setPipeNum(channel); |
| | | detail.setEquNo(equNo); |
| | | detail.setShiftCode(shift); |
| | | detail.setShiftStartTime(stimDate); |
| | | detail.setShiftEndTime(etimDate); |
| | | detail.setShiftStartTime(calcStartDate); |
| | | detail.setShiftEndTime(calcEndDate); |
| | | detail.setOutput(currentRollerOutput); |
| | | rollerDetailList.add(detail); |
| | | } |
| | | |
| | | // ================= 包装机产量统计 ================= |
| | | Double currentPackerOutput = 0.0; |
| | | // 1. 查询班次结束时的产量快照 |
| | | // 1) 查询统计结束时刻 calcEnd 的快照值 Qty(calcEnd) |
| | | LambdaQueryWrapper<PackerTimeData> plqw = new LambdaQueryWrapper<>(); |
| | | plqw.le(PackerTimeData::getTime, shiftEndStr) |
| | | plqw.le(PackerTimeData::getTime, calcEndStr) |
| | | .eq(PackerTimeData::getKey, packerKey) |
| | | .ge(PackerTimeData::getTime, tenMinBeforeShiftEnd) |
| | | .ge(PackerTimeData::getTime, tenMinBeforeCalcEnd) |
| | | .orderByDesc(PackerTimeData::getTime) |
| | | .isNotNull(PackerTimeData::getQty) |
| | | .gt(PackerTimeData::getQty, 0) |
| | | .isNotNull(PackerTimeData::getTsQty) |
| | | .gt(PackerTimeData::getTsQty, 0) |
| | | .last("LIMIT 1"); |
| | | |
| | | PackerTimeData pData = packerTimeDataMapper.selectOne(plqw); |
| | | if (pData != null) { |
| | | // 初始产量设为班次结束时的累计值 |
| | | currentPackerOutput += pData.getQty(); |
| | | // 先把统计结束时刻累计值加进来:current = Qty(calcEnd) |
| | | currentPackerOutput += pData.getTsQty(); |
| | | |
| | | // 2. 处理“扣头” |
| | | if (distimebegin.after(stimDate)) { |
| | | LocalDateTime distBeginTime = LocalDateTime.ofInstant(distimebegin.toInstant(), zone); |
| | | String distBeginStr = distBeginTime.format(formatter); |
| | | String tenMinBeforeDistBegin = distBeginTime.minusMinutes(10).format(formatter); |
| | | // 记录过程:班次截止累计 |
| | | 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.getTsQty()); |
| | | endRecord.setCalcType("班次截止累计"); |
| | | endRecord.setHitTime(pData.getTime()); |
| | | packerRecordList.add(endRecord); |
| | | |
| | | // 2) 扣“头”:如果统计开始时刻晚于班次开始,则减去 Qty(calcStart) |
| | | if (calcStartDate.after(stimDate)) { |
| | | LocalDateTime calcStartLdt = LocalDateTime.ofInstant(calcStartDate.toInstant(), zone); |
| | | String calcStartStr = calcStartLdt.format(formatter); |
| | | String tenMinBeforeCalcStart = calcStartLdt.minusMinutes(10).format(formatter); |
| | | |
| | | LambdaQueryWrapper<PackerTimeData> beginPlqw = new LambdaQueryWrapper<>(); |
| | | beginPlqw.le(PackerTimeData::getTime, distBeginStr) |
| | | .eq(PackerTimeData::getKey, packerKey) |
| | | .ge(PackerTimeData::getTime, tenMinBeforeDistBegin) |
| | | .orderByDesc(PackerTimeData::getTime) |
| | | .isNotNull(PackerTimeData::getQty) |
| | | .gt(PackerTimeData::getQty, 0) |
| | | .last("LIMIT 1"); |
| | | beginPlqw.le(PackerTimeData::getTime, calcStartStr) |
| | | .eq(PackerTimeData::getKey, packerKey) |
| | | .ge(PackerTimeData::getTime, tenMinBeforeCalcStart) |
| | | .orderByDesc(PackerTimeData::getTime) |
| | | .isNotNull(PackerTimeData::getTsQty) |
| | | .gt(PackerTimeData::getTsQty, 0) |
| | | .last("LIMIT 1"); |
| | | |
| | | PackerTimeData pBeginData = packerTimeDataMapper.selectOne(beginPlqw); |
| | | if (pBeginData != null) { |
| | | currentPackerOutput -= pBeginData.getQty(); |
| | | } |
| | | } |
| | | currentPackerOutput -= pBeginData.getTsQty(); |
| | | |
| | | // 3. 处理“扣尾” |
| | | if (etimDate.after(distimeend)) { |
| | | LocalDateTime distEndTime = LocalDateTime.ofInstant(distimeend.toInstant(), zone); |
| | | String distEndStr = distEndTime.format(formatter); |
| | | String tenMinBeforeDistEnd = distEndTime.minusMinutes(10).format(formatter); |
| | | |
| | | LambdaQueryWrapper<PackerTimeData> endPlqw = new LambdaQueryWrapper<>(); |
| | | endPlqw.le(PackerTimeData::getTime, distEndStr) |
| | | .eq(PackerTimeData::getKey, packerKey) |
| | | .ge(PackerTimeData::getTime, tenMinBeforeDistEnd) |
| | | .orderByDesc(PackerTimeData::getTime) |
| | | .isNotNull(PackerTimeData::getQty) |
| | | .gt(PackerTimeData::getQty, 0) |
| | | .last("LIMIT 1"); |
| | | |
| | | PackerTimeData pEndData = packerTimeDataMapper.selectOne(endPlqw); |
| | | if (pEndData != null) { |
| | | double qtyDelta = pData.getQty() - pEndData.getQty(); |
| | | if (qtyDelta > 0) { |
| | | currentPackerOutput -= qtyDelta; |
| | | } |
| | | // 记录过程:扣除头部产量 |
| | | 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.getTsQty()); // 负数表示扣除 |
| | | beginRecord.setCalcType("扣除出料前累计"); |
| | | beginRecord.setHitTime(pBeginData.getTime()); |
| | | packerRecordList.add(beginRecord); |
| | | } |
| | | } |
| | | } |
| | |
| | | detail.setPipeNum(channel); |
| | | detail.setEquNo(equNo); |
| | | detail.setShiftCode(shift); |
| | | detail.setShiftStartTime(stimDate); |
| | | detail.setShiftEndTime(etimDate); |
| | | detail.setShiftStartTime(calcStartDate); |
| | | detail.setShiftEndTime(calcEndDate); |
| | | detail.setOutput(currentPackerOutput); |
| | | packerDetailList.add(detail); |
| | | } |
| | | } |
| | | } |
| | | // 将汇总结果与明细结果回写到 StoreSilkInfoVo,供前端列表/抽屉明细展示使用 |
| | | storeSilkInfoVo.setRollerOutput(rollerOutput); |
| | | storeSilkInfoVo.setPackerOutput(packerOutput); |
| | | storeSilkInfoVo.setRollerDetailList(rollerDetailList); |
| | | storeSilkInfoVo.setPackerDetailList(packerDetailList); |
| | | storeSilkInfoVo.setRollerRecordList(rollerRecordList); |
| | | storeSilkInfoVo.setPackerRecordList(packerRecordList); |
| | | |
| | | } |
| | | |
| | |
| | | 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()); |
| | | 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); |
| | | } |
| | | }); |
| | | } |
| | | }); |
| | | } |
| | | lqw.between(params.get("beginTime") != null && params.get("endTime") != null, |
| | | StoreSilkInfo::getDistimeend, params.get("beginTime"), params.get("endTime")); |
| | | return lqw; |