fix 资产管理支持批量分配到工作空间

This commit is contained in:
bwcx_jzy 2023-03-19 22:52:44 +08:00
parent 0d095a9291
commit 5d3be5c131
No known key found for this signature in database
GPG Key ID: 5E48E9372088B9E5
9 changed files with 174 additions and 100 deletions

View File

@ -20,6 +20,7 @@
7. 【server】优化 缓存管理页面支持查看运行中的线程同步器、正在构建的ID
8. 【server】优化 SSH 脚本批量执行采用线程同步器执行(避免线程数大于 CPU 核心数)
9. 【server】优化 构建 SSH 发布命令响应方式调整为逐行(避免长时间没有任何信息输出)
10. 【server】优化 资产管理支持批量分配到工作空间
### ⚠️ 注意

View File

@ -307,41 +307,46 @@ public class MachineDockerController extends BaseGroupNameController {
/**
* docker 分配到指定工作空间
*
* @param id docker id
* @param ids docker id
* @param workspaceId 工作空间id
* @return json
*/
@PostMapping(value = "distribute", produces = MediaType.APPLICATION_JSON_VALUE)
@Feature(method = MethodFeature.EDIT)
public JsonMessage<String> distribute(@ValidatorItem String id, @ValidatorItem String workspaceId, String type) {
MachineDockerModel machineDockerModel = machineDockerServer.getByKey(id);
Assert.notNull(machineDockerModel, "没有对应的 docker");
boolean exists = workspaceService.exists(new WorkspaceModel(workspaceId));
Assert.state(exists, "不存在对应的工作空间");
if (StrUtil.equals(type, "docker")) {
DockerInfoModel dockerInfoModel = new DockerInfoModel();
dockerInfoModel.setMachineDockerId(id);
dockerInfoModel.setWorkspaceId(workspaceId);
//
exists = dockerInfoService.exists(dockerInfoModel);
Assert.state(!exists, "对应工作空间已经存在此 docker 啦");
//
dockerInfoModel.setName(machineDockerModel.getName());
dockerInfoService.insert(dockerInfoModel);
} else if (StrUtil.equals(type, "swarm")) {
Assert.hasText(machineDockerModel.getSwarmId(), "当前 docker 不在集群中");
DockerSwarmInfoMode dockerInfoModel = new DockerSwarmInfoMode();
dockerInfoModel.setSwarmId(machineDockerModel.getSwarmId());
dockerInfoModel.setWorkspaceId(workspaceId);
//
exists = dockerSwarmInfoService.exists(dockerInfoModel);
Assert.state(!exists, "对应工作空间已经存在此 docker 集群啦");
//
dockerInfoModel.setName(machineDockerModel.getName());
dockerSwarmInfoService.insert(dockerInfoModel);
} else {
throw new IllegalArgumentException("未知参数");
public JsonMessage<String> distribute(@ValidatorItem String ids, @ValidatorItem String workspaceId, String type) {
List<String> list = StrUtil.splitTrim(ids, StrUtil.COMMA);
for (String id : list) {
MachineDockerModel machineDockerModel = machineDockerServer.getByKey(id);
Assert.notNull(machineDockerModel, "没有对应的 docker");
boolean exists = workspaceService.exists(new WorkspaceModel(workspaceId));
Assert.state(exists, "不存在对应的工作空间");
if (StrUtil.equals(type, "docker")) {
DockerInfoModel dockerInfoModel = new DockerInfoModel();
dockerInfoModel.setMachineDockerId(id);
dockerInfoModel.setWorkspaceId(workspaceId);
//
exists = dockerInfoService.exists(dockerInfoModel);
if (!exists) {
//
dockerInfoModel.setName(machineDockerModel.getName());
dockerInfoService.insert(dockerInfoModel);
}
} else if (StrUtil.equals(type, "swarm")) {
Assert.hasText(machineDockerModel.getSwarmId(), () -> "当前 docker " + machineDockerModel.getName() + " 不在集群中");
DockerSwarmInfoMode dockerInfoModel = new DockerSwarmInfoMode();
dockerInfoModel.setSwarmId(machineDockerModel.getSwarmId());
dockerInfoModel.setWorkspaceId(workspaceId);
//
if (!dockerSwarmInfoService.exists(dockerInfoModel)) {
//
dockerInfoModel.setName(machineDockerModel.getName());
dockerSwarmInfoService.insert(dockerInfoModel);
}
} else {
throw new IllegalArgumentException("未知参数");
}
}
return JsonMessage.success("操作成功");
}

View File

@ -94,22 +94,27 @@ public class MachineNodeController extends BaseGroupNameController {
/**
* 将机器分配到指定工作空间
*
* @param id 机器id
* @param ids 机器id
* @param workspaceId 工作空间id
* @return json
*/
@PostMapping(value = "distribute", produces = MediaType.APPLICATION_JSON_VALUE)
@Feature(method = MethodFeature.EDIT)
public JsonMessage<String> distribute(@ValidatorItem String id, @ValidatorItem String workspaceId) {
MachineNodeModel machineNodeModel = machineNodeServer.getByKey(id);
Assert.notNull(machineNodeModel, "没有对应的机器");
WorkspaceModel workspaceModel = new WorkspaceModel(workspaceId);
boolean exists = workspaceService.exists(workspaceModel);
Assert.state(exists, "不存在对应的工作空间");
//
nodeService.existsNode(workspaceId, id);
//
machineNodeServer.insertNode(machineNodeModel, workspaceId);
public JsonMessage<String> distribute(@ValidatorItem String ids, @ValidatorItem String workspaceId) {
List<String> list = StrUtil.splitTrim(ids, StrUtil.COMMA);
for (String id : list) {
MachineNodeModel machineNodeModel = machineNodeServer.getByKey(id);
Assert.notNull(machineNodeModel, "没有对应的机器");
WorkspaceModel workspaceModel = new WorkspaceModel(workspaceId);
boolean exists = workspaceService.exists(workspaceModel);
Assert.state(exists, "不存在对应的工作空间");
//
if (!nodeService.existsNode2(workspaceId, id)) {
//
machineNodeServer.insertNode(machineNodeModel, workspaceId);
}
}
return JsonMessage.success("操作成功");
}

View File

@ -253,10 +253,10 @@ public class MachineSshController extends BaseGroupNameController {
@PostMapping(value = "save-workspace-config", produces = MediaType.APPLICATION_JSON_VALUE)
@Feature(method = MethodFeature.EDIT)
public JsonMessage<String> saveWorkspaceConfig(
String fileDirs,
@ValidatorItem String id,
String notAllowedCommand,
String allowEditSuffix) {
String fileDirs,
@ValidatorItem String id,
String notAllowedCommand,
String allowEditSuffix) {
SshModel sshModel = new SshModel(id);
// 目录
if (StrUtil.isEmpty(fileDirs)) {
@ -282,23 +282,28 @@ public class MachineSshController extends BaseGroupNameController {
}
/**
* 机器分配到指定工作空间
* ssh 分配到指定工作空间
*
* @param id 机器id
* @param ids ssh id
* @param workspaceId 工作空间id
* @return json
*/
@PostMapping(value = "distribute", produces = MediaType.APPLICATION_JSON_VALUE)
@Feature(method = MethodFeature.EDIT)
public JsonMessage<String> distribute(@ValidatorItem String id, @ValidatorItem String workspaceId) {
MachineSshModel machineSshModel = machineSshServer.getByKey(id);
Assert.notNull(machineSshModel, "没有对应的ssh");
boolean exists = workspaceService.exists(new WorkspaceModel(workspaceId));
Assert.state(exists, "不存在对应的工作空间");
//
sshService.existsSsh(workspaceId, id);
//
sshService.insert(machineSshModel, workspaceId);
public JsonMessage<String> distribute(@ValidatorItem String ids, @ValidatorItem String workspaceId) {
List<String> list = StrUtil.splitTrim(ids, StrUtil.COMMA);
for (String id : list) {
MachineSshModel machineSshModel = machineSshServer.getByKey(id);
Assert.notNull(machineSshModel, "没有对应的ssh");
boolean exists = workspaceService.exists(new WorkspaceModel(workspaceId));
Assert.state(exists, "不存在对应的工作空间");
//
if (!sshService.existsSsh2(workspaceId, id)) {
//
sshService.insert(machineSshModel, workspaceId);
}
}
return JsonMessage.success("操作成功");
}
@ -336,7 +341,7 @@ public class MachineSshController extends BaseGroupNameController {
private void setHeader(HttpServletResponse response, String fileName) {
String contentType = ObjectUtil.defaultIfNull(FileUtil.getMimeType(fileName), "application/octet-stream");
response.setHeader("Content-Disposition", StrUtil.format("attachment;filename=\"{}\"",
URLUtil.encode(fileName, CharsetUtil.CHARSET_UTF_8)));
URLUtil.encode(fileName, CharsetUtil.CHARSET_UTF_8)));
response.setContentType(contentType);
}
@ -363,21 +368,21 @@ public class MachineSshController extends BaseGroupNameController {
break;
}
listPage.getResult()
.stream()
.map((Function<MachineSshModel, List<Object>>) machineSshModel -> CollUtil.newArrayList(
machineSshModel.getName(),
machineSshModel.getGroupName(),
machineSshModel.getHost(),
machineSshModel.getPort(),
machineSshModel.getUser(),
machineSshModel.getPassword(),
machineSshModel.getCharset(),
machineSshModel.getConnectType(),
machineSshModel.getPrivateKey(),
machineSshModel.getTimeout()
))
.map(objects -> objects.stream().map(StrUtil::toStringOrNull).toArray(String[]::new))
.forEach(writer::writeLine);
.stream()
.map((Function<MachineSshModel, List<Object>>) machineSshModel -> CollUtil.newArrayList(
machineSshModel.getName(),
machineSshModel.getGroupName(),
machineSshModel.getHost(),
machineSshModel.getPort(),
machineSshModel.getUser(),
machineSshModel.getPassword(),
machineSshModel.getCharset(),
machineSshModel.getConnectType(),
machineSshModel.getPrivateKey(),
machineSshModel.getTimeout()
))
.map(objects -> objects.stream().map(StrUtil::toStringOrNull).toArray(String[]::new))
.forEach(writer::writeLine);
if (ObjectUtil.equal(listPage.getPage(), listPage.getTotalPage())) {
// 最后一页
break;

View File

@ -268,6 +268,14 @@ public class SshService extends BaseGroupService<SshModel> implements Logger {
Assert.isNull(data, () -> "对应工作空间已经存在该 ssh 啦:" + data.getName());
}
public boolean existsSsh2(String workspaceId, String machineSshId) {
//
SshModel where = new SshModel();
where.setWorkspaceId(workspaceId);
where.setMachineSshId(machineSshId);
return this.exists(where);
}
public void insert(MachineSshModel machineSshModel, String workspaceId) {
SshModel data = new SshModel();
data.setWorkspaceId(workspaceId);

View File

@ -7,11 +7,11 @@
<a-row v-for="item in myWorkspaceList" :key="item.id" class="item-row">
<a-col :span="18">
<template v-if="item.edit">
<a-input-search placeholder="请输入工作空间备注" enter-button="确定" v-model="item.name" @search="editOk(item)" />
<a-input-search placeholder="请输入工作空间备注,留空使用默认的名称" enter-button="确定" v-model="item.name" @search="editOk(item)" />
</template>
<template v-else>
<a-tooltip :title="`原始名:${item.originalName}`">
{{ item.name }}
{{ item.name || item.originalName }}
</a-tooltip>
</template>
</a-col>
@ -23,16 +23,23 @@
</a-space>
</a-col>
</a-row>
<!-- <a-list-item slot="renderItem" slot-scope="item">
<template>
</template>
</a-list-item>
</a-list> -->
</draggable>
<a-col style="margin-top: 10px">
<a-button type="primary" @click="save"> 保存 </a-button>
<a-space>
<a-button type="primary" @click="save"> 保存 </a-button>
<a-button
type="primary"
@click="
() => {
myWorkspaceList = myWorkspaceList.map((item) => {
return { ...item, name: '' };
});
}
"
>
恢复默认名称
</a-button>
</a-space>
</a-col>
</a-card>
</a-col>

View File

@ -1,7 +1,7 @@
<template>
<div class="full-content">
<!-- 数据表格 -->
<a-table size="middle" :data-source="list" :columns="columns" @change="changePage" :pagination="pagination" bordered rowKey="id">
<a-table size="middle" :data-source="list" :columns="columns" @change="changePage" :pagination="pagination" bordered rowKey="id" :row-selection="rowSelection">
<template slot="title">
<a-space>
<a-input v-model="listQuery['%name%']" @pressEnter="loadData" placeholder="名称" class="search-input-item" />
@ -12,6 +12,7 @@
<a-button type="primary" @click="loadData" :loading="loading">搜索</a-button>
</a-tooltip>
<a-button type="primary" @click="handleAdd">添加</a-button>
<a-button :disabled="!this.tableSelections.length" @click="syncToWorkspaceShow()" type="primary"> 批量分配</a-button>
<a-tooltip title="自动检测服务端所在服务器中是否存在 docker如果存在将自动添加到列表中">
<a-button type="dashed" @click="handleTryLocalDocker"> <a-icon type="question-circle" theme="filled" />自动探测 </a-button>
</a-tooltip>
@ -234,7 +235,7 @@
<a-form-model-item label="分配类型" prop="type">
<a-radio-group v-model="temp.type">
<a-radio value="docker"> docker </a-radio>
<a-radio value="swarm" :disabled="!temp.swarmId"> 集群 </a-radio>
<a-radio value="swarm" :disabled="temp.swarmId === true ? false : true"> 集群 </a-radio>
</a-radio-group>
</a-form-model-item>
<a-form-model-item label="选择工作空间" prop="workspaceId">
@ -358,6 +359,7 @@ export default {
dockerList: [],
swarmList: [],
},
tableSelections: [],
};
},
computed: {
@ -365,6 +367,14 @@ export default {
pagination() {
return COMPUTED_PAGINATION(this.listQuery);
},
rowSelection() {
return {
onChange: (selectedRowKeys) => {
this.tableSelections = selectedRowKeys;
},
selectedRowKeys: this.tableSelections,
};
},
},
mounted() {
this.loadData();
@ -651,10 +661,16 @@ export default {
syncToWorkspaceShow(item) {
this.syncToWorkspaceVisible = true;
this.loadWorkSpaceListAll();
this.temp = {
id: item.id,
swarmId: item.swarmId,
};
if (item) {
this.temp = {
ids: item.id,
swarmId: item.swarmId ? true : false,
};
} else {
this.temp = {
swarmId: true,
};
}
},
handleSyncToWorkspace() {
if (!this.temp.type) {
@ -669,6 +685,10 @@ export default {
});
return false;
}
if (!this.temp.ids) {
this.temp = { ...this.temp, ids: this.tableSelections.join(",") };
this.tableSelections = [];
}
//
machineDockerDistribute(this.temp).then((res) => {
if (res.code == 200) {

View File

@ -22,13 +22,14 @@
<a-dropdown v-if="this.layoutType === 'table'">
<a-menu slot="overlay">
<a-menu-item key="1" @click="syncToWorkspaceShow"> 分配节点 </a-menu-item>
<a-menu-item key="1" @click="syncNodeWhiteConfig"> 同步白名单 </a-menu-item>
<a-menu-item key="2" @click="syncNodeConfig"> 同步系统配置 </a-menu-item>
</a-menu>
<a-button type="primary"> 同步配置 <a-icon type="down" /> </a-button>
<a-button type="primary"> 批量操作 <a-icon type="down" /> </a-button>
</a-dropdown>
<a-tooltip v-else title="表格视图才能使用同步配置功能">
<a-button :disabled="true" type="primary"> 同步配置 <a-icon type="down" /> </a-button>
<a-button :disabled="true" type="primary"> 批量操作 <a-icon type="down" /> </a-button>
</a-tooltip>
<a-button type="primary" @click="changeLayout" :icon="this.layoutType === 'card' ? 'layout' : 'table'"> {{ this.layoutType === "card" ? "卡片" : "表格" }} </a-button>
<a-tooltip>
@ -249,10 +250,10 @@
</a-form-model-item>
<a-form-model-item label="编码方式" prop="transportEncryption">
<a-select show-search default-value=0 v-model="temp.transportEncryption" placeholder="请选择编码方式">
<a-select-option :value=0>不编码</a-select-option>
<a-select-option :value=1>BASE64</a-select-option>
<a-select-option :value=2>AES</a-select-option>
<a-select show-search default-value="0" v-model="temp.transportEncryption" placeholder="请选择编码方式">
<a-select-option :value="0">不编码</a-select-option>
<a-select-option :value="1">BASE64</a-select-option>
<a-select-option :value="2">AES</a-select-option>
</a-select>
</a-form-model-item>
</a-collapse-panel>
@ -578,9 +579,11 @@ export default {
syncToWorkspaceShow(item) {
this.syncToWorkspaceVisible = true;
this.loadWorkSpaceListAll();
this.temp = {
id: item.id,
};
if (item) {
this.temp = {
ids: item.id,
};
}
},
handleSyncToWorkspace() {
if (!this.temp.workspaceId) {
@ -589,6 +592,10 @@ export default {
});
return false;
}
if (!this.temp.ids) {
this.temp = { ...this.temp, ids: this.tableSelections.join(",") };
this.tableSelections = [];
}
//
machineDistribute(this.temp).then((res) => {
if (res.code == 200) {

View File

@ -3,7 +3,7 @@
<a-tabs default-active-key="1">
<a-tab-pane key="1" tab="管理">
<!-- 数据表格 -->
<a-table :data-source="list" :columns="columns" size="middle" :pagination="pagination" @change="changePage" bordered rowKey="id">
<a-table :data-source="list" :columns="columns" size="middle" :pagination="pagination" @change="changePage" bordered rowKey="id" :row-selection="rowSelection">
<template slot="title">
<a-space>
<a-input class="search-input-item" @pressEnter="loadData" v-model="listQuery['%name%']" placeholder="ssh名称" />
@ -17,6 +17,7 @@
</a-tooltip>
<a-button type="primary" @click="handleAdd">新增</a-button>
<a-button :disabled="!this.tableSelections.length" @click="syncToWorkspaceShow" type="primary"> 批量分配</a-button>
<a-button icon="download" type="primary" @click="handlerExportData()">导出</a-button>
<a-dropdown>
<a-menu slot="overlay">
@ -369,6 +370,14 @@ export default {
pagination() {
return COMPUTED_PAGINATION(this.listQuery);
},
rowSelection() {
return {
onChange: (selectedRowKeys) => {
this.tableSelections = selectedRowKeys;
},
selectedRowKeys: this.tableSelections,
};
},
},
data() {
return {
@ -447,6 +456,7 @@ export default {
configWorkspaceSshVisible: false,
syncToWorkspaceVisible: false,
workspaceList: [],
tableSelections: [],
};
},
created() {
@ -665,9 +675,11 @@ export default {
syncToWorkspaceShow(item) {
this.syncToWorkspaceVisible = true;
this.loadWorkSpaceListAll();
this.temp = {
id: item.id,
};
if (item) {
this.temp = {
ids: item.id,
};
}
},
handleSyncToWorkspace() {
if (!this.temp.workspaceId) {
@ -676,6 +688,10 @@ export default {
});
return false;
}
if (!this.temp.ids) {
this.temp = { ...this.temp, ids: this.tableSelections.join(",") };
this.tableSelections = [];
}
//
machineSshDistribute(this.temp).then((res) => {
if (res.code == 200) {