|
@@ -3,6 +3,7 @@ package com.etotem.cfc.config;
|
|
|
import com.etotem.cfc.mapper.*;
|
|
import com.etotem.cfc.mapper.*;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
import javax.annotation.Resource;
|
|
import javax.annotation.Resource;
|
|
|
|
|
+import org.springframework.beans.factory.annotation.Value;
|
|
|
import org.springframework.boot.CommandLineRunner;
|
|
import org.springframework.boot.CommandLineRunner;
|
|
|
import org.springframework.core.io.ResourceLoader;
|
|
import org.springframework.core.io.ResourceLoader;
|
|
|
import org.springframework.jdbc.core.JdbcTemplate;
|
|
import org.springframework.jdbc.core.JdbcTemplate;
|
|
@@ -25,6 +26,9 @@ public class DatabaseInitializer implements CommandLineRunner {
|
|
|
@Resource
|
|
@Resource
|
|
|
private ResourceLoader resourceLoader;
|
|
private ResourceLoader resourceLoader;
|
|
|
|
|
|
|
|
|
|
+ @Value("${app.init-test-data:false}")
|
|
|
|
|
+ private boolean initTestData;
|
|
|
|
|
+
|
|
|
@Override
|
|
@Override
|
|
|
public void run(String... args) throws Exception {
|
|
public void run(String... args) throws Exception {
|
|
|
log.info("开始初始化数据库...");
|
|
log.info("开始初始化数据库...");
|
|
@@ -38,12 +42,18 @@ public class DatabaseInitializer implements CommandLineRunner {
|
|
|
// 3. 执行数据库迁移(添加新列)
|
|
// 3. 执行数据库迁移(添加新列)
|
|
|
runMigrations();
|
|
runMigrations();
|
|
|
|
|
|
|
|
- // 4. 初始化默认数据
|
|
|
|
|
- initializeDefaultData();
|
|
|
|
|
-
|
|
|
|
|
- // 5. 初始化食品相关表
|
|
|
|
|
|
|
+ // 4. 初始化食品相关表(DDL,始终执行)
|
|
|
initFoodTables();
|
|
initFoodTables();
|
|
|
|
|
|
|
|
|
|
+ // 5. 初始化测试/种子数据(仅测试/开发环境)
|
|
|
|
|
+ if (initTestData) {
|
|
|
|
|
+ log.info("init-test-data=true,开始初始化测试/种子数据...");
|
|
|
|
|
+ initializeDefaultData();
|
|
|
|
|
+ log.info("测试/种子数据初始化完成!");
|
|
|
|
|
+ } else {
|
|
|
|
|
+ log.info("init-test-data=false,跳过测试/种子数据初始化(生产模式)");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
log.info("数据库初始化完成!");
|
|
log.info("数据库初始化完成!");
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -1016,14 +1026,18 @@ log.info("已添加template_id列到tasks表");
|
|
|
insertSysConfigSeed("points_exchange_rate", "1", "积分兑换汇率(1积分=1分,即0.01元)");
|
|
insertSysConfigSeed("points_exchange_rate", "1", "积分兑换汇率(1积分=1分,即0.01元)");
|
|
|
insertSysConfigSeed("points_deduct_cap", "0.5", "积分抵扣上限(50%)");
|
|
insertSysConfigSeed("points_deduct_cap", "0.5", "积分抵扣上限(50%)");
|
|
|
insertSysConfigSeed("member_fee_standard", "1314", "标准会员年费(元)");
|
|
insertSysConfigSeed("member_fee_standard", "1314", "标准会员年费(元)");
|
|
|
- insertSysConfigSeed("member_fee_premium", "3650", "高级会员年费(元)");
|
|
|
|
|
|
|
+ insertSysConfigSeed("member_fee_premium", "1316800", "高级家庭会员年费(分)");
|
|
|
insertSysConfigSeed("member_eligible_budget", "1314", "等值商品选择预算上限(元)");
|
|
insertSysConfigSeed("member_eligible_budget", "1314", "等值商品选择预算上限(元)");
|
|
|
insertSysConfigSeed("renewal_discount_rate", "0.85", "续费折扣率");
|
|
insertSysConfigSeed("renewal_discount_rate", "0.85", "续费折扣率");
|
|
|
insertSysConfigSeed("member_discount_standard", "0.9", "标准会员折扣率");
|
|
insertSysConfigSeed("member_discount_standard", "0.9", "标准会员折扣率");
|
|
|
- insertSysConfigSeed("member_discount_premium", "0.8", "高级会员折扣率");
|
|
|
|
|
- insertSysConfigSeed("member_fee_family", "36500", "家庭会员年费(分)");
|
|
|
|
|
|
|
+ insertSysConfigSeed("member_discount_premium", "8000", "高级家庭会员商品折扣(bps)");
|
|
|
|
|
+ insertSysConfigSeed("member_fee_family", "131400", "家庭会员年费(分)");
|
|
|
|
|
+ insertSysConfigSeed("member_fee_family_yearly", "131400", "家庭会员年费(分)");
|
|
|
|
|
+ insertSysConfigSeed("member_fee_family_monthly", "13140", "家庭会员月费(分)");
|
|
|
|
|
+ insertSysConfigSeed("member_fee_family_quarterly", "32850", "家庭会员季费(分)");
|
|
|
insertSysConfigSeed("member_upgrade_threshold", "131400", "自动升级家庭会员累计消费门槛(分)");
|
|
insertSysConfigSeed("member_upgrade_threshold", "131400", "自动升级家庭会员累计消费门槛(分)");
|
|
|
insertSysConfigSeed("member_discount_family", "9000", "家庭会员商品折扣(bps)");
|
|
insertSysConfigSeed("member_discount_family", "9000", "家庭会员商品折扣(bps)");
|
|
|
|
|
+ insertSysConfigSeed("member_fee_premium_monthly", "131400", "高级家庭会员月费(分)");
|
|
|
insertSysConfigSeed("commission_member_l1_rate", "2000", "推荐家庭会员L1佣金率(bps)");
|
|
insertSysConfigSeed("commission_member_l1_rate", "2000", "推荐家庭会员L1佣金率(bps)");
|
|
|
insertSysConfigSeed("commission_member_l2_rate", "500", "推荐家庭会员L2佣金率(bps)");
|
|
insertSysConfigSeed("commission_member_l2_rate", "500", "推荐家庭会员L2佣金率(bps)");
|
|
|
insertSysConfigSeed("commission_product_profit_share", "1000", "下线商品购买利润分成(bps)");
|
|
insertSysConfigSeed("commission_product_profit_share", "1000", "下线商品购买利润分成(bps)");
|
|
@@ -2946,6 +2960,7 @@ log.info("已添加template_id列到tasks表");
|
|
|
// ==================== 健康维度 Phase 1: health_dimension_score / health_data_source_record / health_norm_reference ====================
|
|
// ==================== 健康维度 Phase 1: health_dimension_score / health_data_source_record / health_norm_reference ====================
|
|
|
migrateHealthDimensionsTable();
|
|
migrateHealthDimensionsTable();
|
|
|
seedHealthNorms();
|
|
seedHealthNorms();
|
|
|
|
|
+ runLateMigrations();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
private void migrateHealthDimensionsTable() {
|
|
private void migrateHealthDimensionsTable() {
|
|
@@ -4778,7 +4793,10 @@ try {
|
|
|
"VALUES (?, ?, ?, ?, ?, 'active', NOW(), NOW())",
|
|
"VALUES (?, ?, ?, ?, ?, 'active', NOW(), NOW())",
|
|
|
pageKey, sectionKey, title, allowedRoles, sortOrder);
|
|
pageKey, sectionKey, title, allowedRoles, sortOrder);
|
|
|
}
|
|
}
|
|
|
-// 迁移51: 创建 member_subscription 表(家庭订阅记录)
|
|
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private void runLateMigrations() {
|
|
|
|
|
+ // 迁移51: 创建 member_subscription 表(家庭订阅记录)
|
|
|
try {
|
|
try {
|
|
|
jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS member_subscription (" +
|
|
jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS member_subscription (" +
|
|
|
"id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
"id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
@@ -5777,6 +5795,358 @@ try {
|
|
|
|
|
|
|
|
// 迁移115b: activity表添加feedback_dimensions字段
|
|
// 迁移115b: activity表添加feedback_dimensions字段
|
|
|
ensureColumn("activities", "feedback_dimensions", "TEXT COMMENT '评价维度列表JSON: [\"组织形式\",\"会场气氛\",\"主持人\",\"专家\"]'");
|
|
ensureColumn("activities", "feedback_dimensions", "TEXT COMMENT '评价维度列表JSON: [\"组织形式\",\"会场气氛\",\"主持人\",\"专家\"]'");
|
|
|
|
|
+
|
|
|
|
|
+ // 迁移116: growth_records表添加family_member_id和family_member_age列(修复/record/list 500错误)
|
|
|
|
|
+ ensureColumn("growth_records", "family_member_id", "BIGINT COMMENT '关联家庭成员ID'");
|
|
|
|
|
+ ensureColumn("growth_records", "family_member_age", "INT COMMENT '成员年龄快照'");
|
|
|
|
|
+
|
|
|
|
|
+ // 迁移117: 创建 emotion_checkin 表情绪打卡表(Fix 心理与情绪模块 500 错误)
|
|
|
|
|
+ try {
|
|
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS emotion_checkin (" +
|
|
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
|
|
+ "child_id BIGINT NOT NULL COMMENT '家庭成员ID', " +
|
|
|
|
|
+ "mood_weather VARCHAR(50) COMMENT '心情晴雨表: sunny/cloudy/rainy/stormy', " +
|
|
|
|
|
+ "emotion_tags VARCHAR(200) COMMENT '情绪标签(逗号分隔)', " +
|
|
|
|
|
+ "photo_url VARCHAR(500) COMMENT '配图URL', " +
|
|
|
|
|
+ "note TEXT COMMENT '文字备注', " +
|
|
|
|
|
+ "energy_awarded INT DEFAULT 0 COMMENT '奖励能量值', " +
|
|
|
|
|
+ "checkin_date DATE COMMENT '打卡日期', " +
|
|
|
|
|
+ "mood_score INT COMMENT '心情分数(1-10)', " +
|
|
|
|
|
+ "emotion_type VARCHAR(50) COMMENT '情绪类型', " +
|
|
|
|
|
+ "stress_level INT COMMENT '压力等级(1-10)', " +
|
|
|
|
|
+ "arousal_level INT COMMENT '激活水平(1-10)', " +
|
|
|
|
|
+ "energy_level INT COMMENT '精力水平(1-10)', " +
|
|
|
|
|
+ "sleep_hours DECIMAL(4,1) COMMENT '睡眠时长', " +
|
|
|
|
|
+ "sleep_quality INT COMMENT '睡眠质量(1-10)', " +
|
|
|
|
|
+ "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
|
|
+ "updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, " +
|
|
|
|
|
+ "INDEX idx_child_date (child_id, checkin_date)" +
|
|
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='情绪打卡记录'");
|
|
|
|
|
+ log.info("已创建emotion_checkin表");
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.warn("创建emotion_checkin表可能已存在: {}", e.getMessage());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 迁移118: 创建 emotion_alert 表情绪告警表
|
|
|
|
|
+ try {
|
|
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS emotion_alert (" +
|
|
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
|
|
+ "child_id BIGINT NOT NULL COMMENT '家庭成员ID', " +
|
|
|
|
|
+ "alert_type VARCHAR(50) NOT NULL COMMENT '告警类型: consecutive_low_mood/crisis_keyword/sharp_decline', " +
|
|
|
|
|
+ "severity VARCHAR(20) NOT NULL DEFAULT 'low' COMMENT '严重级别: low/medium/high', " +
|
|
|
|
|
+ "title VARCHAR(200) COMMENT '告警标题', " +
|
|
|
|
|
+ "message TEXT COMMENT '告警具体描述', " +
|
|
|
|
|
+ "related_checkin_id BIGINT COMMENT '关联情绪打卡ID', " +
|
|
|
|
|
+ "resolved TINYINT(1) DEFAULT 0 COMMENT '是否已处理', " +
|
|
|
|
|
+ "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
|
|
+ "resolved_at DATETIME COMMENT '处理时间', " +
|
|
|
|
|
+ "INDEX idx_child (child_id), " +
|
|
|
|
|
+ "INDEX idx_alert_type (alert_type)" +
|
|
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='情绪告警'");
|
|
|
|
|
+ log.info("已创建emotion_alert表");
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.warn("创建emotion_alert表可能已存在: {}", e.getMessage());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 迁移119: 创建 membership_levels 表(会员等级配置)
|
|
|
|
|
+ try {
|
|
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS membership_levels (" +
|
|
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
|
|
+ "level_code VARCHAR(50) NOT NULL COMMENT '等级编码: FREE/BASIC/PROFESSIONAL/ENTERPRISE', " +
|
|
|
|
|
+ "level_name VARCHAR(100) NOT NULL COMMENT '等级名称', " +
|
|
|
|
|
+ "level_desc TEXT COMMENT '等级描述', " +
|
|
|
|
|
+ "price_monthly INT COMMENT '月费(分)', " +
|
|
|
|
|
+ "price_quarterly INT COMMENT '季费(分)', " +
|
|
|
|
|
+ "price_yearly INT COMMENT '年费(分)', " +
|
|
|
|
|
+ "features TEXT COMMENT '功能权限列表(JSON)', " +
|
|
|
|
|
+ "max_children INT DEFAULT 0 COMMENT '可添加孩子数量', " +
|
|
|
|
|
+ "max_tasks_per_day INT DEFAULT 0 COMMENT '每日任务上限', " +
|
|
|
|
|
+ "ai_review_enabled TINYINT(1) DEFAULT 0 COMMENT 'AI审核功能', " +
|
|
|
|
|
+ "priority_support TINYINT(1) DEFAULT 0 COMMENT '优先客服', " +
|
|
|
|
|
+ "teacher_consultation TINYINT(1) DEFAULT 0 COMMENT '老师咨询', " +
|
|
|
|
|
+ "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
|
|
+ "updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, " +
|
|
|
|
|
+ "UNIQUE KEY uk_level_code (level_code)" +
|
|
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='会员等级配置'");
|
|
|
|
|
+ log.info("已创建membership_levels表");
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.warn("创建membership_levels表可能已存在: {}", e.getMessage());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 迁移120: 插入 membership_levels 种子数据
|
|
|
|
|
+ try {
|
|
|
|
|
+ jdbcTemplate.execute("INSERT IGNORE INTO membership_levels (level_code, level_name, level_desc, price_monthly, price_quarterly, price_yearly, max_children, max_tasks_per_day, ai_review_enabled, priority_support, teacher_consultation) VALUES " +
|
|
|
|
|
+ "('FREE', '免费会员', '基础免费功能', 0, 0, 0, 2, 5, 0, 0, 0), " +
|
|
|
|
|
+ "('FAMILY', '家庭会员', '家庭会员全部功能', 13140, 32850, 131400, 6, 20, 1, 1, 1), " +
|
|
|
|
|
+ "('PROVIDER', '标准会员', '标准会员服务', 13140, 32850, 131400, 10, 50, 1, 1, 1)");
|
|
|
|
|
+ log.info("已插入membership_levels种子数据");
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.warn("插入membership_levels种子数据可能已存在: {}", e.getMessage());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 迁移121: 创建 family_memberships 表(家庭会员订阅记录)
|
|
|
|
|
+ try {
|
|
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS family_memberships (" +
|
|
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
|
|
+ "family_id BIGINT NOT NULL COMMENT '家庭ID', " +
|
|
|
|
|
+ "level_code VARCHAR(50) NOT NULL DEFAULT 'FREE' COMMENT '会员等级编码', " +
|
|
|
|
|
+ "start_date DATETIME COMMENT '开始时间', " +
|
|
|
|
|
+ "end_date DATETIME COMMENT '结束时间', " +
|
|
|
|
|
+ "payment_method VARCHAR(50) COMMENT '支付方式: wechat/alipay/card', " +
|
|
|
|
|
+ "payment_status VARCHAR(20) DEFAULT 'pending' COMMENT '支付状态: pending/paid/expired/refunded', " +
|
|
|
|
|
+ "order_no VARCHAR(100) COMMENT '订单号', " +
|
|
|
|
|
+ "transaction_id VARCHAR(200) COMMENT '交易流水号', " +
|
|
|
|
|
+ "amount INT COMMENT '支付金额(分)', " +
|
|
|
|
|
+ "auto_renew TINYINT(1) DEFAULT 0 COMMENT '自动续费', " +
|
|
|
|
|
+ "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
|
|
+ "updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, " +
|
|
|
|
|
+ "INDEX idx_family_id (family_id), " +
|
|
|
|
|
+ "INDEX idx_end_date (end_date)" +
|
|
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='家庭会员订阅'");
|
|
|
|
|
+ log.info("已创建family_memberships表");
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.warn("创建family_memberships表可能已存在: {}", e.getMessage());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 迁移121: 创建 share_events 表(分享事件记录)
|
|
|
|
|
+ try {
|
|
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS share_events (" +
|
|
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
|
|
+ "user_id BIGINT NOT NULL COMMENT '分享用户ID', " +
|
|
|
|
|
+ "target_type VARCHAR(50) NOT NULL COMMENT '分享目标类型: article/growth/product', " +
|
|
|
|
|
+ "target_id BIGINT NOT NULL COMMENT '分享目标ID', " +
|
|
|
|
|
+ "share_channel VARCHAR(50) COMMENT '分享渠道: wechat/timeline/qq/url', " +
|
|
|
|
|
+ "share_title VARCHAR(200) COMMENT '分享标题', " +
|
|
|
|
|
+ "share_url VARCHAR(500) COMMENT '分享URL', " +
|
|
|
|
|
+ "click_count INT DEFAULT 0 COMMENT '点击次数', " +
|
|
|
|
|
+ "read_count INT DEFAULT 0 COMMENT '阅读次数', " +
|
|
|
|
|
+ "reward_points INT DEFAULT 0 COMMENT '奖励积分', " +
|
|
|
|
|
+ "status VARCHAR(20) DEFAULT 'active' COMMENT '状态: active/expired', " +
|
|
|
|
|
+ "expires_at DATETIME COMMENT '过期时间', " +
|
|
|
|
|
+ "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
|
|
+ "updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, " +
|
|
|
|
|
+ "INDEX idx_user (user_id), " +
|
|
|
|
|
+ "INDEX idx_target (target_type, target_id)" +
|
|
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='分享事件'");
|
|
|
|
|
+ log.info("已创建share_events表");
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.warn("创建share_events表可能已存在: {}", e.getMessage());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 迁移122: 创建 share_reads 表(分享阅读记录)
|
|
|
|
|
+ try {
|
|
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS share_reads (" +
|
|
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
|
|
+ "share_event_id BIGINT NOT NULL COMMENT '分享事件ID', " +
|
|
|
|
|
+ "reader_id BIGINT COMMENT '阅读者用户ID', " +
|
|
|
|
|
+ "reader_ip VARCHAR(50) COMMENT '阅读者IP', " +
|
|
|
|
|
+ "read_duration INT COMMENT '阅读时长(秒)', " +
|
|
|
|
|
+ "read_at DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '阅读时间', " +
|
|
|
|
|
+ "INDEX idx_share_event (share_event_id), " +
|
|
|
|
|
+ "INDEX idx_reader (reader_id)" +
|
|
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='分享阅读记录'");
|
|
|
|
|
+ log.info("已创建share_reads表");
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.warn("创建share_reads表可能已存在: {}", e.getMessage());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 迁移123: 创建 food_recommend_idx 表(饮食推荐指数)
|
|
|
|
|
+ try {
|
|
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS food_recommend_idx (" +
|
|
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
|
|
+ "user_id BIGINT NOT NULL COMMENT '用户ID', " +
|
|
|
|
|
+ "food_id BIGINT NOT NULL COMMENT '食材ID', " +
|
|
|
|
|
+ "food_name VARCHAR(100) COMMENT '食材名称', " +
|
|
|
|
|
+ "index_score INT COMMENT '推荐指数(0-100)', " +
|
|
|
|
|
+ "health_report_id BIGINT COMMENT '关联健康报告ID', " +
|
|
|
|
|
+ "calculated_at DATETIME COMMENT '计算时间', " +
|
|
|
|
|
+ "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
|
|
+ "INDEX idx_user (user_id), " +
|
|
|
|
|
+ "INDEX idx_food (food_id), " +
|
|
|
|
|
+ "INDEX idx_report (health_report_id)" +
|
|
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='饮食推荐指数'");
|
|
|
|
|
+ log.info("已创建food_recommend_idx表");
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.warn("创建food_recommend_idx表可能已存在: {}", e.getMessage());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 迁移124: 创建 points_exchange_products 表(积分兑换商品)
|
|
|
|
|
+ try {
|
|
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS points_exchange_products (" +
|
|
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
|
|
+ "name VARCHAR(200) NOT NULL COMMENT '商品名称', " +
|
|
|
|
|
+ "description TEXT COMMENT '商品描述', " +
|
|
|
|
|
+ "cover_image VARCHAR(500) COMMENT '封面图片', " +
|
|
|
|
|
+ "points_price INT NOT NULL COMMENT '积分价格', " +
|
|
|
|
|
+ "stock INT DEFAULT 0 COMMENT '库存', " +
|
|
|
|
|
+ "product_type VARCHAR(50) COMMENT '商品类型', " +
|
|
|
|
|
+ "category VARCHAR(50) COMMENT '分类', " +
|
|
|
|
|
+ "expiry_days INT 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, " +
|
|
|
|
|
+ "INDEX idx_status (status), " +
|
|
|
|
|
+ "INDEX idx_category (category)" +
|
|
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='积分兑换商品'");
|
|
|
|
|
+ log.info("已创建points_exchange_products表");
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.warn("创建points_exchange_products表可能已存在: {}", e.getMessage());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 迁移125: 创建 points_exchange_records 表(积分兑换记录)
|
|
|
|
|
+ try {
|
|
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS points_exchange_records (" +
|
|
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
|
|
+ "user_id BIGINT COMMENT '用户ID', " +
|
|
|
|
|
+ "child_id BIGINT COMMENT '孩子ID', " +
|
|
|
|
|
+ "family_member_id BIGINT COMMENT '家庭成员ID', " +
|
|
|
|
|
+ "product_id BIGINT NOT NULL COMMENT '商品ID', " +
|
|
|
|
|
+ "product_name VARCHAR(200) COMMENT '商品名称快照', " +
|
|
|
|
|
+ "points_cost INT NOT NULL COMMENT '消耗积分', " +
|
|
|
|
|
+ "quantity INT DEFAULT 1 COMMENT '数量', " +
|
|
|
|
|
+ "status VARCHAR(20) DEFAULT 'pending' COMMENT '状态: pending/completed/cancelled', " +
|
|
|
|
|
+ "redeem_code VARCHAR(100) COMMENT '兑换码', " +
|
|
|
|
|
+ "expired_at DATETIME COMMENT '过期时间', " +
|
|
|
|
|
+ "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
|
|
+ "updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, " +
|
|
|
|
|
+ "INDEX idx_user (user_id), " +
|
|
|
|
|
+ "INDEX idx_product (product_id), " +
|
|
|
|
|
+ "INDEX idx_status (status)" +
|
|
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='积分兑换记录'");
|
|
|
|
|
+ log.info("已创建points_exchange_records表");
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.warn("创建points_exchange_records表可能已存在: {}", e.getMessage());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 迁移126: emotion_checkin表修复列类型与添加新列
|
|
|
|
|
+ ensureColumn("emotion_checkin", "mood_weather", "VARCHAR(20) DEFAULT NULL COMMENT '心情天气: sunny/cloudy/rainy/stormy'");
|
|
|
|
|
+ ensureColumn("emotion_checkin", "emotion_tags", "VARCHAR(200) DEFAULT NULL COMMENT '情绪标签,逗号分隔'");
|
|
|
|
|
+ ensureColumn("emotion_checkin", "energy_awarded", "INT DEFAULT 0 COMMENT '发放能量值'");
|
|
|
|
|
+ ensureColumn("emotion_checkin", "mood_score", "INT DEFAULT NULL COMMENT '心情分数(0-100)'");
|
|
|
|
|
+ ensureColumn("emotion_checkin", "stress_level", "INT DEFAULT NULL COMMENT '压力等级(1-5)'");
|
|
|
|
|
+ ensureColumn("emotion_checkin", "arousal_level", "INT DEFAULT NULL COMMENT '唤醒等级(1-5)'");
|
|
|
|
|
+ ensureColumn("emotion_checkin", "sleep_hours", "DECIMAL(4,1) DEFAULT NULL COMMENT '睡眠时长(小时)'");
|
|
|
|
|
+ ensureColumn("emotion_checkin", "sleep_quality", "VARCHAR(20) DEFAULT NULL COMMENT '睡眠质量: good/fair/poor'");
|
|
|
|
|
+ try {
|
|
|
|
|
+ jdbcTemplate.execute("ALTER TABLE emotion_checkin MODIFY COLUMN user_id BIGINT NULL COMMENT '用户ID(旧字段,可为空)'");
|
|
|
|
|
+ log.info("已修改emotion_checkin.user_id为可空");
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.warn("修改emotion_checkin.user_id可能已处理: {}", e.getMessage());
|
|
|
|
|
+ }
|
|
|
|
|
+ try {
|
|
|
|
|
+ jdbcTemplate.execute("ALTER TABLE emotion_checkin MODIFY COLUMN emotion_type VARCHAR(50) DEFAULT 'custom' COMMENT '情绪类型'");
|
|
|
|
|
+ log.info("已修改emotion_checkin.emotion_type为VARCHAR");
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.warn("修改emotion_checkin.emotion_type可能已处理: {}", e.getMessage());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 迁移127: children表添加name列(兼容旧SQL查询如游戏排行榜LEFT JOIN)
|
|
|
|
|
+ ensureColumn("children", "name", "VARCHAR(50) DEFAULT NULL COMMENT '姓名(兼容旧查询)'");
|
|
|
|
|
+
|
|
|
|
|
+ // 迁移128: family_fortune表添加缺失列
|
|
|
|
|
+ ensureColumn("family_fortune", "fortune_level_text", "VARCHAR(50) DEFAULT NULL COMMENT '运势等级文字'");
|
|
|
|
|
+ ensureColumn("family_fortune", "fortune_description", "VARCHAR(500) DEFAULT NULL COMMENT '运势描述'");
|
|
|
|
|
+ ensureColumn("family_fortune", "mood_emoji", "VARCHAR(20) DEFAULT NULL COMMENT '心情表情'");
|
|
|
|
|
+ ensureColumn("family_fortune", "mood_text", "VARCHAR(100) DEFAULT NULL COMMENT '心情描述文字'");
|
|
|
|
|
+ ensureColumn("family_fortune", "weekly_tip", "VARCHAR(500) DEFAULT NULL COMMENT '本周建议'");
|
|
|
|
|
+
|
|
|
|
|
+ // 迁移129: membership_levels表添加price_quarterly列
|
|
|
|
|
+ ensureColumn("membership_levels", "price_quarterly", "DECIMAL(10,2) DEFAULT NULL COMMENT '季度价格'");
|
|
|
|
|
+
|
|
|
|
|
+ // 迁移130: share_events表添加缺失列
|
|
|
|
|
+ ensureColumn("share_events", "target_type", "VARCHAR(20) DEFAULT NULL COMMENT '分享目标类型'");
|
|
|
|
|
+ ensureColumn("share_events", "share_channel", "VARCHAR(20) DEFAULT NULL COMMENT '分享渠道'");
|
|
|
|
|
+ ensureColumn("share_events", "share_title", "VARCHAR(200) DEFAULT NULL COMMENT '分享标题'");
|
|
|
|
|
+ ensureColumn("share_events", "share_url", "VARCHAR(500) DEFAULT NULL COMMENT '分享URL'");
|
|
|
|
|
+ ensureColumn("share_events", "read_count", "INT DEFAULT 0 COMMENT '阅读次数'");
|
|
|
|
|
+ ensureColumn("share_events", "reward_points", "INT DEFAULT 0 COMMENT '奖励积分'");
|
|
|
|
|
+ ensureColumn("share_events", "status", "VARCHAR(20) DEFAULT 'active' COMMENT '状态'");
|
|
|
|
|
+ ensureColumn("share_events", "expires_at", "DATETIME DEFAULT NULL COMMENT '过期时间'");
|
|
|
|
|
+ ensureColumn("share_events", "updated_at", "DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间'");
|
|
|
|
|
+
|
|
|
|
|
+ // 迁移131: share_reads表添加缺失列
|
|
|
|
|
+ ensureColumn("share_reads", "read_duration", "INT DEFAULT NULL COMMENT '阅读时长(秒)'");
|
|
|
|
|
+ ensureColumn("share_reads", "read_at", "DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '阅读时间'");
|
|
|
|
|
+
|
|
|
|
|
+ // 迁移132: points_exchange_records表确保family_member_id列
|
|
|
|
|
+ ensureColumn("points_exchange_records", "family_member_id", "BIGINT COMMENT '家庭成员ID'");
|
|
|
|
|
+
|
|
|
|
|
+ // 迁移133: products表列对齐 — rename min_quantity→purchase_count_threshold, add missing columns
|
|
|
|
|
+ try {
|
|
|
|
|
+ jdbcTemplate.execute("ALTER TABLE products CHANGE COLUMN min_quantity purchase_count_threshold INT DEFAULT 0 COMMENT '最小购买数量限制'");
|
|
|
|
|
+ log.info("已重命名products.min_quantity→purchase_count_threshold");
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.warn("重命名products列可能已处理: {}", e.getMessage());
|
|
|
|
|
+ }
|
|
|
|
|
+ ensureColumn("products", "recommendation_tags", "VARCHAR(500) DEFAULT NULL COMMENT '推荐标签'");
|
|
|
|
|
+ ensureColumn("products", "repurchase_interval_days", "INT DEFAULT 30 COMMENT '复购间隔天数'");
|
|
|
|
|
+
|
|
|
|
|
+ // 迁移134: 创建 category_ppoint 表(品类P点比例配置)
|
|
|
|
|
+ try {
|
|
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS category_ppoint (" +
|
|
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
|
|
+ "category_id BIGINT NOT NULL COMMENT '商品类目ID', " +
|
|
|
|
|
+ "ratio INT DEFAULT 0 COMMENT 'P点比例(千分比)', " +
|
|
|
|
|
+ "start_date DATETIME COMMENT '生效日期', " +
|
|
|
|
|
+ "end_date DATETIME COMMENT '失效日期(null=永不过期)', " +
|
|
|
|
|
+ "created_by BIGINT COMMENT '创建人', " +
|
|
|
|
|
+ "created_at DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', " +
|
|
|
|
|
+ "INDEX idx_category_id (category_id)" +
|
|
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='品类P点比例配置'");
|
|
|
|
|
+ log.info("已创建category_ppoint表");
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.warn("创建category_ppoint表失败: {}", e.getMessage());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 迁移135: 创建 commission_dist_log 表(P点分润记录)
|
|
|
|
|
+ try {
|
|
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS commission_dist_log (" +
|
|
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
|
|
+ "buyer_id BIGINT NOT NULL COMMENT '买家用户ID', " +
|
|
|
|
|
+ "order_id BIGINT COMMENT '订单ID', " +
|
|
|
|
|
+ "product_id BIGINT COMMENT '商品ID', " +
|
|
|
|
|
+ "source_ppoint INT DEFAULT 0 COMMENT '本次产生的原始P点数量', " +
|
|
|
|
|
+ "level1_user_id BIGINT COMMENT '一级推荐人用户ID', " +
|
|
|
|
|
+ "level1_ppoint INT DEFAULT 0 COMMENT '一级推荐人获得P点', " +
|
|
|
|
|
+ "level1_wealth_percent INT DEFAULT 0 COMMENT '一级推荐人当时财富等级(profitSharePercent)', " +
|
|
|
|
|
+ "level2_user_id BIGINT COMMENT '二级推荐人用户ID', " +
|
|
|
|
|
+ "level2_ppoint INT DEFAULT 0 COMMENT '二级推荐人获得P点', " +
|
|
|
|
|
+ "level2_wealth_percent INT DEFAULT 0 COMMENT '二级推荐人当时财富等级(profitSharePercent)', " +
|
|
|
|
|
+ "status VARCHAR(20) DEFAULT 'pending' COMMENT '状态: pending/completed/failed', " +
|
|
|
|
|
+ "remark VARCHAR(500) COMMENT '备注', " +
|
|
|
|
|
+ "created_at DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', " +
|
|
|
|
|
+ "updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', " +
|
|
|
|
|
+ "INDEX idx_buyer (buyer_id), " +
|
|
|
|
|
+ "INDEX idx_order (order_id), " +
|
|
|
|
|
+ "INDEX idx_level1 (level1_user_id), " +
|
|
|
|
|
+ "INDEX idx_level2 (level2_user_id)" +
|
|
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='P点分润记录'");
|
|
|
|
|
+ log.info("已创建commission_dist_log表");
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.warn("创建commission_dist_log表失败: {}", e.getMessage());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 迁移136: payment_orders表添加period列(会员订阅周期)
|
|
|
|
|
+ ensureColumn("payment_orders", "period", "VARCHAR(20) DEFAULT 'yearly' COMMENT '订阅周期: monthly/quarterly/yearly'");
|
|
|
|
|
+
|
|
|
|
|
+ // 迁移137: 扩大payment_orders.payment_type列长度(原列可能为VARCHAR(5),存不下"yearly")
|
|
|
|
|
+ try {
|
|
|
|
|
+ jdbcTemplate.execute("ALTER TABLE payment_orders MODIFY COLUMN payment_type VARCHAR(20) DEFAULT 'yearly' COMMENT '支付类型: yearly/monthly/trial'");
|
|
|
|
|
+ log.info("已修改payment_orders.payment_type列长度为VARCHAR(20)");
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.warn("修改payment_orders.payment_type列长度失败: {}", e.getMessage());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 迁移138: tasks表添加dimension_code和dimension_weights列(原migration102内从未被调用)
|
|
|
|
|
+ ensureColumn("tasks", "dimension_code", "VARCHAR(20) COMMENT '五维维度: body/mind/wisdom/action/wealth'");
|
|
|
|
|
+ ensureColumn("tasks", "dimension_weights", "JSON COMMENT '五维权重: {\"body\":0,\"mind\":0,\"wisdom\":0,\"action\":0,\"wealth\":0}'");
|
|
|
|
|
+
|
|
|
|
|
+ // 迁移139: products表添加family_scope列(推荐系统-家庭消费去重,原migration102内从未被调用)
|
|
|
|
|
+ ensureColumn("products", "family_scope", "TINYINT DEFAULT 1 COMMENT '是否面向家庭: 1=家庭成员消费过即排除, 0=仅按个人消费排除'");
|
|
|
|
|
+
|
|
|
|
|
+ // 迁移140: activities表添加family_scope列(推荐系统-家庭参与去重,原migration102内从未被调用)
|
|
|
|
|
+ ensureColumn("activities", "family_scope", "TINYINT DEFAULT 1 COMMENT '是否面向家庭: 1=家庭成员参与过即排除, 0=仅按个人参与排除'");
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
private void runMigration82() {
|
|
private void runMigration82() {
|
|
@@ -5894,12 +6264,12 @@ try {
|
|
|
"platform_fee_p BIGINT DEFAULT 0, " +
|
|
"platform_fee_p BIGINT DEFAULT 0, " +
|
|
|
"butler_amount_p BIGINT DEFAULT 0, " +
|
|
"butler_amount_p BIGINT DEFAULT 0, " +
|
|
|
"member_tier VARCHAR(10), " +
|
|
"member_tier VARCHAR(10), " +
|
|
|
- "year_month VARCHAR(7), " +
|
|
|
|
|
|
|
+ "`year_month` VARCHAR(7), " +
|
|
|
"remark VARCHAR(500), " +
|
|
"remark VARCHAR(500), " +
|
|
|
"created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
"created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
"INDEX idx_butler_id (butler_id), " +
|
|
"INDEX idx_butler_id (butler_id), " +
|
|
|
"INDEX idx_member_user_id (member_user_id), " +
|
|
"INDEX idx_member_user_id (member_user_id), " +
|
|
|
- "INDEX idx_year_month (year_month)" +
|
|
|
|
|
|
|
+ "INDEX idx_year_month (`year_month`)" +
|
|
|
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='管家服务记录'");
|
|
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='管家服务记录'");
|
|
|
log.info("已创建butler_service_records表");
|
|
log.info("已创建butler_service_records表");
|
|
|
} catch (Exception e) {
|
|
} catch (Exception e) {
|
|
@@ -5910,7 +6280,7 @@ try {
|
|
|
jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS butler_commission_settlements (" +
|
|
jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS butler_commission_settlements (" +
|
|
|
"id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
"id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
"butler_id BIGINT NOT NULL, " +
|
|
"butler_id BIGINT NOT NULL, " +
|
|
|
- "year_month VARCHAR(7) NOT NULL, " +
|
|
|
|
|
|
|
+ "`year_month` VARCHAR(7) NOT NULL, " +
|
|
|
"member_count INT DEFAULT 0, " +
|
|
"member_count INT DEFAULT 0, " +
|
|
|
"annual_base_amount_p BIGINT DEFAULT 0, " +
|
|
"annual_base_amount_p BIGINT DEFAULT 0, " +
|
|
|
"annual_butler_amount_p BIGINT DEFAULT 0, " +
|
|
"annual_butler_amount_p BIGINT DEFAULT 0, " +
|
|
@@ -5923,9 +6293,9 @@ try {
|
|
|
"settled_at DATETIME, " +
|
|
"settled_at DATETIME, " +
|
|
|
"settled_by BIGINT, " +
|
|
"settled_by BIGINT, " +
|
|
|
"created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
"created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
- "UNIQUE KEY uk_butler_month (butler_id, year_month), " +
|
|
|
|
|
|
|
+ "UNIQUE KEY uk_butler_month (butler_id, `year_month`), " +
|
|
|
"INDEX idx_status (status), " +
|
|
"INDEX idx_status (status), " +
|
|
|
- "INDEX idx_year_month (year_month)" +
|
|
|
|
|
|
|
+ "INDEX idx_year_month (`year_month`)" +
|
|
|
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='管家月度佣金结算单'");
|
|
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='管家月度佣金结算单'");
|
|
|
log.info("已创建butler_commission_settlements表");
|
|
log.info("已创建butler_commission_settlements表");
|
|
|
} catch (Exception e) {
|
|
} catch (Exception e) {
|
|
@@ -6518,6 +6888,18 @@ private void runMigration99() {
|
|
|
} catch (Exception e) {
|
|
} catch (Exception e) {
|
|
|
log.error("迁移99 失败: " + e.getMessage());
|
|
log.error("迁移99 失败: " + e.getMessage());
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ ensureColumn("products", "min_quantity", "INT DEFAULT 1 COMMENT '最小购买数量限制'");
|
|
|
|
|
+
|
|
|
|
|
+ ensureColumn("growth_records", "family_member_id", "BIGINT COMMENT '家庭成员ID'");
|
|
|
|
|
+ ensureColumn("growth_records", "family_member_age", "INT COMMENT '家庭成员年龄(月)'");
|
|
|
|
|
+ ensureColumn("growth_records", "school", "VARCHAR(100) COMMENT '学校名称'");
|
|
|
|
|
+ ensureColumn("growth_records", "grade", "INT COMMENT '年级(数字)'");
|
|
|
|
|
+ ensureColumn("growth_records", "academic_performance", "VARCHAR(200) COMMENT '成绩/学业表现'");
|
|
|
|
|
+ ensureColumn("growth_records", "school_province", "VARCHAR(50) COMMENT '学校所在省'");
|
|
|
|
|
+ ensureColumn("growth_records", "school_city", "VARCHAR(50) COMMENT '学校所在市'");
|
|
|
|
|
+ ensureColumn("growth_records", "school_district", "VARCHAR(50) COMMENT '学校所在区'");
|
|
|
|
|
+ ensureColumn("growth_records", "school_street", "VARCHAR(100) COMMENT '学校所在街道'");
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
private void runMigration100() {
|
|
private void runMigration100() {
|
|
@@ -6592,5 +6974,266 @@ private void runMigration100() {
|
|
|
|
|
|
|
|
// 迁移103: health_indicators表添加collection_date列(指标采集日期)
|
|
// 迁移103: health_indicators表添加collection_date列(指标采集日期)
|
|
|
ensureColumn("health_indicators", "collection_date", "DATETIME COMMENT '指标采集日期(报告检测日期)'");
|
|
ensureColumn("health_indicators", "collection_date", "DATETIME COMMENT '指标采集日期(报告检测日期)'");
|
|
|
|
|
+
|
|
|
|
|
+ // 迁移104: promotion_tier表确保team_size_1st/2nd/3rd列存在(MyBatis-Plus camelCase映射修正)
|
|
|
|
|
+ ensureColumn("promotion_tier", "team_size_1st", "INT DEFAULT 0 COMMENT '一层团队人数'");
|
|
|
|
|
+ ensureColumn("promotion_tier", "team_size_2nd", "INT DEFAULT 0 COMMENT '二层团队人数'");
|
|
|
|
|
+ ensureColumn("promotion_tier", "team_size_3rd", "INT DEFAULT 0 COMMENT '三层团队人数'");
|
|
|
|
|
+
|
|
|
|
|
+ // 迁移105: article_reading_records表添加article_id和updated_at列
|
|
|
|
|
+ try {
|
|
|
|
|
+ jdbcTemplate.execute("ALTER TABLE article_reading_records ADD COLUMN article_id BIGINT DEFAULT NULL COMMENT '文章ID' AFTER id");
|
|
|
|
|
+ log.info("已添加article_id列到article_reading_records表");
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ // 列已存在,忽略
|
|
|
|
|
+ }
|
|
|
|
|
+ try {
|
|
|
|
|
+ jdbcTemplate.execute("ALTER TABLE article_reading_records ADD COLUMN updated_at DATETIME DEFAULT NULL COMMENT '更新时间' AFTER read_at");
|
|
|
|
|
+ log.info("已添加updated_at列到article_reading_records表");
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ // 列已存在,忽略
|
|
|
|
|
+ }
|
|
|
|
|
+ try {
|
|
|
|
|
+ jdbcTemplate.execute("ALTER TABLE article_reading_records ADD INDEX idx_article_child_date (article_id, child_id, read_at)");
|
|
|
|
|
+ log.info("已添加idx_article_child_date索引");
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ // 索引已存在,忽略
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 迁移106: 创建 report_summary 统一报告汇总表
|
|
|
|
|
+ try {
|
|
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS report_summary (" +
|
|
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID', " +
|
|
|
|
|
+ "report_type VARCHAR(32) NOT NULL COMMENT '报告类型', " +
|
|
|
|
|
+ "source_table VARCHAR(32) NOT NULL COMMENT '来源表', " +
|
|
|
|
|
+ "source_id BIGINT NOT NULL COMMENT '来源表记录ID', " +
|
|
|
|
|
+ "user_id BIGINT NOT NULL COMMENT '创建者用户ID', " +
|
|
|
|
|
+ "family_id BIGINT COMMENT '家庭ID', " +
|
|
|
|
|
+ "subject_id BIGINT COMMENT '报告主体ID', " +
|
|
|
|
|
+ "subject_name VARCHAR(50) COMMENT '报告主体名称', " +
|
|
|
|
|
+ "title VARCHAR(200) COMMENT '报告标题', " +
|
|
|
|
|
+ "summary TEXT COMMENT '摘要', " +
|
|
|
|
|
+ "file_url VARCHAR(500) COMMENT '文件URL', " +
|
|
|
|
|
+ "file_size BIGINT COMMENT '文件大小', " +
|
|
|
|
|
+ "file_name VARCHAR(200) COMMENT '文件名', " +
|
|
|
|
|
+ "has_file TINYINT DEFAULT 0 COMMENT '是否有文件', " +
|
|
|
|
|
+ "overall_score INT COMMENT '综合评分', " +
|
|
|
|
|
+ "report_date DATE COMMENT '报告日期', " +
|
|
|
|
|
+ "status VARCHAR(20) COMMENT '状态', " +
|
|
|
|
|
+ "detail_route VARCHAR(100) 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_subject_id (subject_id), " +
|
|
|
|
|
+ "INDEX idx_report_type (report_type), " +
|
|
|
|
|
+ "INDEX idx_report_date (report_date), " +
|
|
|
|
|
+ "INDEX idx_status (status), " +
|
|
|
|
|
+ "UNIQUE KEY uk_source (source_table, source_id)" +
|
|
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='统一报告汇总表'");
|
|
|
|
|
+ log.info("已创建 report_summary 表");
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ // 表已存在,忽略
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 迁移107: tasks表添加dimension_code和dimension_weights列(五维维度支持)
|
|
|
|
|
+ try {
|
|
|
|
|
+ ensureColumn("tasks", "dimension_code", "VARCHAR(20) COMMENT '五维维度: body/mind/wisdom/action/wealth'");
|
|
|
|
|
+ ensureColumn("tasks", "dimension_weights", "JSON COMMENT '五维权重: {\"body\":0,\"mind\":0,\"wisdom\":0,\"action\":0,\"wealth\":0}'");
|
|
|
|
|
+ log.info("已完成tasks表维度字段添加");
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.warn("添加tasks表维度字段失败: {}", e.getMessage());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 迁移108: 创建 energy_behavior_config 表(能量行为配置)
|
|
|
|
|
+ try {
|
|
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS energy_behavior_config (" +
|
|
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
|
|
+ "behavior_code VARCHAR(32) NOT NULL COMMENT '行为编码', " +
|
|
|
|
|
+ "behavior_name VARCHAR(50) NOT NULL COMMENT '行为名称', " +
|
|
|
|
|
+ "description VARCHAR(200) COMMENT '行为描述', " +
|
|
|
|
|
+ "default_amount INT DEFAULT 5 COMMENT '默认能量值', " +
|
|
|
|
|
+ "amount_source VARCHAR(32) DEFAULT 'fixed' COMMENT '能量值来源', " +
|
|
|
|
|
+ "dimension_assign JSON COMMENT '维度分配JSON', " +
|
|
|
|
|
+ "daily_limit INT DEFAULT 0 COMMENT '每日上限', " +
|
|
|
|
|
+ "cooldown_seconds INT DEFAULT 0 COMMENT '冷却时间(秒)', " +
|
|
|
|
|
+ "expire_days INT DEFAULT 0 COMMENT '过期天数', " +
|
|
|
|
|
+ "enabled TINYINT DEFAULT 1 COMMENT '是否启用', " +
|
|
|
|
|
+ "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
|
|
+ "updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, " +
|
|
|
|
|
+ "UNIQUE KEY uk_behavior_code (behavior_code)" +
|
|
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='能量行为配置表'");
|
|
|
|
|
+ log.info("已创建 energy_behavior_config 表");
|
|
|
|
|
+
|
|
|
|
|
+ // 插入预置数据(仅当表为空时插入)
|
|
|
|
|
+ Integer count = jdbcTemplate.queryForObject("SELECT COUNT(*) FROM energy_behavior_config", Integer.class);
|
|
|
|
|
+ if (count == null || count == 0) {
|
|
|
|
|
+ String[][] seedData = {
|
|
|
|
|
+ {"task_complete", "完成任务", "完成任务获得的能量", "5", "points", "[{\"dim\":\"action\",\"ratio\":100}]", "200", "0", "0"},
|
|
|
|
|
+ {"game_finish", "完成游戏", "完成小游戏获得的能量", "5", "points", "[{\"dim\":\"mind\",\"ratio\":30},{\"dim\":\"wisdom\",\"ratio\":50},{\"dim\":\"action\",\"ratio\":20}]", "200", "0", "0"},
|
|
|
|
|
+ {"activity_checkin", "活动签到", "活动签到获得的能量", "5", "fixed", "[{\"dim\":\"body\",\"ratio\":30},{\"dim\":\"action\",\"ratio\":70}]", "100", "0", "0"},
|
|
|
|
|
+ {"article_read", "阅读文章", "阅读文章获得的能量", "5", "points", "[{\"dim\":\"mind\",\"ratio\":30},{\"dim\":\"wisdom\",\"ratio\":50},{\"dim\":\"action\",\"ratio\":20}]", "100", "0", "0"},
|
|
|
|
|
+ {"product_purchase", "商品购买", "购买商品获得的能量", "10", "order_amount", "[{\"dim\":\"wealth\",\"ratio\":100}]", "500", "0", "30"},
|
|
|
|
|
+ {"health_checkin", "健康打卡", "健康打卡获得的能量", "5", "fixed", "[{\"dim\":\"body\",\"ratio\":100}]", "50", "0", "30"},
|
|
|
|
|
+ {"finance_checkin", "理财打卡", "理财打卡获得的能量", "5", "fixed", "[{\"dim\":\"wealth\",\"ratio\":100}]", "50", "0", "0"},
|
|
|
|
|
+ {"emotion_checkin", "情绪打卡", "情绪打卡获得的能量", "5", "fixed", "[{\"dim\":\"mind\",\"ratio\":100}]", "40", "0", "0"},
|
|
|
|
|
+ {"appointment_submit", "提交测评预约", "提交测评预约获得的能量", "10", "fixed", "[{\"dim\":\"action\",\"ratio\":100}]", "50", "0", "0"},
|
|
|
|
|
+ {"appointment_complete", "完成测评", "完成测评获得的能量", "30", "fixed", "[{\"dim\":\"mind\",\"ratio\":50},{\"dim\":\"wisdom\",\"ratio\":50}]", "100", "0", "0"},
|
|
|
|
|
+ {"streak_daily", "连续打卡每日", "每日连续打卡获得的能量", "5", "fixed", "[{\"dim\":\"body\",\"ratio\":30},{\"dim\":\"mind\",\"ratio\":20},{\"dim\":\"action\",\"ratio\":50}]", "50", "86400", "0"},
|
|
|
|
|
+ {"streak_milestone", "打卡里程碑", "达到打卡里程碑获得的能量", "20", "config", "[{\"dim\":\"body\",\"ratio\":30},{\"dim\":\"mind\",\"ratio\":20},{\"dim\":\"action\",\"ratio\":50}]", "100", "0", "0"},
|
|
|
|
|
+ {"growth_task", "成长任务", "完成每日成长任务获得的能量", "10", "config", "[{\"dim\":\"body\",\"ratio\":20},{\"dim\":\"mind\",\"ratio\":20},{\"dim\":\"wisdom\",\"ratio\":20},{\"dim\":\"action\",\"ratio\":20},{\"dim\":\"wealth\",\"ratio\":20}]", "100", "0", "1"},
|
|
|
|
|
+ {"micro_action", "微行动", "完成微行动获得的能量", "1", "fixed", "[{\"dim\":\"action\",\"ratio\":100}]", "20", "60", "30"},
|
|
|
|
|
+ {"onboarding", "新手引导", "完成新手引导任务获得的能量", "10", "config", "[{\"dim\":\"body\",\"ratio\":20},{\"dim\":\"mind\",\"ratio\":20},{\"dim\":\"wisdom\",\"ratio\":20},{\"dim\":\"action\",\"ratio\":20},{\"dim\":\"wealth\",\"ratio\":20}]", "50", "0", "30"},
|
|
|
|
|
+ {"invite_milestone", "邀请里程碑", "达到邀请人数里程碑获得的能量", "30", "config", "[{\"dim\":\"action\",\"ratio\":40},{\"dim\":\"wealth\",\"ratio\":60}]", "200", "0", "30"},
|
|
|
|
|
+ {"dimension_sync", "维度同步", "健康维度数据上传同步获得的能量", "10", "config", "[{\"dim\":\"body\",\"ratio\":100}]", "100", "0", "30"},
|
|
|
|
|
+ {"invite_friend", "邀请好友加入", "邀请好友注册加入获得的能量", "20", "fixed", "[{\"dim\":\"action\",\"ratio\":50},{\"dim\":\"wealth\",\"ratio\":50}]", "200", "0", "0"},
|
|
|
|
|
+ {"invite_family", "邀请家庭成员", "邀请家庭成员加入家庭获得的能量", "10", "fixed", "[{\"dim\":\"mind\",\"ratio\":30},{\"dim\":\"action\",\"ratio\":70}]", "100", "0", "0"},
|
|
|
|
|
+ {"report_upload", "上传体检报告", "上传体检报告获得的能量", "30", "fixed", "[{\"dim\":\"body\",\"ratio\":100}]", "100", "0", "30"},
|
|
|
|
|
+ {"plan_complete", "完成成长计划", "完成成长计划复盘获得的能量", "20", "fixed", "[{\"dim\":\"body\",\"ratio\":20},{\"dim\":\"mind\",\"ratio\":20},{\"dim\":\"wisdom\",\"ratio\":20},{\"dim\":\"action\",\"ratio\":20},{\"dim\":\"wealth\",\"ratio\":20}]", "100", "0", "0"},
|
|
|
|
|
+ {"wisdom_report", "上传智测评报告", "上传智慧测评报告获得的能量", "30", "fixed", "[{\"dim\":\"wisdom\",\"ratio\":100}]", "100", "0", "30"},
|
|
|
|
|
+ };
|
|
|
|
|
+ String sql = "INSERT INTO energy_behavior_config (behavior_code, behavior_name, description, default_amount, amount_source, dimension_assign, daily_limit, cooldown_seconds, expire_days, enabled) VALUES (?,?,?,?,?,?,?,?,?,1)";
|
|
|
|
|
+ for (String[] row : seedData) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ jdbcTemplate.update(sql, row[0], row[1], row[2], Integer.parseInt(row[3]), row[4], row[5], Integer.parseInt(row[6]), Integer.parseInt(row[7]), Integer.parseInt(row[8]));
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.warn("插入能量行为配置失败: behavior_code={}, error={}", row[0], e.getMessage());
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ log.info("已插入{}条能量行为配置", seedData.length);
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.warn("创建 energy_behavior_config 表失败: {}", e.getMessage());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 迁移109: finance_checkins表添加type列(记账类型: income/expense)
|
|
|
|
|
+ try {
|
|
|
|
|
+ ensureColumn("finance_checkins", "type", "VARCHAR(10) DEFAULT 'expense' COMMENT '类型: income(收入)/expense(支出)'");
|
|
|
|
|
+ log.info("已完成finance_checkins.type字段添加");
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.warn("添加finance_checkins.type字段失败: {}", e.getMessage());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 迁移110: 创建 article_comments 表(文章评论)
|
|
|
|
|
+ try {
|
|
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS article_comments (" +
|
|
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
|
|
+ "article_id BIGINT NOT NULL COMMENT '文章ID', " +
|
|
|
|
|
+ "parent_id BIGINT COMMENT '父评论ID', " +
|
|
|
|
|
+ "reply_to_id BIGINT COMMENT '引用的评论ID', " +
|
|
|
|
|
+ "user_id BIGINT NOT NULL COMMENT '评论用户ID', " +
|
|
|
|
|
+ "content TEXT NOT NULL COMMENT '评论内容', " +
|
|
|
|
|
+ "status VARCHAR(20) DEFAULT 'pending' COMMENT 'pending/approved/rejected', " +
|
|
|
|
|
+ "audit_by BIGINT COMMENT '审核人ID', " +
|
|
|
|
|
+ "audit_at DATETIME COMMENT '审核时间', " +
|
|
|
|
|
+ "earned_energy INT DEFAULT 0 COMMENT '获得的能量', " +
|
|
|
|
|
+ "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
|
|
+ "INDEX idx_article (article_id), " +
|
|
|
|
|
+ "INDEX idx_article_status (article_id, status), " +
|
|
|
|
|
+ "INDEX idx_parent (parent_id)" +
|
|
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='文章评论'");
|
|
|
|
|
+ log.info("已创建 article_comments 表");
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.warn("创建 article_comments 表失败: {}", e.getMessage());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 迁移111: 创建 service_role_applications 表(服务角色统一申请)
|
|
|
|
|
+ try {
|
|
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS service_role_applications (" +
|
|
|
|
|
+ "id int(11) NOT NULL AUTO_INCREMENT, " +
|
|
|
|
|
+ "user_id int(11) NOT NULL COMMENT '申请人ID', " +
|
|
|
|
|
+ "roles varchar(255) NOT NULL COMMENT '申请角色,JSON数组', " +
|
|
|
|
|
+ "id_card varchar(18) DEFAULT '' COMMENT '身份证号', " +
|
|
|
|
|
+ "id_card_images text COMMENT '身份证照片,JSON数组', " +
|
|
|
|
|
+ "certificate_images text COMMENT '资质证书照片,JSON数组', " +
|
|
|
|
|
+ "province varchar(50) DEFAULT '' COMMENT '省', " +
|
|
|
|
|
+ "city varchar(50) DEFAULT '' COMMENT '市', " +
|
|
|
|
|
+ "district varchar(50) DEFAULT '' COMMENT '区', " +
|
|
|
|
|
+ "street varchar(50) DEFAULT '' COMMENT '街道', " +
|
|
|
|
|
+ "address_detail varchar(200) DEFAULT '' COMMENT '详细地址', " +
|
|
|
|
|
+ "status tinyint(4) NOT NULL DEFAULT 0 COMMENT '0-pending 1-approved 2-rejected', " +
|
|
|
|
|
+ "reject_reason varchar(500) DEFAULT '' COMMENT '驳回原因', " +
|
|
|
|
|
+ "reviewed_by int(11) DEFAULT NULL COMMENT '审核人ID', " +
|
|
|
|
|
+ "reviewed_at datetime DEFAULT NULL COMMENT '审核时间', " +
|
|
|
|
|
+ "created_at datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
|
|
+ "updated_at datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, " +
|
|
|
|
|
+ "PRIMARY KEY (id), " +
|
|
|
|
|
+ "KEY idx_user_id (user_id), " +
|
|
|
|
|
+ "KEY idx_status (status)" +
|
|
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='服务角色申请表'");
|
|
|
|
|
+ log.info("已创建service_role_applications表");
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ // 表已存在,忽略错误
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 迁移112: users表添加article_admin_status字段(文章管理员状态)
|
|
|
|
|
+ try {
|
|
|
|
|
+ jdbcTemplate.execute("ALTER TABLE users ADD COLUMN article_admin_status VARCHAR(20) DEFAULT NULL COMMENT '文章管理员状态: pending/approved/rejected'");
|
|
|
|
|
+ log.info("已添加article_admin_status列到users表");
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ // 列已存在,忽略错误
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 迁移113: users表添加activity_provider_status字段(活动提供商状态)
|
|
|
|
|
+ try {
|
|
|
|
|
+ jdbcTemplate.execute("ALTER TABLE users ADD COLUMN activity_provider_status VARCHAR(20) DEFAULT NULL COMMENT '活动提供商状态: pending/approved/rejected'");
|
|
|
|
|
+ log.info("已添加activity_provider_status列到users表");
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ // 列已存在,忽略错误
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 迁移114: 创建 periodic_services 表(周期性服务续费配置)
|
|
|
|
|
+ try {
|
|
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS periodic_services (" +
|
|
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
|
|
+ "product_id BIGINT NOT NULL COMMENT '关联商品ID', " +
|
|
|
|
|
+ "service_name VARCHAR(100) COMMENT '服务名称', " +
|
|
|
|
|
+ "cycle_days INT NOT NULL COMMENT '周期天数(如365=年度服务)', " +
|
|
|
|
|
+ "remind_before_days INT DEFAULT 30 COMMENT '提前提醒天数', " +
|
|
|
|
|
+ "auto_renew TINYINT DEFAULT 0 COMMENT '是否支持自动续费: 0=否 1=是', " +
|
|
|
|
|
+ "enabled TINYINT DEFAULT 1 COMMENT '是否启用: 0=禁用 1=启用', " +
|
|
|
|
|
+ "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
|
|
+ "updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, " +
|
|
|
|
|
+ "INDEX idx_product (product_id)" +
|
|
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='周期性服务续费配置'");
|
|
|
|
|
+ log.info("已创建 periodic_services 表");
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.warn("创建 periodic_services 表失败: {}", e.getMessage());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 迁移115: 创建 recommendation_logs 表(推荐行为记录)
|
|
|
|
|
+ try {
|
|
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS recommendation_logs (" +
|
|
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
|
|
+ "user_id BIGINT COMMENT '用户ID', " +
|
|
|
|
|
+ "family_id BIGINT COMMENT '家庭ID', " +
|
|
|
|
|
+ "child_id BIGINT COMMENT '孩子ID', " +
|
|
|
|
|
+ "content_type VARCHAR(20) NOT NULL COMMENT '内容类型: article/task/product/activity', " +
|
|
|
|
|
+ "content_id BIGINT NOT NULL COMMENT '内容ID', " +
|
|
|
|
|
+ "recommend_score INT COMMENT '推荐分值', " +
|
|
|
|
|
+ "action VARCHAR(20) COMMENT '行为: show/click/consume', " +
|
|
|
|
|
+ "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
|
|
+ "INDEX idx_user_content (user_id, content_type, content_id)" +
|
|
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='推荐行为记录'");
|
|
|
|
|
+ log.info("已创建 recommendation_logs 表");
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.warn("创建 recommendation_logs 表失败: {}", e.getMessage());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 迁移116: products表添加family_scope列(推荐系统-家庭消费去重)
|
|
|
|
|
+ try {
|
|
|
|
|
+ ensureColumn("products", "family_scope", "TINYINT DEFAULT 1 COMMENT '是否面向家庭: 1=家庭成员消费过即排除, 0=仅按个人消费排除'");
|
|
|
|
|
+ log.info("已完成products.family_scope字段添加");
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.warn("添加products.family_scope字段失败: {}", e.getMessage());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 迁移117: activities表添加family_scope列(推荐系统-家庭参与去重)
|
|
|
|
|
+ try {
|
|
|
|
|
+ ensureColumn("activities", "family_scope", "TINYINT DEFAULT 1 COMMENT '是否面向家庭: 1=家庭成员参与过即排除, 0=仅按个人参与排除'");
|
|
|
|
|
+ log.info("已完成activities.family_scope字段添加");
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.warn("添加activities.family_scope字段失败: {}", e.getMessage());
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|