feat: add self-operated analytics and enforce Lastleg module boundaries

This commit is contained in:
2026-10-12 20:27:35 +08:00
parent c06e788b0f
commit a1f1d93902
174 changed files with 13514 additions and 490 deletions
@@ -0,0 +1,111 @@
package com.baiyue.lastleg.schema;
import org.junit.jupiter.api.Test;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.regex.Pattern;
import static org.junit.jupiter.api.Assertions.*;
class SelfoperatedAnalyticsSchemaContractTest {
private static final Pattern TABLE = Pattern.compile(
"(?s)CREATE TABLE (ll_selfop_\\w+) \\(.*?\\) ENGINE=InnoDB[^;]+;");
private static Map<String, String> tables(String sql) {
var result = new LinkedHashMap<String, String>();
var matcher = TABLE.matcher(sql);
while (matcher.find()) {
assertNull(result.put(matcher.group(1), matcher.group()), "duplicate table definition");
}
return result;
}
@Test
void sourceAndEmptyDatabaseBaselineContainIdenticalTenIndependentTables() throws Exception {
var source = tables(Files.readString(Path.of("src/main/resources/db/lastleg.sql")));
var deployment = tables(Files.readString(Path.of("../deploy/sql/海外区域统一增量-MySQL8.sql")));
assertEquals(10, source.size());
assertEquals(source, deployment);
for (var entry : source.entrySet()) {
var ddl = entry.getValue();
assertTrue(ddl.contains("COLLATE=utf8mb4_bin"), entry.getKey());
assertTrue(ddl.contains("id BIGINT NOT NULL"));
assertTrue(ddl.contains("created_at DATETIME(3) NOT NULL"));
assertTrue(ddl.contains("updated_at DATETIME(3) NOT NULL"));
assertFalse(ddl.contains("AUTO_INCREMENT"));
assertFalse(ddl.contains("tenant_id"));
assertFalse(ddl.contains("FOREIGN KEY"));
}
}
@Test
void unknownSourceTimeAndStockCannotBecomeZeroOrEligibleSalesByDefault() throws Exception {
var ddl = tables(Files.readString(Path.of("src/main/resources/db/lastleg.sql")));
for (var table : new String[]{"ll_selfop_sync_scope", "ll_selfop_outbound_order"}) {
assertTrue(ddl.get(table).contains("source_timezone VARCHAR(64) NULL COMMENT"));
}
var stock = ddl.get("ll_selfop_inventory_snapshot");
for (var column : new String[]{"physical_quantity", "available_quantity", "frozen_quantity", "transit_quantity"}) {
assertTrue(stock.contains(column + " DECIMAL(20,6) NULL COMMENT"));
}
assertTrue(ddl.get("ll_selfop_outbound_order").contains("sales_eligible TINYINT NOT NULL DEFAULT 0"));
assertTrue(ddl.get("ll_selfop_outbound_order").contains("source_status = 100 AND source_timezone IS NOT NULL"));
assertTrue(ddl.get("ll_selfop_outbound_item").contains("CHECK (actual_quantity >= 0)"));
// Negative observed stock must be retained as INVALID, never rejected/coerced by a quantity CHECK.
assertFalse(stock.contains("CHECK (available_quantity"));
}
@Test
void durableRecoveryAndImmutableVersionKeysArePresent() throws Exception {
var ddl = tables(Files.readString(Path.of("src/main/resources/db/lastleg.sql")));
var task = ddl.get("ll_selfop_sync_task");
for (var column : new String[]{"apply_state", "affected_warehouse_codes_json", "observation_baseline",
"routing_manifest_json", "request_hash", "apply_completed_at_utc", "task_no", "execution_purpose",
"validation_root_task_id", "validation_budget_json", "result_published_run_id", "progress_json",
"progress_updated_at_utc"}) {
assertTrue(task.contains(" " + column + " "), column);
}
assertTrue(task.contains("UNIQUE KEY uk_selfop_task_no (task_no)"));
var account = ddl.get("ll_selfop_outbound_discovery_scope");
for (var column : new String[]{"api_budget_json", "api_cooldown_until_utc", "api_pause_reason"}) {
assertTrue(account.contains(" " + column + " "), column);
}
var run = ddl.get("ll_selfop_sync_run");
for (var column : new String[]{"execution_purpose", "build_started_at_utc", "superseded_at_utc",
"progress_snapshot_json", "heartbeat_at_utc", "inventory_daily_sample_date", "inventory_sample_rule_version"}) {
assertTrue(run.contains(" " + column + " "), column);
}
assertTrue(run.contains("uk_selfop_inventory_daily (source_account,warehouse_code,inventory_daily_sample_date)"));
assertTrue(run.contains("CONSTRAINT chk_selfop_inventory_daily CHECK"));
assertTrue(ddl.get("ll_selfop_sku_daily_sales").contains("idx_selfop_daily_window (sales_run_id,business_date,customer_code,sku_code)"));
assertTrue(ddl.get("ll_selfop_outbound_order").contains("idx_selfop_order_daily (source_account,warehouse_code,sales_eligible,business_date,id)"));
assertTrue(ddl.get("ll_selfop_sync_stage").contains("stage_batch_id,object_type,object_key_hash"));
assertTrue(ddl.get("ll_selfop_sync_stage").contains("observation_sequence IS NOT NULL"));
assertTrue(ddl.get("ll_selfop_outbound_order").contains("source_account,customer_code,delivery_no"));
assertTrue(ddl.get("ll_selfop_sku_daily_sales").contains("sales_run_id,source_account,customer_code,warehouse_code,sku_code,business_date"));
assertTrue(ddl.get("ll_selfop_sku_profile").contains("profile_run_id,source_account,customer_code,warehouse_code,sku_code"));
assertTrue(ddl.get("ll_selfop_inventory_snapshot").contains("snapshot_run_id,source_account,customer_code,warehouse_code,sku_code,product_quality"));
}
@Test
void readAndSyncPermissionsAreIndependentWithThreeAuthorizedAdminMenus() throws Exception {
var seed = Files.readString(Path.of("../iam/src/main/resources/db/selfoperated-analytics-permissions.sql"));
var unified = Files.readString(Path.of("../deploy/sql/头程数据库统一增量-MySQL8.sql"));
assertTrue(unified.contains(seed.strip()));
assertTrue(seed.contains("platform:self-operated-analytics:list"));
assertTrue(seed.contains("platform:self-operated-analytics:sync"));
for (var route : new String[]{"/self-operated/sales", "/self-operated/inventory", "/self-operated/collection"}) {
assertTrue(seed.contains(route));
}
assertTrue(seed.contains("'API',5118100,'platform:self-operated-analytics:list'"));
assertTrue(seed.contains("'API',5118130,'platform:self-operated-analytics:sync'"));
assertFalse(seed.contains("'MERCHANT'"));
assertFalse(seed.contains("INSERT IGNORE"));
assertFalse(seed.contains("ON DUPLICATE KEY"));
assertTrue(seed.contains("(4000001,5118000,0,"));
assertTrue(seed.contains("(4000001,5118001,0,"));
}
}