feat(report): 新增DMS设备相关分析报表功能

- 新增设备故障分析报表实体类DeviceFaultAnalysisReport,包含设备编码、名称、故障类型等属性
- 新增设备OEE分析报表实体类DeviceOeeReport,支持计算计划运行时间、停机时间、可用率等指标
- 新增维修工时统计报表实体类RepairHoursReport,统计执行人工单数量及工时
- 实现DmsReportMapper接口,定义设备故障分析、维修工时统计及设备OEE分析的数据库查询方法
- 编写对应的MyBatis映射文件DmsReportMapper.xml,支持动态查询参数和数据聚合
- 实现IDmsReportService接口及其实现类DmsReportServiceImpl,封装业务逻辑及OEE指标计算
- 新增DmsReportController,提供设备故障分析、维修工时统计、设备OEE分析的REST接口及Excel导出功能
- 支持传入时间等参数过滤报表数据,保证数据准确性和灵活性
master
zangch@mesnac.com 3 months ago
parent 5e1a856d05
commit 86fa02175b

@ -0,0 +1,102 @@
package com.aucma.report.controller;
import com.aucma.common.core.controller.BaseController;
import com.aucma.common.core.domain.AjaxResult;
import com.aucma.common.utils.poi.ExcelUtil;
import com.aucma.report.domain.DeviceFaultAnalysisReport;
import com.aucma.report.domain.DeviceOeeReport;
import com.aucma.report.domain.RepairHoursReport;
import com.aucma.report.service.IDmsReportService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
import java.util.Map;
/**
* DMS Controller
*/
@RestController
@RequestMapping("/report/dmsReport")
public class DmsReportController extends BaseController {
@Autowired
private IDmsReportService dmsReportService;
/**
*
*/
@GetMapping("/deviceFaultAnalysis")
public AjaxResult deviceFaultAnalysis(@RequestParam(required = false) Map<String, Object> params) {
List<DeviceFaultAnalysisReport> list = dmsReportService.deviceFaultAnalysisList(params);
return success(list);
}
/**
*
*/
@PostMapping("/deviceFaultAnalysis/export")
public void deviceFaultAnalysisExport(HttpServletResponse response,
@RequestParam(required = false) Map<String, Object> params) {
if (params != null) {
params.put("pageNum", null);
params.put("pageSize", null);
}
List<DeviceFaultAnalysisReport> list = dmsReportService.deviceFaultAnalysisList(params);
ExcelUtil<DeviceFaultAnalysisReport> util = new ExcelUtil<>(DeviceFaultAnalysisReport.class);
util.exportExcel(response, list, "设备故障分析");
}
/**
*
*/
@GetMapping("/repairHoursStat")
public AjaxResult repairHoursStat(@RequestParam(required = false) Map<String, Object> params) {
List<RepairHoursReport> list = dmsReportService.repairHoursReportList(params);
return success(list);
}
/**
*
*/
@PostMapping("/repairHoursStat/export")
public void repairHoursStatExport(HttpServletResponse response,
@RequestParam(required = false) Map<String, Object> params) {
if (params != null) {
params.put("pageNum", null);
params.put("pageSize", null);
}
List<RepairHoursReport> list = dmsReportService.repairHoursReportList(params);
ExcelUtil<RepairHoursReport> util = new ExcelUtil<>(RepairHoursReport.class);
util.exportExcel(response, list, "维修工时统计");
}
/**
* OEE
*/
@GetMapping("/deviceOeeAnalysis")
public AjaxResult deviceOeeAnalysis(@RequestParam(required = false) Map<String, Object> params) {
List<DeviceOeeReport> list = dmsReportService.deviceOeeReportList(params);
return success(list);
}
/**
* OEE
*/
@PostMapping("/deviceOeeAnalysis/export")
public void deviceOeeAnalysisExport(HttpServletResponse response,
@RequestParam(required = false) Map<String, Object> params) {
if (params != null) {
params.put("pageNum", null);
params.put("pageSize", null);
}
List<DeviceOeeReport> list = dmsReportService.deviceOeeReportList(params);
ExcelUtil<DeviceOeeReport> util = new ExcelUtil<>(DeviceOeeReport.class);
util.exportExcel(response, list, "设备OEE分析");
}
}

@ -0,0 +1,79 @@
package com.aucma.report.domain;
import com.aucma.common.annotation.Excel;
import com.aucma.common.core.domain.BaseEntity;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
*
*
* dms_bills_fault_instance + base_deviceledger
*/
public class DeviceFaultAnalysisReport extends BaseEntity {
private static final long serialVersionUID = 1L;
/** 设备编码 */
@Excel(name = "设备编码")
@JsonProperty("DEVICE_CODE")
private String DEVICE_CODE;
/** 设备名称 */
@Excel(name = "设备名称")
@JsonProperty("DEVICE_NAME")
private String DEVICE_NAME;
/** 故障类型 */
@Excel(name = "故障类型")
@JsonProperty("FAULT_TYPE")
private String FAULT_TYPE;
/** 故障次数 */
@Excel(name = "故障次数")
@JsonProperty("FAULT_COUNT")
private Long FAULT_COUNT;
/** 故障时长(分钟) */
@Excel(name = "故障时长(分钟)")
@JsonProperty("FAULT_DURATION_MINUTES")
private Long FAULT_DURATION_MINUTES;
public String getDEVICE_CODE() {
return DEVICE_CODE;
}
public void setDEVICE_CODE(String DEVICE_CODE) {
this.DEVICE_CODE = DEVICE_CODE;
}
public String getDEVICE_NAME() {
return DEVICE_NAME;
}
public void setDEVICE_NAME(String DEVICE_NAME) {
this.DEVICE_NAME = DEVICE_NAME;
}
public String getFAULT_TYPE() {
return FAULT_TYPE;
}
public void setFAULT_TYPE(String FAULT_TYPE) {
this.FAULT_TYPE = FAULT_TYPE;
}
public Long getFAULT_COUNT() {
return FAULT_COUNT;
}
public void setFAULT_COUNT(Long FAULT_COUNT) {
this.FAULT_COUNT = FAULT_COUNT;
}
public Long getFAULT_DURATION_MINUTES() {
return FAULT_DURATION_MINUTES;
}
public void setFAULT_DURATION_MINUTES(Long FAULT_DURATION_MINUTES) {
this.FAULT_DURATION_MINUTES = FAULT_DURATION_MINUTES;
}
}

@ -0,0 +1,118 @@
package com.aucma.report.domain;
import com.aucma.common.annotation.Excel;
import com.aucma.common.core.domain.BaseEntity;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* OEE
*
* DMS
*/
public class DeviceOeeReport extends BaseEntity {
private static final long serialVersionUID = 1L;
/** 设备编码 */
@Excel(name = "设备编码")
@JsonProperty("DEVICE_CODE")
private String DEVICE_CODE;
/** 设备名称 */
@Excel(name = "设备名称")
@JsonProperty("DEVICE_NAME")
private String DEVICE_NAME;
/** 可用率 */
@Excel(name = "可用率")
@JsonProperty("AVAILABILITY")
private Double AVAILABILITY;
/** 性能稼动率 */
@Excel(name = "性能稼动率")
@JsonProperty("PERFORMANCE")
private Double PERFORMANCE;
/** 良品率 */
@Excel(name = "良品率")
@JsonProperty("QUALITY")
private Double QUALITY;
/** OEE */
@Excel(name = "OEE")
@JsonProperty("OEE")
private Double OEE;
/** 计划运行时间(分钟) */
@Excel(name = "计划运行时间(分钟)")
@JsonProperty("PLANNED_TIME_MINUTES")
private Long PLANNED_TIME_MINUTES;
/** 停机时间(分钟) */
@Excel(name = "停机时间(分钟)")
@JsonProperty("DOWNTIME_MINUTES")
private Long DOWNTIME_MINUTES;
public String getDEVICE_CODE() {
return DEVICE_CODE;
}
public void setDEVICE_CODE(String DEVICE_CODE) {
this.DEVICE_CODE = DEVICE_CODE;
}
public String getDEVICE_NAME() {
return DEVICE_NAME;
}
public void setDEVICE_NAME(String DEVICE_NAME) {
this.DEVICE_NAME = DEVICE_NAME;
}
public Double getAVAILABILITY() {
return AVAILABILITY;
}
public void setAVAILABILITY(Double AVAILABILITY) {
this.AVAILABILITY = AVAILABILITY;
}
public Double getPERFORMANCE() {
return PERFORMANCE;
}
public void setPERFORMANCE(Double PERFORMANCE) {
this.PERFORMANCE = PERFORMANCE;
}
public Double getQUALITY() {
return QUALITY;
}
public void setQUALITY(Double QUALITY) {
this.QUALITY = QUALITY;
}
public Double getOEE() {
return OEE;
}
public void setOEE(Double OEE) {
this.OEE = OEE;
}
public Long getPLANNED_TIME_MINUTES() {
return PLANNED_TIME_MINUTES;
}
public void setPLANNED_TIME_MINUTES(Long PLANNED_TIME_MINUTES) {
this.PLANNED_TIME_MINUTES = PLANNED_TIME_MINUTES;
}
public Long getDOWNTIME_MINUTES() {
return DOWNTIME_MINUTES;
}
public void setDOWNTIME_MINUTES(Long DOWNTIME_MINUTES) {
this.DOWNTIME_MINUTES = DOWNTIME_MINUTES;
}
}

@ -0,0 +1,79 @@
package com.aucma.report.domain;
import com.aucma.common.annotation.Excel;
import com.aucma.common.core.domain.BaseEntity;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
*
*
* dms_repair_work_order
*/
public class RepairHoursReport extends BaseEntity {
private static final long serialVersionUID = 1L;
/** 执行人ID */
@Excel(name = "执行人ID")
@JsonProperty("EXECUTOR_ID")
private Long EXECUTOR_ID;
/** 执行人姓名 */
@Excel(name = "执行人")
@JsonProperty("EXECUTOR_NAME")
private String EXECUTOR_NAME;
/** 工单数量 */
@Excel(name = "工单数量")
@JsonProperty("ORDER_COUNT")
private Long ORDER_COUNT;
/** 合计工时(小时) */
@Excel(name = "合计工时(h)")
@JsonProperty("TOTAL_HOURS")
private Double TOTAL_HOURS;
/** 平均工时(小时) */
@Excel(name = "平均工时(h)")
@JsonProperty("AVG_HOURS")
private Double AVG_HOURS;
public Long getEXECUTOR_ID() {
return EXECUTOR_ID;
}
public void setEXECUTOR_ID(Long EXECUTOR_ID) {
this.EXECUTOR_ID = EXECUTOR_ID;
}
public String getEXECUTOR_NAME() {
return EXECUTOR_NAME;
}
public void setEXECUTOR_NAME(String EXECUTOR_NAME) {
this.EXECUTOR_NAME = EXECUTOR_NAME;
}
public Long getORDER_COUNT() {
return ORDER_COUNT;
}
public void setORDER_COUNT(Long ORDER_COUNT) {
this.ORDER_COUNT = ORDER_COUNT;
}
public Double getTOTAL_HOURS() {
return TOTAL_HOURS;
}
public void setTOTAL_HOURS(Double TOTAL_HOURS) {
this.TOTAL_HOURS = TOTAL_HOURS;
}
public Double getAVG_HOURS() {
return AVG_HOURS;
}
public void setAVG_HOURS(Double AVG_HOURS) {
this.AVG_HOURS = AVG_HOURS;
}
}

@ -0,0 +1,29 @@
package com.aucma.report.mapper;
import com.aucma.report.domain.DeviceFaultAnalysisReport;
import com.aucma.report.domain.RepairHoursReport;
import com.aucma.report.domain.DeviceOeeReport;
import java.util.List;
import java.util.Map;
/**
* DMS Mapper
*/
public interface DmsReportMapper {
/**
*
*/
List<DeviceFaultAnalysisReport> deviceFaultAnalysisList(Map hashMap);
/**
*
*/
List<RepairHoursReport> repairHoursReportList(Map hashMap);
/**
* OEE OEE Service
*/
List<DeviceOeeReport> deviceOeeReportList(Map hashMap);
}

@ -0,0 +1,29 @@
package com.aucma.report.service;
import com.aucma.report.domain.DeviceFaultAnalysisReport;
import com.aucma.report.domain.RepairHoursReport;
import com.aucma.report.domain.DeviceOeeReport;
import java.util.List;
import java.util.Map;
/**
* DMS Service
*/
public interface IDmsReportService {
/**
*
*/
List<DeviceFaultAnalysisReport> deviceFaultAnalysisList(Map<String, Object> params);
/**
*
*/
List<RepairHoursReport> repairHoursReportList(Map<String, Object> params);
/**
* OEE
*/
List<DeviceOeeReport> deviceOeeReportList(Map<String, Object> params);
}

@ -0,0 +1,125 @@
package com.aucma.report.service.impl;
import com.aucma.report.domain.DeviceFaultAnalysisReport;
import com.aucma.report.domain.DeviceOeeReport;
import com.aucma.report.domain.RepairHoursReport;
import com.aucma.report.mapper.DmsReportMapper;
import com.aucma.report.service.IDmsReportService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Map;
/**
* DMS Service
*/
@Service
public class DmsReportServiceImpl implements IDmsReportService {
@Autowired
private DmsReportMapper dmsReportMapper;
@Override
public List<DeviceFaultAnalysisReport> deviceFaultAnalysisList(Map<String, Object> params) {
return dmsReportMapper.deviceFaultAnalysisList(params);
}
@Override
public List<RepairHoursReport> repairHoursReportList(Map<String, Object> params) {
return dmsReportMapper.repairHoursReportList(params);
}
@Override
public List<DeviceOeeReport> deviceOeeReportList(Map<String, Object> params) {
List<DeviceOeeReport> list = dmsReportMapper.deviceOeeReportList(params);
long plannedMinutes = calculatePlannedMinutes(params);
if (plannedMinutes <= 0) {
// 默认按一天 24 小时计算
plannedMinutes = 24L * 60L;
}
for (DeviceOeeReport item : list) {
if (item == null) {
continue;
}
item.setPLANNED_TIME_MINUTES(plannedMinutes);
Long downtime = item.getDOWNTIME_MINUTES();
if (downtime == null) {
downtime = 0L;
}
double availability;
if (plannedMinutes <= 0) {
availability = 1.0D;
} else {
availability = (double) (plannedMinutes - downtime) / (double) plannedMinutes;
if (availability < 0) {
availability = 0;
}
if (availability > 1) {
availability = 1;
}
}
// 当前版本暂不从生产/质量模块获取性能与良品率,先按 1 处理,可后续扩展
double performance = 1.0D;
double quality = 1.0D;
item.setAVAILABILITY(round(availability, 4));
item.setPERFORMANCE(round(performance, 4));
item.setQUALITY(round(quality, 4));
item.setOEE(round(availability * performance * quality, 4));
}
return list;
}
/**
* beginTime/endTime
*/
private long calculatePlannedMinutes(Map<String, Object> params) {
if (params == null) {
return 0L;
}
Object beginObj = params.get("beginTime");
Object endObj = params.get("endTime");
if (beginObj == null || endObj == null) {
return 0L;
}
String beginStr = String.valueOf(beginObj);
String endStr = String.valueOf(endObj);
if (beginStr.isEmpty() || endStr.isEmpty()) {
return 0L;
}
// 统一补上时间,按自然日计算
if (beginStr.length() == 10) {
beginStr = beginStr + " 00:00:00";
}
if (endStr.length() == 10) {
endStr = endStr + " 23:59:59";
}
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
try {
Date begin = sdf.parse(beginStr);
Date end = sdf.parse(endStr);
if (begin == null || end == null) {
return 0L;
}
long diffMillis = end.getTime() - begin.getTime();
if (diffMillis <= 0) {
return 0L;
}
return diffMillis / (1000L * 60L);
} catch (ParseException e) {
return 0L;
}
}
private double round(double value, int scale) {
if (scale < 0) {
return value;
}
double factor = Math.pow(10, scale);
return Math.round(value * factor) / factor;
}
}

@ -0,0 +1,106 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.aucma.report.mapper.DmsReportMapper">
<!-- 设备故障分析 -->
<select id="deviceFaultAnalysisList"
resultType="com.aucma.report.domain.DeviceFaultAnalysisReport"
parameterType="java.util.HashMap">
SELECT
dl.device_code AS DEVICE_CODE,
dl.device_name AS DEVICE_NAME,
fa.fault_type AS FAULT_TYPE,
COUNT(1) AS FAULT_COUNT,
NVL(SUM(
CASE
WHEN fi.real_begin_time IS NOT NULL AND fi.real_end_time IS NOT NULL
THEN (fi.real_end_time - fi.real_begin_time) * 24 * 60
ELSE 0
END
), 0) AS FAULT_DURATION_MINUTES
FROM dms_bills_fault_instance fi
LEFT JOIN base_deviceledger dl ON fi.device_id = dl.OBJ_ID
LEFT JOIN dms_fault_instance_activity fa ON fi.repair_instance_id = fa.repair_instance_id
AND fa.process_step_order = 1
<where>
fi.is_flag = '1'
<if test="deviceCode != null and deviceCode != ''">
AND dl.device_code = #{deviceCode}
</if>
<if test="faultType != null and faultType != ''">
AND fa.fault_type = #{faultType}
</if>
<if test="beginTime != null and beginTime != ''">
AND fi.apply_time &gt;= TO_DATE(#{beginTime}, 'yyyy-mm-dd')
</if>
<if test="endTime != null and endTime != ''">
AND fi.apply_time &lt;= TO_DATE(#{endTime}, 'yyyy-mm-dd')
</if>
</where>
GROUP BY dl.device_code, dl.device_name, fa.fault_type
ORDER BY FAULT_COUNT DESC
</select>
<!-- 维修工时统计:按执行人汇总 -->
<select id="repairHoursReportList"
resultType="com.aucma.report.domain.RepairHoursReport"
parameterType="java.util.HashMap">
SELECT
rwo.executor_id AS EXECUTOR_ID,
rwo.executor_name AS EXECUTOR_NAME,
COUNT(1) AS ORDER_COUNT,
NVL(SUM(rwo.repair_hours), 0) AS TOTAL_HOURS,
CASE WHEN COUNT(1) = 0 THEN 0
ELSE ROUND(NVL(SUM(rwo.repair_hours), 0) / COUNT(1), 2)
END AS AVG_HOURS
FROM dms_repair_work_order rwo
<where>
rwo.is_flag = '1'
AND rwo.order_status = '3'
<if test="executorId != null">
AND rwo.executor_id = #{executorId}
</if>
<if test="executorName != null and executorName != ''">
AND rwo.executor_name LIKE '%' || #{executorName} || '%'
</if>
<if test="beginTime != null and beginTime != ''">
AND rwo.actual_start_time &gt;= TO_DATE(#{beginTime}, 'yyyy-mm-dd')
</if>
<if test="endTime != null and endTime != ''">
AND rwo.actual_start_time &lt;= TO_DATE(#{endTime}, 'yyyy-mm-dd')
</if>
</where>
GROUP BY rwo.executor_id, rwo.executor_name
ORDER BY TOTAL_HOURS DESC
</select>
<!-- 设备 OEE 分析基础数据:按设备统计停机时长(分钟) -->
<select id="deviceOeeReportList"
resultType="com.aucma.report.domain.DeviceOeeReport"
parameterType="java.util.HashMap">
SELECT
dl.device_code AS DEVICE_CODE,
dl.device_name AS DEVICE_NAME,
NVL(SUM(rwo.shutdown_duration), 0) AS DOWNTIME_MINUTES
FROM dms_repair_work_order rwo
LEFT JOIN base_deviceledger dl ON rwo.device_id = dl.OBJ_ID
<where>
rwo.is_flag = '1'
AND rwo.need_shutdown = '1'
<if test="deviceCode != null and deviceCode != ''">
AND dl.device_code = #{deviceCode}
</if>
<if test="beginTime != null and beginTime != ''">
AND rwo.actual_start_time &gt;= TO_DATE(#{beginTime}, 'yyyy-mm-dd')
</if>
<if test="endTime != null and endTime != ''">
AND rwo.actual_start_time &lt;= TO_DATE(#{endTime}, 'yyyy-mm-dd')
</if>
</where>
GROUP BY dl.device_code, dl.device_name
ORDER BY dl.device_code
</select>
</mapper>
Loading…
Cancel
Save