Просмотр исходного кода

fix: 修复数据库迁移重复执行错误

- 移除schema.sql中的ALTER TABLE语句
- 在DatabaseInitializer中添加runMigrations()方法
- 使用try-catch处理列/索引已存在的情况
- 支持幂等性迁移(可重复执行)
- 添加tasks表executor_type/executor_id字段
- 添加users表roles/total_points等新字段
- 添加children表birthday/gender字段
- 支持teacher角色
User 5 месяцев назад
Родитель
Сommit
fa187e9a99

+ 122 - 5
zxyj-backend/src/main/java/com/zxyj/config/DatabaseInitializer.java

@@ -20,16 +20,19 @@ public class DatabaseInitializer implements CommandLineRunner {
     @Override
     @Override
     public void run(String... args) throws Exception {
     public void run(String... args) throws Exception {
         log.info("开始初始化数据库...");
         log.info("开始初始化数据库...");
-        
+
         // 1. 检查并创建数据库
         // 1. 检查并创建数据库
         initializeDatabase();
         initializeDatabase();
-        
+
         // 2. 检查并创建表
         // 2. 检查并创建表
         initializeTables();
         initializeTables();
-        
-        // 3. 初始化默认数据
+
+        // 3. 执行数据库迁移(添加新列)
+        runMigrations();
+
+        // 4. 初始化默认数据
         initializeDefaultData();
         initializeDefaultData();
-        
+
         log.info("数据库初始化完成!");
         log.info("数据库初始化完成!");
     }
     }
 
 
@@ -342,6 +345,120 @@ public class DatabaseInitializer implements CommandLineRunner {
         log.info("数据表检查完成");
         log.info("数据表检查完成");
     }
     }
 
 
+    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) {
+            // 修改失败,忽略错误
+        }
+
+        log.info("数据库迁移完成");
+    }
+
     private void initializeDefaultData() {
     private void initializeDefaultData() {
         // 初始化预设奖励模板
         // 初始化预设奖励模板
         try {
         try {

+ 360 - 4
zxyj-backend/src/main/resources/schema.sql

@@ -17,7 +17,7 @@ CREATE TABLE IF NOT EXISTS users (
     openid VARCHAR(64) NOT NULL,
     openid VARCHAR(64) NOT NULL,
     unionid VARCHAR(64),
     unionid VARCHAR(64),
     family_id BIGINT NOT NULL,
     family_id BIGINT NOT NULL,
-    role ENUM('parent','child') NOT NULL,
+    role ENUM('parent','child','teacher') NOT NULL,
     nickname VARCHAR(50),
     nickname VARCHAR(50),
     avatar VARCHAR(255),
     avatar VARCHAR(255),
     password VARCHAR(128),
     password VARCHAR(128),
@@ -134,7 +134,7 @@ CREATE TABLE IF NOT EXISTS reward_categories (
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
 
 
 -- 预设奖励模板数据
 -- 预设奖励模板数据
-INSERT INTO rewards (family_id, child_id, title, points_required, category, is_template) VALUES
+INSERT IGNORE INTO rewards (family_id, child_id, title, points_required, category, is_template) VALUES
 (0, 0, '看动画片30分钟', 30, '精神奖励', 1),
 (0, 0, '看动画片30分钟', 30, '精神奖励', 1),
 (0, 0, '玩平板游戏30分钟', 40, '特权奖励', 1),
 (0, 0, '玩平板游戏30分钟', 40, '特权奖励', 1),
 (0, 0, '去游乐场', 100, '物质奖励', 1),
 (0, 0, '去游乐场', 100, '物质奖励', 1),
@@ -143,7 +143,7 @@ INSERT INTO rewards (family_id, child_id, title, points_required, category, is_t
 (0, 0, '选择晚餐', 25, '特权奖励', 1);
 (0, 0, '选择晚餐', 25, '特权奖励', 1);
 
 
 -- 默认奖励分类
 -- 默认奖励分类
-INSERT INTO reward_categories (family_id, name, icon, sort_order) VALUES
+INSERT IGNORE INTO reward_categories (family_id, name, icon, sort_order) VALUES
 (0, '物质奖励', 'gift', 1),
 (0, '物质奖励', 'gift', 1),
 (0, '精神奖励', 'star', 2),
 (0, '精神奖励', 'star', 2),
 (0, '特权奖励', 'crown', 3);
 (0, '特权奖励', 'crown', 3);
@@ -208,7 +208,7 @@ CREATE TABLE IF NOT EXISTS streak_milestones (
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
 
 
 -- 插入打卡里程碑数据
 -- 插入打卡里程碑数据
-INSERT INTO streak_milestones (days, reward_points) VALUES
+INSERT IGNORE INTO streak_milestones (days, reward_points) VALUES
 (3, 5),
 (3, 5),
 (7, 10),
 (7, 10),
 (30, 30),
 (30, 30),
@@ -283,3 +283,359 @@ INDEX idx_type (type),
 FOREIGN KEY (task_id) REFERENCES tasks(id) ON DELETE CASCADE,
 FOREIGN KEY (task_id) REFERENCES tasks(id) ON DELETE CASCADE,
 FOREIGN KEY (creator_id) REFERENCES users(id) ON DELETE CASCADE
 FOREIGN KEY (creator_id) REFERENCES users(id) ON DELETE CASCADE
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+-- =============================================
+-- 老师/双培指导师相关表
+-- =============================================
+
+-- 老师信息表
+CREATE TABLE IF NOT EXISTS teachers (
+    id BIGINT AUTO_INCREMENT PRIMARY KEY,
+    user_id BIGINT NOT NULL COMMENT '关联users表ID',
+    teacher_no VARCHAR(20) UNIQUE COMMENT '指导师编号',
+    title VARCHAR(50) COMMENT '职称/称号',
+    specialty VARCHAR(200) COMMENT '专长领域',
+    bio TEXT COMMENT '简介',
+    experience_years TINYINT DEFAULT 0 COMMENT '从业年限',
+    rating DECIMAL(3,2) DEFAULT 5.00 COMMENT '评分',
+    total_appointments INT DEFAULT 0 COMMENT '总测评次数',
+    status ENUM('active', 'inactive', 'suspended') DEFAULT 'active',
+    verification_status ENUM('pending', 'approved', 'rejected') DEFAULT 'pending',
+    verified_at DATETIME,
+    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
+    updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+    INDEX idx_user_id (user_id),
+    INDEX idx_teacher_no (teacher_no),
+    INDEX idx_status (status)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+-- 测评预约表
+CREATE TABLE IF NOT EXISTS assessment_appointments (
+    id BIGINT AUTO_INCREMENT PRIMARY KEY,
+    teacher_id BIGINT NOT NULL,
+    family_id BIGINT NOT NULL,
+    child_id BIGINT NOT NULL,
+    appointment_date DATE NOT NULL,
+    appointment_time TIME NOT NULL,
+    duration_minutes TINYINT DEFAULT 60 COMMENT '测评时长',
+    status ENUM('pending', 'confirmed', 'completed', 'cancelled', 'no_show') DEFAULT 'pending',
+    appointment_type ENUM('initial', 'follow_up', 'review') DEFAULT 'initial' COMMENT '初测/复测/回访',
+    notes TEXT COMMENT '备注',
+    contact_phone VARCHAR(20),
+    meet_link VARCHAR(255) COMMENT '会议链接',
+    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
+    updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+    INDEX idx_teacher_id (teacher_id),
+    INDEX idx_family_id (family_id),
+    INDEX idx_child_id (child_id),
+    INDEX idx_appointment_date (appointment_date),
+    INDEX idx_status (status)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+-- DAN测评结果表
+CREATE TABLE IF NOT EXISTS dan_assessment_results (
+    id BIGINT AUTO_INCREMENT PRIMARY KEY,
+    child_id BIGINT NOT NULL,
+    teacher_id BIGINT NOT NULL,
+    appointment_id BIGINT,
+    dan_level VARCHAR(2) NOT NULL COMMENT 'DAN等级 A/B/C/D',
+    attention_score INT COMMENT '注意力得分',
+    focus_score INT COMMENT '专注力得分',
+    memory_score INT COMMENT '记忆力得分',
+    logic_score INT COMMENT '逻辑思维得分',
+    overall_score INT COMMENT '综合得分',
+    assessment_date DATE NOT NULL,
+    duration_minutes INT COMMENT '测评时长',
+    game_records JSON COMMENT '游戏记录详情',
+    analysis_report TEXT COMMENT '分析报告',
+    growth_suggestions TEXT COMMENT '成长建议',
+    next_review_date DATE COMMENT '下次复测日期',
+    status ENUM('draft', 'completed', 'archived') DEFAULT 'draft',
+    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
+    updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+    INDEX idx_child_id (child_id),
+    INDEX idx_teacher_id (teacher_id),
+    INDEX idx_dan_level (dan_level),
+    INDEX idx_assessment_date (assessment_date)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+-- 成长规划表
+CREATE TABLE IF NOT EXISTS growth_plans (
+    id BIGINT AUTO_INCREMENT PRIMARY KEY,
+    child_id BIGINT NOT NULL,
+    teacher_id BIGINT NOT NULL,
+    assessment_id BIGINT,
+    plan_title VARCHAR(200) NOT NULL,
+    plan_content TEXT NOT NULL COMMENT '规划内容',
+    target_dan_level VARCHAR(2) COMMENT '目标DAN等级',
+    duration_months TINYINT COMMENT '规划周期(月)',
+    status ENUM('draft', 'active', 'completed', 'expired') DEFAULT 'draft',
+    start_date DATE,
+    end_date DATE,
+    review_count INT DEFAULT 0 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_teacher_id (teacher_id),
+    INDEX idx_status (status)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+-- 任务模板推荐表(老师推荐给家长)
+CREATE TABLE IF NOT EXISTS teacher_task_recommendations (
+    id BIGINT AUTO_INCREMENT PRIMARY KEY,
+    teacher_id BIGINT NOT NULL,
+    family_id BIGINT NOT NULL,
+    child_id BIGINT,
+    task_template_id BIGINT,
+    title VARCHAR(200) NOT NULL,
+    description TEXT,
+    points TINYINT DEFAULT 2,
+    duration_minutes TINYINT,
+    category VARCHAR(32),
+    reason TEXT COMMENT '推荐理由',
+    status ENUM('pending', 'accepted', 'rejected', 'expired') DEFAULT 'pending',
+    expires_at DATETIME,
+    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
+    updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+    INDEX idx_teacher_id (teacher_id),
+    INDEX idx_family_id (family_id),
+    INDEX idx_status (status)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+-- 家长授权表(授权老师查看孩子任务)
+CREATE TABLE IF NOT EXISTS parent_authorizations (
+    id BIGINT AUTO_INCREMENT PRIMARY KEY,
+    family_id BIGINT NOT NULL,
+    child_id BIGINT NOT NULL,
+    teacher_id BIGINT NOT NULL,
+    permissions JSON COMMENT '权限集: task_view,task_review,message_send',
+    status ENUM('active', 'revoked', 'expired') DEFAULT 'active',
+    expires_at DATETIME,
+    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_teacher_id (teacher_id),
+    INDEX idx_status (status)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+-- =============================================
+-- 会员相关表
+-- =============================================
+
+-- 会员等级表
+CREATE TABLE IF NOT EXISTS membership_levels (
+    id BIGINT AUTO_INCREMENT PRIMARY KEY,
+    level_code VARCHAR(20) NOT NULL UNIQUE COMMENT 'FREE/BASIC/PROFESSIONAL/ENTERPRISE',
+    level_name VARCHAR(50) NOT NULL COMMENT '等级名称',
+    level_desc TEXT COMMENT '等级描述',
+    price_monthly DECIMAL(10,2) DEFAULT 0 COMMENT '月费',
+    price_yearly DECIMAL(10,2) DEFAULT 0 COMMENT '年费',
+    features JSON COMMENT '功能权限列表',
+    max_children INT DEFAULT 1 COMMENT '可添加孩子数量',
+    max_tasks_per_day INT DEFAULT 10 COMMENT '每日任务上限',
+    ai_review_enabled TINYINT DEFAULT 0 COMMENT 'AI审核',
+    priority_support TINYINT DEFAULT 0 COMMENT '优先客服',
+    teacher_consultation TINYINT DEFAULT 0 COMMENT '老师咨询',
+    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
+    updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+    INDEX idx_level_code (level_code)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+-- 家庭会员表
+CREATE TABLE IF NOT EXISTS family_memberships (
+    id BIGINT AUTO_INCREMENT PRIMARY KEY,
+    family_id BIGINT NOT NULL UNIQUE,
+    level_code VARCHAR(20) NOT NULL,
+    start_date DATETIME NOT NULL,
+    end_date DATETIME NOT NULL,
+    payment_method ENUM('wechat', 'alipay', 'card') COMMENT '支付方式',
+    payment_status ENUM('pending', 'paid', 'expired', 'refunded') DEFAULT 'pending',
+    order_no VARCHAR(64) COMMENT '订单号',
+    transaction_id VARCHAR(64) COMMENT '交易流水号',
+    amount DECIMAL(10,2) COMMENT '支付金额',
+    auto_renew TINYINT DEFAULT 0 COMMENT '自动续费',
+    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
+    updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+    INDEX idx_family_id (family_id),
+    INDEX idx_level_code (level_code),
+    INDEX idx_end_date (end_date)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+-- 支付订单表
+CREATE TABLE IF NOT EXISTS payment_orders (
+    id BIGINT AUTO_INCREMENT PRIMARY KEY,
+    order_no VARCHAR(64) NOT NULL UNIQUE,
+    family_id BIGINT NOT NULL,
+    level_code VARCHAR(20) NOT NULL,
+    payment_type ENUM('monthly', 'yearly') NOT NULL,
+    amount DECIMAL(10,2) NOT NULL,
+    status ENUM('pending', 'paid', 'cancelled', 'refunded') DEFAULT 'pending',
+    pay_method VARCHAR(20),
+    transaction_id VARCHAR(64),
+    paid_at DATETIME,
+    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
+    updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+    INDEX idx_order_no (order_no),
+    INDEX idx_family_id (family_id),
+    INDEX idx_status (status)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+-- =============================================
+-- 初始数据
+-- =============================================
+
+-- 插入会员等级数据
+INSERT IGNORE INTO membership_levels (level_code, level_name, level_desc, price_monthly, price_yearly, features, max_children, max_tasks_per_day, ai_review_enabled, priority_support, teacher_consultation) VALUES
+('FREE', '免费版', '基础功能,适合体验', 0, 0, '["basic_tasks", "points_system"]', 1, 5, 0, 0, 0),
+('BASIC', '基础版', '适合日常使用', 19.9, 199, '["basic_tasks", "points_system", "wishlist", "task_reminder"]', 2, 10, 0, 0, 0),
+('PROFESSIONAL', '专业版', '完整功能,AI辅助', 49.9, 499, '["basic_tasks", "points_system", "wishlist", "task_reminder", "ai_review", "growth_plan", "priority_support"]', 3, 20, 1, 1, 0),
+('ENTERPRISE', '企业版', '全方位服务', 99.9, 999, '["basic_tasks", "points_system", "wishlist", "task_reminder", "ai_review", "growth_plan", "priority_support", "teacher_consultation", "unlimited_children", "unlimited_tasks"]', 99, 999, 1, 1, 1);
+
+-- =============================================
+-- 数据库迁移(新增字段和表)- 通过Java代码处理
+-- =============================================
+
+-- 注意:ALTER TABLE语句已移到 DatabaseInitializer.java 中执行
+-- 避免重复执行导致的错误
+
+-- 3. 家长心愿单表(新增)
+CREATE TABLE IF NOT EXISTS reward_wishlists (
+    id BIGINT AUTO_INCREMENT PRIMARY KEY,
+    user_id BIGINT NOT NULL COMMENT '创建者用户ID',
+    family_id BIGINT NOT NULL COMMENT '家庭ID',
+    title VARCHAR(100) NOT NULL COMMENT '心愿名称',
+    description VARCHAR(500) COMMENT '心愿描述',
+    points_required INT NOT NULL COMMENT '所需积分',
+    category VARCHAR(32) COMMENT '分类',
+    image_url VARCHAR(255) COMMENT '图片链接',
+    status ENUM('available', 'pending', 'exchanged', 'rejected') DEFAULT 'available' COMMENT '状态',
+    exchange_approved TINYINT COMMENT '是否审批通过',
+    exchanged_at DATETIME 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)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+-- 4. 更新任务状态枚举(如果需要支持更多状态)
+-- ALTER TABLE tasks MODIFY COLUMN status ENUM('pending', 'completed', 'overdue', 'cancelled', 'parent_pending', 'parent_completed') DEFAULT 'pending';
+
+-- ===================== 任务模板套餐功能 =====================
+
+-- 指导师-家庭关系表
+CREATE TABLE IF NOT EXISTS guide_families (
+    id BIGINT AUTO_INCREMENT PRIMARY KEY,
+    guide_id BIGINT NOT NULL COMMENT '指导师用户ID',
+    family_id BIGINT NOT NULL COMMENT '家庭ID',
+    service_type VARCHAR(20) DEFAULT 'consulting' COMMENT '服务类型: monthly/yearly/consulting',
+    service_price DECIMAL(10,2) DEFAULT 0 COMMENT '服务价格',
+    status VARCHAR(20) DEFAULT 'binding' COMMENT '状态: binding/paused/cancelled',
+    bound_at DATETIME COMMENT '绑定时间',
+    expire_at DATETIME COMMENT '服务到期时间',
+    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
+    updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+    UNIQUE KEY uk_guide_family (guide_id, family_id),
+    INDEX idx_guide_id (guide_id),
+    INDEX idx_family_id (family_id)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+-- 任务模板套餐
+CREATE TABLE IF NOT EXISTS task_template_packages (
+    id BIGINT AUTO_INCREMENT PRIMARY KEY,
+    creator_id BIGINT NOT NULL COMMENT '创建者指导师ID',
+    name VARCHAR(100) NOT NULL COMMENT '套餐名称',
+    description TEXT COMMENT '详细介绍',
+    cycle_days INT NOT NULL COMMENT '周期天数',
+    category VARCHAR(50) COMMENT '分类',
+    tags JSON COMMENT '标签',
+    price DECIMAL(10,2) DEFAULT 0 COMMENT '售价',
+    platform_fee DECIMAL(5,2) DEFAULT 0 COMMENT '平台服务费',
+    is_public TINYINT DEFAULT 0 COMMENT '是否公开',
+    share_code VARCHAR(50) UNIQUE COMMENT '分享码',
+    cover_image VARCHAR(500) COMMENT '封面图',
+    status VARCHAR(20) DEFAULT 'draft' COMMENT '状态',
+    reviewer_id BIGINT COMMENT '审核人',
+    reviewed_at DATETIME COMMENT '审核时间',
+    reject_reason TEXT COMMENT '拒绝原因',
+    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
+    updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+    INDEX idx_creator_id (creator_id),
+    INDEX idx_status (status),
+    INDEX idx_is_public (is_public)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+-- 套餐内任务项
+CREATE TABLE IF NOT EXISTS task_template_items (
+    id BIGINT AUTO_INCREMENT PRIMARY KEY,
+    package_id BIGINT NOT NULL COMMENT '所属套餐ID',
+    task_name VARCHAR(100) NOT NULL,
+    description TEXT,
+    points INT DEFAULT 0,
+    frequency VARCHAR(20) DEFAULT 'daily',
+    duration INT COMMENT '预计时长(分钟)',
+    is_required TINYINT DEFAULT 0,
+    sort_order INT DEFAULT 0,
+    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
+    updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+    INDEX idx_package_id (package_id)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+-- 套餐应用实例
+CREATE TABLE IF NOT EXISTS task_plan_instances (
+    id BIGINT AUTO_INCREMENT PRIMARY KEY,
+    package_id BIGINT NOT NULL,
+    family_id BIGINT NOT NULL,
+    child_id BIGINT NOT NULL,
+    name VARCHAR(100) NOT NULL,
+    start_date DATE NOT NULL,
+    end_date DATE NOT NULL,
+    status VARCHAR(20) DEFAULT 'active',
+    total_days INT,
+    completed_days INT DEFAULT 0,
+    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;
+
+-- 实例任务项
+CREATE TABLE IF NOT EXISTS task_plan_items (
+    id BIGINT AUTO_INCREMENT PRIMARY KEY,
+    instance_id BIGINT NOT NULL,
+    template_item_id BIGINT,
+    task_name VARCHAR(100) NOT NULL,
+    description TEXT,
+    points INT DEFAULT 0,
+    frequency VARCHAR(20) DEFAULT 'daily',
+    duration INT,
+    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
+    updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+    INDEX idx_instance_id (instance_id)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+-- 套餐购买订单
+CREATE TABLE IF NOT EXISTS package_orders (
+    id BIGINT AUTO_INCREMENT PRIMARY KEY,
+    order_no VARCHAR(32) UNIQUE NOT NULL,
+    user_id BIGINT NOT NULL COMMENT '购买者用户ID',
+    family_id BIGINT NOT NULL COMMENT '家庭ID',
+    package_id BIGINT NOT NULL COMMENT '套餐ID',
+    package_name VARCHAR(100) COMMENT '套餐名称',
+    price DECIMAL(10,2) NOT NULL COMMENT '支付金额',
+    platform_fee DECIMAL(10,2) DEFAULT 0 COMMENT '平台服务费',
+    guide_id BIGINT COMMENT '指导师ID',
+    status VARCHAR(20) DEFAULT 'pending' COMMENT '状态: pending/paid/cancelled/refunded',
+    pay_method VARCHAR(20) COMMENT '支付方式',
+    transaction_id VARCHAR(64) COMMENT '微信支付订单号',
+    paid_at DATETIME COMMENT '支付时间',
+    notify_data TEXT 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_package_id (package_id),
+    INDEX idx_status (status)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;