当前位置: 首页 > news >正文

JP3-3-MyClub后台后端(三)

Java道经 - 项目 - MyClub - 后台后端(三)


传送门:JP3-1-MyClub项目简介
传送门:JP3-2-MyClub公共服务
传送门:JP3-3-MyClub后台后端(一)
传送门:JP3-3-MyClub后台后端(二)
传送门:JP3-3-MyClub后台后端(三)

文章目录

  • S04. CMS班级模块
    • E01. 开发方向接口
      • 1. 基础增删改查
      • 2. 下载数据报表
    • E02. 开发班级接口
      • 1. 基础增删改查
      • 2. 下载数据报表
    • E03. 开发课程接口
      • 1. 基础增删改查
      • 2. 下载数据报表
    • E04. 开发班级进度接口
      • 1. 基础增删改查
      • 2. 下载数据报表
    • E05. 开发学生接口
      • 1. 基础增删改查
      • 2. 下载数据报表
      • 3. 上传学生头像

S04. CMS班级模块

E01. 开发方向接口

1. 基础增删改查

  1. 开发 DTO 实体类:

负责(添加)业务的实体类

package com.joezhou.dto;/** @author 周航宇 */
@Schema(description = "方向添加DTO")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class DirectionInsertDTO implements Serializable {@Schema(description = "方向名称")@NotEmpty(message = "方向名称不能为空")@Pattern(regexp = MC.Regex.TITLE_RE, message = MC.Regex.TITLE_RE_MSG)private String title;@Schema(description = "方向描述")@Pattern(regexp = MC.Regex.INFO_RE, message = MC.Regex.INFO_RE_MSG)private String info;
}

负责(修改)业务的实体类

package com.joezhou.dto;/** @author 周航宇 */
@Schema(description = "方向修改DTO")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class DirectionUpdateDTO implements Serializable {@Schema(description = "主键")@NotNull(message = "主键不能为空")private Long id;@Schema(description = "方向名称")@Pattern(regexp = MC.Regex.TITLE_RE, message = MC.Regex.TITLE_RE_MSG)private String title;@Schema(description = "方向描述")@Pattern(regexp = MC.Regex.INFO_RE, message = MC.Regex.INFO_RE_MSG)private String info;
}

负责(分页)业务的实体类

/**  @author 周航宇 */
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Schema(description = "按条件分页搜索方向DTO")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class DirectionPageDTO extends PageDTO {@Schema(description = "方向名称")private String title;
}

负责(全查)业务的实体类

/** @author 周航宇 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class DirectionVO implements Serializable {/** 主键 */private Long id;/** 方向名称 */private String title;
}
  1. 开发数据层代码:
package com.joezhou.mapper;/** @author 周航宇 */
@Repository
public interface DirectionMapper {@Insert("""insert into cms_direction (title, info, version, deleted, created, updated)values (#{title}, #{info}, #{version}, #{deleted}, #{created}, #{updated})""")@Options(useGeneratedKeys = true, keyProperty = "id")int insert(Direction direction);@Select("""select * from cms_direction t where t.id = #{param1} and t.deleted = 0""")Direction select(Long id);@Select("""<script>select * from cms_direction t<where><if test='title != null'> title like concat('%', #{title}, '%') and </if>t.deleted = 0</where></script>""")List<Direction> list(DirectionPageDTO dto);@Update("""<script>update cms_direction<set><if test='title != null'> title = #{title}, </if><if test='info != null'> info = #{info}, </if><if test='deleted != null'> deleted = #{deleted}, </if><if test='created != null'> created = #{created}, </if><if test='updated != null'> updated = #{updated}, </if>version = version + 1</set>where id = #{id} and deleted = 0 and version = #{version}</script>""")int update(Direction direction);@Update("""update cms_direction set deleted = 1, updated = current_timestamp where id = #{param1}""")int delete(Long id);@Update("""<script>update cms_direction set deleted = 1, updated = current_timestampwhere id in<foreach collection='list' item='e' open='(' close=')' separator=','>${e}</foreach></script>""")int deleteBatch(List<Long> ids);
}
  1. 开发业务层代码:
package com.joezhou.service;/** @author 周航宇 */
public interface DirectionService {int insert(DirectionInsertDTO dto);Direction select(Long id);List<DirectionVO> list();PageInfo<Direction> page(DirectionPageDTO dto);int update(DirectionUpdateDTO dto);int delete(Long id);int deleteBatch(List<Long> ids);
}
package com.joezhou.service.impl;/** @author 周航宇 */
@Service
@CacheConfig(cacheNames = "direction")
public class DirectionServiceImpl implements DirectionService {@Resourceprivate DirectionMapper directionMapper;@CacheEvict(allEntries = true)@Overridepublic int insert(DirectionInsertDTO dto) {String info = dto.getInfo();// 拷贝属性Direction direction = BeanUtil.copyProperties(dto, Direction.class);// 设置默认值direction.setInfo(StrUtil.isBlank(info) ? "暂无描述" : info);direction.setVersion(0L);direction.setDeleted(0);direction.setCreated(LocalDateTime.now());direction.setUpdated(LocalDateTime.now());// DB添加int result = directionMapper.insert(direction);if (result <= 0) {throw new ServerErrorException("DB添加失败");}return result;}@Cacheable(key = "#p0", condition = "#p0 != null", unless = "#result == null")@Overridepublic Direction select(Long id) {Direction result = directionMapper.select(id);if (ObjectUtil.isNull(result)) {throw new ServerErrorException("记录不存在");}return result;}@Cacheable(key = "#root.methodName", unless = "#result == null")@Overridepublic List<DirectionVO> list() {return directionMapper.list(new DirectionPageDTO()).stream().map(direction -> BeanUtil.copyProperties(direction, DirectionVO.class)).collect(Collectors.toList());}@Cacheable(key = "#root.methodName + ':' + #p0.toString()", condition = "#p0 != null", unless = "#result == null")@Overridepublic PageInfo<Direction> page(DirectionPageDTO dto) {PageHelper.startPage(dto.getPageNum(), dto.getPageSize());return new PageInfo<>(directionMapper.list(dto));}@CacheEvict(allEntries = true)@Transactional@Retryable(retryFor = VersionException.class)@Overridepublic int update(DirectionUpdateDTO dto) {Direction direction = directionMapper.select(dto.getId());if (ObjectUtil.isNull(direction)) {throw new ServerErrorException("记录不存在");}BeanUtil.copyProperties(dto, direction);// 设置默认值direction.setUpdated(LocalDateTime.now());// DB修改int result = directionMapper.update(direction);if (result <= 0) {throw new VersionException("DB修改失败");}return result;}@CacheEvict(allEntries = true)@Overridepublic int delete(Long id) {int result = directionMapper.delete(id);if (result <= 0) {throw new ServerErrorException("DB逻辑删除失败");}return result;}@CacheEvict(allEntries = true)@Overridepublic int deleteBatch(List<Long> ids) {int result = directionMapper.deleteBatch(ids);if (result <= 0) {throw new ServerErrorException("DB逻辑批删失败");}return result;}
}
  1. 开发控制层代码:
package com.joezhou.controller;/** @author 周航宇 */
@Tag(name = "方向模块")
@RestController
@RequestMapping("/api/v1/direction")
public class DirectionController {@Resourceprivate DirectionService directionService;@Operation(summary = "新增 - 单条新增")@PostMapping("insert")public Result<Integer> insert(@RequestBody @Validated DirectionInsertDTO dto) {return new Result<>(directionService.insert(dto));}@Operation(summary = "查询 - 单条查询")@GetMapping("select/{id}")public Result<Direction> select(@PathVariable("id") Long id) {return new Result<>(directionService.select(id));}@Operation(summary = "查询 - 全部记录")@GetMapping("list")public Result<List<DirectionVO>> list() {return new Result<>(directionService.list());}@Operation(summary = "查询 - 分页查询")@GetMapping("page")public Result<PageInfo<Direction>> page(@Validated DirectionPageDTO dto) {return new Result<>(directionService.page(dto));}@Operation(summary = "修改 - 单条修改")@PutMapping("update")public Result<Integer> update(@RequestBody @Validated DirectionUpdateDTO dto) {return new Result<>(directionService.update(dto));}@Operation(summary = "删除 - 单条删除")@DeleteMapping("delete/{id}")public Result<Integer> delete(@PathVariable("id") Long id) {return new Result<>(directionService.delete(id));}@Operation(summary = "删除 - 批量删除")@DeleteMapping("deleteBatch")public Result<Integer> deleteBatch(@RequestParam("ids") List<Long> ids) {return new Result<>(directionService.deleteBatch(ids));}
}

2. 下载数据报表

  1. 开发 DTO 实体类:
package com.joezhou.excel;/** @author 周航宇 */
@HeadStyle(horizontalAlignment = HorizontalAlignmentEnum.LEFT, verticalAlignment = VerticalAlignmentEnum.CENTER)
@ContentStyle(horizontalAlignment = HorizontalAlignmentEnum.LEFT, verticalAlignment = VerticalAlignmentEnum.CENTER)
@Data
@AllArgsConstructor
@NoArgsConstructor
public class DirectionExcel implements Serializable {@ExcelProperty(value = {"方向数据统计表", "方向标题"})private String title;@ExcelProperty(value = {"方向数据统计表", "方向描述"})private String info;@ExcelProperty(value = {"方向数据统计表", "首次创建日期"})@DateTimeFormat("yyyy/MM/dd HH:mm:ss")private LocalDateTime created;@ExcelProperty(value = {"方向数据统计表", "最后创建日期"})@DateTimeFormat("yyyy/MM/dd HH:mm:ss")private LocalDateTime updated;
}
  1. 开发业务层代码:
package com.joezhou.service;/*** 导出方向记录的Excel数据** @return 方向记录的Excel数据列表*/
List<DirectionExcel> getExcelData();
package com.joezhou.service.impl;@Override
public List<DirectionExcel> getExcelData() {return directionMapper.list(new DirectionPageDTO()).stream().map(dept -> BeanUtil.copyProperties(dept, DirectionExcel.class)).collect(Collectors.toList());
}
  1. 开发控制层代码:
package com.joezhou.controller;@Operation(summary = "查询 - 报表打印")
@SneakyThrows
@GetMapping("/excel")
public void excel(HttpServletResponse resp) {EasyExcelUtil.download(resp, "方向统计表", directionService.getExcelData());
}

E02. 开发班级接口

心法:班级记录需要关联方向记录,所以需要事先对实体类进行改造。

改造如下:

package com.joezhou.entity;/** @author 周航宇 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Club implements Serializable {.../** 每条班级记录对应 1 条方向记录 */private Direction direction;
}

1. 基础增删改查

  1. 开发 DTO 实体类:

负责(添加)业务的实体类

package com.joezhou.dto;/** @author 周航宇 */
@Schema(description = "班级添加DTO")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ClubInsertDTO implements Serializable {@Schema(description = "班级名称")@NotEmpty(message = "班级名称不能为空")@Pattern(regexp = MC.Regex.TITLE_RE, message = MC.Regex.TITLE_RE_MSG)private String title;@Schema(description = "班级标签")private String tag;@Schema(description = "班级方向ID")@NotNull(message = "班级方向ID不能为空")private Long fkDirectionId;@Schema(description = "开班时间", example = "2023-10-05T12:12:12Z")@NotNull(message = "开班时间不能为空")private LocalDateTime startTime;@Schema(description = "预计结班时间", example = "2023-10-05T12:12:12Z")@NotNull(message = "预计结班时间不能为空")private LocalDateTime expectedEndTime;@Schema(description = "实际结班时间", example = "2023-10-05T12:12:12Z")@NotNull(message = "实际结班时间不能为空")private LocalDateTime endTime;@Schema(description = "班级描述")@Pattern(regexp = MC.Regex.INFO_RE, message = MC.Regex.INFO_RE_MSG)private String info;
}

负责(修改)业务的实体类

package com.joezhou.dto;/** @author 周航宇 */
@Schema(description = "班级修改DTO")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ClubUpdateDTO implements Serializable {@Schema(description = "主键")@NotNull(message = "主键不能为空")private Long id;@Schema(description = "班级名称")@Pattern(regexp = MC.Regex.TITLE_RE, message = MC.Regex.TITLE_RE_MSG)private String title;@Schema(description = "班级标签")private String tag;@Schema(description = "班级方向ID")private Long fkDirectionId;@Schema(description = "开班时间", example = "2023-10-05T12:12:12Z")private LocalDateTime startTime;@Schema(description = "预计结班时间", example = "2023-10-05T12:12:12Z")private LocalDateTime expectedEndTime;@Schema(description = "实际结班时间", example = "2023-10-05T12:12:12Z")private LocalDateTime endTime;@Schema(description = "班级描述")@Pattern(regexp = MC.Regex.INFO_RE, message = MC.Regex.INFO_RE_MSG)private String info;
}

负责(分页)业务的实体类

package com.joezhou.dto;/** @author 周航宇 */
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Schema(description = "按条件分页搜索班级DTO")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ClubPageDTO extends PageDTO {@Schema(description = "班级名称")private String title;@Schema(description = "班级标签")private String tag;@Schema(description = "方向ID")private Long fkDirectionId;
}

负责(全查)业务的实体类

package com.joezhou.vo;/** @author 周航宇 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ClubVO implements Serializable {/** 主键 */private Long id;/** 班级名称 */private String title;
}
  1. 开发数据层代码:
package com.joezhou.mapper;/** @author 周航宇 */
@Repository
public interface ClubMapper {@Insert("""insert into cms_club (title, tag, fk_direction_id, start_time, expected_end_time, end_time, info, version, deleted, created, updated)values (#{title}, #{tag}, #{fkDirectionId}, #{startTime}, #{expectedEndTime}, #{endTime}, #{info}, #{version}, #{deleted}, #{created}, #{updated})""")@Options(useGeneratedKeys = true, keyProperty = "id")int insert(Club club);@Results(id = "clubResultMap", value = {@Result(property = "id", column = "id", id = true),@Result(property = "fkDirectionId", column = "fk_direction_id"),@Result(property = "direction", column = "fk_direction_id", one = @One(select = "com.joezhou.mapper.DirectionMapper.select")),})@Select("""select * from cms_club twhere t.id = #{param1} and t.deleted = 0""")Club select(Long id);@ResultMap("clubResultMap")@Select("""<script>select * from cms_club t<where><if test='title != null'> title like concat('%', #{title}, '%') and </if><if test='tag != null'> tag like concat('%', #{tag}, '%') and </if><if test='fkDirectionId != null'> fk_direction_id = #{fkDirectionId} and </if>t.deleted = 0</where></script>""")List<Club> list(ClubPageDTO dto);@Update("""<script>update cms_club<set><if test='title != null'> title = #{title}, </if><if test='tag != null'> tag = #{tag}, </if><if test='fkDirectionId != null'> fk_direction_id = #{fkDirectionId}, </if><if test='startTime != null'> start_time = #{startTime}, </if><if test='expectedEndTime != null'> expected_end_time = #{expectedEndTime}, </if><if test='endTime != null'> end_time = #{endTime}, </if><if test='info != null'> info = #{info}, </if><if test='deleted != null'> deleted = #{deleted}, </if><if test='created != null'> created = #{created}, </if><if test='updated != null'> updated = #{updated}, </if>version = version + 1</set>where id = #{id} and deleted = 0 and version = #{version}</script>""")int update(Club club);@Update("""update cms_club set deleted = 1, updated = current_timestampwhere id = #{param1}""")int delete(Long id);@Update("""<script>update cms_club set deleted = 1, updated = current_timestampwhere id in<foreach collection='list' item='e' open='(' close=')' separator=','>${e}</foreach></script>""")int deleteBatch(List<Long> ids);
}
  1. 开发业务层代码:
package com.joezhou.service;/** @author 周航宇 */
public interface ClubService {int insert(ClubInsertDTO dto);Club select(Long id);List<ClubVO> list();PageInfo<Club> page(ClubPageDTO dto);int update(ClubUpdateDTO dto);int delete(Long id);int deleteBatch(List<Long> ids);
}
package com.joezhou.service.impl;/** @author 周航宇 */
@Service
@CacheConfig(cacheNames = "club")
public class ClubServiceImpl implements ClubService {@Resourceprivate ClubMapper clubMapper;@CacheEvict(allEntries = true)@Overridepublic int insert(ClubInsertDTO dto) {String info = dto.getInfo();String tag = dto.getTag();// 拷贝属性Club club = BeanUtil.copyProperties(dto, Club.class);// 设置默认值club.setInfo(StrUtil.isBlank(info) ? "暂无描述" : info);club.setTag(StrUtil.isBlank(tag) ? "暂无标签" : tag);club.setVersion(0L);club.setDeleted(0);club.setCreated(LocalDateTime.now());club.setUpdated(LocalDateTime.now());// DB添加int result = clubMapper.insert(club);if (result <= 0) {throw new ServerErrorException("DB添加失败");}return result;}@Cacheable(key = "#p0", condition = "#p0 != null", unless = "#result == null")@Overridepublic Club select(Long id) {Club result = clubMapper.select(id);if (ObjectUtil.isNull(result)) {throw new ServerErrorException("记录不存在");}return result;}@Cacheable(key = "#root.methodName", unless = "#result == null")@Overridepublic List<ClubVO> list() {return clubMapper.list(new ClubPageDTO()).stream().map(club -> BeanUtil.copyProperties(club, ClubVO.class)).collect(Collectors.toList());}@Cacheable(key = "#root.methodName + ':' + #p0.toString()", condition = "#p0 != null", unless = "#result == null")@Overridepublic PageInfo<Club> page(ClubPageDTO dto) {PageHelper.startPage(dto.getPageNum(), dto.getPageSize());return new PageInfo<>(clubMapper.list(dto));}@CacheEvict(allEntries = true)@Transactional@Retryable(retryFor = VersionException.class)@Overridepublic int update(ClubUpdateDTO dto) {Club club = clubMapper.select(dto.getId());if (ObjectUtil.isNull(club)) {throw new ServerErrorException("记录不存在");}BeanUtil.copyProperties(dto, club);// 设置默认值club.setUpdated(LocalDateTime.now());// DB修改int result = clubMapper.update(club);if (result <= 0) {throw new VersionException("DB修改失败");}return result;}@CacheEvict(allEntries = true)@Overridepublic int delete(Long id) {int result = clubMapper.delete(id);if (result <= 0) {throw new ServerErrorException("DB逻辑删除失败");}return result;}@CacheEvict(allEntries = true)@Overridepublic int deleteBatch(List<Long> ids) {int result = clubMapper.deleteBatch(ids);if (result <= 0) {throw new ServerErrorException("DB逻辑批删失败");}return result;}
}
  1. 开发控制层代码:
package com.joezhou.controller;/** @author 周航宇 */
@Tag(name = "班级模块")
@RestController
@RequestMapping("/api/v1/club")
public class ClubController {@Resourceprivate ClubService clubService;@Operation(summary = "新增 - 单条新增")@PostMapping("insert")public Result<Integer> insert(@RequestBody @Validated ClubInsertDTO dto) {return new Result<>(clubService.insert(dto));}@Operation(summary = "查询 - 单条查询")@GetMapping("select/{id}")public Result<Club> select(@PathVariable("id") Long id) {return new Result<>(clubService.select(id));}@Operation(summary = "查询 - 全部记录")@GetMapping("list")public Result<List<ClubVO>> list() {return new Result<>(clubService.list());}@Operation(summary = "查询 - 分页查询")@GetMapping("page")public Result<PageInfo<Club>> page(@Validated ClubPageDTO dto) {return new Result<>(clubService.page(dto));}@Operation(summary = "修改 - 单条修改")@PutMapping("update")public Result<Integer> update(@RequestBody @Validated ClubUpdateDTO dto) {return new Result<>(clubService.update(dto));}@Operation(summary = "删除 - 单条删除")@DeleteMapping("delete/{id}")public Result<Integer> delete(@PathVariable("id") Long id) {return new Result<>(clubService.delete(id));}@Operation(summary = "删除 - 批量删除")@DeleteMapping("deleteBatch")public Result<Integer> deleteBatch(@RequestParam("ids") List<Long> ids) {return new Result<>(clubService.deleteBatch(ids));}
}

2. 下载数据报表

  1. 开发 DTO 实体类:
package com.joezhou.excel;/** @author 周航宇 */
@HeadStyle(horizontalAlignment = HorizontalAlignmentEnum.LEFT, verticalAlignment = VerticalAlignmentEnum.CENTER)
@ContentStyle(horizontalAlignment = HorizontalAlignmentEnum.LEFT, verticalAlignment = VerticalAlignmentEnum.CENTER)
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ClubExcel implements Serializable {@ExcelProperty(value = {"班级数据统计表", "班级标题"})private String title;@ExcelProperty(value = {"班级数据统计表", "班级标签"})private String tag;@ExcelProperty(value = {"班级数据统计表", "班级方向"})private String directionTitle;@ExcelProperty(value = {"班级数据统计表", "开班时间"})@DateTimeFormat("yyyy/MM/dd HH:mm:ss")private LocalDateTime startTime;@ExcelProperty(value = {"班级数据统计表", "预计结班时间"})@DateTimeFormat("yyyy/MM/dd HH:mm:ss")private LocalDateTime expectedEndTime;@ExcelProperty(value = {"班级数据统计表", "实际结班时间"})@DateTimeFormat("yyyy/MM/dd HH:mm:ss")private LocalDateTime endTime;@ExcelProperty(value = {"班级数据统计表", "班级描述"})private String info;@ExcelProperty(value = {"班级数据统计表", "首次创建日期"})@DateTimeFormat("yyyy/MM/dd HH:mm:ss")private LocalDateTime created;@ExcelProperty(value = {"班级数据统计表", "最后创建日期"})@DateTimeFormat("yyyy/MM/dd HH:mm:ss")private LocalDateTime updated;
}
  1. 开发业务层代码:
package com.joezhou.service;/*** 获取班级记录的Excel数据** @return 班级记录的Excel数据列表*/
List<ClubExcel> getExcelData();
package com.joezhou.service.impl;@Override
public List<ClubExcel> getExcelData() {return clubMapper.list(new ClubPageDTO()).stream().map(club -> {ClubExcel clubExcel = BeanUtil.copyProperties(club, ClubExcel.class);if (ObjectUtil.isNotNull(club.getDirection())) {clubExcel.setDirectionTitle(club.getDirection().getTitle());}return clubExcel;}).collect(Collectors.toList());
}
  1. 开发控制层代码:
package com.joezhou.controller;@Operation(summary = "查询 - 报表打印")
@SneakyThrows
@GetMapping("/excel")
public void excel(HttpServletResponse resp) {EasyExcelUtil.download(resp, "班级统计表", clubService.getExcelData());
}

E03. 开发课程接口

1. 基础增删改查

  1. 开发 DTO 实体类:

负责(添加)业务的实体类

package com.joezhou.dto;/** @author 周航宇 */
@Schema(description = "课程添加DTO")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class CourseInsertDTO implements Serializable {@Schema(description = "课程名称")@NotEmpty(message = "课程名称不能为空")@Pattern(regexp = MC.Regex.TITLE_RE, message = MC.Regex.TITLE_RE_MSG)private String title;@Schema(description = "课程分类")@NotEmpty(message = "课程分类不能为空")private String category;@Schema(description = "课程顺序")@NotNull(message = "课程顺序不能为空")@Min(value = 0, message = "课程顺序不能小于0")private Integer idx;@Schema(description = "课程学时")@NotNull(message = "课程学时不能为空")@Min(value = 0, message = "课程学时不能小于0")private Integer hours;@Schema(description = "课程描述")@Pattern(regexp = MC.Regex.INFO_RE, message = MC.Regex.INFO_RE_MSG)private String info;
}

负责(修改)业务的实体类

package com.joezhou.dto;/** @author 周航宇 */
@Schema(description = "课程修改DTO")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class CourseUpdateDTO implements Serializable {@Schema(description = "主键")@NotNull(message = "主键不能为空")private Long id;@Schema(description = "课程名称")@Pattern(regexp = MC.Regex.TITLE_RE, message = MC.Regex.TITLE_RE_MSG)private String title;@Schema(description = "课程分类")private String category;@Schema(description = "课程顺序")@Min(value = 0, message = "课程顺序不能小于0")private Integer idx;@Schema(description = "课程学时")@Min(value = 0, message = "课程学时不能小于0")private Integer hours;@Schema(description = "课程描述")@Pattern(regexp = MC.Regex.INFO_RE, message = MC.Regex.INFO_RE_MSG)private String info;
}

负责(分页)业务的实体类

package com.joezhou.dto;/** @author 周航宇 */
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Schema(description = "按条件分页搜索课程DTO")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class CoursePageDTO extends PageDTO {@Schema(description = "课程名称")private String title;@Schema(description = "课程分类")private String category;
}

负责(全查)业务的实体类

package com.joezhou.vo;/** @author 周航宇 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class CourseVO implements Serializable {/** 主键 */private Long id;/** 课程名称 */private String title;
}
  1. 开发数据层代码:
package com.joezhou.mapper;/** @author 周航宇 */
@Repository
public interface CourseMapper {@Insert("""insert into cms_course (title, category, idx, hours, info, version, deleted, created, updated)values (#{title}, #{category}, #{idx}, #{hours}, #{info}, #{version}, #{deleted}, #{created}, #{updated})""")@Options(useGeneratedKeys = true, keyProperty = "id")int insert(Course course);@Select("""select * from cms_course twhere t.id = #{param1} and t.deleted = 0order by t.idx""")Course select(Long id);@Select("""<script>select * from cms_course t<where><if test='title != null'> title like concat('%', #{title}, '%') and </if><if test='category != null'> category like concat('%', #{category}, '%') and </if>t.deleted = 0</where>order by t.idx</script>""")List<Course> list(CoursePageDTO dto);@Update("""<script>update cms_course<set><if test='title != null'> title = #{title}, </if><if test='category != null'> category = #{category}, </if><if test='idx != null'> idx = #{idx}, </if><if test='hours != null'> hours = #{hours}, </if><if test='info != null'> info = #{info}, </if><if test='deleted != null'> deleted = #{deleted}, </if><if test='created != null'> created = #{created}, </if><if test='updated != null'> updated = #{updated}, </if>version = version + 1</set>where id = #{id} and deleted = 0 and version = #{version}</script>""")int update(Course course);@Update("""update cms_course set deleted = 1, updated = current_timestampwhere id = #{param1}""")int delete(Long id);@Update("""<script>update cms_course set deleted = 1, updated = current_timestampwhere id in<foreach collection='list' item='e' open='(' close=')' separator=','>${e}</foreach></script>""")int deleteBatch(List<Long> ids);
}
  1. 开发业务层代码:
package com.joezhou.service;/** @author 周航宇 */
public interface CourseService {int insert(CourseInsertDTO dto);Course select(Long id);List<CourseVO> list();PageInfo<Course> page(CoursePageDTO dto);int update(CourseUpdateDTO dto);int delete(Long id);int deleteBatch(List<Long> ids);
}
package com.joezhou.service.impl;/** @author 周航宇 */
@Service
@CacheConfig(cacheNames = "course")
public class CourseServiceImpl implements CourseService {@Resourceprivate CourseMapper courseMapper;@CacheEvict(allEntries = true)@Overridepublic int insert(CourseInsertDTO dto) {String info = dto.getInfo();// 拷贝属性Course course = BeanUtil.copyProperties(dto, Course.class);// 设置默认值course.setInfo(StrUtil.isBlank(info) ? "暂无描述" : info);course.setVersion(0L);course.setDeleted(0);course.setCreated(LocalDateTime.now());course.setUpdated(LocalDateTime.now());// DB添加int result = courseMapper.insert(course);if (result <= 0) {throw new ServerErrorException("DB添加失败");}return result;}@Cacheable(key = "#p0", condition = "#p0 != null", unless = "#result == null")@Overridepublic Course select(Long id) {Course result = courseMapper.select(id);if (ObjectUtil.isNull(result)) {throw new ServerErrorException("记录不存在");}return result;}@Cacheable(key = "#root.methodName", unless = "#result == null")@Overridepublic List<CourseVO> list() {return courseMapper.list(new CoursePageDTO()).stream().map(course -> BeanUtil.copyProperties(course, CourseVO.class)).collect(Collectors.toList());}@Cacheable(key = "#root.methodName + ':' + #p0.toString()", condition = "#p0 != null", unless = "#result == null")@Overridepublic PageInfo<Course> page(CoursePageDTO dto) {PageHelper.startPage(dto.getPageNum(), dto.getPageSize());return new PageInfo<>(courseMapper.list(dto));}@CacheEvict(allEntries = true)@Transactional@Retryable(retryFor = VersionException.class)@Overridepublic int update(CourseUpdateDTO dto) {Course course = courseMapper.select(dto.getId());if (ObjectUtil.isNull(course)) {throw new ServerErrorException("记录不存在");}BeanUtil.copyProperties(dto, course);// 设置默认值course.setUpdated(LocalDateTime.now());// DB修改int result = courseMapper.update(course);if (result <= 0) {throw new VersionException("DB修改失败");}return result;}@CacheEvict(allEntries = true)@Overridepublic int delete(Long id) {int result = courseMapper.delete(id);if (result <= 0) {throw new ServerErrorException("DB逻辑删除失败");}return result;}@CacheEvict(allEntries = true)@Overridepublic int deleteBatch(List<Long> ids) {int result = courseMapper.deleteBatch(ids);if (result <= 0) {throw new ServerErrorException("DB逻辑批删失败");}return result;}
}
  1. 开发控制层代码:
package com.joezhou.controller;/** @author 周航宇 */
@Tag(name = "课程模块")
@RestController
@RequestMapping("/api/v1/course")
public class CourseController {@Resourceprivate CourseService courseService;@Operation(summary = "新增 - 单条新增")@PostMapping("insert")public Result<Integer> insert(@RequestBody @Validated CourseInsertDTO dto) {return new Result<>(courseService.insert(dto));}@Operation(summary = "查询 - 单条查询")@GetMapping("select/{id}")public Result<Course> select(@PathVariable("id") Long id) {return new Result<>(courseService.select(id));}@Operation(summary = "查询 - 全部记录")@GetMapping("list")public Result<List<CourseVO>> list() {return new Result<>(courseService.list());}@Operation(summary = "查询 - 分页查询")@GetMapping("page")public Result<PageInfo<Course>> page(@Validated CoursePageDTO dto) {return new Result<>(courseService.page(dto));}@Operation(summary = "修改 - 单条修改")@PutMapping("update")public Result<Integer> update(@RequestBody @Validated CourseUpdateDTO dto) {return new Result<>(courseService.update(dto));}@Operation(summary = "删除 - 单条删除")@DeleteMapping("delete/{id}")public Result<Integer> delete(@PathVariable("id") Long id) {return new Result<>(courseService.delete(id));}@Operation(summary = "删除 - 批量删除")@DeleteMapping("deleteBatch")public Result<Integer> deleteBatch(@RequestParam("ids") List<Long> ids) {return new Result<>(courseService.deleteBatch(ids));}
}

2. 下载数据报表

  1. 开发 DTO 实体类:
package com.joezhou.excel;/** @author 周航宇 */
@HeadStyle(horizontalAlignment = HorizontalAlignmentEnum.LEFT, verticalAlignment = VerticalAlignmentEnum.CENTER)
@ContentStyle(horizontalAlignment = HorizontalAlignmentEnum.LEFT, verticalAlignment = VerticalAlignmentEnum.CENTER)
@Data
@AllArgsConstructor
@NoArgsConstructor
public class CourseExcel implements Serializable {@ExcelProperty(value = {"课程数据统计表", "课程标题"})private String title;@ExcelProperty(value = {"课程数据统计表", "课程分类"})private String category;@ExcelProperty(value = {"课程数据统计表", "课程顺序"})private Integer idx;@ExcelProperty(value = {"课程数据统计表", "课程学时"})private Integer hours;@ExcelProperty(value = {"课程数据统计表", "课程描述"})private String info;@ExcelProperty(value = {"课程数据统计表", "首次创建日期"})@DateTimeFormat("yyyy/MM/dd HH:mm:ss")private LocalDateTime created;@ExcelProperty(value = {"课程数据统计表", "最后创建日期"})@DateTimeFormat("yyyy/MM/dd HH:mm:ss")private LocalDateTime updated;
}
  1. 开发业务层代码:
package com.joezhou.service;/*** 获取课程记录的Excel数据** @return 课程记录的Excel数据列表*/
List<CourseExcel> getExcelData();
package com.joezhou.service.impl;@Override
public List<CourseExcel> getExcelData() {return courseMapper.list(new CoursePageDTO()).stream().map(course -> BeanUtil.copyProperties(course, CourseExcel.class)).collect(Collectors.toList());
}
  1. 开发控制层代码:
package com.joezhou.controller;@Operation(summary = "查询 - 报表打印")
@SneakyThrows
@GetMapping("/excel")
public void excel(HttpServletResponse resp) {EasyExcelUtil.download(resp, "课程统计表", courseService.getExcelData());
}

E04. 开发班级进度接口

心法:班级进度记录需要关联班级,房间,班主任,助理讲师,主讲老师以及课程记录,所以需要事先对实体类进行改造。

改造如下:

package com.joezhou.entity;/** @author 周航宇 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ClubProgress implements Serializable {/** 每条班级进度记录对应 1 条班级记录 */private Club club;/** 每条班级进度记录对应 1 条房间记录 */private Room room;/** 每条班级进度记录对应 1 条员工(班主任)记录 */private Emp manager;/** 每条班级进度记录对应 1 条员工(助理老师)记录 */private Emp assistant;/** 每条班级进度记录对应 1 条员工(主讲老师)记录 */private Emp teacher;/** 每条班级进度记录对应 1 条课程记录 */private Course course;
}

1. 基础增删改查

  1. 开发 DTO 实体类:

负责(添加)业务的实体类

package com.joezhou.dto;/** @author 周航宇 */
@Schema(description = "班级进度添加DTO")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ClubProgressInsertDTO implements Serializable {@Schema(description = "班级进度顺序")@NotNull(message = "班级进度顺序不能为空")private Integer idx;@Schema(description = "班级ID")@NotNull(message = "班级ID不能为空")private Long fkClubId;@Schema(description = "房间ID")@NotNull(message = "房间ID不能为空")private Long fkRoomId;@Schema(description = "班主任ID")@NotNull(message = "班主任ID不能为空")private Long fkManagerId;@Schema(description = "助教老师ID")@NotNull(message = "助教老师ID不能为空")private Long fkAssistantId;@Schema(description = "主讲老师ID")@NotNull(message = "主讲老师ID不能为空")private Long fkTeacherId;@Schema(description = "课程ID")@NotNull(message = "课程ID不能为空")private Long fkCourseId;@Schema(description = "进度开始时间", example = "2023-10-05T12:12:12Z")@NotNull(message = "进度开始时间不能为空")private LocalDateTime startTime;@Schema(description = "预计进度结束时间", example = "2023-10-05T12:12:12Z")@NotNull(message = "预计进度结束时间不能为空")private LocalDateTime expectedEndTime;@Schema(description = "实际进度结束时间", example = "2023-10-05T12:12:12Z")@NotNull(message = "实际进度结束时间不能为空")private LocalDateTime endTime;@Schema(description = "班级进度描述")@Pattern(regexp = MC.Regex.INFO_RE, message = MC.Regex.INFO_RE_MSG)private String info;
}

负责(修改)业务的实体类

package com.joezhou.dto;/** @author 周航宇 */
@Schema(description = "班级进度添加DTO")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ClubProgressUpdateDTO implements Serializable {@Schema(description = "主键")@NotNull(message = "主键不能为空")private Long id;@Schema(description = "班级进度顺序")private Integer idx;@Schema(description = "班级ID")private Long fkClubId;@Schema(description = "房间ID")private Long fkRoomId;@Schema(description = "班主任ID")private Long fkManagerId;@Schema(description = "助教老师ID")private Long fkAssistantId;@Schema(description = "主讲老师ID")private Long fkTeacherId;@Schema(description = "课程ID")private Long fkCourseId;@Schema(description = "进度开始时间", example = "2023-10-05T12:12:12Z")private LocalDateTime startTime;@Schema(description = "预计进度结束时间", example = "2023-10-05T12:12:12Z")private LocalDateTime expectedEndTime;@Schema(description = "实际进度结束时间", example = "2023-10-05T12:12:12Z")private LocalDateTime endTime;@Schema(description = "班级进度描述")@Pattern(regexp = MC.Regex.INFO_RE, message = MC.Regex.INFO_RE_MSG)private String info;
}

负责(分页)业务的实体类

package com.joezhou.dto;/** @author 周航宇 */
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Schema(description = "按条件分页搜索班级进度DTO")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ClubProgressPageDTO extends PageDTO {@Schema(description = "班级ID")private String fkClubId;@Schema(description = "房间ID")private Long fkRoomId;@Schema(description = "班主任ID")private Long fkManagerId;@Schema(description = "助教老师ID")private Long fkAssistantId;@Schema(description = "主讲老师ID")private Long fkTeacherId;@Schema(description = "课程ID")private Long fkCourseId;
}

负责(全查)业务的实体类:班级进度表没有全查的业务场景。

  1. 开发数据层代码:
package com.joezhou.mapper;/** @author 周航宇 */
@Repository
public interface ClubProgressMapper {@Insert("""insert into cms_club_progress (idx, fk_club_id, fk_room_id, fk_manager_id, fk_assistant_id, fk_teacher_id, fk_course_id, start_time, expected_end_time, end_time, info, version, deleted, created, updated)values (#{idx}, #{fkClubId}, #{fkRoomId}, #{fkManagerId}, #{fkAssistantId}, #{fkTeacherId}, #{fkCourseId}, #{startTime}, #{expectedEndTime}, #{endTime}, #{info}, #{version}, #{deleted}, #{created}, #{updated})""")@Options(useGeneratedKeys = true, keyProperty = "id")int insert(ClubProgress clubProgress);@Results(id = "clubProgressResult", value = {@Result(property = "id", column = "id", id = true),@Result(property = "fkClubId", column = "fk_club_id"),@Result(property = "fkRoomId", column = "fk_room_id"),@Result(property = "fkManagerId", column = "fk_manager_id"),@Result(property = "fkAssistantId", column = "fk_assistant_id"),@Result(property = "fkTeacherId", column = "fk_teacher_id"),@Result(property = "fkCourseId", column = "fk_course_id"),@Result(property = "club", column = "fk_club_id", one = @One(select = "com.joezhou.mapper.ClubMapper.select")),@Result(property = "room", column = "fk_room_id", one = @One(select = "com.joezhou.mapper.RoomMapper.select")),@Result(property = "manager", column = "fk_manager_id", one = @One(select = "com.joezhou.mapper.EmpMapper.select")),@Result(property = "assistant", column = "fk_assistant_id", one = @One(select = "com.joezhou.mapper.EmpMapper.select")),@Result(property = "teacher", column = "fk_teacher_id", one = @One(select = "com.joezhou.mapper.EmpMapper.select")),@Result(property = "course", column = "fk_course_id", one = @One(select = "com.joezhou.mapper.CourseMapper.select")),})@Select("""select * from cms_club_progress twhere t.id = #{param1} and t.deleted = 0order by t.idx""")ClubProgress select(Long id);@ResultMap("clubProgressResult")@Select("""<script>select * from cms_club_progress t<where><if test='fkClubId != null'> fk_club_id = #{fkClubId} and </if><if test='fkRoomId != null'> fk_room_id = #{fkRoomId} and </if><if test='fkManagerId != null'> fk_manager_id = #{fkManagerId} and </if><if test='fkAssistantId != null'> fk_assistant_id = #{fkAssistantId} and </if><if test='fkTeacherId != null'> fk_teacher_id = #{fkTeacherId} and </if><if test='fkCourseId != null'> fk_course_id = #{fkCourseId} and </if>t.deleted = 0</where>order by t.idx</script>""")List<ClubProgress> list(ClubProgressPageDTO dto);@Update("""<script>update cms_club_progress<set><if test='idx != null'> title = #{title}, </if><if test='fkClubId != null'> fk_club_id = #{fkClubId}, </if><if test='fkRoomId != null'> fk_room_id = #{fkRoomId}, </if><if test='fkManagerId != null'> fk_manager_id = #{fkManagerId}, </if><if test='fkAssistantId != null'> fk_assistant_id = #{fkAssistantId}, </if><if test='fkTeacherId != null'> fk_teacher_id = #{fkTeacherId}, </if><if test='fkCourseId != null'> fk_course_id = #{fkCourseId}, </if><if test='startTime != null'> start_time = #{startTime}, </if><if test='expectedEndTime != null'> expected_end_time = #{expectedEndTime}, </if><if test='endTime != null'> end_time = #{endTime}, </if><if test='info != null'> info = #{info}, </if><if test='deleted != null'> deleted = #{deleted}, </if><if test='created != null'> created = #{created}, </if><if test='updated != null'> updated = #{updated}, </if>version = version + 1</set>where id = #{id} and deleted = 0 and version = #{version}</script>""")int update(ClubProgress clubProgress);@Update("""update cms_club_progress set deleted = 1, updated = current_timestampwhere id = #{param1}""")int delete(Long id);@Update("""<script>update cms_club_progress set deleted = 1, updated = current_timestampwhere id in<foreach collection='list' item='e' open='(' close=')' separator=','>${e}</foreach></script>""")int deleteBatch(List<Long> ids);
}
  1. 开发业务层代码:
package com.joezhou.service;/** @author 周航宇 */
public interface ClubProgressService {int insert(ClubProgressInsertDTO dto);ClubProgress select(Long id);PageInfo<ClubProgress> page(ClubProgressPageDTO dto);int update(ClubProgressUpdateDTO dto);int delete(Long id);int deleteBatch(List<Long> ids);
}
package com.joezhou.service.impl;/** @author 周航宇 */
@Service
@CacheConfig(cacheNames = "clubProgress")
public class ClubProgressServiceImpl implements ClubProgressService {@Resourceprivate ClubProgressMapper clubProgressMapper;@CacheEvict(allEntries = true)@Overridepublic int insert(ClubProgressInsertDTO dto) {String info = dto.getInfo();// 拷贝属性ClubProgress clubProgress = BeanUtil.copyProperties(dto, ClubProgress.class);// 设置默认值clubProgress.setInfo(StrUtil.isBlank(info) ? "暂无描述" : info);clubProgress.setVersion(0L);clubProgress.setDeleted(0);clubProgress.setCreated(LocalDateTime.now());clubProgress.setUpdated(LocalDateTime.now());// DB添加int result = clubProgressMapper.insert(clubProgress);if (result <= 0) {throw new ServerErrorException("DB添加失败");}return result;}@Cacheable(key = "#p0", condition = "#p0 != null", unless = "#result == null")@Overridepublic ClubProgress select(Long id) {ClubProgress result = clubProgressMapper.select(id);if (ObjectUtil.isNull(result)) {throw new ServerErrorException("记录不存在");}return result;}@Cacheable(key = "#root.methodName + ':' + #p0.toString()", condition = "#p0 != null", unless = "#result == null")@Overridepublic PageInfo<ClubProgress> page(ClubProgressPageDTO dto) {PageHelper.startPage(dto.getPageNum(), dto.getPageSize());return new PageInfo<>(clubProgressMapper.list(dto));}@CacheEvict(allEntries = true)@Transactional@Retryable(retryFor = VersionException.class)@Overridepublic int update(ClubProgressUpdateDTO dto) {ClubProgress clubProgress = clubProgressMapper.select(dto.getId());if (ObjectUtil.isNull(clubProgress)) {throw new ServerErrorException("记录不存在");}BeanUtil.copyProperties(dto, clubProgress);// 设置默认值clubProgress.setUpdated(LocalDateTime.now());// DB修改int result = clubProgressMapper.update(clubProgress);if (result <= 0) {throw new VersionException("DB修改失败");}return result;}@CacheEvict(allEntries = true)@Overridepublic int delete(Long id) {int result = clubProgressMapper.delete(id);if (result <= 0) {throw new ServerErrorException("DB逻辑删除失败");}return result;}@CacheEvict(allEntries = true)@Overridepublic int deleteBatch(List<Long> ids) {int result = clubProgressMapper.deleteBatch(ids);if (result <= 0) {throw new ServerErrorException("DB逻辑批删失败");}return result;}
}
  1. 开发控制层代码:
package com.joezhou.controller;/** @author 周航宇 */
@Tag(name = "班级进度模块")
@RestController
@RequestMapping("/api/v1/clubProgress")
public class ClubProgressController {@Resourceprivate ClubProgressService clubProgressService;@Operation(summary = "新增 - 单条新增")@PostMapping("insert")public Result<Integer> insert(@RequestBody @Validated ClubProgressInsertDTO dto) {return new Result<>(clubProgressService.insert(dto));}@Operation(summary = "查询 - 单条查询")@GetMapping("select/{id}")public Result<ClubProgress> select(@PathVariable("id") Long id) {return new Result<>(clubProgressService.select(id));}@Operation(summary = "查询 - 分页查询")@GetMapping("page")public Result<PageInfo<ClubProgress>> page(@Validated ClubProgressPageDTO dto) {return new Result<>(clubProgressService.page(dto));}@Operation(summary = "修改 - 单条修改")@PutMapping("update")public Result<Integer> update(@RequestBody @Validated ClubProgressUpdateDTO dto) {return new Result<>(clubProgressService.update(dto));}@Operation(summary = "删除 - 单条删除")@DeleteMapping("delete/{id}")public Result<Integer> delete(@PathVariable("id") Long id) {return new Result<>(clubProgressService.delete(id));}@Operation(summary = "删除 - 批量删除")@DeleteMapping("deleteBatch")public Result<Integer> deleteBatch(@RequestParam("ids") List<Long> ids) {return new Result<>(clubProgressService.deleteBatch(ids));}
}

2. 下载数据报表

  1. 开发 DTO 实体类:
package com.joezhou.excel;/** @author 周航宇 */
@HeadStyle(horizontalAlignment = HorizontalAlignmentEnum.LEFT, verticalAlignment = VerticalAlignmentEnum.CENTER)
@ContentStyle(horizontalAlignment = HorizontalAlignmentEnum.LEFT, verticalAlignment = VerticalAlignmentEnum.CENTER)
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ClubProgressExcel implements Serializable {@ExcelProperty(value = {"班级进度数据统计表", "班级进度顺序"})private Integer idx;@ExcelProperty(value = {"班级进度数据统计表", "房间标题"})private String roomTitle;@ExcelProperty(value = {"班级进度数据统计表", "班级标题"})private String clubTitle;@ExcelProperty(value = {"班级进度数据统计表", "课程标题"})private String courseTitle;@ExcelProperty(value = {"班级进度数据统计表", "班主任姓名"})private String managerName;@ExcelProperty(value = {"班级进度数据统计表", "助教姓名"})private String assistantName;@ExcelProperty(value = {"班级进度数据统计表", "主讲老师姓名"})private String teacherName;@ExcelProperty(value = {"班级进度数据统计表", "进度开始时间"})@DateTimeFormat("yyyy/MM/dd HH:mm:ss")private LocalDateTime startTime;@ExcelProperty(value = {"班级进度数据统计表", "预计进度结束时间"})@DateTimeFormat("yyyy/MM/dd HH:mm:ss")private LocalDateTime expectedEndTime;@ExcelProperty(value = {"班级进度数据统计表", "实际进度结束时间"})@DateTimeFormat("yyyy/MM/dd HH:mm:ss")private LocalDateTime endTime;@ExcelProperty(value = {"班级进度数据统计表", "班级进度描述"})private String info;@ExcelProperty(value = {"班级进度数据统计表", "首次创建日期"})@DateTimeFormat("yyyy/MM/dd HH:mm:ss")private LocalDateTime created;@ExcelProperty(value = {"班级进度数据统计表", "最后创建日期"})@DateTimeFormat("yyyy/MM/dd HH:mm:ss")private LocalDateTime updated;
}
  1. 开发业务层代码:
package com.joezhou.service;/*** 获取班级记录的Excel数据** @return 班级记录的Excel数据列表*/
List<ClubProgressExcel> getExcelData();
package com.joezhou.service.impl;@Override
public List<ClubProgressExcel> getExcelData() {return clubProgressMapper.list(new ClubProgressPageDTO()).stream().map(clubProgress -> {ClubProgressExcel clubProgressExcel = BeanUtil.copyProperties(clubProgress, ClubProgressExcel.class);if (ObjectUtil.isNotNull(clubProgress.getClub())) {clubProgressExcel.setClubTitle(clubProgress.getClub().getTitle());}if (ObjectUtil.isNotNull(clubProgress.getRoom())) {clubProgressExcel.setRoomTitle(clubProgress.getRoom().getTitle());}if (ObjectUtil.isNotNull(clubProgress.getCourse())) {clubProgressExcel.setCourseTitle(clubProgress.getCourse().getTitle());}if (ObjectUtil.isNotNull(clubProgress.getManager())) {clubProgressExcel.setManagerName(clubProgress.getManager().getRealname());}if (ObjectUtil.isNotNull(clubProgress.getAssistant())) {clubProgressExcel.setAssistantName(clubProgress.getAssistant().getRealname());}if (ObjectUtil.isNotNull(clubProgress.getTeacher())) {clubProgressExcel.setTeacherName(clubProgress.getTeacher().getRealname());}return clubProgressExcel;}).collect(Collectors.toList());
}
  1. 开发控制层代码:
package com.joezhou.controller;@Operation(summary = "查询 - 报表打印")
@SneakyThrows
@GetMapping("/excel")
public void excel(HttpServletResponse resp) {EasyExcelUtil.download(resp, "班级进度统计表", clubProgressService.getExcelData());
}

E05. 开发学生接口

心法:学生记录需要关联学校,销售人员和班级记录,所以需要事先对实体类进行改造。

改造如下:

package com.joezhou.entity;/** @author 周航宇 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student implements Serializable {.../** 每条学生记录对应 1 条学校记录 */private School school;/** 每条学生记录对应 1 条销售人员记录 */private Emp salesman;/** 每条学生记录对应 1 条班级记录 */private Club club;
}

1. 基础增删改查

  1. 开发 DTO 实体类:

负责(添加)业务的实体类

package com.joezhou.dto;/** @author 周航宇 */
@Schema(description = "学生添加DTO")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class StudentInsertDTO implements Serializable {@Schema(description = "真实姓名")@NotEmpty(message = "真实姓名不能为空")@Pattern(regexp = MC.Regex.REALNAME_RE, message = MC.Regex.REALNAME_RE_MSG)private String realname;@Schema(description = "手机号码")@NotEmpty(message = "手机号码不能为空")@Pattern(regexp = MC.Regex.PHONE_RE, message = MC.Regex.PHONE_RE_MSG)private String phone;@Schema(description = "微信号码")@NotEmpty(message = "微信号码不能为空")private String wechat;@Schema(description = "电子邮箱")@NotEmpty(message = "电子邮箱不能为空")@Pattern(regexp = MC.Regex.EMAIL_RE, message = MC.Regex.EMAIL_RE_MSG)private String email;@Schema(description = "现居住地")@NotEmpty(message = "现居住地不能为空")@Pattern(regexp = MC.Regex.ADDRESS_RE, message = MC.Regex.ADDRESS_RE_MSG)private String address;@Schema(description = "学校ID")@NotNull(message = "学校ID不能为空")private Long fkSchoolId;@Schema(description = "销售ID")@NotNull(message = "销售ID不能为空")private Long fkSalesmanId;@Schema(description = "班级ID")@NotNull(message = "班级ID不能为空")private Long fkClubId;@Schema(description = "身份证号")@Pattern(regexp = MC.Regex.ID_CARD_RE, message = MC.Regex.ID_CARD_RE_MSG)@NotEmpty(message = "身份证号不能为空")private String idcard;@Schema(description = "入学日期", example = "2023-10-05T12:12:12Z")@NotNull(message = "入学日期不能为空")private LocalDateTime studyDate;@Schema(description = "紧急联系人姓名")@NotEmpty(message = "紧急联系人姓名不能为空")@Pattern(regexp = MC.Regex.REALNAME_RE, message = MC.Regex.REALNAME_RE_MSG)private String emergencyName;@Schema(description = "紧急联系人电话")@NotEmpty(message = "紧急联系人电话不能为空")@Pattern(regexp = MC.Regex.PHONE_RE, message = MC.Regex.PHONE_RE_MSG)private String emergencyPhone;@Schema(description = "学生描述")@Pattern(regexp = MC.Regex.INFO_RE, message = MC.Regex.INFO_RE_MSG)private String info;
}

负责(修改)业务的实体类

package com.joezhou.dto;/** @author 周航宇 */
@Schema(description = "学生修改DTO")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class StudentUpdateDTO implements Serializable {@NotNull(message = "主键不能为空")@Schema(description = "主键")private Long id;@Schema(description = "真实姓名")@Pattern(regexp = MC.Regex.REALNAME_RE, message = MC.Regex.REALNAME_RE_MSG)private String realname;@Schema(description = "手机号码")@Pattern(regexp = MC.Regex.PHONE_RE, message = MC.Regex.PHONE_RE_MSG)private String phone;@Schema(description = "学生性别")@Min(value = 0, message = "性别必须为0、1或2")@Max(value = 2, message = "性别必须为0、1或2")private Integer gender;@Schema(description = "学生年龄")@Min(value = 18, message = "年龄不能小于18岁")@Max(value = 60, message = "年龄不能大于60岁")private Integer age;@Schema(description = "所在省份")@Pattern(regexp = MC.Regex.PROVINCE_RE, message = MC.Regex.PROVINCE_RE_MSG)private String province;@Schema(description = "微信号码")private String wechat;@Schema(description = "电子邮箱")@Pattern(regexp = MC.Regex.EMAIL_RE, message = MC.Regex.EMAIL_RE_MSG)private String email;@Schema(description = "现居住地")@Pattern(regexp = MC.Regex.ADDRESS_RE, message = MC.Regex.ADDRESS_RE_MSG)private String address;@Schema(description = "学校ID")private Long fkSchoolId;@Schema(description = "销售ID")private Long fkSalesmanId;@Schema(description = "班级ID")private Long fkClubId;@Schema(description = "身份证号")@Pattern(regexp = MC.Regex.ID_CARD_RE, message = MC.Regex.ID_CARD_RE_MSG)private String idcard;@Schema(description = "入学日期", example = "2023-10-05T12:12:12Z")private LocalDateTime studyDate;@Schema(description = "紧急联系人姓名")@Pattern(regexp = MC.Regex.REALNAME_RE, message = MC.Regex.REALNAME_RE_MSG)private String emergencyName;@Schema(description = "紧急联系人电话")@Pattern(regexp = MC.Regex.PHONE_RE, message = MC.Regex.PHONE_RE_MSG)private String emergencyPhone;@Schema(description = "学生状态")@Min(value = 0, message = "状态必须为0~4")@Max(value = 4, message = "状态必须为0~4")private Integer status;@Schema(description = "学生描述")@Pattern(regexp = MC.Regex.INFO_RE, message = MC.Regex.INFO_RE_MSG)private String info;
}

负责(分页)业务的实体类

package com.joezhou.dto;/** @author 周航宇 */
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Schema(description = "按条件分页搜索学生DTO")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class StudentPageDTO extends PageDTO {@Schema(description = "学生编号")private String number;@Schema(description = "学生姓名")private String realname;@Schema(description = "手机号码")private String phone;@Schema(description = "学校ID")private Long fkSchoolId;@Schema(description = "销售ID")private Long fkSalesmanId;@Schema(description = "班级ID")private Long fkClubId;@Schema(description = "学生状态")private Integer status;
}

负责(全查)业务的实体类

package com.joezhou.vo;/** @author 周航宇 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class StudentVO implements Serializable {/** 主键 */private Long id;/** 学生姓名 */private String realname;/** 学生编号 */private String number;
}
  1. 开发数据层代码:
package com.joezhou.mapper;/** @author 周航宇 */
@Repository
public interface StudentMapper {@Insert("""insert into cms_student (number, realname, avatar, phone, gender, wechat, age, province, address, fk_school_id, fk_salesman_id, fk_club_id, idcard, study_date, emergency_name, emergency_phone, status, info, version, deleted, created, updated)values (#{number}, #{realname}, #{avatar}, #{phone}, #{gender}, #{wechat}, #{age}, #{province}, #{address}, #{fkSchoolId}, #{fkSalesmanId}, #{fkClubId}, #{idcard}, #{studyDate}, #{emergencyName}, #{emergencyPhone}, #{status}, #{info}, #{version}, #{deleted}, #{created}, #{updated})""")@Options(useGeneratedKeys = true, keyProperty = "id")int insert(Student student);@Results(id = "studentResultMap", value = {@Result(property = "id", column = "id", id = true),@Result(property = "fkSchoolId", column = "fk_school_id"),@Result(property = "fkSalesmanId", column = "fk_salesman_id"),@Result(property = "fkClubId", column = "fk_club_id"),@Result(property = "school", column = "fk_school_id", one = @One(select = "com.joezhou.mapper.SchoolMapper.select")),@Result(property = "salesman", column = "fk_salesman_id", one = @One(select = "com.joezhou.mapper.EmpMapper.select")),@Result(property = "club", column = "fk_club_id", one = @One(select = "com.joezhou.mapper.ClubMapper.select")),})@Select("""select * from cms_student twhere t.id = #{param1} and t.deleted = 0""")Student select(Long id);@ResultMap("studentResultMap")@Select("""<script>select * from cms_student t<where><if test='number != null'> number like concat('%', #{number}, '%') and </if><if test='realname != null'> realname like concat('%', #{realname}, '%') and </if><if test='phone != null'> phone = #{phone} and </if><if test='fkSchoolId != null'> fk_school_id = #{fkSchoolId} and </if><if test='fkSalesmanId != null'> fk_salesman_id = #{fkSalesmanId} and </if><if test='fkClubId != null'> fk_club_id = #{fkClubId} and </if><if test='status != null'> status = #{status} and </if>t.deleted = 0</where></script>""")List<Student> list(StudentPageDTO dto);@Update("""<script>update cms_student<set><if test='number != null'> number = #{number}, </if><if test='realname != null'> realname = #{realname}, </if><if test='avatar != null'> avatar = #{avatar}, </if><if test='phone != null'> phone = #{phone}, </if><if test='gender != null'> gender = #{gender}, </if><if test='wechat != null'> wechat = #{wechat}, </if><if test='age != null'> age = #{age}, </if><if test='province != null'> province = #{province}, </if><if test='address != null'> address = #{address}, </if><if test='fkSchoolId != null'> fk_school_id = #{fkSchoolId}, </if><if test='fkSalesmanId != null'> fk_salesman_id = #{fkSalesmanId}, </if><if test='fkClubId != null'> fk_club_id = #{fkClubId}, </if><if test='idcard != null'> idcard = #{idcard}, </if><if test='studyDate != null'> study_date = #{studyDate}, </if><if test='emergencyName != null'> emergency_name = #{emergencyName}, </if><if test='emergencyPhone != null'> emergency_phone = #{emergencyPhone}, </if><if test='status != null'> status = #{status}, </if><if test='info != null'> info = #{info}, </if><if test='deleted != null'> deleted = #{deleted}, </if><if test='created != null'> created = #{created}, </if><if test='updated != null'> updated = #{updated}, </if>version = version + 1</set>where id = #{id} and deleted = 0 and version = #{version}</script>""")int update(Student student);@Update("""update cms_student set deleted = 1, updated = current_timestampwhere id = #{param1}""")int delete(Long id);@Update("""<script>update cms_student set deleted = 1, updated = current_timestampwhere id in<foreach collection='list' item='e' open='(' close=')' separator=','>${e}</foreach></script>""")int deleteBatch(List<Long> ids);
}
  1. 开发业务层代码:
package com.joezhou.service;/** @author 周航宇 */
public interface StudentService {int insert(StudentInsertDTO dto);Student select(Long id);List<StudentVO> list();PageInfo<Student> page(StudentPageDTO dto);int update(StudentUpdateDTO dto);int delete(Long id);int deleteBatch(List<Long> ids);
}
package com.joezhou.service.impl;/** @author 周航宇 */
@Service
@CacheConfig(cacheNames = "student")
public class StudentServiceImpl implements StudentService {@Resourceprivate StudentMapper studentMapper;@Resourceprivate StringRedisTemplate stringRedisTemplate;@CacheEvict(allEntries = true)@Overridepublic int insert(StudentInsertDTO dto) {String info = dto.getInfo();String idcard = dto.getIdcard();// 拷贝属性Student student = BeanUtil.copyProperties(dto, Student.class);// 设置默认值:MC前缀 + 当前年份 + 全局编号student.setNumber("MC" + LocalDateTime.now().getYear() + generateStudentNumber());student.setAvatar(MC.Student.DEFAULT_AVATAR);student.setGender(IdcardUtil.getGenderByIdCard(idcard));student.setAge(IdcardUtil.getAgeByIdCard(idcard));student.setProvince(IdcardUtil.getProvinceByIdCard(idcard));student.setAddress("暂未添加详细住址");student.setStatus(MC.Student.STUDYING);student.setInfo(StrUtil.isBlank(info) ? "暂无描述" : info);student.setVersion(0L);student.setDeleted(0);student.setCreated(LocalDateTime.now());student.setUpdated(LocalDateTime.now());// DB添加int result = studentMapper.insert(student);if (result <= 0) {throw new ServerErrorException("DB添加失败");}return result;}@Cacheable(key = "#p0", condition = "#p0 != null", unless = "#result == null")@Overridepublic Student select(Long id) {Student result = studentMapper.select(id);if (ObjectUtil.isNull(result)) {throw new ServerErrorException("记录不存在");}return result;}@Cacheable(key = "#root.methodName", unless = "#result == null")@Overridepublic List<StudentVO> list() {return studentMapper.list(new StudentPageDTO()).stream().map(student -> BeanUtil.copyProperties(student, StudentVO.class)).collect(Collectors.toList());}@Cacheable(key = "#root.methodName + ':' + #p0.toString()", condition = "#p0 != null", unless = "#result == null")@Overridepublic PageInfo<Student> page(StudentPageDTO dto) {PageHelper.startPage(dto.getPageNum(), dto.getPageSize());return new PageInfo<>(studentMapper.list(dto));}@CacheEvict(allEntries = true)@Transactional@Retryable(retryFor = VersionException.class)@Overridepublic int update(StudentUpdateDTO dto) {Student student = studentMapper.select(dto.getId());if (ObjectUtil.isNull(student)) {throw new ServerErrorException("记录不存在");}BeanUtil.copyProperties(dto, student);// 设置默认值student.setUpdated(LocalDateTime.now());// DB修改int result = studentMapper.update(student);if (result <= 0) {throw new VersionException("DB修改失败");}return result;}@CacheEvict(allEntries = true)@Overridepublic int delete(Long id) {int result = studentMapper.delete(id);if (result <= 0) {throw new ServerErrorException("DB逻辑删除失败");}return result;}@CacheEvict(allEntries = true)@Overridepublic int deleteBatch(List<Long> ids) {int result = studentMapper.deleteBatch(ids);if (result <= 0) {throw new ServerErrorException("DB逻辑批删失败");}return result;}@Overridepublic List<StudentExcel> getExcelData() {return studentMapper.list(new StudentPageDTO()).stream().map(student -> {StudentExcel studentExcel = BeanUtil.copyProperties(student, StudentExcel.class);School school = student.getSchool();if (ObjectUtil.isNotNull(school)) {studentExcel.setSchoolTitle(school.getTitle() + school.getCollege() + school.getMajor() + school.getClazz());}if (ObjectUtil.isNotNull(student.getSalesman())) {studentExcel.setSalesmanName(student.getSalesman().getRealname());}if (ObjectUtil.isNotNull(student.getClub())) {studentExcel.setClubTitle(student.getClub().getTitle());}studentExcel.setGender(MC.Student.genderFormat(student.getGender()));studentExcel.setStatus(MC.Student.statusFormat(student.getStatus()));return studentExcel;}).collect(Collectors.toList());}@Transactional(rollbackFor = RuntimeException.class)@CacheEvict(allEntries = true)@Overridepublic String uploadAvatar(MultipartFile newFile, Long id) {// 按主键查询记录Student student = studentMapper.select(id);if (ObjectUtil.isNull(student)) {throw new ServerErrorException("记录不存在");}// 备份旧文件String oldFile = student.getAvatar();// 生成新文件名String newFileName = MinioUtil.randomFilename(newFile);// DB更新文件名student.setAvatar(newFileName);if (studentMapper.update(student) <= 0) {throw new ServerErrorException("DB更新失败");}try {// MinIO删除旧文件(默认文件不删除)if (!MC.Student.DEFAULT_AVATAR.equals(oldFile)) {MinioUtil.delete(oldFile, MC.MinIO.AVATAR_DIR, MC.MinIO.BUCKET_NAME);}// MinIO上传新文件MinioUtil.upload(newFile, newFileName, MC.MinIO.AVATAR_DIR, MC.MinIO.BUCKET_NAME);} catch (Exception e) {throw new ServerErrorException("MinIO操作失败:" + e.getMessage());}// 返回新文件名return newFileName;}/*** 生成学生编号* @return 学生编号*/private String generateStudentNumber() {final String KEY = "student:globalNumber";String globalNumber = stringRedisTemplate.opsForValue().get(KEY);if (StrUtil.isBlank(globalNumber)) {globalNumber = "00000";}int num = Integer.parseInt(globalNumber) + 1;stringRedisTemplate.opsForValue().set(KEY, String.format("%05d", num));return globalNumber;}
}
  1. 开发控制层代码:
package com.joezhou.controller;/** @author 周航宇 */
@Tag(name = "学生模块")
@RestController
@RequestMapping("/api/v1/student")
public class StudentController {@Resourceprivate StudentService studentService;@Operation(summary = "新增 - 单条新增")@PostMapping("insert")public Result<Integer> insert(@RequestBody @Validated StudentInsertDTO dto) {return new Result<>(studentService.insert(dto));}@Operation(summary = "查询 - 单条查询")@GetMapping("select/{id}")public Result<Student> select(@PathVariable("id") Long id) {return new Result<>(studentService.select(id));}@Operation(summary = "查询 - 全部记录")@GetMapping("list")public Result<List<StudentVO>> list() {return new Result<>(studentService.list());}@Operation(summary = "查询 - 分页查询")@GetMapping("page")public Result<PageInfo<Student>> page(@Validated StudentPageDTO dto) {return new Result<>(studentService.page(dto));}@Operation(summary = "修改 - 单条修改")@PutMapping("update")public Result<Integer> update(@RequestBody @Validated StudentUpdateDTO dto) {return new Result<>(studentService.update(dto));}@Operation(summary = "删除 - 单条删除")@DeleteMapping("delete/{id}")public Result<Integer> delete(@PathVariable("id") Long id) {return new Result<>(studentService.delete(id));}@Operation(summary = "删除 - 批量删除")@DeleteMapping("deleteBatch")public Result<Integer> deleteBatch(@RequestParam("ids") List<Long> ids) {return new Result<>(studentService.deleteBatch(ids));}
}

2. 下载数据报表

  1. 开发 DTO 实体类:
package com.joezhou.excel;/** @author 周航宇 */
@HeadStyle(horizontalAlignment = HorizontalAlignmentEnum.LEFT, verticalAlignment = VerticalAlignmentEnum.CENTER)
@ContentStyle(horizontalAlignment = HorizontalAlignmentEnum.LEFT, verticalAlignment = VerticalAlignmentEnum.CENTER)
@Data
@AllArgsConstructor
@NoArgsConstructor
public class StudentExcel implements Serializable {@ExcelProperty(value = {"学生数据统计表", "学生编号"})private String number;@ExcelProperty(value = {"学生数据统计表", "学生姓名"})private String realname;@ExcelProperty(value = {"学生数据统计表", "手机号码"})private String phone;@ExcelProperty(value = {"学生数据统计表", "学生性别"})private String gender;@ExcelProperty(value = {"学生数据统计表", "微信号码"})private String wechat;@ExcelProperty(value = {"学生数据统计表", "学生年龄"})private Integer age;@ExcelProperty(value = {"学生数据统计表", "籍贯省份"})private String province;@ExcelProperty(value = {"学生数据统计表", "现居住地"})private String address;@ExcelProperty(value = {"学生数据统计表", "所在学校"})private String schoolTitle;@ExcelProperty(value = {"学生数据统计表", "销售姓名"})private String salesmanName;@ExcelProperty(value = {"学生数据统计表", "班级名称"})private String clubTitle;@ExcelProperty(value = {"学生数据统计表", "身份证号"})private String idcard;@ExcelProperty(value = {"学生数据统计表", "入学时间"})@DateTimeFormat("yyyy/MM/dd HH:mm:ss")private LocalDateTime studyDate;@ExcelProperty(value = {"学生数据统计表", "紧急联系人"})private String emergencyName;@ExcelProperty(value = {"学生数据统计表", "紧急联系人手机号"})private String emergencyPhone;@ExcelProperty(value = {"学生数据统计表", "学生状态"})private String status;@ExcelProperty(value = {"学生数据统计表", "学生描述"})private String info;@ExcelProperty(value = {"学生数据统计表", "创建时间"})@DateTimeFormat("yyyy/MM/dd HH:mm:ss")private LocalDateTime created;@ExcelProperty(value = {"学生数据统计表", "修改时间"})@DateTimeFormat("yyyy/MM/dd HH:mm:ss")private LocalDateTime updated;
}
  1. 开发业务层代码:
package com.joezhou.service;/*** 导出学生记录的Excel数据** @return 学生记录的Excel数据列表*/
List<StudentExcel> getExcelData();
package com.joezhou.service.impl;@Override
public List<StudentExcel> getExcelData() {return studentMapper.list(new StudentPageDTO()).stream().map(student -> {StudentExcel studentExcel = BeanUtil.copyProperties(student, StudentExcel.class);School school = student.getSchool();if (ObjectUtil.isNotNull(school)) {studentExcel.setSchoolTitle(school.getTitle() + school.getCollege() + school.getMajor() + school.getClazz());}if (ObjectUtil.isNotNull(student.getSalesman())) {studentExcel.setSalesmanName(student.getSalesman().getRealname());}if (ObjectUtil.isNotNull(student.getClub())) {studentExcel.setClubTitle(student.getClub().getTitle());}studentExcel.setGender(MC.Student.genderFormat(student.getGender()));studentExcel.setStatus(MC.Student.statusFormat(student.getStatus()));return studentExcel;}).collect(Collectors.toList());
}
  1. 开发控制层代码:
package com.joezhou.controller;@Operation(summary = "查询 - 报表打印")
@SneakyThrows
@GetMapping("/excel")
public void excel(HttpServletResponse resp) {EasyExcelUtil.download(resp, "学生统计表", studentService.getExcelData());
}

3. 上传学生头像

  1. 开发业务层代码:
package com.joezhou.service;/*** 上传学生头像** @param newFile 上传学生头像DTO* @param id      学生主键* @return 文件名*/
String uploadAvatar(MultipartFile newFile, Long id);
package com.joezhou.service.impl;@Transactional(rollbackFor = RuntimeException.class)
@CacheEvict(allEntries = true)
@Override
public String uploadAvatar(MultipartFile newFile, Long id) {// 按主键查询记录Student student = studentMapper.select(id);if (ObjectUtil.isNull(student)) {throw new ServerErrorException("记录不存在");}// 备份旧文件String oldFile = student.getAvatar();// 生成新文件名String newFileName = MinioUtil.randomFilename(newFile);// DB更新文件名student.setAvatar(newFileName);if (studentMapper.update(student) <= 0) {throw new ServerErrorException("DB更新失败");}try {// MinIO删除旧文件(默认文件不删除)if (!MC.Student.DEFAULT_AVATAR.equals(oldFile)) {MinioUtil.delete(oldFile, MC.MinIO.AVATAR_DIR, MC.MinIO.BUCKET_NAME);}// MinIO上传新文件MinioUtil.upload(newFile, newFileName, MC.MinIO.AVATAR_DIR, MC.MinIO.BUCKET_NAME);} catch (Exception e) {throw new ServerErrorException("MinIO操作失败:" + e.getMessage());}// 返回新文件名return newFileName;
}
  1. 开发控制层代码:注意上传文件不是 JSON 参数,而是二进制参数,不能使用 @RequestBody 注解:
package com.joezhou.controller;@Operation(summary = "上传 - 学生头像")
@PostMapping("/uploadAvatar/{id}")
public Result<String> uploadAvatar(@RequestParam("avatarFile") MultipartFile avatarFile,@PathVariable("id") Long id) {return new Result<>(studentService.uploadAvatar(avatarFile, id));
}

Java道经 - 项目 - MyClub - 后台后端(三)


传送门:JP3-1-MyClub项目简介
传送门:JP3-2-MyClub公共服务
传送门:JP3-3-MyClub后台后端(一)
传送门:JP3-3-MyClub后台后端(二)
传送门:JP3-3-MyClub后台后端(三)

http://www.lryc.cn/news/608435.html

相关文章:

  • 小迪23-28~31-js简单回顾
  • 解决mac在安装nvm过程中可能遇到的一些问题
  • 小迪23年-22~27——php简单回顾(2)
  • (nice!!!)(LeetCode 每日一题) 2561. 重排水果 (哈希表 + 贪心)
  • 【自动化运维神器Ansible】YAML支持的数据类型详解:构建高效Playbook的基石
  • 译| Netflix内容推荐模型的一些改进方向
  • Tlias案例-登录 退出 打包部署
  • Leetcode 11 java
  • 论文笔记:Bundle Recommendation and Generation with Graph Neural Networks
  • (1-8-1) Java -XML
  • [ LeetCode-----盛最多的水]
  • 如何快速解决PDF解密新方法?
  • SpringBoot启动项目详解
  • 丝杆升降机在物流运输领域有哪些应用场景
  • 大模型Agent记忆的主流技术与优缺点解析
  • 23th Day| 39.组合总和,40.组合总和II,131.分割回文串
  • 数据结构---概念、数据与数据之间的关系(逻辑结构、物理结构)、基本功能、数据结构内容、单向链表(该奶奶、对象、应用)
  • 模型 古德哈特定律(Goodhart’s law)
  • 跨语言AI服务指标收集实战
  • 【深度学习】【三维重建】windows11环境配置PyTorch3d详细教程
  • 智能图书馆管理系统开发实战系列(五):前后端集成 - koffi调用与接口设计
  • WAIC引爆AI,智元机器人收购上纬新材,Geek+上市,157起融资撑起热度|2025年7月人工智能投融资观察 · 极新月报
  • FreeRTOS源码分析一:task启动(RISCV架构)
  • 【图像处理基石】用Python实现基础滤镜效果
  • PCB铜浆塞孔工艺流程
  • 网页操作自动化解决方案:如何用Browser-Use+CPolar提升企业运营效率
  • openwrt下安装istore(基于pve)
  • CCF IVC 2025“汽车安全攻防赛” -- Crypto -- WriteUp
  • ESP2025年6月认证C++八级( 第三部分编程题(2)遍历计数)
  • 线程池的实现