Jelajahi Sumber

赠送行程提交

jinch 4 jam lalu
induk
melakukan
ffe1e8af5a
20 mengubah file dengan 1400 tambahan dan 8 penghapusan
  1. 2 1
      ship-module-trade/ship-module-trade-api/src/main/java/com/yc/ship/module/trade/enums/ErrorCodeConstants.java
  2. 248 3
      ship-module-trade/ship-module-trade-biz/src/main/java/com/yc/ship/module/trade/controller/admin/orderjzdetail/OrderJzDetailController.java
  3. 44 0
      ship-module-trade/ship-module-trade-biz/src/main/java/com/yc/ship/module/trade/controller/admin/orderjzdetail/vo/OrderJzDetailExportVO.java
  4. 12 0
      ship-module-trade/ship-module-trade-biz/src/main/java/com/yc/ship/module/trade/controller/admin/orderjzdetail/vo/OrderJzDetailPageReqVO.java
  5. 9 0
      ship-module-trade/ship-module-trade-biz/src/main/java/com/yc/ship/module/trade/controller/admin/orderjzdetail/vo/OrderJzDetailRespVO.java
  6. 76 0
      ship-module-trade/ship-module-trade-biz/src/main/java/com/yc/ship/module/trade/controller/admin/orderjzdetail/vo/OrderJzDispatchBatchReqVO.java
  7. 58 0
      ship-module-trade/ship-module-trade-biz/src/main/java/com/yc/ship/module/trade/controller/admin/orderjzdetail/vo/OrderJzDispatchPageReqVO.java
  8. 51 0
      ship-module-trade/ship-module-trade-biz/src/main/java/com/yc/ship/module/trade/controller/admin/orderjzdetail/vo/OrderJzDispatchRecordRespVO.java
  9. 60 0
      ship-module-trade/ship-module-trade-biz/src/main/java/com/yc/ship/module/trade/controller/admin/orderjzdetail/vo/OrderJzDispatchRespVO.java
  10. 59 0
      ship-module-trade/ship-module-trade-biz/src/main/java/com/yc/ship/module/trade/controller/admin/orderjzdetail/vo/OrderJzDispatchSaveReqVO.java
  11. 37 0
      ship-module-trade/ship-module-trade-biz/src/main/java/com/yc/ship/module/trade/dal/dataobject/orderjzdetail/OrderJzDetailDO.java
  12. 97 0
      ship-module-trade/ship-module-trade-biz/src/main/java/com/yc/ship/module/trade/dal/dataobject/orderjzdispatch/OrderJzDispatchDO.java
  13. 10 0
      ship-module-trade/ship-module-trade-biz/src/main/java/com/yc/ship/module/trade/dal/mysql/orderjzdetail/OrderJzDetailMapper.java
  14. 56 0
      ship-module-trade/ship-module-trade-biz/src/main/java/com/yc/ship/module/trade/dal/mysql/orderjzdispatch/OrderJzDispatchMapper.java
  15. 20 0
      ship-module-trade/ship-module-trade-biz/src/main/java/com/yc/ship/module/trade/service/orderjzdetail/OrderJzDetailService.java
  16. 28 0
      ship-module-trade/ship-module-trade-biz/src/main/java/com/yc/ship/module/trade/service/orderjzdetail/OrderJzDetailServiceImpl.java
  17. 73 0
      ship-module-trade/ship-module-trade-biz/src/main/java/com/yc/ship/module/trade/service/orderjzdetail/OrderJzDispatchService.java
  18. 188 0
      ship-module-trade/ship-module-trade-biz/src/main/java/com/yc/ship/module/trade/service/orderjzdetail/OrderJzDispatchServiceImpl.java
  19. 170 4
      ship-module-trade/ship-module-trade-biz/src/main/resources/mapper/orderjzdetail/OrderJzDetailMapper.xml
  20. 102 0
      ship-module-trade/ship-module-trade-biz/src/main/resources/mapper/orderjzdispatch/OrderJzDispatchMapper.xml

+ 2 - 1
ship-module-trade/ship-module-trade-api/src/main/java/com/yc/ship/module/trade/enums/ErrorCodeConstants.java

@@ -170,6 +170,7 @@ public interface ErrorCodeConstants {
     ErrorCode YD_CHECK_ERROR = new ErrorCode(30_122, "检票失败,优待票请走人工通道");
     ErrorCode CHECK_PARAM_LOCK_ERROR = new ErrorCode(30_123, "未获取到核销参数锁,核销失败");
 
-
+    // ========== 订单分车相关 ==========
+    ErrorCode ORDER_JZ_DISPATCH_NOT_EXISTS = new ErrorCode(30_124, "订单分车记录不存在");
 
 }

+ 248 - 3
ship-module-trade/ship-module-trade-biz/src/main/java/com/yc/ship/module/trade/controller/admin/orderjzdetail/OrderJzDetailController.java

@@ -12,9 +12,7 @@ import com.yc.ship.module.resource.dal.dataobject.route.ResourceRouteDO;
 import com.yc.ship.module.resource.service.route.ResourceRouteService;
 import com.yc.ship.module.system.api.dict.DictDataApi;
 import com.yc.ship.module.system.api.dict.dto.DictDataRespDTO;
-import com.yc.ship.module.trade.controller.admin.orderjzdetail.vo.OrderJzDetailPageReqVO;
-import com.yc.ship.module.trade.controller.admin.orderjzdetail.vo.OrderJzDetailRespVO;
-import com.yc.ship.module.trade.controller.admin.orderjzdetail.vo.OrderJzDetailSaveReqVO;
+import com.yc.ship.module.trade.controller.admin.orderjzdetail.vo.*;
 import com.yc.ship.module.trade.dal.dataobject.orderjzdetail.OrderJzDetailDO;
 import com.yc.ship.module.trade.service.orderjzdetail.OrderJzDetailService;
 import io.swagger.v3.oas.annotations.Operation;
@@ -182,6 +180,83 @@ public class OrderJzDetailController {
     return success(BeanUtils.toBean(pageResult, OrderJzDetailRespVO.class));
   }
 
+  @GetMapping("/visitor/export-excel")
+  @Operation(summary = "导出游客接站明细 Excel")
+  @ApiAccessLog(operateType = EXPORT)
+  @PreAuthorize("@ss.hasPermission('trade:order-jz-detail-sign:query')")
+  public void exportVisitorExcel(@Valid OrderJzDetailPageReqVO pageReqVO,
+                                 HttpServletResponse response) throws IOException {
+    if (StringUtils.isEmpty(pageReqVO.getVoyageId())) {
+      throw new RuntimeException("航次编号不能为空");
+    }
+    VoyageDO voyageDO = voyageService.getVoyage(Long.valueOf(pageReqVO.getVoyageId()));
+    if (voyageDO == null) {
+      throw new RuntimeException("航次信息不能为空");
+    }
+
+    // 查询导出数据(条件与visitor/query一致)
+    List<OrderJzDetailDO> list = orderJzDetailService.getVisitorExportList(pageReqVO);
+
+    // 获取字典数据
+    List<DictDataRespDTO> dictAddressList = null;
+    ResourceRouteDO resourceRouteDO = resourceRouteService.getRoute(voyageDO.getRouteId());
+    if (resourceRouteDO != null) {
+      int dir = resourceRouteDO.getDirection().intValue();
+      if (dir == 2) {
+        dictAddressList = dictDataApi.getDictDataList("order_jz_x");
+      } else {
+        dictAddressList = dictDataApi.getDictDataList("order_jz");
+      }
+    }
+    final List<DictDataRespDTO> dictTimeList = dictDataApi.getDictDataList("order_time_jz");
+
+    // 转换为导出 VO
+    List<OrderJzDetailExportVO> exportList = new java.util.ArrayList<>();
+    final List<DictDataRespDTO> finalAddressList = dictAddressList;
+    for (int i = 0; i < list.size(); i++) {
+      OrderJzDetailDO detail = list.get(i);
+      OrderJzDetailExportVO vo = new OrderJzDetailExportVO();
+      vo.setIndex(i + 1);
+      vo.setOrderNo(detail.getOrderNo());
+      vo.setName(detail.getName());
+      vo.setPhone(detail.getPhone());
+      vo.setRoomModelName(detail.getRoomModelName());
+      vo.setRemark(detail.getRemark());
+      vo.setSignTime(detail.getSignTime());
+      vo.setDispatchNo(detail.getDispatchNo());
+      vo.setReservationInfo(detail.getRemark());
+
+      // 站点字典转义
+      if (finalAddressList != null && detail.getAddress() != null) {
+        final String addr = detail.getAddress();
+        DictDataRespDTO matched = finalAddressList.stream()
+          .filter(d -> d.getValue().equals(addr)).findFirst().orElse(null);
+        if (matched != null) {
+          vo.setAddress(matched.getLabel());
+        } else {
+          vo.setAddress(addr);
+        }
+      }
+
+      // 接站时间字典转义
+      if (dictTimeList != null && detail.getArriveTime() != null) {
+        final String time = detail.getArriveTime();
+        DictDataRespDTO matched = dictTimeList.stream()
+          .filter(d -> d.getValue().equals(time)).findFirst().orElse(null);
+        if (matched != null) {
+          vo.setArriveTime(matched.getLabel());
+        } else {
+          vo.setArriveTime(time);
+        }
+      }
+
+      exportList.add(vo);
+    }
+
+    ExcelUtils.write(response, "游客接站明细.xls", "数据",
+      OrderJzDetailExportVO.class, exportList);
+  }
+
 
   @GetMapping("/order/query")
   @Operation(summary = "获得订单接站人员订单分页")
@@ -205,4 +280,174 @@ public class OrderJzDetailController {
   public CommonResult<Long> visitorSignClean(String  id) {
     return success(orderJzDetailService.cleanOrderJzDetail(id));
   }
+
+  // ==================== 订单分车相关接口 ====================
+
+  @Resource
+  private com.yc.ship.module.trade.service.orderjzdetail.OrderJzDispatchService orderJzDispatchService;
+
+  @PostMapping("/dispatch/create")
+  @Operation(summary = "创建订单分车")
+  @PreAuthorize("@ss.hasPermission('trade:order-jz-detail:dispatch')")
+  public CommonResult<Long> createOrderJzDispatch(@Valid @RequestBody com.yc.ship.module.trade.controller.admin.orderjzdetail.vo.OrderJzDispatchSaveReqVO createReqVO) {
+    // 如果前端没有传分车时间,则使用当前时间
+    if (createReqVO.getDispatchTime() == null) {
+      createReqVO.setDispatchTime(java.time.LocalDateTime.now());
+    }
+    return success(orderJzDispatchService.createOrderJzDispatch(createReqVO));
+  }
+
+  @PutMapping("/dispatch/update")
+  @Operation(summary = "更新订单分车")
+  @PreAuthorize("@ss.hasPermission('trade:order-jz-detail:dispatch')")
+  public CommonResult<Boolean> updateOrderJzDispatch(@Valid @RequestBody com.yc.ship.module.trade.controller.admin.orderjzdetail.vo.OrderJzDispatchSaveReqVO updateReqVO) {
+    orderJzDispatchService.updateOrderJzDispatch(updateReqVO);
+    return success(true);
+  }
+
+  @DeleteMapping("/dispatch/delete")
+  @Operation(summary = "删除订单分车")
+  @Parameter(name = "id", description = "接站人员ID(OrderJzDetailDO.id)", required = true)
+  public CommonResult<Boolean> deleteOrderJzDispatch(@RequestParam("id") Long id) {
+    orderJzDispatchService.deleteOrderJzDispatch(id);
+    return success(true);
+  }
+
+  @GetMapping("/dispatch/get")
+  @Operation(summary = "获得订单分车")
+  @Parameter(name = "id", description = "编号", required = true, example = "1024")
+  public CommonResult<com.yc.ship.module.trade.controller.admin.orderjzdetail.vo.OrderJzDispatchRespVO> getOrderJzDispatch(@RequestParam("id") Long id) {
+    com.yc.ship.module.trade.dal.dataobject.orderjzdispatch.OrderJzDispatchDO orderJzDispatch = orderJzDispatchService.getOrderJzDispatch(id);
+    return success(BeanUtils.toBean(orderJzDispatch, com.yc.ship.module.trade.controller.admin.orderjzdetail.vo.OrderJzDispatchRespVO.class));
+  }
+
+  @PostMapping("/dispatch/batch-create")
+  @Operation(summary = "批量保存订单分车(有dispatchId更新,无则新建)")
+  @PreAuthorize("@ss.hasPermission('trade:order-jz-detail:dispatch')")
+  public CommonResult<Boolean> batchSaveOrderJzDispatch(@Valid @RequestBody OrderJzDispatchBatchReqVO batchReqVO) {
+    orderJzDispatchService.batchSaveOrderJzDispatch(batchReqVO);
+    return success(true);
+  }
+
+    // ==================== 赠送行程接站相关接口 ====================
+
+  @GetMapping("/gift/query")
+  @Operation(summary = "获得赠送行程接站人员分页")
+  @PreAuthorize("@ss.hasPermission('trade:order-jz-detail-sign:query')")
+  public CommonResult<PageResult<OrderJzDetailRespVO>> queryGiftPage(@Valid OrderJzDetailPageReqVO pageReqVO) {
+    PageResult<OrderJzDetailDO> pageResult = orderJzDetailService.getGiftPage(pageReqVO);
+    return success(BeanUtils.toBean(pageResult, OrderJzDetailRespVO.class));
+  }
+
+  @GetMapping("/gift/export-excel")
+  @Operation(summary = "导出赠送行程接站明细 Excel")
+  @ApiAccessLog(operateType = EXPORT)
+  @PreAuthorize("@ss.hasPermission('trade:order-jz-detail-sign:query')")
+  public void exportGiftExcel(@Valid OrderJzDetailPageReqVO pageReqVO,
+                              HttpServletResponse response) throws IOException {
+    if (StringUtils.isEmpty(pageReqVO.getVoyageId())) {
+      throw new RuntimeException("航次编号不能为空");
+    }
+    VoyageDO voyageDO = voyageService.getVoyage(Long.valueOf(pageReqVO.getVoyageId()));
+    if (voyageDO == null) {
+      throw new RuntimeException("航次信息不能为空");
+    }
+
+    // 查询导出数据(条件与gift/query一致)
+    List<OrderJzDetailDO> list = orderJzDetailService.getGiftExportList(pageReqVO);
+
+    // 获取字典数据
+    List<DictDataRespDTO> dictAddressList = null;
+    ResourceRouteDO resourceRouteDO = resourceRouteService.getRoute(voyageDO.getRouteId());
+    if (resourceRouteDO != null) {
+      int dir = resourceRouteDO.getDirection().intValue();
+      if (dir == 2) {
+        dictAddressList = dictDataApi.getDictDataList("order_jz_x");
+      } else {
+        dictAddressList = dictDataApi.getDictDataList("order_jz");
+      }
+    }
+    final List<DictDataRespDTO> dictTimeList = dictDataApi.getDictDataList("order_time_jz");
+
+    // 转换为导出 VO
+    List<OrderJzDetailExportVO> exportList = new java.util.ArrayList<>();
+    final List<DictDataRespDTO> finalAddressList = dictAddressList;
+    for (int i = 0; i < list.size(); i++) {
+      OrderJzDetailDO detail = list.get(i);
+      OrderJzDetailExportVO vo = new OrderJzDetailExportVO();
+      vo.setIndex(i + 1);
+      vo.setOrderNo(detail.getOrderNo());
+      vo.setName(detail.getName());
+      vo.setPhone(detail.getPhone());
+      vo.setRoomModelName(detail.getRoomModelName());
+      vo.setRemark(detail.getRemark());
+      vo.setSignTime(detail.getSignTime());
+      vo.setDispatchNo(detail.getDispatchNo());
+      vo.setReservationInfo(detail.getRemark());
+
+      // 站点字典转义
+      if (finalAddressList != null && detail.getAddress() != null) {
+        final String addr = detail.getAddress();
+        DictDataRespDTO matched = finalAddressList.stream()
+          .filter(d -> d.getValue().equals(addr)).findFirst().orElse(null);
+        if (matched != null) {
+          vo.setAddress(matched.getLabel());
+        } else {
+          vo.setAddress(addr);
+        }
+      }
+
+      // 接站时间字典转义
+      if (dictTimeList != null && detail.getArriveTime() != null) {
+        final String time = detail.getArriveTime();
+        DictDataRespDTO matched = dictTimeList.stream()
+          .filter(d -> d.getValue().equals(time)).findFirst().orElse(null);
+        if (matched != null) {
+          vo.setArriveTime(matched.getLabel());
+        } else {
+          vo.setArriveTime(time);
+        }
+      }
+
+      exportList.add(vo);
+    }
+
+    ExcelUtils.write(response, "赠送行程接站明细.xls", "数据",
+      OrderJzDetailExportVO.class, exportList);
+  }
+
+  @GetMapping("/gift/order/query")
+  @Operation(summary = "获得赠送行程补登订单分页")
+  @PreAuthorize("@ss.hasPermission('trade:order-jz-detail-sign:query')")
+  public CommonResult<PageResult<?>> queryGiftOrderPage(@Valid OrderJzDetailPageReqVO pageReqVO) {
+    PageResult<?> pageResult = orderJzDetailService.getGiftOrderPage(pageReqVO);
+    return success(pageResult);
+  }
+
+
+    // ==================== 分车记录相关接口 ====================
+
+  @GetMapping("/dispatch/record/page")
+  @Operation(summary = "获得分车记录分页")
+  @PreAuthorize("@ss.hasPermission('trade:order-jz-detail:query')")
+  public CommonResult<PageResult<OrderJzDispatchRecordRespVO>> getDispatchRecordPage(
+          @Valid OrderJzDispatchPageReqVO pageReqVO) {
+    PageResult<OrderJzDispatchRecordRespVO> pageResult = orderJzDispatchService.getDispatchRecordPage(pageReqVO);
+    return success(pageResult);
+  }
+
+  @GetMapping("/dispatch/record/export-excel")
+  @Operation(summary = "导出分车记录 Excel")
+  @ApiAccessLog(operateType = EXPORT)
+  @PreAuthorize("@ss.hasPermission('trade:order-jz-detail:query')")
+  public void exportDispatchRecordExcel(@Valid OrderJzDispatchPageReqVO pageReqVO,
+                                         HttpServletResponse response) throws IOException {
+    if (pageReqVO.getVoyageId() == null) {
+      throw new RuntimeException("航次编号不能为空");
+    }
+    List<OrderJzDispatchRecordRespVO> list = orderJzDispatchService.getDispatchRecordExportList(pageReqVO);
+    ExcelUtils.write(response, "分车记录.xls", "数据", OrderJzDispatchRecordRespVO.class, list);
+  }
+
+
 }

+ 44 - 0
ship-module-trade/ship-module-trade-biz/src/main/java/com/yc/ship/module/trade/controller/admin/orderjzdetail/vo/OrderJzDetailExportVO.java

@@ -0,0 +1,44 @@
+package com.yc.ship.module.trade.controller.admin.orderjzdetail.vo;
+
+import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
+import com.alibaba.excel.annotation.ExcelProperty;
+import lombok.Data;
+
+@ExcelIgnoreUnannotated
+@Data
+public class OrderJzDetailExportVO {
+
+    @ExcelProperty(value = "序号", index = 0)
+    private Integer index;
+
+    @ExcelProperty(value = "订单编号", index = 1)
+    private String orderNo;
+
+    @ExcelProperty(value = "站点", index = 2)
+    private String address;
+
+    @ExcelProperty(value = "接站时间", index = 3)
+    private String arriveTime;
+
+    @ExcelProperty(value = "姓名", index = 4)
+    private String name;
+
+    @ExcelProperty(value = "联系电话", index = 5)
+    private String phone;
+
+    @ExcelProperty(value = "房型", index = 6)
+    private String roomModelName;
+
+    @ExcelProperty(value = "车次/航班号", index = 7)
+    private String remark;
+
+    @ExcelProperty(value = "核销时间", index = 8)
+    private String signTime;
+
+    @ExcelProperty(value = "分车号", index = 9)
+    private String dispatchNo;
+
+    @ExcelProperty(value = "预约情况", index = 10)
+    private String reservationInfo;
+
+}

+ 12 - 0
ship-module-trade/ship-module-trade-biz/src/main/java/com/yc/ship/module/trade/controller/admin/orderjzdetail/vo/OrderJzDetailPageReqVO.java

@@ -55,4 +55,16 @@ public class OrderJzDetailPageReqVO extends PageParam {
     private String oid;
 
     private String queryCode;
+
+    @Schema(description = "分车号")
+    private String dispatchNo;
+
+    @Schema(description = "核销状态:1-已核销,0-未核销", example = "1")
+    private Integer signStatus;
+
+    @Schema(description = "订单号")
+    private String orderNo;
+
+    @Schema(description = "分销商ID")
+    private String otaId;
 }

+ 9 - 0
ship-module-trade/ship-module-trade-biz/src/main/java/com/yc/ship/module/trade/controller/admin/orderjzdetail/vo/OrderJzDetailRespVO.java

@@ -66,4 +66,13 @@ public class OrderJzDetailRespVO {
     private String direction;
     private String roomModelName;
 
+    @Schema(description = "分车id")
+    private Long dispatchId;
+
+    @Schema(description = "订单编号")
+    private String orderNo;
+
+    @Schema(description = "分车号")
+    private String dispatchNo;
+
 }

+ 76 - 0
ship-module-trade/ship-module-trade-biz/src/main/java/com/yc/ship/module/trade/controller/admin/orderjzdetail/vo/OrderJzDispatchBatchReqVO.java

@@ -0,0 +1,76 @@
+package com.yc.ship.module.trade.controller.admin.orderjzdetail.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import javax.validation.Valid;
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
+import java.util.List;
+
+@Schema(description = "管理后台 - 订单批量分车 Request VO")
+@Data
+public class OrderJzDispatchBatchReqVO {
+
+
+    @Schema(description = "分车信息", requiredMode = Schema.RequiredMode.REQUIRED)
+    @NotNull(message = "分车信息不能为空")
+    @Valid
+    private DispatchInfo dispatchInfo;
+
+    @Schema(description = "接站人员列表(含分车ID,有dispatchId则更新,无则新建)", requiredMode = Schema.RequiredMode.REQUIRED)
+    @NotEmpty(message = "接站人员列表不能为空")
+    @Valid
+    private List<OrderItem> orderItems;
+
+    @Schema(description = "接站人员明细")
+    @Data
+    public static class OrderItem {
+
+        @Schema(description = "接站人员ID(OrderJzDetailDO.id)", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+        @NotNull(message = "接站人员ID不能为空")
+        private Long id;
+
+        @Schema(description = "分车记录ID(OrderJzDispatchDO.id),有值则更新,无值则新建", example = "100")
+        private Long dispatchId;
+    }
+
+    @Schema(description = "分车信息")
+    @Data
+    public static class DispatchInfo {
+
+        @Schema(description = "航次ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+        @NotNull(message = "航次ID不能为空")
+        private Long voyageId;
+
+        @Schema(description = "分车号", example = "1号车")
+        private String dispatchNo;
+
+        @Schema(description = "分车组团号", example = "T2024010001")
+        private String groupNo;
+
+        @Schema(description = "车牌号", example = "川A12345")
+        private String busNumber;
+
+        @Schema(description = "司机姓名", example = "张三")
+        private String driverName;
+
+        @Schema(description = "司机联系电话", example = "13800138000")
+        private String driverPhone;
+
+        @Schema(description = "接站人员", example = "李四")
+        private String receiverName;
+
+        @Schema(description = "接站人电话", example = "13900139000")
+        private String receiverPhone;
+
+        @Schema(description = "分车来源类型(1:接站服务 2:赠送行程)", example = "1")
+        private Integer sourceType;
+
+        @Schema(description = "备注", example = "备注信息")
+        private String remark;
+
+        @Schema(description = "接站人数", example = "1")
+        private Integer passengerCount;
+    }
+}

+ 58 - 0
ship-module-trade/ship-module-trade-biz/src/main/java/com/yc/ship/module/trade/controller/admin/orderjzdetail/vo/OrderJzDispatchPageReqVO.java

@@ -0,0 +1,58 @@
+package com.yc.ship.module.trade.controller.admin.orderjzdetail.vo;
+
+import com.yc.ship.framework.common.pojo.PageParam;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.time.LocalDateTime;
+
+import static com.yc.ship.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+@Data
+@Schema(description = "管理后台 - 订单分车分页 Request VO")
+@EqualsAndHashCode(callSuper = false)
+@ToString(callSuper = true)
+public class OrderJzDispatchPageReqVO extends PageParam {
+
+
+    @io.swagger.v3.oas.annotations.media.Schema(description = "页码", example = "1")
+    private Integer pageNo = 1;
+
+    @io.swagger.v3.oas.annotations.media.Schema(description = "每页条数", example = "10")
+    private Integer pageSize = 10;
+
+
+    @Schema(description = "订单ID", example = "18565")
+    private Long orderId;
+
+    @Schema(description = "航次ID", example = "10086")
+    private Long voyageId;
+
+    @Schema(description = "分车号", example = "FC001")
+    private String dispatchNo;
+
+    @Schema(description = "车牌号", example = "川A12345")
+    private String busNumber;
+
+    @Schema(description = "司机姓名", example = "张师傅")
+    private String driverName;
+
+    @Schema(description = "分车来源类型(1:接站服务 2:赠送行程)", example = "1")
+    private Integer sourceType;
+
+
+    @Schema(description = "分车时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] dispatchTime;
+
+    @Schema(description = "创建时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] createTime;
+
+
+
+
+}

+ 51 - 0
ship-module-trade/ship-module-trade-biz/src/main/java/com/yc/ship/module/trade/controller/admin/orderjzdetail/vo/OrderJzDispatchRecordRespVO.java

@@ -0,0 +1,51 @@
+package com.yc.ship.module.trade.controller.admin.orderjzdetail.vo;
+
+import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
+import com.alibaba.excel.annotation.ExcelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Schema(description = "管理后台 - 分车记录 Response VO")
+@Data
+@ExcelIgnoreUnannotated
+public class OrderJzDispatchRecordRespVO implements Serializable {
+
+    @Schema(description = "分车号", example = "1号车")
+    @ExcelProperty(value = "分车号", index = 0)
+    private String dispatchNo;
+
+    @Schema(description = "分车组团号", example = "ZT001")
+    @ExcelProperty(value = "分车组团号", index = 1)
+    private String groupNo;
+
+    @Schema(description = "车牌号", example = "川A12345")
+    @ExcelProperty(value = "车牌号", index = 2)
+    private String busNumber;
+
+    @Schema(description = "人数", example = "10")
+    @ExcelProperty(value = "人数", index = 3)
+    private Integer passengerCount;
+
+    @Schema(description = "司机姓名", example = "张师傅")
+    @ExcelProperty(value = "司机", index = 4)
+    private String driverName;
+
+    @Schema(description = "司机联系电话", example = "13800138000")
+    @ExcelProperty(value = "联系方式", index = 5)
+    private String driverPhone;
+
+    @Schema(description = "接站人员", example = "李四")
+    @ExcelProperty(value = "接站人员", index = 6)
+    private String receiverName;
+
+    @Schema(description = "接站人电话", example = "13900139000")
+    @ExcelProperty(value = "接站人电话", index = 7)
+    private String receiverPhone;
+
+    @Schema(description = "备注", example = "备注信息")
+    @ExcelProperty(value = "备注", index = 8)
+    private String remark;
+
+}

+ 60 - 0
ship-module-trade/ship-module-trade-biz/src/main/java/com/yc/ship/module/trade/controller/admin/orderjzdetail/vo/OrderJzDispatchRespVO.java

@@ -0,0 +1,60 @@
+package com.yc.ship.module.trade.controller.admin.orderjzdetail.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Schema(description = "管理后台 - 订单分车 Response VO")
+@Data
+public class OrderJzDispatchRespVO {
+
+    @Schema(description = "主键ID", requiredMode = Schema.RequiredMode.AUTO)
+    private Long id;
+
+    @Schema(description = "订单ID", example = "1")
+    private Long orderId;
+
+    @Schema(description = "分车时间")
+    private LocalDateTime dispatchTime;
+
+    @Schema(description = "航次ID", example = "1")
+    private Long voyageId;
+
+    @Schema(description = "分车号", example = "1号车")
+    private String dispatchNo;
+
+    @Schema(description = "分车组团号", example = "T2024010001")
+    private String groupNo;
+
+    @Schema(description = "车牌号", example = "川A12345")
+    private String busNumber;
+
+    @Schema(description = "司机姓名", example = "张三")
+    private String driverName;
+
+    @Schema(description = "司机联系电话", example = "13800138000")
+    private String driverPhone;
+
+    @Schema(description = "接站人员", example = "李四")
+    private String receiverName;
+
+    @Schema(description = "接站人电话", example = "13900139000")
+    private String receiverPhone;
+
+    @Schema(description = "分车来源类型(1:接站服务 2:赠送行程)", example = "1")
+    private Integer sourceType;
+
+    @Schema(description = "乘客人数", example = "5")
+    private Integer passengerCount;
+
+    @Schema(description = "备注", example = "备注信息")
+    private String remark;
+
+    @Schema(description = "创建时间")
+    private LocalDateTime createTime;
+
+    @Schema(description = "更新时间")
+    private LocalDateTime updateTime;
+
+}

+ 59 - 0
ship-module-trade/ship-module-trade-biz/src/main/java/com/yc/ship/module/trade/controller/admin/orderjzdetail/vo/OrderJzDispatchSaveReqVO.java

@@ -0,0 +1,59 @@
+package com.yc.ship.module.trade.controller.admin.orderjzdetail.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+import java.time.LocalDateTime;
+
+@Schema(description = "管理后台 - 订单分车创建/更新 Request VO")
+@Data
+public class OrderJzDispatchSaveReqVO {
+
+    @Schema(description = "主键ID", requiredMode = Schema.RequiredMode.AUTO)
+    private Long id;
+
+    @Schema(description = "订单ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+    @NotNull(message = "订单ID不能为空")
+    private Long orderId;
+
+    @Schema(description = "分车时间")
+    private LocalDateTime dispatchTime;
+
+    @Schema(description = "航次ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+    @NotNull(message = "航次ID不能为空")
+    private Long voyageId;
+
+    @Schema(description = "分车号", example = "1号车")
+    private String dispatchNo;
+
+    @Schema(description = "分车组团号", example = "T2024010001")
+    private String groupNo;
+
+    @Schema(description = "车牌号", example = "川A12345")
+    private String busNumber;
+
+    @Schema(description = "司机姓名", example = "张三")
+    private String driverName;
+
+    @Schema(description = "司机联系电话", example = "13800138000")
+    private String driverPhone;
+
+    @Schema(description = "接站人员", example = "李四")
+    private String receiverName;
+
+    @Schema(description = "接站人电话", example = "13900139000")
+    private String receiverPhone;
+
+    @Schema(description = "分车来源类型(1:接站服务 2:赠送行程)", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+    @NotNull(message = "分车来源类型不能为空")
+    private Integer sourceType;
+
+    @Schema(description = "乘客人数", example = "5")
+    private Integer passengerCount;
+
+    @Schema(description = "备注", example = "备注信息")
+    private String remark;
+
+}

+ 37 - 0
ship-module-trade/ship-module-trade-biz/src/main/java/com/yc/ship/module/trade/dal/dataobject/orderjzdetail/OrderJzDetailDO.java

@@ -1,6 +1,7 @@
 package com.yc.ship.module.trade.dal.dataobject.orderjzdetail;
 
 import com.baomidou.mybatisplus.annotation.KeySequence;
+import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.TableName;
 import com.yc.ship.framework.mybatis.core.dataobject.BaseDO;
@@ -71,4 +72,40 @@ public class OrderJzDetailDO extends BaseDO {
     private String signRemark;
     private String signImage;
 
+    /**
+     * 分车ID
+     */
+    @Builder.Default
+    private Long dispatchId = 0L;
+
+    /**
+     * 订单编号(关联查询,非表字段)
+     */
+    @TableField(exist = false)
+    private String orderNo;
+
+    /**
+     * 分车号(关联查询,非表字段)
+     */
+    @TableField(exist = false)
+    private String dispatchNo;
+
+    /**
+     * 分车组团号(关联查询,非表字段)
+     */
+    @TableField(exist = false)
+    private String groupNo;
+
+    /**
+     * 方向(关联查询,非表字段)
+     */
+    @TableField(exist = false)
+    private String direction;
+
+    /**
+     * 房型名称(关联查询,非表字段)
+     */
+    @TableField(exist = false)
+    private String roomModelName;
+
 }

+ 97 - 0
ship-module-trade/ship-module-trade-biz/src/main/java/com/yc/ship/module/trade/dal/dataobject/orderjzdispatch/OrderJzDispatchDO.java

@@ -0,0 +1,97 @@
+package com.yc.ship.module.trade.dal.dataobject.orderjzdispatch;
+
+import com.baomidou.mybatisplus.annotation.KeySequence;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.yc.ship.framework.mybatis.core.dataobject.BaseDO;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+import lombok.ToString;
+
+import java.time.LocalDateTime;
+
+/**
+ * 订单分车 DO
+ *
+ * @author 管理员
+ */
+@TableName("trade_order_jz_dispatch")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class OrderJzDispatchDO extends BaseDO {
+
+    /**
+     * 主键ID
+     */
+    @TableId
+    private Long id;
+
+
+    /**
+     * 分车时间
+     */
+    private LocalDateTime dispatchTime;
+
+    /**
+     * 航次ID
+     */
+    private Long voyageId;
+
+    /**
+     * 分车号
+     */
+    private String dispatchNo;
+
+    /**
+     * 分车组团号
+     */
+    private String groupNo;
+
+    /**
+     * 车牌号
+     */
+    private String busNumber;
+
+    /**
+     * 司机姓名
+     */
+    private String driverName;
+
+    /**
+     * 司机联系电话
+     */
+    private String driverPhone;
+
+    /**
+     * 接站人员
+     */
+    private String receiverName;
+
+    /**
+     * 接站人电话
+     */
+    private String receiverPhone;
+
+    /**
+     * 分车来源类型(1:接站服务 2:赠送行程)
+     */
+    private Integer sourceType;
+
+    /**
+     * 乘客人数
+     */
+    private Integer passengerCount;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+}

+ 10 - 0
ship-module-trade/ship-module-trade-biz/src/main/java/com/yc/ship/module/trade/dal/mysql/orderjzdetail/OrderJzDetailMapper.java

@@ -57,7 +57,17 @@ public interface OrderJzDetailMapper extends BaseMapperX<OrderJzDetailDO> {
 
   IPage<OrderJzDetailDO> selectPage2(IPage<OrderJzDetailDO> page, @Param("vo") OrderJzDetailPageReqVO vo);
 
+  List<OrderJzDetailDO> selectVisitorExportList(@Param("vo") OrderJzDetailPageReqVO vo);
+
   IPage<Map> selectPage3(IPage<Map> page, @Param("vo") OrderJzDetailPageReqVO vo);
 
   Long cleanSign(@Param("id") String id);
+
+  // ==================== 赠送行程接站相关 ====================
+
+  IPage<OrderJzDetailDO> selectGiftPage(IPage<OrderJzDetailDO> page, @Param("vo") OrderJzDetailPageReqVO vo);
+
+  List<OrderJzDetailDO> selectGiftExportList(@Param("vo") OrderJzDetailPageReqVO vo);
+
+  IPage<Map> selectGiftOrderPage(IPage<Map> page, @Param("vo") OrderJzDetailPageReqVO vo);
 }

+ 56 - 0
ship-module-trade/ship-module-trade-biz/src/main/java/com/yc/ship/module/trade/dal/mysql/orderjzdispatch/OrderJzDispatchMapper.java

@@ -0,0 +1,56 @@
+package com.yc.ship.module.trade.dal.mysql.orderjzdispatch;
+
+import com.yc.ship.framework.common.pojo.PageResult;
+import com.yc.ship.framework.mybatis.core.mapper.BaseMapperX;
+import com.yc.ship.framework.mybatis.core.query.LambdaQueryWrapperX;
+import com.yc.ship.module.trade.controller.admin.orderjzdetail.vo.OrderJzDispatchRecordRespVO;
+import com.yc.ship.module.trade.controller.admin.orderjzdetail.vo.OrderJzDispatchPageReqVO;
+import com.yc.ship.module.trade.dal.dataobject.orderjzdispatch.OrderJzDispatchDO;
+import org.apache.ibatis.annotations.Delete;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * 订单分车 Mapper
+ *
+ * @author 管理员
+ */
+@Mapper
+public interface OrderJzDispatchMapper extends BaseMapperX<OrderJzDispatchDO> {
+
+  default PageResult<OrderJzDispatchDO> selectPage(OrderJzDispatchPageReqVO pageReqVO) {
+    return selectPage(pageReqVO, new LambdaQueryWrapperX<OrderJzDispatchDO>()
+      .eqIfPresent(OrderJzDispatchDO::getVoyageId, pageReqVO.getVoyageId())
+      .likeIfPresent(OrderJzDispatchDO::getDispatchNo, pageReqVO.getDispatchNo())
+      .likeIfPresent(OrderJzDispatchDO::getBusNumber, pageReqVO.getBusNumber())
+      .likeIfPresent(OrderJzDispatchDO::getDriverName, pageReqVO.getDriverName())
+      .eqIfPresent(OrderJzDispatchDO::getSourceType, pageReqVO.getSourceType())
+      .betweenIfPresent(OrderJzDispatchDO::getDispatchTime, pageReqVO.getDispatchTime())
+      .betweenIfPresent(OrderJzDispatchDO::getCreateTime, pageReqVO.getCreateTime())
+      .orderByDesc(OrderJzDispatchDO::getDispatchTime));
+  }
+
+    /**
+     * 物理删除分车记录
+     * @param id 记录ID
+     * @return 影响行数
+     */
+    @Delete("DELETE FROM trade_order_jz_dispatch WHERE id = #{id}")
+    int physicalDeleteById(@Param("id") Long id);
+
+    /**
+     * 分页查询分车记录(按分车号分组)
+     */
+    List<OrderJzDispatchRecordRespVO> selectDispatchRecordPage(
+            @Param("reqVO") OrderJzDispatchPageReqVO reqVO,
+            @Param("offset") int offset,
+            @Param("limit") int limit);
+
+    /**
+     * 查询分车记录总数(按分车号分组)
+     */
+    Long selectDispatchRecordCount(@Param("reqVO") OrderJzDispatchPageReqVO reqVO);
+
+}

+ 20 - 0
ship-module-trade/ship-module-trade-biz/src/main/java/com/yc/ship/module/trade/service/orderjzdetail/OrderJzDetailService.java

@@ -66,4 +66,24 @@ public interface OrderJzDetailService {
   PageResult<OrderJzDetailDO> getOrderJzDetailPage2(OrderJzDetailPageReqVO pageReqVO);
 
   PageResult<?> getOrderJzDetailPage3(OrderJzDetailPageReqVO pageReqVO);
+
+  List<OrderJzDetailDO> getVisitorExportList(OrderJzDetailPageReqVO pageReqVO);
+
+  // ==================== 赠送行程接站相关 ====================
+
+  /**
+   * 获得赠送行程接站人员分页
+   */
+  PageResult<OrderJzDetailDO> getGiftPage(OrderJzDetailPageReqVO pageReqVO);
+
+  /**
+   * 获得赠送行程接站导出数据
+   */
+  List<OrderJzDetailDO> getGiftExportList(OrderJzDetailPageReqVO pageReqVO);
+
+  /**
+   * 获得赠送行程补登订单分页
+   */
+  PageResult<?> getGiftOrderPage(OrderJzDetailPageReqVO pageReqVO);
+
 }

+ 28 - 0
ship-module-trade/ship-module-trade-biz/src/main/java/com/yc/ship/module/trade/service/orderjzdetail/OrderJzDetailServiceImpl.java

@@ -236,4 +236,32 @@ public class OrderJzDetailServiceImpl implements OrderJzDetailService {
     IPage<?> iPage = orderJzDetailMapper.selectPage3(page, pageReqVO);
     return new PageResult<>(iPage.getRecords(), iPage.getTotal());
   }
+
+  @Override
+  public List<OrderJzDetailDO> getVisitorExportList(OrderJzDetailPageReqVO pageReqVO) {
+    return orderJzDetailMapper.selectVisitorExportList(pageReqVO);
+  }
+
+  // ==================== 赠送行程接站相关 ====================
+
+  @Override
+  public PageResult<OrderJzDetailDO> getGiftPage(OrderJzDetailPageReqVO pageReqVO) {
+    IPage<OrderJzDetailDO> page = new Page<>(pageReqVO.getPageNo(), pageReqVO.getPageSize());
+    IPage<OrderJzDetailDO> iPage = orderJzDetailMapper.selectGiftPage(page, pageReqVO);
+    return new PageResult<>(iPage.getRecords(), iPage.getTotal());
+  }
+
+  @Override
+  public List<OrderJzDetailDO> getGiftExportList(OrderJzDetailPageReqVO pageReqVO) {
+    return orderJzDetailMapper.selectGiftExportList(pageReqVO);
+  }
+
+  @Override
+  public PageResult<?> getGiftOrderPage(OrderJzDetailPageReqVO pageReqVO) {
+    IPage<Map> page = new Page<>(pageReqVO.getPageNo(), pageReqVO.getPageSize());
+    IPage<?> iPage = orderJzDetailMapper.selectGiftOrderPage(page, pageReqVO);
+    return new PageResult<>(iPage.getRecords(), iPage.getTotal());
+  }
+
+
 }

+ 73 - 0
ship-module-trade/ship-module-trade-biz/src/main/java/com/yc/ship/module/trade/service/orderjzdetail/OrderJzDispatchService.java

@@ -0,0 +1,73 @@
+package com.yc.ship.module.trade.service.orderjzdetail;
+
+import com.yc.ship.framework.common.pojo.PageResult;
+import com.yc.ship.module.trade.controller.admin.orderjzdetail.vo.OrderJzDispatchBatchReqVO;
+import com.yc.ship.module.trade.controller.admin.orderjzdetail.vo.OrderJzDispatchPageReqVO;
+import com.yc.ship.module.trade.controller.admin.orderjzdetail.vo.OrderJzDispatchRecordRespVO;
+import com.yc.ship.module.trade.controller.admin.orderjzdetail.vo.OrderJzDispatchSaveReqVO;
+
+import javax.validation.Valid;
+import java.util.List;
+
+/**
+ * 订单分车 Service 接口
+ *
+ * @author 管理员
+ */
+public interface OrderJzDispatchService {
+
+    /**
+     * 创建订单分车
+     *
+     * @param createReqVO 创建信息
+     * @return 编号
+     */
+    Long createOrderJzDispatch(@Valid OrderJzDispatchSaveReqVO createReqVO);
+
+    /**
+     * 批量保存订单分车(有dispatchId则更新,无则新建)
+     *
+     * @param batchReqVO 批量分车请求
+     */
+    void batchSaveOrderJzDispatch(@Valid OrderJzDispatchBatchReqVO batchReqVO);
+
+    /**
+     * 更新订单分车
+     *
+     * @param updateReqVO 更新信息
+     */
+    void updateOrderJzDispatch(@Valid OrderJzDispatchSaveReqVO updateReqVO);
+
+    /**
+     * 删除订单分车
+     *
+     * @param id 编号
+     */
+    void deleteOrderJzDispatch(Long id);
+
+    /**
+     * 获得订单分车
+     *
+     * @param id 编号
+     * @return 订单分车
+     */
+    com.yc.ship.module.trade.dal.dataobject.orderjzdispatch.OrderJzDispatchDO getOrderJzDispatch(Long id);
+
+    /**
+     * 获得分车记录分页
+     *
+     * @param pageReqVO 分页查询条件
+     * @return 分车记录分页结果
+     */
+    PageResult<OrderJzDispatchRecordRespVO> getDispatchRecordPage(OrderJzDispatchPageReqVO pageReqVO);
+
+    /**
+     * 获得分车记录列表(用于导出)
+     *
+     * @param pageReqVO 查询条件
+     * @return 分车记录列表
+     */
+    List<OrderJzDispatchRecordRespVO> getDispatchRecordExportList(OrderJzDispatchPageReqVO pageReqVO);
+
+
+}

+ 188 - 0
ship-module-trade/ship-module-trade-biz/src/main/java/com/yc/ship/module/trade/service/orderjzdetail/OrderJzDispatchServiceImpl.java

@@ -0,0 +1,188 @@
+package com.yc.ship.module.trade.service.orderjzdetail;
+
+import cn.hutool.core.util.ObjectUtil;
+import com.yc.ship.framework.common.pojo.PageResult;
+import com.yc.ship.module.trade.controller.admin.orderjzdetail.vo.OrderJzDispatchBatchReqVO;
+import com.yc.ship.module.trade.controller.admin.orderjzdetail.vo.OrderJzDispatchPageReqVO;
+import com.yc.ship.module.trade.controller.admin.orderjzdetail.vo.OrderJzDispatchRecordRespVO;
+import com.yc.ship.module.trade.controller.admin.orderjzdetail.vo.OrderJzDispatchSaveReqVO;
+import com.yc.ship.module.trade.dal.dataobject.orderjzdetail.OrderJzDetailDO;
+import com.yc.ship.module.trade.dal.dataobject.orderjzdispatch.OrderJzDispatchDO;
+import com.yc.ship.module.trade.dal.mysql.orderjzdetail.OrderJzDetailMapper;
+import com.yc.ship.module.trade.dal.mysql.orderjzdispatch.OrderJzDispatchMapper;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.validation.annotation.Validated;
+
+import javax.annotation.Resource;
+import javax.validation.Valid;
+
+import java.time.LocalDateTime;
+import java.util.List;
+
+import static com.yc.ship.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static com.yc.ship.module.trade.enums.ErrorCodeConstants.ORDER_JZ_DISPATCH_NOT_EXISTS;
+
+/**
+ * 订单分车 Service 实现类
+ *
+ * @author 管理员
+ */
+@Service
+@Validated
+public class OrderJzDispatchServiceImpl implements OrderJzDispatchService {
+
+    @Resource
+    private OrderJzDispatchMapper orderJzDispatchMapper;
+
+    @Resource
+    private OrderJzDetailMapper orderJzDetailMapper;
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Long createOrderJzDispatch(@Valid OrderJzDispatchSaveReqVO createReqVO) {
+        OrderJzDispatchDO orderJzDispatch = OrderJzDispatchDO.builder()
+                .dispatchTime(LocalDateTime.now())
+                .voyageId(createReqVO.getVoyageId())
+                .dispatchNo(createReqVO.getDispatchNo())
+                .groupNo(createReqVO.getGroupNo())
+                .busNumber(createReqVO.getBusNumber())
+                .driverName(createReqVO.getDriverName())
+                .driverPhone(createReqVO.getDriverPhone())
+                .receiverName(createReqVO.getReceiverName())
+                .receiverPhone(createReqVO.getReceiverPhone())
+                .sourceType(1)
+                .passengerCount(createReqVO.getPassengerCount())
+                .remark(createReqVO.getRemark())
+                .build();
+        orderJzDispatchMapper.insert(orderJzDispatch);
+
+        // 更新接站人员信息的分车ID
+        OrderJzDetailDO orderJzDetail = orderJzDetailMapper.selectById(createReqVO.getOrderId());
+        if (orderJzDetail != null) {
+            orderJzDetail.setDispatchId(orderJzDispatch.getId());
+            orderJzDetailMapper.updateById(orderJzDetail);
+        }
+
+        return orderJzDispatch.getId();
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void batchSaveOrderJzDispatch(@Valid OrderJzDispatchBatchReqVO batchReqVO) {
+        OrderJzDispatchBatchReqVO.DispatchInfo info = batchReqVO.getDispatchInfo();
+        // 无分车记录 -> 新建
+        OrderJzDispatchDO orderJzDispatch = OrderJzDispatchDO.builder()
+                .dispatchTime(LocalDateTime.now())
+                .voyageId(info.getVoyageId())
+                .dispatchNo(info.getDispatchNo())
+                .groupNo(info.getGroupNo())
+                .busNumber(info.getBusNumber())
+                .driverName(info.getDriverName())
+                .driverPhone(info.getDriverPhone())
+                .receiverName(info.getReceiverName())
+                .receiverPhone(info.getReceiverPhone())
+                .sourceType(ObjectUtil.isEmpty(info.getSourceType()) ? 1 : info.getSourceType())
+                .passengerCount(info.getPassengerCount())
+                .remark(info.getRemark())
+                .build();
+        orderJzDispatchMapper.insert(orderJzDispatch);
+        for (OrderJzDispatchBatchReqVO.OrderItem item : batchReqVO.getOrderItems()) {
+                // 更新接站人员信息的分车ID
+                OrderJzDetailDO detail = orderJzDetailMapper.selectById(item.getId());
+                if (detail != null) {
+                    detail.setDispatchId(orderJzDispatch.getId());
+                    orderJzDetailMapper.updateById(detail);
+                }
+        }
+
+
+
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void updateOrderJzDispatch(@Valid OrderJzDispatchSaveReqVO updateReqVO) {
+        // 校验存在
+        validateOrderJzDispatchExists(updateReqVO.getId());
+        // 更新
+        OrderJzDispatchDO updateObj = OrderJzDispatchDO.builder()
+                .id(updateReqVO.getId())
+                .dispatchTime(LocalDateTime.now())
+                .voyageId(updateReqVO.getVoyageId())
+                .dispatchNo(updateReqVO.getDispatchNo())
+                .groupNo(updateReqVO.getGroupNo())
+                .busNumber(updateReqVO.getBusNumber())
+                .driverName(updateReqVO.getDriverName())
+                .driverPhone(updateReqVO.getDriverPhone())
+                .receiverName(updateReqVO.getReceiverName())
+                .receiverPhone(updateReqVO.getReceiverPhone())
+                .sourceType(1)
+                .passengerCount(updateReqVO.getPassengerCount())
+                .remark(updateReqVO.getRemark())
+                .build();
+        orderJzDispatchMapper.updateById(updateObj);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void deleteOrderJzDispatch(Long id) {
+        // 参数 id 现在表示接站人员ID(OrderJzDetailDO.id)
+        // 根据接站人员ID获取接站人员信息
+        OrderJzDetailDO orderJzDetail = orderJzDetailMapper.selectById(id);
+        if (orderJzDetail == null) {
+            // 接站人员不存在,抛出异常
+            throw exception(ORDER_JZ_DISPATCH_NOT_EXISTS);
+        }
+
+        // 获取分车ID
+        Long dispatchId = orderJzDetail.getDispatchId();
+        if (dispatchId != null) {
+
+            OrderJzDispatchDO orderJzDis = orderJzDispatchMapper.selectById(dispatchId);
+            Integer passengerCount = orderJzDis.getPassengerCount();
+            if (passengerCount != null && passengerCount > 1) {
+                // 大于一人 更新分车记录表
+                orderJzDispatchMapper.updateById(OrderJzDispatchDO.builder()
+                        .id(dispatchId)
+                        .passengerCount(passengerCount - 1)
+                        .build());
+            } else {
+                // 删除分车记录
+                orderJzDispatchMapper.physicalDeleteById(dispatchId);
+            }
+
+            // 将接站人员信息的 dispatch_id 设置为 null
+            orderJzDetail.setDispatchId(0L);
+            orderJzDetailMapper.updateById(orderJzDetail);
+        }
+        // 如果 dispatchId 为 0,说明该接站人员没有分车记录,无需处理
+    }
+
+    private void validateOrderJzDispatchExists(Long id) {
+        if (orderJzDispatchMapper.selectById(id) == null) {
+            throw exception(ORDER_JZ_DISPATCH_NOT_EXISTS);
+        }
+    }
+
+    @Override
+    public OrderJzDispatchDO getOrderJzDispatch(Long id) {
+        return orderJzDispatchMapper.selectById(id);
+    }
+
+    @Override
+    public PageResult<OrderJzDispatchRecordRespVO> getDispatchRecordPage(OrderJzDispatchPageReqVO pageReqVO) {
+        int offset = (pageReqVO.getPageNo() - 1) * pageReqVO.getPageSize();
+        List<OrderJzDispatchRecordRespVO> list = orderJzDispatchMapper.selectDispatchRecordPage(
+                pageReqVO, offset, pageReqVO.getPageSize());
+        Long total = orderJzDispatchMapper.selectDispatchRecordCount(pageReqVO);
+        return new PageResult<>(list, total);
+    }
+
+    @Override
+    public List<OrderJzDispatchRecordRespVO> getDispatchRecordExportList(OrderJzDispatchPageReqVO pageReqVO) {
+        return orderJzDispatchMapper.selectDispatchRecordPage(pageReqVO, 0, Integer.MAX_VALUE);
+    }
+
+
+}

+ 170 - 4
ship-module-trade/ship-module-trade-biz/src/main/resources/mapper/orderjzdetail/OrderJzDetailMapper.xml

@@ -10,14 +10,16 @@
 
     <select id="selectPage2"
             resultType="com.yc.ship.module.trade.dal.dataobject.orderjzdetail.OrderJzDetailDO">
-        select distinct d.* , r.direction , rm.name as room_model_name
+        select distinct d.* , r.direction , rm.name as room_model_name,
+        SUBSTRING_INDEX(o.order_no, '-', -1) as orderNo, dj.dispatch_no as dispatchNo, dj.group_no as groupNo
         from trade_order_jz_detail d
         inner join trade_order_jz oz on d.jz_id = oz.id
         inner join trade_order o on o.deleted = 0 and o.id = oz.order_id
         inner join product_voyage v on o.voyage_id = v.id
         inner join resource_route r on v.route_id = r.id
-        inner join trade_visitor u on u.credential_no = d.id_card
-        inner join resource_room_model rm on u.room_model_id = rm.id
+        inner join trade_visitor u on u.credential_no = d.id_card and u.order_id = oz.order_id
+        left join resource_room_model rm on u.room_model_id = rm.id
+        left join trade_order_jz_dispatch dj on d.dispatch_id = dj.id
         where d.deleted = 0 and o.voyage_id = #{vo.voyageId}
         <if test="vo.idCard != null">
             and d.id_card like concat('%', #{vo.idCard}, '%')
@@ -31,8 +33,59 @@
         <if test="vo.arriveTime != null">
             and d.arrive_time = #{vo.arriveTime}
         </if>
-        order by d.address asc, d.arrive_time asc, d.batch_no asc
+        <if test="vo.dispatchNo != null and vo.dispatchNo != ''">
+            and dj.dispatch_no like concat('%', #{vo.dispatchNo}, '%')
+        </if>
+        <if test="vo.signStatus != null and vo.signStatus == 1">
+            and d.sign_time is not null
+        </if>
+        <if test="vo.signStatus != null and vo.signStatus == 0">
+            and d.sign_time is null
+        </if>
+        order by CAST(SUBSTRING_INDEX(o.order_no, '-', -1) AS UNSIGNED) asc,
+                 d.address asc, d.arrive_time asc, d.batch_no asc
     </select>
+
+    <!-- selectVisitorExportList: 导出游客接站明细,条件与selectPage2一致,不分页 -->
+    <select id="selectVisitorExportList"
+            resultType="com.yc.ship.module.trade.dal.dataobject.orderjzdetail.OrderJzDetailDO">
+        select distinct d.* , r.direction , rm.name as room_model_name,
+        SUBSTRING_INDEX(o.order_no, '-', -1) as orderNo, dj.dispatch_no as dispatchNo, dj.group_no as groupNo
+        from trade_order_jz_detail d
+        inner join trade_order_jz oz on d.jz_id = oz.id
+        inner join trade_order o on o.deleted = 0 and o.id = oz.order_id
+        inner join product_voyage v on o.voyage_id = v.id
+        inner join resource_route r on v.route_id = r.id
+        inner join trade_visitor u on u.credential_no = d.id_card and u.order_id = oz.order_id
+        left join resource_room_model rm on u.room_model_id = rm.id
+        left join trade_order_jz_dispatch dj on d.dispatch_id = dj.id
+        inner join trade_detail td on td.order_id = o.id and td.product_id = 2034458675435925505
+        where d.deleted = 0 and o.voyage_id = #{vo.voyageId}
+        <if test="vo.idCard != null and vo.idCard != ''">
+            and d.id_card like concat('%', #{vo.idCard}, '%')
+        </if>
+        <if test="vo.name != null and vo.name != ''">
+            and d.name like concat('%', #{vo.name}, '%')
+        </if>
+        <if test="vo.address != null and vo.address != ''">
+            and d.address = #{vo.address}
+        </if>
+        <if test="vo.arriveTime != null and vo.arriveTime != ''">
+            and d.arrive_time = #{vo.arriveTime}
+        </if>
+        <if test="vo.dispatchNo != null and vo.dispatchNo != ''">
+            and dj.dispatch_no like concat('%', #{vo.dispatchNo}, '%')
+        </if>
+        <if test="vo.signStatus != null and vo.signStatus == 1">
+            and d.sign_time is not null
+        </if>
+        <if test="vo.signStatus != null and vo.signStatus == 0">
+            and d.sign_time is null
+        </if>
+        order by CAST(SUBSTRING_INDEX(o.order_no, '-', -1) AS UNSIGNED) asc,
+                 d.address asc, d.arrive_time asc, d.batch_no asc
+    </select>
+
     <select id="selectPage3" resultType="map">
         select distinct o.id, o.order_no, r.direction , v.name, v.credential_no, v.mobile, rm.name AS room_model_name
         from trade_order o
@@ -51,4 +104,117 @@
         update trade_order_jz_detail set sign_time = null, sign_man = null, sign_remark = null, sign_image = null
         where id = #{id}
     </update>
+
+    <!-- ==================== 赠送行程接站相关 ==================== -->
+
+    <!-- 赠送行程分页查询:基于trade_order_jz_detail,关联trade_detail过滤product_id -->
+    <select id="selectGiftPage"
+            resultType="com.yc.ship.module.trade.dal.dataobject.orderjzdetail.OrderJzDetailDO">
+        select distinct d.* , r.direction , rm.name as room_model_name,
+        SUBSTRING_INDEX(o.order_no, '-', -1) as orderNo, dj.dispatch_no as dispatchNo, dj.group_no as groupNo
+        from trade_order_jz_detail d
+        inner join trade_order_jz oz on d.jz_id = oz.id
+        inner join trade_order o on o.deleted = 0 and o.id = oz.order_id
+        inner join product_voyage v on o.voyage_id = v.id
+        inner join resource_route r on v.route_id = r.id
+        inner join trade_visitor u on u.credential_no = d.id_card and u.order_id = oz.order_id
+        left join resource_room_model rm on u.room_model_id = rm.id
+        left join trade_order_jz_dispatch dj on d.dispatch_id = dj.id
+        inner join trade_detail td on td.order_id = o.id and td.product_id = 2034458675435925505
+        where d.deleted = 0 and o.voyage_id = #{vo.voyageId}
+        <if test="vo.idCard != null">
+            and d.id_card like concat('%', #{vo.idCard}, '%')
+        </if>
+        <if test="vo.name != null">
+            and d.name like concat('%', #{vo.name}, '%')
+        </if>
+        <if test="vo.address != null">
+            and d.address = #{vo.address}
+        </if>
+        <if test="vo.arriveTime != null">
+            and d.arrive_time = #{vo.arriveTime}
+        </if>
+        <if test="vo.dispatchNo != null and vo.dispatchNo != ''">
+            and dj.dispatch_no like concat('%', #{vo.dispatchNo}, '%')
+        </if>
+        <if test="vo.signStatus != null and vo.signStatus == 1">
+            and d.sign_time is not null
+        </if>
+        <if test="vo.signStatus != null and vo.signStatus == 0">
+            and d.sign_time is null
+        </if>
+        <if test="vo.orderNo != null and vo.orderNo != ''">
+            and o.order_no like concat('%', #{vo.orderNo}, '%')
+        </if>
+        <if test="vo.otaId != null and vo.otaId != ''">
+            and o.source_id = #{vo.otaId}
+        </if>
+        order by CAST(SUBSTRING_INDEX(o.order_no, '-', -1) AS UNSIGNED) asc,
+                 d.address asc, d.arrive_time asc, d.batch_no asc
+    </select>
+
+    <!-- 赠送行程导出查询:条件与selectGiftPage一致,不分页 -->
+    <select id="selectGiftExportList"
+            resultType="com.yc.ship.module.trade.dal.dataobject.orderjzdetail.OrderJzDetailDO">
+        select distinct d.* , r.direction , rm.name as room_model_name,
+        SUBSTRING_INDEX(o.order_no, '-', -1) as orderNo, dj.dispatch_no as dispatchNo, dj.group_no as groupNo
+        from trade_order_jz_detail d
+        inner join trade_order_jz oz on d.jz_id = oz.id
+        inner join trade_order o on o.deleted = 0 and o.id = oz.order_id
+        inner join product_voyage v on o.voyage_id = v.id
+        inner join resource_route r on v.route_id = r.id
+        inner join trade_visitor u on u.credential_no = d.id_card and u.order_id = oz.order_id
+        left join resource_room_model rm on u.room_model_id = rm.id
+        left join trade_order_jz_dispatch dj on d.dispatch_id = dj.id
+        inner join trade_detail td on td.order_id = o.id and td.product_id = 2034458675435925505
+        where d.deleted = 0 and o.voyage_id = #{vo.voyageId}
+        <if test="vo.idCard != null and vo.idCard != ''">
+            and d.id_card like concat('%', #{vo.idCard}, '%')
+        </if>
+        <if test="vo.name != null and vo.name != ''">
+            and d.name like concat('%', #{vo.name}, '%')
+        </if>
+        <if test="vo.address != null and vo.address != ''">
+            and d.address = #{vo.address}
+        </if>
+        <if test="vo.arriveTime != null and vo.arriveTime != ''">
+            and d.arrive_time = #{vo.arriveTime}
+        </if>
+        <if test="vo.dispatchNo != null and vo.dispatchNo != ''">
+            and dj.dispatch_no like concat('%', #{vo.dispatchNo}, '%')
+        </if>
+        <if test="vo.signStatus != null and vo.signStatus == 1">
+            and d.sign_time is not null
+        </if>
+        <if test="vo.signStatus != null and vo.signStatus == 0">
+            and d.sign_time is null
+        </if>
+        <if test="vo.orderNo != null and vo.orderNo != ''">
+            and o.order_no like concat('%', #{vo.orderNo}, '%')
+        </if>
+        <if test="vo.otaId != null and vo.otaId != ''">
+            and o.source_id = #{vo.otaId}
+        </if>
+        order by CAST(SUBSTRING_INDEX(o.order_no, '-', -1) AS UNSIGNED) asc,
+                 d.address asc, d.arrive_time asc, d.batch_no asc
+    </select>
+
+    <!-- 赠送行程补登订单查询:根据订单号/证件号后4位搜索,关联trade_detail过滤product_id -->
+    <select id="selectGiftOrderPage" resultType="map">
+        select distinct o.id, o.order_no, r.direction , v.name, v.credential_no, v.mobile, rm.name AS room_model_name
+        from trade_order o
+        inner join product_voyage p on o.voyage_id = p.id
+        inner join resource_route r on p.route_id = r.id
+        inner join trade_visitor v on o.id = v.order_id
+        inner join resource_room_model rm ON v.room_model_id = rm.id
+        inner join trade_detail td on td.order_id = o.id and td.product_id = 2034458675435925505
+        where o.deleted = 0 and p.deleted = 0 and r.deleted = 0 and v.deleted = 0
+        and o.voyage_id = #{vo.voyageId}
+        <if test="vo.queryCode != null">
+            and (v.credential_no like concat('%', #{vo.queryCode}, '%') or v.mobile like concat('%', #{vo.queryCode}, '%') or o.order_no like concat('%', #{vo.queryCode}, '%'))
+        </if>
+        <if test="vo.otaId != null and vo.otaId != ''">
+            and o.source_id = #{vo.otaId}
+        </if>
+    </select>
 </mapper>

+ 102 - 0
ship-module-trade/ship-module-trade-biz/src/main/resources/mapper/orderjzdispatch/OrderJzDispatchMapper.xml

@@ -0,0 +1,102 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.yc.ship.module.trade.dal.mysql.orderjzdispatch.OrderJzDispatchMapper">
+
+    <select id="selectPage" resultType="com.yc.ship.module.trade.dal.dataobject.orderjzdispatch.OrderJzDispatchDO">
+        SELECT id, order_id, dispatch_time, voyage_id, dispatch_no, bus_number, driver_name, driver_phone,
+               receiver_name, receiver_phone, source_type, passenger_count, remark, tenant_id, creator,
+               create_time, updater, update_time, deleted
+        FROM trade_order_jz_dispatch
+        <where>
+            deleted = 0
+            <if test="pageReqVO.orderId != null">
+                AND order_id = #{pageReqVO.orderId}
+            </if>
+            <if test="pageReqVO.voyageId != null">
+                AND voyage_id = #{pageReqVO.voyageId}
+            </if>
+            <if test="pageReqVO.dispatchNo != null and pageReqVO.dispatchNo != ''">
+                AND dispatch_no LIKE CONCAT('%', #{pageReqVO.dispatchNo}, '%')
+            </if>
+            <if test="pageReqVO.busNumber != null and pageReqVO.busNumber != ''">
+                AND bus_number LIKE CONCAT('%', #{pageReqVO.busNumber}, '%')
+            </if>
+            <if test="pageReqVO.sourceType != null">
+                AND source_type = #{pageReqVO.sourceType}
+            </if>
+        </where>
+        ORDER BY id DESC
+        LIMIT #{pageReqVO.pageSize} OFFSET #{pageReqVO.offset}
+    </select>
+
+    <select id="selectCount" resultType="java.lang.Long">
+        SELECT COUNT(1)
+        FROM trade_order_jz_dispatch
+        <where>
+            deleted = 0
+            <if test="pageReqVO.orderId != null">
+                AND order_id = #{pageReqVO.orderId}
+            </if>
+            <if test="pageReqVO.voyageId != null">
+                AND voyage_id = #{pageReqVO.voyageId}
+            </if>
+            <if test="pageReqVO.dispatchNo != null and pageReqVO.dispatchNo != ''">
+                AND dispatch_no LIKE CONCAT('%', #{pageReqVO.dispatchNo}, '%')
+            </if>
+            <if test="pageReqVO.busNumber != null and pageReqVO.busNumber != ''">
+                AND bus_number LIKE CONCAT('%', #{pageReqVO.busNumber}, '%')
+            </if>
+            <if test="pageReqVO.sourceType != null">
+                AND source_type = #{pageReqVO.sourceType}
+            </if>
+        </where>
+    </select>
+
+    <!-- 分页查询分车记录(按分车号分组) -->
+    <select id="selectDispatchRecordPage"
+            resultType="com.yc.ship.module.trade.controller.admin.orderjzdetail.vo.OrderJzDispatchRecordRespVO">
+        SELECT
+            MAX(dispatch_no) AS dispatchNo,
+            MAX(group_no) AS groupNo,
+            MAX(bus_number) AS busNumber,
+            SUM(passenger_count) AS passengerCount,
+            MAX(driver_name) AS driverName,
+            MAX(driver_phone) AS driverPhone,
+            MAX(receiver_name) AS receiverName,
+            MAX(receiver_phone) AS receiverPhone,
+            MAX(remark) AS remark
+        FROM trade_order_jz_dispatch
+        <where>
+            deleted = 0
+            AND dispatch_no IS NOT NULL
+            AND dispatch_no != ''
+            <if test="reqVO.voyageId != null">
+                AND voyage_id = #{reqVO.voyageId}
+            </if>
+            <if test="reqVO.dispatchNo != null and reqVO.dispatchNo != ''">
+                AND dispatch_no LIKE CONCAT('%', #{reqVO.dispatchNo}, '%')
+            </if>
+        </where>
+        GROUP BY dispatch_no,group_no
+        ORDER BY MAX(id) DESC
+        LIMIT #{limit} OFFSET #{offset}
+    </select>
+
+    <!-- 查询分车记录总数 -->
+    <select id="selectDispatchRecordCount" resultType="java.lang.Long">
+        SELECT COUNT(DISTINCT dispatch_no)
+        FROM trade_order_jz_dispatch
+        <where>
+            deleted = 0
+            AND dispatch_no IS NOT NULL
+            AND dispatch_no != ''
+            <if test="reqVO.voyageId != null">
+                AND voyage_id = #{reqVO.voyageId}
+            </if>
+            <if test="reqVO.dispatchNo != null and reqVO.dispatchNo != ''">
+                AND dispatch_no LIKE CONCAT('%', #{reqVO.dispatchNo}, '%')
+            </if>
+        </where>
+    </select>
+
+</mapper>