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;
@Datapublic class MockProjectEntity extends BaseEntity{ private Integer id; private String name; private String type; private String desc; private String owner;}
复制代码
Part2 项目的保存操作需要一个请求 body 实体类 MockProjectRequest,其中时间信息不需要,创建人修改统一通过operator 透传。
@Datapublic 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>
复制代码
参考资料