优化
This commit is contained in:
+3
-1
@@ -53,7 +53,9 @@ public class InternalInboundOrderController {
|
||||
throw new BizException(ResultCode.FORBIDDEN, "内部服务凭证无效");
|
||||
}
|
||||
if (command.tenantId() <= 0 || command.orderId() <= 0 || command.warehouseId() == null
|
||||
|| command.warehouseId() <= 0 || command.customerName() == null || command.customerName().isBlank()) {
|
||||
|| command.warehouseId() <= 0 || command.customerName() == null || command.customerName().isBlank()
|
||||
|| command.inboundCode() == null || command.inboundCode().isBlank()
|
||||
|| command.inboundCode().length() > 40) {
|
||||
throw new BizException(ResultCode.BAD_REQUEST, "订单入库快照不完整");
|
||||
}
|
||||
return ApiResponse.success(service.createFromApprovedOrder(command));
|
||||
|
||||
@@ -7,6 +7,7 @@ public record OrderApprovedInboundCommand(
|
||||
long tenantId,
|
||||
long orderId,
|
||||
String orderNo,
|
||||
String inboundCode,
|
||||
Long customerId,
|
||||
String customerName,
|
||||
Long warehouseId,
|
||||
|
||||
@@ -38,7 +38,7 @@ public class InboundOrderCreationService {
|
||||
inbound.setTenantId(command.tenantId());
|
||||
inbound.setOrderId(command.orderId());
|
||||
inbound.setOrderNo(command.orderNo());
|
||||
inbound.setInboundCode("RK-" + command.orderId());
|
||||
inbound.setInboundCode(command.inboundCode());
|
||||
inbound.setProcessStatus("WAIT_MEASURE");
|
||||
inbound.setOrderStatusSnapshot("APPROVED");
|
||||
inbound.setMeasureReviewStatus("NOT_SUBMITTED");
|
||||
|
||||
@@ -6,6 +6,19 @@ info:
|
||||
security:
|
||||
- bearerAuth: []
|
||||
paths:
|
||||
/internal/firstleg/v1/inbounds/order-approved:
|
||||
post:
|
||||
summary: 订单审核通过后幂等创建入库单
|
||||
description: 仅供订单服务调用;入仓码由订单提交时生成,Firstleg 必须原样沿用。承运商可在后续履约或计费前补充。
|
||||
security:
|
||||
- internalServiceToken: []
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema: {$ref: '#/components/schemas/OrderApprovedInboundCommand'}
|
||||
responses:
|
||||
'200': {description: 成功或业务失败;相同客户和订单重放返回已有入库单 ID}
|
||||
/api/platform/v1/first-leg/tasks:
|
||||
get:
|
||||
summary: 分页查询履约任务
|
||||
@@ -224,6 +237,10 @@ components:
|
||||
in: header
|
||||
name: X-Signature
|
||||
description: HMAC-SHA256 小写十六进制签名
|
||||
internalServiceToken:
|
||||
type: apiKey
|
||||
in: header
|
||||
name: X-Internal-Service-Token
|
||||
parameters:
|
||||
XTimestamp:
|
||||
name: X-Timestamp
|
||||
@@ -259,6 +276,36 @@ components:
|
||||
in: query
|
||||
schema: {type: integer, format: int64, minimum: 1, maximum: 200, default: 20}
|
||||
schemas:
|
||||
OrderApprovedInboundCommand:
|
||||
type: object
|
||||
required: [tenantId, orderId, inboundCode, customerName, warehouseId]
|
||||
properties:
|
||||
tenantId: {type: string, pattern: '^[1-9][0-9]*$', description: 当前客户隔离 ID}
|
||||
orderId: {type: string, pattern: '^[1-9][0-9]*$'}
|
||||
orderNo: {type: string, nullable: true}
|
||||
inboundCode: {type: string, minLength: 1, maxLength: 40, example: RK-2085308606829346818}
|
||||
customerId: {type: string, pattern: '^[1-9][0-9]*$', nullable: true}
|
||||
customerName: {type: string, minLength: 1}
|
||||
warehouseId: {type: string, pattern: '^[1-9][0-9]*$'}
|
||||
warehouseName: {type: string, nullable: true}
|
||||
deliveryWarehouseId: {type: string, pattern: '^[1-9][0-9]*$', nullable: true}
|
||||
deliveryWarehouseName: {type: string, nullable: true}
|
||||
expectedArrivalAt: {type: string, format: date-time, nullable: true}
|
||||
businessType: {type: string, nullable: true}
|
||||
channelCode: {type: string, nullable: true}
|
||||
channelName: {type: string, nullable: true}
|
||||
carrierId: {type: string, pattern: '^[1-9][0-9]*$', nullable: true, description: 可选,可在后续履约或计费前补充}
|
||||
carrierName: {type: string, nullable: true}
|
||||
taxMode: {type: string, nullable: true}
|
||||
containerMode: {type: string, nullable: true}
|
||||
customsInspection: {type: boolean, nullable: true}
|
||||
destinationType: {type: string, nullable: true}
|
||||
destinationWarehouseId: {type: string, pattern: '^[1-9][0-9]*$', nullable: true}
|
||||
destinationCode: {type: string, nullable: true}
|
||||
destinationName: {type: string, nullable: true}
|
||||
publicRemark: {type: string, nullable: true}
|
||||
expressCompanyName: {type: string, nullable: true}
|
||||
expressTrackingNo: {type: string, nullable: true}
|
||||
MerchantInboundBillingEnvelope:
|
||||
type: object
|
||||
properties:
|
||||
|
||||
+25
@@ -2,6 +2,7 @@ package com.baiyue.firstleg.controller;
|
||||
|
||||
import com.baiyue.common.core.exception.BizException;
|
||||
import com.baiyue.firstleg.dto.InboundOrderReference;
|
||||
import com.baiyue.firstleg.dto.OrderApprovedInboundCommand;
|
||||
import com.baiyue.firstleg.service.InboundOrderCreationService;
|
||||
import com.baiyue.firstleg.service.UnifiedInboundQueryService;
|
||||
import com.baiyue.firstleg.dto.UnifiedInboundQuery;
|
||||
@@ -63,4 +64,28 @@ class InternalInboundOrderReferenceControllerTest {
|
||||
.isInstanceOf(BizException.class);
|
||||
verify(unifiedQueryService).page(request);
|
||||
}
|
||||
|
||||
@Test
|
||||
void approvedOrderRequiresInboundCode() {
|
||||
assertThatThrownBy(() -> controller.createFromApprovedOrder(
|
||||
"internal-token", approvedCommand(" ", null)))
|
||||
.isInstanceOf(BizException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void approvedOrderAllowsMissingCarrier() {
|
||||
OrderApprovedInboundCommand command = approvedCommand("RK-77", null);
|
||||
when(service.createFromApprovedOrder(command)).thenReturn(88L);
|
||||
|
||||
assertThat(controller.createFromApprovedOrder("internal-token", command).data()).isEqualTo(88L);
|
||||
verify(service).createFromApprovedOrder(command);
|
||||
}
|
||||
|
||||
private static OrderApprovedInboundCommand approvedCommand(String inboundCode, Long carrierId) {
|
||||
return new OrderApprovedInboundCommand(
|
||||
10001L, 77L, "FL-77", inboundCode, 7L, "测试客户", 9L, "义乌仓",
|
||||
null, null, null, "FBA", "AIR", "空运", carrierId,
|
||||
carrierId == null ? null : "承运商", null, null, null, null,
|
||||
null, null, null, null, null, null);
|
||||
}
|
||||
}
|
||||
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
package com.baiyue.firstleg.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.Wrapper;
|
||||
import com.baiyue.firstleg.dto.OrderApprovedInboundCommand;
|
||||
import com.baiyue.firstleg.entity.InboundOrderEntity;
|
||||
import com.baiyue.firstleg.entity.InboundStatusHistoryEntity;
|
||||
import com.baiyue.firstleg.mapper.InboundOrderMapper;
|
||||
import com.baiyue.firstleg.mapper.InboundStatusHistoryMapper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class InboundOrderCreationServiceTest {
|
||||
@Mock
|
||||
private InboundOrderMapper orders;
|
||||
@Mock
|
||||
private InboundStatusHistoryMapper histories;
|
||||
@InjectMocks
|
||||
private InboundOrderCreationService service;
|
||||
|
||||
@Test
|
||||
void shouldUseSubmittedInboundCodeAndKeepReplayIdempotentWithoutCarrier() {
|
||||
OrderApprovedInboundCommand command = approvedCommand("RK-CUSTOM-77");
|
||||
InboundOrderEntity existing = new InboundOrderEntity();
|
||||
existing.setId(900L);
|
||||
existing.setTenantId(10001L);
|
||||
existing.setOrderId(77L);
|
||||
existing.setInboundCode("RK-CUSTOM-77");
|
||||
when(orders.selectOne(any(Wrapper.class))).thenReturn(null, existing);
|
||||
|
||||
long createdId = service.createFromApprovedOrder(command);
|
||||
long replayedId = service.createFromApprovedOrder(command);
|
||||
|
||||
ArgumentCaptor<InboundOrderEntity> inboundCaptor = ArgumentCaptor.forClass(InboundOrderEntity.class);
|
||||
verify(orders, times(1)).insert(inboundCaptor.capture());
|
||||
verify(histories, times(1)).insert(any(InboundStatusHistoryEntity.class));
|
||||
assertThat(inboundCaptor.getValue().getInboundCode()).isEqualTo("RK-CUSTOM-77");
|
||||
assertThat(inboundCaptor.getValue().getCarrierId()).isNull();
|
||||
assertThat(createdId).isEqualTo(inboundCaptor.getValue().getId());
|
||||
assertThat(replayedId).isEqualTo(900L);
|
||||
}
|
||||
|
||||
private static OrderApprovedInboundCommand approvedCommand(String inboundCode) {
|
||||
return new OrderApprovedInboundCommand(
|
||||
10001L, 77L, "FL-77", inboundCode, 7L, "测试客户", 9L, "义乌仓",
|
||||
null, null, null, "FBA", "AIR", "空运", null, null,
|
||||
null, null, null, null, null, null, null, null, null, null);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user