导入swagger

This commit is contained in:
2025-02-19 11:02:19 +08:00
parent a7029118f2
commit 2ea58667e0
15 changed files with 245 additions and 34 deletions

View File

@@ -61,9 +61,6 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.5.15</version>
<configuration>
<fork>true</fork> <!-- 如果没有该配置devtools不会生效 -->
</configuration>
<executions>
<execution>
<goals>

View File

@@ -14,7 +14,6 @@ import com.test.common.enums.BusinessType;
import com.test.test.domain.TestApi;
import com.test.test.service.ITestApiService;
import com.test.common.core.page.TableDataInfo;
import org.springframework.web.multipart.MultipartFile;
/**
* 接口Controller

View File

@@ -20,7 +20,7 @@ public class TestGroupController extends BaseController {
private ITestGroupService testGroupService;
/**
* 查询接口节点列表
* 查询节点列表
*/
@GetMapping("/list")
public AjaxResult list(String type) {
@@ -29,27 +29,27 @@ public class TestGroupController extends BaseController {
}
/**
* 新增接口节点
* 新增节点
*/
@Log(title = "接口节点", businessType = BusinessType.INSERT)
@Log(title = "节点", businessType = BusinessType.INSERT)
@PostMapping("/add")
public AjaxResult add(@RequestBody TestGroup testGroup) {
return success(testGroupService.insertTestGroup(testGroup));
}
/**
* 修改接口节点
* 修改节点
*/
@Log(title = "接口节点", businessType = BusinessType.UPDATE)
@Log(title = "节点", businessType = BusinessType.UPDATE)
@PostMapping("/edit")
public AjaxResult edit(@RequestBody TestGroup testGroup) {
return toAjax(testGroupService.updateTestGroup(testGroup));
}
/**
* 删除接口节点
* 删除节点
*/
@Log(title = "接口节点", businessType = BusinessType.DELETE)
@Log(title = "节点", businessType = BusinessType.DELETE)
@PostMapping("/del")
public AjaxResult remove(@RequestBody @Validated GroupDelectQO qo) throws Exception {
return toAjax(testGroupService.deleteTestGroupById(qo));

View File

@@ -1,4 +0,0 @@
package com.test.test.domain.dto;
public class SwaggerInfo {
}

View File

@@ -33,4 +33,5 @@ public interface TestGroupMapper
*/
int deleteTestGroupByIds(List<Long> ids);
TestGroup selectGroup(String name, Long parentId, String type);
}

View File

@@ -1,6 +1,7 @@
package com.test.test.service;
import java.util.List;
import com.test.test.domain.TestGroup;
import com.test.test.domain.qo.GroupDelectQO;
@@ -9,8 +10,7 @@ import com.test.test.domain.qo.GroupDelectQO;
*
* @author xiaoe
*/
public interface ITestGroupService
{
public interface ITestGroupService {
/**
* 查询节点(文件夹)列表
*/
@@ -21,6 +21,11 @@ public interface ITestGroupService
*/
TestGroup insertTestGroup(TestGroup testGroup);
/**
* 查询节点(文件夹)
*/
TestGroup selectGroup(String name, Long parentId, String type);
/**
* 修改节点(文件夹)
*/

View File

@@ -1,16 +1,20 @@
package com.test.test.service.impl;
import java.util.List;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import com.alibaba.fastjson2.JSONObject;
import com.test.common.utils.DateUtils;
import com.test.common.utils.http.HttpUtils;
import com.test.test.domain.TestGroup;
import com.test.test.domain.qo.TestApiListQO;
import com.test.test.service.ITestGroupService;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import com.test.test.mapper.TestApiMapper;
import com.test.test.domain.TestApi;
import com.test.test.service.ITestApiService;
import org.springframework.transaction.annotation.Transactional;
/**
* 接口Service业务层处理
@@ -22,6 +26,9 @@ public class TestApiServiceImpl implements ITestApiService {
@Resource
private TestApiMapper testApiMapper;
@Resource
private ITestGroupService testGroupService;
/**
* 查询接口
*
@@ -57,9 +64,159 @@ public class TestApiServiceImpl implements ITestApiService {
}
@Override
@Transactional
public int importSwaggerApi(String url) {
JSONObject json = JSONObject.parse(HttpUtils.sendGet(url));
return 0;
AtomicInteger count = new AtomicInteger();
JSONObject jsonObject = JSONObject.parseObject(HttpUtils.sendGet(url));
String title = jsonObject.getJSONObject("info").getString("title");
// 设置顶级节点
Long parentId = getGroupId(title, 0L);
// 获取Schemas
JSONObject schemas = jsonObject.getJSONObject("components").getJSONObject("schemas");
// 获取所有接口
jsonObject.getJSONObject("paths").forEach((uri, pathJson) -> {
((JSONObject) pathJson).forEach((method, v) -> {
JSONObject json = (JSONObject) v;
// 获取接口名
String name = json.getString("summary");
if (name == null) name = uri;
// 获取接口分组
String groupName = title;
List<String> tags = json.getList("tags", String.class);
if (!tags.isEmpty()) {
groupName = tags.get(0);
}
// 获取body
String body = "";
String contentType = "";
if (json.getJSONObject("requestBody") != null) {
JSONObject bodyJson = json.getJSONObject("requestBody").getJSONObject("content");
if (bodyJson != null) {
Set<String> keys = bodyJson.keySet();
if (!keys.isEmpty()) {
contentType = keys.iterator().next();
JSONObject schema = bodyJson.getJSONObject(contentType).getJSONObject("schema");
body = JSONObject.toJSONString(parseSchema(schema, schemas));
}
}
}
// 获取Param
String param = "";
JSONObject parameters = json.getJSONObject("parameters");
if (parameters!=null) {
JSONObject paramsJson = parseParams(parameters, schemas);
param = JSONObject.toJSONString(paramsJson);
}
TestApi testApi = new TestApi();
testApi.setUri(uri);
testApi.setMethod(method);
testApi.setName(name);
testApi.setGroupId(getGroupId(groupName, parentId));
testApi.setBody(body);
testApi.setParam(param);
count.addAndGet(testApiMapper.insertTestApi(testApi));
});
});
return count.get();
}
private static JSONObject parseSchema(JSONObject schema, JSONObject schemas) {
JSONObject result = new JSONObject();
// 如果 schema 包含 $ref则解析引用
if (schema.containsKey("$ref")) {
String ref = schema.getString("$ref");
// 去掉 #/components/schemas/ 前缀,获取引用的 schema 名称
String schemaName = ref.replace("#/components/schemas/", "");
JSONObject referencedSchema = schemas.getJSONObject(schemaName);
// 递归解析引用的 schema
return parseSchema(referencedSchema, schemas);
}
// 如果 schema 包含 type则直接根据类型构建结果
if (schema.containsKey("type")) {
String type = schema.getString("type");
switch (type) {
case "integer":
result.put("value", 0);
break;
case "string":
result.put("value", "string");
break;
case "boolean":
result.put("value", false);
break;
default:
result.put("value", type);
}
}
// 如果 schema 有 properties则遍历 properties 并递归解析
if (schema.containsKey("properties")) {
JSONObject properties = schema.getJSONObject("properties");
for (Map.Entry<String, Object> entry : properties.entrySet()) {
String key = entry.getKey();
JSONObject propertySchema = (JSONObject) entry.getValue();
JSONObject propertyResult = parseSchema(propertySchema, schemas); // 递归解析每个字段
result.put(key, propertyResult.get("value")); // 获取解析结果的值
}
}
return result;
}
private static JSONObject parseParams(JSONObject params, JSONObject schemas) {
JSONObject result = new JSONObject();
// 遍历 params 中的每个参数
for (String key : params.keySet()) {
// 获取当前参数的 schema 定义
Object value = params.get(key);
// 获取 schema
JSONObject schema = schemas.getJSONObject(key);
// 如果 schema 是基础类型,则直接赋值
if (schema != null && schema.containsKey("type")) {
String type = schema.getString("type");
if ("string".equals(type)) {
result.put(key, value != null ? value : "string"); // 默认值为 "string"
} else if ("integer".equals(type)) {
result.put(key, value != null ? value : 0); // 默认值为 0
} else if ("boolean".equals(type)) {
result.put(key, value != null ? value : false); // 默认值为 false
}
}
// 如果 schema 是引用类型,则递归解析该引用
else if (schema != null && schema.containsKey("$ref")) {
String ref = schema.getString("$ref");
String schemaName = ref.replace("#/components/schemas/", "");
JSONObject referencedSchema = schemas.getJSONObject(schemaName);
// 如果引用的 schema 存在,递归解析该 schema
if (referencedSchema != null) {
// 递归调用,解析引用的 schema
JSONObject nestedResult = parseParams(new JSONObject(), schemas);
result.putAll(nestedResult);
}
}
}
return result;
}
private Long getGroupId(String groupName, Long parentId) {
TestGroup group = testGroupService.selectGroup(groupName, parentId, "api");
if (group == null) {
group = new TestGroup();
group.setParentId(parentId);
group.setName(groupName);
group.setType("api");
group.setCreateTime(DateUtils.getNowDate());
testGroupService.insertTestGroup(group);
}
return group.getId();
}
/**

View File

@@ -8,6 +8,7 @@ import com.test.common.utils.DateUtils;
import com.test.common.utils.StringUtils;
import com.test.test.domain.qo.GroupDelectQO;
import jakarta.annotation.Resource;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import com.test.test.mapper.TestGroupMapper;
import com.test.test.domain.TestGroup;
@@ -26,6 +27,7 @@ public class TestGroupServiceImpl implements ITestGroupService {
private TestGroupMapper testGroupMapper;
@Resource
@Lazy
private TestApiServiceImpl testApiServiceImpl;
/**
@@ -46,6 +48,11 @@ public class TestGroupServiceImpl implements ITestGroupService {
return testGroup;
}
@Override
public TestGroup selectGroup(String name, Long parentId, String type) {
return testGroupMapper.selectGroup(name, parentId, type);
}
/**
* 修改节点(文件夹)
*/

View File

@@ -33,6 +33,7 @@
<if test="method != null and method != ''"> and method = #{method}</if>
<if test="uri != null and uri != ''"> and uri like concat('%', #{uri}, '%')</if>
</where>
order by create_time desc
</select>
<select id="selectTestApiById" parameterType="Long" resultMap="TestApiResult">

View File

@@ -37,6 +37,13 @@
</where>
</select>
<select id="selectGroup" parameterType="String" resultMap="TestGroupResult">
<include refid="selectTestGroupVo"/>
<where>
name = #{name} and type = #{type} and parent_id = #{parentId}
</where>
</select>
<insert id="insertTestGroup" parameterType="TestGroup" useGeneratedKeys="true" keyProperty="id">
insert into test_group
<trim prefix="(" suffix=")" suffixOverrides=",">

View File

@@ -39,8 +39,16 @@ export function updateApi(data) {
// 删除接口
export function delApi(id) {
return request({
url: '/test/api/del/',
url: '/test/api/del',
method: 'post',
params: {id}
data: {id}
})
}
export function importApi(url) {
return request({
url: '/test/api/import/swagger',
method: 'post',
params: url
})
}

View File

@@ -143,7 +143,7 @@ export default {
this.form.header.splice(scope.$index, 1)
},
cancel() {
this.$tab.closeOpenPage({ path: "/api" });
this.$tab.closeOpenPage({path: "/api"});
}
}
}
@@ -158,9 +158,11 @@ export default {
::v-deep .el-collapse-item__wrap {
padding: 0 16px;
}
::v-deep.el-select {
width: 130px;
}
.input-with-select ::v-deep .el-input-group__prepend {
background-color: #fff;
}

View File

@@ -139,7 +139,7 @@ export default {
contentType: this.form.contentType,
body: this.form.body,
}).then(res => {
this.$message.success("新增成功");
this.$message.success("修改成功");
this.cancel();
})
},
@@ -165,7 +165,7 @@ export default {
this.form.header.splice(scope.$index, 1)
},
cancel() {
this.$tab.closeOpenPage({ path: "/api" });
this.$tab.closeOpenPage({path: "/api"});
}
}
}
@@ -180,9 +180,11 @@ export default {
::v-deep .el-collapse-item__wrap {
padding: 0 16px;
}
::v-deep.el-select {
width: 130px;
}
.input-with-select ::v-deep .el-input-group__prepend {
background-color: #fff;
}

View File

@@ -1,22 +1,46 @@
<template>
<div class="app-container">
import
<div>
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
<el-form-item label="swagger" prop="url">
<el-input v-model="form.url" placeholder="请输入swagger文档链接"/>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</div>
</template>
<script>
import {importApi} from "@/api/test/api";
export default {
name: "ApiImport",
dicts: ['http_method'],
created () {
console.log(this.$route.query.groupId)
},
data() {
return {
// 表单参数
form: {},
// 表单校验
rules: {}
rules: {
url: [{required: true, message: "swagger文档链接不能为空", trigger: "blur"}]
}
}
},
methods: {
submitForm() {
this.$refs["form"].validate(valid => {
if (valid) {
importApi({url: this.form.url}).then((res) => {
this.$message.success("导入成功")
this.cancel();
})
}
})
},
cancel() {
this.$emit('close')
}
}
}

View File

@@ -28,6 +28,7 @@
<el-table-column label="接口名称" align="center" prop="name"/>
<el-table-column label="接口请求类型" align="center" prop="method"/>
<el-table-column label="接口路径" align="center" prop="uri"/>
<el-table-column label="创建时间" align="center" prop="createTime"/>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)">修改</el-button>
@@ -35,18 +36,21 @@
</template>
</el-table-column>
</el-table>
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList"/>
<el-dialog title="导入接口" :visible.sync="dialogVisible" :close-on-click-modal="false" destroy-on-close>
<api-import @close="() => dialogVisible = false"/>
</el-dialog>
</folder-page>
</template>
<script>
import {listApi, delApi} from "@/api/test/api";
import FolderPage from "@/components/FolderPage/index.vue";
import ApiImport from "@/views/test/api/import.vue";
export default {
name: "Api",
components: {FolderPage},
components: {ApiImport, FolderPage},
dicts: ['http_method'],
data() {
return {
@@ -62,6 +66,7 @@ export default {
method: null,
uri: null,
},
dialogVisible: false,
};
},
methods: {
@@ -97,7 +102,7 @@ export default {
this.$tab.openPage("添加接口", "/api/add", {groupId: this.queryParams.groupId});
},
handleImport() {
this.$tab.openPage("导入接口", "/api/import", {groupId: this.queryParams.groupId});
this.dialogVisible = true;
},
/** 修改按钮操作 */
handleUpdate(row) {