feat: 实现 KPL 系统功能改进计划
Some checks failed
continuous-integration/drone/push Build is failing

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:
yuliang_guo
2026-01-30 14:22:35 +08:00
parent 9793013a56
commit 64f5d567fa
66 changed files with 18067 additions and 14330 deletions

View File

@@ -1,261 +1,261 @@
# KPL 考培练系统 功能迭代更新日志
**日期**: 2026-01-29
**版本**: v1.5.0
---
## 一、奖章条件优化
### 修复内容
- 修复 `badge_service.py` 中统计查询的 SQL 语法问题
- 将非标准 `func.if_` 替换为 SQLAlchemy 标准 `case` 语句
- 优化考试统计逻辑:通过数、满分数、优秀数分开查询
- 添加 `func.coalesce` 处理空值
### 新增功能
- `check_badges_by_category()` - 按类别检查奖章
- `check_exam_badges()` - 考试后触发
- `check_practice_badges()` - 练习后触发
- `check_streak_badges()` - 签到后触发
- `check_level_badges()` - 等级变化后触发
### 文件变更
- `backend/app/services/badge_service.py`
---
## 二、移动端适配
### 适配页面
| 页面 | 文件 | 适配要点 |
|------|------|----------|
| 登录页 | `views/login/index.vue` | 表单全宽、背景动画隐藏、钉钉安全区域 |
| 课程中心 | `views/trainee/course-center.vue` | 单列卡片、分类横向滚动、操作按钮网格化 |
| 课程详情 | `views/trainee/course-detail.vue` | 侧边栏折叠、视频自适应、工具栏响应式 |
| 考试页面 | `views/exam/practice.vue` | 选项垂直排列、按钮放大、弹窗全屏 |
| 成长路径 | `views/trainee/growth-path.vue` | 雷达图缩放、卡片堆叠、统计区折叠 |
| 排行榜 | `views/trainee/leaderboard.vue` | 列表简化、签到按钮全宽 |
### 技术方案
- 使用 `@media (max-width: 768px)``@media (max-width: 480px)` 断点
- 钉钉环境使用 `env(safe-area-inset-*)` 适配安全区域
---
## 三、证书系统
### 数据库设计
```sql
-- 证书模板表
CREATE TABLE certificate_templates (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(100) NOT NULL,
type ENUM('course', 'exam', 'achievement') NOT NULL,
background_url VARCHAR(500),
template_html TEXT,
template_style TEXT,
is_active BOOLEAN DEFAULT TRUE,
...
);
-- 用户证书表
CREATE TABLE user_certificates (
id INT PRIMARY KEY AUTO_INCREMENT,
user_id INT NOT NULL,
template_id INT NOT NULL,
certificate_no VARCHAR(50) UNIQUE NOT NULL,
title VARCHAR(200) NOT NULL,
...
);
```
### 后端实现
- **模型**: `CertificateTemplate`, `UserCertificate`, `CertificateType`
- **服务**: `CertificateService`
- `issue_course_certificate()` - 颁发课程证书
- `issue_exam_certificate()` - 颁发考试证书
- `issue_achievement_certificate()` - 颁发成就证书
- `generate_certificate_image()` - 生成分享图片
- `get_certificate_by_no()` - 验证证书
### API 端点
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/certificates/me` | 我的证书列表 |
| GET | `/certificates/{id}` | 证书详情 |
| GET | `/certificates/{id}/image` | 获取分享图片 |
| GET | `/certificates/{id}/download` | 下载证书 |
| GET | `/certificates/verify/{no}` | 验证证书(无需登录) |
| POST | `/certificates/issue/course` | 颁发课程证书 |
| POST | `/certificates/issue/exam` | 颁发考试证书 |
### 前端实现
- **API**: `frontend/src/api/certificate.ts`
- **页面**: `frontend/src/views/trainee/my-certificates.vue`
- **功能**: 证书列表、分类筛选、预览、分享、下载
### 文件变更
- `backend/migrations/add_certificate_system.sql` (新增)
- `backend/app/models/certificate.py` (新增)
- `backend/app/services/certificate_service.py` (新增)
- `backend/app/api/v1/endpoints/certificate.py` (新增)
- `backend/app/models/__init__.py` (修改)
- `backend/app/api/v1/__init__.py` (修改)
- `frontend/src/api/certificate.ts` (新增)
- `frontend/src/views/trainee/my-certificates.vue` (新增)
- `frontend/src/router/index.ts` (修改)
---
## 四、数据大屏
### 数据指标
| 类别 | 指标 |
|------|------|
| 概览 | 总学员数、今日活跃、周活跃、月活跃、总学习时长、签到率 |
| 考试 | 总次数、通过率、平均分、满分人数 |
| 部门 | 成员数、通过率、平均学习时长、平均等级 |
| 趋势 | 近7天活跃用户、学习时长、考试次数 |
| 分布 | 1-10级用户数量分布 |
| 动态 | 最新学习活动实时滚动 |
### 后端实现
- **服务**: `DashboardService`
- `get_enterprise_overview()` - 企业级概览
- `get_department_comparison()` - 部门对比
- `get_learning_trend()` - 学习趋势
- `get_level_distribution()` - 等级分布
- `get_realtime_activities()` - 实时动态
- `get_team_dashboard()` - 团队级数据
- `get_course_ranking()` - 课程热度排行
### API 端点
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/dashboard/enterprise/overview` | 企业概览 |
| GET | `/dashboard/enterprise/departments` | 部门对比 |
| GET | `/dashboard/enterprise/trend` | 学习趋势 |
| GET | `/dashboard/enterprise/level-distribution` | 等级分布 |
| GET | `/dashboard/enterprise/activities` | 实时动态 |
| GET | `/dashboard/enterprise/course-ranking` | 课程排行 |
| GET | `/dashboard/team` | 团队数据 |
| GET | `/dashboard/all` | 完整数据(一次性) |
### 前端实现
- **API**: `frontend/src/api/dashboard.ts`
- **页面**: `frontend/src/views/admin/data-dashboard.vue`
- **图表**: ECharts (横向柱状图、折线图、仪表盘、饼图)
- **功能**: 全屏模式、5分钟自动刷新、响应式布局
### 文件变更
- `backend/app/services/dashboard_service.py` (新增)
- `backend/app/api/v1/endpoints/dashboard.py` (新增)
- `backend/app/api/v1/__init__.py` (修改)
- `frontend/src/api/dashboard.ts` (新增)
- `frontend/src/views/admin/data-dashboard.vue` (新增)
- `frontend/src/router/index.ts` (修改)
---
## 部署说明
### 数据库迁移
需执行以下 SQL 脚本:
```bash
# 证书系统迁移
mysql -u root -p kaopeilian < backend/migrations/add_certificate_system.sql
```
### 依赖安装
后端新增依赖(用于证书图片生成):
```bash
pip install Pillow qrcode
```
### 路由变更
新增前端路由:
- `/trainee/my-certificates` - 我的证书
- `/manager/data-dashboard` - 数据大屏
---
## 五、错误提示优化(下午更新)
### 课程名重复错误优化
**问题**:创建课程时名称重复返回 409 错误,前端提示不明确
**修复内容**
1. **后端** `course_service.py`:课程名重复时返回 `existing_id``existing_name`
2. **前端** `edit-course.vue`:检测 409 错误后弹出确认框
- "查看已有课程" → 跳转到已存在的课程
- "修改名称" → 留在当前页面
### 通用错误处理增强
- `errorHandler.ts` 新增 409 冲突错误处理
- 新增工具函数:`isConflictError()`, `getConflictDetail()`, `getConflictMessage()`
### 其他页面错误提示优化
- `position-management.vue`:岗位创建/编辑错误提取详细信息
- `user-management.vue`:用户编辑错误提取详细信息
### 文件变更
- `backend/app/services/course_service.py` (修改)
- `frontend/src/utils/errorHandler.ts` (修改)
- `frontend/src/views/manager/edit-course.vue` (修改)
- `frontend/src/views/admin/position-management.vue` (修改)
- `frontend/src/views/admin/user-management.vue` (修改)
---
## 六、部署问题修复(下午更新)
### 后端导入路径修复
1. `certificate.py` 模型:`from app.core.database import Base``from app.models.base import Base`
2. `certificate.py` API`from app.core.database import get_db``from app.core.deps import get_db`
3. `dashboard.py` API同上
4. 合并 `get_current_user` 导入到 `app.core.deps`
### 依赖安装
```bash
docker exec kpl-backend-dev pip install Pillow qrcode
```
### 前端构建同步问题
- 问题:构建输出到 `/root/aiedu/frontend/dist/`,但容器挂载的是 `/root/aiedu/dist-test/`
- 解决:构建后需手动同步 `cp -r /root/aiedu/frontend/dist/* /root/aiedu/dist-test/`
---
## 七、钉钉免密登录问题修复(下午更新)
### 问题现象
- 钉钉环境打开应用后显示"没有访问此页面的权限"
- 后端日志显示登录实际成功
### 问题原因
登录成功后读取 URL 中的 `redirect` 参数跳转,但该参数指向用户无权限的页面(如 /admin/*
### 修复内容
`login/index.vue`:登录成功后检查 redirect 目标是否有权限
```javascript
// 检查 redirect 目标是否有权限访问
if ((redirect.startsWith('/admin') && userRole !== 'admin') ||
(redirect.startsWith('/manager') && !['admin', 'manager'].includes(userRole))) {
redirect = defaultRoute // 改为跳转到默认页面
}
```
### 调试工具
- 钉钉环境自动启用 vConsole`main.ts` 中根据 UA 判断)
- 依赖:`npm install vconsole`
---
## 待办事项
- [ ] 证书 PDF 生成(需安装 weasyprint
- [ ] 课程完成进度追踪user_course_progress 表)
- [ ] 数据大屏数据缓存优化
- [ ] 钉钉环境下底部导航适配
- [ ] 移除 vConsole 调试代码(问题确认解决后)
# KPL 考培练系统 功能迭代更新日志
**日期**: 2026-01-29
**版本**: v1.5.0
---
## 一、奖章条件优化
### 修复内容
- 修复 `badge_service.py` 中统计查询的 SQL 语法问题
- 将非标准 `func.if_` 替换为 SQLAlchemy 标准 `case` 语句
- 优化考试统计逻辑:通过数、满分数、优秀数分开查询
- 添加 `func.coalesce` 处理空值
### 新增功能
- `check_badges_by_category()` - 按类别检查奖章
- `check_exam_badges()` - 考试后触发
- `check_practice_badges()` - 练习后触发
- `check_streak_badges()` - 签到后触发
- `check_level_badges()` - 等级变化后触发
### 文件变更
- `backend/app/services/badge_service.py`
---
## 二、移动端适配
### 适配页面
| 页面 | 文件 | 适配要点 |
|------|------|----------|
| 登录页 | `views/login/index.vue` | 表单全宽、背景动画隐藏、钉钉安全区域 |
| 课程中心 | `views/trainee/course-center.vue` | 单列卡片、分类横向滚动、操作按钮网格化 |
| 课程详情 | `views/trainee/course-detail.vue` | 侧边栏折叠、视频自适应、工具栏响应式 |
| 考试页面 | `views/exam/practice.vue` | 选项垂直排列、按钮放大、弹窗全屏 |
| 成长路径 | `views/trainee/growth-path.vue` | 雷达图缩放、卡片堆叠、统计区折叠 |
| 排行榜 | `views/trainee/leaderboard.vue` | 列表简化、签到按钮全宽 |
### 技术方案
- 使用 `@media (max-width: 768px)``@media (max-width: 480px)` 断点
- 钉钉环境使用 `env(safe-area-inset-*)` 适配安全区域
---
## 三、证书系统
### 数据库设计
```sql
-- 证书模板表
CREATE TABLE certificate_templates (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(100) NOT NULL,
type ENUM('course', 'exam', 'achievement') NOT NULL,
background_url VARCHAR(500),
template_html TEXT,
template_style TEXT,
is_active BOOLEAN DEFAULT TRUE,
...
);
-- 用户证书表
CREATE TABLE user_certificates (
id INT PRIMARY KEY AUTO_INCREMENT,
user_id INT NOT NULL,
template_id INT NOT NULL,
certificate_no VARCHAR(50) UNIQUE NOT NULL,
title VARCHAR(200) NOT NULL,
...
);
```
### 后端实现
- **模型**: `CertificateTemplate`, `UserCertificate`, `CertificateType`
- **服务**: `CertificateService`
- `issue_course_certificate()` - 颁发课程证书
- `issue_exam_certificate()` - 颁发考试证书
- `issue_achievement_certificate()` - 颁发成就证书
- `generate_certificate_image()` - 生成分享图片
- `get_certificate_by_no()` - 验证证书
### API 端点
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/certificates/me` | 我的证书列表 |
| GET | `/certificates/{id}` | 证书详情 |
| GET | `/certificates/{id}/image` | 获取分享图片 |
| GET | `/certificates/{id}/download` | 下载证书 |
| GET | `/certificates/verify/{no}` | 验证证书(无需登录) |
| POST | `/certificates/issue/course` | 颁发课程证书 |
| POST | `/certificates/issue/exam` | 颁发考试证书 |
### 前端实现
- **API**: `frontend/src/api/certificate.ts`
- **页面**: `frontend/src/views/trainee/my-certificates.vue`
- **功能**: 证书列表、分类筛选、预览、分享、下载
### 文件变更
- `backend/migrations/add_certificate_system.sql` (新增)
- `backend/app/models/certificate.py` (新增)
- `backend/app/services/certificate_service.py` (新增)
- `backend/app/api/v1/endpoints/certificate.py` (新增)
- `backend/app/models/__init__.py` (修改)
- `backend/app/api/v1/__init__.py` (修改)
- `frontend/src/api/certificate.ts` (新增)
- `frontend/src/views/trainee/my-certificates.vue` (新增)
- `frontend/src/router/index.ts` (修改)
---
## 四、数据大屏
### 数据指标
| 类别 | 指标 |
|------|------|
| 概览 | 总学员数、今日活跃、周活跃、月活跃、总学习时长、签到率 |
| 考试 | 总次数、通过率、平均分、满分人数 |
| 部门 | 成员数、通过率、平均学习时长、平均等级 |
| 趋势 | 近7天活跃用户、学习时长、考试次数 |
| 分布 | 1-10级用户数量分布 |
| 动态 | 最新学习活动实时滚动 |
### 后端实现
- **服务**: `DashboardService`
- `get_enterprise_overview()` - 企业级概览
- `get_department_comparison()` - 部门对比
- `get_learning_trend()` - 学习趋势
- `get_level_distribution()` - 等级分布
- `get_realtime_activities()` - 实时动态
- `get_team_dashboard()` - 团队级数据
- `get_course_ranking()` - 课程热度排行
### API 端点
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/dashboard/enterprise/overview` | 企业概览 |
| GET | `/dashboard/enterprise/departments` | 部门对比 |
| GET | `/dashboard/enterprise/trend` | 学习趋势 |
| GET | `/dashboard/enterprise/level-distribution` | 等级分布 |
| GET | `/dashboard/enterprise/activities` | 实时动态 |
| GET | `/dashboard/enterprise/course-ranking` | 课程排行 |
| GET | `/dashboard/team` | 团队数据 |
| GET | `/dashboard/all` | 完整数据(一次性) |
### 前端实现
- **API**: `frontend/src/api/dashboard.ts`
- **页面**: `frontend/src/views/admin/data-dashboard.vue`
- **图表**: ECharts (横向柱状图、折线图、仪表盘、饼图)
- **功能**: 全屏模式、5分钟自动刷新、响应式布局
### 文件变更
- `backend/app/services/dashboard_service.py` (新增)
- `backend/app/api/v1/endpoints/dashboard.py` (新增)
- `backend/app/api/v1/__init__.py` (修改)
- `frontend/src/api/dashboard.ts` (新增)
- `frontend/src/views/admin/data-dashboard.vue` (新增)
- `frontend/src/router/index.ts` (修改)
---
## 部署说明
### 数据库迁移
需执行以下 SQL 脚本:
```bash
# 证书系统迁移
mysql -u root -p kaopeilian < backend/migrations/add_certificate_system.sql
```
### 依赖安装
后端新增依赖(用于证书图片生成):
```bash
pip install Pillow qrcode
```
### 路由变更
新增前端路由:
- `/trainee/my-certificates` - 我的证书
- `/manager/data-dashboard` - 数据大屏
---
## 五、错误提示优化(下午更新)
### 课程名重复错误优化
**问题**:创建课程时名称重复返回 409 错误,前端提示不明确
**修复内容**
1. **后端** `course_service.py`:课程名重复时返回 `existing_id``existing_name`
2. **前端** `edit-course.vue`:检测 409 错误后弹出确认框
- "查看已有课程" → 跳转到已存在的课程
- "修改名称" → 留在当前页面
### 通用错误处理增强
- `errorHandler.ts` 新增 409 冲突错误处理
- 新增工具函数:`isConflictError()`, `getConflictDetail()`, `getConflictMessage()`
### 其他页面错误提示优化
- `position-management.vue`:岗位创建/编辑错误提取详细信息
- `user-management.vue`:用户编辑错误提取详细信息
### 文件变更
- `backend/app/services/course_service.py` (修改)
- `frontend/src/utils/errorHandler.ts` (修改)
- `frontend/src/views/manager/edit-course.vue` (修改)
- `frontend/src/views/admin/position-management.vue` (修改)
- `frontend/src/views/admin/user-management.vue` (修改)
---
## 六、部署问题修复(下午更新)
### 后端导入路径修复
1. `certificate.py` 模型:`from app.core.database import Base``from app.models.base import Base`
2. `certificate.py` API`from app.core.database import get_db``from app.core.deps import get_db`
3. `dashboard.py` API同上
4. 合并 `get_current_user` 导入到 `app.core.deps`
### 依赖安装
```bash
docker exec kpl-backend-dev pip install Pillow qrcode
```
### 前端构建同步问题
- 问题:构建输出到 `/root/aiedu/frontend/dist/`,但容器挂载的是 `/root/aiedu/dist-test/`
- 解决:构建后需手动同步 `cp -r /root/aiedu/frontend/dist/* /root/aiedu/dist-test/`
---
## 七、钉钉免密登录问题修复(下午更新)
### 问题现象
- 钉钉环境打开应用后显示"没有访问此页面的权限"
- 后端日志显示登录实际成功
### 问题原因
登录成功后读取 URL 中的 `redirect` 参数跳转,但该参数指向用户无权限的页面(如 /admin/*
### 修复内容
`login/index.vue`:登录成功后检查 redirect 目标是否有权限
```javascript
// 检查 redirect 目标是否有权限访问
if ((redirect.startsWith('/admin') && userRole !== 'admin') ||
(redirect.startsWith('/manager') && !['admin', 'manager'].includes(userRole))) {
redirect = defaultRoute // 改为跳转到默认页面
}
```
### 调试工具
- 钉钉环境自动启用 vConsole`main.ts` 中根据 UA 判断)
- 依赖:`npm install vconsole`
---
## 待办事项
- [ ] 证书 PDF 生成(需安装 weasyprint
- [ ] 课程完成进度追踪user_course_progress 表)
- [ ] 数据大屏数据缓存优化
- [ ] 钉钉环境下底部导航适配
- [ ] 移除 vConsole 调试代码(问题确认解决后)