优化
This commit is contained in:
+4
-3
@@ -24,6 +24,8 @@ import java.util.List;
|
||||
|
||||
@Service
|
||||
public class DomesticContainerService {
|
||||
@jakarta.annotation.Resource private com.baiyue.warehouse.service.WarehouseDocumentNumberGenerator documentNumbers;
|
||||
|
||||
private static final long PLATFORM_TENANT = 0L;
|
||||
@Resource private DomesticContainerRepository repository;
|
||||
@Resource private WarehouseRepository warehouseRepository;
|
||||
@@ -57,7 +59,7 @@ public class DomesticContainerService {
|
||||
String container = normalize(request.containerNo());
|
||||
DomesticContainerOrderEntity order = new DomesticContainerOrderEntity();
|
||||
order.setId(IdWorker.getId()); order.setTenantId(PLATFORM_TENANT); order.setBusinessType("INBOUND");
|
||||
order.setOrderNo(nextNo("ZGRK")); order.setWarehouseId(warehouse.getId()); order.setWarehouseName(warehouse.getWarehouseName());
|
||||
order.setOrderNo(documentNumbers.next("ZGRK")); order.setWarehouseId(warehouse.getId()); order.setWarehouseName(warehouse.getWarehouseName());
|
||||
order.setContainerNo(container); order.setCargoName(request.cargoName().trim()); order.setBoxCount(request.boxCount());
|
||||
order.setVolumeCbm(request.volumeCbm()); order.setOperationTime(request.inboundTime()); order.setStatus(request.status()); order.setRemark(request.remark());
|
||||
if ("COMPLETED".equals(request.status())) {
|
||||
@@ -86,7 +88,7 @@ public class DomesticContainerService {
|
||||
if (request.boxCount() > inventory.getAvailableBoxes()) throw new BizException(ResultCode.CONFLICT, "出库箱数超过可用库存");
|
||||
}
|
||||
DomesticContainerOrderEntity order = new DomesticContainerOrderEntity(); order.setId(IdWorker.getId()); order.setTenantId(PLATFORM_TENANT); order.setBusinessType("OUTBOUND");
|
||||
order.setOrderNo(nextNo("ZGCK")); order.setWarehouseId(warehouse.getId()); order.setWarehouseName(warehouse.getWarehouseName()); order.setContainerNo(inventory.getContainerNo());
|
||||
order.setOrderNo(documentNumbers.next("ZGCK")); order.setWarehouseId(warehouse.getId()); order.setWarehouseName(warehouse.getWarehouseName()); order.setContainerNo(inventory.getContainerNo());
|
||||
order.setCargoName(inventory.getCargoName()); order.setBoxCount(request.boxCount()); order.setVolumeCbm(BigDecimal.ZERO); order.setOperationTime(request.outboundTime()); order.setStatus(request.status()); order.setRemark(request.remark());
|
||||
repository.insertOrder(order);
|
||||
if ("COMPLETED".equals(request.status())) { inventory.setOutboundBoxes(inventory.getOutboundBoxes()+order.getBoxCount()); inventory.setAvailableBoxes(inventory.getAvailableBoxes()-order.getBoxCount()); inventory.setInventoryStatus(status(inventory)); requireUpdated(repository.updateInventory(inventory)); transaction(inventory, order, "OUTBOUND"); }
|
||||
@@ -107,7 +109,6 @@ public class DomesticContainerService {
|
||||
private void requireUpdated(int count) { if (count != 1) throw new BizException(ResultCode.CONFLICT, "库存已被并发修改,请刷新后重试"); }
|
||||
private void requireClaimCompleted(int count) { if (count != 1) throw new BizException(ResultCode.CONFLICT, "幂等操作状态已变化,请使用原键重试"); }
|
||||
private DomesticContainerDtos.CreateResult claim(String type, String key, String hash) { try { if (repository.claim(IdWorker.getId(), type, key, hash) == 1) return null; } catch (DuplicateKeyException ignored) {} DomesticContainerIdempotencyEntity e = repository.findClaim(type, key); if (e == null || !hash.equals(e.getRequestHash())) throw new BizException(ResultCode.CONFLICT, "幂等键已用于其他请求"); if ("COMPLETED".equals(e.getOperationStatus())) return new DomesticContainerDtos.CreateResult(e.getResourceId(), e.getResourceNo(), true); throw new BizException(ResultCode.CONFLICT, "幂等请求正在处理中"); }
|
||||
private String nextNo(String prefix) { return prefix + java.time.format.DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS").format(LocalDateTime.now()) + IdWorker.getIdStr().substring(10); }
|
||||
private static String normalize(String s) { return s.trim().toUpperCase(java.util.Locale.ROOT); }
|
||||
private static String hash(String value) { try { return HexFormat.of().formatHex(MessageDigest.getInstance("SHA-256").digest(value.getBytes(StandardCharsets.UTF_8))); } catch (Exception e) { throw new IllegalStateException(e); } }
|
||||
private PageResult<DomesticContainerDtos.OrderView> mapOrders(PageResult<DomesticContainerOrderEntity> p) { return new PageResult<>(p.records().stream().map(o -> new DomesticContainerDtos.OrderView(o.getId(),o.getBusinessType(),o.getOrderNo(),o.getWarehouseId(),o.getWarehouseName(),o.getContainerNo(),o.getCargoName(),o.getBoxCount(),o.getVolumeCbm(),o.getOperationTime(),o.getStatus(),o.getRemark(),o.getVersion(),o.getCreatedAt())).toList(),p.page(),p.size(),p.total(),p.pages()); }
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.baiyue.warehouse.mapper;
|
||||
|
||||
import org.apache.ibatis.annotations.Insert;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Options;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
|
||||
/** 本服务业务编号序列,更新与读取必须处于同一事务连接。 */
|
||||
@Mapper
|
||||
public interface DocumentSequenceMapper {
|
||||
/** 唯一序列键原子递增,并在连接内保存本次分配值。 */
|
||||
@Insert("INSERT INTO wh_document_sequence(sequence_key,current_value) VALUES(#{key},LAST_INSERT_ID(1)) "
|
||||
+ "ON DUPLICATE KEY UPDATE current_value=LAST_INSERT_ID(current_value+1)")
|
||||
int allocate(@Param("key") String key);
|
||||
|
||||
/** 禁用一级和二级缓存,返回本事务刚刚分配的值。 */
|
||||
@Select("SELECT LAST_INSERT_ID()")
|
||||
@Options(useCache=false, flushCache=Options.FlushCachePolicy.TRUE)
|
||||
Long allocatedValue();
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package com.baiyue.warehouse.repository;
|
||||
|
||||
import com.baiyue.warehouse.mapper.DocumentSequenceMapper;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import org.springframework.transaction.annotation.Propagation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/** 各业务共用本库序列,独立短事务避免持有编号锁进入业务资源锁。 */
|
||||
@Repository
|
||||
public class DocumentSequenceRepository {
|
||||
private final DocumentSequenceMapper mapper;
|
||||
|
||||
/** 只访问所属服务的编号表。 */
|
||||
public DocumentSequenceRepository(DocumentSequenceMapper mapper) { this.mapper = mapper; }
|
||||
|
||||
/** 已分配号不因外层业务回滚而复用,允许失败请求产生断号。 */
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW)
|
||||
public long next(String key) {
|
||||
mapper.allocate(key);
|
||||
Long value = mapper.allocatedValue();
|
||||
if (value == null || value < 1) throw new IllegalStateException("业务编号分配失败");
|
||||
return value;
|
||||
}
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
package com.baiyue.warehouse.service;
|
||||
|
||||
import com.baiyue.common.core.number.DailyDocumentNumber;
|
||||
import com.baiyue.warehouse.repository.DocumentSequenceRepository;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
import java.time.Clock;
|
||||
import java.util.Set;
|
||||
|
||||
/** 按北京时间生成两至四位前缀、八位日期和四位日流水。 */
|
||||
@Component
|
||||
public class WarehouseDocumentNumberGenerator {
|
||||
private static final Set<String> PREFIXES = Set.of("ZGRK", "ZGCK");
|
||||
private final DocumentSequenceRepository sequences;
|
||||
private final Clock clock;
|
||||
|
||||
/** 正式运行使用统一业务时区。 */
|
||||
@Autowired
|
||||
public WarehouseDocumentNumberGenerator(DocumentSequenceRepository sequences) {
|
||||
this(sequences, Clock.system(DailyDocumentNumber.BUSINESS_ZONE));
|
||||
}
|
||||
|
||||
/** 固定时钟用于跨日及前缀独立性验证。 */
|
||||
WarehouseDocumentNumberGenerator(DocumentSequenceRepository sequences, Clock clock) {
|
||||
this.sequences = sequences;
|
||||
this.clock = clock;
|
||||
}
|
||||
|
||||
/** 只为本服务已登记的业务类型分配号码。 */
|
||||
public String next(String prefix) {
|
||||
if (!PREFIXES.contains(prefix)) throw new IllegalArgumentException("未登记的业务编号前缀:" + prefix);
|
||||
return DailyDocumentNumber.next(prefix, clock, sequences::next);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user