1. 课程学习进度追踪
- 新增 UserCourseProgress 和 UserMaterialProgress 模型
- 新增 /api/v1/progress/* 进度追踪 API
- 更新 admin.py 使用真实课程完成率数据
2. 路由权限检查完善
- 新增前端 permissionChecker.ts 权限检查工具
- 更新 router/guard.ts 实现团队和课程权限验证
- 新增后端 permission_service.py
3. AI 陪练音频转文本
- 新增 speech_recognition.py 语音识别服务
- 新增 /api/v1/speech/* API
- 更新 ai-practice-coze.vue 支持语音输入
4. 双人对练报告生成
- 更新 practice_room_service.py 添加报告生成功能
- 新增 /rooms/{room_code}/report API
- 更新 duo-practice-report.vue 调用真实 API
5. 学习提醒推送
- 新增 notification_service.py 通知服务
- 新增 scheduler_service.py 定时任务服务
- 支持钉钉、企微、站内消息推送
6. 智能学习推荐
- 新增 recommendation_service.py 推荐服务
- 新增 /api/v1/recommendations/* API
- 支持错题、能力、进度、热门多维度推荐
7. 安全问题修复
- DEBUG 默认值改为 False
- 添加 SECRET_KEY 安全警告
- 新增 check_security_settings() 检查函数
8. 证书 PDF 生成
- 更新 certificate_service.py 添加 PDF 生成
- 添加 weasyprint、Pillow、qrcode 依赖
- 更新下载 API 支持 PDF 和 PNG 格式
This commit is contained in:
@@ -1,82 +1,82 @@
|
||||
# 数据库迁移说明
|
||||
|
||||
本目录包含 KPL 考培练系统的数据库迁移脚本。
|
||||
|
||||
## 迁移脚本列表
|
||||
|
||||
| 脚本 | 说明 | 创建时间 |
|
||||
|------|------|----------|
|
||||
| `add_level_badge_system.sql` | 等级与奖章系统 | 2026-01-29 |
|
||||
|
||||
## 执行迁移
|
||||
|
||||
### 测试环境(Docker)
|
||||
|
||||
KPL 测试环境数据库在服务器 Docker 容器中运行:
|
||||
|
||||
```bash
|
||||
# 1. SSH 登录 KPL 服务器
|
||||
ssh root@<KPL服务器IP>
|
||||
|
||||
# 2. 进入项目目录
|
||||
cd /www/wwwroot/kpl.ireborn.com.cn
|
||||
|
||||
# 3. 执行迁移(方法一:直接执行)
|
||||
docker exec -i kpl-mysql-dev mysql -uroot -pnj861021 kpl_dev < backend/migrations/add_level_badge_system.sql
|
||||
|
||||
# 或者(方法二:交互式执行)
|
||||
docker exec -it kpl-mysql-dev mysql -uroot -pnj861021 kpl_dev
|
||||
# 然后复制粘贴 SQL 脚本内容执行
|
||||
|
||||
# 方法三:从本地执行(需要先上传SQL文件到服务器)
|
||||
# scp backend/migrations/add_level_badge_system.sql root@<服务器IP>:/tmp/
|
||||
# ssh root@<服务器IP> "docker exec -i kpl-mysql-dev mysql -uroot -pnj861021 kpl_dev < /tmp/add_level_badge_system.sql"
|
||||
```
|
||||
|
||||
**注意**:MySQL 容器密码为 `nj861021`(之前通过 `docker exec kpl-mysql-dev env | grep MYSQL` 确认)
|
||||
|
||||
### 生产环境
|
||||
|
||||
生产环境迁移前请确保:
|
||||
1. 已备份数据库
|
||||
2. 在低峰期执行
|
||||
3. 测试环境验证通过
|
||||
|
||||
```bash
|
||||
# 执行迁移(替换为实际的生产数据库配置)
|
||||
mysql -h<host> -u<user> -p<password> <database> < backend/migrations/add_level_badge_system.sql
|
||||
```
|
||||
|
||||
## 回滚方法
|
||||
|
||||
如需回滚,执行以下 SQL:
|
||||
|
||||
```sql
|
||||
DROP TABLE IF EXISTS user_badges;
|
||||
DROP TABLE IF EXISTS badge_definitions;
|
||||
DROP TABLE IF EXISTS exp_history;
|
||||
DROP TABLE IF EXISTS level_configs;
|
||||
DROP TABLE IF EXISTS user_levels;
|
||||
```
|
||||
|
||||
## 验证迁移
|
||||
|
||||
执行以下查询验证表是否创建成功:
|
||||
|
||||
```sql
|
||||
SHOW TABLES LIKE '%level%';
|
||||
SHOW TABLES LIKE '%badge%';
|
||||
SHOW TABLES LIKE '%exp%';
|
||||
|
||||
-- 查看表结构
|
||||
DESCRIBE user_levels;
|
||||
DESCRIBE exp_history;
|
||||
DESCRIBE badge_definitions;
|
||||
DESCRIBE user_badges;
|
||||
DESCRIBE level_configs;
|
||||
|
||||
-- 验证初始数据
|
||||
SELECT COUNT(*) FROM level_configs; -- 应该是 10 条
|
||||
SELECT COUNT(*) FROM badge_definitions; -- 应该是 20 条
|
||||
SELECT COUNT(*) FROM user_levels; -- 应该等于用户数
|
||||
```
|
||||
# 数据库迁移说明
|
||||
|
||||
本目录包含 KPL 考培练系统的数据库迁移脚本。
|
||||
|
||||
## 迁移脚本列表
|
||||
|
||||
| 脚本 | 说明 | 创建时间 |
|
||||
|------|------|----------|
|
||||
| `add_level_badge_system.sql` | 等级与奖章系统 | 2026-01-29 |
|
||||
|
||||
## 执行迁移
|
||||
|
||||
### 测试环境(Docker)
|
||||
|
||||
KPL 测试环境数据库在服务器 Docker 容器中运行:
|
||||
|
||||
```bash
|
||||
# 1. SSH 登录 KPL 服务器
|
||||
ssh root@<KPL服务器IP>
|
||||
|
||||
# 2. 进入项目目录
|
||||
cd /www/wwwroot/kpl.ireborn.com.cn
|
||||
|
||||
# 3. 执行迁移(方法一:直接执行)
|
||||
docker exec -i kpl-mysql-dev mysql -uroot -pnj861021 kpl_dev < backend/migrations/add_level_badge_system.sql
|
||||
|
||||
# 或者(方法二:交互式执行)
|
||||
docker exec -it kpl-mysql-dev mysql -uroot -pnj861021 kpl_dev
|
||||
# 然后复制粘贴 SQL 脚本内容执行
|
||||
|
||||
# 方法三:从本地执行(需要先上传SQL文件到服务器)
|
||||
# scp backend/migrations/add_level_badge_system.sql root@<服务器IP>:/tmp/
|
||||
# ssh root@<服务器IP> "docker exec -i kpl-mysql-dev mysql -uroot -pnj861021 kpl_dev < /tmp/add_level_badge_system.sql"
|
||||
```
|
||||
|
||||
**注意**:MySQL 容器密码为 `nj861021`(之前通过 `docker exec kpl-mysql-dev env | grep MYSQL` 确认)
|
||||
|
||||
### 生产环境
|
||||
|
||||
生产环境迁移前请确保:
|
||||
1. 已备份数据库
|
||||
2. 在低峰期执行
|
||||
3. 测试环境验证通过
|
||||
|
||||
```bash
|
||||
# 执行迁移(替换为实际的生产数据库配置)
|
||||
mysql -h<host> -u<user> -p<password> <database> < backend/migrations/add_level_badge_system.sql
|
||||
```
|
||||
|
||||
## 回滚方法
|
||||
|
||||
如需回滚,执行以下 SQL:
|
||||
|
||||
```sql
|
||||
DROP TABLE IF EXISTS user_badges;
|
||||
DROP TABLE IF EXISTS badge_definitions;
|
||||
DROP TABLE IF EXISTS exp_history;
|
||||
DROP TABLE IF EXISTS level_configs;
|
||||
DROP TABLE IF EXISTS user_levels;
|
||||
```
|
||||
|
||||
## 验证迁移
|
||||
|
||||
执行以下查询验证表是否创建成功:
|
||||
|
||||
```sql
|
||||
SHOW TABLES LIKE '%level%';
|
||||
SHOW TABLES LIKE '%badge%';
|
||||
SHOW TABLES LIKE '%exp%';
|
||||
|
||||
-- 查看表结构
|
||||
DESCRIBE user_levels;
|
||||
DESCRIBE exp_history;
|
||||
DESCRIBE badge_definitions;
|
||||
DESCRIBE user_badges;
|
||||
DESCRIBE level_configs;
|
||||
|
||||
-- 验证初始数据
|
||||
SELECT COUNT(*) FROM level_configs; -- 应该是 10 条
|
||||
SELECT COUNT(*) FROM badge_definitions; -- 应该是 20 条
|
||||
SELECT COUNT(*) FROM user_levels; -- 应该等于用户数
|
||||
```
|
||||
|
||||
@@ -1,166 +1,166 @@
|
||||
-- ================================================================
|
||||
-- 证书系统数据库迁移脚本
|
||||
-- 创建日期: 2026-01-29
|
||||
-- 功能: 添加证书模板表和用户证书表
|
||||
-- ================================================================
|
||||
|
||||
-- 事务开始
|
||||
START TRANSACTION;
|
||||
|
||||
-- ================================================================
|
||||
-- 1. 创建证书模板表
|
||||
-- ================================================================
|
||||
CREATE TABLE IF NOT EXISTS certificate_templates (
|
||||
id INT PRIMARY KEY AUTO_INCREMENT,
|
||||
name VARCHAR(100) NOT NULL COMMENT '模板名称',
|
||||
type ENUM('course', 'exam', 'achievement') NOT NULL COMMENT '证书类型: course=课程结业, exam=考试合格, achievement=成就证书',
|
||||
background_url VARCHAR(500) COMMENT '证书背景图URL',
|
||||
template_html TEXT COMMENT 'HTML模板内容',
|
||||
template_style TEXT COMMENT 'CSS样式',
|
||||
is_active BOOLEAN DEFAULT TRUE 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_type (type),
|
||||
INDEX idx_is_active (is_active)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='证书模板表';
|
||||
|
||||
-- ================================================================
|
||||
-- 2. 创建用户证书表
|
||||
-- ================================================================
|
||||
CREATE TABLE IF NOT EXISTS user_certificates (
|
||||
id INT PRIMARY KEY AUTO_INCREMENT,
|
||||
user_id INT NOT NULL COMMENT '用户ID',
|
||||
template_id INT NOT NULL COMMENT '模板ID',
|
||||
certificate_no VARCHAR(50) UNIQUE NOT NULL COMMENT '证书编号 KPL-年份-序号',
|
||||
title VARCHAR(200) NOT NULL COMMENT '证书标题',
|
||||
description TEXT COMMENT '证书描述/成就说明',
|
||||
issued_at DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '颁发时间',
|
||||
valid_until DATETIME COMMENT '有效期至(NULL表示永久)',
|
||||
|
||||
-- 关联信息
|
||||
course_id INT COMMENT '关联课程ID',
|
||||
exam_id INT COMMENT '关联考试ID',
|
||||
badge_id INT COMMENT '关联奖章ID',
|
||||
|
||||
-- 成绩信息
|
||||
score DECIMAL(5,2) COMMENT '考试分数',
|
||||
completion_rate DECIMAL(5,2) COMMENT '完成率',
|
||||
|
||||
-- 生成的文件
|
||||
pdf_url VARCHAR(500) COMMENT 'PDF文件URL',
|
||||
image_url VARCHAR(500) COMMENT '分享图片URL',
|
||||
|
||||
-- 元数据
|
||||
meta_data JSON COMMENT '扩展元数据',
|
||||
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
|
||||
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (template_id) REFERENCES certificate_templates(id),
|
||||
INDEX idx_user_id (user_id),
|
||||
INDEX idx_certificate_no (certificate_no),
|
||||
INDEX idx_course_id (course_id),
|
||||
INDEX idx_exam_id (exam_id),
|
||||
INDEX idx_issued_at (issued_at)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户证书表';
|
||||
|
||||
-- ================================================================
|
||||
-- 3. 插入默认证书模板
|
||||
-- ================================================================
|
||||
INSERT INTO certificate_templates (name, type, template_html, template_style, is_active, sort_order) VALUES
|
||||
-- 课程结业证书模板
|
||||
('课程结业证书', 'course',
|
||||
'<div class="certificate">
|
||||
<div class="header">
|
||||
<div class="logo">考培练系统</div>
|
||||
<h1>结业证书</h1>
|
||||
</div>
|
||||
<div class="body">
|
||||
<p class="recipient">兹证明 <strong>{{user_name}}</strong></p>
|
||||
<p class="content">已完成课程《{{course_name}}》的全部学习内容</p>
|
||||
<p class="completion">完成率:{{completion_rate}}%</p>
|
||||
<p class="date">颁发日期:{{issue_date}}</p>
|
||||
</div>
|
||||
<div class="footer">
|
||||
<div class="cert-no">证书编号:{{certificate_no}}</div>
|
||||
<div class="qrcode">{{qrcode}}</div>
|
||||
</div>
|
||||
</div>',
|
||||
'.certificate { width: 800px; height: 600px; padding: 40px; background: linear-gradient(135deg, #f5f7fa 0%, #e4e9f2 100%); font-family: "Microsoft YaHei", sans-serif; }
|
||||
.header { text-align: center; margin-bottom: 30px; }
|
||||
.header .logo { font-size: 24px; color: #667eea; font-weight: bold; }
|
||||
.header h1 { font-size: 36px; color: #333; margin: 20px 0; }
|
||||
.body { text-align: center; padding: 30px 60px; }
|
||||
.body .recipient { font-size: 20px; margin-bottom: 20px; }
|
||||
.body .content { font-size: 18px; color: #555; margin-bottom: 15px; }
|
||||
.body .completion { font-size: 16px; color: #667eea; }
|
||||
.body .date { font-size: 14px; color: #888; margin-top: 30px; }
|
||||
.footer { display: flex; justify-content: space-between; align-items: center; margin-top: 40px; }
|
||||
.cert-no { font-size: 12px; color: #999; }
|
||||
.qrcode { width: 80px; height: 80px; }',
|
||||
TRUE, 1),
|
||||
|
||||
-- 考试合格证书模板
|
||||
('考试合格证书', 'exam',
|
||||
'<div class="certificate exam-cert">
|
||||
<div class="header">
|
||||
<div class="logo">考培练系统</div>
|
||||
<h1>考试合格证书</h1>
|
||||
</div>
|
||||
<div class="body">
|
||||
<p class="recipient">兹证明 <strong>{{user_name}}</strong></p>
|
||||
<p class="content">在《{{exam_name}}》考试中成绩合格</p>
|
||||
<div class="score-badge">
|
||||
<span class="score">{{score}}</span>
|
||||
<span class="unit">分</span>
|
||||
</div>
|
||||
<p class="date">考试日期:{{exam_date}}</p>
|
||||
</div>
|
||||
<div class="footer">
|
||||
<div class="cert-no">证书编号:{{certificate_no}}</div>
|
||||
<div class="qrcode">{{qrcode}}</div>
|
||||
</div>
|
||||
</div>',
|
||||
'.certificate.exam-cert { width: 800px; height: 600px; padding: 40px; background: linear-gradient(135deg, #e8f5e9 0%, #c8e6c9 100%); font-family: "Microsoft YaHei", sans-serif; }
|
||||
.exam-cert .header h1 { color: #2e7d32; }
|
||||
.score-badge { display: inline-block; padding: 20px 40px; background: #fff; border-radius: 50%; box-shadow: 0 4px 20px rgba(0,0,0,0.1); margin: 20px 0; }
|
||||
.score-badge .score { font-size: 48px; font-weight: bold; color: #2e7d32; }
|
||||
.score-badge .unit { font-size: 18px; color: #666; }',
|
||||
TRUE, 2),
|
||||
|
||||
-- 成就证书模板
|
||||
('成就证书', 'achievement',
|
||||
'<div class="certificate achievement-cert">
|
||||
<div class="header">
|
||||
<div class="logo">考培练系统</div>
|
||||
<h1>成就证书</h1>
|
||||
</div>
|
||||
<div class="body">
|
||||
<p class="recipient">兹证明 <strong>{{user_name}}</strong></p>
|
||||
<div class="achievement-icon">{{badge_icon}}</div>
|
||||
<p class="achievement-name">{{badge_name}}</p>
|
||||
<p class="achievement-desc">{{badge_description}}</p>
|
||||
<p class="date">获得日期:{{achieve_date}}</p>
|
||||
</div>
|
||||
<div class="footer">
|
||||
<div class="cert-no">证书编号:{{certificate_no}}</div>
|
||||
<div class="qrcode">{{qrcode}}</div>
|
||||
</div>
|
||||
</div>',
|
||||
'.certificate.achievement-cert { width: 800px; height: 600px; padding: 40px; background: linear-gradient(135deg, #fff3e0 0%, #ffe0b2 100%); font-family: "Microsoft YaHei", sans-serif; }
|
||||
.achievement-cert .header h1 { color: #e65100; }
|
||||
.achievement-icon { font-size: 64px; margin: 20px 0; }
|
||||
.achievement-name { font-size: 24px; font-weight: bold; color: #e65100; margin: 10px 0; }
|
||||
.achievement-desc { font-size: 16px; color: #666; }',
|
||||
TRUE, 3);
|
||||
|
||||
-- 提交事务
|
||||
COMMIT;
|
||||
|
||||
-- ================================================================
|
||||
-- 验证脚本
|
||||
-- ================================================================
|
||||
-- SELECT * FROM certificate_templates;
|
||||
-- SELECT COUNT(*) AS template_count FROM certificate_templates;
|
||||
-- ================================================================
|
||||
-- 证书系统数据库迁移脚本
|
||||
-- 创建日期: 2026-01-29
|
||||
-- 功能: 添加证书模板表和用户证书表
|
||||
-- ================================================================
|
||||
|
||||
-- 事务开始
|
||||
START TRANSACTION;
|
||||
|
||||
-- ================================================================
|
||||
-- 1. 创建证书模板表
|
||||
-- ================================================================
|
||||
CREATE TABLE IF NOT EXISTS certificate_templates (
|
||||
id INT PRIMARY KEY AUTO_INCREMENT,
|
||||
name VARCHAR(100) NOT NULL COMMENT '模板名称',
|
||||
type ENUM('course', 'exam', 'achievement') NOT NULL COMMENT '证书类型: course=课程结业, exam=考试合格, achievement=成就证书',
|
||||
background_url VARCHAR(500) COMMENT '证书背景图URL',
|
||||
template_html TEXT COMMENT 'HTML模板内容',
|
||||
template_style TEXT COMMENT 'CSS样式',
|
||||
is_active BOOLEAN DEFAULT TRUE 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_type (type),
|
||||
INDEX idx_is_active (is_active)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='证书模板表';
|
||||
|
||||
-- ================================================================
|
||||
-- 2. 创建用户证书表
|
||||
-- ================================================================
|
||||
CREATE TABLE IF NOT EXISTS user_certificates (
|
||||
id INT PRIMARY KEY AUTO_INCREMENT,
|
||||
user_id INT NOT NULL COMMENT '用户ID',
|
||||
template_id INT NOT NULL COMMENT '模板ID',
|
||||
certificate_no VARCHAR(50) UNIQUE NOT NULL COMMENT '证书编号 KPL-年份-序号',
|
||||
title VARCHAR(200) NOT NULL COMMENT '证书标题',
|
||||
description TEXT COMMENT '证书描述/成就说明',
|
||||
issued_at DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '颁发时间',
|
||||
valid_until DATETIME COMMENT '有效期至(NULL表示永久)',
|
||||
|
||||
-- 关联信息
|
||||
course_id INT COMMENT '关联课程ID',
|
||||
exam_id INT COMMENT '关联考试ID',
|
||||
badge_id INT COMMENT '关联奖章ID',
|
||||
|
||||
-- 成绩信息
|
||||
score DECIMAL(5,2) COMMENT '考试分数',
|
||||
completion_rate DECIMAL(5,2) COMMENT '完成率',
|
||||
|
||||
-- 生成的文件
|
||||
pdf_url VARCHAR(500) COMMENT 'PDF文件URL',
|
||||
image_url VARCHAR(500) COMMENT '分享图片URL',
|
||||
|
||||
-- 元数据
|
||||
meta_data JSON COMMENT '扩展元数据',
|
||||
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
|
||||
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (template_id) REFERENCES certificate_templates(id),
|
||||
INDEX idx_user_id (user_id),
|
||||
INDEX idx_certificate_no (certificate_no),
|
||||
INDEX idx_course_id (course_id),
|
||||
INDEX idx_exam_id (exam_id),
|
||||
INDEX idx_issued_at (issued_at)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户证书表';
|
||||
|
||||
-- ================================================================
|
||||
-- 3. 插入默认证书模板
|
||||
-- ================================================================
|
||||
INSERT INTO certificate_templates (name, type, template_html, template_style, is_active, sort_order) VALUES
|
||||
-- 课程结业证书模板
|
||||
('课程结业证书', 'course',
|
||||
'<div class="certificate">
|
||||
<div class="header">
|
||||
<div class="logo">考培练系统</div>
|
||||
<h1>结业证书</h1>
|
||||
</div>
|
||||
<div class="body">
|
||||
<p class="recipient">兹证明 <strong>{{user_name}}</strong></p>
|
||||
<p class="content">已完成课程《{{course_name}}》的全部学习内容</p>
|
||||
<p class="completion">完成率:{{completion_rate}}%</p>
|
||||
<p class="date">颁发日期:{{issue_date}}</p>
|
||||
</div>
|
||||
<div class="footer">
|
||||
<div class="cert-no">证书编号:{{certificate_no}}</div>
|
||||
<div class="qrcode">{{qrcode}}</div>
|
||||
</div>
|
||||
</div>',
|
||||
'.certificate { width: 800px; height: 600px; padding: 40px; background: linear-gradient(135deg, #f5f7fa 0%, #e4e9f2 100%); font-family: "Microsoft YaHei", sans-serif; }
|
||||
.header { text-align: center; margin-bottom: 30px; }
|
||||
.header .logo { font-size: 24px; color: #667eea; font-weight: bold; }
|
||||
.header h1 { font-size: 36px; color: #333; margin: 20px 0; }
|
||||
.body { text-align: center; padding: 30px 60px; }
|
||||
.body .recipient { font-size: 20px; margin-bottom: 20px; }
|
||||
.body .content { font-size: 18px; color: #555; margin-bottom: 15px; }
|
||||
.body .completion { font-size: 16px; color: #667eea; }
|
||||
.body .date { font-size: 14px; color: #888; margin-top: 30px; }
|
||||
.footer { display: flex; justify-content: space-between; align-items: center; margin-top: 40px; }
|
||||
.cert-no { font-size: 12px; color: #999; }
|
||||
.qrcode { width: 80px; height: 80px; }',
|
||||
TRUE, 1),
|
||||
|
||||
-- 考试合格证书模板
|
||||
('考试合格证书', 'exam',
|
||||
'<div class="certificate exam-cert">
|
||||
<div class="header">
|
||||
<div class="logo">考培练系统</div>
|
||||
<h1>考试合格证书</h1>
|
||||
</div>
|
||||
<div class="body">
|
||||
<p class="recipient">兹证明 <strong>{{user_name}}</strong></p>
|
||||
<p class="content">在《{{exam_name}}》考试中成绩合格</p>
|
||||
<div class="score-badge">
|
||||
<span class="score">{{score}}</span>
|
||||
<span class="unit">分</span>
|
||||
</div>
|
||||
<p class="date">考试日期:{{exam_date}}</p>
|
||||
</div>
|
||||
<div class="footer">
|
||||
<div class="cert-no">证书编号:{{certificate_no}}</div>
|
||||
<div class="qrcode">{{qrcode}}</div>
|
||||
</div>
|
||||
</div>',
|
||||
'.certificate.exam-cert { width: 800px; height: 600px; padding: 40px; background: linear-gradient(135deg, #e8f5e9 0%, #c8e6c9 100%); font-family: "Microsoft YaHei", sans-serif; }
|
||||
.exam-cert .header h1 { color: #2e7d32; }
|
||||
.score-badge { display: inline-block; padding: 20px 40px; background: #fff; border-radius: 50%; box-shadow: 0 4px 20px rgba(0,0,0,0.1); margin: 20px 0; }
|
||||
.score-badge .score { font-size: 48px; font-weight: bold; color: #2e7d32; }
|
||||
.score-badge .unit { font-size: 18px; color: #666; }',
|
||||
TRUE, 2),
|
||||
|
||||
-- 成就证书模板
|
||||
('成就证书', 'achievement',
|
||||
'<div class="certificate achievement-cert">
|
||||
<div class="header">
|
||||
<div class="logo">考培练系统</div>
|
||||
<h1>成就证书</h1>
|
||||
</div>
|
||||
<div class="body">
|
||||
<p class="recipient">兹证明 <strong>{{user_name}}</strong></p>
|
||||
<div class="achievement-icon">{{badge_icon}}</div>
|
||||
<p class="achievement-name">{{badge_name}}</p>
|
||||
<p class="achievement-desc">{{badge_description}}</p>
|
||||
<p class="date">获得日期:{{achieve_date}}</p>
|
||||
</div>
|
||||
<div class="footer">
|
||||
<div class="cert-no">证书编号:{{certificate_no}}</div>
|
||||
<div class="qrcode">{{qrcode}}</div>
|
||||
</div>
|
||||
</div>',
|
||||
'.certificate.achievement-cert { width: 800px; height: 600px; padding: 40px; background: linear-gradient(135deg, #fff3e0 0%, #ffe0b2 100%); font-family: "Microsoft YaHei", sans-serif; }
|
||||
.achievement-cert .header h1 { color: #e65100; }
|
||||
.achievement-icon { font-size: 64px; margin: 20px 0; }
|
||||
.achievement-name { font-size: 24px; font-weight: bold; color: #e65100; margin: 10px 0; }
|
||||
.achievement-desc { font-size: 16px; color: #666; }',
|
||||
TRUE, 3);
|
||||
|
||||
-- 提交事务
|
||||
COMMIT;
|
||||
|
||||
-- ================================================================
|
||||
-- 验证脚本
|
||||
-- ================================================================
|
||||
-- SELECT * FROM certificate_templates;
|
||||
-- SELECT COUNT(*) AS template_count FROM certificate_templates;
|
||||
|
||||
@@ -1,41 +1,41 @@
|
||||
-- =====================================================
|
||||
-- 钉钉免密登录功能 - 数据库迁移脚本
|
||||
-- 创建时间: 2026-01-28
|
||||
-- 说明: 为考培练系统添加钉钉免密登录支持
|
||||
-- =====================================================
|
||||
|
||||
-- 1. 用户表添加 dingtalk_id 字段
|
||||
-- -----------------------------------------------------
|
||||
ALTER TABLE users ADD COLUMN dingtalk_id VARCHAR(64) UNIQUE COMMENT '钉钉用户ID';
|
||||
CREATE INDEX idx_users_dingtalk_id ON users(dingtalk_id);
|
||||
|
||||
|
||||
-- 2. 配置模板表添加钉钉配置项
|
||||
-- -----------------------------------------------------
|
||||
INSERT INTO config_templates (config_group, config_key, display_name, description, value_type, is_required, is_secret, sort_order) VALUES
|
||||
('dingtalk', 'DINGTALK_APP_KEY', 'AppKey', '钉钉应用的AppKey(从钉钉开放平台获取)', 'string', 1, 0, 1),
|
||||
('dingtalk', 'DINGTALK_APP_SECRET', 'AppSecret', '钉钉应用的AppSecret(敏感信息)', 'string', 1, 1, 2),
|
||||
('dingtalk', 'DINGTALK_AGENT_ID', 'AgentId', '钉钉应用的AgentId', 'string', 1, 0, 3),
|
||||
('dingtalk', 'DINGTALK_CORP_ID', 'CorpId', '钉钉企业的CorpId', 'string', 1, 0, 4);
|
||||
|
||||
|
||||
-- 3. 功能开关表添加钉钉免密登录开关(默认禁用)
|
||||
-- -----------------------------------------------------
|
||||
INSERT INTO feature_switches (tenant_id, feature_code, feature_name, feature_group, is_enabled, description) VALUES
|
||||
(NULL, 'dingtalk_login', '钉钉免密登录', 'auth', 0, '启用后,用户可通过钉钉免密登录系统');
|
||||
|
||||
|
||||
-- =====================================================
|
||||
-- 回滚脚本(如需回滚,执行以下SQL)
|
||||
-- =====================================================
|
||||
/*
|
||||
-- 回滚步骤1: 删除功能开关
|
||||
DELETE FROM feature_switches WHERE feature_code = 'dingtalk_login';
|
||||
|
||||
-- 回滚步骤2: 删除配置模板
|
||||
DELETE FROM config_templates WHERE config_group = 'dingtalk';
|
||||
|
||||
-- 回滚步骤3: 删除用户表字段
|
||||
ALTER TABLE users DROP INDEX idx_users_dingtalk_id;
|
||||
ALTER TABLE users DROP COLUMN dingtalk_id;
|
||||
*/
|
||||
-- =====================================================
|
||||
-- 钉钉免密登录功能 - 数据库迁移脚本
|
||||
-- 创建时间: 2026-01-28
|
||||
-- 说明: 为考培练系统添加钉钉免密登录支持
|
||||
-- =====================================================
|
||||
|
||||
-- 1. 用户表添加 dingtalk_id 字段
|
||||
-- -----------------------------------------------------
|
||||
ALTER TABLE users ADD COLUMN dingtalk_id VARCHAR(64) UNIQUE COMMENT '钉钉用户ID';
|
||||
CREATE INDEX idx_users_dingtalk_id ON users(dingtalk_id);
|
||||
|
||||
|
||||
-- 2. 配置模板表添加钉钉配置项
|
||||
-- -----------------------------------------------------
|
||||
INSERT INTO config_templates (config_group, config_key, display_name, description, value_type, is_required, is_secret, sort_order) VALUES
|
||||
('dingtalk', 'DINGTALK_APP_KEY', 'AppKey', '钉钉应用的AppKey(从钉钉开放平台获取)', 'string', 1, 0, 1),
|
||||
('dingtalk', 'DINGTALK_APP_SECRET', 'AppSecret', '钉钉应用的AppSecret(敏感信息)', 'string', 1, 1, 2),
|
||||
('dingtalk', 'DINGTALK_AGENT_ID', 'AgentId', '钉钉应用的AgentId', 'string', 1, 0, 3),
|
||||
('dingtalk', 'DINGTALK_CORP_ID', 'CorpId', '钉钉企业的CorpId', 'string', 1, 0, 4);
|
||||
|
||||
|
||||
-- 3. 功能开关表添加钉钉免密登录开关(默认禁用)
|
||||
-- -----------------------------------------------------
|
||||
INSERT INTO feature_switches (tenant_id, feature_code, feature_name, feature_group, is_enabled, description) VALUES
|
||||
(NULL, 'dingtalk_login', '钉钉免密登录', 'auth', 0, '启用后,用户可通过钉钉免密登录系统');
|
||||
|
||||
|
||||
-- =====================================================
|
||||
-- 回滚脚本(如需回滚,执行以下SQL)
|
||||
-- =====================================================
|
||||
/*
|
||||
-- 回滚步骤1: 删除功能开关
|
||||
DELETE FROM feature_switches WHERE feature_code = 'dingtalk_login';
|
||||
|
||||
-- 回滚步骤2: 删除配置模板
|
||||
DELETE FROM config_templates WHERE config_group = 'dingtalk';
|
||||
|
||||
-- 回滚步骤3: 删除用户表字段
|
||||
ALTER TABLE users DROP INDEX idx_users_dingtalk_id;
|
||||
ALTER TABLE users DROP COLUMN dingtalk_id;
|
||||
*/
|
||||
|
||||
@@ -1,192 +1,192 @@
|
||||
-- =====================================================
|
||||
-- 等级与奖章系统数据库迁移脚本
|
||||
-- 版本: 1.0.0
|
||||
-- 创建时间: 2026-01-29
|
||||
-- 说明: 添加用户等级系统和奖章系统相关表
|
||||
-- =====================================================
|
||||
|
||||
-- 使用事务确保原子性
|
||||
START TRANSACTION;
|
||||
|
||||
-- =====================================================
|
||||
-- 1. 用户等级表 (user_levels)
|
||||
-- 存储用户的等级和经验值信息
|
||||
-- =====================================================
|
||||
CREATE TABLE IF NOT EXISTS user_levels (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
user_id INT NOT NULL COMMENT '用户ID',
|
||||
level INT NOT NULL DEFAULT 1 COMMENT '当前等级',
|
||||
exp INT NOT NULL DEFAULT 0 COMMENT '当前经验值',
|
||||
total_exp INT NOT NULL DEFAULT 0 COMMENT '累计获得经验值',
|
||||
login_streak INT NOT NULL DEFAULT 0 COMMENT '连续登录天数',
|
||||
max_login_streak INT NOT NULL DEFAULT 0 COMMENT '历史最长连续登录天数',
|
||||
last_login_date DATE NULL COMMENT '最后登录日期(用于计算连续登录)',
|
||||
last_checkin_at DATETIME NULL COMMENT '最后签到时间',
|
||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
UNIQUE KEY uk_user_id (user_id),
|
||||
INDEX idx_level (level),
|
||||
INDEX idx_total_exp (total_exp),
|
||||
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户等级表';
|
||||
|
||||
-- =====================================================
|
||||
-- 2. 经验值历史表 (exp_history)
|
||||
-- 记录每次经验值变化的详细信息
|
||||
-- =====================================================
|
||||
CREATE TABLE IF NOT EXISTS exp_history (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
user_id INT NOT NULL COMMENT '用户ID',
|
||||
exp_change INT NOT NULL COMMENT '经验值变化(正为获得,负为扣除)',
|
||||
exp_type VARCHAR(50) NOT NULL COMMENT '类型:exam/practice/training/task/login/badge/other',
|
||||
source_id INT NULL COMMENT '来源记录ID(如考试ID、练习ID等)',
|
||||
description VARCHAR(255) NOT NULL COMMENT '描述',
|
||||
level_before INT NULL COMMENT '变化前等级',
|
||||
level_after INT NULL COMMENT '变化后等级',
|
||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
INDEX idx_user_id (user_id),
|
||||
INDEX idx_exp_type (exp_type),
|
||||
INDEX idx_created_at (created_at),
|
||||
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='经验值历史表';
|
||||
|
||||
-- =====================================================
|
||||
-- 3. 奖章定义表 (badge_definitions)
|
||||
-- 定义所有可获得的奖章及其解锁条件
|
||||
-- =====================================================
|
||||
CREATE TABLE IF NOT EXISTS badge_definitions (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
code VARCHAR(50) NOT NULL COMMENT '奖章编码(唯一标识)',
|
||||
name VARCHAR(100) NOT NULL COMMENT '奖章名称',
|
||||
description VARCHAR(255) NOT NULL COMMENT '奖章描述',
|
||||
icon VARCHAR(100) NOT NULL DEFAULT 'Medal' COMMENT '图标名称(Element Plus 图标)',
|
||||
category VARCHAR(50) NOT NULL COMMENT '分类:learning/exam/practice/streak/special',
|
||||
condition_type VARCHAR(50) NOT NULL COMMENT '条件类型:count/score/streak/level/duration',
|
||||
condition_field VARCHAR(100) NULL COMMENT '条件字段(用于复杂条件)',
|
||||
condition_value INT NOT NULL DEFAULT 1 COMMENT '条件数值',
|
||||
exp_reward INT NOT NULL DEFAULT 0 COMMENT '解锁奖励经验值',
|
||||
sort_order INT NOT NULL DEFAULT 0 COMMENT '排序顺序',
|
||||
is_active TINYINT(1) NOT NULL DEFAULT 1 COMMENT '是否启用',
|
||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
UNIQUE KEY uk_code (code),
|
||||
INDEX idx_category (category),
|
||||
INDEX idx_is_active (is_active)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='奖章定义表';
|
||||
|
||||
-- =====================================================
|
||||
-- 4. 用户奖章表 (user_badges)
|
||||
-- 记录用户已解锁的奖章
|
||||
-- =====================================================
|
||||
CREATE TABLE IF NOT EXISTS user_badges (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
user_id INT NOT NULL COMMENT '用户ID',
|
||||
badge_id INT NOT NULL COMMENT '奖章ID',
|
||||
unlocked_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '解锁时间',
|
||||
is_notified TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否已通知用户',
|
||||
notified_at DATETIME NULL COMMENT '通知时间',
|
||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
UNIQUE KEY uk_user_badge (user_id, badge_id),
|
||||
INDEX idx_user_id (user_id),
|
||||
INDEX idx_badge_id (badge_id),
|
||||
INDEX idx_unlocked_at (unlocked_at),
|
||||
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (badge_id) REFERENCES badge_definitions(id) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户奖章表';
|
||||
|
||||
-- =====================================================
|
||||
-- 5. 等级配置表 (level_configs)
|
||||
-- 定义每个等级所需的经验值和称号
|
||||
-- =====================================================
|
||||
CREATE TABLE IF NOT EXISTS level_configs (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
level INT NOT NULL COMMENT '等级',
|
||||
exp_required INT NOT NULL COMMENT '升到此级所需经验值',
|
||||
total_exp_required INT NOT NULL COMMENT '累计所需经验值',
|
||||
title VARCHAR(50) NOT NULL COMMENT '等级称号',
|
||||
color VARCHAR(20) NULL COMMENT '等级颜色(十六进制)',
|
||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
UNIQUE KEY uk_level (level)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='等级配置表';
|
||||
|
||||
-- =====================================================
|
||||
-- 6. 插入等级配置数据
|
||||
-- =====================================================
|
||||
INSERT INTO level_configs (level, exp_required, total_exp_required, title, color) VALUES
|
||||
(1, 0, 0, '初学者', '#909399'),
|
||||
(2, 100, 100, '入门学徒', '#67C23A'),
|
||||
(3, 200, 300, '勤奋学员', '#67C23A'),
|
||||
(4, 400, 700, '进阶学员', '#409EFF'),
|
||||
(5, 600, 1300, '优秀学员', '#409EFF'),
|
||||
(6, 1000, 2300, '精英学员', '#E6A23C'),
|
||||
(7, 1500, 3800, '资深学员', '#E6A23C'),
|
||||
(8, 2000, 5800, '学习达人', '#F56C6C'),
|
||||
(9, 3000, 8800, '学霸', '#F56C6C'),
|
||||
(10, 5000, 13800, '大师', '#9B59B6');
|
||||
|
||||
-- =====================================================
|
||||
-- 7. 插入奖章定义数据
|
||||
-- =====================================================
|
||||
|
||||
-- 7.1 学习进度类奖章
|
||||
INSERT INTO badge_definitions (code, name, description, icon, category, condition_type, condition_field, condition_value, exp_reward, sort_order) VALUES
|
||||
('first_login', '初来乍到', '首次登录系统', 'Star', 'learning', 'count', 'login_count', 1, 10, 101),
|
||||
('course_1', '求知若渴', '完成1门课程学习', 'Reading', 'learning', 'count', 'course_completed', 1, 30, 102),
|
||||
('course_5', '博学多才', '完成5门课程学习', 'Collection', 'learning', 'count', 'course_completed', 5, 100, 103),
|
||||
('course_10', '学识渊博', '完成10门课程学习', 'Files', 'learning', 'count', 'course_completed', 10, 200, 104);
|
||||
|
||||
-- 7.2 考试成绩类奖章
|
||||
INSERT INTO badge_definitions (code, name, description, icon, category, condition_type, condition_field, condition_value, exp_reward, sort_order) VALUES
|
||||
('exam_pass_1', '初试牛刀', '通过1次考试', 'Select', 'exam', 'count', 'exam_passed', 1, 20, 201),
|
||||
('exam_pass_10', '身经百战', '通过10次考试', 'Finished', 'exam', 'count', 'exam_passed', 10, 100, 202),
|
||||
('exam_pass_50', '考试达人', '通过50次考试', 'Trophy', 'exam', 'count', 'exam_passed', 50, 300, 203),
|
||||
('exam_perfect', '完美答卷', '考试获得满分', 'Medal', 'exam', 'score', 'exam_perfect_count', 1, 150, 204),
|
||||
('exam_excellent_10', '学霸之路', '10次考试90分以上', 'TrendCharts', 'exam', 'count', 'exam_excellent', 10, 200, 205);
|
||||
|
||||
-- 7.3 练习时长类奖章
|
||||
INSERT INTO badge_definitions (code, name, description, icon, category, condition_type, condition_field, condition_value, exp_reward, sort_order) VALUES
|
||||
('practice_1h', '初窥门径', '累计练习1小时', 'Clock', 'practice', 'duration', 'practice_hours', 1, 30, 301),
|
||||
('practice_10h', '勤学苦练', '累计练习10小时', 'Timer', 'practice', 'duration', 'practice_hours', 10, 100, 302),
|
||||
('practice_50h', '炉火纯青', '累计练习50小时', 'Stopwatch', 'practice', 'duration', 'practice_hours', 50, 300, 303),
|
||||
('practice_100', '练习狂人', '完成100次练习', 'Operation', 'practice', 'count', 'practice_count', 100, 200, 304);
|
||||
|
||||
-- 7.4 连续打卡类奖章
|
||||
INSERT INTO badge_definitions (code, name, description, icon, category, condition_type, condition_field, condition_value, exp_reward, sort_order) VALUES
|
||||
('streak_3', '小试身手', '连续登录3天', 'Calendar', 'streak', 'streak', 'login_streak', 3, 20, 401),
|
||||
('streak_7', '坚持一周', '连续登录7天', 'Calendar', 'streak', 'streak', 'login_streak', 7, 50, 402),
|
||||
('streak_30', '持之以恒', '连续登录30天', 'Calendar', 'streak', 'streak', 'login_streak', 30, 200, 403),
|
||||
('streak_100', '百日不懈', '连续登录100天', 'Calendar', 'streak', 'streak', 'login_streak', 100, 500, 404);
|
||||
|
||||
-- 7.5 特殊成就类奖章
|
||||
INSERT INTO badge_definitions (code, name, description, icon, category, condition_type, condition_field, condition_value, exp_reward, sort_order) VALUES
|
||||
('level_5', '初露锋芒', '达到5级', 'Rank', 'special', 'level', 'user_level', 5, 100, 501),
|
||||
('level_10', '登峰造极', '达到满级', 'Crown', 'special', 'level', 'user_level', 10, 500, 502),
|
||||
('training_master', '陪练大师', '完成50次陪练', 'Headset', 'special', 'count', 'training_count', 50, 300, 503),
|
||||
('first_perfect_practice', '首次完美', '陪练首次获得90分以上', 'StarFilled', 'special', 'score', 'first_practice_90', 1, 100, 504);
|
||||
|
||||
-- =====================================================
|
||||
-- 8. 为现有用户初始化等级数据
|
||||
-- =====================================================
|
||||
INSERT INTO user_levels (user_id, level, exp, total_exp, login_streak, last_login_date)
|
||||
SELECT
|
||||
id as user_id,
|
||||
1 as level,
|
||||
0 as exp,
|
||||
0 as total_exp,
|
||||
0 as login_streak,
|
||||
NULL as last_login_date
|
||||
FROM users
|
||||
WHERE is_deleted = 0
|
||||
ON DUPLICATE KEY UPDATE updated_at = CURRENT_TIMESTAMP;
|
||||
|
||||
-- 提交事务
|
||||
COMMIT;
|
||||
|
||||
-- =====================================================
|
||||
-- 回滚脚本(如需回滚,执行以下语句)
|
||||
-- =====================================================
|
||||
-- DROP TABLE IF EXISTS user_badges;
|
||||
-- DROP TABLE IF EXISTS badge_definitions;
|
||||
-- DROP TABLE IF EXISTS exp_history;
|
||||
-- DROP TABLE IF EXISTS level_configs;
|
||||
-- DROP TABLE IF EXISTS user_levels;
|
||||
-- =====================================================
|
||||
-- 等级与奖章系统数据库迁移脚本
|
||||
-- 版本: 1.0.0
|
||||
-- 创建时间: 2026-01-29
|
||||
-- 说明: 添加用户等级系统和奖章系统相关表
|
||||
-- =====================================================
|
||||
|
||||
-- 使用事务确保原子性
|
||||
START TRANSACTION;
|
||||
|
||||
-- =====================================================
|
||||
-- 1. 用户等级表 (user_levels)
|
||||
-- 存储用户的等级和经验值信息
|
||||
-- =====================================================
|
||||
CREATE TABLE IF NOT EXISTS user_levels (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
user_id INT NOT NULL COMMENT '用户ID',
|
||||
level INT NOT NULL DEFAULT 1 COMMENT '当前等级',
|
||||
exp INT NOT NULL DEFAULT 0 COMMENT '当前经验值',
|
||||
total_exp INT NOT NULL DEFAULT 0 COMMENT '累计获得经验值',
|
||||
login_streak INT NOT NULL DEFAULT 0 COMMENT '连续登录天数',
|
||||
max_login_streak INT NOT NULL DEFAULT 0 COMMENT '历史最长连续登录天数',
|
||||
last_login_date DATE NULL COMMENT '最后登录日期(用于计算连续登录)',
|
||||
last_checkin_at DATETIME NULL COMMENT '最后签到时间',
|
||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
UNIQUE KEY uk_user_id (user_id),
|
||||
INDEX idx_level (level),
|
||||
INDEX idx_total_exp (total_exp),
|
||||
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户等级表';
|
||||
|
||||
-- =====================================================
|
||||
-- 2. 经验值历史表 (exp_history)
|
||||
-- 记录每次经验值变化的详细信息
|
||||
-- =====================================================
|
||||
CREATE TABLE IF NOT EXISTS exp_history (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
user_id INT NOT NULL COMMENT '用户ID',
|
||||
exp_change INT NOT NULL COMMENT '经验值变化(正为获得,负为扣除)',
|
||||
exp_type VARCHAR(50) NOT NULL COMMENT '类型:exam/practice/training/task/login/badge/other',
|
||||
source_id INT NULL COMMENT '来源记录ID(如考试ID、练习ID等)',
|
||||
description VARCHAR(255) NOT NULL COMMENT '描述',
|
||||
level_before INT NULL COMMENT '变化前等级',
|
||||
level_after INT NULL COMMENT '变化后等级',
|
||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
INDEX idx_user_id (user_id),
|
||||
INDEX idx_exp_type (exp_type),
|
||||
INDEX idx_created_at (created_at),
|
||||
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='经验值历史表';
|
||||
|
||||
-- =====================================================
|
||||
-- 3. 奖章定义表 (badge_definitions)
|
||||
-- 定义所有可获得的奖章及其解锁条件
|
||||
-- =====================================================
|
||||
CREATE TABLE IF NOT EXISTS badge_definitions (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
code VARCHAR(50) NOT NULL COMMENT '奖章编码(唯一标识)',
|
||||
name VARCHAR(100) NOT NULL COMMENT '奖章名称',
|
||||
description VARCHAR(255) NOT NULL COMMENT '奖章描述',
|
||||
icon VARCHAR(100) NOT NULL DEFAULT 'Medal' COMMENT '图标名称(Element Plus 图标)',
|
||||
category VARCHAR(50) NOT NULL COMMENT '分类:learning/exam/practice/streak/special',
|
||||
condition_type VARCHAR(50) NOT NULL COMMENT '条件类型:count/score/streak/level/duration',
|
||||
condition_field VARCHAR(100) NULL COMMENT '条件字段(用于复杂条件)',
|
||||
condition_value INT NOT NULL DEFAULT 1 COMMENT '条件数值',
|
||||
exp_reward INT NOT NULL DEFAULT 0 COMMENT '解锁奖励经验值',
|
||||
sort_order INT NOT NULL DEFAULT 0 COMMENT '排序顺序',
|
||||
is_active TINYINT(1) NOT NULL DEFAULT 1 COMMENT '是否启用',
|
||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
UNIQUE KEY uk_code (code),
|
||||
INDEX idx_category (category),
|
||||
INDEX idx_is_active (is_active)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='奖章定义表';
|
||||
|
||||
-- =====================================================
|
||||
-- 4. 用户奖章表 (user_badges)
|
||||
-- 记录用户已解锁的奖章
|
||||
-- =====================================================
|
||||
CREATE TABLE IF NOT EXISTS user_badges (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
user_id INT NOT NULL COMMENT '用户ID',
|
||||
badge_id INT NOT NULL COMMENT '奖章ID',
|
||||
unlocked_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '解锁时间',
|
||||
is_notified TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否已通知用户',
|
||||
notified_at DATETIME NULL COMMENT '通知时间',
|
||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
UNIQUE KEY uk_user_badge (user_id, badge_id),
|
||||
INDEX idx_user_id (user_id),
|
||||
INDEX idx_badge_id (badge_id),
|
||||
INDEX idx_unlocked_at (unlocked_at),
|
||||
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (badge_id) REFERENCES badge_definitions(id) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户奖章表';
|
||||
|
||||
-- =====================================================
|
||||
-- 5. 等级配置表 (level_configs)
|
||||
-- 定义每个等级所需的经验值和称号
|
||||
-- =====================================================
|
||||
CREATE TABLE IF NOT EXISTS level_configs (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
level INT NOT NULL COMMENT '等级',
|
||||
exp_required INT NOT NULL COMMENT '升到此级所需经验值',
|
||||
total_exp_required INT NOT NULL COMMENT '累计所需经验值',
|
||||
title VARCHAR(50) NOT NULL COMMENT '等级称号',
|
||||
color VARCHAR(20) NULL COMMENT '等级颜色(十六进制)',
|
||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
UNIQUE KEY uk_level (level)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='等级配置表';
|
||||
|
||||
-- =====================================================
|
||||
-- 6. 插入等级配置数据
|
||||
-- =====================================================
|
||||
INSERT INTO level_configs (level, exp_required, total_exp_required, title, color) VALUES
|
||||
(1, 0, 0, '初学者', '#909399'),
|
||||
(2, 100, 100, '入门学徒', '#67C23A'),
|
||||
(3, 200, 300, '勤奋学员', '#67C23A'),
|
||||
(4, 400, 700, '进阶学员', '#409EFF'),
|
||||
(5, 600, 1300, '优秀学员', '#409EFF'),
|
||||
(6, 1000, 2300, '精英学员', '#E6A23C'),
|
||||
(7, 1500, 3800, '资深学员', '#E6A23C'),
|
||||
(8, 2000, 5800, '学习达人', '#F56C6C'),
|
||||
(9, 3000, 8800, '学霸', '#F56C6C'),
|
||||
(10, 5000, 13800, '大师', '#9B59B6');
|
||||
|
||||
-- =====================================================
|
||||
-- 7. 插入奖章定义数据
|
||||
-- =====================================================
|
||||
|
||||
-- 7.1 学习进度类奖章
|
||||
INSERT INTO badge_definitions (code, name, description, icon, category, condition_type, condition_field, condition_value, exp_reward, sort_order) VALUES
|
||||
('first_login', '初来乍到', '首次登录系统', 'Star', 'learning', 'count', 'login_count', 1, 10, 101),
|
||||
('course_1', '求知若渴', '完成1门课程学习', 'Reading', 'learning', 'count', 'course_completed', 1, 30, 102),
|
||||
('course_5', '博学多才', '完成5门课程学习', 'Collection', 'learning', 'count', 'course_completed', 5, 100, 103),
|
||||
('course_10', '学识渊博', '完成10门课程学习', 'Files', 'learning', 'count', 'course_completed', 10, 200, 104);
|
||||
|
||||
-- 7.2 考试成绩类奖章
|
||||
INSERT INTO badge_definitions (code, name, description, icon, category, condition_type, condition_field, condition_value, exp_reward, sort_order) VALUES
|
||||
('exam_pass_1', '初试牛刀', '通过1次考试', 'Select', 'exam', 'count', 'exam_passed', 1, 20, 201),
|
||||
('exam_pass_10', '身经百战', '通过10次考试', 'Finished', 'exam', 'count', 'exam_passed', 10, 100, 202),
|
||||
('exam_pass_50', '考试达人', '通过50次考试', 'Trophy', 'exam', 'count', 'exam_passed', 50, 300, 203),
|
||||
('exam_perfect', '完美答卷', '考试获得满分', 'Medal', 'exam', 'score', 'exam_perfect_count', 1, 150, 204),
|
||||
('exam_excellent_10', '学霸之路', '10次考试90分以上', 'TrendCharts', 'exam', 'count', 'exam_excellent', 10, 200, 205);
|
||||
|
||||
-- 7.3 练习时长类奖章
|
||||
INSERT INTO badge_definitions (code, name, description, icon, category, condition_type, condition_field, condition_value, exp_reward, sort_order) VALUES
|
||||
('practice_1h', '初窥门径', '累计练习1小时', 'Clock', 'practice', 'duration', 'practice_hours', 1, 30, 301),
|
||||
('practice_10h', '勤学苦练', '累计练习10小时', 'Timer', 'practice', 'duration', 'practice_hours', 10, 100, 302),
|
||||
('practice_50h', '炉火纯青', '累计练习50小时', 'Stopwatch', 'practice', 'duration', 'practice_hours', 50, 300, 303),
|
||||
('practice_100', '练习狂人', '完成100次练习', 'Operation', 'practice', 'count', 'practice_count', 100, 200, 304);
|
||||
|
||||
-- 7.4 连续打卡类奖章
|
||||
INSERT INTO badge_definitions (code, name, description, icon, category, condition_type, condition_field, condition_value, exp_reward, sort_order) VALUES
|
||||
('streak_3', '小试身手', '连续登录3天', 'Calendar', 'streak', 'streak', 'login_streak', 3, 20, 401),
|
||||
('streak_7', '坚持一周', '连续登录7天', 'Calendar', 'streak', 'streak', 'login_streak', 7, 50, 402),
|
||||
('streak_30', '持之以恒', '连续登录30天', 'Calendar', 'streak', 'streak', 'login_streak', 30, 200, 403),
|
||||
('streak_100', '百日不懈', '连续登录100天', 'Calendar', 'streak', 'streak', 'login_streak', 100, 500, 404);
|
||||
|
||||
-- 7.5 特殊成就类奖章
|
||||
INSERT INTO badge_definitions (code, name, description, icon, category, condition_type, condition_field, condition_value, exp_reward, sort_order) VALUES
|
||||
('level_5', '初露锋芒', '达到5级', 'Rank', 'special', 'level', 'user_level', 5, 100, 501),
|
||||
('level_10', '登峰造极', '达到满级', 'Crown', 'special', 'level', 'user_level', 10, 500, 502),
|
||||
('training_master', '陪练大师', '完成50次陪练', 'Headset', 'special', 'count', 'training_count', 50, 300, 503),
|
||||
('first_perfect_practice', '首次完美', '陪练首次获得90分以上', 'StarFilled', 'special', 'score', 'first_practice_90', 1, 100, 504);
|
||||
|
||||
-- =====================================================
|
||||
-- 8. 为现有用户初始化等级数据
|
||||
-- =====================================================
|
||||
INSERT INTO user_levels (user_id, level, exp, total_exp, login_streak, last_login_date)
|
||||
SELECT
|
||||
id as user_id,
|
||||
1 as level,
|
||||
0 as exp,
|
||||
0 as total_exp,
|
||||
0 as login_streak,
|
||||
NULL as last_login_date
|
||||
FROM users
|
||||
WHERE is_deleted = 0
|
||||
ON DUPLICATE KEY UPDATE updated_at = CURRENT_TIMESTAMP;
|
||||
|
||||
-- 提交事务
|
||||
COMMIT;
|
||||
|
||||
-- =====================================================
|
||||
-- 回滚脚本(如需回滚,执行以下语句)
|
||||
-- =====================================================
|
||||
-- DROP TABLE IF EXISTS user_badges;
|
||||
-- DROP TABLE IF EXISTS badge_definitions;
|
||||
-- DROP TABLE IF EXISTS exp_history;
|
||||
-- DROP TABLE IF EXISTS level_configs;
|
||||
-- DROP TABLE IF EXISTS user_levels;
|
||||
|
||||
@@ -1,186 +1,186 @@
|
||||
-- ============================================================================
|
||||
-- 双人对练功能数据库迁移脚本
|
||||
-- 版本: 2026-01-28
|
||||
-- 功能: 新增对练房间表,扩展现有表支持多人对练
|
||||
-- ============================================================================
|
||||
|
||||
-- ============================================================================
|
||||
-- 1. 创建对练房间表 practice_rooms
|
||||
-- ============================================================================
|
||||
CREATE TABLE IF NOT EXISTS `practice_rooms` (
|
||||
`id` INT AUTO_INCREMENT PRIMARY KEY COMMENT '房间ID',
|
||||
`room_code` VARCHAR(10) NOT NULL UNIQUE COMMENT '6位邀请码',
|
||||
`room_name` VARCHAR(200) COMMENT '房间名称',
|
||||
|
||||
-- 场景信息
|
||||
`scene_id` INT COMMENT '关联场景ID',
|
||||
`scene_name` VARCHAR(200) COMMENT '场景名称',
|
||||
`scene_type` VARCHAR(50) COMMENT '场景类型',
|
||||
`scene_background` TEXT COMMENT '场景背景',
|
||||
|
||||
-- 角色设置
|
||||
`role_a_name` VARCHAR(50) DEFAULT '角色A' COMMENT '角色A名称(如销售顾问)',
|
||||
`role_b_name` VARCHAR(50) DEFAULT '角色B' COMMENT '角色B名称(如顾客)',
|
||||
`role_a_description` TEXT COMMENT '角色A描述',
|
||||
`role_b_description` TEXT COMMENT '角色B描述',
|
||||
|
||||
-- 参与者信息
|
||||
`host_user_id` INT NOT NULL COMMENT '房主用户ID',
|
||||
`guest_user_id` INT COMMENT '加入者用户ID',
|
||||
`host_role` VARCHAR(10) DEFAULT 'A' COMMENT '房主选择的角色(A/B)',
|
||||
`max_participants` INT DEFAULT 2 COMMENT '最大参与人数',
|
||||
|
||||
-- 状态和时间
|
||||
`status` VARCHAR(20) DEFAULT 'waiting' COMMENT '状态: waiting/ready/practicing/completed/canceled',
|
||||
`created_at` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`started_at` DATETIME COMMENT '开始时间',
|
||||
`ended_at` DATETIME COMMENT '结束时间',
|
||||
`duration_seconds` INT DEFAULT 0 COMMENT '对练时长(秒)',
|
||||
|
||||
-- 对话统计
|
||||
`total_turns` INT DEFAULT 0 COMMENT '总对话轮次',
|
||||
`role_a_turns` INT DEFAULT 0 COMMENT '角色A发言次数',
|
||||
`role_b_turns` INT DEFAULT 0 COMMENT '角色B发言次数',
|
||||
|
||||
-- 软删除
|
||||
`is_deleted` TINYINT(1) DEFAULT 0 COMMENT '是否删除',
|
||||
`deleted_at` DATETIME COMMENT '删除时间',
|
||||
|
||||
-- 索引
|
||||
INDEX `idx_room_code` (`room_code`),
|
||||
INDEX `idx_host_user` (`host_user_id`),
|
||||
INDEX `idx_guest_user` (`guest_user_id`),
|
||||
INDEX `idx_status` (`status`),
|
||||
INDEX `idx_created_at` (`created_at`),
|
||||
|
||||
-- 外键(可选,根据实际需求决定是否启用)
|
||||
-- FOREIGN KEY (`scene_id`) REFERENCES `practice_scenes`(`id`) ON DELETE SET NULL,
|
||||
-- FOREIGN KEY (`host_user_id`) REFERENCES `users`(`id`) ON DELETE CASCADE,
|
||||
-- FOREIGN KEY (`guest_user_id`) REFERENCES `users`(`id`) ON DELETE SET NULL
|
||||
|
||||
CONSTRAINT `chk_host_role` CHECK (`host_role` IN ('A', 'B'))
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='双人对练房间表';
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
-- 2. 扩展对话记录表 practice_dialogues
|
||||
-- ============================================================================
|
||||
|
||||
-- 添加用户ID字段(区分说话人)
|
||||
ALTER TABLE `practice_dialogues`
|
||||
ADD COLUMN IF NOT EXISTS `user_id` INT COMMENT '说话人用户ID' AFTER `speaker`;
|
||||
|
||||
-- 添加角色名称字段
|
||||
ALTER TABLE `practice_dialogues`
|
||||
ADD COLUMN IF NOT EXISTS `role_name` VARCHAR(50) COMMENT '角色名称(如销售顾问/顾客)' AFTER `user_id`;
|
||||
|
||||
-- 添加房间ID字段
|
||||
ALTER TABLE `practice_dialogues`
|
||||
ADD COLUMN IF NOT EXISTS `room_id` INT COMMENT '房间ID(双人对练时使用)' AFTER `session_id`;
|
||||
|
||||
-- 添加消息类型字段
|
||||
ALTER TABLE `practice_dialogues`
|
||||
ADD COLUMN IF NOT EXISTS `message_type` VARCHAR(20) DEFAULT 'text' COMMENT '消息类型: text/voice/system' AFTER `content`;
|
||||
|
||||
-- 添加索引
|
||||
ALTER TABLE `practice_dialogues`
|
||||
ADD INDEX IF NOT EXISTS `idx_user_id` (`user_id`),
|
||||
ADD INDEX IF NOT EXISTS `idx_room_id` (`room_id`);
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
-- 3. 扩展会话表 practice_sessions
|
||||
-- ============================================================================
|
||||
|
||||
-- 添加房间ID字段
|
||||
ALTER TABLE `practice_sessions`
|
||||
ADD COLUMN IF NOT EXISTS `room_id` INT COMMENT '房间ID(双人对练时使用)' AFTER `scene_type`;
|
||||
|
||||
-- 添加参与者角色字段
|
||||
ALTER TABLE `practice_sessions`
|
||||
ADD COLUMN IF NOT EXISTS `participant_role` VARCHAR(50) COMMENT '用户在房间中的角色' AFTER `room_id`;
|
||||
|
||||
-- 添加会话类型字段
|
||||
ALTER TABLE `practice_sessions`
|
||||
ADD COLUMN IF NOT EXISTS `session_type` VARCHAR(20) DEFAULT 'solo' COMMENT '会话类型: solo/duo' AFTER `participant_role`;
|
||||
|
||||
-- 添加索引
|
||||
ALTER TABLE `practice_sessions`
|
||||
ADD INDEX IF NOT EXISTS `idx_room_id` (`room_id`);
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
-- 4. 扩展报告表 practice_reports(支持双人报告)
|
||||
-- ============================================================================
|
||||
|
||||
-- 添加房间ID字段
|
||||
ALTER TABLE `practice_reports`
|
||||
ADD COLUMN IF NOT EXISTS `room_id` INT COMMENT '房间ID(双人对练时使用)' AFTER `session_id`;
|
||||
|
||||
-- 添加用户ID字段(双人模式下每人一份报告)
|
||||
ALTER TABLE `practice_reports`
|
||||
ADD COLUMN IF NOT EXISTS `user_id` INT COMMENT '用户ID' AFTER `room_id`;
|
||||
|
||||
-- 添加报告类型字段
|
||||
ALTER TABLE `practice_reports`
|
||||
ADD COLUMN IF NOT EXISTS `report_type` VARCHAR(20) DEFAULT 'solo' COMMENT '报告类型: solo/duo' AFTER `user_id`;
|
||||
|
||||
-- 添加对方评价字段(双人模式)
|
||||
ALTER TABLE `practice_reports`
|
||||
ADD COLUMN IF NOT EXISTS `partner_feedback` JSON COMMENT '对方表现评价' AFTER `suggestions`;
|
||||
|
||||
-- 添加互动质量评分
|
||||
ALTER TABLE `practice_reports`
|
||||
ADD COLUMN IF NOT EXISTS `interaction_score` INT COMMENT '互动质量评分(0-100)' AFTER `partner_feedback`;
|
||||
|
||||
-- 修改唯一索引(允许同一session有多个报告)
|
||||
-- 注意:需要先删除旧的唯一索引
|
||||
-- ALTER TABLE `practice_reports` DROP INDEX `session_id`;
|
||||
-- ALTER TABLE `practice_reports` ADD UNIQUE INDEX `idx_session_user` (`session_id`, `user_id`);
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
-- 5. 创建房间消息表(用于实时同步)
|
||||
-- ============================================================================
|
||||
CREATE TABLE IF NOT EXISTS `practice_room_messages` (
|
||||
`id` BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '消息ID',
|
||||
`room_id` INT NOT NULL COMMENT '房间ID',
|
||||
`user_id` INT COMMENT '发送者用户ID(系统消息为NULL)',
|
||||
`message_type` VARCHAR(20) NOT NULL COMMENT '消息类型: chat/system/join/leave/start/end',
|
||||
`content` TEXT COMMENT '消息内容',
|
||||
`role_name` VARCHAR(50) COMMENT '角色名称',
|
||||
`sequence` INT NOT NULL COMMENT '消息序号',
|
||||
`created_at` DATETIME(3) DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间(毫秒精度)',
|
||||
|
||||
INDEX `idx_room_id` (`room_id`),
|
||||
INDEX `idx_room_sequence` (`room_id`, `sequence`),
|
||||
INDEX `idx_created_at` (`created_at`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='房间实时消息表';
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
-- 回滚脚本(如需回滚,执行以下语句)
|
||||
-- ============================================================================
|
||||
/*
|
||||
-- 删除新增的表
|
||||
DROP TABLE IF EXISTS `practice_room_messages`;
|
||||
DROP TABLE IF EXISTS `practice_rooms`;
|
||||
|
||||
-- 删除 practice_dialogues 新增的列
|
||||
ALTER TABLE `practice_dialogues` DROP COLUMN IF EXISTS `user_id`;
|
||||
ALTER TABLE `practice_dialogues` DROP COLUMN IF EXISTS `role_name`;
|
||||
ALTER TABLE `practice_dialogues` DROP COLUMN IF EXISTS `room_id`;
|
||||
ALTER TABLE `practice_dialogues` DROP COLUMN IF EXISTS `message_type`;
|
||||
|
||||
-- 删除 practice_sessions 新增的列
|
||||
ALTER TABLE `practice_sessions` DROP COLUMN IF EXISTS `room_id`;
|
||||
ALTER TABLE `practice_sessions` DROP COLUMN IF EXISTS `participant_role`;
|
||||
ALTER TABLE `practice_sessions` DROP COLUMN IF EXISTS `session_type`;
|
||||
|
||||
-- 删除 practice_reports 新增的列
|
||||
ALTER TABLE `practice_reports` DROP COLUMN IF EXISTS `room_id`;
|
||||
ALTER TABLE `practice_reports` DROP COLUMN IF EXISTS `user_id`;
|
||||
ALTER TABLE `practice_reports` DROP COLUMN IF EXISTS `report_type`;
|
||||
ALTER TABLE `practice_reports` DROP COLUMN IF EXISTS `partner_feedback`;
|
||||
ALTER TABLE `practice_reports` DROP COLUMN IF EXISTS `interaction_score`;
|
||||
*/
|
||||
-- ============================================================================
|
||||
-- 双人对练功能数据库迁移脚本
|
||||
-- 版本: 2026-01-28
|
||||
-- 功能: 新增对练房间表,扩展现有表支持多人对练
|
||||
-- ============================================================================
|
||||
|
||||
-- ============================================================================
|
||||
-- 1. 创建对练房间表 practice_rooms
|
||||
-- ============================================================================
|
||||
CREATE TABLE IF NOT EXISTS `practice_rooms` (
|
||||
`id` INT AUTO_INCREMENT PRIMARY KEY COMMENT '房间ID',
|
||||
`room_code` VARCHAR(10) NOT NULL UNIQUE COMMENT '6位邀请码',
|
||||
`room_name` VARCHAR(200) COMMENT '房间名称',
|
||||
|
||||
-- 场景信息
|
||||
`scene_id` INT COMMENT '关联场景ID',
|
||||
`scene_name` VARCHAR(200) COMMENT '场景名称',
|
||||
`scene_type` VARCHAR(50) COMMENT '场景类型',
|
||||
`scene_background` TEXT COMMENT '场景背景',
|
||||
|
||||
-- 角色设置
|
||||
`role_a_name` VARCHAR(50) DEFAULT '角色A' COMMENT '角色A名称(如销售顾问)',
|
||||
`role_b_name` VARCHAR(50) DEFAULT '角色B' COMMENT '角色B名称(如顾客)',
|
||||
`role_a_description` TEXT COMMENT '角色A描述',
|
||||
`role_b_description` TEXT COMMENT '角色B描述',
|
||||
|
||||
-- 参与者信息
|
||||
`host_user_id` INT NOT NULL COMMENT '房主用户ID',
|
||||
`guest_user_id` INT COMMENT '加入者用户ID',
|
||||
`host_role` VARCHAR(10) DEFAULT 'A' COMMENT '房主选择的角色(A/B)',
|
||||
`max_participants` INT DEFAULT 2 COMMENT '最大参与人数',
|
||||
|
||||
-- 状态和时间
|
||||
`status` VARCHAR(20) DEFAULT 'waiting' COMMENT '状态: waiting/ready/practicing/completed/canceled',
|
||||
`created_at` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`started_at` DATETIME COMMENT '开始时间',
|
||||
`ended_at` DATETIME COMMENT '结束时间',
|
||||
`duration_seconds` INT DEFAULT 0 COMMENT '对练时长(秒)',
|
||||
|
||||
-- 对话统计
|
||||
`total_turns` INT DEFAULT 0 COMMENT '总对话轮次',
|
||||
`role_a_turns` INT DEFAULT 0 COMMENT '角色A发言次数',
|
||||
`role_b_turns` INT DEFAULT 0 COMMENT '角色B发言次数',
|
||||
|
||||
-- 软删除
|
||||
`is_deleted` TINYINT(1) DEFAULT 0 COMMENT '是否删除',
|
||||
`deleted_at` DATETIME COMMENT '删除时间',
|
||||
|
||||
-- 索引
|
||||
INDEX `idx_room_code` (`room_code`),
|
||||
INDEX `idx_host_user` (`host_user_id`),
|
||||
INDEX `idx_guest_user` (`guest_user_id`),
|
||||
INDEX `idx_status` (`status`),
|
||||
INDEX `idx_created_at` (`created_at`),
|
||||
|
||||
-- 外键(可选,根据实际需求决定是否启用)
|
||||
-- FOREIGN KEY (`scene_id`) REFERENCES `practice_scenes`(`id`) ON DELETE SET NULL,
|
||||
-- FOREIGN KEY (`host_user_id`) REFERENCES `users`(`id`) ON DELETE CASCADE,
|
||||
-- FOREIGN KEY (`guest_user_id`) REFERENCES `users`(`id`) ON DELETE SET NULL
|
||||
|
||||
CONSTRAINT `chk_host_role` CHECK (`host_role` IN ('A', 'B'))
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='双人对练房间表';
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
-- 2. 扩展对话记录表 practice_dialogues
|
||||
-- ============================================================================
|
||||
|
||||
-- 添加用户ID字段(区分说话人)
|
||||
ALTER TABLE `practice_dialogues`
|
||||
ADD COLUMN IF NOT EXISTS `user_id` INT COMMENT '说话人用户ID' AFTER `speaker`;
|
||||
|
||||
-- 添加角色名称字段
|
||||
ALTER TABLE `practice_dialogues`
|
||||
ADD COLUMN IF NOT EXISTS `role_name` VARCHAR(50) COMMENT '角色名称(如销售顾问/顾客)' AFTER `user_id`;
|
||||
|
||||
-- 添加房间ID字段
|
||||
ALTER TABLE `practice_dialogues`
|
||||
ADD COLUMN IF NOT EXISTS `room_id` INT COMMENT '房间ID(双人对练时使用)' AFTER `session_id`;
|
||||
|
||||
-- 添加消息类型字段
|
||||
ALTER TABLE `practice_dialogues`
|
||||
ADD COLUMN IF NOT EXISTS `message_type` VARCHAR(20) DEFAULT 'text' COMMENT '消息类型: text/voice/system' AFTER `content`;
|
||||
|
||||
-- 添加索引
|
||||
ALTER TABLE `practice_dialogues`
|
||||
ADD INDEX IF NOT EXISTS `idx_user_id` (`user_id`),
|
||||
ADD INDEX IF NOT EXISTS `idx_room_id` (`room_id`);
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
-- 3. 扩展会话表 practice_sessions
|
||||
-- ============================================================================
|
||||
|
||||
-- 添加房间ID字段
|
||||
ALTER TABLE `practice_sessions`
|
||||
ADD COLUMN IF NOT EXISTS `room_id` INT COMMENT '房间ID(双人对练时使用)' AFTER `scene_type`;
|
||||
|
||||
-- 添加参与者角色字段
|
||||
ALTER TABLE `practice_sessions`
|
||||
ADD COLUMN IF NOT EXISTS `participant_role` VARCHAR(50) COMMENT '用户在房间中的角色' AFTER `room_id`;
|
||||
|
||||
-- 添加会话类型字段
|
||||
ALTER TABLE `practice_sessions`
|
||||
ADD COLUMN IF NOT EXISTS `session_type` VARCHAR(20) DEFAULT 'solo' COMMENT '会话类型: solo/duo' AFTER `participant_role`;
|
||||
|
||||
-- 添加索引
|
||||
ALTER TABLE `practice_sessions`
|
||||
ADD INDEX IF NOT EXISTS `idx_room_id` (`room_id`);
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
-- 4. 扩展报告表 practice_reports(支持双人报告)
|
||||
-- ============================================================================
|
||||
|
||||
-- 添加房间ID字段
|
||||
ALTER TABLE `practice_reports`
|
||||
ADD COLUMN IF NOT EXISTS `room_id` INT COMMENT '房间ID(双人对练时使用)' AFTER `session_id`;
|
||||
|
||||
-- 添加用户ID字段(双人模式下每人一份报告)
|
||||
ALTER TABLE `practice_reports`
|
||||
ADD COLUMN IF NOT EXISTS `user_id` INT COMMENT '用户ID' AFTER `room_id`;
|
||||
|
||||
-- 添加报告类型字段
|
||||
ALTER TABLE `practice_reports`
|
||||
ADD COLUMN IF NOT EXISTS `report_type` VARCHAR(20) DEFAULT 'solo' COMMENT '报告类型: solo/duo' AFTER `user_id`;
|
||||
|
||||
-- 添加对方评价字段(双人模式)
|
||||
ALTER TABLE `practice_reports`
|
||||
ADD COLUMN IF NOT EXISTS `partner_feedback` JSON COMMENT '对方表现评价' AFTER `suggestions`;
|
||||
|
||||
-- 添加互动质量评分
|
||||
ALTER TABLE `practice_reports`
|
||||
ADD COLUMN IF NOT EXISTS `interaction_score` INT COMMENT '互动质量评分(0-100)' AFTER `partner_feedback`;
|
||||
|
||||
-- 修改唯一索引(允许同一session有多个报告)
|
||||
-- 注意:需要先删除旧的唯一索引
|
||||
-- ALTER TABLE `practice_reports` DROP INDEX `session_id`;
|
||||
-- ALTER TABLE `practice_reports` ADD UNIQUE INDEX `idx_session_user` (`session_id`, `user_id`);
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
-- 5. 创建房间消息表(用于实时同步)
|
||||
-- ============================================================================
|
||||
CREATE TABLE IF NOT EXISTS `practice_room_messages` (
|
||||
`id` BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '消息ID',
|
||||
`room_id` INT NOT NULL COMMENT '房间ID',
|
||||
`user_id` INT COMMENT '发送者用户ID(系统消息为NULL)',
|
||||
`message_type` VARCHAR(20) NOT NULL COMMENT '消息类型: chat/system/join/leave/start/end',
|
||||
`content` TEXT COMMENT '消息内容',
|
||||
`role_name` VARCHAR(50) COMMENT '角色名称',
|
||||
`sequence` INT NOT NULL COMMENT '消息序号',
|
||||
`created_at` DATETIME(3) DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间(毫秒精度)',
|
||||
|
||||
INDEX `idx_room_id` (`room_id`),
|
||||
INDEX `idx_room_sequence` (`room_id`, `sequence`),
|
||||
INDEX `idx_created_at` (`created_at`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='房间实时消息表';
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
-- 回滚脚本(如需回滚,执行以下语句)
|
||||
-- ============================================================================
|
||||
/*
|
||||
-- 删除新增的表
|
||||
DROP TABLE IF EXISTS `practice_room_messages`;
|
||||
DROP TABLE IF EXISTS `practice_rooms`;
|
||||
|
||||
-- 删除 practice_dialogues 新增的列
|
||||
ALTER TABLE `practice_dialogues` DROP COLUMN IF EXISTS `user_id`;
|
||||
ALTER TABLE `practice_dialogues` DROP COLUMN IF EXISTS `role_name`;
|
||||
ALTER TABLE `practice_dialogues` DROP COLUMN IF EXISTS `room_id`;
|
||||
ALTER TABLE `practice_dialogues` DROP COLUMN IF EXISTS `message_type`;
|
||||
|
||||
-- 删除 practice_sessions 新增的列
|
||||
ALTER TABLE `practice_sessions` DROP COLUMN IF EXISTS `room_id`;
|
||||
ALTER TABLE `practice_sessions` DROP COLUMN IF EXISTS `participant_role`;
|
||||
ALTER TABLE `practice_sessions` DROP COLUMN IF EXISTS `session_type`;
|
||||
|
||||
-- 删除 practice_reports 新增的列
|
||||
ALTER TABLE `practice_reports` DROP COLUMN IF EXISTS `room_id`;
|
||||
ALTER TABLE `practice_reports` DROP COLUMN IF EXISTS `user_id`;
|
||||
ALTER TABLE `practice_reports` DROP COLUMN IF EXISTS `report_type`;
|
||||
ALTER TABLE `practice_reports` DROP COLUMN IF EXISTS `partner_feedback`;
|
||||
ALTER TABLE `practice_reports` DROP COLUMN IF EXISTS `interaction_score`;
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user