diff --git a/AGENTS.md b/AGENTS.md index dd3922234..f1232234c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -55,7 +55,7 @@ | 客户账期、费用池、账单、入仓码联查、收款、成本、核销 | `finance/` | | 客户资金账户、充值、授信、冻结、结算与跨区资金指令 | `finance/`、`lastleg-service/`、`common-event-contract/` | | 柜级公共费用跨客户分摊 | `firstleg/`、`finance/` | -| 海外业务费用来源跨区入池 | `lastleg-service/`、`common-event-contract/`、`finance/` | +| 海外业务费用来源跨区入池、人工费用与自动仓租 | `lastleg-service/`(`financefee/`、`storagerent/`)、`common-event-contract/`、`finance/` | | 入库/柜次应付、直接已付、付款流水与凭证 | `firstleg/`、`finance/`、`file/` | | 仓库、往来单位、快递、托盘、库位 | `warehouse/` | | 供应商、国内整柜库存与出入库 | `warehouse/` | diff --git a/common-event-contract/src/main/java/com/baiyue/common/event/WalletSettlementReference.java b/common-event-contract/src/main/java/com/baiyue/common/event/WalletSettlementReference.java index 71fe1fd86..5824c0f62 100644 --- a/common-event-contract/src/main/java/com/baiyue/common/event/WalletSettlementReference.java +++ b/common-event-contract/src/main/java/com/baiyue/common/event/WalletSettlementReference.java @@ -1,4 +1,13 @@ package com.baiyue.common.event; -/** Finance-issued identity of a successful consumption, carried by its immutable fee source. */ -public record WalletSettlementReference(String commandEventId, String holdId, String ledgerId) { } +/** 成功扣款凭据;资金业务身份独立于费用对应的实际服务订单。 */ +public record WalletSettlementReference(String commandEventId, String holdId, String ledgerId, + @com.fasterxml.jackson.annotation.JsonInclude(com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL) + String walletBusinessType, + @com.fasterxml.jackson.annotation.JsonInclude(com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL) + String walletBusinessNo) { + /** 原有资金身份与服务订单一致的来源继续使用原三项凭据。 */ + public WalletSettlementReference(String commandEventId, String holdId, String ledgerId) { + this(commandEventId, holdId, ledgerId, null, null); + } +} diff --git a/common-event-contract/src/main/java/com/baiyue/common/event/package-info.java b/common-event-contract/src/main/java/com/baiyue/common/event/package-info.java index 785251b9d..41089c3f4 100644 --- a/common-event-contract/src/main/java/com/baiyue/common/event/package-info.java +++ b/common-event-contract/src/main/java/com/baiyue/common/event/package-info.java @@ -1,4 +1,11 @@ /** * 国内与海外业务单元共享的稳定事件契约。 + * + *

海外费用的 walletSettlement 引用成功扣款的 commandEventId、holdId、ledgerId。 + * 独立资金流程另外携带 walletBusinessType、walletBusinessNo:人工费用为 + * OVERSEAS_MANUAL_FEE / 来源费用ID-冻结次数,仓租为 STORAGE_RENT / 出库预占ID。 + * 外层 businessType、businessNo 始终表示实际服务订单,不能替换为资金流程编号。 + * Finance 同时核对冻结、实扣流水、成功指令结果及客户金额,一个 hold 只能关联一个费用来源。 + * 资金身份与服务订单一致的既有引用省略这两个字段,保持既有快照哈希。

*/ package com.baiyue.common.event; diff --git a/common-event-contract/src/test/java/com/baiyue/common/event/WalletSettlementReferenceTest.java b/common-event-contract/src/test/java/com/baiyue/common/event/WalletSettlementReferenceTest.java new file mode 100644 index 000000000..1183d7d88 --- /dev/null +++ b/common-event-contract/src/test/java/com/baiyue/common/event/WalletSettlementReferenceTest.java @@ -0,0 +1,22 @@ +package com.baiyue.common.event; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.Test; +import static org.assertj.core.api.Assertions.assertThat; + +class WalletSettlementReferenceTest { + private final ObjectMapper json = new ObjectMapper(); + + @Test + void oldReferencesRetainExactHashShape() throws Exception { + String original = "{\"commandEventId\":\"c1\",\"holdId\":\"9\",\"ledgerId\":\"72\"}"; + var reference = json.readValue(original, WalletSettlementReference.class); + assertThat(json.readTree(json.writeValueAsString(reference))).isEqualTo(json.readTree(original)); + } + + @Test + void dedicatedFundingIdentitySurvivesRoundTrip() throws Exception { + var reference = new WalletSettlementReference("c1", "9", "72", "STORAGE_RENT", "445"); + assertThat(json.readValue(json.writeValueAsString(reference), WalletSettlementReference.class)).isEqualTo(reference); + } +} diff --git a/config/nacos/test/file-service.yml b/config/nacos/test/file-service.yml index 740fdf618..e43784f54 100644 --- a/config/nacos/test/file-service.yml +++ b/config/nacos/test/file-service.yml @@ -10,6 +10,9 @@ baiyue.file.storage-root: ${FILE_STORAGE_ROOT} baiyue.clients.integration.url: ${INTEGRATION_SERVICE_URL:} baiyue: file: + pda: + # 在目标 Nacos 命名空间填写公网 HTTPS 根地址,不含 /api;真实环境地址不提交仓库。 + public-base-url: "" export: definitions: CUSTOMER_LIST: diff --git a/deploy/sql/20261022-海外费用仓租数据库变更汇总.md b/deploy/sql/20261022-海外费用仓租数据库变更汇总.md new file mode 100644 index 000000000..357741d93 --- /dev/null +++ b/deploy/sql/20261022-海外费用仓租数据库变更汇总.md @@ -0,0 +1,46 @@ +# 海外费用与仓租数据库定义汇总 + +本次功能的数据库定义已统一到下面两份 SQL,五份重复版本脚本和两份专项汇总 SQL 已移除。仅整理文件,未连接数据库、执行 SQL 或部署。 + +## 两个统一入口 + +| 文件 | 相关数据库 | 本次内容 | +|---|---|---| +| [头程数据库统一增量-MySQL8.sql](头程数据库统一增量-MySQL8.sql) | `baiyue_iam` | 海外费用管理菜单及默认平台管理员授权;保留既有财务编辑、审核按钮和授权 | +| [海外区域统一增量-MySQL8.sql](海外区域统一增量-MySQL8.sql) | `baiyue_lastleg` | 资金与仓租四表、费用关联及换标快照、审核人名称、采购退款请求幂等字段与唯一索引 | + +两份文件虽然名称包含“增量”,仍只用于对应区域的空库初始化。新增列、索引和约束直接维护在最终 `CREATE TABLE` 中,菜单及授权使用初始化种子;不追加存量改造、权限版本回填或动态迁移段。目标 schema 必须为空,按文件原有初始化流程执行;不能向已有业务库重放。 + +已有库只按实际缺项执行定向结构补齐,不能把删除重复文件理解为已有库已完成升级。指定 TEST 的最小执行集合如下;文件准备不代表获得执行授权或已部署。 + +| 顺序 | 文件 | 允许的变更与核对结果 | +|---|---|---| +| 1 | [表与审核人字段补齐](overseas-fees-storage-rent-structure-add.sql) | 只补 `ll_storage_rent_inbound`、`ll_finance_fee_source.reviewer_name`;已存在且匹配时跳过,同名异构则停止;成功回查表结构、字段并输出 `TABLE_COLUMN_READY` | +| 2 | [免费仓租定向修复](overseas-storage-rent-zero-fee-repair.sql) | 仅自动应收仓租允许原币/结算双零与空汇率;保留其他费用正额及有效汇率约束,保留已有汇率精度与数值默认值;最终核对 `SHOW CREATE TABLE` 中金额、汇率两项 CHECK 和汇率可空性 | + +两步均固定 `baiyue_lastleg`,使用独立连接、支持 `DELIMITER` 的客户端,禁止遇错继续;不执行业务 DML、不补历史计租记录、不变更余额、汇率配置或权限。DDL 隐式提交,不能宣称整体可回滚;失败保留错误并只读复查,已完成项不删除重建。两步是已核缺项的补齐方案,不是任意版本升级工具;重新执行前仍需核对目标实例与结构。 + +## 海外表和字段 + +| 类型 | 表 / 字段 | 用途 | +|---|---|---| +| 新表 | `ll_finance_fee_funding` | 人工费用冻结、审核扣款、释放及财务流水引用 | +| 新表 | `ll_storage_rent_lot` | 实际入库计租原批次、数量及入库时间 | +| 新表 | `ll_storage_rent_inbound` | 头程每次实收凭证去重,关联同单同仓计租批次 | +| 新表 | `ll_storage_rent_share` | 库存与原计租批次份额关联,保留原库龄 | +| 新表 | `ll_storage_rent_preparation` | 出库计租快照、汇率、资金预占及实际出库记录 | +| 可空字段 | `ll_destuff_pallet_cost_allocation.fee_source_id` | `BIGINT NULL`,允许人工审核后关联海外费用来源 | +| 字段 | `ll_return_sku_conversion.storage_rent_shares_json` | `LONGTEXT NULL`,退货 SKU 换标的原仓租份额快照 | +| 字段 | `ll_finance_fee_source.reviewer_name` | `VARCHAR(255) NULL`,审核人名称快照 | +| 字段 | `ll_overseas_procurement_fee.idempotency_key` | `VARCHAR(128) NULL`,采购退款请求幂等键 | +| 唯一索引 | `uk_ll_procurement_refund_request(order_id,purpose,idempotency_key)` | 同一采购订单、用途和退款请求去重 | + +## 国内菜单和权限 + +海外业务 → 海外费用管理,菜单 ID `5117200`、权限 `platform:overseas-fee:list`、路由 `/overseas-business/fees`;默认仅授予 `PLATFORM_ADMIN`。编辑和审核分别复用 `platform:finance:fee-source:edit` 与 `platform:finance:fee-source:review`,列表权限不包含写操作。国内基线已包含两项既有按钮及管理员授权。 + +已有库的 [费用写权限修复](overseas-fee-canonical-write-permissions-repair.sql) 是单独的定向补丁,继续保留原适用条件和授权边界,不是结构升级或菜单初始化入口。其他独立修复脚本与历史迁移不在本次清理范围。 + +费用项目继续使用 Firstleg 现有目录及 `FEE_TYPE_077 / 仓储费`,不重复插入项目、不批量覆盖客户专价、不回填历史仓租或费用、不修改钱包余额。 + +配套服务、页面、配置及验收边界见 [首版发布验收](../../lastleg-service/docs/overseas-fees-storage-rent-release.md)。 diff --git a/deploy/sql/20261104-unify-utf8mb4-0900-ai-ci.sql b/deploy/sql/20261104-unify-utf8mb4-0900-ai-ci.sql new file mode 100644 index 000000000..dd9075a27 --- /dev/null +++ b/deploy/sql/20261104-unify-utf8mb4-0900-ai-ci.sql @@ -0,0 +1,2071 @@ +-- Generated from local MySQL 8.4.11 on 2026-11-04. Target: existing five named schemas only. +-- Only unicode_ci columns/defaults change; binary collations remain intentional. +-- Exact local column definitions: compare schema before reuse on another instance. +-- DDL implicitly commits. Stop on first error; do not use --force. Same-schema replay is supported. +SET NAMES utf8mb4 COLLATE utf8mb4_0900_ai_ci; +SET SESSION lock_wait_timeout=15; +ALTER DATABASE `baiyue_event_bridge` CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_event_bridge`.`bridge_delivery_attempt` + MODIFY COLUMN `event_id` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '事件全局标识', + MODIFY COLUMN `source_region` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '来源区域', + MODIFY COLUMN `target_region` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '目标区域', + MODIFY COLUMN `status` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '投递结果', + MODIFY COLUMN `response_code` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '对端响应码', + MODIFY COLUMN `error_message` varchar(1000) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '错误信息', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_event_bridge`.`bridge_inbox` + MODIFY COLUMN `event_id` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '事件全局标识', + MODIFY COLUMN `idempotency_key` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '接收幂等键', + MODIFY COLUMN `event_type` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '事件类型', + MODIFY COLUMN `source_region` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '来源区域', + MODIFY COLUMN `target_region` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '目标区域', + MODIFY COLUMN `customer_id` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户或业务隔离标识;跨区域事件沿用来源系统口径', + MODIFY COLUMN `aggregate_type` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '聚合类型', + MODIFY COLUMN `aggregate_id` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '聚合标识', + MODIFY COLUMN `trace_id` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '链路标识', + MODIFY COLUMN `correlation_id` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '业务关联标识', + MODIFY COLUMN `causation_id` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '上游事件标识', + MODIFY COLUMN `payload_hash` varchar(71) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '规范化负载摘要', + MODIFY COLUMN `status` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'RECEIVED' COMMENT '消费状态', + MODIFY COLUMN `lease_owner` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '消费租约持有者', + MODIFY COLUMN `last_error` varchar(1000) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '最后错误', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_event_bridge`.`bridge_outbox` + MODIFY COLUMN `event_id` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '事件全局标识', + MODIFY COLUMN `idempotency_key` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '发送幂等键', + MODIFY COLUMN `event_type` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '事件类型', + MODIFY COLUMN `source_region` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '来源区域', + MODIFY COLUMN `target_region` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '目标区域', + MODIFY COLUMN `customer_id` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户或业务隔离标识;跨区域事件沿用来源系统口径', + MODIFY COLUMN `aggregate_type` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '聚合类型', + MODIFY COLUMN `aggregate_id` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '聚合标识', + MODIFY COLUMN `trace_id` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '链路标识', + MODIFY COLUMN `correlation_id` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '业务关联标识', + MODIFY COLUMN `causation_id` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '上游事件标识', + MODIFY COLUMN `payload_hash` varchar(71) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '规范化负载摘要', + MODIFY COLUMN `status` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'PENDING' COMMENT '投递状态', + MODIFY COLUMN `lease_owner` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '租约持有者', + MODIFY COLUMN `last_error` varchar(1000) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '最后错误', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER DATABASE `baiyue_event_bridge_overseas` CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_event_bridge_overseas`.`bridge_delivery_attempt` + MODIFY COLUMN `event_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '事件全局标识', + MODIFY COLUMN `source_region` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '来源区域', + MODIFY COLUMN `target_region` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '目标区域', + MODIFY COLUMN `status` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '投递结果', + MODIFY COLUMN `response_code` varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '对端响应码', + MODIFY COLUMN `error_message` varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '错误信息', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_event_bridge_overseas`.`bridge_inbox` + MODIFY COLUMN `event_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '事件全局标识', + MODIFY COLUMN `idempotency_key` varchar(191) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '接收幂等键', + MODIFY COLUMN `event_type` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '事件类型', + MODIFY COLUMN `source_region` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '来源区域', + MODIFY COLUMN `target_region` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '目标区域', + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户标识', + MODIFY COLUMN `aggregate_type` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '聚合类型', + MODIFY COLUMN `aggregate_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '聚合标识', + MODIFY COLUMN `trace_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '链路标识', + MODIFY COLUMN `correlation_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '业务关联标识', + MODIFY COLUMN `causation_id` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '上游事件标识', + MODIFY COLUMN `payload_hash` varchar(71) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '规范化负载摘要', + MODIFY COLUMN `status` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'RECEIVED' COMMENT '消费状态', + MODIFY COLUMN `lease_owner` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '消费租约持有者', + MODIFY COLUMN `last_error` varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '最后错误', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_event_bridge_overseas`.`bridge_outbox` + MODIFY COLUMN `event_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '事件全局标识', + MODIFY COLUMN `idempotency_key` varchar(191) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '发送幂等键', + MODIFY COLUMN `event_type` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '事件类型', + MODIFY COLUMN `source_region` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '来源区域', + MODIFY COLUMN `target_region` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '目标区域', + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户标识', + MODIFY COLUMN `aggregate_type` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '聚合类型', + MODIFY COLUMN `aggregate_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '聚合标识', + MODIFY COLUMN `trace_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '链路标识', + MODIFY COLUMN `correlation_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '业务关联标识', + MODIFY COLUMN `causation_id` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '上游事件标识', + MODIFY COLUMN `payload_hash` varchar(71) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '规范化负载摘要', + MODIFY COLUMN `status` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'PENDING' COMMENT '投递状态', + MODIFY COLUMN `lease_owner` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '租约持有者', + MODIFY COLUMN `last_error` varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '最后错误', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER DATABASE `baiyue_firstleg` CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_firstleg`.`fl_direct_delivery_unloading` + MODIFY COLUMN `timezone` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'Explicit destination IANA timezone', + MODIFY COLUMN `received_by` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'Actual recipient', + MODIFY COLUMN `evidence_no` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'Actual delivery receipt reference', + MODIFY COLUMN `remark` varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `idempotency_key` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `request_hash` char(64) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `created_by_name` varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_firstleg`.`fl_sailing` + MODIFY COLUMN `sailing_no` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `channel_code_snapshot` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `channel_name_snapshot` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `warehouse_name_snapshot` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `departure_port_code` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `destination_port_code` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `vessel_name` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `voyage_no` varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `status` varchar(16) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'OPEN', + MODIFY COLUMN `source_type` varchar(24) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'MANUAL', + MODIFY COLUMN `generated_key` varchar(192) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `remark` varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `created_by_name` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `updated_by_name` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_firstleg`.`fl_sailing_channel` + MODIFY COLUMN `channel_code` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `channel_name` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `source_type` varchar(24) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `mapped_channel_code` varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `default_departure_port_code` varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `default_destination_port_code` varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `status` varchar(16) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'ACTIVE', + MODIFY COLUMN `remark` varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `created_by_name` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `updated_by_name` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_firstleg`.`fl_sailing_history` + MODIFY COLUMN `action` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `from_status` varchar(16) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `to_status` varchar(16) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `reason` varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `operator_name` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_firstleg`.`fl_sailing_template` + MODIFY COLUMN `template_no` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `template_name` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `channel_code_snapshot` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `channel_name_snapshot` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `warehouse_name_snapshot` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `departure_port_code` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `destination_port_code` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `voyage_rule` varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `vessel_name` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `status` varchar(16) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'ACTIVE', + MODIFY COLUMN `remark` varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `created_by_name` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `updated_by_name` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER DATABASE `baiyue_lastleg` CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`backup_fba_appointment_item_20261013` + MODIFY COLUMN `tenant_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'BAIYUE' COMMENT 'SaaS租户标识,当前单租户固定为BAIYUE', + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '绉熸埛/瀹㈡埛璐﹀彿鏍囪瘑', + MODIFY COLUMN `inbound_code` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `fba_warehouse_code` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL, + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`backup_ll_local_looseboard_order_20261023` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户租户ID', + MODIFY COLUMN `order_no` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '本土散板订单号', + MODIFY COLUMN `transport_mode` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '运输方式:拼车或优先拼载', + MODIFY COLUMN `pallet_required` varchar(8) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '是否需要打盘:YES或NO', + MODIFY COLUMN `transport_type` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '运输类型', + MODIFY COLUMN `origin` varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '起运地', + MODIFY COLUMN `origin_address` varchar(500) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '起运详细地址', + MODIFY COLUMN `destination` varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '目的地', + MODIFY COLUMN `destination_address` varchar(500) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '目的详细地址、码头或仓号', + MODIFY COLUMN `container_type` varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '柜型', + MODIFY COLUMN `currency` char(3) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'USD' COMMENT '预算币种', + MODIFY COLUMN `contact_name` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '联系人姓名', + MODIFY COLUMN `contact_info` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '联系人联系方式', + MODIFY COLUMN `status` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'PENDING_ACCEPTANCE' COMMENT '本土散板订单状态', + MODIFY COLUMN `remark` varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '备注', + MODIFY COLUMN `idempotency_key` varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '创建幂等键', + MODIFY COLUMN `request_hash` char(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '创建请求内容哈希', + MODIFY COLUMN `created_by` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '创建人ID', + MODIFY COLUMN `product_name` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '商品名称', + MODIFY COLUMN `origin_contact_name` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '起运地联系人', + MODIFY COLUMN `origin_contact_phone` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '起运地联系电话', + MODIFY COLUMN `destination_contact_name` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '目的地联系人', + MODIFY COLUMN `destination_contact_phone` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '目的地联系电话', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_appointment_cargo_verification` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '原货源所属客户', + MODIFY COLUMN `source_key` varchar(160) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '稳定ORDER或BOOKING原来源键', + MODIFY COLUMN `destination_key` varchar(160) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '核实时的原目的地', + MODIFY COLUMN `source_version` varchar(160) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '核实时的原来源版本', + MODIFY COLUMN `shipment_references_json` longtext COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '本票货件号或追踪号列表,不回写原订单', + MODIFY COLUMN `remark` varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '核实说明', + MODIFY COLUMN `operator_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '登录操作人', + MODIFY COLUMN `operator_name` varchar(200) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '操作人名称快照', + MODIFY COLUMN `command_key` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '来源内幂等命令键', + MODIFY COLUMN `request_hash` char(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '来源、操作人及请求摘要', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_appointment_work` + MODIFY COLUMN `work_item_key` varchar(96) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '不可变事项键,业务身份确定性生成', + MODIFY COLUMN `container_booking_id` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '来源柜次内部关联标识,不作展示编号', + MODIFY COLUMN `state_json` longtext COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '工作关系责任跟进及影响基线,不保存可编辑业务执行事实', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_appointment_work_commitment` + MODIFY COLUMN `entity_key` varchar(160) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '原业务实体稳定键', + MODIFY COLUMN `state_json` longtext COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '原业务命令产生的只读语义影响基线,不是第二份可编辑计划', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_appointment_work_guard` + MODIFY COLUMN `scan_cursor` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '已开船来源同步游标', + MODIFY COLUMN `scope_version` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '完整同步已确认的权威来源集合版本', + MODIFY COLUMN `scan_scope_version` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '当前扫描来源集合版本,防跨页漂移', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_appointment_work_history` + MODIFY COLUMN `id` varchar(36) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '历史记录标识', + MODIFY COLUMN `work_item_key` varchar(96) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '事项稳定键', + MODIFY COLUMN `operator_id` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '真实操作人员标识', + MODIFY COLUMN `operator_name` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '操作人显示名称', + MODIFY COLUMN `action` varchar(48) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '工作命令或观察动作', + MODIFY COLUMN `summary` varchar(2000) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '处理或责任变更依据', + MODIFY COLUMN `command_key` varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '请求幂等键,观察记录为空', + MODIFY COLUMN `request_hash` varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '包含操作者的规范请求摘要', + MODIFY COLUMN `response_json` longtext COLLATE utf8mb4_0900_ai_ci COMMENT '原命令响应快照,重放不随当前状态改变', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_cargo_discard_application` + MODIFY COLUMN `tenant_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'BAIYUE' COMMENT 'SaaS租户标识,当前单租户固定为BAIYUE', + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `discard_no` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `outbound_no` varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '废弃出库单号', + MODIFY COLUMN `overseas_warehouse_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `overseas_warehouse_name` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `inbound_codes` varchar(2000) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `currency` char(3) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'USD', + MODIFY COLUMN `status` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'PENDING_REVIEW', + MODIFY COLUMN `remark` varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `idempotency_key` varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `request_hash` char(64) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `review_idempotency_key` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '审核幂等键', + MODIFY COLUMN `review_request_hash` char(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '审核请求内容哈希', + MODIFY COLUMN `created_by` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `reviewed_by` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `review_remark` varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `created_by_name` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '申请人姓名快照', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_cargo_discard_carton` + MODIFY COLUMN `tenant_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'BAIYUE' COMMENT 'SaaS租户标识,当前单租户固定为BAIYUE', + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `carton_mark` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `source_type` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'DROPSHIP' COMMENT '箱子来源:一件代发DROPSHIP、入仓箱INBOUND、转运箱TRANSFER', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_cargo_discard_item` + MODIFY COLUMN `tenant_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'BAIYUE' COMMENT 'SaaS租户标识,当前单租户固定为BAIYUE', + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `source_type` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'DROPSHIP' COMMENT '库存来源:一件代发DROPSHIP、退货箱RETURN_CARTON', + MODIFY COLUMN `inbound_code` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `sku_code` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `product_name` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `pda_status` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'PENDING', + MODIFY COLUMN `pda_operator_id` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `completion_photo_file_ids` text COLLATE utf8mb4_0900_ai_ci COMMENT 'PDA完工照片文件ID JSON数组', + MODIFY COLUMN `carton_marks_json` text COLLATE utf8mb4_0900_ai_ci COMMENT '本次废弃所选真实箱唛快照JSON数组', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_cargo_discard_photo` + MODIFY COLUMN `customer_id` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `pallet_no` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `file_name` varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `content_type` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `operator_id` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL, + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_cargo_discard_status_history` + MODIFY COLUMN `tenant_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'BAIYUE' COMMENT 'SaaS租户标识,当前单租户固定为BAIYUE', + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `from_status` varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `to_status` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `operator_id` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `remark` varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_compact_number_clock` + MODIFY COLUMN `number_key` varchar(16) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '业务前缀和客户名称缩写', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_container_arrival_time_operation` + MODIFY COLUMN `source_container_booking_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '稳定来源柜预订ID', + MODIFY COLUMN `idempotency_key` varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '平台请求幂等键', + MODIFY COLUMN `request_hash` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '规范化修改请求SHA-256摘要', + MODIFY COLUMN `operator_name` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '平台操作人名称', + MODIFY COLUMN `remark` varchar(500) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '预计到仓时间修改备注', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_container_loading_evidence` + MODIFY COLUMN `source_container_booking_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '国内稳定来源柜预订ID', + MODIFY COLUMN `evidence_type` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'PHOTO_STEP_1至PHOTO_STEP_4或SEAL', + MODIFY COLUMN `file_name` varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '国内文件名快照', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_container_plan_conversion_command` + MODIFY COLUMN `source_container_booking_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '稳定来源柜预订ID', + MODIFY COLUMN `idempotency_key` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '调用方幂等键', + MODIFY COLUMN `request_hash` char(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '确认请求SHA-256摘要', + MODIFY COLUMN `snapshot_hash` char(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '确认的柜级快照SHA-256', + MODIFY COLUMN `batch_key` varchar(160) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '海外仓托盘批次幂等键', + MODIFY COLUMN `status` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '命令状态:PREPARED/APPLYING/FAILED/LOCAL_COMMITTED/COMPLETED', + MODIFY COLUMN `lease_token` varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '当前执行租约令牌,用于陈旧执行者隔离', + MODIFY COLUMN `last_error` varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '最近一次跨服务错误', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_container_shipping_gate` + MODIFY COLUMN `container_booking_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '来源头程柜预订ID', + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户标识', + MODIFY COLUMN `ocean_booking_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '来源海运订舱ID', + MODIFY COLUMN `container_no` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '实际集装箱号', + MODIFY COLUMN `status` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'AWAITING_PICKUP_READY' COMMENT '海外收货门禁状态:AWAITING_PICKUP_READY、RECEIVING_AVAILABLE', + MODIFY COLUMN `standard_node` varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '最新有效标准船节点', + MODIFY COLUMN `location_code` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '节点地点编码', + MODIFY COLUMN `location_name` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '节点地点名称', + MODIFY COLUMN `provider_code` varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '船节点供应商编码', + MODIFY COLUMN `provider_event_id` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '供应商事件ID', + MODIFY COLUMN `provider_node_code` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '供应商原始节点编码', + MODIFY COLUMN `source_reference` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '供应商来源引用', + MODIFY COLUMN `raw_payload_hash` varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '供应商原始负载SHA-256', + MODIFY COLUMN `last_event_id` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '最近采纳的跨区事件ID', + MODIFY COLUMN `payload_hash` varchar(71) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '最近采纳事件规范化负载摘要', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_customer_visit` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户数据隔离键', + MODIFY COLUMN `delivery_no` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '关联派送单号', + MODIFY COLUMN `customer_name` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '客户名称快照', + MODIFY COLUMN `contact_name` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '客户联系人姓名快照', + MODIFY COLUMN `contact_phone` varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '客户联系人电话快照', + MODIFY COLUMN `destination` varchar(500) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '派送目的仓编码汇总快照', + MODIFY COLUMN `status` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'PENDING' COMMENT '回访状态:PENDING待回访或COMPLETED已回访', + MODIFY COLUMN `selected_last_mile_order_no` varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '本次回访选择的尾程订单号', + MODIFY COLUMN `selected_inbound_code` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '本次回访选择的入仓码', + MODIFY COLUMN `visit_result` varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '最近一次回访结果', + MODIFY COLUMN `customer_feedback` varchar(2000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '最近一次客户反馈', + MODIFY COLUMN `remark` varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '最近一次回访备注', + MODIFY COLUMN `visited_by` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '最近一次回访操作人ID', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_delivery_document_revision` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '派送所属客户隔离键', + MODIFY COLUMN `idempotency_key` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '资料保存幂等键', + MODIFY COLUMN `request_hash` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '完整保存请求哈希', + MODIFY COLUMN `operator_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '保存操作人ID', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_delivery_facts_changed` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '真实签收货物客户ID', + MODIFY COLUMN `dispatch_no` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '真实排车业务号', + MODIFY COLUMN `delivery_no` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '真实派送业务号', + MODIFY COLUMN `facts_hash` char(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '该版本真实签收事实摘要', + MODIFY COLUMN `state` varchar(16) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'PENDING' COMMENT '待核对PENDING或已核对PROCESSED', + MODIFY COLUMN `last_error_code` varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '最近失败诊断码不保存敏感异常正文', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_delivery_fee` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户数据隔离键', + MODIFY COLUMN `fee_direction` varchar(16) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'PAYABLE' COMMENT '应收应付方向', + MODIFY COLUMN `fee_type` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '费用类型', + MODIFY COLUMN `currency` varchar(8) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '币种', + MODIFY COLUMN `remark` varchar(500) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '费用备注', + MODIFY COLUMN `created_by` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '创建人ID', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_delivery_operation` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户数据隔离键', + MODIFY COLUMN `operation_node` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '操作节点', + MODIFY COLUMN `operator_id` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '操作人ID', + MODIFY COLUMN `operator_name` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '操作人名称', + MODIFY COLUMN `result` varchar(500) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '处理结果', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_delivery_order` + MODIFY COLUMN `tenant_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'BAIYUE' COMMENT 'SaaS租户标识,当前单租户固定为BAIYUE', + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `delivery_no` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `outbound_no` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `dispatch_no` varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '关联派车单号,仓储转运直接出库时为空', + MODIFY COLUMN `overseas_warehouse_id` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `overseas_warehouse_name` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '海外仓名称快照', + MODIFY COLUMN `customer_name` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '客户名称快照', + MODIFY COLUMN `destination_warehouse_id` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '本派送单目的FBA仓ID快照', + MODIFY COLUMN `destination_warehouse_code` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '本派送单目的FBA仓编码快照,私人仓为空', + MODIFY COLUMN `destination_warehouse_name` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '本派送单目的FBA仓名称快照', + MODIFY COLUMN `destination_type` varchar(16) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '目的地类型:FBA或PRIVATE', + MODIFY COLUMN `delivery_address` varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '私人仓派送地址', + MODIFY COLUMN `contact_name` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '私人仓联系人', + MODIFY COLUMN `contact_phone` varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '私人仓联系电话', + MODIFY COLUMN `delivery_group_key` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '同一出库单内按目的FBA仓拆单的稳定分组键', + MODIFY COLUMN `status` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'WAIT_DELIVERY', + MODIFY COLUMN `seller_name` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '卖家姓名', + MODIFY COLUMN `remark` varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '派送备注', + MODIFY COLUMN `idempotency_key` varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `created_by` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `waybill_no` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '承运运单号', + MODIFY COLUMN `delivery_method` varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '派送方式', + MODIFY COLUMN `carrier_name` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '派送承运商', + MODIFY COLUMN `pickup_method` varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '提货方式', + MODIFY COLUMN `loading_type` varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '装车类型', + MODIFY COLUMN `vehicle_type` varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '车辆类型', + MODIFY COLUMN `vehicle_size` varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '车辆大小', + MODIFY COLUMN `vehicle_plate` varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '车牌号', + MODIFY COLUMN `driver_name` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '司机或车队名称', + MODIFY COLUMN `driver_phone` varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '车队联系电话', + MODIFY COLUMN `appointment_account` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '预约账号', + MODIFY COLUMN `bol_no` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '派送资料BOL号,不作为文件归属标识', + MODIFY COLUMN `postal_code` varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '私人仓邮编', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_destuff_pallet_cost` + MODIFY COLUMN `source_container_booking_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '来源柜次业务ID', + MODIFY COLUMN `container_no` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '集装箱号', + MODIFY COLUMN `currency` char(3) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'CNY' COMMENT '成本币种', + MODIFY COLUMN `status` varchar(16) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '生成状态', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_destuff_pallet_cost_allocation` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户ID', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_dispatch_appointment_allocation` + MODIFY COLUMN `batch_key` varchar(96) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '预约稳定批次键', + MODIFY COLUMN `customer_id` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '所属客户标识', + MODIFY COLUMN `source_key` varchar(160) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '货物来源稳定键', + MODIFY COLUMN `destination_key` varchar(160) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '目的地稳定键', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_dispatch_command` + MODIFY COLUMN `dispatch_no` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '命令所属真实排车业务号', + MODIFY COLUMN `action` varchar(40) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '本次整车作业动作', + MODIFY COLUMN `operator_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '原始可信操作主体ID', + MODIFY COLUMN `idempotency_key` varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '跨操作主体全局唯一的原始请求键', + MODIFY COLUMN `command_hash` char(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '绑定原主体动作业务号版本和完整载荷的严格摘要', + MODIFY COLUMN `result_json` longtext COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '按动作类型恢复的不可变成功结果JSON', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_dispatch_daily_sequence` + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_dispatch_delivery_arrangement` + MODIFY COLUMN `dispatch_no` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '真实排车业务号', + MODIFY COLUMN `arrangement_key` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '本次唯一安排业务事实标识', + MODIFY COLUMN `business_hash` char(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '排除操作者及纯并发版本的安排业务摘要', + MODIFY COLUMN `cargo_hash` char(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '本车实际拣货来源摘要', + MODIFY COLUMN `loading_completion_hash` char(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '装车完成事实摘要', + MODIFY COLUMN `original_command_key` varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '首次安排的全局请求键', + MODIFY COLUMN `original_operator_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '首次安排操作主体', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_dispatch_draft` + MODIFY COLUMN `draft_token` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `operator_id` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `plan_ids_json` varchar(4000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `containers_json` longtext COLLATE utf8mb4_0900_ai_ci, + MODIFY COLUMN `status` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'ACTIVE', + MODIFY COLUMN `sources_json` longtext COLLATE utf8mb4_0900_ai_ci COMMENT '服务端核验的真实来源及客户版本快照数组', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_dispatch_draft_command` + MODIFY COLUMN `idempotency_key` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '草稿操作全局唯一幂等键', + MODIFY COLUMN `operator_id` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '原命令操作主体,恢复必须为本人', + MODIFY COLUMN `action` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '草稿创建、维护、选货、释放或转换动作', + MODIFY COLUMN `target_token` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '命令目标草稿标识或创建范围标识', + MODIFY COLUMN `request_hash` char(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '原动作、主体、版本及完整载荷摘要', + MODIFY COLUMN `result_json` longtext COLLATE utf8mb4_0900_ai_ci COMMENT '本事务完成后保存的不可变原结果,不提交空结果', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_dispatch_draft_container` + MODIFY COLUMN `draft_token` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `source_keys_json` longtext COLLATE utf8mb4_0900_ai_ci COMMENT '本装车容器已取得本草稿占用的来源键数组', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_dispatch_history` + MODIFY COLUMN `action` varchar(40) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '本次真实业务动作', + MODIFY COLUMN `operator_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '可信操作主体ID', + MODIFY COLUMN `reason` varchar(2000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '审核驳回或变更原因', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_dispatch_item` + MODIFY COLUMN `tenant_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'BAIYUE' COMMENT 'SaaS租户标识,当前单租户固定为BAIYUE', + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `order_no` varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `order_refs` varchar(2000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `inbound_code` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `pallet_no` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `overseas_warehouse_id` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `overseas_warehouse_name` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `destination_warehouse_id` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `destination_warehouse_code` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `destination_warehouse_name` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `fba_warehouse_code` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `source_stage` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'PLAN or RECEIVED', + MODIFY COLUMN `carton_marks` varchar(4000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '换标箱唛映射快照', + MODIFY COLUMN `source_business_type` varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '来源业务类型', + MODIFY COLUMN `source_business_no` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '来源业务单号', + MODIFY COLUMN `destination_type` varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '目的类型:FBA或PRIVATE', + MODIFY COLUMN `delivery_address` varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '私人仓目的地址快照', + MODIFY COLUMN `postal_code` varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '私人仓邮编快照', + MODIFY COLUMN `contact_name` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '私人仓联系人快照', + MODIFY COLUMN `contact_phone` varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '私人仓联系电话快照', + MODIFY COLUMN `customer_name` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '本条真实客户名称快照', + MODIFY COLUMN `source_key` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '本条货物稳定权威来源键', + MODIFY COLUMN `source_version` char(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '选货时核对的真实来源版本摘要', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_dispatch_item_source` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '本条真实客户ID', + MODIFY COLUMN `relabel_round_key` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '冻结的真实换标作业轮次,扫码数量变化不改变轮次', + MODIFY COLUMN `source_inbound_order_no` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '逐换标明细原货源订单业务号快照', + MODIFY COLUMN `source_inbound_code` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '逐换标明细原入仓业务号快照', + MODIFY COLUMN `binding_basis_hash` char(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '申请轮次、位置及逐来源贡献的冻结依据摘要', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_dispatch_order` + MODIFY COLUMN `tenant_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'BAIYUE' COMMENT 'SaaS租户标识,当前单租户固定为BAIYUE', + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '鏃у鎴锋帓杞﹀綊灞烇紝鏁磋溅涓诲崟涓嶄娇鐢�', + MODIFY COLUMN `dispatch_no` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'Dispatch order number', + MODIFY COLUMN `task_type` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'DELIVERY' COMMENT 'DELIVERY/PICKUP/LOCAL_TRUCK', + MODIFY COLUMN `overseas_warehouse_id` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `overseas_warehouse_name` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `vehicle_plate` varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'Editable vehicle plate; required before PDA picking', + MODIFY COLUMN `driver_name` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `driver_phone` varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `carrier_name` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `route` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `fba_appointment_no` varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `fba_appointment_status` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'PENDING', + MODIFY COLUMN `vehicle_appointment_status` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'PENDING', + MODIFY COLUMN `status` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'PENDING_DEPARTURE', + MODIFY COLUMN `partial_completion_reason` varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `created_by` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `overseas_warehouse_code` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '起运海外仓编码快照', + MODIFY COLUMN `stage` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'BEFORE_LOADING' COMMENT '作业阶段:BEFORE_LOADING装车前、LOADING装车中、AFTER_LOADING装车后、DELIVERING派送中、DELIVERED派送完成', + MODIFY COLUMN `cargo_hash` char(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '当前整车真实货物及来源摘要', + MODIFY COLUMN `review_state` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'DRAFT' COMMENT '审核子流程当前状态', + MODIFY COLUMN `review_basis_hash` char(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '本审核轮次核对的货物和资源依据摘要', + MODIFY COLUMN `loading_completion_hash` char(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '实际装车完成事实摘要', + MODIFY COLUMN `arrangement_key` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '本车唯一派送安排事实标识', + MODIFY COLUMN `arrangement_business_hash` char(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '本车派送安排业务内容摘要', + MODIFY COLUMN `remark` varchar(2000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '排车业务备注', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_dispatch_pallet_lock` + MODIFY COLUMN `tenant_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'BAIYUE' COMMENT 'SaaS租户标识,当前单租户固定为BAIYUE', + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `source_pallet_key` varchar(160) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'PLAN:{planPalletId} or RECEIVED:{warehousePalletId}', + MODIFY COLUMN `active_key` varchar(160) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'Equals source key only while lock is active', + MODIFY COLUMN `draft_token` varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '智能排车草稿随机标识', + MODIFY COLUMN `operator_id` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '草稿锁操作人ID', + MODIFY COLUMN `status` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'ACTIVE', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_dispatch_pda_operation` + MODIFY COLUMN `tenant_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'BAIYUE' COMMENT 'SaaS租户标识,当前单租户固定为BAIYUE', + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `operation_type` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `idempotency_key` varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `request_hash` char(64) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `result_json` longtext COLLATE utf8mb4_0900_ai_ci, + MODIFY COLUMN `status` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL, + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_dispatch_pda_pick` + MODIFY COLUMN `tenant_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'BAIYUE' COMMENT 'SaaS租户标识,当前单租户固定为BAIYUE', + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `idempotency_key` varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL, + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_dispatch_pda_pick_order` + MODIFY COLUMN `tenant_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'BAIYUE' COMMENT 'SaaS租户标识,当前单租户固定为BAIYUE', + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL, + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_dispatch_pda_task` + MODIFY COLUMN `tenant_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'BAIYUE' COMMENT 'SaaS租户标识,当前单租户固定为BAIYUE', + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '历史客户任务归属,整车任务客户归属保留在扫码明细', + MODIFY COLUMN `task_no` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `status` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `operator_id` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `operator_name` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `warehouse_id` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_dispatch_physical_pallet_lock` + MODIFY COLUMN `warehouse_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '真实起运仓ID', + MODIFY COLUMN `draft_token` varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '草稿拥有者令牌,与正式排车归属互斥', + MODIFY COLUMN `operator_id` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '占用操作人ID', + MODIFY COLUMN `status` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'ACTIVE' COMMENT '物理占用状态', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_dispatch_transport_command` + MODIFY COLUMN `dispatch_no` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `request_key` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `request_hash` char(64) COLLATE utf8mb4_0900_ai_ci NOT NULL, + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_dispatch_transport_create` + MODIFY COLUMN `trip_no` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `request_hash` char(64) COLLATE utf8mb4_0900_ai_ci NOT NULL, + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_dispatch_truck_task` + MODIFY COLUMN `dispatch_no` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `trip_no` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `order_no` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `active_order` varchar(193) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '有效原单唯一键;取消置空,完成保留', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_document_sequence` + MODIFY COLUMN `sequence_key` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '业务前缀与北京时间日期组成的全局序列键', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_dropship_carton` + MODIFY COLUMN `tenant_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'BAIYUE' COMMENT 'SaaS租户标识,当前单租户固定为BAIYUE', + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `pallet_no` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `sku_code` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `carton_mark` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `status` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'AVAILABLE', + MODIFY COLUMN `outbound_order_no` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_dropship_carton_ledger` + MODIFY COLUMN `tenant_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'BAIYUE' COMMENT 'SaaS租户标识,当前单租户固定为BAIYUE', + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `carton_mark` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `sku_code` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `operation_type` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `business_order_no` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `idempotency_key` varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `remark` varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_dropship_carton_sku` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户数据隔离键', + MODIFY COLUMN `sku_code` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '箱内SKU编码', + MODIFY COLUMN `product_name` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '产品名称快照', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_dropship_order_profile` + MODIFY COLUMN `tenant_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'BAIYUE' COMMENT 'SaaS租户标识,当前单租户固定为BAIYUE', + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `pallet_no` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `source_pallet_no` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'Pallet number snapshot supplied by first-leg handover', + MODIFY COLUMN `status` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'RECEIVED', + MODIFY COLUMN `idempotency_key` varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `manifest_hash` char(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '整托完整清单摘要', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_dropship_sku_inventory` + MODIFY COLUMN `tenant_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'BAIYUE' COMMENT 'SaaS租户标识,当前单租户固定为BAIYUE', + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `sku_code` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `product_name` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_event_conflict` + MODIFY COLUMN `tenant_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'BAIYUE' COMMENT 'SaaS租户标识,当前单租户固定为BAIYUE', + MODIFY COLUMN `event_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '冲突事件 ID', + MODIFY COLUMN `conflict_type` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '冲突类型', + MODIFY COLUMN `conflict_message` varchar(512) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '冲突说明', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_event_inbox` + MODIFY COLUMN `tenant_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'BAIYUE' COMMENT 'SaaS租户标识,当前单租户固定为BAIYUE', + MODIFY COLUMN `event_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '事件全局唯一 ID', + MODIFY COLUMN `event_type` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '事件类型', + MODIFY COLUMN `aggregate_type` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '聚合类型', + MODIFY COLUMN `aggregate_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '聚合 ID', + MODIFY COLUMN `payload_hash` varchar(71) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '负载摘要', + MODIFY COLUMN `status` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '消费状态', + MODIFY COLUMN `error_message` varchar(512) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '错误信息', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_event_outbox` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户租户ID', + MODIFY COLUMN `aggregate_type` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '聚合类型', + MODIFY COLUMN `aggregate_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '聚合标识', + MODIFY COLUMN `event_type` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '事件类型', + MODIFY COLUMN `event_key` varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '业务幂等键', + MODIFY COLUMN `payload_json` longtext COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '事件载荷JSON', + MODIFY COLUMN `status` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'PENDING' COMMENT '发布状态', + MODIFY COLUMN `locked_by` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '投递租约持有者', + MODIFY COLUMN `last_error` varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '最后一次发布错误', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_exception_order` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `exception_no` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `exception_type` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `status` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'PENDING', + MODIFY COLUMN `reason` varchar(1000) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `last_mile_order_no` varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `inbound_code` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `delivery_no` varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `outbound_no` varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `customer_name` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `overseas_warehouse_id` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `overseas_warehouse_name` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `sku_code` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `return_direction` varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `return_logistics_no` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `handling_action` varchar(500) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `handling_result` varchar(2000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '异常处理结果', + MODIFY COLUMN `handler_id` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `handler_name` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `source_type` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'MANUAL', + MODIFY COLUMN `created_by` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_exception_order_command` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户数据隔离键', + MODIFY COLUMN `action` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '异常处理动作', + MODIFY COLUMN `idempotency_key` varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '幂等键', + MODIFY COLUMN `request_hash` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '请求内容SHA256哈希', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_exception_return_item` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户数据隔离键', + MODIFY COLUMN `pallet_no` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '原出库托盘码快照', + MODIFY COLUMN `sku_code` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '原出库SKU快照', + MODIFY COLUMN `return_carton_mark` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '原生退货箱业务箱码快照', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_fba_appointment_command` + MODIFY COLUMN `command_key` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '持久化幂等键', + MODIFY COLUMN `request_hash` char(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '操作、操作者和请求摘要', + MODIFY COLUMN `operation` varchar(96) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '操作类型及目标业务号', + MODIFY COLUMN `operator_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '登录操作人', + MODIFY COLUMN `appointment_no` varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '系统预约业务单号', + MODIFY COLUMN `response_json` longtext COLLATE utf8mb4_0900_ai_ci COMMENT '原命令返回快照,与业务写入同事务', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_fba_receiving_agreement` + MODIFY COLUMN `agreement_key` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '收货预约独立业务键', + MODIFY COLUMN `status` varchar(24) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '有效、取消或已替代状态', + MODIFY COLUMN `destination_key` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '权威目的仓关联键', + MODIFY COLUMN `confirmation_no` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '当前有效外部约号,失效后清空以释放覆盖', + MODIFY COLUMN `snapshot_json` longtext COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户逐来源覆盖、确认时区及仓库依据快照', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_fba_receiving_change` + MODIFY COLUMN `change_key` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户预约变化业务键', + MODIFY COLUMN `status` varchar(24) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '待仓库确认、已应用或已撤回状态', + MODIFY COLUMN `batch_key` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '受影响原送货批次键', + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '本次变化所属客户', + MODIFY COLUMN `snapshot_json` longtext COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '原覆盖快照、逐来源拟保留量、负责人及跟进资料', + MODIFY COLUMN `result_json` longtext COLLATE utf8mb4_0900_ai_ci COMMENT '确认后的实际批次去向及逐步骤结果', + MODIFY COLUMN `active_ref` varchar(260) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '同批同客户唯一未解决变化引用', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_fba_receiving_member` + MODIFY COLUMN `agreement_key` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '所属收货预约业务键', + MODIFY COLUMN `batch_key` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '稳定送货批次键', + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '货物所属客户标识', + MODIFY COLUMN `active_ref` varchar(260) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '有效批次与客户唯一引用,退出后清空且历史保留', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_fba_receiving_plan` + MODIFY COLUMN `batch_key` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '稳定送货批次键', + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '计划所属客户标识', + MODIFY COLUMN `snapshot_json` longtext COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '本客户未确认计划、目标窗口及业务时区,不含有效预约确认', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_fba_receiving_revision` + MODIFY COLUMN `agreement_key` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '收货预约业务键', + MODIFY COLUMN `snapshot_json` longtext COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '该版本完整客户覆盖、确认时段及回复依据', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_finance_fee_command` + MODIFY COLUMN `operator_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '操作人ID', + MODIFY COLUMN `action` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '费用写操作动作:CREATE、SUBMIT、REVIEW、ADJUST、VOID或资金重试', + MODIFY COLUMN `idempotency_key` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '操作幂等键', + MODIFY COLUMN `request_hash` char(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '请求内容SHA-256', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_finance_fee_source` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户ID和隔离键', + MODIFY COLUMN `customer_name` varchar(200) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '客户名称快照', + MODIFY COLUMN `source_type` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '海外费用来源类型', + MODIFY COLUMN `source_no` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '来源业务编号或入仓码快照', + MODIFY COLUMN `business_type` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '来源业务类型', + MODIFY COLUMN `business_no` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '来源业务编号', + MODIFY COLUMN `inbound_code` varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '关联入仓码;并非所有费用必填', + MODIFY COLUMN `container_no` varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '真实集装箱号快照', + MODIFY COLUMN `fee_item_code` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '费用项目编码快照', + MODIFY COLUMN `fee_item_name` varchar(200) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '费用项目名称快照', + MODIFY COLUMN `fee_remark` varchar(500) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '费用说明,补收或重复作业原因', + MODIFY COLUMN `direction` varchar(16) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'RECEIVABLE应收或PAYABLE应付', + MODIFY COLUMN `supplier_name` varchar(200) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '应付供应商名称快照', + MODIFY COLUMN `supplier_category` varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '应付供应商类别约束', + MODIFY COLUMN `settlement_mode` varchar(16) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'ON_ACCOUNT账期应付或DIRECT_PAID业务已付', + MODIFY COLUMN `payment_method` varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '直接已付方式', + MODIFY COLUMN `payment_reference_no` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '直接已付流水号', + MODIFY COLUMN `currency` char(3) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'CNY' COMMENT '财务结算币种,当前固定CNY', + MODIFY COLUMN `review_status` varchar(16) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'DRAFT、PENDING、APPROVED或REJECTED', + MODIFY COLUMN `publication_status` varchar(16) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'UNPUBLISHED或PUBLISHED', + MODIFY COLUMN `source_status` varchar(16) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'ACTIVE、SUPERSEDED或VOIDED', + MODIFY COLUMN `review_reason` varchar(500) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '审核或作废原因', + MODIFY COLUMN `reviewer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '审核人ID', + MODIFY COLUMN `reviewer_name` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '瀹℃牳浜哄悕绉板揩鐓', + MODIFY COLUMN `created_by` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '创建人ID', + MODIFY COLUMN `updated_by` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '最后更新人ID', + MODIFY COLUMN `original_currency` varchar(8) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'CNY' COMMENT '原币币种', + MODIFY COLUMN `exchange_rate_month` char(7) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '汇率月份YYYY-MM', + MODIFY COLUMN `quantity_unit` varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '计费单位', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_fleet_external_booking` + MODIFY COLUMN `customer_id` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '委托客户及付款归属', + MODIFY COLUMN `incoming_no` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '正式来货单业务编号'; +ALTER TABLE `baiyue_lastleg`.`ll_fleet_finance_source` + MODIFY COLUMN `operator_id` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '提交操作人标识', + MODIFY COLUMN `request_key` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '原请求幂等键', + MODIFY COLUMN `request_hash` varchar(71) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '规范请求摘要,含sha256前缀', + MODIFY COLUMN `event_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '关联公共Outbox事件ID', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_fleet_pool_order_link` + MODIFY COLUMN `customer_id` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '商业客户隔离范围'; +ALTER TABLE `baiyue_lastleg`.`ll_fleet_quote` + MODIFY COLUMN `quote_no` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '报价业务编号', + MODIFY COLUMN `kind` varchar(16) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '报价用途采购或销售', + MODIFY COLUMN `party_name` varchar(160) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '往来单位名称快照', + MODIFY COLUMN `origin_city` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '发货城市', + MODIFY COLUMN `destination_code` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '目的仓业务编码', + MODIFY COLUMN `status` varchar(16) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '报价启停或删除状态', + MODIFY COLUMN `natural_key` char(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '报价组合去重摘要', + MODIFY COLUMN `create_key` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '创建幂等键', + MODIFY COLUMN `create_hash` char(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '创建请求含导入批次内容摘要', + MODIFY COLUMN `created_by` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '创建操作人标识', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_fleet_quote_history` + MODIFY COLUMN `action` varchar(24) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '报价操作类型', + MODIFY COLUMN `operator_id` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '操作人标识', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_inbound_fba_appointment` + MODIFY COLUMN `tenant_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'BAIYUE' COMMENT 'SaaS租户标识,当前单租户固定为BAIYUE', + MODIFY COLUMN `customer_id` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户租户ID', + MODIFY COLUMN `appointment_no` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '预约单号', + MODIFY COLUMN `appointment_status` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'PENDING', + MODIFY COLUMN `appointment_remark` varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `fba_warehouse_code` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `replacement_nos_json` longtext COLLATE utf8mb4_0900_ai_ci COMMENT '分批或合并后的新预约业务号列表,非空时原批次只读', + MODIFY COLUMN `created_by` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `confirmation_no` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '收货方正式外部确认号', + MODIFY COLUMN `timezone` varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '目的仓权威IANA时区,未维护不得确认', + MODIFY COLUMN `origin_warehouse_id` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '实际起运仓', + MODIFY COLUMN `replacement_keys_json` longtext COLLATE utf8mb4_0900_ai_ci COMMENT '替代批次不可变键列表,只用于内部业务关联', + MODIFY COLUMN `repartition_reason` varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '分批或合并原因,原安排及来源明细保留', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_inbound_fba_appointment_item` + MODIFY COLUMN `source_key` varchar(160) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '稳定ORDER或BOOKING货源键', + MODIFY COLUMN `source_version` varchar(160) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '保存时核实的来源版本', + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '本票所属客户', + MODIFY COLUMN `inbound_code` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '真实入仓码,不伪造', + MODIFY COLUMN `fba_warehouse_code` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '目的FBA仓编码', + MODIFY COLUMN `active_source_key` varchar(160) COLLATE utf8mb4_0900_ai_ci GENERATED ALWAYS AS ((case when (`active` = 1) then `source_key` else NULL end)) STORED COMMENT '有效货源唯一占用键', + MODIFY COLUMN `shipment_references_json` longtext COLLATE utf8mb4_0900_ai_ci COMMENT '本次采用的货件或追踪号核实快照', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_inbound_order` + MODIFY COLUMN `tenant_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'BAIYUE' COMMENT 'SaaS租户标识,当前单租户固定为BAIYUE', + MODIFY COLUMN `source_region` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '来源区域', + MODIFY COLUMN `source_inbound_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '来源入仓分片 ID', + MODIFY COLUMN `root_inbound_id` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '根入仓单 ID', + MODIFY COLUMN `source_order_id` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '来源订单 ID', + MODIFY COLUMN `source_order_no` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '来源订单号', + MODIFY COLUMN `root_order_id` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '根订单 ID', + MODIFY COLUMN `inbound_code` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '入仓码', + MODIFY COLUMN `destination_warehouse_id` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '目的仓 ID', + MODIFY COLUMN `destination_warehouse_code` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '目的仓编码', + MODIFY COLUMN `destination_warehouse_name` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '目的仓名称', + MODIFY COLUMN `destination_type` varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '目的类型:FBA按箱数接收、OVERSEAS逐箱扫码', + MODIFY COLUMN `source_event_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '来源事件 ID', + MODIFY COLUMN `source_loading_plan_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '装柜计划 ID', + MODIFY COLUMN `payload_hash` varchar(71) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '负载摘要', + MODIFY COLUMN `status` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '入仓单状态', + MODIFY COLUMN `exception_flag` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'NONE' COMMENT '异常标记', + MODIFY COLUMN `lx_code` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '一件代发领星入库单代码', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_inbound_order_item` + MODIFY COLUMN `tenant_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'BAIYUE' COMMENT 'SaaS租户标识,当前单租户固定为BAIYUE', + MODIFY COLUMN `source_inbound_detail_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '来源入仓明细ID', + MODIFY COLUMN `destination_warehouse_id` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '目的仓ID', + MODIFY COLUMN `destination_warehouse_code` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '目的仓编码', + MODIFY COLUMN `destination_warehouse_name` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '目的仓名称', + MODIFY COLUMN `fba_tracking_no` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'FBA跟踪号', + MODIFY COLUMN `shipment_no` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '货件编号', + MODIFY COLUMN `shipping_mark` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '唛头', + MODIFY COLUMN `sku_code` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'SKU编码', + MODIFY COLUMN `product_name_zh` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '中文品名', + MODIFY COLUMN `product_name_en` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '英文品名', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_inbound_pallet_merge` + MODIFY COLUMN `tenant_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'BAIYUE' COMMENT 'SaaS租户标识,当前单租户固定为BAIYUE', + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `merge_group_no` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `status` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL, + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_inbound_pda_operation` + MODIFY COLUMN `tenant_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'BAIYUE' COMMENT 'SaaS租户标识,当前单租户固定为BAIYUE', + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `action` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `idempotency_key` varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `request_hash` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `request_json` longtext COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `result_json` longtext COLLATE utf8mb4_0900_ai_ci NOT NULL, + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_inbound_pda_task` + MODIFY COLUMN `tenant_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'BAIYUE' COMMENT 'SaaS租户标识,当前单租户固定为BAIYUE', + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `task_no` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `status` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'PENDING', + MODIFY COLUMN `operator_id` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `operator_name` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_inbound_plan` + MODIFY COLUMN `tenant_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'BAIYUE' COMMENT 'SaaS租户标识,当前单租户固定为BAIYUE', + MODIFY COLUMN `plan_no` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '入库单号', + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '租户/客户账号标识', + MODIFY COLUMN `container_no` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '集装箱号', + MODIFY COLUMN `pallet_type` varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '人工计划托盘类型', + MODIFY COLUMN `creation_mode` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'AUTO' COMMENT '计划生成方式:AUTO柜级自动、MANUAL单票人工、WAREHOUSE_TRANSFER仓储转运', + MODIFY COLUMN `plan_remark` varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '入仓计划备注', + MODIFY COLUMN `status` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'PENDING_ASSIGNMENT' COMMENT '入库计划状态:PENDING_ASSIGNMENT待分配及后续正式状态', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_inbound_plan_carton` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户租户ID', + MODIFY COLUMN `source_inbound_detail_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '来源入库明细ID', + MODIFY COLUMN `sku_code` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'SKU编码快照', + MODIFY COLUMN `carton_mark` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '国内冻结箱唛', + MODIFY COLUMN `destination_warehouse_id` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '目的仓ID快照', + MODIFY COLUMN `destination_warehouse_code` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '目的仓编码快照', + MODIFY COLUMN `destination_warehouse_name` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '目的仓名称快照', + MODIFY COLUMN `status` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '箱唛状态:UNASSIGNED、ON_PALLET、AVAILABLE', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_inbound_plan_line` + MODIFY COLUMN `tenant_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'BAIYUE' COMMENT 'SaaS租户标识,当前单租户固定为BAIYUE', + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户账号标识', + MODIFY COLUMN `inbound_code` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '头程入仓码快照', + MODIFY COLUMN `destination_type` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'OVERSEAS' COMMENT '目的类型:FBA按箱数接收、OVERSEAS逐箱扫码', + MODIFY COLUMN `fba_warehouse_code` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'FBA仓库编码', + MODIFY COLUMN `destination_warehouse_id` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '目的仓ID快照', + MODIFY COLUMN `destination_warehouse_code` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '目的仓编码快照', + MODIFY COLUMN `destination_warehouse_name` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '目的仓名称快照', + MODIFY COLUMN `delivery_address` varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '私人仓目的地址快照', + MODIFY COLUMN `postal_code` varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '私人仓邮编快照', + MODIFY COLUMN `contact_name` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '私人仓联系人快照', + MODIFY COLUMN `contact_phone` varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '私人仓联系电话快照', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_inbound_plan_pallet` + MODIFY COLUMN `tenant_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'BAIYUE' COMMENT 'SaaS租户标识,当前单租户固定为BAIYUE', + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户账号标识', + MODIFY COLUMN `pallet_no` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '海外仓资源服务返回的真实托盘码', + MODIFY COLUMN `source_pallet_no` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '一件代发不可变来源托盘号快照', + MODIFY COLUMN `pallet_type` varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '人工计划托盘类型', + MODIFY COLUMN `destination_warehouse_id` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '目的仓ID快照', + MODIFY COLUMN `destination_warehouse_code` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '目的仓编码快照', + MODIFY COLUMN `destination_warehouse_name` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '目的仓名称快照', + MODIFY COLUMN `assignment_status` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'UNASSIGNED', + MODIFY COLUMN `destination_type` varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '目的类型:FBA或PRIVATE', + MODIFY COLUMN `delivery_address` varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '私人仓目的地址快照', + MODIFY COLUMN `postal_code` varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '私人仓邮编快照', + MODIFY COLUMN `contact_name` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '私人仓联系人快照', + MODIFY COLUMN `contact_phone` varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '私人仓联系电话快照', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_inbound_plan_pallet_allocation` + MODIFY COLUMN `tenant_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'BAIYUE' COMMENT 'SaaS租户标识,当前单租户固定为BAIYUE', + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户账号标识', + MODIFY COLUMN `status` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'PENDING_COUNT', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_inbound_stock_receipt` + MODIFY COLUMN `tenant_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'BAIYUE' COMMENT 'SaaS租户标识,当前单租户固定为BAIYUE', + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `inbound_code` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `sku_code` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `product_name_zh` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '中文品名快照', + MODIFY COLUMN `product_name_en` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '英文品名快照', + MODIFY COLUMN `sku_name_zh` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `source_detail_ids` varchar(2000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `pallet_no` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `source_pallet_no` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '一件代发不可变来源托盘号快照', + MODIFY COLUMN `warehouse_location_code` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `batch_no` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `inventory_operation_key` varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL, + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_inventory_balance` + MODIFY COLUMN `tenant_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'BAIYUE' COMMENT 'SaaS租户标识,当前单租户固定为BAIYUE', + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'Customer tenant', + MODIFY COLUMN `customer_name` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'Customer name snapshot', + MODIFY COLUMN `overseas_warehouse_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'Actual overseas warehouse ID', + MODIFY COLUMN `overseas_warehouse_code` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'Actual overseas warehouse code snapshot', + MODIFY COLUMN `overseas_warehouse_name` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'Actual overseas warehouse name snapshot', + MODIFY COLUMN `destination_warehouse_id` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'Customer destination warehouse ID snapshot', + MODIFY COLUMN `destination_warehouse_code` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT 'Customer destination warehouse code snapshot', + MODIFY COLUMN `destination_warehouse_name` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'Customer destination warehouse name snapshot', + MODIFY COLUMN `inbound_order_no` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `inbound_code` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `sku_code` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `sku_name_zh` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'Chinese product name', + MODIFY COLUMN `product_name_zh` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '中文品名快照', + MODIFY COLUMN `product_name_en` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '英文品名快照', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_inventory_ledger` + MODIFY COLUMN `tenant_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'BAIYUE' COMMENT 'SaaS租户标识,当前单租户固定为BAIYUE', + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `customer_name` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '瀹㈡埛鍚嶇О涓嶅彲鍙樺揩鐓�', + MODIFY COLUMN `overseas_warehouse_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `destination_warehouse_code` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '', + MODIFY COLUMN `sku_code` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `carton_mark` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `pallet_no` varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `batch_no` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `warehouse_location_code` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `business_type` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `business_order_no` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `inbound_code` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `inbound_order_no` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `outbound_order_no` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `source_event_id` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `idempotency_key` varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `status_from` varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `status_to` varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `operator_id` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `remark` varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_inventory_operation` + MODIFY COLUMN `tenant_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'BAIYUE' COMMENT 'SaaS租户标识,当前单租户固定为BAIYUE', + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `idempotency_key` varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `operation_type` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `request_hash` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `status` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'PROCESSING', + MODIFY COLUMN `result_json` longtext COLLATE utf8mb4_0900_ai_ci, + MODIFY COLUMN `last_error` varchar(2000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_inventory_position` + MODIFY COLUMN `tenant_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'BAIYUE' COMMENT 'SaaS租户标识,当前单租户固定为BAIYUE', + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `overseas_warehouse_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `destination_warehouse_code` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '', + MODIFY COLUMN `sku_code` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `pallet_no` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `batch_no` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '', + MODIFY COLUMN `warehouse_location_code` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'Informational default location code', + MODIFY COLUMN `status` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'ACTIVE', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_loading_event_inbox` + MODIFY COLUMN `event_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '事件全局唯一标识', + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '租户/客户账号标识', + MODIFY COLUMN `event_type` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '事件类型', + MODIFY COLUMN `payload_hash` varchar(71) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '规范化事件负载 SHA-256 摘要', + MODIFY COLUMN `status` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'RECEIVED' COMMENT '消费状态:RECEIVED/PROCESSED', + MODIFY COLUMN `last_error` varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '最近一次处理错误', + MODIFY COLUMN `tenant_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'BAIYUE' COMMENT 'SaaS租户标识,当前单租户固定为BAIYUE', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_loading_order_status_history` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户数据隔离键', + MODIFY COLUMN `from_status` varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '变更前状态', + MODIFY COLUMN `to_status` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '变更后状态', + MODIFY COLUMN `event_id` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '触发事件 ID', + MODIFY COLUMN `remark` varchar(500) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '状态变更备注', + MODIFY COLUMN `tenant_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'BAIYUE' COMMENT 'SaaS租户标识,当前单租户固定为BAIYUE', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_local_looseboard_history` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户租户ID', + MODIFY COLUMN `operation_type` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '操作类型:CREATE、UPDATE或STATUS', + MODIFY COLUMN `from_status` varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '变更前状态', + MODIFY COLUMN `to_status` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '变更后状态', + MODIFY COLUMN `operator_id` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '操作人ID', + MODIFY COLUMN `idempotency_key` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '操作幂等键', + MODIFY COLUMN `remark` varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '操作备注', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_local_looseboard_operation` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户租户ID', + MODIFY COLUMN `operation_type` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '操作类型', + MODIFY COLUMN `idempotency_key` varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '操作幂等键', + MODIFY COLUMN `request_hash` char(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '操作请求内容哈希', + MODIFY COLUMN `created_by` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '操作人ID', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_local_looseboard_order` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户租户ID', + MODIFY COLUMN `order_no` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '本土散板订单号', + MODIFY COLUMN `pallet_required` varchar(8) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '鏄?惁闇??鎵撶洏锛歒ES鎴朜O锛涙嫾杞︽潵婧愭湭鎻愪緵鏃朵负绌', + MODIFY COLUMN `transport_type` varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '鎺ュ崟杩愯緭瀹夋帓锛涗笅鍗曚笉棰勮?', + MODIFY COLUMN `origin` varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '起运地', + MODIFY COLUMN `origin_address` varchar(500) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '起运详细地址', + MODIFY COLUMN `destination` varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '目的地', + MODIFY COLUMN `destination_address` varchar(500) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '目的详细地址、码头或仓号', + MODIFY COLUMN `currency` char(3) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'USD' COMMENT '预算币种', + MODIFY COLUMN `status` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'PENDING_ACCEPTANCE' COMMENT '本土散板订单状态', + MODIFY COLUMN `remark` varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '备注', + MODIFY COLUMN `idempotency_key` varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '创建幂等键', + MODIFY COLUMN `request_hash` char(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '创建请求内容哈希', + MODIFY COLUMN `created_by` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '创建人ID', + MODIFY COLUMN `product_name` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '商品名称', + MODIFY COLUMN `origin_contact_name` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '起运地联系人', + MODIFY COLUMN `origin_contact_phone` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '起运地联系电话', + MODIFY COLUMN `destination_contact_name` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '目的地联系人', + MODIFY COLUMN `destination_contact_phone` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '目的地联系电话', + MODIFY COLUMN `delivery_mode` varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '入仓方式:送货到仓或仓库上门取货', + MODIFY COLUMN `operating_warehouse_id` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '实际收货海外仓ID', + MODIFY COLUMN `destination_type` varchar(16) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '目的地类型:FBA或私人地址', + MODIFY COLUMN `destination_postal_code` varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '私人目的地邮编', + MODIFY COLUMN `delivery_contact_name` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '送货到仓联系人', + MODIFY COLUMN `delivery_contact_phone` varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '送货到仓联系电话', + MODIFY COLUMN `tracking_no` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '物流跟踪号', + MODIFY COLUMN `transfer_no` varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '关联的仓储转运收货单号', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_monthly_exchange_rate` + MODIFY COLUMN `rate_month` char(7) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '汇率月份,格式yyyy-MM', + MODIFY COLUMN `base_currency` char(3) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'USD' COMMENT '基准币种', + MODIFY COLUMN `quote_currency` char(3) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'CNY' COMMENT '报价币种', + MODIFY COLUMN `status` varchar(16) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'ACTIVE' COMMENT '状态:ACTIVE/INACTIVE', + MODIFY COLUMN `remark` varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '备注', + MODIFY COLUMN `created_by` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '创建人', + MODIFY COLUMN `updated_by` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '更新人', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_monthly_exchange_rate_history` + MODIFY COLUMN `before_status` varchar(16) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '变更前状态', + MODIFY COLUMN `after_status` varchar(16) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '变更后状态', + MODIFY COLUMN `operator_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '操作人', + MODIFY COLUMN `remark` varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '变更说明', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_order` + MODIFY COLUMN `order_no` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '尾程单号', + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '租户/客户账号标识', + MODIFY COLUMN `order_type` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'FIRST_LEG' COMMENT '订单类型', + MODIFY COLUMN `lx_code` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '一件代发领星入库单代码', + MODIFY COLUMN `destination_type` varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '目的类型:FBA按箱数接收、OVERSEAS逐箱扫码', + MODIFY COLUMN `inbound_code` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '真实来源入仓码;无入仓关系的海外本土业务为空', + MODIFY COLUMN `source_region` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '来源区域/系统', + MODIFY COLUMN `source_inbound_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '最终实装 LOADED 分片 ID,建单幂等键', + MODIFY COLUMN `root_inbound_id` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '根入仓单 ID', + MODIFY COLUMN `source_order_id` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '来源订单 ID', + MODIFY COLUMN `source_order_no` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '来源订单号', + MODIFY COLUMN `root_order_id` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '根订单 ID', + MODIFY COLUMN `customer_name` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '货主名称', + MODIFY COLUMN `source_warehouse_id` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '来源国内装柜仓 ID 快照', + MODIFY COLUMN `source_warehouse_name` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '来源国内装柜仓名称快照', + MODIFY COLUMN `overseas_warehouse_id` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '海外接柜仓ID;新建尾程订单由业务服务预分配', + MODIFY COLUMN `overseas_warehouse_code` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '海外接柜仓业务编码快照', + MODIFY COLUMN `overseas_warehouse_name` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '海外接柜仓名称快照', + MODIFY COLUMN `transfer_warehouse_id` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '转入目的仓ID', + MODIFY COLUMN `transfer_warehouse_code` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '转入目的仓编码', + MODIFY COLUMN `transfer_warehouse_name` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '转入目的仓名称', + MODIFY COLUMN `fba_warehouse_code` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'FBA 仓库编码', + MODIFY COLUMN `container_no` varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '集装箱号', + MODIFY COLUMN `container_status` varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT '待转柜' COMMENT '货柜状态', + MODIFY COLUMN `hold_reason` varchar(500) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '当前暂扣原因;解除后为空', + MODIFY COLUMN `inbound_order_no` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '入库单号', + MODIFY COLUMN `outbound_order_no` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '出库单号', + MODIFY COLUMN `delivery_order_no` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '派送单号', + MODIFY COLUMN `relabel_order_no` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '换标单号', + MODIFY COLUMN `relabel_status` varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '换标状态', + MODIFY COLUMN `pallet_merge_flag` varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '合托标记', + MODIFY COLUMN `pallet_merge_batch_id` varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '平台预标记合托分组号', + MODIFY COLUMN `current_status` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'PENDING_ASSIGNMENT' COMMENT '当前业务状态', + MODIFY COLUMN `private_remark` varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '备注(仅自己)', + MODIFY COLUMN `customer_remark` varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '备注(客户)', + MODIFY COLUMN `warehouse_remark` varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '备注(仓库)', + MODIFY COLUMN `source_event_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '来源事件 ID', + MODIFY COLUMN `source_loading_plan_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '来源装柜计划 ID', + MODIFY COLUMN `source_container_booking_id` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '来源头程货柜预订ID', + MODIFY COLUMN `source_payload_hash` varchar(71) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '来源事件负载摘要', + MODIFY COLUMN `tenant_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'BAIYUE' COMMENT 'SaaS租户标识,当前单租户固定为BAIYUE', + MODIFY COLUMN `sub_order_status` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '子单据状态汇总', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_order_budget_reservation` + MODIFY COLUMN `customer_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户隔离标识', + MODIFY COLUMN `order_no` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '成功创建的业务单号', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_order_command_record` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户数据隔离键', + MODIFY COLUMN `action` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '命令动作:MANUAL_PLAN、HOLD、MANUAL_NODES', + MODIFY COLUMN `operator_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '平台操作人ID', + MODIFY COLUMN `idempotency_key` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户端幂等键', + MODIFY COLUMN `request_hash` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '规范化请求SHA-256摘要', + MODIFY COLUMN `result_ref` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '结果业务引用', + MODIFY COLUMN `result_json` longtext COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '首次成功响应JSON快照', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_order_detail` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户数据隔离键', + MODIFY COLUMN `source_inbound_detail_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '来源入仓明细 ID', + MODIFY COLUMN `sku_code` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'SKU 编码', + MODIFY COLUMN `product_name_zh` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '中文品名', + MODIFY COLUMN `product_name_en` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '英文品名', + MODIFY COLUMN `destination_warehouse_id` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '目的仓 ID', + MODIFY COLUMN `destination_warehouse_code` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '目的仓编码', + MODIFY COLUMN `destination_warehouse_name` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '目的仓名称', + MODIFY COLUMN `fba_tracking_no` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'FBA跟踪号', + MODIFY COLUMN `shipment_no` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '货件编号', + MODIFY COLUMN `shipping_mark` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '唛头', + MODIFY COLUMN `tenant_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'BAIYUE' COMMENT 'SaaS租户标识,当前单租户固定为BAIYUE', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_order_fee` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户数据隔离键', + MODIFY COLUMN `fee_item` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '费用项目', + MODIFY COLUMN `currency` varchar(3) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '币种代码', + MODIFY COLUMN `status` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'PENDING_REVIEW' COMMENT '费用状态:PENDING_REVIEW待审核、APPROVED审核通过、REJECTED已驳回', + MODIFY COLUMN `supplier_description` varchar(500) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '供应商或费用说明', + MODIFY COLUMN `created_by` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '创建操作人', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_order_hold_history` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户数据隔离键', + MODIFY COLUMN `reason` varchar(500) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '暂扣或解除原因', + MODIFY COLUMN `operator_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '操作人ID', + MODIFY COLUMN `operator_name` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '操作人名称', + MODIFY COLUMN `idempotency_key` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户端幂等键', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_order_manual_node` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户数据隔离键', + MODIFY COLUMN `node_code` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '八行人工展示节点稳定代码,与Carrier标准节点隔离', + MODIFY COLUMN `source` varchar(16) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'MANUAL' COMMENT '节点来源,固定MANUAL', + MODIFY COLUMN `updated_by` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '最近操作人ID', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_order_manual_node_history` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户数据隔离键', + MODIFY COLUMN `nodes_json` longtext COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '本次全量人工节点JSON快照', + MODIFY COLUMN `operator_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '操作人ID', + MODIFY COLUMN `operator_name` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '操作人名称', + MODIFY COLUMN `idempotency_key` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户端幂等键', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_order_no_daily_sequence` + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_order_operation_log` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户数据隔离键', + MODIFY COLUMN `operation_node` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '操作节点', + MODIFY COLUMN `operator_name` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '操作人名称', + MODIFY COLUMN `result` varchar(500) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '处理结果', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_order_source_pallet` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户隔离键', + MODIFY COLUMN `source_pallet_no` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '头程冻结托盘码', + MODIFY COLUMN `source_event_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '来源装柜完成事件ID', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_order_status_history` + MODIFY COLUMN `from_status` varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '变更前状态', + MODIFY COLUMN `to_status` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '变更后状态', + MODIFY COLUMN `reason` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '变更原因', + MODIFY COLUMN `tenant_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'BAIYUE' COMMENT 'SaaS租户标识,当前单租户固定为BAIYUE', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_outbound_order` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `outbound_no` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `dispatch_no` varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '派车单号,一件代发为空', + MODIFY COLUMN `overseas_warehouse_id` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `status` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'WAIT_OUTBOUND', + MODIFY COLUMN `outbound_type` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'DISPATCH_DELIVERY' COMMENT '出库类型', + MODIFY COLUMN `lx_codes` varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '关联一件代发领星入库单代码汇总', + MODIFY COLUMN `recipient_info` varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '一件代发收件信息', + MODIFY COLUMN `request_hash` char(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '一件代发创建请求摘要,其他出库类型为空', + MODIFY COLUMN `idempotency_key` varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `created_by` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `tenant_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'BAIYUE' COMMENT 'SaaS租户标识,当前单租户固定为BAIYUE', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_outbound_pallet_item` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `pallet_no` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '鎵樼洏鍙凤紝绠辩淮搴﹀嚭搴撲负绌�', + MODIFY COLUMN `destination_warehouse_id` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `destination_warehouse_code` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `destination_warehouse_name` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `status` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'WAIT_PICK' COMMENT '拣货状态', + MODIFY COLUMN `order_refs` varchar(2000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `inbound_code` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `tenant_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'BAIYUE' COMMENT 'SaaS租户标识,当前单租户固定为BAIYUE', + MODIFY COLUMN `sku_code` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '一件代发SKU', + MODIFY COLUMN `carton_marks` varchar(4000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_outbound_pda_pick` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `idempotency_key` varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL, + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_outbound_pda_task` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户租户ID', + MODIFY COLUMN `task_no` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'PDA任务号', + MODIFY COLUMN `status` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'PENDING' COMMENT '任务状态:待拣货、拣货中、已完成', + MODIFY COLUMN `completion_idempotency_key` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '完成命令幂等键', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_overseas_procurement_fee` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户ID', + MODIFY COLUMN `fee_no` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '费用业务号', + MODIFY COLUMN `fee_item_code` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '费用项编码', + MODIFY COLUMN `fee_item_name` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '费用项名称', + MODIFY COLUMN `currency` char(3) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'CNY' COMMENT '费用币种', + MODIFY COLUMN `voucher_file_name` varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '凭证文件名', + MODIFY COLUMN `remark` varchar(500) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '说明', + MODIFY COLUMN `wallet_status` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '资金状态', + MODIFY COLUMN `wallet_error_code` varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '资金错误码', + MODIFY COLUMN `wallet_error_message` varchar(500) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '资金错误说明', + MODIFY COLUMN `wallet_command_event_id` varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '当前资金指令事件ID', + MODIFY COLUMN `created_by` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '登记人', + MODIFY COLUMN `purpose` varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '费用业务用途;由履约节点确定', + MODIFY COLUMN `confirmation_origin` varchar(16) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'BUSINESS' COMMENT '核定入口:BUSINESS业务节点、FINANCIAL财务操作', + MODIFY COLUMN `determination` varchar(24) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'POSITIVE正额、ZERO零元、NOT_APPLICABLE不适用', + MODIFY COLUMN `original_currency` char(3) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '核定原币CNY或USD', + MODIFY COLUMN `exchange_rate_month` char(7) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '已确认汇率所属月份', + MODIFY COLUMN `wallet_business_no` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '资金冻结业务号;实际国际运费引用原预估', + MODIFY COLUMN `wallet_hold_id` varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'Finance冻结引用', + MODIFY COLUMN `wallet_ledger_id` varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'Finance实际处理流水引用', + MODIFY COLUMN `settled_command_event_id` varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '成功实扣或退款的资金命令事件', + MODIFY COLUMN `idempotency_key` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '閫??璇锋眰骞傜瓑閿?紝鐙?珛浜庝笟鍔″崟鍙', + MODIFY COLUMN `request_hash` char(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '确认请求摘要', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_overseas_procurement_file` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户ID', + MODIFY COLUMN `category` varchar(16) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'RECEIPT签收文件或VOUCHER费用凭证', + MODIFY COLUMN `file_name` varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '文件名快照', + MODIFY COLUMN `content_type` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '媒体类型', + MODIFY COLUMN `created_by` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '上传人', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_overseas_procurement_order` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `customer_name` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `order_no` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `purchase_category` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `product_name` varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `product_specification` varchar(500) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `product_url` varchar(2000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `currency` char(3) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'USD', + MODIFY COLUMN `receiving_address` varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `contact_name` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `contact_phone` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `purchase_warehouse_id` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `purchase_warehouse_name` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `domestic_carrier` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `domestic_tracking_no` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `overseas_carrier` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `overseas_tracking_no` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `status` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'PENDING_REVIEW', + MODIFY COLUMN `remark` varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `idempotency_key` varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `request_hash` char(64) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `created_by` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `reviewed_by` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `review_remark` varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `review_idempotency_key` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '审核幂等键', + MODIFY COLUMN `review_request_hash` char(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '审核请求内容哈希', + MODIFY COLUMN `tenant_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'BAIYUE' COMMENT 'SaaS租户标识,当前单租户固定为BAIYUE', + MODIFY COLUMN `procurement_direction` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'OVERSEAS_TO_DOMESTIC' COMMENT '采购方向:海外采购回国内/国内采购发海外', + MODIFY COLUMN `customer_rejection_reason` varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '客户采购结果驳回原因', + MODIFY COLUMN `rejected_photo_ids` text COLLATE utf8mb4_0900_ai_ci COMMENT '本轮客户驳回的收货照片主键快照', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_overseas_procurement_outbound` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `outbound_no` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `carrier` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `tracking_no` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `remark` varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `created_by` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `tenant_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'BAIYUE' COMMENT 'SaaS租户标识,当前单租户固定为BAIYUE', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_overseas_procurement_status_history` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `from_status` varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `to_status` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `operator_id` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `idempotency_key` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '操作幂等键', + MODIFY COLUMN `remark` varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `tenant_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'BAIYUE' COMMENT 'SaaS租户标识,当前单租户固定为BAIYUE', + MODIFY COLUMN `operation_type` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '操作类型:CREATE、REVIEW或UPDATE', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_overseas_quote` + MODIFY COLUMN `name` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '报价名称', + MODIFY COLUMN `warehouse_code` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '合作仓编码快照', + MODIFY COLUMN `warehouse_name` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '合作仓名称快照', + MODIFY COLUMN `country_code` varchar(2) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '国家二字码快照', + MODIFY COLUMN `provider_name` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '报价方', + MODIFY COLUMN `currency` char(3) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '币种', + MODIFY COLUMN `version_code` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '业务版本号', + MODIFY COLUMN `status` varchar(16) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'INACTIVE' COMMENT '状态:ACTIVE/INACTIVE', + MODIFY COLUMN `remark` varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '备注', + MODIFY COLUMN `created_by` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '创建人', + MODIFY COLUMN `updated_by` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '更新人', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_overseas_quote_fee` + MODIFY COLUMN `item` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '费用项目', + MODIFY COLUMN `billing_unit` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '计费单位', + MODIFY COLUMN `remark` varchar(500) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '备注', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_overseas_quote_transit_price` + MODIFY COLUMN `fba_warehouse_code` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'FBA仓编码', + MODIFY COLUMN `delivery_method` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '报价运输方式', + MODIFY COLUMN `remark` varchar(500) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '备注', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_pda_inventory_move` + MODIFY COLUMN `move_no` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '移库操作单号', + MODIFY COLUMN `pallet_code` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '实物托盘码快照', + MODIFY COLUMN `source_location_code` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '来源库位码快照', + MODIFY COLUMN `target_location_code` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '目标库位码快照', + MODIFY COLUMN `operator_key` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'PDA操作账号标识', + MODIFY COLUMN `reason` varchar(500) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '移库原因', + MODIFY COLUMN `idempotency_key` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '移库幂等键', + MODIFY COLUMN `request_hash` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '移库请求哈希', + MODIFY COLUMN `status` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '移库处理状态', + MODIFY COLUMN `result_json` longtext COLLATE utf8mb4_0900_ai_ci COMMENT '成功响应JSON', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_pda_operation_history` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '作业涉及的客户标识', + MODIFY COLUMN `operator_key` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'PDA操作账号标识', + MODIFY COLUMN `operation_type` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '作业类型', + MODIFY COLUMN `business_key` varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '作业业务键', + MODIFY COLUMN `status` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '作业结果状态', + MODIFY COLUMN `idempotency_key` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '写操作幂等键', + MODIFY COLUMN `request_hash` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '写请求内容哈希', + MODIFY COLUMN `summary` varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '作业结果摘要', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_pda_pallet_operation` + MODIFY COLUMN `operator_key` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'PDA操作账号标识', + MODIFY COLUMN `idempotency_key` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '设备幂等键', + MODIFY COLUMN `operation_type` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '托盘作业类型', + MODIFY COLUMN `request_hash` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '请求内容摘要', + MODIFY COLUMN `status` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '处理状态:PROCESSING、COMPLETED、FAILED_COMPENSATED、COMPENSATION_REQUIRED', + MODIFY COLUMN `result_json` longtext COLLATE utf8mb4_0900_ai_ci COMMENT '成功响应快照', + MODIFY COLUMN `remote_transitions_json` longtext COLLATE utf8mb4_0900_ai_ci COMMENT '实物托盘状态变更回执', + MODIFY COLUMN `last_error` varchar(2000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '最后失败原因', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_pda_stocktake_action` + MODIFY COLUMN `operator_key` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'PDA操作账号标识', + MODIFY COLUMN `idempotency_key` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '动作幂等键', + MODIFY COLUMN `action_type` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '盘点动作类型', + MODIFY COLUMN `request_hash` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '动作请求哈希', + MODIFY COLUMN `status` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '动作处理状态', + MODIFY COLUMN `result_json` longtext COLLATE utf8mb4_0900_ai_ci COMMENT '成功响应JSON', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_pda_stocktake_item` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '库存所属客户标识', + MODIFY COLUMN `sku_code` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'SKU快照', + MODIFY COLUMN `product_name` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '品名快照', + MODIFY COLUMN `inbound_code` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '入仓码快照', + MODIFY COLUMN `pallet_code` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '实物托盘码快照', + MODIFY COLUMN `location_code` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '库位码快照', + MODIFY COLUMN `status` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '盘点明细状态', + MODIFY COLUMN `counted_by` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '盘点操作账号标识', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_pda_stocktake_task` + MODIFY COLUMN `task_no` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '盘点任务号', + MODIFY COLUMN `scope_type` varchar(16) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '盘点范围类型LOCATION或PALLET', + MODIFY COLUMN `scope_code` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '盘点库位码或托盘码快照', + MODIFY COLUMN `status` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '盘点任务状态', + MODIFY COLUMN `created_by` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '创建操作账号标识', + MODIFY COLUMN `submitted_by` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '提交操作账号标识', + MODIFY COLUMN `submit_reason` varchar(500) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '盘点差异提交原因', + MODIFY COLUMN `idempotency_key` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '创建幂等键', + MODIFY COLUMN `request_hash` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '创建请求哈希', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_physical_pallet_guard` + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_procurement_budget_authorization` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户隔离键', + MODIFY COLUMN `previous_currency` char(3) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '原预算币种', + MODIFY COLUMN `currency` char(3) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户授权币种', + MODIFY COLUMN `reason` varchar(1000) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '授权依据', + MODIFY COLUMN `reviewed_by` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '核验人员', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_procurement_carton` + MODIFY COLUMN `carton_no` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '采购订单内业务箱号', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_procurement_carton_source` + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_procurement_closure` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户隔离键', + MODIFY COLUMN `closure_no` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '异常关闭业务号', + MODIFY COLUMN `financial_conclusion` varchar(1000) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '已知费用及后续无需收费的处理结论,不改变资金事实', + MODIFY COLUMN `reason` varchar(1000) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '内部关闭原因', + MODIFY COLUMN `client_summary` varchar(500) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '人工确认的对客关闭摘要', + MODIFY COLUMN `created_by` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '采购管理确认人', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_procurement_closure_item` + MODIFY COLUMN `source_kind` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '购买未达、在途未达、合格、破损或多货来源', + MODIFY COLUMN `decision` varchar(40) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '供应商保管、退还、报失、客户授权保管或处置', + MODIFY COLUMN `destination` varchar(500) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '实际去向及接收方说明', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_procurement_command` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户隔离键', + MODIFY COLUMN `action` varchar(48) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '采购节点动作', + MODIFY COLUMN `idempotency_key` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '操作幂等键', + MODIFY COLUMN `request_hash` char(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '请求摘要', + MODIFY COLUMN `operator_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '执行人', + MODIFY COLUMN `reason` varchar(1000) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '操作依据', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_procurement_control_note` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户隔离键', + MODIFY COLUMN `note_no` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '更正说明业务号', + MODIFY COLUMN `target_type` varchar(24) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '购买、仓库、实收或交寄事实类型', + MODIFY COLUMN `reason` varchar(1000) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '内部更正说明', + MODIFY COLUMN `client_summary` varchar(500) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '人工确认的对客摘要,不复制内部原始内容', + MODIFY COLUMN `created_by` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '采购管理操作人', + MODIFY COLUMN `resolved_by` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '处理确认人', + MODIFY COLUMN `resolution_reason` varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '内部处理结果', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_procurement_daily_sequence` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户ID', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_procurement_disposition` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户隔离键', + MODIFY COLUMN `leg` varchar(16) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'OVERSEAS海外或DOMESTIC国内', + MODIFY COLUMN `kind` varchar(24) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'SHORTAGE短少、DAMAGED破损、EXTRA多货', + MODIFY COLUMN `decision` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'ACCEPT_SHORTAGE确认短少、DISPOSE_DAMAGED拒收或处置、RETURN_EXTRA退回多货', + MODIFY COLUMN `reason` varchar(1000) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '处置依据', + MODIFY COLUMN `created_by` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '采购管理人员', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_procurement_estimate_review` + MODIFY COLUMN `reviewed_by` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '核价人员', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_procurement_fee_correction` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户隔离键', + MODIFY COLUMN `correction_no` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '财务更正业务号', + MODIFY COLUMN `idempotency_key` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '本单更正请求幂等键', + MODIFY COLUMN `request_hash` char(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '更正目标及凭证请求摘要', + MODIFY COLUMN `original_currency` char(3) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '沿用原费用币种', + MODIFY COLUMN `exchange_rate_month` char(7) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '原汇率月份,不取本次操作月份', + MODIFY COLUMN `reason` varchar(500) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '更正依据', + MODIFY COLUMN `created_by` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '授权财务操作人', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_procurement_fee_correction_item` + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_procurement_flow` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户隔离键', + MODIFY COLUMN `purchased_product_name` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '实际购买商品', + MODIFY COLUMN `purchased_specification` varchar(500) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '实际购买规格', + MODIFY COLUMN `purchase_currency` char(3) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '供应商实付原币CNY或USD', + MODIFY COLUMN `purchase_issue` varchar(500) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '购买未核定或异常说明', + MODIFY COLUMN `actual_warehouse_code` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '实际收货仓编码快照', + MODIFY COLUMN `actual_warehouse_name` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '实际收货仓名称快照', + MODIFY COLUMN `domestic_receiver_name` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '国际段国内收件人', + MODIFY COLUMN `domestic_receiver_phone` varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '国际段国内收件电话', + MODIFY COLUMN `domestic_receiver_address` varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '国际段国内接收地址', + MODIFY COLUMN `control_reason` varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '取消或关闭原因', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_procurement_label_item` + MODIFY COLUMN `carrier` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '承运商', + MODIFY COLUMN `tracking_no` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '箱主或子运单号', + MODIFY COLUMN `applied_by` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '贴单确认人', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_procurement_label_set` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户隔离键', + MODIFY COLUMN `leg` varchar(16) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'OVERSEAS国际段或DOMESTIC寄客户段', + MODIFY COLUMN `cargo_hash` char(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '箱规及有效货物来源摘要', + MODIFY COLUMN `created_by` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '制单操作人', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_procurement_manifest` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户隔离键', + MODIFY COLUMN `status` varchar(16) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'DRAFT草稿、CONFIRMED已确认、SUPERSEDED已替代', + MODIFY COLUMN `created_by` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '制单人', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_procurement_receipt` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户隔离键', + MODIFY COLUMN `receipt_no` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '采购收货业务编号', + MODIFY COLUMN `leg` varchar(16) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'OVERSEAS海外或DOMESTIC国内', + MODIFY COLUMN `created_by` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '登记人员', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_procurement_repack` + MODIFY COLUMN `reason` varchar(1000) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '换包装依据', + MODIFY COLUMN `created_by` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '国内作业人员', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_procurement_shipment` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户隔离键', + MODIFY COLUMN `shipment_no` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '交寄业务编号', + MODIFY COLUMN `leg` varchar(16) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'OVERSEAS国际交寄或DOMESTIC寄客户', + MODIFY COLUMN `created_by` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '交寄确认人', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_procurement_shipment_item` + MODIFY COLUMN `carrier` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '承运商快照', + MODIFY COLUMN `tracking_no` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '运单号快照', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_procurement_wallet_attempt` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户隔离键', + MODIFY COLUMN `operation` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '资金操作:FREEZE冻结、SETTLE结算、SETTLE_ACTUAL实际结算、RELEASE释放、REFUND退款', + MODIFY COLUMN `business_no` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'Finance资金业务号', + MODIFY COLUMN `command_event_id` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '资金指令事件唯一引用', + MODIFY COLUMN `status` varchar(16) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '资金请求结果:PENDING未决、SUCCEEDED成功、FAILED明确失败', + MODIFY COLUMN `result_hash` char(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '原业务回执摘要', + MODIFY COLUMN `wallet_hold_id` varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'Finance冻结引用', + MODIFY COLUMN `wallet_ledger_id` varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'Finance处理流水引用', + MODIFY COLUMN `error_code` varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '业务失败码', + MODIFY COLUMN `error_message` varchar(500) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '内部失败诊断,不直接对客展示', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_receiving_container_operation` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户账号标识', + MODIFY COLUMN `operator_key` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '操作账号唯一标识', + MODIFY COLUMN `action` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '操作动作', + MODIFY COLUMN `idempotency_key` varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户端幂等键', + MODIFY COLUMN `request_hash` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '请求内容哈希', + MODIFY COLUMN `request_json` longtext COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '请求内容快照', + MODIFY COLUMN `result_json` longtext COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '响应内容快照', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_receiving_container_session` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '所选柜解析出的客户标识', + MODIFY COLUMN `container_no` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '集装箱号', + MODIFY COLUMN `overseas_warehouse_name` varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '仓库服务校验后的海外实际收货共享物理仓名称快照', + MODIFY COLUMN `operator_key` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '操作账号唯一标识', + MODIFY COLUMN `operator_name` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '操作账号名称', + MODIFY COLUMN `active_operator_key` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '未完成会话平台操作账号全局唯一锁键', + MODIFY COLUMN `active_container_key` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '未完成会话柜号全局唯一锁键', + MODIFY COLUMN `status` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'WAIT_SIGN' COMMENT '会话状态:WAIT_SIGN、SIGNED、RECEIVING、COMPLETED', + MODIFY COLUMN `source_container_booking_id` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '稳定来源柜预订ID', + MODIFY COLUMN `container_fact_hash` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '领取时整柜订单与计划事实摘要', + MODIFY COLUMN `overseas_warehouse_code` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '海外接柜仓业务编码快照', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_receiving_fba_pallet_count` + MODIFY COLUMN `group_key` varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '柜号与FBA仓组成的收货组键', + MODIFY COLUMN `pallet_code` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '现有FBA托盘码', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_receiving_sku_receipt` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户账号标识', + MODIFY COLUMN `inbound_code` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '入仓码快照', + MODIFY COLUMN `sku_code` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'SKU编码快照', + MODIFY COLUMN `product_name` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '产品名称快照', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_receiving_warehouse_operation` + MODIFY COLUMN `source_container_booking_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '稳定来源柜预订ID', + MODIFY COLUMN `idempotency_key` varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '平台请求幂等键', + MODIFY COLUMN `request_hash` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '规范化修改请求SHA-256摘要', + MODIFY COLUMN `operator_name` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '平台操作人名称', + MODIFY COLUMN `remark` varchar(500) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '接柜仓修改备注', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_relabel_application` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `relabel_no` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `customer_order_no` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `overseas_warehouse_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `contact_name` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `contact_phone` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `delivery_method` varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '派送方式:TRUCK卡车、PICKUP自提、EXPRESS快递;仅涉及箱唛换标时必填', + MODIFY COLUMN `shipping_label_file_id` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '客户上传面单文件ID', + MODIFY COLUMN `shipping_label_file_name` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '客户上传面单文件名', + MODIFY COLUMN `shipping_label_file_url` varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '客户上传面单下载地址', + MODIFY COLUMN `fulfillment_status` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'WAIT_RELABEL' COMMENT '换标后履约状态', + MODIFY COLUMN `outbound_order_no` varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '自提或快递生成的换标出库单号', + MODIFY COLUMN `customer_confirmation_key` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '客户确认完成幂等键', + MODIFY COLUMN `remark` varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `total_file_id` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '总换标PDF文件ID', + MODIFY COLUMN `total_file_name` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '总换标PDF文件名', + MODIFY COLUMN `total_file_url` varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '总换标PDF预览地址', + MODIFY COLUMN `carton_total_file_id` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '箱唛总文件ID', + MODIFY COLUMN `carton_total_file_name` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '箱唛总文件名', + MODIFY COLUMN `carton_total_file_url` varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '箱唛总文件地址', + MODIFY COLUMN `sku_total_file_id` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'SKU总文件ID', + MODIFY COLUMN `sku_total_file_name` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'SKU总文件名', + MODIFY COLUMN `sku_total_file_url` varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'SKU总文件地址', + MODIFY COLUMN `status` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'PENDING_PROCESSING', + MODIFY COLUMN `charge_currency` char(3) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'CNY' COMMENT '费用币种', + MODIFY COLUMN `wallet_status` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'FREEZE_PENDING' COMMENT '资金状态', + MODIFY COLUMN `wallet_error_code` varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '资金错误码', + MODIFY COLUMN `wallet_error_message` varchar(500) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '资金错误说明', + MODIFY COLUMN `wallet_command_event_id` varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '当前资金指令事件ID', + MODIFY COLUMN `idempotency_key` varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `request_hash` char(64) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `created_by` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `tenant_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'BAIYUE' COMMENT 'SaaS租户标识,当前单租户固定为BAIYUE', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_relabel_item` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `sku_code` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'SKU编码(选填)', + MODIFY COLUMN `product_image_file_id` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `product_image_file_name` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `product_image_url` varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `product_name_zh` varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `shipment_no` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '货件编号(无FBA去向时为空)', + MODIFY COLUMN `tracking_code` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `new_sku_code` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '换标后SKU编码', + MODIFY COLUMN `label_requirement` varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `remark` varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '货物明细备注', + MODIFY COLUMN `relabel_file_id` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `relabel_file_name` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `relabel_file_url` varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `tenant_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'BAIYUE' COMMENT 'SaaS租户标识,当前单租户固定为BAIYUE', + MODIFY COLUMN `carton_mark` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '瀹㈡埛鑷湁绠卞敍', + MODIFY COLUMN `source_carton_mark` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '閫�璐ф崲绠卞敍鍘熺鍞涘揩鐓�', + MODIFY COLUMN `new_carton_mark` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '閫�璐ф崲绠卞敍鏂扮鍞�', + MODIFY COLUMN `fba_carton_mark` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '鍏宠仈FBA绠卞敍', + MODIFY COLUMN `destination_warehouse_id` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '该箱目的海外仓ID', + MODIFY COLUMN `destination_fba_warehouse_code` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '可选FBA目的仓编码(与海外操作仓分离)', + MODIFY COLUMN `operation_type` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'CARTON_LABEL' COMMENT '换标模式:箱唛、SKU或组合换标', + MODIFY COLUMN `source_inbound_order_no` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `source_inbound_code` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `source_warehouse_id` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `source_warehouse_code` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `source_warehouse_name` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_relabel_pda_history` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户数据隔离键', + MODIFY COLUMN `action` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '现场动作类型', + MODIFY COLUMN `from_status` varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '动作前任务状态', + MODIFY COLUMN `to_status` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '动作后任务状态', + MODIFY COLUMN `operator_key` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '操作账号标识', + MODIFY COLUMN `operator_name` varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '操作员名称快照', + MODIFY COLUMN `detail_json` longtext COLLATE utf8mb4_0900_ai_ci COMMENT '动作详情JSON', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_relabel_pda_operation` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户数据隔离键', + MODIFY COLUMN `operator_key` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '请求操作账号标识', + MODIFY COLUMN `action` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '幂等操作动作', + MODIFY COLUMN `idempotency_key` varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '设备生成的幂等键', + MODIFY COLUMN `request_hash` char(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '请求内容哈希', + MODIFY COLUMN `result_json` longtext COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '成功响应快照JSON', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_relabel_pda_task` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户数据隔离键', + MODIFY COLUMN `relabel_no` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '换标申请单号快照', + MODIFY COLUMN `status` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '任务状态:PROCESSING、WAIT_REVIEW或COMPLETED', + MODIFY COLUMN `operator_key` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '认领操作账号标识', + MODIFY COLUMN `operator_name` varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '认领操作员名称快照', + MODIFY COLUMN `reviewer_key` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '复核账号标识', + MODIFY COLUMN `reviewer_name` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '复核员名称快照', + MODIFY COLUMN `completion_remark` varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '完成提交备注', + MODIFY COLUMN `review_decision` varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '最近复核结论:APPROVE、REWORK或CUSTOMER_CONFIRMED', + MODIFY COLUMN `review_remark` varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '最近复核备注', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_relabel_pda_unit_fact` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户数据隔离键', + MODIFY COLUMN `old_label` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '扫描的实物旧标签或箱标', + MODIFY COLUMN `new_label` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '扫描的换标后新标签', + MODIFY COLUMN `result` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '逐件结果:RELABELLED、SKIPPED或EXCEPTION', + MODIFY COLUMN `exception_code` varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '异常编码', + MODIFY COLUMN `reason` varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '跳过或异常原因', + MODIFY COLUMN `operator_key` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '操作账号标识', + MODIFY COLUMN `operator_name` varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '操作员名称快照', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_relabel_status_history` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `from_status` varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `to_status` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `operator_id` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `remark` varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `tenant_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'BAIYUE' COMMENT 'SaaS租户标识,当前单租户固定为BAIYUE', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_return_carton_count_session` + MODIFY COLUMN `operator_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '平台PDA操作员标识', + MODIFY COLUMN `warehouse_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '认领时绑定海外仓标识', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_return_carton_stock_receipt` + MODIFY COLUMN `customer_id` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户数据隔离键', + MODIFY COLUMN `receipt_no` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '原箱回仓实收业务号', + MODIFY COLUMN `source_outbound_no` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '原出库单业务号', + MODIFY COLUMN `carton_mark` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '原箱业务箱码快照', + MODIFY COLUMN `overseas_warehouse_id` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '实际回仓海外仓标识', + MODIFY COLUMN `target_pallet_no` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '实际目标托盘业务码', + MODIFY COLUMN `target_location_code` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '实际目标库位业务码', + MODIFY COLUMN `sku_code` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '原出库快照中的商品编码', + MODIFY COLUMN `product_name_zh` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '原出库快照中的中文品名', + MODIFY COLUMN `received_by` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '本次原箱实收确认操作人', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_return_inventory_balance` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户ID和隔离键', + MODIFY COLUMN `overseas_warehouse_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '海外仓ID', + MODIFY COLUMN `sku_code` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'SKU编码', + MODIFY COLUMN `product_name_zh` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '中文品名', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_return_inventory_carton` + MODIFY COLUMN `customer_id` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户ID和隔离键', + MODIFY COLUMN `overseas_warehouse_id` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '海外仓ID', + MODIFY COLUMN `source_batch_no` text COLLATE utf8mb4_0900_ai_ci COMMENT '实际消耗来源签收批次号,多个批次逗号分隔', + MODIFY COLUMN `source_return_no` text COLLATE utf8mb4_0900_ai_ci COMMENT '实际消耗来源退货单号,多个单号逗号分隔', + MODIFY COLUMN `source_unit_type` varchar(16) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '来源实物类型', + MODIFY COLUMN `source_unit_code` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '来源实物编码', + MODIFY COLUMN `pallet_code` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '签收时绑定的托盘码', + MODIFY COLUMN `carton_mark` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '退货箱号和箱唛编码', + MODIFY COLUMN `fba_carton_mark` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'FBA箱唛', + MODIFY COLUMN `destination_warehouse_code` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '目的仓编码', + MODIFY COLUMN `destination_warehouse_name` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '目的仓名称', + MODIFY COLUMN `relabel_no` varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '绑定换标单号', + MODIFY COLUMN `relabel_status` varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '换标状态', + MODIFY COLUMN `status` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'AVAILABLE可用或MERGED已合箱', + MODIFY COLUMN `new_carton_mark` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '鎹㈡爣鍚庢柊绠卞敍', + MODIFY COLUMN `location_code` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '退货箱当前库位', + MODIFY COLUMN `count_status` varchar(16) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'CONFIRMED' COMMENT '箱内清点状态 PENDING待清点 CONFIRMED已确认', + MODIFY COLUMN `counted_by` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '箱内清点确认操作人', + MODIFY COLUMN `label_status` varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'WAIT_LABEL寰呰创鏍� CONFIRMED宸叉壂鐮佺‘璁ゆ爣绛�', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_return_inventory_carton_item` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户ID和隔离键', + MODIFY COLUMN `sku_code` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'SKU编码', + MODIFY COLUMN `product_name_zh` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '中文品名', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_return_inventory_operation` + MODIFY COLUMN `operator_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'PDA操作人ID', + MODIFY COLUMN `idempotency_key` varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '操作幂等键', + MODIFY COLUMN `request_hash` char(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '请求内容SHA-256', + MODIFY COLUMN `operation_type` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '操作类型,当前MERGE', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_return_inventory_outbound_movement` + MODIFY COLUMN `customer_id` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '库存所属客户ID和隔离键', + MODIFY COLUMN `overseas_warehouse_id` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '实际出库海外仓ID', + MODIFY COLUMN `carton_mark` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '冻结的真实退货箱业务码', + MODIFY COLUMN `outbound_no` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '本次退货出库业务单号', + MODIFY COLUMN `sku_code` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '本次出库SKU编码快照', + MODIFY COLUMN `product_name_zh` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '中文品名快照', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_return_order` + MODIFY COLUMN `customer_id` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户ID和隔离键', + MODIFY COLUMN `return_no` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '退货订单号', + MODIFY COLUMN `overseas_warehouse_id` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '操作海外仓ID', + MODIFY COLUMN `warehouse_contact_name` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '中转仓联系人快照', + MODIFY COLUMN `warehouse_contact_phone` varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '中转仓联系电话快照', + MODIFY COLUMN `customer_reference_no` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '客户参考号', + MODIFY COLUMN `lastleg_order_no` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'linked lastleg order no', + MODIFY COLUMN `relabel_no` varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '绑定换标单号', + MODIFY COLUMN `relabel_status` varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '换标状态', + MODIFY COLUMN `status` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'PENDING_RECEIPT、PARTIALLY_RECEIVED或COMPLETED', + MODIFY COLUMN `remark` varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '客户备注', + MODIFY COLUMN `discrepancy_reason` varchar(500) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '手动完结差异原因', + MODIFY COLUMN `idempotency_key` varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '创建幂等键', + MODIFY COLUMN `request_hash` char(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '创建请求SHA-256', + MODIFY COLUMN `created_by` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '创建操作人ID', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_return_order_item` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户ID和隔离键', + MODIFY COLUMN `sku_code` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'SKU编码', + MODIFY COLUMN `product_name_zh` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '中文品名', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_return_packing_plan` + MODIFY COLUMN `plan_no` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `overseas_warehouse_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `mode` varchar(40) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `status` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `remark` varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `idempotency_key` varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `request_hash` varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '创建载荷摘要', + MODIFY COLUMN `completion_hash` varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '完成载荷摘要', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_return_packing_trace` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '原客户隔离键', + MODIFY COLUMN `source_carton_mark` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '来源箱唛 空值表示目标箱原有库存快照', + MODIFY COLUMN `target_carton_mark` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '目标箱唛', + MODIFY COLUMN `return_no` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '原始退货单号', + MODIFY COLUMN `sku_code` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '商品SKU编码', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_return_receipt` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户ID和隔离键', + MODIFY COLUMN `receipt_no` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '签收单号', + MODIFY COLUMN `receipt_batch_no` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '签收批次号', + MODIFY COLUMN `unit_type` varchar(16) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '签收实物类型 PALLET/BOX/PARCEL', + MODIFY COLUMN `unit_code` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '签收实物编码', + MODIFY COLUMN `measurement_source` varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '尺寸重量来源 PALLET_MEASURED托盘实测 UNIT_SUM子箱汇总', + MODIFY COLUMN `pallet_code` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '本次签收绑定的托盘码', + MODIFY COLUMN `carton_mark` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '签收生成或绑定的退货箱号', + MODIFY COLUMN `tracking_no` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '本次签收包裹的可选快递单号', + MODIFY COLUMN `bound_by` varchar(16) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '开始签收方式,RETURN_NO退货单号或SKU候选', + MODIFY COLUMN `status` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '签收状态,当前SIGNED', + MODIFY COLUMN `operator_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'PDA操作人ID', + MODIFY COLUMN `operator_name` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'PDA操作人名称', + MODIFY COLUMN `idempotency_key` varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'PDA确认幂等键', + MODIFY COLUMN `request_hash` char(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'PDA确认请求SHA-256', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_return_receipt_batch` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户ID和隔离键', + MODIFY COLUMN `batch_no` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '签收批次号', + MODIFY COLUMN `overseas_warehouse_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '操作海外仓ID', + MODIFY COLUMN `storage_billing_status` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '仓储计费状态,初始PENDING_PRICING', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_return_receipt_item` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户ID和隔离键', + MODIFY COLUMN `sku_code` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'SKU编码', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_return_sku_conversion` + MODIFY COLUMN `customer_id` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户隔离键', + MODIFY COLUMN `overseas_warehouse_id` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '操作海外仓ID', + MODIFY COLUMN `relabel_no` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '换标单号', + MODIFY COLUMN `round_key` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '作业轮次,退回重做使用新轮次', + MODIFY COLUMN `carton_mark` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '完工时仓库识别码', + MODIFY COLUMN `old_sku_code` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '换标前SKU', + MODIFY COLUMN `new_sku_code` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '换标后SKU', + MODIFY COLUMN `product_name_zh` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '中文品名快照', + MODIFY COLUMN `status` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'APPLIED待客户确认/CONFIRMED已确认/REVERSED退回冲回', + MODIFY COLUMN `ownership_json` longtext COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '本次转换件数的来源退货单快照,供精确冲回', + MODIFY COLUMN `storage_rent_shares_json` longtext COLLATE utf8mb4_0900_ai_ci COMMENT '原仓租批次迁移快照', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_return_stock_receipt` + MODIFY COLUMN `customer_id` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户数据归属', + MODIFY COLUMN `receipt_no` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '退回实收业务编号', + MODIFY COLUMN `source_outbound_no` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '原发运出库业务编号', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_security_event_inbox` + MODIFY COLUMN `event_id` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '跨区安全事件ID', + MODIFY COLUMN `event_type` varchar(96) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'IAM安全事件类型', + MODIFY COLUMN `payload_hash` char(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '规范化载荷SHA-256哈希', + MODIFY COLUMN `status` varchar(16) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '消费状态:RECEIVED或PROCESSED', + MODIFY COLUMN `result` varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '处理结果:APPLIED、STALE、DUPLICATE或DUPLICATE_VERSION', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_security_projection` + MODIFY COLUMN `aggregate_type` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '聚合类型:USER、TENANT或AUTHORIZATION', + MODIFY COLUMN `aggregate_id` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '用户或客户聚合ID', + MODIFY COLUMN `security_status` varchar(24) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '当前安全状态:ACTIVE或DISABLED', + MODIFY COLUMN `payload_hash` char(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '当前版本载荷哈希', + MODIFY COLUMN `last_event_id` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '最后应用的事件ID', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_storage_rent_inbound` + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_storage_rent_lot` + MODIFY COLUMN `source_key` varchar(80) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '实收来源幂等键', + MODIFY COLUMN `customer_id` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户标识', + MODIFY COLUMN `customer_name` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '客户名称', + MODIFY COLUMN `warehouse_id` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '海外仓标识', + MODIFY COLUMN `warehouse_name` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '海外仓名称', + MODIFY COLUMN `business_type` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '原入库业务类型', + MODIFY COLUMN `business_no` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '原入库业务单号', + MODIFY COLUMN `inbound_code` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '入仓码', + MODIFY COLUMN `batch_no` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '原实收批次', + MODIFY COLUMN `sku_code` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '商品编码', + MODIFY COLUMN `rule_code` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '仓租规则', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_storage_rent_preparation` + MODIFY COLUMN `warehouse_id` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '作业海外仓', + MODIFY COLUMN `customer_id` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '费用客户', + MODIFY COLUMN `mode` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '扫码类型', + MODIFY COLUMN `execution_no` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '实际作业单号', + MODIFY COLUMN `request_key` varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '原扫码幂等键', + MODIFY COLUMN `request_hash` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '原扫码内容哈希', + MODIFY COLUMN `business_type` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '本次应收业务类型', + MODIFY COLUMN `business_no` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '本次应收业务单号', + MODIFY COLUMN `status` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '资金与扫码状态', + MODIFY COLUMN `exchange_rate_month` varchar(7) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '汇率月份', + MODIFY COLUMN `freeze_event_id` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '冻结指令事件', + MODIFY COLUMN `settle_event_id` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '扣款指令事件', + MODIFY COLUMN `release_event_id` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '解冻指令事件', + MODIFY COLUMN `release_status` varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '解冻回执状态,失败后由恢复任务重发', + MODIFY COLUMN `hold_id` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '财务冻结主键', + MODIFY COLUMN `ledger_id` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '财务扣款流水', + MODIFY COLUMN `error_message` varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '资金处理结果', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_storage_rent_share` + MODIFY COLUMN `batch_no` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '普通库存当前批次,换标搬移保持原计租批次引用', + MODIFY COLUMN `customer_id` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户标识', + MODIFY COLUMN `warehouse_id` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '海外仓标识', + MODIFY COLUMN `carton_mark` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '当前箱唛', + MODIFY COLUMN `sku_code` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '商品编码', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_us_truck_order` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `order_no` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `transport_type` varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `origin` varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `origin_address` varchar(500) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `destination` varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `destination_address` varchar(500) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `container_type` varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `currency` varchar(3) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'USD', + MODIFY COLUMN `contact_name` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '旧本土卡车字段,新单使用起终点联系人及时间', + MODIFY COLUMN `contact_info` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '旧本土卡车字段,新单使用起终点联系人及时间', + MODIFY COLUMN `status` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `remark` varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `receipt_idempotency_key` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `receipt_request_hash` char(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `idempotency_key` varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `request_hash` char(64) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `created_by` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `tenant_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'BAIYUE' COMMENT 'SaaS租户标识,当前单租户固定为BAIYUE', + MODIFY COLUMN `vehicle_type` varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '车辆类型:PALLET_15为15拖,PALLET_30为30拖;拼车未提供时为空', + MODIFY COLUMN `origin_contact_name` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '起运地联系人', + MODIFY COLUMN `origin_contact_phone` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '起运地联系电话', + MODIFY COLUMN `destination_contact_name` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '目的地联系人', + MODIFY COLUMN `destination_contact_phone` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '目的地联系电话', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_us_truck_order_operation` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户租户ID', + MODIFY COLUMN `operation_type` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '具名操作类型', + MODIFY COLUMN `idempotency_key` varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '操作幂等键', + MODIFY COLUMN `request_hash` char(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '操作请求内容哈希', + MODIFY COLUMN `created_by` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '操作人ID', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_us_truck_order_pallet` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `pallet_no` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `source` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `status` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `tenant_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'BAIYUE' COMMENT 'SaaS租户标识,当前单租户固定为BAIYUE', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_us_truck_order_status_history` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `operation_type` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'UPDATE' COMMENT '具名操作类型', + MODIFY COLUMN `from_status` varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `to_status` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `operator_id` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `idempotency_key` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '操作幂等键', + MODIFY COLUMN `remark` varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `tenant_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'BAIYUE' COMMENT 'SaaS租户标识,当前单租户固定为BAIYUE', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_vehicle` + MODIFY COLUMN `plate_number` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '车牌号', + MODIFY COLUMN `model` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '车型', + MODIFY COLUMN `driver_or_dispatch_company` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '司机姓名或外派单位', + MODIFY COLUMN `phone` varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '司机或外派单位联系电话', + MODIFY COLUMN `region` varchar(16) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '区域:DOMESTIC/OVERSEAS', + MODIFY COLUMN `ownership` varchar(16) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '车辆归属:OWNED/DISPATCHED', + MODIFY COLUMN `status` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '状态:IN_SERVICE/DISPATCHED/MAINTENANCE/INACTIVE/RETURNED', + MODIFY COLUMN `remark` varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '备注', + MODIFY COLUMN `created_by` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '创建人', + MODIFY COLUMN `updated_by` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '更新人', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_wallet_event_inbox` + MODIFY COLUMN `event_id` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '国内Finance资金结果事件ID', + MODIFY COLUMN `command_event_id` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '原海外资金指令事件ID', + MODIFY COLUMN `payload_hash` varchar(71) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '规范化载荷SHA-256,包含sha256:前缀', + MODIFY COLUMN `status` varchar(16) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'RECEIVED或PROCESSED', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_warehouse_transfer_carton` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户租户ID', + MODIFY COLUMN `carton_mark` varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '系统生成的一箱一码箱唛', + MODIFY COLUMN `customer_carton_mark` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '客户原箱唛,用于换标现场绑定', + MODIFY COLUMN `status` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'EXPECTED' COMMENT '箱状态:EXPECTED待实收', + MODIFY COLUMN `count_idempotency_key` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '箱内清点幂等键', + MODIFY COLUMN `count_request_hash` varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '箱内清点请求摘要', + MODIFY COLUMN `counted_by` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '箱内清点操作人', + MODIFY COLUMN `discrepancy_reason` varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '实收与预报箱数差异原因', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_warehouse_transfer_inventory_allocation` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `inbound_code` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `pallet_no` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `status` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'RESERVED', + MODIFY COLUMN `active_position_key` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `tenant_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'BAIYUE' COMMENT 'SaaS租户标识,当前单租户固定为BAIYUE', + MODIFY COLUMN `sku_code` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '本次来源分配的实际SKU快照', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_warehouse_transfer_item` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `sku_code` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `product_name_zh` varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `shipping_mark` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `tenant_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'BAIYUE' COMMENT 'SaaS租户标识,当前单租户固定为BAIYUE', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_warehouse_transfer_measurement_photo` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户租户ID', + MODIFY COLUMN `file_name` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '文件名快照', + MODIFY COLUMN `content_type` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '文件媒体类型快照', + MODIFY COLUMN `uploaded_by` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'PDA上传操作人', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_warehouse_transfer_order` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `transfer_no` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `inbound_code` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `lastleg_order_no` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `operating_warehouse_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `fba_warehouse_code` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '可选FBA目的仓编码', + MODIFY COLUMN `delivery_address` varchar(1000) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '提货地址或中转仓收货地址', + MODIFY COLUMN `pickup_contact_name` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '提货联系人', + MODIFY COLUMN `pickup_contact_phone` varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '提货联系电话', + MODIFY COLUMN `delivery_method` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '入仓方式', + MODIFY COLUMN `fba_shipment_no` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '可选FBA货件编号', + MODIFY COLUMN `tracking_no` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `fba_box_nos` varchar(2000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `remark` varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `relabel_no` varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `status` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'PENDING_REVIEW', + MODIFY COLUMN `review_decision` varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '审核决定', + MODIFY COLUMN `review_remark` varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '审核备注或驳回原因', + MODIFY COLUMN `reviewed_by` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '审核人ID', + MODIFY COLUMN `allocation_idempotency_key` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '库存分配幂等键', + MODIFY COLUMN `allocation_request_hash` char(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '库存分配请求哈希', + MODIFY COLUMN `outbound_idempotency_key` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '出库幂等键', + MODIFY COLUMN `outbound_request_hash` char(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '出库请求哈希', + MODIFY COLUMN `idempotency_key` varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `request_hash` char(64) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `created_by` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `tenant_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'BAIYUE' COMMENT 'SaaS租户标识,当前单租户固定为BAIYUE', + MODIFY COLUMN `relabel_contact_name` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '换标联系人', + MODIFY COLUMN `relabel_contact_phone` varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '换标联系电话', + MODIFY COLUMN `relabel_delivery_method` varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '换标后派送方式', + MODIFY COLUMN `palletize_idempotency_key` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'PDA打托幂等键', + MODIFY COLUMN `palletize_request_hash` char(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'PDA打托请求哈希', + MODIFY COLUMN `confirmation_idempotency_key` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '客户测量确认幂等键', + MODIFY COLUMN `confirmation_request_hash` char(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '客户测量确认请求哈希', + MODIFY COLUMN `destination_type` varchar(16) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '目的类型:FBA或PRIVATE', + MODIFY COLUMN `destination_warehouse_id` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '目的仓ID快照', + MODIFY COLUMN `destination_warehouse_code` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '目的仓编码快照', + MODIFY COLUMN `destination_warehouse_name` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '目的仓名称快照', + MODIFY COLUMN `private_destination_address` varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '私人仓完整地址', + MODIFY COLUMN `private_destination_postal_code` varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '私人仓邮编', + MODIFY COLUMN `private_destination_contact_name` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '私人仓联系人', + MODIFY COLUMN `private_destination_contact_phone` varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '私人仓联系电话', + MODIFY COLUMN `destination_binding_idempotency_key` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '目的地绑定幂等键', + MODIFY COLUMN `destination_binding_request_hash` char(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '目的地绑定请求哈希', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_warehouse_transfer_pallet` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户租户ID', + MODIFY COLUMN `pallet_no` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '鐢熸垚鍚庣殑鐪熷疄鎵樼洏鐮', + MODIFY COLUMN `warehouse_location_code` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '真实库位码', + MODIFY COLUMN `status` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '打托状态', + MODIFY COLUMN `draft_idempotency_key` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '鑽夌?淇濆瓨骞傜瓑閿', + MODIFY COLUMN `draft_request_hash` char(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '鑽夌?璇锋眰鎽樿?', + MODIFY COLUMN `generate_idempotency_key` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '鏂版墭鐩樼敓鎴愬箓绛夐敭', + MODIFY COLUMN `bind_idempotency_key` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '鎵?爜缁戝畾骞傜瓑閿', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_warehouse_transfer_pallet_item` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户租户ID', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_warehouse_transfer_receipt` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `idempotency_key` varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `request_hash` char(64) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `inventory_operation_key` varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `created_by` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + MODIFY COLUMN `tenant_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'BAIYUE' COMMENT 'SaaS租户标识,当前单租户固定为BAIYUE', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_warehouse_transfer_receipt_command` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户标识', + MODIFY COLUMN `idempotency_key` varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '原始收货幂等键', + MODIFY COLUMN `request_hash` char(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '完整原始收货请求摘要', + MODIFY COLUMN `target_hash` char(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '共享目标资源绑定摘要', + MODIFY COLUMN `reservation_key` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '同转运同目标托盘共享资源占用键', + MODIFY COLUMN `status` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '命令状态PREPARED准备完成RESERVED资源占用COMPLETED收货完成', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_warehouse_transfer_relabel_binding` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户租户ID', + MODIFY COLUMN `operation_type` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '换标类型', + MODIFY COLUMN `customer_carton_mark` varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户原箱唛', + MODIFY COLUMN `original_sku` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '旧SKU', + MODIFY COLUMN `new_sku` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '新SKU', + MODIFY COLUMN `fba_carton_mark` varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '新FBA箱唛', + MODIFY COLUMN `destination_warehouse_code` varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'FBA目的仓', + MODIFY COLUMN `remark` varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '备注', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_lastleg`.`ll_warehouse_transfer_source_order` + MODIFY COLUMN `customer_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `source_lastleg_order_no` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `inbound_code` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL, + MODIFY COLUMN `tenant_id` varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'BAIYUE' COMMENT 'SaaS租户标识,当前单租户固定为BAIYUE', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER DATABASE `baiyue_order` CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +ALTER TABLE `baiyue_order`.`ord_document_sequence` + MODIFY COLUMN `sequence_key` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '业务前缀与北京时间日期组成的全局序列键', + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; diff --git a/deploy/sql/README.md b/deploy/sql/README.md index be0d98a27..ed7145ad7 100644 --- a/deploy/sql/README.md +++ b/deploy/sql/README.md @@ -1,11 +1,43 @@ # 单测试环境数据库空库初始化 +`20261104-unify-utf8mb4-0900-ai-ci.sql` 是本机 MySQL 8.4.11 五个业务库的排序规则汇总脚本,覆盖 `baiyue_event_bridge`、`baiyue_event_bridge_overseas`、`baiyue_firstleg`、`baiyue_lastleg`、`baiyue_order`。脚本已在本机执行并核验 209 张表、1,644 个文本字段;其他环境执行前必须核对实际字段定义和唯一索引冲突,不能默认部署。脚本只收敛旧的 Unicode 不区分大小写排序规则,保留有意使用的 `utf8mb4_bin`。 + +退货仓租改为原托整托计费、余一箱仍计一托,现有阶梯不变。已有 Lastleg 库先执行 [整托计费结构增量](return-storage-rent-whole-pallet.sql) 增加 `ll_storage_rent_lot.charged_days`,再发布匹配的 Lastleg;源基线和海外空库基线已同步。实际出库事务推进已生成费用的截止库龄日,同托同计租日不重复收费。切换期间暂停相关出库、取消未消费预占,并逐单核对旧比例费用;脚本不修改既有费用、库存或资金。本次仅准备代码及脚本,未执行环境 SQL 或部署。 + +散板收货闭环:`migrations/overseas/baiyue_lastleg/V37__looseboard_receiving_workflow.sql` 为已有 Lastleg 库增加入仓方式、收货仓、联系人、预计到仓、物流号及转运关联字段;新库使用同步后的海外统一基线。IAM 使用 `migrations/cn/baiyue_iam/V7__looseboard_demand_cargo_permission.sql` 补齐“散板需求”菜单及“加入散板货物”按钮,并为既有平台管理员补取货排车创建权限;权限源同步于 `iam/src/main/resources/db/fleet-capacity-offer-cargo-permission.sql` 和国内统一基线。两项已在本机应用并通过两场景HTTP验收,未据此认定TEST或生产已部署;已有库不可重放空库基线,V37执行前须核对九个新增字段尚不存在。 + +`migrations/overseas/baiyue_lastleg/V35__widen_finance_fee_command_action.sql` 修复海外人工费用资金重试报 S0500:仅将 `baiyue_lastleg.ll_finance_fee_command.action` 扩为 `VARCHAR(32)`,容纳 20 字符的 `MANUAL_FUNDING_RETRY`,保留中文备注、非空及原幂等唯一键。Lastleg 源基线和海外空库基线同步。已有环境先核对目标库及列定义,经授权执行 V35、回查列长度和记录总数,再通过原费用重试入口验证;可重复执行,不修改费用、审核、余额或资金记录,不重放空库基线。DDL 隐式提交,遇错停止;本次仅准备修复文件,尚未执行测试环境 SQL 或部署,文件存在不表示已经执行。 + +`overseas-fee-menu-finance-parent-repair.sql` 为已有 `baiyue_iam` 的定向权限修复:将菜单 `platform:overseas-fee:list` 改名为“海外费用明细”,父菜单设为 `platform:finance`(财务管理),保留原路由和 PLATFORM_ADMIN 授权;可重复执行,目标菜单缺失或权限身份冲突时停止。 + +`overseas-procurement-test-data-reset.sql` 为用户已确认可丢弃采购造数后的测试专用重置脚本:仅清空六张当前采购专属表,删除二十一张旧采购表;不会修改共用费用、余额、文件对象、库存或其他业务表,也不处理共用表的历史采购关联记录或退款。先停止采购写入并更新分离后的后端,完整执行文件、遇错停止,禁止 `--force`;DELETE 使用事务,后续 DROP 为隐式提交,不能整体回滚。无需存储过程、DELIMITER 或确认变量;成功返回 `procurement_test_reset_complete`,两个数量均为 0,成功后可重复执行。2026-10-27 已在本地隔离 MySQL 8.0.40 使用真实 27 表定义、三张 APPROVED 单及收货/出库/旧费用记录验证两次执行成功,共用表对照数据不变;未连接用户目标数据库,不表示部署完成。 + +`finance-receivable-active-uniqueness-repair.sql` 为 TEST 应收作废后重新建单的定向约束修复,仅以一条原子 ALTER 将 `baiyue_finance.fin_settlement` 的全历史唯一键替换为有效应收唯一键;作废历史仍保留原对账单关联,同一客户、同一对账单最多一张未作废应收。须先具备 Finance 重开校验版本 `fe89b8a2b875`,再单独授权执行 SQL;不修改业务金额、不清理或回填数据、不重放空库基线。执行前后核对表结构与记录总数,遇错停止,结果不明确先回查;脚本存在不表示已执行。 + +`finance-reconciliation-line-uniqueness-repair.sql` 修正 TEST 作废客户对账单后原费用无法重新生成对账单的问题:以一条原子 ALTER 将 `baiyue_finance.fin_bill_line` 的费用版本全历史唯一键改为单内唯一键,并保留费用来源普通索引。作废明细和金额快照完整保留;有效费用占用继续由 Finance 生成时的行锁、AVAILABLE → LOCKED 状态校验及作废单重开校验保证。模块基线与统一空库基线同步。先核对上述服务校验及目标表结构,经单独授权执行,再核对结构和记录总数;遇错停止,结果不明确先回查,不盲目重放,不更新业务数据或重放空库基线。脚本存在不表示已执行。 + +`overseas-procurement-workflow.sql` 新增简化采购的分批收货、分批出库与幂等三表,目标 `baiyue_lastleg`。新采购审核后直接分批收货,客户确认后创建出库,签收才扣采购库存;配套发布 Lastleg、Admin 和 Client。源基线与海外空库基线同步,不回填旧采购事实;结构脚本尚未执行。 + +`overseas-procurement-table-cleanup-review.sql` 为海外采购 27 表的只读核查,元数据行数为估算,另输出可手工执行的精确 COUNT。2026-10-27 已解除 21 张旧采购专属表的生产代码依赖,当前基线只保留六表。`overseas-procurement-legacy-tables-retire.sql` 为单独审阅执行的 MySQL 8.0+ 精确删表清单:仅 `baiyue_lastleg` 的 21 张旧采购表,不修改共用财务、资金、文件或仓储表。须先备份、停止旧版本写入、部署分离版本并验证,在维护窗口同连接设置脚本指定发布确认变量;六表缺失、旧状态采购或旧资金记录存在时拒绝。前置检查在业务 DROP 前完成,不关闭外键校验,使用一条 DROP TABLE IF EXISTS,可在完整成功后重跑;DDL 隐式提交,失败立即停止,禁止 --force。凭据及实例由执行者选择,不内置或自动连接。本轮未执行数据库或部署;[范围、清单及顺序](../../lastleg-service/docs/procurement-table-cleanup.md)。V14—V21 旧采购迁移仅供追溯,退役后不得重放。 + +`overseas-fees-storage-rent-structure-add.sql` 为指定 TEST 海外费用/仓租的定向结构补齐,只增加缺失的 `ll_storage_rent_inbound` 和 `ll_finance_fee_source.reviewer_name`。全部前置检查在首个业务 DDL 前完成;已有结构匹配时跳过,不匹配则停止,不覆盖或回填。随后按单独授权执行已有 `overseas-storage-rent-zero-fee-repair.sql`,补齐自动免费仓租的金额与汇率约束。使用独立连接、遇错停止,不重放空库初始化;DDL 隐式提交,失败先回查,不删除已完成项。两步范围与发布清单见[海外费用数据库定义汇总](20261022-海外费用仓租数据库变更汇总.md)及[发布配套](../../lastleg-service/docs/overseas-fees-storage-rent-release.md#发布配套)。文件准备不代表已执行或部署。 + 车辆与司机档案照片复用:先执行 `lastleg-fleet-resource-photos.sql` 新增照片版本表,再配套发布 File、Lastleg 和 Admin。车辆详情/司机详情维护照片,排车保存时复制缺失照片为当趟证据;已有照片不被后续档案替换覆盖。驾驶证统一接受 PDF/JPG/PNG。源基线与海外空库基线已同步;本次未执行环境 SQL 或部署。 放舱后预计到港窄维护需要配套更新 `trg_ocean_booking_before_update`:仅从冻结比较中移除 `estimated_arrival_at`,其余运输、预报、关系方及柜型冻结不变。定义已同步 `firstleg/src/main/resources/db/phase3-ocean-booking-schema.sql` 与国内统一基线;已有 TEST 需另行审阅并授权替换该触发器,不重放空库文件,不回填数据。本轮只准备定义,未执行 SQL 或部署。 货柜关联已放舱订舱时不再限制 ISO 柜号格式,Firstleg 模块基线与国内统一空库基线已移除 `chk_container_booking_iso6346`,保留同订舱柜号唯一键。既有 TEST 发布前需核对并单独授权移除该约束:`ALTER TABLE baiyue_firstleg.fl_container_booking DROP CHECK chk_container_booking_iso6346;`。配套发布 Firstleg 与 Admin;不重放空库初始化、不修改业务数据。本次仅准备源码与 SQL 定义,未执行环境 SQL 或部署。 +船节点日志在 `fl_carrier_schedule_node_fact.operator_id/operator_name` 保存操作时的账号与用户名快照;人工补录和确认出仓写入,船司自动采集为空。两列定义同步于 Firstleg 船节点模块基线与国内统一空库基线,更新结构后配套发布 Firstleg 与 Admin。既有 TEST 的字段变更须单独授权执行,不重放空库初始化,不回填历史节点。 + +`overseas-storage-rent-zero-fee-repair.sql` 为免费仓租的定向结构修复,固定 `baiyue_lastleg.ll_finance_fee_source`:先检查列类型、既有金额约束及存量行,再以单次原子 ALTER 仅放行自动应收仓租的原币及结算双零金额和空汇率,其余费用仍须正额及有效汇率。不更新费用、余额或库存;禁止重放空库初始化。源码基线与海外统一空库基线同步。脚本待审阅执行,文件存在不代表已执行。 + +`overseas-fee-remark-add.sql` 为海外人工费用说明的独立结构增量,固定目标 `baiyue_lastleg.ll_finance_fee_source`,仅在缺失时增加允许为空的 `fee_remark VARCHAR(500)`,保留旧费用与审核记录,不回填、不扣款、不改菜单。Lastleg 源基线与海外统一空库基线已同步;已有开发库须逐项授权执行本脚本后才能加载配套 Lastleg 版本。文件准备不表示已执行。 + +`finance-overseas-fee-hash-width-repair.sql` 用于明确授权的本机开发环境海外费用推送修复,固定目标 `baiyue_finance`。先核对 `fin_inbox_event.payload_hash`、`fin_fee_source.source_hash` 两列;仅将不足71位的长度扩至71位,兼容 `sha256:` 前缀,保留空值约束和字符集排序规则,不更新业务数据。缺列、异常类型或小于64位时停止;已有足够长度则跳过。使用独立连接、遇错停止,执行后回查结构,再通过原费用事件重试验证,禁止重新生成费用或扣款。脚本包含执行后移除的专用校验过程;创建或执行失败须停止,不扩大修复范围。文件准备不表示已执行。 + +`warehouse-supplier-query-structure-repair.sql` 用于明确授权的本机开发环境供应商查询结构修复,固定目标 `baiyue_warehouse`:仅在缺失时新增 `wh_supplier.trucking_scope`,并按当前模块基线创建缺失的 `wh_supplier_carrier` 空表。结构存在则跳过,不更新供应商类别、费用或资金,不推断历史拖车的国内/海外归属。使用独立连接、遇错停止,执行后回查结构并通过正式供应商列表 API 验证;不需要重启服务。文件准备不表示已获授权或已执行。 + 仓储转运到仓取货、本土卡车接入现有排车的**可重复执行汇总SQL**为 `lastleg-dispatch-transport-tasks.sql`,文件内固定 `USE baiyue_lastleg`,对已存在字段、表安全跳过。新增 `ll_dispatch_order.task_type` 字段、`ll_dispatch_truck_task`、`ll_dispatch_transport_create`、`ll_dispatch_transport_command` 三张表,并确保既有正式排车单号日流水表存在;不回填或删除业务数据。前置是目标库已具备现有整车排车主表、FleetTrip、取货任务及本土卡车原单结构。按序先执行此 SQL,再配套发布 Lastleg 与 Admin;取消释放有效原单关联、完成保留关联。`migrations/overseas/baiyue_lastleg/V32__dispatch_transport_tasks.sql` 是迁移系统中的新增结构记录。本次仅在已授权本机执行并验收,不代表远端已部署。 排车单号使用`PCyyyyMMdd0001`,日期取`Asia/Shanghai`,全平台跨客户/仓库共用独立日流水。`ll_dispatch_daily_sequence`同步Lastleg源基线与海外空库基线;已有结构的定向新增入口为`lastleg-dispatch-daily-sequence.sql`,只建序列表,不改已有业务号、不回填历史、不重放空库初始化。源码和DDL须配套发布;文件存在不表示已执行或部署。流水在独立短事务分配,回滚或取消不复用,四位范围每日0001至9999,准确单量以主单统计为准。 @@ -22,7 +54,7 @@ 本土散板简化下单的当前结构同步于 Lastleg 源基线及海外统一空库基线:移除散板柜型与旧要求日期,增加商品名称、申报预计托数及当地取货/送达时间,移除运输方式,增加起运地及目的地各自的联系人和联系电话;四项联系信息、取货及送货时间均必填;打盘与运输安排允许未知。预计托数不代表实收。字段语义唯一见工作区 `docs/海外仓业务/README.md`,不回填测试存量,不提供自动重置或启动时 SQL;需与匹配 Lastleg、Admin 和 Client 一起发布。本说明不表示 SQL 已执行。 -`migrations/overseas/baiyue_lastleg/V23__align_return_movement_collation.sql` 将退货进销存 UNION 涉及的八张表、22 个文本字段及表默认规则显式统一为 `utf8mb4_unicode_ci`,同步 Lastleg 两份源基线与海外统一空库基线。执行前核对字段长度、默认值、可空性及目标规则下唯一索引无冲突;只修改规则,不回填或删除数据,不重放空库基线。DDL 非事务,失败时停止并复查实际已完成项;可按最终定义重跑。脚本存在不表示测试库已执行,执行后用原始计数/分页查询验证,无需发布查询转换代码。 +`migrations/overseas/baiyue_lastleg/V23__align_return_movement_collation.sql` 将退货进销存 UNION 涉及的八张表、22 个文本字段及表默认规则显式统一为 `utf8mb4_0900_ai_ci`,同步 Lastleg 两份源基线与海外统一空库基线。执行前核对字段长度、默认值、可空性及目标规则下唯一索引无冲突;只修改规则,不回填或删除数据,不重放空库基线。DDL 非事务,失败时停止并复查实际已完成项;可按最终定义重跑。脚本存在不表示测试库已执行,执行后用原始计数/分页查询验证,无需发布查询转换代码。 `migrations/overseas/baiyue_lastleg/V22__selfop_warehouse_daily_sales.sql` 新增自营仓库每日汇总表及销量版本 `warehouse_daily_ready` 标记。与匹配 Lastleg 一起发布;汇总随 SKU 日事实批次原子构建,完整性验证后方可发布。已有未构建汇总的销量版本须重新构建发布,不回填旧版本、不回退旧查询;空版本同样必须有就绪证据。源基线与海外空库基线同步。 @@ -42,11 +74,11 @@ `reset-test-fba-appointments.sql` 仅用于明确授权的 TEST 预约模型重建:删除并按当前基线重建 `ll_inbound_fba_appointment`、`ll_inbound_fba_appointment_item`,并首次创建 `ll_fba_appointment_command` 保存同事务幂等结果与历史。执行任何DROP前必须确认命令表尚不存在;若已存在,先核对上次执行结果,本脚本不清空命令历史、不支持盲目重试。它会丢弃旧预约及关联明细,但不修改订单、入仓计划、库存和排车。调用者必须核对测试连接及两张表的具体重置授权;不得作为自动启动脚本或用于生产。主单区分计划日期/时段、系统单号、外部确认号和确认UTC/IANA时区;明细按稳定货源占用,不再依赖入仓计划明细。结构必须与匹配Lastleg和Admin一同发布,失败立即停止,不回填旧数据。权限从IAM种子核对;文件存在不表示已执行。 -`migrations/overseas/baiyue_lastleg/V13__widen_inbound_plan_creation_mode.sql` 仅将 `ll_inbound_plan.creation_mode` 从旧统一基线的 `VARCHAR(16)` 扩至模块基线已有的 `VARCHAR(32)`,容纳实际来货审核写入的 `WAREHOUSE_TRANSFER`(18字符)。保留 `utf8mb4_unicode_ci`、非空及 `AUTO` 默认值;不更新已有计划或状态。执行前核对目标库该列的长度、排序规则、可空性和默认值,已符合最终定义则核对后跳过;执行结果不确定时先只读复查,不盲目重试。统一空库基线同步最终定义,不重放模块历史初始化脚本。 +`migrations/overseas/baiyue_lastleg/V13__widen_inbound_plan_creation_mode.sql` 仅将 `ll_inbound_plan.creation_mode` 从旧统一基线的 `VARCHAR(16)` 扩至模块基线已有的 `VARCHAR(32)`,容纳实际来货审核写入的 `WAREHOUSE_TRANSFER`(18字符)。保留 `utf8mb4_0900_ai_ci`、非空及 `AUTO` 默认值;不更新已有计划或状态。执行前核对目标库该列的长度、排序规则、可空性和默认值,已符合最终定义则核对后跳过;执行结果不确定时先只读复查,不盲目重试。统一空库基线同步最终定义,不重放模块历史初始化脚本。 `migrations/overseas/baiyue_lastleg/V12__preserve_unknown_pool_order_fields.sql` 仅放开散板打盘需求及两类本土订单柜型三列为空、默认空,供未提供这些事实的拼车商业订单使用;普通订单输入校验仍由原服务端请求合同执行。对应源及统一空库基线同步最终定义,不回填已生成订单或把托盘数量解释成打盘服务需求。 -内部拼车需求池的当前结构在 Lastleg 源基线和海外统一空库基线同步维护,独立五项权限在 IAM 源种子和国内统一基线维护。已存在基础表的指定环境结构补齐使用 `migrations/overseas/baiyue_lastleg/V10__fleet_capacity_pool.sql` 与 `migrations/cn/baiyue_iam/V4__fleet_capacity_offer_permissions.sql`;执行前确认三张新表、六个新增字段均未存在,并单独核对目标环境。随后 `V11__align_capacity_pool_collation.sql` 将预订与商业桥的客户关联字段显式统一为 `utf8mb4_unicode_ci`,与原订单域一致,避免依赖MySQL实例默认排序规则导致JOIN失败。它们不回填、删除或重置业务数据,不能重放整个空库基线。候选文件存在不表示已经执行。 +内部拼车需求池的当前结构在 Lastleg 源基线和海外统一空库基线同步维护,独立五项权限在 IAM 源种子和国内统一基线维护。已存在基础表的指定环境结构补齐使用 `migrations/overseas/baiyue_lastleg/V10__fleet_capacity_pool.sql` 与 `migrations/cn/baiyue_iam/V4__fleet_capacity_offer_permissions.sql`;执行前确认三张新表、六个新增字段均未存在,并单独核对目标环境。随后 `V11__align_capacity_pool_collation.sql` 将预订与商业桥的客户关联字段显式统一为 `utf8mb4_0900_ai_ci`,与原订单域一致,避免依赖MySQL实例默认排序规则导致JOIN失败。它们不回填、删除或重置业务数据,不能重放整个空库基线。候选文件存在不表示已经执行。 平台自营领星统计十张 `ll_selfop_*` 表直接同步 Lastleg 源基线及海外统一空库基线;时间使用 UTC 存储及已确认北京时间业务日,数量使用 DECIMAL 保留 PCS 精度,维持独立发布版本。包含账号共享预算/冷却、验证根用途与硬预算、TEST BUSINESS 根会话持久预算、稳定任务号、阶段进度/心跳和旧版替代时间;任务的 `business_root_task_id` 标记共享根,只有根保存 `business_budget_json`,生产任务不使用该测试预算。库存 run 包含每日代表日期、选择规则及账号/仓库/日期唯一约束,代表库存与 profile 不适用销量副本淘汰。IAM 权限源为 `iam/src/main/resources/db/selfoperated-analytics-permissions.sql`,自营业务下经营总览、销量分析、库存分析、数据采集的菜单资源与独立读写权限同步国内统一基线,仅平台管理员默认授权。执行和部署状态只维护于工作区测试环境主契约;真实采集模式默认 OFF。 @@ -71,7 +103,7 @@ V13 收货覆盖由 `lastleg-service/src/main/resources/db/appointment-receiving | 区域 | 完整入口 | 服务 schema | 表数 | |---|---|---|---:| | 国内 | `头程数据库统一增量-MySQL8.sql` | `baiyue_auth`、`baiyue_iam`、`baiyue_order`、`baiyue_warehouse`、`baiyue_firstleg`、`baiyue_file`、`baiyue_audit`、`baiyue_finance` | 229 | -| 海外 | `海外区域统一增量-MySQL8.sql` | `baiyue_lastleg`、`baiyue_lastlag_warehouse` | 234 | +| 海外 | `海外区域统一增量-MySQL8.sql` | `baiyue_lastleg`、`baiyue_lastlag_warehouse` | 222 | - 所有服务位于同一测试环境;两份文件只分组组织服务 schema,不代表两个部署区域。`baiyue_lastlag_warehouse` 保留当前正式的 `lastlag` 拼写。 @@ -94,7 +126,7 @@ V13 收货覆盖由 `lastleg-service/src/main/resources/db/appointment-receiving 5. DDL 隐式提交,不可整体回滚。失败后停止并保留错误;经另行授权准备空库后从头执行,不针对半初始化库追加迁移或从失败行续跑。 6. Firstleg 测量项与拆分项互为外键:建空表期间暂设会话 `foreign_key_checks=0`,随后立即恢复 `1`,再插入种子。失败时关闭连接。两边外键完整保留在 CREATE TABLE 内。 -成功时只在最末输出一行 `initialization_status=PASS`,并带 `region=CN/OVERSEAS`、`initialized_tables=229/234`。必须同时确认客户端退出码 0;忽略错误后出现的 PASS 不算成功。 +成功时只在最末输出一行 `initialization_status=PASS`,并带 `region=CN/OVERSEAS`、`initialized_tables=229/222`。必须同时确认客户端退出码 0;忽略错误后出现的 PASS 不算成功。 ## 平台管理员密码 @@ -194,3 +226,12 @@ node --test scripts/admin-test-env-modules/initialization-baseline-contract.test ## 统一业务单号 编号规则见 `../../../docs/业务单据编号契约.md`。Order、Warehouse、Lastleg 空库基线新增各自的 `ord_document_sequence`、`wh_document_sequence`、`ll_document_sequence`;Firstleg 与 Finance 复用本库编号表。Lastleg 空库基线移除无生产调用的客户日序列、逻辑秒时钟及旧尾程日序列表,保留排车日序列。采购费用新增可空 `idempotency_key` 及订单/用途/请求键唯一约束,用于退款重放,业务号统一使用 PF。此处只准备基线,不执行 SQL、回填或删除测试存量。 + +海外费用与仓租首版统一维护在 `头程数据库统一增量-MySQL8.sql` 的 IAM 菜单/管理员授权种子及 `海外区域统一增量-MySQL8.sql` 的最终建表定义中。海外基线包含资金与仓租四表、拆柜成本可空费用引用、退货 SKU 换标仓租快照及审核人名称;采购旧费用与退款专属表已在简化流程退役,不再进入空库初始化。同步模块源基线、国内权限种子及海外空库 222 表,复用现有 `FEE_TYPE_077 / 仓储费`,不新增费用项目种子。发布须同时更新 Firstleg、Lastleg、Admin 与海外 PDA;规则、配置及资金验收见 [首版发布验收](../../lastleg-service/docs/overseas-fees-storage-rent-release.md)。仅整理文件,未执行 SQL 或部署。 + +本次重复的五份版本脚本和两份专项汇总 SQL 已移除,内容清单见 [20261022-海外费用仓租数据库变更汇总](20261022-海外费用仓租数据库变更汇总.md)。两份统一文件仍只用于空库初始化,不能向已有业务库重放;本次不再提供这项功能的独立升级脚本,已有库升级须另按实际结构和明确授权准备、审阅增量。其他独立修复补丁及历史迁移保持各自适用边界。 + +采购方向扩展:`migrations/overseas/baiyue_lastleg/V34__procurement_direction.sql` 先于匹配 Lastleg 版本执行,为采购单增加 `procurement_direction`;历史订单默认海外采购回国内,不修改既有实物和资金记录。国内采购通过既有国内仓目录选起运收货仓;源基线与统一空库基线同步。本地验证不表示测试库已执行或服务已发布。 + + +2026-10-29 客户采购驳回和下单预算冻结:源结构与海外空库入口同步,已有 Lastleg 库使用 `overseas-order-budget-and-procurement-rejection.sql` 增加两列和预算预占表,不向已有库重放空库入口。需要匹配的 Lastleg、Admin、Client;本轮仅交付脚本,未执行数据库变更。 diff --git a/deploy/sql/finance-overseas-fee-hash-width-repair.sql b/deploy/sql/finance-overseas-fee-hash-width-repair.sql new file mode 100644 index 000000000..a04ca476f --- /dev/null +++ b/deploy/sql/finance-overseas-fee-hash-width-repair.sql @@ -0,0 +1,75 @@ +-- 本机开发环境海外费用推送结构修复。固定目标:baiyue_finance。 +-- 仅将两个既有摘要字段不足 71 位的长度扩至 71;不改数据、费用、账单或余额。 +-- 保留原字段的空值约束、字符集及排序规则;缺表、缺列或异常字段类型时拒绝执行。 +-- 执行须单独授权。使用独立连接、遇错停止;不得以本文件代替完整数据库初始化。 +USE baiyue_finance; + +-- 执行前只读核对;sha256: 前缀 7 位加摘要 64 位,共 71 位。 +SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLUMN_TYPE, IS_NULLABLE, + CHARACTER_SET_NAME, COLLATION_NAME +FROM information_schema.COLUMNS +WHERE TABLE_SCHEMA = 'baiyue_finance' + AND ((TABLE_NAME = 'fin_inbox_event' AND COLUMN_NAME = 'payload_hash') + OR (TABLE_NAME = 'fin_fee_source' AND COLUMN_NAME = 'source_hash')); + +DELIMITER $$ +CREATE PROCEDURE finance_fee_hash_width_repair_20261023() +BEGIN + DECLARE checked_columns INT DEFAULT 0; + DECLARE column_index INT DEFAULT 0; + DECLARE target_table VARCHAR(64); + DECLARE target_column VARCHAR(64); + DECLARE current_length BIGINT; + DECLARE nullable_value VARCHAR(3); + DECLARE charset_value VARCHAR(64); + DECLARE collation_value VARCHAR(64); + DECLARE comment_value TEXT; + + IF DATABASE() <> 'baiyue_finance' THEN + SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '目标必须是 baiyue_finance'; + END IF; + SELECT COUNT(*) INTO checked_columns + FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = 'baiyue_finance' AND DATA_TYPE IN ('char', 'varchar') + AND CHARACTER_MAXIMUM_LENGTH >= 64 + AND COLUMN_DEFAULT IS NULL AND EXTRA = '' + AND ((TABLE_NAME = 'fin_inbox_event' AND COLUMN_NAME = 'payload_hash') + OR (TABLE_NAME = 'fin_fee_source' AND COLUMN_NAME = 'source_hash')); + IF checked_columns <> 2 THEN + SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '摘要字段缺失或类型异常,停止修复'; + END IF; + + WHILE column_index < 2 DO + SET target_table = IF(column_index = 0, 'fin_inbox_event', 'fin_fee_source'); + SET target_column = IF(column_index = 0, 'payload_hash', 'source_hash'); + SELECT CHARACTER_MAXIMUM_LENGTH, IS_NULLABLE, CHARACTER_SET_NAME, COLLATION_NAME, COLUMN_COMMENT + INTO current_length, nullable_value, charset_value, collation_value, comment_value + FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = 'baiyue_finance' AND TABLE_NAME = target_table + AND COLUMN_NAME = target_column; + IF current_length < 71 THEN + SET @finance_fee_hash_width_ddl = CONCAT( + 'ALTER TABLE baiyue_finance.`', target_table, '` MODIFY COLUMN `', target_column, + '` VARCHAR(71) CHARACTER SET ', charset_value, ' COLLATE ', collation_value, + IF(nullable_value = 'YES', ' NULL', ' NOT NULL'), + ' COMMENT ', QUOTE(comment_value)); + PREPARE finance_fee_hash_width_stmt FROM @finance_fee_hash_width_ddl; + EXECUTE finance_fee_hash_width_stmt; + DEALLOCATE PREPARE finance_fee_hash_width_stmt; + END IF; + SET column_index = column_index + 1; + END WHILE; +END$$ +DELIMITER ; + +CALL finance_fee_hash_width_repair_20261023(); +DROP PROCEDURE finance_fee_hash_width_repair_20261023; +SET @finance_fee_hash_width_ddl = NULL; + +-- 执行后两列长度均应至少为 71;结构已正确时不执行 ALTER。 +SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLUMN_TYPE, IS_NULLABLE, + CHARACTER_MAXIMUM_LENGTH >= 71 AS width_ok +FROM information_schema.COLUMNS +WHERE TABLE_SCHEMA = 'baiyue_finance' + AND ((TABLE_NAME = 'fin_inbox_event' AND COLUMN_NAME = 'payload_hash') + OR (TABLE_NAME = 'fin_fee_source' AND COLUMN_NAME = 'source_hash')); diff --git a/deploy/sql/finance-receivable-active-uniqueness-repair.sql b/deploy/sql/finance-receivable-active-uniqueness-repair.sql new file mode 100644 index 000000000..0395e5b97 --- /dev/null +++ b/deploy/sql/finance-receivable-active-uniqueness-repair.sql @@ -0,0 +1,11 @@ +-- 仅用于单独授权的 TEST:修正作废应收仍占用原对账单的唯一约束。 +-- 不更新、删除或回填业务记录;作废单的原对账单关联与修订历史保持不变。 +-- 一条原子 ALTER 同时替换约束;有效应收重复、原索引缺失或目标列已存在均应报错停止。 +-- 使用独立连接并开启遇错停止;若结果不明确,先核对结构,不盲目重放。 +-- 前置:已部署 Finance 结算重开校验(fe89b8a2b875),目标为 MySQL 8 / InnoDB。 +ALTER TABLE baiyue_finance.fin_settlement + ADD COLUMN active_receivable_statement_id BIGINT UNSIGNED + GENERATED ALWAYS AS (CASE WHEN direction='RECEIVABLE' AND status<>'VOIDED' THEN reconciliation_statement_id ELSE NULL END) + VIRTUAL COMMENT '有效应收占用的对账单;作废单不占用,历史关联仍保留', + DROP INDEX uk_fin_settlement_receivable_statement, + ADD UNIQUE KEY uk_fin_settlement_active_receivable (tenant_id, active_receivable_statement_id); diff --git a/deploy/sql/finance-reconciliation-line-uniqueness-repair.sql b/deploy/sql/finance-reconciliation-line-uniqueness-repair.sql new file mode 100644 index 000000000..73f6110ff --- /dev/null +++ b/deploy/sql/finance-reconciliation-line-uniqueness-repair.sql @@ -0,0 +1,10 @@ +-- 仅用于单独授权的 TEST:修正作废对账单历史明细阻止原费用重新生成对账单的问题。 +-- 固定目标 MySQL 8 / InnoDB;保留全部账单、明细、金额及修订,不更新、删除或回填业务记录。 +-- 单内同一费用版本保持唯一;跨单有效占用由 Finance 生成时的费用行锁和状态校验保证。 +-- 前置:Finance 已具备生成时 AVAILABLE -> LOCKED 校验,以及作废单重开时的来源重新占用校验。 +-- 执行前后核对 SHOW CREATE TABLE 与记录总数;本文件存在不表示已经执行。 +-- 使用独立连接并开启遇错停止;一条原子 ALTER 替换索引,结果不明确先回查,不盲目重放。 +ALTER TABLE baiyue_finance.fin_bill_line + DROP INDEX uk_fin_bill_line_source, + ADD UNIQUE KEY uk_fin_bill_line_bill_source (bill_id, source_type, source_id, source_version), + ADD KEY idx_fin_bill_line_source (source_type, source_id, source_version); diff --git a/deploy/sql/lastleg-dispatch-daily-sequence.sql b/deploy/sql/lastleg-dispatch-daily-sequence.sql index ca4740ee2..0d9a0ecc8 100644 --- a/deploy/sql/lastleg-dispatch-daily-sequence.sql +++ b/deploy/sql/lastleg-dispatch-daily-sequence.sql @@ -4,4 +4,4 @@ CREATE TABLE IF NOT EXISTS ll_dispatch_daily_sequence ( business_date DATE NOT NULL COMMENT '排车编号业务日期(Asia/Shanghai)', last_sequence INT NOT NULL COMMENT '当日已分配排车流水号,范围0001至9999', PRIMARY KEY(business_date) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='排车单号每日独立原子流水'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='排车单号每日独立原子流水'; diff --git a/deploy/sql/lastleg-dispatch-transport-tasks.sql b/deploy/sql/lastleg-dispatch-transport-tasks.sql index a36d454bc..d84812bc5 100644 --- a/deploy/sql/lastleg-dispatch-transport-tasks.sql +++ b/deploy/sql/lastleg-dispatch-transport-tasks.sql @@ -3,6 +3,16 @@ -- 前置:已完成 Lastleg 现有整车排车结构升级;如部署为缺少每日流水表的旧库,也创建正式排车流水表。 USE baiyue_lastleg; +-- 排车保存会同步异常快照;旧库必须先补齐该正式字段。 +SET @dispatch_exception_ddl = IF( + EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'll_dispatch_order' AND column_name = 'exceptions_json'), + 'SELECT 1', + 'ALTER TABLE ll_dispatch_order ADD COLUMN exceptions_json JSON NULL COMMENT ''派送异常登记记录及图片引用,不改变派送及库存状态'' AFTER review_basis_hash' +); +PREPARE dispatch_exception_stmt FROM @dispatch_exception_ddl; +EXECUTE dispatch_exception_stmt; +DEALLOCATE PREPARE dispatch_exception_stmt; + SET @dispatch_transport_column_ddl = IF( EXISTS ( SELECT 1 FROM information_schema.columns @@ -59,7 +69,7 @@ CREATE TABLE IF NOT EXISTS ll_dispatch_daily_sequence ( business_date DATE NOT NULL COMMENT '排车编号业务日期(Asia/Shanghai)', last_sequence INT NOT NULL COMMENT '当日已分配排车流水号,范围0001至9999', PRIMARY KEY (business_date) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='排车单号每日独立原子流水'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='排车单号每日独立原子流水'; CREATE TABLE IF NOT EXISTS ll_dispatch_truck_task ( dispatch_no VARCHAR(64) NOT NULL COMMENT '排车业务号', @@ -71,13 +81,13 @@ CREATE TABLE IF NOT EXISTS ll_dispatch_truck_task ( UNIQUE KEY uk_transport_trip (trip_no), UNIQUE KEY uk_transport_source (active_order), KEY idx_transport_order (customer_id, order_no) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='排车与本土卡车原单关联'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='排车与本土卡车原单关联'; CREATE TABLE IF NOT EXISTS ll_dispatch_transport_create ( trip_no VARCHAR(64) NOT NULL COMMENT '本次运输资源记录号', request_hash CHAR(64) NOT NULL COMMENT '首次安排请求摘要', PRIMARY KEY (trip_no) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='本土卡车排车创建请求账本'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='本土卡车排车创建请求账本'; CREATE TABLE IF NOT EXISTS ll_dispatch_transport_command ( dispatch_no VARCHAR(64) NOT NULL COMMENT '排车业务号', @@ -86,7 +96,7 @@ CREATE TABLE IF NOT EXISTS ll_dispatch_transport_command ( evidence_json JSON NULL COMMENT '已确认运输完成凭证标识列表', created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '业务命令提交时间', PRIMARY KEY (dispatch_no, request_key) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='排车运输执行命令与凭证账本'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='排车运输执行命令与凭证账本'; -- 当前承运占用查询还依赖散板来源标识(与V33一致);仅补结构,不创建散板业务记录。 SET @dispatch_transport_pool_ddl = IF( diff --git a/deploy/sql/migrations/cn/baiyue_firstleg/V4__add_sailing_calendar_schema.sql b/deploy/sql/migrations/cn/baiyue_firstleg/V4__add_sailing_calendar_schema.sql index 63cd59683..dfd8d0c87 100644 --- a/deploy/sql/migrations/cn/baiyue_firstleg/V4__add_sailing_calendar_schema.sql +++ b/deploy/sql/migrations/cn/baiyue_firstleg/V4__add_sailing_calendar_schema.sql @@ -27,7 +27,7 @@ CREATE TABLE IF NOT EXISTS fl_sailing_channel ( PRIMARY KEY (id), UNIQUE KEY uk_fl_sailing_channel_code (channel_code), KEY idx_fl_sailing_channel_status (status) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; CREATE TABLE IF NOT EXISTS fl_sailing_template ( id BIGINT NOT NULL, @@ -63,7 +63,7 @@ CREATE TABLE IF NOT EXISTS fl_sailing_template ( KEY idx_fl_sailing_template_filter (warehouse_id,channel_id,status,weekday), CONSTRAINT ck_fl_sailing_template_weekday CHECK (weekday BETWEEN 1 AND 7), CONSTRAINT ck_fl_sailing_template_cutoff_days CHECK (cutoff_days BETWEEN 0 AND 30) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; CREATE TABLE IF NOT EXISTS fl_sailing ( id BIGINT NOT NULL, @@ -100,7 +100,7 @@ CREATE TABLE IF NOT EXISTS fl_sailing ( KEY idx_fl_sailing_calendar (etd_at,warehouse_id,channel_id,status), KEY idx_fl_sailing_route (departure_port_code,destination_port_code), CONSTRAINT ck_fl_sailing_status CHECK (status IN ('DRAFT','OPEN','CLOSED','DEPARTED','CANCELLED','COMPLETED')) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; CREATE TABLE IF NOT EXISTS fl_sailing_history ( id BIGINT NOT NULL, @@ -114,7 +114,7 @@ CREATE TABLE IF NOT EXISTS fl_sailing_history ( created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), PRIMARY KEY (id), KEY idx_fl_sailing_history_sailing (sailing_id,created_at,id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; /* * 船期日历的事实契约。 diff --git a/deploy/sql/migrations/cn/baiyue_iam/V7__looseboard_demand_cargo_permission.sql b/deploy/sql/migrations/cn/baiyue_iam/V7__looseboard_demand_cargo_permission.sql new file mode 100644 index 000000000..9cebe5712 --- /dev/null +++ b/deploy/sql/migrations/cn/baiyue_iam/V7__looseboard_demand_cargo_permission.sql @@ -0,0 +1,13 @@ +-- 散板需求沿用既有菜单权限,补全当前名称和添加货物独立按钮。 +INSERT INTO iam_permission (id,tenant_id,app_code,permission_type,parent_id,permission_code,permission_name,route_path,http_method,sort_no,status,built_in,created_at,updated_at,deleted) +SELECT 5107410,0,'PLATFORM','MENU',0,'platform:fleet:capacity-offer:list','散板需求','/overseas-master-data/capacity-offers','GET',1040,'ACTIVE',1,CURRENT_TIMESTAMP(3),CURRENT_TIMESTAMP(3),0 +WHERE NOT EXISTS (SELECT 1 FROM iam_permission WHERE permission_code='platform:fleet:capacity-offer:list'); +UPDATE iam_permission SET permission_name='散板需求',status='ACTIVE',deleted=0,updated_at=CURRENT_TIMESTAMP(3) WHERE permission_code='platform:fleet:capacity-offer:list'; +INSERT INTO iam_permission (id,tenant_id,app_code,permission_type,parent_id,permission_code,permission_name,route_path,http_method,sort_no,status,built_in,created_at,updated_at,deleted) +SELECT 5107415,0,'PLATFORM','BUTTON',p.id,'platform:fleet:capacity-offer:cargo-add','加入散板货物',NULL,'POST',1045,'ACTIVE',1,CURRENT_TIMESTAMP(3),CURRENT_TIMESTAMP(3),0 FROM iam_permission p +WHERE p.permission_code='platform:fleet:capacity-offer:list' AND NOT EXISTS (SELECT 1 FROM iam_permission WHERE permission_code='platform:fleet:capacity-offer:cargo-add'); +INSERT INTO iam_role_permission (role_id,permission_id,tenant_id,created_at) +SELECT 4000001,p.id,0,CURRENT_TIMESTAMP(3) FROM iam_permission p WHERE p.permission_code IN ('platform:fleet:capacity-offer:list','platform:fleet:capacity-offer:cargo-add') AND NOT EXISTS (SELECT 1 FROM iam_role_permission rp WHERE rp.role_id=4000001 AND rp.permission_id=p.id); +-- 上门取货复用正式排车创建入口,仅补齐既有平台管理员角色的必要权限。 +INSERT INTO iam_role_permission (role_id,permission_id,tenant_id,created_at) +SELECT 4000001,p.id,0,CURRENT_TIMESTAMP(3) FROM iam_permission p WHERE p.permission_code='platform:overseas-warehouse:dispatch:create' AND p.status='ACTIVE' AND p.deleted=0 AND NOT EXISTS (SELECT 1 FROM iam_role_permission rp WHERE rp.role_id=4000001 AND rp.permission_id=p.id); diff --git a/deploy/sql/migrations/overseas/baiyue_lastleg/V11__align_capacity_pool_collation.sql b/deploy/sql/migrations/overseas/baiyue_lastleg/V11__align_capacity_pool_collation.sql index 477eb0c40..1a18fdb32 100644 --- a/deploy/sql/migrations/overseas/baiyue_lastleg/V11__align_capacity_pool_collation.sql +++ b/deploy/sql/migrations/overseas/baiyue_lastleg/V11__align_capacity_pool_collation.sql @@ -2,6 +2,6 @@ -- Schema: baiyue_lastleg -- Purpose: 拼车履约客户关联字段与现有订单域排序规则一致 -- Preconditions: 外接预订和商业履约桥已存在,客户字段为VARCHAR(128)且非空 --- Postconditions: 两个客户关联字段使用utf8mb4_unicode_ci;不修改客户标识或业务状态 -ALTER TABLE ll_fleet_external_booking MODIFY COLUMN customer_id VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '委托客户及付款归属'; -ALTER TABLE ll_fleet_pool_order_link MODIFY COLUMN customer_id VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '商业客户隔离范围'; +-- Postconditions: 两个客户关联字段使用utf8mb4_0900_ai_ci;不修改客户标识或业务状态 +ALTER TABLE ll_fleet_external_booking MODIFY COLUMN customer_id VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '委托客户及付款归属'; +ALTER TABLE ll_fleet_pool_order_link MODIFY COLUMN customer_id VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '商业客户隔离范围'; diff --git a/deploy/sql/migrations/overseas/baiyue_lastleg/V13__widen_inbound_plan_creation_mode.sql b/deploy/sql/migrations/overseas/baiyue_lastleg/V13__widen_inbound_plan_creation_mode.sql index 4fd9028ae..45842eb1f 100644 --- a/deploy/sql/migrations/overseas/baiyue_lastleg/V13__widen_inbound_plan_creation_mode.sql +++ b/deploy/sql/migrations/overseas/baiyue_lastleg/V13__widen_inbound_plan_creation_mode.sql @@ -1,8 +1,8 @@ -- Region: OVERSEAS -- Schema: baiyue_lastleg -- Purpose: 仓储转运来货审核生成入仓计划时允许完整WAREHOUSE_TRANSFER来源值 --- Preconditions: ll_inbound_plan.creation_mode现为VARCHAR(16),utf8mb4_unicode_ci、NOT NULL、DEFAULT AUTO +-- Preconditions: ll_inbound_plan.creation_mode现为VARCHAR(16),utf8mb4_0900_ai_ci、NOT NULL、DEFAULT AUTO -- Postconditions: 仅将本列扩至VARCHAR(32);保留排序规则、非空、默认值和已有业务数据 ALTER TABLE ll_inbound_plan - MODIFY COLUMN creation_mode VARCHAR(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci + MODIFY COLUMN creation_mode VARCHAR(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'AUTO' COMMENT '计划生成方式:AUTO柜级自动、MANUAL单票人工、WAREHOUSE_TRANSFER仓储转运'; diff --git a/deploy/sql/migrations/overseas/baiyue_lastleg/V15__procurement_fee_versions.sql b/deploy/sql/migrations/overseas/baiyue_lastleg/V15__procurement_fee_versions.sql index 1fcc56763..4ea967a29 100644 --- a/deploy/sql/migrations/overseas/baiyue_lastleg/V15__procurement_fee_versions.sql +++ b/deploy/sql/migrations/overseas/baiyue_lastleg/V15__procurement_fee_versions.sql @@ -45,4 +45,4 @@ CREATE TABLE ll_procurement_wallet_attempt ( UNIQUE KEY uk_procurement_attempt_number(fee_id,attempt_no), KEY idx_procurement_attempt_order(customer_id,order_id,status), CONSTRAINT chk_procurement_attempt_status CHECK(status IN('PENDING','SUCCEEDED','FAILED')) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='采购费用资金命令尝试与不可变结果'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='采购费用资金命令尝试与不可变结果'; diff --git a/deploy/sql/migrations/overseas/baiyue_lastleg/V16__procurement_purchase_facts.sql b/deploy/sql/migrations/overseas/baiyue_lastleg/V16__procurement_purchase_facts.sql index 61e7bf813..784b7216f 100644 --- a/deploy/sql/migrations/overseas/baiyue_lastleg/V16__procurement_purchase_facts.sql +++ b/deploy/sql/migrations/overseas/baiyue_lastleg/V16__procurement_purchase_facts.sql @@ -31,7 +31,7 @@ CREATE TABLE ll_procurement_flow ( PRIMARY KEY(order_id), KEY idx_procurement_flow_customer(customer_id,order_id), CONSTRAINT chk_procurement_purchase_quantity CHECK(purchased_quantity IS NULL OR purchased_quantity>0), CONSTRAINT chk_procurement_purchase_amount CHECK(purchase_amount IS NULL OR purchase_amount>=0) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='采购单一批次履约事实'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='采购单一批次履约事实'; CREATE TABLE ll_procurement_command ( id BIGINT NOT NULL COMMENT '操作记录ID', order_id BIGINT NOT NULL COMMENT '采购订单ID', @@ -42,7 +42,7 @@ CREATE TABLE ll_procurement_command ( created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '执行时间', PRIMARY KEY(id), UNIQUE KEY uk_procurement_command(order_id,action,idempotency_key), KEY idx_procurement_command_customer(customer_id,order_id,created_at) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='采购节点幂等与不可变审计'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='采购节点幂等与不可变审计'; CREATE TABLE ll_procurement_budget_authorization ( id BIGINT NOT NULL COMMENT '预算授权ID', order_id BIGINT NOT NULL COMMENT '采购订单ID', @@ -54,4 +54,4 @@ CREATE TABLE ll_procurement_budget_authorization ( PRIMARY KEY(id), KEY idx_procurement_budget_order(customer_id,order_id,id), CONSTRAINT chk_procurement_budget_amount CHECK(authorized_amount>=0 AND previous_amount>=0), CONSTRAINT chk_procurement_budget_currency CHECK(currency IN('CNY','USD') AND previous_currency IN('CNY','USD')) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='采购货款预算授权核验历史'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='采购货款预算授权核验历史'; diff --git a/deploy/sql/migrations/overseas/baiyue_lastleg/V17__procurement_physical_facts.sql b/deploy/sql/migrations/overseas/baiyue_lastleg/V17__procurement_physical_facts.sql index 084f7b810..d5386796d 100644 --- a/deploy/sql/migrations/overseas/baiyue_lastleg/V17__procurement_physical_facts.sql +++ b/deploy/sql/migrations/overseas/baiyue_lastleg/V17__procurement_physical_facts.sql @@ -10,7 +10,7 @@ CREATE TABLE ll_procurement_receipt ( PRIMARY KEY(id), UNIQUE KEY uk_procurement_receipt_no(receipt_no), KEY idx_procurement_receipt_source(order_id,leg,source_carton_id,active), CONSTRAINT chk_procurement_receipt_quantity CHECK(good_quantity>=0 AND damaged_quantity>=0 AND extra_quantity>=0 AND good_quantity+damaged_quantity+extra_quantity>0), CONSTRAINT chk_procurement_receipt_leg CHECK((leg='OVERSEAS' AND source_carton_id IS NULL) OR (leg='DOMESTIC' AND source_carton_id IS NOT NULL)) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='采购分次实收与受控更正事实'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='采购分次实收与受控更正事实'; CREATE TABLE ll_procurement_disposition ( id BIGINT NOT NULL COMMENT '差异处理ID', order_id BIGINT NOT NULL COMMENT '采购订单ID', customer_id VARCHAR(128) NOT NULL COMMENT '客户隔离键', @@ -22,7 +22,7 @@ CREATE TABLE ll_procurement_disposition ( created_by VARCHAR(128) NOT NULL COMMENT '采购管理人员', created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '确认时间', PRIMARY KEY(id), KEY idx_procurement_disposition_source(order_id,leg,source_carton_id,receipt_id,kind), CONSTRAINT chk_procurement_disposition_quantity CHECK(quantity>0) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='采购单内差异处置,不自动退款'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='采购单内差异处置,不自动退款'; CREATE TABLE ll_procurement_manifest ( id BIGINT NOT NULL COMMENT '装箱清单ID', order_id BIGINT NOT NULL COMMENT '采购订单ID', customer_id VARCHAR(128) NOT NULL COMMENT '客户隔离键', @@ -31,7 +31,7 @@ CREATE TABLE ll_procurement_manifest ( created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '版本保存时间', PRIMARY KEY(id), UNIQUE KEY uk_procurement_manifest_version(order_id,version), CONSTRAINT chk_procurement_manifest_status CHECK(status IN('DRAFT','CONFIRMED','SUPERSEDED')) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='采购装箱清单不可变版本'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='采购装箱清单不可变版本'; CREATE TABLE ll_procurement_carton ( id BIGINT NOT NULL COMMENT '版本内箱记录ID', order_id BIGINT NOT NULL COMMENT '采购订单ID', manifest_id BIGINT NOT NULL COMMENT '装箱清单版本', @@ -40,12 +40,12 @@ CREATE TABLE ll_procurement_carton ( gross_weight_kg DECIMAL(12,3) NOT NULL COMMENT '封箱毛重千克', quantity BIGINT NOT NULL COMMENT '本箱合格商品总件数', PRIMARY KEY(id), UNIQUE KEY uk_procurement_carton_version(manifest_id,carton_no), KEY idx_procurement_carton_order(order_id,manifest_id), CONSTRAINT chk_procurement_carton_measurement CHECK(length_cm>0 AND width_cm>0 AND height_cm>0 AND gross_weight_kg>0 AND quantity>0) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='采购版本化逐箱实测与件数'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='采购版本化逐箱实测与件数'; CREATE TABLE ll_procurement_carton_source ( carton_id BIGINT NOT NULL COMMENT '版本内箱ID', receipt_id BIGINT NOT NULL COMMENT '海外实收明细来源', quantity BIGINT NOT NULL COMMENT '从该实收装入本箱的件数', PRIMARY KEY(carton_id,receipt_id), KEY idx_procurement_carton_receipt(receipt_id), CONSTRAINT chk_procurement_carton_source_quantity CHECK(quantity>0) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='采购箱内货物的实收来源与数量'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='采购箱内货物的实收来源与数量'; CREATE TABLE ll_procurement_shipment ( id BIGINT NOT NULL COMMENT '采购交寄ID', order_id BIGINT NOT NULL COMMENT '采购订单ID', customer_id VARCHAR(128) NOT NULL COMMENT '客户隔离键', @@ -55,11 +55,11 @@ CREATE TABLE ll_procurement_shipment ( created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '确认时间', PRIMARY KEY(id), UNIQUE KEY uk_procurement_shipment_leg(order_id,leg), UNIQUE KEY uk_procurement_shipment_no(shipment_no), CONSTRAINT chk_procurement_shipment_leg CHECK(leg IN('OVERSEAS','DOMESTIC')) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='采购两段各一次整批真实交寄'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='采购两段各一次整批真实交寄'; CREATE TABLE ll_procurement_shipment_item ( shipment_id BIGINT NOT NULL COMMENT '采购交寄ID', carton_id BIGINT NOT NULL COMMENT '原海外装箱版本内箱ID', quantity BIGINT NOT NULL COMMENT '本次实际交寄件数', carrier VARCHAR(128) NOT NULL COMMENT '承运商快照', tracking_no VARCHAR(128) NOT NULL COMMENT '运单号快照', label_snapshot JSON NOT NULL COMMENT '当次箱规、面单及货物来源快照', PRIMARY KEY(shipment_id,carton_id), CONSTRAINT chk_procurement_shipment_item_quantity CHECK(quantity>0) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='采购整批交寄逐箱事实'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='采购整批交寄逐箱事实'; diff --git a/deploy/sql/migrations/overseas/baiyue_lastleg/V18__procurement_transport_facts.sql b/deploy/sql/migrations/overseas/baiyue_lastleg/V18__procurement_transport_facts.sql index cef0f7d88..5fab257bf 100644 --- a/deploy/sql/migrations/overseas/baiyue_lastleg/V18__procurement_transport_facts.sql +++ b/deploy/sql/migrations/overseas/baiyue_lastleg/V18__procurement_transport_facts.sql @@ -7,7 +7,7 @@ CREATE TABLE ll_procurement_label_set ( created_by VARCHAR(128) NOT NULL COMMENT '制单操作人', created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '上传确认时间', PRIMARY KEY(id), UNIQUE KEY uk_procurement_label_version(order_id,leg,version), CONSTRAINT chk_procurement_label_leg CHECK(leg IN('OVERSEAS','DOMESTIC')) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='采购两段面单不可变版本和地址快照'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='采购两段面单不可变版本和地址快照'; CREATE TABLE ll_procurement_label_item ( label_set_id BIGINT NOT NULL COMMENT '面单批版本ID', carton_id BIGINT NOT NULL COMMENT '原海外清单箱ID', @@ -15,14 +15,14 @@ CREATE TABLE ll_procurement_label_item ( carrier VARCHAR(128) NOT NULL COMMENT '承运商', tracking_no VARCHAR(128) NOT NULL COMMENT '箱主或子运单号', applied_at DATETIME(3) NULL COMMENT '真实贴单时间', applied_by VARCHAR(128) NULL COMMENT '贴单确认人', PRIMARY KEY(label_set_id,carton_id), CONSTRAINT chk_procurement_label_page CHECK(page_no>0) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='采购逐箱文件页关联及贴单事实'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='采购逐箱文件页关联及贴单事实'; CREATE TABLE ll_procurement_estimate_review ( id BIGINT NOT NULL COMMENT '预估核对ID', order_id BIGINT NOT NULL COMMENT '采购订单ID', label_set_id BIGINT NOT NULL COMMENT '已核对国际面单版本', fee_id BIGINT NOT NULL COMMENT '唯一有效预估费用版本', reviewed_by VARCHAR(128) NOT NULL COMMENT '核价人员', reviewed_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '核对时间', PRIMARY KEY(id), UNIQUE KEY uk_procurement_estimate_review(label_set_id,fee_id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='面单清单与预估冻结的显式核对关联'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='面单清单与预估冻结的显式核对关联'; CREATE TABLE ll_procurement_repack ( id BIGINT NOT NULL COMMENT '换包装版本ID', order_id BIGINT NOT NULL COMMENT '采购订单ID', carton_id BIGINT NOT NULL COMMENT '原国际交寄箱ID,保持一对一来源', @@ -32,9 +32,9 @@ CREATE TABLE ll_procurement_repack ( created_by VARCHAR(128) NOT NULL COMMENT '国内作业人员', created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '登记时间', PRIMARY KEY(id), KEY idx_procurement_repack_carton(order_id,carton_id,id), CONSTRAINT chk_procurement_repack_measurement CHECK(length_cm>0 AND width_cm>0 AND height_cm>0 AND gross_weight_kg>0) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='采购国内原箱一对一换包装不可变历史'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='采购国内原箱一对一换包装不可变历史'; ALTER TABLE ll_overseas_procurement_file DROP CHECK chk_ll_procurement_file_category, - MODIFY COLUMN category VARCHAR(16) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'RECEIPT实收、VOUCHER费用、LABEL面单、PURCHASE采购、BUDGET预算、EXCEPTION异常凭证', + MODIFY COLUMN category VARCHAR(16) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'RECEIPT实收、VOUCHER费用、LABEL面单、PURCHASE采购、BUDGET预算、EXCEPTION异常凭证', ADD CONSTRAINT chk_ll_procurement_file_category CHECK(category IN ('RECEIPT','VOUCHER','LABEL','PURCHASE','BUDGET','EXCEPTION')); diff --git a/deploy/sql/migrations/overseas/baiyue_lastleg/V19__procurement_fee_correction.sql b/deploy/sql/migrations/overseas/baiyue_lastleg/V19__procurement_fee_correction.sql index a90a59396..e7be92310 100644 --- a/deploy/sql/migrations/overseas/baiyue_lastleg/V19__procurement_fee_correction.sql +++ b/deploy/sql/migrations/overseas/baiyue_lastleg/V19__procurement_fee_correction.sql @@ -12,9 +12,9 @@ CREATE TABLE ll_procurement_fee_correction ( PRIMARY KEY(id), UNIQUE KEY uk_procurement_correction_no(correction_no), UNIQUE KEY uk_procurement_correction_request(order_id,idempotency_key), KEY idx_procurement_correction_root(order_id,root_fee_id,id), CONSTRAINT chk_procurement_correction_amount CHECK(target_original_amount>=0 AND target_amount>=0 AND previous_net_amount>=0 AND exchange_rate>0) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='采购费用链更正目标与原汇率快照'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='采购费用链更正目标与原汇率快照'; CREATE TABLE ll_procurement_fee_correction_item ( correction_id BIGINT NOT NULL COMMENT '本次费用链更正ID', fee_id BIGINT NOT NULL COMMENT '本次产生的独立正差额或退款费用ID', PRIMARY KEY(correction_id,fee_id), UNIQUE KEY uk_procurement_correction_fee(fee_id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='采购费用目标更正与逐笔资金事实关联'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='采购费用目标更正与逐笔资金事实关联'; diff --git a/deploy/sql/migrations/overseas/baiyue_lastleg/V1__align_lastleg_production_schema.sql b/deploy/sql/migrations/overseas/baiyue_lastleg/V1__align_lastleg_production_schema.sql index 4cf897372..73b54d23f 100644 --- a/deploy/sql/migrations/overseas/baiyue_lastleg/V1__align_lastleg_production_schema.sql +++ b/deploy/sql/migrations/overseas/baiyue_lastleg/V1__align_lastleg_production_schema.sql @@ -133,15 +133,15 @@ PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; CREATE TABLE IF NOT EXISTS ll_warehouse_transfer_measurement_photo ( id bigint NOT NULL COMMENT '测量照片引用ID', transfer_order_id bigint NOT NULL COMMENT '仓储转运单ID', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户租户ID', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户租户ID', file_id bigint NOT NULL COMMENT '文件服务文件ID', - file_name varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '文件名快照', - content_type varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '文件媒体类型快照', + file_name varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '文件名快照', + content_type varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '文件媒体类型快照', size_bytes bigint NOT NULL DEFAULT 0 COMMENT '文件字节数快照', - uploaded_by varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'PDA上传操作人', + uploaded_by varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'PDA上传操作人', uploaded_at datetime(3) NOT NULL COMMENT '上传时间', submitted_at datetime(3) DEFAULT NULL COMMENT '随测量打托提交时间', PRIMARY KEY (id), UNIQUE KEY uk_ll_transfer_measurement_photo_file (customer_id,transfer_order_id,file_id), KEY idx_ll_transfer_measurement_photo_order (customer_id,transfer_order_id,uploaded_at) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='仓储转运PDA测量现场照片引用'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='仓储转运PDA测量现场照片引用'; diff --git a/deploy/sql/migrations/overseas/baiyue_lastleg/V20__procurement_exception_control.sql b/deploy/sql/migrations/overseas/baiyue_lastleg/V20__procurement_exception_control.sql index 1014956d7..e3aeb5ebb 100644 --- a/deploy/sql/migrations/overseas/baiyue_lastleg/V20__procurement_exception_control.sql +++ b/deploy/sql/migrations/overseas/baiyue_lastleg/V20__procurement_exception_control.sql @@ -10,7 +10,7 @@ CREATE TABLE ll_procurement_control_note ( resolved_by VARCHAR(128) NULL COMMENT '处理确认人', resolved_at DATETIME(3) NULL COMMENT '阻断解除时间', resolution_reason VARCHAR(1000) NULL COMMENT '内部处理结果', resolution_voucher_id BIGINT NULL COMMENT '本单处理凭证', PRIMARY KEY(id), UNIQUE KEY uk_procurement_note_no(note_no), KEY idx_procurement_note_block(order_id,blocking,resolved_at) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='采购不可覆盖事实的更正说明及阻断异常'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='采购不可覆盖事实的更正说明及阻断异常'; CREATE TABLE ll_procurement_closure ( id BIGINT NOT NULL COMMENT '采购异常关闭ID', order_id BIGINT NOT NULL COMMENT '采购订单ID', customer_id VARCHAR(128) NOT NULL COMMENT '客户隔离键', @@ -19,7 +19,7 @@ CREATE TABLE ll_procurement_closure ( reason VARCHAR(1000) NOT NULL COMMENT '内部关闭原因', client_summary VARCHAR(500) NOT NULL COMMENT '人工确认的对客关闭摘要', created_by VARCHAR(128) NOT NULL COMMENT '采购管理确认人', created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '关闭确认时间', PRIMARY KEY(id), UNIQUE KEY uk_procurement_closure_order(order_id), UNIQUE KEY uk_procurement_closure_no(closure_no) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='采购实物资金收口后的异常关闭事实'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='采购实物资金收口后的异常关闭事实'; CREATE TABLE ll_procurement_closure_item ( id BIGINT NOT NULL COMMENT '关闭货物处置ID', closure_id BIGINT NOT NULL COMMENT '采购关闭ID', @@ -28,4 +28,4 @@ CREATE TABLE ll_procurement_closure_item ( destination VARCHAR(500) NOT NULL COMMENT '实际去向及接收方说明', voucher_file_id BIGINT NOT NULL COMMENT '本单实物处置证明', PRIMARY KEY(id), KEY idx_procurement_closure_source(closure_id,source_kind,source_id), CONSTRAINT chk_procurement_closure_quantity CHECK(quantity>0) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='异常关闭逐来源剩余货物最终去向,不产生新发运'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='异常关闭逐来源剩余货物最终去向,不产生新发运'; diff --git a/deploy/sql/migrations/overseas/baiyue_lastleg/V23__align_return_movement_collation.sql b/deploy/sql/migrations/overseas/baiyue_lastleg/V23__align_return_movement_collation.sql index 588c40a39..62b49924f 100644 --- a/deploy/sql/migrations/overseas/baiyue_lastleg/V23__align_return_movement_collation.sql +++ b/deploy/sql/migrations/overseas/baiyue_lastleg/V23__align_return_movement_collation.sql @@ -2,43 +2,43 @@ -- Schema: baiyue_lastleg -- Purpose: 统一退货进销存 UNION 文本列排序规则,去除查询转换需求 -- Preconditions: 八张退货表已存在;核对字段长度、可空性、默认值及唯一索引无目标规则冲突 --- Postconditions: 指定文本字段及表默认规则统一为 utf8mb4_unicode_ci;不修改数量、状态或业务标识 +-- Postconditions: 指定文本字段及表默认规则统一为 utf8mb4_0900_ai_ci;不修改数量、状态或业务标识 -- 不重放空库基线;本文件可重复执行。DDL 非事务,失败停止并核对后续执行范围。 ALTER TABLE ll_return_order - DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci, - MODIFY COLUMN customer_id VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户ID和隔离键', - MODIFY COLUMN overseas_warehouse_id VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '操作海外仓ID'; + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci, + MODIFY COLUMN customer_id VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户ID和隔离键', + MODIFY COLUMN overseas_warehouse_id VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '操作海外仓ID'; ALTER TABLE ll_return_order_item - DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci, - MODIFY COLUMN sku_code VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'SKU编码', - MODIFY COLUMN product_name_zh VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL COMMENT '中文品名'; + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci, + MODIFY COLUMN sku_code VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'SKU编码', + MODIFY COLUMN product_name_zh VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL COMMENT '中文品名'; ALTER TABLE ll_return_receipt - DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci, - MODIFY COLUMN receipt_no VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '签收单号'; + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci, + MODIFY COLUMN receipt_no VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '签收单号'; ALTER TABLE ll_return_receipt_item - DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci, - MODIFY COLUMN sku_code VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'SKU编码'; + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci, + MODIFY COLUMN sku_code VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'SKU编码'; ALTER TABLE ll_return_inventory_carton - DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci, - MODIFY COLUMN customer_id VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户ID和隔离键', - MODIFY COLUMN overseas_warehouse_id VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '海外仓ID', - MODIFY COLUMN carton_mark VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '退货箱号和箱唛编码'; + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci, + MODIFY COLUMN customer_id VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户ID和隔离键', + MODIFY COLUMN overseas_warehouse_id VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '海外仓ID', + MODIFY COLUMN carton_mark VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '退货箱号和箱唛编码'; ALTER TABLE ll_return_inventory_carton_item - DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci, - MODIFY COLUMN sku_code VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'SKU编码', - MODIFY COLUMN product_name_zh VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL COMMENT '中文品名'; + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci, + MODIFY COLUMN sku_code VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'SKU编码', + MODIFY COLUMN product_name_zh VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL COMMENT '中文品名'; ALTER TABLE ll_return_inventory_outbound_movement - DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci, - MODIFY COLUMN customer_id VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '库存所属客户ID和隔离键', - MODIFY COLUMN overseas_warehouse_id VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '实际出库海外仓ID', - MODIFY COLUMN sku_code VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '本次出库SKU编码快照', - MODIFY COLUMN product_name_zh VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL COMMENT '中文品名快照', - MODIFY COLUMN outbound_no VARCHAR(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '本次退货出库业务单号'; + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci, + MODIFY COLUMN customer_id VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '库存所属客户ID和隔离键', + MODIFY COLUMN overseas_warehouse_id VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '实际出库海外仓ID', + MODIFY COLUMN sku_code VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '本次出库SKU编码快照', + MODIFY COLUMN product_name_zh VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL COMMENT '中文品名快照', + MODIFY COLUMN outbound_no VARCHAR(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '本次退货出库业务单号'; ALTER TABLE ll_return_sku_conversion - DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci, - MODIFY COLUMN customer_id VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户隔离键', - MODIFY COLUMN overseas_warehouse_id VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '操作海外仓ID', - MODIFY COLUMN old_sku_code VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '换标前SKU', - MODIFY COLUMN new_sku_code VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '换标后SKU', - MODIFY COLUMN product_name_zh VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL COMMENT '中文品名快照', - MODIFY COLUMN relabel_no VARCHAR(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '换标单号'; + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci, + MODIFY COLUMN customer_id VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户隔离键', + MODIFY COLUMN overseas_warehouse_id VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '操作海外仓ID', + MODIFY COLUMN old_sku_code VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '换标前SKU', + MODIFY COLUMN new_sku_code VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '换标后SKU', + MODIFY COLUMN product_name_zh VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL COMMENT '中文品名快照', + MODIFY COLUMN relabel_no VARCHAR(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '换标单号'; diff --git a/deploy/sql/migrations/overseas/baiyue_lastleg/V24__cargo_discard_photos.sql b/deploy/sql/migrations/overseas/baiyue_lastleg/V24__cargo_discard_photos.sql index 1373e87ce..21d33d4f9 100644 --- a/deploy/sql/migrations/overseas/baiyue_lastleg/V24__cargo_discard_photos.sql +++ b/deploy/sql/migrations/overseas/baiyue_lastleg/V24__cargo_discard_photos.sql @@ -13,4 +13,4 @@ CREATE TABLE IF NOT EXISTS ll_cargo_discard_photo ( PRIMARY KEY (id), UNIQUE KEY uk_cargo_discard_photo_file (file_id), KEY idx_cargo_discard_photo_application (customer_id, application_id, pallet_no) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; diff --git a/deploy/sql/migrations/overseas/baiyue_lastleg/V28__merge_transfer_measurement_and_compact_numbers.sql b/deploy/sql/migrations/overseas/baiyue_lastleg/V28__merge_transfer_measurement_and_compact_numbers.sql index 19c90b39e..a0c749cae 100644 --- a/deploy/sql/migrations/overseas/baiyue_lastleg/V28__merge_transfer_measurement_and_compact_numbers.sql +++ b/deploy/sql/migrations/overseas/baiyue_lastleg/V28__merge_transfer_measurement_and_compact_numbers.sql @@ -8,7 +8,7 @@ CREATE TABLE IF NOT EXISTS ll_compact_number_clock ( number_key VARCHAR(16) NOT NULL COMMENT '业务前缀和客户名称缩写', last_second BIGINT NOT NULL COMMENT '最后分配的Unix逻辑秒', PRIMARY KEY (number_key) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='海外业务短单号分配时钟'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='海外业务短单号分配时钟'; -- 仓储转运逐箱测量与清点;仅增加字段,不修改业务数据。 SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_warehouse_transfer_carton' AND column_name='length_cm'), 'SELECT 1', 'ALTER TABLE ll_warehouse_transfer_carton ADD COLUMN length_cm DECIMAL(12,2) NULL COMMENT ''箱子实测长度厘米''')); diff --git a/deploy/sql/migrations/overseas/baiyue_lastleg/V2__align_schema_baseline.sql b/deploy/sql/migrations/overseas/baiyue_lastleg/V2__align_schema_baseline.sql index b8369fedf..ada1b6c6e 100644 --- a/deploy/sql/migrations/overseas/baiyue_lastleg/V2__align_schema_baseline.sql +++ b/deploy/sql/migrations/overseas/baiyue_lastleg/V2__align_schema_baseline.sql @@ -25,9 +25,9 @@ PREPARE stmt FROM @guard; EXECUTE stmt; DEALLOCATE PREPARE stmt; -- 测试环境当前缺失、生产已存在的 PDA 废弃作业字段。 SET @ddl = IF(NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'll_cargo_discard_item' AND column_name = 'actual_discard_packages'), 'ALTER TABLE ll_cargo_discard_item ADD COLUMN actual_discard_packages int NULL COMMENT ''PDA确认实际废弃箱数'' AFTER discard_packages', 'SELECT 1'); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; -SET @ddl = IF(NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'll_cargo_discard_item' AND column_name = 'pda_status'), 'ALTER TABLE ll_cargo_discard_item ADD COLUMN pda_status varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT ''PENDING'' COMMENT ''PDA处理状态:待处理PENDING、已完成COMPLETED'' AFTER actual_discard_packages', 'SELECT 1'); +SET @ddl = IF(NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'll_cargo_discard_item' AND column_name = 'pda_status'), 'ALTER TABLE ll_cargo_discard_item ADD COLUMN pda_status varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT ''PENDING'' COMMENT ''PDA处理状态:待处理PENDING、已完成COMPLETED'' AFTER actual_discard_packages', 'SELECT 1'); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; -SET @ddl = IF(NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'll_cargo_discard_item' AND column_name = 'pda_operator_id'), 'ALTER TABLE ll_cargo_discard_item ADD COLUMN pda_operator_id varchar(128) COLLATE utf8mb4_unicode_ci NULL COMMENT ''PDA操作人'' AFTER pda_status', 'SELECT 1'); +SET @ddl = IF(NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'll_cargo_discard_item' AND column_name = 'pda_operator_id'), 'ALTER TABLE ll_cargo_discard_item ADD COLUMN pda_operator_id varchar(128) COLLATE utf8mb4_0900_ai_ci NULL COMMENT ''PDA操作人'' AFTER pda_status', 'SELECT 1'); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; SET @ddl = IF(NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'll_cargo_discard_item' AND column_name = 'pda_completed_at'), 'ALTER TABLE ll_cargo_discard_item ADD COLUMN pda_completed_at datetime(3) NULL COMMENT ''PDA完成时间'' AFTER pda_operator_id', 'SELECT 1'); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; @@ -35,36 +35,36 @@ PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; -- 生产环境当前缺失、测试已存在的换标与退货字段。 SET @ddl = IF(NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'll_relabel_pda_unit_fact' AND column_name = 'quantity'), 'ALTER TABLE ll_relabel_pda_unit_fact ADD COLUMN quantity int NOT NULL DEFAULT 1 COMMENT ''本次事实处理件数,箱级作业固定为1'' AFTER reason', 'SELECT 1'); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; -SET @ddl = IF(NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'll_return_inventory_carton' AND column_name = 'pallet_code'), 'ALTER TABLE ll_return_inventory_carton ADD COLUMN pallet_code varchar(128) COLLATE utf8mb4_unicode_ci NULL COMMENT ''签收时绑定的托盘码'' AFTER source_return_no', 'SELECT 1'); +SET @ddl = IF(NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'll_return_inventory_carton' AND column_name = 'pallet_code'), 'ALTER TABLE ll_return_inventory_carton ADD COLUMN pallet_code varchar(128) COLLATE utf8mb4_0900_ai_ci NULL COMMENT ''签收时绑定的托盘码'' AFTER source_return_no', 'SELECT 1'); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; -SET @ddl = IF(NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'll_return_order' AND column_name = 'discrepancy_reason'), 'ALTER TABLE ll_return_order ADD COLUMN discrepancy_reason varchar(500) COLLATE utf8mb4_unicode_ci NULL COMMENT ''手动完结差异原因'' AFTER status', 'SELECT 1'); +SET @ddl = IF(NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'll_return_order' AND column_name = 'discrepancy_reason'), 'ALTER TABLE ll_return_order ADD COLUMN discrepancy_reason varchar(500) COLLATE utf8mb4_0900_ai_ci NULL COMMENT ''手动完结差异原因'' AFTER status', 'SELECT 1'); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; SET @ddl = IF(NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'll_return_order' AND column_name = 'manually_completed_at'), 'ALTER TABLE ll_return_order ADD COLUMN manually_completed_at datetime(3) NULL COMMENT ''手动差异完结时间'' AFTER discrepancy_reason', 'SELECT 1'); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; -SET @ddl = IF(NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'll_return_receipt' AND column_name = 'pallet_code'), 'ALTER TABLE ll_return_receipt ADD COLUMN pallet_code varchar(128) COLLATE utf8mb4_unicode_ci NULL COMMENT ''本次签收绑定的托盘码'' AFTER carton_mark', 'SELECT 1'); +SET @ddl = IF(NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'll_return_receipt' AND column_name = 'pallet_code'), 'ALTER TABLE ll_return_receipt ADD COLUMN pallet_code varchar(128) COLLATE utf8mb4_0900_ai_ci NULL COMMENT ''本次签收绑定的托盘码'' AFTER carton_mark', 'SELECT 1'); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; -SET @ddl = IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'll_delivery_order' AND column_name = 'delivery_group_key' AND (column_type <> 'varchar(255)' OR is_nullable <> 'NO' OR collation_name <> 'utf8mb4_unicode_ci')), 'ALTER TABLE ll_delivery_order MODIFY COLUMN delivery_group_key varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT ''同一出库单内按目的FBA仓拆单的稳定分组键''', 'SELECT 1'); +SET @ddl = IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'll_delivery_order' AND column_name = 'delivery_group_key' AND (column_type <> 'varchar(255)' OR is_nullable <> 'NO' OR collation_name <> 'utf8mb4_0900_ai_ci')), 'ALTER TABLE ll_delivery_order MODIFY COLUMN delivery_group_key varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT ''同一出库单内按目的FBA仓拆单的稳定分组键''', 'SELECT 1'); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; -SET @ddl = IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'll_local_looseboard_order' AND column_name = 'container_type' AND (column_type <> 'varchar(32)' OR is_nullable <> 'NO' OR column_default <> '40HQ' OR collation_name <> 'utf8mb4_unicode_ci')), 'ALTER TABLE ll_local_looseboard_order MODIFY COLUMN container_type varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT ''40HQ'' COMMENT ''平台统一柜型:40GP、40HQ、45HQ''', 'SELECT 1'); +SET @ddl = IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'll_local_looseboard_order' AND column_name = 'container_type' AND (column_type <> 'varchar(32)' OR is_nullable <> 'NO' OR column_default <> '40HQ' OR collation_name <> 'utf8mb4_0900_ai_ci')), 'ALTER TABLE ll_local_looseboard_order MODIFY COLUMN container_type varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT ''40HQ'' COMMENT ''平台统一柜型:40GP、40HQ、45HQ''', 'SELECT 1'); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; -SET @ddl = IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'll_us_truck_order' AND column_name = 'container_type' AND (column_type <> 'varchar(32)' OR is_nullable <> 'NO' OR column_default <> '40HQ' OR collation_name <> 'utf8mb4_unicode_ci')), 'ALTER TABLE ll_us_truck_order MODIFY COLUMN container_type varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT ''40HQ'' COMMENT ''平台统一柜型:40GP、40HQ、45HQ''', 'SELECT 1'); +SET @ddl = IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'll_us_truck_order' AND column_name = 'container_type' AND (column_type <> 'varchar(32)' OR is_nullable <> 'NO' OR column_default <> '40HQ' OR collation_name <> 'utf8mb4_0900_ai_ci')), 'ALTER TABLE ll_us_truck_order MODIFY COLUMN container_type varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT ''40HQ'' COMMENT ''平台统一柜型:40GP、40HQ、45HQ''', 'SELECT 1'); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; SET @ddl = IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'll_outbound_pda_pick' AND column_name = 'inventory_position_id' AND (column_type <> 'bigint' OR is_nullable <> 'NO')), 'ALTER TABLE ll_outbound_pda_pick MODIFY COLUMN inventory_position_id bigint NOT NULL COMMENT ''库存仓位ID''', 'SELECT 1'); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; SET @ddl = IF( - EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'll_relabel_item' AND column_name = 'new_sku_code' AND (column_type <> 'varchar(128)' OR is_nullable <> 'NO' OR collation_name <> 'utf8mb4_unicode_ci')) - OR EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'll_relabel_item' AND column_name = 'source_warehouse_id' AND (column_type <> 'varchar(128)' OR is_nullable <> 'NO' OR collation_name <> 'utf8mb4_unicode_ci')), - 'ALTER TABLE ll_relabel_item MODIFY COLUMN new_sku_code varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT ''换标后SKU编码'', MODIFY COLUMN source_warehouse_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT ''原海外中转仓ID''', + EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'll_relabel_item' AND column_name = 'new_sku_code' AND (column_type <> 'varchar(128)' OR is_nullable <> 'NO' OR collation_name <> 'utf8mb4_0900_ai_ci')) + OR EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'll_relabel_item' AND column_name = 'source_warehouse_id' AND (column_type <> 'varchar(128)' OR is_nullable <> 'NO' OR collation_name <> 'utf8mb4_0900_ai_ci')), + 'ALTER TABLE ll_relabel_item MODIFY COLUMN new_sku_code varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT ''换标后SKU编码'', MODIFY COLUMN source_warehouse_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT ''原海外中转仓ID''', 'SELECT 1' ); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; -SET @ddl = IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'll_relabel_pda_task' AND column_name = 'review_decision' AND (column_type <> 'varchar(16)' OR is_nullable <> 'YES' OR collation_name <> 'utf8mb4_unicode_ci')), 'ALTER TABLE ll_relabel_pda_task MODIFY COLUMN review_decision varchar(16) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT ''最近复核结论:APPROVE或REWORK''', 'SELECT 1'); +SET @ddl = IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'll_relabel_pda_task' AND column_name = 'review_decision' AND (column_type <> 'varchar(16)' OR is_nullable <> 'YES' OR collation_name <> 'utf8mb4_0900_ai_ci')), 'ALTER TABLE ll_relabel_pda_task MODIFY COLUMN review_decision varchar(16) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT ''最近复核结论:APPROVE或REWORK''', 'SELECT 1'); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; SET @ddl = IF( - (SELECT table_collation FROM information_schema.tables WHERE table_schema = DATABASE() AND table_name = 'll_warehouse_transfer_measurement_photo') <> 'utf8mb4_unicode_ci', - 'ALTER TABLE ll_warehouse_transfer_measurement_photo CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci', + (SELECT table_collation FROM information_schema.tables WHERE table_schema = DATABASE() AND table_name = 'll_warehouse_transfer_measurement_photo') <> 'utf8mb4_0900_ai_ci', + 'ALTER TABLE ll_warehouse_transfer_measurement_photo CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci', 'SELECT 1' ); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; diff --git a/deploy/sql/migrations/overseas/baiyue_lastleg/V32__dispatch_transport_tasks.sql b/deploy/sql/migrations/overseas/baiyue_lastleg/V32__dispatch_transport_tasks.sql index 4bbd4a3e3..c4488e09f 100644 --- a/deploy/sql/migrations/overseas/baiyue_lastleg/V32__dispatch_transport_tasks.sql +++ b/deploy/sql/migrations/overseas/baiyue_lastleg/V32__dispatch_transport_tasks.sql @@ -8,11 +8,11 @@ CREATE TABLE ll_dispatch_truck_task ( active_order VARCHAR(193) NULL COMMENT '有效原单唯一键;取消置空,完成保留', PRIMARY KEY(dispatch_no), UNIQUE KEY uk_transport_trip(trip_no), UNIQUE KEY uk_transport_source(active_order), KEY idx_transport_order(customer_id,order_no) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='排车与本土卡车原单关联'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='排车与本土卡车原单关联'; CREATE TABLE ll_dispatch_transport_create ( trip_no VARCHAR(64) NOT NULL PRIMARY KEY COMMENT '本次运输资源记录号', request_hash CHAR(64) NOT NULL COMMENT '首次安排请求摘要' -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='本土卡车排车创建请求账本'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='本土卡车排车创建请求账本'; CREATE TABLE ll_dispatch_transport_command ( dispatch_no VARCHAR(64) NOT NULL COMMENT '排车业务号', request_key VARCHAR(128) NOT NULL COMMENT '客户端幂等请求键', @@ -20,4 +20,4 @@ CREATE TABLE ll_dispatch_transport_command ( evidence_json JSON NULL COMMENT '已确认运输完成凭证标识列表', created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '业务命令提交时间', PRIMARY KEY(dispatch_no,request_key) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='排车运输执行命令与凭证账本'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='排车运输执行命令与凭证账本'; diff --git a/deploy/sql/migrations/overseas/baiyue_lastleg/V34__procurement_direction.sql b/deploy/sql/migrations/overseas/baiyue_lastleg/V34__procurement_direction.sql new file mode 100644 index 000000000..3f38b7bad --- /dev/null +++ b/deploy/sql/migrations/overseas/baiyue_lastleg/V34__procurement_direction.sql @@ -0,0 +1,9 @@ +-- 先于新版本 Lastleg 上线执行。历史订单保持海外采购回国内,不改数量、资金或交寄事实。 +SET @procurement_direction_ddl = IF( + (SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() + AND TABLE_NAME = 'll_overseas_procurement_order' AND COLUMN_NAME = 'procurement_direction') = 0, + 'ALTER TABLE ll_overseas_procurement_order ADD COLUMN procurement_direction VARCHAR(32) NOT NULL DEFAULT ''OVERSEAS_TO_DOMESTIC'' COMMENT ''采购方向:海外采购回国内/国内采购发海外''', + 'SELECT 1'); +PREPARE procurement_direction_stmt FROM @procurement_direction_ddl; +EXECUTE procurement_direction_stmt; +DEALLOCATE PREPARE procurement_direction_stmt; diff --git a/deploy/sql/migrations/overseas/baiyue_lastleg/V35__widen_finance_fee_command_action.sql b/deploy/sql/migrations/overseas/baiyue_lastleg/V35__widen_finance_fee_command_action.sql new file mode 100644 index 000000000..1efeda27e --- /dev/null +++ b/deploy/sql/migrations/overseas/baiyue_lastleg/V35__widen_finance_fee_command_action.sql @@ -0,0 +1,3 @@ +-- 海外费用资金重试动作长度修复:兼容 MANUAL_FUNDING_RETRY 等费用幂等动作。 +ALTER TABLE baiyue_lastleg.ll_finance_fee_command + MODIFY COLUMN action VARCHAR(32) NOT NULL COMMENT '费用写操作动作:CREATE、SUBMIT、REVIEW、ADJUST、VOID或资金重试'; diff --git a/deploy/sql/migrations/overseas/baiyue_lastleg/V36__align_order_budget_collation.sql b/deploy/sql/migrations/overseas/baiyue_lastleg/V36__align_order_budget_collation.sql new file mode 100644 index 000000000..4117f30d4 --- /dev/null +++ b/deploy/sql/migrations/overseas/baiyue_lastleg/V36__align_order_budget_collation.sql @@ -0,0 +1,10 @@ +-- Region: OVERSEAS +-- Schema: baiyue_lastleg +-- Purpose: 预算释放查询的客户和订单号关联字段与采购单、卡车单排序规则一致 +-- Preconditions: ll_order_budget_reservation 已存在;核对列长度、可空性及索引 +-- Postconditions: customer_id、order_no 使用 utf8mb4_0900_ai_ci;不修改任何业务数据 +-- DDL 非事务,执行前核对目标库;失败后检查列定义再继续。 +ALTER TABLE baiyue_lastleg.ll_order_budget_reservation + DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci, + MODIFY COLUMN customer_id VARCHAR(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户隔离标识', + MODIFY COLUMN order_no VARCHAR(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL COMMENT '成功创建的业务单号'; diff --git a/deploy/sql/migrations/overseas/baiyue_lastleg/V37__looseboard_receiving_workflow.sql b/deploy/sql/migrations/overseas/baiyue_lastleg/V37__looseboard_receiving_workflow.sql new file mode 100644 index 000000000..9eab53bbb --- /dev/null +++ b/deploy/sql/migrations/overseas/baiyue_lastleg/V37__looseboard_receiving_workflow.sql @@ -0,0 +1,10 @@ +ALTER TABLE ll_local_looseboard_order + ADD COLUMN delivery_mode VARCHAR(32) NULL COMMENT '入仓方式:送货到仓或仓库上门取货', + ADD COLUMN operating_warehouse_id VARCHAR(128) NULL COMMENT '实际收货海外仓ID', + ADD COLUMN destination_type VARCHAR(16) NULL COMMENT '目的地类型:FBA或私人地址', + ADD COLUMN destination_postal_code VARCHAR(32) NULL COMMENT '私人目的地邮编', + ADD COLUMN delivery_contact_name VARCHAR(128) NULL COMMENT '送货到仓联系人', + ADD COLUMN delivery_contact_phone VARCHAR(64) NULL COMMENT '送货到仓联系电话', + ADD COLUMN estimated_arrival_at DATETIME(3) NULL COMMENT '预计到仓时间', + ADD COLUMN tracking_no VARCHAR(255) NULL COMMENT '物流跟踪号', + ADD COLUMN transfer_no VARCHAR(64) NULL COMMENT '关联的仓储转运收货单号'; diff --git a/deploy/sql/migrations/overseas/baiyue_lastleg/V3__relax_valid_nullable_fields.sql b/deploy/sql/migrations/overseas/baiyue_lastleg/V3__relax_valid_nullable_fields.sql index 6b5c7795d..82bf8984c 100644 --- a/deploy/sql/migrations/overseas/baiyue_lastleg/V3__relax_valid_nullable_fields.sql +++ b/deploy/sql/migrations/overseas/baiyue_lastleg/V3__relax_valid_nullable_fields.sql @@ -8,5 +8,5 @@ ALTER TABLE ll_outbound_pda_pick MODIFY COLUMN inventory_position_id bigint NULL COMMENT '库存仓位ID;退货箱直接出库时为空'; ALTER TABLE ll_relabel_item - MODIFY COLUMN new_sku_code varchar(128) COLLATE utf8mb4_unicode_ci NULL COMMENT '换标后SKU编码;批量SKU换标使用子明细JSON时为空', - MODIFY COLUMN source_warehouse_id varchar(128) COLLATE utf8mb4_unicode_ci NULL COMMENT '原海外中转仓ID;预入仓批量SKU换标时为空'; + MODIFY COLUMN new_sku_code varchar(128) COLLATE utf8mb4_0900_ai_ci NULL COMMENT '换标后SKU编码;批量SKU换标使用子明细JSON时为空', + MODIFY COLUMN source_warehouse_id varchar(128) COLLATE utf8mb4_0900_ai_ci NULL COMMENT '原海外中转仓ID;预入仓批量SKU换标时为空'; diff --git a/deploy/sql/migrations/overseas/baiyue_lastleg/V9__add_transfer_receipt_resource_command.sql b/deploy/sql/migrations/overseas/baiyue_lastleg/V9__add_transfer_receipt_resource_command.sql index 2e0e6f4cc..bb02f86a8 100644 --- a/deploy/sql/migrations/overseas/baiyue_lastleg/V9__add_transfer_receipt_resource_command.sql +++ b/deploy/sql/migrations/overseas/baiyue_lastleg/V9__add_transfer_receipt_resource_command.sql @@ -27,4 +27,4 @@ CREATE TABLE ll_warehouse_transfer_receipt_command ( UNIQUE KEY uk_ll_wh_receipt_cmd_key (customer_id, idempotency_key), KEY idx_ll_wh_receipt_cmd_target (customer_id, transfer_order_id, target_pallet_id), CONSTRAINT ck_ll_wh_receipt_cmd_status CHECK (status IN ('PREPARED','RESERVED','COMPLETED')) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='转运逐来源收货原请求与共享目标资源占用命令'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='转运逐来源收货原请求与共享目标资源占用命令'; diff --git a/deploy/sql/overseas-fee-canonical-write-permissions-repair.sql b/deploy/sql/overseas-fee-canonical-write-permissions-repair.sql new file mode 100644 index 000000000..f4b432857 --- /dev/null +++ b/deploy/sql/overseas-fee-canonical-write-permissions-repair.sql @@ -0,0 +1,121 @@ +-- 海外费用既有写按钮最小修复;MySQL 8,目标只能是 baiyue_iam。 +-- 仅恢复基线 5105024/5105025 及既有 4000001 PLATFORM_ADMIN 缺失授权。 +-- 不创建角色/成员,不授予其他角色,不新增 overseas-fee:edit/review 别名。 +-- 在支持 DELIMITER、遇错停止的客户端同连接整份执行;执行期间暂停相关角色权限维护。 +-- 不在其他未提交事务中调用。所有业务DML及subject版本变更同事务,任何异常回滚。 +USE baiyue_iam; +SET NAMES utf8mb4; +SELECT DATABASE() AS target_database; + +DROP PROCEDURE IF EXISTS repair_overseas_fee_canonical_write; +DELIMITER $$ +CREATE PROCEDURE repair_overseas_fee_canonical_write() +BEGIN + DECLARE locked_role BIGINT; + DECLARE changed_rows BIGINT DEFAULT 0; + DECLARE EXIT HANDLER FOR SQLEXCEPTION + BEGIN + ROLLBACK; + RESIGNAL; + END; + + START TRANSACTION; + IF DATABASE()<>'baiyue_iam' THEN + SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='Wrong schema: expected baiyue_iam'; + END IF; + IF (SELECT COUNT(*) FROM iam_role WHERE id=4000001 AND tenant_id=0 AND app_code='PLATFORM' + AND role_code='PLATFORM_ADMIN' AND role_type='SYSTEM' AND status='ACTIVE' AND deleted=0)<>1 + OR EXISTS(SELECT 1 FROM iam_role WHERE role_code='PLATFORM_ADMIN' AND id<>4000001) THEN + SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='Existing PLATFORM_ADMIN identity/status mismatch; no role will be created'; + END IF; + SELECT id INTO locked_role FROM iam_role WHERE id=4000001 FOR UPDATE; + + -- 新建缺失按钮使用当前客户应收父菜单;只接受已核对的既有财务菜单,不修复其他菜单。 + IF (SELECT COUNT(*) FROM iam_permission WHERE id=5105041 AND tenant_id=0 AND app_code='PLATFORM' + AND permission_type='MENU' AND permission_code='platform:finance:customer-arap:list' + AND status='ACTIVE' AND deleted=0)<>1 + OR EXISTS(SELECT 1 FROM iam_permission WHERE permission_code='platform:finance:customer-arap:list' AND id<>5105041) THEN + SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='Existing customer AR/AP parent menu is missing or mismatched'; + END IF; + IF EXISTS(SELECT 1 FROM iam_permission WHERE + (id=5105024 AND NOT(permission_code<=>'platform:finance:fee-source:review')) + OR (id=5105025 AND NOT(permission_code<=>'platform:finance:fee-source:edit')) + OR (permission_code='platform:finance:fee-source:review' AND id<>5105024) + OR (permission_code='platform:finance:fee-source:edit' AND id<>5105025) + OR (id IN(5105024,5105025) AND (tenant_id<>0 OR app_code<>'PLATFORM' + OR permission_type<>'BUTTON' OR built_in<>1 OR parent_id NOT IN(5105000,5105041) + OR route_path IS NOT NULL + OR NOT(http_method<=>IF(id=5105024,'POST','PUT'))))) THEN + SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='Canonical fee button identity/type/metadata conflict'; + END IF; + IF EXISTS(SELECT 1 FROM iam_permission child LEFT JOIN iam_permission parent ON parent.id=child.parent_id + WHERE child.id IN(5105024,5105025) AND (parent.id IS NULL OR parent.tenant_id<>0 + OR parent.app_code<>'PLATFORM' OR parent.permission_type<>'MENU' OR parent.status<>'ACTIVE' OR parent.deleted<>0 + OR NOT(parent.permission_code<=>IF(child.parent_id=5105000,'platform:finance','platform:finance:customer-arap:list')))) THEN + SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='Existing fee button parent is invalid'; + END IF; + IF EXISTS(SELECT 1 FROM iam_role_permission WHERE permission_id IN(5105024,5105025) AND tenant_id<>0) + OR EXISTS(SELECT 1 FROM iam_membership_role mr LEFT JOIN iam_membership m ON m.id=mr.membership_id + WHERE mr.role_id=4000001 AND (mr.tenant_id<>0 OR m.id IS NULL OR m.tenant_id<>0)) THEN + SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='Existing canonical permission or administrator membership scope mismatch'; + END IF; + -- 恢复停用/缺失资源也会恢复其他角色的旧授权;这种情况必须单独核对,不能随本补丁扩权。 + IF EXISTS(SELECT 1 FROM iam_role_permission rp LEFT JOIN iam_permission p ON p.id=rp.permission_id + WHERE rp.permission_id IN(5105024,5105025) AND rp.role_id<>4000001 + AND (p.id IS NULL OR p.status<>'ACTIVE' OR p.deleted<>0)) THEN + SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='Restoring fee button would affect another role; review separately'; + END IF; + + INSERT INTO iam_permission + (id,tenant_id,app_code,permission_type,parent_id,permission_code,permission_name,route_path,http_method,sort_no,status,built_in,deleted) + SELECT 5105024,0,'PLATFORM','BUTTON',5105041,'platform:finance:fee-source:review','审核财务费用来源',NULL,'POST',150,'ACTIVE',1,0 + WHERE NOT EXISTS(SELECT 1 FROM iam_permission WHERE id=5105024); + SET changed_rows=changed_rows+ROW_COUNT(); + INSERT INTO iam_permission + (id,tenant_id,app_code,permission_type,parent_id,permission_code,permission_name,route_path,http_method,sort_no,status,built_in,deleted) + SELECT 5105025,0,'PLATFORM','BUTTON',5105041,'platform:finance:fee-source:edit','编辑财务费用来源',NULL,'PUT',160,'ACTIVE',1,0 + WHERE NOT EXISTS(SELECT 1 FROM iam_permission WHERE id=5105025); + SET changed_rows=changed_rows+ROW_COUNT(); + UPDATE iam_permission SET status='ACTIVE',deleted=0 + WHERE id IN(5105024,5105025) AND (status<>'ACTIVE' OR deleted<>0); + SET changed_rows=changed_rows+ROW_COUNT(); + INSERT INTO iam_role_permission(tenant_id,role_id,permission_id,created_at) + SELECT 0,4000001,p.id,CURRENT_TIMESTAMP(3) FROM iam_permission p WHERE p.id IN(5105024,5105025) + AND NOT EXISTS(SELECT 1 FROM iam_role_permission rp WHERE rp.role_id=4000001 AND rp.permission_id=p.id); + SET changed_rows=changed_rows+ROW_COUNT(); + + -- 只有真实变更才推进版本;枚举所有既有管理员成员,不依赖当前登录用户,也不遗漏停用成员。 + IF changed_rows>0 THEN + INSERT INTO iam_security_subject_version(auth_user_id,permission_version) + SELECT DISTINCT u.auth_user_id,1 FROM iam_user u + JOIN iam_membership m ON m.user_id=u.id AND m.tenant_id=0 + JOIN iam_membership_role mr ON mr.membership_id=m.id AND mr.tenant_id=m.tenant_id AND mr.role_id=4000001 + WHERE u.auth_user_id IS NOT NULL + ON DUPLICATE KEY UPDATE permission_version=permission_version+1; + END IF; + IF (SELECT COUNT(*) FROM iam_role_permission rp JOIN iam_permission p ON p.id=rp.permission_id + WHERE rp.role_id=4000001 AND rp.tenant_id=0 AND p.id IN(5105024,5105025) + AND p.tenant_id=0 AND p.app_code='PLATFORM' AND p.permission_type='BUTTON' + AND p.status='ACTIVE' AND p.deleted=0)<>2 THEN + SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='Canonical fee write grants incomplete; repair rolled back'; + END IF; + COMMIT; + SELECT changed_rows AS repaired_catalog_or_grant_rows; +END$$ +DELIMITER ; +CALL repair_overseas_fee_canonical_write(); +DROP PROCEDURE repair_overseas_fee_canonical_write; + +-- 核验1:必须恰好两行,角色4000001、tenant=0、BUTTON、ACTIVE、deleted=0。 +SELECT r.id AS role_id,r.role_code,rp.tenant_id,p.id AS permission_id,p.permission_code, + p.permission_type,p.status,p.deleted +FROM iam_role r JOIN iam_role_permission rp ON rp.role_id=r.id +JOIN iam_permission p ON p.id=rp.permission_id +WHERE r.id=4000001 AND p.id IN(5105024,5105025) ORDER BY p.id; +-- 核验2:仅查看既有管理员主体与当前版本,不创建或切换任何成员角色。 +SELECT DISTINCT u.auth_user_id,m.id AS membership_id,u.status AS user_status,m.status AS membership_status, + COALESCE(sv.permission_version,0) AS permission_version +FROM iam_user u JOIN iam_membership m ON m.user_id=u.id AND m.tenant_id=0 +JOIN iam_membership_role mr ON mr.membership_id=m.id AND mr.tenant_id=m.tenant_id AND mr.role_id=4000001 +LEFT JOIN iam_security_subject_version sv ON sv.auth_user_id=u.auth_user_id +ORDER BY u.auth_user_id,m.id; diff --git a/deploy/sql/overseas-fee-menu-finance-parent-repair.sql b/deploy/sql/overseas-fee-menu-finance-parent-repair.sql new file mode 100644 index 000000000..b7d4acf9b --- /dev/null +++ b/deploy/sql/overseas-fee-menu-finance-parent-repair.sql @@ -0,0 +1,83 @@ +-- 海外费用菜单定向修复,MySQL 8.0+,目标只能 baiyue_iam。 +-- 将“海外费用管理”改为“海外费用明细”,并挂到“财务管理”下。 +-- 权限码、路由和角色授权保持不变;可重复执行。 +USE baiyue_iam; +SET NAMES utf8mb4; + +DROP PROCEDURE IF EXISTS repair_overseas_fee_menu_parent; +DELIMITER $$ +CREATE PROCEDURE repair_overseas_fee_menu_parent() +BEGIN + DECLARE v_menu_changed INT DEFAULT 0; + DECLARE EXIT HANDLER FOR SQLEXCEPTION + BEGIN + ROLLBACK; + RESIGNAL; + END; + + IF (SELECT COUNT(*) FROM iam_permission + WHERE id=5105000 AND tenant_id=0 AND app_code='PLATFORM' + AND permission_type='MENU' AND permission_code='platform:finance' + AND status='ACTIVE' AND deleted=0)<>1 THEN + SIGNAL SQLSTATE '45000' + SET MESSAGE_TEXT='Finance parent menu platform:finance is missing or inactive'; + END IF; + + IF EXISTS (SELECT 1 FROM iam_permission + WHERE (id=5117200 AND (permission_code<>'platform:overseas-fee:list' + OR tenant_id<>0 OR app_code<>'PLATFORM' OR permission_type<>'MENU')) + OR (permission_code='platform:overseas-fee:list' + AND (id<>5117200 OR tenant_id<>0 OR app_code<>'PLATFORM' + OR permission_type<>'MENU'))) THEN + SIGNAL SQLSTATE '45000' + SET MESSAGE_TEXT='Overseas fee menu permission identity conflict'; + END IF; + + IF (SELECT COUNT(*) FROM iam_permission + WHERE id=5117200 AND tenant_id=0 AND app_code='PLATFORM' + AND permission_type='MENU' AND permission_code='platform:overseas-fee:list')<>1 THEN + SIGNAL SQLSTATE '45000' + SET MESSAGE_TEXT='Overseas fee menu platform:overseas-fee:list is missing'; + END IF; + + START TRANSACTION; + UPDATE iam_permission + SET parent_id=5105000, + permission_name='海外费用明细', + route_path='/overseas-business/fees', + http_method='GET', + sort_no=60, + status='ACTIVE', + deleted=0, + updated_at=CURRENT_TIMESTAMP(3) + WHERE id=5117200 AND tenant_id=0 AND app_code='PLATFORM' + AND permission_type='MENU' AND permission_code='platform:overseas-fee:list' + AND (parent_id<>5105000 OR permission_name<>'海外费用明细' + OR route_path<>'/overseas-business/fees' OR http_method<>'GET' + OR sort_no<>60 OR status<>'ACTIVE' OR deleted<>0); + SET v_menu_changed=ROW_COUNT(); + + -- 菜单实际变化时递增已获该菜单权限用户的安全版本,刷新旧授权快照。 + IF v_menu_changed>0 THEN + INSERT INTO iam_security_subject_version(auth_user_id,permission_version) + SELECT DISTINCT u.auth_user_id,1 + FROM iam_user u + JOIN iam_membership m ON m.user_id=u.id AND m.tenant_id=0 + JOIN iam_membership_role mr ON mr.membership_id=m.id AND mr.tenant_id=0 + JOIN iam_role r ON r.id=mr.role_id AND r.tenant_id=0 + JOIN iam_role_permission rp ON rp.role_id=r.id + AND rp.permission_id=5117200 AND rp.tenant_id=0 + WHERE u.auth_user_id IS NOT NULL AND u.status='ACTIVE' AND u.deleted=0 + AND m.status='ACTIVE' AND m.deleted=0 + AND r.app_code='PLATFORM' AND r.status='ACTIVE' AND r.deleted=0 + ON DUPLICATE KEY UPDATE permission_version=permission_version+1; + END IF; + + COMMIT; +END$$ +DELIMITER ; +CALL repair_overseas_fee_menu_parent(); +DROP PROCEDURE repair_overseas_fee_menu_parent; + +SELECT id,parent_id,permission_code,permission_name,route_path,sort_no +FROM iam_permission WHERE id IN (5105000,5117200) ORDER BY id; diff --git a/deploy/sql/overseas-fee-remark-add.sql b/deploy/sql/overseas-fee-remark-add.sql new file mode 100644 index 000000000..10efb6dd9 --- /dev/null +++ b/deploy/sql/overseas-fee-remark-add.sql @@ -0,0 +1,19 @@ +-- 海外人工费用说明字段。仅修改 baiyue_lastleg.ll_finance_fee_source 的结构。 +-- 执行需单独授权;使用独立连接、遇错停止。先执行本脚本,再加载配套 Lastleg 代码。 +-- 无业务数据回填、费用重建、资金操作或菜单变更。已有字段时跳过新增。 +USE baiyue_lastleg; + +SET @overseas_fee_remark_ddl = ( + SELECT IF(EXISTS( + SELECT 1 FROM information_schema.columns + WHERE table_schema = DATABASE() AND table_name = 'll_finance_fee_source' AND column_name = 'fee_remark' + ), 'SELECT ''fee_remark already exists'' AS result', + 'ALTER TABLE ll_finance_fee_source ADD COLUMN fee_remark VARCHAR(500) NULL COMMENT ''费用说明,补收或重复作业原因'' AFTER fee_item_name') +); +PREPARE overseas_fee_remark_stmt FROM @overseas_fee_remark_ddl; +EXECUTE overseas_fee_remark_stmt; +DEALLOCATE PREPARE overseas_fee_remark_stmt; + +SELECT table_schema, table_name, column_name, column_type, is_nullable, column_comment +FROM information_schema.columns +WHERE table_schema = 'baiyue_lastleg' AND table_name = 'll_finance_fee_source' AND column_name = 'fee_remark'; diff --git a/deploy/sql/overseas-fees-storage-rent-structure-add.sql b/deploy/sql/overseas-fees-storage-rent-structure-add.sql new file mode 100644 index 000000000..b45f7bef9 --- /dev/null +++ b/deploy/sql/overseas-fees-storage-rent-structure-add.sql @@ -0,0 +1,97 @@ +-- 指定 TEST 的海外费用/仓租结构补齐;实例必须由执行者另行核对并获准。 +-- 仅新增 ll_storage_rent_inbound 和 ll_finance_fee_source.reviewer_name。 +-- 使用独立连接,支持 DELIMITER,遇错停止;禁止 mysql --force。 +-- 不更新费用、库存、钱包或历史计租数据,不重放空库初始化。 +-- DDL 隐式提交;失败先回查结构,禁止删除已建表/字段来重跑。 +USE baiyue_lastleg; + +-- 仅清理本脚本可能遗留的专用校验过程,不删除业务对象。 +DROP PROCEDURE IF EXISTS overseas_fee_rent_structure_add_20261026; +DELIMITER $$ +CREATE PROCEDURE overseas_fee_rent_structure_add_20261026() +BEGIN + DECLARE inbound_exists INT DEFAULT 0; + DECLARE reviewer_exists INT DEFAULT 0; + + IF DATABASE()<>'baiyue_lastleg' THEN + SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='目标必须为 baiyue_lastleg'; + END IF; + -- 在首个业务 DDL 前核对两张依赖表及新增字段的定位列。 + IF (SELECT COUNT(*) FROM information_schema.TABLES + WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME IN ('ll_finance_fee_source','ll_storage_rent_lot') + AND TABLE_TYPE='BASE TABLE' AND ENGINE='InnoDB')<>2 + OR (SELECT COUNT(*) FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='ll_finance_fee_source' + AND COLUMN_NAME='reviewer_id' AND DATA_TYPE='varchar' AND CHARACTER_MAXIMUM_LENGTH=128)<>1 THEN + SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='费用来源或计租批次基础结构不匹配,停止补齐'; + END IF; + + SELECT COUNT(*) INTO reviewer_exists FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='ll_finance_fee_source' AND COLUMN_NAME='reviewer_name'; + IF reviewer_exists>0 AND (SELECT COUNT(*) FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='ll_finance_fee_source' AND COLUMN_NAME='reviewer_name' + AND DATA_TYPE='varchar' AND CHARACTER_MAXIMUM_LENGTH=255 AND IS_NULLABLE='YES' + AND COLUMN_DEFAULT IS NULL AND EXTRA='')<>1 THEN + SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='已存在的审核人名称字段与正式定义不同,停止且不覆盖'; + END IF; + + SELECT COUNT(*) INTO inbound_exists FROM information_schema.TABLES + WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='ll_storage_rent_inbound'; + -- 已存在的同名表必须完整匹配;不以 IF NOT EXISTS 掩盖半成品或异构表。 + IF inbound_exists>0 THEN + IF (SELECT COUNT(*) FROM information_schema.TABLES + WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='ll_storage_rent_inbound' + AND TABLE_TYPE='BASE TABLE' AND ENGINE='InnoDB' AND TABLE_COLLATION='utf8mb4_0900_ai_ci')<>1 + OR (SELECT COUNT(*) FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='ll_storage_rent_inbound')<>4 + OR (SELECT COUNT(*) FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='ll_storage_rent_inbound' + AND IS_NULLABLE='NO' AND COLUMN_DEFAULT IS NULL AND EXTRA='' + AND ((COLUMN_NAME IN ('receipt_id','lot_id') AND COLUMN_TYPE='bigint') + OR (COLUMN_NAME='quantity' AND COLUMN_TYPE='decimal(24,12)') + OR (COLUMN_NAME='created_at' AND COLUMN_TYPE='datetime(3)')))<>4 THEN + SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='已存在的计租实收表字段与正式定义不同,停止且不覆盖'; + END IF; + IF (SELECT COUNT(*) FROM information_schema.STATISTICS + WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='ll_storage_rent_inbound' AND INDEX_NAME='PRIMARY')<>1 + OR (SELECT COUNT(*) FROM information_schema.STATISTICS + WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='ll_storage_rent_inbound' AND INDEX_NAME='PRIMARY' + AND COLUMN_NAME='receipt_id' AND SEQ_IN_INDEX=1 AND NON_UNIQUE=0 AND SUB_PART IS NULL)<>1 + OR (SELECT COUNT(*) FROM information_schema.STATISTICS + WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='ll_storage_rent_inbound' + AND INDEX_NAME='idx_storage_rent_inbound_lot')<>1 + OR (SELECT COUNT(*) FROM information_schema.STATISTICS + WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='ll_storage_rent_inbound' + AND INDEX_NAME='idx_storage_rent_inbound_lot' AND COLUMN_NAME='lot_id' + AND SEQ_IN_INDEX=1 AND NON_UNIQUE=1 AND SUB_PART IS NULL)<>1 THEN + SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='计租实收表主键或批次索引不匹配,停止且不覆盖'; + END IF; + END IF; + + -- 全部前置检查通过后才补缺项;重跑时保持已存在的业务结构和数据。 + IF reviewer_exists=0 THEN + ALTER TABLE ll_finance_fee_source + ADD COLUMN reviewer_name VARCHAR(255) NULL COMMENT '审核人名称快照' AFTER reviewer_id; + END IF; + IF inbound_exists=0 THEN + CREATE TABLE ll_storage_rent_inbound ( + receipt_id bigint NOT NULL COMMENT '实际入库凭证主键,防止重复累计计租数量', + lot_id bigint NOT NULL COMMENT '头程同单同仓计租批次主键', + quantity decimal(24,12) NOT NULL COMMENT '本次实收追加计租箱数', + created_at datetime(3) NOT NULL COMMENT '计租登记时间,采用协调世界时', + PRIMARY KEY (receipt_id), + KEY idx_storage_rent_inbound_lot (lot_id) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='头程实收计租幂等登记'; + END IF; +END$$ +DELIMITER ; +CALL overseas_fee_rent_structure_add_20261026(); +DROP PROCEDURE overseas_fee_rent_structure_add_20261026; + +SHOW CREATE TABLE ll_storage_rent_inbound; +SELECT COLUMN_NAME,COLUMN_TYPE,IS_NULLABLE,COLUMN_DEFAULT +FROM information_schema.COLUMNS +WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='ll_finance_fee_source' AND COLUMN_NAME='reviewer_name'; +SELECT 'TABLE_COLUMN_READY' AS structure_status; +-- TABLE_COLUMN_READY 只表示上述表/字段补齐;免费仓租另执行已审阅的 +-- overseas-storage-rent-zero-fee-repair.sql,不能据此宣称完整功能上线。 diff --git a/deploy/sql/overseas-order-budget-and-procurement-rejection.sql b/deploy/sql/overseas-order-budget-and-procurement-rejection.sql new file mode 100644 index 000000000..c078d8d00 --- /dev/null +++ b/deploy/sql/overseas-order-budget-and-procurement-rejection.sql @@ -0,0 +1,29 @@ +-- 2026-10-29:采购驳回照片返工及客户下单预估冻结;仅在Lastleg所属库执行。 +SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_overseas_procurement_order' AND column_name='customer_rejection_reason'), 'SELECT 1', 'ALTER TABLE ll_overseas_procurement_order ADD COLUMN customer_rejection_reason VARCHAR(1000) NULL COMMENT ''客户采购结果驳回原因''')); +PREPARE stmt FROM @ddl; +EXECUTE stmt; +DEALLOCATE PREPARE stmt; +SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_overseas_procurement_order' AND column_name='rejected_photo_ids'), 'SELECT 1', 'ALTER TABLE ll_overseas_procurement_order ADD COLUMN rejected_photo_ids TEXT NULL COMMENT ''本轮客户驳回的收货照片主键快照''')); +PREPARE stmt FROM @ddl; +EXECUTE stmt; +DEALLOCATE PREPARE stmt; +CREATE TABLE IF NOT EXISTS ll_order_budget_reservation ( + id BIGINT NOT NULL COMMENT '预算冻结记录主键', + customer_id VARCHAR(64) NOT NULL COMMENT '客户隔离标识', + kind VARCHAR(32) NOT NULL COMMENT '客户下单业务类型', + request_key VARCHAR(128) NOT NULL COMMENT '下单幂等键', + order_no VARCHAR(64) NULL COMMENT '成功创建的业务单号', + original_amount DECIMAL(18,2) NOT NULL COMMENT '客户预估原币金额', + currency VARCHAR(3) NOT NULL COMMENT '预估金额币种', + amount_cny DECIMAL(18,2) NOT NULL COMMENT '按下单月份汇率折算的人民币冻结金额', + status VARCHAR(32) NOT NULL COMMENT '资金冻结或释放回执状态', + error_message VARCHAR(1000) NULL COMMENT '资金失败原因', + command_event_id VARCHAR(64) NULL COMMENT '当前资金指令事件标识', + command_version BIGINT NOT NULL DEFAULT 0 COMMENT '资金指令单调版本', + release_requested TINYINT NOT NULL DEFAULT 0 COMMENT '是否已请求释放预占', + created_at DATETIME(3) NOT NULL COMMENT '首次预占时间', + updated_at DATETIME(3) NOT NULL COMMENT '最近资金状态更新时间', + PRIMARY KEY(id), UNIQUE KEY uk_order_budget_request(customer_id,kind,request_key), + KEY idx_order_budget_release(status,release_requested,created_at), + KEY idx_order_budget_order(customer_id,kind,order_no) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='海外客户下单预估资金冻结投影'; diff --git a/deploy/sql/overseas-procurement-legacy-tables-retire.sql b/deploy/sql/overseas-procurement-legacy-tables-retire.sql new file mode 100644 index 000000000..f7a806dac --- /dev/null +++ b/deploy/sql/overseas-procurement-legacy-tables-retire.sql @@ -0,0 +1,102 @@ +-- 海外采购旧流程专属表退役,MySQL 8.0+,目标仅 baiyue_lastleg。 +-- 必须先停止旧版 Lastleg 写入并部署本次分离版本,验证新流程后在维护窗口执行。 +-- 永久删除下面明确列出的 21 张旧采购表及其记录,DDL 隐式提交;禁止 mysql --force。 +-- 保留主单、审核流水、采购附件、分批收货、出库及幂等六表;不改任何共用财务、资金、文件或仓储表。 +-- 旧资金表有记录或旧状态采购未处理时拒绝,不自动清除、回填或伪造资金完成。 +-- 运行者须在同一连接显式 SET @procurement_retirement_release='procurement-six-tables-20261027'; +-- 文件准备不表示数据库已执行。先审核备份及运行版本,按清单整份执行,遇错停止。 +USE baiyue_lastleg; +DELIMITER $$ +DROP PROCEDURE IF EXISTS retire_procurement_legacy_tables$$ +CREATE PROCEDURE retire_procurement_legacy_tables() +BEGIN + IF DATABASE()<>'baiyue_lastleg' OR COALESCE(@procurement_retirement_release,'')<>'procurement-six-tables-20261027' THEN + SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='Confirm the six-table procurement release and target schema first'; + END IF; + IF (SELECT COUNT(*) FROM information_schema.tables WHERE table_schema=DATABASE() AND table_name IN ( + 'll_overseas_procurement_order','ll_overseas_procurement_status_history','ll_overseas_procurement_file', + 'll_procurement_workflow_receipt','ll_procurement_workflow_outbound','ll_procurement_workflow_command'))<>6 THEN + SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='Six active procurement tables must exist before retiring legacy tables'; + END IF; + IF EXISTS(SELECT 1 FROM ll_overseas_procurement_order WHERE status NOT IN + ('PENDING_REVIEW','RECEIVING','WAIT_CUSTOMER_CONFIRM','CONFIRMED','IN_TRANSIT','DELIVERING','COMPLETED','REJECTED','CANCELLED')) THEN + SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='Legacy procurement orders remain; resolve them before retirement'; + END IF; + -- 仅只读核对共用资金记录;不删除资金命令或回执,不把已投递当作已结清。 + IF EXISTS(SELECT 1 FROM ll_event_outbox WHERE aggregate_type='PROCUREMENT_FEE') + OR EXISTS(SELECT 1 FROM ll_wallet_event_inbox + WHERE JSON_UNQUOTE(JSON_EXTRACT(payload_json,'$.payload.businessType'))='PROCUREMENT_FEE') THEN + SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='Legacy procurement wallet events exist; financial review is required'; + END IF; + IF EXISTS(SELECT 1 FROM information_schema.tables WHERE table_schema=DATABASE() AND table_name='ll_overseas_procurement_fee') THEN + SET @procurement_retirement_financial_rows=0; + SET @procurement_retirement_check_sql='SELECT COUNT(*) INTO @procurement_retirement_financial_rows FROM baiyue_lastleg.ll_overseas_procurement_fee'; + PREPARE procurement_retirement_check FROM @procurement_retirement_check_sql; + EXECUTE procurement_retirement_check; + DEALLOCATE PREPARE procurement_retirement_check; + IF @procurement_retirement_financial_rows<>0 THEN + SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='Legacy procurement financial records remain; do not delete financial evidence'; + END IF; + END IF; + IF EXISTS(SELECT 1 FROM information_schema.tables WHERE table_schema=DATABASE() AND table_name='ll_procurement_wallet_attempt') THEN + SET @procurement_retirement_financial_rows=0; + SET @procurement_retirement_check_sql='SELECT COUNT(*) INTO @procurement_retirement_financial_rows FROM baiyue_lastleg.ll_procurement_wallet_attempt'; + PREPARE procurement_retirement_check FROM @procurement_retirement_check_sql; + EXECUTE procurement_retirement_check; + DEALLOCATE PREPARE procurement_retirement_check; + IF @procurement_retirement_financial_rows<>0 THEN + SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='Legacy procurement financial records remain; do not delete financial evidence'; + END IF; + END IF; + -- 不关闭外键校验;来自其他业务表的外键也会使这条精确删除失败。 + DROP TABLE IF EXISTS + ll_procurement_closure_item, + ll_procurement_closure, + ll_procurement_control_note, + ll_procurement_fee_correction_item, + ll_procurement_fee_correction, + ll_procurement_repack, + ll_procurement_estimate_review, + ll_procurement_label_item, + ll_procurement_label_set, + ll_procurement_shipment_item, + ll_procurement_shipment, + ll_procurement_carton_source, + ll_procurement_carton, + ll_procurement_manifest, + ll_procurement_disposition, + ll_procurement_receipt, + ll_procurement_budget_authorization, + ll_procurement_command, + ll_procurement_flow, + ll_procurement_wallet_attempt, + ll_overseas_procurement_fee; +END$$ +CALL retire_procurement_legacy_tables()$$ +DROP PROCEDURE retire_procurement_legacy_tables$$ +DELIMITER ; + +-- 若上面任一步失败必须停止。剩余旧表数应为 0;此 SELECT 不代替客户端退出码核对。 +SELECT COUNT(*) AS remaining_legacy_procurement_tables +FROM information_schema.tables WHERE table_schema='baiyue_lastleg' AND table_name IN ( +'ll_overseas_procurement_fee', +'ll_procurement_wallet_attempt', +'ll_procurement_flow', +'ll_procurement_command', +'ll_procurement_budget_authorization', +'ll_procurement_receipt', +'ll_procurement_disposition', +'ll_procurement_manifest', +'ll_procurement_carton', +'ll_procurement_carton_source', +'ll_procurement_shipment', +'ll_procurement_shipment_item', +'ll_procurement_label_set', +'ll_procurement_label_item', +'ll_procurement_estimate_review', +'ll_procurement_repack', +'ll_procurement_fee_correction', +'ll_procurement_fee_correction_item', +'ll_procurement_control_note', +'ll_procurement_closure', +'ll_procurement_closure_item'); diff --git a/deploy/sql/overseas-procurement-table-cleanup-review.sql b/deploy/sql/overseas-procurement-table-cleanup-review.sql new file mode 100644 index 000000000..706da9357 --- /dev/null +++ b/deploy/sql/overseas-procurement-table-cleanup-review.sql @@ -0,0 +1,38 @@ +-- 海外采购退役表只读核查;2026-10-27 分离版本。MySQL 8.0+,仅查询,不自动执行删除。 +-- 6 张当前业务表保留;21 张旧采购专属表已从生产代码和初始化基线移除。 +-- 只有先部署分离版本并通过 legacy-tables-retire.sql 前置校验后,才可执行精确删表清单。 +-- 元数据行数是估算;actual_count_sql 供逐表只读核对,不自动执行。 +WITH inventory AS ( + SELECT 'll_overseas_procurement_order' AS table_name, 'KEEP' AS disposition + UNION ALL SELECT 'll_overseas_procurement_status_history' AS table_name, 'KEEP' AS disposition + UNION ALL SELECT 'll_overseas_procurement_file' AS table_name, 'KEEP' AS disposition + UNION ALL SELECT 'll_procurement_workflow_receipt' AS table_name, 'KEEP' AS disposition + UNION ALL SELECT 'll_procurement_workflow_outbound' AS table_name, 'KEEP' AS disposition + UNION ALL SELECT 'll_procurement_workflow_command' AS table_name, 'KEEP' AS disposition + UNION ALL SELECT 'll_overseas_procurement_fee' AS table_name, 'RETIRED_RELEASE_REQUIRED' AS disposition + UNION ALL SELECT 'll_procurement_wallet_attempt' AS table_name, 'RETIRED_RELEASE_REQUIRED' AS disposition + UNION ALL SELECT 'll_procurement_flow' AS table_name, 'RETIRED_RELEASE_REQUIRED' AS disposition + UNION ALL SELECT 'll_procurement_command' AS table_name, 'RETIRED_RELEASE_REQUIRED' AS disposition + UNION ALL SELECT 'll_procurement_budget_authorization' AS table_name, 'RETIRED_RELEASE_REQUIRED' AS disposition + UNION ALL SELECT 'll_procurement_receipt' AS table_name, 'RETIRED_RELEASE_REQUIRED' AS disposition + UNION ALL SELECT 'll_procurement_disposition' AS table_name, 'RETIRED_RELEASE_REQUIRED' AS disposition + UNION ALL SELECT 'll_procurement_manifest' AS table_name, 'RETIRED_RELEASE_REQUIRED' AS disposition + UNION ALL SELECT 'll_procurement_carton' AS table_name, 'RETIRED_RELEASE_REQUIRED' AS disposition + UNION ALL SELECT 'll_procurement_carton_source' AS table_name, 'RETIRED_RELEASE_REQUIRED' AS disposition + UNION ALL SELECT 'll_procurement_shipment' AS table_name, 'RETIRED_RELEASE_REQUIRED' AS disposition + UNION ALL SELECT 'll_procurement_shipment_item' AS table_name, 'RETIRED_RELEASE_REQUIRED' AS disposition + UNION ALL SELECT 'll_procurement_label_set' AS table_name, 'RETIRED_RELEASE_REQUIRED' AS disposition + UNION ALL SELECT 'll_procurement_label_item' AS table_name, 'RETIRED_RELEASE_REQUIRED' AS disposition + UNION ALL SELECT 'll_procurement_estimate_review' AS table_name, 'RETIRED_RELEASE_REQUIRED' AS disposition + UNION ALL SELECT 'll_procurement_repack' AS table_name, 'RETIRED_RELEASE_REQUIRED' AS disposition + UNION ALL SELECT 'll_procurement_fee_correction' AS table_name, 'RETIRED_RELEASE_REQUIRED' AS disposition + UNION ALL SELECT 'll_procurement_fee_correction_item' AS table_name, 'RETIRED_RELEASE_REQUIRED' AS disposition + UNION ALL SELECT 'll_procurement_control_note' AS table_name, 'RETIRED_RELEASE_REQUIRED' AS disposition + UNION ALL SELECT 'll_procurement_closure' AS table_name, 'RETIRED_RELEASE_REQUIRED' AS disposition + UNION ALL SELECT 'll_procurement_closure_item' AS table_name, 'RETIRED_RELEASE_REQUIRED' AS disposition +) +SELECT i.table_name,i.disposition,CASE WHEN t.table_name IS NULL THEN 'MISSING' ELSE 'EXISTS' END AS database_state, + t.table_rows AS table_rows_estimate, + CASE WHEN t.table_name IS NOT NULL THEN CONCAT('SELECT COUNT(*) AS actual_rows FROM baiyue_lastleg.',i.table_name,';') END AS actual_count_sql +FROM inventory i LEFT JOIN information_schema.tables t ON t.table_schema='baiyue_lastleg' AND t.table_name=i.table_name +ORDER BY i.disposition,i.table_name; diff --git a/deploy/sql/overseas-procurement-test-data-reset.sql b/deploy/sql/overseas-procurement-test-data-reset.sql new file mode 100644 index 000000000..f835f402f --- /dev/null +++ b/deploy/sql/overseas-procurement-test-data-reset.sql @@ -0,0 +1,65 @@ +-- 仅用于用户确认可丢弃数据的海外采购测试环境,MySQL 8.0+。 +-- 清空六张当前采购表的数据,删除二十一张旧采购表及其数据。 +-- 仅作用于 baiyue_lastleg 的采购专属表;不修改共用费用、余额、文件对象及库存。 +-- 共用表中已有的采购关联记录不会自动删除或退款;本脚本不用于财务结算。 +-- 先更新分离后的后端并停止采购写入,整份执行,遇错停止,不使用 --force。 +-- 无存储过程、无 DELIMITER、无需设置会话确认变量;DDL 隐式提交。 +USE baiyue_lastleg; +START TRANSACTION; +DELETE FROM ll_procurement_workflow_command; +DELETE FROM ll_procurement_workflow_outbound; +DELETE FROM ll_procurement_workflow_receipt; +DELETE FROM ll_overseas_procurement_file; +DELETE FROM ll_overseas_procurement_status_history; +DELETE FROM ll_overseas_procurement_order; +COMMIT; + +DROP TABLE IF EXISTS + ll_procurement_closure_item, + ll_procurement_closure, + ll_procurement_control_note, + ll_procurement_fee_correction_item, + ll_procurement_fee_correction, + ll_procurement_repack, + ll_procurement_estimate_review, + ll_procurement_label_item, + ll_procurement_label_set, + ll_procurement_shipment_item, + ll_procurement_shipment, + ll_procurement_carton_source, + ll_procurement_carton, + ll_procurement_manifest, + ll_procurement_disposition, + ll_procurement_receipt, + ll_procurement_budget_authorization, + ll_procurement_command, + ll_procurement_flow, + ll_procurement_wallet_attempt, + ll_overseas_procurement_fee; + +SELECT 'procurement_test_reset_complete' AS result, + (SELECT COUNT(*) FROM ll_overseas_procurement_order) AS procurement_orders, + (SELECT COUNT(*) FROM information_schema.tables + WHERE table_schema='baiyue_lastleg' AND table_name IN ( + 'll_overseas_procurement_fee', + 'll_procurement_wallet_attempt', + 'll_procurement_flow', + 'll_procurement_command', + 'll_procurement_budget_authorization', + 'll_procurement_receipt', + 'll_procurement_disposition', + 'll_procurement_manifest', + 'll_procurement_carton', + 'll_procurement_carton_source', + 'll_procurement_shipment', + 'll_procurement_shipment_item', + 'll_procurement_label_set', + 'll_procurement_label_item', + 'll_procurement_estimate_review', + 'll_procurement_repack', + 'll_procurement_fee_correction', + 'll_procurement_fee_correction_item', + 'll_procurement_control_note', + 'll_procurement_closure', + 'll_procurement_closure_item' + )) AS remaining_legacy_procurement_tables; diff --git a/deploy/sql/overseas-procurement-workflow.sql b/deploy/sql/overseas-procurement-workflow.sql new file mode 100644 index 000000000..a6e908273 --- /dev/null +++ b/deploy/sql/overseas-procurement-workflow.sql @@ -0,0 +1,36 @@ +-- 简化采购独立批次表;只增加结构,不回填旧采购记录,执行需另行授权。 +USE baiyue_lastleg; +-- 简化采购:独立采购库存,收货、占用和签收扣减均由订单锁串行控制。 +CREATE TABLE IF NOT EXISTS ll_procurement_workflow_receipt ( + id BIGINT NOT NULL PRIMARY KEY COMMENT '批次主键', + order_id BIGINT NOT NULL COMMENT '采购单主键', + quantity BIGINT NOT NULL COMMENT '本批次数量', + file_ids TEXT NOT NULL COMMENT '本批次关联凭证文件主键列表', + received_at DATETIME(3) NOT NULL COMMENT '本批次实收时间', + created_by VARCHAR(128) NOT NULL COMMENT '作业人标识', + KEY idx_procurement_workflow_receipt (order_id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='采购分批收货照片与实收数量'; + +CREATE TABLE IF NOT EXISTS ll_procurement_workflow_outbound ( + id BIGINT NOT NULL PRIMARY KEY COMMENT '批次主键', + order_id BIGINT NOT NULL COMMENT '采购单主键', + outbound_no VARCHAR(64) NOT NULL COMMENT '采购出库单号', + quantity BIGINT NOT NULL COMMENT '本批次数量', + tracking_no VARCHAR(255) NOT NULL COMMENT '物流跟踪号', + status VARCHAR(32) NOT NULL COMMENT '批次运输派送及签收状态', + file_ids TEXT NOT NULL COMMENT '本批次关联凭证文件主键列表', + created_at DATETIME(3) NOT NULL COMMENT '记录创建时间', + completed_at DATETIME(3) NULL COMMENT '本批次签收完成时间', + created_by VARCHAR(128) NOT NULL COMMENT '作业人标识', + UNIQUE KEY uk_procurement_workflow_outbound_no (outbound_no), + KEY idx_procurement_workflow_outbound (order_id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='采购分批出库物流与签收凭证'; + +CREATE TABLE IF NOT EXISTS ll_procurement_workflow_command ( + order_id BIGINT NOT NULL COMMENT '采购单主键', + command_key VARCHAR(128) NOT NULL COMMENT '本单操作幂等键', + request_hash VARCHAR(64) NOT NULL COMMENT '动作及请求参数哈希', + operator_id VARCHAR(128) NOT NULL COMMENT '作业人标识', + created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '记录创建时间', + PRIMARY KEY (order_id,command_key) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='采购操作幂等及审计'; diff --git a/deploy/sql/overseas-storage-rent-zero-fee-repair.sql b/deploy/sql/overseas-storage-rent-zero-fee-repair.sql new file mode 100644 index 000000000..904fe09bc --- /dev/null +++ b/deploy/sql/overseas-storage-rent-zero-fee-repair.sql @@ -0,0 +1,73 @@ +-- 固定目标经单独授权的 TEST 或本机开发 baiyue_lastleg,只修复自动免费仓租结构,不更新业务数据。 +-- 使用独立连接、遇错停止;先核对既有表结构,不能重放空库初始化。 +USE baiyue_lastleg; +SELECT COLUMN_NAME,COLUMN_TYPE,IS_NULLABLE FROM information_schema.COLUMNS +WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='ll_finance_fee_source' + AND COLUMN_NAME IN ('amount','original_amount','exchange_rate','created_by','fee_item_code','direction'); + +-- 上次 CALL 前置检查失败可能遗留本专用过程,仅清理本脚本自己的过程后重建。 +DROP PROCEDURE IF EXISTS storage_rent_zero_fee_repair_20261023; +DELIMITER $$ +CREATE PROCEDURE storage_rent_zero_fee_repair_20261023() +BEGIN + DECLARE checked_columns INT DEFAULT 0; + DECLARE checked_constraints INT DEFAULT 0; + DECLARE rate_type VARCHAR(64); + DECLARE rate_comment TEXT; + DECLARE rate_default TEXT; + DECLARE rate_default_sql TEXT; + IF DATABASE()<>'baiyue_lastleg' THEN + SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='目标必须为 baiyue_lastleg'; + END IF; + SELECT COUNT(*) INTO checked_columns FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='ll_finance_fee_source' + AND ((COLUMN_NAME IN ('amount','original_amount') AND DATA_TYPE='decimal' AND IS_NULLABLE='NO') + OR (COLUMN_NAME='exchange_rate' AND DATA_TYPE='decimal' AND EXTRA='') + OR (COLUMN_NAME IN ('created_by','fee_item_code','direction') AND DATA_TYPE='varchar' AND IS_NULLABLE='NO')); + SELECT COUNT(*) INTO checked_constraints FROM information_schema.TABLE_CONSTRAINTS + WHERE CONSTRAINT_SCHEMA=DATABASE() AND TABLE_NAME='ll_finance_fee_source' + AND CONSTRAINT_NAME='chk_ll_finance_fee_amount' AND CONSTRAINT_TYPE='CHECK'; + IF checked_columns<>6 OR checked_constraints<>1 THEN + SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='费用源表字段或金额约束异常,停止修复'; + END IF; + -- 增加新约束前只读检查;人工费用仍须正额且有效汇率,防止意外放宽。 + IF EXISTS (SELECT 1 FROM ll_finance_fee_source + WHERE NOT ((amount>0 OR (amount=0 AND original_amount=0 AND created_by='STORAGE_RENT' AND fee_item_code='FEE_TYPE_077' AND direction='RECEIVABLE')) + AND ((exchange_rate IS NOT NULL AND exchange_rate>0) OR (exchange_rate IS NULL AND amount=0 AND original_amount=0 AND created_by='STORAGE_RENT' AND fee_item_code='FEE_TYPE_077' AND direction='RECEIVABLE')))) THEN + SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='存在不满足新约束的费用,停止且不改数据'; + END IF; + SELECT COUNT(*) INTO checked_constraints FROM information_schema.TABLE_CONSTRAINTS + WHERE CONSTRAINT_SCHEMA=DATABASE() AND TABLE_NAME='ll_finance_fee_source' + AND CONSTRAINT_NAME='chk_ll_finance_fee_exchange_rate' AND CONSTRAINT_TYPE='CHECK'; + IF checked_constraints=0 THEN + -- 保留真实已有列的精度和备注,不把存量 DECIMAL(18,8) 收窄到基线精度。 + SELECT COLUMN_TYPE,COLUMN_COMMENT,COLUMN_DEFAULT INTO rate_type,rate_comment,rate_default + FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='ll_finance_fee_source' AND COLUMN_NAME='exchange_rate'; + IF rate_type NOT REGEXP '^decimal[(][0-9]+,[0-9]+[)]$' THEN + SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='汇率列不是预期定点数类型,停止修复'; + END IF; + IF LOCATE('自动零元仓租可空',rate_comment)=0 THEN + SET rate_comment=CONCAT(rate_comment,',自动零元仓租可空'); + END IF; + -- 保留既有数值默认值(例如1.00000000);拒绝表达式或非数值默认值。 + IF rate_default IS NOT NULL AND rate_default NOT REGEXP '^-?[0-9]+([.][0-9]+)?$' THEN + SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='汇率默认值不是预期数值,停止修复'; + END IF; + SET rate_default_sql=IF(rate_default IS NULL,' DEFAULT NULL',CONCAT(' DEFAULT ',rate_default)); + -- 单次原子 ALTER 同步调整可空性与两项约束,原币及结算列完全不改。 + SET @storage_rent_zero_fee_ddl=CONCAT( + 'ALTER TABLE baiyue_lastleg.ll_finance_fee_source DROP CHECK chk_ll_finance_fee_amount, ', + 'MODIFY COLUMN exchange_rate ',rate_type,' NULL',rate_default_sql,' COMMENT ',QUOTE(rate_comment),', ', + 'ADD CONSTRAINT chk_ll_finance_fee_amount CHECK(amount>0 OR (amount=0 AND original_amount=0 AND created_by=''STORAGE_RENT'' AND fee_item_code=''FEE_TYPE_077'' AND direction=''RECEIVABLE'')), ', + 'ADD CONSTRAINT chk_ll_finance_fee_exchange_rate CHECK((exchange_rate IS NOT NULL AND exchange_rate>0) OR (exchange_rate IS NULL AND amount=0 AND original_amount=0 AND created_by=''STORAGE_RENT'' AND fee_item_code=''FEE_TYPE_077'' AND direction=''RECEIVABLE''))'); + PREPARE storage_rent_zero_fee_stmt FROM @storage_rent_zero_fee_ddl; + EXECUTE storage_rent_zero_fee_stmt; + DEALLOCATE PREPARE storage_rent_zero_fee_stmt; + SET @storage_rent_zero_fee_ddl=NULL; + END IF; +END$$ +DELIMITER ; +CALL storage_rent_zero_fee_repair_20261023(); +DROP PROCEDURE storage_rent_zero_fee_repair_20261023; +SHOW CREATE TABLE ll_finance_fee_source; diff --git a/deploy/sql/reset-test-fba-appointments.sql b/deploy/sql/reset-test-fba-appointments.sql index 647947332..b8f1fc257 100644 --- a/deploy/sql/reset-test-fba-appointments.sql +++ b/deploy/sql/reset-test-fba-appointments.sql @@ -52,7 +52,7 @@ CREATE TABLE ll_inbound_fba_appointment ( UNIQUE KEY uk_ll_fba_appointment_no (appointment_no), KEY idx_ll_fba_appointment_list (appointment_at,appointment_status,fba_warehouse_code), KEY idx_ll_fba_appointment_planned (planned_at,appointment_status) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='FBA预约安排与正式确认唯一主记录'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='FBA预约安排与正式确认唯一主记录'; CREATE TABLE ll_inbound_fba_appointment_item ( id BIGINT NOT NULL COMMENT '记录主键', @@ -74,7 +74,7 @@ CREATE TABLE ll_inbound_fba_appointment_item ( KEY idx_ll_fba_active_order (customer_id,order_id,active), KEY idx_ll_fba_item_source_lookup (source_key,customer_id), KEY idx_ll_fba_item_customer_order (customer_id,order_id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='入仓前后共用的预约真实货源'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='入仓前后共用的预约真实货源'; CREATE TABLE ll_appointment_cargo_verification ( id BIGINT NOT NULL COMMENT '核实记录主键', @@ -97,7 +97,7 @@ CREATE TABLE ll_appointment_cargo_verification ( UNIQUE KEY uk_ll_cargo_verification_command (customer_id,source_key,command_key), CONSTRAINT chk_ll_cargo_verification_quantity CHECK (package_count IS NULL OR package_count > 0), CONSTRAINT chk_ll_cargo_verification_version CHECK (version > 0) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='预约货物核实资料及不可覆盖的版本历史'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='预约货物核实资料及不可覆盖的版本历史'; CREATE TABLE ll_fba_appointment_command ( command_key VARCHAR(128) NOT NULL COMMENT '持久化幂等键', @@ -109,4 +109,4 @@ CREATE TABLE ll_fba_appointment_command ( created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建UTC时间', PRIMARY KEY(command_key), KEY idx_ll_fba_command_appointment(appointment_no,created_at) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='预约命令与变更历史'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='预约命令与变更历史'; diff --git a/deploy/sql/return-storage-rent-whole-pallet.sql b/deploy/sql/return-storage-rent-whole-pallet.sql new file mode 100644 index 000000000..e0e4a0ca3 --- /dev/null +++ b/deploy/sql/return-storage-rent-whole-pallet.sql @@ -0,0 +1,13 @@ +-- 在 Lastleg 所属库执行;只补充整托计租截止日,不改写既有费用、库存、冻结或资金流水。 +-- 发布新服务前暂停仓租出库,取消未消费预占;旧比例费用的处理需按业务单核对后单独办理。 +USE baiyue_lastleg; +SET @return_rent_ddl = ( + SELECT IF(EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = DATABASE() AND table_name = 'll_storage_rent_lot' AND column_name = 'charged_days' + ), 'SELECT 1', + 'ALTER TABLE ll_storage_rent_lot ADD COLUMN charged_days BIGINT NOT NULL DEFAULT 0 COMMENT ''退货原托已生成仓租的截止库龄天数,同一天不重复计费'' AFTER original_units') +); +PREPARE return_rent_statement FROM @return_rent_ddl; +EXECUTE return_rent_statement; +DEALLOCATE PREPARE return_rent_statement; diff --git a/deploy/sql/warehouse-supplier-query-structure-repair.sql b/deploy/sql/warehouse-supplier-query-structure-repair.sql new file mode 100644 index 000000000..41cff3c89 --- /dev/null +++ b/deploy/sql/warehouse-supplier-query-structure-repair.sql @@ -0,0 +1,40 @@ +-- 供应商查询缺失结构修复。MySQL 8;仅用于本次明确授权的本机开发库。 +-- 固定目标 baiyue_warehouse;不修改供应商、费用、余额或授信数据。 +-- 已有结构安全跳过;不推断历史拖车供应商的国内/海外类别。 +-- DDL 会隐式提交,请在独立连接以遇错停止模式整份执行。 +USE baiyue_warehouse; +SET NAMES utf8mb4; +SELECT DATABASE() AS target_database; + +-- 先确认正式供应商主表存在;不存在时 ALTER 会报错并停止,不创建第二份主档。 +SET @supplier_query_column_ddl = IF( + EXISTS (SELECT 1 FROM information_schema.columns + WHERE table_schema = 'baiyue_warehouse' + AND table_name = 'wh_supplier' AND column_name = 'trucking_scope'), + 'SELECT ''trucking_scope 已存在,跳过'' AS structure_result', + 'ALTER TABLE baiyue_warehouse.wh_supplier ADD COLUMN trucking_scope VARCHAR(16) NULL COMMENT ''拖车类型:DOMESTIC国内拖车、OVERSEAS海外车队;非拖车为空'' AFTER category' +); +PREPARE supplier_query_statement FROM @supplier_query_column_ddl; +EXECUTE supplier_query_statement; +DEALLOCATE PREPARE supplier_query_statement; + +-- 正式列表还会读取船司关联;若未初始化,仅按当前模块基线建立空表。 +CREATE TABLE IF NOT EXISTS wh_supplier_carrier ( + supplier_id BIGINT UNSIGNED NOT NULL COMMENT 'BOOKING供应商ID', + tenant_id BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '平台公共作用域,固定为0', + carrier_code VARCHAR(16) NOT NULL COMMENT '船司标准编码', + created_by BIGINT UNSIGNED NULL COMMENT '创建人ID', + created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间', + PRIMARY KEY (supplier_id,carrier_code), + KEY idx_wh_supplier_carrier_code (tenant_id,carrier_code,supplier_id), + CONSTRAINT chk_wh_supplier_carrier_tenant CHECK (tenant_id = 0), + CONSTRAINT chk_wh_supplier_carrier_code CHECK (carrier_code IN ('MATSON','SML','ZIM','COSCO','EMC','HEDE','OTHER')) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='BOOKING供应商与标准船司编码多对多关联'; + +-- 执行后只核对结构,不读取供应商联系信息。 +SELECT table_name, column_name, column_type, is_nullable +FROM information_schema.columns +WHERE table_schema = 'baiyue_warehouse' + AND ((table_name = 'wh_supplier' AND column_name = 'trucking_scope') + OR table_name = 'wh_supplier_carrier') +ORDER BY table_name, ordinal_position; diff --git a/deploy/sql/头程数据库统一增量-MySQL8.sql b/deploy/sql/头程数据库统一增量-MySQL8.sql index 8d6c1cedb..b966afba7 100644 --- a/deploy/sql/头程数据库统一增量-MySQL8.sql +++ b/deploy/sql/头程数据库统一增量-MySQL8.sql @@ -1,4 +1,5 @@ -- CN 当前纯空库初始化入口(文件名沿用原入口,不提供增量升级语义)。 +-- 海外费用菜单及平台管理员授权统一在本文件 IAM 种子中维护,不再单列本次版本或专项菜单脚本。 -- MySQL 8.0+;只允许目标测试环境的服务 schema 为空,已有表立即报错;禁止 --force/遇错继续。 -- 字段、索引、约束及注释直接定义在 CREATE TABLE 内;不回填、不清理、不改造存量。 -- 国内 IAM 管理两区域权限;海外不访问国内库。执行方法及系统种子清单见同目录 README.md。 @@ -3092,7 +3093,7 @@ CREATE TABLE fl_loading_cabinet_evidence ( seal_no varchar(64) DEFAULT NULL COMMENT '带锁码照对应锁码', remark varchar(1000) DEFAULT NULL COMMENT '证据备注', status varchar(16) NOT NULL DEFAULT 'ACTIVE' COMMENT '证据状态:ACTIVE有效、VOID作废', - active_flag tinyint unsigned DEFAULT '1' COMMENT '有效记录为1,作废记录为NULL,保证每柜每类型仅一条有效事实', + active_flag tinyint unsigned DEFAULT '1' COMMENT '有效记录为1,作废记录为NULL,同类允许多张有效照片', operation_key varchar(128) NOT NULL COMMENT '任务范围内幂等操作键', request_hash char(64) NOT NULL COMMENT '规范化请求SHA-256摘要', void_reason varchar(500) DEFAULT NULL COMMENT '作废原因', @@ -3108,7 +3109,7 @@ CREATE TABLE fl_loading_cabinet_evidence ( updated_at datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '最后更新时间', PRIMARY KEY (id), UNIQUE KEY uk_cabinet_evidence_operation (source_task_id,operation_key), - UNIQUE KEY uk_cabinet_evidence_active (container_booking_id,evidence_type,active_flag), + KEY idx_cabinet_evidence_active (container_booking_id,evidence_type,active_flag), KEY idx_cabinet_evidence_container (container_booking_id,status), KEY idx_cabinet_evidence_seal (seal_no,active_flag), CONSTRAINT fk_cabinet_evidence_booking FOREIGN KEY (container_booking_id) REFERENCES fl_container_booking (id), @@ -3852,6 +3853,8 @@ CREATE TABLE fl_carrier_schedule_node_fact ( location_name varchar(128) DEFAULT NULL COMMENT '节点地点名称', source_reference varchar(500) DEFAULT NULL COMMENT '可审计来源引用', raw_payload_hash char(64) NOT NULL COMMENT '原始响应SHA-256;不保存敏感原文', + operator_id bigint unsigned DEFAULT NULL COMMENT '节点操作人账号ID;自动采集时为空', + operator_name varchar(128) DEFAULT NULL COMMENT '节点操作人用户名快照;自动采集时为空', created_at datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '事实入库时间', PRIMARY KEY (id), UNIQUE KEY uk_carrier_schedule_node_event (container_booking_id,event_key), @@ -4141,7 +4144,7 @@ CREATE TABLE fl_direct_delivery_unloading ( PRIMARY KEY (container_booking_id,inbound_id), UNIQUE KEY uk_direct_unloading_command (container_booking_id,idempotency_key), CONSTRAINT fk_direct_unloading_container FOREIGN KEY (container_booking_id) REFERENCES fl_container_booking(id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='直送目的仓真实卸货凭据'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='直送目的仓真实卸货凭据'; CREATE TABLE fl_terminal_drayage ( @@ -4762,7 +4765,8 @@ CREATE TABLE fin_bill_line ( currency_code char(3) NOT NULL DEFAULT 'CNY' COMMENT 'ISO 4217三位币种代码,首期仅CNY', created_at datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '账单明细创建时间', PRIMARY KEY (id), - UNIQUE KEY uk_fin_bill_line_source (source_type,source_id,source_version), + UNIQUE KEY uk_fin_bill_line_bill_source (bill_id,source_type,source_id,source_version), + KEY idx_fin_bill_line_source (source_type,source_id,source_version), KEY idx_fin_bill_line_bill (tenant_id,bill_id), CONSTRAINT chk_fin_bill_line_amount CHECK ((amount >= 0)), CONSTRAINT chk_fin_bill_line_currency CHECK ((currency_code = _utf8mb4'CNY')), @@ -5043,6 +5047,7 @@ CREATE TABLE fin_settlement ( due_date date NOT NULL COMMENT '结算到期日期', payment_status varchar(24) NOT NULL DEFAULT 'UNPAID' COMMENT '收付款状态:UNPAID未收付、PARTIALLY_PAID部分收付、PAID已结清', status varchar(24) NOT NULL DEFAULT 'ISSUED' COMMENT '结算状态:ISSUED已生效、PARTIALLY_PAID部分收付、PAID已结清、VOIDED已作废', + active_receivable_statement_id bigint unsigned GENERATED ALWAYS AS (CASE WHEN direction='RECEIVABLE' AND status<>'VOIDED' THEN reconciliation_statement_id ELSE NULL END) VIRTUAL COMMENT '有效应收占用的对账单;作废单不占用,历史关联仍保留', version int unsigned NOT NULL DEFAULT '0' COMMENT '乐观锁版本号', created_by bigint unsigned NOT NULL COMMENT '创建操作人ID', updated_by bigint unsigned NOT NULL COMMENT '最后修改操作人ID', @@ -5050,7 +5055,7 @@ CREATE TABLE fin_settlement ( updated_at datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '最后更新时间', PRIMARY KEY (id), UNIQUE KEY uk_fin_settlement_no (tenant_id,settlement_no), - UNIQUE KEY uk_fin_settlement_receivable_statement (tenant_id,reconciliation_statement_id,direction), + UNIQUE KEY uk_fin_settlement_active_receivable (tenant_id,active_receivable_statement_id), KEY idx_fin_settlement_customer_direction_status (tenant_id,direction,status), KEY idx_fin_settlement_supplier_status (supplier_id,direction,status), KEY idx_fin_settlement_receivable_statistics (direction,currency_code,generated_date,status,tenant_id), @@ -8065,7 +8070,7 @@ CREATE TABLE fl_sailing_channel ( PRIMARY KEY (id), UNIQUE KEY uk_fl_sailing_channel_code (channel_code), KEY idx_fl_sailing_channel_status (status) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; CREATE TABLE fl_sailing_template ( id BIGINT NOT NULL, @@ -8101,7 +8106,7 @@ CREATE TABLE fl_sailing_template ( KEY idx_fl_sailing_template_filter (warehouse_id,channel_id,status,weekday), CONSTRAINT ck_fl_sailing_template_weekday CHECK (weekday BETWEEN 1 AND 7), CONSTRAINT ck_fl_sailing_template_cutoff_days CHECK (cutoff_days BETWEEN 0 AND 30) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; CREATE TABLE fl_sailing ( id BIGINT NOT NULL, @@ -8138,7 +8143,7 @@ CREATE TABLE fl_sailing ( KEY idx_fl_sailing_calendar (etd_at,warehouse_id,channel_id,status), KEY idx_fl_sailing_route (departure_port_code,destination_port_code), CONSTRAINT ck_fl_sailing_status CHECK (status IN ('DRAFT','OPEN','CLOSED','DEPARTED','CANCELLED','COMPLETED')) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; CREATE TABLE fl_sailing_history ( id BIGINT NOT NULL, @@ -8152,7 +8157,7 @@ CREATE TABLE fl_sailing_history ( created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), PRIMARY KEY (id), KEY idx_fl_sailing_history_sailing (sailing_id,created_at,id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; USE baiyue_iam; @@ -8448,7 +8453,7 @@ CREATE TABLE ord_document_sequence ( current_value BIGINT NOT NULL COMMENT '该前缀当日已分配流水,已分配号不回收', PRIMARY KEY (sequence_key), CONSTRAINT chk_ord_document_sequence_value CHECK (current_value > 0) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='业务单据每日独立原子流水'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='业务单据每日独立原子流水'; USE baiyue_warehouse; CREATE TABLE wh_document_sequence ( @@ -8456,6 +8461,24 @@ CREATE TABLE wh_document_sequence ( current_value BIGINT NOT NULL COMMENT '该前缀当日已分配流水,已分配号不回收', PRIMARY KEY (sequence_key), CONSTRAINT chk_wh_document_sequence_value CHECK (current_value > 0) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='业务单据每日独立原子流水'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='业务单据每日独立原子流水'; +-- 海外费用查询菜单;编辑、审核继续使用独立财务按钮授权。 +USE baiyue_iam; +INSERT INTO iam_permission (id,tenant_id,app_code,permission_type,parent_id,permission_code,permission_name,route_path,http_method,sort_no,status,built_in,created_at,updated_at,deleted) VALUES (5117200,0,'PLATFORM','MENU',5105000,'platform:overseas-fee:list','海外费用明细','/overseas-business/fees','GET',60,'ACTIVE',1,CURRENT_TIMESTAMP(3),CURRENT_TIMESTAMP(3),0); +INSERT INTO iam_role_permission (role_id,permission_id,tenant_id,created_at) VALUES (4000001,5117200,0,CURRENT_TIMESTAMP(3)); +USE baiyue_iam; +-- 散板需求沿用既有菜单权限,补全当前名称和添加货物独立按钮。 +INSERT INTO iam_permission (id,tenant_id,app_code,permission_type,parent_id,permission_code,permission_name,route_path,http_method,sort_no,status,built_in,created_at,updated_at,deleted) +SELECT 5107410,0,'PLATFORM','MENU',0,'platform:fleet:capacity-offer:list','散板需求','/overseas-master-data/capacity-offers','GET',1040,'ACTIVE',1,CURRENT_TIMESTAMP(3),CURRENT_TIMESTAMP(3),0 +WHERE NOT EXISTS (SELECT 1 FROM iam_permission WHERE permission_code='platform:fleet:capacity-offer:list'); +UPDATE iam_permission SET permission_name='散板需求',status='ACTIVE',deleted=0,updated_at=CURRENT_TIMESTAMP(3) WHERE permission_code='platform:fleet:capacity-offer:list'; +INSERT INTO iam_permission (id,tenant_id,app_code,permission_type,parent_id,permission_code,permission_name,route_path,http_method,sort_no,status,built_in,created_at,updated_at,deleted) +SELECT 5107415,0,'PLATFORM','BUTTON',p.id,'platform:fleet:capacity-offer:cargo-add','加入散板货物',NULL,'POST',1045,'ACTIVE',1,CURRENT_TIMESTAMP(3),CURRENT_TIMESTAMP(3),0 FROM iam_permission p +WHERE p.permission_code='platform:fleet:capacity-offer:list' AND NOT EXISTS (SELECT 1 FROM iam_permission WHERE permission_code='platform:fleet:capacity-offer:cargo-add'); +INSERT INTO iam_role_permission (role_id,permission_id,tenant_id,created_at) +SELECT 4000001,p.id,0,CURRENT_TIMESTAMP(3) FROM iam_permission p WHERE p.permission_code IN ('platform:fleet:capacity-offer:list','platform:fleet:capacity-offer:cargo-add') AND NOT EXISTS (SELECT 1 FROM iam_role_permission rp WHERE rp.role_id=4000001 AND rp.permission_id=p.id); +-- 上门取货复用正式排车创建入口,仅补齐既有平台管理员角色的必要权限。 +INSERT INTO iam_role_permission (role_id,permission_id,tenant_id,created_at) +SELECT 4000001,p.id,0,CURRENT_TIMESTAMP(3) FROM iam_permission p WHERE p.permission_code='platform:overseas-warehouse:dispatch:create' AND p.status='ACTIVE' AND p.deleted=0 AND NOT EXISTS (SELECT 1 FROM iam_role_permission rp WHERE rp.role_id=4000001 AND rp.permission_id=p.id); SELECT 'PASS' AS initialization_status, 'CN' AS region, 229 AS initialized_tables; diff --git a/deploy/sql/海外区域统一增量-MySQL8.sql b/deploy/sql/海外区域统一增量-MySQL8.sql index 5235e5091..70f42583d 100644 --- a/deploy/sql/海外区域统一增量-MySQL8.sql +++ b/deploy/sql/海外区域统一增量-MySQL8.sql @@ -1,4 +1,5 @@ -- OVERSEAS 当前纯空库初始化入口(文件名沿用原入口,不提供增量升级语义)。 +-- 海外费用资金、仓租四表、审核人名称快照及简化采购六表均在本文件最终建表定义中维护。 -- MySQL 8.0+;只允许目标测试环境的服务 schema 为空,已有表立即报错;禁止 --force/遇错继续。 -- 字段、索引、约束及注释直接定义在 CREATE TABLE 内;不回填、不清理、不改造存量。 -- 国内 IAM 管理两区域权限;海外不访问国内库。执行方法及系统种子清单见同目录 README.md。 @@ -7,30 +8,31 @@ SET NAMES utf8mb4 COLLATE utf8mb4_0900_ai_ci; SET SESSION sql_mode = 'ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION'; -- 服务 schema:baiyue_lastleg -CREATE DATABASE IF NOT EXISTS baiyue_lastleg DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; +CREATE DATABASE IF NOT EXISTS baiyue_lastleg DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; USE baiyue_lastleg; CREATE TABLE ll_cargo_discard_application ( id bigint NOT NULL COMMENT '主键ID', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户租户ID', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户租户ID', customer_confirmed_at datetime(3) DEFAULT NULL COMMENT '客户确认完成时间', - discard_no varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '废弃申请单号', - overseas_warehouse_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '海外仓ID', - overseas_warehouse_name varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '海外仓名称快照', - inbound_codes varchar(2000) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '入仓单号集合', + discard_no varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '废弃申请单号', + overseas_warehouse_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '海外仓ID', + overseas_warehouse_name varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '海外仓名称快照', + inbound_codes varchar(2000) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '入仓单号集合', total_packages int NOT NULL COMMENT '总箱数', fee_per_package decimal(18,2) NOT NULL COMMENT '每箱处理费用', total_fee decimal(18,2) NOT NULL COMMENT '费用合计', - currency char(3) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'USD' COMMENT '币种', - status varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'PENDING_REVIEW' COMMENT '业务状态', - remark varchar(1000) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '备注', - idempotency_key varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '业务幂等键', - request_hash char(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '请求内容哈希', - created_by varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '创建人ID', - reviewed_by varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '审核人ID', - review_remark varchar(1000) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '审核备注', - review_idempotency_key varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '审核幂等键', - review_request_hash char(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '审核请求内容哈希', + currency char(3) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'USD' COMMENT '币种', + status varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'PENDING_REVIEW' COMMENT '业务状态', + remark varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '备注', + idempotency_key varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '业务幂等键', + request_hash char(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '请求内容哈希', + created_by varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '创建人ID', + created_by_name varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '申请人姓名快照', + reviewed_by varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '审核人ID', + review_remark varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '审核备注', + review_idempotency_key varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '审核幂等键', + review_request_hash char(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '审核请求内容哈希', reviewed_at datetime(3) DEFAULT 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 '更新时间', @@ -39,44 +41,45 @@ CREATE TABLE ll_cargo_discard_application ( UNIQUE KEY uk_ll_cargo_discard_key (customer_id,idempotency_key), KEY idx_ll_cargo_discard_list (customer_id,status,created_at), KEY idx_ll_cargo_discard_warehouse (customer_id,overseas_warehouse_id,created_at) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='货物废弃申请单'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='货物废弃申请单'; CREATE TABLE ll_cargo_discard_carton ( id bigint NOT NULL COMMENT '主键ID', application_id bigint NOT NULL COMMENT '申请单ID', item_id bigint NOT NULL COMMENT '明细ID', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户租户ID', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户租户ID', carton_id bigint NOT NULL COMMENT '箱唛记录ID', - carton_mark varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '箱唛', + carton_mark varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '箱唛', source_type VARCHAR(32) NOT NULL DEFAULT 'DROPSHIP' COMMENT '箱子来源:一件代发DROPSHIP、入仓箱INBOUND、转运箱TRANSFER', created_at datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间', PRIMARY KEY (id), UNIQUE KEY uk_ll_cargo_discard_carton (application_id,carton_id), KEY idx_ll_cargo_discard_carton_application (customer_id,application_id,item_id), KEY idx_ll_cargo_discard_carton_identity (customer_id,carton_id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='货物废弃锁定箱明细'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='货物废弃锁定箱明细'; CREATE TABLE ll_cargo_discard_item ( id bigint NOT NULL COMMENT '主键ID', application_id bigint NOT NULL COMMENT '申请单ID', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户租户ID', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户租户ID', order_id bigint DEFAULT NULL COMMENT '一件代发订单ID', - source_type varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'DROPSHIP' COMMENT '库存来源:一件代发DROPSHIP、退货箱RETURN_CARTON、通用库存INVENTORY', + source_type varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'DROPSHIP' COMMENT '库存来源:一件代发DROPSHIP、退货箱RETURN_CARTON、通用库存INVENTORY', return_carton_id bigint DEFAULT NULL COMMENT '退货库存箱ID', - inbound_code varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '入仓单号快照', + inbound_code varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '入仓单号快照', profile_id bigint DEFAULT NULL COMMENT '托盘档案ID', sku_inventory_id bigint DEFAULT NULL COMMENT 'SKU库存记录ID', - sku_code varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'SKU编码', - product_name varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '产品名称', + sku_code varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'SKU编码', + product_name varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '产品名称', inventory_position_id BIGINT NULL COMMENT '废弃来源库存仓位ID', carton_marks_json TEXT NULL COMMENT '本次废弃所选真实箱唛快照JSON数组', carton_scan_required TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否需要逐箱核对本次所选箱子', + actual_carton_marks_json JSON NULL COMMENT 'PDA现场实际废弃箱唛清单,与客户选箱快照独立保存', available_packages_snapshot int NOT NULL COMMENT '可用箱数快照', discard_packages int NOT NULL COMMENT '申请废弃箱数', - completion_photo_file_ids text COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'PDA完工照片文件ID JSON数组', + completion_photo_file_ids text COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'PDA完工照片文件ID JSON数组', actual_discard_packages int DEFAULT NULL COMMENT 'PDA确认实际废弃箱数', - pda_status varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'PENDING' COMMENT 'PDA处理状态:待处理PENDING、已完成COMPLETED', - pda_operator_id varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'PDA操作人', + pda_status varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'PENDING' COMMENT 'PDA处理状态:待处理PENDING、已完成COMPLETED', + pda_operator_id varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'PDA操作人', pda_completed_at datetime(3) DEFAULT NULL COMMENT 'PDA完成时间', created_at datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间', PRIMARY KEY (id), @@ -85,116 +88,116 @@ CREATE TABLE ll_cargo_discard_item ( KEY idx_ll_cargo_discard_item_list (customer_id,application_id), KEY idx_ll_cargo_discard_item_inbound (customer_id,inbound_code,sku_code) ,KEY idx_ll_cargo_discard_return_carton (customer_id,return_carton_id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='货物废弃SKU明细'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='货物废弃SKU明细'; CREATE TABLE ll_cargo_discard_status_history ( id bigint NOT NULL COMMENT '主键ID', application_id bigint NOT NULL COMMENT '申请单ID', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户租户ID', - from_status varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '变更前状态', - to_status varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '变更后状态', - operator_id varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '操作人ID', - remark varchar(1000) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '备注', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户租户ID', + from_status varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '变更前状态', + to_status varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '变更后状态', + operator_id varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '操作人ID', + remark varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '备注', created_at datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间', PRIMARY KEY (id), KEY idx_ll_cargo_discard_history (customer_id,application_id,created_at) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='货物废弃审批历史'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='货物废弃审批历史'; CREATE TABLE ll_container_arrival_time_operation ( id bigint NOT NULL COMMENT '柜级预计到仓时间修改操作ID', - source_container_booking_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '稳定来源柜预订ID', - idempotency_key varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '平台请求幂等键', - request_hash varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '规范化修改请求SHA-256摘要', + source_container_booking_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '稳定来源柜预订ID', + idempotency_key varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '平台请求幂等键', + request_hash varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '规范化修改请求SHA-256摘要', result_version int NOT NULL COMMENT '修改成功后的柜级时间版本', - operator_name varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '平台操作人名称', - remark varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '预计到仓时间修改备注', + operator_name varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '平台操作人名称', + remark varchar(500) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '预计到仓时间修改备注', created_at datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '操作时间', PRIMARY KEY (id), UNIQUE KEY uk_ll_container_arrival_time_idempotency (idempotency_key), KEY idx_ll_container_arrival_time_container (source_container_booking_id,created_at) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='柜级预计到仓时间修改幂等审计记录'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='柜级预计到仓时间修改幂等审计记录'; CREATE TABLE ll_container_loading_evidence ( id bigint NOT NULL COMMENT '海外整柜装柜照片投影ID', - source_container_booking_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '国内稳定来源柜预订ID', - evidence_type varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'PHOTO_STEP_1至PHOTO_STEP_4或SEAL', + source_container_booking_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '国内稳定来源柜预订ID', + evidence_type varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'PHOTO_STEP_1至PHOTO_STEP_4或SEAL', source_file_id bigint NOT NULL COMMENT '国内File服务文件ID', - file_name varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '国内文件名快照', + file_name varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '国内文件名快照', created_at datetime(3) NOT NULL COMMENT '投影创建时间', PRIMARY KEY (id), UNIQUE KEY uk_ll_container_loading_evidence_type (source_container_booking_id,evidence_type), UNIQUE KEY uk_ll_container_loading_evidence_file (source_container_booking_id,source_file_id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='海外拆柜只读国内装柜照片引用'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='海外拆柜只读国内装柜照片引用'; CREATE TABLE ll_container_plan_conversion_command ( id bigint NOT NULL COMMENT '柜级转换命令ID', - source_container_booking_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '稳定来源柜预订ID', - idempotency_key varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '调用方幂等键', - request_hash char(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '确认请求SHA-256摘要', + source_container_booking_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '稳定来源柜预订ID', + idempotency_key varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '调用方幂等键', + request_hash char(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '确认请求SHA-256摘要', snapshot_version bigint NOT NULL COMMENT '确认的柜级快照版本', - snapshot_hash char(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '确认的柜级快照SHA-256', + snapshot_hash char(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '确认的柜级快照SHA-256', warehouse_id bigint NOT NULL COMMENT '海外接柜仓ID', - batch_key varchar(160) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '海外仓托盘批次幂等键', - status varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '命令状态:PREPARED/APPLYING/FAILED/LOCAL_COMMITTED/COMPLETED', - lease_token varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '当前执行租约令牌,用于陈旧执行者隔离', + batch_key varchar(160) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '海外仓托盘批次幂等键', + status varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '命令状态:PREPARED/APPLYING/FAILED/LOCAL_COMMITTED/COMPLETED', + lease_token varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '当前执行租约令牌,用于陈旧执行者隔离', preparation_json json NOT NULL COMMENT '稳定计划号与托盘业务键快照', result_json json DEFAULT NULL COMMENT '本地计划提交结果', - last_error varchar(1000) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '最近一次跨服务错误', + last_error varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT 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_ll_container_conversion_command (source_container_booking_id), UNIQUE KEY uk_ll_container_conversion_batch (warehouse_id,batch_key), KEY idx_ll_container_conversion_status (status,updated_at) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='柜级入库计划转换Saga与幂等命令'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='柜级入库计划转换Saga与幂等命令'; CREATE TABLE ll_container_shipping_gate ( - container_booking_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '来源头程柜预订ID', - ocean_booking_id varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '来源海运订舱ID;货柜未关联订舱时为空', - container_no varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '实际集装箱号', - status varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'AWAITING_PICKUP_READY' COMMENT '海外收货门禁状态:AWAITING_PICKUP_READY、RECEIVING_AVAILABLE', - standard_node varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '最新有效标准船节点', + container_booking_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '来源头程柜预订ID', + ocean_booking_id varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '来源海运订舱ID;货柜未关联订舱时为空', + container_no varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '实际集装箱号', + status varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'AWAITING_PICKUP_READY' COMMENT '海外收货门禁状态:AWAITING_PICKUP_READY、RECEIVING_AVAILABLE', + standard_node varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '最新有效标准船节点', node_sequence int NOT NULL DEFAULT '0' COMMENT '最新有效节点单调序号', observed_at datetime(3) DEFAULT NULL COMMENT '供应商节点观测时间', actual_at datetime(3) DEFAULT NULL COMMENT '节点实际发生时间', - location_code varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '节点地点编码', - location_name varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '节点地点名称', - provider_code varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '船节点供应商编码', - provider_event_id varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '供应商事件ID', - provider_node_code varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '供应商原始节点编码', - source_reference varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '供应商来源引用', - raw_payload_hash varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '供应商原始负载SHA-256', + location_code varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '节点地点编码', + location_name varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '节点地点名称', + provider_code varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '船节点供应商编码', + provider_event_id varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '供应商事件ID', + provider_node_code varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '供应商原始节点编码', + source_reference varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '供应商来源引用', + raw_payload_hash varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '供应商原始负载SHA-256', aggregate_version bigint NOT NULL DEFAULT '0' COMMENT '来源查询聚合版本', handover_version bigint NOT NULL DEFAULT '1' COMMENT '跨区交接版本', - last_event_id varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '最近采纳的跨区事件ID', - payload_hash varchar(71) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '最近采纳事件规范化负载摘要', + last_event_id varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '最近采纳的跨区事件ID', + payload_hash varchar(71) COLLATE utf8mb4_0900_ai_ci DEFAULT 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 (container_booking_id), KEY idx_ll_shipping_gate_version (container_booking_id,aggregate_version,node_sequence), KEY idx_ll_shipping_gate_candidate (container_no,status) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='海外PDA柜号船节点收货资格快照'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='海外PDA柜号船节点收货资格快照'; CREATE TABLE ll_customer_visit ( id bigint NOT NULL COMMENT '客户回访记录ID', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户数据隔离键', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户数据隔离键', delivery_order_id bigint NOT NULL COMMENT '关联派送单ID', - delivery_no varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '关联派送单号', - customer_name varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '客户名称快照', - contact_name varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '客户联系人姓名快照', - contact_phone varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '客户联系人电话快照', - destination varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '派送目的仓编码汇总快照', + delivery_no varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '关联派送单号', + customer_name varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '客户名称快照', + contact_name varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '客户联系人姓名快照', + contact_phone varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '客户联系人电话快照', + destination varchar(500) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '派送目的仓编码汇总快照', delivery_completed_at datetime(3) NOT NULL COMMENT '派送完成时间', - status varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'PENDING' COMMENT '回访状态:PENDING待回访或COMPLETED已回访', + status varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'PENDING' COMMENT '回访状态:PENDING待回访或COMPLETED已回访', selected_last_mile_order_id bigint DEFAULT NULL COMMENT '本次回访选择的尾程订单ID', - selected_last_mile_order_no varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '本次回访选择的尾程订单号', + selected_last_mile_order_no varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '本次回访选择的尾程订单号', selected_inbound_order_id bigint DEFAULT NULL COMMENT '本次回访选择的入仓单ID', - selected_inbound_code varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '本次回访选择的入仓码', - visit_result varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '最近一次回访结果', - customer_feedback varchar(2000) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '最近一次客户反馈', - remark varchar(1000) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '最近一次回访备注', + selected_inbound_code varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '本次回访选择的入仓码', + visit_result varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '最近一次回访结果', + customer_feedback varchar(2000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '最近一次客户反馈', + remark varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '最近一次回访备注', attachment_json json DEFAULT NULL COMMENT '当前回访资料文件元数据列表', - visited_by varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '最近一次回访操作人ID', + visited_by varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '最近一次回访操作人ID', visited_at datetime(3) DEFAULT NULL COMMENT '最近一次回访时间', visit_count int NOT NULL DEFAULT '0' COMMENT '累计保存回访次数', created_at datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间', @@ -203,71 +206,71 @@ CREATE TABLE ll_customer_visit ( UNIQUE KEY uk_ll_customer_visit_delivery (customer_id,delivery_order_id), KEY idx_ll_customer_visit_list (customer_id,status,delivery_completed_at), KEY idx_ll_customer_visit_delivery_no (customer_id,delivery_no) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='派送单维度客户回访记录'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='派送单维度客户回访记录'; CREATE TABLE ll_delivery_document_revision ( id bigint NOT NULL COMMENT '派送资料历史ID', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '派送所属客户隔离键', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '派送所属客户隔离键', delivery_order_id bigint NOT NULL COMMENT '派送单ID', version bigint NOT NULL COMMENT '保存成功时派送单版本', - idempotency_key varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '资料保存幂等键', - request_hash varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '完整保存请求哈希', + idempotency_key varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '资料保存幂等键', + request_hash varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '完整保存请求哈希', snapshot_json json NOT NULL COMMENT '不可变三类资料及BOL号结果快照', - operator_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '保存操作人ID', + operator_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '保存操作人ID', created_at datetime(3) NOT NULL COMMENT '资料保存时间', PRIMARY KEY (id), UNIQUE KEY uk_delivery_document_command (customer_id,delivery_order_id,idempotency_key), UNIQUE KEY uk_delivery_document_version (customer_id,delivery_order_id,version) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='派送资料版本历史与幂等保存记录'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='派送资料版本历史与幂等保存记录'; CREATE TABLE ll_delivery_operation ( id bigint NOT NULL COMMENT '派送操作主键', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户数据隔离键', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户数据隔离键', delivery_order_id bigint NOT NULL COMMENT '派送单ID', - operation_node varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '操作节点', - operator_id varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '操作人ID', - operator_name varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '操作人名称', - result varchar(500) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '处理结果', + operation_node varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '操作节点', + operator_id varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '操作人ID', + operator_name varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '操作人名称', + result varchar(500) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '处理结果', created_at datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '操作时间', PRIMARY KEY (id), KEY idx_ll_delivery_operation_order (customer_id,delivery_order_id,created_at,id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='派送单操作记录'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='派送单操作记录'; CREATE TABLE ll_delivery_order ( id bigint NOT NULL COMMENT '派送单主键ID', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户数据隔离键', - delivery_no varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '派送单号', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户数据隔离键', + delivery_no varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '派送单号', outbound_order_id bigint NOT NULL COMMENT '关联出库单ID', - outbound_no varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '关联出库单号', + outbound_no varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '关联出库单号', dispatch_order_id bigint DEFAULT NULL COMMENT '关联派车单ID,仓储转运直接出库时为空', - dispatch_no varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '关联派车单号,仓储转运直接出库时为空', - overseas_warehouse_id varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '海外仓ID', - overseas_warehouse_name varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '海外仓名称快照', - customer_name varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '客户名称快照', - destination_warehouse_id varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '本派送单目的FBA仓ID快照', - destination_warehouse_code varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '本派送单目的FBA仓编码快照,私人仓为空', - destination_warehouse_name varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '本派送单目的FBA仓名称快照', - destination_type varchar(16) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '目的地类型:FBA或PRIVATE', - delivery_address varchar(1000) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '私人仓派送地址', - postal_code varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '私人仓邮编', - contact_name varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '私人仓联系人', - contact_phone varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '私人仓联系电话', - delivery_group_key varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '同一出库单内按目的FBA仓拆单的稳定分组键', - status varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'WAIT_DELIVERY' COMMENT '派送状态:WAIT_DELIVERY待派送及后续正式状态', - waybill_no varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '承运运单号', - bol_no varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '派送资料BOL号,不作为文件归属标识', + dispatch_no varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '关联派车单号,仓储转运直接出库时为空', + overseas_warehouse_id varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '海外仓ID', + overseas_warehouse_name varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '海外仓名称快照', + customer_name varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '客户名称快照', + destination_warehouse_id varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '本派送单目的FBA仓ID快照', + destination_warehouse_code varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '本派送单目的FBA仓编码快照,私人仓为空', + destination_warehouse_name varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '本派送单目的FBA仓名称快照', + destination_type varchar(16) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '目的地类型:FBA或PRIVATE', + delivery_address varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '私人仓派送地址', + postal_code varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '私人仓邮编', + contact_name varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '私人仓联系人', + contact_phone varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '私人仓联系电话', + delivery_group_key varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '同一出库单内按目的FBA仓拆单的稳定分组键', + status varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'WAIT_DELIVERY' COMMENT '派送状态:WAIT_DELIVERY待派送及后续正式状态', + waybill_no varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '承运运单号', + bol_no varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '派送资料BOL号,不作为文件归属标识', documents_json json DEFAULT NULL COMMENT '派送三类资料当前绑定及权威文件元数据快照', - delivery_method varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '派送方式', - carrier_name varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '派送承运商', - pickup_method varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '提货方式', - loading_type varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '装车类型', - vehicle_type varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '车辆类型', - vehicle_size varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '车辆大小', - vehicle_plate varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '车牌号', - driver_name varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '司机或车队名称', - driver_phone varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '车队联系电话', - appointment_account varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '预约账号', - seller_name varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '卖家姓名', + delivery_method varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '派送方式', + carrier_name varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '派送承运商', + pickup_method varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '提货方式', + loading_type varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '装车类型', + vehicle_type varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '车辆类型', + vehicle_size varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '车辆大小', + vehicle_plate varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '车牌号', + driver_name varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '司机或车队名称', + driver_phone varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '车队联系电话', + appointment_account varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '预约账号', + seller_name varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '卖家姓名', package_count int NOT NULL DEFAULT '0' COMMENT '派送箱数', pallet_count int NOT NULL DEFAULT '0' COMMENT '派送托盘数', total_weight_kg DECIMAL(18,4) NULL COMMENT '总重量(千克),任一来源未确认时为空', @@ -276,9 +279,9 @@ CREATE TABLE ll_delivery_order ( dispatched_at datetime(3) DEFAULT NULL COMMENT '实际派送时间', expected_arrival_at datetime(3) DEFAULT NULL COMMENT '预计到达时间', signed_at datetime(3) DEFAULT NULL COMMENT '签收时间', - remark varchar(1000) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '派送备注', - idempotency_key varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '业务幂等键', - created_by varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '创建人ID', + remark varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '派送备注', + idempotency_key varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '业务幂等键', + created_by varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '创建人ID', version bigint NOT NULL DEFAULT '0' 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 '更新时间', @@ -290,48 +293,48 @@ CREATE TABLE ll_delivery_order ( UNIQUE KEY uk_ll_delivery_idempotency (customer_id,idempotency_key), UNIQUE KEY uk_ll_delivery_outbound_destination (customer_id,outbound_order_id,delivery_group_key), KEY idx_ll_delivery_list (customer_id,status,created_at) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='尾程派送单'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='尾程派送单'; CREATE TABLE ll_destuff_pallet_cost ( id bigint NOT NULL COMMENT '主键ID', - source_container_booking_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '来源柜次业务ID', - container_no varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '集装箱号', + source_container_booking_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '来源柜次业务ID', + container_no varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '集装箱号', warehouse_id bigint DEFAULT NULL COMMENT '海外仓ID', pallet_count int NOT NULL COMMENT '实际打托数', total_actual_volume_cbm decimal(20,6) NOT NULL COMMENT '客户实收总体积', total_cost decimal(20,2) NOT NULL COMMENT '打托总成本', - currency char(3) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'CNY' COMMENT '成本币种', - status varchar(16) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '生成状态', + currency char(3) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'CNY' COMMENT '成本币种', + status varchar(16) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '生成状态', created_at datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间', PRIMARY KEY (id), UNIQUE KEY uk_ll_destuff_cost_booking (source_container_booking_id), CONSTRAINT chk_ll_destuff_cost CHECK (((pallet_count > 0) and (total_actual_volume_cbm > 0) and (total_cost > 0) and (currency = _utf8mb4'CNY'))) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='拆柜打托实际成本汇总'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='拆柜打托实际成本汇总'; CREATE TABLE ll_destuff_pallet_cost_allocation ( id bigint NOT NULL COMMENT '主键ID', cost_id bigint NOT NULL COMMENT '打托成本汇总ID', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户ID', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户ID', actual_volume_cbm decimal(20,6) NOT NULL COMMENT '客户实收体积', allocated_amount decimal(20,2) NOT NULL COMMENT '客户分摊金额', - fee_source_id bigint NOT NULL COMMENT '海外费用来源ID', + fee_source_id bigint DEFAULT NULL COMMENT '人工审核后关联的海外费用来源ID', created_at datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间', PRIMARY KEY (id), UNIQUE KEY uk_ll_destuff_customer (cost_id,customer_id), UNIQUE KEY uk_ll_destuff_fee_source (fee_source_id), CONSTRAINT chk_ll_destuff_allocation CHECK (((actual_volume_cbm > 0) and (allocated_amount >= 0))) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='拆柜打托成本按客户实收体积分摊'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='拆柜打托成本按客户实收体积分摊'; CREATE TABLE ll_dispatch_draft ( id bigint NOT NULL COMMENT '主键ID', - draft_token varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '智能排车草稿随机标识', - operator_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '草稿拥有者身份,仅本人可维护及恢复命令', + draft_token varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '智能排车草稿随机标识', + operator_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '草稿拥有者身份,仅本人可维护及恢复命令', warehouse_id BIGINT NOT NULL COMMENT '本草稿唯一起运公共仓库引用', record_version BIGINT NOT NULL DEFAULT 0 COMMENT '草稿并发版本,显式零版本合法', sources_json LONGTEXT NOT NULL COMMENT '服务端核验的真实来源及客户版本快照数组', - plan_ids_json longtext COLLATE utf8mb4_unicode_ci COMMENT '所选入仓计划ID列表JSON,容纳最多五百个完整长整型引用', - containers_json longtext COLLATE utf8mb4_unicode_ci COMMENT '草稿装车布局JSON快照', - status varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'ACTIVE' COMMENT '草稿状态:ACTIVE有效、RELEASED释放、CONVERTED已整批转换', + plan_ids_json longtext COLLATE utf8mb4_0900_ai_ci COMMENT '所选入仓计划ID列表JSON,容纳最多五百个完整长整型引用', + containers_json longtext COLLATE utf8mb4_0900_ai_ci COMMENT '草稿装车布局JSON快照', + status varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'ACTIVE' COMMENT '草稿状态:ACTIVE有效、RELEASED释放、CONVERTED已整批转换', expires_at datetime(3) DEFAULT NULL COMMENT '草稿失效时间', released_at datetime(3) DEFAULT NULL COMMENT '草稿释放时间', created_at datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间', @@ -339,12 +342,12 @@ CREATE TABLE ll_dispatch_draft ( PRIMARY KEY (id), UNIQUE KEY uk_ll_dispatch_draft_token (draft_token), KEY idx_ll_dispatch_draft_operator (operator_id,status) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='智能排车草稿'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='智能排车草稿'; CREATE TABLE ll_dispatch_draft_container ( id bigint NOT NULL COMMENT '主键ID', draft_id bigint NOT NULL COMMENT '智能排车草稿ID', - draft_token varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '智能排车草稿随机标识', + draft_token varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '智能排车草稿随机标识', sequence_no int NOT NULL COMMENT '草稿车厢顺序号', capacity int NOT NULL COMMENT '草稿车厢托盘容量', source_keys_json LONGTEXT NOT NULL COMMENT '本装车容器已取得本草稿占用的来源键数组', @@ -353,7 +356,7 @@ CREATE TABLE ll_dispatch_draft_container ( PRIMARY KEY (id), UNIQUE KEY uk_ll_dispatch_draft_container_sequence (draft_id,sequence_no), KEY idx_ll_dispatch_draft_container_token (draft_token) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='智能排车草稿装车容器,每个非空容器转换一张整车单'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='智能排车草稿装车容器,每个非空容器转换一张整车单'; CREATE TABLE ll_dispatch_draft_command ( id BIGINT NOT NULL COMMENT '草稿命令内部主键', @@ -366,7 +369,7 @@ CREATE TABLE ll_dispatch_draft_command ( created_at DATETIME(3) NOT NULL COMMENT '命令创建UTC时间', PRIMARY KEY (id), UNIQUE KEY uk_ll_dispatch_draft_command_key (idempotency_key) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='智能排车草稿持久命令及原结果'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='智能排车草稿持久命令及原结果'; CREATE TABLE ll_dispatch_item ( customer_name VARCHAR(255) NULL COMMENT '本条真实客户名称快照', @@ -411,7 +414,7 @@ CREATE TABLE ll_dispatch_item ( KEY idx_ll_dispatch_item_order (customer_id,order_id), KEY idx_ll_dispatch_item_inbound (customer_id,inbound_code), KEY idx_ll_dispatch_item_pallet (customer_id,plan_pallet_id,warehouse_pallet_id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='派车货物冻结明细'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='派车货物冻结明细'; CREATE TABLE ll_dispatch_order ( id BIGINT NOT NULL COMMENT '整车排车主单内部主键', @@ -459,7 +462,7 @@ CREATE TABLE ll_dispatch_order ( UNIQUE KEY uk_dispatch_trip (trip_id), KEY idx_ll_dispatch_order_list (overseas_warehouse_id,status,stage,created_at,id), KEY idx_ll_dispatch_order_vehicle (vehicle_plate,status,created_at) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='平台整车排车作业主单,客户归属在真实明细'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='平台整车排车作业主单,客户归属在真实明细'; CREATE TABLE ll_dispatch_history ( id BIGINT NOT NULL COMMENT '排车历史内部主键', @@ -472,7 +475,7 @@ CREATE TABLE ll_dispatch_history ( occurred_at DATETIME(3) NOT NULL COMMENT '真实操作发生时间', PRIMARY KEY (id), KEY idx_dispatch_history_version (dispatch_id,record_version,id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='整车排车版本与审核轮次的追加历史'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='整车排车版本与审核轮次的追加历史'; CREATE TABLE ll_dispatch_item_source ( id BIGINT NOT NULL COMMENT '逻辑运输来源贡献快照主键', @@ -501,13 +504,13 @@ CREATE TABLE ll_dispatch_item_source ( KEY idx_dispatch_item_source_relabel (customer_id,relabel_application_id,relabel_item_id), CONSTRAINT chk_dispatch_item_source_packages CHECK(notified_package_count>=0 AND package_count>0), CONSTRAINT chk_dispatch_item_source_quantity CHECK(product_quantity IS NULL OR product_quantity>=0) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='换标逻辑来源实际贡献不可变快照'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='换标逻辑来源实际贡献不可变快照'; CREATE TABLE ll_physical_pallet_guard ( warehouse_pallet_id BIGINT NOT NULL COMMENT '真实全局物理托盘ID', created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '首次建立串行点时间', PRIMARY KEY (warehouse_pallet_id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='排车冻结与仓内实托组成修改的共同串行点'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='排车冻结与仓内实托组成修改的共同串行点'; CREATE TABLE ll_dispatch_physical_pallet_lock ( id BIGINT NOT NULL COMMENT '物理实托占用主键', @@ -530,7 +533,7 @@ CREATE TABLE ll_dispatch_physical_pallet_lock ( KEY idx_dispatch_physical_draft (draft_token,draft_container_id,status), CONSTRAINT chk_dispatch_physical_active_key CHECK((status='ACTIVE' AND active_warehouse_pallet_id IS NOT NULL AND active_warehouse_pallet_id=warehouse_pallet_id) OR (status<>'ACTIVE' AND active_warehouse_pallet_id IS NULL)), CONSTRAINT chk_dispatch_physical_owner CHECK((dispatch_order_id IS NOT NULL AND draft_token IS NULL AND draft_container_id IS NULL) OR (dispatch_order_id IS NULL AND draft_token IS NOT NULL AND draft_container_id IS NOT NULL)) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='同车共享且跨车排他的物理实托占用'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='同车共享且跨车排他的物理实托占用'; CREATE TABLE ll_dispatch_pallet_lock ( id BIGINT NOT NULL COMMENT '主键ID', @@ -558,39 +561,39 @@ CREATE TABLE ll_dispatch_pallet_lock ( KEY idx_dispatch_logical_physical (physical_lock_id,status), KEY idx_ll_dispatch_pallet_lock_order (customer_id,dispatch_order_id,status), KEY idx_ll_dispatch_pallet_lock_source (customer_id,source_pallet_key,status) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='客户逻辑运输来源占用'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='客户逻辑运输来源占用'; CREATE TABLE ll_dispatch_pda_operation ( id bigint NOT NULL COMMENT '主键ID', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户租户ID', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户租户ID', task_id bigint DEFAULT NULL COMMENT 'PDA任务ID', - operation_type varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'PDA操作类型', - idempotency_key varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '业务幂等键', - request_hash char(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '请求内容哈希', - result_json longtext COLLATE utf8mb4_unicode_ci COMMENT '处理结果JSON', - status varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'PDA操作处理状态', + operation_type varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'PDA操作类型', + idempotency_key varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '业务幂等键', + request_hash char(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '请求内容哈希', + result_json longtext COLLATE utf8mb4_0900_ai_ci COMMENT '处理结果JSON', + status varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'PDA操作处理状态', 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_ll_dispatch_pda_operation_key (customer_id,idempotency_key) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='派车PDA操作幂等记录'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='派车PDA操作幂等记录'; CREATE TABLE ll_dispatch_pda_pick ( id bigint NOT NULL COMMENT '主键ID', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户租户ID', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户租户ID', task_id bigint NOT NULL COMMENT 'PDA任务ID', dispatch_item_id bigint NOT NULL COMMENT '派车明细ID', inventory_position_id BIGINT NULL COMMENT '普通库存仓位记录ID', packages bigint NOT NULL COMMENT '本次操作箱数', product_quantity bigint NOT NULL DEFAULT '0' COMMENT '产品数量', - idempotency_key varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '业务幂等键', + idempotency_key varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '业务幂等键', picked_at datetime(3) NOT NULL COMMENT '拣货时间', created_at datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间', return_carton_id BIGINT NULL COMMENT '独立退货库存箱ID,与普通仓位互斥', PRIMARY KEY (id), UNIQUE KEY uk_ll_dispatch_pda_pick_key (customer_id,idempotency_key), KEY idx_ll_dispatch_pda_pick_item (customer_id,task_id,dispatch_item_id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='派车PDA拣货记录'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='派车PDA拣货记录'; CREATE TABLE ll_return_stock_receipt ( id BIGINT NOT NULL COMMENT '退回入仓实收凭据ID', @@ -614,7 +617,7 @@ CREATE TABLE ll_return_stock_receipt ( KEY idx_return_stock_receipt_order(customer_id,order_id), KEY idx_return_stock_receipt_position(customer_id,inventory_balance_id,inventory_position_id), CONSTRAINT chk_return_stock_receipt_packages CHECK(received_packages>0) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='真实退回入仓及再次发运来源凭据'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='真实退回入仓及再次发运来源凭据'; CREATE TABLE ll_dispatch_pda_pick_order ( id BIGINT NOT NULL COMMENT '主键ID', customer_id VARCHAR(128) NOT NULL COMMENT '客户租户ID', pick_id BIGINT NOT NULL COMMENT 'PDA拣货记录ID', @@ -630,16 +633,16 @@ CREATE TABLE ll_dispatch_pda_pick_order ( PRIMARY KEY (id), KEY idx_ll_dispatch_pick_order_pick (customer_id,pick_id), KEY idx_ll_dispatch_pick_order_receipt (customer_id,receipt_id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='PDA拣货订单归属记录'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='PDA拣货订单归属记录'; CREATE TABLE ll_dispatch_pda_task ( id bigint NOT NULL COMMENT '主键ID', dispatch_order_id bigint NOT NULL COMMENT '唯一整车排车单引用,客户归属保留在扫码明细', - task_no varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'PDA任务号', - status varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '业务状态', - operator_id varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '操作人ID', - warehouse_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'Token绑定的海外仓库ID', - operator_name varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '操作人姓名', + task_no varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'PDA任务号', + status varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '业务状态', + operator_id varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '操作人ID', + warehouse_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'Token绑定的海外仓库ID', + operator_name varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '操作人姓名', pushed_at datetime(3) NOT NULL COMMENT '任务下发时间', completed_at datetime(3) DEFAULT NULL COMMENT '完成时间', created_at datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间', @@ -647,21 +650,21 @@ CREATE TABLE ll_dispatch_pda_task ( PRIMARY KEY (id), UNIQUE KEY uk_ll_dispatch_pda_task_dispatch (dispatch_order_id), UNIQUE KEY uk_ll_dispatch_pda_task_no (task_no) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='整车PDA拣货任务,一张排车单仅一条任务'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='整车PDA拣货任务,一张排车单仅一条任务'; CREATE TABLE ll_dropship_carton ( id bigint NOT NULL COMMENT '主键ID', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户租户ID', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户租户ID', order_id bigint NOT NULL COMMENT '订单ID', profile_id bigint NOT NULL COMMENT '托盘档案ID', warehouse_pallet_id bigint DEFAULT NULL COMMENT '仓库实物托盘ID', - pallet_no varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '托盘号', - sku_code varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'SKU编码', - carton_mark varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '箱唛', + pallet_no varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '托盘号', + sku_code varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'SKU编码', + carton_mark varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '箱唛', product_quantity bigint NOT NULL COMMENT '该箱实收商品件数', carton_sequence int NOT NULL COMMENT '箱唛序号', - status varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'PENDING_INBOUND' COMMENT '业务状态', - outbound_order_no varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '出库单号', + status varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'PENDING_INBOUND' COMMENT '业务状态', + outbound_order_no varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '出库单号', version bigint NOT NULL DEFAULT '0' 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 '更新时间', @@ -669,16 +672,16 @@ CREATE TABLE ll_dropship_carton ( UNIQUE KEY uk_ll_dropship_carton (customer_id,order_id,carton_mark), KEY idx_ll_dropship_carton_status (customer_id,status), KEY idx_ll_dropship_carton_sku (customer_id,sku_code,status) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='一件代发箱唛明细'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='一件代发箱唛明细'; CREATE TABLE ll_dropship_carton_sku ( id bigint NOT NULL COMMENT '主键ID', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户数据隔离键', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户数据隔离键', order_id bigint NOT NULL COMMENT '尾程订单ID', profile_id bigint NOT NULL COMMENT '一件代发托盘档案ID', carton_id bigint NOT NULL COMMENT '一件代发物理箱ID', - sku_code varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '箱内SKU编码', - product_name varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '产品名称快照', + sku_code varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '箱内SKU编码', + product_name varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '产品名称快照', product_quantity bigint NOT NULL COMMENT '该物理箱内此SKU实收件数', 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 '更新时间', @@ -686,43 +689,43 @@ CREATE TABLE ll_dropship_carton_sku ( UNIQUE KEY uk_ll_dropship_carton_sku (carton_id,sku_code), KEY idx_ll_dropship_carton_sku_order (customer_id,order_id,sku_code), KEY idx_ll_dropship_carton_sku_profile (customer_id,profile_id,sku_code) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='一件代发物理箱内SKU明细'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='一件代发物理箱内SKU明细'; CREATE TABLE ll_dropship_carton_ledger ( id bigint NOT NULL COMMENT '主键ID', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户租户ID', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户租户ID', order_id bigint NOT NULL COMMENT '订单ID', carton_id bigint NOT NULL COMMENT '箱唛记录ID', - carton_mark varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '箱唛', - sku_code varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'SKU编码', - operation_type varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '操作类型', + carton_mark varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '箱唛', + sku_code varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'SKU编码', + operation_type varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '操作类型', before_packages bigint NOT NULL COMMENT '变动前箱数', delta_packages bigint NOT NULL COMMENT '箱数变动量', after_packages bigint NOT NULL COMMENT '变动后箱数', - business_order_no varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '业务单号', - idempotency_key varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '业务幂等键', - remark varchar(1000) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '备注', + business_order_no varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '业务单号', + idempotency_key varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '业务幂等键', + remark varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '备注', occurred_at datetime(3) NOT NULL COMMENT '业务发生时间', created_at datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间', PRIMARY KEY (id), UNIQUE KEY uk_ll_dropship_ledger_key (customer_id,idempotency_key), KEY idx_ll_dropship_ledger_sku (customer_id,sku_code,carton_mark) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='一件代发箱唛库存流水'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='一件代发箱唛库存流水'; CREATE TABLE ll_dropship_order_profile ( id bigint NOT NULL COMMENT '主键ID', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户租户ID', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户租户ID', order_id bigint NOT NULL COMMENT '订单ID', warehouse_pallet_id bigint DEFAULT NULL COMMENT '仓库实物托盘ID', - pallet_no varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '托盘号', - source_pallet_no varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '来源托盘号快照', + pallet_no varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '托盘号', + source_pallet_no varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '来源托盘号快照', manifest_version int NOT NULL COMMENT '整托清单版本', manifest_package_count int NOT NULL COMMENT '整托总箱数', manifest_fragment_count int NOT NULL COMMENT '整托客户分片总数', - manifest_hash char(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '整托完整清单摘要', + manifest_hash char(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '整托完整清单摘要', inventory_position_id bigint DEFAULT NULL COMMENT '库存仓位记录ID', - status varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'PENDING_INBOUND' COMMENT '业务状态', - idempotency_key varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '业务幂等键', + status varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'PENDING_INBOUND' COMMENT '业务状态', + idempotency_key varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '业务幂等键', version bigint NOT NULL DEFAULT '0' 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 '更新时间', @@ -731,15 +734,15 @@ CREATE TABLE ll_dropship_order_profile ( UNIQUE KEY uk_ll_dropship_profile_key (customer_id,idempotency_key), KEY idx_ll_dropship_profile_order (customer_id,order_id), KEY idx_ll_dropship_profile_manifest (source_pallet_no,manifest_hash) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='一件代发托盘档案'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='一件代发托盘档案'; CREATE TABLE ll_dropship_sku_inventory ( id bigint NOT NULL COMMENT '主键ID', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户租户ID', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户租户ID', order_id bigint NOT NULL COMMENT '订单ID', profile_id bigint NOT NULL COMMENT '托盘档案ID', - sku_code varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'SKU编码', - product_name varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '产品名称', + sku_code varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'SKU编码', + product_name varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '产品名称', package_count bigint NOT NULL COMMENT '箱数', product_quantity bigint NOT NULL COMMENT '产品数量', created_at datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间', @@ -747,88 +750,88 @@ CREATE TABLE ll_dropship_sku_inventory ( PRIMARY KEY (id), UNIQUE KEY uk_ll_dropship_sku_inventory (profile_id,sku_code), KEY idx_ll_dropship_sku_order (customer_id,order_id,sku_code) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='一件代发SKU库存汇总'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='一件代发SKU库存汇总'; CREATE TABLE ll_event_conflict ( id bigint NOT NULL COMMENT '冲突记录主键', - event_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '冲突事件 ID', + event_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '冲突事件 ID', customer_id bigint NOT NULL COMMENT '租户 ID', - conflict_type varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '冲突类型', - conflict_message varchar(512) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '冲突说明', + conflict_type varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '冲突类型', + conflict_message varchar(512) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '冲突说明', created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', PRIMARY KEY (id), KEY idx_ll_conflict_event (event_id,customer_id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='事件冲突记录'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='事件冲突记录'; CREATE TABLE ll_event_inbox ( id bigint NOT NULL COMMENT '事件接收记录主键', - event_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '事件全局唯一 ID', + event_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '事件全局唯一 ID', customer_id bigint NOT NULL COMMENT '租户 ID', - event_type varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '事件类型', - aggregate_type varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '聚合类型', - aggregate_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '聚合 ID', + event_type varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '事件类型', + aggregate_type varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '聚合类型', + aggregate_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '聚合 ID', aggregate_version bigint NOT NULL COMMENT '聚合版本', - payload_hash varchar(71) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '负载摘要', - status varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '消费状态', - error_message varchar(512) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '错误信息', + payload_hash varchar(71) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '负载摘要', + status varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '消费状态', + error_message varchar(512) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '错误信息', created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', updated_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', PRIMARY KEY (id), UNIQUE KEY uk_ll_inbox_event (event_id), KEY idx_ll_inbox_source (customer_id,aggregate_id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='通用事件接收幂等箱'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='通用事件接收幂等箱'; CREATE TABLE ll_event_outbox ( id bigint NOT NULL COMMENT '事件主键ID', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户租户ID', - aggregate_type varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '聚合类型', - aggregate_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '聚合标识', - event_type varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '事件类型', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户租户ID', + aggregate_type varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '聚合类型', + aggregate_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '聚合标识', + event_type varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '事件类型', event_version int NOT NULL COMMENT '事件版本', - event_key varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '业务幂等键', - payload_json longtext COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '事件载荷JSON', - status varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'PENDING' COMMENT '发布状态', + event_key varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '业务幂等键', + payload_json longtext COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '事件载荷JSON', + status varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'PENDING' COMMENT '发布状态', retry_count int NOT NULL DEFAULT '0' COMMENT '重试次数', next_retry_at datetime(3) DEFAULT NULL COMMENT '下次重试时间', published_at datetime(3) DEFAULT NULL COMMENT '发布时间', locked_at datetime(3) DEFAULT NULL COMMENT '投递租约获取时间', - locked_by varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '投递租约持有者', - last_error varchar(1000) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '最后一次发布错误', + locked_by varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '投递租约持有者', + last_error varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '最后一次发布错误', created_at datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间', PRIMARY KEY (id), UNIQUE KEY uk_ll_event_outbox_key (event_key), KEY idx_ll_event_outbox_publish (status,next_retry_at,created_at) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='尾程跨区业务事件发件箱'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='尾程跨区业务事件发件箱'; CREATE TABLE ll_exception_order ( id bigint NOT NULL COMMENT '异常订单ID', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户数据隔离键', - exception_no varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '异常订单号', - exception_type varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '异常类型', - status varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'PENDING' COMMENT '异常处理状态', - reason varchar(1000) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '异常原因', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户数据隔离键', + exception_no varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '异常订单号', + exception_type varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '异常类型', + status varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'PENDING' COMMENT '异常处理状态', + reason varchar(1000) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '异常原因', last_mile_order_id bigint DEFAULT NULL COMMENT '关联尾程订单ID', - last_mile_order_no varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '关联尾程订单号', + last_mile_order_no varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '关联尾程订单号', inbound_order_id bigint DEFAULT NULL COMMENT '关联入仓单ID', - inbound_code varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '关联入仓码', + inbound_code varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '关联入仓码', delivery_order_id bigint DEFAULT NULL COMMENT '关联派送单ID', - delivery_no varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '关联派送单号', - outbound_no varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '关联出库单号', - customer_name varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '客户名称快照', - overseas_warehouse_id varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '海外仓ID快照', - overseas_warehouse_name varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '海外仓名称快照', - sku_code varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '异常货物SKU', + delivery_no varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '关联派送单号', + outbound_no varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '关联出库单号', + customer_name varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '客户名称快照', + overseas_warehouse_id varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '海外仓ID快照', + overseas_warehouse_name varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '海外仓名称快照', + sku_code varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '异常货物SKU', package_count int DEFAULT NULL COMMENT '异常箱数', - return_direction varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '退回方向', - return_logistics_no varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '退回物流单号', - handling_action varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '异常处理措施', - handling_result varchar(2000) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '异常处理结果', + return_direction varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '退回方向', + return_logistics_no varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '退回物流单号', + handling_action varchar(500) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '异常处理措施', + handling_result varchar(2000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '异常处理结果', evidence_file_ids_json json DEFAULT NULL COMMENT '异常处理证据文件ID列表', inventory_adjustment_id bigint DEFAULT NULL COMMENT '退回库存恢复操作ID', - handler_id varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '处理人ID', - handler_name varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '处理人名称', - source_type varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'MANUAL' COMMENT '异常来源类型', - created_by varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '创建人ID', + handler_id varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '处理人ID', + handler_name varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '处理人名称', + source_type varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'MANUAL' COMMENT '异常来源类型', + created_by varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '创建人ID', exception_at datetime(3) NOT NULL COMMENT '异常发生时间', completed_at datetime(3) DEFAULT NULL COMMENT '异常处理完成时间', version bigint NOT NULL DEFAULT '0' COMMENT '异常订单乐观锁版本', @@ -842,32 +845,32 @@ CREATE TABLE ll_exception_order ( KEY idx_ll_exception_list (customer_id,status,exception_at), KEY idx_ll_exception_tail (customer_id,last_mile_order_no), KEY idx_ll_exception_inbound (customer_id,inbound_code) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='海外仓异常订单'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='海外仓异常订单'; CREATE TABLE ll_exception_order_command ( id bigint NOT NULL COMMENT '异常处理命令ID', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户数据隔离键', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户数据隔离键', exception_order_id bigint NOT NULL COMMENT '异常订单ID', - action varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '异常处理动作', - idempotency_key varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '幂等键', - request_hash varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '请求内容SHA256哈希', + action varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '异常处理动作', + idempotency_key varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '幂等键', + request_hash varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '请求内容SHA256哈希', result_json json NOT NULL COMMENT '命令结果快照', created_at datetime(3) NOT NULL COMMENT '创建时间', PRIMARY KEY (id), UNIQUE KEY uk_ll_exception_command_key (customer_id,action,idempotency_key), KEY idx_ll_exception_command_order (customer_id,exception_order_id,created_at) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='异常处理命令幂等审计'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='异常处理命令幂等审计'; CREATE TABLE ll_exception_return_item ( id bigint NOT NULL COMMENT '异常退回明细ID', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户数据隔离键', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户数据隔离键', exception_order_id bigint NOT NULL COMMENT '异常订单ID', outbound_pallet_item_id bigint NOT NULL COMMENT '原出库托盘明细ID', inventory_position_id bigint DEFAULT NULL COMMENT '普通原出库库存位置;与退货原箱互斥', return_carton_id bigint DEFAULT NULL COMMENT '原生退货箱标识;与普通库存位置互斥', - return_carton_mark varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '原生退货箱业务箱码快照', - pallet_no varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '原出库托盘码快照', - sku_code varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '原出库SKU快照', + return_carton_mark varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '原生退货箱业务箱码快照', + pallet_no varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '原出库托盘码快照', + sku_code varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '原出库SKU快照', max_return_packages bigint NOT NULL COMMENT '原出库可退箱数上限', approved_return_packages bigint NOT NULL COMMENT '批准退回箱数', created_at datetime(3) NOT NULL COMMENT '创建时间', @@ -879,28 +882,28 @@ CREATE TABLE ll_exception_return_item ( PRIMARY KEY (id), UNIQUE KEY uk_ll_exception_return_source (customer_id,exception_order_id,outbound_pallet_item_id,inventory_position_id), KEY idx_ll_exception_return_order (customer_id,exception_order_id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='异常退回原出库库存冻结明细'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='异常退回原出库库存冻结明细'; CREATE TABLE ll_return_carton_stock_receipt ( id BIGINT NOT NULL COMMENT '原箱回仓凭据明细标识', - customer_id VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户数据隔离键', - receipt_no VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '原箱回仓实收业务号', + customer_id VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户数据隔离键', + receipt_no VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '原箱回仓实收业务号', exception_order_id BIGINT NOT NULL COMMENT '本次异常退回订单', exception_return_item_id BIGINT NOT NULL COMMENT '本次冻结的原箱来源明细', delivery_order_id BIGINT NOT NULL COMMENT '原运输派送单', source_pick_id BIGINT NOT NULL COMMENT '原箱实际出库拣货凭据', - source_outbound_no VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '原出库单业务号', + source_outbound_no VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '原出库单业务号', source_dispatch_item_id BIGINT NOT NULL COMMENT '原排车货物明细', source_outbound_pallet_item_id BIGINT NOT NULL COMMENT '原出库托盘明细', return_carton_id BIGINT NOT NULL COMMENT '真实退货原箱标识', - carton_mark VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '原箱业务箱码快照', - overseas_warehouse_id VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '实际回仓海外仓标识', + carton_mark VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '原箱业务箱码快照', + overseas_warehouse_id VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '实际回仓海外仓标识', target_pallet_id BIGINT NOT NULL COMMENT '实际回仓目标实物托盘', - target_pallet_no VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '实际目标托盘业务码', + target_pallet_no VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '实际目标托盘业务码', target_location_id BIGINT NOT NULL COMMENT '实际回仓目标库位', - target_location_code VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '实际目标库位业务码', - sku_code VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '原出库快照中的商品编码', - product_name_zh VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL COMMENT '原出库快照中的中文品名', + target_location_code VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '实际目标库位业务码', + sku_code VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '原出库快照中的商品编码', + product_name_zh VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL COMMENT '原出库快照中的中文品名', quantity BIGINT NOT NULL COMMENT '本次原SKU实际恢复件数', packages BIGINT NOT NULL COMMENT '本箱仅首条SKU记录计一箱其余为零', evidence_file_ids_json JSON NOT NULL COMMENT '本次实收证据文件引用', @@ -911,72 +914,74 @@ CREATE TABLE ll_return_carton_stock_receipt ( KEY idx_return_carton_receipt_exception(customer_id,exception_order_id,received_at), KEY idx_return_carton_receipt_delivery(customer_id,delivery_order_id), CONSTRAINT chk_return_carton_receipt_quantity CHECK(quantity>0 AND packages IN (0,1)) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='原生退货箱按原出库SKU实际回仓的不可变凭据'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='原生退货箱按原出库SKU实际回仓的不可变凭据'; CREATE TABLE ll_finance_fee_command ( id bigint NOT NULL COMMENT '幂等命令ID', - operator_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '操作人ID', - action varchar(16) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'CREATE、SUBMIT、REVIEW、ADJUST或VOID', - idempotency_key varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '操作幂等键', - request_hash char(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '请求内容SHA-256', + operator_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '操作人ID', + action varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '费用写操作动作:CREATE、SUBMIT、REVIEW、ADJUST、VOID或资金重试', + idempotency_key varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '操作幂等键', + request_hash char(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '请求内容SHA-256', source_fee_id bigint NOT NULL COMMENT '稳定费用来源ID', source_version int NOT NULL COMMENT '命令结果版本', created_at datetime(3) NOT NULL COMMENT '命令创建时间', PRIMARY KEY (id), UNIQUE KEY uk_ll_finance_fee_command (operator_id,action,idempotency_key), KEY idx_ll_finance_fee_command_source (source_fee_id,source_version) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='海外费用来源写操作幂等审计'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='海外费用来源写操作幂等审计'; CREATE TABLE ll_finance_fee_source ( id bigint NOT NULL COMMENT '费用版本记录ID', source_fee_id bigint NOT NULL COMMENT '跨版本稳定费用来源ID', source_version int NOT NULL COMMENT '费用来源版本,从1递增', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户ID和隔离键', - customer_name varchar(200) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '客户名称快照', - source_type varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '海外费用来源类型', - source_no varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '来源业务编号或入仓码快照', - business_type varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '来源业务类型', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户ID和隔离键', + customer_name varchar(200) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '客户名称快照', + source_type varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '海外费用来源类型', + source_no varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '来源业务编号或入仓码快照', + business_type varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '来源业务类型', business_id bigint DEFAULT NULL COMMENT '来源业务记录ID', - business_no varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '来源业务编号', + business_no varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '来源业务编号', order_id bigint DEFAULT NULL COMMENT '关联订单ID', inbound_id bigint DEFAULT NULL COMMENT '关联海外入仓计划或入仓记录ID', - inbound_code varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '关联入仓码;并非所有费用必填', + inbound_code varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '关联入仓码;并非所有费用必填', container_id bigint DEFAULT NULL COMMENT '关联柜次ID', container_booking_id bigint DEFAULT NULL COMMENT '来源订舱柜ID', - container_no varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '真实集装箱号快照', + container_no varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '真实集装箱号快照', warehouse_id bigint DEFAULT NULL COMMENT '关联海外仓ID', - fee_item_code varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '费用项目编码快照', - fee_item_name varchar(200) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '费用项目名称快照', - direction varchar(16) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'RECEIVABLE应收或PAYABLE应付', + fee_item_code varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '费用项目编码快照', + fee_item_name varchar(200) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '费用项目名称快照', + fee_remark varchar(500) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '费用说明,补收或重复作业原因', + direction varchar(16) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'RECEIVABLE应收或PAYABLE应付', supplier_id bigint DEFAULT NULL COMMENT '应付供应商ID', - supplier_name varchar(200) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '应付供应商名称快照', - supplier_category varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '应付供应商类别约束', - settlement_mode varchar(16) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'ON_ACCOUNT账期应付或DIRECT_PAID业务已付', + supplier_name varchar(200) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '应付供应商名称快照', + supplier_category varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '应付供应商类别约束', + settlement_mode varchar(16) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'ON_ACCOUNT账期应付或DIRECT_PAID业务已付', paid_at datetime(3) DEFAULT NULL COMMENT '直接已付时间', - payment_method varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '直接已付方式', - payment_reference_no varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '直接已付流水号', + payment_method varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '直接已付方式', + payment_reference_no varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '直接已付流水号', wallet_settlement json DEFAULT NULL COMMENT '已实扣应收的Finance资金命令、冻结和消费流水引用', payment_voucher_file_ids json DEFAULT NULL COMMENT '直接已付凭证文件ID快照', business_date date NOT NULL COMMENT '费用业务日期', - currency char(3) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'CNY' COMMENT '财务结算币种,固定CNY', + currency char(3) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'CNY' COMMENT '财务结算币种,固定CNY', amount decimal(20,6) NOT NULL COMMENT '人民币结算金额', - original_currency char(3) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '业务原币种,支持CNY或USD', + original_currency char(3) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '业务原币种,支持CNY或USD', original_amount decimal(20,6) NOT NULL COMMENT '业务原币金额', - exchange_rate decimal(20,6) NOT NULL COMMENT '原币折算人民币汇率,人民币固定为1', - exchange_rate_month char(7) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '汇率月份yyyy-MM', + exchange_rate decimal(20,6) DEFAULT NULL COMMENT '原币折算人民币汇率,人民币固定为1,自动零元仓租可空', + exchange_rate_month char(7) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '汇率月份yyyy-MM', chargeable_quantity decimal(20,6) DEFAULT NULL COMMENT '计费数量,如箱数、托数、体积或天数', - quantity_unit varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '计费数量单位', + quantity_unit varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '计费数量单位', unit_price decimal(20,6) DEFAULT NULL COMMENT '原币计费单价', period_start date DEFAULT NULL COMMENT '仓租等期间费用开始日期', period_end date DEFAULT NULL COMMENT '仓租等期间费用结束日期', - review_status varchar(16) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'DRAFT、PENDING、APPROVED或REJECTED', - publication_status varchar(16) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'UNPUBLISHED或PUBLISHED', - source_status varchar(16) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'ACTIVE、SUPERSEDED或VOIDED', - review_reason varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '审核或作废原因', - reviewer_id varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '审核人ID', + review_status varchar(16) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'DRAFT、PENDING、APPROVED或REJECTED', + publication_status varchar(16) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'UNPUBLISHED或PUBLISHED', + source_status varchar(16) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'ACTIVE、SUPERSEDED或VOIDED', + review_reason varchar(500) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '审核或作废原因', + reviewer_id varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '审核人ID', + reviewer_name varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '审核人名称快照', reviewed_at datetime(3) DEFAULT NULL COMMENT '审核时间', - created_by varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '创建人ID', - updated_by varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '最后更新人ID', + created_by varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '创建人ID', + updated_by varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '最后更新人ID', created_at datetime(3) NOT NULL COMMENT '创建时间', updated_at datetime(3) NOT NULL COMMENT '最后更新时间', PRIMARY KEY (id), @@ -984,7 +989,8 @@ CREATE TABLE ll_finance_fee_source ( KEY idx_ll_finance_fee_customer (customer_id,source_status,created_at), KEY idx_ll_finance_fee_business (business_type,business_no,created_at), KEY idx_ll_finance_fee_container_customer (container_booking_id,customer_id,source_status,created_at), - CONSTRAINT chk_ll_finance_fee_amount CHECK ((amount > 0)), + CONSTRAINT chk_ll_finance_fee_exchange_rate CHECK((exchange_rate IS NOT NULL AND exchange_rate>0) OR (exchange_rate IS NULL AND amount=0 AND original_amount=0 AND created_by='STORAGE_RENT' AND fee_item_code='FEE_TYPE_077' AND direction='RECEIVABLE')), + CONSTRAINT chk_ll_finance_fee_amount CHECK(amount>0 OR (amount=0 AND original_amount=0 AND created_by='STORAGE_RENT' AND fee_item_code='FEE_TYPE_077' AND direction='RECEIVABLE')), CONSTRAINT chk_ll_finance_fee_currency CHECK ((currency = _utf8mb4'CNY')), CONSTRAINT chk_ll_finance_fee_direction CHECK ((direction in (_utf8mb4'RECEIVABLE',_utf8mb4'PAYABLE'))), CONSTRAINT chk_ll_finance_fee_original_currency CHECK ((original_currency in (_utf8mb4'CNY',_utf8mb4'USD'))), @@ -992,7 +998,7 @@ CREATE TABLE ll_finance_fee_source ( CONSTRAINT chk_ll_finance_fee_publication CHECK ((publication_status in (_utf8mb4'UNPUBLISHED',_utf8mb4'PUBLISHED'))), CONSTRAINT chk_ll_finance_fee_review CHECK ((review_status in (_utf8mb4'DRAFT',_utf8mb4'PENDING',_utf8mb4'APPROVED',_utf8mb4'REJECTED'))), CONSTRAINT chk_ll_finance_fee_status CHECK ((source_status in (_utf8mb4'ACTIVE',_utf8mb4'SUPERSEDED',_utf8mb4'VOIDED'))) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='海外业务不可变费用来源版本;发布后经跨区事件进入Finance'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='海外业务不可变费用来源版本;发布后经跨区事件进入Finance'; -- V13 receiving coverage and pending changes; baseline only, no test data migration. CREATE TABLE ll_fba_receiving_agreement ( @@ -1004,7 +1010,7 @@ CREATE TABLE ll_fba_receiving_agreement ( snapshot_json LONGTEXT NOT NULL COMMENT '客户逐来源覆盖、确认时区及仓库依据快照', PRIMARY KEY (agreement_key), UNIQUE KEY uk_fba_receiving_confirmation (destination_key, confirmation_no) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='独立于送货批次的收货预约'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='独立于送货批次的收货预约'; CREATE TABLE ll_fba_receiving_member ( agreement_key VARCHAR(64) NOT NULL COMMENT '所属收货预约业务键', @@ -1014,7 +1020,7 @@ CREATE TABLE ll_fba_receiving_member ( PRIMARY KEY (agreement_key,batch_key,customer_id), UNIQUE KEY uk_fba_receiving_active_member (active_ref), KEY idx_fba_receiving_batch (batch_key,customer_id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='收货预约当前覆盖关联及退出历史'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='收货预约当前覆盖关联及退出历史'; CREATE TABLE ll_fba_receiving_change ( change_key VARCHAR(64) NOT NULL COMMENT '客户预约变化业务键', @@ -1028,21 +1034,21 @@ CREATE TABLE ll_fba_receiving_change ( PRIMARY KEY (change_key), UNIQUE KEY uk_fba_receiving_pending (active_ref), KEY idx_fba_receiving_pending_batch (batch_key,status) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='保留原安排且阻止误发车的客户变化'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='保留原安排且阻止误发车的客户变化'; CREATE TABLE ll_fba_receiving_revision ( agreement_key VARCHAR(64) NOT NULL COMMENT '收货预约业务键', version INT NOT NULL COMMENT '不可变覆盖与确认版本', snapshot_json LONGTEXT NOT NULL COMMENT '该版本完整客户覆盖、确认时段及回复依据', PRIMARY KEY (agreement_key,version) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='收货预约覆盖与确认不可变历史'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='收货预约覆盖与确认不可变历史'; CREATE TABLE ll_fba_receiving_plan ( batch_key VARCHAR(128) NOT NULL COMMENT '稳定送货批次键', customer_id VARCHAR(128) NOT NULL COMMENT '计划所属客户标识', snapshot_json LONGTEXT NOT NULL COMMENT '本客户未确认计划、目标窗口及业务时区,不含有效预约确认', PRIMARY KEY (batch_key,customer_id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='按批次及客户保存的未确认收货计划'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='按批次及客户保存的未确认收货计划'; CREATE TABLE ll_inbound_fba_appointment ( @@ -1073,7 +1079,7 @@ CREATE TABLE ll_inbound_fba_appointment ( UNIQUE KEY uk_ll_fba_appointment_no (appointment_no), KEY idx_ll_fba_appointment_list (appointment_at,appointment_status,fba_warehouse_code), KEY idx_ll_fba_appointment_planned (planned_at,appointment_status) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='FBA预约安排与正式确认唯一主记录'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='FBA预约安排与正式确认唯一主记录'; CREATE TABLE ll_inbound_fba_appointment_item ( id BIGINT NOT NULL COMMENT '记录主键', @@ -1095,7 +1101,7 @@ CREATE TABLE ll_inbound_fba_appointment_item ( KEY idx_ll_fba_active_order (customer_id,order_id,active), KEY idx_ll_fba_item_source_lookup (source_key,customer_id), KEY idx_ll_fba_item_customer_order (customer_id,order_id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='入仓前后共用的预约真实货源'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='入仓前后共用的预约真实货源'; CREATE TABLE ll_appointment_cargo_verification ( id BIGINT NOT NULL COMMENT '核实记录主键', @@ -1118,7 +1124,7 @@ CREATE TABLE ll_appointment_cargo_verification ( UNIQUE KEY uk_ll_cargo_verification_command (customer_id,source_key,command_key), CONSTRAINT chk_ll_cargo_verification_quantity CHECK (package_count IS NULL OR package_count > 0), CONSTRAINT chk_ll_cargo_verification_version CHECK (version > 0) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='预约货物核实资料及不可覆盖的版本历史'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='预约货物核实资料及不可覆盖的版本历史'; CREATE TABLE ll_fba_appointment_command ( command_key VARCHAR(128) NOT NULL COMMENT '持久化幂等键', @@ -1130,48 +1136,48 @@ CREATE TABLE ll_fba_appointment_command ( created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建UTC时间', PRIMARY KEY(command_key), KEY idx_ll_fba_command_appointment(appointment_no,created_at) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='预约命令与变更历史'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='预约命令与变更历史'; CREATE TABLE ll_inbound_order ( id bigint NOT NULL COMMENT '入仓单主键', customer_id bigint NOT NULL COMMENT '租户 ID', - source_region varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '来源区域', - source_inbound_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '来源入仓分片 ID', - root_inbound_id varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '根入仓单 ID', - source_order_id varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '来源订单 ID', - source_order_no varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '来源订单号', - root_order_id varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '根订单 ID', - inbound_code varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '入仓码', - lx_code varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '一件代发领星入库单代码', - destination_warehouse_id varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '目的仓 ID', - destination_warehouse_code varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '目的仓编码', - destination_warehouse_name varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '目的仓名称', - destination_type varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '目的类型:FBA按箱数接收、OVERSEAS逐箱扫码', + source_region varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '来源区域', + source_inbound_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '来源入仓分片 ID', + root_inbound_id varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '根入仓单 ID', + source_order_id varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '来源订单 ID', + source_order_no varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '来源订单号', + root_order_id varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '根订单 ID', + inbound_code varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '入仓码', + lx_code varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '一件代发领星入库单代码', + destination_warehouse_id varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '目的仓 ID', + destination_warehouse_code varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '目的仓编码', + destination_warehouse_name varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '目的仓名称', + destination_type varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '目的类型:FBA按箱数接收、OVERSEAS逐箱扫码', package_count int NOT NULL DEFAULT '0' COMMENT '箱数', product_quantity int DEFAULT NULL COMMENT '数量', pallet_count int DEFAULT NULL COMMENT '托数', gross_weight_kg decimal(18,3) DEFAULT NULL COMMENT '毛重(千克)', net_weight_kg decimal(18,3) DEFAULT NULL COMMENT '净重(千克)', volume_cbm decimal(18,6) DEFAULT NULL COMMENT '体积(立方米)', - source_event_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '来源事件 ID', - source_loading_plan_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '装柜计划 ID', + source_event_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '来源事件 ID', + source_loading_plan_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '装柜计划 ID', source_loading_plan_version bigint NOT NULL COMMENT '装柜计划版本', handover_version bigint NOT NULL COMMENT '交接版本', - payload_hash varchar(71) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '负载摘要', - status varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '入仓单状态', - exception_flag varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'NONE' COMMENT '异常标记', + payload_hash varchar(71) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '负载摘要', + status varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '入仓单状态', + exception_flag varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'NONE' COMMENT '异常标记', created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', updated_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', PRIMARY KEY (id), UNIQUE KEY uk_ll_order_source (customer_id,source_region,source_inbound_id), UNIQUE KEY uk_ll_order_event (source_event_id,source_inbound_id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='尾程入仓单快照'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='尾程入仓单快照'; CREATE TABLE ll_inbound_order_item ( id bigint NOT NULL COMMENT '入仓明细主键', customer_id bigint NOT NULL COMMENT '租户ID', order_id bigint NOT NULL COMMENT '入仓单ID', - source_inbound_detail_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '来源入仓明细ID', + source_inbound_detail_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '来源入仓明细ID', package_count int NOT NULL COMMENT '箱数', product_quantity int DEFAULT NULL COMMENT '数量', pallet_count int DEFAULT NULL COMMENT '托数', @@ -1179,25 +1185,25 @@ CREATE TABLE ll_inbound_order_item ( net_weight_kg decimal(18,3) DEFAULT NULL COMMENT '净重(千克)', dimensional_weight_kg decimal(18,3) DEFAULT NULL COMMENT '体积重量(千克)', volume_cbm decimal(18,6) DEFAULT NULL COMMENT '体积(立方米)', - destination_warehouse_id varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '目的仓ID', - destination_warehouse_code varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '目的仓编码', - destination_warehouse_name varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '目的仓名称', - fba_tracking_no varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'FBA跟踪号', - shipment_no varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '货件编号', - shipping_mark varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '唛头', - sku_code varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'SKU编码', - product_name_zh varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '中文品名', - product_name_en varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '英文品名', + destination_warehouse_id varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '目的仓ID', + destination_warehouse_code varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '目的仓编码', + destination_warehouse_name varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '目的仓名称', + fba_tracking_no varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'FBA跟踪号', + shipment_no varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '货件编号', + shipping_mark varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '唛头', + sku_code varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'SKU编码', + product_name_zh varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '中文品名', + product_name_en varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '英文品名', created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', PRIMARY KEY (id), KEY idx_ll_item_order (customer_id,order_id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='尾程入仓商品明细快照'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='尾程入仓商品明细快照'; CREATE TABLE ll_inbound_pallet_merge ( id bigint NOT NULL COMMENT '主键ID', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户租户ID', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户租户ID', task_id bigint NOT NULL COMMENT 'PDA任务ID', - merge_group_no varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '合托分组号', + merge_group_no varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '合托分组号', source_pallet_id bigint NOT NULL COMMENT '来源托盘ID', target_pallet_id bigint NOT NULL COMMENT '目标托盘ID', source_allocation_id bigint NOT NULL COMMENT '来源分配记录ID', @@ -1209,38 +1215,38 @@ CREATE TABLE ll_inbound_pallet_merge ( moved_package_count int NOT NULL COMMENT '移动箱数', moved_volume_cbm decimal(18,4) NOT NULL DEFAULT '0.0000' COMMENT '移动体积(立方米)', source_pallet_scrapped tinyint NOT NULL DEFAULT '0' COMMENT '来源托盘是否已报废', - status varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '业务状态', + status varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '业务状态', created_at datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间', PRIMARY KEY (id), KEY idx_ll_inbound_merge_task (customer_id,task_id), KEY idx_ll_inbound_merge_group (merge_group_no) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='入仓托盘合托记录'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='入仓托盘合托记录'; CREATE TABLE ll_inbound_pda_operation ( id bigint NOT NULL COMMENT '主键ID', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户租户ID', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户租户ID', task_id bigint NOT NULL COMMENT 'PDA任务ID', allocation_id bigint DEFAULT NULL COMMENT '分配记录ID', - action varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '操作动作', - idempotency_key varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '业务幂等键', - request_hash varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '请求内容哈希', - request_json longtext COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '请求内容JSON', - result_json longtext COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '处理结果JSON', + action varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '操作动作', + idempotency_key varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '业务幂等键', + request_hash varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '请求内容哈希', + request_json longtext COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '请求内容JSON', + result_json longtext COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '处理结果JSON', created_at datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间', PRIMARY KEY (id), UNIQUE KEY uk_ll_inbound_pda_operation_key (customer_id,idempotency_key), KEY idx_ll_inbound_pda_operation_task (task_id), KEY idx_ll_inbound_pda_operation_allocation (allocation_id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='入仓PDA操作幂等记录'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='入仓PDA操作幂等记录'; CREATE TABLE ll_inbound_pda_task ( id bigint NOT NULL COMMENT '主键ID', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户租户ID', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户租户ID', plan_id bigint NOT NULL COMMENT '入仓计划ID', - task_no varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'PDA任务号', - status varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'PENDING' COMMENT '业务状态', - operator_id varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '操作人ID', - operator_name varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '操作人姓名', + task_no varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'PDA任务号', + status varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'PENDING' COMMENT '业务状态', + operator_id varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '操作人ID', + operator_name varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '操作人姓名', pushed_at datetime(3) DEFAULT NULL COMMENT '任务下发时间', started_at datetime(3) DEFAULT NULL COMMENT '开始时间', completed_at datetime(3) DEFAULT NULL COMMENT '完成时间', @@ -1253,23 +1259,23 @@ CREATE TABLE ll_inbound_pda_task ( PRIMARY KEY (id), UNIQUE KEY uk_ll_inbound_pda_task_plan (customer_id,plan_id), UNIQUE KEY uk_ll_inbound_pda_task_no (task_no) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='入仓PDA作业任务'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='入仓PDA作业任务'; CREATE TABLE ll_inbound_plan ( id bigint NOT NULL COMMENT '入库计划ID(雪花ID)', - plan_no varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '入库单号', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '租户/客户账号标识', + plan_no varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '入库单号', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '租户/客户账号标识', source_order_id bigint NOT NULL COMMENT '来源尾程订单ID,一张入仓单对应一张入库计划', - container_no varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '集装箱号', + container_no varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '集装箱号', overseas_warehouse_id bigint DEFAULT NULL COMMENT '从尾程订单继承的平台预分配海外接柜仓ID', planned_arrival_date date DEFAULT NULL COMMENT '人工计划预计到仓日期', - pallet_type varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '人工计划托盘类型', + pallet_type varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '人工计划托盘类型', assigned_flag tinyint NOT NULL DEFAULT '0' COMMENT '人工计划是否已分配:0否1是', merge_required tinyint NOT NULL DEFAULT '0' COMMENT '人工计划是否要求合托:0否1是', urgent_flag tinyint NOT NULL DEFAULT '0' COMMENT '人工计划是否加急:0否1是', - creation_mode varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'AUTO' COMMENT '计划生成方式:AUTO柜级自动、MANUAL单票人工、WAREHOUSE_TRANSFER仓储转运', - plan_remark varchar(1000) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '入仓计划备注', - status varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'PENDING_ASSIGNMENT' COMMENT '入库计划状态:PENDING_ASSIGNMENT待分配及后续正式状态', + creation_mode varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'AUTO' COMMENT '计划生成方式:AUTO柜级自动、MANUAL单票人工、WAREHOUSE_TRANSFER仓储转运', + plan_remark varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '入仓计划备注', + status varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'PENDING_ASSIGNMENT' COMMENT '入库计划状态:PENDING_ASSIGNMENT待分配及后续正式状态', line_count int NOT NULL DEFAULT '0' COMMENT '入仓码明细数量', pallet_count int NOT NULL DEFAULT '0' COMMENT '系统自动分配托盘数量', total_volume_cbm decimal(18,4) NOT NULL DEFAULT '0.0000' COMMENT '集装箱总体积(立方米)', @@ -1280,25 +1286,25 @@ CREATE TABLE ll_inbound_plan ( UNIQUE KEY uk_ll_inbound_plan_no (plan_no), UNIQUE KEY uk_ll_inbound_plan_source_order (customer_id,source_order_id), KEY idx_ll_inbound_plan_container (customer_id,container_no) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='尾程入库计划表:每张来源入仓单生成一张入库计划,同柜计划共享集装箱号'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='尾程入库计划表:每张来源入仓单生成一张入库计划,同柜计划共享集装箱号'; CREATE TABLE ll_inbound_plan_carton ( id bigint NOT NULL COMMENT '箱唛作业主键', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户租户ID', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户租户ID', order_id bigint DEFAULT NULL COMMENT '生成尾程订单时绑定的尾程订单ID', - source_inbound_detail_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '来源入库明细ID', - sku_code varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'SKU编码快照', - carton_mark varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '国内冻结箱唛', - destination_warehouse_id varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '目的仓ID快照', - destination_warehouse_code varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '目的仓编码快照', - destination_warehouse_name varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '目的仓名称快照', + source_inbound_detail_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '来源入库明细ID', + sku_code varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'SKU编码快照', + carton_mark varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '国内冻结箱唛', + destination_warehouse_id varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '目的仓ID快照', + destination_warehouse_code varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '目的仓编码快照', + destination_warehouse_name varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '目的仓名称快照', plan_id bigint DEFAULT NULL COMMENT '海外入库计划ID', line_id bigint DEFAULT NULL COMMENT '入库计划明细ID', plan_pallet_id bigint DEFAULT NULL COMMENT '海外计划托盘ID', warehouse_pallet_id bigint DEFAULT NULL COMMENT '海外实物托盘ID', warehouse_location_id bigint DEFAULT NULL COMMENT '海外库位ID', pda_task_id bigint DEFAULT NULL COMMENT '首次扫码PDA任务ID', - status varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '箱唛状态:未绑定UNASSIGNED、已绑托ON_PALLET、整票收货BULK_RECEIVED、可用AVAILABLE、待废弃DISCARD_PENDING、已废弃DISCARDED', + status varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '箱唛状态:未绑定UNASSIGNED、已绑托ON_PALLET、整票收货BULK_RECEIVED、可用AVAILABLE、待废弃DISCARD_PENDING、已废弃DISCARDED', version int NOT NULL DEFAULT '0' COMMENT '乐观锁版本', bound_at datetime(3) DEFAULT NULL COMMENT '绑定托盘时间', received_at datetime(3) DEFAULT NULL COMMENT '完成入库时间', @@ -1308,23 +1314,23 @@ CREATE TABLE ll_inbound_plan_carton ( KEY idx_ll_inbound_carton_plan (plan_id,status), KEY idx_ll_inbound_carton_pallet (plan_pallet_id,status), KEY idx_ll_inbound_carton_order (order_id,status) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='柜次冻结箱唛及海外入库箱级作业账'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='柜次冻结箱唛及海外入库箱级作业账'; CREATE TABLE ll_inbound_plan_line ( id bigint NOT NULL COMMENT '入库计划明细ID(雪花ID)', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户账号标识', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户账号标识', plan_id bigint NOT NULL COMMENT '入库计划ID', order_id bigint NOT NULL COMMENT '尾程订单ID', - inbound_code varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '头程入仓码快照', - fba_warehouse_code varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'FBA仓库编码', - destination_type varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'OVERSEAS' COMMENT '目的类型:FBA按箱数接收、OVERSEAS逐箱扫码', - destination_warehouse_id varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '目的仓ID快照', - destination_warehouse_code varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '目的仓编码快照', - destination_warehouse_name varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '目的仓名称快照', - delivery_address varchar(1000) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '私人仓目的地址快照', - postal_code varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '私人仓邮编快照', - contact_name varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '私人仓联系人快照', - contact_phone varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '私人仓联系电话快照', + inbound_code varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '头程入仓码快照', + fba_warehouse_code varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'FBA仓库编码', + destination_type varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'OVERSEAS' COMMENT '目的类型:FBA按箱数接收、OVERSEAS逐箱扫码', + destination_warehouse_id varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '目的仓ID快照', + destination_warehouse_code varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '目的仓编码快照', + destination_warehouse_name varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '目的仓名称快照', + delivery_address varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '私人仓目的地址快照', + postal_code varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '私人仓邮编快照', + contact_name varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '私人仓联系人快照', + contact_phone varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '私人仓联系电话快照', volume_cbm decimal(18,4) NOT NULL DEFAULT '0.0000' COMMENT '入仓码体积(立方米)', planned_pallet_count int NOT NULL DEFAULT '0' COMMENT '该明细分配的托盘数量', required_package_count int NOT NULL DEFAULT '0' COMMENT '计划明细应收箱数', @@ -1332,23 +1338,23 @@ CREATE TABLE ll_inbound_plan_line ( UNIQUE KEY uk_ll_inbound_line_order (plan_id,order_id), KEY idx_ll_inbound_line_plan (plan_id), KEY idx_ll_inbound_line_customer_plan (customer_id,plan_id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='尾程入库计划明细表:按入仓码和尾程订单维度记录'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='尾程入库计划明细表:按入仓码和尾程订单维度记录'; CREATE TABLE ll_inbound_plan_pallet ( id bigint NOT NULL COMMENT '计划托盘ID(雪花ID)', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户账号标识', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户账号标识', plan_id bigint NOT NULL COMMENT '入库计划ID', - pallet_no varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'Warehouse权威实物托盘码', - source_pallet_no varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '一件代发不可变来源托盘号快照', - pallet_type varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '人工计划托盘类型', - destination_warehouse_id varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '目的仓ID快照', - destination_warehouse_code varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '目的仓编码快照', - destination_warehouse_name varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '目的仓名称快照', - destination_type varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '目的类型:FBA或PRIVATE', - delivery_address varchar(1000) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '私人仓目的地址快照', - postal_code varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '私人仓邮编快照', - contact_name varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '私人仓联系人快照', - contact_phone varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '私人仓联系电话快照', + pallet_no varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'Warehouse权威实物托盘码', + source_pallet_no varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '一件代发不可变来源托盘号快照', + pallet_type varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '人工计划托盘类型', + destination_warehouse_id varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '目的仓ID快照', + destination_warehouse_code varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '目的仓编码快照', + destination_warehouse_name varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '目的仓名称快照', + destination_type varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '目的类型:FBA或PRIVATE', + delivery_address varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '私人仓目的地址快照', + postal_code varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '私人仓邮编快照', + contact_name varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '私人仓联系人快照', + contact_phone varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '私人仓联系电话快照', capacity_cbm decimal(18,4) NOT NULL DEFAULT '2.0000' COMMENT '托盘容量,固定为2立方米', allocated_volume_cbm decimal(18,4) NOT NULL DEFAULT '0.0000' COMMENT '已分配体积(立方米)', sequence_no int NOT NULL COMMENT '计划内托盘序号', @@ -1356,7 +1362,7 @@ CREATE TABLE ll_inbound_plan_pallet ( warehouse_pallet_id bigint DEFAULT NULL COMMENT '海外仓实物托盘ID', warehouse_location_id bigint DEFAULT NULL COMMENT '海外仓库位ID', warehouse_resource_lock_id bigint DEFAULT NULL COMMENT '海外仓资源锁ID', - assignment_status varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'UNASSIGNED' COMMENT '托盘分配状态:UNASSIGNED未分配及后续正式状态', + assignment_status varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'UNASSIGNED' COMMENT '托盘分配状态:UNASSIGNED未分配及后续正式状态', locked_at datetime(3) DEFAULT NULL COMMENT '资源锁定时间', actual_volume_cbm decimal(18,4) NOT NULL DEFAULT '0.0000' COMMENT '实收体积(立方米)', actual_package_count int NOT NULL DEFAULT '0' COMMENT '实收箱数', @@ -1365,11 +1371,11 @@ CREATE TABLE ll_inbound_plan_pallet ( KEY idx_ll_inbound_pallet_no (pallet_no), KEY idx_ll_inbound_pallet_plan (plan_id), KEY idx_ll_inbound_pallet_customer_plan (customer_id,plan_id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='尾程入库计划托盘表:按目的仓隔离合托'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='尾程入库计划托盘表:按目的仓隔离合托'; CREATE TABLE ll_inbound_plan_pallet_allocation ( id bigint NOT NULL COMMENT '托盘分配记录ID(雪花ID)', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户账号标识', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户账号标识', plan_id bigint NOT NULL COMMENT '入库计划ID', line_id bigint NOT NULL COMMENT '入库计划明细ID', pallet_id bigint NOT NULL COMMENT '计划托盘ID', @@ -1378,61 +1384,61 @@ CREATE TABLE ll_inbound_plan_pallet_allocation ( counted_package_count int NOT NULL DEFAULT '0' COMMENT '清点箱数', picked_package_count int NOT NULL DEFAULT '0' COMMENT '已拣箱数', actual_volume_cbm decimal(18,4) NOT NULL DEFAULT '0.0000' COMMENT '实收体积(立方米)', - status varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'PENDING_COUNT' COMMENT '分配作业状态:PENDING_COUNT待清点及后续正式状态', + status varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'PENDING_COUNT' COMMENT '分配作业状态:PENDING_COUNT待清点及后续正式状态', version int NOT NULL DEFAULT '0' COMMENT '托盘分配乐观锁版本', PRIMARY KEY (id), UNIQUE KEY uk_ll_inbound_allocation (line_id,pallet_id), KEY idx_ll_inbound_allocation_plan (plan_id), KEY idx_ll_inbound_allocation_customer_plan (customer_id,plan_id), KEY idx_ll_inbound_allocation_pallet (pallet_id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='尾程入库计划托盘分配表:记录入仓码明细与计划托盘的分配关系'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='尾程入库计划托盘分配表:记录入仓码明细与计划托盘的分配关系'; CREATE TABLE ll_inbound_stock_receipt ( id bigint NOT NULL COMMENT '主键ID', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户租户ID', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户租户ID', plan_id bigint NOT NULL COMMENT '入仓计划ID', task_id bigint NOT NULL COMMENT 'PDA任务ID', allocation_id bigint NOT NULL COMMENT '分配记录ID', line_id bigint NOT NULL COMMENT '入仓计划明细ID', order_id bigint NOT NULL COMMENT '订单ID', - inbound_code varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '入仓单号快照', - sku_code varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'SKU编码快照', - product_name_zh varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '中文品名快照', - product_name_en varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '英文品名快照', + inbound_code varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '入仓单号快照', + sku_code varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'SKU编码快照', + product_name_zh varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '中文品名快照', + product_name_en varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '英文品名快照', warehouse_pallet_id bigint NOT NULL COMMENT '仓库实物托盘ID', - pallet_no varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '海外仓实物托盘码', - source_pallet_no varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '一件代发不可变来源托盘号快照', + pallet_no varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '海外仓实物托盘码', + source_pallet_no varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '一件代发不可变来源托盘号快照', warehouse_location_id bigint DEFAULT NULL COMMENT '仓库库位ID', - warehouse_location_code varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '仓库库位编码', - batch_no varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '库存批次号', + warehouse_location_code varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '仓库库位编码', + batch_no varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '库存批次号', received_packages bigint NOT NULL COMMENT '实收箱数', inventory_balance_id bigint NOT NULL COMMENT '库存余额记录ID', inventory_position_id bigint NOT NULL COMMENT '库存仓位记录ID', - inventory_operation_key varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '库存操作幂等键', + inventory_operation_key varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '库存操作幂等键', received_at datetime(3) NOT NULL COMMENT '实收时间', created_at datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间', PRIMARY KEY (id), UNIQUE KEY uk_ll_stock_receipt_task_allocation_sku (customer_id,task_id,allocation_id,sku_code), KEY idx_ll_stock_receipt_plan (customer_id,plan_id,line_id), KEY idx_ll_stock_receipt_inventory (inventory_balance_id,inventory_position_id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='入仓PDA库存实收快照'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='入仓PDA库存实收快照'; CREATE TABLE ll_inventory_balance ( id bigint NOT NULL COMMENT '主键ID', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户租户ID', - customer_name varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户名称不可变快照', - overseas_warehouse_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '海外仓ID', - overseas_warehouse_code varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '海外仓编码快照', - overseas_warehouse_name varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '海外仓名称快照', - destination_warehouse_id varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '目的仓ID', - destination_warehouse_code varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '目的仓编码快照', - destination_warehouse_name varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '目的仓名称快照', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户租户ID', + customer_name varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户名称不可变快照', + overseas_warehouse_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '海外仓ID', + overseas_warehouse_code varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '海外仓编码快照', + overseas_warehouse_name varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '海外仓名称快照', + destination_warehouse_id varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '目的仓ID', + destination_warehouse_code varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT '目的仓编码快照', + destination_warehouse_name varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '目的仓名称快照', inbound_order_id bigint NOT NULL COMMENT '入仓单ID', - inbound_order_no varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '入仓单号快照', - inbound_code varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '入仓单号快照', - sku_code varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'UNSPECIFIED' COMMENT 'SKU快照;多SKU汇总时为MIXED,不参与当前库存唯一维度', - product_name_zh varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '中文品名快照', - product_name_en varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '英文品名快照', + inbound_order_no varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '入仓单号快照', + inbound_code varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '入仓单号快照', + sku_code varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'UNSPECIFIED' COMMENT 'SKU快照;多SKU汇总时为MIXED,不参与当前库存唯一维度', + product_name_zh varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '中文品名快照', + product_name_en varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '英文品名快照', available_packages bigint NOT NULL DEFAULT '0' COMMENT '可用箱数', hold_packages bigint NOT NULL DEFAULT '0' COMMENT '冻结箱数', allocated_packages bigint NOT NULL DEFAULT '0' COMMENT '已分配箱数', @@ -1448,39 +1454,39 @@ CREATE TABLE ll_inventory_balance ( KEY idx_ll_inventory_list (customer_id,overseas_warehouse_id,updated_at), KEY idx_ll_inventory_inbound (customer_id,inbound_code), KEY idx_ll_inventory_sku (customer_id,sku_code,updated_at) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='客户入仓单与海外目的仓维度实时库存余额'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='客户入仓单与海外目的仓维度实时库存余额'; CREATE TABLE ll_inventory_ledger ( id bigint NOT NULL COMMENT '主键ID', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户租户ID', - customer_name varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户名称不可变快照', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户租户ID', + customer_name varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户名称不可变快照', balance_id bigint NOT NULL COMMENT '库存余额记录ID', position_id bigint DEFAULT NULL COMMENT '库存仓位记录ID', - overseas_warehouse_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '海外仓ID', - destination_warehouse_code varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '目的仓编码快照', + overseas_warehouse_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '海外仓ID', + destination_warehouse_code varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT '目的仓编码快照', inbound_order_id bigint NOT NULL COMMENT '入仓单ID', pallet_id bigint DEFAULT NULL COMMENT '托盘ID', - pallet_no varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '托盘号', - batch_no varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '库存批次号', - warehouse_location_code varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '仓库库位编码', - sku_code varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'SKU编码', - carton_mark varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '箱唛', + pallet_no varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '托盘号', + batch_no varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '库存批次号', + warehouse_location_code varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '仓库库位编码', + sku_code varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'SKU编码', + carton_mark varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '箱唛', carton_packages bigint DEFAULT NULL COMMENT '箱内箱数', - business_type varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '业务类型', + business_type varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '业务类型', business_order_id bigint DEFAULT NULL COMMENT '业务单据ID', - business_order_no varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '业务单号', - inbound_code varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '入仓单号快照', - inbound_order_no varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '入仓单号快照', - outbound_order_no varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '出库单号', - source_event_id varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '来源事件ID', - idempotency_key varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '业务幂等键', - status_from varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '操作前状态', - status_to varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '操作后状态', + business_order_no varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '业务单号', + inbound_code varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '入仓单号快照', + inbound_order_no varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '入仓单号快照', + outbound_order_no varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '出库单号', + source_event_id varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '来源事件ID', + idempotency_key varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '业务幂等键', + status_from varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '操作前状态', + status_to varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '操作后状态', before_packages bigint NOT NULL COMMENT '变动前箱数', delta_packages bigint NOT NULL COMMENT '箱数变动量', after_packages bigint NOT NULL COMMENT '变动后箱数', - operator_id varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '操作人ID', - remark varchar(1000) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '备注', + operator_id varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '操作人ID', + remark varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '备注', occurred_at datetime(3) NOT NULL COMMENT '业务发生时间', created_at datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间', PRIMARY KEY (id), @@ -1488,41 +1494,41 @@ CREATE TABLE ll_inventory_ledger ( KEY idx_ll_inventory_ledger_query (customer_id,overseas_warehouse_id,occurred_at), KEY idx_ll_inventory_ledger_inbound (customer_id,inbound_code,occurred_at), KEY idx_ll_inventory_ledger_business (customer_id,business_type,business_order_no) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='不可变库存变动流水'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='不可变库存变动流水'; CREATE TABLE ll_inventory_operation ( id bigint NOT NULL COMMENT '主键ID', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户租户ID', - idempotency_key varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '业务幂等键', - operation_type varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '操作类型', - request_hash varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '请求内容哈希', - status varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'PROCESSING' COMMENT '库存命令处理状态', - result_json longtext COLLATE utf8mb4_unicode_ci COMMENT '处理结果JSON', - last_error varchar(2000) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '最后错误信息', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户租户ID', + idempotency_key varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '业务幂等键', + operation_type varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '操作类型', + request_hash varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '请求内容哈希', + status varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'PROCESSING' COMMENT '库存命令处理状态', + result_json longtext COLLATE utf8mb4_0900_ai_ci COMMENT '处理结果JSON', + last_error varchar(2000) COLLATE utf8mb4_0900_ai_ci DEFAULT 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_ll_inventory_operation_key (customer_id,idempotency_key), KEY idx_ll_inventory_operation_status (customer_id,status,updated_at) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='库存命令幂等记录'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='库存命令幂等记录'; CREATE TABLE ll_inventory_position ( id bigint NOT NULL COMMENT '主键ID', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户租户ID', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户租户ID', balance_id bigint NOT NULL COMMENT '库存余额记录ID', - overseas_warehouse_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '海外仓ID', - destination_warehouse_code varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '目的仓编码快照', + overseas_warehouse_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '海外仓ID', + destination_warehouse_code varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT '目的仓编码快照', pallet_id bigint NOT NULL COMMENT '托盘ID', - pallet_no varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '托盘号', - batch_no varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '库存批次号', + pallet_no varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '托盘号', + batch_no varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT '库存批次号', warehouse_location_id bigint DEFAULT NULL COMMENT '仓库库位ID', - warehouse_location_code varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '仓库库位编码', + warehouse_location_code varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '仓库库位编码', available_packages bigint NOT NULL DEFAULT '0' COMMENT '可用箱数', hold_packages bigint NOT NULL DEFAULT '0' COMMENT '冻结箱数', allocated_packages bigint NOT NULL DEFAULT '0' COMMENT '已分配箱数', picked_packages bigint NOT NULL DEFAULT '0' COMMENT '已拣箱数', receiving_packages bigint NOT NULL DEFAULT '0' COMMENT '收货处理中箱数', - status varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'ACTIVE' COMMENT '业务状态', + status varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'ACTIVE' COMMENT '业务状态', version bigint NOT NULL DEFAULT '0' 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 '更新时间', @@ -1530,92 +1536,101 @@ CREATE TABLE ll_inventory_position ( UNIQUE KEY uk_ll_inventory_position (balance_id,pallet_id,batch_no), KEY idx_ll_inventory_position_pallet (customer_id,pallet_id), KEY idx_ll_inventory_position_location (customer_id,overseas_warehouse_id,warehouse_location_code) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='托盘库位批次库存明细'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='托盘库位批次库存明细'; CREATE TABLE ll_loading_event_inbox ( - event_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '事件全局唯一标识', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '租户/客户账号标识', - event_type varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '事件类型', + event_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '事件全局唯一标识', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '租户/客户账号标识', + event_type varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '事件类型', event_version int NOT NULL COMMENT '事件契约版本', - payload_hash varchar(71) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '规范化事件负载 SHA-256 摘要', + payload_hash varchar(71) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '规范化事件负载 SHA-256 摘要', payload_json json NOT NULL COMMENT '事件冻结负载 JSON', - status varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'RECEIVED' COMMENT '消费状态:RECEIVED/PROCESSED', + status varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'RECEIVED' COMMENT '消费状态:RECEIVED/PROCESSED', received_at datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '接收时间', processed_at datetime(3) DEFAULT NULL COMMENT '处理完成时间', - last_error varchar(1000) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '最近一次处理错误', + last_error varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '最近一次处理错误', PRIMARY KEY (event_id), UNIQUE KEY uk_ll_loading_inbox_tenant_key (customer_id,event_type,event_id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='头程装柜完成事件接收幂等箱'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='头程装柜完成事件接收幂等箱'; CREATE TABLE ll_loading_order_status_history ( id bigint NOT NULL AUTO_INCREMENT COMMENT '状态历史主键', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户数据隔离键', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户数据隔离键', order_id bigint NOT NULL COMMENT '尾程订单 ID', - from_status varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '变更前状态', - to_status varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '变更后状态', - event_id varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '触发事件 ID', - remark varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '状态变更备注', + from_status varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '变更前状态', + to_status varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '变更后状态', + event_id varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '触发事件 ID', + remark varchar(500) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '状态变更备注', created_at datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '记录时间', PRIMARY KEY (id), KEY idx_ll_loading_history_customer_order (customer_id,order_id,created_at) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='尾程订单状态变更历史'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='尾程订单状态变更历史'; CREATE TABLE ll_local_looseboard_history ( id bigint NOT NULL COMMENT '主键ID', order_id bigint NOT NULL COMMENT '本土散板订单ID', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户租户ID', - operation_type varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '操作类型:CREATE、UPDATE或STATUS', - from_status varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '变更前状态', - to_status varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '变更后状态', - operator_id varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '操作人ID', - idempotency_key varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '操作幂等键', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户租户ID', + operation_type varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '操作类型:CREATE、UPDATE或STATUS', + from_status varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '变更前状态', + to_status varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '变更后状态', + operator_id varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '操作人ID', + idempotency_key varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '操作幂等键', change_detail json DEFAULT NULL COMMENT '字段变更前后值JSON', - remark varchar(1000) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '操作备注', + remark varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '操作备注', created_at datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间', PRIMARY KEY (id), KEY idx_ll_local_looseboard_history (customer_id,order_id,created_at) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='本土散板操作流水'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='本土散板操作流水'; CREATE TABLE ll_local_looseboard_operation ( id bigint NOT NULL COMMENT '操作记录ID', order_id bigint NOT NULL COMMENT '本土散板订单ID', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户租户ID', - operation_type varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '操作类型', - idempotency_key varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '操作幂等键', - request_hash char(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '操作请求内容哈希', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户租户ID', + operation_type varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '操作类型', + idempotency_key varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '操作幂等键', + request_hash char(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '操作请求内容哈希', result_version bigint NOT NULL COMMENT '操作完成后的订单版本', - created_by varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '操作人ID', + created_by varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '操作人ID', created_at datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间', PRIMARY KEY (id), UNIQUE KEY uk_ll_local_looseboard_operation_key (customer_id,order_id,idempotency_key), KEY idx_ll_local_looseboard_operation_order (customer_id,order_id,created_at) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='本土散板订单幂等操作账'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='本土散板订单幂等操作账'; CREATE TABLE ll_local_looseboard_order ( id bigint NOT NULL COMMENT '主键ID', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户租户ID', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户租户ID', lastleg_order_id bigint DEFAULT NULL COMMENT '关联尾程主订单ID', - order_no varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '本土散板订单号', - pallet_required varchar(8) COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '是否需要打盘:YES或NO;拼车来源未提供时为空', + delivery_mode VARCHAR(32) NULL COMMENT '入仓方式:送货到仓或仓库上门取货', + operating_warehouse_id VARCHAR(128) NULL COMMENT '实际收货海外仓ID', + destination_type VARCHAR(16) NULL COMMENT '目的地类型:FBA或私人地址', + destination_postal_code VARCHAR(32) NULL COMMENT '私人目的地邮编', + delivery_contact_name VARCHAR(128) NULL COMMENT '送货到仓联系人', + delivery_contact_phone VARCHAR(64) NULL COMMENT '送货到仓联系电话', + estimated_arrival_at DATETIME(3) NULL COMMENT '预计到仓时间', + tracking_no VARCHAR(255) NULL COMMENT '物流跟踪号', + transfer_no VARCHAR(64) NULL COMMENT '关联的仓储转运收货单号', + order_no varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '本土散板订单号', + pallet_required varchar(8) COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '是否需要打盘:YES或NO;拼车来源未提供时为空', transport_type VARCHAR(32) NULL DEFAULT NULL COMMENT '接单运输安排;下单不预设', - origin varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '起运地', - origin_address varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '起运详细地址', - destination varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '目的地', - destination_address varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '目的详细地址、码头或仓号', + origin varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '起运地', + origin_address varchar(500) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '起运详细地址', + destination varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '目的地', + destination_address varchar(500) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '目的详细地址、码头或仓号', product_name VARCHAR(255) NULL COMMENT '商品名称', estimated_pallet_count INT NULL COMMENT '客户申报预计托数,不代表实际收货', package_count int NOT NULL DEFAULT '0' COMMENT '货物件数', total_weight_kg decimal(18,3) NOT NULL DEFAULT '0.000' COMMENT '总重量(千克)', total_volume_cbm decimal(18,6) NOT NULL DEFAULT '0.000000' COMMENT '总体积(立方米)', budget_amount decimal(18,2) DEFAULT NULL COMMENT '预算总价', - currency char(3) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'USD' COMMENT '预算币种', + currency char(3) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'USD' COMMENT '预算币种', origin_contact_name VARCHAR(128) NULL COMMENT '起运地联系人', origin_contact_phone VARCHAR(128) NULL COMMENT '起运地联系电话', destination_contact_name VARCHAR(128) NULL COMMENT '目的地联系人', destination_contact_phone VARCHAR(128) NULL COMMENT '目的地联系电话', pickup_at DATETIME(3) NULL COMMENT '取货时间(当地时间,不转换时区)', delivery_at DATETIME(3) NULL COMMENT '送货时间(当地时间,不转换时区)', - status varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'PENDING_ACCEPTANCE' COMMENT '本土散板订单状态', - remark varchar(1000) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '备注', - idempotency_key varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '创建幂等键', - request_hash char(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '创建请求内容哈希', - created_by varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '创建人ID', + status varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'PENDING_ACCEPTANCE' COMMENT '本土散板订单状态', + remark varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '备注', + idempotency_key varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '创建幂等键', + request_hash char(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '创建请求内容哈希', + created_by varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '创建人ID', version bigint NOT NULL DEFAULT '0' 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 '更新时间', @@ -1625,70 +1640,70 @@ CREATE TABLE ll_local_looseboard_order ( UNIQUE KEY uk_ll_local_looseboard_lastleg (lastleg_order_id), KEY idx_ll_local_looseboard_list (customer_id,status,created_at), KEY idx_ll_local_looseboard_date (customer_id,pickup_at,created_at) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='本土散板订单'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='本土散板订单'; CREATE TABLE ll_monthly_exchange_rate ( id bigint NOT NULL COMMENT '月度汇率ID', - rate_month char(7) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '汇率月份,格式yyyy-MM', - base_currency char(3) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'USD' COMMENT '基准币种', - quote_currency char(3) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'CNY' COMMENT '报价币种', + rate_month char(7) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '汇率月份,格式yyyy-MM', + base_currency char(3) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'USD' COMMENT '基准币种', + quote_currency char(3) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'CNY' COMMENT '报价币种', usd_to_cny_average decimal(18,8) NOT NULL COMMENT '美元兑人民币月平均汇率', - status varchar(16) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'ACTIVE' COMMENT '状态:ACTIVE/INACTIVE', - remark varchar(1000) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '备注', + status varchar(16) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'ACTIVE' COMMENT '状态:ACTIVE/INACTIVE', + remark varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '备注', record_version bigint NOT NULL DEFAULT '0' COMMENT '乐观锁版本', - created_by varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '创建人', - updated_by varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '更新人', + created_by varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '创建人', + updated_by varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '更新人', created_at datetime(3) NOT NULL COMMENT '创建时间', updated_at datetime(3) NOT NULL COMMENT '更新时间', PRIMARY KEY (id), UNIQUE KEY uk_ll_exchange_rate_month (rate_month), KEY idx_ll_exchange_rate_page (status,rate_month) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='美元兑人民币月度平均汇率'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='美元兑人民币月度平均汇率'; CREATE TABLE ll_monthly_exchange_rate_history ( id bigint NOT NULL COMMENT '汇率变更历史ID', exchange_rate_id bigint NOT NULL COMMENT '月度汇率ID', before_rate decimal(18,8) DEFAULT NULL COMMENT '变更前汇率', after_rate decimal(18,8) NOT NULL COMMENT '变更后汇率', - before_status varchar(16) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '变更前状态', - after_status varchar(16) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '变更后状态', - operator_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '操作人', - remark varchar(1000) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '变更说明', + before_status varchar(16) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '变更前状态', + after_status varchar(16) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '变更后状态', + operator_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '操作人', + remark varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '变更说明', created_at datetime(3) NOT NULL COMMENT '创建时间', PRIMARY KEY (id), KEY idx_ll_exchange_rate_history (exchange_rate_id,created_at) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='月度汇率变更历史'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='月度汇率变更历史'; CREATE TABLE ll_order ( id bigint NOT NULL COMMENT '雪花 ID', - order_no varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '尾程单号', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '租户/客户账号标识', - order_type varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'FIRST_LEG' COMMENT '订单类型:FIRST_LEG头程订单、RELABEL_APPLICATION换标下单、WAREHOUSE_TRANSFER仓储转运、LOCAL_LOOSEBOARD本土散板、RETURN_RELABEL退货换标、ONE_CLICK_DROPSHIP一件代发', - lx_code varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '一件代发领星入库单代码', - destination_type varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '目的类型:FBA按箱数接收、OVERSEAS逐箱扫码', - inbound_code varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '真实来源入仓码;无入仓关系的海外本土业务为空', - source_region varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '来源区域/系统', - source_inbound_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '最终实装 LOADED 分片 ID,建单幂等键', - root_inbound_id varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '根入仓单 ID', - source_order_id varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '来源订单 ID', - source_order_no varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '来源订单号', - root_order_id varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '根订单 ID', - customer_name varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '货主名称', - source_warehouse_id varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '来源国内装柜仓 ID 快照', - source_warehouse_name varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '来源国内装柜仓名称快照', - overseas_warehouse_id varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '海外接柜仓ID;新建尾程订单默认洛杉矶仓', - overseas_warehouse_code varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '海外接柜仓业务编码快照', - overseas_warehouse_name varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '海外接柜仓名称快照', + order_no varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '尾程单号', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '租户/客户账号标识', + order_type varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'FIRST_LEG' COMMENT '订单类型:FIRST_LEG头程订单、RELABEL_APPLICATION换标下单、WAREHOUSE_TRANSFER仓储转运、LOCAL_LOOSEBOARD本土散板、RETURN_RELABEL退货换标、ONE_CLICK_DROPSHIP一件代发', + lx_code varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '一件代发领星入库单代码', + destination_type varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '目的类型:FBA按箱数接收、OVERSEAS逐箱扫码', + inbound_code varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '真实来源入仓码;无入仓关系的海外本土业务为空', + source_region varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '来源区域/系统', + source_inbound_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '最终实装 LOADED 分片 ID,建单幂等键', + root_inbound_id varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '根入仓单 ID', + source_order_id varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '来源订单 ID', + source_order_no varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '来源订单号', + root_order_id varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '根订单 ID', + customer_name varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '货主名称', + source_warehouse_id varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '来源国内装柜仓 ID 快照', + source_warehouse_name varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '来源国内装柜仓名称快照', + overseas_warehouse_id varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '海外接柜仓ID;新建尾程订单默认洛杉矶仓', + overseas_warehouse_code varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '海外接柜仓业务编码快照', + overseas_warehouse_name varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '海外接柜仓名称快照', receiving_warehouse_version int NOT NULL DEFAULT '0' COMMENT '柜级接柜仓分配乐观锁版本', - transfer_warehouse_id varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '转入目的仓ID', - transfer_warehouse_code varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '转入目的仓编码', - transfer_warehouse_name varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '转入目的仓名称', - fba_warehouse_code varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'FBA 仓库编码', - container_no varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '集装箱号', + transfer_warehouse_id varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '转入目的仓ID', + transfer_warehouse_code varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '转入目的仓编码', + transfer_warehouse_name varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '转入目的仓名称', + fba_warehouse_code varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'FBA 仓库编码', + container_no varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '集装箱号', abnormal tinyint NOT NULL DEFAULT '0' COMMENT '是否异常:0否1是', detained tinyint NOT NULL DEFAULT '0' COMMENT '是否暂扣:0否1是', hold_chargeable tinyint NOT NULL DEFAULT '0' COMMENT '当前暂扣是否计费:0否1是', - hold_reason varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '当前暂扣原因;解除后为空', + hold_reason varchar(500) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '当前暂扣原因;解除后为空', manual_node_override tinyint NOT NULL DEFAULT '0' COMMENT '是否启用单票人工计划节点覆盖:0否1是,启用后不再继承柜级节点', version int NOT NULL DEFAULT '0' COMMENT '尾程订单命令乐观锁版本', pallet_count int NOT NULL DEFAULT '0' COMMENT '托数', @@ -1703,24 +1718,24 @@ CREATE TABLE ll_order ( actual_arrival_at datetime(3) DEFAULT NULL COMMENT '实际到港时间', estimated_warehouse_at datetime(3) DEFAULT NULL COMMENT '预计到仓时间', arrival_time_version int NOT NULL DEFAULT '0' COMMENT '柜级预计到仓时间乐观锁版本', - inbound_order_no varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '入库单号', - outbound_order_no varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '出库单号', - delivery_order_no varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '派送单号', - relabel_order_no varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '换标单号', - relabel_status varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '换标状态', - pallet_merge_flag varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '合托标记', - pallet_merge_batch_id varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '平台预标记合托分组号', - current_status varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'PENDING_ASSIGNMENT' COMMENT '当前业务状态:PENDING_ASSIGNMENT待分配、PENDING_INBOUND待入库、INBOUND_COMPLETED入库完成、PENDING_DELIVERY待派送、DELIVERING派送中、DELIVERED派送完成', - sub_order_status varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '子单据状态汇总', - private_remark varchar(1000) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '备注(仅自己)', - customer_remark varchar(1000) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '备注(客户)', - warehouse_remark varchar(1000) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '备注(仓库)', - source_event_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '来源事件 ID', - source_loading_plan_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '来源装柜计划 ID', - source_container_booking_id varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '来源头程货柜预订ID', + inbound_order_no varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '入库单号', + outbound_order_no varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '出库单号', + delivery_order_no varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '派送单号', + relabel_order_no varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '换标单号', + relabel_status varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '换标状态', + pallet_merge_flag varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '合托标记', + pallet_merge_batch_id varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '平台预标记合托分组号', + current_status varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'PENDING_ASSIGNMENT' COMMENT '当前业务状态:PENDING_ASSIGNMENT待分配、PENDING_INBOUND待入库、INBOUND_COMPLETED入库完成、PENDING_DELIVERY待派送、DELIVERING派送中、DELIVERED派送完成', + sub_order_status varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '子单据状态汇总', + private_remark varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '备注(仅自己)', + customer_remark varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '备注(客户)', + warehouse_remark varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '备注(仓库)', + source_event_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '来源事件 ID', + source_loading_plan_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '来源装柜计划 ID', + source_container_booking_id varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '来源头程货柜预订ID', source_loading_plan_version bigint DEFAULT NULL COMMENT '来源装柜计划版本', source_handover_version bigint DEFAULT NULL COMMENT '交接版本', - source_payload_hash varchar(71) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '来源事件负载摘要', + source_payload_hash varchar(71) COLLATE utf8mb4_0900_ai_ci NOT 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), @@ -1729,7 +1744,7 @@ CREATE TABLE ll_order ( KEY idx_ll_order_container (customer_id,container_no), KEY idx_ll_order_list (customer_id,current_status,created_at), KEY idx_ll_order_container_progress (source_container_booking_id,estimated_warehouse_at,created_at,id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='尾程订单主表'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='尾程订单主表'; CREATE TABLE ll_appointment_followup ( id BIGINT NOT NULL COMMENT '预约筹划跟进记录ID', @@ -1757,29 +1772,29 @@ CREATE TABLE ll_appointment_followup ( CREATE TABLE ll_order_command_record ( id bigint NOT NULL COMMENT '尾程订单命令幂等记录ID', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户数据隔离键', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户数据隔离键', order_id bigint NOT NULL COMMENT '尾程订单ID', - action varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '命令动作:MANUAL_PLAN、HOLD、MANUAL_NODES', - operator_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '平台操作人ID', - idempotency_key varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户端幂等键', - request_hash varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '规范化请求SHA-256摘要', - result_ref varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '结果业务引用', - result_json longtext COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '首次成功响应JSON快照', + action varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '命令动作:MANUAL_PLAN、HOLD、MANUAL_NODES', + operator_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '平台操作人ID', + idempotency_key varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户端幂等键', + request_hash varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '规范化请求SHA-256摘要', + result_ref varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '结果业务引用', + result_json longtext COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '首次成功响应JSON快照', result_version int NOT NULL COMMENT '命令成功后的订单版本', created_at datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '首次成功时间', PRIMARY KEY (id), UNIQUE KEY uk_ll_order_command_key (customer_id,order_id,action,operator_id,idempotency_key), KEY idx_ll_order_command_history (customer_id,order_id,created_at) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='尾程订单写命令幂等与结果审计记录'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='尾程订单写命令幂等与结果审计记录'; CREATE TABLE ll_order_detail ( id bigint NOT NULL AUTO_INCREMENT COMMENT '订单明细主键', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户数据隔离键', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户数据隔离键', order_id bigint NOT NULL COMMENT '尾程订单 ID', - source_inbound_detail_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '来源入仓明细 ID', - sku_code varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'SKU 编码', - product_name_zh varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '中文品名', - product_name_en varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '英文品名', + source_inbound_detail_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '来源入仓明细 ID', + sku_code varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'SKU 编码', + product_name_zh varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '中文品名', + product_name_en varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '英文品名', package_count int NOT NULL DEFAULT '0' COMMENT '箱数', product_quantity int DEFAULT NULL COMMENT '数量', pallet_count int DEFAULT NULL COMMENT '托数', @@ -1787,106 +1802,106 @@ CREATE TABLE ll_order_detail ( net_weight_kg decimal(18,4) DEFAULT NULL COMMENT '净重(千克)', dimensional_weight_kg decimal(18,4) DEFAULT NULL COMMENT '体积重量(千克)', volume_cbm decimal(18,4) DEFAULT NULL COMMENT '体积(立方米)', - destination_warehouse_id varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '目的仓 ID', - destination_warehouse_code varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '目的仓编码', - destination_warehouse_name varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '目的仓名称', - fba_tracking_no varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'FBA跟踪号', - shipment_no varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '货件编号', - shipping_mark varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '唛头', + destination_warehouse_id varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '目的仓 ID', + destination_warehouse_code varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '目的仓编码', + destination_warehouse_name varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '目的仓名称', + fba_tracking_no varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'FBA跟踪号', + shipment_no varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '货件编号', + shipping_mark varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '唛头', PRIMARY KEY (id), UNIQUE KEY uk_ll_detail_source (order_id,source_inbound_detail_id), KEY idx_ll_detail_order (order_id), KEY idx_ll_detail_customer_order (customer_id,order_id,id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='尾程订单实装商品冻结明细'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='尾程订单实装商品冻结明细'; CREATE TABLE ll_order_fee ( id bigint NOT NULL COMMENT '费用明细主键', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户数据隔离键', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户数据隔离键', order_id bigint NOT NULL COMMENT '尾程订单ID', - fee_item varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '费用项目', - currency varchar(3) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '币种代码', + fee_item varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '费用项目', + currency varchar(3) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '币种代码', amount decimal(18,2) NOT NULL COMMENT '费用金额', - status varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'PENDING_REVIEW' COMMENT '费用状态:PENDING_REVIEW待审核、APPROVED审核通过、REJECTED已驳回', - supplier_description varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '供应商或费用说明', - created_by varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '创建操作人', + status varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'PENDING_REVIEW' COMMENT '费用状态:PENDING_REVIEW待审核、APPROVED审核通过、REJECTED已驳回', + supplier_description varchar(500) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '供应商或费用说明', + created_by varchar(128) COLLATE utf8mb4_0900_ai_ci NOT 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), KEY idx_ll_order_fee_customer_order (customer_id,order_id,created_at) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='尾程订单费用明细'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='尾程订单费用明细'; CREATE TABLE ll_order_hold_history ( id bigint NOT NULL COMMENT '尾程订单暂扣历史ID', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户数据隔离键', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户数据隔离键', order_id bigint NOT NULL COMMENT '尾程订单ID', held tinyint NOT NULL COMMENT '操作后是否暂扣:0否1是', chargeable tinyint NOT NULL COMMENT '本次暂扣是否计费:0否1是', - reason varchar(500) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '暂扣或解除原因', + reason varchar(500) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '暂扣或解除原因', order_version int NOT NULL COMMENT '操作成功后的订单版本', - operator_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '操作人ID', - operator_name varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '操作人名称', - idempotency_key varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户端幂等键', + operator_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '操作人ID', + operator_name varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '操作人名称', + idempotency_key varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户端幂等键', created_at datetime(3) NOT NULL COMMENT '操作时间', PRIMARY KEY (id), UNIQUE KEY uk_ll_order_hold_key (customer_id,order_id,operator_id,idempotency_key), KEY idx_ll_order_hold_history (customer_id,order_id,created_at) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='尾程订单暂扣与解除不可变历史'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='尾程订单暂扣与解除不可变历史'; CREATE TABLE ll_order_manual_node ( id bigint NOT NULL COMMENT '尾程订单人工节点ID', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户数据隔离键', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户数据隔离键', order_id bigint NOT NULL COMMENT '尾程订单ID', - node_code varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '八行人工展示节点稳定代码,与Carrier标准节点隔离', + node_code varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '八行人工展示节点稳定代码,与Carrier标准节点隔离', node_at datetime(3) DEFAULT NULL COMMENT '人工填写节点时间,允许为空', - source varchar(16) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'MANUAL' COMMENT '节点来源,固定MANUAL', + source varchar(16) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'MANUAL' COMMENT '节点来源,固定MANUAL', order_version int NOT NULL COMMENT '最近编辑后的订单版本', - updated_by varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '最近操作人ID', + updated_by varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '最近操作人ID', updated_at datetime(3) NOT NULL COMMENT '最近编辑时间', PRIMARY KEY (id), UNIQUE KEY uk_ll_order_manual_node (customer_id,order_id,node_code), KEY idx_ll_order_manual_node_order (customer_id,order_id,node_at) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='尾程订单当前人工节点覆盖事实'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='尾程订单当前人工节点覆盖事实'; CREATE TABLE ll_order_manual_node_history ( id bigint NOT NULL COMMENT '尾程订单人工节点历史ID', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户数据隔离键', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户数据隔离键', order_id bigint NOT NULL COMMENT '尾程订单ID', - nodes_json longtext COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '本次全量人工节点JSON快照', + nodes_json longtext COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '本次全量人工节点JSON快照', order_version int NOT NULL COMMENT '操作成功后的订单版本', - operator_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '操作人ID', - operator_name varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '操作人名称', - idempotency_key varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户端幂等键', + operator_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '操作人ID', + operator_name varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '操作人名称', + idempotency_key varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户端幂等键', created_at datetime(3) NOT NULL COMMENT '操作时间', PRIMARY KEY (id), UNIQUE KEY uk_ll_order_manual_node_history_key (customer_id,order_id,operator_id,idempotency_key), KEY idx_ll_order_manual_node_history (customer_id,order_id,created_at) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='尾程订单人工节点覆盖不可变历史'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='尾程订单人工节点覆盖不可变历史'; CREATE TABLE ll_dispatch_daily_sequence ( business_date DATE NOT NULL COMMENT '排车编号业务日期(Asia/Shanghai)', last_sequence INT NOT NULL COMMENT '当日已分配排车流水号,范围0001至9999', PRIMARY KEY(business_date) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='排车单号每日独立原子流水'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='排车单号每日独立原子流水'; CREATE TABLE ll_order_operation_log ( id bigint NOT NULL COMMENT '操作记录主键', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户数据隔离键', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户数据隔离键', order_id bigint NOT NULL COMMENT '尾程订单ID', - operation_node varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '操作节点', - operator_name varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '操作人名称', - result varchar(500) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '处理结果', + operation_node varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '操作节点', + operator_name varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '操作人名称', + result varchar(500) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '处理结果', created_at datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '操作时间', PRIMARY KEY (id), KEY idx_ll_order_operation_customer_order (customer_id,order_id,created_at) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='尾程订单操作记录'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='尾程订单操作记录'; CREATE TABLE ll_order_source_pallet ( id bigint NOT NULL COMMENT '来源托盘快照ID', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户隔离键', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户隔离键', order_id bigint NOT NULL COMMENT '尾程订单ID', - source_pallet_no varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '头程冻结托盘码', + source_pallet_no varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '头程冻结托盘码', sequence_no int NOT NULL COMMENT '订单内稳定顺序', - source_event_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '来源装柜完成事件ID', + source_event_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '来源装柜完成事件ID', created_at datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间', PRIMARY KEY (id), UNIQUE KEY uk_ll_source_pallet_no (source_pallet_no), @@ -1894,47 +1909,47 @@ CREATE TABLE ll_order_source_pallet ( KEY idx_ll_source_pallet_order (customer_id,order_id), KEY fk_ll_source_pallet_order (order_id), CONSTRAINT fk_ll_source_pallet_order FOREIGN KEY (order_id) REFERENCES ll_order (id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='头程交接的不可变托盘码快照'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='头程交接的不可变托盘码快照'; CREATE TABLE ll_order_status_history ( id bigint NOT NULL COMMENT '订单状态历史主键', customer_id bigint NOT NULL COMMENT '租户 ID', order_id bigint NOT NULL COMMENT '订单 ID', - from_status varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '变更前状态', - to_status varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '变更后状态', - reason varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '变更原因', + from_status varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '变更前状态', + to_status varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '变更后状态', + reason varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '变更原因', created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '记录时间', PRIMARY KEY (id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='尾程订单状态历史'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='尾程订单状态历史'; CREATE TABLE ll_outbound_order ( id bigint NOT NULL COMMENT '主键ID', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户租户ID', - outbound_no varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '出库单号', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户租户ID', + outbound_no varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '出库单号', dispatch_order_id bigint DEFAULT NULL COMMENT '派车单ID,一件代发为空', - dispatch_no varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '派车单号,一件代发为空', - overseas_warehouse_id varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '海外仓ID', - status varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'WAIT_OUTBOUND' COMMENT '业务状态', + dispatch_no varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '派车单号,一件代发为空', + overseas_warehouse_id varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '海外仓ID', + status varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'WAIT_OUTBOUND' COMMENT '业务状态', package_count int NOT NULL DEFAULT '0' COMMENT '箱数', pallet_count int NOT NULL DEFAULT '0' COMMENT '托盘数', gross_weight_kg DECIMAL(18,3) NULL COMMENT '毛重(千克),来源未确认时为空', volume_weight_kg decimal(18,3) NOT NULL DEFAULT '0.000' COMMENT '材积重(千克)', volume_cbm DECIMAL(18,4) NULL COMMENT '体积(立方米),来源未确认时为空', fee_coverage_json JSON NULL COMMENT '本次发运引用的原费用版本、实际货物及明确服务覆盖确认快照', - idempotency_key varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '业务幂等键', - created_by varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '创建人ID', + idempotency_key varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '业务幂等键', + created_by varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT 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 '更新时间', - outbound_type varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'DISPATCH_DELIVERY' COMMENT '出库类型:派送转出库、一件代发', - lx_codes varchar(1000) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '关联一件代发领星入库单代码汇总', - recipient_info varchar(1000) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '一件代发收件信息', - request_hash char(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '创建或转派送业务请求摘要,用于幂等内容校验', + outbound_type varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'DISPATCH_DELIVERY' COMMENT '出库类型:派送转出库、一件代发', + lx_codes varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '关联一件代发领星入库单代码汇总', + recipient_info varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '一件代发收件信息', + request_hash char(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '创建或转派送业务请求摘要,用于幂等内容校验', PRIMARY KEY (id), UNIQUE KEY uk_ll_outbound_no (outbound_no), UNIQUE KEY uk_ll_outbound_idempotency (customer_id,idempotency_key), UNIQUE KEY uk_ll_outbound_dispatch (customer_id,dispatch_order_id), KEY idx_ll_outbound_list (customer_id,status,created_at) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='尾程出库单'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='尾程出库单'; CREATE TABLE ll_dispatch_command ( id BIGINT NOT NULL COMMENT '持久命令内部主键', @@ -1950,7 +1965,7 @@ CREATE TABLE ll_dispatch_command ( PRIMARY KEY (id), UNIQUE KEY uk_dispatch_command_key (idempotency_key), KEY idx_dispatch_command_history (dispatch_id,created_at,id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='整车排车公共持久命令账本'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='整车排车公共持久命令账本'; CREATE TABLE ll_dispatch_delivery_arrangement ( id BIGINT NOT NULL COMMENT '安排事实内部主键', @@ -1969,7 +1984,7 @@ CREATE TABLE ll_dispatch_delivery_arrangement ( UNIQUE KEY uk_dispatch_arrangement_dispatch (dispatch_order_id), UNIQUE KEY uk_dispatch_arrangement_key (arrangement_key), UNIQUE KEY uk_dispatch_arrangement_command (original_command_key) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='整车排车唯一派送安排事实'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='整车排车唯一派送安排事实'; CREATE TABLE ll_delivery_facts_changed ( id BIGINT NOT NULL COMMENT '持久信号内部主键', @@ -1990,7 +2005,7 @@ CREATE TABLE ll_delivery_facts_changed ( UNIQUE KEY uk_delivery_fact_version (customer_id,delivery_id,delivery_version), KEY idx_delivery_fact_due (state,next_attempt_at,id), KEY idx_delivery_fact_dispatch (dispatch_id,state,id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='正常签收事实变化的本地持久待汇总信号'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='正常签收事实变化的本地持久待汇总信号'; CREATE TABLE ll_outbound_pallet_item ( return_sku_quantity BIGINT NULL COMMENT '本来源退货出库SKU件数,空表示整箱出库', @@ -2002,50 +2017,50 @@ CREATE TABLE ll_outbound_pallet_item ( source_return_carton_id BIGINT NULL COMMENT '本行实际退货换标来源箱ID', carton_marks VARCHAR(4000) NULL COMMENT '本行真实拣货箱唛快照', id bigint NOT NULL COMMENT '主键ID', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户租户ID', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户租户ID', outbound_order_id bigint NOT NULL COMMENT '出库单ID', warehouse_pallet_id bigint DEFAULT NULL COMMENT '仓库实物托盘ID', - pallet_no varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '托盘号', - destination_warehouse_id varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '目的仓ID', - destination_warehouse_code varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '目的仓编码快照', - destination_warehouse_name varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '目的仓名称快照', + pallet_no varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '托盘号', + destination_warehouse_id varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '目的仓ID', + destination_warehouse_code varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '目的仓编码快照', + destination_warehouse_name varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '目的仓名称快照', package_count int NOT NULL DEFAULT '0' COMMENT '箱数', gross_weight_kg DECIMAL(18,3) NULL COMMENT '毛重(千克),来源未确认时为空', volume_weight_kg decimal(18,3) NOT NULL DEFAULT '0.000' COMMENT '材积重(千克)', volume_cbm DECIMAL(18,4) NULL COMMENT '体积(立方米),来源未确认时为空', picked_package_count int NOT NULL DEFAULT '0' COMMENT '已拣箱数', - status varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'WAIT_PICK' COMMENT '拣货状态', - sku_code varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '一件代发SKU', - order_refs varchar(2000) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '关联订单信息', - inbound_code varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '入仓单号快照', + status varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'WAIT_PICK' COMMENT '拣货状态', + sku_code varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '一件代发SKU', + order_refs varchar(2000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '关联订单信息', + inbound_code varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '入仓单号快照', created_at datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间', PRIMARY KEY (id), UNIQUE KEY uk_ll_outbound_pallet (customer_id,outbound_order_id,warehouse_pallet_id,inbound_code,sku_code), UNIQUE KEY uk_outbound_source_attribution (customer_id,source_attribution_id), KEY idx_ll_outbound_pallet_list (customer_id,outbound_order_id), KEY idx_ll_outbound_pallet_destination (customer_id,destination_warehouse_code) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='出库托盘明细'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='出库托盘明细'; CREATE TABLE ll_outbound_pda_pick ( id bigint NOT NULL COMMENT '主键ID', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户数据隔离键', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户数据隔离键', task_id bigint NOT NULL COMMENT 'PDA任务ID', outbound_pallet_item_id bigint NOT NULL COMMENT '出库托盘明细ID', inventory_position_id bigint DEFAULT NULL COMMENT '库存仓位ID;退货箱直接出库时为空', packages bigint NOT NULL COMMENT '拣货箱数', - idempotency_key varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '拣货幂等键', + idempotency_key varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '拣货幂等键', picked_at datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '拣货时间', PRIMARY KEY (id), UNIQUE KEY uk_ll_outbound_pda_pick_key (customer_id,idempotency_key) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='一件代发出库PDA托盘拣货记录'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='一件代发出库PDA托盘拣货记录'; CREATE TABLE ll_outbound_pda_task ( id bigint NOT NULL COMMENT '主键ID', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户租户ID', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户租户ID', outbound_order_id bigint NOT NULL COMMENT '出库单ID', - task_no varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'PDA任务号', - status varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'PENDING' COMMENT '任务状态:待拣货、拣货中、已完成', - completion_idempotency_key varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '完成命令幂等键', + task_no varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'PDA任务号', + status varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'PENDING' COMMENT '任务状态:待拣货、拣货中、已完成', + completion_idempotency_key varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '完成命令幂等键', pushed_at datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '推送时间', completed_at datetime(3) DEFAULT NULL COMMENT '完成时间', created_at datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间', @@ -2054,101 +2069,57 @@ CREATE TABLE ll_outbound_pda_task ( UNIQUE KEY uk_ll_outbound_pda_task_order (customer_id,outbound_order_id), UNIQUE KEY uk_ll_outbound_pda_task_no (task_no), UNIQUE KEY uk_ll_outbound_pda_complete_key (customer_id,completion_idempotency_key) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='一件代发出库PDA拣货任务'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='一件代发出库PDA拣货任务'; -CREATE TABLE ll_overseas_procurement_fee ( - purpose VARCHAR(32) NULL COMMENT '费用业务用途;由履约节点确定', - confirmation_origin VARCHAR(16) NOT NULL DEFAULT 'BUSINESS' COMMENT '核定入口:BUSINESS业务节点、FINANCIAL财务操作', - fee_version INT NULL COMMENT '同用途费用确认版本', - active TINYINT(1) NOT NULL DEFAULT 1 COMMENT '是否当前有效版本', - determination VARCHAR(24) NULL COMMENT 'POSITIVE正额、ZERO零元、NOT_APPLICABLE不适用', - original_currency CHAR(3) NULL COMMENT '核定原币CNY或USD', - original_amount DECIMAL(20,2) NULL COMMENT '核定原币金额', - exchange_rate DECIMAL(20,6) NULL COMMENT '已确认汇率快照', - exchange_rate_month CHAR(7) NULL COMMENT '已确认汇率所属月份', - business_date DATE NULL COMMENT '实际费用业务日期', - wallet_business_id BIGINT NULL COMMENT '资金冻结对应采购费用记录', - wallet_business_no VARCHAR(128) NULL COMMENT '资金冻结业务号;实际国际运费引用原预估', - wallet_hold_id VARCHAR(64) NULL COMMENT 'Finance冻结引用', - wallet_ledger_id VARCHAR(64) NULL COMMENT 'Finance实际处理流水引用', - settled_command_event_id VARCHAR(64) NULL COMMENT '成功实扣或退款的资金命令事件', - source_fee_id BIGINT NULL COMMENT '已实扣费用来源ID', - parent_fee_id BIGINT NULL COMMENT '原费用或预估引用', - idempotency_key VARCHAR(128) NULL COMMENT '退款请求幂等键,独立于业务单号', - request_hash CHAR(64) NULL COMMENT '确认请求摘要', - id bigint NOT NULL COMMENT '主键ID', - order_id bigint NOT NULL COMMENT '采购订单ID', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户ID', - fee_no varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '费用业务号', - fee_item_code varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '费用项编码', - fee_item_name varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '费用项名称', - amount decimal(20,2) NOT NULL COMMENT 'CNY金额', - currency char(3) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'CNY' COMMENT '费用币种', - voucher_file_id bigint NULL COMMENT '凭证文件ID', - voucher_file_name varchar(255) COLLATE utf8mb4_unicode_ci NULL COMMENT '凭证文件名', - remark varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '说明', - wallet_status varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '资金状态', - wallet_error_code varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '资金错误码', - wallet_error_message varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '资金错误说明', - wallet_command_event_id varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '当前资金指令事件ID', - created_by varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT 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 '更新时间', - CONSTRAINT chk_procurement_confirmation_origin CHECK(confirmation_origin IN('BUSINESS','FINANCIAL')), - UNIQUE KEY uk_ll_procurement_fee_version(order_id,purpose,fee_item_code,fee_version), - UNIQUE KEY uk_ll_procurement_refund_request(order_id,purpose,idempotency_key), - PRIMARY KEY (id), - UNIQUE KEY uk_ll_procurement_fee_no (fee_no), - KEY idx_ll_procurement_fee_order (customer_id,order_id,created_at), - CONSTRAINT chk_ll_procurement_fee_amount CHECK ((amount >= 0)), - CONSTRAINT chk_ll_procurement_fee_currency CHECK ((currency = _utf8mb4'CNY')) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='海外采购费用明细及资金生命周期'; CREATE TABLE ll_overseas_procurement_file ( id bigint NOT NULL COMMENT '主键ID', order_id bigint NOT NULL COMMENT '采购订单ID', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户ID', - category varchar(16) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'RECEIPT实收、VOUCHER费用、LABEL面单、PURCHASE采购、BUDGET预算、EXCEPTION异常凭证', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户ID', + category varchar(16) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'RECEIPT实收、VOUCHER费用、LABEL面单、PURCHASE采购、BUDGET预算、EXCEPTION异常凭证', file_id bigint NOT NULL COMMENT 'File服务文件ID', - file_name varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '文件名快照', - content_type varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '媒体类型', + file_name varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '文件名快照', + content_type varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '媒体类型', size_bytes bigint NOT NULL DEFAULT '0' COMMENT '文件大小', - created_by varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '上传人', + created_by varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '上传人', created_at datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '上传时间', PRIMARY KEY (id), UNIQUE KEY uk_ll_procurement_file (order_id,file_id), KEY idx_ll_procurement_file_order (customer_id,order_id,category,created_at), CONSTRAINT chk_ll_procurement_file_category CHECK (category IN ('RECEIPT','VOUCHER','LABEL','PURCHASE','BUDGET','EXCEPTION')) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='海外采购签收文件及费用凭证'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='海外采购签收文件及费用凭证'; CREATE TABLE ll_overseas_procurement_order ( + procurement_direction VARCHAR(32) NOT NULL DEFAULT 'OVERSEAS_TO_DOMESTIC' COMMENT '采购方向:海外采购回国内/国内采购发海外', id bigint NOT NULL COMMENT '主键ID', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户租户ID', - customer_name varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '客户名称快照', - order_no varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '订单号', - purchase_category varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '采购品类', - product_name varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '产品名称', - product_specification varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '产品规格', - product_url varchar(2000) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '产品链接', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户租户ID', + customer_name varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '客户名称快照', + order_no varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '订单号', + purchase_category varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '采购品类', + product_name varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '产品名称', + product_specification varchar(500) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '产品规格', + product_url varchar(2000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '产品链接', purchase_quantity bigint NOT NULL COMMENT '采购数量', budget_unit_price decimal(18,2) DEFAULT NULL COMMENT '预算单价', - currency char(3) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'USD' COMMENT '币种', - receiving_address varchar(1000) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '收货地址', - contact_name varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '联系人姓名', - contact_phone varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '联系人电话', - purchase_warehouse_id varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '采购入仓仓库ID', - purchase_warehouse_name varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '采购入仓仓库名称', + currency char(3) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'USD' COMMENT '币种', + receiving_address varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '收货地址', + contact_name varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '联系人姓名', + contact_phone varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '联系人电话', + purchase_warehouse_id varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '采购入仓仓库ID', + purchase_warehouse_name varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '采购入仓仓库名称', inbound_quantity bigint NOT NULL DEFAULT '0' COMMENT '采购单据累计入库数量', estimated_arrival_at datetime(3) DEFAULT NULL COMMENT '预计到达时间', - status varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'PENDING_REVIEW' COMMENT '业务状态', - remark varchar(1000) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '备注', - idempotency_key varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '业务幂等键', - request_hash char(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '请求内容哈希', - created_by varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '创建人ID', - reviewed_by varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '审核人ID', - review_remark varchar(1000) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '审核备注', - review_idempotency_key varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '审核幂等键', - review_request_hash char(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '审核请求内容哈希', + status varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'PENDING_REVIEW' COMMENT '业务状态', + remark varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '备注', + idempotency_key varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '业务幂等键', + request_hash char(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '请求内容哈希', + created_by varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '创建人ID', + reviewed_by varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '审核人ID', + review_remark varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '审核备注', + customer_rejection_reason VARCHAR(1000) NULL COMMENT '客户采购结果驳回原因', + rejected_photo_ids TEXT NULL COMMENT '本轮客户驳回的收货照片主键快照', + review_idempotency_key varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '审核幂等键', + review_request_hash char(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '审核请求内容哈希', reviewed_at datetime(3) DEFAULT NULL COMMENT '审核时间', version bigint NOT NULL DEFAULT '0' COMMENT '乐观锁版本', created_at datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间', @@ -2158,180 +2129,180 @@ CREATE TABLE ll_overseas_procurement_order ( UNIQUE KEY uk_ll_overseas_procurement_order_key (customer_id,idempotency_key), KEY idx_ll_overseas_procurement_list (customer_id,status,created_at), KEY idx_ll_overseas_procurement_warehouse (customer_id,purchase_warehouse_id,created_at) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='海外采购订单'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='海外采购订单'; CREATE TABLE ll_overseas_procurement_status_history ( id bigint NOT NULL COMMENT '主键ID', order_id bigint NOT NULL COMMENT '订单ID', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户租户ID', - operation_type varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '操作类型:CREATE、REVIEW或UPDATE', - from_status varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '变更前状态', - to_status varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '变更后状态', - operator_id varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '操作人ID', - idempotency_key varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '操作幂等键', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户租户ID', + operation_type varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '操作类型:CREATE、REVIEW或UPDATE', + from_status varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '变更前状态', + to_status varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '变更后状态', + operator_id varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '操作人ID', + idempotency_key varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '操作幂等键', change_detail json DEFAULT NULL COMMENT '字段变更前后值JSON', - remark varchar(1000) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '备注', + remark varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '备注', created_at datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间', PRIMARY KEY (id), KEY idx_ll_overseas_procurement_history (customer_id,order_id,created_at) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='海外采购操作流水'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='海外采购操作流水'; CREATE TABLE ll_overseas_quote ( id bigint NOT NULL COMMENT '报价ID', - name varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '报价名称', + name varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '报价名称', warehouse_id bigint NOT NULL COMMENT '国内平台海外合作仓ID', - warehouse_code varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '合作仓编码快照', - warehouse_name varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '合作仓名称快照', - country_code varchar(2) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '国家二字码快照', - provider_name varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '报价方', - currency char(3) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '币种', + warehouse_code varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '合作仓编码快照', + warehouse_name varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '合作仓名称快照', + country_code varchar(2) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '国家二字码快照', + provider_name varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '报价方', + currency char(3) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '币种', effective_start date NOT NULL COMMENT '生效开始日期', effective_end date NOT NULL COMMENT '生效结束日期', - version_code varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '业务版本号', - status varchar(16) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'INACTIVE' COMMENT '状态:ACTIVE/INACTIVE', - remark varchar(1000) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '备注', + version_code varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '业务版本号', + status varchar(16) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'INACTIVE' COMMENT '状态:ACTIVE/INACTIVE', + remark varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '备注', record_version bigint NOT NULL DEFAULT '0' COMMENT '乐观锁版本', - created_by varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '创建人', - updated_by varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '更新人', + created_by varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '创建人', + updated_by varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '更新人', created_at datetime(3) NOT NULL COMMENT '创建时间', updated_at datetime(3) NOT NULL COMMENT '更新时间', PRIMARY KEY (id), UNIQUE KEY uk_ll_quote_warehouse_provider_version (warehouse_id,provider_name,version_code), KEY idx_ll_quote_page (status,updated_at,id), KEY idx_ll_quote_effective (warehouse_id,provider_name,currency,effective_start,effective_end) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='海外仓报价主表'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='海外仓报价主表'; CREATE TABLE ll_overseas_quote_fee ( id bigint NOT NULL COMMENT '费用明细ID', quote_id bigint NOT NULL COMMENT '报价ID', sequence_no int NOT NULL COMMENT '显示顺序', - item varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '费用项目', - billing_unit varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '计费单位', + item varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '费用项目', + billing_unit varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '计费单位', unit_price decimal(18,4) NOT NULL COMMENT '单价', minimum_charge decimal(18,4) NOT NULL DEFAULT '0.0000' COMMENT '最低收费', - remark varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '备注', + remark varchar(500) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '备注', PRIMARY KEY (id), UNIQUE KEY uk_ll_quote_fee_sequence (quote_id,sequence_no), KEY idx_ll_quote_fee_quote (quote_id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='海外仓报价仓库费用'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='海外仓报价仓库费用'; CREATE TABLE ll_overseas_quote_transit_price ( id bigint NOT NULL COMMENT '中转价格ID', quote_id bigint NOT NULL COMMENT '报价ID', sequence_no int NOT NULL COMMENT '显示顺序', - fba_warehouse_code varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'FBA仓编码', - delivery_method varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '报价运输方式', + fba_warehouse_code varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'FBA仓编码', + delivery_method varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '报价运输方式', estimated_miles decimal(18,4) NOT NULL DEFAULT '0.0000' COMMENT '预估英里数', price decimal(18,4) NOT NULL COMMENT '报价金额', minimum_charge decimal(18,4) NOT NULL DEFAULT '0.0000' COMMENT '最低收费', - remark varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '备注', + remark varchar(500) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '备注', PRIMARY KEY (id), UNIQUE KEY uk_ll_quote_transit_sequence (quote_id,sequence_no), KEY idx_ll_quote_transit_quote (quote_id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='海外仓报价中转价格明细'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='海外仓报价中转价格明细'; CREATE TABLE ll_pda_inventory_move ( id bigint NOT NULL COMMENT '移库操作ID', - move_no varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '移库操作单号', + move_no varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '移库操作单号', overseas_warehouse_id bigint NOT NULL COMMENT 'Token冻结的海外实际仓ID', pallet_id bigint NOT NULL COMMENT '整托移库的实物托盘ID', - pallet_code varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '实物托盘码快照', + pallet_code varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '实物托盘码快照', source_location_id bigint NOT NULL COMMENT '来源库位ID', - source_location_code varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '来源库位码快照', + source_location_code varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '来源库位码快照', target_location_id bigint NOT NULL COMMENT '目标库位ID', - target_location_code varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '目标库位码快照', + target_location_code varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '目标库位码快照', customer_count int NOT NULL COMMENT '托盘涉及客户数', position_count int NOT NULL COMMENT '迁移库存位置数', package_count bigint NOT NULL COMMENT '迁移总箱数', pallet_version_before int NOT NULL COMMENT '迁移前托盘版本', pallet_version_after int DEFAULT NULL COMMENT '迁移后托盘版本', - operator_key varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'PDA操作账号标识', - reason varchar(500) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '移库原因', - idempotency_key varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '移库幂等键', - request_hash varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '移库请求哈希', - status varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '移库处理状态', - result_json longtext COLLATE utf8mb4_unicode_ci COMMENT '成功响应JSON', + operator_key varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'PDA操作账号标识', + reason varchar(500) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '移库原因', + idempotency_key varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '移库幂等键', + request_hash varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '移库请求哈希', + status varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '移库处理状态', + result_json longtext COLLATE utf8mb4_0900_ai_ci COMMENT '成功响应JSON', created_at datetime(3) NOT NULL COMMENT '创建时间', completed_at datetime(3) DEFAULT NULL COMMENT '完成时间', PRIMARY KEY (id), UNIQUE KEY uk_ll_pda_inventory_move_no (move_no), UNIQUE KEY uk_ll_pda_inventory_move_key (overseas_warehouse_id,operator_key,idempotency_key), KEY idx_ll_pda_inventory_move_pallet (overseas_warehouse_id,pallet_id,created_at) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='海外PDA同仓整托移库记录'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='海外PDA同仓整托移库记录'; CREATE TABLE ll_pda_operation_history ( id bigint NOT NULL COMMENT 'PDA作业历史ID', overseas_warehouse_id bigint NOT NULL COMMENT 'Token冻结的海外实际仓ID', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '作业涉及的客户标识', - operator_key varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'PDA操作账号标识', - operation_type varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '作业类型', - business_key varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '作业业务键', - status varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '作业结果状态', - idempotency_key varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '写操作幂等键', - request_hash varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '写请求内容哈希', - summary varchar(1000) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '作业结果摘要', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '作业涉及的客户标识', + operator_key varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'PDA操作账号标识', + operation_type varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '作业类型', + business_key varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '作业业务键', + status varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '作业结果状态', + idempotency_key varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '写操作幂等键', + request_hash varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '写请求内容哈希', + summary varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '作业结果摘要', occurred_at datetime(3) NOT NULL COMMENT '作业发生时间', created_at datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间', PRIMARY KEY (id), UNIQUE KEY uk_ll_pda_history_idempotency (operator_key,idempotency_key), KEY idx_ll_pda_history_operator (overseas_warehouse_id,operator_key,occurred_at), KEY idx_ll_pda_history_business (overseas_warehouse_id,operation_type,business_key) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='海外PDA真实作业历史'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='海外PDA真实作业历史'; CREATE TABLE ll_pda_pallet_operation ( id bigint NOT NULL COMMENT 'PDA托盘作业ID', overseas_warehouse_id bigint NOT NULL COMMENT 'Token冻结的海外实际仓ID', - operator_key varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'PDA操作账号标识', - idempotency_key varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '设备幂等键', - operation_type varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '托盘作业类型', - request_hash varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '请求内容摘要', - status varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '处理状态:PROCESSING、COMPLETED、FAILED_COMPENSATED、COMPENSATION_REQUIRED', - result_json longtext COLLATE utf8mb4_unicode_ci COMMENT '成功响应快照', - remote_transitions_json longtext COLLATE utf8mb4_unicode_ci COMMENT '实物托盘状态变更回执', - last_error varchar(2000) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '最后失败原因', + operator_key varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'PDA操作账号标识', + idempotency_key varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '设备幂等键', + operation_type varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '托盘作业类型', + request_hash varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '请求内容摘要', + status varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '处理状态:PROCESSING、COMPLETED、FAILED_COMPENSATED、COMPENSATION_REQUIRED', + result_json longtext COLLATE utf8mb4_0900_ai_ci COMMENT '成功响应快照', + remote_transitions_json longtext COLLATE utf8mb4_0900_ai_ci COMMENT '实物托盘状态变更回执', + last_error varchar(2000) COLLATE utf8mb4_0900_ai_ci DEFAULT 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_ll_pda_pallet_idempotency (operator_key,idempotency_key), KEY idx_ll_pda_pallet_warehouse (overseas_warehouse_id,status,updated_at) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='海外PDA托盘作业幂等、补偿与结果记录'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='海外PDA托盘作业幂等、补偿与结果记录'; CREATE TABLE ll_pda_stocktake_action ( id bigint NOT NULL COMMENT '盘点动作ID', overseas_warehouse_id bigint NOT NULL COMMENT 'Token冻结的海外实际仓ID', - operator_key varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'PDA操作账号标识', - idempotency_key varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '动作幂等键', - action_type varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '盘点动作类型', + operator_key varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'PDA操作账号标识', + idempotency_key varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '动作幂等键', + action_type varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '盘点动作类型', task_id bigint DEFAULT NULL COMMENT '盘点任务ID', - request_hash varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '动作请求哈希', - status varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '动作处理状态', - result_json longtext COLLATE utf8mb4_unicode_ci COMMENT '成功响应JSON', + request_hash varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '动作请求哈希', + status varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '动作处理状态', + result_json longtext COLLATE utf8mb4_0900_ai_ci COMMENT '成功响应JSON', created_at datetime(3) NOT NULL COMMENT '创建时间', updated_at datetime(3) NOT NULL COMMENT '更新时间', PRIMARY KEY (id), UNIQUE KEY uk_ll_pda_stocktake_action (overseas_warehouse_id,operator_key,idempotency_key), KEY idx_ll_pda_stocktake_action_task (task_id,created_at) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='海外PDA盘点动作幂等记录'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='海外PDA盘点动作幂等记录'; CREATE TABLE ll_pda_stocktake_item ( id bigint NOT NULL COMMENT '盘点明细ID', task_id bigint NOT NULL COMMENT '盘点任务ID', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '库存所属客户标识', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '库存所属客户标识', balance_id bigint NOT NULL COMMENT '库存余额ID', position_id bigint NOT NULL COMMENT '库存位置ID', - sku_code varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'SKU快照', - product_name varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '品名快照', - inbound_code varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '入仓码快照', + sku_code varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'SKU快照', + product_name varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '品名快照', + inbound_code varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '入仓码快照', pallet_id bigint NOT NULL COMMENT '实物托盘ID', - pallet_code varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '实物托盘码快照', + pallet_code varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '实物托盘码快照', location_id bigint NOT NULL COMMENT '库位ID快照', - location_code varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '库位码快照', + location_code varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '库位码快照', book_packages bigint NOT NULL COMMENT '任务创建时账面总箱数', actual_packages bigint DEFAULT NULL COMMENT '现场实盘总箱数', difference_packages bigint DEFAULT NULL COMMENT '实盘与账面箱数差异', - status varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '盘点明细状态', + status varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '盘点明细状态', position_version bigint NOT NULL COMMENT '任务创建时库存位置版本', - counted_by varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '盘点操作账号标识', + counted_by varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '盘点操作账号标识', version bigint NOT NULL DEFAULT '0' COMMENT '明细乐观锁版本', counted_at datetime(3) DEFAULT NULL COMMENT '盘点时间', created_at datetime(3) NOT NULL COMMENT '创建时间', @@ -2339,24 +2310,24 @@ CREATE TABLE ll_pda_stocktake_item ( PRIMARY KEY (id), UNIQUE KEY uk_ll_pda_stocktake_item (task_id,position_id), KEY idx_ll_pda_stocktake_item_task (task_id,status,id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='海外PDA库存盘点位置明细'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='海外PDA库存盘点位置明细'; CREATE TABLE ll_pda_stocktake_task ( id bigint NOT NULL COMMENT '盘点任务ID', - task_no varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '盘点任务号', + task_no varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '盘点任务号', overseas_warehouse_id bigint NOT NULL COMMENT 'Token冻结的海外实际仓ID', - scope_type varchar(16) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '盘点范围类型LOCATION或PALLET', + scope_type varchar(16) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '盘点范围类型LOCATION或PALLET', scope_resource_id bigint NOT NULL COMMENT '盘点库位或托盘资源ID', - scope_code varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '盘点库位码或托盘码快照', - status varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '盘点任务状态', + scope_code varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '盘点库位码或托盘码快照', + status varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '盘点任务状态', position_count int NOT NULL COMMENT '库存位置总数', counted_count int NOT NULL DEFAULT '0' COMMENT '已盘库存位置数', difference_count int NOT NULL DEFAULT '0' COMMENT '存在差异的库存位置数', - created_by varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '创建操作账号标识', - submitted_by varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '提交操作账号标识', - submit_reason varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '盘点差异提交原因', - idempotency_key varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '创建幂等键', - request_hash varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '创建请求哈希', + created_by varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '创建操作账号标识', + submitted_by varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '提交操作账号标识', + submit_reason varchar(500) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '盘点差异提交原因', + idempotency_key varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '创建幂等键', + request_hash varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '创建请求哈希', version bigint NOT NULL DEFAULT '0' COMMENT '乐观锁版本', created_at datetime(3) NOT NULL COMMENT '创建时间', updated_at datetime(3) NOT NULL COMMENT '更新时间', @@ -2365,36 +2336,36 @@ CREATE TABLE ll_pda_stocktake_task ( UNIQUE KEY uk_ll_pda_stocktake_no (task_no), UNIQUE KEY uk_ll_pda_stocktake_create (overseas_warehouse_id,created_by,idempotency_key), KEY idx_ll_pda_stocktake_list (overseas_warehouse_id,status,created_at) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='海外PDA库存盘点任务'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='海外PDA库存盘点任务'; CREATE TABLE ll_receiving_container_operation ( id bigint NOT NULL COMMENT '柜号收货操作ID', session_id bigint NOT NULL COMMENT '柜号签收会话ID', - operator_key varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '操作账号唯一标识', - action varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '操作动作', - idempotency_key varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户端幂等键', - request_hash varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '请求内容哈希', - request_json longtext COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '请求内容快照', - result_json longtext COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '响应内容快照', + operator_key varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '操作账号唯一标识', + action varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '操作动作', + idempotency_key varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户端幂等键', + request_hash varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '请求内容哈希', + request_json longtext COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '请求内容快照', + result_json longtext COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '响应内容快照', created_at datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间', PRIMARY KEY (id), UNIQUE KEY uk_ll_receiving_operation (operator_key,idempotency_key), KEY idx_ll_receiving_operation_session (session_id,created_at) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='海外PDA柜号收货幂等审计记录'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='海外PDA柜号收货幂等审计记录'; CREATE TABLE ll_receiving_container_session ( id bigint NOT NULL COMMENT '柜号签收会话ID', - source_container_booking_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '稳定来源柜预订ID', - container_fact_hash varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '领取时整柜订单与计划事实摘要', - container_no varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '集装箱号', + source_container_booking_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '稳定来源柜预订ID', + container_fact_hash varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '领取时整柜订单与计划事实摘要', + container_no varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '集装箱号', overseas_warehouse_id bigint NOT NULL COMMENT '平台预分配并由操作员Token匹配的海外接柜仓ID', - overseas_warehouse_code varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '海外接柜仓业务编码快照', - overseas_warehouse_name varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '仓库服务校验后的海外接柜仓名称快照', - operator_key varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '操作账号唯一标识', - operator_name varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '操作账号名称', - active_operator_key varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '未完成会话平台操作账号全局唯一锁键', - active_container_key varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '未完成会话柜号全局唯一锁键', - status varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'WAIT_SIGN' COMMENT '会话状态:WAIT_SIGN、SIGNED、RECEIVING、COMPLETED、COMPLETED_WITH_DIFFERENCE', + overseas_warehouse_code varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '海外接柜仓业务编码快照', + overseas_warehouse_name varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '仓库服务校验后的海外接柜仓名称快照', + operator_key varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '操作账号唯一标识', + operator_name varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '操作账号名称', + active_operator_key varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '未完成会话平台操作账号全局唯一锁键', + active_container_key varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '未完成会话柜号全局唯一锁键', + status varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'WAIT_SIGN' COMMENT '会话状态:WAIT_SIGN、SIGNED、RECEIVING、COMPLETED、COMPLETED_WITH_DIFFERENCE', sign_photos json DEFAULT NULL COMMENT '签收卸柜照片角色与文件ID列表', prepared_at datetime(3) DEFAULT NULL COMMENT '选择柜号时间', signed_at datetime(3) DEFAULT NULL COMMENT '签收时间', @@ -2408,32 +2379,32 @@ CREATE TABLE ll_receiving_container_session ( UNIQUE KEY uk_ll_receiving_active_container (active_container_key), KEY idx_ll_receiving_container (container_no,status), KEY idx_ll_receiving_operator (operator_key,created_at) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='海外PDA柜号签收收货会话'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='海外PDA柜号签收收货会话'; CREATE TABLE ll_receiving_fba_pallet_count ( id bigint NOT NULL COMMENT 'FBA托盘现场累计箱数ID', session_id bigint NOT NULL COMMENT '柜号签收会话ID', - group_key varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '柜号与FBA仓组成的收货组键', - pallet_code varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '现有FBA托盘码', + group_key varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '柜号与FBA仓组成的收货组键', + pallet_code varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '现有FBA托盘码', actual_packages int NOT 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_ll_receiving_fba_count (session_id,group_key,pallet_code), KEY idx_ll_receiving_fba_group (session_id,group_key) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='海外PDA普通FBA托盘现场累计与超收标记'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='海外PDA普通FBA托盘现场累计与超收标记'; CREATE TABLE ll_receiving_sku_receipt ( id bigint NOT NULL COMMENT 'FBA SKU实收进度ID', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户账号标识', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户账号标识', session_id bigint NOT NULL COMMENT '柜号签收会话ID', plan_id bigint NOT NULL COMMENT '入仓计划ID', line_id bigint NOT NULL COMMENT '入仓计划明细ID', pallet_id bigint NOT NULL COMMENT '计划托盘ID', order_id bigint NOT NULL COMMENT '尾程订单ID', - inbound_code varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '入仓码快照', - sku_code varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'SKU编码快照', - product_name varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '产品名称快照', + inbound_code varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '入仓码快照', + sku_code varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'SKU编码快照', + product_name varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '产品名称快照', received_packages int NOT NULL COMMENT 'SKU实收箱数', 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 '更新时间', @@ -2442,64 +2413,64 @@ CREATE TABLE ll_receiving_sku_receipt ( UNIQUE KEY uk_ll_receiving_sku_pallet (customer_id,line_id,pallet_id,sku_code), KEY idx_ll_receiving_sku_session (customer_id,session_id,line_id), KEY idx_ll_receiving_sku_plan (customer_id,plan_id,line_id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='海外PDA柜号FBA SKU实收进度'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='海外PDA柜号FBA SKU实收进度'; CREATE TABLE ll_receiving_warehouse_operation ( id bigint NOT NULL COMMENT '柜级接柜仓修改操作ID', - source_container_booking_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '稳定来源柜预订ID', - idempotency_key varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '平台请求幂等键', - request_hash varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '规范化修改请求SHA-256摘要', + source_container_booking_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '稳定来源柜预订ID', + idempotency_key varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '平台请求幂等键', + request_hash varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '规范化修改请求SHA-256摘要', result_version int NOT NULL COMMENT '修改成功后的柜级分配版本', - operator_name varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '平台操作人名称', - remark varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '接柜仓修改备注', + operator_name varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '平台操作人名称', + remark varchar(500) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '接柜仓修改备注', created_at datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '操作时间', PRIMARY KEY (id), UNIQUE KEY uk_ll_receiving_warehouse_idempotency (idempotency_key), KEY idx_ll_receiving_warehouse_container (source_container_booking_id,created_at) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='柜级海外接柜仓修改幂等审计记录'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='柜级海外接柜仓修改幂等审计记录'; CREATE TABLE ll_relabel_application ( id bigint NOT NULL COMMENT '主键ID', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户租户ID', - relabel_no varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '换标申请单号', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户租户ID', + relabel_no varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '换标申请单号', lastleg_order_id bigint NOT NULL COMMENT '尾程订单ID', - customer_order_no varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户订单号', - overseas_warehouse_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '海外仓ID', + customer_order_no varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户订单号', + overseas_warehouse_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '海外仓ID', label_deadline datetime(3) NOT NULL COMMENT '换标截止时间', - contact_name varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '联系人姓名', - contact_phone varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '联系人电话', - delivery_method varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '派送方式:TRUCK平台卡车、EXPRESS快递、PICKUP自提、AMAZON_PICKUP亚马逊自提、CUSTOMER_PICKUP客户自提;仅平台卡车进入预排车', + contact_name varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '联系人姓名', + contact_phone varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '联系人电话', + delivery_method varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '派送方式:TRUCK平台卡车、EXPRESS快递、PICKUP自提、AMAZON_PICKUP亚马逊自提、CUSTOMER_PICKUP客户自提;仅平台卡车进入预排车', print_shipping_label tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否需要打印面单', - shipping_label_file_id varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '客户上传面单文件ID', - shipping_label_file_name varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '客户上传面单文件名', - shipping_label_file_url varchar(1000) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '客户上传面单下载地址', - fulfillment_status varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'WAIT_RELABEL' COMMENT '换标后履约状态', - outbound_order_no varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '自提或快递生成的换标出库单号', - customer_confirmation_key varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '客户确认完成幂等键', + shipping_label_file_id varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '客户上传面单文件ID', + shipping_label_file_name varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '客户上传面单文件名', + shipping_label_file_url varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '客户上传面单下载地址', + fulfillment_status varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'WAIT_RELABEL' COMMENT '换标后履约状态', + outbound_order_no varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '自提或快递生成的换标出库单号', + customer_confirmation_key varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '客户确认完成幂等键', customer_confirmed_at datetime(3) DEFAULT NULL COMMENT '客户确认换标完成时间', - remark varchar(1000) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '备注', - total_file_id varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '总换标PDF文件ID', - total_file_name varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '总换标PDF文件名', - total_file_url varchar(1000) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '总换标PDF预览地址', - carton_total_file_id varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '箱唛总文件ID', - carton_total_file_name varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '箱唛总文件名', - carton_total_file_url varchar(1000) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '箱唛总文件地址', - sku_total_file_id varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'SKU总文件ID', - sku_total_file_name varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'SKU总文件名', - sku_total_file_url varchar(1000) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'SKU总文件地址', - status varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'PENDING_PROCESSING' COMMENT '业务状态', + remark varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '备注', + total_file_id varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '总换标PDF文件ID', + total_file_name varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '总换标PDF文件名', + total_file_url varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '总换标PDF预览地址', + carton_total_file_id varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '箱唛总文件ID', + carton_total_file_name varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '箱唛总文件名', + carton_total_file_url varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '箱唛总文件地址', + sku_total_file_id varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'SKU总文件ID', + sku_total_file_name varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'SKU总文件名', + sku_total_file_url varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'SKU总文件地址', + status varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'PENDING_PROCESSING' COMMENT '业务状态', item_count int NOT NULL DEFAULT '0' COMMENT '明细数量', total_package_count int NOT NULL DEFAULT '0' COMMENT '总箱数', notified_package_count int NOT NULL DEFAULT '0' COMMENT '已通知箱数', charge_amount decimal(20,2) NOT NULL DEFAULT '0.00' COMMENT '换标费用CNY', - charge_currency char(3) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'CNY' COMMENT '费用币种', - wallet_status varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'FREEZE_PENDING' COMMENT '资金状态', - wallet_error_code varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '资金错误码', - wallet_error_message varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '资金错误说明', - wallet_command_event_id varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '当前资金指令事件ID', - idempotency_key varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '业务幂等键', - request_hash char(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '请求内容哈希', - created_by varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '创建人ID', + charge_currency char(3) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'CNY' COMMENT '费用币种', + wallet_status varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'FREEZE_PENDING' COMMENT '资金状态', + wallet_error_code varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '资金错误码', + wallet_error_message varchar(500) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '资金错误说明', + wallet_command_event_id varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '当前资金指令事件ID', + idempotency_key varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '业务幂等键', + request_hash char(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '请求内容哈希', + created_by varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT 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 '更新时间', PRIMARY KEY (id), @@ -2509,103 +2480,103 @@ CREATE TABLE ll_relabel_application ( KEY idx_ll_relabel_application_list (customer_id,status,created_at), KEY idx_ll_relabel_application_customer (customer_id,customer_order_no), KEY idx_ll_relabel_application_warehouse (customer_id,overseas_warehouse_id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='客户换标申请单'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='客户换标申请单'; CREATE TABLE ll_relabel_item ( return_carton_id bigint DEFAULT NULL COMMENT '退货换箱唛来源退货库存箱ID', - source_carton_mark varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '退货换箱唛原箱唛快照', - new_carton_mark varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '退货换箱唛新箱唛', + source_carton_mark varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '退货换箱唛原箱唛快照', + new_carton_mark varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '退货换箱唛新箱唛', id bigint NOT NULL COMMENT '主键ID', application_id bigint NOT NULL COMMENT '申请单ID', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户租户ID', - sku_code varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'SKU编码(选填)', - product_image_file_id varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '产品图片文件ID', - product_image_file_name varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '产品图片文件名', - product_image_url varchar(1000) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '产品图片地址', - product_name_zh varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '产品中文名称', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户租户ID', + sku_code varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'SKU编码(选填)', + product_image_file_id varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '产品图片文件ID', + product_image_file_name varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '产品图片文件名', + product_image_url varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '产品图片地址', + product_name_zh varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '产品中文名称', total_package_count int NOT NULL COMMENT '总箱数', notified_package_count int NOT NULL COMMENT '已通知箱数', - shipment_no varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '货件编号(无FBA去向时为空)', - tracking_code varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '物流跟踪编码', - new_sku_code varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '换标后SKU编码;批量SKU换标使用子明细JSON时为空', - label_requirement varchar(1000) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '换标要求', - remark varchar(1000) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '货物明细备注', - relabel_file_id varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '换标文件ID', - relabel_file_name varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '换标文件名', - relabel_file_url varchar(1000) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '换标文件地址', - carton_mark varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '客户自有箱唛', - fba_carton_mark varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '关联FBA箱唛', - destination_warehouse_id varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '该箱目的海外仓ID', - destination_fba_warehouse_code varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '可选FBA目的仓编码(与海外操作仓分离)', - operation_type varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'CARTON_LABEL' COMMENT '换标模式:箱唛、SKU或组合换标', + shipment_no varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '货件编号(无FBA去向时为空)', + tracking_code varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '物流跟踪编码', + new_sku_code varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '换标后SKU编码;批量SKU换标使用子明细JSON时为空', + label_requirement varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '换标要求', + remark varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '货物明细备注', + relabel_file_id varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '换标文件ID', + relabel_file_name varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '换标文件名', + relabel_file_url varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '换标文件地址', + carton_mark varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '客户自有箱唛', + fba_carton_mark varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '关联FBA箱唛', + destination_warehouse_id varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '该箱目的海外仓ID', + destination_fba_warehouse_code varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '可选FBA目的仓编码(与海外操作仓分离)', + operation_type varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'CARTON_LABEL' COMMENT '换标模式:箱唛、SKU或组合换标', sku_relabel_items_json json DEFAULT NULL COMMENT '同箱SKU换标子明细快照(数量按件)', source_inventory_balance_id bigint DEFAULT NULL COMMENT '原库存余额ID(预入仓换标任务可为空)', source_inventory_position_id bigint DEFAULT NULL COMMENT '原库存库位ID(预入仓换标任务可为空)', source_inbound_order_id bigint DEFAULT NULL COMMENT '原入仓单ID(预入仓换标任务可为空)', - source_inbound_order_no varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '原入仓单号快照', - source_inbound_code varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '原进仓编码快照', - source_warehouse_id varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '原海外中转仓ID;预入仓批量SKU换标时为空', - source_warehouse_code varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '原海外中转仓编码快照', - source_warehouse_name varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '原海外中转仓名称快照', + source_inbound_order_no varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '原入仓单号快照', + source_inbound_code varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '原进仓编码快照', + source_warehouse_id varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '原海外中转仓ID;预入仓批量SKU换标时为空', + source_warehouse_code varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '原海外中转仓编码快照', + source_warehouse_name varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '原海外中转仓名称快照', created_at datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间', PRIMARY KEY (id), KEY idx_ll_relabel_item_application (customer_id,application_id), KEY idx_ll_relabel_item_sku (customer_id,sku_code), KEY idx_ll_relabel_item_destination (customer_id,destination_warehouse_id,application_id), KEY idx_ll_relabel_item_source_position (customer_id,source_inventory_position_id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='换标货物冻结明细'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='换标货物冻结明细'; CREATE TABLE ll_relabel_pda_history ( id bigint NOT NULL COMMENT '换标PDA任务历史ID', task_id bigint NOT NULL COMMENT '换标PDA任务ID', application_id bigint NOT NULL COMMENT '换标申请单ID', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户数据隔离键', - action varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '现场动作类型', - from_status varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '动作前任务状态', - to_status varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '动作后任务状态', - operator_key varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '操作账号标识', - operator_name varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '操作员名称快照', - detail_json longtext COLLATE utf8mb4_unicode_ci COMMENT '动作详情JSON', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户数据隔离键', + action varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '现场动作类型', + from_status varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '动作前任务状态', + to_status varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '动作后任务状态', + operator_key varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '操作账号标识', + operator_name varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '操作员名称快照', + detail_json longtext COLLATE utf8mb4_0900_ai_ci COMMENT '动作详情JSON', created_at datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间', PRIMARY KEY (id), KEY idx_ll_relabel_pda_history_task (customer_id,task_id,created_at,id), KEY idx_ll_relabel_pda_history_application (customer_id,application_id,created_at) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='海外PDA换标任务不可变操作历史'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='海外PDA换标任务不可变操作历史'; CREATE TABLE ll_relabel_pda_operation ( id bigint NOT NULL COMMENT '换标PDA幂等操作ID', task_id bigint NOT NULL COMMENT '换标PDA任务ID', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户数据隔离键', - operator_key varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '请求操作账号标识', - action varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '幂等操作动作', - idempotency_key varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '设备生成的幂等键', - request_hash char(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '请求内容哈希', - result_json longtext COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '成功响应快照JSON', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户数据隔离键', + operator_key varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '请求操作账号标识', + action varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '幂等操作动作', + idempotency_key varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '设备生成的幂等键', + request_hash char(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '请求内容哈希', + result_json longtext COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '成功响应快照JSON', created_at datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间', PRIMARY KEY (id), UNIQUE KEY uk_ll_relabel_pda_operation_key (operator_key,idempotency_key), KEY idx_ll_relabel_pda_operation_task (customer_id,task_id,created_at) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='海外PDA换标写操作幂等记录'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='海外PDA换标写操作幂等记录'; CREATE TABLE ll_relabel_pda_task ( id bigint NOT NULL COMMENT '换标PDA任务ID', application_id bigint NOT NULL COMMENT '换标申请单ID', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户数据隔离键', - relabel_no varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '换标申请单号快照', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户数据隔离键', + relabel_no varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '换标申请单号快照', overseas_warehouse_id bigint NOT NULL COMMENT 'PDA令牌冻结的海外实际作业仓ID', - status varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '任务状态:PROCESSING、WAIT_REVIEW或COMPLETED', - operator_key varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '认领操作账号标识', - operator_name varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '认领操作员名称快照', - reviewer_key varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '复核账号标识', - reviewer_name varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '复核员名称快照', + status varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '任务状态:PROCESSING、WAIT_REVIEW或COMPLETED', + operator_key varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '认领操作账号标识', + operator_name varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '认领操作员名称快照', + reviewer_key varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '复核账号标识', + reviewer_name varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '复核员名称快照', expected_packages int NOT NULL COMMENT '申请单通知的应换标箱数', relabelled_packages int NOT NULL DEFAULT '0' COMMENT '已正常换标箱数', skipped_packages int NOT NULL DEFAULT '0' COMMENT '已说明原因跳过箱数', exception_packages int NOT NULL DEFAULT '0' COMMENT '已登记异常箱数', completion_photo_file_ids json DEFAULT NULL COMMENT '换标完成现场照片文件ID列表', - completion_remark varchar(1000) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '完成提交备注', - review_decision varchar(16) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '最近复核结论:APPROVE或REWORK', - review_remark varchar(1000) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '最近复核备注', + completion_remark varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '完成提交备注', + review_decision varchar(16) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '最近复核结论:APPROVE或REWORK', + review_remark varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '最近复核备注', claimed_at datetime(3) NOT NULL COMMENT '任务认领时间', submitted_at datetime(3) DEFAULT NULL COMMENT '提交复核时间', reviewed_at datetime(3) DEFAULT NULL COMMENT '最近复核时间', @@ -2617,53 +2588,53 @@ CREATE TABLE ll_relabel_pda_task ( UNIQUE KEY uk_ll_relabel_pda_task_no (relabel_no), KEY idx_ll_relabel_pda_task_warehouse (overseas_warehouse_id,status,updated_at), KEY idx_ll_relabel_pda_task_operator (operator_key,status,updated_at) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='海外PDA换标现场作业任务'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='海外PDA换标现场作业任务'; CREATE TABLE ll_relabel_pda_unit_fact ( id bigint NOT NULL COMMENT '逐件换标事实ID', task_id bigint NOT NULL COMMENT '换标PDA任务ID', application_id bigint NOT NULL COMMENT '换标申请单ID', item_id bigint NOT NULL COMMENT '换标申请明细ID', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户数据隔离键', - old_label varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '扫描的实物旧标签或箱标', - new_label varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '扫描的换标后新标签', - result varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '逐件结果:RELABELLED、SKIPPED或EXCEPTION', - exception_code varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '异常编码', - reason varchar(1000) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '跳过或异常原因', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户数据隔离键', + old_label varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '扫描的实物旧标签或箱标', + new_label varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '扫描的换标后新标签', + result varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '逐件结果:RELABELLED、SKIPPED或EXCEPTION', + exception_code varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '异常编码', + reason varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '跳过或异常原因', quantity int NOT NULL DEFAULT '1' COMMENT '本次事实处理件数,箱级作业固定为1', - operator_key varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '操作账号标识', - operator_name varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '操作员名称快照', + operator_key varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '操作账号标识', + operator_name varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '操作员名称快照', occurred_at datetime(3) NOT NULL COMMENT '逐件事实发生时间', PRIMARY KEY (id), UNIQUE KEY uk_ll_relabel_pda_old_label (old_label), KEY idx_ll_relabel_pda_unit_task (customer_id,task_id,occurred_at,id), KEY idx_ll_relabel_pda_unit_item (customer_id,application_id,item_id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='海外PDA逐件换标扫码与异常事实'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='海外PDA逐件换标扫码与异常事实'; CREATE TABLE ll_relabel_status_history ( id bigint NOT NULL COMMENT '主键ID', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户租户ID', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户租户ID', application_id bigint NOT NULL COMMENT '申请单ID', - from_status varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '变更前状态', - to_status varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '变更后状态', - operator_id varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '操作人ID', - remark varchar(1000) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '备注', + from_status varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '变更前状态', + to_status varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '变更后状态', + operator_id varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '操作人ID', + remark varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '备注', created_at datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间', PRIMARY KEY (id), KEY idx_ll_relabel_status_history_application (customer_id,application_id,created_at) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='换标状态变更历史'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='换标状态变更历史'; -- 退货出库按SKU固化扣减前后快照,与PDA拣货同事务提交;不修改历史箱内容。 CREATE TABLE ll_return_inventory_outbound_movement ( id BIGINT NOT NULL COMMENT '退货出库SKU流水ID', outbound_pick_id BIGINT NOT NULL COMMENT '同事务PDA出库拣货记录ID', - customer_id VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '库存所属客户ID和隔离键', - overseas_warehouse_id VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '实际出库海外仓ID', + customer_id VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '库存所属客户ID和隔离键', + overseas_warehouse_id VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '实际出库海外仓ID', carton_id BIGINT NOT NULL COMMENT '真实退货库存箱ID', carton_mark VARCHAR(128) NOT NULL COMMENT '冻结的真实退货箱业务码', - outbound_no VARCHAR(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '本次退货出库业务单号', - sku_code VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '本次出库SKU编码快照', return_sku_quantity BIGINT NULL COMMENT '退货单SKU本来源计划出库件数快照,NULL为历史整箱出库', - product_name_zh VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL COMMENT '中文品名快照', + outbound_no VARCHAR(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '本次退货出库业务单号', + sku_code VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '本次出库SKU编码快照', return_sku_quantity BIGINT NULL COMMENT '退货单SKU本来源计划出库件数快照,NULL为历史整箱出库', + product_name_zh VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL COMMENT '中文品名快照', quantity BIGINT NOT NULL COMMENT '本箱该SKU实际出库件数,始终为正', total_before BIGINT NOT NULL COMMENT '客户仓SKU出库前实物总数量', total_after BIGINT NOT NULL COMMENT '客户仓SKU出库后实物总数量', @@ -2679,14 +2650,14 @@ CREATE TABLE ll_return_inventory_outbound_movement ( CONSTRAINT chk_ll_return_outbound_movement_delta CHECK(quantity>0 AND total_after>=0 AND available_after>=0 AND total_before-total_after=quantity AND available_before-available_after=quantity AND cartons_after>=0 AND cartons_before-cartons_after IN (0,1) AND total_after>=available_after) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='不可变退货出库SKU库存流水'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='不可变退货出库SKU库存流水'; CREATE TABLE ll_return_inventory_balance ( id bigint NOT NULL COMMENT '退货库存余额ID', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户ID和隔离键', - overseas_warehouse_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '海外仓ID', - sku_code varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'SKU编码', - product_name_zh varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '中文品名', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户ID和隔离键', + overseas_warehouse_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '海外仓ID', + sku_code varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'SKU编码', + product_name_zh varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '中文品名', total_quantity bigint NOT NULL DEFAULT '0' COMMENT '实物总库存,仅实际出库扣减', available_quantity bigint NOT NULL COMMENT '未合箱可用上架数量', carton_quantity bigint NOT NULL COMMENT '包含该SKU的可用箱数', @@ -2695,28 +2666,28 @@ CREATE TABLE ll_return_inventory_balance ( PRIMARY KEY (id), UNIQUE KEY uk_ll_return_inventory_balance (customer_id,overseas_warehouse_id,sku_code), CONSTRAINT chk_ll_return_inventory_balance_quantity CHECK (((total_quantity >= available_quantity) and (available_quantity >= 0) and (carton_quantity >= 0))) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='退货库存按客户海外仓SKU汇总余额'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='退货库存按客户海外仓SKU汇总余额'; CREATE TABLE ll_return_inventory_carton ( original_packaging TINYINT(1) NULL COMMENT '是否使用原箱原包装', label_status varchar(32) DEFAULT NULL COMMENT 'WAIT_LABEL待贴标 CONFIRMED已扫码确认标签', - new_carton_mark varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '换标后新箱唛', + new_carton_mark varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '换标后新箱唛', id bigint NOT NULL COMMENT '退货库存箱ID', - customer_id varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户ID和隔离键', - overseas_warehouse_id varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '海外仓ID', + customer_id varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户ID和隔离键', + overseas_warehouse_id varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '海外仓ID', source_receipt_id bigint DEFAULT NULL COMMENT '来源PDA签收单ID', - source_batch_no text COLLATE utf8mb4_unicode_ci COMMENT '实际消耗来源签收批次号,多个批次逗号分隔', - source_return_no text COLLATE utf8mb4_unicode_ci COMMENT '实际消耗来源退货单号,多个单号逗号分隔', - pallet_code varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '签收时绑定的托盘码', - carton_mark varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '退货箱号和箱唛编码', - fba_carton_mark varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'FBA箱唛', - destination_warehouse_code varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '目的仓编码', - destination_warehouse_name varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '目的仓名称', + source_batch_no text COLLATE utf8mb4_0900_ai_ci COMMENT '实际消耗来源签收批次号,多个批次逗号分隔', + source_return_no text COLLATE utf8mb4_0900_ai_ci COMMENT '实际消耗来源退货单号,多个单号逗号分隔', + pallet_code varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '签收时绑定的托盘码', + carton_mark varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '退货箱号和箱唛编码', + fba_carton_mark varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'FBA箱唛', + destination_warehouse_code varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '目的仓编码', + destination_warehouse_name varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '目的仓名称', relabel_application_id bigint DEFAULT NULL COMMENT '绑定换标申请单ID', - relabel_no varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '绑定换标单号', - relabel_status varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '换标状态', + relabel_no varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '绑定换标单号', + relabel_status varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '换标状态', relabel_completed_at datetime(3) DEFAULT NULL COMMENT '换标完成时间', - status varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'AVAILABLE可用或MERGED已合箱', + status varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'AVAILABLE可用或MERGED已合箱', count_status VARCHAR(16) NOT NULL DEFAULT 'CONFIRMED' COMMENT '箱内清点状态 PENDING待清点 CONFIRMED已确认', counted_at DATETIME(3) NULL COMMENT '箱内清点确认时间', counted_by VARCHAR(128) NULL COMMENT '箱内清点确认操作人', @@ -2739,14 +2710,14 @@ CREATE TABLE ll_return_inventory_carton ( location_code VARCHAR(128) NULL COMMENT '退货箱当前库位', putaway_at DATETIME(3) NULL COMMENT '退货箱上架时间', source_unit_type VARCHAR(32) NULL COMMENT 'PALLET或BOX实物箱 PARCEL散SKU' -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='退货实物库存箱及箱唛'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='退货实物库存箱及箱唛'; CREATE TABLE ll_return_inventory_carton_item ( id bigint NOT NULL COMMENT '退货库存箱SKU明细ID', carton_id bigint NOT NULL COMMENT '退货库存箱ID', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户ID和隔离键', - sku_code varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'SKU编码', - product_name_zh varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '中文品名', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户ID和隔离键', + sku_code varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'SKU编码', + product_name_zh varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '中文品名', quantity bigint NOT NULL COMMENT '该SKU箱内数量', relabelled_quantity BIGINT NOT NULL DEFAULT 0 COMMENT '当前SKU已经换标的实物件数,不可二次换标', PRIMARY KEY (id), @@ -2754,14 +2725,14 @@ CREATE TABLE ll_return_inventory_carton_item ( KEY idx_ll_return_inventory_carton_item_customer (customer_id,sku_code), CONSTRAINT chk_ll_return_inventory_carton_item_quantity CHECK ((quantity >= 0)), packing_reserved_quantity BIGINT NOT NULL DEFAULT 0 COMMENT '合箱计划预占件数' -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='退货库存箱内SKU和数量'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='退货库存箱内SKU和数量'; CREATE TABLE ll_return_inventory_operation ( id bigint NOT NULL COMMENT '退货库存PDA操作ID', - operator_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'PDA操作人ID', - idempotency_key varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '操作幂等键', - request_hash char(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '请求内容SHA-256', - operation_type varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '操作类型,当前MERGE', + operator_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'PDA操作人ID', + idempotency_key varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '操作幂等键', + request_hash char(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '请求内容SHA-256', + operation_type varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '操作类型,当前MERGE', result_carton_id bigint DEFAULT NULL COMMENT '合箱结果退货库存箱ID', created_at datetime(3) NOT NULL COMMENT '创建时间', updated_at datetime(3) NOT NULL COMMENT '最后更新时间', @@ -2769,32 +2740,32 @@ CREATE TABLE ll_return_inventory_operation ( UNIQUE KEY uk_ll_return_inventory_operation_operator_key (operator_id,idempotency_key), KEY idx_ll_return_inventory_operation_result (result_carton_id), result_json JSON NULL COMMENT '幂等操作不可变返回快照' -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='退货库存PDA合箱幂等操作审计'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='退货库存PDA合箱幂等操作审计'; CREATE TABLE ll_return_order ( id bigint NOT NULL COMMENT '退货订单ID', - customer_id varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户ID和隔离键', - return_no varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '退货订单号', - overseas_warehouse_id varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '操作海外仓ID', - warehouse_contact_name varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '中转仓联系人快照', - warehouse_contact_phone varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '中转仓联系电话快照', - customer_reference_no varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '客户参考号', + customer_id varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户ID和隔离键', + return_no varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '退货订单号', + overseas_warehouse_id varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '操作海外仓ID', + warehouse_contact_name varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '中转仓联系人快照', + warehouse_contact_phone varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '中转仓联系电话快照', + customer_reference_no varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '客户参考号', need_relabel tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否需要换标', relabel_application_id bigint DEFAULT NULL COMMENT '绑定换标申请单ID', - relabel_no varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '绑定换标单号', - relabel_status varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '换标状态', + relabel_no varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '绑定换标单号', + relabel_status varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '换标状态', lastleg_order_id bigint DEFAULT NULL COMMENT '尾程订单ID', - lastleg_order_no varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '尾程订单号', - status varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'PENDING_RECEIPT、PARTIALLY_RECEIVED或COMPLETED', + lastleg_order_no varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '尾程订单号', + status varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'PENDING_RECEIPT、PARTIALLY_RECEIVED或COMPLETED', expected_sku_types int NOT NULL COMMENT '预报SKU种类数', expected_quantity bigint NOT NULL COMMENT '预报总数量', received_quantity bigint NOT NULL DEFAULT '0' COMMENT '累计实收总数量', - remark varchar(1000) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '客户备注', - discrepancy_reason varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '手动完结差异原因', + remark varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '客户备注', + discrepancy_reason varchar(500) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '手动完结差异原因', manually_completed_at datetime(3) DEFAULT NULL COMMENT '手动差异完结时间', - idempotency_key varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '创建幂等键', - request_hash char(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '创建请求SHA-256', - created_by varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '创建操作人ID', + idempotency_key varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '创建幂等键', + request_hash char(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '创建请求SHA-256', + created_by varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '创建操作人ID', created_at datetime(3) NOT NULL COMMENT '创建时间', updated_at datetime(3) NOT NULL COMMENT '最后更新时间', PRIMARY KEY (id), @@ -2802,14 +2773,14 @@ CREATE TABLE ll_return_order ( UNIQUE KEY uk_ll_return_order_customer_key (customer_id,idempotency_key), KEY idx_ll_return_order_warehouse_status (overseas_warehouse_id,status,created_at), CONSTRAINT chk_ll_return_order_quantity CHECK (((expected_sku_types > 0) and (expected_quantity > 0) and (received_quantity >= 0) and (received_quantity <= expected_quantity))) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='客户退货订单及SKU实收汇总'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='客户退货订单及SKU实收汇总'; CREATE TABLE ll_return_order_item ( id bigint NOT NULL COMMENT '退货预报明细ID', return_order_id bigint NOT NULL COMMENT '退货订单ID', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户ID和隔离键', - sku_code varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'SKU编码', - product_name_zh varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '中文品名', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户ID和隔离键', + sku_code varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'SKU编码', + product_name_zh varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '中文品名', expected_quantity bigint NOT NULL COMMENT 'SKU预报数量', received_quantity bigint NOT NULL DEFAULT '0' COMMENT 'SKU累计实收数量', created_at datetime(3) NOT NULL COMMENT '创建时间', @@ -2818,24 +2789,24 @@ CREATE TABLE ll_return_order_item ( UNIQUE KEY uk_ll_return_item_order_sku (return_order_id,sku_code), KEY idx_ll_return_item_sku (sku_code,received_quantity), CONSTRAINT chk_ll_return_item_quantity CHECK (((expected_quantity > 0) and (received_quantity >= 0) and (received_quantity <= expected_quantity))) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='客户退货订单的SKU预报与实收明细'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='客户退货订单的SKU预报与实收明细'; CREATE TABLE ll_return_receipt ( id bigint NOT NULL COMMENT 'PDA退货签收单ID', return_order_id bigint NOT NULL COMMENT '退货订单ID', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户ID和隔离键', - receipt_no varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '签收单号', - receipt_batch_no varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '签收批次号', - pallet_code varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '本次签收绑定的托盘码', - carton_mark varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '签收生成或绑定的退货箱号', - tracking_no varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '本次签收包裹的可选快递单号', - bound_by varchar(16) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '开始签收方式,RETURN_NO退货单号或SKU候选', - status varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '签收状态,当前SIGNED', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户ID和隔离键', + receipt_no varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '签收单号', + receipt_batch_no varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '签收批次号', + pallet_code varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '本次签收绑定的托盘码', + carton_mark varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '签收生成或绑定的退货箱号', + tracking_no varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '本次签收包裹的可选快递单号', + bound_by varchar(16) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '开始签收方式,RETURN_NO退货单号或SKU候选', + status varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '签收状态,当前SIGNED', received_quantity bigint NOT NULL COMMENT '本次实收数量', - operator_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'PDA操作人ID', - operator_name varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'PDA操作人名称', - idempotency_key varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'PDA确认幂等键', - request_hash char(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'PDA确认请求SHA-256', + operator_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'PDA操作人ID', + operator_name varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'PDA操作人名称', + idempotency_key varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'PDA确认幂等键', + request_hash char(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'PDA确认请求SHA-256', signed_at datetime(3) NOT NULL COMMENT '签收时间', created_at datetime(3) NOT NULL COMMENT '创建时间', PRIMARY KEY (id), @@ -2854,49 +2825,49 @@ CREATE TABLE ll_return_receipt ( weight DECIMAL(12,3) NULL COMMENT '实测重量kg', original_packaging TINYINT(1) NULL COMMENT '是否使用原箱原包装', measurement_source VARCHAR(32) NULL COMMENT '尺寸重量来源' -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='PDA退货实收签收事实'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='PDA退货实收签收事实'; CREATE TABLE ll_return_receipt_batch ( id bigint NOT NULL COMMENT '退货仓储计费批次ID', return_order_id bigint NOT NULL COMMENT '退货订单ID', receipt_id bigint NOT NULL COMMENT 'PDA退货签收单ID', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户ID和隔离键', - batch_no varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '签收批次号', - overseas_warehouse_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '操作海外仓ID', - storage_billing_status varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '仓储计费状态,初始PENDING_PRICING', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户ID和隔离键', + batch_no varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '签收批次号', + overseas_warehouse_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '操作海外仓ID', + storage_billing_status varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '仓储计费状态,初始PENDING_PRICING', storage_started_at datetime(3) NOT NULL COMMENT '仓储计费起算时间', created_at datetime(3) NOT NULL COMMENT '创建时间', PRIMARY KEY (id), UNIQUE KEY uk_ll_return_batch_no (batch_no), UNIQUE KEY uk_ll_return_batch_receipt (receipt_id), KEY idx_ll_return_batch_billing (customer_id,overseas_warehouse_id,storage_billing_status,storage_started_at) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='每次退货签收生成的仓储费起算批次'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='每次退货签收生成的仓储费起算批次'; CREATE TABLE ll_return_receipt_item ( id bigint NOT NULL COMMENT '签收SKU明细ID', receipt_id bigint NOT NULL COMMENT 'PDA退货签收单ID', return_order_item_id bigint NOT NULL COMMENT '退货预报明细ID', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户ID和隔离键', - sku_code varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'SKU编码', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户ID和隔离键', + sku_code varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'SKU编码', received_quantity bigint NOT NULL COMMENT '本次SKU实收数量', PRIMARY KEY (id), KEY idx_ll_return_receipt_item_receipt (receipt_id), KEY idx_ll_return_receipt_item_order_item (return_order_item_id), CONSTRAINT chk_ll_return_receipt_item_quantity CHECK ((received_quantity > 0)), box_count BIGINT NULL COMMENT 'SKU签收箱数' -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='PDA退货签收SKU实际数量明细'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='PDA退货签收SKU实际数量明细'; CREATE TABLE ll_us_truck_order ( id bigint NOT NULL COMMENT '主键ID', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户租户ID', - order_no varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '订单号', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户租户ID', + order_no varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '订单号', vehicle_type VARCHAR(32) NULL COMMENT '车辆类型:PALLET_15为15拖,PALLET_30为30拖;拼车未提供时为空', - origin varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '起运地', - origin_address varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '起运地址', - destination varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '目的地', - destination_address varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '目的地址', + origin varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '起运地', + origin_address varchar(500) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '起运地址', + destination varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '目的地', + destination_address varchar(500) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '目的地址', pallet_count INT NOT NULL DEFAULT 0 COMMENT '确认托盘数;独立下单首次实收确定,拼车来源取真实预报', package_count int DEFAULT NULL COMMENT '可选箱数,零或空值均有效', received_pallet_count int NOT NULL DEFAULT '0' COMMENT 'PDA实收托盘数', @@ -2904,18 +2875,18 @@ CREATE TABLE ll_us_truck_order ( total_weight_kg decimal(18,3) NOT NULL DEFAULT '0.000' COMMENT '总重量(千克)', total_volume_cbm decimal(18,6) NOT NULL DEFAULT '0.000000' COMMENT '总体积(立方米)', budget_amount decimal(18,2) DEFAULT NULL COMMENT '预算金额', - currency varchar(3) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'USD' COMMENT '币种', + currency varchar(3) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'USD' COMMENT '币种', origin_contact_name VARCHAR(128) NULL COMMENT '起运地联系人', origin_contact_phone VARCHAR(128) NULL COMMENT '起运地联系电话', destination_contact_name VARCHAR(128) NULL COMMENT '目的地联系人', destination_contact_phone VARCHAR(128) NULL COMMENT '目的地联系电话', pickup_at DATETIME NULL COMMENT '取货时间,按填写的日期时分保存,不转换时区', delivery_at DATETIME NULL COMMENT '送货时间,按填写的日期时分保存;拼车来源未提供时为空', - status varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '美国卡车订单状态', - remark varchar(1000) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '备注', - idempotency_key varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '业务幂等键', - request_hash char(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '请求内容哈希', - created_by varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '创建人ID', + status varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '美国卡车订单状态', + remark varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '备注', + idempotency_key varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '业务幂等键', + request_hash char(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '请求内容哈希', + created_by varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '创建人ID', version bigint NOT NULL DEFAULT '0' COMMENT '乐观锁版本', created_at datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间', updated_at datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '更新时间', @@ -2924,31 +2895,31 @@ CREATE TABLE ll_us_truck_order ( UNIQUE KEY uk_ll_us_truck_order_key (customer_id,idempotency_key), KEY idx_ll_us_truck_order_list (customer_id,status,created_at), KEY idx_ll_us_truck_order_date (customer_id,pickup_at,created_at) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='美国卡车运输订单'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='美国卡车运输订单'; CREATE TABLE ll_us_truck_order_operation ( id bigint NOT NULL COMMENT '操作记录ID', order_id bigint NOT NULL COMMENT '美国卡车订单ID', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户租户ID', - operation_type varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '具名操作类型', - idempotency_key varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '操作幂等键', - request_hash char(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '操作请求内容哈希', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户租户ID', + operation_type varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '具名操作类型', + idempotency_key varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '操作幂等键', + request_hash char(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '操作请求内容哈希', result_version bigint NOT NULL COMMENT '操作完成后的订单版本', - created_by varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '操作人ID', + created_by varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '操作人ID', created_at datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间', PRIMARY KEY (id), UNIQUE KEY uk_ll_us_truck_operation_key (customer_id,order_id,idempotency_key), KEY idx_ll_us_truck_operation_order (customer_id,order_id,created_at) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='美国卡车订单幂等操作账'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='美国卡车订单幂等操作账'; CREATE TABLE ll_us_truck_order_pallet ( id bigint NOT NULL COMMENT '托盘记录ID', order_id bigint NOT NULL COMMENT '美国卡车订单ID', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户租户ID', - pallet_no varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '业务托盘号', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户租户ID', + pallet_no varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '业务托盘号', sequence_no int NOT NULL COMMENT '订单内托盘序号', - source varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '托盘来源:订单计划或PDA超收', - status varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '托盘状态:待收、已收或超收待审核', + source varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '托盘来源:订单计划或PDA超收', + status varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '托盘状态:待收、已收或超收待审核', warehouse_pallet_id bigint DEFAULT NULL COMMENT '仓库实物托盘资源ID', package_count int DEFAULT NULL COMMENT '托盘箱数', received_at datetime(3) DEFAULT NULL COMMENT '实收时间', @@ -2958,22 +2929,22 @@ CREATE TABLE ll_us_truck_order_pallet ( UNIQUE KEY uk_ll_us_truck_order_pallet_no (customer_id,order_id,pallet_no), UNIQUE KEY uk_ll_us_truck_order_pallet_seq (customer_id,order_id,sequence_no), KEY idx_ll_us_truck_order_pallet_list (customer_id,order_id,status) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='美国卡车订单托盘实收记录'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='美国卡车订单托盘实收记录'; CREATE TABLE ll_us_truck_order_status_history ( id bigint NOT NULL COMMENT '主键ID', order_id bigint NOT NULL COMMENT '订单ID', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户租户ID', - operation_type varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '具名操作类型', - from_status varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '变更前状态', - to_status varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '变更后状态', - operator_id varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '操作人ID', - idempotency_key varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '操作幂等键', - remark varchar(1000) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '备注', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户租户ID', + operation_type varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '具名操作类型', + from_status varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '变更前状态', + to_status varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '变更后状态', + operator_id varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '操作人ID', + idempotency_key varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '操作幂等键', + remark varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '备注', created_at datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间', PRIMARY KEY (id), KEY idx_ll_us_truck_history (customer_id,order_id,created_at) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='美国卡车订单状态历史'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='美国卡车订单状态历史'; CREATE TABLE ll_fleet_driver ( id BIGINT NOT NULL COMMENT '内部主键', @@ -3035,19 +3006,19 @@ CREATE TABLE ll_vehicle ( max_load_kg DECIMAL(12,3) NULL COMMENT '最大载重,千克', max_volume_cbm DECIMAL(12,3) NULL COMMENT '可用容积,立方米', id bigint NOT NULL COMMENT '车辆档案ID', - plate_number varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '车牌号', - model varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '车型', + plate_number varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '车牌号', + model varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '车型', fuel_consumption_per100_km decimal(10,2) NOT NULL COMMENT '百公里油耗,单位升', mileage_km bigint NOT NULL COMMENT '当前公里码,单位公里', - driver_or_dispatch_company varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '司机姓名或外派单位', - phone varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '司机或外派单位联系电话', - region varchar(16) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '区域:DOMESTIC/OVERSEAS', - ownership varchar(16) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '车辆归属:OWNED/DISPATCHED', - status varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '状态:IN_SERVICE/DISPATCHED/MAINTENANCE/INACTIVE/RETURNED', - remark varchar(1000) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '备注', + driver_or_dispatch_company varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '司机姓名或外派单位', + phone varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '司机或外派单位联系电话', + region varchar(16) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '区域:DOMESTIC/OVERSEAS', + ownership varchar(16) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '车辆归属:OWNED/DISPATCHED', + status varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '状态:IN_SERVICE/DISPATCHED/MAINTENANCE/INACTIVE/RETURNED', + remark varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '备注', record_version bigint NOT NULL DEFAULT '0' COMMENT '乐观锁版本', - created_by varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '创建人', - updated_by varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '更新人', + created_by varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '创建人', + updated_by varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '更新人', created_at datetime(3) NOT NULL COMMENT '创建时间', updated_at datetime(3) NOT NULL COMMENT '更新时间', PRIMARY KEY (id), @@ -3056,7 +3027,7 @@ CREATE TABLE ll_vehicle ( CONSTRAINT chk_ll_vehicle_capacity CHECK ((max_pallet_spaces IS NULL AND max_load_kg IS NULL AND max_volume_cbm IS NULL) OR (max_pallet_spaces IS NOT NULL AND max_load_kg IS NOT NULL AND max_volume_cbm IS NOT NULL AND max_pallet_spaces BETWEEN 1 AND 10000 AND max_load_kg > 0 AND max_volume_cbm > 0)) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='平台车辆主数据'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='平台车辆主数据'; CREATE TABLE ll_fleet_vehicle_record ( id BIGINT NOT NULL COMMENT '记录内部主键', @@ -3188,7 +3159,7 @@ CREATE TABLE ll_fleet_external_booking ( booking_no VARCHAR(32) NOT NULL COMMENT '外接预订业务编号', trip_id BIGINT NOT NULL COMMENT '所属车次', trip_no VARCHAR(32) NOT NULL COMMENT '车次业务编号', - customer_id VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '委托客户及付款归属', + customer_id VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '委托客户及付款归属', customer_code VARCHAR(64) NOT NULL COMMENT '客户编号快照', customer_name VARCHAR(255) NOT NULL COMMENT '客户名称快照', status VARCHAR(16) NOT NULL COMMENT 'DRAFT询价/CONFIRMED占位/RECEIVED确认实收/BOUND关联发运/CANCELLED取消', @@ -3243,7 +3214,7 @@ CREATE TABLE ll_fleet_pool_order_link ( id BIGINT NOT NULL COMMENT '内部主键', booking_id BIGINT NOT NULL COMMENT '唯一外接预订', booking_no VARCHAR(64) NOT NULL COMMENT '预订业务编号', - customer_id VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '商业客户隔离范围', + customer_id VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '商业客户隔离范围', business_type VARCHAR(32) NOT NULL COMMENT '本土散板或本土卡车商业类型', business_order_id BIGINT NOT NULL COMMENT '对应商业订单内部关联', business_order_no VARCHAR(64) NOT NULL COMMENT '本土商业订单业务编号', @@ -3381,7 +3352,7 @@ CREATE TABLE ll_fleet_quote ( UNIQUE KEY uk_fleet_quote_natural (natural_key), UNIQUE KEY uk_fleet_quote_create (created_by,create_key), KEY idx_fleet_quote_search (kind,status,valid_from,valid_to) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='车辆采购与外接销售报价'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='车辆采购与外接销售报价'; CREATE TABLE ll_fleet_quote_history ( id BIGINT PRIMARY KEY COMMENT '报价历史主键', @@ -3392,7 +3363,7 @@ CREATE TABLE ll_fleet_quote_history ( occurred_at DATETIME(3) NOT NULL COMMENT '操作时间UTC', snapshot_json JSON NOT NULL COMMENT '报价完整版本快照', UNIQUE KEY uk_fleet_quote_history_version (quote_id,record_version) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='车辆报价不可变版本历史'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='车辆报价不可变版本历史'; CREATE TABLE ll_fleet_trip_cost ( id BIGINT NOT NULL COMMENT '内部主键', @@ -3449,17 +3420,17 @@ CREATE TABLE ll_fleet_trip_history ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='运输车次操作历史'; CREATE TABLE ll_wallet_event_inbox ( - event_id varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '国内Finance资金结果事件ID', - command_event_id varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '原海外资金指令事件ID', - payload_hash varchar(71) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '规范化载荷SHA-256,包含sha256:前缀', + event_id varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '国内Finance资金结果事件ID', + command_event_id varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '原海外资金指令事件ID', + payload_hash varchar(71) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '规范化载荷SHA-256,包含sha256:前缀', payload_json json NOT NULL COMMENT '冻结事件信封', - status varchar(16) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'RECEIVED或PROCESSED', + status varchar(16) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'RECEIVED或PROCESSED', received_at datetime(3) NOT NULL COMMENT '接收时间', processed_at datetime(3) DEFAULT NULL COMMENT '处理完成时间', PRIMARY KEY (event_id), UNIQUE KEY uk_ll_wallet_command_result (command_event_id), KEY idx_ll_wallet_event_status (status,received_at) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='跨区客户资金结果幂等收件箱'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='跨区客户资金结果幂等收件箱'; CREATE TABLE ll_warehouse_transfer_carton ( length_cm DECIMAL(12,2) NULL COMMENT '箱子实测长度厘米', @@ -3477,34 +3448,34 @@ CREATE TABLE ll_warehouse_transfer_carton ( id bigint NOT NULL COMMENT '预报箱主键', transfer_order_id bigint NOT NULL COMMENT '转运单ID', transfer_item_id bigint DEFAULT NULL COMMENT '转运申报明细ID', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户租户ID', - carton_mark varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '系统生成的一箱一码箱唛', - customer_carton_mark varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '客户原箱唛,用于换标现场绑定', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户租户ID', + carton_mark varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '系统生成的一箱一码箱唛', + customer_carton_mark varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '客户原箱唛,用于换标现场绑定', carton_index int NOT NULL COMMENT '全单箱序号', total_cartons int NOT NULL COMMENT '首次生成时全单总箱数', - status varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'EXPECTED' COMMENT '箱状态:EXPECTED待实收', + status varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'EXPECTED' COMMENT '箱状态:EXPECTED待实收', 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_ll_wh_transfer_carton_mark (customer_id,carton_mark), UNIQUE KEY uk_ll_wh_transfer_carton_index (transfer_order_id,carton_index), KEY idx_ll_wh_transfer_carton_list (customer_id,transfer_order_id,status) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='仓储转运系统箱唛预报'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='仓储转运系统箱唛预报'; CREATE TABLE ll_warehouse_transfer_inventory_allocation ( id bigint NOT NULL COMMENT '雪花主键', transfer_order_id bigint NOT NULL COMMENT '转仓单ID', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户租户ID', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户租户ID', inventory_position_id bigint NOT NULL COMMENT '来源库存仓位记录ID', inventory_balance_id bigint NOT NULL COMMENT '来源库存余额记录ID', inbound_order_id bigint NOT NULL COMMENT '来源入仓单ID', - inbound_code varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '来源入仓单号快照', + inbound_code varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '来源入仓单号快照', pallet_id bigint NOT NULL COMMENT '来源托盘ID', - pallet_no varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '来源托盘号快照', + pallet_no varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '来源托盘号快照', package_count int NOT NULL COMMENT '分配箱数', - sku_code varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '本次来源分配的实际SKU快照', - status varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'RESERVED' COMMENT '分配状态', - active_position_key varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '生效中的仓位排他键', + sku_code varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '本次来源分配的实际SKU快照', + status varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'RESERVED' COMMENT '分配状态', + active_position_key varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '生效中的仓位排他键', allocated_at datetime(3) NOT NULL COMMENT '分配时间', shipped_at datetime(3) DEFAULT NULL COMMENT '出库时间', received_at datetime(3) DEFAULT NULL COMMENT '收货时间', @@ -3514,100 +3485,100 @@ CREATE TABLE ll_warehouse_transfer_inventory_allocation ( KEY idx_ll_wh_transfer_allocation_position (transfer_order_id,inventory_position_id), UNIQUE KEY uk_ll_wh_transfer_active_position (customer_id,active_position_key), KEY idx_ll_wh_transfer_allocation_list (customer_id,transfer_order_id,status) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='转仓单来源库存分配记录'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='转仓单来源库存分配记录'; CREATE TABLE ll_warehouse_transfer_item ( declared_quantity BIGINT NULL COMMENT 'SKU申报件数,非预计或实收箱数', id bigint NOT NULL COMMENT '主键ID', transfer_order_id bigint NOT NULL COMMENT '转仓单ID', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户租户ID', - sku_code varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'SKU编码', - product_name_zh varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '产品中文名称', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户租户ID', + sku_code varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'SKU编码', + product_name_zh varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '产品中文名称', package_count int NOT NULL COMMENT '箱数', - shipping_mark varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '运输唛头', + shipping_mark varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '运输唛头', created_at datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间', PRIMARY KEY (id), KEY idx_ll_wh_transfer_item_list (customer_id,transfer_order_id), KEY idx_ll_wh_transfer_item_sku (customer_id,sku_code) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='转仓货物冻结明细'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='转仓货物冻结明细'; CREATE TABLE ll_warehouse_transfer_order ( id bigint NOT NULL COMMENT '主键ID', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户租户ID', - transfer_no varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '转仓单号', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户租户ID', + transfer_no varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '转仓单号', inbound_order_id bigint NOT NULL COMMENT '入仓单ID', - inbound_code varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '入仓单号快照', + inbound_code varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '入仓单号快照', lastleg_order_id bigint NOT NULL COMMENT '尾程订单ID', - lastleg_order_no varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '尾程订单号', - operating_warehouse_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '入库中转仓ID', - fba_warehouse_code varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '可选FBA目的仓编码', - destination_type varchar(16) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '目的类型:FBA或PRIVATE', - destination_warehouse_id varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '目的仓ID快照', - destination_warehouse_code varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '目的仓编码快照', - destination_warehouse_name varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '目的仓名称快照', - private_destination_address varchar(1000) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '私人仓完整地址', - private_destination_postal_code varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '私人仓邮编', - private_destination_contact_name varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '私人仓联系人', - private_destination_contact_phone varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '私人仓联系电话', - delivery_address varchar(1000) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '提货地址或中转仓收货地址', - pickup_contact_name varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '提货联系人', - pickup_contact_phone varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '提货联系电话', + lastleg_order_no varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '尾程订单号', + operating_warehouse_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '入库中转仓ID', + fba_warehouse_code varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '可选FBA目的仓编码', + destination_type varchar(16) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '目的类型:FBA或PRIVATE', + destination_warehouse_id varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '目的仓ID快照', + destination_warehouse_code varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '目的仓编码快照', + destination_warehouse_name varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '目的仓名称快照', + private_destination_address varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '私人仓完整地址', + private_destination_postal_code varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '私人仓邮编', + private_destination_contact_name varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '私人仓联系人', + private_destination_contact_phone varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '私人仓联系电话', + delivery_address varchar(1000) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '提货地址或中转仓收货地址', + pickup_contact_name varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '提货联系人', + pickup_contact_phone varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '提货联系电话', package_count int NOT NULL COMMENT '箱数', - delivery_method varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '入仓方式', + delivery_method varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '入仓方式', estimated_arrival_at datetime(3) NULL COMMENT '预计入仓时间,库存转运不填写', estimated_ship_at datetime(3) DEFAULT NULL COMMENT '预计发货时间', - fba_shipment_no varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '可选FBA货件编号', - tracking_no varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '物流跟踪号', - fba_box_nos varchar(2000) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'FBA箱号集合', - remark varchar(1000) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '备注', + fba_shipment_no varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '可选FBA货件编号', + tracking_no varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '物流跟踪号', + fba_box_nos varchar(2000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'FBA箱号集合', + remark varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '备注', need_relabel tinyint NOT NULL DEFAULT '0' COMMENT '是否需要换标', relabel_application_id bigint DEFAULT NULL COMMENT '换标申请单ID', - relabel_no varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '换标申请单号', + relabel_no varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '换标申请单号', relabel_completed_at datetime(3) DEFAULT NULL COMMENT '关联贴标完成时间', - status varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'PENDING_REVIEW' COMMENT '业务状态', - review_decision varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '审核决定', - review_remark varchar(1000) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '审核备注或驳回原因', - reviewed_by varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '审核人ID', + status varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'PENDING_REVIEW' COMMENT '业务状态', + review_decision varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '审核决定', + review_remark varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '审核备注或驳回原因', + reviewed_by varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '审核人ID', reviewed_at datetime(3) DEFAULT NULL COMMENT '审核时间', - idempotency_key varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '业务幂等键', - allocation_idempotency_key varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '库存分配幂等键', - allocation_request_hash char(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '库存分配请求哈希', - outbound_idempotency_key varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '出库幂等键', - outbound_request_hash char(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '出库请求哈希', + idempotency_key varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '业务幂等键', + allocation_idempotency_key varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '库存分配幂等键', + allocation_request_hash char(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '库存分配请求哈希', + outbound_idempotency_key varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '出库幂等键', + outbound_request_hash char(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '出库请求哈希', outbound_at datetime(3) DEFAULT NULL COMMENT '出库时间', - request_hash char(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '请求内容哈希', - created_by varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '创建人ID', + request_hash char(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '请求内容哈希', + created_by varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT 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 '更新时间', label_deadline datetime(3) DEFAULT NULL COMMENT '换标截止时间', - relabel_contact_name varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '换标联系人', - relabel_contact_phone varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '换标联系电话', + relabel_contact_name varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '换标联系人', + relabel_contact_phone varchar(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '换标联系电话', relabel_charge_amount decimal(20,2) DEFAULT NULL COMMENT '换标费用CNY', - relabel_delivery_method varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '换标后派送方式', - palletize_idempotency_key varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'PDA打托幂等键', - palletize_request_hash char(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'PDA打托请求哈希', - confirmation_idempotency_key varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '客户测量确认幂等键', - confirmation_request_hash char(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '客户测量确认请求哈希', + relabel_delivery_method varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '换标后派送方式', + palletize_idempotency_key varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'PDA打托幂等键', + palletize_request_hash char(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'PDA打托请求哈希', + confirmation_idempotency_key varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '客户测量确认幂等键', + confirmation_request_hash char(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '客户测量确认请求哈希', customer_confirmed_at datetime(3) DEFAULT NULL COMMENT '客户确认测量时间', - destination_binding_idempotency_key varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '目的地绑定幂等键', - destination_binding_request_hash char(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '目的地绑定请求哈希', + destination_binding_idempotency_key varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '目的地绑定幂等键', + destination_binding_request_hash char(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '目的地绑定请求哈希', PRIMARY KEY (id), UNIQUE KEY uk_ll_wh_transfer_no (customer_id,transfer_no), UNIQUE KEY uk_ll_wh_transfer_key (customer_id,idempotency_key), UNIQUE KEY uk_ll_wh_transfer_tail_order (lastleg_order_id), KEY idx_ll_wh_transfer_list (customer_id,status,created_at), KEY idx_ll_wh_transfer_inbound (customer_id,inbound_code) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='客户转仓单'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='客户转仓单'; CREATE TABLE ll_warehouse_transfer_pallet ( id bigint NOT NULL COMMENT '打托事实ID', transfer_order_id bigint NOT NULL COMMENT '转运单ID', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户租户ID', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户租户ID', sequence_no int NOT NULL COMMENT '托序号', warehouse_pallet_id bigint DEFAULT NULL COMMENT '生成后的海外仓真实托盘ID', - pallet_no varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '生成后的真实托盘码', + pallet_no varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '生成后的真实托盘码', warehouse_location_id bigint DEFAULT NULL COMMENT '生成后的真实库位ID', - warehouse_location_code varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '真实库位码', + warehouse_location_code varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '真实库位码', warehouse_resource_lock_id bigint DEFAULT NULL COMMENT '生成后的仓储资源锁ID', carton_count int NOT NULL COMMENT '本托实收箱数', length_cm decimal(12,2) NOT NULL COMMENT '实测长cm', @@ -3615,11 +3586,11 @@ CREATE TABLE ll_warehouse_transfer_pallet ( height_cm decimal(12,2) NOT NULL COMMENT '实测高cm', gross_weight_kg decimal(12,3) NOT NULL COMMENT '实测毛重kg', volume_cbm decimal(18,6) NOT NULL COMMENT '实测体积CBM', - status varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '逐托状态DRAFT/GENERATED/BOUND/COMPLETED', - draft_idempotency_key varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '草稿保存幂等键', - draft_request_hash char(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '草稿请求摘要', - generate_idempotency_key varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '新托盘生成幂等键', - bind_idempotency_key varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '扫码绑定幂等键', + status varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '逐托状态DRAFT/GENERATED/BOUND/COMPLETED', + draft_idempotency_key varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '草稿保存幂等键', + draft_request_hash char(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '草稿请求摘要', + generate_idempotency_key varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '新托盘生成幂等键', + bind_idempotency_key varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '扫码绑定幂等键', bound_at datetime(3) DEFAULT NULL COMMENT '扫码确认绑定时间', completed_at datetime(3) DEFAULT NULL COMMENT '整单打托完成时间', created_at datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间', @@ -3628,50 +3599,50 @@ CREATE TABLE ll_warehouse_transfer_pallet ( UNIQUE KEY uk_ll_transfer_pallet_seq (transfer_order_id,sequence_no), UNIQUE KEY uk_ll_transfer_pallet_physical (customer_id,warehouse_pallet_id), KEY idx_ll_transfer_pallet_order (customer_id,transfer_order_id,status) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='仓储转运PDA实测打托事实'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='仓储转运PDA实测打托事实'; CREATE TABLE ll_warehouse_transfer_pallet_item ( id bigint NOT NULL COMMENT '主键ID', transfer_order_id bigint NOT NULL COMMENT '转运单ID', transfer_pallet_id bigint NOT NULL COMMENT '转运托盘事实ID', transfer_item_id bigint NOT NULL COMMENT '转运申报明细ID', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户租户ID', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户租户ID', package_count int NOT NULL COMMENT '本托该SKU实收箱数', created_at datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间', PRIMARY KEY (id), UNIQUE KEY uk_ll_transfer_pallet_item (transfer_pallet_id,transfer_item_id), KEY idx_ll_transfer_pallet_item_order (customer_id,transfer_order_id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='仓储转运每托SKU箱数回填'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='仓储转运每托SKU箱数回填'; CREATE TABLE ll_warehouse_transfer_measurement_photo ( id bigint NOT NULL COMMENT '测量照片引用ID', transfer_order_id bigint NOT NULL COMMENT '仓储转运单ID', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户租户ID', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户租户ID', file_id bigint NOT NULL COMMENT '文件服务文件ID', - file_name varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '文件名快照', - content_type varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '文件媒体类型快照', + file_name varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '文件名快照', + content_type varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '文件媒体类型快照', size_bytes bigint NOT NULL DEFAULT '0' COMMENT '文件字节数快照', - uploaded_by varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'PDA上传操作人', + uploaded_by varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'PDA上传操作人', uploaded_at datetime(3) NOT NULL COMMENT '上传时间', submitted_at datetime(3) DEFAULT NULL COMMENT '随测量打托提交时间', PRIMARY KEY (id), UNIQUE KEY uk_ll_transfer_measurement_photo_file (customer_id,transfer_order_id,file_id), KEY idx_ll_transfer_measurement_photo_order (customer_id,transfer_order_id,uploaded_at) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='仓储转运PDA测量现场照片引用'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='仓储转运PDA测量现场照片引用'; CREATE TABLE ll_warehouse_transfer_receipt ( id bigint NOT NULL COMMENT '雪花主键', transfer_order_id bigint NOT NULL COMMENT '转仓单ID', allocation_id bigint NOT NULL COMMENT '库存分配记录ID', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户租户ID', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户租户ID', source_inventory_position_id bigint NOT NULL COMMENT '来源库存仓位记录ID', destination_inventory_balance_id bigint NOT NULL COMMENT '目标库存余额记录ID', destination_inventory_position_id bigint NOT NULL COMMENT '目标库存仓位记录ID', package_count int NOT NULL COMMENT '收货箱数', - idempotency_key varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '收货幂等键', - request_hash char(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '收货请求哈希', - inventory_operation_key varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '库存操作幂等键', - created_by varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '创建人ID', + idempotency_key varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '收货幂等键', + request_hash char(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '收货请求哈希', + inventory_operation_key varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '库存操作幂等键', + created_by varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '创建人ID', received_at datetime(3) NOT NULL COMMENT '收货时间', created_at datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间', PRIMARY KEY (id), @@ -3679,7 +3650,7 @@ CREATE TABLE ll_warehouse_transfer_receipt ( UNIQUE KEY uk_ll_wh_transfer_receipt_key (customer_id,idempotency_key), UNIQUE KEY uk_ll_wh_transfer_receipt_operation (customer_id,inventory_operation_key), KEY idx_ll_wh_transfer_receipt_list (customer_id,transfer_order_id,received_at) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='转仓来源仓位的目标库存收货记录'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='转仓来源仓位的目标库存收货记录'; CREATE TABLE ll_warehouse_transfer_receipt_command ( id BIGINT NOT NULL COMMENT '收货命令主键', @@ -3704,41 +3675,41 @@ CREATE TABLE ll_warehouse_transfer_receipt_command ( UNIQUE KEY uk_ll_wh_receipt_cmd_key (customer_id, idempotency_key), KEY idx_ll_wh_receipt_cmd_target (customer_id, transfer_order_id, target_pallet_id), CONSTRAINT ck_ll_wh_receipt_cmd_status CHECK (status IN ('PREPARED','RESERVED','COMPLETED')) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='转运逐来源收货原请求与共享目标资源占用命令'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='转运逐来源收货原请求与共享目标资源占用命令'; CREATE TABLE ll_warehouse_transfer_relabel_binding ( id bigint NOT NULL COMMENT '主键ID', transfer_order_id bigint NOT NULL COMMENT '转运单ID', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户租户ID', - operation_type varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '换标类型', - customer_carton_mark varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户原箱唛', - original_sku varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '旧SKU', - new_sku varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '新SKU', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户租户ID', + operation_type varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '换标类型', + customer_carton_mark varchar(255) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户原箱唛', + original_sku varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '旧SKU', + new_sku varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '新SKU', quantity int NOT NULL COMMENT '换标件数', - fba_carton_mark varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '新FBA箱唛', - destination_warehouse_code varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'FBA目的仓', - remark varchar(1000) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '备注', + fba_carton_mark varchar(255) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '新FBA箱唛', + destination_warehouse_code varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'FBA目的仓', + remark varchar(1000) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '备注', source_allocation_id bigint DEFAULT NULL COMMENT '已有库存转运的原始来源分配ID', relabel_item_id bigint DEFAULT NULL COMMENT '本票关联换标明细ID', created_at datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间', PRIMARY KEY (id), UNIQUE KEY uk_ll_transfer_relabel_mark (transfer_order_id,customer_carton_mark), KEY idx_ll_transfer_relabel_order (customer_id,transfer_order_id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='仓储转运换标预绑定明细'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='仓储转运换标预绑定明细'; CREATE TABLE ll_warehouse_transfer_source_order ( id bigint NOT NULL COMMENT '主键ID', transfer_order_id bigint NOT NULL COMMENT '转仓单ID', - customer_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户租户ID', + customer_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户租户ID', source_lastleg_order_id bigint NOT NULL COMMENT '来源尾程订单ID', - source_lastleg_order_no varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '来源尾程订单号', + source_lastleg_order_no varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '来源尾程订单号', inbound_order_id bigint NOT NULL COMMENT '入仓单ID', - inbound_code varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '入仓单号快照', + inbound_code varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '入仓单号快照', created_at datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间', PRIMARY KEY (id), UNIQUE KEY uk_ll_wh_transfer_source (transfer_order_id,source_lastleg_order_id), KEY idx_ll_wh_transfer_source_list (customer_id,transfer_order_id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='转仓来源历史订单绑定'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='转仓来源历史订单绑定'; -- 服务 schema:baiyue_lastlag_warehouse CREATE TABLE ll_return_packing_plan ( @@ -3771,19 +3742,19 @@ CREATE TABLE ll_return_packing_trace ( CONSTRAINT chk_return_packing_trace_quantity CHECK(quantity>0) ) ENGINE=InnoDB COMMENT='退货合箱逐SKU原始退货单不可变追溯'; -CREATE DATABASE IF NOT EXISTS baiyue_lastlag_warehouse DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; +CREATE DATABASE IF NOT EXISTS baiyue_lastlag_warehouse DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; USE baiyue_lastlag_warehouse; CREATE TABLE wh_location ( id bigint unsigned NOT NULL COMMENT '库位ID(雪花ID)', customer_id bigint unsigned NOT NULL COMMENT '数据所属租户ID', warehouse_id bigint unsigned NOT NULL COMMENT '所属仓库ID', - location_code varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '仓库内唯一库位编码', - location_type varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '库位类型', + location_code varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '仓库内唯一库位编码', + location_type varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '库位类型', shared_overflow tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否系统共享溢出库位(A0)', max_load_kg decimal(18,3) NOT NULL COMMENT '最大承重(千克)', - status varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'ACTIVE' COMMENT '库位状态:ACTIVE启用、INACTIVE停用', - remark varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '备注', + status varchar(20) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'ACTIVE' COMMENT '库位状态:ACTIVE启用、INACTIVE停用', + remark varchar(500) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '备注', created_by bigint unsigned DEFAULT NULL COMMENT '创建人ID', created_at datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间', updated_by bigint unsigned DEFAULT NULL COMMENT '最后修改人ID', @@ -3793,20 +3764,20 @@ CREATE TABLE wh_location ( PRIMARY KEY (id), UNIQUE KEY uk_location_code (customer_id,warehouse_id,location_code,deleted), KEY idx_location_query (customer_id,warehouse_id,status,deleted) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='仓库库位表:按租户和仓库维护库位及承重信息'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='仓库库位表:按租户和仓库维护库位及承重信息'; CREATE TABLE wh_pallet ( id bigint unsigned NOT NULL COMMENT '托盘ID(雪花ID)', customer_id bigint unsigned NOT NULL COMMENT '数据所属租户ID', warehouse_id bigint unsigned NOT NULL COMMENT '所属仓库ID', location_id bigint unsigned DEFAULT NULL COMMENT '同仓库内绑定的库位ID,可为空', - pallet_code varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '仓库内唯一托盘编码', - pallet_type varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '托盘类型', + pallet_code varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '仓库内唯一托盘编码', + pallet_type varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '托盘类型', capacity_cbm decimal(18,4) NOT NULL DEFAULT '2.0000' COMMENT '托盘容量(立方米)', unit_price decimal(18,2) NOT NULL DEFAULT '0.00' COMMENT '托盘单价', - status varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'IDLE' COMMENT '托盘状态:IDLE闲置、OCCUPIED占用、SCRAPPED废弃', - remark varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '备注', - fba_warehouse_code varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '所属FBA仓编码', + status varchar(20) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'IDLE' COMMENT '托盘状态:IDLE闲置、OCCUPIED占用、SCRAPPED废弃', + remark varchar(500) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '备注', + fba_warehouse_code varchar(128) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '所属FBA仓编码', created_by bigint unsigned DEFAULT NULL COMMENT '创建人ID', created_at datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间', updated_by bigint unsigned DEFAULT NULL COMMENT '最后修改人ID', @@ -3817,20 +3788,20 @@ CREATE TABLE wh_pallet ( UNIQUE KEY uk_pallet_code (customer_id,warehouse_id,pallet_code,deleted), KEY idx_pallet_query (customer_id,warehouse_id,status,deleted), KEY idx_pallet_location (customer_id,warehouse_id,location_id,deleted) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='仓库托盘表:按租户和仓库维护托盘、容量、单价及状态'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='仓库托盘表:按租户和仓库维护托盘、容量、单价及状态'; CREATE TABLE wh_pallet_batch ( id bigint NOT NULL COMMENT '批量托盘预留ID', warehouse_id bigint NOT NULL COMMENT '实际海外仓ID', - batch_key varchar(160) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '柜级入库计划批次稳定业务键', - request_hash char(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '规范化批量请求SHA-256摘要', - status varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '批次状态:RESERVED、CONFIRMED、COMPENSATED', + batch_key varchar(160) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '柜级入库计划批次稳定业务键', + request_hash char(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '规范化批量请求SHA-256摘要', + status varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '批次状态:RESERVED、CONFIRMED、COMPENSATED', attempt_no int NOT NULL DEFAULT '1' COMMENT '同批次重新预留尝试次数', - confirm_command_key varchar(160) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '确认命令幂等键', - confirm_command_hash char(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '确认命令规范化SHA-256摘要', - compensate_command_key varchar(160) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '补偿命令幂等键', - compensate_command_hash char(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '补偿命令规范化SHA-256摘要', - compensate_reason varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '补偿原因', + confirm_command_key varchar(160) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '确认命令幂等键', + confirm_command_hash char(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '确认命令规范化SHA-256摘要', + compensate_command_key varchar(160) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '补偿命令幂等键', + compensate_command_hash char(64) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '补偿命令规范化SHA-256摘要', + compensate_reason varchar(500) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '补偿原因', confirmed_at datetime(3) DEFAULT NULL COMMENT '确认绑定时间', compensated_at datetime(3) DEFAULT NULL COMMENT '补偿释放时间', created_at datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间', @@ -3838,21 +3809,21 @@ CREATE TABLE wh_pallet_batch ( PRIMARY KEY (id), UNIQUE KEY uk_wh_pallet_batch_key (warehouse_id,batch_key), KEY idx_wh_pallet_batch_status (warehouse_id,status,updated_at) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='柜级入库计划批量托盘预留与确认记录'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='柜级入库计划批量托盘预留与确认记录'; CREATE TABLE wh_pallet_batch_item ( id bigint NOT NULL COMMENT '批量托盘明细ID', batch_id bigint NOT NULL COMMENT '所属批量托盘预留ID', customer_id bigint NOT NULL COMMENT '实际占用客户ID', warehouse_id bigint NOT NULL COMMENT '实际海外仓ID', - business_key varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '柜号入库计划序号组成的稳定业务键', - inbound_plan_no varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '入库计划号快照', + business_key varchar(100) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '柜号入库计划序号组成的稳定业务键', + inbound_plan_no varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '入库计划号快照', sequence_no int NOT NULL COMMENT '计划内托盘序号', pallet_id bigint NOT NULL COMMENT '公共实物托盘ID', - pallet_code varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '资源服务生成的规范托盘码快照', + pallet_code varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '资源服务生成的规范托盘码快照', location_id bigint NOT NULL COMMENT '公共托盘当前预留库位ID', resource_lock_id bigint NOT NULL COMMENT '按客户隔离的资源锁ID', - status varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '明细状态:RESERVED、CONFIRMED、RELEASED', + status varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '明细状态:RESERVED、CONFIRMED、RELEASED', 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), @@ -3860,42 +3831,42 @@ CREATE TABLE wh_pallet_batch_item ( UNIQUE KEY uk_wh_pallet_batch_item_sequence (batch_id,sequence_no), KEY idx_wh_pallet_batch_item_pallet (pallet_id), KEY idx_wh_pallet_batch_item_batch (batch_id,status) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='柜级入库计划批量托盘明细与资源锁关联'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='柜级入库计划批量托盘明细与资源锁关联'; CREATE TABLE wh_pallet_business_binding ( id bigint NOT NULL COMMENT '主键ID', customer_id bigint NOT NULL COMMENT '客户租户ID', warehouse_id bigint NOT NULL COMMENT '仓库ID', - business_type varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '业务类型', - business_id varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '绑定业务记录ID', + business_type varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '业务类型', + business_id varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '绑定业务记录ID', pallet_id bigint NOT NULL COMMENT '托盘ID', created_at datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间', PRIMARY KEY (id), UNIQUE KEY uk_wh_pallet_binding (customer_id,warehouse_id,business_type,business_id), KEY idx_wh_pallet_binding_pallet (pallet_id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='托盘业务绑定关系'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='托盘业务绑定关系'; CREATE TABLE wh_pallet_operation ( id bigint NOT NULL COMMENT '托盘状态操作ID', - operation_key varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '跨服务幂等操作键', - request_hash varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '状态变更请求摘要', + operation_key varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '跨服务幂等操作键', + request_hash varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '状态变更请求摘要', warehouse_id bigint NOT NULL COMMENT '实际海外仓ID', pallet_id bigint NOT NULL COMMENT '实物托盘ID', - pallet_code varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '实物托盘码快照', - before_status varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '变更前托盘状态', - after_status varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '变更后托盘状态', + pallet_code varchar(128) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '实物托盘码快照', + before_status varchar(20) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '变更前托盘状态', + after_status varchar(20) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '变更后托盘状态', before_location_id bigint DEFAULT NULL COMMENT '变更前库位ID,无库位托盘为空', after_location_id bigint DEFAULT NULL COMMENT '变更后库位ID,无库位托盘为空', before_version int NOT NULL COMMENT '变更前托盘版本', after_version int NOT NULL COMMENT '变更后托盘版本', - operation_status varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '操作状态:APPLIED已应用、COMPENSATED已补偿', - reason varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '操作原因', + operation_status varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '操作状态:APPLIED已应用、COMPENSATED已补偿', + reason varchar(500) COLLATE utf8mb4_0900_ai_ci DEFAULT 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_wh_pallet_operation_key (operation_key), KEY idx_wh_pallet_operation_pallet (warehouse_id,pallet_id,created_at) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='实物托盘状态幂等变更与补偿记录'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='实物托盘状态幂等变更与补偿记录'; CREATE TABLE wh_resource_lock ( id bigint unsigned NOT NULL COMMENT '主键ID', @@ -3903,10 +3874,10 @@ CREATE TABLE wh_resource_lock ( warehouse_id bigint unsigned NOT NULL COMMENT '仓库ID', pallet_id bigint unsigned NOT NULL COMMENT '托盘ID', location_id bigint unsigned NOT NULL COMMENT '库位ID', - business_type varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '业务类型', - business_id varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '业务记录ID', - reservation_key varchar(160) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '资源预留幂等键', - status varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'ACTIVE' COMMENT '资源锁状态', + business_type varchar(50) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '业务类型', + business_id varchar(100) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '业务记录ID', + reservation_key varchar(160) COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '资源预留幂等键', + status varchar(20) COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'ACTIVE' COMMENT '资源锁状态', locked_at datetime(3) NOT NULL COMMENT '锁定时间', released_at datetime(3) DEFAULT NULL COMMENT '释放时间', created_at datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间', @@ -3916,7 +3887,7 @@ CREATE TABLE wh_resource_lock ( KEY idx_resource_lock_pallet (customer_id,warehouse_id,pallet_id,status), KEY idx_resource_lock_location (customer_id,warehouse_id,location_id,status), KEY idx_resource_lock_business (customer_id,business_type,business_id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='仓储资源锁定记录'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='仓储资源锁定记录'; -- 必要系统种子;不写入测试客户、演示订单、示例供应商、快递、托盘或库位。 @@ -3972,245 +3943,33 @@ CREATE TABLE ll_return_carton_count_session ( UNIQUE KEY uk_ll_return_count_session_receipt(receipt_id) ) ENGINE=InnoDB COMMENT='退货箱内清点单人单任务持久会话'; -CREATE TABLE ll_procurement_wallet_attempt ( - id BIGINT NOT NULL COMMENT '请求尝试ID', - order_id BIGINT NOT NULL COMMENT '采购订单ID', - customer_id VARCHAR(128) NOT NULL COMMENT '客户隔离键', - fee_id BIGINT NOT NULL COMMENT '不可变费用版本ID', - attempt_no BIGINT NOT NULL COMMENT '费用版本内请求序号', - operation VARCHAR(32) NOT NULL COMMENT '资金操作:FREEZE冻结、SETTLE结算、SETTLE_ACTUAL实际结算、RELEASE释放、REFUND退款', - business_no VARCHAR(128) NOT NULL COMMENT 'Finance资金业务号', - amount DECIMAL(20,2) NULL COMMENT '请求金额;全额结算或释放可空', - command_event_id VARCHAR(64) NOT NULL COMMENT '资金指令事件唯一引用', - status VARCHAR(16) NOT NULL COMMENT '资金请求结果:PENDING未决、SUCCEEDED成功、FAILED明确失败', - result_hash CHAR(64) NULL COMMENT '原业务回执摘要', - wallet_hold_id VARCHAR(64) NULL COMMENT 'Finance冻结引用', - wallet_ledger_id VARCHAR(64) NULL COMMENT 'Finance处理流水引用', - error_code VARCHAR(64) NULL COMMENT '业务失败码', - error_message VARCHAR(500) NULL COMMENT '内部失败诊断,不直接对客展示', - created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '请求时间', - completed_at DATETIME(3) NULL COMMENT '最终结果时间', - PRIMARY KEY(id), UNIQUE KEY uk_procurement_attempt_event(command_event_id), - UNIQUE KEY uk_procurement_attempt_number(fee_id,attempt_no), - KEY idx_procurement_attempt_order(customer_id,order_id,status), - CONSTRAINT chk_procurement_attempt_status CHECK(status IN('PENDING','SUCCEEDED','FAILED')) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='采购费用资金命令尝试与不可变结果'; -- 采购节点事实与操作幂等;不补写历史订单的购买、授权或收货事实。 -CREATE TABLE ll_procurement_flow ( - order_id BIGINT NOT NULL COMMENT '采购订单ID,一单一个履约批次', - customer_id VARCHAR(128) NOT NULL COMMENT '客户隔离键', - purchased_at DATETIME(3) NULL COMMENT '真实购买时间', - purchased_product_name VARCHAR(255) NULL COMMENT '实际购买商品', - purchased_specification VARCHAR(500) NULL COMMENT '实际购买规格', - purchased_quantity BIGINT NULL COMMENT '实际购买件数', - purchase_amount DECIMAL(20,2) NULL COMMENT '供应商实付原币金额', - purchase_currency CHAR(3) NULL COMMENT '供应商实付原币CNY或USD', - purchase_voucher_id BIGINT NULL COMMENT '购买凭证引用', - purchase_issue VARCHAR(500) NULL COMMENT '购买未核定或异常说明', - purchase_confirmed_at DATETIME(3) NULL COMMENT '货款核定时间,不代表扣费成功', - actual_warehouse_id BIGINT NULL COMMENT '首次实际收货冻结的仓库ID', - actual_warehouse_code VARCHAR(128) NULL COMMENT '实际收货仓编码快照', - actual_warehouse_name VARCHAR(255) NULL COMMENT '实际收货仓名称快照', - overseas_received_at DATETIME(3) NULL COMMENT '海外整批收货完成时间', - packing_version INT NULL COMMENT '当前确认装箱清单版本', - overseas_shipped_at DATETIME(3) NULL COMMENT '国际段整批实际交寄时间', - domestic_received_at DATETIME(3) NULL COMMENT '国内整批收货完成时间', - charges_confirmed_at DATETIME(3) NULL COMMENT '国内整单费用核定时间', - customer_shipped_at DATETIME(3) NULL COMMENT '国内寄客户实际交寄时间', - domestic_receiver_name VARCHAR(128) NULL COMMENT '国际段国内收件人', - domestic_receiver_phone VARCHAR(64) NULL COMMENT '国际段国内收件电话', - domestic_receiver_address VARCHAR(1000) NULL COMMENT '国际段国内接收地址', - cancelled_at DATETIME(3) NULL COMMENT '购买前取消时间', - closed_at DATETIME(3) NULL COMMENT '异常关闭时间', - control_reason VARCHAR(1000) 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(order_id), KEY idx_procurement_flow_customer(customer_id,order_id), - CONSTRAINT chk_procurement_purchase_quantity CHECK(purchased_quantity IS NULL OR purchased_quantity>0), - CONSTRAINT chk_procurement_purchase_amount CHECK(purchase_amount IS NULL OR purchase_amount>=0) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='采购单一批次履约事实'; -CREATE TABLE ll_procurement_command ( - id BIGINT NOT NULL COMMENT '操作记录ID', order_id BIGINT NOT NULL COMMENT '采购订单ID', - customer_id VARCHAR(128) NOT NULL COMMENT '客户隔离键', action VARCHAR(48) NOT NULL COMMENT '采购节点动作', - idempotency_key VARCHAR(128) NOT NULL COMMENT '操作幂等键', request_hash CHAR(64) NOT NULL COMMENT '请求摘要', - operator_id VARCHAR(128) NOT NULL COMMENT '执行人', reason VARCHAR(1000) NOT NULL COMMENT '操作依据', - detail_json JSON NOT NULL COMMENT '内部操作及更正依据快照,不直接对客展示', - created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '执行时间', - PRIMARY KEY(id), UNIQUE KEY uk_procurement_command(order_id,action,idempotency_key), - KEY idx_procurement_command_customer(customer_id,order_id,created_at) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='采购节点幂等与不可变审计'; -CREATE TABLE ll_procurement_budget_authorization ( - id BIGINT NOT NULL COMMENT '预算授权ID', order_id BIGINT NOT NULL COMMENT '采购订单ID', - customer_id VARCHAR(128) NOT NULL COMMENT '客户隔离键', - previous_amount DECIMAL(20,2) NULL COMMENT '原货款预算总上限;未授权保持空', previous_currency CHAR(3) NOT NULL COMMENT '原预算币种', - authorized_amount DECIMAL(20,2) NOT NULL COMMENT '客户授权的新货款总上限', currency CHAR(3) NOT NULL COMMENT '客户授权币种', - voucher_file_id BIGINT NOT NULL COMMENT '客户确认书面或聊天凭证', reason VARCHAR(1000) NOT NULL COMMENT '授权依据', - reviewed_by VARCHAR(128) NOT NULL COMMENT '核验人员', reviewed_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '核验时间', - PRIMARY KEY(id), KEY idx_procurement_budget_order(customer_id,order_id,id), - CONSTRAINT chk_procurement_budget_amount CHECK(authorized_amount>=0 AND previous_amount>=0), - CONSTRAINT chk_procurement_budget_currency CHECK(currency IN('CNY','USD') AND previous_currency IN('CNY','USD')) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='采购货款预算授权核验历史'; -- 采购实收、差异、装箱和两段整批发运事实。不产生WMS库存流水,不回填历史采购数量。 -CREATE TABLE ll_procurement_receipt ( - id BIGINT NOT NULL COMMENT '实收明细ID', order_id BIGINT NOT NULL COMMENT '采购订单ID', customer_id VARCHAR(128) NOT NULL COMMENT '客户隔离键', - receipt_no VARCHAR(64) NOT NULL COMMENT '采购收货业务编号', leg VARCHAR(16) NOT NULL COMMENT 'OVERSEAS海外或DOMESTIC国内', - source_carton_id BIGINT NULL COMMENT '国内实收关联原国际交寄箱,海外为空', warehouse_id BIGINT NULL COMMENT '海外实际收货仓', - good_quantity BIGINT NOT NULL COMMENT '可发合格件数', damaged_quantity BIGINT NOT NULL COMMENT '破损待处置件数', extra_quantity BIGINT NOT NULL COMMENT '多到件数,不增加可发数量', - received_at DATETIME(3) NOT NULL COMMENT '实际收货时间', active TINYINT(1) NOT NULL DEFAULT 1 COMMENT '当前有效实收事实', - supersedes_id BIGINT NULL COMMENT '受控更正所替代的原实收记录', created_by VARCHAR(128) NOT NULL COMMENT '登记人员', - created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '登记时间', - PRIMARY KEY(id), UNIQUE KEY uk_procurement_receipt_no(receipt_no), KEY idx_procurement_receipt_source(order_id,leg,source_carton_id,active), - CONSTRAINT chk_procurement_receipt_quantity CHECK(good_quantity>=0 AND damaged_quantity>=0 AND extra_quantity>=0 AND good_quantity+damaged_quantity+extra_quantity>0), - CONSTRAINT chk_procurement_receipt_leg CHECK((leg='OVERSEAS' AND source_carton_id IS NULL) OR (leg='DOMESTIC' AND source_carton_id IS NOT NULL)) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='采购分次实收与受控更正事实'; -CREATE TABLE ll_procurement_disposition ( - id BIGINT NOT NULL COMMENT '差异处理ID', order_id BIGINT NOT NULL COMMENT '采购订单ID', customer_id VARCHAR(128) NOT NULL COMMENT '客户隔离键', - leg VARCHAR(16) NOT NULL COMMENT 'OVERSEAS海外或DOMESTIC国内', source_carton_id BIGINT NULL COMMENT '国内原交寄箱来源', - receipt_id BIGINT NULL COMMENT '破损或多货对应实收记录,短少为空', kind VARCHAR(24) NOT NULL COMMENT 'SHORTAGE短少、DAMAGED破损、EXTRA多货', - quantity BIGINT NOT NULL COMMENT '已批准处置件数', decision VARCHAR(32) NOT NULL COMMENT 'ACCEPT_SHORTAGE确认短少、DISPOSE_DAMAGED拒收或处置、RETURN_EXTRA退回多货', - active TINYINT(1) NOT NULL DEFAULT 1 COMMENT '当前有效处置;撤销只改变有效性并保留原事实', - voucher_file_id BIGINT NOT NULL COMMENT '本单客户确认或实际处置凭证', reason VARCHAR(1000) NOT NULL COMMENT '处置依据', - created_by VARCHAR(128) NOT NULL COMMENT '采购管理人员', created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '确认时间', - PRIMARY KEY(id), KEY idx_procurement_disposition_source(order_id,leg,source_carton_id,receipt_id,kind), - CONSTRAINT chk_procurement_disposition_quantity CHECK(quantity>0) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='采购单内差异处置,不自动退款'; -CREATE TABLE ll_procurement_manifest ( - id BIGINT NOT NULL COMMENT '装箱清单ID', order_id BIGINT NOT NULL COMMENT '采购订单ID', customer_id VARCHAR(128) NOT NULL COMMENT '客户隔离键', - version INT NOT NULL COMMENT '订单内装箱版本', status VARCHAR(16) NOT NULL COMMENT 'DRAFT草稿、CONFIRMED已确认、SUPERSEDED已替代', - confirmed_at DATETIME(3) NULL COMMENT '确认时间', created_by VARCHAR(128) NOT NULL COMMENT '制单人', - created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '版本保存时间', - PRIMARY KEY(id), UNIQUE KEY uk_procurement_manifest_version(order_id,version), - CONSTRAINT chk_procurement_manifest_status CHECK(status IN('DRAFT','CONFIRMED','SUPERSEDED')) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='采购装箱清单不可变版本'; -CREATE TABLE ll_procurement_carton ( - id BIGINT NOT NULL COMMENT '版本内箱记录ID', order_id BIGINT NOT NULL COMMENT '采购订单ID', manifest_id BIGINT NOT NULL COMMENT '装箱清单版本', - carton_no VARCHAR(64) NOT NULL COMMENT '采购订单内业务箱号', length_cm DECIMAL(12,2) NOT NULL COMMENT '外箱长厘米', - width_cm DECIMAL(12,2) NOT NULL COMMENT '外箱宽厘米', height_cm DECIMAL(12,2) NOT NULL COMMENT '外箱高厘米', - gross_weight_kg DECIMAL(12,3) NOT NULL COMMENT '封箱毛重千克', quantity BIGINT NOT NULL COMMENT '本箱合格商品总件数', - PRIMARY KEY(id), UNIQUE KEY uk_procurement_carton_version(manifest_id,carton_no), KEY idx_procurement_carton_order(order_id,manifest_id), - CONSTRAINT chk_procurement_carton_measurement CHECK(length_cm>0 AND width_cm>0 AND height_cm>0 AND gross_weight_kg>0 AND quantity>0) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='采购版本化逐箱实测与件数'; -CREATE TABLE ll_procurement_carton_source ( - carton_id BIGINT NOT NULL COMMENT '版本内箱ID', receipt_id BIGINT NOT NULL COMMENT '海外实收明细来源', quantity BIGINT NOT NULL COMMENT '从该实收装入本箱的件数', - PRIMARY KEY(carton_id,receipt_id), KEY idx_procurement_carton_receipt(receipt_id), CONSTRAINT chk_procurement_carton_source_quantity CHECK(quantity>0) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='采购箱内货物的实收来源与数量'; -CREATE TABLE ll_procurement_shipment ( - id BIGINT NOT NULL COMMENT '采购交寄ID', order_id BIGINT NOT NULL COMMENT '采购订单ID', customer_id VARCHAR(128) NOT NULL COMMENT '客户隔离键', - shipment_no VARCHAR(64) NOT NULL COMMENT '交寄业务编号', leg VARCHAR(16) NOT NULL COMMENT 'OVERSEAS国际交寄或DOMESTIC寄客户', - manifest_id BIGINT NOT NULL COMMENT '原海外装箱清单版本', shipped_at DATETIME(3) NOT NULL COMMENT '整批真实交承运方时间', - address_snapshot JSON NOT NULL COMMENT '本段交寄时的收寄姓名电话地址快照', created_by VARCHAR(128) NOT NULL COMMENT '交寄确认人', - created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '确认时间', - PRIMARY KEY(id), UNIQUE KEY uk_procurement_shipment_leg(order_id,leg), UNIQUE KEY uk_procurement_shipment_no(shipment_no), - CONSTRAINT chk_procurement_shipment_leg CHECK(leg IN('OVERSEAS','DOMESTIC')) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='采购两段各一次整批真实交寄'; -CREATE TABLE ll_procurement_shipment_item ( - shipment_id BIGINT NOT NULL COMMENT '采购交寄ID', carton_id BIGINT NOT NULL COMMENT '原海外装箱版本内箱ID', quantity BIGINT NOT NULL COMMENT '本次实际交寄件数', - carrier VARCHAR(128) NOT NULL COMMENT '承运商快照', tracking_no VARCHAR(128) NOT NULL COMMENT '运单号快照', - label_snapshot JSON NOT NULL COMMENT '当次箱规、面单及货物来源快照', - PRIMARY KEY(shipment_id,carton_id), CONSTRAINT chk_procurement_shipment_item_quantity CHECK(quantity>0) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='采购整批交寄逐箱事实'; USE baiyue_lastleg; -- 采购面单、逐箱贴单、预估核对与国内一对一换包装;不重建历史发运事实。 -CREATE TABLE ll_procurement_label_set ( - id BIGINT NOT NULL COMMENT '面单批版本ID', order_id BIGINT NOT NULL COMMENT '采购订单ID', customer_id VARCHAR(128) NOT NULL COMMENT '客户隔离键', - leg VARCHAR(16) NOT NULL COMMENT 'OVERSEAS国际段或DOMESTIC寄客户段', version INT NOT NULL COMMENT '订单段内面单版本', - manifest_id BIGINT NOT NULL COMMENT '原海外确认装箱版本', cargo_hash CHAR(64) NOT NULL COMMENT '箱规及有效货物来源摘要', - sender_snapshot JSON NOT NULL COMMENT '寄件人姓名电话地址快照', receiver_snapshot JSON NOT NULL COMMENT '收件人姓名电话地址快照', - created_by VARCHAR(128) NOT NULL COMMENT '制单操作人', created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '上传确认时间', - PRIMARY KEY(id), UNIQUE KEY uk_procurement_label_version(order_id,leg,version), - CONSTRAINT chk_procurement_label_leg CHECK(leg IN('OVERSEAS','DOMESTIC')) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='采购两段面单不可变版本和地址快照'; -CREATE TABLE ll_procurement_label_item ( - label_set_id BIGINT NOT NULL COMMENT '面单批版本ID', carton_id BIGINT NOT NULL COMMENT '原海外清单箱ID', - file_id BIGINT NOT NULL COMMENT '本采购订单面单文件', page_no INT NOT NULL COMMENT '该箱对应文件页码,从1开始', - carrier VARCHAR(128) NOT NULL COMMENT '承运商', tracking_no VARCHAR(128) NOT NULL COMMENT '箱主或子运单号', - applied_at DATETIME(3) NULL COMMENT '真实贴单时间', applied_by VARCHAR(128) NULL COMMENT '贴单确认人', - PRIMARY KEY(label_set_id,carton_id), CONSTRAINT chk_procurement_label_page CHECK(page_no>0) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='采购逐箱文件页关联及贴单事实'; -CREATE TABLE ll_procurement_estimate_review ( - id BIGINT NOT NULL COMMENT '预估核对ID', order_id BIGINT NOT NULL COMMENT '采购订单ID', label_set_id BIGINT NOT NULL COMMENT '已核对国际面单版本', - fee_id BIGINT NOT NULL COMMENT '唯一有效预估费用版本', reviewed_by VARCHAR(128) NOT NULL COMMENT '核价人员', - reviewed_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '核对时间', - PRIMARY KEY(id), UNIQUE KEY uk_procurement_estimate_review(label_set_id,fee_id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='面单清单与预估冻结的显式核对关联'; -CREATE TABLE ll_procurement_repack ( - id BIGINT NOT NULL COMMENT '换包装版本ID', order_id BIGINT NOT NULL COMMENT '采购订单ID', carton_id BIGINT NOT NULL COMMENT '原国际交寄箱ID,保持一对一来源', - length_cm DECIMAL(12,2) NOT NULL COMMENT '新外箱长厘米', width_cm DECIMAL(12,2) NOT NULL COMMENT '新外箱宽厘米', - height_cm DECIMAL(12,2) NOT NULL COMMENT '新外箱高厘米', gross_weight_kg DECIMAL(12,3) NOT NULL COMMENT '新封箱毛重千克', - voucher_file_id BIGINT NOT NULL COMMENT '本单换包装证据', reason VARCHAR(1000) NOT NULL COMMENT '换包装依据', - created_by VARCHAR(128) NOT NULL COMMENT '国内作业人员', created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '登记时间', - PRIMARY KEY(id), KEY idx_procurement_repack_carton(order_id,carton_id,id), - CONSTRAINT chk_procurement_repack_measurement CHECK(length_cm>0 AND width_cm>0 AND height_cm>0 AND gross_weight_kg>0) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='采购国内原箱一对一换包装不可变历史'; USE baiyue_lastleg; -- 费用链目标更正快照与关联;资金流水仍唯一归Finance,不回填旧费用。 -CREATE TABLE ll_procurement_fee_correction ( - id BIGINT NOT NULL COMMENT '费用链更正ID', order_id BIGINT NOT NULL COMMENT '采购订单ID', customer_id VARCHAR(128) NOT NULL COMMENT '客户隔离键', - correction_no VARCHAR(64) NOT NULL COMMENT '财务更正业务号', root_fee_id BIGINT NOT NULL COMMENT '原实际费用链根', - idempotency_key VARCHAR(128) NOT NULL COMMENT '本单更正请求幂等键', request_hash CHAR(64) NOT NULL COMMENT '更正目标及凭证请求摘要', - target_original_amount DECIMAL(20,2) NOT NULL COMMENT '本次正确原币总额', original_currency CHAR(3) NOT NULL COMMENT '沿用原费用币种', - exchange_rate DECIMAL(20,6) NOT NULL COMMENT '沿用原费用汇率快照', exchange_rate_month CHAR(7) NOT NULL COMMENT '原汇率月份,不取本次操作月份', - target_amount DECIMAL(20,2) NOT NULL COMMENT '正确总额折算CNY', previous_net_amount DECIMAL(20,2) NOT NULL COMMENT '确认前整链净实扣CNY', - difference_amount DECIMAL(20,2) NOT NULL COMMENT 'CNY正差额追加、负差额退款、零差额仅留核对记录', - voucher_file_id BIGINT NOT NULL COMMENT '本订单财务更正凭证', reason VARCHAR(500) NOT NULL COMMENT '更正依据', - created_by VARCHAR(128) NOT NULL COMMENT '授权财务操作人', created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '确认时间', - PRIMARY KEY(id), UNIQUE KEY uk_procurement_correction_no(correction_no), UNIQUE KEY uk_procurement_correction_request(order_id,idempotency_key), - KEY idx_procurement_correction_root(order_id,root_fee_id,id), - CONSTRAINT chk_procurement_correction_amount CHECK(target_original_amount>=0 AND target_amount>=0 AND previous_net_amount>=0 AND exchange_rate>0) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='采购费用链更正目标与原汇率快照'; -CREATE TABLE ll_procurement_fee_correction_item ( - correction_id BIGINT NOT NULL COMMENT '本次费用链更正ID', fee_id BIGINT NOT NULL COMMENT '本次产生的独立正差额或退款费用ID', - PRIMARY KEY(correction_id,fee_id), UNIQUE KEY uk_procurement_correction_fee(fee_id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='采购费用目标更正与逐笔资金事实关联'; USE baiyue_lastleg; -- 单采购订单内的更正说明、阻断异常与关闭证据。不生成WMS库存或退货运输任务。 -CREATE TABLE ll_procurement_control_note ( - id BIGINT NOT NULL COMMENT '采购更正说明ID', order_id BIGINT NOT NULL COMMENT '采购订单ID', customer_id VARCHAR(128) NOT NULL COMMENT '客户隔离键', - note_no VARCHAR(64) NOT NULL COMMENT '更正说明业务号', target_type VARCHAR(24) NOT NULL COMMENT '购买、仓库、实收或交寄事实类型', - target_id BIGINT NULL COMMENT '本单事实对象引用,购买为空', blocking TINYINT(1) NOT NULL COMMENT '是否阻断后续计划履约', - voucher_file_id BIGINT NOT NULL COMMENT '本单内部事实更正凭证', reason VARCHAR(1000) NOT NULL COMMENT '内部更正说明', - client_summary VARCHAR(500) NULL COMMENT '人工确认的对客摘要,不复制内部原始内容', - original_value_json JSON NULL COMMENT '服务端记录的原事实快照', corrected_value_json JSON NULL COMMENT '服务端记录的更正事实快照', - created_by VARCHAR(128) NOT NULL COMMENT '采购管理操作人', created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '登记时间', - resolved_by VARCHAR(128) NULL COMMENT '处理确认人', resolved_at DATETIME(3) NULL COMMENT '阻断解除时间', - resolution_reason VARCHAR(1000) NULL COMMENT '内部处理结果', resolution_voucher_id BIGINT NULL COMMENT '本单处理凭证', - PRIMARY KEY(id), UNIQUE KEY uk_procurement_note_no(note_no), KEY idx_procurement_note_block(order_id,blocking,resolved_at) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='采购不可覆盖事实的更正说明及阻断异常'; -CREATE TABLE ll_procurement_closure ( - id BIGINT NOT NULL COMMENT '采购异常关闭ID', order_id BIGINT NOT NULL COMMENT '采购订单ID', customer_id VARCHAR(128) NOT NULL COMMENT '客户隔离键', - closure_no VARCHAR(64) NOT NULL COMMENT '异常关闭业务号', customer_voucher_file_id BIGINT NOT NULL COMMENT '客户同意停止履约凭证', - financial_conclusion VARCHAR(1000) NOT NULL COMMENT '已知费用及后续无需收费的处理结论,不改变资金事实', - reason VARCHAR(1000) NOT NULL COMMENT '内部关闭原因', client_summary VARCHAR(500) NOT NULL COMMENT '人工确认的对客关闭摘要', - created_by VARCHAR(128) NOT NULL COMMENT '采购管理确认人', created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '关闭确认时间', - PRIMARY KEY(id), UNIQUE KEY uk_procurement_closure_order(order_id), UNIQUE KEY uk_procurement_closure_no(closure_no) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='采购实物资金收口后的异常关闭事实'; -CREATE TABLE ll_procurement_closure_item ( - id BIGINT NOT NULL COMMENT '关闭货物处置ID', closure_id BIGINT NOT NULL COMMENT '采购关闭ID', - source_kind VARCHAR(32) NOT NULL COMMENT '购买未达、在途未达、合格、破损或多货来源', source_id BIGINT NULL COMMENT '原实收或国际交寄箱,购买未达为空', - quantity BIGINT NOT NULL COMMENT '本来源最终明确去向件数', decision VARCHAR(40) NOT NULL COMMENT '供应商保管、退还、报失、客户授权保管或处置', - destination VARCHAR(500) NOT NULL COMMENT '实际去向及接收方说明', voucher_file_id BIGINT NOT NULL COMMENT '本单实物处置证明', - PRIMARY KEY(id), KEY idx_procurement_closure_source(closure_id,source_kind,source_id), - CONSTRAINT chk_procurement_closure_quantity CHECK(quantity>0) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='异常关闭逐来源剩余货物最终去向,不产生新发运'; USE baiyue_lastleg; @@ -4579,20 +4338,21 @@ CREATE TABLE ll_appointment_impact_history ( -- SKU 换标实物转换:先执行本增量,再部署服务。不会自动改写存量业务事实。 CREATE TABLE ll_return_sku_conversion ( id BIGINT NOT NULL COMMENT 'SKU库存转换记录ID', - customer_id VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户隔离键', - overseas_warehouse_id VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '操作海外仓ID', + customer_id VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '客户隔离键', + overseas_warehouse_id VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '操作海外仓ID', application_id BIGINT NOT NULL COMMENT '换标申请ID', - relabel_no VARCHAR(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '换标单号', + relabel_no VARCHAR(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '换标单号', item_id BIGINT NOT NULL COMMENT '换标箱级明细ID', round_key VARCHAR(64) NOT NULL COMMENT '作业轮次,退回重做使用新轮次', carton_id BIGINT NOT NULL COMMENT '实物来源包裹或箱ID', carton_mark VARCHAR(128) NOT NULL COMMENT '完工时仓库识别码', - old_sku_code VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '换标前SKU', - new_sku_code VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '换标后SKU', - product_name_zh VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL COMMENT '中文品名快照', + old_sku_code VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '换标前SKU', + new_sku_code VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '换标后SKU', + product_name_zh VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL COMMENT '中文品名快照', quantity BIGINT NOT NULL COMMENT '实际完成转换件数', status VARCHAR(32) NOT NULL COMMENT 'APPLIED待客户确认/CONFIRMED已确认/REVERSED退回冲回', ownership_json LONGTEXT NOT NULL COMMENT '本次转换件数的来源退货单快照,供精确冲回', + storage_rent_shares_json LONGTEXT NULL COMMENT '原仓租批次迁移快照', completed_at DATETIME(3) NOT NULL COMMENT '实物SKU转换时间', confirmed_at DATETIME(3) NULL COMMENT '客户确认时间', reversed_at DATETIME(3) NULL COMMENT '退回冲回时间', @@ -4601,7 +4361,7 @@ CREATE TABLE ll_return_sku_conversion ( KEY idx_return_sku_conversion_app(customer_id,application_id,status), KEY idx_return_sku_conversion_carton(customer_id,carton_id), CONSTRAINT chk_return_sku_conversion_quantity CHECK(quantity>0) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='退货实物SKU换标库存转换和冲回记录'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='退货实物SKU换标库存转换和冲回记录'; -- PDA cargo discard photo evidence CREATE TABLE ll_cargo_discard_photo ( @@ -4619,7 +4379,7 @@ CREATE TABLE ll_cargo_discard_photo ( PRIMARY KEY (id), UNIQUE KEY uk_cargo_discard_photo_file (file_id), KEY idx_cargo_discard_photo_application (customer_id, application_id, pallet_no) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='海外PDA货物废弃照片凭证'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='海外PDA货物废弃照片凭证'; -- 货物废弃支持所有入库来源及散箱全量作业;不回填或修改历史单据。 @@ -4773,13 +4533,13 @@ CREATE TABLE ll_dispatch_appointment_allocation ( PRIMARY KEY(dispatch_item_id,batch_key,source_key,destination_key), KEY idx_dispatch_appointment_batch(batch_key,customer_id), KEY idx_dispatch_appointment_dispatch(dispatch_id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='排车实物与预约批次逐票分配,原执行域事实'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='排车实物与预约批次逐票分配,原执行域事实'; CREATE TABLE ll_appointment_work_commitment ( entity_key VARCHAR(160) NOT NULL COMMENT '原业务实体稳定键', state_json LONGTEXT NOT NULL COMMENT '原业务命令产生的只读语义影响基线,不是第二份可编辑计划', impact_version BIGINT NOT NULL DEFAULT 0 COMMENT '承诺影响单调递增版本', PRIMARY KEY (entity_key) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='工作事项承诺影响审计基线'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='工作事项承诺影响审计基线'; CREATE TABLE ll_appointment_work_guard ( id INT NOT NULL COMMENT '工作事项协调锁,固定1', scan_cursor VARCHAR(128) NULL COMMENT '已开船来源同步游标', @@ -4791,7 +4551,7 @@ CREATE TABLE ll_appointment_work_guard ( synchronized_requirement_version BIGINT NOT NULL DEFAULT 0 COMMENT '完整扫描已消费需求版本', scan_requirement_version BIGINT NOT NULL DEFAULT 0 COMMENT '当前扫描需求版本,防跨页漂移', PRIMARY KEY (id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='预约工作事项同步与命令串行守卫'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='预约工作事项同步与命令串行守卫'; INSERT INTO ll_appointment_work_guard (id,initialized) VALUES (1,FALSE); CREATE TABLE ll_appointment_work ( @@ -4802,7 +4562,7 @@ CREATE TABLE ll_appointment_work ( state_json LONGTEXT NOT NULL COMMENT '工作关系责任跟进及影响基线,不保存可编辑业务执行事实', PRIMARY KEY (work_item_key), KEY idx_appointment_work_container (container_booking_id,retired) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='预约工作事项需求与关系'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='预约工作事项需求与关系'; CREATE TABLE ll_appointment_work_history ( id VARCHAR(36) NOT NULL COMMENT '历史记录标识', @@ -4818,7 +4578,7 @@ CREATE TABLE ll_appointment_work_history ( PRIMARY KEY (id), UNIQUE KEY uk_appointment_work_command (work_item_key,command_key), KEY idx_appointment_work_history (work_item_key,occurred_at) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='预约工作事项历史与幂等命令'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='预约工作事项历史与幂等命令'; -- 海外业务短单号分配时钟。 -- 仓储转运上门取货表归尾程服务,仅用于空库初始化。 @@ -4965,11 +4725,11 @@ CREATE TABLE ll_dispatch_truck_task ( active_order VARCHAR(193) NULL COMMENT '有效原单唯一键;取消置空,完成保留', PRIMARY KEY(dispatch_no), UNIQUE KEY uk_transport_trip(trip_no), UNIQUE KEY uk_transport_source(active_order), KEY idx_transport_order(customer_id,order_no) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='排车与本土卡车原单关联'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='排车与本土卡车原单关联'; CREATE TABLE ll_dispatch_transport_create ( trip_no VARCHAR(64) NOT NULL PRIMARY KEY COMMENT '本次运输资源记录号', request_hash CHAR(64) NOT NULL COMMENT '首次安排请求摘要' -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='本土卡车排车创建请求账本'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='本土卡车排车创建请求账本'; CREATE TABLE ll_dispatch_transport_command ( dispatch_no VARCHAR(64) NOT NULL COMMENT '排车业务号', request_key VARCHAR(128) NOT NULL COMMENT '客户端幂等请求键', @@ -4977,7 +4737,7 @@ CREATE TABLE ll_dispatch_transport_command ( evidence_json JSON NULL COMMENT '已确认运输完成凭证标识列表', created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '业务命令提交时间', PRIMARY KEY(dispatch_no,request_key) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='排车运输执行命令与凭证账本'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='排车运输执行命令与凭证账本'; USE baiyue_lastleg; CREATE TABLE ll_document_sequence ( @@ -4985,8 +4745,121 @@ CREATE TABLE ll_document_sequence ( current_value BIGINT NOT NULL COMMENT '该前缀当日已分配流水,已分配号不回收', PRIMARY KEY (sequence_key), CONSTRAINT chk_ll_document_sequence_value CHECK (current_value > 0) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='业务单据每日独立原子流水'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='业务单据每日独立原子流水'; +-- 海外费用资金及实收仓租。 +-- 海外人工费用资金编排。金额与余额以 Finance 钱包为唯一事实,本表仅记录指令及回执状态。 +CREATE TABLE ll_finance_fee_funding ( + source_fee_id BIGINT NOT NULL COMMENT '关联海外费用稳定来源ID', + customer_id VARCHAR(128) NOT NULL COMMENT '费用所属客户隔离标识', + collection_mode VARCHAR(16) NOT NULL COMMENT '客户收费方式:BILL账单结算,WALLET余额及授信', + wallet_status VARCHAR(32) NOT NULL COMMENT '资金进度:待冻结、已冻结、待扣款、已扣款、待释放及失败状态', + wallet_business_no VARCHAR(128) NULL COMMENT '本次冻结使用的独立业务编号,重提使用新编号', + command_event_id VARCHAR(64) NULL COMMENT '当前待处理资金指令事件ID', + wallet_hold_id VARCHAR(64) NULL COMMENT 'Finance确认的资金冻结记录ID', + wallet_ledger_id VARCHAR(64) NULL COMMENT 'Finance确认的已扣资金流水ID', + command_version BIGINT NOT NULL DEFAULT 0 COMMENT '资金指令单调递增版本,防止重试重复入账', + error_message VARCHAR(500) NULL COMMENT '最新资金失败原因,成功后清空', + command_history JSON NOT NULL COMMENT '已发资金指令及核验结果哈希,识别重复和错序回执', + created_at DATETIME(3) NOT NULL COMMENT '资金编排创建UTC时间', + updated_at DATETIME(3) NOT NULL COMMENT '资金编排最近变更UTC时间', + PRIMARY KEY (source_fee_id), + KEY idx_ll_fee_funding_customer_state (customer_id, wallet_status), + KEY idx_ll_fee_funding_command (command_event_id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='海外人工费用资金冻结、审核扣款及释放编排'; + +CREATE TABLE IF NOT EXISTS ll_storage_rent_inbound ( + receipt_id bigint NOT NULL COMMENT '实际入库凭证主键,防止重复累计计租数量', + lot_id bigint NOT NULL COMMENT '头程同单同仓计租批次主键', + quantity decimal(24,12) NOT NULL COMMENT '本次实收追加计租箱数', + created_at datetime(3) NOT NULL COMMENT '计租登记时间,采用协调世界时', + PRIMARY KEY (receipt_id), + KEY idx_storage_rent_inbound_lot (lot_id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='头程实收计租幂等登记'; + +CREATE TABLE ll_storage_rent_lot ( + id bigint NOT NULL COMMENT '原批次主键', + source_key varchar(80) NOT NULL COMMENT '实收来源幂等键', + customer_id varchar(32) NOT NULL COMMENT '客户标识', + customer_name varchar(255) DEFAULT NULL COMMENT '客户名称', + warehouse_id varchar(32) NOT NULL COMMENT '海外仓标识', + warehouse_name varchar(255) DEFAULT NULL COMMENT '海外仓名称', + business_type varchar(32) NOT NULL COMMENT '原入库业务类型', + business_id bigint NOT NULL COMMENT '原入库业务主键', + business_no varchar(128) NOT NULL COMMENT '原入库业务单号', + order_id bigint DEFAULT NULL COMMENT '尾程订单主键', + inbound_code varchar(128) DEFAULT NULL COMMENT '入仓码', + balance_id bigint DEFAULT NULL COMMENT '普通库存余额主键', + batch_no varchar(128) DEFAULT NULL COMMENT '原实收批次', + sku_code varchar(128) DEFAULT NULL COMMENT '商品编码', + rule_code varchar(32) NOT NULL COMMENT '仓租规则', + original_quantity decimal(24,12) NOT NULL COMMENT '实收原始数量', + remaining_quantity decimal(24,12) NOT NULL COMMENT '未出库原始数量', + original_units decimal(24,12) DEFAULT NULL COMMENT '原批次计费体积或托数', + charged_days bigint NOT NULL DEFAULT 0 COMMENT '退货原托已生成仓租的截止库龄天数,同一天不重复计费', + inbound_at datetime(3) NOT NULL COMMENT '实际入库时间', + created_at datetime(3) NOT NULL COMMENT '创建时间', + updated_at datetime(3) NOT NULL COMMENT '更新时间', + PRIMARY KEY (id), + UNIQUE KEY uk_storage_rent_lot_source (source_key), + KEY idx_storage_rent_stock (balance_id,batch_no), + KEY idx_storage_rent_customer (customer_id,warehouse_id,inbound_at) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='实收计租原批次'; + +CREATE TABLE ll_storage_rent_share ( + balance_id bigint DEFAULT NULL COMMENT '普通库存当前余额,退货箱份额为空', + batch_no varchar(128) DEFAULT NULL COMMENT '普通库存当前批次,换标搬移保持原计租批次引用', + id bigint NOT NULL COMMENT '份额主键', + lot_id bigint NOT NULL COMMENT '原计租批次主键', + customer_id varchar(32) NOT NULL COMMENT '客户标识', + warehouse_id varchar(32) NOT NULL COMMENT '海外仓标识', + carton_mark varchar(255) DEFAULT NULL COMMENT '当前箱唛', + sku_code varchar(128) DEFAULT NULL COMMENT '商品编码', + quantity decimal(24,12) NOT NULL COMMENT '剩余商品数量', + PRIMARY KEY (id), + UNIQUE KEY uk_storage_rent_return_share (lot_id,carton_mark,sku_code), + UNIQUE KEY uk_storage_rent_stock_share (lot_id,balance_id,batch_no), + KEY idx_storage_rent_carton (customer_id,warehouse_id,carton_mark), + KEY idx_storage_rent_share_stock (customer_id,warehouse_id,balance_id,batch_no) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='当前库存与原计租批次份额'; + +CREATE TABLE ll_storage_rent_preparation ( + id bigint NOT NULL COMMENT '预校验主键', + warehouse_id varchar(32) NOT NULL COMMENT '作业海外仓', + customer_id varchar(32) NOT NULL COMMENT '费用客户', + mode varchar(32) NOT NULL COMMENT '扫码类型', + execution_no varchar(128) NOT NULL COMMENT '实际作业单号', + request_key varchar(255) NOT NULL COMMENT '原扫码幂等键', + request_hash varchar(64) NOT NULL COMMENT '原扫码内容哈希', + attempt int NOT NULL COMMENT '冻结尝试次数', + business_type varchar(32) NOT NULL COMMENT '本次应收业务类型', + business_id bigint NOT NULL COMMENT '本次应收业务主键', + business_no varchar(128) NOT NULL COMMENT '本次应收业务单号', + status varchar(32) NOT NULL COMMENT '资金与扫码状态', + plan_json json NOT NULL COMMENT '原批次计费不可变快照', + original_amount decimal(24,12) NOT NULL COMMENT '原币仓租金额,币种见计费快照', + amount decimal(24,12) NOT NULL COMMENT '人民币结算金额', + exchange_rate decimal(24,12) NULL COMMENT '冻结确认汇率,免费出库为空', + exchange_rate_month varchar(7) NOT NULL COMMENT '汇率月份', + freeze_event_id varchar(128) DEFAULT NULL COMMENT '冻结指令事件', + settle_event_id varchar(128) DEFAULT NULL COMMENT '扣款指令事件', + release_event_id varchar(128) DEFAULT NULL COMMENT '解冻指令事件', + release_status varchar(32) DEFAULT NULL COMMENT '解冻回执状态,失败后由恢复任务重发', + wallet_version bigint NOT NULL COMMENT '同一预占资金命令版本,重试使用新版本保留审计', + hold_id varchar(128) DEFAULT NULL COMMENT '财务冻结主键', + ledger_id varchar(128) DEFAULT NULL COMMENT '财务扣款流水', + error_message varchar(1000) DEFAULT NULL COMMENT '资金处理结果', + source_fee_id bigint DEFAULT NULL COMMENT '财务费用来源主键', + created_at datetime(3) NOT NULL COMMENT '预校验时间', + consumed_at datetime(3) DEFAULT NULL COMMENT '实际扫描出库时间', + updated_at datetime(3) NOT NULL COMMENT '更新时间', + PRIMARY KEY (id), + UNIQUE KEY uk_storage_rent_request (warehouse_id,mode,request_key,attempt), + KEY idx_storage_rent_execution (mode,execution_no,status), + KEY idx_storage_rent_business (customer_id,business_no), + KEY idx_storage_rent_recovery (status,updated_at), + KEY idx_storage_rent_release (release_status,updated_at) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='仓租扫码资金预占与结算快照'; CREATE TABLE ll_fleet_resource_photo ( resource_kind VARCHAR(16) NOT NULL COMMENT '档案类型:车辆或司机', @@ -5002,4 +4875,61 @@ CREATE TABLE ll_fleet_resource_photo ( UNIQUE KEY uk_fleet_resource_photo_file (resource_kind, resource_id, file_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='车辆及司机档案照片版本记录'; -SELECT 'PASS' AS initialization_status, 'OVERSEAS' AS region, 235 AS initialized_tables; +USE baiyue_lastleg; +-- 简化采购:独立采购库存,收货、占用和签收扣减均由订单锁串行控制。 +CREATE TABLE IF NOT EXISTS ll_procurement_workflow_receipt ( + id BIGINT NOT NULL PRIMARY KEY COMMENT '批次主键', + order_id BIGINT NOT NULL COMMENT '采购单主键', + quantity BIGINT NOT NULL COMMENT '本批次数量', + file_ids TEXT NOT NULL COMMENT '本批次关联凭证文件主键列表', + received_at DATETIME(3) NOT NULL COMMENT '本批次实收时间', + created_by VARCHAR(128) NOT NULL COMMENT '作业人标识', + KEY idx_procurement_workflow_receipt (order_id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='采购分批收货照片与实收数量'; + +CREATE TABLE IF NOT EXISTS ll_procurement_workflow_outbound ( + id BIGINT NOT NULL PRIMARY KEY COMMENT '批次主键', + order_id BIGINT NOT NULL COMMENT '采购单主键', + outbound_no VARCHAR(64) NOT NULL COMMENT '采购出库单号', + quantity BIGINT NOT NULL COMMENT '本批次数量', + tracking_no VARCHAR(255) NOT NULL COMMENT '物流跟踪号', + status VARCHAR(32) NOT NULL COMMENT '批次运输派送及签收状态', + file_ids TEXT NOT NULL COMMENT '本批次关联凭证文件主键列表', + created_at DATETIME(3) NOT NULL COMMENT '记录创建时间', + completed_at DATETIME(3) NULL COMMENT '本批次签收完成时间', + created_by VARCHAR(128) NOT NULL COMMENT '作业人标识', + UNIQUE KEY uk_procurement_workflow_outbound_no (outbound_no), + KEY idx_procurement_workflow_outbound (order_id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='采购分批出库物流与签收凭证'; + +CREATE TABLE IF NOT EXISTS ll_procurement_workflow_command ( + order_id BIGINT NOT NULL COMMENT '采购单主键', + command_key VARCHAR(128) NOT NULL COMMENT '本单操作幂等键', + request_hash VARCHAR(64) NOT NULL COMMENT '动作及请求参数哈希', + operator_id VARCHAR(128) NOT NULL COMMENT '作业人标识', + created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '记录创建时间', + PRIMARY KEY (order_id,command_key) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='采购操作幂等及审计'; + +CREATE TABLE ll_order_budget_reservation ( + id BIGINT NOT NULL COMMENT '预算冻结记录主键', + customer_id VARCHAR(64) NOT NULL COMMENT '客户隔离标识', + kind VARCHAR(32) NOT NULL COMMENT '客户下单业务类型', + request_key VARCHAR(128) NOT NULL COMMENT '下单幂等键', + order_no VARCHAR(64) NULL COMMENT '成功创建的业务单号', + original_amount DECIMAL(18,2) NOT NULL COMMENT '客户预估原币金额', + currency VARCHAR(3) NOT NULL COMMENT '预估金额币种', + amount_cny DECIMAL(18,2) NOT NULL COMMENT '按下单月份汇率折算的人民币冻结金额', + status VARCHAR(32) NOT NULL COMMENT '资金冻结或释放回执状态', + error_message VARCHAR(1000) NULL COMMENT '资金失败原因', + command_event_id VARCHAR(64) NULL COMMENT '当前资金指令事件标识', + command_version BIGINT NOT NULL DEFAULT 0 COMMENT '资金指令单调版本', + release_requested TINYINT NOT NULL DEFAULT 0 COMMENT '是否已请求释放预占', + created_at DATETIME(3) NOT NULL COMMENT '首次预占时间', + updated_at DATETIME(3) NOT NULL COMMENT '最近资金状态更新时间', + PRIMARY KEY(id), UNIQUE KEY uk_order_budget_request(customer_id,kind,request_key), + KEY idx_order_budget_release(status,release_requested,created_at), + KEY idx_order_budget_order(customer_id,kind,order_no) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='海外客户下单预估资金冻结投影'; + +SELECT 'PASS' AS initialization_status, 'OVERSEAS' AS region, 223 AS initialized_tables; diff --git a/docs/business-index.md b/docs/business-index.md new file mode 100644 index 000000000..e69de29bb diff --git a/file/src/main/java/com/baiyue/file/service/BusinessFileCategory.java b/file/src/main/java/com/baiyue/file/service/BusinessFileCategory.java index 5de589c09..b777e60a2 100644 --- a/file/src/main/java/com/baiyue/file/service/BusinessFileCategory.java +++ b/file/src/main/java/com/baiyue/file/service/BusinessFileCategory.java @@ -14,8 +14,10 @@ enum BusinessFileCategory { OVERSEAS_DISPATCH_VEHICLE_EVIDENCE(false, FormatPolicy.PDF_IMAGE, Limits.STANDARD, BusinessIdPolicy.POSITIVE_LONG), OVERSEAS_DISPATCH_DRIVER_EVIDENCE(false, FormatPolicy.PDF_IMAGE, Limits.STANDARD, BusinessIdPolicy.POSITIVE_LONG), OVERSEAS_DISPATCH_EXCEPTION_EVIDENCE(false, FormatPolicy.IMAGE, Limits.STANDARD, BusinessIdPolicy.POSITIVE_LONG), + OVERSEAS_DISPATCH_LOOSEBOARD_PICKUP_EVIDENCE(false, FormatPolicy.IMAGE, Limits.STANDARD, BusinessIdPolicy.POSITIVE_LONG), + OVERSEAS_DISPATCH_LOOSEBOARD_ARRIVAL_EVIDENCE(false, FormatPolicy.IMAGE, Limits.STANDARD, BusinessIdPolicy.POSITIVE_LONG), OVERSEAS_DISPATCH_DRIVER_PHOTO_EVIDENCE(false, FormatPolicy.IMAGE, Limits.STANDARD, BusinessIdPolicy.POSITIVE_LONG), - OVERSEAS_DISPATCH_LOADING_PHOTO(false, FormatPolicy.IMAGE, Limits.STANDARD, BusinessIdPolicy.POSITIVE_LONG), + OVERSEAS_DISPATCH_LOADING_PHOTO(false, FormatPolicy.PDF_IMAGE, Limits.STANDARD, BusinessIdPolicy.POSITIVE_LONG), OVERSEAS_DISPATCH_FUEL_PHOTO(false, FormatPolicy.IMAGE, Limits.STANDARD, BusinessIdPolicy.POSITIVE_LONG), FLEET_VEHICLE_RECORD_PHOTO(false, FormatPolicy.IMAGE, Limits.STANDARD, BusinessIdPolicy.POSITIVE_LONG), DELIVERY_LOADING_PHOTO(true, FormatPolicy.ARBITRARY, Limits.CUSTOMS, BusinessIdPolicy.POSITIVE_LONG), @@ -47,6 +49,7 @@ enum BusinessFileCategory { OVERSEAS_RELABEL_SKU_TOTAL_DOCUMENT(true, FormatPolicy.PDF, Limits.STANDARD, BusinessIdPolicy.POSITIVE_LONG), OVERSEAS_RELABEL_ITEM_DOCUMENT(true, FormatPolicy.PDF, Limits.STANDARD, BusinessIdPolicy.POSITIVE_LONG), OVERSEAS_EXCEPTION_EVIDENCE(true, FormatPolicy.STANDARD, Limits.STANDARD, BusinessIdPolicy.POSITIVE_LONG), + OVERSEAS_CUSTOMER_VISIT(true, FormatPolicy.CUSTOMER_VISIT, Limits.CUSTOMER_VISIT, BusinessIdPolicy.POSITIVE_LONG), LOADING_LIST_CUSTOMS_SOURCE(true, FormatPolicy.ARBITRARY, Limits.CUSTOMS, BusinessIdPolicy.POSITIVE_LONG), LOADING_LIST_CUSTOMS_RECEIPT(true, FormatPolicy.ARBITRARY, Limits.CUSTOMS, BusinessIdPolicy.POSITIVE_LONG), LOADING_LIST_CUSTOMS_RESULT(true, FormatPolicy.ARBITRARY, Limits.CUSTOMS, BusinessIdPolicy.POSITIVE_LONG), @@ -82,7 +85,8 @@ enum BusinessFileCategory { && tenantId != 0) { throw new BizException(ResultCode.BAD_REQUEST, "排车运营照片必须归属平台"); } - if ((this == OVERSEAS_DISPATCH_VEHICLE_EVIDENCE || this == OVERSEAS_DISPATCH_DRIVER_EVIDENCE || this == OVERSEAS_DISPATCH_DRIVER_PHOTO_EVIDENCE || this == OVERSEAS_DISPATCH_EXCEPTION_EVIDENCE) + if ((this == OVERSEAS_DISPATCH_VEHICLE_EVIDENCE || this == OVERSEAS_DISPATCH_DRIVER_EVIDENCE || this == OVERSEAS_DISPATCH_DRIVER_PHOTO_EVIDENCE || this == OVERSEAS_DISPATCH_EXCEPTION_EVIDENCE + || this == OVERSEAS_DISPATCH_LOOSEBOARD_PICKUP_EVIDENCE || this == OVERSEAS_DISPATCH_LOOSEBOARD_ARRIVAL_EVIDENCE) && tenantId != 0) { throw new BizException(ResultCode.BAD_REQUEST, "排车证据必须归属平台"); } @@ -132,11 +136,12 @@ enum BusinessFileCategory { return Long.toString(value); } - enum FormatPolicy { STANDARD, ARBITRARY, XLSX, PDF, PDF_IMAGE, IMAGE } + enum FormatPolicy { STANDARD, ARBITRARY, XLSX, PDF, PDF_IMAGE, IMAGE, CUSTOMER_VISIT } private enum BusinessIdPolicy { POSITIVE_LONG, ORDER_BATCH_NO } private static final class Limits { private static final long STANDARD = 10L * 1024 * 1024; private static final long CUSTOMS = 50L * 1024 * 1024; private static final long ORDER_IMPORT = 5L * 1024 * 1024; + private static final long CUSTOMER_VISIT = 20L * 1024 * 1024; } } diff --git a/file/src/main/java/com/baiyue/file/service/BusinessFileStorage.java b/file/src/main/java/com/baiyue/file/service/BusinessFileStorage.java index 2b78410e0..924c8aee1 100644 --- a/file/src/main/java/com/baiyue/file/service/BusinessFileStorage.java +++ b/file/src/main/java/com/baiyue/file/service/BusinessFileStorage.java @@ -18,6 +18,7 @@ import java.security.NoSuchAlgorithmException; import java.util.HexFormat; import java.util.Locale; import java.util.Map; +import java.util.Set; import java.util.UUID; @Component @@ -27,6 +28,8 @@ public class BusinessFileStorage { private static final Map TYPES = Map.of( "text/plain", ".txt", "application/pdf", ".pdf", "image/jpeg", ".jpg", "image/png", ".png"); + private static final Set CUSTOMER_VISIT_EXTENSIONS = Set.of( + "pdf", "doc", "docx", "xls", "xlsx", "ppt", "pptx", "jpg", "jpeg", "png", "webp"); @Value("${baiyue.file.storage-root}") private Path configuredRoot; @@ -46,6 +49,7 @@ public class BusinessFileStorage { return store(tenantId, businessId, file, category.maxSizeBytes(), category.formatPolicy()); } + /** 按业务类别检查大小和格式,保存真实文件并返回存储结果。 */ private Stored store(long tenantId, String businessId, MultipartFile file, long maxSize, BusinessFileCategory.FormatPolicy formatPolicy) { if (file == null || file.isEmpty()) throw bad("附件不能为空"); @@ -55,6 +59,8 @@ public class BusinessFileStorage { String extension; if (formatPolicy == BusinessFileCategory.FormatPolicy.ARBITRARY) { extension = ""; + } else if (formatPolicy == BusinessFileCategory.FormatPolicy.CUSTOMER_VISIT) { + extension = customerVisitExtension(file); } else if (formatPolicy == BusinessFileCategory.FormatPolicy.PDF) { if (!"application/pdf".equals(type)) throw bad("附件仅支持PDF"); verifyContent(file, type); @@ -85,6 +91,17 @@ public class BusinessFileStorage { } } + /** 与回访入口保持相同扩展名约束,接受浏览器未提供 MIME 的 Office 资料。 */ + private static String customerVisitExtension(MultipartFile file) { + String name = safeName(file); + int dot = name.lastIndexOf('.'); + String extension = dot < 0 ? "" : name.substring(dot + 1).toLowerCase(Locale.ROOT); + if (!CUSTOMER_VISIT_EXTENSIONS.contains(extension)) { + throw bad("仅支持PDF、Office、JPG、PNG和WEBP文件"); + } + return "." + extension; + } + private static void verifyXlsx(MultipartFile file) { try (InputStream input = file.getInputStream()) { byte[] prefix = input.readNBytes(4); diff --git a/file/src/main/java/com/baiyue/file/service/GenericXlsxExportRunner.java b/file/src/main/java/com/baiyue/file/service/GenericXlsxExportRunner.java index 8be548608..72102eb77 100644 --- a/file/src/main/java/com/baiyue/file/service/GenericXlsxExportRunner.java +++ b/file/src/main/java/com/baiyue/file/service/GenericXlsxExportRunner.java @@ -174,7 +174,8 @@ public class GenericXlsxExportRunner { case STRING -> cell.setCellValue(safeString(String.valueOf(value), column.key(), warnedTruncatedColumns)); case INTEGER -> cell.setCellValue(new BigDecimal(String.valueOf(value)).longValueExact()); case DECIMAL -> cell.setCellValue(new BigDecimal(String.valueOf(value)).doubleValue()); - case BOOLEAN -> cell.setCellValue(value instanceof Boolean b ? b : Boolean.parseBoolean(String.valueOf(value))); + // 面向业务人员的是否字段统一显示中文,Provider 仍传输布尔类型。 + case BOOLEAN -> cell.setCellValue((value instanceof Boolean b ? b : Boolean.parseBoolean(String.valueOf(value))) ? "是" : "否"); case DATE -> { cell.setCellValue(LocalDate.parse(String.valueOf(value))); cell.setCellStyle(styles.date); } case DATETIME -> { cell.setCellValue(LocalDateTime.parse(String.valueOf(value))); cell.setCellStyle(styles.dateTime); } } diff --git a/file/src/main/resources/openapi/fleet-owned-business-photos-v1.yaml b/file/src/main/resources/openapi/fleet-owned-business-photos-v1.yaml index f1c9fd9f9..532fddedb 100644 --- a/file/src/main/resources/openapi/fleet-owned-business-photos-v1.yaml +++ b/file/src/main/resources/openapi/fleet-owned-business-photos-v1.yaml @@ -7,7 +7,7 @@ info: 复用InternalBusinessFileController,不增加公网File路径。 businessType分为OVERSEAS_DISPATCH_LOADING_PHOTO和OVERSEAS_DISPATCH_FUEL_PHOTO, tenantId固定0,businessId固定原Lastleg排车内部主键,不接受车次主键、车牌或客户ID替代。 - 仅非空JPG/PNG,最大10MiB,同时校验MIME与真实内容签名;两类照片不能交叉引用。 + 装车资料接受非空JPG/PNG/PDF(Lastleg仅允许司机驾驶证类别使用PDF),油耗仍仅接受JPG/PNG,最大10MiB,同时校验MIME与真实内容签名;两类照片不能交叉引用。 Lastleg按公开dispatchNo解析原排车,负责车辆归属、可见阶段、读写权限、附加记录版本、 分类附件引用和历史。文件上传不代表装车确认、签收、实到或运营信息保存。 File不读取Lastleg表,只按客户范围、业务类别、业务ID和文件ID核对文件复合归属。 diff --git a/file/src/test/java/com/baiyue/file/service/BusinessFileStorageTest.java b/file/src/test/java/com/baiyue/file/service/BusinessFileStorageTest.java index a08d53383..64d8492df 100644 --- a/file/src/test/java/com/baiyue/file/service/BusinessFileStorageTest.java +++ b/file/src/test/java/com/baiyue/file/service/BusinessFileStorageTest.java @@ -22,6 +22,12 @@ import static org.mockito.Mockito.when; class BusinessFileStorageTest { @TempDir Path root; + @Test void loadingLicenseStoresPdfButFuelStillRejectsPdf() { + var file=new MockMultipartFile("file","license.pdf","application/pdf","%PDF-1.4\n%%EOF".getBytes()); + var stored=storage().store(0L,"51",file,BusinessFileCategory.OVERSEAS_DISPATCH_LOADING_PHOTO); + assertThat(stored.contentType()).isEqualTo("application/pdf"); + assertThatThrownBy(()->storage().store(0L,"51",file,BusinessFileCategory.OVERSEAS_DISPATCH_FUEL_PHOTO)).isInstanceOf(BizException.class); + } @Test void arbitraryCustomsFormatIsStoredAsAttachmentObject() { BusinessFileStorage storage = storage(); diff --git a/file/src/test/java/com/baiyue/file/service/CustomerVisitBusinessFileTest.java b/file/src/test/java/com/baiyue/file/service/CustomerVisitBusinessFileTest.java new file mode 100644 index 000000000..0ecd8cee9 --- /dev/null +++ b/file/src/test/java/com/baiyue/file/service/CustomerVisitBusinessFileTest.java @@ -0,0 +1,161 @@ +package com.baiyue.file.service; + +import com.baomidou.mybatisplus.core.MybatisConfiguration; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.metadata.TableInfoHelper; +import com.baiyue.common.core.exception.BizException; +import com.baiyue.file.entity.FileObjectEntity; +import com.baiyue.file.mapper.FileObjectMapper; +import org.apache.ibatis.builder.MapperBuilderAssistant; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import org.junit.jupiter.params.provider.ValueSource; +import org.mockito.ArgumentCaptor; +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.test.util.ReflectionTestUtils; +import org.springframework.web.multipart.MultipartFile; + +import java.nio.file.Path; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.when; + +class CustomerVisitBusinessFileTest { + private static final String TYPE = "OVERSEAS_CUSTOMER_VISIT"; + private static final long MAX_SIZE = 20L * 1024 * 1024; + @TempDir Path root; + private BusinessFileStorage storage; + private FileObjectMapper files; + private InternalBusinessFileService service; + + /** 初始化文件元数据映射,供真实归属查询使用。 */ + @BeforeAll + static void initializeMapping() { + TableInfoHelper.initTableInfo(new MapperBuilderAssistant(new MybatisConfiguration(), "visit-files"), + FileObjectEntity.class); + } + + /** 使用真实本地存储,隔离数据库写入。 */ + @BeforeEach + void setUp() { + storage = new BusinessFileStorage(); + ReflectionTestUtils.setField(storage, "configuredRoot", root); + storage.initializeRoot(); + files = mock(FileObjectMapper.class); + service = new InternalBusinessFileService(); + ReflectionTestUtils.setField(service, "files", files); + ReflectionTestUtils.setField(service, "storage", storage); + } + + /** 页面允许的全部回访资料均可保存,并保持客户及回访记录的精确归属。 */ + @ParameterizedTest + @CsvSource(delimiter = '|', value = { + "pdf|application/pdf", "doc|application/msword", "docx|application/octet-stream", + "xls|application/vnd.ms-excel", "xlsx|application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + "ppt|application/vnd.ms-powerpoint", "pptx|application/vnd.openxmlformats-officedocument.presentationml.presentation", + "jpg|image/jpeg", "jpeg|image/jpeg", "png|image/png", "webp|image/webp" + }) + void uploadsAllSupportedFormatsWithCustomerAndVisitOwnership(String extension, String contentType) { + var file = new MockMultipartFile("file", "回访资料." + extension, contentType, new byte[]{1, 2, 3}); + when(files.insert(any(FileObjectEntity.class))).thenAnswer(call -> { + FileObjectEntity row = call.getArgument(0); + row.setId(801L); + return 1; + }); + + var metadata = service.upload(1001L, TYPE, "71", 9L, file); + + assertThat(metadata.ownerScope()).isEqualTo("CUSTOMER"); + assertThat(metadata.tenantId()).isEqualTo(1001L); + assertThat(metadata.businessType()).isEqualTo(TYPE); + assertThat(metadata.businessId()).isEqualTo("71"); + assertThat(metadata.fileId()).isEqualTo(801L); + assertThat(metadata.fileName()).isEqualTo(file.getOriginalFilename()); + assertThat(metadata.contentType()).isEqualTo(contentType); + assertThat(metadata.sizeBytes()).isEqualTo(3L); + var persisted = ArgumentCaptor.forClass(FileObjectEntity.class); + verify(files).insert(persisted.capture()); + assertThat(storage.read(persisted.getValue().getObjectKey())).containsExactly(1, 2, 3); + } + + /** MIME 缺失或扩展名大写不应阻断有效的 Office 回访资料。 */ + @Test + void acceptsOfficeFileWithoutBrowserMimeType() { + var file = new MockMultipartFile("file", "回访记录.DOCX", null, new byte[]{1}); + var stored = storage.store(1001L, "71", file, BusinessFileCategory.require(TYPE)); + assertThat(stored.contentType()).isEqualTo("application/octet-stream"); + assertThat(stored.objectKey()).endsWith(".docx"); + } + + /** 回访资料单独使用 20MiB 限制,不放宽其他业务附件的限制。 */ + @Test + void enforcesNonemptyAndTwentyMibBoundary() { + var category = BusinessFileCategory.require(TYPE); + assertThatThrownBy(() -> storage.store(1001L, "71", new MockMultipartFile("file", new byte[0]), category)) + .isInstanceOf(BizException.class).hasMessageContaining("不能为空"); + var oversized = mock(MultipartFile.class); + when(oversized.isEmpty()).thenReturn(false); + when(oversized.getSize()).thenReturn(MAX_SIZE + 1); + assertThatThrownBy(() -> storage.store(1001L, "71", oversized, category)) + .isInstanceOf(BizException.class).hasMessageContaining("20MB"); + var boundary = new MockMultipartFile("file", "回访.doc", "application/msword", new byte[(int) MAX_SIZE]); + assertThat(storage.store(1001L, "71", boundary, category).size()).isEqualTo(MAX_SIZE); + assertThat(BusinessFileCategory.CUSTOMS_DOCUMENT.maxSizeBytes()).isEqualTo(10L * 1024 * 1024); + } + + /** 不接受回访入口约定之外的文件或双扩展名可执行文件。 */ + @ParameterizedTest + @ValueSource(strings = {"readme.txt", "archive.zip", "visit.pdf.exe", "visit"}) + void rejectsUnsupportedFormatsBeforeMetadataIsWritten(String name) { + var file = new MockMultipartFile("file", name, "application/octet-stream", new byte[]{1}); + assertThatThrownBy(() -> service.upload(1001L, TYPE, "71", 9L, file)) + .isInstanceOf(BizException.class).hasMessageContaining("仅支持"); + verifyNoInteractions(files); + } + + /** 回访资料必须归属明确客户和有效回访记录。 */ + @Test + void rejectsPlatformScopeAndInvalidVisitId() { + var file = new MockMultipartFile("file", "visit.pdf", "application/pdf", new byte[]{1}); + assertThatThrownBy(() -> service.upload(0L, TYPE, "71", 9L, file)) + .isInstanceOf(BizException.class).hasMessageContaining("必须归属明确客户"); + assertThatThrownBy(() -> service.upload(1001L, TYPE, "0", 9L, file)) + .isInstanceOf(BizException.class).hasMessageContaining("业务ID必须有效"); + verifyNoInteractions(files); + } + + /** 保存回访时按客户、类型、回访记录和文件 ID 校验,不能串用其他资料。 */ + @Test + void metadataRequiresEveryOwnershipKey() { + var value = new FileObjectEntity(); + value.setId(801L); + value.setTenantId(1001L); + value.setBusinessType(TYPE); + value.setBusinessId("71"); + when(files.selectOne(any())).thenAnswer(call -> { + LambdaQueryWrapper query = call.getArgument(0); + assertThat(query.getSqlSegment()).contains("tenant_id", "business_type", "business_id", "id", "status", "deleted"); + return query.getParamNameValuePairs().values().containsAll( + java.util.List.of(1001L, TYPE, "71", 801L, "ACTIVE", false)) ? value : null; + }); + + assertThat(service.metadata(1001L, TYPE, "71", 801L).fileId()).isEqualTo(801L); + assertThatThrownBy(() -> service.metadata(1002L, TYPE, "71", 801L)) + .isInstanceOf(BizException.class).hasMessageContaining("归属不匹配"); + assertThatThrownBy(() -> service.metadata(1001L, TYPE, "72", 801L)) + .isInstanceOf(BizException.class).hasMessageContaining("归属不匹配"); + assertThatThrownBy(() -> service.metadata(1001L, "OVERSEAS_EXCEPTION_EVIDENCE", "71", 801L)) + .isInstanceOf(BizException.class).hasMessageContaining("归属不匹配"); + assertThatThrownBy(() -> service.metadata(1001L, TYPE, "71", 802L)) + .isInstanceOf(BizException.class).hasMessageContaining("归属不匹配"); + } +} diff --git a/file/src/test/java/com/baiyue/file/service/GenericXlsxExportRunnerTest.java b/file/src/test/java/com/baiyue/file/service/GenericXlsxExportRunnerTest.java index 815b76abe..cd2c491b5 100644 --- a/file/src/test/java/com/baiyue/file/service/GenericXlsxExportRunnerTest.java +++ b/file/src/test/java/com/baiyue/file/service/GenericXlsxExportRunnerTest.java @@ -48,7 +48,7 @@ class GenericXlsxExportRunnerTest { } @Test void writesRealTypedWorkbookAndCarriesSnapshotAcrossBatches() throws Exception { List columns=List.of(new ExportColumn("text","文本",ExportColumnType.STRING,18),new ExportColumn("integer","整数",ExportColumnType.INTEGER,12),new ExportColumn("decimal","金额",ExportColumnType.DECIMAL,12),new ExportColumn("flag","布尔",ExportColumnType.BOOLEAN,10),new ExportColumn("date","日期",ExportColumnType.DATE,12),new ExportColumn("time","时间",ExportColumnType.DATETIME,20)); - List> firstRows=IntStream.range(0,100).mapToObj(index -> Map.of("text",index==0?"=SUM(1,2)":"row-"+index,"integer",12,"decimal","3.50","flag",true,"date","2026-08-03","time","2026-08-03T21:00:00")).toList(); + List> firstRows=IntStream.range(0,100).mapToObj(index -> Map.of("text",index==0?"=SUM(1,2)":"row-"+index,"integer",12,"decimal","3.50","flag",index % 2 == 0,"date","2026-08-03","time","2026-08-03T21:00:00")).toList(); var first=new ExportBatchResponse(1,"sha256:test","snapshot-9",columns,firstRows,"10",true,101L); var second=new ExportBatchResponse(1,"sha256:test","snapshot-9",null,List.of(Map.of("text","\n=SUM(1,2)","integer",13,"decimal",4.5,"flag",false,"date","2026-08-04","time","2026-08-04T21:00:00")),null,false,101L); when(provider.batch(anyString(),eq("CUSTOMER_LIST"),any(),any())).thenReturn(ApiResponse.success(first),ApiResponse.success(second)); @@ -58,7 +58,7 @@ class GenericXlsxExportRunnerTest { assertEquals(101,result.rows()); try(var workbook=new XSSFWorkbook(storage.load(result.objectKey()).getInputStream())){ var row=workbook.getSheetAt(0).getRow(1);assertEquals("'=SUM(1,2)",row.getCell(0).getStringCellValue()); - assertEquals(CellType.NUMERIC,row.getCell(1).getCellType());assertEquals(CellType.NUMERIC,row.getCell(2).getCellType());assertEquals(CellType.BOOLEAN,row.getCell(3).getCellType());assertEquals(CellType.NUMERIC,row.getCell(4).getCellType());assertEquals(CellType.NUMERIC,row.getCell(5).getCellType()); + assertEquals(CellType.NUMERIC,row.getCell(1).getCellType());assertEquals(CellType.NUMERIC,row.getCell(2).getCellType());assertEquals(CellType.STRING,row.getCell(3).getCellType());assertEquals("是",row.getCell(3).getStringCellValue());assertEquals("否",workbook.getSheetAt(0).getRow(2).getCell(3).getStringCellValue());assertEquals(CellType.NUMERIC,row.getCell(4).getCellType());assertEquals(CellType.NUMERIC,row.getCell(5).getCellType()); assertEquals("'\n=SUM(1,2)",workbook.getSheetAt(0).getRow(101).getCell(0).getStringCellValue()); assertTrue(workbook.getNumCellStyles()<10,"日期样式必须复用,不能按单元格创建"); } diff --git a/file/src/test/java/com/baiyue/file/service/LooseboardTransportPhotoFileTest.java b/file/src/test/java/com/baiyue/file/service/LooseboardTransportPhotoFileTest.java new file mode 100644 index 000000000..db7497d6d --- /dev/null +++ b/file/src/test/java/com/baiyue/file/service/LooseboardTransportPhotoFileTest.java @@ -0,0 +1,19 @@ +package com.baiyue.file.service; + +import org.junit.jupiter.api.Test; +import static org.assertj.core.api.Assertions.*; + +class LooseboardTransportPhotoFileTest { + /** 两节点文件独立归属原排车,客户文件及PDF不能充当现场照片。 */ + @Test void photosHaveDistinctPlatformOwnershipAndImagePolicy(){ + var pickup=BusinessFileCategory.require("OVERSEAS_DISPATCH_LOOSEBOARD_PICKUP_EVIDENCE"); + var arrival=BusinessFileCategory.require("OVERSEAS_DISPATCH_LOOSEBOARD_ARRIVAL_EVIDENCE"); + assertThat(pickup).isNotEqualTo(arrival); + for(var category:new BusinessFileCategory[]{pickup,arrival}){ + category.validateOwner(0);assertThatThrownBy(()->category.validateOwner(100)).hasMessageContaining("平台"); + assertThat(category.formatPolicy()).isEqualTo(BusinessFileCategory.FormatPolicy.IMAGE); + assertThat(category.storageBusinessId("100")).isEqualTo("100"); + assertThatThrownBy(()->category.storageBusinessId("../100")).isInstanceOf(RuntimeException.class); + } + } +} diff --git a/file/src/test/java/com/baiyue/file/service/OwnedBusinessPhotoFileTest.java b/file/src/test/java/com/baiyue/file/service/OwnedBusinessPhotoFileTest.java index d608a51f3..131d9a58d 100644 --- a/file/src/test/java/com/baiyue/file/service/OwnedBusinessPhotoFileTest.java +++ b/file/src/test/java/com/baiyue/file/service/OwnedBusinessPhotoFileTest.java @@ -45,8 +45,9 @@ class OwnedBusinessPhotoFileTest { verifyNoInteractions(files, storage); } + /** 装车驾驶证允许 PDF;油耗仍限图片,两类都拒绝空文件、伪造内容和超限文件。 */ @ParameterizedTest @ValueSource(strings = {LOADING, FUEL}) - void storesRealImagesButRejectsPdfSpoofedEmptyAndOversizedFiles(String type) throws Exception { + void enforcesLoadingAndFuelFormatPoliciesAndRejectsInvalidFiles(String type) throws Exception { var storage = new BusinessFileStorage(); ReflectionTestUtils.setField(storage, "configuredRoot", root); storage.initializeRoot(); @@ -60,8 +61,14 @@ class OwnedBusinessPhotoFileTest { for (var upload : new MockMultipartFile[]{ new MockMultipartFile("file", "empty.png", "image/png", new byte[0]), new MockMultipartFile("file", "spoof.png", "image/png", "not-image".getBytes()), - new MockMultipartFile("file", "receipt.pdf", "application/pdf", "%PDF-1.7\nreceipt".getBytes())}) + new MockMultipartFile("file", "spoof.pdf", "application/pdf", "not-pdf".getBytes())}) assertThatThrownBy(() -> storage.store(0, "71", upload, category)).isInstanceOf(BizException.class); + var pdf = new MockMultipartFile("file", "license.pdf", "application/pdf", "%PDF-1.7\nTEST license".getBytes()); + if (LOADING.equals(type)) { + var saved = storage.store(0, "71", pdf, category); + assertThat(saved.contentType()).isEqualTo("application/pdf"); + assertThat(storage.read(saved.objectKey())).isEqualTo(pdf.getBytes()); + } else assertThatThrownBy(() -> storage.store(0, "71", pdf, category)).isInstanceOf(BizException.class); MultipartFile oversized = mock(MultipartFile.class); when(oversized.isEmpty()).thenReturn(false); when(oversized.getSize()).thenReturn(10L * 1024 * 1024 + 1); diff --git a/finance/AGENTS.md b/finance/AGENTS.md index 5013adedc..4cde3e21b 100644 --- a/finance/AGENTS.md +++ b/finance/AGENTS.md @@ -23,7 +23,7 @@ | 客户只读账单、收款与应收 API | `controller/ClientFinanceController.java`,统一前缀 `/api/client/finance/v1` | | 订单与客户账户、费用来源审核与结算纳入 | `controller/FinancePhase1OperationController.java`、`service/FinancePhase1OperationService.java` | | 客户财务账户汇总 | `service/FinanceCustomerAccountService.java`、`client/IamCustomerClient.java`;客户主档事实仍归 IAM | -| 客户资金账户、授信与充值 | `controller/ClientFinanceWalletController.java`、`controller/PlatformFinanceWalletController.java`、`controller/InternalFinanceWalletController.java`、`service/FinanceWalletService.java`、`dto/FinanceWalletDtos.java`;支持静态收款码、截图申请、平台审核、手工充扣、授信配置及供业务模块使用的冻结/结算/释放,第三方支付回调尚未接入 | +| 客户资金账户、授信与充值 | `controller/ClientFinanceWalletController.java`、`controller/PlatformFinanceWalletController.java`、`controller/InternalFinanceWalletController.java`、`service/FinanceWalletService.java`、`dto/FinanceWalletDtos.java`;支持静态收款码、截图申请、平台审核、手工充扣、授信配置及供业务模块使用的冻结/结算/释放;原消费关联退款契约见 `src/main/resources/openapi/finance-wallet-refund-v1.yaml`,第三方支付回调尚未接入 | | 客户对账单 | `controller/ReconciliationStatementController.java`、`service/ReconciliationStatementService.java` | | 应收款月度统计 | `controller/ReceivableStatisticsController.java`、`service/ReceivableStatisticsService.java`、`dto/ReceivableStatisticsDtos.java`;按结算单生成月份和客户汇总 CNY 正式应收结算单 | | 应收/应付结算单 | `controller/FinanceSettlementController.java`、`service/FinanceSettlementService.java` | diff --git a/finance/src/main/java/com/baiyue/finance/controller/PlatformFinanceWalletController.java b/finance/src/main/java/com/baiyue/finance/controller/PlatformFinanceWalletController.java index c026b5321..2264a4b74 100644 --- a/finance/src/main/java/com/baiyue/finance/controller/PlatformFinanceWalletController.java +++ b/finance/src/main/java/com/baiyue/finance/controller/PlatformFinanceWalletController.java @@ -7,11 +7,16 @@ import com.baiyue.finance.dto.FinanceWalletDtos.CreditLimitRequest; import com.baiyue.finance.dto.FinanceWalletDtos.RechargeReviewRequest; import com.baiyue.finance.dto.FinancePhase1Dtos.PageEnvelope; import com.baiyue.finance.dto.FinanceWalletDtos.RechargeRequest; +import com.baiyue.finance.dto.FinanceWalletDtos.WalletRefundRequest; import com.baiyue.finance.service.FinanceWalletService; +import java.nio.charset.StandardCharsets; +import java.util.UUID; +import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.*; +import org.springframework.web.server.ResponseStatusException; @RestController @RequestMapping("/api/platform/finance/v1") @@ -62,4 +67,25 @@ public class PlatformFinanceWalletController { @GetMapping("/recharge-requests/{requestId}/voucher") @PreAuthorize("hasAuthority('platform:finance:customer-account:list')") public ResponseEntity voucher(@PathVariable long requestId) { return service.voucher(requestId); } + + /** 退款必须由平台财务针对原消费发起,复用资金服务的账户锁及累计可退上限。 */ + @PostMapping("/accounts/{customerId}/ledger/{ledgerId}/refunds") + @PreAuthorize("hasAuthority('platform:finance:customer-account:adjust')") + public ApiResponse refund(@AuthenticationPrincipal AuthenticatedUser user, + @PathVariable long customerId, @PathVariable long ledgerId, + @RequestHeader("Idempotency-Key") String idempotencyKey, + @RequestBody WalletRefundRequest request) { + if (user == null || user.userId() == null || !"PLATFORM".equals(user.appCode()) || !Long.valueOf(0L).equals(user.tenantId())) { + throw new ResponseStatusException(HttpStatus.FORBIDDEN, "仅平台财务可操作退款"); + } + if (request == null || idempotencyKey == null || idempotencyKey.isBlank() + || idempotencyKey.trim().length() > 128) throw new IllegalArgumentException("退款请求及幂等键必填且幂等键不超过128字符"); + if (request.reason() == null || request.reason().isBlank() || request.reason().length() > 500) + throw new IllegalArgumentException("退款原因必填且不超过500字符"); + // 技术退款引用由客户和幂等键确定;资金流水号仍由正式 AC 编号生成器分配。 + String refundNo = "MANUAL_REFUND:" + UUID.nameUUIDFromBytes( + (customerId + ":" + idempotencyKey.trim()).getBytes(StandardCharsets.UTF_8)); + return FinanceResponses.success(service.refundConsumption(customerId, ledgerId, refundNo, + idempotencyKey.trim(), request.amount(), user.userId(), user.displayName(), request.reason().trim())); + } } diff --git a/finance/src/main/java/com/baiyue/finance/dto/BusinessFeeSourceVersion.java b/finance/src/main/java/com/baiyue/finance/dto/BusinessFeeSourceVersion.java new file mode 100644 index 000000000..c4d34898b --- /dev/null +++ b/finance/src/main/java/com/baiyue/finance/dto/BusinessFeeSourceVersion.java @@ -0,0 +1,4 @@ +package com.baiyue.finance.dto; + +/** 接收事务锁定的来源版本及其不可变客户归属。 */ +public record BusinessFeeSourceVersion(int sourceVersion, long customerId) { } diff --git a/finance/src/main/java/com/baiyue/finance/dto/FinanceWalletDtos.java b/finance/src/main/java/com/baiyue/finance/dto/FinanceWalletDtos.java index db7893993..08ec295fe 100644 --- a/finance/src/main/java/com/baiyue/finance/dto/FinanceWalletDtos.java +++ b/finance/src/main/java/com/baiyue/finance/dto/FinanceWalletDtos.java @@ -54,6 +54,9 @@ public final class FinanceWalletDtos { public record WalletHoldActionRequest(String remark) { } + /** 关联原消费退款,原因必填,原流水保持不可变。 */ + public record WalletRefundRequest(BigDecimal amount, String reason) { } + public record WalletHold(long holdId, long customerId, String businessType, String businessNo, BigDecimal amount, BigDecimal actualFrozenAmount, BigDecimal creditFrozenAmount, diff --git a/finance/src/main/java/com/baiyue/finance/mapper/BusinessFeeSourceMapper.java b/finance/src/main/java/com/baiyue/finance/mapper/BusinessFeeSourceMapper.java new file mode 100644 index 000000000..60407b524 --- /dev/null +++ b/finance/src/main/java/com/baiyue/finance/mapper/BusinessFeeSourceMapper.java @@ -0,0 +1,27 @@ +package com.baiyue.finance.mapper; + +import com.baiyue.finance.dto.BusinessFeeSourceVersion; +import org.apache.ibatis.annotations.Arg; +import org.apache.ibatis.annotations.ConstructorArgs; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Options; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; + +import java.util.List; + +/** 费用接收时锁定稳定来源,防止新版本改写其他客户的费用。 */ +@Mapper +public interface BusinessFeeSourceMapper { + /** 使用完整来源键锁定全部版本;锁定读取必须绕过缓存并参与接收事务。 */ + @Select("SELECT source_fee_version,customer_id FROM fin_fee_source " + + "WHERE source_system=#{system} AND source_type=#{type} AND source_fee_id=#{feeId} " + + "ORDER BY source_fee_version FOR UPDATE") + @ConstructorArgs({ + @Arg(column = "source_fee_version", javaType = int.class), + @Arg(column = "customer_id", javaType = long.class) + }) + @Options(useCache = false, flushCache = Options.FlushCachePolicy.TRUE) + List lockVersions(@Param("system") String system, + @Param("type") String type, @Param("feeId") long feeId); +} diff --git a/finance/src/main/java/com/baiyue/finance/mapper/FinanceFeeSourceReadMapper.java b/finance/src/main/java/com/baiyue/finance/mapper/FinanceFeeSourceReadMapper.java index 4fa153600..bc415481b 100644 --- a/finance/src/main/java/com/baiyue/finance/mapper/FinanceFeeSourceReadMapper.java +++ b/finance/src/main/java/com/baiyue/finance/mapper/FinanceFeeSourceReadMapper.java @@ -3,14 +3,23 @@ package com.baiyue.finance.mapper; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Select; +import org.apache.ibatis.annotations.Options; import java.util.List; import java.util.Map; /** 费用池按稳定来源读取当前版本,历史快照保留在同一费用的操作记录中。 */ @Mapper public interface FinanceFeeSourceReadMapper { - String GENERATED = "(EXISTS(SELECT 1 FROM fin_bill_line bl WHERE bl.source_id=f.id AND bl.source_version=f.source_fee_version))"; - String STATUS = "CASE WHEN f.status='AVAILABLE' AND " + GENERATED + " THEN 'BILLED' ELSE f.status END"; + String GENERATED = "(EXISTS(SELECT 1 FROM fin_bill_line bl JOIN fin_bill b ON b.id=bl.bill_id AND b.tenant_id=bl.tenant_id" + + " WHERE bl.tenant_id=f.customer_id AND bl.source_id=f.id AND bl.source_version=f.source_fee_version AND b.status<>'VOIDED'))"; + // 应付进度取同客户的有效结算单;作废单释放来源,直接已付使用来源中的付款事实。 + String PAYABLE_STATUS = "(SELECT CASE s.payment_status WHEN 'PAID' THEN 'PAID' WHEN 'PARTIALLY_PAID' THEN 'PARTIALLY_PAID'" + + " ELSE 'PAYABLE_BILLED' END FROM fin_settlement_line sl JOIN fin_settlement s ON s.id=sl.settlement_id AND s.tenant_id=sl.tenant_id" + + " WHERE sl.tenant_id=f.customer_id AND sl.fee_source_id=f.id AND s.direction='PAYABLE' AND s.status<>'VOIDED' ORDER BY s.id DESC LIMIT 1)"; + String STATUS = "CASE WHEN f.status IN ('VOIDED','SUPERSEDED','SETTLED') THEN f.status" + + " WHEN f.direction='PAYABLE' THEN COALESCE(" + PAYABLE_STATUS + ",CASE WHEN f.settlement_mode='DIRECT_PAID'" + + " AND f.review_status='APPROVED' AND f.paid_at IS NOT NULL THEN 'PAID' ELSE f.status END)" + + " WHEN f.status='AVAILABLE' AND " + GENERATED + " THEN 'BILLED' ELSE f.status END"; String BUSINESS_DATE = "CAST(JSON_UNQUOTE(JSON_EXTRACT(f.source_snapshot,'$.businessDate')) AS DATE)"; String FILTER = " FROM fin_fee_source f WHERE 1=1 " + "" + @@ -41,6 +50,13 @@ public interface FinanceFeeSourceReadMapper { " ORDER BY " + BUSINESS_DATE + " DESC,f.id DESC LIMIT #{limit} OFFSET #{offset}") List> list(Map filters); + /** 详情与列表共用状态投影,仍允许通过精确 ID 查看历史版本。 */ + @Select("") + // 同事务的审核/调整仍含 JDBC 写入,读取必须清空 MyBatis 会话缓存才能返回变更后状态。 + @Options(useCache = false, flushCache = Options.FlushCachePolicy.TRUE) + Map detail(@Param("id") long id); + String LINEAGE = " FROM fin_fee_source anchor JOIN fin_fee_source f ON f.source_system=anchor.source_system" + " AND f.source_type=anchor.source_type AND f.source_fee_id=anchor.source_fee_id" + " AND f.customer_id=anchor.customer_id AND f.direction=anchor.direction" + diff --git a/finance/src/main/java/com/baiyue/finance/mapper/FinanceSettlementStateMapper.java b/finance/src/main/java/com/baiyue/finance/mapper/FinanceSettlementStateMapper.java new file mode 100644 index 000000000..c022c76cc --- /dev/null +++ b/finance/src/main/java/com/baiyue/finance/mapper/FinanceSettlementStateMapper.java @@ -0,0 +1,52 @@ +package com.baiyue.finance.mapper; + +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Options; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; +import org.apache.ibatis.annotations.Update; +import java.util.List; +import java.util.Map; + +/** 结算状态变更与重新占用来源的事务锁;锁定读取不得复用会话缓存。 */ +@Mapper +public interface FinanceSettlementStateMapper { + /** 锁定待作废或重开的结算,保留客户与来源关联。 */ + @Select("SELECT id,tenant_id,total_amount,paid_amount,outstanding_amount,payment_status,status,version,direction,currency_code,supplier_id,reconciliation_statement_id FROM fin_settlement WHERE id=#{settlementId} FOR UPDATE") + @Options(useCache = false, flushCache = Options.FlushCachePolicy.TRUE) + List> lockSettlement(@Param("settlementId") long settlementId); + + /** 直接锁定来源表,不能依赖外连接右侧记录的锁语义。 */ + @Select("SELECT f.id FROM fin_fee_source f WHERE EXISTS (SELECT 1 FROM fin_settlement_line sl WHERE sl.tenant_id=#{customerId} AND sl.settlement_id=#{settlementId} AND sl.fee_source_id=f.id) ORDER BY f.id FOR UPDATE") + @Options(useCache = false, flushCache = Options.FlushCachePolicy.TRUE) + List lockSourceRows(@Param("customerId") long customerId, @Param("settlementId") long settlementId); + + /** 按来源顺序锁定原单全部明细,缺失或跨客户来源仍返回明细供服务拒绝。 */ + @Select("SELECT sl.fee_source_id,sl.amount line_amount,sl.currency_code line_currency,f.id,f.customer_id,f.direction,f.currency_code,f.amount,f.review_status,f.status,f.settlement_mode,f.source_snapshot FROM fin_settlement_line sl LEFT JOIN fin_fee_source f ON f.id=sl.fee_source_id WHERE sl.tenant_id=#{customerId} AND sl.settlement_id=#{settlementId} ORDER BY sl.fee_source_id FOR UPDATE") + @Options(useCache = false, flushCache = Options.FlushCachePolicy.TRUE) + List> lockSources(@Param("customerId") long customerId, @Param("settlementId") long settlementId); + + /** 使用当前锁定读检查其他有效结算,避免等待来源锁后读取旧快照。 */ + @Select("SELECT s.id FROM fin_settlement_line sl JOIN fin_settlement s ON s.id=sl.settlement_id AND s.tenant_id=sl.tenant_id WHERE sl.tenant_id=#{customerId} AND sl.fee_source_id=#{sourceId} AND s.id<>#{settlementId} AND s.status<>'VOIDED' FOR UPDATE") + @Options(useCache = false, flushCache = Options.FlushCachePolicy.TRUE) + List> lockOtherSourceSettlements(@Param("customerId") long customerId, @Param("sourceId") long sourceId, @Param("settlementId") long settlementId); + + /** 与应收新建共同锁定原对账单,防止检查后被调整或再次生成应收。 */ + @Select("SELECT id,tenant_id,status,currency_code,total_amount,received_amount FROM fin_bill WHERE tenant_id=#{customerId} AND id=#{billId} FOR UPDATE") + @Options(useCache = false, flushCache = Options.FlushCachePolicy.TRUE) + List> lockBill(@Param("customerId") long customerId, @Param("billId") long billId); + + /** 读取原账单当前明细,重开不能沿用调账前的行金额。 */ + @Select("SELECT source_id fee_source_id,amount line_amount,currency_code line_currency FROM fin_bill_line WHERE tenant_id=#{customerId} AND bill_id=#{billId} ORDER BY source_id FOR UPDATE") + @Options(useCache = false, flushCache = Options.FlushCachePolicy.TRUE) + List> lockBillLines(@Param("customerId") long customerId, @Param("billId") long billId); + + /** 锁定同账单其他有效应收,作废旧单不可绕过唯一占用。 */ + @Select("SELECT id FROM fin_settlement WHERE tenant_id=#{customerId} AND direction='RECEIVABLE' AND reconciliation_statement_id=#{billId} AND id<>#{settlementId} AND status<>'VOIDED' FOR UPDATE") + @Options(useCache = false, flushCache = Options.FlushCachePolicy.TRUE) + List> lockOtherBillSettlements(@Param("customerId") long customerId, @Param("billId") long billId, @Param("settlementId") long settlementId); + + /** 客户范围内按原版本推进状态。 */ + @Update("UPDATE fin_settlement SET status=#{status},updated_by=#{operatorId},version=version+1 WHERE id=#{settlementId} AND tenant_id=#{customerId} AND version=#{version}") + int changeStatus(@Param("status") String status, @Param("operatorId") long operatorId, @Param("settlementId") long settlementId, @Param("customerId") long customerId, @Param("version") int version); +} diff --git a/finance/src/main/java/com/baiyue/finance/repository/BusinessFeeSourceRepository.java b/finance/src/main/java/com/baiyue/finance/repository/BusinessFeeSourceRepository.java new file mode 100644 index 000000000..4c08e3b38 --- /dev/null +++ b/finance/src/main/java/com/baiyue/finance/repository/BusinessFeeSourceRepository.java @@ -0,0 +1,20 @@ +package com.baiyue.finance.repository; + +import com.baiyue.finance.dto.BusinessFeeSourceVersion; +import com.baiyue.finance.mapper.BusinessFeeSourceMapper; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; + +import java.util.List; + +/** 为接收服务提供同一事务中的费用来源版本与归属事实。 */ +@Repository +@RequiredArgsConstructor +public class BusinessFeeSourceRepository { + private final BusinessFeeSourceMapper mapper; + + /** 原来源的客户不能被新版本替换,查询范围不得按传入客户提前过滤。 */ + public List lockVersions(String system, String type, long feeId) { + return mapper.lockVersions(system, type, feeId); + } +} diff --git a/finance/src/main/java/com/baiyue/finance/repository/FinanceFeeSourceReadRepository.java b/finance/src/main/java/com/baiyue/finance/repository/FinanceFeeSourceReadRepository.java index c5862944e..5dde48e22 100644 --- a/finance/src/main/java/com/baiyue/finance/repository/FinanceFeeSourceReadRepository.java +++ b/finance/src/main/java/com/baiyue/finance/repository/FinanceFeeSourceReadRepository.java @@ -1,6 +1,7 @@ package com.baiyue.finance.repository; import com.baiyue.finance.dto.FinancePhase1Dtos.FeeSourceSummary; +import com.baiyue.finance.dto.FinancePhase1Dtos.FeeSourceDetail; import com.baiyue.finance.dto.FinancePhase1Dtos.OperationLog; import com.baiyue.finance.mapper.FinanceFeeSourceReadMapper; import com.baiyue.finance.service.FinanceEventException; @@ -51,6 +52,21 @@ public class FinanceFeeSourceReadRepository { return mapper.list(filters).stream().map(this::summary).toList(); } + /** 精确版本详情使用与列表相同的金额和状态,并保留审核信息及操作记录。 */ + public FeeSourceDetail detail(long id) { + Map raw = mapper.detail(id); + if (raw == null || raw.isEmpty()) throw new FinanceEventException("FIN-FEE-404", "费用来源不存在"); + Map row = row(raw); + FeeSourceSummary s = summary(row); + return new FeeSourceDetail(s.feeSourceId(), s.sourceSystem(), s.sourceType(), s.sourceBusinessId(), s.sourceFeeId(), + s.sourceVersion(), s.direction(), s.customerId(), s.customerName(), s.supplierId(), s.supplierName(), s.orderId(), + s.inboundId(), s.inboundCode(), s.containerId(), s.containerNo(), s.businessDate(), s.feeItemName(), + s.quantity(), s.quantityUnit(), s.unitPrice(), s.periodStart(), s.periodEnd(), s.amount(), s.currency(), + s.originalCurrency(), s.originalAmount(), s.exchangeRate(), s.exchangeRateMonth(), s.reviewStatus(), + s.sourceStatus(), s.statementGenerated(), text(row.get("review_reason")), text(row.get("source_hash")), + ((Number) row.get("operation_version")).intValue(), logs(s.customerId(), id)); + } + /** 由不可变版本追加同步历史,并串联同一费用所有既有审计记录。 */ public List logs(long customerId, long id) { List result = new ArrayList<>(); diff --git a/finance/src/main/java/com/baiyue/finance/repository/FinanceSettlementStateRepository.java b/finance/src/main/java/com/baiyue/finance/repository/FinanceSettlementStateRepository.java new file mode 100644 index 000000000..1a5b909d2 --- /dev/null +++ b/finance/src/main/java/com/baiyue/finance/repository/FinanceSettlementStateRepository.java @@ -0,0 +1,36 @@ +package com.baiyue.finance.repository; + +import com.baiyue.finance.mapper.FinanceSettlementStateMapper; +import org.springframework.stereotype.Repository; +import org.springframework.util.LinkedCaseInsensitiveMap; +import java.util.List; +import java.util.Map; + +/** 封装结算状态及原始费用、账单的事务访问。 */ +@Repository +public class FinanceSettlementStateRepository { + private final FinanceSettlementStateMapper mapper; + /** 注入结算状态持久化入口。 */ + public FinanceSettlementStateRepository(FinanceSettlementStateMapper mapper) { this.mapper = mapper; } + /** 统一数据库返回的列名大小写。 */ + private List> rows(List> rows) { + return rows.stream().map(row -> { Map value = new LinkedCaseInsensitiveMap<>(); value.putAll(row); return value; }).toList(); + } + /** 锁定目标结算。 */ + public List> lockSettlement(long id) { return rows(mapper.lockSettlement(id)); } + /** 锁定结算所有费用来源。 */ + public List> lockSources(long customerId, long id) { + mapper.lockSourceRows(customerId, id); + return rows(mapper.lockSources(customerId, id)); + } + /** 判断费用是否被其他有效结算占用。 */ + public boolean sourceOccupied(long customerId, long sourceId, long id) { return !mapper.lockOtherSourceSettlements(customerId, sourceId, id).isEmpty(); } + /** 锁定原始对账单。 */ + public List> lockBill(long customerId, long billId) { return rows(mapper.lockBill(customerId, billId)); } + /** 锁定原始对账单的当前明细。 */ + public List> lockBillLines(long customerId, long billId) { return rows(mapper.lockBillLines(customerId, billId)); } + /** 判断原对账单是否已生成其他有效应收。 */ + public boolean billOccupied(long customerId, long billId, long id) { return !mapper.lockOtherBillSettlements(customerId, billId, id).isEmpty(); } + /** 按客户及版本变更结算状态。 */ + public int changeStatus(String status, long operatorId, long id, long customerId, int version) { return mapper.changeStatus(status, operatorId, id, customerId, version); } +} diff --git a/finance/src/main/java/com/baiyue/finance/service/BusinessFeeSourceEventService.java b/finance/src/main/java/com/baiyue/finance/service/BusinessFeeSourceEventService.java index 1312e6dc0..364e8c852 100644 --- a/finance/src/main/java/com/baiyue/finance/service/BusinessFeeSourceEventService.java +++ b/finance/src/main/java/com/baiyue/finance/service/BusinessFeeSourceEventService.java @@ -6,6 +6,7 @@ import com.baiyue.common.event.DeliveryAck; import com.baiyue.common.event.EventPayloadCanonicalizer; import com.baiyue.common.event.EventPayloadHash; import com.baiyue.common.event.EventTypes; +import com.baiyue.finance.repository.BusinessFeeSourceRepository; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.RequiredArgsConstructor; @@ -13,11 +14,13 @@ import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.math.BigDecimal; +import java.math.RoundingMode; import java.util.List; import java.util.Map; import java.util.Set; -/** Consumes complete overseas business fee snapshots without a cross-region synchronous callback. */ +/** 接收海外费用不可变快照,在同一事务内完成来源版本与接收记录。 */ @Service @RequiredArgsConstructor public class BusinessFeeSourceEventService { @@ -26,7 +29,9 @@ public class BusinessFeeSourceEventService { private final JdbcTemplate jdbc; private final ObjectMapper objectMapper; private final FinanceSupplierValidator supplierValidator; + private final BusinessFeeSourceRepository sources; + /** 先核对不可变事件,再恢复已提交结果;仅新事件检查外部主档。 */ @Transactional public DeliveryAck consume(EventEnvelope envelope) { try { @@ -56,6 +61,8 @@ public class BusinessFeeSourceEventService { return DeliveryAck.of(envelope, DeliveryAck.Outcome.DUPLICATE); throw error("FIN-EVT-003", "相同事件正在处理或上次处理失败"); } + // 回执丢失后的原事件重放不能依赖供应商当前是否可用,否则已入账事件无法确认。 + validateSupplier(source); jdbc.update("INSERT INTO fin_inbox_event(event_id,source_system,event_type,payload,payload_hash,status) VALUES (?,?,?,?,?,'RECEIVED')", envelope.eventId(), sourceSystem, envelope.eventType(), eventJson, envelope.payloadHash()); long result = apply(sourceSystem, source); @@ -64,6 +71,7 @@ public class BusinessFeeSourceEventService { return DeliveryAck.of(envelope, DeliveryAck.Outcome.APPLIED); } + /** 锁定同一来源的历史版本,拒绝跨客户替代,并保留已出账来源冻结规则。 */ private long apply(String system, BusinessFeeSourceChangedV1 source) { long feeId = positive(source.sourceFeeId(), "sourceFeeId"); List> same = jdbc.queryForList( @@ -83,10 +91,12 @@ public class BusinessFeeSourceEventService { } if ("VOIDED".equals(source.action())) throw error("FIN-EVT-005", "作废事件对应的费用来源版本不存在"); - List> versions = jdbc.queryForList( - "SELECT source_fee_version FROM fin_fee_source WHERE source_system=? AND source_type=? AND source_fee_id=? FOR UPDATE", - system, source.sourceType(), feeId); - if (versions.stream().anyMatch(row -> ((Number) row.get("source_fee_version")).intValue() > source.sourceVersion())) + var versions = sources.lockVersions(system, source.sourceType(), feeId); + long customerId = positive(source.customerId(), "customerId"); + // 来源身份跨版本保持客户归属;检查失败时 Inbox 插入也随当前事务回滚。 + if (versions.stream().anyMatch(row -> row.customerId() != customerId)) + throw error("FIN-EVT-018", "费用来源客户归属不允许变更"); + if (versions.stream().anyMatch(row -> row.sourceVersion() > source.sourceVersion())) throw error("FIN-EVT-008", "费用来源版本已过期"); requireSourceNotFrozen(system, source.sourceType(), feeId); jdbc.update("UPDATE fin_fee_source SET status='SUPERSEDED' WHERE source_system=? AND source_type=? AND source_fee_id=? AND status='AVAILABLE'", @@ -110,6 +120,7 @@ public class BusinessFeeSourceEventService { return id; } + /** 校验载荷自身的身份、金额和摘要,不依赖会随时间变化的外部数据。 */ private void validate(EventEnvelope envelope, BusinessFeeSourceChangedV1 value) { if (value == null || !ACTIONS.contains(value.action()) || blank(value.sourceSystem()) || "FIRSTLEG".equalsIgnoreCase(value.sourceSystem()) || blank(value.sourceType()) @@ -130,12 +141,17 @@ public class BusinessFeeSourceEventService { } positive(value.sourceFeeId(), "sourceFeeId"); positive(value.customerId(), "customerId"); + validateAmounts(value); if (value.walletSettlement() != null) { if (!"RECEIVABLE".equals(value.direction())) throw error("FIN-EVT-012", "钱包消费只能关联应收费用"); positive(value.walletSettlement().holdId(), "walletSettlement.holdId"); positive(value.walletSettlement().ledgerId(), "walletSettlement.ledgerId"); if (blank(value.walletSettlement().commandEventId())) throw error("FIN-EVT-012", "缺少原资金命令引用"); - } else if ("OVERSEAS_PROCUREMENT_FEE".equals(value.sourceType()) && "RECEIVABLE".equals(value.direction())) { + walletIdentity(value); + } else if ("OVERSEAS_PROCUREMENT_FEE".equals(value.sourceType()) && "RECEIVABLE".equals(value.direction()) + // 新采购主单复用人工收费,只有显式账单结算可入池;旧采购和余额费用仍必须引用实扣。 + && !("LASTLEG".equals(value.sourceSystem()) && "OVERSEAS_PROCUREMENT".equals(value.businessType()) + && "BILL".equals(value.settlementMode()))) { throw error("FIN-EVT-012", "采购应收必须引用成功实扣资金,不能进入待收费池"); } if ("OVERSEAS_CONTAINER_FEE".equals(value.sourceType())) { @@ -148,13 +164,11 @@ public class BusinessFeeSourceEventService { throw error("FIN-EVT-015", "费用来源尚未发布或已失效"); } if ("PAYABLE".equals(value.direction())) { - long supplierId = positive(value.supplierId(), "supplierId"); - var supplier = supplierValidator.requireActive(supplierId, trim(value.supplierCategory())); - if (blank(value.supplierName()) || !value.supplierName().equals(supplier.name())) - throw error("FIN-EVT-012", "应付来源供应商快照不一致"); + positive(value.supplierId(), "supplierId"); + if (blank(value.supplierName())) throw error("FIN-EVT-012", "应付来源供应商快照不完整"); validateSettlement(value); } else if (!blank(value.supplierId()) || !blank(value.supplierName()) || !blank(value.supplierCategory()) - || !blank(value.settlementMode()) || value.paidAt() != null + || value.settlementMode() != null && !Set.of("BILL", "WALLET").contains(value.settlementMode()) || value.paidAt() != null || !blank(value.paymentMethod()) || !blank(value.paymentReferenceNo()) || (value.paymentVoucherFileIds() != null && !value.paymentVoucherFileIds().isEmpty())) { throw error("FIN-EVT-012", "应收来源不得携带供应商或付款事实"); @@ -173,6 +187,31 @@ public class BusinessFeeSourceEventService { if (!expected.equals(value.sourceHash())) throw error("FIN-EVT-004", "费用来源快照哈希不一致"); } + /** 使用来源冻结汇率复核人民币金额,与海外生产端的分位四舍五入口径一致。 */ + private void validateAmounts(BusinessFeeSourceChangedV1 value) { + try { + // 不在接收端静默修正金额或截断汇率;无效精度须由生产端重新审核。 + value.originalAmount().setScale(2, RoundingMode.UNNECESSARY); + value.totalAmount().setScale(2, RoundingMode.UNNECESSARY); + value.exchangeRate().setScale(6, RoundingMode.UNNECESSARY); + } catch (ArithmeticException invalidPrecision) { + throw error("FIN-EVT-019", "费用金额或汇率精度无效"); + } + if ("CNY".equals(value.originalCurrency()) && value.exchangeRate().compareTo(BigDecimal.ONE) != 0) + throw error("FIN-EVT-019", "人民币原币费用汇率必须为1"); + BigDecimal expected = value.originalAmount().multiply(value.exchangeRate()).setScale(2, RoundingMode.HALF_UP); + if (expected.compareTo(value.totalAmount()) != 0) + throw error("FIN-EVT-019", "费用人民币金额与原币金额、汇率不一致"); + } + + /** 首次接收应付仍须校验启用供应商与名称快照,依赖不可用时失败关闭。 */ + private void validateSupplier(BusinessFeeSourceChangedV1 value) { + if (!"PAYABLE".equals(value.direction())) return; + var supplier = supplierValidator.requireActive(positive(value.supplierId(), "supplierId"), trim(value.supplierCategory())); + if (!value.supplierName().equals(supplier.name())) + throw error("FIN-EVT-012", "应付来源供应商快照不一致"); + } + private void validateSettlement(BusinessFeeSourceChangedV1 value) { boolean files = value.paymentVoucherFileIds() != null && !value.paymentVoucherFileIds().isEmpty(); if ("ON_ACCOUNT".equals(value.settlementMode())) { @@ -202,9 +241,10 @@ public class BusinessFeeSourceEventService { long customerId = positive(source.customerId(), "customerId"); long holdId = positive(reference.holdId(), "holdId"); long ledgerId = positive(reference.ledgerId(), "ledgerId"); + String[] identity = walletIdentity(source); List> rows = jdbc.queryForList( "SELECT l.amount,c.result_json FROM fin_wallet_hold h JOIN fin_wallet_ledger l ON l.id=h.action_ledger_id AND l.customer_id=h.customer_id JOIN fin_wallet_command_inbox c ON c.event_id=? AND c.status='SUCCEEDED' WHERE h.id=? AND l.id=? AND h.customer_id=? AND h.business_type=? AND h.business_no=? AND h.status='SETTLED' AND l.change_type='CONSUMPTION' FOR UPDATE", - reference.commandEventId(), holdId, ledgerId, customerId, source.businessType(), source.businessNo()); + reference.commandEventId(), holdId, ledgerId, customerId, identity[0], identity[1]); if (rows.size() != 1 || !(rows.get(0).get("amount") instanceof java.math.BigDecimal amount) || amount.negate().compareTo(source.totalAmount()) != 0) { throw error("FIN-EVT-017", "费用来源与成功扣款的客户、业务或金额不匹配"); @@ -218,8 +258,8 @@ public class BusinessFeeSourceEventService { || !reference.holdId().equals(result.path("walletHoldId").asText()) || !reference.ledgerId().equals(result.path("walletLedgerId").asText()) || result.path("customerId").asLong() != customerId - || !source.businessType().equals(result.path("businessType").asText()) - || !source.businessNo().equals(result.path("businessNo").asText()) + || !identity[0].equals(result.path("businessType").asText()) + || !identity[1].equals(result.path("businessNo").asText()) || result.path("amount").decimalValue().compareTo(source.totalAmount()) != 0) { throw error("FIN-EVT-017", "费用来源引用的资金结果不匹配"); } @@ -234,6 +274,24 @@ public class BusinessFeeSourceEventService { feeSourceId); } + /** 独立锁款仅接收已约定的人工费用或仓租身份,不能借此挂接任意业务扣款。 */ + private String[] walletIdentity(BusinessFeeSourceChangedV1 source) { + var reference = source.walletSettlement(); + String type = reference.walletBusinessType(), no = reference.walletBusinessNo(); + if (type == null && no == null) return new String[]{source.businessType(), source.businessNo()}; + if (blank(type) || blank(no) || !"LASTLEG".equals(source.sourceSystem())) + throw error("FIN-EVT-017", "资金业务引用不完整"); + if ("OVERSEAS_MANUAL_FEE".equals(type)) { + if (!no.matches(java.util.regex.Pattern.quote(source.sourceFeeId()) + "-[1-9][0-9]*")) + throw error("FIN-EVT-017", "人工费用资金引用与来源不匹配"); + } else if ("STORAGE_RENT".equals(type)) { + if (!"FEE_TYPE_077".equals(source.feeItemCode())) + throw error("FIN-EVT-017", "仓租资金引用与费用项目不匹配"); + positive(no, "walletBusinessNo"); + } else throw error("FIN-EVT-017", "不支持的独立资金业务引用"); + return new String[]{type, no}; + } + private void materializeDirectPaidCost(String system, long feeId, BusinessFeeSourceChangedV1 value) { jdbc.update("INSERT INTO fin_cost(tenant_id,cost_no,cost_type,cost_origin,source_system,source_type,source_fee_id,source_fee_version,source_hash,order_id,inbound_id,container_id,warehouse_id,supplier_id,supplier_name_snapshot,fee_item_code,fee_item_name,currency_code,cost_amount,voucher_file_ids,paid_at,payment_method,payment_reference_no,status,confirmed_at,created_by,created_by_name,updated_by) VALUES(?,?,'SOURCE_DIRECT_PAID','BUSINESS_DIRECT_PAID',?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,'CONFIRMED',CURRENT_TIMESTAMP(3),0,'系统',0)", positive(value.customerId(), "customerId"), "DIRECT-" + system + '-' + value.sourceType() + '-' + feeId + "-V" + value.sourceVersion(), diff --git a/finance/src/main/java/com/baiyue/finance/service/FinanceCustomerNames.java b/finance/src/main/java/com/baiyue/finance/service/FinanceCustomerNames.java new file mode 100644 index 000000000..d802e61f9 --- /dev/null +++ b/finance/src/main/java/com/baiyue/finance/service/FinanceCustomerNames.java @@ -0,0 +1,71 @@ +package com.baiyue.finance.service; + +import com.baiyue.finance.client.IamCustomerClient; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import java.util.HashMap; +import java.util.function.BiFunction; + +/** 单据客户名称取真实主档或有效快照,内部引用不能作为名称展示。 */ +@Service +public class FinanceCustomerNames { + private final IamCustomerClient customers; + private final String token; + + /** 复用已存在的 IAM 客户查询,不跨库读取客户资料。 */ + public FinanceCustomerNames(IamCustomerClient customers, + @Value("${baiyue.security.internal-service-token}") String token) { + this.customers = customers; + this.token = token; + } + + /** 新建单据必须取得真实客户名称,依赖故障或客户不存在时停止建单。 */ + public String required(long customerId) { + String name = lookup(customerId); + if (name == null) throw new FinanceEventException("FIN-CUSTOMER-404", "客户名称未提供,无法生成财务单据"); + return name; + } + + /** 应收结算沿用有效的对账单名称,缺失时必须取得真实主档后才能建单。 */ + public String requiredSnapshot(long customerId, String snapshot) { + return valid(customerId, snapshot) ? snapshot.trim() : required(customerId); + } + + /** 每次列表/详情请求内复用同客户查询结果,保留有效的历史名称快照。 */ + public BiFunction resolver() { + var names = new HashMap(); + return (customerId, snapshot) -> { + if (valid(customerId, snapshot)) return snapshot.trim(); + return names.computeIfAbsent(customerId, id -> { + String name = lookup(id); + return name == null ? "—" : name; + }); + }; + } + + /** 仅接受所请求客户的主档,不能拿其他客户记录填充当前单据。 */ + private String lookup(long customerId) { + try { + var response = customers.page(token, 1, 1, null, null, customerId); + if (response == null || !response.successful() || response.data() == null || response.data().records() == null) + throw new FinanceEventException("FIN_503", "客户主数据暂不可用"); + var rows = response.data().records(); + if (rows.isEmpty()) return null; + if (rows.size() != 1 || rows.get(0).id() != customerId) + throw new FinanceEventException("FIN_503", "客户主数据返回范围异常"); + String name = rows.get(0).tenantName(); + return valid(customerId, name) ? name.trim() : null; + } catch (FinanceEventException failure) { + throw failure; + } catch (RuntimeException failure) { + throw new FinanceEventException("FIN_503", "客户主数据暂不可用"); + } + } + + /** 空名称与内部编号占位不是有效的业务名称。 */ + private static boolean valid(long customerId, String name) { + return name != null && !name.isBlank() && !name.trim().equals("—") + && !name.trim().equals(Long.toString(customerId)) && !name.trim().matches("客户\\s*(?:ID)?\\s*\\d+"); + } +} diff --git a/finance/src/main/java/com/baiyue/finance/service/FinanceExportLabels.java b/finance/src/main/java/com/baiyue/finance/service/FinanceExportLabels.java new file mode 100644 index 000000000..25fa9f1fd --- /dev/null +++ b/finance/src/main/java/com/baiyue/finance/service/FinanceExportLabels.java @@ -0,0 +1,107 @@ +package com.baiyue.finance.service; + +import java.util.Locale; +import java.util.Map; + +/** 导出业务枚举沿用财务页面中文口径,不改动筛选值、币种及业务编号。 */ +final class FinanceExportLabels { + /** 中文导出映射仅提供静态转换。 */ + private FinanceExportLabels() { } + private static final Map STATUSES = Map.ofEntries( + Map.entry("ACTIVE", "正常"), + Map.entry("DISABLED", "停用"), + Map.entry("INACTIVE", "停用"), + Map.entry("DRAFT", "草稿"), + Map.entry("PENDING", "待审核"), + Map.entry("PENDING_REVIEW", "待审核"), + Map.entry("APPROVED", "已审核"), + Map.entry("ISSUED", "已出账"), + Map.entry("REJECTED", "已驳回"), + Map.entry("UNPAID", "未支付"), + Map.entry("PARTIALLY_PAID", "部分结清"), + Map.entry("PAID", "已结清"), + Map.entry("VOIDED", "已作废"), + Map.entry("AVAILABLE", "待结算"), + Map.entry("LOCKED", "结算锁定"), + Map.entry("BILLED", "已入账"), + Map.entry("SETTLED", "已结算"), + Map.entry("RECORDED", "已登记"), + Map.entry("CONFIRMED", "已确认"), + Map.entry("PENDING_CONFIRMATION", "待客户确认"), + Map.entry("NOT_REQUIRED", "无需确认")); + private static final Map SOURCES = Map.ofEntries( + Map.entry("INBOUND_FEE", "入库费用"), + Map.entry("ORDER_FEE", "订单费用"), + Map.entry("CONTAINER_FEE", "装柜费用"), + Map.entry("CONTAINER_COST", "整柜成本"), + Map.entry("MANUAL_COST", "人工成本"), + Map.entry("STORAGE_FEE", "仓储费用"), + Map.entry("OVERSEAS_INBOUND_FEE", "海外入仓费用"), + Map.entry("OVERSEAS_PROCUREMENT_FEE", "海外采购费用"), + Map.entry("OVERSEAS_TRUCK_FEE", "本土卡车费用"), + Map.entry("OVERSEAS_LOOSEBOARD_FEE", "本土散板费用"), + Map.entry("OVERSEAS_WAREHOUSE_FEE", "海外仓服务费"), + Map.entry("OVERSEAS_CONTAINER_FEE", "海外柜子费用"), + Map.entry("OVERSEAS_ORDER_FEE", "尾程订单费用"), + Map.entry("OVERSEAS_DELIVERY_FEE", "尾程派送费用"), + Map.entry("OVERSEAS_RELABEL_FEE", "换标费用")); + private static final Map BUSINESSES = Map.ofEntries( + Map.entry("RECHARGE", "充值"), + Map.entry("DEDUCTION", "扣款"), + Map.entry("CREDIT_LIMIT", "授信额度调整"), + Map.entry("FREEZE", "冻结"), + Map.entry("UNFREEZE", "解冻"), + Map.entry("CONSUMPTION", "消费扣款"), + Map.entry("REFUND", "退款"), + Map.entry("RECONCILIATION_CREATED", "生成对账单"), + Map.entry("SETTLEMENT_ISSUED", "结算单生效"), + Map.entry("RECEIPT", "收款"), + Map.entry("PAYMENT", "付款"), + Map.entry("SUPPLIER_DIRECT_PAID", "供应商线下直付"), + Map.entry("SUPPLIER_ON_ACCOUNT_PAYMENT", "供应商账期付款"), + Map.entry("ADJUSTMENT", "账务调整"), + Map.entry("VOID", "作废"), + Map.entry("BILL", "对账单"), + Map.entry("COST", "成本"), + Map.entry("SETTLEMENT", "结算"), + Map.entry("CREATE", "创建"), + Map.entry("APPROVE", "审核通过"), + Map.entry("REJECT", "审核驳回"), + Map.entry("UPDATE", "更新"), + Map.entry("FEE_SOURCE_EDIT", "编辑费用"), + Map.entry("FEE_SOURCE_SUBMIT", "提交审核"), + Map.entry("FEE_SOURCE_REVIEW_APPROVE", "审核通过"), + Map.entry("FEE_SOURCE_REVIEW_REJECT", "审核驳回"), + Map.entry("FEE_SOURCE_ADJUST", "调整费用"), + Map.entry("FEE_SOURCE_VOID", "作废费用"), + Map.entry("FEE_SOURCE_PUBLISHED", "费用同步"), + Map.entry("FEE_SOURCE_VERSION_ADJUST", "费用调整同步"), + Map.entry("RECONCILIATION_CREATE", "生成对账单"), + Map.entry("RECONCILIATION_ADJUST", "调整对账单"), + Map.entry("BILL_CREATE", "创建账单"), + Map.entry("BILL_ISSUE", "出账"), + Map.entry("BILL_ADJUST", "调整账单"), + Map.entry("BILL_VOID", "作废账单"), + Map.entry("SETTLEMENT_AR_CREATE", "生成应收结算单"), + Map.entry("SETTLEMENT_AP_CREATE", "生成应付结算单"), + Map.entry("SETTLEMENT_PAYMENT_CREATE", "登记收付款"), + Map.entry("RECEIPT_CREATE", "登记收款"), + Map.entry("COST_CREATE", "登记成本"), + Map.entry("REOPEN", "重开")); + + /** 转换账户、审核及结算状态。 */ + static String status(String value) { return label(value, STATUSES, "未知状态"); } + /** 转换费用来源类型。 */ + static String source(String value) { return label(value, SOURCES, "未知来源类型"); } + /** 转换账务及资金流水业务类型。 */ + static String business(String value) { return label(value, BUSINESSES, "未知业务类型"); } + /** 转换应收应付方向。 */ + static String direction(String value) { return label(value, Map.of("RECEIVABLE", "应收", "PAYABLE", "应付"), "未知方向"); } + + /** 空值保持空白,已是中文的历史快照保持原文;新增英文枚举不直接泄露到业务报表。 */ + private static String label(String value, Map labels, String fallback) { + if (value == null || value.isBlank()) return value; + String code = value.trim().toUpperCase(Locale.ROOT); + return labels.getOrDefault(code, value.matches(".*[一-鿿].*") ? value : fallback); + } +} diff --git a/finance/src/main/java/com/baiyue/finance/service/FinanceExportProviderService.java b/finance/src/main/java/com/baiyue/finance/service/FinanceExportProviderService.java index ce9af6f84..eb299e566 100644 --- a/finance/src/main/java/com/baiyue/finance/service/FinanceExportProviderService.java +++ b/finance/src/main/java/com/baiyue/finance/service/FinanceExportProviderService.java @@ -7,13 +7,12 @@ import com.baiyue.common.core.export.ExportContracts.ExportBatchResponse; import com.baiyue.common.core.export.ExportContracts.ExportColumn; import com.baiyue.common.core.export.ExportContracts.ExportColumnType; import com.baiyue.finance.dto.FinancePhase1Dtos.AccountSummary; -import com.baiyue.finance.dto.FinancePhase1Dtos.FeeSourceDetail; import com.baiyue.finance.dto.FinancePhase1Dtos.LedgerEntry; import com.baiyue.finance.dto.FinanceSettlementDtos.SettlementDetail; import com.baiyue.finance.dto.FinanceSettlementDtos.SettlementLine; import com.baiyue.finance.dto.FinanceSettlementDtos.SettlementSummary; import com.baiyue.finance.dto.ReconciliationStatementDtos.ReconciliationDetail; -import com.baiyue.finance.dto.FinanceReadModels.FeePoolItem; +import com.baiyue.finance.dto.FinancePhase1Dtos.FeeSourceSummary; import com.baiyue.finance.dto.ReceivableStatisticsDtos.ReceivableStatisticsRow; import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.annotation.PostConstruct; @@ -199,41 +198,16 @@ public class FinanceExportProviderService { .map(value -> receivableStatisticsRow(month, value)).toList(); } + /** 导出与列表使用相同当前版本、状态及筛选,已付款来源不能被可选结算池遗漏。 */ private List> feeRows(Map p) { - Long customerId = longValue(p, "customerId", false); - String direction = text(p, "direction"); - String status = text(p, "status"); - String reviewStatus = text(p, "reviewStatus"); - Boolean generated = bool(p, "statementGenerated"); - LocalDate from = date(p, "businessDateFrom"), to = date(p, "businessDateTo"); - String keyword = lower(text(p, "keyword")); List> rows = new ArrayList<>(); - for (FeePoolItem item : queryService.feePool(customerId, direction)) { - FeeSourceDetail detail = detail(item); - String sourceStatus = detail == null ? item.status() : detail.sourceStatus(); - String actualReview = detail == null ? null : detail.reviewStatus(); - Boolean actualGenerated = detail == null ? null : detail.statementGenerated(); - LocalDate businessDate = detail == null || detail.businessDate() == null - ? (item.createdAt() == null ? null : item.createdAt().toLocalDate()) : detail.businessDate(); - if (status != null && !status.equalsIgnoreCase(sourceStatus)) continue; - if (reviewStatus != null && !reviewStatus.equalsIgnoreCase(actualReview)) continue; - if (generated != null && !generated.equals(actualGenerated)) continue; - if (from != null && (businessDate == null || businessDate.isBefore(from))) continue; - if (to != null && (businessDate == null || businessDate.isAfter(to))) continue; - String searchable = lower(String.join(" ", value(item.poolItemId()), value(item.sourceId()), value(item.feeItemName()), - detail == null ? "" : value(detail.inboundCode()), detail == null ? "" : value(detail.containerNo()))); - if (keyword != null && !searchable.contains(keyword)) continue; - rows.add(feeRow(item, detail, businessDate)); + for (int page = 1; ; page++) { + var result = operationService.listFinanceFeeSources(page, 200, longValue(p, "customerId", false), + text(p, "direction"), text(p, "status"), text(p, "reviewStatus"), bool(p, "statementGenerated"), + date(p, "businessDateFrom"), date(p, "businessDateTo"), text(p, "keyword")); + result.data().forEach(source -> rows.add(feeRow(source))); + if ((long) page * 200 >= result.page().total()) return rows; } - return rows; - } - - private FeeSourceDetail detail(FeePoolItem item) { - if ("MANUAL_COST".equals(item.sourceType())) return null; - String[] parts = item.poolItemId().split(":", -1); - if (parts.length != 2) return null; - try { return operationService.getFinanceFeeSource(Long.parseLong(parts[1])); } - catch (RuntimeException ignored) { return null; } } private List> statementRows(Map p) { @@ -365,42 +339,47 @@ public class FinanceExportProviderService { } catch (GeneralSecurityException exception) { throw new IllegalStateException("HmacSHA256 is unavailable", exception); } } + /** 导出账户摘要,状态使用中文展示。 */ private Map accountRow(AccountSummary value) { return row("customerCode", value.customerCode(), "customerName", value.customerName(), "contactName", value.contactName(), "contactPhone", value.contactPhone(), "outstandingReceivable", value.outstandingReceivable(), "pendingBilling", value.pendingBilling(), "receivedInPeriod", value.receivedInPeriod(), "totalCost", value.totalCost(), "paidCost", value.paidCost(), - "outstandingPayable", value.outstandingPayable(), "status", value.status(), "lastChangedAt", value.lastChangedAt()); + "outstandingPayable", value.outstandingPayable(), "status", FinanceExportLabels.status(value.status()), "lastChangedAt", value.lastChangedAt()); } + /** 导出账户流水,转换业务类型并保留金额和编号。 */ private Map ledgerRow(LedgerEntry value) { - return row("ledgerId", String.valueOf(value.ledgerId()), "occurredAt", value.occurredAt(), "businessType", value.businessType(), + return row("ledgerId", String.valueOf(value.ledgerId()), "occurredAt", value.occurredAt(), "businessType", FinanceExportLabels.business(value.businessType()), "documentNo", value.documentNo(), "receivableChange", value.receivableChange(), "payableChange", value.payableChange(), "outstandingReceivableAfter", value.outstandingReceivableAfter(), "operatorName", value.operatorName(), "remark", value.remark()); } - private Map feeRow(FeePoolItem item, FeeSourceDetail detail, LocalDate businessDate) { - return row("customerName", detail == null ? null : detail.customerName(), "direction", item.direction(), "sourceType", item.sourceType(), - "sourceFeeId", String.valueOf(item.sourceId()), "sourceVersion", item.sourceVersion(), "inboundCode", detail == null ? null : detail.inboundCode(), - "containerNo", detail == null ? null : detail.containerNo(), "businessDate", businessDate, "feeItemName", item.feeItemName(), - "quantity", detail == null ? null : detail.quantity(), "unitPrice", detail == null ? null : detail.unitPrice(), "amount", item.amount(), - "reviewStatus", detail == null ? null : detail.reviewStatus(), - "sourceStatus", detail == null ? item.status() : detail.sourceStatus(), "statementGenerated", detail == null ? null : detail.statementGenerated()); + /** 费用方向、来源类型及审核和结算状态统一展示中文。 */ + private Map feeRow(FeeSourceSummary source) { + return row("customerName", source.customerName(), "direction", FinanceExportLabels.direction(source.direction()), "sourceType", FinanceExportLabels.source(source.sourceType()), + "sourceFeeId", String.valueOf(source.sourceFeeId()), "sourceVersion", source.sourceVersion(), "inboundCode", source.inboundCode(), + "containerNo", source.containerNo(), "businessDate", source.businessDate(), "feeItemName", source.feeItemName(), + "quantity", source.quantity(), "unitPrice", source.unitPrice(), "amount", source.amount(), + "reviewStatus", FinanceExportLabels.status(source.reviewStatus()), "sourceStatus", FinanceExportLabels.status(source.sourceStatus()), "statementGenerated", source.statementGenerated()); } + /** 对账单导出沿用页面状态口径,是否结清仍传递布尔值。 */ private Map statementRow(ReconciliationDetail value) { return row("statementNo", value.statementNo(), "customerName", value.customerName(), "periodStart", value.periodStart(), "periodEnd", value.periodEnd(), "creditDays", value.creditDays(), "currency", value.currency(), "totalReceivable", value.totalReceivable(), - "receivedAmount", value.receivedAmount(), "outstandingAmount", value.outstandingAmount(), "settled", value.settled(), "status", value.status(), + "receivedAmount", value.receivedAmount(), "outstandingAmount", value.outstandingAmount(), "settled", value.settled(), "status", FinanceExportLabels.status(value.status()), "remark", value.remark(), "createdByName", value.createdByName(), "createdAt", value.createdAt(), "updatedByName", value.updatedByName(), "updatedAt", value.updatedAt()); } + /** 结算单列表导出使用中文状态,便于财务人员直接阅读。 */ private Map settlementRow(SettlementSummary value) { return row("settlementNo", value.settlementNo(), "customerName", value.customerName(), "supplierName", value.supplierName(), "currency", value.currency(), "totalAmount", value.totalAmount(), "paidAmount", value.paidAmount(), "outstandingAmount", value.outstandingAmount(), - "businessPeriod", value.businessPeriod(), "generatedDate", value.generatedDate(), "dueDate", value.dueDate(), "status", value.status()); + "businessPeriod", value.businessPeriod(), "generatedDate", value.generatedDate(), "dueDate", value.dueDate(), "status", FinanceExportLabels.status(value.status())); } + /** 明细导出与列表保持相同的中文结算状态。 */ private Map settlementDetailRow(SettlementSummary summary, SettlementLine line) { return row("settlementNo", summary.settlementNo(), "customerName", summary.customerName(), "supplierName", summary.supplierName(), "feeItemName", line == null ? null : line.feeItemName(), "inboundCode", line == null ? null : line.inboundCode(), "containerNo", line == null ? null : line.containerNo(), "businessDate", line == null ? null : line.businessDate(), "lineAmount", line == null ? null : line.amount(), "totalAmount", summary.totalAmount(), "paidAmount", summary.paidAmount(), - "outstandingAmount", summary.outstandingAmount(), "status", summary.status()); + "outstandingAmount", summary.outstandingAmount(), "status", FinanceExportLabels.status(summary.status())); } private Map receivableStatisticsRow(YearMonth month, ReceivableStatisticsRow value) { return row("statisticsMonth", month.toString(), "customerName", value.customerName(), diff --git a/finance/src/main/java/com/baiyue/finance/service/FinancePhase1OperationService.java b/finance/src/main/java/com/baiyue/finance/service/FinancePhase1OperationService.java index 856ae61d7..81bfd5be0 100644 --- a/finance/src/main/java/com/baiyue/finance/service/FinancePhase1OperationService.java +++ b/finance/src/main/java/com/baiyue/finance/service/FinancePhase1OperationService.java @@ -178,7 +178,7 @@ public class FinancePhase1OperationService { String normalizedReview = normalized(reviewStatus, List.of("DRAFT", "PENDING", "APPROVED", "REJECTED"), "审核状态无效"); String normalizedSourceStatus = normalized(sourceStatus, - List.of("AVAILABLE", "LOCKED", "BILLED", "SETTLED", "VOIDED", "SUPERSEDED"), "费用来源状态无效"); + List.of("AVAILABLE", "LOCKED", "BILLED", "SETTLED", "PAYABLE_BILLED", "PARTIALLY_PAID", "PAID", "VOIDED", "SUPERSEDED"), "费用来源状态无效"); Map filters = feeSources.filters(customerId, normalizedDirection, normalizedSourceStatus, normalizedReview, statementGenerated, businessDateFrom, businessDateTo, blankToNull(keyword), page, pageSize); return PageEnvelope.success(feeSources.list(filters), page, pageSize, feeSources.count(filters)); @@ -186,9 +186,7 @@ public class FinancePhase1OperationService { public FeeSourceDetail getFinanceFeeSource(long feeSourceId) { if (feeSourceId <= 0) throw error("FIN-REQ-400", "费用来源ID无效"); - List rows = jdbc.query("SELECT * FROM fin_fee_source WHERE id=?", (rs, n) -> detail(rs), feeSourceId); - if (rows.isEmpty()) throw error("FIN-FEE-404", "费用来源不存在"); - return rows.get(0); + return feeSources.detail(feeSourceId); } @Transactional @@ -225,6 +223,7 @@ public class FinancePhase1OperationService { adjustment ? "POST" : "PUT", path, request); if (replay != null) return getFinanceFeeSource(replay); if (before.statementGenerated()) throw error("FIN-FEE-409", "已生成单据的费用来源不可编辑或调整"); + if (!"AVAILABLE".equals(before.sourceStatus())) throw error("FIN-FEE-409", "费用已被占用、结清或作废,不可编辑或调整"); String requiredStatus = adjustment ? "APPROVED" : before.reviewStatus(); if (!adjustment && !List.of("DRAFT", "REJECTED").contains(requiredStatus)) { throw error("FIN-FEE-409", "只有草稿或已驳回费用可编辑"); @@ -302,6 +301,7 @@ public class FinancePhase1OperationService { "/api/platform/finance/v1/fee-sources/" + feeSourceId + (voidSource ? "/void" : "/submit"), request); if (replay != null) return getFinanceFeeSource(replay); if (before.statementGenerated()) throw error("FIN-FEE-409", "已生成单据的费用来源不可操作"); + if (!"AVAILABLE".equals(before.sourceStatus())) throw error("FIN-FEE-409", "费用已被占用、结清或作废,不可操作"); if (!allowedReviews.contains(before.reviewStatus())) throw error("FIN-FEE-409", "费用来源当前状态不允许该操作"); int changed = voidSource ? jdbc.update("UPDATE fin_fee_source SET status='VOIDED',operation_version=operation_version+1 WHERE id=? AND operation_version=? AND status='AVAILABLE' AND review_status=?", @@ -315,27 +315,6 @@ public class FinancePhase1OperationService { return after; } - /** 读取指定不可变版本,并附带同客户同方向的费用调整历史。 */ - private FeeSourceDetail detail(ResultSet rs) throws SQLException { - JsonNode snapshot = parse(rs.getString("source_snapshot")); - long id = rs.getLong("id"); - boolean generated = Boolean.TRUE.equals(jdbc.queryForObject("SELECT COUNT(*)>0 FROM fin_bill_line WHERE source_id=? AND source_version=?", Boolean.class, id, rs.getInt("source_fee_version"))); - String status = rs.getString("status"); - if (generated && "AVAILABLE".equals(status)) status = "BILLED"; - BigDecimal quantity = decimal(snapshot, "chargeableQuantity"); - BigDecimal amount = rs.getBigDecimal("amount"); - BigDecimal originalAmount = firstDecimal(decimal(snapshot, "originalAmount"), amount); - BigDecimal unitPrice = firstDecimal(decimal(snapshot, "unitPrice"), - quantity != null && quantity.signum() > 0 ? originalAmount.divide(quantity, 6, java.math.RoundingMode.HALF_UP) : null); - return new FeeSourceDetail(id, rs.getString("source_system"), rs.getString("source_type"), firstText(rs.getString("source_no"), text(snapshot, "sourceBusinessId")), rs.getLong("source_fee_id"), rs.getInt("source_fee_version"), rs.getString("direction"), rs.getLong("customer_id"), text(snapshot, "customerName"), jsonLong(snapshot, "supplierId"), text(snapshot, "supplierName"), - nullableLong(rs, "order_id"), nullableLong(rs, "inbound_id"), text(snapshot, "inboundCode"), nullableLong(rs, "container_id"), text(snapshot, "containerNo"), date(snapshot, "businessDate"), rs.getString("fee_item_name"), - quantity, text(snapshot, "quantityUnit"), unitPrice, nullableDate(snapshot, "periodStart"), nullableDate(snapshot, "periodEnd"), - amount, rs.getString("currency_code"), firstText(text(snapshot, "originalCurrency"), rs.getString("currency_code")), originalAmount, - firstDecimal(decimal(snapshot, "exchangeRate"), BigDecimal.ONE), text(snapshot, "exchangeRateMonth"), - defaultString(rs.getString("review_status"), "DRAFT"), status, generated, - rs.getString("review_reason"), rs.getString("source_hash"), rs.getInt("operation_version"), feeSources.logs(rs.getLong("customer_id"), id)); - } - private Long reserve(long customerId, long operatorId, String operation, String key, String method, String path, Object body) { if (!StringUtils.hasText(key) || key.length() > 128) throw error("FIN-IDEM-400", "必须提供有效的Idempotency-Key"); String hash = hash(Map.of("method", method, "path", path, "customerId", customerId, "body", body)); diff --git a/finance/src/main/java/com/baiyue/finance/service/FinanceQueryService.java b/finance/src/main/java/com/baiyue/finance/service/FinanceQueryService.java index 691d46130..435c89856 100644 --- a/finance/src/main/java/com/baiyue/finance/service/FinanceQueryService.java +++ b/finance/src/main/java/com/baiyue/finance/service/FinanceQueryService.java @@ -40,10 +40,12 @@ public class FinanceQueryService { private static final String CNY = "CNY"; private final JdbcTemplate jdbc; private final ObjectMapper objectMapper; + private final FinanceCustomerNames customerNames; - public FinanceQueryService(JdbcTemplate jdbc, ObjectMapper objectMapper) { + public FinanceQueryService(JdbcTemplate jdbc, ObjectMapper objectMapper, FinanceCustomerNames customerNames) { this.jdbc = jdbc; this.objectMapper = objectMapper; + this.customerNames = customerNames; } public CustomerAccount customerAccount(long customerId) { @@ -131,12 +133,13 @@ public class FinanceQueryService { public List statements(Long customerId, boolean clientVisibleOnly) { validateOptionalCustomer(customerId); + var names = customerNames.resolver(); String visible = clientVisibleOnly ? " AND status IN ('ISSUED','PARTIALLY_PAID','PAID')" : ""; String sql = "SELECT id,tenant_id,bill_no,customer_name_snapshot,bill_date,due_date,total_amount,received_amount,outstanding_amount,currency_code,status,version,customer_confirmation_status,customer_confirmed_by,customer_confirmed_by_name,customer_confirmed_at,created_by,created_by_name,created_at,updated_by,updated_by_name,updated_at FROM fin_bill WHERE 1=1"; Object[] args = new Object[0]; if (customerId != null) { sql += " AND tenant_id=?"; args = new Object[]{customerId}; } return jdbc.query(sql + visible + " ORDER BY bill_date DESC,id DESC", - (rs, rowNum) -> new Statement(rs.getLong("id"), rs.getLong("tenant_id"), rs.getString("bill_no"), rs.getString("customer_name_snapshot"), + (rs, rowNum) -> new Statement(rs.getLong("id"), rs.getLong("tenant_id"), rs.getString("bill_no"), names.apply(rs.getLong("tenant_id"), rs.getString("customer_name_snapshot")), rs.getDate("bill_date").toLocalDate(), rs.getDate("due_date").toLocalDate(), rs.getBigDecimal("total_amount"), rs.getBigDecimal("received_amount"), rs.getBigDecimal("outstanding_amount"), rs.getString("currency_code"), rs.getString("status"), rs.getInt("version"), rs.getString("customer_confirmation_status"), nullableLong(rs, "customer_confirmed_by"), @@ -147,6 +150,7 @@ public class FinanceQueryService { public List reconciliationStatements(Long customerId) { validateOptionalCustomer(customerId); + var names = customerNames.resolver(); String sql = "SELECT b.id,b.tenant_id,b.bill_no,b.customer_name_snapshot,b.currency_code,b.status,b.version,b.remark," + "b.total_amount,b.received_amount,b.outstanding_amount,b.issued_at,b.customer_confirmation_status,b.customer_confirmed_by,b.customer_confirmed_by_name,b.customer_confirmed_at,b.created_by,b.created_by_name,b.created_at,b.updated_by,b.updated_by_name,b.updated_at," + "COALESCE(p.credit_days,30) credit_days," + @@ -163,7 +167,7 @@ public class FinanceQueryService { Object[] args = new Object[0]; if (customerId != null) { sql += " AND b.tenant_id=?"; args = new Object[]{customerId}; } return jdbc.query(sql + " ORDER BY b.bill_date DESC,b.id DESC", (rs, rowNum) -> new ReconciliationSummary( - rs.getLong("id"), rs.getString("bill_no"), rs.getLong("tenant_id"), rs.getString("customer_name_snapshot"), + rs.getLong("id"), rs.getString("bill_no"), rs.getLong("tenant_id"), names.apply(rs.getLong("tenant_id"), rs.getString("customer_name_snapshot")), date(rs, "period_start"), date(rs, "period_end"), rs.getInt("credit_days"), rs.getString("currency_code"), rs.getInt("line_count"), rs.getInt("source_count"), rs.getBigDecimal("source_amount"), rs.getBigDecimal("adjustment_amount"), rs.getBigDecimal("total_amount"), rs.getBigDecimal("received_amount"), rs.getBigDecimal("outstanding_amount"), @@ -178,9 +182,10 @@ public class FinanceQueryService { public StatementDetail statement(long customerId, long billId, boolean clientVisibleOnly) { requireCustomer(customerId); + var names = customerNames.resolver(); String visible = clientVisibleOnly ? " AND status IN ('ISSUED','PARTIALLY_PAID','PAID')" : ""; List rows = jdbc.query("SELECT id,tenant_id,bill_no,customer_name_snapshot,bill_date,due_date,total_amount,received_amount,outstanding_amount,currency_code,status,version,customer_confirmation_status,customer_confirmed_by,customer_confirmed_by_name,customer_confirmed_at,created_by,created_by_name,created_at,updated_by,updated_by_name,updated_at FROM fin_bill WHERE tenant_id=? AND id=?" + visible, - (rs, rowNum) -> new Statement(rs.getLong("id"), rs.getLong("tenant_id"), rs.getString("bill_no"), rs.getString("customer_name_snapshot"), + (rs, rowNum) -> new Statement(rs.getLong("id"), rs.getLong("tenant_id"), rs.getString("bill_no"), names.apply(rs.getLong("tenant_id"), rs.getString("customer_name_snapshot")), rs.getDate("bill_date").toLocalDate(), rs.getDate("due_date").toLocalDate(), rs.getBigDecimal("total_amount"), rs.getBigDecimal("received_amount"), rs.getBigDecimal("outstanding_amount"), rs.getString("currency_code"), rs.getString("status"), rs.getInt("version"), rs.getString("customer_confirmation_status"), nullableLong(rs, "customer_confirmed_by"), @@ -219,6 +224,7 @@ public class FinanceQueryService { row.outstandingAmount(), row.currency(), row.status(), row.version())).toList(); } + /** 成本查询按来源付款事实回显金额,直接已付不能再次展示为待付款。 */ public List payableSettlements(Long customerId) { validateOptionalCustomer(customerId); String sql = "SELECT id,tenant_id,cost_no,cost_type,order_id,inbound_id,container_id,supplier_name_snapshot,fee_item_name,cost_amount,currency_code,status,created_at,version FROM fin_cost WHERE 1=1"; @@ -227,9 +233,13 @@ public class FinanceQueryService { return jdbc.query(sql + " ORDER BY created_at DESC,id DESC", (rs, rowNum) -> { BigDecimal amount = rs.getBigDecimal("cost_amount"); + boolean directPaid = "SOURCE_DIRECT_PAID".equals(rs.getString("cost_type")); + // 已确认直付成本已全额付款;作废历史不再计入有效已付,也不形成新应付。 + BigDecimal paid = directPaid && "CONFIRMED".equals(rs.getString("status")) ? amount : BigDecimal.ZERO; + BigDecimal outstanding = directPaid ? BigDecimal.ZERO : amount; return new PayableSettlement(rs.getLong("id"), rs.getLong("tenant_id"), rs.getString("cost_no"), rs.getString("cost_type"), nullableLong(rs, "order_id"), nullableLong(rs, "inbound_id"), nullableLong(rs, "container_id"), rs.getString("supplier_name_snapshot"), - rs.getString("fee_item_name"), amount, BigDecimal.ZERO, amount, rs.getString("currency_code"), rs.getString("status"), + rs.getString("fee_item_name"), amount, paid, outstanding, rs.getString("currency_code"), rs.getString("status"), time(rs, "created_at"), rs.getInt("version")); }, args); } diff --git a/finance/src/main/java/com/baiyue/finance/service/FinanceSettlementService.java b/finance/src/main/java/com/baiyue/finance/service/FinanceSettlementService.java index 3f1e8a1fa..243bdfbc4 100644 --- a/finance/src/main/java/com/baiyue/finance/service/FinanceSettlementService.java +++ b/finance/src/main/java/com/baiyue/finance/service/FinanceSettlementService.java @@ -52,19 +52,24 @@ public class FinanceSettlementService { private final FinanceVoucherValidator voucherValidator; private final FinanceSupplierValidator supplierValidator; private final FinanceDocumentNumberGenerator documentNumbers; + private final FinanceCustomerNames customerNames; + private final com.baiyue.finance.repository.FinanceSettlementStateRepository states; /** 注入统一编号组件,确保所有业务入口共用受事务保护的日流水。 */ @Autowired public FinanceSettlementService(JdbcTemplate jdbc, ObjectMapper objectMapper, FinanceVoucherValidator voucherValidator, FinanceSupplierValidator supplierValidator, FinanceDocumentNumberGenerator documentNumbers, - com.baiyue.finance.repository.FinanceSettlementDetailRepository details) { + com.baiyue.finance.repository.FinanceSettlementDetailRepository details, FinanceCustomerNames customerNames, + com.baiyue.finance.repository.FinanceSettlementStateRepository states) { this.jdbc = jdbc; this.details = details; this.objectMapper = objectMapper; this.voucherValidator = voucherValidator; this.supplierValidator = supplierValidator; this.documentNumbers = documentNumbers; + this.customerNames = customerNames; + this.states = states; } /** 创建应收结算单,幂等请求恢复原单,首次建单分配 AR 日流水。 */ @@ -87,18 +92,16 @@ public class FinanceSettlementService { Long replay = reserve(customerId, operatorId, "SETTLEMENT_AR_CREATE", key, "POST", "/api/platform/finance/v1/receivable-settlements", request); if (replay != null) return getSettlement(replay); - Integer occupied = jdbc.queryForObject( - "SELECT COUNT(*) FROM fin_settlement WHERE tenant_id=? AND direction='RECEIVABLE' " + - "AND reconciliation_statement_id=? AND status<>'VOIDED'", Integer.class, - customerId, request.reconciliationStatementId()); - if (occupied != null && occupied > 0) throw error("FIN_409", "对账单已生成有效应收结算单"); + // 新建与重开共用当前锁定读,等待来源锁后不能沿用事务开始时的占用快照。 + if (states.billOccupied(customerId, request.reconciliationStatementId(), 0)) + throw error("FIN_409", "对账单已生成有效应收结算单"); Integer lineCount = jdbc.queryForObject("SELECT COUNT(*) FROM fin_bill_line WHERE tenant_id=? AND bill_id=?", Integer.class, customerId, request.reconciliationStatementId()); if (lineCount == null || lineCount == 0) throw error("FIN_409", "对账单没有可结算费用明细"); LocalDate generatedDate = LocalDate.now(); long settlementId = insertSettlement(customerId, documentNumbers.next("AR"), "RECEIVABLE", - text(bill, "customer_name_snapshot"), null, null, request.reconciliationStatementId(), + customerNames.requiredSnapshot(customerId, text(bill, "customer_name_snapshot")), null, null, request.reconciliationStatementId(), date(bill, "bill_date").format(DateTimeFormatter.ofPattern("yyyy-MM")), CNY, total, generatedDate, date(bill, "due_date"), operatorId); int inserted = jdbc.update( @@ -146,17 +149,14 @@ public class FinanceSettlementService { throw error("FIN_409", "费用来源未审核、金额无效或状态不可用"); } if (!CNY.equals(text(source, "currency_code"))) throw error("FIN_400", "费用来源币种必须为CNY"); - Integer used = jdbc.queryForObject( - "SELECT COUNT(*) FROM fin_settlement_line sl JOIN fin_settlement s ON s.id=sl.settlement_id " + - "WHERE sl.fee_source_id=? AND s.status<>'VOIDED'", Integer.class, number(source, "id").longValue()); - if (used != null && used > 0) throw error("FIN_409", "费用来源已被有效结算单占用"); + if (states.sourceOccupied(customerId, number(source, "id").longValue(), 0)) + throw error("FIN_409", "费用来源已被有效结算单占用"); } BigDecimal total = sources.stream().map(row -> decimal(row, "amount")).reduce(BigDecimal.ZERO, BigDecimal::add); if (total.signum() <= 0) throw error("FIN_400", "应付结算金额必须大于0"); String supplierCategory = supplierCategoryConstraint(sources); String supplierName = supplierValidator.requireActive(request.supplierId(), supplierCategory).name(); - String customerName = sources.stream().map(row -> jsonText(parseTree(text(row, "source_snapshot")), "customerName")) - .filter(StringUtils::hasText).findFirst().orElse("客户" + customerId); + String customerName = customerNames.required(customerId); LocalDate generatedDate = LocalDate.now(); long settlementId = insertSettlement(customerId, documentNumbers.next("AP"), "PAYABLE", customerName, request.supplierId(), supplierName, null, generatedDate.format(DateTimeFormatter.ofPattern("yyyy-MM")), @@ -176,7 +176,7 @@ public class FinanceSettlementService { if (settlementId <= 0) throw error("FIN_404", "结算单不存在"); List> records = details.summary(settlementId); if (records.isEmpty()) throw error("FIN_404", "结算单不存在"); - SettlementSummary summary = summary(records.get(0)); + SettlementSummary summary = summary(records.get(0), customerNames.resolver()); List lines = details.lines(summary.customerId(), settlementId) .stream().map(source -> line(source, summary)).toList(); List transactions = new ArrayList<>(details.payments(summary.customerId(), settlementId) @@ -206,7 +206,8 @@ public class FinanceSettlementService { args.add(customerId); } sql += " ORDER BY s.generated_date DESC,s.id DESC"; - return jdbc.queryForList(sql, args.toArray()).stream().map(this::summary).toList(); + var names = customerNames.resolver(); + return jdbc.queryForList(sql, args.toArray()).stream().map(row -> summary(row, names)).toList(); } @Transactional @@ -247,13 +248,14 @@ public class FinanceSettlementService { return getSettlement(settlementId); } + /** 作废保留来源关联;重开必须重新校验并锁定当前仍可占用的原始依据。 */ @Transactional public SettlementDetail actionSettlement(long settlementId, long operatorId, String key, SettlementActionRequest request) { validateSettlementAction(request); - Map settlement = one( - "SELECT id,tenant_id,total_amount,paid_amount,outstanding_amount,payment_status,status,version " + - "FROM fin_settlement WHERE id=? FOR UPDATE", "结算单不存在", settlementId); + List> locked = states.lockSettlement(settlementId); + if (locked.isEmpty()) throw error("FIN_404", "结算单不存在"); + Map settlement = locked.get(0); long customerId = number(settlement, "tenant_id").longValue(); Long replay = reserve(customerId, operatorId, "SETTLEMENT_STATE_ACTION", key, "POST", "/api/platform/finance/v1/settlements/" + settlementId + "/actions", request); @@ -272,11 +274,10 @@ public class FinanceSettlementService { if (!"VOIDED".equals(currentStatus) || decimal(settlement, "paid_amount").signum() != 0) { throw error("FIN_409", "只有未发生收付款的已作废结算单可以重开"); } + requireReopenableSources(settlement); targetStatus = "ISSUED"; } - int changed = jdbc.update("UPDATE fin_settlement SET status=?,updated_by=?,version=version+1 " + - "WHERE id=? AND tenant_id=? AND version=?", targetStatus, operatorId, settlementId, customerId, - request.expectedVersion()); + int changed = states.changeStatus(targetStatus, operatorId, settlementId, customerId, request.expectedVersion()); if (changed != 1) throw error("FIN_409", "结算单版本冲突"); audit(customerId, settlementId, request.action(), request.reason(), settlementStateSnapshot(settlement), settlementStateSnapshot(settlement, targetStatus), operatorId); @@ -284,6 +285,77 @@ public class FinanceSettlementService { return getSettlement(settlementId); } + /** 重开沿用原单金额,来源变化必须重新生成结算,不覆盖历史单据。 */ + private void requireReopenableSources(Map settlement) { + long customerId = number(settlement, "tenant_id").longValue(); + long settlementId = number(settlement, "id").longValue(); + if (!CNY.equals(text(settlement, "currency_code")) || decimal(settlement, "total_amount").signum() <= 0) + throw error("FIN_409", "原结算金额或币种已不满足重开条件"); + if ("RECEIVABLE".equals(text(settlement, "direction"))) { + requireReopenableReceivable(settlement); + return; + } + if (!"PAYABLE".equals(text(settlement, "direction"))) throw error("FIN_409", "结算方向不支持重开"); + Long supplierId = nullableNumber(settlement, "supplier_id"); + List> sources = states.lockSources(customerId, settlementId); + if (supplierId == null || sources.isEmpty()) throw error("FIN_409", "原应付来源或供应商不完整"); + BigDecimal total = BigDecimal.ZERO; + List> categories = new ArrayList<>(); + for (Map source : sources) { + JsonNode snapshot = parseTree(text(source, "source_snapshot")); + if (source.get("id") == null || number(source, "customer_id").longValue() != customerId + || !"PAYABLE".equals(text(source, "direction")) || !"AVAILABLE".equals(text(source, "status")) + || !"APPROVED".equals(text(source, "review_status")) || !"ON_ACCOUNT".equals(text(source, "settlement_mode")) + || !CNY.equals(text(source, "currency_code")) || !CNY.equals(text(source, "line_currency")) + || !supplierId.equals(jsonLong(snapshot, "supplierId")) || decimal(source, "amount").signum() <= 0 + || decimal(source, "amount").compareTo(decimal(source, "line_amount")) != 0) + throw error("FIN_409", "原应付费用已调整、作废或不再满足结算条件,请重新生成结算单"); + if (states.sourceOccupied(customerId, number(source, "fee_source_id").longValue(), settlementId)) + throw error("FIN_409", "原应付费用已被其他有效结算单占用"); + total = total.add(decimal(source, "line_amount")); + Map category = new java.util.HashMap<>(); + category.put("supplier_category", jsonText(snapshot, "supplierCategory")); + categories.add(category); + } + if (total.compareTo(decimal(settlement, "total_amount")) != 0) + throw error("FIN_409", "原应付结算与费用金额不一致,请重新生成结算单"); + supplierValidator.requireActive(supplierId, supplierCategoryConstraint(categories)); + } + + /** 应收重开还须核对原账单当前状态及逐行金额,防止调账后恢复旧应收。 */ + private void requireReopenableReceivable(Map settlement) { + long customerId = number(settlement, "tenant_id").longValue(); + long settlementId = number(settlement, "id").longValue(); + Long billId = nullableNumber(settlement, "reconciliation_statement_id"); + if (billId == null) throw error("FIN_409", "原应收缺少对账单关联"); + List> bills = states.lockBill(customerId, billId); + if (bills.isEmpty()) throw error("FIN_409", "原对账单不存在或不属于当前客户"); + Map bill = bills.get(0); + if (!"ISSUED".equals(text(bill, "status")) || !CNY.equals(text(bill, "currency_code")) + || decimal(bill, "received_amount").signum() != 0 + || decimal(bill, "total_amount").compareTo(decimal(settlement, "total_amount")) != 0) + throw error("FIN_409", "原对账单状态或金额已变化,请重新生成应收结算单"); + if (states.billOccupied(customerId, billId, settlementId)) + throw error("FIN_409", "原对账单已生成其他有效应收结算单"); + List> lines = states.lockBillLines(customerId, billId); + List> sources = states.lockSources(customerId, settlementId); + if (lines.isEmpty() || sources.size() != lines.size()) throw error("FIN_409", "原应收明细与对账单不一致"); + BigDecimal total = BigDecimal.ZERO; + for (Map source : sources) { + var matching = lines.stream().filter(line -> number(line, "fee_source_id").longValue() == number(source, "fee_source_id").longValue()).toList(); + if (source.get("id") == null || number(source, "customer_id").longValue() != customerId + || !"RECEIVABLE".equals(text(source, "direction")) || !"BILLED".equals(text(source, "status")) + || !"APPROVED".equals(text(source, "review_status")) || !CNY.equals(text(source, "currency_code")) + || !CNY.equals(text(source, "line_currency")) || matching.size() != 1 + || !CNY.equals(text(matching.get(0), "line_currency")) + || decimal(source, "line_amount").compareTo(decimal(matching.get(0), "line_amount")) != 0) + throw error("FIN_409", "原应收费用或明细金额已变化,请重新生成结算单"); + total = total.add(decimal(source, "line_amount")); + } + if (total.compareTo(decimal(settlement, "total_amount")) != 0) + throw error("FIN_409", "原应收明细合计与结算金额不一致"); + } + private long insertSettlement(long customerId, String no, String direction, String customerName, Long supplierId, String supplierName, Long reconciliationId, String businessPeriod, String currency, BigDecimal total, @@ -419,9 +491,9 @@ public class FinanceSettlementService { MDC.get("requestId"), MDC.get("traceId")); } - private SettlementSummary summary(Map row) { + private SettlementSummary summary(Map row, java.util.function.BiFunction names) { return new SettlementSummary(number(row, "id").longValue(), text(row, "settlement_no"), - text(row, "direction"), number(row, "tenant_id").longValue(), text(row, "customer_name_snapshot"), + text(row, "direction"), number(row, "tenant_id").longValue(), names.apply(number(row, "tenant_id").longValue(), text(row, "customer_name_snapshot")), nullableNumber(row, "supplier_id"), text(row, "supplier_name_snapshot"), nullableNumber(row, "reconciliation_statement_id"), text(row, "reconciliation_statement_no"), text(row, "business_period"), text(row, "fee_item_summary"), diff --git a/finance/src/main/java/com/baiyue/finance/service/ReceivableStatisticsService.java b/finance/src/main/java/com/baiyue/finance/service/ReceivableStatisticsService.java index 07ce5c305..2d1940454 100644 --- a/finance/src/main/java/com/baiyue/finance/service/ReceivableStatisticsService.java +++ b/finance/src/main/java/com/baiyue/finance/service/ReceivableStatisticsService.java @@ -15,9 +15,11 @@ import java.util.List; public class ReceivableStatisticsService { private static final String CURRENCY = "CNY"; private final JdbcTemplate jdbc; + private final FinanceCustomerNames customerNames; - public ReceivableStatisticsService(JdbcTemplate jdbc) { + public ReceivableStatisticsService(JdbcTemplate jdbc, FinanceCustomerNames customerNames) { this.jdbc = jdbc; + this.customerNames = customerNames; } public ReceivableStatisticsResponse query(YearMonth month, Long customerId, int page, int pageSize) { @@ -61,8 +63,9 @@ public class ReceivableStatisticsService { args.add(customerId); } sql.append(" GROUP BY s.tenant_id,p.customer_name_snapshot ORDER BY customer_name_snapshot,s.tenant_id"); + var names = customerNames.resolver(); return jdbc.query(sql.toString(), (rs, rowNum) -> checked(new ReceivableStatisticsRow( - rs.getLong("tenant_id"), rs.getString("customer_name_snapshot"), CURRENCY, + rs.getLong("tenant_id"), names.apply(rs.getLong("tenant_id"), rs.getString("customer_name_snapshot")), CURRENCY, rs.getBigDecimal("total_receivable"), rs.getBigDecimal("total_received"), rs.getBigDecimal("total_outstanding"))), args.toArray()); } diff --git a/finance/src/main/java/com/baiyue/finance/service/ReconciliationStatementService.java b/finance/src/main/java/com/baiyue/finance/service/ReconciliationStatementService.java index 34c5c62eb..ee4fc7f2e 100644 --- a/finance/src/main/java/com/baiyue/finance/service/ReconciliationStatementService.java +++ b/finance/src/main/java/com/baiyue/finance/service/ReconciliationStatementService.java @@ -46,14 +46,16 @@ public class ReconciliationStatementService { private final JdbcTemplate jdbc; private final ObjectMapper objectMapper; private final FinanceDocumentNumberGenerator documentNumbers; + private final FinanceCustomerNames customerNames; /** 注入统一编号组件,确保所有业务入口共用受事务保护的日流水。 */ @Autowired public ReconciliationStatementService(JdbcTemplate jdbc, ObjectMapper objectMapper, - FinanceDocumentNumberGenerator documentNumbers) { + FinanceDocumentNumberGenerator documentNumbers, FinanceCustomerNames customerNames) { this.jdbc = jdbc; this.objectMapper = objectMapper; this.documentNumbers = documentNumbers; + this.customerNames = customerNames; } /** 创建对账单并分配 REC 日流水,重复请求返回已创建记录。 */ @@ -112,6 +114,7 @@ public class ReconciliationStatementService { return detail(request.customerId(), statementId); } + /** 推进对账单状态并保存修订,响应包含本次操作刚写入的审计记录。 */ @Transactional public ReconciliationDetail action(long operatorId, long statementId, String key, ReconciliationActionRequest request) { validateAction(statementId, request); @@ -150,9 +153,11 @@ public class ReconciliationStatementService { ReconciliationDetail after = detail(before.customerId(), statementId); audit(before.customerId(), statementId, "RECONCILIATION_" + action, request.reason(), before, after, operatorId); complete(before.customerId(), operatorId, "RECONCILIATION_ACTION", key, statementId); - return after; + // 审计在金额/状态快照之后写入,返回前重新读取,避免首个响应缺少最新修订。 + return detail(before.customerId(), statementId); } + /** 调整已出账明细并保留前后金额,响应与随后查询的修订历史一致。 */ private ReconciliationDetail adjust(long operatorId, long statementId, String key, ReconciliationActionRequest request, Bill before) { if (!"ISSUED".equals(before.status())) throw error("FIN_409", "仅已出账对账单允许调整"); @@ -174,7 +179,7 @@ public class ReconciliationStatementService { ReconciliationDetail after = detail(before.customerId(), statementId); audit(before.customerId(), statementId, "RECONCILIATION_ADJUST", request.reason(), before, after, operatorId); complete(before.customerId(), operatorId, "RECONCILIATION_ACTION", key, statementId); - return after; + return detail(before.customerId(), statementId); } public ReconciliationDetail detail(long customerId, long statementId) { @@ -195,7 +200,7 @@ public class ReconciliationStatementService { List revisions = jdbc.query( "SELECT action,reason,before_data,after_data,operator_id,operator_name_snapshot,created_at FROM fin_audit_log WHERE tenant_id=? AND resource_type='RECONCILIATION_STATEMENT' AND resource_id=? AND action<>'RECONCILIATION_CUSTOMER_CONFIRM' AND before_data IS NOT NULL ORDER BY created_at,id", (rs, row) -> revision(rs, row + 1), customerId, statementId); - return new ReconciliationDetail(bill.id(), bill.number(), bill.customerId(), bill.customerName(), + return new ReconciliationDetail(bill.id(), bill.number(), bill.customerId(), customerNames.resolver().apply(bill.customerId(), bill.customerName()), metadata.periodStart(), metadata.periodEnd(), bill.creditDays(), "CNY", bill.total(), bill.received(), bill.outstanding(), bill.outstanding().signum() == 0, reconciliationStatus(bill.status()), bill.version(), bill.customerConfirmationStatus(), bill.customerConfirmedBy(), bill.customerConfirmedByName(), @@ -278,7 +283,7 @@ public class ReconciliationStatementService { private CustomerProfile customer(long customerId) { List rows = jdbc.query("SELECT customer_name_snapshot,credit_days FROM fin_customer_settlement_profile WHERE tenant_id=? AND status='ACTIVE'", (rs, row) -> new CustomerProfile(rs.getString("customer_name_snapshot"), rs.getInt("credit_days")), customerId); - return rows.isEmpty() ? new CustomerProfile("客户" + customerId, 30) : rows.get(0); + return new CustomerProfile(customerNames.required(customerId), rows.isEmpty() ? 30 : rows.get(0).creditDays()); } private long activeSubject() { @@ -488,18 +493,19 @@ public class ReconciliationStatementService { } private ReconciliationRevision revision(ResultSet rs, int revisionNo) throws SQLException { - return new ReconciliationRevision(revisionNo, rs.getString("action").replaceFirst("^RECONCILIATION_", ""), amountFromJson(rs.getString("before_data")), - amountFromJson(rs.getString("after_data")), rs.getString("reason"), rs.getLong("operator_id"), + return new ReconciliationRevision(revisionNo, rs.getString("action").replaceFirst("^RECONCILIATION_", ""), amountFromJson(rs.getString("before_data"), "total"), + amountFromJson(rs.getString("after_data"), "totalReceivable"), rs.getString("reason"), rs.getLong("operator_id"), rs.getString("operator_name_snapshot"), rs.getTimestamp("created_at").toLocalDateTime()); } - private BigDecimal amountFromJson(String value) { - if (!StringUtils.hasText(value)) return BigDecimal.ZERO; + /** 变更前为 Bill,变更后为 ReconciliationDetail;缺失金额属于损坏快照,不能伪装为零。 */ + private BigDecimal amountFromJson(String value, String field) { try { - JsonNode node = objectMapper.readTree(value); - JsonNode amount = node.has("totalAmount") ? node.get("totalAmount") : node.get("total"); - return amount == null || amount.isNull() ? BigDecimal.ZERO : amount.decimalValue(); + JsonNode node = objectMapper.reader().with(com.fasterxml.jackson.databind.DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS).readTree(value); + JsonNode amount = node.get(field); + if (amount == null || !amount.isNumber()) throw new IllegalArgumentException("缺失金额"); + return amount.decimalValue(); } catch (Exception exception) { throw error("FIN_001", "对账单修订快照损坏"); } diff --git a/finance/src/main/resources/db/finance.sql b/finance/src/main/resources/db/finance.sql index de37e73e1..d34f37d4f 100644 --- a/finance/src/main/resources/db/finance.sql +++ b/finance/src/main/resources/db/finance.sql @@ -225,7 +225,8 @@ CREATE TABLE IF NOT EXISTS fin_bill_line ( currency_code CHAR(3) NOT NULL DEFAULT 'CNY' COMMENT 'ISO 4217三位币种代码,首期仅CNY', created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '账单明细创建时间', PRIMARY KEY (id), - UNIQUE KEY uk_fin_bill_line_source (source_type, source_id, source_version), + UNIQUE KEY uk_fin_bill_line_bill_source (bill_id, source_type, source_id, source_version), + KEY idx_fin_bill_line_source (source_type, source_id, source_version), KEY idx_fin_bill_line_bill (tenant_id, bill_id), CONSTRAINT chk_fin_bill_line_tenant CHECK (tenant_id > 0), CONSTRAINT chk_fin_bill_line_currency CHECK (currency_code = 'CNY'), @@ -437,6 +438,7 @@ CREATE TABLE IF NOT EXISTS fin_settlement ( due_date DATE NOT NULL COMMENT '结算到期日期', payment_status VARCHAR(24) NOT NULL DEFAULT 'UNPAID' COMMENT '收付款状态:UNPAID未收付、PARTIALLY_PAID部分收付、PAID已结清', status VARCHAR(24) NOT NULL DEFAULT 'ISSUED' COMMENT '结算状态:ISSUED已生效、PARTIALLY_PAID部分收付、PAID已结清、VOIDED已作废', + active_receivable_statement_id BIGINT UNSIGNED GENERATED ALWAYS AS (CASE WHEN direction='RECEIVABLE' AND status<>'VOIDED' THEN reconciliation_statement_id ELSE NULL END) VIRTUAL COMMENT '有效应收占用的对账单;作废单不占用,历史关联仍保留', version INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '乐观锁版本号', created_by BIGINT UNSIGNED NOT NULL COMMENT '创建操作人ID', updated_by BIGINT UNSIGNED NOT NULL COMMENT '最后修改操作人ID', @@ -444,7 +446,7 @@ CREATE TABLE IF NOT EXISTS fin_settlement ( updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '最后更新时间', PRIMARY KEY (id), UNIQUE KEY uk_fin_settlement_no (tenant_id, settlement_no), - UNIQUE KEY uk_fin_settlement_receivable_statement (tenant_id, reconciliation_statement_id, direction), + UNIQUE KEY uk_fin_settlement_active_receivable (tenant_id, active_receivable_statement_id), KEY idx_fin_settlement_customer_direction_status (tenant_id, direction, status), KEY idx_fin_settlement_supplier_status (supplier_id, direction, status), KEY idx_fin_settlement_receivable_statistics (direction, currency_code, generated_date, status, tenant_id), diff --git a/finance/src/main/resources/openapi/finance-wallet-refund-v1.yaml b/finance/src/main/resources/openapi/finance-wallet-refund-v1.yaml new file mode 100644 index 000000000..ec75a8eb3 --- /dev/null +++ b/finance/src/main/resources/openapi/finance-wallet-refund-v1.yaml @@ -0,0 +1,61 @@ +openapi: 3.0.3 +info: + title: 平台资金账户关联退款 + version: 1.0.0 + description: 原消费保持不可变;退款记录关联原消费流水号,累计退款不得超过原消费金额,包含业务退款事件已退金额。 +paths: + /api/platform/finance/v1/accounts/{customerId}/ledger/{ledgerId}/refunds: + post: + operationId: refundWalletConsumption + summary: 原消费关联退款 + description: 仅平台 tenantId=0 且具有 platform:finance:customer-account:adjust 权限可操作。账户行锁串行校验累计可退金额,无需前端提交账户版本。优先归还已用授信,再增加实际余额。相同键同请求返回原流水;相同键不同原消费或金额失败。非成功消费、跨客户原流水、超额退款均拒绝。 + security: + - bearerAuth: [] + parameters: + - {name: customerId, in: path, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}} + - {name: ledgerId, in: path, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}} + - {name: Idempotency-Key, in: header, required: true, schema: {type: string, minLength: 1, maxLength: 128}} + requestBody: + required: true + content: + application/json: + schema: + type: object + required: [amount, reason] + properties: + amount: {type: number, minimum: 0.01, maximum: 999999999999.99, multipleOf: 0.01} + reason: {type: string, minLength: 1, maxLength: 500, description: 必填退款原因,不得仅空白} + responses: + '200': + description: 统一响应 code=200 成功;业务失败使用 FIN-REQ-400、FIN-WALLET-404、FIN-IDEMPOTENCY-CONFLICT 或 FIN-WALLET-REFUND-EXCEEDED 等业务码。 + content: + application/json: + schema: + type: object + properties: + code: {type: string} + data: + $ref: '#/components/schemas/RefundLedgerEntry' + '403': {description: 非平台主体或缺少调账权限} +components: + securitySchemes: + bearerAuth: {type: http, scheme: bearer, bearerFormat: JWT} + schemas: + RefundLedgerEntry: + type: object + description: 完整返回 FinanceWalletDtos.WalletLedgerEntry,资金余额字段沿用账户流水协议。 + properties: + ledgerId: {type: string} + businessNo: {type: string, description: 新退款流水正式 AC 单号} + changeType: {type: string, enum: [REFUND]} + amount: {type: number, description: 实际退款正金额} + sourceType: {type: string, enum: [BUSINESS_REFUND]} + sourceNo: {type: string, description: 客户及幂等键派生的稳定技术退款引用} + referenceNo: {type: string, description: 原消费流水 businessNo,用于关联及累计可退校验} + operatorName: {type: string, nullable: true} + remark: {type: string, description: 退款原因} + actualBalanceAfter: {type: number} + frozenBalanceAfter: {type: number} + availableBalanceAfter: {type: number} + creditUsedAfter: {type: number} + availableCreditAfter: {type: number} diff --git a/finance/src/test/java/com/baiyue/finance/BusinessFeeSourceEventServiceTest.java b/finance/src/test/java/com/baiyue/finance/BusinessFeeSourceEventServiceTest.java index 3d69bc90a..614ee8f91 100644 --- a/finance/src/test/java/com/baiyue/finance/BusinessFeeSourceEventServiceTest.java +++ b/finance/src/test/java/com/baiyue/finance/BusinessFeeSourceEventServiceTest.java @@ -7,9 +7,16 @@ import com.baiyue.common.event.EventPayloadHash; import com.baiyue.common.event.EventTypes; import com.baiyue.finance.service.BusinessFeeSourceEventService; import com.baiyue.finance.service.FinanceSupplierValidator; +import com.baiyue.finance.service.FinanceEventException; +import com.baiyue.finance.repository.BusinessFeeSourceRepository; +import com.baiyue.finance.dto.BusinessFeeSourceVersion; +import com.baiyue.common.event.DeliveryAck; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import org.junit.jupiter.params.provider.ValueSource; import org.mockito.ArgumentCaptor; import org.springframework.jdbc.core.JdbcTemplate; @@ -25,6 +32,7 @@ import static org.mockito.Mockito.never; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; @@ -37,7 +45,8 @@ class BusinessFeeSourceEventServiceTest { private final JdbcTemplate jdbc = mock(JdbcTemplate.class); private final ObjectMapper json = new ObjectMapper().findAndRegisterModules(); private final FinanceSupplierValidator suppliers = mock(FinanceSupplierValidator.class); - private final BusinessFeeSourceEventService service = new BusinessFeeSourceEventService(jdbc, json, suppliers); + private final BusinessFeeSourceRepository sources = mock(BusinessFeeSourceRepository.class); + private final BusinessFeeSourceEventService service = new BusinessFeeSourceEventService(jdbc, json, suppliers, sources); @Test void importsOverseasFeeWithoutInventingInboundReference() { @@ -116,19 +125,93 @@ class BusinessFeeSourceEventServiceTest { verify(jdbc, never()).update(anyString(), any(Object[].class)); } + /** 旧版本不能覆盖已有较新版本,客户相同时仍保留原乱序门禁。 */ @Test void lateOlderVersionCannotSupersedeNewerSourceOrCost() { when(suppliers.requireActive(71L, null)).thenReturn( new com.baiyue.finance.client.WarehouseSupplierClient.SupplierSnapshot(71L, "供应商", null, "ACTIVE")); - when(jdbc.queryForList(startsWith("SELECT source_fee_version"), eq("LASTLEG"), - eq("OVERSEAS_CONTAINER_FEE"), eq(701L))) - .thenReturn(List.of(Map.of("source_fee_version", 2))); + when(sources.lockVersions("LASTLEG", "OVERSEAS_CONTAINER_FEE", 701L)) + .thenReturn(List.of(new BusinessFeeSourceVersion(2, 10001L))); assertThatThrownBy(() -> service.consume(containerEvent("CNY", "61", "PUBLISHED", 1))) .hasMessageContaining("版本已过期"); verify(jdbc, never()).update(startsWith("UPDATE fin_fee_source"), any(Object[].class)); verify(jdbc, never()).update(startsWith("UPDATE fin_cost"), any(Object[].class)); } + /** 已完成的同一事件不再查询会停用、改名或暂时不可用的供应商主档。 */ + @ParameterizedTest + @ValueSource(strings = {"FIN_409", "FIN_503", "RENAMED"}) + void completedReplayDoesNotDependOnCurrentSupplier(String change) { + EventEnvelope event = containerEvent("CNY", "61", "PUBLISHED", 1); + when(jdbc.queryForList(startsWith("SELECT payload_hash,status"), eq("LASTLEG"), eq(event.eventId()))) + .thenReturn(List.of(Map.of("payload_hash", event.payloadHash(), "status", "PROCESSED", "result_resource_id", 901L))); + if ("RENAMED".equals(change)) { + when(suppliers.requireActive(71L, null)).thenReturn( + new com.baiyue.finance.client.WarehouseSupplierClient.SupplierSnapshot(71L, "新供应商名称", null, "ACTIVE")); + } else { + when(suppliers.requireActive(71L, null)).thenThrow(new FinanceEventException(change, "供应商不可用")); + } + assertThat(service.consume(event).outcome()).isEqualTo(DeliveryAck.Outcome.DUPLICATE); + verify(suppliers, never()).requireActive(anyLong(), any()); + verify(jdbc, never()).update(anyString(), any(Object[].class)); + } + + /** 首次接收仍须失败关闭,不能为修复重放而绕过供应商校验。 */ + @ParameterizedTest + @ValueSource(strings = {"FIN_409", "FIN_503"}) + void newPayableStillRejectsUnavailableSupplier(String code) { + EventEnvelope event = containerEvent("CNY", "61", "PUBLISHED", 1); + when(suppliers.requireActive(71L, null)).thenThrow(new FinanceEventException(code, "供应商不可用")); + assertThatThrownBy(() -> service.consume(event)).hasMessageContaining("供应商不可用"); + verify(jdbc, never()).update(anyString(), any(Object[].class)); + } + + /** 两个摘要均正确也不能绕过金额算术、人民币汇率及精度约束。 */ + @ParameterizedTest + @CsvSource({"USD,1.00,8.000000,120.00", "CNY,120.00,2.000000,240.00", + "USD,1.00,8.000000,8.001", "USD,1.001,8.000000,8.01", "USD,1.00,8.0000001,8.00"}) + void rejectsInconsistentAmountOrPrecision(String currency, String original, String rate, String total) { + EventEnvelope event = convertedEvent(currency, original, rate, total); + assertThatThrownBy(() -> service.consume(event)).isInstanceOfSatisfying(FinanceEventException.class, + error -> assertThat(error.code()).isEqualTo("FIN-EVT-019")); + verify(jdbc, never()).update(anyString(), any(Object[].class)); + } + + /** 分位舍入与数值相等的尾零均遵循生产端规则,不错误拒收合法费用。 */ + @ParameterizedTest + @CsvSource({"USD,0.04,0.125000,0.01", "USD,16.85,7.122000,120.01", "CNY,120.0000,1.00000000,120.0000"}) + void acceptsProducerRoundingAndEquivalentTrailingZeros(String currency, String original, String rate, String total) { + when(jdbc.queryForObject(startsWith("SELECT id FROM fin_fee_source"), eq(Long.class), + eq("LASTLEG"), eq("OVERSEAS_WAREHOUSE_FEE"), eq(701L), eq(1))).thenReturn(901L); + assertThat(service.consume(convertedEvent(currency, original, rate, total)).outcome()) + .isEqualTo(DeliveryAck.Outcome.APPLIED); + } + + /** 调整只允许原客户,错误归属不得替代旧费用或旧成本。 */ + @Test + void crossCustomerRevisionCannotReplaceExistingSource() { + var event = payableEvent("ADJUSTED", 2); + when(sources.lockVersions("LASTLEG", "OVERSEAS_CONTAINER_FEE", 701L)) + .thenReturn(List.of(new BusinessFeeSourceVersion(1, 10002L))); + assertThatThrownBy(() -> service.consume(event)).isInstanceOfSatisfying(FinanceEventException.class, + error -> assertThat(error.code()).isEqualTo("FIN-EVT-018")); + noFinancialWrites(); + } + + /** 创建金额测试快照时同时更新正式来源摘要与事件摘要。 */ + private EventEnvelope convertedEvent(String currency, String original, String rate, String total) { + EventEnvelope base = eventWithoutInbound(); + ObjectNode payload = base.payload().deepCopy(); + payload.put("originalCurrency", currency).put("originalAmount", new BigDecimal(original)) + .put("exchangeRate", new BigDecimal(rate)).put("totalAmount", new BigDecimal(total)); + payload.putNull("action"); payload.putNull("sourceHash"); + payload.put("sourceHash", EventPayloadHash.sha256(EventPayloadCanonicalizer.canonicalize(payload))); + payload.put("action", "PUBLISHED"); + return new EventEnvelope(base.eventId(), base.eventType(), base.eventVersion(), base.occurredAt(), base.tenantId(), + base.aggregateType(), base.aggregateId(), base.aggregateVersion(), null, base.traceId(), base.correlationId(), null, + payload, EventPayloadHash.sha256(EventPayloadCanonicalizer.canonicalize(payload))); + } + @Test void replayReturnsOriginalSourceWithoutDuplicatingCost() { EventEnvelope event = containerEvent("CNY", "61", "PUBLISHED", 1); @@ -245,6 +328,53 @@ class BusinessFeeSourceEventServiceTest { noFinancialWrites(); } + /** 新采购主单只有明确选择账单时才可作为未收应收入池。 */ + @Test + void manualProcurementExplicitBillCanEnterReceivablePool() { + EventEnvelope event = manualProcurementEvent("BILL"); + when(jdbc.queryForObject(startsWith("SELECT id FROM fin_fee_source"), eq(Long.class), + eq("LASTLEG"), eq("OVERSEAS_PROCUREMENT_FEE"), eq(701L), eq(1))).thenReturn(901L); + assertThat(service.consume(event).eventId()).isEqualTo(event.eventId()); + verify(jdbc).update(startsWith("INSERT INTO fin_fee_source"), any(Object[].class)); + verify(jdbc, never()).update(startsWith("UPDATE fin_fee_source SET wallet_hold_id="), any(Object[].class)); + } + + /** 采购主单的余额或默认模式不能因兼容账单而绕过实扣校验。 */ + @ParameterizedTest + @org.junit.jupiter.params.provider.NullSource + @ValueSource(strings = {"WALLET"}) + void manualProcurementWithoutConsumptionCannotEnterPool(String mode) { + assertThatThrownBy(() -> service.consume(manualProcurementEvent(mode))).hasMessageContaining("必须引用成功实扣"); + noFinancialWrites(); + } + + /** 保留旧采购子费用的扣款约束,即使载荷声明账单结算也不能入池。 */ + @Test + void legacyProcurementCannotBypassConsumptionWithBillMode() { + EventEnvelope base = procurementEvent(false); + ObjectNode payload = (ObjectNode) base.payload().deepCopy(); + payload.put("settlementMode", "BILL"); + assertThatThrownBy(() -> service.consume(resignProcurementEvent(base, payload))).hasMessageContaining("必须引用成功实扣"); + noFinancialWrites(); + } + + private EventEnvelope manualProcurementEvent(String mode) { + EventEnvelope base = procurementEvent(false); + ObjectNode payload = (ObjectNode) base.payload().deepCopy(); + payload.put("businessType", "OVERSEAS_PROCUREMENT"); + if (mode == null) payload.putNull("settlementMode"); else payload.put("settlementMode", mode); + return resignProcurementEvent(base, payload); + } + + private EventEnvelope resignProcurementEvent(EventEnvelope base, ObjectNode payload) { + payload.putNull("action"); payload.putNull("sourceHash"); + payload.put("sourceHash", EventPayloadHash.sha256(EventPayloadCanonicalizer.canonicalize(payload))); + payload.put("action", "PUBLISHED"); + return new EventEnvelope(base.eventId(), base.eventType(), base.eventVersion(), base.occurredAt(), base.tenantId(), + base.aggregateType(), base.aggregateId(), base.aggregateVersion(), null, base.traceId(), base.correlationId(), null, + payload, EventPayloadHash.sha256(EventPayloadCanonicalizer.canonicalize(payload))); + } + @Test void settledProcurementUsesVerifiedActualConsumptionAndNeverOriginalEstimate() { EventEnvelope event = procurementEvent(true); @@ -286,4 +416,88 @@ class BusinessFeeSourceEventServiceTest { base.aggregateType(), base.aggregateId(), base.aggregateVersion(), null, base.traceId(), base.correlationId(), null, payload, EventPayloadHash.sha256(EventPayloadCanonicalizer.canonicalize(payload))); } + + @Test + void manualConsumptionUsesDedicatedFundingIdentityWhilePreservingServiceOrder() { + verifyDedicatedConsumption("OVERSEAS_MANUAL_FEE", "701-2", "FEE_TYPE_071"); + } + + /** 手动仓租保留人工费用资金身份,仍校验原冻结与实扣来源。 */ + @Test + void manualStorageRentConsumptionUsesManualFundingIdentity() { + verifyDedicatedConsumption("OVERSEAS_MANUAL_FEE", "701-2", "FEE_TYPE_077"); + } + + @Test + void rentConsumptionUsesPreparationIdentityWhilePreservingServiceOrder() { + verifyDedicatedConsumption("STORAGE_RENT", "445", "FEE_TYPE_077"); + } + + @Test + void dedicatedFundingCannotReferenceAnotherManualFeeOrWrongFeeItem() { + assertThatThrownBy(() -> service.consume(dedicatedEvent("OVERSEAS_MANUAL_FEE", "702-1", "FEE_TYPE_071"))) + .hasMessageContaining("来源不匹配"); + assertThatThrownBy(() -> service.consume(dedicatedEvent("STORAGE_RENT", "445", "FEE_TYPE_071"))) + .hasMessageContaining("项目不匹配"); + noFinancialWrites(); + } + + @Test + void dedicatedConsumptionCannotReuseHoldAlreadyLinkedToAnotherSource() { + stubDedicatedConsumption("STORAGE_RENT", "445"); + when(jdbc.queryForList(startsWith("SELECT id FROM fin_fee_source WHERE wallet_hold_id="), + eq(Long.class), eq(9L), eq(901L))).thenReturn(List.of(902L)); + assertThatThrownBy(() -> service.consume(dedicatedEvent("STORAGE_RENT", "445", "FEE_TYPE_077"))) + .hasMessageContaining("已关联其他费用"); + verify(jdbc, never()).update(startsWith("UPDATE fin_fee_source SET wallet_hold_id="), any(Object[].class)); + } + + @Test + void dedicatedConsumptionRejectsResultFromDifferentFundingBusiness() { + stubDedicatedConsumption("STORAGE_RENT", "445"); + when(jdbc.queryForList(startsWith("SELECT l.amount,c.result_json"), eq("command-1"), eq(9L), eq(72L), + eq(10001L), eq("STORAGE_RENT"), eq("445"))).thenReturn(List.of(Map.of( + "amount", new BigDecimal("-120.00"), "result_json", """ + {"result":"SUCCEEDED","operation":"SETTLE","commandEventId":"command-1", + "walletHoldId":"9","walletLedgerId":"72","customerId":10001, + "businessType":"STORAGE_RENT","businessNo":"446","amount":120.00} + """))); + assertThatThrownBy(() -> service.consume(dedicatedEvent("STORAGE_RENT", "445", "FEE_TYPE_077"))) + .hasMessageContaining("资金结果不匹配"); + } + + private void verifyDedicatedConsumption(String type, String no, String item) { + stubDedicatedConsumption(type, no); + EventEnvelope event = dedicatedEvent(type, no, item); + service.consume(event); + verify(jdbc).update(startsWith("UPDATE fin_fee_source SET wallet_hold_id="), eq(9L), eq(901L), eq(10001L)); + assertThat(event.payload().path("businessType").asText()).isEqualTo("WAREHOUSE_SERVICE"); + assertThat(event.payload().path("businessNo").asText()).isEqualTo("PO-US-001"); + } + + private void stubDedicatedConsumption(String type, String no) { + when(jdbc.queryForObject(startsWith("SELECT id FROM fin_fee_source"), eq(Long.class), + eq("LASTLEG"), eq("OVERSEAS_WAREHOUSE_FEE"), eq(701L), eq(1))).thenReturn(901L); + ObjectNode result = json.createObjectNode(); + result.put("result", "SUCCEEDED").put("operation", "SETTLE").put("commandEventId", "command-1") + .put("walletHoldId", "9").put("walletLedgerId", "72").put("customerId", 10001) + .put("businessType", type).put("businessNo", no).put("amount", new BigDecimal("120.00")); + when(jdbc.queryForList(startsWith("SELECT l.amount,c.result_json"), eq("command-1"), eq(9L), eq(72L), + eq(10001L), eq(type), eq(no))).thenReturn(List.of(Map.of( + "amount", new BigDecimal("-120.00"), "result_json", result.toString()))); + } + + private EventEnvelope dedicatedEvent(String type, String no, String item) { + EventEnvelope base = eventWithoutInbound(); + ObjectNode payload = base.payload().deepCopy(); + payload.put("feeItemCode", item); + payload.putObject("walletSettlement").put("commandEventId", "command-1").put("holdId", "9") + .put("ledgerId", "72").put("walletBusinessType", type).put("walletBusinessNo", no); + payload.putNull("action"); payload.putNull("sourceHash"); + payload.put("sourceHash", EventPayloadHash.sha256(EventPayloadCanonicalizer.canonicalize(payload))); + payload.put("action", "PUBLISHED"); + return new EventEnvelope(base.eventId(), base.eventType(), base.eventVersion(), base.occurredAt(), base.tenantId(), + base.aggregateType(), base.aggregateId(), base.aggregateVersion(), null, base.traceId(), base.correlationId(), null, + payload, EventPayloadHash.sha256(EventPayloadCanonicalizer.canonicalize(payload))); + } } diff --git a/finance/src/test/java/com/baiyue/finance/BusinessFeeSourceReceptionPersistenceTest.java b/finance/src/test/java/com/baiyue/finance/BusinessFeeSourceReceptionPersistenceTest.java new file mode 100644 index 000000000..138aadf89 --- /dev/null +++ b/finance/src/test/java/com/baiyue/finance/BusinessFeeSourceReceptionPersistenceTest.java @@ -0,0 +1,238 @@ +package com.baiyue.finance; + +import com.baiyue.common.event.DeliveryAck; +import com.baiyue.common.event.BusinessFeeSourceChangedV1; +import com.baiyue.common.event.EventEnvelope; +import com.baiyue.common.event.EventPayloadCanonicalizer; +import com.baiyue.common.event.EventPayloadHash; +import com.baiyue.common.event.EventTypes; +import com.baiyue.finance.client.WarehouseSupplierClient.SupplierSnapshot; +import com.baiyue.finance.mapper.BusinessFeeSourceMapper; +import com.baiyue.finance.repository.BusinessFeeSourceRepository; +import com.baiyue.finance.service.BusinessFeeSourceEventService; +import com.baiyue.finance.service.FinanceEventException; +import com.baiyue.finance.service.FinanceSupplierValidator; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.apache.ibatis.session.Configuration; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import org.mybatis.spring.SqlSessionFactoryBean; +import org.mybatis.spring.SqlSessionTemplate; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.datasource.DataSourceTransactionManager; +import org.springframework.jdbc.datasource.DriverManagerDataSource; +import org.springframework.transaction.support.TransactionTemplate; + +import java.math.BigDecimal; +import java.time.Instant; +import java.util.UUID; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +/** 隔离数据库执行真实 Mapper 和接收事务,验证失败不留下收件箱或费用半成品。 */ +class BusinessFeeSourceReceptionPersistenceTest { + private final ObjectMapper json = new ObjectMapper().findAndRegisterModules(); + private final FinanceSupplierValidator suppliers = mock(FinanceSupplierValidator.class); + private JdbcTemplate jdbc; + private TransactionTemplate transaction; + private BusinessFeeSourceEventService service; + + /** MyBatis 与既有接收写入共用 Spring 事务连接,不连接共享 TEST 数据库。 */ + @BeforeEach + void setUp() throws Exception { + var data = new DriverManagerDataSource("jdbc:h2:mem:fee-reception-" + UUID.randomUUID() + + ";MODE=MySQL;DB_CLOSE_DELAY=-1", "sa", ""); + jdbc = new JdbcTemplate(data); + jdbc.execute(""" + CREATE TABLE fin_fee_source(id BIGINT AUTO_INCREMENT PRIMARY KEY, + source_system VARCHAR(32),source_type VARCHAR(32),source_fee_id BIGINT,source_fee_version INT, + customer_id BIGINT,source_no VARCHAR(64),business_type VARCHAR(32),business_id BIGINT, + business_no VARCHAR(128),inbound_code VARCHAR(64),warehouse_id BIGINT,direction VARCHAR(16), + order_id BIGINT,inbound_id BIGINT,container_id BIGINT,container_booking_id BIGINT,container_no VARCHAR(64), + fee_item_code VARCHAR(64),fee_item_name VARCHAR(200),amount DECIMAL(20,6),currency_code VARCHAR(3), + settlement_mode VARCHAR(16),paid_at TIMESTAMP,payment_method VARCHAR(32),payment_reference_no VARCHAR(128), + payment_voucher_file_ids VARCHAR(2000),source_hash VARCHAR(71),source_snapshot VARCHAR(10000), + review_status VARCHAR(16),status VARCHAR(16),wallet_hold_id BIGINT,operation_version INT DEFAULT 0, + UNIQUE(source_system,source_type,source_fee_id,source_fee_version)) + """); + jdbc.execute(""" + CREATE TABLE fin_inbox_event(event_id VARCHAR(128),source_system VARCHAR(32),event_type VARCHAR(64), + payload VARCHAR(15000),payload_hash VARCHAR(71),status VARCHAR(16),result_resource_id BIGINT, + processed_at TIMESTAMP,error_code VARCHAR(64),PRIMARY KEY(source_system,event_id)) + """); + jdbc.execute(""" + CREATE TABLE fin_cost(id BIGINT AUTO_INCREMENT PRIMARY KEY,tenant_id BIGINT,cost_no VARCHAR(200), + cost_type VARCHAR(32),cost_origin VARCHAR(32),source_system VARCHAR(32),source_type VARCHAR(32), + source_fee_id BIGINT,source_fee_version INT,source_hash VARCHAR(71),order_id BIGINT,inbound_id BIGINT, + container_id BIGINT,warehouse_id BIGINT,supplier_id BIGINT,supplier_name_snapshot VARCHAR(128), + fee_item_code VARCHAR(64),fee_item_name VARCHAR(200),currency_code VARCHAR(3),cost_amount DECIMAL(20,6), + voucher_file_ids VARCHAR(2000),paid_at TIMESTAMP,payment_method VARCHAR(32),payment_reference_no VARCHAR(128), + status VARCHAR(16),confirmed_at TIMESTAMP,created_by BIGINT,created_by_name VARCHAR(64),updated_by BIGINT, + voided_at TIMESTAMP,void_reason VARCHAR(200),version INT DEFAULT 0, + UNIQUE(source_system,source_type,source_fee_id,source_fee_version)) + """); + jdbc.execute("CREATE TABLE fin_bill(id BIGINT,tenant_id BIGINT,status VARCHAR(16))"); + jdbc.execute("CREATE TABLE fin_bill_line(bill_id BIGINT,tenant_id BIGINT,source_id BIGINT,source_version INT)"); + jdbc.execute("CREATE TABLE fin_settlement(id BIGINT,tenant_id BIGINT,status VARCHAR(16))"); + jdbc.execute("CREATE TABLE fin_settlement_line(settlement_id BIGINT,tenant_id BIGINT,fee_source_id BIGINT)"); + var configuration = new Configuration(); + configuration.addMapper(BusinessFeeSourceMapper.class); + var factory = new SqlSessionFactoryBean(); + factory.setDataSource(data); + factory.setConfiguration(configuration); + var mapper = new SqlSessionTemplate(factory.getObject()).getMapper(BusinessFeeSourceMapper.class); + service = new BusinessFeeSourceEventService(jdbc, json, suppliers, new BusinessFeeSourceRepository(mapper)); + transaction = new TransactionTemplate(new DataSourceTransactionManager(data)); + when(suppliers.requireActive(71L, null)).thenReturn(new SupplierSnapshot(71L, "供应商", null, "ACTIVE")); + } + + /** 释放每个用例独占的内存数据库。 */ + @AfterEach + void tearDown() { + if (jdbc != null) jdbc.execute("SHUTDOWN"); + } + + /** 已提交后模拟丢失回执,供应商停用或故障都不产生第二笔费用和成本。 */ + @ParameterizedTest + @ValueSource(strings = {"FIN_409", "FIN_503"}) + void committedDirectPaidReplayKeepsExactlyOneSourceAndCost(String errorCode) { + var event = event(701, 1, 10001, "DIRECT_PAID", "CNY", "12.00", "1", "12.00"); + assertThat(consume(event).outcome()).isEqualTo(DeliveryAck.Outcome.APPLIED); + reset(suppliers); + when(suppliers.requireActive(71L, null)).thenThrow(new FinanceEventException(errorCode, "供应商不可用")); + + assertThat(consume(event).outcome()).isEqualTo(DeliveryAck.Outcome.DUPLICATE); + verifyNoInteractions(suppliers); + assertCounts(1, 1, 1); + assertThat(jdbc.queryForObject("SELECT status FROM fin_inbox_event", String.class)).isEqualTo("PROCESSED"); + assertThat(jdbc.queryForObject("SELECT cost_amount FROM fin_cost", BigDecimal.class)).isEqualByComparingTo("12.00"); + } + + /** 不同客户的升版失败时回滚已插入 Inbox,原费用与原直付成本保持有效。 */ + @Test + void foreignCustomerRevisionRollsBackAndSameCustomerCanStillAdjust() { + consume(event(701, 1, 10001, "DIRECT_PAID", "CNY", "12.00", "1", "12.00")); + var wrong = event(701, 2, 10002, "DIRECT_PAID", "CNY", "18.00", "1", "18.00"); + assertThatThrownBy(() -> consume(wrong)).isInstanceOfSatisfying(FinanceEventException.class, + error -> assertThat(error.code()).isEqualTo("FIN-EVT-018")); + assertCounts(1, 1, 1); + assertThat(jdbc.queryForObject("SELECT status FROM fin_fee_source", String.class)).isEqualTo("AVAILABLE"); + assertThat(jdbc.queryForObject("SELECT status FROM fin_cost", String.class)).isEqualTo("CONFIRMED"); + assertThat(jdbc.queryForObject("SELECT customer_id FROM fin_fee_source", Long.class)).isEqualTo(10001L); + + consume(event(701, 2, 10001, "DIRECT_PAID", "CNY", "18.00", "1", "18.00")); + assertCounts(2, 2, 2); + assertThat(jdbc.queryForList("SELECT status FROM fin_fee_source ORDER BY source_fee_version", String.class)) + .containsExactly("SUPERSEDED", "AVAILABLE"); + assertThat(jdbc.queryForList("SELECT status FROM fin_cost ORDER BY source_fee_version", String.class)) + .containsExactly("VOIDED", "CONFIRMED"); + } + + /** 错误换算拒收后不残留收件记录,已存在版本也不会被替代。 */ + @Test + void invalidConversionLeavesOriginalAmountsAndReceiptIntact() { + consume(event(701, 1, 10001, "ON_ACCOUNT", "CNY", "12.00", "1", "12.00")); + assertThatThrownBy(() -> consume(event(701, 2, 10001, "ON_ACCOUNT", "USD", "1.00", "8", "120.00"))) + .isInstanceOfSatisfying(FinanceEventException.class, + error -> assertThat(error.code()).isEqualTo("FIN-EVT-019")); + assertCounts(1, 1, 0); + assertThat(jdbc.queryForObject("SELECT amount FROM fin_fee_source", BigDecimal.class)).isEqualByComparingTo("12.00"); + assertThat(jdbc.queryForObject("SELECT status FROM fin_fee_source", String.class)).isEqualTo("AVAILABLE"); + } + + /** 只锁定相同系统、来源类型和费用号,其他客户的独立费用不会阻塞正常升版。 */ + @Test + void independentSourcesKeepSeparateCustomerIdentity() { + consume(event(701, 1, 10001, "ON_ACCOUNT", "CNY", "12.00", "1", "12.00")); + consume(event(702, 1, 10002, "ON_ACCOUNT", "CNY", "9.00", "1", "9.00")); + consume(event(701, 2, 10001, "ON_ACCOUNT", "CNY", "18.00", "1", "18.00")); + assertCounts(3, 3, 0); + assertThat(jdbc.queryForObject("SELECT customer_id FROM fin_fee_source WHERE source_fee_id=702", Long.class)) + .isEqualTo(10002L); + assertThat(jdbc.queryForObject("SELECT status FROM fin_fee_source WHERE source_fee_id=702", String.class)) + .isEqualTo("AVAILABLE"); + } + + /** 提前恢复幂等结果不放宽事件冲突校验,异载荷仍须拒绝。 */ + @Test + void changedPayloadCannotUseProcessedEventIdentity() { + var first = event(701, 1, 10001, "ON_ACCOUNT", "CNY", "12.00", "1", "12.00"); + consume(first); + var changed = event(701, 1, 10001, "ON_ACCOUNT", "CNY", "18.00", "1", "18.00"); + var conflict = new EventEnvelope(first.eventId(), changed.eventType(), changed.eventVersion(), changed.occurredAt(), + changed.tenantId(), changed.aggregateType(), changed.aggregateId(), changed.aggregateVersion(), null, + changed.traceId(), changed.correlationId(), null, changed.payload(), changed.payloadHash()); + reset(suppliers); + assertThatThrownBy(() -> consume(conflict)).isInstanceOfSatisfying(FinanceEventException.class, + error -> assertThat(error.code()).isEqualTo("FIN-EVT-002")); + verifyNoInteractions(suppliers); + assertCounts(1, 1, 0); + } + + /** 即使失败发生在费用插入之后,收件箱、费用及成本也必须一起回滚。 */ + @Test + void costWriteFailureRollsBackReceiptAndSource() { + jdbc.execute("ALTER TABLE fin_cost ADD CONSTRAINT test_cost_limit CHECK(cost_amount <= 10)"); + assertThatThrownBy(() -> consume(event(701, 1, 10001, "DIRECT_PAID", "CNY", "12.00", "1", "12.00"))) + .isInstanceOf(org.springframework.dao.DataIntegrityViolationException.class); + assertCounts(0, 0, 0); + } + + /** 以正式服务事务边界执行,Mapper 与遗留 JDBC 写入必须共进退。 */ + private DeliveryAck consume(EventEnvelope event) { + return transaction.execute(status -> service.consume(event)); + } + + /** 校验整笔接收事实数量,防止幂等通过但产生额外财务明细。 */ + private void assertCounts(int inbox, int sources, int costs) { + assertThat(jdbc.queryForObject("SELECT COUNT(*) FROM fin_inbox_event", Integer.class)).isEqualTo(inbox); + assertThat(jdbc.queryForObject("SELECT COUNT(*) FROM fin_fee_source", Integer.class)).isEqualTo(sources); + assertThat(jdbc.queryForObject("SELECT COUNT(*) FROM fin_cost", Integer.class)).isEqualTo(costs); + } + + /** 构造经过两个正式摘要计算的费用快照,金额错误用例不会被摘要错误提前拦截。 */ + private EventEnvelope event(long feeId, int version, long customer, String mode, String currency, + String original, String rate, String total) { + ObjectNode payload = json.createObjectNode(); + payload.putNull("action"); + payload.put("sourceSystem", "LASTLEG").put("sourceType", "OVERSEAS_WAREHOUSE_FEE") + .put("sourceFeeId", String.valueOf(feeId)).put("sourceVersion", version) + .put("customerId", String.valueOf(customer)).put("customerName", "验收客户") + .put("sourceNo", "ZY-RECEPTION").put("businessType", "WAREHOUSE_SERVICE") + .put("businessId", "801").put("businessNo", "ZY-RECEPTION"); + for (String field : new String[]{"orderId", "inboundId", "inboundCode", "containerId", "containerBookingId", "containerNo"}) + payload.putNull(field); + payload.put("warehouseId", "91").put("feeItemCode", "FEE_TYPE_003").put("feeItemName", "人工费") + .put("direction", "PAYABLE").put("supplierId", "71").put("supplierName", "供应商"); + payload.putNull("supplierCategory"); payload.put("settlementMode", mode); + if ("DIRECT_PAID".equals(mode)) { + payload.set("paidAt", json.valueToTree(Instant.parse("2026-10-27T01:00:00Z"))); + payload.put("paymentMethod", "BANK_TRANSFER").put("paymentReferenceNo", "TEST-RECEPTION"); + } else { + payload.putNull("paidAt"); payload.putNull("paymentMethod"); payload.putNull("paymentReferenceNo"); + } + payload.putArray("paymentVoucherFileIds"); + payload.put("businessDate", "2026-10-27").put("currency", "CNY").put("totalAmount", new BigDecimal(total)) + .put("originalCurrency", currency).put("originalAmount", new BigDecimal(original)) + .put("exchangeRate", new BigDecimal(rate)).put("exchangeRateMonth", "2026-10"); + for (String field : new String[]{"chargeableQuantity", "quantityUnit", "unitPrice", "periodStart", "periodEnd"}) + payload.putNull(field); + payload.put("reviewStatus", "APPROVED").put("publicationStatus", "PUBLISHED").put("sourceStatus", "ACTIVE"); + payload.putNull("sourceHash"); payload.putNull("walletSettlement"); + // 使用正式 DTO 规范化日期等类型,和生产者的序列化输入保持一致。 + payload = json.valueToTree(json.convertValue(payload, BusinessFeeSourceChangedV1.class)); + payload.put("sourceHash", EventPayloadHash.sha256(EventPayloadCanonicalizer.canonicalize(payload))); + payload.put("action", version == 1 ? "PUBLISHED" : "ADJUSTED"); + return new EventEnvelope(UUID.randomUUID().toString(), EventTypes.BUSINESS_FEE_SOURCE_CHANGED, 1, + Instant.parse("2026-10-27T01:00:00Z"), String.valueOf(customer), "BUSINESS_FEE", String.valueOf(feeId), version, + null, UUID.randomUUID().toString(), "TEST:FEE:" + feeId + ':' + version, null, payload, + EventPayloadHash.sha256(EventPayloadCanonicalizer.canonicalize(payload))); + } +} diff --git a/finance/src/test/java/com/baiyue/finance/CustomerAccountSummaryBehaviorTest.java b/finance/src/test/java/com/baiyue/finance/CustomerAccountSummaryBehaviorTest.java index 7e4737ea7..690edc56e 100644 --- a/finance/src/test/java/com/baiyue/finance/CustomerAccountSummaryBehaviorTest.java +++ b/finance/src/test/java/com/baiyue/finance/CustomerAccountSummaryBehaviorTest.java @@ -37,7 +37,7 @@ class CustomerAccountSummaryBehaviorTest { when(jdbc.query(anyString(), any(RowMapper.class), org.mockito.ArgumentMatchers.eq(9L))) .thenReturn(List.of()); - var result = new FinanceQueryService(jdbc, new ObjectMapper()).customerAccount(9L); + var result = new FinanceQueryService(jdbc, new ObjectMapper(), FinanceCustomerNamesTestSupport.names()).customerAccount(9L); assertThat(result.accountMode()).isEqualTo("MANUAL_RECEIVABLE_LEDGER"); var amountSql = org.mockito.ArgumentCaptor.forClass(String.class); @@ -60,7 +60,7 @@ class CustomerAccountSummaryBehaviorTest { ResultSet rs = summaryRow("300.00", "140.00", "160.00", "25.00", "260.00", "180.00", "80.00"); when(jdbc.queryForObject(anyString(), any(RowMapper.class), any(), any(), any(), any(), any(), any(), any(), any(), any(), any())).thenAnswer(invocation -> ((RowMapper) invocation.getArgument(1)).mapRow(rs, 0)); - FinanceQueryService service = new FinanceQueryService(jdbc, new ObjectMapper()); + FinanceQueryService service = new FinanceQueryService(jdbc, new ObjectMapper(), FinanceCustomerNamesTestSupport.names()); CustomerAccountSummary result = service.customerAccountSummary(9L); @@ -99,7 +99,7 @@ class CustomerAccountSummaryBehaviorTest { when(jdbc.queryForObject(anyString(), any(RowMapper.class), any(), any(), any(), any(), any(), any(), any(), any(), any(), any())).thenAnswer(invocation -> ((RowMapper) invocation.getArgument(1)).mapRow(rs, 0)); - CustomerAccountSummary result = new FinanceQueryService(jdbc, new ObjectMapper()).customerAccountSummary(77L); + CustomerAccountSummary result = new FinanceQueryService(jdbc, new ObjectMapper(), FinanceCustomerNamesTestSupport.names()).customerAccountSummary(77L); assertThat(result.totalReceivable()).isZero(); assertThat(result.receivedAmount()).isZero(); @@ -116,7 +116,7 @@ class CustomerAccountSummaryBehaviorTest { when(jdbc.queryForObject(anyString(), any(RowMapper.class), any(), any(), any(), any(), any(), any(), any(), any(), any(), any())) .thenThrow(new BadSqlGrammarException("customer summary", "SELECT ...", null)); - assertThatThrownBy(() -> new FinanceQueryService(jdbc, new ObjectMapper()).customerAccountSummary(88L)) + assertThatThrownBy(() -> new FinanceQueryService(jdbc, new ObjectMapper(), FinanceCustomerNamesTestSupport.names()).customerAccountSummary(88L)) .isInstanceOf(BadSqlGrammarException.class); } diff --git a/finance/src/test/java/com/baiyue/finance/FinanceCustomerNamesTest.java b/finance/src/test/java/com/baiyue/finance/FinanceCustomerNamesTest.java new file mode 100644 index 000000000..57cc7c4a4 --- /dev/null +++ b/finance/src/test/java/com/baiyue/finance/FinanceCustomerNamesTest.java @@ -0,0 +1,53 @@ +package com.baiyue.finance; + +import com.baiyue.common.core.api.ApiResponse; +import com.baiyue.common.core.api.PageResult; +import com.baiyue.finance.client.IamCustomerClient; +import com.baiyue.finance.service.FinanceCustomerNames; +import com.baiyue.finance.service.FinanceEventException; +import org.junit.jupiter.api.Test; +import java.util.List; +import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; + +class FinanceCustomerNamesTest { + private final IamCustomerClient iam = mock(IamCustomerClient.class); + private final FinanceCustomerNames names = new FinanceCustomerNames(iam, "internal-token"); + + @Test + void resolvesInternalLabelsAndMissingNamesOncePerCustomerWithoutReplacingValidSnapshots() { + respond(9, "当前公司名称"); + var resolver = names.resolver(); + assertThat(resolver.apply(9L, "客户9")).isEqualTo("当前公司名称"); + assertThat(resolver.apply(9L, "9")).isEqualTo("当前公司名称"); + assertThat(resolver.apply(9L, null)).isEqualTo("当前公司名称"); + assertThat(resolver.apply(9L, "原单公司名称")).isEqualTo("原单公司名称"); + verify(iam, times(1)).page("internal-token", 1, 1, null, null, 9L); + } + + @Test + void missingCustomerDisplaysDashButCannotCreateNewDocument() { + when(iam.page("internal-token", 1, 1, null, null, 9L)) + .thenReturn(ApiResponse.success(new PageResult<>(List.of(), 1, 1, 0, 0))); + assertThat(names.resolver().apply(9L, "客户9")).isEqualTo("—"); + assertThatThrownBy(() -> names.requiredSnapshot(9, "客户9")).isInstanceOf(FinanceEventException.class).hasMessageContaining("无法生成"); + } + + @Test + void rejectsMismatchedCustomerInsteadOfLeakingAnotherCompanyName() { + respond(10, "其他公司"); + assertThatThrownBy(() -> names.required(9)).isInstanceOf(FinanceEventException.class).hasMessageContaining("范围异常"); + } + + @Test + void dependencyFailureCannotFallBackToInternalIdOrSilentlyCreateDocument() { + when(iam.page("internal-token", 1, 1, null, null, 9L)).thenThrow(new IllegalStateException("offline")); + assertThatThrownBy(() -> names.required(9)).isInstanceOf(FinanceEventException.class).hasMessageContaining("暂不可用"); + assertThat(names.resolver().apply(9L, "已记录公司名称")).isEqualTo("已记录公司名称"); + } + + private void respond(long returnedId, String name) { + when(iam.page("internal-token", 1, 1, null, null, 9L)).thenReturn(ApiResponse.success( + new PageResult<>(List.of(new IamCustomerClient.Customer(returnedId, "TEST", name, "ACTIVE", null, null)), 1, 1, 1, 1))); + } +} diff --git a/finance/src/test/java/com/baiyue/finance/FinanceCustomerNamesTestSupport.java b/finance/src/test/java/com/baiyue/finance/FinanceCustomerNamesTestSupport.java new file mode 100644 index 000000000..18b6d5d8f --- /dev/null +++ b/finance/src/test/java/com/baiyue/finance/FinanceCustomerNamesTestSupport.java @@ -0,0 +1,22 @@ +package com.baiyue.finance; + +import com.baiyue.common.core.api.ApiResponse; +import com.baiyue.common.core.api.PageResult; +import com.baiyue.finance.client.IamCustomerClient; +import com.baiyue.finance.service.FinanceCustomerNames; +import java.util.List; +import static org.mockito.Mockito.mock; + +/** 财务服务单测使用真实名称解析器及隔离的 IAM 主档,不请求测试环境。 */ +public final class FinanceCustomerNamesTestSupport { + private FinanceCustomerNamesTestSupport() {} + + public static FinanceCustomerNames names() { + var iam = mock(IamCustomerClient.class, invocation -> { + if (!invocation.getMethod().getName().equals("page")) return org.mockito.Mockito.RETURNS_DEFAULTS.answer(invocation); + long id = invocation.getArgument(5); + return ApiResponse.success(new PageResult<>(List.of(new IamCustomerClient.Customer(id, "TEST", "测试客户公司", "ACTIVE", null, null)), 1, 1, 1, 1)); + }); + return new FinanceCustomerNames(iam, "internal-token"); + } +} diff --git a/finance/src/test/java/com/baiyue/finance/FinanceDirectPaidCostReadTest.java b/finance/src/test/java/com/baiyue/finance/FinanceDirectPaidCostReadTest.java new file mode 100644 index 000000000..53171d97c --- /dev/null +++ b/finance/src/test/java/com/baiyue/finance/FinanceDirectPaidCostReadTest.java @@ -0,0 +1,89 @@ +package com.baiyue.finance; + +import com.baiyue.finance.service.FinanceQueryService; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.datasource.DriverManagerDataSource; + +import java.util.UUID; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +/** 成本列表和详情须与直接已付事实一致,不能提示供应商费用仍待支付。 */ +class FinanceDirectPaidCostReadTest { + private JdbcTemplate jdbc; + private FinanceQueryService service; + + /** 使用隔离内存表验证正式读取路径,不访问共享测试环境。 */ + @BeforeEach + void setUp() { + jdbc = new JdbcTemplate(new DriverManagerDataSource( + "jdbc:h2:mem:cost-read-" + UUID.randomUUID() + ";MODE=MySQL;DB_CLOSE_DELAY=-1", "sa", "")); + jdbc.execute("CREATE TABLE fin_cost(id BIGINT PRIMARY KEY,tenant_id BIGINT,cost_no VARCHAR(128)," + + "cost_type VARCHAR(24),cost_origin VARCHAR(24),order_id BIGINT,inbound_id BIGINT,container_id BIGINT," + + "supplier_name_snapshot VARCHAR(128),fee_item_name VARCHAR(128),cost_amount DECIMAL(18,2)," + + "currency_code VARCHAR(3),status VARCHAR(24),created_at TIMESTAMP,version INT)"); + jdbc.execute("CREATE TABLE fin_cost_allocation(id BIGINT,tenant_id BIGINT,cost_id BIGINT,order_id BIGINT," + + "inbound_id BIGINT,volume_cbm DECIMAL(18,6),total_volume_cbm DECIMAL(18,6),allocation_rate DECIMAL(18,6)," + + "allocated_amount DECIMAL(18,2),rounding_adjustment BOOLEAN)"); + service = new FinanceQueryService(jdbc, new ObjectMapper(), FinanceCustomerNamesTestSupport.names()); + } + + /** 头程与海外直付均已付款,列表和详情不能再次显示未付余额。 */ + @ParameterizedTest + @ValueSource(strings = {"BUSINESS_DIRECT_PAID", "FIRSTLEG_DIRECT_PAID"}) + void confirmedDirectPaidCostIsFullyPaidInListAndDetail(String origin) { + insertCost(1, 11, "SOURCE_DIRECT_PAID", origin, "CONFIRMED"); + + var row = service.payableSettlements(11L).get(0); + assertThat(row.payableAmount()).isEqualByComparingTo("0.03"); + assertThat(row.paidAmount()).isEqualByComparingTo("0.03"); + assertThat(row.outstandingAmount()).isZero(); + assertThat(service.cost(11, 1).cost()).isEqualTo(row); + } + + /** 已作废的直付历史保留原金额,但不再作为有效已付或待付款展示。 */ + @Test + void voidedDirectPaidCostHasNoActivePaymentOrOutstandingAmount() { + insertCost(1, 11, "SOURCE_DIRECT_PAID", "BUSINESS_DIRECT_PAID", "VOIDED"); + + var row = service.payableSettlements(11L).get(0); + assertThat(row.payableAmount()).isEqualByComparingTo("0.03"); + assertThat(row.paidAmount()).isZero(); + assertThat(row.outstandingAmount()).isZero(); + assertThat(row.status()).isEqualTo("VOIDED"); + } + + /** 人工补充成本没有直接已付事实,不能被自动视为付款完成。 */ + @ParameterizedTest + @ValueSource(strings = {"SINGLE_ORDER", "CONTAINER_SHARED"}) + void manualCostKeepsItsExistingUnpaidAmounts(String type) { + insertCost(1, 11, type, "MANUAL", "CONFIRMED"); + + var row = service.payableSettlements(11L).get(0); + assertThat(row.paidAmount()).isZero(); + assertThat(row.outstandingAmount()).isEqualByComparingTo("0.03"); + } + + /** 金额回显修复不能让其他客户读到成本列表或详情。 */ + @Test + void costReadsKeepCustomerIsolation() { + insertCost(1, 11, "SOURCE_DIRECT_PAID", "BUSINESS_DIRECT_PAID", "CONFIRMED"); + + assertThat(service.payableSettlements(12L)).isEmpty(); + assertThatThrownBy(() -> service.cost(12, 1)).hasMessageContaining("成本不存在或不属于客户"); + } + + /** 仅构造测试所需的成本事实,金额对应本轮发现问题的小额样本。 */ + private void insertCost(long id, long customer, String type, String origin, String status) { + jdbc.update("INSERT INTO fin_cost(id,tenant_id,cost_no,cost_type,cost_origin,order_id,container_id," + + "supplier_name_snapshot,fee_item_name,cost_amount,currency_code,status,created_at,version) " + + "VALUES(?,?,?,?,?,101,201,'验收供应商','人工费',0.03,'CNY',?,CURRENT_TIMESTAMP,0)", + id, customer, "COST-READ-TEST", type, origin, status); + } +} diff --git a/finance/src/test/java/com/baiyue/finance/FinanceFeePoolDirectionBehaviorTest.java b/finance/src/test/java/com/baiyue/finance/FinanceFeePoolDirectionBehaviorTest.java index 8806fff05..953de366f 100644 --- a/finance/src/test/java/com/baiyue/finance/FinanceFeePoolDirectionBehaviorTest.java +++ b/finance/src/test/java/com/baiyue/finance/FinanceFeePoolDirectionBehaviorTest.java @@ -18,7 +18,7 @@ class FinanceFeePoolDirectionBehaviorTest { @Test void receivablePoolNeverContainsPayableSource() { DirectionJdbc jdbc = new DirectionJdbc(); - FinanceQueryService service = new FinanceQueryService(jdbc, new ObjectMapper()); + FinanceQueryService service = new FinanceQueryService(jdbc, new ObjectMapper(), FinanceCustomerNamesTestSupport.names()); List result = service.feePool(7L, "RECEIVABLE"); @@ -30,7 +30,7 @@ class FinanceFeePoolDirectionBehaviorTest { @Test void payablePoolNeverContainsReceivableSource() { DirectionJdbc jdbc = new DirectionJdbc(); - FinanceQueryService service = new FinanceQueryService(jdbc, new ObjectMapper()); + FinanceQueryService service = new FinanceQueryService(jdbc, new ObjectMapper(), FinanceCustomerNamesTestSupport.names()); List result = service.feePool(7L, "PAYABLE"); @@ -42,7 +42,7 @@ class FinanceFeePoolDirectionBehaviorTest { @Test void payablePoolOnlyQueriesApprovedUnsettledSourcesForTheSelectedSupplier() { DirectionJdbc jdbc = new DirectionJdbc(); - FinanceQueryService service = new FinanceQueryService(jdbc, new ObjectMapper()); + FinanceQueryService service = new FinanceQueryService(jdbc, new ObjectMapper(), FinanceCustomerNamesTestSupport.names()); service.feePool(7L, "PAYABLE", 81L); diff --git a/finance/src/test/java/com/baiyue/finance/FinanceFeeSourceReadMapperTest.java b/finance/src/test/java/com/baiyue/finance/FinanceFeeSourceReadMapperTest.java index c48e46ab7..bbde7617a 100644 --- a/finance/src/test/java/com/baiyue/finance/FinanceFeeSourceReadMapperTest.java +++ b/finance/src/test/java/com/baiyue/finance/FinanceFeeSourceReadMapperTest.java @@ -15,6 +15,7 @@ import org.springframework.jdbc.datasource.DriverManagerDataSource; import java.math.BigDecimal; import java.time.LocalDate; import java.util.UUID; +import java.util.List; import static org.assertj.core.api.Assertions.assertThat; @@ -30,8 +31,11 @@ public class FinanceFeeSourceReadMapperTest { try (var connection = data.getConnection()) { jdbc.execute("CREATE ALIAS JSON_EXTRACT FOR 'com.baiyue.finance.FinanceFeeSourceReadMapperTest.jsonExtract'"); jdbc.execute("CREATE ALIAS JSON_UNQUOTE FOR 'com.baiyue.finance.FinanceFeeSourceReadMapperTest.jsonUnquote'"); - jdbc.execute("CREATE TABLE fin_fee_source(id BIGINT PRIMARY KEY,source_system VARCHAR(32),source_type VARCHAR(32),source_fee_id BIGINT,source_fee_version INT,customer_id BIGINT,direction VARCHAR(16),source_no VARCHAR(64),fee_item_name VARCHAR(100),amount DECIMAL(20,6),currency_code VARCHAR(3),review_status VARCHAR(16),status VARCHAR(16),source_snapshot VARCHAR(2000),order_id BIGINT,inbound_id BIGINT,container_id BIGINT,created_at TIMESTAMP)"); - jdbc.execute("CREATE TABLE fin_bill_line(source_id BIGINT,source_version INT)"); + jdbc.execute("CREATE TABLE fin_fee_source(id BIGINT PRIMARY KEY,source_system VARCHAR(32),source_type VARCHAR(32),source_fee_id BIGINT,source_fee_version INT,customer_id BIGINT,direction VARCHAR(16),source_no VARCHAR(64),fee_item_name VARCHAR(100),amount DECIMAL(20,6),currency_code VARCHAR(3),review_status VARCHAR(16),status VARCHAR(16),source_snapshot VARCHAR(2000),order_id BIGINT,inbound_id BIGINT,container_id BIGINT,created_at TIMESTAMP,settlement_mode VARCHAR(32),paid_at TIMESTAMP,operation_version INT DEFAULT 0,review_reason VARCHAR(128),source_hash VARCHAR(64))"); + jdbc.execute("CREATE TABLE fin_bill_line(source_id BIGINT,source_version INT,tenant_id BIGINT,bill_id BIGINT)"); + jdbc.execute("CREATE TABLE fin_bill(id BIGINT PRIMARY KEY,tenant_id BIGINT,status VARCHAR(24))"); + jdbc.execute("CREATE TABLE fin_settlement(id BIGINT PRIMARY KEY,tenant_id BIGINT,direction VARCHAR(16),status VARCHAR(24),payment_status VARCHAR(24))"); + jdbc.execute("CREATE TABLE fin_settlement_line(settlement_id BIGINT,tenant_id BIGINT,fee_source_id BIGINT)"); jdbc.execute("CREATE TABLE fin_audit_log(id BIGINT PRIMARY KEY,tenant_id BIGINT,resource_type VARCHAR(32),resource_id BIGINT,action VARCHAR(64),operator_name_snapshot VARCHAR(64),reason VARCHAR(100),before_data VARCHAR(1000),after_data VARCHAR(1000),created_at TIMESTAMP)"); insert(jdbc, 1, "FIRSTLEG", "INBOUND_FEE", 700, 1, 9, direction, "SUPERSEDED", "100.00"); insert(jdbc, 2, "FIRSTLEG", "INBOUND_FEE", 700, 2, 9, direction, "AVAILABLE", "120.00"); @@ -103,10 +107,44 @@ public class FinanceFeeSourceReadMapperTest { assertThat(repository.logs(9, 1)).extracting(log -> log.action()).containsExactly( "FEE_SOURCE_PUBLISHED", "FEE_SOURCE_REVIEW_APPROVE"); assertThat(jdbc.queryForObject("SELECT COUNT(*) FROM fin_fee_source", Long.class)).isEqualTo(8); - jdbc.update("INSERT INTO fin_bill_line VALUES(2,2)"); - filters.put("status", "BILLED"); filters.put("statementGenerated", true); - assertThat(repository.count(filters)).isEqualTo(1); - assertThat(repository.list(filters)).extracting(row -> row.feeSourceId()).containsExactly(2L); + if ("RECEIVABLE".equals(direction)) { + jdbc.update("INSERT INTO fin_bill VALUES(91,9,'ISSUED')"); + jdbc.update("INSERT INTO fin_bill_line VALUES(2,2,9,91)"); + filters.put("status", "BILLED"); filters.put("statementGenerated", true); + session.clearCache(); + assertThat(repository.count(filters)).isEqualTo(1); + assertThat(repository.list(filters)).extracting(row -> row.feeSourceId()).containsExactly(2L); + assertThat(repository.detail(2).sourceStatus()).isEqualTo("BILLED"); + jdbc.update("UPDATE fin_bill SET status='VOIDED' WHERE id=91"); + session.clearCache(); + assertThat(repository.detail(2).statementGenerated()).isFalse(); + assertThat(repository.detail(2).sourceStatus()).isEqualTo("AVAILABLE"); + } else { + jdbc.update("INSERT INTO fin_settlement VALUES(91,9,'PAYABLE','ISSUED','UNPAID')"); + jdbc.update("INSERT INTO fin_settlement_line VALUES(91,9,2)"); + for (String payment : List.of("UNPAID", "PARTIALLY_PAID", "PAID")) { + jdbc.update("UPDATE fin_settlement SET payment_status=? WHERE id=91", payment); + String expected = "UNPAID".equals(payment) ? "PAYABLE_BILLED" : payment; + filters.put("status", expected); + assertThat(repository.count(filters)).isEqualTo(1); + assertThat(repository.list(filters)).singleElement().satisfies(row -> { + assertThat(row.feeSourceId()).isEqualTo(2); + assertThat(row.sourceStatus()).isEqualTo(expected); + assertThat(row.statementGenerated()).isFalse(); + assertThat(row.amount()).isEqualByComparingTo("120"); + }); + assertThat(repository.detail(2).sourceStatus()).isEqualTo(expected); + } + jdbc.update("UPDATE fin_settlement SET status='VOIDED' WHERE id=91"); + assertThat(repository.detail(2).sourceStatus()).isEqualTo("AVAILABLE"); + jdbc.update("UPDATE fin_settlement SET status='PAID',tenant_id=10 WHERE id=91"); + jdbc.update("UPDATE fin_settlement_line SET tenant_id=10 WHERE settlement_id=91"); + assertThat(repository.detail(2).sourceStatus()).isEqualTo("AVAILABLE"); + jdbc.update("UPDATE fin_fee_source SET settlement_mode='DIRECT_PAID',paid_at='2026-10-21 10:00:00' WHERE id=2"); + assertThat(repository.detail(2).sourceStatus()).isEqualTo("PAID"); + jdbc.update("UPDATE fin_fee_source SET status='VOIDED' WHERE id=2"); + assertThat(repository.detail(2).sourceStatus()).isEqualTo("VOIDED"); + } // 财务内部费用原地改金额,其历史只取真实审计,不能伪造旧时间的同步金额。 insert(jdbc, 9, "FINANCE", "MANUAL", 700, 1, 9, direction, "AVAILABLE", "120.00"); diff --git a/finance/src/test/java/com/baiyue/finance/FinancePhase1OperationBehaviorTest.java b/finance/src/test/java/com/baiyue/finance/FinancePhase1OperationBehaviorTest.java index fa4cdff2d..43187b5eb 100644 --- a/finance/src/test/java/com/baiyue/finance/FinancePhase1OperationBehaviorTest.java +++ b/finance/src/test/java/com/baiyue/finance/FinancePhase1OperationBehaviorTest.java @@ -189,8 +189,8 @@ class FinancePhase1OperationBehaviorTest { /** 保留费用审核状态读取。 */ @Test void approvedFeeCannotBeEditedAsDraft() throws Exception { - JdbcTemplate jdbc = feeSourceJdbc("REJECTED", false, 2); - FinancePhase1OperationService service = new FinancePhase1OperationService(jdbc, objectMapper(), null, "", mock(FinanceFeeSourceReadRepository.class)); + JdbcTemplate jdbc = mock(JdbcTemplate.class); + FinancePhase1OperationService service = new FinancePhase1OperationService(jdbc, objectMapper(), null, "", feeSources("REJECTED", false, 2)); assertThat(service.getFinanceFeeSource(88L).reviewStatus()).isEqualTo("REJECTED"); } @@ -199,7 +199,7 @@ class FinancePhase1OperationBehaviorTest { @Test void feeSourceDetailUsesFixedDtoAndRealCustomerScope() throws Exception { FinancePhase1OperationService service = new FinancePhase1OperationService( - feeSourceJdbc("APPROVED", true, 2), objectMapper(), null, "", mock(FinanceFeeSourceReadRepository.class)); + mock(JdbcTemplate.class), objectMapper(), null, "", feeSources("APPROVED", true, 2)); FeeSourceDetail detail = service.getFinanceFeeSource(88L); @@ -212,8 +212,8 @@ class FinancePhase1OperationBehaviorTest { /** 业务来源费用必须回到所属系统调整。 */ @Test void firstlegFeeEditAndAdjustAreBlockedInsteadOfMutatingFinanceCopy() throws Exception { - JdbcTemplate jdbc = feeSourceJdbc("REJECTED", false, 1); - FinancePhase1OperationService service = new FinancePhase1OperationService(jdbc, objectMapper(), null, "", mock(FinanceFeeSourceReadRepository.class)); + JdbcTemplate jdbc = mock(JdbcTemplate.class); + FinancePhase1OperationService service = new FinancePhase1OperationService(jdbc, objectMapper(), null, "", feeSources("REJECTED", false, 1)); var request = new FinancePhase1Dtos.FeeSourceEditRequest( new BigDecimal("120.00"), "更正金额", 1); @@ -228,7 +228,7 @@ class FinancePhase1OperationBehaviorTest { /** 头程作废等待权威来源事件,不能直接改财务副本。 */ @Test void voidingNyexx2608180003DelegatesToFirstlegAndWaitsForEvent() throws Exception { - JdbcTemplate jdbc = feeSourceJdbc("APPROVED", false, 2); + JdbcTemplate jdbc = mock(JdbcTemplate.class); when(jdbc.queryForList(startsWith("SELECT request_hash"), eq(9L), eq(71L), eq("FEE_SOURCE_VOID"), eq("void-key"))).thenReturn(List.of()); FirstlegFeeSourceClient firstleg = mock(FirstlegFeeSourceClient.class); @@ -236,7 +236,7 @@ class FinancePhase1OperationBehaviorTest { .thenReturn(ApiResponse.success(new FirstlegFeeSourceClient.CommandResult( "INBOUND_FEE", 700L, 3, "VOIDED", false))); FinancePhase1OperationService service = new FinancePhase1OperationService( - jdbc, objectMapper(), firstleg, "internal-token", mock(FinanceFeeSourceReadRepository.class)); + jdbc, objectMapper(), firstleg, "internal-token", feeSources("APPROVED", false, 2)); FeeSourceDetail result = service.voidFinanceFeeSource(88L, 71L, "finance", "void-key", new FinancePhase1Dtos.FeeSourceActionRequest("测试作废", 2)); @@ -262,13 +262,9 @@ class FinancePhase1OperationBehaviorTest { void reviewUsesExpectedVersionAndReturnsUpdatedDetail() throws Exception { JdbcTemplate jdbc = mock(JdbcTemplate.class); AtomicInteger reads = new AtomicInteger(); - when(jdbc.query(startsWith("SELECT * FROM fin_fee_source"), any(RowMapper.class), (Object[]) any())).thenAnswer(invocation -> { - @SuppressWarnings("unchecked") RowMapper mapper = invocation.getArgument(1); - String review = reads.getAndIncrement() == 0 ? "PENDING" : "APPROVED"; - int version = "PENDING".equals(review) ? 0 : 1; - return List.of(mapper.mapRow(resultSet(review, false, version), 0)); - }); - when(jdbc.queryForObject(startsWith("SELECT COUNT(*)>0"), eq(Boolean.class), eq(88L), eq(3))).thenReturn(false); + var sources = mock(FinanceFeeSourceReadRepository.class); + when(sources.detail(88L)).thenAnswer(invocation -> reads.getAndIncrement() == 0 + ? feeDetail("PENDING", false, 0) : feeDetail("APPROVED", false, 1)); when(jdbc.queryForList(startsWith("SELECT request_hash"), eq(9L), eq(71L), eq("FEE_SOURCE_REVIEW"), eq("review-key"))).thenReturn(List.of()); when(jdbc.update(startsWith("INSERT INTO fin_idempotency_record"), eq(9L), eq(71L), @@ -280,7 +276,7 @@ class FinancePhase1OperationBehaviorTest { eq(71L), eq("操作人 71"), any(), any())).thenReturn(1); when(jdbc.update(startsWith("UPDATE fin_idempotency_record SET status='SUCCEEDED'"), eq(88L), eq(9L), eq(71L), eq("FEE_SOURCE_REVIEW"), eq("review-key"))).thenReturn(1); - FinancePhase1OperationService service = new FinancePhase1OperationService(jdbc, objectMapper(), null, "", mock(FinanceFeeSourceReadRepository.class)); + FinancePhase1OperationService service = new FinancePhase1OperationService(jdbc, objectMapper(), null, "", sources); FeeSourceDetail result = service.reviewFinanceFeeSource(88L, 71L, "review-key", new FinancePhase1Dtos.ReviewRequest("APPROVE", "核对无误", 0)); @@ -332,45 +328,30 @@ class FinancePhase1OperationBehaviorTest { .when(service).getFinanceFeeSource(88L); assertThatThrownBy(() -> service.editFinanceFeeSource(88, 71, "frozen", request, true)) .isInstanceOf(FinanceEventException.class).hasMessageContaining("已生成单据"); + for (String occupied : List.of("PAYABLE_BILLED", "PARTIALLY_PAID", "PAID")) { + doReturn(objectMapper().treeToValue(beforeJson.deepCopy().put("sourceStatus", occupied), FeeSourceDetail.class)) + .when(service).getFinanceFeeSource(88L); + assertThatThrownBy(() -> service.editFinanceFeeSource(88, 71, "occupied-" + occupied, request, true)) + .isInstanceOf(FinanceEventException.class).hasMessageContaining("费用已被占用、结清或作废"); + } } - /** 准备指定来源版本的费用查询,不再模拟已迁入 Mapper 的审计查询。 */ - private static JdbcTemplate feeSourceJdbc(String reviewStatus, boolean included, int version) throws Exception { - JdbcTemplate jdbc = mock(JdbcTemplate.class); - when(jdbc.query(startsWith("SELECT * FROM fin_fee_source"), any(RowMapper.class), (Object[]) any())).thenAnswer(invocation -> { - @SuppressWarnings("unchecked") RowMapper mapper = invocation.getArgument(1); - return List.of(mapper.mapRow(resultSet(reviewStatus, included, version), 0)); - }); - when(jdbc.queryForObject(startsWith("SELECT COUNT(*)>0"), eq(Boolean.class), eq(88L), eq(3))).thenReturn(false); - lenient().when(jdbc.queryForList(startsWith("SELECT request_hash"), eq(9L), eq(71L), - eq("FEE_SOURCE_SETTLEMENT"), eq("include-key"))).thenReturn(List.of()); - return jdbc; + /** 服务行为测试由 Repository 返回明确快照;正式 SQL 与 DTO 映射由 Mapper 数据库测试覆盖。 */ + private static FinanceFeeSourceReadRepository feeSources(String review, boolean generated, int version) throws Exception { + var repository = mock(FinanceFeeSourceReadRepository.class); + when(repository.detail(88L)).thenReturn(feeDetail(review, generated, version)); + return repository; } - private static ResultSet resultSet(String reviewStatus, boolean included, int version) throws Exception { - ResultSet rs = mock(ResultSet.class); - when(rs.getLong("id")).thenReturn(88L); - when(rs.getLong("source_fee_id")).thenReturn(700L); - when(rs.getLong("customer_id")).thenReturn(9L); - when(rs.getLong("order_id")).thenReturn(90L); - when(rs.getLong("inbound_id")).thenReturn(100L); - when(rs.getLong("container_id")).thenReturn(200L); - when(rs.wasNull()).thenReturn(false, false, true, false, false, true); - when(rs.getInt("source_fee_version")).thenReturn(3); - when(rs.getString("source_system")).thenReturn("FIRSTLEG"); - when(rs.getString("source_no")).thenReturn("IN-100"); - when(rs.getString("source_type")).thenReturn("INBOUND_FEE"); - when(rs.getString("direction")).thenReturn("RECEIVABLE"); - when(rs.getString("fee_item_name")).thenReturn("头程运费"); - when(rs.getString("currency_code")).thenReturn("CNY"); - when(rs.getString("status")).thenReturn("AVAILABLE"); - when(rs.getString("source_hash")).thenReturn("a".repeat(64)); - when(rs.getString("review_status")).thenReturn(reviewStatus); - when(rs.getString("review_reason")).thenReturn(null); - when(rs.getInt("operation_version")).thenReturn(version); - when(rs.getBigDecimal("amount")).thenReturn(new BigDecimal("100.00")); - when(rs.getString("source_snapshot")).thenReturn("{\"customerName\":\"客户9\",\"inboundCode\":\"NYEXX2608180003\",\"businessDate\":\"2026-08-11\",\"chargeableQuantity\":\"10\",\"reviewStatus\":\"" + reviewStatus + "\",\"publicationStatus\":\"PUBLISHED\",\"operationVersion\":" + version + "}"); - return rs; + private static FeeSourceDetail feeDetail(String review, boolean generated, int version) throws Exception { + var node = objectMapper().createObjectNode().put("feeSourceId",88).put("sourceSystem","FIRSTLEG") + .put("sourceType","INBOUND_FEE").put("sourceFeeId",700).put("sourceVersion",3).put("customerId",9) + .put("direction","RECEIVABLE").put("orderId",90).put("inboundId",100).put("containerId",200) + .put("inboundCode","NYEXX2608180003").put("sourceBusinessId","IN-100").put("feeItemName","头程运费") + .put("amount",100).put("currency","CNY").put("operationVersion",version).put("reviewStatus",review) + .put("sourceStatus",generated ? "BILLED" : "AVAILABLE").put("statementGenerated",generated) + .put("sourceHash","a".repeat(64)).put("businessDate","2026-08-11"); + return objectMapper().treeToValue(node,FeeSourceDetail.class); } private static ObjectMapper objectMapper() { diff --git a/finance/src/test/java/com/baiyue/finance/FinancePlatformOptionalCustomerQueryTest.java b/finance/src/test/java/com/baiyue/finance/FinancePlatformOptionalCustomerQueryTest.java index c54a04c07..5e5efc42d 100644 --- a/finance/src/test/java/com/baiyue/finance/FinancePlatformOptionalCustomerQueryTest.java +++ b/finance/src/test/java/com/baiyue/finance/FinancePlatformOptionalCustomerQueryTest.java @@ -16,7 +16,7 @@ class FinancePlatformOptionalCustomerQueryTest { @Test void allCustomerQueriesOmitCustomerPredicateAndArguments() { RecordingJdbc jdbc = new RecordingJdbc(); - FinanceQueryService service = new FinanceQueryService(jdbc, new ObjectMapper()); + FinanceQueryService service = new FinanceQueryService(jdbc, new ObjectMapper(), FinanceCustomerNamesTestSupport.names()); service.feePool(null, null); service.statements(null, false); @@ -33,7 +33,7 @@ class FinancePlatformOptionalCustomerQueryTest { @Test void customerFilterAddsPredicateAndRealCustomerArgument() { RecordingJdbc jdbc = new RecordingJdbc(); - FinanceQueryService service = new FinanceQueryService(jdbc, new ObjectMapper()); + FinanceQueryService service = new FinanceQueryService(jdbc, new ObjectMapper(), FinanceCustomerNamesTestSupport.names()); service.feePool(31L, null); service.statements(31L, false); @@ -49,7 +49,7 @@ class FinancePlatformOptionalCustomerQueryTest { @Test void optionalCustomerNeverTreatsZeroAsAllCustomers() { - FinanceQueryService service = new FinanceQueryService(new RecordingJdbc(), new ObjectMapper()); + FinanceQueryService service = new FinanceQueryService(new RecordingJdbc(), new ObjectMapper(), FinanceCustomerNamesTestSupport.names()); assertThatThrownBy(() -> service.statements(0L, false)).hasMessageContaining("客户范围无效"); assertThatThrownBy(() -> service.reconciliationStatements(0L)).hasMessageContaining("客户范围无效"); diff --git a/finance/src/test/java/com/baiyue/finance/FinanceReconciliationSchemaContractTest.java b/finance/src/test/java/com/baiyue/finance/FinanceReconciliationSchemaContractTest.java new file mode 100644 index 000000000..e23379fa8 --- /dev/null +++ b/finance/src/test/java/com/baiyue/finance/FinanceReconciliationSchemaContractTest.java @@ -0,0 +1,66 @@ +package com.baiyue.finance; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import org.springframework.dao.DuplicateKeyException; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.datasource.DriverManagerDataSource; + +import java.math.BigDecimal; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.UUID; +import java.util.regex.Pattern; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class FinanceReconciliationSchemaContractTest { + + /** 执行真实明细表约束,复现两笔费用作废后重新出单,保留原单快照且单内不能重复。 */ + @ParameterizedTest + @ValueSource(booleans = {false, true}) + void voidedStatementKeepsHistoryWhileItsTwoSourcesCanEnterANewStatement(boolean unified) throws Exception { + JdbcTemplate jdbc = createSchema(unified); + jdbc.execute("CREATE TABLE fin_bill(id BIGINT PRIMARY KEY,tenant_id BIGINT NOT NULL,status VARCHAR(32) NOT NULL)"); + jdbc.update("INSERT INTO fin_bill VALUES(501,9,'ISSUED'),(502,9,'DRAFT')"); + insertLine(jdbc, 1, 501, 88); + insertLine(jdbc, 2, 501, 89); + jdbc.update("UPDATE fin_bill SET status='VOIDED' WHERE id=501"); + + insertLine(jdbc, 3, 502, 88); + insertLine(jdbc, 4, 502, 89); + + assertThat(jdbc.queryForObject("SELECT COUNT(*) FROM fin_bill_line WHERE bill_id=501", Integer.class)).isEqualTo(2); + assertThat(jdbc.queryForObject("SELECT COUNT(*) FROM fin_bill_line WHERE bill_id=502", Integer.class)).isEqualTo(2); + assertThat(jdbc.queryForObject("SELECT status FROM fin_bill WHERE id=501", String.class)).isEqualTo("VOIDED"); + assertThat(jdbc.queryForObject("SELECT SUM(l.amount) FROM fin_bill_line l JOIN fin_bill b ON b.id=l.bill_id AND b.tenant_id=l.tenant_id WHERE b.status<>'VOIDED'", BigDecimal.class)) + .isEqualByComparingTo("200.00"); + assertThatThrownBy(() -> insertLine(jdbc, 5, 502, 88)).isInstanceOf(DuplicateKeyException.class); + assertThat(jdbc.queryForObject("SELECT COUNT(*) FROM fin_bill_line", Integer.class)).isEqualTo(4); + } + + /** 从模块或统一空库入口读取真实建表语句,仅移除 H2 不支持的 MySQL 存储语法。 */ + private static JdbcTemplate createSchema(boolean unified) throws Exception { + Path unifiedPath = Path.of("deploy", "sql", "头程数据库统一增量-MySQL8.sql"); + if (!Files.exists(unifiedPath)) unifiedPath = Path.of("..", "deploy", "sql", "头程数据库统一增量-MySQL8.sql"); + String sql = Files.readString(unified ? unifiedPath : Path.of("src/main/resources/db/finance.sql"), StandardCharsets.UTF_8); + var table = Pattern.compile("CREATE TABLE (?:IF NOT EXISTS )?fin_bill_line \\(.*?\\) ENGINE=[^;]*;", Pattern.DOTALL).matcher(sql); + assertThat(table.find()).isTrue(); + String ddl = table.group().replaceFirst("\\) ENGINE=[^;]*;", ");") + .replaceAll("(?i) COMMENT '(?:[^']|'')*'", "") + .replaceAll("(?i)\\bUNSIGNED\\b|_utf8mb4", "") + .replaceAll("(?m)^\\s*KEY [^\\r\\n]+\\r?\\n", "") + .replaceAll("UNIQUE KEY (\\w+)", "CONSTRAINT $1 UNIQUE"); + var jdbc = new JdbcTemplate(new DriverManagerDataSource("jdbc:h2:mem:" + UUID.randomUUID() + ";MODE=MySQL;DB_CLOSE_DELAY=-1")); + jdbc.execute(ddl); + return jdbc; + } + + /** 写入完整合成明细,仅改变本测试关注的单据和费用来源身份。 */ + private static void insertLine(JdbcTemplate jdbc, long id, long billId, long sourceId) { + jdbc.update("INSERT INTO fin_bill_line(id,tenant_id,bill_id,source_type,source_id,source_version,fee_item_code,fee_item_name,amount,currency_code) VALUES(?,9,?,'FIRSTLEG_FEE',?,1,'SYNTHETIC','合成测试费用',100,'CNY')", + id, billId, sourceId); + } +} diff --git a/finance/src/test/java/com/baiyue/finance/FinanceSettlementBehaviorTest.java b/finance/src/test/java/com/baiyue/finance/FinanceSettlementBehaviorTest.java index e5923f6ac..41df1fba1 100644 --- a/finance/src/test/java/com/baiyue/finance/FinanceSettlementBehaviorTest.java +++ b/finance/src/test/java/com/baiyue/finance/FinanceSettlementBehaviorTest.java @@ -20,6 +20,8 @@ import com.baiyue.finance.service.FinanceSupplierValidator; import com.baiyue.finance.service.FinanceVoucherValidator; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; @@ -100,6 +102,7 @@ class FinanceSettlementBehaviorTest { assertThat(all).singleElement().extracting(SettlementSummary::settlementId).isEqualTo(88L); assertThat(scoped).singleElement().extracting(SettlementSummary::customerId).isEqualTo(10L); + assertThat(scoped).singleElement().extracting(SettlementSummary::customerName).isEqualTo("测试客户公司"); ArgumentCaptor allSql = ArgumentCaptor.forClass(String.class); ArgumentCaptor scopedSql = ArgumentCaptor.forClass(String.class); verify(jdbc).queryForList(allSql.capture(), eq("PAYABLE")); @@ -221,6 +224,143 @@ class FinanceSettlementBehaviorTest { verify(staleJdbc, never()).update(startsWith("UPDATE fin_settlement SET status="), (Object[]) any()); } + /** 作废应付的费用已被另一张有效单占用时,重开必须拒绝,不能形成双重应付。 */ + @Test + void reopeningCannotReclaimASourceUsedByAnotherSettlement() { + Map row = settlementRow("PAYABLE", "VOIDED", BigDecimal.ZERO, + new BigDecimal("100.00"), 3); + JdbcTemplate jdbc = creationJdbc(row); + when(jdbc.queryForList(startsWith("SELECT id,tenant_id,total_amount"), eq(88L))) + .thenReturn(List.of(row)); + var source = payableSource(10L, 20L); + source.put("fee_source_id", 31L); + source.put("line_amount", new BigDecimal("100.00")); + source.put("line_currency", "CNY"); + org.mockito.Mockito.lenient().when(jdbc.queryForList(startsWith("SELECT sl.fee_source_id"), eq(10L), eq(88L))) + .thenReturn(List.of(source)); + org.mockito.Mockito.lenient().when(jdbc.queryForList(startsWith("SELECT s.id FROM fin_settlement_line"), eq(10L), eq(31L), eq(88L))) + .thenReturn(List.of(Map.of("id", 99L))); + FinanceSettlementService service = service(jdbc, org.mockito.Mockito.mock(FinanceFileClient.class)); + + assertCode(() -> service.actionSettlement(88L, 7L, "reopen-used", + new SettlementActionRequest("REOPEN", "验证来源占用", 3)), "FIN_409"); + verify(jdbc, never()).update(startsWith("UPDATE fin_settlement SET status="), (Object[]) any()); + } + + /** 未变化且无其他占用的应付原单可以恢复,继续保留原金额与修订审计。 */ + @Test + void unchangedPayableCanReopen() { + JdbcTemplate jdbc = reopenJdbc("PAYABLE"); + FinanceSettlementService service = service(jdbc, org.mockito.Mockito.mock(FinanceFileClient.class)); + assertThat(service.actionSettlement(88L, 7L, "reopen-ok", + new SettlementActionRequest("REOPEN", "恢复有效原单", 3)).summary().status()).isEqualTo("ISSUED"); + verify(jdbc).update(startsWith("UPDATE fin_settlement SET status="), eq("ISSUED"), eq(7L), eq(88L), eq(10L), eq(3)); + verify(jdbc).update(startsWith("INSERT INTO fin_audit_log"), eq(10L), eq("FIN_SETTLEMENT"), eq(88L), + eq("REOPEN"), eq("恢复有效原单"), any(), any(), eq(7L), any(), any(), any()); + } + + /** 原应付的失效、金额和归属变化均不能借重开恢复旧快照。 */ + @ParameterizedTest + @ValueSource(strings = {"VOIDED", "SUPERSEDED", "PENDING", "DIRECT_PAID", "CROSS_CUSTOMER", "MISSING", "AMOUNT_CHANGED", "SUPPLIER_CHANGED", "EMPTY"}) + void reopeningRejectsChangedPayableSources(String condition) { + JdbcTemplate jdbc = reopenJdbc("PAYABLE"); + Map source = reopenSource("PAYABLE"); + switch (condition) { + case "VOIDED", "SUPERSEDED" -> source.put("status", condition); + case "PENDING" -> source.put("review_status", "PENDING"); + case "DIRECT_PAID" -> source.put("settlement_mode", "DIRECT_PAID"); + case "CROSS_CUSTOMER" -> source.put("customer_id", 11L); + case "MISSING" -> source.remove("id"); + case "AMOUNT_CHANGED" -> source.put("amount", new BigDecimal("101.00")); + case "SUPPLIER_CHANGED" -> source.put("source_snapshot", "{\"supplierId\":21}"); + default -> { } + } + when(jdbc.queryForList(startsWith("SELECT sl.fee_source_id"), eq(10L), eq(88L))) + .thenReturn("EMPTY".equals(condition) ? List.of() : List.of(source)); + assertCode(() -> service(jdbc, org.mockito.Mockito.mock(FinanceFileClient.class)).actionSettlement(88L, 7L, + "reopen-" + condition, new SettlementActionRequest("REOPEN", "核对原始费用", 3)), "FIN_409"); + verify(jdbc, never()).update(startsWith("UPDATE fin_settlement SET status="), (Object[]) any()); + } + + /** 应收原账单与明细未变化且仍已出账时,允许恢复原单。 */ + @Test + void unchangedReceivableCanReopen() { + JdbcTemplate jdbc = reopenJdbc("RECEIVABLE"); + assertThat(service(jdbc, org.mockito.Mockito.mock(FinanceFileClient.class)).actionSettlement(88L, 7L, + "reopen-ar", new SettlementActionRequest("REOPEN", "恢复原应收", 3)).summary().status()).isEqualTo("ISSUED"); + } + + /** 其他应收占用、对账单作废或调整、已有收款均拒绝恢复旧应收。 */ + @ParameterizedTest + @ValueSource(strings = {"OCCUPIED", "VOIDED_BILL", "ADJUSTED_TOTAL", "ADJUSTED_LINES", "MISSING_BILL", "RECEIPT", "WRONG_SOURCE", "EMPTY"}) + void reopeningRejectsChangedReceivableBasis(String condition) { + JdbcTemplate jdbc = reopenJdbc("RECEIVABLE"); + Map bill = reopenBill(); + if ("VOIDED_BILL".equals(condition)) bill.put("status", "VOIDED"); + if ("ADJUSTED_TOTAL".equals(condition)) bill.put("total_amount", new BigDecimal("101.00")); + if ("RECEIPT".equals(condition)) bill.put("received_amount", new BigDecimal("0.01")); + when(jdbc.queryForList(startsWith("SELECT id,tenant_id,status,currency_code"), eq(10L), eq(501L))) + .thenReturn("MISSING_BILL".equals(condition) ? List.of() : List.of(bill)); + if ("OCCUPIED".equals(condition)) + when(jdbc.queryForList(startsWith("SELECT id FROM fin_settlement WHERE"), eq(10L), eq(501L), eq(88L))) + .thenReturn(List.of(Map.of("id", 99L))); + if (List.of("ADJUSTED_LINES", "WRONG_SOURCE", "EMPTY").contains(condition)) + when(jdbc.queryForList(startsWith("SELECT source_id fee_source_id"), eq(10L), eq(501L))) + .thenReturn("EMPTY".equals(condition) ? List.of() : List.of(Map.of("fee_source_id", "WRONG_SOURCE".equals(condition) ? 32L : 31L, + "line_amount", new BigDecimal("ADJUSTED_LINES".equals(condition) ? "99.00" : "100.00"), "line_currency", "CNY"))); + assertCode(() -> service(jdbc, org.mockito.Mockito.mock(FinanceFileClient.class)).actionSettlement(88L, 7L, + "reopen-ar-" + condition, new SettlementActionRequest("REOPEN", "核对原对账单", 3)), "FIN_409"); + verify(jdbc, never()).update(startsWith("UPDATE fin_settlement SET status="), (Object[]) any()); + } + + /** 重放已成功的重开请求恢复当前单据,不再次修改版本或占用来源。 */ + @Test + void successfulReopenReplayDoesNotReacquireSources() { + JdbcTemplate jdbc = reopenJdbc("PAYABLE"); + FinanceSettlementService service = service(jdbc, org.mockito.Mockito.mock(FinanceFileClient.class)); + var request = new SettlementActionRequest("REOPEN", "恢复原单", 3); + String path = "/api/platform/finance/v1/settlements/88/actions"; + String hash = ReflectionTestUtils.invokeMethod(service, "hash", Map.of("method", "POST", "path", path, "customerId", 10L, "body", request)); + when(jdbc.queryForList(startsWith("SELECT request_hash"), eq(10L), eq(7L), eq("SETTLEMENT_STATE_ACTION"), eq("replay-open"))) + .thenReturn(List.of(Map.of("request_hash", hash, "status", "SUCCEEDED", "result_resource_id", 88L))); + assertThat(service.actionSettlement(88L, 7L, "replay-open", request).summary().status()).isEqualTo("ISSUED"); + verify(jdbc, never()).queryForList(startsWith("SELECT sl.fee_source_id"), (Object[]) any()); + verify(jdbc, never()).update(startsWith("UPDATE fin_settlement SET status="), (Object[]) any()); + } + + /** 为重开场景准备完整且默认有效的原单、来源与对账单。 */ + private static JdbcTemplate reopenJdbc(String direction) { + var settlement = settlementRow(direction, "VOIDED", BigDecimal.ZERO, new BigDecimal("100.00"), 3); + settlement.put("reconciliation_statement_id", 501L); + JdbcTemplate jdbc = creationJdbc(settlementRow(direction, "ISSUED", BigDecimal.ZERO, new BigDecimal("100.00"), 4)); + org.mockito.Mockito.lenient().when(jdbc.queryForList(startsWith("SELECT id,tenant_id,total_amount"), eq(88L))) + .thenReturn(List.of(settlement)); + org.mockito.Mockito.lenient().when(jdbc.queryForList(startsWith("SELECT sl.fee_source_id"), eq(10L), eq(88L))) + .thenReturn(List.of(reopenSource(direction))); + if ("RECEIVABLE".equals(direction)) { + org.mockito.Mockito.lenient().when(jdbc.queryForList(startsWith("SELECT id,tenant_id,status,currency_code"), eq(10L), eq(501L))) + .thenReturn(List.of(reopenBill())); + org.mockito.Mockito.lenient().when(jdbc.queryForList(startsWith("SELECT source_id fee_source_id"), eq(10L), eq(501L))) + .thenReturn(List.of(Map.of("fee_source_id", 31L, "line_amount", new BigDecimal("100.00"), "line_currency", "CNY"))); + } + return jdbc; + } + + /** 提供带原结算金额的真实形状费用记录。 */ + private static Map reopenSource(String direction) { + var row = payableSource(10L, 20L); + row.put("direction", direction); + row.put("status", "RECEIVABLE".equals(direction) ? "BILLED" : "AVAILABLE"); + row.put("fee_source_id", 31L); row.put("line_amount", new BigDecimal("100.00")); row.put("line_currency", "CNY"); + return row; + } + + /** 提供已出账且未发生收款的对账单。 */ + private static Map reopenBill() { + return new java.util.HashMap<>(Map.of("id", 501L, "tenant_id", 10L, "status", "ISSUED", "currency_code", "CNY", + "total_amount", new BigDecimal("100.00"), "received_amount", BigDecimal.ZERO)); + } + @Test void partialPaymentUpdatesOutstandingAndReturnsTypedDetail() { JdbcTemplate jdbc = org.mockito.Mockito.mock(JdbcTemplate.class, invocation -> @@ -275,7 +415,7 @@ class FinanceSettlementBehaviorTest { Map.entry("bill_date", Date.valueOf("2026-08-31")), Map.entry("due_date", Date.valueOf("2026-09-30")), Map.entry("status", "ISSUED"), Map.entry("total_amount", new BigDecimal("300.00"))))); when(jdbc.queryForList(startsWith("SELECT request_hash"), eq(10L), eq(7L), eq("SETTLEMENT_AR_CREATE"), eq("ar-key"))).thenReturn(List.of()); - when(jdbc.queryForObject(startsWith("SELECT COUNT(*) FROM fin_settlement WHERE"), eq(Integer.class), eq(10L), eq(501L))).thenReturn(0); + when(jdbc.queryForList(startsWith("SELECT id FROM fin_settlement WHERE"), eq(10L), eq(501L), eq(0L))).thenReturn(List.of()); when(jdbc.queryForObject(startsWith("SELECT COUNT(*) FROM fin_bill_line"), eq(Integer.class), eq(10L), eq(501L))).thenReturn(1); FinanceSettlementService service = service(jdbc, org.mockito.Mockito.mock(FinanceFileClient.class)); @@ -357,7 +497,7 @@ class FinanceSettlementBehaviorTest { Map.entry("bill_date", Date.valueOf("2026-08-31")), Map.entry("due_date", Date.valueOf("2026-09-30")), Map.entry("status", "ISSUED"), Map.entry("total_amount", new BigDecimal("300.00"))))); when(jdbc.queryForList(startsWith("SELECT request_hash"), eq(10L), eq(7L), eq("SETTLEMENT_AR_CREATE"), eq("ar-duplicate"))).thenReturn(List.of()); - when(jdbc.queryForObject(startsWith("SELECT COUNT(*) FROM fin_settlement WHERE"), eq(Integer.class), eq(10L), eq(501L))).thenReturn(1); + when(jdbc.queryForList(startsWith("SELECT id FROM fin_settlement WHERE"), eq(10L), eq(501L), eq(0L))).thenReturn(List.of(Map.of("id", 99L))); assertCode(() -> service(jdbc, org.mockito.Mockito.mock(FinanceFileClient.class)).createReceivableSettlement(7L, "ar-duplicate", new CreateReceivableSettlementRequest(501L, "CNY")), "FIN_409"); @@ -370,7 +510,7 @@ class FinanceSettlementBehaviorTest { new BigDecimal("100.00"), 0)); when(jdbc.queryForList(startsWith("SELECT request_hash"), eq(10L), eq(7L), eq("SETTLEMENT_AP_CREATE"), eq("ap-key"))).thenReturn(List.of()); when(jdbc.queryForList(startsWith("SELECT id,customer_id"), eq(31L))).thenReturn(List.of(payableSource(10L, 20L))); - when(jdbc.queryForObject(startsWith("SELECT COUNT(*) FROM fin_settlement_line"), eq(Integer.class), eq(31L))).thenReturn(0); + when(jdbc.queryForList(startsWith("SELECT s.id FROM fin_settlement_line"), eq(10L), eq(31L), eq(0L))).thenReturn(List.of()); FinanceSupplierValidator supplierValidator = supplierValidator(); FinanceSettlementService service = service(jdbc, org.mockito.Mockito.mock(FinanceFileClient.class), supplierValidator); @@ -421,8 +561,8 @@ class FinanceSettlementBehaviorTest { if ("ineligible".equals(condition)) source.put("review_status", "PENDING"); when(jdbc.queryForList(startsWith("SELECT request_hash"), eq(10L), eq(7L), eq("SETTLEMENT_AP_CREATE"), eq("ap-" + condition))).thenReturn(List.of()); when(jdbc.queryForList(startsWith("SELECT id,customer_id"), eq(31L))).thenReturn(List.of(source)); - org.mockito.Mockito.lenient().when(jdbc.queryForObject(startsWith("SELECT COUNT(*) FROM fin_settlement_line"), eq(Integer.class), eq(31L))) - .thenReturn("occupied".equals(condition) ? 1 : 0); + org.mockito.Mockito.lenient().when(jdbc.queryForList(startsWith("SELECT s.id FROM fin_settlement_line"), eq(10L), eq(31L), eq(0L))) + .thenReturn("occupied".equals(condition) ? List.of(Map.of("id", 99L)) : List.of()); assertCode(() -> service(jdbc, org.mockito.Mockito.mock(FinanceFileClient.class)).createPayableSettlement(7L, "ap-" + condition, new CreatePayableSettlementRequest(10L, 20L, List.of(31L), LocalDate.of(2026, 9, 30), "CNY")), "FIN_409"); @@ -578,7 +718,7 @@ class FinanceSettlementBehaviorTest { Map.entry("bill_date", Date.valueOf("2026-08-31")), Map.entry("due_date", Date.valueOf("2026-09-30")), Map.entry("status", "ISSUED"), Map.entry("total_amount", new BigDecimal("300.00"))))); when(jdbc.queryForList(startsWith("SELECT request_hash"), eq(10L), eq(7L), eq("SETTLEMENT_AR_CREATE"), eq("ar-key"))).thenReturn(List.of()); - when(jdbc.queryForObject(startsWith("SELECT COUNT(*) FROM fin_settlement WHERE"), eq(Integer.class), eq(10L), eq(501L))).thenReturn(1); + when(jdbc.queryForList(startsWith("SELECT id FROM fin_settlement WHERE"), eq(10L), eq(501L), eq(0L))).thenReturn(List.of(Map.of("id", 99L))); FinanceSettlementService service = service(jdbc, org.mockito.Mockito.mock(FinanceFileClient.class)); assertCode(() -> service.createReceivableSettlement(7L, "ar-key", new CreateReceivableSettlementRequest(501L, "CNY")), "FIN_409"); @@ -597,7 +737,7 @@ class FinanceSettlementBehaviorTest { JdbcTemplate occupiedJdbc = org.mockito.Mockito.mock(JdbcTemplate.class); when(occupiedJdbc.queryForList(startsWith("SELECT request_hash"), eq(10L), eq(7L), eq("SETTLEMENT_AP_CREATE"), eq("occupied-key"))).thenReturn(List.of()); when(occupiedJdbc.queryForList(startsWith("SELECT id,customer_id"), eq(31L))).thenReturn(List.of(payableSource(10L, 20L))); - when(occupiedJdbc.queryForObject(startsWith("SELECT COUNT(*) FROM fin_settlement_line"), eq(Integer.class), eq(31L))).thenReturn(1); + when(occupiedJdbc.queryForList(startsWith("SELECT s.id FROM fin_settlement_line"), eq(10L), eq(31L), eq(0L))).thenReturn(List.of(Map.of("id", 99L))); FinanceSettlementService occupiedService = service(occupiedJdbc, org.mockito.Mockito.mock(FinanceFileClient.class)); assertCode(() -> occupiedService.createPayableSettlement(7L, "occupied-key", request), "FIN_409"); } @@ -632,8 +772,15 @@ class FinanceSettlementBehaviorTest { String sql = String.join(" ", select.value()).replaceAll("#\\{[a-zA-Z]+\\}", "?"); return jdbc.queryForList(sql, invocation.getArguments()); }); + var stateMapper = org.mockito.Mockito.mock(com.baiyue.finance.mapper.FinanceSettlementStateMapper.class, invocation -> { + var select = invocation.getMethod().getAnnotation(org.apache.ibatis.annotations.Select.class); + if (select != null) return jdbc.queryForList(String.join(" ", select.value()).replaceAll("#\\{[a-zA-Z]+\\}", "?"), invocation.getArguments()); + var update = invocation.getMethod().getAnnotation(org.apache.ibatis.annotations.Update.class); + if (update != null) return jdbc.update(String.join(" ", update.value()).replaceAll("#\\{[a-zA-Z]+\\}", "?"), invocation.getArguments()); + return RETURNS_DEFAULTS.answer(invocation); + }); return new FinanceSettlementService(jdbc, new ObjectMapper().findAndRegisterModules(), - new FinanceVoucherValidator(fileClient, "internal-token"), supplierValidator, documentNumbers, new com.baiyue.finance.repository.FinanceSettlementDetailRepository(mapper)); + new FinanceVoucherValidator(fileClient, "internal-token"), supplierValidator, documentNumbers, new com.baiyue.finance.repository.FinanceSettlementDetailRepository(mapper), FinanceCustomerNamesTestSupport.names(), new com.baiyue.finance.repository.FinanceSettlementStateRepository(stateMapper)); } private static FinanceSupplierValidator supplierValidator() { diff --git a/finance/src/test/java/com/baiyue/finance/FinanceSettlementSchemaContractTest.java b/finance/src/test/java/com/baiyue/finance/FinanceSettlementSchemaContractTest.java index 13f0ae1e8..6c38247c4 100644 --- a/finance/src/test/java/com/baiyue/finance/FinanceSettlementSchemaContractTest.java +++ b/finance/src/test/java/com/baiyue/finance/FinanceSettlementSchemaContractTest.java @@ -1,12 +1,20 @@ package com.baiyue.finance; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import org.springframework.dao.DuplicateKeyException; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.datasource.DriverManagerDataSource; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; +import java.util.UUID; +import java.util.regex.Pattern; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; class FinanceSettlementSchemaContractTest { @@ -31,6 +39,51 @@ class FinanceSettlementSchemaContractTest { assertThat(unified).doesNotContain("ALTER TABLE fin_settlement", "assert_finance"); } + /** 执行两份真实基线的结算表定义,作废历史不占位,有效应收仍由数据库兜底唯一。 */ + @ParameterizedTest + @ValueSource(booleans = {false, true}) + void voidedReceivableCanBeReplacedWithoutAllowingDuplicateActiveSettlement(boolean unified) throws Exception { + Path source = unified ? resolveUnifiedSql() : Path.of("src/main/resources/db/finance.sql"); + String sql = Files.readString(source, StandardCharsets.UTF_8); + var table = Pattern.compile("CREATE TABLE (?:IF NOT EXISTS )?fin_settlement \\(.*?\\) ENGINE=[^;]*;", Pattern.DOTALL).matcher(sql); + assertThat(table.find()).isTrue(); + // H2 仅去除 MySQL 的存储、注释和普通索引语法,保留生成列表达式及所有唯一约束。 + String ddl = table.group().replaceFirst("\\) ENGINE=[^;]*;", ");") + .replaceAll("(?i) COMMENT '(?:[^']|'')*'", "") + .replaceAll("(?i)\\bUNSIGNED\\b|\\bVIRTUAL\\b|\\bSTORED\\b|_utf8mb4", "") + .replaceAll("(?m)^\\s*KEY [^\\r\\n]+\\r?\\n", "") + .replaceAll("UNIQUE KEY (\\w+)", "CONSTRAINT $1 UNIQUE"); + var jdbc = new JdbcTemplate(new DriverManagerDataSource("jdbc:h2:mem:" + UUID.randomUUID() + ";MODE=MySQL;DB_CLOSE_DELAY=-1")); + jdbc.execute(ddl); + insertSettlement(jdbc, 1, 10, "AR-OLD", "RECEIVABLE", 501L, "VOIDED"); + insertSettlement(jdbc, 2, 10, "AR-REPLACEMENT", "RECEIVABLE", 501L, "ISSUED"); + insertSettlement(jdbc, 3, 10, "AR-HISTORY", "RECEIVABLE", 501L, "VOIDED"); + for (String status : new String[]{"ISSUED", "PARTIALLY_PAID", "PAID"}) { + jdbc.update("UPDATE fin_settlement SET status=? WHERE id=2", status); + assertThatThrownBy(() -> insertSettlement(jdbc, 4, 10, "AR-DUP", "RECEIVABLE", 501L, "ISSUED")) + .isInstanceOf(DuplicateKeyException.class); + assertThatThrownBy(() -> jdbc.update("UPDATE fin_settlement SET status='ISSUED' WHERE id=1")) + .isInstanceOf(DuplicateKeyException.class); + } + jdbc.update("UPDATE fin_settlement SET status='VOIDED' WHERE id=2"); + assertThat(jdbc.update("UPDATE fin_settlement SET status='ISSUED' WHERE id=1")).isEqualTo(1); + insertSettlement(jdbc, 5, 11, "AR-OTHER-CUSTOMER", "RECEIVABLE", 501L, "ISSUED"); + insertSettlement(jdbc, 6, 10, "AP-ONE", "PAYABLE", null, "ISSUED"); + insertSettlement(jdbc, 7, 10, "AP-TWO", "PAYABLE", null, "ISSUED"); + assertThatThrownBy(() -> insertSettlement(jdbc, 8, 10, "AR-OLD", "RECEIVABLE", 502L, "VOIDED")) + .isInstanceOf(DuplicateKeyException.class); + assertThat(jdbc.queryForObject("SELECT COUNT(*) FROM fin_settlement", Integer.class)).isEqualTo(6); + } + + /** 构造必填字段完整的结算行,仅改变本测试关注的客户、来源与状态。 */ + private static void insertSettlement(JdbcTemplate jdbc, long id, long customerId, String no, + String direction, Long billId, String status) { + jdbc.update("INSERT INTO fin_settlement(id,tenant_id,settlement_no,direction,customer_name_snapshot," + + "reconciliation_statement_id,total_amount,paid_amount,outstanding_amount,generated_date,due_date,status,created_by,updated_by) " + + "VALUES (?,?,?,?,'合成测试客户',?,0.03,0,0.03,CURRENT_DATE,CURRENT_DATE,?,7,7)", + id, customerId, no, direction, billId, status); + } + private static Path resolveUnifiedSql() { Path direct = Path.of("deploy", "sql", "头程数据库统一增量-MySQL8.sql"); return Files.exists(direct) ? direct : Path.of("..", "deploy", "sql", "头程数据库统一增量-MySQL8.sql"); diff --git a/finance/src/test/java/com/baiyue/finance/FinanceSettlementStateMapperTest.java b/finance/src/test/java/com/baiyue/finance/FinanceSettlementStateMapperTest.java new file mode 100644 index 000000000..5a86e7b6f --- /dev/null +++ b/finance/src/test/java/com/baiyue/finance/FinanceSettlementStateMapperTest.java @@ -0,0 +1,123 @@ +package com.baiyue.finance; + +import com.baiyue.finance.mapper.FinanceSettlementStateMapper; +import com.baiyue.finance.repository.FinanceSettlementStateRepository; +import org.apache.ibatis.mapping.Environment; +import org.apache.ibatis.session.Configuration; +import org.apache.ibatis.session.SqlSessionFactory; +import org.apache.ibatis.session.SqlSessionFactoryBuilder; +import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory; +import org.junit.jupiter.api.Test; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.datasource.DriverManagerDataSource; +import java.util.UUID; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class FinanceSettlementStateMapperTest { + /** 真正执行隔离查询,既保留损坏明细供业务拒绝,也不把其他客户占用算入当前客户。 */ + @Test + void originalRowsAndOccupancyStayCustomerScoped() { + var fixture = fixture(); + fixture.jdbc.update("INSERT INTO fin_fee_source VALUES (32,11,'PAYABLE','CNY',100,'APPROVED','AVAILABLE','ON_ACCOUNT','{}')"); + fixture.jdbc.update("INSERT INTO fin_settlement_line VALUES (3,10,88,32,100,'CNY'),(4,11,88,31,100,'CNY')"); + try (var session = fixture.factory.openSession()) { + var repository = new FinanceSettlementStateRepository(session.getMapper(FinanceSettlementStateMapper.class)); + assertThat(repository.lockSources(10, 88)).hasSize(2); + assertThat(repository.lockSources(10, 88).get(1).get("customer_id")).isEqualTo(11L); + assertThat(repository.sourceOccupied(10, 31, 88)).isFalse(); + assertThat(repository.sourceOccupied(11, 31, 88)).isFalse(); + assertThat(repository.lockBill(10, 501)).hasSize(1); + assertThat(repository.lockBill(11, 501)).isEmpty(); + assertThat(repository.lockBillLines(10, 501)).hasSize(1); + assertThat(repository.lockBillLines(11, 501)).isEmpty(); + } + } + + /** 同一会话重复校验必须读取最新状态,状态写入继续校验客户与版本。 */ + @Test + void stateUpdatesCannotReuseCachedOccupancyOrBypassVersion() throws Exception { + var fixture = fixture(); + try (var session = fixture.factory.openSession()) { + var repository = new FinanceSettlementStateRepository(session.getMapper(FinanceSettlementStateMapper.class)); + assertThat(repository.sourceOccupied(10, 31, 99)).isFalse(); + assertThat(repository.changeStatus("ISSUED", 7, 88, 11, 3)).isZero(); + assertThat(repository.changeStatus("ISSUED", 7, 88, 10, 2)).isZero(); + assertThat(repository.changeStatus("ISSUED", 7, 88, 10, 3)).isEqualTo(1); + assertThat(repository.sourceOccupied(10, 31, 99)).isTrue(); + assertThat(repository.lockSettlement(88).get(0).get("version")).isEqualTo(4); + assertThat(repository.lockSources(10, 88).get(0).get("status")).isEqualTo("AVAILABLE"); + try (var statement = session.getConnection().createStatement()) { + statement.executeUpdate("UPDATE fin_fee_source SET status='SUPERSEDED' WHERE id=31"); + } + assertThat(repository.lockSources(10, 88).get(0).get("status")).isEqualTo("SUPERSEDED"); + } + } + + /** 同账单唯一应收采用当前读取,排除自身、作废单及其他客户。 */ + @Test + void receivableOccupancyReflectsCurrentActiveSettlement() { + var fixture = fixture(); + fixture.jdbc.update("UPDATE fin_settlement SET direction='RECEIVABLE',reconciliation_statement_id=501,status='ISSUED' WHERE id=88"); + try (var session = fixture.factory.openSession()) { + var repository = new FinanceSettlementStateRepository(session.getMapper(FinanceSettlementStateMapper.class)); + assertThat(repository.billOccupied(10, 501, 99)).isTrue(); + assertThat(repository.billOccupied(10, 501, 88)).isFalse(); + assertThat(repository.billOccupied(11, 501, 99)).isFalse(); + repository.changeStatus("VOIDED", 7, 88, 10, 3); + assertThat(repository.billOccupied(10, 501, 99)).isFalse(); + } + } + + /** 两个事务争用同一来源时,后到事务须等待并读取先到事务提交后的占用。 */ + @Test + void sourceLockSerializesCompetingReopens() throws Exception { + var fixture = fixture(); + var executor = Executors.newSingleThreadExecutor(); + try (var first = fixture.factory.openSession()) { + var repository = new FinanceSettlementStateRepository(first.getMapper(FinanceSettlementStateMapper.class)); + repository.lockSources(10, 88); + var started = new CountDownLatch(1); + var second = executor.submit(() -> { + try (var session = fixture.factory.openSession()) { + var other = new FinanceSettlementStateRepository(session.getMapper(FinanceSettlementStateMapper.class)); + started.countDown(); + other.lockSources(10, 99); + return other.sourceOccupied(10, 31, 99); + } + }); + assertThat(started.await(2, TimeUnit.SECONDS)).isTrue(); + assertThatThrownBy(() -> second.get(150, TimeUnit.MILLISECONDS)).isInstanceOf(TimeoutException.class); + repository.changeStatus("ISSUED", 7, 88, 10, 3); + first.commit(); + assertThat(second.get(3, TimeUnit.SECONDS)).isTrue(); + } finally { + executor.shutdownNow(); + } + } + + /** 使用独立内存库执行真实 MyBatis SQL,不启动本地业务服务。 */ + private static Fixture fixture() { + var dataSource = new DriverManagerDataSource("jdbc:h2:mem:reopen-" + UUID.randomUUID() + ";MODE=MySQL;DB_CLOSE_DELAY=-1;LOCK_TIMEOUT=4000", "sa", ""); + var jdbc = new JdbcTemplate(dataSource); + jdbc.execute("CREATE TABLE fin_settlement(id BIGINT PRIMARY KEY,tenant_id BIGINT,total_amount DECIMAL(18,2),paid_amount DECIMAL(18,2),outstanding_amount DECIMAL(18,2),payment_status VARCHAR(32),status VARCHAR(32),version INT,direction VARCHAR(32),currency_code VARCHAR(3),supplier_id BIGINT,reconciliation_statement_id BIGINT,updated_by BIGINT)"); + jdbc.execute("CREATE TABLE fin_settlement_line(id BIGINT PRIMARY KEY,tenant_id BIGINT,settlement_id BIGINT,fee_source_id BIGINT,amount DECIMAL(18,2),currency_code VARCHAR(3))"); + jdbc.execute("CREATE TABLE fin_fee_source(id BIGINT PRIMARY KEY,customer_id BIGINT,direction VARCHAR(32),currency_code VARCHAR(3),amount DECIMAL(18,2),review_status VARCHAR(32),status VARCHAR(32),settlement_mode VARCHAR(32),source_snapshot VARCHAR(500))"); + jdbc.execute("CREATE TABLE fin_bill(id BIGINT PRIMARY KEY,tenant_id BIGINT,status VARCHAR(32),currency_code VARCHAR(3),total_amount DECIMAL(18,2),received_amount DECIMAL(18,2))"); + jdbc.execute("CREATE TABLE fin_bill_line(id BIGINT PRIMARY KEY,tenant_id BIGINT,bill_id BIGINT,source_id BIGINT,amount DECIMAL(18,2),currency_code VARCHAR(3))"); + jdbc.update("INSERT INTO fin_settlement VALUES (88,10,100,0,100,'UNPAID','VOIDED',3,'PAYABLE','CNY',20,NULL,7),(99,10,100,0,100,'UNPAID','VOIDED',3,'PAYABLE','CNY',20,NULL,7)"); + jdbc.update("INSERT INTO fin_settlement_line VALUES (1,10,88,31,100,'CNY'),(2,10,99,31,100,'CNY')"); + jdbc.update("INSERT INTO fin_fee_source VALUES (31,10,'PAYABLE','CNY',100,'APPROVED','AVAILABLE','ON_ACCOUNT','{}')"); + jdbc.update("INSERT INTO fin_bill VALUES (501,10,'ISSUED','CNY',100,0)"); + jdbc.update("INSERT INTO fin_bill_line VALUES (1,10,501,31,100,'CNY')"); + var configuration = new Configuration(new Environment("test", new JdbcTransactionFactory(), dataSource)); + configuration.addMapper(FinanceSettlementStateMapper.class); + return new Fixture(jdbc, new SqlSessionFactoryBuilder().build(configuration)); + } + + private record Fixture(JdbcTemplate jdbc, SqlSessionFactory factory) { } +} diff --git a/finance/src/test/java/com/baiyue/finance/FinanceSqlCommentContractTest.java b/finance/src/test/java/com/baiyue/finance/FinanceSqlCommentContractTest.java index 20609d980..30795d6ec 100644 --- a/finance/src/test/java/com/baiyue/finance/FinanceSqlCommentContractTest.java +++ b/finance/src/test/java/com/baiyue/finance/FinanceSqlCommentContractTest.java @@ -44,7 +44,7 @@ class FinanceSqlCommentContractTest { } } assertThat(tableCount).isEqualTo(23); - assertThat(columnCount).isEqualTo(352); + assertThat(columnCount).isEqualTo(353); assertThat(missing).as("缺少中文业务注释的Finance字段").isEmpty(); } diff --git a/finance/src/test/java/com/baiyue/finance/PlatformFinanceWalletRefundControllerTest.java b/finance/src/test/java/com/baiyue/finance/PlatformFinanceWalletRefundControllerTest.java new file mode 100644 index 000000000..0ef3487b2 --- /dev/null +++ b/finance/src/test/java/com/baiyue/finance/PlatformFinanceWalletRefundControllerTest.java @@ -0,0 +1,54 @@ +package com.baiyue.finance; + +import com.baiyue.common.security.AuthenticatedUser; +import com.baiyue.finance.controller.PlatformFinanceWalletController; +import com.baiyue.finance.dto.FinanceWalletDtos.WalletRefundRequest; +import com.baiyue.finance.service.FinanceWalletService; +import org.junit.jupiter.api.Test; +import org.springframework.security.access.prepost.PreAuthorize; +import java.math.BigDecimal; +import java.util.Set; +import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; + +/** 平台退款入口鉴权、必填原因和稳定幂等引用的回归。 */ +class PlatformFinanceWalletRefundControllerTest { + private final FinanceWalletService service = mock(FinanceWalletService.class); + private final PlatformFinanceWalletController controller = new PlatformFinanceWalletController(service); + private final WalletRefundRequest request = new WalletRefundRequest(new BigDecimal("12.00"), "退回多收费用"); + + /** 客户即使携带同名权限也不能借平台入口操作他人资金。 */ + @Test void nonPlatformPrincipalsAreDeniedBeforeWalletAccess() { + for (var user : new AuthenticatedUser[]{null, user("CLIENT", 9L), user("PLATFORM", 9L)}) + assertThatThrownBy(() -> controller.refund(user, 9, 1, "key", request)) + .isInstanceOf(org.springframework.web.server.ResponseStatusException.class); + verifyNoInteractions(service); + } + + /** 同一客户请求键生成同一退款引用,实际防重与金额上限仍由资金事务执行。 */ + @Test void authorizedCallsUseStableReferenceAndExistingAdjustPermission() throws Exception { + controller.refund(user("PLATFORM", 0L), 9, 1, " key ", request); + controller.refund(user("PLATFORM", 0L), 9, 1, "key", request); + var reference = org.mockito.ArgumentCaptor.forClass(String.class); + verify(service, times(2)).refundConsumption(eq(9L), eq(1L), reference.capture(), eq("key"), + eq(request.amount()), eq(7L), isNull(), eq(request.reason())); + assertThat(reference.getAllValues()).allSatisfy(value -> assertThat(value).startsWith("MANUAL_REFUND:")); + assertThat(reference.getAllValues().get(0)).isEqualTo(reference.getAllValues().get(1)); + assertThat(PlatformFinanceWalletController.class.getMethod("refund", AuthenticatedUser.class, + long.class, long.class, String.class, WalletRefundRequest.class).getAnnotation(PreAuthorize.class).value()) + .isEqualTo("hasAuthority('platform:finance:customer-account:adjust')"); + } + + /** 空说明或非法幂等键在触达资金前失败。 */ + @Test void missingReasonAndIdempotencyAreRejected() { + assertThatThrownBy(() -> controller.refund(user("PLATFORM", 0L), 9, 1, " ", request)).isInstanceOf(IllegalArgumentException.class); + assertThatThrownBy(() -> controller.refund(user("PLATFORM", 0L), 9, 1, "key", + new WalletRefundRequest(BigDecimal.ONE, " "))).isInstanceOf(IllegalArgumentException.class); + verifyNoInteractions(service); + } + + private AuthenticatedUser user(String app, long tenant) { + return new AuthenticatedUser(7L, tenant, 0L, null, "finance", app, "WEB", + Set.of("platform:finance:customer-account:adjust")); + } +} diff --git a/finance/src/test/java/com/baiyue/finance/ReceivableStatisticsServiceTest.java b/finance/src/test/java/com/baiyue/finance/ReceivableStatisticsServiceTest.java index b6709ad8e..f764ab344 100644 --- a/finance/src/test/java/com/baiyue/finance/ReceivableStatisticsServiceTest.java +++ b/finance/src/test/java/com/baiyue/finance/ReceivableStatisticsServiceTest.java @@ -28,7 +28,7 @@ class ReceivableStatisticsServiceTest { row(10001, "客户A", "300.00", "100.00", "200.00"), row(10002, "客户B", "80.00", "80.00", "0.00")); when(jdbc.query(anyString(), any(RowMapper.class), any(Object[].class))).thenReturn(rows); - ReceivableStatisticsService service = new ReceivableStatisticsService(jdbc); + ReceivableStatisticsService service = new ReceivableStatisticsService(jdbc, FinanceCustomerNamesTestSupport.names()); var result = service.query(YearMonth.of(2026, 7), null, 1, 1); @@ -51,12 +51,35 @@ class ReceivableStatisticsServiceTest { when(jdbc.query(anyString(), any(RowMapper.class), any(Object[].class))) .thenReturn(List.of(row(10001, "客户A", "300.00", "100.00", "199.99"))); - assertThatThrownBy(() -> new ReceivableStatisticsService(jdbc) + assertThatThrownBy(() -> new ReceivableStatisticsService(jdbc, FinanceCustomerNamesTestSupport.names()) .query(YearMonth.of(2026, 7), null, 1, 20)) .isInstanceOf(FinanceEventException.class) .hasMessageContaining("勾稽异常"); } + @Test + @SuppressWarnings("unchecked") + void queryAndExportResolveTheSameCustomerNameWithoutChangingAmounts() throws Exception { + var jdbc = mock(JdbcTemplate.class); + when(jdbc.query(anyString(), any(RowMapper.class), any(Object[].class))).thenAnswer(invocation -> { + RowMapper mapper = invocation.getArgument(1); + var rs = mock(java.sql.ResultSet.class); + when(rs.getLong("tenant_id")).thenReturn(9L); + when(rs.getString("customer_name_snapshot")).thenReturn("客户9"); + when(rs.getBigDecimal("total_receivable")).thenReturn(new BigDecimal("300.00")); + when(rs.getBigDecimal("total_received")).thenReturn(new BigDecimal("100.00")); + when(rs.getBigDecimal("total_outstanding")).thenReturn(new BigDecimal("200.00")); + return List.of(mapper.mapRow(rs, 0)); + }); + var service = new ReceivableStatisticsService(jdbc, FinanceCustomerNamesTestSupport.names()); + var query = service.query(YearMonth.of(2026, 10), 9L, 1, 20); + assertThat(query.records()).singleElement().satisfies(row -> { + assertThat(row.customerName()).isEqualTo("测试客户公司"); + assertThat(row.totalReceivable()).isEqualByComparingTo("300.00"); + }); + assertThat(service.exportRows(YearMonth.of(2026, 10), 9L)).isEqualTo(query.records()); + } + private static ReceivableStatisticsRow row(long id, String name, String total, String received, String outstanding) { return new ReceivableStatisticsRow(id, name, "CNY", new BigDecimal(total), new BigDecimal(received), new BigDecimal(outstanding)); diff --git a/finance/src/test/java/com/baiyue/finance/ReconciliationStatementBehaviorTest.java b/finance/src/test/java/com/baiyue/finance/ReconciliationStatementBehaviorTest.java index f741990ef..6f59129d3 100644 --- a/finance/src/test/java/com/baiyue/finance/ReconciliationStatementBehaviorTest.java +++ b/finance/src/test/java/com/baiyue/finance/ReconciliationStatementBehaviorTest.java @@ -11,6 +11,8 @@ import com.baiyue.finance.service.FinanceDocumentNumberGenerator; import com.baiyue.finance.service.ReconciliationStatementService; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.ArgumentCaptor; @@ -83,6 +85,8 @@ class ReconciliationStatementBehaviorTest { @Test void byCutoffSelectsEligibleSourcesAndCreatesDraft() throws Exception { JdbcTemplate jdbc = createJdbc(); + when(jdbc.query(org.mockito.ArgumentMatchers.startsWith("SELECT customer_name_snapshot"), any(RowMapper.class), eq(9L))) + .thenReturn(List.of()); ReconciliationStatementService target = serviceWithNumber(jdbc); CreateReconciliationRequest request = new CreateReconciliationRequest(9L, "CNY", "BY_CUTOFF", List.of(), LocalDate.of(2026, 8, 1), LocalDate.of(2026, 8, 31), "截止日对账"); @@ -90,7 +94,15 @@ class ReconciliationStatementBehaviorTest { var result = target.create(71L, "cutoff-key", request); assertThat(result.status()).isEqualTo("DRAFT"); + assertThat(result.customerName()).isEqualTo("测试客户公司"); assertThat(result.totalReceivable()).isEqualByComparingTo("100.00"); + var creator = ArgumentCaptor.forClass(PreparedStatementCreator.class); + verify(jdbc).update(creator.capture(), any(KeyHolder.class)); + var connection = mock(java.sql.Connection.class); + var insert = mock(java.sql.PreparedStatement.class); + when(connection.prepareStatement(anyString(), eq(java.sql.Statement.RETURN_GENERATED_KEYS))).thenReturn(insert); + creator.getValue().createPreparedStatement(connection); + verify(insert).setString(4, "测试客户公司"); verify(jdbc).query(org.mockito.ArgumentMatchers.startsWith("SELECT id,source_fee_id"), any(RowMapper.class), eq(9L), eq(LocalDate.of(2026, 8, 1)), eq(LocalDate.of(2026, 8, 31))); } @@ -114,6 +126,22 @@ class ReconciliationStatementBehaviorTest { verify(jdbc).update(org.mockito.ArgumentMatchers.startsWith("UPDATE fin_fee_source SET status='LOCKED'"), eq(88L), eq(9L)); } + /** 已被占用的费用不能完成第二张有效对账单的生成,事务必须拒绝提交。 */ + @Test + void createRejectsSourceWhoseAvailableStatusCannotBeLocked() throws Exception { + JdbcTemplate jdbc = createJdbc(); + when(jdbc.update(org.mockito.ArgumentMatchers.startsWith("UPDATE fin_fee_source SET status='LOCKED'"), eq(88L), eq(9L))) + .thenReturn(0); + CreateReconciliationRequest request = new CreateReconciliationRequest(9L, "CNY", "SELECTED", List.of(88L), + LocalDate.of(2026, 8, 1), LocalDate.of(2026, 8, 31), null); + + assertThatThrownBy(() -> serviceWithNumber(jdbc).create(71L, "create-key", request)) + .isInstanceOf(FinanceEventException.class) + .hasMessage("费用来源已被其他对账单占用") + .extracting(error -> ((FinanceEventException) error).code()).isEqualTo("FIN_409"); + verify(jdbc, never()).update(org.mockito.ArgumentMatchers.startsWith("UPDATE fin_idempotency_record SET status='SUCCEEDED'"), (Object[]) any()); + } + @Test void selectedCrossCustomerSourceIsHiddenAsNotFoundBeforeBillInsert() throws Exception { JdbcTemplate jdbc = mock(JdbcTemplate.class, invocation -> { @@ -350,6 +378,29 @@ class ReconciliationStatementBehaviorTest { verify(jdbc, never()).update(org.mockito.ArgumentMatchers.startsWith("UPDATE fin_bill SET total_amount="), (Object[]) any()); } + /** 首次操作响应必须带上本次审计修订,不能要求页面刷新后才看到最新操作。 */ + @ParameterizedTest + @ValueSource(strings = {"SUBMIT", "ADJUST"}) + void actionResponseIncludesRevisionWrittenByThatAction(String action) throws Exception { + boolean adjust = "ADJUST".equals(action); + JdbcTemplate jdbc = actionJdbc(adjust ? "ISSUED" : "DRAFT", adjust ? "ISSUED" : "PENDING_REVIEW", "revision-key"); + var auditWritten = new java.util.concurrent.atomic.AtomicBoolean(); + when(jdbc.update(org.mockito.ArgumentMatchers.startsWith("INSERT INTO fin_audit_log"), any(Object[].class))) + .thenAnswer(invocation -> { auditWritten.set(true); return 1; }); + var revision = new com.baiyue.finance.dto.ReconciliationStatementDtos.ReconciliationRevision( + 1, action, new BigDecimal("240.00"), new BigDecimal(adjust ? "280.00" : "240.00"), + "核对即时修订", 71L, "测试财务", LocalDateTime.of(2026, 10, 27, 12, 0)); + when(jdbc.query(org.mockito.ArgumentMatchers.startsWith("SELECT action,reason"), any(RowMapper.class), eq(9L), eq(501L))) + .thenAnswer(invocation -> auditWritten.get() ? List.of(revision) : List.of()); + if (adjust) when(jdbc.queryForObject(org.mockito.ArgumentMatchers.startsWith("SELECT COALESCE(SUM(amount)"), eq(BigDecimal.class), eq(9L), eq(501L))) + .thenReturn(new BigDecimal("280.00")); + var request = new ReconciliationActionRequest(action, "核对即时修订", adjust ? 3 : 0, + adjust ? List.of(new StatementAdjustmentLineRequest(601L, new BigDecimal("280.00"), "调整")) : List.of()); + var result = serviceWithNumber(jdbc).action(71L, 501L, "revision-key", request); + assertThat(auditWritten.get()).isTrue(); + assertThat(result.revisions()).containsExactly(revision); + } + @Test void voidIssuedStatementReleasesSourcesWhenNoReceiptOrSettlementExists() throws Exception { JdbcTemplate jdbc = actionJdbc("ISSUED", "VOIDED", "void-key"); @@ -393,6 +444,24 @@ class ReconciliationStatementBehaviorTest { verify(jdbc).update(org.mockito.ArgumentMatchers.startsWith("UPDATE fin_fee_source f JOIN"), eq(9L), eq(501L), eq(9L)); } + /** 作废单的两笔原费用有任一已进入新单时,旧单不得重开为另一张有效单。 */ + @ParameterizedTest + @ValueSource(ints = {0, 1}) + void reopenRejectsVoidedStatementWhenReplacementOccupiesAnySource(int reacquired) throws Exception { + JdbcTemplate jdbc = actionJdbc("VOIDED", "DRAFT", "reopen-conflict-key"); + when(jdbc.queryForObject(org.mockito.ArgumentMatchers.startsWith("SELECT COUNT(*) FROM fin_bill_line"), eq(Integer.class), eq(9L), eq(501L))) + .thenReturn(2); + when(jdbc.update(org.mockito.ArgumentMatchers.startsWith("UPDATE fin_fee_source f JOIN"), eq(9L), eq(501L), eq(9L))) + .thenReturn(reacquired); + + assertThatThrownBy(() -> serviceWithNumber(jdbc).action(71L, 501L, "reopen-conflict-key", + new ReconciliationActionRequest("REOPEN", "重新核对", 4, List.of()))) + .isInstanceOf(FinanceEventException.class) + .hasMessage("原费用来源已被占用,无法重开对账单") + .extracting(error -> ((FinanceEventException) error).code()).isEqualTo("FIN_409"); + verify(jdbc, never()).update(org.mockito.ArgumentMatchers.startsWith("UPDATE fin_bill SET status="), (Object[]) any()); + } + @Test void reconciliationIdempotencyRecoversFailedAndStaleProcessingRecords() { ReconciliationActionRequest body = new ReconciliationActionRequest("SUBMIT", null, 0, List.of()); @@ -589,9 +658,30 @@ class ReconciliationStatementBehaviorTest { }; } + /** 审批前后使用不同 DTO,实际序列化后的详情金额必须完整返回而非变成零。 */ + @Test + void revisionReadsBeforeBillAndAfterDetailAmountsAndRejectsMissingAmount() throws Exception { + var service = serviceWithNumber(mock(JdbcTemplate.class)); + var rs = mock(ResultSet.class); + when(rs.getString("action")).thenReturn("RECONCILIATION_ADJUST"); + when(rs.getString("before_data")).thenReturn("{\"total\":240.00}"); + when(rs.getString("after_data")).thenReturn("{\"totalReceivable\":240.03}"); + when(rs.getTimestamp("created_at")).thenReturn(Timestamp.valueOf("2026-10-27 10:00:00")); + com.baiyue.finance.dto.ReconciliationStatementDtos.ReconciliationRevision revision = + ReflectionTestUtils.invokeMethod(service, "revision", rs, 1); + assertThat(revision.beforeAmount()).isEqualByComparingTo("240.00"); + assertThat(revision.afterAmount()).isEqualByComparingTo("240.03"); + when(rs.getString("after_data")).thenReturn("{\"totalReceivable\":9999999999999999.99}"); + revision = ReflectionTestUtils.invokeMethod(service, "revision", rs, 1); + assertThat(revision.afterAmount()).isEqualByComparingTo("9999999999999999.99"); + when(rs.getString("after_data")).thenReturn("{}"); + assertThatThrownBy(() -> ReflectionTestUtils.invokeMethod(service, "revision", rs, 1)) + .isInstanceOf(FinanceEventException.class).hasMessageContaining("快照损坏"); + } + private static ReconciliationStatementService serviceWithNumber(JdbcTemplate jdbc) { FinanceDocumentNumberGenerator documentNumbers = mock(FinanceDocumentNumberGenerator.class); org.mockito.Mockito.lenient().when(documentNumbers.next("REC")).thenReturn("REC202608180001"); - return new ReconciliationStatementService(jdbc, new ObjectMapper().findAndRegisterModules(), documentNumbers); + return new ReconciliationStatementService(jdbc, new ObjectMapper().findAndRegisterModules(), documentNumbers, FinanceCustomerNamesTestSupport.names()); } } diff --git a/finance/src/test/java/com/baiyue/finance/service/FinanceExportLabelsTest.java b/finance/src/test/java/com/baiyue/finance/service/FinanceExportLabelsTest.java new file mode 100644 index 000000000..ca71ca2e5 --- /dev/null +++ b/finance/src/test/java/com/baiyue/finance/service/FinanceExportLabelsTest.java @@ -0,0 +1,34 @@ +package com.baiyue.finance.service; + +import org.junit.jupiter.api.Test; +import java.util.List; +import static org.assertj.core.api.Assertions.assertThat; + +class FinanceExportLabelsTest { + @Test void translatesStatusSourceAndLedgerFamiliesWithoutLeakingCodes() { + assertThat(FinanceExportLabels.status("PARTIALLY_PAID")).isEqualTo("部分结清"); + assertThat(FinanceExportLabels.status("SETTLED")).isEqualTo("已结算"); + for (String code : List.of("ACTIVE", "DISABLED", "DRAFT", "PENDING", "PENDING_REVIEW", "APPROVED", "REJECTED", "ISSUED", "PARTIALLY_PAID", "PAID", "VOIDED", "AVAILABLE", "LOCKED", "BILLED")) { + assertThat(FinanceExportLabels.status(code)).matches(".*[一-鿿].*").doesNotContain("未知"); + } + for (String code : List.of("INBOUND_FEE", "ORDER_FEE", "CONTAINER_FEE", "CONTAINER_COST", "MANUAL_COST", "STORAGE_FEE", "OVERSEAS_INBOUND_FEE", "OVERSEAS_PROCUREMENT_FEE", "OVERSEAS_TRUCK_FEE", "OVERSEAS_LOOSEBOARD_FEE", "OVERSEAS_WAREHOUSE_FEE", "OVERSEAS_CONTAINER_FEE", "OVERSEAS_ORDER_FEE", "OVERSEAS_DELIVERY_FEE", "OVERSEAS_RELABEL_FEE")) { + assertThat(FinanceExportLabels.source(code)).matches(".*[一-鿿].*").doesNotContain("未知"); + } + for (String code : List.of("FEE_SOURCE_EDIT", "FEE_SOURCE_SUBMIT", "FEE_SOURCE_REVIEW_APPROVE", "FEE_SOURCE_REVIEW_REJECT", "FEE_SOURCE_ADJUST", "FEE_SOURCE_VOID", "RECONCILIATION_CREATED", "SETTLEMENT_ISSUED", "RECEIPT", "PAYMENT", "SUPPLIER_DIRECT_PAID", "SUPPLIER_ON_ACCOUNT_PAYMENT", "ADJUSTMENT", "VOID")) { + assertThat(FinanceExportLabels.business(code)).matches(".*[一-鿿].*").doesNotContain("未知"); + } + assertThat(FinanceExportLabels.direction("RECEIVABLE")).isEqualTo("应收"); + for (String code : List.of("RECHARGE", "DEDUCTION", "CREDIT_LIMIT", "FREEZE", "UNFREEZE", "CONSUMPTION", "REFUND")) { + assertThat(FinanceExportLabels.business(code)).matches(".*[一-鿿].*").doesNotContain("未知"); + } + assertThat(FinanceExportLabels.direction("PAYABLE")).isEqualTo("应付"); + } + + @Test void handlesEmptyChineseSnapshotsAndUnknownValues() { + assertThat(FinanceExportLabels.status(null)).isNull(); + assertThat(FinanceExportLabels.status("")).isEmpty(); + assertThat(FinanceExportLabels.status("已结清")).isEqualTo("已结清"); + assertThat(FinanceExportLabels.status("NEW_INTERNAL_STATUS")).isEqualTo("未知状态"); + assertThat(FinanceExportLabels.status(" partially_paid ")).isEqualTo("部分结清"); + } +} diff --git a/finance/src/test/java/com/baiyue/finance/service/FinanceExportProviderServiceTest.java b/finance/src/test/java/com/baiyue/finance/service/FinanceExportProviderServiceTest.java index 663f1c3e9..5e51da4d1 100644 --- a/finance/src/test/java/com/baiyue/finance/service/FinanceExportProviderServiceTest.java +++ b/finance/src/test/java/com/baiyue/finance/service/FinanceExportProviderServiceTest.java @@ -45,6 +45,39 @@ class FinanceExportProviderServiceTest { service.initializeSigningKey(); } + @Test + void exportsAccountAndLedgerEnumsAsChineseWithoutChangingFiltersOrReferences() throws Exception { + var mapper = new ObjectMapper().findAndRegisterModules(); + var account = mapper.convertValue(Map.of("customerId", 10001L, "customerCode", "C-10001", "status", "ACTIVE"), com.baiyue.finance.dto.FinancePhase1Dtos.AccountSummary.class); + var ledger = mapper.convertValue(Map.of("ledgerId", 1L, "businessType", "SUPPLIER_ON_ACCOUNT_PAYMENT", "documentNo", "AP-1"), com.baiyue.finance.dto.FinancePhase1Dtos.LedgerEntry.class); + when(accountService.list(1, 100, null, "ACTIVE", 10001L)).thenReturn(PageEnvelope.success(List.of(account), 1, 100, 1)); + when(operationService.listDerivedCustomerAccountLedger(10001L, 1, 100, "SUPPLIER_ON_ACCOUNT_PAYMENT", null, null)).thenReturn(PageEnvelope.success(List.of(ledger), 1, 100, 1)); + var accounts = service.exportBatch(FinanceExportProviderService.CUSTOMER_ACCOUNT_LIST, request(Map.of("customerId", "10001", "status", "ACTIVE"), null, null)); + var ledgers = service.exportBatch(FinanceExportProviderService.CUSTOMER_ACCOUNT_LEDGER, request(Map.of("customerId", "10001", "businessType", "SUPPLIER_ON_ACCOUNT_PAYMENT"), null, null)); + assertThat(accounts.rows().get(0)).containsEntry("status", "正常").containsEntry("customerCode", "C-10001"); + assertThat(ledgers.rows().get(0)).containsEntry("businessType", "供应商账期付款").containsEntry("documentNo", "AP-1"); + } + + @Test + /** 应付结算列表和详情导出均使用中文状态,包括无明细的单据。 */ + void exportsSettlementStatusesInChinese() { + for (var entry : Map.of("ISSUED", "已出账", "PARTIALLY_PAID", "部分结清", "PAID", "已结清", "VOIDED", "已作废").entrySet()) { + var summary = new com.baiyue.finance.dto.FinanceSettlementDtos.SettlementSummary( + 701L, "AP-701", "PAYABLE", 10001L, "客户A", 301L, "供应商A", null, null, + "2026-11", "操作费", "CNY", BigDecimal.TEN, BigDecimal.ZERO, BigDecimal.TEN, + LocalDate.parse("2026-11-03"), null, "UNPAID", entry.getKey(), 7L, "财务员", null, null, null, 1); + when(settlementService.listSettlements("PAYABLE", null)).thenReturn(List.of(summary)); + when(settlementService.getSettlement(701L)).thenReturn(new com.baiyue.finance.dto.FinanceSettlementDtos.SettlementDetail( + summary, List.of(), List.of(), List.of())); + var list = service.exportBatch(FinanceExportProviderService.PAYABLE_SETTLEMENT_LIST, + request(Map.of("status", entry.getKey()), null, null)); + var detail = service.exportBatch(FinanceExportProviderService.PAYABLE_SETTLEMENT_DETAIL, + request(Map.of("settlementId", "701"), null, null)); + assertThat(list.rows().get(0)).containsEntry("status", entry.getValue()); + assertThat(detail.rows().get(0)).containsEntry("status", entry.getValue()); + } + } + @Test void exportsStatementRowsUsingBusinessContainerNumberAndStableSnapshot() { Statement summary = new Statement(501L, 10001L, "REC-501", "客户A", LocalDate.parse("2026-08-31"), @@ -78,13 +111,33 @@ class FinanceExportProviderServiceTest { assertThat(first.columns()).isNotEmpty(); assertThat(first.rows()).hasSize(1); - assertThat(first.rows().get(0)).containsEntry("statementNo", "REC-501").containsEntry("customerName", "客户A"); + assertThat(first.rows().get(0)).containsEntry("statementNo", "REC-501").containsEntry("customerName", "客户A").containsEntry("status", "已出账"); assertThat(first.totalRows()).isEqualTo(1L); assertThat(continued.columns()).isEmpty(); assertThat(continued.rows()).isEmpty(); assertThat(continued.snapshotToken()).isEqualTo(first.snapshotToken()); } + @Test + void exportsPaidSourcesUsingTheSameProjectionAndFiltersAsTheList() throws Exception { + var mapper = new com.fasterxml.jackson.databind.ObjectMapper().findAndRegisterModules(); + var source = mapper.treeToValue(mapper.createObjectNode().put("feeSourceId", 81).put("sourceFeeId", 9001) + .put("sourceVersion", 2).put("sourceSystem", "FIRSTLEG").put("sourceType", "CONTAINER_FEE") + .put("direction", "PAYABLE").put("customerId", 10001).put("customerName", "客户A") + .put("feeItemName", "装车费").put("amount", 300).put("currency", "CNY") + .put("reviewStatus", "APPROVED").put("sourceStatus", "PAID").put("statementGenerated", false), + com.baiyue.finance.dto.FinancePhase1Dtos.FeeSourceSummary.class); + when(operationService.listFinanceFeeSources(1, 200, null, "PAYABLE", "PAID", null, null, null, null, null)) + .thenReturn(PageEnvelope.success(List.of(source), 1, 200, 1)); + var result = service.exportBatch(FinanceExportProviderService.FEE_SOURCE_LIST, + request(Map.of("direction", "PAYABLE", "status", "PAID"), null, null)); + assertThat(result.rows()).singleElement().satisfies(row -> { + assertThat(row).containsEntry("sourceStatus", "已结清").containsEntry("reviewStatus", "已审核").containsEntry("direction", "应付").containsEntry("sourceType", "装柜费用").containsEntry("sourceFeeId", "9001"); + assertThat(row.get("amount")).isEqualTo(new BigDecimal("300")); + }); + org.mockito.Mockito.verifyNoInteractions(queryService); + } + @Test void rejectsUnknownParametersAndSnapshotReuseAcrossFilters() { assertThatThrownBy(() -> service.exportBatch(FinanceExportProviderService.CUSTOMER_ACCOUNT_LIST, diff --git a/finance/src/test/java/com/baiyue/finance/service/OverseasFeeWalletClosureTest.java b/finance/src/test/java/com/baiyue/finance/service/OverseasFeeWalletClosureTest.java new file mode 100644 index 000000000..b6e51e4e6 --- /dev/null +++ b/finance/src/test/java/com/baiyue/finance/service/OverseasFeeWalletClosureTest.java @@ -0,0 +1,183 @@ +package com.baiyue.finance.service; + +import com.baiyue.finance.client.FinanceFileClient; +import com.baiyue.finance.dto.FinanceWalletDtos.*; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.h2.jdbcx.JdbcDataSource; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.datasource.DataSourceTransactionManager; +import org.springframework.transaction.support.TransactionTemplate; + +import java.math.BigDecimal; +import java.nio.file.Files; +import java.nio.file.Path; +import java.time.LocalDate; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.regex.Pattern; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.*; + +/** 真实资金服务、隔离数据库及事务验证海外费用释放、实扣与重复操作。 */ +class OverseasFeeWalletClosureTest { + private JdbcTemplate jdbc; + private FinanceWalletService wallet; + private TransactionTemplate transaction; + + /** 复用正式资金表定义,所有造数只进入本测试独立内存库。 */ + @BeforeEach void setup() throws Exception { + var dataSource = new JdbcDataSource(); + dataSource.setURL("jdbc:h2:mem:" + UUID.randomUUID() + ";MODE=MySQL;DATABASE_TO_LOWER=TRUE;DB_CLOSE_DELAY=-1"); + jdbc = new JdbcTemplate(dataSource); + transaction = new TransactionTemplate(new DataSourceTransactionManager(dataSource)); + var sql = Files.readString(Path.of("src/main/resources/db/finance.sql")); + for (var table : List.of("fin_wallet_account", "fin_wallet_hold", "fin_wallet_ledger", "fin_idempotency_record", "fin_audit_log")) { + var matcher = Pattern.compile("(?s)CREATE TABLE IF NOT EXISTS " + table + " \\(.*?\\) ENGINE=[^;]+;").matcher(sql); + assertThat(matcher.find()).as(table).isTrue(); + // H2 使用同一列及约束,但去除 MySQL 引擎与更新时间自动生成,避免多列生成键差异。 + var ddl = matcher.group().replaceAll("(?s) ENGINE=.*", "") + .replaceAll("(?i) UNSIGNED", "") + .replaceAll("(?i) ON UPDATE CURRENT_TIMESTAMP\\(3\\)", "") + .replaceAll("(?i) DEFAULT CURRENT_TIMESTAMP\\(3\\)", "") + .replaceAll("(?i)((?:created_at|updated_at) DATETIME\\(3\\)) NOT NULL", "$1 NULL"); + jdbc.execute(ddl); + } + var numbers = mock(FinanceDocumentNumberGenerator.class); + var sequence = new AtomicInteger(); + when(numbers.next(anyString())).thenAnswer(call -> "AC-TEST-" + sequence.incrementAndGet()); + wallet = new FinanceWalletService(jdbc, new ObjectMapper().findAndRegisterModules(), + mock(FinanceFileClient.class), numbers, "test", "", "test", "test"); + jdbc.update("INSERT INTO fin_wallet_account(customer_id,currency_code,actual_balance,frozen_balance,available_balance,credit_limit,credit_used,credit_frozen,credit_expires_on,version,created_by,updated_by) VALUES(9,'CNY',60,0,160,100,0,0,?,0,0,0)", LocalDate.now().plusDays(7)); + } + + /** 驳回后释放原冻结,实际余额和授信均恢复可用且不发生消费。 */ + @Test void rejectionReleasesCashAndCreditExactlyOnce() { + freeze(); + assertThat(wallet.account(9).frozenBalance()).isEqualByComparingTo("80.00"); + assertThat(wallet.account(9).creditFrozen()).isEqualByComparingTo("20.00"); + var request = new WalletHoldActionRequest("金额录错,驳回费用"); + transaction.execute(status -> wallet.release(9, "OVERSEAS_MANUAL_FEE", "701-1", "release", request)); + transaction.execute(status -> wallet.release(9, "OVERSEAS_MANUAL_FEE", "701-1", "release", request)); + var account = wallet.account(9); + assertThat(account.actualBalance()).isEqualByComparingTo("60.00"); + assertThat(account.frozenBalance()).isZero(); assertThat(account.creditFrozen()).isZero(); + assertThat(account.availableBalance()).isEqualByComparingTo("160.00"); + assertThat(count("CONSUMPTION")).isZero(); assertThat(count("UNFREEZE")).isEqualTo(1); + } + + /** 扣款事务失败后可重试原请求,多次点击或不同请求键均不重复消费。 */ + @Test void failedSettlementRollsBackAndRetryConsumesOnlyOnce() { + freeze(); + var request = new WalletHoldActionRequest("审核通过扣款"); + assertThatThrownBy(() -> transaction.execute(status -> { + wallet.settle(9, "OVERSEAS_MANUAL_FEE", "701-1", "settle", request); + throw new IllegalStateException("模拟扣款事务提交前异常"); + })).hasMessageContaining("模拟扣款"); + assertThat(wallet.account(9).actualBalance()).isEqualByComparingTo("60.00"); + assertThat(count("CONSUMPTION")).isZero(); + for (int repeat = 0; repeat < 3; repeat++) + transaction.execute(status -> wallet.settle(9, "OVERSEAS_MANUAL_FEE", "701-1", "settle", request)); + assertThatThrownBy(() -> transaction.execute(status -> wallet.settle(9, "OVERSEAS_MANUAL_FEE", "701-1", "another-key", request))) + .hasMessageContaining("已处理"); + assertThat(wallet.account(9).actualBalance()).isZero(); + assertThat(wallet.account(9).creditUsed()).isEqualByComparingTo("20.00"); + assertThat(wallet.account(9).frozenBalance()).isZero(); assertThat(count("CONSUMPTION")).isEqualTo(1); + } + + /** 第一版错扣退回复用手工充值;原因、关联单号和操作人进入资金流水。 */ + @Test void manualCreditRetainsCorrectionReasonAndReplaysWithoutDoubleCredit() { + var request = new AccountAdjustmentRequest("RECHARGE", new BigDecimal("12.00"), LocalDate.now(), + "OTHER", "TR-001 / 费用701", "退回多收操作费", 0); + var first = transaction.execute(status -> wallet.adjust(9, 7, "财务员", "correction-credit", request)); + var second = transaction.execute(status -> wallet.adjust(9, 7, "财务员", "correction-credit", request)); + assertThat(second.ledgerId()).isEqualTo(first.ledgerId()); + assertThat(first.remark()).isEqualTo("退回多收操作费"); + assertThat(first.referenceNo()).isEqualTo("TR-001 / 费用701"); + assertThat(first.operatorName()).isEqualTo("财务员"); + assertThat(wallet.account(9).actualBalance()).isEqualByComparingTo("72.00"); + assertThat(count("RECHARGE")).isEqualTo(1); + } + + /** 部分退款优先还授信,累计退款不能超过原消费,原消费事实保持不变。 */ + @Test void linkedRefundRestoresCreditThenCashAndCannotExceedOriginalConsumption() { + freeze(); + var hold = transaction.execute(status -> wallet.settle(9, "OVERSEAS_MANUAL_FEE", "701-1", "settle", new WalletHoldActionRequest("审核扣款"))); + long original = hold.actionLedgerId(); + var first = transaction.execute(status -> wallet.refundConsumption(9, original, "refund-1", "refund-key-1", new BigDecimal("25"), 7, "财务", "多收退款")); + var replay = transaction.execute(status -> wallet.refundConsumption(9, original, "refund-1", "refund-key-1", new BigDecimal("25"), 7, "财务", "多收退款")); + assertThat(replay.ledgerId()).isEqualTo(first.ledgerId()); + assertThat(first.changeType()).isEqualTo("REFUND"); + assertThat(first.sourceType()).isEqualTo("BUSINESS_REFUND"); + assertThat(first.referenceNo()).isEqualTo(jdbc.queryForObject("SELECT business_no FROM fin_wallet_ledger WHERE id=?", String.class, original)); + assertThat(wallet.account(9).creditUsed()).isZero(); + assertThat(wallet.account(9).actualBalance()).isEqualByComparingTo("5.00"); + assertThatThrownBy(() -> transaction.execute(status -> wallet.refundConsumption(9, original, "refund-1", "refund-key-1", BigDecimal.ONE, 7, "财务", "变更金额"))).hasMessageContaining("幂等"); + assertThatThrownBy(() -> transaction.execute(status -> wallet.refundConsumption(9, original, "refund-too-much", "refund-too-much", new BigDecimal("56"), 7, "财务", "退款"))).hasMessageContaining("超过"); + transaction.execute(status -> wallet.refundConsumption(9, original, "refund-2", "refund-key-2", new BigDecimal("55"), 7, "财务", "余款退款")); + assertThat(wallet.account(9).actualBalance()).isEqualByComparingTo("60.00"); + assertThat(count("REFUND")).isEqualTo(2); + assertThat(count("CONSUMPTION")).isEqualTo(1); + assertThat(jdbc.queryForObject("SELECT amount FROM fin_wallet_ledger WHERE id=?", BigDecimal.class, original)).isEqualByComparingTo("-80.00"); + } + + /** 非消费流水、跨客户原流水及非正金额均不能改变余额。 */ + @Test void linkedRefundRejectsWrongCustomerNonConsumptionAndInvalidAmount() { + freeze(); + long frozenLedger = jdbc.queryForObject("SELECT id FROM fin_wallet_ledger WHERE change_type='FREEZE'", Long.class); + assertThatThrownBy(() -> transaction.execute(status -> wallet.refundConsumption(9, frozenLedger, "bad", "bad", BigDecimal.ONE, 7, "财务", "退款"))).hasMessageContaining("成功业务扣款"); + var hold = transaction.execute(status -> wallet.settle(9, "OVERSEAS_MANUAL_FEE", "701-1", "settle", new WalletHoldActionRequest("扣款"))); + jdbc.update("INSERT INTO fin_wallet_account(customer_id,currency_code,actual_balance,frozen_balance,available_balance,credit_limit,credit_used,credit_frozen,version,created_by,updated_by) VALUES(10,'CNY',0,0,0,0,0,0,0,0,0)"); + assertThatThrownBy(() -> transaction.execute(status -> wallet.refundConsumption(10, hold.actionLedgerId(), "wrong-customer", "wrong-customer", BigDecimal.ONE, 7, "财务", "退款"))).hasMessageContaining("不存在"); + assertThatThrownBy(() -> transaction.execute(status -> wallet.refundConsumption(9, hold.actionLedgerId(), "zero", "zero", BigDecimal.ZERO, 7, "财务", "退款"))).isInstanceOf(RuntimeException.class); + assertThatThrownBy(() -> transaction.execute(status -> wallet.refundConsumption(9, hold.actionLedgerId(), "blank", "blank", BigDecimal.ONE, 7, "财务", " "))).hasMessageContaining("原因"); + assertThat(count("REFUND")).isZero(); + assertThat(wallet.account(9).creditUsed()).isEqualByComparingTo("20.00"); + assertThat(wallet.account(10).actualBalance()).isZero(); + } + + /** 不同请求键的并发退款同样受原消费可退余额约束。 */ + @Test void concurrentRefundsSerializeOnAccountAndNeverOverRefund() throws Exception { + freeze(); + var hold = transaction.execute(status -> wallet.settle(9, "OVERSEAS_MANUAL_FEE", "701-1", "settle", new WalletHoldActionRequest("扣款"))); + var start = new java.util.concurrent.CountDownLatch(1); + var executor = java.util.concurrent.Executors.newFixedThreadPool(2); + try { + var results = new java.util.ArrayList>(); + for (int i = 0; i < 2; i++) { + final String reference = "concurrent-" + i; + results.add(executor.submit(() -> { + start.await(); + try { + transaction.execute(status -> wallet.refundConsumption(9, hold.actionLedgerId(), reference, reference, new BigDecimal("50"), 7, "财务", "并发退款")); + return true; + } catch (FinanceEventException ex) { + assertThat(ex.code()).isEqualTo("FIN-WALLET-REFUND-EXCEEDED"); + return false; + } + })); + } + start.countDown(); + int succeeded = 0; + for (var result : results) if (result.get(10, java.util.concurrent.TimeUnit.SECONDS)) succeeded++; + assertThat(succeeded).isEqualTo(1); + assertThat(count("REFUND")).isEqualTo(1); + assertThat(wallet.account(9).actualBalance()).isEqualByComparingTo("30.00"); + } finally { executor.shutdownNow(); } + } + + /** 冻结始终沿用同一海外费用业务身份。 */ + private void freeze() { + transaction.execute(status -> wallet.freeze(9, "freeze", new WalletFreezeRequest( + new BigDecimal("80.00"), "OVERSEAS_MANUAL_FEE", "701-1", "海外费用"))); + } + + /** 按真实流水类型统计资金影响,避免只检查服务返回状态。 */ + private int count(String type) { + return jdbc.queryForObject("SELECT COUNT(*) FROM fin_wallet_ledger WHERE customer_id=9 AND change_type=?", Integer.class, type); + } +} diff --git a/firstleg/AGENTS.md b/firstleg/AGENTS.md index f3dd81425..08a4de075 100644 --- a/firstleg/AGENTS.md +++ b/firstleg/AGENTS.md @@ -32,8 +32,8 @@ | 称重确认、整票库位块、部分拣货结算 | `service/MeasurementLocationOccupationService.java`、`service/MeasurementSubmissionService.java`;查“首张称重图片”“删除称重图片”“PDA 部分拣货” | | 审核与初始计价 | `service/MeasurementReviewService.java`、`service/InitialInboundPricingService.java`;查“测量审核通过” | | 客户四档箱规与默认算法 | `service/CustomerDimensionConfigService.java`、`service/CustomerDimensionWeightingService.java`;查“客户箱规配置” | -| FBA 计价、客户专属价格簿、柜级阶梯运费 | `service/FbaPricingService.java`、`service/PlatformPriceBookService.java`、`pricing/ContainerTierAmountAllocator.java`;查“客户专属价格簿、柜级阶梯运费与调价边界” | -| 费用项目、价格簿查询内部切片 | `controller/PlatformFeeItemController.java`、`pricebookquery/application/PriceBookQueryService.java`;查“价格簿查询内部切片” | +| FBA 计价、私人地址人工报价、客户价格簿与柜级阶梯运费 | `service/FbaPricingService.java`、`service/PlatformPriceBookService.java`、`pricing/ContainerTierAmountAllocator.java`;查“私人地址”“客户专属价格簿、柜级阶梯运费与调价边界” | +| 费用项目、共享费用目录与价格簿查询内部切片 | `controller/PlatformFeeItemController.java`、`controller/InternalFeeCatalogController.java`、`pricebookquery/application/PriceBookQueryService.java`;共享目录契约 `src/main/resources/openapi/fee-catalog-internal-v1.yaml`,价格簿查“价格簿查询内部切片” | | 入库费用、客户确认、直接已付 | `service/InboundFeeDetailService.java`、`service/MerchantInboundBillingService.java`、`service/FeePaymentSnapshotValidator.java`;查“客户入库费用”“DIRECT_PAID” | | 柜次费用、跨客户分摊与财务快照 | `service/ContainerFeeDetailService.java`、`controller/InternalFinanceFeeSourceController.java`、`loading/service/FinanceCostSnapshotService.java`;查“柜次费用明细与跨客户分摊组” | | 分柜、预装、正式装柜清单生命周期 | `service/LoadingListService.java`、`split/controller/PlatformSplitPreloadController.java`;查“分柜、预装”“预装进入” | diff --git a/firstleg/docs/business-index.md b/firstleg/docs/business-index.md index e9b8b8f95..2509dab84 100644 --- a/firstleg/docs/business-index.md +++ b/firstleg/docs/business-index.md @@ -30,8 +30,8 @@ | 墨家设备测量上传 | `controller/OpenApiInboundMeasurementController.java`、`dto/MojiaMeasurementDtos.java`、`service/MojiaMeasurementOpenApiService.java`、`security/MeasurementRequestAuthenticator.java`;接口契约见 `src/main/resources/openapi/firstleg-v1.yaml` 与 `../../docs/墨家设备测量上传接口说明-V3.md` | | 称重确认与整票库位块 | `service/MeasurementLocationOccupationService.java` 的 `ensureForConfirmedWeight/ensureForSubmission`、`InboundMeasurementService.java`、`MeasurementSubmissionService.java`、`client/WarehousePalletLocationClient.java`;Warehouse 块服务见 `../warehouse/src/main/java/com/baiyue/warehouse/service/LocationBlockService.java`,接口与上架引导见 `src/main/resources/openapi/firstleg-v1.yaml` | | 审核与初始计价 | `controller/PlatformInboundMeasurementReviewController.java`、`service/MeasurementReviewService.java`、`service/InitialInboundPricingService.java`、`dto/MeasurementApprovalResponse.java` | -| 客户四档箱规与默认算法 | `controller/PlatformCustomerDimensionConfigController.java`、`service/CustomerDimensionConfigService.java`、`repository/CustomerDimensionConfigRepository.java`;固定 `SMALL/MEDIUM/LARGE/EXTRA_LARGE`,默认算法只允许 `ADD/MULTIPLY` | -| FBA 主数据/计费与客户规则 | `controller/PlatformFbaMasterDataController.java`、`PlatformFbaPricingController.java`、`service/FbaPricingService.java`、`repository/FbaPricingRepository.java`;客户规则可保存多个版本,但每个客户仅一条默认规则,计算请求可提交已确认的重量/体积选择值 | +| 客户四档箱规与默认算法 | `controller/PlatformCustomerDimensionConfigController.java`、`service/CustomerDimensionConfigService.java`、`repository/CustomerDimensionConfigRepository.java`;固定 `SMALL/MEDIUM/LARGE/EXTRA_LARGE`,默认 `ACTUAL` 不调整尺寸,可启用的调整算法为 `ADD/MULTIPLY` | +| FBA 主数据/计费与客户规则 | `controller/PlatformFbaMasterDataController.java`、`PlatformFbaPricingController.java`、`service/FbaPricingService.java`、`repository/FbaPricingRepository.java`;客户规则可保存多个版本,但每个客户仅一条默认规则,计算请求可提交已确认的重量/体积选择值;私人地址 `PRIVATE` 使用 `/fees/manual-quote` 生成人工头程报价,按客户计费方式计算计费量且附加费为零,预览返回 `priceMatched=false`,报价后沿用费用版本、幂等与预装前初始费用校验;`POST /fees/preview` 与正式计价共用算法,只读返回当前选择值的计费量、参考单价和合计,不生成费用版本或改变履约、财务状态 | | 客户专属价格簿、柜级阶梯运费与调价边界 | `controller/PlatformPriceBookController.java`、`service/PlatformPriceBookService.java`、`pricing/ActivePriceBookEstimator.java`、`service/FbaPricingService.java`、`pricing/ContainerTierAmountAllocator.java`;公共价格簿固定 `customerId=0`,客户专属版本按客户独立发布与替换,计价优先专属 ACTIVE、缺失才回退公共 ACTIVE。`REGION_WEIGHT_TIER` 只对同柜同客户中当前费用快照同为区域重量阶梯的有效成员聚合实际计费量命中一个阶梯,再按这些成员的计费量守恒分摊;同柜允许与 `FBA_WAREHOUSE_RATE` 等固定模型混装,固定模型成员不得参与阶梯聚合或被重算。装柜成员变化调用 `recalculateUnlockedContainerCustomerFees`,已确认/已发送或客户已确认的版本不得改写。人工调整以系统基准量为中心限制上下 10%(含边界),保存版本、原因和二次确认;一件代发 `WAIT_PALLETIZE` 可在预装确认前试算,`markInboundCalculated` 只更新费用并保留该托盘化状态 | | 入库费用明细 | `controller/PlatformInboundFeeDetailController.java`、`service/InboundFeeDetailService.java`、`dto/InboundFeeDetailDtos.java`;`PRIVATE_LAST_MILE_DELIVERY` 是独立私人仓尾程派送费,只允许 `destinationType=PRIVATE`,按稳定费用代码校验而不按中文名判断;Admin 消费端见 `../../apps/admin-web/src/modules/unified-orders/api/inboundFeeDetails.ts` | | 入库/柜次直接已付快照与凭证校验 | `service/FeePaymentSnapshotValidator.java`、`client/FeePaymentVoucherFileClient.java`;写入入口分别为 `InboundFeeDetailService.java`、`ContainerFeeDetailService.java`,File 业务类型固定 `FINANCE_VOUCHER` | @@ -47,7 +47,7 @@ | 货柜与全局智能排柜 | `controller/PlatformContainerController.java`、`PlatformSmartLoadingController.java`、`service/SmartLoadingService.java`、`repository/SmartLoadingRepository.java`、`client/PendingReviewOrderClient.java`、`loading/`;全局候选合并本服务履约投影与 Order `/internal/order/v1/pending-review-orders` 权威待审核页,工作台柜次从 `/inbound-candidates`、`/containers` 进入;普通箱数、待审核占位、同柜组和全部未拣移除分别走 `/inbound-assignments`、`/pending-review-assignments`、`/group-assignments`、`/unpicked-items`,明细规格拆分留在服务端 | | 货柜资料、PDA 证据与业务总览 | `controller/PlatformContainerController.java` 的 `/{id}/overview`、`/{id}/documents/{documentType}`,聚合 `mapper/ContainerOverviewMapper.java`;契约 `src/main/resources/openapi/phase3-container-overview-v1.yaml`,四类资料固定为目的港清关、国内出口报关、DO、PU 的当前唯一版本 | | PDA 拣货与装柜、证据幂等上传 | `controller/PlatformLoadingPdaController.java`、`loading/controller/PlatformLoadingDeliveryController.java`、`loading/service/CabinetLoadingEvidenceService.java`;证据写入和 multipart 上传均先以任务内幂等键匹配请求哈希,上传哈希覆盖仓库、版本、证据类型、锁码、备注、文件名、媒体类型、大小与文件内容;完全重放返回当前视图且不得再次上传 File,相同键不同内容返回冲突 | -| 装柜完成直连事件 | `service/LoadingCompletedOutboxService.java`、`LoadingCompletedOutboxRelay.java`、`repository/LoadingCompletedOutboxRepository.java`、`mapper/LoadingCartonManifestMapper.java`、`loading/mapper/CabinetLoadingEvidenceMapper.java`、`client/LastlegEventClient.java`;当前发布 V3,从 `fl_inbound_order.destination_type` 冻结 `orders[].destinationType`(`FBA` 或 `OVERSEAS`),OVERSEAS 按最终实装与稳定箱码冻结,FBA 可仅冻结箱数,并冻结物理柜五类有效装柜照片引用;真实生产验收 `src/test/java/com/baiyue/firstleg/service/TaillegRealProducerAcceptanceTest.java`;事件模型见 `../common-event-contract/AGENTS.md` | +| 装柜完成直连事件 | `service/LoadingCompletedOutboxService.java`、`LoadingCompletedOutboxRelay.java`、`repository/LoadingCompletedOutboxRepository.java`、`mapper/LoadingCartonManifestMapper.java`、`loading/mapper/CabinetLoadingEvidenceMapper.java`、`client/LastlegEventClient.java`;当前发布 V3,从 `fl_inbound_order.destination_type` 冻结 `orders[].destinationType`(`FBA` 或 `OVERSEAS`),国内 PRIVATE 按 OVERSEAS 交接并保留私人地址及箱码快照,同一入仓单的原始目的类型必须唯一;OVERSEAS 按最终实装与稳定箱码冻结,FBA 可仅冻结箱数,并冻结物理柜五类有效装柜照片引用;真实生产验收 `src/test/java/com/baiyue/firstleg/service/TaillegRealProducerAcceptanceTest.java`;事件模型见 `../common-event-contract/AGENTS.md` | | 海外卸柜完成事件回写 | 内部入口 `controller/InternalOverseasContainerEventController.java`、消费者 `service/OverseasContainerEventConsumer.java`、Inbox `entity/ContainerEventInboxEntity.java` 与 `mapper/ContainerEventInboxMapper.java`;事件模型见 `../common-event-contract/AGENTS.md` | | 海外暂扣状态回写 | 同一内部事件入口、消费者 `service/OverseasInboundOrderDetainedConsumer.java`;按来源入仓单 ID 更新 `fl_inbound_order.hold_flag/current_hold_reason`,复用跨区 Inbox 并写 `fl_inbound_hold_record` | | 海外派送完成回写 | 同一内部事件入口、消费者 `service/OverseasDeliveryCompletedConsumer.java`;按来源入仓单、订单、入仓码和 FBA 仓校验后推进当前入仓单 `LOADED→DELIVERED`,同一 Order 全部有效入仓单完成后才经 `OrderFulfillmentCompletionClient` 推进 Order `COMPLETED` | @@ -76,7 +76,7 @@ - 稳定箱码映射以入库快照和 Order 货物明细为事实;订单审核创建入库单时必须幂等生成,打印接口只复用或补齐同一映射,用户是否下载箱唛不得决定箱码有效性。手工测量、墨家设备和 PDA 扫码统一通过 `InboundCartonLabelService` 校验客户、入库单、货物明细、总箱数/下标和有效批次,不得在测量时临时伪造箱码或接受跨客户、跨入库箱码。 - 一件代发以 `fl_inbound_carton_label_sku` 作为物理箱内不可变 SKU 快照;一个稳定 `box_code` 可以对应多条 SKU,PDA 扫箱后必须一次清点全部 SKU,不得将 SKU 塞入二维码或以 SKU 替代箱码。实际打托后的查询必须能按“现场托盘 -> 客户 -> SKU -> 箱数”聚合;同一国内作业仓的多票允许共用一个开放托盘,单票全部归托不自动封托,只有参与该票装箱的开放托盘阻止该票完成,预生成但未使用的空托盘不得阻塞;禁止按货名、箱规或箱号猜测 SKU。修改本链路时同步检查 `OrderCargoClient`、Order 内部货物接口及 PDA 打托实现。 - 测量审核通过后直接进入 `WAIT_SPLIT`,费用仅作为独立初算和人工维护结果,不得阻塞或回退订单主流程;计费量、单价和附加费调整必须版本化并由服务端重算金额。状态或 DTO 变更时逐个检查 Order/Warehouse/File/Integration 消费者。 -- 客户箱规配置必须恰好保持四档且默认算法在四行一致;`ADD` 以实测尺寸为基准对三个维度分别增加命中箱规的尺寸值,`MULTIPLY` 使用当前客户唯一默认计费规则,并由服务端按规则中的规格档位选择系数。测量行必须保存实际尺寸、选用规则和算法快照,页面不得自行重算后覆盖服务端结果;计价使用用户确认的重量/体积选择值并纳入幂等请求哈希。 +- 客户箱规配置必须恰好保持四档且默认算法在四行一致;`ACTUAL` 不调整尺寸,仓库修正值替换对应实测边;`ADD` 以修正后的实测尺寸为基准对三个维度分别增加命中箱规的尺寸值;`MULTIPLY` 使用选定的启用系数配置,未指定配置时取当前客户唯一默认配置,由服务端按规格档位选择系数并分别作用于长宽高,体积按系数三次方变化。设备与人工录入共用计算入口,行明确选择的算法优先,否则仅使用客户默认算法;预置或保存系数配置不等于启用该算法。测量行必须保存实际尺寸、选用规则和算法快照,修改确认箱数复用已保存单箱快照,页面不得自行重算后覆盖服务端结果;计价使用用户确认的重量/体积选择值并纳入幂等请求哈希。 - 报关、订舱和拖车引用的供应商必须通过 `WarehouseSupplierSnapshotService` 按 ID 向 Warehouse 校验:报关使用 `CUSTOMS`,订舱使用 `BOOKING`(不区分国内/国外 scope),国内拖车使用 `TRUCKING + truckingScope=DOMESTIC`,清关提柜/目的港码头拖车使用 `TRUCKING + truckingScope=OVERSEAS`;供应商必须为 `ACTIVE`。请求中的名称只能作为一致性提示,持久化名称必须来自 Warehouse 快照,禁止用自由文本绕过 ID、类别或启用状态校验。修改任一供应商引用 DTO、OpenAPI 或内部客户端时,必须同步检查三条业务链、Warehouse `supplier-v1.yaml`、数据库快照字段和对应契约测试。 - Order 渠道变更门禁必须检查装柜或后续状态、PDA task、预装、分柜及同柜组兼容性;任何不确定结果均失败关闭。`ORDER_DECLARATION_UPDATED_V1` 消费必须校验订单号/入仓码归属,永久保留 `eventId + payload hash` 幂等历史并按版本单调更新申报投影;不得覆盖仓库实测、箱码、测量审核或已确认费用。 - 订舱状态固定为 `DRAFT → RELEASED`:草稿允许资料不完整并可替换或删除,放舱时校验完整持久化聚合但不要求订舱附件,放舱后除预计到港外的运输、预报货量、三方和柜型需求只读;预计到港允许通过 `/estimated-arrival` 按编辑权限、版本与幂等键单独维护,保留前后审计快照且不得清空或早于预计开船,不改实际到港或计划到仓;三项实际货量允许通过 `OceanBookingService.updateActualCargo` 按编辑权限、版本、幂等键和原因补录并保存前后审计快照。创建草稿和放舱都只维护订舱聚合、三类参与方及柜型需求,不创建真实 `containerNo`、货柜行或分配关系;真实货柜只在后续提柜关联时占用已放舱柜型需求。 diff --git a/firstleg/src/main/java/com/baiyue/firstleg/controller/InternalFeeCatalogController.java b/firstleg/src/main/java/com/baiyue/firstleg/controller/InternalFeeCatalogController.java new file mode 100644 index 000000000..6254de465 --- /dev/null +++ b/firstleg/src/main/java/com/baiyue/firstleg/controller/InternalFeeCatalogController.java @@ -0,0 +1,58 @@ +package com.baiyue.firstleg.controller; + +import com.baiyue.common.core.api.ApiResponse; +import com.baiyue.common.core.api.ResultCode; +import com.baiyue.common.core.exception.BizException; +import com.baiyue.firstleg.service.SharedFeeCatalogService; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.util.List; + +/** 公共费用目录内部只读入口,禁止借用平台员工令牌跨服务查询。 */ +@RestController +@RequestMapping("/internal/firstleg/v1/fee-items") +public class InternalFeeCatalogController { + private final SharedFeeCatalogService service; + private final byte[] expectedToken; + + /** 绑定费用目录服务与内部身份凭证,空凭证始终拒绝访问。 */ + public InternalFeeCatalogController(SharedFeeCatalogService service, + @Value("${baiyue.security.internal-service-token:}") String token) { + this.service = service; + this.expectedToken = token.getBytes(StandardCharsets.UTF_8); + } + + /** 返回适用海外仓收付方向的启用项目。 */ + @GetMapping + public ApiResponse> list( + @RequestHeader(value = "X-Internal-Service-Token", required = false) String token, + @RequestParam(defaultValue = "RECEIVABLE") String direction) { + requireToken(token); + return ApiResponse.success(service.list(direction)); + } + + /** 在费用创建时验证实时项目资格。 */ + @GetMapping("/{code}") + public ApiResponse detail( + @RequestHeader(value = "X-Internal-Service-Token", required = false) String token, + @PathVariable String code, @RequestParam String direction) { + requireToken(token); + return ApiResponse.success(service.requireActive(code, direction)); + } + + /** 使用常量时间比较内部凭证,未配置凭证时关闭入口。 */ + private void requireToken(String token) { + if (expectedToken.length == 0 || token == null + || !MessageDigest.isEqual(expectedToken, token.getBytes(StandardCharsets.UTF_8))) { + throw new BizException(ResultCode.FORBIDDEN, "内部服务凭证无效"); + } + } +} diff --git a/firstleg/src/main/java/com/baiyue/firstleg/controller/InternalInboundOrderController.java b/firstleg/src/main/java/com/baiyue/firstleg/controller/InternalInboundOrderController.java index 0059b8b86..b49d52d86 100644 --- a/firstleg/src/main/java/com/baiyue/firstleg/controller/InternalInboundOrderController.java +++ b/firstleg/src/main/java/com/baiyue/firstleg/controller/InternalInboundOrderController.java @@ -37,6 +37,16 @@ import java.security.MessageDigest; @RestController @RequestMapping("/internal/firstleg/v1/inbounds") public class InternalInboundOrderController { + /** 海外废弃溯源只读取已生成的有效箱唛,不触发打印或补建。 */ + @org.springframework.web.bind.annotation.GetMapping("/{inboundId}/existing-carton-marks") + public ApiResponse> existingCartonMarks( + @RequestHeader(value = "X-Internal-Service-Token", required = false) String token, + @org.springframework.web.bind.annotation.PathVariable long inboundId, + @org.springframework.web.bind.annotation.RequestParam long customerId, + @org.springframework.web.bind.annotation.RequestParam String inboundCode) { + requireToken(token); + return ApiResponse.success(cartonLabels.existingCartonMarks(customerId, inboundId, inboundCode)); + } @Resource private InboundOrderCreationService service; @Resource diff --git a/firstleg/src/main/java/com/baiyue/firstleg/controller/PlatformFbaPricingController.java b/firstleg/src/main/java/com/baiyue/firstleg/controller/PlatformFbaPricingController.java index c3c315a72..7e18a893a 100644 --- a/firstleg/src/main/java/com/baiyue/firstleg/controller/PlatformFbaPricingController.java +++ b/firstleg/src/main/java/com/baiyue/firstleg/controller/PlatformFbaPricingController.java @@ -7,6 +7,8 @@ import com.baiyue.common.core.api.ApiResponse; import com.baiyue.common.security.AuthenticatedUser; import com.baiyue.firstleg.dto.FbaPricingDtos.CalculateRequest; import com.baiyue.firstleg.dto.FbaPricingDtos.FeeSnapshotResponse; +import com.baiyue.firstleg.dto.FbaPricingDtos.FeePreviewResponse; +import com.baiyue.firstleg.dto.FbaPricingDtos.FeePreviewRequest; import com.baiyue.firstleg.dto.FbaPricingDtos.AdjustFeeRequest; import com.baiyue.firstleg.dto.FbaPricingDtos.ManualQuoteRequest; import com.baiyue.firstleg.service.FbaPricingService; @@ -34,6 +36,16 @@ public class PlatformFbaPricingController { @Resource private FbaPricingService service; + /** 预览当前重量、体积对应的计费量和价格,不生成费用版本或改变履约状态。 */ + @PostMapping("/preview") + @PreAuthorize("hasAnyAuthority('firstleg:inbound:confirm-send-fee', 'firstleg:fba-pricing:calculate')") + public ApiResponse preview( + @AuthenticationPrincipal AuthenticatedUser user, @RequestParam long tenantId, + @PathVariable long inboundId, @Valid @RequestBody FeePreviewRequest request) { + long scope = FirstlegScopeGuard.platform(user, tenantId); + return ApiResponse.success(service.preview(scope, inboundId, request)); + } + @PostMapping("/calculate") @PreAuthorize("hasAuthority('firstleg:fba-pricing:calculate')") @BusinessAudit(event = "FBA_FEE_CALCULATE", resourceType = "INBOUND", resourceId = "#inboundId", @@ -75,11 +87,12 @@ public class PlatformFbaPricingController { return ApiResponse.success(service.confirm(scope, user.userId(), inboundId, idempotencyKey)); } + /** 按费用发送权限发布已确认费用,审核门禁由服务端校验且不推进履约状态。 */ @PostMapping("/send") @PreAuthorize("hasAnyAuthority('firstleg:inbound:confirm-send-fee'," + " 'firstleg:inbound:send-fee')") @BusinessAudit(event = "FBA_FEE_SEND", resourceType = "INBOUND", resourceId = "#inboundId", - description = "发送FBA费用快照并进入待托盘状态") + description = "发送FBA费用快照") public ApiResponse send( @AuthenticationPrincipal AuthenticatedUser user, @RequestParam long tenantId, @PathVariable long inboundId, @RequestHeader("Idempotency-Key") String idempotencyKey) { diff --git a/firstleg/src/main/java/com/baiyue/firstleg/customs/container/mapper/ContainerCustomsMapper.java b/firstleg/src/main/java/com/baiyue/firstleg/customs/container/mapper/ContainerCustomsMapper.java index 38353542b..e5d76e11e 100644 --- a/firstleg/src/main/java/com/baiyue/firstleg/customs/container/mapper/ContainerCustomsMapper.java +++ b/firstleg/src/main/java/com/baiyue/firstleg/customs/container/mapper/ContainerCustomsMapper.java @@ -35,6 +35,13 @@ public interface ContainerCustomsMapper { f.file_hash fileHash,f.content_type contentType,f.size_bytes sizeBytes,f.uploaded_by uploadedBy, f.uploaded_by_name uploadedByName,f.uploaded_at uploadedAt,f.version version """; + String CONTAINER_COLUMNS = """ + id id,loading_plan_id loadingPlanId,container_booking_id containerBookingId,status status, + started_by startedBy,started_by_name startedByName,started_at startedAt, + completed_by completedBy,completed_by_name completedByName,completed_at completedAt, + broker_submitted_by brokerSubmittedBy,broker_submitted_by_name brokerSubmittedByName, + broker_submitted_at brokerSubmittedAt,created_at createdAt,updated_at updatedAt,version version + """; @Select(""" SELECT DISTINCT p.id loadingPlanId,p.plan_no planNo,p.container_booking_id containerBookingId, @@ -132,19 +139,19 @@ public interface ContainerCustomsMapper { int insertContainer(@Param("id") long id,@Param("planId") long planId, @Param("containerBookingId") long containerBookingId,@Param("status") String status); - @Select("SELECT * FROM fl_customs_container WHERE loading_plan_id=#{planId}") + @Select("SELECT " + CONTAINER_COLUMNS + " FROM fl_customs_container WHERE loading_plan_id=#{planId}") ContainerRow container(@Param("planId") long planId); - @Select("SELECT * FROM fl_customs_container WHERE id=#{id}") + @Select("SELECT " + CONTAINER_COLUMNS + " FROM fl_customs_container WHERE id=#{id}") ContainerRow containerById(@Param("id") long id); - @Select("SELECT * FROM fl_customs_container WHERE container_booking_id=#{containerBookingId}") + @Select("SELECT " + CONTAINER_COLUMNS + " FROM fl_customs_container WHERE container_booking_id=#{containerBookingId}") ContainerRow containerByBooking(@Param("containerBookingId") long containerBookingId); - @Select("SELECT * FROM fl_customs_container WHERE loading_plan_id=#{planId} FOR UPDATE") + @Select("SELECT " + CONTAINER_COLUMNS + " FROM fl_customs_container WHERE loading_plan_id=#{planId} FOR UPDATE") ContainerRow lockContainer(@Param("planId") long planId); - @Select("SELECT * FROM fl_customs_container WHERE id=#{id} FOR UPDATE") + @Select("SELECT " + CONTAINER_COLUMNS + " FROM fl_customs_container WHERE id=#{id} FOR UPDATE") ContainerRow lockContainerById(@Param("id") long id); @Update(""" diff --git a/firstleg/src/main/java/com/baiyue/firstleg/customs/container/service/ContainerCustomsService.java b/firstleg/src/main/java/com/baiyue/firstleg/customs/container/service/ContainerCustomsService.java index 5e204711a..6e8c632c6 100644 --- a/firstleg/src/main/java/com/baiyue/firstleg/customs/container/service/ContainerCustomsService.java +++ b/firstleg/src/main/java/com/baiyue/firstleg/customs/container/service/ContainerCustomsService.java @@ -254,6 +254,9 @@ public class ContainerCustomsService { throw conflict("当前柜尚未完成首次发送报关行资料"); } CompletionRow completion = mapper.lockCompletion(documentId); + if (completion != null && "CONFIRMED".equals(completion.customerConfirmStatus())) { + throw conflict("客户已确认预录单,不可再次上传"); + } CustomsFileClient.Metadata metadata = upload(source.tenantId(), RECEIPT_FILE_TYPE, Long.toString(container.loadingPlanId()), operatorId, file); long completionId; int version; diff --git a/firstleg/src/main/java/com/baiyue/firstleg/dto/FbaPricingDtos.java b/firstleg/src/main/java/com/baiyue/firstleg/dto/FbaPricingDtos.java index e6401d4bc..3d6d33960 100644 --- a/firstleg/src/main/java/com/baiyue/firstleg/dto/FbaPricingDtos.java +++ b/firstleg/src/main/java/com/baiyue/firstleg/dto/FbaPricingDtos.java @@ -76,6 +76,25 @@ public final class FbaPricingDtos { } } + /** 当前输入的只读预览,人工单价仅用于预览金额,不写入费用。 */ + public record FeePreviewRequest( + LocalDateTime pricingAt, + @DecimalMin(value = "0", inclusive = false) BigDecimal selectedWeightKg, + @DecimalMin(value = "0", inclusive = false) BigDecimal selectedVolumeCbm, + @Size(max = 32) String weightSource, + @Size(max = 32) String volumeSource, + @DecimalMin(value = "0", inclusive = false) BigDecimal selectedUnitPrice + ) {} + + /** 当前选择值的只读计费预览,不包含费用版本或持久化标识。 */ + public record FeePreviewResponse( + BigDecimal actualWeightKg, BigDecimal actualVolumeCbm, + BigDecimal chargeableQuantity, String chargeUnit, + BigDecimal conversionFactor, Integer roundingScale, String roundingMode, + BigDecimal unitPrice, BigDecimal additionalAmount, BigDecimal totalAmount, + String currency, boolean priceMatched + ) {} + public record ManualQuoteRequest( @NotNull @DecimalMin(value = "0", inclusive = false) BigDecimal selectedWeightKg, @NotNull @DecimalMin(value = "0", inclusive = false) BigDecimal selectedVolumeCbm, diff --git a/firstleg/src/main/java/com/baiyue/firstleg/entity/InboundMeasurementItemEntity.java b/firstleg/src/main/java/com/baiyue/firstleg/entity/InboundMeasurementItemEntity.java index fe5aa1ee9..1bee1a9e2 100644 --- a/firstleg/src/main/java/com/baiyue/firstleg/entity/InboundMeasurementItemEntity.java +++ b/firstleg/src/main/java/com/baiyue/firstleg/entity/InboundMeasurementItemEntity.java @@ -1,6 +1,8 @@ package com.baiyue.firstleg.entity; import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.FieldStrategy; +import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Getter; @@ -34,6 +36,7 @@ public class InboundMeasurementItemEntity { private BigDecimal volumeCbm; private BigDecimal totalVolumeCbm; private BigDecimal volumeWeightKg; + @TableField(updateStrategy = FieldStrategy.ALWAYS) private Long chargeRuleId; private String coefficientMode; private String calculationRuleVersion; @@ -44,8 +47,12 @@ public class InboundMeasurementItemEntity { private BigDecimal outerLengthCm; private BigDecimal outerWidthCm; private BigDecimal outerHeightCm; + // 清空修正表示恢复对应实测边,必须显式写入 NULL,不能被默认的非空更新策略忽略。 + @TableField(updateStrategy = FieldStrategy.ALWAYS) private BigDecimal manualOuterLengthCm; + @TableField(updateStrategy = FieldStrategy.ALWAYS) private BigDecimal manualOuterWidthCm; + @TableField(updateStrategy = FieldStrategy.ALWAYS) private BigDecimal manualOuterHeightCm; private String remark; private LocalDateTime createdAt; diff --git a/firstleg/src/main/java/com/baiyue/firstleg/loading/mapper/CabinetLoadingEvidenceMapper.java b/firstleg/src/main/java/com/baiyue/firstleg/loading/mapper/CabinetLoadingEvidenceMapper.java index 688a912b8..4174c6218 100644 --- a/firstleg/src/main/java/com/baiyue/firstleg/loading/mapper/CabinetLoadingEvidenceMapper.java +++ b/firstleg/src/main/java/com/baiyue/firstleg/loading/mapper/CabinetLoadingEvidenceMapper.java @@ -14,7 +14,7 @@ public interface CabinetLoadingEvidenceMapper extends BaseMapper byContainer(@Param("containerBookingId") long containerBookingId); - @Select("SELECT * FROM fl_loading_cabinet_evidence WHERE container_booking_id=#{containerBookingId} AND evidence_type=#{type} AND active_flag=1 LIMIT 1 FOR UPDATE") + @Select("SELECT * FROM fl_loading_cabinet_evidence WHERE container_booking_id=#{containerBookingId} AND evidence_type=#{type} AND active_flag=1 ORDER BY id DESC LIMIT 1 FOR UPDATE") CabinetLoadingEvidenceEntity activeForUpdate(@Param("containerBookingId") long containerBookingId, @Param("type") String type); diff --git a/firstleg/src/main/java/com/baiyue/firstleg/loading/service/CabinetLoadingEvidenceService.java b/firstleg/src/main/java/com/baiyue/firstleg/loading/service/CabinetLoadingEvidenceService.java index d42c07b48..fd6a80ea7 100644 --- a/firstleg/src/main/java/com/baiyue/firstleg/loading/service/CabinetLoadingEvidenceService.java +++ b/firstleg/src/main/java/com/baiyue/firstleg/loading/service/CabinetLoadingEvidenceService.java @@ -74,8 +74,6 @@ public class CabinetLoadingEvidenceService { if (current != null && !Objects.equals(current.getVersion(), expected)) throw conflict("装柜证据版本已变化,请刷新后重试"); if (current != null && Objects.equals(current.getFileId(), request.fileId()) && Objects.equals(current.getSealNo(), sealNo)) return view(scope); - if (current != null && mapper.voidActive(current.getId(), scope.containerBookingId(), current.getVersion(), - "REPLACED", operatorId, operatorName) != 1) throw conflict("装柜证据版本已变化,请刷新后重试"); CabinetLoadingEvidenceEntity value = new CabinetLoadingEvidenceEntity(); value.setContainerBookingId(scope.containerBookingId()); value.setSourceTaskId(taskId); value.setEvidenceType(type); value.setFileId(request.fileId()); value.setFileName(file.fileName()); @@ -84,8 +82,7 @@ public class CabinetLoadingEvidenceService { value.setCreatedBy(operatorId); value.setCreatedByName(operatorName); value.setUpdatedBy(operatorId); value.setUpdatedByName(operatorName); try { mapper.insert(value); } catch (DuplicateKeyException exception) { - // A replacement may already have voided the old row in this transaction. - // Roll it back; a subsequent same-key request can read the committed winner. + // The task-scoped operation key protects concurrent duplicate uploads. throw conflict("装柜证据发生并发变更,请刷新后重试"); } return view(scope); diff --git a/firstleg/src/main/java/com/baiyue/firstleg/mapper/ContainerBookingMapper.java b/firstleg/src/main/java/com/baiyue/firstleg/mapper/ContainerBookingMapper.java index f8d3764ca..af1fe235e 100644 --- a/firstleg/src/main/java/com/baiyue/firstleg/mapper/ContainerBookingMapper.java +++ b/firstleg/src/main/java/com/baiyue/firstleg/mapper/ContainerBookingMapper.java @@ -100,8 +100,8 @@ public interface ContainerBookingMapper extends BaseMapper sealedPalletLines(@Param("palletId") long palletId); + /** 未装托盘标签按物理箱身份转移,保留原箱码不重新编号。 */ + @Update(""" + """) + int transferRemainderLabels(@Param("tenant") long tenant,@Param("source") long source,@Param("child") long child, + @Param("order") long order,@Param("code") String code,@Param("packages") int packages,@Param("picked") List picked); + /** 实箱随余量单转移,已装托与跨客户托内其他成员保持原归属。 */ + @Update(""" + """) + int transferRemainderCartons(@Param("tenant") long tenant,@Param("source") long source, + @Param("child") long child,@Param("picked") List picked); + @Getter @Setter class TaskRow { private Long id,tenantId,inboundId,warehouseId,assigneeId; private String inboundCode,customerName,warehouseName,taskStatus,assigneeName,processStatus,fulfillmentType; private Integer totalCartons,countedCartons,allocatedCartons,discrepancyCartons,expectedPalletCount,version; } @Getter @Setter class PalletRow { private Long id,taskId,tenantId,inboundId,warehouseId; private String palletNo,palletStatus,sealOperationId; private Integer version; private LocalDateTime completedAt; } @Getter @Setter class PlanPalletMemberRow { private Long palletId,accountId,inboundId,orderId; private String orderNo,inboundCode,customerName,orderStatusSnapshot; private Integer availablePackageCount; private LocalDateTime expectedArrivalAt; } diff --git a/firstleg/src/main/java/com/baiyue/firstleg/portschedule/dto/CarrierScheduleQueryDtos.java b/firstleg/src/main/java/com/baiyue/firstleg/portschedule/dto/CarrierScheduleQueryDtos.java index 1f7b76063..f7ddb3729 100644 --- a/firstleg/src/main/java/com/baiyue/firstleg/portschedule/dto/CarrierScheduleQueryDtos.java +++ b/firstleg/src/main/java/com/baiyue/firstleg/portschedule/dto/CarrierScheduleQueryDtos.java @@ -103,7 +103,8 @@ public final class CarrierScheduleQueryDtos { String providerNodeCode, String standardNode, int nodeSequence, LocalDateTime observedAt, LocalDateTime estimatedAt, LocalDateTime actualAt, String locationCode, String locationName, - String sourceReference, String rawPayloadHash, LocalDateTime createdAt) {} + String sourceReference, String rawPayloadHash, LocalDateTime createdAt, + String operatorName) {} public record ContainerNodeDetail(Long containerBookingId, String currentNode, LocalDateTime currentNodeAt, List nodes) {} diff --git a/firstleg/src/main/java/com/baiyue/firstleg/portschedule/entity/CarrierScheduleNodeFactEntity.java b/firstleg/src/main/java/com/baiyue/firstleg/portschedule/entity/CarrierScheduleNodeFactEntity.java index 2b60fb108..d29db66ef 100644 --- a/firstleg/src/main/java/com/baiyue/firstleg/portschedule/entity/CarrierScheduleNodeFactEntity.java +++ b/firstleg/src/main/java/com/baiyue/firstleg/portschedule/entity/CarrierScheduleNodeFactEntity.java @@ -30,5 +30,7 @@ public class CarrierScheduleNodeFactEntity { private String locationName; private String sourceReference; private String rawPayloadHash; + private Long operatorId; + private String operatorName; @TableField(fill = FieldFill.INSERT) private LocalDateTime createdAt; } diff --git a/firstleg/src/main/java/com/baiyue/firstleg/portschedule/mapper/CarrierScheduleNodeFactMapper.java b/firstleg/src/main/java/com/baiyue/firstleg/portschedule/mapper/CarrierScheduleNodeFactMapper.java index 2769556f7..dadef5640 100644 --- a/firstleg/src/main/java/com/baiyue/firstleg/portschedule/mapper/CarrierScheduleNodeFactMapper.java +++ b/firstleg/src/main/java/com/baiyue/firstleg/portschedule/mapper/CarrierScheduleNodeFactMapper.java @@ -11,14 +11,15 @@ import java.util.List; @Mapper public interface CarrierScheduleNodeFactMapper extends BaseMapper { + /** 保存不可变节点事实及执行该操作的账号快照。 */ @Insert(""" INSERT INTO fl_carrier_schedule_node_fact (id,query_id,container_booking_id,event_key,provider_code,provider_event_id,provider_node_code,standard_node,node_sequence,observed_at, - estimated_at,actual_at,location_code,location_name,source_reference,raw_payload_hash,created_at) + estimated_at,actual_at,location_code,location_name,source_reference,raw_payload_hash,operator_id,operator_name,created_at) VALUES (#{value.id},#{value.queryId},#{value.containerBookingId},#{value.eventKey},#{value.providerCode},#{value.providerEventId},#{value.providerNodeCode}, #{value.standardNode},#{value.nodeSequence},#{value.observedAt},#{value.estimatedAt},#{value.actualAt}, - #{value.locationCode},#{value.locationName},#{value.sourceReference},#{value.rawPayloadHash}, + #{value.locationCode},#{value.locationName},#{value.sourceReference},#{value.rawPayloadHash},#{value.operatorId},#{value.operatorName}, CURRENT_TIMESTAMP(3)) """) int insertStrict(@Param("value") CarrierScheduleNodeFactEntity value); diff --git a/firstleg/src/main/java/com/baiyue/firstleg/portschedule/service/CarrierScheduleQueryService.java b/firstleg/src/main/java/com/baiyue/firstleg/portschedule/service/CarrierScheduleQueryService.java index 6f05c6b98..269317e32 100644 --- a/firstleg/src/main/java/com/baiyue/firstleg/portschedule/service/CarrierScheduleQueryService.java +++ b/firstleg/src/main/java/com/baiyue/firstleg/portschedule/service/CarrierScheduleQueryService.java @@ -111,7 +111,7 @@ public class CarrierScheduleQueryService { public long customerId(long id) { return required(id).getCustomerId(); } - /** Idempotently starts the canonical tracking task after domestic outbound confirmation. */ + /** 国内确认出仓后幂等建立跟踪任务,并保存确认人的账号快照。 */ @Transactional public CarrierScheduleQueryEntity ensureFromOutboundConfirm(Long customerId, long oceanBookingId, long containerBookingId, @@ -161,7 +161,7 @@ public class CarrierScheduleQueryService { persistenceStage = "NODE_FACT"; CarrierScheduleNodeFactEntity departed = addNode(value, "FIRSTLEG", "OUTBOUND_CONFIRM", "OUTBOUND_CONFIRM", CarrierScheduleNode.DEPARTED_WAREHOUSE, now, null, now, null, null, "OUTBOUND_CONFIRM", - sha256("OUTBOUND_CONFIRM|" + containerBookingId)); + sha256("OUTBOUND_CONFIRM|" + containerBookingId), operatorId, operatorName); if (departed != null) { persistenceStage = "CROSS_REGION_OUTBOX"; nodeOutbox.publish(value, departed, requireContainerNo(containerBookingId)); @@ -185,6 +185,7 @@ public class CarrierScheduleQueryService { + ";shippingCompanyName=" + Objects.toString(booking.getShippingCompanyName(), ""); } + /** 保存船司自动采集事实,操作人留空以区别人工补录。 */ @Transactional public Detail applyProviderObservation(long id, ProviderObservationRequest request) { CarrierScheduleQueryEntity value = requiredLocked(id); @@ -193,7 +194,7 @@ public class CarrierScheduleQueryService { CarrierScheduleNodeFactEntity fact = addNode(value, request.providerCode(), request.providerEventId(), request.providerNodeCode(), node, request.observedAt(), request.estimatedAt(), request.actualAt(), request.locationCode(), - request.locationName(), request.sourceReference(), request.rawPayloadHash()); + request.locationName(), request.sourceReference(), request.rawPayloadHash(), null, null); if (fact == null) { return detail(value); } @@ -220,6 +221,7 @@ public class CarrierScheduleQueryService { return detail(value); } + /** 人工补录节点与操作账号一并留存,重放不覆盖原有事实。 */ @Transactional public Detail addManualNode(long id, long operatorId, String operatorName, String key, ManualNodeRequest request) { @@ -238,7 +240,7 @@ public class CarrierScheduleQueryService { CarrierScheduleNodeFactEntity fact = addNode(value, "MANUAL", providerEventId, node.name(), node, request.observedAt(), request.estimatedAt(), request.actualAt(), request.locationCode(), request.locationName(), - "MANUAL:" + operatorId, hash(operation, id, request)); + "MANUAL:" + operatorId, hash(operation, id, request), operatorId, operatorName); if (fact == null) { return complete(id, operatorId, key, detail(value)); } @@ -261,6 +263,7 @@ public class CarrierScheduleQueryService { return containerNodeDetail(containerBookingId, repository.nodesByContainer(containerBookingId)); } + /** 按货柜补录节点并保存操作账号,未建立查询任务时同样保留署名。 */ @Transactional public ContainerNodeDetail addContainerManualNode(long containerBookingId, long operatorId, String operatorName, String key, @@ -280,7 +283,8 @@ public class CarrierScheduleQueryService { CarrierScheduleNodeFactEntity fact = addNode(query == null ? null : query.getId(), containerBookingId, "MANUAL", providerEventId, node.name(), node, request.observedAt(), request.estimatedAt(), request.actualAt(), - request.locationCode(), request.locationName(), "MANUAL:" + operatorId, payloadHash); + request.locationCode(), request.locationName(), "MANUAL:" + operatorId, payloadHash, + operatorId, operatorName); if (fact != null && query != null) { CarrierScheduleQueryEntity before = copy(query); requireManualActualAdvance(query, node, request.actualAt()); @@ -546,11 +550,12 @@ public class CarrierScheduleQueryService { repository.histories(value.getId()).stream().map(this::history).toList()); } + /** 返回节点发生时保存的用户名,避免以来源引用代替操作人。 */ private NodeFact node(CarrierScheduleNodeFactEntity value) { return new NodeFact(value.getId(), value.getProviderCode(), value.getProviderEventId(), value.getProviderNodeCode(), value.getStandardNode(), value.getNodeSequence(), value.getObservedAt(), value.getEstimatedAt(), value.getActualAt(), value.getLocationCode(), value.getLocationName(), value.getSourceReference(), - value.getRawPayloadHash(), value.getCreatedAt()); + value.getRawPayloadHash(), value.getCreatedAt(), value.getOperatorName()); } private ExceptionFact exception(CarrierScheduleExceptionFactEntity value) { @@ -565,21 +570,23 @@ public class CarrierScheduleQueryService { value.getReason(), value.getOperatorId(), value.getOperatorName(), value.getCreatedAt()); } + /** 为查询任务追加节点事实并透传人工操作账号。 */ private CarrierScheduleNodeFactEntity addNode(CarrierScheduleQueryEntity query, String providerCode, String providerEventId, String providerNodeCode, CarrierScheduleNode node, LocalDateTime observedAt, LocalDateTime estimatedAt, LocalDateTime actualAt, String locationCode, String locationName, - String sourceReference, String rawPayloadHash) { + String sourceReference, String rawPayloadHash, Long operatorId, String operatorName) { return addNode(query.getId(), query.getContainerBookingId(), providerCode, providerEventId, providerNodeCode, node, observedAt, estimatedAt, actualAt, locationCode, locationName, - sourceReference, rawPayloadHash); + sourceReference, rawPayloadHash, operatorId, operatorName); } + /** 节点与操作人快照同次入库,重复事件保留首次记录。 */ private CarrierScheduleNodeFactEntity addNode(Long queryId, long containerBookingId, String providerCode, String providerEventId, String providerNodeCode, CarrierScheduleNode node, LocalDateTime observedAt, LocalDateTime estimatedAt, LocalDateTime actualAt, String locationCode, String locationName, - String sourceReference, String rawPayloadHash) { + String sourceReference, String rawPayloadHash, Long operatorId, String operatorName) { CarrierScheduleNodeFactEntity fact = new CarrierScheduleNodeFactEntity(); fact.setId(IdWorker.getId()); fact.setQueryId(queryId); fact.setContainerBookingId(containerBookingId); fact.setEventKey(providerCode.trim().toUpperCase(Locale.ROOT) + ':' + providerEventId.trim()); @@ -589,6 +596,7 @@ public class CarrierScheduleQueryService { fact.setNodeSequence(node.sequence()); fact.setObservedAt(observedAt); fact.setEstimatedAt(estimatedAt); fact.setActualAt(actualAt); fact.setLocationCode(trim(locationCode)); fact.setLocationName(trim(locationName)); fact.setSourceReference(trim(sourceReference)); fact.setRawPayloadHash(rawPayloadHash); + fact.setOperatorId(operatorId); fact.setOperatorName(trim(operatorName)); if (repository.insertIfAbsent(fact)) return fact; CarrierScheduleNodeFactEntity existing = repository.nodeByContainer(containerBookingId, fact.getEventKey()); if (existing != null && !nodeFactHash(existing).equals(nodeFactHash(fact))) { diff --git a/firstleg/src/main/java/com/baiyue/firstleg/repository/DropshipSplitRepository.java b/firstleg/src/main/java/com/baiyue/firstleg/repository/DropshipSplitRepository.java new file mode 100644 index 000000000..5f8d2f827 --- /dev/null +++ b/firstleg/src/main/java/com/baiyue/firstleg/repository/DropshipSplitRepository.java @@ -0,0 +1,23 @@ +package com.baiyue.firstleg.repository; + +import com.baiyue.firstleg.mapper.DropshipPalletizationMapper; +import com.baiyue.common.core.exception.BizException; +import com.baiyue.common.core.api.ResultCode; +import org.springframework.stereotype.Repository; +import java.util.List; + +/** 分托装柜余量的实箱归属迁移。 */ +@Repository +public class DropshipSplitRepository { + private final DropshipPalletizationMapper mapper; + /** 复用一件代发实箱账本,不另建库存。 */ + public DropshipSplitRepository(DropshipPalletizationMapper mapper) { this.mapper = mapper; } + /** 排除本柜已拣托盘,逐箱迁移到余量入仓单;数量不一致回滚本地结算。 */ + public void transferRemainder(long tenant, long source, long child, long order, String code, + int packages, List pickedPallets) { + if (pickedPallets.isEmpty()) throw new BizException(ResultCode.CONFLICT, "缺少已装整托事实"); + if (mapper.transferRemainderLabels(tenant, source, child, order, code, packages, pickedPallets) != packages + || mapper.transferRemainderCartons(tenant, source, child, pickedPallets) != packages) + throw new BizException(ResultCode.CONFLICT, "未装托盘箱数与余量不一致,请刷新后重试"); + } +} diff --git a/firstleg/src/main/java/com/baiyue/firstleg/repository/FbaPricingRepository.java b/firstleg/src/main/java/com/baiyue/firstleg/repository/FbaPricingRepository.java index 4949dee6f..ac879b1fc 100644 --- a/firstleg/src/main/java/com/baiyue/firstleg/repository/FbaPricingRepository.java +++ b/firstleg/src/main/java/com/baiyue/firstleg/repository/FbaPricingRepository.java @@ -331,6 +331,7 @@ public class FbaPricingRepository { return inbounds.update(null, update); } + /** 保存试算及计费量,保留测量、审核与仓库作业的主状态。 */ public int markInboundCalculated(long tenantId, long inboundId, String processStatus, String chargeMode, java.math.BigDecimal selectedWeightKg, java.math.BigDecimal selectedVolumeCbm, @@ -341,10 +342,6 @@ public class FbaPricingRepository { .eq(InboundPricingEntity::getProcessStatus, processStatus) .in(InboundPricingEntity::getFeeStatus, List.of("UNPRICED", "CALCULATED", "SENT")) .set(InboundPricingEntity::getFeeStatus, "CALCULATED"); - if (!List.of("WAIT_PALLET", "WAIT_PALLETIZE", "WAIT_SPLIT", "LOADING", "LOADED", "DELIVERED") - .contains(processStatus)) { - update.set(InboundPricingEntity::getProcessStatus, "FEE_CONFIRMING"); - } applyChargeableSnapshot(update, selectedWeightKg, selectedVolumeCbm, chargeMode, chargeable); return inbounds.update(null, update); } @@ -383,12 +380,14 @@ public class FbaPricingRepository { .set(InboundFeeEntity::getConfirmedAt, now)); } - /** 确认费用时保留物流主状态,一件代发待组托也允许确认。 */ + /** 仅确认已通过测量审核的费用,并保留当前履约状态。 */ public int markInboundConfirmed(long tenantId, long inboundId) { return inbounds.update(null, new LambdaUpdateWrapper() .eq(InboundPricingEntity::getTenantId, tenantId).eq(InboundPricingEntity::getId, inboundId) .in(InboundPricingEntity::getProcessStatus, - List.of("FEE_CONFIRMING", "WAIT_PALLET", "WAIT_PALLETIZE", "WAIT_SPLIT", "LOADING", "LOADED", "DELIVERED")) + List.of("WAIT_PRICING", "FEE_CONFIRMING", "WAIT_PALLET", "WAIT_PALLETIZE", "WAIT_SPLIT", + "LOADING", "LOADED", "DELIVERED")) + .eq(InboundPricingEntity::getMeasureReviewStatus, "APPROVED") .eq(InboundPricingEntity::getFeeStatus, "CALCULATED") .set(InboundPricingEntity::getFeeStatus, "CONFIRMED")); } @@ -436,15 +435,14 @@ public class FbaPricingRepository { .set(InboundPricingEntity::getFeeStatus, "VOIDED")); } + /** 发送仅更新费用状态,并原子校验测量已审核及履约状态未变化。 */ public int markInboundFeeSent(long tenantId, long inboundId, String processStatus) { LambdaUpdateWrapper update = new LambdaUpdateWrapper() .eq(InboundPricingEntity::getTenantId, tenantId).eq(InboundPricingEntity::getId, inboundId) .eq(InboundPricingEntity::getProcessStatus, processStatus) + .eq(InboundPricingEntity::getMeasureReviewStatus, "APPROVED") .eq(InboundPricingEntity::getFeeStatus, "CONFIRMED") .set(InboundPricingEntity::getFeeStatus, "SENT"); - if ("FEE_CONFIRMING".equals(processStatus)) { - update.set(InboundPricingEntity::getProcessStatus, "WAIT_PALLET"); - } return inbounds.update(null, update); } diff --git a/firstleg/src/main/java/com/baiyue/firstleg/repository/FeeItemRepository.java b/firstleg/src/main/java/com/baiyue/firstleg/repository/FeeItemRepository.java index 0008d8859..7161acf59 100644 --- a/firstleg/src/main/java/com/baiyue/firstleg/repository/FeeItemRepository.java +++ b/firstleg/src/main/java/com/baiyue/firstleg/repository/FeeItemRepository.java @@ -54,6 +54,18 @@ public class FeeItemRepository { .eq(FeeItemEntity::getFeeCode, feeCode)); } + /** 海外录费仅提供平台启用的国际仓库收付费项目,不复制费用目录。 */ + public List listActiveInternationalWarehouseItems(String direction) { + return feeItems.selectList(new LambdaQueryWrapper() + .eq(FeeItemEntity::getTenantId, PLATFORM_TENANT_ID) + .eq(FeeItemEntity::getStatus, "ACTIVE") + .eq(FeeItemEntity::getTradeScope, "INTERNATIONAL") + .eq("RECEIVABLE".equals(direction), FeeItemEntity::getWarehouseReceivable, true) + .eq("PAYABLE".equals(direction), FeeItemEntity::getWarehousePayable, true) + .orderByAsc(FeeItemEntity::getCategory) + .orderByAsc(FeeItemEntity::getFeeTypeName)); + } + public boolean itemNameExists(String tradeScope, String feeTypeName, Long excludedId) { return feeItems.selectCount(new LambdaQueryWrapper() .eq(FeeItemEntity::getTenantId, PLATFORM_TENANT_ID) diff --git a/firstleg/src/main/java/com/baiyue/firstleg/sailingcalendar/dto/SailingCalendarDtos.java b/firstleg/src/main/java/com/baiyue/firstleg/sailingcalendar/dto/SailingCalendarDtos.java index e626f65ac..3e5bd9219 100644 --- a/firstleg/src/main/java/com/baiyue/firstleg/sailingcalendar/dto/SailingCalendarDtos.java +++ b/firstleg/src/main/java/com/baiyue/firstleg/sailingcalendar/dto/SailingCalendarDtos.java @@ -204,13 +204,15 @@ public final class SailingCalendarDtos { int recommendedContainerCount, int pendingContainerCount, int lateBatchCount, boolean overCapacity, BigDecimal planningCapacityCbm, BigDecimal bookedPlanningCapacityCbm, LocalDateTime snapshotAt, - BigDecimal catchableReceivedCbm, BigDecimal lateReceivedCbm) {} + BigDecimal catchableReceivedCbm, BigDecimal lateReceivedCbm, + BigDecimal availableBookedCapacityCbm) {} public record CalendarResult(String month, String timezone, List records, - Summary summary, FilterOptions filterOptions) { + Summary summary, FilterOptions filterOptions, LocalDateTime nextRefreshAt) { public record Summary(BigDecimal actualReceivedCbm, BigDecimal catchableForecastCbm, BigDecimal riskUnmatchedCbm, int bookedContainerCount, int recommendedContainerCount, int pendingContainerCount, int lateBatchCount, boolean overCapacity, BigDecimal planningCapacityCbm, int sailingCount, - BigDecimal catchableReceivedCbm, BigDecimal lateReceivedCbm) {} + BigDecimal catchableReceivedCbm, BigDecimal lateReceivedCbm, + BigDecimal availableBookedCapacityCbm) {} public record FilterOptions(List