【测试开发】八、接口测试-后端-模块树列表接口实现,使用HutoolUtil中TreeUtil快速处理树形结构

简介: 【测试开发】八、接口测试-后端-模块树列表接口实现,使用HutoolUtil中TreeUtil快速处理树形结构

国庆节快乐!基于 springboot+vue 的测试平台开发继续更新。


上次完成了接口定义功能的前端页面,那么后端现在开始逐一实现对应的功能,首先就是提供模块列表接口,这个模块是支持子层级的,所以大概结构是这样:


[
  {
    id: 1,
    label: '默认',
    children: [
      {
        id: 4,
        label: '二级子模块1',
        children: [
          {
            id: 9,
            label: '三级子模块1'
          }, 
          {
            id: 10,
            label: '三级子模块2'
          }
        ]
      }
    ]
  }, 
  {
    id: 2,
    label: '一级子模块2',
    children: [
      {
        id: 5,
        label: '二级子模块 1'
      }, 
      {
        id: 6,
        label: '二级子模块 2'
      }
    ]
  }
]


通常来说,可以写递归代码来找出子层级的数据,然后再进行封装返回出来,比较麻烦。


后来发现 HutoolUtil 中有个工具类 TreeUtil 可以完成我需求,非常便捷,本次就使用它来实现。


HutoolUtil 这个框架还是大奇分享给我的,这货是个测试领域资深大佬,它的公众号里更是干活满满。而且最近他在更新基于python语言的web开发知识,不容错过。


VX搜索【大奇测试开发】即可找到他。


言归正传,下面来完成接口功能的开发。


一、引用 HutoolUtil


Hutool是一个小而全的Java工具类库,通过静态方法封装,降低相关API的学习成本,提高工作效率,使Java拥有函数式语言般的优雅,让Java语言也可以“甜甜的”。


Hutool中的工具方法来自每个用户的精雕细琢,它涵盖了Java开发底层代码中的方方面面,它既是大型项目开发中解决小问题的利器,也是小型项目中的效率担当;


Hutool是项目中“util”包友好的替代,它节省了开发人员对项目中公用类和公用工具方法的封装时间,使开发专注于业务,同时可以最大限度的避免封装不完善带来的bug。


要使用它直接添加依赖即可:


<!--hutool-->
    <dependency>
        <groupId>cn.hutool</groupId>
        <artifactId>hutool-all</artifactId>
        <version>5.7.12</version>
    </dependency>


官方文档:https://www.hutool.cn/docs/#/


内容很详细,不仅后面的树结构工具,像常用的集合类、JSON、日志、缓存、文件、线程和并发等等应有尽有。


二、建表


给模块建一张新表api_module:


CREATE TABLE `api_module` (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT 'id',
  `projectId` bigint NOT NULL COMMENT '该节点所属项目id',
  `name` varchar(64) COLLATE utf8mb4_general_ci NOT NULL COMMENT '节点名称',
  `parentId` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '父节点id',
  `level` int DEFAULT '1' COMMENT '节点层级',
  `createTime` datetime NOT NULL DEFAULT '1900-01-01 00:00:00' COMMENT '创建时间',
  `updateTime` datetime NOT NULL DEFAULT '1900-01-01 00:00:00' COMMENT '更新时间',
  `pos` double DEFAULT NULL COMMENT '节点顺序位置',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='模块表';


重要字段:


  • projectId:与项目进行关联
  • parentId:该节点的父节点,一级目录的父节点我会设置为 0 。
  • level:该节点对应层级,从 1 开始。
  • pos:表示该节点在父节点下的位置顺序。


三、后端接口实现


1. Controller 层


新建 ApiModuleController 类,添加一个处理器方法 getNodeByProjectId,通过项目 ID 查询出下面的所有模块。


package com.pingguo.bloomtest.controller;
import com.pingguo.bloomtest.common.Result;
import com.pingguo.bloomtest.service.ApiModuleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("module")
public class ApiModuleController {
    @Autowired
    ApiModuleService apiModuleService;
    @GetMapping("/list/{projectId}")
    public Result getNodeByProjectId(@PathVariable Long projectId) {
        return Result.success(apiModuleService.getNodeTreeByProjectId(projectId));
    }
}


2. DAO层


dao 层自然也要有。


package com.pingguo.bloomtest.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.pingguo.bloomtest.pojo.ApiModule;
import org.springframework.stereotype.Repository;
@Repository
public interface ApiModuleDAO extends BaseMapper<ApiModule> {
}


3. Service 层


实现 getNodeTreeByProjectId 方法。


public List<Tree<String>> getNodeTreeByProjectId(Long projectId) {
        this.getDefaultNode(projectId);
        // 根据 projectId 查询所有节点
        QueryWrapper<ApiModule> wrapperApiModule = new QueryWrapper<>();
        List<ApiModule> apiModules = apiModuleDAO.selectList(wrapperApiModule.eq("projectId", projectId));
        // 配置
        TreeNodeConfig treeNodeConfig = new TreeNodeConfig();
        // 自定义属性名 ,即返回列表里对象的字段名
        treeNodeConfig.setIdKey("id");
        treeNodeConfig.setWeightKey("pos");
        treeNodeConfig.setParentIdKey("parentId");
        treeNodeConfig.setChildrenKey("children");
        // 最大递归深度
//        treeNodeConfig.setDeep(5);
        treeNodeConfig.setNameKey("name");
        //转换器
        List<Tree<String>> treeNodes = TreeUtil.build(apiModules, "0", treeNodeConfig,
                (treeNode, tree) -> {
                    tree.setId(treeNode.getId().toString());
                    tree.setParentId(treeNode.getParentId().toString());
                    tree.setWeight(treeNode.getPos());
                    tree.setName(treeNode.getName());
                    // 扩展属性 ...
                    tree.putExtra("projectId", treeNode.getProjectId());
                    tree.putExtra("level", treeNode.getLevel());
                    tree.putExtra("label", treeNode.getName());
                    tree.putExtra("createTime", treeNode.getCreateTime());
                    tree.putExtra("updateTime", treeNode.getUpdateTime());
                });
        return treeNodes;
    }


这里开头有个方法 getDefaultNode,在这里面会判断当前项目下是否有默认模块,没有则添加默认模块。


private void getDefaultNode(Long projectId) {
        QueryWrapper<ApiModule> wrapperApiModule = new QueryWrapper<>();
        wrapperApiModule.eq("projectId", projectId)
                        .eq("pos", 1.0);
        // 判断当前项目下是否有默认模块,没有则添加默认模块
        if (apiModuleDAO.selectCount(wrapperApiModule) == 0) {
            ApiModule apiModule = new ApiModule();
            apiModule.setName("默认");
            apiModule.setPos(1.0);
            apiModule.setLevel(1);
            apiModule.setParentId(0L);
            apiModule.setCreateTime(new Date());
            apiModule.setUpdateTime(new Date());
            apiModule.setProjectId(projectId);
            apiModuleDAO.insert(apiModule);
        }
    }


然后通过 项目id 把项目下所有的数据查询出来:


1268169-20211001121439564-2081859584.png


接下来使用 TreeUtil 来完成树结构处理。


首先,创建一个配置类 TreeNodeConfig 对象,在这个对象里设置属性,对应的就是返回出来的字段名。


1268169-20211001121536552-739486529.png


还可以设置最大递归深度,也可以不设。我测试之后就注释掉了,先不加限制。

最后就是构建树结构 treeNodes,完成处理后返回给 controller 层。


1268169-20211001121837788-318543184.png


因为我要返回的还有其他的字段,可以使用tree.putExtra来添加要返回的其他字段,比如:


tree.putExtra("projectId", treeNode.getProjectId());


一个参数是定义的字段名称,第二个参数就是使用这个结点的 get 方法获取对应的属性值。


最后返回到上层的是List<Tree<String>>类型,可以直接塞到统一结果里去返回。


1268169-20211001122235152-2031877142.png


四、测试一下


1. 测试结构数据


测试一下接口,先手动网表里插入了对应结构的数据。


1268169-20211001122324773-1833490636.png


请求接口,传入 projectId 为 3。


{
    "code": 20000,
    "message": "成功",
    "data": [
        {
            "id": "9",
            "parentId": "0",
            "pos": 1.0,
            "name": "默认",
            "projectId": 3,
            "level": 1,
            "label": "默认",
            "createTime": "2021-09-29 10:50:00",
            "updateTime": "2021-09-29 10:50:00",
            "children": [
                {
                    "id": "14",
                    "parentId": "9",
                    "pos": 1.0,
                    "name": "默认-2",
                    "projectId": 3,
                    "level": 2,
                    "label": "默认-2",
                    "createTime": "1900-01-01 08:00:00",
                    "updateTime": "1900-01-01 08:00:00"
                },
                {
                    "id": "10",
                    "parentId": "9",
                    "pos": 1.0,
                    "name": "默认-1",
                    "projectId": 3,
                    "level": 2,
                    "label": "默认-1",
                    "createTime": "2021-10-01 08:00:00",
                    "updateTime": "1900-01-01 08:00:00",
                    "children": [
                        {
                            "id": "11",
                            "parentId": "10",
                            "pos": 1.0,
                            "name": "默认-1-1",
                            "projectId": 3,
                            "level": 3,
                            "label": "默认-1-1",
                            "createTime": "1900-01-01 08:00:00",
                            "updateTime": "1900-01-01 08:00:00",
                            "children": [
                                {
                                    "id": "12",
                                    "parentId": "11",
                                    "pos": 1.0,
                                    "name": "默认-1-1-1",
                                    "projectId": 3,
                                    "level": 4,
                                    "label": "默认-1-1-1",
                                    "createTime": "1900-01-01 08:00:00",
                                    "updateTime": "1900-01-01 08:00:00",
                                    "children": [
                                        {
                                            "id": "13",
                                            "parentId": "12",
                                            "pos": 1.0,
                                            "name": "默认-1-1-1-1",
                                            "projectId": 3,
                                            "level": 5,
                                            "label": "默认-1-1-1-1",
                                            "createTime": "1900-01-01 08:00:00",
                                            "updateTime": "1900-01-01 08:00:00"
                                        }
                                    ]
                                }
                            ]
                        }
                    ]
                }
            ]
        }
    ]
}


结果正确。


2. 测试新增默认


传入一个 projectId 为 4 ,localhost:8080/bloomtest/module/list/4


{
    "code": 20000,
    "message": "成功",
    "data": [
        {
            "id": "15",
            "parentId": "0",
            "pos": 1.0,
            "name": "默认",
            "projectId": 4,
            "level": 1,
            "label": "默认",
            "createTime": "2021-10-01 12:25:54",
            "updateTime": "2021-10-01 12:25:54"
        }
    ]
}


返回正确。


1268169-20211001122644501-1553673534.png


落库正常。

相关文章
|
11月前
|
Java 测试技术 容器
Jmeter工具使用:HTTP接口性能测试实战
希望这篇文章能够帮助你初步理解如何使用JMeter进行HTTP接口性能测试,有兴趣的话,你可以研究更多关于JMeter的内容。记住,只有理解并掌握了这些工具,你才能充分利用它们发挥其应有的价值。+
1538 23
|
SQL 安全 测试技术
2025接口测试全攻略:高并发、安全防护与六大工具实战指南
本文探讨高并发稳定性验证、安全防护实战及六大工具(Postman、RunnerGo、Apipost、JMeter、SoapUI、Fiddler)选型指南,助力构建未来接口测试体系。接口测试旨在验证数据传输、参数合法性、错误处理能力及性能安全性,其重要性体现在早期发现问题、保障系统稳定和支撑持续集成。常用方法包括功能、性能、安全性及兼容性测试,典型场景涵盖前后端分离开发、第三方服务集成与数据一致性检查。选择合适的工具需综合考虑需求与团队协作等因素。
2041 24
|
SQL 测试技术
除了postman还有什么接口测试工具
最好还是使用国内的接口测试软件,其实国内替换postman的软件有很多,这里我推荐使用yunedit-post这款接口测试工具来代替postman,因为它除了接口测试功能外,在动态参数的支持、后置处理执行sql语句等支持方面做得比较好。而且还有接口分享功能,可以生成接口文档给团队在线浏览。
656 2
|
10月前
|
人工智能 Java API
后端开发必看:零代码实现存量服务改造成MCP服务
本文介绍如何通过 **Nacos** 和 **Higress** 实现存量 Spring Boot 服务的零代码改造,使其支持 MCP 协议,供 AI Agent 调用。全程无需修改业务代码,仅通过配置完成服务注册、协议转换与工具映射,显著降低改造成本,提升服务的可集成性与智能化能力。
2972 1
|
10月前
|
前端开发 Java 数据库连接
后端开发中的错误处理实践:原则与实战
在后端开发中,错误处理是保障系统稳定性的关键。本文介绍了错误分类、响应设计、统一处理机制及日志追踪等实践方法,帮助开发者提升系统的可维护性与排障效率,做到防患于未然。
|
12月前
|
存储 消息中间件 前端开发
PHP后端与uni-app前端协同的校园圈子系统:校园社交场景的跨端开发实践
校园圈子系统校园论坛小程序采用uni-app前端框架,支持多端运行,结合PHP后端(如ThinkPHP/Laravel),实现用户认证、社交关系管理、动态发布与实时聊天功能。前端通过组件化开发和uni.request与后端交互,后端提供RESTful API处理业务逻辑并存储数据于MySQL。同时引入Redis缓存热点数据,RabbitMQ处理异步任务,优化系统性能。核心功能包括JWT身份验证、好友系统、WebSocket实时聊天及活动管理,确保高效稳定的用户体验。
653 4
PHP后端与uni-app前端协同的校园圈子系统:校园社交场景的跨端开发实践
|
前端开发 JavaScript 关系型数据库
2025 年前端与后端开发方向的抉择与展望-优雅草卓伊凡
2025 年前端与后端开发方向的抉择与展望-优雅草卓伊凡
914 5
2025 年前端与后端开发方向的抉择与展望-优雅草卓伊凡
|
人工智能 小程序 NoSQL
【一步步开发AI运动小程序】二十一、如何将AI运动项目配置持久化到后端?
本文介绍基于云智「Ai运动识别引擎」的运动配置持久化方案,旨在优化小程序或Uni APP中AI运动识别能力。通过将运动检测参数(如`Key`、`Name`、`TickMode`、`rules`或`samples`)持久化到后端,可避免因频繁调整运动参数而重新发布应用,提升用户体验。持久化数据结构支持规则和姿态样本存储,适用于关系数据库、文件或文档数据库(如MongoDB)。此外,云智还提供运动自动适配工具及「AI乐运动」产品,助力快速实现AI体育、全民健身等场景。
|
JSON 自然语言处理 前端开发
【01】对APP进行语言包功能开发-APP自动识别地区ip后分配对应的语言功能复杂吗?-成熟app项目语言包功能定制开发-前端以uniapp-基于vue.js后端以laravel基于php为例项目实战-优雅草卓伊凡
【01】对APP进行语言包功能开发-APP自动识别地区ip后分配对应的语言功能复杂吗?-成熟app项目语言包功能定制开发-前端以uniapp-基于vue.js后端以laravel基于php为例项目实战-优雅草卓伊凡
722 72
【01】对APP进行语言包功能开发-APP自动识别地区ip后分配对应的语言功能复杂吗?-成熟app项目语言包功能定制开发-前端以uniapp-基于vue.js后端以laravel基于php为例项目实战-优雅草卓伊凡
|
前端开发 Java 数据库连接
Java后端开发-使用springboot进行Mybatis连接数据库步骤
本文介绍了使用Java和IDEA进行数据库操作的详细步骤,涵盖从数据库准备到测试类编写及运行的全过程。主要内容包括: 1. **数据库准备**:创建数据库和表。 2. **查询数据库**:验证数据库是否可用。 3. **IDEA代码配置**:构建实体类并配置数据库连接。 4. **测试类编写**:编写并运行测试类以确保一切正常。
791 2

热门文章

最新文章