二期内容提交

This commit is contained in:
2026-08-04 10:07:45 +08:00
parent b2b4ff429d
commit 57b54a5236
184 changed files with 13195 additions and 5 deletions
@@ -23,6 +23,11 @@ spring:
baiyue:
security:
jwt-secret: ${JWT_SECRET:change-me-to-at-least-32-bytes-long-before-start}
firstleg:
inbound:
allow-rejected-measurement: false
default-pallet-capacity-cbm: ${DEFAULT_PALLET_CAPACITY_CBM:1.200000}
reservation-ttl: ${PALLET_RESERVATION_TTL:30m}
management.endpoints.web.exposure.include: health,info,prometheus
logging:
@@ -0,0 +1,52 @@
SET NAMES utf8mb4;
USE baiyue_firstleg;
-- 二期 FBA 计费增量迁移。通过 information_schema 判断,允许重复部署。
DROP PROCEDURE IF EXISTS phase2_fba_add_column;
DELIMITER $$
CREATE PROCEDURE phase2_fba_add_column(IN p_table VARCHAR(64), IN p_column VARCHAR(64), IN p_definition TEXT)
BEGIN
IF NOT EXISTS (
SELECT 1 FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = p_table AND COLUMN_NAME = p_column
) THEN
SET @sql = CONCAT('ALTER TABLE `', p_table, '` ADD COLUMN `', p_column, '` ', p_definition);
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
END IF;
END$$
DELIMITER ;
CALL phase2_fba_add_column('fl_inbound_order', 'carrier_id', "BIGINT UNSIGNED NULL COMMENT '计费船司逻辑ID' AFTER channel_name");
CALL phase2_fba_add_column('fl_inbound_order', 'carrier_name', "VARCHAR(128) NULL COMMENT '计费船司名称快照' AFTER carrier_id");
CALL phase2_fba_add_column('fl_carrier_fba_rate', 'business_type', "VARCHAR(32) NOT NULL DEFAULT 'FBA' COMMENT '业务或计费类型' AFTER origin_warehouse_name");
CALL phase2_fba_add_column('fl_customer_charge_rule', 'version', "INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号' AFTER deleted");
CALL phase2_fba_add_column('fl_carrier_fba_rate', 'version', "INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号' AFTER deleted");
CALL phase2_fba_add_column('fl_inbound_fee', 'rule_code', "VARCHAR(50) NOT NULL DEFAULT '' COMMENT '客户规则编码快照' AFTER carrier_rate_version");
CALL phase2_fba_add_column('fl_inbound_fee', 'customer_id', "BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '客户ID快照' AFTER rule_code");
CALL phase2_fba_add_column('fl_inbound_fee', 'customer_name', "VARCHAR(128) NOT NULL DEFAULT '' COMMENT '客户名称快照' AFTER customer_id");
CALL phase2_fba_add_column('fl_inbound_fee', 'business_type', "VARCHAR(32) NOT NULL DEFAULT '' COMMENT '业务类型快照' AFTER customer_name");
CALL phase2_fba_add_column('fl_inbound_fee', 'tax_mode', "VARCHAR(24) NULL COMMENT '包税模式快照' AFTER business_type");
CALL phase2_fba_add_column('fl_inbound_fee', 'rounding_scale', "TINYINT UNSIGNED NOT NULL DEFAULT 3 COMMENT '计费量保留位数快照' AFTER tax_mode");
CALL phase2_fba_add_column('fl_inbound_fee', 'rounding_mode', "VARCHAR(24) NOT NULL DEFAULT 'CEILING' COMMENT '舍入方式快照' AFTER rounding_scale");
CALL phase2_fba_add_column('fl_inbound_fee', 'rule_effective_from', "DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '客户规则生效开始快照' AFTER rounding_mode");
CALL phase2_fba_add_column('fl_inbound_fee', 'rule_effective_to', "DATETIME(3) NULL COMMENT '客户规则生效结束快照' AFTER rule_effective_from");
CALL phase2_fba_add_column('fl_inbound_fee', 'rate_code', "VARCHAR(50) NOT NULL DEFAULT '' COMMENT '船司价格编码快照' AFTER rule_effective_to");
CALL phase2_fba_add_column('fl_inbound_fee', 'origin_warehouse_id', "BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '起运仓ID快照' AFTER rate_code");
CALL phase2_fba_add_column('fl_inbound_fee', 'origin_warehouse_name', "VARCHAR(128) NOT NULL DEFAULT '' COMMENT '起运仓名称快照' AFTER origin_warehouse_id");
CALL phase2_fba_add_column('fl_inbound_fee', 'channel_code', "VARCHAR(64) NOT NULL DEFAULT '' COMMENT '渠道编码快照' AFTER origin_warehouse_name");
CALL phase2_fba_add_column('fl_inbound_fee', 'carrier_id', "BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '船司ID快照' AFTER channel_code");
CALL phase2_fba_add_column('fl_inbound_fee', 'carrier_name', "VARCHAR(128) NOT NULL DEFAULT '' COMMENT '船司名称快照' AFTER carrier_id");
CALL phase2_fba_add_column('fl_inbound_fee', 'destination_code', "VARCHAR(32) NOT NULL DEFAULT '' COMMENT 'FBA仓编码快照' AFTER carrier_name");
CALL phase2_fba_add_column('fl_inbound_fee', 'rate_effective_from', "DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '价格生效开始快照' AFTER destination_code");
CALL phase2_fba_add_column('fl_inbound_fee', 'rate_effective_to', "DATETIME(3) NULL COMMENT '价格生效结束快照' AFTER rate_effective_from");
CALL phase2_fba_add_column('fl_inbound_idempotency', 'result_resource_id', "BIGINT UNSIGNED NULL COMMENT '成功结果资源ID' AFTER result_json");
DROP PROCEDURE IF EXISTS phase2_fba_add_column;
-- 旧索引可能缺少业务类型、船司和结束时间;用新索引名并存,避免破坏线上依赖。
SET @idx_exists = (SELECT COUNT(*) FROM information_schema.STATISTICS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'fl_carrier_fba_rate' AND INDEX_NAME = 'idx_fba_rate_unique_match');
SET @sql = IF(@idx_exists = 0,
'ALTER TABLE fl_carrier_fba_rate ADD INDEX idx_fba_rate_unique_match (tenant_id, origin_warehouse_id, business_type, channel_code, carrier_id, destination_code, charge_unit, status, effective_from, effective_to)',
'SELECT 1');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
@@ -0,0 +1,90 @@
-- 二期入库测量与 OCR 维护增量(只增不删,可重复执行)。
-- 执行顺序:phase2-inbound-schema.sql 后执行本文件;已有二期库只需执行本文件。
SET NAMES utf8mb4;
USE baiyue_firstleg;
DROP PROCEDURE IF EXISTS phase2_measure_add_column;
DELIMITER $$
CREATE PROCEDURE phase2_measure_add_column(
IN p_table_name VARCHAR(64),
IN p_column_name VARCHAR(64),
IN p_column_definition TEXT
)
BEGIN
IF NOT EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_schema = DATABASE()
AND table_name = p_table_name
AND column_name = p_column_name
) THEN
SET @phase2_measure_sql = CONCAT(
'ALTER TABLE `', REPLACE(p_table_name, '`', '``'),
'` ADD COLUMN `', REPLACE(p_column_name, '`', '``'), '` ', p_column_definition
);
PREPARE phase2_measure_statement FROM @phase2_measure_sql;
EXECUTE phase2_measure_statement;
DEALLOCATE PREPARE phase2_measure_statement;
END IF;
END$$
DELIMITER ;
CALL phase2_measure_add_column('fl_inbound_measurement', 'original_document_no',
'VARCHAR(64) NULL COMMENT ''原单据号码快照'' AFTER raw_payload_json');
CALL phase2_measure_add_column('fl_inbound_measurement', 'original_gross_weight_kg',
'DECIMAL(18,3) NULL COMMENT ''原单据毛重kg'' AFTER original_document_no');
CALL phase2_measure_add_column('fl_inbound_measurement', 'original_volume_cbm',
'DECIMAL(18,4) NULL COMMENT ''原单据体积或材积m3'' AFTER original_gross_weight_kg');
CALL phase2_measure_add_column('fl_inbound_measurement', 'actual_goods_name',
'VARCHAR(255) NULL COMMENT ''实际品名'' AFTER original_volume_cbm');
CALL phase2_measure_add_column('fl_inbound_measurement', 'volume_weight_kg',
'DECIMAL(18,3) NULL COMMENT ''本批次总材积重kg'' AFTER total_volume_cbm');
CALL phase2_measure_add_column('fl_inbound_measurement', 'version',
'INT NOT NULL DEFAULT 0 COMMENT ''乐观锁版本号'' AFTER updated_at');
CALL phase2_measure_add_column('fl_inbound_measurement_item', 'box_specification',
'VARCHAR(64) NOT NULL DEFAULT '''' COMMENT ''箱规标识'' AFTER item_no');
CALL phase2_measure_add_column('fl_inbound_measurement_item', 'girth_cm',
'DECIMAL(12,3) NOT NULL DEFAULT 0 COMMENT ''周长cm,规则为长+2*(宽+高)'' AFTER height_cm');
CALL phase2_measure_add_column('fl_inbound_measurement_item', 'unit_weight_kg',
'DECIMAL(18,3) NULL COMMENT ''单箱重量kg'' AFTER girth_cm');
CALL phase2_measure_add_column('fl_inbound_measurement_item', 'unit_volume_cbm',
'DECIMAL(18,4) NOT NULL DEFAULT 0 COMMENT ''单箱业务体积m3'' AFTER unit_weight_kg');
CALL phase2_measure_add_column('fl_inbound_measurement_item', 'total_weight_kg',
'DECIMAL(18,3) NULL COMMENT ''本箱规总重量kg'' AFTER volume_cbm');
CALL phase2_measure_add_column('fl_inbound_measurement_item', 'total_volume_cbm',
'DECIMAL(18,4) NOT NULL DEFAULT 0 COMMENT ''本箱规总体积m3'' AFTER total_weight_kg');
CALL phase2_measure_add_column('fl_inbound_measurement_item', 'volume_weight_kg',
'DECIMAL(18,3) NOT NULL DEFAULT 0 COMMENT ''本箱规材积重kg'' AFTER total_volume_cbm');
CALL phase2_measure_add_column('fl_inbound_measurement_item', 'over_limit_warning',
'VARCHAR(500) NULL COMMENT ''超线提醒快照'' AFTER source_image_file_id');
CALL phase2_measure_add_column('fl_inbound_measurement_item', 'outer_length_cm',
'DECIMAL(12,3) NULL COMMENT ''外箱规长度cm'' AFTER over_limit_warning');
CALL phase2_measure_add_column('fl_inbound_measurement_item', 'outer_width_cm',
'DECIMAL(12,3) NULL COMMENT ''外箱规宽度cm'' AFTER outer_length_cm');
CALL phase2_measure_add_column('fl_inbound_measurement_item', 'outer_height_cm',
'DECIMAL(12,3) NULL COMMENT ''外箱规高度cm'' AFTER outer_width_cm');
DROP PROCEDURE IF EXISTS phase2_measure_add_column;
CREATE TABLE IF NOT EXISTS fl_inbound_weight_revision (
id BIGINT UNSIGNED NOT NULL COMMENT 'OCR重量修订记录ID',
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '数据所属租户ID',
inbound_id BIGINT UNSIGNED NOT NULL COMMENT '入库单ID',
weight_result_id BIGINT UNSIGNED NOT NULL COMMENT 'OCR重量结果ID',
revision_no INT UNSIGNED NOT NULL COMMENT '结果版本号,与乐观锁版本一致',
action VARCHAR(24) NOT NULL COMMENT '动作:CORRECT或CONFIRM',
before_weight_value DECIMAL(18,3) NULL COMMENT '变更前原单位重量',
before_weight_unit VARCHAR(8) NULL COMMENT '变更前单位',
after_weight_value DECIMAL(18,3) NULL COMMENT '变更后原单位重量',
after_weight_unit VARCHAR(8) NULL COMMENT '变更后单位',
final_weight_kg DECIMAL(18,3) NULL COMMENT '确认时统一换算后的重量kg',
reason VARCHAR(500) NULL COMMENT '人工修正原因',
operator_id BIGINT UNSIGNED NOT NULL COMMENT '操作人ID',
operator_name VARCHAR(64) NULL COMMENT '操作人名称快照',
operated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '操作时间',
PRIMARY KEY (id),
UNIQUE KEY uk_weight_revision_no (tenant_id, weight_result_id, revision_no),
KEY idx_weight_revision_time (tenant_id, inbound_id, operated_at),
CONSTRAINT fk_weight_revision_inbound FOREIGN KEY (inbound_id) REFERENCES fl_inbound_order (id),
CONSTRAINT fk_weight_revision_result FOREIGN KEY (weight_result_id) REFERENCES fl_inbound_weight_result (id)
) ENGINE=InnoDB COMMENT='OCR重量人工修正和确认的不可变版本历史';
@@ -0,0 +1,533 @@
SET NAMES utf8mb4;
CREATE DATABASE IF NOT EXISTS baiyue_firstleg
DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
USE baiyue_firstleg;
CREATE TABLE IF NOT EXISTS fl_inbound_order (
id BIGINT UNSIGNED NOT NULL COMMENT '入库单ID(雪花ID)',
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '数据所属租户ID',
task_id BIGINT UNSIGNED NULL COMMENT '头程履约任务ID',
order_id BIGINT UNSIGNED NOT NULL COMMENT '订单服务订单ID,不建立跨库外键',
order_no VARCHAR(32) NOT NULL COMMENT '订单号快照',
inbound_code VARCHAR(40) NOT NULL COMMENT '租户内唯一入仓码',
process_status VARCHAR(24) NOT NULL DEFAULT 'WAIT_MEASURE' COMMENT '入库流程状态',
order_status_snapshot VARCHAR(24) NOT NULL COMMENT '订单状态快照',
measure_review_status VARCHAR(24) NOT NULL DEFAULT 'NOT_SUBMITTED' COMMENT '测量数据审核状态',
fee_status VARCHAR(24) NOT NULL DEFAULT 'UNPRICED' COMMENT '费用业务状态',
fee_review_status VARCHAR(24) NOT NULL DEFAULT 'NOT_SUBMITTED' COMMENT '费用审核状态',
notification_status VARCHAR(24) NOT NULL DEFAULT 'NOT_CREATED' COMMENT '客户通知状态',
expected_arrival_at DATETIME(3) NULL COMMENT '预计到达时间',
customer_id BIGINT UNSIGNED NULL COMMENT '客户逻辑ID',
customer_name VARCHAR(128) NOT NULL COMMENT '客户名称快照',
company_id BIGINT UNSIGNED NULL COMMENT '运营公司逻辑ID',
company_name VARCHAR(128) NULL COMMENT '运营公司名称快照',
warehouse_id BIGINT UNSIGNED NOT NULL COMMENT '实际作业仓库ID',
warehouse_name VARCHAR(128) NOT NULL COMMENT '实际作业仓库名称快照',
delivery_warehouse_id BIGINT UNSIGNED NULL COMMENT '交货仓逻辑ID',
delivery_warehouse_name VARCHAR(128) NULL COMMENT '交货仓名称快照',
business_type VARCHAR(32) NOT NULL COMMENT '业务类型',
channel_code VARCHAR(64) NULL COMMENT '渠道编码',
channel_name VARCHAR(128) NULL COMMENT '渠道名称快照',
carrier_id BIGINT UNSIGNED NULL COMMENT '计费船司逻辑ID',
carrier_name VARCHAR(128) NULL COMMENT '计费船司名称快照',
tax_mode VARCHAR(24) NULL COMMENT '买单或退税模式',
container_mode VARCHAR(24) NULL COMMENT '整柜或散货模式',
customs_inspection TINYINT(1) NULL COMMENT '是否商检',
destination_type VARCHAR(24) NULL COMMENT '目的仓类型',
destination_warehouse_id BIGINT UNSIGNED NULL COMMENT '目的仓逻辑ID',
destination_code VARCHAR(32) NULL COMMENT '目的仓编码,例如FBA仓编码',
destination_name VARCHAR(128) NULL COMMENT '目的仓名称快照',
hold_flag TINYINT(1) NOT NULL DEFAULT 0 COMMENT '当前是否扣货',
current_hold_reason VARCHAR(500) NULL COMMENT '当前扣货理由快照',
total_package_count INT UNSIGNED NULL COMMENT '确认后的总箱数',
actual_gross_weight_kg DECIMAL(18,3) NULL COMMENT '确认后的实际总毛重kg',
chargeable_weight_kg DECIMAL(18,3) NULL COMMENT '当前费用快照的计费重量kg',
actual_volume_cbm DECIMAL(18,4) NULL COMMENT '确认后的实际总体积m3',
chargeable_volume_cbm DECIMAL(18,4) NULL COMMENT '当前费用快照的计费体积m3',
pallet_count INT UNSIGNED NULL COMMENT '已分配总托盘数快照',
volume_weight_kg DECIMAL(18,3) NULL COMMENT '总材积重kg',
actual_net_weight_kg DECIMAL(18,3) NULL COMMENT '总净重kg',
measure_completed_at DATETIME(3) NULL COMMENT '测量完成时间',
inbounded_at DATETIME(3) NULL COMMENT '入库完成时间',
express_company_code VARCHAR(64) NULL COMMENT '快递公司编码快照',
express_company_name VARCHAR(128) NULL COMMENT '快递公司名称快照',
express_tracking_no VARCHAR(128) NULL COMMENT '快递单号快照',
public_remark VARCHAR(1000) NULL COMMENT '客户可见备注',
created_by BIGINT UNSIGNED NOT NULL COMMENT '创建人ID',
created_by_name VARCHAR(64) NULL COMMENT '创建人名称快照',
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
updated_by BIGINT UNSIGNED NOT NULL COMMENT '最后修改人ID',
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '更新时间',
deleted TINYINT(1) NOT NULL DEFAULT 0 COMMENT '逻辑删除标记',
version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
PRIMARY KEY (id),
UNIQUE KEY uk_inbound_tenant_code (tenant_id, inbound_code),
UNIQUE KEY uk_inbound_tenant_order (tenant_id, order_id),
KEY idx_inbound_process_time (tenant_id, process_status, created_at, id),
KEY idx_inbound_measure_time (tenant_id, measure_review_status, created_at, id),
KEY idx_inbound_fee_time (tenant_id, fee_review_status, created_at, id),
KEY idx_inbound_warehouse_status (tenant_id, warehouse_id, process_status, created_at),
KEY idx_inbound_customer_time (tenant_id, customer_id, created_at),
KEY idx_inbound_company_time (tenant_id, company_id, created_at),
KEY idx_inbound_arrival (tenant_id, expected_arrival_at),
KEY idx_inbound_destination (tenant_id, destination_code, created_at),
KEY idx_inbound_channel (tenant_id, channel_code, created_at),
CONSTRAINT fk_inbound_task FOREIGN KEY (task_id) REFERENCES fl_fulfillment_task (id)
) ENGINE=InnoDB COMMENT='二期头程入库单聚合根和列表查询快照';
CREATE TABLE IF NOT EXISTS fl_inbound_pickup_snapshot (
id BIGINT UNSIGNED NOT NULL COMMENT '提货信息快照ID',
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '数据所属租户ID',
inbound_id BIGINT UNSIGNED NOT NULL COMMENT '入库单ID',
pickup_method VARCHAR(24) NULL COMMENT '提货方式',
pickup_at DATETIME(3) NULL COMMENT '提货时间',
contact_name VARCHAR(64) NULL COMMENT '提货联系人',
contact_phone VARCHAR(32) NULL COMMENT '提货联系人电话',
contact_email VARCHAR(128) NULL COMMENT '提货联系人邮箱',
country_code CHAR(2) NULL COMMENT '国家二字码',
province VARCHAR(64) NULL COMMENT '省或州',
city VARCHAR(64) NULL COMMENT '城市',
district VARCHAR(64) NULL COMMENT '区县',
address_line VARCHAR(512) NULL COMMENT '提货详细地址',
postal_code VARCHAR(20) NULL COMMENT '提货邮编',
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
PRIMARY KEY (id),
UNIQUE KEY uk_pickup_inbound (tenant_id, inbound_id),
CONSTRAINT fk_pickup_inbound FOREIGN KEY (inbound_id) REFERENCES fl_inbound_order (id)
) ENGINE=InnoDB COMMENT='入库单提货信息不可变快照';
CREATE TABLE IF NOT EXISTS fl_inbound_destination_snapshot (
id BIGINT UNSIGNED NOT NULL COMMENT '目的仓地址快照ID',
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '数据所属租户ID',
inbound_id BIGINT UNSIGNED NOT NULL COMMENT '入库单ID',
destination_type VARCHAR(24) NULL COMMENT '目的仓类型',
destination_code VARCHAR(32) NULL COMMENT '目的仓编码',
destination_name VARCHAR(128) NULL COMMENT '目的仓名称',
country_code CHAR(2) NULL COMMENT '国家二字码',
state_province VARCHAR(100) NULL COMMENT '省或州',
city VARCHAR(100) NULL COMMENT '城市',
address_line VARCHAR(512) NULL COMMENT '详细地址',
postal_code VARCHAR(20) NULL COMMENT '邮编',
contact_name VARCHAR(64) NULL COMMENT '联系人',
contact_phone VARCHAR(32) NULL COMMENT '联系人电话',
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
PRIMARY KEY (id),
UNIQUE KEY uk_destination_inbound (tenant_id, inbound_id),
CONSTRAINT fk_destination_inbound FOREIGN KEY (inbound_id) REFERENCES fl_inbound_order (id)
) ENGINE=InnoDB COMMENT='入库单目的仓地址和联系人快照';
CREATE TABLE IF NOT EXISTS fl_inbound_transport_snapshot (
id BIGINT UNSIGNED NOT NULL COMMENT '运输信息快照ID',
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '数据所属租户ID',
inbound_id BIGINT UNSIGNED NOT NULL COMMENT '入库单ID',
loading_status VARCHAR(24) NULL COMMENT '配载状态快照',
container_no VARCHAR(64) NULL COMMENT '集装箱号',
cabinet_no VARCHAR(64) NULL COMMENT '柜号或内部柜单号',
vessel_name VARCHAR(128) NULL COMMENT '船名',
voyage_no VARCHAR(64) NULL COMMENT '航次',
sailing_at DATETIME(3) NULL COMMENT '开船时间',
port_arrival_at DATETIME(3) NULL COMMENT '到港时间',
source_updated_at DATETIME(3) NULL COMMENT '上游数据更新时间',
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '更新时间',
PRIMARY KEY (id),
UNIQUE KEY uk_transport_inbound (tenant_id, inbound_id),
KEY idx_transport_container (tenant_id, container_no),
KEY idx_transport_cabinet (tenant_id, cabinet_no),
KEY idx_transport_loading (tenant_id, loading_status, sailing_at),
CONSTRAINT fk_transport_inbound FOREIGN KEY (inbound_id) REFERENCES fl_inbound_order (id)
) ENGINE=InnoDB COMMENT='入库单关联运输信息只读快照';
CREATE TABLE IF NOT EXISTS fl_inbound_measurement (
id BIGINT UNSIGNED NOT NULL COMMENT '测量批次ID',
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '数据所属租户ID',
inbound_id BIGINT UNSIGNED NOT NULL COMMENT '入库单ID',
source_type VARCHAR(24) NOT NULL COMMENT '数据来源:THIRD_PARTY或MANUAL',
source_code VARCHAR(64) NULL COMMENT '第三方应用或来源编码',
external_event_no VARCHAR(128) NULL COMMENT '第三方事件号',
device_code VARCHAR(64) NULL COMMENT '测量设备编码',
warehouse_code VARCHAR(50) NULL COMMENT '第三方回传仓库编码',
measured_at DATETIME(3) NULL COMMENT '设备测量时间',
status VARCHAR(24) NOT NULL DEFAULT 'RECEIVED' COMMENT '测量批次状态',
raw_payload_json JSON NULL COMMENT '第三方原始报文',
original_document_no VARCHAR(64) NULL COMMENT '原单据号码快照',
original_gross_weight_kg DECIMAL(18,3) NULL COMMENT '原单据毛重kg',
original_volume_cbm DECIMAL(18,4) NULL COMMENT '原单据体积或材积m3',
actual_goods_name VARCHAR(255) NULL COMMENT '实际品名',
total_package_count INT UNSIGNED NULL COMMENT '本批次总箱数',
total_volume_raw_cbm DECIMAL(18,6) NULL COMMENT '本批次原始总体积m3',
total_volume_cbm DECIMAL(18,4) NULL COMMENT '本批次业务总体积m3',
volume_weight_kg DECIMAL(18,3) NULL COMMENT '本批次总材积重kg',
created_by BIGINT UNSIGNED NULL COMMENT '创建人或系统主体ID',
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '更新时间',
version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
PRIMARY KEY (id),
UNIQUE KEY uk_measure_external_event (tenant_id, source_code, external_event_no),
KEY idx_measure_inbound_time (tenant_id, inbound_id, created_at),
CONSTRAINT fk_measure_inbound FOREIGN KEY (inbound_id) REFERENCES fl_inbound_order (id)
) ENGINE=InnoDB COMMENT='入库长宽高测量批次和第三方原始数据';
CREATE TABLE IF NOT EXISTS fl_inbound_measurement_item (
id BIGINT UNSIGNED NOT NULL COMMENT '测量明细ID',
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '数据所属租户ID',
measurement_id BIGINT UNSIGNED NOT NULL COMMENT '测量批次ID',
item_no VARCHAR(32) NOT NULL COMMENT '批次内明细号',
box_specification VARCHAR(64) NOT NULL COMMENT '箱规标识',
package_count INT UNSIGNED NOT NULL COMMENT '相同箱规箱数',
length_cm DECIMAL(12,3) NOT NULL COMMENT '原始长度cm',
width_cm DECIMAL(12,3) NOT NULL COMMENT '原始宽度cm',
height_cm DECIMAL(12,3) NOT NULL COMMENT '原始高度cm',
girth_cm DECIMAL(12,3) NOT NULL COMMENT '周长cm,规则为长+2*(宽+高)',
unit_weight_kg DECIMAL(18,3) NULL COMMENT '单箱重量kg,第三方仅回传尺寸时可为空',
unit_volume_cbm DECIMAL(18,4) NOT NULL COMMENT '单箱业务体积m3',
volume_raw_cbm DECIMAL(18,6) NOT NULL COMMENT '原始体积m3',
volume_cbm DECIMAL(18,4) NOT NULL COMMENT '业务体积m3',
total_weight_kg DECIMAL(18,3) NULL COMMENT '本箱规总重量kg',
total_volume_cbm DECIMAL(18,4) NOT NULL COMMENT '本箱规总体积m3',
volume_weight_kg DECIMAL(18,3) NOT NULL COMMENT '本箱规材积重kg',
calculation_rule_version VARCHAR(32) NOT NULL COMMENT '体积计算和舍入规则版本',
source_image_file_id BIGINT UNSIGNED NULL COMMENT '测量设备图片文件ID',
over_limit_warning VARCHAR(500) NULL COMMENT '超线提醒快照',
outer_length_cm DECIMAL(12,3) NULL COMMENT '外箱规长度cm',
outer_width_cm DECIMAL(12,3) NULL COMMENT '外箱规宽度cm',
outer_height_cm DECIMAL(12,3) NULL COMMENT '外箱规高度cm',
remark VARCHAR(500) NULL COMMENT '测量明细备注',
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
PRIMARY KEY (id),
UNIQUE KEY uk_measure_item_no (tenant_id, measurement_id, item_no),
CONSTRAINT fk_measure_item_batch FOREIGN KEY (measurement_id) REFERENCES fl_inbound_measurement (id)
) ENGINE=InnoDB COMMENT='入库长宽高和箱数测量明细';
CREATE TABLE IF NOT EXISTS fl_inbound_weight_result (
id BIGINT UNSIGNED NOT NULL COMMENT 'OCR重量结果ID',
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '数据所属租户ID',
inbound_id BIGINT UNSIGNED NOT NULL COMMENT '入库单ID',
file_id BIGINT UNSIGNED NOT NULL COMMENT '称重图片文件ID',
ocr_provider VARCHAR(64) NULL COMMENT 'OCR服务商编码',
ocr_raw_text VARCHAR(1000) NULL COMMENT 'OCR原始文本',
ocr_weight_value DECIMAL(18,3) NULL COMMENT 'OCR识别原始重量值',
ocr_weight_unit VARCHAR(8) NULL COMMENT 'OCR识别原始单位',
confidence DECIMAL(6,5) NULL COMMENT '识别置信度',
adjusted_weight_value DECIMAL(18,3) NULL COMMENT '人工修正后的原单位重量',
adjusted_weight_unit VARCHAR(8) NULL COMMENT '人工修正后的单位',
final_weight_kg DECIMAL(18,3) NULL COMMENT '确认并统一换算后的重量kg',
recognition_status VARCHAR(24) NOT NULL DEFAULT 'PENDING' COMMENT '识别和确认状态',
adjustment_reason VARCHAR(500) NULL COMMENT '人工修正原因',
adjusted_by BIGINT UNSIGNED NULL COMMENT '修正人ID',
adjusted_at DATETIME(3) NULL COMMENT '修正时间',
confirmed_by BIGINT UNSIGNED NULL COMMENT '确认人ID',
confirmed_at DATETIME(3) NULL COMMENT '确认时间',
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '更新时间',
deleted TINYINT(1) NOT NULL DEFAULT 0 COMMENT '逻辑删除标记',
version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
PRIMARY KEY (id),
UNIQUE KEY uk_weight_inbound_file (tenant_id, inbound_id, file_id),
KEY idx_weight_inbound_status (tenant_id, inbound_id, recognition_status),
CONSTRAINT fk_weight_inbound FOREIGN KEY (inbound_id) REFERENCES fl_inbound_order (id)
) ENGINE=InnoDB COMMENT='称重图片OCR原值、人工修正值和确认重量';
CREATE TABLE IF NOT EXISTS fl_inbound_weight_revision (
id BIGINT UNSIGNED NOT NULL COMMENT 'OCR重量修订记录ID',
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '数据所属租户ID',
inbound_id BIGINT UNSIGNED NOT NULL COMMENT '入库单ID',
weight_result_id BIGINT UNSIGNED NOT NULL COMMENT 'OCR重量结果ID',
revision_no INT UNSIGNED NOT NULL COMMENT '结果版本号,与乐观锁版本一致',
action VARCHAR(24) NOT NULL COMMENT '动作:CORRECT或CONFIRM',
before_weight_value DECIMAL(18,3) NULL COMMENT '变更前原单位重量',
before_weight_unit VARCHAR(8) NULL COMMENT '变更前单位',
after_weight_value DECIMAL(18,3) NULL COMMENT '变更后原单位重量',
after_weight_unit VARCHAR(8) NULL COMMENT '变更后单位',
final_weight_kg DECIMAL(18,3) NULL COMMENT '确认时统一换算后的重量kg',
reason VARCHAR(500) NULL COMMENT '人工修正原因',
operator_id BIGINT UNSIGNED NOT NULL COMMENT '操作人ID',
operator_name VARCHAR(64) NULL COMMENT '操作人名称快照',
operated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '操作时间',
PRIMARY KEY (id),
UNIQUE KEY uk_weight_revision_no (tenant_id, weight_result_id, revision_no),
KEY idx_weight_revision_time (tenant_id, inbound_id, operated_at),
CONSTRAINT fk_weight_revision_inbound FOREIGN KEY (inbound_id) REFERENCES fl_inbound_order (id),
CONSTRAINT fk_weight_revision_result FOREIGN KEY (weight_result_id) REFERENCES fl_inbound_weight_result (id)
) ENGINE=InnoDB COMMENT='OCR重量人工修正和确认的不可变版本历史';
CREATE TABLE IF NOT EXISTS fl_measure_review_record (
id BIGINT UNSIGNED NOT NULL COMMENT '测量审核记录ID',
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '数据所属租户ID',
inbound_id BIGINT UNSIGNED NOT NULL COMMENT '入库单ID',
measurement_id BIGINT UNSIGNED NULL COMMENT '被审核的测量批次ID',
review_result VARCHAR(24) NOT NULL COMMENT '审核结果',
review_reason VARCHAR(1000) NULL COMMENT '审核或驳回原因',
exception_release TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否异常放行',
release_reason VARCHAR(1000) NULL COMMENT '异常放行原因',
allow_rejected_config TINYINT(1) NOT NULL DEFAULT 0 COMMENT '操作时Nacos异常放行开关值',
reviewer_id BIGINT UNSIGNED NOT NULL COMMENT '审核人ID',
reviewer_name VARCHAR(64) NULL COMMENT '审核人名称快照',
reviewed_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '审核时间',
PRIMARY KEY (id),
KEY idx_review_inbound_time (tenant_id, inbound_id, reviewed_at),
KEY idx_review_exception_release (tenant_id, inbound_id, exception_release, reviewed_at),
CONSTRAINT fk_review_inbound FOREIGN KEY (inbound_id) REFERENCES fl_inbound_order (id),
CONSTRAINT fk_review_measurement FOREIGN KEY (measurement_id) REFERENCES fl_inbound_measurement (id)
) ENGINE=InnoDB COMMENT='测量审核、驳回和异常放行不可变记录';
CREATE TABLE IF NOT EXISTS fl_customer_charge_rule (
id BIGINT UNSIGNED NOT NULL COMMENT '客户计费规则ID',
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '数据所属租户ID',
rule_code VARCHAR(50) NOT NULL COMMENT '租户内规则编码',
rule_version INT UNSIGNED NOT NULL COMMENT '规则版本号',
customer_id BIGINT UNSIGNED NOT NULL COMMENT '客户逻辑ID',
customer_name VARCHAR(128) NOT NULL COMMENT '客户名称快照',
business_type VARCHAR(32) NOT NULL COMMENT '业务类型',
charge_mode VARCHAR(16) NOT NULL COMMENT '计费口径:KG或CBM',
tax_mode VARCHAR(24) NULL COMMENT '包税或不包税类型',
conversion_factor DECIMAL(18,6) NOT NULL COMMENT '重量体积换算系数',
rounding_scale TINYINT UNSIGNED NOT NULL DEFAULT 3 COMMENT '结果保留小数位数',
rounding_mode VARCHAR(24) NOT NULL DEFAULT 'CEILING' COMMENT '舍入方式',
currency CHAR(3) NOT NULL COMMENT '默认币种',
effective_from DATETIME(3) NOT NULL COMMENT '生效开始时间',
effective_to DATETIME(3) NULL COMMENT '生效结束时间',
status VARCHAR(16) NOT NULL DEFAULT 'ACTIVE' COMMENT '规则状态',
remark VARCHAR(500) NULL COMMENT '规则备注',
created_by BIGINT UNSIGNED NOT NULL COMMENT '创建人ID',
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
updated_by BIGINT UNSIGNED NOT NULL COMMENT '最后修改人ID',
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '更新时间',
deleted TINYINT(1) NOT NULL DEFAULT 0 COMMENT '逻辑删除标记',
version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
PRIMARY KEY (id),
UNIQUE KEY uk_charge_rule_version (tenant_id, rule_code, rule_version),
KEY idx_charge_rule_match (tenant_id, customer_id, business_type, status, effective_from, effective_to)
) ENGINE=InnoDB COMMENT='客户头程计费规则版本';
CREATE TABLE IF NOT EXISTS fl_carrier_fba_rate (
id BIGINT UNSIGNED NOT NULL COMMENT '船司FBA价格ID',
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '数据所属租户ID',
rate_code VARCHAR(50) NOT NULL COMMENT '租户内价格编码',
rate_version INT UNSIGNED NOT NULL COMMENT '价格版本号',
origin_warehouse_id BIGINT UNSIGNED NOT NULL COMMENT '起运仓逻辑ID',
origin_warehouse_name VARCHAR(128) NOT NULL COMMENT '起运仓名称快照',
business_type VARCHAR(32) NOT NULL COMMENT '业务或计费类型',
channel_code VARCHAR(64) NULL COMMENT '渠道编码',
carrier_id BIGINT UNSIGNED NULL COMMENT '船司逻辑ID',
carrier_name VARCHAR(128) NOT NULL COMMENT '船司名称快照',
destination_code VARCHAR(32) NOT NULL COMMENT 'FBA目的仓编码',
charge_unit VARCHAR(16) NOT NULL COMMENT '计费单位:KG或CBM',
unit_price DECIMAL(18,6) NOT NULL COMMENT '基础单价',
currency CHAR(3) NOT NULL COMMENT '币种',
additional_fee DECIMAL(18,2) NOT NULL DEFAULT 0 COMMENT '固定附加费',
effective_from DATETIME(3) NOT NULL COMMENT '生效开始时间',
effective_to DATETIME(3) NULL COMMENT '生效结束时间',
status VARCHAR(16) NOT NULL DEFAULT 'ACTIVE' COMMENT '价格状态',
remark VARCHAR(500) NULL COMMENT '价格备注',
created_by BIGINT UNSIGNED NOT NULL COMMENT '创建人ID',
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
updated_by BIGINT UNSIGNED NOT NULL COMMENT '最后修改人ID',
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '更新时间',
deleted TINYINT(1) NOT NULL DEFAULT 0 COMMENT '逻辑删除标记',
version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
PRIMARY KEY (id),
UNIQUE KEY uk_fba_rate_version (tenant_id, rate_code, rate_version),
KEY idx_fba_rate_match (tenant_id, origin_warehouse_id, business_type, channel_code, carrier_id, destination_code, charge_unit, status, effective_from, effective_to)
) ENGINE=InnoDB COMMENT='船司FBA目的仓价格版本';
CREATE TABLE IF NOT EXISTS fl_inbound_fee (
id BIGINT UNSIGNED NOT NULL COMMENT '入库费用ID',
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '数据所属租户ID',
inbound_id BIGINT UNSIGNED NOT NULL COMMENT '入库单ID',
fee_no VARCHAR(40) NOT NULL COMMENT '租户内唯一费用单号',
fee_version INT UNSIGNED NOT NULL DEFAULT 1 COMMENT '费用快照版本',
charge_rule_id BIGINT UNSIGNED NOT NULL COMMENT '客户计费规则ID',
charge_rule_version INT UNSIGNED NOT NULL COMMENT '客户规则版本快照',
carrier_rate_id BIGINT UNSIGNED NOT NULL COMMENT '船司FBA价格ID',
carrier_rate_version INT UNSIGNED NOT NULL COMMENT '船司价格版本快照',
rule_code VARCHAR(50) NOT NULL COMMENT '客户规则编码快照',
customer_id BIGINT UNSIGNED NOT NULL COMMENT '客户ID快照',
customer_name VARCHAR(128) NOT NULL COMMENT '客户名称快照',
business_type VARCHAR(32) NOT NULL COMMENT '业务类型快照',
tax_mode VARCHAR(24) NULL COMMENT '包税模式快照',
rounding_scale TINYINT UNSIGNED NOT NULL COMMENT '计费量保留位数快照',
rounding_mode VARCHAR(24) NOT NULL COMMENT '舍入方式快照',
rule_effective_from DATETIME(3) NOT NULL COMMENT '客户规则生效开始快照',
rule_effective_to DATETIME(3) NULL COMMENT '客户规则生效结束快照',
rate_code VARCHAR(50) NOT NULL COMMENT '船司价格编码快照',
origin_warehouse_id BIGINT UNSIGNED NOT NULL COMMENT '起运仓ID快照',
origin_warehouse_name VARCHAR(128) NOT NULL COMMENT '起运仓名称快照',
channel_code VARCHAR(64) NOT NULL COMMENT '渠道编码快照',
carrier_id BIGINT UNSIGNED NOT NULL COMMENT '船司ID快照',
carrier_name VARCHAR(128) NOT NULL COMMENT '船司名称快照',
destination_code VARCHAR(32) NOT NULL COMMENT 'FBA仓编码快照',
rate_effective_from DATETIME(3) NOT NULL COMMENT '价格生效开始快照',
rate_effective_to DATETIME(3) NULL COMMENT '价格生效结束快照',
charge_mode VARCHAR(16) NOT NULL COMMENT '计费口径:KG或CBM',
conversion_factor DECIMAL(18,6) NOT NULL COMMENT '使用的换算系数快照',
actual_weight_kg DECIMAL(18,3) NOT NULL COMMENT '试算时实际重量kg',
actual_volume_cbm DECIMAL(18,4) NOT NULL COMMENT '试算时实际体积m3',
converted_quantity DECIMAL(18,4) NOT NULL COMMENT '换算后的重量或体积',
chargeable_quantity DECIMAL(18,4) NOT NULL COMMENT '最终计费量',
charge_unit VARCHAR(16) NOT NULL COMMENT '计费单位',
currency CHAR(3) NOT NULL COMMENT '币种',
subtotal_amount DECIMAL(18,2) NOT NULL COMMENT '基础费用金额',
additional_amount DECIMAL(18,2) NOT NULL DEFAULT 0 COMMENT '附加费金额',
total_amount DECIMAL(18,2) NOT NULL COMMENT '总金额',
fee_status VARCHAR(24) NOT NULL DEFAULT 'CALCULATED' COMMENT '费用状态',
review_status VARCHAR(24) NOT NULL DEFAULT 'NOT_SUBMITTED' COMMENT '费用审核状态',
exception_release TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否基于测量异常放行生成',
calculated_by BIGINT UNSIGNED NOT NULL COMMENT '试算人ID',
calculated_at DATETIME(3) NOT NULL COMMENT '试算时间',
confirmed_by BIGINT UNSIGNED NULL COMMENT '确认人ID',
confirmed_at DATETIME(3) NULL COMMENT '确认时间',
sent_by BIGINT UNSIGNED NULL COMMENT '发送人ID',
sent_at DATETIME(3) NULL COMMENT '发送客户时间',
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '更新时间',
PRIMARY KEY (id),
UNIQUE KEY uk_inbound_fee_no (tenant_id, fee_no),
UNIQUE KEY uk_inbound_fee_version (tenant_id, inbound_id, fee_version),
KEY idx_inbound_fee_status (tenant_id, inbound_id, fee_status, review_status),
CONSTRAINT fk_fee_inbound FOREIGN KEY (inbound_id) REFERENCES fl_inbound_order (id),
CONSTRAINT fk_fee_charge_rule FOREIGN KEY (charge_rule_id) REFERENCES fl_customer_charge_rule (id),
CONSTRAINT fk_fee_carrier_rate FOREIGN KEY (carrier_rate_id) REFERENCES fl_carrier_fba_rate (id)
) ENGINE=InnoDB COMMENT='入库头程费用计算和确认快照';
CREATE TABLE IF NOT EXISTS fl_inbound_fee_item (
id BIGINT UNSIGNED NOT NULL COMMENT '费用明细ID',
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '数据所属租户ID',
fee_id BIGINT UNSIGNED NOT NULL COMMENT '费用头ID',
item_no INT UNSIGNED NOT NULL COMMENT '费用单内序号',
fee_code VARCHAR(50) NOT NULL COMMENT '费用项目编码',
fee_name VARCHAR(128) NOT NULL COMMENT '费用项目名称快照',
quantity DECIMAL(18,4) NOT NULL COMMENT '计费数量',
unit VARCHAR(16) NOT NULL COMMENT '计费单位',
unit_price DECIMAL(18,6) NOT NULL COMMENT '单价',
amount DECIMAL(18,2) NOT NULL COMMENT '金额',
currency CHAR(3) NOT NULL COMMENT '币种',
automatic_flag TINYINT(1) NOT NULL DEFAULT 1 COMMENT '是否自动计算',
remark VARCHAR(500) NULL COMMENT '费用备注',
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
PRIMARY KEY (id),
UNIQUE KEY uk_fee_item_no (tenant_id, fee_id, item_no),
CONSTRAINT fk_fee_item_header FOREIGN KEY (fee_id) REFERENCES fl_inbound_fee (id)
) ENGINE=InnoDB COMMENT='入库头程费用项目明细';
CREATE TABLE IF NOT EXISTS fl_inbound_pallet_allocation (
id BIGINT UNSIGNED NOT NULL COMMENT '头程托盘分配引用ID',
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '数据所属租户ID',
inbound_id BIGINT UNSIGNED NOT NULL COMMENT '入库单ID',
allocation_no INT UNSIGNED NOT NULL COMMENT '入库单内分配序号',
warehouse_id BIGINT UNSIGNED NOT NULL COMMENT '仓库服务仓库ID',
pallet_id BIGINT UNSIGNED NOT NULL COMMENT '仓库服务托盘ID',
pallet_code VARCHAR(50) NOT NULL COMMENT '托盘编码快照',
pallet_capacity_cbm DECIMAL(18,4) NOT NULL COMMENT '托盘可用容积快照',
allocated_volume_cbm DECIMAL(18,4) NOT NULL COMMENT '本托盘分配体积',
allocated_package_count INT UNSIGNED NULL COMMENT '本托盘分配箱数',
location_id BIGINT UNSIGNED NOT NULL COMMENT '仓库服务库位ID',
location_code VARCHAR(50) NOT NULL COMMENT '库位编码快照',
reservation_no VARCHAR(64) NOT NULL COMMENT '仓库库位预占单号',
allocation_status VARCHAR(24) NOT NULL DEFAULT 'RESERVED' COMMENT '分配状态',
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '更新时间',
PRIMARY KEY (id),
UNIQUE KEY uk_pallet_allocation_no (tenant_id, inbound_id, allocation_no),
UNIQUE KEY uk_pallet_allocation_pallet (tenant_id, inbound_id, pallet_id),
KEY idx_allocation_location (tenant_id, warehouse_id, location_id, allocation_status),
CONSTRAINT fk_allocation_inbound FOREIGN KEY (inbound_id) REFERENCES fl_inbound_order (id)
) ENGINE=InnoDB COMMENT='头程侧托盘、库位和仓库预占结果引用';
CREATE TABLE IF NOT EXISTS fl_inbound_file (
id BIGINT UNSIGNED NOT NULL COMMENT '入库单文件关系ID',
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '数据所属租户ID',
inbound_id BIGINT UNSIGNED NOT NULL COMMENT '入库单ID',
file_id BIGINT UNSIGNED NOT NULL COMMENT '文件服务文件ID',
file_type VARCHAR(24) NOT NULL COMMENT '文件类型:RELATED、MARK、PC_ORDER、MEASURE或WEIGHT',
file_name_snapshot VARCHAR(255) NOT NULL COMMENT '文件名快照',
visibility VARCHAR(16) NOT NULL DEFAULT 'INTERNAL' COMMENT '可见范围',
file_version INT UNSIGNED NOT NULL DEFAULT 1 COMMENT '业务文件版本',
uploaded_by BIGINT UNSIGNED NOT NULL COMMENT '上传人ID',
uploaded_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '上传时间',
deleted TINYINT(1) NOT NULL DEFAULT 0 COMMENT '逻辑删除标记',
PRIMARY KEY (id),
UNIQUE KEY uk_inbound_file_relation (tenant_id, inbound_id, file_id, file_type),
KEY idx_inbound_file_type (tenant_id, inbound_id, file_type, deleted),
CONSTRAINT fk_file_inbound FOREIGN KEY (inbound_id) REFERENCES fl_inbound_order (id)
) ENGINE=InnoDB COMMENT='入库单与文件中心对象的分类关系';
CREATE TABLE IF NOT EXISTS fl_inbound_hold_record (
id BIGINT UNSIGNED NOT NULL COMMENT '扣货记录ID',
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '数据所属租户ID',
inbound_id BIGINT UNSIGNED NOT NULL COMMENT '入库单ID',
hold_action VARCHAR(16) NOT NULL COMMENT '动作:HOLD或RELEASE',
reason VARCHAR(1000) NOT NULL COMMENT '扣货或解除原因',
evidence_file_id BIGINT UNSIGNED NULL COMMENT '证据文件ID',
operator_id BIGINT UNSIGNED NOT NULL COMMENT '操作人ID',
operator_name VARCHAR(64) NULL COMMENT '操作人名称快照',
operated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '操作时间',
PRIMARY KEY (id),
KEY idx_hold_inbound_time (tenant_id, inbound_id, operated_at),
CONSTRAINT fk_hold_inbound FOREIGN KEY (inbound_id) REFERENCES fl_inbound_order (id)
) ENGINE=InnoDB COMMENT='入库单扣货和解除扣货不可变历史';
CREATE TABLE IF NOT EXISTS fl_inbound_internal_note (
id BIGINT UNSIGNED NOT NULL COMMENT '客服内部备注ID',
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '数据所属租户ID',
inbound_id BIGINT UNSIGNED NOT NULL COMMENT '入库单ID',
content VARCHAR(2000) NOT NULL COMMENT '仅内部可见备注',
created_by BIGINT UNSIGNED NOT NULL COMMENT '创建人ID',
created_by_name VARCHAR(64) NULL COMMENT '创建人名称快照',
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
PRIMARY KEY (id),
KEY idx_internal_note_time (tenant_id, inbound_id, created_at),
CONSTRAINT fk_internal_note_inbound FOREIGN KEY (inbound_id) REFERENCES fl_inbound_order (id)
) ENGINE=InnoDB COMMENT='入库单客服内部备注历史';
CREATE TABLE IF NOT EXISTS fl_inbound_status_history (
id BIGINT UNSIGNED NOT NULL COMMENT '入库状态历史ID',
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '数据所属租户ID',
inbound_id BIGINT UNSIGNED NOT NULL COMMENT '入库单ID',
from_status VARCHAR(24) NULL COMMENT '变更前状态',
to_status VARCHAR(24) NOT NULL COMMENT '变更后状态',
action VARCHAR(32) NOT NULL COMMENT '触发动作',
reason VARCHAR(1000) NULL COMMENT '变更原因',
operator_id BIGINT UNSIGNED NOT NULL COMMENT '操作人ID',
operator_name VARCHAR(64) NULL COMMENT '操作人名称快照',
occurred_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '发生时间',
PRIMARY KEY (id),
KEY idx_inbound_history_time (tenant_id, inbound_id, occurred_at),
CONSTRAINT fk_history_inbound FOREIGN KEY (inbound_id) REFERENCES fl_inbound_order (id)
) ENGINE=InnoDB COMMENT='入库单流程状态不可变历史';
CREATE TABLE IF NOT EXISTS fl_inbound_outbox (
id BIGINT UNSIGNED NOT NULL COMMENT '领域事件全局ID',
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '事件所属租户ID',
aggregate_type VARCHAR(32) NOT NULL DEFAULT 'INBOUND' COMMENT '聚合类型',
aggregate_id BIGINT UNSIGNED NOT NULL COMMENT '入库单ID',
event_type VARCHAR(64) NOT NULL COMMENT '事件类型',
payload_json JSON NOT NULL COMMENT '事件负载快照',
publish_status VARCHAR(16) NOT NULL DEFAULT 'PENDING' COMMENT '发布状态',
retry_count INT NOT NULL DEFAULT 0 COMMENT '重试次数',
next_retry_at DATETIME(3) NULL COMMENT '下次重试时间',
published_at DATETIME(3) NULL COMMENT '发布时间',
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
PRIMARY KEY (id),
KEY idx_inbound_outbox_publish (publish_status, next_retry_at, created_at)
) ENGINE=InnoDB COMMENT='入库事务Outbox事件表';
CREATE TABLE IF NOT EXISTS fl_inbound_idempotency (
id BIGINT UNSIGNED NOT NULL COMMENT '入库写操作幂等记录ID',
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '租户ID',
operation VARCHAR(40) NOT NULL COMMENT '操作类型',
operator_id BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '操作者或外部应用ID',
inbound_id BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '入库单ID,创建前可为0',
idempotency_key VARCHAR(128) NOT NULL COMMENT '幂等键',
request_hash CHAR(64) NOT NULL COMMENT '请求内容摘要',
status VARCHAR(16) NOT NULL COMMENT '处理状态',
result_json JSON NULL COMMENT '成功结果摘要',
result_resource_id BIGINT UNSIGNED NULL COMMENT '成功结果资源ID',
completed_at DATETIME(3) NULL COMMENT '完成时间',
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '更新时间',
PRIMARY KEY (id),
UNIQUE KEY uk_inbound_idempotency (tenant_id, operation, operator_id, idempotency_key),
KEY idx_inbound_idempotency_created (created_at)
) ENGINE=InnoDB COMMENT='第三方测量、审核、费用发送和入库写操作幂等记录';
@@ -0,0 +1,13 @@
-- 业务块4增量迁移:既有二期库补齐异常放行关口查询索引。
-- 可重复执行;新库仍先执行 phase2-inbound-schema.sql。
USE baiyue_firstleg;
SET NAMES utf8mb4;
SET @ddl = IF(
EXISTS(SELECT 1 FROM information_schema.statistics
WHERE table_schema = DATABASE() AND table_name = 'fl_measure_review_record'
AND index_name = 'idx_review_exception_release'),
'SELECT 1',
'ALTER TABLE fl_measure_review_record ADD INDEX idx_review_exception_release (tenant_id, inbound_id, exception_release, reviewed_at)'
);
PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
@@ -0,0 +1,30 @@
SET NAMES utf8mb4;
USE baiyue_firstleg;
-- 既有二期数据库增量:先确保 phase2-inbound-schema.sql 中的测量和幂等表已存在。
CREATE TABLE IF NOT EXISTS fl_third_party_call_log (
id BIGINT UNSIGNED NOT NULL COMMENT '第三方调用日志ID(雪花ID)',
tenant_id BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '识别出的租户ID,未知应用为0',
app_key VARCHAR(128) NULL COMMENT '第三方应用标识',
signature_subject VARCHAR(128) NULL COMMENT '签名主体快照',
request_method VARCHAR(8) NOT NULL COMMENT 'HTTP方法',
request_path VARCHAR(255) NOT NULL COMMENT '签名使用的规范路径',
request_timestamp VARCHAR(32) NULL COMMENT '请求头Unix毫秒时间戳原值',
nonce VARCHAR(128) NULL COMMENT '请求Nonce',
idempotency_key VARCHAR(128) NULL COMMENT 'HTTP幂等键,查询接口为空',
idempotency_anchor TINYINT UNSIGNED NULL COMMENT '签名通过后首个请求为1,重复调用为空',
signature_value VARCHAR(128) NULL COMMENT '第三方签名原值',
request_hash CHAR(64) NOT NULL COMMENT '原始请求体SHA-256小写十六进制',
raw_payload LONGTEXT NOT NULL COMMENT '未经重新序列化的UTF-8原始请求体',
processing_status VARCHAR(16) NOT NULL COMMENT 'PROCESSING、ACCEPTED或REJECTED',
result_code VARCHAR(32) NULL COMMENT '统一响应业务码',
result_message VARCHAR(1000) NULL COMMENT '处理结果或失败原因',
result_json JSON NULL COMMENT '首次成功结果快照',
requested_at DATETIME(3) NOT NULL COMMENT '服务端接收时间',
processed_at DATETIME(3) NULL COMMENT '处理完成时间',
PRIMARY KEY (id),
KEY idx_third_call_app_time (tenant_id, app_key, requested_at),
KEY idx_third_call_idempotency (tenant_id, app_key, idempotency_key),
KEY idx_third_call_nonce (tenant_id, app_key, nonce),
UNIQUE KEY uk_third_call_idempotency_owner (tenant_id, app_key, idempotency_key, idempotency_anchor)
) ENGINE=InnoDB COMMENT='第三方测量开放接口原始请求、签名主体和处理结果留档';
@@ -0,0 +1,138 @@
openapi: 3.0.3
info:
title: 柏悦二期 FBA 计费 API
version: 2.0.0
description: 业务成功和失败均使用 HTTP 200;仅响应体 code 为字符串 "200" 时表示成功。
security:
- bearerAuth: []
paths:
/api/platform/v1/fba-pricing/customer-rules:
get:
summary: 查询客户计费规则版本
description: 需要 firstleg:fba-pricing:rule:view 权限
parameters: [{$ref: '#/components/parameters/TenantId'}]
responses: {'200': {description: 成功或业务失败}, '403': {description: 无权限}}
/api/platform/v1/fba-pricing/customer-rules/versions:
post:
summary: 新建客户计费规则版本
description: 版本内容不可原地修改;有效期使用 [effectiveFrom,effectiveTo) 半开区间。
parameters: [{$ref: '#/components/parameters/TenantId'}]
requestBody: {required: true, content: {application/json: {schema: {$ref: '#/components/schemas/CustomerRuleVersionRequest'}}}}
responses: {'200': {description: 成功或业务失败}, '403': {description: 无权限}}
/api/platform/v1/fba-pricing/customer-rules/{id}:
get:
summary: 查询客户计费规则版本详情
parameters: [{$ref: '#/components/parameters/TenantId'}, {$ref: '#/components/parameters/Id'}]
responses: {'200': {description: 成功或业务失败}, '403': {description: 无权限}}
delete:
summary: 停用客户计费规则版本
parameters: [{$ref: '#/components/parameters/TenantId'}, {$ref: '#/components/parameters/Id'}]
responses: {'200': {description: 成功或业务失败}, '403': {description: 无权限}}
/api/platform/v1/fba-pricing/carrier-rates:
get:
summary: 查询船司 FBA 价格版本
description: 需要 firstleg:fba-pricing:rate:view 权限
parameters: [{$ref: '#/components/parameters/TenantId'}]
responses: {'200': {description: 成功或业务失败}, '403': {description: 无权限}}
/api/platform/v1/fba-pricing/carrier-rates/versions:
post:
summary: 新建船司 FBA 价格版本
parameters: [{$ref: '#/components/parameters/TenantId'}]
requestBody: {required: true, content: {application/json: {schema: {$ref: '#/components/schemas/CarrierRateVersionRequest'}}}}
responses: {'200': {description: 成功或业务失败}, '403': {description: 无权限}}
/api/platform/v1/fba-pricing/carrier-rates/{id}:
get:
summary: 查询船司 FBA 价格版本详情
parameters: [{$ref: '#/components/parameters/TenantId'}, {$ref: '#/components/parameters/Id'}]
responses: {'200': {description: 成功或业务失败}, '403': {description: 无权限}}
delete:
summary: 停用船司 FBA 价格版本
parameters: [{$ref: '#/components/parameters/TenantId'}, {$ref: '#/components/parameters/Id'}]
responses: {'200': {description: 成功或业务失败}, '403': {description: 无权限}}
/api/platform/v1/inbounds/{inboundId}/fees/calculate:
post:
summary: 按审核通过测量快照试算 FBA 费用
description: 精确匹配唯一客户规则和唯一船司价格;需要 firstleg:fba-pricing:calculate 权限。
parameters: [{$ref: '#/components/parameters/TenantId'}, {$ref: '#/components/parameters/InboundId'}, {$ref: '#/components/parameters/IdempotencyKey'}]
requestBody: {content: {application/json: {schema: {$ref: '#/components/schemas/CalculateRequest'}}}}
responses: {'200': {description: 成功或业务失败}, '403': {description: 无权限}}
/api/platform/v1/inbounds/{inboundId}/fees/confirm:
post:
summary: 确认最新费用快照
parameters: [{$ref: '#/components/parameters/TenantId'}, {$ref: '#/components/parameters/InboundId'}, {$ref: '#/components/parameters/IdempotencyKey'}]
responses: {'200': {description: 成功或业务失败}, '403': {description: 无权限}}
/api/platform/v1/inbounds/{inboundId}/fees/send:
post:
summary: 手工恢复发送费用快照
description: 仅作为异常恢复接口;正常业务由客服测量审核通过后自动计费、发布并完成入库。
parameters: [{$ref: '#/components/parameters/TenantId'}, {$ref: '#/components/parameters/InboundId'}, {$ref: '#/components/parameters/IdempotencyKey'}]
responses: {'200': {description: 成功或业务失败}, '403': {description: 无权限}}
/api/platform/v1/inbounds/{inboundId}/fees/latest:
get:
summary: 查询最新费用快照
parameters: [{$ref: '#/components/parameters/TenantId'}, {$ref: '#/components/parameters/InboundId'}]
responses: {'200': {description: 成功或业务失败}, '403': {description: 无权限}}
/api/platform/v1/inbounds/{inboundId}/fees/published:
put:
summary: 调整已发布费用
description: 需要 firstleg:fba-pricing:adjust 权限;生成新 SENT 版本并将旧版本标记 SUPERSEDED,不回退 INBOUNDED 状态。
parameters: [{$ref: '#/components/parameters/TenantId'}, {$ref: '#/components/parameters/InboundId'}, {$ref: '#/components/parameters/IdempotencyKey'}]
requestBody:
required: true
content:
application/json:
schema: {$ref: '#/components/schemas/AdjustFeeRequest'}
responses: {'200': {description: 成功或业务失败}, '403': {description: 无调整权限}}
components:
securitySchemes:
bearerAuth: {type: http, scheme: bearer, bearerFormat: JWT}
parameters:
TenantId: {name: tenantId, in: query, required: true, schema: {type: integer, format: int64, minimum: 1}}
Id: {name: id, in: path, required: true, schema: {type: integer, format: int64}}
InboundId: {name: inboundId, in: path, required: true, schema: {type: integer, format: int64}}
IdempotencyKey: {name: Idempotency-Key, in: header, required: true, schema: {type: string, maxLength: 128}}
schemas:
AdjustFeeRequest:
type: object
required: [subtotalAmount, additionalAmount, reason]
properties:
subtotalAmount: {type: number, minimum: 0, multipleOf: 0.01}
additionalAmount: {type: number, minimum: 0, multipleOf: 0.01}
reason: {type: string, minLength: 1, maxLength: 500}
CalculateRequest:
type: object
properties: {pricingAt: {type: string, format: date-time, description: 缺省为服务端当前时间}}
CustomerRuleVersionRequest:
type: object
required: [ruleCode, customerId, customerName, businessType, chargeMode, taxMode, conversionFactor, roundingScale, roundingMode, currency, effectiveFrom]
properties:
ruleCode: {type: string, maxLength: 50}
customerId: {type: integer, format: int64}
customerName: {type: string, maxLength: 128}
businessType: {type: string, maxLength: 32}
chargeMode: {type: string, enum: [KG, CBM]}
taxMode: {type: string, enum: [TAX_INCLUDED, TAX_EXCLUDED]}
conversionFactor: {type: number, exclusiveMinimum: 0, example: 167}
roundingScale: {type: integer, minimum: 0, maximum: 4}
roundingMode: {type: string, enum: [UP, DOWN, CEILING, FLOOR, HALF_UP, HALF_DOWN, HALF_EVEN]}
currency: {type: string, pattern: '^[A-Z]{3}$'}
effectiveFrom: {type: string, format: date-time}
effectiveTo: {type: string, format: date-time, nullable: true}
CarrierRateVersionRequest:
type: object
required: [rateCode, originWarehouseId, originWarehouseName, businessType, channelCode, carrierId, carrierName, destinationCode, chargeUnit, unitPrice, currency, additionalFee, effectiveFrom]
properties:
rateCode: {type: string, maxLength: 50}
originWarehouseId: {type: integer, format: int64}
originWarehouseName: {type: string}
businessType: {type: string}
channelCode: {type: string}
carrierId: {type: integer, format: int64}
carrierName: {type: string}
destinationCode: {type: string}
chargeUnit: {type: string, enum: [KG, CBM]}
unitPrice: {type: number, exclusiveMinimum: 0}
currency: {type: string, pattern: '^[A-Z]{3}$'}
additionalFee: {type: number, minimum: 0}
effectiveFrom: {type: string, format: date-time}
effectiveTo: {type: string, format: date-time, nullable: true}
@@ -67,13 +67,160 @@ paths:
responses:
'200': {description: 成功或业务失败,按响应体 code 判断}
'403': {description: 无分配权限}
/openapi/v1/inbound-measurements:
post:
summary: 提交第三方长宽高测量结果
description: >-
只写 fl_third_party_call_log、fl_inbound_measurement、fl_inbound_measurement_item
和 HTTP 幂等结果,不审核、不计费、不入库,也不改变入库单状态。
签名串依次为 METHOD、原始 PATH、X-Timestamp、X-Nonce、Idempotency-Key、
原始 UTF-8 body 的 SHA-256 小写十六进制,六部分使用 LF(\n)连接且末尾不追加换行。
三层语义:Nonce 防短时重放;Idempotency-Key 标识一次 HTTP 写操作;
appKey + eventNo 标识第三方业务事件。
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: {$ref: '#/components/schemas/InboundMeasurementCallbackRequest'}
responses:
'200': {description: 成功或业务失败,按响应体字符串 code 判断;非法签名返回 A0401}
'413': {description: 请求体超过网关限制}
'415': {description: Content-Type 不支持}
/openapi/v1/inbound-measurements/events/{eventNo}:
get:
summary: 查询第三方测量事件状态
description: 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 权限;仅使用 V2 四个页签
parameters:
- {name: tenantId, in: query, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}}
- {name: tab, in: query, schema: {type: string, enum: [WAIT_MEASURE, WAIT_REVIEW, WAIT_FEE_SEND, INBOUNDED]}}
responses:
'200': {description: 成功或业务失败,按响应体 code 判断}
'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/v1/inbounds/{inboundId}/measurements/approve:
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/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 权限;客户只读查看,不需要确认费用,未完成客服审核发布时不可见。
parameters:
- {name: orderId, in: path, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}}
responses:
'200': {description: 成功或业务失败,按响应体 code 判断}
'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 小写十六进制签名
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
@@ -93,6 +240,39 @@ components:
in: query
schema: {type: integer, format: int64, minimum: 1, maximum: 200, default: 20}
schemas:
InboundMeasurementCallbackRequest:
type: object
required: [eventNo, warehouseCode, inboundCode, deviceCode, measuredAt, items]
properties:
eventNo: {type: string, maxLength: 128, pattern: '^[A-Za-z0-9._:-]+$', example: M202608010001}
warehouseCode: {type: string, maxLength: 50, example: CN-YW-01}
inboundCode: {type: string, maxLength: 40, example: RK202608010001}
deviceCode: {type: string, maxLength: 64, example: DEVICE-01}
measuredAt: {type: string, format: date-time, example: '2026-08-01T10:30:00+08:00'}
items:
type: array
minItems: 1
maxItems: 500
items: {$ref: '#/components/schemas/InboundMeasurementItem'}
InboundMeasurementItem:
type: object
required: [itemNo, boxSpecification, lengthCm, widthCm, heightCm, quantity]
properties:
itemNo: {type: string, maxLength: 32, example: '001'}
boxSpecification: {type: string, maxLength: 64, example: '61.5x42x42cm', description: 入库单绑定的箱规明细}
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}
quantity: {type: integer, minimum: 1, maximum: 1000000, example: 5}
sourceImageUrl: {type: string, maxLength: 2048, nullable: true, description: 仅随原始报文留档,不直接写文件中心}
InboundMeasurementEventResponse:
type: object
properties:
eventNo: {type: string}
inboundCode: {type: string}
accepted: {type: boolean}
status: {type: string, enum: [RECEIVED]}
receivedAt: {type: string, format: date-time}
AssignRequest:
type: object
required: [assigneeId, assigneeName]