Procházet zdrojové kódy

Merge remote-tracking branch 'origin/dev_20260630' into dev_20260630

xucaiqin před 1 dnem
rodič
revize
8272c27566

+ 27 - 0
sckw-modules/sckw-transport/src/main/java/com/sckw/transport/controller/enterpriseApp/AppGatekeeperController.java

@@ -4,7 +4,9 @@ package com.sckw.transport.controller.enterpriseApp;
 import com.sckw.core.web.response.BaseResult;
 import com.sckw.core.web.response.result.PageDataResult;
 import com.sckw.transport.model.param.forklift.reponse.GatekeeperOrderResp;
+import com.sckw.transport.model.param.forklift.request.GatekeeperOrderIsPassParam;
 import com.sckw.transport.model.param.forklift.request.GatekeeperOrderPassParam;
+import com.sckw.transport.model.param.forklift.request.GatekeeperOrderPassReasonParam;
 import com.sckw.transport.model.param.forklift.request.GatekeeperOrderQueryParam;
 import com.sckw.transport.model.vo.StatisticsWaybillResp;
 import com.sckw.transport.service.app.GatekeeperOrderService;
@@ -70,4 +72,29 @@ public class AppGatekeeperController {
         return BaseResult.success();
     }
 
+    /**
+     * 保存放行原因
+     *
+     * @param param
+     * @return
+     */
+    @Operation(summary = "保存放行原因", description = "保存放行原因")
+    @PostMapping("/savePassReason")
+    public BaseResult savePassReason(@RequestBody @Valid GatekeeperOrderPassReasonParam param){
+        gatekeeperOrderService.savePassReason(param);
+        return BaseResult.success();
+    }
+
+    /**
+     * 是否放行
+     *
+     * @param param
+     * @return
+     */
+    @Operation(summary = "放行", description = "放行")
+    @PostMapping("/isPass")
+    public BaseResult isPass(@RequestBody @Valid GatekeeperOrderIsPassParam param){
+        Boolean isPass = gatekeeperOrderService.checkIsPass(param);
+        return BaseResult.success(isPass);
+    }
 }

+ 13 - 1
sckw-modules/sckw-transport/src/main/java/com/sckw/transport/controller/enterpriseApp/AppWayBillController.java

@@ -5,6 +5,7 @@ import com.sckw.core.web.constant.HttpStatus;
 import com.sckw.core.web.response.BaseResult;
 import com.sckw.core.web.response.HttpResult;
 import com.sckw.core.web.response.result.PageDataResult;
+import com.sckw.transport.model.WaybillOrderWeighImageParam;
 import com.sckw.transport.model.dto.WaybillListAppDTO;
 import com.sckw.transport.model.param.*;
 import com.sckw.transport.model.vo.OrderTotalTakeVo;
@@ -390,5 +391,16 @@ public class AppWayBillController {
         return BaseResult.success();
     }
 
-
+    /**
+     * 推送过磅图片
+     *
+     * @param param
+     * @return
+     */
+    @Operation(summary = "推送过磅图片", description = "推送过磅图片")
+    @PostMapping("/weighImage")
+    public BaseResult weighImage(@RequestBody @Valid WaybillOrderWeighImageParam param){
+        waybillOrderService.weighImage(param);
+        return BaseResult.success();
+    }
 }

+ 10 - 0
sckw-modules/sckw-transport/src/main/java/com/sckw/transport/model/KwtWaybillOrder.java

@@ -188,6 +188,16 @@ public class KwtWaybillOrder implements Serializable {
      */
     private Integer label;
 
+    /**
+     * 放行原因
+     */
+    private String passReason;
+
+    /**
+     * 放行图片地址
+     */
+    private String passUrl;
+
     //用于存储动态状态
     @TableField(exist = false)
     private Integer targetStatus;

+ 37 - 0
sckw-modules/sckw-transport/src/main/java/com/sckw/transport/model/WaybillOrderWeighImageParam.java

@@ -0,0 +1,37 @@
+package com.sckw.transport.model;
+
+import com.sckw.transport.model.param.WaybillOrderProcessParam;
+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 :donglang
+ * @version :1.0
+ * @description : 车辆过磅入参信息
+ * @create :2025-11-13 08:59:00
+ */
+@Data
+public class WaybillOrderWeighImageParam extends WaybillOrderProcessParam implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1038619782660342061L;
+
+    /**
+     * 车辆号
+     */
+    @Schema(description = "车辆号")
+    private String truckNo;
+
+    /**
+     * 称重图片
+     */
+    @NotEmpty(message = "称重图片不能为空!")
+    @Schema(description = "称重图片")
+    private List<String> weighImageList;
+
+}

+ 29 - 0
sckw-modules/sckw-transport/src/main/java/com/sckw/transport/model/param/forklift/request/GatekeeperOrderIsPassParam.java

@@ -0,0 +1,29 @@
+package com.sckw.transport.model.param.forklift.request;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * @author :donglang
+ * @version :1.0
+ * @description :
+ * @create :2026-01-05 08:59:00
+ */
+@Data
+public class GatekeeperOrderIsPassParam implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = -767569553526384839L;
+
+    /**
+     * 车牌号
+     */
+    @Schema(description = "车牌号")
+    @NotBlank(message = "车牌号不能为空")
+    private String truckNo;
+
+}

+ 61 - 0
sckw-modules/sckw-transport/src/main/java/com/sckw/transport/model/param/forklift/request/GatekeeperOrderPassReasonParam.java

@@ -0,0 +1,61 @@
+ package com.sckw.transport.model.param.forklift.request;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+ /**
+  * @author :donglang
+  * @version :1.0
+  * @description :
+  * @create :2026-01-05 08:59:00
+  */
+ @Data
+ public class GatekeeperOrderPassReasonParam implements Serializable {
+
+     @Serial
+     private static final long serialVersionUID = 8601314159466179906L;
+
+     /**
+      * 门卫订单id
+      */
+     @NotNull(message = "门卫订单id不能为空")
+     @Schema(description = "门卫订单id")
+     private Long id;
+
+     /**
+      * 门卫id
+      */
+     @NotNull(message = "门卫id不能为空")
+     @Schema(description = "门卫id")
+     private Long gatekeeperUserId;
+
+     /**
+      * 门卫姓名
+      */
+     @NotBlank(message = "门卫姓名不能为空")
+     @Schema(description = "门卫姓名")
+     private String gatekeeperName;
+
+     /**
+      * 放行原因
+      */
+     @NotBlank(message = "放行原因不能为空")
+     @Schema(description = "放行原因")
+     private String passReason;
+
+     /**
+      * 放行图片地址
+      */
+     @NotBlank(message = "放行图片地址不能为空")
+     @Schema(description = "放行图片地址")
+     private String passUrl;
+
+
+
+
+ }

+ 6 - 1
sckw-modules/sckw-transport/src/main/java/com/sckw/transport/repository/KwtWaybillOrderRepository.java

@@ -352,5 +352,10 @@ public class KwtWaybillOrderRepository extends ServiceImpl<KwtWaybillOrderMapper
                 .orderByDesc(KwtWaybillOrder::getCreateTime);
         return page(page,wrapper);
     }
-
+    public List<KwtWaybillOrder> findWbOrderByTruckNo(String truckNo) {
+        return list(Wrappers.<KwtWaybillOrder>lambdaQuery()
+                .eq(KwtWaybillOrder::getDelFlag,0)
+                .eq(KwtWaybillOrder::getTruckNo,truckNo)
+                .orderByDesc(KwtWaybillOrder::getId));
+    }
 }

+ 122 - 0
sckw-modules/sckw-transport/src/main/java/com/sckw/transport/service/app/GatekeeperOrderService.java

@@ -5,6 +5,7 @@ import com.alibaba.fastjson.JSON;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.google.common.collect.Lists;
 import com.sckw.contract.api.RemoteContractService;
 import com.sckw.core.common.enums.enums.ErrorCodeEnum;
 import com.sckw.core.exception.BusinessPlatfromException;
@@ -21,7 +22,9 @@ import com.sckw.product.api.dubbo.GoodsInfoService;
 import com.sckw.system.api.RemoteSystemService;
 import com.sckw.transport.model.*;
 import com.sckw.transport.model.param.forklift.reponse.GatekeeperOrderResp;
+import com.sckw.transport.model.param.forklift.request.GatekeeperOrderIsPassParam;
 import com.sckw.transport.model.param.forklift.request.GatekeeperOrderPassParam;
+import com.sckw.transport.model.param.forklift.request.GatekeeperOrderPassReasonParam;
 import com.sckw.transport.model.param.forklift.request.GatekeeperOrderQueryParam;
 import com.sckw.transport.model.vo.StatisticsWaybillResp;
 import com.sckw.transport.repository.*;
@@ -30,6 +33,7 @@ import lombok.extern.slf4j.Slf4j;
 import org.apache.dubbo.config.annotation.DubboReference;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
 
 import java.util.*;
 import java.util.concurrent.ConcurrentHashMap;
@@ -668,6 +672,124 @@ public class GatekeeperOrderService {
         log.info("[门卫放行]记录节点轨迹成功,节点ID:{}", node.getId());
     }
 
+    /**
+     * 校验是否可放行
+     * @param gatekeeper
+     */
+    /**
+     * 保存放行原因并执行门卫放行操作
+     * <p>
+     * 业务流程:
+     * 1. 校验并保存门卫放行的具体原因及凭证图片到运单表
+     * 2. 构建放行参数,调用核心放行逻辑,更新门卫订单状态及运单状态,并记录轨迹
+     *
+     * @param gatekeeper 包含订单ID、放行原因、放行凭证URL及操作人信息的参数对象
+     */
+    public void savePassReason(GatekeeperOrderPassReasonParam gatekeeper) {
+
+        // 1. 保存放行原因及凭证至运单表
+        saveGatePassReason(gatekeeper);
+
+        // 2. 执行门卫放行核心逻辑
+        // 构建放行参数,传递必要的身份标识
+        GatekeeperOrderPassParam passParam = new GatekeeperOrderPassParam();
+        passParam.setId(gatekeeper.getId());
+        passParam.setGatekeeperUserId(gatekeeper.getGatekeeperUserId());
+        passParam.setGatekeeperName(gatekeeper.getGatekeeperName());
+        
+        // 调用放行方法,该方法内部会更新门卫订单状态、运单状态、子任务状态并创建节点轨迹
+        pass(passParam);
+        
+        log.info("[门卫放行]带原因放行处理完成,订单ID:{}", gatekeeper.getId());
+    }
+    /**
+     * 保存放行原因
+     * @param gatekeeper
+     */
+    private void saveGatePassReason(GatekeeperOrderPassReasonParam gatekeeper) {
+        log.info("[门卫放行原因]保存放行原因, param:{}", JSON.toJSONString(gatekeeper));
+        KwtGatekeeperWaybillOrder gatekeeperWaybillOrder = gatekeeperWaybillOrderRepository.getById(gatekeeper.getId());
+        if (gatekeeperWaybillOrder == null) {
+            throw new BusinessPlatfromException(ErrorCodeEnum.GATEKEEPER_ORDER_NOT_FOUND, "[门卫放行原因]门卫订单不存在!");
+        }
+        KwtWaybillOrder waybillOrder = waybillOrderRepository.getById(gatekeeperWaybillOrder.getWOrderId());
+        if (waybillOrder == null) {
+            throw new BusinessPlatfromException(ErrorCodeEnum.WAYBILL_ORDER_NOT_FOUND, "[门卫放行原因]运单不存在!");
+        }
+        waybillOrder.setPassReason(gatekeeper.getPassReason());
+        waybillOrder.setPassUrl(gatekeeper.getPassUrl());
+        waybillOrderRepository.updateById(waybillOrder);
+        log.info("[门卫放行原因]保存放行原因完成!");
+    }
 
+    /**
+     * 校验车辆是否允许放行
+     * <p>
+     * 业务逻辑:
+     * 1. 校验车牌号非空
+     * 2. 根据车牌号查询最新的运单记录
+     * 3. 根据运单ID查询对应的门卫订单记录
+     * 4. 判断门卫订单状态是否在允许的放行状态列表中
+     *
+     * @param gatekeeper 包含车牌号的校验参数
+     * @return true-允许放行, false-不允许放行
+     */
+    public Boolean checkIsPass(GatekeeperOrderIsPassParam gatekeeper) {
+        log.info("[门卫校验]开始校验车辆是否可放行,入参:{}", JSON.toJSONString(gatekeeper));
+
+        // 1. 参数校验:车牌号不能为空
+        if (StringUtils.isBlank(gatekeeper.getTruckNo())) {
+            log.warn("[门卫校验]校验失败,车牌号为空");
+            throw new BusinessPlatfromException(ErrorCodeEnum.PARAM_ERROR, "车牌号不能为空!");
+        }
+
+        // 2. 根据车牌号查询运单列表
+        List<KwtWaybillOrder> wbOrderByTruckNo = waybillOrderRepository.findWbOrderByTruckNo(gatekeeper.getTruckNo());
+        if (CollectionUtils.isEmpty(wbOrderByTruckNo)) {
+            log.info("[门卫校验]校验结果:未找到该车牌[{}]对应的运单记录,禁止放行", gatekeeper.getTruckNo());
+            return Boolean.FALSE;
+        }
+
+        // 获取最新的一条运单数据(假设列表已按时间倒序排列或取第一条为最新)
+        KwtWaybillOrder waybillOrder = wbOrderByTruckNo.get(0);
+        log.debug("[门卫校验]找到对应运单,运单ID:{},运单号:{}", waybillOrder.getId(), waybillOrder.getWOrderNo());
+
+        // 3. 根据运单ID查询门卫订单
+        List<KwtGatekeeperWaybillOrder> gatekeeperWaybillOrders = gatekeeperWaybillOrderRepository
+                .queryGatekeeperWaybillOrderByWOrderIds(Lists.newArrayList(waybillOrder.getId()));
+        
+        if (CollectionUtils.isEmpty(gatekeeperWaybillOrders)) {
+            log.info("[门卫校验]校验结果:运单ID[{}]未关联门卫订单,禁止放行", waybillOrder.getId());
+            return Boolean.FALSE;
+        }
+
+        // 获取对应的门卫订单
+        KwtGatekeeperWaybillOrder gatekeeperWaybillOrder = gatekeeperWaybillOrders.get(0);
+        Integer currentStatus = gatekeeperWaybillOrder.getStatus();
+        log.debug("[门卫校验]找到对应门卫订单,订单ID:{},当前状态:{}", gatekeeperWaybillOrder.getId(), currentStatus);
+
+        // 4. 定义允许放行的状态集合
+        // PENDING_ENTRY: 待进场 (可能用于重新入场或特殊流程)
+        // READY_RELEASE: 已放行/待离场 (正常放行流程)
+        // EXITED: 已离场 (可能用于补录或异常处理)
+        // EMPTY_EXITED: 空车离场
+        List<Integer> PASS_STATUSES = Arrays.asList(
+                GatekeeperStatusEnum.PENDING_ENTRY.getCode(),
+                GatekeeperStatusEnum.READY_RELEASE.getCode(),
+                GatekeeperStatusEnum.EXITED.getCode(),
+                GatekeeperStatusEnum.EMPTY_EXITED.getCode()
+        );
+
+        // 5. 判断当前状态是否允许放行
+        boolean isAllowed = PASS_STATUSES.contains(currentStatus);
+        
+        if (isAllowed) {
+            log.info("[门卫校验]校验通过,车牌[{}]当前状态[{}]在允许放行列表中", gatekeeper.getTruckNo(), currentStatus);
+        } else {
+            log.info("[门卫校验]校验拒绝,车牌[{}]当前状态[{}]不在允许放行列表中", gatekeeper.getTruckNo(), currentStatus);
+        }
+
+        return isAllowed;
+    }
 
 }

+ 77 - 0
sckw-modules/sckw-transport/src/main/java/com/sckw/transport/service/app/WaybillOrderService.java

@@ -2132,5 +2132,82 @@ public class WaybillOrderService {
 //        log.info("创建车辆运单节点轨迹,节点轨迹ID:{}", orderNode.getId());
 //    }
 
+    /**
+     * 过磅推送图片保存
+     * <p>
+     * 业务逻辑:
+     * 1. 校验车牌号非空。
+     * 2. 根据车牌号和允许的状态列表查询当前有效的运单。
+     *    - 允许的状态包括:待派车、已入场、已装货离场、空载待离场、待离场、卸货中、补货中、补货完成、待装车。
+     *    - 这些状态代表车辆正在作业流程中,允许上传过磅图片。
+     * 3. 校验运单存在且唯一(防止一车多单冲突)。
+     * 4. 查询当前运单状态对应的节点记录。
+     * 5. 将传入的图片URL列表拼接后保存到节点记录的 weighUrl 字段。
+     *
+     * @param param 过磅图片参数,包含车牌号和图片URL列表
+     */
+    public void weighImage(WaybillOrderWeighImageParam param) {
+        log.info("[过磅图片]开始处理,入参:{}", JSON.toJSONString(param));
+
+        // 1. 基础参数校验
+        if (StringUtils.isBlank(param.getTruckNo())) {
+            log.warn("[过磅图片]车牌号为空,请求被拒绝");
+            throw new BusinessPlatfromException(ErrorCodeEnum.PARAM_ERROR, "车牌号不能为空!");
+        }
+
+        // 2. 定义允许上传过磅图片的运单状态集合
+        // 涵盖从接单到卸货完成前的主要作业状态,确保只有在有效作业期间的运单才能接收图片
+        List<Integer> ALLOWED_STATUSES = Arrays.asList(
+                CarWaybillV1Enum.PENDING_VEHICLE.getCode(),      // 待派车
+                CarWaybillV1Enum.REFUSE_TRAFFIC.getCode(),       // 已入场(待过皮重)
+                CarWaybillV1Enum.EXIT_COMPLETED.getCode(),       // 已装货离场(待过毛重)
+                CarWaybillV1Enum.EMPTY_WAIT_LEAVE.getCode(),     // 空载待离场
+                CarWaybillV1Enum.WAIT_LEAVE.getCode(),           // 待离场
+                CarWaybillV1Enum.UNLOADING.getCode(),            // 卸货中
+                CarWaybillV1Enum.REPLENISHING.getCode(),         // 补货中
+                CarWaybillV1Enum.REPLENISH_FINISH.getCode(),     // 补货完成
+                CarWaybillV1Enum.WAIT_LOADING.getCode()          // 待装车
+                // CarWaybillV1Enum.UNLOADING_POINT.getCode()   // 如需支持卸货点状态可在此添加
+        );
+
+        // 3. 查询当前车辆下处于允许状态的运单
+        List<KwtWaybillOrder> wbOrderByTruckNo = waybillOrderRepository.findWbOrderByTruckNoAndStatus(param.getTruckNo(), ALLOWED_STATUSES);
+
+        // 4. 校验运单是否存在
+        if (CollectionUtils.isEmpty(wbOrderByTruckNo)) {
+            log.warn("[过磅图片]未找到符合条件的运单,truckNo: {}, 允许状态: {}", param.getTruckNo(), ALLOWED_STATUSES);
+            throw new BusinessPlatfromException(ErrorCodeEnum.WAYBILL_ORDER_NOT_FOUND, "[过磅推送图片]当前车辆没有可以过磅称重状态的运单");
+        }
+
+        // 5. 校验运单唯一性,避免一车多单导致的数据混乱
+        if (wbOrderByTruckNo.size() > 1) {
+            log.error("[过磅图片]当前车辆存在多条有效运单,数据异常,truckNo: {}, 运单数量: {}", param.getTruckNo(), wbOrderByTruckNo.size());
+            throw new BusinessPlatfromException(ErrorCodeEnum.WAYBILL_ORDER_NOT_FOUND, "[过磅推送图片]当前车辆过磅称重状态存在多条运单");
+        }
+
+        KwtWaybillOrder waybillOrder = wbOrderByTruckNo.get(0);
+        log.debug("[过磅图片]匹配到运单,waybillOrderId: {}, status: {}", waybillOrder.getId(), waybillOrder.getStatus());
+
+        // 6. 查询该运单在当前状态下的节点记录
+        KwtWaybillOrderNode waybillOrderNode = waybillOrderNodeRepository.queryNodesByOrderId(waybillOrder.getId(), waybillOrder.getStatus());
+        if (waybillOrderNode == null) {
+            log.error("[过磅图片]未查询到运单节点数据,waybillOrderId: {}, status: {}", waybillOrder.getId(), waybillOrder.getStatus());
+            throw new BusinessPlatfromException(ErrorCodeEnum.WAYBILL_ORDER_NOT_FOUND, "[过磅推送图片]未查询运单节点数据");
+        }
+
+        // 7. 处理图片URL列表,转换为逗号分隔的字符串
+        String weighImageUrlStr = "";
+        if (CollectionUtils.isNotEmpty(param.getWeighImageList())) {
+            weighImageUrlStr = String.join(",", param.getWeighImageList());
+        }
+
+        // 8. 更新节点记录中的过磅图片URL
+        waybillOrderNode.setWeighUrl(weighImageUrlStr);
+        waybillOrderNodeRepository.updateById(waybillOrderNode);
+
+        log.info("[过磅图片]保存成功,waybillOrderId: {}, nodeId: {}, imageUrlCount: {}", 
+                waybillOrder.getId(), waybillOrderNode.getId(), param.getWeighImageList() == null ? 0 : param.getWeighImageList().size());
+    }
+
 
 }

+ 3 - 0
sql/2026/06/2026_06_16_kwt_waybill_order.sql

@@ -0,0 +1,3 @@
+ALTER TABLE kwt_waybill_order
+    ADD COLUMN pass_reason VARCHAR(255) NULL COMMENT '放行原因',
+    ADD COLUMN pass_url VARCHAR(255) NULL COMMENT '放行图片地址';