feat: 主机连接日志.

This commit is contained in:
lijiahang
2023-12-27 12:31:18 +08:00
parent 459002f666
commit 6fdd29b3fe
18 changed files with 147 additions and 97 deletions

View File

@@ -11,8 +11,10 @@ Authorization: {{token}}
"type": "", "type": "",
"token": "", "token": "",
"status": "", "status": "",
"startTimeStart": "", "startTimeRange": [
"endTimeEnd": "" "",
""
]
} }

View File

@@ -36,6 +36,10 @@ public class HostConnectLogDO extends BaseDO {
@TableField("user_id") @TableField("user_id")
private Long userId; private Long userId;
@Schema(description = "用户名")
@TableField("username")
private String username;
@Schema(description = "主机id") @Schema(description = "主机id")
@TableField("host_id") @TableField("host_id")
private Long hostId; private Long hostId;

View File

@@ -24,9 +24,18 @@ public class HostConnectLogCreateRequest extends PageRequest {
@Schema(description = "用户id") @Schema(description = "用户id")
private Long userId; private Long userId;
@Schema(description = "用户名")
private String username;
@Schema(description = "主机id") @Schema(description = "主机id")
private Long hostId; private Long hostId;
@Schema(description = "主机名称")
private String hostName;
@Schema(description = "主机地址")
private String hostAddress;
@Size(max = 128) @Size(max = 128)
@Schema(description = "token") @Schema(description = "token")
private String token; private String token;

View File

@@ -45,12 +45,8 @@ public class HostConnectLogQueryRequest extends PageRequest {
@Schema(description = "状态") @Schema(description = "状态")
private String status; private String status;
@Schema(description = "开始时间-区间") @Schema(description = "开始时间-区间")
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date startTimeStart; private Date[] startTimeRange;
@Schema(description = "开始时间-闭区间")
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date startTimeEnd;
} }

View File

@@ -31,6 +31,9 @@ public class HostConnectLogVO implements Serializable {
@Schema(description = "用户id") @Schema(description = "用户id")
private Long userId; private Long userId;
@Schema(description = "用户名")
private String username;
@Schema(description = "主机id") @Schema(description = "主机id")
private Long hostId; private Long hostId;

View File

@@ -2,6 +2,7 @@ package com.orion.ops.module.asset.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.orion.lang.define.wrapper.DataGrid; import com.orion.lang.define.wrapper.DataGrid;
import com.orion.lang.utils.Arrays1;
import com.orion.ops.framework.mybatis.core.query.Conditions; import com.orion.ops.framework.mybatis.core.query.Conditions;
import com.orion.ops.module.asset.convert.HostConnectLogConvert; import com.orion.ops.module.asset.convert.HostConnectLogConvert;
import com.orion.ops.module.asset.dao.HostConnectLogDAO; import com.orion.ops.module.asset.dao.HostConnectLogDAO;
@@ -74,8 +75,8 @@ public class HostConnectLogServiceImpl implements HostConnectLogService {
.eq(HostConnectLogDO::getType, request.getType()) .eq(HostConnectLogDO::getType, request.getType())
.like(HostConnectLogDO::getToken, request.getToken()) .like(HostConnectLogDO::getToken, request.getToken())
.eq(HostConnectLogDO::getStatus, request.getStatus()) .eq(HostConnectLogDO::getStatus, request.getStatus())
.ge(HostConnectLogDO::getStartTime, request.getStartTimeStart()) .ge(HostConnectLogDO::getStartTime, Arrays1.getIfPresent(request.getStartTimeRange(), 0))
.le(HostConnectLogDO::getStartTime, request.getStartTimeEnd()); .le(HostConnectLogDO::getStartTime, Arrays1.getIfPresent(request.getStartTimeRange(), 1));
} }
} }

View File

@@ -6,6 +6,7 @@
<resultMap id="BaseResultMap" type="com.orion.ops.module.asset.entity.domain.HostConnectLogDO"> <resultMap id="BaseResultMap" type="com.orion.ops.module.asset.entity.domain.HostConnectLogDO">
<id column="id" property="id"/> <id column="id" property="id"/>
<result column="user_id" property="userId"/> <result column="user_id" property="userId"/>
<result column="username" property="username"/>
<result column="host_id" property="hostId"/> <result column="host_id" property="hostId"/>
<result column="host_name" property="hostName"/> <result column="host_name" property="hostName"/>
<result column="host_address" property="hostAddress"/> <result column="host_address" property="hostAddress"/>
@@ -22,7 +23,7 @@
<!-- 通用查询结果列 --> <!-- 通用查询结果列 -->
<sql id="Base_Column_List"> <sql id="Base_Column_List">
id, user_id, host_id, host_name, host_address, type, token, status, start_time, end_time, extra_info, create_time, update_time, deleted id, user_id, username, host_id, host_name, host_address, type, token, status, start_time, end_time, extra_info, create_time, update_time, deleted
</sql> </sql>
</mapper> </mapper>

View File

@@ -44,12 +44,8 @@ public class OperatorLogQueryRequest extends PageRequest {
@Schema(description = "操作结果 0失败 1成功") @Schema(description = "操作结果 0失败 1成功")
private Integer result; private Integer result;
@Schema(description = "开始时间-区间") @Schema(description = "开始时间-区间")
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date startTimeStart; private Date[] startTimeRange;
@Schema(description = "开始时间-闭区间")
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date startTimeEnd;
} }

View File

@@ -2,6 +2,7 @@ package com.orion.ops.module.infra.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.orion.lang.define.wrapper.DataGrid; import com.orion.lang.define.wrapper.DataGrid;
import com.orion.lang.utils.Arrays1;
import com.orion.ops.framework.biz.operator.log.core.model.OperatorLogModel; import com.orion.ops.framework.biz.operator.log.core.model.OperatorLogModel;
import com.orion.ops.framework.common.constant.Const; import com.orion.ops.framework.common.constant.Const;
import com.orion.ops.module.infra.convert.OperatorLogConvert; import com.orion.ops.module.infra.convert.OperatorLogConvert;
@@ -77,8 +78,8 @@ public class OperatorLogServiceImpl implements OperatorLogService {
.eq(OperatorLogDO::getModule, request.getModule()) .eq(OperatorLogDO::getModule, request.getModule())
.eq(OperatorLogDO::getType, request.getType()) .eq(OperatorLogDO::getType, request.getType())
.eq(OperatorLogDO::getResult, request.getResult()) .eq(OperatorLogDO::getResult, request.getResult())
.ge(OperatorLogDO::getStartTime, request.getStartTimeStart()) .ge(OperatorLogDO::getStartTime, Arrays1.getIfPresent(request.getStartTimeRange(), 0))
.le(OperatorLogDO::getStartTime, request.getStartTimeEnd()) .le(OperatorLogDO::getStartTime, Arrays1.getIfPresent(request.getStartTimeRange(), 1))
.orderByDesc(OperatorLogDO::getId); .orderByDesc(OperatorLogDO::getId);
} }

View File

@@ -12,8 +12,7 @@ export interface HostConnectLogQueryRequest extends Pagination {
type?: string; type?: string;
token?: string; token?: string;
status?: string; status?: string;
startTimeStart?: string; startTimeRange?: string[];
startTimeEnd?: string;
} }
/** /**

View File

@@ -11,8 +11,7 @@ export interface OperatorLogQueryRequest extends Pagination {
type?: string; type?: string;
riskLevel?: string; riskLevel?: string;
result?: number; result?: number;
startTimeStart?: string; startTimeRange?: string[];
startTimeEnd?: string;
} }
/** /**

View File

@@ -0,0 +1,65 @@
<template>
<a-select v-model:model-value="value"
:options="optionData"
:loading="loading"
placeholder="请选择主机"
allow-clear />
</template>
<script lang="ts">
export default {
name: 'host-selector'
};
</script>
<script lang="ts" setup>
import type { SelectOptionData } from '@arco-design/web-vue';
import { computed, onBeforeMount, ref } from 'vue';
import { useCacheStore } from '@/store';
import useLoading from '@/hooks/loading';
const props = defineProps({
modelValue: Number
});
const emits = defineEmits(['update:modelValue']);
const { loading, setLoading } = useLoading();
const cacheStore = useCacheStore();
const value = computed<number>({
get() {
return props.modelValue as number;
},
set(e) {
if (e) {
emits('update:modelValue', e);
} else {
emits('update:modelValue', null);
}
}
});
const optionData = ref<Array<SelectOptionData>>([]);
// 初始化选项
onBeforeMount(async () => {
setLoading(true);
try {
const hosts = await cacheStore.loadHosts();
optionData.value = hosts.map(s => {
return {
label: `${s.name} - ${s.address}`,
value: s.id,
};
});
} catch (e) {
} finally {
setLoading(false);
}
});
</script>
<style lang="less" scoped>
</style>

View File

@@ -15,11 +15,13 @@
</template> </template>
<!-- 数据 --> <!-- 数据 -->
<template #item="{ item }"> <template #item="{ item }">
<a-list-item :title="`${item.name}(${item.code}) - ${item.address}`"> <a-tooltip :content="`${item.name} - ${item.address}`">
<icon-desktop class="host-list-icon" /> <a-list-item>
<span>{{ `${item.name}(${item.code}) - ` }}</span> <icon-desktop class="host-list-icon" />
<span class="span-blue">{{ item.address }}</span> <span>{{ `${item.name} - ` }}</span>
</a-list-item> <span class="span-blue">{{ item.address }}</span>
</a-list-item>
</a-tooltip>
</template> </template>
</a-list> </a-list>
</template> </template>

View File

@@ -3,21 +3,21 @@
<a-card class="general-card table-search-card"> <a-card class="general-card table-search-card">
<a-query-header :model="formModel" <a-query-header :model="formModel"
label-align="left" label-align="left"
:itemOptions="{ 5: { span: 2 } }"
@submit="fetchTableData" @submit="fetchTableData"
@reset="reset" @reset="fetchTableData"
@keyup.enter="() => fetchTableData()"> @keyup.enter="() => fetchTableData()">
<!-- 用户 --> <!-- 连接用户 -->
<a-form-item field="userId" label="用户" label-col-flex="50px"> <a-form-item field="userId" label="连接用户" label-col-flex="50px">
<user-selector v-model="formModel.userId" <user-selector v-model="formModel.userId"
placeholder="请选择用户" placeholder="请选择用户"
allow-clear /> allow-clear />
</a-form-item> </a-form-item>
<!-- 主机 --> <!-- 连接主机 -->
<a-form-item field="hostId" label="主机" label-col-flex="50px"> <a-form-item field="hostId" label="连接主机" label-col-flex="50px">
<a-input-number v-model="formModel.hostId" <host-selector v-model="formModel.hostId"
placeholder="FIXME 请输入主机" placeholder="请选择主机"
allow-clear allow-clear />
hide-button />
</a-form-item> </a-form-item>
<!-- 主机地址 --> <!-- 主机地址 -->
<a-form-item field="hostAddress" label="主机地址" label-col-flex="50px"> <a-form-item field="hostAddress" label="主机地址" label-col-flex="50px">
@@ -35,12 +35,12 @@
<a-input v-model="formModel.token" placeholder="请输入token" allow-clear /> <a-input v-model="formModel.token" placeholder="请输入token" allow-clear />
</a-form-item> </a-form-item>
<!-- 开始时间 --> <!-- 开始时间 -->
<a-form-item field="startTime" label="开始时间" label-col-flex="50px"> <a-form-item field="startTimeRange" label="开始时间" label-col-flex="50px">
<a-range-picker v-model="timeRange" <a-range-picker v-model="formModel.startTimeRange"
style="width: 100%"
:time-picker-props="{ defaultValue: ['00:00:00', '23:59:59'] }" :time-picker-props="{ defaultValue: ['00:00:00', '23:59:59'] }"
show-time show-time
format="YYYY-MM-DD HH:mm:ss" format="YYYY-MM-DD HH:mm:ss" />
@ok="timeRangePicked" />
</a-form-item> </a-form-item>
</a-query-header> </a-query-header>
</a-card> </a-card>
@@ -69,6 +69,14 @@
@page-change="(page) => fetchTableData(page, pagination.pageSize)" @page-change="(page) => fetchTableData(page, pagination.pageSize)"
@page-size-change="(size) => fetchTableData(1, size)" @page-size-change="(size) => fetchTableData(1, size)"
:bordered="false"> :bordered="false">
<!-- 连接用户 -->
<template #username="{ record }">
{{ record.userId }} - {{ record.username }}
</template>
<!-- 连接主机 -->
<template #hostName="{ record }">
{{ record.hostId }} - {{ record.hostName }}
</template>
<!-- 主机地址 --> <!-- 主机地址 -->
<template #hostAddress="{ record }"> <template #hostAddress="{ record }">
<span class="copy-left" title="复制" @click="copy(record.hostAddress)"> <span class="copy-left" title="复制" @click="copy(record.hostAddress)">
@@ -104,6 +112,7 @@
import { useDictStore } from '@/store'; import { useDictStore } from '@/store';
import useCopy from '@/hooks/copy'; import useCopy from '@/hooks/copy';
import UserSelector from '@/components/user/user/user-selector.vue'; import UserSelector from '@/components/user/user/user-selector.vue';
import HostSelector from '@/components/asset/host/host-selector.vue';
const emits = defineEmits(['openAdd', 'openUpdate']); const emits = defineEmits(['openAdd', 'openUpdate']);
@@ -114,7 +123,6 @@
const { toOptions, getDictValue } = useDictStore(); const { toOptions, getDictValue } = useDictStore();
const { copy } = useCopy(); const { copy } = useCopy();
const timeRange = ref<string[]>([]);
const formModel = reactive<HostConnectLogQueryRequest>({ const formModel = reactive<HostConnectLogQueryRequest>({
userId: undefined, userId: undefined,
hostId: undefined, hostId: undefined,
@@ -122,16 +130,9 @@
type: undefined, type: undefined,
token: undefined, token: undefined,
status: undefined, status: undefined,
startTimeStart: undefined, startTimeRange: undefined,
startTimeEnd: undefined,
}); });
// 选择时间
const timeRangePicked = (e: string[]) => {
formModel.startTimeStart = e[0];
formModel.startTimeEnd = e[1];
};
// 加载数据 // 加载数据
const doFetchTableData = async (request: HostConnectLogQueryRequest) => { const doFetchTableData = async (request: HostConnectLogQueryRequest) => {
try { try {
@@ -152,14 +153,6 @@
doFetchTableData({ page, limit, ...form }); doFetchTableData({ page, limit, ...form });
}; };
// 重置
const reset = () => {
timeRange.value = [];
formModel.startTimeStart = undefined;
formModel.startTimeEnd = undefined;
fetchTableData();
};
onMounted(() => { onMounted(() => {
fetchTableData(); fetchTableData();
}); });

View File

@@ -10,19 +10,14 @@ const columns = [
align: 'left', align: 'left',
fixed: 'left', fixed: 'left',
}, { }, {
title: '用户id', title: '连接用户',
dataIndex: 'userId', dataIndex: 'username',
slotName: 'userId', slotName: 'username',
width: 110,
align: 'left', align: 'left',
ellipsis: true,
tooltip: true,
}, { }, {
title: '主机id', title: '连接主机',
dataIndex: 'hostId',
slotName: 'hostId',
width: 110,
align: 'left',
}, {
title: '主机名称',
dataIndex: 'hostName', dataIndex: 'hostName',
slotName: 'hostName', slotName: 'hostName',
align: 'left', align: 'left',
@@ -48,8 +43,7 @@ const columns = [
dataIndex: 'status', dataIndex: 'status',
slotName: 'status', slotName: 'status',
align: 'left', align: 'left',
ellipsis: true, width: 90,
tooltip: true,
}, { }, {
title: '开始时间', title: '开始时间',
dataIndex: 'startTime', dataIndex: 'startTime',

View File

@@ -94,8 +94,8 @@
const renderLabel = (label: string) => { const renderLabel = (label: string) => {
const last = label.lastIndexOf('-'); const last = label.lastIndexOf('-');
const prefix = label.substring(0, last - 1); const prefix = label.substring(0, last - 1);
const ip = label.substring(last + 2, label.length); const address = label.substring(last + 2, label.length);
return `${prefix} - <span class="span-blue">${ip}</span>`; return `${prefix} - <span class="span-blue">${address}</span>`;
}; };
// 查询组内数据 // 查询组内数据
@@ -121,7 +121,7 @@
data.value = hosts.map(s => { data.value = hosts.map(s => {
return { return {
value: String(s.id), value: String(s.id),
label: `${s.name} (${s.code}) - ${s.address}`, label: `${s.name} - ${s.address}`,
disabled: false disabled: false
}; };
}); });

View File

@@ -33,7 +33,7 @@
<!-- 返回 --> <!-- 返回 -->
<a-tab-pane key="back" v-if="userId"> <a-tab-pane key="back" v-if="userId">
<template #title> <template #title>
<icon-arrow-left style="font-size: 16px" /> <icon-left style="font-size: 16px; padding-top: 2px;" />
返回 返回
</template> </template>
</a-tab-pane> </a-tab-pane>

View File

@@ -1,8 +1,9 @@
<template> <template>
<a-query-header :model="formModel" <a-query-header :model="formModel"
label-align="left" label-align="left"
:itemOptions="{ [visibleUser ? 5 : 4]: { span: 2 } }"
@submit="submit" @submit="submit"
@reset="reset" @reset="submit"
@keyup.enter="submit"> @keyup.enter="submit">
<!-- 操作用户 --> <!-- 操作用户 -->
<a-form-item v-if="visibleUser" <a-form-item v-if="visibleUser"
@@ -47,12 +48,12 @@
allow-clear /> allow-clear />
</a-form-item> </a-form-item>
<!-- 执行时间 --> <!-- 执行时间 -->
<a-form-item field="startTime" label="执行时间" label-col-flex="50px"> <a-form-item field="startTimeRange" label="执行时间" label-col-flex="50px">
<a-range-picker v-model="timeRange" <a-range-picker v-model="formModel.startTimeRange"
style="width: 100%"
:time-picker-props="{ defaultValue: ['00:00:00', '23:59:59'] }" :time-picker-props="{ defaultValue: ['00:00:00', '23:59:59'] }"
show-time show-time
format="YYYY-MM-DD HH:mm:ss" format="YYYY-MM-DD HH:mm:ss" />
@ok="timeRangePicked" />
</a-form-item> </a-form-item>
</a-query-header> </a-query-header>
</template> </template>
@@ -84,23 +85,15 @@
const { loading, setLoading } = useLoading(); const { loading, setLoading } = useLoading();
const { $state: dictState, toOptions } = useDictStore(); const { $state: dictState, toOptions } = useDictStore();
const timeRange = ref<string[]>([]);
const typeOptions = ref<SelectOptionData[]>(toOptions(operatorLogTypeKey)); const typeOptions = ref<SelectOptionData[]>(toOptions(operatorLogTypeKey));
const formModel = reactive<OperatorLogQueryRequest>({ const formModel = reactive<OperatorLogQueryRequest>({
module: undefined, module: undefined,
type: undefined, type: undefined,
riskLevel: undefined, riskLevel: undefined,
result: undefined, result: undefined,
startTimeStart: undefined, startTimeRange: undefined,
startTimeEnd: undefined,
}); });
// 选择时间
const timeRangePicked = (e: string[]) => {
formModel.startTimeStart = e[0];
formModel.startTimeEnd = e[1];
};
// 选择类型 // 选择类型
const selectedModule = (module: string) => { const selectedModule = (module: string) => {
if (!module) { if (!module) {
@@ -118,14 +111,6 @@
} }
}; };
// 重置
const reset = () => {
timeRange.value = [];
formModel.startTimeStart = undefined;
formModel.startTimeEnd = undefined;
submit();
};
// 切换页码 // 切换页码
const submit = () => { const submit = () => {
emits('submit', { ...formModel }); emits('submit', { ...formModel });