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
英文地址,請注明出處:http://en.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;通過重復比較相鄰的元素并交換它們的位置來實現排序。它的名稱來源于較小的元素像氣泡一樣逐漸“浮”到數組的頂端。 工作原理 介紹…