Compare commits

..

24 Commits

Author SHA1 Message Date
lijiahangmax
2f48ba95a2 Merge pull request #8 from lijiahangmax/dev
Dev
2024-04-26 17:08:48 +08:00
lijiahang
f420315d3e 设置缓存过期时间. 2024-04-26 16:37:47 +08:00
lijiahang
2b8d64da45 🚀 修复构建失败. 2024-04-26 16:12:06 +08:00
lijiahang
4fb4afdd53 查询用户角色. 2024-04-26 16:01:06 +08:00
lijiahang
1decd92bd9 添加终端快捷键. 2024-04-26 16:00:24 +08:00
lijiahang
91b25b8d0c 📝 更新文档. 2024-04-26 11:55:47 +08:00
lijiahang
af8fedbe44 ansi 执行日志. 2024-04-26 11:21:56 +08:00
lijiahang
142c0fff1d 添加分布式锁组件. 2024-04-25 19:00:12 +08:00
lijiahang
f5b07ee906 执行日志文件自动清理. 2024-04-25 16:40:00 +08:00
lijiahang
d52c1f4d88 定时删除未使用的分组. 2024-04-25 11:50:09 +08:00
lijiahang
d6a021b4d9 路径标签. 2024-04-24 16:43:59 +08:00
lijiahang
8ecb5a687e 路径标签. 2024-04-24 13:39:21 +08:00
lijiahangmax
cdf10770d6 🚧 命令书签. 2024-04-24 00:13:42 +08:00
lijiahang
e04e14a6e4 🔨 命令分组替换为通用分组模型. 2024-04-23 19:10:15 +08:00
lijiahang
f146989a1a 修改缓存时间. 2024-04-23 16:38:47 +08:00
lijiahang
c14c0248cb 🔨 数据分组添加 userId. 2024-04-23 16:26:55 +08:00
lijiahang
b6ee28731b 回车重连. 2024-04-23 11:07:01 +08:00
lijiahangmax
c8c947d8a8 回车重连. 2024-04-23 09:15:22 +08:00
lijiahangmax
c66e62623e 🐛 释放资源. 2024-04-22 21:43:12 +08:00
lijiahang
f1207be0ec 🔨 回车重连. 2024-04-22 18:48:52 +08:00
lijiahang
be87c930e2 🔨 执行模板主机. 2024-04-22 16:27:19 +08:00
lijiahang
e057ab858f 🔨 模板主机后端服务. 2024-04-22 13:59:55 +08:00
lijiahang
30e0d23d11 Merge remote-tracking branch 'origin/dev' into dev
# Conflicts:
#	README.md
#	docs/README.md
2024-04-22 11:26:05 +08:00
lijiahang
c46fa28d25 🔖 升级版本. 2024-04-22 10:54:37 +08:00
185 changed files with 4987 additions and 896 deletions

View File

@@ -26,7 +26,7 @@
</a>
</p>
当前版本: **1.0.5**
当前版本: **1.0.6**
**github:** https://github.com/lijiahangmax/orion-ops-pro
**gitee:** https://gitee.com/lijiahangmax/orion-ops-pro

View File

@@ -1,7 +1,7 @@
version: '3.3'
services:
orion-ops-pro:
image: registry.cn-hangzhou.aliyuncs.com/lijiahangmax/orion-ops-pro:1.0.5
image: registry.cn-hangzhou.aliyuncs.com/lijiahangmax/orion-ops-pro:1.0.6
ports:
- 1081:80
environment:
@@ -13,6 +13,7 @@ services:
- SECRET_KEY=uQeacXV8b3isvKLK
volumes:
- /data/orion-ops-pro-space/docker-volumes/orion-ops-pro/logs:/root/orion/logs/orion-ops-pro
- /data/orion-ops-pro-space/docker-volumes/orion-ops-pro/app:/root/orion/orion-ops-pro
depends_on:
- orion-ops-pro-db
- orion-ops-pro-redis
@@ -34,11 +35,12 @@ services:
- /data/orion-ops-pro-space/docker-volumes/mysql/etc-mysql:/etc/mysql
orion-ops-pro-redis:
image: redis:6.0.16-alpine
command: redis-server --requirepass Data@123456
command: redis-server --appendonly yes --requirepass Data@123456
ports:
- 6380:6379
volumes:
- /data/orion-ops-pro-space/docker-volumes/redis/data:/data
- /data/orion-ops-pro-space/docker-volumes/redis/redis.conf:/usr/local/etc/redis/redis.conf
orion-ops-pro-adminer:
image: adminer
ports:

View File

@@ -1,3 +1,3 @@
mv ../../orion-ops-launch/target/orion-ops-launch.jar ./
mv ../../orion-ops-ui/dist ./dist
docker build -t orion-ops-pro:1.0.5 .
docker build -t orion-ops-pro:1.0.6 .

View File

@@ -26,7 +26,7 @@
</a>
</p>
当前版本: **1.0.5**
当前版本: **1.0.6**
**github:** https://github.com/lijiahangmax/orion-ops-pro
**gitee:** https://gitee.com/lijiahangmax/orion-ops-pro

View File

@@ -1,4 +1,4 @@
# orion-ops-pro <small>1.0.5</small>
# orion-ops-pro <small>1.0.6</small>
> 一款开箱即用的运维平台。

View File

@@ -1,9 +1,33 @@
> 版本号严格遵循 Semver 规范。
***sql 脚本可以在 adminer 中执行。**
***应用不支持跨版本升级, 可以进行多次升级。**
### 升级须知
## v1.0.5
* 应用 **不支持** 跨版本升级, 可以进行 **多次升级**
* docker 安装的 sql 脚本可以在 `adminer` 中执行。
* 执行完成菜单 sql 后请刷新缓存 `系统设置` > `系统菜单` > `刷新缓存`
* 执行完成字典 sql 后请刷新缓存 `系统设置` > `数据字典项` > `刷新缓存`
### v1.0.6
`2024-04-26` `release`
* 🐞 修复 终端页签关闭后不会自动切换
* 🩰 修改 命令执行日志 UI 修改
* 🌈 新增 命令执行模板配置默认主机
* 🌈 新增 主机终端书签路径
* 🌈 新增 命令执行日志添加 `ansi` 日志 `app.exec-log.append-ansi`
* 🌈 新增 定时删除命令执行日志文件 `app.exec-log.auto-clear`
* 🌈 新增 终端设置添加了几个全局快捷键
* 🔨 优化 通用分组模型添加 `userId`
* 🔨 优化 退出登录不重定向
* 🔨 优化 动态设置页面标题
* 🔨 优化 终端断开后回车重连
* 🔨 优化 自动删除未使用的命令片段分组
* 🔨 优化 添加分布式锁工具类
[如何升级](/update/v1.0.6.md)
### v1.0.5
`2024-04-22` `release`
@@ -28,7 +52,7 @@
[如何升级](/update/v1.0.5.md)
## v1.0.4
### v1.0.4
`2024-04-15` `release`
@@ -50,7 +74,7 @@
[如何升级](/update/v1.0.4.md)
## v1.0.3
### v1.0.3
`2024-03-25` `release`
@@ -65,7 +89,7 @@
[如何升级](/update/v1.0.3.md)
## v1.0.2
### v1.0.2
`2024-03-22` `release`
@@ -84,7 +108,7 @@
[如何升级](/update/v1.0.2.md)
## v1.0.1
### v1.0.1
`2024-03-06` `release`
@@ -99,7 +123,7 @@
[如何升级](/update/v1.0.1.md)
## v1.0.0
### v1.0.0
`2024-03-01` `release`
@@ -108,7 +132,7 @@
[如何升级](/update/v1.0.0.md)
## v1.0.0-beta.1
### v1.0.0-beta.1
`2024-02-28` `preview`

View File

@@ -1,8 +1,6 @@
## 功能排期
* 默认主机
* 批量上传
* 文件夹书签
* 站内消息
* 终端背景图片
* 资产授权 UI 改版

View File

@@ -3,24 +3,24 @@
数据库的数据都采用了逻辑删除, 可以将已删除的数据中的 `deleted` 字段改为 `0`
如果不知道数据是哪一条, 可以查询用户操作日志, 点击 `参数` 寻找操作的id
> ##### 2. 是否支持维护 Windows 主机?
支持, 但是 Windows 的 ssh 命令兼容性不好, 一切需要执行ssh命令的地方都不友好
> ##### 3. 执行命令时为什么会找不到环境变量?
> ##### 2. 执行命令时为什么会找不到环境变量?
可以在执行命令的第一行设置 `source /etc/profile` 来加载环境变量
> ##### 4. 命令中途执行失败如何设置中断执行?
> ##### 3. 命令中途执行失败如何设置中断执行?
可以在执行命令的第一行设置 `set -e`
作用是: 当执行出现意料之外的情况时, 立即退出
> ##### 5. 在调度任务、批量执行 命令执行成功的依据是什么?
> ##### 4. 在调度任务、批量执行 命令执行成功的依据是什么?
是获取命令的 `exitcode` 判断是否为 `0` 如果非0则代表命令执行失败
同理, 在命令的最后一行设置 `exit 1` 结果将会是失败, 可以用此来中断后续流程
> ##### 5. 调度任务、批量执行 的日志文件中如何只保存原始输出?
修改 application.yaml `app.exec-log.append-ansi` 为 false
> ##### 6. 为什么使用秘钥认证还是无法连接机器?
```

View File

@@ -3,9 +3,104 @@
> sql 脚本 - DDL
```sql
-- 数据分组添加 userId
ALTER TABLE `data_group`
ADD COLUMN `user_id` bigint(0) NULL COMMENT '用户id' AFTER `type`,
MODIFY COLUMN `name` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '组名称' AFTER `user_id`,
DROP INDEX `idx_type`,
ADD INDEX `idx_type_user`(`type`, `user_id`) USING BTREE;
ALTER TABLE `data_group_rel`
MODIFY COLUMN `type` char(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '组类型' AFTER `id`,
ADD COLUMN `user_id` bigint(0) NULL COMMENT '用户id' AFTER `type`,
DROP INDEX `idx_type`,
ADD INDEX `idx_type_user`(`type`, `user_id`) USING BTREE;
-- 执行模板主机表
DROP TABLE IF EXISTS `exec_template_host`;
CREATE TABLE `exec_template_host`
(
`id` bigint(0) NOT NULL AUTO_INCREMENT COMMENT 'id',
`template_id` bigint(0) NULL DEFAULT NULL COMMENT '模板id',
`host_id` bigint(0) NULL DEFAULT NULL COMMENT '主机id',
`create_time` datetime(0) NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime(0) NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP(0) COMMENT '修改时间',
`creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '创建人',
`updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '更新人',
`deleted` tinyint(1) NULL DEFAULT 0 COMMENT '是否删除 0未删除 1已删除',
PRIMARY KEY (`id`) USING BTREE,
INDEX `template_id` (`template_id`) USING BTREE
) ENGINE = InnoDB
AUTO_INCREMENT = 1
CHARACTER SET = utf8mb4
COLLATE = utf8mb4_general_ci COMMENT = '执行模板主机'
ROW_FORMAT = Dynamic;
-- 路径书签表
DROP TABLE IF EXISTS `path_bookmark`;
CREATE TABLE `path_bookmark`
(
`id` bigint(0) NOT NULL AUTO_INCREMENT COMMENT 'id',
`user_id` bigint(0) NULL DEFAULT NULL COMMENT '用户id',
`group_id` bigint(0) NULL DEFAULT NULL COMMENT '分组id',
`name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '名称',
`type` char(4) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '类型',
`path` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '路径',
`create_time` datetime(0) NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime(0) NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP(0) COMMENT '修改时间',
`creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '创建人',
`updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '更新人',
`deleted` tinyint(1) NULL DEFAULT 0 COMMENT '是否删除 0未删除 1已删除',
PRIMARY KEY (`id`) USING BTREE,
INDEX `idx_user` (`user_id`) USING BTREE
) ENGINE = InnoDB
AUTO_INCREMENT = 1
CHARACTER SET = utf8mb4
COLLATE = utf8mb4_general_ci COMMENT = '路径书签'
ROW_FORMAT = Dynamic;
```
> sql 脚本 - DML
```sql
-- 设置数据分组 user_id
UPDATE data_group SET user_id = 0;
UPDATE data_group_rel SET user_id = 0;
-- 菜单
DELETE FROM `system_menu` WHERE id >= 190;
INSERT INTO `system_menu` VALUES (190, 184, '中断计划任务', 'asset:exec-job-log:interrupt', 3, 40, 1, 1, 1, 0, NULL, NULL, NULL, '2024-03-13 15:08:23', '2024-04-12 00:00:33', '1', '1', 0);
INSERT INTO `system_menu` VALUES (191, 0, '提交bug', NULL, 1, 1020, 1, 1, 0, 1, 'IconBug', 'https://github.com/lijiahangmax/orion-ops-pro/issues', NULL, '2024-04-26 11:30:18', '2024-04-26 11:30:30', '1', '1', 0);
INSERT INTO `system_menu` VALUES (192, 0, '点个赞~', NULL, 1, 1030, 1, 1, 0, 1, 'IconThumbUp', 'https://gitee.com/lijiahangmax/orion-ops-pro', NULL, '2024-04-26 11:32:30', '2024-04-26 11:32:30', '1', '1', 0);
-- 字典项
DELETE FROM `dict_key` WHERE id = 27 OR id >= 39;
INSERT INTO `dict_key` VALUES (27, 'hostConnectType', 'STRING', '[{\"name\": \"color\", \"type\": \"COLOR\"}]', '主机连接类型', '2023-12-26 23:23:08', '2024-04-24 16:37:48', '1', '1', 0);
INSERT INTO `dict_key` VALUES (39, 'pathBookmarkType', 'STRING', '[]', '路径标签类型', '2024-04-24 13:43:12', '2024-04-24 13:43:12', '1', '1', 0);
-- 字典值
DELETE FROM `dict_value` WHERE id IN (7, 9, 18, 21, 176, 193) OR id >= 274;
INSERT INTO `dict_value` VALUES (7, 5, 'systemMenuStatus', '1', '启用', '{\"color\": \"arcoblue\"}', 20, '2023-10-26 17:00:54', '2024-04-24 16:35:09', '1', '1', 0);
INSERT INTO `dict_value` VALUES (9, 6, 'systemMenuVisible', '1', '显示', '{\"color\": \"arcoblue\"}', 20, '2023-10-27 00:25:30', '2024-04-24 16:35:05', '1', '1', 0);
INSERT INTO `dict_value` VALUES (18, 9, 'systemUserStatus', '1', '启用', '{\"color\": \"arcoblue\"}', 20, '2023-10-27 12:13:17', '2024-04-24 16:35:00', '1', '1', 0);
INSERT INTO `dict_value` VALUES (21, 10, 'systemRoleStatus', '1', '启用', '{\"color\": \"arcoblue\", \"status\": \"default\"}', 20, '2023-10-27 12:33:56', '2024-04-24 16:34:52', '1', '1', 0);
INSERT INTO `dict_value` VALUES (176, 27, 'hostConnectType', 'SSH', 'SSH', '{\"color\": \"arcoblue\"}', 10, '2023-12-26 23:23:18', '2024-04-24 16:38:28', '1', '1', 0);
INSERT INTO `dict_value` VALUES (193, 27, 'hostConnectType', 'SFTP', 'SFTP', '{\"color\": \"purple\"}', 20, '2024-02-04 18:23:10', '2024-04-24 16:38:22', '1', '1', 0);
INSERT INTO `dict_value` VALUES (274, 39, 'pathBookmarkType', 'FILE', '文件', '{}', 10, '2024-04-24 13:43:28', '2024-04-24 13:43:28', '1', '1', 0);
INSERT INTO `dict_value` VALUES (275, 39, 'pathBookmarkType', 'DIR', '文件夹', '{}', 20, '2024-04-24 13:43:39', '2024-04-24 13:43:39', '1', '1', 0);
```
> sql 脚本 - 命令分组初始化
```sql
-- 插入命令片段分组
INSERT INTO `data_group` (`parent_id`, `type`, `user_id`, `name`, `sort`, `creator`, `updater`, `deleted`)
SELECT 0, 'COMMAND_SNIPPET', user_id, name, id, creator, updater, deleted
FROM command_snippet_group;
-- 需要命令分组 groupId
UPDATE command_snippet s
LEFT JOIN data_group g ON g.type = 'COMMAND_SNIPPET' AND g.sort = s.group_id
SET s.group_id = g.id;
-- 删除命令片段分组表
DROP TABLE IF EXISTS `command_snippet_group`;
```

11
docs/update/v1.0.7.md Normal file
View File

@@ -0,0 +1,11 @@
## v1.0.7
> sql 脚本 - DDL
```sql
```
> sql 脚本 - DML
```sql
```

View File

@@ -14,7 +14,7 @@
<url>https://github.com/lijiahangmax/orion-ops-pro</url>
<properties>
<revision>1.0.5</revision>
<revision>1.0.6</revision>
<spring.boot.version>2.7.17</spring.boot.version>
<spring.boot.admin.version>2.7.15</spring.boot.admin.version>
<flatten.maven.plugin.version>1.5.0</flatten.maven.plugin.version>

View File

@@ -14,7 +14,7 @@ public interface AppConst extends OrionConst {
/**
* 同 ${orion.version} 迭代时候需要手动更改
*/
String VERSION = "1.0.5";
String VERSION = "1.0.6";
String ORION_OPS_PRO = "orion-ops-pro";
@@ -22,6 +22,6 @@ public interface AppConst extends OrionConst {
String GITEE = "https://gitee.com/lijiahangmax/orion-ops-pro";
String ISSUES = "https://gitee.com/lijiahangmax/orion-ops-pro/issues";
String ISSUES = "https://github.com/lijiahangmax/orion-ops-pro/issues";
}

View File

@@ -37,7 +37,6 @@ public class PathUtils {
}
}
/**
* 获取应用路径
*

View File

@@ -10,6 +10,7 @@ import com.orion.ops.framework.mybatis.core.generator.template.Table;
import com.orion.ops.framework.mybatis.core.generator.template.Template;
import java.io.File;
import java.util.concurrent.TimeUnit;
/**
* 代码生成器
@@ -33,7 +34,7 @@ public class CodeGenerators {
// .enableProviderApi()
// .disableUnitTest()
// .cache("dict:keys", "字典配置项")
// .expire(1, TimeUnit.DAYS)
// .expire(8, TimeUnit.HOURS)
// .vue("system", "dict-key")
// .enableRowSelection()
// .enableCardView()
@@ -44,20 +45,15 @@ public class CodeGenerators {
// .color("blue", "gray", "red", "green", "white")
// .valueUseFields()
// .build(),
Template.create("exec_job", "计划任务", "exec")
// Template.create("exec_template_host", "执行模板主机", "exec")
// .disableUnitTest()
// .vue("exec", "exec-template-host")
// .build(),
Template.create("path_bookmark", "路径标签", "path")
.disableUnitTest()
.vue("exec", "exec-job")
.enableDrawerForm()
.dict("execJobStatus", "status")
.comment("计划任务状态")
.field("execJobStatus")
.fields("DISABLED", "ENABLED")
.labels("禁用", "启用")
.values(0, 1)
.build(),
Template.create("exec_job_host", "计划任务主机", "exec")
.disableUnitTest()
.vue("exec", "exec-job-host")
.cache("path:bookmark:list:{}", "路径标签列表 ${userId}")
.expire(8, TimeUnit.HOURS)
.vue("host", "path-bookmark")
.build(),
};
// jdbc 配置 - 使用配置文件

View File

@@ -2,7 +2,10 @@ package com.orion.ops.framework.redis.configuration;
import com.orion.ops.framework.common.constant.AutoConfigureOrderConst;
import com.orion.ops.framework.redis.configuration.config.RedissonConfig;
import com.orion.ops.framework.redis.core.lock.RedisLocker;
import com.orion.ops.framework.redis.core.utils.RedisLocks;
import com.orion.ops.framework.redis.core.utils.RedisUtils;
import org.redisson.api.RedissonClient;
import org.redisson.spring.starter.RedissonAutoConfigurationCustomizer;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigureOrder;
@@ -56,5 +59,16 @@ public class OrionRedisAutoConfiguration {
};
}
/**
* @param redissonClient redissonClient
* @return redis 分布式锁
*/
@Bean
public RedisLocker redisLocker(RedissonClient redissonClient) {
RedisLocker redisLocker = new RedisLocker(redissonClient);
RedisLocks.setRedisLocker(redisLocker);
return redisLocker;
}
}

View File

@@ -0,0 +1,74 @@
package com.orion.ops.framework.redis.core.lock;
import com.orion.lang.utils.Exceptions;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import java.util.function.Supplier;
/**
* redis 分布式锁
*
* @author Jiahang Li
* @version 1.0.0
* @since 2024/4/25 16:42
*/
@Slf4j
public class RedisLocker {
private final RedissonClient redissonClient;
public RedisLocker(RedissonClient redissonClient) {
this.redissonClient = redissonClient;
}
/**
* 尝试获取锁
*
* @param key key
* @param run run
* @return 是否获取到锁
*/
public boolean tryLock(String key, Runnable run) {
// 获取锁
RLock lock = redissonClient.getLock(key);
// 未获取到直接返回
if (!lock.tryLock()) {
log.info("RedisLocks.tryLock failed {}", key);
return false;
}
// 执行
try {
run.run();
} finally {
lock.unlock();
}
return true;
}
/**
* 尝试获取锁
*
* @param key key
* @param call call
* @param <T> T
* @return 执行结果
*/
public <T> T tryLock(String key, Supplier<T> call) {
// 获取锁
RLock lock = redissonClient.getLock(key);
// 未获取到直接返回
if (!lock.tryLock()) {
log.info("RedisLocks.tryLock failed {}", key);
throw Exceptions.lock();
}
// 执行
try {
return call.get();
} finally {
lock.unlock();
}
}
}

View File

@@ -0,0 +1,55 @@
package com.orion.ops.framework.redis.core.utils;
import com.orion.lang.utils.Exceptions;
import com.orion.ops.framework.redis.core.lock.RedisLocker;
import lombok.extern.slf4j.Slf4j;
import java.util.function.Supplier;
/**
* redis 分布式锁工具类
*
* @author Jiahang Li
* @version 1.0.0
* @since 2024/4/25 16:42
*/
@Slf4j
public class RedisLocks {
private static RedisLocker redisLocker;
private RedisLocks() {
}
/**
* 尝试获取锁
*
* @param key key
* @param run run
* @return 是否获取到锁
*/
public static boolean tryLock(String key, Runnable run) {
return redisLocker.tryLock(key, run);
}
/**
* 尝试获取锁
*
* @param key key
* @param call call
* @param <T> T
* @return 执行结果
*/
public static <T> T tryLock(String key, Supplier<T> call) {
return redisLocker.tryLock(key, call);
}
public static void setRedisLocker(RedisLocker redisLocker) {
if (RedisLocks.redisLocker != null) {
// unmodified
throw Exceptions.state();
}
RedisLocks.redisLocker = redisLocker;
}
}

View File

@@ -177,6 +177,14 @@ app:
upload-present-backup: true
# 备份文件名称
backup-file-name: bk_${fileName}_${timestamp}
# 执行日志
exec-log:
# 是否拼接 ansi 执行状态日志
append-ansi: true
# 自动清理执行文件
auto-clear: true
# 保留周期 (天)
keep-period: 60
# orion framework config
orion:

View File

@@ -66,6 +66,15 @@ public class ExecTemplateController {
return execTemplateService.getExecTemplateById(id);
}
@IgnoreLog(IgnoreLogMode.RET)
@GetMapping("/get-with-authorized")
@Operation(summary = "查询执行模板 (查询认证的主机)")
@Parameter(name = "id", description = "id", required = true)
@PreAuthorize("@ss.hasPermission('asset:exec-template:query')")
public ExecTemplateVO getExecTemplateWithAuthorized(@RequestParam("id") Long id) {
return execTemplateService.getExecTemplateWithAuthorized(id);
}
@IgnoreLog(IgnoreLogMode.RET)
@PostMapping("/query")
@Operation(summary = "分页查询执行模板")
@@ -83,6 +92,5 @@ public class ExecTemplateController {
return execTemplateService.deleteExecTemplateById(id);
}
}

View File

@@ -0,0 +1,34 @@
### 创建路径标签
POST {{baseUrl}}/asset/path-bookmark/create
Content-Type: application/json
Authorization: {{token}}
{
"name": "",
"path": ""
}
### 更新路径标签
PUT {{baseUrl}}/asset/path-bookmark/update
Content-Type: application/json
Authorization: {{token}}
{
"id": "",
"name": "",
"path": ""
}
### 查询全部路径标签
GET {{baseUrl}}/asset/path-bookmark/list
Authorization: {{token}}
### 删除路径标签
DELETE {{baseUrl}}/asset/path-bookmark/delete?id=1
Authorization: {{token}}
###

View File

@@ -0,0 +1,62 @@
package com.orion.ops.module.asset.controller;
import com.orion.ops.framework.web.core.annotation.RestWrapper;
import com.orion.ops.module.asset.entity.request.path.PathBookmarkCreateRequest;
import com.orion.ops.module.asset.entity.request.path.PathBookmarkUpdateRequest;
import com.orion.ops.module.asset.entity.vo.PathBookmarkWrapperVO;
import com.orion.ops.module.asset.service.PathBookmarkService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
/**
* 路径标签 api
*
* @author Jiahang Li
* @version 1.0.6
* @since 2024-4-23 23:15
*/
@Tag(name = "asset - 路径标签服务")
@Slf4j
@Validated
@RestWrapper
@RestController
@RequestMapping("/asset/path-bookmark")
@SuppressWarnings({"ELValidationInJSP", "SpringElInspection"})
public class PathBookmarkController {
@Resource
private PathBookmarkService pathBookmarkService;
@PostMapping("/create")
@Operation(summary = "创建路径标签")
public Long createPathBookmark(@Validated @RequestBody PathBookmarkCreateRequest request) {
return pathBookmarkService.createPathBookmark(request);
}
@PutMapping("/update")
@Operation(summary = "更新路径标签")
public Integer updatePathBookmark(@Validated @RequestBody PathBookmarkUpdateRequest request) {
return pathBookmarkService.updatePathBookmarkById(request);
}
@GetMapping("/list")
@Operation(summary = "查询全部路径标签")
public PathBookmarkWrapperVO getPathBookmarkList() {
return pathBookmarkService.getPathBookmark();
}
@DeleteMapping("/delete")
@Operation(summary = "删除路径标签")
@Parameter(name = "id", description = "id", required = true)
public Integer deletePathBookmark(@RequestParam("id") Long id) {
return pathBookmarkService.deletePathBookmarkById(id);
}
}

View File

@@ -0,0 +1,32 @@
### 创建路径标签分组
POST {{baseUrl}}/asset/path-bookmark-group/create
Content-Type: application/json
Authorization: {{token}}
{
"name": ""
}
### 更新路径标签分组
PUT {{baseUrl}}/asset/path-bookmark-group/update
Content-Type: application/json
Authorization: {{token}}
{
"id": "",
"name": ""
}
### 查询全部路径标签分组
GET {{baseUrl}}/asset/path-bookmark-group/list
Authorization: {{token}}
### 删除路径标签分组
DELETE {{baseUrl}}/asset/path-bookmark-group/delete?id=1
Authorization: {{token}}
###

View File

@@ -0,0 +1,66 @@
package com.orion.ops.module.asset.controller;
import com.orion.ops.framework.common.validator.group.Id;
import com.orion.ops.framework.log.core.annotation.IgnoreLog;
import com.orion.ops.framework.log.core.enums.IgnoreLogMode;
import com.orion.ops.framework.web.core.annotation.RestWrapper;
import com.orion.ops.module.asset.entity.request.path.PathBookmarkGroupCreateRequest;
import com.orion.ops.module.asset.entity.request.path.PathBookmarkGroupDeleteRequest;
import com.orion.ops.module.asset.entity.request.path.PathBookmarkGroupUpdateRequest;
import com.orion.ops.module.asset.entity.vo.PathBookmarkGroupVO;
import com.orion.ops.module.asset.service.PathBookmarkGroupService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.List;
/**
* 路径标签分组 api
*
* @author Jiahang Li
* @version 1.0.0
* @since 2024-1-24 12:28
*/
@Tag(name = "asset - 路径标签分组服务")
@Slf4j
@Validated
@RestWrapper
@RestController
@RequestMapping("/asset/path-bookmark-group")
@SuppressWarnings({"ELValidationInJSP", "SpringElInspection"})
public class PathBookmarkGroupController {
@Resource
private PathBookmarkGroupService pathBookmarkGroupService;
@PostMapping("/create")
@Operation(summary = "创建路径标签分组")
public Long createPathBookmarkGroup(@Validated @RequestBody PathBookmarkGroupCreateRequest request) {
return pathBookmarkGroupService.createPathBookmarkGroup(request);
}
@PutMapping("/update")
@Operation(summary = "更新路径标签分组")
public Integer updatePathBookmarkGroup(@Validated @RequestBody PathBookmarkGroupUpdateRequest request) {
return pathBookmarkGroupService.updatePathBookmarkGroupById(request);
}
@IgnoreLog(IgnoreLogMode.RET)
@GetMapping("/list")
@Operation(summary = "查询全部路径标签分组")
public List<PathBookmarkGroupVO> getPathBookmarkGroupList() {
return pathBookmarkGroupService.getPathBookmarkGroupList();
}
@DeleteMapping("/delete")
@Operation(summary = "删除路径标签分组")
public Integer deletePathBookmarkGroup(@Validated(Id.class) @RequestBody PathBookmarkGroupDeleteRequest request) {
return pathBookmarkGroupService.deletePathBookmarkGroup(request);
}
}

View File

@@ -1,15 +1,14 @@
package com.orion.ops.module.asset.convert;
import com.orion.ops.module.asset.entity.domain.CommandSnippetGroupDO;
import com.orion.ops.module.asset.entity.dto.CommandSnippetGroupCacheDTO;
import com.orion.ops.module.asset.entity.request.command.CommandSnippetGroupCreateRequest;
import com.orion.ops.module.asset.entity.request.command.CommandSnippetGroupUpdateRequest;
import com.orion.ops.module.asset.entity.vo.CommandSnippetGroupVO;
import com.orion.ops.module.infra.entity.dto.data.DataGroupCreateDTO;
import com.orion.ops.module.infra.entity.dto.data.DataGroupDTO;
import com.orion.ops.module.infra.entity.dto.data.DataGroupRenameDTO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
import java.util.List;
/**
* 命令片段分组 内部对象转换器
*
@@ -22,16 +21,10 @@ public interface CommandSnippetGroupConvert {
CommandSnippetGroupConvert MAPPER = Mappers.getMapper(CommandSnippetGroupConvert.class);
CommandSnippetGroupDO to(CommandSnippetGroupCreateRequest request);
DataGroupCreateDTO to(CommandSnippetGroupCreateRequest request);
CommandSnippetGroupDO to(CommandSnippetGroupUpdateRequest request);
DataGroupRenameDTO to(CommandSnippetGroupUpdateRequest request);
CommandSnippetGroupVO to(CommandSnippetGroupDO domain);
List<CommandSnippetGroupVO> to(List<CommandSnippetGroupDO> list);
CommandSnippetGroupVO to(CommandSnippetGroupCacheDTO cache);
CommandSnippetGroupCacheDTO toCache(CommandSnippetGroupDO domain);
CommandSnippetGroupVO to(DataGroupDTO domain);
}

View File

@@ -0,0 +1,37 @@
package com.orion.ops.module.asset.convert;
import com.orion.ops.module.asset.entity.domain.PathBookmarkDO;
import com.orion.ops.module.asset.entity.dto.PathBookmarkCacheDTO;
import com.orion.ops.module.asset.entity.request.path.PathBookmarkCreateRequest;
import com.orion.ops.module.asset.entity.request.path.PathBookmarkUpdateRequest;
import com.orion.ops.module.asset.entity.vo.PathBookmarkVO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
import java.util.List;
/**
* 路径标签 内部对象转换器
*
* @author Jiahang Li
* @version 1.0.6
* @since 2024-4-23 23:15
*/
@Mapper
public interface PathBookmarkConvert {
PathBookmarkConvert MAPPER = Mappers.getMapper(PathBookmarkConvert.class);
PathBookmarkDO to(PathBookmarkCreateRequest request);
PathBookmarkDO to(PathBookmarkUpdateRequest request);
PathBookmarkVO to(PathBookmarkDO domain);
List<PathBookmarkVO> to(List<PathBookmarkDO> list);
PathBookmarkVO to(PathBookmarkCacheDTO cache);
PathBookmarkCacheDTO toCache(PathBookmarkDO domain);
}

View File

@@ -0,0 +1,30 @@
package com.orion.ops.module.asset.convert;
import com.orion.ops.module.asset.entity.request.path.PathBookmarkGroupCreateRequest;
import com.orion.ops.module.asset.entity.request.path.PathBookmarkGroupUpdateRequest;
import com.orion.ops.module.asset.entity.vo.PathBookmarkGroupVO;
import com.orion.ops.module.infra.entity.dto.data.DataGroupCreateDTO;
import com.orion.ops.module.infra.entity.dto.data.DataGroupDTO;
import com.orion.ops.module.infra.entity.dto.data.DataGroupRenameDTO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
/**
* 路径标签分组 内部对象转换器
*
* @author Jiahang Li
* @version 1.0.0
* @since 2024-1-24 12:28
*/
@Mapper
public interface PathBookmarkGroupConvert {
PathBookmarkGroupConvert MAPPER = Mappers.getMapper(PathBookmarkGroupConvert.class);
DataGroupCreateDTO to(PathBookmarkGroupCreateRequest request);
DataGroupRenameDTO to(PathBookmarkGroupUpdateRequest request);
PathBookmarkGroupVO to(DataGroupDTO domain);
}

View File

@@ -1,17 +0,0 @@
package com.orion.ops.module.asset.dao;
import com.orion.ops.framework.mybatis.core.mapper.IMapper;
import com.orion.ops.module.asset.entity.domain.CommandSnippetGroupDO;
import org.apache.ibatis.annotations.Mapper;
/**
* 命令片段分组 Mapper 接口
*
* @author Jiahang Li
* @version 1.0.0
* @since 2024-1-24 12:28
*/
@Mapper
public interface CommandSnippetGroupDAO extends IMapper<CommandSnippetGroupDO> {
}

View File

@@ -0,0 +1,17 @@
package com.orion.ops.module.asset.dao;
import com.orion.ops.framework.mybatis.core.mapper.IMapper;
import com.orion.ops.module.asset.entity.domain.ExecTemplateHostDO;
import org.apache.ibatis.annotations.Mapper;
/**
* 执行模板主机 Mapper 接口
*
* @author Jiahang Li
* @version 1.0.6
* @since 2024-4-22 12:13
*/
@Mapper
public interface ExecTemplateHostDAO extends IMapper<ExecTemplateHostDO> {
}

View File

@@ -0,0 +1,45 @@
package com.orion.ops.module.asset.dao;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.orion.ops.framework.mybatis.core.mapper.IMapper;
import com.orion.ops.module.asset.entity.domain.PathBookmarkDO;
import org.apache.ibatis.annotations.Mapper;
/**
* 路径标签 Mapper 接口
*
* @author Jiahang Li
* @version 1.0.6
* @since 2024-4-23 23:15
*/
@Mapper
public interface PathBookmarkDAO extends IMapper<PathBookmarkDO> {
/**
* 设置 groupId 为 null
*
* @param groupId groupId
* @return effect
*/
default int setGroupIdWithNull(Long groupId) {
LambdaUpdateWrapper<PathBookmarkDO> wrapper = Wrappers.<PathBookmarkDO>lambdaUpdate()
.set(PathBookmarkDO::getGroupId, null)
.eq(PathBookmarkDO::getGroupId, groupId);
return this.update(null, wrapper);
}
/**
* 通过 groupId 删除
*
* @param groupId groupId
* @return effect
*/
default int deleteByGroupId(Long groupId) {
LambdaQueryWrapper<PathBookmarkDO> wrapper = this.lambda()
.eq(PathBookmarkDO::getGroupId, groupId);
return this.delete(wrapper);
}
}

View File

@@ -4,7 +4,6 @@ import com.orion.lang.define.cache.key.CacheKeyBuilder;
import com.orion.lang.define.cache.key.CacheKeyDefine;
import com.orion.lang.define.cache.key.struct.RedisCacheStruct;
import com.orion.ops.module.asset.entity.dto.CommandSnippetCacheDTO;
import com.orion.ops.module.asset.entity.dto.CommandSnippetGroupCacheDTO;
import java.util.concurrent.TimeUnit;
@@ -22,15 +21,7 @@ public interface CommandSnippetCacheKeyDefine {
.desc("命令片段列表 ${userId}")
.type(CommandSnippetCacheDTO.class)
.struct(RedisCacheStruct.HASH)
.timeout(1, TimeUnit.DAYS)
.build();
CacheKeyDefine SNIPPET_GROUP = new CacheKeyBuilder()
.key("command:snippet:group:{}")
.desc("命令片段分组 ${userId}")
.type(CommandSnippetGroupCacheDTO.class)
.struct(RedisCacheStruct.HASH)
.timeout(1, TimeUnit.DAYS)
.timeout(8, TimeUnit.HOURS)
.build();
}

View File

@@ -23,7 +23,7 @@ public interface HostCacheKeyDefine {
.desc("主机列表")
.type(HostCacheDTO.class)
.struct(RedisCacheStruct.HASH)
.timeout(1, TimeUnit.DAYS)
.timeout(8, TimeUnit.HOURS)
.build();
CacheKeyDefine HOST_KEY = new CacheKeyBuilder()
@@ -31,7 +31,7 @@ public interface HostCacheKeyDefine {
.desc("主机秘钥列表")
.type(HostKeyCacheDTO.class)
.struct(RedisCacheStruct.HASH)
.timeout(1, TimeUnit.DAYS)
.timeout(8, TimeUnit.HOURS)
.build();
CacheKeyDefine HOST_IDENTITY = new CacheKeyBuilder()
@@ -39,7 +39,7 @@ public interface HostCacheKeyDefine {
.desc("主机身份列表")
.type(HostIdentityCacheDTO.class)
.struct(RedisCacheStruct.HASH)
.timeout(1, TimeUnit.DAYS)
.timeout(8, TimeUnit.HOURS)
.build();
}

View File

@@ -0,0 +1,27 @@
package com.orion.ops.module.asset.define.cache;
import com.orion.lang.define.cache.key.CacheKeyBuilder;
import com.orion.lang.define.cache.key.CacheKeyDefine;
import com.orion.lang.define.cache.key.struct.RedisCacheStruct;
import com.orion.ops.module.asset.entity.dto.PathBookmarkCacheDTO;
import java.util.concurrent.TimeUnit;
/**
* 路径标签缓存 key
*
* @author Jiahang Li
* @version 1.0.6
* @since 2024-4-23 23:15
*/
public interface PathBookmarkCacheKeyDefine {
CacheKeyDefine PATH_BOOKMARK = new CacheKeyBuilder()
.key("path:bookmark:list:{}")
.desc("路径标签列表 ${userId}")
.type(PathBookmarkCacheDTO.class)
.struct(RedisCacheStruct.HASH)
.timeout(8, TimeUnit.HOURS)
.build();
}

View File

@@ -0,0 +1,40 @@
package com.orion.ops.module.asset.define.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* 应用执行日志配置
*
* @author Jiahang Li
* @version 1.0.0
* @since 2024/4/25 13:36
*/
@Data
@Component
@ConfigurationProperties(prefix = "app.exec-log")
public class AppExecLogConfig {
/**
* 是否拼接 ansi 执行状态日志
*/
private Boolean appendAnsi;
/**
* 自动清理执行文件
*/
private Boolean autoClear;
/**
* 保留周期 (天)
*/
private Integer keepPeriod;
public AppExecLogConfig() {
this.appendAnsi = true;
this.autoClear = true;
this.keepPeriod = 60;
}
}

View File

@@ -0,0 +1,41 @@
package com.orion.ops.module.asset.entity.domain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.orion.ops.framework.mybatis.core.domain.BaseDO;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
/**
* 执行模板主机 实体对象
*
* @author Jiahang Li
* @version 1.0.6
* @since 2024-4-22 12:13
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
@TableName(value = "exec_template_host", autoResultMap = true)
@Schema(name = "ExecTemplateHostDO", description = "执行模板主机 实体对象")
public class ExecTemplateHostDO extends BaseDO {
private static final long serialVersionUID = 1L;
@Schema(description = "id")
@TableId(value = "id", type = IdType.AUTO)
private Long id;
@Schema(description = "模板id")
@TableField("template_id")
private Long templateId;
@Schema(description = "主机id")
@TableField("host_id")
private Long hostId;
}

View File

@@ -9,20 +9,20 @@ import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
/**
* 命令片段分组 实体对象
* 路径标签 实体对象
*
* @author Jiahang Li
* @version 1.0.0
* @since 2024-1-24 12:28
* @version 1.0.6
* @since 2024-4-23 23:15
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
@TableName(value = "command_snippet_group", autoResultMap = true)
@Schema(name = "CommandSnippetGroupDO", description = "命令片段分组 实体对象")
public class CommandSnippetGroupDO extends BaseDO {
@TableName(value = "path_bookmark", autoResultMap = true)
@Schema(name = "PathBookmarkDO", description = "路径标签 实体对象")
public class PathBookmarkDO extends BaseDO {
private static final long serialVersionUID = 1L;
@@ -34,8 +34,20 @@ public class CommandSnippetGroupDO extends BaseDO {
@TableField("user_id")
private Long userId;
@Schema(description = "分组名称")
@Schema(description = "分组id")
@TableField("group_id")
private Long groupId;
@Schema(description = "名称")
@TableField("name")
private String name;
@Schema(description = "类型")
@TableField("type")
private String type;
@Schema(description = "路径")
@TableField("path")
private String path;
}

View File

@@ -0,0 +1,43 @@
package com.orion.ops.module.asset.entity.dto;
import com.orion.lang.define.cache.key.model.LongCacheIdModel;
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.6
* @since 2024-4-23 23:15
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Schema(name = "PathBookmarkCacheDTO", description = "路径标签 缓存对象")
public class PathBookmarkCacheDTO implements LongCacheIdModel, Serializable {
private static final long serialVersionUID = 1L;
@Schema(description = "id")
private Long id;
@Schema(description = "分组id")
private Long groupId;
@Schema(description = "名称")
private String name;
@Schema(description = "类型")
private String type;
@Schema(description = "路径")
private String path;
}

View File

@@ -26,14 +26,14 @@ public class CommandSnippetCreateRequest implements Serializable {
private static final long serialVersionUID = 1L;
@Schema(description = "分组id")
private Long groupId;
@NotBlank
@Size(max = 64)
@Schema(description = "名称")
private String name;
@Schema(description = "分组id")
private Long groupId;
@NotBlank
@Schema(description = "代码片段")
private String command;

View File

@@ -12,6 +12,7 @@ import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.io.Serializable;
import java.util.List;
/**
* 执行模板 创建请求对象
@@ -51,4 +52,7 @@ public class ExecTemplateCreateRequest implements Serializable {
@Schema(description = "参数定义")
private String parameterSchema;
@Schema(description = "模板主机")
private List<Long> hostIdList;
}

View File

@@ -12,6 +12,7 @@ import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.io.Serializable;
import java.util.List;
/**
* 执行模板 更新请求对象
@@ -55,4 +56,7 @@ public class ExecTemplateUpdateRequest implements Serializable {
@Schema(description = "参数定义")
private String parameterSchema;
@Schema(description = "模板主机")
private List<Long> hostIdList;
}

View File

@@ -0,0 +1,47 @@
package com.orion.ops.module.asset.entity.request.path;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
import java.io.Serializable;
/**
* 路径标签 创建请求对象
*
* @author Jiahang Li
* @version 1.0.6
* @since 2024-4-23 23:15
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Schema(name = "PathBookmarkCreateRequest", description = "路径标签 创建请求对象")
public class PathBookmarkCreateRequest implements Serializable {
private static final long serialVersionUID = 1L;
@Schema(description = "分组id")
private Long groupId;
@NotBlank
@Size(max = 64)
@Schema(description = "名称")
private String name;
@NotBlank
@Size(max = 4)
@Schema(description = "类型")
private String type;
@NotBlank
@Size(max = 1024)
@Schema(description = "路径")
private String path;
}

View File

@@ -0,0 +1,34 @@
package com.orion.ops.module.asset.entity.request.path;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
import java.io.Serializable;
/**
* 路径标签分组 创建请求对象
*
* @author Jiahang Li
* @version 1.0.0
* @since 2024-1-24 12:28
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Schema(name = "PathBookmarkGroupCreateRequest", description = "路径标签分组 创建请求对象")
public class PathBookmarkGroupCreateRequest implements Serializable {
private static final long serialVersionUID = 1L;
@NotBlank
@Size(max = 64)
@Schema(description = "分组名称")
private String name;
}

View File

@@ -0,0 +1,37 @@
package com.orion.ops.module.asset.entity.request.path;
import com.orion.ops.framework.common.validator.group.Id;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
/**
* 路径标签分组 删除请求对象
*
* @author Jiahang Li
* @version 1.0.0
* @since 2024-1-24 12:28
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Schema(name = "PathBookmarkGroupDeleteRequest", description = "路径标签分组 删除请求对象")
public class PathBookmarkGroupDeleteRequest implements Serializable {
private static final long serialVersionUID = 1L;
@NotNull(groups = Id.class)
@Schema(description = "id")
private Long id;
@NotNull
@Schema(description = "是否删除组内数据")
private Boolean deleteItem;
}

View File

@@ -0,0 +1,39 @@
package com.orion.ops.module.asset.entity.request.path;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.io.Serializable;
/**
* 路径标签分组 更新请求对象
*
* @author Jiahang Li
* @version 1.0.0
* @since 2024-1-24 12:28
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Schema(name = "PathBookmarkGroupUpdateRequest", description = "路径标签分组 更新请求对象")
public class PathBookmarkGroupUpdateRequest implements Serializable {
private static final long serialVersionUID = 1L;
@NotNull
@Schema(description = "id")
private Long id;
@NotBlank
@Size(max = 64)
@Schema(description = "分组名称")
private String name;
}

View File

@@ -0,0 +1,52 @@
package com.orion.ops.module.asset.entity.request.path;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.io.Serializable;
/**
* 路径标签 更新请求对象
*
* @author Jiahang Li
* @version 1.0.6
* @since 2024-4-23 23:15
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Schema(name = "PathBookmarkUpdateRequest", description = "路径标签 更新请求对象")
public class PathBookmarkUpdateRequest implements Serializable {
private static final long serialVersionUID = 1L;
@NotNull
@Schema(description = "id")
private Long id;
@Schema(description = "分组id")
private Long groupId;
@NotBlank
@Size(max = 64)
@Schema(description = "名称")
private String name;
@NotBlank
@Size(max = 4)
@Schema(description = "类型")
private String type;
@NotBlank
@Size(max = 1024)
@Schema(description = "路径")
private String path;
}

View File

@@ -8,6 +8,7 @@ import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.Date;
import java.util.Set;
/**
* 执行模板 视图响应对象
@@ -55,4 +56,7 @@ public class ExecTemplateVO implements Serializable {
@Schema(description = "修改人")
private String updater;
@Schema(description = "模板主机")
private Set<Long> hostIdList;
}

View File

@@ -1,6 +1,5 @@
package com.orion.ops.module.asset.entity.dto;
package com.orion.ops.module.asset.entity.vo;
import com.orion.lang.define.cache.key.model.LongCacheIdModel;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
@@ -8,9 +7,10 @@ import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.List;
/**
* 命令片段分组 缓存对象
* 路径标签分组 视图响应对象
*
* @author Jiahang Li
* @version 1.0.0
@@ -20,8 +20,8 @@ import java.io.Serializable;
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Schema(name = "CommandSnippetGroupCacheDTO", description = "命令片段分组 缓存对象")
public class CommandSnippetGroupCacheDTO implements LongCacheIdModel, Serializable {
@Schema(name = "PathBookmarkGroupVO", description = "路径标签分组 视图响应对象")
public class PathBookmarkGroupVO implements Serializable {
private static final long serialVersionUID = 1L;
@@ -31,4 +31,7 @@ public class CommandSnippetGroupCacheDTO implements LongCacheIdModel, Serializab
@Schema(description = "分组名称")
private String name;
@Schema(description = "路径标签列表")
private List<PathBookmarkVO> items;
}

View File

@@ -0,0 +1,45 @@
package com.orion.ops.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 java.io.Serializable;
/**
* 路径标签 视图响应对象
*
* @author Jiahang Li
* @version 1.0.6
* @since 2024-4-23 23:15
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Schema(name = "PathBookmarkVO", description = "路径标签 视图响应对象")
public class PathBookmarkVO implements Serializable {
private static final long serialVersionUID = 1L;
@Schema(description = "id")
private Long id;
@Schema(description = "用户id")
private Long userId;
@Schema(description = "分组id")
private Long groupId;
@Schema(description = "名称")
private String name;
@Schema(description = "类型")
private String type;
@Schema(description = "路径")
private String path;
}

View File

@@ -0,0 +1,34 @@
package com.orion.ops.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 java.io.Serializable;
import java.util.List;
/**
* 路径标签组合 视图响应对象
*
* @author Jiahang Li
* @version 1.0.0
* @since 2024/1/24 17:30
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Schema(name = "PathBookmarkWrapperVO", description = "路径标签组合 视图响应对象")
public class PathBookmarkWrapperVO implements Serializable {
private static final long serialVersionUID = 1L;
@Schema(description = "分组")
private List<PathBookmarkGroupVO> groups;
@Schema(description = "未分组的路径标签")
private List<PathBookmarkVO> ungroupedItems;
}

View File

@@ -25,13 +25,25 @@ public class ExecCommandDTO {
@Schema(description = "hostId")
private Long logId;
@Schema(description = "用户id")
private Long userId;
@Schema(description = "用户名")
private String username;
@Schema(description = "执行描述")
private String description;
@Schema(description = "执行序列")
private Integer execSeq;
@Schema(description = "超时时间")
private Integer timeout;
@Schema(description = "是否使用脚本执行")
private Boolean scriptExec;
@Schema(description = "主机")
@Schema(description = "执行主机")
private List<ExecCommandHostDTO> hosts;
}

View File

@@ -26,6 +26,12 @@ public class ExecCommandHostDTO {
@Schema(description = "hostId")
private Long hostId;
@Schema(description = "主机名称")
private String hostName;
@Schema(description = "主机地址")
private String hostAddress;
@Schema(description = "日志文件路径")
private String logPath;
@@ -35,6 +41,9 @@ public class ExecCommandHostDTO {
@Schema(description = "执行命令")
private String command;
@Schema(description = "主机用户")
private String username;
@Schema(description = "命令编码")
private String charset;

View File

@@ -6,9 +6,11 @@ import com.orion.lang.exception.ConnectionRuntimeException;
import com.orion.lang.exception.SftpException;
import com.orion.lang.exception.argument.InvalidArgumentException;
import com.orion.lang.support.timeout.TimeoutChecker;
import com.orion.lang.support.timeout.TimeoutEndpoint;
import com.orion.lang.utils.Booleans;
import com.orion.lang.utils.Exceptions;
import com.orion.lang.utils.Strings;
import com.orion.lang.utils.ansi.AnsiAppender;
import com.orion.lang.utils.io.Streams;
import com.orion.net.host.SessionStore;
import com.orion.net.host.sftp.SftpExecutor;
@@ -31,14 +33,14 @@ import java.util.Date;
import java.util.concurrent.TimeUnit;
/**
* 命令执行器
* 命令执行器 基类
*
* @author Jiahang Li
* @version 1.0.0
* @since 2024/3/12 11:30
* @since 2024/4/25 18:35
*/
@Slf4j
public class ExecCommandHandler implements IExecCommandHandler {
public abstract class BaseExecCommandHandler implements IExecCommandHandler {
private final FileClient fileClient = SpringHolder.getBean("logsFileClient");
@@ -48,32 +50,35 @@ public class ExecCommandHandler implements IExecCommandHandler {
private final ExecHostLogDAO execHostLogDAO = SpringHolder.getBean(ExecHostLogDAO.class);
private final ExecCommandDTO execCommand;
protected final ExecCommandDTO execCommand;
private final ExecCommandHostDTO execHostCommand;
protected final ExecCommandHostDTO execHostCommand;
private final TimeoutChecker timeoutChecker;
private final TimeoutChecker<TimeoutEndpoint> timeoutChecker;
@Getter
private ExecHostStatusEnum status;
protected ExecHostStatusEnum status;
protected ExecHostLogDO updateRecord;
private OutputStream logOutputStream;
private SessionStore sessionStore;
private CommandExecutor executor;
private OutputStream logOutputStream;
private volatile boolean closed;
private volatile boolean interrupted;
public ExecCommandHandler(ExecCommandDTO execCommand,
ExecCommandHostDTO execHostCommand,
TimeoutChecker timeoutChecker) {
public BaseExecCommandHandler(ExecCommandDTO execCommand,
ExecCommandHostDTO execHostCommand,
TimeoutChecker<TimeoutEndpoint> timeoutChecker) {
this.status = ExecHostStatusEnum.WAITING;
this.execCommand = execCommand;
this.execHostCommand = execHostCommand;
this.timeoutChecker = timeoutChecker;
this.updateRecord = new ExecHostLogDO();
}
@Override
@@ -90,23 +95,25 @@ public class ExecCommandHandler implements IExecCommandHandler {
} catch (Exception e) {
log.error("ExecCommandHandler run error id: {}", id, e);
ex = e;
}
// 执行完成回调
try {
// 回调
this.onFinishCallback(ex);
} finally {
// 释放资源
Streams.close(this);
}
// 执行回调
if (this.interrupted) {
// 中断执行
this.updateStatus(ExecHostStatusEnum.INTERRUPTED, null);
} else if (ex != null) {
// 执行失败
this.updateStatus(ExecHostStatusEnum.FAILED, ex);
} else if (executor.isTimeout()) {
// 更新执行超时
this.updateStatus(ExecHostStatusEnum.TIMEOUT, null);
} else {
// 更新执行完成
this.updateStatus(ExecHostStatusEnum.COMPLETED, null);
}
}
/**
* 初始化日志输出流
*
* @throws Exception Exception
*/
protected void initLogOutputStream() throws Exception {
// 打开日志流
this.logOutputStream = fileClient.getContentOutputStream(execHostCommand.getLogPath());
}
/**
@@ -114,9 +121,9 @@ public class ExecCommandHandler implements IExecCommandHandler {
*
* @throws IOException IOException
*/
private void execCommand() throws Exception {
// 打开日志
this.logOutputStream = fileClient.getContentOutputStream(execHostCommand.getLogPath());
protected void execCommand() throws Exception {
// 初始化日志
this.initLogOutputStream();
// 打开会话
this.sessionStore = hostTerminalService.openSessionStore(execHostCommand.getHostId());
if (Booleans.isTrue(execCommand.getScriptExec())) {
@@ -140,7 +147,7 @@ public class ExecCommandHandler implements IExecCommandHandler {
/**
* 上传脚本文件
*/
private void uploadScriptFile() {
protected void uploadScriptFile() {
SftpExecutor sftpExecutor = null;
try {
// 打开 sftp
@@ -165,6 +172,42 @@ public class ExecCommandHandler implements IExecCommandHandler {
}
}
/**
* 执行完成回调
*
* @param e e
*/
protected void onFinishCallback(Exception e) {
// 执行回调
if (this.interrupted) {
// 中断执行
this.updateStatus(ExecHostStatusEnum.INTERRUPTED, null);
} else if (e != null) {
// 执行失败
this.updateStatus(ExecHostStatusEnum.FAILED, e);
} else if (executor.isTimeout()) {
// 更新执行超时
this.updateStatus(ExecHostStatusEnum.TIMEOUT, null);
} else {
// 更新执行完成
this.updateStatus(ExecHostStatusEnum.COMPLETED, null);
}
}
/**
* 拼接日志
*
* @param appender appender
*/
protected void appendLog(AnsiAppender appender) {
try {
logOutputStream.write(Strings.bytes(appender.toString()));
logOutputStream.flush();
} catch (Exception e) {
log.error("BaseExecCommandHandler.appendLog error", e);
}
}
/**
* 更新状态
*
@@ -175,30 +218,29 @@ public class ExecCommandHandler implements IExecCommandHandler {
this.status = status;
Long id = execHostCommand.getHostLogId();
String statusName = status.name();
log.info("ExecCommandHandler.updateStatus start id: {}, status: {}", id, statusName);
ExecHostLogDO update = new ExecHostLogDO();
update.setId(id);
update.setStatus(statusName);
log.info("BaseExecCommandHandler.updateStatus start id: {}, status: {}", id, statusName);
updateRecord.setId(id);
updateRecord.setStatus(statusName);
if (ExecHostStatusEnum.RUNNING.equals(status)) {
// 运行中
update.setStartTime(new Date());
updateRecord.setStartTime(new Date());
} else if (ExecHostStatusEnum.COMPLETED.equals(status)) {
// 完成
update.setFinishTime(new Date());
update.setExitStatus(executor.getExitCode());
updateRecord.setFinishTime(new Date());
updateRecord.setExitStatus(executor.getExitCode());
} else if (ExecHostStatusEnum.FAILED.equals(status)) {
// 失败
update.setFinishTime(new Date());
update.setErrorMessage(this.getErrorMessage(ex));
updateRecord.setFinishTime(new Date());
updateRecord.setErrorMessage(this.getErrorMessage(ex));
} else if (ExecHostStatusEnum.TIMEOUT.equals(status)) {
// 超时
update.setFinishTime(new Date());
updateRecord.setFinishTime(new Date());
} else if (ExecHostStatusEnum.INTERRUPTED.equals(status)) {
// 中断
update.setFinishTime(new Date());
updateRecord.setFinishTime(new Date());
}
int effect = execHostLogDAO.updateById(update);
log.info("ExecCommandHandler.updateStatus finish id: {}, effect: {}", id, effect);
int effect = execHostLogDAO.updateById(updateRecord);
log.info("BaseExecCommandHandler.updateStatus finish id: {}, effect: {}", id, effect);
}
@Override
@@ -207,20 +249,21 @@ public class ExecCommandHandler implements IExecCommandHandler {
}
@Override
public void interrupted() {
log.info("ExecCommandHandler.interrupted id: {}, interrupted: {}, closed: {}",
public void interrupt() {
log.info("BaseExecCommandHandler.interrupt id: {}, interrupted: {}, closed: {}",
execHostCommand.getHostLogId(), interrupted, closed);
if (this.interrupted || this.closed) {
return;
}
// 关闭
this.interrupted = true;
this.close();
Streams.close(executor);
Streams.close(sessionStore);
}
@Override
public void close() {
log.info("ExecCommandHandler.closed id: {}, closed: {}",
log.info("BaseExecCommandHandler.closed id: {}, closed: {}",
execHostCommand.getHostLogId(), closed);
if (this.closed) {
return;
@@ -238,7 +281,7 @@ public class ExecCommandHandler implements IExecCommandHandler {
* @param ex ex
* @return errorMessage
*/
private String getErrorMessage(Exception ex) {
protected String getErrorMessage(Exception ex) {
String message;
if (ex instanceof InvalidArgumentException) {
message = ex.getMessage();

View File

@@ -0,0 +1,177 @@
package com.orion.ops.module.asset.handler.host.exec.command.handler;
import com.orion.lang.support.timeout.TimeoutChecker;
import com.orion.lang.support.timeout.TimeoutEndpoint;
import com.orion.lang.utils.Booleans;
import com.orion.lang.utils.ansi.AnsiAppender;
import com.orion.lang.utils.ansi.style.AnsiFont;
import com.orion.lang.utils.ansi.style.color.AnsiForeground;
import com.orion.lang.utils.time.Dates;
import com.orion.net.host.ssh.ExitCode;
import com.orion.ops.framework.common.constant.Const;
import com.orion.ops.module.asset.enums.ExecHostStatusEnum;
import com.orion.ops.module.asset.handler.host.exec.command.dto.ExecCommandDTO;
import com.orion.ops.module.asset.handler.host.exec.command.dto.ExecCommandHostDTO;
/**
* 命令执行器 ansi 日志输出
*
* @author Jiahang Li
* @version 1.0.0
* @since 2024/4/25 18:19
*/
public class ExecCommandAnsiHandler extends BaseExecCommandHandler {
public ExecCommandAnsiHandler(ExecCommandDTO execCommand, ExecCommandHostDTO execHostCommand, TimeoutChecker<TimeoutEndpoint> timeoutChecker) {
super(execCommand, execHostCommand, timeoutChecker);
}
@Override
protected void initLogOutputStream() throws Exception {
super.initLogOutputStream();
// 拼接启动日志
AnsiAppender appender = AnsiAppender.create()
.append(AnsiForeground.BRIGHT_GREEN.and(AnsiFont.BOLD), "> 准备执行命令 ")
.append(Dates.current())
.newLine()
.append(AnsiForeground.BRIGHT_BLUE.and(AnsiFont.BOLD), "执行记录: ")
.append(execCommand.getLogId())
.newLine()
.append(AnsiForeground.BRIGHT_BLUE.and(AnsiFont.BOLD), "执行描述: ")
.append(execCommand.getDescription())
.newLine()
.append(AnsiForeground.BRIGHT_BLUE.and(AnsiFont.BOLD), "执行用户: ")
.append(execCommand.getUsername());
// 非系统用户执行添加 userId
if (Const.SYSTEM_USER_ID.equals(execCommand.getUserId())) {
appender.newLine();
} else {
appender.append(" (")
.append(execCommand.getUserId())
.append(")")
.newLine();
}
// 执行序列
if (execCommand.getExecSeq() != null) {
appender.append(AnsiForeground.BRIGHT_BLUE.and(AnsiFont.BOLD), "执行序列: ")
.append('#')
.append(execCommand.getExecSeq())
.newLine();
}
appender.append(AnsiForeground.BRIGHT_BLUE.and(AnsiFont.BOLD), "执行主机: ")
.append(execHostCommand.getHostName())
.append(" (")
.append(execHostCommand.getHostId())
.append(")")
.newLine()
.append(AnsiForeground.BRIGHT_BLUE.and(AnsiFont.BOLD), "主机地址: ")
.append(execHostCommand.getHostAddress())
.newLine()
.append(AnsiForeground.BRIGHT_BLUE.and(AnsiFont.BOLD), "超时时间: ")
.append(execCommand.getTimeout())
.newLine()
.append(AnsiForeground.BRIGHT_BLUE.and(AnsiFont.BOLD), "脚本执行: ")
.append(execCommand.getScriptExec())
.newLine()
.newLine()
.append(AnsiForeground.BRIGHT_GREEN.and(AnsiFont.BOLD), "> 执行命令 ")
.newLine()
.append(execHostCommand.getCommand())
.newLine()
.newLine();
// 非脚本执行拼接开始执行日志
if (!Booleans.isTrue(execCommand.getScriptExec())) {
appender.append(AnsiForeground.BRIGHT_GREEN.and(AnsiFont.BOLD), "> 开始执行命令 ")
.append(Dates.current())
.newLine();
}
this.appendLog(appender);
}
@Override
protected void uploadScriptFile() {
try {
// 拼接上传日志
AnsiAppender startAppender = AnsiAppender.create()
.newLine()
.append(AnsiForeground.BRIGHT_GREEN.and(AnsiFont.BOLD), "> 准备上传脚本 ")
.append(Dates.current())
.newLine()
.append(AnsiForeground.BRIGHT_BLUE.and(AnsiFont.BOLD), "文件路径: ")
.append(execHostCommand.getScriptPath())
.newLine();
this.appendLog(startAppender);
// 上传脚本文件
super.uploadScriptFile();
// 拼接完成日志
AnsiAppender finishAppender = AnsiAppender.create()
.append(AnsiForeground.BRIGHT_GREEN.and(AnsiFont.BOLD), "< 脚本上传成功 ")
.append(Dates.current())
.newLine()
.newLine()
.append(AnsiForeground.BRIGHT_GREEN.and(AnsiFont.BOLD), "> 开始执行脚本 ")
.append(Dates.current())
.newLine();
this.appendLog(finishAppender);
} catch (Exception e) {
// 拼接失败日志
AnsiAppender errorAppender = AnsiAppender.create()
.append(AnsiForeground.BRIGHT_RED.and(AnsiFont.BOLD), "< 脚本上传失败 ")
.append(Dates.current())
.newLine();
this.appendLog(errorAppender);
throw e;
}
}
@Override
protected void onFinishCallback(Exception e) {
// 执行完成回调
super.onFinishCallback(e);
// 拼接日志
AnsiAppender appender = AnsiAppender.create()
.newLine();
if (this.status == ExecHostStatusEnum.INTERRUPTED) {
// 中断执行
appender.append(AnsiForeground.BRIGHT_YELLOW.and(AnsiFont.BOLD), "< 命令执行中断 ")
.append(Dates.current())
.newLine();
} else if (this.status == ExecHostStatusEnum.FAILED) {
// 执行失败
appender.append(AnsiForeground.BRIGHT_RED.and(AnsiFont.BOLD), "< 命令执行失败 ")
.append(Dates.current())
.newLine()
.append(AnsiForeground.BRIGHT_RED.and(AnsiFont.BOLD), "错误原因: ")
.append(this.getErrorMessage(e))
.newLine();
} else if (this.status == ExecHostStatusEnum.TIMEOUT) {
// 更新执行超时
appender.append(AnsiForeground.BRIGHT_YELLOW.and(AnsiFont.BOLD), "< 命令执行超时 ")
.append(Dates.current())
.newLine();
} else {
long ms = this.updateRecord.getFinishTime().getTime() - this.updateRecord.getStartTime().getTime();
Integer exitStatus = this.updateRecord.getExitStatus();
// 执行完成
appender.append(AnsiForeground.BRIGHT_GREEN.and(AnsiFont.BOLD), "< 命令执行完成 ")
.append(Dates.current())
.newLine()
.append(AnsiForeground.BRIGHT_BLUE.and(AnsiFont.BOLD), "exit: ");
if (ExitCode.isSuccess(exitStatus)) {
appender.append(AnsiForeground.BRIGHT_GREEN.and(AnsiFont.BOLD), exitStatus);
} else {
appender.append(AnsiForeground.BRIGHT_RED.and(AnsiFont.BOLD), exitStatus);
}
appender.newLine()
.append(AnsiForeground.BRIGHT_BLUE.and(AnsiFont.BOLD), "used: ")
.append(Dates.interval(ms, false, "d ", "h ", "m ", "s"))
.append(" (")
.append(ms)
.append(" ms)")
.newLine();
}
// 拼接日志
this.appendLog(appender);
}
}

View File

@@ -0,0 +1,23 @@
package com.orion.ops.module.asset.handler.host.exec.command.handler;
import com.orion.lang.support.timeout.TimeoutChecker;
import com.orion.lang.support.timeout.TimeoutEndpoint;
import com.orion.ops.module.asset.handler.host.exec.command.dto.ExecCommandDTO;
import com.orion.ops.module.asset.handler.host.exec.command.dto.ExecCommandHostDTO;
import lombok.extern.slf4j.Slf4j;
/**
* 命令执行器 原始日志输出
*
* @author Jiahang Li
* @version 1.0.0
* @since 2024/3/12 11:30
*/
@Slf4j
public class ExecCommandOriginHandler extends BaseExecCommandHandler {
public ExecCommandOriginHandler(ExecCommandDTO execCommand, ExecCommandHostDTO execHostCommand, TimeoutChecker<TimeoutEndpoint> timeoutChecker) {
super(execCommand, execHostCommand, timeoutChecker);
}
}

View File

@@ -3,11 +3,13 @@ package com.orion.ops.module.asset.handler.host.exec.command.handler;
import com.orion.lang.support.timeout.TimeoutChecker;
import com.orion.lang.support.timeout.TimeoutCheckers;
import com.orion.lang.support.timeout.TimeoutEndpoint;
import com.orion.lang.utils.Booleans;
import com.orion.lang.utils.Threads;
import com.orion.lang.utils.collect.Lists;
import com.orion.lang.utils.io.Streams;
import com.orion.ops.module.asset.dao.ExecLogDAO;
import com.orion.ops.module.asset.define.AssetThreadPools;
import com.orion.ops.module.asset.define.config.AppExecLogConfig;
import com.orion.ops.module.asset.entity.domain.ExecLogDO;
import com.orion.ops.module.asset.enums.ExecStatusEnum;
import com.orion.ops.module.asset.handler.host.exec.command.dto.ExecCommandDTO;
@@ -34,6 +36,8 @@ public class ExecTaskHandler implements IExecTaskHandler {
private static final ExecTaskManager execTaskManager = SpringHolder.getBean(ExecTaskManager.class);
private static final AppExecLogConfig appExecLogConfig = SpringHolder.getBean(AppExecLogConfig.class);
private final ExecCommandDTO execCommand;
private TimeoutChecker<TimeoutEndpoint> timeoutChecker;
@@ -73,9 +77,9 @@ public class ExecTaskHandler implements IExecTaskHandler {
}
@Override
public void interrupted() {
log.info("ExecTaskHandler-interrupted id: {}", execCommand.getLogId());
handlers.forEach(IExecCommandHandler::interrupted);
public void interrupt() {
log.info("ExecTaskHandler-interrupt id: {}", execCommand.getLogId());
handlers.forEach(IExecCommandHandler::interrupt);
}
/**
@@ -93,18 +97,34 @@ public class ExecTaskHandler implements IExecTaskHandler {
List<ExecCommandHostDTO> hosts = execCommand.getHosts();
if (hosts.size() == 1) {
// 单个主机直接执行
ExecCommandHandler handler = new ExecCommandHandler(execCommand, hosts.get(0), timeoutChecker);
IExecCommandHandler handler = this.createCommandHandler(hosts.get(0));
handlers.add(handler);
handler.run();
} else {
hosts.stream()
.map(s -> new ExecCommandHandler(execCommand, s, timeoutChecker))
.map(this::createCommandHandler)
.forEach(handlers::add);
// 多个主机异步阻塞执行
Threads.blockRun(handlers, AssetThreadPools.EXEC_HOST);
}
}
/**
* 创建命令执行器
*
* @param host host
* @return handler
*/
private IExecCommandHandler createCommandHandler(ExecCommandHostDTO host) {
if (Booleans.isTrue(appExecLogConfig.getAppendAnsi())) {
// ansi 日志
return new ExecCommandAnsiHandler(execCommand, host, timeoutChecker);
} else {
// 原始日志
return new ExecCommandOriginHandler(execCommand, host, timeoutChecker);
}
}
/**
* 更新状态
*

View File

@@ -22,7 +22,7 @@ public interface IExecCommandHandler extends Runnable, SafeCloseable {
/**
* 中断执行
*/
void interrupted();
void interrupt();
/**
* 获取当前状态

View File

@@ -23,6 +23,6 @@ public interface IExecTaskHandler extends Runnable, SafeCloseable {
/**
* 中断执行
*/
void interrupted();
void interrupt();
}

View File

@@ -129,7 +129,7 @@ public class SftpSession extends TerminalSession implements ISftpSession {
throw Exceptions.ioRuntime(e);
} finally {
// 同关闭 transfer downloader
// 关闭 inputStream 可能会被阻塞 ??..?? 只能关闭 executor
// 关闭 inputStream 可能会被阻塞 ???...??? 只能关闭 executor
Streams.close(this.executor);
this.connect();
}

View File

@@ -98,7 +98,7 @@ public class DownloadSession extends TransferHostSession implements IDownloadSes
@Override
protected void closeStream() {
// 关闭 inputStream 可能会被阻塞 ??..?? 只能关闭 executor
// 关闭 inputStream 可能会被阻塞 ???...??? 只能关闭 executor
Streams.close(this.executor);
this.executor = null;
this.inputStream = null;

View File

@@ -47,4 +47,9 @@ public interface CommandSnippetGroupService {
*/
Integer deleteCommandSnippetGroup(CommandSnippetGroupDeleteRequest request);
/**
* 清理未使用的分组
*/
void clearUnusedGroup();
}

View File

@@ -0,0 +1,56 @@
package com.orion.ops.module.asset.service;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* 执行模板主机 服务类
*
* @author Jiahang Li
* @version 1.0.6
* @since 2024-4-22 12:13
*/
public interface ExecTemplateHostService {
/**
* 查询模板主机
*
* @param templateId templateId
* @return hostId
*/
Set<Long> getHostByTemplateId(Long templateId);
/**
* 查询模板主机
*
* @param templateIdList templateIdList
* @return hostIdMap
*/
Map<Long, Set<Long>> getHostByTemplateIdList(List<Long> templateIdList);
/**
* 设置模板主机
*
* @param templateId templateId
* @param hostList hostList
*/
void setTemplateHost(Long templateId, List<Long> hostList);
/**
* 通过 templateId 删除
*
* @param templateId templateId
* @return effect
*/
Integer deleteByTemplateId(Long templateId);
/**
* 通过 hostId 删除
*
* @param hostId hostId
* @return effect
*/
Integer deleteByHostId(Long hostId);
}

View File

@@ -39,6 +39,14 @@ public interface ExecTemplateService {
*/
ExecTemplateVO getExecTemplateById(Long id);
/**
* 查询执行模板 (查询认证的主机)
*
* @param id id
* @return row
*/
ExecTemplateVO getExecTemplateWithAuthorized(Long id);
/**
* 分页查询执行模板
*

View File

@@ -0,0 +1,55 @@
package com.orion.ops.module.asset.service;
import com.orion.ops.module.asset.entity.request.path.PathBookmarkGroupCreateRequest;
import com.orion.ops.module.asset.entity.request.path.PathBookmarkGroupDeleteRequest;
import com.orion.ops.module.asset.entity.request.path.PathBookmarkGroupUpdateRequest;
import com.orion.ops.module.asset.entity.vo.PathBookmarkGroupVO;
import java.util.List;
/**
* 路径标签分组 服务类
*
* @author Jiahang Li
* @version 1.0.0
* @since 2024-1-24 12:28
*/
public interface PathBookmarkGroupService {
/**
* 创建路径标签分组
*
* @param request request
* @return id
*/
Long createPathBookmarkGroup(PathBookmarkGroupCreateRequest request);
/**
* 更新路径标签分组
*
* @param request request
* @return effect
*/
Integer updatePathBookmarkGroupById(PathBookmarkGroupUpdateRequest request);
/**
* 查询全部路径标签分组
*
* @return rows
*/
List<PathBookmarkGroupVO> getPathBookmarkGroupList();
/**
* 删除路径标签分组
*
* @param request request
* @return effect
*/
Integer deletePathBookmarkGroup(PathBookmarkGroupDeleteRequest request);
/**
* 清理未使用的分组
*/
void clearUnusedGroup();
}

View File

@@ -0,0 +1,75 @@
package com.orion.ops.module.asset.service;
import com.orion.ops.module.asset.entity.request.path.PathBookmarkCreateRequest;
import com.orion.ops.module.asset.entity.request.path.PathBookmarkUpdateRequest;
import com.orion.ops.module.asset.entity.vo.PathBookmarkVO;
import com.orion.ops.module.asset.entity.vo.PathBookmarkWrapperVO;
import java.util.List;
/**
* 路径标签 服务类
*
* @author Jiahang Li
* @version 1.0.6
* @since 2024-4-23 23:15
*/
public interface PathBookmarkService {
/**
* 创建路径标签
*
* @param request request
* @return id
*/
Long createPathBookmark(PathBookmarkCreateRequest request);
/**
* 更新路径标签
*
* @param request request
* @return effect
*/
Integer updatePathBookmarkById(PathBookmarkUpdateRequest request);
/**
* 查询路径标签
*
* @return rows
*/
PathBookmarkWrapperVO getPathBookmark();
/**
* 查询全部路径标签
*
* @return rows
*/
List<PathBookmarkVO> getPathBookmarkList();
/**
* 删除路径标签
*
* @param id id
* @return effect
*/
Integer deletePathBookmarkById(Long id);
/**
* 设置分组为 null
*
* @param userId userId
* @param groupId groupId
* @return effect
*/
Integer setGroupNull(Long userId, Long groupId);
/**
* 通过 groupId 删除
*
* @param userId userId
* @param groupId groupId
* @return effect
*/
Integer deleteByGroupId(Long userId, Long groupId);
}

View File

@@ -1,24 +1,26 @@
package com.orion.ops.module.asset.service.impl;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.orion.lang.utils.Booleans;
import com.orion.ops.framework.common.constant.Const;
import com.orion.ops.framework.common.constant.ErrorMessage;
import com.orion.ops.framework.common.utils.Valid;
import com.orion.ops.framework.redis.core.utils.RedisMaps;
import com.orion.ops.framework.redis.core.utils.barrier.CacheBarriers;
import com.orion.ops.framework.security.core.utils.SecurityUtils;
import com.orion.ops.module.asset.convert.CommandSnippetGroupConvert;
import com.orion.ops.module.asset.dao.CommandSnippetGroupDAO;
import com.orion.ops.module.asset.define.cache.CommandSnippetCacheKeyDefine;
import com.orion.ops.module.asset.entity.domain.CommandSnippetGroupDO;
import com.orion.ops.module.asset.entity.dto.CommandSnippetGroupCacheDTO;
import com.orion.ops.module.asset.dao.CommandSnippetDAO;
import com.orion.ops.module.asset.entity.domain.CommandSnippetDO;
import com.orion.ops.module.asset.entity.request.command.CommandSnippetGroupCreateRequest;
import com.orion.ops.module.asset.entity.request.command.CommandSnippetGroupDeleteRequest;
import com.orion.ops.module.asset.entity.request.command.CommandSnippetGroupUpdateRequest;
import com.orion.ops.module.asset.entity.vo.CommandSnippetGroupVO;
import com.orion.ops.module.asset.service.CommandSnippetGroupService;
import com.orion.ops.module.asset.service.CommandSnippetService;
import com.orion.ops.module.infra.api.DataGroupApi;
import com.orion.ops.module.infra.api.DataGroupUserApi;
import com.orion.ops.module.infra.entity.dto.data.DataGroupCreateDTO;
import com.orion.ops.module.infra.entity.dto.data.DataGroupDTO;
import com.orion.ops.module.infra.entity.dto.data.DataGroupRenameDTO;
import com.orion.ops.module.infra.enums.DataGroupTypeEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -26,6 +28,7 @@ import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
@@ -40,7 +43,13 @@ import java.util.stream.Collectors;
public class CommandSnippetGroupServiceImpl implements CommandSnippetGroupService {
@Resource
private CommandSnippetGroupDAO commandSnippetGroupDAO;
private DataGroupApi dataGroupApi;
@Resource
private DataGroupUserApi dataGroupUserApi;
@Resource
private CommandSnippetDAO commandSnippetDAO;
@Resource
private CommandSnippetService commandSnippetService;
@@ -49,110 +58,81 @@ public class CommandSnippetGroupServiceImpl implements CommandSnippetGroupServic
public Long createCommandSnippetGroup(CommandSnippetGroupCreateRequest request) {
Long userId = SecurityUtils.getLoginUserId();
log.info("CommandSnippetGroupService-createCommandSnippetGroup request: {}", JSON.toJSONString(request));
// 转换
CommandSnippetGroupDO record = CommandSnippetGroupConvert.MAPPER.to(request);
record.setUserId(userId);
// 查询数据是否冲突
this.checkCommandSnippetGroupPresent(record);
// 插入
int effect = commandSnippetGroupDAO.insert(record);
Long id = record.getId();
log.info("CommandSnippetGroupService-createCommandSnippetGroup id: {}, effect: {}", id, effect);
// 删除缓存
String cacheKey = CommandSnippetCacheKeyDefine.SNIPPET_GROUP.format(userId);
RedisMaps.delete(cacheKey);
return id;
// 创建
DataGroupCreateDTO create = CommandSnippetGroupConvert.MAPPER.to(request);
create.setParentId(Const.ROOT_PARENT_ID);
return dataGroupUserApi.createDataGroup(DataGroupTypeEnum.COMMAND_SNIPPET, userId, create);
}
@Override
public Integer updateCommandSnippetGroupById(CommandSnippetGroupUpdateRequest request) {
Long id = Valid.notNull(request.getId(), ErrorMessage.ID_MISSING);
log.info("CommandSnippetGroupService-updateCommandSnippetGroupById id: {}, request: {}", id, JSON.toJSONString(request));
// 查询
CommandSnippetGroupDO record = commandSnippetGroupDAO.selectById(id);
Valid.notNull(record, ErrorMessage.DATA_ABSENT);
// 转换
CommandSnippetGroupDO updateRecord = CommandSnippetGroupConvert.MAPPER.to(request);
updateRecord.setUserId(record.getUserId());
// 查询数据是否冲突
this.checkCommandSnippetGroupPresent(updateRecord);
// 更新
int effect = commandSnippetGroupDAO.updateById(updateRecord);
log.info("CommandSnippetGroupService-updateCommandSnippetGroupById effect: {}", effect);
// 删除缓存
String cacheKey = CommandSnippetCacheKeyDefine.SNIPPET_GROUP.format(record.getUserId());
RedisMaps.delete(cacheKey);
return effect;
// 重命名
DataGroupRenameDTO rename = CommandSnippetGroupConvert.MAPPER.to(request);
return dataGroupApi.renameDataGroup(rename);
}
@Override
public List<CommandSnippetGroupVO> getCommandSnippetGroupList() {
Long userId = SecurityUtils.getLoginUserId();
// 查询缓存
String cacheKey = CommandSnippetCacheKeyDefine.SNIPPET_GROUP.format(userId);
List<CommandSnippetGroupCacheDTO> list = RedisMaps.valuesJson(cacheKey, CommandSnippetCacheKeyDefine.SNIPPET_GROUP);
if (list.isEmpty()) {
// 查询数据库
list = commandSnippetGroupDAO.of()
.createWrapper()
.eq(CommandSnippetGroupDO::getUserId, userId)
.then()
.list(CommandSnippetGroupConvert.MAPPER::toCache);
// 设置屏障 防止穿透
CacheBarriers.checkBarrier(list, CommandSnippetGroupCacheDTO::new);
// 设置缓存
RedisMaps.putAllJson(cacheKey, s -> s.getId().toString(), list);
}
// 删除屏障
CacheBarriers.removeBarrier(list);
// 转换
return list.stream()
// 查询分组
return dataGroupUserApi.getDataGroupList(DataGroupTypeEnum.COMMAND_SNIPPET, userId)
.stream()
.sorted(Comparator.comparing(DataGroupDTO::getSort))
.map(CommandSnippetGroupConvert.MAPPER::to)
.sorted(Comparator.comparing(CommandSnippetGroupVO::getId))
.collect(Collectors.toList());
}
@Override
@Transactional(rollbackFor = Exception.class)
public Integer deleteCommandSnippetGroup(CommandSnippetGroupDeleteRequest request) {
Long userId = SecurityUtils.getLoginUserId();
Long id = request.getId();
log.info("CommandSnippetGroupService-deleteCommandSnippetGroupById id: {}", id);
// 检查数据是否存在
CommandSnippetGroupDO record = commandSnippetGroupDAO.selectById(id);
Valid.notNull(record, ErrorMessage.DATA_ABSENT);
Long userId = record.getUserId();
// 删除
int effect = commandSnippetGroupDAO.deleteById(id);
log.info("CommandSnippetGroupService-deleteCommandSnippetGroupById id: {}, effect: {}", id, effect);
// 删除分组
Integer effect = dataGroupApi.deleteDataGroupById(id);
if (Booleans.isTrue(request.getDeleteItem())) {
// 删除组内数据
commandSnippetService.deleteByGroupId(userId, id);
effect += commandSnippetService.deleteByGroupId(userId, id);
} else {
// 移动到根节点
commandSnippetService.setGroupNull(userId, id);
effect += commandSnippetService.setGroupNull(userId, id);
}
// 删除缓存
String cacheKey = CommandSnippetCacheKeyDefine.SNIPPET_GROUP.format(userId);
RedisMaps.delete(cacheKey, id);
return effect;
}
/**
* 检查对象是否存在
*
* @param domain domain
*/
private void checkCommandSnippetGroupPresent(CommandSnippetGroupDO domain) {
// 构造条件
LambdaQueryWrapper<CommandSnippetGroupDO> wrapper = commandSnippetGroupDAO.wrapper()
// 更新时忽略当前记录
.ne(CommandSnippetGroupDO::getId, domain.getId())
// 用其他字段做重复校验
.eq(CommandSnippetGroupDO::getUserId, domain.getUserId())
.eq(CommandSnippetGroupDO::getName, domain.getName());
// 检查是否存在
boolean present = commandSnippetGroupDAO.of(wrapper).present();
Valid.isFalse(present, ErrorMessage.DATA_PRESENT);
@Override
public void clearUnusedGroup() {
// 查询全部 groupId
Map<Long, List<Long>> userGroupMap = commandSnippetDAO.of()
.createWrapper()
.select(CommandSnippetDO::getUserId, CommandSnippetDO::getGroupId)
.isNotNull(CommandSnippetDO::getGroupId)
.groupBy(CommandSnippetDO::getGroupId)
.then()
.stream()
.collect(Collectors.groupingBy(CommandSnippetDO::getUserId,
Collectors.mapping(CommandSnippetDO::getGroupId, Collectors.toList())));
userGroupMap.forEach((k, v) -> {
// 查询用户分组
List<DataGroupDTO> groups = dataGroupUserApi.getDataGroupList(DataGroupTypeEnum.COMMAND_SNIPPET, k);
if (groups.isEmpty()) {
return;
}
// 不存在的则移除
List<Long> deleteGroupList = groups.stream()
.map(DataGroupDTO::getId)
.filter(s -> !v.contains(s))
.collect(Collectors.toList());
if (deleteGroupList.isEmpty()) {
return;
}
log.info("CommandSnippetGroupService.clearUnusedGroup userId: {}, deleteGroupList: {}", k, deleteGroupList);
// 删除分组
Integer effect = dataGroupUserApi.deleteDataGroupByIdList(DataGroupTypeEnum.COMMAND_SNIPPET, k, deleteGroupList);
log.info("CommandSnippetGroupService.clearUnusedGroup userId: {}, effect: {}", k, effect);
});
}
}

View File

@@ -1,6 +1,7 @@
package com.orion.ops.module.asset.service.impl;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.orion.ops.framework.common.constant.ErrorMessage;
@@ -55,18 +56,18 @@ public class CommandSnippetServiceImpl implements CommandSnippetService {
// 转换
CommandSnippetDO record = CommandSnippetConvert.MAPPER.to(request);
record.setUserId(userId);
// 查询数据是否冲突
this.checkCommandSnippetPresent(record);
// 插入
int effect = commandSnippetDAO.insert(record);
Long id = record.getId();
log.info("CommandSnippetService-createCommandSnippet id: {}, effect: {}", id, effect);
// 删除缓存
String cacheKey = CommandSnippetCacheKeyDefine.SNIPPET.format(userId);
RedisMaps.delete(cacheKey);
RedisMaps.delete(CommandSnippetCacheKeyDefine.SNIPPET.format(userId));
return id;
}
@Override
@Transactional(rollbackFor = Exception.class)
public Integer updateCommandSnippetById(CommandSnippetUpdateRequest request) {
Long id = Valid.notNull(request.getId(), ErrorMessage.ID_MISSING);
Long userId = SecurityUtils.getLoginUserId();
@@ -74,6 +75,9 @@ public class CommandSnippetServiceImpl implements CommandSnippetService {
// 查询
CommandSnippetDO record = commandSnippetDAO.selectById(id);
Valid.notNull(record, ErrorMessage.DATA_ABSENT);
// 查询数据是否冲突
CommandSnippetDO updateRecord = CommandSnippetConvert.MAPPER.to(request);
this.checkCommandSnippetPresent(updateRecord);
// 更新
LambdaUpdateWrapper<CommandSnippetDO> update = Wrappers.<CommandSnippetDO>lambdaUpdate()
.set(CommandSnippetDO::getGroupId, request.getGroupId())
@@ -84,8 +88,7 @@ public class CommandSnippetServiceImpl implements CommandSnippetService {
int effect = commandSnippetDAO.update(null, update);
log.info("CommandSnippetService-updateCommandSnippetById effect: {}", effect);
// 删除缓存
String cacheKey = CommandSnippetCacheKeyDefine.SNIPPET.format(userId);
RedisMaps.delete(cacheKey);
RedisMaps.delete(CommandSnippetCacheKeyDefine.SNIPPET.format(userId));
return effect;
}
@@ -130,7 +133,7 @@ public class CommandSnippetServiceImpl implements CommandSnippetService {
// 设置屏障 防止穿透
CacheBarriers.checkBarrier(list, CommandSnippetCacheDTO::new);
// 设置缓存
RedisMaps.putAllJson(CommandSnippetCacheKeyDefine.SNIPPET, s -> s.getId().toString(), list);
RedisMaps.putAllJson(cacheKey, CommandSnippetCacheKeyDefine.SNIPPET, s -> s.getId().toString(), list);
}
// 删除屏障
CacheBarriers.removeBarrier(list);
@@ -153,8 +156,7 @@ public class CommandSnippetServiceImpl implements CommandSnippetService {
int effect = commandSnippetDAO.deleteById(id);
log.info("CommandSnippetService-deleteCommandSnippetById id: {}, effect: {}", id, effect);
// 删除缓存
String cacheKey = CommandSnippetCacheKeyDefine.SNIPPET.format(userId);
RedisMaps.delete(cacheKey, id);
RedisMaps.delete(CommandSnippetCacheKeyDefine.SNIPPET.format(userId), id);
return effect;
}
@@ -162,8 +164,7 @@ public class CommandSnippetServiceImpl implements CommandSnippetService {
public Integer setGroupNull(Long userId, Long groupId) {
int effect = commandSnippetDAO.setGroupIdWithNull(groupId);
// 删除缓存
String cacheKey = CommandSnippetCacheKeyDefine.SNIPPET.format(userId);
RedisMaps.delete(cacheKey);
RedisMaps.delete(CommandSnippetCacheKeyDefine.SNIPPET.format(userId));
return effect;
}
@@ -171,9 +172,27 @@ public class CommandSnippetServiceImpl implements CommandSnippetService {
public Integer deleteByGroupId(Long userId, Long groupId) {
int effect = commandSnippetDAO.deleteByGroupId(groupId);
// 删除缓存
String cacheKey = CommandSnippetCacheKeyDefine.SNIPPET.format(userId);
RedisMaps.delete(cacheKey);
RedisMaps.delete(CommandSnippetCacheKeyDefine.SNIPPET.format(userId));
return effect;
}
/**
* 检查对象是否存在
*
* @param domain domain
*/
private void checkCommandSnippetPresent(CommandSnippetDO domain) {
// 构造条件
LambdaQueryWrapper<CommandSnippetDO> wrapper = commandSnippetDAO.wrapper()
// 更新时忽略当前记录
.ne(CommandSnippetDO::getId, domain.getId())
// 用其他字段做重复校验
.eq(CommandSnippetDO::getUserId, domain.getUserId())
.eq(CommandSnippetDO::getGroupId, domain.getGroupId())
.eq(CommandSnippetDO::getName, domain.getName());
// 检查是否存在
boolean present = commandSnippetDAO.of(wrapper).present();
Valid.isFalse(present, ErrorMessage.DATA_PRESENT);
}
}

View File

@@ -197,9 +197,12 @@ public class ExecCommandServiceImpl implements ExecCommandService {
return ExecCommandHostDTO.builder()
.hostId(s.getHostId())
.hostLogId(s.getId())
.hostName(s.getHostName())
.hostAddress(s.getHostAddress())
.command(s.getCommand())
.logPath(s.getLogPath())
.scriptPath(s.getScriptPath())
.username(config.getUsername())
.charset(config.getCharset())
.fileNameCharset(config.getFileNameCharset())
.fileContentCharset(config.getFileContentCharset())
@@ -208,6 +211,10 @@ public class ExecCommandServiceImpl implements ExecCommandService {
// 执行信息
ExecCommandDTO exec = ExecCommandDTO.builder()
.logId(execLog.getId())
.userId(execLog.getUserId())
.username(execLog.getUsername())
.description(execLog.getDescription())
.execSeq(execLog.getExecSeq())
.timeout(execLog.getTimeout())
.scriptExec(ScriptExecEnum.isEnabled(execLog.getScriptExec()))
.hosts(hosts)

View File

@@ -79,7 +79,7 @@ public class ExecHostLogServiceImpl implements ExecHostLogService {
.flatMap(s -> s.stream()
.filter(h -> h.getHostId().equals(record.getHostId()))
.findFirst())
.ifPresent(IExecCommandHandler::interrupted);
.ifPresent(IExecCommandHandler::interrupt);
// 删除
int effect = execHostLogDAO.deleteById(id);
log.info("ExecHostLogService-deleteExecHostLogById id: {}, effect: {}", id, effect);

View File

@@ -10,6 +10,7 @@ import com.orion.lang.utils.Objects1;
import com.orion.lang.utils.Strings;
import com.orion.lang.utils.collect.Lists;
import com.orion.lang.utils.io.Files1;
import com.orion.lang.utils.io.Streams;
import com.orion.ops.framework.biz.operator.log.core.utils.OperatorLogs;
import com.orion.ops.framework.common.constant.Const;
import com.orion.ops.framework.common.constant.ErrorMessage;
@@ -175,7 +176,7 @@ public class ExecLogServiceImpl implements ExecLogService {
ExecLogDO record = execLogDAO.selectByIdSource(id, source);
Valid.notNull(record, ErrorMessage.DATA_ABSENT);
// 中断命令执行
this.interruptedTask(Lists.singleton(id));
this.interruptTask(Lists.singleton(id));
// 删除执行日志
int effect = execLogDAO.deleteById(id);
// 删除主机日志
@@ -199,7 +200,7 @@ public class ExecLogServiceImpl implements ExecLogService {
.intValue();
Valid.isTrue(idList.size() == count, ErrorMessage.DATA_MODIFIED);
// 中断命令执行
this.interruptedTask(idList);
this.interruptTask(idList);
// 删除执行日志
int effect = execLogDAO.deleteBatchIds(idList);
// 删除主机日志
@@ -229,7 +230,7 @@ public class ExecLogServiceImpl implements ExecLogService {
int effect = 0;
if (!idList.isEmpty()) {
// 中断命令执行
this.interruptedTask(idList);
this.interruptTask(idList);
// 删除执行日志
effect = execLogDAO.delete(wrapper);
// 删除主机日志
@@ -255,9 +256,9 @@ public class ExecLogServiceImpl implements ExecLogService {
// 中断执行
IExecTaskHandler task = execTaskManager.getTask(logId);
if (task != null) {
log.info("ExecLogService.interruptExec interrupted logId: {}", logId);
log.info("ExecLogService.interruptExec interrupt logId: {}", logId);
// 中断
task.interrupted();
task.interrupt();
} else {
log.info("ExecLogService.interruptExec updateStatus start logId: {}", logId);
// 不存在则直接修改状态
@@ -298,7 +299,7 @@ public class ExecLogServiceImpl implements ExecLogService {
// 中断执行
IExecTaskHandler task = execTaskManager.getTask(logId);
if (task != null) {
log.info("ExecLogService.interruptHostExec interrupted logId: {}, hostLogId: {}", logId, hostLogId);
log.info("ExecLogService.interruptHostExec interrupt logId: {}, hostLogId: {}", logId, hostLogId);
IExecCommandHandler handler = task.getHandlers()
.stream()
.filter(s -> s.getHostId().equals(hostLog.getHostId()))
@@ -306,7 +307,7 @@ public class ExecLogServiceImpl implements ExecLogService {
.orElse(null);
// 中断
if (handler != null) {
handler.interrupted();
handler.interrupt();
}
} else {
log.info("ExecLogService.interruptHostExec updateStatus start logId: {}, hostLogId: {}", logId, hostLogId);
@@ -405,6 +406,7 @@ public class ExecLogServiceImpl implements ExecLogService {
@Override
public void downloadLogFile(Long id, String source, HttpServletResponse response) {
log.info("ExecLogService.downloadLogFile id: {}, source: {}", id, source);
InputStream in = null;
try {
// 获取主机执行日志
ExecHostLogDO hostLog = execHostLogDAO.selectById(id);
@@ -418,11 +420,12 @@ public class ExecLogServiceImpl implements ExecLogService {
OperatorLogs.add(OperatorLogs.HOST_ID, hostLog.getHostId());
OperatorLogs.add(OperatorLogs.HOST_NAME, hostLog.getHostName());
// 获取日志
InputStream in = logsFileClient.getContentInputStream(logPath);
in = logsFileClient.getContentInputStream(logPath);
// 返回
Servlets.transfer(response, in, Files1.getFileName(logPath));
} catch (Exception e) {
log.error("ExecLogService.downloadLogFile error id: {}", id, e);
Streams.close(in);
String errorMessage = ErrorMessage.FILE_READ_ERROR;
if (e instanceof InvalidArgumentException) {
errorMessage = e.getMessage();
@@ -462,11 +465,11 @@ public class ExecLogServiceImpl implements ExecLogService {
*
* @param idList idList
*/
private void interruptedTask(List<Long> idList) {
private void interruptTask(List<Long> idList) {
idList.stream()
.map(execTaskManager::getTask)
.filter(Objects::nonNull)
.forEach(IExecTaskHandler::interrupted);
.forEach(IExecTaskHandler::interrupt);
}
}

View File

@@ -0,0 +1,116 @@
package com.orion.ops.module.asset.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.orion.lang.function.Functions;
import com.orion.lang.utils.collect.Lists;
import com.orion.ops.module.asset.dao.ExecTemplateHostDAO;
import com.orion.ops.module.asset.entity.domain.ExecTemplateHostDO;
import com.orion.ops.module.asset.service.ExecTemplateHostService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
/**
* 执行模板主机 服务实现类
*
* @author Jiahang Li
* @version 1.0.6
* @since 2024-4-22 12:13
*/
@Slf4j
@Service
public class ExecTemplateHostServiceImpl implements ExecTemplateHostService {
@Resource
private ExecTemplateHostDAO execTemplateHostDAO;
@Override
public Set<Long> getHostByTemplateId(Long templateId) {
return execTemplateHostDAO.of()
.createWrapper()
.select(ExecTemplateHostDO::getHostId)
.eq(ExecTemplateHostDO::getTemplateId, templateId)
.then()
.stream()
.map(ExecTemplateHostDO::getHostId)
.collect(Collectors.toSet());
}
@Override
public Map<Long, Set<Long>> getHostByTemplateIdList(List<Long> templateIdList) {
return execTemplateHostDAO.of()
.createWrapper()
.select(ExecTemplateHostDO::getTemplateId, ExecTemplateHostDO::getHostId)
.in(ExecTemplateHostDO::getTemplateId, templateIdList)
.then()
.stream()
.collect(Collectors.groupingBy(
ExecTemplateHostDO::getTemplateId,
Collectors.mapping(ExecTemplateHostDO::getHostId, Collectors.toSet())
));
}
@Override
public void setTemplateHost(Long templateId, List<Long> hostList) {
LambdaQueryWrapper<ExecTemplateHostDO> wrapper = execTemplateHostDAO.wrapper()
.eq(ExecTemplateHostDO::getTemplateId, templateId);
if (Lists.isEmpty(hostList)) {
// 为空移除
execTemplateHostDAO.delete(wrapper);
} else {
// 查询主机
wrapper.select(ExecTemplateHostDO::getId, ExecTemplateHostDO::getHostId);
Map<Long, Long> hostMap = execTemplateHostDAO.of(wrapper)
.stream()
.collect(Collectors.toMap(ExecTemplateHostDO::getHostId,
ExecTemplateHostDO::getId,
Functions.right()));
// 插入主机
List<ExecTemplateHostDO> insertRecords = hostList.stream()
.filter(s -> !hostMap.containsKey(s))
.map(s -> ExecTemplateHostDO.builder()
.templateId(templateId)
.hostId(s)
.build())
.collect(Collectors.toList());
if (!insertRecords.isEmpty()) {
execTemplateHostDAO.insertBatch(insertRecords);
}
// 删除主机
List<Long> deleteIdList = hostMap.keySet()
.stream()
.filter(s -> !hostList.contains(s))
.map(hostMap::get)
.collect(Collectors.toList());
if (!deleteIdList.isEmpty()) {
execTemplateHostDAO.deleteBatchIds(deleteIdList);
}
}
}
@Override
public Integer deleteByTemplateId(Long templateId) {
LambdaQueryWrapper<ExecTemplateHostDO> wrapper = execTemplateHostDAO.lambda()
.eq(ExecTemplateHostDO::getTemplateId, templateId);
log.info("ExecTemplateHostService-deleteByTemplateId idList: {}", templateId);
int effect = execTemplateHostDAO.delete(wrapper);
log.info("ExecTemplateHostService-deleteByTemplateId effect: {}", effect);
return effect;
}
@Override
public Integer deleteByHostId(Long hostId) {
LambdaQueryWrapper<ExecTemplateHostDO> wrapper = execTemplateHostDAO.lambda()
.eq(ExecTemplateHostDO::getHostId, hostId);
log.info("ExecTemplateHostService-deleteByHostId idList: {}", hostId);
int effect = execTemplateHostDAO.delete(wrapper);
log.info("ExecTemplateHostService-deleteByHostId effect: {}", effect);
return effect;
}
}

View File

@@ -3,8 +3,10 @@ package com.orion.ops.module.asset.service.impl;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.orion.lang.define.wrapper.DataGrid;
import com.orion.lang.utils.collect.Lists;
import com.orion.ops.framework.common.constant.ErrorMessage;
import com.orion.ops.framework.common.utils.Valid;
import com.orion.ops.framework.security.core.utils.SecurityUtils;
import com.orion.ops.module.asset.convert.ExecTemplateConvert;
import com.orion.ops.module.asset.dao.ExecTemplateDAO;
import com.orion.ops.module.asset.entity.domain.ExecTemplateDO;
@@ -12,12 +14,17 @@ import com.orion.ops.module.asset.entity.request.exec.ExecTemplateCreateRequest;
import com.orion.ops.module.asset.entity.request.exec.ExecTemplateQueryRequest;
import com.orion.ops.module.asset.entity.request.exec.ExecTemplateUpdateRequest;
import com.orion.ops.module.asset.entity.vo.ExecTemplateVO;
import com.orion.ops.module.asset.enums.HostConfigTypeEnum;
import com.orion.ops.module.asset.enums.ScriptExecEnum;
import com.orion.ops.module.asset.service.AssetAuthorizedDataService;
import com.orion.ops.module.asset.service.ExecTemplateHostService;
import com.orion.ops.module.asset.service.ExecTemplateService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
import java.util.Set;
/**
* 执行模板 服务实现类
@@ -33,6 +40,12 @@ public class ExecTemplateServiceImpl implements ExecTemplateService {
@Resource
private ExecTemplateDAO execTemplateDAO;
@Resource
private ExecTemplateHostService execTemplateHostService;
@Resource
private AssetAuthorizedDataService assetAuthorizedDataService;
@Override
public Long createExecTemplate(ExecTemplateCreateRequest request) {
log.info("ExecTemplateService-createExecTemplate request: {}", JSON.toJSONString(request));
@@ -41,9 +54,11 @@ public class ExecTemplateServiceImpl implements ExecTemplateService {
ExecTemplateDO record = ExecTemplateConvert.MAPPER.to(request);
// 查询数据是否冲突
this.checkExecTemplatePresent(record);
// 插入
// 插入模板
int effect = execTemplateDAO.insert(record);
Long id = record.getId();
// 修改模板主机
execTemplateHostService.setTemplateHost(id, request.getHostIdList());
log.info("ExecTemplateService-createExecTemplate id: {}, effect: {}", id, effect);
return id;
}
@@ -60,26 +75,51 @@ public class ExecTemplateServiceImpl implements ExecTemplateService {
ExecTemplateDO updateRecord = ExecTemplateConvert.MAPPER.to(request);
// 查询数据是否冲突
this.checkExecTemplatePresent(updateRecord);
// 更新
// 更新模板
int effect = execTemplateDAO.updateById(updateRecord);
// 修改模板主机
execTemplateHostService.setTemplateHost(id, request.getHostIdList());
log.info("ExecTemplateService-updateExecTemplateById effect: {}", effect);
return effect;
}
@Override
public ExecTemplateVO getExecTemplateById(Long id) {
// 查询
// 查询模板
ExecTemplateDO record = execTemplateDAO.selectById(id);
Valid.notNull(record, ErrorMessage.DATA_ABSENT);
// 转换
return ExecTemplateConvert.MAPPER.to(record);
ExecTemplateVO template = ExecTemplateConvert.MAPPER.to(record);
// 查询模板主机
Set<Long> hosts = execTemplateHostService.getHostByTemplateId(id);
template.setHostIdList(hosts);
return template;
}
@Override
public ExecTemplateVO getExecTemplateWithAuthorized(Long id) {
// 查询模板
ExecTemplateDO record = execTemplateDAO.selectById(id);
Valid.notNull(record, ErrorMessage.DATA_ABSENT);
// 转换
ExecTemplateVO template = ExecTemplateConvert.MAPPER.to(record);
// 查询模板主机
Set<Long> hostIdList = execTemplateHostService.getHostByTemplateId(id);
if (Lists.isEmpty(hostIdList)) {
return template;
}
// 过滤认证的主机
List<Long> authorizedHostIdList = assetAuthorizedDataService.getUserAuthorizedHostIdWithEnabledConfig(SecurityUtils.getLoginUserId(), HostConfigTypeEnum.SSH);
hostIdList.removeIf(s -> !authorizedHostIdList.contains(s));
template.setHostIdList(hostIdList);
return template;
}
@Override
public DataGrid<ExecTemplateVO> getExecTemplatePage(ExecTemplateQueryRequest request) {
// 条件
LambdaQueryWrapper<ExecTemplateDO> wrapper = this.buildQueryWrapper(request);
// 查询
// 查询模板
return execTemplateDAO.of(wrapper)
.page(request)
.dataGrid(ExecTemplateConvert.MAPPER::to);
@@ -91,9 +131,11 @@ public class ExecTemplateServiceImpl implements ExecTemplateService {
// 检查数据是否存在
ExecTemplateDO record = execTemplateDAO.selectById(id);
Valid.notNull(record, ErrorMessage.DATA_ABSENT);
// 删除
// 删除模板
int effect = execTemplateDAO.deleteById(id);
log.info("ExecTemplateService-deleteExecTemplateById id: {}, effect: {}", id, effect);
// 删除模板主机
effect += execTemplateHostService.deleteByTemplateId(id);
return effect;
}

View File

@@ -22,13 +22,13 @@ import com.orion.ops.module.asset.entity.request.host.HostQueryRequest;
import com.orion.ops.module.asset.entity.request.host.HostUpdateRequest;
import com.orion.ops.module.asset.entity.vo.HostVO;
import com.orion.ops.module.asset.service.ExecJobHostService;
import com.orion.ops.module.asset.service.ExecTemplateHostService;
import com.orion.ops.module.asset.service.HostConfigService;
import com.orion.ops.module.asset.service.HostService;
import com.orion.ops.module.infra.api.DataExtraApi;
import com.orion.ops.module.infra.api.DataGroupRelApi;
import com.orion.ops.module.infra.api.FavoriteApi;
import com.orion.ops.module.infra.api.TagRelApi;
import com.orion.ops.module.infra.entity.dto.data.DataGroupRelCreateDTO;
import com.orion.ops.module.infra.entity.dto.tag.TagDTO;
import com.orion.ops.module.infra.enums.DataExtraTypeEnum;
import com.orion.ops.module.infra.enums.DataGroupTypeEnum;
@@ -70,6 +70,9 @@ public class HostServiceImpl implements HostService {
@Resource
private ExecJobHostService execJobHostService;
@Resource
private ExecTemplateHostService execTemplateHostService;
@Resource
private TagRelApi tagRelApi;
@@ -99,13 +102,7 @@ public class HostServiceImpl implements HostService {
// 引用分组
List<Long> groupIdList = request.getGroupIdList();
if (!Lists.isEmpty(groupIdList)) {
List<DataGroupRelCreateDTO> groupRelList = groupIdList.stream()
.map(s -> DataGroupRelCreateDTO.builder()
.groupId(s)
.relId(id)
.build())
.collect(Collectors.toList());
dataGroupRelApi.addGroupRel(groupRelList);
dataGroupRelApi.updateRelGroup(DataGroupTypeEnum.HOST, request.getGroupIdList(), id);
}
// 创建配置
hostConfigService.initHostConfig(id);
@@ -130,7 +127,7 @@ public class HostServiceImpl implements HostService {
int effect = hostDAO.updateById(updateRecord);
log.info("HostService-updateHostById effect: {}", effect);
// 引用分组
dataGroupRelApi.updateGroupRel(DataGroupTypeEnum.HOST, request.getGroupIdList(), id);
dataGroupRelApi.updateRelGroup(DataGroupTypeEnum.HOST, request.getGroupIdList(), id);
// 更新 tag
tagRelApi.setTagRel(TagTypeEnum.HOST, id, request.getTags());
// 删除缓存
@@ -219,6 +216,8 @@ public class HostServiceImpl implements HostService {
hostConfigDAO.deleteByHostId(id);
// 删除计划任务主机
execJobHostService.deleteByHostId(id);
// 删除执行模板主机
execTemplateHostService.deleteByHostId(id);
// 删除分组
dataGroupRelApi.deleteByRelId(DataGroupTypeEnum.HOST, id);
// 删除 tag 引用

View File

@@ -0,0 +1,138 @@
package com.orion.ops.module.asset.service.impl;
import com.alibaba.fastjson.JSON;
import com.orion.lang.utils.Booleans;
import com.orion.ops.framework.common.constant.Const;
import com.orion.ops.framework.common.constant.ErrorMessage;
import com.orion.ops.framework.common.utils.Valid;
import com.orion.ops.framework.security.core.utils.SecurityUtils;
import com.orion.ops.module.asset.convert.PathBookmarkGroupConvert;
import com.orion.ops.module.asset.dao.PathBookmarkDAO;
import com.orion.ops.module.asset.entity.domain.PathBookmarkDO;
import com.orion.ops.module.asset.entity.request.path.PathBookmarkGroupCreateRequest;
import com.orion.ops.module.asset.entity.request.path.PathBookmarkGroupDeleteRequest;
import com.orion.ops.module.asset.entity.request.path.PathBookmarkGroupUpdateRequest;
import com.orion.ops.module.asset.entity.vo.PathBookmarkGroupVO;
import com.orion.ops.module.asset.service.PathBookmarkGroupService;
import com.orion.ops.module.asset.service.PathBookmarkService;
import com.orion.ops.module.infra.api.DataGroupApi;
import com.orion.ops.module.infra.api.DataGroupUserApi;
import com.orion.ops.module.infra.entity.dto.data.DataGroupCreateDTO;
import com.orion.ops.module.infra.entity.dto.data.DataGroupDTO;
import com.orion.ops.module.infra.entity.dto.data.DataGroupRenameDTO;
import com.orion.ops.module.infra.enums.DataGroupTypeEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* 路径标签分组 服务实现类
*
* @author Jiahang Li
* @version 1.0.0
* @since 2024-1-24 12:28
*/
@Slf4j
@Service
public class PathBookmarkGroupServiceImpl implements PathBookmarkGroupService {
@Resource
private DataGroupApi dataGroupApi;
@Resource
private DataGroupUserApi dataGroupUserApi;
@Resource
private PathBookmarkDAO pathBookmarkDAO;
@Resource
private PathBookmarkService pathBookmarkService;
@Override
public Long createPathBookmarkGroup(PathBookmarkGroupCreateRequest request) {
Long userId = SecurityUtils.getLoginUserId();
log.info("PathBookmarkGroupService-createPathBookmarkGroup request: {}", JSON.toJSONString(request));
// 创建
DataGroupCreateDTO create = PathBookmarkGroupConvert.MAPPER.to(request);
create.setParentId(Const.ROOT_PARENT_ID);
return dataGroupUserApi.createDataGroup(DataGroupTypeEnum.PATH_BOOKMARK, userId, create);
}
@Override
public Integer updatePathBookmarkGroupById(PathBookmarkGroupUpdateRequest request) {
Long id = Valid.notNull(request.getId(), ErrorMessage.ID_MISSING);
log.info("PathBookmarkGroupService-updatePathBookmarkGroupById id: {}, request: {}", id, JSON.toJSONString(request));
// 重命名
DataGroupRenameDTO rename = PathBookmarkGroupConvert.MAPPER.to(request);
return dataGroupApi.renameDataGroup(rename);
}
@Override
public List<PathBookmarkGroupVO> getPathBookmarkGroupList() {
Long userId = SecurityUtils.getLoginUserId();
// 查询分组
return dataGroupUserApi.getDataGroupList(DataGroupTypeEnum.PATH_BOOKMARK, userId)
.stream()
.sorted(Comparator.comparing(DataGroupDTO::getSort))
.map(PathBookmarkGroupConvert.MAPPER::to)
.collect(Collectors.toList());
}
@Override
@Transactional(rollbackFor = Exception.class)
public Integer deletePathBookmarkGroup(PathBookmarkGroupDeleteRequest request) {
Long userId = SecurityUtils.getLoginUserId();
Long id = request.getId();
log.info("PathBookmarkGroupService-deletePathBookmarkGroupById id: {}", id);
// 删除分组
Integer effect = dataGroupApi.deleteDataGroupById(id);
if (Booleans.isTrue(request.getDeleteItem())) {
// 删除组内数据
effect += pathBookmarkService.deleteByGroupId(userId, id);
} else {
// 移动到根节点
effect += pathBookmarkService.setGroupNull(userId, id);
}
return effect;
}
@Override
public void clearUnusedGroup() {
// 查询全部 groupId
Map<Long, List<Long>> userGroupMap = pathBookmarkDAO.of()
.createWrapper()
.select(PathBookmarkDO::getUserId, PathBookmarkDO::getGroupId)
.isNotNull(PathBookmarkDO::getGroupId)
.groupBy(PathBookmarkDO::getGroupId)
.then()
.stream()
.collect(Collectors.groupingBy(PathBookmarkDO::getUserId,
Collectors.mapping(PathBookmarkDO::getGroupId, Collectors.toList())));
userGroupMap.forEach((k, v) -> {
// 查询用户分组
List<DataGroupDTO> groups = dataGroupUserApi.getDataGroupList(DataGroupTypeEnum.PATH_BOOKMARK, k);
if (groups.isEmpty()) {
return;
}
// 不存在的则移除
List<Long> deleteGroupList = groups.stream()
.map(DataGroupDTO::getId)
.filter(s -> !v.contains(s))
.collect(Collectors.toList());
if (deleteGroupList.isEmpty()) {
return;
}
log.info("PathBookmarkGroupService.clearUnusedGroup userId: {}, deleteGroupList: {}", k, deleteGroupList);
// 删除分组
Integer effect = dataGroupUserApi.deleteDataGroupByIdList(DataGroupTypeEnum.PATH_BOOKMARK, k, deleteGroupList);
log.info("PathBookmarkGroupService.clearUnusedGroup userId: {}, effect: {}", k, effect);
});
}
}

View File

@@ -0,0 +1,196 @@
package com.orion.ops.module.asset.service.impl;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.orion.ops.framework.common.constant.ErrorMessage;
import com.orion.ops.framework.common.utils.Valid;
import com.orion.ops.framework.redis.core.utils.RedisMaps;
import com.orion.ops.framework.redis.core.utils.barrier.CacheBarriers;
import com.orion.ops.framework.security.core.utils.SecurityUtils;
import com.orion.ops.module.asset.convert.PathBookmarkConvert;
import com.orion.ops.module.asset.dao.PathBookmarkDAO;
import com.orion.ops.module.asset.define.cache.PathBookmarkCacheKeyDefine;
import com.orion.ops.module.asset.entity.domain.PathBookmarkDO;
import com.orion.ops.module.asset.entity.dto.PathBookmarkCacheDTO;
import com.orion.ops.module.asset.entity.request.path.PathBookmarkCreateRequest;
import com.orion.ops.module.asset.entity.request.path.PathBookmarkUpdateRequest;
import com.orion.ops.module.asset.entity.vo.PathBookmarkGroupVO;
import com.orion.ops.module.asset.entity.vo.PathBookmarkVO;
import com.orion.ops.module.asset.entity.vo.PathBookmarkWrapperVO;
import com.orion.ops.module.asset.service.PathBookmarkGroupService;
import com.orion.ops.module.asset.service.PathBookmarkService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* 路径标签 服务实现类
*
* @author Jiahang Li
* @version 1.0.6
* @since 2024-4-23 23:15
*/
@Slf4j
@Service
public class PathBookmarkServiceImpl implements PathBookmarkService {
@Resource
private PathBookmarkDAO pathBookmarkDAO;
@Resource
private PathBookmarkGroupService pathBookmarkGroupService;
@Override
public Long createPathBookmark(PathBookmarkCreateRequest request) {
Long userId = SecurityUtils.getLoginUserId();
log.info("PathBookmarkService-createPathBookmark request: {}", JSON.toJSONString(request));
// 转换
PathBookmarkDO record = PathBookmarkConvert.MAPPER.to(request);
record.setUserId(userId);
// 查询数据是否冲突
this.checkPathBookmarkPresent(record);
// 插入
int effect = pathBookmarkDAO.insert(record);
Long id = record.getId();
log.info("PathBookmarkService-createPathBookmark id: {}, effect: {}", id, effect);
// 删除缓存
RedisMaps.delete(PathBookmarkCacheKeyDefine.PATH_BOOKMARK.format(userId));
return id;
}
@Override
public Integer updatePathBookmarkById(PathBookmarkUpdateRequest request) {
Long id = Valid.notNull(request.getId(), ErrorMessage.ID_MISSING);
Long userId = SecurityUtils.getLoginUserId();
log.info("PathBookmarkService-updatePathBookmarkById id: {}, request: {}", id, JSON.toJSONString(request));
// 查询
PathBookmarkDO record = pathBookmarkDAO.selectById(id);
Valid.notNull(record, ErrorMessage.DATA_ABSENT);
// 查询数据是否冲突
PathBookmarkDO updateRecord = PathBookmarkConvert.MAPPER.to(request);
this.checkPathBookmarkPresent(updateRecord);
// 更新
LambdaUpdateWrapper<PathBookmarkDO> update = Wrappers.<PathBookmarkDO>lambdaUpdate()
.set(PathBookmarkDO::getGroupId, request.getGroupId())
.set(PathBookmarkDO::getName, request.getName())
.set(PathBookmarkDO::getPath, request.getPath())
.eq(PathBookmarkDO::getId, id)
.eq(PathBookmarkDO::getUserId, userId);
int effect = pathBookmarkDAO.update(null, update);
log.info("PathBookmarkService-updatePathBookmarkById effect: {}", effect);
// 删除缓存
RedisMaps.delete(PathBookmarkCacheKeyDefine.PATH_BOOKMARK.format(userId));
return effect;
}
@Override
public PathBookmarkWrapperVO getPathBookmark() {
// 查询分组
List<PathBookmarkGroupVO> groups = pathBookmarkGroupService.getPathBookmarkGroupList();
// 查询命令片段
List<PathBookmarkVO> items = this.getPathBookmarkList();
// 设置组内数据
Map<Long, PathBookmarkGroupVO> groupMap = groups.stream()
.collect(Collectors.toMap(PathBookmarkGroupVO::getId, Function.identity()));
groupMap.forEach((groupId, group) -> {
List<PathBookmarkVO> groupedItems = items.stream()
.filter(s -> groupId.equals(s.getGroupId()))
.collect(Collectors.toList());
group.setItems(groupedItems);
});
// 未分组数据
List<PathBookmarkVO> ungroupedItems = items.stream()
.filter(s -> s.getGroupId() == null)
.collect(Collectors.toList());
return PathBookmarkWrapperVO.builder()
.groups(groups)
.ungroupedItems(ungroupedItems)
.build();
}
@Override
public List<PathBookmarkVO> getPathBookmarkList() {
Long userId = SecurityUtils.getLoginUserId();
String cacheKey = PathBookmarkCacheKeyDefine.PATH_BOOKMARK.format(userId);
// 查询缓存
List<PathBookmarkCacheDTO> list = RedisMaps.valuesJson(cacheKey, PathBookmarkCacheKeyDefine.PATH_BOOKMARK);
if (list.isEmpty()) {
// 查询数据库
list = pathBookmarkDAO.of()
.createWrapper()
.eq(PathBookmarkDO::getUserId, userId)
.then()
.list(PathBookmarkConvert.MAPPER::toCache);
// 设置屏障 防止穿透
CacheBarriers.checkBarrier(list, PathBookmarkCacheDTO::new);
// 设置缓存
RedisMaps.putAllJson(cacheKey, PathBookmarkCacheKeyDefine.PATH_BOOKMARK, s -> s.getId().toString(), list);
}
// 删除屏障
CacheBarriers.removeBarrier(list);
// 转换
return list.stream()
.map(PathBookmarkConvert.MAPPER::to)
.sorted(Comparator.comparing(PathBookmarkVO::getId))
.collect(Collectors.toList());
}
@Override
public Integer deletePathBookmarkById(Long id) {
Long userId = SecurityUtils.getLoginUserId();
log.info("PathBookmarkService-deletePathBookmarkById id: {}", id);
// 检查数据是否存在
PathBookmarkDO record = pathBookmarkDAO.selectById(id);
Valid.notNull(record, ErrorMessage.DATA_ABSENT);
// 删除
int effect = pathBookmarkDAO.deleteById(id);
log.info("PathBookmarkService-deletePathBookmarkById id: {}, effect: {}", id, effect);
// 删除缓存
RedisMaps.delete(PathBookmarkCacheKeyDefine.PATH_BOOKMARK.format(userId), id);
return effect;
}
@Override
public Integer setGroupNull(Long userId, Long groupId) {
int effect = pathBookmarkDAO.setGroupIdWithNull(groupId);
// 删除缓存
RedisMaps.delete(PathBookmarkCacheKeyDefine.PATH_BOOKMARK.format(userId));
return effect;
}
@Override
public Integer deleteByGroupId(Long userId, Long groupId) {
int effect = pathBookmarkDAO.deleteByGroupId(groupId);
// 删除缓存
RedisMaps.delete(PathBookmarkCacheKeyDefine.PATH_BOOKMARK.format(userId));
return effect;
}
/**
* 检查对象是否存在
*
* @param domain domain
*/
private void checkPathBookmarkPresent(PathBookmarkDO domain) {
// 构造条件
LambdaQueryWrapper<PathBookmarkDO> wrapper = pathBookmarkDAO.wrapper()
// 更新时忽略当前记录
.ne(PathBookmarkDO::getId, domain.getId())
// 用其他字段做重复校验
.eq(PathBookmarkDO::getUserId, domain.getUserId())
.eq(PathBookmarkDO::getGroupId, domain.getGroupId())
.eq(PathBookmarkDO::getName, domain.getName());
// 检查是否存在
boolean present = pathBookmarkDAO.of(wrapper).present();
Valid.isFalse(present, ErrorMessage.DATA_PRESENT);
}
}

View File

@@ -0,0 +1,41 @@
package com.orion.ops.module.asset.task;
import com.orion.ops.framework.redis.core.utils.RedisLocks;
import com.orion.ops.module.asset.service.CommandSnippetGroupService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* 定时清理未使用的命令片段分组
*
* @author Jiahang Li
* @version 1.0.0
* @since 2024/4/24 23:50
*/
@Slf4j
@Component
public class CommandSnippetGroupAutoClearTask {
/**
* 分布式锁名称
*/
private static final String LOCK_KEY = "clear:csg:lock";
@Resource
private CommandSnippetGroupService commandSnippetGroupService;
/**
* 清理
*/
@Scheduled(cron = "0 10 2 * * ?")
public void clear() {
log.info("CommandSnippetGroupAutoClearTask.clear start");
// 获取锁并清理
RedisLocks.tryLock(LOCK_KEY, commandSnippetGroupService::clearUnusedGroup);
log.info("CommandSnippetGroupAutoClearTask.clear finish");
}
}

View File

@@ -0,0 +1,94 @@
package com.orion.ops.module.asset.task;
import com.orion.lang.utils.Strings;
import com.orion.lang.utils.io.Files1;
import com.orion.lang.utils.time.Dates;
import com.orion.ops.framework.common.file.FileClient;
import com.orion.ops.framework.redis.core.utils.RedisLocks;
import com.orion.ops.module.asset.dao.ExecHostLogDAO;
import com.orion.ops.module.asset.define.config.AppExecLogConfig;
import com.orion.ops.module.asset.entity.domain.ExecHostLogDO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.io.File;
import java.util.List;
/**
* 执行日志文件自动清理
*
* @author Jiahang Li
* @version 1.0.0
* @since 2024/4/24 23:50
*/
@Slf4j
@Component
@ConditionalOnProperty(value = "app.exec-log.auto-clear", havingValue = "true", matchIfMissing = true)
public class ExecLogFileAutoClearTask {
/**
* 分布式锁名称
*/
private static final String LOCK_KEY = "clear:elf:lock";
@Resource
private AppExecLogConfig appExecLogConfig;
@Resource
private FileClient logsFileClient;
@Resource
private ExecHostLogDAO execHostLogDAO;
/**
* 清理
*/
@Scheduled(cron = "0 0 3 * * ?")
public void clear() {
log.info("ExecLogFileAutoClearTask.clear start");
// 获取锁并且执行
RedisLocks.tryLock(LOCK_KEY, this::doClearFile);
log.info("ExecLogFileAutoClearTask.clear finish");
}
/**
* 执行清理文件
*/
private void doClearFile() {
// 删除的时间区间
String maxPeriod = Dates.stream()
.subDay(appExecLogConfig.getKeepPeriod())
.format();
// 获取需要删除的最大id
ExecHostLogDO hostLog = execHostLogDAO.of()
.createWrapper()
.select(ExecHostLogDO::getLogId, ExecHostLogDO::getLogPath)
.lt(ExecHostLogDO::getCreateTime, maxPeriod)
.orderByDesc(ExecHostLogDO::getId)
.then()
.getOne();
if (hostLog == null) {
return;
}
// 获取执行日志根目录
String hostLogPath = logsFileClient.getAbsolutePath(hostLog.getLogPath());
String execLogPath = Files1.getParentPath(hostLogPath);
String parentPath = Files1.getParentPath(execLogPath);
// 获取需要删除的文件
List<File> files = Files1.listFilesFilter(parentPath, s -> {
if (!Strings.isInteger(s.getName())) {
return false;
}
return Long.parseLong(s.getName()) <= hostLog.getLogId();
}, false, true);
if (files.isEmpty()) {
return;
}
// 删除日志文件
files.forEach(Files1::delete);
}
}

View File

@@ -0,0 +1,41 @@
package com.orion.ops.module.asset.task;
import com.orion.ops.framework.redis.core.utils.RedisLocks;
import com.orion.ops.module.asset.service.PathBookmarkGroupService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* 定时清理未使用的路径标签分组
*
* @author Jiahang Li
* @version 1.0.0
* @since 2024/4/24 23:50
*/
@Slf4j
@Component
public class PathBookmarkGroupAutoClearTask {
/**
* 分布式锁名称
*/
private static final String LOCK_KEY = "clear:pbg:lock";
@Resource
private PathBookmarkGroupService pathBookmarkGroupService;
/**
* 清理
*/
@Scheduled(cron = "0 20 2 * * ?")
public void clear() {
log.info("PathBookmarkGroupAutoClearTask.clear start");
// 获取锁并清理
RedisLocks.tryLock(LOCK_KEY, pathBookmarkGroupService::clearUnusedGroup);
log.info("PathBookmarkGroupAutoClearTask.clear finish");
}
}

View File

@@ -9,6 +9,11 @@
"name": "app.sftp",
"type": "com.orion.ops.module.asset.define.config.AppSftpConfig",
"sourceType": "com.orion.ops.module.asset.define.config.AppSftpConfig"
},
{
"name": "app.exec-log",
"type": "com.orion.ops.module.asset.define.config.AppExecLogConfig",
"sourceType": "com.orion.ops.module.asset.define.config.AppExecLogConfig"
}
],
"properties": [
@@ -41,6 +46,24 @@
"type": "java.lang.String",
"description": "备份文件名称.",
"defaultValue": "bk_${fileName}_${timestamp}"
},
{
"name": "app.exec-log.append-ansi",
"type": "java.lang.Boolean",
"description": "是否拼接 ansi 执行状态日志.",
"defaultValue": "true"
},
{
"name": "app.exec-log.auto-clear",
"type": "java.lang.Boolean",
"description": "自动清理执行文件.",
"defaultValue": "true"
},
{
"name": "app.exec-log.keep-period",
"type": "java.lang.Integer",
"description": "保留周期 (天)",
"defaultValue": "60"
}
]
}

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.orion.ops.module.asset.dao.ExecTemplateHostDAO">
<!-- 通用查询映射结果 -->
<resultMap id="BaseResultMap" type="com.orion.ops.module.asset.entity.domain.ExecTemplateHostDO">
<id column="id" property="id"/>
<result column="template_id" property="templateId"/>
<result column="host_id" property="hostId"/>
<result column="create_time" property="createTime"/>
<result column="update_time" property="updateTime"/>
<result column="creator" property="creator"/>
<result column="updater" property="updater"/>
<result column="deleted" property="deleted"/>
</resultMap>
<!-- 通用查询结果列 -->
<sql id="Base_Column_List">
id, template_id, host_id, create_time, update_time, creator, updater, deleted
</sql>
</mapper>

View File

@@ -1,12 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.orion.ops.module.asset.dao.CommandSnippetGroupDAO">
<mapper namespace="com.orion.ops.module.asset.dao.PathBookmarkDAO">
<!-- 通用查询映射结果 -->
<resultMap id="BaseResultMap" type="com.orion.ops.module.asset.entity.domain.CommandSnippetGroupDO">
<resultMap id="BaseResultMap" type="com.orion.ops.module.asset.entity.domain.PathBookmarkDO">
<id column="id" property="id"/>
<result column="user_id" property="userId"/>
<result column="group_id" property="groupId"/>
<result column="name" property="name"/>
<result column="type" property="type"/>
<result column="path" property="path"/>
<result column="create_time" property="createTime"/>
<result column="update_time" property="updateTime"/>
<result column="creator" property="creator"/>
@@ -16,7 +19,7 @@
<!-- 通用查询结果列 -->
<sql id="Base_Column_List">
id, user_id, name, create_time, update_time, creator, updater, deleted
id, user_id, group_id, name, type, path, create_time, update_time, creator, updater, deleted
</sql>
</mapper>

View File

@@ -74,4 +74,13 @@ public interface DataGroupApi {
*/
Integer deleteDataGroupById(Long id);
/**
* 删除数据分组
*
* @param type type
* @param idList idList
* @return effect
*/
Integer deleteDataGroupByIdList(DataGroupTypeEnum type, List<Long> idList);
}

View File

@@ -32,7 +32,7 @@ public interface DataGroupRelApi {
* @param groupIdList groupIdList
* @param relId relId
*/
void updateGroupRel(DataGroupTypeEnum type, List<Long> groupIdList, Long relId);
void updateRelGroup(DataGroupTypeEnum type, List<Long> groupIdList, Long relId);
/**
* 添加关联
@@ -40,14 +40,14 @@ public interface DataGroupRelApi {
* @param groupId groupId
* @param relId relId
*/
void addGroupRel(Long groupId, Long relId);
void addGroupRel(DataGroupTypeEnum type, Long groupId, Long relId);
/**
* 批量添加关联
*
* @param list list
*/
void addGroupRel(List<DataGroupRelCreateDTO> list);
void addGroupRel(DataGroupTypeEnum type, List<DataGroupRelCreateDTO> list);
/**
* 通过 type 查询 relId 缓存

View File

@@ -0,0 +1,56 @@
package com.orion.ops.module.infra.api;
import com.orion.ops.module.infra.entity.dto.data.DataGroupCreateDTO;
import com.orion.ops.module.infra.entity.dto.data.DataGroupDTO;
import com.orion.ops.module.infra.enums.DataGroupTypeEnum;
import java.util.List;
/**
* 数据分组用户 对外服务类
*
* @author Jiahang Li
* @version 1.0.0
* @since 2023-11-7 18:44
*/
public interface DataGroupUserApi {
/**
* 创建数据分组
*
* @param type type
* @param userId userId
* @param dto dto
* @return id
*/
Long createDataGroup(DataGroupTypeEnum type, Long userId, DataGroupCreateDTO dto);
/**
* 通过缓存查询数据分组
*
* @param type type
* @param userId userId
* @return rows
*/
List<DataGroupDTO> getDataGroupList(DataGroupTypeEnum type, Long userId);
/**
* 通过缓存查询数据分组
*
* @param type type
* @param userId userId
* @return rows
*/
List<DataGroupDTO> getDataGroupTree(DataGroupTypeEnum type, Long userId);
/**
* 删除数据分组
*
* @param type type
* @param userId userId
* @param idList idList
* @return effect
*/
Integer deleteDataGroupByIdList(DataGroupTypeEnum type, Long userId, List<Long> idList);
}

View File

@@ -0,0 +1,109 @@
package com.orion.ops.module.infra.api;
import com.orion.ops.module.infra.entity.dto.data.DataGroupRelCreateDTO;
import com.orion.ops.module.infra.enums.DataGroupTypeEnum;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Future;
/**
* 数据分组用户关联 对外服务类
*
* @author Jiahang Li
* @version 1.0.0
* @since 2023-11-7 18:44
*/
public interface DataGroupUserRelApi {
/**
* 设置关联
*
* @param type type
* @param userId userId
* @param groupIdList groupIdList
* @param relId relId
*/
void updateGroupRel(DataGroupTypeEnum type, Long userId, List<Long> groupIdList, Long relId);
/**
* 添加关联
*
* @param type type
* @param userId userId
* @param groupId groupId
* @param relId relId
*/
void addGroupRel(DataGroupTypeEnum type, Long userId, Long groupId, Long relId);
/**
* 批量添加关联
*
* @param type type
* @param userId userId
* @param list list
*/
void addGroupRel(DataGroupTypeEnum type, Long userId, List<DataGroupRelCreateDTO> list);
/**
* 通过 type 查询 relId 缓存
* <p>
* groupId - relId
*
* @param type type
* @param userId userId
* @return rows
*/
Map<Long, Set<Long>> getGroupRelList(DataGroupTypeEnum type, Long userId);
/**
* 通过 groupId 查询 relId 缓存
*
* @param type type
* @param userId userId
* @param groupId groupId
* @return relId
*/
Set<Long> getGroupRelIdByGroupId(DataGroupTypeEnum type, Long userId, Long groupId);
/**
* 通过 relId 查询 groupId
*
* @param type type
* @param relId relId
* @return groupId
*/
Future<Set<Long>> getGroupIdByRelIdAsync(DataGroupTypeEnum type, Long userId, Long relId);
/**
* 删除数据分组关联
*
* @param type type
* @param userId userId
* @param relId relId
* @return effect
*/
Integer deleteByRelId(DataGroupTypeEnum type, Long userId, Long relId);
/**
* 批量删除数据分组关联
*
* @param type type
* @param userId userId
* @param relIdList relIdList
* @return effect
*/
Integer deleteByRelIdList(DataGroupTypeEnum type, Long userId, List<Long> relIdList);
/**
* 批量删除数据分组关联
*
* @param type type
* @param userId userId
* @param groupIdList groupIdList
* @return effect
*/
Integer deleteByGroupIdList(DataGroupTypeEnum type, Long userId, List<Long> groupIdList);
}

View File

@@ -30,7 +30,7 @@ public interface FavoriteApi {
* @param userId userId
* @return relIdList
*/
Future<List<Long>> getFavoriteRelIdListAsync(FavoriteTypeEnum type, java.lang.Long userId);
Future<List<Long>> getFavoriteRelIdListAsync(FavoriteTypeEnum type, Long userId);
/**
* 通过 relId 删除收藏

View File

@@ -19,6 +19,11 @@ public enum DataGroupTypeEnum {
*/
COMMAND_SNIPPET,
/**
* 路径书签
*/
PATH_BOOKMARK,
;
public static DataGroupTypeEnum of(String type) {

View File

@@ -1,5 +1,6 @@
package com.orion.ops.module.infra.api.impl;
import com.orion.ops.framework.common.constant.Const;
import com.orion.ops.framework.common.utils.Valid;
import com.orion.ops.module.infra.api.DataGroupApi;
import com.orion.ops.module.infra.convert.DataGroupProviderConvert;
@@ -44,6 +45,7 @@ public class DataGroupApiImpl implements DataGroupApi {
Valid.valid(dto);
DataGroupCreateRequest request = DataGroupProviderConvert.MAPPER.toRequest(dto);
request.setType(type.name());
request.setUserId(Const.SYSTEM_USER_ID);
return dataGroupService.createDataGroup(request);
}
@@ -63,13 +65,13 @@ public class DataGroupApiImpl implements DataGroupApi {
@Override
public List<DataGroupDTO> getDataGroupList(DataGroupTypeEnum type) {
List<DataGroupCacheDTO> rows = dataGroupService.getDataGroupListByCache(type.name());
List<DataGroupCacheDTO> rows = dataGroupService.getDataGroupListByCache(type.name(), Const.SYSTEM_USER_ID);
return DataGroupProviderConvert.MAPPER.toList(rows);
}
@Override
public List<DataGroupDTO> getDataGroupTree(DataGroupTypeEnum type) {
List<DataGroupCacheDTO> rows = dataGroupService.getDataGroupTreeByCache(type.name());
List<DataGroupCacheDTO> rows = dataGroupService.getDataGroupTreeByCache(type.name(), Const.SYSTEM_USER_ID);
return DataGroupProviderConvert.MAPPER.toList(rows);
}
@@ -86,4 +88,9 @@ public class DataGroupApiImpl implements DataGroupApi {
return dataGroupService.deleteDataGroupById(id);
}
@Override
public Integer deleteDataGroupByIdList(DataGroupTypeEnum type, List<Long> idList) {
return dataGroupService.deleteDataGroupByIdList(type.name(), Const.SYSTEM_USER_ID, idList);
}
}

View File

@@ -1,5 +1,6 @@
package com.orion.ops.module.infra.api.impl;
import com.orion.ops.framework.common.constant.Const;
import com.orion.ops.framework.common.utils.Valid;
import com.orion.ops.module.infra.api.DataGroupRelApi;
import com.orion.ops.module.infra.convert.DataGroupRelProviderConvert;
@@ -43,28 +44,28 @@ public class DataGroupRelApiImpl implements DataGroupRelApi {
}
@Override
public void updateGroupRel(DataGroupTypeEnum type, List<Long> groupIdList, Long relId) {
public void updateRelGroup(DataGroupTypeEnum type, List<Long> groupIdList, Long relId) {
Valid.notNull(relId);
dataGroupRelService.updateGroupRel(type.name(), groupIdList, relId);
dataGroupRelService.updateRelGroup(type.name(), Const.SYSTEM_USER_ID, groupIdList, relId);
}
@Override
public void addGroupRel(Long groupId, Long relId) {
public void addGroupRel(DataGroupTypeEnum type, Long groupId, Long relId) {
Valid.notNull(groupId);
Valid.notNull(relId);
dataGroupRelService.addGroupRel(groupId, relId);
dataGroupRelService.addGroupRel(type.name(), groupId, Const.SYSTEM_USER_ID, relId);
}
@Override
public void addGroupRel(List<DataGroupRelCreateDTO> list) {
public void addGroupRel(DataGroupTypeEnum type, List<DataGroupRelCreateDTO> list) {
Valid.valid(list);
List<DataGroupRelCreateRequest> rows = DataGroupRelProviderConvert.MAPPER.toList(list);
dataGroupRelService.addGroupRel(rows);
dataGroupRelService.addGroupRel(type.name(), Const.SYSTEM_USER_ID, rows);
}
@Override
public Map<Long, Set<Long>> getGroupRelList(DataGroupTypeEnum type) {
List<DataGroupRelCacheDTO> rows = dataGroupRelService.getGroupRelListByCache(type.name());
List<DataGroupRelCacheDTO> rows = dataGroupRelService.getGroupRelListByCache(type.name(), Const.SYSTEM_USER_ID);
return rows.stream().collect(
Collectors.groupingBy(
DataGroupRelCacheDTO::getGroupId,
@@ -81,7 +82,7 @@ public class DataGroupRelApiImpl implements DataGroupRelApi {
@Override
@Async("asyncExecutor")
public Future<Set<Long>> getGroupIdByRelIdAsync(DataGroupTypeEnum type, Long relId) {
Set<Long> groupIdList = dataGroupRelService.getGroupRelByRelId(type.name(), relId)
Set<Long> groupIdList = dataGroupRelService.getGroupRelByRelId(type.name(), Const.SYSTEM_USER_ID, relId)
.stream()
.map(DataGroupRelDO::getGroupId)
.collect(Collectors.toSet());
@@ -90,17 +91,17 @@ public class DataGroupRelApiImpl implements DataGroupRelApi {
@Override
public Integer deleteByRelId(DataGroupTypeEnum type, Long relId) {
return dataGroupRelService.deleteByRelId(type.name(), relId);
return dataGroupRelService.deleteByRelId(type.name(), Const.SYSTEM_USER_ID, relId);
}
@Override
public Integer deleteByRelIdList(DataGroupTypeEnum type, List<Long> relIdList) {
return dataGroupRelService.deleteByRelIdList(type.name(), relIdList);
return dataGroupRelService.deleteByRelIdList(type.name(), Const.SYSTEM_USER_ID, relIdList);
}
@Override
public Integer deleteByGroupIdList(DataGroupTypeEnum type, List<Long> groupIdList) {
return dataGroupRelService.deleteByGroupIdList(type.name(), groupIdList);
return dataGroupRelService.deleteByGroupIdList(type.name(), Const.SYSTEM_USER_ID, groupIdList);
}
}

View File

@@ -0,0 +1,58 @@
package com.orion.ops.module.infra.api.impl;
import com.orion.ops.framework.common.utils.Valid;
import com.orion.ops.module.infra.api.DataGroupUserApi;
import com.orion.ops.module.infra.convert.DataGroupProviderConvert;
import com.orion.ops.module.infra.entity.dto.DataGroupCacheDTO;
import com.orion.ops.module.infra.entity.dto.data.DataGroupCreateDTO;
import com.orion.ops.module.infra.entity.dto.data.DataGroupDTO;
import com.orion.ops.module.infra.entity.request.data.DataGroupCreateRequest;
import com.orion.ops.module.infra.enums.DataGroupTypeEnum;
import com.orion.ops.module.infra.service.DataGroupService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
/**
* 数据分组 对外服务实现类
*
* @author Jiahang Li
* @version 1.0.0
* @since 2023-11-7 18:44
*/
@Slf4j
@Service
public class DataGroupUserApiImpl implements DataGroupUserApi {
@Resource
private DataGroupService dataGroupService;
@Override
public Long createDataGroup(DataGroupTypeEnum type, Long userId, DataGroupCreateDTO dto) {
Valid.valid(dto);
DataGroupCreateRequest request = DataGroupProviderConvert.MAPPER.toRequest(dto);
request.setType(type.name());
request.setUserId(userId);
return dataGroupService.createDataGroup(request);
}
@Override
public List<DataGroupDTO> getDataGroupList(DataGroupTypeEnum type, Long userId) {
List<DataGroupCacheDTO> rows = dataGroupService.getDataGroupListByCache(type.name(), userId);
return DataGroupProviderConvert.MAPPER.toList(rows);
}
@Override
public List<DataGroupDTO> getDataGroupTree(DataGroupTypeEnum type, Long userId) {
List<DataGroupCacheDTO> rows = dataGroupService.getDataGroupTreeByCache(type.name(), userId);
return DataGroupProviderConvert.MAPPER.toList(rows);
}
@Override
public Integer deleteDataGroupByIdList(DataGroupTypeEnum type, Long userId, List<Long> idList) {
return dataGroupService.deleteDataGroupByIdList(type.name(), userId, idList);
}
}

View File

@@ -0,0 +1,100 @@
package com.orion.ops.module.infra.api.impl;
import com.orion.ops.framework.common.utils.Valid;
import com.orion.ops.module.infra.api.DataGroupUserRelApi;
import com.orion.ops.module.infra.convert.DataGroupRelProviderConvert;
import com.orion.ops.module.infra.entity.domain.DataGroupRelDO;
import com.orion.ops.module.infra.entity.dto.DataGroupRelCacheDTO;
import com.orion.ops.module.infra.entity.dto.data.DataGroupRelCreateDTO;
import com.orion.ops.module.infra.entity.request.data.DataGroupRelCreateRequest;
import com.orion.ops.module.infra.enums.DataGroupTypeEnum;
import com.orion.ops.module.infra.service.DataGroupRelService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Future;
import java.util.stream.Collectors;
/**
* 数据分组关联 对外服务实现类
*
* @author Jiahang Li
* @version 1.0.0
* @since 2023-11-7 18:44
*/
@Slf4j
@Service
public class DataGroupUserRelApiImpl implements DataGroupUserRelApi {
@Resource
private DataGroupRelService dataGroupRelService;
@Override
public void updateGroupRel(DataGroupTypeEnum type, Long userId, List<Long> groupIdList, Long relId) {
Valid.notNull(relId);
dataGroupRelService.updateRelGroup(type.name(), userId, groupIdList, relId);
}
@Override
public void addGroupRel(DataGroupTypeEnum type, Long userId, Long groupId, Long relId) {
Valid.notNull(groupId);
Valid.notNull(relId);
dataGroupRelService.addGroupRel(type.name(), userId, groupId, relId);
}
@Override
public void addGroupRel(DataGroupTypeEnum type, Long userId, List<DataGroupRelCreateDTO> list) {
Valid.valid(list);
List<DataGroupRelCreateRequest> rows = DataGroupRelProviderConvert.MAPPER.toList(list);
dataGroupRelService.addGroupRel(type.name(), userId, rows);
}
@Override
public Map<Long, Set<Long>> getGroupRelList(DataGroupTypeEnum type, Long userId) {
List<DataGroupRelCacheDTO> rows = dataGroupRelService.getGroupRelListByCache(type.name(), userId);
return rows.stream().collect(
Collectors.groupingBy(
DataGroupRelCacheDTO::getGroupId,
Collectors.mapping(DataGroupRelCacheDTO::getRelId, Collectors.toSet())
));
}
@Override
public Set<Long> getGroupRelIdByGroupId(DataGroupTypeEnum type, Long userId, Long groupId) {
List<Long> rows = dataGroupRelService.getGroupRelIdListByCache(type.name(), groupId);
return new HashSet<>(rows);
}
@Override
@Async("asyncExecutor")
public Future<Set<Long>> getGroupIdByRelIdAsync(DataGroupTypeEnum type, Long userId, Long relId) {
Set<Long> groupIdList = dataGroupRelService.getGroupRelByRelId(type.name(), userId, relId)
.stream()
.map(DataGroupRelDO::getGroupId)
.collect(Collectors.toSet());
return CompletableFuture.completedFuture(groupIdList);
}
@Override
public Integer deleteByRelId(DataGroupTypeEnum type, Long userId, Long relId) {
return dataGroupRelService.deleteByRelId(type.name(), userId, relId);
}
@Override
public Integer deleteByRelIdList(DataGroupTypeEnum type, Long userId, List<Long> relIdList) {
return dataGroupRelService.deleteByRelIdList(type.name(), userId, relIdList);
}
@Override
public Integer deleteByGroupIdList(DataGroupTypeEnum type, Long userId, List<Long> groupIdList) {
return dataGroupRelService.deleteByGroupIdList(type.name(), userId, groupIdList);
}
}

View File

@@ -40,4 +40,12 @@ public interface SystemRoleDAO extends IMapper<SystemRoleDO> {
*/
Long getRoleIdByUserIdAndRoleCode(@Param("userId") Long userId, @Param("code") String code);
/**
* 查询用户角色
*
* @param userId userId
* @return roles
*/
List<SystemRoleDO> selectRoleByUserId(@Param("userId") Long userId);
}

View File

@@ -20,7 +20,7 @@ public interface DataExtraCacheKeyDefine {
.desc("数据拓展信息 ${userId} ${type} ${item}")
.type(String.class)
.struct(RedisCacheStruct.HASH)
.timeout(1, TimeUnit.DAYS)
.timeout(8, TimeUnit.HOURS)
.build();
}

View File

@@ -18,19 +18,19 @@ import java.util.concurrent.TimeUnit;
public interface DataGroupCacheKeyDefine {
CacheKeyDefine DATA_GROUP_LIST = new CacheKeyBuilder()
.key("data:group-list:{}")
.desc("数据分组列表结构 ${type}")
.key("data:group-list:{}:{}")
.desc("数据分组列表结构 ${type} ${userId}")
.type(DataGroupCacheDTO.class)
.struct(RedisCacheStruct.STRING)
.timeout(1, TimeUnit.DAYS)
.timeout(8, TimeUnit.HOURS)
.build();
CacheKeyDefine DATA_GROUP_TREE = new CacheKeyBuilder()
.key("data:group-tree:{}")
.desc("数据分组树结构 ${type}")
.key("data:group-tree:{}:{}")
.desc("数据分组树结构 ${type} ${userId}")
.type(DataGroupCacheDTO.class)
.struct(RedisCacheStruct.STRING)
.timeout(1, TimeUnit.DAYS)
.timeout(8, TimeUnit.HOURS)
.build();
CacheKeyDefine DATA_GROUP_REL_GROUP = new CacheKeyBuilder()
@@ -38,15 +38,15 @@ public interface DataGroupCacheKeyDefine {
.desc("数据分组数据关联-分组 ${groupId}")
.type(Long.class)
.struct(RedisCacheStruct.LIST)
.timeout(1, TimeUnit.DAYS)
.timeout(8, TimeUnit.HOURS)
.build();
CacheKeyDefine DATA_GROUP_REL_TYPE = new CacheKeyBuilder()
.key("data:group-rel:type:{}")
.desc("数据分组数据关联-类型 ${type}")
.key("data:group-rel:type:{}:{}")
.desc("数据分组数据关联-类型 ${type} ${userId}")
.type(DataGroupRelCacheDTO.class)
.struct(RedisCacheStruct.STRING)
.timeout(1, TimeUnit.DAYS)
.timeout(8, TimeUnit.HOURS)
.build();
}

View File

@@ -20,7 +20,7 @@ public interface DataPermissionCacheKeyDefine {
.desc("用户所有数据权限 ${type} ${userId}")
.type(Long.class)
.struct(RedisCacheStruct.LIST)
.timeout(1, TimeUnit.DAYS)
.timeout(8, TimeUnit.HOURS)
.build();
}

View File

@@ -22,7 +22,7 @@ public interface DictCacheKeyDefine {
.desc("字典配置项")
.type(DictKeyCacheDTO.class)
.struct(RedisCacheStruct.HASH)
.timeout(1, TimeUnit.DAYS)
.timeout(8, TimeUnit.HOURS)
.build();
CacheKeyDefine DICT_SCHEMA = new CacheKeyBuilder()
@@ -30,7 +30,7 @@ public interface DictCacheKeyDefine {
.desc("字典配置项 schema ${key}")
.type(JSONObject.class)
.struct(RedisCacheStruct.STRING)
.timeout(1, TimeUnit.DAYS)
.timeout(8, TimeUnit.HOURS)
.build();
CacheKeyDefine DICT_VALUE = new CacheKeyBuilder()
@@ -38,7 +38,7 @@ public interface DictCacheKeyDefine {
.desc("字典配置值 ${key}")
.type(JSONObject.class)
.struct(RedisCacheStruct.STRING)
.timeout(1, TimeUnit.DAYS)
.timeout(8, TimeUnit.HOURS)
.build();
}

View File

@@ -20,7 +20,7 @@ public interface FavoriteCacheKeyDefine {
.desc("收藏信息 ${type} ${userId}")
.type(Long.class)
.struct(RedisCacheStruct.LIST)
.timeout(3, TimeUnit.DAYS)
.timeout(8, TimeUnit.HOURS)
.build();
}

View File

@@ -21,7 +21,7 @@ public interface PreferenceCacheKeyDefine {
.desc("用户偏好 ${userId} ${type}")
.type(Ref.class)
.struct(RedisCacheStruct.HASH)
.timeout(1, TimeUnit.DAYS)
.timeout(8, TimeUnit.HOURS)
.build();
}

View File

@@ -23,6 +23,7 @@ public interface UserCacheKeyDefine {
.desc("用户信息 ${id}")
.type(LoginUser.class)
.struct(RedisCacheStruct.STRING)
.timeout(8, TimeUnit.HOURS)
.build();
CacheKeyDefine USER_LIST = new CacheKeyBuilder()
@@ -30,7 +31,7 @@ public interface UserCacheKeyDefine {
.desc("用户列表")
.type(UserInfoDTO.class)
.struct(RedisCacheStruct.HASH)
.timeout(1, TimeUnit.DAYS)
.timeout(8, TimeUnit.HOURS)
.build();
CacheKeyDefine LOGIN_FAILED_COUNT = new CacheKeyBuilder()
@@ -53,7 +54,7 @@ public interface UserCacheKeyDefine {
.desc("用户刷新 token ${id} ${time}")
.type(LoginTokenDTO.class)
.struct(RedisCacheStruct.STRING)
.timeout(28, TimeUnit.HOURS)
.timeout(32, TimeUnit.HOURS)
.build();
}

View File

@@ -42,6 +42,10 @@ public class DataGroupDO extends BaseDO {
@TableField("type")
private String type;
@Schema(description = "用户id")
@TableField("user_id")
private Long userId;
@Schema(description = "排序")
@TableField("sort")
private Integer sort;

View File

@@ -42,4 +42,8 @@ public class DataGroupRelDO extends BaseDO {
@TableField("type")
private String type;
@Schema(description = "用户id")
@TableField("user_id")
private Long userId;
}

View File

@@ -25,6 +25,15 @@ import java.io.Serializable;
@Schema(name = "DataGroupCreateRequest", description = "数据分组 创建请求对象")
public class DataGroupCreateRequest implements Serializable {
@NotBlank
@Size(max = 16)
@Schema(description = "组类型")
private String type;
@NotNull
@Schema(description = "用户id")
private Long userId;
@NotNull
@Schema(description = "父id")
private Long parentId;
@@ -34,9 +43,4 @@ public class DataGroupCreateRequest implements Serializable {
@Schema(description = "组名称")
private String name;
@NotBlank
@Size(max = 16)
@Schema(description = "组类型")
private String type;
}

Some files were not shown because too many files have changed in this diff Show More