|
|
@@ -1,4740 +1,4511 @@
|
|
|
-package com.etotem.cfc.config;
|
|
|
-
|
|
|
-import com.etotem.cfc.mapper.*;
|
|
|
-import lombok.extern.slf4j.Slf4j;
|
|
|
-import javax.annotation.Resource;
|
|
|
-import org.springframework.boot.CommandLineRunner;
|
|
|
-import org.springframework.core.io.ResourceLoader;
|
|
|
-import org.springframework.jdbc.core.JdbcTemplate;
|
|
|
-import org.springframework.stereotype.Component;
|
|
|
-import org.springframework.util.StreamUtils;
|
|
|
-
|
|
|
-import java.nio.charset.StandardCharsets;
|
|
|
-import java.util.ArrayList;
|
|
|
-import java.util.HashMap;
|
|
|
-import java.util.List;
|
|
|
-import java.util.Map;
|
|
|
-import java.util.regex.Matcher;
|
|
|
-import java.util.regex.Pattern;
|
|
|
-
|
|
|
-@Slf4j
|
|
|
-@Component
|
|
|
-public class DatabaseInitializer implements CommandLineRunner {
|
|
|
-
|
|
|
- @Resource
|
|
|
- private JdbcTemplate jdbcTemplate;
|
|
|
-
|
|
|
- @Resource
|
|
|
- private ResourceLoader resourceLoader;
|
|
|
-
|
|
|
- @Override
|
|
|
- public void run(String... args) throws Exception {
|
|
|
- log.info("开始初始化数据...");
|
|
|
-
|
|
|
- // 1. 检查并创建数据库
|
|
|
- initializeDatabase();
|
|
|
-
|
|
|
- // 2. 检查并创建表
|
|
|
- initializeTables();
|
|
|
-
|
|
|
- // 3. 执行数据库迁移(添加新列)
|
|
|
- runMigrations();
|
|
|
-
|
|
|
- // 4. 初始化默认数据
|
|
|
- initializeDefaultData();
|
|
|
-
|
|
|
- // 5. 初始化食品相关表
|
|
|
- initFoodTables();
|
|
|
-
|
|
|
- log.info("数据库初始化完成");
|
|
|
- }
|
|
|
-
|
|
|
- private void initializeDatabase() {
|
|
|
- try {
|
|
|
- // 尝试连接数据库,如果不存在则创建
|
|
|
- jdbcTemplate.execute("CREATE DATABASE IF NOT EXISTS xzyj DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci");
|
|
|
- log.info("数据库检查完成");
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("创建数据库失败 {}", e.getMessage());
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private void initializeTables() {
|
|
|
- try {
|
|
|
- org.springframework.core.io.Resource resource = resourceLoader.getResource("classpath:schema.sql");
|
|
|
- String content;
|
|
|
- try (java.io.InputStream is = resource.getInputStream()) {
|
|
|
- content = new String(StreamUtils.copyToByteArray(is), StandardCharsets.UTF_8);
|
|
|
- }
|
|
|
-
|
|
|
- // Remove SQL single-line comments
|
|
|
- content = content.replaceAll("(?m)^\\s*--.*$", "");
|
|
|
-
|
|
|
- // Extract all CREATE TABLE IF NOT EXISTS statements
|
|
|
- Pattern pattern = Pattern.compile(
|
|
|
- "CREATE\\s+TABLE\\s+IF\\s+NOT\\s+EXISTS\\s+.*?;",
|
|
|
- Pattern.CASE_INSENSITIVE | Pattern.DOTALL
|
|
|
- );
|
|
|
- Matcher matcher = pattern.matcher(content);
|
|
|
-
|
|
|
- List<String> createTableSQLs = new ArrayList<>();
|
|
|
- while (matcher.find()) {
|
|
|
- String sql = matcher.group().trim();
|
|
|
- if (!sql.isEmpty()) {
|
|
|
- createTableSQLs.add(sql);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- log.info("schema.sql 加载{} 个建表语句", createTableSQLs.size());
|
|
|
-
|
|
|
- for (String sql : createTableSQLs) {
|
|
|
- try {
|
|
|
- jdbcTemplate.execute(sql);
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("创建表失败 {}", e.getMessage());
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- log.info("数据表检查完成");
|
|
|
- } catch (Exception e) {
|
|
|
- log.error("读取 schema.sql 失败", e);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private void runMigrations() {
|
|
|
- // 迁移1: 任务表添加执行者字段
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE tasks ADD COLUMN executor_type ENUM('child', 'parent') DEFAULT 'child' COMMENT '执行者类型'");
|
|
|
- log.info("已添加executor_type列到tasks表");
|
|
|
- } catch (Exception e) {
|
|
|
- // 列已存在,忽略错误
|
|
|
- }
|
|
|
-
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE tasks ADD COLUMN executor_id BIGINT COMMENT '执行者ID'");
|
|
|
- log.info("已添加executor_id列到tasks表");
|
|
|
- } catch (Exception e) {
|
|
|
- // 列已存在,忽略错误
|
|
|
- }
|
|
|
-
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE tasks ADD INDEX idx_executor (executor_type, executor_id)");
|
|
|
- log.info("已添加idx_executor索引到tasks表");
|
|
|
- } catch (Exception e) {
|
|
|
- // 索引已存在,忽略错误
|
|
|
- }
|
|
|
-
|
|
|
- // 迁移2: 用户表添加角色和积分字段
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE users ADD COLUMN roles VARCHAR(100) COMMENT '角色列表'");
|
|
|
- log.info("已添加roles列到users表");
|
|
|
- } catch (Exception e) {
|
|
|
- // 列已存在,忽略错误
|
|
|
- }
|
|
|
-
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE users ADD COLUMN total_points INT DEFAULT 0 COMMENT '家长总积分'");
|
|
|
- log.info("已添加total_points列到users表");
|
|
|
- } catch (Exception e) {
|
|
|
- // 列已存在,忽略错误
|
|
|
- }
|
|
|
-
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE users ADD INDEX idx_roles (roles(20))");
|
|
|
- log.info("已添加idx_roles索引到users表");
|
|
|
- } catch (Exception e) {
|
|
|
- // 索引已存在,忽略错误
|
|
|
- }
|
|
|
-
|
|
|
- // 迁移3: users表添加新字段
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE users ADD COLUMN id_card VARCHAR(18) COMMENT '身份证号'");
|
|
|
- log.info("已添加id_card列到users表");
|
|
|
- } catch (Exception e) {
|
|
|
- // 列已存在,忽略错误
|
|
|
- }
|
|
|
-
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE users ADD COLUMN real_name VARCHAR(50) COMMENT '真实姓名'");
|
|
|
- log.info("已添加real_name列到users表");
|
|
|
- } catch (Exception e) {
|
|
|
- // 列已存在,忽略错误
|
|
|
- }
|
|
|
-
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE users ADD COLUMN phone VARCHAR(11) COMMENT '手机号'");
|
|
|
- log.info("已添加phone列到users表");
|
|
|
- } catch (Exception e) {
|
|
|
- // 列已存在,忽略错误
|
|
|
- }
|
|
|
-
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE users ADD COLUMN birthday DATE COMMENT '生日'");
|
|
|
- log.info("已添加birthday列到users表");
|
|
|
- } catch (Exception e) {
|
|
|
- // 列已存在,忽略错误
|
|
|
- }
|
|
|
-
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE users ADD COLUMN gender ENUM('male', 'female') COMMENT '性别'");
|
|
|
- log.info("已添加gender列到users表");
|
|
|
- } catch (Exception e) {
|
|
|
- // 列已存在,忽略错误
|
|
|
- }
|
|
|
-
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE users ADD INDEX idx_phone (phone)");
|
|
|
- log.info("已添加idx_phone索引到users表");
|
|
|
- } catch (Exception e) {
|
|
|
- // 索引已存在,忽略错误
|
|
|
- }
|
|
|
-
|
|
|
- // 迁移4: children表添加新字段
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE children ADD COLUMN birthday DATE COMMENT '生日'");
|
|
|
- log.info("已添加birthday列到children表");
|
|
|
- } catch (Exception e) {
|
|
|
- // 列已存在,忽略错误
|
|
|
- }
|
|
|
-
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE children ADD COLUMN gender ENUM('male', 'female') COMMENT '性别'");
|
|
|
- log.info("已添加gender列到children表");
|
|
|
- } catch (Exception e) {
|
|
|
- // 列已存在,忽略错误
|
|
|
- }
|
|
|
-
|
|
|
- // 迁移5: 修改users.role为支持teacher
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE users MODIFY COLUMN role ENUM('parent', 'child', 'teacher') NOT NULL");
|
|
|
- log.info("已更新users.role字段支持teacher角色");
|
|
|
- } catch (Exception e) {
|
|
|
- // 修改失败,忽略错误
|
|
|
- }
|
|
|
-
|
|
|
- // 迁移5.1: 修改users.role为支持admin
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE users MODIFY COLUMN role ENUM('parent', 'child', 'teacher', 'admin') NOT NULL");
|
|
|
- log.info("已更新users.role字段支持admin角色");
|
|
|
- } catch (Exception e) {
|
|
|
- // 修改失败,忽略错误
|
|
|
- }
|
|
|
-
|
|
|
- // 迁移5.2: 确保管理员角色为admin(修复旧数据遗留问题
|
|
|
- try {
|
|
|
- int updated = jdbcTemplate.update("UPDATE admins SET role = 'admin' WHERE username = 'admin'");
|
|
|
- if (updated > 0) {
|
|
|
- log.info("已修正管理员角色为admin");
|
|
|
- }
|
|
|
- } catch (Exception e) {
|
|
|
- // 更新失败,忽略错误
|
|
|
- }
|
|
|
-
|
|
|
- // 迁移6: 创建streets表(街道地址维度表)
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS streets (" +
|
|
|
- "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
- "province VARCHAR(50) COMMENT '省份', " +
|
|
|
- "province_code VARCHAR(20) COMMENT '省份编码', " +
|
|
|
- "city VARCHAR(50) COMMENT '城市', " +
|
|
|
- "city_code VARCHAR(20) COMMENT '城市编码', " +
|
|
|
- "district VARCHAR(50) COMMENT '区县', " +
|
|
|
- "district_code VARCHAR(20) COMMENT '区县编码', " +
|
|
|
- "street VARCHAR(100) COMMENT '街道', " +
|
|
|
- "street_code VARCHAR(20) COMMENT '街道编码', " +
|
|
|
- "full_name VARCHAR(200) COMMENT '完整地址', " +
|
|
|
- "level INT COMMENT '层级:1街道', " +
|
|
|
- "parent_id BIGINT COMMENT '父级ID', " +
|
|
|
- "sort_order INT DEFAULT 0 COMMENT '排序', " +
|
|
|
- "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
- "updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, " +
|
|
|
- "INDEX idx_level (level), " +
|
|
|
- "INDEX idx_parent_id (parent_id), " +
|
|
|
- "INDEX idx_street_code (street_code), " +
|
|
|
- "INDEX idx_full_name (full_name(50))" +
|
|
|
- ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4");
|
|
|
- log.info("已创建streets表");
|
|
|
- } catch (Exception e) {
|
|
|
- // 表已存在,忽略错误
|
|
|
- }
|
|
|
-
|
|
|
- // 迁移7: families表添加地址字段
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE families ADD COLUMN street_id BIGINT COMMENT '街道ID'");
|
|
|
- log.info("已添加street_id列到families表");
|
|
|
- } catch (Exception e) {
|
|
|
- // 列已存在,忽略错误
|
|
|
- }
|
|
|
-
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE families ADD COLUMN province VARCHAR(50) COMMENT '省份'");
|
|
|
- jdbcTemplate.execute("ALTER TABLE families ADD COLUMN city VARCHAR(50) COMMENT '城市'");
|
|
|
- jdbcTemplate.execute("ALTER TABLE families ADD COLUMN district VARCHAR(50) COMMENT '区县'");
|
|
|
- jdbcTemplate.execute("ALTER TABLE families ADD COLUMN street VARCHAR(100) COMMENT '街道'");
|
|
|
- log.info("已添加地址字段到families表");
|
|
|
- } catch (Exception e) {
|
|
|
- // 列已存在,忽略错误
|
|
|
- }
|
|
|
-
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE families ADD INDEX idx_street_id (street_id)");
|
|
|
- log.info("已添加idx_street_id索引到families表");
|
|
|
- } catch (Exception e) {
|
|
|
- // 索引已存在,忽略错误
|
|
|
- }
|
|
|
-
|
|
|
- // 迁移7b: families表添加teacher_id和creator_id字段
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE families ADD COLUMN teacher_id BIGINT COMMENT '绑定的成长规划师ID'");
|
|
|
- log.info("已添加teacher_id列到families表");
|
|
|
- } catch (Exception e) {
|
|
|
- // 列已存在,忽略错误
|
|
|
- }
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE families ADD COLUMN creator_id BIGINT COMMENT '家庭创建者ID'");
|
|
|
- log.info("已添加creator_id列到families表");
|
|
|
- } catch (Exception e) {
|
|
|
- // 列已存在,忽略错误
|
|
|
- }
|
|
|
-
|
|
|
- // 迁移8: teachers表添加地址字段
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE teachers ADD COLUMN street_id BIGINT COMMENT '街道ID'");
|
|
|
- log.info("已添加street_id列到teachers表");
|
|
|
- } catch (Exception e) {
|
|
|
- // 列已存在,忽略错误
|
|
|
- }
|
|
|
-
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE teachers ADD COLUMN province VARCHAR(50) COMMENT '省份'");
|
|
|
- jdbcTemplate.execute("ALTER TABLE teachers ADD COLUMN city VARCHAR(50) COMMENT '城市'");
|
|
|
- jdbcTemplate.execute("ALTER TABLE teachers ADD COLUMN district VARCHAR(50) COMMENT '区县'");
|
|
|
- jdbcTemplate.execute("ALTER TABLE teachers ADD COLUMN street VARCHAR(100) COMMENT '街道'");
|
|
|
- log.info("已添加地址字段到teachers表");
|
|
|
- } catch (Exception e) {
|
|
|
- // 列已存在,忽略错误
|
|
|
- }
|
|
|
-
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE teachers ADD INDEX idx_street_id (street_id)");
|
|
|
- log.info("已添加idx_street_id索引到teachers表");
|
|
|
- } catch (Exception e) {
|
|
|
- // 索引已存在,忽略错误
|
|
|
- }
|
|
|
-
|
|
|
- // 迁移9: users表添加成长规划师相关字段
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE users ADD COLUMN teacher_no VARCHAR(50) COMMENT '成长规划师证书号'");
|
|
|
- log.info("已添加teacher_no列到users表");
|
|
|
- } catch (Exception e) {
|
|
|
- // 列已存在,忽略错误
|
|
|
- }
|
|
|
-
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE users ADD COLUMN teacher_photo VARCHAR(500) COMMENT '成长规划师照片'");
|
|
|
- log.info("已添加teacher_photo列到users表");
|
|
|
- } catch (Exception e) {
|
|
|
- // 列已存在,忽略错误
|
|
|
- }
|
|
|
-
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE users ADD COLUMN certificate_image VARCHAR(500) COMMENT '成长规划师证书照片'");
|
|
|
- log.info("已添加certificate_image列到users表");
|
|
|
- } catch (Exception e) {
|
|
|
- // 列已存在,忽略错误
|
|
|
- }
|
|
|
-
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE users ADD COLUMN teacher_status VARCHAR(20) DEFAULT 'pending' COMMENT '成长规划师审核状态'");
|
|
|
- log.info("已添加teacher_status列到users表");
|
|
|
- } catch (Exception e) {
|
|
|
- // 列已存在,忽略错误
|
|
|
- }
|
|
|
-
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE users ADD INDEX idx_teacher_status (teacher_status)");
|
|
|
- log.info("已添加idx_teacher_status索引到users表");
|
|
|
- } catch (Exception e) {
|
|
|
- // 索引已存在,忽略错误
|
|
|
- }
|
|
|
-
|
|
|
- // 迁移10: users表添加teacherFamilyIds字段(成长规划师关联的家庭ID列表)
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE users ADD COLUMN teacher_family_ids VARCHAR(500) COMMENT '成长规划师关联的家庭ID列表(逗号分隔)'");
|
|
|
- log.info("已添加teacher_family_ids列到users表");
|
|
|
- } catch (Exception e) {
|
|
|
- // 列已存在,忽略错误
|
|
|
- }
|
|
|
-
|
|
|
- // 迁移11: families表添加teacherId字段(绑定的成长规划师ID
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE families ADD COLUMN teacher_id BIGINT COMMENT '绑定的成长规划师ID'");
|
|
|
- log.info("已添加teacher_id列到families表");
|
|
|
- } catch (Exception e) {
|
|
|
- // 列已存在,忽略错误
|
|
|
- }
|
|
|
-
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE families ADD INDEX idx_teacher_id (teacher_id)");
|
|
|
- log.info("已添加idx_teacher_id索引到families表");
|
|
|
- } catch (Exception e) {
|
|
|
- // 索引已存在,忽略错误
|
|
|
- }
|
|
|
-
|
|
|
- // 迁移12: 创建 teacher_messages 表(家长咨询成长规划师)
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS teacher_messages (" +
|
|
|
- "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
- "family_id BIGINT NOT NULL COMMENT '家庭ID', " +
|
|
|
- "parent_id BIGINT NOT NULL COMMENT '家长用户ID', " +
|
|
|
- "teacher_id BIGINT NOT NULL COMMENT '成长规划师用户ID', " +
|
|
|
- "content TEXT NOT NULL COMMENT '消息内容', " +
|
|
|
- "reply_content TEXT COMMENT '回复内容', " +
|
|
|
- "status VARCHAR(20) DEFAULT 'pending' COMMENT '状态pending(待回/replied(已回', " +
|
|
|
- "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
- "replied_at DATETIME COMMENT '回复时间', " +
|
|
|
- "INDEX idx_family_id (family_id), " +
|
|
|
- "INDEX idx_teacher_id (teacher_id), " +
|
|
|
- "INDEX idx_parent_id (parent_id), " +
|
|
|
- "INDEX idx_status (status)" +
|
|
|
- ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4");
|
|
|
- log.info("已创建teacher_messages表");
|
|
|
- } catch (Exception e) {
|
|
|
- // 表已存在,忽略错误
|
|
|
- }
|
|
|
-
|
|
|
-// 迁移13: admin_task_templates表添加teacher_id字段
|
|
|
-try {
|
|
|
-jdbcTemplate.execute("ALTER TABLE admin_task_templates ADD COLUMN teacher_id BIGINT COMMENT '成长规划师ID'");
|
|
|
-log.info("已添加teacher_id列到admin_task_templates表");
|
|
|
-} catch (Exception e) {
|
|
|
-// 列已存在,忽略错误
|
|
|
-}
|
|
|
-
|
|
|
-// 迁移13b: admin_task_templates表添加新字段
|
|
|
-try {
|
|
|
-jdbcTemplate.execute("ALTER TABLE admin_task_templates ADD COLUMN complete_types VARCHAR(100) COMMENT '完成方式JSON'");
|
|
|
-log.info("已添加complete_types列到admin_task_templates表");
|
|
|
-} catch (Exception e) {
|
|
|
-// 列已存在,忽略错误
|
|
|
-}
|
|
|
-
|
|
|
-try {
|
|
|
-jdbcTemplate.execute("ALTER TABLE admin_task_templates ADD COLUMN duration INT COMMENT '预计完成时长(分钟)'");
|
|
|
-log.info("已添加duration列到admin_task_templates表");
|
|
|
-} catch (Exception e) {
|
|
|
-// 列已存在,忽略错误
|
|
|
-}
|
|
|
-
|
|
|
-try {
|
|
|
-jdbcTemplate.execute("ALTER TABLE admin_task_templates ADD COLUMN review_type VARCHAR(20) COMMENT '审核人类 creator/parent/teacher/ai'");
|
|
|
-log.info("已添加review_type列到admin_task_templates表");
|
|
|
-} catch (Exception e) {
|
|
|
-// 列已存在,忽略错误
|
|
|
-}
|
|
|
-
|
|
|
-try {
|
|
|
-jdbcTemplate.execute("ALTER TABLE admin_task_templates ADD COLUMN task_type VARCHAR(20) COMMENT '任务性质: onetime/recurring'");
|
|
|
-log.info("已添加task_type列到admin_task_templates表");
|
|
|
-} catch (Exception e) {
|
|
|
-// 列已存在,忽略错误
|
|
|
-}
|
|
|
-
|
|
|
-try {
|
|
|
-jdbcTemplate.execute("ALTER TABLE admin_task_templates ADD COLUMN frequency VARCHAR(20) COMMENT '频率: daily/weekly/monthly'");
|
|
|
-log.info("已添加frequency列到admin_task_templates表");
|
|
|
-} catch (Exception e) {
|
|
|
-// 列已存在,忽略错误
|
|
|
-}
|
|
|
-
|
|
|
-try {
|
|
|
-jdbcTemplate.execute("ALTER TABLE admin_task_templates ADD COLUMN max_frequency INT COMMENT '周期内最多完成次");
|
|
|
-log.info("已添加max_frequency列到admin_task_templates表");
|
|
|
-} catch (Exception e) {
|
|
|
-// 列已存在,忽略错误
|
|
|
-}
|
|
|
-
|
|
|
-// 迁移14: rewards表child_id字段允许NULL(模板奖励不需要child_id
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE rewards MODIFY COLUMN child_id BIGINT NULL COMMENT '孩子ID,模板奖励为NULL'");
|
|
|
- log.info("已修改rewards.child_id字段允许NULL");
|
|
|
- } catch (Exception e) {
|
|
|
- // 修改失败,忽略错误
|
|
|
- }
|
|
|
-
|
|
|
- // 迁移15: admins表添加缺失列
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE admins ADD COLUMN nickname VARCHAR(50) DEFAULT NULL AFTER password");
|
|
|
- log.info("已添加nickname列到admins表");
|
|
|
- } catch (Exception e) {
|
|
|
- // 列已存在,忽略错误
|
|
|
- }
|
|
|
-
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE admins ADD COLUMN email VARCHAR(100) DEFAULT NULL AFTER phone");
|
|
|
- log.info("已添加email列到admins表");
|
|
|
- } catch (Exception e) {
|
|
|
- // 列已存在,忽略错误
|
|
|
- }
|
|
|
-
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE admins MODIFY COLUMN status VARCHAR(20) DEFAULT 'active'");
|
|
|
- log.info("已修改admins.status字段类型");
|
|
|
- } catch (Exception e) {
|
|
|
- // 修改失败,忽略错误
|
|
|
- }
|
|
|
-
|
|
|
- // 迁移16: 创建用户操作日志
|
|
|
-try {
|
|
|
-jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS user_operation_logs (" +
|
|
|
-"id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
-"user_id BIGINT COMMENT \'用户ID\', " +
|
|
|
-"nickname VARCHAR(50) COMMENT \'用户昵称\', " +
|
|
|
-"operation_type VARCHAR(20) NOT NULL COMMENT \'操作类型: login/logout/create/update/delete/query等\', " +
|
|
|
-"description VARCHAR(500) COMMENT \'操作描述\', " +
|
|
|
-"request_path VARCHAR(200) COMMENT \'请求路径\', " +
|
|
|
-"request_method VARCHAR(10) COMMENT \'请求方法\', " +
|
|
|
-"request_params TEXT COMMENT \'请求参数(脱敏)\', " +
|
|
|
-"response_status INT COMMENT \'响应状态码\', " +
|
|
|
-"client_ip VARCHAR(50) COMMENT \'客户端IP\', " +
|
|
|
-"user_agent VARCHAR(500) COMMENT \'User-Agent\', " +
|
|
|
-"created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
-"INDEX idx_user_id (user_id), " +
|
|
|
-"INDEX idx_operation_type (operation_type), " +
|
|
|
-"INDEX idx_created_at (created_at)" +
|
|
|
-") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4");
|
|
|
-log.info("已创建user_operation_logs表");
|
|
|
-} catch (Exception e) {
|
|
|
-// 表已存在,忽略错误
|
|
|
-}
|
|
|
-
|
|
|
-// 迁移16: tasks表添加bonus_rule字段(POINT-002: 超额积分规则
|
|
|
-try {
|
|
|
-jdbcTemplate.execute("ALTER TABLE tasks ADD COLUMN bonus_rule VARCHAR(500) COMMENT \'超额积分规则JSON\'");
|
|
|
-log.info("已添加bonus_rule列到tasks表");
|
|
|
-} catch (Exception e) {
|
|
|
-// 列已存在,忽略错误
|
|
|
-}
|
|
|
-
|
|
|
-// 迁移17: tasks表添加points_expire_days字段(POINT-009: 积分有效期)
|
|
|
-try {
|
|
|
-jdbcTemplate.execute("ALTER TABLE tasks ADD COLUMN points_expire_days INT DEFAULT 0 COMMENT \'积分有效期天数,0表示永久有效\'");
|
|
|
-log.info("已添加points_expire_days列到tasks表");
|
|
|
-} catch (Exception e) {
|
|
|
-// 列已存在,忽略错误
|
|
|
-}
|
|
|
-
|
|
|
-// 迁移18: points_log表添加expire_at字段(POINT-009: 积分有效期)
|
|
|
-try {
|
|
|
-jdbcTemplate.execute("ALTER TABLE points_log ADD COLUMN expire_at DATE COMMENT \'积分过期日期\'");
|
|
|
-log.info("已添加expire_at列到points_log表");
|
|
|
-} catch (Exception e) {
|
|
|
-// 列已存在,忽略错误
|
|
|
-}
|
|
|
-
|
|
|
-// 迁移19: points_log表添加expired字段(POINT-009: 过期状态标记)
|
|
|
-try {
|
|
|
-jdbcTemplate.execute("ALTER TABLE points_log ADD COLUMN expired TINYINT DEFAULT 0 COMMENT \'是否已过 0是\'");
|
|
|
-log.info("已添加expired列到points_log表");
|
|
|
-} catch (Exception e) {
|
|
|
-// 列已存在,忽略错误
|
|
|
-}
|
|
|
-
|
|
|
-// 迁移20: tasks表添加template_id字段(TASK-004: 重复任务模板追踪
|
|
|
-try {
|
|
|
-jdbcTemplate.execute("ALTER TABLE tasks ADD COLUMN template_id BIGINT COMMENT \'模板任务ID\'");
|
|
|
-log.info("已添加template_id列到tasks表");
|
|
|
-} catch (Exception e) {
|
|
|
-// 列已存在,忽略错误
|
|
|
-}
|
|
|
-
|
|
|
- // 迁移21: tasks表添加is_template字段(TASK-004: 标记模板任务
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE tasks ADD COLUMN is_template TINYINT DEFAULT 0 COMMENT '是否为模板任");
|
|
|
- log.info("已添加is_template列到tasks表");
|
|
|
- } catch (Exception e) {
|
|
|
- // 列已存在,忽略错误
|
|
|
- }
|
|
|
-
|
|
|
- // 迁移22: 修复tasks表child_id和deadline允许NULL(支持多孩子和家长任务)
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE tasks MODIFY COLUMN child_id BIGINT NULL COMMENT '执行者ID'");
|
|
|
- log.info("已修改tasks.child_id允许NULL");
|
|
|
- } catch (Exception e) {
|
|
|
- // 修改失败,忽略错误
|
|
|
- }
|
|
|
-
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE tasks MODIFY COLUMN deadline DATETIME NULL COMMENT '截止时间'");
|
|
|
- log.info("已修改tasks.deadline允许NULL");
|
|
|
- } catch (Exception e) {
|
|
|
- // 修改失败,忽略错误
|
|
|
- }
|
|
|
-
|
|
|
- // 迁移23: tasks表添加新字段
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE tasks ADD COLUMN task_type VARCHAR(20) COMMENT '任务性质: onetime/recurring'");
|
|
|
- log.info("已添加task_type列到tasks表");
|
|
|
- } catch (Exception e) {
|
|
|
- // 列已存在,忽略错误
|
|
|
- }
|
|
|
-
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE tasks ADD COLUMN frequency VARCHAR(20) COMMENT '频率: daily/weekly/monthly'");
|
|
|
- log.info("已添加frequency列到tasks表");
|
|
|
- } catch (Exception e) {
|
|
|
- // 列已存在,忽略错误
|
|
|
- }
|
|
|
-
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE tasks ADD COLUMN max_frequency INT COMMENT '周期内最多完成次");
|
|
|
- log.info("已添加max_frequency列到tasks表");
|
|
|
- } catch (Exception e) {
|
|
|
- // 列已存在,忽略错误
|
|
|
- }
|
|
|
-
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE tasks ADD COLUMN complete_types VARCHAR(100) COMMENT '完成方式JSON'");
|
|
|
- log.info("已添加complete_types列到tasks表");
|
|
|
- } catch (Exception e) {
|
|
|
- // 列已存在,忽略错误
|
|
|
- }
|
|
|
-
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE tasks ADD COLUMN review_type VARCHAR(20) COMMENT '审核人类 creator/parent/teacher/ai'");
|
|
|
- log.info("已添加review_type列到tasks表");
|
|
|
- } catch (Exception e) {
|
|
|
- // 列已存在,忽略错误
|
|
|
- }
|
|
|
-
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE tasks ADD COLUMN earliest_start DATETIME COMMENT '最早开始时");
|
|
|
- log.info("已添加earliest_start列到tasks表");
|
|
|
- } catch (Exception e) {
|
|
|
- // 列已存在,忽略错误
|
|
|
- }
|
|
|
-
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE tasks ADD COLUMN latest_end DATETIME COMMENT '最晚结束时");
|
|
|
- log.info("已添加latest_end列到tasks表");
|
|
|
- } catch (Exception e) {
|
|
|
- // 列已存在,忽略错误
|
|
|
- }
|
|
|
-
|
|
|
- // 迁移24: tasks表添加minigame_code字段(小游戏任务
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE tasks ADD COLUMN minigame_code VARCHAR(50) DEFAULT NULL COMMENT '小游戏代 schulte/1a2b/sudoku'");
|
|
|
- log.info("已添加minigame_code列到tasks表");
|
|
|
- } catch (Exception e) {
|
|
|
- // 列已存在,忽略错误
|
|
|
- }
|
|
|
-
|
|
|
- // 迁移25: tasks表添加duration字段(TASK-011: 任务预计时长
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE tasks ADD COLUMN duration INT DEFAULT 0 COMMENT '预计时长(分钟)'");
|
|
|
- log.info("已添加duration列到tasks表");
|
|
|
- } catch (Exception e) {
|
|
|
- // 列已存在,忽略错误
|
|
|
- }
|
|
|
-
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE tasks ADD INDEX idx_minigame (minigame_code) WHERE minigame_code IS NOT NULL");
|
|
|
- log.info("已添加idx_minigame索引到tasks表");
|
|
|
- } catch (Exception e) {
|
|
|
- // 索引已存在,忽略错误
|
|
|
- }
|
|
|
-
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS trial_memberships (" +
|
|
|
- "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
- "user_id BIGINT NOT NULL COMMENT '用户ID', " +
|
|
|
- "family_id BIGINT NOT NULL COMMENT '家庭ID', " +
|
|
|
- "start_date DATETIME NOT NULL COMMENT '试用开始时, " +
|
|
|
- "end_date DATETIME NOT NULL COMMENT '试用结束时间', " +
|
|
|
- "status VARCHAR(20) DEFAULT 'PENDING' COMMENT '状态PENDING/ACTIVE/EXPIRED/CONVERTED', " +
|
|
|
- "trial_days INT DEFAULT 7 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_status (status), " +
|
|
|
- "INDEX idx_end_date (end_date)" +
|
|
|
- ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4");
|
|
|
- log.info("已创建trial_memberships表");
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("创建trial_memberships表可能已存在: {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- // assessment_appointments 新字段
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE assessment_appointments ADD COLUMN user_id BIGINT COMMENT '用户ID'");
|
|
|
- } catch (Exception e) {
|
|
|
- }
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE assessment_appointments ADD COLUMN guide_id BIGINT COMMENT '成长规划师ID'");
|
|
|
- } catch (Exception e) {
|
|
|
- }
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE assessment_appointments ADD COLUMN package_id BIGINT COMMENT '套餐ID'");
|
|
|
- } catch (Exception e) {
|
|
|
- }
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE assessment_appointments ADD COLUMN remark VARCHAR(500) COMMENT '备注'");
|
|
|
- } catch (Exception e) {
|
|
|
- }
|
|
|
-
|
|
|
- // assessment_orders
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS assessment_orders (" +
|
|
|
- "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
- "order_no VARCHAR(64) NOT NULL UNIQUE, " +
|
|
|
- "family_id BIGINT NOT NULL, " +
|
|
|
- "user_id BIGINT NOT NULL, " +
|
|
|
- "child_id BIGINT, " +
|
|
|
- "guide_id BIGINT NOT NULL, " +
|
|
|
- "package_id BIGINT NOT NULL, " +
|
|
|
- "guide_name VARCHAR(50), " +
|
|
|
- "package_name VARCHAR(100), " +
|
|
|
- "total_price BIGINT DEFAULT 0, " +
|
|
|
- "discount_amount BIGINT DEFAULT 0, " +
|
|
|
- "actual_price BIGINT DEFAULT 0, " +
|
|
|
- "status VARCHAR(20) DEFAULT 'pending', " +
|
|
|
- "pay_time VARCHAR(50), " +
|
|
|
- "pay_type VARCHAR(20), " +
|
|
|
- "transaction_id VARCHAR(64), " +
|
|
|
- "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
- "updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, " +
|
|
|
- "INDEX idx_family (family_id), " +
|
|
|
- "INDEX idx_guide (guide_id), " +
|
|
|
- "INDEX idx_status (status)) " +
|
|
|
- "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='评估订单");
|
|
|
- } catch (Exception e) {
|
|
|
- }
|
|
|
-
|
|
|
- // 迁移26: guide_applications表添加address和bank_card字段
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE guide_applications ADD COLUMN address VARCHAR(500) COMMENT '地址'");
|
|
|
- log.info("已添加address列到guide_applications表");
|
|
|
- } catch (Exception e) {
|
|
|
- // 列已存在,忽略错误
|
|
|
- }
|
|
|
-
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE guide_applications ADD COLUMN bank_card VARCHAR(50) COMMENT '银行卡号'");
|
|
|
- log.info("已添加bank_card列到guide_applications表");
|
|
|
- } catch (Exception e) {
|
|
|
- // 列已存在,忽略错误
|
|
|
- }
|
|
|
-
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE guide_applications ADD COLUMN bank_name VARCHAR(100) COMMENT '开户行'");
|
|
|
- log.info("已添加bank_name列到guide_applications表");
|
|
|
- } catch (Exception e) {
|
|
|
- // 列已存在,忽略错误
|
|
|
- }
|
|
|
-
|
|
|
- // 迁移27: users表添加address字段(成长规划师地址
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE users ADD COLUMN address VARCHAR(500) COMMENT '地址'");
|
|
|
- log.info("已添加address列到users表");
|
|
|
- } catch (Exception e) {
|
|
|
- // 列已存在,忽略错误
|
|
|
- }
|
|
|
-
|
|
|
- // 迁移28: 创建guide_application_records表(申请过程记录
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS guide_application_records (" +
|
|
|
- "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
- "application_id BIGINT NOT NULL COMMENT '关联申请ID', " +
|
|
|
- "user_id BIGINT NOT NULL COMMENT '操作人用户ID', " +
|
|
|
- "action VARCHAR(50) NOT NULL COMMENT '操作: submit/approve/reject/withdraw/resubmit', " +
|
|
|
- "from_status VARCHAR(20) COMMENT '操作前状态, " +
|
|
|
- "to_status VARCHAR(20) NOT NULL COMMENT '操作后状态, " +
|
|
|
- "remark VARCHAR(500) COMMENT '备注/原因', " +
|
|
|
- "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
- "INDEX idx_application_id (application_id), " +
|
|
|
- "INDEX idx_user_id (user_id), " +
|
|
|
- "INDEX idx_action (action)" +
|
|
|
- ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='成长规划师申请过程记录表'");
|
|
|
- log.info("已创建guide_application_records表");
|
|
|
- } catch (Exception e) {
|
|
|
- // 表已存在,忽略错误
|
|
|
- }
|
|
|
-
|
|
|
- // 迁移20: children表添加system_points字段
|
|
|
- try {
|
|
|
- Integer exists = jdbcTemplate.queryForObject(
|
|
|
- "SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'children' AND COLUMN_NAME = 'system_points'",
|
|
|
- Integer.class);
|
|
|
- if (exists == null || exists == 0) {
|
|
|
- jdbcTemplate.execute("ALTER TABLE children ADD COLUMN system_points INT DEFAULT 0 COMMENT '系统积分'");
|
|
|
- log.info("已添加system_points列到children表");
|
|
|
- } else {
|
|
|
- log.info("system_points列已存在,跳过表");
|
|
|
- }
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("检查添加system_points列失败 {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- // 迁移21: points_log表添加category字段
|
|
|
- try {
|
|
|
- Integer exists = jdbcTemplate.queryForObject(
|
|
|
- "SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'points_log' AND COLUMN_NAME = 'category'",
|
|
|
- Integer.class);
|
|
|
- if (exists == null || exists == 0) {
|
|
|
- jdbcTemplate.execute("ALTER TABLE points_log ADD COLUMN category VARCHAR(20) DEFAULT 'task' COMMENT '积分分类: task任务积分, system系统积分'");
|
|
|
- log.info("已添加category列到points_log表");
|
|
|
- } else {
|
|
|
- log.info("category列已存在,跳过表");
|
|
|
- }
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("检查添加category列失败 {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- // 迁移22: 创建invite_card
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS invite_card (" +
|
|
|
- "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
- "type VARCHAR(20) NOT NULL COMMENT 'child/family/guide', " +
|
|
|
- "code VARCHAR(64) NOT NULL UNIQUE COMMENT '邀请码', " +
|
|
|
- "ref_id BIGINT NOT NULL COMMENT '关联ID(childId/familyId)', " +
|
|
|
- "creator_id BIGINT NOT NULL COMMENT '创建, " +
|
|
|
- "expires_at DATETIME NOT NULL, " +
|
|
|
- "used TINYINT DEFAULT 0, " +
|
|
|
- "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
- "updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, " +
|
|
|
- "INDEX idx_code (code), " +
|
|
|
- "INDEX idx_ref_type (ref_id, type)" +
|
|
|
- ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='邀请卡");
|
|
|
- log.info("已创建invite_card表");
|
|
|
- } catch (Exception e) {
|
|
|
- // 表已存在,忽略错误
|
|
|
- }
|
|
|
-
|
|
|
- // 迁移23: 创建wishes
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS wishes (" +
|
|
|
- "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
- "family_id BIGINT COMMENT '家庭ID', " +
|
|
|
- "child_id BIGINT COMMENT '目标孩子ID', " +
|
|
|
- "created_by BIGINT COMMENT '创建者用户ID', " +
|
|
|
- "creator_type VARCHAR(20) COMMENT '创建者类 child/parent', " +
|
|
|
- "title VARCHAR(200) NOT NULL COMMENT '心愿标题', " +
|
|
|
- "description VARCHAR(500) COMMENT '心愿描述', " +
|
|
|
- "category VARCHAR(32) COMMENT '心愿分类', " +
|
|
|
- "points_required INT COMMENT '所需积分', " +
|
|
|
- "points_set_by BIGINT COMMENT '定价者ID', " +
|
|
|
- "points_set_at DATETIME COMMENT '定价时间', " +
|
|
|
- "status VARCHAR(32) DEFAULT 'active' COMMENT '状态 draft/active/pending_exchange/approved/exchanging/fulfilled/cancelled/rejected', " +
|
|
|
- "exchange_requested_at DATETIME COMMENT '申请兑换时间', " +
|
|
|
- "exchange_approved_by BIGINT COMMENT '批准者ID', " +
|
|
|
- "exchange_approved_at DATETIME COMMENT '批准时间', " +
|
|
|
- "points_deducted_at DATETIME COMMENT '扣积分时, " +
|
|
|
- "fulfilled_by BIGINT COMMENT '兑现者ID', " +
|
|
|
- "fulfilled_at DATETIME COMMENT '兑现时间', " +
|
|
|
- "child_confirmed_at DATETIME COMMENT '孩子确认收货时间', " +
|
|
|
- "rejected_by BIGINT COMMENT '拒绝者ID', " +
|
|
|
- "rejected_at DATETIME COMMENT '拒绝时间', " +
|
|
|
- "reject_reason VARCHAR(500) COMMENT '拒绝原因', " +
|
|
|
- "reject_count INT DEFAULT 0 COMMENT '被拒绝次, " +
|
|
|
- "cancelled_by BIGINT COMMENT '取消者ID', " +
|
|
|
- "cancelled_at DATETIME COMMENT '取消时间', " +
|
|
|
- "cancel_reason VARCHAR(500) 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_child_id (child_id), " +
|
|
|
- "INDEX idx_status (status)" +
|
|
|
- ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='心愿单表'");
|
|
|
- log.info("已创建wishes表");
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("创建wishes表可能已存在: {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- // 迁移24: users表添加个人信息补充字段
|
|
|
- try {
|
|
|
- Integer exists = jdbcTemplate.queryForObject(
|
|
|
- "SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'users' AND COLUMN_NAME = 'ethnicity'",
|
|
|
- Integer.class);
|
|
|
- if (exists == null || exists == 0) {
|
|
|
- jdbcTemplate.execute("ALTER TABLE users " +
|
|
|
- "ADD COLUMN ethnicity VARCHAR(32) DEFAULT NULL COMMENT '民族', " +
|
|
|
- "ADD COLUMN blood_type VARCHAR(16) DEFAULT NULL COMMENT '血, " +
|
|
|
- "ADD COLUMN highest_education VARCHAR(32) DEFAULT NULL COMMENT '最高学, " +
|
|
|
- "ADD COLUMN marital_status VARCHAR(16) DEFAULT NULL COMMENT '婚姻状态, " +
|
|
|
- "ADD COLUMN family_role VARCHAR(16) DEFAULT NULL COMMENT '家长身份: 爸爸/妈妈/爷爷/奶奶/姥爷/姥姥/其他'");
|
|
|
- log.info("已添加个人信息补充字段到users表");
|
|
|
- } else {
|
|
|
- log.info("个人信息补充字段已存在,跳过");
|
|
|
- }
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("检查添加个人信息补充字段失败: {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- // Phase 0: 浠艾福俱乐部 服务商字段
|
|
|
- try {
|
|
|
- Integer exists = jdbcTemplate.queryForObject(
|
|
|
- "SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'users' AND COLUMN_NAME = 'vendor_type'",
|
|
|
- Integer.class);
|
|
|
- if (exists == null || exists == 0) {
|
|
|
- jdbcTemplate.execute("ALTER TABLE users ADD COLUMN vendor_type VARCHAR(32) DEFAULT NULL COMMENT '服务商类 planner/activity_provider/product_supplier'");
|
|
|
- jdbcTemplate.execute("ALTER TABLE users ADD COLUMN vendor_status VARCHAR(16) DEFAULT NULL COMMENT '服务商审核状态 pending/approved/rejected'");
|
|
|
- jdbcTemplate.execute("ALTER TABLE users ADD COLUMN vendor_reject_reason VARCHAR(500) DEFAULT NULL COMMENT '审核拒绝原因'");
|
|
|
- jdbcTemplate.execute("ALTER TABLE users ADD COLUMN is_family_admin TINYINT DEFAULT 0 COMMENT '是否家庭管理");
|
|
|
- jdbcTemplate.execute("ALTER TABLE users ADD COLUMN vendor_info VARCHAR(2000) DEFAULT NULL COMMENT '服务商资质信JSON)'");
|
|
|
- log.info("已添加浠艾福俱乐部服务商字段到users表");
|
|
|
- } else {
|
|
|
- log.info("浠艾福俱乐部服务商字段已存在,跳过表");
|
|
|
- }
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("检查添加服务商字段失败 {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- // Phase 0: 迁移已有规划师到服务商体
|
|
|
- try {
|
|
|
- int updated = jdbcTemplate.update(
|
|
|
- "UPDATE users SET vendor_type = 'planner', vendor_status = 'approved' WHERE teacher_status = 'approved' AND vendor_type IS NULL"
|
|
|
- );
|
|
|
- if (updated > 0) {
|
|
|
- log.info("已迁移{} 名规划师到服务商体系", updated);
|
|
|
- }
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("迁移规划师到服务商体系失败 {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- // ====== Phase 1: 浠艾福俱乐部商品系统 ======
|
|
|
-
|
|
|
- // 商品
|
|
|
- try {
|
|
|
- jdbcTemplate.execute(
|
|
|
- "CREATE TABLE IF NOT EXISTS products (" +
|
|
|
- " id BIGINT AUTO_INCREMENT PRIMARY KEY," +
|
|
|
- " name VARCHAR(200) NOT NULL," +
|
|
|
- " description VARCHAR(1000)," +
|
|
|
- " intro TEXT," +
|
|
|
- " cover_image VARCHAR(500)," +
|
|
|
- " images TEXT," +
|
|
|
- " price INT NOT NULL DEFAULT 0 COMMENT '价格('," +
|
|
|
- " stock INT NOT NULL DEFAULT 0," +
|
|
|
- " sales_count INT NOT NULL DEFAULT 0," +
|
|
|
- " product_type VARCHAR(32) NOT NULL COMMENT 'activity/course/physical/digital/service'," +
|
|
|
- " vendor_id BIGINT," +
|
|
|
- " vendor_name VARCHAR(100)," +
|
|
|
- " status VARCHAR(32) NOT NULL DEFAULT 'draft' COMMENT 'draft/pending/approved/rejected/on_shelf/off_shelf'," +
|
|
|
- " reject_reason VARCHAR(500)," +
|
|
|
- " member_price INT COMMENT '会员'," +
|
|
|
- " profit_rate INT DEFAULT 0 COMMENT '平台利润千分'," +
|
|
|
- " member_eligible VARCHAR(32) DEFAULT 'none' COMMENT 'all/budget/none'," +
|
|
|
- " domain VARCHAR(32) COMMENT 'action/mind/body/wisdom/wealth'," +
|
|
|
- " external_source VARCHAR(64)," +
|
|
|
- " external_id VARCHAR(128)," +
|
|
|
- " external_data TEXT," +
|
|
|
- " created_at DATETIME DEFAULT CURRENT_TIMESTAMP," +
|
|
|
- " updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP" +
|
|
|
- ")"
|
|
|
- );
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("创建products表失败 {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- // 商品SKU
|
|
|
- try {
|
|
|
- jdbcTemplate.execute(
|
|
|
- "CREATE TABLE IF NOT EXISTS product_skus (" +
|
|
|
- " id INT NOT NULL AUTO_INCREMENT," +
|
|
|
- " product_id INT NOT NULL COMMENT '商品ID'," +
|
|
|
- " sku_code VARCHAR(64) NOT NULL COMMENT 'SKU编码'," +
|
|
|
- " specs JSON NOT NULL COMMENT '规格JSON'," +
|
|
|
- " price DECIMAL(10,2) NOT NULL COMMENT 'SKU价格'," +
|
|
|
- " stock INT NOT NULL DEFAULT 0 COMMENT '库存'," +
|
|
|
- " original_price DECIMAL(10,2) COMMENT '原价/划线," +
|
|
|
- " image VARCHAR(512) COMMENT 'SKU图片'," +
|
|
|
- " enabled TINYINT(1) NOT NULL DEFAULT 1," +
|
|
|
- " create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP," +
|
|
|
- " update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP," +
|
|
|
- " PRIMARY KEY (id)," +
|
|
|
- " KEY idx_product_id (product_id)" +
|
|
|
- ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品SKU'"
|
|
|
- );
|
|
|
- log.info("已创建product_skus表");
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("创建product_skus表失败 {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- // 订单
|
|
|
- try {
|
|
|
- jdbcTemplate.execute(
|
|
|
- "CREATE TABLE IF NOT EXISTS product_orders (" +
|
|
|
- " id BIGINT AUTO_INCREMENT PRIMARY KEY," +
|
|
|
- " order_no VARCHAR(64) NOT NULL UNIQUE," +
|
|
|
- " product_id BIGINT NOT NULL," +
|
|
|
- " product_name VARCHAR(200)," +
|
|
|
- " product_type VARCHAR(32)," +
|
|
|
- " buyer_id BIGINT NOT NULL," +
|
|
|
- " family_id BIGINT," +
|
|
|
- " quantity INT NOT NULL DEFAULT 1," +
|
|
|
- " unit_price INT NOT NULL COMMENT '单价('," +
|
|
|
- " total_amount INT NOT NULL COMMENT '总金'," +
|
|
|
- " status VARCHAR(32) NOT NULL DEFAULT 'pending' COMMENT 'pending/paid/completed/cancelled/refunded'," +
|
|
|
- " payment_method VARCHAR(32) COMMENT 'wechat/points/membership'," +
|
|
|
- " remark VARCHAR(500)," +
|
|
|
- " paid_at DATETIME," +
|
|
|
- " created_at DATETIME DEFAULT CURRENT_TIMESTAMP," +
|
|
|
- " updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP" +
|
|
|
- ")"
|
|
|
- );
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("创建product_orders表失败 {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- // 系统配置
|
|
|
- try {
|
|
|
- jdbcTemplate.execute(
|
|
|
- "CREATE TABLE IF NOT EXISTS sys_config (" +
|
|
|
- " id BIGINT AUTO_INCREMENT PRIMARY KEY," +
|
|
|
- " config_key VARCHAR(64) NOT NULL UNIQUE," +
|
|
|
- " config_value TEXT," +
|
|
|
- " description VARCHAR(500)," +
|
|
|
- " updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP" +
|
|
|
- ")"
|
|
|
- );
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("创建sys_config表失败 {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- // Migration: product_orders add logistics fields
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE product_orders ADD COLUMN logistics_no VARCHAR(64) COMMENT '物流单号'");
|
|
|
- log.info("已添加logistics_no列到product_orders表");
|
|
|
- } catch (Exception e) { }
|
|
|
-
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE product_orders ADD COLUMN logistics_company VARCHAR(100) COMMENT '物流公司'");
|
|
|
- log.info("已添加logistics_company列到product_orders表");
|
|
|
- } catch (Exception e) { }
|
|
|
-
|
|
|
- // SysConfig 种子数据
|
|
|
- try {
|
|
|
- insertSysConfigSeed("points_exchange_rate", "1", "积分兑换汇率积分=1分,.01元)");
|
|
|
- insertSysConfigSeed("points_deduct_cap", "0.5", "积分抵扣上限0%");
|
|
|
- insertSysConfigSeed("member_fee_standard", "1314", "标准会员年费(元)");
|
|
|
- insertSysConfigSeed("member_fee_premium", "3650", "高级会员年费(元)");
|
|
|
- insertSysConfigSeed("member_eligible_budget", "1314", "等值商品选择预算上限(元)");
|
|
|
- insertSysConfigSeed("renewal_discount_rate", "0.85", "续费折扣");
|
|
|
- insertSysConfigSeed("member_discount_standard", "0.9", "标准会员折扣");
|
|
|
- insertSysConfigSeed("member_discount_premium", "0.8", "高级会员折扣");
|
|
|
- insertSysConfigSeed("member_fee_family", "36500", "家庭会员年费(分)");
|
|
|
- insertSysConfigSeed("member_upgrade_threshold", "131400", "自动升级家庭会员累计消费门槛(分)");
|
|
|
- insertSysConfigSeed("member_discount_family", "9000", "家庭会员商品折扣(bps)");
|
|
|
- insertSysConfigSeed("commission_member_l1_rate", "2000", "推荐家庭会员L1佣金bps)");
|
|
|
- insertSysConfigSeed("commission_member_l2_rate", "500", "推荐家庭会员L2佣金bps)");
|
|
|
- insertSysConfigSeed("commission_product_profit_share", "1000", "下线商品购买利润分成(bps)");
|
|
|
- insertSysConfigSeed("commission_service_rate", "1000", "下线服务购买佣金bps)");
|
|
|
- insertSysConfigSeed("commission_event_fixed", "2000", "推荐新用户报名活动固定佣");
|
|
|
- insertSysConfigSeed("withdraw_min_amount", "10000", "最低提现金");
|
|
|
- insertSysConfigSeed("withdraw_tax_rate", "2000", "佣金税率(bps)");
|
|
|
- insertSysConfigSeed("withdraw_fee_free_threshold", "80000", "免税额度(");
|
|
|
- insertSysConfigSeed("article_energy_rate_normal", "1", "普通文章能量汇00");
|
|
|
- insertSysConfigSeed("article_energy_rate_premium", "2", "优质文章能量汇率(00");
|
|
|
- insertSysConfigSeed("article_energy_rate_original", "3", "原创文章能量汇率(00");
|
|
|
- insertSysConfigSeed("article_reading_min_seconds", "60", "文章阅读达标最少秒");
|
|
|
- insertSysConfigSeed("activity_checkin_default_points", "5", "活动签到默认积分");
|
|
|
- insertSysConfigSeed("health_checkin_points", "5", "健康打卡积分");
|
|
|
- insertSysConfigSeed("finance_checkin_points", "5", "理财打卡积分");
|
|
|
- insertSysConfigSeed("trial_enabled", "true", "是否启用会员试用");
|
|
|
- insertSysConfigSeed("trial_days", "7", "试用天数");
|
|
|
- insertSysConfigSeed("trial_features", "[\"free_activities\",\"free_courses\",\"teacher_consultation\"]", "试用期间可用功能");
|
|
|
- insertSysConfigSeed("health_dimension_weights", "{\"growth\":0.20,\"sleep\":0.15,\"vision\":0.10,\"immunity\":0.15,\"nutrition\":0.15,\"gut\":0.10,\"exercise\":0.15}", "7维健康→身能量权重配置");
|
|
|
- insertSysConfigSeed("health_dimension_sync_factor", "0.5", "7维健康同步至身能量系数");
|
|
|
- log.info("SysConfig 种子数据已加载表");
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("SysConfig 种子数据初始化失败 {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- // Product 种子数据 个示例商品,覆盖全部 5 productType
|
|
|
- try {
|
|
|
- insertProductSeed("亲子关系工作坊", "线下半日工作坊,重建家庭对话模式", "activity", 38800, "action", "all");
|
|
|
- insertProductSeed("情绪管理21天训练营", "21天视频课程,识别并管理情绪", "course", 19900, "mind", "all");
|
|
|
- insertProductSeed("肠道菌群检测套装", "家用检查+ 报告解读", "physical", 59900, "body", "budget");
|
|
|
- insertProductSeed("儿童多模态认知测评", "在线测评 + 个性化建议", "digital", 9900, "wisdom", "none");
|
|
|
- insertProductSeed("成长规划咨询", "60分钟专业规划", "service", 50000, "wealth", "budget");
|
|
|
- log.info("Product 种子数据已加载表");
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("Product 种子数据初始化失败 {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- // ====== Phase 2: 内容区块可见性配======
|
|
|
-
|
|
|
- // 内容区块配置
|
|
|
- try {
|
|
|
- jdbcTemplate.execute(
|
|
|
- "CREATE TABLE IF NOT EXISTS content_sections (" +
|
|
|
- " id BIGINT AUTO_INCREMENT PRIMARY KEY," +
|
|
|
- " page_key VARCHAR(50) NOT NULL," +
|
|
|
- " section_key VARCHAR(50) NOT NULL," +
|
|
|
- " title VARCHAR(100)," +
|
|
|
- " allowed_roles VARCHAR(255) DEFAULT '[\"anonymous\"]'," +
|
|
|
- " sort_order INT DEFAULT 0," +
|
|
|
- " status VARCHAR(20) DEFAULT 'active'," +
|
|
|
- " created_at DATETIME DEFAULT CURRENT_TIMESTAMP," +
|
|
|
- " updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP" +
|
|
|
- ")"
|
|
|
- );
|
|
|
- log.info("content_sections 表已创建");
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("创建 content_sections 表失败 {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- // ContentSection 种子数据
|
|
|
- try {
|
|
|
- insertContentSectionSeed("mind", "func_entries", "功能入口", "[\"anonymous\"]", 1);
|
|
|
- insertContentSectionSeed("mind", "daily_tip", "每日心理", "[\"anonymous\"]", 2);
|
|
|
- insertContentSectionSeed("mind", "recommended_reading", "推荐阅读", "[\"anonymous\"]", 3);
|
|
|
- insertContentSectionSeed("mind", "premium_content", "会员专属内容", "[\"parent\",\"teacher\"]", 4);
|
|
|
- insertContentSectionSeed("mind", "mind_tasks", "心智任务", "[\"parent\",\"child\"]", 5);
|
|
|
- insertContentSectionSeed("mind", "related_activities", "相关活动", "[\"parent\",\"child\"]", 6);
|
|
|
- insertContentSectionSeed("mind", "purchased_courses", "已购课程", "[\"parent\",\"child\"]", 7);
|
|
|
- insertContentSectionSeed("body", "func_entries", "功能入口", "[\"anonymous\"]", 1);
|
|
|
- insertContentSectionSeed("body", "daily_stats", "今日数据", "[\"anonymous\"]", 2);
|
|
|
- insertContentSectionSeed("body", "health_tips", "健康小贴士", "[\"anonymous\"]", 3);
|
|
|
- insertContentSectionSeed("body", "teacher_tips", "规划师推荐", "[\"teacher\"]", 4);
|
|
|
- log.info("ContentSection 种子数据已加载表");
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("ContentSection 种子数据初始化失败 {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- // ====== 文章内容系统种子数据 ======
|
|
|
-
|
|
|
- // 文章分类种子数据
|
|
|
- try {
|
|
|
- Integer catCount = jdbcTemplate.queryForObject(
|
|
|
- "SELECT COUNT(*) FROM article_categories WHERE id IN (1,2,3,4,5)", Integer.class);
|
|
|
- if (catCount == null || catCount == 0) {
|
|
|
- jdbcTemplate.execute("INSERT IGNORE INTO article_categories (id, name, icon, color, sort_order, status) VALUES " +
|
|
|
- "(1, '心理健康', '🧠', '#5B9BD5', 1, 1), " +
|
|
|
- "(2, '情绪管理', '💖', '#FF6B35', 2, 1), " +
|
|
|
- "(3, '亲子教育', '👨👩👧, '#4CAF50', 3, 1), " +
|
|
|
- "(4, '学习, '📚', '#FFD700', 4, 1), " +
|
|
|
- "(5, '专注, '🎯', '#8D6E63', 5, 1)");
|
|
|
- log.info("文章分类种子数据已加载表");
|
|
|
- }
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("文章分类种子数据初始化失败 {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- // ====== 分佣裂变系统 ======
|
|
|
-
|
|
|
- // 迁移: users表添加推荐人字段
|
|
|
- try {
|
|
|
- Integer exists = jdbcTemplate.queryForObject(
|
|
|
- "SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'users' AND COLUMN_NAME = 'referrer_id'",
|
|
|
- Integer.class);
|
|
|
- if (exists == null || exists == 0) {
|
|
|
- jdbcTemplate.execute("ALTER TABLE users ADD COLUMN referrer_id BIGINT DEFAULT NULL COMMENT '推荐人ID'");
|
|
|
- jdbcTemplate.execute("ALTER TABLE users ADD COLUMN referral_code VARCHAR(32) DEFAULT NULL COMMENT '个人邀请码'");
|
|
|
- jdbcTemplate.execute("ALTER TABLE users ADD INDEX idx_referral_code (referral_code)");
|
|
|
- log.info("已添加分佣字段到users表");
|
|
|
- } else {
|
|
|
- log.info("分佣金字段已存在,跳过表");
|
|
|
- }
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("检查添加分佣金字段失败 {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- // 迁移: products表添加利润率字段
|
|
|
- try {
|
|
|
- Integer exists = jdbcTemplate.queryForObject(
|
|
|
- "SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'products' AND COLUMN_NAME = 'profit_rate'",
|
|
|
- Integer.class);
|
|
|
- if (exists == null || exists == 0) {
|
|
|
- jdbcTemplate.execute("ALTER TABLE products ADD COLUMN profit_rate INT DEFAULT 0 COMMENT '平台利润bps,100=1%)'");
|
|
|
- log.info("已添加profit_rate列到products表");
|
|
|
- } else {
|
|
|
- log.info("profit_rate列已存在,跳过表");
|
|
|
- }
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("检查添加profit_rate列失败 {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- // 新表: commission_records (佣金记录)
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS commission_records (" +
|
|
|
- "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
- "order_id BIGINT NOT NULL COMMENT '订单ID', " +
|
|
|
- "order_type VARCHAR(32) NOT NULL COMMENT '订单类型: membership/product/assessment/package', " +
|
|
|
- "referrer_id BIGINT NOT NULL COMMENT '推荐人ID', " +
|
|
|
- "buyer_id BIGINT NOT NULL COMMENT '购买人ID', " +
|
|
|
- "commission_type VARCHAR(16) NOT NULL COMMENT '佣金类型: member/profit', " +
|
|
|
- "order_amount INT NOT NULL COMMENT '订单金额(', " +
|
|
|
- "profit_rate INT DEFAULT 0 COMMENT '利润bps,100=1%)', " +
|
|
|
- "commission_amount INT NOT NULL COMMENT '佣金金额(', " +
|
|
|
- "status VARCHAR(16) NOT NULL DEFAULT 'settled' COMMENT '状态 pending/settled', " +
|
|
|
- "remark VARCHAR(255) DEFAULT NULL, " +
|
|
|
- "created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
- "INDEX idx_referrer (referrer_id), " +
|
|
|
- "INDEX idx_buyer (buyer_id), " +
|
|
|
- "INDEX idx_order (order_id, order_type)" +
|
|
|
- ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4");
|
|
|
- log.info("已创建commission_records表");
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("创建commission_records表失败 {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- // 新表: withdrawal_requests (提现申请)
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS withdrawal_requests (" +
|
|
|
- "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
- "user_id BIGINT NOT NULL COMMENT '用户ID', " +
|
|
|
- "amount INT NOT NULL COMMENT '提现金额(', " +
|
|
|
- "account_info TEXT COMMENT '账户信息(JSON)', " +
|
|
|
- "status VARCHAR(16) NOT NULL DEFAULT 'pending' COMMENT '状态 pending/approved/rejected', " +
|
|
|
- "audit_by BIGINT DEFAULT NULL COMMENT '审核人ID', " +
|
|
|
- "audit_at DATETIME DEFAULT NULL COMMENT '审核时间', " +
|
|
|
- "remark VARCHAR(255) DEFAULT NULL COMMENT '审核备注', " +
|
|
|
- "created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
- "INDEX idx_user (user_id), " +
|
|
|
- "INDEX idx_status (status)" +
|
|
|
- ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4");
|
|
|
- log.info("已创建withdrawal_requests表");
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("创建withdrawal_requests表失败 {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- // ====== 会员体系 & 分佣扩展 ======
|
|
|
-
|
|
|
- // 迁移: users表添加会员字段
|
|
|
- try {
|
|
|
- Integer exists = jdbcTemplate.queryForObject(
|
|
|
- "SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'users' AND COLUMN_NAME = 'member_level'",
|
|
|
- Integer.class);
|
|
|
- if (exists == null || exists == 0) {
|
|
|
- jdbcTemplate.execute("ALTER TABLE users ADD COLUMN member_level VARCHAR(20) DEFAULT 'FREE' COMMENT '会员等级: FREE/FAMILY/PROVIDER'");
|
|
|
- jdbcTemplate.execute("ALTER TABLE users ADD COLUMN member_expire_time DATETIME DEFAULT NULL COMMENT '家庭会员到期时间'");
|
|
|
- jdbcTemplate.execute("ALTER TABLE users ADD COLUMN total_consumption BIGINT DEFAULT 0 COMMENT '累计消费金额(分");
|
|
|
- jdbcTemplate.execute("ALTER TABLE users ADD COLUMN upgrade_from_consumption TINYINT DEFAULT 0 COMMENT '是否通过累计消费自动升级'");
|
|
|
- jdbcTemplate.execute("ALTER TABLE users ADD COLUMN direct_count INT DEFAULT 0 COMMENT '直接推荐人数'");
|
|
|
- jdbcTemplate.execute("ALTER TABLE users ADD COLUMN indirect_count INT DEFAULT 0 COMMENT '间接推荐人数'");
|
|
|
- jdbcTemplate.execute("ALTER TABLE users ADD COLUMN total_commission_earned BIGINT DEFAULT 0 COMMENT '累计获得佣金(分");
|
|
|
- log.info("已添加会员字段到users表");
|
|
|
- } else {
|
|
|
- log.info("会员字段已存在,跳过");
|
|
|
- }
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("检查添加会员字段失败: {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- // 迁移: commission_records表添加扩展字段
|
|
|
- try {
|
|
|
- Integer exists = jdbcTemplate.queryForObject(
|
|
|
- "SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'commission_records' AND COLUMN_NAME = 'level'",
|
|
|
- Integer.class);
|
|
|
- if (exists == null || exists == 0) {
|
|
|
- jdbcTemplate.execute("ALTER TABLE commission_records ADD COLUMN level INT DEFAULT 1 COMMENT '佣金层级: 1=直接推荐, 2=间接推荐'");
|
|
|
- jdbcTemplate.execute("ALTER TABLE commission_records ADD COLUMN settled_at DATETIME DEFAULT NULL COMMENT '结算时间'");
|
|
|
- jdbcTemplate.execute("ALTER TABLE commission_records ADD COLUMN withdrawn_at DATETIME DEFAULT NULL COMMENT '提现时间'");
|
|
|
- log.info("已添加扩展字段到commission_records表");
|
|
|
- } else {
|
|
|
- log.info("commission_records扩展字段已存在,跳过");
|
|
|
- }
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("检查添加commission_records扩展字段失败: {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- // 新表: member_upgrade_records (会员升级记录)
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS member_upgrade_records (" +
|
|
|
- "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
- "user_id BIGINT NOT NULL COMMENT '用户ID', " +
|
|
|
- "from_level VARCHAR(20) NOT NULL COMMENT '升级前等, " +
|
|
|
- "to_level VARCHAR(20) NOT NULL COMMENT '升级后等, " +
|
|
|
- "upgrade_type VARCHAR(20) NOT NULL COMMENT '升级类型: pay/consumption/admin', " +
|
|
|
- "order_id BIGINT DEFAULT NULL COMMENT '关联支付订单ID', " +
|
|
|
- "consumption_amount BIGINT DEFAULT NULL COMMENT '触发升级的累计消费金, " +
|
|
|
- "expire_time DATETIME DEFAULT NULL COMMENT '家庭会员到期时间', " +
|
|
|
- "created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
- "INDEX idx_user (user_id), " +
|
|
|
- "INDEX idx_order (order_id)" +
|
|
|
- ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='会员升级记录'");
|
|
|
- log.info("已创建member_upgrade_records表");
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("创建member_upgrade_records表失败 {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- // ====== 文章内容系统 ======
|
|
|
-
|
|
|
- // 文章分类
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS article_categories (" +
|
|
|
- "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
- "name VARCHAR(50) NOT NULL COMMENT '分类, " +
|
|
|
- "icon VARCHAR(20) DEFAULT '' COMMENT '图标emoji', " +
|
|
|
- "color VARCHAR(20) DEFAULT '#5B9BD5' COMMENT '标识, " +
|
|
|
- "sort_order INT DEFAULT 0 COMMENT '排序', " +
|
|
|
- "status TINYINT DEFAULT 1 COMMENT '1启用/0禁用'" +
|
|
|
- ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='文章分类'");
|
|
|
- log.info("已创建article_categories表");
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("创建article_categories表失败 {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- // 文章
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS articles (" +
|
|
|
- "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
- "category_id BIGINT DEFAULT 0 COMMENT '所属分类ID', " +
|
|
|
- "title VARCHAR(200) NOT NULL COMMENT '标题', " +
|
|
|
- "summary VARCHAR(500) DEFAULT '' COMMENT '摘要', " +
|
|
|
- "cover_image VARCHAR(500) DEFAULT '' COMMENT '封面图URL', " +
|
|
|
- "content LONGTEXT COMMENT '富文本内, " +
|
|
|
- "tags VARCHAR(200) DEFAULT '' COMMENT '标签JSON数组', " +
|
|
|
- "author VARCHAR(100) DEFAULT '浠艾 COMMENT '作, " +
|
|
|
- "read_time INT DEFAULT 0 COMMENT '预计阅读分钟, " +
|
|
|
- "related_dimensions VARCHAR(100) DEFAULT '' COMMENT '关联五维JSON数组', " +
|
|
|
- "visibility VARCHAR(20) DEFAULT 'public' COMMENT '浏览权限:public/login/private', " +
|
|
|
- "visible_to TEXT COMMENT '私密指定人群JSON', " +
|
|
|
- "status VARCHAR(20) DEFAULT 'draft' COMMENT 'draft/published/archived', " +
|
|
|
- "is_featured TINYINT DEFAULT 0 COMMENT '1精0普, " +
|
|
|
- "published_at DATETIME DEFAULT NULL COMMENT '发布时间', " +
|
|
|
- "view_count INT DEFAULT 0 COMMENT '浏览次数', " +
|
|
|
- "created_by BIGINT DEFAULT 0 COMMENT '发布人adminID', " +
|
|
|
- "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
- "updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP" +
|
|
|
- ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='文章'");
|
|
|
- log.info("已创建articles表");
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("创建articles表失败 {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- // 迁移: articles表补充新字段(兼容旧表)
|
|
|
- try {
|
|
|
- Integer colExists = jdbcTemplate.queryForObject(
|
|
|
- "SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'articles' AND COLUMN_NAME = 'visibility'",
|
|
|
- Integer.class);
|
|
|
- if (colExists == null || colExists == 0) {
|
|
|
- jdbcTemplate.execute("ALTER TABLE articles ADD COLUMN visibility VARCHAR(20) DEFAULT 'public' COMMENT '浏览权限:public/login/private' AFTER related_dimensions");
|
|
|
- jdbcTemplate.execute("ALTER TABLE articles ADD COLUMN visible_to TEXT COMMENT '私密指定人群JSON' AFTER visibility");
|
|
|
- jdbcTemplate.execute("ALTER TABLE articles ADD COLUMN tags VARCHAR(200) DEFAULT '' COMMENT '标签JSON数组' AFTER content");
|
|
|
- jdbcTemplate.execute("ALTER TABLE articles ADD COLUMN related_dimensions VARCHAR(100) DEFAULT '' COMMENT '关联五维JSON数组' AFTER read_time");
|
|
|
- jdbcTemplate.execute("ALTER TABLE articles ADD COLUMN is_featured TINYINT DEFAULT 0 COMMENT '1精0普 AFTER status");
|
|
|
- jdbcTemplate.execute("ALTER TABLE articles ADD COLUMN published_at DATETIME DEFAULT NULL COMMENT '发布时间' AFTER is_featured");
|
|
|
- jdbcTemplate.execute("ALTER TABLE articles ADD COLUMN view_count INT DEFAULT 0 COMMENT '浏览次数' AFTER published_at");
|
|
|
- jdbcTemplate.execute("ALTER TABLE articles ADD COLUMN created_by BIGINT DEFAULT 0 COMMENT '发布人adminID' AFTER view_count");
|
|
|
- log.info("已迁移articles表补充字段表");
|
|
|
- } else {
|
|
|
- log.info("articles表字段已完整,跳过迁移表");
|
|
|
- }
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("articles表字段迁移失败 {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- // 迁移: articles表补充知识中心新字段(content_type/difficulty_level/dimension_ids
|
|
|
- try {
|
|
|
- Integer colExists = jdbcTemplate.queryForObject(
|
|
|
- "SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'articles' AND COLUMN_NAME = 'content_type'",
|
|
|
- Integer.class);
|
|
|
- if (colExists == null || colExists == 0) {
|
|
|
- jdbcTemplate.execute("ALTER TABLE articles ADD COLUMN content_type VARCHAR(20) DEFAULT 'article' COMMENT '内容类型: article/knowledge/course/tip' AFTER article_type");
|
|
|
- jdbcTemplate.execute("ALTER TABLE articles ADD COLUMN difficulty_level TINYINT DEFAULT 0 COMMENT '难度等级 1-5' AFTER content_type");
|
|
|
- jdbcTemplate.execute("ALTER TABLE articles ADD COLUMN dimension_ids TEXT COMMENT '关联维度ID列表JSON' AFTER related_dimensions");
|
|
|
- log.info("已迁移articles表知识中心新字段");
|
|
|
- } else {
|
|
|
- log.info("articles表知识中心字段已完整,跳过迁移表");
|
|
|
- }
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("articles表知识中心字段迁移失败 {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- // 文章标签关联
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS article_tags (" +
|
|
|
- "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
- "article_id BIGINT NOT NULL COMMENT '文章ID', " +
|
|
|
- "tag_id BIGINT NOT NULL COMMENT '标签ID(关联dan_knowledge_tag.id, " +
|
|
|
- "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
- "UNIQUE KEY uk_article_tag (article_id, tag_id)" +
|
|
|
- ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='文章-标签关联");
|
|
|
- log.info("已创建article_tags表");
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("创建article_tags表失败 {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- // ====== 五维能量系统(账本模式) ======
|
|
|
-
|
|
|
- // 维度定义
|
|
|
- try {
|
|
|
- jdbcTemplate.execute(
|
|
|
- "CREATE TABLE IF NOT EXISTS energy_dimension (" +
|
|
|
- "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
- "code VARCHAR(16) NOT NULL UNIQUE COMMENT '维度代码: body/mind/wisdom/action/wealth', " +
|
|
|
- "name VARCHAR(20) NOT NULL COMMENT '维度名称: , " +
|
|
|
- "icon VARCHAR(16) COMMENT '图标emoji', " +
|
|
|
- "element VARCHAR(10) COMMENT '五行元素: , " +
|
|
|
- "sort_order INT DEFAULT 0 COMMENT '显示顺序', " +
|
|
|
- "status TINYINT DEFAULT 1 COMMENT '1启用/0禁用', " +
|
|
|
- "created_at DATETIME DEFAULT CURRENT_TIMESTAMP" +
|
|
|
- ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='能量维度定义"
|
|
|
- );
|
|
|
- log.info("已创建energy_dimension表");
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("创建energy_dimension表失败 {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- // 服务-维度比例配置
|
|
|
- try {
|
|
|
- jdbcTemplate.execute(
|
|
|
- "CREATE TABLE IF NOT EXISTS energy_source_config (" +
|
|
|
- "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
- "source_type VARCHAR(32) NOT NULL COMMENT '来源类型: task/activity/course/product/consultation', " +
|
|
|
- "source_id BIGINT NOT NULL COMMENT '来源业务表ID', " +
|
|
|
- "source_name VARCHAR(200) COMMENT '冗余名称', " +
|
|
|
- "dimension_id BIGINT NOT NULL COMMENT '维度ID', " +
|
|
|
- "ratio INT NOT NULL COMMENT '占比(bps,10000=100%)', " +
|
|
|
- "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
- "INDEX idx_source (source_type, source_id)" +
|
|
|
- ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='服务-维度比例配置"
|
|
|
- );
|
|
|
- log.info("已创建energy_source_config表");
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("创建energy_source_config表失败 {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- // 购物
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS cart_items (" +
|
|
|
- "id BIGINT AUTO_INCREMENT PRIMARY KEY," +
|
|
|
- "user_id BIGINT NOT NULL COMMENT '用户ID'," +
|
|
|
- "product_id BIGINT NOT NULL COMMENT '商品ID'," +
|
|
|
- "quantity INT DEFAULT 1 COMMENT '数量'," +
|
|
|
- "selected TINYINT(1) DEFAULT 1 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)," +
|
|
|
- "UNIQUE KEY uk_user_product (user_id, product_id)" +
|
|
|
- ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='购物");
|
|
|
- log.info("已创建cart_items表");
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("创建cart_items表失败 {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS shop_categories (" +
|
|
|
- "id BIGINT AUTO_INCREMENT PRIMARY KEY," +
|
|
|
- "name VARCHAR(64) NOT NULL COMMENT '分类名称'," +
|
|
|
- "parent_id BIGINT DEFAULT 0 COMMENT '父分类ID(0=根节'," +
|
|
|
- "level INT DEFAULT 0 COMMENT '层级(0/1/2/3)'," +
|
|
|
- "sort INT DEFAULT 0 COMMENT '排序'," +
|
|
|
- "image VARCHAR(512) COMMENT '分类图标'," +
|
|
|
- "enabled TINYINT(1) DEFAULT 1 COMMENT '启用'," +
|
|
|
- "dimension_codes VARCHAR(128) COMMENT '维度编码(逗号分隔: body,mind,heart)'," +
|
|
|
- "product_types VARCHAR(128) COMMENT '商品类型(逗号分隔: PHYSICAL,COURSE,ASSESSMENT)'," +
|
|
|
- "created_at DATETIME DEFAULT CURRENT_TIMESTAMP," +
|
|
|
- "updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP," +
|
|
|
- "INDEX idx_parent (parent_id)," +
|
|
|
- "INDEX idx_sort (sort)" +
|
|
|
- ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品类目'");
|
|
|
- log.info("已创建shop_categories表");
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("创建shop_categories表失败 {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS distribution_systems (" +
|
|
|
- "id BIGINT AUTO_INCREMENT PRIMARY KEY," +
|
|
|
- "name VARCHAR(64) NOT NULL COMMENT '体系名称'," +
|
|
|
- "description VARCHAR(255) COMMENT '描述'," +
|
|
|
- "owner_id BIGINT COMMENT '创建者ID'," +
|
|
|
- "profit_rate 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" +
|
|
|
- ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='分销体系'");
|
|
|
- log.info("已创建distribution_systems表");
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("创建distribution_systems表失败 {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS distribution_relations (" +
|
|
|
- "id BIGINT AUTO_INCREMENT PRIMARY KEY," +
|
|
|
- "system_id BIGINT NOT NULL COMMENT '体系ID'," +
|
|
|
- "user_id BIGINT NOT NULL COMMENT '用户ID'," +
|
|
|
- "parent_id BIGINT COMMENT '上级用户ID(null='," +
|
|
|
- "depth INT DEFAULT 0 COMMENT '层级深度'," +
|
|
|
- "created_at DATETIME DEFAULT CURRENT_TIMESTAMP," +
|
|
|
- "INDEX idx_system (system_id)," +
|
|
|
- "INDEX idx_user (user_id)," +
|
|
|
- "INDEX idx_parent (parent_id)" +
|
|
|
- ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='分销关系'");
|
|
|
- log.info("已创建distribution_relations表");
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("创建distribution_relations表失败 {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS after_sales_requests (" +
|
|
|
- "id BIGINT AUTO_INCREMENT PRIMARY KEY," +
|
|
|
- "order_id BIGINT NOT NULL COMMENT '订单ID'," +
|
|
|
- "order_no VARCHAR(32) NOT NULL COMMENT '订单," +
|
|
|
- "user_id BIGINT NOT NULL COMMENT '用户ID'," +
|
|
|
- "type VARCHAR(32) NOT NULL COMMENT '类型: REFUND/RETURN_REFUND'," +
|
|
|
- "reason VARCHAR(255) COMMENT '原因'," +
|
|
|
- "description TEXT COMMENT '描述'," +
|
|
|
- "images TEXT COMMENT '凭证图片(JSON数组)'," +
|
|
|
- "refund_amount INT DEFAULT 0 COMMENT '退款金'," +
|
|
|
- "status VARCHAR(20) DEFAULT 'pending' COMMENT '状态 pending/approved/rejected/completed'," +
|
|
|
- "reject_reason VARCHAR(255) COMMENT '驳回原因'," +
|
|
|
- "handled_by BIGINT COMMENT '处理人ID'," +
|
|
|
- "handled_at DATETIME COMMENT '处理时间'," +
|
|
|
- "created_at DATETIME DEFAULT CURRENT_TIMESTAMP," +
|
|
|
- "updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP," +
|
|
|
- "INDEX idx_order (order_id)," +
|
|
|
- "INDEX idx_user (user_id)," +
|
|
|
- "INDEX idx_status (status)" +
|
|
|
- ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='售后申请'");
|
|
|
- log.info("已创建after_sales_requests表");
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("创建after_sales_requests表失败 {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS shop_discount_rules (" +
|
|
|
- "id BIGINT AUTO_INCREMENT PRIMARY KEY," +
|
|
|
- "name VARCHAR(64) NOT NULL COMMENT '规则名称'," +
|
|
|
- "type VARCHAR(32) NOT NULL COMMENT '类型: PERCENT/FIXED/THRESHOLD'," +
|
|
|
- "value INT NOT NULL COMMENT '折扣PERCENT=百分比数据 FIXED/THRESHOLD=金额'," +
|
|
|
- "threshold_amount INT COMMENT '满减门槛(仅THRESHOLD)'," +
|
|
|
- "category_id BIGINT COMMENT '适用类目ID(null=全部)'," +
|
|
|
- "distribution_system_id BIGINT COMMENT '适用体系ID(null=全部)'," +
|
|
|
- "product_ids TEXT COMMENT '适用商品ID(逗号分隔,null=全部)'," +
|
|
|
- "start_time DATETIME COMMENT '开始时," +
|
|
|
- "end_time DATETIME COMMENT '结束时间'," +
|
|
|
- "max_uses INT DEFAULT 0 COMMENT '最大使用次0=不限)'," +
|
|
|
- "used_count 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," +
|
|
|
- "INDEX idx_category (category_id)," +
|
|
|
- "INDEX idx_enabled (enabled)" +
|
|
|
- ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='折扣规则'");
|
|
|
- log.info("已创建shop_discount_rules表");
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("创建shop_discount_rules表失败 {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- // 电商扩展字段 - products
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE products ADD COLUMN category_id BIGINT COMMENT '商品类目ID'");
|
|
|
- log.info("已添加category_id列到products表");
|
|
|
- } catch (Exception e) {
|
|
|
- // 列已存在,忽略错误
|
|
|
- }
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE products ADD COLUMN distribution_system_id BIGINT COMMENT '分销体系ID'");
|
|
|
- log.info("已添加distribution_system_id列到products表");
|
|
|
- } catch (Exception e) {
|
|
|
- // 列已存在,忽略错误
|
|
|
- }
|
|
|
-
|
|
|
- // 电商扩展字段 - product_orders
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE product_orders ADD COLUMN discount_amount INT DEFAULT 0 COMMENT '优惠金额('");
|
|
|
- log.info("已添加discount_amount列到product_orders表");
|
|
|
- } catch (Exception e) {
|
|
|
- // 列已存在,忽略错误
|
|
|
- }
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE product_orders ADD COLUMN coupon_id BIGINT COMMENT '优惠券ID'");
|
|
|
- log.info("已添加coupon_id列到product_orders表");
|
|
|
- } catch (Exception e) {
|
|
|
- // 列已存在,忽略错误
|
|
|
- }
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE product_orders ADD COLUMN cancel_at DATETIME COMMENT '取消时间'");
|
|
|
- log.info("已添加cancel_at列到product_orders表");
|
|
|
- } catch (Exception e) {
|
|
|
- // 列已存在,忽略错误
|
|
|
- }
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE product_orders ADD COLUMN transaction_id VARCHAR(64) COMMENT '微信支付交易");
|
|
|
- log.info("已添加transaction_id列到product_orders表");
|
|
|
- } catch (Exception e) {
|
|
|
- // 列已存在,忽略错误
|
|
|
- }
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE product_orders ADD COLUMN cover_image VARCHAR(512) COMMENT '商品封面图快");
|
|
|
- log.info("已添加cover_image列到product_orders表");
|
|
|
- } catch (Exception e) {
|
|
|
- // 列已存在,忽略错误
|
|
|
- }
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE product_orders ADD COLUMN address_snapshot TEXT COMMENT '收货地址快照(JSON)'");
|
|
|
- log.info("已添加address_snapshot列到product_orders表");
|
|
|
- } catch (Exception e) {
|
|
|
- // 列已存在,忽略错误
|
|
|
- }
|
|
|
-
|
|
|
- // 能量流水
|
|
|
- try {
|
|
|
- jdbcTemplate.execute(
|
|
|
- "CREATE TABLE IF NOT EXISTS energy_log (" +
|
|
|
- "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
- "child_id BIGINT NOT NULL COMMENT '孩子ID', " +
|
|
|
- "dimension_id BIGINT NOT NULL COMMENT '维度ID', " +
|
|
|
- "amount INT NOT NULL COMMENT '变动获得,消', " +
|
|
|
- "balance_after INT NOT NULL COMMENT '变动后余, " +
|
|
|
- "source_type VARCHAR(32) COMMENT '来源类型: task/product/medical_report/daily_record', " +
|
|
|
- "source_id BIGINT COMMENT '来源ID', " +
|
|
|
- "ref_id BIGINT COMMENT '关联旧流水ID(覆盖时标', " +
|
|
|
- "expires_at DATETIME COMMENT '过期时间(永不过期)', " +
|
|
|
- "description VARCHAR(500) COMMENT '描述', " +
|
|
|
- "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
- "INDEX idx_child_dim (child_id, dimension_id), " +
|
|
|
- "INDEX idx_created (created_at), " +
|
|
|
- "INDEX idx_source (source_type, source_id)" +
|
|
|
- ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='能量流水"
|
|
|
- );
|
|
|
- log.info("已创建energy_log表");
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("创建energy_log表失败 {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- // 维度余额
|
|
|
- try {
|
|
|
- jdbcTemplate.execute(
|
|
|
- "CREATE TABLE IF NOT EXISTS energy_balance (" +
|
|
|
- "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
- "child_id BIGINT NOT NULL COMMENT '孩子ID', " +
|
|
|
- "dimension_id BIGINT NOT NULL COMMENT '维度ID', " +
|
|
|
- "balance INT DEFAULT 0 COMMENT '当前能量可为', " +
|
|
|
- "total_earned INT DEFAULT 0 COMMENT '累计获得', " +
|
|
|
- "total_spent INT DEFAULT 0 COMMENT '累计消, " +
|
|
|
- "updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, " +
|
|
|
- "UNIQUE KEY uk_child_dim (child_id, dimension_id)" +
|
|
|
- ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='维度余额"
|
|
|
- );
|
|
|
- log.info("已创建energy_balance表");
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("创建energy_balance表失败 {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- // 维度种子数据(按相生 智→富→行→心→身)
|
|
|
- try {
|
|
|
- Integer dimCount = jdbcTemplate.queryForObject(
|
|
|
- "SELECT COUNT(*) FROM energy_dimension", Integer.class);
|
|
|
- if (dimCount == null || dimCount == 0) {
|
|
|
- jdbcTemplate.execute("INSERT INTO energy_dimension (code, name, icon, element, sort_order, status) VALUES " +
|
|
|
- "('mind', ', '🔥', ', 4, 1), " +
|
|
|
- "('body', ', '🌏', ', 5, 1), " +
|
|
|
- "('wisdom', ', '⚔️', ', 1, 1), " +
|
|
|
- "('action', ', '🌿', ', 3, 1), " +
|
|
|
- "('wealth', ', '💧', ', 2, 1)");
|
|
|
- log.info("能量维度种子数据已初始化");
|
|
|
- }
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("初始化能量维度种子数据失败 {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- // 为已有商品插入默认比例配置(与Product.domain一致)
|
|
|
- try {
|
|
|
- Integer productConfigCount = jdbcTemplate.queryForObject(
|
|
|
- "SELECT COUNT(*) FROM energy_source_config WHERE source_type = 'product'", Integer.class);
|
|
|
- if (productConfigCount == null || productConfigCount == 0) {
|
|
|
- String[][] domainMapping = {
|
|
|
- {"action", "action"}, {"mind", "mind"}, {"body", "body"},
|
|
|
- {"wisdom", "wisdom"}, {"wealth", "wealth"}
|
|
|
- };
|
|
|
- for (String[] mapping : domainMapping) {
|
|
|
- jdbcTemplate.execute(
|
|
|
- "INSERT INTO energy_source_config (source_type, source_id, source_name, dimension_id, ratio) " +
|
|
|
- "SELECT 'product', p.id, p.name, d.id, 10000 " +
|
|
|
- "FROM products p " +
|
|
|
- "JOIN energy_dimension d ON d.code = '" + mapping[1] + "' " +
|
|
|
- "WHERE p.domain = '" + mapping[0] + "' " +
|
|
|
- "AND NOT EXISTS (" +
|
|
|
- " SELECT 1 FROM energy_source_config esc " +
|
|
|
- " WHERE esc.source_type = 'product' AND esc.source_id = p.id" +
|
|
|
- ")"
|
|
|
- );
|
|
|
- }
|
|
|
- log.info("已有商品能量比例配置已初始化");
|
|
|
- }
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("初始化商品能量比例配置失败 {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- // ====== 精准营养 P0: health_report_survey ======
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS health_report_survey (" +
|
|
|
- "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
- "report_id BIGINT NOT NULL COMMENT '关联报告ID', " +
|
|
|
- "user_id BIGINT NOT NULL COMMENT '用户ID', " +
|
|
|
- "child_id BIGINT NOT NULL COMMENT '孩子ID', " +
|
|
|
- "survey_data JSON COMMENT '调研问卷结果JSON', " +
|
|
|
- "status VARCHAR(20) DEFAULT 'pending' COMMENT '状态 pending/completed', " +
|
|
|
- "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
- "updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, " +
|
|
|
- "UNIQUE KEY uk_report (report_id)" +
|
|
|
- ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='报告调研问卷'");
|
|
|
- log.info("已创建health_report_survey 表");
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("创建 health_report_survey 表失败 {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- // ====== 精准营养 P0: health_gut_flora + health_disease_risk ======
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS health_gut_flora (" +
|
|
|
- "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
- "report_id BIGINT NOT NULL COMMENT '关联报告ID', " +
|
|
|
- "bacteria_name VARCHAR(100) NOT NULL COMMENT '菌种名称', " +
|
|
|
- "bacteria_value VARCHAR(50) COMMENT '菌种丰度, " +
|
|
|
- "normal_range VARCHAR(100) COMMENT '正常范围', " +
|
|
|
- "status VARCHAR(20) COMMENT '状态 正常/偏低/偏高', " +
|
|
|
- "category VARCHAR(50) COMMENT '分类: 有益有害中性菌', " +
|
|
|
- "sort_order INT DEFAULT 0 COMMENT '排序', " +
|
|
|
- "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
- "INDEX idx_report (report_id)" +
|
|
|
- ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='菌群检测专项数据");
|
|
|
- log.info("已创建health_gut_flora 表");
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("创建 health_gut_flora 表失败 {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS health_disease_risk (" +
|
|
|
- "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
- "report_id BIGINT NOT NULL COMMENT '关联报告ID', " +
|
|
|
- "disease_name VARCHAR(100) NOT NULL COMMENT '疾病名称', " +
|
|
|
- "risk_value VARCHAR(50) COMMENT '风险0-1)', " +
|
|
|
- "risk_level VARCHAR(20) COMMENT '风险等级: 高风需注意/低风, " +
|
|
|
- "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
- "INDEX idx_report (report_id)" +
|
|
|
- ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='疾病风险评估数据'");
|
|
|
- log.info("已创建health_disease_risk 表");
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("创建 health_disease_risk 表失败 {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- // ====== 迁移: health_reports 表添加新字段 ======
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE health_reports ADD COLUMN balance_score INT DEFAULT NULL COMMENT '菌群平衡");
|
|
|
- log.info("已添加balance_score列到health_reports表");
|
|
|
- } catch (Exception e) {
|
|
|
- // 列已存在,忽略错误
|
|
|
- }
|
|
|
-
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE health_reports ADD COLUMN diversity_score INT DEFAULT NULL COMMENT '菌群多样性分'");
|
|
|
- log.info("已添加diversity_score列到health_reports表");
|
|
|
- } catch (Exception e) {
|
|
|
- // 列已存在,忽略错误
|
|
|
- }
|
|
|
-
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE health_reports ADD COLUMN beneficial_score INT DEFAULT NULL COMMENT '有益菌评");
|
|
|
- log.info("已添加beneficial_score列到health_reports表");
|
|
|
- } catch (Exception e) {
|
|
|
- // 列已存在,忽略错误
|
|
|
- }
|
|
|
-
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE health_reports ADD COLUMN harmful_score INT DEFAULT NULL COMMENT '有害菌评");
|
|
|
- log.info("已添加harmful_score列到health_reports表");
|
|
|
- } catch (Exception e) {
|
|
|
- // 列已存在,忽略错误
|
|
|
- }
|
|
|
-
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE health_reports ADD COLUMN core_genus_score INT DEFAULT NULL COMMENT '核心菌属评分'");
|
|
|
- log.info("已添加core_genus_score列到health_reports表");
|
|
|
- } catch (Exception e) {
|
|
|
- // 列已存在,忽略错误
|
|
|
- }
|
|
|
-
|
|
|
- // 迁移: health_reports 表添加报告编号和姓名字段
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE health_reports ADD COLUMN report_number VARCHAR(50) DEFAULT NULL COMMENT '报告编号'");
|
|
|
- log.info("已添加report_number列到health_reports表");
|
|
|
- } catch (Exception e) {
|
|
|
- // 列已存在,忽略错误
|
|
|
- }
|
|
|
-
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE health_reports ADD COLUMN person_name VARCHAR(50) DEFAULT NULL COMMENT '被检测人姓名'");
|
|
|
- log.info("已添加person_name列到health_reports表");
|
|
|
- } catch (Exception e) {
|
|
|
- // 列已存在,忽略错误
|
|
|
- }
|
|
|
-
|
|
|
- // ====== 迁移: health_gut_flora 表添加新字段 ======
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE health_gut_flora ADD COLUMN population_level VARCHAR(50) DEFAULT NULL COMMENT '处于人群%水平'");
|
|
|
- log.info("已添加population_level列到health_gut_flora表");
|
|
|
- } catch (Exception e) {
|
|
|
- // 列已存在,忽略错误
|
|
|
- }
|
|
|
-
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE health_gut_flora ADD COLUMN detection_rate VARCHAR(50) DEFAULT NULL COMMENT '%正常人有检查");
|
|
|
- log.info("已添加detection_rate列到health_gut_flora表");
|
|
|
- } catch (Exception e) {
|
|
|
- // 列已存在,忽略错误
|
|
|
- }
|
|
|
-
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE health_gut_flora ADD COLUMN description TEXT DEFAULT NULL COMMENT '菌种说明'");
|
|
|
- log.info("已添加description列到health_gut_flora表");
|
|
|
- } catch (Exception e) {
|
|
|
- // 列已存在,忽略错误
|
|
|
- }
|
|
|
-
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE health_gut_flora ADD COLUMN `level` VARCHAR(20) DEFAULT 'GENUS' COMMENT '级别: GENUS/SPECIES'");
|
|
|
- log.info("已添加level列到health_gut_flora表");
|
|
|
- } catch (Exception e) {
|
|
|
- // 列已存在,忽略错误
|
|
|
- }
|
|
|
-
|
|
|
- // ====== 迁移: guides 表统一 ======
|
|
|
-
|
|
|
- // 迁移: guides 添加 type 字段(区分服务商类型
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE guides ADD COLUMN type VARCHAR(32) DEFAULT 'teacher' COMMENT '服务商类 teacher/planner/activity_provider/product_supplier'");
|
|
|
- log.info("已添加type列到guides表");
|
|
|
- } catch (Exception e) {
|
|
|
- // 列已存在,忽略错误
|
|
|
- }
|
|
|
-
|
|
|
- // 迁移: guides 添加 teachers 合并字段
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE guides ADD COLUMN teacher_no VARCHAR(20) COMMENT '服务商编");
|
|
|
- log.info("已添加teacher_no列到guides表");
|
|
|
- } catch (Exception e) {}
|
|
|
-
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE guides ADD COLUMN title VARCHAR(50) COMMENT '职称/称号'");
|
|
|
- } catch (Exception e) {}
|
|
|
-
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE guides ADD COLUMN specialty VARCHAR(200) COMMENT '专长领域'");
|
|
|
- } catch (Exception e) {}
|
|
|
-
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE guides ADD COLUMN bio TEXT COMMENT '简");
|
|
|
- } catch (Exception e) {}
|
|
|
-
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE guides ADD COLUMN experience_years TINYINT DEFAULT 0 COMMENT '从业年限'");
|
|
|
- } catch (Exception e) {}
|
|
|
-
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE guides ADD COLUMN rating INT DEFAULT 500 COMMENT '评分(百分500=5.00)'");
|
|
|
- } catch (Exception e) {}
|
|
|
-
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE guides ADD COLUMN total_appointments INT DEFAULT 0 COMMENT '总服务次");
|
|
|
- } catch (Exception e) {}
|
|
|
-
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE guides ADD COLUMN street_id BIGINT COMMENT '街道ID'");
|
|
|
- } catch (Exception e) {}
|
|
|
-
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE guides ADD COLUMN province VARCHAR(50) COMMENT '省份'");
|
|
|
- jdbcTemplate.execute("ALTER TABLE guides ADD COLUMN city VARCHAR(50) COMMENT '城市'");
|
|
|
- jdbcTemplate.execute("ALTER TABLE guides ADD COLUMN district VARCHAR(50) COMMENT '区县'");
|
|
|
- jdbcTemplate.execute("ALTER TABLE guides ADD COLUMN street VARCHAR(100) COMMENT '街道'");
|
|
|
- log.info("已添加地址字段到guides表");
|
|
|
- } catch (Exception e) {}
|
|
|
-
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE guides ADD COLUMN verified_at DATETIME COMMENT '审核通过时间'");
|
|
|
- } catch (Exception e) {}
|
|
|
-
|
|
|
- // 迁移: teachers 表数据合并到 guides(type='teacher'
|
|
|
- try {
|
|
|
- int migrated = jdbcTemplate.update(
|
|
|
- "INSERT IGNORE INTO guides (user_id, type, teacher_no, title, specialty, bio, experience_years, rating, total_appointments, " +
|
|
|
- "street_id, province, city, district, street, verified_at, status, level, created_at, updated_at) " +
|
|
|
- "SELECT t.user_id, 'teacher', t.teacher_no, t.title, t.specialty, t.bio, t.experience_years, t.rating, t.total_appointments, " +
|
|
|
- "t.street_id, t.province, t.city, t.district, t.street, t.verified_at, " +
|
|
|
- "CASE WHEN t.status = 'active' THEN 'active' ELSE 'inactive' END, " +
|
|
|
- "CASE WHEN t.verification_status = 'approved' THEN 'senior' WHEN t.verification_status = 'pending' THEN 'junior' ELSE 'junior' END, " +
|
|
|
- "t.created_at, t.updated_at " +
|
|
|
- "FROM teachers t " +
|
|
|
- "LEFT JOIN guides g ON g.user_id = t.user_id " +
|
|
|
- "WHERE g.id IS NULL"
|
|
|
- );
|
|
|
- if (migrated > 0) {
|
|
|
- log.info("已从 teachers 表迁移{} 条数据到 guides(type=teacher, migrated表");
|
|
|
- }
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("迁移 teachers guides 数据失败: {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- // 迁移: dan_assessment_results 表添加认知维度字段(6维认知体系扩展)
|
|
|
- try {
|
|
|
- Integer colExists = jdbcTemplate.queryForObject(
|
|
|
- "SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'dan_assessment_results' AND COLUMN_NAME = 'perception_score'",
|
|
|
- Integer.class);
|
|
|
- if (colExists == null || colExists == 0) {
|
|
|
- jdbcTemplate.execute("ALTER TABLE dan_assessment_results " +
|
|
|
- "ADD COLUMN perception_score INT COMMENT '感知得分', " +
|
|
|
- "ADD COLUMN spatial_score INT COMMENT '空间思维得分', " +
|
|
|
- "ADD COLUMN processing_speed_score INT COMMENT '加工速度得分'");
|
|
|
- log.info("已为 dan_assessment_results 表添加认知维度字段表");
|
|
|
- } else {
|
|
|
- log.info("dan_assessment_results 认知维度字段已存在,跳过");
|
|
|
- }
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("检查添加 dan_assessment_results 认知维度字段失败: {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- // 迁移: dan_assessment_results 表添加EMI 维度字段(v1.2 心智模型重构
|
|
|
- try {
|
|
|
- Integer colExists = jdbcTemplate.queryForObject(
|
|
|
- "SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'dan_assessment_results' AND COLUMN_NAME = 'emotion_management_score'",
|
|
|
- Integer.class);
|
|
|
- if (colExists == null || colExists == 0) {
|
|
|
- jdbcTemplate.execute("ALTER TABLE dan_assessment_results " +
|
|
|
- "ADD COLUMN emotion_management_score INT COMMENT '情绪管理得分(EMI)', " +
|
|
|
- "ADD COLUMN empathy_score INT COMMENT '同理心得EMI)', " +
|
|
|
- "ADD COLUMN social_adaptability_score INT COMMENT '社会适应得分(EMI)', " +
|
|
|
- "ADD COLUMN self_motivation_score INT COMMENT '自我激励得EMI)'");
|
|
|
- log.info("已为 dan_assessment_results 表添加EMI 维度字段");
|
|
|
- } else {
|
|
|
- log.info("dan_assessment_results EMI 维度字段已存在,跳过");
|
|
|
- }
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("检查添加 dan_assessment_results EMI 维度字段失败: {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- // 迁移: dan_assessment_results 表添加EMI 心理子维度字段
|
|
|
- try {
|
|
|
- Integer colExists = jdbcTemplate.queryForObject(
|
|
|
- "SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'dan_assessment_results' AND COLUMN_NAME = 'emotion_score'",
|
|
|
- Integer.class);
|
|
|
- if (colExists == null || colExists == 0) {
|
|
|
- jdbcTemplate.execute("ALTER TABLE dan_assessment_results " +
|
|
|
- "ADD COLUMN emotion_score INT COMMENT '情绪商数(0-100)', " +
|
|
|
- "ADD COLUMN resilience_score INT COMMENT '心理韧0-100)', " +
|
|
|
- "ADD COLUMN stress_coping_score INT COMMENT '压力应对(0-100)', " +
|
|
|
- "ADD COLUMN self_awareness_score INT COMMENT '自我认知(0-100)'");
|
|
|
- log.info("已为 dan_assessment_results 表添加EMI 心理子维度字段表");
|
|
|
- } else {
|
|
|
- log.info("dan_assessment_results EMI 心理子维度字段已存在,跳过表");
|
|
|
- }
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("检查添加 dan_assessment_results EMI 心理子维度字段失败 {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- // 迁移: dan_assessment_results 表添加大五人格维度字段
|
|
|
- try {
|
|
|
- Integer colExists = jdbcTemplate.queryForObject(
|
|
|
- "SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'dan_assessment_results' AND COLUMN_NAME = 'openness_score'",
|
|
|
- Integer.class);
|
|
|
- if (colExists == null || colExists == 0) {
|
|
|
- jdbcTemplate.execute("ALTER TABLE dan_assessment_results " +
|
|
|
- "ADD COLUMN openness_score INT COMMENT '开放0-100)', " +
|
|
|
- "ADD COLUMN conscientiousness_score INT COMMENT '尽责0-100)', " +
|
|
|
- "ADD COLUMN extraversion_score INT COMMENT '外向0-100)', " +
|
|
|
- "ADD COLUMN agreeableness_score INT COMMENT '宜人0-100)', " +
|
|
|
- "ADD COLUMN neuroticism_score INT COMMENT '神经0-100)', " +
|
|
|
- "ADD COLUMN big_five_overall_score INT COMMENT '大五人格综合");
|
|
|
- log.info("已为 dan_assessment_results 表添加大五人格维度字段表");
|
|
|
- } else {
|
|
|
- log.info("dan_assessment_results 大五人格维度字段已存在,跳过");
|
|
|
- }
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("检查添加 dan_assessment_results 大五人格维度字段失败: {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- // 迁移: users 表添加出生时辰字段
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE users ADD COLUMN birth_hour VARCHAR(10) COMMENT '出生时辰: 子丑寅卯辰巳午未申酉戌亥'");
|
|
|
- log.info("已添加birth_hour列到users表");
|
|
|
- } catch (Exception e) {
|
|
|
- // 列已存在,忽略错误
|
|
|
- }
|
|
|
-
|
|
|
- // 迁移: 重建 family_member_attributes 表结构(v1.2 心智模型
|
|
|
- try {
|
|
|
- Integer colExists = jdbcTemplate.queryForObject(
|
|
|
- "SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'family_member_attributes' AND COLUMN_NAME = 'member_id'",
|
|
|
- Integer.class);
|
|
|
- if (colExists == null || colExists == 0) {
|
|
|
- // 旧表存在但结构不同,先删除旧表重
|
|
|
- jdbcTemplate.execute("DROP TABLE IF EXISTS family_member_attributes_old");
|
|
|
- jdbcTemplate.execute("RENAME TABLE family_member_attributes TO family_member_attributes_old");
|
|
|
- jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS family_member_attributes (" +
|
|
|
- "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
- "member_id BIGINT NOT NULL COMMENT '家庭成员ID(children表或users表)', " +
|
|
|
- "member_type VARCHAR(10) NOT NULL COMMENT '成员类型: child/parent', " +
|
|
|
- "birth_datetime DATETIME COMMENT '出生日期(精确到分钟,用于八字排盘)', " +
|
|
|
- "eight_characters JSON COMMENT '八字四柱JSON', " +
|
|
|
- "wuxing_elements JSON COMMENT '五行星盘元素JSON', " +
|
|
|
- "zodiac VARCHAR(16) COMMENT '生肖: rat/ox/tiger/rabbit/dragon/snake/horse/sheep/monkey/rooster/dog/pig', " +
|
|
|
- "blood_type VARCHAR(5) COMMENT '血 A/B/AB/O', " +
|
|
|
- "mind_base_score INT DEFAULT 0 COMMENT '心维度基础百分500=5.00)', " +
|
|
|
- "wisdom_base_score INT DEFAULT 0 COMMENT '智维度基础百分500=5.00)', " +
|
|
|
- "behavior_modifier JSON COMMENT '行为修正因子JSON', " +
|
|
|
- "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
- "updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, " +
|
|
|
- "UNIQUE KEY uk_member (member_id, member_type)" +
|
|
|
- ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='家庭成员先天属性表'");
|
|
|
- log.info("已重family_member_attributes 表(v1.2 心智模型表");
|
|
|
- } else {
|
|
|
- log.info("family_member_attributes 表已v1.2 结构,跳过表");
|
|
|
- }
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("重建 family_member_attributes 表失败 {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- // 迁移: children 表添加show_to_family 列(v1.3 家庭成员卡)
|
|
|
- try {
|
|
|
- Integer colExists = jdbcTemplate.queryForObject(
|
|
|
- "SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'children' AND COLUMN_NAME = 'show_to_family'",
|
|
|
- Integer.class);
|
|
|
- if (colExists == null || colExists == 0) {
|
|
|
- jdbcTemplate.execute("ALTER TABLE children ADD COLUMN show_to_family TINYINT DEFAULT 1 COMMENT '是否对家庭成员可 1= 0=");
|
|
|
- log.info("已为 children 表添加show_to_family 表");
|
|
|
- } else {
|
|
|
- log.info("children show_to_family 列已存在,跳过表");
|
|
|
- }
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("检查添加 children show_to_family 列失败 {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- // 迁移: users 表添加show_to_family 列(v1.3 家庭成员卡)
|
|
|
- try {
|
|
|
- Integer colExists = jdbcTemplate.queryForObject(
|
|
|
- "SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'users' AND COLUMN_NAME = 'show_to_family'",
|
|
|
- Integer.class);
|
|
|
- if (colExists == null || colExists == 0) {
|
|
|
- jdbcTemplate.execute("ALTER TABLE users ADD COLUMN show_to_family TINYINT DEFAULT 1 COMMENT '是否对家庭成员可 1= 0=");
|
|
|
- log.info("已为 users 表添加show_to_family 表");
|
|
|
- } else {
|
|
|
- log.info("users show_to_family 列已存在,跳过表");
|
|
|
- }
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("检查添加 users show_to_family 列失败 {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- // 迁移: tasks 表添加family_member_id 列(FamilyMember 迁移
|
|
|
- try {
|
|
|
- Integer colExists = jdbcTemplate.queryForObject(
|
|
|
- "SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'tasks' AND COLUMN_NAME = 'family_member_id'",
|
|
|
- Integer.class);
|
|
|
- if (colExists == null || colExists == 0) {
|
|
|
- jdbcTemplate.execute("ALTER TABLE tasks ADD COLUMN family_member_id BIGINT COMMENT '家庭成员ID(关family_members.id");
|
|
|
- log.info("已为 tasks 表添加family_member_id 表");
|
|
|
- } else {
|
|
|
- log.info("tasks family_member_id 列已存在,跳过表");
|
|
|
- }
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("检查添加 tasks family_member_id 列失败 {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- // 迁移: family_members 表添加role_override 列(FamilyMember 迁移
|
|
|
- try {
|
|
|
- Integer colExists = jdbcTemplate.queryForObject(
|
|
|
- "SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'family_members' AND COLUMN_NAME = 'role_override'",
|
|
|
- Integer.class);
|
|
|
- if (colExists == null || colExists == 0) {
|
|
|
- jdbcTemplate.execute("ALTER TABLE family_members ADD COLUMN role_override VARCHAR(20) DEFAULT 'auto' COMMENT '角色覆盖: auto/parent/child/elderly,空=auto'");
|
|
|
- log.info("已为 family_members 表添加role_override 表");
|
|
|
- } else {
|
|
|
- log.info("family_members role_override 列已存在,跳过表");
|
|
|
- }
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("检查添加 family_members role_override 列失败 {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- // 迁移: family_members 表添加id_card 列(孩子身份证号
|
|
|
- try {
|
|
|
- Integer colExists = jdbcTemplate.queryForObject(
|
|
|
- "SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'family_members' AND COLUMN_NAME = 'id_card'",
|
|
|
- Integer.class);
|
|
|
- if (colExists == null || colExists == 0) {
|
|
|
- jdbcTemplate.execute("ALTER TABLE family_members ADD COLUMN id_card VARCHAR(18) DEFAULT NULL COMMENT '身份证号(孩子)'");
|
|
|
- log.info("已为 family_members 表添加id_card 表");
|
|
|
- } else {
|
|
|
- log.info("family_members id_card 列已存在,跳过表");
|
|
|
- }
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("检查添加 family_members id_card 列失败 {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- // 迁移: family_members 表添加孩子专属字段(原children表合并)
|
|
|
- String[][] childColumns = {
|
|
|
- {"penalty_enabled", "TINYINT DEFAULT 1 COMMENT '是否扣减积分(0'"},
|
|
|
- {"theme", "VARCHAR(32) DEFAULT 'default' COMMENT '主题'"},
|
|
|
- {"system_points", "INT DEFAULT 0 COMMENT '系统积分(干预/平台奖励)'"},
|
|
|
- {"streak_days", "INT DEFAULT 0 COMMENT '连续打卡天数'"},
|
|
|
- {"last_task_date", "DATE COMMENT '上次任务日期'"},
|
|
|
- {"focus_max_daily", "TINYINT DEFAULT 3 COMMENT '每日专注上限'"},
|
|
|
- {"focus_remaining","TINYINT DEFAULT 3 COMMENT '今日剩余专注次数'"},
|
|
|
- {"focus_reset_date","DATE COMMENT '专注次数重置日期'"}
|
|
|
- };
|
|
|
- for (String[] col : childColumns) {
|
|
|
- try {
|
|
|
- Integer colExists = jdbcTemplate.queryForObject(
|
|
|
- "SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'family_members' AND COLUMN_NAME = ?",
|
|
|
- Integer.class, col[0]);
|
|
|
- if (colExists == null || colExists == 0) {
|
|
|
- jdbcTemplate.execute("ALTER TABLE family_members ADD COLUMN " + col[0] + " " + col[1]);
|
|
|
- log.info("已为 family_members 表添加{} , col[0]表");
|
|
|
- }
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("检查添加 family_members {} 列失败 {}", col[0], e.getMessage());
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // 关系类型表添加default_role 列(角色模板
|
|
|
- ensureColumn("relationship_types", "default_role", "VARCHAR(20) DEFAULT NULL COMMENT '默认角色模板: parent/child'");
|
|
|
-
|
|
|
- // 家庭成员表添加role_override 列(替换capability_role
|
|
|
- ensureColumn("family_members", "role_override", "VARCHAR(20) DEFAULT 'auto' COMMENT '角色覆盖: auto/parent/child/elderly'");
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("ALTER TABLE family_members DROP COLUMN capability_role");
|
|
|
- log.info("已删family_members.capability_role 旧列,已role_override 替代");
|
|
|
- } catch (Exception e) {
|
|
|
- // 列不存在或已删除,忽略错误
|
|
|
- }
|
|
|
-
|
|
|
- // activities 表添加报签到相关字段
|
|
|
- ensureColumn("activities", "require_registration", "TINYINT DEFAULT 1 COMMENT '是否需要报 1=需 0=直接参与'");
|
|
|
- ensureColumn("activities", "require_checkin_review", "TINYINT DEFAULT 1 COMMENT '打卡是否需要审 1=需 0=直接通过'");
|
|
|
- ensureColumn("activities", "energy_penalty_unregistered", "INT DEFAULT 5 COMMENT '未报名打卡的行能量扣减");
|
|
|
-
|
|
|
- // family_members 表添加关系评分字段
|
|
|
- ensureColumn("family_members", "trust_score", "DECIMAL(5,2) DEFAULT 0 COMMENT '信任0-100'");
|
|
|
- ensureColumn("family_members", "intimacy_score", "DECIMAL(5,2) DEFAULT 0 COMMENT '亲密0-100'");
|
|
|
- ensureColumn("family_members", "communication_score", "DECIMAL(5,2) DEFAULT 0 COMMENT '沟通质0-100'");
|
|
|
- ensureColumn("family_members", "last_interaction_at", "DATETIME COMMENT '最近互动时");
|
|
|
-
|
|
|
- // 创建购物车表
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS cart (" +
|
|
|
- "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
- "user_id BIGINT NOT NULL COMMENT '用户ID', " +
|
|
|
- "product_id BIGINT NOT NULL COMMENT '商品ID', " +
|
|
|
- "quantity INT NOT NULL DEFAULT 1 COMMENT '数量', " +
|
|
|
- "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
- "updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, " +
|
|
|
- "INDEX idx_user_product (user_id, product_id)" +
|
|
|
- ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='购物");
|
|
|
- log.info("已创建cart 表");
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("创建 cart 表失败 {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- // ====== DanShop 电商模块建表 ======
|
|
|
- // 1. danshop_categories (商品分类
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS danshop_categories (" +
|
|
|
- "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
- "parent_id BIGINT DEFAULT NULL COMMENT '父分类ID(null=一级)', " +
|
|
|
- "name VARCHAR(64) NOT NULL COMMENT '分类名称', " +
|
|
|
- "image VARCHAR(512) DEFAULT NULL COMMENT '分类图标URL', " +
|
|
|
- "sort_order INT DEFAULT 0 COMMENT '排序(越小越靠前, " +
|
|
|
- "energy_dimension_id BIGINT DEFAULT NULL COMMENT '关联五维维度ID', " +
|
|
|
- "status INT DEFAULT 1 COMMENT '1启用/0禁用', " +
|
|
|
- "created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
- "updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, " +
|
|
|
- "INDEX idx_parent (parent_id), " +
|
|
|
- "INDEX idx_status (status)" +
|
|
|
- ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品分类");
|
|
|
- log.info("已创建danshop_categories 表");
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("创建 danshop_categories 表失败 {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- // 2. danshop_products (商品
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS danshop_products (" +
|
|
|
- "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
- "category_id BIGINT NOT NULL COMMENT '所属分类ID', " +
|
|
|
- "name VARCHAR(128) NOT NULL COMMENT '商品名称', " +
|
|
|
- "description TEXT COMMENT '商品详细描述', " +
|
|
|
- "cover_image VARCHAR(512) COMMENT '封面图URL', " +
|
|
|
- "images TEXT COMMENT '多图(JSON数组)', " +
|
|
|
- "price INT NOT NULL COMMENT '售价(', " +
|
|
|
- "member_price INT DEFAULT NULL COMMENT '会员', " +
|
|
|
- "cost_price INT DEFAULT NULL COMMENT '成本', " +
|
|
|
- "stock INT DEFAULT 0 COMMENT '库存数量', " +
|
|
|
- "sales_count INT DEFAULT 0 COMMENT '销, " +
|
|
|
- "product_type VARCHAR(32) NOT NULL COMMENT '类型: physical/virtual/coupon/course', " +
|
|
|
- "vendor_id BIGINT DEFAULT NULL COMMENT '供应商ID', " +
|
|
|
- "vendor_name VARCHAR(64) COMMENT '供应商名, " +
|
|
|
- "status VARCHAR(16) NOT NULL DEFAULT 'pending' COMMENT 'pending/approved/rejected/on_shelf/off_shelf', " +
|
|
|
- "reject_reason VARCHAR(255) DEFAULT NULL COMMENT '审核拒绝原因', " +
|
|
|
- "profit_rate INT DEFAULT 0 COMMENT '平台利润bps,100=1%)', " +
|
|
|
- "external_source VARCHAR(32) COMMENT '外部商品来源', " +
|
|
|
- "external_id VARCHAR(64) COMMENT '外部商品ID', " +
|
|
|
- "external_data TEXT COMMENT '外部商品完整数据(JSON)', " +
|
|
|
- "created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
- "updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, " +
|
|
|
- "INDEX idx_category (category_id), " +
|
|
|
- "INDEX idx_vendor (vendor_id), " +
|
|
|
- "INDEX idx_status (status)" +
|
|
|
- ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品");
|
|
|
- log.info("已创建danshop_products 表");
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("创建 danshop_products 表失败 {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- // 3. danshop_orders (主订单表)
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS danshop_orders (" +
|
|
|
- "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
- "order_no VARCHAR(32) NOT NULL UNIQUE COMMENT '订单DS+时间6位随', " +
|
|
|
- "buyer_id BIGINT NOT NULL COMMENT '买家ID', " +
|
|
|
- "family_id BIGINT DEFAULT NULL COMMENT '家庭ID', " +
|
|
|
- "total_amount INT NOT NULL COMMENT '订单总金', " +
|
|
|
- "discount_amount INT DEFAULT 0 COMMENT '优惠金额(', " +
|
|
|
- "actual_amount INT NOT NULL COMMENT '实付金额(', " +
|
|
|
- "status VARCHAR(20) NOT NULL DEFAULT 'pending' COMMENT 'pending/paid/shipped/completed/refunding/refunded/cancelled', " +
|
|
|
- "payment_method VARCHAR(16) DEFAULT 'wechat' COMMENT 'wechat/alipay', " +
|
|
|
- "transaction_id VARCHAR(64) DEFAULT NULL COMMENT '微信/支付宝交易号', " +
|
|
|
- "paid_at DATETIME DEFAULT NULL COMMENT '支付时间', " +
|
|
|
- "shipped_at DATETIME DEFAULT NULL COMMENT '发货时间', " +
|
|
|
- "completed_at DATETIME DEFAULT NULL COMMENT '完成时间', " +
|
|
|
- "remark VARCHAR(255) DEFAULT NULL COMMENT '买家备注', " +
|
|
|
- "cancel_reason VARCHAR(255) DEFAULT NULL COMMENT '取消原因', " +
|
|
|
- "receiver_name VARCHAR(32) DEFAULT NULL COMMENT '收件人姓, " +
|
|
|
- "receiver_phone VARCHAR(16) DEFAULT NULL COMMENT '收件人电, " +
|
|
|
- "receiver_address VARCHAR(256) DEFAULT NULL COMMENT '收件人地址', " +
|
|
|
- "freight INT DEFAULT 0 COMMENT '运费(', " +
|
|
|
- "profit_amount INT DEFAULT 0 COMMENT '分润金额(', " +
|
|
|
- "created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
- "updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, " +
|
|
|
- "INDEX idx_buyer (buyer_id), " +
|
|
|
- "INDEX idx_family (family_id), " +
|
|
|
- "INDEX idx_status (status), " +
|
|
|
- "INDEX idx_order_no (order_no)" +
|
|
|
- ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='主订单表'");
|
|
|
- log.info("已创建danshop_orders 表");
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("创建 danshop_orders 表失败 {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- // 4. danshop_order_items (订单明细
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS danshop_order_items (" +
|
|
|
- "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
- "order_id BIGINT NOT NULL COMMENT '所属订单ID', " +
|
|
|
- "product_id BIGINT NOT NULL COMMENT '商品ID', " +
|
|
|
- "product_name VARCHAR(128) NOT NULL COMMENT '商品名称(快照)', " +
|
|
|
- "cover_image VARCHAR(512) COMMENT '封面快照)', " +
|
|
|
- "unit_price INT NOT NULL COMMENT '单价(快照)', " +
|
|
|
- "price INT NOT NULL COMMENT '单价(别名兼容)', " +
|
|
|
- "quantity INT NOT NULL DEFAULT 1 COMMENT '购买数量', " +
|
|
|
- "subtotal INT NOT NULL COMMENT '小计(', " +
|
|
|
- "product_type VARCHAR(32) COMMENT '商品类型(快照)', " +
|
|
|
- "vendor_id BIGINT COMMENT '供应商ID(快照)', " +
|
|
|
- "vendor_name VARCHAR(64) COMMENT '供应商名快照)', " +
|
|
|
- "created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
- "INDEX idx_order (order_id), " +
|
|
|
- "INDEX idx_product (product_id)" +
|
|
|
- ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单明细");
|
|
|
- log.info("已创建danshop_order_items 表");
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("创建 danshop_order_items 表失败 {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- // 5. danshop_order_logs (订单操作日志
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS danshop_order_logs (" +
|
|
|
- "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
- "order_id BIGINT NOT NULL COMMENT '订单ID', " +
|
|
|
- "action VARCHAR(32) NOT NULL COMMENT '操作: create/pay/ship/confirm/refund/cancel', " +
|
|
|
- "operator_id BIGINT DEFAULT NULL COMMENT '操作人ID', " +
|
|
|
- "operator_type VARCHAR(16) DEFAULT NULL COMMENT 'buyer/vendor/admin/system', " +
|
|
|
- "detail TEXT DEFAULT NULL COMMENT '操作详情(JSON)', " +
|
|
|
- "created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
- "INDEX idx_order (order_id)" +
|
|
|
- ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单操作日志");
|
|
|
- log.info("已创建danshop_order_logs 表");
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("创建 danshop_order_logs 表失败 {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- // 6. danshop_logistics (物流信息
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS danshop_logistics (" +
|
|
|
- "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
- "order_id BIGINT NOT NULL COMMENT '订单ID', " +
|
|
|
- "order_item_id BIGINT DEFAULT NULL COMMENT '订单明细ID', " +
|
|
|
- "logistics_no VARCHAR(64) NOT NULL COMMENT '物流单号', " +
|
|
|
- "logistics_company VARCHAR(32) NOT NULL COMMENT '物流公司编码: sf/yto/zt/sto/yd/ttkd', " +
|
|
|
- "logistics_name VARCHAR(32) DEFAULT NULL COMMENT '物流公司名称', " +
|
|
|
- "sender_name VARCHAR(32) DEFAULT NULL COMMENT '发货人姓, " +
|
|
|
- "sender_phone VARCHAR(16) DEFAULT NULL COMMENT '发货人电, " +
|
|
|
- "sender_address VARCHAR(256) DEFAULT NULL COMMENT '发货人地址', " +
|
|
|
- "receiver_name VARCHAR(32) NOT NULL COMMENT '收件人姓, " +
|
|
|
- "receiver_phone VARCHAR(16) NOT NULL COMMENT '收件人电, " +
|
|
|
- "receiver_address VARCHAR(256) NOT NULL COMMENT '收件人地址', " +
|
|
|
- "status VARCHAR(16) NOT NULL DEFAULT 'pending' COMMENT 'pending/shipped/signing/completed', " +
|
|
|
- "tracking_data TEXT DEFAULT NULL COMMENT '物流轨迹(JSON数组)', " +
|
|
|
- "shipped_at DATETIME DEFAULT NULL COMMENT '发货时间', " +
|
|
|
- "signed_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, " +
|
|
|
- "INDEX idx_order (order_id), " +
|
|
|
- "INDEX idx_logistics_no (logistics_no), " +
|
|
|
- "INDEX idx_status (status)" +
|
|
|
- ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='物流信息");
|
|
|
- log.info("已创建danshop_logistics 表");
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("创建 danshop_logistics 表失败 {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- // 7. danshop_profit_rules (分润规则配置
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS danshop_profit_rules (" +
|
|
|
- "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
- "rule_name VARCHAR(64) NOT NULL COMMENT '规则名称', " +
|
|
|
- "rule_type VARCHAR(32) NOT NULL COMMENT '规则类型: product_category/vendor_level/global', " +
|
|
|
- "target_id BIGINT DEFAULT NULL COMMENT '关联目标ID', " +
|
|
|
- "platform_profit_rate INT NOT NULL COMMENT '平台利润bps,100=1%)', " +
|
|
|
- "referrer_l1_rate INT DEFAULT 0 COMMENT '直接推荐人分润比bps,占利)', " +
|
|
|
- "referrer_l2_rate INT DEFAULT 0 COMMENT '间接推荐人分润比bps,占利)', " +
|
|
|
- "platform_service_rate INT DEFAULT 0 COMMENT '平台服务费率(bps,占订单金)', " +
|
|
|
- "is_active INT DEFAULT 1 COMMENT '1启用/0禁用', " +
|
|
|
- "created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
- "updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, " +
|
|
|
- "INDEX idx_type (rule_type, target_id), " +
|
|
|
- "INDEX idx_active (is_active)" +
|
|
|
- ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='分润规则配置");
|
|
|
- log.info("已创建danshop_profit_rules 表");
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("创建 danshop_profit_rules 表失败 {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- // 8. danshop_profit_logs (分润流水
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS danshop_profit_logs (" +
|
|
|
- "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
- "order_id BIGINT NOT NULL COMMENT '订单ID', " +
|
|
|
- "order_no VARCHAR(32) NOT NULL COMMENT '订单, " +
|
|
|
- "order_amount INT NOT NULL COMMENT '订单实付金额(', " +
|
|
|
- "cost_amount INT DEFAULT 0 COMMENT '成本金额(', " +
|
|
|
- "profit_amount INT NOT NULL COMMENT '利润金额(', " +
|
|
|
- "platform_retained INT NOT NULL COMMENT '平台留存金额(', " +
|
|
|
- "vendor_income INT NOT NULL COMMENT '供应商实收金', " +
|
|
|
- "commission_l1 INT DEFAULT 0 COMMENT 'L1推荐人佣', " +
|
|
|
- "commission_l2 INT DEFAULT 0 COMMENT 'L2推荐人佣', " +
|
|
|
- "rule_id BIGINT DEFAULT NULL COMMENT '使用的分润规则ID', " +
|
|
|
- "created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
- "INDEX idx_order (order_id), " +
|
|
|
- "INDEX idx_order_no (order_no)" +
|
|
|
- ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='分润流水");
|
|
|
- log.info("已创建danshop_profit_logs 表");
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("创建 danshop_profit_logs 表失败 {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- // 9. danshop_favorites (商品收藏
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS danshop_favorites (" +
|
|
|
- "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
- "user_id BIGINT NOT NULL COMMENT '用户ID', " +
|
|
|
- "product_id BIGINT NOT NULL COMMENT '商品ID', " +
|
|
|
- "created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
- "UNIQUE INDEX idx_user_product (user_id, product_id), " +
|
|
|
- "INDEX idx_user (user_id)" +
|
|
|
- ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品收藏");
|
|
|
- log.info("已创建danshop_favorites 表");
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("创建 danshop_favorites 表失败 {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- // 10. danshop_addresses (收货地址
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS danshop_addresses (" +
|
|
|
- "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
- "user_id BIGINT NOT NULL COMMENT '用户ID', " +
|
|
|
- "receiver_name VARCHAR(32) NOT NULL COMMENT '收货人姓, " +
|
|
|
- "phone VARCHAR(16) NOT NULL COMMENT '收货人电, " +
|
|
|
- "province VARCHAR(32) NOT NULL COMMENT '省份', " +
|
|
|
- "city VARCHAR(32) NOT NULL COMMENT '城市', " +
|
|
|
- "district VARCHAR(32) NOT NULL COMMENT '区县', " +
|
|
|
- "street VARCHAR(256) NOT NULL COMMENT '详细地址', " +
|
|
|
- "is_default INT DEFAULT 0 COMMENT '是否默认地址 1=0=, " +
|
|
|
- "created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
- "updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, " +
|
|
|
- "INDEX idx_user (user_id), " +
|
|
|
- "INDEX idx_user_default (user_id, is_default)" +
|
|
|
- ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='收货地址");
|
|
|
- log.info("已创建danshop_addresses 表");
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("创建 danshop_addresses 表失败 {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- // ====== 迁移: activities表添加visible_scope======
|
|
|
- try {
|
|
|
- Integer colExists = jdbcTemplate.queryForObject(
|
|
|
- "SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'activities' AND COLUMN_NAME = 'visible_scope'",
|
|
|
- Integer.class);
|
|
|
- if (colExists == null || colExists == 0) {
|
|
|
- jdbcTemplate.execute("ALTER TABLE activities ADD COLUMN visible_scope VARCHAR(20) DEFAULT NULL COMMENT '可见范围: family(家庭)/role(角色)/user(用户)' AFTER visibility");
|
|
|
- log.info("已添加visible_scope列到activities表");
|
|
|
- } else {
|
|
|
- log.info("activities表visible_scope列已存在,跳过表");
|
|
|
- }
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("检查添加activities表visible_scope列失败 {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- // ====== 迁移: articles表添加article_type======
|
|
|
- try {
|
|
|
- Integer colExists = jdbcTemplate.queryForObject(
|
|
|
- "SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'articles' AND COLUMN_NAME = 'article_type'",
|
|
|
- Integer.class);
|
|
|
- if (colExists == null || colExists == 0) {
|
|
|
- jdbcTemplate.execute("ALTER TABLE articles ADD COLUMN article_type VARCHAR(20) DEFAULT 'normal' COMMENT '文章类型: normal(普/premium(优质)/original(原创)' AFTER read_time");
|
|
|
- log.info("已添加article_type列到articles表");
|
|
|
- } else {
|
|
|
- log.info("articles表article_type列已存在,跳过表");
|
|
|
- }
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("检查添加articles表article_type列失败 {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- // ====== 迁移: articles表添加word_count======
|
|
|
- try {
|
|
|
- Integer colExists = jdbcTemplate.queryForObject(
|
|
|
- "SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'articles' AND COLUMN_NAME = 'word_count'",
|
|
|
- Integer.class);
|
|
|
- if (colExists == null || colExists == 0) {
|
|
|
- jdbcTemplate.execute("ALTER TABLE articles ADD COLUMN word_count INT DEFAULT 0 COMMENT '文章字数' AFTER article_type");
|
|
|
- log.info("已添加word_count列到articles表");
|
|
|
- } else {
|
|
|
- log.info("articles表word_count列已存在,跳过表");
|
|
|
- }
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("检查添加articles表word_count列失败 {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- // ====== 迁移: article_reading_records表添加task_id======
|
|
|
- try {
|
|
|
- Integer colExists = jdbcTemplate.queryForObject(
|
|
|
- "SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'article_reading_records' AND COLUMN_NAME = 'task_id'",
|
|
|
- Integer.class);
|
|
|
- if (colExists == null || colExists == 0) {
|
|
|
- jdbcTemplate.execute("ALTER TABLE article_reading_records ADD COLUMN task_id BIGINT DEFAULT NULL COMMENT '关联的任务ID' AFTER duration_seconds");
|
|
|
- log.info("已添加task_id列到article_reading_records表");
|
|
|
- } else {
|
|
|
- log.info("article_reading_records表task_id列已存在,跳过表");
|
|
|
- }
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("检查添加article_reading_records表task_id列失败 {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- // ====== 迁移: tasks表添加source_type======
|
|
|
- try {
|
|
|
- Integer colExists = jdbcTemplate.queryForObject(
|
|
|
- "SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'tasks' AND COLUMN_NAME = 'source_type'",
|
|
|
- Integer.class);
|
|
|
- if (colExists == null || colExists == 0) {
|
|
|
- jdbcTemplate.execute("ALTER TABLE tasks ADD COLUMN source_type VARCHAR(20) DEFAULT NULL COMMENT '来源类型: article/activity/manual' AFTER minigame_code");
|
|
|
- log.info("已添加source_type列到tasks表");
|
|
|
- } else {
|
|
|
- log.info("tasks表source_type列已存在,跳过表");
|
|
|
- }
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("检查添加tasks表source_type列失败 {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- // ====== 迁移: tasks表添加source_id======
|
|
|
- try {
|
|
|
- Integer colExists = jdbcTemplate.queryForObject(
|
|
|
- "SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'tasks' AND COLUMN_NAME = 'source_id'",
|
|
|
- Integer.class);
|
|
|
- if (colExists == null || colExists == 0) {
|
|
|
- jdbcTemplate.execute("ALTER TABLE tasks ADD COLUMN source_id BIGINT DEFAULT NULL COMMENT '来源ID' AFTER source_type");
|
|
|
- log.info("已添加source_id列到tasks表");
|
|
|
- } else {
|
|
|
- log.info("tasks表source_id列已存在,跳过表");
|
|
|
- }
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("检查添加tasks表source_id列失败 {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- // ====== 迁移: activities表添加checkin_points======
|
|
|
- try {
|
|
|
- Integer colExists = jdbcTemplate.queryForObject(
|
|
|
- "SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'activities' AND COLUMN_NAME = 'checkin_points'",
|
|
|
- Integer.class);
|
|
|
- if (colExists == null || colExists == 0) {
|
|
|
- jdbcTemplate.execute("ALTER TABLE activities ADD COLUMN checkin_points INT DEFAULT 0 COMMENT '签到积分(0=使用系统默认)' AFTER price");
|
|
|
- log.info("已添加checkin_points列到activities表");
|
|
|
- } else {
|
|
|
- log.info("activities表checkin_points列已存在,跳过表");
|
|
|
- }
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("检查添加activities表checkin_points列失败 {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- // 迁移: 创建family_invitations表(家庭邀请令牌)
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS family_invitations (" +
|
|
|
- "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
- "family_id BIGINT NOT NULL COMMENT '家庭ID', " +
|
|
|
- "token VARCHAR(64) NOT NULL UNIQUE COMMENT '邀请令UUID)', " +
|
|
|
- "created_by BIGINT NOT NULL COMMENT '创建人用户ID', " +
|
|
|
- "status VARCHAR(16) DEFAULT 'active' COMMENT '状态 active/used/expired/revoked', " +
|
|
|
- "expires_at DATETIME 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_token (token), " +
|
|
|
- "INDEX idx_status (status)" +
|
|
|
- ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='家庭邀请令");
|
|
|
- log.info("已创建family_invitations表");
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("创建family_invitations表失败 {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- // ====== P0 营养 nutrition_indicator_mapping ======
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS nutrition_indicator_mapping (" +
|
|
|
- "id BIGINT AUTO_INCREMENT PRIMARY KEY," +
|
|
|
- "indicator_name_pattern VARCHAR(200) NOT NULL COMMENT '指标名匹配模SQL LIKE语法,%为通配'," +
|
|
|
- "nutrient VARCHAR(50) NOT NULL COMMENT '关联营养素名'," +
|
|
|
- "severity_weight DECIMAL(3,2) DEFAULT 1.00 COMMENT '权重(0-1)'," +
|
|
|
- "deficiency_symptoms TEXT COMMENT '缺乏症状描述'," +
|
|
|
- "excess_symptoms TEXT COMMENT '过量症状描述'," +
|
|
|
- "food_suggestions TEXT COMMENT '推荐食物(逗号分隔)'," +
|
|
|
- "sort_order INT DEFAULT 0 COMMENT '排序(大值优先匹'," +
|
|
|
- "created_at DATETIME DEFAULT CURRENT_TIMESTAMP," +
|
|
|
- "updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP," +
|
|
|
- "INDEX idx_nutrient (nutrient)" +
|
|
|
- ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='指标-营养素映射表'");
|
|
|
- log.info("已创建nutrition_indicator_mapping 表");
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("创建 nutrition_indicator_mapping 表失败 {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- // P0 营养 nutrition_deficiency_record
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS nutrition_deficiency_record (" +
|
|
|
- "id BIGINT AUTO_INCREMENT PRIMARY KEY," +
|
|
|
- "child_id BIGINT NOT NULL COMMENT '孩子ID'," +
|
|
|
- "report_id BIGINT NOT NULL COMMENT '关联报告ID'," +
|
|
|
- "nutrient VARCHAR(50) NOT NULL COMMENT '营养素名'," +
|
|
|
- "deficiency_type VARCHAR(20) NOT NULL COMMENT 'deficiency/excess/borderline/unclassified'," +
|
|
|
- "severity DECIMAL(5,2) DEFAULT 0 COMMENT '严重程度(0-1)'," +
|
|
|
- "indicator_name VARCHAR(100) COMMENT '关联指标," +
|
|
|
- "indicator_value VARCHAR(50) COMMENT '指标检测," +
|
|
|
- "ref_range VARCHAR(100) COMMENT '参考范," +
|
|
|
- "symptoms TEXT COMMENT '症状描述'," +
|
|
|
- "food_suggestions TEXT COMMENT '推荐食物'," +
|
|
|
- "created_at DATETIME DEFAULT CURRENT_TIMESTAMP," +
|
|
|
- "INDEX idx_child (child_id)," +
|
|
|
- "INDEX idx_report (report_id)," +
|
|
|
- "INDEX idx_nutrient (nutrient)" +
|
|
|
- ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='营养缺乏分析记录'");
|
|
|
- log.info("已创建nutrition_deficiency_record 表");
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("创建 nutrition_deficiency_record 表失败 {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- // P0 营养 指标-营养素映种子数据
|
|
|
- try {
|
|
|
- Integer count = jdbcTemplate.queryForObject(
|
|
|
- "SELECT COUNT(*) FROM nutrition_indicator_mapping", Integer.class);
|
|
|
- if (count == null || count == 0) {
|
|
|
- String[][] seedData = {
|
|
|
- {"铁", "1.00", "乏力、面色苍白、注意力不集中", "铁过量可能损伤肝脏", "动物肝脏,红肉,菠菜,黑木耳", "10"},
|
|
|
- {"25-羟基维生素D", "维生素D", "1.00", "影响钙吸收、骨骼发育", "维生素D过量可致高钙血症", "三文鱼,蛋黄,蘑菇,强化牛奶", "20"},
|
|
|
- {"维生素D", "维生素D", "1.00", "影响钙吸收、骨骼发育、免疫力下降", "维生素D过量可致高钙血症", "三文鱼,蛋黄,蘑菇,强化牛奶", "15"},
|
|
|
- {"锌", "锌", "0.80", "食欲减退、味觉异常、免疫力降低", "锌过量影响铜吸收", "牡蛎,牛肉,南瓜,扁豆", "10"},
|
|
|
- {"钙", "钙", "0.80", "骨骼发育迟缓、牙齿问题", "高钙血症、肾结石", "牛奶,豆腐,虾皮,芝麻", "10"},
|
|
|
- {"维生素B12", "维生素B12", "1.00", "贫血、神经发育迟缓", "一般不易过量", "牛肉,鸡蛋,牛奶,鱼肉", "10"},
|
|
|
- {"维生素C", "维生素C", "0.60", "免疫力下降、牙龈出血", "腹泻、肾结石风险", "猕猴橙子,草莓,青椒", "10"},
|
|
|
- {"蛋白质", "蛋白质", "0.90", "发育迟缓、肌肉萎缩", "肾脏负担", "鸡蛋,鱼肉,牛奶,豆制品", "10"},
|
|
|
- {"总蛋白", "蛋白质", "0.90", "营养不良、免疫力下降", "血液粘稠", "鸡蛋,鱼肉,牛奶,豆制品", "9"},
|
|
|
- {"膳食纤维", "膳食纤维", "0.50", "便秘、肠道菌群失衡", "腹胀、排便过多", "全谷物,蔬菜,豆类,水果", "10"},
|
|
|
- {"叶酸", "叶酸", "0.70", "贫血、发育迟缓", "一般不易过量", "菠菜,动物肝脏,豆类,柑橘", "10"},
|
|
|
- {"维生素A", "维生素A", "0.70", "视力问题、皮肤干燥、免疫力下降", "头痛、肝脏损伤", "胡萝卜,动物肝脏,南瓜,菠菜", "10"},
|
|
|
- {"硒", "硒", "0.50", "免疫力下降、甲状腺功能异常", "脱发、指甲脆", "巴西坚果,海鲜,蘑菇", "10"},
|
|
|
- {"镁", "镁", "0.40", "肌肉痉挛、睡眠问题", "腹泻、低血压", "坚果,全谷物,深绿蔬菜", "10"},
|
|
|
- {"益生菌", "益生菌", "0.60", "肠道菌群失衡、消化问题", "一般不易过量", "酸奶,泡菜,味噌,康普茶", "10"}
|
|
|
- };
|
|
|
- for (String[] row : seedData) {
|
|
|
- jdbcTemplate.update("INSERT IGNORE INTO nutrition_indicator_mapping " +
|
|
|
- "(indicator_name_pattern, nutrient, severity_weight, deficiency_symptoms, excess_symptoms, food_suggestions, sort_order) " +
|
|
|
- "VALUES (?, ?, ?, ?, ?, ?, ?)",
|
|
|
- row[0], row[1], new java.math.BigDecimal(row[2]), row[3], row[4], row[5], Integer.parseInt(row[6]));
|
|
|
- }
|
|
|
- log.info("nutrition_indicator_mapping 种子数据已加载(15");
|
|
|
- } else {
|
|
|
- log.info("nutrition_indicator_mapping 种子数据已存在({}, 跳过", count);
|
|
|
- }
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("nutrition_indicator_mapping 种子数据初始化失败 {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- // ====== 心版Phase 1: emotion_checkin ======
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS emotion_checkin (" +
|
|
|
- "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
- "child_id BIGINT NOT NULL COMMENT '孩子ID', " +
|
|
|
- "user_id BIGINT NOT NULL COMMENT '打卡人用户ID', " +
|
|
|
- "emotion_type INT NOT NULL COMMENT '情绪类型: 1快乐/2平和平静/3平静/4有点不舒5难受/6烦躁/7生气/8焦虑/9恐惧', " +
|
|
|
- "emotion_name VARCHAR(20) COMMENT '情绪名称: happy/calm/peaceful/uncomfortable/sad/irritable/angry/anxious/fearful', " +
|
|
|
- "energy_level INT NOT NULL DEFAULT 3 COMMENT '能量状态 1很低/23中等/45很高', " +
|
|
|
- "note TEXT COMMENT '打卡备注', " +
|
|
|
- "photo_url VARCHAR(500) COMMENT '照片URL', " +
|
|
|
- "favorite_thing VARCHAR(500) COMMENT '今日最喜欢的一件事', " +
|
|
|
- "points_awarded INT DEFAULT 0 COMMENT '奖励积分', " +
|
|
|
- "parent_visible TINYINT DEFAULT 1 COMMENT '家长可见: 1可见/0不可, " +
|
|
|
- "checkin_date DATE NOT NULL COMMENT '打卡日期', " +
|
|
|
- "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
- "updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, " +
|
|
|
- "INDEX idx_child_id (child_id), " +
|
|
|
- "INDEX idx_user_id (user_id), " +
|
|
|
- "INDEX idx_checkin_date (checkin_date), " +
|
|
|
- "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());
|
|
|
- }
|
|
|
-
|
|
|
- // ====== 1a-2: DAN Schema 修复 ======
|
|
|
- // v1.2 EMI 心理子维
|
|
|
- String[] danEmiFields = {"emotion_score", "resilience_score", "stress_coping_score", "self_awareness_score"};
|
|
|
- for (String field : danEmiFields) {
|
|
|
- try {
|
|
|
- Integer exists = jdbcTemplate.queryForObject(
|
|
|
- "SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'dan_assessment_results' AND COLUMN_NAME = ?",
|
|
|
- Integer.class, field);
|
|
|
- if (exists == null || exists == 0) {
|
|
|
- jdbcTemplate.execute("ALTER TABLE dan_assessment_results ADD COLUMN " + field + " INT DEFAULT NULL COMMENT 'EMI子维0-100'");
|
|
|
- log.info("已添加{} 列到 dan_assessment_results , field表");
|
|
|
- }
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("检查添加 {} 列失败 {}", field, e.getMessage());
|
|
|
- }
|
|
|
- }
|
|
|
- // 大五人格维度
|
|
|
- String[] danBigFiveFields = {"openness_score", "conscientiousness_score", "extraversion_score", "agreeableness_score", "neuroticism_score", "big_five_overall_score"};
|
|
|
- for (String field : danBigFiveFields) {
|
|
|
- try {
|
|
|
- Integer exists = jdbcTemplate.queryForObject(
|
|
|
- "SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'dan_assessment_results' AND COLUMN_NAME = ?",
|
|
|
- Integer.class, field);
|
|
|
- if (exists == null || exists == 0) {
|
|
|
- jdbcTemplate.execute("ALTER TABLE dan_assessment_results ADD COLUMN " + field + " INT DEFAULT NULL COMMENT '大五人格 0-100'");
|
|
|
- log.info("已添加{} 列到 dan_assessment_results , field表");
|
|
|
- }
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("检查添加 {} 列失败 {}", field, e.getMessage());
|
|
|
- }
|
|
|
- }
|
|
|
- // EMI 维度(情绪商数评估字段)
|
|
|
- String[] danEmiDimFields = {"emotion_management_score", "empathy_score", "social_adaptability_score", "self_motivation_score"};
|
|
|
- for (String field : danEmiDimFields) {
|
|
|
- try {
|
|
|
- Integer exists = jdbcTemplate.queryForObject(
|
|
|
- "SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'dan_assessment_results' AND COLUMN_NAME = ?",
|
|
|
- Integer.class, field);
|
|
|
- if (exists == null || exists == 0) {
|
|
|
- jdbcTemplate.execute("ALTER TABLE dan_assessment_results ADD COLUMN " + field + " INT DEFAULT NULL COMMENT 'EMI维度 0-100'");
|
|
|
- log.info("已添加{} 列到 dan_assessment_results , field表");
|
|
|
- }
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("检查添加 {} 列失败 {}", field, e.getMessage());
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // ====== 1b-1: bazi_reading_template 五行命理解读======
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS bazi_reading_template (" +
|
|
|
- "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
- "element VARCHAR(20) NOT NULL COMMENT '五行: wood/fire/earth/metal/water', " +
|
|
|
- "condition VARCHAR(20) NOT NULL COMMENT '状态 strong/weak/balanced', " +
|
|
|
- "personal_trait TEXT COMMENT '性格特质描述', " +
|
|
|
- "mind_growth_advice TEXT COMMENT '心理成长建议', " +
|
|
|
- "daily_tip_template TEXT COMMENT '每日宜忌模板', " +
|
|
|
- "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
- "UNIQUE KEY uk_element_condition (element, condition)" +
|
|
|
- ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='五行命理解读模板'");
|
|
|
- log.info("已创建bazi_reading_template 表");
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("创建 bazi_reading_template 表失败 {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- // ====== 1b-1: numsoul_config 生命灵数配置======
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS numsoul_config (" +
|
|
|
- "number INT PRIMARY KEY COMMENT '灵数1-9', " +
|
|
|
- "title VARCHAR(50) NOT NULL COMMENT '称号', " +
|
|
|
- "keywords VARCHAR(200) COMMENT '性格关键, " +
|
|
|
- "mind_growth_advice TEXT COMMENT '心理成长建议', " +
|
|
|
- "recommended_article_tags VARCHAR(200) COMMENT '关联文章标签', " +
|
|
|
- "color_hex VARCHAR(7) COMMENT '代表 " +
|
|
|
- ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='生命灵数配置'");
|
|
|
- log.info("已创建numsoul_config 表");
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("创建 numsoul_config 表失败 {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- // ====== 1b-1: bazi_reading_template 种子数据 ======
|
|
|
- try {
|
|
|
- Integer count = jdbcTemplate.queryForObject(
|
|
|
- "SELECT COUNT(*) FROM bazi_reading_template", Integer.class);
|
|
|
- if (count == null || count == 0) {
|
|
|
- jdbcTemplate.batchUpdate(
|
|
|
- "INSERT INTO bazi_reading_template (element, condition, personal_trait, mind_growth_advice, daily_tip_template) VALUES (?, ?, ?, ?, ?)",
|
|
|
- java.util.Arrays.asList(new Object[][]{
|
|
|
- {"wood", "strong", "性格坚韧、有进取心,具有领导力和开拓精神", "学会适当放松,避免过刚易折,培养弹性思维", "宜进行伸展运动,忌高强度冲突"},
|
|
|
- {"wood", "weak", "温和有礼、善于合作,但决策时容易犹豫", "增强自信心训练,培养果断决策能力", "宜与自然相处,忌过度迁就他人"},
|
|
|
- {"wood", "balanced", "性格中庸有度,既有韧性又有灵活性", "保持现状,适当拓展社交圈", "宜户外活动,忌久坐不动"},
|
|
|
- {"fire", "strong", "热情开朗、充满活力,富有感染力和创造力", "注意情绪管理,避免急躁冲动", "宜冷水刺激冷静,忌暴怒争吵"},
|
|
|
- {"fire", "weak", "内敛含蓄、心思细腻,对艺术有敏锐感知", "适当表达情感,培养社交自信", "宜温暖陪伴,忌独处过度"},
|
|
|
- {"fire", "balanced", "性格温润有度,既有热情又懂克制", "保持兴趣爱好,适度拓展表达", "宜参与社交活动,忌情绪压抑"},
|
|
|
- {"earth", "strong", "稳重可靠、有责任心,是团队中的中流砥柱", "避免过度承担,学会说不", "宜适量运动,忌思虑过重"},
|
|
|
- {"earth", "weak", "随和友善、适应性强,容易与他人相处", "增强主见性,培养独立思考能力", "宜接触新事物,忌固守不变"},
|
|
|
- {"earth", "balanced", "性情敦厚、处事稳妥,人际关系和谐", "保持学习,适当挑战自我", "宜亲近自然,忌暴饮暴食"},
|
|
|
- {"metal", "strong", "意志坚定、追求完美,具有卓越的执行力", "学会灵活变通,避免过于严苛", "宜艺术鉴赏,忌过度批评"},
|
|
|
- {"metal", "weak", "温和寡言、善于倾听,是值得信赖的朋友", "增强表达勇气,培养自信心", "宜团体活动,忌过度自我封闭"},
|
|
|
- {"metal", "balanced", "为人正直、做事有度,不乏柔韧与包容", "保持开放心态,适度多元发展", "宜阅读学习,忌钻牛角尖"},
|
|
|
- {"water", "strong", "智慧深沉、善于谋略,具有出色洞察力", "避免过度缜密,适当放松心情", "宜静坐冥想,忌思虑过深"},
|
|
|
- {"water", "weak", "敏感细腻、有同理心,富有创作天赋", "增强抵抗力,培养情绪韧性", "宜温暖环境,忌受寒受凉"},
|
|
|
- {"water", "balanced", "性格圆融、随机应变,处事灵活得宜", "保持好奇心,适度扩展影响力", "宜游泳泡澡,忌情绪波动"},
|
|
|
- })
|
|
|
- );
|
|
|
- log.info("bazi_reading_template 种子数据已加载(15");
|
|
|
- }
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("bazi_reading_template 种子数据初始化失败 {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- // ====== 1b-1: numsoul_config 种子数据 ======
|
|
|
- try {
|
|
|
- Integer count = jdbcTemplate.queryForObject(
|
|
|
- "SELECT COUNT(*) FROM numsoul_config", Integer.class);
|
|
|
- if (count == null || count == 0) {
|
|
|
- jdbcTemplate.batchUpdate(
|
|
|
- "INSERT INTO numsoul_config (number, title, keywords, mind_growth_advice, recommended_article_tags, color_hex) VALUES (?, ?, ?, ?, ?, ?)",
|
|
|
- java.util.Arrays.asList(new Object[][]{
|
|
|
- {1, "开创", "独立、领导、创新、自主", "培养团队协作意识,避免独断专行", "领导自信培养", "#FF4444"},
|
|
|
- {2, "和平", "温和、合作、敏感、耐心", "增强自我主张,避免过度迎合他人", "人际交往,情绪表达", "#FF8C00"},
|
|
|
- {3, "表达", "创意、社交、乐观、表现力", "保持专注,避免浅尝辄止", "创造力,社交能力", "#FFD700"},
|
|
|
- {4, "建设", "务实、稳定、有序、勤奋", "学会变通,避免过于固执", "专注学习习惯", "#4CAF50"},
|
|
|
- {5, "自由", "冒险、变化、好奇、灵活", "培养持久力,避免缺乏恒心", "探索精神,适应能力", "#2196F3"},
|
|
|
- {6, "关怀", "责任、关爱、奉献、平衡", "学会自我关爱,避免过度付出", "家庭关系,责任", "#9C27B0"},
|
|
|
- {7, "探索", "智慧、分析、内省、完美", "与人分享见解,避免过度封闭", "思维能力,学习", "#673AB7"},
|
|
|
- {8, "成就", "雄心、管理、决策、权力", "关注内在成长,避免过于追求外在成就", "目标管理,理财思维", "#3F51B5"},
|
|
|
- {9, "博爱", "包容、智慧、善良、豁达", "设定个人边界,避免过度牺牲", "人际关系,情绪管理", "#E91E63"},
|
|
|
- })
|
|
|
- );
|
|
|
- log.info("numsoul_config 种子数据已加载(9");
|
|
|
- }
|
|
|
-} catch (Exception e) {
|
|
|
- log.warn("numsoul_config 种子数据初始化失败 {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- 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 '折扣百分0表示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());
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- 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, \'玩平板游0分钟\', 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', '积分达人', '一周获0积分', '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', '舒尔特方, '训练专注力和眼动能力,找-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), " +
|
|
|
- "('练习书法', '练习毛笔或硬笔书0分钟', 7, '学习', 0, 7, 'medium', 1), " +
|
|
|
- "('早睡早起', '晚上9点前睡觉,早点前起床', 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)",
|
|
|
- "新用户专0元券", "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表添加个菌群摘要字段表");
|
|
|
- } 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(福宝)'");
|
|
|
-
|
|
|
-
|
|
|
- // family_relationships 表(远程新增
|
|
|
- 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());
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- // 迁移41: 初始化五维知识中心文章分类和种子文章
|
|
|
- try {
|
|
|
- Integer catCount = jdbcTemplate.queryForObject(
|
|
|
- "SELECT COUNT(*) FROM article_categories WHERE name IN (', ', ', ', ')", Integer.class);
|
|
|
- if (catCount == null || catCount == 0) {
|
|
|
- jdbcTemplate.execute("INSERT IGNORE INTO article_categories (name, icon, color, sort_order, status) VALUES " +
|
|
|
- "(', 'health', '#FF8C42', 1, 1), " +
|
|
|
- "(', 'wisdom', '#6366F1', 2, 1), " +
|
|
|
- "(', 'love', '#FF6B9D', 3, 1), " +
|
|
|
- "(', 'action', '#10B981', 4, 1), " +
|
|
|
- "(', 'wealth', '#F59E0B', 5, 1)");
|
|
|
- log.info("五维文章分类已初始化");
|
|
|
- }
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("初始化文章分类失败 {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- try {
|
|
|
- Integer articleCount = jdbcTemplate.queryForObject(
|
|
|
- "SELECT COUNT(*) FROM articles WHERE created_by = 0 AND status = 'published'", Integer.class);
|
|
|
- if (articleCount != null && articleCount > 0) {
|
|
|
- log.info("种子文章已存在,跳过初始表");
|
|
|
- } else {
|
|
|
- Map<String, Long> catIdMap = new HashMap<>();
|
|
|
- jdbcTemplate.query(
|
|
|
- "SELECT id, name FROM article_categories WHERE name IN (', ', ', ', ')",
|
|
|
- (rs) -> {
|
|
|
- catIdMap.put(rs.getString("name"), rs.getLong("id"));
|
|
|
- });
|
|
|
-
|
|
|
- if (catIdMap.size() == 5) {
|
|
|
- long bodyCatId = catIdMap.get("body");
|
|
|
- long mindCatId = catIdMap.get("mind");
|
|
|
- long heartCatId = catIdMap.get("wisdom");
|
|
|
- long actionCatId = catIdMap.get("action");
|
|
|
- long wealthCatId = catIdMap.get("wealth");
|
|
|
-
|
|
|
- insertSeedArticle("好水养人:家庭健康从一杯好水开",
|
|
|
-
|
|
|
- "水是生命之源,但你真的会喝水吗?从水质选择到饮水习惯,本文将带你了解如何通过一杯好水滋养全家人",
|
|
|
-
|
|
|
- "水被忽视的营养素。在营养学中,水被称为\"第七大营养素\",然而大多数人只关注吃什么,却忽略了喝什么。人体的60%是水,血液的90%是水,大脑的75%是水。水的质量,直接决定了身体的运转效率。好水的标准:安全是第一位的,天然的矿物质含量是加分项。每个家庭成员对水的需求不同,从今天开始,为家人准备一杯好水,就是最简单的健康投资",
|
|
|
- "浠艾", 6, "body", 1);
|
|
|
-
|
|
|
- insertSeedArticle("儿童体质调理的黄金期,你抓住了吗",
|
|
|
-
|
|
|
- "中医讲\"春生夏长,秋收冬藏\",儿童的体质调理也有其黄金时期。本文从营养、运动、睡眠三个维度,告诉你如何为孩子打下坚实的健康基础",
|
|
|
- "体质调理,从了解开始。每个孩子天生体质不同,了解孩子的体质类型,是调理的第一步。抓住三个黄金期-3岁基础免疫期,母乳喂养提供天然抗体-7岁脾胃调理期,少食生冷、定时定量;7-12岁生长发育期,需要充足的蛋白质、钙和维生素D。家庭调理小贴士:晨起捏脊增强免疫力,睡前泡脚促进血液循环,节气调理调整饮食和作息",
|
|
|
-
|
|
|
- "浠艾", 8, "body", 1);
|
|
|
-
|
|
|
- insertSeedArticle("五维健康早餐:让孩子一天元气满",
|
|
|
-
|
|
|
- "一日之计在于晨,早餐的质量直接影响孩子一天的学习效率和身体状态。一份覆盖五维营养的早餐,让孩子从早上就充满能量",
|
|
|
-
|
|
|
- "为什么早餐如此重要?研究表明,吃早餐的孩子在注意力、记忆力和创造力方面都明显优于不吃早餐的孩子。五维营养早餐公式:优质碳水提供持续能量,优质蛋白是身体修复的基础,维生素和矿物质增强免疫力,健康脂肪促进大脑发育,益生菌呵护肠道健康。养成规律的早餐习惯,是给孩子最好的健康礼物",
|
|
|
-
|
|
|
- "浠艾", 5, "body", 0);
|
|
|
-
|
|
|
- insertSeedArticle("DAN测评:发现孩子与众不同的天赋密码",
|
|
|
- "每个孩子都是独一无二的。DAN多维认知测评从六大维度全面解读孩子的认知特点,帮助家长找到最适合孩子的成长路径",
|
|
|
-
|
|
|
- "什么是DAN测评?DAN测评是一套基于认知心理学和发展心理学理论的多维评估体系,从注意力、记忆力、逻辑思维、空间感知、语言能力和情绪理解六个维度,全面描绘孩子的认知发展图谱。测评的目的不是给孩子贴标签,而是为家长提供一幅\"认知地图\"。有了这张地图,你就可以选择适合孩子认知特点的学习方法,发现潜在的天赋领域加以培养,针对性地训练薄弱环节",
|
|
|
- "浠艾", 7, "mind", 1);
|
|
|
-
|
|
|
- insertSeedArticle("成长型思维:给孩子一生受用的心理资本",
|
|
|
- "斯坦福大学心理学教授卡罗尔·德韦克研究发现,拥有成长型思维的孩子更愿意接受挑战,面对挫折时更有韧性。如何培养孩子的成长型思维",
|
|
|
-
|
|
|
- "固定型思维的孩子认为\"能力是天生的,改变不了\",遇到困难容易放弃。而成长型思维的孩子相信\"能力可以通过努力提升\",把挑战视为成长的机会。研究发现,这两种思维模式对孩子的学业成就、人际关系甚至未来的职业发展都有深远影响。当孩子完成一件事时,你的反馈方式至关重要:表扬努力而非聪明,关注进步而非结果,把失败变成学习机会",
|
|
|
- "浠艾", 6, "mind", 0);
|
|
|
-
|
|
|
- insertSeedArticle("专注力:决定孩子学习效率的关键能",
|
|
|
-
|
|
|
- "在信息爆炸的时代,专注力成为最稀缺的能力。本文从脑科学角度解读专注力的本质,并给出提升孩子专注力的实用方法",
|
|
|
-
|
|
|
- "哈佛大学的一项长5年的追踪研究发现,决定一个人成功的不是智商,而是专注力和自控力。孩子的专注力是随着年龄逐渐发展的:3岁孩子能专注5-10分钟岁孩子能专注15-20分钟0岁以上可以专5-30分钟。破坏孩子专注力的三个习惯:频繁打断、多任务并行、过度刺激。提升专注力的实用方法:番茄工作5分钟专注分钟休息,舒尔特方格每天5分钟训练视觉专注,冥想练习每-5分钟的正念呼吸",
|
|
|
-
|
|
|
- "浠艾", 7, "mind", 0);
|
|
|
-
|
|
|
- insertSeedArticle("读懂孩子的情绪密码:亲子沟通的艺术",
|
|
|
- "孩子哭闹、发脾气、沉默不语……每个情绪背后都有一个未被满足的需求。学会倾听孩子的情绪语言,是建立亲子信任的第一步",
|
|
|
-
|
|
|
- "情绪是孩子最真实的语言。对于语言表达能力尚未成熟的孩子来说,情绪是他们最直接的沟通方式。当孩子发脾气时,他最需要的不是被制止,而是被理解。情绪接纳四步法:停下来蹲下来与孩子平视,帮孩子给情绪一个名字,让孩子知道所有情绪都是被允许的,一起寻找更合适的表达方式。通过绘本故事帮助孩子认识情绪,是很好的情绪启蒙方式",
|
|
|
-
|
|
|
- "浠艾", 6, "wisdom", 1);
|
|
|
-
|
|
|
- insertSeedArticle("家庭氛围:孩子心理健康的第一土壤",
|
|
|
- "一个温暖、稳定、充满爱的家庭环境,是孩子心理健康最坚实的保障。本文探讨如何营造积极健康的家庭氛围,让爱在家庭中流动",
|
|
|
-
|
|
|
- "<h2>家庭氛围的隐形力/h2><p>心理学研究表明,家庭氛围对儿童人格发展的影响超过了学校教育和社会环境。孩子就像一块海绵,无时无刻不在吸收着家庭中的情绪能量/p><p>如果家庭氛围是温暖、开放、包容的,孩子就会发展出安全感、自信心和探索世界的勇气。反之,如果家庭中充满了争吵、冷漠和批评,孩子就会变得焦虑、退缩或叛逆/p><h2>营造积极家庭氛围的三要/h2><p><b>1. 有规律的仪式/b>。每周一次的家庭会议、每晚的亲子阅读时间、周末的户外活动——这些固定的仪式让家庭成员之间建立情感连接/p><p><b>2. 积极沟通的语言习惯</b>。多用\"我\"字句表达感受(\"我感到有点累\"),少用\"你\"字句指责(\"你总是这样\")。每天至少说出三件值得感恩的事/p><p><b>3. 情绪修复的能/b>。冲突不可避免,重要的是冲突后的修复。真诚的道歉(\"刚才妈妈对你发脾气了,对不起\")比完美的教育更重要/p><h2>父母的关系是家庭的定海神/h2><p>对孩子来说,感受到父母相爱,比任何教育都重要。和谐的夫妻关系,就是给孩子最好的心理礼物/p>",
|
|
|
- "浠艾", 7, "wisdom", 0);
|
|
|
-
|
|
|
- insertSeedArticle("每个孩子都是天使:在不完美中发现完美",
|
|
|
- "我们总是在追求\"完美小孩\",却忽略了每个孩子身上那些独特的光芒。学会接纳孩子的不完美,也许是我们能给他们的最好的爱",
|
|
|
- "<h2>放下\"别人家孩子\"的执/h2><p>\"你看看人家小明,每次考试都是第一。\"这句话可能在很多人的童年里留下过阴影。我们一边反感自己被比较,一边又在不自觉地比较自己的孩子/p><p>每个孩子都有自己的成长节奏。有的孩子语言发育早,有的运动能力强,有的在社交方面天赋异禀。用统一的标准去衡量不同的孩子,就像用爬树的能力去评价一条鱼/p><h2>看见孩子真正的样/h2><p>试着静下心来,观察你的孩子:他专注时是什么样子?他开心时是怎么表达的?他遇到困难时是什么反应?当你放下期望,你才能真正看见——那个独一无二的小生命/p><p>也许他数学不好,但很有爱心;也许他好动坐不住,但充满创造力;也许他内向不爱说话,但心思细腻善解人意/p><h2>接纳是最好的教育</h2><p>当孩子感受到被完全接纳——包括他的优点和缺点——他就会建立起内在的安全感。这种安全感,是他探索世界、面对挑战的底气。正如心理学家温尼科特所说:\"孩子不需要完美的父母,只需要\"足够好\"的父母。\"</p>",
|
|
|
- "浠艾", 5, "wisdom", 0);
|
|
|
-
|
|
|
- // 行(人际和谐
|
|
|
- insertSeedArticle("亲子关系的五个黄金法",
|
|
|
-
|
|
|
- "好的亲子关系胜过任何教育方法。本文总结了五个经过实践验证的亲子关系法则,帮助你与孩子建立深厚的情感连接",
|
|
|
-
|
|
|
- "<h2>法则一:陪伴是最好的礼物</h2><p>不是\"在一起\"就是陪伴。高质量的陪伴需要全神贯注——放下手机,蹲下来,进入孩子的世界。每5分钟的全情陪伴,胜过一整天的身在心不在/p><h2>法则二:尊重是爱的前/h2><p>尊重孩子意味着:认真对待他的感受,不轻视他的需求,尊重他的节奏。当他感到被尊重,他才会学会尊重别人/p><h2>法则三:界限是爱的护/h2><p>爱不等于没有界限。清晰、一致、合理的规则让孩子感到安全。没有护栏的路,孩子不敢走;有护栏的路,孩子才能自由奔跑/p><h2>法则四:倾听比说教重/h2><p>当孩子想说话时,放下说教的心,只是认真地听。很多时候,孩子不需要你的答案,他只需要被听见/p><h2>法则五:错误是成长的机会</h2><p>没有人是完美的父母,也没有完美的孩子。犯错不可怕,重要的是从错误中学习。当你能坦然面对自己的错误,孩子也会学会勇敢面对他的不完美/p>",
|
|
|
- "浠艾", 5, "action", 1);
|
|
|
-
|
|
|
- insertSeedArticle("夫妻关系:家庭幸福的第一颗扣",
|
|
|
-
|
|
|
- "如果把家庭比作一件衣服,夫妻关系就是第一颗扣子。第一颗扣子扣对了,后面的扣子自然就顺了。良好的夫妻关系,是给孩子最好的成长礼物",
|
|
|
-
|
|
|
- "<h2>为什么夫妻关系比亲子关系更重/h2><p>在家庭系统中,夫妻关系是核心,亲子关系是从属关系。健康的家庭模式应该是:夫妻关系 > 亲子关系 > 原生家庭关系/p><p>当夫妻关系稳固,孩子会从父母身上学习到如何爱人、如何沟通、如何处理冲突。这些隐性教育,比任何课程都重要/p><h2>经营夫妻关系的三个小习惯</h2><p><b>1. 每天15分钟的\"夫妻时间\"</b>。孩子入睡后,放下家务和手机,只是聊聊天。不谈孩子,只谈彼此/p><p><b>2. 每周一次\"约会\"</b>。不用很隆重,两个人一起散步、看场电影、吃顿饭,找回恋爱时的感觉/p><p><b>3. 在孩子面前\"秀恩爱\"</b>。让孩子看到父母拥抱、说\"我爱你\"、互相感谢。这是最好的爱的教育/p><h2>冲突也是成长的机/h2><p>避免冲突不是健康的婚姻。重要的是如何解决冲突:不翻旧账、不人身攻击、不冷战。一次成功的冲突解决,会让关系更加亲密/p>",
|
|
|
- "浠艾", 6, "action", 0);
|
|
|
-
|
|
|
- insertSeedArticle("如何帮助孩子建立健康的社交圈",
|
|
|
- "社交能力是孩子未来成功的核心能力之一。从家庭到学校,从线上到线下,本文提供一套系统的儿童社交能力培养方案",
|
|
|
-
|
|
|
- "<h2>社交能力从家庭开/h2><p>孩子的第一个社交圈是家庭。在家庭中学会的沟通方式、分享精神和情绪管理,会直接迁移到学校和社会的社交中/p><p>父母要做的是:为孩子创造安全的社交练习环境,而不是替他解决问题/p><h2>分年龄段社交培养重点</h2><p><b>3-5岁:学会分享和轮/b>。通过游戏和日常活动,让孩子体验分享的快乐。不强迫分享,而是引导孩子理解\"轮流\"的概念/p><p><b>6-8岁:学习解决冲突</b>。当孩子和朋友发生矛盾时,引导他表达自己的感受,倾听对方的想法,一起寻找解决方案/p><p><b>9-12岁:培养团队合作</b>。鼓励参加团队运动和小组项目,学习在团队中发挥自己的优势,理解和尊重他人的不同/p><h2>当孩子遇到社交困难时</h2><p>如果孩子在学校感到孤独或被排挤:</p><ul><li>认真倾听他的感受,不要急于给建/li><li>帮助他发现自己的优势,建立自信心</li><li>创造课外社交机会,如兴趣班、社区活/li><li>必要时与老师沟通,了解学校里的情况</li></ul>",
|
|
|
- "浠艾", 7, "action", 0);
|
|
|
-
|
|
|
- // 富(利他创富
|
|
|
- insertSeedArticle("孩子的财商启蒙:从零花钱开",
|
|
|
-
|
|
|
- "财商教育不是教孩子\"赚钱\",而是培养他管理资源的智慧和能力。零花钱是一个绝佳的教育工具,让孩子在实践中学会规划、储蓄和分享",
|
|
|
- "<h2>为什么财商教育很重要</h2><p>财商(Financial Quotient, FQ)与智商、情商并列为现代社会三大核心素质。财商教育不是教孩子功利,而是教他如何与资源建立健康的关系——如何规划、如何选择、如何延迟满足、如何感恩和分享/p><h2>零花钱制度的黄金法则</h2><p><b>1. 定时给,不定/b>。每周或每月固定时间发放,但金额不宜过多,让孩子学会在有限的资源中做选择/p><p><b>2. 三个存储罐法/b>。准备三个罐子:\"储蓄罐\"(存起来以后用)、\"消费罐\"(可以随时花的)、\"分享罐\"(给需要帮助的人或买礼物)。每一次得到零花钱,按照比例分配/p><p><b>3. 允许犯错</b>。孩子可能会把钱一次性花光,这是他学习的机会。不要训斥,而是和他一起复盘:\"如果下次再来一次,你会怎么做?\"</p><h2>在浠艾福,我们这样做</h2><p>在浠艾福平台,孩子可以通过完成任务、坚持打卡获得积分,自己决定如何使用积分兑换奖励。这种\"付出→获得→选择\"的闭环,让孩子在真实场景中锻炼财商/p>",
|
|
|
- "浠艾", 6, "wealth", 1);
|
|
|
-
|
|
|
- insertSeedArticle("家庭理财入门:用爱管理家庭财",
|
|
|
-
|
|
|
- "家庭理财不只是管钱,更是管理家庭的幸福和安全感。一个健康的家庭财务体系,让每个家庭成员都能安心追求梦想",
|
|
|
-
|
|
|
- "<h2>家庭理财的本/h2><p>很多人觉得理财就是投资、赚钱,但其实家庭理财的本质是:用有限的资源,实现家庭最大的幸福。它包含四个层面:保障、消费、储蓄和增值/p><h2>家庭理财四步/h2><p><b>第一步:建立家庭账本</b>。记录一个月的收支,搞清楚钱花在哪里。推荐使用\"三个账户\"法:日常开销账户、储蓄投资账户、娱乐成长账户/p><p><b>第二步:建立应急基/b>。储-6个月的生活费,放在随时可取的地方。这笔钱是家庭的\"安全垫\"/p><p><b>第三步:先储蓄后消费</b>。每个月收入到账后,先存钱(收入0%-30%),再安排消费。这是一种强制储蓄的习惯/p><p><b>第四步:投资孩子的未/b>。除了教育储蓄,更重要的是投资在孩子的能力培养上——阅读、探索、体验/p><h2>给孩子的财富/h2><p>从小培养正确的财富观:钱是工具,不是目的;财富来源于创造价值;分享让财富更有意义。这些观念会影响孩子一生的财富之路/p>",
|
|
|
- "浠艾", 6, "wealth", 0);
|
|
|
-
|
|
|
- insertSeedArticle("创客成长计划:在利他中成就自",
|
|
|
-
|
|
|
- "浠艾福创客计划为每个家庭提供了低门槛的创客机会。在这里,你不仅能够获得额外收入,更能在帮助他人的过程中实现自我成长",
|
|
|
-
|
|
|
- "<h2>什么是创客成长计划</h2><p>创客成长计划是浠艾福打造的利他创富生态。我们相信,最好的商业模式是\"利他\"——通过分享优质的产品和服务,帮助更多家庭获得幸福,同时也实现自身的价值/p><h2>为什么选择成为创客</h2><p><b>低门槛启/b>:不需要大量资金,不需要囤货,只需要一颗愿意分享的心/p><p><b>成长体系完善</b>:从初级创客到高级合伙人,每一步都有清晰的成长路径和配套培训/p><p><b>利他即利/b>:当你把好的产品和服务分享给身边的朋友,帮助他们改善家庭健康和生活品质,这种价值感本身就是最大的回报/p><h2>创客的一/h2><p>早晨,你在朋友圈分享了一篇关于儿童营养的好文章;中午,有朋友私信咨询你关于孩子体质调理的建议;下午,你在创客社群参加了一场线上培训;晚上,你收获了几份订单和满满的成就感/p><p>这就是创客的生活——在学习和分享中,实现家庭与事业的双重成长/p>",
|
|
|
- "浠艾", 5, "wealth", 0);
|
|
|
-
|
|
|
- log.info("五维种子文章已初始化(共15篇)");
|
|
|
- } else {
|
|
|
- log.warn("文章分类不完整,跳过种子文章初始表");
|
|
|
- }
|
|
|
- }
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("初始化种子文章失败 {}", e.getMessage());
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private void insertSeedArticle(String title, String summary, String content,
|
|
|
- String author, int readTime, String dimensionCode, int isFeatured) {
|
|
|
- try {
|
|
|
- // 查询分类ID
|
|
|
- String dimCatMap = dimensionCode.equals("body") ? "body"
|
|
|
- : dimensionCode.equals("mind") ? "mind"
|
|
|
- : dimensionCode.equals("wisdom") ? "wisdom"
|
|
|
- : dimensionCode.equals("action") ? "action"
|
|
|
- : dimensionCode.equals("wealth") ? "wealth" : " ";
|
|
|
- Long categoryId = jdbcTemplate.queryForObject(
|
|
|
- "SELECT id FROM article_categories WHERE name = ?", Long.class, dimCatMap);
|
|
|
-
|
|
|
- if (categoryId == null) {
|
|
|
- log.warn("未找到分 {}, 跳过文章: {}", dimCatMap, title);
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- jdbcTemplate.update(
|
|
|
- "INSERT INTO articles (category_id, title, summary, content, author, read_time, " +
|
|
|
- "related_dimensions, status, audit_status, is_featured, visibility, content_type, " +
|
|
|
- "article_type, word_count, published_at, created_by, created_at, updated_at) " +
|
|
|
- "VALUES (?, ?, ?, ?, ?, ?, ?, 'published', 'approved', ?, 'public', 'article', " +
|
|
|
- "'original', ?, NOW(), 0, NOW(), NOW())",
|
|
|
- categoryId, title, summary, content, author, readTime,
|
|
|
- dimensionCode, isFeatured, content.length()
|
|
|
- );
|
|
|
- log.info("已创建种子文 {}", title);
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("创建种子文章失败 [{}]: {}", title, e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- // 迁移42: 创建收货人表(含购买所需特殊信息
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS consignees (" +
|
|
|
- "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
- "user_id BIGINT NOT NULL COMMENT '所属用户ID', " +
|
|
|
- "name VARCHAR(100) NOT NULL COMMENT '收货人姓, " +
|
|
|
- "phone VARCHAR(20) COMMENT '手机号', " +
|
|
|
- "id_card VARCHAR(20) COMMENT '身份证号', " +
|
|
|
- "hand_signature TEXT COMMENT '手签base64图片或URL)', " +
|
|
|
- "ethnicity VARCHAR(20) COMMENT '民族', " +
|
|
|
- "blood_type VARCHAR(20) COMMENT '血, " +
|
|
|
- "address VARCHAR(500) COMMENT '收货地址', " +
|
|
|
- "is_default TINYINT(1) DEFAULT 0 COMMENT '是否默认收货, " +
|
|
|
- "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("已创建consignees表");
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("创建consignees表可能已存在: {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- // 迁移42: 创建商品购买信息字段配置
|
|
|
- try {
|
|
|
- jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS product_purchase_fields (" +
|
|
|
- "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
- "product_id BIGINT NOT NULL COMMENT '商品ID', " +
|
|
|
- "field_key VARCHAR(50) NOT NULL COMMENT '字段标识', " +
|
|
|
- "field_name VARCHAR(100) NOT NULL COMMENT '显示, " +
|
|
|
- "is_required TINYINT(1) DEFAULT 1 COMMENT '是否必填', " +
|
|
|
- "sort_order INT DEFAULT 0 COMMENT '排序', " +
|
|
|
- "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
- "INDEX idx_product_id (product_id), " +
|
|
|
- "UNIQUE KEY uk_product_field (product_id, field_key)" +
|
|
|
- ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品购买信息字段配置'");
|
|
|
- log.info("已创建product_purchase_fields表");
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("创建product_purchase_fields表可能已存在: {}", e.getMessage());
|
|
|
- }
|
|
|
-
|
|
|
- // 迁移42: product_orders表添加consignee_id和purchase_info字段
|
|
|
- ensureColumn("product_orders", "consignee_id", "BIGINT COMMENT '收货人ID'");
|
|
|
- ensureColumn("product_orders", "purchase_info", "TEXT COMMENT '购买信息JSON: {id_card, hand_signature, ...}'");
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- 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 '00g含量', "
|
|
|
- + "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);
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
+package com.etotem.cfc.config;
|
|
|
+
|
|
|
+import com.etotem.cfc.mapper.*;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+import javax.annotation.Resource;
|
|
|
+import org.springframework.boot.CommandLineRunner;
|
|
|
+import org.springframework.core.io.ResourceLoader;
|
|
|
+import org.springframework.jdbc.core.JdbcTemplate;
|
|
|
+import org.springframework.stereotype.Component;
|
|
|
+import org.springframework.util.StreamUtils;
|
|
|
+
|
|
|
+import java.nio.charset.StandardCharsets;
|
|
|
+import java.util.ArrayList;
|
|
|
+import java.util.List;
|
|
|
+import java.util.regex.Matcher;
|
|
|
+import java.util.regex.Pattern;
|
|
|
+
|
|
|
+@Slf4j
|
|
|
+@Component
|
|
|
+public class DatabaseInitializer implements CommandLineRunner {
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private JdbcTemplate jdbcTemplate;
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private ResourceLoader resourceLoader;
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void run(String... args) throws Exception {
|
|
|
+ log.info("开始初始化数据库...");
|
|
|
+
|
|
|
+ // 1. 检查并创建数据库
|
|
|
+ initializeDatabase();
|
|
|
+
|
|
|
+ // 2. 检查并创建表
|
|
|
+ initializeTables();
|
|
|
+
|
|
|
+ // 3. 执行数据库迁移(添加新列)
|
|
|
+ runMigrations();
|
|
|
+
|
|
|
+ // 4. 初始化默认数据
|
|
|
+ initializeDefaultData();
|
|
|
+
|
|
|
+ // 5. 初始化食品相关表
|
|
|
+ initFoodTables();
|
|
|
+
|
|
|
+ log.info("数据库初始化完成!");
|
|
|
+ }
|
|
|
+
|
|
|
+ private void initializeDatabase() {
|
|
|
+ try {
|
|
|
+ // 尝试连接数据库,如果不存在则创建
|
|
|
+ jdbcTemplate.execute("CREATE DATABASE IF NOT EXISTS xzyj DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci");
|
|
|
+ log.info("数据库检查完成");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建数据库失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void initializeTables() {
|
|
|
+ try {
|
|
|
+ org.springframework.core.io.Resource resource = resourceLoader.getResource("classpath:schema.sql");
|
|
|
+ String content;
|
|
|
+ try (java.io.InputStream is = resource.getInputStream()) {
|
|
|
+ content = new String(StreamUtils.copyToByteArray(is), StandardCharsets.UTF_8);
|
|
|
+ }
|
|
|
+
|
|
|
+ // Remove SQL single-line comments
|
|
|
+ content = content.replaceAll("(?m)^\\s*--.*$", "");
|
|
|
+
|
|
|
+ // Extract all CREATE TABLE IF NOT EXISTS statements
|
|
|
+ Pattern pattern = Pattern.compile(
|
|
|
+ "CREATE\\s+TABLE\\s+IF\\s+NOT\\s+EXISTS\\s+.*?;",
|
|
|
+ Pattern.CASE_INSENSITIVE | Pattern.DOTALL
|
|
|
+ );
|
|
|
+ Matcher matcher = pattern.matcher(content);
|
|
|
+
|
|
|
+ List<String> createTableSQLs = new ArrayList<>();
|
|
|
+ while (matcher.find()) {
|
|
|
+ String sql = matcher.group().trim();
|
|
|
+ if (!sql.isEmpty()) {
|
|
|
+ createTableSQLs.add(sql);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ log.info("从 schema.sql 加载了 {} 个建表语句", createTableSQLs.size());
|
|
|
+
|
|
|
+ for (String sql : createTableSQLs) {
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute(sql);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建表失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ log.info("数据表检查完成");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("读取 schema.sql 失败", e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void runMigrations() {
|
|
|
+ // 迁移1: 任务表添加执行者字段
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE tasks ADD COLUMN executor_type ENUM('child', 'parent') DEFAULT 'child' COMMENT '执行者类型'");
|
|
|
+ log.info("已添加executor_type列到tasks表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 列已存在,忽略错误
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE tasks ADD COLUMN executor_id BIGINT COMMENT '执行者ID'");
|
|
|
+ log.info("已添加executor_id列到tasks表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 列已存在,忽略错误
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE tasks ADD INDEX idx_executor (executor_type, executor_id)");
|
|
|
+ log.info("已添加idx_executor索引到tasks表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 索引已存在,忽略错误
|
|
|
+ }
|
|
|
+
|
|
|
+ // 迁移2: 用户表添加角色和积分字段
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE users ADD COLUMN roles VARCHAR(100) COMMENT '角色列表'");
|
|
|
+ log.info("已添加roles列到users表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 列已存在,忽略错误
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE users ADD COLUMN total_points INT DEFAULT 0 COMMENT '家长总积分'");
|
|
|
+ log.info("已添加total_points列到users表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 列已存在,忽略错误
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE users ADD INDEX idx_roles (roles(20))");
|
|
|
+ log.info("已添加idx_roles索引到users表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 索引已存在,忽略错误
|
|
|
+ }
|
|
|
+
|
|
|
+ // 迁移3: users表添加新字段
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE users ADD COLUMN id_card VARCHAR(18) COMMENT '身份证号'");
|
|
|
+ log.info("已添加id_card列到users表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 列已存在,忽略错误
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE users ADD COLUMN real_name VARCHAR(50) COMMENT '真实姓名'");
|
|
|
+ log.info("已添加real_name列到users表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 列已存在,忽略错误
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE users ADD COLUMN phone VARCHAR(11) COMMENT '手机号'");
|
|
|
+ log.info("已添加phone列到users表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 列已存在,忽略错误
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE users ADD COLUMN birthday DATE COMMENT '生日'");
|
|
|
+ log.info("已添加birthday列到users表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 列已存在,忽略错误
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE users ADD COLUMN gender ENUM('male', 'female') COMMENT '性别'");
|
|
|
+ log.info("已添加gender列到users表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 列已存在,忽略错误
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE users ADD INDEX idx_phone (phone)");
|
|
|
+ log.info("已添加idx_phone索引到users表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 索引已存在,忽略错误
|
|
|
+ }
|
|
|
+
|
|
|
+ // 迁移4: children表添加新字段
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE children ADD COLUMN birthday DATE COMMENT '生日'");
|
|
|
+ log.info("已添加birthday列到children表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 列已存在,忽略错误
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE children ADD COLUMN gender ENUM('male', 'female') COMMENT '性别'");
|
|
|
+ log.info("已添加gender列到children表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 列已存在,忽略错误
|
|
|
+ }
|
|
|
+
|
|
|
+ // 迁移5: 修改users.role为支持teacher
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE users MODIFY COLUMN role ENUM('parent', 'child', 'teacher') NOT NULL");
|
|
|
+ log.info("已更新users.role字段支持teacher角色");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 修改失败,忽略错误
|
|
|
+ }
|
|
|
+
|
|
|
+ // 迁移5.1: 修改users.role为支持admin
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE users MODIFY COLUMN role ENUM('parent', 'child', 'teacher', 'admin') NOT NULL");
|
|
|
+ log.info("已更新users.role字段支持admin角色");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 修改失败,忽略错误
|
|
|
+ }
|
|
|
+
|
|
|
+ // 迁移5.2: 确保管理员角色为admin(修复旧数据遗留问题)
|
|
|
+ try {
|
|
|
+ int updated = jdbcTemplate.update("UPDATE admins SET role = 'admin' WHERE username = 'admin'");
|
|
|
+ if (updated > 0) {
|
|
|
+ log.info("已修正管理员角色为admin");
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 更新失败,忽略错误
|
|
|
+ }
|
|
|
+
|
|
|
+ // 迁移6: 创建streets表(街道地址维度表)
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS streets (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
+ "province VARCHAR(50) COMMENT '省份', " +
|
|
|
+ "province_code VARCHAR(20) COMMENT '省份编码', " +
|
|
|
+ "city VARCHAR(50) COMMENT '城市', " +
|
|
|
+ "city_code VARCHAR(20) COMMENT '城市编码', " +
|
|
|
+ "district VARCHAR(50) COMMENT '区县', " +
|
|
|
+ "district_code VARCHAR(20) COMMENT '区县编码', " +
|
|
|
+ "street VARCHAR(100) COMMENT '街道', " +
|
|
|
+ "street_code VARCHAR(20) COMMENT '街道编码', " +
|
|
|
+ "full_name VARCHAR(200) COMMENT '完整地址', " +
|
|
|
+ "level INT COMMENT '层级:1省2市3区4街道', " +
|
|
|
+ "parent_id BIGINT COMMENT '父级ID', " +
|
|
|
+ "sort_order INT DEFAULT 0 COMMENT '排序', " +
|
|
|
+ "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
+ "updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, " +
|
|
|
+ "INDEX idx_level (level), " +
|
|
|
+ "INDEX idx_parent_id (parent_id), " +
|
|
|
+ "INDEX idx_street_code (street_code), " +
|
|
|
+ "INDEX idx_full_name (full_name(50))" +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4");
|
|
|
+ log.info("已创建streets表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 表已存在,忽略错误
|
|
|
+ }
|
|
|
+
|
|
|
+ // 迁移7: families表添加地址字段
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE families ADD COLUMN street_id BIGINT COMMENT '街道ID'");
|
|
|
+ log.info("已添加street_id列到families表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 列已存在,忽略错误
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE families ADD COLUMN province VARCHAR(50) COMMENT '省份'");
|
|
|
+ jdbcTemplate.execute("ALTER TABLE families ADD COLUMN city VARCHAR(50) COMMENT '城市'");
|
|
|
+ jdbcTemplate.execute("ALTER TABLE families ADD COLUMN district VARCHAR(50) COMMENT '区县'");
|
|
|
+ jdbcTemplate.execute("ALTER TABLE families ADD COLUMN street VARCHAR(100) COMMENT '街道'");
|
|
|
+ log.info("已添加地址字段到families表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 列已存在,忽略错误
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE families ADD INDEX idx_street_id (street_id)");
|
|
|
+ log.info("已添加idx_street_id索引到families表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 索引已存在,忽略错误
|
|
|
+ }
|
|
|
+
|
|
|
+ // 迁移7b: families表添加teacher_id和creator_id字段
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE families ADD COLUMN teacher_id BIGINT COMMENT '绑定的成长规划师ID'");
|
|
|
+ log.info("已添加teacher_id列到families表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 列已存在,忽略错误
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE families ADD COLUMN creator_id BIGINT COMMENT '家庭创建者ID'");
|
|
|
+ log.info("已添加creator_id列到families表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 列已存在,忽略错误
|
|
|
+ }
|
|
|
+
|
|
|
+ // 迁移8: teachers表添加地址字段
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE teachers ADD COLUMN street_id BIGINT COMMENT '街道ID'");
|
|
|
+ log.info("已添加street_id列到teachers表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 列已存在,忽略错误
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE teachers ADD COLUMN province VARCHAR(50) COMMENT '省份'");
|
|
|
+ jdbcTemplate.execute("ALTER TABLE teachers ADD COLUMN city VARCHAR(50) COMMENT '城市'");
|
|
|
+ jdbcTemplate.execute("ALTER TABLE teachers ADD COLUMN district VARCHAR(50) COMMENT '区县'");
|
|
|
+ jdbcTemplate.execute("ALTER TABLE teachers ADD COLUMN street VARCHAR(100) COMMENT '街道'");
|
|
|
+ log.info("已添加地址字段到teachers表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 列已存在,忽略错误
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE teachers ADD INDEX idx_street_id (street_id)");
|
|
|
+ log.info("已添加idx_street_id索引到teachers表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 索引已存在,忽略错误
|
|
|
+ }
|
|
|
+
|
|
|
+ // 迁移9: users表添加成长规划师相关字段
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE users ADD COLUMN teacher_no VARCHAR(50) COMMENT '成长规划师证书号'");
|
|
|
+ log.info("已添加teacher_no列到users表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 列已存在,忽略错误
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE users ADD COLUMN teacher_photo VARCHAR(500) COMMENT '成长规划师照片'");
|
|
|
+ log.info("已添加teacher_photo列到users表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 列已存在,忽略错误
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE users ADD COLUMN certificate_image VARCHAR(500) COMMENT '成长规划师证书照片'");
|
|
|
+ log.info("已添加certificate_image列到users表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 列已存在,忽略错误
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE users ADD COLUMN teacher_status VARCHAR(20) DEFAULT 'pending' COMMENT '成长规划师审核状态'");
|
|
|
+ log.info("已添加teacher_status列到users表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 列已存在,忽略错误
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE users ADD INDEX idx_teacher_status (teacher_status)");
|
|
|
+ log.info("已添加idx_teacher_status索引到users表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 索引已存在,忽略错误
|
|
|
+ }
|
|
|
+
|
|
|
+ // 迁移10: users表添加teacherFamilyIds字段(成长规划师关联的家庭ID列表)
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE users ADD COLUMN teacher_family_ids VARCHAR(500) COMMENT '成长规划师关联的家庭ID列表(逗号分隔)'");
|
|
|
+ log.info("已添加teacher_family_ids列到users表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 列已存在,忽略错误
|
|
|
+ }
|
|
|
+
|
|
|
+ // 迁移11: families表添加teacherId字段(绑定的成长规划师ID)
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE families ADD COLUMN teacher_id BIGINT COMMENT '绑定的成长规划师ID'");
|
|
|
+ log.info("已添加teacher_id列到families表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 列已存在,忽略错误
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE families ADD INDEX idx_teacher_id (teacher_id)");
|
|
|
+ log.info("已添加idx_teacher_id索引到families表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 索引已存在,忽略错误
|
|
|
+ }
|
|
|
+
|
|
|
+ // 迁移12: 创建 teacher_messages 表(家长咨询成长规划师)
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS teacher_messages (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
+ "family_id BIGINT NOT NULL COMMENT '家庭ID', " +
|
|
|
+ "parent_id BIGINT NOT NULL COMMENT '家长用户ID', " +
|
|
|
+ "teacher_id BIGINT NOT NULL COMMENT '成长规划师用户ID', " +
|
|
|
+ "content TEXT NOT NULL COMMENT '消息内容', " +
|
|
|
+ "reply_content TEXT COMMENT '回复内容', " +
|
|
|
+ "status VARCHAR(20) DEFAULT 'pending' COMMENT '状态:pending(待回复)/replied(已回复)', " +
|
|
|
+ "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
+ "replied_at DATETIME COMMENT '回复时间', " +
|
|
|
+ "INDEX idx_family_id (family_id), " +
|
|
|
+ "INDEX idx_teacher_id (teacher_id), " +
|
|
|
+ "INDEX idx_parent_id (parent_id), " +
|
|
|
+ "INDEX idx_status (status)" +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4");
|
|
|
+ log.info("已创建teacher_messages表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 表已存在,忽略错误
|
|
|
+ }
|
|
|
+
|
|
|
+// 迁移13: admin_task_templates表添加teacher_id字段
|
|
|
+try {
|
|
|
+jdbcTemplate.execute("ALTER TABLE admin_task_templates ADD COLUMN teacher_id BIGINT COMMENT '成长规划师ID'");
|
|
|
+log.info("已添加teacher_id列到admin_task_templates表");
|
|
|
+} catch (Exception e) {
|
|
|
+// 列已存在,忽略错误
|
|
|
+}
|
|
|
+
|
|
|
+// 迁移13b: admin_task_templates表添加新字段
|
|
|
+try {
|
|
|
+jdbcTemplate.execute("ALTER TABLE admin_task_templates ADD COLUMN complete_types VARCHAR(100) COMMENT '完成方式JSON'");
|
|
|
+log.info("已添加complete_types列到admin_task_templates表");
|
|
|
+} catch (Exception e) {
|
|
|
+// 列已存在,忽略错误
|
|
|
+}
|
|
|
+
|
|
|
+try {
|
|
|
+jdbcTemplate.execute("ALTER TABLE admin_task_templates ADD COLUMN duration INT COMMENT '预计完成时长(分钟)'");
|
|
|
+log.info("已添加duration列到admin_task_templates表");
|
|
|
+} catch (Exception e) {
|
|
|
+// 列已存在,忽略错误
|
|
|
+}
|
|
|
+
|
|
|
+try {
|
|
|
+jdbcTemplate.execute("ALTER TABLE admin_task_templates ADD COLUMN review_type VARCHAR(20) COMMENT '审核人类型: creator/parent/teacher/ai'");
|
|
|
+log.info("已添加review_type列到admin_task_templates表");
|
|
|
+} catch (Exception e) {
|
|
|
+// 列已存在,忽略错误
|
|
|
+}
|
|
|
+
|
|
|
+try {
|
|
|
+jdbcTemplate.execute("ALTER TABLE admin_task_templates ADD COLUMN task_type VARCHAR(20) COMMENT '任务性质: onetime/recurring'");
|
|
|
+log.info("已添加task_type列到admin_task_templates表");
|
|
|
+} catch (Exception e) {
|
|
|
+// 列已存在,忽略错误
|
|
|
+}
|
|
|
+
|
|
|
+try {
|
|
|
+jdbcTemplate.execute("ALTER TABLE admin_task_templates ADD COLUMN frequency VARCHAR(20) COMMENT '频率: daily/weekly/monthly'");
|
|
|
+log.info("已添加frequency列到admin_task_templates表");
|
|
|
+} catch (Exception e) {
|
|
|
+// 列已存在,忽略错误
|
|
|
+}
|
|
|
+
|
|
|
+try {
|
|
|
+jdbcTemplate.execute("ALTER TABLE admin_task_templates ADD COLUMN max_frequency INT COMMENT '周期内最多完成次数'");
|
|
|
+log.info("已添加max_frequency列到admin_task_templates表");
|
|
|
+} catch (Exception e) {
|
|
|
+// 列已存在,忽略错误
|
|
|
+}
|
|
|
+
|
|
|
+// 迁移14: rewards表child_id字段允许NULL(模板奖励不需要child_id)
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE rewards MODIFY COLUMN child_id BIGINT NULL COMMENT '孩子ID,模板奖励为NULL'");
|
|
|
+ log.info("已修改rewards.child_id字段允许NULL");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 修改失败,忽略错误
|
|
|
+ }
|
|
|
+
|
|
|
+ // 迁移15: admins表添加缺失列
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE admins ADD COLUMN nickname VARCHAR(50) DEFAULT NULL AFTER password");
|
|
|
+ log.info("已添加nickname列到admins表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 列已存在,忽略错误
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE admins ADD COLUMN email VARCHAR(100) DEFAULT NULL AFTER phone");
|
|
|
+ log.info("已添加email列到admins表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 列已存在,忽略错误
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE admins MODIFY COLUMN status VARCHAR(20) DEFAULT 'active'");
|
|
|
+ log.info("已修改admins.status字段类型");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 修改失败,忽略错误
|
|
|
+ }
|
|
|
+
|
|
|
+ // 迁移16: 创建用户操作日志表
|
|
|
+try {
|
|
|
+jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS user_operation_logs (" +
|
|
|
+"id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
+"user_id BIGINT COMMENT \'用户ID\', " +
|
|
|
+"nickname VARCHAR(50) COMMENT \'用户昵称\', " +
|
|
|
+"operation_type VARCHAR(20) NOT NULL COMMENT \'操作类型: login/logout/create/update/delete/query等\', " +
|
|
|
+"description VARCHAR(500) COMMENT \'操作描述\', " +
|
|
|
+"request_path VARCHAR(200) COMMENT \'请求路径\', " +
|
|
|
+"request_method VARCHAR(10) COMMENT \'请求方法\', " +
|
|
|
+"request_params TEXT COMMENT \'请求参数(脱敏)\', " +
|
|
|
+"response_status INT COMMENT \'响应状态码\', " +
|
|
|
+"client_ip VARCHAR(50) COMMENT \'客户端IP\', " +
|
|
|
+"user_agent VARCHAR(500) COMMENT \'User-Agent\', " +
|
|
|
+"created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
+"INDEX idx_user_id (user_id), " +
|
|
|
+"INDEX idx_operation_type (operation_type), " +
|
|
|
+"INDEX idx_created_at (created_at)" +
|
|
|
+") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4");
|
|
|
+log.info("已创建user_operation_logs表");
|
|
|
+} catch (Exception e) {
|
|
|
+// 表已存在,忽略错误
|
|
|
+}
|
|
|
+
|
|
|
+// 迁移16: tasks表添加bonus_rule字段(POINT-002: 超额积分规则)
|
|
|
+try {
|
|
|
+jdbcTemplate.execute("ALTER TABLE tasks ADD COLUMN bonus_rule VARCHAR(500) COMMENT \'超额积分规则JSON\'");
|
|
|
+log.info("已添加bonus_rule列到tasks表");
|
|
|
+} catch (Exception e) {
|
|
|
+// 列已存在,忽略错误
|
|
|
+}
|
|
|
+
|
|
|
+// 迁移17: tasks表添加points_expire_days字段(POINT-009: 积分有效期)
|
|
|
+try {
|
|
|
+jdbcTemplate.execute("ALTER TABLE tasks ADD COLUMN points_expire_days INT DEFAULT 0 COMMENT \'积分有效期天数,0表示永久有效\'");
|
|
|
+log.info("已添加points_expire_days列到tasks表");
|
|
|
+} catch (Exception e) {
|
|
|
+// 列已存在,忽略错误
|
|
|
+}
|
|
|
+
|
|
|
+// 迁移18: points_log表添加expire_at字段(POINT-009: 积分有效期)
|
|
|
+try {
|
|
|
+jdbcTemplate.execute("ALTER TABLE points_log ADD COLUMN expire_at DATE COMMENT \'积分过期日期\'");
|
|
|
+log.info("已添加expire_at列到points_log表");
|
|
|
+} catch (Exception e) {
|
|
|
+// 列已存在,忽略错误
|
|
|
+}
|
|
|
+
|
|
|
+// 迁移19: points_log表添加expired字段(POINT-009: 过期状态标记)
|
|
|
+try {
|
|
|
+jdbcTemplate.execute("ALTER TABLE points_log ADD COLUMN expired TINYINT DEFAULT 0 COMMENT \'是否已过期: 0否1是\'");
|
|
|
+log.info("已添加expired列到points_log表");
|
|
|
+} catch (Exception e) {
|
|
|
+// 列已存在,忽略错误
|
|
|
+}
|
|
|
+
|
|
|
+// 迁移20: tasks表添加template_id字段(TASK-004: 重复任务模板追踪)
|
|
|
+try {
|
|
|
+jdbcTemplate.execute("ALTER TABLE tasks ADD COLUMN template_id BIGINT COMMENT \'模板任务ID\'");
|
|
|
+log.info("已添加template_id列到tasks表");
|
|
|
+} catch (Exception e) {
|
|
|
+// 列已存在,忽略错误
|
|
|
+}
|
|
|
+
|
|
|
+ // 迁移21: tasks表添加is_template字段(TASK-004: 标记模板任务)
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE tasks ADD COLUMN is_template TINYINT DEFAULT 0 COMMENT '是否为模板任务'");
|
|
|
+ log.info("已添加is_template列到tasks表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 列已存在,忽略错误
|
|
|
+ }
|
|
|
+
|
|
|
+ // 迁移22: 修复tasks表child_id和deadline允许NULL(支持多孩子和家长任务)
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE tasks MODIFY COLUMN child_id BIGINT NULL COMMENT '执行者ID'");
|
|
|
+ log.info("已修改tasks.child_id允许NULL");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 修改失败,忽略错误
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE tasks MODIFY COLUMN deadline DATETIME NULL COMMENT '截止时间'");
|
|
|
+ log.info("已修改tasks.deadline允许NULL");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 修改失败,忽略错误
|
|
|
+ }
|
|
|
+
|
|
|
+ // 迁移23: tasks表添加新字段
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE tasks ADD COLUMN task_type VARCHAR(20) COMMENT '任务性质: onetime/recurring'");
|
|
|
+ log.info("已添加task_type列到tasks表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 列已存在,忽略错误
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE tasks ADD COLUMN frequency VARCHAR(20) COMMENT '频率: daily/weekly/monthly'");
|
|
|
+ log.info("已添加frequency列到tasks表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 列已存在,忽略错误
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE tasks ADD COLUMN max_frequency INT COMMENT '周期内最多完成次数'");
|
|
|
+ log.info("已添加max_frequency列到tasks表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 列已存在,忽略错误
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE tasks ADD COLUMN complete_types VARCHAR(100) COMMENT '完成方式JSON'");
|
|
|
+ log.info("已添加complete_types列到tasks表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 列已存在,忽略错误
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE tasks ADD COLUMN review_type VARCHAR(20) COMMENT '审核人类型: creator/parent/teacher/ai'");
|
|
|
+ log.info("已添加review_type列到tasks表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 列已存在,忽略错误
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE tasks ADD COLUMN earliest_start DATETIME COMMENT '最早开始时间'");
|
|
|
+ log.info("已添加earliest_start列到tasks表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 列已存在,忽略错误
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE tasks ADD COLUMN latest_end DATETIME COMMENT '最晚结束时间'");
|
|
|
+ log.info("已添加latest_end列到tasks表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 列已存在,忽略错误
|
|
|
+ }
|
|
|
+
|
|
|
+ // 迁移24: tasks表添加minigame_code字段(小游戏任务)
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE tasks ADD COLUMN minigame_code VARCHAR(50) DEFAULT NULL COMMENT '小游戏代码: schulte/1a2b/sudoku'");
|
|
|
+ log.info("已添加minigame_code列到tasks表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 列已存在,忽略错误
|
|
|
+ }
|
|
|
+
|
|
|
+ // 迁移25: tasks表添加duration字段(TASK-011: 任务预计时长)
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE tasks ADD COLUMN duration INT DEFAULT 0 COMMENT '预计时长(分钟)'");
|
|
|
+ log.info("已添加duration列到tasks表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 列已存在,忽略错误
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE tasks ADD INDEX idx_minigame (minigame_code) WHERE minigame_code IS NOT NULL");
|
|
|
+ log.info("已添加idx_minigame索引到tasks表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 索引已存在,忽略错误
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS trial_memberships (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
+ "user_id BIGINT NOT NULL COMMENT '用户ID', " +
|
|
|
+ "family_id BIGINT NOT NULL COMMENT '家庭ID', " +
|
|
|
+ "start_date DATETIME NOT NULL COMMENT '试用开始时间', " +
|
|
|
+ "end_date DATETIME NOT NULL COMMENT '试用结束时间', " +
|
|
|
+ "status VARCHAR(20) DEFAULT 'PENDING' COMMENT '状态:PENDING/ACTIVE/EXPIRED/CONVERTED', " +
|
|
|
+ "trial_days INT DEFAULT 7 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_status (status), " +
|
|
|
+ "INDEX idx_end_date (end_date)" +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4");
|
|
|
+ log.info("已创建trial_memberships表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建trial_memberships表可能已存在: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // assessment_appointments 新字段
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE assessment_appointments ADD COLUMN user_id BIGINT COMMENT '用户ID'");
|
|
|
+ } catch (Exception e) {
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE assessment_appointments ADD COLUMN guide_id BIGINT COMMENT '成长规划师ID'");
|
|
|
+ } catch (Exception e) {
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE assessment_appointments ADD COLUMN package_id BIGINT COMMENT '套餐ID'");
|
|
|
+ } catch (Exception e) {
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE assessment_appointments ADD COLUMN remark VARCHAR(500) COMMENT '备注'");
|
|
|
+ } catch (Exception e) {
|
|
|
+ }
|
|
|
+
|
|
|
+ // assessment_orders 表
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS assessment_orders (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
+ "order_no VARCHAR(64) NOT NULL UNIQUE, " +
|
|
|
+ "family_id BIGINT NOT NULL, " +
|
|
|
+ "user_id BIGINT NOT NULL, " +
|
|
|
+ "child_id BIGINT, " +
|
|
|
+ "guide_id BIGINT NOT NULL, " +
|
|
|
+ "package_id BIGINT NOT NULL, " +
|
|
|
+ "guide_name VARCHAR(50), " +
|
|
|
+ "package_name VARCHAR(100), " +
|
|
|
+ "total_price BIGINT DEFAULT 0, " +
|
|
|
+ "discount_amount BIGINT DEFAULT 0, " +
|
|
|
+ "actual_price BIGINT DEFAULT 0, " +
|
|
|
+ "status VARCHAR(20) DEFAULT 'pending', " +
|
|
|
+ "pay_time VARCHAR(50), " +
|
|
|
+ "pay_type VARCHAR(20), " +
|
|
|
+ "transaction_id VARCHAR(64), " +
|
|
|
+ "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
+ "updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, " +
|
|
|
+ "INDEX idx_family (family_id), " +
|
|
|
+ "INDEX idx_guide (guide_id), " +
|
|
|
+ "INDEX idx_status (status)) " +
|
|
|
+ "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='评估订单表'");
|
|
|
+ } catch (Exception e) {
|
|
|
+ }
|
|
|
+
|
|
|
+ // 迁移26: guide_applications表添加address和bank_card字段
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE guide_applications ADD COLUMN address VARCHAR(500) COMMENT '地址'");
|
|
|
+ log.info("已添加address列到guide_applications表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 列已存在,忽略错误
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE guide_applications ADD COLUMN bank_card VARCHAR(50) COMMENT '银行卡号'");
|
|
|
+ log.info("已添加bank_card列到guide_applications表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 列已存在,忽略错误
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE guide_applications ADD COLUMN bank_name VARCHAR(100) COMMENT '开户行'");
|
|
|
+ log.info("已添加bank_name列到guide_applications表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 列已存在,忽略错误
|
|
|
+ }
|
|
|
+
|
|
|
+ // 迁移27: users表添加address字段(成长规划师地址)
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE users ADD COLUMN address VARCHAR(500) COMMENT '地址'");
|
|
|
+ log.info("已添加address列到users表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 列已存在,忽略错误
|
|
|
+ }
|
|
|
+
|
|
|
+ // 迁移28: 创建guide_application_records表(申请过程记录)
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS guide_application_records (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
+ "application_id BIGINT NOT NULL COMMENT '关联申请ID', " +
|
|
|
+ "user_id BIGINT NOT NULL COMMENT '操作人用户ID', " +
|
|
|
+ "action VARCHAR(50) NOT NULL COMMENT '操作: submit/approve/reject/withdraw/resubmit', " +
|
|
|
+ "from_status VARCHAR(20) COMMENT '操作前状态', " +
|
|
|
+ "to_status VARCHAR(20) NOT NULL COMMENT '操作后状态', " +
|
|
|
+ "remark VARCHAR(500) COMMENT '备注/原因', " +
|
|
|
+ "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
+ "INDEX idx_application_id (application_id), " +
|
|
|
+ "INDEX idx_user_id (user_id), " +
|
|
|
+ "INDEX idx_action (action)" +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='成长规划师申请过程记录表'");
|
|
|
+ log.info("已创建guide_application_records表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 表已存在,忽略错误
|
|
|
+ }
|
|
|
+
|
|
|
+ // 迁移20: children表添加system_points字段
|
|
|
+ try {
|
|
|
+ Integer exists = jdbcTemplate.queryForObject(
|
|
|
+ "SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'children' AND COLUMN_NAME = 'system_points'",
|
|
|
+ Integer.class);
|
|
|
+ if (exists == null || exists == 0) {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE children ADD COLUMN system_points INT DEFAULT 0 COMMENT '系统积分'");
|
|
|
+ log.info("已添加system_points列到children表");
|
|
|
+ } else {
|
|
|
+ log.info("system_points列已存在,跳过");
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("检查/添加system_points列失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 迁移21: points_log表添加category字段
|
|
|
+ try {
|
|
|
+ Integer exists = jdbcTemplate.queryForObject(
|
|
|
+ "SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'points_log' AND COLUMN_NAME = 'category'",
|
|
|
+ Integer.class);
|
|
|
+ if (exists == null || exists == 0) {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE points_log ADD COLUMN category VARCHAR(20) DEFAULT 'task' COMMENT '积分分类: task任务积分, system系统积分'");
|
|
|
+ log.info("已添加category列到points_log表");
|
|
|
+ } else {
|
|
|
+ log.info("category列已存在,跳过");
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("检查/添加category列失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 迁移22: 创建invite_card表
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS invite_card (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
+ "type VARCHAR(20) NOT NULL COMMENT 'child/family/guide', " +
|
|
|
+ "code VARCHAR(64) NOT NULL UNIQUE COMMENT '邀请码', " +
|
|
|
+ "ref_id BIGINT NOT NULL COMMENT '关联ID(childId/familyId)', " +
|
|
|
+ "creator_id BIGINT NOT NULL COMMENT '创建人', " +
|
|
|
+ "expires_at DATETIME NOT NULL, " +
|
|
|
+ "used TINYINT DEFAULT 0, " +
|
|
|
+ "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
+ "updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, " +
|
|
|
+ "INDEX idx_code (code), " +
|
|
|
+ "INDEX idx_ref_type (ref_id, type)" +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='邀请卡片'");
|
|
|
+ log.info("已创建invite_card表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 表已存在,忽略错误
|
|
|
+ }
|
|
|
+
|
|
|
+ // 迁移23: 创建wishes表
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS wishes (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
+ "family_id BIGINT COMMENT '家庭ID', " +
|
|
|
+ "child_id BIGINT COMMENT '目标孩子ID', " +
|
|
|
+ "created_by BIGINT COMMENT '创建者用户ID', " +
|
|
|
+ "creator_type VARCHAR(20) COMMENT '创建者类型: child/parent', " +
|
|
|
+ "title VARCHAR(200) NOT NULL COMMENT '心愿标题', " +
|
|
|
+ "description VARCHAR(500) COMMENT '心愿描述', " +
|
|
|
+ "category VARCHAR(32) COMMENT '心愿分类', " +
|
|
|
+ "points_required INT COMMENT '所需积分', " +
|
|
|
+ "points_set_by BIGINT COMMENT '定价者ID', " +
|
|
|
+ "points_set_at DATETIME COMMENT '定价时间', " +
|
|
|
+ "status VARCHAR(32) DEFAULT 'active' COMMENT '状态: draft/active/pending_exchange/approved/exchanging/fulfilled/cancelled/rejected', " +
|
|
|
+ "exchange_requested_at DATETIME COMMENT '申请兑换时间', " +
|
|
|
+ "exchange_approved_by BIGINT COMMENT '批准者ID', " +
|
|
|
+ "exchange_approved_at DATETIME COMMENT '批准时间', " +
|
|
|
+ "points_deducted_at DATETIME COMMENT '扣积分时间', " +
|
|
|
+ "fulfilled_by BIGINT COMMENT '兑现者ID', " +
|
|
|
+ "fulfilled_at DATETIME COMMENT '兑现时间', " +
|
|
|
+ "child_confirmed_at DATETIME COMMENT '孩子确认收货时间', " +
|
|
|
+ "rejected_by BIGINT COMMENT '拒绝者ID', " +
|
|
|
+ "rejected_at DATETIME COMMENT '拒绝时间', " +
|
|
|
+ "reject_reason VARCHAR(500) COMMENT '拒绝原因', " +
|
|
|
+ "reject_count INT DEFAULT 0 COMMENT '被拒绝次数', " +
|
|
|
+ "cancelled_by BIGINT COMMENT '取消者ID', " +
|
|
|
+ "cancelled_at DATETIME COMMENT '取消时间', " +
|
|
|
+ "cancel_reason VARCHAR(500) 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_child_id (child_id), " +
|
|
|
+ "INDEX idx_status (status)" +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='心愿单表'");
|
|
|
+ log.info("已创建wishes表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建wishes表可能已存在: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 迁移24: users表添加个人信息补充字段
|
|
|
+ try {
|
|
|
+ Integer exists = jdbcTemplate.queryForObject(
|
|
|
+ "SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'users' AND COLUMN_NAME = 'ethnicity'",
|
|
|
+ Integer.class);
|
|
|
+ if (exists == null || exists == 0) {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE users " +
|
|
|
+ "ADD COLUMN ethnicity VARCHAR(32) DEFAULT NULL COMMENT '民族', " +
|
|
|
+ "ADD COLUMN blood_type VARCHAR(16) DEFAULT NULL COMMENT '血型', " +
|
|
|
+ "ADD COLUMN highest_education VARCHAR(32) DEFAULT NULL COMMENT '最高学历', " +
|
|
|
+ "ADD COLUMN marital_status VARCHAR(16) DEFAULT NULL COMMENT '婚姻状态', " +
|
|
|
+ "ADD COLUMN family_role VARCHAR(16) DEFAULT NULL COMMENT '家长身份: 爸爸/妈妈/爷爷/奶奶/姥爷/姥姥/其他'");
|
|
|
+ log.info("已添加个人信息补充字段到users表");
|
|
|
+ } else {
|
|
|
+ log.info("个人信息补充字段已存在,跳过");
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("检查/添加个人信息补充字段失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // Phase 0: 浠艾福俱乐部 — 服务商字段
|
|
|
+ try {
|
|
|
+ Integer exists = jdbcTemplate.queryForObject(
|
|
|
+ "SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'users' AND COLUMN_NAME = 'vendor_type'",
|
|
|
+ Integer.class);
|
|
|
+ if (exists == null || exists == 0) {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE users ADD COLUMN vendor_type VARCHAR(32) DEFAULT NULL COMMENT '服务商类型: planner/activity_provider/product_supplier'");
|
|
|
+ jdbcTemplate.execute("ALTER TABLE users ADD COLUMN vendor_status VARCHAR(16) DEFAULT NULL COMMENT '服务商审核状态: pending/approved/rejected'");
|
|
|
+ jdbcTemplate.execute("ALTER TABLE users ADD COLUMN vendor_reject_reason VARCHAR(500) DEFAULT NULL COMMENT '审核拒绝原因'");
|
|
|
+ jdbcTemplate.execute("ALTER TABLE users ADD COLUMN is_family_admin TINYINT DEFAULT 0 COMMENT '是否家庭管理员'");
|
|
|
+ jdbcTemplate.execute("ALTER TABLE users ADD COLUMN vendor_info VARCHAR(2000) DEFAULT NULL COMMENT '服务商资质信息(JSON)'");
|
|
|
+ log.info("已添加浠艾福俱乐部服务商字段到users表");
|
|
|
+ } else {
|
|
|
+ log.info("浠艾福俱乐部服务商字段已存在,跳过");
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("检查/添加服务商字段失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // Phase 0: 迁移已有规划师到服务商体系
|
|
|
+ try {
|
|
|
+ int updated = jdbcTemplate.update(
|
|
|
+ "UPDATE users SET vendor_type = 'planner', vendor_status = 'approved' WHERE teacher_status = 'approved' AND vendor_type IS NULL"
|
|
|
+ );
|
|
|
+ if (updated > 0) {
|
|
|
+ log.info("已迁移 {} 名规划师到服务商体系", updated);
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("迁移规划师到服务商体系失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== Phase 1: 浠艾福俱乐部商品系统 ====================
|
|
|
+
|
|
|
+ // 商品表
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute(
|
|
|
+ "CREATE TABLE IF NOT EXISTS products (" +
|
|
|
+ " id BIGINT AUTO_INCREMENT PRIMARY KEY," +
|
|
|
+ " name VARCHAR(200) NOT NULL," +
|
|
|
+ " description VARCHAR(1000)," +
|
|
|
+ " intro TEXT," +
|
|
|
+ " cover_image VARCHAR(500)," +
|
|
|
+ " images TEXT," +
|
|
|
+ " price INT NOT NULL DEFAULT 0 COMMENT '价格(分)'," +
|
|
|
+ " stock INT NOT NULL DEFAULT 0," +
|
|
|
+ " sales_count INT NOT NULL DEFAULT 0," +
|
|
|
+ " product_type VARCHAR(32) NOT NULL COMMENT 'activity/course/physical/digital/service'," +
|
|
|
+ " vendor_id BIGINT," +
|
|
|
+ " vendor_name VARCHAR(100)," +
|
|
|
+ " status VARCHAR(32) NOT NULL DEFAULT 'draft' COMMENT 'draft/pending/approved/rejected/on_shelf/off_shelf'," +
|
|
|
+ " reject_reason VARCHAR(500)," +
|
|
|
+ " member_price INT COMMENT '会员价(分)'," +
|
|
|
+ " profit_rate INT DEFAULT 0 COMMENT '平台利润率(千分比)'," +
|
|
|
+ " member_eligible VARCHAR(32) DEFAULT 'none' COMMENT 'all/budget/none'," +
|
|
|
+ " domain VARCHAR(32) COMMENT 'action/mind/body/wisdom/wealth'," +
|
|
|
+ " external_source VARCHAR(64)," +
|
|
|
+ " external_id VARCHAR(128)," +
|
|
|
+ " external_data TEXT," +
|
|
|
+ " created_at DATETIME DEFAULT CURRENT_TIMESTAMP," +
|
|
|
+ " updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP" +
|
|
|
+ ")"
|
|
|
+ );
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建products表失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 商品SKU表
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute(
|
|
|
+ "CREATE TABLE IF NOT EXISTS product_skus (" +
|
|
|
+ " id INT NOT NULL AUTO_INCREMENT," +
|
|
|
+ " product_id INT NOT NULL COMMENT '商品ID'," +
|
|
|
+ " sku_code VARCHAR(64) NOT NULL COMMENT 'SKU编码'," +
|
|
|
+ " specs JSON NOT NULL COMMENT '规格JSON'," +
|
|
|
+ " price DECIMAL(10,2) NOT NULL COMMENT 'SKU价格'," +
|
|
|
+ " stock INT NOT NULL DEFAULT 0 COMMENT '库存'," +
|
|
|
+ " original_price DECIMAL(10,2) COMMENT '原价/划线价'," +
|
|
|
+ " image VARCHAR(512) COMMENT 'SKU图片'," +
|
|
|
+ " enabled TINYINT(1) NOT NULL DEFAULT 1," +
|
|
|
+ " create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP," +
|
|
|
+ " update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP," +
|
|
|
+ " PRIMARY KEY (id)," +
|
|
|
+ " KEY idx_product_id (product_id)" +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品SKU'"
|
|
|
+ );
|
|
|
+ log.info("已创建product_skus表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建product_skus表失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 订单表
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute(
|
|
|
+ "CREATE TABLE IF NOT EXISTS product_orders (" +
|
|
|
+ " id BIGINT AUTO_INCREMENT PRIMARY KEY," +
|
|
|
+ " order_no VARCHAR(64) NOT NULL UNIQUE," +
|
|
|
+ " product_id BIGINT NOT NULL," +
|
|
|
+ " product_name VARCHAR(200)," +
|
|
|
+ " product_type VARCHAR(32)," +
|
|
|
+ " buyer_id BIGINT NOT NULL," +
|
|
|
+ " family_id BIGINT," +
|
|
|
+ " quantity INT NOT NULL DEFAULT 1," +
|
|
|
+ " unit_price INT NOT NULL COMMENT '单价(分)'," +
|
|
|
+ " total_amount INT NOT NULL COMMENT '总金额(分)'," +
|
|
|
+ " status VARCHAR(32) NOT NULL DEFAULT 'pending' COMMENT 'pending/paid/completed/cancelled/refunded'," +
|
|
|
+ " payment_method VARCHAR(32) COMMENT 'wechat/points/membership'," +
|
|
|
+ " remark VARCHAR(500)," +
|
|
|
+ " paid_at DATETIME," +
|
|
|
+ " created_at DATETIME DEFAULT CURRENT_TIMESTAMP," +
|
|
|
+ " updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP" +
|
|
|
+ ")"
|
|
|
+ );
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建product_orders表失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 系统配置表
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute(
|
|
|
+ "CREATE TABLE IF NOT EXISTS sys_config (" +
|
|
|
+ " id BIGINT AUTO_INCREMENT PRIMARY KEY," +
|
|
|
+ " config_key VARCHAR(64) NOT NULL UNIQUE," +
|
|
|
+ " config_value TEXT," +
|
|
|
+ " description VARCHAR(500)," +
|
|
|
+ " updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP" +
|
|
|
+ ")"
|
|
|
+ );
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建sys_config表失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // Migration: product_orders add logistics fields
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE product_orders ADD COLUMN logistics_no VARCHAR(64) COMMENT '物流单号'");
|
|
|
+ log.info("已添加logistics_no列到product_orders表");
|
|
|
+ } catch (Exception e) { }
|
|
|
+
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE product_orders ADD COLUMN logistics_company VARCHAR(100) COMMENT '物流公司'");
|
|
|
+ log.info("已添加logistics_company列到product_orders表");
|
|
|
+ } catch (Exception e) { }
|
|
|
+
|
|
|
+ // SysConfig 种子数据
|
|
|
+ try {
|
|
|
+ insertSysConfigSeed("points_exchange_rate", "1", "积分兑换汇率(1积分=1分,即0.01元)");
|
|
|
+ insertSysConfigSeed("points_deduct_cap", "0.5", "积分抵扣上限(50%)");
|
|
|
+ insertSysConfigSeed("member_fee_standard", "1314", "标准会员年费(元)");
|
|
|
+ insertSysConfigSeed("member_fee_premium", "3650", "高级会员年费(元)");
|
|
|
+ insertSysConfigSeed("member_eligible_budget", "1314", "等值商品选择预算上限(元)");
|
|
|
+ insertSysConfigSeed("renewal_discount_rate", "0.85", "续费折扣率");
|
|
|
+ insertSysConfigSeed("member_discount_standard", "0.9", "标准会员折扣率");
|
|
|
+ insertSysConfigSeed("member_discount_premium", "0.8", "高级会员折扣率");
|
|
|
+ insertSysConfigSeed("member_fee_family", "36500", "家庭会员年费(分)");
|
|
|
+ insertSysConfigSeed("member_upgrade_threshold", "131400", "自动升级家庭会员累计消费门槛(分)");
|
|
|
+ insertSysConfigSeed("member_discount_family", "9000", "家庭会员商品折扣(bps)");
|
|
|
+ insertSysConfigSeed("commission_member_l1_rate", "2000", "推荐家庭会员L1佣金率(bps)");
|
|
|
+ insertSysConfigSeed("commission_member_l2_rate", "500", "推荐家庭会员L2佣金率(bps)");
|
|
|
+ insertSysConfigSeed("commission_product_profit_share", "1000", "下线商品购买利润分成(bps)");
|
|
|
+ insertSysConfigSeed("commission_service_rate", "1000", "下线服务购买佣金率(bps)");
|
|
|
+ insertSysConfigSeed("commission_event_fixed", "2000", "推荐新用户报名活动固定佣金(分)");
|
|
|
+ insertSysConfigSeed("withdraw_min_amount", "10000", "最低提现金额(分)");
|
|
|
+ insertSysConfigSeed("withdraw_tax_rate", "2000", "佣金税率(bps)");
|
|
|
+ insertSysConfigSeed("withdraw_fee_free_threshold", "80000", "免税额度(分)");
|
|
|
+ insertSysConfigSeed("article_energy_rate_normal", "1", "普通文章能量汇率(每100字)");
|
|
|
+ insertSysConfigSeed("article_energy_rate_premium", "2", "优质文章能量汇率(每100字)");
|
|
|
+ insertSysConfigSeed("article_energy_rate_original", "3", "原创文章能量汇率(每100字)");
|
|
|
+ insertSysConfigSeed("article_reading_min_seconds", "60", "文章阅读达标最少秒数");
|
|
|
+ insertSysConfigSeed("activity_checkin_default_points", "5", "活动签到默认积分");
|
|
|
+ insertSysConfigSeed("health_checkin_points", "5", "健康打卡积分");
|
|
|
+ insertSysConfigSeed("finance_checkin_points", "5", "理财打卡积分");
|
|
|
+ insertSysConfigSeed("trial_enabled", "true", "是否启用会员试用");
|
|
|
+ insertSysConfigSeed("trial_days", "7", "试用天数");
|
|
|
+ insertSysConfigSeed("trial_features", "[\"free_activities\",\"free_courses\",\"teacher_consultation\"]", "试用期间可用功能");
|
|
|
+ insertSysConfigSeed("health_dimension_weights", "{\"growth\":0.20,\"sleep\":0.15,\"vision\":0.10,\"immunity\":0.15,\"nutrition\":0.15,\"gut\":0.10,\"exercise\":0.15}", "7维健康→身能量权重配置");
|
|
|
+ insertSysConfigSeed("health_dimension_sync_factor", "0.5", "7维健康同步至身能量系数");
|
|
|
+ log.info("SysConfig 种子数据已加载");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("SysConfig 种子数据初始化失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // Product 种子数据(5 个示例商品,覆盖全部 5 种 productType)
|
|
|
+ try {
|
|
|
+ insertProductSeed("亲子关系工作坊", "线下半日工作坊,重建家庭对话模式", "activity", 38800, "action", "all");
|
|
|
+ insertProductSeed("情绪管理21天训练营", "21天视频课程,识别并管理情绪", "course", 19900, "mind", "all");
|
|
|
+ insertProductSeed("肠道菌群检测套装", "家用检测 + 报告解读", "physical", 59900, "body", "budget");
|
|
|
+ insertProductSeed("儿童多模态认知测评", "在线测评 + 个性化建议", "digital", 9900, "wisdom", "none");
|
|
|
+ insertProductSeed("成长规划师1对1咨询", "60分钟专业规划", "service", 50000, "wealth", "budget");
|
|
|
+ log.info("Product 种子数据已加载");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("Product 种子数据初始化失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== Phase 2: 内容区块可见性配置 ====================
|
|
|
+
|
|
|
+ // 内容区块配置表
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute(
|
|
|
+ "CREATE TABLE IF NOT EXISTS content_sections (" +
|
|
|
+ " id BIGINT AUTO_INCREMENT PRIMARY KEY," +
|
|
|
+ " page_key VARCHAR(50) NOT NULL," +
|
|
|
+ " section_key VARCHAR(50) NOT NULL," +
|
|
|
+ " title VARCHAR(100)," +
|
|
|
+ " allowed_roles VARCHAR(255) DEFAULT '[\"anonymous\"]'," +
|
|
|
+ " sort_order INT DEFAULT 0," +
|
|
|
+ " status VARCHAR(20) DEFAULT 'active'," +
|
|
|
+ " created_at DATETIME DEFAULT CURRENT_TIMESTAMP," +
|
|
|
+ " updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP" +
|
|
|
+ ")"
|
|
|
+ );
|
|
|
+ log.info("content_sections 表已创建");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建 content_sections 表失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // ContentSection 种子数据
|
|
|
+ try {
|
|
|
+ insertContentSectionSeed("mind", "func_entries", "功能入口", "[\"anonymous\"]", 1);
|
|
|
+ insertContentSectionSeed("mind", "daily_tip", "每日心理", "[\"anonymous\"]", 2);
|
|
|
+ insertContentSectionSeed("mind", "recommended_reading", "推荐阅读", "[\"anonymous\"]", 3);
|
|
|
+ insertContentSectionSeed("mind", "premium_content", "会员专属内容", "[\"parent\",\"teacher\"]", 4);
|
|
|
+ insertContentSectionSeed("mind", "mind_tasks", "心智任务", "[\"parent\",\"child\"]", 5);
|
|
|
+ insertContentSectionSeed("mind", "related_activities", "相关活动", "[\"parent\",\"child\"]", 6);
|
|
|
+ insertContentSectionSeed("mind", "purchased_courses", "已购课程", "[\"parent\",\"child\"]", 7);
|
|
|
+ insertContentSectionSeed("body", "func_entries", "功能入口", "[\"anonymous\"]", 1);
|
|
|
+ insertContentSectionSeed("body", "daily_stats", "今日数据", "[\"anonymous\"]", 2);
|
|
|
+ insertContentSectionSeed("body", "health_tips", "健康小贴士", "[\"anonymous\"]", 3);
|
|
|
+ insertContentSectionSeed("body", "teacher_tips", "规划师推荐", "[\"teacher\"]", 4);
|
|
|
+ log.info("ContentSection 种子数据已加载");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("ContentSection 种子数据初始化失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== 文章内容系统种子数据 ====================
|
|
|
+
|
|
|
+ // 文章分类种子数据
|
|
|
+ try {
|
|
|
+ Integer catCount = jdbcTemplate.queryForObject(
|
|
|
+ "SELECT COUNT(*) FROM article_categories WHERE id IN (1,2,3,4,5)", Integer.class);
|
|
|
+ if (catCount == null || catCount == 0) {
|
|
|
+ jdbcTemplate.execute("INSERT IGNORE INTO article_categories (id, name, icon, color, sort_order, status) VALUES " +
|
|
|
+ "(1, '心理健康', '🧠', '#5B9BD5', 1, 1), " +
|
|
|
+ "(2, '情绪管理', '💖', '#FF6B35', 2, 1), " +
|
|
|
+ "(3, '亲子教育', '👨👩👧👦', '#4CAF50', 3, 1), " +
|
|
|
+ "(4, '学习力', '📚', '#FFD700', 4, 1), " +
|
|
|
+ "(5, '专注力', '🎯', '#8D6E63', 5, 1)");
|
|
|
+ log.info("文章分类种子数据已加载");
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("文章分类种子数据初始化失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== 分佣裂变系统 ====================
|
|
|
+
|
|
|
+ // 迁移: users表添加推荐人字段
|
|
|
+ try {
|
|
|
+ Integer exists = jdbcTemplate.queryForObject(
|
|
|
+ "SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'users' AND COLUMN_NAME = 'referrer_id'",
|
|
|
+ Integer.class);
|
|
|
+ if (exists == null || exists == 0) {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE users ADD COLUMN referrer_id BIGINT DEFAULT NULL COMMENT '推荐人ID'");
|
|
|
+ jdbcTemplate.execute("ALTER TABLE users ADD COLUMN referral_code VARCHAR(32) DEFAULT NULL COMMENT '个人邀请码'");
|
|
|
+ jdbcTemplate.execute("ALTER TABLE users ADD INDEX idx_referral_code (referral_code)");
|
|
|
+ log.info("已添加分佣字段到users表");
|
|
|
+ } else {
|
|
|
+ log.info("分佣金字段已存在,跳过");
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("检查/添加分佣金字段失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 迁移: products表添加利润率字段
|
|
|
+ try {
|
|
|
+ Integer exists = jdbcTemplate.queryForObject(
|
|
|
+ "SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'products' AND COLUMN_NAME = 'profit_rate'",
|
|
|
+ Integer.class);
|
|
|
+ if (exists == null || exists == 0) {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE products ADD COLUMN profit_rate INT DEFAULT 0 COMMENT '平台利润率(bps,100=1%)'");
|
|
|
+ log.info("已添加profit_rate列到products表");
|
|
|
+ } else {
|
|
|
+ log.info("profit_rate列已存在,跳过");
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("检查/添加profit_rate列失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 新表: commission_records (佣金记录)
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS commission_records (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
+ "order_id BIGINT NOT NULL COMMENT '订单ID', " +
|
|
|
+ "order_type VARCHAR(32) NOT NULL COMMENT '订单类型: membership/product/assessment/package', " +
|
|
|
+ "referrer_id BIGINT NOT NULL COMMENT '推荐人ID', " +
|
|
|
+ "buyer_id BIGINT NOT NULL COMMENT '购买人ID', " +
|
|
|
+ "commission_type VARCHAR(16) NOT NULL COMMENT '佣金类型: member/profit', " +
|
|
|
+ "order_amount INT NOT NULL COMMENT '订单金额(分)', " +
|
|
|
+ "profit_rate INT DEFAULT 0 COMMENT '利润率(bps,100=1%)', " +
|
|
|
+ "commission_amount INT NOT NULL COMMENT '佣金金额(分)', " +
|
|
|
+ "status VARCHAR(16) NOT NULL DEFAULT 'settled' COMMENT '状态: pending/settled', " +
|
|
|
+ "remark VARCHAR(255) DEFAULT NULL, " +
|
|
|
+ "created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
+ "INDEX idx_referrer (referrer_id), " +
|
|
|
+ "INDEX idx_buyer (buyer_id), " +
|
|
|
+ "INDEX idx_order (order_id, order_type)" +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4");
|
|
|
+ log.info("已创建commission_records表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建commission_records表失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 新表: withdrawal_requests (提现申请)
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS withdrawal_requests (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
+ "user_id BIGINT NOT NULL COMMENT '用户ID', " +
|
|
|
+ "amount INT NOT NULL COMMENT '提现金额(分)', " +
|
|
|
+ "account_info TEXT COMMENT '账户信息(JSON)', " +
|
|
|
+ "status VARCHAR(16) NOT NULL DEFAULT 'pending' COMMENT '状态: pending/approved/rejected', " +
|
|
|
+ "audit_by BIGINT DEFAULT NULL COMMENT '审核人ID', " +
|
|
|
+ "audit_at DATETIME DEFAULT NULL COMMENT '审核时间', " +
|
|
|
+ "remark VARCHAR(255) DEFAULT NULL COMMENT '审核备注', " +
|
|
|
+ "created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
+ "INDEX idx_user (user_id), " +
|
|
|
+ "INDEX idx_status (status)" +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4");
|
|
|
+ log.info("已创建withdrawal_requests表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建withdrawal_requests表失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== 会员体系 & 分佣扩展 ====================
|
|
|
+
|
|
|
+ // 迁移: users表添加会员字段
|
|
|
+ try {
|
|
|
+ Integer exists = jdbcTemplate.queryForObject(
|
|
|
+ "SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'users' AND COLUMN_NAME = 'member_level'",
|
|
|
+ Integer.class);
|
|
|
+ if (exists == null || exists == 0) {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE users ADD COLUMN member_level VARCHAR(20) DEFAULT 'FREE' COMMENT '会员等级: FREE/FAMILY/PROVIDER'");
|
|
|
+ jdbcTemplate.execute("ALTER TABLE users ADD COLUMN member_expire_time DATETIME DEFAULT NULL COMMENT '家庭会员到期时间'");
|
|
|
+ jdbcTemplate.execute("ALTER TABLE users ADD COLUMN total_consumption BIGINT DEFAULT 0 COMMENT '累计消费金额(分)'");
|
|
|
+ jdbcTemplate.execute("ALTER TABLE users ADD COLUMN upgrade_from_consumption TINYINT DEFAULT 0 COMMENT '是否通过累计消费自动升级'");
|
|
|
+ jdbcTemplate.execute("ALTER TABLE users ADD COLUMN direct_count INT DEFAULT 0 COMMENT '直接推荐人数'");
|
|
|
+ jdbcTemplate.execute("ALTER TABLE users ADD COLUMN indirect_count INT DEFAULT 0 COMMENT '间接推荐人数'");
|
|
|
+ jdbcTemplate.execute("ALTER TABLE users ADD COLUMN total_commission_earned BIGINT DEFAULT 0 COMMENT '累计获得佣金(分)'");
|
|
|
+ log.info("已添加会员字段到users表");
|
|
|
+ } else {
|
|
|
+ log.info("会员字段已存在,跳过");
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("检查/添加会员字段失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 迁移: commission_records表添加扩展字段
|
|
|
+ try {
|
|
|
+ Integer exists = jdbcTemplate.queryForObject(
|
|
|
+ "SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'commission_records' AND COLUMN_NAME = 'level'",
|
|
|
+ Integer.class);
|
|
|
+ if (exists == null || exists == 0) {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE commission_records ADD COLUMN level INT DEFAULT 1 COMMENT '佣金层级: 1=直接推荐, 2=间接推荐'");
|
|
|
+ jdbcTemplate.execute("ALTER TABLE commission_records ADD COLUMN settled_at DATETIME DEFAULT NULL COMMENT '结算时间'");
|
|
|
+ jdbcTemplate.execute("ALTER TABLE commission_records ADD COLUMN withdrawn_at DATETIME DEFAULT NULL COMMENT '提现时间'");
|
|
|
+ log.info("已添加扩展字段到commission_records表");
|
|
|
+ } else {
|
|
|
+ log.info("commission_records扩展字段已存在,跳过");
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("检查/添加commission_records扩展字段失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 新表: member_upgrade_records (会员升级记录)
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS member_upgrade_records (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
+ "user_id BIGINT NOT NULL COMMENT '用户ID', " +
|
|
|
+ "from_level VARCHAR(20) NOT NULL COMMENT '升级前等级', " +
|
|
|
+ "to_level VARCHAR(20) NOT NULL COMMENT '升级后等级', " +
|
|
|
+ "upgrade_type VARCHAR(20) NOT NULL COMMENT '升级类型: pay/consumption/admin', " +
|
|
|
+ "order_id BIGINT DEFAULT NULL COMMENT '关联支付订单ID', " +
|
|
|
+ "consumption_amount BIGINT DEFAULT NULL COMMENT '触发升级的累计消费金额', " +
|
|
|
+ "expire_time DATETIME DEFAULT NULL COMMENT '家庭会员到期时间', " +
|
|
|
+ "created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
+ "INDEX idx_user (user_id), " +
|
|
|
+ "INDEX idx_order (order_id)" +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='会员升级记录'");
|
|
|
+ log.info("已创建member_upgrade_records表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建member_upgrade_records表失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== 文章内容系统 ====================
|
|
|
+
|
|
|
+ // 文章分类表
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS article_categories (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
+ "name VARCHAR(50) NOT NULL COMMENT '分类名', " +
|
|
|
+ "icon VARCHAR(20) DEFAULT '' COMMENT '图标emoji', " +
|
|
|
+ "color VARCHAR(20) DEFAULT '#5B9BD5' COMMENT '标识色', " +
|
|
|
+ "sort_order INT DEFAULT 0 COMMENT '排序', " +
|
|
|
+ "status TINYINT DEFAULT 1 COMMENT '1启用/0禁用'" +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='文章分类'");
|
|
|
+ log.info("已创建article_categories表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建article_categories表失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 文章表
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS articles (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
+ "category_id BIGINT DEFAULT 0 COMMENT '所属分类ID', " +
|
|
|
+ "title VARCHAR(200) NOT NULL COMMENT '标题', " +
|
|
|
+ "summary VARCHAR(500) DEFAULT '' COMMENT '摘要', " +
|
|
|
+ "cover_image VARCHAR(500) DEFAULT '' COMMENT '封面图URL', " +
|
|
|
+ "content LONGTEXT COMMENT '富文本内容', " +
|
|
|
+ "tags VARCHAR(200) DEFAULT '' COMMENT '标签JSON数组', " +
|
|
|
+ "author VARCHAR(100) DEFAULT '浠艾福' COMMENT '作者', " +
|
|
|
+ "read_time INT DEFAULT 0 COMMENT '预计阅读分钟数', " +
|
|
|
+ "related_dimensions VARCHAR(100) DEFAULT '' COMMENT '关联五维JSON数组', " +
|
|
|
+ "visibility VARCHAR(20) DEFAULT 'public' COMMENT '浏览权限:public/login/private', " +
|
|
|
+ "visible_to TEXT COMMENT '私密指定人群JSON', " +
|
|
|
+ "status VARCHAR(20) DEFAULT 'draft' COMMENT 'draft/published/archived', " +
|
|
|
+ "is_featured TINYINT DEFAULT 0 COMMENT '1精选/0普通', " +
|
|
|
+ "published_at DATETIME DEFAULT NULL COMMENT '发布时间', " +
|
|
|
+ "view_count INT DEFAULT 0 COMMENT '浏览次数', " +
|
|
|
+ "created_by BIGINT DEFAULT 0 COMMENT '发布人adminID', " +
|
|
|
+ "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
+ "updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP" +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='文章'");
|
|
|
+ log.info("已创建articles表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建articles表失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 迁移: articles表补充新字段(兼容旧表)
|
|
|
+ try {
|
|
|
+ Integer colExists = jdbcTemplate.queryForObject(
|
|
|
+ "SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'articles' AND COLUMN_NAME = 'visibility'",
|
|
|
+ Integer.class);
|
|
|
+ if (colExists == null || colExists == 0) {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE articles ADD COLUMN visibility VARCHAR(20) DEFAULT 'public' COMMENT '浏览权限:public/login/private' AFTER related_dimensions");
|
|
|
+ jdbcTemplate.execute("ALTER TABLE articles ADD COLUMN visible_to TEXT COMMENT '私密指定人群JSON' AFTER visibility");
|
|
|
+ jdbcTemplate.execute("ALTER TABLE articles ADD COLUMN tags VARCHAR(200) DEFAULT '' COMMENT '标签JSON数组' AFTER content");
|
|
|
+ jdbcTemplate.execute("ALTER TABLE articles ADD COLUMN related_dimensions VARCHAR(100) DEFAULT '' COMMENT '关联五维JSON数组' AFTER read_time");
|
|
|
+ jdbcTemplate.execute("ALTER TABLE articles ADD COLUMN is_featured TINYINT DEFAULT 0 COMMENT '1精选/0普通' AFTER status");
|
|
|
+ jdbcTemplate.execute("ALTER TABLE articles ADD COLUMN published_at DATETIME DEFAULT NULL COMMENT '发布时间' AFTER is_featured");
|
|
|
+ jdbcTemplate.execute("ALTER TABLE articles ADD COLUMN view_count INT DEFAULT 0 COMMENT '浏览次数' AFTER published_at");
|
|
|
+ jdbcTemplate.execute("ALTER TABLE articles ADD COLUMN created_by BIGINT DEFAULT 0 COMMENT '发布人adminID' AFTER view_count");
|
|
|
+ log.info("已迁移articles表补充字段");
|
|
|
+ } else {
|
|
|
+ log.info("articles表字段已完整,跳过迁移");
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("articles表字段迁移失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 迁移: articles表补充知识中心新字段(content_type/difficulty_level/dimension_ids)
|
|
|
+ try {
|
|
|
+ Integer colExists = jdbcTemplate.queryForObject(
|
|
|
+ "SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'articles' AND COLUMN_NAME = 'content_type'",
|
|
|
+ Integer.class);
|
|
|
+ if (colExists == null || colExists == 0) {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE articles ADD COLUMN content_type VARCHAR(20) DEFAULT 'article' COMMENT '内容类型: article/knowledge/course/tip' AFTER article_type");
|
|
|
+ jdbcTemplate.execute("ALTER TABLE articles ADD COLUMN difficulty_level TINYINT DEFAULT 0 COMMENT '难度等级 1-5' AFTER content_type");
|
|
|
+ jdbcTemplate.execute("ALTER TABLE articles ADD COLUMN dimension_ids TEXT COMMENT '关联维度ID列表JSON' AFTER related_dimensions");
|
|
|
+ log.info("已迁移articles表知识中心新字段");
|
|
|
+ } else {
|
|
|
+ log.info("articles表知识中心字段已完整,跳过迁移");
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("articles表知识中心字段迁移失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 文章标签关联表
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS article_tags (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
+ "article_id BIGINT NOT NULL COMMENT '文章ID', " +
|
|
|
+ "tag_id BIGINT NOT NULL COMMENT '标签ID(关联dan_knowledge_tag.id)', " +
|
|
|
+ "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
+ "UNIQUE KEY uk_article_tag (article_id, tag_id)" +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='文章-标签关联表'");
|
|
|
+ log.info("已创建article_tags表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建article_tags表失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== 五维能量系统(账本模式) ====================
|
|
|
+
|
|
|
+ // 维度定义表
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute(
|
|
|
+ "CREATE TABLE IF NOT EXISTS energy_dimension (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
+ "code VARCHAR(16) NOT NULL UNIQUE COMMENT '维度代码: body/mind/wisdom/action/wealth', " +
|
|
|
+ "name VARCHAR(20) NOT NULL COMMENT '维度名称: 身/心/智/行/富', " +
|
|
|
+ "icon VARCHAR(16) COMMENT '图标emoji', " +
|
|
|
+ "element VARCHAR(10) COMMENT '五行元素: 土/火/金/木/水', " +
|
|
|
+ "sort_order INT DEFAULT 0 COMMENT '显示顺序', " +
|
|
|
+ "status TINYINT DEFAULT 1 COMMENT '1启用/0禁用', " +
|
|
|
+ "created_at DATETIME DEFAULT CURRENT_TIMESTAMP" +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='能量维度定义表'"
|
|
|
+ );
|
|
|
+ log.info("已创建energy_dimension表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建energy_dimension表失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 服务-维度比例配置表
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute(
|
|
|
+ "CREATE TABLE IF NOT EXISTS energy_source_config (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
+ "source_type VARCHAR(32) NOT NULL COMMENT '来源类型: task/activity/course/product/consultation', " +
|
|
|
+ "source_id BIGINT NOT NULL COMMENT '来源业务表ID', " +
|
|
|
+ "source_name VARCHAR(200) COMMENT '冗余名称', " +
|
|
|
+ "dimension_id BIGINT NOT NULL COMMENT '维度ID', " +
|
|
|
+ "ratio INT NOT NULL COMMENT '占比(bps,10000=100%)', " +
|
|
|
+ "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
+ "INDEX idx_source (source_type, source_id)" +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='服务-维度比例配置表'"
|
|
|
+ );
|
|
|
+ log.info("已创建energy_source_config表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建energy_source_config表失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 购物车
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS cart_items (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY," +
|
|
|
+ "user_id BIGINT NOT NULL COMMENT '用户ID'," +
|
|
|
+ "product_id BIGINT NOT NULL COMMENT '商品ID'," +
|
|
|
+ "quantity INT DEFAULT 1 COMMENT '数量'," +
|
|
|
+ "selected TINYINT(1) DEFAULT 1 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)," +
|
|
|
+ "UNIQUE KEY uk_user_product (user_id, product_id)" +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='购物车'");
|
|
|
+ log.info("已创建cart_items表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建cart_items表失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS shop_categories (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY," +
|
|
|
+ "name VARCHAR(64) NOT NULL COMMENT '分类名称'," +
|
|
|
+ "parent_id BIGINT DEFAULT 0 COMMENT '父分类ID(0=根节点)'," +
|
|
|
+ "level INT DEFAULT 0 COMMENT '层级(0/1/2/3)'," +
|
|
|
+ "sort INT DEFAULT 0 COMMENT '排序'," +
|
|
|
+ "image VARCHAR(512) COMMENT '分类图标'," +
|
|
|
+ "enabled TINYINT(1) DEFAULT 1 COMMENT '启用'," +
|
|
|
+ "dimension_codes VARCHAR(128) COMMENT '维度编码(逗号分隔: body,mind,heart)'," +
|
|
|
+ "product_types VARCHAR(128) COMMENT '商品类型(逗号分隔: PHYSICAL,COURSE,ASSESSMENT)'," +
|
|
|
+ "created_at DATETIME DEFAULT CURRENT_TIMESTAMP," +
|
|
|
+ "updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP," +
|
|
|
+ "INDEX idx_parent (parent_id)," +
|
|
|
+ "INDEX idx_sort (sort)" +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品类目'");
|
|
|
+ log.info("已创建shop_categories表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建shop_categories表失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS distribution_systems (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY," +
|
|
|
+ "name VARCHAR(64) NOT NULL COMMENT '体系名称'," +
|
|
|
+ "description VARCHAR(255) COMMENT '描述'," +
|
|
|
+ "owner_id BIGINT COMMENT '创建者ID'," +
|
|
|
+ "profit_rate 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" +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='分销体系'");
|
|
|
+ log.info("已创建distribution_systems表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建distribution_systems表失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS distribution_relations (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY," +
|
|
|
+ "system_id BIGINT NOT NULL COMMENT '体系ID'," +
|
|
|
+ "user_id BIGINT NOT NULL COMMENT '用户ID'," +
|
|
|
+ "parent_id BIGINT COMMENT '上级用户ID(null=根)'," +
|
|
|
+ "depth INT DEFAULT 0 COMMENT '层级深度'," +
|
|
|
+ "created_at DATETIME DEFAULT CURRENT_TIMESTAMP," +
|
|
|
+ "INDEX idx_system (system_id)," +
|
|
|
+ "INDEX idx_user (user_id)," +
|
|
|
+ "INDEX idx_parent (parent_id)" +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='分销关系'");
|
|
|
+ log.info("已创建distribution_relations表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建distribution_relations表失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS after_sales_requests (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY," +
|
|
|
+ "order_id BIGINT NOT NULL COMMENT '订单ID'," +
|
|
|
+ "order_no VARCHAR(32) NOT NULL COMMENT '订单号'," +
|
|
|
+ "user_id BIGINT NOT NULL COMMENT '用户ID'," +
|
|
|
+ "type VARCHAR(32) NOT NULL COMMENT '类型: REFUND/RETURN_REFUND'," +
|
|
|
+ "reason VARCHAR(255) COMMENT '原因'," +
|
|
|
+ "description TEXT COMMENT '描述'," +
|
|
|
+ "images TEXT COMMENT '凭证图片(JSON数组)'," +
|
|
|
+ "refund_amount INT DEFAULT 0 COMMENT '退款金额(分)'," +
|
|
|
+ "status VARCHAR(20) DEFAULT 'pending' COMMENT '状态: pending/approved/rejected/completed'," +
|
|
|
+ "reject_reason VARCHAR(255) COMMENT '驳回原因'," +
|
|
|
+ "handled_by BIGINT COMMENT '处理人ID'," +
|
|
|
+ "handled_at DATETIME COMMENT '处理时间'," +
|
|
|
+ "created_at DATETIME DEFAULT CURRENT_TIMESTAMP," +
|
|
|
+ "updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP," +
|
|
|
+ "INDEX idx_order (order_id)," +
|
|
|
+ "INDEX idx_user (user_id)," +
|
|
|
+ "INDEX idx_status (status)" +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='售后申请'");
|
|
|
+ log.info("已创建after_sales_requests表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建after_sales_requests表失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS shop_discount_rules (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY," +
|
|
|
+ "name VARCHAR(64) NOT NULL COMMENT '规则名称'," +
|
|
|
+ "type VARCHAR(32) NOT NULL COMMENT '类型: PERCENT/FIXED/THRESHOLD'," +
|
|
|
+ "value INT NOT NULL COMMENT '折扣值(PERCENT=百分比数字, FIXED/THRESHOLD=金额分)'," +
|
|
|
+ "threshold_amount INT COMMENT '满减门槛(分,仅THRESHOLD)'," +
|
|
|
+ "category_id BIGINT COMMENT '适用类目ID(null=全部)'," +
|
|
|
+ "distribution_system_id BIGINT COMMENT '适用体系ID(null=全部)'," +
|
|
|
+ "product_ids TEXT COMMENT '适用商品ID(逗号分隔,null=全部)'," +
|
|
|
+ "start_time DATETIME COMMENT '开始时间'," +
|
|
|
+ "end_time DATETIME COMMENT '结束时间'," +
|
|
|
+ "max_uses INT DEFAULT 0 COMMENT '最大使用次数(0=不限)'," +
|
|
|
+ "used_count 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," +
|
|
|
+ "INDEX idx_category (category_id)," +
|
|
|
+ "INDEX idx_enabled (enabled)" +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='折扣规则'");
|
|
|
+ log.info("已创建shop_discount_rules表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建shop_discount_rules表失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 电商扩展字段 - products表
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE products ADD COLUMN category_id BIGINT COMMENT '商品类目ID'");
|
|
|
+ log.info("已添加category_id列到products表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 列已存在,忽略错误
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE products ADD COLUMN distribution_system_id BIGINT COMMENT '分销体系ID'");
|
|
|
+ log.info("已添加distribution_system_id列到products表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 列已存在,忽略错误
|
|
|
+ }
|
|
|
+
|
|
|
+ // 电商扩展字段 - product_orders表
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE product_orders ADD COLUMN discount_amount INT DEFAULT 0 COMMENT '优惠金额(分)'");
|
|
|
+ log.info("已添加discount_amount列到product_orders表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 列已存在,忽略错误
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE product_orders ADD COLUMN coupon_id BIGINT COMMENT '优惠券ID'");
|
|
|
+ log.info("已添加coupon_id列到product_orders表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 列已存在,忽略错误
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE product_orders ADD COLUMN cancel_at DATETIME COMMENT '取消时间'");
|
|
|
+ log.info("已添加cancel_at列到product_orders表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 列已存在,忽略错误
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE product_orders ADD COLUMN transaction_id VARCHAR(64) COMMENT '微信支付交易号'");
|
|
|
+ log.info("已添加transaction_id列到product_orders表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 列已存在,忽略错误
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE product_orders ADD COLUMN cover_image VARCHAR(512) COMMENT '商品封面图快照'");
|
|
|
+ log.info("已添加cover_image列到product_orders表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 列已存在,忽略错误
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE product_orders ADD COLUMN address_snapshot TEXT COMMENT '收货地址快照(JSON)'");
|
|
|
+ log.info("已添加address_snapshot列到product_orders表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 列已存在,忽略错误
|
|
|
+ }
|
|
|
+
|
|
|
+ // 能量流水表
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute(
|
|
|
+ "CREATE TABLE IF NOT EXISTS energy_log (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
+ "child_id BIGINT NOT NULL COMMENT '孩子ID', " +
|
|
|
+ "dimension_id BIGINT NOT NULL COMMENT '维度ID', " +
|
|
|
+ "amount INT NOT NULL COMMENT '变动量(正=获得,负=消耗)', " +
|
|
|
+ "balance_after INT NOT NULL COMMENT '变动后余额', " +
|
|
|
+ "source_type VARCHAR(32) COMMENT '来源类型: task/product/medical_report/daily_record', " +
|
|
|
+ "source_id BIGINT COMMENT '来源ID', " +
|
|
|
+ "ref_id BIGINT COMMENT '关联旧流水ID(覆盖时标记)', " +
|
|
|
+ "expires_at DATETIME COMMENT '过期时间(空=永不过期)', " +
|
|
|
+ "description VARCHAR(500) COMMENT '描述', " +
|
|
|
+ "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
+ "INDEX idx_child_dim (child_id, dimension_id), " +
|
|
|
+ "INDEX idx_created (created_at), " +
|
|
|
+ "INDEX idx_source (source_type, source_id)" +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='能量流水表'"
|
|
|
+ );
|
|
|
+ log.info("已创建energy_log表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建energy_log表失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 维度余额表
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute(
|
|
|
+ "CREATE TABLE IF NOT EXISTS energy_balance (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
+ "child_id BIGINT NOT NULL COMMENT '孩子ID', " +
|
|
|
+ "dimension_id BIGINT NOT NULL COMMENT '维度ID', " +
|
|
|
+ "balance INT DEFAULT 0 COMMENT '当前能量值(可为负)', " +
|
|
|
+ "total_earned INT DEFAULT 0 COMMENT '累计获得', " +
|
|
|
+ "total_spent INT DEFAULT 0 COMMENT '累计消耗', " +
|
|
|
+ "updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, " +
|
|
|
+ "UNIQUE KEY uk_child_dim (child_id, dimension_id)" +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='维度余额表'"
|
|
|
+ );
|
|
|
+ log.info("已创建energy_balance表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建energy_balance表失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 维度种子数据(按相生链: 智→富→行→心→身)
|
|
|
+ try {
|
|
|
+ Integer dimCount = jdbcTemplate.queryForObject(
|
|
|
+ "SELECT COUNT(*) FROM energy_dimension", Integer.class);
|
|
|
+ if (dimCount == null || dimCount == 0) {
|
|
|
+ jdbcTemplate.execute("INSERT INTO energy_dimension (code, name, icon, element, sort_order, status) VALUES " +
|
|
|
+ "('mind', '心', '🔥', '火', 4, 1), " +
|
|
|
+ "('body', '身', '🌏', '土', 5, 1), " +
|
|
|
+ "('wisdom', '智', '⚔️', '金', 1, 1), " +
|
|
|
+ "('action', '行', '🌿', '木', 3, 1), " +
|
|
|
+ "('wealth', '富', '💧', '水', 2, 1)");
|
|
|
+ log.info("能量维度种子数据已初始化");
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("初始化能量维度种子数据失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 为已有商品插入默认比例配置(与Product.domain一致)
|
|
|
+ try {
|
|
|
+ Integer productConfigCount = jdbcTemplate.queryForObject(
|
|
|
+ "SELECT COUNT(*) FROM energy_source_config WHERE source_type = 'product'", Integer.class);
|
|
|
+ if (productConfigCount == null || productConfigCount == 0) {
|
|
|
+ String[][] domainMapping = {
|
|
|
+ {"action", "action"}, {"mind", "mind"}, {"body", "body"},
|
|
|
+ {"wisdom", "wisdom"}, {"wealth", "wealth"}
|
|
|
+ };
|
|
|
+ for (String[] mapping : domainMapping) {
|
|
|
+ jdbcTemplate.execute(
|
|
|
+ "INSERT INTO energy_source_config (source_type, source_id, source_name, dimension_id, ratio) " +
|
|
|
+ "SELECT 'product', p.id, p.name, d.id, 10000 " +
|
|
|
+ "FROM products p " +
|
|
|
+ "JOIN energy_dimension d ON d.code = '" + mapping[1] + "' " +
|
|
|
+ "WHERE p.domain = '" + mapping[0] + "' " +
|
|
|
+ "AND NOT EXISTS (" +
|
|
|
+ " SELECT 1 FROM energy_source_config esc " +
|
|
|
+ " WHERE esc.source_type = 'product' AND esc.source_id = p.id" +
|
|
|
+ ")"
|
|
|
+ );
|
|
|
+ }
|
|
|
+ log.info("已有商品能量比例配置已初始化");
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("初始化商品能量比例配置失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== 精准营养 P0: health_report_survey ====================
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS health_report_survey (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
+ "report_id BIGINT NOT NULL COMMENT '关联报告ID', " +
|
|
|
+ "user_id BIGINT NOT NULL COMMENT '用户ID', " +
|
|
|
+ "child_id BIGINT NOT NULL COMMENT '孩子ID', " +
|
|
|
+ "survey_data JSON COMMENT '调研问卷结果JSON', " +
|
|
|
+ "status VARCHAR(20) DEFAULT 'pending' COMMENT '状态: pending/completed', " +
|
|
|
+ "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
+ "updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, " +
|
|
|
+ "UNIQUE KEY uk_report (report_id)" +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='报告调研问卷'");
|
|
|
+ log.info("已创建 health_report_survey 表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建 health_report_survey 表失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== 精准营养 P0: health_gut_flora + health_disease_risk ====================
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS health_gut_flora (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
+ "report_id BIGINT NOT NULL COMMENT '关联报告ID', " +
|
|
|
+ "bacteria_name VARCHAR(100) NOT NULL COMMENT '菌种名称', " +
|
|
|
+ "bacteria_value VARCHAR(50) COMMENT '菌种丰度值', " +
|
|
|
+ "normal_range VARCHAR(100) COMMENT '正常范围', " +
|
|
|
+ "status VARCHAR(20) COMMENT '状态: 正常/偏低/偏高', " +
|
|
|
+ "category VARCHAR(50) COMMENT '分类: 有益菌/有害菌/中性菌', " +
|
|
|
+ "sort_order INT DEFAULT 0 COMMENT '排序', " +
|
|
|
+ "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
+ "INDEX idx_report (report_id)" +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='菌群检测专项数据'");
|
|
|
+ log.info("已创建 health_gut_flora 表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建 health_gut_flora 表失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS health_disease_risk (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
+ "report_id BIGINT NOT NULL COMMENT '关联报告ID', " +
|
|
|
+ "disease_name VARCHAR(100) NOT NULL COMMENT '疾病名称', " +
|
|
|
+ "risk_value VARCHAR(50) COMMENT '风险值(0-1)', " +
|
|
|
+ "risk_level VARCHAR(20) COMMENT '风险等级: 高风险/需注意/低风险', " +
|
|
|
+ "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
+ "INDEX idx_report (report_id)" +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='疾病风险评估数据'");
|
|
|
+ log.info("已创建 health_disease_risk 表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建 health_disease_risk 表失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== 迁移: health_reports 表添加新字段 ====================
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE health_reports ADD COLUMN balance_score INT DEFAULT NULL COMMENT '菌群平衡分'");
|
|
|
+ log.info("已添加balance_score列到health_reports表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 列已存在,忽略错误
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE health_reports ADD COLUMN diversity_score INT DEFAULT NULL COMMENT '菌群多样性分'");
|
|
|
+ log.info("已添加diversity_score列到health_reports表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 列已存在,忽略错误
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE health_reports ADD COLUMN beneficial_score INT DEFAULT NULL COMMENT '有益菌评分'");
|
|
|
+ log.info("已添加beneficial_score列到health_reports表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 列已存在,忽略错误
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE health_reports ADD COLUMN harmful_score INT DEFAULT NULL COMMENT '有害菌评分'");
|
|
|
+ log.info("已添加harmful_score列到health_reports表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 列已存在,忽略错误
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE health_reports ADD COLUMN core_genus_score INT DEFAULT NULL COMMENT '核心菌属评分'");
|
|
|
+ log.info("已添加core_genus_score列到health_reports表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 列已存在,忽略错误
|
|
|
+ }
|
|
|
+
|
|
|
+ // 迁移: health_reports 表添加报告编号和姓名字段
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE health_reports ADD COLUMN report_number VARCHAR(50) DEFAULT NULL COMMENT '报告编号'");
|
|
|
+ log.info("已添加report_number列到health_reports表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 列已存在,忽略错误
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE health_reports ADD COLUMN person_name VARCHAR(50) DEFAULT NULL COMMENT '被检测人姓名'");
|
|
|
+ log.info("已添加person_name列到health_reports表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 列已存在,忽略错误
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== 迁移: health_gut_flora 表添加新字段 ====================
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE health_gut_flora ADD COLUMN population_level VARCHAR(50) DEFAULT NULL COMMENT '处于人群%水平'");
|
|
|
+ log.info("已添加population_level列到health_gut_flora表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 列已存在,忽略错误
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE health_gut_flora ADD COLUMN detection_rate VARCHAR(50) DEFAULT NULL COMMENT '%正常人有检出'");
|
|
|
+ log.info("已添加detection_rate列到health_gut_flora表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 列已存在,忽略错误
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE health_gut_flora ADD COLUMN description TEXT DEFAULT NULL COMMENT '菌种说明'");
|
|
|
+ log.info("已添加description列到health_gut_flora表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 列已存在,忽略错误
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE health_gut_flora ADD COLUMN `level` VARCHAR(20) DEFAULT 'GENUS' COMMENT '级别: GENUS/SPECIES'");
|
|
|
+ log.info("已添加level列到health_gut_flora表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 列已存在,忽略错误
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== 迁移: guides 表统一 ====================
|
|
|
+
|
|
|
+ // 迁移: guides 添加 type 字段(区分服务商类型)
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE guides ADD COLUMN type VARCHAR(32) DEFAULT 'teacher' COMMENT '服务商类型: teacher/planner/activity_provider/product_supplier'");
|
|
|
+ log.info("已添加type列到guides表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 列已存在,忽略错误
|
|
|
+ }
|
|
|
+
|
|
|
+ // 迁移: guides 添加 teachers 合并字段
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE guides ADD COLUMN teacher_no VARCHAR(20) COMMENT '服务商编号'");
|
|
|
+ log.info("已添加teacher_no列到guides表");
|
|
|
+ } catch (Exception e) {}
|
|
|
+
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE guides ADD COLUMN title VARCHAR(50) COMMENT '职称/称号'");
|
|
|
+ } catch (Exception e) {}
|
|
|
+
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE guides ADD COLUMN specialty VARCHAR(200) COMMENT '专长领域'");
|
|
|
+ } catch (Exception e) {}
|
|
|
+
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE guides ADD COLUMN bio TEXT COMMENT '简介'");
|
|
|
+ } catch (Exception e) {}
|
|
|
+
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE guides ADD COLUMN experience_years TINYINT DEFAULT 0 COMMENT '从业年限'");
|
|
|
+ } catch (Exception e) {}
|
|
|
+
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE guides ADD COLUMN rating INT DEFAULT 500 COMMENT '评分(百分位,500=5.00)'");
|
|
|
+ } catch (Exception e) {}
|
|
|
+
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE guides ADD COLUMN total_appointments INT DEFAULT 0 COMMENT '总服务次数'");
|
|
|
+ } catch (Exception e) {}
|
|
|
+
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE guides ADD COLUMN street_id BIGINT COMMENT '街道ID'");
|
|
|
+ } catch (Exception e) {}
|
|
|
+
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE guides ADD COLUMN province VARCHAR(50) COMMENT '省份'");
|
|
|
+ jdbcTemplate.execute("ALTER TABLE guides ADD COLUMN city VARCHAR(50) COMMENT '城市'");
|
|
|
+ jdbcTemplate.execute("ALTER TABLE guides ADD COLUMN district VARCHAR(50) COMMENT '区县'");
|
|
|
+ jdbcTemplate.execute("ALTER TABLE guides ADD COLUMN street VARCHAR(100) COMMENT '街道'");
|
|
|
+ log.info("已添加地址字段到guides表");
|
|
|
+ } catch (Exception e) {}
|
|
|
+
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE guides ADD COLUMN verified_at DATETIME COMMENT '审核通过时间'");
|
|
|
+ } catch (Exception e) {}
|
|
|
+
|
|
|
+ // 迁移: 将 teachers 表数据合并到 guides(type='teacher')
|
|
|
+ try {
|
|
|
+ int migrated = jdbcTemplate.update(
|
|
|
+ "INSERT IGNORE INTO guides (user_id, type, teacher_no, title, specialty, bio, experience_years, rating, total_appointments, " +
|
|
|
+ "street_id, province, city, district, street, verified_at, status, level, created_at, updated_at) " +
|
|
|
+ "SELECT t.user_id, 'teacher', t.teacher_no, t.title, t.specialty, t.bio, t.experience_years, t.rating, t.total_appointments, " +
|
|
|
+ "t.street_id, t.province, t.city, t.district, t.street, t.verified_at, " +
|
|
|
+ "CASE WHEN t.status = 'active' THEN 'active' ELSE 'inactive' END, " +
|
|
|
+ "CASE WHEN t.verification_status = 'approved' THEN 'senior' WHEN t.verification_status = 'pending' THEN 'junior' ELSE 'junior' END, " +
|
|
|
+ "t.created_at, t.updated_at " +
|
|
|
+ "FROM teachers t " +
|
|
|
+ "LEFT JOIN guides g ON g.user_id = t.user_id " +
|
|
|
+ "WHERE g.id IS NULL"
|
|
|
+ );
|
|
|
+ if (migrated > 0) {
|
|
|
+ log.info("已从 teachers 表迁移 {} 条数据到 guides(type=teacher)", migrated);
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("迁移 teachers → guides 数据失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 迁移: dan_assessment_results 表添加认知维度字段(6维认知体系扩展)
|
|
|
+ try {
|
|
|
+ Integer colExists = jdbcTemplate.queryForObject(
|
|
|
+ "SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'dan_assessment_results' AND COLUMN_NAME = 'perception_score'",
|
|
|
+ Integer.class);
|
|
|
+ if (colExists == null || colExists == 0) {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE dan_assessment_results " +
|
|
|
+ "ADD COLUMN perception_score INT COMMENT '感知得分', " +
|
|
|
+ "ADD COLUMN spatial_score INT COMMENT '空间思维得分', " +
|
|
|
+ "ADD COLUMN processing_speed_score INT COMMENT '加工速度得分'");
|
|
|
+ log.info("已为 dan_assessment_results 表添加认知维度字段");
|
|
|
+ } else {
|
|
|
+ log.info("dan_assessment_results 认知维度字段已存在,跳过");
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("检查/添加 dan_assessment_results 认知维度字段失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 迁移: dan_assessment_results 表添加 EMI 维度字段(v1.2 心智模型重构)
|
|
|
+ try {
|
|
|
+ Integer colExists = jdbcTemplate.queryForObject(
|
|
|
+ "SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'dan_assessment_results' AND COLUMN_NAME = 'emotion_management_score'",
|
|
|
+ Integer.class);
|
|
|
+ if (colExists == null || colExists == 0) {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE dan_assessment_results " +
|
|
|
+ "ADD COLUMN emotion_management_score INT COMMENT '情绪管理得分(EMI)', " +
|
|
|
+ "ADD COLUMN empathy_score INT COMMENT '同理心得分(EMI)', " +
|
|
|
+ "ADD COLUMN social_adaptability_score INT COMMENT '社会适应得分(EMI)', " +
|
|
|
+ "ADD COLUMN self_motivation_score INT COMMENT '自我激励得分(EMI)'");
|
|
|
+ log.info("已为 dan_assessment_results 表添加 EMI 维度字段");
|
|
|
+ } else {
|
|
|
+ log.info("dan_assessment_results EMI 维度字段已存在,跳过");
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("检查/添加 dan_assessment_results EMI 维度字段失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 迁移: dan_assessment_results 表添加 EMI 心理子维度字段
|
|
|
+ try {
|
|
|
+ Integer colExists = jdbcTemplate.queryForObject(
|
|
|
+ "SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'dan_assessment_results' AND COLUMN_NAME = 'emotion_score'",
|
|
|
+ Integer.class);
|
|
|
+ if (colExists == null || colExists == 0) {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE dan_assessment_results " +
|
|
|
+ "ADD COLUMN emotion_score INT COMMENT '情绪商数(0-100)', " +
|
|
|
+ "ADD COLUMN resilience_score INT COMMENT '心理韧性(0-100)', " +
|
|
|
+ "ADD COLUMN stress_coping_score INT COMMENT '压力应对(0-100)', " +
|
|
|
+ "ADD COLUMN self_awareness_score INT COMMENT '自我认知(0-100)'");
|
|
|
+ log.info("已为 dan_assessment_results 表添加 EMI 心理子维度字段");
|
|
|
+ } else {
|
|
|
+ log.info("dan_assessment_results EMI 心理子维度字段已存在,跳过");
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("检查/添加 dan_assessment_results EMI 心理子维度字段失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 迁移: dan_assessment_results 表添加大五人格维度字段
|
|
|
+ try {
|
|
|
+ Integer colExists = jdbcTemplate.queryForObject(
|
|
|
+ "SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'dan_assessment_results' AND COLUMN_NAME = 'openness_score'",
|
|
|
+ Integer.class);
|
|
|
+ if (colExists == null || colExists == 0) {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE dan_assessment_results " +
|
|
|
+ "ADD COLUMN openness_score INT COMMENT '开放性(0-100)', " +
|
|
|
+ "ADD COLUMN conscientiousness_score INT COMMENT '尽责性(0-100)', " +
|
|
|
+ "ADD COLUMN extraversion_score INT COMMENT '外向性(0-100)', " +
|
|
|
+ "ADD COLUMN agreeableness_score INT COMMENT '宜人性(0-100)', " +
|
|
|
+ "ADD COLUMN neuroticism_score INT COMMENT '神经质(0-100)', " +
|
|
|
+ "ADD COLUMN big_five_overall_score INT COMMENT '大五人格综合分'");
|
|
|
+ log.info("已为 dan_assessment_results 表添加大五人格维度字段");
|
|
|
+ } else {
|
|
|
+ log.info("dan_assessment_results 大五人格维度字段已存在,跳过");
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("检查/添加 dan_assessment_results 大五人格维度字段失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 迁移: users 表添加出生时辰字段
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE users ADD COLUMN birth_hour VARCHAR(10) COMMENT '出生时辰: 子丑寅卯辰巳午未申酉戌亥'");
|
|
|
+ log.info("已添加birth_hour列到users表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 列已存在,忽略错误
|
|
|
+ }
|
|
|
+
|
|
|
+ // 迁移: 重建 family_member_attributes 表结构(v1.2 心智模型)
|
|
|
+ try {
|
|
|
+ Integer colExists = jdbcTemplate.queryForObject(
|
|
|
+ "SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'family_member_attributes' AND COLUMN_NAME = 'member_id'",
|
|
|
+ Integer.class);
|
|
|
+ if (colExists == null || colExists == 0) {
|
|
|
+ // 旧表存在但结构不同,先删除旧表重建
|
|
|
+ jdbcTemplate.execute("DROP TABLE IF EXISTS family_member_attributes_old");
|
|
|
+ jdbcTemplate.execute("RENAME TABLE family_member_attributes TO family_member_attributes_old");
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS family_member_attributes (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
+ "member_id BIGINT NOT NULL COMMENT '家庭成员ID(children表或users表)', " +
|
|
|
+ "member_type VARCHAR(10) NOT NULL COMMENT '成员类型: child/parent', " +
|
|
|
+ "birth_datetime DATETIME COMMENT '出生日期(精确到分钟,用于八字排盘)', " +
|
|
|
+ "eight_characters JSON COMMENT '八字四柱JSON', " +
|
|
|
+ "wuxing_elements JSON COMMENT '五行星盘元素JSON', " +
|
|
|
+ "zodiac VARCHAR(16) COMMENT '生肖: rat/ox/tiger/rabbit/dragon/snake/horse/sheep/monkey/rooster/dog/pig', " +
|
|
|
+ "blood_type VARCHAR(5) COMMENT '血型: A/B/AB/O', " +
|
|
|
+ "mind_base_score INT DEFAULT 0 COMMENT '心维度基础分(百分位,500=5.00)', " +
|
|
|
+ "wisdom_base_score INT DEFAULT 0 COMMENT '智维度基础分(百分位,500=5.00)', " +
|
|
|
+ "behavior_modifier JSON COMMENT '行为修正因子JSON', " +
|
|
|
+ "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
+ "updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, " +
|
|
|
+ "UNIQUE KEY uk_member (member_id, member_type)" +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='家庭成员先天属性表'");
|
|
|
+ log.info("已重建 family_member_attributes 表(v1.2 心智模型)");
|
|
|
+ } else {
|
|
|
+ log.info("family_member_attributes 表已为 v1.2 结构,跳过");
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("重建 family_member_attributes 表失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 迁移: children 表添加 show_to_family 列(v1.3 家庭成员卡)
|
|
|
+ try {
|
|
|
+ Integer colExists = jdbcTemplate.queryForObject(
|
|
|
+ "SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'children' AND COLUMN_NAME = 'show_to_family'",
|
|
|
+ Integer.class);
|
|
|
+ if (colExists == null || colExists == 0) {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE children ADD COLUMN show_to_family TINYINT DEFAULT 1 COMMENT '是否对家庭成员可见: 1=是, 0=否'");
|
|
|
+ log.info("已为 children 表添加 show_to_family 列");
|
|
|
+ } else {
|
|
|
+ log.info("children 表 show_to_family 列已存在,跳过");
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("检查/添加 children 表 show_to_family 列失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 迁移: users 表添加 show_to_family 列(v1.3 家庭成员卡)
|
|
|
+ try {
|
|
|
+ Integer colExists = jdbcTemplate.queryForObject(
|
|
|
+ "SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'users' AND COLUMN_NAME = 'show_to_family'",
|
|
|
+ Integer.class);
|
|
|
+ if (colExists == null || colExists == 0) {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE users ADD COLUMN show_to_family TINYINT DEFAULT 1 COMMENT '是否对家庭成员可见: 1=是, 0=否'");
|
|
|
+ log.info("已为 users 表添加 show_to_family 列");
|
|
|
+ } else {
|
|
|
+ log.info("users 表 show_to_family 列已存在,跳过");
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("检查/添加 users 表 show_to_family 列失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 迁移: tasks 表添加 family_member_id 列(FamilyMember 迁移)
|
|
|
+ try {
|
|
|
+ Integer colExists = jdbcTemplate.queryForObject(
|
|
|
+ "SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'tasks' AND COLUMN_NAME = 'family_member_id'",
|
|
|
+ Integer.class);
|
|
|
+ if (colExists == null || colExists == 0) {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE tasks ADD COLUMN family_member_id BIGINT COMMENT '家庭成员ID(关联 family_members.id)'");
|
|
|
+ log.info("已为 tasks 表添加 family_member_id 列");
|
|
|
+ } else {
|
|
|
+ log.info("tasks 表 family_member_id 列已存在,跳过");
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("检查/添加 tasks 表 family_member_id 列失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 迁移: family_members 表添加 role_override 列(FamilyMember 迁移)
|
|
|
+ try {
|
|
|
+ Integer colExists = jdbcTemplate.queryForObject(
|
|
|
+ "SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'family_members' AND COLUMN_NAME = 'role_override'",
|
|
|
+ Integer.class);
|
|
|
+ if (colExists == null || colExists == 0) {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE family_members ADD COLUMN role_override VARCHAR(20) DEFAULT 'auto' COMMENT '角色覆盖: auto/parent/child/elderly,空=auto'");
|
|
|
+ log.info("已为 family_members 表添加 role_override 列");
|
|
|
+ } else {
|
|
|
+ log.info("family_members 表 role_override 列已存在,跳过");
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("检查/添加 family_members 表 role_override 列失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 迁移: family_members 表添加 id_card 列(孩子身份证号)
|
|
|
+ try {
|
|
|
+ Integer colExists = jdbcTemplate.queryForObject(
|
|
|
+ "SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'family_members' AND COLUMN_NAME = 'id_card'",
|
|
|
+ Integer.class);
|
|
|
+ if (colExists == null || colExists == 0) {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE family_members ADD COLUMN id_card VARCHAR(18) DEFAULT NULL COMMENT '身份证号(孩子)'");
|
|
|
+ log.info("已为 family_members 表添加 id_card 列");
|
|
|
+ } else {
|
|
|
+ log.info("family_members 表 id_card 列已存在,跳过");
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("检查/添加 family_members 表 id_card 列失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 迁移: family_members 表添加孩子专属字段(原children表合并)
|
|
|
+ String[][] childColumns = {
|
|
|
+ {"penalty_enabled", "TINYINT DEFAULT 1 COMMENT '是否扣减积分(0否1是)'"},
|
|
|
+ {"theme", "VARCHAR(32) DEFAULT 'default' COMMENT '主题'"},
|
|
|
+ {"system_points", "INT DEFAULT 0 COMMENT '系统积分(干预/平台奖励)'"},
|
|
|
+ {"streak_days", "INT DEFAULT 0 COMMENT '连续打卡天数'"},
|
|
|
+ {"last_task_date", "DATE COMMENT '上次任务日期'"},
|
|
|
+ {"focus_max_daily", "TINYINT DEFAULT 3 COMMENT '每日专注上限'"},
|
|
|
+ {"focus_remaining","TINYINT DEFAULT 3 COMMENT '今日剩余专注次数'"},
|
|
|
+ {"focus_reset_date","DATE COMMENT '专注次数重置日期'"}
|
|
|
+ };
|
|
|
+ for (String[] col : childColumns) {
|
|
|
+ try {
|
|
|
+ Integer colExists = jdbcTemplate.queryForObject(
|
|
|
+ "SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'family_members' AND COLUMN_NAME = ?",
|
|
|
+ Integer.class, col[0]);
|
|
|
+ if (colExists == null || colExists == 0) {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE family_members ADD COLUMN " + col[0] + " " + col[1]);
|
|
|
+ log.info("已为 family_members 表添加 {} 列", col[0]);
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("检查/添加 family_members 表 {} 列失败: {}", col[0], e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 关系类型表添加 default_role 列(角色模板)
|
|
|
+ ensureColumn("relationship_types", "default_role", "VARCHAR(20) DEFAULT NULL COMMENT '默认角色模板: parent/child'");
|
|
|
+
|
|
|
+ // 家庭成员表添加 role_override 列(替换旧 capability_role)
|
|
|
+ ensureColumn("family_members", "role_override", "VARCHAR(20) DEFAULT 'auto' COMMENT '角色覆盖: auto/parent/child/elderly'");
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE family_members DROP COLUMN capability_role");
|
|
|
+ log.info("已删除 family_members.capability_role 旧列,已由 role_override 替代");
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 列不存在或已删除,忽略错误
|
|
|
+ }
|
|
|
+
|
|
|
+ // activities 表添加报名/签到相关字段
|
|
|
+ ensureColumn("activities", "require_registration", "TINYINT DEFAULT 1 COMMENT '是否需要报名: 1=需要, 0=直接参与'");
|
|
|
+ ensureColumn("activities", "require_checkin_review", "TINYINT DEFAULT 1 COMMENT '打卡是否需要审核: 1=需要, 0=直接通过'");
|
|
|
+ ensureColumn("activities", "energy_penalty_unregistered", "INT DEFAULT 5 COMMENT '未报名打卡的行能量扣减值'");
|
|
|
+
|
|
|
+ // family_members 表添加关系评分字段
|
|
|
+ ensureColumn("family_members", "trust_score", "DECIMAL(5,2) DEFAULT 0 COMMENT '信任度 0-100'");
|
|
|
+ ensureColumn("family_members", "intimacy_score", "DECIMAL(5,2) DEFAULT 0 COMMENT '亲密度 0-100'");
|
|
|
+ ensureColumn("family_members", "communication_score", "DECIMAL(5,2) DEFAULT 0 COMMENT '沟通质量 0-100'");
|
|
|
+ ensureColumn("family_members", "last_interaction_at", "DATETIME COMMENT '最近互动时间'");
|
|
|
+
|
|
|
+ // 创建购物车表
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS cart (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
+ "user_id BIGINT NOT NULL COMMENT '用户ID', " +
|
|
|
+ "product_id BIGINT NOT NULL COMMENT '商品ID', " +
|
|
|
+ "quantity INT NOT NULL DEFAULT 1 COMMENT '数量', " +
|
|
|
+ "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
+ "updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, " +
|
|
|
+ "INDEX idx_user_product (user_id, product_id)" +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='购物车'");
|
|
|
+ log.info("已创建 cart 表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建 cart 表失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== DanShop 电商模块建表 ====================
|
|
|
+ // 1. danshop_categories (商品分类表)
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS danshop_categories (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
+ "parent_id BIGINT DEFAULT NULL COMMENT '父分类ID(null=一级)', " +
|
|
|
+ "name VARCHAR(64) NOT NULL COMMENT '分类名称', " +
|
|
|
+ "image VARCHAR(512) DEFAULT NULL COMMENT '分类图标URL', " +
|
|
|
+ "sort_order INT DEFAULT 0 COMMENT '排序(越小越靠前)', " +
|
|
|
+ "energy_dimension_id BIGINT DEFAULT NULL COMMENT '关联五维维度ID', " +
|
|
|
+ "status INT DEFAULT 1 COMMENT '1启用/0禁用', " +
|
|
|
+ "created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
+ "updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, " +
|
|
|
+ "INDEX idx_parent (parent_id), " +
|
|
|
+ "INDEX idx_status (status)" +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品分类表'");
|
|
|
+ log.info("已创建 danshop_categories 表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建 danshop_categories 表失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. danshop_products (商品表)
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS danshop_products (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
+ "category_id BIGINT NOT NULL COMMENT '所属分类ID', " +
|
|
|
+ "name VARCHAR(128) NOT NULL COMMENT '商品名称', " +
|
|
|
+ "description TEXT COMMENT '商品详细描述', " +
|
|
|
+ "cover_image VARCHAR(512) COMMENT '封面图URL', " +
|
|
|
+ "images TEXT COMMENT '多图(JSON数组)', " +
|
|
|
+ "price INT NOT NULL COMMENT '售价(分)', " +
|
|
|
+ "member_price INT DEFAULT NULL COMMENT '会员价(分)', " +
|
|
|
+ "cost_price INT DEFAULT NULL COMMENT '成本价(分)', " +
|
|
|
+ "stock INT DEFAULT 0 COMMENT '库存数量', " +
|
|
|
+ "sales_count INT DEFAULT 0 COMMENT '销量', " +
|
|
|
+ "product_type VARCHAR(32) NOT NULL COMMENT '类型: physical/virtual/coupon/course', " +
|
|
|
+ "vendor_id BIGINT DEFAULT NULL COMMENT '供应商ID', " +
|
|
|
+ "vendor_name VARCHAR(64) COMMENT '供应商名称', " +
|
|
|
+ "status VARCHAR(16) NOT NULL DEFAULT 'pending' COMMENT 'pending/approved/rejected/on_shelf/off_shelf', " +
|
|
|
+ "reject_reason VARCHAR(255) DEFAULT NULL COMMENT '审核拒绝原因', " +
|
|
|
+ "profit_rate INT DEFAULT 0 COMMENT '平台利润率(bps,100=1%)', " +
|
|
|
+ "external_source VARCHAR(32) COMMENT '外部商品来源', " +
|
|
|
+ "external_id VARCHAR(64) COMMENT '外部商品ID', " +
|
|
|
+ "external_data TEXT COMMENT '外部商品完整数据(JSON)', " +
|
|
|
+ "created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
+ "updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, " +
|
|
|
+ "INDEX idx_category (category_id), " +
|
|
|
+ "INDEX idx_vendor (vendor_id), " +
|
|
|
+ "INDEX idx_status (status)" +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品表'");
|
|
|
+ log.info("已创建 danshop_products 表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建 danshop_products 表失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 3. danshop_orders (主订单表)
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS danshop_orders (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
+ "order_no VARCHAR(32) NOT NULL UNIQUE COMMENT '订单号(DS+时间戳+6位随机)', " +
|
|
|
+ "buyer_id BIGINT NOT NULL COMMENT '买家ID', " +
|
|
|
+ "family_id BIGINT DEFAULT NULL COMMENT '家庭ID', " +
|
|
|
+ "total_amount INT NOT NULL COMMENT '订单总金额(分)', " +
|
|
|
+ "discount_amount INT DEFAULT 0 COMMENT '优惠金额(分)', " +
|
|
|
+ "actual_amount INT NOT NULL COMMENT '实付金额(分)', " +
|
|
|
+ "status VARCHAR(20) NOT NULL DEFAULT 'pending' COMMENT 'pending/paid/shipped/completed/refunding/refunded/cancelled', " +
|
|
|
+ "payment_method VARCHAR(16) DEFAULT 'wechat' COMMENT 'wechat/alipay', " +
|
|
|
+ "transaction_id VARCHAR(64) DEFAULT NULL COMMENT '微信/支付宝交易号', " +
|
|
|
+ "paid_at DATETIME DEFAULT NULL COMMENT '支付时间', " +
|
|
|
+ "shipped_at DATETIME DEFAULT NULL COMMENT '发货时间', " +
|
|
|
+ "completed_at DATETIME DEFAULT NULL COMMENT '完成时间', " +
|
|
|
+ "remark VARCHAR(255) DEFAULT NULL COMMENT '买家备注', " +
|
|
|
+ "cancel_reason VARCHAR(255) DEFAULT NULL COMMENT '取消原因', " +
|
|
|
+ "receiver_name VARCHAR(32) DEFAULT NULL COMMENT '收件人姓名', " +
|
|
|
+ "receiver_phone VARCHAR(16) DEFAULT NULL COMMENT '收件人电话', " +
|
|
|
+ "receiver_address VARCHAR(256) DEFAULT NULL COMMENT '收件人地址', " +
|
|
|
+ "freight INT DEFAULT 0 COMMENT '运费(分)', " +
|
|
|
+ "profit_amount INT DEFAULT 0 COMMENT '分润金额(分)', " +
|
|
|
+ "created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
+ "updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, " +
|
|
|
+ "INDEX idx_buyer (buyer_id), " +
|
|
|
+ "INDEX idx_family (family_id), " +
|
|
|
+ "INDEX idx_status (status), " +
|
|
|
+ "INDEX idx_order_no (order_no)" +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='主订单表'");
|
|
|
+ log.info("已创建 danshop_orders 表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建 danshop_orders 表失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 4. danshop_order_items (订单明细表)
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS danshop_order_items (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
+ "order_id BIGINT NOT NULL COMMENT '所属订单ID', " +
|
|
|
+ "product_id BIGINT NOT NULL COMMENT '商品ID', " +
|
|
|
+ "product_name VARCHAR(128) NOT NULL COMMENT '商品名称(快照)', " +
|
|
|
+ "cover_image VARCHAR(512) COMMENT '封面图(快照)', " +
|
|
|
+ "unit_price INT NOT NULL COMMENT '单价(分,快照)', " +
|
|
|
+ "price INT NOT NULL COMMENT '单价(分,别名兼容)', " +
|
|
|
+ "quantity INT NOT NULL DEFAULT 1 COMMENT '购买数量', " +
|
|
|
+ "subtotal INT NOT NULL COMMENT '小计(分)', " +
|
|
|
+ "product_type VARCHAR(32) COMMENT '商品类型(快照)', " +
|
|
|
+ "vendor_id BIGINT COMMENT '供应商ID(快照)', " +
|
|
|
+ "vendor_name VARCHAR(64) COMMENT '供应商名称(快照)', " +
|
|
|
+ "created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
+ "INDEX idx_order (order_id), " +
|
|
|
+ "INDEX idx_product (product_id)" +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单明细表'");
|
|
|
+ log.info("已创建 danshop_order_items 表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建 danshop_order_items 表失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 5. danshop_order_logs (订单操作日志表)
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS danshop_order_logs (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
+ "order_id BIGINT NOT NULL COMMENT '订单ID', " +
|
|
|
+ "action VARCHAR(32) NOT NULL COMMENT '操作: create/pay/ship/confirm/refund/cancel', " +
|
|
|
+ "operator_id BIGINT DEFAULT NULL COMMENT '操作人ID', " +
|
|
|
+ "operator_type VARCHAR(16) DEFAULT NULL COMMENT 'buyer/vendor/admin/system', " +
|
|
|
+ "detail TEXT DEFAULT NULL COMMENT '操作详情(JSON)', " +
|
|
|
+ "created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
+ "INDEX idx_order (order_id)" +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单操作日志表'");
|
|
|
+ log.info("已创建 danshop_order_logs 表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建 danshop_order_logs 表失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 6. danshop_logistics (物流信息表)
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS danshop_logistics (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
+ "order_id BIGINT NOT NULL COMMENT '订单ID', " +
|
|
|
+ "order_item_id BIGINT DEFAULT NULL COMMENT '订单明细ID', " +
|
|
|
+ "logistics_no VARCHAR(64) NOT NULL COMMENT '物流单号', " +
|
|
|
+ "logistics_company VARCHAR(32) NOT NULL COMMENT '物流公司编码: sf/yto/zt/sto/yd/ttkd', " +
|
|
|
+ "logistics_name VARCHAR(32) DEFAULT NULL COMMENT '物流公司名称', " +
|
|
|
+ "sender_name VARCHAR(32) DEFAULT NULL COMMENT '发货人姓名', " +
|
|
|
+ "sender_phone VARCHAR(16) DEFAULT NULL COMMENT '发货人电话', " +
|
|
|
+ "sender_address VARCHAR(256) DEFAULT NULL COMMENT '发货人地址', " +
|
|
|
+ "receiver_name VARCHAR(32) NOT NULL COMMENT '收件人姓名', " +
|
|
|
+ "receiver_phone VARCHAR(16) NOT NULL COMMENT '收件人电话', " +
|
|
|
+ "receiver_address VARCHAR(256) NOT NULL COMMENT '收件人地址', " +
|
|
|
+ "status VARCHAR(16) NOT NULL DEFAULT 'pending' COMMENT 'pending/shipped/signing/completed', " +
|
|
|
+ "tracking_data TEXT DEFAULT NULL COMMENT '物流轨迹(JSON数组)', " +
|
|
|
+ "shipped_at DATETIME DEFAULT NULL COMMENT '发货时间', " +
|
|
|
+ "signed_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, " +
|
|
|
+ "INDEX idx_order (order_id), " +
|
|
|
+ "INDEX idx_logistics_no (logistics_no), " +
|
|
|
+ "INDEX idx_status (status)" +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='物流信息表'");
|
|
|
+ log.info("已创建 danshop_logistics 表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建 danshop_logistics 表失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 7. danshop_profit_rules (分润规则配置表)
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS danshop_profit_rules (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
+ "rule_name VARCHAR(64) NOT NULL COMMENT '规则名称', " +
|
|
|
+ "rule_type VARCHAR(32) NOT NULL COMMENT '规则类型: product_category/vendor_level/global', " +
|
|
|
+ "target_id BIGINT DEFAULT NULL COMMENT '关联目标ID', " +
|
|
|
+ "platform_profit_rate INT NOT NULL COMMENT '平台利润率(bps,100=1%)', " +
|
|
|
+ "referrer_l1_rate INT DEFAULT 0 COMMENT '直接推荐人分润比例(bps,占利润%)', " +
|
|
|
+ "referrer_l2_rate INT DEFAULT 0 COMMENT '间接推荐人分润比例(bps,占利润%)', " +
|
|
|
+ "platform_service_rate INT DEFAULT 0 COMMENT '平台服务费率(bps,占订单金额%)', " +
|
|
|
+ "is_active INT DEFAULT 1 COMMENT '1启用/0禁用', " +
|
|
|
+ "created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
+ "updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, " +
|
|
|
+ "INDEX idx_type (rule_type, target_id), " +
|
|
|
+ "INDEX idx_active (is_active)" +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='分润规则配置表'");
|
|
|
+ log.info("已创建 danshop_profit_rules 表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建 danshop_profit_rules 表失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 8. danshop_profit_logs (分润流水表)
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS danshop_profit_logs (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
+ "order_id BIGINT NOT NULL COMMENT '订单ID', " +
|
|
|
+ "order_no VARCHAR(32) NOT NULL COMMENT '订单号', " +
|
|
|
+ "order_amount INT NOT NULL COMMENT '订单实付金额(分)', " +
|
|
|
+ "cost_amount INT DEFAULT 0 COMMENT '成本金额(分)', " +
|
|
|
+ "profit_amount INT NOT NULL COMMENT '利润金额(分)', " +
|
|
|
+ "platform_retained INT NOT NULL COMMENT '平台留存金额(分)', " +
|
|
|
+ "vendor_income INT NOT NULL COMMENT '供应商实收金额(分)', " +
|
|
|
+ "commission_l1 INT DEFAULT 0 COMMENT 'L1推荐人佣金(分)', " +
|
|
|
+ "commission_l2 INT DEFAULT 0 COMMENT 'L2推荐人佣金(分)', " +
|
|
|
+ "rule_id BIGINT DEFAULT NULL COMMENT '使用的分润规则ID', " +
|
|
|
+ "created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
+ "INDEX idx_order (order_id), " +
|
|
|
+ "INDEX idx_order_no (order_no)" +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='分润流水表'");
|
|
|
+ log.info("已创建 danshop_profit_logs 表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建 danshop_profit_logs 表失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 9. danshop_favorites (商品收藏表)
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS danshop_favorites (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
+ "user_id BIGINT NOT NULL COMMENT '用户ID', " +
|
|
|
+ "product_id BIGINT NOT NULL COMMENT '商品ID', " +
|
|
|
+ "created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
+ "UNIQUE INDEX idx_user_product (user_id, product_id), " +
|
|
|
+ "INDEX idx_user (user_id)" +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品收藏表'");
|
|
|
+ log.info("已创建 danshop_favorites 表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建 danshop_favorites 表失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 10. danshop_addresses (收货地址表)
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS danshop_addresses (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
+ "user_id BIGINT NOT NULL COMMENT '用户ID', " +
|
|
|
+ "receiver_name VARCHAR(32) NOT NULL COMMENT '收货人姓名', " +
|
|
|
+ "phone VARCHAR(16) NOT NULL COMMENT '收货人电话', " +
|
|
|
+ "province VARCHAR(32) NOT NULL COMMENT '省份', " +
|
|
|
+ "city VARCHAR(32) NOT NULL COMMENT '城市', " +
|
|
|
+ "district VARCHAR(32) NOT NULL COMMENT '区县', " +
|
|
|
+ "street VARCHAR(256) NOT NULL COMMENT '详细地址', " +
|
|
|
+ "is_default INT DEFAULT 0 COMMENT '是否默认地址 1=是 0=否', " +
|
|
|
+ "created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
+ "updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, " +
|
|
|
+ "INDEX idx_user (user_id), " +
|
|
|
+ "INDEX idx_user_default (user_id, is_default)" +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='收货地址表'");
|
|
|
+ log.info("已创建 danshop_addresses 表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建 danshop_addresses 表失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== 迁移: activities表添加visible_scope列 ====================
|
|
|
+ try {
|
|
|
+ Integer colExists = jdbcTemplate.queryForObject(
|
|
|
+ "SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'activities' AND COLUMN_NAME = 'visible_scope'",
|
|
|
+ Integer.class);
|
|
|
+ if (colExists == null || colExists == 0) {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE activities ADD COLUMN visible_scope VARCHAR(20) DEFAULT NULL COMMENT '可见范围: family(家庭)/role(角色)/user(用户)' AFTER visibility");
|
|
|
+ log.info("已添加visible_scope列到activities表");
|
|
|
+ } else {
|
|
|
+ log.info("activities表visible_scope列已存在,跳过");
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("检查/添加activities表visible_scope列失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== 迁移: articles表添加article_type列 ====================
|
|
|
+ try {
|
|
|
+ Integer colExists = jdbcTemplate.queryForObject(
|
|
|
+ "SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'articles' AND COLUMN_NAME = 'article_type'",
|
|
|
+ Integer.class);
|
|
|
+ if (colExists == null || colExists == 0) {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE articles ADD COLUMN article_type VARCHAR(20) DEFAULT 'normal' COMMENT '文章类型: normal(普通)/premium(优质)/original(原创)' AFTER read_time");
|
|
|
+ log.info("已添加article_type列到articles表");
|
|
|
+ } else {
|
|
|
+ log.info("articles表article_type列已存在,跳过");
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("检查/添加articles表article_type列失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== 迁移: articles表添加word_count列 ====================
|
|
|
+ try {
|
|
|
+ Integer colExists = jdbcTemplate.queryForObject(
|
|
|
+ "SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'articles' AND COLUMN_NAME = 'word_count'",
|
|
|
+ Integer.class);
|
|
|
+ if (colExists == null || colExists == 0) {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE articles ADD COLUMN word_count INT DEFAULT 0 COMMENT '文章字数' AFTER article_type");
|
|
|
+ log.info("已添加word_count列到articles表");
|
|
|
+ } else {
|
|
|
+ log.info("articles表word_count列已存在,跳过");
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("检查/添加articles表word_count列失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== 迁移: article_reading_records表添加task_id列 ====================
|
|
|
+ try {
|
|
|
+ Integer colExists = jdbcTemplate.queryForObject(
|
|
|
+ "SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'article_reading_records' AND COLUMN_NAME = 'task_id'",
|
|
|
+ Integer.class);
|
|
|
+ if (colExists == null || colExists == 0) {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE article_reading_records ADD COLUMN task_id BIGINT DEFAULT NULL COMMENT '关联的任务ID' AFTER duration_seconds");
|
|
|
+ log.info("已添加task_id列到article_reading_records表");
|
|
|
+ } else {
|
|
|
+ log.info("article_reading_records表task_id列已存在,跳过");
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("检查/添加article_reading_records表task_id列失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== 迁移: tasks表添加source_type列 ====================
|
|
|
+ try {
|
|
|
+ Integer colExists = jdbcTemplate.queryForObject(
|
|
|
+ "SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'tasks' AND COLUMN_NAME = 'source_type'",
|
|
|
+ Integer.class);
|
|
|
+ if (colExists == null || colExists == 0) {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE tasks ADD COLUMN source_type VARCHAR(20) DEFAULT NULL COMMENT '来源类型: article/activity/manual' AFTER minigame_code");
|
|
|
+ log.info("已添加source_type列到tasks表");
|
|
|
+ } else {
|
|
|
+ log.info("tasks表source_type列已存在,跳过");
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("检查/添加tasks表source_type列失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== 迁移: tasks表添加source_id列 ====================
|
|
|
+ try {
|
|
|
+ Integer colExists = jdbcTemplate.queryForObject(
|
|
|
+ "SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'tasks' AND COLUMN_NAME = 'source_id'",
|
|
|
+ Integer.class);
|
|
|
+ if (colExists == null || colExists == 0) {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE tasks ADD COLUMN source_id BIGINT DEFAULT NULL COMMENT '来源ID' AFTER source_type");
|
|
|
+ log.info("已添加source_id列到tasks表");
|
|
|
+ } else {
|
|
|
+ log.info("tasks表source_id列已存在,跳过");
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("检查/添加tasks表source_id列失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== 迁移: activities表添加checkin_points列 ====================
|
|
|
+ try {
|
|
|
+ Integer colExists = jdbcTemplate.queryForObject(
|
|
|
+ "SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'activities' AND COLUMN_NAME = 'checkin_points'",
|
|
|
+ Integer.class);
|
|
|
+ if (colExists == null || colExists == 0) {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE activities ADD COLUMN checkin_points INT DEFAULT 0 COMMENT '签到积分(0=使用系统默认)' AFTER price");
|
|
|
+ log.info("已添加checkin_points列到activities表");
|
|
|
+ } else {
|
|
|
+ log.info("activities表checkin_points列已存在,跳过");
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("检查/添加activities表checkin_points列失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 迁移: 创建family_invitations表(家庭邀请令牌)
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS family_invitations (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
+ "family_id BIGINT NOT NULL COMMENT '家庭ID', " +
|
|
|
+ "token VARCHAR(64) NOT NULL UNIQUE COMMENT '邀请令牌(UUID)', " +
|
|
|
+ "created_by BIGINT NOT NULL COMMENT '创建人用户ID', " +
|
|
|
+ "status VARCHAR(16) DEFAULT 'active' COMMENT '状态: active/used/expired/revoked', " +
|
|
|
+ "expires_at DATETIME 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_token (token), " +
|
|
|
+ "INDEX idx_status (status)" +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='家庭邀请令牌'");
|
|
|
+ log.info("已创建family_invitations表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建family_invitations表失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== P0 营养师: nutrition_indicator_mapping ====================
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS nutrition_indicator_mapping (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY," +
|
|
|
+ "indicator_name_pattern VARCHAR(200) NOT NULL COMMENT '指标名匹配模式(SQL LIKE语法,%为通配符)'," +
|
|
|
+ "nutrient VARCHAR(50) NOT NULL COMMENT '关联营养素名'," +
|
|
|
+ "severity_weight DECIMAL(3,2) DEFAULT 1.00 COMMENT '权重(0-1)'," +
|
|
|
+ "deficiency_symptoms TEXT COMMENT '缺乏症状描述'," +
|
|
|
+ "excess_symptoms TEXT COMMENT '过量症状描述'," +
|
|
|
+ "food_suggestions TEXT COMMENT '推荐食物(逗号分隔)'," +
|
|
|
+ "sort_order INT DEFAULT 0 COMMENT '排序(大值优先匹配)'," +
|
|
|
+ "created_at DATETIME DEFAULT CURRENT_TIMESTAMP," +
|
|
|
+ "updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP," +
|
|
|
+ "INDEX idx_nutrient (nutrient)" +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='指标-营养素映射表'");
|
|
|
+ log.info("已创建 nutrition_indicator_mapping 表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建 nutrition_indicator_mapping 表失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // P0 营养师: nutrition_deficiency_record
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS nutrition_deficiency_record (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY," +
|
|
|
+ "child_id BIGINT NOT NULL COMMENT '孩子ID'," +
|
|
|
+ "report_id BIGINT NOT NULL COMMENT '关联报告ID'," +
|
|
|
+ "nutrient VARCHAR(50) NOT NULL COMMENT '营养素名'," +
|
|
|
+ "deficiency_type VARCHAR(20) NOT NULL COMMENT 'deficiency/excess/borderline/unclassified'," +
|
|
|
+ "severity DECIMAL(5,2) DEFAULT 0 COMMENT '严重程度(0-1)'," +
|
|
|
+ "indicator_name VARCHAR(100) COMMENT '关联指标名'," +
|
|
|
+ "indicator_value VARCHAR(50) COMMENT '指标检测值'," +
|
|
|
+ "ref_range VARCHAR(100) COMMENT '参考范围'," +
|
|
|
+ "symptoms TEXT COMMENT '症状描述'," +
|
|
|
+ "food_suggestions TEXT COMMENT '推荐食物'," +
|
|
|
+ "created_at DATETIME DEFAULT CURRENT_TIMESTAMP," +
|
|
|
+ "INDEX idx_child (child_id)," +
|
|
|
+ "INDEX idx_report (report_id)," +
|
|
|
+ "INDEX idx_nutrient (nutrient)" +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='营养缺乏分析记录'");
|
|
|
+ log.info("已创建 nutrition_deficiency_record 表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建 nutrition_deficiency_record 表失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // P0 营养师: 指标-营养素映射 种子数据
|
|
|
+ try {
|
|
|
+ Integer count = jdbcTemplate.queryForObject(
|
|
|
+ "SELECT COUNT(*) FROM nutrition_indicator_mapping", Integer.class);
|
|
|
+ if (count == null || count == 0) {
|
|
|
+ String[][] seedData = {
|
|
|
+ {"铁蛋白", "铁", "1.00", "乏力、面色苍白、注意力不集中", "铁过量可能损伤肝脏", "动物肝脏,红肉,菠菜,黑木耳", "10"},
|
|
|
+ {"25-羟基维生素D", "维生素D", "1.00", "影响钙吸收、骨骼发育", "维生素D过量可致高钙血症", "三文鱼,蛋黄,蘑菇,强化牛奶", "20"},
|
|
|
+ {"维生素D", "维生素D", "1.00", "影响钙吸收、骨骼发育、免疫力下降", "维生素D过量可致高钙血症", "三文鱼,蛋黄,蘑菇,强化牛奶", "15"},
|
|
|
+ {"锌", "锌", "0.80", "食欲减退、味觉异常、免疫力降低", "锌过量影响铜吸收", "牡蛎,牛肉,南瓜子,扁豆", "10"},
|
|
|
+ {"钙", "钙", "0.80", "骨骼发育迟缓、牙齿问题", "高钙血症、肾结石", "牛奶,豆腐,虾皮,芝麻酱", "10"},
|
|
|
+ {"维生素B12", "维生素B12", "1.00", "贫血、神经发育迟缓", "一般不易过量", "牛肉,鸡蛋,牛奶,鱼肉", "10"},
|
|
|
+ {"维生素C", "维生素C", "0.60", "免疫力下降、牙龈出血", "腹泻、肾结石风险", "猕猴桃,橙子,草莓,青椒", "10"},
|
|
|
+ {"白蛋白", "蛋白质", "0.90", "发育迟缓、肌肉萎缩", "肾脏负担", "鸡蛋,鱼肉,牛奶,豆制品", "10"},
|
|
|
+ {"总蛋白", "蛋白质", "0.90", "营养不良、免疫力下降", "血液粘稠", "鸡蛋,鱼肉,牛奶,豆制品", "9"},
|
|
|
+ {"膳食纤维", "膳食纤维", "0.50", "便秘、肠道菌群失衡", "腹胀、排便过多", "全谷物,蔬菜,豆类,水果", "10"},
|
|
|
+ {"叶酸", "叶酸", "0.70", "贫血、发育迟缓", "一般不易过量", "菠菜,动物肝脏,豆类,柑橘", "10"},
|
|
|
+ {"维生素A", "维生素A", "0.70", "视力问题、皮肤干燥、免疫力下降", "头痛、肝脏损伤", "胡萝卜,动物肝脏,南瓜,菠菜", "10"},
|
|
|
+ {"硒", "硒", "0.50", "免疫力下降、甲状腺功能异常", "脱发、指甲脆裂", "巴西坚果,海鲜,蘑菇", "10"},
|
|
|
+ {"镁", "镁", "0.40", "肌肉痉挛、睡眠问题", "腹泻、低血压", "坚果,全谷物,深绿蔬菜", "10"},
|
|
|
+ {"益生菌%", "益生菌", "0.60", "肠道菌群失衡、消化问题", "一般不易过量", "酸奶,泡菜,味噌,康普茶", "10"}
|
|
|
+ };
|
|
|
+ for (String[] row : seedData) {
|
|
|
+ jdbcTemplate.update("INSERT IGNORE INTO nutrition_indicator_mapping " +
|
|
|
+ "(indicator_name_pattern, nutrient, severity_weight, deficiency_symptoms, excess_symptoms, food_suggestions, sort_order) " +
|
|
|
+ "VALUES (?, ?, ?, ?, ?, ?, ?)",
|
|
|
+ row[0], row[1], new java.math.BigDecimal(row[2]), row[3], row[4], row[5], Integer.parseInt(row[6]));
|
|
|
+ }
|
|
|
+ log.info("nutrition_indicator_mapping 种子数据已加载 (15条)");
|
|
|
+ } else {
|
|
|
+ log.info("nutrition_indicator_mapping 种子数据已存在 ({}条), 跳过", count);
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("nutrition_indicator_mapping 种子数据初始化失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== 心版块 Phase 1: emotion_checkin 表 ====================
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS emotion_checkin (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
+ "child_id BIGINT NOT NULL COMMENT '孩子ID', " +
|
|
|
+ "user_id BIGINT NOT NULL COMMENT '打卡人用户ID', " +
|
|
|
+ "emotion_type INT NOT NULL COMMENT '情绪类型: 1快乐/2平和平静/3平静/4有点不舒服/5难受/6烦躁/7生气/8焦虑/9恐惧', " +
|
|
|
+ "emotion_name VARCHAR(20) COMMENT '情绪名称: happy/calm/peaceful/uncomfortable/sad/irritable/angry/anxious/fearful', " +
|
|
|
+ "energy_level INT NOT NULL DEFAULT 3 COMMENT '能量状态: 1很低/2低/3中等/4高/5很高', " +
|
|
|
+ "note TEXT COMMENT '打卡备注', " +
|
|
|
+ "photo_url VARCHAR(500) COMMENT '照片URL', " +
|
|
|
+ "favorite_thing VARCHAR(500) COMMENT '今日最喜欢的一件事', " +
|
|
|
+ "points_awarded INT DEFAULT 0 COMMENT '奖励积分', " +
|
|
|
+ "parent_visible TINYINT DEFAULT 1 COMMENT '家长可见: 1可见/0不可见', " +
|
|
|
+ "checkin_date DATE NOT NULL COMMENT '打卡日期', " +
|
|
|
+ "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
+ "updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, " +
|
|
|
+ "INDEX idx_child_id (child_id), " +
|
|
|
+ "INDEX idx_user_id (user_id), " +
|
|
|
+ "INDEX idx_checkin_date (checkin_date), " +
|
|
|
+ "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());
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== 1a-2: DAN Schema 修复 ====================
|
|
|
+ // v1.2 EMI 心理子维度
|
|
|
+ String[] danEmiFields = {"emotion_score", "resilience_score", "stress_coping_score", "self_awareness_score"};
|
|
|
+ for (String field : danEmiFields) {
|
|
|
+ try {
|
|
|
+ Integer exists = jdbcTemplate.queryForObject(
|
|
|
+ "SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'dan_assessment_results' AND COLUMN_NAME = ?",
|
|
|
+ Integer.class, field);
|
|
|
+ if (exists == null || exists == 0) {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE dan_assessment_results ADD COLUMN " + field + " INT DEFAULT NULL COMMENT 'EMI子维度 0-100'");
|
|
|
+ log.info("已添加 {} 列到 dan_assessment_results 表", field);
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("检查/添加 {} 列失败: {}", field, e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 大五人格维度
|
|
|
+ String[] danBigFiveFields = {"openness_score", "conscientiousness_score", "extraversion_score", "agreeableness_score", "neuroticism_score", "big_five_overall_score"};
|
|
|
+ for (String field : danBigFiveFields) {
|
|
|
+ try {
|
|
|
+ Integer exists = jdbcTemplate.queryForObject(
|
|
|
+ "SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'dan_assessment_results' AND COLUMN_NAME = ?",
|
|
|
+ Integer.class, field);
|
|
|
+ if (exists == null || exists == 0) {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE dan_assessment_results ADD COLUMN " + field + " INT DEFAULT NULL COMMENT '大五人格 0-100'");
|
|
|
+ log.info("已添加 {} 列到 dan_assessment_results 表", field);
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("检查/添加 {} 列失败: {}", field, e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // EMI 维度(情绪商数评估字段)
|
|
|
+ String[] danEmiDimFields = {"emotion_management_score", "empathy_score", "social_adaptability_score", "self_motivation_score"};
|
|
|
+ for (String field : danEmiDimFields) {
|
|
|
+ try {
|
|
|
+ Integer exists = jdbcTemplate.queryForObject(
|
|
|
+ "SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'dan_assessment_results' AND COLUMN_NAME = ?",
|
|
|
+ Integer.class, field);
|
|
|
+ if (exists == null || exists == 0) {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE dan_assessment_results ADD COLUMN " + field + " INT DEFAULT NULL COMMENT 'EMI维度 0-100'");
|
|
|
+ log.info("已添加 {} 列到 dan_assessment_results 表", field);
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("检查/添加 {} 列失败: {}", field, e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== 1b-1: bazi_reading_template 五行命理解读表 ====================
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS bazi_reading_template (" +
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
+ "element VARCHAR(20) NOT NULL COMMENT '五行: wood/fire/earth/metal/water', " +
|
|
|
+ "condition VARCHAR(20) NOT NULL COMMENT '状态: strong/weak/balanced', " +
|
|
|
+ "personal_trait TEXT COMMENT '性格特质描述', " +
|
|
|
+ "mind_growth_advice TEXT COMMENT '心理成长建议', " +
|
|
|
+ "daily_tip_template TEXT COMMENT '每日宜忌模板', " +
|
|
|
+ "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
|
|
|
+ "UNIQUE KEY uk_element_condition (element, condition)" +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='五行命理解读模板'");
|
|
|
+ log.info("已创建 bazi_reading_template 表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建 bazi_reading_template 表失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== 1b-1: numsoul_config 生命灵数配置表 ====================
|
|
|
+ try {
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS numsoul_config (" +
|
|
|
+ "number INT PRIMARY KEY COMMENT '灵数1-9', " +
|
|
|
+ "title VARCHAR(50) NOT NULL COMMENT '称号', " +
|
|
|
+ "keywords VARCHAR(200) COMMENT '性格关键词', " +
|
|
|
+ "mind_growth_advice TEXT COMMENT '心理成长建议', " +
|
|
|
+ "recommended_article_tags VARCHAR(200) COMMENT '关联文章标签', " +
|
|
|
+ "color_hex VARCHAR(7) COMMENT '代表色' " +
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='生命灵数配置'");
|
|
|
+ log.info("已创建 numsoul_config 表");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("创建 numsoul_config 表失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== 1b-1: bazi_reading_template 种子数据 ====================
|
|
|
+ try {
|
|
|
+ Integer count = jdbcTemplate.queryForObject(
|
|
|
+ "SELECT COUNT(*) FROM bazi_reading_template", Integer.class);
|
|
|
+ if (count == null || count == 0) {
|
|
|
+ jdbcTemplate.batchUpdate(
|
|
|
+ "INSERT INTO bazi_reading_template (element, condition, personal_trait, mind_growth_advice, daily_tip_template) VALUES (?, ?, ?, ?, ?)",
|
|
|
+ java.util.Arrays.asList(new Object[][]{
|
|
|
+ {"wood", "strong", "性格坚韧、有进取心,具有领导力和开拓精神", "学会适当放松,避免过刚易折,培养弹性思维", "宜进行伸展运动,忌高强度冲突"},
|
|
|
+ {"wood", "weak", "温和有礼、善于合作,但决策时容易犹豫", "增强自信心训练,培养果断决策能力", "宜与自然相处,忌过度迁就他人"},
|
|
|
+ {"wood", "balanced", "性格中庸有度,既有韧性又有灵活性", "保持现状,适当拓展社交圈", "宜户外活动,忌久坐不动"},
|
|
|
+ {"fire", "strong", "热情开朗、充满活力,富有感染力和创造力", "注意情绪管理,避免急躁冲动", "宜冷水刺激冷静,忌暴怒争执"},
|
|
|
+ {"fire", "weak", "内敛含蓄、心思细腻,对艺术有敏锐感知", "适当表达情感,培养社交自信", "宜温暖陪伴,忌独处过久"},
|
|
|
+ {"fire", "balanced", "性格温润有度,既有热情又懂克制", "保持兴趣爱好,适度拓展表达", "宜参与社交活动,忌情绪压抑"},
|
|
|
+ {"earth", "strong", "稳重可靠、有责任心,是团队中的中流砥柱", "避免过度承担,学会说'不'", "宜适量运动,忌思虑过重"},
|
|
|
+ {"earth", "weak", "随和友善、适应性强,容易与他人相处", "增强主见性,培养独立思考能力", "宜接触新事物,忌固守不变"},
|
|
|
+ {"earth", "balanced", "性情敦厚、处事稳妥,人际关系和谐", "保持学习,适当挑战自我", "宜亲近自然,忌暴饮暴食"},
|
|
|
+ {"metal", "strong", "意志坚定、追求完美,具有卓越的执行力", "学会灵活变通,避免过于严苛", "宜艺术鉴赏,忌过度批评"},
|
|
|
+ {"metal", "weak", "温和寡言、善于倾听,是值得信赖的朋友", "增强表达勇气,培养自信心", "宜团体活动,忌过度自我封闭"},
|
|
|
+ {"metal", "balanced", "为人正直、做事有度,不乏柔韧与包容", "保持开放心态,适度多元发展", "宜阅读学习,忌钻牛角尖"},
|
|
|
+ {"water", "strong", "智慧深沉、善于谋略,具有出色洞察力", "避免过度缜密,适当放松心情", "宜静坐冥想,忌思虑过深"},
|
|
|
+ {"water", "weak", "敏感细腻、有同理心,富有创作天赋", "增强抵抗力,培养情绪韧性", "宜温暖环境,忌受寒受凉"},
|
|
|
+ {"water", "balanced", "性格圆融、随机应变,处事灵活得宜", "保持好奇心,适度扩展影响力", "宜游泳泡澡,忌情绪波动"},
|
|
|
+ })
|
|
|
+ );
|
|
|
+ log.info("bazi_reading_template 种子数据已加载 (15条)");
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("bazi_reading_template 种子数据初始化失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== 1b-1: numsoul_config 种子数据 ====================
|
|
|
+ try {
|
|
|
+ Integer count = jdbcTemplate.queryForObject(
|
|
|
+ "SELECT COUNT(*) FROM numsoul_config", Integer.class);
|
|
|
+ if (count == null || count == 0) {
|
|
|
+ jdbcTemplate.batchUpdate(
|
|
|
+ "INSERT INTO numsoul_config (number, title, keywords, mind_growth_advice, recommended_article_tags, color_hex) VALUES (?, ?, ?, ?, ?, ?)",
|
|
|
+ java.util.Arrays.asList(new Object[][]{
|
|
|
+ {1, "开创者", "独立、领导、创新、自信", "培养团队协作意识,避免独断专行", "领导力,自信培养", "#FF4444"},
|
|
|
+ {2, "和平者", "温和、合作、敏感、耐心", "增强自我主张,避免过度迎合他人", "人际交往,情绪表达", "#FF8C00"},
|
|
|
+ {3, "表达者", "创意、社交、乐观、表达", "保持专注,避免浅尝辄止", "创造力,社交能力", "#FFD700"},
|
|
|
+ {4, "建设者", "务实、稳定、有序、勤奋", "学会变通,避免过于固执", "专注力,学习习惯", "#4CAF50"},
|
|
|
+ {5, "自由者", "冒险、变化、好奇、灵活", "培养持久力,避免缺乏恒心", "探索精神,适应能力", "#2196F3"},
|
|
|
+ {6, "关怀者", "责任、关爱、奉献、平衡", "学会自我关爱,避免过度付出", "家庭关系,责任心", "#9C27B0"},
|
|
|
+ {7, "探索者", "智慧、分析、内省、完美", "与人分享见解,避免过度封闭", "思维能力,学习力", "#673AB7"},
|
|
|
+ {8, "成就者", "雄心、管理、决策、权威", "关注内在成长,避免过于追求外在成就", "目标管理,理财思维", "#3F51B5"},
|
|
|
+ {9, "博爱者", "包容、智慧、善良、豁达", "设定个人边界,避免过度牺牲", "人际关系,情绪管理", "#E91E63"},
|
|
|
+ })
|
|
|
+ );
|
|
|
+ log.info("numsoul_config 种子数据已加载 (9条)");
|
|
|
+ }
|
|
|
+} catch (Exception e) {
|
|
|
+ log.warn("numsoul_config 种子数据初始化失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ 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());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ 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());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ 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);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|