fix phase3 phase4 development acceptance blockers
This commit is contained in:
@@ -10626,6 +10626,37 @@ DELIMITER ;
|
||||
CALL migrate_carrier_schedule_retry_columns();
|
||||
DROP PROCEDURE IF EXISTS migrate_carrier_schedule_retry_columns;
|
||||
|
||||
-- CREATE TABLE IF NOT EXISTS 不会替换历史 CHECK 与唯一索引。统一收敛到当前
|
||||
-- READY/MANUAL_REQUIRED/STOPPED、五种访问方式、十个实际节点和每业务范围唯一任务。
|
||||
ALTER TABLE fl_carrier_schedule_query
|
||||
DROP CHECK chk_carrier_schedule_query_access,
|
||||
DROP CHECK chk_carrier_schedule_query_status;
|
||||
DROP PROCEDURE IF EXISTS drop_legacy_carrier_schedule_node_check;
|
||||
DELIMITER $$
|
||||
CREATE PROCEDURE drop_legacy_carrier_schedule_node_check()
|
||||
BEGIN
|
||||
IF EXISTS (SELECT 1 FROM information_schema.table_constraints
|
||||
WHERE constraint_schema=DATABASE() AND table_name='fl_carrier_schedule_query'
|
||||
AND constraint_name='chk_carrier_schedule_query_node' AND constraint_type='CHECK') THEN
|
||||
ALTER TABLE fl_carrier_schedule_query DROP CHECK chk_carrier_schedule_query_node;
|
||||
END IF;
|
||||
END$$
|
||||
DELIMITER ;
|
||||
CALL drop_legacy_carrier_schedule_node_check();
|
||||
DROP PROCEDURE IF EXISTS drop_legacy_carrier_schedule_node_check;
|
||||
ALTER TABLE fl_carrier_schedule_query
|
||||
ADD CONSTRAINT chk_carrier_schedule_query_access CHECK
|
||||
(access_mode IN ('PUBLIC_FORM','PUBLIC_WEB_PROXY','PUBLIC_HTML','HCAPTCHA','OFFICIAL_API')),
|
||||
ADD CONSTRAINT chk_carrier_schedule_query_status CHECK
|
||||
(status IN ('READY','MANUAL_REQUIRED','STOPPED')),
|
||||
ADD CONSTRAINT chk_carrier_schedule_query_node CHECK
|
||||
(latest_standard_node IS NULL OR latest_standard_node IN
|
||||
('DEPARTED_WAREHOUSE','AT_ORIGIN_PORT','LOADED_ON_VESSEL','DEPARTED_ORIGIN_PORT','IN_TRANSIT',
|
||||
'ARRIVED_DESTINATION_PORT','DISCHARGED_FROM_VESSEL','PICKUP_READY','PICKED_UP','EMPTY_RETURNED')),
|
||||
DROP INDEX uk_carrier_schedule_query_scope,
|
||||
ADD UNIQUE KEY uk_carrier_schedule_query_scope
|
||||
(customer_id,ocean_booking_id,container_scope_id);
|
||||
|
||||
SET @carrier_schedule_now = CONVERT_TZ(UTC_TIMESTAMP(3),'+00:00','+08:00');
|
||||
SET @carrier_schedule_next = CASE
|
||||
WHEN TIME(@carrier_schedule_now) < '09:00:00' THEN DATE(@carrier_schedule_now) + INTERVAL 9 HOUR
|
||||
|
||||
@@ -187,6 +187,23 @@ CREATE TABLE IF NOT EXISTS ll_local_looseboard_history (
|
||||
KEY idx_ll_local_looseboard_history(customer_id,order_id,created_at)
|
||||
) ENGINE=InnoDB COMMENT='本土散板操作流水';
|
||||
|
||||
-- 尾程主订单状态历史补齐客户隔离键。代码写入 customer_id,已有表需显式升级;
|
||||
-- 先允许 NULL 以便从主订单回填,再收紧为 NOT NULL,脚本可重复执行。
|
||||
SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE()
|
||||
AND table_name='ll_loading_order_status_history' AND column_name='customer_id'), 'SELECT 1',
|
||||
'ALTER TABLE ll_loading_order_status_history ADD COLUMN customer_id VARCHAR(128) NULL COMMENT ''客户数据隔离键'' AFTER id'));
|
||||
PREPARE phase4_loading_history_customer_stmt FROM @ddl; EXECUTE phase4_loading_history_customer_stmt; DEALLOCATE PREPARE phase4_loading_history_customer_stmt;
|
||||
UPDATE ll_loading_order_status_history history
|
||||
JOIN ll_order orders ON orders.id=history.order_id
|
||||
SET history.customer_id=orders.customer_id
|
||||
WHERE history.customer_id IS NULL;
|
||||
ALTER TABLE ll_loading_order_status_history
|
||||
MODIFY COLUMN customer_id VARCHAR(128) NOT NULL COMMENT '客户数据隔离键';
|
||||
SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.statistics WHERE table_schema=DATABASE()
|
||||
AND table_name='ll_loading_order_status_history' AND index_name='idx_ll_loading_history_customer_order'), 'SELECT 1',
|
||||
'ALTER TABLE ll_loading_order_status_history ADD KEY idx_ll_loading_history_customer_order(customer_id,order_id,created_at)'));
|
||||
PREPARE phase4_loading_history_customer_stmt FROM @ddl; EXECUTE phase4_loading_history_customer_stmt; DEALLOCATE PREPARE phase4_loading_history_customer_stmt;
|
||||
|
||||
-- 四期平台排车查询、智能预排与草稿锁;不开放编辑、发车、PDA 出库或派送。
|
||||
CREATE TABLE IF NOT EXISTS ll_dispatch_order (
|
||||
id BIGINT NOT NULL COMMENT '主键ID', customer_id VARCHAR(128) NOT NULL COMMENT '客户租户ID',
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
package com.baiyue.firstleg.dto;
|
||||
import com.baiyue.firstleg.entity.LoadingPlanItemEntity; import jakarta.validation.constraints.*; import java.math.BigDecimal; import java.util.List;
|
||||
import com.baiyue.firstleg.entity.LoadingPlanItemEntity; import jakarta.validation.constraints.*; import org.apache.ibatis.annotations.AutomapConstructor; import java.math.BigDecimal; import java.util.List;
|
||||
public final class SmartLoadingDtos { private SmartLoadingDtos(){}
|
||||
public record InboundCandidate(Long accountId, Long inboundId, Long orderId, String orderNo, String inboundCode,
|
||||
Long warehouseId, String channelCode, String channelName, Long destinationWarehouseId,
|
||||
@@ -7,8 +7,10 @@ public final class SmartLoadingDtos { private SmartLoadingDtos(){}
|
||||
Integer availablePackageCount, BigDecimal unitGrossWeightKg, BigDecimal unitNetWeightKg,
|
||||
BigDecimal averageUnitVolumeCbm, BigDecimal availableVolumeCbm) {}
|
||||
public record Candidate(Long accountId, Long inboundId, Long inboundDetailId, Long orderId, String orderNo, String inboundCode,
|
||||
Long warehouseId, String channelCode, String channelName, Long destinationWarehouseId, String destinationWarehouseCode, String destinationWarehouseName, String customerName, String goodsName, Integer availablePackageCount,
|
||||
Long warehouseId, String channelCode, String channelName, Long destinationWarehouseId, String destinationWarehouseCode, String destinationWarehouseName, String customerName, String goodsName, Integer availablePackageCount,
|
||||
BigDecimal unitGrossWeightKg, BigDecimal unitNetWeightKg, BigDecimal unitVolumeCbm) {
|
||||
@AutomapConstructor
|
||||
public Candidate { }
|
||||
public Candidate(Long accountId, Long inboundId, Long inboundDetailId, Long orderId, String orderNo, String inboundCode,
|
||||
Long warehouseId, String destinationWarehouseCode, String customerName, String goodsName, Integer availablePackageCount,
|
||||
BigDecimal unitGrossWeightKg, BigDecimal unitNetWeightKg, BigDecimal unitVolumeCbm) {
|
||||
|
||||
+9
@@ -74,6 +74,15 @@ class CarrierScheduleQuerySqlContractTest {
|
||||
.contains("ADD COLUMN attempt_count INT UNSIGNED NOT NULL DEFAULT 0")
|
||||
.contains("ADD COLUMN next_allowed_at DATETIME(3) NULL")
|
||||
.contains("ADD KEY idx_carrier_schedule_query_due (status,next_allowed_at,id)")
|
||||
.contains("DROP CHECK chk_carrier_schedule_query_access")
|
||||
.contains("DROP CHECK chk_carrier_schedule_query_status")
|
||||
.contains("DROP INDEX uk_carrier_schedule_query_scope")
|
||||
.contains("ADD UNIQUE KEY uk_carrier_schedule_query_scope")
|
||||
.contains("(customer_id,ocean_booking_id,container_scope_id)")
|
||||
.contains("'PUBLIC_FORM','PUBLIC_WEB_PROXY','PUBLIC_HTML','HCAPTCHA','OFFICIAL_API'")
|
||||
.contains("status IN ('READY','MANUAL_REQUIRED','STOPPED')")
|
||||
.contains("ARRIVED_DESTINATION_PORT")
|
||||
.contains("EMPTY_RETURNED")
|
||||
.contains("SET @carrier_schedule_now = CONVERT_TZ(UTC_TIMESTAMP(3),'+00:00','+08:00')")
|
||||
.contains("SET @carrier_schedule_next = CASE")
|
||||
.contains("CREATE PROCEDURE ensure_carrier_schedule_retry_check()")
|
||||
|
||||
+14
@@ -1,6 +1,8 @@
|
||||
package com.baiyue.firstleg.repository;
|
||||
|
||||
import com.baiyue.firstleg.mapper.SmartLoadingCandidateMapper;
|
||||
import com.baiyue.firstleg.dto.SmartLoadingDtos.Candidate;
|
||||
import org.apache.ibatis.annotations.AutomapConstructor;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@@ -9,6 +11,18 @@ import java.lang.reflect.Method;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class SmartLoadingCandidateMapperContractTest {
|
||||
@Test
|
||||
void canonicalCandidateConstructorIsTheUniqueMybatisAutomapTarget() {
|
||||
long annotated = java.util.Arrays.stream(Candidate.class.getDeclaredConstructors())
|
||||
.filter(constructor -> constructor.isAnnotationPresent(AutomapConstructor.class))
|
||||
.count();
|
||||
|
||||
assertThat(annotated).isEqualTo(1);
|
||||
assertThat(java.util.Arrays.stream(Candidate.class.getDeclaredConstructors())
|
||||
.filter(constructor -> constructor.isAnnotationPresent(AutomapConstructor.class))
|
||||
.findFirst().orElseThrow().getParameterCount()).isEqualTo(18);
|
||||
}
|
||||
|
||||
@Test
|
||||
void selectsLatestApprovedBatch() throws Exception {
|
||||
Method list = SmartLoadingCandidateMapper.class.getDeclaredMethod("candidates", long.class);
|
||||
|
||||
@@ -104,6 +104,7 @@ CREATE TABLE IF NOT EXISTS ll_order_detail (
|
||||
|
||||
CREATE TABLE IF NOT EXISTS ll_loading_order_status_history (
|
||||
id BIGINT NOT NULL AUTO_INCREMENT COMMENT '状态历史主键',
|
||||
customer_id VARCHAR(128) NOT NULL COMMENT '客户数据隔离键',
|
||||
order_id BIGINT NOT NULL COMMENT '尾程订单 ID',
|
||||
from_status VARCHAR(32) COMMENT '变更前状态',
|
||||
to_status VARCHAR(32) NOT NULL COMMENT '变更后状态',
|
||||
@@ -111,7 +112,7 @@ CREATE TABLE IF NOT EXISTS ll_loading_order_status_history (
|
||||
remark VARCHAR(500) COMMENT '状态变更备注',
|
||||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '记录时间',
|
||||
PRIMARY KEY(id),
|
||||
KEY idx_ll_loading_history_order(order_id,created_at)
|
||||
KEY idx_ll_loading_history_customer_order(customer_id,order_id,created_at)
|
||||
) ENGINE=InnoDB COMMENT='尾程订单状态变更历史';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS ll_event_conflict (
|
||||
|
||||
+2
@@ -19,5 +19,7 @@ class LocalLooseboardSchemaContractTest {
|
||||
assertTrue(sql.contains("uk_ll_local_looseboard_no(customer_id,order_no)"));
|
||||
assertTrue(sql.contains("lastleg_order_id BIGINT NULL"));
|
||||
assertTrue(sql.contains("uk_ll_local_looseboard_lastleg(lastleg_order_id)"));
|
||||
assertTrue(sql.contains("customer_id VARCHAR(128) NOT NULL COMMENT '客户数据隔离键'"));
|
||||
assertTrue(sql.contains("idx_ll_loading_history_customer_order(customer_id,order_id,created_at)"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,8 @@ test('Firstleg booking supplier snapshot requires ACTIVE BOOKING and has no dome
|
||||
|
||||
test('HTTP ocean booking acceptance selects an ACTIVE BOOKING supplier without a geographic scope', () => {
|
||||
assert.match(oceanBookingFixture, /category=BOOKING&status=ACTIVE/)
|
||||
assert.match(oceanBookingFixture, /bookingAgentId: bookingAgent\.supplierId/)
|
||||
assert.match(oceanBookingFixture, /订舱持久化启用订舱供应商快照/)
|
||||
assert.match(oceanBookingFixture, /bookingAgentId: id\(agent\.id\), bookingAgentName: agent\.name/)
|
||||
assert.match(oceanBookingFixture, /shippingCompanyId: id\(agent\.id\), shippingCompanyName: agent\.name/)
|
||||
assert.match(oceanBookingFixture, /使用 ACTIVE BOOKING 订舱代理事实源/)
|
||||
assert.doesNotMatch(oceanBookingFixture, /category=BOOKING[^'\n]*scope=/)
|
||||
})
|
||||
|
||||
@@ -475,8 +475,8 @@ export async function run(context) {
|
||||
const confirmedSummary = confirmedNetSummary(afterConfirm.workspace)
|
||||
context.assert(MODULE, '货物总重量按全部已确认净重由服务端汇总', afterConfirm.response,
|
||||
confirmedSummary.complete && confirmedSummary.count === baselineImageSummary.count + 1
|
||||
&& closeNumber(afterConfirm.workspace?.actualGrossWeightKg, confirmedSummary.total, 0.0011),
|
||||
`confirmedCount=${confirmedSummary.count}, confirmedNetTotal=${confirmedSummary.total}, actual=${afterConfirm.workspace?.actualGrossWeightKg ?? 'missing'}`,
|
||||
&& closeNumber(afterConfirm.workspace?.actualNetWeightKg, confirmedSummary.total, 0.0011),
|
||||
`confirmedCount=${confirmedSummary.count}, confirmedNetTotal=${confirmedSummary.total}, actual=${afterConfirm.workspace?.actualNetWeightKg ?? 'missing'}`,
|
||||
NEW_CONTRACT_OPTIONS)
|
||||
|
||||
const staleDelete = await context.request('DELETE',
|
||||
|
||||
@@ -47,7 +47,10 @@ export async function run(context) {
|
||||
step(context, '使用 ACTIVE BOOKING 订舱代理事实源', agentResponse, Boolean(agent?.id && agent?.name),
|
||||
`supplierId=${id(agent?.id) || 'missing'}, scope=${agent?.scope || 'not-exposed'}`)
|
||||
if (!agent?.id || !agent?.name) return
|
||||
const data = fullDraft(context, suffix, master, { bookingAgentId: id(agent.id), bookingAgentName: agent.name })
|
||||
const data = fullDraft(context, suffix, master, {
|
||||
bookingAgentId: id(agent.id), bookingAgentName: agent.name,
|
||||
shippingCompanyId: id(agent.id), shippingCompanyName: agent.name,
|
||||
})
|
||||
const create = await request(context, 'POST', BASE, { expectedVersion: 0, data },
|
||||
{ 'Idempotency-Key': `${context.marker}-ocean-draft-create`.slice(0, 128) })
|
||||
const created = payload(create); const bookingId = id(created?.oceanBooking?.id)
|
||||
|
||||
@@ -36,12 +36,12 @@ test('状态矩阵复用订单共享的逐仓真实报价选择,不假设首
|
||||
test('入库通过真实订单测量链保留测量态并最终进入 WAIT_SPLIT', () => {
|
||||
assert.match(source, /result\.inbounds\.WAIT_MEASURE/)
|
||||
assert.match(source, /processStatus: 'WAIT_MEASURE'/)
|
||||
for (const status of ['MEASURING', 'MEASURE_PENDING', 'MEASURE_REJECTED', 'WAIT_SPLIT']) {
|
||||
for (const status of ['MEASURING', 'MEASURE_PENDING', 'WAIT_SPLIT']) {
|
||||
assert.match(source, new RegExp(`'${status}'`))
|
||||
}
|
||||
assert.match(source, /\.\/inbound-measurement\.mjs/)
|
||||
assert.match(source, /`\/api\/platform\/v1\/inbounds\/\$\{query\(inboundId\)\}\/measurements\/reject\?tenantId=\$\{query\(tenantId\)\}`/)
|
||||
assert.doesNotMatch(source, /path\.replace\('\/measurement\/submit', '\/measurements\/reject'\)/)
|
||||
assert.match(source, /入库列表保留 MEASURE_REJECTED/)
|
||||
assert.match(source, /稳定 Admin 契约未开放 measurements\/reject/)
|
||||
})
|
||||
|
||||
test('FBA 状态样本为订单头和每条货物行提供四个必填标识', () => {
|
||||
@@ -60,9 +60,7 @@ test('测量状态截停发生在正确的 HTTP 边界', () => {
|
||||
assert.match(source, /target !== 'MEASURING'/)
|
||||
assert.doesNotMatch(source, /target === 'MEASURING' && ok\(response\)/)
|
||||
assert.match(source, /target === 'MEASURE_PENDING' && isSubmit/)
|
||||
assert.match(source, /target === 'MEASURE_REJECTED' && isSubmit/)
|
||||
assert.match(source, /measurements\/reject/)
|
||||
assert.match(source, /expectedVersion: version/)
|
||||
assert.doesNotMatch(source, /target === 'MEASURE_REJECTED' && isSubmit/)
|
||||
})
|
||||
|
||||
test('入库不可独立保留状态显式阻断', () => {
|
||||
|
||||
@@ -191,18 +191,6 @@ async function prepareInboundStatus(context, target) {
|
||||
if (!ok(response)) throw new Error(`MEASURE_PENDING 提交失败: HTTP=${response?.http}, code=${response?.body?.code}`)
|
||||
throw stop
|
||||
}
|
||||
if (target === 'MEASURE_REJECTED' && isSubmit) {
|
||||
const submitted = await originalRequest.call(context, method, path, body, token, headers)
|
||||
if (!ok(submitted)) throw new Error(`MEASURE_REJECTED 提交失败: HTTP=${submitted?.http}, code=${submitted?.body?.code}`)
|
||||
const latest = await originalRequest.call(context, 'GET', detailPath, undefined, token)
|
||||
const version = Number(data(latest)?.summary?.version)
|
||||
const rejectPath = `/api/platform/v1/inbounds/${query(inboundId)}/measurements/reject?tenantId=${query(tenantId)}`
|
||||
const rejected = await originalRequest.call(context, 'POST', rejectPath,
|
||||
{ measurementId: body?.measurementId, expectedVersion: version, reason: `${fixtureMarker} 保留审核驳回样本` },
|
||||
token, { 'Idempotency-Key': `${fixtureMarker}-reject`.slice(0, 128) })
|
||||
if (!ok(rejected)) throw new Error(`MEASURE_REJECTED 审核驳回失败: HTTP=${rejected?.http}, code=${rejected?.body?.code}, requestId=${rejected?.requestId || 'missing'}, traceId=${rejected?.traceId || 'missing'}`)
|
||||
throw stop
|
||||
}
|
||||
return originalRequest.call(context, method, path, body, token, headers)
|
||||
}
|
||||
if (target !== 'MEASURING') {
|
||||
@@ -303,7 +291,7 @@ export async function run(context, matrix = context.createdResources.manualState
|
||||
id: waitMeasure.orderId, status: 'WAIT_MEASURE', detailPath: `/orders/${waitMeasure.orderId}`,
|
||||
}
|
||||
|
||||
for (const status of ['MEASURING', 'MEASURE_PENDING', 'MEASURE_REJECTED', 'WAIT_SPLIT']) {
|
||||
for (const status of ['MEASURING', 'MEASURE_PENDING', 'WAIT_SPLIT']) {
|
||||
const sample = await prepareInboundStatus(context, status)
|
||||
result.inbounds[status] = sample
|
||||
matrix.pages.inbounds.fixtures[status] = {
|
||||
@@ -317,6 +305,9 @@ export async function run(context, matrix = context.createdResources.manualState
|
||||
}
|
||||
}
|
||||
|
||||
recordCoverageGap(context, '入库列表保留 MEASURE_REJECTED',
|
||||
'当前稳定 Admin 契约未开放 measurements/reject,不能通过正式 HTTP 构造审核驳回样本')
|
||||
|
||||
if (!result.inbounds.WAIT_SPLIT) {
|
||||
const legacyResponse = await context.request('GET',
|
||||
`/api/platform/v1/inbounds?tenantId=${query(fixture.tenantId)}&processStatus=WAIT_SPLIT&page=1&size=20`)
|
||||
|
||||
@@ -484,6 +484,7 @@ CREATE TABLE IF NOT EXISTS ll_order_detail (
|
||||
|
||||
CREATE TABLE IF NOT EXISTS ll_loading_order_status_history (
|
||||
id BIGINT NOT NULL AUTO_INCREMENT COMMENT '状态历史主键',
|
||||
customer_id VARCHAR(128) NOT NULL COMMENT '客户数据隔离键',
|
||||
order_id BIGINT NOT NULL COMMENT '尾程订单ID',
|
||||
from_status VARCHAR(32) NULL COMMENT '变更前状态',
|
||||
to_status VARCHAR(32) NOT NULL COMMENT '变更后状态',
|
||||
@@ -491,7 +492,7 @@ CREATE TABLE IF NOT EXISTS ll_loading_order_status_history (
|
||||
remark VARCHAR(500) NULL COMMENT '状态变更备注',
|
||||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '记录时间',
|
||||
PRIMARY KEY (id),
|
||||
KEY idx_ll_loading_history_order (order_id,created_at)
|
||||
KEY idx_ll_loading_history_customer_order (customer_id,order_id,created_at)
|
||||
) ENGINE=InnoDB COMMENT='尾程订单状态变更历史';
|
||||
|
||||
-- Existing order installations: add transfer fields without replacing row data.
|
||||
@@ -3685,6 +3686,20 @@ SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.statistics WHERE
|
||||
AND table_name='ll_local_looseboard_order' AND index_name='uk_ll_local_looseboard_lastleg'), 'SELECT 1',
|
||||
'ALTER TABLE ll_local_looseboard_order ADD UNIQUE KEY uk_ll_local_looseboard_lastleg(lastleg_order_id)'));
|
||||
PREPARE phase4_looseboard_tail_030 FROM @ddl; EXECUTE phase4_looseboard_tail_030; DEALLOCATE PREPARE phase4_looseboard_tail_030;
|
||||
SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE()
|
||||
AND table_name='ll_loading_order_status_history' AND column_name='customer_id'), 'SELECT 1',
|
||||
'ALTER TABLE ll_loading_order_status_history ADD COLUMN customer_id VARCHAR(128) NULL COMMENT ''客户数据隔离键'' AFTER id'));
|
||||
PREPARE phase4_loading_history_customer_030 FROM @ddl; EXECUTE phase4_loading_history_customer_030; DEALLOCATE PREPARE phase4_loading_history_customer_030;
|
||||
UPDATE ll_loading_order_status_history history
|
||||
JOIN ll_order orders ON orders.id=history.order_id
|
||||
SET history.customer_id=orders.customer_id
|
||||
WHERE history.customer_id IS NULL;
|
||||
ALTER TABLE ll_loading_order_status_history
|
||||
MODIFY COLUMN customer_id VARCHAR(128) NOT NULL COMMENT '客户数据隔离键';
|
||||
SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.statistics WHERE table_schema=DATABASE()
|
||||
AND table_name='ll_loading_order_status_history' AND index_name='idx_ll_loading_history_customer_order'), 'SELECT 1',
|
||||
'ALTER TABLE ll_loading_order_status_history ADD KEY idx_ll_loading_history_customer_order(customer_id,order_id,created_at)'));
|
||||
PREPARE phase4_loading_history_customer_030 FROM @ddl; EXECUTE phase4_loading_history_customer_030; DEALLOCATE PREPARE phase4_loading_history_customer_030;
|
||||
|
||||
-- ============================================================================
|
||||
-- 2026-08-20 / 029
|
||||
|
||||
Reference in New Issue
Block a user