From 58726a675e143abe435e5a76ab4caa0de51b4bd6 Mon Sep 17 00:00:00 2001 From: b2894lxlx <517289602@qq.com> Date: Wed, 5 Aug 2026 15:51:44 +0800 Subject: [PATCH] =?UTF-8?q?1=E3=80=81=E5=AE=8C=E5=96=84=E5=8F=91=E8=B4=A7?= =?UTF-8?q?=E6=A8=A1=E6=9D=BF=202=E3=80=81=E5=AE=8C=E5=96=84=E8=BF=90?= =?UTF-8?q?=E8=BE=93=E8=AE=A1=E5=88=92=203=E3=80=81=E5=AE=8C=E5=96=84?= =?UTF-8?q?=E8=BF=90=E5=8D=95=E7=AE=A1=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/TransportPlanDispatchRequest.java | 24 +++ .../transport/pojo/entity/Waybill.java | 12 +- .../system/mapper/PortTerminalMapper.xml | 3 + .../ShippingTemplateController.java | 15 +- .../controller/TransportPlanController.java | 54 +++++- .../controller/WaybillController.java | 76 ++++++-- .../excel/TransportPlanImportExcel.java | 96 +++++++++++ .../transport/excel/WaybillExcel.java | 9 +- .../excel/WaybillImportBatchExcel.java | 66 +++++++ .../service/IShippingTemplateService.java | 1 + .../service/ITransportPlanService.java | 4 + .../impl/ShippingTemplateServiceImpl.java | 11 +- .../impl/TransportPlanServiceImpl.java | 162 ++++++++++++++++++ .../service/impl/WaybillServiceImpl.java | 2 +- doc/sql/transport/blade_tms_business.sql | 4 +- .../blade_waybill_date_patch_20260805.sql | 3 + .../blade_waybill_import_batch_20260805.sql | 33 ++++ .../blade_waybill_patch_20260804.sql | 4 +- 18 files changed, 544 insertions(+), 35 deletions(-) create mode 100644 blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/dto/TransportPlanDispatchRequest.java create mode 100644 blade-service/blade-transport/src/main/java/org/springblade/transport/excel/TransportPlanImportExcel.java create mode 100644 blade-service/blade-transport/src/main/java/org/springblade/transport/excel/WaybillImportBatchExcel.java create mode 100644 doc/sql/transport/blade_waybill_date_patch_20260805.sql create mode 100644 doc/sql/transport/blade_waybill_import_batch_20260805.sql diff --git a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/dto/TransportPlanDispatchRequest.java b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/dto/TransportPlanDispatchRequest.java new file mode 100644 index 0000000..ea5be2b --- /dev/null +++ b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/dto/TransportPlanDispatchRequest.java @@ -0,0 +1,24 @@ +package org.springblade.transport.pojo.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.springblade.transport.pojo.entity.Waybill; + +import java.util.List; + +/** + * 运输计划调度请求。 + */ +@Data +@Schema(description = "运输计划调度请求") +public class TransportPlanDispatchRequest { + + @Schema(description = "运输计划主键") + private Long id; + + @Schema(description = "调度方式:draft 或 submit") + private String mode; + + @Schema(description = "本次生成的运单") + private List waybills; +} diff --git a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/Waybill.java b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/Waybill.java index a17c99d..1a01d2b 100644 --- a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/Waybill.java +++ b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/Waybill.java @@ -31,7 +31,6 @@ import org.springblade.core.tenant.mp.TenantEntity; import java.io.Serial; import java.math.BigDecimal; import java.time.LocalDate; -import java.time.LocalDateTime; /** * 运单管理实体类 @@ -152,11 +151,11 @@ public class Waybill extends TenantEntity { @Schema(description = "里程") private BigDecimal mileage; - @Schema(description = "预计发货时间") - private LocalDateTime estimatedStartTime; + @Schema(description = "预计发货日期") + private LocalDate estimatedStartTime; - @Schema(description = "预计完成时间") - private LocalDateTime estimatedEndTime; + @Schema(description = "预计完成日期") + private LocalDate estimatedEndTime; @Schema(description = "单价") private BigDecimal unitPrice; @@ -200,6 +199,9 @@ public class Waybill extends TenantEntity { @Schema(description = "运单批次号") private String batchNo; + @Schema(description = "导入批次ID") + private Long importBatchId; + @Schema(description = "关联单号") private String relationNo; diff --git a/blade-service/blade-system/src/main/java/org/springblade/system/mapper/PortTerminalMapper.xml b/blade-service/blade-system/src/main/java/org/springblade/system/mapper/PortTerminalMapper.xml index f81e280..50c7bbf 100644 --- a/blade-service/blade-system/src/main/java/org/springblade/system/mapper/PortTerminalMapper.xml +++ b/blade-service/blade-system/src/main/java/org/springblade/system/mapper/PortTerminalMapper.xml @@ -72,6 +72,9 @@ AND pt.category = #{portTerminal.category} + + AND pt.parent_id = #{portTerminal.parentId} + diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/ShippingTemplateController.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/ShippingTemplateController.java index 055ddac..b8e3444 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/ShippingTemplateController.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/ShippingTemplateController.java @@ -80,22 +80,29 @@ public class ShippingTemplateController extends BladeController { return R.data(shippingTemplateService.selectShippingTemplatePage(Condition.getPage(query), shippingTemplate)); } - @PostMapping("/submit") + @GetMapping("/next-code") @ApiOperationSupport(order = 3) + @Operation(summary = "下一个模板编号") + public R nextCode() { + return R.data(shippingTemplateService.nextTemplateCode()); + } + + @PostMapping("/submit") + @ApiOperationSupport(order = 4) @Operation(summary = "新增或修改", description = "传入shippingTemplate") public R submit(@RequestBody ShippingTemplate shippingTemplate) { return R.status(shippingTemplateService.submit(shippingTemplate)); } @PostMapping("/remove") - @ApiOperationSupport(order = 4) + @ApiOperationSupport(order = 5) @Operation(summary = "逻辑删除", description = "传入ids") public R remove(@Parameter(description = "主键集合", required = true) @RequestParam String ids) { return R.data(shippingTemplateService.removeShippingTemplate(ids)); } @GetMapping("/export-shipping-template") - @ApiOperationSupport(order = 5) + @ApiOperationSupport(order = 6) @Operation(summary = "导出发货模板") public void exportShippingTemplate(ShippingTemplateVO shippingTemplate, @RequestParam(required = false) String ids, HttpServletResponse response) { List list = shippingTemplateService.exportShippingTemplate(shippingTemplate, ids); @@ -103,7 +110,7 @@ public class ShippingTemplateController extends BladeController { } @PostMapping("/copy") - @ApiOperationSupport(order = 6) + @ApiOperationSupport(order = 7) @Operation(summary = "复制", description = "传入id") public R copy(@Parameter(description = "主键", required = true) @RequestParam Long id) { return R.data(shippingTemplateService.copy(id)); diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/TransportPlanController.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/TransportPlanController.java index 2f41417..072e0e6 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/TransportPlanController.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/TransportPlanController.java @@ -37,7 +37,10 @@ import org.springblade.core.secure.annotation.PreAuth; import org.springblade.core.tool.api.R; import org.springblade.core.tool.utils.DateUtil; import org.springblade.core.tool.utils.Func; +import org.springblade.common.excel.ImportFailureExcelUtil; import org.springblade.transport.excel.TransportPlanExcel; +import org.springblade.transport.excel.TransportPlanImportExcel; +import org.springblade.transport.pojo.dto.TransportPlanDispatchRequest; import org.springblade.transport.pojo.entity.TransportPlan; import org.springblade.transport.pojo.vo.BusinessRemoveResultVO; import org.springblade.transport.pojo.vo.TransportPlanVO; @@ -48,8 +51,10 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; -import java.util.ArrayList; +import java.math.BigDecimal; +import java.time.LocalDate; import java.util.List; /** @@ -102,22 +107,63 @@ public class TransportPlanController extends BladeController { ExcelUtil.export(response, "运输计划" + DateUtil.time(), "运输计划", list, TransportPlanExcel.class); } - @PostMapping("/copy") + @GetMapping("/export-template") @ApiOperationSupport(order = 6) + @Operation(summary = "导出运输计划模板") + public void exportTemplate(HttpServletResponse response) { + TransportPlanImportExcel template = new TransportPlanImportExcel(); + template.setPlanName("示例运输计划"); + template.setTransportType("公路运输"); + template.setPlanStartDate(LocalDate.now().toString()); + template.setPlanEndDate(LocalDate.now().plusDays(1).toString()); + template.setCargoName("示例货物"); + template.setCargoType("示例货物类型"); + template.setQuantity(new BigDecimal("10")); + template.setQuantityUnit("吨"); + template.setPackageType("袋装"); + template.setDepartureAddress("示例发货地址"); + template.setArrivalAddress("示例收货地址"); + ExcelUtil.export(response, "运输计划模板", "运输计划导入模板", List.of(template), TransportPlanImportExcel.class); + } + + @PostMapping("/import-transport-plan") + @ApiOperationSupport(order = 7) + @Operation(summary = "导入运输计划", description = "传入 Excel、项目和客户合同") + public R importTransportPlan(MultipartFile file, @RequestParam Long projectId, @RequestParam String projectName, + @RequestParam Long contractId, @RequestParam String contractName, @RequestParam String customerName, + HttpServletResponse response) { + List failureList = transportPlanService.importTransportPlan( + ExcelUtil.read(file, TransportPlanImportExcel.class), projectId, projectName, contractId, contractName, customerName); + if (Func.isNotEmpty(failureList)) { + ImportFailureExcelUtil.export(response, "运输计划导入失败明细" + DateUtil.time(), "导入失败明细", failureList, TransportPlanImportExcel.class); + return null; + } + return R.success("导入数据成功"); + } + + @PostMapping("/copy") + @ApiOperationSupport(order = 8) @Operation(summary = "复制", description = "传入id") public R copy(@Parameter(description = "主键", required = true) @RequestParam Long id) { return R.data(transportPlanService.copy(id)); } + @PostMapping("/dispatch") + @ApiOperationSupport(order = 9) + @Operation(summary = "计划调度并生成运单") + public R dispatch(@RequestBody TransportPlanDispatchRequest request) { + return R.data(transportPlanService.dispatch(request)); + } + @PostMapping("/cancel") - @ApiOperationSupport(order = 7) + @ApiOperationSupport(order = 10) @Operation(summary = "取消", description = "传入id") public R cancel(@Parameter(description = "主键", required = true) @RequestParam Long id) { return R.status(transportPlanService.cancel(id)); } @PostMapping("/complete") - @ApiOperationSupport(order = 8) + @ApiOperationSupport(order = 11) @Operation(summary = "完成", description = "传入id") public R complete(@Parameter(description = "主键", required = true) @RequestParam Long id) { return R.status(transportPlanService.complete(id)); diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/WaybillController.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/WaybillController.java index 142c317..bde3dd4 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/WaybillController.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/WaybillController.java @@ -39,9 +39,18 @@ import org.springblade.core.tool.api.R; import org.springblade.core.tool.utils.DateUtil; import org.springblade.core.tool.utils.Func; import org.springblade.transport.excel.WaybillExcel; +import org.springblade.transport.excel.WaybillImportBatchExcel; +import org.springblade.transport.pojo.entity.ContractManage; +import org.springblade.transport.pojo.entity.CustomerArchive; +import org.springblade.transport.pojo.entity.ProjectApply; +import org.springblade.transport.pojo.entity.TransportPlan; import org.springblade.transport.pojo.entity.Waybill; import org.springblade.transport.pojo.vo.BusinessRemoveResultVO; import org.springblade.transport.pojo.vo.WaybillVO; +import org.springblade.transport.service.IContractManageService; +import org.springblade.transport.service.ICustomerArchiveService; +import org.springblade.transport.service.IProjectApplyService; +import org.springblade.transport.service.ITransportPlanService; import org.springblade.transport.service.IWaybillService; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; @@ -52,7 +61,9 @@ import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; import java.util.ArrayList; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; /** * 运单管理 控制器 @@ -67,6 +78,10 @@ import java.util.List; public class WaybillController extends BladeController { private final IWaybillService waybillService; + private final IProjectApplyService projectApplyService; + private final IContractManageService contractManageService; + private final ICustomerArchiveService customerArchiveService; + private final ITransportPlanService transportPlanService; @GetMapping("/detail") @ApiOperationSupport(order = 1) @@ -82,22 +97,46 @@ public class WaybillController extends BladeController { return R.data(waybillService.selectWaybillPage(Condition.getPage(query), waybill)); } - @PostMapping("/submit") + @GetMapping("/import-batch/options") @ApiOperationSupport(order = 3) + @Operation(summary = "运单批量导入选项") + public R>>> importBatchOptions() { + Map>> options = new LinkedHashMap<>(); + options.put("projects", projectApplyService.list().stream() + .filter(project -> "approved".equals(project.getApprovalStatus())) + .map(project -> option(project.getId(), "projectName", project.getProjectName(), "customerName", project.getCustomerNames())) + .toList()); + options.put("contracts", contractManageService.list().stream() + .filter(contract -> "approved".equals(contract.getApprovalStatus())) + .map(contract -> option(contract.getId(), "contractName", contract.getContractName(), "projectId", contract.getProjectId())) + .toList()); + options.put("carriers", customerArchiveService.list().stream() + .filter(customer -> "承运商".equals(customer.getCustomerType()) && Integer.valueOf(1).equals(customer.getStatus())) + .map(customer -> option(customer.getId(), "name", customer.getFullName(), null, null)) + .toList()); + options.put("creators", new ArrayList<>()); + options.put("plans", transportPlanService.list().stream() + .map(plan -> option(plan.getId(), "planName", plan.getPlanName(), "projectId", plan.getProjectId())) + .toList()); + return R.data(options); + } + + @PostMapping("/submit") + @ApiOperationSupport(order = 4) @Operation(summary = "新增或修改", description = "传入waybill") public R submit(@RequestBody Waybill waybill) { return R.status(waybillService.submit(waybill)); } @PostMapping("/remove") - @ApiOperationSupport(order = 4) + @ApiOperationSupport(order = 5) @Operation(summary = "逻辑删除", description = "传入ids") public R remove(@Parameter(description = "主键集合", required = true) @RequestParam String ids) { return R.data(waybillService.removeWaybill(ids)); } @GetMapping("/export-waybill-manage") - @ApiOperationSupport(order = 5) + @ApiOperationSupport(order = 6) @Operation(summary = "导出运单管理") public void exportWaybill(WaybillVO waybill, @RequestParam(required = false) String ids, HttpServletResponse response) { List list = waybillService.exportWaybill(waybill, ids); @@ -105,7 +144,7 @@ public class WaybillController extends BladeController { } @PostMapping("/import-waybill-manage") - @ApiOperationSupport(order = 6) + @ApiOperationSupport(order = 7) @Operation(summary = "导入运单管理", description = "传入excel") public R importWaybill(MultipartFile file, HttpServletResponse response) { List failureList = waybillService.importWaybill(ExcelUtil.read(file, WaybillExcel.class)); @@ -117,45 +156,62 @@ public class WaybillController extends BladeController { } @GetMapping("/export-template") - @ApiOperationSupport(order = 7) + @ApiOperationSupport(order = 8) @Operation(summary = "导出模板") public void exportTemplate(HttpServletResponse response) { ExcelUtil.export(response, "运单管理模板", "运单管理导入模板", new ArrayList(), WaybillExcel.class); } + @GetMapping("/import-batch/export-template") + @ApiOperationSupport(order = 9) + @Operation(summary = "导出运单批量导入模板") + public void exportImportBatchTemplate(HttpServletResponse response) { + ExcelUtil.export(response, "运单批量导入模板", "运单批量导入模板", new ArrayList(), WaybillImportBatchExcel.class); + } + @PostMapping("/copy") - @ApiOperationSupport(order = 8) + @ApiOperationSupport(order = 10) @Operation(summary = "复制", description = "传入id") public R copy(@Parameter(description = "主键", required = true) @RequestParam Long id) { return R.data(waybillService.copy(id)); } @PostMapping("/cancel") - @ApiOperationSupport(order = 9) + @ApiOperationSupport(order = 11) @Operation(summary = "取消", description = "传入id") public R cancel(@Parameter(description = "主键", required = true) @RequestParam Long id) { return R.status(waybillService.cancel(id)); } @PostMapping("/reassign") - @ApiOperationSupport(order = 10) + @ApiOperationSupport(order = 12) @Operation(summary = "重新派单", description = "传入id") public R reassign(@Parameter(description = "主键", required = true) @RequestParam Long id) { return R.status(waybillService.reassign(id)); } @PostMapping("/complete") - @ApiOperationSupport(order = 11) + @ApiOperationSupport(order = 13) @Operation(summary = "完成", description = "传入id") public R complete(@Parameter(description = "主键", required = true) @RequestParam Long id) { return R.status(waybillService.complete(id)); } @PostMapping("/batch-complete") - @ApiOperationSupport(order = 12) + @ApiOperationSupport(order = 14) @Operation(summary = "批量完成", description = "传入ids") public R batchComplete(@Parameter(description = "主键集合", required = true) @RequestParam String ids) { return R.data(waybillService.batchComplete(ids)); } + private Map option(Long id, String nameKey, String name, String extraKey, Object extraValue) { + Map option = new LinkedHashMap<>(); + option.put("id", id); + option.put(nameKey, name); + if (Func.isNotEmpty(extraKey)) { + option.put(extraKey, extraValue); + } + return option; + } + } diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/TransportPlanImportExcel.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/TransportPlanImportExcel.java new file mode 100644 index 0000000..09c7523 --- /dev/null +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/TransportPlanImportExcel.java @@ -0,0 +1,96 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package org.springblade.transport.excel; + +import cn.idev.excel.annotation.ExcelIgnore; +import cn.idev.excel.annotation.ExcelProperty; +import cn.idev.excel.annotation.write.style.ColumnWidth; +import cn.idev.excel.annotation.write.style.ContentRowHeight; +import cn.idev.excel.annotation.write.style.HeadRowHeight; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; + +/** + * 运输计划导入 Excel + * + * @author Chill + */ +@Data +@ColumnWidth(22) +@HeadRowHeight(20) +@ContentRowHeight(18) +public class TransportPlanImportExcel implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + @ExcelProperty("*计划名称") + private String planName; + @ExcelProperty("*运输方式") + private String transportType; + @ExcelProperty("*计划开始日期") + private String planStartDate; + @ExcelProperty("*计划结束日期") + private String planEndDate; + @ExcelProperty("货物名称") + private String cargoName; + @ExcelProperty("*货物类型") + private String cargoType; + @ExcelProperty("数量") + private BigDecimal quantity; + @ExcelProperty("数量单位") + private String quantityUnit; + @ExcelProperty("包装") + private String packageType; + @ExcelProperty("品牌") + private String brand; + @ExcelProperty("规格") + private String specification; + @ExcelProperty("型号") + private String model; + @ExcelProperty("物料编码") + private String materialCode; + @ExcelProperty("设备编码") + private String deviceCode; + @ExcelProperty("*发货地址") + private String departureAddress; + @ExcelProperty("发货联系人") + private String departureContact; + @ExcelProperty("发货联系方式") + private String departurePhone; + @ExcelProperty("*收货地址") + private String arrivalAddress; + @ExcelProperty("收货联系人") + private String arrivalContact; + @ExcelProperty("收货联系方式") + private String arrivalPhone; + @ExcelProperty("备注") + private String remark; + + @ExcelIgnore + private String errorMessage; + +} diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/WaybillExcel.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/WaybillExcel.java index 5f9e605..3a68f3e 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/WaybillExcel.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/WaybillExcel.java @@ -34,6 +34,7 @@ import java.io.Serializable; import java.math.BigDecimal; import java.time.LocalDate; import java.time.LocalDateTime; +import java.time.LocalDate; /** * 运单管理 Excel @@ -107,10 +108,10 @@ public class WaybillExcel implements Serializable { private String escortPhone; @ExcelProperty("里程(km)") private BigDecimal mileage; - @ExcelProperty("预计发货时间") - private LocalDateTime estimatedStartTime; - @ExcelProperty("预计完成时间") - private LocalDateTime estimatedEndTime; + @ExcelProperty("预计发货日期") + private LocalDate estimatedStartTime; + @ExcelProperty("预计完成日期") + private LocalDate estimatedEndTime; @ExcelProperty("单价") private BigDecimal unitPrice; @ExcelProperty("计价单位") diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/WaybillImportBatchExcel.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/WaybillImportBatchExcel.java new file mode 100644 index 0000000..c8a05c9 --- /dev/null +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/WaybillImportBatchExcel.java @@ -0,0 +1,66 @@ +package org.springblade.transport.excel; + +import cn.idev.excel.annotation.ExcelProperty; +import cn.idev.excel.annotation.write.style.ColumnWidth; +import cn.idev.excel.annotation.write.style.ContentRowHeight; +import cn.idev.excel.annotation.write.style.HeadRowHeight; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; + +/** + * 运单批量导入模板。 + */ +@Data +@ColumnWidth(22) +@HeadRowHeight(28) +@ContentRowHeight(18) +public class WaybillImportBatchExcel implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + @ExcelProperty("原始单号") + private String originalNo; + @ExcelProperty("*车牌号/航班号/船号/班列号") + private String vehicleNo; + @ExcelProperty("*司机/船长") + private String driverName; + @ExcelProperty("*运输类型") + private String transportType; + @ExcelProperty("*货物名称") + private String cargoName; + @ExcelProperty("*货物类型") + private String cargoType; + @ExcelProperty("重量") + private BigDecimal quantity; + @ExcelProperty("*发货地址") + private String departureAddress; + @ExcelProperty("*发货联系人") + private String departureContact; + @ExcelProperty("*发货联系人电话") + private String departurePhone; + @ExcelProperty("*到货地址") + private String arrivalAddress; + @ExcelProperty("*到货联系人") + private String arrivalContact; + @ExcelProperty("*收货联系人电话") + private String arrivalPhone; + @ExcelProperty("*开始时间") + private String startDate; + @ExcelProperty("*结束时间") + private String endDate; + @ExcelProperty("*单价") + private BigDecimal unitPrice; + @ExcelProperty("*运费") + private BigDecimal freight; + @ExcelProperty("其他费用合计") + private BigDecimal otherFeeTotal; + @ExcelProperty("运费合计") + private BigDecimal freightTotal; + @ExcelProperty("备注") + private String remark; + +} diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/IShippingTemplateService.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/IShippingTemplateService.java index 7511237..31a7cfd 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/IShippingTemplateService.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/IShippingTemplateService.java @@ -40,6 +40,7 @@ public interface IShippingTemplateService extends BaseService IPage selectShippingTemplatePage(IPage page, ShippingTemplateVO shippingTemplate); ShippingTemplateVO detail(Long id); + String nextTemplateCode(); boolean submit(ShippingTemplate shippingTemplate); BusinessRemoveResultVO removeShippingTemplate(String ids); List exportShippingTemplate(ShippingTemplateVO shippingTemplate, String ids); diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/ITransportPlanService.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/ITransportPlanService.java index e50d37f..bcf452f 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/ITransportPlanService.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/ITransportPlanService.java @@ -25,6 +25,8 @@ package org.springblade.transport.service; import com.baomidou.mybatisplus.core.metadata.IPage; import org.springblade.core.mp.base.BaseService; import org.springblade.transport.excel.TransportPlanExcel; +import org.springblade.transport.excel.TransportPlanImportExcel; +import org.springblade.transport.pojo.dto.TransportPlanDispatchRequest; import org.springblade.transport.pojo.entity.TransportPlan; import org.springblade.transport.pojo.vo.BusinessRemoveResultVO; import org.springblade.transport.pojo.vo.TransportPlanVO; @@ -43,7 +45,9 @@ public interface ITransportPlanService extends BaseService { boolean submit(TransportPlan transportPlan); BusinessRemoveResultVO removeTransportPlan(String ids); List exportTransportPlan(TransportPlanVO transportPlan, String ids); + List importTransportPlan(List data, Long projectId, String projectName, Long contractId, String contractName, String customerName); TransportPlanVO copy(Long id); + int dispatch(TransportPlanDispatchRequest request); boolean cancel(Long id); boolean complete(Long id); diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/ShippingTemplateServiceImpl.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/ShippingTemplateServiceImpl.java index 7ba3485..bce8f82 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/ShippingTemplateServiceImpl.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/ShippingTemplateServiceImpl.java @@ -67,6 +67,11 @@ public class ShippingTemplateServiceImpl extends BaseServiceImpl latestList = list(Wrappers.lambdaQuery() .select(ShippingTemplate::getTemplateCode) .likeRight(ShippingTemplate::getTemplateCode, prefix) diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/TransportPlanServiceImpl.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/TransportPlanServiceImpl.java index b2c6bbf..a6e56db 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/TransportPlanServiceImpl.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/TransportPlanServiceImpl.java @@ -25,26 +25,33 @@ package org.springblade.transport.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import lombok.AllArgsConstructor; import org.springblade.core.log.exception.ServiceException; import org.springblade.core.mp.base.BaseServiceImpl; +import org.springblade.core.tool.jackson.JsonUtil; import org.springblade.core.tool.utils.BeanUtil; import org.springblade.core.tool.utils.Func; import org.springblade.system.cache.UserCache; import org.springblade.system.pojo.entity.Dept; import org.springblade.transport.excel.TransportPlanExcel; +import org.springblade.transport.excel.TransportPlanImportExcel; import java.time.LocalDate; import java.time.format.DateTimeFormatter; import org.springblade.transport.mapper.TransportPlanMapper; +import org.springblade.transport.pojo.dto.TransportPlanDispatchRequest; import org.springblade.transport.pojo.entity.TransportPlan; +import org.springblade.transport.pojo.entity.Waybill; import org.springblade.transport.pojo.vo.BusinessRemoveResultVO; import org.springblade.transport.pojo.vo.TransportPlanVO; import org.springblade.transport.service.ITransportPlanService; +import org.springblade.transport.service.IWaybillService; import org.springblade.transport.support.TransportBusinessSupport; import org.springblade.transport.wrapper.TransportPlanWrapper; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.ArrayList; +import java.util.LinkedHashMap; import java.util.List; import java.util.Objects; @@ -54,8 +61,11 @@ import java.util.Objects; * @author Chill */ @Service +@AllArgsConstructor public class TransportPlanServiceImpl extends BaseServiceImpl implements ITransportPlanService { + private final IWaybillService waybillService; + @Override public IPage selectTransportPlanPage(IPage page, TransportPlanVO transportPlan) { IPage entityPage = page(page, buildQuery(transportPlan)); @@ -125,6 +135,106 @@ public class TransportPlanServiceImpl extends BaseServiceImpl importTransportPlan(List data, Long projectId, String projectName, Long contractId, String contractName, String customerName) { + if (Func.isEmpty(data)) { + throw new ServiceException("导入数据不能为空"); + } + if (Func.isEmpty(projectId)) { + throw new ServiceException("项目不能为空"); + } + TransportBusinessSupport.validateRequired(projectName, "项目不能为空"); + if (Func.isEmpty(contractId)) { + throw new ServiceException("客户合同不能为空"); + } + TransportBusinessSupport.validateRequired(contractName, "客户合同不能为空"); + List errorList = new ArrayList<>(); + for (int index = 0; index < data.size(); index++) { + TransportPlanImportExcel excel = data.get(index); + try { + LocalDate planStartDate = parseImportDate(excel.getPlanStartDate(), "计划开始日期"); + LocalDate planEndDate = parseImportDate(excel.getPlanEndDate(), "计划结束日期"); + validateImportExcel(excel, planStartDate, planEndDate); + TransportPlan transportPlan = new TransportPlan(); + transportPlan.setProjectId(projectId); + transportPlan.setProjectName(projectName); + transportPlan.setContractId(contractId); + transportPlan.setContractName(contractName); + transportPlan.setCustomerName(customerName); + transportPlan.setPlanName(excel.getPlanName()); + transportPlan.setTransportType(excel.getTransportType()); + transportPlan.setPlanStartDate(planStartDate); + transportPlan.setPlanEndDate(planEndDate); + transportPlan.setDepartureAddress(excel.getDepartureAddress()); + transportPlan.setDepartureContact(excel.getDepartureContact()); + transportPlan.setDeparturePhone(excel.getDeparturePhone()); + transportPlan.setArrivalAddress(excel.getArrivalAddress()); + transportPlan.setArrivalContact(excel.getArrivalContact()); + transportPlan.setArrivalPhone(excel.getArrivalPhone()); + transportPlan.setRemark(excel.getRemark()); + transportPlan.setGoodsJson(JsonUtil.toJson(List.of(importGoods(excel)))); + transportPlan.setDataSource("批量导入"); + transportPlan.setBusinessStatus("waiting_dispatch"); + submit(transportPlan); + } catch (Exception exception) { + excel.setErrorMessage("第" + (index + 2) + "行:" + exception.getMessage()); + errorList.add(excel); + } + } + return errorList; + } + + private LinkedHashMap importGoods(TransportPlanImportExcel excel) { + LinkedHashMap goods = new LinkedHashMap<>(); + goods.put("cargoName", excel.getCargoName()); + goods.put("cargoType", excel.getCargoType()); + goods.put("quantity", excel.getQuantity()); + goods.put("quantityUnit", excel.getQuantityUnit()); + goods.put("packageType", excel.getPackageType()); + goods.put("brand", excel.getBrand()); + goods.put("specification", excel.getSpecification()); + goods.put("model", excel.getModel()); + goods.put("materialCode", excel.getMaterialCode()); + goods.put("deviceCode", excel.getDeviceCode()); + return goods; + } + + private void validateImportExcel(TransportPlanImportExcel excel, LocalDate planStartDate, LocalDate planEndDate) { + TransportBusinessSupport.validateRequired(excel.getPlanName(), "计划名称不能为空"); + TransportBusinessSupport.validateRequired(excel.getTransportType(), "运输方式不能为空"); + TransportBusinessSupport.validateRequired(excel.getCargoType(), "货物类型不能为空"); + TransportBusinessSupport.validateRequired(excel.getDepartureAddress(), "发货地址不能为空"); + TransportBusinessSupport.validateRequired(excel.getArrivalAddress(), "收货地址不能为空"); + validateImportLength(excel.getPlanName(), 50, "计划名称"); + validateImportLength(excel.getDepartureAddress(), 255, "发货地址"); + validateImportLength(excel.getArrivalAddress(), 255, "收货地址"); + validateImportLength(excel.getDepartureContact(), 50, "发货联系人"); + validateImportLength(excel.getArrivalContact(), 50, "收货联系人"); + validateImportLength(excel.getDeparturePhone(), 50, "发货联系方式"); + validateImportLength(excel.getArrivalPhone(), 50, "收货联系方式"); + validateImportLength(excel.getRemark(), 200, "备注"); + TransportBusinessSupport.validatePhone(excel.getDeparturePhone(), "发货联系方式格式不正确"); + TransportBusinessSupport.validatePhone(excel.getArrivalPhone(), "收货联系方式格式不正确"); + TransportBusinessSupport.validateNonNegative(excel.getQuantity(), "数量"); + TransportBusinessSupport.validateDateRange(planStartDate, planEndDate, "计划结束日期不能早于计划开始日期"); + } + + private LocalDate parseImportDate(String value, String fieldName) { + TransportBusinessSupport.validateRequired(value, fieldName + "不能为空"); + try { + return LocalDate.parse(value.trim(), DateTimeFormatter.ISO_LOCAL_DATE); + } catch (Exception exception) { + throw new ServiceException(fieldName + "格式必须为 YYYY-MM-DD"); + } + } + + private void validateImportLength(String value, int maxLength, String fieldName) { + if (Func.isNotEmpty(value) && value.length() > maxLength) { + throw new ServiceException(fieldName + "不能超过" + maxLength + "个字符"); + } + } + @Override @Transactional(rollbackFor = Exception.class) public TransportPlanVO copy(Long id) { @@ -163,6 +273,58 @@ public class TransportPlanServiceImpl extends BaseServiceImpl if (waybill.getEstimatedStartTime() != null && waybill.getEstimatedEndTime() != null && waybill.getEstimatedEndTime().isBefore(waybill.getEstimatedStartTime())) { - throw new ServiceException("预计完成时间不能早于预计发货时间"); + throw new ServiceException("预计完成日期不能早于预计发货日期"); } } diff --git a/doc/sql/transport/blade_tms_business.sql b/doc/sql/transport/blade_tms_business.sql index e8d557f..f4f02e7 100644 --- a/doc/sql/transport/blade_tms_business.sql +++ b/doc/sql/transport/blade_tms_business.sql @@ -245,8 +245,8 @@ CREATE TABLE `blade_waybill` ( `escort_name` varchar(100) DEFAULT NULL COMMENT '押运人', `escort_phone` varchar(50) DEFAULT NULL COMMENT '押运人手机号', `mileage` decimal(18,2) DEFAULT NULL COMMENT '里程', - `estimated_start_time` datetime DEFAULT NULL COMMENT '预计发货时间', - `estimated_end_time` datetime DEFAULT NULL COMMENT '预计完成时间', + `estimated_start_time` date DEFAULT NULL COMMENT '预计发货日期', + `estimated_end_time` date DEFAULT NULL COMMENT '预计完成日期', `unit_price` decimal(18,2) DEFAULT NULL COMMENT '单价', `price_unit` varchar(50) DEFAULT NULL COMMENT '计价单位', `other_fee_total` decimal(18,2) DEFAULT NULL COMMENT '其他费用合计', diff --git a/doc/sql/transport/blade_waybill_date_patch_20260805.sql b/doc/sql/transport/blade_waybill_date_patch_20260805.sql new file mode 100644 index 0000000..3bd6257 --- /dev/null +++ b/doc/sql/transport/blade_waybill_date_patch_20260805.sql @@ -0,0 +1,3 @@ +ALTER TABLE `blade_waybill` + MODIFY COLUMN `estimated_start_time` date DEFAULT NULL COMMENT '预计发货日期', + MODIFY COLUMN `estimated_end_time` date DEFAULT NULL COMMENT '预计完成日期'; diff --git a/doc/sql/transport/blade_waybill_import_batch_20260805.sql b/doc/sql/transport/blade_waybill_import_batch_20260805.sql new file mode 100644 index 0000000..6c66f2e --- /dev/null +++ b/doc/sql/transport/blade_waybill_import_batch_20260805.sql @@ -0,0 +1,33 @@ +CREATE TABLE IF NOT EXISTS `blade_waybill_import_batch` ( + `id` bigint NOT NULL, + `tenant_id` varchar(12) NOT NULL DEFAULT '000000', + `batch_no` varchar(64) NOT NULL COMMENT '运单批次号', + `project_id` bigint DEFAULT NULL, + `project_name` varchar(200) DEFAULT NULL, + `customer_name` varchar(200) DEFAULT NULL, + `contract_id` bigint DEFAULT NULL, + `contract_name` varchar(200) DEFAULT NULL, + `carrier_type` varchar(32) DEFAULT NULL, + `carrier_ids` varchar(1000) DEFAULT NULL, + `carrier_name` varchar(500) DEFAULT NULL, + `import_type` varchar(32) NOT NULL COMMENT 'waybill/settlement', + `import_status` varchar(32) NOT NULL COMMENT 'draft/processing/completed', + `plan_id` bigint DEFAULT NULL, + `plan_name` varchar(200) DEFAULT NULL, + `waybill_count` int NOT NULL DEFAULT 0, + `remark` varchar(500) DEFAULT NULL, + `create_user` bigint DEFAULT NULL, + `create_dept` bigint DEFAULT NULL, + `create_time` datetime DEFAULT NULL, + `update_user` bigint DEFAULT NULL, + `update_time` datetime DEFAULT NULL, + `status` tinyint NOT NULL DEFAULT 1, + `is_deleted` tinyint NOT NULL DEFAULT 0, + PRIMARY KEY (`id`), + UNIQUE KEY `uk_waybill_import_batch_no` (`tenant_id`,`batch_no`), + KEY `idx_waybill_import_batch_project` (`project_id`), + KEY `idx_waybill_import_batch_create_time` (`create_time`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='运单批量导入批次记录'; + +ALTER TABLE `blade_waybill` ADD COLUMN `import_batch_id` bigint DEFAULT NULL COMMENT '运单导入批次ID'; +ALTER TABLE `blade_waybill` ADD KEY `idx_waybill_import_batch_id` (`import_batch_id`); diff --git a/doc/sql/transport/blade_waybill_patch_20260804.sql b/doc/sql/transport/blade_waybill_patch_20260804.sql index d75703f..497b288 100644 --- a/doc/sql/transport/blade_waybill_patch_20260804.sql +++ b/doc/sql/transport/blade_waybill_patch_20260804.sql @@ -20,8 +20,8 @@ ALTER TABLE `blade_waybill` ADD COLUMN `escort_name` varchar(100) DEFAULT NULL COMMENT '押运人' AFTER `trailer_vehicle_no`, ADD COLUMN `escort_phone` varchar(50) DEFAULT NULL COMMENT '押运人手机号' AFTER `escort_name`, ADD COLUMN `mileage` decimal(18,2) DEFAULT NULL COMMENT '里程' AFTER `escort_phone`, - ADD COLUMN `estimated_start_time` datetime DEFAULT NULL COMMENT '预计发货时间' AFTER `mileage`, - ADD COLUMN `estimated_end_time` datetime DEFAULT NULL COMMENT '预计完成时间' AFTER `estimated_start_time`, + ADD COLUMN `estimated_start_time` date DEFAULT NULL COMMENT '预计发货日期' AFTER `mileage`, + ADD COLUMN `estimated_end_time` date DEFAULT NULL COMMENT '预计完成日期' AFTER `estimated_start_time`, ADD COLUMN `unit_price` decimal(18,2) DEFAULT NULL COMMENT '单价' AFTER `estimated_end_time`, ADD COLUMN `price_unit` varchar(50) DEFAULT NULL COMMENT '计价单位' AFTER `unit_price`, ADD COLUMN `other_fee_total` decimal(18,2) DEFAULT NULL COMMENT '其他费用合计' AFTER `price_unit`,