Compare commits
17 Commits
d0e7990d38
...
main
Author | SHA1 | Date | |
---|---|---|---|
0fc9b4545c | |||
296477bdb0 | |||
a8f6a3864a | |||
2e21140921 | |||
ab6ed0b409 | |||
6eb7e12030 | |||
44a25e65a5 | |||
0e395fef32 | |||
c207a202b8 | |||
4f645c6e78 | |||
a19102f4d4 | |||
5c632a724f | |||
7436928328 | |||
7f48021452 | |||
3c40fca9b3 | |||
6dd845cdd7 | |||
f990f6b7a3 |
3
.gitignore
vendored
3
.gitignore
vendored
@ -31,3 +31,6 @@ build/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
||||
|
||||
# 忽略 .DS_Store 文件
|
||||
.DS_Store
|
549
API文档.md
549
API文档.md
@ -1,549 +0,0 @@
|
||||
# 进度管理系统 API 文档
|
||||
|
||||
## 基础信息
|
||||
|
||||
- 基础 URL: `http://localhost:1218`
|
||||
- 所有请求和响应均使用 JSON 格式
|
||||
- 所有需要认证的接口都需要在请求头中携带 `Authorization: Bearer {token}`
|
||||
|
||||
## 通用响应格式
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 10000, // 响应码
|
||||
"message": "成功", // 响应消息
|
||||
"data": {} // 响应数据
|
||||
}
|
||||
```
|
||||
|
||||
### 响应码说明
|
||||
|
||||
| 响应码 | 说明 | 消息 |
|
||||
| ------ | ---------- | ---------- |
|
||||
| 10000 | 成功 | 成功 |
|
||||
| 10001 | 参数错误 | 参数无效 |
|
||||
| 10002 | 邮箱重复 | 邮箱已存在 |
|
||||
| 10003 | 用户不存在 | 用户不存在 |
|
||||
| 10004 | 密码错误 | 密码错误 |
|
||||
| 10005 | 邮箱已存在 | 邮箱已存在 |
|
||||
| 10006 | 未授权 | 未授权 |
|
||||
| 10007 | 令牌过期 | 令牌已过期 |
|
||||
| 10008 | 令牌无效 | 无效的令牌 |
|
||||
| 10009 | 系统错误 | 系统错误 |
|
||||
|
||||
## 字段说明
|
||||
|
||||
### 用户相关字段
|
||||
|
||||
1. **角色 (roles)**
|
||||
|
||||
- 1: 教师
|
||||
- 2: 普通管理员
|
||||
- 3: 沟通联络人
|
||||
- 4: 系统管理员
|
||||
|
||||
2. **岗位 (jobs)**
|
||||
|
||||
- 1: 课程制作教师
|
||||
- 2: 课程购买方项目负责人
|
||||
- 3: 课程制作方沟通联络人
|
||||
- 4: 系统制作方项目负责人
|
||||
|
||||
3. **用户状态 (status)**
|
||||
- 1: 正常
|
||||
- 0: 禁用
|
||||
|
||||
### 课程任务相关字段
|
||||
|
||||
1. **进度状态 (progressStatus)**
|
||||
- 0: 脚本上传
|
||||
- 1: 脚本确认
|
||||
- 2: 视频拍摄
|
||||
- 3: 后期制作
|
||||
- 4: 任务完成
|
||||
|
||||
## 用户接口
|
||||
|
||||
### 1. 用户注册
|
||||
|
||||
- **接口**:`POST /api/users`
|
||||
- **描述**:创建新用户
|
||||
- **认证**:不需要
|
||||
- **请求体**:
|
||||
```json
|
||||
{
|
||||
"username": "testuser", // 用户名,不可为空
|
||||
"email": "test@example.com", // 邮箱,不可为空且唯一
|
||||
"password": "password123", // 密码,不可为空
|
||||
"departmentId": 1, // 部门ID,不可为空,关联departments表
|
||||
"roles": 1, // 角色:1-教师,2-普通管理员,3-沟通联络人,4-系统管理员
|
||||
"jobs": 1, // 岗位:1-课程制作教师,2-课程购买方项目负责人,3-课程制作方沟通联络人,4-系统制作方项目负责人
|
||||
"avatar": "http://example.com/avatar.jpg", // 头像URL,可选
|
||||
"creatorId": 1 // 创建者ID,不可为空
|
||||
}
|
||||
```
|
||||
- **成功响应**:
|
||||
```json
|
||||
{
|
||||
"code": 10000,
|
||||
"message": "成功",
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
- **错误响应**:
|
||||
```json
|
||||
{
|
||||
"code": 10005,
|
||||
"message": "邮箱已存在",
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 用户登录
|
||||
|
||||
- **接口**:`POST /api/users/login`
|
||||
- **描述**:用户登录获取 token
|
||||
- **认证**:不需要
|
||||
- **请求体**:
|
||||
```json
|
||||
{
|
||||
"email": "test@example.com", // 邮箱
|
||||
"password": "password123", // 密码
|
||||
"remember": true // 是否记住登录(可选)
|
||||
}
|
||||
```
|
||||
- **成功响应**:
|
||||
```json
|
||||
{
|
||||
"code": 10000,
|
||||
"message": "成功",
|
||||
"data": {
|
||||
"token": "eyJhbGciOiJIUzM4NCJ9..." // JWT令牌
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 用户登出
|
||||
|
||||
- **接口**:`POST /api/users/logout`
|
||||
- **描述**:用户登出,使当前 token 失效
|
||||
- **认证**:需要
|
||||
- **成功响应**:
|
||||
```json
|
||||
{
|
||||
"code": 10000,
|
||||
"message": "成功",
|
||||
"data": "登出成功"
|
||||
}
|
||||
```
|
||||
|
||||
### 4. 获取当前用户信息
|
||||
|
||||
- **接口**:`GET /api/users/current`
|
||||
- **描述**:获取当前登录用户信息
|
||||
- **认证**:需要
|
||||
- **成功响应**:
|
||||
```json
|
||||
{
|
||||
"code": 10000,
|
||||
"message": "成功",
|
||||
"data": {
|
||||
"id": 12,
|
||||
"username": "testuser",
|
||||
"email": "test@example.com",
|
||||
"departmentId": 1,
|
||||
"roles": 1,
|
||||
"jobs": 1,
|
||||
"avatar": null,
|
||||
"creatorId": 1,
|
||||
"status": 1,
|
||||
"createdAt": 1734578081,
|
||||
"updatedAt": 1734578081,
|
||||
"enabled": true,
|
||||
"authorities": [
|
||||
{
|
||||
"authority": "ROLE_USER"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 5. 获取用户列表
|
||||
|
||||
- **接口**:`GET /api/users/list`
|
||||
- **描述**:分页获取用户列表
|
||||
- **认证**:需要
|
||||
- **查询参数**:
|
||||
- `page`: 页码(从 1 开始)
|
||||
- `limit`: 每页数量(默认 10)
|
||||
- **成功响应**:
|
||||
```json
|
||||
{
|
||||
"code": 10000,
|
||||
"message": "成功",
|
||||
"data": {
|
||||
"list": [
|
||||
{
|
||||
"id": 1,
|
||||
"username": "admin",
|
||||
"email": "admin@example.com",
|
||||
"departmentId": 1,
|
||||
"roles": 4,
|
||||
"jobs": 4,
|
||||
"avatar": null,
|
||||
"creatorId": 1,
|
||||
"status": 1,
|
||||
"createdAt": 1734578081,
|
||||
"updatedAt": 1734578081
|
||||
}
|
||||
],
|
||||
"total": 12,
|
||||
"currentPage": 1,
|
||||
"pageSize": 10
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 6. 禁用用户
|
||||
|
||||
- **接口**:`POST /api/users/disable/{userId}`
|
||||
- **描述**:禁用指定用户
|
||||
- **认证**:需要
|
||||
- **路径参数**:
|
||||
- `userId`: 用户 ID
|
||||
- **成功响应**:
|
||||
```json
|
||||
{
|
||||
"code": 10000,
|
||||
"message": "成功",
|
||||
"data": "用户已禁用"
|
||||
}
|
||||
```
|
||||
|
||||
### 7. 查询部门用户列表
|
||||
|
||||
- **接口**:`GET /api/users/department/{departmentId}`
|
||||
- **描述**:获取指定部门下的所有正常状态用户列表
|
||||
- **认证**:需要
|
||||
- **路径参数**:
|
||||
- `departmentId`: 部门 ID
|
||||
- **成功响应**:
|
||||
```json
|
||||
{
|
||||
"code": 10000,
|
||||
"message": "成功",
|
||||
"data": [
|
||||
{
|
||||
"id": 2,
|
||||
"username": "普通管理员账号2",
|
||||
"email": "user2@qq.com",
|
||||
"password": null,
|
||||
"departmentId": 1,
|
||||
"roles": 2,
|
||||
"jobs": 2,
|
||||
"avatar": null,
|
||||
"creatorId": 1,
|
||||
"status": 1,
|
||||
"createdAt": 1734504506,
|
||||
"updatedAt": 1734504506,
|
||||
"enabled": true,
|
||||
"authorities": [
|
||||
{
|
||||
"authority": "ROLE_USER"
|
||||
}
|
||||
],
|
||||
"accountNonExpired": true,
|
||||
"accountNonLocked": true,
|
||||
"credentialsNonExpired": true
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
- **错误响应**:
|
||||
```json
|
||||
{
|
||||
"code": 50000,
|
||||
"message": "获取部门用户列表失败",
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
|
||||
## 课程任务接口
|
||||
|
||||
### 1. 获取课程任务列表
|
||||
|
||||
- **接口**:`GET /api/lesson-tasks`
|
||||
- **描述**:分页获取课程任务列表
|
||||
- **认证**:需要
|
||||
- **查询参数**:
|
||||
- `page`: 页码(从 1 开始)
|
||||
- `size`: 每页数量(默认 10)
|
||||
- `userId`: 用户 ID(可选)
|
||||
- **成功响应**:
|
||||
```json
|
||||
{
|
||||
"code": 10000,
|
||||
"message": "成功",
|
||||
"data": {
|
||||
"content": [
|
||||
{
|
||||
"id": 6,
|
||||
"courseName": "Test Course",
|
||||
"microLessonName": "Test Lesson",
|
||||
"userId": 12,
|
||||
"progressStatus": 1,
|
||||
"scriptUploadTime": 1734578081,
|
||||
"scriptConfirmTime": 1734578081,
|
||||
"videoCaptureTime": 1734578081,
|
||||
"videoConfirmTime": 1734578081,
|
||||
"finishTime": 1734578081,
|
||||
"advise": "Test advice",
|
||||
"createdAt": 1734578081,
|
||||
"updatedAt": 1734578081
|
||||
}
|
||||
],
|
||||
"totalElements": 4,
|
||||
"totalPages": 1,
|
||||
"size": 10,
|
||||
"number": 0,
|
||||
"first": true,
|
||||
"last": true,
|
||||
"empty": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 获取单个课程任务
|
||||
|
||||
- **接口**:`GET /api/lesson-tasks/{id}`
|
||||
- **描述**:获取指定 ID 的课程任务
|
||||
- **认证**:需要
|
||||
- **路径参数**:
|
||||
- `id`: 课程任务 ID
|
||||
- **成功响应**:
|
||||
```json
|
||||
{
|
||||
"code": 10000,
|
||||
"message": "成功",
|
||||
"data": {
|
||||
"id": 6,
|
||||
"courseName": "Test Course",
|
||||
"microLessonName": "Test Lesson",
|
||||
"userId": 12,
|
||||
"progressStatus": 1,
|
||||
"scriptUploadTime": 1734578081,
|
||||
"scriptConfirmTime": 1734578081,
|
||||
"videoCaptureTime": 1734578081,
|
||||
"videoConfirmTime": 1734578081,
|
||||
"finishTime": 1734578081,
|
||||
"advise": "Test advice",
|
||||
"createdAt": 1734578081,
|
||||
"updatedAt": 1734578081
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 创建课程任务
|
||||
|
||||
- **接口**:`POST /api/lesson-tasks`
|
||||
- **描述**:创建新的课程任务
|
||||
- **认证**:需要
|
||||
- **请求体**:
|
||||
```json
|
||||
{
|
||||
"courseName": "Java基础", // 课程名称,不可为空
|
||||
"microLessonName": "Java变量", // 微课名称,不可为空
|
||||
"userId": 1, // 负责人ID,不可为空,关联users表
|
||||
"advise": "请注意讲解速度" // 任务建议或备注,可选
|
||||
}
|
||||
```
|
||||
- **成功响应**:
|
||||
```json
|
||||
{
|
||||
"code": 10000,
|
||||
"message": "成功",
|
||||
"data": {
|
||||
"id": 6,
|
||||
"courseName": "Java基础",
|
||||
"microLessonName": "Java变量",
|
||||
"userId": 1,
|
||||
"progressStatus": 1,
|
||||
"scriptUploadTime": ,
|
||||
"scriptConfirmTime": ,
|
||||
"videoCaptureTime": ,
|
||||
"videoConfirmTime": ,
|
||||
"finishTime": ,
|
||||
"advise": "请注意讲解速度",
|
||||
"createdAt": 1734578081,
|
||||
"updatedAt": 1734578081
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4. 更新课程任务
|
||||
|
||||
- **接口**:`PUT /api/lesson-tasks/{id}`
|
||||
- **描述**:更新指定 ID 的课程任务
|
||||
- **认证**:需要
|
||||
- **路径参数**:
|
||||
- `id`: 课程任务 ID
|
||||
- **请求体**:
|
||||
```json
|
||||
{
|
||||
"courseName": "Updated Course", // 课程名称
|
||||
"microLessonName": "Updated Lesson", // 微课名称
|
||||
"userId": 12, // 用户ID
|
||||
"progressStatus": 2, // 进度状态
|
||||
"scriptUploadTime": 1734578081, // 脚本上传时间
|
||||
"scriptConfirmTime": 1734578081, // 脚本确认时间
|
||||
"videoCaptureTime": 1734578081, // 视频录制时间
|
||||
"videoConfirmTime": 1734578081, // 视频确认时间
|
||||
"finishTime": 1734578081, // 完成时间
|
||||
"advise": "Updated advice" // 建议
|
||||
}
|
||||
```
|
||||
- **成功响应**:
|
||||
```json
|
||||
{
|
||||
"code": 10000,
|
||||
"message": "成功",
|
||||
"data": {
|
||||
"id": 6,
|
||||
"courseName": "Updated Course",
|
||||
"microLessonName": "Updated Lesson",
|
||||
"userId": 12,
|
||||
"progressStatus": 2,
|
||||
"scriptUploadTime": 1734578081,
|
||||
"scriptConfirmTime": 1734578081,
|
||||
"videoCaptureTime": 1734578081,
|
||||
"videoConfirmTime": 1734578081,
|
||||
"finishTime": 1734578081,
|
||||
"advise": "Updated advice",
|
||||
"createdAt": 1734578081,
|
||||
"updatedAt": 1734578081
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 5. 删除课程任务
|
||||
|
||||
- **接口**:`DELETE /api/lesson-tasks/{id}`
|
||||
- **描述**:删除指定 ID 的课程任务
|
||||
- **认证**:需要
|
||||
- **路径参数**:
|
||||
- `id`: 课程任务 ID
|
||||
- **成功响应**:
|
||||
```json
|
||||
{
|
||||
"code": 10000,
|
||||
"message": "成功",
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
|
||||
### 6. 按部门 ID 查询课程任务
|
||||
|
||||
- **接口**:`GET /api/lesson-tasks/department/{departmentId}`
|
||||
- **描述**:获取指定部门下正常状态用户的课程任务列表(分页)
|
||||
- **认证**:需要
|
||||
- **路径参数**:
|
||||
- `departmentId`: 部门 ID
|
||||
- **查询参数**:
|
||||
- `page`: 页码(从 1 开始)
|
||||
- `size`: 每页数量(默认 10)
|
||||
- **成功响应**:
|
||||
```json
|
||||
{
|
||||
"code": 10000,
|
||||
"message": "成功",
|
||||
"data": {
|
||||
"content": [
|
||||
{
|
||||
"id": 1,
|
||||
"courseName": "物理",
|
||||
"microLessonName": "微课1-1",
|
||||
"userId": 1,
|
||||
"username": "教师账号1", // 新增:用户名字段
|
||||
"progressStatus": 4,
|
||||
"scriptUploadTime": 1734498510,
|
||||
"scriptConfirmTime": 1734498510,
|
||||
"videoCaptureTime": 1734498510,
|
||||
"videoConfirmTime": 1734498510,
|
||||
"finishTime": 1734498510,
|
||||
"advise": null,
|
||||
"createdAt": 1734578081,
|
||||
"updatedAt": 1734580393
|
||||
}
|
||||
],
|
||||
"totalElements": 10,
|
||||
"totalPages": 1,
|
||||
"size": 10,
|
||||
"number": 0,
|
||||
"first": true,
|
||||
"last": true,
|
||||
"empty": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 7. 更新课程任务进度
|
||||
|
||||
- **接口**:`PUT /api/lesson-tasks/{id}`
|
||||
- **描述**:更新课程任务的进度状态和建议
|
||||
- **认证**:需要
|
||||
- **路径参数**:
|
||||
- `id`: 课程任务 ID
|
||||
- **请求体**:
|
||||
```json
|
||||
{
|
||||
"progressStatus": 2, // 进度状态(可选)
|
||||
"advise": "{\"method\":\"wechat\",\"uploaded\":true}" // 建议(可选)
|
||||
}
|
||||
```
|
||||
- **说明**:
|
||||
- 更新进度状态时会自动更新对应的时间戳:
|
||||
- 状态 1:更新 scriptUploadTime
|
||||
- 状态 2:更新 scriptConfirmTime
|
||||
- 状态 3:更新 videoCaptureTime
|
||||
- 状态 4:更新 videoConfirmTime
|
||||
- 状态 5:更新 finishTime
|
||||
- 只会更新请求体中包含的字段,未提供的字段保持不变
|
||||
- **成功响应**:
|
||||
```json
|
||||
{
|
||||
"code": 10000,
|
||||
"message": "成功",
|
||||
"data": {
|
||||
"id": 10,
|
||||
"courseName": "测试课程",
|
||||
"microLessonName": "测试微课",
|
||||
"userId": 1,
|
||||
"progressStatus": 2,
|
||||
"scriptUploadTime": null,
|
||||
"scriptConfirmTime": 1734663755,
|
||||
"videoCaptureTime": null,
|
||||
"videoConfirmTime": null,
|
||||
"finishTime": null,
|
||||
"advise": "{\"method\":\"wechat\",\"uploaded\":true}",
|
||||
"createdAt": 1734602440,
|
||||
"updatedAt": 1734663755
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. 所有需要认证的接口必须在请求头中携带有效的 JWT 令牌
|
||||
2. 所有时间戳字段均为秒级时间戳
|
||||
3. 分页接口的页码从 1 开始
|
||||
4. 用户密码在传输和存储时都会进行加密处理
|
||||
5. 课程任务的 progressStatus 字段状态码说明:
|
||||
- 0: 脚本上传
|
||||
- 1: 脚本确认
|
||||
- 2: 视频拍摄
|
||||
- 3: 后期制作
|
||||
- 4: 任务完成
|
||||
6. 用户状态说明:
|
||||
- 1: 正常
|
||||
- 0: 禁用
|
13
pom.xml
13
pom.xml
@ -92,6 +92,19 @@
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-pool2</artifactId>
|
||||
</dependency>
|
||||
<!-- EasyExcel -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>easyexcel</artifactId>
|
||||
<version>3.3.2</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Apache Commons Lang3 -->
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>3.12.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
@ -1,14 +1,68 @@
|
||||
package com.huertian.jinduguanli.common;
|
||||
|
||||
public class ErrorCode {
|
||||
/**
|
||||
* 操作成功
|
||||
*/
|
||||
public static final int SUCCESS = 10000;
|
||||
|
||||
/**
|
||||
* 系统内部错误
|
||||
*/
|
||||
public static final int SYSTEM_ERROR = 10001;
|
||||
|
||||
/**
|
||||
* 参数校验错误
|
||||
*/
|
||||
public static final int INVALID_PARAM = 10002;
|
||||
|
||||
/**
|
||||
* 用户不存在
|
||||
*/
|
||||
public static final int USER_NOT_FOUND = 10003;
|
||||
|
||||
/**
|
||||
* 用户不存在或已被禁用
|
||||
*/
|
||||
public static final int USER_NOT_FOUND_OR_DISABLED = 10004;
|
||||
|
||||
/**
|
||||
* 邮箱已存在
|
||||
*/
|
||||
public static final int EMAIL_EXISTS = 10005;
|
||||
public static final int PASSWORD_INCORRECT = 10006;
|
||||
|
||||
/**
|
||||
* 业务逻辑错误
|
||||
*/
|
||||
public static final int BUSINESS_ERROR = 10006;
|
||||
|
||||
/**
|
||||
* 无效的令牌
|
||||
*/
|
||||
public static final int INVALID_TOKEN = 10007;
|
||||
|
||||
/**
|
||||
* 未授权访问
|
||||
*/
|
||||
public static final int UNAUTHORIZED = 10008;
|
||||
|
||||
/**
|
||||
* 用户名已存在
|
||||
*/
|
||||
public static final int USERNAME_EXISTS = 10009;
|
||||
|
||||
/**
|
||||
* 密码错误
|
||||
*/
|
||||
public static final int PASSWORD_INCORRECT = 10010;
|
||||
|
||||
/**
|
||||
* 参数为空
|
||||
*/
|
||||
public static final int PARAM_IS_BLANK = 10011;
|
||||
|
||||
/**
|
||||
* 参数错误
|
||||
*/
|
||||
public static final int PARAM_ERROR = 10012;
|
||||
}
|
||||
|
@ -52,6 +52,7 @@ public class RedisConfig {
|
||||
.build();
|
||||
}
|
||||
|
||||
@SuppressWarnings("null")
|
||||
@Bean
|
||||
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
|
||||
RedisTemplate<String, Object> template = new RedisTemplate<>();
|
||||
@ -59,15 +60,15 @@ public class RedisConfig {
|
||||
|
||||
// 使用 GenericJackson2JsonRedisSerializer 进行序列化
|
||||
GenericJackson2JsonRedisSerializer jackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
|
||||
|
||||
|
||||
// 设置 key 的序列化方式
|
||||
template.setKeySerializer(new StringRedisSerializer());
|
||||
template.setHashKeySerializer(new StringRedisSerializer());
|
||||
|
||||
|
||||
// 设置 value 的序列化方式
|
||||
template.setValueSerializer(jackson2JsonRedisSerializer);
|
||||
template.setHashValueSerializer(jackson2JsonRedisSerializer);
|
||||
|
||||
|
||||
template.afterPropertiesSet();
|
||||
|
||||
// 测试连接
|
||||
@ -77,7 +78,7 @@ public class RedisConfig {
|
||||
} catch (Exception e) {
|
||||
logger.error("Redis连接测试失败: {}", e.getMessage(), e);
|
||||
}
|
||||
|
||||
|
||||
return template;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,134 @@
|
||||
package com.huertian.jinduguanli.controller;
|
||||
|
||||
import com.alibaba.excel.EasyExcel;
|
||||
import com.huertian.jinduguanli.common.ApiResponse;
|
||||
import com.huertian.jinduguanli.common.ErrorCode;
|
||||
import com.huertian.jinduguanli.dto.DepartmentImportDTO;
|
||||
import com.huertian.jinduguanli.dto.LessonTaskImportDTO;
|
||||
import com.huertian.jinduguanli.dto.UserImportDTO;
|
||||
import com.huertian.jinduguanli.service.DepartmentService;
|
||||
import com.huertian.jinduguanli.service.LessonTaskService;
|
||||
import com.huertian.jinduguanli.service.UserService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestHeader;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/api/import")
|
||||
public class ImportController {
|
||||
|
||||
@Autowired
|
||||
private UserService userService;
|
||||
|
||||
@Autowired
|
||||
private LessonTaskService lessonTaskService;
|
||||
|
||||
@Autowired
|
||||
private DepartmentService departmentService;
|
||||
|
||||
@PostMapping("/users")
|
||||
public ApiResponse<String> importUsers(
|
||||
@RequestParam("file") MultipartFile file,
|
||||
@RequestHeader("Authorization") String authHeader) {
|
||||
if (file.isEmpty()) {
|
||||
return new ApiResponse<>(ErrorCode.INVALID_PARAM, "文件不能为空", null);
|
||||
}
|
||||
|
||||
String originalFilename = file.getOriginalFilename();
|
||||
if (!isValidExcelFile(originalFilename)) {
|
||||
return new ApiResponse<>(ErrorCode.INVALID_PARAM, "只支持.xlsx格式的文件", null);
|
||||
}
|
||||
|
||||
try {
|
||||
log.info("开始导入用户数据,文件名:{}", originalFilename);
|
||||
List<UserImportDTO> userList = EasyExcel.read(file.getInputStream())
|
||||
.head(UserImportDTO.class)
|
||||
.sheet()
|
||||
.doReadSync();
|
||||
log.info("读取到{}条用户数据", userList.size());
|
||||
|
||||
for (int i = 0; i < userList.size(); i++) {
|
||||
log.info("第{}行数据:{}", i + 1, userList.get(i));
|
||||
}
|
||||
|
||||
String token = authHeader.substring(7);
|
||||
return userService.batchImportUsers(userList, token);
|
||||
} catch (Exception e) {
|
||||
log.error("导入用户数据失败", e);
|
||||
return new ApiResponse<>(ErrorCode.SYSTEM_ERROR, "导入用户数据失败:" + e.getMessage(), null);
|
||||
}
|
||||
}
|
||||
|
||||
@PostMapping("/lesson-tasks")
|
||||
public ApiResponse<String> importLessonTasks(@RequestParam("file") MultipartFile file) {
|
||||
if (file.isEmpty()) {
|
||||
return new ApiResponse<>(ErrorCode.INVALID_PARAM, "文件不能为空", null);
|
||||
}
|
||||
|
||||
String originalFilename = file.getOriginalFilename();
|
||||
if (!isValidExcelFile(originalFilename)) {
|
||||
return new ApiResponse<>(ErrorCode.INVALID_PARAM, "只支持.xlsx格式的文件", null);
|
||||
}
|
||||
|
||||
try {
|
||||
log.info("开始导入课程任务数据,文件名:{}", originalFilename);
|
||||
List<LessonTaskImportDTO> taskList = EasyExcel.read(file.getInputStream())
|
||||
.head(LessonTaskImportDTO.class)
|
||||
.sheet()
|
||||
.doReadSync();
|
||||
log.info("读取到{}条课程任务数据", taskList.size());
|
||||
|
||||
for (int i = 0; i < taskList.size(); i++) {
|
||||
log.info("第{}行数据:{}", i + 1, taskList.get(i));
|
||||
}
|
||||
|
||||
return lessonTaskService.batchImportLessonTasks(taskList);
|
||||
} catch (Exception e) {
|
||||
log.error("导入课程任务数据失败", e);
|
||||
return new ApiResponse<>(ErrorCode.SYSTEM_ERROR, "导入课程任务数据失败:" + e.getMessage(), null);
|
||||
}
|
||||
}
|
||||
|
||||
@PostMapping("/departments")
|
||||
public ApiResponse<String> importDepartments(@RequestParam("file") MultipartFile file) {
|
||||
if (file.isEmpty()) {
|
||||
return new ApiResponse<>(ErrorCode.INVALID_PARAM, "文件不能为空", null);
|
||||
}
|
||||
|
||||
String originalFilename = file.getOriginalFilename();
|
||||
if (!isValidExcelFile(originalFilename)) {
|
||||
return new ApiResponse<>(ErrorCode.INVALID_PARAM, "只支持.xlsx格式的文件", null);
|
||||
}
|
||||
|
||||
try {
|
||||
log.info("开始导入部门数据,文件名:{}", originalFilename);
|
||||
List<DepartmentImportDTO> departmentList = EasyExcel.read(file.getInputStream())
|
||||
.head(DepartmentImportDTO.class)
|
||||
.sheet()
|
||||
.doReadSync();
|
||||
log.info("读取到{}条部门数据", departmentList.size());
|
||||
|
||||
for (int i = 0; i < departmentList.size(); i++) {
|
||||
log.info("第{}行数据:{}", i + 1, departmentList.get(i));
|
||||
}
|
||||
|
||||
return departmentService.batchImportDepartments(departmentList);
|
||||
} catch (Exception e) {
|
||||
log.error("导入部门数据失败", e);
|
||||
return new ApiResponse<>(ErrorCode.SYSTEM_ERROR, "导入部门数据失败:" + e.getMessage(), null);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isValidExcelFile(String filename) {
|
||||
return StringUtils.isNotBlank(filename) && filename.toLowerCase().endsWith(".xlsx");
|
||||
}
|
||||
}
|
@ -102,4 +102,15 @@ public class UserController {
|
||||
return new ApiResponse<>(ErrorCode.SYSTEM_ERROR, "获取部门用户列表失败", null);
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/teacher/search")
|
||||
public ApiResponse<User> searchTeacher(@RequestParam String email) {
|
||||
logger.info("收到查询教师信息请求,邮箱: {}", email);
|
||||
try {
|
||||
return userService.findTeacherByEmail(email);
|
||||
} catch (Exception e) {
|
||||
logger.error("查询教师信息失败", e);
|
||||
return new ApiResponse<>(ErrorCode.SYSTEM_ERROR, "查询教师信息失败", null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,28 @@
|
||||
package com.huertian.jinduguanli.dto;
|
||||
|
||||
import com.alibaba.excel.annotation.ExcelProperty;
|
||||
|
||||
public class DepartmentImportDTO {
|
||||
@ExcelProperty("部门名称")
|
||||
private String departmentName;
|
||||
|
||||
@ExcelProperty("部门描述")
|
||||
private String description;
|
||||
|
||||
public String getDepartmentName() {
|
||||
return departmentName;
|
||||
}
|
||||
|
||||
public void setDepartmentName(String departmentName) {
|
||||
this.departmentName = departmentName;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
}
|
@ -11,9 +11,10 @@ public class LessonTaskDTO {
|
||||
private Long userId;
|
||||
private String username; // 添加用户名字段
|
||||
private Integer progressStatus;
|
||||
private Long scriptUploadTime;
|
||||
private Long scriptCreateTime;
|
||||
private Long scriptReviewTime;
|
||||
private Long scriptConfirmTime;
|
||||
private Long videoCaptureTime;
|
||||
private Long videoCreateTime;
|
||||
private Long videoConfirmTime;
|
||||
private Long finishTime;
|
||||
private String advise;
|
||||
@ -29,9 +30,10 @@ public class LessonTaskDTO {
|
||||
dto.setUserId(lessonTask.getUserId());
|
||||
dto.setUsername(username);
|
||||
dto.setProgressStatus(lessonTask.getProgressStatus());
|
||||
dto.setScriptUploadTime(lessonTask.getScriptUploadTime());
|
||||
dto.setScriptCreateTime(lessonTask.getScriptCreateTime());
|
||||
dto.setScriptReviewTime(lessonTask.getScriptReviewTime());
|
||||
dto.setScriptConfirmTime(lessonTask.getScriptConfirmTime());
|
||||
dto.setVideoCaptureTime(lessonTask.getVideoCaptureTime());
|
||||
dto.setVideoCreateTime(lessonTask.getVideoCreateTime());
|
||||
dto.setVideoConfirmTime(lessonTask.getVideoConfirmTime());
|
||||
dto.setFinishTime(lessonTask.getFinishTime());
|
||||
dto.setAdvise(lessonTask.getAdvise());
|
||||
@ -88,12 +90,20 @@ public class LessonTaskDTO {
|
||||
this.progressStatus = progressStatus;
|
||||
}
|
||||
|
||||
public Long getScriptUploadTime() {
|
||||
return scriptUploadTime;
|
||||
public Long getScriptCreateTime() {
|
||||
return scriptCreateTime;
|
||||
}
|
||||
|
||||
public void setScriptUploadTime(Long scriptUploadTime) {
|
||||
this.scriptUploadTime = scriptUploadTime;
|
||||
public void setScriptCreateTime(Long scriptCreateTime) {
|
||||
this.scriptCreateTime = scriptCreateTime;
|
||||
}
|
||||
|
||||
public Long getScriptReviewTime() {
|
||||
return scriptReviewTime;
|
||||
}
|
||||
|
||||
public void setScriptReviewTime(Long scriptReviewTime) {
|
||||
this.scriptReviewTime = scriptReviewTime;
|
||||
}
|
||||
|
||||
public Long getScriptConfirmTime() {
|
||||
@ -104,12 +114,12 @@ public class LessonTaskDTO {
|
||||
this.scriptConfirmTime = scriptConfirmTime;
|
||||
}
|
||||
|
||||
public Long getVideoCaptureTime() {
|
||||
return videoCaptureTime;
|
||||
public Long getVideoCreateTime() {
|
||||
return videoCreateTime;
|
||||
}
|
||||
|
||||
public void setVideoCaptureTime(Long videoCaptureTime) {
|
||||
this.videoCaptureTime = videoCaptureTime;
|
||||
public void setVideoCreateTime(Long videoCreateTime) {
|
||||
this.videoCreateTime = videoCreateTime;
|
||||
}
|
||||
|
||||
public Long getVideoConfirmTime() {
|
||||
|
@ -0,0 +1,38 @@
|
||||
package com.huertian.jinduguanli.dto;
|
||||
|
||||
import com.alibaba.excel.annotation.ExcelProperty;
|
||||
|
||||
public class LessonTaskImportDTO {
|
||||
@ExcelProperty("课程名称")
|
||||
private String lessonName;
|
||||
|
||||
@ExcelProperty("微课名称")
|
||||
private String microLessonName;
|
||||
|
||||
@ExcelProperty("教师账号")
|
||||
private String teacherEmail;
|
||||
|
||||
public String getLessonName() {
|
||||
return lessonName;
|
||||
}
|
||||
|
||||
public void setLessonName(String lessonName) {
|
||||
this.lessonName = lessonName;
|
||||
}
|
||||
|
||||
public String getMicroLessonName() {
|
||||
return microLessonName;
|
||||
}
|
||||
|
||||
public void setMicroLessonName(String microLessonName) {
|
||||
this.microLessonName = microLessonName;
|
||||
}
|
||||
|
||||
public String getTeacherEmail() {
|
||||
return teacherEmail;
|
||||
}
|
||||
|
||||
public void setTeacherEmail(String teacherEmail) {
|
||||
this.teacherEmail = teacherEmail;
|
||||
}
|
||||
}
|
@ -10,9 +10,10 @@ public class LessonTaskRequest {
|
||||
private String microLessonName;
|
||||
private Long userId;
|
||||
private Integer progressStatus;
|
||||
private Long scriptUploadTime;
|
||||
private Long scriptCreateTime;
|
||||
private Long scriptReviewTime;
|
||||
private Long scriptConfirmTime;
|
||||
private Long videoCaptureTime;
|
||||
private Long videoCreateTime;
|
||||
private Long videoConfirmTime;
|
||||
private Long finishTime;
|
||||
private String advise;
|
||||
@ -49,12 +50,20 @@ public class LessonTaskRequest {
|
||||
this.progressStatus = progressStatus;
|
||||
}
|
||||
|
||||
public Long getScriptUploadTime() {
|
||||
return scriptUploadTime;
|
||||
public Long getScriptCreateTime() {
|
||||
return scriptCreateTime;
|
||||
}
|
||||
|
||||
public void setScriptUploadTime(Long scriptUploadTime) {
|
||||
this.scriptUploadTime = scriptUploadTime;
|
||||
public void setScriptCreateTime(Long scriptCreateTime) {
|
||||
this.scriptCreateTime = scriptCreateTime;
|
||||
}
|
||||
|
||||
public Long getScriptReviewTime() {
|
||||
return scriptReviewTime;
|
||||
}
|
||||
|
||||
public void setScriptReviewTime(Long scriptReviewTime) {
|
||||
this.scriptReviewTime = scriptReviewTime;
|
||||
}
|
||||
|
||||
public Long getScriptConfirmTime() {
|
||||
@ -65,12 +74,12 @@ public class LessonTaskRequest {
|
||||
this.scriptConfirmTime = scriptConfirmTime;
|
||||
}
|
||||
|
||||
public Long getVideoCaptureTime() {
|
||||
return videoCaptureTime;
|
||||
public Long getVideoCreateTime() {
|
||||
return videoCreateTime;
|
||||
}
|
||||
|
||||
public void setVideoCaptureTime(Long videoCaptureTime) {
|
||||
this.videoCaptureTime = videoCaptureTime;
|
||||
public void setVideoCreateTime(Long videoCreateTime) {
|
||||
this.videoCreateTime = videoCreateTime;
|
||||
}
|
||||
|
||||
public Long getVideoConfirmTime() {
|
||||
|
@ -0,0 +1,60 @@
|
||||
package com.huertian.jinduguanli.dto;
|
||||
|
||||
import com.alibaba.excel.annotation.ExcelProperty;
|
||||
|
||||
public class UserImportDTO {
|
||||
@ExcelProperty(index = 0)
|
||||
private String username;
|
||||
|
||||
@ExcelProperty(index = 1)
|
||||
private String email;
|
||||
|
||||
@ExcelProperty(index = 2)
|
||||
private String password;
|
||||
|
||||
@ExcelProperty(index = 3)
|
||||
private String departmentName;
|
||||
|
||||
@ExcelProperty(index = 4)
|
||||
private String roleAndJob;
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getDepartmentName() {
|
||||
return departmentName;
|
||||
}
|
||||
|
||||
public void setDepartmentName(String departmentName) {
|
||||
this.departmentName = departmentName;
|
||||
}
|
||||
|
||||
public String getRoleAndJob() {
|
||||
return roleAndJob;
|
||||
}
|
||||
|
||||
public void setRoleAndJob(String roleAndJob) {
|
||||
this.roleAndJob = roleAndJob;
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package com.huertian.jinduguanli.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@Entity
|
||||
@Table(name = "departments")
|
||||
public class Department {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@Column(unique = true)
|
||||
private String name;
|
||||
|
||||
private String description;
|
||||
|
||||
private Long createdAt;
|
||||
|
||||
private Long updatedAt;
|
||||
}
|
@ -32,9 +32,10 @@ public class LessonTask implements Serializable {
|
||||
@Column(nullable = false)
|
||||
private Integer progressStatus;
|
||||
|
||||
private Long scriptUploadTime;
|
||||
private Long scriptCreateTime;
|
||||
private Long scriptReviewTime;
|
||||
private Long scriptConfirmTime;
|
||||
private Long videoCaptureTime;
|
||||
private Long videoCreateTime;
|
||||
private Long videoConfirmTime;
|
||||
private Long finishTime;
|
||||
|
||||
@ -48,9 +49,9 @@ public class LessonTask implements Serializable {
|
||||
|
||||
@PrePersist
|
||||
protected void onCreate() {
|
||||
logger.info("创建新课程任务 - 课程名称: {}, 小课名称: {}, 用户ID: {}",
|
||||
logger.info("创建新课程任务 - 课程名称: {}, 微课名称: {}, 用户ID: {}",
|
||||
this.courseName, this.microLessonName, this.userId);
|
||||
progressStatus = 1;
|
||||
progressStatus = 0;
|
||||
long now = System.currentTimeMillis() / 1000;
|
||||
createdAt = now;
|
||||
updatedAt = now;
|
||||
@ -60,7 +61,7 @@ public class LessonTask implements Serializable {
|
||||
|
||||
@PreUpdate
|
||||
protected void onUpdate() {
|
||||
logger.info("更新课程任务 - ID: {}, 课程名称: {}, 小课名称: {}",
|
||||
logger.info("更新课程任务 - ID: {}, 课程名称: {}, 微课名称: {}",
|
||||
this.id, this.courseName, this.microLessonName);
|
||||
updatedAt = System.currentTimeMillis() / 1000;
|
||||
logger.info("更新课程任务成功 - ID: {}, 更新时间: {}", id, updatedAt);
|
||||
|
@ -47,6 +47,18 @@ public class User implements UserDetails {
|
||||
@Column(name = "updated_at", nullable = false)
|
||||
private Long updatedAt;
|
||||
|
||||
@PrePersist
|
||||
protected void onCreate() {
|
||||
long now = System.currentTimeMillis() / 1000;
|
||||
createdAt = now;
|
||||
updatedAt = now;
|
||||
}
|
||||
|
||||
@PreUpdate
|
||||
protected void onUpdate() {
|
||||
updatedAt = System.currentTimeMillis() / 1000;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<? extends GrantedAuthority> getAuthorities() {
|
||||
return Collections.singletonList(new SimpleGrantedAuthority("ROLE_USER"));
|
||||
|
@ -0,0 +1,8 @@
|
||||
package com.huertian.jinduguanli.repository;
|
||||
|
||||
import com.huertian.jinduguanli.entity.Department;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
public interface DepartmentRepository extends JpaRepository<Department, Long> {
|
||||
Department findByName(String name);
|
||||
}
|
@ -66,6 +66,7 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
|
||||
}
|
||||
|
||||
final String jwt = authHeader.substring(7);
|
||||
logger.debug("Received JWT token: {}", jwt);
|
||||
final String username = jwtService.extractUsername(jwt);
|
||||
|
||||
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
|
||||
|
@ -0,0 +1,76 @@
|
||||
package com.huertian.jinduguanli.service;
|
||||
|
||||
import com.huertian.jinduguanli.common.ApiResponse;
|
||||
import com.huertian.jinduguanli.common.ErrorCode;
|
||||
import com.huertian.jinduguanli.dto.DepartmentImportDTO;
|
||||
import com.huertian.jinduguanli.entity.Department;
|
||||
import com.huertian.jinduguanli.repository.DepartmentRepository;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
public class DepartmentService {
|
||||
private static final Logger logger = LoggerFactory.getLogger(DepartmentService.class);
|
||||
private final DepartmentRepository departmentRepository;
|
||||
|
||||
@Autowired
|
||||
public DepartmentService(DepartmentRepository departmentRepository) {
|
||||
this.departmentRepository = departmentRepository;
|
||||
}
|
||||
|
||||
public ApiResponse<String> batchImportDepartments(List<DepartmentImportDTO> departmentList) {
|
||||
if (departmentList == null || departmentList.isEmpty()) {
|
||||
return new ApiResponse<>(ErrorCode.PARAM_ERROR, "导入数据不能为空", null);
|
||||
}
|
||||
|
||||
StringBuilder errorMsg = new StringBuilder();
|
||||
int successCount = 0;
|
||||
|
||||
for (int i = 0; i < departmentList.size(); i++) {
|
||||
DepartmentImportDTO dto = departmentList.get(i);
|
||||
try {
|
||||
// 基本数据验证
|
||||
if (StringUtils.isBlank(dto.getDepartmentName())) {
|
||||
errorMsg.append(String.format("第%d行部门名称不能为空;", i + 2));
|
||||
continue;
|
||||
}
|
||||
|
||||
// 检查部门名称是否已存在
|
||||
if (departmentRepository.findByName(dto.getDepartmentName()) != null) {
|
||||
errorMsg.append(String.format("第%d行部门名称已存在;", i + 2));
|
||||
continue;
|
||||
}
|
||||
|
||||
// 创建部门
|
||||
Department department = new Department();
|
||||
department.setName(dto.getDepartmentName());
|
||||
department.setDescription(dto.getDescription());
|
||||
department.setCreatedAt(System.currentTimeMillis() / 1000);
|
||||
department.setUpdatedAt(System.currentTimeMillis() / 1000);
|
||||
|
||||
departmentRepository.save(department);
|
||||
successCount++;
|
||||
} catch (Exception e) {
|
||||
logger.error("导入第{}行数据失败", i + 2, e);
|
||||
errorMsg.append(String.format("第%d行导入失败;", i + 2));
|
||||
}
|
||||
}
|
||||
|
||||
String resultMsg = String.format("成功导入%d条数据", successCount);
|
||||
if (errorMsg.length() > 0) {
|
||||
resultMsg += "。错误信息:" + errorMsg;
|
||||
}
|
||||
|
||||
// 如果没有成功导入任何数据,返回错误状态
|
||||
if (successCount == 0) {
|
||||
return new ApiResponse<>(ErrorCode.PARAM_ERROR, resultMsg, null);
|
||||
}
|
||||
|
||||
return ApiResponse.success(resultMsg);
|
||||
}
|
||||
}
|
@ -1,8 +1,12 @@
|
||||
package com.huertian.jinduguanli.service;
|
||||
|
||||
import com.huertian.jinduguanli.common.ApiResponse;
|
||||
import com.huertian.jinduguanli.common.ErrorCode;
|
||||
import com.huertian.jinduguanli.dto.LessonTaskDTO;
|
||||
import com.huertian.jinduguanli.dto.LessonTaskImportDTO;
|
||||
import com.huertian.jinduguanli.dto.LessonTaskRequest;
|
||||
import com.huertian.jinduguanli.entity.LessonTask;
|
||||
import com.huertian.jinduguanli.entity.User;
|
||||
import com.huertian.jinduguanli.repository.LessonTaskRepository;
|
||||
import jakarta.persistence.EntityNotFoundException;
|
||||
import org.slf4j.Logger;
|
||||
@ -15,13 +19,19 @@ import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
@Service
|
||||
public class LessonTaskService {
|
||||
private static final Logger logger = LoggerFactory.getLogger(LessonTaskService.class);
|
||||
private final LessonTaskRepository lessonTaskRepository;
|
||||
private final UserService userService; // 添加 UserService 的依赖
|
||||
|
||||
public LessonTaskService(LessonTaskRepository lessonTaskRepository) {
|
||||
public LessonTaskService(LessonTaskRepository lessonTaskRepository, UserService userService) {
|
||||
this.lessonTaskRepository = lessonTaskRepository;
|
||||
this.userService = userService;
|
||||
}
|
||||
|
||||
@Cacheable(value = "lessonTasks", key = "#userId != null ? 'user:' + #userId + ':page:' + #pageable.pageNumber : 'all:page:' + #pageable.pageNumber")
|
||||
@ -51,21 +61,21 @@ public class LessonTaskService {
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@CacheEvict(value = {"lessonTasks", "lessonTask"}, allEntries = true)
|
||||
@CacheEvict(value = { "lessonTasks", "lessonTask" }, allEntries = true)
|
||||
public LessonTask create(LessonTaskRequest request) {
|
||||
logger.info("开始创建课程任务 - 课程名称: {}, 小课名称: {}, 用户ID: {}",
|
||||
logger.info("开始创建课程任务 - 课程名称: {}, 微课名称: {}, 用户ID: {}",
|
||||
request.getCourseName(), request.getMicroLessonName(), request.getUserId());
|
||||
validateRequest(request);
|
||||
LessonTask task = new LessonTask();
|
||||
BeanUtils.copyProperties(request, task);
|
||||
task.setProgressStatus(0); // 初始状态
|
||||
task.setProgressStatus(0); // 初始状态:未开始
|
||||
LessonTask savedTask = lessonTaskRepository.save(task);
|
||||
logger.info("创建课程任务成功 - 任务ID: {}", savedTask.getId());
|
||||
return savedTask;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@CacheEvict(value = {"lessonTasks", "lessonTask"}, allEntries = true)
|
||||
@CacheEvict(value = { "lessonTasks", "lessonTask" }, allEntries = true)
|
||||
public LessonTask update(Long id, LessonTaskRequest request) {
|
||||
logger.info("开始更新课程任务 - 任务ID: {}, 进度状态: {}", id, request.getProgressStatus());
|
||||
LessonTask task = findById(id);
|
||||
@ -76,19 +86,42 @@ public class LessonTaskService {
|
||||
// 根据进度状态更新时间戳
|
||||
long currentTime = System.currentTimeMillis() / 1000;
|
||||
switch (request.getProgressStatus()) {
|
||||
case 1: // 脚本上传
|
||||
task.setScriptUploadTime(currentTime);
|
||||
case 0: // 未开始
|
||||
task.setScriptCreateTime(null);
|
||||
task.setScriptReviewTime(null);
|
||||
task.setScriptConfirmTime(null);
|
||||
task.setVideoCreateTime(null);
|
||||
task.setVideoConfirmTime(null);
|
||||
task.setFinishTime(null);
|
||||
break;
|
||||
case 2: // 脚本确认
|
||||
case 1: // 脚本制作
|
||||
task.setScriptCreateTime(currentTime);
|
||||
task.setScriptReviewTime(null);
|
||||
task.setScriptConfirmTime(null);
|
||||
task.setVideoCreateTime(null);
|
||||
task.setVideoConfirmTime(null);
|
||||
task.setFinishTime(null);
|
||||
break;
|
||||
case 2: // 脚本审核
|
||||
task.setScriptReviewTime(currentTime);
|
||||
task.setScriptConfirmTime(null);
|
||||
task.setVideoCreateTime(null);
|
||||
task.setVideoConfirmTime(null);
|
||||
task.setFinishTime(null);
|
||||
break;
|
||||
case 3: // 脚本确认
|
||||
task.setScriptConfirmTime(currentTime);
|
||||
task.setVideoCreateTime(null);
|
||||
task.setVideoConfirmTime(null);
|
||||
task.setFinishTime(null);
|
||||
break;
|
||||
case 3: // 视频拍摄
|
||||
task.setVideoCaptureTime(currentTime);
|
||||
case 4: // 视频拍摄与制作
|
||||
task.setVideoCreateTime(currentTime);
|
||||
task.setVideoConfirmTime(null);
|
||||
task.setFinishTime(null);
|
||||
break;
|
||||
case 4: // 视频确认
|
||||
case 5: // 视频确认
|
||||
task.setVideoConfirmTime(currentTime);
|
||||
break;
|
||||
case 5: // 任务完成
|
||||
task.setFinishTime(currentTime);
|
||||
break;
|
||||
}
|
||||
@ -113,7 +146,7 @@ public class LessonTaskService {
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@CacheEvict(value = {"lessonTasks", "lessonTask"}, allEntries = true)
|
||||
@CacheEvict(value = { "lessonTasks", "lessonTask" }, allEntries = true)
|
||||
public void delete(Long id) {
|
||||
logger.info("开始删除课程任务 - 任务ID: {}", id);
|
||||
if (!lessonTaskRepository.existsById(id)) {
|
||||
@ -133,7 +166,8 @@ public class LessonTaskService {
|
||||
*/
|
||||
public Page<LessonTaskDTO> findByDepartmentIdAndNormalUser(Long departmentId, Pageable pageable) {
|
||||
logger.info("查询部门正常用户的课程任务,部门ID: {}", departmentId);
|
||||
Page<java.util.Map<String, Object>> result = lessonTaskRepository.findByDepartmentIdAndNormalUserWithUsername(departmentId, pageable);
|
||||
Page<java.util.Map<String, Object>> result = lessonTaskRepository
|
||||
.findByDepartmentIdAndNormalUserWithUsername(departmentId, pageable);
|
||||
|
||||
return result.map(map -> {
|
||||
LessonTask lessonTask = (LessonTask) map.get("lessonTask");
|
||||
@ -146,7 +180,7 @@ public class LessonTaskService {
|
||||
* 根据部门ID和状态查询课程任务列表(分页)
|
||||
*
|
||||
* @param departmentId 部门ID
|
||||
* @param status 任务状态(可选):0-脚本上传, 1-脚本确认, 2-视频拍摄, 3-后期制作, 4-任务完成
|
||||
* @param status 任务状态(可选):0-未开始, 1-脚本制作, 2-脚本审核, 3-脚本确认, 4-视频拍摄与制作, 5-视频确认
|
||||
* @param pageable 分页参数
|
||||
* @return 课程任务分页数据
|
||||
*/
|
||||
@ -168,12 +202,80 @@ public class LessonTaskService {
|
||||
return lessonTaskRepository.findByDepartmentIdAndUserStatus(departmentId, userStatus, pageable);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public ApiResponse<String> batchImportLessonTasks(List<LessonTaskImportDTO> taskList) {
|
||||
if (taskList == null || taskList.isEmpty()) {
|
||||
logger.error("导入任务列表为空");
|
||||
return new ApiResponse<>(ErrorCode.PARAM_IS_BLANK, "导入任务列表为空", null);
|
||||
}
|
||||
|
||||
StringBuilder errorMsg = new StringBuilder();
|
||||
int successCount = 0;
|
||||
|
||||
for (int i = 0; i < taskList.size(); i++) {
|
||||
LessonTaskImportDTO dto = taskList.get(i);
|
||||
try {
|
||||
// 基本数据验证
|
||||
if (StringUtils.isBlank(dto.getLessonName())) {
|
||||
errorMsg.append(String.format("第%d行课程名称不能为空;", i + 2));
|
||||
continue;
|
||||
}
|
||||
if (StringUtils.isBlank(dto.getMicroLessonName())) {
|
||||
errorMsg.append(String.format("第%d行微课名称不能为空;", i + 2));
|
||||
continue;
|
||||
}
|
||||
if (StringUtils.isBlank(dto.getTeacherEmail())) {
|
||||
errorMsg.append(String.format("第%d行教师账号不能为空;", i + 2));
|
||||
continue;
|
||||
}
|
||||
|
||||
// 查找教师用户ID
|
||||
ApiResponse<User> teacherResponse = userService.findTeacherByEmail(dto.getTeacherEmail());
|
||||
if (teacherResponse.getCode() != ErrorCode.SUCCESS) {
|
||||
errorMsg.append(String.format("第%d行未找到教师用户(%s);", i + 2, dto.getTeacherEmail()));
|
||||
continue;
|
||||
}
|
||||
|
||||
LessonTask task = new LessonTask();
|
||||
task.setCourseName(dto.getLessonName());
|
||||
task.setMicroLessonName(dto.getMicroLessonName());
|
||||
task.setUserId(teacherResponse.getData().getId());
|
||||
task.setProgressStatus(0); // 初始状态:未开始
|
||||
task.setCreatedAt(System.currentTimeMillis() / 1000);
|
||||
task.setUpdatedAt(System.currentTimeMillis() / 1000);
|
||||
|
||||
try {
|
||||
lessonTaskRepository.save(task);
|
||||
successCount++;
|
||||
} catch (Exception e) {
|
||||
logger.error("保存课程任务失败", e);
|
||||
errorMsg.append(String.format("第%d行保存失败: %s;", i + 2, e.getMessage()));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("导入第{}行数据失败", i + 2, e);
|
||||
errorMsg.append(String.format("第%d行导入失败: %s;", i + 2, e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
String resultMsg = String.format("成功导入%d条数据", successCount);
|
||||
if (errorMsg.length() > 0) {
|
||||
resultMsg += "。错误信息:" + errorMsg;
|
||||
}
|
||||
|
||||
// 如果没有成功导入任何数据,返回错误状态
|
||||
if (successCount == 0) {
|
||||
return new ApiResponse<>(ErrorCode.PARAM_ERROR, resultMsg, null);
|
||||
}
|
||||
|
||||
return ApiResponse.success(resultMsg);
|
||||
}
|
||||
|
||||
private void validateRequest(LessonTaskRequest request) {
|
||||
if (request.getCourseName() == null || request.getCourseName().trim().isEmpty()) {
|
||||
throw new IllegalArgumentException("课程名称不能为空");
|
||||
}
|
||||
if (request.getMicroLessonName() == null || request.getMicroLessonName().trim().isEmpty()) {
|
||||
throw new IllegalArgumentException("小课名称不能为空");
|
||||
throw new IllegalArgumentException("微课名称不能为空");
|
||||
}
|
||||
if (request.getUserId() == null) {
|
||||
throw new IllegalArgumentException("用户ID不能为空");
|
||||
|
@ -2,13 +2,13 @@ package com.huertian.jinduguanli.service;
|
||||
|
||||
import com.huertian.jinduguanli.common.ApiResponse;
|
||||
import com.huertian.jinduguanli.common.ErrorCode;
|
||||
import com.huertian.jinduguanli.dto.CreateUserRequest;
|
||||
import com.huertian.jinduguanli.dto.LoginResponse;
|
||||
import com.huertian.jinduguanli.dto.UserLoginRequest;
|
||||
import com.huertian.jinduguanli.dto.UserPageResponse;
|
||||
import com.huertian.jinduguanli.dto.*;
|
||||
import com.huertian.jinduguanli.entity.Department;
|
||||
import com.huertian.jinduguanli.entity.User;
|
||||
import com.huertian.jinduguanli.repository.DepartmentRepository;
|
||||
import com.huertian.jinduguanli.repository.UserRepository;
|
||||
import com.huertian.jinduguanli.security.service.JwtService;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@ -28,13 +28,16 @@ public class UserService implements UserDetailsService {
|
||||
private final UserRepository userRepository;
|
||||
private final PasswordEncoder passwordEncoder;
|
||||
private final JwtService jwtService;
|
||||
private final DepartmentRepository departmentRepository;
|
||||
private static final Logger logger = LoggerFactory.getLogger(UserService.class);
|
||||
|
||||
@Autowired
|
||||
public UserService(JwtService jwtService, PasswordEncoder passwordEncoder, UserRepository userRepository) {
|
||||
public UserService(JwtService jwtService, PasswordEncoder passwordEncoder, UserRepository userRepository,
|
||||
DepartmentRepository departmentRepository) {
|
||||
this.jwtService = jwtService;
|
||||
this.passwordEncoder = passwordEncoder;
|
||||
this.userRepository = userRepository;
|
||||
this.departmentRepository = departmentRepository;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -76,8 +79,7 @@ public class UserService implements UserDetailsService {
|
||||
user.setJobs(request.getJobs());
|
||||
user.setCreatorId(request.getCreatorId());
|
||||
user.setStatus(1);
|
||||
user.setCreatedAt(System.currentTimeMillis() / 1000);
|
||||
user.setUpdatedAt(System.currentTimeMillis() / 1000);
|
||||
// 时间戳由 @PrePersist 处理,这里不需要手动设置
|
||||
|
||||
userRepository.save(user);
|
||||
|
||||
@ -173,4 +175,115 @@ public class UserService implements UserDetailsService {
|
||||
logger.info("查询部门用户列表,部门ID: {}", departmentId);
|
||||
return userRepository.findByDepartmentIdAndNormalStatus(departmentId);
|
||||
}
|
||||
|
||||
public ApiResponse<User> findTeacherByEmail(String email) {
|
||||
if (email == null || email.trim().isEmpty()) {
|
||||
return new ApiResponse<>(ErrorCode.INVALID_PARAM, "邮箱不能为空", null);
|
||||
}
|
||||
|
||||
User user = userRepository.findByEmail(email).orElse(null);
|
||||
if (user == null) {
|
||||
return new ApiResponse<>(ErrorCode.USER_NOT_FOUND, "用户不存在", null);
|
||||
}
|
||||
|
||||
if (user.getStatus() != 1) {
|
||||
return new ApiResponse<>(ErrorCode.INVALID_PARAM, "用户状态异常", null);
|
||||
}
|
||||
|
||||
return ApiResponse.success(user);
|
||||
}
|
||||
|
||||
public ApiResponse<String> batchImportUsers(List<UserImportDTO> userList, String token) {
|
||||
if (userList == null || userList.isEmpty()) {
|
||||
return new ApiResponse<>(ErrorCode.PARAM_ERROR, "导入数据不能为空", null);
|
||||
}
|
||||
|
||||
StringBuilder errorMsg = new StringBuilder();
|
||||
int successCount = 0;
|
||||
|
||||
for (int i = 0; i < userList.size(); i++) {
|
||||
UserImportDTO dto = userList.get(i);
|
||||
try {
|
||||
// 基本数据验证
|
||||
if (StringUtils.isBlank(dto.getUsername()) ||
|
||||
StringUtils.isBlank(dto.getEmail()) ||
|
||||
StringUtils.isBlank(dto.getPassword()) ||
|
||||
StringUtils.isBlank(dto.getDepartmentName()) ||
|
||||
StringUtils.isBlank(dto.getRoleAndJob())) {
|
||||
errorMsg.append(String.format("第%d行数据不完整;", i + 2));
|
||||
continue;
|
||||
}
|
||||
|
||||
// 检查邮箱是否已存在
|
||||
if (userRepository.findByEmail(dto.getEmail()).isPresent()) {
|
||||
errorMsg.append(String.format("第%d行邮箱已存在;", i + 2));
|
||||
continue;
|
||||
}
|
||||
|
||||
// 查找部门ID
|
||||
Department department = departmentRepository.findByName(dto.getDepartmentName());
|
||||
logger.info("查找部门:{},结果:{}", dto.getDepartmentName(), department);
|
||||
if (department == null) {
|
||||
errorMsg.append(String.format("第%d行部门不存在;", i + 2));
|
||||
continue;
|
||||
}
|
||||
|
||||
// 创建新用户
|
||||
User user = new User();
|
||||
user.setUsername(dto.getUsername());
|
||||
user.setEmail(dto.getEmail());
|
||||
user.setPassword(passwordEncoder.encode(dto.getPassword()));
|
||||
user.setDepartmentId(department.getId());
|
||||
|
||||
// 处理角色和岗位
|
||||
String roleAndJob = dto.getRoleAndJob();
|
||||
if (!StringUtils.isBlank(roleAndJob)) {
|
||||
try {
|
||||
user.setRoles(Integer.parseInt(roleAndJob));
|
||||
user.setJobs(Integer.parseInt(roleAndJob));
|
||||
} catch (NumberFormatException e) {
|
||||
logger.warn("角色/岗位不是有效的数字: {}", roleAndJob);
|
||||
errorMsg.append(String.format("第%d行角色/岗位不是有效的数字;", i + 2));
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
logger.warn("角色/岗位不能为空");
|
||||
errorMsg.append(String.format("第%d行角色/岗位不能为空;", i + 2));
|
||||
continue;
|
||||
}
|
||||
|
||||
// 设置创建者ID
|
||||
String email = jwtService.extractUsername(token);
|
||||
Optional<User> creator = userRepository.findByEmail(email);
|
||||
user.setCreatorId(creator.map(User::getId).orElse(0L));
|
||||
|
||||
user.setStatus(1); // 设置状态为正常
|
||||
user.setCreatedAt(System.currentTimeMillis() / 1000);
|
||||
user.setUpdatedAt(System.currentTimeMillis() / 1000);
|
||||
|
||||
try {
|
||||
userRepository.save(user);
|
||||
successCount++;
|
||||
} catch (Exception e) {
|
||||
logger.error("保存用户失败", e);
|
||||
errorMsg.append(String.format("第%d行保存失败: %s;", i + 2, e.getMessage()));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("导入第{}行数据失败", i + 2, e);
|
||||
errorMsg.append(String.format("第%d行导入失败;", i + 2));
|
||||
}
|
||||
}
|
||||
|
||||
String resultMsg = String.format("成功导入%d条数据", successCount);
|
||||
if (errorMsg.length() > 0) {
|
||||
resultMsg += "。错误信息:" + errorMsg;
|
||||
}
|
||||
|
||||
// 如果没有成功导入任何数据,返回错误状态
|
||||
if (successCount == 0) {
|
||||
return new ApiResponse<>(ErrorCode.PARAM_ERROR, resultMsg, null);
|
||||
}
|
||||
|
||||
return ApiResponse.success(resultMsg);
|
||||
}
|
||||
}
|
||||
|
145
数据库.md
145
数据库.md
@ -1,145 +0,0 @@
|
||||
# 数据库设计文档
|
||||
|
||||
## 1. 部门表 (departments)
|
||||
|
||||
用于存储组织的部门信息。
|
||||
|
||||
```sql
|
||||
CREATE TABLE departments (
|
||||
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
||||
name VARCHAR(100) NOT NULL COMMENT '部门名称',
|
||||
description TEXT DEFAULT NULL COMMENT '部门描述',
|
||||
created_at BIGINT NOT NULL COMMENT '创建时间(时间戳)',
|
||||
updated_at BIGINT NOT NULL COMMENT '更新时间(时间戳)'
|
||||
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COMMENT ='部门表';
|
||||
```
|
||||
|
||||
### 字段说明
|
||||
- `id`: 部门ID,自增主键
|
||||
- `name`: 部门名称,不可为空
|
||||
- `description`: 部门描述,可为空
|
||||
- `created_at`: 创建时间,毫秒级时间戳
|
||||
- `updated_at`: 更新时间,毫秒级时间戳
|
||||
|
||||
## 2. 用户表 (users)
|
||||
|
||||
存储系统用户信息,包括教师、管理员等角色。
|
||||
|
||||
```sql
|
||||
CREATE TABLE users (
|
||||
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
||||
username VARCHAR(50) NOT NULL COMMENT '用户名',
|
||||
email VARCHAR(100) NOT NULL UNIQUE COMMENT '邮箱',
|
||||
password VARCHAR(100) NOT NULL COMMENT '密码',
|
||||
department_id BIGINT NOT NULL COMMENT '所属部门',
|
||||
roles INT NOT NULL COMMENT '角色: 1-教师, 2-普通管理员, 3-沟通联络人, 4-系统管理员',
|
||||
jobs INT NOT NULL COMMENT '岗位: 1-课程制作教师, 2-课程购买方项目负责人, 3-课程制作方沟通联络人, 4-系统制作方项目负责人',
|
||||
avatar VARCHAR(255) DEFAULT NULL COMMENT '头像',
|
||||
creator_id BIGINT NOT NULL DEFAULT 1 COMMENT '创建用户的管理员ID',
|
||||
status INT NOT NULL DEFAULT 1 COMMENT '用户状态: 1-正常, 0-禁用',
|
||||
created_at BIGINT NOT NULL COMMENT '创建时间(时间戳)',
|
||||
updated_at BIGINT NOT NULL COMMENT '更新时间(时间戳)',
|
||||
FOREIGN KEY (department_id) REFERENCES departments (id) ON DELETE RESTRICT,
|
||||
INDEX idx_users_department_id (department_id)
|
||||
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COMMENT ='用户表';
|
||||
```
|
||||
|
||||
### 字段说明
|
||||
- `id`: 用户ID,自增主键
|
||||
- `username`: 用户名,不可为空
|
||||
- `email`: 邮箱地址,不可为空,唯一索引
|
||||
- `password`: 密码(加密存储),不可为空
|
||||
- `department_id`: 所属部门ID,外键关联departments表
|
||||
- `roles`: 用户角色,整数枚举:
|
||||
- 1: 教师
|
||||
- 2: 普通管理员
|
||||
- 3: 沟通联络人
|
||||
- 4: 系统管理员
|
||||
- `jobs`: 用户岗位,整数枚举:
|
||||
- 1: 课程制作教师
|
||||
- 2: 课程购买方项目负责人
|
||||
- 3: 课程制作方沟通联络人
|
||||
- 4: 系统制作方项目负责人
|
||||
- `avatar`: 用户头像URL,可为空
|
||||
- `creator_id`: 创建该用户的管理员ID
|
||||
- `status`: 用户状态:
|
||||
- 1: 正常
|
||||
- 0: 禁用
|
||||
- `created_at`: 创建时间,毫秒级时间戳
|
||||
- `updated_at`: 更新时间,毫秒级时间戳
|
||||
|
||||
### 索引
|
||||
- 主键索引:`id`
|
||||
- 外键索引:`idx_users_department_id (department_id)`
|
||||
- 唯一索引:`email`
|
||||
|
||||
## 3. 课程任务表 (lesson_tasks)
|
||||
|
||||
存储课程制作任务的信息和进度。
|
||||
|
||||
```sql
|
||||
CREATE TABLE lesson_tasks (
|
||||
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
||||
course_name VARCHAR(100) NOT NULL COMMENT '课程名称',
|
||||
micro_lesson_name VARCHAR(100) NOT NULL COMMENT '微课名称',
|
||||
user_id BIGINT NOT NULL COMMENT '负责人ID',
|
||||
progress_status INT NOT NULL DEFAULT 0 COMMENT '当前任务进度状态: 0-脚本上传, 1-脚本确认, 2-视频拍摄, 3-后期制作, 4-任务完成',
|
||||
script_upload_time BIGINT DEFAULT NULL COMMENT '脚本上传时间(时间戳)',
|
||||
script_confirm_time BIGINT DEFAULT NULL COMMENT '脚本确认时间(时间戳)',
|
||||
video_capture_time BIGINT DEFAULT NULL COMMENT '视频拍摄时间(时间戳)',
|
||||
video_confirm_time BIGINT DEFAULT NULL COMMENT '视频确认时间(时间戳)',
|
||||
finish_time BIGINT DEFAULT NULL COMMENT '任务完成时间(时间戳)',
|
||||
advise TEXT DEFAULT NULL COMMENT '任务建议或备注',
|
||||
created_at BIGINT NOT NULL COMMENT '创建时间(时间戳)',
|
||||
updated_at BIGINT NOT NULL COMMENT '更新时间(时间戳)',
|
||||
FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE,
|
||||
INDEX idx_lesson_tasks_user_id (user_id),
|
||||
INDEX idx_lesson_tasks_progress_status (progress_status)
|
||||
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COMMENT ='课程任务表';
|
||||
```
|
||||
|
||||
### 字段说明
|
||||
- `id`: 任务ID,自增主键
|
||||
- `course_name`: 课程名称,不可为空
|
||||
- `micro_lesson_name`: 微课名称,不可为空
|
||||
- `user_id`: 负责人ID,外键关联users表
|
||||
- `progress_status`: 任务进度状态:
|
||||
- 0: 脚本上传
|
||||
- 1: 脚本确认
|
||||
- 2: 视频拍摄
|
||||
- 3: 后期制作
|
||||
- 4: 任务完成
|
||||
- `script_upload_time`: 脚本上传时间,毫秒级时间戳
|
||||
- `script_confirm_time`: 脚本确认时间,毫秒级时间戳
|
||||
- `video_capture_time`: 视频拍摄时间,毫秒级时间戳
|
||||
- `video_confirm_time`: 视频确认时间,毫秒级时间戳
|
||||
- `finish_time`: 任务完成时间,毫秒级时间戳
|
||||
- `advise`: 任务相关的建议或备注,文本字段
|
||||
- `created_at`: 创建时间,毫秒级时间戳
|
||||
- `updated_at`: 更新时间,毫秒级时间戳
|
||||
|
||||
### 索引
|
||||
- 主键索引:`id`
|
||||
- 外键索引:`idx_lesson_tasks_user_id (user_id)`
|
||||
- 普通索引:`idx_lesson_tasks_progress_status (progress_status)`
|
||||
|
||||
## 数据库关系
|
||||
|
||||
1. `users.department_id` -> `departments.id`
|
||||
- 一个部门可以有多个用户
|
||||
- 一个用户只能属于一个部门
|
||||
- 使用RESTRICT约束,防止删除仍有用户的部门
|
||||
|
||||
2. `lesson_tasks.user_id` -> `users.id`
|
||||
- 一个用户可以负责多个课程任务
|
||||
- 一个课程任务只能有一个负责人
|
||||
- 使用CASCADE约束,删除用户时自动删除其负责的课程任务
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. 所有时间戳字段使用BIGINT类型,存储毫秒级时间戳
|
||||
2. 字符编码统一使用utf8mb4,支持完整的Unicode字符集
|
||||
3. 所有表都使用InnoDB引擎,支持事务和外键
|
||||
4. 关键字段都建立了适当的索引以提高查询性能
|
||||
5. 用户密码在存储前需要进行加密处理
|
||||
6. 删除用户时会自动删除其关联的课程任务,但不会影响部门数据
|
Reference in New Issue
Block a user