openapi: 3.0.3 info: title: 柏悦头程履约 API description: 业务成功和失败均使用 HTTP 200;仅响应体 code 为字符串 "200" 时表示成功,其他 code 表示失败。 version: 1.1.0 security: - bearerAuth: [] paths: /api/merchant/v1/first-leg/inbounds/{inboundId}/carton-label-pdf: post: summary: 客户端生成本客户入库单箱唛 PDF parameters: - {name: inboundId, in: path, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}} - {name: cartonIndex, in: query, required: false, schema: {type: array, items: {type: integer, minimum: 1}}} responses: '200': {description: PDF 文件} '403': {description: 非客户端身份、无订单查看权限或入库单不属于当前客户} /api/merchant/v1/first-leg/orders/{orderId}/carton-label-pdf: post: summary: 客户端按订单生成本客户草稿或正式订单箱唛 PDF description: 后端按登录客户和 orderId 查找真实预入库单;不要求客户端先取得 inboundId。 parameters: - {name: orderId, in: path, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}} - {name: cartonIndex, in: query, required: false, schema: {type: array, items: {type: integer, minimum: 1}}} responses: '200': {description: PDF 文件} '403': {description: 非客户端身份、无订单查看权限或订单不属于当前客户} '404': {description: 订单尚未生成预入库单} /internal/firstleg/v1/customer-dimension-configs/defaults: post: summary: 幂等初始化客户四箱规默认配置 description: 原子创建 SMALL、MEDIUM、LARGE、EXTRA_LARGE 四行;同一客户和 initializationKey 重试稳定返回 createdByThisInitialization=true,configId 为 SMALL 行 ID。 security: - internalServiceToken: [] requestBody: required: true content: application/json: schema: {$ref: '#/components/schemas/CustomerDimensionConfigInitializeRequest'} responses: '200': {description: 成功或业务失败,data 返回 configId 和 createdByThisInitialization} delete: summary: 补偿删除本次新建且尚未修改的四箱规默认配置 description: 仅当四行均匹配 initializationKey、默认尺寸、默认2cm和version=0时整组物理删除,不会部分删除。 security: - internalServiceToken: [] parameters: - {name: tenantId, in: query, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}} - {name: initializationKey, in: query, required: true, schema: {type: string, minLength: 1, maxLength: 128}} responses: '200': {description: 幂等补偿完成} /internal/firstleg/v1/pricing/channels/validate: post: summary: 校验订单渠道并返回进仓编码前缀 description: 仅供订单服务调用;前缀按渠道和目的仓类型选择,同一渠道的包税与不包税订单共用编码前缀。 security: - internalServiceToken: [] requestBody: required: true content: application/json: schema: type: object required: [tenantId, originWarehouseId, channelCode, destinationType] 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]} responses: '200': {description: 成功时 data 返回 channelCode 和 inboundCodePrefix;未配置对应进仓编码时返回业务冲突} /api/platform/v1/first-leg/customer-dimension-config: get: summary: 查询客户固定四箱规尺寸配置 description: 需要 firstleg:dimension-config:view 权限;按 SMALL、MEDIUM、LARGE、EXTRA_LARGE 固定顺序返回;规则用于实测尺寸归档和计费体积加权,不包含价格。 parameters: - {name: tenantId, in: query, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}} responses: '200': {description: 成功时 data 为 CustomerDimensionConfigGroupResponse} '403': {description: 无查询权限} put: summary: 整组编辑客户固定四箱规尺寸配置 description: 需要 firstleg:dimension-config:edit 权限;请求必须恰好包含四种固定 boxType,四行逐行乐观锁并在同一事务原子保存。 parameters: - {name: tenantId, in: query, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}} requestBody: required: true content: application/json: schema: {$ref: '#/components/schemas/CustomerDimensionConfigUpdateRequest'} responses: '200': {description: 成功时 data 为更新后的 CustomerDimensionConfigGroupResponse} '403': {description: 无编辑权限} /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} /internal/firstleg/v1/inbounds/unified-orders/query: post: summary: 按统一订单条件分页查询履约摘要(Order 内部) description: >- mainStatus 接受六态产品状态;WAIT_SPLIT 为入库完成后的唯一待分柜状态。 计费与入库处理中状态在完成前仍归并为 WAIT_MEASURE,不提前开放分柜。 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: 分页查询履约任务 description: 需要 platform:firstleg:task:list 权限 parameters: - $ref: '#/components/parameters/TenantId' - $ref: '#/components/parameters/Page' - $ref: '#/components/parameters/Size' - {name: keyword, in: query, schema: {type: string, maxLength: 64}} - {name: status, in: query, schema: {type: string, enum: [PENDING_ASSIGNMENT, ASSIGNED, PROCESSING, COMPLETED, CANCELLED]}} - {name: assigneeId, in: query, schema: {type: integer, format: int64}} responses: '200': {description: 成功或业务失败,按响应体 code 判断} '403': {description: 无查询权限} /api/platform/v1/first-leg/tasks/{taskId}: get: summary: 查询履约任务详情 description: 需要 platform:firstleg:task:view 权限 parameters: - $ref: '#/components/parameters/TenantId' - $ref: '#/components/parameters/TaskId' responses: '200': {description: 成功或业务失败,按响应体 code 判断} '403': {description: 无查看权限} /api/platform/v1/first-leg/tasks/by-order/{orderId}: get: summary: 按订单查询履约任务 description: 需要 platform:firstleg:task:view 权限 parameters: - $ref: '#/components/parameters/TenantId' - {name: orderId, in: path, required: true, schema: {type: integer, format: int64}} responses: '200': {description: 成功或业务失败,按响应体 code 判断} '403': {description: 无查看权限} /api/platform/v1/first-leg/tasks/{taskId}/assignment-history: get: summary: 分页查询任务分配历史 description: 需要 platform:firstleg:assignment:history 权限 parameters: - $ref: '#/components/parameters/TenantId' - $ref: '#/components/parameters/TaskId' - $ref: '#/components/parameters/Page' - $ref: '#/components/parameters/Size' responses: '200': {description: 成功或业务失败,按响应体 code 判断} '403': {description: 无历史查看权限} /api/platform/v1/first-leg/tasks/{taskId}/assignment: post: summary: 分配或改派履约任务 description: 需要 platform:firstleg:assignment:assign 权限 parameters: - $ref: '#/components/parameters/TenantId' - $ref: '#/components/parameters/TaskId' requestBody: required: true content: application/json: schema: {$ref: '#/components/schemas/AssignRequest'} responses: '200': {description: 成功或业务失败,按响应体 code 判断} '403': {description: 无分配权限} /api/platform/v1/containers: get: summary: 分页查询平台货柜 description: 需要 platform:firstleg:container:list 权限;货柜为平台级数据,不传 tenant/account parameters: - {$ref: '#/components/parameters/Page'} - {$ref: '#/components/parameters/Size'} - {name: keyword, in: query, schema: {type: string, maxLength: 128}} - {name: customerId, in: query, description: 仅返回包含该客户货物的柜次, schema: {type: string, pattern: '^[1-9][0-9]*$'}} - {name: containerNo, in: query, schema: {type: string, maxLength: 64}} - {name: cabinetNo, in: query, schema: {type: string, maxLength: 64}} - {name: warehouseId, in: query, schema: {type: string, pattern: '^[1-9][0-9]*$'}} - {name: status, in: query, schema: {type: string, enum: [WAIT_LOADING, EDITING, LOADING, COMPLETED]}} responses: '200': description: 成功或业务失败,按响应体 code 判断 content: application/json: schema: {$ref: '#/components/schemas/ContainerBookingPageEnvelope'} '403': {description: 无货柜查询权限} post: summary: 新增内部模拟柜 description: 需要 platform:firstleg:container:create 权限;服务端生成稳定 containerBookingId/cabinetNo,不要求真实柜号或订舱 requestBody: required: true content: application/json: schema: {$ref: '#/components/schemas/ContainerSimulationCreateRequest'} responses: '200': {description: 成功或重复号、参数校验等业务失败} '403': {description: 无新增按钮权限} /api/platform/v1/containers/{id}/ocean-booking-link: post: summary: 将内部模拟柜关联到真实已确认订舱柜 description: 需要 platform:firstleg:container:edit;首次关联允许 WAIT_LOADING 或已有预排货的 EDITING 柜,保留原 containerBookingId/cabinetNo/loadingPlanId/items;存在 PDA、有效装柜证据、业务单证或已进入 LOADING/COMPLETED 等不可逆事实时拒绝。 parameters: [{$ref: '#/components/parameters/ContainerBookingId'}] requestBody: {required: true, content: {application/json: {schema: {$ref: '#/components/schemas/ContainerOceanBookingLinkRequest'}}}} responses: {'200': {description: 关联后的同一 containerBookingId/cabinetNo}} delete: summary: 解除内部模拟柜的真实订舱关联 description: 需要 platform:firstleg:container:edit;解绑或换绑仅允许完全空的 WAIT_LOADING 模拟柜;任何排货、PDA、有效装柜证据或业务单证事实均拒绝。 parameters: [{$ref: '#/components/parameters/ContainerBookingId'}] requestBody: {required: true, content: {application/json: {schema: {$ref: '#/components/schemas/ContainerOceanBookingUnlinkRequest'}}}} responses: {'200': {description: 解除后的同一 containerBookingId/cabinetNo}} /api/platform/v1/containers/{id}: get: summary: 查询平台货柜详情 description: 需要 platform:firstleg:container:list 权限 parameters: - {$ref: '#/components/parameters/ContainerBookingId'} responses: '200': {description: 成功或货柜不存在} '403': {description: 无货柜查询权限} put: summary: 草稿阶段完整编辑货柜 description: 需要 platform:firstleg:container:edit 权限;仅待装柜或计划为 EDITING(预装柜中)时允许 parameters: - {$ref: '#/components/parameters/ContainerBookingId'} requestBody: required: true content: application/json: schema: {$ref: '#/components/schemas/ContainerFullUpdateRequest'} responses: '200': {description: 成功或状态、版本、重复号冲突} '403': {description: 无货柜编辑权限} /api/platform/v1/containers/{id}/transport: patch: summary: 修改集装箱号和航次 description: 需要 platform:firstleg:container:edit 权限;LOADING 仅开放此白名单,COMPLETED 全部只读 parameters: - {$ref: '#/components/parameters/ContainerBookingId'} requestBody: required: true content: application/json: schema: {$ref: '#/components/schemas/ContainerTransportUpdateRequest'} responses: '200': {description: 成功或状态、版本、重复号冲突} '403': {description: 无货柜编辑权限} /api/platform/v1/containers/{id}/clearance: get: summary: 查询货柜目的港清关资料 description: 需要 platform:firstleg:customs:manage 权限 parameters: [{$ref: '#/components/parameters/ContainerBookingId'}] responses: {'200': {description: 成功或货柜不存在}} /api/platform/v1/containers/{id}/clearance/document: post: summary: 上传或替换货柜目的港清关资料 description: 需要 platform:firstleg:customs:manage 权限,fileId 必须严格绑定当前货柜 parameters: [{$ref: '#/components/parameters/ContainerBookingId'}, {$ref: '#/components/parameters/IdempotencyKey'}] requestBody: required: true content: {application/json: {schema: {$ref: '#/components/schemas/ContainerDocumentAttachRequest'}}} responses: {'200': {description: 成功、文件归属不匹配或版本冲突}} /api/platform/v1/containers/{id}/clearance/document/upload: post: summary: 直接选择文件上传或替换货柜目的港清关资料 description: Firstleg 使用内部服务令牌上传 File,浏览器不得传递 fileId 或内部令牌;旧 JSON document 接口仅兼容存量调用。 parameters: [{$ref: '#/components/parameters/ContainerBookingId'}, {$ref: '#/components/parameters/IdempotencyKey'}] requestBody: required: true content: multipart/form-data: schema: {$ref: '#/components/schemas/ContainerDocumentUploadRequest'} responses: {'200': {description: 成功、文件上传失败或版本冲突}} /api/platform/v1/containers/{id}/clearance/complete: post: summary: 点击完成目的港清关 description: 需要 platform:firstleg:customs:manage 权限 parameters: [{$ref: '#/components/parameters/ContainerBookingId'}, {$ref: '#/components/parameters/IdempotencyKey'}] requestBody: required: true content: {application/json: {schema: {$ref: '#/components/schemas/ContainerDocumentCompleteRequest'}}} responses: {'200': {description: 成功、未上传资料或版本冲突}} /api/platform/v1/first-leg/smart-loading/plans/by-booking/{bookingId}: get: summary: 按货柜恢复智能装柜草稿 description: 需要 platform:firstleg:smart-loading:list 权限;仅返回 EDITING 草稿 parameters: - {name: bookingId, in: path, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}} responses: '200': {description: 成功、草稿不存在或计划已进入预装流程} '403': {description: 无智能装柜查询权限} /api/platform/v1/first-leg/smart-loading/candidates: get: summary: Query same-channel smart-loading candidates parameters: - {name: bookingId, in: query, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}} - {name: warehouseId, in: query, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}} responses: {'200': {description: Candidates filtered by the server to the booking channel}} /api/platform/v1/first-leg/smart-loading/plans/{planId}: get: summary: 查询智能装柜草稿详情 description: 需要 platform:firstleg:smart-loading:list 权限;返回草稿及当前未删除明细 parameters: - {name: planId, in: path, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}} responses: '200': {description: 成功、计划不存在或计划已进入预装流程} '403': {description: 无智能装柜查询权限} /api/platform/v1/first-leg/smart-loading/plans/{planId}/push-preload: post: summary: 推送智能装柜计划到预装清单 description: >- 需要 platform:firstleg:smart-loading:push 权限。push-preload 不要求真实柜号, 只冻结 EDITING 柜次计划的明细并建立稳定预装关联,不创建 PDA 作业任务;计划必须处于 EDITING 且版本匹配。 预装页面随后补录真实柜号;只有预装 CONFIRMED、真实柜号存在、已关联 CONFIRMED/RELEASED 订舱时才创建待领取 PDA 任务,领取后才进入 LOADING。 智能装柜首次分配会创建入库单级 PENDING 预装聚合;push 会在同一事务内对全部实际分配再次确保该聚合存在且仍为 PENDING, 但不会覆盖 CONFIRMING/CONFIRMED 记录,也不会在 LOADING 后补建。 推送事务会先为所有已分配入库单建立 PENDING 预装计划与 EDITING 柜次计划的正式关联, 任一关联缺失、版本冲突或分配数量为零都会整体失败并禁止 PDA/LOADING;不得在 LOADING 后补关联。 parameters: - {name: planId, in: path, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}} - {$ref: '#/components/parameters/IdempotencyKey'} requestBody: required: true content: {application/json: {schema: {$ref: '#/components/schemas/SmartLoadingPushRequest'}}} responses: '200': {description: 推送成功或业务失败,按响应体 code 判断} '403': {description: 无智能装柜推送权限} /openapi/v1/inbound-measurements: post: summary: 提交一条第三方箱规测量结果和一张图片 description: >- data 中的 inboundCode 匹配入库单,inboundDetailId 必须匹配该入库单关联订单中的已有货物明细; 每次只保存一条箱规明细和一张图片,不审核、不计费、不入库,也不改变入库单状态。 首轮联调可由服务端配置 signature-required=false:此时只强制固定 X-App-Key 和 Idempotency-Key,X-Timestamp、X-Nonce、X-Signature 可不传;恢复签名后按下述规则计算。 签名串依次为 METHOD、原始 PATH、X-Timestamp、X-Nonce、Idempotency-Key、 签名body的 SHA-256 小写十六进制,六部分使用 LF(\n)连接且末尾不追加换行。 签名body = data原始UTF-8文本 + LF + image文件内容SHA-256小写十六进制。 三层语义:Nonce 防短时重放;Idempotency-Key 标识一次 HTTP 写操作; appKey + eventNo 标识第三方业务事件。多台设备可共用同一 appKey/secret, 但 deviceCode、eventNo 和 Idempotency-Key 必须由调用方生成全局唯一值, 不得仅在单台设备内从 1 开始递增。 security: - measurementAppKey: [] measurementTimestamp: [] measurementNonce: [] measurementSignature: [] parameters: - {$ref: '#/components/parameters/XTimestamp'} - {$ref: '#/components/parameters/XNonce'} - {$ref: '#/components/parameters/IdempotencyKey'} requestBody: required: true content: application/json: schema: type: object required: [boxCode, length, width, height, machineNo, picture] properties: boxCode: {type: string, maxLength: 128, example: 'ZBYHK2608140001#4-1'} shipment_id: {type: string, maxLength: 64, example: 'FBA19L5KMY2CU000002', description: '可选;设备从Amazon FBA箱标实际扫描得到,有则原样上传;系统只保存,不生成且不覆盖客户下单值'} length: {type: number, format: decimal, minimum: 0.001} width: {type: number, format: decimal, minimum: 0.001} height: {type: number, format: decimal, minimum: 0.001} weight: {type: number, format: decimal, minimum: 0.001, nullable: true} machineNo: {type: string, maxLength: 64} measuredAt: {type: string, format: date-time, nullable: true} picture: {type: string, description: 'data:image/jpeg;base64,...'} multipart/form-data: schema: type: object required: [data, image] properties: data: type: string description: InboundMeasurementCallbackRequest 的原始UTF-8 JSON文本 image: type: string format: binary description: 当前测量明细唯一对应图片,JPG/PNG/BMP且不超过5MB responses: '200': description: JSON 墨家协议在正式箱码匹配或未匹配联调收件成功时均返回 code=200;data.matched 区分结果。其他业务失败按 code 判断。 content: application/json: schema: {$ref: '#/components/schemas/MojiaMeasurementUploadEnvelope'} '413': {description: 请求体超过网关限制} '415': {description: Content-Type 不支持} /openapi/v1/inbound-measurements/events/{eventNo}: get: summary: 查询第三方测量事件状态 description: >- signature-required=false 时只需固定 X-App-Key;恢复签名后,GET 签名串的 Idempotency-Key 行为空字符串,body 摘要为空字节 SHA-256。 security: - measurementAppKey: [] measurementTimestamp: [] measurementNonce: [] measurementSignature: [] parameters: - name: eventNo in: path required: true schema: {type: string, pattern: '^[A-Za-z0-9._:-]{1,128}$'} - {$ref: '#/components/parameters/XTimestamp'} - {$ref: '#/components/parameters/XNonce'} responses: '200': {description: 成功或业务失败,按响应体字符串 code 判断} /api/platform/inbounds: get: summary: 分页查询入库单 description: 需要 platform:firstleg:inbound:list 权限;默认查询全部企业,可选 tenantId 筛选;仅使用 V2 四个页签 parameters: - {name: tenantId, in: query, required: false, schema: {type: string, pattern: '^[1-9][0-9]*$'}} - {name: tab, in: query, schema: {type: string, enum: [WAIT_MEASURE, WAIT_REVIEW, WAIT_FEE_SEND, WAIT_SPLIT]}} responses: '200': description: 成功或业务失败,按响应体 code 判断;每条记录携带后续客户隔离操作所需的 tenantId,该字段当前表示客户 ID content: application/json: schema: {$ref: '#/components/schemas/InboundOrderPageEnvelope'} '403': {description: 无查询权限} /api/platform/inbounds/{inboundId}: get: summary: 查询入库单详情 description: 需要 platform:firstleg:inbound:view 权限 parameters: - {name: tenantId, in: query, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}} - {name: inboundId, in: path, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}} responses: '200': {description: 成功或业务失败,按响应体 code 判断} '403': {description: 无查看权限} /api/platform/inbounds/{inboundId}/carton-label-pdf: post: summary: 生成或补打入仓箱唛 PDF description: 需要 platform:firstleg:inbound:view 权限;首次打印冻结整单箱号,重复打印和部分补打复用原映射。 parameters: - {name: tenantId, in: query, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}} - {name: inboundId, in: path, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}} - name: cartonIndex in: query required: false style: form explode: true schema: {type: array, items: {type: integer, minimum: 1}} description: 不传时打印整单;可重复传入以部分补打指定箱下标。 responses: '200': description: 成功返回 PDF;业务失败返回统一 JSON 响应,调用方必须按 Content-Type 区分。 content: application/pdf: {schema: {type: string, format: binary}} application/json: schema: type: object required: [code, message] properties: code: {type: string} message: {type: string} requestId: {type: string, nullable: true} traceId: {type: string, nullable: true} '403': {description: 无查看权限} /api/platform/v1/first-leg/inbounds/{inboundId}/measurement/carton-scans: post: summary: 平台模拟单箱设备扫描 description: >- 需要 platform:firstleg:inbound:measure:edit 权限;仅用于平台联调和代设备录入, 复用正式单箱覆盖规则,不代表最终第三方设备协议或鉴权方式。 parameters: - {name: tenantId, in: query, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}} - {name: inboundId, in: path, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}} requestBody: required: true content: multipart/form-data: schema: type: object required: [data, image] properties: data: type: object required: [boxCode, length, width, height, machineNo, measuredAt] properties: boxCode: {type: string, maxLength: 128} length: {type: number, format: double, minimum: 0.001} width: {type: number, format: double, minimum: 0.001} height: {type: number, format: double, minimum: 0.001} weight: {type: number, format: double, minimum: 0.001, nullable: true} machineNo: {type: string, maxLength: 64} measuredAt: {type: string, format: date-time} image: {type: string, format: binary} responses: '200': {description: 成功或业务失败,按响应体 code 判断} '403': {description: 无测量编辑权限} /api/platform/v1/first-leg/inbounds/{inboundId}/measurement/manual-cartons: post: summary: 平台人工录入单箱测量 description: >- 需要 platform:firstleg:inbound:measure:manual-entry 权限;人工录入独立记录为 MANUAL/PLATFORM, 复用正式单箱绑定、图片、计算和覆盖规则,不影响设备扫描入口。 parameters: - {name: tenantId, in: query, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}} - {name: inboundId, in: path, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}} requestBody: required: true content: multipart/form-data: schema: type: object required: [data, image] properties: data: type: object required: [boxCode, length, width, height, machineNo, measuredAt] properties: boxCode: {type: string, maxLength: 128} length: {type: number, format: double, minimum: 0.001} width: {type: number, format: double, minimum: 0.001} height: {type: number, format: double, minimum: 0.001} weight: {type: number, format: double, minimum: 0.001, nullable: true} machineNo: {type: string, maxLength: 64} measuredAt: {type: string, format: date-time} image: {type: string, format: binary} responses: '200': {description: 成功或业务失败,按响应体 code 判断} '403': {description: 无测量编辑权限} /api/platform/v1/first-leg/inbounds/{inboundId}/weight-pallet-options: get: summary: 查询称重可选托盘快照 description: >- 需要入库测量查看或称重编辑权限。Firstleg 按入库单 warehouseId 查询 Warehouse 公共托盘快照; 仅限定同仓且未删除,不校验托盘状态或占用资格,Admin 不需要 platform:pallet:list。 parameters: - {name: tenantId, in: query, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}} - {name: inboundId, in: path, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}} responses: '200': description: 成功或业务失败,按响应体 code 判断 content: application/json: schema: type: object properties: code: {type: string} message: {type: string} data: {type: array, items: {$ref: '#/components/schemas/WeightPalletSnapshot'}} '403': {description: 无入库测量查看或称重编辑权限} /api/platform/v1/first-leg/inbounds/{inboundId}/weight-results: post: summary: 登记称重图片 OCR 毛重结果 description: >- 需要称重编辑权限。请求必须绑定 palletId;Firstleg 按入库单 warehouseId 读取 Warehouse 权威快照并固化托盘编码、类型、容量和皮重,不信任前端托盘快照。 parameters: - {name: tenantId, in: query, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}} - {name: inboundId, in: path, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}} requestBody: required: true content: application/json: schema: {$ref: '#/components/schemas/CreateWeightResultRequest'} responses: '200': description: 成功或业务失败,按响应体 code 判断 content: application/json: schema: type: object properties: code: {type: string} message: {type: string} data: {$ref: '#/components/schemas/InboundWeightResult'} '403': {description: 无称重编辑权限} /api/platform/v1/first-leg/inbounds/{inboundId}/weight-results/{resultId}/confirm: post: summary: 确认 OCR 重量 description: >- 需要 platform:firstleg:inbound:weight:confirm 权限;携带人工修改字段时还需要 platform:firstleg:inbound:weight:edit 权限。修改与确认在同一事务内完成,保留 OCR 原值, 分别写入 CORRECT 和 CONFIRM 修订记录。输入表示含托盘毛重;服务端统一换算为 KG, 使用创建时固化的托盘皮重计算 netWeightKg,且毛重必须大于皮重。 入库单 actualGrossWeightKg 明确保留为下游货物实际毛重字段,值汇总所有已确认 netWeightKg。 parameters: - {name: tenantId, in: query, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}} - {name: inboundId, in: path, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}} - {name: resultId, in: path, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}} requestBody: required: true content: application/json: schema: {$ref: '#/components/schemas/ConfirmWeightResultRequest'} responses: '200': description: 成功或业务失败,按响应体 code 判断 content: application/json: schema: type: object properties: code: {type: string} message: {type: string} data: {$ref: '#/components/schemas/InboundWeightResult'} '403': {description: 无确认权限,或修改重量时无编辑权限} /api/platform/v1/first-leg/inbounds/{inboundId}/weight-results/{resultId}: put: summary: 人工修正 OCR 毛重 description: >- 需要称重编辑权限。修正值仍表示含托盘毛重;保留 OCR 原值,已确认结果修正后立即移出净重汇总并回到待确认。 parameters: - {name: tenantId, in: query, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}} - {name: inboundId, in: path, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}} - {name: resultId, in: path, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}} requestBody: required: true content: application/json: schema: {$ref: '#/components/schemas/CorrectWeightResultRequest'} responses: '200': description: 成功或业务失败,按响应体 code 判断 content: application/json: schema: type: object properties: code: {type: string} message: {type: string} data: {$ref: '#/components/schemas/InboundWeightResult'} '403': {description: 无称重编辑权限} delete: summary: 删除称重图片及重量结果 description: >- 需要 platform:firstleg:inbound:weight:edit 权限;仅允许在可编辑的测量状态删除。 删除已确认图片后重新汇总剩余已确认图片的净重,删除最后一张时 actualGrossWeightKg 恢复为空。 parameters: - {name: tenantId, in: query, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}} - {name: version, in: query, required: true, schema: {type: integer, format: int32, minimum: 0}} - {name: inboundId, in: path, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}} - {name: resultId, in: path, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}} responses: '200': {description: 成功或业务失败,按响应体 code 判断} '403': {description: 无重量编辑权限} /api/platform/v1/inbounds/{inboundId}/measurements/approve: post: summary: 审核通过测量 description: 需要 firstleg:inbound:measurement-review 权限。审核通过后立即进入 WAIT_SPLIT,并按该次测量数据尽力生成 CALCULATED 初算费用;价格未覆盖或初算异常只返回非阻断提示,不影响审核结果和待分柜状态。 parameters: - {name: inboundId, in: path, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}} - {name: tenantId, in: query, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}} - {name: Idempotency-Key, in: header, required: true, schema: {type: string}} requestBody: required: true content: application/json: schema: type: object required: [measurementId, expectedVersion] properties: measurementId: {type: string, pattern: '^[1-9][0-9]*$'} expectedVersion: {type: integer, minimum: 0} reason: {type: string, maxLength: 1000} responses: '200': {description: 成功或业务失败,按响应体 code 判断;审核成功 data.processStatus 固定为 WAIT_SPLIT,initialPricingSucceeded 表示是否同时生成初算费用,初算失败时 feeStatus=UNPRICED 且主流程仍成功} /api/platform/v1/inbounds/{inboundId}/measurements/reject: post: summary: 驳回测量 description: 需要 firstleg:inbound:measurement-review 权限;只退回测量数据修改,已确认托盘和库位占用保持不动。 parameters: - {name: inboundId, in: path, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}} - {name: Idempotency-Key, in: header, required: true, schema: {type: string}} responses: '200': {description: 成功或业务失败} /api/platform/v1/inbounds/{inboundId}/measurements/submit: post: summary: 提交测量完成 description: 测量提交后立即按总体积拆分普通托盘、分配并确认占用库位,再进入客服待审核;无普通空闲位时共享 A0。 parameters: - {name: tenantId, in: query, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}} - {name: inboundId, in: path, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}} responses: '200': {description: 成功时 processStatus=MEASURE_PENDING、measureReviewStatus=PENDING,且库位已 OCCUPIED} '403': {description: 无测量提交权限} /api/merchant/v1/first-leg/orders/{orderId}/inbound-billing: get: summary: 客户查询已完成入库结果和最新已发布费用 description: 需要 merchant:order:view 权限;客户只读查看,不需要确认费用,仅 WAIT_SPLIT + APPROVED + CONFIRMED 的二期终态可见。 parameters: - {name: orderId, in: path, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}} responses: '200': description: 成功;publishedFeeDetail 是后台和客户共同读取的同一条已发布入库运费明细,latestFee 仅为兼容保留的计费快照 content: application/json: schema: {$ref: '#/components/schemas/MerchantInboundBillingEnvelope'} '403': {description: 非当前客户或无订单查看权限} /api/platform/v1/inbounds/{inboundId}/measurements/exception-release: post: summary: 测量异常放行 description: 需要 firstleg:inbound:exception-release 权限 parameters: - {name: inboundId, in: path, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}} - {name: Idempotency-Key, in: header, required: true, schema: {type: string}} responses: '200': {description: 成功或业务失败} components: securitySchemes: bearerAuth: type: http scheme: bearer bearerFormat: JWT measurementAppKey: type: apiKey in: header name: X-App-Key description: 分配给测量服务商的应用标识 measurementTimestamp: type: apiKey in: header name: X-Timestamp description: Unix 毫秒时间戳 measurementNonce: type: apiKey in: header name: X-Nonce description: 每次请求唯一的随机串 measurementSignature: type: apiKey in: header name: X-Signature description: HMAC-SHA256 小写十六进制签名 internalServiceToken: type: apiKey in: header name: X-Internal-Service-Token parameters: XTimestamp: name: X-Timestamp in: header required: true schema: {type: string, pattern: '^[0-9]{13}$', example: '1785551400000'} XNonce: name: X-Nonce in: header required: true schema: {type: string, minLength: 1, maxLength: 128} IdempotencyKey: name: Idempotency-Key in: header required: true schema: {type: string, minLength: 1, maxLength: 128} TenantId: name: tenantId in: query required: true schema: {type: integer, format: int64, minimum: 1} TaskId: name: taskId in: path required: true schema: {type: integer, format: int64} Page: name: page in: query schema: {type: integer, format: int64, minimum: 1, default: 1} Size: name: size in: query schema: {type: integer, format: int64, minimum: 1, maximum: 200, default: 20} ContainerBookingId: name: id in: path required: true schema: {type: string, pattern: '^[1-9][0-9]*$'} schemas: UnifiedMainStatus: type: string enum: [PENDING_REVIEW, WAIT_MEASURE, WAIT_SPLIT, LOADING, LOADED, DELIVERED] description: 六态产品主状态;WAIT_SPLIT 为入库完成后的唯一待分柜状态 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, WAIT_SPLIT, LOADING, LOADED, DELIVERED] nullable: true description: WAIT_SPLIT 为唯一待分柜筛选值 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: code: {type: string, example: '200'} message: {type: string, example: success} data: {$ref: '#/components/schemas/MojiaMeasurementUploadResponse'} MojiaMeasurementUploadResponse: type: object required: [accepted, matched, boxCode, created] properties: accepted: {type: boolean, example: true} matched: {type: boolean, description: true 表示已写入正式箱级测量;false 表示已写入联调收件箱等待排查} receiptId: {type: string, nullable: true, description: matched=false 时返回的联调收件记录ID} measurementId: {type: string, nullable: true} itemId: {type: string, nullable: true} boxCode: {type: string} imageFileId: {type: string, nullable: true} created: {type: boolean} CustomerDimensionConfigUpdateRequest: type: object required: [rules] properties: rules: type: array minItems: 4 maxItems: 4 description: 必须恰好包含 SMALL、MEDIUM、LARGE、EXTRA_LARGE 各一行 items: {$ref: '#/components/schemas/CustomerDimensionConfigRuleUpdateRequest'} CustomerDimensionConfigRuleUpdateRequest: type: object required: [boxType, innerLengthCm, innerWidthCm, innerHeightCm, edgeAdditionCm, version] properties: boxType: {type: string, enum: [SMALL, MEDIUM, LARGE, EXTRA_LARGE]} innerLengthCm: {type: number, minimum: 0.001, multipleOf: 0.001} innerWidthCm: {type: number, minimum: 0.001, multipleOf: 0.001} innerHeightCm: {type: number, minimum: 0.001, multipleOf: 0.001} edgeAdditionCm: type: number minimum: 0.001 maximum: 100 multipleOf: 0.001 description: 箱规长、宽、高三个方向分别使用的每侧加宽厘米数 example: 2.000 remark: {type: string, maxLength: 500, nullable: true} version: {type: integer, minimum: 0, description: 乐观锁版本号} CustomerDimensionConfigGroupResponse: type: object required: [tenantId, rules] properties: tenantId: {type: string, pattern: '^[1-9][0-9]*$'} rules: type: array minItems: 4 maxItems: 4 items: {$ref: '#/components/schemas/CustomerDimensionConfigRuleResponse'} CustomerDimensionConfigRuleResponse: type: object required: [id, boxType, boxTypeName, innerLengthCm, innerWidthCm, innerHeightCm, edgeAdditionCm, outerLengthCm, outerWidthCm, outerHeightCm, volumeCbm, version] properties: id: {type: string, pattern: '^[1-9][0-9]*$'} boxType: {type: string, enum: [SMALL, MEDIUM, LARGE, EXTRA_LARGE]} boxTypeName: {type: string, enum: [小箱, 中箱, 大箱, 特大箱]} innerLengthCm: {type: number, minimum: 0.001, multipleOf: 0.001} innerWidthCm: {type: number, minimum: 0.001, multipleOf: 0.001} innerHeightCm: {type: number, minimum: 0.001, multipleOf: 0.001} edgeAdditionCm: {type: number, minimum: 0.001, maximum: 100, example: 2.000} outerLengthCm: {type: number, minimum: 0.001, description: innerLengthCm + edgeAdditionCm * 2} outerWidthCm: {type: number, minimum: 0.001, description: innerWidthCm + edgeAdditionCm * 2} outerHeightCm: {type: number, minimum: 0.001, description: innerHeightCm + edgeAdditionCm * 2} volumeCbm: {type: number, multipleOf: 0.001, description: 使用未舍入外径完整计算后四舍五入保留3位} remark: {type: string, nullable: true} version: {type: integer, minimum: 0} createdAt: {type: string, format: date-time, nullable: true} updatedAt: {type: string, format: date-time, nullable: true} CustomerDimensionConfigInitializeRequest: type: object required: [tenantId, operatorId, initializationKey] properties: tenantId: {type: string, pattern: '^[1-9][0-9]*$'} operatorId: {type: string, pattern: '^[0-9]+$'} initializationKey: {type: string, minLength: 1, maxLength: 128} CustomerDimensionConfigInitializeResponse: type: object required: [configId, createdByThisInitialization] properties: configId: {type: string, pattern: '^[1-9][0-9]*$'} createdByThisInitialization: {type: boolean} WeightPalletSnapshot: type: object required: [palletId, palletCode, palletType, capacityCbm, tareWeightKg] properties: palletId: {type: string, pattern: '^[1-9][0-9]*$'} palletCode: {type: string} palletType: {type: string} capacityCbm: {type: number, format: double, minimum: 0.0001} tareWeightKg: {type: number, format: double, minimum: 0, multipleOf: 0.001} CreateWeightResultRequest: type: object required: [fileId, palletId] properties: fileId: {type: string, pattern: '^[1-9][0-9]*$'} palletId: {type: string, pattern: '^[1-9][0-9]*$'} ocrProvider: {type: string, maxLength: 64, nullable: true} ocrRawText: {type: string, maxLength: 1000, nullable: true} ocrWeightValue: {type: number, format: double, minimum: 0, nullable: true, description: OCR 原始含托盘毛重} ocrWeightUnit: {type: string, maxLength: 8, nullable: true} confidence: {type: number, format: double, minimum: 0, maximum: 1, nullable: true} CorrectWeightResultRequest: type: object required: [version, adjustedWeightValue, adjustedWeightUnit, adjustmentReason] properties: version: {type: integer, minimum: 0} adjustedWeightValue: {type: number, format: double, minimum: 0.001, description: 人工修正后的含托盘毛重} adjustedWeightUnit: {type: string, enum: [KG, LB]} adjustmentReason: {type: string, minLength: 1, maxLength: 500} InboundWeightResult: type: object required: [id, fileId, palletId, palletCode, palletType, palletCapacityCbm, palletTareWeightKg, recognitionStatus, version] properties: id: {type: string, pattern: '^[1-9][0-9]*$'} fileId: {type: string, pattern: '^[1-9][0-9]*$'} palletId: {type: string, pattern: '^[1-9][0-9]*$'} palletCode: {type: string} palletType: {type: string} palletCapacityCbm: {type: number, format: double, minimum: 0.0001} palletTareWeightKg: {type: number, format: double, minimum: 0, multipleOf: 0.001} ocrProvider: {type: string, nullable: true} ocrRawText: {type: string, nullable: true} ocrWeightValue: {type: number, format: double, nullable: true, description: OCR 原始含托盘毛重} ocrWeightUnit: {type: string, nullable: true} confidence: {type: number, format: double, nullable: true} grossWeightKg: {type: number, format: double, nullable: true, multipleOf: 0.001, description: 确认时标准化的含托盘毛重 KG} adjustedWeightValue: {type: number, format: double, nullable: true, description: 人工修正后的含托盘毛重} adjustedWeightUnit: {type: string, nullable: true} netWeightKg: {type: number, format: double, nullable: true, multipleOf: 0.001, description: grossWeightKg - palletTareWeightKg} recognitionStatus: {type: string, enum: [PENDING, PENDING_CONFIRM, NEEDS_MANUAL, CONFIRMED]} adjustmentReason: {type: string, nullable: true} adjustedBy: {type: string, nullable: true} adjustedAt: {type: string, format: date-time, nullable: true} confirmedBy: {type: string, nullable: true} confirmedAt: {type: string, format: date-time, nullable: true} version: {type: integer, minimum: 0} ConfirmWeightResultRequest: type: object required: [version] description: 人工修改字段表示含托盘毛重且必须同时提供;全部省略时直接确认现有 OCR 或已修正毛重。 properties: version: {type: integer, minimum: 0} adjustedWeightValue: {type: number, format: double, minimum: 0.001, nullable: true} adjustedWeightUnit: {type: string, enum: [KG, LB], nullable: true} adjustmentReason: {type: string, minLength: 1, maxLength: 500, nullable: true} 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: ZBYHK2608140001} 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} pricingTaxMode: {type: string, enum: [TAX_INCLUDED, TAX_EXCLUDED], nullable: true, description: 报价包税维度;独立于订单taxMode} 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} ContainerBookingPageEnvelope: type: object properties: code: {type: string, example: '200'} message: {type: string} data: type: object properties: records: {type: array, items: {$ref: '#/components/schemas/ContainerBooking'}} page: {type: string, pattern: '^[1-9][0-9]*$'} size: {type: string, pattern: '^[1-9][0-9]*$'} total: {type: string, pattern: '^[0-9]+$'} pages: {type: string, pattern: '^[0-9]+$'} ContainerBooking: type: object required: [id, bookingNo, cabinetNo, warehouseId, warehouseName, containerType, planStatus, version] properties: id: {type: string, pattern: '^[1-9][0-9]*$'} planId: {type: string, pattern: '^[1-9][0-9]*$', nullable: true, description: 关联智能装柜计划ID;未创建计划时为空} bookingNo: {type: string, maxLength: 40} containerNo: {type: string, maxLength: 64, nullable: true} cabinetNo: {type: string, maxLength: 64} channelCode: {type: string, maxLength: 64, nullable: true, description: Missing legacy snapshots are blocked from loading until explicitly backfilled} channelName: {type: string, maxLength: 128, nullable: true} oceanBookingId: {type: string, pattern: '^[1-9][0-9]*$', nullable: true} oceanBookingRequirementId: {type: string, pattern: '^[1-9][0-9]*$', nullable: true} warehouseId: {type: string, pattern: '^[1-9][0-9]*$'} warehouseName: {type: string, maxLength: 128} billOfLadingNo: {type: string, maxLength: 128, nullable: true} vesselName: {type: string, maxLength: 128, nullable: true} voyageNo: {type: string, maxLength: 64, nullable: true} containerType: {type: string, maxLength: 32} shippingCompanyId: {type: string, nullable: true} shippingCompanyName: {type: string, maxLength: 128, nullable: true} departurePortCode: {type: string, maxLength: 32, nullable: true} departurePortName: {type: string, maxLength: 128, nullable: true} destinationPortCode: {type: string, maxLength: 32, nullable: true} destinationPortName: {type: string, maxLength: 128, nullable: true} destinationWarehouseCode: {type: string, maxLength: 64, nullable: true} destinationWarehouseName: {type: string, maxLength: 128, nullable: true} capacityCbm: {type: number, exclusiveMinimum: true, minimum: 0, nullable: true} maxWeightKg: {type: number, exclusiveMinimum: true, minimum: 0, nullable: true} terminalTruckingCompanyId: {type: string, pattern: '^[1-9][0-9]*$', nullable: true, description: ACTIVE Warehouse Supplier ID validated as TRUCKING} terminalTruckingCompany: {type: string, maxLength: 128, nullable: true, description: Server-frozen Warehouse supplier name snapshot} loadingAt: {type: string, format: date-time, nullable: true} estimatedSailingAt: {type: string, format: date-time, nullable: true} actualSailingAt: {type: string, format: date-time, nullable: true} estimatedPortArrivalAt: {type: string, format: date-time, nullable: true} actualPortArrivalAt: {type: string, format: date-time, nullable: true} estimatedWarehouseArrivalAt: {type: string, format: date-time, nullable: true} actualWarehouseArrivalAt: {type: string, format: date-time, nullable: true} planStatus: {type: string, enum: [WAIT_LOADING, EDITING, LOADING, COMPLETED]} version: {type: integer, minimum: 0} ContainerSaveRequest: type: object required: [cabinetNo, warehouseId, warehouseName, containerType] properties: containerNo: {type: string, maxLength: 64, nullable: true} cabinetNo: {type: string, maxLength: 64} warehouseId: {type: string, pattern: '^[1-9][0-9]*$'} warehouseName: {type: string, maxLength: 128} billOfLadingNo: {type: string, maxLength: 128, nullable: true} vesselName: {type: string, maxLength: 128, nullable: true} voyageNo: {type: string, maxLength: 64, nullable: true} containerType: {type: string, maxLength: 32} shippingCompanyId: {type: string, pattern: '^[1-9][0-9]*$', nullable: true} shippingCompanyName: {type: string, maxLength: 128, nullable: true} departurePortCode: {type: string, maxLength: 32, nullable: true} departurePortName: {type: string, maxLength: 128, nullable: true} destinationPortCode: {type: string, maxLength: 32, nullable: true} destinationPortName: {type: string, maxLength: 128, nullable: true} destinationWarehouseCode: {type: string, maxLength: 64, nullable: true} destinationWarehouseName: {type: string, maxLength: 128, nullable: true} capacityCbm: {type: number, exclusiveMinimum: true, minimum: 0, nullable: true} maxWeightKg: {type: number, exclusiveMinimum: true, minimum: 0, nullable: true} terminalTruckingCompanyId: {type: string, pattern: '^[1-9][0-9]*$', nullable: true, description: ACTIVE Warehouse Supplier ID validated as TRUCKING} terminalTruckingCompany: {type: string, maxLength: 128, nullable: true, description: Optional consistency hint; persisted name always comes from Warehouse and cannot replace the ID} loadingAt: {type: string, format: date-time, nullable: true} estimatedSailingAt: {type: string, format: date-time, nullable: true} actualSailingAt: {type: string, format: date-time, nullable: true} estimatedPortArrivalAt: {type: string, format: date-time, nullable: true} actualPortArrivalAt: {type: string, format: date-time, nullable: true} estimatedWarehouseArrivalAt: {type: string, format: date-time, nullable: true} actualWarehouseArrivalAt: {type: string, format: date-time, nullable: true} ContainerFullUpdateRequest: type: object required: [version, data] properties: version: {type: integer, minimum: 0} data: {$ref: '#/components/schemas/ContainerSaveRequest'} ContainerTransportUpdateRequest: type: object required: [version, reason] properties: containerNo: {type: string, maxLength: 64, nullable: true, description: 真实集装箱号;PDA任务创建前可空,任务创建后允许替换但禁止清空} voyageNo: {type: string, maxLength: 64, nullable: true} version: {type: integer, minimum: 0} reason: {type: string, maxLength: 500} SmartLoadingPushRequest: type: object required: [expectedVersion] properties: expectedVersion: {type: integer, minimum: 0} ContainerDocumentAttachRequest: type: object required: [fileId, expectedVersion] properties: fileId: {type: string, pattern: '^[1-9][0-9]*$'} expectedVersion: {type: integer, minimum: 0} operationReason: {type: string, maxLength: 500, nullable: true} ContainerDocumentCompleteRequest: type: object required: [expectedVersion] properties: expectedVersion: {type: integer, minimum: 0} operationReason: {type: string, maxLength: 500, nullable: true} ContainerDocumentUploadRequest: type: object required: [file, expectedVersion] additionalProperties: false properties: file: {type: string, format: binary, description: 用户直接选择的清关资料} expectedVersion: {type: integer, minimum: 0, description: 当前文档乐观锁版本,首次上传为 0} operationReason: {type: string, maxLength: 500, nullable: true} MerchantInboundBillingEnvelope: type: object properties: code: {type: string, example: '200'} message: {type: string} data: type: object properties: inbound: {type: object, description: 入库单详情} measurementSummary: {type: object, description: 测量汇总} measurementItems: {type: array, items: {type: object}} weightResults: {type: array, items: {type: object}} measurementImages: {type: array, items: {type: object}} weightImages: {type: array, items: {type: object}} billingBasis: {type: object, description: 计费依据} latestFee: {type: object, nullable: true, description: 兼容保留的最新计费快照} publishedFeeDetail: allOf: - {$ref: '#/components/schemas/PublishedFreightFeeDetail'} nullable: true description: 后台与客户共用的已发布FBA头程运费应收明细 PublishedFreightFeeDetail: type: object description: 审核发布后绑定入库单的FBA头程运费应收明细 properties: id: {type: integer, format: int64, description: 入库费用明细ID} inboundId: {type: integer, format: int64} inboundCode: {type: string} sourceFeeId: {type: integer, format: int64, description: 来源计费快照ID} sourceFeeVersion: {type: integer} sourceFeeNo: {type: string} direction: {type: string, enum: [RECEIVABLE]} businessDate: {type: string, format: date} customerId: {type: integer, format: int64} customerName: {type: string} invoiceTitle: {type: string, nullable: true} channelCode: {type: string, nullable: true} channelName: {type: string, nullable: true} destinationCode: {type: string, nullable: true} destinationName: {type: string, nullable: true} packageCount: {type: integer, nullable: true} actualGrossWeightKg: {type: number, format: double, nullable: true} actualVolumeCbm: {type: number, format: double, nullable: true} volumeWeightKg: {type: number, format: double, nullable: true} chargeableWeightKg: {type: number, format: double, nullable: true} chargeableVolumeCbm: {type: number, format: double, nullable: true} chargeableQuantity: {type: number, format: double} chargeUnit: {type: string, enum: [KG, CBM]} feeTypeName: {type: string, example: FBA头程运费} unitPrice: {type: number, format: double} currency: {type: string, example: CNY} amount: {type: number, format: double, description: 客户应收金额} reviewStatus: {type: string, enum: [APPROVED]} publicationStatus: {type: string, enum: [PUBLISHED]} publishedAt: {type: string, format: date-time} inboundedAt: {type: string, format: date-time, nullable: true} settlementIncluded: {type: boolean, example: true} InboundOrderPageEnvelope: type: object properties: code: {type: string, example: '200'} message: {type: string} data: {$ref: '#/components/schemas/InboundOrderPage'} InboundOrderPage: type: object properties: records: type: array items: {$ref: '#/components/schemas/InboundOrderSummary'} page: {type: integer, minimum: 1} size: {type: integer, minimum: 1} total: {type: integer, format: int64, minimum: 0} pages: {type: integer, format: int64, minimum: 0} InboundOrderSummary: type: object required: [id, tenantId] properties: id: {type: string, pattern: '^[1-9][0-9]*$'} tenantId: {type: string, pattern: '^[1-9][0-9]*$', description: 记录所属客户 ID;字段名为历史兼容,用于详情和处理接口的客户隔离} orderId: {type: string, nullable: true} orderNo: {type: string, nullable: true} inboundCode: {type: string, nullable: true} processStatus: {type: string} customerId: {type: string, nullable: true} companyId: {type: string, nullable: true} taxMode: {type: string, nullable: true, description: 订单报关方式快照} pricingTaxMode: {type: string, enum: [TAX_INCLUDED, TAX_EXCLUDED], nullable: true, description: 下单时选择的报价包税维度快照} InboundMeasurementCallbackRequest: type: object required: [eventNo, externalDetailNo, warehouseCode, inboundCode, inboundDetailId, deviceCode, measuredAt, boxSpecification, packageCount, lengthCm, widthCm, heightCm] properties: eventNo: {type: string, maxLength: 128, pattern: '^[A-Za-z0-9._:-]+$', example: M202608010001} externalDetailNo: {type: string, maxLength: 128, pattern: '^[A-Za-z0-9._:-]+$', example: BOX-20260801-0001} warehouseCode: {type: string, maxLength: 50, example: CN-YW-01} inboundCode: {type: string, maxLength: 40, example: ZBYHK2608140001} inboundDetailId: {type: string, example: '190000000000000001', description: 柏悦订单货物明细ID} deviceCode: {type: string, maxLength: 64, example: DEVICE-01} measuredAt: {type: string, format: date-time, example: '2026-08-01T10:30:00+08:00'} boxSpecification: {type: string, maxLength: 64, example: '40x30x47cm'} lengthCm: {type: number, exclusiveMinimum: true, minimum: 0, example: 50.20} widthCm: {type: number, exclusiveMinimum: true, minimum: 0, example: 40.10} heightCm: {type: number, exclusiveMinimum: true, minimum: 0, example: 30.00} packageCount: {type: integer, minimum: 1, maximum: 1000000, example: 50} unitWeightKg: {type: number, exclusiveMinimum: true, minimum: 0, nullable: true, example: 20} remark: {type: string, maxLength: 500, nullable: true} InboundMeasurementEventResponse: type: object properties: eventNo: {type: string} inboundCode: {type: string} inboundDetailId: {type: string} measurementId: {type: string} measurementItemId: {type: string} imageFileId: {type: string} accepted: {type: boolean} status: {type: string, enum: [RECEIVED]} receivedAt: {type: string, format: date-time} AssignRequest: type: object required: [assigneeId, assigneeName] properties: assigneeId: {type: integer, format: int64} assigneeName: {type: string, maxLength: 64} reason: {type: string, maxLength: 500} ContainerSimulationCreateRequest: type: object required: [channelCode, warehouseId, containerType, capacityCbm, maxWeightKg] properties: channelCode: {type: string, maxLength: 64, description: 当前 ACTIVE 价格簿中的真实物流渠道} warehouseId: {type: integer, format: int64, minimum: 1} containerType: {type: string, maxLength: 32} capacityCbm: {type: number, minimum: 0, exclusiveMinimum: true} maxWeightKg: {type: number, minimum: 0, exclusiveMinimum: true} ContainerOceanBookingLinkRequest: type: object required: [oceanBookingId, requirementId, expectedVersion] properties: oceanBookingId: {type: integer, format: int64, minimum: 1} requirementId: {type: integer, format: int64, minimum: 1} expectedVersion: {type: integer, minimum: 0} ContainerOceanBookingUnlinkRequest: type: object required: [expectedVersion, reason] properties: expectedVersion: {type: integer, minimum: 0} reason: {type: string, minLength: 1, maxLength: 500}