package cn.iocoder.yudao.module.mes.service.pro.worktime; import cn.hutool.core.collection.CollUtil; import cn.iocoder.yudao.module.mes.controller.admin.pro.worktime.vo.MesProWorktimeReportReqVO; import cn.iocoder.yudao.module.mes.controller.admin.pro.worktime.vo.MesProWorktimeReportRespVO; import cn.iocoder.yudao.module.mes.dal.dataobject.md.workstation.MesMdWorkstationDO; import cn.iocoder.yudao.module.mes.dal.dataobject.pro.feedback.MesProFeedbackDO; import cn.iocoder.yudao.module.mes.dal.dataobject.pro.process.MesProProcessDO; import cn.iocoder.yudao.module.mes.dal.mysql.pro.feedback.MesProFeedbackMapper; import cn.iocoder.yudao.module.mes.service.md.workstation.MesMdWorkstationService; import cn.iocoder.yudao.module.mes.service.pro.process.MesProProcessService; import cn.iocoder.yudao.module.system.api.user.AdminUserApi; import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; import jakarta.annotation.Resource; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; import java.math.BigDecimal; import java.math.RoundingMode; import java.util.ArrayList; import java.util.Comparator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PRO_WORKTIME_REPORT_DIM_INVALID; /** * MES 工时考核报表 Service 实现类 * * @author 超级管理员 */ @Service @Validated public class MesProWorktimeReportServiceImpl implements MesProWorktimeReportService { private static final BigDecimal HUNDRED = BigDecimal.valueOf(100); @Resource private MesProFeedbackMapper feedbackMapper; @Resource private MesProProcessService processService; @Resource private MesMdWorkstationService workstationService; @Resource private AdminUserApi adminUserApi; @Override public MesProWorktimeReportRespVO getReport(MesProWorktimeReportReqVO reqVO) { Integer dimType = reqVO.getDimType(); if (dimType == null || dimType < 1 || dimType > 4) { throw exception(PRO_WORKTIME_REPORT_DIM_INVALID, dimType); } List list = feedbackMapper.selectWorktimeReportList(reqVO.getBeginTime(), reqVO.getEndTime(), reqVO.getFeedbackUserId(), reqVO.getProcessId(), reqVO.getWorkstationId()); // 1. 按维度分组累加 Map accumulatorMap = new LinkedHashMap<>(); for (MesProFeedbackDO feedback : list) { String key = buildDimKey(feedback, dimType); accumulatorMap.computeIfAbsent(key, k -> new Accumulator()).add(feedback); } // 2. 解析维度名称 Map nameMap = buildDimNameMap(accumulatorMap.keySet(), dimType); // 3. 输出行 + 合计 MesProWorktimeReportRespVO resp = new MesProWorktimeReportRespVO(); Accumulator total = new Accumulator(); list.forEach(total::add); List rows = new ArrayList<>(); accumulatorMap.forEach((key, accumulator) -> { MesProWorktimeReportRespVO.Row row = accumulator.toRow(key); row.setDimName(nameMap.getOrDefault(key, key)); rows.add(row); }); rows.sort(buildComparator(dimType)); resp.setRows(rows); resp.setSummary(total.toRow("ALL")); resp.getSummary().setDimName("合计"); return resp; } private static String buildDimKey(MesProFeedbackDO feedback, int dimType) { return switch (dimType) { case 1 -> String.valueOf(feedback.getFeedbackUserId()); case 2 -> String.valueOf(feedback.getProcessId()); case 3 -> feedback.getWorkstationId() == null ? "0" : String.valueOf(feedback.getWorkstationId()); default -> feedback.getFeedbackTime() == null ? "" : feedback.getFeedbackTime().toLocalDate().toString(); }; } private Map buildDimNameMap(java.util.Set keys, int dimType) { Map nameMap = new LinkedHashMap<>(); if (CollUtil.isEmpty(keys)) { return nameMap; } switch (dimType) { case 1 -> { Map userMap = adminUserApi.getUserMap( keys.stream().map(Long::valueOf).toList()); keys.forEach(key -> nameMap.put(key, userMap.containsKey(Long.valueOf(key)) ? userMap.get(Long.valueOf(key)).getNickname() : "ID=" + key)); } case 2 -> { Map processMap = processService.getProcessMap( keys.stream().map(Long::valueOf).toList()); keys.forEach(key -> nameMap.put(key, processMap.containsKey(Long.valueOf(key)) ? processMap.get(Long.valueOf(key)).getName() : "ID=" + key)); } case 3 -> { List workstationIds = keys.stream().filter(key -> !"0".equals(key)).map(Long::valueOf).toList(); Map workstationMap = workstationService.getWorkstationMap(workstationIds); keys.forEach(key -> { if ("0".equals(key)) { nameMap.put(key, "未指定机台"); } else { nameMap.put(key, workstationMap.containsKey(Long.valueOf(key)) ? workstationMap.get(Long.valueOf(key)).getName() : "ID=" + key); } }); } default -> keys.forEach(key -> nameMap.put(key, key)); } return nameMap; } private static Comparator buildComparator(int dimType) { if (dimType == 4) { return Comparator.comparing(MesProWorktimeReportRespVO.Row::getDimKey); } return Comparator.comparing(row -> { try { return Long.parseLong(row.getDimKey()); } catch (NumberFormatException ignored) { return Long.MAX_VALUE; } }); } private static class Accumulator { private long count; private BigDecimal productionLength = BigDecimal.ZERO; private BigDecimal actualHours = BigDecimal.ZERO; private BigDecimal standardHours = BigDecimal.ZERO; private BigDecimal wageAmount = BigDecimal.ZERO; void add(MesProFeedbackDO feedback) { count++; productionLength = productionLength.add(nvl(feedback.getProductionLength())); actualHours = actualHours.add(nvl(feedback.getWorkHours())); standardHours = standardHours.add(nvl(feedback.getStandardWorkHours())); wageAmount = wageAmount.add(nvl(feedback.getWageAmount())); } MesProWorktimeReportRespVO.Row toRow(String key) { MesProWorktimeReportRespVO.Row row = new MesProWorktimeReportRespVO.Row(); row.setDimKey(key); row.setFeedbackCount(count); row.setProductionLength(productionLength.setScale(2, RoundingMode.HALF_UP)); row.setActualHours(actualHours.setScale(2, RoundingMode.HALF_UP)); row.setStandardHours(standardHours.setScale(2, RoundingMode.HALF_UP)); BigDecimal diff = actualHours.subtract(standardHours).setScale(2, RoundingMode.HALF_UP); row.setWorkHoursDiff(diff); row.setWorkHoursDiffRate(standardHours.signum() > 0 ? diff.multiply(HUNDRED).divide(standardHours, 2, RoundingMode.HALF_UP) : null); row.setWageAmount(wageAmount.setScale(2, RoundingMode.HALF_UP)); return row; } private static BigDecimal nvl(BigDecimal value) { return value != null ? value : BigDecimal.ZERO; } } }