🔨 命令分组替换为通用分组模型.

This commit is contained in:
lijiahang
2024-04-23 19:10:15 +08:00
parent f146989a1a
commit e04e14a6e4
11 changed files with 92 additions and 184 deletions

View File

@@ -11,6 +11,7 @@
* 🩰 修改 命令执行日志 UI 修改 * 🩰 修改 命令执行日志 UI 修改
* 🌈 新增 命令执行模板配置默认主机 * 🌈 新增 命令执行模板配置默认主机
* 🌈 新增 主机终端书签路径 * 🌈 新增 主机终端书签路径
* 🔨 优化 通用分组模型添加 `userId`
* 🔨 优化 退出登录不重定向 * 🔨 优化 退出登录不重定向
* 🔨 优化 动态设置页面标题 * 🔨 优化 动态设置页面标题
* 🔨 优化 终端断开后回车重连 * 🔨 优化 终端断开后回车重连

View File

@@ -3,9 +3,41 @@
> sql 脚本 - DDL > sql 脚本 - DDL
```sql ```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;
``` ```
> sql 脚本 - DML > sql 脚本 - DML
```sql ```sql
-- 设置数据分组 user_id
UPDATE data_group SET user_id = 0;
UPDATE data_group_rel SET user_id = 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 command_snippet_group;
``` ```

View File

@@ -1,10 +1,12 @@
package com.orion.ops.module.asset.convert; package com.orion.ops.module.asset.convert;
import com.orion.ops.module.asset.entity.domain.CommandSnippetGroupDO; 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.CommandSnippetGroupCreateRequest;
import com.orion.ops.module.asset.entity.request.command.CommandSnippetGroupUpdateRequest; 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.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.Mapper;
import org.mapstruct.factory.Mappers; import org.mapstruct.factory.Mappers;
@@ -22,16 +24,12 @@ public interface CommandSnippetGroupConvert {
CommandSnippetGroupConvert MAPPER = Mappers.getMapper(CommandSnippetGroupConvert.class); 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); CommandSnippetGroupVO to(DataGroupDTO domain);
List<CommandSnippetGroupVO> to(List<CommandSnippetGroupDO> list); List<CommandSnippetGroupVO> to(List<CommandSnippetGroupDO> list);
CommandSnippetGroupVO to(CommandSnippetGroupCacheDTO cache);
CommandSnippetGroupCacheDTO toCache(CommandSnippetGroupDO 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

@@ -25,12 +25,4 @@ public interface CommandSnippetCacheKeyDefine {
.timeout(8, TimeUnit.HOURS) .timeout(8, TimeUnit.HOURS)
.build(); .build();
CacheKeyDefine SNIPPET_GROUP = new CacheKeyBuilder()
.key("command:snippet:group:{}")
.desc("命令片段分组 ${userId}")
.type(CommandSnippetGroupCacheDTO.class)
.struct(RedisCacheStruct.HASH)
.timeout(8, TimeUnit.HOURS)
.build();
} }

View File

@@ -1,34 +0,0 @@
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.0
* @since 2024-1-24 12:28
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Schema(name = "CommandSnippetGroupCacheDTO", description = "命令片段分组 缓存对象")
public class CommandSnippetGroupCacheDTO implements LongCacheIdModel, Serializable {
private static final long serialVersionUID = 1L;
@Schema(description = "id")
private Long id;
@Schema(description = "分组名称")
private String name;
}

View File

@@ -1,24 +1,24 @@
package com.orion.ops.module.asset.service.impl; package com.orion.ops.module.asset.service.impl;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.orion.lang.utils.Booleans; 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.constant.ErrorMessage;
import com.orion.ops.framework.common.utils.Valid; 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.framework.security.core.utils.SecurityUtils;
import com.orion.ops.module.asset.convert.CommandSnippetGroupConvert; 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.entity.request.command.CommandSnippetGroupCreateRequest; 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.CommandSnippetGroupDeleteRequest;
import com.orion.ops.module.asset.entity.request.command.CommandSnippetGroupUpdateRequest; 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.entity.vo.CommandSnippetGroupVO;
import com.orion.ops.module.asset.service.CommandSnippetGroupService; import com.orion.ops.module.asset.service.CommandSnippetGroupService;
import com.orion.ops.module.asset.service.CommandSnippetService; 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 lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@@ -40,7 +40,10 @@ import java.util.stream.Collectors;
public class CommandSnippetGroupServiceImpl implements CommandSnippetGroupService { public class CommandSnippetGroupServiceImpl implements CommandSnippetGroupService {
@Resource @Resource
private CommandSnippetGroupDAO commandSnippetGroupDAO; private DataGroupApi dataGroupApi;
@Resource
private DataGroupUserApi dataGroupUserApi;
@Resource @Resource
private CommandSnippetService commandSnippetService; private CommandSnippetService commandSnippetService;
@@ -49,81 +52,40 @@ public class CommandSnippetGroupServiceImpl implements CommandSnippetGroupServic
public Long createCommandSnippetGroup(CommandSnippetGroupCreateRequest request) { public Long createCommandSnippetGroup(CommandSnippetGroupCreateRequest request) {
Long userId = SecurityUtils.getLoginUserId(); Long userId = SecurityUtils.getLoginUserId();
log.info("CommandSnippetGroupService-createCommandSnippetGroup request: {}", JSON.toJSONString(request)); log.info("CommandSnippetGroupService-createCommandSnippetGroup request: {}", JSON.toJSONString(request));
// 转换 // 创建
CommandSnippetGroupDO record = CommandSnippetGroupConvert.MAPPER.to(request); DataGroupCreateDTO create = CommandSnippetGroupConvert.MAPPER.to(request);
record.setUserId(userId); create.setParentId(Const.ROOT_PARENT_ID);
// 查询数据是否冲突 return dataGroupUserApi.createDataGroup(DataGroupTypeEnum.COMMAND_SNIPPET, userId, create);
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;
} }
@Override @Override
public Integer updateCommandSnippetGroupById(CommandSnippetGroupUpdateRequest request) { public Integer updateCommandSnippetGroupById(CommandSnippetGroupUpdateRequest request) {
Long id = Valid.notNull(request.getId(), ErrorMessage.ID_MISSING); Long id = Valid.notNull(request.getId(), ErrorMessage.ID_MISSING);
log.info("CommandSnippetGroupService-updateCommandSnippetGroupById id: {}, request: {}", id, JSON.toJSONString(request)); log.info("CommandSnippetGroupService-updateCommandSnippetGroupById id: {}, request: {}", id, JSON.toJSONString(request));
// 查询 // 重命名
CommandSnippetGroupDO record = commandSnippetGroupDAO.selectById(id); DataGroupRenameDTO rename = CommandSnippetGroupConvert.MAPPER.to(request);
Valid.notNull(record, ErrorMessage.DATA_ABSENT); return dataGroupApi.renameDataGroup(rename);
// 转换
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;
} }
@Override @Override
public List<CommandSnippetGroupVO> getCommandSnippetGroupList() { public List<CommandSnippetGroupVO> getCommandSnippetGroupList() {
Long userId = SecurityUtils.getLoginUserId(); Long userId = SecurityUtils.getLoginUserId();
// 查询缓存 // 查询分组
String cacheKey = CommandSnippetCacheKeyDefine.SNIPPET_GROUP.format(userId); return dataGroupUserApi.getDataGroupList(DataGroupTypeEnum.COMMAND_SNIPPET, userId)
List<CommandSnippetGroupCacheDTO> list = RedisMaps.valuesJson(cacheKey, CommandSnippetCacheKeyDefine.SNIPPET_GROUP); .stream()
if (list.isEmpty()) { .sorted(Comparator.comparing(DataGroupDTO::getSort))
// 查询数据库
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()
.map(CommandSnippetGroupConvert.MAPPER::to) .map(CommandSnippetGroupConvert.MAPPER::to)
.sorted(Comparator.comparing(CommandSnippetGroupVO::getId))
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public Integer deleteCommandSnippetGroup(CommandSnippetGroupDeleteRequest request) { public Integer deleteCommandSnippetGroup(CommandSnippetGroupDeleteRequest request) {
Long userId = SecurityUtils.getLoginUserId();
Long id = request.getId(); Long id = request.getId();
log.info("CommandSnippetGroupService-deleteCommandSnippetGroupById id: {}", id); log.info("CommandSnippetGroupService-deleteCommandSnippetGroupById id: {}", id);
// 检查数据是否存在 // 删除分组
CommandSnippetGroupDO record = commandSnippetGroupDAO.selectById(id); Integer effect = dataGroupApi.deleteDataGroupById(id);
Valid.notNull(record, ErrorMessage.DATA_ABSENT);
Long userId = record.getUserId();
// 删除
int effect = commandSnippetGroupDAO.deleteById(id);
log.info("CommandSnippetGroupService-deleteCommandSnippetGroupById id: {}, effect: {}", id, effect);
if (Booleans.isTrue(request.getDeleteItem())) { if (Booleans.isTrue(request.getDeleteItem())) {
// 删除组内数据 // 删除组内数据
commandSnippetService.deleteByGroupId(userId, id); commandSnippetService.deleteByGroupId(userId, id);
@@ -131,28 +93,7 @@ public class CommandSnippetGroupServiceImpl implements CommandSnippetGroupServic
// 移动到根节点 // 移动到根节点
commandSnippetService.setGroupNull(userId, id); commandSnippetService.setGroupNull(userId, id);
} }
// 删除缓存
String cacheKey = CommandSnippetCacheKeyDefine.SNIPPET_GROUP.format(userId);
RedisMaps.delete(cacheKey, id);
return effect; 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);
}
} }

View File

@@ -1,22 +0,0 @@
<?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">
<!-- 通用查询映射结果 -->
<resultMap id="BaseResultMap" type="com.orion.ops.module.asset.entity.domain.CommandSnippetGroupDO">
<id column="id" property="id"/>
<result column="user_id" property="userId"/>
<result column="name" property="name"/>
<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, user_id, name, create_time, update_time, creator, updater, deleted
</sql>
</mapper>

View File

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

View File

@@ -173,10 +173,13 @@ public class DataGroupRelServiceImpl implements DataGroupRelService {
.relId(s.getRelId()) .relId(s.getRelId())
.build())); .build()));
}); });
// 插入 // 不为空则插入
dataGroupRelDAO.insertBatch(records); if (!records.isEmpty()) {
// 删除缓存 // 插入
this.deleteCache(type, userId, groupMapping.keySet()); dataGroupRelDAO.insertBatch(records);
// 删除缓存
this.deleteCache(type, userId, groupMapping.keySet());
}
} }
@Override @Override

View File

@@ -14,12 +14,21 @@
<div class="snippet-container"> <div class="snippet-container">
<!-- 命令头部 --> <!-- 命令头部 -->
<div class="snippet-header"> <div class="snippet-header">
<!-- 创建命令 --> <!-- 左侧按钮 -->
<span class="click-icon-wrapper snippet-header-icon" <a-space size="small">
title="创建命令" <!-- 创建命令 -->
@click="openAdd"> <span class="click-icon-wrapper snippet-header-icon"
title="创建命令"
@click="openAdd">
<icon-plus /> <icon-plus />
</span> </span>
<!-- 刷新 -->
<span class="click-icon-wrapper snippet-header-icon"
title="刷新"
@click="fetchData(true)">
<icon-refresh />
</span>
</a-space>
<!-- 搜索框 --> <!-- 搜索框 -->
<a-input-search class="snippet-header-input" <a-input-search class="snippet-header-input"
v-model="filterValue" v-model="filterValue"
@@ -100,8 +109,8 @@
defineExpose({ open }); defineExpose({ open });
// 加载数据 // 加载数据
const fetchData = async () => { const fetchData = async (force: boolean = false) => {
if (snippet.value) { if (snippet.value && !force) {
return; return;
} }
setLoading(true); setLoading(true);
@@ -298,7 +307,7 @@
} }
&-input { &-input {
width: 220px; width: 248px;
user-select: none; user-select: none;
} }
} }