diff --git a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/DriverWaybillTabCountsVO.java b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/DriverWaybillTabCountsVO.java index 3aa95f5e..79787cb7 100644 --- a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/DriverWaybillTabCountsVO.java +++ b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/DriverWaybillTabCountsVO.java @@ -40,7 +40,7 @@ public class DriverWaybillTabCountsVO implements Serializable { @Serial private static final long serialVersionUID = 1L; - @Schema(description = "全部(待接单+运输中+已完成)") + @Schema(description = "全部(待接单+运输中+待确认+已完成)") private long all; @Schema(description = "待接单") @@ -49,6 +49,9 @@ public class DriverWaybillTabCountsVO implements Serializable { @Schema(description = "进行中(运输中)") private long doing; + @Schema(description = "待确认(待回单确认)") + private long confirm; + @Schema(description = "已完成") private long done; diff --git a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/TripTabCountsVO.java b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/TripTabCountsVO.java index 944c4ded..01eb22f7 100644 --- a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/TripTabCountsVO.java +++ b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/TripTabCountsVO.java @@ -40,7 +40,7 @@ public class TripTabCountsVO implements Serializable { @Serial private static final long serialVersionUID = 1L; - @Schema(description = "全部(待接单+进行中+已完成,不含已取消)") + @Schema(description = "全部(待接单+进行中+待确认+已完成,不含已取消)") private long all; @Schema(description = "待接单") @@ -49,6 +49,9 @@ public class TripTabCountsVO implements Serializable { @Schema(description = "进行中") private long doing; + @Schema(description = "待确认(待回单确认)") + private long confirm; + @Schema(description = "已完成") private long done; diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/DriverWaybillController.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/DriverWaybillController.java index 313db1da..06eaf200 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/DriverWaybillController.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/DriverWaybillController.java @@ -107,7 +107,7 @@ public class DriverWaybillController extends BladeController { @GetMapping("/task/current") @ApiOperationSupport(order = 21) - @Operation(summary = "首页:当前运输中运次", description = "当前用户手机号可见的运输中运次(有配载单的一趟车算一个)最新一条") + @Operation(summary = "首页:当前运输中运次", description = "当前用户手机号可见的运输中或待确认(待回单确认)运次(有配载单的一趟车算一个)最新一条") public R tripCurrentTask() { return R.data(driverWaybillService.tripCurrentTask()); } @@ -122,18 +122,18 @@ public class DriverWaybillController extends BladeController { @GetMapping("/task/counts") @ApiOperationSupport(order = 23) - @Operation(summary = "运次 Tab 统计", description = "按运次统计:全部 = 待接单 + 进行中 + 已完成(不含已取消)") + @Operation(summary = "运次 Tab 统计", description = "按运次统计:全部 = 待接单 + 进行中 + 待确认 + 已完成(不含已取消);confirm 为待确认(待回单确认)") public R tripCounts() { return R.data(driverWaybillService.tripTabCounts()); } @GetMapping("/task/page") @ApiOperationSupport(order = 24) - @Operation(summary = "运次分页列表", description = "一页即若干个运次;status:空=全部,0待接单,1运输中,2已完成;keyword 支持运单号/配载单号/项目/司机/车牌/起终点/地址") + @Operation(summary = "运次分页列表", description = "一页即若干个运次;status:空=全部,0待接单,1运输中(不含待回单确认),2已完成,4待确认(待回单确认);keyword 支持运单号/配载单号/项目/司机/车牌/起终点/地址") public R> tripPage( @Parameter(description = "当前页") @RequestParam(required = false) Integer current, @Parameter(description = "每页运次数") @RequestParam(required = false) Integer size, - @Parameter(description = "状态:0待接单/1运输中/2已完成,不传为全部") @RequestParam(required = false) String status, + @Parameter(description = "状态:0待接单/1运输中/2已完成/4待确认,不传为全部") @RequestParam(required = false) String status, @Parameter(description = "关键字:运单号/配载单号/项目/司机/车牌/起终点/地址") @RequestParam(required = false) String keyword) { Integer statusCode = parseStatus(status); return R.data(driverWaybillService.tripPage(current, size, statusCode, keyword)); 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 81265fc4..3a71937d 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 @@ -84,12 +84,12 @@ public class ManageWaybillController extends BladeController { @GetMapping("/list") @ApiOperationSupport(order = 3) - @Operation(summary = "运单分页列表", description = "当前组织运单;status:0待接单/1运输中/2已完成;exception:exception/normal;transportType:common/load") + @Operation(summary = "运单分页列表", description = "当前组织运单;status:0待接单/1运输中(不含待回单确认)/2已完成/4待确认(待回单确认);exception:exception/normal;transportType:common/load") public R> list( @Parameter(description = "当前页") @RequestParam(required = false) Integer current, @Parameter(description = "每页条数") @RequestParam(required = false) Integer size, @Parameter(description = "关键字:运单号/配载单号/项目/司机/车牌/起终点/地址") @RequestParam(required = false) String keyword, - @Parameter(description = "状态:0待接单/1运输中/2已完成,不传为全部") @RequestParam(required = false) String status, + @Parameter(description = "状态:0待接单/1运输中/2已完成/4待确认,不传为全部") @RequestParam(required = false) String status, @Parameter(description = "异常:exception有异常/normal无异常") @RequestParam(required = false) String exception, @Parameter(description = "运输组织:common普通/load配载") @RequestParam(required = false) String transportType, @Parameter(description = "创建日起 YYYY-MM-DD") @RequestParam(required = false) String startDate, @@ -108,7 +108,7 @@ public class ManageWaybillController extends BladeController { @GetMapping("/pending") @ApiOperationSupport(order = 5) - @Operation(summary = "待处理运单", description = "待接单/运输中;needReassign=true 仅司机已拒单") + @Operation(summary = "待处理运单", description = "待接单/运输中/待确认;needReassign=true 仅司机已拒单") public R> pending( @Parameter(description = "当前页") @RequestParam(required = false) Integer current, @Parameter(description = "每页条数") @RequestParam(required = false) Integer size, 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 b4817c57..25b6bd4a 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 @@ -149,6 +149,9 @@ public class DriverWaybillServiceImpl implements IDriverWaybillService { private static final int TRIP_STATUS_PENDING = 0; private static final int TRIP_STATUS_RUNNING = 1; + /** 运次列表档位:4 待确认(待回单确认)。卡片状态码仍按 1 展示,只有列表 Tab 用它分档 */ + private static final int TRIP_STATUS_PENDING_RETURN_CONFIRM = 4; + private final IWaybillService waybillService; private final IDriverService driverService; private final IMpUserClient mpUserClient; @@ -280,7 +283,8 @@ public class DriverWaybillServiceImpl implements IDriverWaybillService { return null; } TripQueryVO query = TripQueryVO.forDriver(phone); - query.setStatus(TRIP_STATUS_RUNNING); + // 待确认(待回单确认)的趟车司机仍需回到卡片上继续操作,与进行中并列作为当前任务 + query.setStatuses(List.of(TRIP_STATUS_RUNNING, TRIP_STATUS_PENDING_RETURN_CONFIRM)); query.setCurrent(1); query.setSize(1); IPage page = tripQueryService.page(query); @@ -316,6 +320,7 @@ public class DriverWaybillServiceImpl implements IDriverWaybillService { TripTabCountsVO tripCounts = tripQueryService.tabCounts(TripQueryVO.forDriver(phone)); counts.setPending(tripCounts.getPending()); counts.setDoing(tripCounts.getDoing()); + counts.setConfirm(tripCounts.getConfirm()); counts.setDone(tripCounts.getDone()); counts.setAll(tripCounts.getAll()); return counts; 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 c9f42670..8b82d417 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 @@ -236,8 +236,8 @@ public class ManageWaybillServiceImpl implements IManageWaybillService { TripQueryVO query = TripQueryVO.forAdmin(); applyAdminDataScope(query); query.setKeyword(keyword); - // 待处理只要待接单与进行中两档;「未拒单」也是交给运次读服务按子单行筛 - query.setStatuses(List.of(0, 1)); + // 待处理含待接单、进行中与待确认(待回单确认原本就算进行中,拆档后不能从待处理里消失) + query.setStatuses(List.of(0, 1, 4)); query.setExcludeRejectedTrips(Boolean.FALSE.equals(needReassign)); query.setCurrent(pageNo); query.setSize(pageSize); @@ -269,6 +269,19 @@ public class ManageWaybillServiceImpl implements IManageWaybillService { return page; } + /** + * 卡片展示用的原始业务状态:整趟车子单同一业务状态时取它,混着多种状态时为空,卡片按状态码展示。 + *

+ * 待回单确认的趟车状态码仍是 1(运输中),只有这个字段能把它区分开。 + */ + private String uniqueChildBusinessStatus(List children) { + Set statuses = children.stream() + .map(TripVO.TripWaybillVO::getBusinessStatus) + .filter(Func::isNotBlank) + .collect(Collectors.toCollection(LinkedHashSet::new)); + return statuses.size() == 1 ? statuses.iterator().next() : null; + } + /** * 运次卡片:展示单号、整趟车起/经/终与站点数、货物汇总、计划时间都取运次字段。 * 旧字段 id / waybillNo 保留运单语义(未配载就是这张运单,配载取第一张可见子运单), @@ -302,6 +315,7 @@ public class ManageWaybillServiceImpl implements IManageWaybillService { card.setPlanTime(formatLocalDate(trip.getEstimatedStartDate())); card.setPlanTimeEnd(formatLocalDate(trip.getEstimatedEndDate())); card.setStatus(trip.getStatus()); + card.setBusinessStatus(uniqueChildBusinessStatus(children)); card.setDriverName(Func.toStr(trip.getDriverName(), "")); card.setVehicleNo(Func.toStr(trip.getVehicleNo(), "")); card.setCreateTime(formatDateTime(trip.getSortTime())); @@ -336,7 +350,11 @@ public class ManageWaybillServiceImpl implements IManageWaybillService { return childIds.stream().map(this::parseId).filter(Objects::nonNull).anyMatch(exceptionWaybillIds::contains); } - /** 小程序 status → 运次状态:0待接单 / 1进行中 / 2已完成 */ + /** + * 小程序 status → 运次可筛选档位:0待接单 / 1进行中 / 2已完成 / 3已取消 / 4待确认(待回单确认)。 + *

+ * 1 不含待回单确认——待回单确认单独成档,卡片状态码仍是 1。 + */ private Integer toTripStatus(String status) { if (Func.isBlank(status)) { return null; @@ -346,6 +364,7 @@ public class ManageWaybillServiceImpl implements IManageWaybillService { case "1", STATUS_RUNNING, "transporting", "doing" -> 1; case "2", STATUS_COMPLETED, "done" -> 2; case "3", STATUS_CANCELLED -> 3; + case "4", WaybillProcessSupport.STATUS_PENDING_RETURN_CONFIRM, "confirm" -> 4; default -> null; }; } @@ -515,6 +534,7 @@ public class ManageWaybillServiceImpl implements IManageWaybillService { detail.setId(head == null ? loading.getId() : head.getId()); detail.setWaybillNo(head == null ? loading.getLoadingNo() : Func.toStr(head.getWaybillNo(), loading.getLoadingNo())); detail.setStatus(TripAggregation.appStatus(tripBusinessStatus(children))); + detail.setBusinessStatus(tripBusinessStatus(children)); detail.setTransportMode(Func.toStr(loading.getTransportType(), "")); detail.setTransportType(toTransportTypeLabel(Func.toStr(loading.getTransportType(), ""))); detail.setTransportOrgType(TRANSPORT_LOAD); diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/TripAggregation.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/TripAggregation.java index 6eb4689a..2f2952eb 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/TripAggregation.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/TripAggregation.java @@ -82,21 +82,41 @@ final class TripAggregation { return switch (Func.toStr(status, "")) { case PENDING -> 0; case RUNNING -> 1; + case PENDING_RETURN_CONFIRM -> 1; case COMPLETED -> 2; case CANCELLED -> 3; default -> null; }; } + /** + * 运次的列表档位:0 待接单 / 1 进行中 / 2 已完成 / 3 已取消 / 4 待确认(待回单确认)。 + *

+ * 与 {@link #appStatus(String)} 的区别只在待回单确认:卡片状态仍按 1(运输中)展示, + * 列表档位单独成档,待回单确认不再落进行中。 + */ + static Integer tabOf(String status) { + return switch (Func.toStr(status, "")) { + case PENDING -> 0; + case RUNNING -> 1; + case COMPLETED -> 2; + case CANCELLED -> 3; + case PENDING_RETURN_CONFIRM -> 4; + default -> null; + }; + } + /** * 运次状态:终态优先——子单全为终态时有已完成取已完成、否则已取消; - * 否则需接单且未接单取待接单,其余取运输中。 + * 其次待接单档优先;再次有在跑的子单取运输中;只剩待回单确认的取待回单确认。 + *

+ * 待回单确认不受接单需求影响:需要接单的趟车走到回单确认时司机已接过单,不再回退成待接单。 *

* 子单集合为空时无从判定,返回空。 */ static String statusOf(List children, boolean requireAccept, boolean accepted) { String status = classify(count(children)); - if (status == null || TERMINAL_STATUSES.contains(status)) { + if (status == null || TERMINAL_STATUSES.contains(status) || PENDING_RETURN_CONFIRM.equals(status)) { return status; } if (!requireAccept) { @@ -106,13 +126,15 @@ final class TripAggregation { } /** - * 子单聚合结果:子单总数、终态数、已完成数、待接单档数 + * 子单聚合结果:子单总数、终态数、已完成数、待接单档数、运输中数、待回单确认数 */ static Counters count(List children) { int total = 0; int terminal = 0; int completed = 0; int pending = 0; + int running = 0; + int returnConfirm = 0; for (Waybill child : children) { String status = child == null ? null : child.getBusinessStatus(); total++; @@ -125,10 +147,20 @@ final class TripAggregation { if (status != null && PENDING_STATUSES.contains(status)) { pending++; } + if (RUNNING.equals(status)) { + running++; + } + if (PENDING_RETURN_CONFIRM.equals(status)) { + returnConfirm++; + } } - return new Counters(total, terminal, completed, pending); + return new Counters(total, terminal, completed, pending, running, returnConfirm); } + /** + * 待接单档优先,其次运输中,最后只剩待回单确认——整趟车都打完卡等回单确认才单独成档, + * 混着在跑的子单时仍算运输中。 + */ private static String classify(Counters counters) { if (counters.total() == 0) { return null; @@ -136,7 +168,16 @@ final class TripAggregation { if (counters.terminal() == counters.total()) { return counters.completed() > 0 ? COMPLETED : CANCELLED; } - return counters.pending() > 0 ? PENDING : RUNNING; + if (counters.pending() > 0) { + return PENDING; + } + if (counters.running() > 0) { + return RUNNING; + } + if (counters.returnConfirm() > 0) { + return PENDING_RETURN_CONFIRM; + } + return RUNNING; } /** @@ -169,7 +210,7 @@ final class TripAggregation { } /** 子单聚合计数 */ - record Counters(int total, int terminal, int completed, int pending) { + record Counters(int total, int terminal, int completed, int pending, int running, int returnConfirm) { } } diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/TripQueryServiceImpl.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/TripQueryServiceImpl.java index abf0fc92..56a2a5d3 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/TripQueryServiceImpl.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/TripQueryServiceImpl.java @@ -128,9 +128,10 @@ public class TripQueryServiceImpl implements ITripQueryService { if (!scoped(condition)) { return counts; } - // 一次取数算清四档:分档口径与列表、卡片同一份推导,不再按库里的业务状态分批计数 + // 一次取数算清各档:分档口径与列表、卡片同一份推导,不再按库里的业务状态分批计数 long pending = 0; long doing = 0; + long confirm = 0; long done = 0; for (TripRows trip : trips(withoutTab(condition))) { Integer tier = trip.tier(); @@ -141,14 +142,16 @@ public class TripQueryServiceImpl implements ITripQueryService { case 0 -> pending++; case 1 -> doing++; case 2 -> done++; + case 4 -> confirm++; default -> { } } } counts.setPending(pending); counts.setDoing(doing); + counts.setConfirm(confirm); counts.setDone(done); - counts.setAll(pending + doing + done); + counts.setAll(pending + doing + confirm + done); return counts; } @@ -205,7 +208,7 @@ public class TripQueryServiceImpl implements ITripQueryService { boolean requireAccept = TripAggregation.requiresAccept(children); boolean accepted = WaybillProcessSupport.ACCEPT_ACCEPTED.equals(TripAggregation.acceptStatus(children)); String status = TripAggregation.statusOf(children, requireAccept, accepted); - Integer tier = TripAggregation.appStatus(status); + Integer tier = TripAggregation.tabOf(status); if (tier == null || !tiers.contains(tier)) { return; } @@ -361,7 +364,7 @@ public class TripQueryServiceImpl implements ITripQueryService { } /** - * 要哪几档:多档优先,其次单档,都不传按「全部」=待接单/进行中/已完成(不含已取消) + * 要哪几档:多档优先,其次单档,都不传按「全部」=待接单/进行中/待确认/已完成(不含已取消) */ private Set tiers(TripQueryVO condition) { if (Func.isNotEmpty(condition.getStatuses())) { @@ -370,7 +373,7 @@ public class TripQueryServiceImpl implements ITripQueryService { if (condition.getStatus() != null) { return Set.of(condition.getStatus()); } - return Set.of(0, 1, 2); + return Set.of(0, 1, 2, 4); } /** diff --git a/blade-service/blade-transport/src/test/java/org/springblade/transport/service/impl/TripAggregationTest.java b/blade-service/blade-transport/src/test/java/org/springblade/transport/service/impl/TripAggregationTest.java index 1bee8fd5..3fafa0fe 100644 --- a/blade-service/blade-transport/src/test/java/org/springblade/transport/service/impl/TripAggregationTest.java +++ b/blade-service/blade-transport/src/test/java/org/springblade/transport/service/impl/TripAggregationTest.java @@ -140,7 +140,7 @@ class TripAggregationTest { } @Test - @DisplayName("四档都由同一份推导给出:待接单 / 进行中 / 已完成 / 已取消") + @DisplayName("五档都由同一份推导给出:待接单 / 进行中 / 待确认 / 已完成 / 已取消") void tiersComeFromTheSameDerivation() { // 需接单且未接单 → 待接单档 assertEquals(0, tier(List.of(waybill("pending", "pending", ACCEPT_YES)))); @@ -150,6 +150,9 @@ class TripAggregationTest { assertEquals(2, tier(List.of(waybill("completed", "accepted", ACCEPT_YES)))); // 子单全取消 → 已取消档 assertEquals(3, tier(List.of(waybill("cancelled", null, ACCEPT_YES)))); + // 待回单确认独立成档,即便项目要接单也不回落到待接单 + assertEquals(4, tier(List.of(waybill( + org.springblade.transport.support.WaybillProcessSupport.STATUS_PENDING_RETURN_CONFIRM, "pending", ACCEPT_YES)))); // 子单无从判定(空集合)时不落任何档,不会混进「全部」 assertNull(tier(List.of())); } @@ -182,7 +185,7 @@ class TripAggregationTest { boolean requireAccept = TripAggregation.requiresAccept(children); boolean accepted = org.springblade.transport.support.WaybillProcessSupport.ACCEPT_ACCEPTED .equals(TripAggregation.acceptStatus(children)); - return TripAggregation.appStatus(TripAggregation.statusOf(children, requireAccept, accepted)); + return TripAggregation.tabOf(TripAggregation.statusOf(children, requireAccept, accepted)); } @Test @@ -194,20 +197,26 @@ class TripAggregationTest { } @Test - @DisplayName("待回单确认的子单仍进运次,且不会把运次判成已完成") + @DisplayName("待回单确认的子单仍进运次,独占一档且不会被判成已完成") void pendingReturnConfirmChildKeepsTripRunning() { String pendingReturnConfirm = org.springblade.transport.support.WaybillProcessSupport.STATUS_PENDING_RETURN_CONFIRM; assertTrue(TripAggregation.CHILD_STATUSES.contains(pendingReturnConfirm), "漏掉这个状态,子单会从运次读服务消失,配载单被当成全部完成"); - // 与一张已完成的子单拼载:还有子单没走完,运次只能是运输中 + // 与一张已完成的子单拼载:还有子单没走完,运次只能是待回单确认 List children = List.of( waybill("completed", null, null), waybill(pendingReturnConfirm, null, null)); String status = TripAggregation.statusOf(children, false, false); - assertEquals("running", status); - assertEquals(1, TripAggregation.appStatus(status), "运次不进已完成档"); + assertEquals(pendingReturnConfirm, status); + assertEquals(4, TripAggregation.tabOf(status), "待回单确认单独一档"); + assertEquals(1, TripAggregation.appStatus(status), "卡片状态码仍是运输中,不进已完成档"); + + // 还挂着在途子单时仍算进行中:待确认只在整趟车没有司机待办时成立 + assertEquals("running", TripAggregation.statusOf(List.of( + waybill("running", null, null), + waybill(pendingReturnConfirm, null, null)), false, false)); } private Waybill waybill(String businessStatus, String acceptStatus, String processJson) {