Compare commits

...

3 Commits

Author SHA1 Message Date
yulinling
3e3eded4ab feat:
- 添加分页对象
- 添加用户服务
- 添加用户服务测试
- 添加UserVO
- 修改统一异常处理ApiResponse内容
2025-06-08 23:59:28 +08:00
yulinling
45bda6c962 feat:
- 将.idea添加到gitignore
2025-06-08 22:37:18 +08:00
yulinling
8a6a1edaa8 feat:
- 将mybatis更换为mybatis-plus
- 修改mapper测试类
2025-06-08 22:36:32 +08:00
16 changed files with 319 additions and 63 deletions

1
.gitignore vendored
View File

@ -37,3 +37,4 @@ build/
### Mac OS ###
.DS_Store
/logs/
/.idea/

View File

@ -1,28 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
<data-source source="LOCAL" name="@122.152.201.90" uuid="a3d85aef-f993-4fcd-8b75-c4caadf67022">
<data-source source="LOCAL" name="workflow@122.152.201.90 [2]" uuid="a3d85aef-f993-4fcd-8b75-c4caadf67022">
<driver-ref>mysql.8</driver-ref>
<synchronize>true</synchronize>
<jdbc-driver>com.mysql.cj.jdbc.Driver</jdbc-driver>
<jdbc-url>jdbc:mysql://122.152.201.90:9912</jdbc-url>
<jdbc-additional-properties>
<property name="com.intellij.clouds.kubernetes.db.host.port" />
<property name="com.intellij.clouds.kubernetes.db.enabled" value="false" />
<property name="com.intellij.clouds.kubernetes.db.container.port" />
</jdbc-additional-properties>
<working-dir>$ProjectFileDir$</working-dir>
</data-source>
<data-source source="LOCAL" name="workflow@122.152.201.90" uuid="ef18523f-13a8-4f05-b7a1-136f3abe519b">
<driver-ref>mysql.8</driver-ref>
<synchronize>true</synchronize>
<imported>true</imported>
<remarks>$PROJECT_DIR$/src/main/resources/application.properties</remarks>
<jdbc-driver>com.mysql.cj.jdbc.Driver</jdbc-driver>
<jdbc-url>jdbc:mysql://122.152.201.90:9912/workflow?useUnicode=true&amp;characterEncoding=UTF-8&amp;useSSL=false&amp;autoReconnect=true&amp;failOverReadOnly=false&amp;serverTimezone=GMT</jdbc-url>
<jdbc-url>jdbc:mysql://122.152.201.90:9912/workflow</jdbc-url>
<jdbc-additional-properties>
<property name="com.intellij.clouds.kubernetes.db.host.port" />
<property name="com.intellij.clouds.kubernetes.db.enabled" value="false" />
<property name="com.intellij.clouds.kubernetes.db.resource.type" value="Deployment" />
<property name="com.intellij.clouds.kubernetes.db.container.port" />
</jdbc-additional-properties>
<working-dir>$ProjectFileDir$</working-dir>

View File

@ -1,7 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="SqlDialectMappings">
<file url="file://$PROJECT_DIR$/src/main/java/asia/yulinling/workflow/mapper/UserMapper.java" dialect="GenericSQL" />
<file url="file://$PROJECT_DIR$/src/main/resources/db/data.sql" dialect="MySQL" />
<file url="file://$PROJECT_DIR$/src/main/resources/db/schema.sql" dialect="MySQL" />
<file url="file://$PROJECT_DIR$/src/main/resources/mapper/UserMapper.xml" dialect="MySQL" />
<file url="PROJECT" dialect="MySQL" />
</component>
</project>

15
pom.xml
View File

@ -43,11 +43,6 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.3</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
@ -86,6 +81,16 @@
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
<version>3.5.12</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter-test</artifactId>
<version>3.5.12</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>

View File

@ -1,6 +1,7 @@
package asia.yulinling.workflow;
import lombok.extern.slf4j.Slf4j;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
@ -8,14 +9,15 @@ import org.springframework.context.ConfigurableApplicationContext;
@SpringBootApplication
@Slf4j
@MapperScan("asia.yulinling.workflow.mapper")
public class WorkFlowMain {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(WorkFlowMain.class, args);
int length = context.getBeanDefinitionCount();
log.trace("Number of beans in application context: " + length);
log.debug("Number of beans in application context: " + length);
log.info("Number of beans in application context: " + length);
log.warn("Number of beans in application context: " + length);
log.error("Number of beans in application context: " + length);
log.trace("Number of beans in application context: {}", length);
log.debug("Number of beans in application context: {}", length);
log.info("Number of beans in application context: {}", length);
log.warn("Number of beans in application context: {}", length);
log.error("Number of beans in application context: {}", length);
}
}

View File

@ -4,8 +4,12 @@ import asia.yulinling.workflow.constant.Status;
import asia.yulinling.workflow.exception.JsonException;
import asia.yulinling.workflow.exception.PageException;
import asia.yulinling.workflow.model.ApiResponse;
import asia.yulinling.workflow.model.PageResult;
import asia.yulinling.workflow.model.vo.user.UserVO;
import asia.yulinling.workflow.service.UserService;
import cn.hutool.core.lang.Dict;
import cn.hutool.json.JSONUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
@ -24,7 +28,9 @@ import java.util.Map;
*/
@Slf4j
@RestController
@RequiredArgsConstructor
public class TestController {
private final UserService userService;
/**
* 测试方法 GET
@ -49,13 +55,23 @@ public class TestController {
}
@GetMapping("/json")
public ApiResponse jsonException() {
public ApiResponse<?> jsonException() {
throw new JsonException(Status.UNKNOWN_ERROR);
}
@GetMapping("/page")
public ApiResponse PageException() {
public ApiResponse<?> PageException() {
throw new PageException(Status.UNKNOWN_ERROR);
}
@GetMapping("/testPage")
public ApiResponse<PageResult<?>> testPage() {
PageResult<?> pageResult = new PageResult<>(1, 1, 10, null);
return ApiResponse.ofSuccess(pageResult);
}
@GetMapping("/users")
public ApiResponse<PageResult<UserVO>> usersPage() {
return userService.getUserList();
}
}

View File

@ -30,7 +30,7 @@ public class GlobalExceptionHandler {
*/
@ExceptionHandler(Exception.class)
@ResponseBody
public ApiResponse jsonErrorHandler(JsonException e) {
public ApiResponse<?> jsonErrorHandler(JsonException e) {
log.error(e.getMessage());
return ApiResponse.ofException(e);
}

View File

@ -1,6 +1,7 @@
package asia.yulinling.workflow.mapper;
import asia.yulinling.workflow.model.entity.User;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
@ -18,15 +19,7 @@ import java.util.List;
*/
@Mapper
@Component
public interface UserMapper {
/**
* 查询所有用户
*
* @return 用户列表
*/
@Select("SELECT * FROM orm_user")
List<User> selectAllUser();
public interface UserMapper extends BaseMapper<User> {
/**
* 根据id查询用户

View File

@ -13,7 +13,7 @@ import lombok.Data;
* @since 2025/6/5
*/
@Data
public class ApiResponse {
public class ApiResponse<T> {
/**
* 状态码
*/
@ -55,8 +55,8 @@ public class ApiResponse {
* @param data 返回数据
* @return ApiResponse
*/
public static ApiResponse of(Integer code, String message, Object data) {
return new ApiResponse(code, message, data);
public static <T> ApiResponse<T> of(Integer code, String message, T data) {
return new ApiResponse<>(code, message, data);
}
/**
@ -65,7 +65,7 @@ public class ApiResponse {
* @param data 返回数据
* @return ApiResponse
*/
public static ApiResponse ofSuccess(Object data) {
public static <T> ApiResponse<T> ofSuccess(T data) {
return ofStatus(Status.OK, data);
}
@ -75,8 +75,8 @@ public class ApiResponse {
* @param message 返回内容
* @return ApiResponse
*/
public static ApiResponse ofMessage(String message) {
return ofStatus(Status.OK, message);
public static ApiResponse<Void> ofMessage(String message) {
return of(Status.OK.getCode(), message, null);
}
/**
@ -85,10 +85,9 @@ public class ApiResponse {
* @param status 状态{@link Status}
* @return ApiResponse
*/
public static ApiResponse ofStatus(Status status) {
public static <T> ApiResponse<T> ofStatus(Status status) {
return ofStatus(status, null);
}
/**
* 构造一个有状态且带数据的Api返回
*
@ -96,11 +95,10 @@ public class ApiResponse {
* @param data 返回数据
* @return ApiResponse
*/
public static ApiResponse ofStatus(Status status, Object data) {
public static <T> ApiResponse<T> ofStatus(Status status, T data) {
return of(status.getCode(), status.getMessage(), data);
}
/**
* 构造一个异常且带数据的Api返回
*
@ -109,7 +107,7 @@ public class ApiResponse {
* @return ApiResponse
* @param <T> {@link BaseException} 子类
*/
public static <T extends BaseException> ApiResponse ofException(T t, Object data) {
public static <T extends BaseException> ApiResponse<T> ofException(T t, T data) {
return of(t.getCode(), t.getMessage(), data);
}
/**
@ -119,7 +117,7 @@ public class ApiResponse {
* @return ApiResponse
* @param <T> {@link BaseException} 子类
*/
public static <T extends BaseException> ApiResponse ofException(T t) {
public static <T extends BaseException> ApiResponse<T> ofException(T t) {
return ofException(t, null);
}
}

View File

@ -0,0 +1,80 @@
package asia.yulinling.workflow.model;
import lombok.Data;
import java.util.List;
/**
* 分页返回结果
*
* @author YLL
* @since 2025/6/8
*/
@Data
public class PageResult<T> {
private long total; // 总记录数
private int totalPage; // 总页数
private int currentPage; // 当前页码
private int pageSize; // 每页数量
private List<T> list; // 当前页数据
private boolean hasNext; // 是否有下一页
private boolean hasPrev; // 是否有上一页
private boolean firstPage; // 是否是首页
private boolean lastPage; // 是否是尾页
private int navigatePages; // 导航页码数显示5个页码 [1,2,3,4,5]
private int[] navigatePageNums; // 导航页码数组
private int from; // 当前页第一条数据在总数据中的位置从1开始
private int to; // 当前页最后一条数据在总数据中的位置
// 构造方法可选传参
public PageResult(long total, int currentPage, int pageSize, List<T> list) {
this.total = total;
this.currentPage = currentPage;
this.pageSize = pageSize;
this.list = list;
// 自动计算
this.totalPage = (int) Math.ceil((double) total / pageSize);
this.hasNext = currentPage < totalPage;
this.hasPrev = currentPage > 1;
this.firstPage = currentPage == 1;
this.lastPage = currentPage == totalPage;
this.from = (currentPage - 1) * pageSize + 1;
this.to = Math.min(currentPage * pageSize, (int) total);
// 可选导航页码生成逻辑需要时可扩展
navigatePageNums = calculateNavigatePageNumbers(currentPage, totalPage, navigatePages);
}
private int[] calculateNavigatePageNumbers(int currentPage, int totalPage, int navigatePages) {
int[] navigatePageNums;
int startPage = 1;
int endPage = totalPage;
// 中间页码计算逻辑
if (totalPage > navigatePages) {
int half = navigatePages / 2;
startPage = currentPage - half;
endPage = currentPage + half;
if (startPage < 1) {
startPage = 1;
endPage = navigatePages;
}
if (endPage > totalPage) {
endPage = totalPage;
startPage = endPage - navigatePages + 1;
if (startPage < 1) startPage = 1;
}
}
navigatePageNums = new int[endPage - startPage + 1];
for (int i = 0; i < navigatePageNums.length; i++) {
navigatePageNums[i] = startPage + i;
}
return navigatePageNums;
}
}

View File

@ -1,5 +1,9 @@
package asia.yulinling.workflow.model.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
@ -19,10 +23,12 @@ import java.util.Date;
@NoArgsConstructor
@AllArgsConstructor
@Builder
@TableName("`orm_user`")
public class User {
/**
* 主键id
*/
@TableId(type = IdType.AUTO)
private Long id;
/**
@ -58,20 +64,18 @@ public class User {
/**
* 创建时间
*/
@TableField("create_time")
private Date createTime;
/**
* 更新时间
*/
private Date updateTime;
/**
* 上次登录时间
*/
@TableField("last_login_time")
private Date lastLoginTime;
/**
* 上次更新时间
*/
@TableField("last_update_time")
private Date lastUpdateTime;
}

View File

@ -0,0 +1,34 @@
package asia.yulinling.workflow.model.vo.user;
import lombok.Data;
/**
* <p>
* 用户信息VO
* </p>
*
* @author YLL
* @since 2025/6/8
*/
@Data
public class UserVO {
/**
* 用户名
*/
private String name;
/**
* 邮箱
*/
private String email;
/**
* 手机号
*/
private String phone;
/**
* 状态 -1删除 0警用 1启用
*/
private Integer status;
}

View File

@ -0,0 +1,23 @@
package asia.yulinling.workflow.service;
import asia.yulinling.workflow.model.ApiResponse;
import asia.yulinling.workflow.model.PageResult;
import asia.yulinling.workflow.model.entity.User;
import asia.yulinling.workflow.model.vo.user.UserVO;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
/**
* <p>
* 用户接口
* </p>
*
* @author YLL
* @since 2025/6/8
*/
@Transactional
public interface UserService {
ApiResponse<PageResult<UserVO>> getUserList();
}

View File

@ -0,0 +1,58 @@
package asia.yulinling.workflow.service.impl;
import asia.yulinling.workflow.mapper.UserMapper;
import asia.yulinling.workflow.model.ApiResponse;
import asia.yulinling.workflow.model.PageResult;
import asia.yulinling.workflow.model.entity.User;
import asia.yulinling.workflow.model.vo.user.UserVO;
import asia.yulinling.workflow.service.UserService;
import cn.hutool.core.util.ArrayUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* <p>
* 用户接口
* </p>
*
* @author YLL
* @since 2025/6/8
*/
@Service
@RequiredArgsConstructor
public class UserServiceImpl implements UserService {
/** 注入用户Mapper */
private final UserMapper userMapper;
@Override
public ApiResponse<PageResult<UserVO>> getUserList() {
List<UserVO> userVOList = new ArrayList<>();
List<User> users = userMapper.selectList(null);
if (ArrayUtil.isNotEmpty(users)) {
for (User user : users) {
UserVO userVO = new UserVO();
userVO.setName(user.getName());
userVO.setEmail(user.getEmail());
userVO.setPhone(user.getPhone());
userVO.setStatus(user.getStatus());
userVOList.add(userVO);
}
PageResult<UserVO> pageResult = new PageResult<>(
users.toArray().length, 1, users.toArray().length / 10, userVOList
);
return ApiResponse.ofSuccess(pageResult);
}
return ApiResponse.ofSuccess(null);
}
}

View File

@ -1,20 +1,27 @@
package asia.yulinling.workflow.mapper;
import asia.yulinling.workflow.WorkFlowMainTests;
import asia.yulinling.workflow.model.entity.User;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.util.IdUtil;
import cn.hutool.crypto.SecureUtil;
import com.baomidou.mybatisplus.test.autoconfigure.MybatisPlusTest;
import lombok.extern.slf4j.Slf4j;
import org.junit.Assert;
import org.junit.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import static org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase.Replace.NONE;
/**
* <p>
* 用户Mapper测试
@ -23,21 +30,32 @@ import java.util.List;
* @author yulinling
* @since 2025/6/4
*/
@MybatisPlusTest
@RunWith(SpringRunner.class)
@AutoConfigureTestDatabase(replace = NONE)
@Slf4j
public class UserMapperTest extends WorkFlowMainTests {
public class UserMapperTest {
@Autowired
private UserMapper userMapper;
// @Test
// public void selectAllUser() {
// List<User> users = userMapper.selectAllUser();
// Assert.assertTrue(CollUtil.isNotEmpty(users));
// log.info("users={}", users);
// }
@Test
public void selectAllUser() {
List<User> users = userMapper.selectAllUser();
public void selectAllUser2() {
List<User> users = userMapper.selectList(null);
Assert.assertTrue(CollUtil.isNotEmpty(users));
log.info("users={}", users);
}
@Test
public void selectUserById() {
User user = userMapper.selectUserById(1L);
// User user = userMapper.selectUserById(1L);
User user = userMapper.selectById(1);
Assert.assertNotNull(user);
log.info("user={}", user);
}
@ -59,7 +77,8 @@ public class UserMapperTest extends WorkFlowMainTests {
.lastUpdateTime(new DateTime())
.build();
int i = userMapper.saveUser(user);
// int i = userMapper.saveUser(user);
int i = userMapper.insert(user);
Assert.assertEquals(1, i);
}
@ -67,7 +86,8 @@ public class UserMapperTest extends WorkFlowMainTests {
@Transactional
@Rollback
public void deleteById() {
int i = userMapper.deleteById(1L);
// int i = userMapper.deleteById(1L);
int i = userMapper.deleteById(1);
Assert.assertEquals(1, i);
}
}

View File

@ -0,0 +1,32 @@
package asia.yulinling.workflow.service;
import asia.yulinling.workflow.WorkFlowMainTests;
import asia.yulinling.workflow.model.ApiResponse;
import asia.yulinling.workflow.model.PageResult;
import asia.yulinling.workflow.model.entity.User;
import asia.yulinling.workflow.model.vo.user.UserVO;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.List;
/**
* <p>
* 用户测试类
* </p>
*
* @author YLL
* @since 2025/6/8
*/
public class UserServiceTest extends WorkFlowMainTests {
@Autowired
private UserService userService;
@Test
public void getUserList() {
ApiResponse<PageResult<UserVO>> users =userService.getUserList();
Assert.assertEquals(200,users.getCode().intValue());
}
}