fix(shadow): capture and validate inbound task evidence for v3.4

This commit is contained in:
zgw
2026-09-08 10:37:14 +08:00
parent 699310e61d
commit c387ba0b50
17 changed files with 1552 additions and 10 deletions

View File

@@ -10,31 +10,37 @@ set "DATABASE=%~2"
set "MODE=%~3"
set "SQL_FILE="
set "V33_FILE=%~dp0..\fortucky-algorithm\src\main\resources\db\sqlserver\upgrade-shadow-v3-to-v33.sql"
set "V34_FILE=%~dp0..\fortucky-algorithm\src\main\resources\db\sqlserver\upgrade-shadow-v33-to-v34.sql"
if /I "%~3"=="upgrade" set "SQL_FILE=%~dp0..\fortucky-algorithm\src\main\resources\db\sqlserver\upgrade-shadow-v2-to-v3.sql"
if /I "%~3"=="clean" set "SQL_FILE=%~dp0..\fortucky-algorithm\src\main\resources\db\sqlserver\install-shadow.sql"
if /I "%~3"=="v33" set "SQL_FILE=%V33_FILE%"
if not defined SQL_FILE (call :fail "mode must be upgrade, clean or v33" & exit /b 1)
if /I "%~3"=="v34" set "SQL_FILE=%V34_FILE%"
if not defined SQL_FILE (call :fail "mode must be upgrade, clean, v33 or v34" & exit /b 1)
if not exist "%SQL_FILE%" (call :fail "SQL package file is missing" & exit /b 1)
if not exist "%V33_FILE%" (call :fail "V3.3 SQL package file is missing" & exit /b 1)
if not exist "%V34_FILE%" (call :fail "V3.4 SQL package file is missing" & exit /b 1)
where sqlcmd >nul 2>nul
if errorlevel 1 (call :fail "sqlcmd is not available on PATH" & exit /b 1)
echo WARNING: stop the WMS shadow-enabled JVM and wait over 120 seconds first.
echo Installing FT546WMS shadow V3.3 in %MODE% mode...
echo Installing FT546WMS shadow package in %MODE% mode...
sqlcmd -S "%SQL_SERVER%" -d "%DATABASE%" -E -b -r1 -i "%SQL_FILE%"
if errorlevel 1 exit /b 1
if /I "%MODE%"=="v33" goto :installed
if /I "%MODE%"=="v34" goto :installed
sqlcmd -S "%SQL_SERVER%" -d "%DATABASE%" -E -b -r1 -i "%V33_FILE%"
if errorlevel 1 exit /b 1
sqlcmd -S "%SQL_SERVER%" -d "%DATABASE%" -E -b -r1 -i "%V34_FILE%"
if errorlevel 1 exit /b 1
:installed
echo INSTALL_OK mode=%MODE%
exit /b 0
:usage
echo Usage: %~nx0 SQL_SERVER DATABASE upgrade^|clean^|v33 1>&2
echo Usage: %~nx0 SQL_SERVER DATABASE upgrade^|clean^|v33^|v34 1>&2
exit /b 2
:fail

View File

@@ -52,10 +52,13 @@ if errorlevel 1 (call :fail "jps could not inspect running JVMs" & exit /b 1)
for /f %%I in ('jps -lv 2^>nul ^| findstr /R /I /C:"^[0-9][0-9]* com\.ruoyi\.RuoYiApplication" ^| find /C /V ""') do set "WMS_PROCESS_COUNT=%%I"
if %WMS_PROCESS_COUNT% GTR 1 (call :fail "more than one FT546WMS JVM is running" & exit /b 1)
set "PREFLIGHT_SQL=SET NOCOUNT ON; IF SCHEMA_ID(N'alg') IS NULL SELECT N'CLEAN_INSTALL_ELIGIBLE' AS package_state; ELSE BEGIN IF OBJECT_ID(N'alg.shadow_package_manifest', N'U') IS NULL BEGIN; THROW 51050, 'shadow manifest is missing', 1; END; IF (SELECT COUNT(*) FROM alg.shadow_package_manifest) <> 1 OR NOT EXISTS (SELECT 1 FROM alg.shadow_package_manifest WHERE package_id='FT546WMS_SHADOW_ONLY' AND package_version IN ('2026-08-12-1','2026-09-03-3','2026-09-07-33')) BEGIN; THROW 51051, 'shadow manifest is not a supported version', 1; END; IF EXISTS (SELECT 1 FROM alg.shadow_package_manifest WHERE package_id='FT546WMS_SHADOW_ONLY' AND package_version IN ('2026-09-03-3','2026-09-07-33')) BEGIN IF OBJECT_ID(N'alg.shadow_snapshot_state', N'U') IS NULL BEGIN; THROW 51052, 'snapshot state is missing', 1; END; IF OBJECT_ID(N'alg.shadow_instance_heartbeat', N'U') IS NULL BEGIN; THROW 51055, 'shadow instance heartbeat is missing', 1; END; IF NOT EXISTS (SELECT 1 FROM alg.shadow_snapshot_state WHERE warehouse_id='01' AND snapshot_generation>0 AND snapshot_status='PUBLISHED' AND synchronized_at IS NOT NULL AND DATEDIFF(SECOND,synchronized_at,SYSUTCDATETIME()) BETWEEN 0 AND 300 AND source_signature IS NOT NULL AND last_error IS NULL) BEGIN; THROW 51053, 'published warehouse 01 snapshot is unusable', 1; END; IF EXISTS (SELECT 1 FROM alg.shadow_instance_heartbeat WHERE warehouse_id='01' AND ((DATEDIFF(SECOND,last_seen_at,SYSUTCDATETIME()) BETWEEN 0 AND 120) OR (DATEDIFF(SECOND,claimed_at,SYSUTCDATETIME()) BETWEEN 0 AND 120))) BEGIN; THROW 51054, 'a recent shadow instance heartbeat or claim exists', 1; END; END; SELECT package_id, package_version, installed_at FROM alg.shadow_package_manifest; IF OBJECT_ID(N'alg.shadow_snapshot_state', N'U') IS NOT NULL SELECT warehouse_id, snapshot_generation, snapshot_status, synchronized_at, CONVERT(varchar(64), source_signature, 2) AS source_signature, last_error FROM alg.shadow_snapshot_state; END"
set "PREFLIGHT_SQL=SET NOCOUNT ON; IF SCHEMA_ID(N'alg') IS NULL SELECT N'CLEAN_INSTALL_ELIGIBLE' AS package_state; ELSE BEGIN IF OBJECT_ID(N'alg.shadow_package_manifest', N'U') IS NULL BEGIN; THROW 51050, 'shadow manifest is missing', 1; END; IF (SELECT COUNT(*) FROM alg.shadow_package_manifest) <> 1 OR NOT EXISTS (SELECT 1 FROM alg.shadow_package_manifest WHERE package_id='FT546WMS_SHADOW_ONLY' AND package_version IN ('2026-08-12-1','2026-09-03-3','2026-09-07-33','2026-09-07-34')) BEGIN; THROW 51051, 'shadow manifest is not a supported version', 1; END; IF EXISTS (SELECT 1 FROM alg.shadow_package_manifest WHERE package_id='FT546WMS_SHADOW_ONLY' AND package_version IN ('2026-09-03-3','2026-09-07-33','2026-09-07-34')) BEGIN IF OBJECT_ID(N'alg.shadow_snapshot_state', N'U') IS NULL BEGIN; THROW 51052, 'snapshot state is missing', 1; END; IF OBJECT_ID(N'alg.shadow_instance_heartbeat', N'U') IS NULL BEGIN; THROW 51055, 'shadow instance heartbeat is missing', 1; END; IF NOT EXISTS (SELECT 1 FROM alg.shadow_snapshot_state WHERE warehouse_id='01' AND snapshot_generation>0 AND snapshot_status='PUBLISHED' AND synchronized_at IS NOT NULL AND DATEDIFF(SECOND,synchronized_at,SYSUTCDATETIME()) BETWEEN 0 AND 300 AND source_signature IS NOT NULL AND last_error IS NULL) BEGIN; THROW 51053, 'published warehouse 01 snapshot is unusable', 1; END; IF EXISTS (SELECT 1 FROM alg.shadow_instance_heartbeat WHERE warehouse_id='01' AND ((DATEDIFF(SECOND,last_seen_at,SYSUTCDATETIME()) BETWEEN 0 AND 120) OR (DATEDIFF(SECOND,claimed_at,SYSUTCDATETIME()) BETWEEN 0 AND 120))) BEGIN; THROW 51054, 'a recent shadow instance heartbeat or claim exists', 1; END; END; SELECT package_id, package_version, installed_at FROM alg.shadow_package_manifest; IF OBJECT_ID(N'alg.shadow_snapshot_state', N'U') IS NOT NULL SELECT warehouse_id, snapshot_generation, snapshot_status, synchronized_at, CONVERT(varchar(64), source_signature, 2) AS source_signature, last_error FROM alg.shadow_snapshot_state; END"
sqlcmd -S "%SQL_SERVER%" -d "%DATABASE%" -E -b -r1 -Q "%PREFLIGHT_SQL%"
if errorlevel 1 (call :fail "SQL connectivity or shadow package validation failed" & exit /b 1)
sqlcmd -S "%SQL_SERVER%" -d "%DATABASE%" -E -b -r1 -i "%~dp0shadow-v34-schema-check.sql"
if errorlevel 1 (call :fail "shadow package schema validation failed" & exit /b 1)
echo PREFLIGHT_OK branch=%CURRENT_BRANCH% commit=%CURRENT_COMMIT% wmsProcesses=%WMS_PROCESS_COUNT%
exit /b 0

View File

@@ -0,0 +1,85 @@
/* Read-only structural gate for installed V3.4 packages. No refresh or business procedure calls. */
SET NOCOUNT ON;
SET LOCK_TIMEOUT 5000;
SET DEADLOCK_PRIORITY LOW;
IF OBJECT_ID(N'alg.shadow_package_manifest', N'U') IS NULL RETURN;
IF NOT EXISTS (SELECT 1 FROM alg.shadow_package_manifest
WHERE package_id = 'FT546WMS_SHADOW_ONLY' AND package_version = '2026-09-07-34') RETURN;
DECLARE @expected TABLE (object_name sysname PRIMARY KEY, object_type char(2) NOT NULL);
INSERT INTO @expected VALUES
(N'shadow_package_manifest','U'),(N'warehouse_policy','U'),(N'project_policy','U'),
(N'space_role','U'),(N'space_topology','U'),(N'inventory_item_snapshot','U'),
(N'space_inventory_summary','U'),(N'lane_state','U'),(N'reservation','U'),
(N'deep_workflow_lock','U'),(N'decision_log','U'),(N'shadow_snapshot_state','U'),
(N'cargo_space_snapshot','U'),(N'active_job_snapshot','U'),
(N'shadow_inbound_request_snapshot','U'),(N'shadow_outbound_request_snapshot','U'),
(N'shadow_runtime_metric','U'),(N'shadow_instance_heartbeat','U'),
(N'v_inventory_item','V'),(N'v_active_job','V'),(N'v_space_state','V'),
(N'usp_rebuild_space_topology','P'),(N'usp_sync_inventory_snapshot','P'),
(N'usp_compute_lane_semantic_fingerprint','P'),(N'usp_rebuild_lane_state','P'),
(N'usp_refresh_dirty_lanes','P'),(N'usp_claim_shadow_instance','P'),
(N'usp_refresh_shadow_snapshot','P');
IF EXISTS (SELECT 1 FROM sys.objects actual LEFT JOIN @expected expected
ON expected.object_name = actual.name AND expected.object_type = actual.type COLLATE DATABASE_DEFAULT
WHERE actual.schema_id = SCHEMA_ID(N'alg') AND actual.is_ms_shipped = 0
AND actual.parent_object_id = 0 AND expected.object_name IS NULL)
THROW 51071, 'V3.4 schema check failed: unknown alg object', 1;
IF EXISTS (SELECT 1 FROM @expected expected LEFT JOIN sys.objects actual
ON actual.schema_id = SCHEMA_ID(N'alg') AND actual.name = expected.object_name
AND actual.type COLLATE DATABASE_DEFAULT = expected.object_type
WHERE actual.object_id IS NULL)
THROW 51072, 'V3.4 schema check failed: expected alg object missing or wrong type', 1;
IF EXISTS (SELECT 1 FROM sys.triggers t JOIN sys.objects parent ON parent.object_id = t.parent_id
WHERE parent.schema_id = SCHEMA_ID(N'alg'))
THROW 51073, 'V3.4 schema check failed: unexpected shadow trigger', 1;
DECLARE @columns TABLE (
table_name sysname, column_name sysname, type_name sysname, max_length smallint,
precision_value tinyint, scale_value tinyint, PRIMARY KEY (table_name, column_name));
INSERT INTO @columns VALUES
(N'inventory_item_snapshot',N'spare1',N'varchar',255,0,0),
(N'inventory_item_snapshot',N'spare2',N'varchar',255,0,0),
(N'inventory_item_snapshot',N'spare3',N'varchar',255,0,0),
(N'inventory_item_snapshot',N'voltage',N'varchar',255,0,0),
(N'inventory_item_snapshot',N'phase',N'varchar',255,0,0),
(N'inventory_item_snapshot',N'tray_job_count',N'int',4,10,0),
(N'shadow_outbound_request_snapshot',N'spare1',N'varchar',255,0,0),
(N'shadow_outbound_request_snapshot',N'spare2',N'varchar',255,0,0),
(N'shadow_outbound_request_snapshot',N'spare3',N'varchar',255,0,0),
(N'shadow_outbound_request_snapshot',N'voltage',N'varchar',255,0,0),
(N'shadow_outbound_request_snapshot',N'phase',N'varchar',255,0,0),
(N'shadow_outbound_request_snapshot',N'allocated_quantity',N'decimal',17,38,6),
(N'shadow_outbound_request_snapshot',N'predicate_version',N'int',4,10,0),
(N'cargo_space_snapshot',N'this_cargo_area',N'varchar',255,0,0),
(N'shadow_inbound_request_snapshot',N'spare1',N'varchar',255,0,0),
(N'shadow_inbound_request_snapshot',N'spare2',N'varchar',255,0,0),
(N'shadow_inbound_request_snapshot',N'spare3',N'varchar',255,0,0),
(N'shadow_inbound_request_snapshot',N'fault_flag',N'varchar',255,0,0),
(N'shadow_inbound_request_snapshot',N'detail_count',N'int',4,10,0),
(N'shadow_inbound_request_snapshot',N'field_group_count',N'int',4,10,0),
(N'shadow_inbound_request_snapshot',N'product_scope_count',N'int',4,10,0),
(N'shadow_inbound_request_snapshot',N'tray_job_count',N'int',4,10,0),
(N'shadow_inbound_request_snapshot',N'legacy_scope_complete',N'int',4,10,0),
(N'shadow_inbound_request_snapshot',N'predicate_version',N'int',4,10,0);
IF EXISTS (
SELECT 1 FROM @columns expected
LEFT JOIN sys.columns column_info
ON column_info.object_id = OBJECT_ID(N'alg.' + expected.table_name)
AND column_info.name = expected.column_name
WHERE column_info.column_id IS NULL
OR TYPE_NAME(column_info.user_type_id) <> expected.type_name
OR column_info.max_length <> expected.max_length
OR column_info.precision <> expected.precision_value
OR column_info.scale <> expected.scale_value
OR column_info.is_nullable <> 1)
THROW 51074, 'V3.4 schema check failed: predicate columns missing or incompatible', 1;
DECLARE @definition nvarchar(max) = OBJECT_DEFINITION(OBJECT_ID(N'alg.usp_refresh_shadow_snapshot', N'P'));
IF @definition IS NULL OR CHARINDEX(N'INBOUND_JOB_FIELDS_V1', @definition) = 0
OR CHARINDEX(N'field_group_count', @definition) = 0 OR CHARINDEX(N'this_cargo_area', @definition) = 0
THROW 51075, 'V3.4 schema check failed: refresh procedure missing, unreadable or obsolete', 1;
PRINT 'V34_SCHEMA_OK';