暂存提交

This commit is contained in:
2026-08-19 16:24:02 +08:00
parent f5c4bf130a
commit 6acb6178da
354 changed files with 25000 additions and 198 deletions
@@ -0,0 +1,176 @@
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.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) {
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, "租户不匹配");
}
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 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);
}
@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);
}
}
}
@@ -0,0 +1,158 @@
package com.baiyue.warehouse.service;
import com.baiyue.common.core.api.ResultCode;
import com.baiyue.common.core.exception.BizException;
import com.baiyue.warehouse.dto.WarehouseResourceLockRequests;
import com.baiyue.warehouse.dto.WarehouseResourceLockResponse;
import com.baiyue.warehouse.dto.WarehousePalletScrapResponse;
import com.baiyue.warehouse.entity.WarehouseLocationEntity;
import com.baiyue.warehouse.entity.WarehousePalletEntity;
import com.baiyue.warehouse.entity.WarehouseResourceLockEntity;
import com.baiyue.warehouse.repository.WarehouseResourceLockRepository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
import jakarta.annotation.Resource;
import java.time.LocalDateTime;
import java.util.Objects;
@Service
public class WarehouseResourceLockService {
@Resource private WarehouseResourceLockRepository repository;
@Transactional(isolation = Isolation.READ_COMMITTED)
public WarehouseResourceLockResponse reserve(WarehouseResourceLockRequests.ReserveRequest request) {
WarehouseResourceLockEntity existing = repository.findByReservationKey(request.customerId(), request.reservationKey());
if (existing != null) return idempotentResult(existing, request);
WarehousePalletEntity pallet = repository.findPallet(request.customerId(), request.warehouseId(), request.palletId());
WarehouseLocationEntity location = repository.findLocation(request.customerId(), request.warehouseId(), request.locationId());
if (pallet == null || location == null) {
WarehouseResourceLockEntity concurrent =
repository.findByReservationKey(request.customerId(), request.reservationKey());
if (concurrent != null) return idempotentResult(concurrent, request);
throw new BizException(ResultCode.NOT_FOUND, "Pallet or location does not belong to the tenant warehouse");
}
boolean virtualLocation = isVirtual(location);
boolean locationAvailable = virtualLocation ? "ACTIVE".equals(location.getStatus()) : "IDLE".equals(location.getStatus());
if (!"IDLE".equals(pallet.getStatus()) || !locationAvailable) {
WarehouseResourceLockEntity concurrent =
repository.findByReservationKey(request.customerId(), request.reservationKey());
if (concurrent != null) return idempotentResult(concurrent, request);
throw new BizException(ResultCode.CONFLICT, "Only IDLE pallet and location can be reserved");
}
if (repository.occupyPallet(request.customerId(), request.warehouseId(), request.palletId(), request.locationId()) != 1) {
return concurrentIdempotentOrConflict(request);
}
if (!virtualLocation && repository.occupyLocation(request.customerId(), request.warehouseId(), request.locationId()) != 1) {
throw new BizException(ResultCode.CONFLICT, "Location is no longer IDLE");
}
WarehouseResourceLockEntity lock = new WarehouseResourceLockEntity();
lock.setCustomerId(request.customerId());
lock.setWarehouseId(request.warehouseId());
lock.setPalletId(request.palletId());
lock.setLocationId(request.locationId());
lock.setBusinessType(request.businessType());
lock.setBusinessId(request.businessId());
lock.setReservationKey(request.reservationKey());
lock.setStatus("ACTIVE");
lock.setLockedAt(LocalDateTime.now());
repository.insert(lock);
return WarehouseResourceLockResponse.from(lock);
}
@Transactional
public WarehouseResourceLockResponse release(long customerId, long lockId) {
WarehouseResourceLockEntity lock = requireLock(customerId, lockId);
if ("RELEASED".equals(lock.getStatus())) return WarehouseResourceLockResponse.from(lock);
requireActive(lock);
boolean virtualLocation = isVirtualLockLocation(lock);
int palletResult = virtualLocation ? repository.releasePalletAtVirtualLocation(lock) : repository.releasePallet(lock);
if (palletResult != 1 || (!virtualLocation && repository.releaseLocation(lock) != 1)
|| repository.close(lock, "RELEASED") != 1) {
throw new BizException(ResultCode.CONFLICT, "Resource lock state changed concurrently");
}
lock.setStatus("RELEASED");
lock.setReleasedAt(LocalDateTime.now());
return WarehouseResourceLockResponse.from(lock);
}
@Transactional
public WarehouseResourceLockResponse scrap(long customerId, long lockId) {
WarehouseResourceLockEntity lock = requireLock(customerId, lockId);
if ("SCRAPPED".equals(lock.getStatus())) return WarehouseResourceLockResponse.from(lock);
requireActive(lock);
boolean virtualLocation = isVirtualLockLocation(lock);
int palletResult = virtualLocation ? repository.scrapPalletAtVirtualLocation(lock) : repository.scrapPallet(lock);
if (palletResult != 1 || (!virtualLocation && repository.releaseLocation(lock) != 1)
|| repository.close(lock, "SCRAPPED") != 1) {
throw new BizException(ResultCode.CONFLICT, "Resource lock state changed concurrently");
}
lock.setStatus("SCRAPPED");
lock.setReleasedAt(LocalDateTime.now());
return WarehouseResourceLockResponse.from(lock);
}
@Transactional
public WarehousePalletScrapResponse scrapByPallet(long customerId, long palletId) {
WarehousePalletEntity pallet = repository.findPallet(customerId, palletId);
if (pallet == null) throw new BizException(ResultCode.NOT_FOUND, "Pallet not found");
if ("SCRAPPED".equals(pallet.getStatus())) {
return new WarehousePalletScrapResponse(palletId, pallet.getWarehouseId(), pallet.getLocationId(), "SCRAPPED");
}
if (!"OCCUPIED".equals(pallet.getStatus())) {
throw new BizException(ResultCode.CONFLICT, "Only an OCCUPIED pallet can be scrapped after shipment");
}
WarehouseResourceLockEntity lock = repository.findActiveByPallet(customerId, palletId);
if (lock != null) {
scrap(customerId, lock.getId());
} else if (repository.scrapPalletWithoutLock(customerId, palletId) != 1) {
throw new BizException(ResultCode.CONFLICT, "Pallet state changed concurrently");
}
return new WarehousePalletScrapResponse(palletId, pallet.getWarehouseId(), pallet.getLocationId(), "SCRAPPED");
}
private WarehouseResourceLockResponse concurrentIdempotentOrConflict(
WarehouseResourceLockRequests.ReserveRequest request) {
WarehouseResourceLockEntity existing = repository.findByReservationKey(request.customerId(), request.reservationKey());
if (existing != null) return idempotentResult(existing, request);
throw new BizException(ResultCode.CONFLICT, "Pallet is no longer IDLE");
}
private WarehouseResourceLockResponse idempotentResult(WarehouseResourceLockEntity existing,
WarehouseResourceLockRequests.ReserveRequest request) {
if (existing.getWarehouseId() != request.warehouseId()
|| existing.getPalletId() != request.palletId()
|| existing.getLocationId() != request.locationId()
|| !Objects.equals(existing.getBusinessType(), request.businessType())
|| !Objects.equals(existing.getBusinessId(), request.businessId())) {
throw new BizException(ResultCode.CONFLICT, "Reservation key was already used with different resources");
}
return WarehouseResourceLockResponse.from(existing);
}
private WarehouseResourceLockEntity requireLock(long customerId, long lockId) {
WarehouseResourceLockEntity lock = repository.find(customerId, lockId);
if (lock == null) throw new BizException(ResultCode.NOT_FOUND, "Resource lock not found");
return lock;
}
private static void requireActive(WarehouseResourceLockEntity lock) {
if (!"ACTIVE".equals(lock.getStatus())) {
throw new BizException(ResultCode.CONFLICT, "Resource lock is not ACTIVE");
}
}
private boolean isVirtualLockLocation(WarehouseResourceLockEntity lock) {
WarehouseLocationEntity location = repository.findLocation(lock.getCustomerId(), lock.getWarehouseId(), lock.getLocationId());
if (location == null) throw new BizException(ResultCode.NOT_FOUND, "Resource lock location not found");
return isVirtual(location);
}
private static boolean isVirtual(WarehouseLocationEntity location) {
return "VIRTUAL".equals(location.getLocationType());
}
}
@@ -0,0 +1,282 @@
package com.baiyue.warehouse.service;
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.LocationRequests;
import com.baiyue.warehouse.dto.PalletRequests;
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;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import jakarta.annotation.Resource;
import java.util.Set;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import org.springframework.util.StringUtils;
@Service
public class WarehouseResourceService {
private static final Set<String> LOCATION_STATUSES = Set.of("ACTIVE", "IDLE", "OCCUPIED", "INACTIVE");
private static final Set<String> PALLET_STATUSES = Set.of("IDLE", "OCCUPIED", "SCRAPPED");
@Resource private WarehouseResourceRepository repository;
@Transactional
public Long createLocation(long customerId, long warehouseId, LocationRequests.SaveRequest request) {
requireWarehouse(customerId, warehouseId);
validateLocation(request, customerId, warehouseId, null);
WarehouseLocationEntity entity = locationEntity(customerId, warehouseId, request);
repository.insertLocation(entity);
return entity.getId();
}
@Transactional
public void updateLocation(long customerId, long warehouseId, long id, LocationRequests.SaveRequest request) {
WarehouseLocationEntity existing = requireLocation(customerId, warehouseId, id);
if ("VIRTUAL".equals(existing.getLocationType())
&& (!"VIRTUAL".equals(request.locationType()) || !"ACTIVE".equals(request.status())
|| !existing.getLocationCode().equals(request.locationCode()))) {
throw new BizException(ResultCode.CONFLICT, "Default virtual location must remain VIRTUAL and ACTIVE");
}
validateLocation(request, customerId, warehouseId, id);
WarehouseLocationEntity entity = locationEntity(customerId, warehouseId, request);
entity.setId(existing.getId());
repository.updateLocation(entity);
}
public WarehouseLocationEntity location(long customerId, long warehouseId, long id) {
requireWarehouse(customerId, warehouseId);
return requireLocation(customerId, warehouseId, id);
}
public PageResult<WarehouseLocationEntity> locations(long customerId, long warehouseId,
LocationRequests.QueryRequest query) {
requireWarehouse(customerId, warehouseId);
return repository.pageLocations(customerId, warehouseId, query);
}
@Transactional
public void deleteLocation(long customerId, long warehouseId, long id) {
WarehouseLocationEntity location = requireLocation(customerId, warehouseId, id);
if ("VIRTUAL".equals(location.getLocationType())) {
throw new BizException(ResultCode.CONFLICT, "Default virtual location cannot be deleted");
}
if (repository.locationHasPallets(customerId, warehouseId, id)) {
throw new BizException(ResultCode.CONFLICT, "库位仍绑定托盘,不能删除");
}
repository.deleteLocation(id);
}
@Transactional
public Long createPallet(long customerId, long warehouseId, PalletRequests.SaveRequest request) {
WarehouseEntity warehouse = requireWarehouseEntity(customerId, warehouseId);
validatePalletStatus(request.status());
Long palletId = IdWorker.getId();
String palletCode = StringUtils.hasText(request.palletCode()) ? request.palletCode().trim()
: generatedPalletCode(warehouse.getWarehouseCode(), palletId);
Long locationId = request.locationId();
if (locationId == null) {
locationId = ensureDefaultVirtualLocation(customerId, warehouseId).getId();
}
validatePallet(request, palletCode, locationId, customerId, warehouseId, null);
WarehousePalletEntity entity = palletEntity(customerId, warehouseId, request, palletCode, locationId);
entity.setId(palletId);
repository.insertPallet(entity);
return entity.getId();
}
@Transactional
public WarehousePalletEntity ensureDropshipPallet(long customerId, long warehouseId, String businessId) {
WarehousePalletBindingEntity existing = repository.findPalletBinding(customerId, warehouseId, "DROPSHIP", businessId);
if (existing != null) return requirePallet(customerId, warehouseId, existing.getPalletId());
Long palletId = createPallet(customerId, warehouseId,
new PalletRequests.SaveRequest(null, "DROPSHIP", null, java.math.BigDecimal.ZERO, "IDLE", null,
"Default dropship pallet for " + businessId));
WarehousePalletBindingEntity binding = new WarehousePalletBindingEntity();
binding.setId(IdWorker.getId()); binding.setCustomerId(customerId); binding.setWarehouseId(warehouseId);
binding.setBusinessType("DROPSHIP"); binding.setBusinessId(businessId); binding.setPalletId(palletId);
binding.setCreatedAt(java.time.LocalDateTime.now());
try {
repository.insertPalletBinding(binding);
return requirePallet(customerId, warehouseId, palletId);
} catch (org.springframework.dao.DuplicateKeyException duplicate) {
WarehousePalletBindingEntity winner = repository.findPalletBinding(customerId, warehouseId, "DROPSHIP", businessId);
if (winner == null) throw duplicate;
return requirePallet(customerId, warehouseId, winner.getPalletId());
}
}
/**
* Returns the warehouse-scoped virtual location used only as a pallet/inventory reference.
* It is deliberately never made IDLE/OCCUPIED by resource locking.
*/
@Transactional
public WarehouseLocationEntity ensureDefaultVirtualLocation(long customerId, long warehouseId) {
WarehouseEntity warehouse = requireWarehouseEntity(customerId, warehouseId);
String locationCode = defaultLocationCode(warehouse.getWarehouseCode());
WarehouseLocationEntity existing = repository.findLocationByCode(customerId, warehouseId, locationCode);
if (existing != null) {
if (!"VIRTUAL".equals(existing.getLocationType()) || !"ACTIVE".equals(existing.getStatus())) {
throw new BizException(ResultCode.CONFLICT,
"Default virtual location code is occupied by a non-active virtual location");
}
return existing;
}
WarehouseLocationEntity location = new WarehouseLocationEntity();
location.setId(IdWorker.getId());
location.setCustomerId(customerId);
location.setWarehouseId(warehouseId);
location.setZoneCode("DEFAULT");
location.setLocationCode(locationCode);
location.setLocationType("VIRTUAL");
location.setMaxLoadKg(new java.math.BigDecimal("999999999.000"));
location.setStatus("ACTIVE");
location.setRemark("System default virtual location");
repository.insertDefaultVirtualLocationIgnore(location);
WarehouseLocationEntity resolved = repository.findLocationByCode(customerId, warehouseId, locationCode);
if (resolved == null) {
throw new BizException(ResultCode.CONFLICT, "Default virtual location could not be created");
}
return resolved;
}
@Transactional
public void updatePallet(long customerId, long warehouseId, long id, PalletRequests.SaveRequest request) {
WarehousePalletEntity existing = requirePallet(customerId, warehouseId, id);
if ("SCRAPPED".equals(existing.getStatus()) && !"SCRAPPED".equals(request.status())) {
throw new BizException(ResultCode.CONFLICT, "SCRAPPED pallet cannot be reused");
}
String palletCode = StringUtils.hasText(request.palletCode()) ? request.palletCode().trim()
: existing.getPalletCode();
Long locationId = request.locationId() == null ? existing.getLocationId() : request.locationId();
validatePallet(request, palletCode, locationId, customerId, warehouseId, id);
WarehousePalletEntity entity = palletEntity(customerId, warehouseId, request, palletCode, locationId);
entity.setId(existing.getId());
repository.updatePallet(entity);
}
public WarehousePalletEntity pallet(long customerId, long warehouseId, long id) {
requireWarehouse(customerId, warehouseId);
return requirePallet(customerId, warehouseId, id);
}
public PageResult<WarehousePalletEntity> pallets(long customerId, long warehouseId,
PalletRequests.QueryRequest query) {
requireWarehouse(customerId, warehouseId);
return repository.pagePallets(customerId, warehouseId, query);
}
@Transactional
public void deletePallet(long customerId, long warehouseId, long id) {
WarehousePalletEntity pallet = requirePallet(customerId, warehouseId, id);
if ("OCCUPIED".equals(pallet.getStatus())) {
throw new BizException(ResultCode.CONFLICT, "占用中的托盘不能删除");
}
if ("SCRAPPED".equals(pallet.getStatus())) {
throw new BizException(ResultCode.CONFLICT, "Scrapped pallet must be retained and cannot be reused");
}
repository.deletePallet(id);
}
private void requireWarehouse(long customerId, long warehouseId) {
if (!repository.warehouseOwned(customerId, warehouseId)) {
throw new BizException(ResultCode.NOT_FOUND, "当前租户下仓库不存在");
}
}
private WarehouseEntity requireWarehouseEntity(long customerId, long warehouseId) {
WarehouseEntity warehouse = repository.findWarehouse(customerId, warehouseId);
if (warehouse == null) {
throw new BizException(ResultCode.NOT_FOUND, "Tenant warehouse not found");
}
return warehouse;
}
private WarehouseLocationEntity requireLocation(long customerId, long warehouseId, long id) {
requireWarehouse(customerId, warehouseId);
WarehouseLocationEntity entity = repository.findLocation(customerId, warehouseId, id);
if (entity == null) throw new BizException(ResultCode.NOT_FOUND, "当前仓库下库位不存在");
return entity;
}
private WarehousePalletEntity requirePallet(long customerId, long warehouseId, long id) {
requireWarehouse(customerId, warehouseId);
WarehousePalletEntity entity = repository.findPallet(customerId, warehouseId, id);
if (entity == null) throw new BizException(ResultCode.NOT_FOUND, "当前仓库下托盘不存在");
return entity;
}
private void validateLocation(LocationRequests.SaveRequest request, long customerId,
long warehouseId, Long excludedId) {
if (!LOCATION_STATUSES.contains(request.status())) {
throw new BizException("库位状态仅支持 ACTIVE、IDLE、OCCUPIED、INACTIVE");
}
if (repository.locationCodeExists(customerId, warehouseId, request.locationCode(), excludedId)) {
throw new BizException(ResultCode.CONFLICT, "当前仓库下库位编码已存在");
}
}
private void validatePallet(PalletRequests.SaveRequest request, String palletCode, Long locationId,
long customerId, long warehouseId, Long excludedId) {
validatePalletStatus(request.status());
if (repository.palletCodeExists(customerId, warehouseId, palletCode, excludedId)) {
throw new BizException(ResultCode.CONFLICT, "当前仓库下托盘编码已存在");
}
if (locationId != null) {
requireLocation(customerId, warehouseId, locationId);
}
}
private void validatePalletStatus(String status) {
if (!PALLET_STATUSES.contains(status)) {
throw new BizException("托盘状态仅支持 IDLE、OCCUPIED、SCRAPPED");
}
}
private WarehouseLocationEntity locationEntity(long customerId, long warehouseId,
LocationRequests.SaveRequest request) {
WarehouseLocationEntity entity = new WarehouseLocationEntity();
entity.setCustomerId(customerId);
entity.setWarehouseId(warehouseId);
entity.setZoneCode(request.zoneCode());
entity.setLocationCode(request.locationCode());
entity.setLocationType(request.locationType());
entity.setMaxLoadKg(request.maxLoadKg());
entity.setStatus(request.status());
entity.setRemark(request.remark());
return entity;
}
private WarehousePalletEntity palletEntity(long customerId, long warehouseId,
PalletRequests.SaveRequest request,
String palletCode, Long locationId) {
WarehousePalletEntity entity = new WarehousePalletEntity();
entity.setCustomerId(customerId);
entity.setWarehouseId(warehouseId);
entity.setLocationId(locationId);
entity.setPalletCode(palletCode);
entity.setPalletType(request.palletType());
entity.setCapacityCbm(request.capacityCbm());
entity.setUnitPrice(request.unitPrice());
entity.setStatus(request.status());
entity.setRemark(request.remark());
return entity;
}
private static String generatedPalletCode(String warehouseCode, long palletId) {
return "PLT-" + warehouseCode + "-" + LocalDate.now().format(DateTimeFormatter.BASIC_ISO_DATE)
+ "-" + Long.toUnsignedString(palletId);
}
private static String defaultLocationCode(String warehouseCode) {
return "LOC-" + warehouseCode + "-DEFAULT";
}
}