refactor(qms-pdi): 重构PDI检测项及部件管理相关功能
- 将检测项中的部件描述字段改为部件ID,关联部件表数据 - 新增部件管理功能,支持部件的增删改查及排序 - 在PDI检测项新增接口自动处理部件注册及排序 - 批量删除支持区分部件ID和检测项ID,实现联动删除 - 导入检测项时自动识别导入部件,动态创建部件及排序 - 查询检测项时按部件分组返回,提升数据结构清晰度 - 巡检工单及检测结果展示时通过部件ID获取部件名称显示 - 新增API支持根据部件ID查询检测项及现场记录 - QmsPdiReportVO 和 QmsIqcReportVO新增环比统计字段,完善报表数据 - 修改导出模板及分页查询逻辑,统一部件相关字段处理 - 优化图片ID收集及批量查询,提升接口性能和稳定性
This commit is contained in:
parent
5be1791cb4
commit
6822693429
|
|
@ -110,14 +110,14 @@ public class QmsIssueTicketController extends BaseController {
|
||||||
return ApiResult.success();
|
return ApiResult.success();
|
||||||
}
|
}
|
||||||
|
|
||||||
// /**
|
/**
|
||||||
// * 查询PDI工单详情
|
* 查询PDI工单详情
|
||||||
// * 返回工单基本信息、处理记录及措施列表
|
* 返回工单基本信息、处理记录及措施列表
|
||||||
// */
|
*/
|
||||||
// @GetMapping("detail/pdi-ticket")
|
@GetMapping("detail/pdi-ticket")
|
||||||
// public ApiResult<QmsPdiTicketDetailVO> detailPdiTicket(@NotNull(message = "ID不能为空") Long id) {
|
public ApiResult<QmsPdiTicketDetailVO> detailPdiTicket(@NotNull(message = "ID不能为空") Long id) {
|
||||||
// return ApiResult.success(issueTicketControllerService.getPdiTicketDetail(id));
|
return ApiResult.success(issueTicketControllerService.getPdiTicketDetail(id));
|
||||||
// }
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询本人的PDI工单详情
|
* 查询本人的PDI工单详情
|
||||||
|
|
|
||||||
|
|
@ -47,10 +47,26 @@ public class QmsReportController extends BaseController {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 超时统计数据
|
* IQC超时统计数据
|
||||||
*/
|
*/
|
||||||
@PostMapping("overdue")
|
@PostMapping("overdue/iqc")
|
||||||
public ApiResult<QmsOverdueReportVO> getOverdueReport() {
|
public ApiResult<QmsIqcOverdueReportVO> getIqcOverdueReport() {
|
||||||
return ApiResult.success(reportControllerService.getOverdueReport());
|
return ApiResult.success(reportControllerService.getIqcOverdueReport());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PDI超时统计数据
|
||||||
|
*/
|
||||||
|
@PostMapping("overdue/pdi")
|
||||||
|
public ApiResult<QmsPdiOverdueReportVO> getPdiOverdueReport() {
|
||||||
|
return ApiResult.success(reportControllerService.getPdiOverdueReport());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 工单超时统计数据
|
||||||
|
*/
|
||||||
|
@PostMapping("overdue/ticket")
|
||||||
|
public ApiResult<QmsTicketOverdueReportVO> getTicketOverdueReport() {
|
||||||
|
return ApiResult.success(reportControllerService.getTicketOverdueReport());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -358,6 +358,15 @@ public class QmsPdiStatusItemControllerService {
|
||||||
componentsId = null;
|
componentsId = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 处理检查核实内容:如果以英文字母结束,追加换行符
|
||||||
|
String content = dto.getInspectionContent();
|
||||||
|
if (StrUtil.isNotBlank(content)) {
|
||||||
|
char lastChar = content.charAt(content.length() - 1);
|
||||||
|
if ((lastChar >= 'a' && lastChar <= 'z') || (lastChar >= 'A' && lastChar <= 'Z')) {
|
||||||
|
dto.setInspectionContent(content + "\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 插入检测项
|
// 插入检测项
|
||||||
int itemSort = componentItemCountMap.getOrDefault(componentsId, 0) + 1;
|
int itemSort = componentItemCountMap.getOrDefault(componentsId, 0) + 1;
|
||||||
componentItemCountMap.put(componentsId, itemSort);
|
componentItemCountMap.put(componentsId, itemSort);
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package com.nflg.qms.admin.service;
|
package com.nflg.qms.admin.service;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
import com.nflg.wms.common.pojo.qo.QmsReportQueryQO;
|
import com.nflg.wms.common.pojo.qo.QmsReportQueryQO;
|
||||||
import com.nflg.wms.common.pojo.vo.*;
|
import com.nflg.wms.common.pojo.vo.*;
|
||||||
import com.nflg.wms.repository.entity.*;
|
import com.nflg.wms.repository.entity.*;
|
||||||
|
|
@ -50,6 +51,12 @@ public class QmsReportControllerService {
|
||||||
@Resource
|
@Resource
|
||||||
private IQmsPdiDetectionRulesService pdiDetectionRulesService;
|
private IQmsPdiDetectionRulesService pdiDetectionRulesService;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private IQmsIssueTicketService issueTicketService;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private IQmsIssueTicketProcessService issueTicketProcessService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* IQC报表数据
|
* IQC报表数据
|
||||||
*/
|
*/
|
||||||
|
|
@ -61,13 +68,13 @@ public class QmsReportControllerService {
|
||||||
LocalDateTime currentEnd = request.getEndTime();
|
LocalDateTime currentEnd = request.getEndTime();
|
||||||
boolean hasTimeRange = currentStart != null || currentEnd != null;
|
boolean hasTimeRange = currentStart != null || currentEnd != null;
|
||||||
|
|
||||||
// 如果都没传,查询全部
|
// 如果都没传,使用合理的时间范围(最近1年)
|
||||||
if (!hasTimeRange) {
|
if (!hasTimeRange) {
|
||||||
currentStart = LocalDateTime.MIN;
|
currentStart = LocalDateTime.now().minusYears(1);
|
||||||
currentEnd = LocalDateTime.MAX;
|
currentEnd = LocalDateTime.now();
|
||||||
} else {
|
} else {
|
||||||
if (currentStart == null) currentStart = LocalDateTime.MIN;
|
if (currentStart == null) currentStart = LocalDateTime.now().minusYears(1);
|
||||||
if (currentEnd == null) currentEnd = LocalDateTime.MAX;
|
if (currentEnd == null) currentEnd = LocalDateTime.now();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 计算上周期
|
// 计算上周期
|
||||||
|
|
@ -460,13 +467,13 @@ public class QmsReportControllerService {
|
||||||
LocalDateTime currentEnd = request.getEndTime();
|
LocalDateTime currentEnd = request.getEndTime();
|
||||||
boolean hasTimeRange = currentStart != null || currentEnd != null;
|
boolean hasTimeRange = currentStart != null || currentEnd != null;
|
||||||
|
|
||||||
// 如果都没传,查询全部
|
// 如果都没传,使用合理的时间范围(最近1年)
|
||||||
if (!hasTimeRange) {
|
if (!hasTimeRange) {
|
||||||
currentStart = LocalDateTime.MIN;
|
currentStart = LocalDateTime.now().minusYears(1);
|
||||||
currentEnd = LocalDateTime.MAX;
|
currentEnd = LocalDateTime.now();
|
||||||
} else {
|
} else {
|
||||||
if (currentStart == null) currentStart = LocalDateTime.MIN;
|
if (currentStart == null) currentStart = LocalDateTime.now().minusYears(1);
|
||||||
if (currentEnd == null) currentEnd = LocalDateTime.MAX;
|
if (currentEnd == null) currentEnd = LocalDateTime.now();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 计算上周期
|
// 计算上周期
|
||||||
|
|
@ -606,7 +613,7 @@ public class QmsReportControllerService {
|
||||||
Map<String, Object> result = new HashMap<>();
|
Map<String, Object> result = new HashMap<>();
|
||||||
|
|
||||||
// 当前周期平均时间
|
// 当前周期平均时间
|
||||||
String currentAvgTime = "0H0Min0S";
|
String currentAvgTime = "0天0小时";
|
||||||
long currentAvgSeconds = 0;
|
long currentAvgSeconds = 0;
|
||||||
|
|
||||||
List<QmsPdiTaskRecord> currentCompletedTasks = pdiTaskRecordService.lambdaQuery()
|
List<QmsPdiTaskRecord> currentCompletedTasks = pdiTaskRecordService.lambdaQuery()
|
||||||
|
|
@ -628,7 +635,7 @@ public class QmsReportControllerService {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 上周期平均时间
|
// 上周期平均时间
|
||||||
String previousAvgTime = "0H0Min0S";
|
String previousAvgTime = "0天0小时";
|
||||||
long previousAvgSeconds = 0;
|
long previousAvgSeconds = 0;
|
||||||
|
|
||||||
if (hasTimeRange) {
|
if (hasTimeRange) {
|
||||||
|
|
@ -812,28 +819,613 @@ public class QmsReportControllerService {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 格式化时长:秒 -> xxHxxMinxxS
|
* 格式化时长:秒 -> xx天xx小时
|
||||||
*/
|
*/
|
||||||
private String formatDurationPDI(long seconds) {
|
private String formatDurationPDI(long seconds) {
|
||||||
long hours = seconds / 3600;
|
long days = seconds / 86400;
|
||||||
long minutes = (seconds % 3600) / 60;
|
double hours = (seconds % 86400) / 3600.0;
|
||||||
long secs = seconds % 60;
|
return days + "天" + String.format("%.2f", hours) + "小时";
|
||||||
return hours + "H" + minutes + "Min" + secs + "S";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 工单报表数据
|
* 工单报表数据
|
||||||
*/
|
*/
|
||||||
public QmsTicketReportVO getTicketReport(QmsReportQueryQO request) {
|
public QmsTicketReportVO getTicketReport(QmsReportQueryQO request) {
|
||||||
// TODO: 实现逻辑
|
QmsTicketReportVO vo = new QmsTicketReportVO();
|
||||||
return new QmsTicketReportVO();
|
|
||||||
|
// 计算时间范围
|
||||||
|
LocalDateTime start = request.getStartTime() != null ? request.getStartTime() : LocalDateTime.now().minusYears(1);
|
||||||
|
LocalDateTime end = request.getEndTime() != null ? request.getEndTime() : LocalDateTime.now();
|
||||||
|
|
||||||
|
// 1. 查询周期内所有工单
|
||||||
|
List<QmsIssueTicket> allTickets = issueTicketService.lambdaQuery()
|
||||||
|
.ge(QmsIssueTicket::getCreateTime, start)
|
||||||
|
.le(QmsIssueTicket::getCreateTime, end)
|
||||||
|
.list();
|
||||||
|
|
||||||
|
int total = allTickets.size();
|
||||||
|
vo.setTotalTickets(total);
|
||||||
|
|
||||||
|
if (total == 0) {
|
||||||
|
// 无数据时返回空结构
|
||||||
|
vo.setCompletedCount(0);
|
||||||
|
vo.setUnfinishedCount(0);
|
||||||
|
vo.setPendingTransferCount(0);
|
||||||
|
vo.setProcessingCount(0);
|
||||||
|
vo.setCompletionRate(0.0);
|
||||||
|
vo.setAvgProcessingTime("0天0小时");
|
||||||
|
vo.setStatusDistributions(buildEmptyStatusDistributions());
|
||||||
|
vo.setCompletionStatusList(Collections.emptyList());
|
||||||
|
vo.setIssueTypeCounts(Collections.emptyList());
|
||||||
|
vo.setTop10Users(Collections.emptyList());
|
||||||
|
vo.setBottom10Users(Collections.emptyList());
|
||||||
|
vo.setOverallAvgCompletionRate(0.0);
|
||||||
|
return vo;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 各状态统计
|
||||||
|
int completedCount = (int) allTickets.stream().filter(t -> t.getStatus() != null && t.getStatus() == 2).count();
|
||||||
|
int pendingTransferCount = (int) allTickets.stream().filter(t -> t.getStatus() != null && t.getStatus() == 0).count();
|
||||||
|
int processingCount = (int) allTickets.stream().filter(t -> t.getStatus() != null && t.getStatus() == 1).count();
|
||||||
|
int unfinishedCount = total - completedCount;
|
||||||
|
|
||||||
|
vo.setCompletedCount(completedCount);
|
||||||
|
vo.setUnfinishedCount(unfinishedCount);
|
||||||
|
vo.setPendingTransferCount(pendingTransferCount);
|
||||||
|
vo.setProcessingCount(processingCount);
|
||||||
|
|
||||||
|
// 3. 总体完成率
|
||||||
|
vo.setCompletionRate(round2(completedCount * 100.0 / total));
|
||||||
|
|
||||||
|
// 4. 平均处理时长(已完成工单的 completeTime - createTime)
|
||||||
|
vo.setAvgProcessingTime(calculateTicketAvgProcessingTime(allTickets));
|
||||||
|
|
||||||
|
// 5. 三种状态占比
|
||||||
|
vo.setStatusDistributions(buildStatusDistributions(total, pendingTransferCount, processingCount, completedCount));
|
||||||
|
|
||||||
|
// 6. 完成状态分布(已完成工单中各审批结果占比)
|
||||||
|
vo.setCompletionStatusList(buildCompletionStatusList(allTickets, completedCount));
|
||||||
|
|
||||||
|
// 7. 问题类别占比(暂空置)
|
||||||
|
vo.setIssueTypeCounts(Collections.emptyList());
|
||||||
|
|
||||||
|
// 8. 用户完成率(按处理人)
|
||||||
|
buildUserCompletionRates(vo, allTickets);
|
||||||
|
|
||||||
|
return vo;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ======================== 工单报表辅助方法 ========================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算平均处理时长
|
||||||
|
*/
|
||||||
|
private String calculateTicketAvgProcessingTime(List<QmsIssueTicket> tickets) {
|
||||||
|
List<QmsIssueTicket> completedTickets = tickets.stream()
|
||||||
|
.filter(t -> t.getStatus() != null && t.getStatus() == 2)
|
||||||
|
.filter(t -> t.getCompleteTime() != null && t.getCreateTime() != null)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
if (completedTickets.isEmpty()) {
|
||||||
|
return "0天0.00小时";
|
||||||
|
}
|
||||||
|
|
||||||
|
long totalSeconds = completedTickets.stream()
|
||||||
|
.mapToLong(t -> Duration.between(t.getCreateTime(), t.getCompleteTime()).getSeconds())
|
||||||
|
.sum();
|
||||||
|
long avgSeconds = totalSeconds / completedTickets.size();
|
||||||
|
|
||||||
|
long days = avgSeconds / 86400;
|
||||||
|
double hours = (avgSeconds % 86400) / 3600.0;
|
||||||
|
|
||||||
|
return days + "天" + String.format("%.2f", hours) + "小时";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 超时统计数据
|
* 构建三种状态占比
|
||||||
*/
|
*/
|
||||||
public QmsOverdueReportVO getOverdueReport() {
|
private List<QmsTicketReportVO.StatusDistribution> buildStatusDistributions(
|
||||||
// TODO: 实现逻辑
|
int total, int pendingTransfer, int processing, int completed) {
|
||||||
return new QmsOverdueReportVO();
|
List<QmsTicketReportVO.StatusDistribution> list = new ArrayList<>();
|
||||||
|
|
||||||
|
QmsTicketReportVO.StatusDistribution d0 = new QmsTicketReportVO.StatusDistribution();
|
||||||
|
d0.setStatus((short) 0);
|
||||||
|
d0.setStatusName("待流转");
|
||||||
|
d0.setPercentage(round2(pendingTransfer * 100.0 / total));
|
||||||
|
list.add(d0);
|
||||||
|
|
||||||
|
QmsTicketReportVO.StatusDistribution d1 = new QmsTicketReportVO.StatusDistribution();
|
||||||
|
d1.setStatus((short) 1);
|
||||||
|
d1.setStatusName("处理中");
|
||||||
|
d1.setPercentage(round2(processing * 100.0 / total));
|
||||||
|
list.add(d1);
|
||||||
|
|
||||||
|
QmsTicketReportVO.StatusDistribution d2 = new QmsTicketReportVO.StatusDistribution();
|
||||||
|
d2.setStatus((short) 2);
|
||||||
|
d2.setStatusName("已完成");
|
||||||
|
d2.setPercentage(round2(completed * 100.0 / total));
|
||||||
|
list.add(d2);
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建空状态分布
|
||||||
|
*/
|
||||||
|
private List<QmsTicketReportVO.StatusDistribution> buildEmptyStatusDistributions() {
|
||||||
|
return buildStatusDistributions(1, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建完成状态分布
|
||||||
|
*/
|
||||||
|
private List<QmsTicketReportVO.CompletionStatus> buildCompletionStatusList(
|
||||||
|
List<QmsIssueTicket> allTickets, int completedCount) {
|
||||||
|
if (completedCount == 0) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 已完成工单的审批结果统计
|
||||||
|
Map<Short, Long> approvalCountMap = allTickets.stream()
|
||||||
|
.filter(t -> t.getStatus() != null && t.getStatus() == 2)
|
||||||
|
.filter(t -> t.getApprovalStatus() != null)
|
||||||
|
.collect(Collectors.groupingBy(QmsIssueTicket::getApprovalStatus, Collectors.counting()));
|
||||||
|
|
||||||
|
// 审批结果名称映射
|
||||||
|
Map<Short, String> approvalNames = new LinkedHashMap<>();
|
||||||
|
approvalNames.put((short) 0, "通过");
|
||||||
|
approvalNames.put((short) 1, "驳回");
|
||||||
|
approvalNames.put((short) 2, "退货");
|
||||||
|
approvalNames.put((short) 3, "报废");
|
||||||
|
approvalNames.put((short) 4, "维修");
|
||||||
|
approvalNames.put((short) 5, "挑选使用");
|
||||||
|
approvalNames.put((short) 6, "让渡使用");
|
||||||
|
|
||||||
|
List<QmsTicketReportVO.CompletionStatus> result = new ArrayList<>();
|
||||||
|
for (Map.Entry<Short, String> entry : approvalNames.entrySet()) {
|
||||||
|
Long count = approvalCountMap.getOrDefault(entry.getKey(), 0L);
|
||||||
|
if (count > 0) {
|
||||||
|
QmsTicketReportVO.CompletionStatus cs = new QmsTicketReportVO.CompletionStatus();
|
||||||
|
cs.setApprovalStatus(entry.getKey());
|
||||||
|
cs.setStatusName(entry.getValue());
|
||||||
|
cs.setCount(count.intValue());
|
||||||
|
cs.setPercentage(round2(count * 100.0 / completedCount));
|
||||||
|
result.add(cs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 按处理人统计用户完成率
|
||||||
|
*/
|
||||||
|
private void buildUserCompletionRates(QmsTicketReportVO vo, List<QmsIssueTicket> allTickets) {
|
||||||
|
// 收集所有工单ID
|
||||||
|
Set<Long> ticketIds = allTickets.stream()
|
||||||
|
.map(QmsIssueTicket::getId)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
|
||||||
|
if (ticketIds.isEmpty()) {
|
||||||
|
vo.setTop10Users(Collections.emptyList());
|
||||||
|
vo.setBottom10Users(Collections.emptyList());
|
||||||
|
vo.setOverallAvgCompletionRate(0.0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询所有处理记录
|
||||||
|
List<QmsIssueTicketProcess> processes = issueTicketProcessService.lambdaQuery()
|
||||||
|
.in(QmsIssueTicketProcess::getIssueTicketId, ticketIds)
|
||||||
|
.list();
|
||||||
|
|
||||||
|
// 工单状态映射(ticketId → status)
|
||||||
|
Map<Long, Short> ticketStatusMap = allTickets.stream()
|
||||||
|
.collect(Collectors.toMap(QmsIssueTicket::getId, t -> t.getStatus() != null ? t.getStatus() : 0));
|
||||||
|
|
||||||
|
// 按处理人分组统计
|
||||||
|
Map<Long, List<QmsIssueTicketProcess>> handlerGroup = processes.stream()
|
||||||
|
.filter(p -> p.getHandlerUserId() != null)
|
||||||
|
.collect(Collectors.groupingBy(QmsIssueTicketProcess::getHandlerUserId));
|
||||||
|
|
||||||
|
List<QmsTicketReportVO.UserCompletionRate> userRates = new ArrayList<>();
|
||||||
|
for (Map.Entry<Long, List<QmsIssueTicketProcess>> entry : handlerGroup.entrySet()) {
|
||||||
|
Long userId = entry.getKey();
|
||||||
|
List<QmsIssueTicketProcess> userProcesses = entry.getValue();
|
||||||
|
int totalAssigned = userProcesses.size();
|
||||||
|
int totalCompleted = (int) userProcesses.stream()
|
||||||
|
.filter(p -> {
|
||||||
|
Short status = ticketStatusMap.get(p.getIssueTicketId());
|
||||||
|
return status != null && status == 2;
|
||||||
|
})
|
||||||
|
.count();
|
||||||
|
|
||||||
|
String userName = userProcesses.get(0).getHandlerUserName();
|
||||||
|
|
||||||
|
QmsTicketReportVO.UserCompletionRate rate = new QmsTicketReportVO.UserCompletionRate();
|
||||||
|
rate.setUserId(userId);
|
||||||
|
rate.setUserName(userName);
|
||||||
|
rate.setTotalAssigned(totalAssigned);
|
||||||
|
rate.setTotalCompleted(totalCompleted);
|
||||||
|
rate.setCompletionRate(round2(totalCompleted * 100.0 / totalAssigned));
|
||||||
|
userRates.add(rate);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 按完成率降序排列
|
||||||
|
userRates.sort((a, b) -> Double.compare(b.getCompletionRate(), a.getCompletionRate()));
|
||||||
|
|
||||||
|
// 整体平均完成率
|
||||||
|
if (!userRates.isEmpty()) {
|
||||||
|
double avgRate = userRates.stream()
|
||||||
|
.mapToDouble(QmsTicketReportVO.UserCompletionRate::getCompletionRate)
|
||||||
|
.average()
|
||||||
|
.orElse(0.0);
|
||||||
|
vo.setOverallAvgCompletionRate(round2(avgRate));
|
||||||
|
} else {
|
||||||
|
vo.setOverallAvgCompletionRate(0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 用户总数 < 20 → 全部返回
|
||||||
|
if (userRates.size() < 20) {
|
||||||
|
vo.setTop10Users(new ArrayList<>(userRates));
|
||||||
|
List<QmsTicketReportVO.UserCompletionRate> reversed = new ArrayList<>(userRates);
|
||||||
|
reversed.sort((a, b) -> Double.compare(a.getCompletionRate(), b.getCompletionRate()));
|
||||||
|
vo.setBottom10Users(reversed);
|
||||||
|
} else {
|
||||||
|
vo.setTop10Users(new ArrayList<>(userRates.subList(0, 10)));
|
||||||
|
List<QmsTicketReportVO.UserCompletionRate> reversed = new ArrayList<>(userRates);
|
||||||
|
reversed.sort((a, b) -> Double.compare(a.getCompletionRate(), b.getCompletionRate()));
|
||||||
|
vo.setBottom10Users(new ArrayList<>(reversed.subList(0, 10)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保留两位小数
|
||||||
|
*/
|
||||||
|
private double round2(double value) {
|
||||||
|
return Math.round(value * 100.0) / 100.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IQC超时统计数据
|
||||||
|
*/
|
||||||
|
public QmsIqcOverdueReportVO getIqcOverdueReport() {
|
||||||
|
QmsIqcOverdueReportVO vo = new QmsIqcOverdueReportVO();
|
||||||
|
LocalDateTime now = LocalDateTime.now();
|
||||||
|
|
||||||
|
List<QmsOverdueReportVO.OverdueTaskItem> iqcOverdueTasks = buildIqcOverdueTasks(now);
|
||||||
|
vo.setIqcOverdueTasks(iqcOverdueTasks);
|
||||||
|
vo.setTotalOverdueCount(iqcOverdueTasks.size());
|
||||||
|
vo.setAvgOverdueTime(calculateAvgOverdueTimeFromTasks(iqcOverdueTasks));
|
||||||
|
vo.setSevereOverdueCount(countSevereOverdue(iqcOverdueTasks));
|
||||||
|
|
||||||
|
return vo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PDI超时统计数据
|
||||||
|
*/
|
||||||
|
public QmsPdiOverdueReportVO getPdiOverdueReport() {
|
||||||
|
QmsPdiOverdueReportVO vo = new QmsPdiOverdueReportVO();
|
||||||
|
LocalDateTime now = LocalDateTime.now();
|
||||||
|
|
||||||
|
List<QmsOverdueReportVO.OverdueTaskItem> pdiOverdueTasks = buildPdiOverdueTasks(now);
|
||||||
|
vo.setPdiOverdueTasks(pdiOverdueTasks);
|
||||||
|
vo.setTotalOverdueCount(pdiOverdueTasks.size());
|
||||||
|
vo.setAvgOverdueTime(calculateAvgOverdueTimeFromTasks(pdiOverdueTasks));
|
||||||
|
vo.setSevereOverdueCount(countSevereOverdue(pdiOverdueTasks));
|
||||||
|
|
||||||
|
return vo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 工单超时统计数据
|
||||||
|
*/
|
||||||
|
public QmsTicketOverdueReportVO getTicketOverdueReport() {
|
||||||
|
QmsTicketOverdueReportVO vo = new QmsTicketOverdueReportVO();
|
||||||
|
LocalDateTime now = LocalDateTime.now();
|
||||||
|
|
||||||
|
List<QmsOverdueReportVO.OverdueTaskItem> ticketOverdueTasks = buildTicketOverdueTasks(now);
|
||||||
|
vo.setTicketOverdueTasks(ticketOverdueTasks);
|
||||||
|
vo.setTotalOverdueCount(ticketOverdueTasks.size());
|
||||||
|
vo.setAvgOverdueTime(calculateAvgOverdueTimeFromTasks(ticketOverdueTasks));
|
||||||
|
vo.setSevereOverdueCount(countTicketSevereOverdue(ticketOverdueTasks));
|
||||||
|
|
||||||
|
return vo;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ======================== 超时统计辅助方法 ========================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建工单超时任务列表
|
||||||
|
*/
|
||||||
|
private List<QmsOverdueReportVO.OverdueTaskItem> buildTicketOverdueTasks(LocalDateTime now) {
|
||||||
|
// 查询未完成且超时的工单
|
||||||
|
List<QmsIssueTicket> overdueTickets = issueTicketService.lambdaQuery()
|
||||||
|
.ne(QmsIssueTicket::getStatus, 2)
|
||||||
|
.in(QmsIssueTicket::getOverdue, 1, 2)
|
||||||
|
.list();
|
||||||
|
|
||||||
|
if (overdueTickets.isEmpty()) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 批量查询处理记录(用于PDI和巡检工单)
|
||||||
|
Set<Long> ticketIds = overdueTickets.stream()
|
||||||
|
.map(QmsIssueTicket::getId)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
|
||||||
|
List<QmsIssueTicketProcess> processes = issueTicketProcessService.lambdaQuery()
|
||||||
|
.in(QmsIssueTicketProcess::getIssueTicketId, ticketIds)
|
||||||
|
.list();
|
||||||
|
|
||||||
|
Map<Long, List<QmsIssueTicketProcess>> processMap = processes.stream()
|
||||||
|
.collect(Collectors.groupingBy(QmsIssueTicketProcess::getIssueTicketId));
|
||||||
|
|
||||||
|
// 组装VO
|
||||||
|
List<QmsOverdueReportVO.OverdueTaskItem> result = new ArrayList<>();
|
||||||
|
for (QmsIssueTicket ticket : overdueTickets) {
|
||||||
|
QmsOverdueReportVO.OverdueTaskItem item = new QmsOverdueReportVO.OverdueTaskItem();
|
||||||
|
item.setTaskId(ticket.getId());
|
||||||
|
item.setTaskNo(ticket.getTicketNo());
|
||||||
|
item.setCreateDate(ticket.getCreateTime() != null ? ticket.getCreateTime().format(java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")) : "");
|
||||||
|
|
||||||
|
// 根据事故类型计算要求完成时间:严重(2)=3天,较严重(1)=7天,一般(0)=14天
|
||||||
|
LocalDateTime requiredFinishTime = null;
|
||||||
|
if (ticket.getCreateTime() != null) {
|
||||||
|
int days = 14; // 默认一般
|
||||||
|
if (ticket.getIncidentType() != null) {
|
||||||
|
if (ticket.getIncidentType() == 2) days = 3;
|
||||||
|
else if (ticket.getIncidentType() == 1) days = 7;
|
||||||
|
}
|
||||||
|
requiredFinishTime = ticket.getCreateTime().plusDays(days);
|
||||||
|
}
|
||||||
|
item.setRequiredCompletionTime(requiredFinishTime != null ? requiredFinishTime.format(java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")) : "");
|
||||||
|
item.setOverdueDuration(calculateOverdueDuration(requiredFinishTime, now));
|
||||||
|
|
||||||
|
// 根据sourceType确定类型和处理人
|
||||||
|
if (ticket.getSourceType() != null && ticket.getSourceType() == 0) {
|
||||||
|
// IQC工单:处理人 = approval_user
|
||||||
|
item.setType("IQC工单");
|
||||||
|
if (ticket.getApprovalUserId() != null) {
|
||||||
|
item.setHandlerNames(Collections.singletonList(ticket.getApprovalUserName()));
|
||||||
|
item.setHandlerIds(Collections.singletonList(ticket.getApprovalUserId()));
|
||||||
|
} else {
|
||||||
|
item.setHandlerNames(Collections.emptyList());
|
||||||
|
item.setHandlerIds(Collections.emptyList());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// PDI工单(1) 或 巡检工单(2):处理人 = process.handler
|
||||||
|
item.setType(ticket.getSourceType() != null && ticket.getSourceType() == 1 ? "PDI工单" : "巡检工单");
|
||||||
|
List<QmsIssueTicketProcess> ticketProcesses = processMap.getOrDefault(ticket.getId(), Collections.emptyList());
|
||||||
|
List<String> handlerNames = ticketProcesses.stream()
|
||||||
|
.map(QmsIssueTicketProcess::getHandlerUserName)
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.distinct()
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
List<Long> handlerIds = ticketProcesses.stream()
|
||||||
|
.map(QmsIssueTicketProcess::getHandlerUserId)
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.distinct()
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
item.setHandlerNames(handlerNames);
|
||||||
|
item.setHandlerIds(handlerIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
result.add(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建IQC任务超时列表
|
||||||
|
*/
|
||||||
|
private List<QmsOverdueReportVO.OverdueTaskItem> buildIqcOverdueTasks(LocalDateTime now) {
|
||||||
|
List<QmsIncomingInspectionTask> overdueTasks = incomingInspectionTaskService.lambdaQuery()
|
||||||
|
.eq(QmsIncomingInspectionTask::getIsOverdue, true)
|
||||||
|
.ne(QmsIncomingInspectionTask::getInspectionStatus, 2)
|
||||||
|
.list();
|
||||||
|
|
||||||
|
if (overdueTasks.isEmpty()) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
List<QmsOverdueReportVO.OverdueTaskItem> result = new ArrayList<>();
|
||||||
|
for (QmsIncomingInspectionTask task : overdueTasks) {
|
||||||
|
QmsOverdueReportVO.OverdueTaskItem item = new QmsOverdueReportVO.OverdueTaskItem();
|
||||||
|
item.setTaskId(task.getId());
|
||||||
|
item.setTaskNo(task.getTaskNo());
|
||||||
|
item.setType("IQC");
|
||||||
|
item.setCreateDate(task.getSubmitTime() != null ? task.getSubmitTime().format(java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")) : "");
|
||||||
|
item.setRequiredCompletionTime(task.getRequiredFinishTime() != null ? task.getRequiredFinishTime().format(java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")) : "");
|
||||||
|
item.setOverdueDuration(calculateOverdueDuration(task.getRequiredFinishTime(), now));
|
||||||
|
|
||||||
|
// 处理人:inspector + agent
|
||||||
|
List<String> handlerNames = new ArrayList<>();
|
||||||
|
List<Long> handlerIds = new ArrayList<>();
|
||||||
|
if (StrUtil.isNotBlank(task.getInspectorName())) {
|
||||||
|
handlerNames.add(task.getInspectorName());
|
||||||
|
handlerIds.add(task.getInspectorId());
|
||||||
|
}
|
||||||
|
if (StrUtil.isNotBlank(task.getAgentName())) {
|
||||||
|
handlerNames.add(task.getAgentName());
|
||||||
|
handlerIds.add(task.getAgentId());
|
||||||
|
}
|
||||||
|
item.setHandlerNames(handlerNames);
|
||||||
|
item.setHandlerIds(handlerIds);
|
||||||
|
|
||||||
|
result.add(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建PDI任务超时列表
|
||||||
|
*/
|
||||||
|
private List<QmsOverdueReportVO.OverdueTaskItem> buildPdiOverdueTasks(LocalDateTime now) {
|
||||||
|
List<QmsPdiTaskRecord> overdueTasks = pdiTaskRecordService.lambdaQuery()
|
||||||
|
.eq(QmsPdiTaskRecord::getOverdue, true)
|
||||||
|
.ne(QmsPdiTaskRecord::getInspectionEnable, 2)
|
||||||
|
.list();
|
||||||
|
|
||||||
|
if (overdueTasks.isEmpty()) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 批量查询检测规则获取inspector
|
||||||
|
Set<Long> ruleIds = overdueTasks.stream()
|
||||||
|
.map(QmsPdiTaskRecord::getDetectionRulesId)
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
|
||||||
|
Map<Long, QmsPdiDetectionRules> rulesMap = pdiDetectionRulesService.listByIds(ruleIds).stream()
|
||||||
|
.collect(Collectors.toMap(QmsPdiDetectionRules::getId, r -> r, (a, b) -> a));
|
||||||
|
|
||||||
|
// 收集所有需要查询的用户ID
|
||||||
|
Set<Long> userIds = new HashSet<>();
|
||||||
|
rulesMap.values().forEach(rule -> {
|
||||||
|
if (rule.getInspectorId() != null) {
|
||||||
|
userIds.add(rule.getInspectorId());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
overdueTasks.forEach(task -> {
|
||||||
|
if (task.getAssistantId() != null) {
|
||||||
|
userIds.add(task.getAssistantId());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 批量查询用户信息
|
||||||
|
Map<Long, User> userMap = new HashMap<>();
|
||||||
|
if (!userIds.isEmpty()) {
|
||||||
|
userMap = userService.listByIds(userIds).stream()
|
||||||
|
.collect(Collectors.toMap(User::getId, u -> u, (a, b) -> a));
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<Long, User> finalUserMap = userMap;
|
||||||
|
List<QmsOverdueReportVO.OverdueTaskItem> result = new ArrayList<>();
|
||||||
|
for (QmsPdiTaskRecord task : overdueTasks) {
|
||||||
|
QmsOverdueReportVO.OverdueTaskItem item = new QmsOverdueReportVO.OverdueTaskItem();
|
||||||
|
item.setTaskId(task.getId());
|
||||||
|
item.setTaskNo(task.getTaskNo());
|
||||||
|
item.setType("PDI");
|
||||||
|
item.setCreateDate(task.getSubmissionTime() != null ? task.getSubmissionTime().format(java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")) : "");
|
||||||
|
item.setRequiredCompletionTime(task.getRequiredCompletionTime() != null ? task.getRequiredCompletionTime().format(java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")) : "");
|
||||||
|
item.setOverdueDuration(calculateOverdueDuration(task.getRequiredCompletionTime(), now));
|
||||||
|
|
||||||
|
// 处理人:rules.inspector + task.assistant
|
||||||
|
List<String> handlerNames = new ArrayList<>();
|
||||||
|
List<Long> handlerIds = new ArrayList<>();
|
||||||
|
|
||||||
|
QmsPdiDetectionRules rule = rulesMap.get(task.getDetectionRulesId());
|
||||||
|
if (rule != null && rule.getInspectorId() != null) {
|
||||||
|
User inspector = finalUserMap.get(rule.getInspectorId());
|
||||||
|
if (inspector != null) {
|
||||||
|
handlerNames.add(inspector.getUserName());
|
||||||
|
}
|
||||||
|
handlerIds.add(rule.getInspectorId());
|
||||||
|
}
|
||||||
|
if (task.getAssistantId() != null) {
|
||||||
|
User assistant = finalUserMap.get(task.getAssistantId());
|
||||||
|
if (assistant != null) {
|
||||||
|
handlerNames.add(assistant.getUserName());
|
||||||
|
}
|
||||||
|
handlerIds.add(task.getAssistantId());
|
||||||
|
}
|
||||||
|
|
||||||
|
item.setHandlerNames(handlerNames);
|
||||||
|
item.setHandlerIds(handlerIds);
|
||||||
|
|
||||||
|
result.add(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算超时时长
|
||||||
|
*/
|
||||||
|
private String calculateOverdueDuration(LocalDateTime startTime, LocalDateTime endTime) {
|
||||||
|
if (startTime == null) {
|
||||||
|
return "0.00小时";
|
||||||
|
}
|
||||||
|
long totalMinutes = Duration.between(startTime, endTime).toMinutes();
|
||||||
|
if (totalMinutes < 0) totalMinutes = 0;
|
||||||
|
double hours = totalMinutes / 60.0;
|
||||||
|
return String.format("%.2f小时", hours);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是否严重超时(>=24小时)
|
||||||
|
*/
|
||||||
|
private boolean isSevereOverdue(String overdueDuration) {
|
||||||
|
try {
|
||||||
|
double hours = Double.parseDouble(overdueDuration.replace("小时", ""));
|
||||||
|
return hours >= 24;
|
||||||
|
} catch (Exception e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 统计严重超时数量(>=24小时)
|
||||||
|
*/
|
||||||
|
private int countSevereOverdue(List<QmsOverdueReportVO.OverdueTaskItem> tasks) {
|
||||||
|
if (tasks.isEmpty()) return 0;
|
||||||
|
|
||||||
|
return (int) tasks.stream()
|
||||||
|
.filter(t -> {
|
||||||
|
String duration = t.getOverdueDuration();
|
||||||
|
return duration != null && isSevereOverdue(duration);
|
||||||
|
})
|
||||||
|
.count();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 统计工单严重超时数量(overdue=2)
|
||||||
|
*/
|
||||||
|
private int countTicketSevereOverdue(List<QmsOverdueReportVO.OverdueTaskItem> tasks) {
|
||||||
|
if (tasks.isEmpty()) return 0;
|
||||||
|
|
||||||
|
// 需要从原始工单数据中筛选overdue=2的
|
||||||
|
// 这里简化处理:通过taskNo重新查询
|
||||||
|
Set<String> taskNos = tasks.stream()
|
||||||
|
.map(QmsOverdueReportVO.OverdueTaskItem::getTaskNo)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
|
||||||
|
return (int) issueTicketService.lambdaQuery()
|
||||||
|
.in(QmsIssueTicket::getTicketNo, taskNos)
|
||||||
|
.eq(QmsIssueTicket::getOverdue, 2)
|
||||||
|
.count()
|
||||||
|
.intValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算平均超时时长(单个类别)
|
||||||
|
*/
|
||||||
|
private String calculateAvgOverdueTimeFromTasks(List<QmsOverdueReportVO.OverdueTaskItem> tasks) {
|
||||||
|
if (tasks.isEmpty()) {
|
||||||
|
return "0.00小时";
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> durations = tasks.stream()
|
||||||
|
.map(QmsOverdueReportVO.OverdueTaskItem::getOverdueDuration)
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
if (durations.isEmpty()) {
|
||||||
|
return "0.00小时";
|
||||||
|
}
|
||||||
|
|
||||||
|
double totalHours = 0;
|
||||||
|
for (String duration : durations) {
|
||||||
|
try {
|
||||||
|
totalHours += Double.parseDouble(duration.replace("小时", ""));
|
||||||
|
} catch (Exception e) {
|
||||||
|
// 忽略解析错误
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
double avgHours = totalHours / durations.size();
|
||||||
|
return String.format("%.2f小时", avgHours);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,9 @@ package com.nflg.wms.common.pojo.qo;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.OffsetDateTime;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 报表查询请求QO
|
* 报表查询请求QO
|
||||||
|
|
@ -11,12 +14,53 @@ import java.time.LocalDateTime;
|
||||||
public class QmsReportQueryQO {
|
public class QmsReportQueryQO {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 开始时间(可选)
|
* 时间范围(前端传入,支持多种格式)
|
||||||
*/
|
*/
|
||||||
private LocalDateTime startTime;
|
private List<String> dateRange;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 结束时间(可选)
|
* 获取开始时间
|
||||||
*/
|
*/
|
||||||
private LocalDateTime endTime;
|
public LocalDateTime getStartTime() {
|
||||||
|
if (dateRange != null && dateRange.size() >= 1) {
|
||||||
|
return parseDateTime(dateRange.get(0));
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取结束时间
|
||||||
|
*/
|
||||||
|
public LocalDateTime getEndTime() {
|
||||||
|
if (dateRange != null && dateRange.size() >= 2) {
|
||||||
|
return parseDateTime(dateRange.get(1));
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析时间字符串,兼容多种格式
|
||||||
|
*/
|
||||||
|
private LocalDateTime parseDateTime(String text) {
|
||||||
|
if (text == null || text.isBlank()) return null;
|
||||||
|
try {
|
||||||
|
// ISO格式带时区:2026-05-02T16:00:00.000Z
|
||||||
|
if (text.endsWith("Z") || text.contains("+")) {
|
||||||
|
return OffsetDateTime.parse(text).toLocalDateTime();
|
||||||
|
}
|
||||||
|
// yyyy/MM/dd
|
||||||
|
if (text.contains("/")) {
|
||||||
|
return LocalDateTime.parse(text + " 00:00:00",
|
||||||
|
DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss"));
|
||||||
|
}
|
||||||
|
// yyyy-MM-dd
|
||||||
|
if (text.length() == 10) {
|
||||||
|
return LocalDateTime.parse(text + "T00:00:00");
|
||||||
|
}
|
||||||
|
// yyyy-MM-ddTHH:mm:ss
|
||||||
|
return LocalDateTime.parse(text);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
package com.nflg.wms.common.pojo.vo;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IQC超时统计报表VO
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class QmsIqcOverdueReportVO {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IQC超时任务总数
|
||||||
|
*/
|
||||||
|
private Integer totalOverdueCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IQC平均超时时间(格式:X.XX小时)
|
||||||
|
*/
|
||||||
|
private String avgOverdueTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IQC严重超时数(>=24小时)
|
||||||
|
*/
|
||||||
|
private Integer severeOverdueCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IQC超时任务列表
|
||||||
|
*/
|
||||||
|
private List<QmsOverdueReportVO.OverdueTaskItem> iqcOverdueTasks;
|
||||||
|
}
|
||||||
|
|
@ -10,31 +10,67 @@ import java.util.List;
|
||||||
@Data
|
@Data
|
||||||
public class QmsOverdueReportVO {
|
public class QmsOverdueReportVO {
|
||||||
|
|
||||||
/**
|
// ===== IQC超时统计 =====
|
||||||
* 超时任务总数
|
|
||||||
*/
|
|
||||||
private Integer totalOverdueCount;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 平均超时时间(格式:xx小时xx分钟xx秒)
|
* IQC超时任务总数
|
||||||
*/
|
*/
|
||||||
private String avgOverdueTime;
|
private Integer iqcTotalOverdueCount;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 严重超时数(阈值待定)
|
* IQC平均超时时间(格式:X.XX小时)
|
||||||
*/
|
*/
|
||||||
private Integer severeOverdueCount;
|
private String iqcAvgOverdueTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IQC严重超时数(>=24小时)
|
||||||
|
*/
|
||||||
|
private Integer iqcSevereOverdueCount;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* IQC超时任务列表
|
* IQC超时任务列表
|
||||||
*/
|
*/
|
||||||
private List<OverdueTaskItem> iqcOverdueTasks;
|
private List<OverdueTaskItem> iqcOverdueTasks;
|
||||||
|
|
||||||
|
// ===== PDI超时统计 =====
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PDI超时任务总数
|
||||||
|
*/
|
||||||
|
private Integer pdiTotalOverdueCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PDI平均超时时间(格式:X.XX小时)
|
||||||
|
*/
|
||||||
|
private String pdiAvgOverdueTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PDI严重超时数(>=24小时)
|
||||||
|
*/
|
||||||
|
private Integer pdiSevereOverdueCount;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* PDI超时任务列表
|
* PDI超时任务列表
|
||||||
*/
|
*/
|
||||||
private List<OverdueTaskItem> pdiOverdueTasks;
|
private List<OverdueTaskItem> pdiOverdueTasks;
|
||||||
|
|
||||||
|
// ===== 工单超时统计 =====
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 工单超时任务总数
|
||||||
|
*/
|
||||||
|
private Integer ticketTotalOverdueCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 工单平均超时时间(格式:X.XX小时)
|
||||||
|
*/
|
||||||
|
private String ticketAvgOverdueTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 工单严重超时数(overdue=2)
|
||||||
|
*/
|
||||||
|
private Integer ticketSevereOverdueCount;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 工单超时任务列表
|
* 工单超时任务列表
|
||||||
*/
|
*/
|
||||||
|
|
@ -45,39 +81,28 @@ public class QmsOverdueReportVO {
|
||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
public static class OverdueTaskItem {
|
public static class OverdueTaskItem {
|
||||||
/**
|
/** 任务/工单ID */
|
||||||
* 工单编号
|
private Long taskId;
|
||||||
*/
|
|
||||||
|
/** 工单编号 */
|
||||||
private String taskNo;
|
private String taskNo;
|
||||||
|
|
||||||
/**
|
/** 类型(IQC/PDI/IQC工单/PDI工单/巡检工单) */
|
||||||
* 类型(IQC/PDI/工单类型)
|
|
||||||
*/
|
|
||||||
private String type;
|
private String type;
|
||||||
|
|
||||||
/**
|
/** 处理人姓名列表 */
|
||||||
* 处理人姓名列表
|
|
||||||
*/
|
|
||||||
private List<String> handlerNames;
|
private List<String> handlerNames;
|
||||||
|
|
||||||
/**
|
/** 处理人ID列表 */
|
||||||
* 处理人ID列表
|
|
||||||
*/
|
|
||||||
private List<Long> handlerIds;
|
private List<Long> handlerIds;
|
||||||
|
|
||||||
/**
|
/** 创建日期(格式:yyyy-MM-dd HH:mm) */
|
||||||
* 创建日期
|
|
||||||
*/
|
|
||||||
private String createDate;
|
private String createDate;
|
||||||
|
|
||||||
/**
|
/** 要求完成时间(格式:yyyy-MM-dd HH:mm) */
|
||||||
* 要求完成时间
|
|
||||||
*/
|
|
||||||
private String requiredCompletionTime;
|
private String requiredCompletionTime;
|
||||||
|
|
||||||
/**
|
/** 超时时长(格式:X.XX小时) */
|
||||||
* 超时时长(格式:xx小时xx分钟xx秒)
|
|
||||||
*/
|
|
||||||
private String overdueDuration;
|
private String overdueDuration;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
package com.nflg.wms.common.pojo.vo;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PDI超时统计报表VO
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class QmsPdiOverdueReportVO {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PDI超时任务总数
|
||||||
|
*/
|
||||||
|
private Integer totalOverdueCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PDI平均超时时间(格式:X.XX小时)
|
||||||
|
*/
|
||||||
|
private String avgOverdueTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PDI严重超时数(>=24小时)
|
||||||
|
*/
|
||||||
|
private Integer severeOverdueCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PDI超时任务列表
|
||||||
|
*/
|
||||||
|
private List<QmsOverdueReportVO.OverdueTaskItem> pdiOverdueTasks;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
package com.nflg.wms.common.pojo.vo;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 工单超时统计报表VO
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class QmsTicketOverdueReportVO {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 工单超时任务总数
|
||||||
|
*/
|
||||||
|
private Integer totalOverdueCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 工单平均超时时间(格式:X.XX小时)
|
||||||
|
*/
|
||||||
|
private String avgOverdueTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 工单严重超时数(overdue=2)
|
||||||
|
*/
|
||||||
|
private Integer severeOverdueCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 工单超时任务列表
|
||||||
|
*/
|
||||||
|
private List<QmsOverdueReportVO.OverdueTaskItem> ticketOverdueTasks;
|
||||||
|
}
|
||||||
|
|
@ -16,15 +16,25 @@ public class QmsTicketReportVO {
|
||||||
private Integer totalTickets;
|
private Integer totalTickets;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 已完成数
|
* 已完成数(status=2)
|
||||||
*/
|
*/
|
||||||
private Integer completedCount;
|
private Integer completedCount;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 未完成数
|
* 未完成数(status≠2)
|
||||||
*/
|
*/
|
||||||
private Integer unfinishedCount;
|
private Integer unfinishedCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 待流转数(status=0)
|
||||||
|
*/
|
||||||
|
private Integer pendingTransferCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理中数(status=1)
|
||||||
|
*/
|
||||||
|
private Integer processingCount;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 总体完成率(百分比)
|
* 总体完成率(百分比)
|
||||||
*/
|
*/
|
||||||
|
|
@ -36,38 +46,62 @@ public class QmsTicketReportVO {
|
||||||
private String avgProcessingTime;
|
private String avgProcessingTime;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 各状态占比列表
|
* 各状态占比列表(待流转、处理中、已完成)
|
||||||
*/
|
*/
|
||||||
private List<StatusDistribution> statusDistributions;
|
private List<StatusDistribution> statusDistributions;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 问题类别数量列表
|
* 完成状态分布(已完成工单中各审批结果占比)
|
||||||
|
*/
|
||||||
|
private List<CompletionStatus> completionStatusList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 问题类别数量列表(暂空置)
|
||||||
*/
|
*/
|
||||||
private List<IssueTypeCount> issueTypeCounts;
|
private List<IssueTypeCount> issueTypeCounts;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 用户完成率列表
|
* 用户完成率前10
|
||||||
*/
|
*/
|
||||||
private List<UserCompletionRate> userCompletionRates;
|
private List<UserCompletionRate> top10Users;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户完成率后10
|
||||||
|
*/
|
||||||
|
private List<UserCompletionRate> bottom10Users;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 整体平均完成率
|
||||||
|
*/
|
||||||
|
private Double overallAvgCompletionRate;
|
||||||
|
|
||||||
|
// ===== 内部类 =====
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 状态分布
|
* 状态分布
|
||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
public static class StatusDistribution {
|
public static class StatusDistribution {
|
||||||
/**
|
/** 状态(0=待流转,1=处理中,2=已完成) */
|
||||||
* 状态(0=待流转,1=处理中,2=已完成)
|
|
||||||
*/
|
|
||||||
private Short status;
|
private Short status;
|
||||||
|
/** 状态名称 */
|
||||||
/**
|
|
||||||
* 状态名称
|
|
||||||
*/
|
|
||||||
private String statusName;
|
private String statusName;
|
||||||
|
/** 占比(百分比) */
|
||||||
|
private Double percentage;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 占比(百分比)
|
* 完成状态(已完成工单的审批结果分布)
|
||||||
*/
|
*/
|
||||||
|
@Data
|
||||||
|
public static class CompletionStatus {
|
||||||
|
/** 审批结果(0=通过,1=驳回,2=退货,3=报废,4=维修,5=挑选使用,6=让渡使用) */
|
||||||
|
private Short approvalStatus;
|
||||||
|
/** 状态名称 */
|
||||||
|
private String statusName;
|
||||||
|
/** 数量 */
|
||||||
|
private Integer count;
|
||||||
|
/** 占比(百分比) */
|
||||||
private Double percentage;
|
private Double percentage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -76,14 +110,9 @@ public class QmsTicketReportVO {
|
||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
public static class IssueTypeCount {
|
public static class IssueTypeCount {
|
||||||
/**
|
/** 问题类别名称 */
|
||||||
* 问题类别名称
|
|
||||||
*/
|
|
||||||
private String issueTypeName;
|
private String issueTypeName;
|
||||||
|
/** 数量 */
|
||||||
/**
|
|
||||||
* 数量
|
|
||||||
*/
|
|
||||||
private Integer count;
|
private Integer count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -92,19 +121,15 @@ public class QmsTicketReportVO {
|
||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
public static class UserCompletionRate {
|
public static class UserCompletionRate {
|
||||||
/**
|
/** 用户姓名 */
|
||||||
* 用户姓名
|
|
||||||
*/
|
|
||||||
private String userName;
|
private String userName;
|
||||||
|
/** 用户ID */
|
||||||
/**
|
|
||||||
* 用户ID
|
|
||||||
*/
|
|
||||||
private Long userId;
|
private Long userId;
|
||||||
|
/** 完成率(百分比) */
|
||||||
/**
|
|
||||||
* 完成率(百分比)
|
|
||||||
*/
|
|
||||||
private Double completionRate;
|
private Double completionRate;
|
||||||
|
/** 分配工单数 */
|
||||||
|
private Integer totalAssigned;
|
||||||
|
/** 完成工单数 */
|
||||||
|
private Integer totalCompleted;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -109,6 +109,11 @@ public class QmsIssueTicket implements Serializable {
|
||||||
*/
|
*/
|
||||||
private Short status;
|
private Short status;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 超时状态:0=未超时,1=已超时,2=严重超时
|
||||||
|
*/
|
||||||
|
private Integer overdue;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 审批状态:0=通过,1=驳回,2=退货,3=报废,4=维修,5=挑选使用,6=让渡使用
|
* 审批状态:0=通过,1=驳回,2=退货,3=报废,4=维修,5=挑选使用,6=让渡使用
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,8 @@
|
||||||
<!-- 查询检测项分布(按检测项名称统计数量) -->
|
<!-- 查询检测项分布(按检测项名称统计数量) -->
|
||||||
<select id="getInspectionItemDistribution" resultType="map">
|
<select id="getInspectionItemDistribution" resultType="map">
|
||||||
SELECT
|
SELECT
|
||||||
sic.name AS itemName,
|
sic.name AS "itemName",
|
||||||
COUNT(*) AS itemCount
|
COUNT(*) AS "itemCount"
|
||||||
FROM qms_incoming_inspection_task t
|
FROM qms_incoming_inspection_task t
|
||||||
INNER JOIN qms_incoming_inspection_task_record r ON r.task_id = t.id
|
INNER JOIN qms_incoming_inspection_task_record r ON r.task_id = t.id
|
||||||
INNER JOIN qms_incoming_inspection_task_record_item ri ON ri.record_id = r.id
|
INNER JOIN qms_incoming_inspection_task_record_item ri ON ri.record_id = r.id
|
||||||
|
|
@ -14,34 +14,34 @@
|
||||||
WHERE t.submit_time >= #{startTime}
|
WHERE t.submit_time >= #{startTime}
|
||||||
AND t.submit_time <= #{endTime}
|
AND t.submit_time <= #{endTime}
|
||||||
GROUP BY sic.name
|
GROUP BY sic.name
|
||||||
ORDER BY itemCount DESC
|
ORDER BY COUNT(*) DESC
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<!-- 查询物料类别不合格率(按不合格数量排序) -->
|
<!-- 查询物料类别不合格率(按不合格数量排序) -->
|
||||||
<select id="getMaterialCategoryFailRate" resultType="map">
|
<select id="getMaterialCategoryFailRate" resultType="map">
|
||||||
SELECT
|
SELECT
|
||||||
mc.name AS categoryName,
|
mc.category_name AS "categoryName",
|
||||||
SUM(CASE WHEN t.inspection_result = false THEN 1 ELSE 0 END) AS failCount
|
SUM(CASE WHEN t.inspection_result = false THEN 1 ELSE 0 END) AS "failCount"
|
||||||
FROM qms_incoming_inspection_task t
|
FROM qms_incoming_inspection_task t
|
||||||
INNER JOIN qms_qc_material m ON m.id = t.material_id
|
INNER JOIN qms_qc_material m ON m.id = t.material_id
|
||||||
INNER JOIN qms_qc_material_category mc ON mc.id = m.category_id
|
INNER JOIN qms_qc_material_category mc ON mc.category_code = m.material_category_code
|
||||||
WHERE t.submit_time >= #{startTime}
|
WHERE t.submit_time >= #{startTime}
|
||||||
AND t.submit_time <= #{endTime}
|
AND t.submit_time <= #{endTime}
|
||||||
GROUP BY mc.name
|
GROUP BY mc.category_name
|
||||||
ORDER BY failCount DESC
|
ORDER BY SUM(CASE WHEN t.inspection_result = false THEN 1 ELSE 0 END) DESC
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<!-- 查询PDI主要缺陷(按机型编号统计数量) -->
|
<!-- 查询PDI主要缺陷(按机型编号统计数量) -->
|
||||||
<select id="getPdiMainDefects" resultType="map">
|
<select id="getPdiMainDefects" resultType="map">
|
||||||
SELECT
|
SELECT
|
||||||
dr.machine_no AS machineNo,
|
dr.machine_no AS "machineNo",
|
||||||
COUNT(*) AS defectCount
|
COUNT(*) AS "defectCount"
|
||||||
FROM qms_pdi_task_record t
|
FROM qms_pdi_task_record t
|
||||||
INNER JOIN qms_pdi_detection_rules dr ON dr.id = t.detection_rules_id
|
INNER JOIN qms_pdi_detection_rules dr ON dr.id = t.detection_rules_id
|
||||||
WHERE t.submission_time >= #{startTime}
|
WHERE t.submission_time >= #{startTime}
|
||||||
AND t.submission_time <= #{endTime}
|
AND t.submission_time <= #{endTime}
|
||||||
GROUP BY dr.machine_no
|
GROUP BY dr.machine_no
|
||||||
ORDER BY defectCount DESC
|
ORDER BY COUNT(*) DESC
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<!-- 查询部件名称 -->
|
<!-- 查询部件名称 -->
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue