fix: make unified database migrations converge
This commit is contained in:
@@ -18580,6 +18580,20 @@ DROP PROCEDURE IF EXISTS migrate_fin_wallet_credit;
|
||||
DELIMITER $$
|
||||
CREATE PROCEDURE migrate_fin_wallet_credit()
|
||||
BEGIN
|
||||
DECLARE v_legacy_wallet_constraint BOOLEAN DEFAULT FALSE;
|
||||
DECLARE v_credit_expiry_missing BOOLEAN DEFAULT FALSE;
|
||||
SET v_legacy_wallet_constraint = EXISTS (
|
||||
SELECT 1 FROM information_schema.CHECK_CONSTRAINTS
|
||||
WHERE CONSTRAINT_SCHEMA='baiyue_finance'
|
||||
AND CONSTRAINT_NAME='chk_fin_wallet_account_balance'
|
||||
AND CHECK_CLAUSE LIKE '%actual_balance%'
|
||||
);
|
||||
SET v_credit_expiry_missing = NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA='baiyue_finance'
|
||||
AND TABLE_NAME='fin_wallet_account'
|
||||
AND COLUMN_NAME='credit_expires_on'
|
||||
);
|
||||
IF NOT EXISTS (SELECT 1 FROM information_schema.COLUMNS WHERE TABLE_SCHEMA='baiyue_finance' AND TABLE_NAME='fin_wallet_account' AND COLUMN_NAME='credit_limit') THEN
|
||||
ALTER TABLE fin_wallet_account
|
||||
ADD COLUMN credit_limit DECIMAL(20,2) NOT NULL DEFAULT 0 COMMENT '授信额度' AFTER available_balance,
|
||||
@@ -18588,15 +18602,17 @@ BEGIN
|
||||
END IF;
|
||||
IF NOT EXISTS (SELECT 1 FROM information_schema.COLUMNS WHERE TABLE_SCHEMA='baiyue_finance' AND TABLE_NAME='fin_wallet_account' AND COLUMN_NAME='credit_expires_on') THEN
|
||||
ALTER TABLE fin_wallet_account ADD COLUMN credit_expires_on DATE NULL COMMENT '授信到期日,当天仍有效;次日失效' AFTER credit_frozen;
|
||||
UPDATE fin_wallet_account
|
||||
SET available_balance=actual_balance-credit_used-frozen_balance;
|
||||
END IF;
|
||||
IF EXISTS (SELECT 1 FROM information_schema.TABLE_CONSTRAINTS WHERE CONSTRAINT_SCHEMA='baiyue_finance' AND TABLE_NAME='fin_wallet_account' AND CONSTRAINT_NAME='chk_fin_wallet_account_balance' AND CONSTRAINT_TYPE='CHECK') THEN
|
||||
ALTER TABLE fin_wallet_account DROP CHECK chk_fin_wallet_account_balance;
|
||||
END IF;
|
||||
IF v_legacy_wallet_constraint OR v_credit_expiry_missing THEN
|
||||
UPDATE fin_wallet_account
|
||||
SET actual_balance=actual_balance-credit_used,
|
||||
credit_used=0,
|
||||
credit_expires_on=NULL;
|
||||
END IF;
|
||||
IF EXISTS (SELECT 1 FROM information_schema.TABLE_CONSTRAINTS WHERE CONSTRAINT_SCHEMA='baiyue_finance' AND TABLE_NAME='fin_wallet_account' AND CONSTRAINT_NAME='chk_fin_wallet_account_balance' AND CONSTRAINT_TYPE='CHECK') THEN
|
||||
ALTER TABLE fin_wallet_account DROP CHECK chk_fin_wallet_account_balance;
|
||||
UPDATE fin_wallet_account
|
||||
SET available_balance=actual_balance-credit_used-frozen_balance;
|
||||
END IF;
|
||||
ALTER TABLE fin_wallet_account ADD CONSTRAINT chk_fin_wallet_account_balance
|
||||
CHECK (frozen_balance >= 0 AND credit_limit >= 0 AND credit_used >= 0 AND credit_frozen >= 0 AND frozen_balance >= credit_frozen);
|
||||
|
||||
@@ -1793,6 +1793,10 @@ CREATE TABLE IF NOT EXISTS ll_relabel_item (
|
||||
KEY idx_ll_relabel_item_destination (customer_id,destination_warehouse_id,application_id)
|
||||
) ENGINE=InnoDB COMMENT='换标货物冻结明细';
|
||||
|
||||
SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_relabel_item' AND column_name='destination_fba_warehouse_code'), 'SELECT 1', 'ALTER TABLE ll_relabel_item ADD COLUMN destination_fba_warehouse_code VARCHAR(128) NULL COMMENT ''可选FBA目的仓编码(与海外操作仓分离)''')); 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_relabel_item' AND column_name='source_inventory_balance_id'), 'SELECT 1', 'ALTER TABLE ll_relabel_item ADD COLUMN source_inventory_balance_id BIGINT NULL COMMENT ''原库存余额ID(预入仓换标任务可为空)''')); 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_relabel_item' AND column_name='source_inventory_position_id'), 'SELECT 1', 'ALTER TABLE ll_relabel_item ADD COLUMN source_inventory_position_id BIGINT NULL COMMENT ''原库存库位ID(预入仓换标任务可为空)''')); 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_relabel_item' AND column_name='source_inbound_order_id'), 'SELECT 1', 'ALTER TABLE ll_relabel_item ADD COLUMN source_inbound_order_id BIGINT NULL COMMENT ''原入仓单ID(预入仓换标任务可为空)''')); 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_relabel_item' AND column_name IN ('shipment_no','destination_fba_warehouse_code','source_inventory_balance_id','source_inventory_position_id','source_inbound_order_id') AND is_nullable='NO'), 'ALTER TABLE ll_relabel_item MODIFY COLUMN shipment_no VARCHAR(128) NULL COMMENT ''货件编号(无FBA去向时为空)'', MODIFY COLUMN destination_fba_warehouse_code VARCHAR(128) NULL COMMENT ''可选FBA目的仓编码(与海外操作仓分离)'', MODIFY COLUMN source_inventory_balance_id BIGINT NULL COMMENT ''原库存余额ID(预入仓换标任务可为空)'', MODIFY COLUMN source_inventory_position_id BIGINT NULL COMMENT ''原库存库位ID(预入仓换标任务可为空)'', MODIFY COLUMN source_inbound_order_id BIGINT NULL COMMENT ''原入仓单ID(预入仓换标任务可为空)''', 'SELECT 1'));
|
||||
PREPARE relabel_pre_inbound_stmt FROM @ddl; EXECUTE relabel_pre_inbound_stmt; DEALLOCATE PREPARE relabel_pre_inbound_stmt;
|
||||
|
||||
@@ -4172,7 +4176,7 @@ BEGIN
|
||||
AND aggregate_version=0 AND last_event_id='baseline-platform-admin-authorization-v0'
|
||||
AND (JSON_LENGTH(roles_json) <> 1
|
||||
OR NOT JSON_CONTAINS(roles_json, JSON_QUOTE('PLATFORM_ADMIN'))
|
||||
OR JSON_LENGTH(authorities_json) <> 109
|
||||
OR JSON_LENGTH(authorities_json) <> 105
|
||||
OR NOT JSON_CONTAINS(authorities_json, JSON_QUOTE('platform:overseas-warehouse:last-mile-order:list'))
|
||||
OR NOT JSON_CONTAINS(authorities_json, JSON_QUOTE('platform:overseas-warehouse:inbound-order:list'))
|
||||
OR NOT JSON_CONTAINS(authorities_json, JSON_QUOTE('platform:overseas-warehouse:dispatch:list'))
|
||||
@@ -4740,7 +4744,12 @@ SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE tab
|
||||
PREPARE ll_delivery_order_bol_no_stmt FROM @ddl; EXECUTE ll_delivery_order_bol_no_stmt; DEALLOCATE PREPARE ll_delivery_order_bol_no_stmt;
|
||||
SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_return_order' AND column_name='lastleg_order_id'), 'SELECT 1', 'ALTER TABLE ll_return_order ADD COLUMN lastleg_order_id BIGINT NULL COMMENT ''关联尾程主订单ID'' AFTER customer_reference_no'));
|
||||
PREPARE ll_return_order_lastleg_id_stmt FROM @ddl; EXECUTE ll_return_order_lastleg_id_stmt; DEALLOCATE PREPARE ll_return_order_lastleg_id_stmt;
|
||||
UPDATE ll_return_order r JOIN ll_order o ON o.source_order_id = CAST(r.id AS CHAR) AND o.source_order_no = r.return_no AND o.order_type = 'RETURN_RELABEL' SET r.lastleg_order_id = o.id WHERE r.lastleg_order_id IS NULL;
|
||||
UPDATE ll_return_order r JOIN ll_order o
|
||||
ON o.source_order_id COLLATE utf8mb4_unicode_ci = CAST(r.id AS CHAR) COLLATE utf8mb4_unicode_ci
|
||||
AND o.source_order_no COLLATE utf8mb4_unicode_ci = r.return_no COLLATE utf8mb4_unicode_ci
|
||||
AND o.order_type = 'RETURN_RELABEL'
|
||||
SET r.lastleg_order_id = o.id
|
||||
WHERE r.lastleg_order_id IS NULL;
|
||||
|
||||
-- 柜级入库计划跨客户批量托盘预留、确认与失败补偿。
|
||||
USE baiyue_lastlag_warehouse;
|
||||
|
||||
@@ -9,6 +9,21 @@ import java.nio.file.Path;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class FinanceWalletContractTest {
|
||||
@Test
|
||||
void unifiedMigrationDropsTheLegacyBalanceConstraintBeforeRewritingBalances() throws Exception {
|
||||
Path migration = Path.of("deploy/sql/头程数据库统一增量-MySQL8.sql");
|
||||
if (!Files.exists(migration)) migration = Path.of("..").resolve(migration);
|
||||
String sql = Files.readString(migration, StandardCharsets.UTF_8);
|
||||
int procedure = sql.indexOf("CREATE PROCEDURE migrate_fin_wallet_credit()");
|
||||
int dropConstraint = sql.indexOf("ALTER TABLE fin_wallet_account DROP CHECK chk_fin_wallet_account_balance", procedure);
|
||||
int rewriteBalance = sql.indexOf("SET actual_balance=actual_balance-credit_used", procedure);
|
||||
|
||||
assertThat(procedure).isGreaterThanOrEqualTo(0);
|
||||
assertThat(sql.substring(procedure, rewriteBalance))
|
||||
.contains("CHECK_CLAUSE LIKE '%actual_balance%'", "v_legacy_wallet_constraint OR v_credit_expiry_missing");
|
||||
assertThat(dropConstraint).isBetween(procedure, rewriteBalance - 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void schemaKeepsBalancesConsistentAndRechargeReviewTraceable() throws Exception {
|
||||
String sql = Files.readString(Path.of("src/main/resources/db/finance.sql"), StandardCharsets.UTF_8);
|
||||
|
||||
@@ -1700,6 +1700,10 @@ CREATE TABLE IF NOT EXISTS ll_relabel_item (
|
||||
KEY idx_ll_relabel_item_destination (customer_id,destination_warehouse_id,application_id)
|
||||
) ENGINE=InnoDB COMMENT='换标货物冻结明细';
|
||||
|
||||
SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_relabel_item' AND column_name='destination_fba_warehouse_code'), 'SELECT 1', 'ALTER TABLE ll_relabel_item ADD COLUMN destination_fba_warehouse_code VARCHAR(128) NULL COMMENT ''可选FBA目的仓编码(与海外操作仓分离)''')); 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_relabel_item' AND column_name='source_inventory_balance_id'), 'SELECT 1', 'ALTER TABLE ll_relabel_item ADD COLUMN source_inventory_balance_id BIGINT NULL COMMENT ''原库存余额ID(预入仓换标任务可为空)''')); 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_relabel_item' AND column_name='source_inventory_position_id'), 'SELECT 1', 'ALTER TABLE ll_relabel_item ADD COLUMN source_inventory_position_id BIGINT NULL COMMENT ''原库存库位ID(预入仓换标任务可为空)''')); 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_relabel_item' AND column_name='source_inbound_order_id'), 'SELECT 1', 'ALTER TABLE ll_relabel_item ADD COLUMN source_inbound_order_id BIGINT NULL COMMENT ''原入仓单ID(预入仓换标任务可为空)''')); 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_relabel_item' AND column_name IN ('shipment_no','destination_fba_warehouse_code','source_inventory_balance_id','source_inventory_position_id','source_inbound_order_id') AND is_nullable='NO'), 'ALTER TABLE ll_relabel_item MODIFY COLUMN shipment_no VARCHAR(128) NULL COMMENT ''货件编号(无FBA去向时为空)'', MODIFY COLUMN destination_fba_warehouse_code VARCHAR(128) NULL COMMENT ''可选FBA目的仓编码(与海外操作仓分离)'', MODIFY COLUMN source_inventory_balance_id BIGINT NULL COMMENT ''原库存余额ID(预入仓换标任务可为空)'', MODIFY COLUMN source_inventory_position_id BIGINT NULL COMMENT ''原库存库位ID(预入仓换标任务可为空)'', MODIFY COLUMN source_inbound_order_id BIGINT NULL COMMENT ''原入仓单ID(预入仓换标任务可为空)''', 'SELECT 1'));
|
||||
PREPARE relabel_pre_inbound_stmt FROM @ddl; EXECUTE relabel_pre_inbound_stmt; DEALLOCATE PREPARE relabel_pre_inbound_stmt;
|
||||
|
||||
|
||||
+29
@@ -51,6 +51,14 @@ class Phase5OverseasUpgradeSqlContractTest {
|
||||
"ALTER TABLE ll_dropship_order_profile ADD COLUMN source_pallet_no",
|
||||
"ADD UNIQUE KEY uk_ll_dropship_sku_inventory(profile_id,sku_code)",
|
||||
"MODIFY COLUMN source_pallet_no VARCHAR(128) NOT NULL");
|
||||
int relabelCreateEnd = overseas.indexOf(") ENGINE=InnoDB COMMENT='换标货物冻结明细';");
|
||||
int relabelModify = overseas.indexOf("MODIFY COLUMN destination_fba_warehouse_code", relabelCreateEnd);
|
||||
for (String column : List.of("destination_fba_warehouse_code", "source_inventory_balance_id",
|
||||
"source_inventory_position_id", "source_inbound_order_id")) {
|
||||
assertThat(overseas.indexOf("ALTER TABLE ll_relabel_item ADD COLUMN " + column, relabelCreateEnd))
|
||||
.as(column + " converges before grouped MODIFY")
|
||||
.isBetween(relabelCreateEnd, relabelModify - 1);
|
||||
}
|
||||
int start = overseas.indexOf("-- 五期正式结构必须由海外固定入口自行补齐");
|
||||
int end = overseas.indexOf("USE baiyue_lastleg;", start);
|
||||
String phase5 = overseas.substring(start, end);
|
||||
@@ -69,6 +77,27 @@ class Phase5OverseasUpgradeSqlContractTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void overseasFixedAdminProjectionAssertionMatchesItsAuthorityArray() throws Exception {
|
||||
String overseas = repositoryFile("deploy/sql/海外区域统一增量-MySQL8.sql");
|
||||
int start = overseas.indexOf("SET @platform_admin_overseas_authorities = JSON_ARRAY(");
|
||||
int end = overseas.indexOf(");", start);
|
||||
String authorityArray = overseas.substring(start, end);
|
||||
int authorityCount = count(authorityArray, "'platform:");
|
||||
|
||||
assertThat(authorityCount).isPositive();
|
||||
assertThat(overseas).contains("JSON_LENGTH(authorities_json) <> " + authorityCount);
|
||||
}
|
||||
|
||||
@Test
|
||||
void overseasReturnOrderBackfillNormalizesLegacyColumnCollations() throws Exception {
|
||||
String overseas = repositoryFile("deploy/sql/海外区域统一增量-MySQL8.sql");
|
||||
|
||||
assertThat(overseas).contains(
|
||||
"o.source_order_id COLLATE utf8mb4_unicode_ci = CAST(r.id AS CHAR) COLLATE utf8mb4_unicode_ci",
|
||||
"o.source_order_no COLLATE utf8mb4_unicode_ci = r.return_no COLLATE utf8mb4_unicode_ci");
|
||||
}
|
||||
|
||||
@Test
|
||||
void domesticFixedEntryKeepsIamPermissionsButOwnsNoLastlegBusinessSchema() throws Exception {
|
||||
String domestic = repositoryFile("deploy/sql/头程数据库统一增量-MySQL8.sql");
|
||||
|
||||
Reference in New Issue
Block a user