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

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


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

文章目录

  • S01. RMS资源模块
    • E01. 开发房间接口
      • 1. 基础增删改查
      • 2. 下载数据报表
    • E02. 开发学校接口
      • 1. 基础增删改查
      • 2. 下载数据报表
    • E03. 开发资产接口
      • 1. 基础增删改查
      • 2. 下载数据报表
      • 3. 上传资产图片
    • E04. 开发资产申请接口
      • 1. 基础增删改查
      • 2. 下载数据报表

心法:manage 是管理员后台核心项目,负责提供管理员的全部功能。

武技:创建 mc-manage 子项目

  1. 添加三方依赖:包括 common,AOP,MySQL,SpringCache,Redis, Swagger,HibernateValidator,JWT,EasyExcel,MinIO,MyBatis,SpringBootAdmin,PageHelper 和 SpringRetry 等:
<dependencies><!--引入自己的common项目--><dependency><groupId>com.joezhou</groupId><artifactId>mc-common</artifactId><version>1.0-SNAPSHOT</version></dependency><!--spring-boot-starter-aop--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency><!--mysql-connector-j--><dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId><version>${mysql-connector-j.version}</version><scope>runtime</scope></dependency><!--spring-boot-starter-cache--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-cache</artifactId></dependency><!--spring-boot-starter-data-redis--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><!--knife4j-openapi3-jakarta-spring-boot-starter--><dependency><groupId>com.github.xiaoymin</groupId><artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId><version>${knife4j-openapi3-jakarta-spring-boot-starter.version}</version></dependency><!--hibernate-validator--><dependency><groupId>org.hibernate.validator</groupId><artifactId>hibernate-validator</artifactId><version>${hibernate-validator.version}</version></dependency><!--jjwt--><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>${jjwt.version}</version></dependency><!--jaxb-api:jdk8以上使用jjwt时需要--><dependency><groupId>javax.xml.bind</groupId><artifactId>jaxb-api</artifactId><version>${jaxb-api.version}</version></dependency><!--easyexcel--><dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>${easyexcel.version}</version></dependency><!--minio--><dependency><groupId>io.minio</groupId><artifactId>minio</artifactId><version>${minio.version}</version><!--冲突项排除--><exclusions><exclusion><artifactId>jsr305</artifactId><groupId>com.google.code.findbugs</groupId></exclusion><exclusion><artifactId>okio</artifactId><groupId>com.squareup.okio</groupId></exclusion></exclusions></dependency><!--mybatis-spring-boot-starter--><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>${mybatis-spring-boot-starter.version}</version></dependency><!--spring-boot-admin-starter-client--><dependency><groupId>de.codecentric</groupId><artifactId>spring-boot-admin-starter-client</artifactId><version>${spring-boot-admin-starter-client.version}</version></dependency><!--pagehelper-spring-boot-starter--><dependency><groupId>com.github.pagehelper</groupId><artifactId>pagehelper-spring-boot-starter</artifactId><version>${pagehelper-spring-boot-starter.version}</version><!--冲突项排除--><exclusions><exclusion><artifactId>mybatis-spring-boot-starter</artifactId><groupId>org.mybatis.spring.boot</groupId></exclusion></exclusions></dependency><!--okhttp--><dependency><groupId>com.squareup.okhttp3</groupId><artifactId>okhttp</artifactId><version>${okhttp.version}</version></dependency><!--spring-retry--><dependency><groupId>org.springframework.retry</groupId><artifactId>spring-retry</artifactId></dependency>
</dependencies>
  1. 开发主配文件:
server:port: 23101 # 端口号spring:application:name: mc-manage # 项目名称datasource:url: jdbc:mysql://192.168.40.77:3306/myclub?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8username: rootpassword: rootboot:admin:client:instance:service-base-url: http://192.168.40.77:23101 # 客户端地址url: http://192.168.40.77:23102 # 服务端地址username: admin # SpringBootAdmin账号password: admin # SpringBootAdmin密码servlet:multipart:max-file-size: -1 # 文件上传最大限制,-1表示无限制max-request-size: 1GB # 每个文件大小限制cache:cache-names: room,school,assets,dept,emp,role,menu,assets_borrow,direction,club,club_progress,course,student # SpringCache缓存名称列表data:redis:host: 192.168.40.77 # redis服务地址port: 6379 # redis服务端口号management:endpoints:web:exposure:include: "*" # 暴露所有端点endpoint:health:show-details: always # 展示 health 端点的详细信息mybatis:configuration:map-underscore-to-camel-case: true # 下划线转驼峰log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 控制台SQLtype-aliases-package: com.joezhou.entity # 实体类别名包扫描pagehelper:helper-dialect: mysql # 数据库方言reasonable: true # page小于0或大于总页数的时候视为查询首页或尾页page-size-zero: true #size为0时视为全查springdoc:api-docs:enabled: true # 启用SpringDocgroup-configs:- group: v1 # v1分组paths-to-match: /api/v1/** # 分组规则packages-to-scan: com.joezhou.controller # 控制器包扫描knife4j:enable: true # 启用knife4jsetting:language: zh_cn # 中文
  1. 开发启动类:
package com.joezhou;/** @author 周航宇 */
@EnableRetry
@EnableScheduling
@MapperScan("com.joezhou.mapper")
@SpringBootApplication
public class ManageApp {public static void main(String[] args) {SpringApplication.run(ManageApp.class, args);}
}
  1. 开发 SpringDoc 配置类:
package com.joezhou.config;/** @author 周航宇 */
@Configuration
public class SpringDocConfig {private static final String AUTHOR = "JoeZhou";private static final String URL = "http://localhost:23101/index.html";private static final String TITLE = "my-club";private static final String INFO = "MyClub 管理系统是基于 SpringBoot 开发的,旨在提供一个全面而高效的管理平台,该系统目前仅支持内部员工登录,登陆后可以轻松管理俱乐部的房间,学校,资产,部门,员工,角色,菜单,班级,课程,学员等相关数据。该系统使用前后端分离的模式进行开发,数据库使用 MySQL,后端使用经典的 SSM 架构,前端使用 Vue + ElementPlus 的组合。该系统具有良好的可扩展性和稳定性,为俱乐部的管理和运营提供了可靠的支持。";private static final String VERSION = "1.0.0";/** 通用信息Bean */@Beanpublic OpenAPI commonInfo() {return new OpenAPI().info(new Info().title(TITLE).description(INFO).version(VERSION).contact(new Contact().name(AUTHOR).url(URL)));}
}
  1. 访问 SpringDoc 文档页面 http://localhost:23101/doc.html

S01. RMS资源模块

E01. 开发房间接口

1. 基础增删改查

  1. 开发 DTO 实体类:

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

package com.joezhou.dto;/** @author 周航宇 */
@Schema(description = "房间添加DTO")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class RoomInsertDTO 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 = "房间地址不能为空")@Pattern(regexp = MC.Regex.ADDRESS_RE, message = MC.Regex.ADDRESS_RE_MSG)private String address;@Schema(description = "房间描述")@Pattern(regexp = MC.Regex.INFO_RE, message = MC.Regex.INFO_RE_MSG)private String info;@Schema(description = "房间容量")@Min(value = 0, message = "房间容量必须大于0")@NotNull(message = "房间容量不能为空")private Integer capacity;
}

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

package com.joezhou.dto;/** @author 周航宇 */
@Schema(description = "房间修改DTO")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class RoomUpdateDTO 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.ADDRESS_RE, message = MC.Regex.ADDRESS_RE_MSG)private String address;@Schema(description = "房间描述")@Pattern(regexp = MC.Regex.INFO_RE, message = MC.Regex.INFO_RE_MSG)private String info;@Min(value = 0, message = "房间容量必须大于0")@Schema(description = "房间容量")private Integer capacity;
}

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

package com.joezhou.dto;/** @author 周航宇 */
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Schema(description = "按条件分页搜索房间DTO")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class RoomPageDTO extends PageDTO {@Schema(description = "房间名称")@Pattern(regexp = MC.Regex.TITLE_RE, message = MC.Regex.TITLE_RE_MSG)private String title;
}

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

package com.joezhou.vo;/** @author 周航宇 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class RoomVO implements Serializable {/** 主键 */private Long id;/** 房间名称 */private String title;
}
  1. 开发数据层代码:
package com.joezhou.mapper;/** @author 周航宇 */
@Repository
public interface RoomMapper {@Insert("""insert into rms_room (title, address, info, capacity, version, deleted, created, updated)values (#{title}, #{address}, #{info}, #{capacity}, #{version}, #{deleted}, #{created}, #{updated})""")@Options(useGeneratedKeys = true, keyProperty = "id")int insert(Room room);@Select("""select * from rms_room twhere t.id = #{param1} and t.deleted = 0""")Room select(Long id);@Select("""<script>select * from rms_room t<where><if test='title != null'> title like concat('%', #{title}, '%') and </if>t.deleted = 0</where></script>""")List<Room> list(RoomPageDTO dto);@Update("""<script>update rms_room<set><if test='title != null'> title = #{title}, </if><if test='address != null'> address = #{address}, </if><if test='info != null'> info = #{info}, </if><if test='capacity != null'> capacity = #{capacity}, </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(Room room);@Update("""update rms_room set deleted = 1, updated = current_timestampwhere id = #{param1}""")int delete(Long id);@Update("""<script>update rms_room 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 周航宇 */
@Repository  
public interface RoomService {int insert(RoomInsertDTO dto);Room select(Long id);List<RoomVO> list();PageInfo<Room> page(RoomPageDTO dto);int update(RoomUpdateDTO dto);int delete(Long id);int deleteBatch(List<Long> ids);
}
package com.joezhou.service.impl;/** @author 周航宇 */
@Service
@CacheConfig(cacheNames = "room")
public class RoomServiceImpl implements RoomService {@Resourceprivate RoomMapper roomMapper;@CacheEvict(allEntries = true)@Overridepublic int insert(RoomInsertDTO dto) {String info = dto.getInfo();// 拷贝属性Room room = BeanUtil.copyProperties(dto, Room.class);// 设置默认值room.setInfo(ObjectUtil.isNull(info) ? "暂无描述" : info);room.setVersion(0L);room.setDeleted(0);room.setCreated(LocalDateTime.now());room.setUpdated(LocalDateTime.now());// DB添加int result = roomMapper.insert(room);if (result <= 0) {throw new ServerErrorException("DB添加失败");}return result;}@Cacheable(key = "#p0", condition = "#p0 != null", unless = "#result == null")@Overridepublic Room select(Long id) {Room result = roomMapper.select(id);if (ObjectUtil.isNull(result)) {throw new ServerErrorException("记录不存在");}return result;}@Cacheable(key = "#root.methodName", unless = "#result == null")@Overridepublic List<RoomVO> list() {return roomMapper.list(new RoomPageDTO()).stream().map(room -> BeanUtil.copyProperties(room, RoomVO.class)).collect(Collectors.toList());}@Cacheable(key = "#root.methodName + ':' + #p0.toString()",condition = "#p0 != null",unless = "#result == null")@Overridepublic PageInfo<Room> page(RoomPageDTO dto) {PageHelper.startPage(dto.getPageNum(), dto.getPageSize());return new PageInfo<>(roomMapper.list(dto));}@CacheEvict(allEntries = true)@Transactional@Retryable(retryFor = VersionException.class)@Overridepublic int update(RoomUpdateDTO dto) {Room room = roomMapper.select(dto.getId());if(ObjectUtil.isNull(room)){throw new ServerErrorException("记录不存在");}BeanUtil.copyProperties(dto, room);// 设置默认值room.setUpdated(LocalDateTime.now());// DB修改int result = roomMapper.update(room);if (result <= 0) {throw new VersionException("DB修改失败");}return result;}@CacheEvict(allEntries = true)@Overridepublic int delete(Long id) {int result = roomMapper.delete(id);if (result <= 0) {throw new ServerErrorException("DB删除失败");}return result;}@CacheEvict(allEntries = true)@Overridepublic int deleteBatch(List<Long> ids) {int result = roomMapper.deleteBatch(ids);if (result <= 0) {throw new ServerErrorException("DB批删失败");}return result;}
}
  1. 开发控制层代码:
package com.joezhou.controller;/** @author 周航宇 */  
@Tag(name = "房间模块")  
@RestController  
@RequestMapping("/api/v1/room")  
public class RoomController {  @Resourceprivate RoomService roomService;@Operation(summary = "新增 - 单条新增")@PostMapping("insert")public Result<Integer> insert(@RequestBody @Validated RoomInsertDTO dto) {return new Result<>(roomService.insert(dto));}@Operation(summary = "查询 - 单条查询")@GetMapping("select/{id}")public Result<Room> select(@PathVariable("id") Long id) {return new Result<>(roomService.select(id));}@Operation(summary = "查询 - 全部记录")@GetMapping("list")public Result<List<RoomVO>> list() {return new Result<>(roomService.list());}@Operation(summary = "查询 - 分页查询")@GetMapping("page")public Result<PageInfo<Room>> page(@Validated RoomPageDTO dto) {return new Result<>(roomService.page(dto));}@Operation(summary = "修改 - 单条修改")@PutMapping("update")public Result<Integer> update(@RequestBody @Validated RoomUpdateDTO dto) {return new Result<>(roomService.update(dto));}@Operation(summary = "删除 - 单条删除")@DeleteMapping("delete/{id}")public Result<Integer> delete(@PathVariable("id") Long id) {return new Result<>(roomService.delete(id));}@Operation(summary = "删除 - 批量删除")@DeleteMapping("deleteBatch")public Result<Integer> deleteBatch(@RequestParam("ids") List<Long> ids) {return new Result<>(roomService.deleteBatch(ids));}
}

2. 下载数据报表

  1. 开发 DTO 实体类:
package com.joezhou.excel;/** @author 周航宇 */
@ColumnWidth(20)
@HeadStyle(horizontalAlignment = HorizontalAlignmentEnum.LEFT, verticalAlignment = VerticalAlignmentEnum.CENTER)  
@ContentStyle(horizontalAlignment = HorizontalAlignmentEnum.LEFT, verticalAlignment = VerticalAlignmentEnum.CENTER)
@Data
@AllArgsConstructor
@NoArgsConstructor
public class RoomExcel implements Serializable {@ExcelProperty(value = {"房间数据统计表", "房间标题"})private String title;@ExcelProperty(value = {"房间数据统计表", "房间容量(人)"})private Integer capacity;@ColumnWidth(40)@ExcelProperty(value = {"房间数据统计表", "房间地址"})private String address;@ColumnWidth(40)@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<RoomExcel> getExcelData();
package com.joezhou.service.impl;@Override
public List<RoomExcel> getExcelData() {// 获取所有房间数据并转换为Excel格式对象列表(使用Stream简化集合操作)return roomMapper.list(new RoomPageDTO()).stream().map(room -> BeanUtil.copyProperties(room, RoomExcel.class)).collect(Collectors.toList());
}
  1. 开发控制层代码:
package com.joezhou.controller;@Operation(summary = "查询 - 报表打印")
@SneakyThrows
@GetMapping("/excel")
public void excel(HttpServletResponse resp) {EasyExcelUtil.download(resp, "房间统计表", roomService.getExcelData());
}

E02. 开发学校接口

1. 基础增删改查

  1. 开发 DTO 实体类:

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

package com.joezhou.dto;/** @author 周航宇 */
@Schema(description = "学校添加DTO")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class SchoolInsertDTO 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 = "学院名称不能为空")@Pattern(regexp = MC.Regex.TITLE_RE, message = MC.Regex.TITLE_RE_MSG)private String college;@Schema(description = "专业名称")@NotEmpty(message = "专业名称不能为空")@Pattern(regexp = MC.Regex.TITLE_RE, message = MC.Regex.TITLE_RE_MSG)private String major;@Schema(description = "班级名称")@NotEmpty(message = "班级名称不能为空")@Pattern(regexp = MC.Regex.TITLE_RE, message = MC.Regex.TITLE_RE_MSG)private String clazz;@Schema(description = "学校地址")@Pattern(regexp = MC.Regex.ADDRESS_RE, message = MC.Regex.ADDRESS_RE_MSG)private String address;@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 SchoolUpdateDTO 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.TITLE_RE, message = MC.Regex.TITLE_RE_MSG)private String college;@Schema(description = "专业名称")@Pattern(regexp = MC.Regex.TITLE_RE, message = MC.Regex.TITLE_RE_MSG)private String major;@Schema(description = "班级名称")@Pattern(regexp = MC.Regex.TITLE_RE, message = MC.Regex.TITLE_RE_MSG)private String clazz;@Schema(description = "学校地址")@Pattern(regexp = MC.Regex.ADDRESS_RE, message = MC.Regex.ADDRESS_RE_MSG)private String address;@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 SchoolPageDTO extends PageDTO {@Schema(description = "学校名称")@Pattern(regexp = MC.Regex.TITLE_RE, message = MC.Regex.TITLE_RE_MSG)private String title;
}

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

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

2. 下载数据报表

  1. 开发 DTO 实体类:
package com.joezhou.excel;/** @author 周航宇 */
@ColumnWidth(20)
@HeadStyle(horizontalAlignment = HorizontalAlignmentEnum.LEFT, verticalAlignment = VerticalAlignmentEnum.CENTER)  
@ContentStyle(horizontalAlignment = HorizontalAlignmentEnum.LEFT, verticalAlignment = VerticalAlignmentEnum.CENTER)
@Data
@AllArgsConstructor
@NoArgsConstructor
public class SchoolExcel implements Serializable {@ExcelProperty(value = {"学校数据统计表", "学校标题"})private String title;@ExcelProperty(value = {"学校数据统计表", "学院名称"})private String college;@ExcelProperty(value = {"学校数据统计表", "专业名称"})private String major;@ExcelProperty(value = {"学校数据统计表", "班级名称"})private String clazz;@ColumnWidth(40)@ExcelProperty(value = {"学校数据统计表", "学校地址"})private String address;@ColumnWidth(40)@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<SchoolExcel> getExcelData();
package com.joezhou.service.impl;@Override
public List<SchoolExcel> getExcelData() {return schoolMapper.list(new SchoolPageDTO()).stream().map(school -> BeanUtil.copyProperties(school, SchoolExcel.class)).collect(Collectors.toList());
}
  1. 开发控制层代码:
package com.joezhou.controller;@Operation(summary = "查询 - 报表打印")
@SneakyThrows
@GetMapping("/excel")
public void excel(HttpServletResponse resp) {EasyExcelUtil.download(resp, "学校统计表", schoolService.getExcelData());
}

E03. 开发资产接口

1. 基础增删改查

  1. 开发 DTO 实体类:

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

package com.joezhou.dto;/** @author 周航宇 */
@Schema(description = "资产添加DTO")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class AssetsInsertDTO implements Serializable {@Schema(description = "资产名称")@NotEmpty(message = "资产名称不能为空")@Pattern(regexp = MC.Regex.TITLE_RE, message = MC.Regex.TITLE_RE_MSG)private String title;@Schema(description = "资产单价")@NotNull(message = "资产单价不能为空")@DecimalMin(value = "0.01", message = "资产单价不能小于0.01元")private Double price;@Schema(description = "单价单位")@NotEmpty(message = "单价单位不能为空")private String priceUnit;@Schema(description = "总计库存")@NotNull(message = "总计库存不能为空")@Min(value = 0, message = "总计库存不能小于0")private Integer total;@Schema(description = "库存单位")@NotEmpty(message = "库存单位不能为空")private String stockUnit;@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 AssetsUpdateDTO 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 = "资产单价")@DecimalMin(value = "0.01", message = "资产单价不能小于0.01元")private Double price;@Schema(description = "单价单位")private String priceUnit;@Schema(description = "剩余库存")@Min(value = 0, message = "剩余库存不能小于0")private Integer stock;@Schema(description = "总计库存")@Min(value = 0, message = "总计库存不能小于0")private Integer total;@Schema(description = "库存单位")private String stockUnit;@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 AssetsPageDTO extends PageDTO {@Schema(description = "资产名称")@Pattern(regexp = MC.Regex.TITLE_RE, message = MC.Regex.TITLE_RE_MSG)private String title;
}

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

package com.joezhou.vo;/** @author 周航宇 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class AssetsVO implements Serializable {/** 主键 */private Long id;/** 资产名称 */private String title;
}
  1. 开发数据层代码:
package com.joezhou.mapper;/** @author 周航宇 */
@Repository
public interface AssetsMapper {@Insert("""insert into rms_assets (title, picture, price, price_unit, stock, stock_unit, total, info, version, deleted, created, updated)values (#{title}, #{picture}, #{price}, #{priceUnit}, #{stock}, #{stockUnit}, #{total}, #{info}, #{version}, #{deleted}, #{created}, #{updated})""")@Options(useGeneratedKeys = true, keyProperty = "id")int insert(Assets assets);@Select("""select * from rms_assets twhere t.id = #{param1} and t.deleted = 0""")Assets select(Long id);@Select("""<script>select * from rms_assets t<where><if test='title != null'> title like concat('%', #{title}, '%') and </if>t.deleted = 0</where></script>""")List<Assets> list(AssetsPageDTO dto);@Update("""<script>update rms_assets<set><if test='title != null'> title = #{title}, </if><if test='picture != null'> picture = #{picture}, </if><if test='price != null'> price = #{price}, </if><if test='priceUnit != null'> price_unit = #{priceUnit}, </if><if test='stock != null'> stock = #{stock}, </if><if test='stockUnit != null'> stock_unit = #{stockUnit}, </if><if test='total != null'> total = #{total}, </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(Assets assets);@Update("""update rms_assets set deleted = 1, updated = current_timestampwhere id = #{param1}""")int delete(Long id);@Update("""<script>update rms_assets 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 AssetsService {int insert(AssetsInsertDTO dto);Assets select(Long id);List<AssetsVO> list();PageInfo<Assets> page(AssetsPageDTO dto);int update(AssetsUpdateDTO dto);int delete(Long id);int deleteBatch(List<Long> ids);
}
package com.joezhou.service.impl;/** @author 周航宇 */
@Service
@CacheConfig(cacheNames = "assets")
public class AssetsServiceImpl implements AssetsService {@Resourceprivate AssetsMapper assetsMapper;@CacheEvict(allEntries = true)@Overridepublic int insert(AssetsInsertDTO dto) {String info = dto.getInfo();// 拷贝属性Assets assets = BeanUtil.copyProperties(dto, Assets.class);// 设置默认值assets.setPicture(MC.Assets.DEFAULT_ASSETS);assets.setStock(assets.getTotal());assets.setInfo(StrUtil.isBlank(info) ? "暂无描述" : info);assets.setVersion(0L);assets.setDeleted(0);assets.setCreated(LocalDateTime.now());assets.setUpdated(LocalDateTime.now());// DB添加int result = assetsMapper.insert(assets);if (result <= 0) {throw new ServerErrorException("DB添加失败");}return result;}@Cacheable(key = "#p0", condition = "#p0 != null", unless = "#result == null")@Overridepublic Assets select(Long id) {Assets result = assetsMapper.select(id);if (ObjectUtil.isNull(result)) {throw new ServerErrorException("记录不存在");}return result;}@Cacheable(key = "#root.methodName", unless = "#result == null")@Overridepublic List<AssetsVO> list() {return assetsMapper.list(new AssetsPageDTO()).stream().map(assets -> BeanUtil.copyProperties(assets, AssetsVO.class)).collect(Collectors.toList());}@Cacheable(key = "#root.methodName + ':' + #p0.toString()",condition = "#p0 != null",unless = "#result == null")@Overridepublic PageInfo<Assets> page(AssetsPageDTO dto) {PageHelper.startPage(dto.getPageNum(), dto.getPageSize());return new PageInfo<>(assetsMapper.list(dto));}@CacheEvict(allEntries = true)@Transactional@Retryable(retryFor = VersionException.class)@Overridepublic int update(AssetsUpdateDTO dto) {Assets assets = assetsMapper.select(dto.getId());if (ObjectUtil.isNull(assets)) {throw new ServerErrorException("记录不存在");}BeanUtil.copyProperties(dto, assets);// 设置默认值assets.setUpdated(LocalDateTime.now());// DB修改int result = assetsMapper.update(assets);if (result <= 0) {throw new VersionException("DB修改失败");}return result;}@CacheEvict(allEntries = true)@Overridepublic int delete(Long id) {int result = assetsMapper.delete(id);if (result <= 0) {throw new ServerErrorException("DB逻辑删除失败");}return result;}@CacheEvict(allEntries = true)@Overridepublic int deleteBatch(List<Long> ids) {int result = assetsMapper.deleteBatch(ids);if (result <= 0) {throw new ServerErrorException("DB逻辑批删失败");}return result;}
}
  1. 开发控制层代码:
package com.joezhou.controller;/** @author 周航宇 */
@Tag(name = "资产模块")
@RestController
@RequestMapping("/api/v1/assets")
public class AssetsController {@Resourceprivate AssetsService assetsService;@Operation(summary = "新增 - 单条新增")@PostMapping("insert")public Result<Integer> insert(@RequestBody @Validated AssetsInsertDTO dto) {return new Result<>(assetsService.insert(dto));}@Operation(summary = "查询 - 单条查询")@GetMapping("select/{id}")public Result<Assets> select(@PathVariable("id") Long id) {return new Result<>(assetsService.select(id));}@Operation(summary = "查询 - 全部记录")@GetMapping("list")public Result<List<AssetsVO>> list() {return new Result<>(assetsService.list());}@Operation(summary = "查询 - 分页查询")@GetMapping("page")public Result<PageInfo<Assets>> page(@Validated AssetsPageDTO dto) {return new Result<>(assetsService.page(dto));}@Operation(summary = "修改 - 单条修改")@PutMapping("update")public Result<Integer> update(@RequestBody @Validated AssetsUpdateDTO dto) {return new Result<>(assetsService.update(dto));}@Operation(summary = "删除 - 单条删除")@DeleteMapping("delete/{id}")public Result<Integer> delete(@PathVariable("id") Long id) {return new Result<>(assetsService.delete(id));}@Operation(summary = "删除 - 批量删除")@DeleteMapping("deleteBatch")public Result<Integer> deleteBatch(@RequestParam("ids") List<Long> ids) {return new Result<>(assetsService.deleteBatch(ids));}
}

2. 下载数据报表

  1. 开发 DTO 实体类:
package com.joezhou.excel;/** @author 周航宇 */
@ColumnWidth(20)
@HeadStyle(horizontalAlignment = HorizontalAlignmentEnum.LEFT, verticalAlignment = VerticalAlignmentEnum.CENTER)  
@ContentStyle(horizontalAlignment = HorizontalAlignmentEnum.LEFT, verticalAlignment = VerticalAlignmentEnum.CENTER)
@Data
@AllArgsConstructor
@NoArgsConstructor
public class AssetsExcel implements Serializable {@ExcelProperty(value = {"资产数据统计表", "资产标题"})private String title;@ExcelProperty(value = {"资产数据统计表", "资产图片"})private String picture;@ExcelProperty(value = {"资产数据统计表", "资产单价"})private Double price;@ExcelProperty(value = {"资产数据统计表", "单价单位"})private String priceUnit;@ExcelProperty(value = {"资产数据统计表", "剩余库存"})private Integer stock;@ExcelProperty(value = {"资产数据统计表", "总计库存"})private Integer total;@ExcelProperty(value = {"资产数据统计表", "库存单位"})private String stockUnit;@ColumnWidth(40)@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<AssetsExcel> getExcelData();
package com.joezhou.service.impl;@Override
public List<AssetsExcel> getExcelData() {return assetsMapper.list(new AssetsPageDTO()).stream().map(assets -> BeanUtil.copyProperties(assets, AssetsExcel.class)).collect(Collectors.toList());
}
  1. 开发控制层代码:
package com.joezhou.controller;@Operation(summary = "查询 - 报表打印")
@SneakyThrows
@GetMapping("/excel")
public void excel(HttpServletResponse resp) {EasyExcelUtil.download(resp, "资产统计表", assetsService.getExcelData());
}

3. 上传资产图片

  1. 开发业务层代码:
package com.joezhou.service;/*** 上传资产图片** @param newFile 上传资产图片DTO* @param id 资产主键* @return 文件名*/
String uploadPicture(MultipartFile newFile, Long id);
package com.joezhou.service.impl;@Transactional(rollbackFor = RuntimeException.class)
@CacheEvict(allEntries = true)
@Override
public String uploadPicture(MultipartFile newFile, Long id) {// 按主键查询记录Assets assets = assetsMapper.select(id);if (ObjectUtil.isNull(assets)) {throw new ServerErrorException("记录不存在");}// 备份旧文件String oldFile = assets.getPicture();// 生成新文件名String newFileName = MinioUtil.randomFilename(newFile);// DB更新文件名assets.setPicture(newFileName);if (assetsMapper.update(assets) <= 0) {throw new ServerErrorException("DB更新失败");}try {// MinIO删除旧文件(默认文件不删除)if (!MC.Assets.DEFAULT_ASSETS.equals(oldFile)) {MinioUtil.delete(oldFile, MC.MinIO.ASSETS_DIR, MC.MinIO.BUCKET_NAME);}// MinIO上传新文件MinioUtil.upload(newFile, newFileName, MC.MinIO.ASSETS_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("/uploadPicture/{id}")
public Result<String> uploadPicture(@RequestParam("pictureFile") MultipartFile pictureFile,@PathVariable("id") Long id) {return new Result<>(assetsService.uploadPicture(pictureFile, id));
}

E04. 开发资产申请接口

心法:资产申请记录需要关联资产记录和员工记录,所以需要事先对实体类进行改造。

改造如下:

package com.joezhou.entity;/** @author 周航宇 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class AssetsBorrow implements Serializable {.../** 每条资产申请记录对应 1 条资产记录 */private Assets assets;/** 每条资产申请记录对应 1 条员工记录 */private Emp emp;
}

1. 基础增删改查

  1. 开发 DTO 实体类:

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

package com.joezhou.dto;/** @author 周航宇 */
@Schema(description = "资产申请添加DTO")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class AssetsBorrowInsertDTO implements Serializable {@Schema(description = "资产ID")@NotNull(message = "资产ID不能为空")private Long fkAssetsId;@Schema(description = "员工ID")@NotNull(message = "员工ID不能为空")private Long fkEmpId;@Schema(description = "申请数量")@NotNull(message = "申请数量不能为空")@Min(value = 0, message = "申请数量不能小于0")private Integer count;@Schema(description = "申请时间")@NotNull(message = "申请时间不能为空")private LocalDateTime borrowTime;@Schema(description = "预计归还时间")@NotNull(message = "预计归还时间不能为空")private LocalDateTime expectedReturnTime;@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 AssetsBorrowUpdateDTO implements Serializable {@Schema(description = "资产申请ID")@NotNull(message = "资产申请ID不能为空")private Long id;@Schema(description = "资产ID")private Long fkAssetsId;@Schema(description = "员工ID")private Long fkEmpId;@Schema(description = "申请数量")@Min(value = 0, message = "申请数量不能小于0")private Integer count;@Schema(description = "申请时间")private LocalDateTime borrowTime;@Schema(description = "预计归还时间")private LocalDateTime expectedReturnTime;@Schema(description = "实际归还时间")private LocalDateTime returnTime;@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 AssetsBorrowPageDTO extends PageDTO {@Schema(description = "资产ID")private Long fkAssetsId;@Schema(description = "员工ID")private Long fkEmpId;
}
  1. 开发数据层代码:
package com.joezhou.mapper;/** @author 周航宇 */
@Repository
public interface AssetsBorrowMapper {@Insert("""insert into rms_assets_borrow (fk_assets_id, fk_emp_id, count, borrow_time, expected_return_time, return_time, info, version, deleted, created, updated)values (#{fkAssetsId}, #{fkEmpId}, #{count}, #{borrowTime}, #{expectedReturnTime}, #{returnTime}, #{info}, #{version}, #{deleted}, #{created}, #{updated})""")@Options(useGeneratedKeys = true, keyProperty = "id")int insert(AssetsBorrow assetsBorrow);@Results(id = "assetsBorrowResultMap", value = {@Result(property = "id", column = "id", id = true),@Result(property = "fkAssetsId", column = "fk_assets_id"),@Result(property = "fkEmpId", column = "fk_emp_id"),@Result(property = "assets", column = "fk_assets_id", one = @One(select = "com.joezhou.mapper.AssetsMapper.select")),@Result(property = "emp", column = "fk_emp_id", one = @One(select = "com.joezhou.mapper.EmpMapper.select"))})@Select("""select * from rms_assets_borrow twhere t.id = #{param1} and t.deleted = 0""")AssetsBorrow select(Long id);@ResultMap("assetsBorrowResultMap")@Select("""<script>select * from rms_assets_borrow t<where><if test='fkAssetsId != null'> fk_assets_id = #{fkAssetsId} and </if><if test='fkEmpId != null'> fk_emp_id = #{fkEmpId} and </if>t.deleted = 0</where></script>""")List<AssetsBorrow> list(AssetsBorrowPageDTO dto);@Update("""<script>update rms_assets_borrow<set><if test='fkAssetsId != null'> fk_assets_id = #{fkAssetsId}, </if><if test='fkEmpId != null'> fk_emp_id = #{fkEmpId}, </if><if test='count != null'> count = #{count}, </if><if test='borrowTime != null'> borrow_time = #{borrowTime}, </if><if test='expectedReturnTime != null'> expected_return_time = #{expectedReturnTime}, </if><if test='returnTime != null'> return_time = #{returnTime}, </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(AssetsBorrow assetsBorrow);@Update("""update rms_assets_borrow set deleted = 1, updated = current_timestampwhere id = #{param1}""")int delete(Long id);@Update("""<script>update rms_assets_borrow set deleted = 1, updated = current_timestampwhere id in<foreach collection='list' item='e' open='(' close=')' separator=','>${e}</foreach></script>""")int deleteBatch(List<Long> ids);
}

在员工数据层 EmpMapper 中补充如下查询块

@Select("""select * from ums_emp twhere t.id = #{param1} and t.deleted = 0""")
Emp select(Long id);
  1. 开发业务层代码:
package com.joezhou.service;/** @author 周航宇 */
public interface AssetsBorrowService {int insert(AssetsBorrowInsertDTO dto);AssetsBorrow select(Long id);List<AssetsBorrow> list();PageInfo<AssetsBorrow> page(AssetsBorrowPageDTO dto);int update(AssetsBorrowUpdateDTO dto);int delete(Long id);int deleteBatch(List<Long> ids);
}
package com.joezhou.service.impl;/** @author 周航宇 */
@Service
@CacheConfig(cacheNames = "assetsBorrow")
public class AssetsBorrowServiceImpl implements AssetsBorrowService {@Resourceprivate AssetsBorrowMapper assetsBorrowMapper;@CacheEvict(allEntries = true)@Overridepublic int insert(AssetsBorrowInsertDTO dto) {String info = dto.getInfo();// 拷贝属性AssetsBorrow assetsBorrow = BeanUtil.copyProperties(dto, AssetsBorrow.class);// 设置默认值assetsBorrow.setInfo(StrUtil.isBlank(info) ? "暂无描述" : info);assetsBorrow.setVersion(0L);assetsBorrow.setDeleted(0);assetsBorrow.setCreated(LocalDateTime.now());assetsBorrow.setUpdated(LocalDateTime.now());// DB添加int result = assetsBorrowMapper.insert(assetsBorrow);if (result <= 0) {throw new ServerErrorException("DB添加失败");}return result;}@Cacheable(key = "#p0", condition = "#p0 != null", unless = "#result == null")@Overridepublic AssetsBorrow select(Long id) {AssetsBorrow result = assetsBorrowMapper.select(id);if (ObjectUtil.isNull(result)) {throw new ServerErrorException("记录不存在");}return result;}@Cacheable(key = "#root.methodName", unless = "#result == null")@Overridepublic List<AssetsBorrow> list() {return assetsBorrowMapper.list(new AssetsBorrowPageDTO());}@Cacheable(key = "#root.methodName + ':' + #p0.toString()",condition = "#p0 != null",unless = "#result == null")@Overridepublic PageInfo<AssetsBorrow> page(AssetsBorrowPageDTO dto) {PageHelper.startPage(dto.getPageNum(), dto.getPageSize());return new PageInfo<>(assetsBorrowMapper.list(dto));}@CacheEvict(allEntries = true)@Transactional@Retryable(retryFor = VersionException.class)@Overridepublic int update(AssetsBorrowUpdateDTO dto) {AssetsBorrow assetsBorrow = assetsBorrowMapper.select(dto.getId());if (ObjectUtil.isNull(assetsBorrow)) {throw new ServerErrorException("记录不存在");}BeanUtil.copyProperties(dto, assetsBorrow);// 设置默认值assetsBorrow.setUpdated(LocalDateTime.now());// DB修改int result = assetsBorrowMapper.update(assetsBorrow);if (result <= 0) {throw new VersionException("DB修改失败");}return result;}@CacheEvict(allEntries = true)@Overridepublic int delete(Long id) {int result = assetsBorrowMapper.delete(id);if (result <= 0) {throw new ServerErrorException("DB逻辑删除失败");}return result;}@CacheEvict(allEntries = true)@Overridepublic int deleteBatch(List<Long> ids) {int result = assetsBorrowMapper.deleteBatch(ids);if (result <= 0) {throw new ServerErrorException("DB逻辑批删失败");}return result;}
}
  1. 开发控制层代码:
package com.joezhou.controller;/** @author 周航宇 */
@Tag(name = "资产申请模块")
@RestController
@RequestMapping("/api/v1/assetsBorrow")
public class AssetsBorrowController {@Resourceprivate AssetsBorrowService assetsBorrowService;@Operation(summary = "新增 - 单条新增")@PostMapping("insert")public Result<Integer> insert(@RequestBody @Validated AssetsBorrowInsertDTO dto) {return new Result<>(assetsBorrowService.insert(dto));}@Operation(summary = "查询 - 单条查询")@GetMapping("select/{id}")public Result<AssetsBorrow> select(@PathVariable("id") Long id) {return new Result<>(assetsBorrowService.select(id));}@Operation(summary = "查询 - 全部记录")@GetMapping("list")public Result<List<AssetsBorrow>> list() {return new Result<>(assetsBorrowService.list());}@Operation(summary = "查询 - 分页查询")@GetMapping("page")public Result<PageInfo<AssetsBorrow>> page(@Validated AssetsBorrowPageDTO dto) {return new Result<>(assetsBorrowService.page(dto));}@Operation(summary = "修改 - 单条修改")@PutMapping("update")public Result<Integer> update(@RequestBody @Validated AssetsBorrowUpdateDTO dto) {return new Result<>(assetsBorrowService.update(dto));}@Operation(summary = "删除 - 单条删除")@DeleteMapping("delete/{id}")public Result<Integer> delete(@PathVariable("id") Long id) {return new Result<>(assetsBorrowService.delete(id));}@Operation(summary = "删除 - 批量删除")@DeleteMapping("deleteBatch")public Result<Integer> deleteBatch(@RequestParam("ids") List<Long> ids) {return new Result<>(assetsBorrowService.deleteBatch(ids));}
}

2. 下载数据报表

  1. 开发 DTO 实体类:
package com.joezhou.excel;/** @author 周航宇 */
@ColumnWidth(20)
@HeadStyle(horizontalAlignment = HorizontalAlignmentEnum.LEFT, verticalAlignment = VerticalAlignmentEnum.CENTER)  
@ContentStyle(horizontalAlignment = HorizontalAlignmentEnum.LEFT, verticalAlignment = VerticalAlignmentEnum.CENTER)
@Data
@AllArgsConstructor
@NoArgsConstructor
public class AssetsBorrowExcel implements Serializable {@ExcelProperty(value = {"资产申请数据统计表", "资产标题"})private String assetsTitle;@ExcelProperty(value = {"资产申请数据统计表", "员工姓名"})private String empName;@ExcelProperty(value = {"资产申请数据统计表", "申请数量"})private Integer count;@ExcelProperty(value = {"资产申请数据统计表", "申请时间"})@DateTimeFormat("yyyy/MM/dd HH:mm:ss")private LocalDateTime borrowTime;@ExcelProperty(value = {"资产申请数据统计表", "预计归还时间"})@DateTimeFormat("yyyy/MM/dd HH:mm:ss")private LocalDateTime expectedReturnTime;@ExcelProperty(value = {"资产申请数据统计表", "实际归还时间"})@DateTimeFormat("yyyy/MM/dd HH:mm:ss")private LocalDateTime returnTime;@ExcelProperty(value = {"资产申请数据统计表", "是否已归还"})private String isReturn;@ColumnWidth(40)@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<AssetsBorrowExcel> getExcelData();
package com.joezhou.service.impl;@Override
public List<AssetsBorrowExcel> getExcelData() {return assetsBorrowMapper.list(new AssetsBorrowPageDTO()).stream().map(assetsBorrow -> {AssetsBorrowExcel assetsBorrowExcel = new AssetsBorrowExcel();BeanUtil.copyProperties(assetsBorrow, assetsBorrowExcel);if (ObjectUtil.isNotNull(assetsBorrow.getAssets())) {assetsBorrowExcel.setAssetsTitle(assetsBorrow.getAssets().getTitle());}if (ObjectUtil.isNotNull(assetsBorrow.getEmp())) {assetsBorrowExcel.setEmpName(assetsBorrow.getEmp().getRealname());}assetsBorrowExcel.setIsReturn(ObjectUtil.isNotNull(assetsBorrow.getReturnTime()) ? "已归还" : "未归还");return assetsBorrowExcel;}).collect(Collectors.toList());
}
  1. 开发控制层代码:
package com.joezhou.controller;@Operation(summary = "查询 - 报表打印")
@SneakyThrows
@GetMapping("/excel")
public void excel(HttpServletResponse resp) {EasyExcelUtil.download(resp, "资产申请统计表", assetsBorrowService.getExcelData());
}

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


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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/917096.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/917096.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

架构实战——互联网架构模板(“存储层”技术)

目录 一、SQL 二、NoSQL 三、小文件存储 四、大文件存储 本文来源:极客时间vip课程笔记 一、SQL SQL 即我们通常所说的关系数据。前几年 NoSQL 火了一阵子,很多人都理解为 NoSQL 是完全抛弃关系数据,全部采用非关系型数据。但经过几年的试验后,大家发现关系数据不可能完全被…

CentOS7.9在线部署Dify

一、CentOS7.9安装dify 二、检查是否安装dcoker docker --version2.1下载后将安装包上传至服务器对应文件夹下,我选在放在了 /root文件夹下 cd /root2.2 上传至服务器 cd /root #对应目录下tar -xvf docker-26.1.4.tgz # 解压安装包:chmod 755 -R docker # 赋予可执…

深入浅出C语言指针:从数组到函数指针的进阶之路(中)

指针是C语言的灵魂&#xff0c;也是初学者最头疼的知识点。它像一把锋利的刀&#xff0c;用得好能大幅提升代码效率&#xff0c;用不好则会让程序漏洞百出。今天这篇文章&#xff0c;我们从数组与指针的基础关系讲起&#xff0c;一步步揭开指针进阶类型的神秘面纱&#xff0c;最…

java web Cookie处理

java web 设置cookie更改启动端口// Directory tree (5 levels) ├── src\ │ ├── a.txt │ └── com\ │ └── zhang\ │ └── ServletContext\ │ ├── cookie\ │ └── servletContext.java └── web\├─…

机器学习—线性回归

一线性回归线性回归是利用数理统计中回归分析&#xff0c;来确定两种或两种以上变量间相互依赖的定量关系的一种统计分析方法。相关关系&#xff1a;包含因果关系和平行关系因果关系&#xff1a;回归分析【原因引起结果&#xff0c;需要明确自变量和因变量】平行关系&#xff1…

Spring Boot Admin 监控模块笔记-实现全链路追踪

一、概述Spring Boot Admin&#xff08;SBA&#xff09;是一个用于监控和管理 Spring Boot 应用程序的工具。它提供了一个 Web 界面&#xff0c;可以集中管理多个 Spring Boot 应用程序的健康状态、指标、日志、配置等信息。通过 SBA&#xff0c;你可以轻松地监控和管理你的微服…

容器化与Docker核心原理

目录 专栏介绍 作者与平台 您将学到什么&#xff1f; 学习特色 容器化与Docker核心原理 引言&#xff1a;为什么容器化成为云计算时代的基石&#xff1f; 容器化技术全景与Docker核心原理&#xff1a;从概念到实践 文章摘要 1. 引言&#xff1a;为什么容器化成为云计算…

掌握Python三大语句:顺序、条件与循环

PS不好意思各位&#xff0c;由于最近笔者在参加全国大学生电子设计大赛&#xff0c;所以最近会出现停更的情况&#xff0c;望大家谅解&#xff0c;比赛结束后我会加大力度&#xff0c;火速讲Python的知识给大家写完&#x1f396;️&#x1f396;️&#x1f396;️&#x1f396;…

JAVA结合AI

Java 与人工智能&#xff08;AI&#xff09;的结合正经历从技术探索到深度融合的关键阶段。以下从技术生态、应用场景、工具创新、行业实践及未来趋势五个维度展开分析&#xff0c;结合最新技术动态与企业级案例&#xff0c;揭示 Java 在 AI 时代的独特价值与发展路径。一、技术…

本土DevOps平台Gitee如何重塑中国研发团队的工作流

本土DevOps平台Gitee如何重塑中国研发团队的工作流 在数字化转型浪潮席卷各行各业的当下&#xff0c;软件开发效率已成为企业竞争力的核心指标。Gitee DevOps作为专为中国开发团队打造的本土化研发管理平台&#xff0c;正在改变国内技术团队的工作方式。该平台通过从代码管理到…

5G MBS(组播广播服务)深度解析:从标准架构到商用实践

一、MBS技术背景与核心价值 1.1 业务需求驱动 随着超高清视频(4K/8K)、多视角直播、XR元宇宙应用爆发式增长,传统单播传输面临带宽浪费(相同内容重复发送)与拥塞风险(万人并发场景)的双重挑战。5G MBS通过点对多点(PTM)传输实现内容一次发送、多终端接收,频谱效率提…

如何将照片从 realme 手机传输到电脑?

对于 realme 用户来说&#xff0c;将照片传输到电脑可以有多种用途&#xff0c;从释放设备空间到在单独的存储设备上创建备份。这个过程不仅有助于高效管理设备内存&#xff0c;还可以让您利用电脑上强大的照片编辑软件进行高级增强和创意项目。了解如何将照片从 realme 手机传…

Centos 7部署.NET 8网站项目

简介 本文详细介绍了在CentOS 7系统上部署.NET 8网站项目的完整流程&#xff0c;主要内容包括&#xff1a;系统版本更新与检查、PostgreSQL数据库的安装配置&#xff08;含防火墙设置、数据库初始化及远程访问配置&#xff09;、Nginx Web服务的安装与防火墙配置。文章通过分步…

Windows 11下IDEA中使用git突然变得卡慢及解决办法

1. 表象 使用idea的git进行update、commit、push等操作时&#xff0c;极度卡慢。需等待几十秒到几分钟。修改文件后&#xff0c;git刷新也不及时。update命令有时候无法点击。 2.解决方法 停止PC Manager ServiceCtrl shift esc : 打开任务管理器找到服务&#xff1a; 服务中…

MyBatis 的两级缓存机制

现实分布式项目中会不会开启mybatis的二级缓存&#xff1f; 在分布式项目中&#xff0c;是否开启MyBatis的二级缓存需结合具体场景和技术方案综合评估。 以下是关键考量因素&#xff1a; 一、默认二级缓存的局限性 隔离性问题&#xff1a;MyBatis默认的二级缓存基于HashMap实…

分布式原子序列(Distributed Atomic Sequence)

这段内容是关于 Apache Ignite 中的 分布式原子序列&#xff08;Distributed Atomic Sequence&#xff09;&#xff0c;也就是一个分布式 ID 生成器。我们来一步步深入理解它的原理、用途和使用方式。&#x1f539; 一、核心概念&#xff1a;什么是分布式 ID 生成器&#xff1f…

VSCode——插件分享:Markdown PDF

该插件可以将markdown编写内容转成PDF。 ✅ 支持渲染图表、代码高亮、表格等 Markdown 内容 安装 Visual Studio Code安装插件&#xff1a;Markdown PDF 打开扩展商店&#xff0c;搜索 Markdown PDF 并安装 打开你的 .md 文件右键 → 点击 Markdown PDF: Export (pdf)自动生成 …

rust-模块树中引用项的路径

模块树中引用项的路径 为了告诉 Rust 在模块树中如何找到某个项&#xff0c;我们使用路径&#xff0c;就像在文件系统中导航时使用路径一样。要调用一个函数&#xff0c;我们需要知道它的路径。 路径有两种形式&#xff1a; 绝对路径是从 crate 根开始的完整路径&#xff1b…

mac n切换node版本报错Bad CPU type in executable

该node版本仅支持intel芯片&#xff0c;不支持Apple 芯片&#xff08;M1/M2/M3/M4&#xff09;&#xff0c;所以需要下载Rosetta 2 &#xff0c;让node可以在搭载 Apple 芯片的 Mac 上运行。 env: node: Bad CPU type in executable /opt/homebrew/bin/n: line 753: /usr/local…

经典算法之美:冒泡排序的优雅实现

经典算法之美&#xff1a;冒泡排序的优雅实现基本概念工作原理介绍具体实现代码实现总结基本概念 冒泡排序是一种简单的排序算法&#xff0c;通过重复比较相邻的元素并交换它们的位置来实现排序。它的名称来源于较小的元素像气泡一样逐渐“浮”到数组的顶端。 工作原理 介绍…