This commit is contained in:
2026-11-05 16:52:35 +08:00
parent 83c3b02d96
commit 08ea663334
2 changed files with 71 additions and 4 deletions
@@ -1307,14 +1307,14 @@ public class DispatchAggregateService implements DispatchExecutionPort,DispatchA
public long pickedRemotePackages(DispatchAggregate row){return looseboardFacts==null?0:looseboardFacts.pickedRemotePackages(row);}
/** 仓内实物作业范围,普通未收货计划仍保留并按原规则阻断。 */
public List<DispatchItem> warehouseItems(DispatchAggregate row){return looseboardFacts==null?repository.cargo(row.getId()):looseboardFacts.warehouseItems(row.getId());}
/** 异地取货仍占用本趟容量;使用仓内实测加散板申报,不补造实托或实测。 */
/** 混排核对已知占用;未知重量体积不阻塞仓内出库,也不补造实测。 */
public void requireRemoteCapacity(DispatchAggregate row,List<LoadingMeasurement> measured){
var ids=remoteItemIds(row);if(ids.isEmpty())return;
var remote=repository.cargo(row.getId()).stream().filter(i->ids.contains(i.getId())).toList();
var capacity=read(row.getResourcesJson(),Resources.class).vehicle().capacity();
if(remote.stream().anyMatch(i->i.getGrossWeightKg()==null || i.getVolumeCbm()==null) || measured.stream().anyMatch(m->m.weightKg()==null || m.volumeCbm()==null))throw conflict("混排实测与异地取货申报货量未明确");
var weight=remote.stream().map(DispatchItem::getGrossWeightKg).reduce(java.math.BigDecimal.ZERO,java.math.BigDecimal::add).add(measured.stream().map(LoadingMeasurement::weightKg).reduce(java.math.BigDecimal.ZERO,java.math.BigDecimal::add));
var volume=remote.stream().map(DispatchItem::getVolumeCbm).reduce(java.math.BigDecimal.ZERO,java.math.BigDecimal::add).add(measured.stream().map(LoadingMeasurement::volumeCbm).reduce(java.math.BigDecimal.ZERO,java.math.BigDecimal::add));
// 只累加已知值作为占用下限;某项未知不能掩盖其余已知货量已经超载的事实。
var weight=remote.stream().map(DispatchItem::getGrossWeightKg).filter(Objects::nonNull).reduce(java.math.BigDecimal.ZERO,java.math.BigDecimal::add).add(measured.stream().map(LoadingMeasurement::weightKg).filter(Objects::nonNull).reduce(java.math.BigDecimal.ZERO,java.math.BigDecimal::add));
var volume=remote.stream().map(DispatchItem::getVolumeCbm).filter(Objects::nonNull).reduce(java.math.BigDecimal.ZERO,java.math.BigDecimal::add).add(measured.stream().map(LoadingMeasurement::volumeCbm).filter(Objects::nonNull).reduce(java.math.BigDecimal.ZERO,java.math.BigDecimal::add));
if(remote.size()+measured.stream().mapToInt(LoadingMeasurement::palletSpaces).sum()>capacity.palletSpaces() || weight.compareTo(capacity.maxLoadKg())>0 || volume.compareTo(capacity.maxVolumeCbm())>0)throw conflict("仓内装载与异地取货合计超过本车容量");
}
/** 取货照片保存事务复验已确认仓内实载与当前异地申报。 */
@@ -0,0 +1,67 @@
package com.baiyue.lastleg.dispatch.service;
import com.baiyue.lastleg.dispatch.entity.DispatchAggregate;
import com.baiyue.lastleg.dispatch.entity.DispatchItem;
import com.baiyue.lastleg.dispatch.repository.DispatchAggregateRepository;
import com.baiyue.lastleg.dispatch.port.DispatchContracts.LoadingMeasurement;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import org.springframework.test.util.ReflectionTestUtils;
import java.math.BigDecimal;
import java.util.List;
import java.util.Set;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.Mockito.*;
/** 混排仓内出库允许未知实测,但已知容量超限仍必须拒绝。 */
class DispatchMixedCapacityTest {
/** 仓内九箱已核平时,缺少重量体积不应额外要求异地货补测。 */
@Test void unknownMeasurementsDoNotBlockWarehouseLoading() {
var f=new Fixture(null,null);
assertThatCode(()->f.service.requireRemoteCapacity(f.row,f.measured(null,null,1))).doesNotThrowAnyException();
}
/** 部分未知不能掩盖已知重量已超载。 */
@Test void knownWeightStillBlocksWithUnknownVolume() {
var f=new Fixture("600",null);
assertThatThrownBy(()->f.service.requireRemoteCapacity(f.row,f.measured("401",null,1))).hasMessageContaining("超过本车容量");
}
/** 部分未知不能掩盖已知体积已超载。 */
@Test void knownVolumeStillBlocksWithUnknownWeight() {
var f=new Fixture(null,"30");
assertThatThrownBy(()->f.service.requireRemoteCapacity(f.row,f.measured(null,"21",1))).hasMessageContaining("超过本车容量");
}
/** 无重量体积依据时,明确的托位上限仍然有效。 */
@Test void palletSpacesRemainLimited() {
var f=new Fixture(null,null);
assertThatThrownBy(()->f.service.requireRemoteCapacity(f.row,f.measured(null,null,10))).hasMessageContaining("超过本车容量");
}
/** 已知占用恰好达到容量时允许确认。 */
@Test void exactKnownCapacityIsAllowed() {
var f=new Fixture("600","30");
assertThatCode(()->f.service.requireRemoteCapacity(f.row,f.measured("400","20",9))).doesNotThrowAnyException();
}
/** 隔离容量校验,异地身份及货量均由正式依赖返回。 */
private static class Fixture {
final DispatchAggregateService service=mock(DispatchAggregateService.class,CALLS_REAL_METHODS);
final DispatchAggregate row=new DispatchAggregate();
/** 构造一条异地取货来源与车辆真实容量,不制造未知实测。 */
Fixture(String weight,String volume) {
var repository=mock(DispatchAggregateRepository.class);
var facts=mock(DispatchLooseboardFacts.class);
ReflectionTestUtils.setField(service,"repository",repository);
ReflectionTestUtils.setField(service,"looseboardFacts",facts);
ReflectionTestUtils.setField(service,"json",new ObjectMapper());
row.setId(1L);
row.setResourcesJson("{\"vehicle\":{\"capacity\":{\"palletSpaces\":10,\"maxLoadKg\":1000,\"maxVolumeCbm\":50}}}");
var remote=new DispatchItem();remote.setId(2L);
remote.setGrossWeightKg(weight==null?null:new BigDecimal(weight));
remote.setVolumeCbm(volume==null?null:new BigDecimal(volume));
when(facts.remoteItemIds(1L)).thenReturn(Set.of(2L));
when(repository.cargo(1L)).thenReturn(List.of(remote));
}
/** 保留仓内原始可空测量值供校验使用。 */
List<LoadingMeasurement> measured(String weight,String volume,int spaces) {
return List.of(new LoadingMeasurement(3L,spaces,weight==null?null:new BigDecimal(weight),volume==null?null:new BigDecimal(volume),1));
}
}
}