Skip to content
This repository was archived by the owner on Apr 14, 2026. It is now read-only.

Commit 2d4dfa0

Browse files
committed
feat: implement Excel import functionality for student account creation
- Add Apache POI dependencies for Excel file processing - Update /review/import endpoint to support multipart file uploads - Implement Excel parsing to read student code and name from uploaded files - Add User entity creation with password encryption and salt generation - Configure User entity with auto-increment ID annotation - Generate passwords as studentCode + 6 random lowercase letters - Return account information with original passwords and student codes
1 parent 8c1198f commit 2d4dfa0

6 files changed

Lines changed: 188 additions & 6 deletions

File tree

approval-pojo/src/main/java/fun/sast/entity/User.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package fun.sast.entity;
22

3+
import com.baomidou.mybatisplus.annotation.IdType;
4+
import com.baomidou.mybatisplus.annotation.TableId;
35
import java.io.Serializable;
46
import java.time.LocalDateTime;
57
import lombok.AllArgsConstructor;
@@ -15,6 +17,7 @@ public class User implements Serializable {
1517
private Integer depId;
1618

1719
/** 用户id编号 */
20+
@TableId(type = IdType.AUTO)
1821
private Integer id;
1922

2023
/** 用户姓名 */

approval-server/pom.xml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,16 @@
109109
<artifactId>spring-boot-starter-aop</artifactId>
110110
</dependency>
111111

112+
<dependency>
113+
<groupId>org.apache.poi</groupId>
114+
<artifactId>poi</artifactId>
115+
</dependency>
116+
117+
<dependency>
118+
<groupId>org.apache.poi</groupId>
119+
<artifactId>poi-ooxml</artifactId>
120+
</dependency>
121+
112122
</dependencies>
113123

114124
<build>

approval-server/src/main/java/fun/sast/controller/reviewController/ReviewController.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import java.util.Map;
1111
import lombok.RequiredArgsConstructor;
1212
import org.springframework.web.bind.annotation.*;
13+
import org.springframework.web.multipart.MultipartFile;
1314

1415
@RestController
1516
@RequiredArgsConstructor
@@ -21,13 +22,16 @@ public class ReviewController {
2122
/**
2223
* 导入学生账号并导出账号 excel
2324
*
24-
* @param depId 学院 ID
25+
* @param depId 学院 ID (可选)
26+
* @param file Excel 文件 (可选)
2527
* @return List<Account> 账号列表
2628
*/
2729
@ResponseResult
2830
@PostMapping("/import")
29-
public List<AccountImportVO> importAccount(@RequestParam String depId) {
30-
return reviewService.importAccount(depId);
31+
public List<AccountImportVO> importAccount(
32+
@RequestParam(required = false) String depId,
33+
@RequestParam(value = "file", required = false) MultipartFile file) {
34+
return reviewService.importAccount(depId, file);
3135
}
3236

3337
/**

approval-server/src/main/java/fun/sast/service/ReviewService.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@
44
import fun.sast.vo.CompetitionListVO;
55
import fun.sast.vo.WorkReviewListVO;
66
import java.util.List;
7+
import org.springframework.web.multipart.MultipartFile;
78

89
public interface ReviewService {
9-
List<AccountImportVO> importAccount(String comId);
10+
List<AccountImportVO> importAccount(String depId, MultipartFile file);
1011

1112
void uploadReview(String id, boolean accept, String opinion);
1213

approval-server/src/main/java/fun/sast/service/impl/ReviewServiceImpl.java

Lines changed: 153 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,19 @@
2424
import fun.sast.vo.CompetitionListVO;
2525
import fun.sast.vo.WorkReviewListVO;
2626
import fun.sast.vo.WorkReviewVO;
27+
import java.io.IOException;
28+
import java.security.SecureRandom;
2729
import java.time.LocalDateTime;
2830
import java.util.ArrayList;
2931
import java.util.List;
3032
import java.util.stream.Collectors;
3133
import lombok.RequiredArgsConstructor;
34+
import org.apache.poi.ss.usermodel.*;
35+
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
36+
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
3237
import org.springframework.stereotype.Service;
38+
import org.springframework.web.multipart.MultipartFile;
39+
import org.springframework.util.DigestUtils;
3340

3441
@Service
3542
@RequiredArgsConstructor
@@ -44,8 +51,152 @@ public class ReviewServiceImpl implements ReviewService {
4451
private static final int PAGE_SIZE = 10;
4552

4653
@Override
47-
public List<AccountImportVO> importAccount(String comId) {
48-
return null;
54+
public List<AccountImportVO> importAccount(String depId, MultipartFile file) {
55+
List<AccountImportVO> accounts = new ArrayList<>();
56+
57+
if (file != null && !file.isEmpty()) {
58+
try {
59+
List<StudentInfo> students = parseExcelFile(file);
60+
accounts = createUsersAndGenerateAccounts(students, depId);
61+
} catch (IOException e) {
62+
throw new BaseException(ErrorEnum.IMPORT_ERROR);
63+
}
64+
}
65+
66+
return accounts;
67+
}
68+
69+
private static class StudentInfo {
70+
String code;
71+
String name;
72+
73+
StudentInfo(String code, String name) {
74+
this.code = code;
75+
this.name = name;
76+
}
77+
}
78+
79+
private List<StudentInfo> parseExcelFile(MultipartFile file) throws IOException {
80+
List<StudentInfo> students = new ArrayList<>();
81+
82+
Workbook workbook = null;
83+
try {
84+
String fileName = file.getOriginalFilename();
85+
if (fileName != null && fileName.endsWith(".xlsx")) {
86+
workbook = new XSSFWorkbook(file.getInputStream());
87+
} else if (fileName != null && fileName.endsWith(".xls")) {
88+
workbook = new HSSFWorkbook(file.getInputStream());
89+
} else {
90+
throw new BaseException(ErrorEnum.INVALID_FILE_TYPE_ERROR);
91+
}
92+
93+
Sheet sheet = workbook.getSheetAt(0);
94+
95+
for (int i = 1; i <= sheet.getLastRowNum(); i++) {
96+
Row row = sheet.getRow(i);
97+
if (row != null) {
98+
Cell codeCell = row.getCell(0);
99+
Cell nameCell = row.getCell(1);
100+
101+
if (codeCell != null && nameCell != null) {
102+
String studentCode = getCellValueAsString(codeCell);
103+
String studentName = getCellValueAsString(nameCell);
104+
105+
if (studentCode != null && !studentCode.trim().isEmpty() &&
106+
studentName != null && !studentName.trim().isEmpty()) {
107+
students.add(new StudentInfo(studentCode.trim(), studentName.trim()));
108+
}
109+
}
110+
}
111+
}
112+
} finally {
113+
if (workbook != null) {
114+
workbook.close();
115+
}
116+
}
117+
118+
return students;
119+
}
120+
121+
private String getCellValueAsString(Cell cell) {
122+
if (cell == null) {
123+
return null;
124+
}
125+
126+
switch (cell.getCellType()) {
127+
case STRING:
128+
return cell.getStringCellValue();
129+
case NUMERIC:
130+
if (DateUtil.isCellDateFormatted(cell)) {
131+
return cell.getDateCellValue().toString();
132+
} else {
133+
return String.valueOf((long) cell.getNumericCellValue());
134+
}
135+
case BOOLEAN:
136+
return String.valueOf(cell.getBooleanCellValue());
137+
case FORMULA:
138+
return cell.getCellFormula();
139+
default:
140+
return null;
141+
}
142+
}
143+
144+
private List<AccountImportVO> createUsersAndGenerateAccounts(List<StudentInfo> students, String depId) {
145+
List<AccountImportVO> accounts = new ArrayList<>();
146+
User currentUser = UserInterceptor.userHolder.get();
147+
Long operatorId = currentUser != null ? currentUser.getId().longValue() : 0L;
148+
Integer departmentId = depId != null ? Integer.parseInt(depId) : null;
149+
150+
for (StudentInfo student : students) {
151+
String randomSuffix = generateRandomPassword();
152+
String password = student.code + randomSuffix;
153+
String salt = generateSalt();
154+
String encryptedPassword = encryptPassword(password, salt);
155+
156+
User user = new User();
157+
user.setDepId(departmentId);
158+
user.setName(student.name);
159+
user.setCode(student.code);
160+
user.setPassword(encryptedPassword);
161+
user.setSalt(salt);
162+
user.setRole(UserRoleEnum.STUDENT.getRole());
163+
user.setMajor("");
164+
user.setContact("");
165+
user.setCreateTime(LocalDateTime.now());
166+
user.setUpdateTime(LocalDateTime.now());
167+
user.setCreateUser(operatorId);
168+
user.setUpdateUser(operatorId);
169+
170+
userMapper.insert(user);
171+
accounts.add(new AccountImportVO(password, student.code));
172+
}
173+
174+
return accounts;
175+
}
176+
177+
private String generateSalt() {
178+
return generateRandomString(16);
179+
}
180+
181+
private String encryptPassword(String password, String salt) {
182+
return DigestUtils.md5DigestAsHex((password + salt).getBytes());
183+
}
184+
185+
186+
private String generateRandomPassword() {
187+
return generateRandomString(6);
188+
}
189+
190+
private String generateRandomString(int length) {
191+
String chars = "abcdefghijklmnopqrstuvwxyz";
192+
SecureRandom random = new SecureRandom();
193+
StringBuilder result = new StringBuilder();
194+
195+
for (int i = 0; i < length; i++) {
196+
result.append(chars.charAt(random.nextInt(chars.length())));
197+
}
198+
199+
return result.toString();
49200
}
50201

51202
@Override

pom.xml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
<bucket4j.version>8.10.1</bucket4j.version>
4141
<aspectj.version>1.9.24</aspectj.version>
4242
<qcloud.version>5.6.227</qcloud.version>
43+
<poi.version>5.2.5</poi.version>
4344

4445
</properties>
4546

@@ -184,6 +185,18 @@
184185
<version>${qcloud.version}</version>
185186
</dependency>
186187

188+
<dependency>
189+
<groupId>org.apache.poi</groupId>
190+
<artifactId>poi</artifactId>
191+
<version>${poi.version}</version>
192+
</dependency>
193+
194+
<dependency>
195+
<groupId>org.apache.poi</groupId>
196+
<artifactId>poi-ooxml</artifactId>
197+
<version>${poi.version}</version>
198+
</dependency>
199+
187200
</dependencies>
188201
</dependencyManagement>
189202

0 commit comments

Comments
 (0)