-- OnboardX 入职模板聚合重构 -- 适用数据库:MySQL 8.x -- 执行前请备份 OnboardX 数据库。 -- 新逻辑不再读取 ob_form_requirement、ob_material_requirement、ob_contract_scope, -- 三张旧表暂时保留,仅用于迁移和回滚;对应写接口已从业务代码中移除。 -- 执行本脚本后请重启后端服务。 SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0; CREATE TABLE IF NOT EXISTS `ob_onboarding_template` ( `Id` BIGINT NOT NULL COMMENT '主键Id', `TemplateCode` VARCHAR(100) NOT NULL COMMENT '入职模板编码', `TemplateName` VARCHAR(200) NOT NULL COMMENT '入职模板名称', `CompanyId` BIGINT NOT NULL COMMENT '所属公司Id', `Description` VARCHAR(1000) NULL COMMENT '模板说明', `Status` VARCHAR(20) NULL DEFAULT 'enable' COMMENT '状态', `CreateTime` DATETIME NULL COMMENT '创建时间', `UpdateTime` DATETIME NULL COMMENT '更新时间', `CreateUserId` BIGINT NULL COMMENT '创建者Id', `UpdateUserId` BIGINT NULL COMMENT '修改者Id', `CreateUser` VARCHAR(100) NULL COMMENT '创建人', `UpdateUser` VARCHAR(100) NULL COMMENT '更新人', `IsDelete` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '软删除', `ExtJson` LONGTEXT NULL COMMENT '扩展信息', PRIMARY KEY (`Id`), UNIQUE KEY `uk_ob_onboarding_template_code` (`TemplateCode`), KEY `idx_ob_onboarding_template_company` (`CompanyId`, `Status`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='入职模板'; CREATE TABLE IF NOT EXISTS `ob_onboarding_template_position` ( `Id` BIGINT NOT NULL, `OnboardingTemplateId` BIGINT NOT NULL COMMENT '入职模板Id', `PositionId` BIGINT NOT NULL COMMENT '岗位Id', `Status` VARCHAR(20) NULL DEFAULT 'enable', `CreateTime` DATETIME NULL, `UpdateTime` DATETIME NULL, `CreateUserId` BIGINT NULL, `UpdateUserId` BIGINT NULL, `CreateUser` VARCHAR(100) NULL, `UpdateUser` VARCHAR(100) NULL, `IsDelete` TINYINT(1) NOT NULL DEFAULT 0, `ExtJson` LONGTEXT NULL, PRIMARY KEY (`Id`), UNIQUE KEY `uk_ob_template_position` (`OnboardingTemplateId`, `PositionId`), KEY `idx_ob_template_position_position` (`PositionId`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='入职模板适用岗位'; CREATE TABLE IF NOT EXISTS `ob_onboarding_template_contract` ( `Id` BIGINT NOT NULL, `OnboardingTemplateId` BIGINT NOT NULL COMMENT '入职模板Id', `ContractTemplateId` BIGINT NOT NULL COMMENT '合同模板Id', `ContractTemplateVersionId` BIGINT NOT NULL COMMENT '合同模板版本Id', `Required` TINYINT(1) NOT NULL DEFAULT 1 COMMENT '是否必须确认', `PrintCopies` INT NOT NULL DEFAULT 1 COMMENT '打印份数', `SortCode` INT NULL COMMENT '排序码', `Status` VARCHAR(20) NULL DEFAULT 'enable', `CreateTime` DATETIME NULL, `UpdateTime` DATETIME NULL, `CreateUserId` BIGINT NULL, `UpdateUserId` BIGINT NULL, `CreateUser` VARCHAR(100) NULL, `UpdateUser` VARCHAR(100) NULL, `IsDelete` TINYINT(1) NOT NULL DEFAULT 0, `ExtJson` LONGTEXT NULL, PRIMARY KEY (`Id`), UNIQUE KEY `uk_ob_template_contract_version` (`OnboardingTemplateId`, `ContractTemplateVersionId`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='入职模板合同配置'; CREATE TABLE IF NOT EXISTS `ob_onboarding_template_form_field` ( `Id` BIGINT NOT NULL, `OnboardingTemplateId` BIGINT NOT NULL COMMENT '入职模板Id', `FieldId` BIGINT NOT NULL COMMENT '动态字段Id', `Required` TINYINT(1) NOT NULL DEFAULT 1 COMMENT '是否必填', `SortCode` INT NULL COMMENT '排序码', `Status` VARCHAR(20) NULL DEFAULT 'enable', `CreateTime` DATETIME NULL, `UpdateTime` DATETIME NULL, `CreateUserId` BIGINT NULL, `UpdateUserId` BIGINT NULL, `CreateUser` VARCHAR(100) NULL, `UpdateUser` VARCHAR(100) NULL, `IsDelete` TINYINT(1) NOT NULL DEFAULT 0, `ExtJson` LONGTEXT NULL, PRIMARY KEY (`Id`), UNIQUE KEY `uk_ob_template_form_field` (`OnboardingTemplateId`, `FieldId`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='入职模板补充字段'; CREATE TABLE IF NOT EXISTS `ob_onboarding_template_material` ( `Id` BIGINT NOT NULL, `OnboardingTemplateId` BIGINT NOT NULL COMMENT '入职模板Id', `MaterialTypeId` BIGINT NOT NULL COMMENT '材料类型Id', `Required` TINYINT(1) NOT NULL DEFAULT 1 COMMENT '是否必传', `SortCode` INT NULL COMMENT '排序码', `Status` VARCHAR(20) NULL DEFAULT 'enable', `CreateTime` DATETIME NULL, `UpdateTime` DATETIME NULL, `CreateUserId` BIGINT NULL, `UpdateUserId` BIGINT NULL, `CreateUser` VARCHAR(100) NULL, `UpdateUser` VARCHAR(100) NULL, `IsDelete` TINYINT(1) NOT NULL DEFAULT 0, `ExtJson` LONGTEXT NULL, PRIMARY KEY (`Id`), UNIQUE KEY `uk_ob_template_material` (`OnboardingTemplateId`, `MaterialTypeId`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='入职模板上传材料'; CREATE TABLE IF NOT EXISTS `ob_task_form_requirement` ( `Id` BIGINT NOT NULL, `TaskId` BIGINT NOT NULL COMMENT '入职任务Id', `FieldId` BIGINT NOT NULL COMMENT '原动态字段Id', `FieldCode` VARCHAR(100) NOT NULL COMMENT '冻结字段编码', `FieldName` VARCHAR(200) NOT NULL COMMENT '冻结字段名称', `DataType` VARCHAR(50) NOT NULL COMMENT '冻结数据类型', `OptionsJson` JSON NULL COMMENT '冻结选项JSON', `Required` TINYINT(1) NOT NULL DEFAULT 1 COMMENT '是否必填', `SortCode` INT NULL COMMENT '排序码', `Status` VARCHAR(20) NULL DEFAULT 'enable', `CreateTime` DATETIME NULL, `UpdateTime` DATETIME NULL, `CreateUserId` BIGINT NULL, `UpdateUserId` BIGINT NULL, `CreateUser` VARCHAR(100) NULL, `UpdateUser` VARCHAR(100) NULL, `IsDelete` TINYINT(1) NOT NULL DEFAULT 0, `ExtJson` LONGTEXT NULL, PRIMARY KEY (`Id`), UNIQUE KEY `uk_ob_task_form_requirement` (`TaskId`, `FieldId`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='任务补充字段快照'; CREATE TABLE IF NOT EXISTS `ob_task_material_requirement` ( `Id` BIGINT NOT NULL, `TaskId` BIGINT NOT NULL COMMENT '入职任务Id', `MaterialTypeId` BIGINT NOT NULL COMMENT '原材料类型Id', `MaterialCode` VARCHAR(100) NOT NULL COMMENT '冻结材料编码', `MaterialName` VARCHAR(200) NOT NULL COMMENT '冻结材料名称', `AllowedExtensions` VARCHAR(500) NULL COMMENT '冻结允许扩展名', `MaxFileCount` INT NOT NULL DEFAULT 1 COMMENT '冻结最大文件数', `MaxFileSizeMb` INT NULL COMMENT '冻结单文件大小MB', `Required` TINYINT(1) NOT NULL DEFAULT 1 COMMENT '是否必传', `SortCode` INT NULL COMMENT '排序码', `Status` VARCHAR(20) NULL DEFAULT 'enable', `CreateTime` DATETIME NULL, `UpdateTime` DATETIME NULL, `CreateUserId` BIGINT NULL, `UpdateUserId` BIGINT NULL, `CreateUser` VARCHAR(100) NULL, `UpdateUser` VARCHAR(100) NULL, `IsDelete` TINYINT(1) NOT NULL DEFAULT 0, `ExtJson` LONGTEXT NULL, PRIMARY KEY (`Id`), UNIQUE KEY `uk_ob_task_material_requirement` (`TaskId`, `MaterialTypeId`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='任务上传材料快照'; DROP PROCEDURE IF EXISTS `ob_add_column_if_missing`; DELIMITER $$ CREATE PROCEDURE `ob_add_column_if_missing`( IN p_table_name VARCHAR(128), IN p_column_name VARCHAR(128), IN p_column_definition VARCHAR(1000) ) BEGIN IF NOT EXISTS ( SELECT 1 FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = p_table_name AND COLUMN_NAME = p_column_name ) THEN SET @ddl = CONCAT('ALTER TABLE `', p_table_name, '` ADD COLUMN `', p_column_name, '` ', p_column_definition); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; END IF; END$$ DELIMITER ; CALL `ob_add_column_if_missing`( 'ob_onboarding_task', 'OnboardingTemplateId', 'BIGINT NOT NULL DEFAULT 0 COMMENT ''创建任务时锁定的入职模板Id'' AFTER `PositionId`' ); CALL `ob_add_column_if_missing`( 'ob_task_contract', 'PrintCopies', 'INT NOT NULL DEFAULT 1 COMMENT ''合同打印份数'' AFTER `Required`' ); DROP PROCEDURE IF EXISTS `ob_add_column_if_missing`; -- 将旧的公司/岗位合同范围迁移为“一公司一岗位一入职模板”。 -- 公司级合同、字段和材料要求会自动继承到该公司的每个岗位模板。 DROP TEMPORARY TABLE IF EXISTS `tmp_ob_template_map`; CREATE TEMPORARY TABLE `tmp_ob_template_map` ( `CompanyId` BIGINT NOT NULL, `PositionId` BIGINT NOT NULL, `TemplateCode` VARCHAR(100) NOT NULL, `OnboardingTemplateId` BIGINT NULL, PRIMARY KEY (`CompanyId`, `PositionId`) ); INSERT IGNORE INTO `tmp_ob_template_map` (`CompanyId`, `PositionId`, `TemplateCode`) SELECT DISTINCT p.`CompanyId`, p.`Id`, CONCAT('MIGRATED_', p.`CompanyId`, '_', p.`Id`) FROM `ob_position` p INNER JOIN ( SELECT `CompanyId` FROM `ob_contract_scope` WHERE `IsDelete` = 0 UNION SELECT `CompanyId` FROM `ob_form_requirement` WHERE `IsDelete` = 0 UNION SELECT `CompanyId` FROM `ob_material_requirement` WHERE `IsDelete` = 0 ) configured_company ON configured_company.`CompanyId` = p.`CompanyId` WHERE p.`IsDelete` = 0; INSERT IGNORE INTO `ob_onboarding_template` ( `Id`, `TemplateCode`, `TemplateName`, `CompanyId`, `Description`, `Status`, `CreateTime`, `IsDelete` ) SELECT UUID_SHORT(), m.`TemplateCode`, CONCAT(COALESCE(c.`CompanyName`, m.`CompanyId`), '-', COALESCE(p.`PositionName`, m.`PositionId`), '-入职模板'), m.`CompanyId`, '由旧公司岗位配置自动迁移,请管理员检查后使用', 'enable', NOW(), 0 FROM `tmp_ob_template_map` m LEFT JOIN `ob_company` c ON c.`Id` = m.`CompanyId` LEFT JOIN `ob_position` p ON p.`Id` = m.`PositionId`; UPDATE `tmp_ob_template_map` m INNER JOIN `ob_onboarding_template` t ON t.`TemplateCode` = m.`TemplateCode` SET m.`OnboardingTemplateId` = t.`Id`; INSERT IGNORE INTO `ob_onboarding_template_position` ( `Id`, `OnboardingTemplateId`, `PositionId`, `Status`, `CreateTime`, `IsDelete` ) SELECT UUID_SHORT(), m.`OnboardingTemplateId`, m.`PositionId`, 'enable', NOW(), 0 FROM `tmp_ob_template_map` m; INSERT IGNORE INTO `ob_onboarding_template_contract` ( `Id`, `OnboardingTemplateId`, `ContractTemplateId`, `ContractTemplateVersionId`, `Required`, `PrintCopies`, `SortCode`, `Status`, `CreateTime`, `IsDelete` ) SELECT UUID_SHORT(), m.`OnboardingTemplateId`, MAX(s.`TemplateId`), s.`TemplateVersionId`, COALESCE( MAX(CASE WHEN s.`PositionId` = m.`PositionId` THEN s.`Required` END), MAX(s.`Required`) ), 1, COALESCE( MAX(CASE WHEN s.`PositionId` = m.`PositionId` THEN s.`SortCode` END), MAX(s.`SortCode`) ), 'enable', NOW(), 0 FROM `tmp_ob_template_map` m INNER JOIN `ob_contract_scope` s ON s.`CompanyId` = m.`CompanyId` AND (s.`PositionId` IS NULL OR s.`PositionId` = m.`PositionId`) AND s.`IsDelete` = 0 GROUP BY m.`OnboardingTemplateId`, s.`TemplateVersionId`; INSERT IGNORE INTO `ob_onboarding_template_form_field` ( `Id`, `OnboardingTemplateId`, `FieldId`, `Required`, `SortCode`, `Status`, `CreateTime`, `IsDelete` ) SELECT UUID_SHORT(), m.`OnboardingTemplateId`, r.`FieldId`, COALESCE( MAX(CASE WHEN r.`PositionId` = m.`PositionId` THEN r.`Required` END), MAX(r.`Required`) ), COALESCE( MAX(CASE WHEN r.`PositionId` = m.`PositionId` THEN r.`SortCode` END), MAX(r.`SortCode`) ), 'enable', NOW(), 0 FROM `tmp_ob_template_map` m INNER JOIN `ob_form_requirement` r ON r.`CompanyId` = m.`CompanyId` AND (r.`PositionId` IS NULL OR r.`PositionId` = m.`PositionId`) AND r.`IsDelete` = 0 GROUP BY m.`OnboardingTemplateId`, r.`FieldId`; INSERT IGNORE INTO `ob_onboarding_template_material` ( `Id`, `OnboardingTemplateId`, `MaterialTypeId`, `Required`, `SortCode`, `Status`, `CreateTime`, `IsDelete` ) SELECT UUID_SHORT(), m.`OnboardingTemplateId`, r.`MaterialTypeId`, COALESCE( MAX(CASE WHEN r.`PositionId` = m.`PositionId` THEN r.`Required` END), MAX(r.`Required`) ), COALESCE( MAX(CASE WHEN r.`PositionId` = m.`PositionId` THEN r.`SortCode` END), MAX(r.`SortCode`) ), 'enable', NOW(), 0 FROM `tmp_ob_template_map` m INNER JOIN `ob_material_requirement` r ON r.`CompanyId` = m.`CompanyId` AND (r.`PositionId` IS NULL OR r.`PositionId` = m.`PositionId`) AND r.`IsDelete` = 0 GROUP BY m.`OnboardingTemplateId`, r.`MaterialTypeId`; -- 为迁移前已创建的入职任务回填入职模板,并冻结字段/材料要求快照。 UPDATE `ob_onboarding_task` task INNER JOIN `tmp_ob_template_map` m ON m.`CompanyId` = task.`CompanyId` AND m.`PositionId` = task.`PositionId` SET task.`OnboardingTemplateId` = m.`OnboardingTemplateId` WHERE task.`OnboardingTemplateId` = 0; INSERT IGNORE INTO `ob_task_form_requirement` ( `Id`, `TaskId`, `FieldId`, `FieldCode`, `FieldName`, `DataType`, `OptionsJson`, `Required`, `SortCode`, `Status`, `CreateTime`, `IsDelete` ) SELECT UUID_SHORT(), task.`Id`, config.`FieldId`, field.`FieldCode`, field.`FieldName`, field.`DataType`, field.`OptionsJson`, config.`Required`, config.`SortCode`, 'enable', NOW(), 0 FROM `ob_onboarding_task` task INNER JOIN `ob_onboarding_template_form_field` config ON config.`OnboardingTemplateId` = task.`OnboardingTemplateId` INNER JOIN `ob_form_field` field ON field.`Id` = config.`FieldId` AND field.`IsDelete` = 0 WHERE task.`OnboardingTemplateId` <> 0; INSERT IGNORE INTO `ob_task_material_requirement` ( `Id`, `TaskId`, `MaterialTypeId`, `MaterialCode`, `MaterialName`, `AllowedExtensions`, `MaxFileCount`, `MaxFileSizeMb`, `Required`, `SortCode`, `Status`, `CreateTime`, `IsDelete` ) SELECT UUID_SHORT(), task.`Id`, config.`MaterialTypeId`, material.`MaterialCode`, material.`MaterialName`, material.`AllowedExtensions`, material.`MaxFileCount`, material.`MaxFileSizeMb`, config.`Required`, config.`SortCode`, 'enable', NOW(), 0 FROM `ob_onboarding_task` task INNER JOIN `ob_onboarding_template_material` config ON config.`OnboardingTemplateId` = task.`OnboardingTemplateId` INNER JOIN `ob_material_type` material ON material.`Id` = config.`MaterialTypeId` AND material.`IsDelete` = 0 WHERE task.`OnboardingTemplateId` <> 0; DROP TEMPORARY TABLE IF EXISTS `tmp_ob_template_map`; SET FOREIGN_KEY_CHECKS = 1; -- 执行后检查: -- SELECT * FROM ob_onboarding_template; -- SELECT * FROM ob_onboarding_template_position; -- SELECT * FROM ob_onboarding_template_contract; -- SELECT * FROM ob_onboarding_template_form_field; -- SELECT * FROM ob_onboarding_template_material;