package cn.iocoder.yudao.module.mes.service.home;
|
|
import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
|
import cn.iocoder.yudao.module.mes.controller.admin.home.vo.MesHomeProductionTrendRespVO;
|
import cn.iocoder.yudao.module.mes.controller.admin.home.vo.MesHomeSummaryRespVO;
|
import cn.iocoder.yudao.module.mes.controller.admin.home.vo.MesHomeWorkOrderStatusRespVO;
|
import cn.iocoder.yudao.module.mes.dal.mysql.home.MesHomeStatisticsMapper;
|
import cn.iocoder.yudao.module.mes.enums.dv.MesDvMachineryStatusEnum;
|
import cn.iocoder.yudao.module.mes.enums.pro.MesProWorkOrderStatusEnum;
|
import jakarta.annotation.Resource;
|
import org.springframework.stereotype.Service;
|
|
import java.math.BigDecimal;
|
import java.math.RoundingMode;
|
import java.time.LocalDate;
|
import java.time.LocalDateTime;
|
import java.time.format.DateTimeFormatter;
|
import java.util.*;
|
|
/**
|
* MES 首页统计 Service 实现类
|
*
|
* @author 超级管理员
|
*/
|
@Service
|
public class MesHomeStatisticsServiceImpl implements MesHomeStatisticsService {
|
|
@Resource
|
private MesHomeStatisticsMapper homeStatisticsMapper;
|
|
@Override
|
public MesHomeSummaryRespVO getHomeSummary() {
|
MesHomeSummaryRespVO summary = new MesHomeSummaryRespVO();
|
|
// 1. 工单统计
|
List<Map<String, Object>> workOrderStats = homeStatisticsMapper.selectWorkOrderCountGroupByStatus();
|
long activeCount = 0L;
|
long prepareCount = 0L;
|
long finishedCount = 0L;
|
for (Map<String, Object> stat : workOrderStats) {
|
int status = ((Number) stat.get("status")).intValue();
|
long count = ((Number) stat.get("count")).longValue();
|
if (status == MesProWorkOrderStatusEnum.CONFIRMED.getStatus()) {
|
activeCount = count;
|
} else if (status == MesProWorkOrderStatusEnum.PREPARE.getStatus()) {
|
prepareCount = count;
|
} else if (status == MesProWorkOrderStatusEnum.FINISHED.getStatus()) {
|
finishedCount = count;
|
}
|
}
|
summary.setWorkOrderActiveCount(activeCount);
|
summary.setWorkOrderPrepareCount(prepareCount);
|
summary.setWorkOrderFinishedCount(finishedCount);
|
|
// 2. 今日产量
|
LocalDateTime today = LocalDateTimeUtils.getToday();
|
LocalDateTime yesterday = LocalDateTimeUtils.getYesterday();
|
Map<String, BigDecimal> todayFeedback = homeStatisticsMapper.selectFeedbackSummary(today, null);
|
summary.setTodayOutput(getDecimal(todayFeedback, "feedbackQuantity"));
|
summary.setTodayQualifiedQuantity(getDecimal(todayFeedback, "qualifiedQuantity"));
|
summary.setTodayUnqualifiedQuantity(getDecimal(todayFeedback, "unqualifiedQuantity"));
|
|
// 3. 昨日产量
|
Map<String, BigDecimal> yesterdayFeedback = homeStatisticsMapper.selectFeedbackSummary(yesterday, today);
|
summary.setYesterdayOutput(getDecimal(yesterdayFeedback, "feedbackQuantity"));
|
|
// 4. 设备统计
|
List<Map<String, Object>> machineryStats = homeStatisticsMapper.selectMachineryCountGroupByStatus();
|
long total = 0L;
|
long producing = 0L;
|
long stop = 0L;
|
long maintenance = 0L;
|
for (Map<String, Object> stat : machineryStats) {
|
int status = ((Number) stat.get("status")).intValue();
|
long count = ((Number) stat.get("count")).longValue();
|
total += count;
|
if (status == MesDvMachineryStatusEnum.PRODUCING.getStatus()) {
|
producing = count;
|
} else if (status == MesDvMachineryStatusEnum.STOP.getStatus()) {
|
stop = count;
|
} else if (status == MesDvMachineryStatusEnum.MAINTENANCE.getStatus()) {
|
maintenance = count;
|
}
|
}
|
summary.setMachineryTotal(total);
|
summary.setMachineryProducing(producing);
|
summary.setMachineryStop(stop);
|
summary.setMachineryMaintenance(maintenance);
|
|
// 5. 异常/待办
|
summary.setAndonActiveCount(homeStatisticsMapper.selectAndonActiveCount());
|
summary.setRepairActiveCount(homeStatisticsMapper.selectRepairActiveCount());
|
|
// 6. 质检合格率(来料 / 过程 / 出货)
|
// 数量口径 = 合格数量 / 检验数量;不过滤单据状态,也不做时间范围限制(全量累计)
|
Map<String, Map<String, BigDecimal>> qcSummary = new HashMap<>();
|
for (Map<String, Object> row : homeStatisticsMapper.selectQcQualifiedSummary()) {
|
Map<String, BigDecimal> metrics = new HashMap<>();
|
metrics.put("checkQuantity", getDecimalFromObj(row.get("checkQuantity")));
|
metrics.put("qualifiedQuantity", getDecimalFromObj(row.get("qualifiedQuantity")));
|
qcSummary.put(String.valueOf(row.get("type")), metrics);
|
}
|
summary.setIqcQualifiedRate(calcQcQualifiedRate(qcSummary.get("IQC")));
|
summary.setIpqcQualifiedRate(calcQcQualifiedRate(qcSummary.get("IPQC")));
|
summary.setOqcQualifiedRate(calcQcQualifiedRate(qcSummary.get("OQC")));
|
// 三个环节各自的检验数量合计(合格率的分母),供卡片副行展示样本量
|
summary.setIqcCheckQuantity(calcCheckQuantity(qcSummary.get("IQC")));
|
summary.setIpqcCheckQuantity(calcCheckQuantity(qcSummary.get("IPQC")));
|
summary.setOqcCheckQuantity(calcCheckQuantity(qcSummary.get("OQC")));
|
|
return summary;
|
}
|
|
@Override
|
public List<MesHomeWorkOrderStatusRespVO> getWorkOrderStatusDistribution() {
|
List<Map<String, Object>> stats = homeStatisticsMapper.selectWorkOrderCountGroupByStatus();
|
List<MesHomeWorkOrderStatusRespVO> result = new ArrayList<>();
|
for (MesProWorkOrderStatusEnum statusEnum : MesProWorkOrderStatusEnum.values()) {
|
long count = 0L;
|
for (Map<String, Object> stat : stats) {
|
if (((Number) stat.get("status")).intValue() == statusEnum.getStatus()) {
|
count = ((Number) stat.get("count")).longValue();
|
break;
|
}
|
}
|
result.add(new MesHomeWorkOrderStatusRespVO(statusEnum.getStatus(), statusEnum.getName(), count));
|
}
|
return result;
|
}
|
|
@Override
|
public List<MesHomeProductionTrendRespVO> getProductionTrend(Integer days) {
|
LocalDate endDate = LocalDate.now().plusDays(1);
|
LocalDate startDate = endDate.minusDays(days);
|
LocalDateTime beginTime = startDate.atStartOfDay();
|
LocalDateTime endTime = endDate.atStartOfDay();
|
|
// 查询数据库
|
List<Map<String, Object>> dbData = homeStatisticsMapper.selectDailyFeedbackTrend(beginTime, endTime);
|
Map<String, Map<String, Object>> dateMap = new LinkedHashMap<>();
|
for (Map<String, Object> row : dbData) {
|
dateMap.put((String) row.get("date"), row);
|
}
|
|
// 补全所有日期(无数据的日期填 0)
|
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
List<MesHomeProductionTrendRespVO> result = new ArrayList<>();
|
for (LocalDate d = startDate; d.isBefore(endDate); d = d.plusDays(1)) {
|
String dateStr = d.format(fmt);
|
Map<String, Object> row = dateMap.get(dateStr);
|
MesHomeProductionTrendRespVO vo = new MesHomeProductionTrendRespVO();
|
vo.setDate(dateStr);
|
if (row != null) {
|
vo.setQuantity(getDecimalFromObj(row.get("quantity")));
|
vo.setQualifiedQuantity(getDecimalFromObj(row.get("qualifiedQuantity")));
|
vo.setUnqualifiedQuantity(getDecimalFromObj(row.get("unqualifiedQuantity")));
|
} else {
|
vo.setQuantity(BigDecimal.ZERO);
|
vo.setQualifiedQuantity(BigDecimal.ZERO);
|
vo.setUnqualifiedQuantity(BigDecimal.ZERO);
|
}
|
result.add(vo);
|
}
|
return result;
|
}
|
|
// ========== 工具方法 ==========
|
|
private BigDecimal getDecimal(Map<String, BigDecimal> map, String key) {
|
if (map == null || map.get(key) == null) {
|
return BigDecimal.ZERO;
|
}
|
return map.get(key);
|
}
|
|
private BigDecimal getDecimalFromObj(Object obj) {
|
if (obj == null) {
|
return BigDecimal.ZERO;
|
}
|
if (obj instanceof BigDecimal) {
|
return (BigDecimal) obj;
|
}
|
return new BigDecimal(obj.toString());
|
}
|
|
/**
|
* 计算质检合格率(百分比,保留两位小数)
|
*
|
* 检验数量为 0(即没有已完成的质检单)时返回 null,
|
* 让页面显示「—」,而不是误导性的 0%。
|
*/
|
private BigDecimal calcQcQualifiedRate(Map<String, BigDecimal> qcSummary) {
|
if (qcSummary == null) {
|
return null;
|
}
|
BigDecimal checkedQuantity = getDecimal(qcSummary, "checkQuantity");
|
if (checkedQuantity.compareTo(BigDecimal.ZERO) <= 0) {
|
return null;
|
}
|
return getDecimal(qcSummary, "qualifiedQuantity")
|
.multiply(BigDecimal.valueOf(100))
|
.divide(checkedQuantity, 2, RoundingMode.HALF_UP);
|
}
|
|
/**
|
* 取该检验环节的检验数量合计(合格率的分母,即卡片副行展示的样本量)
|
*
|
* 该环节没有任何质检单时返回 0。
|
*/
|
private Long calcCheckQuantity(Map<String, BigDecimal> qcSummary) {
|
if (qcSummary == null) {
|
return 0L;
|
}
|
return getDecimal(qcSummary, "checkQuantity").longValue();
|
}
|
|
}
|