前言
今天我们来学习学员管理模块的实现。
相比前面已经完成的班级管理,学员管理这一部分会更复杂一些。因为它不仅要实现基础的增删改查,还要完成条件分页查询、班级名称关联显示、违纪处理以及后续统计功能的预留。
一、学员管理模块实现
1. 学员条件分页查询
1.1 功能需求
学员列表页需要支持:
- 按姓名模糊查询
- 按学历筛选
- 按班级筛选
- 分页查询
- 展示班级名称
1.2 Controller 层实现
java
@GetMapping
public Result getStudent(studentQueryParam studentqueryparam){
log.info("条件分页查询学生信息");
PageResult<Student> pageResult = studentservice.getbytime(studentqueryparam);
return Result.success(pageResult);
}
1.3 Service 层实现
java
@Override
public PageResult<Student> getbytime(studentQueryParam studentqueryparam) {
if (studentqueryparam.getPage() == null) {
studentqueryparam.setPage(1);
}
if (studentqueryparam.getPageSize() == null) {
studentqueryparam.setPageSize(10);
}
PageHelper.startPage(studentqueryparam.getPage(), studentqueryparam.getPageSize());
List<Student> list = studentMapper.getbytime(studentqueryparam);
Page<Student> p = (Page<Student>) list;
return new PageResult<>(p.getTotal(), p.getResult());
}
1.4 Mapper 层实现
xml
<select id="getbytime" resultType="com.heimait.pojo.Student">
SELECT s.*, c.name clazzName
FROM student s
LEFT JOIN clazz c ON s.clazz_id = c.id
ORDER BY s.violation_count DESC
<where>
<if test="name != null and name != ''">
AND s.name LIKE CONCAT('%', #{name}, '%')
</if>
<if test="degree != null">
AND s.degree = #{degree}
</if>
<if test="clazzId != null">
AND s.clazz_id = #{clazzId}
</if>
</where>
</select>
1.5 文字说明
学员分页查询和班级分页查询的整体思路是一样的,只不过这次多了两个查询条件:
- 学历
- 班级 ID
同时通过 LEFT JOIN clazz 把班级名称 clazzName 一起查出来,方便前端直接展示。
1.6 涉及知识点
1. 查询参数对象复用
studentQueryParam 这个类同时承担了班级和学员的部分查询参数封装职责。
这种做法虽然能复用,但从规范角度来看,后续如果项目继续扩大,最好拆成更独立的参数类。
2. 关联查询补充显示字段
clazzName 和数据库表字段不是一一对应的,它是关联查询补出来的扩展字段。
2. 新增学员
2.1 Controller 层实现
java
@PostMapping
public Result add(@RequestBody Student student){
log.info("添加学生信息");
studentservice.add(student);
return Result.success();
}
2.2 Service 层实现
java
@Override
public void add(Student student) {
student.setCreateTime(LocalDateTime.now());
student.setUpdateTime(LocalDateTime.now());
if (student.getViolationCount() == null) {
student.setViolationCount((short) 0);
}
if (student.getViolationScore() == null) {
student.setViolationScore((short) 0);
}
if (student.getGender() == null) {
student.setGender(0);
}
if (student.getIsCollege() == null) {
student.setIsCollege(0);
}
if (student.getDegree() == null) {
student.setDegree(0);
}
if (student.getClazzId() == null) {
student.setClazzId(0);
}
studentMapper.insert(student);
}
2.3 Mapper 层实现
xml
<insert id="insert">
INSERT INTO student (name, no, gender, phone, id_card, is_college, address, degree, graduation_date,
clazz_id, violation_count, violation_score, create_time, update_time)
VALUES (#{name}, #{no}, #{gender}, #{phone}, #{idCard}, #{isCollege}, #{address}, #{degree},
#{graduationDate}, #{clazzId}, #{violationCount}, #{violationScore}, #{createTime}, #{updateTime})
</insert>
2.4 文字说明
新增学员时,这里除了补时间,还做了一件很重要的事:给很多字段设置默认值。
原因很简单,前端有些字段可能不传,如果后端不兜底,就可能导致数据库插入时报错。
我当时写的时候气炸了,为什么要传那么多,因为建立数据库表的时候规定了不能空
比如:
violationCountviolationScore
这些字段在业务上很明显应该默认是 0。
2.5 涉及知识点
1. Service 层做默认值兜底
前端参数并不一定总是完整可靠,所以一些默认值处理放在 Service 层非常有必要。
2. 为什么不能完全依赖前端
因为前端校验是"用户体验层面"的校验,而后端校验才是"真正保证数据安全"的校验。
3. 根据 ID 查询学员详情
3.1 Controller 层实现
java
@GetMapping("/{id}")
public Result getById(@PathVariable Integer id){
log.info("根据id查询学生信息");
Student student = studentservice.getById(id);
return Result.success(student);
}
3.2 Service 层实现
java
@Override
public Student getById(Integer id) {
return studentMapper.selectByKey(id);
}
3.3 Mapper 层实现
xml
<select id="selectByKey" resultType="com.heimait.pojo.Student">
SELECT * FROM student WHERE id = #{id}
</select>
3.4 文字说明
这个功能和班级详情查询类似,主要用于学员编辑前的数据回显。
4. 修改学员
4.1 Controller 层实现
java
@PutMapping
public Result update(@RequestBody Student student){
log.info("修改学生信息");
studentservice.update(student);
return Result.success();
}
4.2 Service 层实现
java
@Override
public void update(Student student) {
student.setUpdateTime(LocalDateTime.now());
studentMapper.update(student);
}
4.3 Mapper 层实现
xml
<update id="update">
Update student
set name=#{name}, no=#{no}, gender=#{gender}, phone=#{phone}, id_card=#{idCard},
is_college=#{isCollege}, address=#{address}, degree=#{degree},
graduation_date=#{graduationDate}, clazz_id=#{clazzId},
violation_count=#{violationCount}, violation_score=#{violationScore},
update_time=#{updateTime}
where id=#{id}
</update>
4.4 文字说明
修改学员信息时,本质上还是标准的更新逻辑。
不过这里也能看出来,当前这个更新语句会把违纪次数和违纪扣分一起更新,所以前端在提交修改时要注意数据完整性。
5. 删除学员
5.1 Controller 层实现
java
@DeleteMapping("/{id}")
public Result delete(@PathVariable Integer id){
log.info("删除学生信息");
studentservice.delete(id);
return Result.success();
}
5.2 Service 层实现
java
@Override
public void delete(Integer id) {
studentMapper.delete(id);
}
5.3 Mapper 层实现
xml
<delete id="delete">
DELETE FROM student WHERE id = #{id}
</delete>
5.4 文字说明
这个功能比较直接,因为删除学生不需要像删除班级那样做额外关联校验。
6. 学员违纪处理
6.1 功能需求
学员违纪处理一次时:
- 违纪次数
+1 - 违纪扣分
+前端输入分数
6.2 Controller 层实现
java
@PutMapping("/violation/{id}/{score}")
public Result violation(@PathVariable Integer id,@PathVariable Integer score){
log.info("违纪处理扣分");
studentservice.violation(id,score);
return Result.success();
}
6.3 Service 层实现
java
@Override
public void violation(Integer id, Integer score) {
Student student = studentMapper.selectByKey(id);
if (student == null) {
throw new RuntimeException("对不起 没有找到该学员");
}
student.setViolationCount((short) (student.getViolationCount() + 1));
student.setViolationScore((short) (student.getViolationScore() + score));
student.setUpdateTime(LocalDateTime.now());
studentMapper.update(student);
}
6.4 Mapper 层实现
这里没有单独写新的 SQL,而是复用了原有的更新方法:
xml
<update id="update">
Update student
set name=#{name}, no=#{no}, gender=#{gender}, phone=#{phone}, id_card=#{idCard},
is_college=#{isCollege}, address=#{address}, degree=#{degree},
graduation_date=#{graduationDate}, clazz_id=#{clazzId},
violation_count=#{violationCount}, violation_score=#{violationScore},
update_time=#{updateTime}
where id=#{id}
</update>
6.5 文字说明
这个功能很值得单独拿出来讲,因为它已经不是简单 CRUD 了,而是一个标准的"业务状态更新"。
处理过程是:
- 先根据 ID 查询学生是否存在
- 存在则把违纪次数
+1 - 再把违纪分数累加
- 最后更新时间并写回数据库
6.6 涉及知识点
1. 业务型更新接口
并不是所有更新都是前端传一个完整对象,后端直接 update。
像违纪处理这种功能,本质上是围绕业务规则做状态变更。
2. 先查再改
这种写法的好处是逻辑清晰,容易理解。
后续如果要进一步优化,也可以考虑直接写成 SQL 累加。
三、统计功能实现
在 ClazzMapper 中,还预留了两个统计查询,非常适合后续对接图表展示。
1. 按学历统计学员数量
1.1 Mapper 层实现
xml
<select id="countstudentbydegree" resultType="java.util.Map">
SELECT
CASE degree
WHEN 1 THEN '初中'
WHEN 2 THEN '高中'
WHEN 3 THEN '大专'
WHEN 4 THEN '本科'
WHEN 5 THEN '硕士'
WHEN 6 THEN '博士'
ELSE '其他'
END name,
COUNT(*) AS value
FROM student
GROUP BY degree
</select>
1.2 文字说明
这个 SQL 会把数据库中的学历编码,直接转换成前端更容易展示的中文名称,同时统计每种学历的人数。
这种结果特别适合拿去做饼图、柱状图。
2. 按班级统计学员数量
2.1 Mapper 层实现
xml
<select id="countstudentbyclass" resultType="java.util.Map">
SELECT c.name, COUNT(*) AS value
FROM student
LEFT JOIN clazz c ON student.clazz_id = c.id
GROUP BY c.name
</select>
2.2 文字说明
这个统计接口主要用于查看每个班级有多少学员。
本质上就是一个分组统计查询。
2.3 涉及知识点
分组统计 GROUP BY
统计类接口中,GROUP BY 是非常高频的 SQL 语法。
它可以把多条明细数据聚合成前端图表需要的结果。
总结
这一篇博客重点围绕学员管理模块展开,完整实现了:
- 学员条件分页查询
- 新增学员
- 根据 ID 查询学员详情
- 修改学员
- 删除学员
- 学员违纪处理
- 学员统计接口预留
和基础 CRUD 相比,学员管理模块已经更贴近真实业务开发。
因为它不仅仅是在做简单的数据维护,还加入了:
- 条件筛选
- 分页查询
- 班级关联显示
- 默认值兜底
- 业务型更新
- 分组统计
所以这一部分内容,既能帮助我们继续理解三层架构,也能让我们进一步熟悉真实项目中的业务处理方式。
我是新手程序猿乐锅。本次分享到此结束,感谢大家的观看与支持!如果本期内容对您有帮助,欢迎点赞、收藏,您的支持将是我持续创作的最大动力,谢谢!