Skip to content

🤖 ​Tlias 系统 - 作业部分_1


一、概述



❗ 注意:

所有的接口在开发时,一定一定一定 要按照接口文档开发 !!!

否则,和前端进行联调时,将会出现问题 !!!


微信截图_20250821165510.FL0-Q0D7
  • 如上图,本篇笔记将记录完成 班级管理学员管理学员信息统计 、 和 日志信息统计 的 个人笔记

二、数据准备


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

sql
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');

  • 表结构关系说明:
ee677eb4-7e97-4afa-a268-b512bb531809
  • pojo 下创建实体类 Clazz
java
@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; //班级状态 - 未开班 , 在读 , 已结课
}

  • pojo 下创建实体类 Student
java
@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;//班级名称
}

三、班级管理


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

  • 条件分页查询接口
  • 查询所有员工接口
  • 新增班级信息接口
  • 根据 ID 查询班级接口
  • 修改班级信息接口
  • 删除班级信息接口

3.1 条件分页查询接口


📌 参考员工管理模块的条件分页查询的实现


3.1.1 需求


01ca68b8-4870-4238-8b43-7264041cf696

📌 注意:

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

如果:

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

3.1.2 接口文档

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

3.1.3 思路


  • 结合 实体类数据库表前端展示的表 先初步了解一下需求

  • 实体类
微信截图_20250908114154
  • 前端展示的表
微信截图_20250908114446
  • 数据库表
微信截图_20250908114653
  • 查看接口文档

    • 请求路径:/clazzs
    • 请求方式 :GET
    • 请求数据样例:/clazzs?name=java&begin=2023-01-01&end=2023-06-30&page=1&pageSize=5
    • 响应数据样例 :
    json
    {
      "code": 1,
      "msg": "success",
      "data": {
        "total": 6,
        "rows": [
          {
            "id": 7,
            "name": "黄埔四期",
            "room": "209",
            "beginDate": "2023-08-01",
            "endDate": "2024-02-15",
            "masterId": 7,
            "createTime": "2023-06-01T17:51:21",
            "updateTime": "2023-06-01T17:51:21",
            "masterName": "纪晓芙",
            "status": "已开班"
          },
          {
            "id": 6,
            "name": "JavaEE就业166期",
            "room": "105",
            "beginDate": "2023-07-20",
            "endDate": "2024-02-20",
            "masterId": 20,
            "createTime": "2023-06-01T17:46:10",
            "updateTime": "2023-06-01T17:46:10",
            "masterName": "陈友谅",
            "status": "未开班"
          }
        ]
      }
    }

📌 注意:

从前端展示的表格来看,或者响应数据来看,需要后端返回班主任的名字 masterName ,

而且在班级表的一条班级数据中,只有 master_id , 观察建表给出的注释 comment 可以知道

这个字段其实是关联了员工表的,也就是说,这个需求涉及到多表查询,

需要用 master_id 对应员工表的 id 从而获取到员工表的 name 作为 masterName .


  • 三层架构思路:
微信截图_20250908140342

3.1.4 基础代码准备


准备 Clazz 班级管理的基础结构,包括 ControllerServiceMapper


  • ClazzController
java
@Slf4j
@RestController
@RequestMapping("/clazzs")
public class ClazzController {

    //注入Service层bean
    @Autowired
    private ClazzService clazzService;
}
  • ClazzService 接口
java
public interface ClazzService {
}

  • ClazzServiceImpl
java
@Service
public class ClazzServiceImpl implements ClazzService {

    //注入Mapper层bean
    @Autowired
    private ClazzMapper clazzMapper;
}

  • ClazzMapper
java
@Mapper
public interface ClazzMapper {
}

3.1.5 SQL & Mapper


  • 从前面可以知道,这是个多表查询,需要查询班级表中的所有信息,及根据 master_id

作为员工表的 id 去查询 masterName 班主任的名字。就可以先考虑一下,如果写一个查询

所有班级的基础的多表查询的 SQL 应该怎么写 ?

sql
-- 涉及到多表查询,需要按照 clazz 的 master_id 对应 emp 表的id 查出对应的班主任名称 masterName
select c.* , e.name as masterName
from clazz as c left join emp as e on c.master_id = e.id;
  • 查询结果如下:
微信截图_20250908144009
  • 根据这个基本的 SQL 尝试着写一个 Mapper 接口
java
@Mapper
public interface ClazzMapper {

    /**
     * 查询所有的班级及班主任姓名
     */
    @Select("select c.* , e.name as masterName from clazz as c left join emp as e on c.master_id = e.id")
    public List<Clazz> list();

}

📌 ​注意:

注意,上述 SQL 语句中,给 班主任名称 起了别名 masterName ,是因为在接口文档中,

要求部门名称给前端返回的数据中,就必须叫 masterName。 而这里我们需要将查询返回的每一条记录

都封装到 Clazz 对象中,那么就必须保证查询返回的字段名与属性名是一一对应的。


此时,我们就需要在 Clazz 中定义一个属性 masterName 用来封装班主任名称。

刚刚建实体类的时候已经携带 , 具体如下高亮:

java
@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; //班级状态 - 未开班 , 在读 , 已结课
}

  • 代码编写完毕后,我们可以编写一个单元测试,对上述的程序进行测试:
java
@SpringBootTest
class TliasWebManagementApplicationTests {

	@Autowired
	private ClazzMapper clazzMapper;

	@Test
	public void testClazzList(){
		List<Clazz> clazzList = clazzMapper.list();
		clazzList.forEach(System.out::println);
	}

}
  • 运行单元测试后,我们看到控制台输出的数据:
微信截图_20250908145322

可以看到,班级的信息,以及班级对应班主任的名称都查询出来了。


3.1.6 基础分页查询


先完成基础的分页查询,再在此基础上修改为 条件分页查询


3.1.6.1 思路

  • 梳理一下每层要做什么事

    • 首先需要用到 PageHelper 分页插件,确保依赖已经导入(在前面完成员工管理的时候已经导入)
    xml
    <!--分页插件PageHelper-->
    <dependency>
        <groupId>com.github.pagehelper</groupId>
        <artifactId>pagehelper-spring-boot-starter</artifactId>
        <version>1.4.7</version>
    </dependency>

    记得刷新 maven ...

    • application.yml 配置
    yaml
    # 分页插件 PageHelper
    pagehelper:
      reasonable: true # 启用分页参数的合理性验证
    helper-dialect: mysql # 指定数据库的方言
    • Controller 层 :

      • 需要接收分页参数 pagepageSize ,

        需要设置默认值, 使用 @RequestParam(defaultValue = "")

      • log.info 打印日志信息

    • 然后调用 service 层返回一个 PageResult<Clazz> pageResult

  • 把这个 pageResult 返回给前端

    • Service 层 :

      • 接收参数 pagepageSize
      • 设置分页参数 PageHelper.startPage(page,pageSize);
      • 调用 Mapper 层接口执行查询操作,获取一个返回的 List<Clazz> clazzList
    • clazzList 强转成 Page<Clazz> 类型

    • 封装结果 return new PageResult<Clazz>(p.getTotal(), p.getResult());

    • Mapper 层 :

      • 只需要进行正常的列表查询即可,而且不需要考虑分页操作,就是一条正常的查询语句

3.1.6.2 代码实现

  • ClazzController
java
/*
查询班级信息及其班主任名称
*/
@GetMapping
public Result page(@RequestParam(defaultValue = "1") Integer page,
                   @RequestParam(defaultValue = "10") Integer pageSize){
    log.info("查询班级信息,page={},pageSize={}",page,pageSize);
    PageResult<Clazz> pageResult = clazzService.page(page, pageSize);
    return Result.success(pageResult);
}

  • ClazzServiceImpl
java
@Override
public PageResult<Clazz> page(Integer page, Integer pageSize) {
    //1.设置分页参数
    PageHelper.startPage(page,pageSize);

    //2.调用Mapper层接口执行查询操作
    List<Clazz> clazzList = clazzMapper.list();

    //3.把返回结果强转成 Page<Clazz> 类型
    Page<Clazz> p = (Page<Clazz>) clazzList;

    //4.封装结果返回
    return new PageResult<Clazz>(p.getTotal(),p.getResult());
}

  • ClazzMapper 没有变化,因为有 PageHelper , 只需要进行正常的列表查询即可
java
/**
* 查询所有的班级及班主任姓名
*/
@Select("select c.* , e.name as masterName from clazz as c left join emp as e on c.master_id = e.id")
public List<Clazz> list();

3.1.6.3 测试

  • ApiFox 测试 :http://localhost:8080/clazzs
微信截图_20250909201323
  • 前后端联调
微信截图_20250909201444
  • 可见,数据我们都是请求到了,并且前端也是接收并渲染好了

    只不过还有一个 班级状态 我们没有做处理


3.1.6.4 班级状态

3.1.1 需求 的时候就说明到班级状态如下:

📌 注意:

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

如果:

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

  • 思考一下,这部分其实我们应该在 Service 层做补充,在 ServiceMapper

    请求到数据后就是一个 List<Clazz>可以遍历出每一个 Clazz ,并且获取到当前时间,

    然后把开课时间和结课时间与当前时间做一下比较。


📌 注意:

  • Mybatis 给我们返回来的时间数据已经是 LocalDateTime 类型了

    而且我们获取时间使用 LocalDateTime.now() 也是 LocalDateTime 类型

  • 两个 LocalDateTime 比较时间:

    • isBefore():检查一个时间是否在另一个时间之前。
    • isAfter():检查一个时间是否在另一个时间之后。
    • isEqual():检查两个时间是否相等。

  • 代码实现:
java
@Override
public PageResult<Clazz> page(Integer page, Integer pageSize) {
    //1.设置分页参数
    PageHelper.startPage(page,pageSize);

    //2.调用Mapper层接口执行查询操作
    List<Clazz> clazzList = clazzMapper.list();

    //3. 补充班级状态
    //3.1 获取当前时间
    LocalDateTime now = LocalDateTime.now();
    //3.2 遍历出每个Clazz对象,把开课时间和结课时间与当前时间做一下比较
    clazzList.forEach(clazz -> {
        //做判断,然后给clazz的status赋值
        if(clazz.getBeginDate().isAfter(ChronoLocalDate.from(now))){
            //开课时间在当前时间后面,说明未开班
            clazz.setStatus("未开班");
        } else if (clazz.getEndDate().isBefore(ChronoLocalDate.from(now))) {
            //结课时间在当前时间之前,说明已结课
            clazz.setStatus("已结课");
        }else{
            //其他就是在读中
            clazz.setStatus("在读中");
        }
    });

    //4.把返回结果强转成 Page<Clazz> 类型
    Page<Clazz> p = (Page<Clazz>) clazzList;

    //5.封装结果返回
    return new PageResult<Clazz>(p.getTotal(),p.getResult());
}

  • 测试:
微信截图_20250909210404
微信截图_20250909210431

3.1.7 条件分页查询


做完了分页查询后,下面我们需要在分页查询的基础上,添加条件。


3.1.7.1 需求

微信截图_20250910080918
  • 可以看到页面原型及需求中的描述,搜索栏的搜索条件有两个:
    • 班级名称 :模糊匹配
    • 结课时间:范围匹配

3.1.7.2 SQL

  • 在前面我们写了一个基本的 查询全部班级及其班主任名称的 SQL 语句,可以尝试根据条件描述

    添加上 班级名称 和 结课时间 这两个条件,写一个示例的 SQL:

    sql
    select c.* , e.name as masterName
    from clazz as c left join emp as e on c.master_id = e.id
    where c.name like '%java%'
    and c.end_date between '2024-01-01' and '2024-03-03'; 
  • 比如上述这个添加了 班级名称(模糊匹配有 java )的 并且 结课时间在 2024-01-012024-03-03

    这两个条件的,运行的效果如下:

微信截图_20250910082454
  • 经过条件的筛选后,6 条数据 只有一条满足条件

3.1.7.3 功能开发

  • 请求路径:/clazzs
  • 请求方式 :GET
  • 请求数据样例:/clazzs?name=java&begin=2023-01-01&end=2023-06-30&page=1&pageSize=5

  • 在原有分页查询的代码基础上进行改造:

  • 1). 在 ClazzController 方法中通过多个方法形参,依次接收这几个参数
java
/*
查询班级信息及其班主任名称
*/
@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("查询班级信息,请求参数:{},{},{},{},{}",page,pageSize,name,begin,end);
    PageResult<Clazz> pageResult = clazzService.page(page, pageSize,name,begin,end);
    return Result.success(pageResult);
}

  • 2). 修改 ClazzServiceClazzServiceImpl 中的代码逻辑

  • ClazzService

java
public interface ClazzService {

    /*
    条件分页查询
     */
    PageResult<Clazz> page(Integer page, Integer pageSize, String name, LocalDate begin , LocalDate end);
    
}
  • ClazzServiceImpl
java
@Override
public PageResult<Clazz> page(Integer page, Integer pageSize, String name, LocalDate begin, LocalDate end) {
    //1.设置分页参数
    PageHelper.startPage(page,pageSize);

    //2.调用Mapper层接口执行查询操作
    List<Clazz> clazzList = clazzMapper.list(name,begin,end); 

    //3. 补充班级状态
    //3.1 获取当前时间
    LocalDateTime now = LocalDateTime.now();
    //3.2 遍历出每个Clazz对象,把开课时间和结课时间与当前时间做一下比较
    clazzList.forEach(clazz -> {
        //做判断,然后给clazz的status赋值
        if(clazz.getBeginDate().isAfter(ChronoLocalDate.from(now))){
            //开课时间在当前时间后面,说明未开班
            clazz.setStatus("未开班");
        } else if (clazz.getEndDate().isBefore(ChronoLocalDate.from(now))) {
            //结课时间在当前时间之前,说明已结课
            clazz.setStatus("已结课");
        }else{
            //其他就是在读中
            clazz.setStatus("在读中");
        }
    });

    //4.把返回结果强转成 Page<Clazz> 类型
    Page<Clazz> p = (Page<Clazz>) clazzList;

    //5.封装结果返回
    return new PageResult<Clazz>(p.getTotal(),p.getResult());
}

📌 由于 SQL 语句比较复杂,建议将 SQL 语句配置在 XML 映射文件中。


  • 新增 Mapper 映射文件 ClazzMapper.xml

    • 路径: resource/com/itheima/mapper/ClazzMapper.xml
    • 1). 先搭好 dtd 约束:
    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="">
    
    </mapper>
    • 2). namespace 属性为 Mapper 接口全限定名
    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.ClazzMapper"> 
    
    
    </mapper>
    • 3). 动态SQL 语句:
    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.ClazzMapper">
    
    
        <select id="list" resultType="com.itheima.pojo.Clazz">
            select c.* , e.name as masterName
            from clazz as c left join emp as e on c.master_id = e.id
            
            <where>
                <if test="name != null and name != ''">
                    c.name like concat('%',#{name},'%')
                </if>
                <if test="begin != null and end != null">
                    and c.end_date between #{begin} and #{end}
                </if>
            </where>
    
        </select>
    
    </mapper>

    📌 回顾:两个动态SQL标签​

    <if> :判断条件是否成立,如果条件为 true ,则拼接 SQL

    <where> :根据查询条件,来生成 where 关键字,并会自动去除条件前面多余的 andor


3.1.7.4 测试

记得重新启动服务 ...


  • Apifox 测试 :
微信截图_20250910091405
  • 前后端联调:
微信截图_20250910091532

3.2 查询所有员工接口


3.2.1 需求


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

展示的就是所有的员工职位为班主任的数据。

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

e38129c6-bee5-4d0c-94ad-005873611b3a

3.2.2 接口文档


  • 请求路径:/emps/list

  • 请求方式:GET

  • 接口描述:该接口用于查询全部员工信息 .

  • 响应数据样例:

json
{
  "code": 1,
  "msg": "success",
  "data": [
    {
      "id": 21,
      "username": "zcc",
      "password": "123456",
      "name": "周星驰",
      "gender": 1,
      "image": "https://web-65.oss-cn-beijing.aliyuncs.com/99c143e9-0241-41f3-bc55-dd5e4e0824f4.jpg",
      "job": 1,
      "salary": 8000,
      "entryDate": "2023-04-23",
      "deptId": 2,
      "createTime": "2023-05-26T17:25:01",
      "updateTime": "2023-06-04T19:25:15"
    },
    {
      "id": 6,
      "username": "xiaozhao",
      "password": "123456",
      "name": "小昭",
      "gender": 2,
      "image": "https://web-65.oss-cn-beijing.aliyuncs.com/da94dc38-f165-480c-b8b7-0b3f4bcd1602.jpg",
      "job": 3,
      "salary": 8000,
      "entryDate": "2013-09-05",
      "deptId": 1,
      "createTime": "2023-04-07T11:16:00",
      "updateTime": "2023-04-14T08:22:41"
    }
  ]
}

3.2.3 功能开发


  • 既然是查询全部员工信息,就写在员工管理部分的代码里。

📌 注意:

需要注意的是,虽然说是 查询所有员工接口 , 但其实我们的 Emp 是有 job

1:班主任,2:讲师,3:学工主管,4:教研主管,5:咨询师 我们需要在查询的时候加上条件

job 为 1 也就是 班主任才返回


  • EmpController
java
/*
查询所有员工数据(班级管理的班主任下拉列表)
*/
@GetMapping("/list")
public Result list(){
    log.info("查询所有员工数据...");
    List<Emp> empList = empService.list();
    return Result.success(empList);
}

  • EmpService
java
/*
查询所有员工数据(班级管理的班主任下拉列表)
*/
List<Emp> list();

  • EmpServiceImpl
java
/*
查询所有员工数据(班级管理的班主任下拉列表)
*/
@Override
public List<Emp> list() {
    return empMapper.listAll();
}

  • EmpMapper
java
/*
查询所有员工数据(班级管理的班主任下拉列表)
*/
@Select("select * from emp")
List<Emp> listAll();

📌 注意:

前面根据查询条件查询员工的命名是

List<Emp> list(EmpQueryParam empQueryParam);

这里就不能用 list()


3.2.4 测试


  • Apifox 测试:
微信截图_20250910205205
  • 前后端联调:
微信截图_20250910205225

3.3 新增班级信息接口


3.3.1 需求


3752563b-5a88-46ec-a22b-52fe6a489e9d
  • 在表单中录入信息,对应的表结构是 Clazz

    而且里面的班主任也是在已有员工职位为班主任的里面选

    不涉及到多表的操作,还是不太复杂的。


3.3.2 接口文档


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


  • 请求路径:/clazzs

  • 请求方式:POST

  • 接口描述:该接口用于添加班级信息


微信截图_20250911082734
  • 请求数据样例:
json
{
  "name": "JavaEE就业166期",
  "room": "101",
  "beginDate": "2023-06-01",
  "endDate": "2024-01-25",
  "masterId": 7,
  "subject": 1
}

📌 回顾:

  • 问题1:如何限定请求方式是 POST ? @PostMapping
  • 问题2:怎么在 controller 中接收 json 格式的请求参数?@RequestBody

3.3 功能开发


  • 首先因为是 json 格式的请求数据需要准备一个实体类来接收

    我们发现 Clazz 就已经包含这些属性,所以直接用 Clazz 就好了


  • ClazzController 里面添加 sava 方法
java
/*
新增班级信息
*/
@PostMapping
public Result save(@RequestBody Clazz clazz){
    log.info("新增班级: {}",clazz);
    clazzService.save(clazz);
    return Result.success();
}

  • ClazzService 中增加 save 方法
java
/*
新增班级
*/
void save(Clazz clazz);

  • ClazzServiceImpl 中增加 save 方法 , 实现接口中的 save 方法
java
/*
新增班级信息
*/
@Override
public void save(Clazz clazz) {
    //补全基础属性
    clazz.setCreateTime(LocalDateTime.now());
    clazz.setUpdateTime(LocalDateTime.now());

    //保存新班级
    clazzMapper.insert(clazz);
}

  • ClazzMapper 中增加 insert 方法
java
/*
新增班级信息
*/
@Insert("insert into clazz(name,room,begin_date,end_date,master_id,subject,create_time,update_time) values " +
        "(#{name},#{room},#{beginDate},#{endDate},#{masterId},#{subject},#{createTime},#{updateTime})")
void insert(Clazz clazz);

3.4 测试


  • Apifox 测试:
微信截图_20250911090404
  • 前后端联调:
微信截图_20250911090631
微信截图_20250911090709

3.4 根据ID查询班级接口


3.4.1 需求


b13051ed-606d-4ae2-9813-7b09d3efa4f8
  • 需要在点击 编辑按钮 的时候根据 ID 查询对应的班级信息,然后 回显 到前端的表单里。

3.4.2 接口文档


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

  • 请求路径:/clazzs/{id}

  • 请求方式:GET

  • 接口描述:该接口用于根据主键 ID 查询班级的信息

  • 请求参数:路径参数

  • 请求参数样例 :/clazzs/8

  • 响应数据样例:

json
{
  "code": 1,
  "msg": "success",
  "data": {
    "id": 8,
    "name": "JavaEE就业166期",
    "room": "101",
    "beginDate": "2023-06-01",
    "endDate": "2024-01-25",
    "masterId": 7,
    "subject": 1,
    "createTime": "2023-06-04T17:37:45",
    "updateTime": "2023-06-04T17:37:45"
  }
}

📌 注意:路径参数用到的注解是 @PathVariable


3.4.3 功能开发


  • ClazzController
java
/*
* 根据ID查询班级的详细信息
*/
@GetMapping("/{id}")
public Result getInfo(@PathVariable Integer id){
    log.info("根据 id : {} 查询员工信息",id);
    Clazz clazz = clazzService.getInfo(id);
    return Result.success(clazz);
}

  • ClazzService
java
/*
* 根据ID查询班级的详细信息
*/
Clazz getInfo(Integer id);

  • ClazzService
java
/*
* 根据ID查询班级的详细信息
*/
@Override
public Clazz getInfo(Integer id) {
    return clazzMapper.getById(id);
}

  • ClazzMapper
java
/*
* 根据ID查询班级的详细信息
*/
@Select("select * from clazz where id=#{id}")
Clazz getById(Integer id);

3.4.4 测试


  • Apifox 测试:
微信截图_20250911102740
  • 前后端联调:
微信截图_20250911102801

3.5 修改班级信息接口


3.5.1 需求


  • 前面 3.4 我们已经实现把对应的班级信息回显到前端页面的表单上了

    接下来如果用户对信息进行了更改,点击了保存按钮,那么我们就要更新这个班级信息

6b7af3e1-0137-4a6a-b735-67b676bc19b4

3.5.2 接口文档


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

  • 请求路径:/clazzs

  • 请求方式:PUT

  • 接口描述:该接口用于修改班级的数据信息

  • 请求参数:application/json

  • 请求参数样例:

json
{
    "id": 2,
    "name": "前端就业90期(修改测试)",
    "room": "210",
    "beginDate": "2024-07-10",
    "endDate": "2025-01-20",
    "masterId": 3,
    "subject": 2,
    "createTime": "2023-06-01T17:46:10",
    "updateTime": "2023-06-01T17:46:10"
}

3.5.3 功能开发


  • 1). ClazzController 增加 update 方法接收请求参数,响应数据
java
//修改班级
@PutMapping
public Result update(@RequestBody Clazz clazz) {
    log.info("修改班级信息 : {}", clazz);
    clazzService.update(clazz);
    return Result.success();
}

  • 2). ClazzService 接口增加 update 方法
java
//修改班级信息
void update(Clazz clazz);

  • 3). ClazzServiceImpl 实现类实现 update 方法
java
//修改班级信息
@Override
public void update(Clazz clazz) {
    //修改更新时间
    clazz.setUpdateTime(LocalDateTime.now());

    //调用mapper更新数据库
    clazzMapper.updateById(clazz);
}

  • 4). ClazzMapper 接口中增加 updateById 方法
java
//修改班级信息
void updateById(Clazz clazz);

  • 5). ClazzMapper.xml 配置文件中定义对应的 SQL 语句,基于动态 SQL 更新班级信息
xml
<!-- 修改班级信息 -->
<update id="updateById">
    update clazz
    <set>
        <if test="name != null and name != ''">name = #{name},</if>
        <if test="room != null and room != ''">room = #{room},</if>
        <if test="beginDate != null">begin_date = #{beginDate},</if>
        <if test="endDate != null">end_date = #{endDate},</if>
        <if test="masterId != null">master_id = #{masterId},</if>
        <if test="subject != null">subject = #{subject},</if>
        <if test="createTime != null">create_time = #{createTime},</if>
        <if test="updateTime != null">update_time = #{updateTime},</if>
    </set>
    where id = #{id}
</update>

📌 回顾:

  • 通过 if 标签来进行如果我们某些参数没有传参数过来,这时候我们就不修改其原来的值,

    如果传过来了,就执行相应的修改语句,这样应该能保证数据完整性

  • <set> 标签用来替换 set 关键字,可以自动帮助我们生成 set 关键字,

    同时可以自动去除掉更新字段后面多余的逗号


3.5.4 测试


  • Apifox 测试:
微信截图_20250912083031
  • 前后端联调:
微信截图_20250912083158

3.6 删除班级信息接口


3.6.1 需求


3641b206-9400-4b13-bb2d-b7c03b8e0504
  • 当我们点击后面的 删除按钮 的复选框,然后再 确认 就可以根据 ID 删除这个班级的信息

3.6.2 接口文档


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

  • 请求路径:/clazzs/{id}

  • 请求方式:DELETE

  • 接口描述:该接口用于删除班级信息

  • 请求参数:路径参数

  • 请求参数样例:/clazzs/5


3.6.3 功能开发


  • ClazzController 中增加如下方法 delete ,使用路径参数请求,执行删除班级的操作。
java
//删除班级信息
@DeleteMapping("/{id}")
public Result delete(@PathVariable Integer id){
    log.info("删除班级信息: id 为 {}",id);
    clazzService.delete(id);
    return Result.success();
}

  • 在接口中 ClazzService 中定义接口方法 delete
java
//删除班级信息
void delete(Integer id);

  • 在实现类 ClazzServiceImpl 中实现接口方法 delete
java
//删除班级信息
@Override
public void delete(Integer id) {
    clazzMapper.deleteById(id);
}

  • ClazzMapper 接口中增加 deleteById 方法,实现删除班级信息
java
//删除班级信息
@Delete("delete from clazz where id=#{id}")
void deleteById(Integer id);

3.6.4 测试


  • Apifox 测试:

先添加一条用于删除测试的班级信息数据

微信截图_20250912084715

把刚刚添加的测试数据删除

微信截图_20250912084700
微信截图_20250912133104
  • 前后端测试:

先添加一条用于删除测试的班级信息数据

微信截图_20250912133221

把刚刚添加的测试数据删除

微信截图_20250912133302

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




🤖 ​Tlias 系统 - 作业部分_2


前面完成了 班级管理 部分的内容,接着从 学员管理 继续 ...


四、学员管理


4.1 查询所有班级接口


4.1.1 需求


d3b61fdc-fa14-47f5-bfab-e96a95c30051
  • 在新增学员的时候,要展示出所有的班级信息。

4.1.2 接口文档


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

  • 请求路径:/clazzs/list

  • 请求方式:GET

  • 接口描述:该接口用于查询所有班级信息

  • 请求参数:无

  • 响应数据案例:

java
{
  "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() 方法
java
//查询所有班级接口(用于新增学员的时候,要展示出所有的班级信息。)
@GetMapping("/list")
public Result list(){
    log.info("查询所有班级接口 ... ");
    List<Clazz> clazzList =  clazzService.list();
    return Result.success(clazzList);
}

  • ClazzService 定义接口方法 list()
java
//查询所有班级信息
List<Clazz> list();

  • ClazzServiceImpl 实现接口方法 list()
java
//查询所有班级信息
@Override
public List<Clazz> list() {
    return clazzMapper.getAllInfo();
}

  • ClazzMapper 接口中增加 getAllInfo()
java
//查询所有班级信息
@Select("select * from clazz")
List<Clazz> getAllInfo();

4.1.4 测试


  • Apifox 测试
微信截图_20250912135513

因为还没有写 学生的分页查询 所以暂时还不能前后端联调


4.2 条件分页查询接口


4.2.1 需求


d572d94e-e1a4-4834-903d-0bd82f892353
  • 分页查找出数据库所有的学生信息 , 若有条件按条件筛选查询

4.2.2 接口文档


  • 参照接口文档 学员管理 --> 学员列表查询

  • 请求路径:/students

  • 请求方式:GET

  • 接口描述:该接口用于学员列表数据的条件分页查询

  • 请求参数格式:queryString

  • 请求数据样例:/students?name=张三&degree=1&clazzId=2&page=1&pageSize=5

  • 响应数据样例:

json
{
  "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 班级管理的基础结构,包括 ControllerServiceMapper


  • StudentController
java
@Slf4j
@RestController
public class StudentController {


    //注入Service层bean
    @Autowired
    private StudentService studentService;
}

  • StudentService
java
public interface StudentService {
}

  • StudentServiceImpl
java
@Service
public class StudentServiceImpl implements StudentService {

    //注入Mapper层bean
    @Autowired
    private StudentMapper studentMapper;
}

  • StudentMapper
java
@Mapper
public interface StudentMapper {
}

4.2.4 SQL & Mapper


  • 前面我们已经创建了实体类 Student

  • 观察前端表格展示的数据列需要什么
微信截图_20250913080025
  • 这跟我们前面做班级的分页查询很相似
    • 前面是需要展示 班主任的名称 而且对应的 Clazz 数据库表中只有 master_id
    • 这里是需要展示 班级的名称 而且对应的 Student 数据库表中只有 clazz_id
  • 所以共同点就是都要做 多表查询

  • 所以先做出基础的多表查询的 SQL 代码:需要查询学生表中的所有信息,及

    根据 clazz_id 作为班级表的 id 去查询 clazzName 班级的名称。

sql
-- 基础多表查询代码 
-- 需要按照 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
  • 查询结果如下:
微信截图_20250913080831
  • 根据这个基本的 SQL 尝试着写一个 Mapper 接口
java
@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 用来封装班级名称。

刚刚建实体类的时候已经携带 , 具体如下高亮:

java
@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;//班级名称
}

  • 代码编写完毕后,我们可以编写一个单元测试,对上述的程序进行测试:
java
@SpringBootTest
class TliasWebManagementApplicationTests {

	@Autowired
	private StudentMapper studentMapper;

	@Test
	public void testStudentList(){
		List<Student> studentList = studentMapper.list();
		studentList.forEach(System.out::println);
	}
}
  • 运行单元测试后,我们看到控制台输出的数据:
微信截图_20250913081813

可以看到,学生的信息,以及班级对应班主任的名称都查询出来了。


4.2.5 基础分页查询


先完成基础的分页查询,再在此基础上修改为 条件分页查询


4.2.5.1 思路

  • Controller 层 :

    • 需要接收分页参数 pagepageSize ,

      需要设置默认值, 使用 @RequestParam(defaultValue = "")

    • log.info 打印日志信息

  • 然后调用 service 层返回一个 PageResult pageResult

    • 把这个 pageResult 返回给前端
  • Service 层 :

    • 接收参数 pagepageSize
    • 设置分页参数 PageHelper.startPage(page,pageSize);
    • 调用 Mapper 层接口执行查询操作,获取一个返回的 List studentList
    • clazzList 强转成 Page 类型
    • 封装结果 return new PageResult(p.getTotal(), p.getResult());
  • Mapper 层 :

    • 只需要进行正常的列表查询即可,而且不需要考虑分页操作,就是一条正常的查询语句

4.2.5.2 代码实现

  • StudentController
java
//查询学生信息及其班主任名称
@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
java
//查询学生信息及其班主任名称
PageResult<Student> page(Integer page, Integer pageSize);

  • StudentServiceImpl
java
//查询学生信息及其班主任名称
@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
java
//查询所有的学生信息及其班级名称
@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 测试:
微信截图_20250913092013
  • 前后端联调:
微信截图_20250913092055
  • 现在可以做一下 4.1.4 的前后端联调 也就是 查询所有班级接口的前后端联调
微信截图_20250916101844

可以发现也是没问题的 ...


4.3 新增学生信息接口


4.3.1 需求


91d8c0cb-3e9a-49c5-96a2-9adbb8bf0c4a
  • 在这个表单中我们先需要完成 页面回显 的功能,把对应的学员信息 回显 到前端的这个表单上
    • 其中 所属班级 我们已经在 4.1 完成了
    • 点击保存按钮,根据用户填写的信息进行新增学生信息

4.3.2 接口文档


  • 参照接口文档 学员管理 --> 添加学员

  • 请求路径:/students

  • 请求方式:POST

  • 接口描述:该接口用于添加学员信息

  • 请求参数格式:application/json

  • 请求数据样例:

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
java
//新增学生信息
@PostMapping
public Result save(@RequestBody Student student){
    log.info("新增学生信息: {}",student);
    studentService.save(student);
    return Result.success();
}

  • StudentService
java
//新增学生信息
void save(Student student);

  • StudentServiceImpl
java
//新增学生信息
@Override
public void save(Student student) {
    //补全基础信息
    student.setCreateTime(LocalDateTime.now());
    student.setUpdateTime(LocalDateTime.now());

    //执行新增操作
    studentMapper.insert(student);
}

  • StudentMapper
java
//新增学生信息
@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 测试
微信截图_20250916105840

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

微信截图_20250916105852
  • 前后端联调
微信截图_20250916110138

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

微信截图_20250916110150

4.4 根据ID查询学生接口


4.4.1 需求


6cd54ad9-8763-4253-9cc3-61317e90a0ad
  • 点击 编辑 按钮的时候,我们需要 页面回显 该学生的信息

4.4.2 接口文档


  • 参照接口文档 学员管理 --> 根据ID查询学员

  • 请求路径:/students/{id}

  • 请求方式:GET

  • 接口描述:该接口用于根据主键ID查询学员的信息

  • 请求参数格式:路径参数

  • 请求参数样例:/students/8

  • 响应数据样例:

json
{
  "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
java
//根据ID查询学生
@GetMapping("/{id}")
public Result getInfo(@PathVariable Integer id){
    log.info("根据 ID : {} 查询学生",id);
    Student student = studentService.getInfo(id);
    return Result.success(student);
}

  • StudentService
java
//根据ID查询学生
Student getInfo(Integer id);

  • StudentServiceImpl
java
//根据ID查询学生
@Override
public Student getInfo(Integer id) {
    return studentMapper.getById(id);
}

  • StudentMapper
java
//根据ID查询学生信息
@Select("select * from student where id=#{id}")
Student getById(Integer id);

4.4.4 测试


  • Apifox 测试
微信截图_20250916112016
  • 前后端联调
微信截图_20250916112043

4.5 修改学生信息接口


4.5.1 需求


52df2028-5966-42fe-8a0b-b99984d14407
  • 前面 4.4 已经完成点击 编辑 按钮然后 页面回显 接着就是如果用户修改信息,点击保存需要修改学员信息。

4.5.2 接口文档


  • 参照接口文档 学员管理 --> 修改学员

  • 请求路径:/students

  • 请求方式:PUT

  • 接口描述:该接口用于修改学员的数据信息

  • 请求参数格式:application/json

  • 请求参数样例:

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
java
//修改学生信息
@PutMapping
public Result update(@RequestBody Student student){
    log.info("修改学生信息,{}",student);
    studentService.update(student);
    return Result.success();
}

  • StudentService
java
//修改学生信息
void update(Student student);

  • StudentServiceImpl
java
//修改学生信息
@Override
public void update(Student student) {
    //更新修改时间
    student.setUpdateTime(LocalDateTime.now());

    //执行修改操作
    studentMapper.updateById(student);
}

  • StudentMapper
java
//修改学生信息
void updateById(Student student);

  • StudentMapper.xml
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 测试
微信截图_20250916115508

查看数据库的数据修改

微信截图_20250916115520
  • 前后端联调
微信截图_20250916115740

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

微信截图_20250916115751

4.6 删除学生信息接口


4.6.1 需求


eb44dcaf-bed1-48cb-bb54-6d52fa24186e
  • 这有两个按钮的接口需要实现:一个是 批量删除 , 一个是 单行的删除

    那么这跟我们前面的案例的思考方式是一样的:我们没必要为这个去设计两个接口

    我们只需要设计一个 根据ID来批量删除 的接口,删除一个是 Ids 这个数组长度为 1 的情况


4.6.2 接口文档


  • 参照接口文档 学员管理 --> 删除学员

  • 请求路径:/students/{ids}

  • 请求方式:DELETE

  • 接口描述:该接口用于批量删除学员信息

  • 请求参数格式:路径参数

  • 请求参数样例:/students/1,2,3


4.6.3 功能开发


  • StudentController
java
//批量删除学生信息
@DeleteMapping("/{ids}")
public Result delete(@PathVariable List<Integer> ids){
    log.info("根据 id 列表 : {} 批量删除学生信息",ids);
    studentService.delete(ids);
    return Result.success();
}

  • StudentService
java
//批量删除学生信息
void delete(List<Integer> ids);

  • StudentServiceImpl
java
//批量删除学生信息
@Override
public void delete(List<Integer> ids) {
    studentMapper.deleteByIds(ids);
}

  • StudentMapper
java
//批量删除学生信息
void deleteByIds(List<Integer> ids);

  • StudentMapper.xml
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)

微信截图_20250916210903


微信截图_20250916210947
  • 前后端联调
微信截图_20250916211058
微信截图_20250916211113

4.7 违纪处理接口


4.7.1 需求


26e20d63-d2c7-4451-982f-92c991bce6f1
  • 违纪处理一次,需要将 违纪次数+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
java
//违纪处理
@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
java
//违纪处理
void deal_violation(Integer id, Integer score);

  • StudentServiceImpl
java
//违纪处理
@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 测试
微信截图_20250916215900

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

微信截图_20250916215948

一开始都是 0 的 , 现在次数+1,分数+5 , 没问题


  • 前后端联调
微信截图_20250916220032
微信截图_20250916220048
  • 到此,关于学生管理的增删改查功能,我们就已经全部实现了。

五、学员信息统计


5.1 班级人数统计接口


5.1.1 需求


7eab0f2d-44de-4be4-8a75-1c756fdd3a2d
  • 如上面左图,统计出每一个班级的人数是多少, 主要注意接口文档以什么样的形式返回给前端数据

5.1.2 接口文档


  • 参照接口文档 数据统计 --> 班级人数统计

  • 请求路径:/report/studentCountData

  • 请求方式:GET

  • 接口描述:统计每一个班级的人数

  • 请求参数:无

  • 响应数据样例:

json
{
  "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
java
//班级人数统计
@GetMapping("/studentCountData")
public Result countStudent(){
    log.info("班级人数统计...");
    ClazzOption clazzOption = reportService.countStudent();
    return Result.success(clazzOption);
}

  • reportService
java
//班级人数统计
ClazzOption countStudent();

  • reportServiceImpl
java
//班级人数统计
@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
java
//班级人数统计
@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 测试
微信截图_20250917094933
  • 前后端联调
微信截图_20250917095031

5.2 学员学历信息统计接口


5.2.1 需求


7eab0f2d-44de-4be4-8a75-1c756fdd3a2d
  • 如上面右图,统计出每一种学历的人数是多少, 主要注意接口文档以什么样的形式返回给前端数据

5.2.2 接口文档


  • 参照接口文档 数据统计 --> 学员学历统计

  • 请求路径:/report/studentDegreeData

  • 请求方式:GET

  • 接口描述:统计学员的学历信息

  • 请求参数:无

  • 响应数据样例:

json
{
  "code": 1,
  "msg": "success",
  "data": [
    {
      "name": "初中",
      "value": 5
    },
    {
      "name": "高中",
      "value": 6
    },
    {
      "name": "大专",
      "value": 126
    },
    {
      "name": "本科",
      "value": 182
    },
    {
      "name": "硕士",
      "value": 6
    }
  ]
}

5.2.3 功能开发


  • reportController
java
//统计学历信息
@GetMapping("/studentDegreeData")
public Result countStudentDegree(){
    log.info("学历信息统计...");
    List<Map<String,Object>> list = reportService.countStudentDegree();
    return Result.success(list);
}

  • reportService
java
//统计学历信息
List<Map<String, Object>> countStudentDegree();

  • reportServiceImpl
java
//统计学历信息
@Override
public List<Map<String, Object>> countStudentDegree() {
    //调用mapper接口,获取统计数据
    return studentMapper.countStudentDegreeData();
}

  • StudentMapper
java
//统计学历信息
@MapKey("name")
List<Map<String, Object>> countStudentDegreeData();

  • StudentMapper.xml
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 测试
微信截图_20250917102026
  • 前后端联调
微信截图_20250917102208

六、删除部门功能完善


6.1 需求


  • 删除部门时:如果部门下有员工,则不允许删除该部门,

    并给前端提示错误信息:对不起,当前部门下有员工,不能直接删除!

📌 思路:

当删除部门的时候,在员工表查询一下所有员工的 dept_id 然后按这个进行 group by

返回的就是有员工的部门 id , 那么如果要删除的部门 id 包含在这个列表里面,那么就可以

抛出一个错误,然后用全局异常处理器处理...


6.2 功能开发


  • EmpMapper
java
//查找员工一共在哪些部门(用于如果部门下有员工则不能删除该部门)
@Select("select dept_id from emp group by dept_id")
List<Integer> getAllDeptWithEmp();

  • DeptServiceImpl
java
/**
* 根据id删除部门
*/
@Override
public void deleteById(Integer id) {
    List<Integer> allDeptWithEmp = empMapper.getAllDeptWithEmp();
    if (allDeptWithEmp.contains(id)){
        //那么就说明这个部门有员工,不能删除
        throw new IllegalStateException("对不起,当前部门下有员工,不能直接删除!");
    }
    deptMapper.deleteById(id);
}

  • GlobalExceptionHandler
java
//处理删除部门异常
@ExceptionHandler
public Result handleIllegalStateException(IllegalStateException e){
    log.info("程序出错了:",e);
    return Result.error(e.getMessage());
}

6.3 测试


  • 先添加一个用于测试的员工
微信截图_20250917105230
  • Apifox 测试
微信截图_20250917105159
  • 前后端联调
微信截图_20250917105521