From 01b4dd0b5bdd8f7aa3bac3fe97bf8f5c36030737 Mon Sep 17 00:00:00 2001
From: weicw <594098497@qq.com>
Date: Tue, 27 Oct 2026 21:07:07 +0800
Subject: [PATCH] =?UTF-8?q?:recycle:=20=E8=BF=90=E6=AC=A1=E6=94=B6?=
=?UTF-8?q?=E5=8F=A3=E5=88=A4=E6=8D=AE=E7=94=B1=E5=8F=B8=E6=9C=BA=E6=89=8B?=
=?UTF-8?q?=E6=9C=BA=E5=8F=B7=E6=94=B9=E4=B8=BA=E5=8F=B8=E6=9C=BA=E6=A1=A3?=
=?UTF-8?q?=E6=A1=88=20ID?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- TripQueryVO 的收口字段由 driverPhone 换成 driverId,删除按手机号构造收口条件的入口,
让编译器暴露全部调用点;forAdmin() 与出参 driverPhone 快照不变
- 运次可见范围两段同时切:已配载段按配载单 lm.driver_id 子查询,未配载段按运单 driver_id
- 详情复核改为比较档案 ID(ownedBy)、删除 samePhone;收口缺省(空/0/负数)仍返回空集
- 司机端六个运次入口改用当前司机档案 ID;删除 currentUserPhone/isMobileLike 手机号兜底
- 测试夹具改为同时铺设 driverId 与派单快照号,新增改号仍可见、同号换人、管理端、缺省收口四组用例
---
.../transport/pojo/vo/TripQueryVO.java | 13 +-
.../impl/DriverWaybillServiceImpl.java | 58 ++---
.../service/impl/TripQueryServiceImpl.java | 40 ++--
.../impl/DriverTripAcceptRejectTest.java | 4 +-
.../service/impl/DriverTripCardTest.java | 7 +-
.../impl/TripQueryServiceImplTest.java | 204 ++++++++++++++----
.../impl/TripReadServiceBoundaryTest.java | 15 +-
7 files changed, 220 insertions(+), 121 deletions(-)
diff --git a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/TripQueryVO.java b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/TripQueryVO.java
index 750dd6d0..cbdc9f69 100644
--- a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/TripQueryVO.java
+++ b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/TripQueryVO.java
@@ -34,8 +34,11 @@ import java.util.Set;
/**
* 运次查询条件
*
- * 司机端用 {@link #forDriver(String)} 按司机手机号收口;管理端用 {@link #forAdmin()} 不按司机收口。
+ * 司机端用 {@link #forDriver(Long)} 按司机档案 ID 收口;管理端用 {@link #forAdmin()} 不按司机收口。
* 两者都不给时收口为空集——收口一旦缺省就等于把别人的运次发出去,故缺省不放行。
+ *
+ * 判据是司机档案 ID 而不是手机号:手机号在配载单/运单上只是派单时的快照,司机换号后并不跟随,
+ * 按号收口会让改过号的司机看不到自己手上的运次,号码被回收给别人时还会反向串号。
*/
@Data
@Schema(description = "运次查询条件")
@@ -47,8 +50,8 @@ public class TripQueryVO implements Serializable {
@Schema(description = "运次主键(详情按它取单个运次)")
private String tripId;
- @Schema(description = "司机手机号")
- private String driverPhone;
+ @Schema(description = "司机档案 ID(司机端收口判据,空或非正数一律不放行)")
+ private Long driverId;
@Schema(description = "是否不按司机收口(管理端)")
private boolean allDrivers;
@@ -92,9 +95,9 @@ public class TripQueryVO implements Serializable {
@Schema(description = "每页条数(按运次,不是运单)")
private Integer size;
- public static TripQueryVO forDriver(String driverPhone) {
+ public static TripQueryVO forDriver(Long driverId) {
TripQueryVO query = new TripQueryVO();
- query.setDriverPhone(driverPhone);
+ query.setDriverId(driverId);
return query;
}
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 9bd09ed8..8aab3912 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
@@ -33,10 +33,8 @@ import org.springblade.core.secure.utils.AuthUtil;
import org.springblade.core.tool.jackson.JsonUtil;
import org.springblade.core.tool.utils.DateUtil;
import org.springblade.core.tool.constant.BladeConstant;
-import org.springblade.core.tool.api.R;
import org.springblade.core.tool.utils.Func;
import org.springblade.system.feign.IMpUserClient;
-import org.springblade.system.pojo.entity.MpUser;
import org.springblade.transport.mapper.ExceptionDisposalMapper;
import org.springblade.transport.mapper.WaybillEnroutePunchMapper;
import org.springblade.transport.mapper.WaybillNodePunchMapper;
@@ -277,11 +275,11 @@ public class DriverWaybillServiceImpl implements IDriverWaybillService {
@Override
public DriverWaybillCardVO tripCurrentTask() {
- String phone = currentUserPhone();
- if (Func.isEmpty(phone)) {
+ Driver driver = currentDriver();
+ if (driver == null) {
return null;
}
- TripQueryVO query = TripQueryVO.forDriver(phone);
+ TripQueryVO query = TripQueryVO.forDriver(driver.getId());
// 待确认(待回单确认)的趟车司机仍需回到卡片上继续操作,与进行中并列作为当前任务
query.setStatuses(List.of(TRIP_STATUS_RUNNING, TRIP_STATUS_PENDING_RETURN_CONFIRM));
query.setCurrent(1);
@@ -293,11 +291,11 @@ public class DriverWaybillServiceImpl implements IDriverWaybillService {
@Override
public DriverWaybillPreviewVO tripPendingPreview(Integer size) {
DriverWaybillPreviewVO preview = new DriverWaybillPreviewVO();
- String phone = currentUserPhone();
- if (Func.isEmpty(phone)) {
+ Driver driver = currentDriver();
+ if (driver == null) {
return preview;
}
- TripQueryVO query = TripQueryVO.forDriver(phone);
+ TripQueryVO query = TripQueryVO.forDriver(driver.getId());
query.setStatus(TRIP_STATUS_PENDING);
query.setCurrent(1);
query.setSize(normalizePreviewSize(size));
@@ -312,11 +310,11 @@ public class DriverWaybillServiceImpl implements IDriverWaybillService {
@Override
public DriverWaybillTabCountsVO tripTabCounts() {
DriverWaybillTabCountsVO counts = new DriverWaybillTabCountsVO();
- String phone = currentUserPhone();
- if (Func.isEmpty(phone)) {
+ Driver driver = currentDriver();
+ if (driver == null) {
return counts;
}
- TripTabCountsVO tripCounts = tripQueryService.tabCounts(TripQueryVO.forDriver(phone));
+ TripTabCountsVO tripCounts = tripQueryService.tabCounts(TripQueryVO.forDriver(driver.getId()));
counts.setPending(tripCounts.getPending());
counts.setDoing(tripCounts.getDoing());
counts.setConfirm(tripCounts.getConfirm());
@@ -327,11 +325,11 @@ public class DriverWaybillServiceImpl implements IDriverWaybillService {
@Override
public IPage tripPage(Integer current, Integer size, Integer status, String keyword) {
- String phone = currentUserPhone();
- if (Func.isEmpty(phone)) {
+ Driver driver = currentDriver();
+ if (driver == null) {
return new Page<>(normalizePageNo(current), normalizePageSize(size));
}
- TripQueryVO query = TripQueryVO.forDriver(phone);
+ TripQueryVO query = TripQueryVO.forDriver(driver.getId());
query.setStatus(status);
query.setKeyword(keyword);
query.setCurrent(current);
@@ -346,8 +344,8 @@ public class DriverWaybillServiceImpl implements IDriverWaybillService {
@Override
public DriverWaybillCardVO tripDetail(String tripId) {
- requireCurrentDriver();
- TripQueryVO query = TripQueryVO.forDriver(currentUserPhone());
+ Driver driver = requireCurrentDriver();
+ TripQueryVO query = TripQueryVO.forDriver(driver.getId());
query.setTripId(tripId);
return toTripCard(tripQueryService.detail(query), true);
}
@@ -740,7 +738,7 @@ public class DriverWaybillServiceImpl implements IDriverWaybillService {
* 子单集合是司机可见的那一批:已拒单的子单不在其中,否则接单会把本人拒掉的单重新拉回来。
*/
private List tripChildren(String tripId) {
- TripQueryVO query = TripQueryVO.forDriver(currentUserPhone());
+ TripQueryVO query = TripQueryVO.forDriver(requireCurrentDriver().getId());
query.setTripId(tripId);
TripVO trip = tripQueryService.detail(query);
List children = trip.getWaybills() == null ? List.of() : trip.getWaybills();
@@ -922,28 +920,6 @@ public class DriverWaybillServiceImpl implements IDriverWaybillService {
.or().like(Waybill::getVehicleNo, kw));
}
- /**
- * 当前用户手机号:司机档案 mobile 优先,其次小程序用户账号。
- *
- * 派单时运单 driver_phone 取自司机档案 mobile,故档案有手机号就以它为准。
- */
- private String currentUserPhone() {
- Driver driver = currentDriver();
- if (driver != null && isMobileLike(driver.getMobile())) {
- return driver.getMobile().trim();
- }
- Long userId = AuthUtil.getUserId();
- if (userId == null || userId <= 0) {
- return null;
- }
- R result = mpUserClient.userById(userId);
- if (result != null && R.isSuccess(result) && result.getData() != null
- && isMobileLike(result.getData().getAccount())) {
- return result.getData().getAccount().trim();
- }
- return null;
- }
-
/**
* 当前登录司机:只按登录用户 ID 查司机档案,查不到即「无归属」。
*
@@ -960,10 +936,6 @@ public class DriverWaybillServiceImpl implements IDriverWaybillService {
.last("LIMIT 1"));
}
- private boolean isMobileLike(String value) {
- return Func.isNotEmpty(value) && value.trim().matches("^1\\d{10,14}$");
- }
-
private List splitPlates(String drivingVehicle) {
return VehiclePlateSupport.splitPlates(drivingVehicle);
}
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 fcff3fdc..7c35161c 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
@@ -389,22 +389,24 @@ public class TripQueryServiceImpl implements ITripQueryService {
}
/**
- * 运次可见范围:配载段按配载单的司机手机号收口,未配载段按运单的司机手机号收口。
+ * 运次可见范围:配载段按配载单的司机档案 ID 收口,未配载段按运单的司机档案 ID 收口。
*
- * 手机号允许带空格,与运单列表的收口判据一致。
+ * 判据是司机档案 ID 而不是手机号:司机换号后档案 ID 不变,而派单时落在配载单/运单上的手机号
+ * 是快照,按号收口会让改过号的司机看不到自己手上的运次;号码被回收给别人时又会反向串号。
+ * 两段必须同时切:只切一段,另一段就会把不属于该司机的运次放进来。
+ * 缺省收口(空、0、负数)不放行,由 {@link #scoped} 在外层挡掉。
*/
private void applyScope(LambdaQueryWrapper wrapper, TripQueryVO condition) {
if (condition.isAllDrivers()) {
return;
}
- String phone = Func.toStr(condition.getDriverPhone(), "").trim();
+ Long driverId = condition.getDriverId();
wrapper.and(scope -> scope
.apply("(IFNULL(loading_no, '') <> '' AND loading_no IN (SELECT lm.loading_no FROM blade_loading_manage lm"
- + " WHERE lm.is_deleted = 0 AND lm.driver_phone = {0}))", phone)
+ + " WHERE lm.is_deleted = 0 AND lm.driver_id = {0}))", driverId)
.or(lone -> lone
.apply("IFNULL(loading_no, '') = ''")
- .and(row -> row.eq(Waybill::getDriverPhone, phone)
- .or().apply("REPLACE(IFNULL(driver_phone, ''), ' ', '') = {0}", phone))));
+ .and(row -> row.eq(Waybill::getDriverId, driverId))));
}
/**
@@ -786,7 +788,7 @@ public class TripQueryServiceImpl implements ITripQueryService {
}
/**
- * 按运次主键取配载单:主键命中配载单即配载运次,再按司机收口
+ * 按运次主键取配载单:主键命中配载单即配载运次,再按配载单的司机档案 ID 收口
*/
private LoadingManage visibleLoading(Long id, TripQueryVO condition) {
LoadingManage loading = loadingManageService.getById(id);
@@ -796,7 +798,7 @@ public class TripQueryServiceImpl implements ITripQueryService {
if (condition.isAllDrivers()) {
return loading;
}
- return samePhone(loading.getDriverPhone(), condition.getDriverPhone()) ? loading : null;
+ return ownedBy(loading.getDriverId(), condition.getDriverId()) ? loading : null;
}
/**
@@ -813,19 +815,23 @@ public class TripQueryServiceImpl implements ITripQueryService {
if (condition.isAllDrivers()) {
return waybill;
}
- return samePhone(waybill.getDriverPhone(), condition.getDriverPhone()) ? waybill : null;
+ return ownedBy(waybill.getDriverId(), condition.getDriverId()) ? waybill : null;
}
+ /** 收口条件是否成立:管理端不收口;司机端必须给出有效的档案 ID,缺省一律拒绝 */
private boolean scoped(TripQueryVO condition) {
- return condition.isAllDrivers() || Func.isNotEmpty(Func.toStr(condition.getDriverPhone(), "").trim());
+ Long driverId = condition.getDriverId();
+ return condition.isAllDrivers() || (driverId != null && driverId > 0);
}
- private boolean samePhone(String left, String right) {
- String expected = Func.toStr(right, "").trim().replace(" ", "");
- if (Func.isEmpty(expected)) {
- return false;
- }
- return expected.equals(Func.toStr(left, "").trim().replace(" ", ""));
+ /**
+ * 归属判据:行的司机档案 ID 与收口条件相等。
+ *
+ * 条件侧缺省(空、0、负数)一律不可见——没有有效档案 ID 就等于没有收口条件,
+ * 此时任何行都不能算作「本人」,否则业务主键未回填的历史数据会把运次漏给所有人。
+ */
+ private boolean ownedBy(Long ownerId, Long expectedId) {
+ return expectedId != null && expectedId > 0 && expectedId.equals(ownerId);
}
private TripQueryVO condition(TripQueryVO query) {
@@ -835,7 +841,7 @@ public class TripQueryServiceImpl implements ITripQueryService {
/** 去掉 Tab 条件、其余照搬:统计四档时不按某一档收窄 */
private TripQueryVO withoutTab(TripQueryVO source) {
TripQueryVO copy = new TripQueryVO();
- copy.setDriverPhone(source.getDriverPhone());
+ copy.setDriverId(source.getDriverId());
copy.setAllDrivers(source.isAllDrivers());
copy.setDeptIds(source.getDeptIds());
copy.setProjectIds(source.getProjectIds());
diff --git a/blade-service/blade-transport/src/test/java/org/springblade/transport/service/impl/DriverTripAcceptRejectTest.java b/blade-service/blade-transport/src/test/java/org/springblade/transport/service/impl/DriverTripAcceptRejectTest.java
index 4b18c922..8e8e474d 100644
--- a/blade-service/blade-transport/src/test/java/org/springblade/transport/service/impl/DriverTripAcceptRejectTest.java
+++ b/blade-service/blade-transport/src/test/java/org/springblade/transport/service/impl/DriverTripAcceptRejectTest.java
@@ -124,8 +124,8 @@ class DriverTripAcceptRejectTest {
"两张子运单与接单司机一起下发:" + capturedUpdateParams());
assertTrue(capturedWhereSql().contains("(id IN ("), capturedWhereSql());
assertEquals(TRIP_ID, capturedQuery().getTripId());
- assertEquals(CURRENT_PHONE, capturedQuery().getDriverPhone(),
- "运次侧本票不动:仍按档案当前手机号收口(票 04 统一改成档案 ID)");
+ assertEquals(DRIVER_ID, capturedQuery().getDriverId(),
+ "运次查询按司机档案 ID 收口:手机号换了也认得出本人");
}
@Test
diff --git a/blade-service/blade-transport/src/test/java/org/springblade/transport/service/impl/DriverTripCardTest.java b/blade-service/blade-transport/src/test/java/org/springblade/transport/service/impl/DriverTripCardTest.java
index 8ac29d0f..d783008e 100644
--- a/blade-service/blade-transport/src/test/java/org/springblade/transport/service/impl/DriverTripCardTest.java
+++ b/blade-service/blade-transport/src/test/java/org/springblade/transport/service/impl/DriverTripCardTest.java
@@ -125,7 +125,7 @@ class DriverTripCardTest {
IPage page = runAsDriver(() -> service.tripPage(1, 10, null, null));
given = capturedQuery();
- assertEquals(PHONE, given.getDriverPhone());
+ assertEquals(DRIVER_ID, given.getDriverId(), "运次列表按司机档案 ID 收口");
assertEquals(1, given.getCurrent());
assertEquals(10, given.getSize(), "一页 10 条即 10 个运次");
assertNull(given.getStatus());
@@ -334,10 +334,9 @@ class DriverTripCardTest {
}
@Test
- @DisplayName("收口缺失时不放行:空页、零统计、当前任务为空,且不查运次")
- void missingPhoneIsRefused() {
+ @DisplayName("收口缺失时不放行:没有司机档案就空页、零统计、当前任务为空,且不查运次")
+ void missingDriverProfileIsRefused() {
when(driverService.getOne(any())).thenReturn(null);
- when(mpUserClient.userById(any())).thenReturn(null);
IPage page = runAsDriver(() -> service.tripPage(null, null, null, null));
DriverWaybillPreviewVO preview = runAsDriver(() -> service.tripPendingPreview(null));
diff --git a/blade-service/blade-transport/src/test/java/org/springblade/transport/service/impl/TripQueryServiceImplTest.java b/blade-service/blade-transport/src/test/java/org/springblade/transport/service/impl/TripQueryServiceImplTest.java
index d717921a..b12f1624 100644
--- a/blade-service/blade-transport/src/test/java/org/springblade/transport/service/impl/TripQueryServiceImplTest.java
+++ b/blade-service/blade-transport/src/test/java/org/springblade/transport/service/impl/TripQueryServiceImplTest.java
@@ -50,7 +50,10 @@ import static org.mockito.Mockito.when;
*/
class TripQueryServiceImplTest {
+ /** 派单时的司机手机号快照:只用于展示,归属判据是下面的档案 ID */
private static final String PHONE = "17587421208";
+ private static final Long DRIVER_ID = 9001L;
+ private static final Long OTHER_DRIVER_ID = 9002L;
private static final String LOADING_NO = "PZ-20261017-001";
private static final Long LOADING_ID = 2111350784070275123L;
private static final Long WAYBILL_ID = 2110690671695380513L;
@@ -85,7 +88,7 @@ class TripQueryServiceImplTest {
@Test
@DisplayName("段一在库层只做收口与行级筛选,分组与分档不在 SQL 里")
void rowsAreFetchedWithoutDatabaseGrouping() {
- TripQueryVO query = TripQueryVO.forDriver(PHONE);
+ TripQueryVO query = TripQueryVO.forDriver(DRIVER_ID);
query.setStatus(1);
query.setCurrent(1);
query.setSize(10);
@@ -98,9 +101,9 @@ class TripQueryServiceImplTest {
assertFalse(sql.contains("LIMIT"), "分页在服务层,不在 wrapper 里拼:" + sql);
assertFalse(sql.contains("MAX(create_time)"), "排序键在服务层算:" + sql);
assertTrue(sql.contains("blade_loading_manage"), "配载段按配载单的司机收口:" + sql);
- assertTrue(sql.contains("lm.driver_phone = ?"), "司机手机号要作为参数下发:" + sql);
- assertTrue(sql.contains("REPLACE(IFNULL(driver_phone, ''), ' ', '') = ?"),
- "未配载段按运单司机收口,且容忍手机号带空格:" + sql);
+ assertTrue(sql.contains("lm.driver_id = ?"), "配载段按配载单的司机档案 ID 收口:" + sql);
+ assertTrue(sql.contains("driver_id = ?"), "未配载段按运单的司机档案 ID 收口:" + sql);
+ assertFalse(sql.contains("driver_phone"), "手机号不再是归属判据:" + sql);
assertTrue(sql.contains("business_status IN"), "草稿子单不成运次:" + sql);
assertTrue(sql.contains("driver_accept_status"), "被该司机拒单的子单不进运次:" + sql);
}
@@ -108,7 +111,7 @@ class TripQueryServiceImplTest {
@Test
@DisplayName("段一只 select 用得上的列,大 JSON 列不拖回来")
void onlyNeededColumnsAreSelected() {
- service.page(TripQueryVO.forDriver(PHONE));
+ service.page(TripQueryVO.forDriver(DRIVER_ID));
String select = String.valueOf(capturedRowQuery().getSqlSelect());
assertTrue(select.contains("process_json"), "分档要按过程配置推导,必须取:" + select);
@@ -124,7 +127,7 @@ class TripQueryServiceImplTest {
@Test
@DisplayName("组织收口给了就落在库层运单行上,分档与分页都由缩过的行集合算")
void deptScopeIsAppliedOnRows() {
- TripQueryVO query = TripQueryVO.forDriver(PHONE);
+ TripQueryVO query = TripQueryVO.forDriver(DRIVER_ID);
query.setDeptIds(List.of(1123598813738675202L, 1123598813738675201L));
service.page(query);
@@ -134,7 +137,7 @@ class TripQueryServiceImplTest {
Set