openapi: 3.0.3 info: title: 美国预约筹划货源 version: 1.0.0 description: 读取真实已开船头程货物,不要求预先创建入仓计划,不生成库存、车次或对外确认。 security: - bearerAuth: [] paths: /api/platform/v1/last-leg/appointment-planning/cargo/destinations: get: operationId: listAppointmentCargoDestinations summary: 读取预约货物查询的目的FBA仓选项 description: >- 仅平台;同时要求platform:appointment:list与platform:overseas-warehouse:last-mile-order:list。 与货物分页共用权威启用美国FBA目录,返回去重、按仓码排序的完整选项,不受货物当前分页或关键词限制。 不增加仓库主档读取权限;目录失败返回503,不当作空选项。 responses: '200': description: 业务响应;data为目的FBA仓码数组 content: application/json: schema: type: object properties: code: {type: string} data: {type: array, items: {type: string}, example: [ABE4, ONT8]} '403': {description: 非平台或缺少任一读取权限} '503': {description: 权威目录不可用} /api/platform/v1/last-leg/appointment-planning/sources/resolve-order: get: operationId: resolveOrderAppointmentSource summary: 从实际订单读取规范原货源及预约覆盖 description: >- 仅平台;同时要求platform:appointment:list与platform:overseas-warehouse:last-mile-order:list。 入仓列表只知道订单时使用此只读入口;服务端保留外接登记BOOKING键,其他来源返回ORDER键,核对客户、订单关联及目的地。 前端后续核实和预约命令必须使用返回的source.sourceKey,不将外接来货改写为ORDER别名。 parameters: - {name: orderId, in: query, required: true, schema: {type: integer, format: int64, minimum: 1}} - {name: customerId, in: query, required: true, schema: {type: string, minLength: 1, maxLength: 128}} - {name: destinationKey, in: query, schema: {type: string}} responses: '200': description: 真实原来源和当前活动预约覆盖 content: application/json: schema: type: object properties: code: {type: string} data: type: object properties: source: {$ref: '#/components/schemas/PlanningSource'} coverage: {$ref: '#/components/schemas/Coverage'} '400': {description: 订单、客户或目的地参数无效} '403': {description: 非平台或缺少任一读取权限} '404': {description: 当前客户下来源不存在} '409': {description: 订单与原来源关联变化} '503': {description: 权威目录不可用} /api/platform/v1/last-leg/appointment-planning/external-candidates: get: operationId: listExternalAppointmentCandidates summary: 分页发现已确认承运的美国FBA外接货 description: '仅平台;同时要求platform:appointment:list与platform:overseas-warehouse:last-mile-order:list。按权威启用美国接仓点/FBA目录筛选原拼车登记,排除询价、取消和已完成车次。来货前后仅返回同一BOOKING来源;已有预约仍展示覆盖状态,不能重复加入。整页批量解析及核对覆盖,不逐条HTTP请求。' parameters: - {name: page, in: query, schema: {type: integer, minimum: 1, default: 1}} - {name: size, in: query, schema: {type: integer, minimum: 1, maximum: 100, default: 20}} - {name: warehouseId, in: query, schema: {type: string}} - {name: destination, in: query, schema: {type: string}, description: 美国FBA仓码} - {name: keyword, in: query, schema: {type: string, maxLength: 128}, description: 登记号、车次号、客户名称或来货号} responses: '200': description: 业务响应;data为服务器分页结果 content: application/json: schema: type: object properties: code: {type: string} data: type: object properties: page: {type: integer} size: {type: integer} total: {type: integer, format: int64} pages: {type: integer, format: int64} records: type: array items: type: object properties: customerName: {type: string, nullable: true} tripNo: {type: string} source: {$ref: '#/components/schemas/PlanningSource'} coverage: {$ref: '#/components/schemas/Coverage'} '403': {description: 非平台或缺少读取权限} '503': {description: 仓库目录失败,不能当作无货} /api/platform/v1/last-leg/appointment-planning/appointments: post: operationId: createAppointmentFromSources summary: 在入仓前按稳定货源保存跨客户预约安排 description: >- 仅平台;同时需要platform:appointment:list、platform:overseas-warehouse:last-mile-order:list及platform:overseas-warehouse:inbound-order:fba-appointment。 与入仓明细入口共用同一FBA预约聚合;系统生成AP单号,不创建入仓计划、库存或车次。 PENDING允许日期、时段、外部约号及箱数暂缺。SUCCESS必须有权威目的仓时区、真实货件材料、确定箱数及外部约号和确认时段。 实际客户、起运仓、目的仓及原BOOKING由服务端核对;锁原来源后按剩余未分配箱数建立新安排,分批总量不能超过独立核实或原来源总量。取消仅释放有效预约覆盖,仍需履约的本批分配保留,不重新作为整票余量;历史保留。 命令与主单、明细同事务;同键同操作人同内容重试返回原快照,不受后来编辑影响。异内容或来源占用冲突拒绝。 requestBody: required: true content: application/json: schema: type: object required: [sources, originWarehouseId, fbaWarehouseCode, details, idempotencyKey] properties: originWarehouseId: {type: string, minLength: 1, description: 选择货源时的接仓点,保存时重新核对} fbaWarehouseCode: {type: string, minLength: 1, maxLength: 64, description: 选择货源时的目的仓编码,变化则拒绝保存} sources: type: array minItems: 1 maxItems: 100 items: type: object required: [sourceKey, customerId] properties: sourceKey: {type: string, maxLength: 160} customerId: {type: string, maxLength: 128} destinationKey: {type: string} details: {$ref: './fba-appointment-calendar-v1.yaml#/components/schemas/AppointmentDetails'} idempotencyKey: {type: string, minLength: 1, maxLength: 64} responses: '200': {$ref: './fba-appointment-calendar-v1.yaml#/components/responses/AppointmentResult'} '400': {description: 来源、时段或正式确认材料无效} '403': {description: 权限不足或非平台身份} '404': {description: 来源不存在或客户不匹配} '409': {description: 同键异内容、版本或活动货物占用冲突} '503': {description: 权威仓库目录或时区暂不可用} /api/platform/v1/last-leg/appointment-planning/appointments/unscheduled: get: operationId: listUnscheduledAppointments summary: 分页读取没有日期或时段的预约安排 description: 仅平台,权限platform:appointment:list;排除取消/失败,不用09点或零点占位。 parameters: - {name: page, in: query, schema: {type: integer, minimum: 1, default: 1}} - {name: size, in: query, schema: {type: integer, minimum: 1, maximum: 100, default: 20}} responses: '200': description: code=200时data为分页 content: application/json: schema: type: object properties: code: {type: string} data: type: object properties: records: {type: array, items: {$ref: './fba-appointment-calendar-v1.yaml#/components/schemas/Appointment'}} total: {type: integer} page: {type: integer} size: {type: integer} pages: {type: integer} '403': {description: 无列表权限或非平台身份} /api/platform/v1/last-leg/appointment-planning/batches/preview: post: operationId: previewAppointmentBatch summary: 核对跨客户同仓货源合批资格 description: >- 只读核对,不保存预约安排、不占货、不确认预约。仅平台,同时要求platform:appointment:list与platform:overseas-warehouse:last-mile-order:list。 最多100票,批量查询货源、实际开船节点和美国仓目录,支持ORDER与已确认承运BOOKING混合;同一订单重复别名拒绝。 必须同美国起运仓及同美国FBA目的仓;暂扣、多目的地、已分配完或已取消完成货源拒绝;部分分配后仍可选择剩余量。未知箱数保留待核实,不填0。 未建入仓计划及尚未来货的已确认BOOKING不伪造计划、库存或入仓码;正式确认前必须核实箱数。 未接入权威备妥窗口时返回readinessIncomplete及READY_WINDOW_UNVERIFIED,不拿到港/到仓预测代替。 requestBody: required: true content: application/json: schema: type: object required: [sources] properties: sources: type: array minItems: 1 maxItems: 100 items: type: object required: [sourceKey, customerId] properties: sourceKey: {type: string, maxLength: 160} customerId: {type: string, maxLength: 128} destinationKey: {type: string, description: 可选预期目的地;存在时必须与来源一致} responses: '200': description: code=200时data为本次核对结果,不是已保存单据 content: application/json: schema: type: object properties: code: {type: string} message: {type: string} data: type: object properties: originWarehouseName: {type: string} destinationWarehouseCode: {type: string} destinationTimezone: {type: string, nullable: true, description: 当前目的仓主档IANA时区;主档为空时为null,读取失败返回错误,不使用历史预约快照} sources: {type: array, items: {$ref: '#/components/schemas/PlanningSource'}} cargoVerification: {type: array, items: {$ref: './appointment-cargo-verification-v1.yaml#/components/schemas/CargoContext'}, description: 各原来源当前独立核实及剩余分配量,sources本身仍保留原事实} missingFields: {type: array, items: {type: string}} group: type: object properties: originWarehouseId: {type: string} destinationKey: {type: string} packages: {type: integer, nullable: true} unknownQuantitySources: {type: integer} sourceCount: {type: integer} customerCount: {type: integer} readyFrom: {type: string, format: date-time, nullable: true} readyTo: {type: string, format: date-time, nullable: true} readinessIncomplete: {type: boolean} '400': {description: 来源重复、跨仓、数量不明或不满足合批资格} '403': {description: 权限不足或非平台身份} '404': {description: 来源与客户不匹配} '503': {description: 美国仓目录不可用} /api/platform/v1/last-leg/appointment-planning/containers: get: operationId: appointmentPlanningContainers summary: 分页读取美国整柜提柜与接仓范围 description: >- 仅平台,同时要求platform:appointment:list与platform:overseas-warehouse:last-mile-order:list。 已实际开船且仍有效的FIRST_LEG货物按柜汇总,不限FBA目的地,不要求入仓计划。 仅统计美国接仓点货物;存在未知或非美国接仓点的同柜有效货物时incompleteScope=true,禁止新增整柜跟进。 关键词命中某票时仍返回整柜美国范围数量;任一数量缺失时packages为空。预计到港保留来源快照,不代表实时船司ETA。 parameters: - {name: page, in: query, schema: {type: integer, minimum: 1, default: 1}} - {name: size, in: query, schema: {type: integer, minimum: 1, maximum: 100, default: 20}} - {name: keyword, in: query, schema: {type: string, maxLength: 128}} responses: '200': description: 核对code后读取整柜分页 content: application/json: schema: type: object properties: code: {type: string} message: {type: string} data: type: object properties: records: {type: array, items: {$ref: '#/components/schemas/Container'}} page: {type: integer} size: {type: integer} total: {type: integer} pages: {type: integer} '400': {description: 分页或搜索条件无效} '403': {description: 缺少读取权限或非平台身份} '503': {description: 美国仓库目录不可用} /api/platform/v1/last-leg/appointment-planning/containers/{containerId}: parameters: - {$ref: '#/components/parameters/ContainerId'} - {$ref: '#/components/parameters/ReceivingWarehouse'} get: operationId: resolveAppointmentContainer summary: 核对整柜或接仓点的跟进资格 description: 仅平台,同时要求两项列表权限;接仓点必须属于当前柜次。内部柜次ID仅供关联,不能作为展示柜号。 responses: '200': description: code=200时data为已核实上下文 content: application/json: schema: type: object properties: code: {type: string} data: {$ref: '#/components/schemas/ContainerContext'} '400': {description: 标识无效或接仓点不属于该柜} '403': {description: 缺少读取权限或非平台身份} '404': {description: 未找到有效美国整柜来源} '503': {description: 仓库目录不可用} /api/platform/v1/last-leg/appointment-planning/containers/{containerId}/follow-ups: parameters: - {$ref: '#/components/parameters/ContainerId'} - {$ref: '#/components/parameters/ReceivingWarehouse'} get: operationId: appointmentContainerFollowupHistory summary: 读取整柜或接仓点共享跟进历史 description: 仅平台,同时要求两项列表权限;已有历史在来源离开活动列表后仍可读取,不按客户拆分。 parameters: - {name: page, in: query, schema: {type: integer, minimum: 1, default: 1}} - {name: size, in: query, schema: {type: integer, minimum: 1, maximum: 100, default: 20}} responses: '200': {$ref: '#/paths/~1api~1platform~1v1~1last-leg~1appointment-planning~1sources~1{sourceKey}~1follow-ups/get/responses/200'} '400': {description: 标识或分页无效} '403': {description: 缺少读取权限或非平台身份} '404': {description: 无历史且来源不存在} post: operationId: appendAppointmentContainerFollowup summary: 追加整柜提柜联系或指定仓库接仓联系 description: >- 仅平台,两项列表权限及platform:appointment:followup均必需。 未传warehouseId只接受DRAYAGE_CONTACT或TERMINAL_APPOINTMENT;传入时只接受WAREHOUSE_RECEIVING。 服务端产生共享来源键,不接收customerId或操作者字段;同柜跨客户共用一份历史。 版本、持久化幂等和跟进日期规则与货源跟进相同,不产生正式预约或执行结果。 requestBody: required: true content: application/json: schema: type: object required: [stage, expectedVersion, idempotencyKey, result] properties: stage: {type: string, enum: [DRAYAGE_CONTACT, TERMINAL_APPOINTMENT, WAREHOUSE_RECEIVING]} expectedVersion: {type: integer, minimum: 0} idempotencyKey: {type: string, minLength: 1, maxLength: 64} contactedParty: {type: string, maxLength: 200, nullable: true} result: {type: string, minLength: 1, maxLength: 1000} nextAction: {type: string, maxLength: 500, nullable: true} followupDate: {type: string, format: date, nullable: true} responses: '200': {$ref: '#/paths/~1api~1platform~1v1~1last-leg~1appointment-planning~1sources~1{sourceKey}~1follow-ups/post/responses/200'} '400': {description: 事项与范围不符或参数无效} '403': {description: 缺少权限或非平台身份} '404': {description: 来源不存在} '409': {description: 范围待核对、版本或幂等内容冲突} '503': {description: 仓库目录不可用} /api/platform/v1/last-leg/appointment-planning/cargo: get: operationId: appointmentPlanningCargo summary: 分页读取美国FBA筹划货源 description: >- 仅平台身份;同时要求platform:appointment:list与platform:overseas-warehouse:last-mile-order:list。 从公共仓库目录核对美国接仓点与美国FBA目的仓;目录失败返回错误,不冒充空结果。 当前读取FIRST_LEG货物与已确认开船或更晚的实际船节点,未建计划可见;已有计划不重复计数。 预计时间来自尾程订单保存的来源快照,不代表实时船司ETA;shippingObservedAt仅是实际节点观察时间。 sourcePalletCount是头程预报托数,不是海外实测托位。其他货源由后续正式合同接入,不能从本接口推断已支持。 parameters: - {name: page, in: query, schema: {type: integer, minimum: 1, default: 1}} - {name: size, in: query, schema: {type: integer, minimum: 1, maximum: 100, default: 20}} - {name: warehouseId, in: query, schema: {type: string}, description: 美国接仓点主档ID,仅用于关联} - {name: destination, in: query, schema: {type: string}, description: 美国FBA仓编码} - {name: keyword, in: query, schema: {type: string, maxLength: 128}, description: 入仓码、柜号或客户名称} responses: '200': description: 业务响应,必须核对code content: application/json: schema: type: object properties: code: {type: string} message: {type: string} data: type: object properties: records: {type: array, items: {$ref: '#/components/schemas/Cargo'}} page: {type: integer} size: {type: integer} total: {type: integer} pages: {type: integer} '400': {description: 查询参数或美国范围无效} '403': {description: 无来源读取权限或非平台身份} '503': {description: 仓库目录暂不可用,不能证明美国货源范围} /api/platform/v1/last-leg/appointment-planning/sources/resolve: get: operationId: resolveAppointmentPlanningSource summary: 核对深链来源并读取预约覆盖 description: >- 仅平台;同时要求platform:appointment:list与platform:overseas-warehouse:last-mile-order:list。 依据真实客户、货源与美国仓目录核对来源,URL参数不能授予预约资格。 ORDER接受已开船头程FBA货和没有外接登记的真实仓储转运;有原登记必须使用BOOKING稳定身份,确认承运后尚未生成入仓单也能跟进。 此查询不建单、不确认预约。当前覆盖读取已有FBA预约,尚未生成货物时返回SOURCE_NOT_MATERIALIZED。 parameters: - {name: sourceKey, in: query, required: true, schema: {type: string, maxLength: 160}, description: 'ORDER:<内部关联ID>或BOOKING:<登记号>,非用户展示编号'} - {name: customerId, in: query, required: true, schema: {type: string, maxLength: 128}} - {name: destinationKey, in: query, schema: {type: string}, description: 'FBA:<仓编码>,传入后必须与真实来源一致'} responses: '200': description: code=200时data包含已核实来源和预约覆盖 content: application/json: schema: type: object properties: code: {type: string} message: {type: string} data: type: object properties: source: {$ref: '#/components/schemas/PlanningSource'} coverage: {$ref: '#/components/schemas/Coverage'} '400': {description: 非美国FBA来源、尚未开船或目的地变化} '403': {description: 无读取权限或非平台身份} '404': {description: 客户与来源不匹配或来源不存在} '503': {description: 仓库目录不可用} /api/platform/v1/last-leg/appointment-planning/sources/{sourceKey}/follow-ups: parameters: - {name: sourceKey, in: path, required: true, schema: {type: string, maxLength: 160}, description: 'ORDER或BOOKING稳定来源键,路径须URL编码'} - {name: customerId, in: query, required: true, schema: {type: string, maxLength: 128}} get: summary: 分页读取分事项跟进历史与最新版本 description: 仅平台;同时要求platform:appointment:list与platform:overseas-warehouse:last-mile-order:list。已有跟进历史在货物完成后仍可读取。 parameters: - {name: page, in: query, schema: {type: integer, minimum: 1, default: 1}} - {name: size, in: query, schema: {type: integer, minimum: 1, maximum: 100, default: 20}} responses: '200': description: code=200时data含history分页和按stage排列的versions;createdAt为UTC时间点,followupDate为业务地点当地日期。 content: application/json: schema: type: object properties: code: {type: string} message: {type: string} data: type: object properties: history: type: object properties: records: {type: array, items: {$ref: '#/components/schemas/FollowupRecord'}} page: {type: integer} size: {type: integer} total: {type: integer} pages: {type: integer} versions: {type: object, additionalProperties: {type: integer, minimum: 0}, description: 四个事项各自的最新版本} '403': {description: 无读取权限或非平台身份} '404': {description: 无历史且来源不具备美国筹划资格} post: summary: 追加人工联系结果与下次跟进 description: >- 仅平台;除读取权限外要求platform:appointment:followup。 跟进人来自登录主体,不接受客户端操作者ID。核实来源后按stage版本追加,以持久化唯一约束防止并发覆盖;幂等键持久化,同键同操作者同内容返回原记录,异内容拒绝。 不要求已创建入仓计划、外部预约号或司机,不更新正式预约、库存和车次。设置followupDate时nextAction必填。 requestBody: required: true content: application/json: schema: type: object required: [stage, expectedVersion, idempotencyKey, result] properties: stage: {type: string, enum: [FBA_RECEIVING], description: 提柜及接仓统一使用整柜接口,不按客户货源重复登记} expectedVersion: {type: integer, minimum: 0, description: 初次为0,后续取history.versions中对应事项版本} idempotencyKey: {type: string, maxLength: 64} contactedParty: {type: string, maxLength: 200, nullable: true} result: {type: string, minLength: 1, maxLength: 1000} nextAction: {type: string, maxLength: 500, nullable: true} followupDate: {type: string, format: date, nullable: true} responses: '200': description: data为原记录或新增记录 content: application/json: schema: type: object properties: code: {type: string} message: {type: string} data: {$ref: '#/components/schemas/FollowupRecord'} '400': {description: 无效事项或缺少下一步安排} '403': {description: 无写权限或非平台身份} '404': {description: 来源不存在或不可筹划} '409': {description: 版本冲突或同键异内容} components: parameters: ContainerId: {name: containerId, in: path, required: true, schema: {type: string, pattern: '^[A-Za-z0-9_-]{1,64}$'}} ReceivingWarehouse: {name: warehouseId, in: query, schema: {type: string, pattern: '^[0-9]{1,20}$'}, description: 不传表示整柜,传入表示该柜实际接仓点} securitySchemes: bearerAuth: {type: http, scheme: bearer, bearerFormat: JWT} schemas: Container: type: object properties: containerBookingId: {type: string} containerNo: {type: string, nullable: true} orderCount: {type: integer} customerCount: {type: integer} packages: {type: integer, nullable: true} unknownPackageOrders: {type: integer} earliestArrivalAt: {type: string, nullable: true} latestArrivalAt: {type: string, nullable: true} standardNode: {type: string} sourceVersion: {type: string, nullable: true} incompleteScope: {type: boolean} warehouses: type: array items: type: object properties: containerBookingId: {type: string} warehouseId: {type: string} warehouseName: {type: string} orderCount: {type: integer} customerCount: {type: integer} ContainerContext: type: object properties: containerBookingId: {type: string} containerNo: {type: string, nullable: true} warehouseId: {type: string, nullable: true} warehouseName: {type: string, nullable: true} canFollowup: {type: boolean} blockReasons: {type: array, items: {type: string}} FollowupRecord: type: object properties: stage: {type: string, enum: [DRAYAGE_CONTACT, TERMINAL_APPOINTMENT, WAREHOUSE_RECEIVING, FBA_RECEIVING]} version: {type: integer, minimum: 1} contactedParty: {type: string, nullable: true} result: {type: string} nextAction: {type: string, nullable: true} followupDate: {type: string, format: date, nullable: true} operatorName: {type: string, nullable: true} createdAt: {type: string, format: date-time, description: UTC时间点} PlanningSource: type: object properties: sourceKey: {type: string} customerId: {type: string} orderId: {type: string, nullable: true} inboundCode: {type: string, nullable: true} bookingNo: {type: string, nullable: true} containerNo: {type: string, nullable: true} originWarehouseId: {type: string} originWarehouseName: {type: string} destinationKey: {type: string} destinationWarehouseCode: {type: string} packages: {type: integer, nullable: true, description: 未知数量保持空值,不能替换为0} sourceVersion: {type: string, nullable: true} canFollowup: {type: boolean} blockReasons: {type: array, items: {type: string}} Coverage: type: object properties: customerId: {type: string} sourceKey: {type: string} destinationKey: {type: string} status: {type: string, enum: [SOURCE_NOT_FOUND, SOURCE_NOT_MATERIALIZED, SOURCE_NOT_READY, DESTINATION_MISMATCH, UNASSIGNED, PARTIAL, PENDING, CONFIRMED, REVIEW_REQUIRED]} sourceVersion: {type: string, nullable: true} changeReviewRequired: {type: boolean} missingFields: {type: array, items: {type: string}, description: 缺项或待核实原因代码,客户端需显示业务文案} appointments: type: array items: type: object properties: appointmentNo: {type: string} confirmationNo: {type: string, nullable: true} status: {type: string} confirmedAt: {type: string, nullable: true} confirmedEndAt: {type: string, nullable: true} timezone: {type: string, nullable: true, description: 旧预约未记录时区时为空并返回缺项,不能按浏览器时区推定} Cargo: type: object properties: sourceKey: {type: string, description: 稳定ORDER来源键,不因生成入仓计划而变化;非展示编号} orderId: {type: string} customerId: {type: string} customerName: {type: string, nullable: true} inboundCode: {type: string, nullable: true} containerBookingId: {type: string} containerNo: {type: string, nullable: true} originWarehouseId: {type: string} originWarehouseCode: {type: string} originWarehouseName: {type: string} destinationWarehouseCode: {type: string} packages: {type: integer, nullable: true} receivedPackages: {type: integer, nullable: true} shippedPackages: {type: integer, nullable: true} sourcePalletCount: {type: integer, nullable: true} volumeCbm: {type: number, nullable: true} estimatedArrivalAt: {type: string, nullable: true, description: 来源预计到港快照,保留原业务当地时间;不得按浏览器时区转换} estimatedWarehouseAt: {type: string, nullable: true, description: 已保存预计到仓,不因本查询改变} orderVersion: {type: integer} standardNode: {type: string} shippingVersion: {type: string, nullable: true} shippingObservedAt: {type: string, nullable: true} shippingProvider: {type: string, nullable: true} hasInboundPlan: {type: boolean} hasActiveAppointment: {type: boolean} detained: {type: boolean} mixedDestinations: {type: boolean, description: 存在其他目的地明细时,不能按整票默认为同仓货物}