diff --git a/firstleg/src/main/resources/openapi/firstleg-v1.yaml b/firstleg/src/main/resources/openapi/firstleg-v1.yaml index cf7562e73..cfb47e708 100644 --- a/firstleg/src/main/resources/openapi/firstleg-v1.yaml +++ b/firstleg/src/main/resources/openapi/firstleg-v1.yaml @@ -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: diff --git a/firstleg/src/test/java/com/baiyue/firstleg/controller/InboundOrderControllerContractTest.java b/firstleg/src/test/java/com/baiyue/firstleg/controller/InboundOrderControllerContractTest.java index e3eb38edd..cabc48b84 100644 --- a/firstleg/src/test/java/com/baiyue/firstleg/controller/InboundOrderControllerContractTest.java +++ b/firstleg/src/test/java/com/baiyue/firstleg/controller/InboundOrderControllerContractTest.java @@ -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"); } diff --git a/firstleg/src/test/java/com/baiyue/firstleg/controller/UnifiedInboundOpenApiContractTest.java b/firstleg/src/test/java/com/baiyue/firstleg/controller/UnifiedInboundOpenApiContractTest.java new file mode 100644 index 000000000..aef03ce21 --- /dev/null +++ b/firstleg/src/test/java/com/baiyue/firstleg/controller/UnifiedInboundOpenApiContractTest.java @@ -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 document = stringMap(new Yaml().load(yaml)); + Map 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 schemas = stringMap(stringMap(document.get("components")).get("schemas")); + Map 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 query = stringMap(schemas.get("UnifiedInboundQuery")); + Map queryStatus = stringMap(stringMap(query.get("properties")).get("mainStatus")); + assertThat(stringList(queryStatus.get("enum"))).contains("WAIT_SPLIT"); + assertThat(queryStatus.get("description").toString()).contains("INBOUNDED"); + + Map 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 stringMap(Object value) { + return (Map) value; + } + + @SuppressWarnings("unchecked") + private static List stringList(Object value) { + return (List) value; + } +} diff --git a/order/src/main/resources/openapi/order-v1.yaml b/order/src/main/resources/openapi/order-v1.yaml index 893157603..e409d7a5d 100644 --- a/order/src/main/resources/openapi/order-v1.yaml +++ b/order/src/main/resources/openapi/order-v1.yaml @@ -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: diff --git a/order/src/test/java/com/baiyue/order/controller/UnifiedOrderApiContractTest.java b/order/src/test/java/com/baiyue/order/controller/UnifiedOrderApiContractTest.java index 686d47623..8f46d0e70 100644 --- a/order/src/test/java/com/baiyue/order/controller/UnifiedOrderApiContractTest.java +++ b/order/src/test/java/com/baiyue/order/controller/UnifiedOrderApiContractTest.java @@ -39,7 +39,24 @@ class UnifiedOrderApiContractTest { Map mainStatus = stringMap(parameters.get("UnifiedMainStatus")); Map 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 schemas = stringMap(components.get("schemas")); + Map 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 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 stringList(Object value) { return (List) value; } + + private static Map responseSchema(Map paths, String path, String method) { + Map operation = stringMap(stringMap(paths.get(path)).get(method)); + Map response = stringMap(stringMap(operation.get("responses")).get("200")); + Map content = stringMap(response.get("content")); + return stringMap(stringMap(content.get("application/json")).get("schema")); + } }