🤖 Tlias 系统 - 作业部分_2
前面完成了
班级管理部分的内容,接着从学员管理继续 ...
四、学员管理
4.1 查询所有班级接口
4.1.1 需求

- 在新增学员的时候,要展示出所有的班级信息。
4.1.2 接口文档
参照接口文档
班级管理-->查询所有班级请求路径:
/clazzs/list请求方式:
GET接口描述:该接口用于查询所有班级信息
请求参数:无
响应数据案例:
{
"code": 1,
"msg": "success",
"data":[
{
"id": 7,
"name": "黄埔四期",
"room": "209",
"beginDate": "2023-08-01",
"endDate": "2024-02-15",
"masterId": 7,
"subject": 1,
"createTime": "2023-06-01T17:51:21",
"updateTime": "2023-06-01T17:51:21"
},
{
"id": 6,
"name": "JavaEE就业166期",
"room": "105",
"beginDate": "2023-07-20",
"endDate": "2024-02-20",
"masterId": 20,
"subject": 1,
"createTime": "2023-06-01T17:46:10",
"updateTime": "2023-06-01T17:46:10"
}
]
}4.1.3 功能开发
ClazzController增加list()方法
//查询所有班级接口(用于新增学员的时候,要展示出所有的班级信息。)
@GetMapping("/list")
public Result list(){
log.info("查询所有班级接口 ... ");
List<Clazz> clazzList = clazzService.list();
return Result.success(clazzList);
}ClazzService定义接口方法list()
//查询所有班级信息
List<Clazz> list();ClazzServiceImpl实现接口方法list()
//查询所有班级信息
@Override
public List<Clazz> list() {
return clazzMapper.getAllInfo();
}ClazzMapper接口中增加getAllInfo()
//查询所有班级信息
@Select("select * from clazz")
List<Clazz> getAllInfo();4.1.4 测试
Apifox测试

因为还没有写
学生的分页查询所以暂时还不能前后端联调
4.2 条件分页查询接口
4.2.1 需求

- 分页查找出数据库所有的学生信息 , 若有条件按条件筛选查询
4.2.2 接口文档
参照接口文档
学员管理-->学员列表查询请求路径:
/students请求方式:
GET接口描述:该接口用于学员列表数据的条件分页查询
请求参数格式:
queryString请求数据样例:
/students?name=张三°ree=1&clazzId=2&page=1&pageSize=5响应数据样例:
{
"code": 1,
"msg": "success",
"data": {
"total": 5,
"rows": [
{
"id": 3,
"name": "Lily",
"no": "2023001003",
"gender": 2,
"phone": "13309230912",
"degree": 4,
"idCard": "110090110090110090",
"isCollege": 0,
"address": "回龙观东大街110号",
"graduationDate": "2020-07-01",
"violationCount": 2,
"violationScore": 5,
"clazzId": 1,
"createTime": "2023-06-01T18:35:23",
"updateTime": "2023-06-01T19:37:42",
"clazzName": "黄埔班一期"
},
{
"id": 4,
"name": "Jerry",
"no": "2023001004",
"gender": 1,
"phone": "15309232323",
"degree": 4,
"idCard": "110090110090110090",
"isCollege": 0,
"address": "回龙观东大街110号",
"graduationDate": "2020-07-01",
"violationCount": 1,
"violationScore": 2,
"clazzId": 1,
"createTime": "2023-06-01T18:35:48",
"updateTime": "2023-06-01T19:37:35",
"clazzName": "黄埔班一期"
}
]
}
}4.2.3 基础代码准备
准备
Student班级管理的基础结构,包括Controller、Service、Mapper
StudentController
@Slf4j
@RestController
public class StudentController {
//注入Service层bean
@Autowired
private StudentService studentService;
}StudentService
public interface StudentService {
}StudentServiceImpl
@Service
public class StudentServiceImpl implements StudentService {
//注入Mapper层bean
@Autowired
private StudentMapper studentMapper;
}StudentMapper
@Mapper
public interface StudentMapper {
}4.2.4 SQL & Mapper
- 前面我们已经创建了实体类
Student
- 观察前端表格展示的数据列需要什么

- 这跟我们前面做班级的分页查询很相似
- 前面是需要展示
班主任的名称而且对应的Clazz数据库表中只有master_id - 这里是需要展示
班级的名称而且对应的Student数据库表中只有clazz_id
- 前面是需要展示
- 所以共同点就是都要做
多表查询
所以先做出基础的多表查询的
SQL代码:需要查询学生表中的所有信息,及根据
clazz_id作为班级表的id去查询clazzName班级的名称。
-- 基础多表查询代码
-- 需要按照 student 的 clazz_id 对应 clazz 表的 id 查出对应的班主任名称 clazzName
select s.* , c.name as clazzName
from student as s left join clazz as c on s.clazz_id = c.id- 查询结果如下:

- 根据这个基本的
SQL尝试着写一个Mapper接口
@Mapper
public interface StudentMapper {
//查询所有的学生信息及其班级名称
@Select("select s.* , c.name as clazzName from student as s left join clazz as c on s.clazz_id = c.id")
public List<Student> list();
}📌 注意:
注意,上述 SQL 语句中,给 班级名称 起了别名 clazzName ,是因为在接口文档中,
要求部门名称给前端返回的数据中,就必须叫 clazzName。 而这里我们需要将查询返回的每一条记录
都封装到 Clazz 对象中,那么就必须保证查询返回的字段名与属性名是一一对应的。
此时,我们就需要在 Student 中定义一个属性 ClazzName 用来封装班级名称。
刚刚建实体类的时候已经携带 , 具体如下高亮:
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student {
private Integer id; //ID
private String name; //姓名
private String no; //序号
private Integer gender; //性别 , 1: 男 , 2 : 女
private String phone; //手机号
private String idCard; //身份证号
private Integer isCollege; //是否来自于院校, 1: 是, 0: 否
private String address; //联系地址
private Integer degree; //最高学历, 1: 初中, 2: 高中 , 3: 大专 , 4: 本科 , 5: 硕士 , 6: 博士
private LocalDate graduationDate; //毕业时间
private Integer clazzId; //班级ID
private Short violationCount; //违纪次数
private Short violationScore; //违纪扣分
private LocalDateTime createTime; //创建时间
private LocalDateTime updateTime; //修改时间
private String clazzName;//班级名称
}- 代码编写完毕后,我们可以编写一个单元测试,对上述的程序进行测试:
@SpringBootTest
class TliasWebManagementApplicationTests {
@Autowired
private StudentMapper studentMapper;
@Test
public void testStudentList(){
List<Student> studentList = studentMapper.list();
studentList.forEach(System.out::println);
}
}- 运行单元测试后,我们看到控制台输出的数据:

可以看到,学生的信息,以及班级对应班主任的名称都查询出来了。
4.2.5 基础分页查询
先完成基础的分页查询,再在此基础上修改为
条件分页查询
4.2.5.1 思路
在
Controller层 :需要接收分页参数
page和pageSize,需要设置默认值, 使用
@RequestParam(defaultValue = "")log.info打印日志信息
然后调用
service层返回一个PageResult pageResult- 把这个
pageResult返回给前端
- 把这个
在
Service层 :- 接收参数
page和pageSize - 设置分页参数
PageHelper.startPage(page,pageSize); - 调用
Mapper层接口执行查询操作,获取一个返回的List studentList - 把
clazzList强转成Page类型 - 封装结果
return new PageResult(p.getTotal(), p.getResult());
- 接收参数
在
Mapper层 :- 只需要进行正常的列表查询即可,而且不需要考虑分页操作,就是一条正常的查询语句
4.2.5.2 代码实现
StudentController
//查询学生信息及其班主任名称
@GetMapping
public Result page(@RequestParam(defaultValue = "1") Integer page,
@RequestParam(defaultValue = "10") Integer pageSize){
log.info("查询学生信息,page:{} , pageSize:{}",page,pageSize);
PageResult<Student> pageResult = studentService.page(page,pageSize);
return Result.success(pageResult);
}StudentService
//查询学生信息及其班主任名称
PageResult<Student> page(Integer page, Integer pageSize);StudentServiceImpl
//查询学生信息及其班主任名称
@Override
public PageResult<Student> page(Integer page, Integer pageSize) {
//设置分页参数
PageHelper.startPage(page,pageSize);
//执行查询
List<Student> studentList = studentMapper.list();
//强转为 Page<Student> 类型
Page<Student> p = (Page<Student>) studentList;
//封装结果返回
return new PageResult<Student>(p.getTotal(),p.getResult());
}StudentMapper
//查询所有的学生信息及其班级名称
@Select("select s.* , c.name as clazzName from student as s left join clazz as c on s.clazz_id = c.id")
public List<Student> list();4.2.5.3 测试
Apifox测试:

- 前后端联调:

- 现在可以做一下
4.1.4 的前后端联调也就是 查询所有班级接口的前后端联调

可以发现也是没问题的 ...
4.3 新增学生信息接口
4.3.1 需求

- 在这个表单中我们先需要完成
页面回显的功能,把对应的学员信息回显到前端的这个表单上- 其中
所属班级我们已经在4.1完成了 - 点击保存按钮,根据用户填写的信息进行新增学生信息
- 其中
4.3.2 接口文档
参照接口文档
学员管理-->添加学员请求路径:
/students请求方式:
POST接口描述:该接口用于添加学员信息
请求参数格式:
application/json请求数据样例:
{
"name": "阿大",
"no": "2024010801",
"gender": 1,
"phone": "15909091235",
"idCard": "159090912351590909",
"isCollege": 1,
"address": "昌平回龙观",
"degree": 4,
"graduationDate": "2024-01-01",
"clazzId": 9
}4.3.3 功能开发
StudentController
//新增学生信息
@PostMapping
public Result save(@RequestBody Student student){
log.info("新增学生信息: {}",student);
studentService.save(student);
return Result.success();
}StudentService
//新增学生信息
void save(Student student);StudentServiceImpl
//新增学生信息
@Override
public void save(Student student) {
//补全基础信息
student.setCreateTime(LocalDateTime.now());
student.setUpdateTime(LocalDateTime.now());
//执行新增操作
studentMapper.insert(student);
}StudentMapper
//新增学生信息
@Insert("insert into student(name, no, gender, phone, id_card, is_college, address, degree, graduation_date, clazz_id, create_time, update_time)" +
"values (#{name},#{no},#{gender},#{phone},#{idCard},#{isCollege},#{address},#{degree},#{graduationDate},#{clazzId},#{createTime},#{updateTime})")
void insert(Student student);4.3.4 测试
Apifox测试

查看数据库有没有添加成功

前后端联调

填写信息后点击保存,添加成功

4.4 根据ID查询学生接口
4.4.1 需求

- 点击
编辑按钮的时候,我们需要页面回显该学生的信息
4.4.2 接口文档
参照接口文档
学员管理-->根据ID查询学员请求路径:
/students/{id}请求方式:
GET接口描述:该接口用于根据主键ID查询学员的信息
请求参数格式:路径参数
请求参数样例:
/students/8响应数据样例:
{
"code": 1,
"msg": "success",
"data": {
"id": 7,
"name": "Locos",
"no": "2023001010",
"gender": 1,
"phone": "13712345678",
"degree": 5,
"idCard": "110090110090110090",
"isCollege": 0,
"address": "回龙观东大街110号",
"graduationDate": "2020-07-01",
"violationCount": 0,
"violationScore": 0,
"clazzId": 2,
"createTime": "2023-06-04T18:27:27",
"updateTime": "2023-06-04T18:27:27"
}
}4.4.3 功能实现
StudentController
//根据ID查询学生
@GetMapping("/{id}")
public Result getInfo(@PathVariable Integer id){
log.info("根据 ID : {} 查询学生",id);
Student student = studentService.getInfo(id);
return Result.success(student);
}StudentService
//根据ID查询学生
Student getInfo(Integer id);StudentServiceImpl
//根据ID查询学生
@Override
public Student getInfo(Integer id) {
return studentMapper.getById(id);
}StudentMapper
//根据ID查询学生信息
@Select("select * from student where id=#{id}")
Student getById(Integer id);4.4.4 测试
Apifox测试

前后端联调

4.5 修改学生信息接口
4.5.1 需求

- 前面
4.4已经完成点击编辑按钮然后页面回显接着就是如果用户修改信息,点击保存需要修改学员信息。
4.5.2 接口文档
参照接口文档
学员管理-->修改学员请求路径:
/students请求方式:
PUT接口描述:该接口用于修改学员的数据信息
请求参数格式:
application/json请求参数样例:
{
"id": 7,
"name": "Locos",
"no": "2023001010",
"gender": 1,
"phone": "13712345678",
"degree": 5,
"idCard": "110090110090110090",
"isCollege": 0,
"address": "回龙观东大街110号",
"graduationDate": "2020-07-01",
"violationCount": 0,
"violationScore": 0,
"clazzId": 2
}4.5.3 功能开发
StudentController
//修改学生信息
@PutMapping
public Result update(@RequestBody Student student){
log.info("修改学生信息,{}",student);
studentService.update(student);
return Result.success();
}StudentService
//修改学生信息
void update(Student student);StudentServiceImpl
//修改学生信息
@Override
public void update(Student student) {
//更新修改时间
student.setUpdateTime(LocalDateTime.now());
//执行修改操作
studentMapper.updateById(student);
}StudentMapper
//修改学生信息
void updateById(Student student);StudentMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.mapper.StudentMapper">
<!--修改学生信息-->
<update id="updateById">
update student
<set>
<if test="name != null and name != ''">name = #{name},</if>
<if test="no != null and no != ''">no = #{no},</if>
<if test="gender != null">gender = #{gender},</if>
<if test="phone != null and phone != ''">phone = #{phone},</if>
<if test="degree != null">degree = #{degree},</if>
<if test="idCard != null and idCard != ''">id_card = #{idCard},</if>
<if test="isCollege != null">is_college = #{isCollege},</if>
<if test="address != null and address != ''">address = #{address},</if>
<if test="graduationDate != null">graduation_date = #{graduationDate},</if>
<if test="violationCount != null">violation_count = #{violationCount},</if>
<if test="violationScore != null">violation_score = #{violationScore},</if>
<if test="clazzId != null">clazz_id = #{clazzId},</if>
<if test="createTime != null">create_time = #{createTime},</if>
<if test="updateTime != null">update_time = #{updateTime},</if>
</set>
where id = #{id}
</update>
</mapper>4.5.4 测试
Apifox测试

查看数据库的数据修改

前后端联调

更改后点击保存按钮,修改成功

4.6 删除学生信息接口
4.6.1 需求

这有两个按钮的接口需要实现:一个是
批量删除, 一个是单行的删除。那么这跟我们前面的案例的思考方式是一样的:我们没必要为这个去设计两个接口
我们只需要设计一个
根据ID来批量删除的接口,删除一个是Ids这个数组长度为 1 的情况
4.6.2 接口文档
参照接口文档
学员管理-->删除学员请求路径:
/students/{ids}请求方式:
DELETE接口描述:该接口用于批量删除学员信息
请求参数格式:路径参数
请求参数样例:
/students/1,2,3
4.6.3 功能开发
StudentController
//批量删除学生信息
@DeleteMapping("/{ids}")
public Result delete(@PathVariable List<Integer> ids){
log.info("根据 id 列表 : {} 批量删除学生信息",ids);
studentService.delete(ids);
return Result.success();
}StudentService
//批量删除学生信息
void delete(List<Integer> ids);StudentServiceImpl
//批量删除学生信息
@Override
public void delete(List<Integer> ids) {
studentMapper.deleteByIds(ids);
}StudentMapper
//批量删除学生信息
void deleteByIds(List<Integer> ids);StudentMapper.xml
<!--批量删除学生信息-->
<delete id="deleteByIds">
delete from student where id in
<foreach collection="ids" item="id" open="(" close=")" separator=",">
#{id}
</foreach>
</delete>4.6.4 测试
Apifox测试
先新增一条测试数据用于删除 ... (测试数据在学生表的 id 是 21)


前后端联调


4.7 违纪处理接口
4.7.1 需求

- 违纪处理一次,需要将
违纪次数+1,违纪扣分+前端输入的分数。 - 根据后面的接口文档可以看到,我们接收到前端有两个参数:
id,score- 那么我们要做的事情其实首先是
根据ID查询学生这个在前面4.4实现过了 - 然后再拿着这个学生信息对
violation_count加一,violation_score+score - 注意还需要修改
update_time改为当前时间
- 那么我们要做的事情其实首先是
📌 注意:
这涉及到多次与数据库交互了,这时候我们很自然的就会思考到 要不要使用事务 ,
前面是一个查询的操作,没有对数据进行改变,后面是一个修改操作,改变了数据。
感觉没什么必要使用事务啊 ... 。但是如果说在比较多用户的场景下,会不会说有一个用户读了信息,
紧接着另一个用户又读了信息。那么第一个用户读的信息就是基于它读的那个信息进行修改,
那要是第二个用户修改得比较快完成了呢?那么第一个修改了再覆盖数据,这样岂不是第二个用户的操作作废了...
所以为了养成好的习惯和不必要的风险 , 我们还是把事务加上去
4.7.2 接口文档
参照接口文档
学员管理-->违纪处理请求路径:
/students/violation/{id}/{score}请求方式:
PUT接口描述:该接口用于修改学员的数据信息
请求参数格式:路径参数
请求参数样例:
/students/violation/3/5
4.7.3 功能开发
StudentController
//违纪处理
@PutMapping("/violation/{id}/{score}")
public Result deal_violation(@PathVariable Integer id,@PathVariable Integer score){
log.info("违纪学生 id : {} , 扣除分数 : {}",id,score);
studentService.deal_violation(id,score);
return Result.success();
}StudentService
//违纪处理
void deal_violation(Integer id, Integer score);StudentServiceImpl
//违纪处理
@Transactional(rollbackFor = Exception.class)
@Override
public void deal_violation(Integer id, Integer score) {
//根据id查询学生
Student student = getInfo(id);
//违纪次数加一
student.setViolationCount((short) (student.getViolationCount() + 1));
//违纪分数 + score
student.setViolationScore((short) (student.getViolationScore() + score));
//执行修改
//更新修改时间涵盖在下面的修改信息接口
studentMapper.updateById(student);
}StudentMapper用到的getById()和updateById()都是前面有的
4.7.4 测试
Apifox测试

调用一下查询接口看看有没有改成功

一开始都是 0 的 , 现在次数+1,分数+5 , 没问题
前后端联调


- 到此,关于学生管理的增删改查功能,我们就已经全部实现了。
五、学员信息统计
5.1 班级人数统计接口
5.1.1 需求

- 如上面左图,统计出每一个班级的人数是多少, 主要注意接口文档以什么样的形式返回给前端数据
5.1.2 接口文档
参照接口文档
数据统计-->班级人数统计请求路径:
/report/studentCountData请求方式:
GET接口描述:统计每一个班级的人数
请求参数:无
响应数据样例:
{
"code": 1,
"msg": "success",
"data": {
"clazzList": ["Java就业100期","Java就业101期","Java就业102期","Java就业103期","Java就业104期"],
"dataList": [77,82,70,80,90]
}
}5.1.3 功能开发
reportController
//班级人数统计
@GetMapping("/studentCountData")
public Result countStudent(){
log.info("班级人数统计...");
ClazzOption clazzOption = reportService.countStudent();
return Result.success(clazzOption);
}reportService
//班级人数统计
ClazzOption countStudent();reportServiceImpl
//班级人数统计
@Override
public ClazzOption countStudent() {
//调用mapper接口,获取统计数据: map:clazz=JavaEE就业165期,num=2
List<Map<String,Object>> list = studentMapper.countStudentClazzData();
//构建职位列表
List<Object> clazz = list.stream().map(dataMap -> dataMap.get("clazz")).toList();
//构建数据列表
List<Object> num = list.stream().map(dataMap -> dataMap.get("num")).toList();
//封装返回结果
return new ClazzOption(clazz,num);
}StudentMapper
//班级人数统计
@Select("select c.name as clazz,count(*) as num from student s left join clazz c on c.id = s.clazz_id group by clazz_id order by num")
List<Map<String, Object>> countStudentClazzData();5.1.4 测试
Apifox测试

前后端联调

5.2 学员学历信息统计接口
5.2.1 需求

- 如上面右图,统计出每一种学历的人数是多少, 主要注意接口文档以什么样的形式返回给前端数据
5.2.2 接口文档
参照接口文档
数据统计-->学员学历统计请求路径:
/report/studentDegreeData请求方式:
GET接口描述:统计学员的学历信息
请求参数:无
响应数据样例:
{
"code": 1,
"msg": "success",
"data": [
{
"name": "初中",
"value": 5
},
{
"name": "高中",
"value": 6
},
{
"name": "大专",
"value": 126
},
{
"name": "本科",
"value": 182
},
{
"name": "硕士",
"value": 6
}
]
}5.2.3 功能开发
reportController
//统计学历信息
@GetMapping("/studentDegreeData")
public Result countStudentDegree(){
log.info("学历信息统计...");
List<Map<String,Object>> list = reportService.countStudentDegree();
return Result.success(list);
}reportService
//统计学历信息
List<Map<String, Object>> countStudentDegree();reportServiceImpl
//统计学历信息
@Override
public List<Map<String, Object>> countStudentDegree() {
//调用mapper接口,获取统计数据
return studentMapper.countStudentDegreeData();
}StudentMapper
//统计学历信息
@MapKey("name")
List<Map<String, Object>> countStudentDegreeData();StudentMapper.xml
<!--统计学历信息-->
<select id="countStudentDegreeData" 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 '博士' end) as name
,count(*) as value
from student group by degree
</select>5.2.4 测试
Apifox测试

前后端联调

六、删除部门功能完善
6.1 需求
删除部门时:如果部门下有员工,则不允许删除该部门,
并给前端提示错误信息:
对不起,当前部门下有员工,不能直接删除!
📌 思路:
当删除部门的时候,在员工表查询一下所有员工的 dept_id 然后按这个进行 group by
返回的就是有员工的部门 id , 那么如果要删除的部门 id 包含在这个列表里面,那么就可以
抛出一个错误,然后用全局异常处理器处理...
6.2 功能开发
EmpMapper
//查找员工一共在哪些部门(用于如果部门下有员工则不能删除该部门)
@Select("select dept_id from emp group by dept_id")
List<Integer> getAllDeptWithEmp();DeptServiceImpl
/**
* 根据id删除部门
*/
@Override
public void deleteById(Integer id) {
List<Integer> allDeptWithEmp = empMapper.getAllDeptWithEmp();
if (allDeptWithEmp.contains(id)){
//那么就说明这个部门有员工,不能删除
throw new IllegalStateException("对不起,当前部门下有员工,不能直接删除!");
}
deptMapper.deleteById(id);
}GlobalExceptionHandler
//处理删除部门异常
@ExceptionHandler
public Result handleIllegalStateException(IllegalStateException e){
log.info("程序出错了:",e);
return Result.error(e.getMessage());
}6.3 测试
- 先添加一个用于测试的员工

Apifox测试

前后端联调

