From 59c8a7fd2fe4d2dfd6417ff376692b7a8b6b1407 Mon Sep 17 00:00:00 2001 From: lijiahang Date: Fri, 15 Dec 2023 16:35:11 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=B8=BB=E6=9C=BA=E5=88=97=E8=A1=A8?= =?UTF-8?q?=E8=A7=86=E5=9B=BE.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../entity/vo/AuthorizedHostWrapperVO.java | 3 - .../ops/module/asset/entity/vo/HostVO.java | 3 + .../impl/AssetAuthorizedDataServiceImpl.java | 70 +++++++-- .../ops/module/infra/api/FavoriteApi.java | 10 ++ .../infra/api/impl/FavoriteApiImpl.java | 10 ++ .../service/impl/FavoriteServiceImpl.java | 21 ++- orion-ops-ui/src/api/asset/host.ts | 2 - orion-ops-ui/src/assets/style/global.less | 22 ++- orion-ops-ui/src/hooks/favorite.ts | 11 +- .../host-list/components/host-card-list.vue | 1 - .../asset/host-list/components/host-table.vue | 60 -------- .../new-connection/host-list-view.vue | 31 ++++ .../components/new-connection/host-list.vue | 137 +++++++++++++++--- .../new-connection/new-connection-view.vue | 5 +- 14 files changed, 270 insertions(+), 116 deletions(-) create mode 100644 orion-ops-ui/src/views/host-ops/terminal/components/new-connection/host-list-view.vue diff --git a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/entity/vo/AuthorizedHostWrapperVO.java b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/entity/vo/AuthorizedHostWrapperVO.java index fb3e4355..d7cb3c2f 100644 --- a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/entity/vo/AuthorizedHostWrapperVO.java +++ b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/entity/vo/AuthorizedHostWrapperVO.java @@ -33,7 +33,4 @@ public class AuthorizedHostWrapperVO { @Schema(description = "分组树节点映射 'groupId':hostIdList") private Map> treeNodes; - // TODO 我的收藏 - // TODO 最近连接 - } diff --git a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/entity/vo/HostVO.java b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/entity/vo/HostVO.java index 7facdabb..87cfb745 100644 --- a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/entity/vo/HostVO.java +++ b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/entity/vo/HostVO.java @@ -52,6 +52,9 @@ public class HostVO implements Serializable { @Schema(description = "修改人") private String updater; + @Schema(description = "是否收藏") + private Boolean favorite; + @Schema(description = "tags") private List tags; diff --git a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/service/impl/AssetAuthorizedDataServiceImpl.java b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/service/impl/AssetAuthorizedDataServiceImpl.java index 20074d0f..3f8832ff 100644 --- a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/service/impl/AssetAuthorizedDataServiceImpl.java +++ b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/service/impl/AssetAuthorizedDataServiceImpl.java @@ -12,18 +12,20 @@ import com.orion.ops.module.asset.service.AssetAuthorizedDataService; import com.orion.ops.module.asset.service.HostIdentityService; import com.orion.ops.module.asset.service.HostKeyService; import com.orion.ops.module.asset.service.HostService; -import com.orion.ops.module.infra.api.DataGroupApi; -import com.orion.ops.module.infra.api.DataGroupRelApi; -import com.orion.ops.module.infra.api.DataPermissionApi; -import com.orion.ops.module.infra.api.SystemUserApi; +import com.orion.ops.module.infra.api.*; import com.orion.ops.module.infra.entity.dto.data.DataGroupDTO; +import com.orion.ops.module.infra.entity.dto.tag.TagDTO; import com.orion.ops.module.infra.enums.DataGroupTypeEnum; import com.orion.ops.module.infra.enums.DataPermissionTypeEnum; +import com.orion.ops.module.infra.enums.FavoriteTypeEnum; +import com.orion.ops.module.infra.enums.TagTypeEnum; +import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.util.*; +import java.util.concurrent.Future; import java.util.function.Function; import java.util.stream.Collectors; @@ -59,6 +61,12 @@ public class AssetAuthorizedDataServiceImpl implements AssetAuthorizedDataServic @Resource private HostIdentityService hostIdentityService; + @Resource + private FavoriteApi favoriteApi; + + @Resource + private TagRelApi tagRelApi; + @Override public List getAuthorizedDataRelId(DataPermissionTypeEnum type, AssetAuthorizedDataQueryRequest request) { Long userId = request.getUserId(); @@ -77,7 +85,7 @@ public class AssetAuthorizedDataServiceImpl implements AssetAuthorizedDataServic public AuthorizedHostWrapperVO getUserAuthorizedHostGroup(Long userId) { if (systemUserApi.isAdminUser(userId)) { // 管理员查询所有 - return this.buildUserAuthorizedHostGroup(null); + return this.buildUserAuthorizedHostGroup(userId, null); } else { // 其他用户 查询授权的数据 List authorizedIdList = dataPermissionApi.getUserAuthorizedRelIdList(DataPermissionTypeEnum.HOST_GROUP, userId); @@ -88,7 +96,7 @@ public class AssetAuthorizedDataServiceImpl implements AssetAuthorizedDataServic .hostList(Lists.empty()) .build(); } - return this.buildUserAuthorizedHostGroup(authorizedIdList); + return this.buildUserAuthorizedHostGroup(userId, authorizedIdList); } } @@ -139,14 +147,16 @@ public class AssetAuthorizedDataServiceImpl implements AssetAuthorizedDataServic /** * 构建授权的主机分组树 * + * @param userId userId * @param authorizedGroupIdList authorizedGroupIdList * @return tree */ - private AuthorizedHostWrapperVO buildUserAuthorizedHostGroup(List authorizedGroupIdList) { + @SneakyThrows + private AuthorizedHostWrapperVO buildUserAuthorizedHostGroup(Long userId, List authorizedGroupIdList) { final boolean allData = Lists.isEmpty(authorizedGroupIdList); AuthorizedHostWrapperVO wrapper = new AuthorizedHostWrapperVO(); - // TODO async get 最近连接 - // TODO async get 我的收藏 + // 查询我的收藏 + Future> favoriteResult = favoriteApi.getFavoriteRelIdListAsync(FavoriteTypeEnum.HOST, userId); // 查询分组 List dataGroup = dataGroupApi.getDataGroupList(DataGroupTypeEnum.HOST); // 查询分组引用 @@ -161,12 +171,17 @@ public class AssetAuthorizedDataServiceImpl implements AssetAuthorizedDataServic // 设置主机分组树 wrapper.setGroupTree(this.getAuthorizedHostGroupTree(dataGroup)); // 设置主机分组下的主机 - wrapper.setTreeNodes(this.getAuthorizedHostGroupNodes(allData, dataGroup, dataGroupRel, authorizedGroupIdList)); + wrapper.setTreeNodes(this.getAuthorizedHostGroupNodes(allData, + dataGroup, + dataGroupRel, + authorizedGroupIdList)); // 设置已授权的所有主机 - wrapper.setHostList(this.getAuthorizedHostList(allData, dataGroup, dataGroupRel, authorizedGroupIdList)); - // TODO set 最近连接 - // TODO set 我的收藏 - + wrapper.setHostList(this.getAuthorizedHostList(allData, + dataGroup, + dataGroupRel, + authorizedGroupIdList)); + // 设置主机拓展信息 + this.getAuthorizedHostExtra(wrapper.getHostList(), favoriteResult.get()); return wrapper; } @@ -223,6 +238,7 @@ public class AssetAuthorizedDataServiceImpl implements AssetAuthorizedDataServic List authorizedGroupIdList) { // 查询主机列表 List hosts = hostService.getHostListByCache(); + // 全部数据直接返回 if (allData) { return hosts; @@ -232,6 +248,7 @@ public class AssetAuthorizedDataServiceImpl implements AssetAuthorizedDataServic // 仅设置已授权的数据 return dataGroup.stream() .map(DataGroupDTO::getId) + // 因为可能父菜单没有授权 这里需要判断分组权限 .filter(authorizedGroupIdList::contains) .map(dataGroupRel::get) .filter(Lists::isNoneEmpty) @@ -241,4 +258,29 @@ public class AssetAuthorizedDataServiceImpl implements AssetAuthorizedDataServic .collect(Collectors.toList()); } + /** + * 设置授权主机的额外参数 + * + * @param hosts hosts + * @param favorite favorite + */ + private void getAuthorizedHostExtra(List hosts, + List favorite) { + if (Lists.isEmpty(hosts)) { + return; + } + // 设置收藏结果 + if (!Lists.isEmpty(favorite)) { + hosts.forEach(s -> s.setFavorite(favorite.contains(s.getId()))); + } + List hostIdList = hosts.stream() + .map(HostVO::getId) + .collect(Collectors.toList()); + // 查询 tag 信息 + List> tags = tagRelApi.getRelTags(TagTypeEnum.HOST, hostIdList); + for (int i = 0; i < hosts.size(); i++) { + hosts.get(i).setTags(tags.get(i)); + } + } + } diff --git a/orion-ops-module-infra/orion-ops-module-infra-provider/src/main/java/com/orion/ops/module/infra/api/FavoriteApi.java b/orion-ops-module-infra/orion-ops-module-infra-provider/src/main/java/com/orion/ops/module/infra/api/FavoriteApi.java index 11ad9367..12aec3b3 100644 --- a/orion-ops-module-infra/orion-ops-module-infra-provider/src/main/java/com/orion/ops/module/infra/api/FavoriteApi.java +++ b/orion-ops-module-infra/orion-ops-module-infra-provider/src/main/java/com/orion/ops/module/infra/api/FavoriteApi.java @@ -3,6 +3,7 @@ package com.orion.ops.module.infra.api; import com.orion.ops.module.infra.enums.FavoriteTypeEnum; import java.util.List; +import java.util.concurrent.Future; /** * 收藏 对外服务类 @@ -22,6 +23,15 @@ public interface FavoriteApi { */ List getFavoriteRelIdList(FavoriteTypeEnum type, Long userId); + /** + * 异步查询用户收藏 + * + * @param type type + * @param userId userId + * @return relIdList + */ + Future> getFavoriteRelIdListAsync(FavoriteTypeEnum type, java.lang.Long userId); + /** * 通过 relId 删除收藏 * diff --git a/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/api/impl/FavoriteApiImpl.java b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/api/impl/FavoriteApiImpl.java index b7cfe283..2c6a3fa9 100644 --- a/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/api/impl/FavoriteApiImpl.java +++ b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/api/impl/FavoriteApiImpl.java @@ -11,6 +11,8 @@ import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Future; /** * 收藏 对外服务实现类 @@ -38,6 +40,14 @@ public class FavoriteApiImpl implements FavoriteApi { return favoriteService.getFavoriteRelIdList(request); } + @Override + @Async("asyncExecutor") + public Future> getFavoriteRelIdListAsync(FavoriteTypeEnum type, Long userId) { + Valid.allNotNull(type, userId); + // 查询 + return CompletableFuture.completedFuture(this.getFavoriteRelIdList(type, userId)); + } + @Override public void deleteByRelId(FavoriteTypeEnum type, Long relId) { Valid.allNotNull(type, relId); diff --git a/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/service/impl/FavoriteServiceImpl.java b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/service/impl/FavoriteServiceImpl.java index a24f1f86..659384c2 100644 --- a/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/service/impl/FavoriteServiceImpl.java +++ b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/service/impl/FavoriteServiceImpl.java @@ -46,16 +46,24 @@ public class FavoriteServiceImpl implements FavoriteService { public Long addFavorite(FavoriteOperatorRequest request) { String type = Valid.valid(FavoriteTypeEnum::of, request.getType()).name(); Long userId = SecurityUtils.getLoginUserId(); + // 检查是否存在 + FavoriteDO check = favoriteDAO.of() + .createWrapper() + .eq(FavoriteDO::getUserId, userId) + .eq(FavoriteDO::getType, request.getType()) + .eq(FavoriteDO::getRelId, request.getRelId()) + .then() + .getOne(); + if (check != null) { + return check.getId(); + } // 转换 FavoriteDO record = FavoriteConvert.MAPPER.to(request); record.setUserId(userId); // 插入 int effect = favoriteDAO.insert(record); - // 设置缓存 - String key = FavoriteCacheKeyDefine.FAVORITE.format(type, userId); - RedisLists.push(key, request.getRelId(), String::valueOf); - // 设置过期时间 - RedisLists.setExpire(key, FavoriteCacheKeyDefine.FAVORITE); + // 删除缓存 + RedisLists.delete(FavoriteCacheKeyDefine.FAVORITE.format(type, userId)); return record.getId(); } @@ -67,8 +75,7 @@ public class FavoriteServiceImpl implements FavoriteService { // 删除库 int effect = favoriteDAO.deleteFavorite(type, userId, relId); // 删除缓存 - String key = FavoriteCacheKeyDefine.FAVORITE.format(type, userId); - redisTemplate.opsForList().remove(key, 1, relId.toString()); + RedisLists.delete(FavoriteCacheKeyDefine.FAVORITE.format(type, userId)); return effect; } diff --git a/orion-ops-ui/src/api/asset/host.ts b/orion-ops-ui/src/api/asset/host.ts index 32042626..d30deb9f 100644 --- a/orion-ops-ui/src/api/asset/host.ts +++ b/orion-ops-ui/src/api/asset/host.ts @@ -29,7 +29,6 @@ export interface HostQueryRequest extends Pagination { name?: string; code?: string; address?: string; - favorite?: boolean; tags?: Array; queryTag?: boolean; } @@ -46,7 +45,6 @@ export interface HostQueryResponse extends TableData { updateTime: number; creator: string; updater: string; - // FIXME 删除 favorite: boolean; tags: Array<{ id: number, name: string }>; groupIdList: Array; diff --git a/orion-ops-ui/src/assets/style/global.less b/orion-ops-ui/src/assets/style/global.less index 4ac86c3b..b47c67c3 100644 --- a/orion-ops-ui/src/assets/style/global.less +++ b/orion-ops-ui/src/assets/style/global.less @@ -94,6 +94,10 @@ body { border-color: rgb(var(--gray-2)); } +.disabled { + pointer-events: none; +} + .usn { user-select: none; } @@ -106,16 +110,28 @@ body { cursor: pointer; } +.m0 { + margin: 0; +} + .mx0 { - margin: 0 0; + margin-left: 0; + margin-right: 0; +} + +.my0 { + margin-top: 0; + margin-bottom: 0; } .mx2 { - margin: 0 2px; + margin-left: 2px; + margin-right: 2px; } .mx4 { - margin: 0 4px; + margin-left: 4px; + margin-right: 4px; } .ml4 { diff --git a/orion-ops-ui/src/hooks/favorite.ts b/orion-ops-ui/src/hooks/favorite.ts index 0c3bf747..6ea2955c 100644 --- a/orion-ops-ui/src/hooks/favorite.ts +++ b/orion-ops-ui/src/hooks/favorite.ts @@ -1,30 +1,29 @@ import type { FavoriteType } from '@/api/meta/favorite'; -import { Message } from '@arco-design/web-vue'; import { addFavorite, cancelFavorite } from '@/api/meta/favorite'; +import { ref } from 'vue'; export default function useFavorite(type: FavoriteType) { + const loading = ref(false); const toggle = async (record: any, id: number, cancelField = 'favorite') => { const request = { relId: id, type }; - Message.clear(); - const loading = Message.loading(record[cancelField] ? '取消中' : '收藏中'); try { + loading.value = true; if (record[cancelField]) { // 取消收藏 await cancelFavorite(request); record[cancelField] = false; - Message.success('已取消'); } else { // 添加收藏 await addFavorite(request); record[cancelField] = true; - Message.success('已收藏'); } } catch (e) { } finally { - loading.close(); + loading.value = false; } }; return { + loading, toggle }; } diff --git a/orion-ops-ui/src/views/asset/host-list/components/host-card-list.vue b/orion-ops-ui/src/views/asset/host-list/components/host-card-list.vue index 454f372b..c09a46a2 100644 --- a/orion-ops-ui/src/views/asset/host-list/components/host-card-list.vue +++ b/orion-ops-ui/src/views/asset/host-list/components/host-card-list.vue @@ -199,7 +199,6 @@ name: undefined, code: undefined, address: undefined, - favorite: undefined, tags: undefined, queryTag: true }); diff --git a/orion-ops-ui/src/views/asset/host-list/components/host-table.vue b/orion-ops-ui/src/views/asset/host-list/components/host-table.vue index db1f4029..1363a720 100644 --- a/orion-ops-ui/src/views/asset/host-list/components/host-table.vue +++ b/orion-ops-ui/src/views/asset/host-list/components/host-table.vue @@ -49,18 +49,6 @@
- - - - - - - - - - -
@@ -197,7 +172,6 @@ import { tagColor } from '../types/const'; import { usePagination } from '@/types/table'; import useCopy from '@/hooks/copy'; - import useFavorite from '@/hooks/favorite'; import { dataColor } from '@/utils'; import TagMultiSelector from '@/components/meta/tag/tag-multi-selector.vue'; import { GrantKey, GrantRouteName } from '@/views/asset/grant/types/const'; @@ -208,7 +182,6 @@ const emits = defineEmits(['openAdd', 'openUpdate', 'openUpdateConfig', 'openHostGroup']); const { copy } = useCopy(); - const { toggle: toggleFavorite } = useFavorite('HOST'); const pagination = usePagination(); @@ -217,7 +190,6 @@ name: undefined, code: undefined, address: undefined, - favorite: undefined, tags: undefined, queryTag: true }); @@ -284,37 +256,5 @@ .row-handle-wrapper { display: flex; align-items: center; - - .host-favorite { - cursor: pointer; - line-height: 19px; - font-size: 19px; - margin: 0 4px; - } - - .host-favorite-choice { - color: rgb(var(--yellow-6)); - - :hover { - transition: .2s; - color: rgba(232, 203, 12, .9); - } - } - - .host-favorite-un-choice { - color: rgb(var(--gray-6)); - - :hover { - transition: .2s; - color: rgb(var(--yellow-6)); - } - } } - - .only-favorite { - user-select: none; - color: rgb(var(--arcoblue-5)); - background: rgb(var(--gray-2)); - } - diff --git a/orion-ops-ui/src/views/host-ops/terminal/components/new-connection/host-list-view.vue b/orion-ops-ui/src/views/host-ops/terminal/components/new-connection/host-list-view.vue new file mode 100644 index 00000000..49607fab --- /dev/null +++ b/orion-ops-ui/src/views/host-ops/terminal/components/new-connection/host-list-view.vue @@ -0,0 +1,31 @@ + + + + + + + diff --git a/orion-ops-ui/src/views/host-ops/terminal/components/new-connection/host-list.vue b/orion-ops-ui/src/views/host-ops/terminal/components/new-connection/host-list.vue index f17a1cc5..5b1abb84 100644 --- a/orion-ops-ui/src/views/host-ops/terminal/components/new-connection/host-list.vue +++ b/orion-ops-ui/src/views/host-ops/terminal/components/new-connection/host-list.vue @@ -1,5 +1,6 @@