mirror of
https://gitee.com/dromara/Jpom.git
synced 2024-11-29 18:38:32 +08:00
feta 触发器调用次数统计、触发器统一管理
This commit is contained in:
parent
483f05b33a
commit
0cf80417f9
@ -2,6 +2,10 @@
|
||||
|
||||
## 2.11.1.2-beta
|
||||
|
||||
### 🐣 新增功能
|
||||
|
||||
1. 【server】新增 触发器调用次数统计、触发器统一管理
|
||||
|
||||
### 🐞 解决BUG、优化功能
|
||||
|
||||
1. 【all】优化 机器状态新增:资源监控异常(资源监控异常不影响功能使用)
|
||||
|
@ -20,7 +20,7 @@
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package org.dromara.jpom.controller.system;
|
||||
package org.dromara.jpom.func.system.controller;
|
||||
|
||||
import cn.hutool.cache.impl.CacheObj;
|
||||
import cn.hutool.cache.impl.LFUCache;
|
@ -0,0 +1,85 @@
|
||||
package org.dromara.jpom.func.system.controller;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.keepbx.jpom.IJsonMessage;
|
||||
import cn.keepbx.jpom.model.BaseIdModel;
|
||||
import cn.keepbx.jpom.model.JsonMessage;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import org.dromara.jpom.model.PageResultDto;
|
||||
import org.dromara.jpom.model.user.TriggerTokenLogBean;
|
||||
import org.dromara.jpom.permission.ClassFeature;
|
||||
import org.dromara.jpom.permission.Feature;
|
||||
import org.dromara.jpom.permission.MethodFeature;
|
||||
import org.dromara.jpom.permission.SystemPermission;
|
||||
import org.dromara.jpom.service.ITriggerToken;
|
||||
import org.dromara.jpom.service.user.TriggerTokenLogServer;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author bwcx_jzy
|
||||
* @since 24/1/17 017
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping(value = "system/trigger")
|
||||
@Feature(cls = ClassFeature.SYSTEM_CACHE)
|
||||
@SystemPermission
|
||||
public class TriggerTokenController {
|
||||
|
||||
private final TriggerTokenLogServer triggerTokenLogServer;
|
||||
|
||||
public TriggerTokenController(TriggerTokenLogServer triggerTokenLogServer) {
|
||||
this.triggerTokenLogServer = triggerTokenLogServer;
|
||||
}
|
||||
|
||||
@GetMapping(value = "all-type", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
@Feature(method = MethodFeature.LIST)
|
||||
public IJsonMessage<List<JSONObject>> allType() {
|
||||
List<JSONObject> jsonObjects = triggerTokenLogServer.allType();
|
||||
return JsonMessage.success("", jsonObjects);
|
||||
}
|
||||
|
||||
@GetMapping(value = "delete", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
@Feature(method = MethodFeature.LIST)
|
||||
public IJsonMessage<String> delete(String id) {
|
||||
triggerTokenLogServer.delete(id);
|
||||
return JsonMessage.success("删除成功");
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页列表
|
||||
*
|
||||
* @return json
|
||||
*/
|
||||
@PostMapping(value = "/list", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
@Feature(method = MethodFeature.LIST)
|
||||
public IJsonMessage<PageResultDto<TriggerTokenLogBean>> list(HttpServletRequest request) {
|
||||
PageResultDto<TriggerTokenLogBean> listPage = triggerTokenLogServer.listPage(request);
|
||||
listPage.each(triggerTokenLogBean -> {
|
||||
String type = triggerTokenLogBean.getType();
|
||||
ITriggerToken byType = triggerTokenLogServer.getByType(type);
|
||||
if (byType == null) {
|
||||
triggerTokenLogBean.setDataName("ERROR:类型不存在" + type);
|
||||
} else {
|
||||
BaseIdModel byKey = byType.getByKey(triggerTokenLogBean.getDataId());
|
||||
if (byKey == null) {
|
||||
triggerTokenLogBean.setDataName("ERROR:关联数据丢失");
|
||||
} else {
|
||||
Object name = BeanUtil.getProperty(byKey, "name");
|
||||
if (name == null) {
|
||||
triggerTokenLogBean.setDataName("ERROR:关联数据名称不存在");
|
||||
} else {
|
||||
triggerTokenLogBean.setDataName(name.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return JsonMessage.success("", listPage);
|
||||
}
|
||||
}
|
@ -22,6 +22,7 @@
|
||||
*/
|
||||
package org.dromara.jpom.model.user;
|
||||
|
||||
import cn.hutool.core.annotation.PropIgnore;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.dromara.jpom.db.TableName;
|
||||
@ -55,6 +56,11 @@ public class TriggerTokenLogBean extends BaseDbModel {
|
||||
* 关联数据ID
|
||||
*/
|
||||
private String dataId;
|
||||
/**
|
||||
* 关联数据名称
|
||||
*/
|
||||
@PropIgnore
|
||||
private String dataName;
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
@ -62,4 +68,8 @@ public class TriggerTokenLogBean extends BaseDbModel {
|
||||
* @see UserModel#getId()
|
||||
*/
|
||||
private String userId;
|
||||
/**
|
||||
* 触发次数
|
||||
*/
|
||||
private Integer triggerCount;
|
||||
}
|
||||
|
@ -22,6 +22,8 @@
|
||||
*/
|
||||
package org.dromara.jpom.service;
|
||||
|
||||
import cn.keepbx.jpom.model.BaseIdModel;
|
||||
|
||||
/**
|
||||
* 带有触发器 token 相关实现服务
|
||||
*
|
||||
@ -37,6 +39,13 @@ public interface ITriggerToken {
|
||||
*/
|
||||
String typeName();
|
||||
|
||||
/**
|
||||
* 数据描述
|
||||
*
|
||||
* @return 描述
|
||||
*/
|
||||
String getDataDesc();
|
||||
|
||||
/**
|
||||
* 判断是否存在
|
||||
*
|
||||
@ -45,4 +54,5 @@ public interface ITriggerToken {
|
||||
*/
|
||||
boolean exists(String dataId);
|
||||
|
||||
BaseIdModel getByKey(String keyValue);
|
||||
}
|
||||
|
@ -22,14 +22,17 @@
|
||||
*/
|
||||
package org.dromara.jpom.service.user;
|
||||
|
||||
import cn.hutool.core.collection.CollStreamUtil;
|
||||
import cn.hutool.core.date.BetweenFormatter;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.date.SystemClock;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.db.Entity;
|
||||
import cn.hutool.db.Page;
|
||||
import cn.keepbx.jpom.event.ISystemTask;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.jpom.model.PageResultDto;
|
||||
import org.dromara.jpom.model.user.TriggerTokenLogBean;
|
||||
@ -41,6 +44,8 @@ import org.springframework.util.Assert;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author bwcx_jzy
|
||||
@ -52,11 +57,58 @@ public class TriggerTokenLogServer extends BaseDbService<TriggerTokenLogBean> im
|
||||
|
||||
private final UserService userService;
|
||||
private final List<ITriggerToken> triggerTokens;
|
||||
private final Map<String, ITriggerToken> triggerTokenMap;
|
||||
|
||||
public TriggerTokenLogServer(UserService userService,
|
||||
List<ITriggerToken> triggerTokens) {
|
||||
this.userService = userService;
|
||||
this.triggerTokens = triggerTokens;
|
||||
triggerTokenMap = CollStreamUtil.toMap(triggerTokens, ITriggerToken::typeName, iTriggerToken -> iTriggerToken);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取类型
|
||||
*
|
||||
* @param type 类型名称
|
||||
* @return 接口
|
||||
*/
|
||||
public ITriggerToken getByType(String type) {
|
||||
return MapUtil.get(triggerTokenMap, type, ITriggerToken.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除触发器
|
||||
*
|
||||
* @param id Id
|
||||
*/
|
||||
public void delete(String id) {
|
||||
TriggerTokenLogBean tokenLogBean = this.getByKey(id);
|
||||
if (tokenLogBean == null) {
|
||||
return;
|
||||
}
|
||||
ITriggerToken token = triggerTokens.stream()
|
||||
.filter(iTriggerToken -> StrUtil.equals(iTriggerToken.typeName(), tokenLogBean.getType()))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new IllegalArgumentException("没有对应的触发器类型:" + tokenLogBean.getType()));
|
||||
String sql = "update " + tokenLogBean.getType() + " set triggerToken='' where id=?";
|
||||
this.execute(sql, tokenLogBean.getDataId());
|
||||
this.delByKey(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有类型
|
||||
*
|
||||
* @return list
|
||||
*/
|
||||
public List<JSONObject> allType() {
|
||||
return triggerTokens.stream()
|
||||
.map(iTriggerToken -> {
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
jsonObject.put("name", iTriggerToken.typeName());
|
||||
jsonObject.put("desc", iTriggerToken.getDataDesc());
|
||||
return jsonObject;
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -84,6 +136,9 @@ public class TriggerTokenLogServer extends BaseDbService<TriggerTokenLogBean> im
|
||||
if (userModel != null && StrUtil.equals(type, tokenLogBean.getType())) {
|
||||
boolean demoUser = userModel.isDemoUser();
|
||||
Assert.state(!demoUser, "当前用户触发器不可用");
|
||||
// 修改触发次数
|
||||
String sql = "update " + this.getTableName() + " set triggerCount=ifnull(triggerCount,0)+1 where id=?";
|
||||
int execute = this.execute(sql, tokenLogBean.getId());
|
||||
return userModel;
|
||||
}
|
||||
}
|
||||
|
@ -13,3 +13,4 @@ ADD,NODE_INFO,jpomScriptCount,Integer,,,jpom脚本数,
|
||||
ALTER,STATIC_FILE_STORAGE,parentAbsolutePath,String,300,,父级文件路径,false
|
||||
ALTER,STATIC_FILE_STORAGE,absolutePath,String,300,,文件路径,false
|
||||
ALTER,STATIC_FILE_STORAGE,name,String,100,,文件名,false
|
||||
ADD,TRIGGER_TOKEN_LOG,triggerCount,Integer,,,触发次数
|
||||
|
|
@ -83,6 +83,12 @@ public abstract class BaseDbCommonService<T> {
|
||||
this.tableName = annotation.value();
|
||||
}
|
||||
|
||||
public String getDataDesc() {
|
||||
TableName annotation = tClass.getAnnotation(TableName.class);
|
||||
Assert.notNull(annotation, "请配置 table Name");
|
||||
return annotation.name();
|
||||
}
|
||||
|
||||
protected DataSource getDataSource() {
|
||||
DSFactory dsFactory = StorageServiceFactory.get().getDsFactory();
|
||||
return dsFactory.getDataSource();
|
||||
|
25
web-vue/src/api/trigger-token.ts
Normal file
25
web-vue/src/api/trigger-token.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import axios from './config'
|
||||
|
||||
export function triggerTokenList(data) {
|
||||
return axios({
|
||||
url: '/system/trigger/list',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
export function triggerTokenAllType(data) {
|
||||
return axios({
|
||||
url: '/system/trigger/all-type',
|
||||
method: 'get',
|
||||
params: data
|
||||
})
|
||||
}
|
||||
|
||||
export function triggerTokenDelete(data) {
|
||||
return axios({
|
||||
url: '/system/trigger/delete',
|
||||
method: 'get',
|
||||
params: data
|
||||
})
|
||||
}
|
@ -149,18 +149,23 @@
|
||||
</a-timeline> -->
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="2" tab="运行中的定时任务" force-render>
|
||||
<task-stat :taskList="taskList" @refresh="loadData"
|
||||
/></a-tab-pane>
|
||||
<task-stat :taskList="taskList" @refresh="loadData" />
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="3" tab="触发器管理">
|
||||
<TriggerToken />
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { getServerCache, clearCache, clearErrorWorkspace } from '@/api/system'
|
||||
import TaskStat from '@/pages/system/taskStat'
|
||||
import TriggerToken from '@/pages/system/trigger-token'
|
||||
import { renderSize, formatDuration } from '@/utils/const'
|
||||
export default {
|
||||
components: {
|
||||
TaskStat
|
||||
TaskStat,
|
||||
TriggerToken
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
209
web-vue/src/pages/system/trigger-token.vue
Normal file
209
web-vue/src/pages/system/trigger-token.vue
Normal file
@ -0,0 +1,209 @@
|
||||
<template>
|
||||
<div>
|
||||
<!-- 数据表格 -->
|
||||
<a-table
|
||||
:data-source="viewOperationLogList"
|
||||
:loading="viewOperationLoading"
|
||||
:columns="viewOperationLogColumns"
|
||||
:pagination="viewOperationLogPagination"
|
||||
@change="changeListLog"
|
||||
bordered
|
||||
size="middle"
|
||||
:scroll="{
|
||||
x: 'max-content'
|
||||
}"
|
||||
>
|
||||
<template v-slot:title>
|
||||
<a-space>
|
||||
<a-input
|
||||
class="search-input-item"
|
||||
@pressEnter="handleListLog"
|
||||
v-model:value="viewOperationLogListQuery['userId']"
|
||||
placeholder="创建人,全匹配"
|
||||
/>
|
||||
<a-input
|
||||
class="search-input-item"
|
||||
@pressEnter="handleListLog"
|
||||
v-model:value="viewOperationLogListQuery['triggerToken']"
|
||||
placeholder="token,全匹配"
|
||||
/>
|
||||
<a-select
|
||||
v-model:value="viewOperationLogListQuery.type"
|
||||
allowClear
|
||||
placeholder="类型"
|
||||
class="search-input-item"
|
||||
>
|
||||
<a-select-option v-for="item in allTypeList" :key="item.name">{{ item.desc }}</a-select-option>
|
||||
</a-select>
|
||||
<a-range-picker
|
||||
:show-time="{ format: 'HH:mm:ss' }"
|
||||
format="YYYY-MM-DD HH:mm:ss"
|
||||
@change="onchangeListLogTime"
|
||||
/>
|
||||
<a-button type="primary" @click="handleListLog">搜索</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
<template #bodyCell="{ column, text, record }">
|
||||
<template v-if="column.dataIndex === 'commands'">
|
||||
<a-tooltip placement="topLeft" :title="text">
|
||||
<a-typography-paragraph
|
||||
v-if="text"
|
||||
:copyable="{ tooltip: false, text: text }"
|
||||
style="display: inline-block; margin-bottom: 0"
|
||||
>
|
||||
</a-typography-paragraph>
|
||||
{{ text }}
|
||||
</a-tooltip>
|
||||
</template>
|
||||
|
||||
<template v-else-if="column.tooltip">
|
||||
<a-tooltip placement="topLeft" :title="text">
|
||||
<span>{{ text }}</span>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'operation'">
|
||||
<a-space>
|
||||
<a-button size="small" type="primary" danger @click="handleDelete(record)">删除</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getSshOperationLogList } from '@/api/ssh'
|
||||
import { CHANGE_PAGE, COMPUTED_PAGINATION, PAGE_DEFAULT_LIST_QUERY, parseTime } from '@/utils/const'
|
||||
import { triggerTokenList, triggerTokenAllType, triggerTokenDelete } from '@/api/trigger-token'
|
||||
export default {
|
||||
components: {},
|
||||
props: {},
|
||||
computed: {
|
||||
viewOperationLogPagination() {
|
||||
return COMPUTED_PAGINATION(this.viewOperationLogListQuery)
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
viewOperationLoading: false,
|
||||
viewOperationLogList: [],
|
||||
viewOperationLogListQuery: Object.assign(
|
||||
{ sshId: this.sshId, machineSshId: this.machineSshId },
|
||||
PAGE_DEFAULT_LIST_QUERY
|
||||
),
|
||||
viewOperationLogColumns: [
|
||||
{
|
||||
title: '创建人',
|
||||
dataIndex: 'userId',
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
title: 'token',
|
||||
dataIndex: 'triggerToken',
|
||||
width: 100
|
||||
},
|
||||
|
||||
{
|
||||
title: '关联数据名',
|
||||
dataIndex: 'dataName'
|
||||
// width: 100
|
||||
},
|
||||
{
|
||||
title: '调用次数',
|
||||
dataIndex: 'triggerCount',
|
||||
width: 100,
|
||||
sorter: true
|
||||
},
|
||||
{
|
||||
title: '关联数据',
|
||||
dataIndex: 'dataId',
|
||||
width: 100
|
||||
},
|
||||
|
||||
{
|
||||
title: '创建时间',
|
||||
dataIndex: 'createTimeMillis',
|
||||
sorter: true,
|
||||
customRender: ({ text }) => {
|
||||
return parseTime(text)
|
||||
},
|
||||
width: '180px'
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
dataIndex: 'operation',
|
||||
width: '80px',
|
||||
|
||||
align: 'center',
|
||||
fixed: 'right'
|
||||
}
|
||||
],
|
||||
allTypeList: []
|
||||
}
|
||||
},
|
||||
created() {
|
||||
triggerTokenAllType().then((res) => {
|
||||
if (res.code === 200) {
|
||||
this.allTypeList = res.data || []
|
||||
}
|
||||
})
|
||||
this.handleListLog()
|
||||
},
|
||||
methods: {
|
||||
handleListLog() {
|
||||
this.viewOperationLoading = true
|
||||
|
||||
triggerTokenList(this.viewOperationLogListQuery).then((res) => {
|
||||
if (res.code === 200) {
|
||||
this.viewOperationLogList = res.data.result
|
||||
this.viewOperationLogListQuery.total = res.data.total
|
||||
}
|
||||
this.viewOperationLoading = false
|
||||
})
|
||||
},
|
||||
changeListLog(pagination, filters, sorter) {
|
||||
this.viewOperationLogListQuery = CHANGE_PAGE(this.viewOperationLogListQuery, { pagination, sorter })
|
||||
|
||||
this.handleListLog()
|
||||
},
|
||||
// 选择时间
|
||||
onchangeListLogTime(value, dateString) {
|
||||
if (dateString[0]) {
|
||||
this.viewOperationLogListQuery.createTimeMillis = `${dateString[0]} ~ ${dateString[1]}`
|
||||
} else {
|
||||
this.viewOperationLogListQuery.createTimeMillis = ''
|
||||
}
|
||||
},
|
||||
// 删除
|
||||
handleDelete(record) {
|
||||
const that = this
|
||||
$confirm({
|
||||
title: '系统提示',
|
||||
zIndex: 1009,
|
||||
content: '真的要删除对应的触发器吗?',
|
||||
okText: '确认',
|
||||
cancelText: '取消',
|
||||
async onOk() {
|
||||
return await new Promise((resolve, reject) => {
|
||||
// 删除
|
||||
triggerTokenDelete({
|
||||
id: record.id
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.code === 200) {
|
||||
$notification.success({
|
||||
message: res.msg
|
||||
})
|
||||
that.handleListLog()
|
||||
}
|
||||
resolve()
|
||||
})
|
||||
.catch(reject)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
Loading…
Reference in New Issue
Block a user