@@ -65,7 +65,8 @@
size="small"
effect="plain"
class="copyBtn"
- @click.stop="copyCurl(buildRequestUrl(hostConfig))">{{ $t('copy') + 'CURL' }}
+ @click.stop="copyCurl(buildRequestUrl(hostConfig))"
+ >{{ $t('copy') + 'CURL' }}
@@ -81,15 +82,15 @@
{{ $t('description') }}
-
+
ContentType{{ docInfo.contentType }}
@@ -189,7 +190,7 @@
-
+
@@ -244,7 +245,7 @@ import CodeGenDrawer from '@/components/CodeGenDrawer'
import CopyText from '@/components/CopyText'
import ExportUtil from '@/utils/export'
import { generate } from 'json2interface'
-import {get_effective_url, parse_root_array, StringBuilder} from '@/utils/common'
+import { get_effective_url, parse_root_array, StringBuilder } from '@/utils/common'
import { mavonEditor } from 'mavon-editor'
export default {
@@ -529,7 +530,7 @@ export default {
// --location --globoff
const str = new StringBuilder(`curl -L -g `)
- const httpMethod = this.docInfo.httpMethod;
+ const httpMethod = this.docInfo.httpMethod
if (httpMethod === 'POST' || httpMethod === 'PUT') {
str.append(`-X ${httpMethod} `)
}
diff --git a/front/src/components/ParameterCompareTable/index.vue b/front/src/components/ParameterCompareTable/index.vue
new file mode 100644
index 0000000000000000000000000000000000000000..9c9765b6ef8ccbd298430c719bd94874e91a9750
--- /dev/null
+++ b/front/src/components/ParameterCompareTable/index.vue
@@ -0,0 +1,125 @@
+
+
+
+
+
+
+
+ {{ scope.row.name }}
+
+
+
+
+
+
+ {{ scope.row.type }} -> {{ compareModifyParam[scope.row.id].diffParamType }}
+
+ {{ scope.row.type }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/front/src/utils/http.js b/front/src/utils/http.js
index dbf9f8c5c88a6fd0245168a210ec51fb3cfe3da7..107a7b0ce2c460603cbb7bcc43d96c1fe9e31d11 100644
--- a/front/src/utils/http.js
+++ b/front/src/utils/http.js
@@ -20,7 +20,7 @@ const frontURL = getBrowserUrl()
const serverUrl = process.env.VUE_APP_BASE_API || frontURL
// 创建axios实例
-const client = axios.create({
+export const client = axios.create({
baseURL: serverUrl, // api 的 base_url
timeout: 600000, // 请求超时时间,600秒
headers: {
diff --git a/front/src/utils/i18n/languages/en-us.js b/front/src/utils/i18n/languages/en-us.js
index 569be7a2b69c42d170886f4325f475cd53e6079f..b9d6def9bd28dfa7eb38d265be5820b79b57071b 100644
--- a/front/src/utils/i18n/languages/en-us.js
+++ b/front/src/utils/i18n/languages/en-us.js
@@ -562,6 +562,10 @@ export default {
'weComWebhookUrlPlacehoder': 'Input full url with token parameter',
'weComWebhookUrlTip': 'Push message to weCom group when doc is changed'
},
+ ModSetting: {
+ 'isMonitorSetting': 'Is Monitor',
+ 'isMonitorSettingTip': 'When monitoring is enabled, data will be pushed to the document push table for parameter difference comparison. otherwise, the registration document will be updated'
+ },
UserInfo: {
'bindDingDingTip': 'Use DingDing app scan the QR to bind account'
},
@@ -569,7 +573,8 @@ export default {
'weComSetting': 'WeCom Setting',
'dingdingSetting': 'DingTalk Setting',
'swaggerSetting': 'Swagger Setting',
- 'meterSphereSetting': 'MeterSphere Setting'
+ 'meterSphereSetting': 'MeterSphere Setting',
+ 'modSetting': 'Base Setting'
},
DocChangelog: {
confirmRestore: 'Restore this version?'
diff --git a/front/src/utils/i18n/languages/zh-cn.js b/front/src/utils/i18n/languages/zh-cn.js
index 05d9f5d4b08a630da64f45479abc69ff5774c6d3..5336855d170561d5fe1faba247ae4c1878398d85 100644
--- a/front/src/utils/i18n/languages/zh-cn.js
+++ b/front/src/utils/i18n/languages/zh-cn.js
@@ -565,6 +565,10 @@ export default {
'weComWebhookUrlPlacehoder': '输入完整带token参数的url',
'weComWebhookUrlTip': '当文档变更时推送消息到企业微信群。'
},
+ ModSetting: {
+ 'isMonitorSetting': '是否监控',
+ 'isMonitorSettingTip': '开启监控则推送数据入文档推送表,用于比对参数差异。否则更新登记文档'
+ },
UserInfo: {
'bindDingDingTip': '使用钉钉App扫一扫进行账号绑定'
},
@@ -572,7 +576,8 @@ export default {
'weComSetting': '企业微信配置',
'dingdingSetting': '钉钉配置',
'swaggerSetting': 'Swagger设置',
- 'meterSphereSetting': 'MeterSphere设置'
+ 'meterSphereSetting': 'MeterSphere设置',
+ 'modSetting': '基础配置'
},
DocChangelog: {
confirmRestore: '确认还原到此版本吗?'
diff --git a/front/src/views/project/DocTable/index.vue b/front/src/views/project/DocTable/index.vue
index 9416b6ff99d5e4b99dc3d18a9528d94ac10211a8..a9a96002916e18b46c098bfac60a0ff994a1a55b 100644
--- a/front/src/views/project/DocTable/index.vue
+++ b/front/src/views/project/DocTable/index.vue
@@ -75,6 +75,7 @@
:height="tableHeight"
:row-height="30"
border
+ :row-style="rowStyle"
>
+
+
+
+
+
md
@@ -233,6 +239,17 @@
:title="$t('unlock')"
:command="() => { onDocUnLock(scope.row) }"
/>
+
+
+
+
+
+
@@ -300,6 +317,7 @@ import SvgIcon from '@/components/SvgIcon'
import TimeTooltip from '@/components/TimeTooltip'
import DocExportDialog from '@/components/DocExportDialog'
import PopoverUpdate from '@/components/PopoverUpdate'
+import { client } from '@/utils/http'
export default {
name: 'DocTable',
@@ -336,7 +354,9 @@ export default {
status: null,
filterEmptyFolder: null
},
- token: ''
+ token: '',
+ docDiffMap: {}, // 文档比对差异map {key: docId, value: diffType}
+ docParamIgnoreMap: {} // 文档参数比对是否忽略map {key: docId, value: 1 | 0}
}
},
computed: {
@@ -397,9 +417,22 @@ export default {
}
Object.assign(searchData, this.searchForm)
this.post('/doc/list-v2', searchData, function(resp) {
- this.tableData = this.convertTree(resp.data)
- callback && callback.call(this)
- this.loading = false
+ Promise.all([client.get('/doc/view/compare/doc/result', { moduleId: moduleId }),
+ client.get('/doc/view/ext/isIgnoreCompareParam/' + moduleId + '/list')]).then(resps => {
+ this.tableData = this.convertTree(resp.data)
+ callback && callback.call(this)
+ this.loading = false
+ // 组装文档比对差异map
+ this.docDiffMap = {}
+ resps[0].data.data.forEach(docDiff => {
+ this.docDiffMap[docDiff.docId] = docDiff.diffType
+ })
+ // 组装是否忽略参数比对map
+ this.docParamIgnoreMap = {}
+ resps[1].data.data.forEach(docInfoExt => {
+ this.docParamIgnoreMap[docInfoExt.docId] = docInfoExt.docValue
+ })
+ })
})
this.loadToken(moduleId)
},
@@ -547,6 +580,12 @@ export default {
this.loadTable()
})
},
+ onDocParamIgnore(row, status) {
+ this.get('/doc/monitor/ignore/status', { docId: row.id, moduleId: row.moduleId, status: status }, () => {
+ this.tipSuccess(this.$t('operateSuccess'))
+ this.loadTable()
+ })
+ },
onDocUnLock(row) {
this.post('/doc/unlock', { id: row.id }, () => {
this.tipSuccess(this.$t('operateSuccess'))
@@ -609,6 +648,15 @@ export default {
},
onCopyPostmanUrl() {
this.copyText(this.postmanUrl)
+ },
+ rowStyle({ row, rowIndex }) {
+ if (this.docDiffMap[row.id]) {
+ if (this.docDiffMap[row.id] === 1) {
+ return { 'color': '#C0C4CC' }
+ } else if (this.docDiffMap[row.id] === 2) {
+ return { 'color': '#F56C6C' }
+ }
+ }
}
}
}
diff --git a/front/src/views/project/ModuleSetting/ModSetting/index.vue b/front/src/views/project/ModuleSetting/ModSetting/index.vue
new file mode 100644
index 0000000000000000000000000000000000000000..226f8f2862e853f5b0f4759107613bcb74ce614a
--- /dev/null
+++ b/front/src/views/project/ModuleSetting/ModSetting/index.vue
@@ -0,0 +1,70 @@
+
+
+
+
+
+
+
+
+ {{ $t('save') }}
+
+
+
+
+
diff --git a/front/src/views/project/ModuleSetting/index.vue b/front/src/views/project/ModuleSetting/index.vue
index 5296fae8e911deac84e13c92322c507407944b66..160eae394a4c8d6f96fa2ebf7ebb72a62ca5ed04 100644
--- a/front/src/views/project/ModuleSetting/index.vue
+++ b/front/src/views/project/ModuleSetting/index.vue
@@ -31,6 +31,9 @@
+
+
+
@@ -42,10 +45,11 @@ import PopoverUpdate from '@/components/PopoverUpdate'
import SwaggerSetting from '@/components/ModuleSetting/SwaggerSetting'
import MeterSphereSetting from './MeterSphereSetting'
import EnvSetting from './EnvSetting'
+import ModSetting from './ModSetting'
export default {
name: 'ModuleSetting',
- components: { MeterSphereSetting, WeComSetting, DingDingSetting, PopoverUpdate, SwaggerSetting, EnvSetting },
+ components: { MeterSphereSetting, WeComSetting, DingDingSetting, PopoverUpdate, SwaggerSetting, EnvSetting, ModSetting },
props: {
projectId: {
type: String,
diff --git a/front/src/views/view/CompareResult/index.vue b/front/src/views/view/CompareResult/index.vue
new file mode 100644
index 0000000000000000000000000000000000000000..17b0cc068dfdbfe934333b2191413f987791406e
--- /dev/null
+++ b/front/src/views/view/CompareResult/index.vue
@@ -0,0 +1,224 @@
+
+
+
+
diff --git a/front/src/views/view/index.vue b/front/src/views/view/index.vue
index 27f716f9d3c10bd2c5c48eee0aebc7adf6bf3104..79bf1eec9a58380ba85c022b1ca8b97f7ba70db3 100644
--- a/front/src/views/view/index.vue
+++ b/front/src/views/view/index.vue
@@ -2,21 +2,25 @@
- {{ $t('apiInfo') }}
+ {{ $t('apiInfo') }}
- {{ $t('debugApi') }}
+ {{ $t('debugApi') }}
- Mock
+ Mock
+
+ 比对结果
+
+
- {{ $t('apiInfo') }}
+ {{ $t('apiInfo') }}
@@ -31,15 +35,18 @@ import DubboView from '@/components/DubboView'
import DocDebug from '@/components/DocDebug'
import Mock from '@/components/Mock'
import DocViewCustom from '@/components/DocViewCustom'
+import CompareResult from './CompareResult'
export default {
- components: { DocView, DocDebug, Mock, DubboView, DocViewCustom },
+ components: { DocView, DocDebug, Mock, DubboView, DocViewCustom, CompareResult },
data() {
return {
active: 'info',
item: {
type: 0
},
+ compareModifyParam: { pathParams: {}, headerParams: {}, requestParams: {}, responseParams: {}, queryParams: {}}, // 文档比对差异字段
+ compareNewParam: { pathParams: [], headerParams: [], requestParams: [], responseParams: [], queryParams: [] }, // 文档比对新增字段
infoItem: {},
debugItem: {},
mockItem: {}
@@ -61,17 +68,61 @@ export default {
if (docId) {
this.$nextTick(() => {
this.get('/doc/view/detail', { id: docId }, function(resp) {
- const data = resp.data
- this.setProjectId(data.projectId)
- this.initDocInfoView(data)
- this.item = data
- this.selectTab('info')
- this.setTitle(data.name)
- if (this.item.type === this.getEnums().DOC_TYPE.DUBBO) {
- this.$nextTick(() => {
- this.$refs.docViewDubbo.setData(this.item)
+ this.get('/doc/view/compare/param/result', { id: docId }, function(compareResp) {
+ const data = resp.data
+ this.setProjectId(data.projectId)
+ this.initDocInfoView(data)
+ this.item = data
+ this.selectTab('info')
+ this.setTitle(data.name)
+ if (this.item.type === this.getEnums().DOC_TYPE.DUBBO) {
+ this.$nextTick(() => {
+ this.$refs.docViewDubbo.setData(this.item)
+ })
+ }
+ const compareModifyData = compareResp.data.filter(item => item.diffType !== -1)
+ compareModifyData.forEach(item => {
+ switch (item.style) {
+ case 0:
+ this.compareModifyParam.pathParams[item.paramId] = item
+ break
+ case 1:
+ this.compareModifyParam.headerParams[item.paramId] = item
+ break
+ case 2:
+ this.compareModifyParam.requestParams[item.paramId] = item
+ break
+ case 3:
+ this.compareModifyParam.responseParams[item.paramId] = item
+ break
+ case 5:
+ this.compareModifyParam.queryParams[item.paramId] = item
+ break
+ default:
+ }
+ })
+ const compareNewData = compareResp.data.filter(item => item.diffType === -1)
+ compareNewData.forEach(item => {
+ switch (item.style) {
+ case 0:
+ this.compareNewParam.pathParams.push(item)
+ break
+ case 1:
+ this.compareNewParam.headerParams.push(item)
+ break
+ case 2:
+ this.compareNewParam.requestParams.push(item)
+ break
+ case 3:
+ this.compareNewParam.responseParams.push(item)
+ break
+ case 5:
+ this.compareNewParam.queryParams.push(item)
+ break
+ default:
+ }
})
- }
+ })
})
})
} else {
diff --git a/mysql.sql b/mysql.sql
index 66908b2f414192580faa49163a4702ecc5f7d057..59fe81d21305aa69e4e3aee2757e1594202f3779 100644
--- a/mysql.sql
+++ b/mysql.sql
@@ -731,6 +731,76 @@ CREATE TABLE `user_wecom_info` (
KEY `idx_userid` (`user_info_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+DROP TABLE IF EXISTS `doc_info_ext`;
+CREATE TABLE `doc_info_ext` (
+ `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ `doc_id` varchar(64) NOT NULL DEFAULT '' COMMENT '文档唯一key',
+ `module_id` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '模块id,module.id',
+ `doc_prop` varchar(64) NOT NULL COMMENT '文档属性',
+ `doc_value` varchar(128) NOT NULL COMMENT '文档属性值',
+ `gmt_create` datetime DEFAULT CURRENT_TIMESTAMP,
+ `gmt_modified` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+ PRIMARY KEY (`id`) USING BTREE,
+ KEY `idx_docid` (`doc_id`) USING BTREE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='文档信息扩展表';
+
+DROP TABLE IF EXISTS `doc_info_push`;
+CREATE TABLE `doc_info_push` (
+ `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ `doc_key` varchar(64) NOT NULL DEFAULT '' COMMENT '文档唯一key',
+ `name` varchar(128) NOT NULL DEFAULT '' COMMENT '文档名称',
+ `url` varchar(256) NOT NULL DEFAULT '' COMMENT '访问URL',
+ `http_method` varchar(12) NOT NULL DEFAULT '' COMMENT 'http方法',
+ `content_type` varchar(128) NOT NULL DEFAULT '' COMMENT 'contentType',
+ `module_id` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '模块id,module.id',
+ `is_compare` tinyint(4) NOT NULL DEFAULT '0' COMMENT '是否比对 1:已经比对,0:未比对',
+ `gmt_create` datetime DEFAULT CURRENT_TIMESTAMP,
+ PRIMARY KEY (`id`) USING BTREE,
+ KEY `idx_moduleid` (`module_id`) USING BTREE,
+ KEY `idx_dockey` (`doc_key`) USING BTREE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='文档信息推送表';
+
+DROP TABLE IF EXISTS `doc_param_push`;
+CREATE TABLE `doc_param_push` (
+ `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ `name` varchar(64) NOT NULL DEFAULT '' COMMENT '字段名称',
+ `type` varchar(64) NOT NULL DEFAULT 'String' COMMENT '字段类型',
+ `doc_id` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT 'doc_info.id',
+ `parent_id` bigint(20) unsigned NOT NULL DEFAULT '0',
+ `style` tinyint(4) NOT NULL DEFAULT '0' COMMENT '0:path, 1:header, 2:body参数,3:返回参数,4:错误码, 5:query参数',
+ `order_index` int(11) NOT NULL DEFAULT '0' COMMENT '排序索引',
+ `gmt_create` datetime DEFAULT CURRENT_TIMESTAMP,
+ PRIMARY KEY (`id`) USING BTREE,
+ KEY `idx_docid` (`doc_id`) USING BTREE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='文档参数推送表';
+
+DROP TABLE IF EXISTS `doc_info_diff`;
+CREATE TABLE `doc_info_diff` (
+ `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ `doc_id` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT 'doc_info.id',
+ `diff_type` tinyint(4) NOT NULL COMMENT '差异类型: 1: 推送缺少文档 2: 文档比对差异',
+ `module_id` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '模块id,module.id',
+ `gmt_create` datetime DEFAULT CURRENT_TIMESTAMP,
+ PRIMARY KEY (`id`) USING BTREE,
+ KEY `idx_docid` (`doc_id`) USING BTREE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='文档差异表';
+
+DROP TABLE IF EXISTS `doc_param_diff`;
+CREATE TABLE `doc_param_diff` (
+ `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ `param_id` bigint(20) unsigned COMMENT '根据diff_type决定doc_param.id 或 doc_param_push.id',
+ `doc_id` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT 'doc_info.id',
+ `style` tinyint(4) NOT NULL DEFAULT '0' COMMENT '0:path, 1:header, 2:body参数,3:返回参数,4:错误码, 5:query参数',
+ `diff_type` tinyint(4) NOT NULL COMMENT '差异类型: 1: 推送参数缺少字段,-1: 推送参数多出字段, 2: 名称一致,类型不一致',
+ `order_index` int(11) NOT NULL DEFAULT '0' COMMENT '排序索引',
+ `diff_param_name` varchar(256) NOT NULL DEFAULT 'String' COMMENT '差异字段名称',
+ `diff_param_type` varchar(64) COMMENT '差异字段类型',
+ `gmt_create` datetime DEFAULT CURRENT_TIMESTAMP,
+ PRIMARY KEY (`id`) USING BTREE,
+ KEY `idx_docid` (`doc_id`) USING BTREE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='文档参数差异表';
+
+
INSERT INTO `constant_info` (`id`, `project_id`, `module_id`, `doc_id`, `content`, `gmt_create`, `gmt_modified`) VALUES
(1, 5, 0, 0, '
全局状态码
| HTTP状态码 | 说明 |
|---|
| 200 | OK |
| 400 | Bad Request |
| 401 | Unauthorized |
| 403 | Forbidden |
| 404 | Not Found |
| 405 | Method Not Allowed |
| 500 | Internal Server Error |
| 502 | Bad Gateway |
| 503 | Service Unavailable |
| 504 | Gateway Timeout |
', '2022-11-03 20:32:43', '2022-11-03 20:32:43'),
(2, 0, 5, 0, '
商品中心错误码
- 10001:商品不存在
- 10002:分类不存在
- 10003:商品已下架
- 10004:商品已售罄
', '2022-11-03 20:33:53', '2022-11-03 20:33:53');
diff --git a/server/boot/src/main/resources/META-INF/torna.properties b/server/boot/src/main/resources/META-INF/torna.properties
index b3f2cef1c0a77715f1800ac1c30d7d61cab3a39e..f0bf68ff46f9feb9a92099373815eed24367fb26 100644
--- a/server/boot/src/main/resources/META-INF/torna.properties
+++ b/server/boot/src/main/resources/META-INF/torna.properties
@@ -73,6 +73,8 @@ torna.view-config.response-hidden-columns=required,maxLength
# \u521D\u59CB\u6392\u5E8F\u503C
torna.view-config.init-order=10000
torna.view-config.compose-show-debug=true
+# \u662F\u5426\u4FDD\u5B58\u6240\u6709\u5FEB\u7167
+torna.snapshot.save.all=false
# \u65E5\u5FD7\u7B49\u7EA7
diff --git a/server/server-api/src/main/java/cn/torna/api/open/DocApi.java b/server/server-api/src/main/java/cn/torna/api/open/DocApi.java
index 1ae8aa289530f127d24539256a32e1f813acf6b1..bad845dca77ea4950afc3a4648bcaaddec0445d2 100644
--- a/server/server-api/src/main/java/cn/torna/api/open/DocApi.java
+++ b/server/server-api/src/main/java/cn/torna/api/open/DocApi.java
@@ -37,14 +37,7 @@ import cn.torna.dao.entity.DocParam;
import cn.torna.dao.entity.Module;
import cn.torna.dao.entity.Project;
import cn.torna.manager.tx.TornaTransactionManager;
-import cn.torna.service.DocDiffRecordService;
-import cn.torna.service.DocInfoService;
-import cn.torna.service.DocViewService;
-import cn.torna.service.MockConfigService;
-import cn.torna.service.ModuleConfigService;
-import cn.torna.service.ModuleEnvironmentService;
-import cn.torna.service.ProjectService;
-import cn.torna.service.UserMessageService;
+import cn.torna.service.*;
import cn.torna.service.dto.DocFolderCreateDTO;
import cn.torna.service.dto.DocInfoDTO;
import cn.torna.service.dto.DocMeta;
@@ -115,6 +108,9 @@ public class DocApi {
@Autowired
private DocInfoService docInfoService;
+ @Autowired
+ private DocInfoPushService docInfoPushService;
+
@Autowired
private ModuleConfigService moduleConfigService;
@@ -156,19 +152,28 @@ public class DocApi {
if (Boolean.parseBoolean(isPrint) || isPrintContent(module.getId())) {
log.info("【PUSH】推送内容:{}", JSON.toJSONString(param));
}
- // 允许有相同的目录
- String allowSameFolder = EnvironmentKeys.TORNA_PUSH_ALLOW_SAME_FOLDER.getValue();
- if (Boolean.parseBoolean(allowSameFolder)) {
- this.mergeSameFolder(param);
+ // 获取该模块是否开启监控
+ boolean isMonitor = moduleConfigService.getCommonConfigValue(module.getId(), "isMonitor", "0")
+ .equals("0") ? false : true;
+ if (isMonitor) {
+ log.info("===========开始更新推送文档信息===========");
+ ThreadPoolUtil.execute(() -> doMonitorPush(param, context));
} else {
- this.checkSameFolder(param);
- }
- String author = param.getAuthor();
- if (StringUtils.hasText(author)) {
- ApiUser user = (ApiUser) context.getApiUser();
- user.setNickname(author);
+ log.info("===========开始更新平台文档信息===========");
+ // 允许有相同的目录
+ String allowSameFolder = EnvironmentKeys.TORNA_PUSH_ALLOW_SAME_FOLDER.getValue();
+ if (Boolean.parseBoolean(allowSameFolder)) {
+ this.mergeSameFolder(param);
+ } else {
+ this.checkSameFolder(param);
+ }
+ String author = param.getAuthor();
+ if (StringUtils.hasText(author)) {
+ ApiUser user = (ApiUser) context.getApiUser();
+ user.setNickname(author);
+ }
+ ThreadPoolUtil.execute(() -> doPush(param, context));
}
- ThreadPoolUtil.execute(() -> doPush(param, context));
}
private void checkSameFolder(DocPushParam param) {
@@ -218,6 +223,34 @@ public class DocApi {
param.setApis(new ArrayList<>(folderItems.keySet()));
}
+ /**
+ * 监控比对推送
+ *
+ * @param param
+ * @param context
+ */
+ private void doMonitorPush(DocPushParam param, RequestContext context) {
+ Module module = context.getModule();
+ ThreadLocal
docPushItemParamThreadLocal = new ThreadLocal<>();
+ synchronized (lock) {
+ tornaTransactionManager.execute(() -> {
+ // 处理文档
+ for (DocPushItemParam detailPushParam : param.getApis()) {
+ docPushItemParamThreadLocal.set(detailPushParam);
+ this.saveDocItemPush(detailPushParam, module.getId());
+ }
+ log.info("完成模块【{}】推送文档更新,更新比对状态缓存", module.getName());
+ ScheduleTask.MODULE_COMPARE_STATUS_CACHE.put(module.getId(), true);
+ return true;
+ }, e -> {
+ DocPushItemParam docPushItemParam = docPushItemParamThreadLocal.get();
+ String paramInfo = JSON.toJSONString(docPushItemParam);
+ log.error("【PUSH_DOC】保存推送文档失败,模块名称:{},推送人:{},ip:{},token:{}, 文档信息:{}", module.getName(),
+ param.getAuthor(), context.getIp(), context.getToken(), paramInfo, e);
+ });
+ }
+ }
+
private void doPush(DocPushParam param, RequestContext context) {
Module module = context.getModule();
long moduleId = module.getId();
@@ -353,6 +386,23 @@ public class DocApi {
}
}
+ public void saveDocItemPush(DocPushItemParam param, long moduleId) {
+ if (Booleans.isTrue(param.getIsFolder())) {
+ List items = param.getItems();
+ if (items != null) {
+ for (DocPushItemParam item : items) {
+ this.saveDocItemPush(item, moduleId);
+ }
+ }
+ } else {
+ DocInfoDTO docInfoDTO = buildDocInfoDTO(param);
+ docInfoDTO.setType(DocTypeEnum.HTTP.getType());
+ formatUrl(docInfoDTO);
+ docInfoDTO.setModuleId(moduleId);
+ docInfoPushService.saveDocInfo(docInfoDTO);
+ }
+ }
+
public void pushDocItem(DocPushItemParam param, RequestContext context, Long parentId, PushContext pushContext) {
User user = context.getApiUser();
long moduleId = context.getModuleId();
diff --git a/server/server-dao/src/main/java/cn/torna/dao/entity/DocInfoDiff.java b/server/server-dao/src/main/java/cn/torna/dao/entity/DocInfoDiff.java
new file mode 100644
index 0000000000000000000000000000000000000000..cf64a4d78d319714d4c83783b6161d724ef7f90d
--- /dev/null
+++ b/server/server-dao/src/main/java/cn/torna/dao/entity/DocInfoDiff.java
@@ -0,0 +1,37 @@
+package cn.torna.dao.entity;
+
+import com.gitee.fastmybatis.annotation.Pk;
+import com.gitee.fastmybatis.annotation.PkStrategy;
+import com.gitee.fastmybatis.annotation.Table;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+/**
+ * 文档差异
+ *
+ * @Author jyh
+ * @Date 2025/10/30 16:34
+ * @Version 1.0
+ **/
+@Table(name = "doc_info_diff", pk = @Pk(name = "id", strategy = PkStrategy.INCREMENT))
+@Data
+public class DocInfoDiff {
+
+ /** 数据库字段:id */
+ private Long id;
+
+ /** doc_info.id, 数据库字段:doc_id */
+ private Long docId;
+
+ /**
+ * 模块id,module.id
+ */
+ private long moduleId;
+
+ /** 差异类型: 1: 推送缺少文档,2:文档比对差异 */
+ private Byte diffType;
+
+ /** 数据库字段:gmt_create */
+ private LocalDateTime gmtCreate;
+}
diff --git a/server/server-dao/src/main/java/cn/torna/dao/entity/DocInfoExt.java b/server/server-dao/src/main/java/cn/torna/dao/entity/DocInfoExt.java
new file mode 100644
index 0000000000000000000000000000000000000000..f2c75411fbba11ca9d437f380d8d0991580afc3d
--- /dev/null
+++ b/server/server-dao/src/main/java/cn/torna/dao/entity/DocInfoExt.java
@@ -0,0 +1,41 @@
+package cn.torna.dao.entity;
+
+import com.gitee.fastmybatis.annotation.Pk;
+import com.gitee.fastmybatis.annotation.PkStrategy;
+import com.gitee.fastmybatis.annotation.Table;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+/**
+ * 文档信息扩展表
+ *
+ * @Author jyh
+ * @Date 2025/11/3 09:02
+ * @Version 1.0
+ **/
+@Table(name = "doc_info_ext", pk = @Pk(name = "id", strategy = PkStrategy.INCREMENT))
+@Data
+public class DocInfoExt {
+
+ /** 数据库字段:id */
+ private Long id;
+
+ /** doc_info.id, 数据库字段:doc_id */
+ private Long docId;
+
+ /** 模块id,module.id */
+ private long moduleId;
+
+ /** 文档属性 */
+ private String docProp;
+
+ /** 文档属性值 */
+ private String docValue;
+
+ /** 数据库字段:gmt_create */
+ private LocalDateTime gmtCreate;
+
+ /** 数据库字段:gmt_modified */
+ private LocalDateTime gmtModified;
+}
diff --git a/server/server-dao/src/main/java/cn/torna/dao/entity/DocInfoPush.java b/server/server-dao/src/main/java/cn/torna/dao/entity/DocInfoPush.java
new file mode 100755
index 0000000000000000000000000000000000000000..dc4fd751d7814e3acc3d90bad8a50a04a8c9a0a3
--- /dev/null
+++ b/server/server-dao/src/main/java/cn/torna/dao/entity/DocInfoPush.java
@@ -0,0 +1,45 @@
+package cn.torna.dao.entity;
+
+import com.gitee.fastmybatis.annotation.Pk;
+import com.gitee.fastmybatis.annotation.PkStrategy;
+import com.gitee.fastmybatis.annotation.Table;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+
+/**
+ * 表名:doc_info_push
+ * 备注:文档推送信息
+ *
+ * @author jyh
+ */
+@Table(name = "doc_info_push", pk = @Pk(name = "id", strategy = PkStrategy.INCREMENT))
+@Data
+public class DocInfoPush {
+
+ /** 数据库字段:id */
+ private Long id;
+
+ /** 唯一id,接口规则:md5(module_id:parent_id:url:http_method)。分类规则:md5(module_id:parent_id:name), 数据库字段:doc_key */
+ private String docKey;
+
+ /** 访问URL, 数据库字段:url */
+ private String url;
+
+ /** http方法, 数据库字段:http_method */
+ private String httpMethod;
+
+ /** contentType, 数据库字段:content_type */
+ private String contentType;
+
+ /** 模块id,module.id, 数据库字段:module_id */
+ private Long moduleId;
+
+ /** 是否比对 1:已经比对,0:未比对 */
+ private Byte isCompare;
+
+ /** 数据库字段:gmt_create */
+ private LocalDateTime gmtCreate;
+
+}
diff --git a/server/server-dao/src/main/java/cn/torna/dao/entity/DocParamDiff.java b/server/server-dao/src/main/java/cn/torna/dao/entity/DocParamDiff.java
new file mode 100644
index 0000000000000000000000000000000000000000..efac157abbd8cc9649a708b27ec10537fe80003b
--- /dev/null
+++ b/server/server-dao/src/main/java/cn/torna/dao/entity/DocParamDiff.java
@@ -0,0 +1,47 @@
+package cn.torna.dao.entity;
+
+import com.gitee.fastmybatis.annotation.Pk;
+import com.gitee.fastmybatis.annotation.PkStrategy;
+import com.gitee.fastmybatis.annotation.Table;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+/**
+ * 文档参数差异
+ *
+ * @Author jyh
+ * @Date 2025/10/27 14:07
+ * @Version 1.0
+ **/
+@Table(name = "doc_param_diff", pk = @Pk(name = "id", strategy = PkStrategy.INCREMENT))
+@Data
+public class DocParamDiff {
+
+ /** 数据库字段:id */
+ private Long id;
+
+ /** 根据diff_type决定doc_param.id 或 doc_param_tmp.id */
+ private Long paramId;
+
+ /** doc_info.id, 数据库字段:doc_id */
+ private Long docId;
+
+ /** 0:path, 1:header, 2:请求参数,3:返回参数,4:错误码, 数据库字段:style */
+ private Byte style;
+
+ /** 差异类型: 1: 临时参数缺少字段,-1: 临时参数多出字段, 2: 名称一致,类型不一致 */
+ private Byte diffType;
+
+ /** 排序索引, 数据库字段:order_index */
+ private Integer orderIndex;
+
+ /** 差异字段名称 */
+ private String diffParamName;
+
+ /** 差异字段类型 */
+ private String diffParamType;
+
+ /** 数据库字段:gmt_create */
+ private LocalDateTime gmtCreate;
+}
diff --git a/server/server-dao/src/main/java/cn/torna/dao/entity/DocParamPush.java b/server/server-dao/src/main/java/cn/torna/dao/entity/DocParamPush.java
new file mode 100755
index 0000000000000000000000000000000000000000..ccca732aae88365061bee9dd79b7d017fa2f6947
--- /dev/null
+++ b/server/server-dao/src/main/java/cn/torna/dao/entity/DocParamPush.java
@@ -0,0 +1,45 @@
+package cn.torna.dao.entity;
+
+import com.gitee.fastmybatis.annotation.Pk;
+import com.gitee.fastmybatis.annotation.PkStrategy;
+import com.gitee.fastmybatis.annotation.Table;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+
+/**
+ * 表名:doc_param_push
+ * 备注:文档参数推送表
+ *
+ * @author jyh
+ */
+@Table(name = "doc_param_push", pk = @Pk(name = "id", strategy = PkStrategy.INCREMENT))
+@Data
+public class DocParamPush {
+
+ /** 数据库字段:id */
+ private Long id;
+
+ /** 字段名称, 数据库字段:name */
+ private String name;
+
+ /** 字段类型, 数据库字段:type */
+ private String type;
+
+ /** doc_info.id, 数据库字段:doc_id */
+ private Long docId;
+
+ /** 数据库字段:parent_id */
+ private Long parentId;
+
+ /** 0:path, 1:header, 2:请求参数,3:返回参数,4:错误码, 数据库字段:style */
+ private Byte style;
+
+ /** 排序索引, 数据库字段:order_index */
+ private Integer orderIndex;
+
+ /** 数据库字段:gmt_create */
+ private LocalDateTime gmtCreate;
+
+}
\ No newline at end of file
diff --git a/server/server-dao/src/main/java/cn/torna/dao/mapper/DocInfoDiffMapper.java b/server/server-dao/src/main/java/cn/torna/dao/mapper/DocInfoDiffMapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..96ee87d8f3e33a5df58134fb99e544509897d8a9
--- /dev/null
+++ b/server/server-dao/src/main/java/cn/torna/dao/mapper/DocInfoDiffMapper.java
@@ -0,0 +1,12 @@
+package cn.torna.dao.mapper;
+
+import cn.torna.dao.entity.DocInfoDiff;
+import com.gitee.fastmybatis.core.mapper.BaseMapper;
+
+/**
+ * @Author jyh
+ * @Date 2025/10/30 16:40
+ * @Version 1.0
+ **/
+public interface DocInfoDiffMapper extends BaseMapper {
+}
diff --git a/server/server-dao/src/main/java/cn/torna/dao/mapper/DocInfoExtMapper.java b/server/server-dao/src/main/java/cn/torna/dao/mapper/DocInfoExtMapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..2c218b901c8ca00140ac6a0144ddf55ce5a8f49d
--- /dev/null
+++ b/server/server-dao/src/main/java/cn/torna/dao/mapper/DocInfoExtMapper.java
@@ -0,0 +1,13 @@
+package cn.torna.dao.mapper;
+
+import cn.torna.dao.entity.DocInfoExt;
+import com.gitee.fastmybatis.core.mapper.BaseMapper;
+
+/**
+ *
+ * @Author jyh
+ * @Date 2025/11/3 09:09
+ * @Version 1.0
+ **/
+public interface DocInfoExtMapper extends BaseMapper {
+}
diff --git a/server/server-dao/src/main/java/cn/torna/dao/mapper/DocInfoPushMapper.java b/server/server-dao/src/main/java/cn/torna/dao/mapper/DocInfoPushMapper.java
new file mode 100755
index 0000000000000000000000000000000000000000..e4de8e18855678f8662de56754fd970dd2f77b6b
--- /dev/null
+++ b/server/server-dao/src/main/java/cn/torna/dao/mapper/DocInfoPushMapper.java
@@ -0,0 +1,11 @@
+package cn.torna.dao.mapper;
+
+import cn.torna.dao.entity.DocInfoPush;
+import com.gitee.fastmybatis.core.mapper.BaseMapper;
+
+/**
+ * @author jyh
+ */
+public interface DocInfoPushMapper extends BaseMapper {
+
+}
diff --git a/server/server-dao/src/main/java/cn/torna/dao/mapper/DocParamDiffMapper.java b/server/server-dao/src/main/java/cn/torna/dao/mapper/DocParamDiffMapper.java
new file mode 100755
index 0000000000000000000000000000000000000000..3b6b520166e2ee6039f283f80066271dc3c4e56b
--- /dev/null
+++ b/server/server-dao/src/main/java/cn/torna/dao/mapper/DocParamDiffMapper.java
@@ -0,0 +1,11 @@
+package cn.torna.dao.mapper;
+
+import cn.torna.dao.entity.DocParamDiff;
+import com.gitee.fastmybatis.core.mapper.BaseMapper;
+
+/**
+ * @author jyh
+ */
+public interface DocParamDiffMapper extends BaseMapper {
+
+}
diff --git a/server/server-dao/src/main/java/cn/torna/dao/mapper/DocParamPushMapper.java b/server/server-dao/src/main/java/cn/torna/dao/mapper/DocParamPushMapper.java
new file mode 100755
index 0000000000000000000000000000000000000000..4179ad885ee70d59d9cf8ba8b24929a882b37e65
--- /dev/null
+++ b/server/server-dao/src/main/java/cn/torna/dao/mapper/DocParamPushMapper.java
@@ -0,0 +1,11 @@
+package cn.torna.dao.mapper;
+
+import cn.torna.dao.entity.DocParamPush;
+import com.gitee.fastmybatis.core.mapper.BaseMapper;
+
+/**
+ * @author jyh
+ */
+public interface DocParamPushMapper extends BaseMapper {
+
+}
diff --git a/server/server-service/src/main/java/cn/torna/service/DocDiffRecordService.java b/server/server-service/src/main/java/cn/torna/service/DocDiffRecordService.java
index 25af52b408320b4f8715672b855f23b3703ce22c..280123d387b60a031b6c9b991b2824ade12a9021 100644
--- a/server/server-service/src/main/java/cn/torna/service/DocDiffRecordService.java
+++ b/server/server-service/src/main/java/cn/torna/service/DocDiffRecordService.java
@@ -62,17 +62,18 @@ public class DocDiffRecordService extends BaseLambdaService listDocDiff(Long docId) {
- DocInfo docInfo = docInfoService.getById(docId);
- String version = docInfo.getVersion();
- List diffRecordList;
- // 如果有版本号,只能看截止到当前版本的记录
- if (StringUtils.hasText(version) && !"-".equals(version)) {
- diffRecordList = listByField(DocDiffRecord::getDocId, docId);
- } else {
- // 可以查看所有变更记录
- String docKey = docInfoService.getDocKey(docId);
- diffRecordList = listByField(DocDiffRecord::getDocKey, docKey);
- }
+// DocInfo docInfo = docInfoService.getById(docId);
+// String version = docInfo.getVersion();
+// List diffRecordList;
+// // 如果有版本号,只能看截止到当前版本的记录
+// if (StringUtils.hasText(version) && !"-".equals(version)) {
+// diffRecordList = listByField(DocDiffRecord::getDocId, docId);
+// } else {
+// // 可以查看所有变更记录
+// String docKey = docInfoService.getDocKey(docId);
+// diffRecordList = listByField(DocDiffRecord::getDocKey, docKey);
+// }
+ List diffRecordList = listByField(DocDiffRecord::getDocId, docId);
if (CollectionUtils.isEmpty(diffRecordList)) {
return Collections.emptyList();
}
diff --git a/server/server-service/src/main/java/cn/torna/service/DocInfoPushService.java b/server/server-service/src/main/java/cn/torna/service/DocInfoPushService.java
new file mode 100644
index 0000000000000000000000000000000000000000..e1262fb1e2a01ff6e1e8fd9f3bdf9f84478b02b2
--- /dev/null
+++ b/server/server-service/src/main/java/cn/torna/service/DocInfoPushService.java
@@ -0,0 +1,67 @@
+package cn.torna.service;
+
+import cn.torna.common.enums.ParamStyleEnum;
+import cn.torna.common.util.CopyUtil;
+import cn.torna.dao.entity.DocInfoPush;
+import cn.torna.dao.mapper.DocInfoPushMapper;
+import cn.torna.service.dto.DocInfoDTO;
+import com.gitee.fastmybatis.core.support.BaseLambdaService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+/**
+ * @Author jyh
+ * @Date 2025/10/22 14:15
+ * @Version 1.0
+ **/
+@Service
+@Slf4j
+public class DocInfoPushService extends BaseLambdaService {
+
+ @Autowired
+ private DocParamPushService docParamPushService;
+
+ /**
+ * 保存推送文档信息
+ */
+ @Transactional(rollbackFor = Exception.class)
+ public synchronized void saveDocInfo(DocInfoDTO docInfoDTO) {
+ // 根据doc_key删除原先信息
+ String docKey = docInfoDTO.buildDocKey();
+ docInfoDTO.setDocKey(docKey);
+ DocInfoPush docInfoPush = this.getByField(DocInfoPush::getDocKey, docKey);
+ if (docInfoPush != null) {
+ this.deleteByColumn(DocInfoPush::getDocKey, docKey);
+ docParamPushService.deleteParamByDocId(docInfoPush.getId());
+ }
+ // 新增基本信息
+ long docId = insertDocInfoPush(docInfoDTO);
+ // 新增参数
+ this.saveParams(docId, docInfoDTO);
+ }
+
+ private void saveParams(long docId, DocInfoDTO docInfoDTO) {
+ docParamPushService.saveParams(docId, docInfoDTO.getPathParams(), ParamStyleEnum.PATH);
+ docParamPushService.saveParams(docId, docInfoDTO.getHeaderParams(), ParamStyleEnum.HEADER);
+ docParamPushService.saveParams(docId, docInfoDTO.getQueryParams(), ParamStyleEnum.QUERY);
+ docParamPushService.saveParams(docId, docInfoDTO.getRequestParams(), ParamStyleEnum.REQUEST);
+ docParamPushService.saveParams(docId, docInfoDTO.getResponseParams(), ParamStyleEnum.RESPONSE);
+ docParamPushService.saveParams(docId, docInfoDTO.getErrorCodeParams(), ParamStyleEnum.ERROR_CODE);
+ }
+
+ private long insertDocInfoPush(DocInfoDTO docInfoDTO) {
+ DocInfoPush docInfoPush = CopyUtil.copyBean(docInfoDTO, DocInfoPush::new);
+ this.getMapper().saveIgnoreNull(docInfoPush);
+ // 修复使用非MYSQL数据库插入数据id不返回问题
+ if (docInfoPush.getId() == null) {
+ DocInfoPush one = getByField(DocInfoPush::getDocKey, docInfoPush.getDocKey());
+ if (one != null) {
+ docInfoPush.setId(one.getId());
+ }
+ }
+ return docInfoPush.getId();
+ }
+
+}
diff --git a/server/server-service/src/main/java/cn/torna/service/DocInfoService.java b/server/server-service/src/main/java/cn/torna/service/DocInfoService.java
index 0a8040db947654323d8fc58b689e67c1d1fae572..b2cade9b8c5532cd303af634535bbcb661dbafc3 100755
--- a/server/server-service/src/main/java/cn/torna/service/DocInfoService.java
+++ b/server/server-service/src/main/java/cn/torna/service/DocInfoService.java
@@ -17,29 +17,27 @@ import cn.torna.common.util.IdGen;
import cn.torna.common.util.Markdown2HtmlUtil;
import cn.torna.common.util.TreeUtil;
import cn.torna.dao.entity.DocInfo;
+import cn.torna.dao.entity.DocInfoDiff;
+import cn.torna.dao.entity.DocInfoExt;
+import cn.torna.dao.entity.DocInfoPush;
import cn.torna.dao.entity.DocParam;
+import cn.torna.dao.entity.DocParamDiff;
+import cn.torna.dao.entity.DocParamPush;
import cn.torna.dao.entity.EnumInfo;
import cn.torna.dao.entity.Module;
import cn.torna.dao.entity.ModuleEnvironment;
import cn.torna.dao.entity.ModuleEnvironmentParam;
import cn.torna.dao.entity.UserDingtalkInfo;
import cn.torna.dao.entity.UserWeComInfo;
+import cn.torna.dao.mapper.DocInfoDiffMapper;
+import cn.torna.dao.mapper.DocInfoExtMapper;
import cn.torna.dao.mapper.DocInfoMapper;
+import cn.torna.dao.mapper.DocInfoPushMapper;
+import cn.torna.dao.mapper.DocParamDiffMapper;
import cn.torna.dao.mapper.UserDingtalkInfoMapper;
import cn.torna.dao.mapper.UserWeComInfoMapper;
import cn.torna.manager.doc.DataType;
-import cn.torna.service.dto.DocFolderCreateDTO;
-import cn.torna.service.dto.DocInfoDTO;
-import cn.torna.service.dto.DocItemCreateDTO;
-import cn.torna.service.dto.DocListFormDTO;
-import cn.torna.service.dto.DocMeta;
-import cn.torna.service.dto.DocParamDTO;
-import cn.torna.service.dto.DocRefDTO;
-import cn.torna.service.dto.DubboInfoDTO;
-import cn.torna.service.dto.EnumInfoDTO;
-import cn.torna.service.dto.EnumItemDTO;
-import cn.torna.service.dto.ModuleEnvironmentDTO;
-import cn.torna.service.dto.UpdateDocFolderDTO;
+import cn.torna.service.dto.*;
import cn.torna.service.event.DocAddEvent;
import cn.torna.service.event.DocUpdateEvent;
import com.gitee.fastmybatis.core.PageInfo;
@@ -68,6 +66,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.Set;
import java.util.stream.Collectors;
/**
@@ -82,6 +81,9 @@ public class DocInfoService extends BaseLambdaService {
@Autowired
private DocParamService docParamService;
+ @Autowired
+ private DocParamPushService docParamPushService;
+
@Autowired
private ModuleConfigService moduleConfigService;
@@ -124,6 +126,17 @@ public class DocInfoService extends BaseLambdaService {
@Autowired
private MockConfigService mockConfigService;
+ @Autowired
+ private DocInfoPushMapper docInfoPushMapper;
+
+ @Autowired
+ private DocParamDiffMapper docParamDiffMapper;
+
+ @Autowired
+ private DocInfoDiffMapper docInfoDiffMapper;
+
+ @Autowired
+ private DocInfoExtMapper docInfoExtMapper;
/**
* 查询模块下的所有文档
@@ -267,6 +280,34 @@ public class DocInfoService extends BaseLambdaService {
return getDocDetail(docInfo);
}
+ /**
+ * 返回参数比对结果
+ *
+ * @param docId
+ * @return
+ */
+ public List getParamCompareResult(long docId) {
+ Query query = Query.create().eq("doc_id", docId).orderByAsc("order_index");
+ List docParamDiffList = docParamDiffMapper.list(query);
+ return docParamDiffList.stream().map(item -> CopyUtil.copyBean(item, DocParamDiffDTO::new))
+ .collect(Collectors.toList());
+ }
+
+ /**
+ * 获取文档比对结果
+ *
+ * @param moduleId
+ * @return
+ */
+ public List getDocCompareResult(Long moduleId) {
+ List docInfoDiffList = docInfoDiffMapper.listByField(DocInfoDiff::getModuleId, moduleId);
+ return docInfoDiffList.stream().map(docInfoDiff -> {
+ DocInfoDiffDTO docInfoDiffDTO = new DocInfoDiffDTO();
+ CopyUtil.copyProperties(docInfoDiff, docInfoDiffDTO);
+ return docInfoDiffDTO;
+ }).collect(Collectors.toList());
+ }
+
/**
* 返回文档详情
*
@@ -992,4 +1033,237 @@ public class DocInfoService extends BaseLambdaService {
return listByField(DocInfo::getParentId, parentId);
}
+ /**
+ * 按模块比对文档
+ *
+ * @param moduleId
+ */
+ public void compareMonitorDoc(long moduleId) {
+ // 获取平台文档
+ List docInfoList = this.listDocDetail(moduleId);
+ Map docInfoMap = docInfoList.stream().filter(item -> item.getIsFolder() == 0)
+ .collect(Collectors.toMap(item -> item.getDocKey(),
+ item -> item, (existing, replacement) -> existing));
+ // 获取推送文档
+ List docInfoPushList = docInfoPushMapper.listByField(DocInfoPush::getModuleId, moduleId);
+ Map docInfoPushMap = docInfoPushList.stream().collect(Collectors.toMap(item -> item.getDocKey(),
+ item -> item, (existing, replacement) -> existing));
+
+ // 获取文档参数忽略比对数据
+ Query docInfoExtQuery = Query.create().eq("module_id", moduleId).eq("doc_prop", "isIgnoreCompareParam");
+ List ignoreCompareParamDocIds = docInfoExtMapper.listUniqueValue(docInfoExtQuery, DocInfoExt::getDocId);
+ // 遍历比对文档
+ docInfoMap.forEach((docKey, docInfoDTO) -> {
+ if (docInfoPushMap.get(docKey) != null) {
+ if (!ignoreCompareParamDocIds.contains(docInfoDTO.getId())) { // 未忽略参数比对
+ DocInfoPush docInfoPush = docInfoPushMap.get(docKey);
+ log.info("开始比对文档【id:{},name:{},url:{},httpMethod:{}】", docInfoDTO.getId(), docInfoDTO.getName(),
+ docInfoDTO.getUrl(), docInfoDTO.getHttpMethod());
+ // 获取平台文档参数
+ List params = docParamService.listByField(DocParam::getDocId, docInfoDTO.getId());
+ params.sort(Comparator.comparing(DocParam::getOrderIndex));
+ Map> paramsMap = params.stream()
+ .collect(Collectors.groupingBy(DocParam::getStyle));
+ // 获取推送文档参数
+ List pushParams = docParamPushService.listByField(DocParamPush::getDocId, docInfoPush.getId());
+ pushParams.sort(Comparator.comparing(DocParamPush::getOrderIndex));
+ Map> pushParamsMap = pushParams.stream()
+ .collect(Collectors.groupingBy(DocParamPush::getStyle));
+ // 按类型比对参数
+ paramsMap.forEach((paramType, docParams) -> {
+ log.info("开始比对参数类型【{}】", paramType);
+ List pushDocParams = pushParamsMap.get(paramType) == null ? new ArrayList<>() : pushParamsMap.get(paramType);
+ List docParamDiffList = compareParam(docParams, pushDocParams);
+ if (docParamDiffList.size() > 0) {
+ log.info("【id:{},name:{},url:{},httpMethod:{}】参数类型【{}】有差异,更新比对结果", docInfoDTO.getId(), docInfoDTO.getName(),
+ docInfoDTO.getUrl(), docInfoDTO.getHttpMethod(), paramType);
+ docInfoDiffMapper.deleteByColumn(DocInfoDiff::getDocId, docInfoDTO.getId());
+ saveDocInfoCompareResult(docInfoDTO, moduleId, (byte) 2);
+ } else {
+ log.debug("【id:{},name:{},url:{},httpMethod:{}】参数类型【{}】无差异,清除比对结果", docInfoDTO.getId(), docInfoDTO.getName(),
+ docInfoDTO.getUrl(), docInfoDTO.getHttpMethod(), paramType);
+ docInfoDiffMapper.deleteByColumn(DocInfoDiff::getDocId, docInfoDTO.getId());
+ }
+ updateDocParamDiffByType(docParamDiffList, docInfoDTO.getId(), paramType);
+ });
+ }
+ } else {
+ log.info("平台文档【id:{},name:{},url:{},httpMethod:{}】无推送内容!", docInfoDTO.getId(), docInfoDTO.getName(),
+ docInfoDTO.getUrl(), docInfoDTO.getHttpMethod());
+ docInfoDiffMapper.deleteByColumn(DocInfoDiff::getDocId, docInfoDTO.getId());
+ saveDocInfoCompareResult(docInfoDTO, moduleId, (byte) 1);
+ }
+ });
+ log.info("完成模块【{}】比对,更新比对状态缓存", moduleId);
+ ScheduleTask.MODULE_COMPARE_STATUS_CACHE.put(moduleId, false);
+ }
+
+ /**
+ * 是否忽略参数比较(应用于动态参数)
+ * 逻辑:status == false 直接删除条目,否则新增条目 docValue = 1
+ *
+ * @param docId
+ * @param moduleId
+ * @param status
+ */
+ @Transactional(rollbackFor = Exception.class)
+ public void ignoreCompareParam(Long docId, Long moduleId, boolean status) {
+ if (status) {
+ DocInfoExt docInfoExt = new DocInfoExt();
+ docInfoExt.setDocId(docId);
+ docInfoExt.setModuleId(moduleId);
+ docInfoExt.setDocProp("isIgnoreCompareParam");
+ docInfoExt.setDocValue("1");
+ docInfoExtMapper.save(docInfoExt);
+
+ log.info("平台文档【id:{}】开启禁止参数比对,清除之前比对结果!", docId);
+ docParamDiffMapper.deleteByColumn(DocParamDiff::getDocId, docId);
+ Query query = Query.create().eq("doc_id", docId).eq("diff_type", (byte) 2);
+ docInfoDiffMapper.deleteByQuery(query);
+ } else {
+ Query query = Query.create()
+ .eq("doc_prop", "isIgnoreCompareParam")
+ .eq("doc_id", docId)
+ .eq("module_id", moduleId);
+ docInfoExtMapper.deleteByQuery(query);
+
+ }
+ }
+
+ /**
+ * 列出该文档属性和模块ID下所有扩展属性
+ *
+ * @param docProp
+ * @param moduleId
+ * @return 扩展属性列表
+ */
+ public List listDocExtByPropAndModuleId(String docProp, Long moduleId) {
+ Query query = Query.create().eq("doc_prop", docProp).eq("module_id", moduleId);
+ List docInfoExtList = docInfoExtMapper.list(query);
+ return docInfoExtList.stream().map(item -> {
+ DocInfoExtDTO docInfoExtDTO = new DocInfoExtDTO();
+ CopyUtil.copyProperties(item, docInfoExtDTO);
+ return docInfoExtDTO;
+ }).collect(Collectors.toList());
+ }
+
+ private void saveDocInfoCompareResult(DocInfoDTO docInfoDTO, long moduleId, byte diffType) {
+ DocInfoDiff docInfoDiff = new DocInfoDiff();
+ docInfoDiff.setDocId(docInfoDTO.getId());
+ docInfoDiff.setDiffType(diffType);
+ docInfoDiff.setModuleId(moduleId);
+ docInfoDiffMapper.save(docInfoDiff);
+ }
+
+ /**
+ * 保存比对结果列表
+ *
+ * @param docParamDiffList
+ * @param docId
+ * @param paramType
+ */
+ private void updateDocParamDiffByType(List docParamDiffList, long docId, Byte paramType) {
+ Query query = Query.create().eq("doc_id", docId).eq("style", paramType);
+ docParamDiffMapper.deleteByQuery(query);
+ docParamDiffList.forEach(docParamDiff -> {
+ docParamDiff.setDocId(docId);
+ docParamDiff.setStyle(paramType);
+ docParamDiffMapper.save(docParamDiff);
+ });
+ }
+
+ /**
+ * 比对平台参数与推送文档参数差异
+ *
+ * @param docParams
+ * @param pushDocParams
+ * @return 比对结果列表
+ */
+ private List compareParam(List docParams, List pushDocParams) {
+ // DocParam -->> DocParamTmpDTO
+ List docParamDTOS = new ArrayList<>();
+ for (DocParam item : docParams) {
+ DocParamPushDTO docParamPushDTO = new DocParamPushDTO();
+ CopyUtil.copyProperties(item, docParamPushDTO);
+ docParamDTOS.add(docParamPushDTO);
+ }
+ // 组装全名称路径的参数映射
+ Map paramNamePathMap = new HashMap<>();
+ this.convertTree(docParamDTOS, 0L, null, paramNamePathMap);
+ Set paramNamePathSet = paramNamePathMap.keySet();
+ // DocParamTmp -->> DocParamTmpDTO
+ List docParamPushDTOS = new ArrayList<>();
+ for (DocParamPush item : pushDocParams) {
+ DocParamPushDTO docParamPushDTO = new DocParamPushDTO();
+ CopyUtil.copyProperties(item, docParamPushDTO);
+ docParamPushDTOS.add(docParamPushDTO);
+ }
+ Map tmpParamNamePathMap = new HashMap<>();
+ this.convertTree(docParamPushDTOS, 0L, null, tmpParamNamePathMap);
+ Set tmpParamNameSet = tmpParamNamePathMap.keySet();
+ // 以平台文档维度比对参数
+ Map nameDiffMap = new HashMap<>(); // key: 参数全路径名(包含父路径,'-'号分隔), value: DocParamDiff
+ paramNamePathSet.forEach(paramName -> {
+ if (tmpParamNameSet.contains(paramName)) { // 包含字段
+ if (!paramNamePathMap.get(paramName).getType().equals(tmpParamNamePathMap.get(paramName).getType())) { // 类型不一致
+ DocParamDiff docParamDiff = new DocParamDiff();
+ docParamDiff.setParamId(paramNamePathMap.get(paramName).getId());
+ docParamDiff.setDiffType((byte) 2);
+ docParamDiff.setDiffParamType(tmpParamNamePathMap.get(paramName).getType());
+ docParamDiff.setOrderIndex(paramNamePathMap.get(paramName).getOrderIndex());
+ nameDiffMap.put(paramName, docParamDiff);
+ }
+ } else { // 临时参数缺少字段
+ DocParamDiff docParamDiff = new DocParamDiff();
+ docParamDiff.setParamId(paramNamePathMap.get(paramName).getId());
+ docParamDiff.setDiffType((byte) 1);
+ docParamDiff.setOrderIndex(paramNamePathMap.get(paramName).getOrderIndex());
+ nameDiffMap.put(paramName, docParamDiff);
+ }
+ });
+ // 以推送文档维度比对参数
+ tmpParamNameSet.forEach(tmpParamName -> {
+ if (!paramNamePathSet.contains(tmpParamName)) { // 临时参数多出字段
+ DocParamDiff docParamDiff = new DocParamDiff();
+ docParamDiff.setDiffType((byte) -1);
+ docParamDiff.setDiffParamType(tmpParamNamePathMap.get(tmpParamName).getType());
+ docParamDiff.setOrderIndex(tmpParamNamePathMap.get(tmpParamName).getOrderIndex());
+ nameDiffMap.put(tmpParamName, docParamDiff);
+ }
+ });
+ // 组装比对结果
+ List docParamDiffList = new ArrayList<>();
+ nameDiffMap.forEach((paramName, docParamDiff) -> {
+ docParamDiff.setDiffParamName(paramName);
+ docParamDiffList.add(docParamDiff);
+ });
+ return docParamDiffList;
+ }
+
+ /**
+ * list转换成tree
+ *
+ * @param list
+ * @param parentId 当前父节点id
+ * @param parentName 拼接父节点名称
+ * @param paramNamePathMap 记录全路径名称->param映射
+ * @return
+ */
+ private List convertTree(List list, long parentId, String parentName, Map paramNamePathMap) {
+ if (list == null) {
+ return Collections.emptyList();
+ }
+ List temp = new ArrayList<>();
+ for (int i = 0; i < list.size(); i++) {
+ DocParamPushDTO item = list.get(i);
+ if (Objects.equals(item.getParentId(), parentId)) {
+ String fullPath = (parentId == 0L ? item.getName() : parentName + "-" + item.getName());
+ List children = convertTree(list, item.getId(), fullPath, paramNamePathMap);
+ paramNamePathMap.put(fullPath, item);
+ item.setChildren(children);
+ temp.add(item);
+ }
+ }
+ return temp;
+ }
}
diff --git a/server/server-service/src/main/java/cn/torna/service/DocParamPushService.java b/server/server-service/src/main/java/cn/torna/service/DocParamPushService.java
new file mode 100644
index 0000000000000000000000000000000000000000..4640c35a74079fb95a722be7fe52ade81a2de919
--- /dev/null
+++ b/server/server-service/src/main/java/cn/torna/service/DocParamPushService.java
@@ -0,0 +1,49 @@
+package cn.torna.service;
+
+import cn.torna.common.enums.ParamStyleEnum;
+import cn.torna.dao.entity.DocParamPush;
+import cn.torna.dao.mapper.DocParamPushMapper;
+import cn.torna.service.dto.DocParamDTO;
+import com.gitee.fastmybatis.core.support.BaseLambdaService;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+/**
+ * @Author jyh
+ * @Date 2025/10/22 16:59
+ * @Version 1.0
+ **/
+@Service
+public class DocParamPushService extends BaseLambdaService {
+
+ public void saveParams(long docId, List docParamDTOS, ParamStyleEnum paramStyleEnum) {
+ for (DocParamDTO docParamDTO : docParamDTOS) {
+ this.doSave(docParamDTO, 0L, docId, paramStyleEnum);
+ }
+ }
+
+ public void deleteParamByDocId(long docId) {
+ this.deleteByColumn(DocParamPush::getDocId, docId);
+ }
+
+ private void doSave(DocParamDTO docParamDTO, long parentId, long docId, ParamStyleEnum paramStyleEnum) {
+ DocParamPush docParamPush = new DocParamPush();
+ docParamDTO.setParentId(parentId);
+ docParamPush.setName(docParamDTO.getName());
+ docParamPush.setType(docParamDTO.getType());
+ docParamPush.setDocId(docId);
+ docParamPush.setParentId(parentId);
+ docParamPush.setStyle(paramStyleEnum.getStyle());
+ docParamPush.setOrderIndex(docParamDTO.getOrderIndex());
+ this.save(docParamPush);
+
+ List children = docParamDTO.getChildren();
+ if (children != null) {
+ Long pid = docParamPush.getId();
+ for (DocParamDTO child : children) {
+ this.doSave(child, pid, docId, paramStyleEnum);
+ }
+ }
+ }
+}
diff --git a/server/server-service/src/main/java/cn/torna/service/DocSnapshotService.java b/server/server-service/src/main/java/cn/torna/service/DocSnapshotService.java
index d2d93ef059a918ca21ef66013b6b07443f4b23b6..c861f54b78211ab2aa62558f63de6279cbaf135a 100644
--- a/server/server-service/src/main/java/cn/torna/service/DocSnapshotService.java
+++ b/server/server-service/src/main/java/cn/torna/service/DocSnapshotService.java
@@ -16,6 +16,7 @@ import com.gitee.fastmybatis.core.support.BaseLambdaService;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
@@ -32,6 +33,9 @@ import java.util.stream.Collectors;
@Slf4j
public class DocSnapshotService extends BaseLambdaService {
+ @Value("${torna.snapshot.save.all:false}")
+ private boolean saveAllSnapshot;
+
@Autowired
private DocInfoService docInfoService;
@@ -73,10 +77,12 @@ public class DocSnapshotService extends BaseLambdaService MODULE_COMPARE_STATUS_CACHE = new ConcurrentHashMap<>();
+
@Autowired
private DocDiffRecordService docDiffRecordService;
+ @Autowired
+ private DocInfoService docInfoService;
+
+ @Autowired
+ private ModuleConfigMapper moduleConfigMapper;
@Scheduled(initialDelay = 1000, fixedDelay = 15 * 1000)
public void saveDocDiff() {
@@ -31,5 +51,21 @@ public class ScheduleTask {
}
}
-
+ /**
+ * 比对文档差异
+ */
+ @Scheduled(initialDelay = 300 * 1000, fixedDelay = 300 * 1000)
+ public void compareDocDiff() {
+ Query query = Query.create()
+ .eq("type", ModuleConfigTypeEnum.COMMON)
+ .eq("config_key", "isMonitor")
+ .eq("config_value", "1");
+ List compareModuleIdList = moduleConfigMapper.listUniqueValue(query, ModuleConfig::getModuleId);
+ compareModuleIdList.forEach(moduleId -> {
+ if (MODULE_COMPARE_STATUS_CACHE.getOrDefault(moduleId, false)) {
+ log.info("==========开始比对模块【{}】推送文档=====", moduleId);
+ docInfoService.compareMonitorDoc(moduleId);
+ }
+ });
+ }
}
diff --git a/server/server-service/src/main/java/cn/torna/service/dto/DocInfoDiffDTO.java b/server/server-service/src/main/java/cn/torna/service/dto/DocInfoDiffDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..9673f714b2cf40a98916a5b385d135ecad201ce4
--- /dev/null
+++ b/server/server-service/src/main/java/cn/torna/service/dto/DocInfoDiffDTO.java
@@ -0,0 +1,37 @@
+package cn.torna.service.dto;
+
+import cn.torna.common.support.IdCodec;
+import com.alibaba.fastjson.annotation.JSONField;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+/**
+ *
+ * @Author jyh
+ * @Date 2025/10/30 16:36
+ * @Version 1.0
+ **/
+@Data
+public class DocInfoDiffDTO {
+
+ @JSONField(serializeUsing = IdCodec.class, deserializeUsing = IdCodec.class)
+ private Long id;
+
+ /**
+ * doc_info.id, 数据库字段:doc_id
+ */
+ @JSONField(serializeUsing = IdCodec.class, deserializeUsing = IdCodec.class)
+ private Long docId;
+
+ /**
+ * 模块id,module.id
+ */
+ private long moduleId;
+
+ /** 差异类型: 1: 推送缺少文档,2:文档比对差异 */
+ private Byte diffType;
+
+ /** 数据库字段:gmt_create */
+ private LocalDateTime gmtCreate;
+}
diff --git a/server/server-service/src/main/java/cn/torna/service/dto/DocInfoExtDTO.java b/server/server-service/src/main/java/cn/torna/service/dto/DocInfoExtDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..50b632623114661dfb8ad2457b933850e744726f
--- /dev/null
+++ b/server/server-service/src/main/java/cn/torna/service/dto/DocInfoExtDTO.java
@@ -0,0 +1,41 @@
+package cn.torna.service.dto;
+
+import cn.torna.common.support.IdCodec;
+import com.alibaba.fastjson.annotation.JSONField;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+/**
+ *
+ * @Author jyh
+ * @Date 2025/11/3 09:07
+ * @Version 1.0
+ **/
+@Data
+public class DocInfoExtDTO {
+
+ @JSONField(serializeUsing = IdCodec.class, deserializeUsing = IdCodec.class)
+ private Long id;
+
+ /**
+ * doc_info.id, 数据库字段:doc_id
+ */
+ @JSONField(serializeUsing = IdCodec.class, deserializeUsing = IdCodec.class)
+ private Long docId;
+
+ /** 模块id,module.id */
+ private long moduleId;
+
+ /** 文档属性 */
+ private String docProp;
+
+ /** 文档属性值 */
+ private String docValue;
+
+ /** 数据库字段:gmt_create */
+ private LocalDateTime gmtCreate;
+
+ /** 数据库字段:gmt_modified */
+ private LocalDateTime gmtModified;
+}
diff --git a/server/server-service/src/main/java/cn/torna/service/dto/DocParamDiffDTO.java b/server/server-service/src/main/java/cn/torna/service/dto/DocParamDiffDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..227ca6f6fe99b8fc0c2005462d12ccbd50533493
--- /dev/null
+++ b/server/server-service/src/main/java/cn/torna/service/dto/DocParamDiffDTO.java
@@ -0,0 +1,59 @@
+package cn.torna.service.dto;
+
+import cn.torna.common.support.IdCodec;
+import com.alibaba.fastjson.annotation.JSONField;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+/**
+ * @Author jyh
+ * @Date 2025/10/24 14:53
+ * @Version 1.0
+ **/
+@Data
+public class DocParamDiffDTO {
+
+ @JSONField(serializeUsing = IdCodec.class, deserializeUsing = IdCodec.class)
+ private Long id;
+
+ /**
+ * 根据diff_type决定doc_param.id 或 doc_param_tmp.id
+ */
+ @JSONField(serializeUsing = IdCodec.class, deserializeUsing = IdCodec.class)
+ private Long paramId;
+
+ /**
+ * doc_info.id, 数据库字段:doc_id
+ */
+ private Long docId;
+
+ /**
+ * 0:path, 1:header, 2:请求参数,3:返回参数,4:错误码, 数据库字段:style
+ */
+ private Byte style;
+
+ /**
+ * 差异类型: 1: 临时参数缺少字段,-1: 临时参数多出字段, 2: 名称一致,类型不一致
+ */
+ private Byte diffType;
+
+ /** 排序索引, 数据库字段:order_index */
+ private Integer orderIndex;
+
+ /**
+ * 差异字段名称
+ */
+ private String diffParamName;
+
+ /**
+ * 差异字段类型
+ */
+ private String diffParamType;
+
+ /**
+ * 数据库字段:gmt_create
+ */
+ private LocalDateTime gmtCreate;
+
+}
diff --git a/server/server-service/src/main/java/cn/torna/service/dto/DocParamPushDTO.java b/server/server-service/src/main/java/cn/torna/service/dto/DocParamPushDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..4b25235af5ed42bbbd9fe186376f1bf6a775b320
--- /dev/null
+++ b/server/server-service/src/main/java/cn/torna/service/dto/DocParamPushDTO.java
@@ -0,0 +1,59 @@
+package cn.torna.service.dto;
+
+import cn.torna.common.annotation.Diff;
+import cn.torna.common.bean.TreeAware;
+import cn.torna.common.enums.PositionType;
+import cn.torna.common.support.IdCodec;
+import com.alibaba.fastjson.annotation.JSONField;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+import java.util.List;
+
+/**
+ * @Author jyh
+ * @Date 2025/10/24 14:53
+ * @Version 1.0
+ **/
+@Data
+public class DocParamPushDTO implements TreeAware {
+
+ @JSONField(serializeUsing = IdCodec.class, deserializeUsing = IdCodec.class)
+ private Long id;
+
+ /**
+ * 字段名称, 数据库字段:name
+ */
+ private String name;
+
+ /**
+ * 字段类型, 数据库字段:type
+ */
+ private String type;
+
+ /**
+ * doc_info.id, 数据库字段:doc_id
+ */
+ private Long docId;
+
+ /** 排序索引, 数据库字段:order_index */
+ private Integer orderIndex;
+
+ /**
+ * 父节点, 数据库字段:parent_id
+ */
+ private Long parentId;
+
+ /**
+ * 0:header, 1:请求参数,2:返回参数,3:错误码, 数据库字段:style
+ */
+ private Byte style;
+
+ /**
+ * 数据库字段:gmt_create
+ */
+ private LocalDateTime gmtCreate;
+
+ private List children;
+
+}
diff --git a/server/server-web/src/main/java/cn/torna/web/controller/doc/DocController.java b/server/server-web/src/main/java/cn/torna/web/controller/doc/DocController.java
index 18496c10ceaaebb6eba147ae33285a0dbd8913ba..4759a4460e15e3938fa78b476ddab00784ee5da4 100644
--- a/server/server-web/src/main/java/cn/torna/web/controller/doc/DocController.java
+++ b/server/server-web/src/main/java/cn/torna/web/controller/doc/DocController.java
@@ -10,6 +10,7 @@ import cn.torna.common.enums.ParamStyleEnum;
import cn.torna.common.exception.BizException;
import cn.torna.common.util.CopyUtil;
import cn.torna.dao.entity.DocInfo;
+import cn.torna.dao.entity.DocInfoExt;
import cn.torna.dao.entity.ModuleEnvironment;
import cn.torna.dao.entity.ModuleEnvironmentParam;
import cn.torna.service.DocInfoService;
@@ -38,6 +39,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
@@ -382,4 +384,29 @@ public class DocController {
return Result.ok();
}
+ /**
+ * 比对文档
+ *
+ * @param moduleId
+ * @return 比对结果
+ */
+ @GetMapping("monitor/compare")
+ public Result