fix(dispatch): publish PC date and daily sequence numbering
This commit is contained in:
+22
@@ -0,0 +1,22 @@
|
||||
package com.baiyue.lastleg.dispatch.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;
|
||||
import java.time.LocalDate;
|
||||
|
||||
/** 排车日期流水的原子分配与连接级结果读取,必须加入同一个短事务。 */
|
||||
@Mapper
|
||||
public interface DispatchNumberSequenceMapper {
|
||||
/** 日期主键串行递增,同时在当前数据库连接记住本次实际分配值。 */
|
||||
@Insert("INSERT INTO ll_dispatch_daily_sequence(business_date,last_sequence) VALUES(#{date},LAST_INSERT_ID(1)) "
|
||||
+ "ON DUPLICATE KEY UPDATE last_sequence=LAST_INSERT_ID(last_sequence+1)")
|
||||
int allocate(@Param("date") LocalDate date);
|
||||
|
||||
/** 连接级值不能使用会话或二级查询缓存,须读取本事务刚刚分配的值。 */
|
||||
@Select("SELECT LAST_INSERT_ID()")
|
||||
@Options(useCache=false,flushCache=Options.FlushCachePolicy.TRUE)
|
||||
Long allocatedValue();
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
package com.baiyue.lastleg.dispatch.repository;
|
||||
|
||||
import com.baiyue.common.core.api.ResultCode;
|
||||
import com.baiyue.common.core.exception.BizException;
|
||||
import com.baiyue.lastleg.dispatch.mapper.DispatchNumberSequenceMapper;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import org.springframework.transaction.annotation.Propagation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import java.time.LocalDate;
|
||||
|
||||
/** 整车单号独立日流水;独立短事务归还序列锁后才进入车辆、司机及货物锁。 */
|
||||
@Repository
|
||||
public class DispatchNumberSequence {
|
||||
private final DispatchNumberSequenceMapper mapper;
|
||||
|
||||
/** 流水与排车主单使用相同服务数据库。 */
|
||||
public DispatchNumberSequence(DispatchNumberSequenceMapper mapper) { this.mapper = mapper; }
|
||||
|
||||
/** 唯一日期行原子递增,连接内取回本次值;失败不复用已分配的业务号。 */
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW)
|
||||
public long next(LocalDate date) {
|
||||
// 两个实际 Mapper 操作加入本次 REQUIRES_NEW,连接级结果不跨连接也不读缓存。
|
||||
mapper.allocate(date);
|
||||
Long value = mapper.allocatedValue();
|
||||
if (value == null || value < 1) throw new IllegalStateException("排车日流水分配失败");
|
||||
if (value > 9999) throw new BizException(ResultCode.CONFLICT, "当日排车单四位流水号已用满");
|
||||
return value;
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -32,6 +32,7 @@ import static com.baiyue.lastleg.dispatch.port.DispatchContracts.*;
|
||||
/** 真整车创建、加货与查询;客户只属于明细,不属于整车头。 */
|
||||
@Service
|
||||
public class DispatchAggregateService implements DispatchExecutionPort,DispatchAppointmentAmendmentPort {
|
||||
@jakarta.annotation.Resource private DispatchNumberGenerator numbers;
|
||||
@jakarta.annotation.Resource private DispatchAppointmentAllocations appointments;
|
||||
@jakarta.annotation.Resource private DispatchPhysicalClaimService physicalClaims;
|
||||
@jakarta.annotation.Resource private DispatchItemSourceService relabelSources;
|
||||
@@ -269,7 +270,7 @@ public class DispatchAggregateService implements DispatchExecutionPort,DispatchA
|
||||
var replay = commands.replay(actor,null,identity);
|
||||
if (replay.isPresent()) return replay.get();
|
||||
long id=IdWorker.getId();
|
||||
String number="DS-"+UUID.randomUUID().toString().replace("-","").toUpperCase(Locale.ROOT);
|
||||
String number=numbers.next();
|
||||
var shell=fleet.createDispatchShell(actor.operatorId(),new FleetExecutionModels.DispatchShellRequest(id,number,
|
||||
Long.parseLong(request.warehouseId()),request.idempotencyKey(),identity.commandHash(),request.plannedDepartureAt(),
|
||||
fleetVehicle(request.vehicle()),fleetDriver(request.driver()),request.remark()));
|
||||
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
package com.baiyue.lastleg.dispatch.service;
|
||||
|
||||
import com.baiyue.lastleg.dispatch.repository.DispatchNumberSequence;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
import java.time.Clock;
|
||||
import java.time.LocalDate;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Locale;
|
||||
|
||||
/** 平台排车单号:PC、北京时间日期与每日独立四位流水。 */
|
||||
@Component
|
||||
public class DispatchNumberGenerator {
|
||||
private static final ZoneId BUSINESS_ZONE = ZoneId.of("Asia/Shanghai");
|
||||
private final DispatchNumberSequence sequences;
|
||||
private final Clock clock;
|
||||
|
||||
/** 正式运行按统一业务时区取当天日期。 */
|
||||
@Autowired
|
||||
public DispatchNumberGenerator(DispatchNumberSequence sequences) { this(sequences, Clock.systemUTC()); }
|
||||
|
||||
/** 固定时钟用于核实跨午夜归零边界。 */
|
||||
DispatchNumberGenerator(DispatchNumberSequence sequences, Clock clock) { this.sequences = sequences; this.clock = clock; }
|
||||
|
||||
/** 已有命令重放由上层账本恢复原号,本方法只用于首次建单。 */
|
||||
public String next() {
|
||||
LocalDate date = LocalDate.now(clock.withZone(BUSINESS_ZONE));
|
||||
long value = sequences.next(date);
|
||||
if (value < 1 || value > 9999) throw new IllegalStateException("排车日流水不在四位范围内");
|
||||
return "PC" + date.format(DateTimeFormatter.BASIC_ISO_DATE) + String.format(Locale.ROOT, "%04d", value);
|
||||
}
|
||||
}
|
||||
@@ -41,6 +41,12 @@ CREATE TABLE IF NOT EXISTS ll_order_no_daily_sequence (
|
||||
PRIMARY KEY(business_date)
|
||||
) ENGINE=InnoDB COMMENT='尾程单号按日原子流水';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS ll_dispatch_daily_sequence (
|
||||
business_date DATE NOT NULL COMMENT '排车编号业务日期(Asia/Shanghai)',
|
||||
last_sequence INT NOT NULL COMMENT '当日已分配排车流水号,范围0001至9999',
|
||||
PRIMARY KEY(business_date)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='排车单号每日独立原子流水';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS ll_order (
|
||||
id BIGINT NOT NULL COMMENT '雪花 ID',
|
||||
order_no VARCHAR(32) NOT NULL COMMENT '尾程单号',
|
||||
|
||||
Reference in New Issue
Block a user