| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280 |
- -- =====================================================================================
- -- 1.0.504 · S0 MASTER DATA PLATFORM · PILOT(Batch 1~3)
- --
- -- 目标:为 S0 建立「业务主表 → mdp_stg_s0_* → dim_*」的主数据供给链路(只读取源,FULL REPLACE)。
- -- S0 业务主表仍是 System of Record;本脚本不改任何 S0 业务表、不改任何共享 MDP 对象。
- --
- -- 新建 7 张表 + 4 条 mdp_entity:
- -- 贴源:mdp_stg_s0_work_center / mdp_stg_s0_department / mdp_stg_s0_location
- -- (后者被 LocationMaster 与 LocationShelfMaster 共用,靠 source_table 隔离)
- -- 维度:dim_work_center / dim_department / dim_location / dim_location_shelf
- --
- -- 明确不做(跨模块红线):
- -- · 不建、不改、不删 mdp_stg_location(1.0.300 建的共享表,dopdemorq 侧在用)
- -- · 不改 DOPDEMORQ_SQLSERVER 的 mdp_source / mdp_entity 任何一行
- -- · 不改 mdp_std_item / mdp_std_supplier 及 S3 同步服务
- -- · 不写 mdp_field_mapping:实测其运行时消费者只有 Excel 导入链路
- -- (MdpExcelImportService / MdpExcelTemplateService / MdpExcelValidation),
- -- DB 拉取链路读的是 mdp_entity.biz_key_expr —— 写了就是死配置
- --
- -- 幂等:CREATE TABLE IF NOT EXISTS + INSERT ... WHERE NOT EXISTS;DELETE = 0,UPDATE = 0。
- -- 回滚:见文件末尾注释块(全部为新建对象,可整体 DROP)。
- -- =====================================================================================
- -- =====================================================================================
- -- 一、贴源层(信封逐列对齐 1.0.300,另有 3 处刻意偏离,见下)
- --
- -- 偏离 1:UNIQUE 纳入 source_system。
- -- 1.0.300 的 uk 是 (tenant_id, source_table, source_row_id),不含 source_system;
- -- 而不同源系统的 source_table 可能同名(dopdemorq 侧就与本库同名),
- -- 且 MdpStagingWriter 的 ON DUPLICATE KEY UPDATE 不更新 source_system —— 会静默串源。
- -- 偏离 2:source_biz_key 放宽到 varchar(300)。
- -- 货架业务键 = domain_code(50)+'#'+location(100)+'#'+inv_shelf(100) 最长 252 字符,
- -- 原 varchar(200) 在 STRICT 模式下会直接报错。
- -- 偏离 3:factory_id 用 bigint(对齐较新的 mdp_stg_item 信封),而非 1.0.300 的 varchar(64)。
- -- MdpStagingWriter 写入的本就是 long?。
- -- =====================================================================================
- CREATE TABLE IF NOT EXISTS `mdp_stg_s0_work_center` (
- `id` bigint NOT NULL AUTO_INCREMENT,
- `tenant_id` bigint NOT NULL DEFAULT 0,
- `factory_id` bigint DEFAULT NULL,
- `source_system` varchar(50) NOT NULL DEFAULT 'AIDOPDEV_MYSQL',
- `source_table` varchar(100) NOT NULL,
- `source_row_id` varchar(100) NOT NULL,
- `source_biz_key` varchar(300) DEFAULT NULL,
- `sync_batch_id` varchar(100) NOT NULL,
- `sync_time` datetime NOT NULL,
- `process_status` varchar(20) NOT NULL DEFAULT 'PENDING',
- `raw_data` json NOT NULL,
- `process_message` varchar(500) DEFAULT NULL,
- `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
- `create_time` datetime DEFAULT CURRENT_TIMESTAMP,
- PRIMARY KEY (`id`),
- UNIQUE KEY `uk_stg_s0_work_center` (`tenant_id`,`source_system`,`source_table`,`source_row_id`),
- KEY `idx_stg_s0_work_center_biz` (`source_biz_key`),
- KEY `idx_stg_s0_work_center_batch` (`tenant_id`,`sync_batch_id`)
- ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='S0 主数据贴源:工作中心(源 WorkCtrMaster)';
- CREATE TABLE IF NOT EXISTS `mdp_stg_s0_department` (
- `id` bigint NOT NULL AUTO_INCREMENT,
- `tenant_id` bigint NOT NULL DEFAULT 0,
- `factory_id` bigint DEFAULT NULL,
- `source_system` varchar(50) NOT NULL DEFAULT 'AIDOPDEV_MYSQL',
- `source_table` varchar(100) NOT NULL,
- `source_row_id` varchar(100) NOT NULL,
- `source_biz_key` varchar(300) DEFAULT NULL,
- `sync_batch_id` varchar(100) NOT NULL,
- `sync_time` datetime NOT NULL,
- `process_status` varchar(20) NOT NULL DEFAULT 'PENDING',
- `raw_data` json NOT NULL,
- `process_message` varchar(500) DEFAULT NULL,
- `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
- `create_time` datetime DEFAULT CURRENT_TIMESTAMP,
- PRIMARY KEY (`id`),
- UNIQUE KEY `uk_stg_s0_department` (`tenant_id`,`source_system`,`source_table`,`source_row_id`),
- KEY `idx_stg_s0_department_biz` (`source_biz_key`),
- KEY `idx_stg_s0_department_batch` (`tenant_id`,`sync_batch_id`)
- ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='S0 主数据贴源:部门(源 DepartmentMaster)';
- -- LocationMaster 与 LocationShelfMaster 共用本表,靠 source_table 隔离。
- -- 货架源主键是 rec_id(不在 MdpDbPullExecutor.ResolveSourceRowId 的候选列名里),
- -- 会退化成随机 GUID;且父表 FULL Replace 会让 rec_id 每轮变化。
- -- 因此本链路:purge 分区 → FULL pull → 物化只认本批 sync_batch_id,
- -- 完全不依赖 source_row_id 的稳定性。
- CREATE TABLE IF NOT EXISTS `mdp_stg_s0_location` (
- `id` bigint NOT NULL AUTO_INCREMENT,
- `tenant_id` bigint NOT NULL DEFAULT 0,
- `factory_id` bigint DEFAULT NULL,
- `source_system` varchar(50) NOT NULL DEFAULT 'AIDOPDEV_MYSQL',
- `source_table` varchar(100) NOT NULL,
- `source_row_id` varchar(100) NOT NULL,
- `source_biz_key` varchar(300) DEFAULT NULL,
- `sync_batch_id` varchar(100) NOT NULL,
- `sync_time` datetime NOT NULL,
- `process_status` varchar(20) NOT NULL DEFAULT 'PENDING',
- `raw_data` json NOT NULL,
- `process_message` varchar(500) DEFAULT NULL,
- `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
- `create_time` datetime DEFAULT CURRENT_TIMESTAMP,
- PRIMARY KEY (`id`),
- UNIQUE KEY `uk_stg_s0_location` (`tenant_id`,`source_system`,`source_table`,`source_row_id`),
- KEY `idx_stg_s0_location_biz` (`source_biz_key`),
- KEY `idx_stg_s0_location_batch` (`tenant_id`,`source_table`,`sync_batch_id`)
- ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='S0 主数据贴源:库位 + 货架(源 LocationMaster / LocationShelfMaster)';
- -- =====================================================================================
- -- 二、维度层
- --
- -- 共同约定:
- -- · id 只是技术行号:FULL REPLACE 每轮重建即变化,**禁止任何外部按它建稳定引用**,下游一律按业务键 join。
- -- · tenant_id NOT NULL:dim 无 C# 实体、不进 CodeFirst,不受 ITenantIdFilter.TenantId 必须 long? 的约束。
- -- · is_active 是源 IsActive 的 1:1 透传,**不是** dim 自己的存在性状态;
- -- 源删除的行下一轮直接从 dim 消失(FULL REPLACE),不做 SCD、不加 is_present/deleted_at。
- -- · 排序规则与源表一致(utf8mb4_0900_ai_ci),保证 dim UK 的判等语义与源 UK 完全相同。
- -- · 无 DB 外键:子维度孤儿策略是 ALLOW + QUALITY WARNING(保留并外化,不丢弃、不阻断管线)。
- -- =====================================================================================
- -- 源 WorkCtrMaster 没有 company_ref_id / factory_ref_id 两列,故本 dim 也不设 —— 不为四表整齐而发明数据。
- CREATE TABLE IF NOT EXISTS `dim_work_center` (
- `id` bigint NOT NULL AUTO_INCREMENT COMMENT '技术行号;每轮 FULL 重建即变,禁止外部引用',
- `tenant_id` bigint NOT NULL COMMENT '租户',
- `domain_code` varchar(50) NOT NULL COMMENT '工厂域编码 ← raw_data.$.Domain',
- `work_center_code` varchar(100) NOT NULL COMMENT '工作中心编码(业务键)← $.WorkCtr',
- `work_center_name` varchar(255) DEFAULT NULL COMMENT '← $.Descr',
- `department_code` varchar(100) DEFAULT NULL COMMENT '所属部门编码 ← $.Department;按业务键引用 dim_department,无 DB FK',
- `is_active` tinyint(1) NOT NULL COMMENT '源 IsActive 透传(raw_data 中为 JSON BOOLEAN)',
- `source_system` varchar(50) NOT NULL,
- `source_biz_key` varchar(300) NOT NULL COMMENT '回溯 staging 用',
- `source_updated_at` datetime DEFAULT NULL COMMENT '← $.UpdateTime',
- `sync_batch_id` varchar(64) NOT NULL,
- `sync_time` datetime NOT NULL,
- `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
- `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
- PRIMARY KEY (`id`),
- UNIQUE KEY `uk_dim_work_center` (`tenant_id`,`domain_code`,`work_center_code`),
- KEY `idx_dim_work_center_batch` (`sync_batch_id`)
- ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='S0 主数据维度:工作中心。源 WorkCtrMaster,FULL REPLACE。';
- CREATE TABLE IF NOT EXISTS `dim_department` (
- `id` bigint NOT NULL AUTO_INCREMENT COMMENT '技术行号;禁止外部引用',
- `tenant_id` bigint NOT NULL,
- `company_id` bigint NOT NULL COMMENT '← $.company_ref_id;legacy 数值域与 SysOrg 雪花域并存,原样透传',
- `factory_id` bigint NOT NULL COMMENT '← $.factory_ref_id;与 domain_code 非 1:1,属性非身份',
- `domain_code` varchar(50) NOT NULL COMMENT '← $.Domain(物理列名,不是 domain_code)',
- `department_code` varchar(100) NOT NULL COMMENT '部门编码(业务键)← $.Department',
- `department_name` varchar(255) DEFAULT NULL COMMENT '← $.Descr',
- `is_active` tinyint(1) NOT NULL COMMENT '源 IsActive 透传',
- `source_system` varchar(50) NOT NULL,
- `source_biz_key` varchar(300) NOT NULL,
- `source_updated_at` datetime DEFAULT NULL COMMENT '← $.UpdateTime;源侧存在恒 NULL 的历史行,FULL 语义下不受影响',
- `sync_batch_id` varchar(64) NOT NULL,
- `sync_time` datetime NOT NULL,
- `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
- `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
- PRIMARY KEY (`id`),
- UNIQUE KEY `uk_dim_department` (`tenant_id`,`domain_code`,`department_code`),
- KEY `idx_dim_department_batch` (`sync_batch_id`)
- ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='S0 主数据维度:部门。源 DepartmentMaster,FULL REPLACE。';
- -- 🔴 源 LocationMaster 是 dual-column 复刻表,snake_case 一侧是**填了假值的死列**(不是 NULL):
- -- rec_id 恒 0 / domain_code 恒空串 / is_active 恒 0 / physical_address 恒 NULL / create_user 恒 NULL。
- -- 因此 COALESCE(rec_id, RecID) 这类防御写法无效,WHERE is_active=1 会静默返回空集。
- -- 本 dim 的所有取值一律显式声明 PascalCase 真列,见 S0DimCatalog.Location。
- CREATE TABLE IF NOT EXISTS `dim_location` (
- `id` bigint NOT NULL AUTO_INCREMENT COMMENT '技术行号;禁止外部引用',
- `tenant_id` bigint NOT NULL,
- `company_id` bigint NOT NULL COMMENT '← $.company_ref_id',
- `factory_id` bigint NOT NULL COMMENT '← $.factory_ref_id',
- `domain_code` varchar(50) NOT NULL COMMENT '← $.Domain(真列)。禁止读 $.domain_code —— 该影子列恒空串',
- `location_code` varchar(100) NOT NULL COMMENT '库位编码(业务键)← $.location',
- `location_name` varchar(255) DEFAULT NULL COMMENT '← $.descr',
- `location_type` varchar(50) DEFAULT NULL COMMENT '← $.typed',
- `storer` varchar(300) DEFAULT NULL COMMENT '货主/保管方 ← $.storer',
- `physical_address` varchar(500) DEFAULT NULL COMMENT '← $.PhysicalAddress(真列)。禁止读 $.physical_address',
- `is_active` tinyint(1) NOT NULL COMMENT '← $.IsActive(真列)。禁止读 $.is_active —— 影子列恒 0',
- `source_system` varchar(50) NOT NULL,
- `source_biz_key` varchar(300) NOT NULL,
- `source_updated_at` datetime DEFAULT NULL COMMENT '← $.UpdateTime',
- `sync_batch_id` varchar(64) NOT NULL,
- `sync_time` datetime NOT NULL,
- `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
- `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
- PRIMARY KEY (`id`),
- UNIQUE KEY `uk_dim_location` (`tenant_id`,`domain_code`,`location_code`),
- KEY `idx_dim_location_code` (`tenant_id`,`location_code`) COMMENT '支撑 Reconciler 的「镜像源 UK (tenant_id,location) 唯一性」断言',
- KEY `idx_dim_location_batch` (`sync_batch_id`)
- ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='S0 主数据维度:库位。源 LocationMaster(dual-column,域列真名 Domain),FULL REPLACE。';
- -- 源 LocationShelfMaster 全列 snake_case、无影子列 —— 不可套用 LocationMaster 的禁用规则。
- -- 源无 IsActive 列,故本 dim 也不设 is_active(不发明状态语义)。
- CREATE TABLE IF NOT EXISTS `dim_location_shelf` (
- `id` bigint NOT NULL AUTO_INCREMENT COMMENT '技术行号;禁止外部引用(源 rec_id 每次父表保存即变,更不可引用)',
- `tenant_id` bigint NOT NULL,
- `company_id` bigint NOT NULL COMMENT '← $.company_ref_id',
- `factory_id` bigint NOT NULL COMMENT '← $.factory_ref_id',
- `domain_code` varchar(50) NOT NULL COMMENT '← $.domain_code(本表确为 snake_case)|父引用',
- `location_code` varchar(100) NOT NULL COMMENT '← $.location|父引用 → dim_location(tenant_id,domain_code,location_code)',
- `shelf_code` varchar(100) NOT NULL COMMENT '货架编码(业务键)← $.inv_shelf',
- `shelf_name` varchar(255) DEFAULT NULL COMMENT '← $.descr',
- `area` varchar(100) DEFAULT NULL COMMENT '← $.area',
- `source_system` varchar(50) NOT NULL,
- `source_biz_key` varchar(300) NOT NULL,
- `source_updated_at` datetime DEFAULT NULL COMMENT '← $.update_time;源侧该列全为 NULL,本列现阶段恒 NULL(保留以便源将来启用)',
- `sync_batch_id` varchar(64) NOT NULL,
- `sync_time` datetime NOT NULL,
- `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
- `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
- PRIMARY KEY (`id`),
- UNIQUE KEY `uk_dim_location_shelf` (`tenant_id`,`domain_code`,`location_code`,`shelf_code`),
- KEY `idx_dim_location_shelf_parent` (`tenant_id`,`domain_code`,`location_code`),
- KEY `idx_dim_location_shelf_batch` (`sync_batch_id`)
- ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='S0 主数据维度子表:货架。源 LocationShelfMaster,FULL REPLACE,无 is_active,无 FK。';
- -- =====================================================================================
- -- 三、mdp_entity 入站配置(4 条,全 FULL)
- --
- -- · 源一律解析 mdp_source.source_code='AIDOPDEV_MYSQL'(本库样板源),禁止硬编码 source_id。
- -- · 变量解析失败则全部 INSERT 降为空操作(@src_id IS NOT NULL 守卫)。
- -- · mdp_entity 全库 tenant_id 恒为 0(全局配置),不涉及租户解析。
- -- · incr_column 必须为 NULL:留了它会让 BuildSelectSql 生成 "col > @cursor",
- -- 使源侧水位为 NULL 的行(DepartmentMaster 存在这样的历史行)永久不可达。
- -- · biz_key_expr 是【源物理列名】的逗号列表,由 MdpStagingWriter.BuildBizKey 以 '#' 拼接;
- -- 任一字段取不到值就整体回落到 source_row_id —— 货架若写成 PascalCase 就会静默退化成 GUID。
- -- =====================================================================================
- SET @src_id = (SELECT `id` FROM `mdp_source` WHERE `source_code` = 'AIDOPDEV_MYSQL' LIMIT 1);
- INSERT INTO `mdp_entity`
- (`tenant_id`,`source_id`,`entity_code`,`entity_name`,`entity_type`,
- `source_table_name`,`target_table_name`,`sync_mode`,`incr_column`,`batch_size`,
- `biz_key_expr`,`status`,`create_time`,`update_time`)
- SELECT 0, @src_id, 'S0_WORK_CENTER', 'S0 工作中心主数据入站', 'TABLE',
- 'WorkCtrMaster', 'mdp_stg_s0_work_center', 'FULL', NULL, 5000,
- 'Domain,WorkCtr', 1, NOW(), NOW()
- WHERE @src_id IS NOT NULL
- AND NOT EXISTS (SELECT 1 FROM `mdp_entity` WHERE `entity_code` = 'S0_WORK_CENTER');
- INSERT INTO `mdp_entity`
- (`tenant_id`,`source_id`,`entity_code`,`entity_name`,`entity_type`,
- `source_table_name`,`target_table_name`,`sync_mode`,`incr_column`,`batch_size`,
- `biz_key_expr`,`status`,`create_time`,`update_time`)
- SELECT 0, @src_id, 'S0_DEPARTMENT', 'S0 部门主数据入站', 'TABLE',
- 'DepartmentMaster', 'mdp_stg_s0_department', 'FULL', NULL, 5000,
- 'Domain,Department', 1, NOW(), NOW()
- WHERE @src_id IS NOT NULL
- AND NOT EXISTS (SELECT 1 FROM `mdp_entity` WHERE `entity_code` = 'S0_DEPARTMENT');
- INSERT INTO `mdp_entity`
- (`tenant_id`,`source_id`,`entity_code`,`entity_name`,`entity_type`,
- `source_table_name`,`target_table_name`,`sync_mode`,`incr_column`,`batch_size`,
- `biz_key_expr`,`status`,`create_time`,`update_time`)
- SELECT 0, @src_id, 'S0_LOCATION', 'S0 库位主数据入站', 'TABLE',
- 'LocationMaster', 'mdp_stg_s0_location', 'FULL', NULL, 5000,
- 'Domain,location', 1, NOW(), NOW()
- WHERE @src_id IS NOT NULL
- AND NOT EXISTS (SELECT 1 FROM `mdp_entity` WHERE `entity_code` = 'S0_LOCATION');
- INSERT INTO `mdp_entity`
- (`tenant_id`,`source_id`,`entity_code`,`entity_name`,`entity_type`,
- `source_table_name`,`target_table_name`,`sync_mode`,`incr_column`,`batch_size`,
- `biz_key_expr`,`status`,`create_time`,`update_time`)
- SELECT 0, @src_id, 'S0_LOCATION_SHELF', 'S0 货架主数据入站', 'TABLE',
- 'LocationShelfMaster', 'mdp_stg_s0_location', 'FULL', NULL, 5000,
- 'domain_code,location,inv_shelf', 1, NOW(), NOW()
- WHERE @src_id IS NOT NULL
- AND NOT EXISTS (SELECT 1 FROM `mdp_entity` WHERE `entity_code` = 'S0_LOCATION_SHELF');
- -- =====================================================================================
- -- 回滚(全部为本脚本新建对象;执行前请确认无其他消费者)
- --
- -- DROP TABLE IF EXISTS `dim_location_shelf`;
- -- DROP TABLE IF EXISTS `dim_location`;
- -- DROP TABLE IF EXISTS `dim_department`;
- -- DROP TABLE IF EXISTS `dim_work_center`;
- -- DROP TABLE IF EXISTS `mdp_stg_s0_location`;
- -- DROP TABLE IF EXISTS `mdp_stg_s0_department`;
- -- DROP TABLE IF EXISTS `mdp_stg_s0_work_center`;
- -- DELETE FROM `mdp_entity`
- -- WHERE `entity_code` IN ('S0_WORK_CENTER','S0_DEPARTMENT','S0_LOCATION','S0_LOCATION_SHELF');
- -- =====================================================================================
|