fix(appointments): expose dispatch cabinet short numbers
This commit is contained in:
+3
-3
@@ -7,7 +7,7 @@ import org.springframework.stereotype.Repository;
|
||||
/** 日历只读访问保留真实主单范围,整批读取原货物快照而不逐车查询。 */
|
||||
@Repository
|
||||
public class FleetTripCalendarRepository {
|
||||
public record Source(String dispatchNo, String sourceSnapshotJson) { }
|
||||
public record Source(String dispatchNo, String sourceSnapshotJson, String loadingPayloadJson) { }
|
||||
private final FleetTripMapper mapper;
|
||||
|
||||
/** 装配现有车次Mapper,日历服务不直接访问SQL。 */
|
||||
@@ -18,10 +18,10 @@ public class FleetTripCalendarRepository {
|
||||
return mapper.calendar(from, to);
|
||||
}
|
||||
|
||||
/** 一次读取指定主单的全部货物快照,空窗口不发送空IN查询。 */
|
||||
/** 一次读取指定主单的全部货物与原装柜快照,空窗口不发送空IN查询。 */
|
||||
public List<Source> sourceSnapshots(List<String> dispatchNumbers) {
|
||||
if (dispatchNumbers.isEmpty()) return List.of();
|
||||
return mapper.calendarSources(dispatchNumbers).stream()
|
||||
.map(row -> new Source(row.dispatchNo(), row.sourceSnapshotJson())).toList();
|
||||
.map(row -> new Source(row.dispatchNo(), row.sourceSnapshotJson(), row.loadingPayloadJson())).toList();
|
||||
}
|
||||
}
|
||||
|
||||
+34
-18
@@ -19,8 +19,9 @@ public class FleetTripCalendarService {
|
||||
String originWarehouseCode, String originWarehouseName, String originCountryCode,
|
||||
String originTimezone, String originWarehouseStatus, Instant plannedDepartureAt,
|
||||
Instant expectedDriverEndAt, Instant departedAt, Instant completedAt,
|
||||
List<FleetTripModels.Stop> stops, List<String> containerNumbers, List<String> incompleteReasons) { }
|
||||
List<FleetTripModels.Stop> stops, List<String> containerNumbers, List<String> cabinetNumbers, List<String> incompleteReasons) { }
|
||||
public record Result(List<Entry> records, List<Entry> unresolvedRecords) { }
|
||||
private record ContainerReferences(List<String> containerNumbers, List<String> cabinetNumbers) { }
|
||||
private static final Set<String> COUNTRIES = Set.of(Locale.getISOCountries());
|
||||
private final FleetTripCalendarRepository trips;
|
||||
private final FleetWarehouseClient warehouses;
|
||||
@@ -41,7 +42,7 @@ public class FleetTripCalendarService {
|
||||
var rows=trips.calendar(LocalDateTime.ofInstant(from,ZoneOffset.UTC),LocalDateTime.ofInstant(to,ZoneOffset.UTC));
|
||||
if (rows.size()>2000) throw new ResponseStatusException(HttpStatus.BAD_REQUEST,"排车过多,请缩小查询日期范围");
|
||||
if (rows.isEmpty()) return new Result(List.of(),List.of());
|
||||
var containers=containerNumbers(rows);
|
||||
var containers=containerReferences(rows);
|
||||
var ids=rows.stream().map(FleetTripEntity::getWarehouseId).filter(Objects::nonNull).filter(id->id>0).distinct().sorted().toList();
|
||||
var metadata=metadata(ids);
|
||||
var records=new ArrayList<Entry>(); var unresolved=new ArrayList<Entry>();
|
||||
@@ -64,35 +65,50 @@ public class FleetTripCalendarService {
|
||||
throw new IllegalArgumentException("incomplete route");
|
||||
stops=List.copyOf(stops);
|
||||
} catch (Exception invalid) { stops=List.of(); reasons.add("ROUTE_INCOMPLETE"); }
|
||||
var references=containers.getOrDefault(row.getDispatchNo(),new ContainerReferences(List.of(),List.of()));
|
||||
var entry=new Entry("DISPATCH",row.getDispatchNo(),row.getStatus(),row.getWarehouseId()==null?null:row.getWarehouseId().toString(),
|
||||
warehouse==null?row.getWarehouseCode():warehouse.warehouseCode(),warehouse==null?row.getWarehouseName():warehouse.warehouseName(),
|
||||
country,zone,warehouse==null?null:warehouse.status(),instant(row.getPlannedDepartureAt()),instant(row.getExpectedDriverEndAt()),
|
||||
instant(row.getDepartedAt()),instant(row.getCompletedAt()),stops,containers.getOrDefault(row.getDispatchNo(),List.of()),List.copyOf(reasons));
|
||||
instant(row.getDepartedAt()),instant(row.getCompletedAt()),stops,references.containerNumbers(),references.cabinetNumbers(),List.copyOf(reasons));
|
||||
(unresolvedOrigin?unresolved:records).add(entry);
|
||||
}
|
||||
return new Result(List.copyOf(records),List.copyOf(unresolved));
|
||||
}
|
||||
|
||||
/** 柜号只取排车建单时正式保存的SourceDisplay.containerNo,不反查或推测其他来源。 */
|
||||
private Map<String,List<String>> containerNumbers(List<FleetTripCalendarRow> rows) {
|
||||
/** 完整柜号取排车货物快照,柜次短号取同客户真实来源的原装柜事件,分别去空去重。 */
|
||||
private Map<String,ContainerReferences> containerReferences(List<FleetTripCalendarRow> rows) {
|
||||
var numbers=rows.stream().map(FleetTripCalendarRow::getDispatchNo).distinct().sorted().toList();
|
||||
var requested=new HashSet<>(numbers);var grouped=new LinkedHashMap<String,LinkedHashSet<String>>();
|
||||
var requested=new HashSet<>(numbers);
|
||||
var containers=new LinkedHashMap<String,LinkedHashSet<String>>();
|
||||
var cabinets=new LinkedHashMap<String,LinkedHashSet<String>>();
|
||||
for(var source:trips.sourceSnapshots(numbers)) {
|
||||
if(source==null || !requested.contains(source.dispatchNo()) || source.sourceSnapshotJson()==null || source.sourceSnapshotJson().isBlank())
|
||||
throw sourceUnavailable();
|
||||
try {
|
||||
var snapshot=json.reader().with(com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_TRAILING_TOKENS)
|
||||
.readTree(source.sourceSnapshotJson());
|
||||
if(snapshot==null || !snapshot.isObject())throw sourceUnavailable();
|
||||
var value=snapshot.get("containerNo");
|
||||
if(value==null || value.isNull())continue;
|
||||
if(!value.isTextual())throw sourceUnavailable();
|
||||
var number=value.textValue().strip();
|
||||
if(!number.isEmpty())grouped.computeIfAbsent(source.dispatchNo(),ignored->new LinkedHashSet<>()).add(number);
|
||||
} catch(com.fasterxml.jackson.core.JsonProcessingException invalid) { throw sourceUnavailable(); }
|
||||
var container=snapshotReference(source.sourceSnapshotJson(),"containerNo");
|
||||
if(container!=null)containers.computeIfAbsent(source.dispatchNo(),ignored->new LinkedHashSet<>()).add(container);
|
||||
// 非头程来源没有装柜事件,保持短号为空,不从完整柜号或内部ID猜测。
|
||||
if(source.loadingPayloadJson()!=null) {
|
||||
var cabinet=snapshotReference(source.loadingPayloadJson(),"cabinetNo");
|
||||
if(cabinet!=null)cabinets.computeIfAbsent(source.dispatchNo(),ignored->new LinkedHashSet<>()).add(cabinet);
|
||||
}
|
||||
}
|
||||
var result=new LinkedHashMap<String,List<String>>();
|
||||
grouped.forEach((number,values)->result.put(number,List.copyOf(values)));return Map.copyOf(result);
|
||||
var result=new LinkedHashMap<String,ContainerReferences>();
|
||||
for(var number:numbers)result.put(number,new ContainerReferences(
|
||||
List.copyOf(containers.getOrDefault(number,new LinkedHashSet<>())),List.copyOf(cabinets.getOrDefault(number,new LinkedHashSet<>()))));
|
||||
return Map.copyOf(result);
|
||||
}
|
||||
|
||||
/** 只接受正式JSON对象中的文本业务标识,缺失留空,损坏资料明确报错。 */
|
||||
private String snapshotReference(String payload,String field) {
|
||||
try {
|
||||
var snapshot=json.reader().with(com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_TRAILING_TOKENS).readTree(payload);
|
||||
if(snapshot==null || !snapshot.isObject())throw sourceUnavailable();
|
||||
var value=snapshot.get(field);
|
||||
if(value==null || value.isNull())return null;
|
||||
if(!value.isTextual())throw sourceUnavailable();
|
||||
var number=value.textValue().strip();
|
||||
return number.isEmpty()?null:number;
|
||||
} catch(com.fasterxml.jackson.core.JsonProcessingException invalid) { throw sourceUnavailable(); }
|
||||
}
|
||||
|
||||
/** 元数据批次必须完整覆盖请求身份,未知地点保持显式未核实。 */
|
||||
|
||||
@@ -19,12 +19,14 @@ public interface FleetTripMapper extends BaseMapper<FleetTripEntity> {
|
||||
ORDER BY COALESCE(t.departed_at,t.planned_departure_at),d.dispatch_no LIMIT 2001
|
||||
""")
|
||||
java.util.List<FleetTripCalendarRow> calendar(@Param("from") java.time.LocalDateTime from,@Param("to") java.time.LocalDateTime to);
|
||||
record CalendarSource(String dispatchNo, String sourceSnapshotJson) { }
|
||||
/** 主查询限额之后批量读取原货物快照,不以货物JOIN放大整车记录或截断多柜。 */
|
||||
record CalendarSource(String dispatchNo, String sourceSnapshotJson, String loadingPayloadJson) { }
|
||||
/** 主查询限额后批读货物快照及同客户原装柜事件,柜次短号只沿真实订单关联读取。 */
|
||||
@Select("""
|
||||
<script>
|
||||
SELECT d.dispatch_no AS dispatchNo,i.source_snapshot_json AS sourceSnapshotJson
|
||||
SELECT d.dispatch_no AS dispatchNo,i.source_snapshot_json AS sourceSnapshotJson,e.payload_json AS loadingPayloadJson
|
||||
FROM ll_dispatch_order d JOIN ll_dispatch_item i ON i.dispatch_order_id=d.id
|
||||
LEFT JOIN ll_order o ON o.id=i.order_id AND o.customer_id=i.customer_id
|
||||
LEFT JOIN ll_loading_event_inbox e ON e.event_id=o.source_event_id AND e.customer_id=o.customer_id
|
||||
WHERE d.status='ACTIVE' AND d.dispatch_no IN
|
||||
<foreach collection='numbers' item='number' open='(' separator=',' close=')'>#{number}</foreach>
|
||||
ORDER BY d.dispatch_no,i.id
|
||||
|
||||
@@ -359,7 +359,7 @@ components:
|
||||
schemas:
|
||||
TripCalendarEntry:
|
||||
type: object
|
||||
required: [sourceType, dispatchNo, status, plannedDepartureAt, expectedDriverEndAt, stops, containerNumbers, incompleteReasons]
|
||||
required: [sourceType, dispatchNo, status, plannedDepartureAt, expectedDriverEndAt, stops, containerNumbers, cabinetNumbers, incompleteReasons]
|
||||
properties:
|
||||
sourceType: {type: string, enum: [DISPATCH]}
|
||||
dispatchNo: {type: string, description: 真实整车主单排车号,用于排车详情路由}
|
||||
@@ -380,6 +380,11 @@ components:
|
||||
description: 本车全部货物正式来源快照中的真实柜号,去空去重;多客户、多柜保留完整数组,无柜号为空数组,不推测或补造。
|
||||
items: {type: string}
|
||||
uniqueItems: true
|
||||
cabinetNumbers:
|
||||
type: array
|
||||
description: 本车货物通过同客户真实尾程订单关联的原装柜事件中的柜次短号(如D1821),去空去重;无装柜来源为空数组,不用完整柜号或内部ID推测。
|
||||
items: {type: string}
|
||||
uniqueItems: true
|
||||
incompleteReasons:
|
||||
type: array
|
||||
items: {type: string, enum: [ORIGIN_WAREHOUSE_UNKNOWN, ORIGIN_COUNTRY_UNKNOWN, ORIGIN_TIMEZONE_UNKNOWN, ROUTE_INCOMPLETE]}
|
||||
|
||||
+14
-3
@@ -62,16 +62,22 @@ class FleetTripCalendarMapperTest {
|
||||
bindDispatches(jdbc);
|
||||
assertThat(mapper.calendar(from,to)).hasSize(2001);
|
||||
}
|
||||
/** 实际Mapper批读同单跨客户多柜,排除其他主单与取消主单,不用货物行放大日历。 */
|
||||
/** 实际Mapper批读跨客户多柜及原装柜短号,隔离订单和事件客户,排除其他及取消主单。 */
|
||||
@Test void batchReadsEverySourceSnapshotOnlyForTheRequestedActiveDispatches() throws Exception {
|
||||
var ds=new DriverManagerDataSource("jdbc:h2:mem:"+UUID.randomUUID()+";MODE=MySQL;DB_CLOSE_DELAY=-1","sa","");
|
||||
var jdbc=new JdbcTemplate(ds);
|
||||
jdbc.execute("CREATE TABLE ll_dispatch_order(id BIGINT PRIMARY KEY,dispatch_no VARCHAR(80) UNIQUE,status VARCHAR(32))");
|
||||
jdbc.execute("CREATE TABLE ll_dispatch_item(id BIGINT PRIMARY KEY,dispatch_order_id BIGINT,customer_id VARCHAR(64),source_snapshot_json VARCHAR(2000))");
|
||||
jdbc.execute("CREATE TABLE ll_dispatch_item(id BIGINT PRIMARY KEY,dispatch_order_id BIGINT,customer_id VARCHAR(64),source_snapshot_json VARCHAR(2000),order_id BIGINT)");
|
||||
jdbc.execute("CREATE TABLE ll_order(id BIGINT PRIMARY KEY,customer_id VARCHAR(64),source_event_id VARCHAR(64))");
|
||||
jdbc.execute("CREATE TABLE ll_loading_event_inbox(event_id VARCHAR(64) PRIMARY KEY,customer_id VARCHAR(64),payload_json VARCHAR(2000))");
|
||||
jdbc.update("INSERT INTO ll_dispatch_order VALUES(1,'PC-A','ACTIVE'),(2,'PC-B','ACTIVE'),(3,'PC-OTHER','ACTIVE'),(4,'PC-CANCELLED','CANCELLED')");
|
||||
jdbc.update("INSERT INTO ll_dispatch_item VALUES(11,1,'101',?), (12,1,'102',?), (13,1,'101',?), (21,2,'103',?), (31,3,'101',?), (41,4,'101',?)",
|
||||
jdbc.update("INSERT INTO ll_dispatch_item(id,dispatch_order_id,customer_id,source_snapshot_json) VALUES(11,1,'101',?), (12,1,'102',?), (13,1,'101',?), (21,2,'103',?), (31,3,'101',?), (41,4,'101',?)",
|
||||
"{\"containerNo\":\"TSTU1234567\"}","{\"containerNo\":\"TSTU1234567\"}","{\"containerNo\":\"TSTU7654321\"}",
|
||||
"{\"planNo\":\"PLAN-B\"}","{\"containerNo\":\"OTHER-CARGO\"}","{\"containerNo\":\"CANCELLED-CARGO\"}");
|
||||
jdbc.update("INSERT INTO ll_order VALUES(1011,'101','E1'),(1012,'102','E2'),(1013,'101','E3'),(1021,'104','E4')");
|
||||
jdbc.update("INSERT INTO ll_loading_event_inbox VALUES('E1','101',?),('E2','102',?),('E3','102',?),('E4','104',?)",
|
||||
"{\"cabinetNo\":\"D1821\"}","{\"cabinetNo\":\"D1822\"}","{\"cabinetNo\":\"WRONG-EVENT-CUSTOMER\"}","{\"cabinetNo\":\"WRONG-ORDER-CUSTOMER\"}");
|
||||
jdbc.update("UPDATE ll_dispatch_item SET order_id=CASE id WHEN 11 THEN 1011 WHEN 12 THEN 1012 WHEN 13 THEN 1013 WHEN 21 THEN 1021 END");
|
||||
var config=new MybatisConfiguration();config.setMapUnderscoreToCamelCase(true);config.addMapper(FleetTripMapper.class);
|
||||
var factory=new MybatisSqlSessionFactoryBean();factory.setDataSource(ds);factory.setConfiguration(config);
|
||||
var mapper=new SqlSessionTemplate(factory.getObject()).getMapper(FleetTripMapper.class);
|
||||
@@ -81,5 +87,10 @@ class FleetTripCalendarMapperTest {
|
||||
assertThat(rows).extracting(FleetTripCalendarRepository.Source::dispatchNo).containsExactly("PC-A","PC-A","PC-A","PC-B");
|
||||
assertThat(rows).extracting(FleetTripCalendarRepository.Source::sourceSnapshotJson).containsExactly(
|
||||
"{\"containerNo\":\"TSTU1234567\"}","{\"containerNo\":\"TSTU1234567\"}","{\"containerNo\":\"TSTU7654321\"}","{\"planNo\":\"PLAN-B\"}");
|
||||
assertThat(rows).extracting(FleetTripCalendarRepository.Source::loadingPayloadJson).containsExactly(
|
||||
"{\"cabinetNo\":\"D1821\"}","{\"cabinetNo\":\"D1822\"}",null,null);
|
||||
jdbc.update("UPDATE ll_dispatch_item SET order_id=NULL WHERE id=11");
|
||||
assertThat(repository.sourceSnapshots(List.of("PC-A"))).hasSize(3).first()
|
||||
.extracting(FleetTripCalendarRepository.Source::loadingPayloadJson).isNull();
|
||||
}
|
||||
}
|
||||
|
||||
+26
-11
@@ -107,22 +107,26 @@ class FleetTripCalendarServiceTest {
|
||||
var platform=new com.baiyue.common.security.AuthenticatedUser(1L,0L,0L,1L,"platform","PLATFORM","WEB",Set.of("platform:overseas-warehouse:dispatch:list"));
|
||||
controller.calendar(platform,from,to);verify(calendar).calendar(from,to);
|
||||
}
|
||||
/** 同车全部来源只批读一次,跨客户重复柜号去重,多柜完整保留。 */
|
||||
/** 同车全部来源只批读一次,跨客户重复柜号及短号去重,多柜完整保留。 */
|
||||
@Test void readsAllContainerNumbersInOneBatchAndPreservesTheLocalDateFacts() {
|
||||
var first=trip("PC-A",1L);first.setPlannedDepartureAt(LocalDateTime.of(2026,10,22,16,0));
|
||||
when(trips.calendar(any(),any())).thenReturn(List.of(first,trip("PC-B",1L)));
|
||||
when(trips.sourceSnapshots(List.of("PC-A","PC-B"))).thenReturn(List.of(
|
||||
new FleetTripCalendarRepository.Source("PC-A","{\"containerNo\":\" TSTU1234567 \",\"planNo\":\"PLAN-A\"}"),
|
||||
new FleetTripCalendarRepository.Source("PC-A","{\"containerNo\":\"TSTU1234567\"}"),
|
||||
new FleetTripCalendarRepository.Source("PC-A","{\"containerNo\":\"TSTU7654321\"}"),
|
||||
new FleetTripCalendarRepository.Source("PC-A","{\"containerNo\":null}"),
|
||||
new FleetTripCalendarRepository.Source("PC-B","{}"),
|
||||
new FleetTripCalendarRepository.Source("PC-B","{\"containerNo\":\" \"}")));
|
||||
new FleetTripCalendarRepository.Source("PC-A","{\"containerNo\":\" TSTU1234567 \",\"planNo\":\"PLAN-A\"}","{\"cabinetNo\":\" D1821 \"}"),
|
||||
new FleetTripCalendarRepository.Source("PC-A","{\"containerNo\":\"TSTU1234567\"}","{\"cabinetNo\":\"D1821\"}"),
|
||||
new FleetTripCalendarRepository.Source("PC-A","{\"containerNo\":\"TSTU7654321\"}","{\"cabinetNo\":\"D1822\"}"),
|
||||
new FleetTripCalendarRepository.Source("PC-A","{\"containerNo\":null}","{\"cabinetNo\":\"D1823\"}"),
|
||||
new FleetTripCalendarRepository.Source("PC-B","{}",null),
|
||||
new FleetTripCalendarRepository.Source("PC-B","{\"containerNo\":\" \"}","{\"cabinetNo\":null}"),
|
||||
new FleetTripCalendarRepository.Source("PC-B","{}","{}"),
|
||||
new FleetTripCalendarRepository.Source("PC-B","{}","{\"cabinetNo\":\" \"}")));
|
||||
metadata(List.of(warehouse(1,"US","America/Los_Angeles")),List.of());
|
||||
var result=service.calendar(from,to);
|
||||
assertThat(result.records()).hasSize(2);
|
||||
assertThat(result.records().get(0).containerNumbers()).containsExactly("TSTU1234567","TSTU7654321");
|
||||
assertThat(result.records().get(1).containerNumbers()).isEmpty();
|
||||
assertThat(result.records().get(0).cabinetNumbers()).containsExactly("D1821","D1822","D1823");
|
||||
assertThat(result.records().get(1).cabinetNumbers()).isEmpty();
|
||||
assertThat(result.records().get(0).plannedDepartureAt()).isEqualTo(Instant.parse("2026-10-22T16:00:00Z"));
|
||||
assertThat(result.records().get(0).originTimezone()).isEqualTo("America/Los_Angeles");
|
||||
assertThat(result.unresolvedRecords()).isEmpty();
|
||||
@@ -130,15 +134,16 @@ class FleetTripCalendarServiceTest {
|
||||
verify(warehouses).platformMetadata("internal-test",new FleetWarehouseClient.MetadataRequest(List.of(1L)));
|
||||
verifyNoMoreInteractions(warehouses);
|
||||
}
|
||||
/** 缺时区的主单仍保留真实柜号,不将货物信息丢失误作起运地已核实。 */
|
||||
/** 缺时区的主单仍保留真实柜号及短号,不将货物信息丢失误作起运地已核实。 */
|
||||
@Test void keepsKnownContainersOnAnUnresolvedOrigin() {
|
||||
when(trips.calendar(any(),any())).thenReturn(List.of(trip("PC-UNKNOWN",1L)));
|
||||
when(trips.sourceSnapshots(List.of("PC-UNKNOWN"))).thenReturn(List.of(
|
||||
new FleetTripCalendarRepository.Source("PC-UNKNOWN","{\"containerNo\":\"TSTU1234567\"}")));
|
||||
new FleetTripCalendarRepository.Source("PC-UNKNOWN","{\"containerNo\":\"TSTU1234567\"}","{\"cabinetNo\":\"D1821\"}")));
|
||||
metadata(List.of(warehouse(1,"US",null)),List.of());
|
||||
var result=service.calendar(from,to);assertThat(result.records()).isEmpty();
|
||||
assertThat(result.unresolvedRecords()).singleElement().satisfies(row->{
|
||||
assertThat(row.containerNumbers()).containsExactly("TSTU1234567");
|
||||
assertThat(row.cabinetNumbers()).containsExactly("D1821");
|
||||
assertThat(row.incompleteReasons()).containsExactly("ORIGIN_TIMEZONE_UNKNOWN");
|
||||
});
|
||||
}
|
||||
@@ -146,12 +151,22 @@ class FleetTripCalendarServiceTest {
|
||||
@Test void rejectsMalformedOrForeignSourceSnapshotsExplicitly() {
|
||||
when(trips.calendar(any(),any())).thenReturn(List.of(trip("PC-A",1L)));
|
||||
for(var invalid:Arrays.asList(null,"","not-json","null","[]","{\"containerNo\":123}","{\"containerNo\":[\"TSTU1234567\"]}","{} {}")) {
|
||||
when(trips.sourceSnapshots(List.of("PC-A"))).thenReturn(List.of(new FleetTripCalendarRepository.Source("PC-A",invalid)));
|
||||
when(trips.sourceSnapshots(List.of("PC-A"))).thenReturn(List.of(new FleetTripCalendarRepository.Source("PC-A",invalid,null)));
|
||||
assertThatThrownBy(()->service.calendar(from,to)).isInstanceOf(ResponseStatusException.class)
|
||||
.hasMessageContaining("柜号资料暂不可用");
|
||||
}
|
||||
when(trips.sourceSnapshots(List.of("PC-A"))).thenReturn(List.of(new FleetTripCalendarRepository.Source("OTHER-PC","{}")));
|
||||
when(trips.sourceSnapshots(List.of("PC-A"))).thenReturn(List.of(new FleetTripCalendarRepository.Source("OTHER-PC","{}",null)));
|
||||
assertThatThrownBy(()->service.calendar(from,to)).isInstanceOf(ResponseStatusException.class).hasMessageContaining("柜号资料暂不可用");
|
||||
verifyNoInteractions(warehouses);
|
||||
}
|
||||
/** 损坏短号来源明确失败,不伪装为没有关联柜次。 */
|
||||
@Test void rejectsMalformedLoadingSnapshotsExplicitly() {
|
||||
when(trips.calendar(any(),any())).thenReturn(List.of(trip("PC-A",1L)));
|
||||
for(var invalid:List.of("","not-json","null","[]","{\"cabinetNo\":1821}","{\"cabinetNo\":[\"D1821\"]}","{} {}")) {
|
||||
when(trips.sourceSnapshots(List.of("PC-A"))).thenReturn(List.of(new FleetTripCalendarRepository.Source("PC-A","{}",invalid)));
|
||||
assertThatThrownBy(()->service.calendar(from,to)).isInstanceOf(ResponseStatusException.class)
|
||||
.hasMessageContaining("柜号资料暂不可用");
|
||||
}
|
||||
verifyNoInteractions(warehouses);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user