This commit is contained in:
2026-10-19 10:23:09 +08:00
parent 45fbd6eff8
commit e8f6bb32dc
30 changed files with 1612 additions and 49 deletions
@@ -1086,6 +1086,7 @@ CREATE TABLE ll_fleet_vehicle_record_history (
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='车辆运维记录不可变历史';
CREATE TABLE ll_fleet_trip (
trip_purpose VARCHAR(16) NOT NULL DEFAULT 'DELIVERY' COMMENT 'DELIVERY出库派送 PICKUP上门取货入仓',
id BIGINT NOT NULL COMMENT '内部主键',
trip_no VARCHAR(32) NOT NULL COMMENT '车次业务编号',
idempotency_key VARCHAR(128) NOT NULL COMMENT '创建请求幂等键',
@@ -4324,3 +4325,56 @@ CREATE TABLE IF NOT EXISTS ll_compact_number_clock (
-- 库存转运无需再次预约入仓;目标库:lastleg-service 所属数据库。可重复执行。
SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_warehouse_transfer_order' AND column_name='estimated_arrival_at' AND is_nullable='NO'), 'ALTER TABLE ll_warehouse_transfer_order MODIFY COLUMN estimated_arrival_at DATETIME(3) NULL COMMENT ''预计入仓时间,库存转运不填写''', 'SELECT 1'));
PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
-- 仓储转运上门取货,共用车辆和司机资源。
CREATE TABLE IF NOT EXISTS ll_fleet_pickup_plan (
trip_no VARCHAR(64) NOT NULL COMMENT '现有车辆车次号',
request_hash VARCHAR(64) NOT NULL COMMENT '创建来源与计划摘要',
PRIMARY KEY(trip_no)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='仓储转运取货计划';
CREATE TABLE IF NOT EXISTS ll_fleet_pickup_task (
transfer_order_id BIGINT NOT NULL COMMENT '原仓储转运单ID,一票唯一',
customer_id VARCHAR(64) NOT NULL COMMENT '客户隔离键',
transfer_no VARCHAR(64) NOT NULL COMMENT '原仓储转运单号',
trip_no VARCHAR(64) NULL COMMENT '当前取货车次,解除后为空',
status VARCHAR(32) NOT NULL COMMENT 'WAITING待安排 ASSIGNED已安排 PICKED已取货 ARRIVED已送仓',
actual_packages INT NULL COMMENT '司机实提箱数,非仓库实收',
picked_at DATETIME NULL COMMENT '实际确认取货UTC时间',
arrived_at DATETIME NULL COMMENT '实际确认送仓UTC时间',
evidence_json LONGTEXT NULL COMMENT '本票已确认凭证元数据',
remark VARCHAR(1000) NULL COMMENT '取货备注',
updated_at DATETIME NOT NULL COMMENT '最近修改UTC时间',
PRIMARY KEY(transfer_order_id),
UNIQUE KEY uk_pickup_source(customer_id,transfer_no),
KEY idx_pickup_trip(trip_no,status)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='仓储转运逐票取货执行';
CREATE TABLE IF NOT EXISTS ll_fleet_pickup_command (
trip_no VARCHAR(64) NOT NULL COMMENT '取货车次号',
request_key VARCHAR(128) NOT NULL COMMENT '成功命令幂等键',
request_hash VARCHAR(64) NOT NULL COMMENT '命令动作与内容摘要',
PRIMARY KEY(trip_no,request_key)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='取货命令幂等记录';
CREATE TABLE IF NOT EXISTS ll_fleet_pickup_upload (
transfer_order_id BIGINT NOT NULL COMMENT '凭证所属原单',
trip_no VARCHAR(64) NOT NULL COMMENT '凭证所属车次',
file_id BIGINT NOT NULL COMMENT '平台归属文件ID',
PRIMARY KEY(file_id),
KEY idx_pickup_upload_source(transfer_order_id,trip_no)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='取货上传凭证精确单据归属';
CREATE TABLE IF NOT EXISTS ll_fleet_pickup_history (
id BIGINT NOT NULL AUTO_INCREMENT COMMENT '操作流水',
customer_id VARCHAR(64) NOT NULL COMMENT '客户隔离键',
transfer_no VARCHAR(64) NOT NULL COMMENT '原转运单号',
trip_no VARCHAR(64) NOT NULL COMMENT '当时取货车次号',
action VARCHAR(32) NOT NULL COMMENT '安排解除取货到仓动作',
operator_id VARCHAR(64) NOT NULL COMMENT '操作人',
occurred_at DATETIME NOT NULL COMMENT '操作UTC时间',
detail VARCHAR(2000) NULL COMMENT '操作说明',
PRIMARY KEY(id),
KEY idx_pickup_history_source(customer_id,transfer_no,id),
KEY idx_pickup_history_trip(trip_no,id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='取货不可变操作历史';
@@ -0,0 +1,159 @@
openapi: 3.0.3
info:
title: 仓储转运上门取货与车辆排车
version: 1.0.0
description: |
审核通过的 WAREHOUSE_PICKUP 外来转运单自动进入待取货候选;客户自送、库存转运、未审核及已完成测量单不进入。
入仓取货和出库派送共用 ll_fleet_trip 及车辆司机占用,tripPurpose 分别为 PICKUP / DELIVERY,禁止混用库存出库命令、外接预订及余位发布。
同车多单必须送往同一公共海外仓,原客户归属与转运单保留,不重复生成尾程单、不修改预报箱唛或库存。
取货确认必须填写实提箱数并引用本票本车已上传的 PDF/JPG/PNG 凭证;送仓后开放 PDA 清点。
司机实提箱数不替代仓库实收。全部送仓后结案运输,车辆和司机仍通过原 resources/release 分别结束任务。
取消 PLANNED 车次原子释放资源和未执行取货关联,原单重新成为候选;已发车不可取消。
写命令具备车次版本及请求键,同键同内容重放不重复写入,同键不同内容拒绝。普通业务返回 HTTP 200 + body.code。
security:
- bearerAuth: []
paths:
/api/platform/v1/last-leg/fleet/pickups:
get:
summary: 分页读取待取货需求
description: 仅平台主体,权限 platform:fleet:trip: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}}
- {name: keyword, in: query, schema: {type: string, maxLength: 128}}
responses: {'200': {description: ApiResponse 分页候选}}
post:
summary: 选单创建同仓多点取货车次
description: 仅平台主体,权限 platform:fleet:trip:create;创建后沿原 assignment 接口分配车辆司机,复用原 plan 接口调整时间,取货路线由原单地址确定。
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [sources, plannedDepartureAt, expectedDriverEndAt, idempotencyKey]
properties:
sources:
type: array
minItems: 1
maxItems: 100
items:
type: object
required: [customerId, transferNo]
properties:
customerId: {type: string}
transferNo: {type: string}
plannedDepartureAt: {type: string, format: date-time}
expectedDriverEndAt: {type: string, format: date-time}
remark: {type: string, maxLength: 1000}
idempotencyKey: {type: string, minLength: 8, maxLength: 128}
responses: {'200': {description: ApiResponse FleetTripModels.Detail,trip.tripPurpose=PICKUP}}
/api/platform/v1/last-leg/fleet/pickups/{tripNo}/tasks:
parameters: [{$ref: '#/components/parameters/tripNo'}]
get:
summary: 读取本车所有逐票取货任务
description: 仅平台主体,权限 platform:fleet:trip:list。任务状态 WAITING / ASSIGNED / PICKED / ARRIVED;凭证按本票返回,不暴露文件公共直链。
responses: {'200': {description: ApiResponse Task 数组}}
/api/platform/v1/last-leg/fleet/pickups/{tripNo}/depart:
parameters: [{$ref: '#/components/parameters/tripNo'}]
post:
summary: 确认取货发车
description: 仅平台主体,权限 platform:fleet:trip:assign。复验车辆状态、司机资格、任务覆盖和两类资源占用。只推进车次 IN_TRANSIT,不触发出库。
requestBody: {$ref: '#/components/requestBodies/command'}
responses: {'200': {description: ApiResponse FleetTripModels.Detail}}
/api/platform/v1/last-leg/fleet/pickups/{tripNo}/tasks/{customerId}/{transferNo}/files:
parameters: [{$ref: '#/components/parameters/tripNo'}, {$ref: '#/components/parameters/customerId'}, {$ref: '#/components/parameters/transferNo'}]
post:
summary: 上传本票取货凭证
description: 仅平台主体,权限 platform:fleet:trip:edit。最大10MiB,File沿 FLEET_TRIP_OPERATION_EVIDENCE / tenantId=0 / businessId=车次ID校验格式;Lastleg另存当前票上传归属。
requestBody:
required: true
content:
multipart/form-data:
schema:
type: object
required: [file]
properties: {file: {type: string, format: binary}}
responses: {'200': {description: ApiResponse 文件ID、名称、类型和大小}}
/api/platform/v1/last-leg/fleet/pickups/{tripNo}/tasks/{customerId}/{transferNo}/picked:
parameters: [{$ref: '#/components/parameters/tripNo'}, {$ref: '#/components/parameters/customerId'}, {$ref: '#/components/parameters/transferNo'}]
post:
summary: 逐票确认取货
description: 仅平台主体,权限 platform:fleet:trip:edit。车次已发车且任务 ASSIGNED,核对本票本车上传归属后转 PICKED,实提箱数不覆盖预报。
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [recordVersion, idempotencyKey, actualPackages, evidenceFileIds]
properties:
recordVersion: {type: integer, format: int64, minimum: 0}
idempotencyKey: {type: string, minLength: 8, maxLength: 128}
actualPackages: {type: integer, minimum: 1, maximum: 5000}
evidenceFileIds: {type: array, minItems: 1, maxItems: 10, items: {type: string, description: 正Long文件ID}}
remark: {type: string, maxLength: 1000}
responses: {'200': {description: ApiResponse 当前本车 Task 数组;车次版本已更新}}
/api/platform/v1/last-leg/fleet/pickups/{tripNo}/tasks/{customerId}/{transferNo}/arrive:
parameters: [{$ref: '#/components/parameters/tripNo'}, {$ref: '#/components/parameters/customerId'}, {$ref: '#/components/parameters/transferNo'}]
post:
summary: 确认本票送达海外仓
description: 仅平台主体,权限 platform:fleet:trip:edit。仅 PICKED 可以转 ARRIVED;开放原 PDA 清点,不直接完成测量或入库。
requestBody: {$ref: '#/components/requestBodies/command'}
responses: {'200': {description: ApiResponse 当前本车 Task 数组;车次版本已更新}}
/api/platform/v1/last-leg/fleet/pickups/{tripNo}/complete:
parameters: [{$ref: '#/components/parameters/tripNo'}]
post:
summary: 全车送仓后结案取货运输
description: 仅平台主体,权限 platform:fleet:trip:complete。全车非空且均 ARRIVED,车次转 COMPLETED;原单继续仓库作业,不自动释放车和司机。
requestBody: {$ref: '#/components/requestBodies/command'}
responses: {'200': {description: ApiResponse FleetTripModels.Detail}}
/api/merchant/v1/last-leg/warehouse-transfers/{transferNo}/pickup-progress:
parameters: [{$ref: '#/components/parameters/transferNo'}]
get:
summary: 客户读取本企业本票取货安排
description: 权限 merchant:overseas-warehouse:transfer:list;客户ID从认证上下文取得,自送及库存来源返回null。仅返回本票运输与凭证,不授予整车读取。
responses: {'200': {description: ApiResponse Task或null}}
/api/platform/v1/last-leg/warehouse-transfers/{transferNo}/pickup-progress:
parameters:
- {$ref: '#/components/parameters/transferNo'}
- {name: customerId, in: query, required: true, schema: {type: string}}
get:
summary: 平台尾程详情读取本票取货安排
description: 仅平台主体,权限 platform:overseas-warehouse:last-mile-order:list。
responses: {'200': {description: ApiResponse Task或null}}
/api/merchant/v1/last-leg/warehouse-transfers/{transferNo}/pickup-evidence/{fileId}:
parameters: [{$ref: '#/components/parameters/transferNo'}, {$ref: '#/components/parameters/fileId'}]
get:
summary: 客户下载本票已确认凭证
description: 权限 merchant:overseas-warehouse:transfer:list;复验客户、原订单、文件引用及File归属,no-store。
responses: {'200': {description: application/octet-stream 文件;业务错误仍为ApiResponse}}
/api/platform/v1/last-leg/warehouse-transfers/{transferNo}/pickup-evidence/{fileId}:
parameters:
- {$ref: '#/components/parameters/transferNo'}
- {$ref: '#/components/parameters/fileId'}
- {name: customerId, in: query, required: true, schema: {type: string}}
get:
summary: 平台下载本票已确认凭证
description: 仅平台主体,权限 platform:fleet:trip:list 或 platform:overseas-warehouse:last-mile-order:list;复验本票文件引用,不因共车授权其他客户文件。
responses: {'200': {description: application/octet-stream 文件;业务错误仍为ApiResponse}}
components:
securitySchemes:
bearerAuth: {type: http, scheme: bearer, bearerFormat: JWT}
parameters:
tripNo: {name: tripNo, in: path, required: true, schema: {type: string}}
customerId: {name: customerId, in: path, required: true, schema: {type: string}}
transferNo: {name: transferNo, in: path, required: true, schema: {type: string}}
fileId: {name: fileId, in: path, required: true, schema: {type: integer, format: int64}}
requestBodies:
command:
required: true
content:
application/json:
schema:
type: object
required: [recordVersion, idempotencyKey, reason]
properties:
recordVersion: {type: integer, format: int64, minimum: 0}
idempotencyKey: {type: string, minLength: 8, maxLength: 128}
reason: {type: string, maxLength: 1000}
@@ -27,6 +27,36 @@ info:
security:
- bearerAuth: []
paths:
/api/platform/v1/last-leg/fleet/trips/{tripNo}/resource-occupancy:
get:
summary: 批量读取当前下拉候选的实际车辆或司机占用
description: 仅平台且须platform:fleet:trip:list。本车已占用仍可保留;别车占用不可选。只反映占用,不代表证照或容量合格,不产生资源预留,保存分配时再次复验。
parameters:
- {in: path, name: tripNo, required: true, schema: {type: string}}
- {in: query, name: kind, required: true, schema: {type: string, enum: [VEHICLE, DRIVER]}}
- in: query
name: resourceIds
required: true
style: form
explode: false
schema: {type: array, minItems: 1, maxItems: 100, items: {type: string, pattern: '^[1-9][0-9]*$'}}
responses:
'200':
description: 标准业务响应;data为候选占用数组,HTTP200亦需检查code。业务无权限、参数错误及资源不存在沿统一错误码。
content:
application/json:
schema:
type: object
properties:
code: {type: string}
data:
type: array
items:
type: object
properties:
resourceId: {type: string}
occupiedTripNo: {type: string, nullable: true}
available: {type: boolean}
/api/platform/v1/last-leg/fleet/trips/calendar:
get:
summary: 读取美国车次用车日历来源
@@ -746,6 +776,7 @@ components:
type: object
properties:
tripNo: {type: string}
tripPurpose: {type: string, enum: [DELIVERY, PICKUP], description: 派送车次或入仓取货车次}
status: {type: string, enum: [PLANNED, IN_TRANSIT, COMPLETED, CANCELLED]}
warehouseId: {type: string}
warehouseCode: {type: string}