2025山东大学软件学院创新项目实训博客(四)
时间
2025.3.31-2025.4.6
工作内容
本周按照项目计划,继续推进后端开发工作,主要完成教师服务功能实现、用户认证功能的整体实现以及相关接口的完善。同时对上周开发的功能进行测试和优化,确保系统稳定性和安全性。
预计产出:教师服务实现类、教师相关API接口、用户认证完整功能、测试报告
详细内容
教师服务实现
学生列表管理
实现了教师查看和管理自己课程学生的功能:
实现思路
addStudent
方法:此方法旨在将学生添加至教师所教授课程的学生列表中。首先严格校验传入的studentId
和teacherId
,一旦发现为空,立即返回Code.CODE_PARAM_ERROR
错误码,并附带 “学生 ID 和教师 ID 不能为空” 的错误提示,以此确保关键数据的完整性。在验证通过后,创建AddStudentBody
对象,将studentId
和teacherId
封装其中,随后调用teacherDao.addStudent
方法,将学生与教师的关联关系持久化到数据库。倘若在操作期间遭遇异常,通过log.error
记录详细的错误日志,并返回Code.CODE_SERVER_ERROR
错误码以及 “添加学生失败” 的提示信息 ,保证问题可追溯。
deleteStudent
方法:该方法用于从教师的课程学生列表中移除指定学生。同样先对studentId
和teacherId
进行非空验证,若不符合要求,返回Code.CODE_PARAM_ERROR
错误码及对应提示。验证通过后,直接调用teacherDao.deleteStudent
方法执行数据库删除操作,当出现异常时,利用log.error
记录 “移除学生失败” 的错误日志,并返回Code.CODE_SERVER_ERROR
错误码和相应错误提示,确保数据删除操作的准确性和可查性。
getStudentList
方法:该方法用于获取教师的学生列表,其逻辑根据teacherId
是否为空分情况处理。当teacherId
为空时,先调用userDao.getUserList
获取所有用户列表,若获取成功且返回数据有效,对用户列表进行筛选,仅保留类型为学生(UserConstants.TYPE_STUDENT
)的用户,重新封装成UserListResponse
对象后返回;当teacherId
不为空时,通过teacherDao.getStudentList
从数据库获取该教师对应的学生列表,将列表中的Student
对象转换为User
对象,构建新的UserListResponse
对象返回。无论哪种情况,若在获取过程中出现异常,均通过log.error
记录 “获取学生列表失败” 的日志,并返回Code.CODE_SERVER_ERROR
错误码及对应提示。
@Override
public Result<Void> addStudent(String studentId, String teacherId) {
try {
if (studentId == null || teacherId == null) {
return Result.fail(Code.CODE_PARAM_ERROR, "学生ID和教师ID不能为空");
}
AddStudentBody addStudentBody=new AddStudentBody();
addStudentBody.setStudentId(studentId);
addStudentBody.setTeacherId(teacherId);
return teacherDao.addStudent(addStudentBody);
} catch (Exception e) {
log.error("添加学生失败", e);
return Result.fail(Code.CODE_SERVER_ERROR, "添加学生失败");
}
}
@Override
public Result<Void> deleteStudent(String studentId, String teacherId) {
try {
if (studentId == null || teacherId == null) {
return Result.fail(Code.CODE_PARAM_ERROR, "学生ID和教师ID不能为空");
}
return teacherDao.deleteStudent(studentId,teacherId);
} catch (Exception e) {
log.error("移除学生失败", e);
return Result.fail(Code.CODE_SERVER_ERROR, "移除学生失败");
}
}
@Override
public Result<UserListResponse> getStudentList(String teacherId) {
try {
Result<UserListResponse> result;
if(teacherId==null){
result=userDao.getUserList();
if(result.getCode()==0)
{
//过滤出学生用户
UserListResponse userListResponse= result.getData();
if(userListResponse!=null){
List<User> filteredUsers = userListResponse.getUserList().stream()
.filter(user -> user.getType() == UserConstants.TYPE_STUDENT)
.collect(Collectors.toList());
UserListResponse filteredUserListResponse = new UserListResponse();
filteredUserListResponse.setUserList(filteredUsers);
result = Result.success(filteredUserListResponse);
}
}
}
else {
Result<StudentListResponse> studentListResponse = teacherDao.getStudentList(teacherId);
List<Student> students = studentListResponse.getData().getStudents();
List<User> uStudents =new ArrayList<>();
for(Student s : students)
{
User u = new User();
u.setAccount(s.getAccount());
u.setName(s.getName());
u.setType(Integer.valueOf(s.getType()));
uStudents.add(u);
}
UserListResponse userListResponse = new UserListResponse();
userListResponse.setUserList(uStudents);
result = Result.success(userListResponse);
}
System.out.println(result);
if (result.getCode() != Code.CODE_SUCCESS) {
return Result.fail(result.getCode(), result.getMessage());
}
return Result.success(result.getData());
} catch (Exception e) {
log.error("获取学生列表失败", e);
return Result.fail(Code.CODE_SERVER_ERROR, "获取学生列表失败");
}
}
作业管理
实现思路
publishHomework
方法:作为作业发布的核心方法,首先对传入的homework
对象进行全面检查,重点验证homework
对象本身以及其中的courseId
是否为空,若存在缺失,即刻返回Code.CODE_INVALID_PARAM
错误码和 “作业信息不完整” 的提示。在验证通过后,设置作业状态为 “已发布”(HomeworkStatus.PUBLISHED.getValue()
),并记录作业的创建时间为当前时间(new Date()
),接着借助homeworkMapper.insert
方法将作业信息插入数据库。依据插入操作影响的行数判断结果,若行数大于 0,表明插入成功,通过logger.info
记录教师发布作业的日志,并返回包含作业 ID 的成功结果;否则返回Code.CODE_SYSTEM_ERROR
错误码和 “发布作业失败” 的提示。
getHomeworkList
方法:该方法主要负责根据课程 ID 获取对应的作业列表。首先对传入的courseId
进行非空验证,若为空则返回Code.CODE_INVALID_PARAM
错误码和 “课程 ID 不能为空” 的提示。验证通过后,调用homeworkMapper.selectByCourseId
方法从数据库查询该课程下的所有作业列表,将查询结果封装在Result
对象中返回,若查询过程出现异常,同样返回相应的错误提示。
endHomework
方法:用于结束指定作业,首先对传入的homeworkId
进行非空验证,若为空返回Code.CODE_INVALID_PARAM
错误码和 “作业 ID 不能为空” 的提示。验证通过后,使用homeworkMapper.selectById
方法根据homeworkId
从数据库查询作业信息,若未查询到对应作业,则返回Code.CODE_NOT_FOUND
错误码和 “作业不存在” 的提示。若查询到作业,将作业状态设置为 “已结束”(HomeworkStatus.ENDED.getValue()
),通过homeworkMapper.updateById
方法更新作业状态,根据更新影响的行数判断操作结果,若行数大于 0,记录结束作业的日志并返回成功结果,否则返回Code.CODE_SYSTEM_ERROR
错误码和 “结束作业失败” 的提示。
完成了作业的发布、查看和结束等功能:
javaCopy@Override
public Result<?> publishHomework(Homework homework) {
if (homework == null || homework.getCourseId() == null) {
return Result.fail(Code.CODE_INVALID_PARAM, "作业信息不完整");
}
// 设置作业状态为已发布
homework.setStatus(HomeworkStatus.PUBLISHED.getValue());
homework.setCreateTime(new Date());
int rows = homeworkMapper.insert(homework);
if (rows > 0) {
// 记录日志
logger.info("教师 {} 发布了作业 {}", homework.getTeacherId(), homework.getHomeworkId());
return Result.success(homework.getHomeworkId());
} else {
return Result.fail(Code.CODE_SYSTEM_ERROR, "发布作业失败");
}
}
@Override
public Result<List<Homework>> getHomeworkList(Integer courseId) {
if (courseId == null) {
return Result.fail(Code.CODE_INVALID_PARAM, "课程ID不能为空");
}
List<Homework> homeworks = homeworkMapper.selectByCourseId(courseId);
return Result.success(homeworks);
}
@Override
public Result<?> endHomework(Integer homeworkId) {
if (homeworkId == null) {
return Result.fail(Code.CODE_INVALID_PARAM, "作业ID不能为空");
}
Homework homework = homeworkMapper.selectById(homeworkId);
if (homework == null) {
return Result.fail(Code.CODE_NOT_FOUND, "作业不存在");
}
// 设置作业状态为已结束
homework.setStatus(HomeworkStatus.ENDED.getValue());
int rows = homeworkMapper.updateById(homework);
if (rows > 0) {
// 记录日志
logger.info("教师结束了作业 {}", homeworkId);
return Result.success();
} else {
return Result.fail(Code.CODE_SYSTEM_ERROR, "结束作业失败");
}
}
用户认证功能完善
在上周JWT工具类的基础上,本周完成了完整的用户认证功能:
用户登录功能
实现思路
login
接口接收account
和password
两个请求参数,首先利用StringUtils.isEmpty
方法对参数进行严格校验,一旦发现账号或密码为空,立即返回Code.CODE_INVALID_PARAM
错误码和 “账号或密码不能为空” 的提示。接着通过userService.getUserByAccount
方法根据账号查询用户信息,若未查询到对应用户,返回Code.CODE_NOT_FOUND
错误码和 “用户不存在” 的提示。在获取到用户信息后,使用passwordEncoder.matches
方法对传入的密码和数据库中存储的加密密码进行比对,若密码不匹配,返回Code.CODE_INVALID_PASSWORD
错误码和 “密码错误” 的提示。当密码验证通过后,调用jwtUtil.generateToken
方法生成 JWT 令牌,将令牌封装在HashMap
中,并以Result.success
的形式返回给前端,确保用户身份认证和令牌发放的准确性。
实现了管理员和教师两种角色的登录,包括表单验证、状态管理和路由跳转:
javaCopy@PostMapping("/login")
public Result<?> login(@RequestParam String account, @RequestParam String password) {
// 参数校验
if (StringUtils.isEmpty(account) || StringUtils.isEmpty(password)) {
return Result.fail(Code.CODE_INVALID_PARAM, "账号或密码不能为空");
}
// 查询用户
User user = userService.getUserByAccount(account);
if (user == null) {
return Result.fail(Code.CODE_NOT_FOUND, "用户不存在");
}
// 验证密码
if (!passwordEncoder.matches(password, user.getPassword())) {
return Result.fail(Code.CODE_INVALID_PASSWORD, "密码错误");
}
// 生成JWT令牌
String token = jwtUtil.generateToken(user);
// 返回结果
Map<String, String> data = new HashMap<>();
data.put("token", token);
return Result.success(data);
}
用户注册功能
实现思路
register
方法在用户注册流程中起着关键作用。首先调用userDao.getUser
方法,传入空的用户 ID 和待注册的账号,检查该账号是否已被注册,若查询结果中存在对应数据,即表明账号已存在,返回Code.CODE_PARAM_ERROR
错误码和 “账号已存在” 的提示。若账号可用,进一步检查用户传入的type
字段,若为空则将用户类型设置为默认的学生类型(UserConstants.TYPE_STUDENT
)。随后,使用passwordUtil.encode
方法对用户密码进行加密处理,最后调用userDao.addUser
方法将处理后的用户信息添加到数据库,依据添加操作的结果返回相应的成功或失败信息,保障注册流程的安全性和数据准确性。
完成了用户注册功能,包括表单验证、密码确认和页面跳转:
@Override
public Result<Void> register(User user) {
// 检查账号是否已存在
Result<User> existingUser = userDao.getUser(null, user.getAccount());
if (existingUser.getData() != null) {
return Result.fail(Code.CODE_PARAM_ERROR, "账号已存在");
}
// 设置用户类型(默认为学生)
if (user.getType() == null) {
user.setType(UserConstants.TYPE_STUDENT);
}
// 加密密码
user.setPassword(passwordUtil.encode(user.getPassword()));
// 添加用户
return userDao.addUser(user);
}
重置密码功能
实现了忘记密码后的重置功能:
实现思路
forgetPassword
方法用于处理用户忘记密码后的重置操作。首先通过userDao.getUser
方法,传入空的用户 ID 和需要重置密码的账号,获取用户信息,若未查询到对应账号,返回Code.CODE_NOT_FOUND
错误码和 “账号不存在” 的提示。在获取到用户信息后,使用passwordUtil.encode
方法对新传入的密码进行加密处理,将加密后的密码更新到用户对象中,接着调用userDao.updateUser
方法更新数据库中的用户密码信息,根据更新操作的结果返回相应的成功或失败信息,确保密码重置过程的安全可靠。
@Override
public Result<Void> forgetPassword(String account, String password) {
// 获取用户信息
Result<User> userResult = userDao.getUser(null, account);
if (userResult.getData() == null) {
return Result.fail(Code.CODE_NOT_FOUND, "账号不存在");
}
// 更新密码(加密)
User user = userResult.getData();
user.setPassword(passwordUtil.encode(password));
return userDao.updateUser(user);
}
后端接口完善
本周根据前端需求,对API接口进行了完善和测试,确保接口的可用性和稳定性。主要修改了以下接口:
- 用户登录接口优化:添加了更详细的错误提示和日志记录
- 用户信息获取接口增强:支持根据不同条件查询用户信息
- 密码重置接口改进:增加了安全验证步骤
- 删除用户接口完善:添加了级联删除相关数据的功能
- 修改用户接口优化:支持部分字段更新
测试结果
进行了全面的功能测试和边界测试,测试结果如下:
- 功能测试通过
- 管理员登录
- 教师登录
- 管理员注册
- 教师注册
- 管理员重置密码
- 教师重置密码
- 表单验证
- 错误提示
- 路由跳转
- 边界测试通过
- 空表单提交
- 密码不一致
- 网络错误处理
- 登录状态保持
本周工作结果
- 完成TeacherServiceImpl类实现:实现了教师端主要业务逻辑,包括教师信息管理、学生列表管理、作业管理和题目管理。
- 实现用户认证完整功能:完成了用户登录、注册和密码重置功能,提升了系统安全性。
- 完善API接口:根据前端需求优化了接口设计,提高了系统的可用性。
- 添加代码注释和日志记录:规范了代码风格,便于后续维护。
- 完成功能测试:对所有实现的功能进行了测试,确保系统稳定可靠。
总结
本周在后端功能开发上取得了显著进展,完成了教师服务实现类和用户认证功能的开发,为前端提供了完整的API支持。通过统一使用Code
类中定义的常量作为错误码,使系统响应更加规范和一致。同时,完善的日志记录和代码注释也提高了代码的可维护性。