diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/ManageWaybillController.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/ManageWaybillController.java index ee10e0b6..67c2d15c 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/ManageWaybillController.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/ManageWaybillController.java @@ -39,6 +39,7 @@ import org.springblade.transport.pojo.vo.AdminVehicleOptionVO; import org.springblade.transport.pojo.vo.AdminWaybillCardVO; import org.springblade.transport.pojo.vo.AdminWaybillDetailVO; import org.springblade.transport.pojo.vo.WaybillPunchRecordsVO; +import org.springblade.transport.pojo.vo.WaybillTrackVO; import org.springblade.transport.service.IManageWaybillService; import org.springblade.transport.service.IWaybillService; import org.springframework.web.bind.annotation.GetMapping; @@ -159,4 +160,14 @@ public class ManageWaybillController extends BladeController { return R.status(waybillService.savePunchRecord(request)); } + @PostMapping("/track") + @ApiOperationSupport(order = 11) + @Operation(summary = "车辆历史轨迹", description = "小程序调度端:仅需登录态,按运单绑定车牌和日期区间查询 LBS 轨迹") + public R track( + @Parameter(description = "运单ID", required = true) @RequestParam Long id, + @Parameter(description = "开始日期 YYYY-MM-DD", required = true) @RequestParam String startDate, + @Parameter(description = "结束日期 YYYY-MM-DD", required = true) @RequestParam String endDate) { + return R.data(waybillService.trackVehicle(id, startDate, endDate)); + } + } diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/SettlementImportFeeReader.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/SettlementImportFeeReader.java index cc43d6e1..12b44580 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/SettlementImportFeeReader.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/SettlementImportFeeReader.java @@ -54,7 +54,8 @@ import java.util.Set; /** * 导入结算单 Excel 读取器(Apache POI)。 *

- * 定位「*运输费」或「运输费」列,其后直到「结算金额」为动态费用列。 + * 定位「*运输费」或「运输费」列,其后直到日期/备注等固定列(兼容旧模板中的「结算金额」)为动态费用列。 + * 结算金额不由模板填写,解析时按「运输费 + 各费用项」自动合计。 * * @author Chill */ @@ -66,6 +67,14 @@ public final class SettlementImportFeeReader { "actualStartDate", "actualEndDate", "planStartDate", "planEndDate" ); private static final Set OPTIONAL_DECIMAL_FIELDS = Set.of("mileage", "unitPrice"); + /** 费用列结束边界:其后为固定业务列;含旧模板「结算金额」以便兼容历史文件 */ + private static final Set FEE_END_HEADERS = Set.of( + "结算金额", + "*实际发货时间", "实际发货时间", + "*实际完成时间", "实际完成时间", + "预计发货时间", "预计完成时间", + "备注", "同一运单标识号" + ); private SettlementImportFeeReader() { } @@ -119,15 +128,12 @@ public final class SettlementImportFeeReader { if (transportFeeColumn < 0) { throw new ServiceException("未找到「*运输费」或「运输费」列"); } - int settlementAmountColumn = findColumnExact(headers, "结算金额"); List feeColumns = new ArrayList<>(); List feeItemNames = new ArrayList<>(); - int endColumn = settlementAmountColumn > transportFeeColumn - ? settlementAmountColumn - : headers.keySet().stream().mapToInt(Integer::intValue).max().orElse(transportFeeColumn) + 1; + int endColumn = findFeeEndColumn(headers, transportFeeColumn); for (int column = transportFeeColumn + 1; column < endColumn; column++) { String name = headers.get(column); - if (name == null || name.isBlank() || "结算金额".equals(name)) { + if (name == null || name.isBlank() || FEE_END_HEADERS.contains(name)) { continue; } feeColumns.add(column); @@ -229,7 +235,8 @@ public final class SettlementImportFeeReader { } else if ("transportFee".equals(field) || "quantity".equals(field)) { mapped.put(field, decimal(cellString(poiCell), trimmedHeader, excelRowIndex)); } else if ("settlementAmount".equals(field)) { - mapped.put(field, optionalDecimal(cellString(poiCell), trimmedHeader, excelRowIndex)); + // 旧模板可能仍含结算金额列,读入后忽略,统一按运输费+费用项自动合计 + continue; } else if ("serialNumber".equals(field)) { mapped.put(field, intValue(cellString(poiCell))); } else { @@ -255,9 +262,7 @@ public final class SettlementImportFeeReader { BigDecimal feeItemTotal = feeItems.values().stream() .reduce(BigDecimal.ZERO, BigDecimal::add) .setScale(2, RoundingMode.HALF_UP); - BigDecimal settlementAmount = mapped.get("settlementAmount") == null - ? transportFee.add(feeItemTotal).setScale(2, RoundingMode.HALF_UP) - : toDecimal(mapped.get("settlementAmount")); + BigDecimal settlementAmount = transportFee.add(feeItemTotal).setScale(2, RoundingMode.HALF_UP); mapped.put("settlementAmount", settlementAmount); mapped.put("结算金额", settlementAmount); mapped.put("feeItems", feeItems); @@ -284,6 +289,21 @@ public final class SettlementImportFeeReader { return findColumnExact(headers, "运输费"); } + /** 费用列右边界:取运输费之后第一个固定结束表头列下标;无则取表头最大下标 + 1。 */ + private static int findFeeEndColumn(Map headers, int transportFeeColumn) { + int endColumn = headers.keySet().stream().mapToInt(Integer::intValue).max().orElse(transportFeeColumn) + 1; + for (Map.Entry entry : headers.entrySet()) { + int column = entry.getKey(); + if (column <= transportFeeColumn) { + continue; + } + if (FEE_END_HEADERS.contains(entry.getValue()) && column < endColumn) { + endColumn = column; + } + } + return endColumn; + } + private static int findColumnExact(Map headers, String header) { return headers.entrySet().stream() .filter(entry -> header.equals(entry.getValue())) diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/SettlementImportTemplateExcel.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/SettlementImportTemplateExcel.java index a060b15e..871599eb 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/SettlementImportTemplateExcel.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/SettlementImportTemplateExcel.java @@ -100,7 +100,8 @@ public class SettlementImportTemplateExcel implements Serializable { private BigDecimal loadingFee; @ExcelProperty("过路费") private BigDecimal tollFee; - @ExcelProperty("结算金额") + /** 模板已去掉结算金额列;仅失败明细内部保留,不导出为表头 */ + @ExcelIgnore private BigDecimal settlementAmount; @ExcelProperty("*实际发货时间") private String actualStartDate; diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/SettlementImportTemplateExporter.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/SettlementImportTemplateExporter.java index a33dbb6c..91eb317e 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/SettlementImportTemplateExporter.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/SettlementImportTemplateExporter.java @@ -85,7 +85,6 @@ public final class SettlementImportTemplateExporter { ); private static final List FIXED_HEADERS_AFTER_FEE = List.of( - "结算金额", "*实际发货时间", "*实际完成时间", "预计发货时间", @@ -159,8 +158,8 @@ public final class SettlementImportTemplateExporter { lines.add("2. 运输方式请从下拉项中选择,须与系统字典一致。"); lines.add("3. 日期请填写 yyyy-MM-dd(如 2026-01-01);Excel 日期单元格也可直接选择。"); lines.add("4. 里程(km) 可为空;填写时须为正数。"); - lines.add("5. 「*运输费」之后、「结算金额」之前的列为费用项,列名须与系统费用项主数据名称完全一致;金额不为 0 时若不存在或已停用则无法导入。"); - lines.add("6. 结算金额 = 运输费 + 各费用项合计,系统会按此重算。"); + lines.add("5. 「*运输费」之后、「*实际发货时间」之前的列为费用项,列名须与系统费用项主数据名称完全一致;金额不为 0 时若不存在或已停用则无法导入。"); + lines.add("6. 模板不含结算金额列;导入后系统按「运输费 + 各费用项」自动合计结算金额。"); lines.add("7. 当前可选运输方式:" + String.join("、", transportOptions) + "。"); if (feeItemNames != null && !feeItemNames.isEmpty()) { lines.add("8. 当前启用费用项示例:" + String.join("、", feeItemNames.stream().limit(30).toList()) diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/DriverWaybillServiceImpl.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/DriverWaybillServiceImpl.java index 59e02bb0..534f043d 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/DriverWaybillServiceImpl.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/DriverWaybillServiceImpl.java @@ -349,9 +349,14 @@ public class DriverWaybillServiceImpl implements IDriverWaybillService { /** * 按打卡记录 / 计划时间推导轨迹查询起止日期 + *

+ * TODO(临时):当前仅查当天,便于联调 LBS;恢复正式逻辑时删掉本段 early-return。 */ private LocalDate[] resolveTrackDateRange(Waybill waybill) { LocalDate today = LocalDate.now(); + // TEMP: 行驶轨迹时间段临时改为仅当天 + return new LocalDate[]{today, today}; + /* List punchDates = new ArrayList<>(); nodePunchMapper.selectList(Wrappers.lambdaQuery() .eq(WaybillNodePunch::getWaybillId, waybill.getId()) @@ -383,6 +388,7 @@ public class DriverWaybillServiceImpl implements IDriverWaybillService { return new LocalDate[]{waybill.getStartDate(), today}; } return new LocalDate[]{today.minusDays(3), today}; + */ } @Override diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/ManageWaybillServiceImpl.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/ManageWaybillServiceImpl.java index f8701641..4b1e5c93 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/ManageWaybillServiceImpl.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/ManageWaybillServiceImpl.java @@ -886,11 +886,18 @@ public class ManageWaybillServiceImpl implements IManageWaybillService { /** * 调度端可见组织:普通用户(含各级角色)只出自己所属组织,超级管理员不过滤。 *

- * 与后台运单列表同一条规则({@link TransportBusinessSupport#resolveDeptFilterIds}), - * 小程序端不提供「全部组织」开关,故两个入参固定传空;详情与重新派单不按组织收口。 + * 小程序不校验「所属组织不能为空」:账号没有组织时不按组织收口,避免首页、待处理等接口直接报错。 + * 详情与重新派单本身不走这里。 */ private List manageableDeptIds() { - return TransportBusinessSupport.resolveDeptFilterIds(null, null, "运单管理"); + if (TransportBusinessSupport.isAdministrator()) { + return List.of(); + } + Long deptId = Func.firstLong(AuthUtil.getDeptId()); + if (Func.isEmpty(deptId) || deptId <= 0) { + return List.of(); + } + return List.of(deptId); } /** diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/TransportVehicleServiceImpl.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/TransportVehicleServiceImpl.java index e28d7329..d9e1c734 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/TransportVehicleServiceImpl.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/TransportVehicleServiceImpl.java @@ -291,13 +291,13 @@ public class TransportVehicleServiceImpl extends BaseServiceImpl row, List errors) { - String freightTotalStr = stringValue(row, "freightTotal", "运费合计"); - if (Func.isEmpty(freightTotalStr) || isImportOptionalNumberEmpty(freightTotalStr)) { - return; - } - - try { - BigDecimal freightTotal = new BigDecimal(freightTotalStr); - String freightStr = stringValue(row, "freight", "运费"); - String otherFeeTotalStr = stringValue(row, "otherFeeTotal", "其他费用合计"); - - BigDecimal freight = Func.isNotEmpty(freightStr) && !isImportOptionalNumberEmpty(freightStr) - ? new BigDecimal(freightStr) : BigDecimal.ZERO; - BigDecimal otherFeeTotal = Func.isNotEmpty(otherFeeTotalStr) && !isImportOptionalNumberEmpty(otherFeeTotalStr) - ? new BigDecimal(otherFeeTotalStr) : BigDecimal.ZERO; - - BigDecimal calculatedTotal = freight.add(otherFeeTotal); - if (freightTotal.compareTo(calculatedTotal) != 0) { - errors.add("运费合计应等于运费+其他费用合计"); - } - } catch (NumberFormatException e) { - errors.add("运费相关字段格式不正确"); - } - } - private void validateActualStartDate(Map row, boolean isDraft, List errors) { Object actualStartDate = row.get("actualStartDate"); if (actualStartDate == null || String.valueOf(actualStartDate).isBlank()) { diff --git a/blade-service/blade-transport/src/test/java/org/springblade/transport/service/impl/ManageWaybillOrgScopeTest.java b/blade-service/blade-transport/src/test/java/org/springblade/transport/service/impl/ManageWaybillOrgScopeTest.java index 15a31a27..95346246 100644 --- a/blade-service/blade-transport/src/test/java/org/springblade/transport/service/impl/ManageWaybillOrgScopeTest.java +++ b/blade-service/blade-transport/src/test/java/org/springblade/transport/service/impl/ManageWaybillOrgScopeTest.java @@ -52,7 +52,7 @@ import static org.mockito.Mockito.when; * 三个取数入口必须同一条规则——列表、待处理、首页三档统计各走一次查询,漏一处就会出现 * 「统计说 46 张、列表只出 18 张」这种对不上的数;待重新派单另走库层分组查询,也要单独带上。 *

- * 组织取不到时报错而不是放行:放行等于把全租户的运单发给一个没有组织的账号。 + * 所属组织为空时不拦截:小程序调度端不要求账号必须挂组织。 */ class ManageWaybillOrgScopeTest { @@ -190,13 +190,13 @@ class ManageWaybillOrgScopeTest { } @Test - @DisplayName("组织取不到时报错,不退回不带组织条件") - void blankDeptFailsClosed() { + @DisplayName("组织取不到时不拦截,小程序接口不按组织收口") + void blankDeptDoesNotFail() { authUtil.when(AuthUtil::getDeptId).thenReturn(""); - assertThrows(ServiceException.class, - () -> service.pageList(1, 10, null, null, null, null, null, null), - "没有组织的账号不能拿到全租户运单"); + service.pageList(1, 10, null, null, null, null, null, null); + + assertTrue(capturedPageQuery().getDeptIds().isEmpty(), "没有组织时不报错、不按组织过滤"); } // ------------------------------------------------------------ 夹具