feat(车辆生命周期): 新增维保前后轮胎对比功能并优化聚合接口

新增 CarLatestMaintenanceDTO 用于展示最近维保工单的轮胎快照对比
将维保历史合并到生命周期聚合接口,减少页面重复请求
重构轮胎花纹深度计算逻辑到 TyreLifecycleCalc 工具类
优化里程记录页面显示花纹深度字段
master
zch 2 days ago
parent bc5fb56516
commit 95c0303196

@ -13,9 +13,9 @@ import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.enums.BusinessType; import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.system.domain.BaseCar; import com.ruoyi.system.domain.BaseCar;
import com.ruoyi.system.domain.CarCheckLifecycleDTO; import com.ruoyi.system.domain.CarCheckLifecycleDTO;
import com.ruoyi.system.domain.CarLatestMaintenanceDTO;
import com.ruoyi.system.domain.CarLifecycleDTO; import com.ruoyi.system.domain.CarLifecycleDTO;
import com.ruoyi.system.domain.CarLifecycleQuery; import com.ruoyi.system.domain.CarLifecycleQuery;
import com.ruoyi.system.domain.CarMaintenanceLifecycleDTO;
import com.ruoyi.system.domain.CarMileageLifecycleDTO; import com.ruoyi.system.domain.CarMileageLifecycleDTO;
import com.ruoyi.system.domain.CarTyreInstallLifecycleDTO; import com.ruoyi.system.domain.CarTyreInstallLifecycleDTO;
import com.ruoyi.system.service.IBaseCarLifecycleService; import com.ruoyi.system.service.IBaseCarLifecycleService;
@ -205,6 +205,18 @@ public class BaseCarController extends BaseController
return AjaxResult.success(lifecycle); return AjaxResult.success(lifecycle);
} }
/**
*
*/
@RequiresPermissions("system:car:view")
@GetMapping("/lifecycle/{carNo}/latest-maintenance")
@ResponseBody
public AjaxResult latestMaintenance(@PathVariable("carNo") String carNo)
{
CarLatestMaintenanceDTO latestMaintenance = baseCarLifecycleService.selectLatestMaintenance(buildLifecycleQuery(carNo));
return AjaxResult.success(latestMaintenance);
}
/** /**
* *
*/ */
@ -218,19 +230,6 @@ public class BaseCarController extends BaseController
return getDataTable(list); return getDataTable(list);
} }
/**
*
*/
@RequiresPermissions("system:car:view")
@PostMapping("/lifecycle/{carNo}/maintenance/list")
@ResponseBody
public TableDataInfo lifecycleMaintenanceList(@PathVariable("carNo") String carNo)
{
startPage();
List<CarMaintenanceLifecycleDTO> list = baseCarLifecycleService.selectMaintenanceList(buildLifecycleQuery(carNo));
return getDataTable(list);
}
/** /**
* *
*/ */

@ -43,9 +43,9 @@
} }
.summary-counts { .summary-counts {
min-width: 360px; min-width: 180px;
display: grid; display: grid;
grid-template-columns: repeat(2, minmax(130px, 1fr)); grid-template-columns: minmax(130px, 1fr);
gap: 10px; gap: 10px;
} }
@ -136,6 +136,135 @@
word-break: break-all; word-break: break-all;
} }
.maintenance-summary {
margin-bottom: 12px;
color: #666;
font-size: 13px;
line-height: 1.8;
}
.maintenance-compare {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 12px;
}
.maintenance-side {
border: 1px solid #edf1f2;
border-radius: 8px;
padding: 12px;
background: #fff;
}
.maintenance-side-title {
margin-bottom: 10px;
color: #2f4050;
font-weight: 600;
}
.axle-row {
position: relative;
display: flex;
justify-content: center;
gap: 6px;
margin-bottom: 12px;
min-width: 0;
}
.axle-label {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
padding: 2px 7px;
border: 1px solid #e7eaec;
border-radius: 4px;
background: #fff;
color: #666;
font-size: 12px;
z-index: 2;
}
.compare-tyre-card {
position: relative;
width: 66px;
min-height: 126px;
padding: 24px 6px 8px;
border-radius: 8px 8px 16px 16px;
background: #2f4050;
color: #fff;
overflow: hidden;
box-sizing: border-box;
}
.compare-tyre-card.empty {
border: 1px dashed #c9cfd6;
background: #eef1f4;
color: #8a929a;
}
.compare-tyre-card.installed {
border: 2px solid #f8ac59;
}
.compare-tyre-card.removed {
border: 2px solid #1ab394;
}
.compare-tyre-card:before {
content: "";
position: absolute;
inset: 0;
opacity: 0.16;
background-image: repeating-linear-gradient(45deg, transparent, transparent 8px, #fff 8px, #fff 14px);
}
.compare-tyre-card.empty:before {
display: none;
}
.compare-pos {
position: absolute;
top: 0;
left: 0;
padding: 2px 6px;
border-radius: 0 0 8px 0;
background: #8a929a;
color: #fff;
font-size: 11px;
z-index: 2;
}
.compare-tyre-card.installed .compare-pos {
background: #f8ac59;
}
.compare-tyre-card.removed .compare-pos {
background: #1ab394;
}
.compare-tyre-info {
position: relative;
z-index: 1;
font-size: 11px;
line-height: 1.45;
word-break: break-all;
}
.compare-depth {
margin-top: 3px;
color: #1ab394;
font-weight: 600;
}
.clickable-title {
cursor: pointer;
}
.clickable-title:hover {
color: #1c84c6;
}
.timeline-card { .timeline-card {
padding: 18px 18px 12px; padding: 18px 18px 12px;
min-height: 520px; min-height: 520px;
@ -277,7 +406,8 @@
@media (max-width: 640px) { @media (max-width: 640px) {
.summary-counts, .summary-counts,
.life-detail-grid { .life-detail-grid,
.maintenance-compare {
grid-template-columns: 1fr; grid-template-columns: 1fr;
} }
@ -317,14 +447,21 @@
</div> </div>
<div class="col-lg-8 col-md-7 col-sm-12"> <div class="col-lg-8 col-md-7 col-sm-12">
<div class="info-card">
<div class="section-title"><i class="fa fa-wrench"></i>最近一次维保</div>
<div id="latestMaintenance">
<div class="text-center text-muted-dash" style="padding: 48px 0;">正在加载...</div>
</div>
</div>
<div class="timeline-card"> <div class="timeline-card">
<div class="timeline-header"> <div class="timeline-header">
<span class="title">生命周期时间轴</span> <span class="title clickable-title" onclick="goMaintenanceList()">保养信息</span>
<span class="mini-tip">按时间倒序展示,包含装卸、维保、质检、里程记录</span> <span class="mini-tip">按时间倒序展示维保工单</span>
</div> </div>
<ul class="life-list" id="timelineList"></ul> <ul class="life-list" id="timelineList"></ul>
<div class="text-center text-muted-dash" id="emptyTimeline" style="display: none; padding: 80px 0;"> <div class="text-center text-muted-dash" id="emptyTimeline" style="display: none; padding: 80px 0;">
暂无生命周期记录 暂无保养信息
</div> </div>
</div> </div>
</div> </div>
@ -335,9 +472,11 @@
<script th:inline="javascript"> <script th:inline="javascript">
var prefix = ctx + "tyre/car"; var prefix = ctx + "tyre/car";
var carNo = $("#carNo").val(); var carNo = $("#carNo").val();
var POSITION_ORDER = ["左前轮", "右前轮", "左外轮", "左内轮", "右内轮", "右外轮"];
$(function () { $(function () {
loadLifecycle(); loadLifecycle();
loadLatestMaintenance();
// 兜底关闭父窗口 loading避免 iframe 加载时序导致遮罩残留。 // 兜底关闭父窗口 loading避免 iframe 加载时序导致遮罩残留。
if (window.parent && window.parent !== window && window.parent.$ && window.parent.$.modal) { if (window.parent && window.parent !== window && window.parent.$ && window.parent.$.modal) {
window.parent.$.modal.closeLoading(); window.parent.$.modal.closeLoading();
@ -354,26 +493,33 @@
renderSummary(data); renderSummary(data);
renderVehicleInfo(data.car || {}); renderVehicleInfo(data.car || {});
renderMountedTyres(data.mountedTyres || []); renderMountedTyres(data.mountedTyres || []);
renderTimeline(data.recentEvents || []); // 维保历史已合并到生命周期聚合接口,页面不再额外调用独立列表接口,避免同一车牌重复查数。
renderTimeline(data.maintenanceList || []);
});
}
function loadLatestMaintenance() {
$.get(prefix + "/lifecycle/" + encodeURIComponent(carNo) + "/latest-maintenance", function (result) {
if (result.code != web_status.SUCCESS) {
$("#latestMaintenance").html('<div class="text-center text-muted-dash" style="padding: 48px 0;">维保对比加载失败</div>');
return;
}
renderLatestMaintenance(result.data || {});
}); });
} }
function renderSummary(data) { function renderSummary(data) {
var car = data.car || {}; var car = data.car || {};
var latestMileage = car.inputMileage == null ? "-" : safeText(car.inputMileage) + " km";
$("#summaryName").html("车辆:" + safeText(car.carNo)); $("#summaryName").html("车辆:" + safeText(car.carNo));
$("#summaryMeta").html([ $("#summaryMeta").html([
"所属车队:" + safeText(car.team), "所属车队:" + safeText(car.team),
"线路:" + safeText(car.line), "线路:" + safeText(car.line),
"车型:" + safeText(car.type), "车型:" + safeText(car.type),
// "当前里程:" + safeText(car.inputMileage) "当前里程:" + latestMileage
].join("<br/>")); ].join("<br/>"));
$("#summaryCounts").html([ $("#summaryCounts").html(countItem("维保工单", data.maintenanceCount));
countItem("装卸记录", data.installCount),
countItem("维保工单", data.maintenanceCount),
countItem("质检记录", data.checkCount),
countItem("里程记录", data.mileageCount)
].join(""));
} }
function renderVehicleInfo(car) { function renderVehicleInfo(car) {
@ -382,6 +528,7 @@
tableRow("车队", car.team), tableRow("车队", car.team),
tableRow("线路", car.line), tableRow("线路", car.line),
tableRow("车型", car.type), tableRow("车型", car.type),
tableRow("车辆最新里程", car.inputMileage == null ? "-" : safeText(car.inputMileage) + " km")
// tableRow("车辆主键", car.carId), // tableRow("车辆主键", car.carId),
// tableRow("部门ID", car.deptId), // tableRow("部门ID", car.deptId),
// tableRow("最近工单里程", car.inputMileage) // tableRow("最近工单里程", car.inputMileage)
@ -409,17 +556,18 @@
} }
function renderTimeline(rows) { function renderTimeline(rows) {
if (!rows.length) { var maintenanceRows = rows || [];
if (!maintenanceRows.length) {
$("#timelineList").empty(); $("#timelineList").empty();
$("#emptyTimeline").show(); $("#emptyTimeline").show();
return; return;
} }
$("#emptyTimeline").hide(); $("#emptyTimeline").hide();
var html = []; var html = [];
$.each(rows, function (index, item) { $.each(maintenanceRows, function (index, item) {
html.push('<li class="life-item">'); html.push('<li class="life-item">');
html.push('<span class="life-dot ' + eventClass(item.eventType) + '">' + eventIcon(item.eventType) + '</span>'); html.push('<span class="life-dot maintenance"></span>');
html.push('<div class="life-time">' + safeText(item.eventTime) + '</div>'); html.push('<div class="life-time">' + safeText(item.maintainDate) + '</div>');
html.push('<div class="life-title">' + safeText(eventTitle(item)) + '</div>'); html.push('<div class="life-title">' + safeText(eventTitle(item)) + '</div>');
html.push('<div class="life-body">' + eventDetails(item) + '</div>'); html.push('<div class="life-body">' + eventDetails(item) + '</div>');
html.push('</li>'); html.push('</li>');
@ -427,6 +575,92 @@
$("#timelineList").html(html.join("")); $("#timelineList").html(html.join(""));
} }
function renderLatestMaintenance(data) {
var order = data.order;
if (!order) {
$("#latestMaintenance").html('<div class="text-center text-muted-dash" style="padding: 48px 0;">暂无维保记录</div>');
return;
}
var html = [];
html.push('<div class="maintenance-summary">');
html.push(metaCell("工单号", order.orderNo));
html.push(metaCell("维保类型", orderTypeFormatter(order.typeCode)));
html.push(metaCell("状态", statusFormatter(order.status)));
html.push(metaCell("维修站点", order.factoryName));
html.push(metaCell("保养日期", order.maintainDate));
html.push(metaCell("录入里程", order.inputMileage == null ? "-" : order.inputMileage + " km"));
html.push(metaCell("上次里程", order.lastMileage == null ? "-" : order.lastMileage + " km"));
if (order.description) {
html.push('<div>补充说明:' + safeText(order.description) + '</div>');
}
html.push('</div>');
html.push('<div class="maintenance-compare">');
html.push('<div class="maintenance-side"><div class="maintenance-side-title">维保前</div>');
html.push(renderCompareTyres(data.tireDetailsBefore || []));
html.push('</div>');
html.push('<div class="maintenance-side"><div class="maintenance-side-title">维保后</div>');
html.push(renderCompareTyres(data.tireDetailsAfter || []));
html.push('</div>');
html.push('</div>');
$("#latestMaintenance").html(html.join(""));
}
function renderCompareTyres(rows) {
var data = transformTyreData(rows);
var html = [];
html.push('<div class="axle-row"><div class="axle-label">1</div>');
html.push(createTyreCard(data[0], "左前轮"));
html.push(createTyreCard(data[1], "右前轮"));
html.push('</div>');
html.push('<div class="axle-row"><div class="axle-label">2</div>');
html.push(createTyreCard(data[2], "左外轮"));
html.push(createTyreCard(data[3], "左内轮"));
html.push(createTyreCard(data[4], "右内轮"));
html.push(createTyreCard(data[5], "右外轮"));
html.push('</div>');
return html.join("");
}
function transformTyreData(rows) {
var result = new Array(6).fill(null);
$.each(rows || [], function (index, item) {
var position = displayValue(item.position || item.pos);
var posIndex = POSITION_ORDER.indexOf(position);
if (posIndex === -1) {
return true;
}
result[posIndex] = {
pos: position,
brand: displayValue(item.brand),
spec: displayValue(item.spec),
dot: displayValue(item.dot),
depth: item.depth == null || item.depth === "" ? "-" : item.depth + " mm",
status: displayValue(item.status)
};
});
return result;
}
function createTyreCard(tyre, fallbackPos) {
if (!tyre) {
return '<div class="compare-tyre-card empty"><div class="compare-pos">' + safeText(fallbackPos) + '</div></div>';
}
var statusClass = tyre.status === "installed" ? " installed" : (tyre.status === "removed" ? " removed" : "");
return [
'<div class="compare-tyre-card' + statusClass + '">',
'<div class="compare-pos">' + safeText(tyre.pos) + '</div>',
'<div class="compare-tyre-info">',
'<div>' + safeText(tyre.brand) + '</div>',
'<div>' + safeText(tyre.spec) + '</div>',
'<div>' + safeText(tyre.dot) + '</div>',
'<div class="compare-depth">' + safeText(tyre.depth) + '</div>',
'</div>',
'</div>'
].join("");
}
function eventDetails(item) { function eventDetails(item) {
var details = []; var details = [];
if (item.eventType == "INSTALL") { if (item.eventType == "INSTALL") {
@ -439,14 +673,14 @@
details.push(detail("里程", item.mileage)); details.push(detail("里程", item.mileage));
details.push(detail("花纹深度", item.patternDepth)); details.push(detail("花纹深度", item.patternDepth));
details.push(detail("动作", installTypeFormatter(item.status))); details.push(detail("动作", installTypeFormatter(item.status)));
} else if (item.eventType == "MAINTENANCE") { } else if (item.eventType == "MAINTENANCE" || item.orderNo) {
// 工单节点展示工单号和站点,排查车辆维保历史时不用再跳转列表反查。 // 工单节点展示工单号和站点,排查车辆维保历史时不用再跳转列表反查。
details.push(detail("工单号", item.orderNo)); details.push(detail("工单号", item.orderNo));
details.push(detail("维保类型", orderTypeFormatter(item.typeCode))); details.push(detail("维保类型", orderTypeFormatter(item.typeCode)));
details.push(detail("状态", statusFormatter(item.status))); details.push(detail("状态", statusFormatter(item.status)));
details.push(detail("维修站点", item.factoryName)); details.push(detail("维修站点", item.factoryName));
details.push(detail("录入里程", item.inputMileage)); details.push(detail("录入里程", item.inputMileage == null ? "-" : item.inputMileage + " km"));
details.push(detail("上次里程", item.lastMileage)); details.push(detail("上次里程", item.lastMileage == null ? "-" : item.lastMileage + " km"));
details.push(detail("维保日期", item.maintainDate)); details.push(detail("维保日期", item.maintainDate));
} else if (item.eventType == "CHECK") { } else if (item.eventType == "CHECK") {
// 质检节点通过曾装车轮胎间接归属车辆,页面上明确展示轮胎身份,避免误以为质检表直接有车牌。 // 质检节点通过曾装车轮胎间接归属车辆,页面上明确展示轮胎身份,避免误以为质检表直接有车牌。
@ -483,6 +717,10 @@
if (item.eventType == "MAINTENANCE") { if (item.eventType == "MAINTENANCE") {
return orderTypeFormatter(item.typeCode) + "" + displayValue(item.orderNo); return orderTypeFormatter(item.typeCode) + "" + displayValue(item.orderNo);
} }
if (item.orderNo) {
// 合并接口后时间线直接接收维保工单DTO通过工单号识别维保节点。
return orderTypeFormatter(item.typeCode) + "" + displayValue(item.orderNo);
}
if (item.eventType == "CHECK") { if (item.eventType == "CHECK") {
return "轮胎质检:" + displayValue(item.tyreNo || item.selfNo || item.tyreRfid); return "轮胎质检:" + displayValue(item.tyreNo || item.selfNo || item.tyreRfid);
} }
@ -522,6 +760,10 @@
return '<div class="life-detail"><span class="label-title">' + safeText(name) + '</span>' + safeText(value) + '</div>'; return '<div class="life-detail"><span class="label-title">' + safeText(name) + '</span>' + safeText(value) + '</div>';
} }
function metaCell(name, value) {
return '<div>' + safeText(name) + '' + safeText(value) + '</div>';
}
function tableRow(name, value) { function tableRow(name, value) {
return '<tr><th>' + safeText(name) + '</th><td>' + safeText(value) + '</td></tr>'; return '<tr><th>' + safeText(name) + '</th><td>' + safeText(value) + '</td></tr>';
} }
@ -589,6 +831,11 @@
} }
return displayValue(value); return displayValue(value);
} }
function goMaintenanceList() {
// ctx 由 include.html 注入且自带尾斜杠,按项目既有写法直接拼接相对路径。
window.open(ctx + "tyre/order", "_blank");
}
</script> </script>
</body> </body>
</html> </html>

@ -0,0 +1,57 @@
package com.ruoyi.system.domain;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* DTO
* <p>
* /
* Map
* </p>
*/
public class CarLatestMaintenanceDTO implements Serializable
{
private static final long serialVersionUID = 1L;
/** 最近一次维保工单概要;车辆无维保记录时为空。 */
private CarMaintenanceLifecycleDTO order;
/** 维保前轮胎布局,字段同 BizOrderTireDetailMapper.selectBaseTrieInstall / selectBizOrderTireDetail。 */
private List<Map> tireDetailsBefore = new ArrayList<>();
/** 维保后轮胎布局,字段同 BizOrderTireDetailMapper.selectBizOrderTireDetail。 */
private List<Map> tireDetailsAfter = new ArrayList<>();
public CarMaintenanceLifecycleDTO getOrder()
{
return order;
}
public void setOrder(CarMaintenanceLifecycleDTO order)
{
this.order = order;
}
public List<Map> getTireDetailsBefore()
{
return tireDetailsBefore;
}
public void setTireDetailsBefore(List<Map> tireDetailsBefore)
{
this.tireDetailsBefore = tireDetailsBefore;
}
public List<Map> getTireDetailsAfter()
{
return tireDetailsAfter;
}
public void setTireDetailsAfter(List<Map> tireDetailsAfter)
{
this.tireDetailsAfter = tireDetailsAfter;
}
}

@ -8,8 +8,8 @@ import java.util.List;
* DTO * DTO
* <p> * <p>
* *
* 线 * 线
* *
* </p> * </p>
*/ */
public class CarLifecycleDTO implements Serializable public class CarLifecycleDTO implements Serializable
@ -25,6 +25,9 @@ public class CarLifecycleDTO implements Serializable
/** 最近事件时间线,聚合装卸与维保两类关键事件,按时间倒序排列,用于快速定位最新动作。 */ /** 最近事件时间线,聚合装卸与维保两类关键事件,按时间倒序排列,用于快速定位最新动作。 */
private List<CarLifecycleEventDTO> recentEvents = new ArrayList<>(); private List<CarLifecycleEventDTO> recentEvents = new ArrayList<>();
/** 车辆维保工单历史,随生命周期聚合接口返回,避免页面重复调用单独的维保列表接口。 */
private List<CarMaintenanceLifecycleDTO> maintenanceList = new ArrayList<>();
/** 该车辆历史轮胎装卸记录总条数,用于前端分页组件初始化总页数。 */ /** 该车辆历史轮胎装卸记录总条数,用于前端分页组件初始化总页数。 */
private Integer installCount; private Integer installCount;
@ -67,6 +70,16 @@ public class CarLifecycleDTO implements Serializable
this.recentEvents = recentEvents; this.recentEvents = recentEvents;
} }
public List<CarMaintenanceLifecycleDTO> getMaintenanceList()
{
return maintenanceList;
}
public void setMaintenanceList(List<CarMaintenanceLifecycleDTO> maintenanceList)
{
this.maintenanceList = maintenanceList;
}
public Integer getInstallCount() public Integer getInstallCount()
{ {
return installCount; return installCount;

@ -34,6 +34,9 @@ public class RecordWarehousing extends BaseEntity
private Long deptId; private Long deptId;
/** 入库录入人所属的修理厂/车队名称,用于详情抽屉回溯入库场站。 */
private String deptName;
public String getTyreModel() { public String getTyreModel() {
return tyreModel; return tyreModel;
} }
@ -50,6 +53,14 @@ public class RecordWarehousing extends BaseEntity
this.deptId = deptId; this.deptId = deptId;
} }
public String getDeptName() {
return deptName;
}
public void setDeptName(String deptName) {
this.deptName = deptName;
}
public String getTyreBrand() { public String getTyreBrand() {
return tyreBrand; return tyreBrand;
} }
@ -103,6 +114,8 @@ public class RecordWarehousing extends BaseEntity
.append("tyreRfid", getTyreRfid()) .append("tyreRfid", getTyreRfid())
.append("type", getType()) .append("type", getType())
.append("deptName", getDeptName())
.append("createBy", getCreateBy()) .append("createBy", getCreateBy())

@ -2,9 +2,9 @@ package com.ruoyi.system.service;
import java.util.List; import java.util.List;
import com.ruoyi.system.domain.CarCheckLifecycleDTO; import com.ruoyi.system.domain.CarCheckLifecycleDTO;
import com.ruoyi.system.domain.CarLatestMaintenanceDTO;
import com.ruoyi.system.domain.CarLifecycleDTO; import com.ruoyi.system.domain.CarLifecycleDTO;
import com.ruoyi.system.domain.CarLifecycleQuery; import com.ruoyi.system.domain.CarLifecycleQuery;
import com.ruoyi.system.domain.CarMaintenanceLifecycleDTO;
import com.ruoyi.system.domain.CarMileageLifecycleDTO; import com.ruoyi.system.domain.CarMileageLifecycleDTO;
import com.ruoyi.system.domain.CarTyreInstallLifecycleDTO; import com.ruoyi.system.domain.CarTyreInstallLifecycleDTO;
@ -29,14 +29,6 @@ public interface IBaseCarLifecycleService
*/ */
public List<CarTyreInstallLifecycleDTO> selectInstallList(CarLifecycleQuery query); public List<CarTyreInstallLifecycleDTO> selectInstallList(CarLifecycleQuery query);
/**
*
*
* @param query
* @return
*/
public List<CarMaintenanceLifecycleDTO> selectMaintenanceList(CarLifecycleQuery query);
/** /**
* *
* *
@ -52,4 +44,12 @@ public interface IBaseCarLifecycleService
* @return * @return
*/ */
public List<CarMileageLifecycleDTO> selectMileageList(CarLifecycleQuery query); public List<CarMileageLifecycleDTO> selectMileageList(CarLifecycleQuery query);
/**
*
*
* @param query
* @return DTO
*/
public CarLatestMaintenanceDTO selectLatestMaintenance(CarLifecycleQuery query);
} }

@ -1,10 +1,15 @@
package com.ruoyi.system.service.impl; package com.ruoyi.system.service.impl;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map;
import com.ruoyi.common.annotation.DataScope; import com.ruoyi.common.annotation.DataScope;
import com.ruoyi.common.exception.ServiceException; import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.system.domain.BizMaintenanceOrder;
import com.ruoyi.system.domain.BizOrderTireDetail;
import com.ruoyi.system.domain.CarCheckLifecycleDTO; import com.ruoyi.system.domain.CarCheckLifecycleDTO;
import com.ruoyi.system.domain.CarLatestMaintenanceDTO;
import com.ruoyi.system.domain.CarLifecycleDTO; import com.ruoyi.system.domain.CarLifecycleDTO;
import com.ruoyi.system.domain.CarLifecycleQuery; import com.ruoyi.system.domain.CarLifecycleQuery;
import com.ruoyi.system.domain.CarLifecycleSummaryDTO; import com.ruoyi.system.domain.CarLifecycleSummaryDTO;
@ -12,6 +17,8 @@ import com.ruoyi.system.domain.CarMaintenanceLifecycleDTO;
import com.ruoyi.system.domain.CarMileageLifecycleDTO; import com.ruoyi.system.domain.CarMileageLifecycleDTO;
import com.ruoyi.system.domain.CarTyreInstallLifecycleDTO; import com.ruoyi.system.domain.CarTyreInstallLifecycleDTO;
import com.ruoyi.system.mapper.BaseCarLifecycleMapper; import com.ruoyi.system.mapper.BaseCarLifecycleMapper;
import com.ruoyi.system.mapper.BizMaintenanceOrderMapper;
import com.ruoyi.system.mapper.BizOrderTireDetailMapper;
import com.ruoyi.system.service.IBaseCarLifecycleService; import com.ruoyi.system.service.IBaseCarLifecycleService;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -30,6 +37,12 @@ public class BaseCarLifecycleServiceImpl implements IBaseCarLifecycleService
@Autowired @Autowired
private BaseCarLifecycleMapper baseCarLifecycleMapper; private BaseCarLifecycleMapper baseCarLifecycleMapper;
@Autowired
private BizMaintenanceOrderMapper bizMaintenanceOrderMapper;
@Autowired
private BizOrderTireDetailMapper bizOrderTireDetailMapper;
/** /**
* *
* <p> * <p>
@ -37,8 +50,9 @@ public class BaseCarLifecycleServiceImpl implements IBaseCarLifecycleService
* - 线 * - 线
* - mountedTyres * - mountedTyres
* - 线recentEvents * - 线recentEvents
* - maintenanceList
* - installCount / maintenanceCount / checkCount / mileageCount * - installCount / maintenanceCount / checkCount / mileageCount
* *
* </p> * </p>
* *
* @param query carNo * @param query carNo
@ -54,6 +68,7 @@ public class BaseCarLifecycleServiceImpl implements IBaseCarLifecycleService
dto.setCar(car); dto.setCar(car);
dto.setMountedTyres(baseCarLifecycleMapper.selectMountedTyres(query)); dto.setMountedTyres(baseCarLifecycleMapper.selectMountedTyres(query));
dto.setRecentEvents(baseCarLifecycleMapper.selectRecentEvents(query)); dto.setRecentEvents(baseCarLifecycleMapper.selectRecentEvents(query));
dto.setMaintenanceList(baseCarLifecycleMapper.selectMaintenanceList(query));
dto.setInstallCount(baseCarLifecycleMapper.countInstallRecords(query)); dto.setInstallCount(baseCarLifecycleMapper.countInstallRecords(query));
dto.setMaintenanceCount(baseCarLifecycleMapper.countMaintenanceOrders(query)); dto.setMaintenanceCount(baseCarLifecycleMapper.countMaintenanceOrders(query));
dto.setCheckCount(baseCarLifecycleMapper.countCheckRecords(query)); dto.setCheckCount(baseCarLifecycleMapper.countCheckRecords(query));
@ -81,23 +96,50 @@ public class BaseCarLifecycleServiceImpl implements IBaseCarLifecycleService
} }
/** /**
* / *
* <p> * <p>
* {@code biz_maintenance_order.plate_number = carNo} * buildAuthorizedCar SQL
*
* maintain_date
* </p> * </p>
*
* @param query buildAuthorizedCar carId
* @return
* @throws ServiceException 访
*/ */
@Override @Override
@DataScope(deptAlias = "d", userAlias = "u") @DataScope(deptAlias = "d", userAlias = "u")
public List<CarMaintenanceLifecycleDTO> selectMaintenanceList(CarLifecycleQuery query) public CarLatestMaintenanceDTO selectLatestMaintenance(CarLifecycleQuery query)
{ {
buildAuthorizedCar(query); buildAuthorizedCar(query);
return baseCarLifecycleMapper.selectMaintenanceList(query); CarLatestMaintenanceDTO dto = new CarLatestMaintenanceDTO();
List<CarMaintenanceLifecycleDTO> maintenanceList = baseCarLifecycleMapper.selectMaintenanceList(query);
if (maintenanceList == null || maintenanceList.isEmpty())
{
return dto;
}
CarMaintenanceLifecycleDTO latest = maintenanceList.get(0);
dto.setOrder(latest);
if (latest.getOrderId() != null)
{
BizOrderTireDetail afterQuery = new BizOrderTireDetail();
afterQuery.setOrderId(latest.getOrderId());
dto.setTireDetailsAfter(emptyIfNull(bizOrderTireDetailMapper.selectBizOrderTireDetail(afterQuery)));
}
if (latest.getOrderId() != null && !StringUtils.isBlank(latest.getPlateNumber()))
{
BizMaintenanceOrder probe = new BizMaintenanceOrder();
probe.setOrderId(latest.getOrderId());
// 上一条工单SQL同时依赖 orderId 与 plateNumber缺车牌会导致无法命中上一条记录。
probe.setPlateNumber(latest.getPlateNumber());
BizMaintenanceOrder previousOrder = bizMaintenanceOrderMapper.selectBizMaintenanceOrderByOrderIdBefore(probe);
if (previousOrder != null && previousOrder.getOrderId() != null)
{
BizOrderTireDetail beforeQuery = new BizOrderTireDetail();
beforeQuery.setOrderId(previousOrder.getOrderId());
dto.setTireDetailsBefore(emptyIfNull(bizOrderTireDetailMapper.selectBizOrderTireDetail(beforeQuery)));
return dto;
}
}
dto.setTireDetailsBefore(emptyIfNull(bizOrderTireDetailMapper.selectBaseTrieInstall(latest.getPlateNumber())));
return dto;
} }
/** /**
@ -193,4 +235,9 @@ public class BaseCarLifecycleServiceImpl implements IBaseCarLifecycleService
} }
query.setCarNo(carNo); query.setCarNo(carNo);
} }
private List<Map> emptyIfNull(List<Map> list)
{
return list == null ? new ArrayList<>() : list;
}
} }

@ -0,0 +1,191 @@
package com.ruoyi.system.util;
import com.ruoyi.system.domain.RecordTyreInstall;
import com.ruoyi.system.domain.RecordTyreMileage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
*
* <p>
* WebPDA/
* </p>
*/
public final class TyreLifecycleCalc
{
private static final Logger log = LoggerFactory.getLogger(TyreLifecycleCalc.class);
private TyreLifecycleCalc()
{
}
/**
*
* <p>
* id
* </p>
*/
public static String currentPatternDepth(List<RecordTyreInstall> installRecords)
{
if (installRecords == null || installRecords.isEmpty())
{
return null;
}
RecordTyreInstall latest = null;
for (RecordTyreInstall item : installRecords)
{
if (item == null || isBlank(item.getPatternDepth()))
{
continue;
}
if (latest == null || after(item, latest))
{
latest = item;
}
}
return latest == null ? null : latest.getPatternDepth().trim();
}
/**
*
* <p>
* record_tyre_mileage.pattern_depth mileage
* mileage {@link #sumMileage(List)}
* </p>
*/
public static Map<Long, String> computeSegmentWears(BigDecimal initialDepth,
List<RecordTyreMileage> mileageList)
{
Map<Long, String> wearMap = new HashMap<>();
if (mileageList == null || mileageList.isEmpty())
{
return wearMap;
}
List<RecordTyreMileage> sortedList = new ArrayList<>();
for (RecordTyreMileage item : mileageList)
{
if (item != null)
{
sortedList.add(item);
}
}
sortedList.sort(Comparator
.comparing(RecordTyreMileage::getStartTime, Comparator.nullsLast(Comparator.naturalOrder()))
.thenComparing(RecordTyreMileage::getId, Comparator.nullsLast(Comparator.naturalOrder())));
BigDecimal previousDepth = initialDepth;
for (RecordTyreMileage item : sortedList)
{
BigDecimal currentDepth = parseDepth(item.getPatternDepth());
String wearDepth = null;
if (previousDepth != null && currentDepth != null)
{
wearDepth = formatDecimal(previousDepth.subtract(currentDepth));
}
if (item.getId() != null)
{
wearMap.put(item.getId(), wearDepth);
}
if (currentDepth != null)
{
// 当前段脏数据不更新 prev避免一条坏花纹深度污染后续所有段。
previousDepth = currentDepth;
}
}
return wearMap;
}
/**
* pattern_depth
* <p>
* base_tyre / record_tyre_install / record_tyre_mileage pattern_depth varchar
* mileage Long
* </p>
*/
public static BigDecimal parseDepth(String value)
{
if (isBlank(value))
{
return null;
}
String trimmed = value.trim();
try
{
return new BigDecimal(trimmed);
}
catch (NumberFormatException ex)
{
log.warn("轮胎花纹深度无法解析,已按空值处理:{}", trimmed);
return null;
}
}
/**
*
* <p>
* RecordTyreMileage.mileage Java Long km 使
* </p>
*/
public static BigDecimal sumMileage(List<RecordTyreMileage> mileageList)
{
if (mileageList == null || mileageList.isEmpty())
{
return null;
}
BigDecimal total = BigDecimal.ZERO;
boolean hasMileage = false;
for (RecordTyreMileage item : mileageList)
{
if (item != null && item.getMileage() != null)
{
total = total.add(BigDecimal.valueOf(item.getMileage()));
hasMileage = true;
}
}
return hasMileage ? total : null;
}
private static boolean after(RecordTyreInstall left, RecordTyreInstall right)
{
if (left.getCreateTime() != null && right.getCreateTime() != null)
{
int compare = left.getCreateTime().compareTo(right.getCreateTime());
if (compare != 0)
{
return compare > 0;
}
}
else if (left.getCreateTime() != null)
{
return true;
}
else if (right.getCreateTime() != null)
{
return false;
}
if (left.getId() != null && right.getId() != null)
{
return left.getId().compareTo(right.getId()) > 0;
}
return left.getId() != null && right.getId() == null;
}
private static String formatDecimal(BigDecimal value)
{
return value == null ? null : value.stripTrailingZeros().toPlainString();
}
private static boolean isBlank(String value)
{
return value == null || value.trim().isEmpty();
}
}

@ -17,14 +17,15 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<result property="remark" column="remark" /> <result property="remark" column="remark" />
<result property="tyreBrand" column="tyre_brand" /> <result property="tyreBrand" column="tyre_brand" />
<result property="tyreNo" column="tyre_no" /> <result property="tyreNo" column="tyre_no" />
<result property="patternDepth" column="pattern_depth" />
</resultMap> </resultMap>
<sql id="selectRecordTyreMileageVo"> <sql id="selectRecordTyreMileageVo">
select id, tyre_rfid, start_time, end_time, mileage, create_by, create_time, update_by, update_time, remark from record_tyre_mileage select id, tyre_rfid, start_time, end_time, mileage, pattern_depth, create_by, create_time, update_by, update_time, remark from record_tyre_mileage
</sql> </sql>
<select id="selectRecordTyreMileageList" parameterType="RecordTyreMileage" resultMap="RecordTyreMileageResult"> <select id="selectRecordTyreMileageList" parameterType="RecordTyreMileage" resultMap="RecordTyreMileageResult">
select rtm.id, rtm.tyre_rfid, rtm.start_time, rtm.end_time, rtm.mileage, select rtm.id, rtm.tyre_rfid, rtm.start_time, rtm.end_time, rtm.mileage, rtm.pattern_depth,
rtm.create_by, rtm.create_time, rtm.update_by, rtm.update_time, rtm.remark,bt.tyre_brand,bt.tyre_no rtm.create_by, rtm.create_time, rtm.update_by, rtm.update_time, rtm.remark,bt.tyre_brand,bt.tyre_no
from record_tyre_mileage rtm from record_tyre_mileage rtm
LEFT JOIN base_tyre bt ON rtm.tyre_rfid = bt.tyre_epc LEFT JOIN base_tyre bt ON rtm.tyre_rfid = bt.tyre_epc
@ -84,6 +85,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="createTime != null ">create_time = #{createTime},</if> <if test="createTime != null ">create_time = #{createTime},</if>
<if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if> <if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
<if test="updateTime != null ">update_time = #{updateTime},</if> <if test="updateTime != null ">update_time = #{updateTime},</if>
<if test="patternDepth != null ">pattern_depth = #{patternDepth},</if>
<if test="remark != null and remark != ''">remark = #{remark},</if> <if test="remark != null and remark != ''">remark = #{remark},</if>
</trim> </trim>
where id = #{id} where id = #{id}
@ -100,4 +102,4 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</foreach> </foreach>
</delete> </delete>
</mapper> </mapper>

@ -16,6 +16,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<result property="tyreBrand" column="tyre_brand" /> <result property="tyreBrand" column="tyre_brand" />
<result property="tyreNo" column="tyre_no" /> <result property="tyreNo" column="tyre_no" />
<result property="tyreModel" column="tyre_model" /> <result property="tyreModel" column="tyre_model" />
<result property="deptName" column="dept_name" />
</resultMap> </resultMap>
@ -25,10 +26,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<select id="selectRecordWarehousingList" parameterType="RecordWarehousing" resultMap="RecordWarehousingResult"> <select id="selectRecordWarehousingList" parameterType="RecordWarehousing" resultMap="RecordWarehousingResult">
select rw.id, rw.tyre_rfid, rw.type, su.user_name as create_by, rw.create_time, rw.update_by, rw.update_time, rw.remark, select rw.id, rw.tyre_rfid, rw.type, su.user_name as create_by, rw.create_time, rw.update_by, rw.update_time, rw.remark,
bt.tyre_brand,bt.tyre_no,bt.tyre_model bt.tyre_brand,bt.tyre_no,bt.tyre_model, sd.dept_name
from record_warehousing rw from record_warehousing rw
LEFT JOIN base_tyre bt ON rw.tyre_rfid = bt.tyre_epc LEFT JOIN base_tyre bt ON rw.tyre_rfid = bt.tyre_epc
left join sys_user su ON su.login_name = rw.create_by left join sys_user su ON su.login_name = rw.create_by
left join sys_dept sd ON sd.dept_id = su.dept_id
where rw.id is not null where rw.id is not null
<if test="tyreRfid != null and tyreRfid != ''"> and rw.tyre_rfid = #{tyreRfid}</if> <if test="tyreRfid != null and tyreRfid != ''"> and rw.tyre_rfid = #{tyreRfid}</if>
<if test="type != null and type != ''"> and rw.type = #{type}</if> <if test="type != null and type != ''"> and rw.type = #{type}</if>
@ -132,4 +134,4 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</foreach> </foreach>
</delete> </delete>
</mapper> </mapper>

Loading…
Cancel
Save