优化
This commit is contained in:
+69
@@ -0,0 +1,69 @@
|
||||
package com.baiyue.warehouse.service;
|
||||
|
||||
import com.baiyue.common.core.api.ResultCode;
|
||||
import com.baiyue.common.core.exception.BizException;
|
||||
import com.baiyue.warehouse.client.DomesticWarehouseDirectoryClient;
|
||||
import com.baiyue.warehouse.dto.SharedWarehouseReference;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.time.Clock;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
@Component
|
||||
public class DomesticWarehouseDirectory {
|
||||
@Resource private DomesticWarehouseDirectoryClient client;
|
||||
@Value("${baiyue.security.internal-service-token:${baiyue.internal-service.token}}")
|
||||
private String internalToken;
|
||||
@Value("${baiyue.clients.domestic-warehouse.cache-ttl:30m}") private Duration ttl;
|
||||
private final ConcurrentHashMap<Long, CachedWarehouse> cache = new ConcurrentHashMap<>();
|
||||
private Clock clock = Clock.systemUTC();
|
||||
|
||||
public SharedWarehouseReference required(long warehouseId) {
|
||||
CachedWarehouse current = cache.get(warehouseId);
|
||||
Instant now = clock.instant();
|
||||
if (current != null && now.isBefore(current.expiresAt())) return current.warehouse();
|
||||
var response = client.activeSharedWarehouse(internalToken, warehouseId);
|
||||
if (response == null || !response.successful() || response.data() == null
|
||||
|| !valid(response.data())) {
|
||||
cache.remove(warehouseId);
|
||||
throw new BizException(ResultCode.NOT_FOUND,
|
||||
"启用的公共海外收货仓不存在");
|
||||
}
|
||||
SharedWarehouseReference warehouse = toReference(response.data());
|
||||
cache.put(warehouseId, new CachedWarehouse(warehouse, now.plus(ttl)));
|
||||
return warehouse;
|
||||
}
|
||||
|
||||
public List<SharedWarehouseReference> options() {
|
||||
var response = client.activeSharedWarehouses(internalToken);
|
||||
if (response == null || !response.successful() || response.data() == null) {
|
||||
throw new BizException(ResultCode.SERVICE_UNAVAILABLE, "国内仓库目录暂不可用");
|
||||
}
|
||||
List<SharedWarehouseReference> warehouses = response.data().stream()
|
||||
.filter(this::valid).map(this::toReference).toList();
|
||||
if (!warehouses.isEmpty()) {
|
||||
Instant expiresAt = clock.instant().plus(ttl);
|
||||
warehouses.forEach(value -> cache.put(value.warehouseId(), new CachedWarehouse(value, expiresAt)));
|
||||
}
|
||||
return warehouses;
|
||||
}
|
||||
|
||||
private boolean valid(DomesticWarehouseDirectoryClient.WarehouseReference value) {
|
||||
return value.warehouseId() != null && value.warehouseId() > 0
|
||||
&& StringUtils.hasText(value.warehouseCode())
|
||||
&& StringUtils.hasText(value.warehouseName()) && "ACTIVE".equals(value.status());
|
||||
}
|
||||
|
||||
private SharedWarehouseReference toReference(DomesticWarehouseDirectoryClient.WarehouseReference value) {
|
||||
return new SharedWarehouseReference(value.warehouseId(), value.warehouseCode().trim(),
|
||||
value.warehouseName().trim(), value.status());
|
||||
}
|
||||
|
||||
private record CachedWarehouse(SharedWarehouseReference warehouse, Instant expiresAt) { }
|
||||
}
|
||||
-206
@@ -1,206 +0,0 @@
|
||||
package com.baiyue.warehouse.service;
|
||||
import jakarta.annotation.Resource;
|
||||
|
||||
import com.baiyue.common.audit.BusinessAudit;
|
||||
import com.baiyue.common.core.api.PageResult;
|
||||
import com.baiyue.common.core.api.ResultCode;
|
||||
import com.baiyue.common.core.exception.BizException;
|
||||
import com.baiyue.warehouse.dto.WarehouseDetailResponse;
|
||||
import com.baiyue.warehouse.dto.WarehouseRequests;
|
||||
import com.baiyue.warehouse.dto.SharedWarehouseReference;
|
||||
import com.baiyue.warehouse.entity.ServiceCapabilityEntity;
|
||||
import com.baiyue.warehouse.entity.WarehouseAddressEntity;
|
||||
import com.baiyue.warehouse.entity.WarehouseEntity;
|
||||
import com.baiyue.warehouse.entity.WarehouseLocationEntity;
|
||||
import com.baiyue.warehouse.repository.WarehouseRepository;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.List;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 仓库应用服务,负责仓库主数据、地址和服务能力的租户级用例编排、事务控制与业务校验。
|
||||
*/
|
||||
@Service
|
||||
public class WarehouseApplicationService {
|
||||
@Resource
|
||||
private WarehouseRepository repository;
|
||||
|
||||
|
||||
@Transactional
|
||||
@BusinessAudit(
|
||||
event = "WAREHOUSE_CREATE",
|
||||
resourceType = "WAREHOUSE",
|
||||
resourceId = "#r.warehouseCode",
|
||||
description = "新建仓库",
|
||||
captureBefore = false
|
||||
)
|
||||
public Long create(WarehouseRequests.SaveRequest request) {
|
||||
validateSharedWarehouse(request);
|
||||
ensureCodeUnique(request.customerId(), request.warehouseCode(), null);
|
||||
WarehouseEntity warehouse = toEntity(request);
|
||||
repository.insertWarehouse(warehouse);
|
||||
ensureDefaultVirtualLocation(warehouse);
|
||||
insertChildren(warehouse.getCustomerId(), warehouse.getId(), request.address(), request.capabilities());
|
||||
return warehouse.getId();
|
||||
}
|
||||
|
||||
private void ensureDefaultVirtualLocation(WarehouseEntity warehouse) {
|
||||
String locationCode = "LOC-" + warehouse.getWarehouseCode() + "-DEFAULT";
|
||||
if (repository.existsLocationCode(warehouse.getCustomerId(), warehouse.getId(), locationCode)) {
|
||||
return;
|
||||
}
|
||||
WarehouseLocationEntity location = new WarehouseLocationEntity();
|
||||
location.setCustomerId(warehouse.getCustomerId());
|
||||
location.setWarehouseId(warehouse.getId());
|
||||
location.setZoneCode("DEFAULT");
|
||||
location.setLocationCode(locationCode);
|
||||
location.setLocationType("VIRTUAL");
|
||||
location.setMaxLoadKg(new BigDecimal("999999999.000"));
|
||||
location.setStatus("ACTIVE");
|
||||
location.setRemark("System default virtual location");
|
||||
repository.insertLocation(location);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@BusinessAudit(
|
||||
event = "WAREHOUSE_UPDATE",
|
||||
resourceType = "WAREHOUSE",
|
||||
resourceId = "#id",
|
||||
description = "更新仓库"
|
||||
)
|
||||
public void update(long customerId, long id, WarehouseRequests.SaveRequest request) {
|
||||
if (request.customerId() == null || customerId != request.customerId()) {
|
||||
throw new BizException(ResultCode.FORBIDDEN, "租户不匹配");
|
||||
}
|
||||
validateSharedWarehouse(request);
|
||||
WarehouseEntity old = owned(customerId, id);
|
||||
ensureCodeUnique(customerId, request.warehouseCode(), id);
|
||||
|
||||
WarehouseEntity warehouse = toEntity(request);
|
||||
warehouse.setId(old.getId());
|
||||
repository.saveWarehouse(warehouse);
|
||||
|
||||
repository.physicallyDeleteChildren(customerId, id);
|
||||
insertChildren(customerId, id, request.address(), request.capabilities());
|
||||
}
|
||||
|
||||
public PageResult<WarehouseEntity> page(long customerId, WarehouseRequests.QueryRequest query) {
|
||||
return repository.page(customerId, query);
|
||||
}
|
||||
|
||||
public PageResult<WarehouseEntity> pageAll(WarehouseRequests.QueryRequest query) {
|
||||
return repository.pageAll(query);
|
||||
}
|
||||
|
||||
public WarehouseDetailResponse detail(long customerId, long id) {
|
||||
WarehouseEntity warehouse = owned(customerId, id);
|
||||
List<WarehouseAddressEntity> addresses = repository.findAddresses(customerId, id);
|
||||
List<ServiceCapabilityEntity> capabilities = repository.findCapabilities(customerId, id);
|
||||
return new WarehouseDetailResponse(warehouse, addresses, capabilities);
|
||||
}
|
||||
|
||||
public SharedWarehouseReference activeSharedReference(long warehouseId) {
|
||||
WarehouseEntity warehouse = repository.findOwned(0L, warehouseId);
|
||||
boolean receiving = warehouse != null && repository.findCapabilities(0L, warehouseId).stream()
|
||||
.anyMatch(value -> "RECEIVING".equals(value.getCapabilityCode()) && Boolean.TRUE.equals(value.getEnabled()));
|
||||
if (warehouse == null || !"ACTIVE".equals(warehouse.getStatus())
|
||||
|| !"OVERSEAS".equals(warehouse.getWarehouseType()) || !receiving) {
|
||||
throw new BizException(ResultCode.NOT_FOUND, "Active shared overseas warehouse not found");
|
||||
}
|
||||
return new SharedWarehouseReference(warehouse.getId(), warehouse.getWarehouseCode(),
|
||||
warehouse.getWarehouseName(), warehouse.getStatus());
|
||||
}
|
||||
|
||||
private void validateSharedWarehouse(WarehouseRequests.SaveRequest request) {
|
||||
if (request.customerId() == null || request.customerId() != 0L) return;
|
||||
boolean receiving = request.capabilities() != null && request.capabilities().stream()
|
||||
.anyMatch(value -> "RECEIVING".equals(value.capabilityCode())
|
||||
&& !Boolean.FALSE.equals(value.enabled()));
|
||||
if (!"OVERSEAS".equals(request.warehouseType()) || !receiving) {
|
||||
throw new BizException(ResultCode.BAD_REQUEST,
|
||||
"Shared physical warehouse must be OVERSEAS with RECEIVING enabled");
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@BusinessAudit(
|
||||
event = "WAREHOUSE_STATUS_CHANGE",
|
||||
resourceType = "WAREHOUSE",
|
||||
resourceId = "#id",
|
||||
description = "启用或停用仓库"
|
||||
)
|
||||
public void changeStatus(long customerId, long id, String status) {
|
||||
if (!"ACTIVE".equals(status) && !"INACTIVE".equals(status)) {
|
||||
throw new BizException("无效状态");
|
||||
}
|
||||
WarehouseEntity warehouse = owned(customerId, id);
|
||||
warehouse.setStatus(status);
|
||||
repository.saveWarehouse(warehouse);
|
||||
}
|
||||
|
||||
private WarehouseEntity owned(long customerId, long id) {
|
||||
WarehouseEntity warehouse = repository.findOwned(customerId, id);
|
||||
if (warehouse == null) {
|
||||
throw new BizException(ResultCode.NOT_FOUND, "仓库不存在");
|
||||
}
|
||||
return warehouse;
|
||||
}
|
||||
|
||||
private void ensureCodeUnique(long customerId, String code, Long excludedId) {
|
||||
if (repository.existsCode(customerId, code, excludedId)) {
|
||||
throw new BizException(ResultCode.CONFLICT, "仓库编码已存在");
|
||||
}
|
||||
}
|
||||
|
||||
private WarehouseEntity toEntity(WarehouseRequests.SaveRequest request) {
|
||||
WarehouseEntity warehouse = new WarehouseEntity();
|
||||
warehouse.setCustomerId(request.customerId());
|
||||
warehouse.setWarehouseCode(request.warehouseCode());
|
||||
warehouse.setWarehouseName(request.warehouseName());
|
||||
warehouse.setWarehouseType(request.warehouseType());
|
||||
warehouse.setCountryCode(request.countryCode());
|
||||
warehouse.setTimezone(request.timezone());
|
||||
warehouse.setContactName(request.contactName());
|
||||
warehouse.setContactPhone(request.contactPhone());
|
||||
warehouse.setContactEmail(request.contactEmail());
|
||||
warehouse.setStatus(request.status());
|
||||
warehouse.setRemark(request.remark());
|
||||
return warehouse;
|
||||
}
|
||||
|
||||
private void insertChildren(long customerId, long warehouseId,
|
||||
WarehouseRequests.AddressRequest address,
|
||||
List<WarehouseRequests.CapabilityRequest> requestedCapabilities) {
|
||||
WarehouseAddressEntity addressEntity = new WarehouseAddressEntity();
|
||||
addressEntity.setCustomerId(customerId);
|
||||
addressEntity.setWarehouseId(warehouseId);
|
||||
addressEntity.setAddressType(address.addressType());
|
||||
addressEntity.setCountryCode(address.countryCode());
|
||||
addressEntity.setStateProvince(address.stateProvince());
|
||||
addressEntity.setCity(address.city());
|
||||
addressEntity.setDistrict(address.district());
|
||||
addressEntity.setPostalCode(address.postalCode());
|
||||
addressEntity.setAddressLine1(address.addressLine1());
|
||||
addressEntity.setAddressLine2(address.addressLine2());
|
||||
addressEntity.setLatitude(address.latitude());
|
||||
addressEntity.setLongitude(address.longitude());
|
||||
repository.insertAddress(addressEntity);
|
||||
|
||||
if (requestedCapabilities == null) {
|
||||
return;
|
||||
}
|
||||
for (WarehouseRequests.CapabilityRequest capability : requestedCapabilities) {
|
||||
ServiceCapabilityEntity entity = new ServiceCapabilityEntity();
|
||||
entity.setCustomerId(customerId);
|
||||
entity.setWarehouseId(warehouseId);
|
||||
entity.setCapabilityCode(capability.capabilityCode());
|
||||
entity.setCapabilityName(capability.capabilityName());
|
||||
entity.setEnabled(capability.enabled() == null || capability.enabled());
|
||||
entity.setCutoffTime(capability.cutoffTime());
|
||||
entity.setDescription(capability.description());
|
||||
repository.insertCapability(entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
+9
-21
@@ -7,9 +7,9 @@ import com.baiyue.warehouse.dto.LocationRequests;
|
||||
import com.baiyue.warehouse.dto.PalletRequests;
|
||||
import com.baiyue.warehouse.dto.WarehousePalletResolveResponse;
|
||||
import com.baiyue.warehouse.dto.WarehouseLocationResolveResponse;
|
||||
import com.baiyue.warehouse.dto.SharedWarehouseReference;
|
||||
import com.baiyue.warehouse.entity.WarehouseLocationEntity;
|
||||
import com.baiyue.warehouse.entity.WarehousePalletEntity;
|
||||
import com.baiyue.warehouse.entity.WarehouseEntity;
|
||||
import com.baiyue.warehouse.entity.WarehousePalletBindingEntity;
|
||||
import com.baiyue.warehouse.repository.WarehouseResourceRepository;
|
||||
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
|
||||
@@ -29,6 +29,7 @@ public class WarehouseResourceService {
|
||||
private static final Set<String> PALLET_STATUSES = Set.of("IDLE", "OCCUPIED", "SCRAPPED");
|
||||
|
||||
@Resource private WarehouseResourceRepository repository;
|
||||
@Resource private DomesticWarehouseDirectory warehouseDirectory;
|
||||
|
||||
@Transactional
|
||||
public Long createLocation(long customerId, long warehouseId, LocationRequests.SaveRequest request) {
|
||||
@@ -78,11 +79,11 @@ public class WarehouseResourceService {
|
||||
|
||||
@Transactional
|
||||
public Long createPallet(long customerId, long warehouseId, PalletRequests.SaveRequest request) {
|
||||
WarehouseEntity warehouse = requireWarehouseEntity(customerId, warehouseId);
|
||||
SharedWarehouseReference warehouse = requireWarehouseReference(warehouseId);
|
||||
validatePalletStatus(request.status());
|
||||
Long palletId = IdWorker.getId();
|
||||
String palletCode = StringUtils.hasText(request.palletCode()) ? request.palletCode().trim()
|
||||
: generatedPalletCode(warehouse.getWarehouseCode(), palletId);
|
||||
: generatedPalletCode(warehouse.warehouseCode(), palletId);
|
||||
Long locationId = request.locationId();
|
||||
if (locationId == null) {
|
||||
locationId = ensureDefaultVirtualLocation(customerId, warehouseId).getId();
|
||||
@@ -121,8 +122,8 @@ public class WarehouseResourceService {
|
||||
*/
|
||||
@Transactional
|
||||
public WarehouseLocationEntity ensureDefaultVirtualLocation(long customerId, long warehouseId) {
|
||||
WarehouseEntity warehouse = requireWarehouseEntity(customerId, warehouseId);
|
||||
String locationCode = defaultLocationCode(warehouse.getWarehouseCode());
|
||||
SharedWarehouseReference warehouse = requireWarehouseReference(warehouseId);
|
||||
String locationCode = defaultLocationCode(warehouse.warehouseCode());
|
||||
WarehouseLocationEntity existing = repository.findLocationByCode(customerId, warehouseId, locationCode);
|
||||
if (existing != null) {
|
||||
if (!"VIRTUAL".equals(existing.getLocationType()) || !"ACTIVE".equals(existing.getStatus())) {
|
||||
@@ -230,24 +231,11 @@ public class WarehouseResourceService {
|
||||
}
|
||||
|
||||
private void requireWarehouse(long customerId, long warehouseId) {
|
||||
if (customerId == PUBLIC_RESOURCE_SCOPE) {
|
||||
if (repository.findWarehouseById(warehouseId) == null) {
|
||||
throw new BizException(ResultCode.NOT_FOUND, "Warehouse not found");
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!repository.warehouseOwned(customerId, warehouseId)) {
|
||||
throw new BizException(ResultCode.NOT_FOUND, "当前租户下仓库不存在");
|
||||
}
|
||||
warehouseDirectory.required(warehouseId);
|
||||
}
|
||||
|
||||
private WarehouseEntity requireWarehouseEntity(long customerId, long warehouseId) {
|
||||
WarehouseEntity warehouse = customerId == PUBLIC_RESOURCE_SCOPE
|
||||
? repository.findWarehouseById(warehouseId) : repository.findWarehouse(customerId, warehouseId);
|
||||
if (warehouse == null) {
|
||||
throw new BizException(ResultCode.NOT_FOUND, "Tenant warehouse not found");
|
||||
}
|
||||
return warehouse;
|
||||
private SharedWarehouseReference requireWarehouseReference(long warehouseId) {
|
||||
return warehouseDirectory.required(warehouseId);
|
||||
}
|
||||
|
||||
private WarehouseLocationEntity requireLocation(long customerId, long warehouseId, long id) {
|
||||
|
||||
Reference in New Issue
Block a user