feat(qms): 新增PQC工单分派及详情查看功能
- 增加接口支持发起PQC工单,更新工单状态为处理中并分配处理人推送待办 - 实现按处理人分组合并不合格项并创建处理记录及待办消息 - 新增查询本人相关PQC工单详情接口,仅返回本人相关处理记录和措施 - 完善PQC工单处理流程,支持处理人提交审批及措施填写校验 - 添加PQC工单结束处理判断,最后一人提交时更新工单状态为已完成 - 新增文件及措施信息的解析和转换辅助方法,丰富详情数据展示 - 新增PQC任务及任务详情相关实体、接口及实现,支持任务管理功能 - 新增PQC任务相关Controller接口,包括查询、暂存和提交功能 - 优化PQC工单流程中的权限校验和异常处理,保障业务流程合理性
This commit is contained in:
parent
4f78cbe83b
commit
3b782f471f
|
|
@ -46,6 +46,22 @@ public class QmsReportController extends BaseController {
|
||||||
return ApiResult.success(reportControllerService.getTicketReport(request));
|
return ApiResult.success(reportControllerService.getTicketReport(request));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PQC报表数据
|
||||||
|
*/
|
||||||
|
@PostMapping("pqc")
|
||||||
|
public ApiResult<QmsPqcReportVO> getPqcReport(@Valid @RequestBody QmsReportQueryQO request) {
|
||||||
|
return ApiResult.success(reportControllerService.getPqcReport(request));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PQC待复核统计数据
|
||||||
|
*/
|
||||||
|
@PostMapping("pqc/pending-review")
|
||||||
|
public ApiResult<QmsPqcPendingReviewVO> getPqcPendingReviewStats() {
|
||||||
|
return ApiResult.success(reportControllerService.getPqcPendingReviewStats());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* IQC超时统计数据
|
* IQC超时统计数据
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -1428,4 +1428,439 @@ public class QmsReportControllerService {
|
||||||
double avgHours = totalHours / durations.size();
|
double avgHours = totalHours / durations.size();
|
||||||
return String.format("%.2f小时", avgHours);
|
return String.format("%.2f小时", avgHours);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ===== PQC报表 =====
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private IQmsPqcTaskRecordService pqcTaskRecordService;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private IQmsPqcInspectionPointService pqcInspectionPointService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PQC报表数据
|
||||||
|
*/
|
||||||
|
public QmsPqcReportVO getPqcReport(QmsReportQueryQO request) {
|
||||||
|
QmsPqcReportVO vo = new QmsPqcReportVO();
|
||||||
|
|
||||||
|
// 计算时间范围
|
||||||
|
LocalDateTime currentStart = request.getStartTime();
|
||||||
|
LocalDateTime currentEnd = request.getEndTime();
|
||||||
|
boolean hasTimeRange = currentStart != null || currentEnd != null;
|
||||||
|
|
||||||
|
if (!hasTimeRange) {
|
||||||
|
currentStart = LocalDateTime.now().minusYears(1);
|
||||||
|
currentEnd = LocalDateTime.now();
|
||||||
|
} else {
|
||||||
|
if (currentStart == null) currentStart = LocalDateTime.now().minusYears(1);
|
||||||
|
if (currentEnd == null) currentEnd = LocalDateTime.now();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算上周期
|
||||||
|
long daysBetween = java.time.temporal.ChronoUnit.DAYS.between(currentStart, currentEnd);
|
||||||
|
LocalDateTime previousStart = currentStart.minusDays(daysBetween);
|
||||||
|
LocalDateTime previousEnd = currentStart;
|
||||||
|
|
||||||
|
// 1. 总工单数
|
||||||
|
Long currentTotal = pqcTaskRecordService.lambdaQuery()
|
||||||
|
.ge(QmsPqcTaskRecord::getCreateTime, currentStart)
|
||||||
|
.le(QmsPqcTaskRecord::getCreateTime, currentEnd)
|
||||||
|
.count();
|
||||||
|
Long previousTotal = pqcTaskRecordService.lambdaQuery()
|
||||||
|
.ge(QmsPqcTaskRecord::getCreateTime, previousStart)
|
||||||
|
.lt(QmsPqcTaskRecord::getCreateTime, previousEnd)
|
||||||
|
.count();
|
||||||
|
|
||||||
|
vo.setTotalTickets(currentTotal.intValue());
|
||||||
|
vo.setTotalChange((int) (currentTotal - previousTotal));
|
||||||
|
vo.setTotalChangeRate(calculateChangeRate(currentTotal, previousTotal));
|
||||||
|
|
||||||
|
// 2. 已完成数(状态=3)
|
||||||
|
Long currentCompleted = pqcTaskRecordService.lambdaQuery()
|
||||||
|
.eq(QmsPqcTaskRecord::getStatus, (short) 3)
|
||||||
|
.ge(QmsPqcTaskRecord::getCreateTime, currentStart)
|
||||||
|
.le(QmsPqcTaskRecord::getCreateTime, currentEnd)
|
||||||
|
.count();
|
||||||
|
Long previousCompleted = pqcTaskRecordService.lambdaQuery()
|
||||||
|
.eq(QmsPqcTaskRecord::getStatus, (short) 3)
|
||||||
|
.ge(QmsPqcTaskRecord::getCreateTime, previousStart)
|
||||||
|
.lt(QmsPqcTaskRecord::getCreateTime, previousEnd)
|
||||||
|
.count();
|
||||||
|
|
||||||
|
vo.setCompletedTickets(currentCompleted.intValue());
|
||||||
|
double currentCompletionRate = currentTotal > 0 ? (currentCompleted * 100.0 / currentTotal) : 0;
|
||||||
|
double previousCompletionRate = previousTotal > 0 ? (previousCompleted * 100.0 / previousTotal) : 0;
|
||||||
|
vo.setCompletionRate(String.format("%.2f%%", currentCompletionRate));
|
||||||
|
vo.setCompletionRateChange(calculateChangeRate(currentCompletionRate, previousCompletionRate));
|
||||||
|
|
||||||
|
// 3. 待处理数(状态≠3)
|
||||||
|
vo.setPendingTickets((int) (currentTotal - currentCompleted));
|
||||||
|
|
||||||
|
// 4. 平均耗时
|
||||||
|
String avgTime = calculatePqcAvgProcessingTime(currentStart, currentEnd);
|
||||||
|
String previousAvgTime = calculatePqcAvgProcessingTime(previousStart, previousEnd);
|
||||||
|
vo.setAvgProcessingTime(avgTime);
|
||||||
|
|
||||||
|
// 解析平均耗时(小时)用于计算增减率
|
||||||
|
double currentAvgHours = parseHoursFromTimeStr(avgTime);
|
||||||
|
double previousAvgHours = parseHoursFromTimeStr(previousAvgTime);
|
||||||
|
vo.setAvgTimeChangeRate(calculateChangeRate(currentAvgHours, previousAvgHours));
|
||||||
|
|
||||||
|
// 5. 一次合格率(无关联任务)
|
||||||
|
calculateFirstPassRate(vo, currentStart, currentEnd, previousStart, previousEnd);
|
||||||
|
|
||||||
|
// 6. 人效统计
|
||||||
|
vo.setEfficiencyStats(calculateEfficiencyStats(currentStart, currentEnd));
|
||||||
|
|
||||||
|
// 7. 步装合格率
|
||||||
|
vo.setStepAssemblyRates(calculateStepAssemblyRates(currentStart, currentEnd));
|
||||||
|
|
||||||
|
return vo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算一次合格率
|
||||||
|
*/
|
||||||
|
private void calculateFirstPassRate(QmsPqcReportVO vo, LocalDateTime currentStart, LocalDateTime currentEnd,
|
||||||
|
LocalDateTime previousStart, LocalDateTime previousEnd) {
|
||||||
|
// 当前周期
|
||||||
|
Long currentTotalNoRelated = pqcTaskRecordService.lambdaQuery()
|
||||||
|
.isNull(QmsPqcTaskRecord::getRelatedTaskId)
|
||||||
|
.ge(QmsPqcTaskRecord::getCreateTime, currentStart)
|
||||||
|
.le(QmsPqcTaskRecord::getCreateTime, currentEnd)
|
||||||
|
.count();
|
||||||
|
Long currentPassed = pqcTaskRecordService.lambdaQuery()
|
||||||
|
.isNull(QmsPqcTaskRecord::getRelatedTaskId)
|
||||||
|
.eq(QmsPqcTaskRecord::getEnable, true)
|
||||||
|
.ge(QmsPqcTaskRecord::getCreateTime, currentStart)
|
||||||
|
.le(QmsPqcTaskRecord::getCreateTime, currentEnd)
|
||||||
|
.count();
|
||||||
|
|
||||||
|
double currentRate = currentTotalNoRelated > 0 ? (currentPassed * 100.0 / currentTotalNoRelated) : 0;
|
||||||
|
|
||||||
|
// 上周期
|
||||||
|
Long previousTotalNoRelated = pqcTaskRecordService.lambdaQuery()
|
||||||
|
.isNull(QmsPqcTaskRecord::getRelatedTaskId)
|
||||||
|
.ge(QmsPqcTaskRecord::getCreateTime, previousStart)
|
||||||
|
.lt(QmsPqcTaskRecord::getCreateTime, previousEnd)
|
||||||
|
.count();
|
||||||
|
Long previousPassed = pqcTaskRecordService.lambdaQuery()
|
||||||
|
.isNull(QmsPqcTaskRecord::getRelatedTaskId)
|
||||||
|
.eq(QmsPqcTaskRecord::getEnable, true)
|
||||||
|
.ge(QmsPqcTaskRecord::getCreateTime, previousStart)
|
||||||
|
.lt(QmsPqcTaskRecord::getCreateTime, previousEnd)
|
||||||
|
.count();
|
||||||
|
|
||||||
|
double previousRate = previousTotalNoRelated > 0 ? (previousPassed * 100.0 / previousTotalNoRelated) : 0;
|
||||||
|
|
||||||
|
vo.setFirstPassRate(String.format("%.2f%%", currentRate));
|
||||||
|
vo.setFirstPassRateChange(calculateChangeRate(currentRate, previousRate));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算人效统计
|
||||||
|
*/
|
||||||
|
private List<QmsPqcReportVO.EfficiencyVO> calculateEfficiencyStats(LocalDateTime start, LocalDateTime end) {
|
||||||
|
// 查询所有无关联任务
|
||||||
|
List<QmsPqcTaskRecord> tasks = pqcTaskRecordService.lambdaQuery()
|
||||||
|
.isNull(QmsPqcTaskRecord::getRelatedTaskId)
|
||||||
|
.ge(QmsPqcTaskRecord::getCreateTime, start)
|
||||||
|
.le(QmsPqcTaskRecord::getCreateTime, end)
|
||||||
|
.list();
|
||||||
|
|
||||||
|
if (tasks.isEmpty()) {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 按自检人分组统计
|
||||||
|
Map<Long, List<QmsPqcTaskRecord>> groupByTester = tasks.stream()
|
||||||
|
.filter(t -> t.getSelfTesterId() != null)
|
||||||
|
.collect(Collectors.groupingBy(QmsPqcTaskRecord::getSelfTesterId));
|
||||||
|
|
||||||
|
List<QmsPqcReportVO.EfficiencyVO> stats = new ArrayList<>();
|
||||||
|
for (Map.Entry<Long, List<QmsPqcTaskRecord>> entry : groupByTester.entrySet()) {
|
||||||
|
Long testerId = entry.getKey();
|
||||||
|
List<QmsPqcTaskRecord> testerTasks = entry.getValue();
|
||||||
|
|
||||||
|
long totalTasks = testerTasks.size();
|
||||||
|
long passedTasks = testerTasks.stream().filter(QmsPqcTaskRecord::getEnable).count();
|
||||||
|
double passRate = totalTasks > 0 ? (passedTasks * 100.0 / totalTasks) : 0;
|
||||||
|
|
||||||
|
QmsPqcReportVO.EfficiencyVO efficiencyVO = new QmsPqcReportVO.EfficiencyVO();
|
||||||
|
efficiencyVO.setSelfTesterId(testerId);
|
||||||
|
efficiencyVO.setSelfTesterName(testerTasks.get(0).getSelfTesterName());
|
||||||
|
efficiencyVO.setPassRate(String.format("%.2f%%", passRate));
|
||||||
|
stats.add(efficiencyVO);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 按合格率降序
|
||||||
|
stats.sort((a, b) -> Double.compare(
|
||||||
|
Double.parseDouble(b.getPassRate().replace("%", "")),
|
||||||
|
Double.parseDouble(a.getPassRate().replace("%", ""))
|
||||||
|
));
|
||||||
|
|
||||||
|
// 截取前10+后10
|
||||||
|
if (stats.size() > 20) {
|
||||||
|
List<QmsPqcReportVO.EfficiencyVO> result = new ArrayList<>();
|
||||||
|
result.addAll(stats.subList(0, 10)); // 前10
|
||||||
|
result.addAll(stats.subList(stats.size() - 10, stats.size())); // 后10
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return stats;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算步装合格率
|
||||||
|
*/
|
||||||
|
private List<QmsPqcReportVO.StepAssemblyRateVO> calculateStepAssemblyRates(LocalDateTime start, LocalDateTime end) {
|
||||||
|
// 查询所有无关联任务
|
||||||
|
List<QmsPqcTaskRecord> tasks = pqcTaskRecordService.lambdaQuery()
|
||||||
|
.isNull(QmsPqcTaskRecord::getRelatedTaskId)
|
||||||
|
.ge(QmsPqcTaskRecord::getCreateTime, start)
|
||||||
|
.le(QmsPqcTaskRecord::getCreateTime, end)
|
||||||
|
.list();
|
||||||
|
|
||||||
|
if (tasks.isEmpty()) {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 批量查询检查点信息获取步装名
|
||||||
|
Set<Long> pointIds = tasks.stream()
|
||||||
|
.map(QmsPqcTaskRecord::getInspectionPointId)
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
|
||||||
|
Map<Long, String> pointStepNameMap = new HashMap<>();
|
||||||
|
if (!pointIds.isEmpty()) {
|
||||||
|
List<QmsPqcInspectionPoint> points = pqcInspectionPointService.listByIds(pointIds);
|
||||||
|
pointStepNameMap = points.stream()
|
||||||
|
.filter(p -> p.getStepName() != null)
|
||||||
|
.collect(Collectors.toMap(QmsPqcInspectionPoint::getId, QmsPqcInspectionPoint::getStepName));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 按 步装+机型 分组
|
||||||
|
Map<String, Map<String, List<QmsPqcTaskRecord>>> groupByStepAndModel = new LinkedHashMap<>();
|
||||||
|
for (QmsPqcTaskRecord task : tasks) {
|
||||||
|
String stepName = pointStepNameMap.get(task.getInspectionPointId());
|
||||||
|
if (stepName == null) continue;
|
||||||
|
|
||||||
|
String modelNo = task.getModelNo();
|
||||||
|
if (modelNo == null) continue;
|
||||||
|
|
||||||
|
groupByStepAndModel.computeIfAbsent(stepName, k -> new LinkedHashMap<>())
|
||||||
|
.computeIfAbsent(modelNo, k -> new ArrayList<>())
|
||||||
|
.add(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<QmsPqcReportVO.StepAssemblyRateVO> result = new ArrayList<>();
|
||||||
|
for (Map.Entry<String, Map<String, List<QmsPqcTaskRecord>>> stepEntry : groupByStepAndModel.entrySet()) {
|
||||||
|
String stepName = stepEntry.getKey();
|
||||||
|
Map<String, List<QmsPqcTaskRecord>> modelMap = stepEntry.getValue();
|
||||||
|
|
||||||
|
List<QmsPqcReportVO.StepAssemblyRateVO.ModelRateVO> modelRates = new ArrayList<>();
|
||||||
|
for (Map.Entry<String, List<QmsPqcTaskRecord>> modelEntry : modelMap.entrySet()) {
|
||||||
|
String modelNo = modelEntry.getKey();
|
||||||
|
List<QmsPqcTaskRecord> modelTasks = modelEntry.getValue();
|
||||||
|
|
||||||
|
long totalCheckpoints = modelTasks.size();
|
||||||
|
long passedCheckpoints = modelTasks.stream().filter(QmsPqcTaskRecord::getEnable).count();
|
||||||
|
double passRate = totalCheckpoints > 0 ? (passedCheckpoints * 100.0 / totalCheckpoints) : 0;
|
||||||
|
|
||||||
|
QmsPqcReportVO.StepAssemblyRateVO.ModelRateVO modelRateVO = new QmsPqcReportVO.StepAssemblyRateVO.ModelRateVO();
|
||||||
|
modelRateVO.setModelNo(modelNo);
|
||||||
|
modelRateVO.setPassRate(String.format("%.2f%%", passRate));
|
||||||
|
modelRates.add(modelRateVO);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 按合格率降序
|
||||||
|
modelRates.sort((a, b) -> Double.compare(
|
||||||
|
Double.parseDouble(b.getPassRate().replace("%", "")),
|
||||||
|
Double.parseDouble(a.getPassRate().replace("%", ""))
|
||||||
|
));
|
||||||
|
|
||||||
|
// 截取前5+后5
|
||||||
|
List<QmsPqcReportVO.StepAssemblyRateVO.ModelRateVO> finalModelRates;
|
||||||
|
if (modelRates.size() > 10) {
|
||||||
|
finalModelRates = new ArrayList<>();
|
||||||
|
finalModelRates.addAll(modelRates.subList(0, 5));
|
||||||
|
finalModelRates.addAll(modelRates.subList(modelRates.size() - 5, modelRates.size()));
|
||||||
|
} else {
|
||||||
|
finalModelRates = modelRates;
|
||||||
|
}
|
||||||
|
|
||||||
|
QmsPqcReportVO.StepAssemblyRateVO stepVO = new QmsPqcReportVO.StepAssemblyRateVO();
|
||||||
|
stepVO.setStepAssemblyName(stepName);
|
||||||
|
stepVO.setModelRates(finalModelRates);
|
||||||
|
result.add(stepVO);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算PQC平均处理时间
|
||||||
|
*/
|
||||||
|
private String calculatePqcAvgProcessingTime(LocalDateTime start, LocalDateTime end) {
|
||||||
|
List<QmsPqcTaskRecord> completedTasks = pqcTaskRecordService.lambdaQuery()
|
||||||
|
.eq(QmsPqcTaskRecord::getStatus, (short) 3)
|
||||||
|
.isNotNull(QmsPqcTaskRecord::getCompleteTime)
|
||||||
|
.ge(QmsPqcTaskRecord::getCreateTime, start)
|
||||||
|
.le(QmsPqcTaskRecord::getCreateTime, end)
|
||||||
|
.list();
|
||||||
|
|
||||||
|
if (completedTasks.isEmpty()) {
|
||||||
|
return "0小时";
|
||||||
|
}
|
||||||
|
|
||||||
|
double totalHours = 0;
|
||||||
|
for (QmsPqcTaskRecord task : completedTasks) {
|
||||||
|
if (task.getCompleteTime() != null && task.getCreateTime() != null) {
|
||||||
|
long seconds = java.time.temporal.ChronoUnit.SECONDS.between(task.getCreateTime(), task.getCompleteTime());
|
||||||
|
totalHours += seconds / 3600.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
double avgHours = totalHours / completedTasks.size();
|
||||||
|
return formatHoursToDaysAndHours(avgHours);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化小时为xx天xx小时
|
||||||
|
*/
|
||||||
|
private String formatHoursToDaysAndHours(double hours) {
|
||||||
|
long days = (long) (hours / 24);
|
||||||
|
long remainingHours = Math.round(hours % 24);
|
||||||
|
|
||||||
|
if (days > 0) {
|
||||||
|
return days + "天" + remainingHours + "小时";
|
||||||
|
} else {
|
||||||
|
return remainingHours + "小时";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从时间字符串解析小时数
|
||||||
|
*/
|
||||||
|
private double parseHoursFromTimeStr(String timeStr) {
|
||||||
|
if (timeStr == null || timeStr.isEmpty()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (timeStr.contains("天")) {
|
||||||
|
String[] parts = timeStr.replace("小时", "").split("天");
|
||||||
|
long days = Long.parseLong(parts[0]);
|
||||||
|
long hours = parts.length > 1 ? Long.parseLong(parts[1]) : 0;
|
||||||
|
return days * 24 + hours;
|
||||||
|
} else {
|
||||||
|
return Double.parseDouble(timeStr.replace("小时", ""));
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算增减率
|
||||||
|
*/
|
||||||
|
private String calculateChangeRate(double current, double previous) {
|
||||||
|
if (previous == 0) {
|
||||||
|
if (current == 0) {
|
||||||
|
return "0.00%";
|
||||||
|
} else {
|
||||||
|
return "新增";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
double rate = ((current - previous) / previous) * 100;
|
||||||
|
return String.format("%.2f%%", rate);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== PQC待复核统计 =====
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PQC待复核统计数据
|
||||||
|
*/
|
||||||
|
public QmsPqcPendingReviewVO getPqcPendingReviewStats() {
|
||||||
|
QmsPqcPendingReviewVO vo = new QmsPqcPendingReviewVO();
|
||||||
|
LocalDateTime now = LocalDateTime.now();
|
||||||
|
|
||||||
|
// 1. 查询待复核任务(状态=1)
|
||||||
|
List<QmsPqcTaskRecord> pendingTasks = pqcTaskRecordService.lambdaQuery()
|
||||||
|
.eq(QmsPqcTaskRecord::getStatus, (short) 1)
|
||||||
|
.orderByDesc(QmsPqcTaskRecord::getCreateTime)
|
||||||
|
.list();
|
||||||
|
|
||||||
|
// 2. 待复核任务总数
|
||||||
|
vo.setTotalCount(pendingTasks.size());
|
||||||
|
|
||||||
|
if (pendingTasks.isEmpty()) {
|
||||||
|
vo.setAvgWaitTime("0小时");
|
||||||
|
vo.setTasks(new ArrayList<>());
|
||||||
|
return vo;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 计算平均等待时间
|
||||||
|
long totalWaitSeconds = 0;
|
||||||
|
List<QmsPqcPendingReviewVO.PendingReviewTaskVO> taskVOs = new ArrayList<>();
|
||||||
|
|
||||||
|
for (QmsPqcTaskRecord task : pendingTasks) {
|
||||||
|
// 自检提交时间使用 complete_time
|
||||||
|
LocalDateTime submitTime = task.getCompleteTime();
|
||||||
|
if (submitTime == null) {
|
||||||
|
submitTime = task.getCreateTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
long waitSeconds = java.time.temporal.ChronoUnit.SECONDS.between(submitTime, now);
|
||||||
|
totalWaitSeconds += waitSeconds;
|
||||||
|
|
||||||
|
// 构建任务VO
|
||||||
|
QmsPqcPendingReviewVO.PendingReviewTaskVO taskVO = new QmsPqcPendingReviewVO.PendingReviewTaskVO();
|
||||||
|
taskVO.setTaskId(task.getId());
|
||||||
|
taskVO.setTaskNo(task.getTaskNo());
|
||||||
|
taskVO.setAufnr(task.getAufnr());
|
||||||
|
taskVO.setModelNo(task.getModelNo());
|
||||||
|
taskVO.setNo(task.getNo());
|
||||||
|
taskVO.setSelfTestSubmitTime(submitTime);
|
||||||
|
taskVO.setWaitTime(formatSecondsToDaysAndHours(waitSeconds));
|
||||||
|
taskVOs.add(taskVO);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 平均等待时间(秒)
|
||||||
|
long avgWaitSeconds = totalWaitSeconds / pendingTasks.size();
|
||||||
|
vo.setAvgWaitTime(formatSecondsToDaysAndHoursWithDecimal(avgWaitSeconds));
|
||||||
|
vo.setTasks(taskVOs);
|
||||||
|
|
||||||
|
return vo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化秒为xx天xx小时(小时保留两位小数)
|
||||||
|
* 例如:15分钟 = 900秒 = 0.25小时
|
||||||
|
*/
|
||||||
|
private String formatSecondsToDaysAndHoursWithDecimal(long seconds) {
|
||||||
|
long days = seconds / 86400; // 86400秒 = 1天
|
||||||
|
double remainingHours = (seconds % 86400) / 3600.0;
|
||||||
|
|
||||||
|
if (days > 0) {
|
||||||
|
return days + "天" + String.format("%.2f", remainingHours) + "小时";
|
||||||
|
} else {
|
||||||
|
return String.format("%.2f", remainingHours) + "小时";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化秒为xx天xx小时(用于单个任务)
|
||||||
|
*/
|
||||||
|
private String formatSecondsToDaysAndHours(long seconds) {
|
||||||
|
long days = seconds / 86400;
|
||||||
|
double remainingHours = (seconds % 86400) / 3600.0;
|
||||||
|
|
||||||
|
if (days > 0) {
|
||||||
|
return days + "天" + String.format("%.2f", remainingHours) + "小时";
|
||||||
|
} else {
|
||||||
|
return String.format("%.2f", remainingHours) + "小时";
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,69 @@
|
||||||
|
package com.nflg.wms.common.pojo.vo;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PQC待复核统计VO
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class QmsPqcPendingReviewVO {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 待复核任务总数
|
||||||
|
*/
|
||||||
|
private Integer totalCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 平均等待时间(格式:xx天xx.xx小时)
|
||||||
|
*/
|
||||||
|
private String avgWaitTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 待复核任务列表
|
||||||
|
*/
|
||||||
|
private List<PendingReviewTaskVO> tasks;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 待复核任务
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public static class PendingReviewTaskVO {
|
||||||
|
/**
|
||||||
|
* 任务ID
|
||||||
|
*/
|
||||||
|
private Long taskId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务编号
|
||||||
|
*/
|
||||||
|
private String taskNo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 订单编号
|
||||||
|
*/
|
||||||
|
private String aufnr;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 机型编号
|
||||||
|
*/
|
||||||
|
private String modelNo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 机台编号
|
||||||
|
*/
|
||||||
|
private String no;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自检提交时间
|
||||||
|
*/
|
||||||
|
private LocalDateTime selfTestSubmitTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 等待时长(格式:xx天xx.xx小时)
|
||||||
|
*/
|
||||||
|
private String waitTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,138 @@
|
||||||
|
package com.nflg.wms.common.pojo.vo;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PQC报表VO
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class QmsPqcReportVO {
|
||||||
|
|
||||||
|
// ===== 总体统计 =====
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 总工单数
|
||||||
|
*/
|
||||||
|
private Integer totalTickets;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 较上周期增减数
|
||||||
|
*/
|
||||||
|
private Integer totalChange;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 较上周期增减率
|
||||||
|
*/
|
||||||
|
private String totalChangeRate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 已完成数(状态=3)
|
||||||
|
*/
|
||||||
|
private Integer completedTickets;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 完成率
|
||||||
|
*/
|
||||||
|
private String completionRate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 完成率较上周期增减率
|
||||||
|
*/
|
||||||
|
private String completionRateChange;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 待处理数(状态≠3)
|
||||||
|
*/
|
||||||
|
private Integer pendingTickets;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 平均耗时(格式:xx天xx小时)
|
||||||
|
*/
|
||||||
|
private String avgProcessingTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 平均耗时较上周期增减率
|
||||||
|
*/
|
||||||
|
private String avgTimeChangeRate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 一次合格率
|
||||||
|
*/
|
||||||
|
private String firstPassRate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 一次合格率较上周期增减率
|
||||||
|
*/
|
||||||
|
private String firstPassRateChange;
|
||||||
|
|
||||||
|
// ===== 人效统计 =====
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 人效统计列表
|
||||||
|
*/
|
||||||
|
private List<EfficiencyVO> efficiencyStats;
|
||||||
|
|
||||||
|
// ===== 步装合格率 =====
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 步装合格率列表
|
||||||
|
*/
|
||||||
|
private List<StepAssemblyRateVO> stepAssemblyRates;
|
||||||
|
|
||||||
|
// ===== 嵌套类 =====
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 人效统计
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public static class EfficiencyVO {
|
||||||
|
/**
|
||||||
|
* 自检人ID
|
||||||
|
*/
|
||||||
|
private Long selfTesterId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自检人姓名
|
||||||
|
*/
|
||||||
|
private String selfTesterName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 合格率
|
||||||
|
*/
|
||||||
|
private String passRate;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 步装合格率
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public static class StepAssemblyRateVO {
|
||||||
|
/**
|
||||||
|
* 步装名
|
||||||
|
*/
|
||||||
|
private String stepAssemblyName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 机型合格率列表(前5+后5)
|
||||||
|
*/
|
||||||
|
private List<ModelRateVO> modelRates;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 机型合格率
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public static class ModelRateVO {
|
||||||
|
/**
|
||||||
|
* 机型编号
|
||||||
|
*/
|
||||||
|
private String modelNo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 合格率
|
||||||
|
*/
|
||||||
|
private String passRate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue