From 8314e1b94fd39276b65e7e6047b55c91e5df9bbb Mon Sep 17 00:00:00 2001 From: zch Date: Mon, 11 May 2026 16:18:07 +0800 Subject: [PATCH] =?UTF-8?q?feat(tyre):=20=E6=96=B0=E5=A2=9E=E8=BD=AE?= =?UTF-8?q?=E8=83=8E=E6=8A=A5=E5=BA=9F=E7=AE=A1=E7=90=86=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=EF=BC=8C=E4=BC=98=E5=8C=96=E7=BB=B4=E4=BF=9D=E5=B7=A5=E5=8D=95?= =?UTF-8?q?=E4=B8=8E=E7=94=9F=E5=91=BD=E5=91=A8=E6=9C=9F=E5=B1=95=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 新增轮胎报废查询页面与控制器,仅读取PDA落库的报废事实 2. 扩展维保类型枚举,新增抢碎修、小修、轮胎修补/报废类型 3. 重构车辆生命周期里程计算逻辑,修复历史里程显示异常 4. 优化工单详情页渲染,支持空工单空态提示,修复轮位展示逻辑 5. 新增维保前后轮胎快照查询接口,优化工单校验逻辑 6. 完善轮胎生命周期页面,展示报废记录与关联照片 7. 修复订单查询条件与映射逻辑,修正部分页面API路径 --- .../tyre/BaseTyreScrapController.java | 117 ++++++++ .../tyre/BizMaintenanceOrderController.java | 86 ++++-- .../resources/templates/tyre/car/add.html | 2 +- .../resources/templates/tyre/car/edit.html | 2 +- .../templates/tyre/car/lifecycle.html | 6 + .../resources/templates/tyre/order/edit.html | 91 +++--- .../resources/templates/tyre/order/order.html | 4 +- .../resources/templates/tyre/scrap/scrap.html | 283 ++++++++++++++++++ .../templates/tyre/tyre/typreDetill2.html | 97 +++++- .../system/domain/BizOrderTireDetail.java | 214 +++++++++++++ .../mapper/BizOrderTireDetailMapper.java | 45 +++ .../service/IBizOrderTireDetailService.java | 40 +++ .../impl/BaseCarLifecycleServiceImpl.java | 48 ++- .../service/impl/BaseTyreServiceImpl.java | 51 +++- .../impl/BizOrderTireDetailServiceImpl.java | 25 ++ .../mapper/tyre/BaseCarLifecycleMapper.xml | 31 +- .../mapper/tyre/BizMaintenanceOrderMapper.xml | 41 ++- .../mapper/tyre/BizOrderTireDetailMapper.xml | 214 ++++++++++++- 18 files changed, 1300 insertions(+), 97 deletions(-) create mode 100644 ruoyi-admin/src/main/java/com/ruoyi/web/controller/tyre/BaseTyreScrapController.java create mode 100644 ruoyi-admin/src/main/resources/templates/tyre/scrap/scrap.html diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tyre/BaseTyreScrapController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tyre/BaseTyreScrapController.java new file mode 100644 index 00000000..51134325 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tyre/BaseTyreScrapController.java @@ -0,0 +1,117 @@ +package com.ruoyi.web.controller.tyre; + +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.system.domain.BizOrderTireDetail; +import com.ruoyi.system.domain.SysAttachment; +import com.ruoyi.system.service.IBizOrderTireDetailService; +import com.ruoyi.system.service.ISysAttachmentService; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; + +import java.util.Calendar; +import java.util.Date; +import java.util.List; +import java.util.Map; + +/** + * 轮胎报废查询Controller。 + * + * 当前 Web 端只读取 PDA 已落库的报废事实,不提供新增/修改能力,避免越过 PDA 写入职责边界。 + */ +@Controller +@RequestMapping("/tyre/scrap") +public class BaseTyreScrapController extends BaseController +{ + private String prefix = "tyre/scrap"; + + @Autowired + private IBizOrderTireDetailService bizOrderTireDetailService; + + @Autowired + private ISysAttachmentService sysAttachmentService; + + @RequiresPermissions("tyre:scrap:view") + @GetMapping() + public String scrap() + { + return prefix + "/scrap"; + } + + /** + * 查询轮胎报废列表。 + */ + @RequiresPermissions("tyre:scrap:list") + @PostMapping("/list") + @ResponseBody + public TableDataInfo list(BizOrderTireDetail bizOrderTireDetail) + { + normalizeScrapTimeRange(bizOrderTireDetail); + startPage(); + List list = bizOrderTireDetailService.selectScrapList(bizOrderTireDetail); + return getDataTable(list); + } + + /** + * 导出轮胎报废列表。 + */ + @RequiresPermissions("tyre:scrap:export") + @Log(title = "轮胎报废查询", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @ResponseBody + public AjaxResult export(BizOrderTireDetail bizOrderTireDetail) + { + normalizeScrapTimeRange(bizOrderTireDetail); + List list = bizOrderTireDetailService.selectScrapList(bizOrderTireDetail); + ExcelUtil util = new ExcelUtil<>(BizOrderTireDetail.class); + return util.exportExcel(list, "轮胎报废数据"); + } + + /** + * 查询报废工单照片。 + */ + @RequiresPermissions("tyre:scrap:list") + @GetMapping("/photos/{orderId}") + @ResponseBody + public AjaxResult photos(@PathVariable("orderId") Long orderId) + { + List attachments = sysAttachmentService.selectAttachmentsByOrderId(orderId); + return AjaxResult.success(attachments); + } + + private void normalizeScrapTimeRange(BizOrderTireDetail bizOrderTireDetail) + { + if (bizOrderTireDetail == null) + { + return; + } + + Map params = bizOrderTireDetail.getParams(); + Date beginTime = DateUtils.parseDate(params.get("beginTime")); + if (beginTime != null) + { + params.put("beginTimeDate", beginTime); + } + + Date endTime = DateUtils.parseDate(params.get("endTime")); + if (endTime != null) + { + Calendar calendar = Calendar.getInstance(); + calendar.setTime(endTime); + calendar.add(Calendar.DATE, 1); + // 使用半开区间规避 MySQL/SQL Server 日期函数差异,并覆盖结束日期当天 23:59:59 的数据。 + params.put("endTimeExclusive", calendar.getTime()); + } + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tyre/BizMaintenanceOrderController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tyre/BizMaintenanceOrderController.java index 0e0975c7..cb422650 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tyre/BizMaintenanceOrderController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tyre/BizMaintenanceOrderController.java @@ -25,7 +25,6 @@ import org.springframework.web.multipart.MultipartFile; import java.io.IOException; import java.math.BigDecimal; import java.time.LocalDate; -import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -132,23 +131,21 @@ public class BizMaintenanceOrderController extends BaseController { /** * 维保工单详情 + *

+ * 维保前 / 维保后以当前工单为锚点,但不再只看 biz_order_tire_detail: + *

    + *
  • 维保前 = 当前明细对应轮胎在明细写入前的最近里程快照
  • + *
  • 维保后 = 当前明细 + 工单时间窗内 PDA 实时装胎记录
  • + *
+ * status 字段已被 mapper 映射为 'removed' / 'installed' / 'normal',与前端 CSS 状态类一一对应。 */ @RequiresPermissions("system:order:edit") @GetMapping("/edit/{orderId}") public String edit(@PathVariable("orderId") Long orderId, ModelMap mmap) { BizMaintenanceOrder bizMaintenanceOrder = bizMaintenanceOrderService.selectBizMaintenanceOrderByOrderId(orderId); mmap.put("bizMaintenanceOrder", bizMaintenanceOrder); - // 查询同车牌上一张已完成维保工单;“维保前”按业务口径取上一单的维保后结果,不能再混入当前车辆实时装胎状态。 - BizMaintenanceOrder bizMaintenanceOrderBefore = bizMaintenanceOrderService.selectBizMaintenanceOrderByOrderIdBefore(bizMaintenanceOrder); - BizOrderTireDetail bizOrderTireDetail = new BizOrderTireDetail(); - List bizOrderTireDetailsBefore = new ArrayList<>(); - if (bizMaintenanceOrderBefore != null) { - bizOrderTireDetail.setOrderId(bizMaintenanceOrderBefore.getOrderId()); - bizOrderTireDetailsBefore = bizOrderTireDetailService.selectBizOrderTireDetail(bizOrderTireDetail); - } - //查询此车辆的维保数据 - bizOrderTireDetail.setOrderId(orderId); - List bizOrderTireDetails = bizOrderTireDetailService.selectBizOrderTireDetail(bizOrderTireDetail); + List bizOrderTireDetailsBefore = bizOrderTireDetailService.selectBeforeSnapshot(orderId); + List bizOrderTireDetails = bizOrderTireDetailService.selectAfterSnapshot(orderId); mmap.put("bizOrderTireDetailsBefore", bizOrderTireDetailsBefore); mmap.put("bizOrderTireDetails", bizOrderTireDetails); return prefix + "/edit"; @@ -230,8 +227,24 @@ public class BizMaintenanceOrderController extends BaseController { BizMaintenanceOrder order = bizMaintenanceOrder.getOrder(); + // 工单类型权威依据:typeCode="7" 表示轮胎报废工单(NewHomePageActivity 映射), + // 报废页面不采集里程/花纹深度,相关校验需要按工单类型分流,不能依赖 detail.dataType + // 兜底(旧版 APK 可能漏字段)。 + boolean isScrapOrder = "7".equals(order.getTypeCode()); + + // 兜住真正的"空提交":若 PDA 已实时完成装卸胎,允许继续完成工单;若完全没有轮胎动作,则在更新主单前拦截。 + // 这样既避免历史脏单(例如 order_id=107),也避免用户装胎返回后被误提示“请至少处理一条轮胎”。 + List tireDetails = bizMaintenanceOrder.getTireDetails(); + boolean hasSubmittedTireDetails = tireDetails != null && !tireDetails.isEmpty(); + if (!hasSubmittedTireDetails && (isScrapOrder || !hasRealtimeTyreRecord(order))) { + return error(isScrapOrder + ? "请扫描轮胎并选择报废位置后再提交" + : "请至少处理一条轮胎后再完成保养工单"); + } + + // 报废工单整体跳过里程校验;其它工单仍要求 inputMileage>0。 BigDecimal inputMileage = order.getInputMileage(); - if (inputMileage == null|| inputMileage.compareTo(BigDecimal.ZERO) == 0) { + if (!isScrapOrder && (inputMileage == null || inputMileage.compareTo(BigDecimal.ZERO) == 0)) { return error("请输入里程"); } @@ -243,26 +256,32 @@ public class BizMaintenanceOrderController extends BaseController { // 4. 核心逻辑:只有更新成功(影响行数 > 0)才继续执行 if (n > 0) { // 保存详细表 - if (bizMaintenanceOrder.getTireDetails() != null && bizMaintenanceOrder.getTireDetails().size() > 0) { - for (BizOrderTireDetail bizOrderTireDetail : bizMaintenanceOrder.getTireDetails()) { + if (hasSubmittedTireDetails) { + for (BizOrderTireDetail bizOrderTireDetail : tireDetails) { bizOrderTireDetail.setCreateTime(DateUtils.getNowDate()); bizOrderTireDetail.setOrderId(orderId); BaseTyre baseTyre = baseTyreService.selectBaseTyreById(bizOrderTireDetail.getTireId()); - recordTyreMileageService.funInsertTyreMileage( - "保养", - order.getInputMileage().longValue(), - bizOrderTireDetail.getTreadDepth().toString(), - baseTyre.getTyreEpc(), - order.getPlateNumber(), null); + // 报废工单整体不写里程历史:报废页面不采集里程/花纹深度,强行写入会 NPE, + // 也会产生不属于报废动作的"保养"里程记录。以工单 typeCode 为主,detail + // 的 dataType 仅作兜底(避免旧版 APK 漏字段时误写)。 + boolean isScrapDetail = isScrapOrder || "报废".equals(bizOrderTireDetail.getDataType()); + if (!isScrapDetail) { + recordTyreMileageService.funInsertTyreMileage( + "保养", + order.getInputMileage().longValue(), + bizOrderTireDetail.getTreadDepth().toString(), + baseTyre.getTyreEpc(), + order.getPlateNumber(), null); + } // 这里如果抛出异常,整个事务会回滚 bizOrderTireDetailService.insertBizOrderTireDetail(bizOrderTireDetail); } } - if (files != null && files.size() > 0) { + if (files != null && !files.isEmpty()) { //保存图片 for (int i = 0; i < files.size(); i++) { try { @@ -338,4 +357,27 @@ public class BizMaintenanceOrderController extends BaseController { bizMaintenanceOrder.getParams().put("beginTime", DateUtils.toDate(today)); bizMaintenanceOrder.getParams().put("endTime", DateUtils.toDate(today.plusDays(1))); } + + private boolean hasRealtimeTyreRecord(BizMaintenanceOrder submittedOrder) { + if (submittedOrder == null || submittedOrder.getOrderId() == null) { + return false; + } + BizMaintenanceOrder persistedOrder = bizMaintenanceOrderService.selectBizMaintenanceOrderByOrderId(submittedOrder.getOrderId()); + if (persistedOrder == null) { + return false; + } + String plateNumber = StringUtils.isEmpty(persistedOrder.getPlateNumber()) + ? submittedOrder.getPlateNumber() + : persistedOrder.getPlateNumber(); + if (StringUtils.isEmpty(plateNumber) || persistedOrder.getCreateTime() == null) { + return false; + } + + BizMaintenanceOrder realtimeQuery = new BizMaintenanceOrder(); + realtimeQuery.setPlateNumber(plateNumber); + realtimeQuery.setCreateTime(persistedOrder.getCreateTime()); + // 装胎/卸胎是实时落库动作;返回工单完成页时明细可能尚未随表单提交,所以按工单创建时间到当前时刻兜底识别。 + realtimeQuery.setUpdateTime(DateUtils.getNowDate()); + return bizOrderTireDetailService.countRealtimeHandledTyreRecords(realtimeQuery) > 0; + } } diff --git a/ruoyi-admin/src/main/resources/templates/tyre/car/add.html b/ruoyi-admin/src/main/resources/templates/tyre/car/add.html index 22d13c90..ef0af55e 100644 --- a/ruoyi-admin/src/main/resources/templates/tyre/car/add.html +++ b/ruoyi-admin/src/main/resources/templates/tyre/car/add.html @@ -42,7 +42,7 @@ - \ No newline at end of file + diff --git a/ruoyi-admin/src/main/resources/templates/tyre/scrap/scrap.html b/ruoyi-admin/src/main/resources/templates/tyre/scrap/scrap.html new file mode 100644 index 00000000..8fb53328 --- /dev/null +++ b/ruoyi-admin/src/main/resources/templates/tyre/scrap/scrap.html @@ -0,0 +1,283 @@ + + + + + + + +
+
+
+
+
+
    +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
  • + + + - + +
  • +
  • +  搜索 +  重置 +
  • +
+
+
+
+ + +
+
+
+
+
+ + + + diff --git a/ruoyi-admin/src/main/resources/templates/tyre/tyre/typreDetill2.html b/ruoyi-admin/src/main/resources/templates/tyre/tyre/typreDetill2.html index 283d4e71..7abf878d 100644 --- a/ruoyi-admin/src/main/resources/templates/tyre/tyre/typreDetill2.html +++ b/ruoyi-admin/src/main/resources/templates/tyre/tyre/typreDetill2.html @@ -242,6 +242,21 @@ border-top: 0; } + .scrap-photo-grid { + display: flex; + flex-wrap: wrap; + gap: 6px; + margin-top: 6px; + } + + .scrap-photo-grid img { + width: 64px; + height: 64px; + object-fit: cover; + border: 1px solid #e7eaec; + border-radius: 4px; + } + .empty-life { padding: 48px 16px; color: #999; @@ -284,7 +299,9 @@ totalMileage=${resultMap['totalMileage']}, currentPatternDepth=${resultMap['currentPatternDepth']}, bizMaintenanceOrder=${resultMap['bizMaintenanceOrder']}, - bizMaintenanceOrderList=${resultMap['bizMaintenanceOrderList']}"> + bizMaintenanceOrderList=${resultMap['bizMaintenanceOrderList']}, + scrapList=${resultMap['scrapList']}, + scrapPhotoMap=${resultMap['scrapPhotoMap']}">
@@ -391,6 +408,7 @@ 里程使用记录:0 保养记录:0 维保工单:0 + 报废记录:0
@@ -503,6 +521,9 @@ 拆报废车 月检 小修 + + 轮胎修补 + 轮胎报废 维保工单 :- @@ -550,7 +571,60 @@
-
+
+
报废记录
+
    +
  • + +
    +
    + + 轮胎报废 + :- +
    +
    -
    +
    +
    +
    车牌号:-
    +
    轮位:-
    +
    报废前花纹:-
    +
    当时车辆里程:-
    +
    维修站点:-
    +
    操作人:-
    +
    轮胎编号:-
    +
    + 工单状态: + + 未开始 + 执行中 + 已完成 + - + +
    +
    备注:-
    + + +
    +
    该报废工单照片(0 张):
    +
    + + + 报废照片 + +
    +
    +
    +
    +
  • +
+
+ +
暂无生命周期记录
@@ -562,7 +636,26 @@ if (window.parent && window.parent !== window && window.parent.$ && window.parent.$.modal) { window.parent.$.modal.closeLoading(); } + + $('.js-scrap-photo-link').each(function () { + var $link = $(this); + var url = buildLifecycleFileUrl($link.attr('data-file-path')); + // FileUploadUtils 返回 /profile/...;生命周期页同样拼 ctx,避免部署在非根路径时图片 404。 + $link.attr('href', url); + $link.find('img').attr('src', url); + }); }); + + function buildLifecycleFileUrl(filePath) { + if ($.common.isEmpty(filePath)) { + return '#'; + } + if (filePath.indexOf('http://') === 0 || filePath.indexOf('https://') === 0) { + return filePath; + } + filePath = filePath.replace(/\\/g, '/'); + return filePath.charAt(0) === '/' ? ctx + filePath.substring(1) : ctx + filePath; + } diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/BizOrderTireDetail.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/BizOrderTireDetail.java index d939d7e2..1049e4d9 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/domain/BizOrderTireDetail.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/BizOrderTireDetail.java @@ -1,6 +1,8 @@ package com.ruoyi.system.domain; import java.math.BigDecimal; +import java.util.Date; +import com.fasterxml.jackson.annotation.JsonFormat; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; import com.ruoyi.common.annotation.Excel; @@ -51,6 +53,64 @@ public class BizOrderTireDetail extends BaseEntity // 数据类型(保养;换新胎,卸车;换新胎,装车) private String dataType; + /** 报废执行时间,查询侧按工单日期优先、明细创建时间兜底 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + @Excel(name = "报废执行时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss") + private Date scrapTime; + + /** 报废工单编号 */ + @Excel(name = "工单编号") + private String orderNo; + + /** 工单车辆号/轮胎工单号快照 */ + @Excel(name = "车牌号码") + private String plateNumber; + + /** 维保类型编码,PDA 报废工单通常为 7 */ + @Excel(name = "工单类型") + private String typeCode; + + /** 工单状态 */ + @Excel(name = "工单状态") + private String orderStatus; + + /** 维修站点/修理厂名称 */ + @Excel(name = "维修站点") + private String factoryName; + + /** 报废时车辆里程 */ + @Excel(name = "报废时车辆里程") + private BigDecimal inputMileage; + + /** 工单计划日期 */ + @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") + @Excel(name = "工单日期", width = 30, dateFormat = "yyyy-MM-dd") + private Date maintainDate; + + /** 轮胎档案胎号 */ + @Excel(name = "档案胎号") + private String tyreNo; + + /** 轮胎芯片 */ + @Excel(name = "轮胎芯片") + private String tyreEpc; + + /** 自编号 */ + @Excel(name = "自编号") + private String selfNo; + + /** 轮胎品牌 */ + @Excel(name = "轮胎品牌") + private String tyreBrand; + + /** 轮胎规格 */ + @Excel(name = "轮胎规格") + private String tyreModel; + + /** 同一报废工单下的照片数量,照片按 sys_attachment.order_id 关联 */ + @Excel(name = "照片张数") + private Long photoCount; + public String getPositionName() { return positionName; } @@ -147,6 +207,146 @@ public class BizOrderTireDetail extends BaseEntity return tireStatus; } + public Date getScrapTime() + { + return scrapTime; + } + + public void setScrapTime(Date scrapTime) + { + this.scrapTime = scrapTime; + } + + public String getOrderNo() + { + return orderNo; + } + + public void setOrderNo(String orderNo) + { + this.orderNo = orderNo; + } + + public String getPlateNumber() + { + return plateNumber; + } + + public void setPlateNumber(String plateNumber) + { + this.plateNumber = plateNumber; + } + + public String getTypeCode() + { + return typeCode; + } + + public void setTypeCode(String typeCode) + { + this.typeCode = typeCode; + } + + public String getOrderStatus() + { + return orderStatus; + } + + public void setOrderStatus(String orderStatus) + { + this.orderStatus = orderStatus; + } + + public String getFactoryName() + { + return factoryName; + } + + public void setFactoryName(String factoryName) + { + this.factoryName = factoryName; + } + + public BigDecimal getInputMileage() + { + return inputMileage; + } + + public void setInputMileage(BigDecimal inputMileage) + { + this.inputMileage = inputMileage; + } + + public Date getMaintainDate() + { + return maintainDate; + } + + public void setMaintainDate(Date maintainDate) + { + this.maintainDate = maintainDate; + } + + public String getTyreNo() + { + return tyreNo; + } + + public void setTyreNo(String tyreNo) + { + this.tyreNo = tyreNo; + } + + public String getTyreEpc() + { + return tyreEpc; + } + + public void setTyreEpc(String tyreEpc) + { + this.tyreEpc = tyreEpc; + } + + public String getSelfNo() + { + return selfNo; + } + + public void setSelfNo(String selfNo) + { + this.selfNo = selfNo; + } + + public String getTyreBrand() + { + return tyreBrand; + } + + public void setTyreBrand(String tyreBrand) + { + this.tyreBrand = tyreBrand; + } + + public String getTyreModel() + { + return tyreModel; + } + + public void setTyreModel(String tyreModel) + { + this.tyreModel = tyreModel; + } + + public Long getPhotoCount() + { + return photoCount; + } + + public void setPhotoCount(Long photoCount) + { + this.photoCount = photoCount; + } + @Override public String toString() { return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) @@ -158,6 +358,20 @@ public class BizOrderTireDetail extends BaseEntity .append("treadDepth", getTreadDepth()) .append("tirePress", getTirePress()) .append("tireStatus", getTireStatus()) + .append("scrapTime", getScrapTime()) + .append("orderNo", getOrderNo()) + .append("plateNumber", getPlateNumber()) + .append("typeCode", getTypeCode()) + .append("orderStatus", getOrderStatus()) + .append("factoryName", getFactoryName()) + .append("inputMileage", getInputMileage()) + .append("maintainDate", getMaintainDate()) + .append("tyreNo", getTyreNo()) + .append("tyreEpc", getTyreEpc()) + .append("selfNo", getSelfNo()) + .append("tyreBrand", getTyreBrand()) + .append("tyreModel", getTyreModel()) + .append("photoCount", getPhotoCount()) .append("createBy", getCreateBy()) .append("createTime", getCreateTime()) .append("updateBy", getUpdateBy()) diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/BizOrderTireDetailMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/BizOrderTireDetailMapper.java index 69926499..5bffd32c 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/BizOrderTireDetailMapper.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/BizOrderTireDetailMapper.java @@ -3,6 +3,7 @@ package com.ruoyi.system.mapper; import java.util.List; import java.util.Map; +import com.ruoyi.system.domain.BizMaintenanceOrder; import com.ruoyi.system.domain.BizOrderTireDetail; import org.apache.ibatis.annotations.Param; @@ -64,8 +65,52 @@ public interface BizOrderTireDetailMapper List selectBizOrderTireDetail(BizOrderTireDetail bizOrderTireDetail); + /** + * 查询维保前的轮胎快照。 + *

+ * 基于当前工单明细定位轮胎,再回看该轮胎本次明细写入前的最近里程快照,避免前后胎纹深度都显示成完成后的值。 + * status 列映射规则:'换新胎,卸车' -> 'removed','保养' -> 'normal'。 + * + * @param orderId 当前工单ID + * @return 维保前轮胎快照 + */ + List selectBeforeSnapshot(@Param("orderId") Long orderId); + + /** + * 查询维保后的轮胎快照。 + *

+ * 取当前工单保存明细,并补入工单时间窗内 PDA 已实时写入的装胎记录,反映"维保完成后"车上的胎。 + * status 列映射规则:'换新胎,装车'/实时装胎 -> 'installed','保养' -> 'normal'。 + * + * @param orderId 当前工单ID + * @return 维保后轮胎快照 + */ + List selectAfterSnapshot(@Param("orderId") Long orderId); + List selectBaseTrieInstall(String plateNumber); + /** + * 统计工单时间窗内 PDA 已实时落库的装卸胎记录。 + *

+ * 装胎/卸胎接口会先写 record_tyre_install,再回到工单完成页;这里用于避免“已经处理轮胎但明细尚未提交” + * 时被空明细校验误拦截。 + * + * @param bizMaintenanceOrder 已补齐车牌、创建时间、截止时间的工单查询条件 + * @return 实时装卸胎记录数 + */ + int countRealtimeHandledTyreRecords(BizMaintenanceOrder bizMaintenanceOrder); + + /** + * 查询轮胎报废明细列表。 + *

+ * 当前报废事实来自 PDA 写入的 tire_status='报废',这里复用工单明细表做只读查询, + * 避免在 Web 侧另起一套报废事实口径。 + * + * @param bizOrderTireDetail 查询条件 + * @return 报废明细集合 + */ + List selectScrapList(BizOrderTireDetail bizOrderTireDetail); + /** * 查询车辆全部维保轮胎明细,Service 层按轮位截取最新一条。 * diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/IBizOrderTireDetailService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/IBizOrderTireDetailService.java index 188e7f6f..3eead653 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/IBizOrderTireDetailService.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/IBizOrderTireDetailService.java @@ -3,6 +3,7 @@ package com.ruoyi.system.service; import java.util.List; import java.util.Map; +import com.ruoyi.system.domain.BizMaintenanceOrder; import com.ruoyi.system.domain.BizOrderTireDetail; /** @@ -63,5 +64,44 @@ public interface IBizOrderTireDetailService List selectBizOrderTireDetail(BizOrderTireDetail bizOrderTireDetail); + /** + * 查询维保前的轮胎快照。 + *

+ * 基于当前工单明细定位轮胎,再回看该轮胎本次明细写入前的最近里程快照,反映"维保开始时"车上的胎。 + * 返回字段:brand / spec / dot / depth / position / status,其中 status 已被 mapper 映射为 + * 'removed'(卸车)或 'normal'(保养)。 + * + * @param orderId 当前工单ID + * @return 维保前轮胎快照 + */ + List selectBeforeSnapshot(Long orderId); + + /** + * 查询维保后的轮胎快照。 + *

+ * 取当前工单保存明细,并补入工单时间窗内 PDA 已实时写入的装胎记录,反映"维保完成后"车上的胎。 + * 返回字段同 {@link #selectBeforeSnapshot},status 已被映射为 'installed'(装车)或 'normal'(保养)。 + * + * @param orderId 当前工单ID + * @return 维保后轮胎快照 + */ + List selectAfterSnapshot(Long orderId); + List selectBaseTrieInstall(String plateNumber); + + /** + * 统计工单时间窗内 PDA 已实时落库的装卸胎记录。 + * + * @param bizMaintenanceOrder 已补齐车牌、创建时间、截止时间的工单查询条件 + * @return 实时装卸胎记录数 + */ + int countRealtimeHandledTyreRecords(BizMaintenanceOrder bizMaintenanceOrder); + + /** + * 查询轮胎报废明细列表。 + * + * @param bizOrderTireDetail 查询条件 + * @return 报废明细集合 + */ + List selectScrapList(BizOrderTireDetail bizOrderTireDetail); } diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/BaseCarLifecycleServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/BaseCarLifecycleServiceImpl.java index f3874a3f..17e7c2a7 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/BaseCarLifecycleServiceImpl.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/BaseCarLifecycleServiceImpl.java @@ -16,9 +16,9 @@ import com.ruoyi.system.domain.CarLifecycleQuery; import com.ruoyi.system.domain.CarLifecycleSummaryDTO; import com.ruoyi.system.domain.CarMaintenanceLifecycleDTO; import com.ruoyi.system.domain.CarMileageLifecycleDTO; +import com.ruoyi.system.domain.CarMountedTyreDTO; import com.ruoyi.system.domain.CarTyreInstallLifecycleDTO; import com.ruoyi.system.mapper.BaseCarLifecycleMapper; -import com.ruoyi.system.mapper.BizOrderTireDetailMapper; import com.ruoyi.system.service.IBaseCarLifecycleService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -42,9 +42,6 @@ public class BaseCarLifecycleServiceImpl implements IBaseCarLifecycleService @Autowired private BaseCarLifecycleMapper baseCarLifecycleMapper; - @Autowired - private BizOrderTireDetailMapper bizOrderTireDetailMapper; - /** * 查询车辆生命周期概要。 *

@@ -108,8 +105,8 @@ public class BaseCarLifecycleServiceImpl implements IBaseCarLifecycleService /** * 查询车辆 6 个轮位各自最新一条维护数据。 *

- * 轮位视图只服务车辆生命周期抽屉,不改维保工单详情页;SQL 拉取该车全部维保轮胎明细, - * Java 按固定轮位顺序截取每个轮位第一条,避免在 SQL 中使用 TOP / LIMIT / ROW_NUMBER 等方言差异写法。 + * 轮位视图只服务车辆生命周期抽屉,展示 base_tyre 当前装车事实; + * Java 按固定轮位顺序补齐空位,避免历史工单明细覆盖实时装胎后的轮位状态。 *

*/ @Override @@ -117,19 +114,20 @@ public class BaseCarLifecycleServiceImpl implements IBaseCarLifecycleService public List selectLatestWheelPositionMaintenance(CarLifecycleQuery query) { buildAuthorizedCar(query); - List detailRows = bizOrderTireDetailMapper.selectCarMaintenanceTireDetails(query.getCarNo()); + List mountedTyres = baseCarLifecycleMapper.selectMountedTyres(query); Map latestByPosition = new LinkedHashMap<>(); Set expectedPositions = new HashSet<>(WHEEL_POSITION_ORDER); - if (detailRows != null) + if (mountedTyres != null) { - for (Map row : detailRows) + for (CarMountedTyreDTO tyre : mountedTyres) { - String position = row == null || row.get("position") == null ? null : String.valueOf(row.get("position")); + String position = tyre == null ? null : tyre.getWheelPostion(); if (!expectedPositions.contains(position) || latestByPosition.containsKey(position)) { continue; } - latestByPosition.put(position, row); + // 轮位视图展示“当前装车事实”,以 base_tyre 实时轮位为准,避免历史工单明细覆盖 PDA 刚完成的装胎结果。 + latestByPosition.put(position, buildMountedTyreRow(tyre)); } } @@ -153,6 +151,34 @@ public class BaseCarLifecycleServiceImpl implements IBaseCarLifecycleService return result; } + private Map buildMountedTyreRow(CarMountedTyreDTO tyre) + { + Map row = new LinkedHashMap(); + row.put("position", tyre.getWheelPostion()); + row.put("brand", tyre.getTyreBrand()); + row.put("spec", tyre.getTyreModel()); + row.put("dot", firstNonBlank(tyre.getTyreNo(), tyre.getSelfNo(), tyre.getTyreEpc())); + row.put("depth", tyre.getPatternDepth()); + row.put("status", "installed"); + return row; + } + + private String firstNonBlank(String... values) + { + if (values == null) + { + return null; + } + for (String value : values) + { + if (!StringUtils.isBlank(value)) + { + return value; + } + } + return null; + } + /** * 查询车辆关联轮胎质检历史(分页/全量)。 *

diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/BaseTyreServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/BaseTyreServiceImpl.java index 7e532c3c..eef7c6ab 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/BaseTyreServiceImpl.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/BaseTyreServiceImpl.java @@ -18,6 +18,7 @@ import com.ruoyi.system.mapper.BaseTyreMapper; import com.ruoyi.system.mapper.RecordTyreInstallMapper; import com.ruoyi.system.mapper.RecordWarehousingMapper; import com.ruoyi.system.service.IBaseTyreService; +import com.ruoyi.system.service.ISysAttachmentService; import com.ruoyi.system.util.TyreLifecycleCalc; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -55,6 +56,9 @@ public class BaseTyreServiceImpl implements IBaseTyreService private BizMaintenanceOrderMapper bizMaintenanceOrderMapper; private static final Logger log = LoggerFactory.getLogger(BaseTyreServiceImpl.class); + @Autowired + private ISysAttachmentService sysAttachmentService; + @Autowired private RecordTyreMileageMapper recordTyreMileageMapper; @@ -302,11 +306,14 @@ public class BaseTyreServiceImpl implements IBaseTyreService for (BizOrderTireDetail biz : bizOrderTireDetailList){ //查询维修单//筛选工单 List filteredList = bizOrderTireDetailList.stream() - .filter(detail -> detail.getOrderId() == biz.getOrderId()) + .filter(detail -> Objects.equals(detail.getOrderId(), biz.getOrderId())) .collect(Collectors.toList()); BizMaintenanceOrder bizMaintenanceOrder = bizMaintenanceOrderMapper.selectBizMaintenanceOrderByOrderId(biz.getOrderId()); + if (bizMaintenanceOrder == null) { + // 历史明细可能残留已删除/未同步的工单ID;Web详情以可追溯数据为准,缺主单时跳过,避免一条脏明细拖垮整条生命周期。 + continue; + } bizMaintenanceOrder.setBizOrderTireDetailList(filteredList); - System.out.println(bizMaintenanceOrder.getLastMileage()); if (bizMaintenanceOrder.getLastMileage() ==null || String.valueOf(bizMaintenanceOrder.getLastMileage() ).equals("null")) { bizMaintenanceOrder.setLastMileage(BigDecimal.valueOf(0)); } @@ -333,6 +340,8 @@ public class BaseTyreServiceImpl implements IBaseTyreService map.put("recordTyreMileageList", new ArrayList<>()); map.put("maintenanceMileageList", new ArrayList<>()); map.put("bizMaintenanceOrderList", new ArrayList<>()); + map.put("scrapList", new ArrayList<>()); + map.put("scrapPhotoMap", new LinkedHashMap<>()); map.put("totalMileage", null); map.put("currentPatternDepth", null); @@ -345,6 +354,8 @@ public class BaseTyreServiceImpl implements IBaseTyreService // 5. 提取轮胎实体和 RFID(EPC)编码 BaseTyre resultBase = (BaseTyre) resultBaseObj; + // 报废事实按轮胎 ID 关联工单明细,即使历史胎缺 RFID,也应优先把 Web 可追溯的报废记录展示出来。 + putScrapList(map, resultBase); String tyreRfid = resultBase.getTyreEpc(); // 6. RFID 为空时清空流转列表并返回,避免展示错误数据 if (StringUtils.isBlank(tyreRfid)) @@ -558,6 +569,42 @@ public class BaseTyreServiceImpl implements IBaseTyreService map.put("bizMaintenanceOrder", orderList.isEmpty() ? null : orderList.get(0)); } + private void putScrapList(Map map, BaseTyre resultBase) + { + if (resultBase.getTyreId() == null) + { + return; + } + + BizOrderTireDetail query = new BizOrderTireDetail(); + query.setTireId(resultBase.getTyreId()); + List scrapList = bizOrderTireDetailMapper.selectScrapList(query); + map.put("scrapList", scrapList == null ? new ArrayList<>() : scrapList); + + Map> scrapPhotoMap = new LinkedHashMap<>(); + if (scrapList == null || scrapList.isEmpty()) + { + map.put("scrapPhotoMap", scrapPhotoMap); + return; + } + + Set seenOrderIds = new LinkedHashSet<>(); + for (BizOrderTireDetail scrap : scrapList) + { + if (scrap == null || scrap.getOrderId() == null || !seenOrderIds.add(scrap.getOrderId())) + { + continue; + } + // PDA 图片只按 order_id 入附件表,同一报废工单多条轮胎共享同一组照片,Web 端不能伪造成单胎照片。 + List attachments = sysAttachmentService.selectAttachmentsByOrderId(scrap.getOrderId()); + if (attachments != null && !attachments.isEmpty()) + { + scrapPhotoMap.put(scrap.getOrderId(), attachments); + } + } + map.put("scrapPhotoMap", scrapPhotoMap); + } + private Date maintenanceRecordTime(RecordTyreMileage item) { if (item == null) diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/BizOrderTireDetailServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/BizOrderTireDetailServiceImpl.java index 3470f662..a6d9fa80 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/BizOrderTireDetailServiceImpl.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/BizOrderTireDetailServiceImpl.java @@ -7,6 +7,7 @@ import com.ruoyi.common.utils.DateUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.ruoyi.system.mapper.BizOrderTireDetailMapper; +import com.ruoyi.system.domain.BizMaintenanceOrder; import com.ruoyi.system.domain.BizOrderTireDetail; import com.ruoyi.system.service.IBizOrderTireDetailService; import com.ruoyi.common.core.text.Convert; @@ -102,8 +103,32 @@ public class BizOrderTireDetailServiceImpl implements IBizOrderTireDetailService return bizOrderTireDetailMapper.selectBizOrderTireDetail(bizOrderTireDetail); } + @Override + public List selectBeforeSnapshot(Long orderId) { + return bizOrderTireDetailMapper.selectBeforeSnapshot(orderId); + } + + @Override + public List selectAfterSnapshot(Long orderId) { + return bizOrderTireDetailMapper.selectAfterSnapshot(orderId); + } + @Override public List selectBaseTrieInstall(String plateNumber) { return bizOrderTireDetailMapper.selectBaseTrieInstall(plateNumber); } + + @Override + public int countRealtimeHandledTyreRecords(BizMaintenanceOrder bizMaintenanceOrder) { + return bizOrderTireDetailMapper.countRealtimeHandledTyreRecords(bizMaintenanceOrder); + } + + /** + * 查询轮胎报废明细列表。 + * 只做查询透传,不在 Service 层重写报废规则,避免与 PDA 写入口径产生第二套判断。 + */ + @Override + public List selectScrapList(BizOrderTireDetail bizOrderTireDetail) { + return bizOrderTireDetailMapper.selectScrapList(bizOrderTireDetail); + } } diff --git a/ruoyi-system/src/main/resources/mapper/tyre/BaseCarLifecycleMapper.xml b/ruoyi-system/src/main/resources/mapper/tyre/BaseCarLifecycleMapper.xml index aec02c9d..ed2a67b6 100644 --- a/ruoyi-system/src/main/resources/mapper/tyre/BaseCarLifecycleMapper.xml +++ b/ruoyi-system/src/main/resources/mapper/tyre/BaseCarLifecycleMapper.xml @@ -4,6 +4,32 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> + + COALESCE( + o.last_mileage, + ( + SELECT MAX(rtm.mileage_old - rtm.mileage) + FROM record_tyre_mileage rtm + WHERE rtm.plate_number = o.plate_number + AND rtm.record_type = '保养' + AND o.create_time IS NOT NULL + AND rtm.create_time >= o.create_time + + AND NOT EXISTS ( + SELECT 1 + FROM biz_maintenance_order next_order + WHERE next_order.plate_number = o.plate_number + AND next_order.type_code IN ('1', '2', '4', '5') + AND ( + next_order.create_time > o.create_time + OR (next_order.create_time = o.create_time AND next_order.order_id > o.order_id) + ) + AND next_order.create_time <= rtm.create_time + ) + ) + ) + + @@ -47,8 +73,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" select tyre_rfid, pattern_depth, row_number() over (partition by tyre_rfid order by create_time desc, id desc) as rn from record_tyre_mileage - where record_type = '保养' - and pattern_depth is not null + where pattern_depth is not null and pattern_depth <> '' ) ranked where rn = 1 @@ -83,7 +108,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" o.type_code as typeCode, o.status as status, o.input_mileage as inputMileage, - o.last_mileage as lastMileage, + as lastMileage, o.maintain_date as maintainDate, d.dept_name as factoryName, o.description as description diff --git a/ruoyi-system/src/main/resources/mapper/tyre/BizMaintenanceOrderMapper.xml b/ruoyi-system/src/main/resources/mapper/tyre/BizMaintenanceOrderMapper.xml index 94bfd23d..aeaae3fa 100644 --- a/ruoyi-system/src/main/resources/mapper/tyre/BizMaintenanceOrderMapper.xml +++ b/ruoyi-system/src/main/resources/mapper/tyre/BizMaintenanceOrderMapper.xml @@ -32,6 +32,32 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" select order_id, order_no, vehicle_id, plate_number, type_code, factory_id, input_mileage, last_mileage, maintain_date, description, status, order_type, create_by, create_time, update_by, update_time, remark from biz_maintenance_order + + COALESCE( + bmo.last_mileage, + ( + SELECT MAX(rtm.mileage_old - rtm.mileage) + FROM record_tyre_mileage rtm + WHERE rtm.plate_number = bmo.plate_number + AND rtm.record_type = '保养' + AND bmo.create_time IS NOT NULL + AND rtm.create_time >= bmo.create_time + + AND NOT EXISTS ( + SELECT 1 + FROM biz_maintenance_order next_order + WHERE next_order.plate_number = bmo.plate_number + AND next_order.type_code IN ('1', '2', '4', '5') + AND ( + next_order.create_time > bmo.create_time + OR (next_order.create_time = bmo.create_time AND next_order.order_id > bmo.order_id) + ) + AND next_order.create_time <= rtm.create_time + ) + ) + ) + + + + + + + + + + + + + insert into biz_order_tire_detail