-- ============================================================ -- 测试环境 crm 库 schema 补齐(opportunity / opportunity_customer) -- 背景 20260901:导表后两表为残缺混合结构(缺 party_a_clear 等列, -- 实体映射全列 SELECT/INSERT → SQLSyntaxErrorException → 50001)。 -- 用法:Navicat 整段执行 / mysql -uroot -p crm < opp-schema-migration.sql -- 幂等:列已存在自动跳过(information_schema 判存 + 动态 SQL,MySQL 8 无 ADD COLUMN IF NOT EXISTS)。 -- 对齐实体:Opportunity.java(L40-230)+ OpportunityCustomer.java + BaseEntity.java。 -- 更省事的替代:重启 crm-app 容器即可,jpa.hibernate.ddl-auto=update 会自动补齐全部缺列。 -- ============================================================ DELIMITER $$ DROP PROCEDURE IF EXISTS crm_add_column $$ CREATE PROCEDURE crm_add_column( IN p_table VARCHAR(64), IN p_column VARCHAR(64), IN p_ddl TEXT) BEGIN IF NOT EXISTS ( SELECT 1 FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = p_table AND COLUMN_NAME = p_column ) THEN SET @s = CONCAT('ALTER TABLE `', p_table, '` ADD COLUMN `', p_column, '` ', p_ddl); PREPARE st FROM @s; EXECUTE st; DEALLOCATE PREPARE st; SELECT CONCAT('ADD ', p_table, '.', p_column) AS applied; END IF; END $$ DELIMITER ; -- ---------- opportunity 主表 ---------- CALL crm_add_column('opportunity','opp_name', "varchar(200) NOT NULL COMMENT '商机名称'"); CALL crm_add_column('opportunity','opp_source', "varchar(64) NOT NULL COMMENT '商机来源字典code'"); CALL crm_add_column('opportunity','opp_type', "varchar(64) NOT NULL COMMENT '商机类型字典code'"); CALL crm_add_column('opportunity','industry_code', "varchar(64) NOT NULL COMMENT '行业字典code'"); CALL crm_add_column('opportunity','bid_form', "varchar(64) NOT NULL COMMENT '招标形式字典code'"); CALL crm_add_column('opportunity','locality_type', "varchar(64) NOT NULL COMMENT '项目属地字典code'"); CALL crm_add_column('opportunity','party_a_clear', "tinyint NOT NULL DEFAULT 0 COMMENT '甲方是否明确'"); CALL crm_add_column('opportunity','party_a', "varchar(200) NULL COMMENT '甲方名称'"); CALL crm_add_column('opportunity','province_code', "varchar(12) NOT NULL COMMENT '省份国标code'"); CALL crm_add_column('opportunity','city_code', "varchar(12) NOT NULL COMMENT '市/区国标code'"); CALL crm_add_column('opportunity','remark', "text NULL COMMENT '备注'"); CALL crm_add_column('opportunity','project_amount', "decimal(14,2) NULL COMMENT '项目金额'"); CALL crm_add_column('opportunity','address', "varchar(255) NULL COMMENT '项目详细地址'"); CALL crm_add_column('opportunity','owner_user_id', "bigint NULL COMMENT '领取人ID'"); CALL crm_add_column('opportunity','owner_name_snapshot', "varchar(64) NULL COMMENT '领取人姓名快照'"); CALL crm_add_column('opportunity','owner_dept_id', "bigint NULL COMMENT '领取人部门快照'"); CALL crm_add_column('opportunity','origin_dept_id', "bigint NULL COMMENT '原始血缘部门'"); CALL crm_add_column('opportunity','pool_reason', "varchar(32) NULL COMMENT '进入公海原因'"); CALL crm_add_column('opportunity','creator_user_id', "bigint NOT NULL COMMENT '创建人ID'"); CALL crm_add_column('opportunity','opp_status', "tinyint NOT NULL COMMENT '商机状态'"); CALL crm_add_column('opportunity','current_stage_id', "bigint NOT NULL COMMENT '当前阶段节点ID'"); CALL crm_add_column('opportunity','stage_template_id', "bigint NOT NULL COMMENT '阶段模板版本行ID'"); CALL crm_add_column('opportunity','stage_template_version', "varchar(20) NOT NULL COMMENT '阶段模板版本号冗余'"); CALL crm_add_column('opportunity','source_lead_id', "bigint NULL COMMENT '来源线索ID'"); CALL crm_add_column('opportunity','source_lead_name', "varchar(200) NULL COMMENT '线索名快照'"); CALL crm_add_column('opportunity','source_phone', "varchar(32) NULL COMMENT '联系电话快照'"); CALL crm_add_column('opportunity','source_product_code', "varchar(64) NULL COMMENT '需求产品码快照'"); CALL crm_add_column('opportunity','claim_time', "datetime NULL COMMENT '领取时刻'"); CALL crm_add_column('opportunity','last_valid_follow_time', "datetime NULL COMMENT '最近有效跟进时间'"); CALL crm_add_column('opportunity','primary_customer_id', "bigint NULL COMMENT '主要意向客户ID'"); CALL crm_add_column('opportunity','primary_customer_name_snapshot', "varchar(200) NULL COMMENT '主客户名快照'"); CALL crm_add_column('opportunity','key_contact_id', "bigint NULL COMMENT '商机关键联系人ID'"); CALL crm_add_column('opportunity','key_contact_name_snapshot', "varchar(200) NULL COMMENT '商机关键联系人姓名快照'"); CALL crm_add_column('opportunity','key_contact_company_snapshot', "varchar(200) NULL COMMENT '关键人所在公司快照'"); CALL crm_add_column('opportunity','scheme_budget', "decimal(18,2) NULL COMMENT '方案卡预算快照'"); CALL crm_add_column('opportunity','scheme_card_status', "int NULL COMMENT '方案卡状态快照'"); CALL crm_add_column('opportunity','pause_time', "datetime NULL COMMENT '暂缓开始时刻'"); CALL crm_add_column('opportunity','pause_reason', "varchar(32) NULL COMMENT '暂缓原因'"); CALL crm_add_column('opportunity','pause_expected_restart_date', "date NULL COMMENT '预计重启日期'"); CALL crm_add_column('opportunity','pause_remark', "varchar(500) NULL COMMENT '暂缓补充说明'"); CALL crm_add_column('opportunity','version', "int NOT NULL DEFAULT 0 COMMENT '乐观锁版本号'"); -- BaseEntity 公共列(通常都在,幂等兜底) CALL crm_add_column('opportunity','creator_id', "varchar(50) NULL COMMENT '创建用户id'"); CALL crm_add_column('opportunity','updater_id', "varchar(50) NULL COMMENT '更新用户id'"); CALL crm_add_column('opportunity','create_time', "datetime NULL COMMENT '创建时间'"); CALL crm_add_column('opportunity','update_time', "datetime NULL COMMENT '更新时间'"); CALL crm_add_column('opportunity','delete_key', "tinyint NOT NULL DEFAULT 0 COMMENT '逻辑删除位'"); -- ---------- opportunity_customer 关联客户表 ---------- CALL crm_add_column('opportunity_customer','opportunity_id', "bigint NOT NULL COMMENT '所属商机ID'"); CALL crm_add_column('opportunity_customer','customer_id', "bigint NOT NULL COMMENT '客户ID'"); CALL crm_add_column('opportunity_customer','customer_name_snapshot', "varchar(200) NOT NULL COMMENT '客户名快照'"); CALL crm_add_column('opportunity_customer','customer_role', "varchar(32) NULL COMMENT '客户角色字典code'"); CALL crm_add_column('opportunity_customer','is_primary_intended', "tinyint NOT NULL DEFAULT 0 COMMENT '是否主要意向客户'"); CALL crm_add_column('opportunity_customer','delete_key', "bigint NOT NULL DEFAULT 0 COMMENT '软删除复用键'"); CALL crm_add_column('opportunity_customer','creator_id', "varchar(50) NULL COMMENT '创建用户id'"); CALL crm_add_column('opportunity_customer','updater_id', "varchar(50) NULL COMMENT '更新用户id'"); CALL crm_add_column('opportunity_customer','create_time', "datetime NULL COMMENT '创建时间'"); CALL crm_add_column('opportunity_customer','update_time', "datetime NULL COMMENT '更新时间'"); -- ---------- 索引/约束补齐(不阻塞运行,建议顺手执行) ---------- SET @ix := (SELECT COUNT(*) FROM information_schema.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'opportunity' AND INDEX_NAME = 'idx_source_lead'); SET @sql := IF(@ix = 0, 'ALTER TABLE `opportunity` ADD UNIQUE INDEX `idx_source_lead` (`source_lead_id`)', 'SELECT 1'); PREPARE st FROM @sql; EXECUTE st; DEALLOCATE PREPARE st; SET @ix := (SELECT COUNT(*) FROM information_schema.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'opportunity' AND INDEX_NAME = 'idx_owner_status'); SET @sql := IF(@ix = 0, 'ALTER TABLE `opportunity` ADD INDEX `idx_owner_status` (`owner_user_id`, `opp_status`)', 'SELECT 1'); PREPARE st FROM @sql; EXECUTE st; DEALLOCATE PREPARE st; SET @ix := (SELECT COUNT(*) FROM information_schema.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'opportunity' AND INDEX_NAME = 'idx_owner_dept_status'); SET @sql := IF(@ix = 0, 'ALTER TABLE `opportunity` ADD INDEX `idx_owner_dept_status` (`owner_dept_id`, `opp_status`)', 'SELECT 1'); PREPARE st FROM @sql; EXECUTE st; DEALLOCATE PREPARE st; DROP PROCEDURE crm_add_column; -- 执行完核对(应全部返回 0 行 = 缺列已补齐): -- SELECT TABLE_NAME, COLUMN_NAME FROM information_schema.COLUMNS -- WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME IN ('opportunity','opportunity_customer') -- AND COLUMN_NAME IN ('party_a_clear','project_amount','address','key_contact_id', -- 'key_contact_name_snapshot','key_contact_company_snapshot', -- 'customer_role','is_primary_intended');