优化
This commit is contained in:
@@ -64,6 +64,58 @@ paths:
|
||||
schema: {$ref: '#/components/schemas/OrderApprovedInboundCommand'}
|
||||
responses:
|
||||
'200': {description: 成功或业务失败;相同客户和订单重放返回已有入库单 ID}
|
||||
/internal/firstleg/v1/inbounds/unified-orders/query:
|
||||
post:
|
||||
summary: 按统一订单条件分页查询履约摘要(Order 内部)
|
||||
description: >-
|
||||
mainStatus 接受六态产品状态;历史 WAIT_SPLIT 仅作为兼容筛选值,按 INBOUNDED 范围查询。
|
||||
所有响应 mainStatus 均归并为六态产品状态,不返回 WAIT_SPLIT。
|
||||
security:
|
||||
- internalServiceToken: []
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema: {$ref: '#/components/schemas/UnifiedInboundQuery'}
|
||||
responses:
|
||||
'200':
|
||||
description: 成功或业务失败,按响应体 code 判断
|
||||
content:
|
||||
application/json:
|
||||
schema: {$ref: '#/components/schemas/UnifiedInboundPageEnvelope'}
|
||||
/internal/firstleg/v1/inbounds/unified-orders/summaries:
|
||||
post:
|
||||
summary: 按订单 ID 批量查询统一履约摘要(Order 内部)
|
||||
security:
|
||||
- internalServiceToken: []
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema: {$ref: '#/components/schemas/UnifiedInboundBatchRequest'}
|
||||
responses:
|
||||
'200':
|
||||
description: 成功或业务失败,按响应体 code 判断
|
||||
content:
|
||||
application/json:
|
||||
schema: {$ref: '#/components/schemas/UnifiedInboundListEnvelope'}
|
||||
/internal/firstleg/v1/inbounds/unified-orders/detail:
|
||||
post:
|
||||
summary: 按订单 ID 查询统一履约详情(Order 内部)
|
||||
description: 时间线保留 Firstleg 内部 fromStatus/toStatus;summary.mainStatus 始终使用六态产品状态。
|
||||
security:
|
||||
- internalServiceToken: []
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema: {$ref: '#/components/schemas/UnifiedInboundDetailRequest'}
|
||||
responses:
|
||||
'200':
|
||||
description: 成功或业务失败;找不到对应入库单时 data 为 null
|
||||
content:
|
||||
application/json:
|
||||
schema: {$ref: '#/components/schemas/UnifiedInboundDetailEnvelope'}
|
||||
/api/platform/v1/first-leg/tasks:
|
||||
get:
|
||||
summary: 分页查询履约任务
|
||||
@@ -528,6 +580,198 @@ components:
|
||||
required: true
|
||||
schema: {type: string, pattern: '^[1-9][0-9]*$'}
|
||||
schemas:
|
||||
UnifiedMainStatus:
|
||||
type: string
|
||||
enum: [PENDING_REVIEW, WAIT_MEASURE, INBOUNDED, LOADING, LOADED, DELIVERED]
|
||||
description: 产品主状态;历史内部状态 WAIT_SPLIT 在响应中统一归并为 INBOUNDED
|
||||
UnifiedInboundQuery:
|
||||
type: object
|
||||
properties:
|
||||
tenantId: {type: integer, format: int64, nullable: true}
|
||||
keyword: {type: string, maxLength: 64, nullable: true}
|
||||
orderNo: {type: string, maxLength: 64, nullable: true}
|
||||
mainStatus:
|
||||
type: string
|
||||
enum: [PENDING_REVIEW, WAIT_MEASURE, INBOUNDED, LOADING, LOADED, DELIVERED, WAIT_SPLIT]
|
||||
nullable: true
|
||||
description: WAIT_SPLIT 仅为历史兼容筛选值,等价于 INBOUNDED
|
||||
customerId: {type: integer, format: int64, nullable: true}
|
||||
receivingWarehouseId: {type: integer, format: int64, nullable: true}
|
||||
destinationWarehouseId: {type: integer, format: int64, nullable: true}
|
||||
inboundCode: {type: string, maxLength: 64, nullable: true}
|
||||
internalOrderStatus: {type: string, maxLength: 32, nullable: true}
|
||||
expectedArrivalDate: {type: string, format: date, nullable: true}
|
||||
feeReviewStatus: {type: string, maxLength: 32, nullable: true}
|
||||
channelName: {type: string, maxLength: 128, nullable: true}
|
||||
containerNo: {type: string, maxLength: 64, nullable: true}
|
||||
cabinetNo: {type: string, maxLength: 64, nullable: true}
|
||||
sailingDate: {type: string, format: date, nullable: true}
|
||||
loaded: {type: boolean, nullable: true}
|
||||
fullContainer: {type: boolean, nullable: true}
|
||||
measurementReviewStatus: {type: string, maxLength: 32, nullable: true}
|
||||
candidateOrderIds: {type: array, nullable: true, items: {type: integer, format: int64}}
|
||||
createdFrom: {type: string, format: date-time, nullable: true}
|
||||
createdTo: {type: string, format: date-time, nullable: true}
|
||||
page: {type: integer, format: int64, minimum: 1, default: 1}
|
||||
size: {type: integer, format: int64, minimum: 1, maximum: 200, default: 20}
|
||||
UnifiedInboundBatchRequest:
|
||||
type: object
|
||||
required: [orderIds]
|
||||
properties:
|
||||
tenantId: {type: integer, format: int64, nullable: true}
|
||||
orderIds:
|
||||
type: array
|
||||
minItems: 1
|
||||
maxItems: 200
|
||||
items: {type: integer, format: int64, minimum: 1}
|
||||
UnifiedInboundDetailRequest:
|
||||
type: object
|
||||
required: [tenantId, orderId]
|
||||
properties:
|
||||
tenantId: {type: integer, format: int64, minimum: 1}
|
||||
orderId: {type: integer, format: int64, minimum: 1}
|
||||
UnifiedInboundPageEnvelope:
|
||||
type: object
|
||||
properties:
|
||||
code: {type: string, example: '200'}
|
||||
message: {type: string}
|
||||
data:
|
||||
type: object
|
||||
properties:
|
||||
records: {type: array, items: {$ref: '#/components/schemas/UnifiedInboundSummary'}}
|
||||
page: {type: integer, format: int64, minimum: 1}
|
||||
size: {type: integer, format: int64, minimum: 1}
|
||||
total: {type: integer, format: int64, minimum: 0}
|
||||
pages: {type: integer, format: int64, minimum: 0}
|
||||
UnifiedInboundListEnvelope:
|
||||
type: object
|
||||
properties:
|
||||
code: {type: string, example: '200'}
|
||||
message: {type: string}
|
||||
data: {type: array, items: {$ref: '#/components/schemas/UnifiedInboundSummary'}}
|
||||
UnifiedInboundDetailEnvelope:
|
||||
type: object
|
||||
properties:
|
||||
code: {type: string, example: '200'}
|
||||
message: {type: string}
|
||||
data:
|
||||
allOf: [{$ref: '#/components/schemas/UnifiedInboundDetail'}]
|
||||
nullable: true
|
||||
UnifiedInboundDetail:
|
||||
type: object
|
||||
properties:
|
||||
summary: {$ref: '#/components/schemas/UnifiedInboundSummary'}
|
||||
timeline:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
source: {type: string}
|
||||
eventType: {type: string}
|
||||
fromStatus: {type: string, nullable: true}
|
||||
toStatus: {type: string, nullable: true}
|
||||
action: {type: string, nullable: true}
|
||||
remark: {type: string, nullable: true}
|
||||
operatorId: {type: integer, format: int64, nullable: true}
|
||||
operatorName: {type: string, nullable: true}
|
||||
occurredAt: {type: string, format: date-time, nullable: true}
|
||||
UnifiedInboundSummary:
|
||||
type: object
|
||||
properties:
|
||||
inbound: {$ref: '#/components/schemas/UnifiedInboundOrderSummary'}
|
||||
mainStatus: {$ref: '#/components/schemas/UnifiedMainStatus'}
|
||||
nextAction: {type: string, nullable: true}
|
||||
preload: {$ref: '#/components/schemas/UnifiedPreloadSummary'}
|
||||
loading: {$ref: '#/components/schemas/UnifiedLoadingSummary'}
|
||||
delivery: {$ref: '#/components/schemas/UnifiedDeliverySummary'}
|
||||
UnifiedInboundOrderSummary:
|
||||
type: object
|
||||
description: InboundOrderSummary 的内部统一订单投影
|
||||
properties:
|
||||
id: {type: integer, format: int64}
|
||||
tenantId: {type: integer, format: int64}
|
||||
orderId: {type: integer, format: int64, nullable: true}
|
||||
orderNo: {type: string, nullable: true}
|
||||
inboundCode: {type: string, nullable: true}
|
||||
processStatus: {type: string, nullable: true, description: Firstleg 内部状态,历史数据可能为 WAIT_SPLIT}
|
||||
orderStatusSnapshot: {type: string, nullable: true}
|
||||
measureReviewStatus: {type: string, nullable: true}
|
||||
feeStatus: {type: string, nullable: true}
|
||||
feeReviewStatus: {type: string, nullable: true}
|
||||
notificationStatus: {type: string, nullable: true}
|
||||
version: {type: integer, nullable: true}
|
||||
expectedArrivalAt: {type: string, format: date-time, nullable: true}
|
||||
customerId: {type: integer, format: int64, nullable: true}
|
||||
customerName: {type: string, nullable: true}
|
||||
companyId: {type: integer, format: int64, nullable: true}
|
||||
companyName: {type: string, nullable: true}
|
||||
warehouseId: {type: integer, format: int64, nullable: true}
|
||||
warehouseName: {type: string, nullable: true}
|
||||
deliveryWarehouseId: {type: integer, format: int64, nullable: true}
|
||||
deliveryWarehouseName: {type: string, nullable: true}
|
||||
businessType: {type: string, nullable: true}
|
||||
channelCode: {type: string, nullable: true}
|
||||
channelName: {type: string, nullable: true}
|
||||
taxMode: {type: string, nullable: true}
|
||||
pricingTaxMode: {type: string, nullable: true}
|
||||
containerMode: {type: string, nullable: true}
|
||||
customsInspection: {type: boolean, nullable: true}
|
||||
destinationType: {type: string, nullable: true}
|
||||
destinationWarehouseId: {type: integer, format: int64, nullable: true}
|
||||
destinationCode: {type: string, nullable: true}
|
||||
destinationName: {type: string, nullable: true}
|
||||
holdFlag: {type: boolean, nullable: true}
|
||||
currentHoldReason: {type: string, nullable: true}
|
||||
totalPackageCount: {type: integer, nullable: true}
|
||||
locationCodes: {type: string, nullable: true}
|
||||
actualGrossWeightKg: {type: number, nullable: true}
|
||||
actualNetWeightKg: {type: number, nullable: true}
|
||||
actualVolumeCbm: {type: number, nullable: true}
|
||||
volumeWeightKg: {type: number, nullable: true}
|
||||
chargeableWeightKg: {type: number, nullable: true}
|
||||
chargeableVolumeCbm: {type: number, nullable: true}
|
||||
palletCount: {type: integer, nullable: true}
|
||||
measureCompletedAt: {type: string, format: date-time, nullable: true}
|
||||
inboundedAt: {type: string, format: date-time, nullable: true}
|
||||
expressCompanyCode: {type: string, nullable: true}
|
||||
expressCompanyName: {type: string, nullable: true}
|
||||
loadingStatus: {type: string, nullable: true}
|
||||
containerNo: {type: string, nullable: true}
|
||||
cabinetNo: {type: string, nullable: true}
|
||||
vesselName: {type: string, nullable: true}
|
||||
voyageNo: {type: string, nullable: true}
|
||||
sailingAt: {type: string, format: date-time, nullable: true}
|
||||
portArrivalAt: {type: string, format: date-time, nullable: true}
|
||||
expressTrackingNo: {type: string, nullable: true}
|
||||
createdAt: {type: string, format: date-time, nullable: true}
|
||||
updatedAt: {type: string, format: date-time, nullable: true}
|
||||
UnifiedPreloadSummary:
|
||||
type: object
|
||||
nullable: true
|
||||
properties:
|
||||
status: {type: string, nullable: true}
|
||||
version: {type: integer, nullable: true}
|
||||
itemCount: {type: integer, nullable: true}
|
||||
updatedAt: {type: string, format: date-time, nullable: true}
|
||||
UnifiedLoadingSummary:
|
||||
type: object
|
||||
nullable: true
|
||||
properties:
|
||||
status: {type: string, nullable: true}
|
||||
loadingVersion: {type: integer, nullable: true, description: 当前为 pickingVersion 兼容别名}
|
||||
pickingVersion: {type: integer, nullable: true}
|
||||
expectedCount: {type: integer, nullable: true}
|
||||
pickedCount: {type: integer, nullable: true}
|
||||
evidenceCount: {type: integer, nullable: true}
|
||||
sealNo: {type: string, nullable: true}
|
||||
updatedAt: {type: string, format: date-time, nullable: true}
|
||||
UnifiedDeliverySummary:
|
||||
type: object
|
||||
nullable: true
|
||||
properties:
|
||||
status: {type: string, nullable: true}
|
||||
completedAt: {type: string, format: date-time, nullable: true}
|
||||
trackingNo: {type: string, nullable: true}
|
||||
MojiaMeasurementUploadEnvelope:
|
||||
type: object
|
||||
properties:
|
||||
|
||||
+3
-2
@@ -41,6 +41,8 @@ class InboundOrderControllerContractTest {
|
||||
String taskList = openapi.substring(
|
||||
openapi.indexOf("/api/platform/v1/first-leg/tasks:"),
|
||||
openapi.indexOf("/api/platform/v1/first-leg/tasks/{taskId}:"));
|
||||
assertThat(inboundList)
|
||||
.doesNotContain("WAIT_SPLIT");
|
||||
assertThat(openapi)
|
||||
.contains("/api/platform/inbounds:")
|
||||
.contains("/api/platform/inbounds/{inboundId}:")
|
||||
@@ -53,8 +55,7 @@ class InboundOrderControllerContractTest {
|
||||
.contains("tenantId: {type: string, pattern: '^[1-9][0-9]*$', description:")
|
||||
.contains("[WAIT_MEASURE, WAIT_REVIEW, WAIT_FEE_SEND, INBOUNDED]")
|
||||
.contains("platform:firstleg:inbound:list")
|
||||
.doesNotContain("WAIT_CUSTOMER_CONFIRM")
|
||||
.doesNotContain("WAIT_SPLIT");
|
||||
.doesNotContain("WAIT_CUSTOMER_CONFIRM");
|
||||
assertThat(inboundList).contains("#/components/schemas/InboundOrderPageEnvelope");
|
||||
assertThat(taskList).doesNotContain("InboundOrderPageEnvelope");
|
||||
}
|
||||
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
package com.baiyue.firstleg.controller;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class UnifiedInboundOpenApiContractTest {
|
||||
@Test
|
||||
void documentsInternalUnifiedOrderEndpointsAndLegacyWaitSplitMapping() throws Exception {
|
||||
String yaml = new ClassPathResource("openapi/firstleg-v1.yaml")
|
||||
.getContentAsString(StandardCharsets.UTF_8);
|
||||
Map<String, Object> document = stringMap(new Yaml().load(yaml));
|
||||
Map<String, Object> paths = stringMap(document.get("paths"));
|
||||
assertThat(paths).containsKeys(
|
||||
"/internal/firstleg/v1/inbounds/unified-orders/query",
|
||||
"/internal/firstleg/v1/inbounds/unified-orders/summaries",
|
||||
"/internal/firstleg/v1/inbounds/unified-orders/detail");
|
||||
|
||||
Map<String, Object> schemas = stringMap(stringMap(document.get("components")).get("schemas"));
|
||||
Map<String, Object> responseStatus = stringMap(schemas.get("UnifiedMainStatus"));
|
||||
assertThat(stringList(responseStatus.get("enum"))).containsExactly(
|
||||
"PENDING_REVIEW", "WAIT_MEASURE", "INBOUNDED", "LOADING", "LOADED", "DELIVERED");
|
||||
assertThat(responseStatus.get("description").toString()).contains("WAIT_SPLIT", "INBOUNDED");
|
||||
|
||||
Map<String, Object> query = stringMap(schemas.get("UnifiedInboundQuery"));
|
||||
Map<String, Object> queryStatus = stringMap(stringMap(query.get("properties")).get("mainStatus"));
|
||||
assertThat(stringList(queryStatus.get("enum"))).contains("WAIT_SPLIT");
|
||||
assertThat(queryStatus.get("description").toString()).contains("INBOUNDED");
|
||||
|
||||
Map<String, Object> inbound = stringMap(schemas.get("UnifiedInboundOrderSummary"));
|
||||
assertThat(stringMap(inbound.get("properties"))).containsKeys(
|
||||
"id", "tenantId", "orderId", "inboundCode", "processStatus", "version",
|
||||
"customerId", "warehouseId", "deliveryWarehouseId", "totalPackageCount",
|
||||
"businessType", "channelCode", "pricingTaxMode", "containerMode", "destinationType",
|
||||
"holdFlag", "actualGrossWeightKg", "actualNetWeightKg", "actualVolumeCbm",
|
||||
"volumeWeightKg", "chargeableWeightKg", "chargeableVolumeCbm", "palletCount",
|
||||
"inboundedAt", "expressCompanyCode", "expressCompanyName", "loadingStatus", "containerNo",
|
||||
"cabinetNo", "sailingAt", "portArrivalAt", "createdAt", "updatedAt");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static Map<String, Object> stringMap(Object value) {
|
||||
return (Map<String, Object>) value;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static List<String> stringList(Object value) {
|
||||
return (List<String>) value;
|
||||
}
|
||||
}
|
||||
@@ -43,7 +43,11 @@ paths:
|
||||
- $ref: '#/components/parameters/UnifiedPage'
|
||||
- $ref: '#/components/parameters/UnifiedSize'
|
||||
responses:
|
||||
'200': {description: 成功或业务失败,响应体 code 为字符串 200 时成功}
|
||||
'200':
|
||||
description: 成功或业务失败,响应体 code 为字符串 200 时成功
|
||||
content:
|
||||
application/json:
|
||||
schema: {$ref: '#/components/schemas/UnifiedOrderPageEnvelope'}
|
||||
'403': {description: 需要平台身份和 platform:order:list 权限}
|
||||
/api/platform/v1/orders/{orderId}:
|
||||
get:
|
||||
@@ -53,7 +57,11 @@ paths:
|
||||
- $ref: '#/components/parameters/UnifiedOrderId'
|
||||
- $ref: '#/components/parameters/UnifiedTenantId'
|
||||
responses:
|
||||
'200': {description: 成功或业务失败,按响应体 code 判断}
|
||||
'200':
|
||||
description: 成功或业务失败,按响应体 code 判断
|
||||
content:
|
||||
application/json:
|
||||
schema: {$ref: '#/components/schemas/UnifiedOrderDetailEnvelope'}
|
||||
'403': {description: 需要平台身份和 platform:order:list 权限}
|
||||
/api/platform/v1/orders/{orderId}/internal-notes:
|
||||
post:
|
||||
@@ -105,7 +113,11 @@ paths:
|
||||
- $ref: '#/components/parameters/UnifiedPage'
|
||||
- $ref: '#/components/parameters/UnifiedSize'
|
||||
responses:
|
||||
'200': {description: 成功或业务失败,按响应体 code 判断}
|
||||
'200':
|
||||
description: 成功或业务失败,按响应体 code 判断
|
||||
content:
|
||||
application/json:
|
||||
schema: {$ref: '#/components/schemas/UnifiedOrderPageEnvelope'}
|
||||
'403': {description: 需要 merchant:order:list 权限}
|
||||
/api/merchant/v1/orders/{orderId}:
|
||||
get:
|
||||
@@ -118,7 +130,7 @@ paths:
|
||||
description: 成功或业务失败,按响应体 code 判断
|
||||
content:
|
||||
application/json:
|
||||
schema: {$ref: '#/components/schemas/FirstLegOrderSummary'}
|
||||
schema: {$ref: '#/components/schemas/UnifiedOrderDetailEnvelope'}
|
||||
'403': {description: 需要 merchant:order:view 权限}
|
||||
/api/merchant/v1/orders/first-leg:
|
||||
get:
|
||||
@@ -295,6 +307,146 @@ components:
|
||||
scheme: bearer
|
||||
bearerFormat: JWT
|
||||
schemas:
|
||||
UnifiedMainStatus:
|
||||
type: string
|
||||
enum: [PENDING_REVIEW, WAIT_MEASURE, INBOUNDED, LOADING, LOADED, DELIVERED]
|
||||
description: >-
|
||||
统一订单产品主状态。历史内部状态 WAIT_SPLIT 仅作为查询入参兼容,所有列表、详情和时间线响应均归并为 INBOUNDED。
|
||||
UnifiedOrderPageEnvelope:
|
||||
type: object
|
||||
properties:
|
||||
code: {type: string, example: '200'}
|
||||
message: {type: string}
|
||||
data: {$ref: '#/components/schemas/UnifiedOrderPage'}
|
||||
UnifiedOrderPage:
|
||||
type: object
|
||||
properties:
|
||||
records: {type: array, items: {$ref: '#/components/schemas/UnifiedOrderRow'}}
|
||||
page: {type: integer, format: int64, minimum: 1}
|
||||
size: {type: integer, format: int64, minimum: 1}
|
||||
total: {type: integer, format: int64, minimum: 0}
|
||||
pages: {type: integer, format: int64, minimum: 0}
|
||||
UnifiedOrderDetailEnvelope:
|
||||
type: object
|
||||
properties:
|
||||
code: {type: string, example: '200'}
|
||||
message: {type: string}
|
||||
data: {$ref: '#/components/schemas/UnifiedOrderDetail'}
|
||||
UnifiedOrderDetail:
|
||||
type: object
|
||||
properties:
|
||||
summary: {$ref: '#/components/schemas/UnifiedOrderRow'}
|
||||
order:
|
||||
type: object
|
||||
properties:
|
||||
order: {type: object, description: Order 服务订单主实体}
|
||||
cargoes: {type: array, items: {type: object}}
|
||||
parties: {type: array, items: {type: object}}
|
||||
internalNotes: {type: array, items: {type: object}, description: 客户详情始终为空数组}
|
||||
statusHistory: {type: array, items: {type: object}}
|
||||
timeline: {type: array, items: {$ref: '#/components/schemas/UnifiedOrderTimelineEvent'}}
|
||||
UnifiedOrderRow:
|
||||
type: object
|
||||
properties:
|
||||
orderId: {type: integer, format: int64}
|
||||
tenantId: {type: integer, format: int64}
|
||||
orderNo: {type: string}
|
||||
mainStatus: {$ref: '#/components/schemas/UnifiedMainStatus'}
|
||||
nextAction: {type: string, nullable: true}
|
||||
relation: {$ref: '#/components/schemas/UnifiedOrderRelation'}
|
||||
internalOrderStatus: {type: string, enum: [DRAFT, SUBMITTED, APPROVED, ACCEPTED, REJECTED, CANCELLED, PROCESSING, COMPLETED], nullable: true}
|
||||
customerId: {type: integer, format: int64, nullable: true}
|
||||
customerName: {type: string, nullable: true}
|
||||
channelName: {type: string, nullable: true}
|
||||
receivingWarehouseId: {type: integer, format: int64, nullable: true}
|
||||
receivingWarehouseName: {type: string, nullable: true}
|
||||
destinationWarehouseId: {type: integer, format: int64, nullable: true}
|
||||
destinationWarehouseName: {type: string, nullable: true}
|
||||
inboundId: {type: integer, format: int64, nullable: true}
|
||||
inboundVersion: {type: integer, nullable: true, description: C04 loading/complete 的 expectedVersion}
|
||||
inboundCode: {type: string, nullable: true}
|
||||
expectedShipDate: {type: string, format: date, nullable: true}
|
||||
totalPackageCount: {type: integer, nullable: true}
|
||||
recommendedPalletType: {type: string, nullable: true}
|
||||
locationCodes: {type: string, nullable: true}
|
||||
actualGrossWeightKg: {type: number, nullable: true}
|
||||
actualVolumeCbm: {type: number, nullable: true}
|
||||
measurement: {$ref: '#/components/schemas/UnifiedMeasurementSummary'}
|
||||
preload: {$ref: '#/components/schemas/UnifiedPreloadSummary'}
|
||||
loading: {$ref: '#/components/schemas/UnifiedLoadingSummary'}
|
||||
delivery: {$ref: '#/components/schemas/UnifiedDeliverySummary'}
|
||||
transport: {$ref: '#/components/schemas/UnifiedTransportSummary'}
|
||||
customerRemark: {type: string, nullable: true}
|
||||
latestInternalNote: {type: string, nullable: true, description: 客户详情不返回平台内部备注}
|
||||
createdAt: {type: string, format: date-time, nullable: true}
|
||||
updatedAt: {type: string, format: date-time, nullable: true}
|
||||
UnifiedOrderRelation:
|
||||
type: object
|
||||
nullable: true
|
||||
properties:
|
||||
role: {type: string, enum: [PARENT, CHILD]}
|
||||
rootOrderId: {type: integer, format: int64, nullable: true}
|
||||
parentOrderId: {type: integer, format: int64, nullable: true}
|
||||
splitSequence: {type: integer, nullable: true}
|
||||
childCount: {type: integer, nullable: true}
|
||||
UnifiedMeasurementSummary:
|
||||
type: object
|
||||
nullable: true
|
||||
properties:
|
||||
reviewStatus: {type: string, nullable: true}
|
||||
actualGrossWeightKg: {type: number, nullable: true}
|
||||
actualVolumeCbm: {type: number, nullable: true}
|
||||
chargeableWeightKg: {type: number, nullable: true}
|
||||
completedAt: {type: string, format: date-time, nullable: true}
|
||||
UnifiedPreloadSummary:
|
||||
type: object
|
||||
nullable: true
|
||||
properties:
|
||||
status: {type: string, nullable: true}
|
||||
version: {type: integer, nullable: true}
|
||||
itemCount: {type: integer, nullable: true}
|
||||
updatedAt: {type: string, format: date-time, nullable: true}
|
||||
UnifiedLoadingSummary:
|
||||
type: object
|
||||
nullable: true
|
||||
properties:
|
||||
status: {type: string, nullable: true}
|
||||
loadingVersion: {type: integer, nullable: true, description: 当前为 pickingVersion 兼容别名}
|
||||
pickingVersion: {type: integer, nullable: true}
|
||||
expectedCount: {type: integer, nullable: true}
|
||||
pickedCount: {type: integer, nullable: true}
|
||||
evidenceCount: {type: integer, nullable: true}
|
||||
sealNo: {type: string, nullable: true}
|
||||
updatedAt: {type: string, format: date-time, nullable: true}
|
||||
UnifiedDeliverySummary:
|
||||
type: object
|
||||
nullable: true
|
||||
properties:
|
||||
status: {type: string, nullable: true}
|
||||
completedAt: {type: string, format: date-time, nullable: true}
|
||||
trackingNo: {type: string, nullable: true}
|
||||
UnifiedTransportSummary:
|
||||
type: object
|
||||
nullable: true
|
||||
properties:
|
||||
loadingStatus: {type: string, nullable: true}
|
||||
containerNo: {type: string, nullable: true}
|
||||
cabinetNo: {type: string, nullable: true}
|
||||
vesselName: {type: string, nullable: true}
|
||||
voyageNo: {type: string, nullable: true}
|
||||
sailingAt: {type: string, format: date-time, nullable: true}
|
||||
portArrivalAt: {type: string, format: date-time, nullable: true}
|
||||
UnifiedOrderTimelineEvent:
|
||||
type: object
|
||||
properties:
|
||||
source: {type: string}
|
||||
eventType: {type: string}
|
||||
mainStatus: {$ref: '#/components/schemas/UnifiedMainStatus'}
|
||||
action: {type: string, nullable: true}
|
||||
remark: {type: string, nullable: true}
|
||||
operatorId: {type: integer, format: int64, nullable: true}
|
||||
operatorName: {type: string, nullable: true}
|
||||
occurredAt: {type: string, format: date-time, nullable: true}
|
||||
FulfillmentSplitQuantityLine:
|
||||
type: object
|
||||
required: [sourceDetailId, packageCount]
|
||||
@@ -459,9 +611,10 @@ components:
|
||||
name: mainStatus
|
||||
in: query
|
||||
required: false
|
||||
description: 历史值 WAIT_SPLIT 仍可作为兼容筛选传入,但响应 mainStatus 始终归并为 INBOUNDED
|
||||
schema:
|
||||
type: string
|
||||
enum: [PENDING_REVIEW, WAIT_MEASURE, INBOUNDED, LOADING, LOADED, DELIVERED]
|
||||
enum: [PENDING_REVIEW, WAIT_MEASURE, INBOUNDED, LOADING, LOADED, DELIVERED, WAIT_SPLIT]
|
||||
UnifiedInboundCode:
|
||||
{name: inboundCode, in: query, required: false, description: 客户提交后生成的入仓码,待审核订单同样可查询, schema: {type: string, maxLength: 40}}
|
||||
UnifiedInternalOrderStatus:
|
||||
|
||||
@@ -39,7 +39,24 @@ class UnifiedOrderApiContractTest {
|
||||
Map<String, Object> mainStatus = stringMap(parameters.get("UnifiedMainStatus"));
|
||||
Map<String, Object> schema = stringMap(mainStatus.get("schema"));
|
||||
assertThat(stringList(schema.get("enum"))).containsExactly(
|
||||
"PENDING_REVIEW", "WAIT_MEASURE", "INBOUNDED", "LOADING", "LOADED", "DELIVERED", "WAIT_SPLIT");
|
||||
assertThat(mainStatus.get("description").toString())
|
||||
.contains("WAIT_SPLIT", "INBOUNDED");
|
||||
|
||||
Map<String, Object> schemas = stringMap(components.get("schemas"));
|
||||
Map<String, Object> responseStatus = stringMap(schemas.get("UnifiedMainStatus"));
|
||||
assertThat(stringList(responseStatus.get("enum"))).containsExactly(
|
||||
"PENDING_REVIEW", "WAIT_MEASURE", "INBOUNDED", "LOADING", "LOADED", "DELIVERED");
|
||||
assertThat(responseStatus.get("description").toString()).contains("WAIT_SPLIT", "INBOUNDED");
|
||||
|
||||
Map<String, Object> row = stringMap(schemas.get("UnifiedOrderRow"));
|
||||
assertThat(stringMap(row.get("properties"))).containsKeys(
|
||||
"orderId", "mainStatus", "inboundId", "inboundVersion", "measurement", "preload",
|
||||
"loading", "delivery", "transport", "customerRemark", "latestInternalNote");
|
||||
assertThat(responseSchema(paths, "/api/platform/v1/orders", "get"))
|
||||
.containsEntry("$ref", "#/components/schemas/UnifiedOrderPageEnvelope");
|
||||
assertThat(responseSchema(paths, "/api/merchant/v1/orders/{orderId}", "get"))
|
||||
.containsEntry("$ref", "#/components/schemas/UnifiedOrderDetailEnvelope");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -61,4 +78,11 @@ class UnifiedOrderApiContractTest {
|
||||
private static List<String> stringList(Object value) {
|
||||
return (List<String>) value;
|
||||
}
|
||||
|
||||
private static Map<String, Object> responseSchema(Map<String, Object> paths, String path, String method) {
|
||||
Map<String, Object> operation = stringMap(stringMap(paths.get(path)).get(method));
|
||||
Map<String, Object> response = stringMap(stringMap(operation.get("responses")).get("200"));
|
||||
Map<String, Object> content = stringMap(response.get("content"));
|
||||
return stringMap(stringMap(content.get("application/json")).get("schema"));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user