feat: 添加批量导入功能

1. 添加用户、课程任务和部门的批量导入功能
2. 优化错误处理逻辑,提供更详细的错误信息
3. 更新API文档,添加批量导入接口说明
This commit is contained in:
huertian 2025-01-04 15:07:14 +08:00
parent 7436928328
commit 5c632a724f
15 changed files with 688 additions and 20 deletions

View File

@ -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 位)
2. 课程任务状态变更时会自动记录相应的时间戳
3. 部门课程任务列表会额外返回用户名信息
4. 分页参数中的页码从 1 开始
5. 批量导入时Excel 文件必须严格按照模板格式填写
6. 批量导入时,如果某行数据导入失败,会在返回信息中说明具体原因
7. 批量导入时如果全部数据导入失败会返回错误状态码10012

13
pom.xml
View File

@ -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>

View File

@ -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;
}

View File

@ -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");
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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);
}
}

View File

@ -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,20 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.beans.BeanUtils;
import java.util.ArrayList;
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")
@ -192,6 +203,74 @@ 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("课程名称不能为空");

View File

@ -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
@ -77,7 +80,7 @@ public class UserService implements UserDetailsService {
user.setCreatorId(request.getCreatorId());
user.setStatus(1);
// 时间戳由 @PrePersist 处理这里不需要手动设置
userRepository.save(user);
return ApiResponse.success();
@ -183,22 +186,83 @@ public class UserService implements UserDetailsService {
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) {
return new ApiResponse<>(ErrorCode.INVALID_PARAM, "用户状态异常", null);
}
// 清除敏感信息
user.setPassword(null);
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.