Ver Fonte

提交合同查询接口

chenxiaofei há 1 mês atrás
pai
commit
cd76cdd700
15 ficheiros alterados com 796 adições e 29 exclusões
  1. 174 0
      sckw-common/sckw-common-core/src/main/java/com/sckw/core/utils/TruckNoUtils.java
  2. 1 0
      sckw-modules/sckw-contract/src/main/java/com/sckw/contract/service/operateService/KwcContractTradeService.java
  3. 17 0
      sckw-modules/sckw-fleet/src/main/java/com/sckw/fleet/controller/KwfTruckController.java
  4. 29 0
      sckw-modules/sckw-fleet/src/main/java/com/sckw/fleet/model/request/BatchTruckValidateReq.java
  5. 80 0
      sckw-modules/sckw-fleet/src/main/java/com/sckw/fleet/model/vo/TruckValidateVo.java
  6. 103 0
      sckw-modules/sckw-fleet/src/main/java/com/sckw/fleet/service/KwfTruckService.java
  7. 17 0
      sckw-modules/sckw-transport/src/main/java/com/sckw/transport/controller/KwtWaybillOrderController.java
  8. 1 1
      sckw-modules/sckw-transport/src/main/java/com/sckw/transport/dubbo/TransportServiceImpl.java
  9. 2 1
      sckw-modules/sckw-transport/src/main/java/com/sckw/transport/model/KwtWaybillOrderNode.java
  10. 32 0
      sckw-modules/sckw-transport/src/main/java/com/sckw/transport/model/param/WaybillOrderNodeReq.java
  11. 4 2
      sckw-modules/sckw-transport/src/main/java/com/sckw/transport/model/vo/OrderFinishReq.java
  12. 92 0
      sckw-modules/sckw-transport/src/main/java/com/sckw/transport/model/vo/WaybillOrderNodeVo.java
  13. 20 0
      sckw-modules/sckw-transport/src/main/java/com/sckw/transport/repository/KwtWaybillOrderNodeRepository.java
  14. 31 25
      sckw-modules/sckw-transport/src/main/java/com/sckw/transport/service/KwtLogisticsConsignmentService.java
  15. 193 0
      sckw-modules/sckw-transport/src/main/java/com/sckw/transport/service/KwtWaybillOrderV1Service.java

+ 174 - 0
sckw-common/sckw-common-core/src/main/java/com/sckw/core/utils/TruckNoUtils.java

@@ -0,0 +1,174 @@
+package com.sckw.core.utils;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+/**
+ * @author system
+ * @description 车牌号工具类
+ * @date 2025-06-24
+ */
+public class TruckNoUtils {
+
+    /**
+     * 车牌号正则表达式(7位标准车牌)
+     * 格式:省份简称(1位) + 字母(1位) + 数字/字母(5位)
+     */
+    private static final String TRUCK_NO_PATTERN_7 = "^[京津冀晋蒙辽吉黑沪苏浙皖闽赣鲁豫鄂湘粤桂琼川贵云渝藏陕甘青宁新使]{1}[A-Z]{1}[0-9A-Z]{5}$";
+
+    /**
+     * 车牌号正则表达式(8位新能源车牌)
+     * 格式:省份简称(1位) + 字母(1位) + 数字/字母(6位)
+     */
+    private static final String TRUCK_NO_PATTERN_8 = "^[京津冀晋蒙辽吉黑沪苏浙皖闽赣鲁豫鄂湘粤桂琼川贵云渝藏陕甘青宁新使]{1}[A-Z]{1}[0-9A-Z]{6}$";
+
+    /**
+     * 支持的分隔符:中文逗号、英文逗号、空格、换行符、制表符
+     */
+    private static final String DELIMITER_PATTERN = "[,,\\s\\n\\r\\t]+";
+
+    /**
+     * 校验单个车牌号格式是否正确
+     *
+     * @param truckNo 车牌号
+     * @return true-格式正确,false-格式错误
+     */
+    public static boolean isValidTruckNo(String truckNo) {
+        if (StringUtils.isBlank(truckNo)) {
+            return false;
+        }
+
+        String formatted = formatTruckNo(truckNo);
+        return Pattern.matches(TRUCK_NO_PATTERN_7, formatted) 
+                || Pattern.matches(TRUCK_NO_PATTERN_8, formatted);
+    }
+
+    /**
+     * 格式化车牌号(去除空格、转大写)
+     *
+     * @param truckNo 原始车牌号
+     * @return 格式化后的车牌号
+     */
+    public static String formatTruckNo(String truckNo) {
+        if (StringUtils.isBlank(truckNo)) {
+            return "";
+        }
+        // 去除所有空格并转大写
+        return truckNo.trim().replaceAll("\\s+", "").toUpperCase();
+    }
+
+    /**
+     * 批量格式化车牌号
+     *
+     * @param truckNos 车牌号列表
+     * @return 格式化后的车牌号列表
+     */
+    public static List<String> formatTruckNos(List<String> truckNos) {
+        if (CollectionUtils.isEmpty(truckNos)) {
+            return new ArrayList<>();
+        }
+        return truckNos.stream()
+                .map(TruckNoUtils::formatTruckNo)
+                .filter(StringUtils::isNotBlank)
+                .distinct()
+                .collect(Collectors.toList());
+    }
+
+    /**
+     * 从字符串中拆分车牌号
+     * 支持中文逗号、英文逗号、空格、换行符等分隔符
+     *
+     * @param truckNoStr 车牌号字符串,如:"川A12345,京B67890,粤C11111 沪D22222"
+     * @return 车牌号列表
+     */
+    public static List<String> splitTruckNos(String truckNoStr) {
+        if (StringUtils.isBlank(truckNoStr)) {
+            return new ArrayList<>();
+        }
+
+        // 使用正则表达式分割
+        String[] parts = truckNoStr.split(DELIMITER_PATTERN);
+        
+        return Arrays.stream(parts)
+                .map(String::trim)
+                .filter(StringUtils::isNotBlank)
+                .map(TruckNoUtils::formatTruckNo)
+                .distinct()
+                .collect(Collectors.toList());
+    }
+
+    /**
+     * 获取车牌号校验失败的原因
+     *
+     * @param truckNo 车牌号
+     * @return 失败原因
+     */
+    public static String getValidateErrorMsg(String truckNo) {
+        if (StringUtils.isBlank(truckNo)) {
+            return "车牌号不能为空";
+        }
+
+        String formatted = formatTruckNo(truckNo);
+        int length = formatted.length();
+
+        if (length < 7) {
+            return "车牌号长度不足(标准车牌7位,新能源车牌8位)";
+        }
+
+        if (length > 8) {
+            return "车牌号长度超出(标准车牌7位,新能源车牌8位)";
+        }
+
+        // 检查省份简称
+        char province = formatted.charAt(0);
+        String provinces = "京津冀晋蒙辽吉黑沪苏浙皖闽赣鲁豫鄂湘粤桂琼川贵云渝藏陕甘青宁新使";
+        if (provinces.indexOf(province) == -1) {
+            return "省份简称不正确:" + province;
+        }
+
+        // 检查第二位是否为字母
+        char second = formatted.charAt(1);
+        if (!Character.isLetter(second) || !Character.isUpperCase(second)) {
+            return "第二位必须为大写字母:" + second;
+        }
+
+        // 检查后续位是否为数字或字母
+        String suffix = formatted.substring(2);
+        if (!suffix.matches("[0-9A-Z]+")) {
+            return "车牌号包含非法字符,只能包含数字和大写字母";
+        }
+
+        return "车牌号格式不正确";
+    }
+
+    /**
+     * 提取车牌号中的省份简称
+     *
+     * @param truckNo 车牌号
+     * @return 省份简称
+     */
+    public static String getProvince(String truckNo) {
+        if (StringUtils.isBlank(truckNo)) {
+            return "";
+        }
+        String formatted = formatTruckNo(truckNo);
+        return formatted.length() > 0 ? String.valueOf(formatted.charAt(0)) : "";
+    }
+
+    /**
+     * 提取车牌号中的城市代码(第二位字母)
+     *
+     * @param truckNo 车牌号
+     * @return 城市代码
+     */
+    public static String getCityCode(String truckNo) {
+        if (StringUtils.isBlank(truckNo)) {
+            return "";
+        }
+        String formatted = formatTruckNo(truckNo);
+        return formatted.length() > 1 ? String.valueOf(formatted.charAt(1)) : "";
+    }
+}

+ 1 - 0
sckw-modules/sckw-contract/src/main/java/com/sckw/contract/service/operateService/KwcContractTradeService.java

@@ -1679,6 +1679,7 @@ public class KwcContractTradeService {
         }
         Date date = new Date();
         logistics = logistics.stream()
+                .filter(log -> Objects.nonNull(log.getStartTime()) && Objects.nonNull(log.getEndTime()))
                 .filter(log -> Objects.equals(log.getStatus(),ContractStatusEnum.SIGNED.getCode())
                 && log.getStartTime().before(date) &&  log.getEndTime().after( date))
                 .collect(Collectors.toList());

+ 17 - 0
sckw-modules/sckw-fleet/src/main/java/com/sckw/fleet/controller/KwfTruckController.java

@@ -351,4 +351,21 @@ public class KwfTruckController {
     public BaseResult<PageDataResult<KwfTruckDetailVo>> queryPageCapacityTruck(@RequestBody TruckInfoReq req) {
         return BaseResult.success(truckService.queryPageCapacityTruck(req));
     }
+
+    /**
+     * 批量校验车牌号并查询车辆信息
+     * 支持中文逗号、英文逗号、空格、换行符分隔
+     *
+     * @param req 批量车牌号校验请求
+     * @return 车牌号校验结果列表
+     * @author system
+     * @date 2025/06/24
+     */
+    @PostMapping("/batchValidateTruckNos")
+    @Operation(summary = "批量校验车牌号并查询车辆信息", description = "支持按中文逗号、英文逗号、空格、换行符拆分车牌号,校验格式,查询车牌号、车辆ID和轴数")
+    public BaseResult<List<TruckValidateVo>> batchValidateTruckNos(@RequestBody @Valid BatchTruckValidateReq req) {
+        return BaseResult.success(truckService.batchValidateTruckNos(req));
+    }
+
+
 }

+ 29 - 0
sckw-modules/sckw-fleet/src/main/java/com/sckw/fleet/model/request/BatchTruckValidateReq.java

@@ -0,0 +1,29 @@
+package com.sckw.fleet.model.request;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotEmpty;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * @author system
+ * @description 批量车牌号校验请求
+ * @date 2025-06-24
+ */
+@Data
+@Schema(description = "批量车牌号校验请求")
+public class BatchTruckValidateReq implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 车牌号列表(支持中文逗号、英文逗号、空格、换行符分隔)
+     */
+    @NotEmpty(message = "车牌号列表不能为空")
+    @Schema(description = "车牌号列表", required = true, example = "[\"川A12345\", \"京B67890\", \"粤C11111\"]")
+    private List<String> truckNos;
+}

+ 80 - 0
sckw-modules/sckw-fleet/src/main/java/com/sckw/fleet/model/vo/TruckValidateVo.java

@@ -0,0 +1,80 @@
+package com.sckw.fleet.model.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * @author system
+ * @description 车牌号校验结果VO
+ * @date 2025-06-24
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+@Schema(description = "车牌号校验结果")
+public class TruckValidateVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 原始车牌号
+     */
+    @Schema(description = "原始车牌号")
+    private String originalTruckNo;
+
+    /**
+     * 格式化后的车牌号
+     */
+    @Schema(description = "格式化后的车牌号(去除空格等)")
+    private String formattedTruckNo;
+
+    /**
+     * 是否校验通过
+     */
+    @Schema(description = "是否校验通过")
+    private Boolean valid;
+
+    /**
+     * 校验失败原因
+     */
+    @Schema(description = "校验失败原因")
+    private String errorMsg;
+
+    /**
+     * 车辆ID(如果在系统中存在)
+     */
+    @Schema(description = "车辆ID")
+    private Long truckId;
+
+    /**
+     * 轴数
+     */
+    @Schema(description = "轴数")
+    private String carAxis;
+
+    /**
+     * 是否在系统中存在
+     */
+    @Schema(description = "是否在系统中存在")
+    private Boolean exists;
+
+    /**
+     * 企业ID
+     */
+    @Schema(description = "企业ID")
+    private Long entId;
+
+    /**
+     * 企业名称
+     */
+    @Schema(description = "企业名称")
+    private String entName;
+}

+ 103 - 0
sckw-modules/sckw-fleet/src/main/java/com/sckw/fleet/service/KwfTruckService.java

@@ -1833,4 +1833,107 @@ public class KwfTruckService {
         }).collect(Collectors.toList());
         return PageDataResult.of(kwfTruckIPage, truckInfoVos);
     }
+
+    /**
+     * 批量校验车牌号并查询车辆信息
+     *
+     * @param req 批量车牌号校验请求
+     * @return 车牌号校验结果列表
+     * @author system
+     * @date 2025/06/24
+     */
+    public List<TruckValidateVo> batchValidateTruckNos(BatchTruckValidateReq req) {
+        log.info("批量校验车牌号,请求参数:{}", JSON.toJSONString(req));
+        
+        List<TruckValidateVo> result = new ArrayList<>();
+        
+        if (CollectionUtils.isEmpty(req.getTruckNos())) {
+            return result;
+        }
+
+        // 格式化车牌号列表
+        List<String> formattedTruckNos = TruckNoUtils.formatTruckNos(req.getTruckNos());
+        
+        // 批量查询车辆信息
+        Map<String, KwfTruck> truckMap = new HashMap<>();
+        if (!formattedTruckNos.isEmpty()) {
+            List<KwfTruck> trucks = kwfTruckRepository.list(
+                Wrappers.lambdaQuery(KwfTruck.class)
+                    .in(KwfTruck::getTruckNo, formattedTruckNos)
+                    .eq(KwfTruck::getDelFlag, 0)
+            );
+            truckMap = trucks.stream()
+                .collect(Collectors.toMap(KwfTruck::getTruckNo, Function.identity(), (k1, k2) -> k1));
+        }
+
+        // 批量查询企业信息
+        Set<Long> entIds = truckMap.values().stream()
+            .map(KwfTruck::getEntId)
+            .collect(Collectors.toSet());
+        Map<Long, EntCacheResDto> entMap = new HashMap<>();
+        if (!entIds.isEmpty()) {
+            entMap = remoteSystemService.queryEntCacheMapByIds(new ArrayList<>(entIds));
+        }
+
+        // 处理每个车牌号
+        for (String originalTruckNo : req.getTruckNos()) {
+            TruckValidateVo vo = validateSingleTruckNo(originalTruckNo, truckMap, entMap);
+            result.add(vo);
+        }
+
+        return result;
+    }
+
+    /**
+     * 校验单个车牌号
+     *
+     * @param originalTruckNo 原始车牌号
+     * @param truckMap 车辆信息Map
+     * @param entMap 企业信息Map
+     * @return 校验结果
+     */
+    private TruckValidateVo validateSingleTruckNo(String originalTruckNo, 
+                                                  Map<String, KwfTruck> truckMap,
+                                                  Map<Long, EntCacheResDto> entMap) {
+        TruckValidateVo.TruckValidateVoBuilder builder = TruckValidateVo.builder()
+            .originalTruckNo(originalTruckNo);
+
+        // 格式化车牌号
+        String formattedTruckNo = TruckNoUtils.formatTruckNo(originalTruckNo);
+        builder.formattedTruckNo(formattedTruckNo);
+
+        // 校验车牌号格式
+        boolean isValid = TruckNoUtils.isValidTruckNo(formattedTruckNo);
+        builder.valid(isValid);
+
+        if (!isValid) {
+            // 格式校验失败,返回错误信息
+            String errorMsg = TruckNoUtils.getValidateErrorMsg(formattedTruckNo);
+            builder.errorMsg(errorMsg)
+                   .exists(false);
+            return builder.build();
+        }
+
+        // 查询车辆信息
+        KwfTruck truck = truckMap.get(formattedTruckNo);
+        if (truck != null) {
+            // 车辆存在
+            builder.exists(true)
+                   .truckId(truck.getId())
+                   .carAxis(truck.getCarAxis())
+                   .entId(truck.getEntId());
+
+            // 获取企业名称
+            EntCacheResDto ent = entMap.get(truck.getEntId());
+            if (ent != null) {
+                builder.entName(ent.getFirmName());
+            }
+        } else {
+            // 车辆不存在
+            builder.exists(false)
+                   .errorMsg("车辆不在系统中");
+        }
+
+        return builder.build();
+    }
 }

+ 17 - 0
sckw-modules/sckw-transport/src/main/java/com/sckw/transport/controller/KwtWaybillOrderController.java

@@ -18,6 +18,7 @@ import com.sckw.core.web.response.HttpResult;
 import com.sckw.core.web.response.result.PageDataResult;
 import com.sckw.excel.utils.ExcelUtil;
 import com.sckw.transport.model.dto.*;
+import com.sckw.transport.model.param.WaybillOrderNodeReq;
 import com.sckw.transport.model.param.WaybillOrderReq;
 import com.sckw.transport.model.param.WaybillOrderResp;
 import com.sckw.transport.model.vo.*;
@@ -691,4 +692,20 @@ public class KwtWaybillOrderController {
     public BaseResult<StatisticsWaybillResp> statisticsWaybillOrder() {
         return BaseResult.success(waybillOrderV1Service.statisticsWaybillOrder());
     }
+
+    /**
+     * 查询运单节点轨迹时间线
+     *
+     * @param req 请求参数
+     * @return 节点轨迹列表
+     * @author system
+     * @date 2025/06/24
+     */
+    @PostMapping("/queryWaybillOrderNodeTimeline")
+    @Operation(summary = "查询运单节点轨迹时间线")
+    public BaseResult<List<WaybillOrderNodeVo>> queryWaybillOrderNodeTimeline(@RequestBody @Valid WaybillOrderNodeReq req) {
+        return BaseResult.success(waybillOrderV1Service.queryWaybillOrderNodeTimeline(req));
+    }
+
+
 }

+ 1 - 1
sckw-modules/sckw-transport/src/main/java/com/sckw/transport/dubbo/TransportServiceImpl.java

@@ -1104,7 +1104,7 @@ public class TransportServiceImpl implements TransportRemoteService {
             throw new BusinessException("物流订单id不能为空");
         }
         OrderFinishReq orderFinishReq = new OrderFinishReq();
-        orderFinishReq.setLogisticOrderId(String.valueOf(req.getLogisticOrderId()));
+        orderFinishReq.setLogisticOrderId(Collections.singletonList(req.getLogisticOrderId()));
         return logisticsConsignmentService.logisticOrderFinish(orderFinishReq);
 
     }

+ 2 - 1
sckw-modules/sckw-transport/src/main/java/com/sckw/transport/model/KwtWaybillOrderNode.java

@@ -7,6 +7,7 @@ import lombok.Data;
 
 import java.io.Serial;
 import java.io.Serializable;
+import java.util.Date;
 
 /**
  * @author lfdc
@@ -94,5 +95,5 @@ public class KwtWaybillOrderNode implements Serializable {
     /**
      * 创建时间
      */
-    private Data createTime;
+    private Date createTime;
 }

+ 32 - 0
sckw-modules/sckw-transport/src/main/java/com/sckw/transport/model/param/WaybillOrderNodeReq.java

@@ -0,0 +1,32 @@
+package com.sckw.transport.model.param;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * @author system
+ * @description 运单节点轨迹查询请求
+ * @date 2025-06-24
+ */
+@Data
+@Schema(description = "运单节点轨迹查询请求")
+public class WaybillOrderNodeReq implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 运单ID
+     */
+    @Schema(description = "运单ID", required = true)
+    private Long wOrderId;
+
+    /**
+     * 运单子单ID
+     */
+    @Schema(description = "运单子单ID")
+    private Long wSubtaskId;
+}

+ 4 - 2
sckw-modules/sckw-transport/src/main/java/com/sckw/transport/model/vo/OrderFinishReq.java

@@ -2,10 +2,12 @@ package com.sckw.transport.model.vo;
 
 import io.swagger.v3.oas.annotations.media.Schema;
 import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotEmpty;
 import lombok.Data;
 
 import java.io.Serial;
 import java.io.Serializable;
+import java.util.List;
 
 /**
  * @author :chenXiaoFei
@@ -21,6 +23,6 @@ public class OrderFinishReq implements Serializable {
      * 物流订单id
      */
     @Schema(description = "物流订单id")
-    @NotBlank(message = "物流订单id不能为空")
-    private String logisticOrderId;
+    @NotEmpty(message = "物流订单id不能为空")
+    private List<Long> logisticOrderId;
 }

+ 92 - 0
sckw-modules/sckw-transport/src/main/java/com/sckw/transport/model/vo/WaybillOrderNodeVo.java

@@ -0,0 +1,92 @@
+package com.sckw.transport.model.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * @author system
+ * @description 运单节点轨迹VO
+ * @date 2025-06-24
+ */
+@Data
+@Schema(description = "运单节点轨迹响应对象")
+public class WaybillOrderNodeVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 节点ID
+     */
+    @Schema(description = "节点ID")
+    private Long id;
+
+    /**
+     * 时间
+     */
+    @Schema(description = "操作时间,格式:yyyy-MM-dd HH:mm:ss")
+    private String createTime;
+
+    /**
+     * 运单状态
+     */
+    @Schema(description = "运单状态码")
+    private Integer orderStatus;
+
+    /**
+     * 状态描述
+     */
+    @Schema(description = "状态描述,如:车辆【川A5478C】司机【张三】装载货物【铁矿石】【参运】【28.70吨】")
+    private String statusDesc;
+
+    /**
+     * 车牌号
+     */
+    @Schema(description = "车牌号")
+    private String truckNo;
+
+    /**
+     * 司机名称
+     */
+    @Schema(description = "司机名称")
+    private String driverName;
+
+    /**
+     * 地磅名称
+     */
+    @Schema(description = "地磅名称")
+    private String weighbridgeName;
+
+    /**
+     * 地址
+     */
+    @Schema(description = "地址,如:四川省成都市新都区新都大道")
+    private String address;
+
+    /**
+     * 经度
+     */
+    @Schema(description = "经度")
+    private String lng;
+
+    /**
+     * 纬度
+     */
+    @Schema(description = "纬度")
+    private String lat;
+
+    /**
+     * 是否过磅
+     */
+    @Schema(description = "是否过磅")
+    private Boolean hasWeighbridge;
+
+    /**
+     * 图片路径
+     */
+    @Schema(description = "过磅图片路径")
+    private String imageUrl;
+}

+ 20 - 0
sckw-modules/sckw-transport/src/main/java/com/sckw/transport/repository/KwtWaybillOrderNodeRepository.java

@@ -1,10 +1,13 @@
 package com.sckw.transport.repository;
 
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.sckw.transport.dao.KwtWaybillOrderNodeMapper;
 import com.sckw.transport.model.KwtWaybillOrderNode;
 import org.springframework.stereotype.Repository;
 
+import java.util.List;
 
 /**
  * @author PC
@@ -12,4 +15,21 @@ import org.springframework.stereotype.Repository;
 @Repository
 public class KwtWaybillOrderNodeRepository extends ServiceImpl<KwtWaybillOrderNodeMapper, KwtWaybillOrderNode> {
 
+    /**
+     * 根据运单ID查询节点轨迹列表
+     *
+     * @param wOrderId   运单ID
+     * @param wSubtaskId 运单子单ID(可选)
+     * @return 节点轨迹列表,按创建时间正序排列
+     */
+    public List<KwtWaybillOrderNode> queryNodesByOrderId(Long wOrderId, Long wSubtaskId) {
+        LambdaQueryWrapper<KwtWaybillOrderNode> wrapper = Wrappers.lambdaQuery();
+        wrapper.eq(KwtWaybillOrderNode::getWOrderId, wOrderId);
+        if (wSubtaskId != null) {
+            wrapper.eq(KwtWaybillOrderNode::getWSubtaskId, wSubtaskId);
+        }
+        wrapper.orderByAsc(KwtWaybillOrderNode::getCreateTime);
+        //return this.list(wrapper);
+        return list();
+    }
 }

+ 31 - 25
sckw-modules/sckw-transport/src/main/java/com/sckw/transport/service/KwtLogisticsConsignmentService.java

@@ -2885,36 +2885,42 @@ public class KwtLogisticsConsignmentService {
     public Boolean logisticOrderFinish(@Valid OrderFinishReq req) {
         log.info("物流订单-完结订单传递参数信息:{}", JSONObject.toJSONString(req));
         //查询物流订单
-        Long logOrderId = Long.parseLong(req.getLogisticOrderId());
-        KwtLogisticsOrder logisticsOrder =
-                logisticsOrderRepository.queryByLogisticsOrderId(logOrderId);
-        if (Objects.isNull(logisticsOrder)){
+        List<KwtLogisticsOrder> logisticsOrder =
+                logisticsOrderRepository.queryByLogisticsOrderIds(req.getLogisticOrderId());
+        if (CollectionUtils.isEmpty(logisticsOrder)){
             throw new BusinessException("未找到该物流订单信息");
         }
-        KwtLogisticsOrder updateLogisticsOrder = new KwtLogisticsOrder();
-        updateLogisticsOrder.setId(logOrderId);
-
-        if (!Arrays.asList(LogisticsOrderV1Enum.WAIT_DELIVERY.getCode(), LogisticsOrderV1Enum.IN_TRANSIT.getCode()).contains(logisticsOrder.getStatus())){
-            throw new BusinessException("该物流订单状态不能进行完结");
-        }
-        if (Objects.equals(logisticsOrder.getStatus(), LogisticsOrderV1Enum.WAIT_DELIVERY.getCode())){
-            updateLogisticsOrder.setStatus(LogisticsOrderV1Enum.HAVE_RECONCILED.getCode());
-            return logisticsOrderRepository.updateLogisticsOrder(updateLogisticsOrder);
-        }
+        List<KwtLogisticsOrder> logisticsOrders = Lists.newArrayList();
+        logisticsOrder.forEach(x->{
+            KwtLogisticsOrder updateLogisticsOrder = new KwtLogisticsOrder();
+            updateLogisticsOrder.setId(x.getId());
 
-        //查询物流订单下的所有运单
-        List<KwtWaybillOrderSubtask> waybillOrderSubtasks = waybillOrderSubtaskRepository.queryByLogId(logOrderId);
-        if (org.apache.commons.collections4.CollectionUtils.isNotEmpty(waybillOrderSubtasks) && Objects.equals(logisticsOrder.getStatus(), LogisticsOrderV1Enum.IN_TRANSIT.getCode())){
-            boolean b = waybillOrderSubtasks.stream().anyMatch(x -> !Objects.equals(x.getStatus(),
-                    CarWaybillV1Enum.WAIT_UNLOADING.getCode()) || !Objects.equals(x.getStatus(),
-                    CarWaybillV1Enum.APPROVAL_TREAT.getCode()));
-            if (b){
-                updateLogisticsOrder.setStatus(LogisticsOrderV1Enum.HAVE_FINISHED.getCode());
-            }else {
+            if (!Arrays.asList(LogisticsOrderV1Enum.WAIT_DELIVERY.getCode(), LogisticsOrderV1Enum.IN_TRANSIT.getCode()).contains(x.getStatus())){
+                throw new BusinessException("该物流订单状态不能进行完结,订单编号,{},订单id:{}",x.getLOrderNo(),x.getId());
+            }
+            if (Objects.equals(x.getStatus(), LogisticsOrderV1Enum.WAIT_DELIVERY.getCode())){
                 updateLogisticsOrder.setStatus(LogisticsOrderV1Enum.HAVE_RECONCILED.getCode());
+                logisticsOrders.add(updateLogisticsOrder);
+                return;
+               // return logisticsOrderRepository.updateLogisticsOrder(updateLogisticsOrder);
             }
-        }
-        return logisticsOrderRepository.updateLogisticsOrder(updateLogisticsOrder);
+
+            //查询物流订单下的所有运单
+            List<KwtWaybillOrderSubtask> waybillOrderSubtasks = waybillOrderSubtaskRepository.queryByLogId(x.getId());
+            if (org.apache.commons.collections4.CollectionUtils.isNotEmpty(waybillOrderSubtasks) && Objects.equals(x.getStatus(), LogisticsOrderV1Enum.IN_TRANSIT.getCode())){
+                boolean b = waybillOrderSubtasks.stream()
+                        .anyMatch(y -> !Arrays.asList(CarWaybillV1Enum.WAIT_UNLOADING.getCode(),CarWaybillV1Enum.APPROVAL_TREAT.getCode()).contains(y.getStatus()));
+
+                if (b){
+                    updateLogisticsOrder.setStatus(LogisticsOrderV1Enum.HAVE_FINISHED.getCode());
+                    logisticsOrders.add(updateLogisticsOrder);
+                }else {
+                    updateLogisticsOrder.setStatus(LogisticsOrderV1Enum.HAVE_RECONCILED.getCode());
+                    logisticsOrders.add(updateLogisticsOrder);
+                }
+            }
+        });
+        return logisticsOrderRepository.updateBatchById(logisticsOrders);
     }
 
 

+ 193 - 0
sckw-modules/sckw-transport/src/main/java/com/sckw/transport/service/KwtWaybillOrderV1Service.java

@@ -45,6 +45,7 @@ import com.sckw.transport.model.enuma.ApproveStatusEnum;
 import com.sckw.transport.model.enuma.CarWaybillDetailEnum;
 import com.sckw.transport.model.enuma.CarWaybillNdexTopEnum;
 import com.sckw.transport.model.enuma.CarWaybillTableTopEnum;
+import com.sckw.transport.model.param.WaybillOrderNodeReq;
 import com.sckw.transport.model.param.WaybillOrderReq;
 import com.sckw.transport.model.param.WaybillOrderResp;
 import com.sckw.transport.model.vo.*;
@@ -125,6 +126,7 @@ public class KwtWaybillOrderV1Service {
     private final KwtWaybillOrderSubtaskRepository kwtWaybillOrderSubtaskRepository;
     private final KwtLogisticsOrderRepository kwtLogisticsOrderRepository;
     private final KwtWaybillOrderTicketRepository  kwtWaybillOrderTicketRepository;
+    private final KwtWaybillOrderNodeRepository kwtWaybillOrderNodeRepository;
     @Resource
     private StreamBridge streamBridge;
     @DubboReference(version = "1.0.0", group = "design", check = false)
@@ -3459,4 +3461,195 @@ public class KwtWaybillOrderV1Service {
         orderBillStatusStatistics.setOrderNum(String.valueOf(finalStausAndBillOrdersMap.getOrDefault(c.getCode(), new ArrayList<>()).size()));
         return orderBillStatusStatistics;
     }
+
+    /**
+     * 查询运单节点轨迹时间线
+     *
+     * @param req 请求参数
+     * @return 节点轨迹列表
+     * @author system
+     * @date 2025/06/24
+     */
+    public List<WaybillOrderNodeVo> queryWaybillOrderNodeTimeline(WaybillOrderNodeReq req) {
+        log.info("查询运单节点轨迹,请求参数:{}", JSON.toJSONString(req));
+        
+        // 查询节点轨迹数据
+        List<KwtWaybillOrderNode> nodes = kwtWaybillOrderNodeRepository.queryNodesByOrderId(
+                req.getWOrderId(), req.getWSubtaskId());
+        
+        if (CollectionUtils.isEmpty(nodes)) {
+            return new ArrayList<>();
+        }
+
+        // 查询运单子单信息,用于获取商品名称和重量
+        Set<Long> subtaskIds = nodes.stream()
+                .map(KwtWaybillOrderNode::getWSubtaskId)
+                .collect(Collectors.toSet());
+        List<KwtWaybillOrderSubtask> subtasks = kwtWaybillOrderSubtaskRepository.listByIds(subtaskIds);
+        Map<Long, KwtWaybillOrderSubtask> subtaskMap = subtasks.stream()
+                .collect(Collectors.toMap(KwtWaybillOrderSubtask::getId, Function.identity()));
+
+        // 查询物流订单商品信息
+        Set<Long> logisticsOrderIds = subtasks.stream()
+                .map(KwtWaybillOrderSubtask::getLOrderId)
+                .collect(Collectors.toSet());
+        List<KwtLogisticsOrderGoods> goodsList = kwtLogisticsOrderGoodsRepository.queryByLogOrderIds(
+                new ArrayList<>(logisticsOrderIds));
+        Map<Long, KwtLogisticsOrderGoods> goodsMap = goodsList.stream()
+                .collect(Collectors.toMap(KwtLogisticsOrderGoods::getLOrderId, Function.identity(), (k1, k2) -> k1));
+
+        // 查询过磅单据信息(如果有地磅)
+        Set<Long> nodeIds = nodes.stream()
+                .filter(n -> n.getWeighbridgeId() != null)
+                .map(KwtWaybillOrderNode::getId)
+                .collect(Collectors.toSet());
+        Map<Long, KwtWaybillOrderTicket> ticketMap = new HashMap<>();
+        if (!nodeIds.isEmpty()) {
+            // 这里假设根据wSubtaskId和地址ID查询
+            // 实际可能需要根据具体业务调整查询逻辑
+        }
+
+        // 单位字典
+        Map<String, Map<String, String>> dict = remoteSystemService.queryDictByType(
+                List.of(DictTypeEnum.UNIT_TYPE.getType()));
+        Map<String, String> unitMap = CollectionUtils.isNotEmpty(dict) ? 
+                dict.get(DictTypeEnum.UNIT_TYPE.getType()) : new HashMap<>(NumberConstant.SIXTEEN);
+
+        // 转换为VO对象
+        List<WaybillOrderNodeVo> voList = new ArrayList<>();
+        for (KwtWaybillOrderNode node : nodes) {
+            WaybillOrderNodeVo vo = new WaybillOrderNodeVo();
+            vo.setId(node.getId());
+            vo.setCreateTime(DateUtils.format(node.getCreateTime(), DateUtils.DATE_TIME_PATTERN));
+            vo.setOrderStatus(node.getOrderStatus());
+            vo.setTruckNo(node.getTruckNo());
+            vo.setDriverName(node.getDriverName());
+            vo.setWeighbridgeName(node.getWeighbridgeName());
+            vo.setLng(node.getLng());
+            vo.setLat(node.getLat());
+            vo.setHasWeighbridge(node.getWeighbridgeId() != null);
+
+            // 构建状态描述
+            KwtWaybillOrderSubtask subtask = subtaskMap.get(node.getWSubtaskId());
+            String statusDesc = buildStatusDescription(node, subtask, goodsMap, unitMap);
+            vo.setStatusDesc(statusDesc);
+
+            // 构建地址信息
+            String address = buildAddressFromLatLng(node.getLng(), node.getLat());
+            vo.setAddress(address);
+
+            voList.add(vo);
+        }
+
+        return voList;
+    }
+
+    /**
+     * 构建状态描述文本
+     * 格式:车辆【川A5478C】司机【张三】装载货物【铁矿石】【参运】【28.70吨】
+     */
+    private String buildStatusDescription(KwtWaybillOrderNode node, 
+                                          KwtWaybillOrderSubtask subtask,
+                                          Map<Long, KwtLogisticsOrderGoods> goodsMap,
+                                          Map<String, String> unitMap) {
+        StringBuilder sb = new StringBuilder();
+        
+        // 状态名称
+        String statusName = CarWaybillEnum.getAlias(node.getOrderStatus());
+        if (StringUtils.isNotBlank(statusName)) {
+            sb.append(statusName);
+        }
+        
+        // 车辆信息
+        if (StringUtils.isNotBlank(node.getTruckNo())) {
+            sb.append(" 车辆【").append(node.getTruckNo()).append("】");
+        }
+        
+        // 司机信息
+        if (StringUtils.isNotBlank(node.getDriverName())) {
+            sb.append(" 司机【").append(node.getDriverName()).append("】");
+        }
+        
+        // 货物信息
+        if (subtask != null) {
+            KwtLogisticsOrderGoods goods = goodsMap.get(subtask.getLOrderId());
+            if (goods != null && StringUtils.isNotBlank(goods.getGoodsName())) {
+                sb.append(" 装载货物【").append(goods.getGoodsName()).append("】");
+            }
+            
+            // 状态标签
+            String statusLabel = getStatusLabel(node.getOrderStatus());
+            if (StringUtils.isNotBlank(statusLabel)) {
+                sb.append(" 【").append(statusLabel).append("】");
+            }
+            
+            // 重量信息(根据状态显示不同的重量)
+            String weight = getWeightByStatus(node.getOrderStatus(), subtask);
+            String unit = unitMap.getOrDefault(subtask.getUnit(), "吨");
+            if (StringUtils.isNotBlank(weight)) {
+                sb.append(" 【").append(weight).append(unit).append("】");
+            }
+        }
+        
+        // 地磅信息
+        if (StringUtils.isNotBlank(node.getWeighbridgeName())) {
+            sb.append(" 地磅【").append(node.getWeighbridgeName()).append("】");
+        }
+        
+        return sb.toString();
+    }
+
+    /**
+     * 根据状态获取标签
+     */
+    private String getStatusLabel(Integer status) {
+        if (status == null) {
+            return "";
+        }
+        // 根据具体业务逻辑返回标签,如:参运、实装、实卸等
+        if (status.equals(CarWaybillEnum.COMPLETION_LOADING.getCode())) {
+            return "实装";
+        } else if (status.equals(CarWaybillEnum.COMPLETION_UNLOADING.getCode())) {
+            return "实卸";
+        } else if (status.equals(CarWaybillEnum.EXIT_COMPLETED.getCode())) {
+            return "参运";
+        }
+        return "";
+    }
+
+    /**
+     * 根据状态获取对应的重量
+     */
+    private String getWeightByStatus(Integer status, KwtWaybillOrderSubtask subtask) {
+        if (status == null || subtask == null) {
+            return "";
+        }
+        
+        // 根据状态返回不同的重量
+        if (status.equals(CarWaybillEnum.EXIT_COMPLETED.getCode())) {
+            // 参运显示委托量
+            return String.valueOf(subtask.getEntrustAmount());
+        } else if (status.equals(CarWaybillEnum.COMPLETION_LOADING.getCode())) {
+            // 装货显示实装量
+            return String.valueOf(subtask.getLoadAmount());
+        } else if (status.equals(CarWaybillEnum.COMPLETION_UNLOADING.getCode())) {
+            // 卸货显示实卸量
+            return String.valueOf(subtask.getUnloadAmount());
+        }
+        
+        return "";
+    }
+
+    /**
+     * 根据经纬度构建地址信息
+     * 实际项目中可以调用地图服务API进行逆地理编码
+     */
+    private String buildAddressFromLatLng(String lng, String lat) {
+        if (StringUtils.isBlank(lng) || StringUtils.isBlank(lat)) {
+            return "";
+        }
+        // 这里可以调用高德/百度等地图API进行逆地理编码
+        // 暂时返回经纬度格式
+        return String.format("经度:%s, 纬度:%s", lng, lat);
+    }
 }