优化
This commit is contained in:
+1
-1
@@ -36,7 +36,7 @@ public class InternalPriceBookController {
|
||||
@Valid @RequestBody PriceBookChannelValidation.Request request) {
|
||||
requireToken(token);
|
||||
return ApiResponse.success(service.requireAvailableChannel(request.tenantId(), request.originWarehouseId(),
|
||||
request.channelCode(), request.destinationType()));
|
||||
request.channelCode(), request.destinationType(), request.requireInboundCodePrefix()));
|
||||
}
|
||||
|
||||
private void requireToken(String token) {
|
||||
|
||||
@@ -12,7 +12,8 @@ public final class PriceBookChannelValidation {
|
||||
@Min(1) long tenantId,
|
||||
@Min(1) long originWarehouseId,
|
||||
@NotBlank @Size(max = 64) String channelCode,
|
||||
@NotBlank @Pattern(regexp = "FBA|OVERSEAS|PRIVATE") String destinationType
|
||||
@NotBlank @Pattern(regexp = "FBA|OVERSEAS|PRIVATE") String destinationType,
|
||||
boolean requireInboundCodePrefix
|
||||
) {}
|
||||
|
||||
public record Response(String channelCode, String inboundCodePrefix) {}
|
||||
|
||||
@@ -49,7 +49,7 @@ public class MerchantPriceBookService {
|
||||
}
|
||||
|
||||
public Response requireAvailableChannel(long merchantTenantId, long originWarehouseId, String channelCode,
|
||||
String destinationType) {
|
||||
String destinationType, boolean requireInboundCodePrefix) {
|
||||
requireMerchant(merchantTenantId);
|
||||
if (originWarehouseId <= 0) throw validation("请选择有效起运仓");
|
||||
Long versionId = repository.activeVersionId();
|
||||
@@ -57,6 +57,9 @@ public class MerchantPriceBookService {
|
||||
if (versionId == null || !repository.channelAvailable(versionId, originWarehouseId, normalized)) {
|
||||
throw new BizException(ResultCode.CONFLICT, "所选渠道已下线,请重新选择");
|
||||
}
|
||||
if (!requireInboundCodePrefix) {
|
||||
return new Response(normalized, null);
|
||||
}
|
||||
var prefix = repository.channelPrefix(normalized);
|
||||
if (prefix == null || !StringUtils.hasText(prefix.inboundCodePrefix())) {
|
||||
throw new BizException(ResultCode.CONFLICT, "所选渠道未配置进仓编码");
|
||||
|
||||
@@ -10,7 +10,7 @@ CREATE TABLE IF NOT EXISTS fl_inbound_order (
|
||||
task_id BIGINT UNSIGNED NULL COMMENT '头程履约任务ID',
|
||||
order_id BIGINT UNSIGNED NOT NULL COMMENT '订单服务订单ID,不建立跨库外键',
|
||||
order_no VARCHAR(32) NOT NULL COMMENT '订单号快照',
|
||||
inbound_code VARCHAR(40) NOT NULL COMMENT '客户内唯一入仓码',
|
||||
inbound_code VARCHAR(40) NOT NULL COMMENT '全平台唯一入仓码',
|
||||
process_status VARCHAR(24) NOT NULL DEFAULT 'WAIT_MEASURE' COMMENT '入库流程状态:WAIT_MEASURE待测量、MEASURING测量中、MEASURE_PENDING待审核、MEASURE_REJECTED测量驳回、WAIT_PRICING待计费、FEE_CONFIRMING费用确认中、INBOUNDING入库收口中、WAIT_SPLIT待分柜',
|
||||
order_status_snapshot VARCHAR(24) NOT NULL COMMENT '订单状态快照',
|
||||
measure_review_status VARCHAR(24) NOT NULL DEFAULT 'NOT_SUBMITTED' COMMENT '测量审核状态:NOT_SUBMITTED未提交、PENDING待审核、APPROVED已通过、REJECTED已驳回',
|
||||
@@ -63,7 +63,7 @@ CREATE TABLE IF NOT EXISTS fl_inbound_order (
|
||||
deleted TINYINT(1) NOT NULL DEFAULT 0 COMMENT '逻辑删除标记',
|
||||
version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY uk_inbound_tenant_code (tenant_id, inbound_code),
|
||||
UNIQUE KEY uk_inbound_code (inbound_code),
|
||||
UNIQUE KEY uk_inbound_tenant_order (tenant_id, order_id),
|
||||
KEY idx_inbound_process_time (tenant_id, process_status, created_at, id),
|
||||
KEY idx_inbound_measure_time (tenant_id, measure_review_status, created_at, id),
|
||||
@@ -77,6 +77,33 @@ CREATE TABLE IF NOT EXISTS fl_inbound_order (
|
||||
CONSTRAINT fk_inbound_task FOREIGN KEY (task_id) REFERENCES fl_fulfillment_task (id)
|
||||
) ENGINE=InnoDB COMMENT='二期头程入库单聚合根和列表查询快照';
|
||||
|
||||
-- 既有开发库若存在跨客户重复码则严格失败,禁止自动覆盖或改写入仓码。
|
||||
SET @inbound_global_code_index_sql = IF(
|
||||
NOT EXISTS(
|
||||
SELECT 1 FROM information_schema.statistics
|
||||
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_inbound_order'
|
||||
AND index_name = 'uk_inbound_code'
|
||||
),
|
||||
'ALTER TABLE baiyue_firstleg.fl_inbound_order ADD UNIQUE KEY uk_inbound_code (inbound_code)',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE inbound_global_code_index_stmt FROM @inbound_global_code_index_sql;
|
||||
EXECUTE inbound_global_code_index_stmt;
|
||||
DEALLOCATE PREPARE inbound_global_code_index_stmt;
|
||||
|
||||
SET @inbound_tenant_code_index_drop_sql = IF(
|
||||
EXISTS(
|
||||
SELECT 1 FROM information_schema.statistics
|
||||
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_inbound_order'
|
||||
AND index_name = 'uk_inbound_tenant_code'
|
||||
),
|
||||
'ALTER TABLE baiyue_firstleg.fl_inbound_order DROP INDEX uk_inbound_tenant_code',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE inbound_tenant_code_index_drop_stmt FROM @inbound_tenant_code_index_drop_sql;
|
||||
EXECUTE inbound_tenant_code_index_drop_stmt;
|
||||
DEALLOCATE PREPARE inbound_tenant_code_index_drop_stmt;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS fl_inbound_pickup_snapshot (
|
||||
id BIGINT UNSIGNED NOT NULL COMMENT '提货信息快照ID',
|
||||
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '客户ID和数据隔离键',
|
||||
|
||||
@@ -51,8 +51,8 @@ paths:
|
||||
'200': {description: 幂等补偿完成}
|
||||
/internal/firstleg/v1/pricing/channels/validate:
|
||||
post:
|
||||
summary: 校验订单渠道并返回进仓编码前缀
|
||||
description: 仅供订单服务调用;前缀按渠道和目的仓类型选择,同一渠道的包税与不包税订单共用编码前缀。
|
||||
summary: 校验订单渠道并按需返回进仓编码前缀
|
||||
description: 仅供订单服务调用;AUTO 模式要求并返回编码前缀,LEGACY_MANUAL 模式仅校验渠道可用性。
|
||||
security:
|
||||
- internalServiceToken: []
|
||||
requestBody:
|
||||
@@ -61,14 +61,15 @@ paths:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
required: [tenantId, originWarehouseId, channelCode, destinationType]
|
||||
required: [tenantId, originWarehouseId, channelCode, destinationType, requireInboundCodePrefix]
|
||||
properties:
|
||||
tenantId: {type: string, pattern: '^[1-9][0-9]*$'}
|
||||
originWarehouseId: {type: string, pattern: '^[1-9][0-9]*$'}
|
||||
channelCode: {type: string, minLength: 1, maxLength: 64}
|
||||
destinationType: {type: string, enum: [FBA, OVERSEAS, PRIVATE]}
|
||||
requireInboundCodePrefix: {type: boolean}
|
||||
responses:
|
||||
'200': {description: 成功时 data 返回 channelCode 和 inboundCodePrefix;未配置对应进仓编码时返回业务冲突}
|
||||
'200': {description: 成功时 data 返回 channelCode;requireInboundCodePrefix=true 时同时返回 inboundCodePrefix}
|
||||
/api/platform/v1/first-leg/customer-dimension-config:
|
||||
get:
|
||||
summary: 查询客户固定四箱规尺寸配置
|
||||
|
||||
@@ -120,7 +120,7 @@ class PriceBookContractTest {
|
||||
.getContentAsString(StandardCharsets.UTF_8);
|
||||
assertThat(firstlegOpenapi)
|
||||
.contains("/internal/firstleg/v1/pricing/channels/validate:")
|
||||
.contains("required: [tenantId, originWarehouseId, channelCode, destinationType]")
|
||||
.contains("同一渠道的包税与不包税订单共用编码前缀");
|
||||
.contains("required: [tenantId, originWarehouseId, channelCode, destinationType, requireInboundCodePrefix]")
|
||||
.contains("LEGACY_MANUAL 模式仅校验渠道可用性");
|
||||
}
|
||||
}
|
||||
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package com.baiyue.firstleg.repository;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class InboundCodeGlobalUniquenessContractTest {
|
||||
@Test
|
||||
void inboundSchemaUsesTheSamePlatformWideInboundCodeIndex() throws Exception {
|
||||
String sql = new ClassPathResource("db/phase2-inbound-schema.sql")
|
||||
.getContentAsString(StandardCharsets.UTF_8);
|
||||
|
||||
assertThat(sql)
|
||||
.contains("UNIQUE KEY uk_inbound_code (inbound_code)")
|
||||
.doesNotContain("UNIQUE KEY uk_inbound_tenant_code (tenant_id, inbound_code)");
|
||||
}
|
||||
}
|
||||
+15
-3
@@ -51,9 +51,9 @@ class MerchantPriceBookServiceTest {
|
||||
when(repository.channelPrefix("MATSON_CLX_TRUCK"))
|
||||
.thenReturn(new ChannelPrefixRow("MATSON_CLX_TRUCK", "ZBYHK", "ZBYHWK"));
|
||||
|
||||
assertThat(service.requireAvailableChannel(10001L, 10L, "matson_clx_truck", "FBA")
|
||||
assertThat(service.requireAvailableChannel(10001L, 10L, "matson_clx_truck", "FBA", true)
|
||||
.inboundCodePrefix()).isEqualTo("ZBYHK");
|
||||
assertThat(service.requireAvailableChannel(10001L, 10L, "MATSON_CLX_TRUCK", "OVERSEAS")
|
||||
assertThat(service.requireAvailableChannel(10001L, 10L, "MATSON_CLX_TRUCK", "OVERSEAS", true)
|
||||
.inboundCodePrefix()).isEqualTo("ZBYHWK");
|
||||
}
|
||||
|
||||
@@ -66,10 +66,22 @@ class MerchantPriceBookServiceTest {
|
||||
when(repository.channelPrefix("OA_HOUSTON_TRUCK"))
|
||||
.thenReturn(new ChannelPrefixRow("OA_HOUSTON_TRUCK", "HPHK", null));
|
||||
|
||||
assertThat(service.requireAvailableChannel(10001L, 10L, "OA_HOUSTON_TRUCK", "OVERSEAS")
|
||||
assertThat(service.requireAvailableChannel(10001L, 10L, "OA_HOUSTON_TRUCK", "OVERSEAS", true)
|
||||
.inboundCodePrefix()).isEqualTo("HPHK");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldValidateLegacyChannelWithoutRequiringAnInboundCodePrefix() {
|
||||
when(repository.activeVersionId()).thenReturn(7L);
|
||||
when(repository.channelAvailable(7L, 10L, "LEGACY_CHANNEL")).thenReturn(true);
|
||||
var response = service.requireAvailableChannel(
|
||||
10001L, 10L, "LEGACY_CHANNEL", "FBA", false);
|
||||
|
||||
assertThat(response.channelCode()).isEqualTo("LEGACY_CHANNEL");
|
||||
assertThat(response.inboundCodePrefix()).isNull();
|
||||
verify(repository, never()).channelPrefix("LEGACY_CHANNEL");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnEmptyChannelsWithoutActiveVersion() {
|
||||
when(repository.activeVersionId()).thenReturn(null);
|
||||
|
||||
Reference in New Issue
Block a user