Pārlūkot izejas kodu

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

- 移除schema.sql中的ALTER TABLE语句
- 在DatabaseInitializer中添加runMigrations()方法
- 使用try-catch处理列/索引已存在的情况
- 支持幂等性迁移(可重复执行)
- 添加tasks表executor_type/executor_id字段
- 添加users表roles/total_points等新字段
- 添加children表birthday/gender字段
- 支持teacher角色
User 5 mēneši atpakaļ
vecāks
revīzija
fa187e9a99

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

@@ -20,16 +20,19 @@ public class DatabaseInitializer implements CommandLineRunner {
     @Override
     public void run(String... args) throws Exception {
         log.info("开始初始化数据库...");
-        
+
         // 1. 检查并创建数据库
         initializeDatabase();
-        
+
         // 2. 检查并创建表
         initializeTables();
-        
-        // 3. 初始化默认数据
+
+        // 3. 执行数据库迁移(添加新列)
+        runMigrations();
+
+        // 4. 初始化默认数据
         initializeDefaultData();
-        
+
         log.info("数据库初始化完成!");
     }
 
@@ -342,6 +345,120 @@ public class DatabaseInitializer implements CommandLineRunner {
         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() {
         // 初始化预设奖励模板
         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,
     unionid VARCHAR(64),
     family_id BIGINT NOT NULL,
-    role ENUM('parent','child') NOT NULL,
+    role ENUM('parent','child','teacher') NOT NULL,
     nickname VARCHAR(50),
     avatar VARCHAR(255),
     password VARCHAR(128),
@@ -134,7 +134,7 @@ CREATE TABLE IF NOT EXISTS reward_categories (
 ) 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分钟', 40, '特权奖励', 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);
 
 -- 默认奖励分类
-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, '精神奖励', 'star', 2),
 (0, '特权奖励', 'crown', 3);
@@ -208,7 +208,7 @@ CREATE TABLE IF NOT EXISTS streak_milestones (
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
 
 -- 插入打卡里程碑数据
-INSERT INTO streak_milestones (days, reward_points) VALUES
+INSERT IGNORE INTO streak_milestones (days, reward_points) VALUES
 (3, 5),
 (7, 10),
 (30, 30),
@@ -283,3 +283,359 @@ INDEX idx_type (type),
 FOREIGN KEY (task_id) REFERENCES tasks(id) ON DELETE CASCADE,
 FOREIGN KEY (creator_id) REFERENCES users(id) ON DELETE CASCADE
 ) 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;