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();
|
||||
}
|
||||
|
||||
// /**
|
||||
// * 查询PDI工单详情
|
||||
// * 返回工单基本信息、处理记录及措施列表
|
||||
// */
|
||||
// @GetMapping("detail/pdi-ticket")
|
||||
// public ApiResult<QmsPdiTicketDetailVO> detailPdiTicket(@NotNull(message = "ID不能为空") Long id) {
|
||||
// return ApiResult.success(issueTicketControllerService.getPdiTicketDetail(id));
|
||||
// }
|
||||
/**
|
||||
* 查询PDI工单详情
|
||||
* 返回工单基本信息、处理记录及措施列表
|
||||
*/
|
||||
@GetMapping("detail/pdi-ticket")
|
||||
public ApiResult<QmsPdiTicketDetailVO> detailPdiTicket(@NotNull(message = "ID不能为空") Long id) {
|
||||
return ApiResult.success(issueTicketControllerService.getPdiTicketDetail(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询本人的PDI工单详情
|
||||
|
|
|
|||
|
|
@ -47,10 +47,26 @@ public class QmsReportController extends BaseController {
|
|||
}
|
||||
|
||||
/**
|
||||
* 超时统计数据
|
||||
* IQC超时统计数据
|
||||
*/
|
||||
@PostMapping("overdue")
|
||||
public ApiResult<QmsOverdueReportVO> getOverdueReport() {
|
||||
return ApiResult.success(reportControllerService.getOverdueReport());
|
||||
@PostMapping("overdue/iqc")
|
||||
public ApiResult<QmsIqcOverdueReportVO> getIqcOverdueReport() {
|
||||
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;
|
||||
}
|
||||
|
||||
// 处理检查核实内容:如果以英文字母结束,追加换行符
|
||||
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;
|
||||
componentItemCountMap.put(componentsId, itemSort);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
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.vo.*;
|
||||
import com.nflg.wms.repository.entity.*;
|
||||
|
|
@ -50,6 +51,12 @@ public class QmsReportControllerService {
|
|||
@Resource
|
||||
private IQmsPdiDetectionRulesService pdiDetectionRulesService;
|
||||
|
||||
@Resource
|
||||
private IQmsIssueTicketService issueTicketService;
|
||||
|
||||
@Resource
|
||||
private IQmsIssueTicketProcessService issueTicketProcessService;
|
||||
|
||||
/**
|
||||
* IQC报表数据
|
||||
*/
|
||||
|
|
@ -61,13 +68,13 @@ public class QmsReportControllerService {
|
|||
LocalDateTime currentEnd = request.getEndTime();
|
||||
boolean hasTimeRange = currentStart != null || currentEnd != null;
|
||||
|
||||
// 如果都没传,查询全部
|
||||
// 如果都没传,使用合理的时间范围(最近1年)
|
||||
if (!hasTimeRange) {
|
||||
currentStart = LocalDateTime.MIN;
|
||||
currentEnd = LocalDateTime.MAX;
|
||||
currentStart = LocalDateTime.now().minusYears(1);
|
||||
currentEnd = LocalDateTime.now();
|
||||
} else {
|
||||
if (currentStart == null) currentStart = LocalDateTime.MIN;
|
||||
if (currentEnd == null) currentEnd = LocalDateTime.MAX;
|
||||
if (currentStart == null) currentStart = LocalDateTime.now().minusYears(1);
|
||||
if (currentEnd == null) currentEnd = LocalDateTime.now();
|
||||
}
|
||||
|
||||
// 计算上周期
|
||||
|
|
@ -460,13 +467,13 @@ public class QmsReportControllerService {
|
|||
LocalDateTime currentEnd = request.getEndTime();
|
||||
boolean hasTimeRange = currentStart != null || currentEnd != null;
|
||||
|
||||
// 如果都没传,查询全部
|
||||
// 如果都没传,使用合理的时间范围(最近1年)
|
||||
if (!hasTimeRange) {
|
||||
currentStart = LocalDateTime.MIN;
|
||||
currentEnd = LocalDateTime.MAX;
|
||||
currentStart = LocalDateTime.now().minusYears(1);
|
||||
currentEnd = LocalDateTime.now();
|
||||
} else {
|
||||
if (currentStart == null) currentStart = LocalDateTime.MIN;
|
||||
if (currentEnd == null) currentEnd = LocalDateTime.MAX;
|
||||
if (currentStart == null) currentStart = LocalDateTime.now().minusYears(1);
|
||||
if (currentEnd == null) currentEnd = LocalDateTime.now();
|
||||
}
|
||||
|
||||
// 计算上周期
|
||||
|
|
@ -606,7 +613,7 @@ public class QmsReportControllerService {
|
|||
Map<String, Object> result = new HashMap<>();
|
||||
|
||||
// 当前周期平均时间
|
||||
String currentAvgTime = "0H0Min0S";
|
||||
String currentAvgTime = "0天0小时";
|
||||
long currentAvgSeconds = 0;
|
||||
|
||||
List<QmsPdiTaskRecord> currentCompletedTasks = pdiTaskRecordService.lambdaQuery()
|
||||
|
|
@ -628,7 +635,7 @@ public class QmsReportControllerService {
|
|||
}
|
||||
|
||||
// 上周期平均时间
|
||||
String previousAvgTime = "0H0Min0S";
|
||||
String previousAvgTime = "0天0小时";
|
||||
long previousAvgSeconds = 0;
|
||||
|
||||
if (hasTimeRange) {
|
||||
|
|
@ -812,28 +819,613 @@ public class QmsReportControllerService {
|
|||
}
|
||||
|
||||
/**
|
||||
* 格式化时长:秒 -> xxHxxMinxxS
|
||||
* 格式化时长:秒 -> xx天xx小时
|
||||
*/
|
||||
private String formatDurationPDI(long seconds) {
|
||||
long hours = seconds / 3600;
|
||||
long minutes = (seconds % 3600) / 60;
|
||||
long secs = seconds % 60;
|
||||
return hours + "H" + minutes + "Min" + secs + "S";
|
||||
long days = seconds / 86400;
|
||||
double hours = (seconds % 86400) / 3600.0;
|
||||
return days + "天" + String.format("%.2f", hours) + "小时";
|
||||
}
|
||||
|
||||
/**
|
||||
* 工单报表数据
|
||||
*/
|
||||
public QmsTicketReportVO getTicketReport(QmsReportQueryQO request) {
|
||||
// TODO: 实现逻辑
|
||||
return new QmsTicketReportVO();
|
||||
QmsTicketReportVO vo = 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() {
|
||||
// TODO: 实现逻辑
|
||||
return new QmsOverdueReportVO();
|
||||
private List<QmsTicketReportVO.StatusDistribution> buildStatusDistributions(
|
||||
int total, int pendingTransfer, int processing, int completed) {
|
||||
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 java.time.LocalDateTime;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 报表查询请求QO
|
||||
|
|
@ -11,12 +14,53 @@ import java.time.LocalDateTime;
|
|||
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
|
||||
public class QmsOverdueReportVO {
|
||||
|
||||
/**
|
||||
* 超时任务总数
|
||||
*/
|
||||
private Integer totalOverdueCount;
|
||||
// ===== IQC超时统计 =====
|
||||
|
||||
/**
|
||||
* 平均超时时间(格式: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超时任务列表
|
||||
*/
|
||||
private List<OverdueTaskItem> iqcOverdueTasks;
|
||||
|
||||
// ===== PDI超时统计 =====
|
||||
|
||||
/**
|
||||
* PDI超时任务总数
|
||||
*/
|
||||
private Integer pdiTotalOverdueCount;
|
||||
|
||||
/**
|
||||
* PDI平均超时时间(格式:X.XX小时)
|
||||
*/
|
||||
private String pdiAvgOverdueTime;
|
||||
|
||||
/**
|
||||
* PDI严重超时数(>=24小时)
|
||||
*/
|
||||
private Integer pdiSevereOverdueCount;
|
||||
|
||||
/**
|
||||
* PDI超时任务列表
|
||||
*/
|
||||
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
|
||||
public static class OverdueTaskItem {
|
||||
/**
|
||||
* 工单编号
|
||||
*/
|
||||
/** 任务/工单ID */
|
||||
private Long taskId;
|
||||
|
||||
/** 工单编号 */
|
||||
private String taskNo;
|
||||
|
||||
/**
|
||||
* 类型(IQC/PDI/工单类型)
|
||||
*/
|
||||
/** 类型(IQC/PDI/IQC工单/PDI工单/巡检工单) */
|
||||
private String type;
|
||||
|
||||
/**
|
||||
* 处理人姓名列表
|
||||
*/
|
||||
/** 处理人姓名列表 */
|
||||
private List<String> handlerNames;
|
||||
|
||||
/**
|
||||
* 处理人ID列表
|
||||
*/
|
||||
/** 处理人ID列表 */
|
||||
private List<Long> handlerIds;
|
||||
|
||||
/**
|
||||
* 创建日期
|
||||
*/
|
||||
/** 创建日期(格式:yyyy-MM-dd HH:mm) */
|
||||
private String createDate;
|
||||
|
||||
/**
|
||||
* 要求完成时间
|
||||
*/
|
||||
/** 要求完成时间(格式:yyyy-MM-dd HH:mm) */
|
||||
private String requiredCompletionTime;
|
||||
|
||||
/**
|
||||
* 超时时长(格式:xx小时xx分钟xx秒)
|
||||
*/
|
||||
/** 超时时长(格式:X.XX小时) */
|
||||
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;
|
||||
|
||||
/**
|
||||
* 已完成数
|
||||
* 已完成数(status=2)
|
||||
*/
|
||||
private Integer completedCount;
|
||||
|
||||
/**
|
||||
* 未完成数
|
||||
* 未完成数(status≠2)
|
||||
*/
|
||||
private Integer unfinishedCount;
|
||||
|
||||
/**
|
||||
* 待流转数(status=0)
|
||||
*/
|
||||
private Integer pendingTransferCount;
|
||||
|
||||
/**
|
||||
* 处理中数(status=1)
|
||||
*/
|
||||
private Integer processingCount;
|
||||
|
||||
/**
|
||||
* 总体完成率(百分比)
|
||||
*/
|
||||
|
|
@ -36,38 +46,62 @@ public class QmsTicketReportVO {
|
|||
private String avgProcessingTime;
|
||||
|
||||
/**
|
||||
* 各状态占比列表
|
||||
* 各状态占比列表(待流转、处理中、已完成)
|
||||
*/
|
||||
private List<StatusDistribution> statusDistributions;
|
||||
|
||||
/**
|
||||
* 问题类别数量列表
|
||||
* 完成状态分布(已完成工单中各审批结果占比)
|
||||
*/
|
||||
private List<CompletionStatus> completionStatusList;
|
||||
|
||||
/**
|
||||
* 问题类别数量列表(暂空置)
|
||||
*/
|
||||
private List<IssueTypeCount> issueTypeCounts;
|
||||
|
||||
/**
|
||||
* 用户完成率列表
|
||||
* 用户完成率前10
|
||||
*/
|
||||
private List<UserCompletionRate> userCompletionRates;
|
||||
private List<UserCompletionRate> top10Users;
|
||||
|
||||
/**
|
||||
* 用户完成率后10
|
||||
*/
|
||||
private List<UserCompletionRate> bottom10Users;
|
||||
|
||||
/**
|
||||
* 整体平均完成率
|
||||
*/
|
||||
private Double overallAvgCompletionRate;
|
||||
|
||||
// ===== 内部类 =====
|
||||
|
||||
/**
|
||||
* 状态分布
|
||||
*/
|
||||
@Data
|
||||
public static class StatusDistribution {
|
||||
/**
|
||||
* 状态(0=待流转,1=处理中,2=已完成)
|
||||
*/
|
||||
/** 状态(0=待流转,1=处理中,2=已完成) */
|
||||
private Short status;
|
||||
|
||||
/**
|
||||
* 状态名称
|
||||
*/
|
||||
/** 状态名称 */
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
@ -76,14 +110,9 @@ public class QmsTicketReportVO {
|
|||
*/
|
||||
@Data
|
||||
public static class IssueTypeCount {
|
||||
/**
|
||||
* 问题类别名称
|
||||
*/
|
||||
/** 问题类别名称 */
|
||||
private String issueTypeName;
|
||||
|
||||
/**
|
||||
* 数量
|
||||
*/
|
||||
/** 数量 */
|
||||
private Integer count;
|
||||
}
|
||||
|
||||
|
|
@ -92,19 +121,15 @@ public class QmsTicketReportVO {
|
|||
*/
|
||||
@Data
|
||||
public static class UserCompletionRate {
|
||||
/**
|
||||
* 用户姓名
|
||||
*/
|
||||
/** 用户姓名 */
|
||||
private String userName;
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
/** 用户ID */
|
||||
private Long userId;
|
||||
|
||||
/**
|
||||
* 完成率(百分比)
|
||||
*/
|
||||
/** 完成率(百分比) */
|
||||
private Double completionRate;
|
||||
/** 分配工单数 */
|
||||
private Integer totalAssigned;
|
||||
/** 完成工单数 */
|
||||
private Integer totalCompleted;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -109,6 +109,11 @@ public class QmsIssueTicket implements Serializable {
|
|||
*/
|
||||
private Short status;
|
||||
|
||||
/**
|
||||
* 超时状态:0=未超时,1=已超时,2=严重超时
|
||||
*/
|
||||
private Integer overdue;
|
||||
|
||||
/**
|
||||
* 审批状态:0=通过,1=驳回,2=退货,3=报废,4=维修,5=挑选使用,6=让渡使用
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@
|
|||
<!-- 查询检测项分布(按检测项名称统计数量) -->
|
||||
<select id="getInspectionItemDistribution" resultType="map">
|
||||
SELECT
|
||||
sic.name AS itemName,
|
||||
COUNT(*) AS itemCount
|
||||
sic.name AS "itemName",
|
||||
COUNT(*) AS "itemCount"
|
||||
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_item ri ON ri.record_id = r.id
|
||||
|
|
@ -14,34 +14,34 @@
|
|||
WHERE t.submit_time >= #{startTime}
|
||||
AND t.submit_time <= #{endTime}
|
||||
GROUP BY sic.name
|
||||
ORDER BY itemCount DESC
|
||||
ORDER BY COUNT(*) DESC
|
||||
</select>
|
||||
|
||||
<!-- 查询物料类别不合格率(按不合格数量排序) -->
|
||||
<select id="getMaterialCategoryFailRate" resultType="map">
|
||||
SELECT
|
||||
mc.name AS categoryName,
|
||||
SUM(CASE WHEN t.inspection_result = false THEN 1 ELSE 0 END) AS failCount
|
||||
mc.category_name AS "categoryName",
|
||||
SUM(CASE WHEN t.inspection_result = false THEN 1 ELSE 0 END) AS "failCount"
|
||||
FROM qms_incoming_inspection_task t
|
||||
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}
|
||||
AND t.submit_time <= #{endTime}
|
||||
GROUP BY mc.name
|
||||
ORDER BY failCount DESC
|
||||
GROUP BY mc.category_name
|
||||
ORDER BY SUM(CASE WHEN t.inspection_result = false THEN 1 ELSE 0 END) DESC
|
||||
</select>
|
||||
|
||||
<!-- 查询PDI主要缺陷(按机型编号统计数量) -->
|
||||
<select id="getPdiMainDefects" resultType="map">
|
||||
SELECT
|
||||
dr.machine_no AS machineNo,
|
||||
COUNT(*) AS defectCount
|
||||
dr.machine_no AS "machineNo",
|
||||
COUNT(*) AS "defectCount"
|
||||
FROM qms_pdi_task_record t
|
||||
INNER JOIN qms_pdi_detection_rules dr ON dr.id = t.detection_rules_id
|
||||
WHERE t.submission_time >= #{startTime}
|
||||
AND t.submission_time <= #{endTime}
|
||||
GROUP BY dr.machine_no
|
||||
ORDER BY defectCount DESC
|
||||
ORDER BY COUNT(*) DESC
|
||||
</select>
|
||||
|
||||
<!-- 查询部件名称 -->
|
||||
|
|
|
|||
Loading…
Reference in New Issue