优化
This commit is contained in:
@@ -10,8 +10,10 @@ import org.springframework.web.bind.annotation.RequestHeader;
|
||||
url = "${baiyue.clients.firstleg.url:}", path = "/internal/firstleg/v1/pricing")
|
||||
public interface FirstlegPricingClient {
|
||||
@PostMapping("/channels/validate")
|
||||
ApiResponse<Void> validateChannel(@RequestHeader("X-Internal-Service-Token") String token,
|
||||
@RequestBody ChannelValidationRequest request);
|
||||
ApiResponse<ChannelValidationResponse> validateChannel(@RequestHeader("X-Internal-Service-Token") String token,
|
||||
@RequestBody ChannelValidationRequest request);
|
||||
|
||||
record ChannelValidationRequest(long tenantId, long originWarehouseId, String channelCode) {}
|
||||
record ChannelValidationRequest(long tenantId, long originWarehouseId, String channelCode,
|
||||
String destinationType) {}
|
||||
record ChannelValidationResponse(String channelCode, String inboundCodePrefix) {}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.baiyue.order.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/** 全平台入仓码票序;自增主键保证并发创建时不会重复。 */
|
||||
@Getter
|
||||
@Setter
|
||||
@TableName("ord_inbound_code_sequence")
|
||||
public class InboundCodeSequenceEntity {
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.baiyue.order.mapper;
|
||||
|
||||
import com.baiyue.order.entity.InboundCodeSequenceEntity;
|
||||
import org.apache.ibatis.annotations.Insert;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Options;
|
||||
|
||||
@Mapper
|
||||
public interface InboundCodeSequenceMapper {
|
||||
@Insert("INSERT INTO ord_inbound_code_sequence (created_at) VALUES (CURRENT_TIMESTAMP(3))")
|
||||
@Options(useGeneratedKeys = true, keyProperty = "id")
|
||||
int allocate(InboundCodeSequenceEntity value);
|
||||
}
|
||||
@@ -33,6 +33,15 @@ public class OrderRepository {
|
||||
@Resource private OutboxEventMapper outboxMapper;
|
||||
@Resource private OrderChildPurgeMapper childPurgeMapper;
|
||||
@Resource private OrderIdempotencyRecordMapper idempotencyMapper;
|
||||
@Resource private InboundCodeSequenceMapper inboundCodeSequenceMapper;
|
||||
|
||||
public long nextInboundCodeSequence() {
|
||||
InboundCodeSequenceEntity value = new InboundCodeSequenceEntity();
|
||||
if (inboundCodeSequenceMapper.allocate(value) != 1 || value.getId() == null) {
|
||||
throw new IllegalStateException("Failed to allocate inbound code sequence");
|
||||
}
|
||||
return value.getId();
|
||||
}
|
||||
|
||||
public void insertOrder(OrderEntity order) { orderMapper.insert(order); }
|
||||
public int saveOrder(long tenantId, OrderEntity order) {
|
||||
|
||||
@@ -33,9 +33,12 @@ import java.time.*;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
||||
@Service
|
||||
public class OrderApplicationService {
|
||||
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;
|
||||
@Resource private ObjectMapper json;
|
||||
@Resource private OrderEligibilityGuard eligibilityGuard;
|
||||
@@ -61,12 +64,15 @@ public class OrderApplicationService {
|
||||
private Long createDraftInternal(long tenantId, long userId, OrderRequests.SaveRequest request) {
|
||||
productImageGuard.requireOwned(tenantId, request.cargoes());
|
||||
var snapshots = masterDataGuard.validate(tenantId, request);
|
||||
pricingChannelGuard.requireAvailable(tenantId, request.receivingWarehouseId(), request.channelCode());
|
||||
String inboundCodePrefix = pricingChannelGuard.requireAvailable(tenantId, request.receivingWarehouseId(),
|
||||
request.channelCode(), request.destinationType());
|
||||
OrderEntity order = new OrderEntity();
|
||||
long orderId = IdWorker.getId();
|
||||
order.setId(orderId); order.setTenantId(tenantId); order.setOrderNo(nextNo(orderId));
|
||||
order.setInboundCode("RK-" + orderId); order.setStatus(OrderStatus.DRAFT);
|
||||
apply(order, request, snapshots); order.setCreatedBy(userId); order.setUpdatedBy(userId);
|
||||
order.setStatus(OrderStatus.DRAFT);
|
||||
apply(order, request, snapshots);
|
||||
order.setInboundCode(nextInboundCode(inboundCodePrefix));
|
||||
order.setCreatedBy(userId); order.setUpdatedBy(userId);
|
||||
repository.insertOrder(order);
|
||||
saveChildren(tenantId, userId, order.getId(), request);
|
||||
record(tenantId, order.getId(), null, OrderStatus.DRAFT, "CREATE", null, userId, null);
|
||||
@@ -100,11 +106,9 @@ public class OrderApplicationService {
|
||||
&& !(allowSubmitted && before == OrderStatus.SUBMITTED)) {
|
||||
throw new BizException(ResultCode.CONFLICT, "当前订单状态不允许编辑订单资料");
|
||||
}
|
||||
pricingChannelGuard.requireAvailable(tenantId, request.receivingWarehouseId(), request.channelCode());
|
||||
pricingChannelGuard.requireAvailable(tenantId, request.receivingWarehouseId(), request.channelCode(),
|
||||
request.destinationType());
|
||||
apply(order, request, snapshots); order.setStatus(OrderStatus.DRAFT); order.setUpdatedBy(userId);
|
||||
if (before == OrderStatus.REJECTED && !StringUtils.hasText(order.getInboundCode())) {
|
||||
order.setInboundCode("RK-" + order.getId());
|
||||
}
|
||||
if (before == OrderStatus.SUBMITTED) {
|
||||
order.setStatus(OrderStatus.SUBMITTED);
|
||||
}
|
||||
@@ -122,23 +126,22 @@ public class OrderApplicationService {
|
||||
OrderStatus from = order.getStatus();
|
||||
if (!allowed(from, target)) throw new BizException(com.baiyue.common.core.api.ResultCode.CONFLICT,
|
||||
from == target ? "该操作已完成,请勿重复提交" : "订单状态不允许当前操作: " + from + " -> " + target);
|
||||
String inboundCodePrefix = null;
|
||||
if (target == OrderStatus.SUBMITTED) {
|
||||
validatePersistedPrototypeFields(tenantId, order);
|
||||
masterDataGuard.validatePersisted(tenantId, order.getCustomerId(), order.getReceivingWarehouseId(),
|
||||
order.getDestinationWarehouseId(), order.getDestinationType());
|
||||
pricingChannelGuard.requireAvailable(tenantId, order.getReceivingWarehouseId(), order.getChannelCode());
|
||||
inboundCodePrefix = pricingChannelGuard.requireAvailable(tenantId, order.getReceivingWarehouseId(),
|
||||
order.getChannelCode(), order.getDestinationType());
|
||||
}
|
||||
if ((target == OrderStatus.REJECTED || target == OrderStatus.CANCELLED) && !StringUtils.hasText(reason)) {
|
||||
throw new BizException("Reason is required");
|
||||
}
|
||||
order.setStatus(target); order.setUpdatedBy(userId);
|
||||
if (target == OrderStatus.SUBMITTED) {
|
||||
if (!StringUtils.hasText(order.getInboundCode())) order.setInboundCode("RK-" + order.getId());
|
||||
if (!StringUtils.hasText(order.getInboundCode())) order.setInboundCode(nextInboundCode(inboundCodePrefix));
|
||||
order.setSubmittedAt(LocalDateTime.now());
|
||||
}
|
||||
if (target == OrderStatus.DRAFT && !StringUtils.hasText(order.getInboundCode())) {
|
||||
order.setInboundCode("RK-" + order.getId());
|
||||
}
|
||||
if (target == OrderStatus.COMPLETED) order.setCompletedAt(LocalDateTime.now());
|
||||
if (repository.saveOrder(tenantId, order) != 1) throw new BizException("Concurrent order update");
|
||||
record(tenantId, id, from, target, target.name(), reason, userId, username);
|
||||
@@ -159,6 +162,20 @@ public class OrderApplicationService {
|
||||
|
||||
public PageResult<OrderEntity> page(Long tenantId, OrderRequests.QueryRequest query) { return repository.page(tenantId, query); }
|
||||
|
||||
String nextInboundCode(String prefix) {
|
||||
return formatInboundCode(prefix, LocalDate.now(INBOUND_CODE_ZONE), repository.nextInboundCodeSequence());
|
||||
}
|
||||
|
||||
static String formatInboundCode(String prefix, LocalDate date, long sequence) {
|
||||
if (!StringUtils.hasText(prefix) || !prefix.matches("[A-Z]{4,6}")) {
|
||||
throw new BizException(ResultCode.CONFLICT, "渠道入仓码前缀配置无效");
|
||||
}
|
||||
if (date == null || sequence < 1) {
|
||||
throw new BizException(ResultCode.CONFLICT, "入仓码票序生成失败");
|
||||
}
|
||||
return prefix + date.format(INBOUND_CODE_DATE) + String.format(Locale.ROOT, "%04d", sequence);
|
||||
}
|
||||
|
||||
public List<TenantOrderCountResponse> countByTenantIds(List<Long> tenantIds) {
|
||||
var counts = repository.countByTenantIds(tenantIds);
|
||||
return tenantIds.stream().distinct()
|
||||
|
||||
@@ -14,10 +14,12 @@ public class OrderPricingChannelGuard {
|
||||
@Resource private FirstlegPricingClient client;
|
||||
@Value("${baiyue.security.internal-service-token}") private String internalToken;
|
||||
|
||||
public void requireAvailable(long tenantId, long originWarehouseId, String channelCode) {
|
||||
public String requireAvailable(long tenantId, long originWarehouseId, String channelCode,
|
||||
String destinationType) {
|
||||
try {
|
||||
ApiResponse<Void> response = client.validateChannel(internalToken,
|
||||
new FirstlegPricingClient.ChannelValidationRequest(tenantId, originWarehouseId, channelCode));
|
||||
ApiResponse<FirstlegPricingClient.ChannelValidationResponse> response = client.validateChannel(internalToken,
|
||||
new FirstlegPricingClient.ChannelValidationRequest(
|
||||
tenantId, originWarehouseId, channelCode, destinationType));
|
||||
if (response == null) {
|
||||
throw unavailable();
|
||||
}
|
||||
@@ -27,6 +29,11 @@ public class OrderPricingChannelGuard {
|
||||
? code.message() : response.message();
|
||||
throw new BizException(code, message);
|
||||
}
|
||||
if (response.data() == null || response.data().inboundCodePrefix() == null
|
||||
|| response.data().inboundCodePrefix().isBlank()) {
|
||||
throw new BizException(ResultCode.CONFLICT, "所选渠道未配置入仓码前缀");
|
||||
}
|
||||
return response.data().inboundCodePrefix();
|
||||
} catch (BizException exception) {
|
||||
throw exception;
|
||||
} catch (FeignException exception) {
|
||||
|
||||
@@ -9,7 +9,7 @@ CREATE TABLE IF NOT EXISTS ord_order (
|
||||
id BIGINT UNSIGNED NOT NULL COMMENT '订单ID(雪花ID)',
|
||||
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '客户ID和数据隔离键',
|
||||
order_no VARCHAR(32) NOT NULL COMMENT '客户范围内唯一订单号',
|
||||
inbound_code VARCHAR(40) NULL COMMENT '客户提交后生成的入仓码,草稿为空',
|
||||
inbound_code VARCHAR(40) NOT NULL COMMENT '订单创建时生成并固定的入仓码:物流前缀+YYMMDD+全局票序',
|
||||
customer_id BIGINT UNSIGNED NULL COMMENT '客户逻辑ID',
|
||||
customer_name VARCHAR(128) NOT NULL COMMENT '下单时客户名称快照',
|
||||
status VARCHAR(24) NOT NULL DEFAULT 'DRAFT' COMMENT '订单业务状态:DRAFT草稿、SUBMITTED已提交、APPROVED审核通过、ACCEPTED已接单、REJECTED审核驳回、PROCESSING履约中、COMPLETED已完成、CANCELLED已取消',
|
||||
@@ -60,6 +60,12 @@ CREATE TABLE IF NOT EXISTS ord_order (
|
||||
KEY idx_order_tenant_warehouse (tenant_id, destination_warehouse_id)
|
||||
) ENGINE=InnoDB COMMENT='头程订单主表:保存商户委托、目的仓、运输方式和订单当前状态';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS ord_inbound_code_sequence (
|
||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '全平台入仓码票序',
|
||||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '票序分配时间',
|
||||
PRIMARY KEY (id)
|
||||
) ENGINE=InnoDB COMMENT='入仓码全局票序;不同客户和物流渠道共用且不按日期清零';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS ord_order_fulfillment_split (
|
||||
id BIGINT UNSIGNED NOT NULL COMMENT '拆分台账ID',
|
||||
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '客户ID和数据隔离键',
|
||||
|
||||
@@ -11,7 +11,7 @@ paths:
|
||||
summary: 平台统一订单列表
|
||||
description: >-
|
||||
统一返回七态产品主状态、下一步动作及测量/预装/装柜/派送摘要,需要 platform:order:list。
|
||||
草稿不生成 inboundCode,客户提交进入待审核后即返回固定 inboundCode;有入库单时同时返回
|
||||
订单创建时按物流渠道生成固定 inboundCode,格式为物流前缀+YYMMDD+全局票序;有入库单时同时返回
|
||||
inboundId、inboundVersion、loadingVersion、pickingVersion;inboundVersion
|
||||
才是 C04 loading/complete 的 expectedVersion,loadingVersion 当前等于 pickingVersion(兼容别名)。
|
||||
tenantId 省略时跨全部客户;mainStatus 省略时按订单创建时间分页,指定 PENDING_REVIEW 时由 Order 查询,
|
||||
@@ -52,7 +52,7 @@ paths:
|
||||
/api/platform/v1/orders/{orderId}:
|
||||
get:
|
||||
summary: 平台统一订单详情
|
||||
description: 合并 Order 与 Firstleg 时间线并按发生时间升序返回;待审核订单直接返回 Order 在提交时生成的 inboundCode;平台可见内部备注。
|
||||
description: 合并 Order 与 Firstleg 时间线并按发生时间升序返回;所有状态直接返回 Order 创建时生成的 inboundCode;平台可见内部备注。
|
||||
parameters:
|
||||
- $ref: '#/components/parameters/UnifiedOrderId'
|
||||
- $ref: '#/components/parameters/UnifiedTenantId'
|
||||
@@ -620,7 +620,7 @@ components:
|
||||
success: {type: boolean}
|
||||
orderId: {type: string, pattern: '^[1-9][0-9]*$'}
|
||||
orderNo: {type: string}
|
||||
inboundCode: {type: string, pattern: '^RK-[A-Za-z0-9-]+$'}
|
||||
inboundCode: {type: string, pattern: '^[A-Z]{4,6}[0-9]{10,}$', example: ZBYHK2608140897}
|
||||
errorCode: {type: string}
|
||||
errorMessage: {type: string}
|
||||
FirstLegOrderCargo:
|
||||
@@ -721,7 +721,7 @@ components:
|
||||
type: string
|
||||
enum: [PENDING_REVIEW, WAIT_MEASURE, INBOUNDED, LOADING, LOADED, DELIVERED, WAIT_SPLIT]
|
||||
UnifiedInboundCode:
|
||||
{name: inboundCode, in: query, required: false, description: 客户提交后生成的入仓码,待审核订单同样可查询, schema: {type: string, maxLength: 40}}
|
||||
{name: inboundCode, in: query, required: false, description: 订单创建时生成的入仓码,所有订单状态均可查询, schema: {type: string, maxLength: 40}}
|
||||
UnifiedInternalOrderStatus:
|
||||
name: internalOrderStatus
|
||||
in: query
|
||||
|
||||
@@ -22,6 +22,7 @@ import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalTime;
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
@@ -57,7 +58,7 @@ class OrderApplicationServiceStateTest {
|
||||
assertThat(orderId).isNotNull();
|
||||
verify(eligibility, never()).requireApproved(anyLong());
|
||||
verify(repository).insertOrder(argThat(order -> order.getStatus() == OrderStatus.DRAFT
|
||||
&& ("RK-" + order.getId()).equals(order.getInboundCode())));
|
||||
&& order.getInboundCode().matches("PCHWK\\d{6}0897")));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -105,13 +106,14 @@ class OrderApplicationServiceStateTest {
|
||||
OrderRequests.SaveRequest request = request();
|
||||
when(masterData.validate(10001L, request)).thenReturn(snapshots());
|
||||
when(repository.completeIdempotency(anyLong(), any(), anyLong())).thenReturn(1);
|
||||
when(pricing.requireAvailable(10001L, 8L, "CH", "OVERSEAS")).thenReturn("PCHWK");
|
||||
OrderApplicationService service = service(repository, mock(OrderEligibilityGuard.class), masterData);
|
||||
ReflectionTestUtils.setField(service, "pricingChannelGuard", pricing);
|
||||
|
||||
service.createDraft(10001L, 2L, request, "validate-channel-on-create");
|
||||
|
||||
var ordered = inOrder(pricing, repository);
|
||||
ordered.verify(pricing).requireAvailable(10001L, 8L, "CH");
|
||||
ordered.verify(pricing).requireAvailable(10001L, 8L, "CH", "OVERSEAS");
|
||||
ordered.verify(repository).insertOrder(argThat(order ->
|
||||
"CH".equals(order.getChannelCode())
|
||||
&& "TAX_INCLUDED".equals(order.getPricingTaxMode())));
|
||||
@@ -133,12 +135,13 @@ class OrderApplicationServiceStateTest {
|
||||
source.amazonWarehouseCode(), source.shipmentId(), source.referenceId());
|
||||
when(masterData.validate(10001L, request)).thenReturn(snapshots());
|
||||
when(repository.completeIdempotency(anyLong(), any(), anyLong())).thenReturn(1);
|
||||
when(pricing.requireAvailable(10001L, 8L, " ch ", "OVERSEAS")).thenReturn("PCHWK");
|
||||
OrderApplicationService service = service(repository, mock(OrderEligibilityGuard.class), masterData);
|
||||
ReflectionTestUtils.setField(service, "pricingChannelGuard", pricing);
|
||||
|
||||
service.createDraft(10001L, 2L, request, "canonical-channel-code");
|
||||
|
||||
verify(pricing).requireAvailable(10001L, 8L, " ch ");
|
||||
verify(pricing).requireAvailable(10001L, 8L, " ch ", "OVERSEAS");
|
||||
verify(repository).insertOrder(argThat(order ->
|
||||
"CH".equals(order.getChannelCode())
|
||||
&& "BUY_DECLARATION".equals(order.getTaxMode())
|
||||
@@ -182,7 +185,7 @@ class OrderApplicationServiceStateTest {
|
||||
service.updateDraft(10001L, 2L, 1L, request);
|
||||
|
||||
var ordered = inOrder(pricing, repository);
|
||||
ordered.verify(pricing).requireAvailable(10001L, 8L, "CH");
|
||||
ordered.verify(pricing).requireAvailable(10001L, 8L, "CH", "OVERSEAS");
|
||||
ordered.verify(repository).saveOrder(10001L, order);
|
||||
}
|
||||
|
||||
@@ -244,7 +247,7 @@ class OrderApplicationServiceStateTest {
|
||||
.transit(20002L, 2L, "u", 1L, OrderStatus.SUBMITTED, null);
|
||||
|
||||
assertThat(order.getStatus()).isEqualTo(OrderStatus.SUBMITTED);
|
||||
assertThat(order.getInboundCode()).isEqualTo("RK-1");
|
||||
assertThat(order.getInboundCode()).matches("PCHWK\\d{6}0897");
|
||||
assertThat(order.getSubmittedAt()).isNotNull();
|
||||
verify(repository).saveOrder(20002L, order);
|
||||
}
|
||||
@@ -274,7 +277,7 @@ class OrderApplicationServiceStateTest {
|
||||
when(repository.findCargo(20002L, 1L)).thenReturn(List.of(completeCargo()));
|
||||
when(repository.findParties(20002L, 1L)).thenReturn(List.of());
|
||||
doThrow(new BizException(ResultCode.CONFLICT, "所选渠道已下线,请重新选择"))
|
||||
.when(pricing).requireAvailable(20002L, 8L, "CH");
|
||||
.when(pricing).requireAvailable(20002L, 8L, "CH", "OVERSEAS");
|
||||
OrderApplicationService service = service(repository, mock(OrderEligibilityGuard.class),
|
||||
mock(OrderMasterDataGuard.class));
|
||||
ReflectionTestUtils.setField(service, "pricingChannelGuard", pricing);
|
||||
@@ -486,13 +489,24 @@ class OrderApplicationServiceStateTest {
|
||||
verify(repository, never()).saveOrder(anyLong(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void inboundCodeUsesChannelPrefixShanghaiDateAndGlobalTicketSequence() {
|
||||
assertThat(OrderApplicationService.formatInboundCode("ZBYHK", LocalDate.of(2026, 8, 14), 897L))
|
||||
.isEqualTo("ZBYHK2608140897");
|
||||
assertThat(OrderApplicationService.formatInboundCode("HDHK", LocalDate.of(2026, 8, 13), 10000L))
|
||||
.isEqualTo("HDHK26081310000");
|
||||
}
|
||||
|
||||
private OrderApplicationService service(OrderRepository repository, OrderEligibilityGuard eligibility,
|
||||
OrderMasterDataGuard masterData) {
|
||||
OrderApplicationService service = springService;
|
||||
ReflectionTestUtils.setField(service, "repository", repository);
|
||||
ReflectionTestUtils.setField(service, "eligibilityGuard", eligibility);
|
||||
ReflectionTestUtils.setField(service, "masterDataGuard", masterData);
|
||||
ReflectionTestUtils.setField(service, "pricingChannelGuard", mock(OrderPricingChannelGuard.class));
|
||||
OrderPricingChannelGuard pricing = mock(OrderPricingChannelGuard.class);
|
||||
when(pricing.requireAvailable(anyLong(), anyLong(), anyString(), anyString())).thenReturn("PCHWK");
|
||||
when(repository.nextInboundCodeSequence()).thenReturn(897L);
|
||||
ReflectionTestUtils.setField(service, "pricingChannelGuard", pricing);
|
||||
ReflectionTestUtils.setField(service, "productImageGuard", mock(ProductImageGuard.class));
|
||||
ReflectionTestUtils.setField(service, "json", new ObjectMapper());
|
||||
return service;
|
||||
|
||||
@@ -18,6 +18,7 @@ import java.nio.charset.StandardCharsets;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@@ -30,21 +31,22 @@ class OrderPricingChannelGuardTest {
|
||||
|
||||
@Test
|
||||
void forwardsCustomerOriginAndChannelToFirstleg() {
|
||||
var request = new FirstlegPricingClient.ChannelValidationRequest(20002L, 8L, "MATSON_TRUCK");
|
||||
when(client.validateChannel("token", request)).thenReturn(ApiResponse.success(null));
|
||||
var request = new FirstlegPricingClient.ChannelValidationRequest(20002L, 8L, "MATSON_TRUCK", "FBA");
|
||||
when(client.validateChannel("token", request)).thenReturn(ApiResponse.success(
|
||||
new FirstlegPricingClient.ChannelValidationResponse("MATSON_TRUCK", "ZBYHK")));
|
||||
|
||||
guard.requireAvailable(20002L, 8L, "MATSON_TRUCK");
|
||||
assertThat(guard.requireAvailable(20002L, 8L, "MATSON_TRUCK", "FBA")).isEqualTo("ZBYHK");
|
||||
|
||||
verify(client).validateChannel("token", request);
|
||||
}
|
||||
|
||||
@Test
|
||||
void removedOrForgedChannelKeepsFirstlegBusinessError() {
|
||||
var request = new FirstlegPricingClient.ChannelValidationRequest(20002L, 8L, "FAKE");
|
||||
var request = new FirstlegPricingClient.ChannelValidationRequest(20002L, 8L, "FAKE", "FBA");
|
||||
when(client.validateChannel("token", request)).thenReturn(
|
||||
ApiResponse.failure(ResultCode.CONFLICT, "所选渠道已下线,请重新选择", null, null));
|
||||
|
||||
assertThatThrownBy(() -> guard.requireAvailable(20002L, 8L, "FAKE"))
|
||||
assertThatThrownBy(() -> guard.requireAvailable(20002L, 8L, "FAKE", "FBA"))
|
||||
.isInstanceOf(BizException.class)
|
||||
.hasMessage("所选渠道已下线,请重新选择")
|
||||
.extracting(exception -> ((BizException) exception).getResultCode())
|
||||
@@ -53,18 +55,18 @@ class OrderPricingChannelGuardTest {
|
||||
|
||||
@Test
|
||||
void nullResponseMapsToServiceUnavailable() {
|
||||
var request = new FirstlegPricingClient.ChannelValidationRequest(20002L, 8L, "MATSON_TRUCK");
|
||||
var request = new FirstlegPricingClient.ChannelValidationRequest(20002L, 8L, "MATSON_TRUCK", "FBA");
|
||||
when(client.validateChannel("token", request)).thenReturn(null);
|
||||
|
||||
assertUnavailable(() -> guard.requireAvailable(20002L, 8L, "MATSON_TRUCK"));
|
||||
assertUnavailable(() -> guard.requireAvailable(20002L, 8L, "MATSON_TRUCK", "FBA"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void downstreamHttpFailureMapsToServiceUnavailable() {
|
||||
var request = new FirstlegPricingClient.ChannelValidationRequest(20002L, 8L, "MATSON_TRUCK");
|
||||
var request = new FirstlegPricingClient.ChannelValidationRequest(20002L, 8L, "MATSON_TRUCK", "FBA");
|
||||
when(client.validateChannel("token", request)).thenThrow(feign(403));
|
||||
|
||||
assertUnavailable(() -> guard.requireAvailable(20002L, 8L, "MATSON_TRUCK"));
|
||||
assertUnavailable(() -> guard.requireAvailable(20002L, 8L, "MATSON_TRUCK", "FBA"));
|
||||
}
|
||||
|
||||
private void assertUnavailable(org.assertj.core.api.ThrowableAssert.ThrowingCallable invocation) {
|
||||
|
||||
Reference in New Issue
Block a user