| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.service.safety; |
| | | |
| | | import cn.iocoder.yudao.module.mes.controller.admin.safety.vo.dashboard.MesSafetyDashboardRespVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.safety.vo.dashboard.MesSafetyDashboardRespVO.*; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.md.workstation.MesMdWorkshopDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.safety.*; |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.md.workstation.MesMdWorkshopMapper; |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.safety.*; |
| | | 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.time.LocalDate; |
| | | import java.time.LocalDateTime; |
| | | import java.util.*; |
| | | import java.util.function.Function; |
| | | import java.util.stream.Collectors; |
| | | |
| | | /** |
| | | * å®å
¨ç产 - å¯è§åçæ¿ Service å®ç° |
| | | * |
| | | * æ°æ®éå¨å½åè§æ¨¡ä¸è¾å°ï¼éç¨å
¨éæ¥è¯¢ + å
åèåï¼é¿å
为æ¯å¼ ç»è®¡åä¸å¥ SQL |
| | | * |
| | | * @author è¶
级管çå |
| | | */ |
| | | @Service |
| | | @Validated |
| | | public class MesSafetyDashboardServiceImpl implements MesSafetyDashboardService { |
| | | |
| | | @Resource |
| | | private MesSafetyRiskMapper riskMapper; |
| | | |
| | | @Resource |
| | | private MesSafetyRiskUnitMapper riskUnitMapper; |
| | | |
| | | @Resource |
| | | private MesSafetyHazardMapper hazardMapper; |
| | | |
| | | @Resource |
| | | private MesSafetyWorkTicketMapper ticketMapper; |
| | | |
| | | @Resource |
| | | private MesSafetyTrainingMapper trainingMapper; |
| | | |
| | | @Resource |
| | | private MesSafetyTrainingAttendeeMapper attendeeMapper; |
| | | |
| | | @Resource |
| | | private MesSafetyCertificateMapper certificateMapper; |
| | | |
| | | @Resource |
| | | private MesMdWorkshopMapper workshopMapper; |
| | | |
| | | /** Top N */ |
| | | private static final int TOP_N = 10; |
| | | /** æè¿å¨ææ¡æ° */ |
| | | private static final int RECENT_N = 5; |
| | | /** è¯ä¹¦å³å°å°æéå¼ */ |
| | | private static final long EXPIRING_SOON_DAYS = 30L; |
| | | |
| | | @Override |
| | | public MesSafetyDashboardRespVO getOverview() { |
| | | MesSafetyDashboardRespVO vo = new MesSafetyDashboardRespVO(); |
| | | LocalDate today = LocalDate.now(); |
| | | LocalDateTime now = LocalDateTime.now(); |
| | | |
| | | // 车é´åç§°æ å°ï¼å¤ä¸ªç»è®¡å
±ç¨ï¼ |
| | | Map<Long, String> workshopNameMap = loadWorkshopNames(); |
| | | |
| | | // ========== é£é© ========== |
| | | List<MesSafetyRiskDO> risks = riskMapper.selectAllList(); |
| | | vo.setRiskTotal((long) risks.size()); |
| | | vo.setRiskLevelStats(buildRiskLevelStats(risks)); |
| | | vo.setRiskByWorkshop(buildRiskByWorkshop(risks, workshopNameMap)); |
| | | |
| | | // ========== 鿣 ========== |
| | | List<MesSafetyHazardDO> hazards = hazardMapper.selectAllList(); |
| | | vo.setHazardStats(buildHazardStats(hazards, today)); |
| | | vo.setHazardByWorkshop(buildHazardByWorkshop(hazards, workshopNameMap)); |
| | | |
| | | // ========== ä½ä¸ç¥¨ ========== |
| | | List<MesSafetyWorkTicketDO> tickets = ticketMapper.selectAllList(); |
| | | vo.setTicketStats(buildTicketStats(tickets, now)); |
| | | |
| | | // ========== å¹è® ========== |
| | | List<MesSafetyTrainingDO> trainings = trainingMapper.selectAllList(); |
| | | vo.setTrainingStats(buildTrainingStats(trainings)); |
| | | |
| | | // ========== è¯ä¹¦ ========== |
| | | List<MesSafetyCertificateDO> certificates = certificateMapper.selectAllList(); |
| | | vo.setCertificateStats(buildCertificateStats(certificates, today)); |
| | | |
| | | // ========== æè¿å¨æ ========== |
| | | vo.setRecentHazards(buildRecentHazards(hazards)); |
| | | vo.setRecentTickets(buildRecentTickets(tickets)); |
| | | |
| | | return vo; |
| | | } |
| | | |
| | | // ==================== é£é© ==================== |
| | | |
| | | private List<NameCount> buildRiskLevelStats(List<MesSafetyRiskDO> risks) { |
| | | // 1é大 2è¾å¤§ 3ä¸è¬ 4ä½ |
| | | String[] levelNames = {"é大é£é©", "è¾å¤§é£é©", "ä¸è¬é£é©", "ä½é£é©"}; |
| | | Map<Integer, Long> countMap = risks.stream() |
| | | .filter(r -> r.getRiskLevel() != null) |
| | | .collect(Collectors.groupingBy(MesSafetyRiskDO::getRiskLevel, Collectors.counting())); |
| | | List<NameCount> list = new ArrayList<>(4); |
| | | for (int i = 1; i <= 4; i++) { |
| | | list.add(new NameCount(levelNames[i - 1], countMap.getOrDefault(i, 0L), i)); |
| | | } |
| | | return list; |
| | | } |
| | | |
| | | private List<NameCount> buildRiskByWorkshop(List<MesSafetyRiskDO> risks, Map<Long, String> workshopNameMap) { |
| | | // é£é©æ¬èº«æ²¡æè½¦é´ï¼ç»é£é©åå
ç workshopId å½å± |
| | | Set<Long> unitIds = risks.stream().map(MesSafetyRiskDO::getUnitId) |
| | | .filter(Objects::nonNull).collect(Collectors.toSet()); |
| | | if (unitIds.isEmpty()) { |
| | | return Collections.emptyList(); |
| | | } |
| | | Map<Long, MesSafetyRiskUnitDO> unitMap = riskUnitMapper.selectByIds(unitIds).stream() |
| | | .collect(Collectors.toMap(MesSafetyRiskUnitDO::getId, Function.identity(), (a, b) -> a)); |
| | | Map<Long, Long> countMap = new HashMap<>(); |
| | | for (MesSafetyRiskDO risk : risks) { |
| | | MesSafetyRiskUnitDO unit = unitMap.get(risk.getUnitId()); |
| | | if (unit == null || unit.getWorkshopId() == null) { |
| | | continue; |
| | | } |
| | | countMap.merge(unit.getWorkshopId(), 1L, Long::sum); |
| | | } |
| | | return toTopList(countMap, workshopNameMap); |
| | | } |
| | | |
| | | // ==================== 鿣 ==================== |
| | | |
| | | private HazardStats buildHazardStats(List<MesSafetyHazardDO> hazards, LocalDate today) { |
| | | HazardStats stats = new HazardStats(); |
| | | long total = hazards.size(); |
| | | long pending = 0, rectifying = 0, toCheck = 0, closed = 0, overdue = 0; |
| | | for (MesSafetyHazardDO h : hazards) { |
| | | Integer s = h.getStatus(); |
| | | if (MesSafetyHazardDO.STATUS_PENDING.equals(s)) pending++; |
| | | else if (MesSafetyHazardDO.STATUS_RECTIFYING.equals(s)) rectifying++; |
| | | else if (MesSafetyHazardDO.STATUS_TO_CHECK.equals(s)) toCheck++; |
| | | else if (MesSafetyHazardDO.STATUS_CLOSED.equals(s)) closed++; |
| | | if (h.getDeadline() != null |
| | | && !MesSafetyHazardDO.STATUS_CLOSED.equals(s) |
| | | && h.getDeadline().isBefore(today)) { |
| | | overdue++; |
| | | } |
| | | } |
| | | stats.setTotal(total); |
| | | stats.setPending(pending); |
| | | stats.setRectifying(rectifying); |
| | | stats.setToCheck(toCheck); |
| | | stats.setClosed(closed); |
| | | stats.setOverdue(overdue); |
| | | stats.setClosedRate(total > 0 |
| | | ? BigDecimal.valueOf(closed * 100.0 / total).setScale(1, RoundingMode.HALF_UP) |
| | | : BigDecimal.ZERO); |
| | | return stats; |
| | | } |
| | | |
| | | private List<NameCount> buildHazardByWorkshop(List<MesSafetyHazardDO> hazards, Map<Long, String> workshopNameMap) { |
| | | Map<Long, Long> countMap = new HashMap<>(); |
| | | for (MesSafetyHazardDO h : hazards) { |
| | | if (h.getWorkshopId() != null) { |
| | | countMap.merge(h.getWorkshopId(), 1L, Long::sum); |
| | | } |
| | | } |
| | | return toTopList(countMap, workshopNameMap); |
| | | } |
| | | |
| | | // ==================== ä½ä¸ç¥¨ ==================== |
| | | |
| | | private TicketStats buildTicketStats(List<MesSafetyWorkTicketDO> tickets, LocalDateTime now) { |
| | | TicketStats stats = new TicketStats(); |
| | | long pending = 0, approved = 0, finished = 0, archived = 0, rejected = 0, overdue = 0; |
| | | for (MesSafetyWorkTicketDO t : tickets) { |
| | | Integer s = t.getStatus(); |
| | | if (MesSafetyWorkTicketDO.STATUS_PENDING.equals(s)) pending++; |
| | | else if (MesSafetyWorkTicketDO.STATUS_APPROVED.equals(s)) approved++; |
| | | else if (MesSafetyWorkTicketDO.STATUS_FINISHED.equals(s)) finished++; |
| | | else if (MesSafetyWorkTicketDO.STATUS_ARCHIVED.equals(s)) archived++; |
| | | else if (MesSafetyWorkTicketDO.STATUS_REJECTED.equals(s)) rejected++; |
| | | if (MesSafetyWorkTicketDO.STATUS_APPROVED.equals(s) |
| | | && t.getPlanEndTime() != null |
| | | && t.getPlanEndTime().isBefore(now)) { |
| | | overdue++; |
| | | } |
| | | } |
| | | stats.setTotal((long) tickets.size()); |
| | | stats.setPending(pending); |
| | | stats.setApproved(approved); |
| | | stats.setFinished(finished); |
| | | stats.setArchived(archived); |
| | | stats.setRejected(rejected); |
| | | stats.setOverdue(overdue); |
| | | return stats; |
| | | } |
| | | |
| | | // ==================== å¹è® ==================== |
| | | |
| | | private TrainingStats buildTrainingStats(List<MesSafetyTrainingDO> trainings) { |
| | | TrainingStats stats = new TrainingStats(); |
| | | long finished = 0, ongoing = 0, planned = 0; |
| | | long attendeeTotal = 0; |
| | | BigDecimal scoreSum = BigDecimal.ZERO; |
| | | int scoredCount = 0; |
| | | for (MesSafetyTrainingDO t : trainings) { |
| | | Integer s = t.getStatus(); |
| | | if (MesSafetyTrainingDO.STATUS_FINISHED.equals(s)) finished++; |
| | | else if (MesSafetyTrainingDO.STATUS_ONGOING.equals(s)) ongoing++; |
| | | else if (MesSafetyTrainingDO.STATUS_PLANNED.equals(s)) planned++; |
| | | if (t.getAttendeeCount() != null) { |
| | | attendeeTotal += t.getAttendeeCount(); |
| | | } |
| | | if (t.getAvgScore() != null) { |
| | | scoreSum = scoreSum.add(t.getAvgScore()); |
| | | scoredCount++; |
| | | } |
| | | } |
| | | stats.setTotal((long) trainings.size()); |
| | | stats.setFinished(finished); |
| | | stats.setOngoing(ongoing); |
| | | stats.setPlanned(planned); |
| | | stats.setAttendeeTotal(attendeeTotal); |
| | | stats.setAvgScore(scoredCount > 0 |
| | | ? scoreSum.divide(BigDecimal.valueOf(scoredCount), 1, RoundingMode.HALF_UP) |
| | | : null); |
| | | return stats; |
| | | } |
| | | |
| | | // ==================== è¯ä¹¦ ==================== |
| | | |
| | | private CertificateStats buildCertificateStats(List<MesSafetyCertificateDO> certificates, LocalDate today) { |
| | | CertificateStats stats = new CertificateStats(); |
| | | long valid = 0, expired = 0, expiringSoon = 0; |
| | | for (MesSafetyCertificateDO c : certificates) { |
| | | boolean isValid = MesSafetyCertificateDO.STATUS_VALID.equals(c.getStatus()); |
| | | if (isValid) { |
| | | valid++; |
| | | } |
| | | if (c.getExpireDate() != null) { |
| | | long days = java.time.temporal.ChronoUnit.DAYS.between(today, c.getExpireDate()); |
| | | if (days < 0) { |
| | | expired++; |
| | | } else if (days <= EXPIRING_SOON_DAYS) { |
| | | expiringSoon++; |
| | | } |
| | | } |
| | | } |
| | | stats.setTotal((long) certificates.size()); |
| | | stats.setValid(valid); |
| | | stats.setExpired(expired); |
| | | stats.setExpiringSoon(expiringSoon); |
| | | return stats; |
| | | } |
| | | |
| | | // ==================== æè¿å¨æ ==================== |
| | | |
| | | private List<RecentHazard> buildRecentHazards(List<MesSafetyHazardDO> hazards) { |
| | | return hazards.stream() |
| | | .sorted(Comparator.comparing(MesSafetyHazardDO::getId, Comparator.nullsLast(Comparator.reverseOrder()))) |
| | | .limit(RECENT_N) |
| | | .map(h -> { |
| | | RecentHazard r = new RecentHazard(); |
| | | r.setId(h.getId()); |
| | | r.setCode(h.getCode()); |
| | | r.setName(h.getName()); |
| | | r.setHazardLevel(h.getHazardLevel()); |
| | | r.setStatus(h.getStatus()); |
| | | r.setFoundDate(h.getFoundDate()); |
| | | return r; |
| | | }) |
| | | .collect(Collectors.toList()); |
| | | } |
| | | |
| | | private List<RecentTicket> buildRecentTickets(List<MesSafetyWorkTicketDO> tickets) { |
| | | return tickets.stream() |
| | | .sorted(Comparator.comparing(MesSafetyWorkTicketDO::getId, Comparator.nullsLast(Comparator.reverseOrder()))) |
| | | .limit(RECENT_N) |
| | | .map(t -> { |
| | | RecentTicket r = new RecentTicket(); |
| | | r.setId(t.getId()); |
| | | r.setCode(t.getCode()); |
| | | r.setTicketType(t.getTicketType()); |
| | | r.setWorkLocation(t.getWorkLocation()); |
| | | r.setStatus(t.getStatus()); |
| | | r.setCreateTime(t.getCreateTime()); |
| | | return r; |
| | | }) |
| | | .collect(Collectors.toList()); |
| | | } |
| | | |
| | | // ==================== å·¥å
· ==================== |
| | | |
| | | private Map<Long, String> loadWorkshopNames() { |
| | | return workshopMapper.selectList().stream() |
| | | .collect(Collectors.toMap(MesMdWorkshopDO::getId, MesMdWorkshopDO::getName, (a, b) -> a)); |
| | | } |
| | | |
| | | private List<NameCount> toTopList(Map<Long, Long> countMap, Map<Long, String> nameMap) { |
| | | return countMap.entrySet().stream() |
| | | .sorted(Map.Entry.<Long, Long>comparingByValue().reversed()) |
| | | .limit(TOP_N) |
| | | .map(e -> new NameCount( |
| | | nameMap.getOrDefault(e.getKey(), "æªç¥è½¦é´"), |
| | | e.getValue(), |
| | | e.getKey().intValue())) |
| | | .collect(Collectors.toList()); |
| | | } |
| | | |
| | | } |