🔨 资产模块统计.

This commit is contained in:
lijiahang
2024-12-27 14:44:59 +08:00
parent 7996ae5b63
commit 1846a496c1
11 changed files with 606 additions and 0 deletions

View File

@@ -0,0 +1,65 @@
/*
* Copyright (c) 2023 - present Dromara, All rights reserved.
*
* https://visor.dromara.org
* https://visor.dromara.org.cn
* https://visor.orionsec.cn
*
* Members:
* Jiahang Li - ljh1553488six@139.com - author
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.visor.module.asset.controller;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.dromara.visor.framework.log.core.annotation.IgnoreLog;
import org.dromara.visor.framework.log.core.enums.IgnoreLogMode;
import org.dromara.visor.framework.web.core.annotation.RestWrapper;
import org.dromara.visor.module.asset.entity.vo.AssetWorkplaceStatisticsVO;
import org.dromara.visor.module.asset.service.AssetStatisticsService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
/**
* asset - 统计服务
*
* @author Jiahang Li
* @version 1.0.0
* @since 2024/12/23 16:07
*/
@Tag(name = "asset - 统计服务")
@Slf4j
@Validated
@RestWrapper
@RestController
@RequestMapping("/asset/statistics")
public class AssetStatisticsController {
@Resource
private AssetStatisticsService assetStatisticsService;
@IgnoreLog(IgnoreLogMode.RET)
@GetMapping("/get-workplace")
@Operation(summary = "查询工作台统计信息")
public AssetWorkplaceStatisticsVO getWorkplaceStatisticsData() {
return assetStatisticsService.getWorkplaceStatisticsData();
}
}

View File

@@ -26,7 +26,9 @@ import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.dromara.visor.framework.mybatis.core.mapper.IMapper;
import org.dromara.visor.module.asset.entity.domain.ExecLogDO;
import org.dromara.visor.module.asset.entity.po.ExecLogCountPO;
import java.util.Date;
import java.util.List;
/**
@@ -67,4 +69,18 @@ public interface ExecLogDAO extends IMapper<ExecLogDO> {
@Param("userId") Long userId,
@Param("limit") Integer limit);
/**
* 获取执行日志统计
*
* @param userId userId
* @param source source
* @param startTime startTime
* @param endTime endTime
* @return count
*/
List<ExecLogCountPO> selectExecLogCount(@Param("userId") Long userId,
@Param("source") String source,
@Param("startTime") Date startTime,
@Param("endTime") Date endTime);
}

View File

@@ -26,7 +26,9 @@ import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.dromara.visor.framework.mybatis.core.mapper.IMapper;
import org.dromara.visor.module.asset.entity.domain.TerminalConnectLogDO;
import org.dromara.visor.module.asset.entity.po.TerminalConnectLogCountPO;
import java.util.Date;
import java.util.List;
/**
@@ -49,4 +51,16 @@ public interface TerminalConnectLogDAO extends IMapper<TerminalConnectLogDO> {
*/
List<Long> selectLatestConnectHostId(@Param("userId") Long userId, @Param("type") String type, @Param("limit") Integer limit);
/**
* 查询终端连接日志用户数量
*
* @param userId userId
* @param startTime startTime
* @param endTime endTime
* @return rows
*/
List<TerminalConnectLogCountPO> selectConnectLogUserCount(@Param("userId") Long userId,
@Param("startTime") Date startTime,
@Param("endTime") Date endTime);
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright (c) 2023 - present Dromara, All rights reserved.
*
* https://visor.dromara.org
* https://visor.dromara.org.cn
* https://visor.orionsec.cn
*
* Members:
* Jiahang Li - ljh1553488six@139.com - author
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.visor.module.asset.define.cache;
import cn.orionsec.kit.lang.define.cache.key.CacheKeyBuilder;
import cn.orionsec.kit.lang.define.cache.key.CacheKeyDefine;
import cn.orionsec.kit.lang.define.cache.key.struct.RedisCacheStruct;
import org.dromara.visor.module.asset.entity.vo.AssetWorkplaceStatisticsVO;
import java.util.concurrent.TimeUnit;
/**
* 资产模块统计缓存 key
*
* @author Jiahang Li
* @version 1.0.0
* @since 2024/12/23 16:10
*/
public interface AssetStatisticsCacheKeyDefine {
CacheKeyDefine WORKPLACE_DATA = new CacheKeyBuilder()
.key("data:statistics:asset-workplace:{}:{}")
.desc("资产模块工作台统计 ${userId} ${time}")
.type(AssetWorkplaceStatisticsVO.class)
.struct(RedisCacheStruct.STRING)
.timeout(10, TimeUnit.MINUTES)
.build();
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright (c) 2023 - present Dromara, All rights reserved.
*
* https://visor.dromara.org
* https://visor.dromara.org.cn
* https://visor.orionsec.cn
*
* Members:
* Jiahang Li - ljh1553488six@139.com - author
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.visor.module.asset.entity.po;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
* 执行日志数量
*
* @author Jiahang Li
* @version 1.0.0
* @since 2024/12/23 21:35
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Schema(name = "ExecLogCountPO", description = "执行日志数量")
public class ExecLogCountPO implements Serializable {
private static final long serialVersionUID = 1L;
@Schema(description = "执行日期")
private String execDate;
@Schema(description = "数量")
private Integer count;
}

View File

@@ -0,0 +1,58 @@
/*
* Copyright (c) 2023 - present Dromara, All rights reserved.
*
* https://visor.dromara.org
* https://visor.dromara.org.cn
* https://visor.orionsec.cn
*
* Members:
* Jiahang Li - ljh1553488six@139.com - author
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.visor.module.asset.entity.po;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
* 终端连接日志数量
*
* @author Jiahang Li
* @version 1.0.0
* @since 2024/12/23 22:31
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Schema(name = "TerminalConnectLogCountPO", description = "终端连接日志数量")
public class TerminalConnectLogCountPO implements Serializable {
private static final long serialVersionUID = 1L;
@Schema(description = "连接日期")
private String connectDate;
@Schema(description = "类型")
private String type;
@Schema(description = "数量")
private Integer count;
}

View File

@@ -0,0 +1,75 @@
/*
* Copyright (c) 2023 - present Dromara, All rights reserved.
*
* https://visor.dromara.org
* https://visor.dromara.org.cn
* https://visor.orionsec.cn
*
* Members:
* Jiahang Li - ljh1553488six@139.com - author
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.visor.module.asset.entity.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.dromara.visor.framework.common.entity.chart.LineSingleChartData;
import java.util.List;
/**
* 资产模块工作台统计响应
*
* @author Jiahang Li
* @version 1.0.0
* @since 2024/12/26 15:32
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Schema(name = "AssetWorkplaceStatisticsVO", description = "资产模块工作台统计响应")
public class AssetWorkplaceStatisticsVO {
@Schema(description = "执行的计划任务数量")
private Integer execJobCount;
@Schema(description = "今日批量执行数量")
private Integer todayExecCommandCount;
@Schema(description = "7日批量执行次数")
private Integer weekExecCommandCount;
@Schema(description = "今日连接终端次数")
private Integer todayTerminalConnectCount;
@Schema(description = "7日连接终端次数")
private Integer weekTerminalConnectCount;
@Schema(description = "批量执行数量图表")
private LineSingleChartData execCommandChart;
@Schema(description = "连接终端次数图表")
private LineSingleChartData terminalConnectChart;
@Schema(description = "连接终端记录")
private List<TerminalConnectLogVO> terminalConnectList;
@Schema(description = "批量执行记录")
private List<ExecLogVO> execLogList;
}

View File

@@ -0,0 +1,62 @@
/*
* Copyright (c) 2023 - present Dromara, All rights reserved.
*
* https://visor.dromara.org
* https://visor.dromara.org.cn
* https://visor.orionsec.cn
*
* Members:
* Jiahang Li - ljh1553488six@139.com - author
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.visor.module.asset.service;
import org.dromara.visor.framework.common.entity.chart.LineSingleChartData;
import org.dromara.visor.module.asset.entity.vo.AssetWorkplaceStatisticsVO;
import org.dromara.visor.module.asset.enums.ExecSourceEnum;
/**
* 资产模块统计服务
*
* @author Jiahang Li
* @version 1.0.0
* @since 2024/12/23 22:24
*/
public interface AssetStatisticsService {
/**
* 查询工作台统计信息
*
* @return data
*/
AssetWorkplaceStatisticsVO getWorkplaceStatisticsData();
/**
* 获取用户终端连接日志数量图表
*
* @param userId userId
* @return data
*/
LineSingleChartData getTerminalConnectCountChart(Long userId);
/**
* 获取用户执行日志数量图表
*
* @param userId userId
* @param source source
* @return chart
*/
LineSingleChartData getUserExecLogCountChart(Long userId, ExecSourceEnum source);
}

View File

@@ -0,0 +1,178 @@
/*
* Copyright (c) 2023 - present Dromara, All rights reserved.
*
* https://visor.dromara.org
* https://visor.dromara.org.cn
* https://visor.orionsec.cn
*
* Members:
* Jiahang Li - ljh1553488six@139.com - author
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.visor.module.asset.service.impl;
import cn.orionsec.kit.lang.utils.collect.Lists;
import cn.orionsec.kit.lang.utils.time.Dates;
import org.dromara.visor.framework.common.entity.StatisticsRange;
import org.dromara.visor.framework.common.entity.chart.LineSingleChartData;
import org.dromara.visor.framework.redis.core.utils.RedisStrings;
import org.dromara.visor.framework.security.core.utils.SecurityUtils;
import org.dromara.visor.module.asset.convert.ExecLogConvert;
import org.dromara.visor.module.asset.convert.TerminalConnectLogConvert;
import org.dromara.visor.module.asset.dao.ExecJobDAO;
import org.dromara.visor.module.asset.dao.ExecLogDAO;
import org.dromara.visor.module.asset.dao.TerminalConnectLogDAO;
import org.dromara.visor.module.asset.define.cache.AssetStatisticsCacheKeyDefine;
import org.dromara.visor.module.asset.entity.domain.ExecJobDO;
import org.dromara.visor.module.asset.entity.domain.ExecLogDO;
import org.dromara.visor.module.asset.entity.domain.TerminalConnectLogDO;
import org.dromara.visor.module.asset.entity.po.ExecLogCountPO;
import org.dromara.visor.module.asset.entity.po.TerminalConnectLogCountPO;
import org.dromara.visor.module.asset.entity.vo.AssetWorkplaceStatisticsVO;
import org.dromara.visor.module.asset.entity.vo.ExecLogVO;
import org.dromara.visor.module.asset.entity.vo.TerminalConnectLogVO;
import org.dromara.visor.module.asset.enums.ExecSourceEnum;
import org.dromara.visor.module.asset.service.AssetStatisticsService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* 资产模块统计服务实现
*
* @author Jiahang Li
* @version 1.0.0
* @since 2024/12/23 20:47
*/
@Service
public class AssetStatisticsServiceImpl implements AssetStatisticsService {
@Resource
private ExecJobDAO execJobDAO;
@Resource
private ExecLogDAO execLogDAO;
@Resource
private TerminalConnectLogDAO terminalConnectLogDAO;
@Override
public AssetWorkplaceStatisticsVO getWorkplaceStatisticsData() {
Long userId = SecurityUtils.getLoginUserId();
// 读取缓存
String cacheKey = AssetStatisticsCacheKeyDefine.WORKPLACE_DATA.format(userId, Dates.current(Dates.YMD2));
AssetWorkplaceStatisticsVO data = RedisStrings.getJson(cacheKey, AssetStatisticsCacheKeyDefine.WORKPLACE_DATA);
if (data == null) {
// 查询执行的计划任务数量
int execJobCount = execJobDAO.of()
.createWrapper()
.eq(ExecJobDO::getExecUserId, userId)
.then()
.count()
.intValue();
// 查询批量执行次数图表
LineSingleChartData execLogCountChart = this.getUserExecLogCountChart(userId, ExecSourceEnum.BATCH);
List<Integer> execLogCountData = execLogCountChart.getData();
int execLogCount = execLogCountData.stream()
.mapToInt(Integer::intValue)
.sum();
// 查询终端连接次数图表
LineSingleChartData terminalConnectCountChart = this.getTerminalConnectCountChart(userId);
List<Integer> terminalConnectCountData = terminalConnectCountChart.getData();
int terminalConnectCount = terminalConnectCountData.stream()
.mapToInt(Integer::intValue)
.sum();
data = AssetWorkplaceStatisticsVO.builder()
.execJobCount(execJobCount)
.todayExecCommandCount(Lists.last(execLogCountData))
.weekExecCommandCount(execLogCount)
.todayTerminalConnectCount(Lists.last(terminalConnectCountData))
.weekTerminalConnectCount(terminalConnectCount)
.execCommandChart(execLogCountChart)
.terminalConnectChart(terminalConnectCountChart)
.build();
// 设置缓存
RedisStrings.setJson(cacheKey, AssetStatisticsCacheKeyDefine.WORKPLACE_DATA, data);
}
// 查询命令执行记录
List<ExecLogVO> execLogList = execLogDAO.of()
.createWrapper()
.eq(ExecLogDO::getUserId, userId)
.eq(ExecLogDO::getSource, ExecSourceEnum.BATCH.name())
.orderByDesc(ExecLogDO::getId)
.then()
.limit(10)
.list(ExecLogConvert.MAPPER::to);
data.setExecLogList(execLogList);
// 查询终端连接记录
List<TerminalConnectLogVO> connectList = terminalConnectLogDAO.of()
.createWrapper()
.eq(TerminalConnectLogDO::getUserId, userId)
.orderByDesc(TerminalConnectLogDO::getId)
.then()
.limit(10)
.list(TerminalConnectLogConvert.MAPPER::to);
data.setTerminalConnectList(connectList);
return data;
}
@Override
public LineSingleChartData getUserExecLogCountChart(Long userId, ExecSourceEnum source) {
Date endTime = new Date();
Date startTime = Dates.stream()
.clearHms()
.addDay(-6)
.get();
List<String> rangeDays = StatisticsRange.WEEK.getDateRanges(startTime);
// 查询连接数量
Map<String, Integer> countMap = execLogDAO.selectExecLogCount(userId, source.name(), startTime, endTime)
.stream()
.collect(Collectors.toMap(ExecLogCountPO::getExecDate, ExecLogCountPO::getCount));
// 构建每天的数据
List<Integer> data = rangeDays.stream()
.map(s -> countMap.getOrDefault(s, 0))
.collect(Collectors.toList());
return LineSingleChartData.builder()
.x(rangeDays)
.data(data)
.build();
}
@Override
public LineSingleChartData getTerminalConnectCountChart(Long userId) {
Date endTime = new Date();
Date startTime = Dates.stream()
.clearHms()
.addDay(-6)
.get();
List<String> rangeDays = StatisticsRange.WEEK.getDateRanges(startTime);
// 查询连接数量
Map<String, Integer> countMap = terminalConnectLogDAO.selectConnectLogUserCount(userId, startTime, endTime)
.stream()
.collect(Collectors.toMap(TerminalConnectLogCountPO::getConnectDate, TerminalConnectLogCountPO::getCount));
// 构建每天的数据
List<Integer> data = rangeDays.stream()
.map(s -> countMap.getOrDefault(s, 0))
.collect(Collectors.toList());
return LineSingleChartData.builder()
.x(rangeDays)
.data(data)
.build();
}
}

View File

@@ -26,6 +26,12 @@
<result column="deleted" property="deleted"/>
</resultMap>
<!-- 数量询映射结果 -->
<resultMap id="CountResultMap" type="org.dromara.visor.module.asset.entity.po.ExecLogCountPO">
<result column="exec_date" property="execDate"/>
<result column="total_count" property="count"/>
</resultMap>
<!-- 通用查询结果列 -->
<sql id="Base_Column_List">
id, user_id, username, source, source_id, exec_mode, description, exec_seq, command, parameter_schema, timeout, script_exec, status, start_time, finish_time, create_time, update_time, creator, updater, deleted
@@ -47,4 +53,16 @@
) sub ON e.id = sub.max_id
</select>
<select id="selectExecLogCount" resultMap="CountResultMap">
SELECT DATE(create_time) exec_date, COUNT(1) total_count
FROM exec_log
WHERE deleted = 0
<if test="userId != null">
AND user_id = #{userId}
</if>
AND source = #{source}
AND create_time BETWEEN #{startTime} AND #{endTime}
GROUP BY exec_date
</select>
</mapper>

View File

@@ -21,6 +21,13 @@
<result column="deleted" property="deleted"/>
</resultMap>
<!-- 数量查询映射结果 -->
<resultMap id="CountResultMap" type="org.dromara.visor.module.asset.entity.po.TerminalConnectLogCountPO">
<result column="connect_date" property="connectDate"/>
<result column="type" property="type"/>
<result column="total_count" property="count"/>
</resultMap>
<!-- 通用查询结果列 -->
<sql id="Base_Column_List">
id, user_id, username, host_id, host_name, host_address, type, token, status, start_time, end_time, extra_info, create_time, update_time, deleted
@@ -36,4 +43,13 @@
LIMIT #{limit}
</select>
<select id="selectConnectLogUserCount" resultMap="CountResultMap">
SELECT DATE(create_time) connect_date, COUNT(1) total_count
FROM terminal_connect_log
WHERE deleted = 0
AND user_id = #{userId}
AND create_time BETWEEN #{startTime} AND #{endTime}
GROUP BY connect_date
</select>
</mapper>