package com.jjt.calc.service.impl; import com.jjt.biz.domain.TwinDevice; import com.jjt.biz.service.ITwinDeviceService; import com.jjt.calc.domain.TwinCalcAlarms; import com.jjt.calc.domain.TwinCalcDay; import com.jjt.calc.domain.TwinCalcHour; import com.jjt.calc.domain.TwinCalcStop; import com.jjt.calc.mapper.TwinCalcDayMapper; import com.jjt.calc.service.ITwinCalcAlarmsService; import com.jjt.calc.service.ITwinCalcDayService; import com.jjt.calc.service.ITwinCalcHourService; import com.jjt.calc.service.ITwinCalcStopService; import com.jjt.common.utils.DateUtils; import com.jjt.utils.AsyncService; import com.jjt.utils.Tools; import javafx.util.Pair; import org.apache.ibatis.session.ExecutorType; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.math.BigDecimal; import java.math.RoundingMode; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; import java.util.*; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicLong; import java.util.function.BiConsumer; import java.util.stream.Collectors; /** * 日统计数据Service业务层处理 * * @author wukai * @date 2025-01-18 */ @Service public class TwinCalcDayServiceImpl implements ITwinCalcDayService { @Resource private TwinCalcDayMapper twinCalcDayMapper; @Resource private ITwinCalcHourService hourService; @Resource private ITwinCalcStopService stopService; @Resource private ITwinCalcAlarmsService alarmsService; @Resource private ITwinDeviceService deviceService; @Resource private AsyncService asyncService; @Resource private SqlSessionFactory factory; /** * 查询日统计数据 * * @param id 日统计数据主键 * @return 日统计数据 */ @Override public TwinCalcDay selectTwinCalcDayById(Long id) { return twinCalcDayMapper.selectTwinCalcDayById(id); } /** * 查询日统计数据列表 * * @param twinCalcDay 日统计数据 * @return 日统计数据 */ @Override public List selectTwinCalcDayList(TwinCalcDay twinCalcDay) { return twinCalcDayMapper.selectTwinCalcDayList(twinCalcDay); } /** * 新增日统计数据 * * @param twinCalcDay 日统计数据 * @return 结果 */ @Override public int insertTwinCalcDay(TwinCalcDay twinCalcDay) { return twinCalcDayMapper.insertTwinCalcDay(twinCalcDay); } /** * 修改日统计数据 * * @param twinCalcDay 日统计数据 * @return 结果 */ @Override public int updateTwinCalcDay(TwinCalcDay twinCalcDay) { return twinCalcDayMapper.updateTwinCalcDay(twinCalcDay); } /** * 批量删除日统计数据 * * @param ids 需要删除的日统计数据主键 * @return 结果 */ @Override public int deleteTwinCalcDayByIds(Long[] ids) { return twinCalcDayMapper.deleteTwinCalcDayByIds(ids); } /** * 删除日统计数据信息 * * @param id 日统计数据主键 * @return 结果 */ @Override public int deleteTwinCalcDayById(Long id) { return twinCalcDayMapper.deleteTwinCalcDayById(id); } /** * 近日统计数据 * * @param localDate 日期 */ @Override public void day(LocalDate localDate) { Date date = DateUtils.toDate(localDate); //先删除当前日期数据 twinCalcDayMapper.delete4date(date); //计算统计时间 Pair pair = Tools.calcDay(localDate); Date sTime = pair.getKey(); Date eTime = pair.getValue(); List hourList = hourService.selectTwinCalcHourListByDate(sTime, eTime); List stopList = stopService.selectTwinCalcStopListByDate(sTime, eTime); List alarmsList = alarmsService.selectTwinCalcAlarmsListByDate(sTime, eTime); Pair teamPairA = Tools.teamA(localDate); Pair teamPairB = Tools.teamB(localDate); // 提取A班和B班的开始和结束时间,避免在循环中重复调用 Date startTimeA = teamPairA.getKey(); Date endTimeA = teamPairA.getValue(); Date startTimeB = teamPairB.getKey(); Date endTimeB = teamPairB.getValue(); Map> hourDeviceGroup = hourList.stream().collect(Collectors.groupingBy(TwinCalcHour::getDeviceId, LinkedHashMap::new, Collectors.toList())); List dayList = new ArrayList<>(); for (Map.Entry> entry : hourDeviceGroup.entrySet()) { List hours = entry.getValue(); TwinCalcDay day = new TwinCalcDay(date); day.setDeviceId(entry.getKey()); //计算产量数据 day.calcHours(hours); //计算停机数据 List stops = stopList.stream().filter(s -> s.getDeviceId().equals(entry.getKey())).collect(Collectors.toList()); AtomicLong closeA = new AtomicLong(0L); AtomicLong closeB = new AtomicLong(0L); //A班停机计数 BiConsumer updateDayA = (type, count) -> { switch (type) { case 1: day.setStop1A(day.getStop1A() + count); break; case 2: day.setStop2A(day.getStop2A() + count); break; case 3: day.setStop3A(day.getStop3A() + count); break; default: } }; BiConsumer updateDayB = (type, count) -> { switch (type) { case 1: day.setStop1B(day.getStop1B() + count); break; case 2: day.setStop2B(day.getStop2B() + count); break; case 3: day.setStop3B(day.getStop3B() + count); break; default: } }; // 使用Stream API,结合if-else逻辑的优化 stops.forEach(stop -> { // 根据停机时间判断属于哪个班次 boolean isAshift = !stop.getStartTime().after(endTimeA) && !stop.getEndTime().before(startTimeA); Date shiftStart = isAshift ? startTimeA : startTimeB; Date shiftEnd = isAshift ? endTimeA : endTimeB; AtomicLong totalStopTime = isAshift ? closeA : closeB; BiConsumer eventCounter = isAshift ? updateDayA : updateDayB; // 调整停机时间 if (stop.getEndTime().compareTo(shiftEnd) > 0) { stop.setEndTime(shiftEnd); } if (stop.getStartTime().compareTo(shiftStart) < 0) { stop.setStartTime(shiftStart); } // 计算并更新停机时间和事件计数 long stopTime = (stop.getEndTime().getTime() - stop.getStartTime().getTime()) / 1000; updateStopCounters(stopTime, stop.getStopType(), totalStopTime, eventCounter); }); //计算稼动率 //AB班排班时间,要乘以设备总数 long teamTimeA = 12L * 60 * 60; long teamTimeB = 12L * 60 * 60; long totalTeamTime = teamTimeA + teamTimeB; long openTimeA = teamTimeA - closeA.get(); long openTimeB = teamTimeB - closeB.get(); long totalOpenTime = openTimeA + openTimeB; BigDecimal ea = BigDecimal.valueOf(openTimeA).divide(BigDecimal.valueOf(teamTimeA), 4, RoundingMode.HALF_UP); BigDecimal eb = BigDecimal.valueOf(openTimeB).divide(BigDecimal.valueOf(teamTimeB), 4, RoundingMode.HALF_UP); BigDecimal ee = BigDecimal.valueOf(totalOpenTime).divide(BigDecimal.valueOf(totalTeamTime), 4, RoundingMode.HALF_UP); day.setOpenTimeA(BigDecimal.valueOf(openTimeA)); day.setOpenTimeB(BigDecimal.valueOf(openTimeB)); day.setCloseTimeA(BigDecimal.valueOf(closeA.get())); day.setCloseTimeB(BigDecimal.valueOf(closeB.get())); day.setEfficiencyA(ea); day.setEfficiencyB(eb); day.setEfficiency(ee); //如果米长为0,稼动率处理 //20250317 潘工提的需求,把没有开机的设备也需要计算稼动 if (day.getLengthA().doubleValue() == 0d) { day.setOpenTimeA(BigDecimal.ZERO); day.setCloseTimeA(BigDecimal.valueOf(teamTimeA)); day.setEfficiencyA(BigDecimal.ZERO); } if (day.getLengthB().doubleValue() == 0d) { day.setOpenTimeB(BigDecimal.ZERO); day.setCloseTimeB(BigDecimal.valueOf(teamTimeB)); day.setEfficiencyB(BigDecimal.ZERO); } if (day.getLength().doubleValue() == 0d) { day.setEfficiency(BigDecimal.ZERO); } List alarms = alarmsList.stream().filter(a -> a.getDeviceId().equals(entry.getKey())).collect(Collectors.toList()); day.setAlarm((long) alarms.size()); dayList.add(day); } try (SqlSession sqlSession = factory.openSession(ExecutorType.BATCH, false)) { TwinCalcDayMapper mapper = sqlSession.getMapper(TwinCalcDayMapper.class); dayList.forEach(mapper::insertTwinCalcDay); sqlSession.commit(); } } /** * 用于更新停机时间计数器和事件计数 * * @param stopTime 停机时间 * @param stopType 停机类型 * @param totalTimeCounter 停机总时间 * @param eventCounterUpdater 抽象方法 */ private void updateStopCounters(long stopTime, int stopType, AtomicLong totalTimeCounter, BiConsumer eventCounterUpdater) { totalTimeCounter.addAndGet(stopTime); eventCounterUpdater.accept(stopType, 1); } /** * 统计今天的产量数据 * * @return 当日数据 7:00至第二天7点(不含) */ @Override public TwinCalcDay today() { //计算统计时间 Pair pair = Tools.calcToday(); Date sTime = pair.getKey(); Date eTime = pair.getValue(); List hourList = hourService.selectTwinCalcHourListByDate(sTime, eTime); // 按照deviceId、density、mick、team,并统计每组的length总和 //按照deviceId分组统计 Map resultMap = hourList.stream().collect(Collectors.groupingBy(TwinCalcHour::getDeviceId, Collectors.reducing(BigDecimal.ZERO, TwinCalcHour::getLength, BigDecimal::add))); TwinCalcDay calcDay = new TwinCalcDay(sTime); if (hourList.size() > 0) { calcDay.calcHours(hourList); } List hours = new ArrayList<>(); TwinCalcHour lastHour = hourService.lastHour(); LocalDate localDate = DateUtils.toLocalDate(lastHour.getDataDate()); LocalDateTime start = LocalDateTime.of(localDate, LocalTime.MIN).plusHours(lastHour.getHour() + 1); //为了避免多线程同时获取token导致重复执行,先执行一次获取token TwinDevice search = new TwinDevice(); //查询所有在线的设备 search.setOnline("1"); List list = deviceService.selectTwinDeviceList(search); List>>> futureList = new ArrayList<>(); for (int i = 0; i < list.size(); i++) { TwinDevice twinDevice = list.get(i); futureList.add(asyncService.process(twinDevice, start, LocalDateTime.now())); } try { for (Future>> future : futureList) { // 任务完成后获取结果 try { Map> map = future.get(10L, TimeUnit.SECONDS); List calcHours = (List) map.get("calc"); hours.addAll(calcHours); } catch (TimeoutException e) { e.printStackTrace(); } } } catch (InterruptedException | ExecutionException e) { throw new RuntimeException(e); } if (hours.size() > 0) { calcDay.calcHours(hours); int num = 0; for (TwinCalcHour hour : hours) { BigDecimal v = BigDecimal.ZERO; if (resultMap.get(hour.getDeviceId()) != null) { v = resultMap.get(hour.getDeviceId()); } v.add(hour.getLength()); if (v.doubleValue() > 0d) { num++; } } int deviceNum = list.size(); BigDecimal ratio = BigDecimal.valueOf(num).divide(BigDecimal.valueOf(deviceNum), 4, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)); calcDay.setRunningRatio(ratio.doubleValue()); } BigDecimal totalTimeA = calcDay.getOpenTimeA().add(calcDay.getCloseTimeA()); BigDecimal totalTimeB = calcDay.getOpenTimeB().add(calcDay.getCloseTimeB()); if (!totalTimeA.equals(BigDecimal.ZERO)) { BigDecimal eff = calcDay.getOpenTimeA().divide(totalTimeA, 4, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)); calcDay.setEfficiencyA(eff); } if (!totalTimeB.equals(BigDecimal.ZERO)) { BigDecimal eff = calcDay.getOpenTimeB().divide(totalTimeB, 4, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)); calcDay.setEfficiencyB(eff); } return calcDay; } /** * 按月度查询 * * @param month 月份 * @return 结果 */ @Override public List selectTwinCalcDayListByMonth(String month) { return twinCalcDayMapper.selectTwinCalcDayListByMonth(month); } /** * 查询指定日期及之后的数据 * * @param sd 开始日期 * @return 列表 */ @Override public List selectTwinCalcDayListByTime(Date sd) { return twinCalcDayMapper.selectTwinCalcDayListByTime(sd, null); } /** * 查询指定日期的数据 * * @param sd 开始日期 * @param ed 结束日期 * @return 列表 */ @Override public List selectTwinCalcDayListByTime(Date sd, Date ed) { return twinCalcDayMapper.selectTwinCalcDayListByTime(sd, ed); } }