This commit is contained in:
2026-10-20 23:49:55 +08:00
parent 35a1b835fe
commit af5cf1e3bd
37 changed files with 714 additions and 184 deletions
@@ -0,0 +1,21 @@
package com.baiyue.order.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 ord_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();
}
@@ -0,0 +1,24 @@
package com.baiyue.order.repository;
import com.baiyue.order.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;
}
}
@@ -44,6 +44,8 @@ import java.time.format.DateTimeFormatter;
@Service
public class OrderApplicationService {
@jakarta.annotation.Resource private OrderDocumentNumberGenerator documentNumbers;
private static final ZoneId INBOUND_CODE_ZONE = ZoneId.of("Asia/Shanghai");
private static final DateTimeFormatter INBOUND_CODE_DATE = DateTimeFormatter.ofPattern("yyMMdd");
@Resource private OrderRepository repository;
@@ -83,6 +85,7 @@ public class OrderApplicationService {
return orderId;
}
/** 创建主订单时分配独立日流水,入仓码与后续拆单规则保持不变。 */
private Long createDraftInternal(long tenantId, long userId, String username,
OrderRequests.SaveRequest request, boolean allowLegacyInboundCode) {
AddressSnapshots addressSnapshots = resolveCustomerAddresses(tenantId, request);
@@ -94,7 +97,7 @@ public class OrderApplicationService {
request.destinationType());
OrderEntity order = new OrderEntity();
long orderId = IdWorker.getId();
order.setId(orderId); order.setTenantId(tenantId); order.setOrderNo(nextNo(orderId));
order.setId(orderId); order.setTenantId(tenantId); order.setOrderNo(documentNumbers.next("FL"));
order.setStatus(OrderStatus.DRAFT);
apply(order, tenantId, request, snapshots, addressSnapshots, allowLegacyInboundCode);
order.setCreatedBy(userId); order.setUpdatedBy(userId);
@@ -984,7 +987,4 @@ public class OrderApplicationService {
private static String normalize(String value) {
return StringUtils.hasText(value) ? value.trim() : null;
}
private String nextNo(long orderId) {
return "FL" + orderId;
}
}
@@ -0,0 +1,34 @@
package com.baiyue.order.service;
import com.baiyue.common.core.number.DailyDocumentNumber;
import com.baiyue.order.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 OrderDocumentNumberGenerator {
private static final Set<String> PREFIXES = Set.of("FL");
private final DocumentSequenceRepository sequences;
private final Clock clock;
/** 正式运行使用统一业务时区。 */
@Autowired
public OrderDocumentNumberGenerator(DocumentSequenceRepository sequences) {
this(sequences, Clock.system(DailyDocumentNumber.BUSINESS_ZONE));
}
/** 固定时钟用于跨日及前缀独立性验证。 */
OrderDocumentNumberGenerator(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);
}
}