diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/DriverAppServiceImpl.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/DriverAppServiceImpl.java index 9acc9d76..ee8feb27 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/DriverAppServiceImpl.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/DriverAppServiceImpl.java @@ -155,20 +155,27 @@ public class DriverAppServiceImpl implements IDriverAppService { } /** - * 日期字段的展示值:长期有效显示「长期」,没填显示空串,其余显示日期 + * 日期字段的下发值:只下发 {@code yyyy-MM-dd} 或空串,**从不下发「长期」这类文字** *

- * 卡片是「重新认证」表单的回填来源,空值必须是空串而不是「null」这类字符串, - * 否则用户会在日期框里看到一个字面量 null。 + * 卡片是「重新认证」表单的回填来源,值会直接进日期框,所以两条约束必须守住: + *

* * @param date 日期 * @param longTerm 长期有效标志:1 表示长期 - * @return 展示文本 + * @return {@code yyyy-MM-dd} 或空串 */ private String dateText(LocalDate date, Integer longTerm) { - if (date != null) { - return date.toString(); + if (Integer.valueOf(1).equals(longTerm)) { + return ""; } - return Integer.valueOf(1).equals(longTerm) ? "长期" : ""; + return date == null ? "" : date.toString(); } /** diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/DriverCertServiceImpl.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/DriverCertServiceImpl.java index 81699684..d02d556e 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/DriverCertServiceImpl.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/DriverCertServiceImpl.java @@ -161,23 +161,27 @@ public class DriverCertServiceImpl implements IDriverCertService { } /** - * 有效期校验:未勾「长期有效」时起止必填,且止不得早于起 + * 有效期校验:未勾「长期有效」时**止期**必填;勾了长期有效则止期也不要求 + *

+ * 起期不再是必填:司机端表单不采集它(管理端车辆表单同样只填止期), + * 存量车辆档案里起期列全是空的,强制必填会让每一次重新认证都卡在提交前。 + * 起止先后只在**两端都有值**时才比较——只有止期时没有可比的对象。 * - * @param start 有效期起 + * @param start 有效期起,可为空 * @param end 有效期止 * @param longTerm 长期有效标志:1 表示长期 - * @param emptyText 缺值提示 - * @param rangeText 区间颠倒提示 + * @param emptyText 缺止期时的提示 + * @param rangeText 区间颠倒时的提示 */ private void requireDateRange(LocalDate start, LocalDate end, Integer longTerm, String emptyText, String rangeText) { if (Integer.valueOf(1).equals(longTerm)) { return; } - if (start == null || end == null) { + if (end == null) { throw new ServiceException(emptyText); } - if (end.isBefore(start)) { + if (start != null && end.isBefore(start)) { throw new ServiceException(rangeText); } } diff --git a/blade-service/blade-transport/src/test/java/org/springblade/transport/service/impl/DriverAppServiceImplTest.java b/blade-service/blade-transport/src/test/java/org/springblade/transport/service/impl/DriverAppServiceImplTest.java new file mode 100644 index 00000000..0894e214 --- /dev/null +++ b/blade-service/blade-transport/src/test/java/org/springblade/transport/service/impl/DriverAppServiceImplTest.java @@ -0,0 +1,125 @@ +package org.springblade.transport.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; +import org.springblade.core.secure.utils.AuthUtil; +import org.springblade.transport.pojo.entity.Driver; +import org.springblade.transport.pojo.entity.TransportVehicle; +import org.springblade.transport.pojo.vo.DriverVehicleCardVO; +import org.springblade.transport.service.IDriverService; +import org.springblade.transport.service.ITransportVehicleService; +import org.springblade.system.feign.IUserClient; + +import java.time.LocalDate; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.when; + +/** + * 司机端车辆卡片(重新认证表单的数据来源)测试。 + *

+ * 卡片字段会**直接进表单**,所以这里盯的不是「显示得好不好看」,而是两条会让回显出错的契约: + * 日期字段只能是 {@code yyyy-MM-dd} 或空串(不能是「长期」这类文字); + * 标了长期有效的字段不能再下发一个作废的止期。 + */ +class DriverAppServiceImplTest { + + private static final String MOBILE = "15577122721"; + private static final Long USER_ID = 7001L; + + private IDriverService driverService; + private ITransportVehicleService transportVehicleService; + private DriverAppServiceImpl service; + + @BeforeEach + void setUp() { + driverService = mock(IDriverService.class); + transportVehicleService = mock(ITransportVehicleService.class); + service = new DriverAppServiceImpl(driverService, transportVehicleService, mock(IUserClient.class)); + } + + @Test + @DisplayName("长期有效且没有止期时下发空串,不能下发「长期」——否则它会被当日期填进日期框") + void longTermWithoutEndDateSendsEmptyText() { + DriverVehicleCardVO card = firstCard(vehicle(LocalDate.of(2022, 8, 17), null, 1)); + + assertEquals("", card.getDrivingLicenseEndDate(), "长期有效时日期字段只能是空串"); + assertEquals(1, card.getDrivingLicenseLongTerm(), "长期有效标志本身要照常下发"); + assertEquals("2022-08-17", card.getDrivingLicenseStartDate(), "起期有值时照常下发"); + } + + @Test + @DisplayName("长期有效时即使库里还留着止期也不下发:那个止期是上一版证件作废的值") + void longTermWithStaleEndDateSendsEmptyText() { + DriverVehicleCardVO card = firstCard(vehicle(null, LocalDate.of(2040, 8, 14), 1)); + + assertEquals("", card.getDrivingLicenseEndDate()); + assertEquals("", card.getRoadTransportCertEndDate()); + assertEquals("", card.getCompulsoryScrapDate()); + } + + @Test + @DisplayName("未标长期有效时照常下发止期,空值一律是空串而不是 null 字面量") + void normalVehicleSendsRealDates() { + TransportVehicle vehicle = vehicle(null, LocalDate.of(2030, 8, 14), 0); + vehicle.setCompulsoryScrapDate(LocalDate.of(2035, 1, 1)); + vehicle.setRoadTransportCertEndDate(null); + vehicle.setRoadTransportCertLongTerm(0); + vehicle.setRoadTransportCertStartDate(LocalDate.of(2028, 12, 4)); + DriverVehicleCardVO card = firstCard(vehicle); + + assertEquals("2030-08-14", card.getDrivingLicenseEndDate()); + assertEquals("2035-01-01", card.getCompulsoryScrapDate()); + assertEquals("", card.getRoadTransportCertEndDate(), "没填的止期是空串"); + assertEquals("2028-12-04", card.getRoadTransportCertStartDate()); + } + + // ---------------------------------------------------------------- 辅助 + + /** 造一辆只关心有效期三个字段的车 */ + private TransportVehicle vehicle(LocalDate licenseStart, LocalDate licenseEnd, Integer licenseLongTerm) { + TransportVehicle vehicle = new TransportVehicle(); + vehicle.setId(8001L); + vehicle.setPlateNo("桂AAW3255"); + vehicle.setDrivingLicenseStartDate(licenseStart); + vehicle.setDrivingLicenseEndDate(licenseEnd); + vehicle.setDrivingLicenseLongTerm(licenseLongTerm); + // 三组有效期都按同一个长期有效标志造,便于一次断言三处 + vehicle.setCompulsoryScrapDate(null); + vehicle.setCompulsoryScrapLongTerm(licenseLongTerm); + vehicle.setRoadTransportCertStartDate(LocalDate.of(2028, 12, 3)); + vehicle.setRoadTransportCertEndDate(LocalDate.of(2043, 8, 18)); + vehicle.setRoadTransportCertLongTerm(licenseLongTerm); + return vehicle; + } + + /** 按当前登录司机的手机号跑一次「我的车辆」,取第一张卡片 */ + private DriverVehicleCardVO firstCard(TransportVehicle vehicle) { + Driver driver = new Driver(); + driver.setId(9001L); + driver.setUserId(USER_ID); + driver.setDriverName("韦司机"); + driver.setMobile(MOBILE); + driver.setDrivingVehicle(vehicle.getPlateNo()); + + when(driverService.getOne(any(LambdaQueryWrapper.class))).thenReturn(driver); + when(transportVehicleService.list(any(LambdaQueryWrapper.class))).thenReturn(List.of(vehicle)); + + try (MockedStatic authUtil = mockStatic(AuthUtil.class)) { + authUtil.when(AuthUtil::getUserId).thenReturn(USER_ID); + // 账号本身就是手机号,resolvePhone 走这条短路分支,不再回调用户中心 + authUtil.when(AuthUtil::getUserAccount).thenReturn(MOBILE); + List cards = service.myVehicles(); + assertEquals(1, cards.size(), "应当只返回绑定到本人的这一辆车"); + return cards.get(0); + } + } + +} diff --git a/blade-service/blade-transport/src/test/java/org/springblade/transport/service/impl/DriverCertServiceImplTest.java b/blade-service/blade-transport/src/test/java/org/springblade/transport/service/impl/DriverCertServiceImplTest.java index ea3dc404..61afbac0 100644 --- a/blade-service/blade-transport/src/test/java/org/springblade/transport/service/impl/DriverCertServiceImplTest.java +++ b/blade-service/blade-transport/src/test/java/org/springblade/transport/service/impl/DriverCertServiceImplTest.java @@ -519,6 +519,65 @@ class DriverCertServiceImplTest { assertTrue(exception.getMessage().contains("所属组织")); } + @Test + @DisplayName("有效期只填止期:起期为空也允许提交(司机端不采集起期)") + void submitVehicleAcceptsBlankStartDate() { + Driver driver = driver(null); + when(driverAppService.currentDriverEntity()).thenReturn(driver); + when(transportVehicleService.list(any(LambdaQueryWrapper.class))).thenReturn(List.of()); + when(certRecordService.selectPendingRecord(any(), anyLong())).thenReturn(null); + + VehicleCertSubmitRequest request = completeVehicleRequest(PLATE); + request.setDrivingLicenseStartDate(null); + request.setRoadTransportCertStartDate(null); + + service.submitVehicleCert(request); + + ArgumentCaptor vehicleCaptor = ArgumentCaptor.forClass(TransportVehicle.class); + verify(transportVehicleService).save(vehicleCaptor.capture()); + assertNotNull(vehicleCaptor.getValue().getDrivingLicenseEndDate(), "止期照常写入车辆行"); + assertNull(vehicleCaptor.getValue().getDrivingLicenseStartDate(), "起期为空就是空,不该编一个值出来"); + } + + @Test + @DisplayName("有效期未勾长期有效时止期必填:起期可以空,止期不能") + void submitVehicleRejectsBlankEndDate() { + Driver driver = driver(null); + when(driverAppService.currentDriverEntity()).thenReturn(driver); + when(transportVehicleService.list(any(LambdaQueryWrapper.class))).thenReturn(List.of()); + when(certRecordService.selectPendingRecord(any(), anyLong())).thenReturn(null); + + VehicleCertSubmitRequest request = completeVehicleRequest(PLATE); + request.setDrivingLicenseStartDate(null); + request.setDrivingLicenseEndDate(null); + + ServiceException exception = assertThrows(ServiceException.class, + () -> service.submitVehicleCert(request)); + assertEquals("请选择行驶证有效期", exception.getMessage()); + verify(transportVehicleService, never()).save(any(TransportVehicle.class)); + } + + @Test + @DisplayName("长期有效时止期可以为空,此时不再比较起止先后") + void submitVehicleAllowsLongTermWithoutEndDate() { + Driver driver = driver(null); + when(driverAppService.currentDriverEntity()).thenReturn(driver); + when(transportVehicleService.list(any(LambdaQueryWrapper.class))).thenReturn(List.of()); + when(certRecordService.selectPendingRecord(any(), anyLong())).thenReturn(null); + + VehicleCertSubmitRequest request = completeVehicleRequest(PLATE); + request.setDrivingLicenseStartDate(LocalDate.of(2020, 1, 1)); + request.setDrivingLicenseEndDate(null); + request.setDrivingLicenseLongTerm(1); + + service.submitVehicleCert(request); + + ArgumentCaptor captor = ArgumentCaptor.forClass(TransportCertRecord.class); + verify(certRecordService).save(captor.capture()); + assertTrue(captor.getValue().getSnapshotJson().contains("\"drivingLicenseLongTerm\":1"), + "长期有效标志要落进快照"); + } + /** * 一份字段齐全的车辆认证提交内容 *