feat: 添加批量导入功能
1. 添加用户、课程任务和部门的批量导入功能 2. 优化错误处理逻辑,提供更详细的错误信息 3. 更新API文档,添加批量导入接口说明
This commit is contained in:
@@ -545,9 +545,101 @@
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## 批量导入接口
|
||||||
|
|
||||||
|
### 1. 批量导入用户
|
||||||
|
|
||||||
|
- **接口**:`POST /api/import/users`
|
||||||
|
- **描述**:通过 Excel 文件批量导入用户
|
||||||
|
- **认证**:需要
|
||||||
|
- **请求体**:
|
||||||
|
- `Content-Type`: `multipart/form-data`
|
||||||
|
- `file`: Excel 文件(.xlsx)
|
||||||
|
- **Excel 文件格式**:
|
||||||
|
| 用户名 | 邮箱 | 密码 | 部门名称 |
|
||||||
|
|--------|------|------|----------|
|
||||||
|
| 张三 | zhangsan@example.com | 123456 | 技术部 |
|
||||||
|
- **成功响应**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 10000,
|
||||||
|
"message": "成功",
|
||||||
|
"data": "成功导入2条数据"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- **错误响应**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 10012,
|
||||||
|
"message": "成功导入0条数据。错误信息:第2行邮箱已存在;第3行部门不存在;",
|
||||||
|
"data": null
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 批量导入课程任务
|
||||||
|
|
||||||
|
- **接口**:`POST /api/import/lesson-tasks`
|
||||||
|
- **描述**:通过 Excel 文件批量导入课程任务
|
||||||
|
- **认证**:需要
|
||||||
|
- **请求体**:
|
||||||
|
- `Content-Type`: `multipart/form-data`
|
||||||
|
- `file`: Excel 文件(.xlsx)
|
||||||
|
- **Excel 文件格式**:
|
||||||
|
| 课程名称 | 微课名称 | 教师邮箱 |
|
||||||
|
|----------|----------|-----------|
|
||||||
|
| 数学课程 | 第一章 | teacher@example.com |
|
||||||
|
- **成功响应**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 10000,
|
||||||
|
"message": "成功",
|
||||||
|
"data": "成功导入3条数据"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- **错误响应**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 10012,
|
||||||
|
"message": "成功导入0条数据。错误信息:第2行未找到教师用户(teacher@example.com);",
|
||||||
|
"data": null
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 批量导入部门
|
||||||
|
|
||||||
|
- **接口**:`POST /api/import/departments`
|
||||||
|
- **描述**:通过 Excel 文件批量导入部门
|
||||||
|
- **认证**:需要
|
||||||
|
- **请求体**:
|
||||||
|
- `Content-Type`: `multipart/form-data`
|
||||||
|
- `file`: Excel 文件(.xlsx)
|
||||||
|
- **Excel 文件格式**:
|
||||||
|
| 部门名称 | 部门描述 |
|
||||||
|
|----------|----------|
|
||||||
|
| 技术部 | 负责技术研发 |
|
||||||
|
- **成功响应**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 10000,
|
||||||
|
"message": "成功",
|
||||||
|
"data": "成功导入2条数据"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- **错误响应**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 10012,
|
||||||
|
"message": "成功导入0条数据。错误信息:第2行部门名称已存在;",
|
||||||
|
"data": null
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## 注意事项
|
## 注意事项
|
||||||
|
|
||||||
1. 所有时间戳字段均使用秒级时间戳(10 位)
|
1. 所有时间戳字段均使用秒级时间戳(10 位)
|
||||||
2. 课程任务状态变更时会自动记录相应的时间戳
|
2. 课程任务状态变更时会自动记录相应的时间戳
|
||||||
3. 部门课程任务列表会额外返回用户名信息
|
3. 部门课程任务列表会额外返回用户名信息
|
||||||
4. 分页参数中的页码从 1 开始
|
4. 分页参数中的页码从 1 开始
|
||||||
|
5. 批量导入时,Excel 文件必须严格按照模板格式填写
|
||||||
|
6. 批量导入时,如果某行数据导入失败,会在返回信息中说明具体原因
|
||||||
|
7. 批量导入时,如果全部数据导入失败,会返回错误状态码(10012)
|
||||||
|
|||||||
@@ -92,6 +92,19 @@
|
|||||||
<groupId>org.apache.commons</groupId>
|
<groupId>org.apache.commons</groupId>
|
||||||
<artifactId>commons-pool2</artifactId>
|
<artifactId>commons-pool2</artifactId>
|
||||||
</dependency>
|
</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>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
|||||||
@@ -1,14 +1,68 @@
|
|||||||
package com.huertian.jinduguanli.common;
|
package com.huertian.jinduguanli.common;
|
||||||
|
|
||||||
public class ErrorCode {
|
public class ErrorCode {
|
||||||
|
/**
|
||||||
|
* 操作成功
|
||||||
|
*/
|
||||||
public static final int SUCCESS = 10000;
|
public static final int SUCCESS = 10000;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统内部错误
|
||||||
|
*/
|
||||||
public static final int SYSTEM_ERROR = 10001;
|
public static final int SYSTEM_ERROR = 10001;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 参数校验错误
|
||||||
|
*/
|
||||||
public static final int INVALID_PARAM = 10002;
|
public static final int INVALID_PARAM = 10002;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户不存在
|
||||||
|
*/
|
||||||
public static final int USER_NOT_FOUND = 10003;
|
public static final int USER_NOT_FOUND = 10003;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户不存在或已被禁用
|
||||||
|
*/
|
||||||
public static final int USER_NOT_FOUND_OR_DISABLED = 10004;
|
public static final int USER_NOT_FOUND_OR_DISABLED = 10004;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 邮箱已存在
|
||||||
|
*/
|
||||||
public static final int EMAIL_EXISTS = 10005;
|
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 INVALID_TOKEN = 10007;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 未授权访问
|
||||||
|
*/
|
||||||
public static final int UNAUTHORIZED = 10008;
|
public static final int UNAUTHORIZED = 10008;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户名已存在
|
||||||
|
*/
|
||||||
public static final int USERNAME_EXISTS = 10009;
|
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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,130 @@
|
|||||||
|
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.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) {
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
return userService.batchImportUsers(userList);
|
||||||
|
} 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");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package com.huertian.jinduguanli.dto;
|
||||||
|
|
||||||
|
import com.alibaba.excel.annotation.ExcelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
package com.huertian.jinduguanli.dto;
|
||||||
|
|
||||||
|
import com.alibaba.excel.annotation.ExcelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
package com.huertian.jinduguanli.dto;
|
||||||
|
|
||||||
|
import com.alibaba.excel.annotation.ExcelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
@@ -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;
|
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.LessonTaskDTO;
|
||||||
|
import com.huertian.jinduguanli.dto.LessonTaskImportDTO;
|
||||||
import com.huertian.jinduguanli.dto.LessonTaskRequest;
|
import com.huertian.jinduguanli.dto.LessonTaskRequest;
|
||||||
import com.huertian.jinduguanli.entity.LessonTask;
|
import com.huertian.jinduguanli.entity.LessonTask;
|
||||||
|
import com.huertian.jinduguanli.entity.User;
|
||||||
import com.huertian.jinduguanli.repository.LessonTaskRepository;
|
import com.huertian.jinduguanli.repository.LessonTaskRepository;
|
||||||
import jakarta.persistence.EntityNotFoundException;
|
import jakarta.persistence.EntityNotFoundException;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@@ -15,13 +19,20 @@ import org.springframework.stereotype.Service;
|
|||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
import org.springframework.beans.BeanUtils;
|
import org.springframework.beans.BeanUtils;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class LessonTaskService {
|
public class LessonTaskService {
|
||||||
private static final Logger logger = LoggerFactory.getLogger(LessonTaskService.class);
|
private static final Logger logger = LoggerFactory.getLogger(LessonTaskService.class);
|
||||||
private final LessonTaskRepository lessonTaskRepository;
|
private final LessonTaskRepository lessonTaskRepository;
|
||||||
|
private final UserService userService; // 添加 UserService 的依赖
|
||||||
|
|
||||||
public LessonTaskService(LessonTaskRepository lessonTaskRepository) {
|
public LessonTaskService(LessonTaskRepository lessonTaskRepository, UserService userService) {
|
||||||
this.lessonTaskRepository = lessonTaskRepository;
|
this.lessonTaskRepository = lessonTaskRepository;
|
||||||
|
this.userService = userService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Cacheable(value = "lessonTasks", key = "#userId != null ? 'user:' + #userId + ':page:' + #pageable.pageNumber : 'all:page:' + #pageable.pageNumber")
|
@Cacheable(value = "lessonTasks", key = "#userId != null ? 'user:' + #userId + ':page:' + #pageable.pageNumber : 'all:page:' + #pageable.pageNumber")
|
||||||
@@ -192,6 +203,74 @@ public class LessonTaskService {
|
|||||||
return lessonTaskRepository.findByDepartmentIdAndUserStatus(departmentId, userStatus, pageable);
|
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) {
|
private void validateRequest(LessonTaskRequest request) {
|
||||||
if (request.getCourseName() == null || request.getCourseName().trim().isEmpty()) {
|
if (request.getCourseName() == null || request.getCourseName().trim().isEmpty()) {
|
||||||
throw new IllegalArgumentException("课程名称不能为空");
|
throw new IllegalArgumentException("课程名称不能为空");
|
||||||
|
|||||||
@@ -2,13 +2,13 @@ package com.huertian.jinduguanli.service;
|
|||||||
|
|
||||||
import com.huertian.jinduguanli.common.ApiResponse;
|
import com.huertian.jinduguanli.common.ApiResponse;
|
||||||
import com.huertian.jinduguanli.common.ErrorCode;
|
import com.huertian.jinduguanli.common.ErrorCode;
|
||||||
import com.huertian.jinduguanli.dto.CreateUserRequest;
|
import com.huertian.jinduguanli.dto.*;
|
||||||
import com.huertian.jinduguanli.dto.LoginResponse;
|
import com.huertian.jinduguanli.entity.Department;
|
||||||
import com.huertian.jinduguanli.dto.UserLoginRequest;
|
|
||||||
import com.huertian.jinduguanli.dto.UserPageResponse;
|
|
||||||
import com.huertian.jinduguanli.entity.User;
|
import com.huertian.jinduguanli.entity.User;
|
||||||
|
import com.huertian.jinduguanli.repository.DepartmentRepository;
|
||||||
import com.huertian.jinduguanli.repository.UserRepository;
|
import com.huertian.jinduguanli.repository.UserRepository;
|
||||||
import com.huertian.jinduguanli.security.service.JwtService;
|
import com.huertian.jinduguanli.security.service.JwtService;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
@@ -28,13 +28,16 @@ public class UserService implements UserDetailsService {
|
|||||||
private final UserRepository userRepository;
|
private final UserRepository userRepository;
|
||||||
private final PasswordEncoder passwordEncoder;
|
private final PasswordEncoder passwordEncoder;
|
||||||
private final JwtService jwtService;
|
private final JwtService jwtService;
|
||||||
|
private final DepartmentRepository departmentRepository;
|
||||||
private static final Logger logger = LoggerFactory.getLogger(UserService.class);
|
private static final Logger logger = LoggerFactory.getLogger(UserService.class);
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public UserService(JwtService jwtService, PasswordEncoder passwordEncoder, UserRepository userRepository) {
|
public UserService(JwtService jwtService, PasswordEncoder passwordEncoder, UserRepository userRepository,
|
||||||
|
DepartmentRepository departmentRepository) {
|
||||||
this.jwtService = jwtService;
|
this.jwtService = jwtService;
|
||||||
this.passwordEncoder = passwordEncoder;
|
this.passwordEncoder = passwordEncoder;
|
||||||
this.userRepository = userRepository;
|
this.userRepository = userRepository;
|
||||||
|
this.departmentRepository = departmentRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -183,22 +186,83 @@ public class UserService implements UserDetailsService {
|
|||||||
return new ApiResponse<>(ErrorCode.USER_NOT_FOUND, "用户不存在", null);
|
return new ApiResponse<>(ErrorCode.USER_NOT_FOUND, "用户不存在", null);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 验证用户是否为教师角色且为课程制作教师岗位
|
|
||||||
if (user.getRoles() != 1) {
|
|
||||||
return new ApiResponse<>(ErrorCode.INVALID_PARAM, "非教师用户", null);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (user.getJobs() != 1) {
|
|
||||||
return new ApiResponse<>(ErrorCode.INVALID_PARAM, "非课程制作教师", null);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (user.getStatus() != 1) {
|
if (user.getStatus() != 1) {
|
||||||
return new ApiResponse<>(ErrorCode.INVALID_PARAM, "用户状态异常", null);
|
return new ApiResponse<>(ErrorCode.INVALID_PARAM, "用户状态异常", null);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 清除敏感信息
|
|
||||||
user.setPassword(null);
|
|
||||||
|
|
||||||
return ApiResponse.success(user);
|
return ApiResponse.success(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ApiResponse<String> batchImportUsers(List<UserImportDTO> userList) {
|
||||||
|
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());
|
||||||
|
user.setRoles(0); // 默认角色
|
||||||
|
user.setJobs(0); // 默认工作
|
||||||
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user