Web后端实战(5)

Tlias智能学习辅助系统

数据准备

在数据库中,创建学生表 clazz , student,SQL如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
create table clazz(
    id   int unsigned primary key auto_increment comment 'ID,主键',
    name  varchar(30) not null unique  comment '班级名称',
    room  varchar(20) comment '班级教室',
    begin_date date not null comment '开课时间',
    end_date date not null comment '结课时间',
    master_id int unsigned null comment '班主任ID, 关联员工表ID',
    subject tinyint unsigned not null comment '学科, 1:java, 2:前端, 3:大数据, 4:Python, 5:Go, 6: 嵌入式',
    create_time datetime  comment '创建时间',
    update_time datetime  comment '修改时间'
)comment '班级表';

INSERT INTO clazz VALUES (1,'JavaEE就业163期','212','2024-04-30','2024-06-29',10,1,'2024-06-01 17:08:23','2024-06-01 17:39:58'),
    (2,'前端就业90期','210','2024-07-10','2024-01-20',3,2,'2024-06-01 17:45:12','2024-06-01 17:45:12'),
    (3,'JavaEE就业165期','108','2024-06-15','2024-12-25',6,1,'2024-06-01 17:45:40','2024-06-01 17:45:40'),
    (4,'JavaEE就业166期','105','2024-07-20','2024-02-20',20,1,'2024-06-01 17:46:10','2024-06-01 17:46:10'),
    (5,'大数据就业58期','209','2024-08-01','2024-02-15',7,3,'2024-06-01 17:51:21','2024-06-01 17:51:21'),
    (6,'JavaEE就业167期','325','2024-11-20','2024-05-10',36,1,'2024-11-15 11:35:46','2024-12-13 14:31:24');


create table student(
  id int unsigned primary key auto_increment comment 'ID,主键',
  name varchar(10)  not null comment '姓名',
  no char(10)  not null unique comment '学号',
  gender tinyint unsigned  not null comment '性别, 1: 男, 2: 女',
  phone  varchar(11)  not null unique comment '手机号',
  id_card  char(18)  not null unique comment '身份证号',
  is_college tinyint unsigned  not null comment '是否来自于院校, 1:是, 0:否',
  address  varchar(100)  comment '联系地址',
  degree  tinyint unsigned  comment '最高学历, 1:初中, 2:高中, 3:大专, 4:本科, 5:硕士, 6:博士',
  graduation_date date comment '毕业时间',
  clazz_id  int unsigned not null comment '班级ID, 关联班级表ID',
  violation_count tinyint unsigned default '0' not null comment '违纪次数',
  violation_score tinyint unsigned default '0' not null comment '违纪扣分',
  create_time  datetime  comment '创建时间',
  update_time  datetime  comment '修改时间'
) comment '学员表';


INSERT INTO student VALUES (1,'段誉','2022000001',1,'18800000001','110120000300200001',1,'北京市昌平区建材城西路1号',1,'2021-07-01',2,0,0,'2024-11-14 21:22:19','2024-11-15 16:20:59'),
    (2,'萧峰','2022000002',1,'18800210003','110120000300200002',1,'北京市昌平区建材城西路2号',2,'2022-07-01',1,0,0,'2024-11-14 21:22:19','2024-11-14 21:22:19'),
    (3,'虚竹','2022000003',1,'18800013001','110120000300200003',1,'北京市昌平区建材城西路3号',2,'2024-07-01',1,0,0,'2024-11-14 21:22:19','2024-11-14 21:22:19'),
    (4,'萧远山','2022000004',1,'18800003211','110120000300200004',1,'北京市昌平区建材城西路4号',3,'2024-07-01',1,0,0,'2024-11-14 21:22:19','2024-11-14 21:22:19'),
    (5,'阿朱','2022000005',2,'18800160002','110120000300200005',1,'北京市昌平区建材城西路5号',4,'2020-07-01',1,0,0,'2024-11-14 21:22:19','2024-11-14 21:22:19'),
    (6,'阿紫','2022000006',2,'18800000034','110120000300200006',1,'北京市昌平区建材城西路6号',4,'2021-07-01',2,0,0,'2024-11-14 21:22:19','2024-11-14 21:22:19'),
    (7,'游坦之','2022000007',1,'18800000067','110120000300200007',1,'北京市昌平区建材城西路7号',4,'2022-07-01',2,0,0,'2024-11-14 21:22:19','2024-11-14 21:22:19'),
    (8,'康敏','2022000008',2,'18800000077','110120000300200008',1,'北京市昌平区建材城西路8号',5,'2024-07-01',2,0,0,'2024-11-14 21:22:19','2024-11-14 21:22:19'),
    (9,'徐长老','2022000009',1,'18800000341','110120000300200009',1,'北京市昌平区建材城西路9号',3,'2024-07-01',2,0,0,'2024-11-14 21:22:19','2024-11-14 21:22:19'),
    (10,'云中鹤','2022000010',1,'18800006571','110120000300200010',1,'北京市昌平区建材城西路10号',2,'2020-07-01',2,0,0,'2024-11-14 21:22:19','2024-11-14 21:22:19'),
    (11,'钟万仇','2022000011',1,'18800000391','110120000300200011',1,'北京市昌平区建材城西路11号',4,'2021-07-01',1,0,0,'2024-11-14 21:22:19','2024-11-15 16:21:24'),
    (12,'崔百泉','2022000012',1,'18800000781','110120000300200018',1,'北京市昌平区建材城西路12号',4,'2022-07-05',3,6,17,'2024-11-14 21:22:19','2024-12-13 14:33:58'),
    (13,'耶律洪基','2022000013',1,'18800008901','110120000300200013',1,'北京市昌平区建材城西路13号',4,'2024-07-01',2,0,0,'2024-11-14 21:22:19','2024-11-15 16:21:21'),
    (14,'天山童姥','2022000014',2,'18800009201','110120000300200014',1,'北京市昌平区建材城西路14号',4,'2024-07-01',1,0,0,'2024-11-14 21:22:19','2024-11-15 16:21:17'),
    (15,'刘竹庄','2022000015',1,'18800009401','110120000300200015',1,'北京市昌平区建材城西路15号',3,'2020-07-01',4,0,0,'2024-11-14 21:22:19','2024-11-14 21:22:19'),
    (16,'李春来','2022000016',1,'18800008501','110120000300200016',1,'北京市昌平区建材城西路16号',4,'2021-07-01',4,0,0,'2024-11-14 21:22:19','2024-11-14 21:22:19'),
    (17,'王语嫣','2022000017',2,'18800007601','110120000300200017',1,'北京市昌平区建材城西路17号',2,'2022-07-01',4,0,0,'2024-11-14 21:22:19','2024-11-14 21:22:19'),
    (18,'郑成功','2024001101',1,'13309092345','110110110110110110',0,'北京市昌平区回龙观街道88号',5,'2021-07-01',3,2,7,'2024-11-15 16:26:18','2024-11-15 16:40:10');

表结构关系说明:

实体类Clazz:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Clazz {
    private Integer id; //ID
    private String name; //班级名称
    private String room; //班级教室
    private LocalDate beginDate; //开课时间
    private LocalDate endDate; //结课时间
    private Integer masterId; //班主任
    private Integer subject; //学科
    private LocalDateTime createTime; //创建时间
    private LocalDateTime updateTime; //修改时间

    private String masterName; //班主任姓名
    private String status; //班级状态 - 未开班 , 在读 , 已结课
}

实体类Student:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
@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;//班级名称
}

需求:班级管理

需要开发如下几个接口,且开发的时候建议按照如下顺序开发 :

条件分页查询接口

地址:接口文档 - 飞书云文档

参照接口文档 班级管理 -> 班级列表查询

注意:班级状态,显示为:未开班、已结课、在读中 这三种。

如果:

  • 当前时间 > 结课时间:状态未 已结课
  • 当前时间 < 开课时间:状态未 未开班
  • 否则,就是 在读中

1).准备Clazz表对应的基础结构,包括Controller、Server、Mapper:

ClazzController:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@Slf4j
@RestController
@RequestMapping("/clazzs")
public class ClazzController {

    @Autowired
    private ClazzService clazzService;

    /**
     * 查询所有班级数据
     */
        @GetMapping
        public Result page(@RequestParam(defaultValue = "1") Integer page ,
                           @RequestParam(defaultValue = "10") Integer pageSize,
                           String name,
                           @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate begin,
                           @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate end) {

            log.info("分页查询:{},{},{},{},{}",name,begin,end,page,pageSize);
            return Result.success(clazzService.page(page,pageSize,name,begin,end));


    }

}

ClazzService

1
2
3
4
5
6
7
8
9
@Service
public interface ClazzService {
    /*
     * 查询所有班级
     * */
     PageResult<Clazz> page(Integer page, Integer pageSize, String name, LocalDate begin,LocalDate end);


}

ClazzServiceImpl

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
@Service
public class ClazzServiceImpl implements ClazzService {

    @Autowired
    private ClazzMapper ClazzMapper;


    public PageResult<Clazz> page(Integer page, Integer pageSize, String name, LocalDate begin, LocalDate end) {
        PageHelper.startPage(page,pageSize);
        List<Clazz> clazzList = ClazzMapper.list(name,begin,end);
        Page<Clazz> p = (Page<Clazz>) clazzList;
        return new PageResult<>(p.getTotal(),p.getResult());
    }
}

ClazzMapper

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
@Mapper
public interface ClazzMapper {

    /*
    * 查询所有班级
    * */
    @Select("select * from clazz;")
    List<Clazz> findall();

    List<Clazz> list(String name, LocalDate begin, LocalDate end);
}

将对应的查询语句写在映射文件中

ClazzMapper.xml

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
<select id="page" resultType="com.itheima.pojo.Clazz">
        SELECT
        c.id,
        c.name,
        c.room,
        c.begin_date,
        c.end_date,
        c.create_time,
        c.update_time,
        -- 重点:从 emp 表中获取 name,并起别名为 masterName
        e.name AS masterName,
        -- 之前的状态计算逻辑也可以保留在这里
        CASE
            WHEN CURDATE() &gt; c.end_date THEN '已结课'
            WHEN CURDATE() &lt; c.begin_date THEN '未开班'
            ELSE '在读中'
        END AS status
        FROM
        clazz c
        LEFT JOIN
        emp e ON c.master_id = e.id
    </select>

XML 常用的转义字符:

&lt;:<

&gt;::>

&amp;::&

&quote;:"

&apos;:'

功能测试

前后端联调测试

(此处页面展示不同是因为前面代码写错了,没有将班主任和状态展示出来,这是修改以后的图,下面几张是修改前的图)

查询所有员工接口

那其实,对于培训机构来说,班主任就是这个企业的员工。所以,班主任下拉列表中展示的就是所有的员工数据。

地址:接口文档 - 飞书云文档

参照接口文档 员工管理 -> 查询全部员工

EmpController

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
/**
 * 查询所有员工
 */
@GetMapping("/list")
public Result list() {
    // 调用业务逻辑层(Service)的 findall 方法,获取部门列表数据
    List<Emp> empList = empService.findAll();
    // 将查询到的 deptList 封装在 Result 对象中返回,通常包含状态码、消息和数据。
    return Result.success(empList);
}

EmpService

1
List<Emp> findAll();

EmpServiceImpl

1
2
3
4
@Override
public List<Emp> findAll() {
    return empMapper.findAll();
}

EmpMapper

1
2
//查询所有员工
List<Emp> findAll();

EmpMapper.xml

1
2
3
<select id="findAll" resultType="com.itheima.pojo.Emp">
    select * from emp;
</select>

功能测试

前后端联调测试

新增班级信息接口

参照接口文档 班级管理 -> 添加班级

功能测试

前后端联调测试

根据ID查询班级接口

参照接口文档 班级管理 -> 根据ID查询

ClazzController:

1
2
3
4
5
6
7
8
9
/**
 * 查询回显
 */
@GetMapping("/{id}")
public Result getInfo(@PathVariable Integer id){
    log.info("根据id查询员工的详细信息");
    Clazz clazz  = clazzService.getInfo(id);
    return Result.success(clazz);
}

ClazzService:

1
2
3
4
/*
 * 根据ID查询班级信息
 * */
Clazz getInfo(Integer id);

ClazzServiceImpl:

1
2
3
4
@Override
public Clazz getInfo(Integer id) {
    return clazzMapper.getById(id);
}

ClazzMapper:

1
2
3
4
/*
* 根据ID查询班级信息
* */
Clazz getById(Integer id);

ClazzMapper.xml:

1
2
3
<select id="getById" resultType="com.itheima.pojo.Clazz">
    select * from clazz where id = #{id}
</select>

功能测试

前后端联调测试

选择与我们刚刚查询id相同的班级信息,点击编辑,发现班级的信息都展示出来了

修改班级信息接口

参照接口文档 班级管理 -> 修改班级

ClazzController:

1
2
3
4
5
6
7
8
9
/**
 * 更新班级信息
 */
@PutMapping
public Result update(@RequestBody Clazz clazz){
    log.info("修改部门信息, {}", clazz);
    clazzService.update(clazz);
    return Result.success();
}

ClazzService:

1
2
3
4
/*
* 修改班级信息
* */
void update(Clazz clazz);

ClazzServiceImpl:

1
2
3
4
5
6
7
@Override
public void update(Clazz clazz) {
    //补全基础属性 - updateTime
    clazz.setUpdateTime(LocalDateTime.now());
    //调用Mapper接口方法更新班级
    clazzMapper.update(clazz);
}

ClazzMapper:

1
2
3
4
5
6
/*
* 修改班级信息
* */
@Update("update clazz set name=#{name},room=#{room},begin_date=#{beginDate},end_date=#{endDate},master_id=#{masterId},subject=#{subject}" +
        " where id = #{id}")
void update(Clazz clazz);

功能测试

前后端联调测试

修改前:

修改后:

删除班级信息接口

参照接口文档 班级管理 -> 删除班级

ClazzController:

1
2
3
4
5
6
7
8
9
/**
 * 删除班级 - List
 */
@DeleteMapping("/{id}")
public Result delete(@PathVariable Integer id){
    log.info("删除班级: id={} ", id);
    clazzService.delete(id);
    return Result.success("删除成功");
}

ClazzService:

1
2
3
4
/*
* 删除班级
* */
void delete(Integer id);

ClazzServiceImpl:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
@Transactional(rollbackFor = {Exception.class})
@Override
public void delete(Integer id) {
    //1. 检查班级下是否有学生
    int count = clazzMapper.countStudentsByClazzId(id);
    if (count > 0) {
        throw new ClassHasStudentsException();
    }

    //2. 根据ID批量删除班级基本信息
    clazzMapper.deleteById(id);
}

ClazzMapper:

1
2
3
4
5
6
7
8
9
/*
* 删除班级
* */
void deleteById(@Param("id") Integer id);

/*
* 根据班级ID统计学生数量
* */
int countStudentsByClazzId(@Param("clazzId") Integer clazzId);

ClazzMapper.xml:

1
2
3
<delete id="deleteById">
    DELETE FROM clazz WHERE id = #{id}
</delete>

自定义异常处理器:ClassHasStudentsException

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
package com.itheima.exception;

/**
 * 班级下有学生时抛出的异常
 */
public class ClassHasStudentsException extends RuntimeException {

    public ClassHasStudentsException() {
        super("对不起,该班级下有学生,不能直接删除");
    }

    public ClassHasStudentsException(String message) {
        super(message);
    }
}

全局异常处理器:GlobalExceptionHandler

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/*
* 全局异常处理器
* */
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

    //处理异常
    @ExceptionHandler
    public Result ex(Exception e){//方法形参中指定能够处理的异常类型
        log.error("程序出错啦~",e);
        //e.printStackTrace();//打印堆栈中的异常信息
        //捕获到异常之后,响应一个标准的Result
        return Result.error("对不起,操作失败,请联系管理员");
    }

    @ExceptionHandler
    //如果是DuplicateKeyException,那么这个处理器就会生效,如果是别的上面的就会生效
    public Result handleDuplicateKeyException(DuplicateKeyException e){
        log.error("程序出错啦~",e);
        // 获取异常的具体描述信息,通常包含 "Duplicate entry 'xxx' for key 'xxx'"
        String message = e.getMessage();
        // 定位 "Duplicate entry" 关键字的位置,作为截取错误信息的起点
        int i = message.indexOf("Duplicate entry");
        // 从关键字位置开始截取字符串,获取包含具体重复值的片段
        String errMsg = message.substring(i);
        // 按照空格将截取的字符串分割成数组
        String[] arr = errMsg.split(" ");
        // 数组下标为2的元素通常是具体的重复值(如 'admin'),拼接提示语后返回
        // 注意:这里假设了异常信息的格式是固定的,且重复值在数组的第三个位置
        return Result.error(arr[2] + "已存在");
    }

    @ExceptionHandler
    public Result handleClassHasStudentsException(ClassHasStudentsException e){
        return Result.error(e.getMessage());
    }
}

功能测试

前后端联调测试

删除无学生班级:

删除有学生班级:

需求:学员管理

由于该部分为学习了后面知识后回来补充的,而后面有些功能会影响到这个部分的测试(如测试时无Token令牌会导致报错,测试起来比较麻烦),所以该部分就不进行前后端联调测试了,仅为完成功能实现

需要开发如下几个接口,建议按照如下顺序开发接口:

查询所有班级接口

地址:接口文档 - 飞书云文档

参照接口文档 班级管理 -> 查询所有班级

在新增学员的时候,要展示出所有的班级信息。

ClazzController:新增 @GetMapping("/list") 端点,返回所有班级

1
2
3
4
5
6
7
8
9
/**
 * 查询所有班级数据
 */
@GetMapping("/list")
public Result list() {
    log.info("查询所有班级数据");
    List<Clazz> clazzList = clazzService.findAll();
    return Result.success(clazzList);
}

ClazzService:新增 List findAll() 接口方法

1
2
3
4
/*
 * 查询所有班级
 * */
List<Clazz> findAll();

ClazzServiceImpl:

1
2
3
4
@Override
public List<Clazz> findAll() {
    return clazzMapper.findall();
}

由于ClazzMapper.findall() 已经存在(@Select(“select * from clazz;")),所以 Mapper 层无需改动。

条件分页查询接口

接口描述:

在pojo层创建StudentQueryParam.java :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
package com.itheima.pojo;

import lombok.Data;

@Data
public class StudentQueryParam {
    private Integer page = 1; //页码
    private Integer pageSize = 10; //每页展示记录数
    private String name; //姓名
    private Integer degree; //最高学历
    private Integer clazzId; //班级ID
}

在mapper层创建StudentMapper.java

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
package com.itheima.mapper;

import com.itheima.pojo.Student;
import com.itheima.pojo.StudentQueryParam;
import org.apache.ibatis.annotations.Mapper;

import java.util.List;

@Mapper
public interface StudentMapper {

    /*
    * 条件分页查询学员列表
    * */
    List<Student> list(StudentQueryParam studentQueryParam);
}

在resource目录下的mapper层创建StudentMapper.xml ,基于动态SQL,通过left join clazz 表获取班级名称

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.mapper.StudentMapper">

    <select id="list" resultType="com.itheima.pojo.Student">
        select s.*, c.name clazzName
        from student s left join clazz c on s.clazz_id = c.id
        <where>
            <if test="name != null and name != ''">
                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>

</mapper>

在service层创建StudentService.java :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
package com.itheima.service;

import com.itheima.pojo.PageResult;
import com.itheima.pojo.Student;
import com.itheima.pojo.StudentQueryParam;

public interface StudentService {

    /*
    * 学员分页查询
    * */
    PageResult<Student> page(StudentQueryParam studentQueryParam);
}

在service层创建StudentServiceImpl.java :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package com.itheima.service.impl;

import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.itheima.mapper.StudentMapper;
import com.itheima.pojo.PageResult;
import com.itheima.pojo.Student;
import com.itheima.pojo.StudentQueryParam;
import com.itheima.service.StudentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class StudentServiceImpl implements StudentService {

    @Autowired
    private StudentMapper studentMapper;

    @Override
    public PageResult<Student> page(StudentQueryParam studentQueryParam) {
        //1. 设置分页参数
        PageHelper.startPage(studentQueryParam.getPage(), studentQueryParam.getPageSize());
        //2. 执行查询
        List<Student> studentList = studentMapper.list(studentQueryParam);
        //3. 封装分页结果
        Page<Student> p = (Page<Student>) studentList;
        return new PageResult<>(p.getTotal(), p.getResult());
    }
}

在controller层创建StudentController.java

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package com.itheima.controller;

import com.itheima.pojo.PageResult;
import com.itheima.pojo.Result;
import com.itheima.pojo.Student;
import com.itheima.pojo.StudentQueryParam;
import com.itheima.service.StudentService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * 学员管理
 */
@Slf4j
@RequestMapping("/students")
@RestController
public class StudentController {

    @Autowired
    private StudentService studentService;

    /*
    * 条件分页查询学员列表
    * */
    @GetMapping
    public Result page(StudentQueryParam studentQueryParam) {
        log.info("学员查询请求参数:{}", studentQueryParam);
        PageResult<Student> pageResult = studentService.page(studentQueryParam);
        return Result.success(pageResult);
    }
}

新增学生信息接口

接口文档:

StudentController:

1
2
3
4
5
6
7
8
9
/*
* 新增学员
* */
@PostMapping
public Result save(@RequestBody Student student) {
    log.info("新增学员: {}", student);
    studentService.save(student);
    return Result.success();
}

StudentService:

1
2
3
4
/*
* 新增学员
* */
void save(Student student);

StudentServiceImpl:

1
2
3
4
5
6
7
8
@Override
public void save(Student student) {
    //补全基础属性
    student.setCreateTime(LocalDateTime.now());
    student.setUpdateTime(LocalDateTime.now());
    //调用Mapper接口方法插入数据
    studentMapper.insert(student);
}

StudentMapper:

1
2
3
4
5
6
7
*
* 新增学员
* */
@Options(useGeneratedKeys = true, keyProperty = "id")
@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);

根据ID查询学生接口

接口文档:

StudentController:

1
2
3
4
5
6
7
8
9
/*
* 根据ID查询学员
* */
@GetMapping("/{id}")
public Result getInfo(@PathVariable Integer id) {
    log.info("根据id查询学员信息: {}", id);
    Student student = studentService.getInfo(id);
    return Result.success(student);
}

StudentService:

1
2
3
4
/*
* 根据ID查询学员
* */
Student getInfo(Integer id);

StudentServiceImpl:

1
2
3
4
@Override
public Student getInfo(Integer id) {
    return studentMapper.getById(id);
}

StudentMapper.java:

1
2
3
4
/*
* 根据ID查询学员
* */
Student getById(Integer id);

StudentMapper.xml:

1
2
3
4
5
<select id="getById" resultType="com.itheima.pojo.Student">
    select s.*, c.name clazzName
    from student s left join clazz c on s.clazz_id = c.id
    where s.id = #{id}
</select>

修改学生信息接口

接口文档:

StudentController:

1
2
3
4
5
6
7
8
9
/*
* 修改学员
* */
@PutMapping
public Result update(@RequestBody Student student) {
    log.info("修改学员信息: {}", student);
    studentService.update(student);
    return Result.success();
}

StudentService:

1
2
3
4
/*
* 修改学员
* */
void update(Student student);

StudentServiceImpl:

1
2
3
4
5
6
7
@Override
public void update(Student student) {
    //补全基础属性
    student.setUpdateTime(LocalDateTime.now());
    //调用Mapper接口方法更新数据
    studentMapper.update(student);
}

StudentMapper:

1
2
3
4
5
6
/*
* 修改学员
* */
@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}, update_time=#{updateTime} where id = #{id}")
void update(Student student);

修改前:

修改后:

删除学生信息接口

接口文档:

StudentController:

1
2
3
4
5
6
7
8
9
/*
* 删除学员
* */
@DeleteMapping("/{ids}")
public Result delete(@PathVariable Integer ids) {
    log.info("删除学员: id={}", ids);
    studentService.delete(ids);
    return Result.success();
}

StudentService:

1
2
3
4
*
* 删除学员
* */
void delete(Integer id);

StudentServiceImpl:

1
2
3
4
@Override
public void delete(Integer id) {
    studentMapper.deleteById(id);
}

StudentMapper:

1
2
3
4
5
*
* 删除学员
* */
@Delete("delete from student where id = #{id}")
void deleteById(Integer id);

删除前:

删除后:

违纪处理接口

接口文档:

违纪处理一次,需要将违纪次数+1,违纪扣分+前端输入的分数。

StudentController:

1
2
3
4
5
6
7
8
9
/*
* 违纪处理
* */
@PutMapping("/violation/{id}/{score}")
public Result violation(@PathVariable Integer id, @PathVariable Integer score) {
    log.info("违纪处理: id={}, score={}", id, score);
    studentService.violation(id, score);
    return Result.success();
}

StudentService:

1
2
3
4
*
* 违纪处理
* */
void violation(Integer id, Integer score);

StudentServiceImpl:

1
2
3
4
@Override
public void violation(Integer id, Integer score) {
    studentMapper.updateViolation(id, score, LocalDateTime.now());
}

StudentMapper:

1
2
3
4
5
/*
* 违纪处理
* */
@Update("update student set violation_count = violation_count + 1, violation_score = violation_score + #{score}, update_time = #{updateTime} where id = #{id}")
void updateViolation(@Param("id") Integer id, @Param("score") Integer score, @Param("updateTime") LocalDateTime updateTime);

再添加一次违纪记录,分值为1分

到此,关于学生管理的增删改查功能,我们就已经全部实现了。

需求:学员信息统计

需要开发如下几个接口:

  • 班级人数统计接口开发:参照接口文档 数据统计 -> 班级人数统计
  • 学员学历信息统计接口开发:参照接口文档 数据统计 -> 学员学历统计

班级人数统计接口:

ClazzOption:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
package com.itheima.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.List;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class ClazzOption {
    private List clazzList; // 班级列表
    private List dataList;  // 数据列表
}

ReportController:

1
2
3
4
5
6
7
8
9
 /**
     * 统计班级人数
     */
    @GetMapping("/studentCountData")
    public Result getStudentCountData(){
        log.info("统计班级人数");
        ClazzOption clazzOption = reportService.getStudentCountData();
        return Result.success(clazzOption);
    }

ReportService:

1
2
3
4
/*
* 统计班级人数
* */
List<Map> getStudentCountData();

ReportServiceImpl:

1
2
3
4
5
6
7
8
9
@Override
    public ClazzOption getStudentCountData() {
        //1.调用mapper接口,获取统计数据
        List<Map> list = studentMapper.countStudentByClazz(); // map:name 班级名称,value = 班级人数
        //2.组装结果并返回
        List<Object> clazzList = list.stream().map(dataMap -> dataMap.get("name")).toList();
        List<Object> dataList = list.stream().map(dataMap -> dataMap.get("value")).toList();
        return new ClazzOption(clazzList, dataList);
    }

StudentMapper.java:

1
2
3
4
/*
* 统计班级人数
* */
List<Map> countStudentByClazz();

StudentMapper.xml:

1
2
3
4
5
   <select id="countStudentByClazz" resultType="java.util.Map">
        select c.name as name, count(s.id) as value
        from clazz c left join student s on c.id = s.clazz_id
        group by c.id, c.name
    </select>

结果与学历统计一起放在下面了

学员学历信息统计接口:

ReportController:

1
2
3
4
5
6
7
8
9
/**
 * 统计学历分布
 */
@GetMapping("/studentDegreeData")
public Result getStudentDegreeData(){
    log.info("统计学历分布");
    List<Map> degreeList = reportService.getStudentDegreeData();
    return Result.success(degreeList);
}

ReportService:

1
2
3
4
/*
* 统计学历分布
* */
List<Map> getStudentDegreeData();

ReportServiceImpl:

1
2
3
4
@Override
public List<Map> getStudentDegreeData() {
    return studentMapper.countStudentByDegree();
}

StudentMapper.java:

1
2
3
4
/*
* 统计学历分布
* */
List<Map> countStudentByDegree();

StudentMapper.xml:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<select id="countStudentByDegree" resultType="java.util.Map">
    select
        case when degree = 1 then '初中'
             when degree = 2 then '高中'
             when degree = 3 then '大专'
             when degree = 4 then '本科'
             when degree = 5 then '硕士'
             when degree = 6 then '博士'
        end as name,
        count(*) as value
    from student group by degree order by degree
</select>

需求:功能完善

  • 删除部门时:如果部门下有员工,则不允许删除该部门,并给前端提示错误信息:对不起,当前部门下有员工,不能直接删除!

DeptHasEmployeesException.java:新建,RuntimeException,提示消息:“对不起,当前部门下有员工,不能直接删除!”

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
package com.itheima.exception;

/**
 * 部门下有员工时抛出的异常
 */
public class DeptHasEmployeesException extends RuntimeException {

    public DeptHasEmployeesException() {
        super("对不起,当前部门下有员工,不能直接删除!");
    }

    public DeptHasEmployeesException(String message) {
        super(message);
    }
}

GlobalExceptionHandler.java:新增 handleDeptHasEmployeesException() 处理器

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
package com.itheima.exception;

/**
 * 班级下有学生时抛出的异常
 */
public class ClassHasStudentsException extends RuntimeException {

    public ClassHasStudentsException() {
        super("对不起,该班级下有学生,不能直接删除");
    }

    public ClassHasStudentsException(String message) {
        super(message);
    }
}

DeptMapper.java:新增 countEmployeesByDeptId(Integer deptId)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
/*
* 根据部门ID统计员工数量
* */
@Select("select count(*) from emp where dept_id = #{deptId}")
int countEmployeesByDeptId(Integer deptId);

/*
*删除部门
* */
@Delete("delete from dept where id = #{id}")
void deleteById(Integer id);

DeptServiceImpl.java:deleteById() 中新增检查逻辑

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
@Override
public void deleteById(Integer id) {
    //1. 检查部门下是否有员工
    int count = deptMapper.countEmployeesByDeptId(id);
    if (count > 0) {
        throw new DeptHasEmployeesException();
    }
    //2. 根据ID删除部门
    deptMapper.deleteById(id);
}

本站于2026年3月31日建立
使用 Hugo 构建
主题 StackJimmy 设计