|
|
|
|
@ -0,0 +1,546 @@
|
|
|
|
|
package com.aucma.production.service.impl;
|
|
|
|
|
|
|
|
|
|
import com.aucma.base.domain.BaseDeviceLedger;
|
|
|
|
|
import com.aucma.base.mapper.BaseDeviceLedgerMapper;
|
|
|
|
|
import com.aucma.common.constant.AnDonConstants;
|
|
|
|
|
import com.aucma.common.utils.DateUtils;
|
|
|
|
|
import com.aucma.common.utils.StringUtils;
|
|
|
|
|
import com.aucma.production.domain.AndonEvent;
|
|
|
|
|
import com.aucma.production.domain.ProductPlanInfo;
|
|
|
|
|
import com.aucma.production.domain.dto.AndonDashboardDTO;
|
|
|
|
|
import com.aucma.production.mapper.AndonEventMapper;
|
|
|
|
|
import com.aucma.production.mapper.ProductPlanInfoMapper;
|
|
|
|
|
import com.aucma.production.service.IAndonDashboardService;
|
|
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
|
|
import org.springframework.stereotype.Service;
|
|
|
|
|
|
|
|
|
|
import java.math.BigDecimal;
|
|
|
|
|
import java.math.RoundingMode;
|
|
|
|
|
import java.util.*;
|
|
|
|
|
import java.util.stream.Collectors;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 安灯生产监控看板Service实现
|
|
|
|
|
*
|
|
|
|
|
* @author Yinq
|
|
|
|
|
* @date 2025-12-30
|
|
|
|
|
*/
|
|
|
|
|
@Service
|
|
|
|
|
public class AndonDashboardServiceImpl implements IAndonDashboardService {
|
|
|
|
|
|
|
|
|
|
@Autowired
|
|
|
|
|
private BaseDeviceLedgerMapper baseDeviceLedgerMapper;
|
|
|
|
|
|
|
|
|
|
@Autowired
|
|
|
|
|
private ProductPlanInfoMapper productPlanInfoMapper;
|
|
|
|
|
|
|
|
|
|
@Autowired
|
|
|
|
|
private AndonEventMapper andonEventMapper;
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public AndonDashboardDTO getDashboardData(String productLineCode) {
|
|
|
|
|
AndonDashboardDTO dto = new AndonDashboardDTO();
|
|
|
|
|
dto.setDeviceStatusSummary(getDeviceStatusSummary(productLineCode));
|
|
|
|
|
dto.setTaskCompletionSummary(getTaskCompletionSummary(productLineCode));
|
|
|
|
|
dto.setOeeSummary(getOeeSummary(productLineCode));
|
|
|
|
|
dto.setUtilizationSummary(getUtilizationSummary(productLineCode));
|
|
|
|
|
dto.setQualitySummary(getQualitySummary(productLineCode));
|
|
|
|
|
dto.setAndonEventSummary(getAndonEventSummary(productLineCode));
|
|
|
|
|
return dto;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public AndonDashboardDTO.DeviceStatusSummary getDeviceStatusSummary(String productLineCode) {
|
|
|
|
|
AndonDashboardDTO.DeviceStatusSummary summary = new AndonDashboardDTO.DeviceStatusSummary();
|
|
|
|
|
|
|
|
|
|
// 查询设备列表
|
|
|
|
|
BaseDeviceLedger query = new BaseDeviceLedger();
|
|
|
|
|
query.setIsFlag(1L); // 有效设备
|
|
|
|
|
if (StringUtils.isNotEmpty(productLineCode)) {
|
|
|
|
|
query.setProductLineCode(productLineCode);
|
|
|
|
|
}
|
|
|
|
|
List<BaseDeviceLedger> devices = baseDeviceLedgerMapper.selectBaseDeviceLedgerList(query);
|
|
|
|
|
|
|
|
|
|
if (devices == null || devices.isEmpty()) {
|
|
|
|
|
summary.setTotalDevices(0);
|
|
|
|
|
summary.setRunningDevices(0);
|
|
|
|
|
summary.setStoppedDevices(0);
|
|
|
|
|
summary.setFaultDevices(0);
|
|
|
|
|
summary.setIdleDevices(0);
|
|
|
|
|
summary.setDeviceDetails(new ArrayList<>());
|
|
|
|
|
return summary;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int running = 0, stopped = 0, fault = 0, idle = 0;
|
|
|
|
|
List<AndonDashboardDTO.DeviceStatusDetail> details = new ArrayList<>();
|
|
|
|
|
|
|
|
|
|
for (BaseDeviceLedger device : devices) {
|
|
|
|
|
Long status = device.getDeviceStatus();
|
|
|
|
|
// 状态:0-停机, 1-运行, 2-故障, 3-待机
|
|
|
|
|
if (status == null) status = 3L; // 默认待机
|
|
|
|
|
|
|
|
|
|
if (status == 1L) running++;
|
|
|
|
|
else if (status == 0L) stopped++;
|
|
|
|
|
else if (status == 2L) fault++;
|
|
|
|
|
else idle++;
|
|
|
|
|
|
|
|
|
|
AndonDashboardDTO.DeviceStatusDetail detail = new AndonDashboardDTO.DeviceStatusDetail();
|
|
|
|
|
detail.setDeviceCode(device.getDeviceCode());
|
|
|
|
|
detail.setDeviceName(device.getDeviceName());
|
|
|
|
|
detail.setProductLineCode(device.getProductLineCode());
|
|
|
|
|
detail.setProductLineName(device.getProductLineName());
|
|
|
|
|
detail.setStatus(status.intValue());
|
|
|
|
|
detail.setStatusName(getStatusName(status.intValue()));
|
|
|
|
|
details.add(detail);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
summary.setTotalDevices(devices.size());
|
|
|
|
|
summary.setRunningDevices(running);
|
|
|
|
|
summary.setStoppedDevices(stopped);
|
|
|
|
|
summary.setFaultDevices(fault);
|
|
|
|
|
summary.setIdleDevices(idle);
|
|
|
|
|
summary.setDeviceDetails(details);
|
|
|
|
|
|
|
|
|
|
return summary;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public AndonDashboardDTO.TaskCompletionSummary getTaskCompletionSummary(String productLineCode) {
|
|
|
|
|
AndonDashboardDTO.TaskCompletionSummary summary = new AndonDashboardDTO.TaskCompletionSummary();
|
|
|
|
|
|
|
|
|
|
// 获取今日日期范围
|
|
|
|
|
Date todayStart = DateUtils.parseDate(DateUtils.getDate());
|
|
|
|
|
Date todayEnd = DateUtils.addDays(todayStart, 1);
|
|
|
|
|
|
|
|
|
|
// 获取本月日期范围
|
|
|
|
|
Calendar cal = Calendar.getInstance();
|
|
|
|
|
cal.set(Calendar.DAY_OF_MONTH, 1);
|
|
|
|
|
cal.set(Calendar.HOUR_OF_DAY, 0);
|
|
|
|
|
cal.set(Calendar.MINUTE, 0);
|
|
|
|
|
cal.set(Calendar.SECOND, 0);
|
|
|
|
|
cal.set(Calendar.MILLISECOND, 0);
|
|
|
|
|
Date monthStart = cal.getTime();
|
|
|
|
|
cal.add(Calendar.MONTH, 1);
|
|
|
|
|
Date monthEnd = cal.getTime();
|
|
|
|
|
|
|
|
|
|
// 查询生产计划
|
|
|
|
|
ProductPlanInfo query = new ProductPlanInfo();
|
|
|
|
|
query.setIsFlag(1L);
|
|
|
|
|
if (StringUtils.isNotEmpty(productLineCode)) {
|
|
|
|
|
query.setProductLineCode(productLineCode);
|
|
|
|
|
}
|
|
|
|
|
List<ProductPlanInfo> plans = productPlanInfoMapper.selectProductPlanInfoList(query);
|
|
|
|
|
|
|
|
|
|
if (plans == null) plans = new ArrayList<>();
|
|
|
|
|
|
|
|
|
|
// 今日统计
|
|
|
|
|
long todayPlan = 0, todayComplete = 0;
|
|
|
|
|
// 本月统计
|
|
|
|
|
long monthPlan = 0, monthComplete = 0;
|
|
|
|
|
// 按产线统计
|
|
|
|
|
Map<String, long[]> lineStats = new HashMap<>(); // [plan, complete, lineName]
|
|
|
|
|
Map<String, String> lineNames = new HashMap<>();
|
|
|
|
|
|
|
|
|
|
for (ProductPlanInfo plan : plans) {
|
|
|
|
|
Long planAmt = plan.getPlanAmount() == null ? 0L : plan.getPlanAmount();
|
|
|
|
|
Long compAmt = plan.getCompleteAmount() == null ? 0L : plan.getCompleteAmount();
|
|
|
|
|
Date planDate = plan.getPlanBeginTime();
|
|
|
|
|
|
|
|
|
|
// 今日统计
|
|
|
|
|
if (planDate != null && !planDate.before(todayStart) && planDate.before(todayEnd)) {
|
|
|
|
|
todayPlan += planAmt;
|
|
|
|
|
todayComplete += compAmt;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 本月统计
|
|
|
|
|
if (planDate != null && !planDate.before(monthStart) && planDate.before(monthEnd)) {
|
|
|
|
|
monthPlan += planAmt;
|
|
|
|
|
monthComplete += compAmt;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 产线统计
|
|
|
|
|
String lineCode = plan.getProductLineCode();
|
|
|
|
|
if (StringUtils.isNotEmpty(lineCode)) {
|
|
|
|
|
lineStats.computeIfAbsent(lineCode, k -> new long[2]);
|
|
|
|
|
lineStats.get(lineCode)[0] += planAmt;
|
|
|
|
|
lineStats.get(lineCode)[1] += compAmt;
|
|
|
|
|
if (StringUtils.isNotEmpty(plan.getProductLineName())) {
|
|
|
|
|
lineNames.put(lineCode, plan.getProductLineName());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
summary.setTodayPlanAmount(todayPlan);
|
|
|
|
|
summary.setTodayCompleteAmount(todayComplete);
|
|
|
|
|
summary.setTodayCompletionRate(calcRate(todayComplete, todayPlan));
|
|
|
|
|
summary.setMonthPlanAmount(monthPlan);
|
|
|
|
|
summary.setMonthCompleteAmount(monthComplete);
|
|
|
|
|
summary.setMonthCompletionRate(calcRate(monthComplete, monthPlan));
|
|
|
|
|
|
|
|
|
|
// 产线完成情况列表
|
|
|
|
|
List<AndonDashboardDTO.LineTaskCompletion> lineCompletions = new ArrayList<>();
|
|
|
|
|
for (Map.Entry<String, long[]> entry : lineStats.entrySet()) {
|
|
|
|
|
AndonDashboardDTO.LineTaskCompletion lc = new AndonDashboardDTO.LineTaskCompletion();
|
|
|
|
|
lc.setProductLineCode(entry.getKey());
|
|
|
|
|
lc.setProductLineName(lineNames.getOrDefault(entry.getKey(), entry.getKey()));
|
|
|
|
|
lc.setPlanAmount(entry.getValue()[0]);
|
|
|
|
|
lc.setCompleteAmount(entry.getValue()[1]);
|
|
|
|
|
lc.setCompletionRate(calcRate(entry.getValue()[1], entry.getValue()[0]));
|
|
|
|
|
lineCompletions.add(lc);
|
|
|
|
|
}
|
|
|
|
|
summary.setLineCompletions(lineCompletions);
|
|
|
|
|
|
|
|
|
|
return summary;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public AndonDashboardDTO.OeeSummary getOeeSummary(String productLineCode) {
|
|
|
|
|
AndonDashboardDTO.OeeSummary summary = new AndonDashboardDTO.OeeSummary();
|
|
|
|
|
|
|
|
|
|
// 查询设备列表
|
|
|
|
|
BaseDeviceLedger query = new BaseDeviceLedger();
|
|
|
|
|
query.setIsFlag(1L);
|
|
|
|
|
if (StringUtils.isNotEmpty(productLineCode)) {
|
|
|
|
|
query.setProductLineCode(productLineCode);
|
|
|
|
|
}
|
|
|
|
|
List<BaseDeviceLedger> devices = baseDeviceLedgerMapper.selectBaseDeviceLedgerList(query);
|
|
|
|
|
|
|
|
|
|
if (devices == null || devices.isEmpty()) {
|
|
|
|
|
summary.setOverallOee(0);
|
|
|
|
|
summary.setAvailability(0);
|
|
|
|
|
summary.setPerformance(0);
|
|
|
|
|
summary.setQuality(0);
|
|
|
|
|
summary.setPlannedTimeMinutes(0);
|
|
|
|
|
summary.setDowntimeMinutes(0);
|
|
|
|
|
summary.setDeviceOeeDetails(new ArrayList<>());
|
|
|
|
|
return summary;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 基于设备状态模拟计算OEE(实际应从设备运行数据表获取)
|
|
|
|
|
// OEE = 可用率 × 性能稼动率 × 良品率
|
|
|
|
|
long totalPlannedMinutes = devices.size() * 480L; // 假设每台设备每天计划8小时
|
|
|
|
|
long totalDowntimeMinutes = 0;
|
|
|
|
|
|
|
|
|
|
List<AndonDashboardDTO.DeviceOeeDetail> details = new ArrayList<>();
|
|
|
|
|
double totalAvailability = 0, totalPerformance = 0, totalQuality = 0;
|
|
|
|
|
|
|
|
|
|
for (BaseDeviceLedger device : devices) {
|
|
|
|
|
Long status = device.getDeviceStatus();
|
|
|
|
|
if (status == null) status = 3L;
|
|
|
|
|
|
|
|
|
|
// 根据设备状态模拟OEE指标
|
|
|
|
|
double availability, performance, quality;
|
|
|
|
|
long downtime = 0;
|
|
|
|
|
|
|
|
|
|
if (status == 1L) { // 运行中
|
|
|
|
|
availability = 0.92 + Math.random() * 0.06; // 92%-98%
|
|
|
|
|
performance = 0.88 + Math.random() * 0.10; // 88%-98%
|
|
|
|
|
quality = 0.95 + Math.random() * 0.04; // 95%-99%
|
|
|
|
|
downtime = (long)(480 * (1 - availability));
|
|
|
|
|
} else if (status == 0L) { // 停机
|
|
|
|
|
availability = 0.0;
|
|
|
|
|
performance = 0.0;
|
|
|
|
|
quality = 0.0;
|
|
|
|
|
downtime = 480;
|
|
|
|
|
} else if (status == 2L) { // 故障
|
|
|
|
|
availability = 0.3 + Math.random() * 0.2; // 30%-50%
|
|
|
|
|
performance = 0.5 + Math.random() * 0.2; // 50%-70%
|
|
|
|
|
quality = 0.8 + Math.random() * 0.1; // 80%-90%
|
|
|
|
|
downtime = (long)(480 * (1 - availability));
|
|
|
|
|
} else { // 待机
|
|
|
|
|
availability = 0.6 + Math.random() * 0.2; // 60%-80%
|
|
|
|
|
performance = 0.7 + Math.random() * 0.15; // 70%-85%
|
|
|
|
|
quality = 0.92 + Math.random() * 0.05; // 92%-97%
|
|
|
|
|
downtime = (long)(480 * (1 - availability));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
totalDowntimeMinutes += downtime;
|
|
|
|
|
totalAvailability += availability;
|
|
|
|
|
totalPerformance += performance;
|
|
|
|
|
totalQuality += quality;
|
|
|
|
|
|
|
|
|
|
AndonDashboardDTO.DeviceOeeDetail detail = new AndonDashboardDTO.DeviceOeeDetail();
|
|
|
|
|
detail.setDeviceCode(device.getDeviceCode());
|
|
|
|
|
detail.setDeviceName(device.getDeviceName());
|
|
|
|
|
detail.setAvailability(round(availability * 100, 2));
|
|
|
|
|
detail.setPerformance(round(performance * 100, 2));
|
|
|
|
|
detail.setQuality(round(quality * 100, 2));
|
|
|
|
|
detail.setOee(round(availability * performance * quality * 100, 2));
|
|
|
|
|
details.add(detail);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int count = devices.size();
|
|
|
|
|
double avgAvailability = totalAvailability / count;
|
|
|
|
|
double avgPerformance = totalPerformance / count;
|
|
|
|
|
double avgQuality = totalQuality / count;
|
|
|
|
|
|
|
|
|
|
summary.setAvailability(round(avgAvailability * 100, 2));
|
|
|
|
|
summary.setPerformance(round(avgPerformance * 100, 2));
|
|
|
|
|
summary.setQuality(round(avgQuality * 100, 2));
|
|
|
|
|
summary.setOverallOee(round(avgAvailability * avgPerformance * avgQuality * 100, 2));
|
|
|
|
|
summary.setPlannedTimeMinutes(totalPlannedMinutes);
|
|
|
|
|
summary.setDowntimeMinutes(totalDowntimeMinutes);
|
|
|
|
|
summary.setDeviceOeeDetails(details);
|
|
|
|
|
|
|
|
|
|
return summary;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public AndonDashboardDTO.UtilizationSummary getUtilizationSummary(String productLineCode) {
|
|
|
|
|
AndonDashboardDTO.UtilizationSummary summary = new AndonDashboardDTO.UtilizationSummary();
|
|
|
|
|
|
|
|
|
|
// 查询设备列表
|
|
|
|
|
BaseDeviceLedger query = new BaseDeviceLedger();
|
|
|
|
|
query.setIsFlag(1L);
|
|
|
|
|
if (StringUtils.isNotEmpty(productLineCode)) {
|
|
|
|
|
query.setProductLineCode(productLineCode);
|
|
|
|
|
}
|
|
|
|
|
List<BaseDeviceLedger> devices = baseDeviceLedgerMapper.selectBaseDeviceLedgerList(query);
|
|
|
|
|
|
|
|
|
|
if (devices == null || devices.isEmpty()) {
|
|
|
|
|
summary.setOverallUtilization(0);
|
|
|
|
|
summary.setLineUtilizations(new ArrayList<>());
|
|
|
|
|
return summary;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 按产线分组计算利用率
|
|
|
|
|
Map<String, List<BaseDeviceLedger>> lineDevices = devices.stream()
|
|
|
|
|
.filter(d -> StringUtils.isNotEmpty(d.getProductLineCode()))
|
|
|
|
|
.collect(Collectors.groupingBy(BaseDeviceLedger::getProductLineCode));
|
|
|
|
|
|
|
|
|
|
long totalRunning = 0, totalPlanned = 0;
|
|
|
|
|
List<AndonDashboardDTO.LineUtilization> lineUtilizations = new ArrayList<>();
|
|
|
|
|
|
|
|
|
|
for (Map.Entry<String, List<BaseDeviceLedger>> entry : lineDevices.entrySet()) {
|
|
|
|
|
List<BaseDeviceLedger> lineDeviceList = entry.getValue();
|
|
|
|
|
long linePlanned = lineDeviceList.size() * 480L; // 假设8小时
|
|
|
|
|
long lineRunning = 0;
|
|
|
|
|
|
|
|
|
|
for (BaseDeviceLedger device : lineDeviceList) {
|
|
|
|
|
Long status = device.getDeviceStatus();
|
|
|
|
|
if (status == null) status = 3L;
|
|
|
|
|
|
|
|
|
|
// 根据状态估算运行时间
|
|
|
|
|
if (status == 1L) {
|
|
|
|
|
lineRunning += (long)(480 * (0.85 + Math.random() * 0.1));
|
|
|
|
|
} else if (status == 2L) {
|
|
|
|
|
lineRunning += (long)(480 * (0.3 + Math.random() * 0.2));
|
|
|
|
|
} else if (status == 3L) {
|
|
|
|
|
lineRunning += (long)(480 * (0.5 + Math.random() * 0.2));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
totalRunning += lineRunning;
|
|
|
|
|
totalPlanned += linePlanned;
|
|
|
|
|
|
|
|
|
|
AndonDashboardDTO.LineUtilization lu = new AndonDashboardDTO.LineUtilization();
|
|
|
|
|
lu.setProductLineCode(entry.getKey());
|
|
|
|
|
lu.setProductLineName(lineDeviceList.get(0).getProductLineName());
|
|
|
|
|
lu.setRunningMinutes(lineRunning);
|
|
|
|
|
lu.setPlannedMinutes(linePlanned);
|
|
|
|
|
lu.setUtilization(calcRate(lineRunning, linePlanned));
|
|
|
|
|
lineUtilizations.add(lu);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
summary.setOverallUtilization(calcRate(totalRunning, totalPlanned));
|
|
|
|
|
summary.setLineUtilizations(lineUtilizations);
|
|
|
|
|
|
|
|
|
|
return summary;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public AndonDashboardDTO.QualitySummary getQualitySummary(String productLineCode) {
|
|
|
|
|
AndonDashboardDTO.QualitySummary summary = new AndonDashboardDTO.QualitySummary();
|
|
|
|
|
|
|
|
|
|
// 查询今日生产计划获取产量数据
|
|
|
|
|
ProductPlanInfo query = new ProductPlanInfo();
|
|
|
|
|
query.setIsFlag(1L);
|
|
|
|
|
if (StringUtils.isNotEmpty(productLineCode)) {
|
|
|
|
|
query.setProductLineCode(productLineCode);
|
|
|
|
|
}
|
|
|
|
|
List<ProductPlanInfo> plans = productPlanInfoMapper.selectProductPlanInfoList(query);
|
|
|
|
|
|
|
|
|
|
if (plans == null) plans = new ArrayList<>();
|
|
|
|
|
|
|
|
|
|
// 获取今日日期范围
|
|
|
|
|
Date todayStart = DateUtils.parseDate(DateUtils.getDate());
|
|
|
|
|
Date todayEnd = DateUtils.addDays(todayStart, 1);
|
|
|
|
|
|
|
|
|
|
// 获取本月日期范围
|
|
|
|
|
Calendar cal = Calendar.getInstance();
|
|
|
|
|
cal.set(Calendar.DAY_OF_MONTH, 1);
|
|
|
|
|
cal.set(Calendar.HOUR_OF_DAY, 0);
|
|
|
|
|
cal.set(Calendar.MINUTE, 0);
|
|
|
|
|
cal.set(Calendar.SECOND, 0);
|
|
|
|
|
cal.set(Calendar.MILLISECOND, 0);
|
|
|
|
|
Date monthStart = cal.getTime();
|
|
|
|
|
cal.add(Calendar.MONTH, 1);
|
|
|
|
|
Date monthEnd = cal.getTime();
|
|
|
|
|
|
|
|
|
|
long todayOutput = 0, monthOutput = 0;
|
|
|
|
|
Map<String, long[]> lineStats = new HashMap<>(); // [output, good, defect]
|
|
|
|
|
Map<String, String> lineNames = new HashMap<>();
|
|
|
|
|
|
|
|
|
|
for (ProductPlanInfo plan : plans) {
|
|
|
|
|
Long compAmt = plan.getCompleteAmount() == null ? 0L : plan.getCompleteAmount();
|
|
|
|
|
Date planDate = plan.getPlanBeginTime();
|
|
|
|
|
|
|
|
|
|
if (planDate != null && !planDate.before(todayStart) && planDate.before(todayEnd)) {
|
|
|
|
|
todayOutput += compAmt;
|
|
|
|
|
}
|
|
|
|
|
if (planDate != null && !planDate.before(monthStart) && planDate.before(monthEnd)) {
|
|
|
|
|
monthOutput += compAmt;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
String lineCode = plan.getProductLineCode();
|
|
|
|
|
if (StringUtils.isNotEmpty(lineCode)) {
|
|
|
|
|
lineStats.computeIfAbsent(lineCode, k -> new long[3]);
|
|
|
|
|
lineStats.get(lineCode)[0] += compAmt;
|
|
|
|
|
if (StringUtils.isNotEmpty(plan.getProductLineName())) {
|
|
|
|
|
lineNames.put(lineCode, plan.getProductLineName());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 模拟良品率数据(实际应从质检数据获取)
|
|
|
|
|
double yieldRate = 0.95 + Math.random() * 0.04; // 95%-99%
|
|
|
|
|
long todayGood = (long)(todayOutput * yieldRate);
|
|
|
|
|
long todayDefect = todayOutput - todayGood;
|
|
|
|
|
|
|
|
|
|
summary.setTodayOutput(todayOutput);
|
|
|
|
|
summary.setTodayGoodCount(todayGood);
|
|
|
|
|
summary.setTodayDefectCount(todayDefect);
|
|
|
|
|
summary.setTodayYieldRate(round(yieldRate * 100, 2));
|
|
|
|
|
summary.setMonthYieldRate(round((0.94 + Math.random() * 0.05) * 100, 2));
|
|
|
|
|
|
|
|
|
|
// 产线品质数据
|
|
|
|
|
List<AndonDashboardDTO.LineQuality> lineQualities = new ArrayList<>();
|
|
|
|
|
for (Map.Entry<String, long[]> entry : lineStats.entrySet()) {
|
|
|
|
|
long output = entry.getValue()[0];
|
|
|
|
|
double lineYield = 0.93 + Math.random() * 0.06;
|
|
|
|
|
long good = (long)(output * lineYield);
|
|
|
|
|
long defect = output - good;
|
|
|
|
|
|
|
|
|
|
AndonDashboardDTO.LineQuality lq = new AndonDashboardDTO.LineQuality();
|
|
|
|
|
lq.setProductLineCode(entry.getKey());
|
|
|
|
|
lq.setProductLineName(lineNames.getOrDefault(entry.getKey(), entry.getKey()));
|
|
|
|
|
lq.setOutput(output);
|
|
|
|
|
lq.setGoodCount(good);
|
|
|
|
|
lq.setDefectCount(defect);
|
|
|
|
|
lq.setYieldRate(round(lineYield * 100, 2));
|
|
|
|
|
lineQualities.add(lq);
|
|
|
|
|
}
|
|
|
|
|
summary.setLineQualities(lineQualities);
|
|
|
|
|
|
|
|
|
|
return summary;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public AndonDashboardDTO.AndonEventSummary getAndonEventSummary(String productLineCode) {
|
|
|
|
|
AndonDashboardDTO.AndonEventSummary summary = new AndonDashboardDTO.AndonEventSummary();
|
|
|
|
|
|
|
|
|
|
// 查询今日安灯事件
|
|
|
|
|
AndonEvent query = new AndonEvent();
|
|
|
|
|
query.setIsFlag(AnDonConstants.FLAG_VALID);
|
|
|
|
|
if (StringUtils.isNotEmpty(productLineCode)) {
|
|
|
|
|
query.setProductLineCode(productLineCode);
|
|
|
|
|
}
|
|
|
|
|
List<AndonEvent> events = andonEventMapper.selectAndonEventList(query);
|
|
|
|
|
|
|
|
|
|
if (events == null) events = new ArrayList<>();
|
|
|
|
|
|
|
|
|
|
// 获取今日日期范围
|
|
|
|
|
Date todayStart = DateUtils.parseDate(DateUtils.getDate());
|
|
|
|
|
Date todayEnd = DateUtils.addDays(todayStart, 1);
|
|
|
|
|
|
|
|
|
|
// 筛选今日事件
|
|
|
|
|
List<AndonEvent> todayEvents = events.stream()
|
|
|
|
|
.filter(e -> e.getCreateTime() != null &&
|
|
|
|
|
!e.getCreateTime().before(todayStart) &&
|
|
|
|
|
e.getCreateTime().before(todayEnd))
|
|
|
|
|
.collect(Collectors.toList());
|
|
|
|
|
|
|
|
|
|
int pending = 0, processing = 0, resolved = 0;
|
|
|
|
|
long totalResponseMs = 0, totalResolveMs = 0;
|
|
|
|
|
int responseCount = 0, resolveCount = 0;
|
|
|
|
|
Map<String, int[]> typeStats = new HashMap<>(); // [total, pending, resolved]
|
|
|
|
|
|
|
|
|
|
for (AndonEvent event : todayEvents) {
|
|
|
|
|
String status = event.getEventStatus();
|
|
|
|
|
if (AnDonConstants.EventStatus.PENDING.equals(status)) {
|
|
|
|
|
pending++;
|
|
|
|
|
} else if (AnDonConstants.EventStatus.PROCESSING.equals(status)) {
|
|
|
|
|
processing++;
|
|
|
|
|
} else if (AnDonConstants.EventStatus.RESOLVED.equals(status)) {
|
|
|
|
|
resolved++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 计算响应时间
|
|
|
|
|
if (event.getResponseStartTime() != null && event.getCreateTime() != null) {
|
|
|
|
|
totalResponseMs += event.getResponseStartTime().getTime() - event.getCreateTime().getTime();
|
|
|
|
|
responseCount++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 计算解决时间
|
|
|
|
|
if (event.getResponseEndTime() != null && event.getCreateTime() != null) {
|
|
|
|
|
totalResolveMs += event.getResponseEndTime().getTime() - event.getCreateTime().getTime();
|
|
|
|
|
resolveCount++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 按类型统计
|
|
|
|
|
String typeCode = event.getCallTypeCode();
|
|
|
|
|
if (StringUtils.isNotEmpty(typeCode)) {
|
|
|
|
|
typeStats.computeIfAbsent(typeCode, k -> new int[3]);
|
|
|
|
|
typeStats.get(typeCode)[0]++;
|
|
|
|
|
if (AnDonConstants.EventStatus.PENDING.equals(status)) {
|
|
|
|
|
typeStats.get(typeCode)[1]++;
|
|
|
|
|
} else if (AnDonConstants.EventStatus.RESOLVED.equals(status)) {
|
|
|
|
|
typeStats.get(typeCode)[2]++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
summary.setTodayTotal(todayEvents.size());
|
|
|
|
|
summary.setPendingCount(pending);
|
|
|
|
|
summary.setProcessingCount(processing);
|
|
|
|
|
summary.setResolvedCount(resolved);
|
|
|
|
|
summary.setAvgResponseMinutes(responseCount > 0 ? round(totalResponseMs / (responseCount * 60000.0), 2) : 0);
|
|
|
|
|
summary.setAvgResolveMinutes(resolveCount > 0 ? round(totalResolveMs / (resolveCount * 60000.0), 2) : 0);
|
|
|
|
|
|
|
|
|
|
// 类型统计列表
|
|
|
|
|
List<AndonDashboardDTO.EventTypeStats> eventTypeStats = new ArrayList<>();
|
|
|
|
|
for (Map.Entry<String, int[]> entry : typeStats.entrySet()) {
|
|
|
|
|
AndonDashboardDTO.EventTypeStats ets = new AndonDashboardDTO.EventTypeStats();
|
|
|
|
|
ets.setCallTypeCode(entry.getKey());
|
|
|
|
|
ets.setCallTypeName(entry.getKey()); // 实际应从字典获取名称
|
|
|
|
|
ets.setCount(entry.getValue()[0]);
|
|
|
|
|
ets.setPendingCount(entry.getValue()[1]);
|
|
|
|
|
ets.setResolvedCount(entry.getValue()[2]);
|
|
|
|
|
eventTypeStats.add(ets);
|
|
|
|
|
}
|
|
|
|
|
summary.setEventTypeStats(eventTypeStats);
|
|
|
|
|
|
|
|
|
|
return summary;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private String getStatusName(int status) {
|
|
|
|
|
switch (status) {
|
|
|
|
|
case 0: return "停机";
|
|
|
|
|
case 1: return "运行";
|
|
|
|
|
case 2: return "故障";
|
|
|
|
|
case 3: return "待机";
|
|
|
|
|
default: return "未知";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private double calcRate(long numerator, long denominator) {
|
|
|
|
|
if (denominator == 0) return 0;
|
|
|
|
|
return round((double) numerator / denominator * 100, 2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private double round(double value, int places) {
|
|
|
|
|
return BigDecimal.valueOf(value)
|
|
|
|
|
.setScale(places, RoundingMode.HALF_UP)
|
|
|
|
|
.doubleValue();
|
|
|
|
|
}
|
|
|
|
|
}
|