|
|
@@ -1,5 +1,6 @@
|
|
|
package com.etotem.cfc.config;
|
|
|
|
|
|
+<<<<<<< HEAD
|
|
|
import com.etotem.cfc.mapper.*;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
import javax.annotation.Resource;
|
|
|
@@ -5005,4 +5006,2231 @@ try {
|
|
|
ensureColumn("family_invitations", "max_use_count", "INT DEFAULT 1 COMMENT '最大使用次数'");
|
|
|
ensureColumn("family_invitations", "used_count", "INT DEFAULT 0 COMMENT '已使用次数'");
|
|
|
}
|
|
|
+=======
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS user_address (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
+ "user_id BIGINT NOT NULL COMMENT '用户ID', " +
|
|
|
+ "receiver_name VARCHAR(50) NOT NULL COMMENT '收货人姓名', " +
|
|
|
+ "phone VARCHAR(20) NOT NULL COMMENT '收货人电话', " +
|
|
|
+ "province VARCHAR(50) COMMENT '省份', " +
|
|
|
+ "city VARCHAR(50) COMMENT '城市', " +
|
|
|
+ "district VARCHAR(50) COMMENT '区县', " +
|
|
|
+ "street VARCHAR(200) COMMENT '详细地址', " +
|
|
|
+ "is_default INT DEFAULT 0 COMMENT '是否默认 1=是 0=否', " +
|
|
|
+ "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
+ "updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, " +
|
|
|
+ "INDEX idx_user_id (user_id)" +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='收货地址表'");
|
|
|
+ log.info("已创建 user_address 表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建 user_address 表失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== 会员价格体系 ====================
|
|
|
+
|
|
|
+ // 迁移: activities表添加member_price列
|
|
|
+ try {
|
|
|
+ Integer exists = jdbcTemplate.queryForObject(
|
|
|
+ "SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'activities' AND COLUMN_NAME = 'member_price'",
|
|
|
+ Integer.class);
|
|
|
+ if (exists == null || exists == 0) {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE activities ADD COLUMN member_price INT COMMENT '会员价(分)' AFTER price");
|
|
|
+ log.info("已添加member_price列到activities表");
|
|
|
+ } else {
|
|
|
+ log.info("member_price列已存在,跳过");
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("检查/添加activities.member_price失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 迁移: 创建 member_discount_configs 表(分类级别折扣配置)
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS member_discount_configs (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
+ "level_code VARCHAR(20) NOT NULL COMMENT '会员等级: FAMILY/PROVIDER', " +
|
|
|
+ "target_type VARCHAR(20) NOT NULL COMMENT '目标类型: product/activity', " +
|
|
|
+ "target_id BIGINT DEFAULT NULL COMMENT '目标ID(NULL表示分类级别)', " +
|
|
|
+ "category_id BIGINT DEFAULT NULL COMMENT '类目ID(分类级别折扣)', " +
|
|
|
+ "discount_percent INT NOT NULL COMMENT '折扣百分比(如90表示90%)', " +
|
|
|
+ "enabled TINYINT DEFAULT 1 COMMENT '是否启用', " +
|
|
|
+ "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
+ "updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, " +
|
|
|
+ "INDEX idx_level (level_code), " +
|
|
|
+ "INDEX idx_target (target_type, target_id), " +
|
|
|
+ "INDEX idx_category (target_type, category_id)" +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='会员折扣配置'");
|
|
|
+ log.info("已创建member_discount_configs表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建member_discount_configs表失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ Integer hasCap = jdbcTemplate.queryForObject(
|
|
|
+ "SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'relationship_types' AND COLUMN_NAME = 'capability_role'",
|
|
|
+ Integer.class);
|
|
|
+ Integer hasDefault = jdbcTemplate.queryForObject(
|
|
|
+ "SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'relationship_types' AND COLUMN_NAME = 'default_role'",
|
|
|
+ Integer.class);
|
|
|
+ if (hasCap != null && hasCap == 0) {
|
|
|
+ if (hasDefault != null && hasDefault > 0) {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE relationship_types CHANGE COLUMN default_role capability_role VARCHAR(20) NOT NULL COMMENT '能力角色: parent/child'");
|
|
|
+ log.info("已将relationship_types.default_role重命名为capability_role");
|
|
|
+ } else {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE relationship_types ADD COLUMN capability_role VARCHAR(20) NOT NULL DEFAULT 'parent' COMMENT '能力角色: parent/child' AFTER type_name");
|
|
|
+ log.info("已添加capability_role列到relationship_types表");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("relationship_types capability_role 迁移失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 确保capability_role有默认值(生产库该列NOT NULL无默认值,导致INSERT时Field doesn't have a default value错误)
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE relationship_types MODIFY COLUMN capability_role VARCHAR(20) NOT NULL DEFAULT 'parent' COMMENT '能力角色: parent/child'");
|
|
|
+ log.info("已修改relationship_types.capability_role添加默认值'parent'");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("修改relationship_types.capability_role默认值失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== 食材推荐指数 Phase: food_recommend_idx / food_recommend_idx_log ====================
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS `food_recommend_idx` (" +
|
|
|
+ "`id` BIGINT NOT NULL AUTO_INCREMENT, " +
|
|
|
+ "`user_id` BIGINT NOT NULL COMMENT '用户ID', " +
|
|
|
+ "`food_id` BIGINT NOT NULL COMMENT '食材ID', " +
|
|
|
+ "`food_name` VARCHAR(100) COMMENT '食材名称', " +
|
|
|
+ "`index_score` INT NOT NULL DEFAULT 0 COMMENT '推荐指数(0-100, 来自菌群检测报告)', " +
|
|
|
+ "`health_report_id` BIGINT COMMENT '关联健康报告ID', " +
|
|
|
+ "`calculated_at` DATETIME COMMENT '计算时间', " +
|
|
|
+ "`created_at` DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
+ "PRIMARY KEY (`id`), " +
|
|
|
+ "INDEX `idx_fri_user` (`user_id`), " +
|
|
|
+ "INDEX `idx_fri_food` (`food_id`), " +
|
|
|
+ "UNIQUE KEY `uk_fri_user_food` (`user_id`, `food_id`) " +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='食材推荐指数当前值'");
|
|
|
+ log.info("已创建/确认 food_recommend_idx 表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建 food_recommend_idx 表失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS `food_recommend_idx_log` (" +
|
|
|
+ "`id` BIGINT NOT NULL AUTO_INCREMENT, " +
|
|
|
+ "`user_id` BIGINT NOT NULL COMMENT '用户ID', " +
|
|
|
+ "`food_id` BIGINT NOT NULL COMMENT '食材ID', " +
|
|
|
+ "`food_name` VARCHAR(100) COMMENT '食材名称', " +
|
|
|
+ "`previous_index` INT COMMENT '变更前推荐指数', " +
|
|
|
+ "`new_index` INT COMMENT '变更后推荐指数', " +
|
|
|
+ "`health_report_id` BIGINT COMMENT '触发的健康报告ID', " +
|
|
|
+ "`change_reason` VARCHAR(200) COMMENT '变更原因', " +
|
|
|
+ "`changed_at` DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
+ "PRIMARY KEY (`id`), " +
|
|
|
+ "INDEX `idx_fril_user` (`user_id`), " +
|
|
|
+ "INDEX `idx_fril_food` (`food_id`) " +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='食材推荐指数变更历史日志'");
|
|
|
+ log.info("已创建/确认 food_recommend_idx_log 表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建 food_recommend_idx_log 表失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 迁移25: activities表添加审核相关字段(活动工作流:待审核/驳回/发布)
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE activities ADD COLUMN audit_status VARCHAR(20) DEFAULT NULL COMMENT '审核状态:approved/pending/rejected'");
|
|
|
+ log.info("已添加audit_status列到activities表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 列已存在,忽略错误
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE activities ADD COLUMN audit_reason VARCHAR(500) DEFAULT NULL COMMENT '驳回原因'");
|
|
|
+ log.info("已添加audit_reason列到activities表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 列已存在,忽略错误
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE activities ADD COLUMN auditor_id BIGINT DEFAULT NULL COMMENT '审核人ID'");
|
|
|
+ log.info("已添加auditor_id列到activities表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 列已存在,忽略错误
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE activities ADD COLUMN audited_at DATETIME DEFAULT NULL COMMENT '审核时间'");
|
|
|
+ log.info("已添加audited_at列到activities表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 列已存在,忽略错误
|
|
|
+ }
|
|
|
+
|
|
|
+ log.info("数据库迁移完成");
|
|
|
+
|
|
|
+ // ==================== 健康维度 Phase 1: health_dimension_score / health_data_source_record / health_norm_reference ====================
|
|
|
+ migrateHealthDimensionsTable();
|
|
|
+ seedHealthNorms();
|
|
|
+ }
|
|
|
+
|
|
|
+ private void migrateHealthDimensionsTable() {
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("RENAME TABLE IF EXISTS `health_dimension_score` TO `health_dimension_score_old`");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("health_dimension_score rename skipped: {}", e.getMessage());
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS `health_dimension_scores` (\n" +
|
|
|
+ " `id` bigint NOT NULL AUTO_INCREMENT,\n" +
|
|
|
+ " `member_id` bigint NOT NULL COMMENT '家庭成员ID',\n" +
|
|
|
+ " `dimension` varchar(20) NOT NULL COMMENT '维度: growth/sleep/vision/immunity/nutrition/gut/exercise',\n" +
|
|
|
+ " `score` int NOT NULL COMMENT '0-100分',\n" +
|
|
|
+ " `percentile` int DEFAULT NULL COMMENT '人群百分位 0-100',\n" +
|
|
|
+ " `data_source` varchar(20) NOT NULL COMMENT '数据源: REPORT/PHOTO/VOICE/MANUAL',\n" +
|
|
|
+ " `tier` tinyint NOT NULL DEFAULT 3 COMMENT '数据层级: 1自动/2语音/3手动',\n" +
|
|
|
+ " `raw_data` json DEFAULT NULL COMMENT '原始数据快照',\n" +
|
|
|
+ " `assess_date` date NOT NULL COMMENT '评估日期',\n" +
|
|
|
+ " `expire_date` date DEFAULT NULL COMMENT '数据过期日',\n" +
|
|
|
+ " `created_at` datetime DEFAULT CURRENT_TIMESTAMP,\n" +
|
|
|
+ " `updated_at` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n" +
|
|
|
+ " PRIMARY KEY (`id`),\n" +
|
|
|
+ " UNIQUE KEY `uk_member_dim_date` (`member_id`, `dimension`, `assess_date`)\n" +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='健康维度评分表'");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("health_dimension_scores 表创建失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("RENAME TABLE IF EXISTS `health_data_source_record` TO `health_data_source_record_old`");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("health_data_source_record rename skipped: {}", e.getMessage());
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS `health_data_sources` (\n" +
|
|
|
+ " `id` bigint NOT NULL AUTO_INCREMENT,\n" +
|
|
|
+ " `member_id` bigint NOT NULL,\n" +
|
|
|
+ " `source_type` varchar(20) NOT NULL COMMENT 'PDF/PHOTO/VOICE/MANUAL/WEIXIN_STEP',\n" +
|
|
|
+ " `dimension_codes` json NOT NULL COMMENT '影响的维度列表',\n" +
|
|
|
+ " `parsed_result` json DEFAULT NULL COMMENT '解析结果全量',\n" +
|
|
|
+ " `file_url` varchar(500) DEFAULT NULL COMMENT '原始文件URL',\n" +
|
|
|
+ " `confidence` decimal(3,2) DEFAULT NULL COMMENT 'AI解析置信度',\n" +
|
|
|
+ " `created_at` datetime DEFAULT CURRENT_TIMESTAMP,\n" +
|
|
|
+ " PRIMARY KEY (`id`),\n" +
|
|
|
+ " KEY `idx_member_source` (`member_id`, `source_type`)\n" +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='健康数据源记录表'");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("health_data_sources 表创建失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("RENAME TABLE IF EXISTS `health_norm_reference` TO `health_norm_reference_old`");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("health_norm_reference rename skipped: {}", e.getMessage());
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS `health_norm_reference` (\n" +
|
|
|
+ " `id` bigint NOT NULL AUTO_INCREMENT,\n" +
|
|
|
+ " `dimension` varchar(20) NOT NULL COMMENT '维度',\n" +
|
|
|
+ " `gender` varchar(10) NOT NULL COMMENT 'male/female/all',\n" +
|
|
|
+ " `age_min` int NOT NULL COMMENT '年龄下限(月)',\n" +
|
|
|
+ " `age_max` int NOT NULL COMMENT '年龄上限(月)',\n" +
|
|
|
+ " `percentile_5` int DEFAULT NULL,\n" +
|
|
|
+ " `percentile_15` int DEFAULT NULL,\n" +
|
|
|
+ " `percentile_25` int DEFAULT NULL,\n" +
|
|
|
+ " `percentile_50` int DEFAULT NULL COMMENT '中位值',\n" +
|
|
|
+ " `percentile_75` int DEFAULT NULL,\n" +
|
|
|
+ " `percentile_85` int DEFAULT NULL,\n" +
|
|
|
+ " `percentile_95` int DEFAULT NULL,\n" +
|
|
|
+ " `source` varchar(50) DEFAULT NULL COMMENT '常模来源',\n" +
|
|
|
+ " `created_at` datetime DEFAULT CURRENT_TIMESTAMP,\n" +
|
|
|
+ " PRIMARY KEY (`id`),\n" +
|
|
|
+ " UNIQUE KEY `uk_dim_gender_age` (`dimension`, `gender`, `age_min`, `age_max`)\n" +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='健康常模对照表'");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("health_norm_reference 表重建失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("INSERT IGNORE INTO health_dimension_scores (member_id, dimension, score, data_source, tier, assess_date, created_at)\n" +
|
|
|
+ " SELECT member_id,\n" +
|
|
|
+ " CASE dimension\n" +
|
|
|
+ " WHEN 'body' THEN 'growth'\n" +
|
|
|
+ " WHEN 'fitness' THEN 'exercise'\n" +
|
|
|
+ " WHEN 'cardio' THEN 'exercise'\n" +
|
|
|
+ " WHEN 'blood' THEN 'immunity'\n" +
|
|
|
+ " WHEN 'sleep' THEN 'sleep'\n" +
|
|
|
+ " WHEN 'psyche' THEN 'growth'\n" +
|
|
|
+ " WHEN 'diet' THEN 'nutrition'\n" +
|
|
|
+ " ELSE dimension\n" +
|
|
|
+ " END,\n" +
|
|
|
+ " score,\n" +
|
|
|
+ " UPPER(data_source),\n" +
|
|
|
+ " 3,\n" +
|
|
|
+ " record_date,\n" +
|
|
|
+ " NOW()\n" +
|
|
|
+ " FROM health_dimension_score_old WHERE is_latest = 1");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("旧维度评分数据迁移跳过: {}", e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void seedHealthNorms() {
|
|
|
+ try {
|
|
|
+ int count = jdbcTemplate.queryForObject("SELECT COUNT(*) FROM health_norm_reference", Integer.class);
|
|
|
+ if (count == 0) {
|
|
|
+ jdbcTemplate.execute("INSERT INTO health_norm_reference (dimension, gender, age_min, age_max, percentile_5, percentile_15, percentile_25, percentile_50, percentile_75, percentile_85, percentile_95, source) VALUES\n" +
|
|
|
+ "('growth', 'male', 72, 84, 25, 35, 45, 55, 70, 80, 90, 'BMI-for-age→百分制'),\n" +
|
|
|
+ "('growth', 'female', 72, 84, 25, 35, 45, 55, 70, 80, 90, 'BMI-for-age→百分制'),\n" +
|
|
|
+ "('growth', 'male', 84, 96, 25, 35, 45, 55, 70, 80, 90, 'BMI-for-age→百分制'),\n" +
|
|
|
+ "('growth', 'female', 84, 96, 25, 35, 45, 55, 70, 80, 90, 'BMI-for-age→百分制'),\n" +
|
|
|
+ "('growth', 'male', 96, 120, 25, 35, 45, 55, 70, 80, 90, 'BMI-for-age→百分制'),\n" +
|
|
|
+ "('growth', 'female', 96, 120, 25, 35, 45, 55, 70, 80, 90, 'BMI-for-age→百分制'),\n" +
|
|
|
+ "('sleep', 'all', 0, 12, 40, 50, 60, 70, 80, 88, 95, 'NSF婴幼儿睡眠→百分制'),\n" +
|
|
|
+ "('sleep', 'all', 12, 36, 40, 50, 60, 70, 80, 88, 95, 'NSF幼儿睡眠→百分制'),\n" +
|
|
|
+ "('sleep', 'all', 36, 72, 40, 50, 60, 70, 80, 88, 95, 'NSF学龄前睡眠→百分制'),\n" +
|
|
|
+ "('sleep', 'all', 72, 156, 40, 50, 60, 70, 80, 88, 95, 'NSF学龄儿童睡眠→百分制'),\n" +
|
|
|
+ "('vision', 'male', 72, 144, 45, 55, 65, 75, 85, 90, 95, '视力发育标准→百分制'),\n" +
|
|
|
+ "('vision', 'female', 72, 144, 45, 55, 65, 75, 85, 90, 95, '视力发育标准→百分制'),\n" +
|
|
|
+ "('immunity', 'all', 0, 72, 15, 30, 45, 65, 80, 90, 95, '年患病次数→百分制'),\n" +
|
|
|
+ "('immunity', 'all', 72, 192, 15, 35, 50, 70, 85, 92, 98, '年患病次数→百分制'),\n" +
|
|
|
+ "('nutrition', 'all', 36, 192, 20, 30, 40, 50, 60, 70, 80, '膳食多样性→百分制'),\n" +
|
|
|
+ "('gut', 'all', 0, 192, 40, 50, 60, 70, 80, 85, 95, '菌群人群分布(平台自建)'),\n" +
|
|
|
+ "('exercise', 'male', 72, 144, 30, 40, 50, 60, 70, 80, 90, '国家学生体质健康标准'),\n" +
|
|
|
+ "('exercise', 'female', 72, 144, 25, 35, 45, 55, 65, 75, 85, '国家学生体质健康标准')");
|
|
|
+ log.info("health_norm_reference 常模种子数据已加载 (18条, 统一0-100百分制)");
|
|
|
+ } else {
|
|
|
+ log.info("health_norm_reference 常模数据已存在 ({}条), 跳过", count);
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("health_norm_reference 常模种子数据初始化失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 迁移: articles表添加审核和作者类型字段
|
|
|
+ try {
|
|
|
+ Integer exists = jdbcTemplate.queryForObject(
|
|
|
+ "SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'articles' AND COLUMN_NAME = 'audit_status'",
|
|
|
+ Integer.class);
|
|
|
+ if (exists == null || exists == 0) {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE articles " +
|
|
|
+ "ADD COLUMN author_type VARCHAR(20) DEFAULT 'admin' COMMENT '作者类型:admin/parent/child' AFTER created_by, " +
|
|
|
+ "ADD COLUMN audit_status VARCHAR(20) DEFAULT 'approved' COMMENT '审核状态:approved/pending/rejected' AFTER status, " +
|
|
|
+ "ADD COLUMN audit_reason TEXT COMMENT '驳回原因' AFTER audit_status, " +
|
|
|
+ "ADD COLUMN auditor_id BIGINT DEFAULT 0 COMMENT '审核人 ID' AFTER audit_reason, " +
|
|
|
+ "ADD COLUMN audited_at DATETIME COMMENT '审核时间' AFTER auditor_id");
|
|
|
+ log.info("已添加审核和作者类型字段到articles表");
|
|
|
+ } else {
|
|
|
+ log.info("articles表审核字段已存在,跳过");
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("检查/添加articles表审核字段失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 迁移20240709-01: content字段扩为TEXT - 用户输入内容可能超VARCHAR上限
|
|
|
+ // activities.description
|
|
|
+ modifyColumnToText("activities", "description", "活动描述");
|
|
|
+ // tasks.description
|
|
|
+ modifyColumnToText("tasks", "description", "任务描述");
|
|
|
+ // wishes.description, reject_reason, cancel_reason
|
|
|
+ modifyColumnToText("wishes", "description", "心愿描述");
|
|
|
+ modifyColumnToText("wishes", "reject_reason", "拒绝原因");
|
|
|
+ modifyColumnToText("wishes", "cancel_reason", "取消原因");
|
|
|
+ // reward_wishlists.description
|
|
|
+ modifyColumnToText("reward_wishlists", "description", "心愿描述");
|
|
|
+ // products.description, reject_reason
|
|
|
+ modifyColumnToText("products", "description", "商品描述");
|
|
|
+ modifyColumnToText("products", "reject_reason", "审核拒绝原因");
|
|
|
+ // product_orders.remark
|
|
|
+ modifyColumnToText("product_orders", "remark", "备注");
|
|
|
+ // articles.summary
|
|
|
+ modifyColumnToText("articles", "summary", "摘要");
|
|
|
+ // activities.audit_reason
|
|
|
+ modifyColumnToText("activities", "audit_reason", "驳回原因");
|
|
|
+ // guide_applications.reject_reason
|
|
|
+ modifyColumnToText("guide_applications", "reject_reason", "拒绝原因");
|
|
|
+ // guide_application_records.remark
|
|
|
+ modifyColumnToText("guide_application_records", "remark", "备注/原因");
|
|
|
+ // user_operation_log.description
|
|
|
+ modifyColumnToText("user_operation_log", "description", "操作描述");
|
|
|
+ // follow_up.description, teacher_review
|
|
|
+ modifyColumnToText("follow_up", "description", "描述");
|
|
|
+ modifyColumnToText("follow_up", "teacher_review", "规划师点评");
|
|
|
+ // energy_log.description
|
|
|
+ modifyColumnToText("energy_log", "description", "描述");
|
|
|
+ // sys_config.description
|
|
|
+ modifyColumnToText("sys_config", "description", "配置说明");
|
|
|
+ // points_log.description
|
|
|
+ modifyColumnToText("points_log", "description", "描述");
|
|
|
+ // insurance_policies.remark
|
|
|
+ modifyColumnToText("insurance_policies", "remark", "备注");
|
|
|
+ // health_reports.remark
|
|
|
+ modifyColumnToText("health_reports", "remark", "备注");
|
|
|
+ // guide_service_logs.notes
|
|
|
+ modifyColumnToText("guide_service_logs", "notes", "服务记录");
|
|
|
+ // guide_packages.description
|
|
|
+ modifyColumnToText("guide_packages", "description", "套餐说明");
|
|
|
+ // guide_package_templates.description
|
|
|
+ modifyColumnToText("guide_package_templates", "description", "说明");
|
|
|
+ // service_types.description
|
|
|
+ modifyColumnToText("service_types", "description", "描述");
|
|
|
+ // commission_records.remark
|
|
|
+ modifyColumnToText("commission_records", "remark", "备注");
|
|
|
+ // zodiac_pairing.description, advice
|
|
|
+ modifyColumnToText("zodiac_pairing", "description", "配对解读");
|
|
|
+ modifyColumnToText("zodiac_pairing", "advice", "相处建议");
|
|
|
+ // blood_type_pairing.description, advice
|
|
|
+ modifyColumnToText("blood_type_pairing", "description", "配对描述");
|
|
|
+ modifyColumnToText("blood_type_pairing", "advice", "相处建议");
|
|
|
+ // life_number_relationship.description, advice
|
|
|
+ modifyColumnToText("life_number_relationship", "description", "关系解读");
|
|
|
+ modifyColumnToText("life_number_relationship", "advice", "相处建议");
|
|
|
+ // daily_health_checkin.remark
|
|
|
+ modifyColumnToText("daily_health_checkin", "remark", "备注");
|
|
|
+ // health_sleep_record.remark
|
|
|
+ modifyColumnToText("health_sleep_record", "remark", "备注");
|
|
|
+ // health_exercise_record.remark
|
|
|
+ modifyColumnToText("health_exercise_record", "remark", "备注");
|
|
|
+ // finance_checkins.note
|
|
|
+ modifyColumnToText("finance_checkins", "note", "备注");
|
|
|
+ // guide_packages.description (old duplicate)
|
|
|
+ // assessment_appointments.remark
|
|
|
+ modifyColumnToText("assessment_appointments", "remark", "备注");
|
|
|
+ // withdrawal_requests.remark
|
|
|
+ modifyColumnToText("withdrawal_requests", "remark", "备注");
|
|
|
+ // supply_settlements.remark
|
|
|
+ modifyColumnToText("supply_settlements", "remark", "备注");
|
|
|
+ // content_sections.description
|
|
|
+ modifyColumnToText("content_sections", "description", "描述说明");
|
|
|
+ // education_systems.description
|
|
|
+ modifyColumnToText("education_systems", "description", "描述");
|
|
|
+ // dimension_config.description
|
|
|
+ modifyColumnToText("dimension_config", "description", "描述");
|
|
|
+ // energy_rules.description
|
|
|
+ modifyColumnToText("energy_rules", "description", "规则描述");
|
|
|
+ // team_goals.description
|
|
|
+ modifyColumnToText("team_goals", "description", "团队描述");
|
|
|
+ // product_dimension_config.remark
|
|
|
+ modifyColumnToText("product_dimension_config", "remark", "备注");
|
|
|
+ // indicator_values.remark
|
|
|
+ modifyColumnToText("indicator_values", "remark", "备注");
|
|
|
+ // tongue_records.overall_assessment
|
|
|
+ modifyColumnToText("tongue_records", "overall_assessment", "总体评估");
|
|
|
+ // system_config.description, config_value
|
|
|
+ modifyColumnToText("system_config", "description", "说明");
|
|
|
+ modifyColumnToText("system_config", "config_value", "配置值");
|
|
|
+ // supply_system.description
|
|
|
+ modifyColumnToText("supply_system", "description", "描述");
|
|
|
+ // supply_settlement.remark
|
|
|
+ modifyColumnToText("supply_settlement", "remark", "备注");
|
|
|
+ // growth_task.description
|
|
|
+ modifyColumnToText("growth_task", "description", "描述");
|
|
|
+ // distribution_systems.description
|
|
|
+ modifyColumnToText("distribution_systems", "description", "团队描述");
|
|
|
+ // mini_games.description
|
|
|
+ modifyColumnToText("mini_games", "description", "游戏描述");
|
|
|
+ // blood_type_config.description
|
|
|
+ modifyColumnToText("blood_type_config", "description", "性格描述");
|
|
|
+ }
|
|
|
+
|
|
|
+ private void modifyColumnToText(String table, String column, String comment) {
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE " + table + " MODIFY COLUMN " + column + " TEXT COMMENT '" + comment + "'");
|
|
|
+ log.info("已修改{}.{}为TEXT类型", table, column);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("修改{}.{}为TEXT失败: {}", table, column, e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void initializeDefaultData() {
|
|
|
+ // 初始化预设奖励模板
|
|
|
+ try {
|
|
|
+ // 检查是否已有预设奖励
|
|
|
+ Integer count = jdbcTemplate.queryForObject(
|
|
|
+ "SELECT COUNT(*) FROM rewards WHERE is_template = 1", Integer.class);
|
|
|
+
|
|
|
+if (count == null || count == 0) {
|
|
|
+// 插入预设奖励模板(child_id为NULL表示模板)
|
|
|
+jdbcTemplate.execute("INSERT INTO rewards (family_id, child_id, title, points_required, category, is_template) VALUES " +
|
|
|
+"(0, NULL, \'看动画片30分钟\', 30, \'精神类\', 1), " +
|
|
|
+"(0, NULL, \'玩平板游戏30分钟\', 40, \'特权类\', 1), " +
|
|
|
+"(0, NULL, \'去游乐场\', 100, \'物质类\', 1), " +
|
|
|
+"(0, NULL, \'买玩具\', 150, \'物质类\', 1), " +
|
|
|
+"(0, NULL, \'吃冰淇淋\', 20, \'物质类\', 1), " +
|
|
|
+"(0, NULL, \'选择晚餐\', 25, \'特权类\', 1)");
|
|
|
+log.info("预设奖励模板已初始化");
|
|
|
+}
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("初始化预设奖励失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 初始化奖励分类
|
|
|
+ try {
|
|
|
+ Integer count = jdbcTemplate.queryForObject(
|
|
|
+ "SELECT COUNT(*) FROM reward_categories WHERE family_id = 0", Integer.class);
|
|
|
+
|
|
|
+ if (count == null || count == 0) {
|
|
|
+ jdbcTemplate.execute("INSERT INTO reward_categories (family_id, name, icon, sort_order) VALUES " +
|
|
|
+ "(0, '物质类', 'gift', 1), " +
|
|
|
+ "(0, '精神类', 'star', 2), " +
|
|
|
+ "(0, '特权类', 'crown', 3)");
|
|
|
+ log.info("奖励分类已初始化");
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("初始化奖励分类失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 初始化打卡里程碑
|
|
|
+ try {
|
|
|
+ Integer count = jdbcTemplate.queryForObject(
|
|
|
+ "SELECT COUNT(*) FROM streak_milestones", Integer.class);
|
|
|
+
|
|
|
+ if (count == null || count == 0) {
|
|
|
+ jdbcTemplate.execute("INSERT INTO streak_milestones (days, reward_points) VALUES " +
|
|
|
+ "(3, 5), (7, 10), (30, 30), (100, 100)");
|
|
|
+ log.info("打卡里程碑已初始化");
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("初始化打卡里程碑失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 初始化勋章定义
|
|
|
+ try {
|
|
|
+ Integer count = jdbcTemplate.queryForObject(
|
|
|
+ "SELECT COUNT(*) FROM badge_definitions", Integer.class);
|
|
|
+
|
|
|
+ if (count == null || count == 0) {
|
|
|
+ jdbcTemplate.execute("INSERT INTO badge_definitions (badge_type, name, description, icon, condition_type, condition_value, reward_points) VALUES " +
|
|
|
+ "('first_task', '初次完成任务', '首次完成任意任务', 'star', 'task_count', 1, 0), " +
|
|
|
+ "('streak_3', '连续3天', '连续3天按时完成任务', 'fire', 'streak_days', 3, 5), " +
|
|
|
+ "('streak_7', '连续7天', '连续7天按时完成任务', 'fire', 'streak_days', 7, 10), " +
|
|
|
+ "('points_50', '积分达人', '一周获得50积分', 'trophy', 'weekly_points', 50, 0), " +
|
|
|
+ "('task_100', '任务达人', '完成100个任务', 'medal', 'task_count', 100, 20)");
|
|
|
+ log.info("勋章定义已初始化");
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("初始化勋章定义失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 初始化小游戏数据
|
|
|
+ try {
|
|
|
+ Integer count = jdbcTemplate.queryForObject(
|
|
|
+ "SELECT COUNT(*) FROM mini_games", Integer.class);
|
|
|
+
|
|
|
+ if (count == null || count == 0) {
|
|
|
+ jdbcTemplate.execute("INSERT INTO mini_games (game_code, game_name, description, icon, default_duration, default_points, game_url, sort_order, status) VALUES " +
|
|
|
+ "('schulte', '舒尔特方格', '训练专注力和眼动能力,找到1-25按顺序点击', '🎯', 5, 5, '/pages/games/schulte', 1, 1), " +
|
|
|
+ "('1a2b', '猜数字', '猜对数字和位置获得积分,锻炼逻辑思维', '🔢', 10, 5, '/pages/games/1a2b', 2, 1), " +
|
|
|
+ "('sudoku', '数独', '填充数字使每行每列每个宫格都包含1-9', '🔳', 15, 8, '/pages/games/sudoku', 3, 1)");
|
|
|
+ log.info("小游戏数据已初始化");
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("初始化小游戏数据失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+// 初始化默认管理员
|
|
|
+try {
|
|
|
+ // 先检查并添加role列(如果不存在)
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE admins ADD COLUMN role VARCHAR(20) DEFAULT \'admin\' AFTER email");
|
|
|
+ log.info("已添加role列到admins表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 列已存在,忽略错误
|
|
|
+ }
|
|
|
+
|
|
|
+ Integer count = jdbcTemplate.queryForObject(
|
|
|
+ "SELECT COUNT(*) FROM admins", Integer.class);
|
|
|
+
|
|
|
+ if (count == null || count == 0) {
|
|
|
+ // 默认管理员: 手机号 13800138000, 密码 admin123
|
|
|
+ String encryptedPassword = org.springframework.util.DigestUtils.md5DigestAsHex("xzyj_admin_salt_admin123".getBytes());
|
|
|
+ jdbcTemplate.execute("INSERT INTO admins (username, password, real_name, phone, email, role, status) VALUES " +
|
|
|
+ "(\'admin\', \'" + encryptedPassword + "\', \'系统管理员\', \'13800138000\', \'admin@xzyj.com\', \'admin\', 1)");
|
|
|
+ log.info("默认管理员已初始化: 手机号 13800138000, 密码 admin123");
|
|
|
+ }
|
|
|
+} catch (Exception e) {
|
|
|
+ log.warn("初始化默认管理员失败: {}", e.getMessage());
|
|
|
+}
|
|
|
+
|
|
|
+ // 初始化成长规划师WEB管理账号
|
|
|
+ try {
|
|
|
+ Integer teacherCount = jdbcTemplate.queryForObject(
|
|
|
+ "SELECT COUNT(*) FROM admins WHERE username = 'teacher'", Integer.class);
|
|
|
+ if (teacherCount == null || teacherCount == 0) {
|
|
|
+ String teacherPassword = org.springframework.util.DigestUtils.md5DigestAsHex("xzyj_admin_salt_teacher123".getBytes());
|
|
|
+ jdbcTemplate.execute("INSERT INTO admins (username, password, real_name, phone, role, status) VALUES " +
|
|
|
+ "('teacher', '" + teacherPassword + "', '成长规划师', '13800138003', 'teacher', 1)");
|
|
|
+ log.info("成长规划师管理账号已初始化: 用户名 teacher, 密码 teacher123");
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("初始化成长规划师管理账号失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 初始化任务模板示例数据
|
|
|
+ try {
|
|
|
+ Integer count = jdbcTemplate.queryForObject(
|
|
|
+ "SELECT COUNT(*) FROM admin_task_templates", Integer.class);
|
|
|
+
|
|
|
+ if (count == null || count == 0) {
|
|
|
+ jdbcTemplate.execute("INSERT INTO admin_task_templates (title, description, points, category, need_review, recommended_age, difficulty, is_active) VALUES " +
|
|
|
+ "('完成作业', '按时完成学校布置的家庭作业', 10, '学习', 1, 6, 'medium', 1), " +
|
|
|
+ "('阅读30分钟', '自主阅读课外书籍30分钟', 8, '阅读', 0, 5, 'easy', 1), " +
|
|
|
+ "('整理房间', '收拾整理自己的房间和书桌', 6, '家务', 0, 6, 'easy', 1), " +
|
|
|
+ "('洗碗', '饭后帮忙清洗餐具', 5, '家务', 1, 8, 'medium', 1), " +
|
|
|
+ "('户外运动1小时', '进行跑步、球类等户外运动', 12, '运动', 0, 7, 'hard', 1), " +
|
|
|
+ "('练习书法', '练习毛笔或硬笔书法20分钟', 7, '学习', 0, 7, 'medium', 1), " +
|
|
|
+ "('早睡早起', '晚上9点前睡觉,早上7点前起床', 5, '生活习惯', 0, 5, 'easy', 1), " +
|
|
|
+ "('背诵古诗', '背诵一首古诗词', 8, '学习', 1, 6, 'medium', 1)");
|
|
|
+ log.info("任务模板示例数据已初始化");
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("初始化任务模板失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 初始化街道数据示例(广东省广州市)
|
|
|
+ try {
|
|
|
+ Integer count = jdbcTemplate.queryForObject(
|
|
|
+ "SELECT COUNT(*) FROM streets", Integer.class);
|
|
|
+
|
|
|
+ if (count == null || count == 0) {
|
|
|
+ // 省
|
|
|
+ jdbcTemplate.execute("INSERT INTO streets (province, province_code, full_name, level, parent_id, sort_order) VALUES " +
|
|
|
+ "('广东省', '44', '广东省', 1, NULL, 1)");
|
|
|
+
|
|
|
+ // 市
|
|
|
+ jdbcTemplate.execute("INSERT INTO streets (province, province_code, city, city_code, full_name, level, parent_id, sort_order) VALUES " +
|
|
|
+ "('广东省', '44', '广州市', '4401', '广东省广州市', 2, 1, 1), " +
|
|
|
+ "('广东省', '44', '深圳市', '4403', '广东省深圳市', 2, 1, 2), " +
|
|
|
+ "('广东省', '44', '珠海市', '4404', '广东省珠海市', 2, 1, 3)");
|
|
|
+
|
|
|
+ // 区
|
|
|
+ jdbcTemplate.execute("INSERT INTO streets (province, province_code, city, city_code, district, district_code, full_name, level, parent_id, sort_order) VALUES " +
|
|
|
+ "('广东省', '44', '广州市', '4401', '天河区', '440106', '广东省广州市天河区', 3, 2, 1), " +
|
|
|
+ "('广东省', '44', '广州市', '4401', '越秀区', '440104', '广东省广州市越秀区', 3, 2, 2), " +
|
|
|
+ "('广东省', '44', '广州市', '4401', '番禺区', '440113', '广东省广州市番禺区', 3, 2, 3), " +
|
|
|
+ "('广东省', '44', '深圳市', '4403', '南山区', '440305', '广东省深圳市南山区', 3, 3, 1), " +
|
|
|
+ "('广东省', '44', '深圳市', '4403', '福田区', '440304', '广东省深圳市福田区', 3, 3, 2)");
|
|
|
+
|
|
|
+ // 街道
|
|
|
+ jdbcTemplate.execute("INSERT INTO streets (province, province_code, city, city_code, district, district_code, street, street_code, full_name, level, parent_id, sort_order) VALUES " +
|
|
|
+ "('广东省', '44', '广州市', '4401', '天河区', '440106', '天河南街道', '440106001', '广东省广州市天河区天河南街道', 4, 4, 1), " +
|
|
|
+ "('广东省', '44', '广州市', '4401', '天河区', '440106', '沙河街道', '440106002', '广东省广州市天河区沙河街道', 4, 4, 2), " +
|
|
|
+ "('广东省', '44', '广州市', '4401', '越秀区', '440104', '东山街道', '440104001', '广东省广州市越秀区东山街道', 4, 5, 1), " +
|
|
|
+ "('广东省', '44', '广州市', '4401', '番禺区', '440113', '市桥街道', '440113001', '广东省广州市番禺区市桥街道', 4, 6, 1), " +
|
|
|
+ "('广东省', '44', '深圳市', '4403', '南山区', '440305', '南山街道', '440305001', '广东省深圳市南山区南山街道', 4, 7, 1), " +
|
|
|
+ "('广东省', '44', '深圳市', '4403', '福田区', '440304', '福田街道', '440304001', '广东省深圳市福田区福田街道', 4, 8, 1)");
|
|
|
+
|
|
|
+ log.info("街道示例数据已初始化");
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("初始化街道数据失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+try {
|
|
|
+ Integer count = jdbcTemplate.queryForObject(
|
|
|
+ "SELECT COUNT(*) FROM admins", Integer.class);
|
|
|
+
|
|
|
+ if (count == null || count == 0) {
|
|
|
+ // 默认管理员: 手机号 13800138000, 密码 admin123
|
|
|
+ String encryptedPassword = org.springframework.util.DigestUtils.md5DigestAsHex("xzyj_admin_salt_admin123".getBytes());
|
|
|
+ jdbcTemplate.execute("INSERT INTO admins (username, password, real_name, phone, email, role, status) VALUES " +
|
|
|
+ "('admin', '" + encryptedPassword + "', '系统管理员', '13800138000', 'admin@xzyj.com', 'admin', 1)");
|
|
|
+ log.info("默认管理员已初始化: 手机号 13800138000, 密码 admin123");
|
|
|
+ }
|
|
|
+} catch (Exception e) {
|
|
|
+ log.warn("初始化任务模板失败: {}", e.getMessage());
|
|
|
+}
|
|
|
+
|
|
|
+// 初始化 users 表测试用户
|
|
|
+try {
|
|
|
+ // 创建默认家庭(如果不存在)
|
|
|
+ Integer familyCount = jdbcTemplate.queryForObject(
|
|
|
+ "SELECT COUNT(*) FROM families WHERE id = 1", Integer.class);
|
|
|
+ if (familyCount == null || familyCount == 0) {
|
|
|
+ jdbcTemplate.execute("INSERT INTO families (id, name, invite_code) VALUES (1, '默认家庭', '888888')");
|
|
|
+ log.info("默认家庭已创建");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 密码加密: MD5("xzyj_salt_" + password)
|
|
|
+ String adminPassword = org.springframework.util.DigestUtils.md5DigestAsHex("xzyj_salt_admin123".getBytes());
|
|
|
+ String teacherPassword = org.springframework.util.DigestUtils.md5DigestAsHex("xzyj_salt_teacher123".getBytes());
|
|
|
+
|
|
|
+ // 创建管理员用户(管理员角色,密码 admin123)
|
|
|
+ Integer adminUserCount = jdbcTemplate.queryForObject(
|
|
|
+ "SELECT COUNT(*) FROM users WHERE phone = '13800138000'", Integer.class);
|
|
|
+ if (adminUserCount == null || adminUserCount == 0) {
|
|
|
+ jdbcTemplate.execute("INSERT INTO users (openid, family_id, role, phone, nickname, real_name, password) VALUES " +
|
|
|
+ "('seed_admin_13800138000', 1, 'admin', '13800138000', '系统管理员', '系统管理员', '" + adminPassword + "')");
|
|
|
+ log.info("测试管理员用户已创建: 13800138000 (admin角色, 密码 admin123)");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 创建测试教师用户
|
|
|
+ Integer teacherUserCount = jdbcTemplate.queryForObject(
|
|
|
+ "SELECT COUNT(*) FROM users WHERE phone = '13800138001'", Integer.class);
|
|
|
+ if (teacherUserCount == null || teacherUserCount == 0) {
|
|
|
+ jdbcTemplate.execute("INSERT INTO users (openid, family_id, role, phone, nickname, real_name, password) VALUES " +
|
|
|
+ "('seed_teacher_13800138001', 1, 'teacher', '13800138001', '测试教师', '测试教师', '" + teacherPassword + "')");
|
|
|
+ log.info("测试教师用户已创建: 13800138001 (教师角色, 密码 teacher123)");
|
|
|
+ }
|
|
|
+} catch (Exception e) {
|
|
|
+ log.warn("初始化测试用户失败: {}", e.getMessage());
|
|
|
+}
|
|
|
+
|
|
|
+ // 初始化星座配置种子数据
|
|
|
+ try {
|
|
|
+ Integer count = jdbcTemplate.queryForObject(
|
|
|
+ "SELECT COUNT(*) FROM zodiac_config", Integer.class);
|
|
|
+ if (count == null || count == 0) {
|
|
|
+ jdbcTemplate.execute("INSERT INTO zodiac_config (zodiac_code, zodiac_name, element, start_month, start_day, end_month, end_day, mind_base, wisdom_base, body_modifier, action_modifier, wealth_modifier) VALUES " +
|
|
|
+ "('aries', '白羊座', '火', 3, 21, 4, 19, 65, 70, 1.10, 1.05, 0.95), " +
|
|
|
+ "('taurus', '金牛座', '土', 4, 20, 5, 20, 60, 65, 1.05, 1.10, 1.15), " +
|
|
|
+ "('gemini', '双子座', '金', 5, 21, 6, 21, 70, 80, 0.95, 1.15, 0.90), " +
|
|
|
+ "('cancer', '巨蟹座', '水', 6, 22, 7, 22, 75, 60, 1.00, 0.90, 1.05), " +
|
|
|
+ "('leo', '狮子座', '火', 7, 23, 8, 22, 65, 75, 1.15, 1.00, 1.10), " +
|
|
|
+ "('virgo', '处女座', '土', 8, 23, 9, 22, 55, 85, 0.95, 1.05, 1.00), " +
|
|
|
+ "('libra', '天秤座', '金', 9, 23, 10, 23, 70, 75, 0.90, 0.95, 1.10), " +
|
|
|
+ "('scorpio', '天蝎座', '水', 10, 24, 11, 22, 80, 70, 1.10, 1.00, 1.15), " +
|
|
|
+ "('sagittarius', '射手座', '火', 11, 23, 12, 21, 60, 65, 1.15, 1.15, 0.95), " +
|
|
|
+ "('capricorn', '摩羯座', '土', 12, 22, 1, 19, 55, 75, 1.05, 1.10, 1.20), " +
|
|
|
+ "('aquarius', '水瓶座', '金', 1, 20, 2, 18, 75, 85, 0.95, 0.90, 1.00), " +
|
|
|
+ "('pisces', '双鱼座', '水', 2, 19, 3, 20, 80, 60, 0.90, 0.95, 0.95)");
|
|
|
+ log.info("星座配置种子数据已初始化");
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("初始化星座配置失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 初始化八字配置种子数据(十天干 + 十二地支)
|
|
|
+ try {
|
|
|
+ Integer count = jdbcTemplate.queryForObject(
|
|
|
+ "SELECT COUNT(*) FROM bazi_config", Integer.class);
|
|
|
+ if (count == null || count == 0) {
|
|
|
+ jdbcTemplate.execute("INSERT INTO bazi_config (stem_or_branch, type, element, yin_yang, mind_base, wisdom_base, body_modifier, action_modifier, wealth_modifier) VALUES " +
|
|
|
+ "('甲', 'stem', '木', 'yang', 60, 75, 1.05, 1.10, 1.00), " +
|
|
|
+ "('乙', 'stem', '木', 'yin', 65, 70, 0.95, 1.05, 1.00), " +
|
|
|
+ "('丙', 'stem', '火', 'yang', 75, 65, 1.10, 1.00, 0.95), " +
|
|
|
+ "('丁', 'stem', '火', 'yin', 70, 60, 1.05, 0.95, 1.00), " +
|
|
|
+ "('戊', 'stem', '土', 'yang', 60, 65, 1.15, 1.05, 1.10), " +
|
|
|
+ "('己', 'stem', '土', 'yin', 55, 60, 1.10, 1.00, 1.15), " +
|
|
|
+ "('庚', 'stem', '金', 'yang', 65, 80, 1.05, 1.10, 1.05), " +
|
|
|
+ "('辛', 'stem', '金', 'yin', 60, 75, 0.95, 1.00, 1.10), " +
|
|
|
+ "('壬', 'stem', '水', 'yang', 70, 70, 1.00, 0.95, 1.15), " +
|
|
|
+ "('癸', 'stem', '水', 'yin', 75, 65, 0.95, 0.90, 1.10), " +
|
|
|
+ "('子', 'branch', '水', 'yang', 70, 65, 1.00, 0.95, 1.05), " +
|
|
|
+ "('丑', 'branch', '土', 'yin', 55, 60, 1.10, 1.05, 1.10), " +
|
|
|
+ "('寅', 'branch', '木', 'yang', 65, 75, 1.15, 1.10, 1.00), " +
|
|
|
+ "('卯', 'branch', '木', 'yin', 60, 70, 1.05, 1.05, 0.95), " +
|
|
|
+ "('辰', 'branch', '土', 'yang', 60, 65, 1.10, 1.00, 1.10), " +
|
|
|
+ "('巳', 'branch', '火', 'yin', 75, 70, 1.05, 0.95, 1.00), " +
|
|
|
+ "('午', 'branch', '火', 'yang', 80, 65, 1.15, 1.00, 0.95), " +
|
|
|
+ "('未', 'branch', '土', 'yin', 55, 60, 1.05, 1.00, 1.05), " +
|
|
|
+ "('申', 'branch', '金', 'yang', 65, 80, 1.00, 1.10, 1.05), " +
|
|
|
+ "('酉', 'branch', '金', 'yin', 60, 75, 0.95, 1.05, 1.10), " +
|
|
|
+ "('戌', 'branch', '土', 'yang', 60, 65, 1.10, 1.00, 1.15), " +
|
|
|
+ "('亥', 'branch', '水', 'yin', 70, 60, 1.00, 0.95, 1.05)");
|
|
|
+ log.info("八字配置种子数据已初始化");
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("初始化八字配置失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 初始化血型配置种子数据
|
|
|
+ try {
|
|
|
+ Integer count = jdbcTemplate.queryForObject(
|
|
|
+ "SELECT COUNT(*) FROM blood_type_config", Integer.class);
|
|
|
+ if (count == null || count == 0) {
|
|
|
+ jdbcTemplate.execute("INSERT INTO blood_type_config (blood_type, description, mind_base, wisdom_base, body_modifier, action_modifier, wealth_modifier) VALUES " +
|
|
|
+ "('A', '认真负责、严谨细致,但易焦虑', 70, 75, 0.95, 1.00, 1.05), " +
|
|
|
+ "('B', '乐观开朗、好奇心强,但缺乏耐心', 65, 70, 1.05, 1.10, 0.95), " +
|
|
|
+ "('AB', '理性冷静、适应力强,但优柔寡断', 75, 80, 1.00, 0.95, 1.10), " +
|
|
|
+ "('O', '自信果断、行动力强,但固执己见', 60, 65, 1.15, 1.15, 1.15)");
|
|
|
+ log.info("血型配置种子数据已初始化");
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("初始化血型配置失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 初始化优惠券种子数据
|
|
|
+ try {
|
|
|
+ Integer count = jdbcTemplate.queryForObject(
|
|
|
+ "SELECT COUNT(*) FROM coupon", Integer.class);
|
|
|
+ if (count == null || count == 0) {
|
|
|
+ jdbcTemplate.update(
|
|
|
+ "INSERT INTO coupon (name, type, value, min_spend, applicable_to, valid_from, valid_until, total_count, used_count) " +
|
|
|
+ "VALUES (?, ?, ?, ?, ?, NOW(), DATE_ADD(NOW(), INTERVAL 1 YEAR), ?, 0)",
|
|
|
+ "新用户专享10元券", "FIXED", 1000, 0, "ALL", 100);
|
|
|
+ jdbcTemplate.update(
|
|
|
+ "INSERT INTO coupon (name, type, value, min_spend, applicable_to, valid_from, valid_until, total_count, used_count) " +
|
|
|
+ "VALUES (?, ?, ?, ?, ?, NOW(), DATE_ADD(NOW(), INTERVAL 1 YEAR), ?, 0)",
|
|
|
+ "会员升级50元券", "FIXED", 5000, 9900, "MEMBERSHIP", 50);
|
|
|
+ log.info("优惠券种子数据已初始化");
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("初始化优惠券数据失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 初始化成长规划师模块相关表
|
|
|
+ initGuideModuleTables(jdbcTemplate);
|
|
|
+ }
|
|
|
+
|
|
|
+ private void initGuideModuleTables(JdbcTemplate jdbcTemplate) {
|
|
|
+ // 服务类型表
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS service_types (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
+ "name VARCHAR(100) NOT NULL COMMENT '类型名称', " +
|
|
|
+ "icon VARCHAR(50) COMMENT '图标', " +
|
|
|
+ "description VARCHAR(500) COMMENT '描述', " +
|
|
|
+ "sort_order INT DEFAULT 0 COMMENT '排序', " +
|
|
|
+ "status VARCHAR(20) DEFAULT 'ACTIVE' COMMENT '状态:ACTIVE/DISABLED', " +
|
|
|
+ "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
+ "updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP" +
|
|
|
+ ")");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建service_types表失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 服务内容表
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS service_contents (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
+ "type_id BIGINT NOT NULL COMMENT '关联服务类型', " +
|
|
|
+ "title VARCHAR(200) NOT NULL COMMENT '内容标题', " +
|
|
|
+ "content TEXT COMMENT '富文本内容', " +
|
|
|
+ "duration INT COMMENT '建议时长(分钟)', " +
|
|
|
+ "age_range VARCHAR(50) COMMENT '适用年龄', " +
|
|
|
+ "price INT COMMENT '参考价格(分)', " +
|
|
|
+ "status VARCHAR(20) DEFAULT 'DRAFT' COMMENT '状态:DRAFT/ACTIVE', " +
|
|
|
+ "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
+ "updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP" +
|
|
|
+ ")");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建service_contents表失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 套餐模板表
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS guide_package_templates (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
+ "name VARCHAR(100) NOT NULL COMMENT '模板名称', " +
|
|
|
+ "validity_months INT COMMENT '有效期(月)', " +
|
|
|
+ "reference_price INT COMMENT '参考价格(分)', " +
|
|
|
+ "min_price INT COMMENT '最低售价(分)', " +
|
|
|
+ "description VARCHAR(500) COMMENT '说明', " +
|
|
|
+ "status VARCHAR(20) DEFAULT 'ACTIVE' COMMENT '状态:ACTIVE/DISABLED', " +
|
|
|
+ "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
+ "updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP" +
|
|
|
+ ")");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建guide_package_templates表失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 套餐模板服务项表
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS package_template_services (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
+ "template_id BIGINT NOT NULL COMMENT '关联模板', " +
|
|
|
+ "content_id BIGINT NOT NULL COMMENT '关联服务内容', " +
|
|
|
+ "quantity INT DEFAULT 1 COMMENT '数量', " +
|
|
|
+ "unit VARCHAR(20) COMMENT '单位' " +
|
|
|
+ ")");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建package_template_services表失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 成长规划师套餐表
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS guide_packages (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
+ "teacher_id BIGINT NOT NULL COMMENT '成长规划师ID', " +
|
|
|
+ "template_id BIGINT COMMENT '关联模板', " +
|
|
|
+ "name VARCHAR(100) NOT NULL COMMENT '套餐名称', " +
|
|
|
+ "price INT NOT NULL COMMENT '销售价格(分)', " +
|
|
|
+ "validity_months INT COMMENT '有效期(月)', " +
|
|
|
+ "description VARCHAR(500) COMMENT '套餐说明', " +
|
|
|
+ "status VARCHAR(20) DEFAULT 'DRAFT' COMMENT '状态:DRAFT/PUBLISHED/PAUSED', " +
|
|
|
+ "sales_count INT DEFAULT 0 COMMENT '销量', " +
|
|
|
+ "share_code VARCHAR(50) COMMENT '分享码', " +
|
|
|
+ "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
+ "updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP" +
|
|
|
+ ")");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建guide_packages表失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 迁移:guide_packages 表添加缺失列
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE guide_packages ADD COLUMN guide_id BIGINT COMMENT '成长规划师ID' AFTER id");
|
|
|
+ } catch (Exception e) {
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE guide_packages ADD COLUMN package_id BIGINT COMMENT '关联模板' AFTER guide_id");
|
|
|
+ } catch (Exception e) {
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE guide_packages ADD COLUMN commission_rate INT COMMENT '佣金比例(bps,100=1%)' AFTER package_id");
|
|
|
+ } catch (Exception e) {
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE guide_packages MODIFY COLUMN teacher_id BIGINT NULL");
|
|
|
+ } catch (Exception e) {
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE guide_packages MODIFY COLUMN template_id BIGINT NULL");
|
|
|
+ } catch (Exception e) {
|
|
|
+ }
|
|
|
+
|
|
|
+ // 迁移:guide_packages 表添加 level 列(GuidePackage 实体需要)
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE guide_packages ADD COLUMN level VARCHAR(20) COMMENT '套餐等级' AFTER name");
|
|
|
+ } catch (Exception e) {
|
|
|
+ }
|
|
|
+
|
|
|
+ // 迁移:guide_packages 表添加 validity_days 列
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE guide_packages ADD COLUMN validity_days INT DEFAULT 0 COMMENT '有效期天数' AFTER validity_months");
|
|
|
+ } catch (Exception e) {
|
|
|
+ }
|
|
|
+
|
|
|
+ // 迁移:guide_packages 表添加 sort_order 列
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE guide_packages ADD COLUMN sort_order INT DEFAULT 0 COMMENT '排序' AFTER status");
|
|
|
+ } catch (Exception e) {
|
|
|
+ }
|
|
|
+
|
|
|
+ // 成长规划师套餐服务项表
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS guide_package_services (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
+ "package_id BIGINT NOT NULL COMMENT '关联套餐', " +
|
|
|
+ "content_id BIGINT NOT NULL COMMENT '关联服务内容', " +
|
|
|
+ "quantity INT DEFAULT 1 COMMENT '数量', " +
|
|
|
+ "unit VARCHAR(20) COMMENT '单位' " +
|
|
|
+ ")");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建guide_package_services表失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 订单表
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS guide_orders (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
+ "order_no VARCHAR(50) NOT NULL COMMENT '订单号', " +
|
|
|
+ "teacher_id BIGINT NOT NULL COMMENT '成长规划师ID', " +
|
|
|
+ "family_id BIGINT NOT NULL COMMENT '家庭ID', " +
|
|
|
+ "package_id BIGINT NOT NULL COMMENT '套餐ID', " +
|
|
|
+ "amount INT NOT NULL COMMENT '金额(分)', " +
|
|
|
+ "status VARCHAR(20) DEFAULT 'PENDING' COMMENT '状态:PENDING/PAID/COMPLETED/REFUNDED', " +
|
|
|
+ "pay_time DATETIME COMMENT '支付时间', " +
|
|
|
+ "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
+ "updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP" +
|
|
|
+ ")");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建guide_orders表失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 佣金明细表
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS guide_commissions (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
+ "teacher_id BIGINT NOT NULL COMMENT '成长规划师ID', " +
|
|
|
+ "order_id BIGINT NOT NULL COMMENT '订单ID', " +
|
|
|
+ "amount INT NOT NULL COMMENT '佣金金额(分)', " +
|
|
|
+ "rate INT COMMENT '佣金比例(bps,100=1%)', " +
|
|
|
+ "status VARCHAR(20) DEFAULT 'PENDING' COMMENT '状态:PENDING/COMPLETED', " +
|
|
|
+ "created_at DATETIME DEFAULT CURRENT_TIMESTAMP" +
|
|
|
+ ")");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建guide_commissions表失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 服务记录表
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS guide_service_logs (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
+ "teacher_id BIGINT NOT NULL COMMENT '成长规划师ID', " +
|
|
|
+ "family_id BIGINT NOT NULL COMMENT '家庭ID', " +
|
|
|
+ "order_id BIGINT COMMENT '订单ID', " +
|
|
|
+ "service_content_id BIGINT NOT NULL COMMENT '服务内容ID', " +
|
|
|
+ "notes VARCHAR(500) COMMENT '服务记录', " +
|
|
|
+ "duration INT COMMENT '服务时长(分钟)', " +
|
|
|
+ "created_at DATETIME DEFAULT CURRENT_TIMESTAMP" +
|
|
|
+ ")");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建guide_service_logs表失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 成长规划师邀请码表
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS guide_invite_codes (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
+ "guide_id BIGINT NOT NULL COMMENT '成长规划师用户ID', " +
|
|
|
+ "code VARCHAR(50) NOT NULL UNIQUE COMMENT '邀请码', " +
|
|
|
+ "status VARCHAR(20) DEFAULT 'active' COMMENT '状态', " +
|
|
|
+ "used_by_user_id BIGINT COMMENT '使用该邀请码的用户ID', " +
|
|
|
+ "used_at DATETIME COMMENT '使用时间', " +
|
|
|
+ "expire_at DATETIME COMMENT '过期时间', " +
|
|
|
+ "use_count INT DEFAULT 0 COMMENT '使用次数', " +
|
|
|
+ "max_use_count INT COMMENT '最大使用次数', " +
|
|
|
+ "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
+ "updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP" +
|
|
|
+ ")");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建guide_invite_codes表失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 迁移29: assessment_orders表添加appointment_id字段
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE assessment_orders ADD COLUMN appointment_id BIGINT COMMENT '关联的预约ID'");
|
|
|
+ log.info("已添加appointment_id列到assessment_orders表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 列已存在,忽略错误
|
|
|
+ }
|
|
|
+
|
|
|
+ // 财富维度: 财商打卡记录表
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS finance_checkins (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
+ "user_id BIGINT NOT NULL COMMENT '用户ID', " +
|
|
|
+ "child_id BIGINT NOT NULL COMMENT '孩子ID', " +
|
|
|
+ "checkin_type VARCHAR(20) NOT NULL COMMENT '打卡类型: daily_plan/saving/expense/review', " +
|
|
|
+ "checkin_date DATE NOT NULL COMMENT '打卡日期', " +
|
|
|
+ "amount INT COMMENT '金额(分,收支记录)', " +
|
|
|
+ "category VARCHAR(50) COMMENT '收支分类', " +
|
|
|
+ "note VARCHAR(200) COMMENT '备注', " +
|
|
|
+ "mood VARCHAR(20) COMMENT '心情', " +
|
|
|
+ "earned_energy INT DEFAULT 0 COMMENT '获得的能量值', " +
|
|
|
+ "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
+ "INDEX idx_user_date (user_id, checkin_date), " +
|
|
|
+ "INDEX idx_child (child_id)" +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='财商打卡记录表'");
|
|
|
+ log.info("已创建finance_checkins表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建finance_checkins表失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 财富维度: 保单管理表
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS insurance_policies (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
+ "user_id BIGINT NOT NULL COMMENT '用户ID', " +
|
|
|
+ "child_id BIGINT COMMENT '关联孩子ID', " +
|
|
|
+ "policy_name VARCHAR(100) NOT NULL COMMENT '保单名称', " +
|
|
|
+ "insurance_company VARCHAR(100) COMMENT '保险公司', " +
|
|
|
+ "policy_type VARCHAR(50) COMMENT '保单类型: medical/life/accident/education/endowment', " +
|
|
|
+ "insured_person VARCHAR(50) NOT NULL COMMENT '被保人姓名', " +
|
|
|
+ "premium INT COMMENT '保费(分)', " +
|
|
|
+ "sum_insured INT COMMENT '保额(分)', " +
|
|
|
+ "start_date DATE COMMENT '生效日期', " +
|
|
|
+ "end_date DATE COMMENT '到期日期', " +
|
|
|
+ "renewal_type VARCHAR(20) DEFAULT 'annual' COMMENT '续费类型: annual/monthly/single', " +
|
|
|
+ "status VARCHAR(20) DEFAULT 'active' COMMENT '状态: active/expired/cancelled', " +
|
|
|
+ "policy_no VARCHAR(100) COMMENT '保单号', " +
|
|
|
+ "remark VARCHAR(500) COMMENT '备注', " +
|
|
|
+ "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
+ "updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, " +
|
|
|
+ "INDEX idx_user (user_id), " +
|
|
|
+ "INDEX idx_child (child_id), " +
|
|
|
+ "INDEX idx_status (status)" +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='保单管理表'");
|
|
|
+ log.info("已创建insurance_policies表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建insurance_policies表失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== 健康检测报告模块 ====================
|
|
|
+ // 健康报告表(体检报告/菌群检测报告)
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS health_reports (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
+ "user_id BIGINT NOT NULL COMMENT '用户ID', " +
|
|
|
+ "family_id BIGINT COMMENT '家庭ID', " +
|
|
|
+ "report_type VARCHAR(32) NOT NULL COMMENT '报告类型: physical_exam(体检)/gut_flora(菌群检测)', " +
|
|
|
+ "report_date DATE COMMENT '报告日期', " +
|
|
|
+ "overall_score INT COMMENT '健康总分(0-100)', " +
|
|
|
+ "gut_health_score INT COMMENT '菌群健康分', " +
|
|
|
+ "chronic_disease_score INT COMMENT '慢病控制分', " +
|
|
|
+ "nutrition_score INT COMMENT '营养均衡分', " +
|
|
|
+ "gut_age VARCHAR(32) COMMENT '肠道预测年龄', " +
|
|
|
+ "gut_type VARCHAR(50) COMMENT '肠型', " +
|
|
|
+ "file_url VARCHAR(500) COMMENT '报告文件URL', " +
|
|
|
+ "status VARCHAR(20) DEFAULT 'active' COMMENT '状态: active/archived', " +
|
|
|
+ "remark VARCHAR(500) COMMENT '备注', " +
|
|
|
+ "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
+ "updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, " +
|
|
|
+ "INDEX idx_user_id (user_id), " +
|
|
|
+ "INDEX idx_family_id (family_id), " +
|
|
|
+ "INDEX idx_report_type (report_type)" +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='健康检测报告表'");
|
|
|
+ log.info("已创建health_reports表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建health_reports表可能已存在: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 健康指标明细表
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS health_indicators (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
+ "report_id BIGINT NOT NULL COMMENT '关联报告ID', " +
|
|
|
+ "category VARCHAR(50) COMMENT '指标分类: 营养状况/疾病风险/肠道屏障/神经递质/...', " +
|
|
|
+ "indicator_name VARCHAR(100) NOT NULL COMMENT '指标名称', " +
|
|
|
+ "indicator_value VARCHAR(100) COMMENT '检测值', " +
|
|
|
+ "unit VARCHAR(50) COMMENT '单位', " +
|
|
|
+ "ref_range VARCHAR(200) COMMENT '参考范围', " +
|
|
|
+ "status VARCHAR(50) COMMENT '评估状态: 正常/偏低/偏高/过多/注意/低风险/未检出', " +
|
|
|
+ "sort_order INT DEFAULT 0 COMMENT '显示排序', " +
|
|
|
+ "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
+ "INDEX idx_report_id (report_id), " +
|
|
|
+ "INDEX idx_category (category)" +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='健康指标明细表'");
|
|
|
+ log.info("已创建health_indicators表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建health_indicators表可能已存在: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // ---------- 健康报告表 新增菌群摘要字段 ----------
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE health_reports " +
|
|
|
+ "ADD COLUMN gut_balance_score INT COMMENT '菌群平衡分(0-100)', " +
|
|
|
+ "ADD COLUMN gut_diversity_score INT COMMENT '菌群多样性分(0-100)', " +
|
|
|
+ "ADD COLUMN beneficial_bacteria_score INT COMMENT '有益菌分(0-100)', " +
|
|
|
+ "ADD COLUMN harmful_bacteria_score INT COMMENT '有害菌分(0-100)', " +
|
|
|
+ "ADD COLUMN core_species_score INT COMMENT '核心菌属分(0-100)', " +
|
|
|
+ "ADD COLUMN health_rating VARCHAR(100) COMMENT '总分评级,如亚健康/优秀等'");
|
|
|
+ log.info("已为health_reports表添加6个菌群摘要字段");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("为health_reports表添加字段可能已存在: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ ensureColumn("health_reports", "subject_id", "BIGINT COMMENT '报告主体用户ID(关联users表,为null表示未绑定)'");
|
|
|
+
|
|
|
+ // ---------- 健康指标表 新增symptoms字段 ----------
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE health_indicators ADD COLUMN symptoms TEXT COMMENT '过量/缺乏相关症状描述'");
|
|
|
+ log.info("已为health_indicators表添加symptoms字段");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("为health_indicators表添加symptoms字段可能已存在: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ log.info("成长规划师模块、财富维度及健康检测报告数据库表初始化完成");
|
|
|
+
|
|
|
+ // 健康打卡表
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS health_checkins (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
+ "user_id BIGINT COMMENT '用户ID(家长)', " +
|
|
|
+ "child_id BIGINT COMMENT '孩子ID', " +
|
|
|
+ "checkin_date DATE COMMENT '打卡日期', " +
|
|
|
+ "behaviors TEXT COMMENT '打卡行为JSON数组', " +
|
|
|
+ "note VARCHAR(500) COMMENT '备注', " +
|
|
|
+ "mood VARCHAR(20) COMMENT '心情: happy/normal/sad/angry/sick', " +
|
|
|
+ "earned_energy INT DEFAULT 5 COMMENT '获得能量值', " +
|
|
|
+ "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
+ "INDEX idx_child_id (child_id), " +
|
|
|
+ "INDEX idx_checkin_date (checkin_date)" +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='健康打卡表'");
|
|
|
+ log.info("已创建health_checkins表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建health_checkins表可能已存在: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 迁移31: 创建联系人表
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS contacts (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
+ "user_id BIGINT NOT NULL COMMENT '用户ID', " +
|
|
|
+ "name VARCHAR(100) NOT NULL COMMENT '姓名', " +
|
|
|
+ "phone VARCHAR(20) COMMENT '电话', " +
|
|
|
+ "avatar VARCHAR(500) COMMENT '头像', " +
|
|
|
+ "relationship_type VARCHAR(20) COMMENT '关系类型: family/friend/partner/colleague/other', " +
|
|
|
+ "known_since DATETIME COMMENT '认识时间', " +
|
|
|
+ "bio VARCHAR(500) COMMENT '简介', " +
|
|
|
+ "birthday DATE COMMENT '生日', " +
|
|
|
+ "intimacy_level INT DEFAULT 0 COMMENT '亲密度(0-100)', " +
|
|
|
+ "contact_source VARCHAR(20) DEFAULT 'manual' COMMENT '来源: phone/manual', " +
|
|
|
+ "tags VARCHAR(500) COMMENT '标签(逗号分隔)', " +
|
|
|
+ "last_contact_at DATETIME COMMENT '最近联系时间', " +
|
|
|
+ "contact_count INT DEFAULT 0 COMMENT '联系次数', " +
|
|
|
+ "notes TEXT COMMENT '备注', " +
|
|
|
+ "created_at DATETIME, " +
|
|
|
+ "updated_at DATETIME, " +
|
|
|
+ "INDEX idx_user_id (user_id)" +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='联系人表'");
|
|
|
+ log.info("已创建contacts联系表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建contacts表可能已存在: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 创建文章阅读记录表
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS article_reading_records (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
+ "user_id BIGINT COMMENT '用户ID', " +
|
|
|
+ "child_id BIGINT NOT NULL COMMENT '孩子ID', " +
|
|
|
+ "content VARCHAR(500) COMMENT '阅读内容摘要', " +
|
|
|
+ "duration_seconds INT DEFAULT 0 COMMENT '阅读时长(秒)', " +
|
|
|
+ "read_at DATETIME COMMENT '阅读时间', " +
|
|
|
+ "created_at DATETIME, " +
|
|
|
+ "INDEX idx_child_id (child_id), " +
|
|
|
+ "INDEX idx_read_at (read_at)" +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='文章阅读记录表'");
|
|
|
+ log.info("已创建article_reading_records表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建article_reading_records表可能已存在: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 迁移30: activities表添加visibility和visible_to字段
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE activities ADD COLUMN visibility VARCHAR(20) DEFAULT 'all' COMMENT '可见性: all/member'");
|
|
|
+ log.info("已添加visibility列到activities表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 列已存在,忽略错误
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE activities ADD COLUMN visible_to VARCHAR(255) COMMENT '可见角色列表(逗号分隔)'");
|
|
|
+ log.info("已添加visible_to列到activities表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 列已存在,忽略错误
|
|
|
+ }
|
|
|
+
|
|
|
+ // 创建维度配置表
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS product_dimension_config (" +
|
|
|
+ "id BIGINT PRIMARY KEY AUTO_INCREMENT, " +
|
|
|
+ "dimension_code VARCHAR(50) NOT NULL UNIQUE COMMENT '维度代码: body/mind/wisdom/action/wealth', " +
|
|
|
+ "dimension_name VARCHAR(100) NOT NULL COMMENT '维度名称', " +
|
|
|
+ "dimension_type VARCHAR(50) NOT NULL DEFAULT 'five' COMMENT '类型: five(五维)/action(行动)/other', " +
|
|
|
+ "parent_id BIGINT COMMENT '父级ID,无父级为NULL', " +
|
|
|
+ "sort INT DEFAULT 0 COMMENT '排序', " +
|
|
|
+ "enabled TINYINT(1) DEFAULT 1 COMMENT '启用: 0禁用 1启用', " +
|
|
|
+ "dify_dataset_id VARCHAR(100) COMMENT 'Dify Dataset ID,不填则不同步', " +
|
|
|
+ "remark VARCHAR(500) COMMENT '备注', " +
|
|
|
+ "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
+ "updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP " +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='维度配置表'");
|
|
|
+ log.info("已创建product_dimension_config表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建product_dimension_config表可能已存在: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 创建知识库表
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS dan_knowledge_base (" +
|
|
|
+ "id BIGINT PRIMARY KEY AUTO_INCREMENT, " +
|
|
|
+ "title VARCHAR(200) NOT NULL COMMENT '知识标题', " +
|
|
|
+ "content TEXT NOT NULL COMMENT '知识内容', " +
|
|
|
+ "sort INT DEFAULT 0 COMMENT '排序', " +
|
|
|
+ "status TINYINT(1) DEFAULT 1 COMMENT '状态: 0禁用 1启用', " +
|
|
|
+ "remark VARCHAR(500) COMMENT '备注', " +
|
|
|
+ "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
+ "updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP " +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='知识库表'");
|
|
|
+ log.info("已创建dan_knowledge_base表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建dan_knowledge_base表可能已存在: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 创建标签表
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS dan_knowledge_tag (" +
|
|
|
+ "id BIGINT PRIMARY KEY AUTO_INCREMENT, " +
|
|
|
+ "tag_name VARCHAR(100) NOT NULL UNIQUE COMMENT '标签名', " +
|
|
|
+ "tag_type VARCHAR(50) DEFAULT 'custom' COMMENT '标签类型: custom/auto', " +
|
|
|
+ "created_at DATETIME DEFAULT CURRENT_TIMESTAMP " +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='标签表'");
|
|
|
+ log.info("已创建dan_knowledge_tag表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建dan_knowledge_tag表可能已存在: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 创建知识-维度关联表
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS dan_knowledge_base_dimension (" +
|
|
|
+ "id BIGINT PRIMARY KEY AUTO_INCREMENT, " +
|
|
|
+ "knowledge_id BIGINT NOT NULL COMMENT '知识库ID', " +
|
|
|
+ "dimension_id BIGINT NOT NULL COMMENT '维度配置ID', " +
|
|
|
+ "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
+ "UNIQUE KEY uk_knowledge_dimension (knowledge_id, dimension_id) " +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='知识-维度关联表'");
|
|
|
+ log.info("已创建dan_knowledge_base_dimension表");
|
|
|
+
|
|
|
+ // 添加外键约束(单独执行避免CREATE TABLE时依赖表不存在)
|
|
|
+ jdbcTemplate.execute("ALTER TABLE dan_knowledge_base_dimension ADD CONSTRAINT fk_kd_knowledge FOREIGN KEY (knowledge_id) REFERENCES dan_knowledge_base(id) ON DELETE CASCADE");
|
|
|
+ jdbcTemplate.execute("ALTER TABLE dan_knowledge_base_dimension ADD CONSTRAINT fk_kd_dimension FOREIGN KEY (dimension_id) REFERENCES product_dimension_config(id) ON DELETE CASCADE");
|
|
|
+ log.info("已添加dan_knowledge_base_dimension外键约束");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建dan_knowledge_base_dimension表可能已存在: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 创建知识-标签关联表
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS dan_knowledge_base_tag (" +
|
|
|
+ "id BIGINT PRIMARY KEY AUTO_INCREMENT, " +
|
|
|
+ "knowledge_id BIGINT NOT NULL, " +
|
|
|
+ "tag_id BIGINT NOT NULL, " +
|
|
|
+ "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
+ "UNIQUE KEY uk_knowledge_tag (knowledge_id, tag_id) " +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='知识-标签关联表'");
|
|
|
+ log.info("已创建dan_knowledge_base_tag表");
|
|
|
+
|
|
|
+ // 添加外键约束
|
|
|
+ jdbcTemplate.execute("ALTER TABLE dan_knowledge_base_tag ADD CONSTRAINT fk_kt_knowledge FOREIGN KEY (knowledge_id) REFERENCES dan_knowledge_base(id) ON DELETE CASCADE");
|
|
|
+ jdbcTemplate.execute("ALTER TABLE dan_knowledge_base_tag ADD CONSTRAINT fk_kt_tag FOREIGN KEY (tag_id) REFERENCES dan_knowledge_tag(id) ON DELETE CASCADE");
|
|
|
+ log.info("已添加dan_knowledge_base_tag外键约束");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建dan_knowledge_base_tag表可能已存在: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 迁移32: products表添加配送/类目/销售字段(商品编辑表单对齐)
|
|
|
+ ensureColumn("products", "delivery_method", "TINYINT DEFAULT 1 COMMENT '配送方式: 1=快递 2=自提 3=两者皆可'");
|
|
|
+ ensureColumn("products", "category_id", "BIGINT COMMENT '商品类目ID'");
|
|
|
+ ensureColumn("products", "distribution_system_id", "BIGINT COMMENT '销售体系ID'");
|
|
|
+
|
|
|
+ // 迁移33: 创建供应商销售体系表 + product_orders扩展字段
|
|
|
+ ensureColumn("product_orders", "supply_system_id", "BIGINT COMMENT '所属供应商体系ID'");
|
|
|
+ ensureColumn("product_orders", "supplier_id", "BIGINT COMMENT '收款供应商用户ID'");
|
|
|
+ ensureColumn("product_orders", "supply_commission", "INT COMMENT '销售佣金(分)'");
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE product_orders ADD KEY idx_supply_system_id (supply_system_id)");
|
|
|
+ log.info("已添加idx_supply_system_id索引到product_orders表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("添加idx_supply_system_id索引可能已存在: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS supply_system (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
+ "name VARCHAR(100) NOT NULL COMMENT '体系名称', " +
|
|
|
+ "admin_id BIGINT NOT NULL COMMENT '管理员用户ID', " +
|
|
|
+ "settlement_period_days INT NOT NULL DEFAULT 30 COMMENT '账期(天)', " +
|
|
|
+ "platform_profit_rate DECIMAL(5,2) NOT NULL DEFAULT 0 COMMENT '平台留利比例(%)', " +
|
|
|
+ "description VARCHAR(500) COMMENT '描述', " +
|
|
|
+ "status VARCHAR(20) NOT NULL DEFAULT 'active' COMMENT '状态: active/disabled', " +
|
|
|
+ "created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
+ "updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, " +
|
|
|
+ "KEY idx_admin_id (admin_id)" +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='供应商体系'");
|
|
|
+ log.info("已创建supply_system表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建supply_system表可能已存在: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS supply_system_member (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
+ "system_id BIGINT NOT NULL COMMENT '体系ID', " +
|
|
|
+ "user_id BIGINT NOT NULL COMMENT '用户ID', " +
|
|
|
+ "role VARCHAR(20) NOT NULL DEFAULT 'member' COMMENT '角色: admin/member', " +
|
|
|
+ "status VARCHAR(20) NOT NULL DEFAULT 'active' COMMENT '状态: active/inactive', " +
|
|
|
+ "joined_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
+ "UNIQUE KEY uk_system_user (system_id, user_id), " +
|
|
|
+ "KEY idx_user_id (user_id)" +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='体系成员'");
|
|
|
+ log.info("已创建supply_system_member表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建supply_system_member表可能已存在: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS supply_settlement (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
+ "system_id BIGINT NOT NULL COMMENT '体系ID', " +
|
|
|
+ "period_start DATE NOT NULL COMMENT '结算周期开始', " +
|
|
|
+ "period_end DATE NOT NULL COMMENT '结算周期结束', " +
|
|
|
+ "total_sales INT NOT NULL DEFAULT 0 COMMENT '销售总额(分)', " +
|
|
|
+ "platform_profit INT NOT NULL DEFAULT 0 COMMENT '平台留利金额(分)', " +
|
|
|
+ "supplier_payout INT NOT NULL DEFAULT 0 COMMENT '供应商实付(分)', " +
|
|
|
+ "status VARCHAR(20) NOT NULL DEFAULT 'pending' COMMENT '状态: pending/settled', " +
|
|
|
+ "settled_at DATETIME COMMENT '结算时间', " +
|
|
|
+ "remark VARCHAR(500) COMMENT '备注', " +
|
|
|
+ "created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
+ "KEY idx_system_id (system_id), " +
|
|
|
+ "KEY idx_period (period_start, period_end)" +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='结算记录'");
|
|
|
+ log.info("已创建supply_settlement表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建supply_settlement表可能已存在: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS supply_settlement_detail (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
+ "settlement_id BIGINT NOT NULL COMMENT '结算ID', " +
|
|
|
+ "order_id BIGINT NOT NULL COMMENT '订单ID', " +
|
|
|
+ "order_no VARCHAR(64) NOT NULL COMMENT '订单号', " +
|
|
|
+ "product_name VARCHAR(200) NOT NULL COMMENT '商品名称', " +
|
|
|
+ "amount INT NOT NULL COMMENT '订单金额(分)', " +
|
|
|
+ "platform_fee INT NOT NULL COMMENT '平台留利(分)', " +
|
|
|
+ "supplier_id BIGINT COMMENT '收款供应商用户ID', " +
|
|
|
+ "supplier_name VARCHAR(100) COMMENT '收款供应商名称', " +
|
|
|
+ "payout INT NOT NULL COMMENT '供应商实付(分)', " +
|
|
|
+ "created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
+ "KEY idx_settlement_id (settlement_id), " +
|
|
|
+ "KEY idx_order_id (order_id)" +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='结算明细'");
|
|
|
+ log.info("已创建supply_settlement_detail表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建supply_settlement_detail表可能已存在: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 迁移: growth_records表添加来源类型/去重/补充字段
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE growth_records ADD COLUMN source_type VARCHAR(20) DEFAULT 'manual' COMMENT '来源类型: manual/platform_purchase/self_upload/supplement'");
|
|
|
+ log.info("已添加source_type列到growth_records表");
|
|
|
+ } catch (Exception e) {}
|
|
|
+
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE growth_records ADD COLUMN source_file_url VARCHAR(500) COMMENT '原始报告文件URL'");
|
|
|
+ log.info("已添加source_file_url列到growth_records表");
|
|
|
+ } catch (Exception e) {}
|
|
|
+
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE growth_records ADD COLUMN order_id BIGINT COMMENT '关联订单ID'");
|
|
|
+ log.info("已添加order_id列到growth_records表");
|
|
|
+ } catch (Exception e) {}
|
|
|
+
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE growth_records ADD COLUMN report_date DATETIME COMMENT '测评/报告日期'");
|
|
|
+ log.info("已添加report_date列到growth_records表");
|
|
|
+ } catch (Exception e) {}
|
|
|
+
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE growth_records ADD COLUMN parent_record_id BIGINT COMMENT '关联的原始档案ID(补充档案专用)'");
|
|
|
+ log.info("已添加parent_record_id列到growth_records表");
|
|
|
+ } catch (Exception e) {}
|
|
|
+
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE growth_records ADD COLUMN is_latest TINYINT(1) DEFAULT 1 COMMENT '是否同组最新记录'");
|
|
|
+ log.info("已添加is_latest列到growth_records表");
|
|
|
+ } catch (Exception e) {}
|
|
|
+
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE growth_records ADD COLUMN dedup_hash VARCHAR(64) COMMENT '去重哈希值(SHA256)'");
|
|
|
+ log.info("已添加dedup_hash列到growth_records表");
|
|
|
+ } catch (Exception e) {}
|
|
|
+
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE growth_records ADD COLUMN parsed_data TEXT COMMENT '上传报告的解析结果(JSON)'");
|
|
|
+ log.info("已添加parsed_data列到growth_records表");
|
|
|
+ } catch (Exception e) {}
|
|
|
+
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE UNIQUE INDEX IF NOT EXISTS uk_growth_dedup_hash ON growth_records(dedup_hash)");
|
|
|
+ log.info("已添加uk_growth_dedup_hash唯一索引到growth_records表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE growth_records ADD UNIQUE INDEX uk_growth_dedup_hash (dedup_hash)");
|
|
|
+ log.info("已通过ALTER TABLE添加uk_growth_dedup_hash唯一索引");
|
|
|
+ } catch (Exception e2) {
|
|
|
+ log.warn("添加uk_growth_dedup_hash索引可能已存在: {}", e2.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 迁移: foods表添加营养字段
|
|
|
+ ensureColumn("foods", "energy_kj", "DECIMAL(8,2) COMMENT '能量(kJ/100g)'");
|
|
|
+ ensureColumn("foods", "starch", "DECIMAL(8,2) COMMENT '淀粉(g/100g)'");
|
|
|
+ ensureColumn("foods", "cholesterol", "DECIMAL(8,2) COMMENT '胆固醇(mg/100g)'");
|
|
|
+
|
|
|
+ // 初始化成长任务种子数据
|
|
|
+ try {
|
|
|
+ Integer count = jdbcTemplate.queryForObject(
|
|
|
+ "SELECT COUNT(*) FROM growth_task", Integer.class);
|
|
|
+
|
|
|
+ if (count == null || count == 0) {
|
|
|
+ jdbcTemplate.execute("INSERT INTO growth_task (type, title, description, reward_points, reward_energy, target_value, task_key, enabled, sort_order, created_at) VALUES " +
|
|
|
+ "('DAILY', '每日签到', '完成健康打卡签到', 20, 3, 1, 'DAILY_CHECKIN', 1, 1, NOW()), " +
|
|
|
+ "('DAILY', '完成任务', '完成一个任务', 30, 5, 1, 'DAILY_TASK', 1, 2, NOW()), " +
|
|
|
+ "('DAILY', '每日分享', '分享小程序给好友', 10, 2, 1, 'DAILY_SHARE', 1, 3, NOW()), " +
|
|
|
+ "('DAILY', 'AI对话', '与AI助手对话一次', 15, 3, 1, 'DAILY_AI', 1, 4, NOW())");
|
|
|
+ log.info("成长任务种子数据已初始化");
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("初始化成长任务种子数据失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 迁移34: dan_assessment_results表添加家庭成员/EMI心理/大五人格/孩子快照字段
|
|
|
+ ensureColumn("dan_assessment_results", "family_member_id", "BIGINT COMMENT '家庭成员ID'");
|
|
|
+ ensureColumn("dan_assessment_results", "family_member_name", "VARCHAR(50) COMMENT '成员姓名快照'");
|
|
|
+ ensureColumn("dan_assessment_results", "family_member_birthday", "DATE COMMENT '成员出生日期快照'");
|
|
|
+ ensureColumn("dan_assessment_results", "family_member_gender", "VARCHAR(10) COMMENT '成员性别快照'");
|
|
|
+ ensureColumn("dan_assessment_results", "family_member_age", "INT COMMENT '成员年龄快照'");
|
|
|
+ ensureColumn("dan_assessment_results", "emotion_score", "INT COMMENT '情绪商数(EMI)'");
|
|
|
+ ensureColumn("dan_assessment_results", "resilience_score", "INT COMMENT '心理韧性(EMI)'");
|
|
|
+ ensureColumn("dan_assessment_results", "stress_coping_score", "INT COMMENT '压力应对(EMI)'");
|
|
|
+ ensureColumn("dan_assessment_results", "self_awareness_score", "INT COMMENT '自我认知(EMI)'");
|
|
|
+ ensureColumn("dan_assessment_results", "openness_score", "INT COMMENT '开放性(大五人格)'");
|
|
|
+ ensureColumn("dan_assessment_results", "conscientiousness_score", "INT COMMENT '尽责性(大五人格)'");
|
|
|
+ ensureColumn("dan_assessment_results", "extraversion_score", "INT COMMENT '外向性(大五人格)'");
|
|
|
+ ensureColumn("dan_assessment_results", "agreeableness_score", "INT COMMENT '宜人性(大五人格)'");
|
|
|
+ ensureColumn("dan_assessment_results", "neuroticism_score", "INT COMMENT '神经质(大五人格)'");
|
|
|
+ ensureColumn("dan_assessment_results", "big_five_overall_score", "INT COMMENT '大五人格综合分'");
|
|
|
+ ensureColumn("dan_assessment_results", "child_name", "VARCHAR(50) COMMENT '孩子姓名快照'");
|
|
|
+ ensureColumn("dan_assessment_results", "child_birthday", "DATE COMMENT '出生日期快照'");
|
|
|
+ ensureColumn("dan_assessment_results", "child_gender", "VARCHAR(10) COMMENT '性别快照'");
|
|
|
+ ensureColumn("dan_assessment_results", "child_age", "INT COMMENT '年龄快照'");
|
|
|
+ ensureColumn("dan_assessment_results", "school", "VARCHAR(100) COMMENT '学校快照'");
|
|
|
+ ensureColumn("dan_assessment_results", "grade", "VARCHAR(20) COMMENT '年级快照'");
|
|
|
+ ensureColumn("dan_assessment_results", "grade_class", "VARCHAR(20) COMMENT '班级快照'");
|
|
|
+ ensureColumn("dan_assessment_results", "height", "INT COMMENT '身高(CM)快照'");
|
|
|
+ ensureColumn("dan_assessment_results", "weight", "INT COMMENT '体重(KG)快照'");
|
|
|
+ ensureColumn("dan_assessment_results", "family_status", "VARCHAR(50) COMMENT '家庭状况快照'");
|
|
|
+ ensureColumn("dan_assessment_results", "parent_degree", "VARCHAR(50) COMMENT '父母最高学历快照'");
|
|
|
+ ensureColumn("dan_assessment_results", "nation", "VARCHAR(20) COMMENT '民族快照'");
|
|
|
+ ensureColumn("dan_assessment_results", "province", "VARCHAR(50) COMMENT '省快照'");
|
|
|
+ ensureColumn("dan_assessment_results", "city", "VARCHAR(50) COMMENT '市快照'");
|
|
|
+ ensureColumn("dan_assessment_results", "district", "VARCHAR(50) COMMENT '区快照'");
|
|
|
+ ensureColumn("dan_assessment_results", "address", "VARCHAR(200) COMMENT '详细地址快照'");
|
|
|
+ ensureColumn("dan_assessment_results", "single_child", "VARCHAR(10) COMMENT '是否独生子女快照'");
|
|
|
+ ensureColumn("dan_assessment_results", "school_score", "VARCHAR(20) COMMENT '在校成绩快照'");
|
|
|
+ ensureColumn("dan_assessment_results", "urgent_name", "VARCHAR(50) COMMENT '紧急联系人姓名'");
|
|
|
+ ensureColumn("dan_assessment_results", "urgent_phone", "VARCHAR(20) COMMENT '紧急联系人电话'");
|
|
|
+ ensureColumn("dan_assessment_results", "pics", "TEXT COMMENT '图片IDs'");
|
|
|
+ ensureColumn("dan_assessment_results", "videos", "TEXT COMMENT '视频IDs'");
|
|
|
+ ensureColumn("dan_assessment_results", "audio", "VARCHAR(100) COMMENT '音频ID'");
|
|
|
+ ensureColumn("dan_assessment_results", "audio_url", "VARCHAR(255) COMMENT '音频URL'");
|
|
|
+ ensureColumn("dan_assessment_results", "process_desc", "TEXT COMMENT '过程描述'");
|
|
|
+ ensureColumn("dan_assessment_results", "result_desc", "TEXT COMMENT '结果简评'");
|
|
|
+
|
|
|
+ // 迁移35: points_log表添加family_member_id列(EnergyService.calcChildWealth家庭能量计算需要)
|
|
|
+ ensureColumn("points_log", "family_member_id", "BIGINT COMMENT '家庭成员ID'");
|
|
|
+
|
|
|
+ // 迁移36: family_members表添加generation + is_spouse列(辈分重构)
|
|
|
+ ensureColumn("family_members", "generation", "INT COMMENT '辈分值(0=家庭创建者,+n=向上n代,-n=向下n代)'");
|
|
|
+ ensureColumn("family_members", "is_spouse", "TINYINT(1) DEFAULT 0 COMMENT '是否配偶(同辈)'");
|
|
|
+
|
|
|
+ // 迁移37: 旧数据generation赋值(基于relationship_type映射)
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("UPDATE family_members SET generation = 0, is_spouse = 1 WHERE relationship_type = 'spouse'");
|
|
|
+ jdbcTemplate.execute("UPDATE family_members SET generation = 1 WHERE relationship_type = 'parent'");
|
|
|
+ jdbcTemplate.execute("UPDATE family_members SET generation = -1 WHERE relationship_type IN ('child')");
|
|
|
+ jdbcTemplate.execute("UPDATE family_members SET generation = 0 WHERE relationship_type = 'sibling'");
|
|
|
+ jdbcTemplate.execute("UPDATE family_members SET generation = 0 WHERE generation IS NULL AND relationship_type IS NOT NULL");
|
|
|
+ log.info("已迁移family_members.generation数据");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("迁移family_members.generation数据失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 迁移38: product_orders表添加points_used/points_cost/money_amount列(混合支付支持)
|
|
|
+ ensureColumn("product_orders", "points_used", "INT DEFAULT 0 COMMENT '使用的积分数量'");
|
|
|
+ ensureColumn("product_orders", "points_cost", "INT DEFAULT 0 COMMENT '积分抵扣金额(分)'");
|
|
|
+ ensureColumn("product_orders", "money_amount", "INT DEFAULT 0 COMMENT '现金支付金额(分)'");
|
|
|
+
|
|
|
+ // 迁移39: 创建供应商体系相关表
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS supply_system (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
+ "name VARCHAR(100) NOT NULL COMMENT '体系名称', " +
|
|
|
+ "admin_id BIGINT COMMENT '管理员用户ID', " +
|
|
|
+ "settlement_period_days INT DEFAULT 30 COMMENT '账期天数', " +
|
|
|
+ "platform_profit_rate DECIMAL(10,2) DEFAULT 0.00 COMMENT '平台留利比例(%)', " +
|
|
|
+ "description VARCHAR(500) COMMENT '描述', " +
|
|
|
+ "status VARCHAR(20) DEFAULT 'active' COMMENT '状态 active/disabled', " +
|
|
|
+ "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
+ "updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, " +
|
|
|
+ "INDEX idx_admin (admin_id), " +
|
|
|
+ "INDEX idx_status (status)" +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='供应商体系'");
|
|
|
+ log.info("已创建supply_system表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建supply_system表失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS supply_system_member (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
+ "system_id BIGINT NOT NULL COMMENT '体系ID', " +
|
|
|
+ "user_id BIGINT NOT NULL COMMENT '用户ID', " +
|
|
|
+ "role VARCHAR(20) DEFAULT 'member' COMMENT '角色 admin/member', " +
|
|
|
+ "status VARCHAR(20) DEFAULT 'active' COMMENT '状态 active/disabled', " +
|
|
|
+ "joined_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
+ "INDEX idx_system (system_id), " +
|
|
|
+ "INDEX idx_user (user_id), " +
|
|
|
+ "UNIQUE INDEX idx_system_user (system_id, user_id)" +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='供应商体系成员'");
|
|
|
+ log.info("已创建supply_system_member表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建supply_system_member表失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS supply_settlement (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
+ "system_id BIGINT NOT NULL COMMENT '体系ID', " +
|
|
|
+ "period_start DATE COMMENT '结算周期开始', " +
|
|
|
+ "period_end DATE COMMENT '结算周期结束', " +
|
|
|
+ "total_sales INT DEFAULT 0 COMMENT '销售总额(分)', " +
|
|
|
+ "platform_profit INT DEFAULT 0 COMMENT '平台留利(分)', " +
|
|
|
+ "supplier_payout INT DEFAULT 0 COMMENT '供应商实付(分)', " +
|
|
|
+ "status VARCHAR(20) DEFAULT 'pending' COMMENT '状态 pending/settled', " +
|
|
|
+ "settled_at DATETIME COMMENT '结算时间', " +
|
|
|
+ "remark VARCHAR(500) COMMENT '备注', " +
|
|
|
+ "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
+ "INDEX idx_system (system_id), " +
|
|
|
+ "INDEX idx_status (status), " +
|
|
|
+ "INDEX idx_period (period_start, period_end)" +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='供应商结算单'");
|
|
|
+ log.info("已创建supply_settlement表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建supply_settlement表失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS supply_settlement_detail (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
+ "settlement_id BIGINT NOT NULL COMMENT '结算单ID', " +
|
|
|
+ "order_id BIGINT COMMENT '订单ID', " +
|
|
|
+ "order_no VARCHAR(64) COMMENT '订单号', " +
|
|
|
+ "product_name VARCHAR(200) COMMENT '商品名称', " +
|
|
|
+ "amount INT DEFAULT 0 COMMENT '订单金额(分)', " +
|
|
|
+ "platform_fee INT DEFAULT 0 COMMENT '平台留利(分)', " +
|
|
|
+ "supplier_id BIGINT COMMENT '收款供应商ID', " +
|
|
|
+ "supplier_name VARCHAR(100) COMMENT '收款供应商名称', " +
|
|
|
+ "payout INT DEFAULT 0 COMMENT '实付(分)', " +
|
|
|
+ "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
+ "INDEX idx_settlement (settlement_id), " +
|
|
|
+ "INDEX idx_order (order_id)" +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='供应商结算明细'");
|
|
|
+ log.info("已创建supply_settlement_detail表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建supply_settlement_detail表失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 迁移40: 添加supplier_admin角色到users.role ENUM
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE users MODIFY COLUMN role ENUM('parent','child','teacher','supplier_admin') NOT NULL DEFAULT 'parent' COMMENT '当前活跃角色'");
|
|
|
+ log.info("已添加supplier_admin到users.role ENUM");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("添加supplier_admin到users.role ENUM失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 迁移40: users表添加mascot字段(AI助手形象: xibao浠宝/fubao福宝)
|
|
|
+ ensureColumn("users", "mascot", "VARCHAR(20) DEFAULT NULL COMMENT 'AI助手形象: xibao(浠宝)/fubao(福宝)'");
|
|
|
+
|
|
|
+ // 迁移41: 创建family_relationships表(家庭成员双向关系,替代relationship_type/role_override)
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS family_relationships (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
+ "family_id BIGINT NOT NULL COMMENT '所属家庭ID', " +
|
|
|
+ "from_member_id BIGINT NOT NULL COMMENT '关系起点成员ID', " +
|
|
|
+ "to_member_id BIGINT NOT NULL COMMENT '关系终点成员ID', " +
|
|
|
+ "generation_value INT NOT NULL COMMENT '辈分值: -1=子女, 0=同辈, 1=父母, 2=祖父母, 3=曾祖父母', " +
|
|
|
+ "relationship_key VARCHAR(50) COMMENT '关系标签key(冗余用于显示)', " +
|
|
|
+ "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
+ "updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, " +
|
|
|
+ "UNIQUE KEY uk_from_to (from_member_id, to_member_id), " +
|
|
|
+ "INDEX idx_family_id (family_id), " +
|
|
|
+ "INDEX idx_from (from_member_id), " +
|
|
|
+ "INDEX idx_to (to_member_id)" +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='家庭成员双向关系表'");
|
|
|
+ log.info("已创建family_relationships表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建family_relationships表可能已存在: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 迁移42: 创建butler_sessions表(AI管家会话管理)
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS butler_sessions (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
+ "user_id BIGINT NOT NULL COMMENT '用户ID', " +
|
|
|
+ "title VARCHAR(200) DEFAULT '新会话' COMMENT '会话标题', " +
|
|
|
+ "conversation_id VARCHAR(100) COMMENT 'Dify对话ID', " +
|
|
|
+ "context TEXT COMMENT '上下文信息(JSON)', " +
|
|
|
+ "status VARCHAR(20) DEFAULT 'active' COMMENT '状态: active(进行中)/archived(已归档)', " +
|
|
|
+ "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
+ "updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, " +
|
|
|
+ "INDEX idx_user_id (user_id), " +
|
|
|
+ "INDEX idx_status (status), " +
|
|
|
+ "INDEX idx_user_status (user_id, status)" +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='AI管家会话表'");
|
|
|
+ log.info("已创建butler_sessions表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建butler_sessions表可能已存在: {}", e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void insertSysConfigSeed(String key, String value, String desc) {
|
|
|
+ Integer count = jdbcTemplate.queryForObject(
|
|
|
+ "SELECT COUNT(*) FROM sys_config WHERE config_key = ?", Integer.class, key);
|
|
|
+ if (count == null || count == 0) {
|
|
|
+ jdbcTemplate.update(
|
|
|
+ "INSERT INTO sys_config (config_key, config_value, description, updated_at) VALUES (?, ?, ?, NOW())",
|
|
|
+ key, value, desc);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void initFoodTables() {
|
|
|
+ log.info("开始初始化食品相关表...");
|
|
|
+
|
|
|
+ // 4张新表 (CREATE TABLE IF NOT EXISTS)
|
|
|
+ String[] ddl = {
|
|
|
+ "CREATE TABLE IF NOT EXISTS foods ("
|
|
|
+ + "id BIGINT AUTO_INCREMENT PRIMARY KEY, "
|
|
|
+ + "name VARCHAR(100) NOT NULL COMMENT '食材名称', "
|
|
|
+ + "category VARCHAR(50) COMMENT '分类: 蔬菜/水果/肉禽/水产/蛋奶/豆类/谷物/调味/其他', "
|
|
|
+ + "unit VARCHAR(20) DEFAULT 'g' COMMENT '计量单位', "
|
|
|
+ + "calories DECIMAL(8,2) COMMENT '热量(kcal/100g)', "
|
|
|
+ + "protein DECIMAL(8,2) COMMENT '蛋白质(g/100g)', "
|
|
|
+ + "fat DECIMAL(8,2) COMMENT '脂肪(g/100g)', "
|
|
|
+ + "carbs DECIMAL(8,2) COMMENT '碳水(g/100g)', "
|
|
|
+ + "fiber DECIMAL(8,2) COMMENT '膳食纤维(g/100g)', "
|
|
|
+ + "price_level TINYINT COMMENT '价格等级 1-5', "
|
|
|
+ + "nutrition_tags VARCHAR(500) COMMENT '营养标签JSON', "
|
|
|
+ + "suitable_for VARCHAR(200) COMMENT '适宜人群JSON', "
|
|
|
+ + "allergens VARCHAR(200) COMMENT '过敏原JSON', "
|
|
|
+ + "external_source VARCHAR(100) COMMENT '数据来源', "
|
|
|
+ + "external_id VARCHAR(100) COMMENT '外部ID', "
|
|
|
+ + "cover_image VARCHAR(500) COMMENT '食材图片', "
|
|
|
+ + "status VARCHAR(20) DEFAULT 'active', "
|
|
|
+ + "sort_order INT DEFAULT 0, "
|
|
|
+ + "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, "
|
|
|
+ + "updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, "
|
|
|
+ + "INDEX idx_category (category), "
|
|
|
+ + "INDEX idx_price (price_level)"
|
|
|
+ + ");",
|
|
|
+
|
|
|
+ "CREATE TABLE IF NOT EXISTS recipes ("
|
|
|
+ + "id BIGINT AUTO_INCREMENT PRIMARY KEY, "
|
|
|
+ + "name VARCHAR(200) NOT NULL COMMENT '食谱名称', "
|
|
|
+ + "meal_type VARCHAR(20) COMMENT '早餐/午餐/晚餐/加餐', "
|
|
|
+ + "description TEXT COMMENT '简介', "
|
|
|
+ + "ingredients TEXT NOT NULL COMMENT '食材清单JSON', "
|
|
|
+ + "steps TEXT COMMENT '烹饪步骤JSON', "
|
|
|
+ + "cook_time INT COMMENT '烹饪时间(分钟)', "
|
|
|
+ + "difficulty VARCHAR(20) COMMENT '难度: 简单/中等/困难', "
|
|
|
+ + "total_calories DECIMAL(8,2) COMMENT '总热量', "
|
|
|
+ + "total_protein DECIMAL(8,2) COMMENT '总蛋白质', "
|
|
|
+ + "total_fat DECIMAL(8,2) COMMENT '总脂肪', "
|
|
|
+ + "total_carbs DECIMAL(8,2) COMMENT '总碳水', "
|
|
|
+ + "total_fiber DECIMAL(8,2) COMMENT '总膳食纤维', "
|
|
|
+ + "suitable_for VARCHAR(200) COMMENT '适宜场景', "
|
|
|
+ + "nutrition_tags VARCHAR(500) COMMENT '营养标签', "
|
|
|
+ + "external_source VARCHAR(100), "
|
|
|
+ + "external_id VARCHAR(100), "
|
|
|
+ + "cover_image VARCHAR(500), "
|
|
|
+ + "status VARCHAR(20) DEFAULT 'active', "
|
|
|
+ + "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, "
|
|
|
+ + "updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, "
|
|
|
+ + "INDEX idx_meal_type (meal_type), "
|
|
|
+ + "INDEX idx_cook_time (cook_time)"
|
|
|
+ + ");",
|
|
|
+
|
|
|
+ "CREATE TABLE IF NOT EXISTS seasonal_foods ("
|
|
|
+ + "id BIGINT AUTO_INCREMENT PRIMARY KEY, "
|
|
|
+ + "food_id BIGINT NOT NULL COMMENT '关联foods.id', "
|
|
|
+ + "month TINYINT NOT NULL COMMENT '月份 1-12', "
|
|
|
+ + "region VARCHAR(50) DEFAULT '全国' COMMENT '产地', "
|
|
|
+ + "peak_flag TINYINT DEFAULT 0 COMMENT '盛产标志', "
|
|
|
+ + "sort_order INT DEFAULT 0, "
|
|
|
+ + "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, "
|
|
|
+ + "INDEX idx_food_month (food_id, month), "
|
|
|
+ + "INDEX idx_month (month)"
|
|
|
+ + ");",
|
|
|
+
|
|
|
+ "CREATE TABLE IF NOT EXISTS meal_logs ("
|
|
|
+ + "id BIGINT AUTO_INCREMENT PRIMARY KEY, "
|
|
|
+ + "user_id BIGINT NOT NULL COMMENT '用户ID', "
|
|
|
+ + "child_id BIGINT COMMENT '孩子ID', "
|
|
|
+ + "meal_type VARCHAR(20) NOT NULL COMMENT '早餐/午餐/晚餐/加餐', "
|
|
|
+ + "meal_date DATE NOT NULL COMMENT '用餐日期', "
|
|
|
+ + "meal_time DATETIME COMMENT '具体时间', "
|
|
|
+ + "foods TEXT NOT NULL COMMENT '食物清单JSON', "
|
|
|
+ + "total_calories DECIMAL(8,2) COMMENT '总热量', "
|
|
|
+ + "total_protein DECIMAL(8,2) COMMENT '总蛋白质', "
|
|
|
+ + "total_fat DECIMAL(8,2) COMMENT '总脂肪', "
|
|
|
+ + "total_carbs DECIMAL(8,2) COMMENT '总碳水', "
|
|
|
+ + "total_fiber DECIMAL(8,2) COMMENT '总膳食纤维', "
|
|
|
+ + "note TEXT COMMENT '备注', "
|
|
|
+ + "images VARCHAR(1000) COMMENT '图片JSON', "
|
|
|
+ + "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, "
|
|
|
+ + "INDEX idx_user_date (user_id, meal_date), "
|
|
|
+ + "INDEX idx_child_date (child_id, meal_date)"
|
|
|
+ + ");",
|
|
|
+
|
|
|
+ "CREATE TABLE IF NOT EXISTS report_food_suitability ("
|
|
|
+ + "id BIGINT AUTO_INCREMENT PRIMARY KEY, "
|
|
|
+ + "report_id BIGINT NOT NULL COMMENT '健康报告ID', "
|
|
|
+ + "food_id BIGINT COMMENT '食材ID', "
|
|
|
+ + "food_name VARCHAR(100) NOT NULL COMMENT '食材名称', "
|
|
|
+ + "category VARCHAR(50) COMMENT '分类', "
|
|
|
+ + "score INT NOT NULL COMMENT '适宜度评分', "
|
|
|
+ + "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, "
|
|
|
+ + "INDEX idx_report (report_id), "
|
|
|
+ + "INDEX idx_food (food_id)"
|
|
|
+ + ");",
|
|
|
+
|
|
|
+ "CREATE TABLE IF NOT EXISTS user_food_preference ("
|
|
|
+ + "id BIGINT AUTO_INCREMENT PRIMARY KEY, "
|
|
|
+ + "user_id BIGINT NOT NULL COMMENT '用户ID', "
|
|
|
+ + "food_id BIGINT COMMENT '食材ID', "
|
|
|
+ + "food_name VARCHAR(100) NOT NULL COMMENT '食材名称', "
|
|
|
+ + "score INT NOT NULL DEFAULT 0 COMMENT '评分', "
|
|
|
+ + "reason VARCHAR(500) COMMENT '原因', "
|
|
|
+ + "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, "
|
|
|
+ + "updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, "
|
|
|
+ + "INDEX idx_user (user_id)"
|
|
|
+ + ");",
|
|
|
+
|
|
|
+ "CREATE TABLE IF NOT EXISTS food_nutrients ("
|
|
|
+ + "id BIGINT AUTO_INCREMENT PRIMARY KEY, "
|
|
|
+ + "food_id BIGINT NOT NULL COMMENT '食材ID', "
|
|
|
+ + "nutrient_code VARCHAR(20) NOT NULL COMMENT '营养素编码', "
|
|
|
+ + "amount_per_100g DECIMAL(10,4) NOT NULL COMMENT '每100g含量', "
|
|
|
+ + "unit VARCHAR(20) NOT NULL COMMENT '单位', "
|
|
|
+ + "source VARCHAR(50) DEFAULT 'importer' COMMENT '数据来源', "
|
|
|
+ + "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, "
|
|
|
+ + "updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, "
|
|
|
+ + "UNIQUE KEY uk_food_nutrient (food_id, nutrient_code), "
|
|
|
+ + "INDEX idx_nutrient (nutrient_code)"
|
|
|
+ + ");",
|
|
|
+
|
|
|
+ "CREATE TABLE IF NOT EXISTS rdi_standards ("
|
|
|
+ + "id BIGINT AUTO_INCREMENT PRIMARY KEY, "
|
|
|
+ + "nutrient_code VARCHAR(20) NOT NULL COMMENT '营养素编码', "
|
|
|
+ + "age_group VARCHAR(10) NOT NULL COMMENT '年龄段', "
|
|
|
+ + "gender VARCHAR(6) DEFAULT 'all' COMMENT '性别', "
|
|
|
+ + "rdi_type VARCHAR(10) NOT NULL COMMENT '参考类型', "
|
|
|
+ + "amount DECIMAL(10,4) NOT NULL COMMENT '推荐量', "
|
|
|
+ + "unit VARCHAR(20) NOT NULL COMMENT '单位', "
|
|
|
+ + "source VARCHAR(100) COMMENT '数据来源', "
|
|
|
+ + "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, "
|
|
|
+ + "UNIQUE KEY uk_rdi (nutrient_code, age_group, gender, rdi_type), "
|
|
|
+ + "INDEX idx_age (age_group)"
|
|
|
+ + ");",
|
|
|
+
|
|
|
+ "CREATE TABLE IF NOT EXISTS shopping_lists ("
|
|
|
+ + "id BIGINT AUTO_INCREMENT PRIMARY KEY, "
|
|
|
+ + "user_id BIGINT NOT NULL COMMENT '用户ID', "
|
|
|
+ + "child_id BIGINT COMMENT '孩子ID', "
|
|
|
+ + "report_id BIGINT COMMENT '健康报告ID', "
|
|
|
+ + "title VARCHAR(200) COMMENT '标题', "
|
|
|
+ + "source VARCHAR(20) NOT NULL COMMENT '来源', "
|
|
|
+ + "status VARCHAR(20) DEFAULT 'active' COMMENT '状态', "
|
|
|
+ + "total_items INT DEFAULT 0 COMMENT '总项数', "
|
|
|
+ + "purchased_items INT DEFAULT 0 COMMENT '已购项数', "
|
|
|
+ + "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, "
|
|
|
+ + "updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, "
|
|
|
+ + "INDEX idx_user (user_id), "
|
|
|
+ + "INDEX idx_report (report_id), "
|
|
|
+ + "INDEX idx_status (status)"
|
|
|
+ + ");",
|
|
|
+
|
|
|
+ "CREATE TABLE IF NOT EXISTS shopping_list_items ("
|
|
|
+ + "id BIGINT AUTO_INCREMENT PRIMARY KEY, "
|
|
|
+ + "list_id BIGINT NOT NULL COMMENT '购物清单ID', "
|
|
|
+ + "food_id BIGINT COMMENT '食材ID', "
|
|
|
+ + "food_name VARCHAR(100) NOT NULL COMMENT '食材名称', "
|
|
|
+ + "category VARCHAR(50) COMMENT '分类', "
|
|
|
+ + "quantity DECIMAL(8,2) DEFAULT 1 COMMENT '数量', "
|
|
|
+ + "unit VARCHAR(20) DEFAULT '份' COMMENT '单位', "
|
|
|
+ + "priority VARCHAR(10) DEFAULT 'medium' COMMENT '优先级', "
|
|
|
+ + "reason VARCHAR(200) COMMENT '推荐原因', "
|
|
|
+ + "score INT COMMENT '评分', "
|
|
|
+ + "source VARCHAR(20) COMMENT '来源', "
|
|
|
+ + "purchased TINYINT DEFAULT 0 COMMENT '是否已购', "
|
|
|
+ + "purchased_at DATETIME COMMENT '购买时间', "
|
|
|
+ + "sort_order INT DEFAULT 0 COMMENT '排序', "
|
|
|
+ + "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, "
|
|
|
+ + "INDEX idx_list (list_id), "
|
|
|
+ + "INDEX idx_purchased (purchased)"
|
|
|
+ + ");",
|
|
|
+
|
|
|
+ "CREATE TABLE IF NOT EXISTS follow_up ("
|
|
|
+ + "id BIGINT AUTO_INCREMENT PRIMARY KEY, "
|
|
|
+ + "family_id BIGINT NOT NULL COMMENT '家庭ID', "
|
|
|
+ + "child_id BIGINT COMMENT '孩子ID', "
|
|
|
+ + "teacher_id BIGINT COMMENT '规划师ID', "
|
|
|
+ + "deficiency_id BIGINT COMMENT '缺乏项ID', "
|
|
|
+ + "message_id BIGINT COMMENT '消息ID', "
|
|
|
+ + "status VARCHAR(20) DEFAULT 'pending' COMMENT '状态', "
|
|
|
+ + "priority VARCHAR(10) DEFAULT 'medium' COMMENT '优先级', "
|
|
|
+ + "description VARCHAR(500) COMMENT '描述', "
|
|
|
+ + "parent_feedback TEXT COMMENT '家长反馈', "
|
|
|
+ + "teacher_review VARCHAR(500) COMMENT '规划师点评', "
|
|
|
+ + "due_date DATETIME COMMENT '截止日期', "
|
|
|
+ + "completed_at DATETIME COMMENT '完成时间', "
|
|
|
+ + "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, "
|
|
|
+ + "updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, "
|
|
|
+ + "INDEX idx_family (family_id), "
|
|
|
+ + "INDEX idx_teacher (teacher_id), "
|
|
|
+ + "INDEX idx_status (status), "
|
|
|
+ + "INDEX idx_due (due_date)"
|
|
|
+ + ");"
|
|
|
+ };
|
|
|
+
|
|
|
+ for (String sql : ddl) {
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute(sql);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("食品表DDL执行失败(可能已存在): {}", e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 扩展 user_nutrition_profile(如果表不存在则跳过)
|
|
|
+ ensureColumn("user_nutrition_profile", "budget_monthly", "INT COMMENT '月食品预算(元)'");
|
|
|
+ ensureColumn("user_nutrition_profile", "family_taste_preferences", "TEXT COMMENT '家庭成员口味偏好JSON'");
|
|
|
+ ensureColumn("user_nutrition_profile", "cuisine_style", "VARCHAR(100) COMMENT '偏好菜系'");
|
|
|
+ ensureColumn("user_nutrition_profile", "meal_count", "INT DEFAULT 3 COMMENT '每日餐数'");
|
|
|
+ ensureColumn("user_nutrition_profile", "cooking_ability", "VARCHAR(20) DEFAULT 'simple' COMMENT '烹饪能力: simple/medium/advanced'");
|
|
|
+
|
|
|
+ log.info("食品相关表初始化完成");
|
|
|
+ }
|
|
|
+
|
|
|
+ private void ensureColumn(String table, String column, String definition) {
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE " + table + " ADD COLUMN " + column + " " + definition);
|
|
|
+ log.info("已添加列 {}.{}", table, column);
|
|
|
+ } catch (Exception e) {
|
|
|
+ if (e.getMessage() != null && e.getMessage().contains("Duplicate column")) {
|
|
|
+ log.info("列 {}.{} 已存在,跳过", table, column);
|
|
|
+ } else {
|
|
|
+ log.warn("添加列 {}.{} 失败: {}", table, column, e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void insertProductSeed(String name, String desc, String type,
|
|
|
+ Integer price,
|
|
|
+ String domain, String memberEligible) {
|
|
|
+ Integer count = jdbcTemplate.queryForObject(
|
|
|
+ "SELECT COUNT(*) FROM products WHERE name = ? AND product_type = ?", Integer.class, name, type);
|
|
|
+ if (count == null || count == 0) {
|
|
|
+ jdbcTemplate.update(
|
|
|
+ "INSERT INTO products (name, description, price, product_type, vendor_id, vendor_name, status, domain, member_eligible, created_at, updated_at) " +
|
|
|
+ "VALUES (?, ?, ?, ?, NULL, '平台官方', 'on_shelf', ?, ?, NOW(), NOW())",
|
|
|
+ name, desc, price, type, domain, memberEligible);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void insertContentSectionSeed(String pageKey, String sectionKey, String title,
|
|
|
+ String allowedRoles, int sortOrder) {
|
|
|
+ Integer count = jdbcTemplate.queryForObject(
|
|
|
+ "SELECT COUNT(*) FROM content_sections WHERE page_key = ? AND section_key = ?",
|
|
|
+ Integer.class, pageKey, sectionKey);
|
|
|
+ if (count == null || count == 0) {
|
|
|
+ jdbcTemplate.update(
|
|
|
+ "INSERT INTO content_sections (page_key, section_key, title, allowed_roles, sort_order, status, created_at, updated_at) " +
|
|
|
+ "VALUES (?, ?, ?, ?, ?, 'active', NOW(), NOW())",
|
|
|
+ pageKey, sectionKey, title, allowedRoles, sortOrder);
|
|
|
+ }
|
|
|
+// 迁移51: 创建 member_subscription 表(家庭订阅记录)
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS member_subscription (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
+ "family_id BIGINT NOT NULL COMMENT '家庭ID', " +
|
|
|
+ "`level` VARCHAR(10) NOT NULL DEFAULT 'L1' COMMENT 'L1=一生一世, L2=久久一生', " +
|
|
|
+ "status VARCHAR(20) NOT NULL DEFAULT 'active' COMMENT 'active/expired/cancelled', " +
|
|
|
+ "start_time DATETIME DEFAULT NULL COMMENT '开始时间', " +
|
|
|
+ "expire_time DATETIME DEFAULT NULL COMMENT '到期时间', " +
|
|
|
+ "payment_id BIGINT DEFAULT NULL COMMENT '关联订单ID', " +
|
|
|
+ "auto_renew TINYINT(1) DEFAULT 0 COMMENT '自动续费(L2)', " +
|
|
|
+ "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
+ "updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, " +
|
|
|
+ "INDEX idx_family_id (family_id), " +
|
|
|
+ "INDEX idx_status (status), " +
|
|
|
+ "INDEX idx_expire_time (expire_time)" +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='家庭订阅记录'");
|
|
|
+ log.info("已创建member_subscription表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建member_subscription表失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 迁移52: 创建 member_subscription_order 表(订阅订单)
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS member_subscription_order (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
+ "family_id BIGINT NOT NULL COMMENT '家庭ID', " +
|
|
|
+ "`level` VARCHAR(10) NOT NULL COMMENT 'L1/L2', " +
|
|
|
+ "amount INT NOT NULL COMMENT '金额(分)', " +
|
|
|
+ "status VARCHAR(20) NOT NULL DEFAULT 'pending' COMMENT 'pending/paid/cancelled/refunded', " +
|
|
|
+ "payment_type VARCHAR(20) DEFAULT NULL COMMENT '支付方式 wechat/alipay', " +
|
|
|
+ "transaction_id VARCHAR(64) DEFAULT NULL COMMENT '微信支付订单号', " +
|
|
|
+ "order_no VARCHAR(64) NOT NULL COMMENT '订单号', " +
|
|
|
+ "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
+ "paid_at DATETIME DEFAULT NULL, " +
|
|
|
+ "UNIQUE KEY uk_order_no (order_no), " +
|
|
|
+ "INDEX idx_family_id (family_id), " +
|
|
|
+ "INDEX idx_status (status)" +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订阅订单'");
|
|
|
+ log.info("已创建member_subscription_order表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建member_subscription_order表失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 迁移53: 创建 subscription_benefit_log 表(权益使用日志)
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS subscription_benefit_log (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
+ "subscription_id BIGINT NOT NULL COMMENT '订阅ID', " +
|
|
|
+ "benefit_code VARCHAR(50) NOT NULL COMMENT '权益代码', " +
|
|
|
+ "used_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
+ "detail TEXT COMMENT '使用详情(JSON)', " +
|
|
|
+ "INDEX idx_subscription_id (subscription_id)" +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='权益使用日志'");
|
|
|
+ log.info("已创建subscription_benefit_log表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建subscription_benefit_log表失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 迁移54: 创建 promotion_tier 表(推广等级 R0-R4)
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS promotion_tier (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
+ "user_id BIGINT NOT NULL COMMENT '用户ID', " +
|
|
|
+ "tier VARCHAR(20) NOT NULL DEFAULT 'R0' COMMENT 'R0利他者/R1传福人/R2聚能师/R3启慧者/R4传承者', " +
|
|
|
+ "team_size_1st INT DEFAULT 0 COMMENT '一层团队人数', " +
|
|
|
+ "team_size_2nd INT DEFAULT 0 COMMENT '二层团队人数', " +
|
|
|
+ "team_size_3rd INT DEFAULT 0 COMMENT '三层团队人数', " +
|
|
|
+ "total_team_size INT DEFAULT 0 COMMENT '三层累计总人数', " +
|
|
|
+ "total_referral_earnings INT DEFAULT 0 COMMENT '累计推荐佣金(分)', " +
|
|
|
+ "total_share_earnings INT DEFAULT 0 COMMENT '累计消费返利(分)', " +
|
|
|
+ "promoted_at DATETIME DEFAULT NULL COMMENT '最近晋级时间', " +
|
|
|
+ "last_change_at DATETIME DEFAULT NULL COMMENT '最后变更时间', " +
|
|
|
+ "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
+ "updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, " +
|
|
|
+ "UNIQUE KEY uk_user_id (user_id), " +
|
|
|
+ "INDEX idx_tier (tier)" +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='推广等级'");
|
|
|
+ log.info("已创建promotion_tier表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建promotion_tier表失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 迁移55: 创建 promotion_tier_change_log 表(推广等级变更记录)
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS promotion_tier_change_log (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
+ "user_id BIGINT NOT NULL COMMENT '用户ID', " +
|
|
|
+ "old_tier VARCHAR(20) DEFAULT NULL COMMENT '变更前等级', " +
|
|
|
+ "new_tier VARCHAR(20) NOT NULL COMMENT '变更后等级', " +
|
|
|
+ "reason VARCHAR(50) DEFAULT NULL COMMENT 'qualify升级/demote降级/admin管理员调整', " +
|
|
|
+ "changed_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
+ "INDEX idx_user_id (user_id)" +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='推广等级变更记录'");
|
|
|
+ log.info("已创建promotion_tier_change_log表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建promotion_tier_change_log表失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 迁移56: 创建 referral_tree 表(推荐关系树三层)
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS referral_tree (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
+ "parent_id BIGINT NOT NULL COMMENT '推荐人ID', " +
|
|
|
+ "child_id BIGINT NOT NULL COMMENT '被推荐人ID', " +
|
|
|
+ "level INT NOT NULL COMMENT '层级深度(1/2/3)', " +
|
|
|
+ "path VARCHAR(255) DEFAULT NULL COMMENT 'materialized path如/1/2/3/', " +
|
|
|
+ "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
+ "UNIQUE KEY uk_child_id (child_id), " +
|
|
|
+ "INDEX idx_parent_id (parent_id), " +
|
|
|
+ "INDEX idx_level (level)" +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='推荐关系树'");
|
|
|
+ log.info("已创建referral_tree表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建referral_tree表失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 迁移57: 创建 product_ppoint 表(产品P点配置)
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS product_ppoint (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
+ "product_id BIGINT NOT NULL COMMENT '产品ID', " +
|
|
|
+ "ppoint INT NOT NULL DEFAULT 0 COMMENT 'P点值(平台利润分)', " +
|
|
|
+ "start_date DATE DEFAULT NULL COMMENT '生效日期', " +
|
|
|
+ "end_date DATE DEFAULT NULL COMMENT '失效日期', " +
|
|
|
+ "created_by BIGINT DEFAULT NULL COMMENT '创建人', " +
|
|
|
+ "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
+ "INDEX idx_product_id (product_id), " +
|
|
|
+ "INDEX idx_dates (start_date, end_date)" +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='产品P点配置'");
|
|
|
+ log.info("已创建product_ppoint表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建product_ppoint表失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 迁移58: 创建 assessment_products 表(测评商品扩展信息)
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS assessment_products (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
+ "product_id BIGINT NOT NULL COMMENT '关联products表', " +
|
|
|
+ "assessment_type VARCHAR(20) NOT NULL COMMENT 'single=单包, bundle=套餐', " +
|
|
|
+ "total_sessions INT NOT NULL DEFAULT 1 COMMENT '包含测评次数', " +
|
|
|
+ "validity_days INT NOT NULL DEFAULT 365 COMMENT '有效期(天)', " +
|
|
|
+ "guide_scope VARCHAR(50) DEFAULT 'all' COMMENT '规划师范围: all/assigned/auto', " +
|
|
|
+ "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
+ "updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, " +
|
|
|
+ "INDEX idx_product_id (product_id)" +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='测评商品扩展信息'");
|
|
|
+ log.info("已创建assessment_products表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建assessment_products表失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 迁移59: 创建 assessment_quotas 表(测评额度)
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS assessment_quotas (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
+ "quota_no VARCHAR(32) NOT NULL COMMENT '额度编号', " +
|
|
|
+ "family_id BIGINT NOT NULL COMMENT '家庭ID', " +
|
|
|
+ "child_id BIGINT COMMENT '指定孩子ID(NULL=不限)', " +
|
|
|
+ "product_order_id BIGINT NOT NULL COMMENT '来源订单ID', " +
|
|
|
+ "product_id BIGINT NOT NULL COMMENT '商品ID', " +
|
|
|
+ "product_name VARCHAR(100) NOT NULL COMMENT '商品名快照', " +
|
|
|
+ "total_sessions INT NOT NULL DEFAULT 1 COMMENT '总次数', " +
|
|
|
+ "remaining_sessions INT NOT NULL DEFAULT 1 COMMENT '剩余次数', " +
|
|
|
+ "validity_start DATETIME NOT NULL COMMENT '有效期开始', " +
|
|
|
+ "validity_end DATETIME NOT NULL COMMENT '有效期截止', " +
|
|
|
+ "status VARCHAR(20) NOT NULL DEFAULT 'active' COMMENT 'active/expired/used_all/cancelled', " +
|
|
|
+ "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
+ "updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, " +
|
|
|
+ "INDEX idx_family (family_id), " +
|
|
|
+ "INDEX idx_order (product_order_id), " +
|
|
|
+ "UNIQUE KEY uk_quota_no (quota_no)" +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='测评额度表'");
|
|
|
+ log.info("已创建assessment_quotas表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建assessment_quotas表失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 迁移60: 创建 assessment_executions 表(测评执行记录)
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS assessment_executions (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
+ "quota_id BIGINT NOT NULL COMMENT '关联额度ID', " +
|
|
|
+ "child_id BIGINT NOT NULL COMMENT '被测评孩子', " +
|
|
|
+ "guide_id BIGINT COMMENT '执行规划师ID', " +
|
|
|
+ "assessment_type VARCHAR(30) COMMENT '本次具体测评类型', " +
|
|
|
+ "appointment_id BIGINT COMMENT '关联预约ID', " +
|
|
|
+ "result_id BIGINT COMMENT '关联测评结果ID', " +
|
|
|
+ "status VARCHAR(20) NOT NULL DEFAULT 'pending' COMMENT 'pending/in_progress/completed/cancelled', " +
|
|
|
+ "scheduled_date DATETIME COMMENT '预约执行时间', " +
|
|
|
+ "completed_at DATETIME COMMENT '实际完成时间', " +
|
|
|
+ "notes VARCHAR(500) COMMENT '备注', " +
|
|
|
+ "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
+ "updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, " +
|
|
|
+ "INDEX idx_quota (quota_id), " +
|
|
|
+ "INDEX idx_child (child_id), " +
|
|
|
+ "INDEX idx_appointment (appointment_id)" +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='测评执行记录表'");
|
|
|
+ log.info("已创建assessment_executions表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建assessment_executions表失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 迁移61: dan_assessment_results表添加source字段(报告来源)
|
|
|
+ ensureColumn("dan_assessment_results", "source",
|
|
|
+ "VARCHAR(20) DEFAULT 'planner_entry' COMMENT '报告来源: planner_entry/parent_upload/auto_fetch'");
|
|
|
+
|
|
|
+ // 迁移62: 创建 growth_guidances 表(结构化指导意见)
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS growth_guidances (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
+ "result_id BIGINT NOT NULL COMMENT '关联测评结果ID', " +
|
|
|
+ "dimension VARCHAR(30) NOT NULL COMMENT '维度: attention/focus/memory/logic/emotion/general', " +
|
|
|
+ "score INT COMMENT '该维度得分(0-100)', " +
|
|
|
+ "suggestion TEXT NOT NULL COMMENT '建议内容', " +
|
|
|
+ "priority INT DEFAULT 0 COMMENT '优先级(0=普通, 1=重要, 2=紧急)', " +
|
|
|
+ "sort_order INT DEFAULT 0 COMMENT '排序', " +
|
|
|
+ "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
+ "updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, " +
|
|
|
+ "INDEX idx_result (result_id)" +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='测评指导意见(结构化)'");
|
|
|
+ log.info("已创建growth_guidances表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建growth_guidances表失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 迁移63: 创建 assessment_plan_rules 表(方案生成规则)
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS assessment_plan_rules (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
+ "dimension VARCHAR(30) NOT NULL COMMENT '测评维度: attention/focus/memory/logic/emotion', " +
|
|
|
+ "score_min INT NOT NULL COMMENT '分数下限(含)', " +
|
|
|
+ "score_max INT NOT NULL COMMENT '分数上限(含)', " +
|
|
|
+ "template_id BIGINT NOT NULL COMMENT '关联TaskTemplatePackage ID', " +
|
|
|
+ "priority INT DEFAULT 0 COMMENT '优先级', " +
|
|
|
+ "is_active TINYINT(1) DEFAULT 1 COMMENT '是否启用', " +
|
|
|
+ "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
+ "updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, " +
|
|
|
+ "INDEX idx_dimension (dimension), " +
|
|
|
+ "INDEX idx_score (score_min, score_max)" +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='方案生成规则'");
|
|
|
+ log.info("已创建assessment_plan_rules表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建assessment_plan_rules表失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 迁移64: task_plan_instances表添加source字段(方案来源)
|
|
|
+ ensureColumn("task_plan_instances", "source",
|
|
|
+ "VARCHAR(20) DEFAULT 'manual' COMMENT '方案来源: manual/assessment/template'");
|
|
|
+
|
|
|
+ // 迁移65: task_plan_instances表添加source_result_id和remark字段
|
|
|
+ ensureColumn("task_plan_instances", "source_result_id",
|
|
|
+ "BIGINT COMMENT '关联测评结果ID(来源为assessment时)'");
|
|
|
+ ensureColumn("task_plan_instances", "remark",
|
|
|
+ "TEXT COMMENT '方案备注(含指导意见摘要)'");
|
|
|
+
|
|
|
+ // 迁移66: task_plan_instances表添加审核确认字段(阶段五)
|
|
|
+ ensureColumn("task_plan_instances", "reviewed_by",
|
|
|
+ "BIGINT COMMENT '审核人(规划师ID)'");
|
|
|
+ ensureColumn("task_plan_instances", "reviewed_at",
|
|
|
+ "DATETIME COMMENT '审核时间'");
|
|
|
+ ensureColumn("task_plan_instances", "review_comment",
|
|
|
+ "VARCHAR(500) COMMENT '审核意见'");
|
|
|
+ ensureColumn("task_plan_instances", "confirmed_by",
|
|
|
+ "BIGINT COMMENT '确认人(家长ID)'");
|
|
|
+ ensureColumn("task_plan_instances", "confirmed_at",
|
|
|
+ "DATETIME COMMENT '确认时间'");
|
|
|
+ ensureColumn("task_plan_instances", "activated_at",
|
|
|
+ "DATETIME COMMENT '激活时间'");
|
|
|
+
|
|
|
+ // 迁移67: 创建 promotion_tier_config 表(推广等级阈值配置)+ 插入默认配置
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS promotion_tier_config (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
+ "tier_code VARCHAR(20) NOT NULL COMMENT '等级代码', " +
|
|
|
+ "tier_name VARCHAR(50) NOT NULL COMMENT '等级名称', " +
|
|
|
+ "min_team_size INT NOT NULL DEFAULT 0 COMMENT '晋级所需最小团队人数', " +
|
|
|
+ "min_referral_earnings INT DEFAULT 0 COMMENT '晋级所需最小累计推荐佣金(分)', " +
|
|
|
+ "min_share_earnings INT DEFAULT 0 COMMENT '晋级所需最小累计消费分润(分)', " +
|
|
|
+ "profit_share_percent INT DEFAULT 20 COMMENT '产品P点比例(%)', " +
|
|
|
+ "sort_order INT DEFAULT 0 COMMENT '排序', " +
|
|
|
+ "enabled TINYINT(1) DEFAULT 1 COMMENT '是否启用', " +
|
|
|
+ "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
+ "updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, " +
|
|
|
+ "UNIQUE KEY uk_tier_code (tier_code), " +
|
|
|
+ "INDEX idx_enabled (enabled) " +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='推广等级阈值配置表'");
|
|
|
+ log.info("已创建promotion_tier_config表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.info("promotion_tier_config表已存在,跳过");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 插入默认等级配置(R0-R4)
|
|
|
+ try {
|
|
|
+ String[][] defaultConfigs = {
|
|
|
+ {"R0", "利他者", "0", "0", "0", "20", "0"},
|
|
|
+ {"R1", "传福人", "10", "100000", "50000", "25", "1"},
|
|
|
+ {"R2", "聚能师", "50", "500000", "200000", "30", "2"},
|
|
|
+ {"R3", "启慧者", "200", "2000000", "800000", "35", "3"},
|
|
|
+ {"R4", "传承者", "500", "5000000", "2000000", "40", "4"}
|
|
|
+ };
|
|
|
+ for (String[] cfg : defaultConfigs) {
|
|
|
+ try {
|
|
|
+ jdbcTemplate.update("INSERT INTO promotion_tier_config " +
|
|
|
+ "(tier_code, tier_name, min_team_size, min_referral_earnings, min_share_earnings, profit_share_percent, sort_order, enabled) " +
|
|
|
+ "VALUES (?, ?, ?, ?, ?, ?, ?, ?) " +
|
|
|
+ "ON DUPLICATE KEY UPDATE tier_name=VALUES(tier_name), min_team_size=VALUES(min_team_size), " +
|
|
|
+ "min_referral_earnings=VALUES(min_referral_earnings), min_share_earnings=VALUES(min_share_earnings), " +
|
|
|
+ "profit_share_percent=VALUES(profit_share_percent), sort_order=VALUES(sort_order), enabled=VALUES(enabled)",
|
|
|
+ cfg[0], cfg[1], Integer.parseInt(cfg[2]), Integer.parseInt(cfg[3]),
|
|
|
+ Integer.parseInt(cfg[4]), Integer.parseInt(cfg[5]), Integer.parseInt(cfg[6]), 1);
|
|
|
+ } catch (Exception ex) {
|
|
|
+ // ignore duplicate
|
|
|
+ }
|
|
|
+ }
|
|
|
+ log.info("已插入推广等级默认配置");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.info("推广等级默认配置已存在,跳过");
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+>>>>>>> 81845ce (feat: integrate promotion tier scaling into commission settlement + DB migration)
|
|
|
}
|