https://xie.infoq.cn/article/1182b71d5ad01137ab290eaa1
【Mock 平台】为系列测试开发教程,从 0 到 1 编码带你一步步使用 Spring Boot 和 Antd React 框架完成搭建一个测试工具平台,希望作为一个实战项目对各位的测试开发学习之路有帮助,欢迎关注《大奇测试开发》公众号、博客等原创渠道获取最佳阅读,大奇一个专注测试技术干货原创与分享的家伙。
概要引导
从这篇要开始和大家分享需求实现了,项目管理是基础功能也是比较简单的结构,我们先从简单入手,多花几篇把前后端用到的讲到说明白,夯牢基石才能在后边游刃有余,这里还是要强调一点,此系列是要求对 JAVA 有基础,以及了解些前端的一些知识,否则跟下来还是有一定难度的,但如果读者比较多想先学些基础,可以留言根据人数看看是否需要开个 JAVA 基础系列。
回归 Mock 需求开发,之前的 PRD 文档给出了项目管理的需求说明,贴下原型图,具体参考头篇文章《Mock01-开篇 平台原型和需求说明》本节重点后端接口实现,有大量源代码,强烈建议使用电脑浏览学习。
1.需求实现
1.1 项目管理表
首先设计所需的项目管理表,表名定义为 **mock_project**
结构如下表,需要在qmock
数据库中将数据表创建好。
1.2 后端接口
从项目管理需求上来讲,后端暂需要实现如下接口
-
条件查询接口,返回项目信息列表
-
项目新增和修改接口,此处合并成一个保存接口
按照之前一篇 springboot resful api 的实现步骤,来回顾和优化顺序
-
创建数据库对应的 Entity 字段实体类
-
创建对应接口请求 request 实体类,按需或者共用 entity
-
创建 Mapper 数据库接口类,并在此类中实现 SQL 语句,或者通过 xml 映射
-
创建 Service 服务接口及实现类,做逻辑业务逻辑处理
-
创建 Controller 接口类,实现接口定定义
给出一张截图,主观的先展示下后端新增的代码(红色),修改的部分文件(蓝色)文件。
接下来一次给出实现的代码,其中有新的内容我会给出必要的讲解
Part1 对应数据库表的实体类 MockProjectEntity
创建修改人和日期基础字段集成 BaseEntity,其中字段对应的 getter 和 setter 使用Lombok
实现。
import lombok.Data;
@Data
public class MockProjectEntity extends BaseEntity{
private Integer id;
private String name;
private String type;
private String desc;
private String owner;
}
复制代码
Part2 项目的保存操作需要一个请求 body 实体类 MockProjectRequest
,其中时间信息不需要,创建人修改统一通过operator
透传。
@Data
public class MockProjectRequest {
private Integer id;
@NotBlank(message = "项目名称不能为空")
private String name;
private String desc;
private String owner;
private String type;
private String operator;
}
复制代码
Part3 编写数据操作接口类 MockProjectMapper ,直接使用 mybatis 中的注解方式进行查询、插入和修改语句操作,使用这些注解需要导入 import org.apache.ibatis.annotations.*;
为了实现前端一些演示交互和服务类的必要逻辑,数据操作分别实现如下几个方法
-
selectMockProject 全量查询方法,上一篇讲过自动下划线自动转驼峰,这里从上 Entity 和 Request 实体类看到,并不符合这个规则,所以新的注解**@Results**
作用可以实现数据表字段和自定义实体类字段匹配。
-
searchMockProject 根据名称模糊查询方法
-
insertProject 项目插入方法
-
updateProject 项目根据 id 修改方法
除了截图中看到的方法其他代码实现如下
* @param name
* @return List<MockProjectEntity>
* @desc 数据库项目表,根据id查询project详细信息
*/
@Select("SELECT * FROM mock_project WHERE mp_name LIKE CONCAT(CONCAT('%',#{name},'%'))")
@ResultMap("projectMap")
List<MockProjectEntity> searchMockProject(String name);
* @param projectEntity
* @return 影响数量 插入成功默认1
* @desc 项目插入SQL
*/
@Insert({"INSERT INTO mock_project (mp_name,mp_desc,mp_type,mp_owner,mp_create_user,mp_create_date) VALUES ( #{name},#{desc},#{type},#{owner},#{createUser},NOW())"})
@ResultMap("projectMap")
int insertProject(MockProjectEntity projectEntity);
* 项目修改SQL
* @param projectEntity
* @return 影响数量 更新成功默认1
*/
@Update({"UPDATE mock_project SET mp_name=#{name},mp_desc=#{desc},mp_type=#{type},mp_owner=#{owner},mp_update_user=#{updateUser},mp_update_date=NOW() WHERE mp_id=#{id}"})
@ResultMap("projectMap")
int updateProject(MockProjectEntity projectEntity);
复制代码
扩展说明:@Results 无需每个方法重复添加,一般只需要一个方法上定好 id 名,其他方法上引用即可,关于内部 value Result 的更多属性定义,可以在 IDE 中点击跳转查看
* The annotation that specify a mapping definition for the property.
*
* @see Results
* @author Clinton Begin
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Repeatable(Results.class)
public @interface Result {
* Returns whether id column or not.
* @return {@code true} if id column; {@code false} if otherwise
*/
boolean id() default false;
* Return the column name(or column label) to map to this argument.
* @return the column name(or column label)
*/
String column() default "";
* Returns the property name for applying this mapping.
* @return the property name
*/
String property() default "";
* Return the java type for this argument.
* @return the java type
*/
Class<?> javaType() default void.class;
* Return the jdbc type for column that map to this argument.
* @return the jdbc type
*/
JdbcType jdbcType() default JdbcType.UNDEFINED;
* Returns the {@link TypeHandler} type for retrieving a column value from result set.
* @return the {@link TypeHandler} type
*/
Class<? extends TypeHandler> typeHandler() default UnknownTypeHandler.class;
* Returns the mapping definition for single relationship.
* @return the mapping definition for single relationship
*/
One one() default @One;
* Returns the mapping definition for collection relationship.
* @return the mapping definition for collection relationship
*/
Many many() default @Many;
}
复制代码
Part4 编写服务类,包括两个一个是接口类,只有方法名和返回值,这其中涉及的 java 的基础知识,如果还不知道这些,最好去花个两天的时间补充下基础知识,或者留言给我,我单独开个 java 基础系列分享。
public interface MockProjectService {
RespResult selectMockProjectList();
RespResult searchMockProject(String name, int current, int pageSize);
RespResult saveMockProject(MockProjectRequest mockProject);
}
复制代码
另一个是实现类,这里再次强调的重点是@Autowired
自动注解,是实现标记类在有需要的时候的自动声明,也可以使用@Resource
替代,这块的知识点如果要弄明白原理有点复杂,随着系列分享的递进,我专门弄的扩展篇讲解下,这里还是先习惯和熟练它的用法。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service("MockProjectService")
public class MockProjectServiceImpl implements MockProjectService {
@Autowired
MockProjectMapper mockProjectMapper;
* 获取项目列表
* @return Resp
*/
@Override
public RespResult selectMockProjectList() {
List<MockProjectEntity> mockProjectEntityList= mockProjectMapper.selectMockProject();
return RespResult.success(mockProjectEntityList);
}
* 根据搜索条件搜索项目List
* name为空的时候按全部条件搜索
* @param name
* @return
*/
@Override
public RespResult searchMockProject(String name, int current, int pageSize) {
List<MockProjectEntity> mockProjectEntityList;
PageHelper.startPage(current, pageSize);
if (name.isEmpty()){
mockProjectEntityList = mockProjectMapper.selectMockProject();
} else{
mockProjectEntityList = mockProjectMapper.searchMockProject(name);
}
PageInfo pageData = new PageInfo(mockProjectEntityList);
return RespResult.success(pageData);
}
* 项目添加和保存实现
* @param mockProject
* @return
*/
public RespResult saveMockProject(MockProjectRequest mockProject) {
MockProjectEntity mockProjectEntity = new MockProjectEntity();
mockProjectEntity.setName(mockProject.getName());
mockProjectEntity.setDesc(mockProject.getDesc());
mockProjectEntity.setType(mockProject.getType());
mockProjectEntity.setOwner(mockProject.getOwner());
if(mockProject.getId() == null){
mockProjectEntity.setCreateUser(mockProject.getOperator());
mockProjectMapper.insertProject(mockProjectEntity);
}
else {
mockProjectEntity.setId(mockProject.getId());
mockProjectEntity.setUpdateUser(mockProject.getOperator());
mockProjectMapper.updateProject(mockProjectEntity);
}
return RespResult.success();
}
}
复制代码
在代码中saveMockProject
服务方法是根据是否传了 ID,将增加和修改合并处理了。
插一个新知识点:分页查询**PageHelper[1]**
**的使用 ** <br />在之前的系列 python 做数据查询的分页的时候,通过控制 sql 语句用 limit 实现的,在 JAVA 使用 Mybatis 框架中有pagehelper
插件可以帮助其快速实现分页,它支持很多方法,上边代码也是官方比较推荐的第二种方法 ,Mapper 接口方式的调用,即在执行 mapper 查询语句前加 PageHelper.startPage()
或 PageHelper.offsetPage()
更多方法,也找个时机单独研究下分享给大家。
最后要使用这个插件时候需要在 pom.xml 添加依赖
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.4.2</version>
</dependency>
复制代码
以及优化 RespResult 增加了个多重结构方法,处理分页对象数据的格式化返回
public static RespResult success(PageInfo pageData) {
RespResult respResult = new RespResult();
respResult.setResultCode(RespCode.SUCCESS);
respResult.setData(pageData.getList());
respResult.setTotal(pageData.getTotal());
return respResult;
}
复制代码
Part5 接口实现,到了最后一步MockProjectController
真正接口的控制类实现,以上是笔者个人习惯从里到外层的顺序写法,也有些比较习惯先从这层写开始,按照接口需要逐层按照方法往下写。
@RestController
@RequestMapping("/api/mock/")
public class MockProjectController {
@Autowired
private MockProjectService mockProjectService;
@GetMapping(value = "/project/list")
public RespResult getProjectList() {
return mockProjectService.selectMockProjectList();
}
@RequestMapping(value = "/project/search", method = RequestMethod.GET)
public RespResult searchProject(@RequestParam(value = "name") String name, int current, int pageSize) {
return mockProjectService.searchMockProject(name, current, pageSize);
}
* 项目增加保存类
* @param mockProject
* @return
*/
@PostMapping(value = "/project/save")
public RespResult saveProject(@RequestBody MockProjectRequest mockProject) {
try {
return mockProjectService.saveMockProject(mockProject);
}catch (Exception e){
System.out.println(e);
}
return RespResult.failure(RespCode.SYSTEM_ERROR);
}
}
复制代码
以上所有罗列的说明和代码就构成了,项目管理功能所需要接口服务的后端代码,开发完最后还有一项非常重要的事情要做就是进行测试,接口功能测试应该是测试人员最强的部分,用例就详细罗列了,直接给出笔者某接口的测试结果,以示接口服务的正常。<br />
本篇代码量有点多,一口气的实现项目管理的需要的全部接口,大家可以先编写实现,熟悉这个套路为主,如果有余力可以先查询资料理解下强调的新知识点。接下来会有 2 篇左右来实现前端交互,也可到攒到那时根据用到接口按需编码。
2.遇到的报错问题解决
后端在参照之前项目添加 pagehelper 依赖的时候报了个循环依赖错误,心如如下:
Description:
The dependencies of some of the beans in the application context form a cycle:
┌──->──┐
| com.github.pagehelper.autoconfigure.PageHelperAutoConfiguration
└──<-──┘
复制代码
经过排查和搜索,此问题为版本依赖冲突引起的,如图所示在对 maven 依赖进行分析的时候有一条红线表示有冲突,并且在右侧以来包列表中也可以看到。
这个问题解决办法pom.xml
升级依赖版本到最新pagehelper-spring-boot-starter
版本,或者降级spring-boot-starter-parent
均可以。
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
+ <version>1.4.2</version>
-
</dependency>
复制代码
参考资料