Sfoglia il codice sorgente

铲车司机接单

donglang 5 giorni fa
parent
commit
3e6f1b668c

+ 45 - 0
sckw-common/sckw-common-core/src/main/java/com/sckw/core/model/enums/ForkliftStatusEnum.java

@@ -0,0 +1,45 @@
+package com.sckw.core.model.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+import java.util.EnumSet;
+
+/**
+ * @author lfdc
+ * @desc 车辆运单状态枚举
+ * @date 2023-07-14 14:07:23
+ */
+@Getter
+@AllArgsConstructor
+public enum ForkliftStatusEnum {
+
+    /**
+     * 已接单
+     */
+    ORDER_TAKING(1,  "已接单"),
+    /**
+     * 已完成
+     */
+    COMPLETED(5, "已完成"),
+
+    ;
+
+    private final Integer code;
+
+    private final String destination;
+
+
+    public static String geDesc(Integer status) {
+        if (status == null) {
+            return null;
+        }
+        for (ForkliftStatusEnum forkliftStatusEnum : EnumSet.allOf(ForkliftStatusEnum.class)) {
+            if (forkliftStatusEnum.code.equals(status)) {
+                return forkliftStatusEnum.destination;
+            }
+        }
+        return null;
+    }
+
+}

+ 46 - 0
sckw-common/sckw-common-core/src/main/java/com/sckw/core/model/enums/LoadingTypeEnum.java

@@ -0,0 +1,46 @@
+package com.sckw.core.model.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+import java.util.EnumSet;
+
+/**
+ * @author lfdc
+ * @desc 车辆运单状态枚举
+ * @date 2023-07-14 14:07:23
+ */
+@Getter
+@AllArgsConstructor
+public enum LoadingTypeEnum {
+
+    /**
+     * 装载
+     */
+    LOADING(0,  "装载"),
+
+    /**
+     * 补货
+     */
+    SUPPLEMENT(1, "补货"),
+
+    ;
+
+    private final Integer code;
+
+    private final String destination;
+
+
+    public static String geDesc(Integer status) {
+        if (status == null) {
+            return null;
+        }
+        for (LoadingTypeEnum loadingTypeEnum : EnumSet.allOf(LoadingTypeEnum.class)) {
+            if (loadingTypeEnum.code.equals(status)) {
+                return loadingTypeEnum.destination;
+            }
+        }
+        return null;
+    }
+
+}

+ 71 - 0
sckw-modules/sckw-transport/src/main/java/com/sckw/transport/controller/enterpriseApp/ForkliftAppController.java

@@ -0,0 +1,71 @@
+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.ForkliftOrderResp;
+import com.sckw.transport.model.param.forklift.request.ForkliftOrderQueryParam;
+import com.sckw.transport.model.param.forklift.request.ForkliftOrderTakingParam;
+import com.sckw.transport.service.app.ForkliftOrderService;
+import io.swagger.v3.oas.annotations.Operation;
+import jakarta.validation.Valid;
+import lombok.RequiredArgsConstructor;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * Author: donglang
+ * Time: 2026-01-05
+ * Des: 铲车司机Controller
+ * Version: 1.0
+ */
+
+@RestController
+@RequestMapping("/forklift")
+@RequiredArgsConstructor
+public class ForkliftAppController {
+
+    private final ForkliftOrderService forkliftOrderService;
+
+    /**
+     * 分页查询铲车运单
+     *
+     * @param param
+     * @return
+     */
+    @Operation(summary = "分页铲车运单", description = "分页查询铲车的运单")
+    @PostMapping("/pageQueryForkliftOrder")
+    public BaseResult<PageDataResult<ForkliftOrderResp>> pageQueryForkliftOrder(@RequestBody @Valid ForkliftOrderQueryParam param){
+        PageDataResult<ForkliftOrderResp> LogisticsOrderList = forkliftOrderService.pageQueryForkliftOrder(param);
+        return BaseResult.success(LogisticsOrderList);
+    }
+
+    /**
+     * 铲车接单
+     *
+     * @param param
+     * @return
+     */
+    @Operation(summary = "铲车接单", description = "铲车接单")
+    @PostMapping("/forkliftOrderTaking")
+    public BaseResult<PageDataResult<ForkliftOrderResp>> forkliftOrderTaking(@RequestBody @Valid ForkliftOrderTakingParam param){
+        forkliftOrderService.forkliftOrderTaking(param);
+        return BaseResult.success();
+    }
+
+    /**
+     * 铲车取消接单
+     *
+     * @param param
+     * @return
+     */
+    @Operation(summary = "铲车取消接单", description = "铲车取消接单")
+    @PostMapping("/cancelForkliftOrder")
+    public BaseResult<PageDataResult<ForkliftOrderResp>> cancelForkliftOrder(@RequestBody @Valid ForkliftOrderQueryParam param){
+        PageDataResult<ForkliftOrderResp> LogisticsOrderList = forkliftOrderService.cancelForkliftOrder(param);
+        return BaseResult.success(LogisticsOrderList);
+    }
+
+}

+ 16 - 0
sckw-modules/sckw-transport/src/main/java/com/sckw/transport/dao/KwtForkliftWaybillOrderMapper.java

@@ -0,0 +1,16 @@
+package com.sckw.transport.dao;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.sckw.transport.model.KwtForkliftWaybillOrder;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * @author zk
+ * @desc 铲车订单
+ * @date 2026/01/06
+ */
+@Mapper
+public interface KwtForkliftWaybillOrderMapper extends BaseMapper<KwtForkliftWaybillOrder> {
+
+
+}

+ 98 - 0
sckw-modules/sckw-transport/src/main/java/com/sckw/transport/model/KwtForkliftWaybillOrder.java

@@ -0,0 +1,98 @@
+package com.sckw.transport.model;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * @author lfdc
+ * @description 铲车订单
+ * @date 2026-01-06 15:10:15
+ */
+@Data
+@TableName("kwt_forklift_waybill_order")
+public class KwtForkliftWaybillOrder implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 3847964050204875986L;
+
+    /**
+     * 主键
+     */
+    private Long id;
+
+    /**
+     * 铲车订单编号
+     */
+    private String fOrderNo;
+
+    /**
+     * 企业id
+     */
+    private Long entId;
+
+    /**
+     * 物流运单id
+     */
+    private Long wOrderId;
+
+    /**
+     * 物流订单id
+     */
+    private Long lOrderId;
+
+    /**
+     * 装载类型(0装载,1补货)
+     */
+    private Integer loadingType;
+
+    /**
+     * 是否为当前车辆(1已结单、5已完成)
+     */
+    private Integer status;
+
+    /**
+     * 装载员id
+     */
+    private Long driverId;
+
+    /**
+     * 接单的装载员
+     */
+    private String driverName;
+
+    /**
+     * 装载员联系电话
+     */
+    private String driverPhone;
+
+    /**
+     * 装载完成时间
+     */
+    private Date finishTime;
+
+    /**
+     * 创建时间
+     */
+
+    private Date createTime;
+
+    /**
+     * 更新时间
+     */
+    private Date updateTime;
+
+    /**
+     * 创建人
+     */
+    private Long createUser;
+
+    /**
+     * 更新人
+     */
+    private Long updateUser;
+
+}

+ 212 - 0
sckw-modules/sckw-transport/src/main/java/com/sckw/transport/model/param/forklift/reponse/ForkliftOrderResp.java

@@ -0,0 +1,212 @@
+package com.sckw.transport.model.param.forklift.reponse;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.math.BigDecimal;
+
+/**
+ * @author :chenXiaoFei
+ * @version :1.0
+ * @description : 分页查询运单信息响应参数
+ * @create :2025-11-11 20:16:00
+ */
+@Data
+public class ForkliftOrderResp implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = -6153312023002477484L;
+
+    /**
+     * 运单id
+     */
+    @Schema(description = "运单id")
+    private Long id;
+
+    /**
+     * 物流订单id
+     */
+    @Schema(description = "物流订单id")
+    private String logisticsOrderId;
+
+    /**
+     * 物流订单编号
+     */
+    @Schema(description = "物流订单编号")
+    private String logisticsOrderNo;
+    /**
+     * 运单编号
+     */
+    @Schema(description = "运单编号")
+    private String waybillNo;
+
+    /**
+     * 托运单位id
+     */
+    @Schema(description = "托运单位id")
+    private String consignCompanyId;
+
+    /**
+     * 托运单位名称
+     */
+    @Schema(description = "托运单位名称")
+    private String consignCompanyName;
+
+    /**
+     * 商品id
+     */
+    @Schema(description = "商品id")
+    private String goodsId;
+
+    /**
+     * 商品名称
+     */
+    @Schema(description = "商品名称")
+    private String goodsName;
+
+    /**
+     * 任务量
+     */
+    @Schema(description = "任务量")
+    private BigDecimal taskNum;
+
+    /**
+     * 皮重
+     */
+    @Schema(description = "皮重")
+    private BigDecimal tareAmount;
+
+    /**
+     * 毛重
+     */
+    @Schema(description = "毛重")
+    private BigDecimal grossAmount;
+
+    /**
+     * 装货净重
+     */
+    @Schema(description = "装货净化重")
+    private BigDecimal loadingNetWeight;
+
+    /**
+     * 卸货净重
+     */
+    @Schema(description = "卸货净重")
+    private BigDecimal unloadingNetWeight;
+
+    /**
+     * 法定载重
+     */
+    @Schema(description = "法定载重")
+    private BigDecimal legalLoad;
+
+    /**
+     * 计费方式
+     */
+    @Schema(description = "计费方式")
+    private String chargeType;
+
+    /**
+     * 计费方式描述
+     */
+    @Schema(description = "计费方式描述")
+    private String chargeTypeDesc;
+
+    /**
+     * 司机id
+     */
+    @Schema(description = "司机id")
+    private Long driverId;
+
+    /**
+     * 司机id
+     */
+    @Schema(description = "司机id")
+    private String driverName;
+
+    /**
+     * 司机电话
+     */
+    @Schema(description = "司机id")
+    private String driverPhone;
+
+    /**
+     * 装货地址
+     */
+    @Schema(description = "装货地址")
+    private String loadAddress;
+
+    /**
+     * 经度(装货地址)
+     */
+    @Schema(description = "经度(装货地址)")
+    private String loadLng;
+
+    /**
+     * 纬度(装货地址)
+     */
+    @Schema(description = "纬度(装货地址)")
+    private String loadLat;
+
+    /**
+     * 卸货地址
+     */
+
+    @Schema(description = "卸货地址")
+    private String unloadAddress;
+
+    /**
+     * 经度(卸货地址)
+     */
+    @Schema(description = "经度(卸货地址)")
+    private String unloadLng;
+
+    /**
+     * 纬度(卸货地址)
+     */
+    @Schema(description = "经度(卸货地址)")
+    private String unloadLat;
+
+    /**
+     * 装卸货之间距离
+     */
+    @Schema(description = "装卸货之间距离")
+    private String distanceKm;
+
+    /**
+     * 派单时间
+     */
+    @Schema(description = "派单时间")
+    private String sendTime;
+
+    /**
+     * 派单人
+     */
+    @Schema(description = "派单人")
+    private String sendOperator;
+
+    /**
+     * 派单人电话
+     */
+    @Schema(description = "派单人电话")
+    private String sendPhone;
+
+    /**
+     * 状态
+     */
+    @Schema(description = "状态")
+    private Integer status;
+
+    /**
+     * 状态描述
+     */
+    @Schema(description = "状态描述")
+    private String statusDesc;
+    /**
+     * 订单余量
+     */
+    @Schema(description = "订单余量")
+    private String orderSurplus;
+}

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

@@ -0,0 +1,29 @@
+package com.sckw.transport.model.param.forklift.request;
+
+import com.sckw.core.web.request.PageReq;
+import io.swagger.v3.oas.annotations.media.Schema;
+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 ForkliftOrderQueryParam extends PageReq implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 8601314159466179906L;
+
+    /**
+     * 商品名称
+     */
+    @Schema(description = "商品名称")
+    private Long goodsName;
+
+
+}

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

@@ -0,0 +1,44 @@
+package com.sckw.transport.model.param.forklift.request;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+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 ForkliftOrderTakingParam implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 8601314159466179906L;
+
+    /**
+     * 运单id
+     */
+    @NotNull(message = "运单id不能为空")
+    @Schema(description = "运单id")
+    private Long waybillOrderId;
+
+
+    /**
+     * 装载类型
+     */
+    @NotNull(message = "装载类型不能为空")
+    @Schema(description = "装载类型")
+    private Integer LoadingType;
+
+    /**
+     * 铲车司机id
+     */
+    @NotNull(message = "铲车司机id不能为空")
+    @Schema(description = "铲车司机id")
+    private Long driverId;
+
+}

+ 31 - 0
sckw-modules/sckw-transport/src/main/java/com/sckw/transport/repository/KwtForkliftWaybillOrderRepository.java

@@ -0,0 +1,31 @@
+package com.sckw.transport.repository;
+
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.sckw.transport.dao.KwtForkliftWaybillOrderMapper;
+import com.sckw.transport.model.KwtForkliftWaybillOrder;
+import org.springframework.stereotype.Repository;
+
+/**
+ * @Author: 马超伟
+ * @CreateTime: 2025-10-12
+ * @Description:
+ * @Version: 1.0
+ */
+
+@Repository
+public class KwtForkliftWaybillOrderRepository extends ServiceImpl<KwtForkliftWaybillOrderMapper, KwtForkliftWaybillOrder> {
+
+    public KwtForkliftWaybillOrder queryForkliftWaybillOrderByWOrderId(Long wOrderId, Long driverId) {
+        return getOne(Wrappers.<KwtForkliftWaybillOrder>lambdaQuery()
+                .eq(KwtForkliftWaybillOrder::getWOrderId, wOrderId)
+                .eq(KwtForkliftWaybillOrder::getDriverId, driverId)
+                .eq(KwtForkliftWaybillOrder::getStatus,5)
+                .orderByDesc(KwtForkliftWaybillOrder::getId)
+                .last("limit 1"));
+    }
+
+
+  
+
+}

+ 13 - 4
sckw-modules/sckw-transport/src/main/java/com/sckw/transport/repository/KwtWaybillOrderRepository.java

@@ -11,13 +11,13 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.sckw.core.model.enums.CarWaybillEnum;
 import com.sckw.transport.dao.KwtWaybillOrderMapper;
 import com.sckw.transport.model.KwtWaybillOrder;
-import com.sckw.transport.model.vo.WaybillOrderSelectOptionVo;
-import jakarta.validation.constraints.NotBlank;
 import org.springframework.stereotype.Repository;
 
 import java.time.LocalDateTime;
-import java.util.*;
-import java.util.stream.Collectors;
+import java.util.Date;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
 
 /**
  * @Author: 马超伟
@@ -331,4 +331,13 @@ public class KwtWaybillOrderRepository extends ServiceImpl<KwtWaybillOrderMapper
                 .le(KwtWaybillOrder::getCreateTime, endDate)
         );
     }
+
+    public Page<KwtWaybillOrder> findPageByStatus(int pageNum, int pageSize) {
+        Page<KwtWaybillOrder> page = new Page<>(pageNum, pageSize);
+        LambdaQueryWrapper<KwtWaybillOrder> wrapper = Wrappers.<KwtWaybillOrder>lambdaQuery()
+                .eq(KwtWaybillOrder::getDelFlag, 0)
+                .eq(KwtWaybillOrder::getStatus, 5)
+                .orderByDesc(KwtWaybillOrder::getCreateTime);
+        return page(page,wrapper);
+    }
 }

+ 250 - 0
sckw-modules/sckw-transport/src/main/java/com/sckw/transport/service/app/ForkliftOrderService.java

@@ -0,0 +1,250 @@
+package com.sckw.transport.service.app;
+
+
+import com.alibaba.fastjson.JSON;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.sckw.core.common.enums.enums.ErrorCodeEnum;
+import com.sckw.core.exception.BusinessPlatfromException;
+import com.sckw.core.model.enums.ForkliftStatusEnum;
+import com.sckw.core.utils.CollectionUtils;
+import com.sckw.core.web.response.result.PageDataResult;
+import com.sckw.transport.model.KwtForkliftWaybillOrder;
+import com.sckw.transport.model.KwtWaybillOrder;
+import com.sckw.transport.model.param.forklift.reponse.ForkliftOrderResp;
+import com.sckw.transport.model.param.forklift.request.ForkliftOrderQueryParam;
+import com.sckw.transport.model.param.forklift.request.ForkliftOrderTakingParam;
+import com.sckw.transport.repository.KwtForkliftWaybillOrderRepository;
+import com.sckw.transport.repository.KwtWaybillOrderRepository;
+import jakarta.annotation.Resource;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.core.script.DefaultRedisScript;
+import org.springframework.stereotype.Service;
+
+import java.util.*;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Author: donglang
+ * Time: 2026-01-05
+ * Des: 铲车司机Service
+ * Version: 1.0
+ */
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class ForkliftOrderService {
+
+    private final KwtWaybillOrderRepository waybillOrderRepository;
+
+    private final KwtForkliftWaybillOrderRepository forkliftWaybillOrderRepository;
+
+
+    // 注入RedisTemplate用于分布式锁
+    @Resource
+    private RedisTemplate<String, String> redisTemplate;
+
+    // 分布式锁相关常量
+    private static final String TAKING_ORDER_LOCK_PREFIX = "transport:taking_order:lock:";
+    // 锁超时时间30秒
+    private static final long LOCK_EXPIRE_SECONDS = 30;
+    // 锁等待时间500毫秒
+    private static final long LOCK_WAIT_MILLIS = 500;
+    // 锁重试间隔100毫秒
+    private static final long LOCK_RETRY_INTERVAL = 100;
+
+    /**
+     * 分页查询铲车运单
+     * @param param
+     * @return
+     */
+    public PageDataResult<ForkliftOrderResp> pageQueryForkliftOrder(ForkliftOrderQueryParam param) {
+        log.info("查询铲车司机的物流运单:{}", JSON.toJSONString(param));
+        //查询到底装货点的运单
+        Page<KwtWaybillOrder> pageByStatus = waybillOrderRepository.findPageByStatus(param.getPageNum(), param.getPageSize());
+        List<KwtWaybillOrder> records = pageByStatus.getRecords();
+        if (CollectionUtils.isEmpty(records)) {
+            return PageDataResult.empty(param.getPageNum(), param.getPageSize());
+        }
+        List<ForkliftOrderResp> forkliftOrderList = new ArrayList<>();
+        for (KwtWaybillOrder record : records) {
+            ForkliftOrderResp forklift = new ForkliftOrderResp();
+            forklift.setDriverId(record.getDriverId());
+            forklift.setDriverName(record.getDriverName());
+            forkliftOrderList.add(forklift);
+        }
+        return PageDataResult.success(param.getPageNum(), param.getPageSize(), pageByStatus.getTotal(), forkliftOrderList);
+    }
+
+    /**
+     * 铲车接单
+     * @param param
+     * @return
+     */
+    public void forkliftOrderTaking(ForkliftOrderTakingParam param) {
+        log.info("铲车接单:{}", JSON.toJSONString(param));
+        //校验运单状态
+        KwtWaybillOrder waybillOrder = checkWOrderStatus(param);
+
+        // 1. 幂等性校验
+        checkIdempotent(param);
+
+        // 2. 构建分布式锁Key(物流运单ID+铲车司机id 唯一标识)
+        String lockKey = TAKING_ORDER_LOCK_PREFIX + param.getWaybillOrderId() + "_" + param.getDriverId();
+        String requestId = UUID.randomUUID().toString();
+        boolean lockAcquired = false;
+
+        try {
+            // 3. 获取分布式锁(带重试机制)
+            lockAcquired = acquireLock(lockKey, requestId, LOCK_EXPIRE_SECONDS, LOCK_WAIT_MILLIS);
+            if (!lockAcquired) {
+                log.warn("接单请求获取分布式锁失败,可能存在重复提交,param:{}", JSON.toJSONString(param));
+                throw new BusinessPlatfromException(ErrorCodeEnum.REPEAT_SUBMIT, "当前接单请求正在处理中,请稍后再试");
+            }
+
+            // 4. 再次幂等性校验(防止锁等待期间已经创建运单)
+            checkIdempotent(param);
+
+            // 5. 执行接单
+            createForkliftWaybillOrder(param, waybillOrder);
+
+        } finally {
+            // 6. 释放分布式锁
+            if (lockAcquired) {
+                releaseLock(lockKey, requestId);
+                log.info("释放接单分布式锁成功,lockKey:{}, requestId:{}", lockKey, requestId);
+            }
+        }
+    }
+
+    /**
+     * 校验运单状态
+     * @param param
+     */
+    private KwtWaybillOrder checkWOrderStatus(ForkliftOrderTakingParam param) {
+        //校验
+        KwtWaybillOrder waybillOrder = waybillOrderRepository.getById(param.getWaybillOrderId());
+        if (waybillOrder == null) {
+            throw new BusinessPlatfromException(ErrorCodeEnum.WAYBILL_ORDER_STATUS_ERROR, "当前运单不是可接单状态!");
+        }
+        return waybillOrder;
+    }
+
+
+    /**
+     * 幂等性校验:检查是否已经存在该铲车针对该物流运单的有效运单
+     * @param param 接单参数
+     */
+    private void checkIdempotent(ForkliftOrderTakingParam param) {
+        // 查询该物流运单+铲车司机id下是否已有接单状态运单
+        KwtForkliftWaybillOrder existingOrder = forkliftWaybillOrderRepository.queryForkliftWaybillOrderByWOrderId(param.getWaybillOrderId(), param.getDriverId());
+        if (existingOrder != null) {
+            log.warn("重复接单校验失败,已存在有效铲车订单,fLogOrderId:{}, driverId:{}, waybillOrderId:{}",
+                    existingOrder.getId(), param.getDriverId(), param.getWaybillOrderId());
+            throw new BusinessPlatfromException(ErrorCodeEnum.REPEAT_SUBMIT,
+                    String.format("该铲车[%s]已针对该物流运单接单,订单ID:%s", existingOrder.getDriverId(), existingOrder.getId()));
+        }
+    }
+
+    /**
+     * 获取分布式锁(带重试机制)
+     * @param lockKey       锁Key
+     * @param requestId     请求ID(保证只有自己能释放锁)
+     * @param expireSeconds 锁过期时间(秒)
+     * @param waitMillis    最大等待时间(毫秒)
+     * @return 是否获取成功
+     */
+    private boolean acquireLock(String lockKey, String requestId, long expireSeconds, long waitMillis) {
+        long startTime = System.currentTimeMillis();
+        while (System.currentTimeMillis() - startTime < waitMillis) {
+            // 使用SET NX EX命令获取锁
+            Boolean success = redisTemplate.opsForValue().setIfAbsent(lockKey, requestId, expireSeconds, TimeUnit.SECONDS);
+            if (Boolean.TRUE.equals(success)) {
+                log.info("获取接单分布式锁成功,lockKey:{}, requestId:{}", lockKey, requestId);
+                return true;
+            }
+
+            // 未获取到锁,短暂休眠后重试
+            try {
+                Thread.sleep(LOCK_RETRY_INTERVAL);
+            } catch (InterruptedException e) {
+                Thread.currentThread().interrupt();
+                log.error("获取锁时线程中断", e);
+                return false;
+            }
+        }
+
+        log.warn("获取接单分布式锁超时,lockKey:{}, waitMillis:{}", lockKey, waitMillis);
+        return false;
+    }
+
+    /**
+     * 释放分布式锁(使用Lua脚本保证原子性)
+     * @param lockKey   锁Key
+     * @param requestId 请求ID
+     */
+    private void releaseLock(String lockKey, String requestId) {
+        String luaScript = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
+        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
+        redisScript.setScriptText(luaScript);
+        redisScript.setResultType(Long.class);
+
+        Long result = redisTemplate.execute(redisScript, Collections.singletonList(lockKey), requestId);
+        if (result == null || result == 0) {
+            log.warn("释放接单分布式锁失败,可能锁已过期或被其他请求释放,lockKey:{}, requestId:{}", lockKey, requestId);
+        }
+    }
+
+    /**
+     * 创建铲车订单
+     * @param param
+     * @param waybillOrder
+     */
+    private void createForkliftWaybillOrder(ForkliftOrderTakingParam param, KwtWaybillOrder waybillOrder) {
+        log.info("生成铲车订单,入参参数:{}", JSON.toJSONString(param));
+        KwtForkliftWaybillOrder forkliftWaybillOrder = new KwtForkliftWaybillOrder();
+        forkliftWaybillOrder.setFOrderNo("W" + System.currentTimeMillis());
+        forkliftWaybillOrder.setEntId(null);
+        forkliftWaybillOrder.setLOrderId(waybillOrder.getLOrderId());
+        forkliftWaybillOrder.setLoadingType(param.getLoadingType());
+        forkliftWaybillOrder.setStatus(ForkliftStatusEnum.ORDER_TAKING.getCode());
+        forkliftWaybillOrder.setDriverName(null);
+        forkliftWaybillOrder.setDriverPhone(null);
+        forkliftWaybillOrder.setFinishTime(new Date());
+        forkliftWaybillOrder.setCreateUser(null);
+        forkliftWaybillOrder.setUpdateUser(null);
+        forkliftWaybillOrderRepository.save(forkliftWaybillOrder);
+        log.info("创建铲车订单成功,订单ID:{}", waybillOrder.getId());
+    }
+
+
+
+
+    /**
+     * 铲车取消接单
+     * @param param
+     * @return
+     */
+    public PageDataResult<ForkliftOrderResp> cancelForkliftOrder(ForkliftOrderQueryParam param) {
+        log.info("查询铲车司机的物流运单:{}", JSON.toJSONString(param));
+        //查询到底装货点的运单
+        Page<KwtWaybillOrder> pageByStatus = waybillOrderRepository.findPageByStatus(param.getPageNum(), param.getPageSize());
+        List<KwtWaybillOrder> records = pageByStatus.getRecords();
+        if (CollectionUtils.isEmpty(records)) {
+            return PageDataResult.empty(param.getPageNum(), param.getPageSize());
+        }
+        List<ForkliftOrderResp> forkliftOrderList = new ArrayList<>();
+        for (KwtWaybillOrder record : records) {
+            ForkliftOrderResp forklift = new ForkliftOrderResp();
+            forklift.setDriverId(record.getDriverId());
+            forklift.setDriverName(record.getDriverName());
+            forkliftOrderList.add(forklift);
+        }
+        return PageDataResult.success(param.getPageNum(), param.getPageSize(), pageByStatus.getTotal(), forkliftOrderList);
+    }
+
+
+
+}

+ 20 - 0
sql/2026/01/2026_01_06_donglang_create.sql

@@ -0,0 +1,20 @@
+
+create table kwt_forklift_waybill_order
+(
+    id                              bigint          NOT NULL AUTO_INCREMENT COMMENT '主键',
+    f_order_no                      varchar(30)     NOT NULL DEFAULT '-1' COMMENT '编号',
+    ent_id                          bigint          NOT NULL DEFAULT '-1' COMMENT '企业id',
+    w_order_id                      bigint          NOT NULL DEFAULT '-1' COMMENT '物流运单id',
+    l_order_id                      bigint          NOT NULL DEFAULT '-1' COMMENT '物流订单id',
+    loading_type                    int             NOT NULL DEFAULT '0' COMMENT '装载类型(0装载,1补货)',
+    status                          int             NOT NULL DEFAULT '0' COMMENT '是否为当前车辆(1已结单、5已完成)',
+    driver_id                      bigint          NOT NULL DEFAULT '-1' COMMENT '装载员id',
+    driver_name                     varchar(40)     DEFAULT NULL default '' COMMENT '接单的装载员',
+    driver_phone                    varchar(20)     DEFAULT NULL default '' COMMENT '装载员联系电话',
+    finish_time                     datetime        NOT NULL DEFAULT '1000-01-01 00:00:00' COMMENT '装载完成时间',
+    create_time                     datetime        NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+    update_time                     datetime        NOT NULL DEFAULT CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP COMMENT '更新时间',
+    create_user                     bigint          NOT NULL DEFAULT '-1' COMMENT '创建人',
+    update_user                     bigint          NOT NULL DEFAULT '-1' COMMENT '更新人',
+    PRIMARY KEY (`id`) USING BTREE
+) comment '铲车司机运单';