需求:班级管理
需要开发如下几个接口,且开发的时候建议按照如下顺序开发 :
准备工作
ClazzController控制层
import com.itheima.service.ClazzService;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.web.bind.annotation.*;
import java.time.LocalDate;
import java.util.List;
@Slf4j
@RestController
@RequestMapping("/clazzs")
public class ClazzController {
@Autowired
private ClazzService clazzService;
ClazzService业务接口层
package com.itheima.service;
import com.itheima.pojo.Clazz;
import com.itheima.pojo.ClazzQueryParam;
import com.itheima.pojo.PageResult;
import java.time.LocalDate;
import java.util.List;
public interface ClazzService {
ClazzServiceImpl业务实现类
package com.itheima.service.impl;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.itheima.exception.BusinessException;
import com.itheima.mapper.ClazzMapper;
import com.itheima.mapper.StudentMapper;
import com.itheima.pojo.Clazz;
import com.itheima.pojo.ClazzQueryParam;
import com.itheima.pojo.PageResult;
import com.itheima.service.ClazzService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
@Service
public class ClazzServiceImpl implements ClazzService {
@Autowired
private ClazzMapper clazzMapper;
@Autowired
private StudentMapper studentMapper;
记得添加注入翻转,控制依赖的注解,实现这个功能
-
条件分页查询接口
参照接口文档 班级管理 -> 班级列表查询
注意:班级状态,显示为:未开班、已结课、在读中 这三种。
如果:
-
当前时间 > 结课时间:状态未 已结课。
-
当前时间 < 开课时间:状态未 未开班。
-
否则,就是 在读中。

-
班级列表查询
-
基本信息
请求路径:/clazzs
请求方式:GET
接口描述:该接口用于班级列表数据的条件分页查询
-
请求参数
参数格式:queryString
参数说明:
| 参数名称 | 是否必须 | 示例 | 备注 |
| name | 否 | 黄埔一期 | 班级名称 |
| begin | 否 | 2023/1/1 | 范围匹配的开始时间(结课时间) |
| end | 否 | 2023/5/1 | 范围匹配的结束时间(结课时间) |
| page | 是 | 1 | 分页查询的页码,如果未指定,默认为1 |
| pageSize | 是 | 10 | 分页查询的每页记录数,如果未指定,默认为10 |
请求数据样例:/clazzs?name=java&begin=2023-01-01&end=2023-06-30&page=1&pageSize=5
-
响应数据
参数格式:application/json
参数说明:
| 名称 | 类型 | 是否必须 | 备注 | 其他信息 |
| code | number | 必须 | 响应码, 1 成功 , 0 失败 | |
| msg | string | 非必须 | 提示信息 | |
| data | object | 必须 | 返回的数据 | |
| |- total | number | 必须 | 总记录数 | |
| |- rows | object [] | 必须 | 数据列表 | item 类型: object |
| |- id | number | 非必须 | id | |
| |- name | string | 非必须 | 班级名称 | |
| |- room | string | 非必须 | 班级教室 | |
| |- beginDate | string | 非必须 | 开课时间 | |
| |- endDate | string | 非必须 | 结课时间 | |
| |- masterId | number | 非必须 | 班主任(员工ID) | |
| |- masterName | string | 非必须 | 班主任姓名(员工姓名) | |
| |- createTime | string | 非必须 | 创建时间 | |
| |- updateTime | string | 非必须 | 更新时间 | |
| |- status | string | 非必须 | 状态 (未开班、已开班、已结课) |
响应数据样例:
{
"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": "未开班"
}
]
}
}
我们先将分页查询需要的对象单独封装到一个对象类中
ClazzQueryParam
package com.itheima.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDate;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ClazzQueryParam {
private Integer page=1;
private Integer pageSize=10;
private String name;
@DateTimeFormat(pattern = "yyyy-MM-dd")
private LocalDate begin;
@DateTimeFormat(pattern = "yyyy-MM-dd")
private LocalDate end;
}
我们分页查询的几个范围和条件就是根据班级名称,起始时间,结束时间进行查询,日期则是按照年月日的格式去查询 ,实体类如上,接下来我们看接口
首先是Get方法,不需要额外写路径,
- GET方法:数据在URL中,Spring自动绑定到对象属性
参数也不需要再去加注解
在响应数据样例中需要返回total和班级集合List,我们可以再把这两个封装到pageresult中
ClazzController
@GetMapping
public Result page(ClazzQueryParam clazzQueryParam){
log.info("分页查询,参数{}",clazzQueryParam);
PageResult pageResult = clazzService.page(clazzQueryParam);
return Result.success(pageResult);
}
pageresult
package com.itheima.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class PageResult<T> {
private Long total;
private List<T> rows;
}
我们具体来看实现类中的方法
@Override
public PageResult page(ClazzQueryParam clazzQueryParam) {
// 1. 使用 PageHelper 设置分页参数(当前页码和每页大小)
PageHelper.startPage(clazzQueryParam.getPage(), clazzQueryParam.getPageSize());
// 2. 调用 Mapper 层的 list 方法,查询符合条件的班级列表
// 查询结果是一个经过增强的 List,实际类型为 Page<Clazz>
List<Clazz> dataList = clazzMapper.list(clazzQueryParam);
// 3. 将查询结果强转为 Page 类型,以提取分页相关的元数据
Page<Clazz> p = (Page<Clazz>) dataList;
// 4. 封装分页结果并返回
// PageResult 包含总记录数(p.getTotal())和当前页的数据列表(p.getResult())
return new PageResult(p.getTotal(), p.getResult());
}
由于 Page 类继承自 ArrayList 实现了 List 接口,所以从类型兼容性来看,Page 是 List 的子类,可以安全向下转型,前提是确实使用了 PageHelper 且紧跟分页查询操作。
我们再来看mapper层的list方法是怎么实现的
我们在xml中配置mybatis完整动态sql查询
<select id="list" resultType="com.itheima.pojo.Clazz">
select
c.*,
e.name masterName,
(case when begin_date > now() then '未开班'
when now() > end_date then '已结课'
else '在读中' end) status
from clazz c
left join emp 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>
order by c.update_time desc
</select>
- 如果
<where>标签内的第一个条件成立,它会去掉条件前面的 AND 或 OR - 如果所有条件都不成立,它会完全忽略 WHERE 子句
concat()是 SQL 函数,用于连接多个字符串,避免 SQL 注入攻击。直接拼接字符串(如like '%'+name+'%')容易导致安全问题
-
查询所有员工接口
那其实,对于培训机构来说,班主任就是这个企业的员工。所以,班主任下拉列表中展示的就是所有的员工数据。
参照接口文档 员工管理 -> 查询全部员工

-
查询全部员工
-
基本信息
请求路径:/emps/list
请求方式:GET
接口描述:该接口用于查询全部员工信息
-
请求参数
无
-
响应数据
参数格式:application/json
参数说明:
| 名称 | 类型 | 是否必须 | 备注 |
| code | number | 必须 | 响应码, 1 成功 , 0 失败 |
| msg | string | 非必须 | 提示信息 |
| data | object[] | 必须 | 返回的数据 |
| |- id | number | 必须 | id |
| |- username | string | 必须 | 用户名 |
| |- name | string | 必须 | 姓名 |
| |- password | string | 非必须 | 密码 |
| |- entryDate | string | 非必须 | 入职日期 |
| |- gender | number | 非必须 | 性别 , 1 男 ; 2 女 |
| |- image | string | 非必须 | 图像 |
| |- job | number | 非必须 | 职位, 说明: 1 班主任,2 讲师, 3 学工主管, 4 教研主管, 5 咨询师 |
| |- salary | number | 非必须 | 薪资 |
| |- deptId | number | 非必须 | 部门id |
| |- createTime | string | 非必须 | 创建时间 |
| |- updateTime | string | 非必须 | 更新时间 |
响应数据样例:
{
"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"
}
]
}
来看控制层
/**
* 查询所有的员工
*/
@GetMapping("/list")
public Result list(){
log.info("查询所有的员工");
List<Emp> list = empService.list();
return Result.success(list);
}
路径如上 ,不需要请求参数,所以不需要参数
返回的响应数据样例是集合,集合里面包含了每个员工的详细信息,所以我们用List<Emp>来储存返回信息
最后result需要响应结果
serviceImpl层
@Override
public List<Emp> list() {
return empMapper.findAll();
}
mapper层
@Select("select * from emp")
List<Emp> findAll();
-
新增班级信息接口
参照接口文档 班级管理 -> 添加班级

-
添加班级
-
基本信息
请求路径:/clazzs
请求方式:POST
接口描述:该接口用于添加班级信息
-
请求参数
参数格式:application/json
参数说明:
| 名称 | 类型 | 是否必须 | 备注 |
| name | string | 必须 | 班级名称 |
| room | string | 必须 | 班级教室 |
| beginDate | string | 必须 | 开课时间 |
| endDate | string | 必须 | 结课时间 |
| masterId | number | 非必须 | 班主任 |
| subject | number | 必须 | 学科, 1:java, 2:前端, 3:大数据, 4:Python, 5:Go, 6:嵌入式 |
请求数据样例:
{
"name": "JavaEE就业166期",
"room": "101",
"beginDate": "2023-06-01",
"endDate": "2024-01-25",
"masterId": 7,
"subject": 1
}
-
响应数据
参数格式:application/json
参数说明:
| 参数名 | 类型 | 是否必须 | 备注 |
| code | number | 必须 | 响应码,1 代表成功,0 代表失败 |
| msg | string | 非必须 | 提示信息 |
| data | object | 非必须 | 返回的数据 |
响应数据样例:
{
"code":1,
"msg":"success",
"data":null
}
为Post请求,需要将请求数据封装到Clazz实体类中,所以要用@ResquestBody注解,响应数据的data为null,所以不需要将service层的处理方法返回给实体类
/**
* 新增班级
*/
@PostMapping
public Result save(@RequestBody Clazz clazz){
log.info("新增班级,数据:{}",clazz);
clazzService.save(clazz);
return Result.success();
}
注意涉及到新增或者修改的时候都要顺带修改创建时间和更新时间
@Override
public void save(Clazz clazz) {
clazz.setCreateTime(LocalDateTime.now());
clazz.setUpdateTime(LocalDateTime.now());
clazzMapper.insert(clazz);
}
在进行insert方法的时候,不需要去给id赋值,因为id是自增的,如下即可
@Insert("insert into clazz VALUES (null,#{name},#{room},#{beginDate},#{endDate},#{masterId}, #{subject},#{createTime},#{updateTime})")
void insert(Clazz clazz);
-
根据ID查询班级接口
参照接口文档 班级管理 -> 根据ID查询

-
根据ID查询
-
基本信息
请求路径:/clazzs/{id}
请求方式:GET
接口描述:该接口用于根据主键ID查询班级的信息
-
请求参数
参数格式:路径参数
参数说明:
| 参数名 | 类型 | 是否必须 | 备注 |
| id | number | 必须 | 班级ID |
请求参数样例:/clazzs/8
-
响应数据
参数格式:application/json
参数说明:
| 名称 | 类型 | 是否必须 | 备注 |
| code | number | 必须 | 响应码, 1 成功 , 0 失败 |
| msg | string | 非必须 | 提示信息 |
| data | object | 必须 | 返回的数据 |
| |- id | number | 必须 | id |
| |- name | string | 必须 | 班级名称 |
| |- room | string | 必须 | 班级教室 |
| |- beginDate | string | 必须 | 开课时间 |
| |- endDate | string | 必须 | 结课时间 |
| |- masterId | number | 必须 | 班主任(员工ID) |
| |- subject | number | 非必须 | 学科, 1:java, 2:前端, 3:大数据, 4:Python, 5:Go, 6:嵌入式 |
| |- createTime | string | 必须 | 创建时间 |
| |- updateTime | string | 必须 | 更新时间 |
响应数据样例:
{
"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"
}
}
根据id查询,路径需要增加id,我们需要将路径中的id获取下来作为参数,需要用@PathVariable注解
/**
* 根据id查询
*/
@GetMapping("/{id}")
public Result getInfo(@PathVariable Integer id){
log.info("根据id查询班级:{}",id);
Clazz clazz = clazzService.getInfo(id);
return Result.success(clazz);
}
@Override
public Clazz getInfo(Integer id) {
return clazzMapper.getInfo(id);
}
@Select("select * from clazz where id = #{id}")
Clazz getInfo(Integer id);
-
修改班级信息接口
参照接口文档 班级管理 -> 修改班级

-
修改班级
-
基本信息
请求路径:/clazzs
请求方式:PUT
接口描述:该接口用于修改班级的数据信息
-
请求参数
参数格式:application/json
参数说明:
| 名称 | 类型 | 是否必须 | 备注 |
| id | number | 必须 | id |
| name | string | 必须 | 班级名称 |
| room | string | 必须 | 班级教室 |
| beginDate | string | 必须 | 开课时间 |
| endDate | string | 必须 | 结课时间 |
| masterId | number | 必须 | 班主任ID(员工ID) |
| subject | number | 非必须 | 学科, 1:java, 2:前端, 3:大数据, 4:Python, 5:Go, 6:嵌入式 |
请求数据样例:
{
"id": 8,
"name": "JavaEE就业166期",
"room": "101",
"beginDate": "2023-06-01",
"endDate": "2024-01-25",
"masterId": 7,
"subject": 1
}
-
响应数据
参数格式:application/json
参数说明:
| 参数名 | 类型 | 是否必须 | 备注 |
| code | number | 必须 | 响应码,1 代表成功,0 代表失败 |
| msg | string | 非必须 | 提示信息 |
| data | object | 非必须 | 返回的数据 |
响应数据样例:
{
"code":1,
"msg":"success",
"data":null
}
/**
* 更新班级信息
*/
@PutMapping
public Result update(@RequestBody Clazz clazz){
log.info("更新班级信息:{}",clazz);
clazzService.update(clazz);
return Result.success();
}
@Override
public void update(Clazz clazz) {
clazz.setUpdateTime(LocalDateTime.now());
clazzMapper.update(clazz);
}
在修改班级的功能中,我们需要通过动态sql进行mapper层中的数据处理,因为我们修改的数据可以是一个,几个或者全部,动态sql可以更好的处理这些情况

<set>标签可以处理最后一个更新语句最后一个逗号的情况,代码如下
<!--动态更新班级信息-->
<update id="update">
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="updateTime != null">
update_time = #{updateTime}
</if>
</set>
where id = #{id}
</update>
注意如果我们需要查询回显,需要写一个查询全部班级的接口,不然无法将所有班级的信息显示在前端
博主因为这个调了一个小时,发现哪里都没问题才想到少写了这个接口
/**
* 查询全部班级
*/
@GetMapping("/list")
public Result findAll(){
List<Clazz> clazzList = clazzService.findAll();
return Result.success(clazzList);
}
@Override
public List<Clazz> findAll() {
return clazzMapper.findAll();
}
@Select("select * from clazz")
List<Clazz> findAll();
-
删除班级信息接口
参照接口文档 班级管理 -> 删除班级
注意:在页面原型中,要求如果该班级下关联的有学生,是不允许删除的,并提示错误信息:"对不起, 该班级下有学生, 不能直接删除"。 (提示:可以通过自定义异常 + 全局异常处理器实现)
-
删除班级
-
基本信息
请求路径:/clazzs/{id}
请求方式:DELETE
接口描述:该接口用于删除班级信息
-
请求参数
参数格式:路径参数
参数说明:
| 参数名 | 类型 | 示例 | 是否必须 | 备注 |
| id | number | 1 | 必须 | 班级的ID |
请求参数样例:/clazzs/5
-
响应数据
参数格式:application/json
参数说明:
| 参数名 | 类型 | 是否必须 | 备注 |
| code | number | 必须 | 响应码,1 代表成功,0 代表失败 |
| msg | string | 非必须 | 提示信息 |
| data | object | 非必须 | 返回的数据 |
响应数据样例:
{
"code":1,
"msg":"success",
"data":null
}
我们还是一步一步来看,从控制层
/**
* 根据id删除班级
*/
@DeleteMapping("/{id}")
public Result delete(@PathVariable Integer id){
log.info("根据id删除班级:{}",id);
clazzService.deleteById(id);
return Result.success();
}
因为需要查询班级下是否有学员,我们在开发的过程中可以先将这一部分留到学员管理开发的时候一起完成,这里因为是已经开发完的完整版本,我就不省略啦
@Override
public void deleteById(Integer id) {
//1. 查询班级下是否有学员
Integer count = studentMapper.countByClazzId(id);
if(count > 0){
throw new BusinessException("班级下有学员, 不能直接删除~");
}
clazzMapper.deleteById(id);
}
然后在这里我们定义了一个 自定义异常 + 全局异常处理器
package com.itheima.exception;
public class BusinessException extends RuntimeException{
public BusinessException(String message) {
super(message);
}
}

继承了RuntimeException并提供了一个接收错误消息的构造方法
将其引入到全局异常处理器中
/**
* 声明异常处理的方法 - BusinessException
*/
@ExceptionHandler
public Result handleBuinessException(BusinessException businessException) {
log.error("服务器异常", businessException);
return Result.error(businessException.getMessage());
}

@Delete("delete from clazz where id = #{id}")
void deleteById(Integer id);
到此,关于班级管理的增删改查功能,我们就已经全部实现了。
!&spm=1001.2101.3001.5002&articleId=147529405&d=1&t=3&u=6d8ed866d20f4531a1bc619c28607d81)
1229

被折叠的 条评论
为什么被折叠?



