mirror of
https://gitee.com/dromara/mayfly-go
synced 2025-11-02 23:40:24 +08:00
refactor: 机器计划任务与流程定义关联至标签
This commit is contained in:
@@ -33,7 +33,7 @@
|
||||
"sql-formatter": "^15.0.2",
|
||||
"trzsz": "^1.1.5",
|
||||
"uuid": "^9.0.1",
|
||||
"vue": "^3.4.26",
|
||||
"vue": "^3.4.27",
|
||||
"vue-router": "^4.3.2",
|
||||
"xterm": "^5.3.0",
|
||||
"xterm-addon-fit": "^0.8.0",
|
||||
@@ -48,13 +48,13 @@
|
||||
"@typescript-eslint/eslint-plugin": "^6.7.4",
|
||||
"@typescript-eslint/parser": "^6.7.4",
|
||||
"@vitejs/plugin-vue": "^5.0.4",
|
||||
"@vue/compiler-sfc": "^3.4.25",
|
||||
"@vue/compiler-sfc": "^3.4.27",
|
||||
"code-inspector-plugin": "^0.4.5",
|
||||
"dotenv": "^16.3.1",
|
||||
"eslint": "^8.35.0",
|
||||
"eslint-plugin-vue": "^9.25.0",
|
||||
"prettier": "^3.2.5",
|
||||
"sass": "^1.75.0",
|
||||
"sass": "^1.76.0",
|
||||
"typescript": "^5.4.5",
|
||||
"vite": "^5.2.11",
|
||||
"vue-eslint-parser": "^9.4.2"
|
||||
|
||||
@@ -15,7 +15,7 @@ const config = {
|
||||
baseWsUrl: `${(window as any).globalConfig.BaseWsUrl || `${location.protocol == 'https:' ? 'wss:' : 'ws:'}//${getBaseApiUrl()}`}/api`,
|
||||
|
||||
// 系统版本
|
||||
version: 'v1.8.2',
|
||||
version: 'v1.8.3',
|
||||
};
|
||||
|
||||
export default config;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-drawer @open="initSort" :title="title" v-model="visible" :before-close="cancel" :destroy-on-close="true" :close-on-click-modal="false">
|
||||
<el-drawer @open="initSort" :title="title" v-model="visible" :before-close="cancel" :destroy-on-close="true" :close-on-click-modal="false" size="40%">
|
||||
<template #header>
|
||||
<DrawerHeader :header="title" :back="cancel" />
|
||||
</template>
|
||||
@@ -21,6 +21,10 @@
|
||||
<el-input v-model.trim="form.remark" placeholder="备注" auto-complete="off" clearable></el-input>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item ref="tagSelectRef" prop="codePaths" label="关联资源">
|
||||
<tag-tree-check height="300px" v-model="form.codePaths" :tag-type="[TagResourceTypeEnum.DbName.value, TagResourceTypeEnum.Redis.value]" />
|
||||
</el-form-item>
|
||||
|
||||
<el-divider content-position="left">审批节点</el-divider>
|
||||
|
||||
<el-table ref="taskTableRef" :data="tasks" row-key="taskKey" stripe style="width: 100%">
|
||||
@@ -70,6 +74,8 @@ import AccountSelectFormItem from '@/views/system/account/components/AccountSele
|
||||
import Sortable from 'sortablejs';
|
||||
import { randomUuid } from '../../common/utils/string';
|
||||
import { ProcdefStatus } from './enums';
|
||||
import TagTreeCheck from '../ops/component/TagTreeCheck.vue';
|
||||
import { TagResourceTypeEnum } from '@/common/commonEnum';
|
||||
|
||||
const props = defineProps({
|
||||
data: {
|
||||
@@ -115,6 +121,7 @@ const state = reactive({
|
||||
remark: null,
|
||||
// 流程的审批节点任务
|
||||
tasks: '',
|
||||
codePaths: [],
|
||||
},
|
||||
sortable: '' as any,
|
||||
});
|
||||
@@ -126,6 +133,7 @@ const { isFetching: saveBtnLoading, execute: saveFlowDefExec } = procdefApi.save
|
||||
watch(props, (newValue: any) => {
|
||||
if (newValue.data) {
|
||||
state.form = { ...newValue.data };
|
||||
state.form.codePaths = newValue.data.tags?.map((tag: any) => tag.codePath);
|
||||
const tasks = JSON.parse(state.form.tasks);
|
||||
tasks.forEach((t: any) => {
|
||||
t.userId = Number.parseInt(t.userId);
|
||||
@@ -160,25 +168,26 @@ const deleteTask = (idx: any) => {
|
||||
};
|
||||
|
||||
const btnOk = async () => {
|
||||
formRef.value.validate(async (valid: boolean) => {
|
||||
if (!valid) {
|
||||
ElMessage.error('表单填写有误');
|
||||
return false;
|
||||
}
|
||||
const checkRes = checkTasks();
|
||||
if (checkRes.err) {
|
||||
ElMessage.error(checkRes.err);
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
await formRef.value.validate();
|
||||
} catch (e: any) {
|
||||
ElMessage.error('请正确填写信息');
|
||||
return false;
|
||||
}
|
||||
|
||||
state.form.tasks = JSON.stringify(checkRes.tasks);
|
||||
await saveFlowDefExec();
|
||||
ElMessage.success('操作成功');
|
||||
emit('val-change', state.form);
|
||||
//重置表单域
|
||||
formRef.value.resetFields();
|
||||
state.form = {} as any;
|
||||
});
|
||||
const checkRes = checkTasks();
|
||||
if (checkRes.err) {
|
||||
ElMessage.error(checkRes.err);
|
||||
return false;
|
||||
}
|
||||
|
||||
state.form.tasks = JSON.stringify(checkRes.tasks);
|
||||
await saveFlowDefExec();
|
||||
ElMessage.success('操作成功');
|
||||
emit('val-change', state.form);
|
||||
//重置表单域
|
||||
formRef.value.resetFields();
|
||||
state.form = {} as any;
|
||||
};
|
||||
|
||||
const checkTasks = () => {
|
||||
|
||||
@@ -18,6 +18,10 @@
|
||||
<el-link @click="showProcdefTasks(data)" icon="view" type="primary" :underline="false"> </el-link>
|
||||
</template>
|
||||
|
||||
<template #codePaths="{ data }">
|
||||
<TagCodePath :path="data.tags?.map((tag: any) => tag.codePath)" />
|
||||
</template>
|
||||
|
||||
<template #action="{ data }">
|
||||
<el-button link v-if="actionBtns[perms.save]" @click="editFlowDef(data)" type="primary">编辑</el-button>
|
||||
</template>
|
||||
@@ -42,6 +46,7 @@ import { SearchItem } from '@/components/SearchForm';
|
||||
import ProcdefEdit from './ProcdefEdit.vue';
|
||||
import ProcdefTasks from './components/ProcdefTasks.vue';
|
||||
import { ProcdefStatus } from './enums';
|
||||
import TagCodePath from '../ops/component/TagCodePath.vue';
|
||||
|
||||
const perms = {
|
||||
save: 'flow:procdef:save',
|
||||
@@ -55,6 +60,7 @@ const columns = [
|
||||
TableColumn.new('status', '状态').typeTag(ProcdefStatus),
|
||||
TableColumn.new('remark', '备注'),
|
||||
TableColumn.new('tasks', '审批节点').isSlot().alignCenter().setMinWidth(60),
|
||||
TableColumn.new('codePaths', '关联资源').isSlot().setMinWidth('250px'),
|
||||
TableColumn.new('creator', '创建账号'),
|
||||
TableColumn.new('createTime', '创建时间').isTime(),
|
||||
];
|
||||
|
||||
@@ -2,7 +2,7 @@ import Api from '@/common/Api';
|
||||
|
||||
export const procdefApi = {
|
||||
list: Api.newGet('/flow/procdefs'),
|
||||
getByKey: Api.newGet('/flow/procdefs/{key}'),
|
||||
getByResource: Api.newGet('/flow/procdefs/{resourceType}/{resourceCode}'),
|
||||
save: Api.newPost('/flow/procdefs'),
|
||||
del: Api.newDelete('/flow/procdefs/{id}'),
|
||||
};
|
||||
|
||||
@@ -15,7 +15,6 @@ import { toRefs, reactive, watch, onMounted } from 'vue';
|
||||
import { accountApi } from '../../system/api';
|
||||
import { ProcinstTaskStatus } from '../enums';
|
||||
import { dateFormat } from '@/common/utils/date';
|
||||
import { procdefApi } from '../api';
|
||||
import { ElSteps, ElStep } from 'element-plus';
|
||||
|
||||
const props = defineProps({
|
||||
@@ -23,8 +22,8 @@ const props = defineProps({
|
||||
tasks: {
|
||||
type: [String, Object],
|
||||
},
|
||||
procdefKey: {
|
||||
type: String,
|
||||
procdef: {
|
||||
type: [Object],
|
||||
},
|
||||
// 流程实例任务列表
|
||||
procinstTasks: {
|
||||
@@ -54,7 +53,7 @@ watch(
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.procdefKey,
|
||||
() => props.procdef,
|
||||
async (newValue: any) => {
|
||||
if (newValue) {
|
||||
parseTasksByKey(newValue);
|
||||
@@ -63,15 +62,14 @@ watch(
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
if (props.procdefKey) {
|
||||
parseTasksByKey(props.procdefKey);
|
||||
if (props.procdef) {
|
||||
parseTasksByKey(props.procdef);
|
||||
return;
|
||||
}
|
||||
parseTasks(props.tasks);
|
||||
});
|
||||
|
||||
const parseTasksByKey = async (key: string) => {
|
||||
const procdef = await procdefApi.getByKey.request({ key });
|
||||
const parseTasksByKey = async (procdef: any) => {
|
||||
parseTasks(procdef.tasks);
|
||||
};
|
||||
|
||||
|
||||
@@ -35,7 +35,9 @@
|
||||
</slot>
|
||||
</span>
|
||||
|
||||
<slot :node="node" :data="data" name="suffix"></slot>
|
||||
<span class="label-suffix">
|
||||
<slot :node="node" :data="data" name="suffix"></slot>
|
||||
</span>
|
||||
</span>
|
||||
</template>
|
||||
</el-tree>
|
||||
@@ -222,5 +224,13 @@ defineExpose({
|
||||
display: inline-block;
|
||||
min-width: 100%;
|
||||
}
|
||||
|
||||
.label-suffix {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
color: #c4c9c4;
|
||||
font-size: 10px;
|
||||
margin-top: 2px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -56,7 +56,7 @@ const props = defineProps({
|
||||
default: 'calc(100vh - 330px)',
|
||||
},
|
||||
tagType: {
|
||||
type: Number,
|
||||
type: [Number, Array<Number>],
|
||||
default: TagResourceTypeEnum.Tag.value,
|
||||
},
|
||||
nodeKey: {
|
||||
@@ -81,7 +81,12 @@ onMounted(() => {
|
||||
});
|
||||
|
||||
const search = async () => {
|
||||
state.tags = await tagApi.getTagTrees.request({ type: props.tagType });
|
||||
let tagType: any = props.tagType;
|
||||
if (Array.isArray(props.tagType)) {
|
||||
tagType = props.tagType.join(',');
|
||||
}
|
||||
|
||||
state.tags = await tagApi.getTagTrees.request({ type: tagType });
|
||||
|
||||
setTimeout(() => {
|
||||
const checkedNodes = tagTreeRef.value.getCheckedNodes();
|
||||
|
||||
@@ -62,8 +62,6 @@
|
||||
<el-form-item prop="remark" label="备注">
|
||||
<el-input v-model.trim="form.remark" auto-complete="off" type="textarea"></el-input>
|
||||
</el-form-item>
|
||||
|
||||
<procdef-select-form-item v-model="form.flowProcdefKey" />
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
@@ -80,9 +78,7 @@
|
||||
import { toRefs, reactive, watch, ref, watchEffect } from 'vue';
|
||||
import { dbApi } from './api';
|
||||
import { ElMessage } from 'element-plus';
|
||||
// import TagTreeSelect from '../component/TagTreeSelect.vue';
|
||||
import type { CheckboxValueType } from 'element-plus';
|
||||
import ProcdefSelectFormItem from '@/views/flow/components/ProcdefSelectFormItem.vue';
|
||||
import { DbType } from '@/views/ops/db/dialect';
|
||||
import { ResourceCodePattern } from '@/common/pattern';
|
||||
|
||||
@@ -183,7 +179,6 @@ const state = reactive({
|
||||
remark: '',
|
||||
instanceId: null as any,
|
||||
authCertName: '',
|
||||
flowProcdefKey: '',
|
||||
},
|
||||
instances: [] as any,
|
||||
});
|
||||
|
||||
@@ -187,8 +187,6 @@
|
||||
<el-descriptions-item :span="3" label="数据库">{{ infoDialog.data?.database }}</el-descriptions-item>
|
||||
<el-descriptions-item :span="3" label="备注">{{ infoDialog.data?.remark }}</el-descriptions-item>
|
||||
|
||||
<el-descriptions-item :span="3" label="工单流程key">{{ infoDialog.data?.flowProcdefKey }}</el-descriptions-item>
|
||||
|
||||
<el-descriptions-item :span="2" label="创建时间">{{ dateFormat(infoDialog.data?.createTime) }} </el-descriptions-item>
|
||||
<el-descriptions-item :span="1" label="创建者">{{ infoDialog.data?.creator }}</el-descriptions-item>
|
||||
|
||||
@@ -240,7 +238,6 @@ const columns = ref([
|
||||
TableColumn.new('host', 'ip:port').isSlot().setAddWidth(40),
|
||||
TableColumn.new('authCertName', '授权凭证'),
|
||||
TableColumn.new('database', '库').isSlot().setMinWidth(80),
|
||||
TableColumn.new('flowProcdefKey', '关联流程'),
|
||||
TableColumn.new('remark', '备注'),
|
||||
TableColumn.new('code', '编号'),
|
||||
]);
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column prop="remark" label="备注" show-overflow-tooltip min-width="120"> </el-table-column>
|
||||
<el-table-column prop="flowProcdefKey" label="关联流程" min-width="120" show-overflow-tooltip> </el-table-column>
|
||||
<el-table-column prop="code" label="编号" show-overflow-tooltip min-width="120"> </el-table-column>
|
||||
<el-table-column min-wdith="120px">
|
||||
<template #header>
|
||||
|
||||
@@ -47,10 +47,8 @@
|
||||
</template>
|
||||
|
||||
<template #suffix="{ data }">
|
||||
<span class="db-table-size" v-if="data.type.value == SqlExecNodeType.Table && data.params.size">{{ ` ${data.params.size}` }}</span>
|
||||
<span class="db-table-size" v-if="data.type.value == SqlExecNodeType.TableMenu && data.params.dbTableSize">{{
|
||||
` ${data.params.dbTableSize}`
|
||||
}}</span>
|
||||
<span v-if="data.type.value == SqlExecNodeType.Table && data.params.size">{{ ` ${data.params.size}` }}</span>
|
||||
<span v-if="data.type.value == SqlExecNodeType.TableMenu && data.params.dbTableSize">{{ ` ${data.params.dbTableSize}` }}</span>
|
||||
</template>
|
||||
</tag-tree>
|
||||
</Pane>
|
||||
@@ -148,7 +146,7 @@
|
||||
:db-id="dt.params.id"
|
||||
:db="dt.params.db"
|
||||
:db-type="dt.params.type"
|
||||
:flow-procdef-key="dt.params.flowProcdefKey"
|
||||
:flow-procdef="dt.params.flowProcdef"
|
||||
:height="state.tablesOpHeight"
|
||||
/>
|
||||
</el-tab-pane>
|
||||
@@ -163,7 +161,7 @@
|
||||
:dbId="tableCreateDialog.dbId"
|
||||
:db="tableCreateDialog.db"
|
||||
:dbType="tableCreateDialog.dbType"
|
||||
:flow-procdef-key="tableCreateDialog.flowProcdefKey"
|
||||
:flow-procdef="tableCreateDialog.flowProcdef"
|
||||
:data="tableCreateDialog.data"
|
||||
v-model:visible="tableCreateDialog.visible"
|
||||
@submit-sql="onSubmitEditTableSql"
|
||||
@@ -190,6 +188,7 @@ import { useEventListener } from '@vueuse/core';
|
||||
import SqlExecBox from '@/views/ops/db/component/sqleditor/SqlExecBox';
|
||||
import { useAutoOpenResource } from '@/store/autoOpenResource';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { procdefApi } from '@/views/flow/api';
|
||||
|
||||
const DbTableOp = defineAsyncComponent(() => import('./component/table/DbTableOp.vue'));
|
||||
const DbSqlEditor = defineAsyncComponent(() => import('./component/sqleditor/DbSqlEditor.vue'));
|
||||
@@ -243,7 +242,7 @@ const nodeClickChangeDb = (nodeData: TagTreeNode) => {
|
||||
type: params.type,
|
||||
tagPath: params.tagPath,
|
||||
databases: params.dbs,
|
||||
flowProcdefKey: params.flowProcdefKey,
|
||||
flowProcdef: params.flowProcdef,
|
||||
},
|
||||
params.db
|
||||
);
|
||||
@@ -271,10 +270,11 @@ const NodeTypeTagPath = new NodeType(TagTreeNode.TagPath)
|
||||
.withContextMenuItems([ContextmenuItemRefresh]);
|
||||
|
||||
// 数据库实例节点类型
|
||||
const NodeTypeDbInst = new NodeType(SqlExecNodeType.DbInst).withLoadNodesFunc((parentNode: TagTreeNode) => {
|
||||
const NodeTypeDbInst = new NodeType(SqlExecNodeType.DbInst).withLoadNodesFunc(async (parentNode: TagTreeNode) => {
|
||||
const params = parentNode.params;
|
||||
const dbs = params.database.split(' ')?.sort();
|
||||
|
||||
const flowProcdef = await procdefApi.getByResource.request({ resourceType: TagResourceTypeEnum.DbName.value, resourceCode: params.code });
|
||||
return dbs.map((x: any) => {
|
||||
return new TagTreeNode(`${parentNode.key}.${x}`, x, NodeTypeDb)
|
||||
.withParams({
|
||||
@@ -285,7 +285,7 @@ const NodeTypeDbInst = new NodeType(SqlExecNodeType.DbInst).withLoadNodesFunc((p
|
||||
host: `${params.host}:${params.port}`,
|
||||
dbs: dbs,
|
||||
db: x,
|
||||
flowProcdefKey: params.flowProcdefKey,
|
||||
flowProcdef: flowProcdef,
|
||||
})
|
||||
.withIcon(DbIcon);
|
||||
});
|
||||
@@ -346,7 +346,7 @@ const NodeTypeTableMenu = new NodeType(SqlExecNodeType.TableMenu)
|
||||
])
|
||||
.withLoadNodesFunc(async (parentNode: TagTreeNode) => {
|
||||
const params = parentNode.params;
|
||||
let { id, db, type, flowProcdefKey, schema } = params;
|
||||
let { id, db, type, flowProcdef, schema } = params;
|
||||
// 获取当前库的所有表信息
|
||||
let tables = await DbInst.getInst(id).loadTables(db, state.reloadStatus);
|
||||
state.reloadStatus = false;
|
||||
@@ -362,7 +362,7 @@ const NodeTypeTableMenu = new NodeType(SqlExecNodeType.TableMenu)
|
||||
db,
|
||||
type,
|
||||
schema,
|
||||
flowProcdefKey: flowProcdefKey,
|
||||
flowProcdef: flowProcdef,
|
||||
key: key,
|
||||
parentKey: parentNode.key,
|
||||
tableName: x.tableName,
|
||||
@@ -448,7 +448,7 @@ const state = reactive({
|
||||
dbId: 0,
|
||||
db: '',
|
||||
dbType: '',
|
||||
flowProcdefKey: '',
|
||||
flowProcdef: null as any,
|
||||
data: {},
|
||||
parentKey: '',
|
||||
},
|
||||
@@ -497,7 +497,7 @@ const autoOpenDb = (codePath: string) => {
|
||||
// 置空
|
||||
autoOpenResourceStore.setDbCodePath('');
|
||||
tagTreeRef.value.setCurrentKey(dbCode);
|
||||
}, 600);
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -669,6 +669,8 @@ const onTabChange = () => {
|
||||
// 注册sql提示
|
||||
registerDbCompletionItemProvider(nowTab.dbId, nowTab.db, nowTab.params.dbs, nowDbInst.value.type);
|
||||
}
|
||||
|
||||
tagTreeRef.value.setCurrentKey(nowTab?.treeNodeKey);
|
||||
};
|
||||
|
||||
const reloadSqls = (dbId: number, db: string) => {
|
||||
@@ -700,7 +702,7 @@ const reloadNode = (nodeKey: string) => {
|
||||
};
|
||||
|
||||
const onEditTable = async (data: any) => {
|
||||
let { db, id, tableName, tableComment, type, parentKey, key, flowProcdefKey } = data.params;
|
||||
let { db, id, tableName, tableComment, type, parentKey, key, flowProcdef } = data.params;
|
||||
// data.label就是表名
|
||||
if (tableName) {
|
||||
state.tableCreateDialog.title = '修改表';
|
||||
@@ -719,12 +721,12 @@ const onEditTable = async (data: any) => {
|
||||
state.tableCreateDialog.dbId = id;
|
||||
state.tableCreateDialog.db = db;
|
||||
state.tableCreateDialog.dbType = type;
|
||||
state.tableCreateDialog.flowProcdefKey = flowProcdefKey;
|
||||
state.tableCreateDialog.flowProcdef = flowProcdef;
|
||||
state.tableCreateDialog.visible = true;
|
||||
};
|
||||
|
||||
const onDeleteTable = async (data: any) => {
|
||||
let { db, id, tableName, parentKey, flowProcdefKey, schema } = data.params;
|
||||
let { db, id, tableName, parentKey, flowProcdef, schema } = data.params;
|
||||
await ElMessageBox.confirm(`此操作是永久性且无法撤销,确定删除【${tableName}】? `, '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
@@ -736,7 +738,7 @@ const onDeleteTable = async (data: any) => {
|
||||
let schemaStr = schema ? `${dialect.quoteIdentifier(schema)}.` : '';
|
||||
|
||||
dbApi.sqlExec.request({ id, db, sql: `drop table ${schemaStr + dialect.quoteIdentifier(tableName)}` }).then(() => {
|
||||
if (flowProcdefKey) {
|
||||
if (flowProcdef) {
|
||||
ElMessage.success('工单提交成功');
|
||||
return;
|
||||
}
|
||||
@@ -748,7 +750,7 @@ const onDeleteTable = async (data: any) => {
|
||||
};
|
||||
|
||||
const onRenameTable = async (data: any) => {
|
||||
let { db, id, tableName, parentKey, flowProcdefKey } = data.params;
|
||||
let { db, id, tableName, parentKey, flowProcdef } = data.params;
|
||||
let tableData = { db, oldTableName: tableName, tableName };
|
||||
|
||||
let value = ref(tableName);
|
||||
@@ -771,7 +773,7 @@ const onRenameTable = async (data: any) => {
|
||||
dbId: id as any,
|
||||
db: db as any,
|
||||
dbType: nowDbInst.value.getDialect().getInfo().formatSqlDialect,
|
||||
flowProcdefKey: flowProcdefKey,
|
||||
flowProcdef: flowProcdef,
|
||||
runSuccessCallback: () => {
|
||||
setTimeout(() => {
|
||||
parentKey && reloadNode(parentKey);
|
||||
@@ -831,7 +833,7 @@ const getNowDbInfo = () => {
|
||||
name: di.name,
|
||||
type: di.type,
|
||||
host: di.host,
|
||||
flowProcdefKey: di.flowProcdefKey,
|
||||
flowProcdef: di.flowProcdef,
|
||||
dbName: state.db,
|
||||
};
|
||||
};
|
||||
@@ -839,11 +841,6 @@ const getNowDbInfo = () => {
|
||||
|
||||
<style lang="scss">
|
||||
.db-sql-exec {
|
||||
.db-table-size {
|
||||
color: #c4c9c4;
|
||||
font-size: 9px;
|
||||
}
|
||||
|
||||
.db-op {
|
||||
height: calc(100vh - 106px);
|
||||
}
|
||||
|
||||
@@ -325,7 +325,7 @@ const onRunSql = async (newTab = false) => {
|
||||
}
|
||||
|
||||
// 启用工单审批
|
||||
if (execRemark && getNowDbInst().flowProcdefKey) {
|
||||
if (execRemark && getNowDbInst().flowProcdef) {
|
||||
try {
|
||||
await getNowDbInst().runSql(props.dbName, sql, execRemark);
|
||||
ElMessage.success('工单提交成功');
|
||||
|
||||
@@ -6,7 +6,7 @@ export type SqlExecProps = {
|
||||
dbId: number;
|
||||
db: string;
|
||||
dbType?: string;
|
||||
flowProcdefKey?: string;
|
||||
flowProcdef?: any;
|
||||
runSuccessCallback?: Function;
|
||||
cancelCallback?: Function;
|
||||
};
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-dialog title="待执行SQL" v-model="dialogVisible" :show-close="false" width="600px">
|
||||
<el-dialog title="待执行SQL" v-model="dialogVisible" :show-close="false" width="600px" :close-on-click-modal="false">
|
||||
<monaco-editor height="300px" class="codesql" language="sql" v-model="sqlValue" />
|
||||
<el-input
|
||||
@keyup.enter="runSql"
|
||||
ref="remarkInputRef"
|
||||
v-model="remark"
|
||||
:placeholder="props.flowProcdefKey ? '执行备注(必填)' : '执行备注(选填)'"
|
||||
:placeholder="props.flowProcdef ? '执行备注(必填)' : '执行备注(选填)'"
|
||||
class="mt5"
|
||||
/>
|
||||
|
||||
<div v-if="props.flowProcdefKey">
|
||||
<div v-if="props.flowProcdef">
|
||||
<el-divider content-position="left">审批节点</el-divider>
|
||||
<procdef-tasks :procdef-key="props.flowProcdefKey" />
|
||||
<procdef-tasks :procdef="props.flowProcdef" />
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
@@ -59,7 +59,7 @@ onMounted(() => {
|
||||
*/
|
||||
const runSql = async () => {
|
||||
// 存在流程审批,则备注为必填
|
||||
if (!state.remark && props.flowProcdefKey) {
|
||||
if (!state.remark && props.flowProcdef) {
|
||||
ElMessage.error('请输入执行的备注信息');
|
||||
return;
|
||||
}
|
||||
@@ -74,7 +74,7 @@ const runSql = async () => {
|
||||
});
|
||||
|
||||
// 存在流程审批
|
||||
if (props.flowProcdefKey) {
|
||||
if (props.flowProcdef) {
|
||||
runSuccess = false;
|
||||
ElMessage.success('工单提交成功');
|
||||
return;
|
||||
@@ -113,7 +113,7 @@ const cancel = () => {
|
||||
};
|
||||
|
||||
const open = () => {
|
||||
state.sqlValue = sqlFormatter(props.sql, { language: props.dbType || 'mysql' });
|
||||
state.sqlValue = sqlFormatter(props.sql, { language: (props.dbType || 'mysql') as any });
|
||||
state.dialogVisible = true;
|
||||
setTimeout(() => {
|
||||
remarkInputRef.value?.focus();
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
<!-- 字段列的数据类型 -->
|
||||
<div class="column-type">
|
||||
<span v-if="column.dataTypeSubscript === 'icon-clock'">
|
||||
<SvgIcon :size="10" name="Clock" style="cursor: unset" />
|
||||
<SvgIcon :size="9" name="Clock" style="cursor: unset" />
|
||||
</span>
|
||||
<span class="font8" v-else>{{ column.dataTypeSubscript }}</span>
|
||||
</div>
|
||||
@@ -880,8 +880,8 @@ defineExpose({
|
||||
color: var(--el-color-info-light-3);
|
||||
font-weight: bold;
|
||||
position: absolute;
|
||||
top: -5px;
|
||||
padding: 2px;
|
||||
top: -7px;
|
||||
padding: 1px;
|
||||
}
|
||||
|
||||
.column-right {
|
||||
|
||||
@@ -37,7 +37,6 @@ import { ref, watch, onMounted } from 'vue';
|
||||
import ColumnFormItem from './ColumnFormItem.vue';
|
||||
import { DbInst } from '../../db';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { getDbDialect } from '@/views/ops/db/dialect';
|
||||
|
||||
export interface ColumnFormItemProps {
|
||||
dbInst: DbInst;
|
||||
|
||||
@@ -152,8 +152,8 @@ const props = defineProps({
|
||||
dbType: {
|
||||
type: String,
|
||||
},
|
||||
flowProcdefKey: {
|
||||
type: String,
|
||||
flowProcdef: {
|
||||
type: Object,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -335,7 +335,7 @@ const submit = async () => {
|
||||
dbId: props.dbId as any,
|
||||
db: props.db as any,
|
||||
dbType: dbDialect.getInfo().formatSqlDialect,
|
||||
flowProcdefKey: props.flowProcdefKey,
|
||||
flowProcdef: props.flowProcdef,
|
||||
runSuccessCallback: () => {
|
||||
emit('submit-sql', { tableName: state.tableData.tableName });
|
||||
// cancel();
|
||||
|
||||
@@ -108,7 +108,7 @@
|
||||
:dbId="dbId"
|
||||
:db="db"
|
||||
:dbType="dbType"
|
||||
:flow-procdef-key="props.flowProcdefKey"
|
||||
:flow-procdef="props.flowProcdef"
|
||||
:data="tableCreateDialog.data"
|
||||
v-model:visible="tableCreateDialog.visible"
|
||||
@submit-sql="onSubmitSql"
|
||||
@@ -150,8 +150,8 @@ const props = defineProps({
|
||||
type: [String],
|
||||
required: true,
|
||||
},
|
||||
flowProcdefKey: {
|
||||
type: [String],
|
||||
flowProcdef: {
|
||||
type: [Object],
|
||||
},
|
||||
});
|
||||
|
||||
@@ -327,7 +327,7 @@ const dropTable = async (row: any) => {
|
||||
sql: `DROP TABLE ${tableName}`,
|
||||
dbId: props.dbId as any,
|
||||
db: props.db as any,
|
||||
flowProcdefKey: props.flowProcdefKey,
|
||||
flowProcdef: props.flowProcdef,
|
||||
runSuccessCallback: async () => {
|
||||
await getTables();
|
||||
},
|
||||
|
||||
@@ -41,9 +41,9 @@ export class DbInst {
|
||||
type: string;
|
||||
|
||||
/**
|
||||
* 流程定义key,若存在则需要审批执行
|
||||
* 流程定义,若存在则需要审批执行
|
||||
*/
|
||||
flowProcdefKey: string;
|
||||
flowProcdef: any;
|
||||
|
||||
/**
|
||||
* dbName -> db
|
||||
@@ -352,6 +352,7 @@ export class DbInst {
|
||||
* 弹框提示是否执行sql
|
||||
*/
|
||||
promptExeSql = (db: string, sql: string, cancelFunc: any = null, successFunc: any = null) => {
|
||||
console.log(this);
|
||||
SqlExecBox({
|
||||
sql,
|
||||
dbId: this.id,
|
||||
@@ -359,7 +360,7 @@ export class DbInst {
|
||||
dbType: this.getDialect().getInfo().formatSqlDialect,
|
||||
runSuccessCallback: successFunc,
|
||||
cancelCallback: cancelFunc,
|
||||
flowProcdefKey: this.flowProcdefKey,
|
||||
flowProcdef: this.flowProcdef,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -383,6 +384,11 @@ export class DbInst {
|
||||
}
|
||||
let dbInst = dbInstCache.get(inst.id);
|
||||
if (dbInst) {
|
||||
// 更新可能更改的流程定义
|
||||
if (inst.flowProcdef !== undefined) {
|
||||
dbInst.flowProcdef = inst.flowProcdef;
|
||||
dbInstCache.set(dbInst.id, dbInst);
|
||||
}
|
||||
return dbInst;
|
||||
}
|
||||
console.info(`new dbInst: ${inst.id}, tagPath: ${inst.tagPath}`);
|
||||
@@ -393,7 +399,7 @@ export class DbInst {
|
||||
dbInst.name = inst.name;
|
||||
dbInst.type = inst.type;
|
||||
dbInst.databases = inst.databases;
|
||||
dbInst.flowProcdefKey = inst.flowProcdefKey;
|
||||
dbInst.flowProcdef = inst.flowProcdef;
|
||||
|
||||
dbInstCache.set(dbInst.id, dbInst);
|
||||
return dbInst;
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
</template>
|
||||
|
||||
<template #suffix="{ data }">
|
||||
<span style="color: #c4c9c4; font-size: 9px" v-if="data.type.value == MachineNodeType.AuthCert">{{
|
||||
<span v-if="data.type.value == MachineNodeType.AuthCert">{{
|
||||
` ${data.params.selectAuthCert.username}@${data.params.ip}:${data.params.port}`
|
||||
}}</span>
|
||||
</template>
|
||||
@@ -368,7 +368,7 @@ const autoOpenTerminal = (codePath: string) => {
|
||||
|
||||
const acNode = tagTreeRef.value.getNode(authCertName);
|
||||
openTerminal(acNode.data.params);
|
||||
}, 600);
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
const openTerminal = (machine: any, ex?: boolean) => {
|
||||
@@ -402,14 +402,14 @@ const openTerminal = (machine: any, ex?: boolean) => {
|
||||
}
|
||||
}
|
||||
|
||||
let { name, username } = machine;
|
||||
let { name } = machine;
|
||||
const labelName = `${machine.selectAuthCert.username}@${name}`;
|
||||
|
||||
// 同一个机器的终端打开多次,key后添加下划线和数字区分
|
||||
openIds[ac] = openIds[ac] ? ++openIds[ac] : 1;
|
||||
let sameIndex = openIds[ac];
|
||||
|
||||
let key = `${ac}_${username}_${sameIndex}`;
|
||||
let key = `${ac}_${sameIndex}`;
|
||||
// 只保留name的15个字,超出部分只保留前后10个字符,中间用省略号代替
|
||||
const label = labelName.length > 15 ? labelName.slice(0, 10) + '...' + labelName.slice(-10) : labelName;
|
||||
|
||||
@@ -537,6 +537,9 @@ const onResizeTagTree = () => {
|
||||
|
||||
const onTabChange = () => {
|
||||
fitTerminal();
|
||||
|
||||
const nowTab = state.tabs.get(state.activeTermName);
|
||||
tagTreeRef.value.setCurrentKey(nowTab?.authCert);
|
||||
};
|
||||
|
||||
const fitTerminal = () => {
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
<template>
|
||||
<div class="mock-data-dialog">
|
||||
<el-dialog
|
||||
<el-drawer
|
||||
:title="title"
|
||||
v-model="dialogVisible"
|
||||
:close-on-click-modal="false"
|
||||
:before-close="cancel"
|
||||
:show-close="true"
|
||||
:destroy-on-close="true"
|
||||
width="900px"
|
||||
size="40%"
|
||||
>
|
||||
<template #header>
|
||||
<DrawerHeader :header="title" :back="cancel" />
|
||||
</template>
|
||||
|
||||
<el-form :model="form" ref="formRef" :rules="rules" label-width="auto">
|
||||
<el-form-item prop="name" label="名称">
|
||||
<el-input v-model="form.name" placeholder="请输入名称"></el-input>
|
||||
@@ -34,19 +38,13 @@
|
||||
<el-input v-model="form.remark" placeholder="请输入备注"></el-input>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item prop="machineIds" label="关联机器">
|
||||
<el-select multiple v-model="form.machineIds" filterable placeholder="请选关联机器" style="width: 100%">
|
||||
<el-option v-for="ac in state.machines" :key="ac.id" :value="ac.id" :label="ac.ip">
|
||||
{{ ac.ip }}
|
||||
<el-divider direction="vertical" border-style="dashed" />
|
||||
{{ ac.tagPath }}{{ ac.name }}
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item prop="script" label="执行脚本" required>
|
||||
<monaco-editor style="width: 100%" v-model="form.script" language="shell" height="300px"
|
||||
<monaco-editor style="width: 100%" v-model="form.script" language="shell" height="200px"
|
||||
/></el-form-item>
|
||||
|
||||
<el-form-item ref="tagSelectRef" prop="codePaths" label="关联机器">
|
||||
<tag-tree-check height="200px" :tag-type="TagResourceTypeEnum.Machine.value" v-model="form.codePaths" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
@@ -55,7 +53,7 @@
|
||||
<el-button v-auth="'machine:script:save'" type="primary" :loading="btnLoading" @click="btnOk" :disabled="submitDisabled">保 存</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</el-drawer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -67,6 +65,9 @@ import { CronJobStatusEnum, CronJobSaveExecResTypeEnum } from '../enums';
|
||||
import { notEmpty } from '@/common/assert';
|
||||
import MonacoEditor from '@/components/monaco/MonacoEditor.vue';
|
||||
import CrontabInput from '@/components/crontab/CrontabInput.vue';
|
||||
import DrawerHeader from '@/components/drawer-header/DrawerHeader.vue';
|
||||
import TagTreeCheck from '../../component/TagTreeCheck.vue';
|
||||
import { TagResourceTypeEnum } from '@/common/commonEnum';
|
||||
|
||||
const props = defineProps({
|
||||
visible: {
|
||||
@@ -130,11 +131,11 @@ const state = reactive({
|
||||
id: null,
|
||||
name: '',
|
||||
cron: '',
|
||||
machineIds: [],
|
||||
remark: '',
|
||||
script: '',
|
||||
status: 1,
|
||||
saveExecResType: -1,
|
||||
codePaths: [],
|
||||
},
|
||||
machines: [] as any,
|
||||
btnLoading: false,
|
||||
@@ -154,7 +155,7 @@ watch(props, async (newValue: any) => {
|
||||
}
|
||||
if (newValue.data) {
|
||||
state.form = { ...newValue.data };
|
||||
state.form.machineIds = await cronJobApi.relateMachineIds.request({ cronJobId: state.form.id });
|
||||
state.form.codePaths = newValue.data.tags?.map((tag: any) => tag.codePath);
|
||||
} else {
|
||||
state.form = { script: '', status: 1 } as any;
|
||||
state.chooseMachines = [];
|
||||
|
||||
@@ -19,6 +19,10 @@
|
||||
<el-tag v-else type="danger" effect="plain">未运行</el-tag>
|
||||
</template>
|
||||
|
||||
<template #codePaths="{ data }">
|
||||
<TagCodePath :path="data.tags?.map((tag: any) => tag.codePath)" />
|
||||
</template>
|
||||
|
||||
<template #action="{ data }">
|
||||
<el-button :disabled="data.status == CronJobStatusEnum.Disable.value" v-auth="perms.saveCronJob" type="primary" @click="runCronJob(data)" link
|
||||
>执行</el-button
|
||||
@@ -41,6 +45,7 @@ import PageTable from '@/components/pagetable/PageTable.vue';
|
||||
import { TableColumn } from '@/components/pagetable';
|
||||
import { CronJobStatusEnum, CronJobSaveExecResTypeEnum } from '../enums';
|
||||
import { SearchItem } from '@/components/SearchForm';
|
||||
import TagCodePath from '../../component/TagCodePath.vue';
|
||||
|
||||
const CronJobEdit = defineAsyncComponent(() => import('./CronJobEdit.vue'));
|
||||
const CronJobExecList = defineAsyncComponent(() => import('./CronJobExecList.vue'));
|
||||
@@ -61,6 +66,7 @@ const columns = ref([
|
||||
TableColumn.new('running', '运行状态').isSlot(),
|
||||
TableColumn.new('saveExecResType', '记录类型').typeTag(CronJobSaveExecResTypeEnum),
|
||||
TableColumn.new('remark', '备注'),
|
||||
TableColumn.new('codePaths', '关联机器').isSlot().setMinWidth('250px'),
|
||||
TableColumn.new('action', '操作').isSlot().setMinWidth(180).fixedRight().alignCenter(),
|
||||
]);
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="codePaths" label="关联机器" min-width="220px" show-overflow-tooltip>
|
||||
<el-table-column prop="codePaths" label="关联机器" min-width="250px" show-overflow-tooltip>
|
||||
<template #default="scope">
|
||||
<TagCodePath :path="scope.row.tags.map((tag: any) => tag.codePath)" />
|
||||
</template>
|
||||
|
||||
@@ -36,13 +36,8 @@
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #label="{ data }">
|
||||
<span v-if="data.type.value == MongoNodeType.Dbs">
|
||||
{{ data.params.database }}
|
||||
<span style="color: #8492a6; font-size: 13px"> [{{ formatByteSize(data.params.size) }}] </span>
|
||||
</span>
|
||||
|
||||
<span v-else>{{ data.label }}</span>
|
||||
<template #suffix="{ data }">
|
||||
<span v-if="data.type.value == MongoNodeType.Dbs">{{ formatByteSize(data.params.size) }}</span>
|
||||
</template>
|
||||
</tag-tree>
|
||||
</Pane>
|
||||
|
||||
@@ -35,6 +35,10 @@
|
||||
|
||||
<SvgIcon v-if="data.type.value == RedisNodeType.Db" name="Coin" color="#67c23a" />
|
||||
</template>
|
||||
|
||||
<template #suffix="{ data }">
|
||||
<span v-if="data.type.value == RedisNodeType.Db">{{ data.params.keys }}</span>
|
||||
</template>
|
||||
</tag-tree>
|
||||
</Pane>
|
||||
|
||||
@@ -197,6 +201,7 @@ import { Splitpanes, Pane } from 'splitpanes';
|
||||
import { RedisInst } from './redis';
|
||||
import { useAutoOpenResource } from '@/store/autoOpenResource';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { procdefApi } from '@/views/flow/api';
|
||||
|
||||
const KeyDetail = defineAsyncComponent(() => import('./KeyDetail.vue'));
|
||||
|
||||
@@ -244,11 +249,13 @@ const NodeTypeTagPath = new NodeType(TagTreeNode.TagPath).withLoadNodesFunc(asyn
|
||||
// redis实例节点类型
|
||||
const NodeTypeRedis = new NodeType(RedisNodeType.Redis).withLoadNodesFunc(async (parentNode: TagTreeNode) => {
|
||||
const redisInfo = parentNode.params;
|
||||
const flowProcdef = await procdefApi.getByResource.request({ resourceType: TagResourceTypeEnum.Redis.value, resourceCode: redisInfo.code });
|
||||
|
||||
let dbs: TagTreeNode[] = redisInfo.db.split(',').map((x: string) => {
|
||||
return new TagTreeNode(x, `db${x}`, NodeTypeDb).withIsLeaf(true).withParams({
|
||||
id: redisInfo.id,
|
||||
db: x,
|
||||
flowProcdefKey: redisInfo.flowProcdefKey,
|
||||
flowProcdef: flowProcdef,
|
||||
name: `db${x}`,
|
||||
keys: 0,
|
||||
});
|
||||
@@ -268,7 +275,7 @@ const NodeTypeRedis = new NodeType(RedisNodeType.Redis).withLoadNodesFunc(async
|
||||
}
|
||||
// 替换label
|
||||
dbs.forEach((e: any) => {
|
||||
e.label = `${e.params.name} [${e.params.keys}]`;
|
||||
e.label = `${e.params.name}`;
|
||||
});
|
||||
return dbs;
|
||||
});
|
||||
@@ -281,7 +288,7 @@ const NodeTypeDb = new NodeType(RedisNodeType.Db).withNodeClickFunc((nodeData: T
|
||||
|
||||
redisInst.value.id = nodeData.params.id;
|
||||
redisInst.value.db = Number.parseInt(nodeData.params.db);
|
||||
redisInst.value.flowProcdefKey = nodeData.params.flowProcdefKey;
|
||||
redisInst.value.flowProcdef = nodeData.params.flowProcdef;
|
||||
|
||||
scan();
|
||||
});
|
||||
|
||||
@@ -84,8 +84,6 @@
|
||||
<el-form-item prop="remark" label="备注">
|
||||
<el-input v-model.trim="form.remark" auto-complete="off" type="textarea"></el-input>
|
||||
</el-form-item>
|
||||
|
||||
<procdef-select-form-item v-model="form.flowProcdefKey" />
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="其他配置" name="other">
|
||||
@@ -113,7 +111,6 @@ import { redisApi } from './api';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import TagTreeSelect from '../component/TagTreeSelect.vue';
|
||||
import SshTunnelSelect from '../component/SshTunnelSelect.vue';
|
||||
import ProcdefSelectFormItem from '@/views/flow/components/ProcdefSelectFormItem.vue';
|
||||
import { ResourceCodePattern } from '@/common/pattern';
|
||||
import DrawerHeader from '@/components/drawer-header/DrawerHeader.vue';
|
||||
|
||||
@@ -199,7 +196,6 @@ const state = reactive({
|
||||
db: '',
|
||||
remark: '',
|
||||
sshTunnelMachineId: -1,
|
||||
flowProcdefKey: '',
|
||||
},
|
||||
submitForm: {} as any,
|
||||
dbList: [0],
|
||||
|
||||
@@ -128,7 +128,6 @@
|
||||
|
||||
<el-descriptions-item :span="3" label="库">{{ detailDialog.data.db }}</el-descriptions-item>
|
||||
<el-descriptions-item :span="3" label="备注">{{ detailDialog.data.remark }}</el-descriptions-item>
|
||||
<el-descriptions-item :span="3" label="工单流程key">{{ detailDialog.data?.flowProcdefKey }}</el-descriptions-item>
|
||||
<el-descriptions-item :span="3" label="SSH隧道">{{ detailDialog.data.sshTunnelMachineId > 0 ? '是' : '否' }} </el-descriptions-item>
|
||||
|
||||
<el-descriptions-item :span="2" label="创建时间">{{ dateFormat(detailDialog.data.createTime) }} </el-descriptions-item>
|
||||
@@ -180,7 +179,6 @@ const columns = ref([
|
||||
TableColumn.new('name', '名称'),
|
||||
TableColumn.new('host', 'host:port'),
|
||||
TableColumn.new('mode', 'mode'),
|
||||
TableColumn.new('flowProcdefKey', '关联流程'),
|
||||
TableColumn.new('remark', '备注'),
|
||||
TableColumn.new('code', '编号'),
|
||||
TableColumn.new('action', '操作').isSlot().setMinWidth(200).fixedRight().alignCenter(),
|
||||
|
||||
@@ -5,7 +5,7 @@ export type CmdExecProps = {
|
||||
id: number;
|
||||
db: number | string;
|
||||
cmd: any[];
|
||||
flowProcdefKey?: string;
|
||||
flowProcdef?: any;
|
||||
visible?: boolean;
|
||||
runSuccessFn?: Function;
|
||||
cancelFn?: Function;
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
<el-input type="textarea" disabled v-model="state.cmdStr" class="mt5" rows="5" />
|
||||
<el-input @keyup.enter="runCmd" ref="remarkInputRef" v-model="remark" placeholder="请输入执行备注" class="mt5" />
|
||||
|
||||
<div v-if="props.flowProcdefKey">
|
||||
<div v-if="props.flowProcdef">
|
||||
<el-divider content-position="left">审批节点</el-divider>
|
||||
<procdef-tasks :procdef-key="props.flowProcdefKey" />
|
||||
<procdef-tasks :procdef="props.flowProcdef" />
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
@@ -32,7 +32,7 @@ const props = withDefaults(defineProps<CmdExecProps>(), {});
|
||||
const remarkInputRef = ref<InputInstance>();
|
||||
const state = reactive({
|
||||
dialogVisible: false,
|
||||
flowProcdefKey: '' as any,
|
||||
flowProcdef: null as any,
|
||||
cmdStr: '',
|
||||
remark: '',
|
||||
btnLoading: false,
|
||||
|
||||
@@ -13,9 +13,9 @@ export class RedisInst {
|
||||
db: number;
|
||||
|
||||
/**
|
||||
* 流程定义key,若存在则需要审批执行
|
||||
* 流程定义,若存在则需要审批执行
|
||||
*/
|
||||
flowProcdefKey: string;
|
||||
flowProcdef: any;
|
||||
|
||||
/**
|
||||
* 执行命令
|
||||
@@ -24,11 +24,11 @@ export class RedisInst {
|
||||
*/
|
||||
async runCmd(cmd: any[]) {
|
||||
// 工单流程定义存在,并且为写入命令时,弹窗输入工单相关信息并提交
|
||||
if (this.flowProcdefKey && writeCmd[cmd[0].toUpperCase()]) {
|
||||
if (this.flowProcdef && writeCmd[cmd[0].toUpperCase()]) {
|
||||
showCmdExecBox({
|
||||
id: this.id,
|
||||
db: this.db,
|
||||
flowProcdefKey: this.flowProcdefKey,
|
||||
flowProcdef: this.flowProcdef,
|
||||
cmd,
|
||||
});
|
||||
// 报错,阻止后续继续执行
|
||||
|
||||
@@ -7,13 +7,13 @@ require (
|
||||
gitee.com/liuzongyang/libpq v1.0.9
|
||||
github.com/buger/jsonparser v1.1.1
|
||||
github.com/emirpasic/gods v1.18.1
|
||||
github.com/gin-gonic/gin v1.9.1
|
||||
github.com/gin-gonic/gin v1.10.0
|
||||
github.com/glebarez/sqlite v1.11.0
|
||||
github.com/go-gormigrate/gormigrate/v2 v2.1.0
|
||||
github.com/go-ldap/ldap/v3 v3.4.8
|
||||
github.com/go-playground/locales v0.14.1
|
||||
github.com/go-playground/universal-translator v0.18.1
|
||||
github.com/go-playground/validator/v10 v10.14.0
|
||||
github.com/go-playground/validator/v10 v10.20.0
|
||||
github.com/go-sql-driver/mysql v1.8.1
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1
|
||||
github.com/google/uuid v1.6.0
|
||||
@@ -28,12 +28,12 @@ require (
|
||||
github.com/pquerna/otp v1.4.0
|
||||
github.com/redis/go-redis/v9 v9.5.1
|
||||
github.com/robfig/cron/v3 v3.0.1 // 定时任务
|
||||
github.com/sijms/go-ora/v2 v2.8.13
|
||||
github.com/stretchr/testify v1.8.4
|
||||
github.com/sijms/go-ora/v2 v2.8.16
|
||||
github.com/stretchr/testify v1.9.0
|
||||
github.com/veops/go-ansiterm v0.0.5
|
||||
go.mongodb.org/mongo-driver v1.15.0 // mongo
|
||||
golang.org/x/crypto v0.22.0 // ssh
|
||||
golang.org/x/oauth2 v0.19.0
|
||||
golang.org/x/crypto v0.23.0 // ssh
|
||||
golang.org/x/oauth2 v0.20.0
|
||||
golang.org/x/sync v0.7.0
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
@@ -46,13 +46,15 @@ require (
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect
|
||||
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
|
||||
github.com/bytedance/sonic v1.9.1 // indirect
|
||||
github.com/bytedance/sonic v1.11.6 // indirect
|
||||
github.com/bytedance/sonic/loader v0.1.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
|
||||
github.com/cloudwego/base64x v0.1.4 // indirect
|
||||
github.com/cloudwego/iasm v0.2.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/glebarez/go-sqlite v1.21.2 // indirect
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.5 // indirect
|
||||
@@ -67,36 +69,36 @@ require (
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/compress v1.16.5 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.4 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
|
||||
github.com/kr/fs v0.1.0 // indirect
|
||||
github.com/leodido/go-urn v1.2.4 // indirect
|
||||
github.com/mattn/go-isatty v0.0.19 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.15 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.17 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/montanaflynn/stats v0.7.0 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.0.8 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
github.com/rivo/uniseg v0.4.3 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/tjfoc/gmsm v1.4.1 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.2.11 // indirect
|
||||
github.com/ugorji/go/codec v1.2.12 // indirect
|
||||
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
||||
github.com/xdg-go/scram v1.1.2 // indirect
|
||||
github.com/xdg-go/stringprep v1.0.4 // indirect
|
||||
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
|
||||
golang.org/x/arch v0.3.0 // indirect
|
||||
golang.org/x/arch v0.8.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20230519143937-03e91628a987 // indirect
|
||||
golang.org/x/image v0.13.0 // indirect
|
||||
golang.org/x/net v0.22.0 // indirect
|
||||
golang.org/x/sys v0.19.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
golang.org/x/net v0.25.0 // indirect
|
||||
golang.org/x/sys v0.20.0 // indirect
|
||||
golang.org/x/text v0.15.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20230131230820-1c016267d619 // indirect
|
||||
google.golang.org/grpc v1.52.3 // indirect
|
||||
google.golang.org/protobuf v1.31.0 // indirect
|
||||
google.golang.org/protobuf v1.34.1 // indirect
|
||||
modernc.org/libc v1.22.5 // indirect
|
||||
modernc.org/mathutil v1.5.0 // indirect
|
||||
modernc.org/memory v1.5.0 // indirect
|
||||
|
||||
@@ -95,9 +95,9 @@ func (d *Db) ExecSql(rc *req.Ctx) {
|
||||
dbId := getDbId(rc)
|
||||
dbConn, err := d.DbApp.GetDbConn(dbId, form.Db)
|
||||
biz.ErrIsNil(err)
|
||||
biz.ErrIsNilAppendErr(d.TagApp.CanAccess(rc.GetLoginAccount().Id, dbConn.Info.TagPath...), "%s")
|
||||
biz.ErrIsNilAppendErr(d.TagApp.CanAccess(rc.GetLoginAccount().Id, dbConn.Info.CodePath...), "%s")
|
||||
|
||||
global.EventBus.Publish(rc.MetaCtx, event.EventTopicResourceOp, dbConn.Info.TagPath[0])
|
||||
global.EventBus.Publish(rc.MetaCtx, event.EventTopicResourceOp, dbConn.Info.CodePath[0])
|
||||
|
||||
sqlBytes, err := base64.StdEncoding.DecodeString(form.Sql)
|
||||
biz.ErrIsNilAppendErr(err, "sql解码失败: %s")
|
||||
@@ -172,10 +172,8 @@ func (d *Db) ExecSqlFile(rc *req.Ctx) {
|
||||
clientId := rc.Query("clientId")
|
||||
|
||||
dbConn, err := d.DbApp.GetDbConn(dbId, dbName)
|
||||
// 开启流程审批时,执行文件暂时还未处理
|
||||
biz.IsTrue(dbConn.Info.FlowProcdefKey == "", "该库已开启流程审批,暂不支持该操作")
|
||||
biz.ErrIsNil(err)
|
||||
biz.ErrIsNilAppendErr(d.TagApp.CanAccess(rc.GetLoginAccount().Id, dbConn.Info.TagPath...), "%s")
|
||||
biz.ErrIsNilAppendErr(d.TagApp.CanAccess(rc.GetLoginAccount().Id, dbConn.Info.CodePath...), "%s")
|
||||
rc.ReqParam = fmt.Sprintf("filename: %s -> %s", filename, dbConn.Info.GetLogDesc())
|
||||
|
||||
defer func() {
|
||||
@@ -243,7 +241,7 @@ func (d *Db) ExecSqlFile(rc *req.Ctx) {
|
||||
}
|
||||
dbConn, err = d.DbApp.GetDbConn(dbId, stmtUse.DBName.String())
|
||||
biz.ErrIsNil(err)
|
||||
biz.ErrIsNilAppendErr(d.TagApp.CanAccess(laId, dbConn.Info.TagPath...), "%s")
|
||||
biz.ErrIsNilAppendErr(d.TagApp.CanAccess(laId, dbConn.Info.CodePath...), "%s")
|
||||
execReq.DbConn = dbConn
|
||||
}
|
||||
// 需要记录执行记录
|
||||
|
||||
@@ -27,7 +27,7 @@ type DbBackup struct {
|
||||
func (d *DbBackup) GetPageList(rc *req.Ctx) {
|
||||
dbId := uint64(rc.PathParamInt("dbId"))
|
||||
biz.IsTrue(dbId > 0, "无效的 dbId: %v", dbId)
|
||||
db, err := d.dbApp.GetById(dbId, "db_instance_id", "database")
|
||||
db, err := d.dbApp.GetById(dbId)
|
||||
biz.ErrIsNilAppendErr(err, "获取数据库信息失败: %v")
|
||||
|
||||
queryCond, page := req.BindQueryAndPage[*entity.DbBackupQuery](rc, new(entity.DbBackupQuery))
|
||||
@@ -49,7 +49,7 @@ func (d *DbBackup) Create(rc *req.Ctx) {
|
||||
|
||||
dbId := uint64(rc.PathParamInt("dbId"))
|
||||
biz.IsTrue(dbId > 0, "无效的 dbId: %v", dbId)
|
||||
db, err := d.dbApp.GetById(dbId, "instanceId")
|
||||
db, err := d.dbApp.GetById(dbId)
|
||||
biz.ErrIsNilAppendErr(err, "获取数据库信息失败: %v")
|
||||
jobs := make([]*entity.DbBackup, 0, len(dbNames))
|
||||
for _, dbName := range dbNames {
|
||||
@@ -147,7 +147,7 @@ func (d *DbBackup) GetDbNamesWithoutBackup(rc *req.Ctx) {
|
||||
func (d *DbBackup) GetHistoryPageList(rc *req.Ctx) {
|
||||
dbId := uint64(rc.PathParamInt("dbId"))
|
||||
biz.IsTrue(dbId > 0, "无效的 dbId: %v", dbId)
|
||||
db, err := d.dbApp.GetById(dbId, "db_instance_id", "database")
|
||||
db, err := d.dbApp.GetById(dbId, "instance_id", "database")
|
||||
biz.ErrIsNilAppendErr(err, "获取数据库信息失败: %v")
|
||||
|
||||
backupHistoryCond, page := req.BindQueryAndPage[*entity.DbBackupHistoryQuery](rc, new(entity.DbBackupHistoryQuery))
|
||||
|
||||
@@ -21,8 +21,6 @@ type DbListVO struct {
|
||||
Host string `json:"host"`
|
||||
Port int `json:"port"`
|
||||
|
||||
FlowProcdefKey string `json:"flowProcdefKey"`
|
||||
|
||||
CreateTime *time.Time `json:"createTime"`
|
||||
Creator *string `json:"creator"`
|
||||
CreatorId *int64 `json:"creatorId"`
|
||||
|
||||
@@ -186,13 +186,9 @@ func (d *dbAppImpl) GetDbConn(dbId uint64, dbName string) (*dbi.DbConn, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
di.TagPath = d.tagApp.ListTagPathByTypeAndCode(int8(tagentity.TagTypeDbName), db.Code)
|
||||
di.CodePath = d.tagApp.ListTagPathByTypeAndCode(int8(tagentity.TagTypeDbName), db.Code)
|
||||
di.Id = db.Id
|
||||
|
||||
if db.FlowProcdefKey != nil {
|
||||
di.FlowProcdefKey = *db.FlowProcdefKey
|
||||
}
|
||||
|
||||
checkDb := di.GetDatabase()
|
||||
if !strings.Contains(" "+db.Database+" ", " "+checkDb+" ") {
|
||||
return nil, errorx.NewBiz("未配置数据库【%s】的操作权限", dbName)
|
||||
|
||||
@@ -67,6 +67,7 @@ type dbSqlExecAppImpl struct {
|
||||
dbSqlExecRepo repository.DbSqlExec `inject:"DbSqlExecRepo"`
|
||||
|
||||
flowProcinstApp flowapp.Procinst `inject:"ProcinstApp"`
|
||||
flowProcdefApp flowapp.Procdef `inject:"ProcdefApp"`
|
||||
}
|
||||
|
||||
func createSqlExecRecord(ctx context.Context, execSqlReq *DbSqlExecReq) *entity.DbSqlExec {
|
||||
@@ -348,11 +349,11 @@ func (d *dbSqlExecAppImpl) doInsert(ctx context.Context, insert *sqlparser.Inser
|
||||
|
||||
func (d *dbSqlExecAppImpl) doExec(ctx context.Context, execSqlReq *DbSqlExecReq, dbSqlExecRecord *entity.DbSqlExec) (*DbSqlExecRes, error) {
|
||||
dbConn := execSqlReq.DbConn
|
||||
flowProcdefKey := dbConn.Info.FlowProcdefKey
|
||||
if flowProcdefKey != "" {
|
||||
|
||||
if flowProcdefId := d.flowProcdefApp.GetProcdefIdByCodePath(ctx, dbConn.Info.CodePath...); flowProcdefId != 0 {
|
||||
bizKey := stringx.Rand(24)
|
||||
// 如果该库关联了审批流程,则启动流程实例即可
|
||||
_, err := d.flowProcinstApp.StartProc(ctx, flowProcdefKey, &flowapp.StarProcParam{
|
||||
_, err := d.flowProcinstApp.StartProc(ctx, flowProcdefId, &flowapp.StarProcParam{
|
||||
BizType: DbSqlExecFlowBizType,
|
||||
BizKey: bizKey,
|
||||
Remark: dbSqlExecRecord.Remark,
|
||||
|
||||
@@ -47,8 +47,7 @@ type DbInfo struct {
|
||||
Params string
|
||||
Database string // 若有schema的库则为'database/scheam'格式
|
||||
|
||||
FlowProcdefKey string // 流程定义key
|
||||
TagPath []string
|
||||
CodePath []string
|
||||
SshTunnelMachineId int
|
||||
|
||||
Meta Meta
|
||||
@@ -56,7 +55,7 @@ type DbInfo struct {
|
||||
|
||||
// 获取记录日志的描述
|
||||
func (d *DbInfo) GetLogDesc() string {
|
||||
return fmt.Sprintf("DB[id=%d, tag=%s, name=%s, ip=%s:%d, database=%s]", d.Id, d.TagPath, d.Name, d.Host, d.Port, d.Database)
|
||||
return fmt.Sprintf("DB[id=%d, tag=%s, name=%s, ip=%s:%d, database=%s]", d.Id, d.CodePath, d.Name, d.Host, d.Port, d.Database)
|
||||
}
|
||||
|
||||
// 连接数据库
|
||||
|
||||
@@ -7,11 +7,10 @@ import (
|
||||
type Db struct {
|
||||
model.Model
|
||||
|
||||
Code string `orm:"column(code)" json:"code"`
|
||||
Name string `orm:"column(name)" json:"name"`
|
||||
Database string `orm:"column(database)" json:"database"`
|
||||
Remark string `json:"remark"`
|
||||
InstanceId uint64
|
||||
AuthCertName string `json:"authCertName"`
|
||||
FlowProcdefKey *string `json:"flowProcdefKey"` // 审批流-流程定义key(有值则说明关键操作需要进行审批执行),使用指针为了方便更新空字符串(取消流程审批)
|
||||
Code string `orm:"column(code)" json:"code"`
|
||||
Name string `orm:"column(name)" json:"name"`
|
||||
Database string `orm:"column(database)" json:"database"`
|
||||
Remark string `json:"remark"`
|
||||
InstanceId uint64
|
||||
AuthCertName string `json:"authCertName"`
|
||||
}
|
||||
|
||||
@@ -9,4 +9,6 @@ type Procdef struct {
|
||||
Tasks string `json:"tasks" binding:"required"` // 审批节点任务信息
|
||||
Status entity.ProcdefStatus `json:"status" binding:"required"`
|
||||
Remark string `json:"remark"`
|
||||
|
||||
CodePaths []string `json:"codePaths"`
|
||||
}
|
||||
|
||||
@@ -2,39 +2,50 @@ package api
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/flow/api/form"
|
||||
"mayfly-go/internal/flow/api/vo"
|
||||
"mayfly-go/internal/flow/application"
|
||||
"mayfly-go/internal/flow/domain/entity"
|
||||
tagapp "mayfly-go/internal/tag/application"
|
||||
tagentity "mayfly-go/internal/tag/domain/entity"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/req"
|
||||
"mayfly-go/pkg/utils/collx"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Procdef struct {
|
||||
ProcdefApp application.Procdef `inject:""`
|
||||
ProcdefApp application.Procdef `inject:""`
|
||||
TagTreeRelateApp tagapp.TagTreeRelate `inject:"TagTreeRelateApp"`
|
||||
}
|
||||
|
||||
func (p *Procdef) GetProcdefPage(rc *req.Ctx) {
|
||||
cond, page := req.BindQueryAndPage(rc, new(entity.Procdef))
|
||||
res, err := p.ProcdefApp.GetPageList(cond, page, new([]entity.Procdef))
|
||||
var procdefs []*vo.Procdef
|
||||
res, err := p.ProcdefApp.GetPageList(cond, page, &procdefs)
|
||||
biz.ErrIsNil(err)
|
||||
|
||||
p.TagTreeRelateApp.FillTagInfo(tagentity.TagRelateTypeFlowDef, collx.ArrayMap(procdefs, func(mvo *vo.Procdef) tagentity.IRelateTag {
|
||||
return mvo
|
||||
})...)
|
||||
|
||||
rc.ResData = res
|
||||
}
|
||||
|
||||
func (p *Procdef) GetProcdef(rc *req.Ctx) {
|
||||
defkey := rc.PathParam("key")
|
||||
biz.NotEmpty(defkey, "流程定义key不能为空")
|
||||
|
||||
procdef := &entity.Procdef{DefKey: defkey}
|
||||
biz.ErrIsNil(p.ProcdefApp.GetByCond(procdef), "该流程定义不存在")
|
||||
rc.ResData = procdef
|
||||
resourceType := rc.PathParamInt("resourceType")
|
||||
resourceCode := rc.PathParam("resourceCode")
|
||||
rc.ResData = p.ProcdefApp.GetProcdefByResource(rc.MetaCtx, int8(resourceType), resourceCode)
|
||||
}
|
||||
|
||||
func (a *Procdef) Save(rc *req.Ctx) {
|
||||
form := &form.Procdef{}
|
||||
procdef := req.BindJsonAndCopyTo(rc, form, new(entity.Procdef))
|
||||
rc.ReqParam = form
|
||||
biz.ErrIsNil(a.ProcdefApp.Save(rc.MetaCtx, procdef))
|
||||
biz.ErrIsNil(a.ProcdefApp.SaveProcdef(rc.MetaCtx, &application.SaveProcdefParam{
|
||||
Procdef: procdef,
|
||||
CodePaths: form.CodePaths,
|
||||
}))
|
||||
}
|
||||
|
||||
func (p *Procdef) Delete(rc *req.Ctx) {
|
||||
|
||||
15
server/internal/flow/api/vo/procdef.go
Normal file
15
server/internal/flow/api/vo/procdef.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package vo
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/flow/domain/entity"
|
||||
tagentity "mayfly-go/internal/tag/domain/entity"
|
||||
)
|
||||
|
||||
type Procdef struct {
|
||||
tagentity.RelateTags // 标签信息
|
||||
entity.Procdef
|
||||
}
|
||||
|
||||
func (p *Procdef) GetRelateId() uint64 {
|
||||
return p.Id
|
||||
}
|
||||
@@ -4,29 +4,47 @@ import (
|
||||
"context"
|
||||
"mayfly-go/internal/flow/domain/entity"
|
||||
"mayfly-go/internal/flow/domain/repository"
|
||||
tagapp "mayfly-go/internal/tag/application"
|
||||
tagentity "mayfly-go/internal/tag/domain/entity"
|
||||
"mayfly-go/pkg/base"
|
||||
"mayfly-go/pkg/errorx"
|
||||
"mayfly-go/pkg/model"
|
||||
)
|
||||
|
||||
type SaveProcdefParam struct {
|
||||
Procdef *entity.Procdef
|
||||
CodePaths []string
|
||||
}
|
||||
|
||||
type Procdef interface {
|
||||
base.App[*entity.Procdef]
|
||||
|
||||
GetPageList(condition *entity.Procdef, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error)
|
||||
|
||||
// 保存流程实例信息
|
||||
Save(ctx context.Context, def *entity.Procdef) error
|
||||
SaveProcdef(ctx context.Context, def *SaveProcdefParam) error
|
||||
|
||||
// 删除流程实例信息
|
||||
DeleteProcdef(ctx context.Context, defId uint64) error
|
||||
|
||||
// GetProcdefIdByCodePath 根据资源编号路径获取对应的流程定义id
|
||||
GetProcdefIdByCodePath(ctx context.Context, codePaths ...string) uint64
|
||||
|
||||
// GetProcdefByResource 根据资源获取对应的流程定义
|
||||
GetProcdefByResource(ctx context.Context, resourceType int8, resourceCode string) *entity.Procdef
|
||||
}
|
||||
|
||||
type procdefAppImpl struct {
|
||||
base.AppImpl[*entity.Procdef, repository.Procdef]
|
||||
|
||||
procinstApp Procinst `inject:"ProcinstApp"`
|
||||
|
||||
tagTreeApp tagapp.TagTree `inject:"TagTreeApp"`
|
||||
tagTreeRelateApp tagapp.TagTreeRelate `inject:"TagTreeRelateApp"`
|
||||
}
|
||||
|
||||
var _ (Procdef) = (*procdefAppImpl)(nil)
|
||||
|
||||
// 注入repo
|
||||
func (p *procdefAppImpl) InjectProcdefRepo(procdefRepo repository.Procdef) {
|
||||
p.Repo = procdefRepo
|
||||
@@ -36,24 +54,29 @@ func (p *procdefAppImpl) GetPageList(condition *entity.Procdef, pageParam *model
|
||||
return p.Repo.GetPageList(condition, pageParam, toEntity, orderBy...)
|
||||
}
|
||||
|
||||
func (p *procdefAppImpl) Save(ctx context.Context, def *entity.Procdef) error {
|
||||
func (p *procdefAppImpl) SaveProcdef(ctx context.Context, defParam *SaveProcdefParam) error {
|
||||
def := defParam.Procdef
|
||||
if err := entity.ProcdefStatusEnum.Valid(def.Status); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if def.Id == 0 {
|
||||
if p.GetByCond(&entity.Procdef{DefKey: def.DefKey}) == nil {
|
||||
return errorx.NewBiz("该流程实例key已存在")
|
||||
}
|
||||
return p.Insert(ctx, def)
|
||||
} else {
|
||||
// 防止误修改key
|
||||
def.DefKey = ""
|
||||
if err := p.canModify(def.Id); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// 防止误修改key
|
||||
def.DefKey = ""
|
||||
if err := p.canModify(def.Id); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return p.UpdateById(ctx, def)
|
||||
return p.Tx(ctx, func(ctx context.Context) error {
|
||||
return p.Save(ctx, def)
|
||||
}, func(ctx context.Context) error {
|
||||
return p.tagTreeRelateApp.RelateTag(ctx, tagentity.TagRelateTypeFlowDef, def.Id, defParam.CodePaths...)
|
||||
})
|
||||
}
|
||||
|
||||
func (p *procdefAppImpl) DeleteProcdef(ctx context.Context, defId uint64) error {
|
||||
@@ -63,6 +86,31 @@ func (p *procdefAppImpl) DeleteProcdef(ctx context.Context, defId uint64) error
|
||||
return p.DeleteById(ctx, defId)
|
||||
}
|
||||
|
||||
func (p *procdefAppImpl) GetProcdefIdByCodePath(ctx context.Context, codePaths ...string) uint64 {
|
||||
relateIds, err := p.tagTreeRelateApp.GetRelateIds(ctx, tagentity.TagRelateTypeFlowDef, codePaths...)
|
||||
if err != nil || len(relateIds) == 0 {
|
||||
return 0
|
||||
}
|
||||
return relateIds[len(relateIds)-1]
|
||||
}
|
||||
|
||||
func (p *procdefAppImpl) GetProcdefByResource(ctx context.Context, resourceType int8, resourceCode string) *entity.Procdef {
|
||||
resourceCodePaths := p.tagTreeApp.ListTagPathByTypeAndCode(resourceType, resourceCode)
|
||||
procdefId := p.GetProcdefIdByCodePath(ctx, resourceCodePaths...)
|
||||
if procdefId == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
procdef, err := p.GetById(procdefId)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
if procdef.Status == entity.ProcdefStatusDisable {
|
||||
return nil
|
||||
}
|
||||
return procdef
|
||||
}
|
||||
|
||||
// 判断该流程实例是否可以执行修改操作
|
||||
func (p *procdefAppImpl) canModify(prodefId uint64) error {
|
||||
if activeInstCount := p.procinstApp.CountByCond(&entity.Procinst{ProcdefId: prodefId, Status: entity.ProcinstStatusActive}); activeInstCount > 0 {
|
||||
|
||||
@@ -19,8 +19,8 @@ type Procinst interface {
|
||||
// 获取流程实例审批节点任务
|
||||
GetProcinstTasks(condition *entity.ProcinstTaskQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error)
|
||||
|
||||
// 根据流程定义key启动一个流程实例
|
||||
StartProc(ctx context.Context, procdefKey string, reqParam *StarProcParam) (*entity.Procinst, error)
|
||||
// StartProc 根据流程定义启动一个流程实例
|
||||
StartProc(ctx context.Context, procdefId uint64, reqParam *StarProcParam) (*entity.Procinst, error)
|
||||
|
||||
// 取消流程
|
||||
CancelProc(ctx context.Context, procinstId uint64) error
|
||||
@@ -42,6 +42,8 @@ type procinstAppImpl struct {
|
||||
procdefApp Procdef `inject:"ProcdefApp"`
|
||||
}
|
||||
|
||||
var _ (Procinst) = (*procinstAppImpl)(nil)
|
||||
|
||||
// 注入repo
|
||||
func (p *procinstAppImpl) InjectProcinstRepo(procinstRepo repository.Procinst) {
|
||||
p.Repo = procinstRepo
|
||||
@@ -55,10 +57,10 @@ func (p *procinstAppImpl) GetProcinstTasks(condition *entity.ProcinstTaskQuery,
|
||||
return p.procinstTaskRepo.GetPageList(condition, pageParam, toEntity, orderBy...)
|
||||
}
|
||||
|
||||
func (p *procinstAppImpl) StartProc(ctx context.Context, procdefKey string, reqParam *StarProcParam) (*entity.Procinst, error) {
|
||||
procdef := &entity.Procdef{DefKey: procdefKey}
|
||||
if err := p.procdefApp.GetByCond(procdef); err != nil {
|
||||
return nil, errorx.NewBiz("流程实例[%s]不存在", procdefKey)
|
||||
func (p *procinstAppImpl) StartProc(ctx context.Context, procdefId uint64, reqParam *StarProcParam) (*entity.Procinst, error) {
|
||||
procdef, err := p.procdefApp.GetById(procdefId)
|
||||
if err != nil {
|
||||
return nil, errorx.NewBiz("流程实例[%d]不存在", procdefId)
|
||||
}
|
||||
|
||||
if procdef.Status != entity.ProcdefStatusEnable {
|
||||
|
||||
@@ -18,7 +18,7 @@ func InitProcdefouter(router *gin.RouterGroup) {
|
||||
reqs := [...]*req.Conf{
|
||||
req.NewGet("", p.GetProcdefPage),
|
||||
|
||||
req.NewGet("/:key", p.GetProcdef),
|
||||
req.NewGet("/:resourceType/:resourceCode", p.GetProcdef),
|
||||
|
||||
req.NewPost("", p.Save).Log(req.NewLogSave("流程定义-保存")).RequiredPermissionCode("flow:procdef:save"),
|
||||
|
||||
|
||||
@@ -41,8 +41,8 @@ type MachineCronJobForm struct {
|
||||
Script string `json:"script" binding:"required"`
|
||||
Status int `json:"status" binding:"required"`
|
||||
SaveExecResType int `json:"saveExecResType" binding:"required"`
|
||||
MachineIds []uint64 `json:"machineIds"`
|
||||
Remark string `json:"remark"`
|
||||
CodePaths []string `json:"codePaths"`
|
||||
}
|
||||
|
||||
type MachineCmdConfForm struct {
|
||||
|
||||
@@ -152,7 +152,7 @@ func (m *Machine) GetProcess(rc *req.Ctx) {
|
||||
|
||||
cli, err := m.MachineApp.GetCli(GetMachineId(rc))
|
||||
biz.ErrIsNilAppendErr(err, "获取客户端连接失败: %s")
|
||||
biz.ErrIsNilAppendErr(m.TagApp.CanAccess(rc.GetLoginAccount().Id, cli.Info.TagPath...), "%s")
|
||||
biz.ErrIsNilAppendErr(m.TagApp.CanAccess(rc.GetLoginAccount().Id, cli.Info.CodePath...), "%s")
|
||||
|
||||
res, err := cli.Run(cmd)
|
||||
biz.ErrIsNilAppendErr(err, "获取进程信息失败: %s")
|
||||
@@ -166,7 +166,7 @@ func (m *Machine) KillProcess(rc *req.Ctx) {
|
||||
|
||||
cli, err := m.MachineApp.GetCli(GetMachineId(rc))
|
||||
biz.ErrIsNilAppendErr(err, "获取客户端连接失败: %s")
|
||||
biz.ErrIsNilAppendErr(m.TagApp.CanAccess(rc.GetLoginAccount().Id, cli.Info.TagPath...), "%s")
|
||||
biz.ErrIsNilAppendErr(m.TagApp.CanAccess(rc.GetLoginAccount().Id, cli.Info.CodePath...), "%s")
|
||||
|
||||
res, err := cli.Run("sudo kill -9 " + pid)
|
||||
biz.ErrIsNil(err, "终止进程失败: %s", res)
|
||||
@@ -194,9 +194,9 @@ func (m *Machine) WsSSH(g *gin.Context) {
|
||||
cli, err := m.MachineApp.NewCli(GetMachineAc(rc))
|
||||
biz.ErrIsNilAppendErr(err, mcm.GetErrorContentRn("获取客户端连接失败: %s"))
|
||||
defer cli.Close()
|
||||
biz.ErrIsNilAppendErr(m.TagApp.CanAccess(rc.GetLoginAccount().Id, cli.Info.TagPath...), mcm.GetErrorContentRn("%s"))
|
||||
biz.ErrIsNilAppendErr(m.TagApp.CanAccess(rc.GetLoginAccount().Id, cli.Info.CodePath...), mcm.GetErrorContentRn("%s"))
|
||||
|
||||
global.EventBus.Publish(rc.MetaCtx, event.EventTopicResourceOp, cli.Info.TagPath[0])
|
||||
global.EventBus.Publish(rc.MetaCtx, event.EventTopicResourceOp, cli.Info.CodePath[0])
|
||||
|
||||
cols := rc.QueryIntDefault("cols", 80)
|
||||
rows := rc.QueryIntDefault("rows", 32)
|
||||
|
||||
@@ -5,28 +5,37 @@ import (
|
||||
"mayfly-go/internal/machine/api/vo"
|
||||
"mayfly-go/internal/machine/application"
|
||||
"mayfly-go/internal/machine/domain/entity"
|
||||
tagapp "mayfly-go/internal/tag/application"
|
||||
tagentity "mayfly-go/internal/tag/domain/entity"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/req"
|
||||
"mayfly-go/pkg/scheduler"
|
||||
"mayfly-go/pkg/utils/collx"
|
||||
)
|
||||
|
||||
type MachineCronJob struct {
|
||||
MachineCronJobApp application.MachineCronJob `inject:""`
|
||||
TagTreeRelateApp tagapp.TagTreeRelate `inject:"TagTreeRelateApp"`
|
||||
}
|
||||
|
||||
func (m *MachineCronJob) MachineCronJobs(rc *req.Ctx) {
|
||||
cond, pageParam := req.BindQueryAndPage(rc, new(entity.MachineCronJob))
|
||||
|
||||
vos := new([]*vo.MachineCronJobVO)
|
||||
pageRes, err := m.MachineCronJobApp.GetPageList(cond, pageParam, vos)
|
||||
var vos []*vo.MachineCronJobVO
|
||||
pageRes, err := m.MachineCronJobApp.GetPageList(cond, pageParam, &vos)
|
||||
biz.ErrIsNil(err)
|
||||
for _, mcj := range *vos {
|
||||
|
||||
for _, mcj := range vos {
|
||||
mcj.Running = scheduler.ExistKey(mcj.Key)
|
||||
}
|
||||
|
||||
m.TagTreeRelateApp.FillTagInfo(tagentity.TagRelateTypeMachineCronJob, collx.ArrayMap(vos, func(mvo *vo.MachineCronJobVO) tagentity.IRelateTag {
|
||||
return mvo
|
||||
})...)
|
||||
|
||||
rc.ResData = pageRes
|
||||
}
|
||||
|
||||
@@ -35,11 +44,11 @@ func (m *MachineCronJob) Save(rc *req.Ctx) {
|
||||
mcj := req.BindJsonAndCopyTo[*entity.MachineCronJob](rc, jobForm, new(entity.MachineCronJob))
|
||||
rc.ReqParam = jobForm
|
||||
|
||||
cronJobId, err := m.MachineCronJobApp.SaveMachineCronJob(rc.MetaCtx, mcj)
|
||||
err := m.MachineCronJobApp.SaveMachineCronJob(rc.MetaCtx, &application.SaveMachineCronJobParam{
|
||||
CronJob: mcj,
|
||||
CodePaths: jobForm.CodePaths,
|
||||
})
|
||||
biz.ErrIsNil(err)
|
||||
|
||||
// 关联机器
|
||||
m.MachineCronJobApp.CronJobRelateMachines(rc.MetaCtx, cronJobId, jobForm.MachineIds)
|
||||
}
|
||||
|
||||
func (m *MachineCronJob) Delete(rc *req.Ctx) {
|
||||
@@ -54,14 +63,6 @@ func (m *MachineCronJob) Delete(rc *req.Ctx) {
|
||||
}
|
||||
}
|
||||
|
||||
func (m *MachineCronJob) GetRelateMachineIds(rc *req.Ctx) {
|
||||
rc.ResData = m.MachineCronJobApp.GetRelateMachineIds(uint64(rc.QueryIntDefault("cronJobId", -1)))
|
||||
}
|
||||
|
||||
func (m *MachineCronJob) GetRelateCronJobIds(rc *req.Ctx) {
|
||||
rc.ResData = m.MachineCronJobApp.GetRelateMachineIds(uint64(rc.QueryIntDefault("machineId", -1)))
|
||||
}
|
||||
|
||||
func (m *MachineCronJob) RunCronJob(rc *req.Ctx) {
|
||||
cronJobKey := rc.PathParam("key")
|
||||
biz.NotEmpty(cronJobKey, "cronJob key不能为空")
|
||||
|
||||
@@ -62,7 +62,7 @@ func (m *MachineScript) RunMachineScript(rc *req.Ctx) {
|
||||
}
|
||||
cli, err := m.MachineApp.GetCliByAc(ac)
|
||||
biz.ErrIsNilAppendErr(err, "获取客户端连接失败: %s")
|
||||
biz.ErrIsNilAppendErr(m.TagApp.CanAccess(rc.GetLoginAccount().Id, cli.Info.TagPath...), "%s")
|
||||
biz.ErrIsNilAppendErr(m.TagApp.CanAccess(rc.GetLoginAccount().Id, cli.Info.CodePath...), "%s")
|
||||
|
||||
res, err := cli.Run(script)
|
||||
// 记录请求参数
|
||||
|
||||
@@ -46,6 +46,8 @@ type MachineScriptVO struct {
|
||||
|
||||
// 机器记录任务
|
||||
type MachineCronJobVO struct {
|
||||
tagentity.RelateTags // 标签信息
|
||||
|
||||
Id uint64 `json:"id"`
|
||||
Key string `json:"key"`
|
||||
Name string `json:"name"`
|
||||
@@ -57,6 +59,10 @@ type MachineCronJobVO struct {
|
||||
Running bool `json:"running" gorm:"-"` // 是否运行中
|
||||
}
|
||||
|
||||
func (mcj *MachineCronJobVO) GetRelateId() uint64 {
|
||||
return mcj.Id
|
||||
}
|
||||
|
||||
type MachineFileVO struct {
|
||||
Id *int64 `json:"id"`
|
||||
Name *string `json:"name"`
|
||||
|
||||
@@ -341,7 +341,7 @@ func (m *machineAppImpl) toMi(me *entity.Machine, authCert *tagentity.ResourceAu
|
||||
mi.Name = me.Name
|
||||
mi.Ip = me.Ip
|
||||
mi.Port = me.Port
|
||||
mi.TagPath = m.tagApp.ListTagPathByTypeAndCode(int8(tagentity.TagTypeMachineAuthCert), authCert.Name)
|
||||
mi.CodePath = m.tagApp.ListTagPathByTypeAndCode(int8(tagentity.TagTypeMachineAuthCert), authCert.Name)
|
||||
mi.EnableRecorder = me.EnableRecorder
|
||||
mi.Protocol = me.Protocol
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ type MachineCmdConf interface {
|
||||
|
||||
DeleteCmdConf(ctx context.Context, id uint64) error
|
||||
|
||||
GetCmdConfsByMachineTags(tagPaths ...string) []*MachineCmd
|
||||
GetCmdConfsByMachineTags(ctx context.Context, tagPaths ...string) []*MachineCmd
|
||||
}
|
||||
|
||||
type machineCmdConfAppImpl struct {
|
||||
@@ -71,9 +71,9 @@ func (m *machineCmdConfAppImpl) DeleteCmdConf(ctx context.Context, id uint64) er
|
||||
})
|
||||
}
|
||||
|
||||
func (m *machineCmdConfAppImpl) GetCmdConfsByMachineTags(tagPaths ...string) []*MachineCmd {
|
||||
func (m *machineCmdConfAppImpl) GetCmdConfsByMachineTags(ctx context.Context, tagPaths ...string) []*MachineCmd {
|
||||
var cmds []*MachineCmd
|
||||
cmdConfIds, err := m.tagTreeRelateApp.GetRelateIds(tagentity.TagRelateTypeMachineCmd, tagPaths...)
|
||||
cmdConfIds, err := m.tagTreeRelateApp.GetRelateIds(ctx, tagentity.TagRelateTypeMachineCmd, tagPaths...)
|
||||
if err != nil {
|
||||
logx.Errorf("获取命令配置信息失败: %s", err.Error())
|
||||
return cmds
|
||||
|
||||
@@ -4,6 +4,8 @@ import (
|
||||
"context"
|
||||
"mayfly-go/internal/machine/domain/entity"
|
||||
"mayfly-go/internal/machine/domain/repository"
|
||||
tagapp "mayfly-go/internal/tag/application"
|
||||
tagentity "mayfly-go/internal/tag/domain/entity"
|
||||
"mayfly-go/pkg/base"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/errorx"
|
||||
@@ -17,6 +19,11 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type SaveMachineCronJobParam struct {
|
||||
CronJob *entity.MachineCronJob
|
||||
CodePaths []string
|
||||
}
|
||||
|
||||
type MachineCronJob interface {
|
||||
base.App[*entity.MachineCronJob]
|
||||
|
||||
@@ -26,22 +33,10 @@ type MachineCronJob interface {
|
||||
// 获取分页执行结果列表
|
||||
GetExecPageList(condition *entity.MachineCronJobExec, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error)
|
||||
|
||||
SaveMachineCronJob(ctx context.Context, entity *entity.MachineCronJob) (uint64, error)
|
||||
SaveMachineCronJob(ctx context.Context, param *SaveMachineCronJobParam) error
|
||||
|
||||
Delete(ctx context.Context, id uint64)
|
||||
|
||||
// 获取计划任务关联的机器列表id
|
||||
GetRelateMachineIds(cronJobId uint64) []uint64
|
||||
|
||||
// 获取机器关联的计划任务列表
|
||||
GetRelateCronJobIds(machineId uint64) []uint64
|
||||
|
||||
// 计划任务关联机器
|
||||
CronJobRelateMachines(ctx context.Context, cronJobId uint64, machineIds []uint64)
|
||||
|
||||
// 机器关联计划任务
|
||||
MachineRelateCronJobs(ctx context.Context, machineId uint64, cronJobs []uint64)
|
||||
|
||||
// 初始化计划任务
|
||||
InitCronJob()
|
||||
|
||||
@@ -53,11 +48,15 @@ type MachineCronJob interface {
|
||||
type machineCronJobAppImpl struct {
|
||||
base.AppImpl[*entity.MachineCronJob, repository.MachineCronJob]
|
||||
|
||||
machineCronJobRelateRepo repository.MachineCronJobRelate `inject:"MachineCronJobRelateRepo"`
|
||||
machineCronJobExecRepo repository.MachineCronJobExec `inject:"MachineCronJobExecRepo"`
|
||||
machineApp Machine `inject:"MachineApp"`
|
||||
machineCronJobExecRepo repository.MachineCronJobExec `inject:"MachineCronJobExecRepo"`
|
||||
machineApp Machine `inject:"MachineApp"`
|
||||
|
||||
tagTreeApp tagapp.TagTree `inject:"TagTreeApp"`
|
||||
tagTreeRelateApp tagapp.TagTreeRelate `inject:"TagTreeRelateApp"`
|
||||
}
|
||||
|
||||
var _ (MachineCronJob) = (*machineCronJobAppImpl)(nil)
|
||||
|
||||
// 注入MachineCronJobRepo
|
||||
func (m *machineCronJobAppImpl) InjectMachineCronJobRepo(repo repository.MachineCronJob) {
|
||||
m.Repo = repo
|
||||
@@ -74,79 +73,36 @@ func (m *machineCronJobAppImpl) GetExecPageList(condition *entity.MachineCronJob
|
||||
}
|
||||
|
||||
// 保存机器任务信息
|
||||
func (m *machineCronJobAppImpl) SaveMachineCronJob(ctx context.Context, mcj *entity.MachineCronJob) (uint64, error) {
|
||||
// 更新操作
|
||||
if mcj.Id != 0 {
|
||||
m.UpdateById(ctx, mcj)
|
||||
cj, err := m.GetById(mcj.Id)
|
||||
func (m *machineCronJobAppImpl) SaveMachineCronJob(ctx context.Context, param *SaveMachineCronJobParam) error {
|
||||
mcj := param.CronJob
|
||||
|
||||
// 赋值cron job key
|
||||
if mcj.Id == 0 {
|
||||
mcj.Key = stringx.Rand(16)
|
||||
} else {
|
||||
oldMcj, err := m.GetById(mcj.Id)
|
||||
if err != nil {
|
||||
return 0, errorx.NewBiz("该任务不存在")
|
||||
return errorx.NewBiz("该计划任务不存在")
|
||||
}
|
||||
// 处理最新的计划任务
|
||||
m.addCronJob(cj)
|
||||
return mcj.Id, nil
|
||||
mcj.Key = oldMcj.Key
|
||||
}
|
||||
|
||||
err := m.Tx(ctx, func(ctx context.Context) error {
|
||||
return m.Save(ctx, mcj)
|
||||
}, func(ctx context.Context) error {
|
||||
return m.tagTreeRelateApp.RelateTag(ctx, tagentity.TagRelateTypeMachineCronJob, mcj.Id, param.CodePaths...)
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m.addCronJob(mcj)
|
||||
if err := m.Insert(ctx, mcj); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return mcj.Id, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *machineCronJobAppImpl) Delete(ctx context.Context, id uint64) {
|
||||
m.DeleteById(ctx, id)
|
||||
m.machineCronJobExecRepo.DeleteByCond(ctx, &entity.MachineCronJobExec{CronJobId: id})
|
||||
m.machineCronJobRelateRepo.DeleteByCond(ctx, &entity.MachineCronJobRelate{CronJobId: id})
|
||||
}
|
||||
|
||||
func (m *machineCronJobAppImpl) GetRelateMachineIds(cronJobId uint64) []uint64 {
|
||||
return m.machineCronJobRelateRepo.GetMachineIds(cronJobId)
|
||||
}
|
||||
|
||||
func (m *machineCronJobAppImpl) GetRelateCronJobIds(machineId uint64) []uint64 {
|
||||
return m.machineCronJobRelateRepo.GetCronJobIds(machineId)
|
||||
}
|
||||
|
||||
func (m *machineCronJobAppImpl) CronJobRelateMachines(ctx context.Context, cronJobId uint64, machineIds []uint64) {
|
||||
oldMachineIds := m.machineCronJobRelateRepo.GetMachineIds(cronJobId)
|
||||
addIds, delIds, _ := collx.ArrayCompare[uint64](machineIds, oldMachineIds)
|
||||
addVals := make([]*entity.MachineCronJobRelate, 0)
|
||||
|
||||
for _, addId := range addIds {
|
||||
addVals = append(addVals, &entity.MachineCronJobRelate{
|
||||
MachineId: addId,
|
||||
CronJobId: cronJobId,
|
||||
})
|
||||
}
|
||||
m.machineCronJobRelateRepo.BatchInsert(ctx, addVals)
|
||||
|
||||
for _, delId := range delIds {
|
||||
m.machineCronJobRelateRepo.DeleteByCond(ctx, &entity.MachineCronJobRelate{CronJobId: cronJobId, MachineId: delId})
|
||||
}
|
||||
}
|
||||
|
||||
func (m *machineCronJobAppImpl) MachineRelateCronJobs(ctx context.Context, machineId uint64, cronJobs []uint64) {
|
||||
if len(cronJobs) == 0 {
|
||||
m.machineCronJobRelateRepo.DeleteByCond(ctx, &entity.MachineCronJobRelate{MachineId: machineId})
|
||||
return
|
||||
}
|
||||
|
||||
oldCronIds := m.machineCronJobRelateRepo.GetCronJobIds(machineId)
|
||||
addIds, delIds, _ := collx.ArrayCompare[uint64](cronJobs, oldCronIds)
|
||||
addVals := make([]*entity.MachineCronJobRelate, 0)
|
||||
|
||||
for _, addId := range addIds {
|
||||
addVals = append(addVals, &entity.MachineCronJobRelate{
|
||||
MachineId: machineId,
|
||||
CronJobId: addId,
|
||||
})
|
||||
}
|
||||
m.machineCronJobRelateRepo.BatchInsert(ctx, addVals)
|
||||
|
||||
for _, delId := range delIds {
|
||||
m.machineCronJobRelateRepo.DeleteByCond(ctx, &entity.MachineCronJobRelate{CronJobId: delId, MachineId: machineId})
|
||||
}
|
||||
}
|
||||
|
||||
func (m *machineCronJobAppImpl) InitCronJob() {
|
||||
@@ -160,17 +116,16 @@ func (m *machineCronJobAppImpl) InitCronJob() {
|
||||
PageSize: 100,
|
||||
PageNum: 1,
|
||||
}
|
||||
cond := new(entity.MachineCronJob)
|
||||
cond.Status = entity.MachineCronJobStatusEnable
|
||||
mcjs := new([]entity.MachineCronJob)
|
||||
|
||||
pr, _ := m.GetPageList(cond, pageParam, mcjs)
|
||||
var mcjs []*entity.MachineCronJob
|
||||
cond := &entity.MachineCronJob{Status: entity.MachineCronJobStatusEnable}
|
||||
pr, _ := m.GetPageList(cond, pageParam, &mcjs)
|
||||
total := pr.Total
|
||||
add := 0
|
||||
|
||||
for {
|
||||
for _, mcj := range *mcjs {
|
||||
m.addCronJob(&mcj)
|
||||
for _, mcj := range mcjs {
|
||||
m.addCronJob(mcj)
|
||||
add++
|
||||
}
|
||||
if add >= int(total) {
|
||||
@@ -197,26 +152,24 @@ func (m *machineCronJobAppImpl) RunCronJob(key string) {
|
||||
// 不存在或禁用,则移除该任务
|
||||
if err != nil || cronJob.Status == entity.MachineCronJobStatusDisable {
|
||||
scheduler.RemoveByKey(key)
|
||||
return
|
||||
}
|
||||
|
||||
machienIds := m.machineCronJobRelateRepo.GetMachineIds(cronJob.Id)
|
||||
for _, machineId := range machienIds {
|
||||
go m.runCronJob0(machineId, cronJob)
|
||||
relateCodePaths := m.tagTreeRelateApp.GetTagPathsByRelate(tagentity.TagRelateTypeMachineCronJob, cronJob.Id)
|
||||
var machineTags []tagentity.TagTree
|
||||
m.tagTreeApp.ListByQuery(&tagentity.TagTreeQuery{CodePathLikes: relateCodePaths, Type: tagentity.TagTypeMachine}, &machineTags)
|
||||
machines, _ := m.machineApp.ListByCond(model.NewCond().In("code", collx.ArrayMap(machineTags, func(tag tagentity.TagTree) string {
|
||||
return tag.Code
|
||||
})), "id")
|
||||
|
||||
for _, machine := range machines {
|
||||
go m.runCronJob0(machine.Id, cronJob)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *machineCronJobAppImpl) addCronJob(mcj *entity.MachineCronJob) {
|
||||
var key string
|
||||
key := mcj.Key
|
||||
isDisable := mcj.Status == entity.MachineCronJobStatusDisable
|
||||
if mcj.Id == 0 {
|
||||
key = stringx.Rand(16)
|
||||
mcj.Key = key
|
||||
if isDisable {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
key = mcj.Key
|
||||
}
|
||||
|
||||
if isDisable {
|
||||
scheduler.RemoveByKey(key)
|
||||
@@ -227,6 +180,7 @@ func (m *machineCronJobAppImpl) addCronJob(mcj *entity.MachineCronJob) {
|
||||
m.RunCronJob(key)
|
||||
})
|
||||
}
|
||||
|
||||
func (m *machineCronJobAppImpl) runCronJob0(mid uint64, cronJob *entity.MachineCronJob) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
|
||||
@@ -26,7 +26,6 @@ import (
|
||||
)
|
||||
|
||||
type MachineFileOpParam struct {
|
||||
Ua *model.LoginAccount
|
||||
MachineId uint64 `json:"machineId" binding:"required" form:"machineId"`
|
||||
Protocol int `json:"protocol" binding:"required" form:"protocol"`
|
||||
AuthCertName string `json:"authCertName" binding:"required" form:"authCertName"` // 授权凭证
|
||||
@@ -221,7 +220,7 @@ func (m *machineFileAppImpl) MkDir(ctx context.Context, opParam *MachineFileOpPa
|
||||
if opParam.Protocol == entity.MachineProtocolRdp {
|
||||
path = m.GetRdpFilePath(contextx.GetLoginAccount(ctx), path)
|
||||
os.MkdirAll(path, os.ModePerm)
|
||||
return nil, nil
|
||||
return &mcm.MachineInfo{Name: opParam.AuthCertName, Ip: opParam.AuthCertName}, nil
|
||||
}
|
||||
|
||||
mi, sftpCli, err := m.GetMachineSftpCli(opParam)
|
||||
@@ -238,8 +237,11 @@ func (m *machineFileAppImpl) CreateFile(ctx context.Context, opParam *MachineFil
|
||||
if opParam.Protocol == entity.MachineProtocolRdp {
|
||||
path = m.GetRdpFilePath(contextx.GetLoginAccount(ctx), path)
|
||||
file, err := os.Create(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
return nil, err
|
||||
return &mcm.MachineInfo{Name: opParam.AuthCertName, Ip: opParam.AuthCertName}, err
|
||||
}
|
||||
|
||||
mi, sftpCli, err := m.GetMachineSftpCli(opParam)
|
||||
@@ -271,12 +273,12 @@ func (m *machineFileAppImpl) WriteFileContent(ctx context.Context, opParam *Mach
|
||||
if opParam.Protocol == entity.MachineProtocolRdp {
|
||||
path = m.GetRdpFilePath(contextx.GetLoginAccount(ctx), path)
|
||||
file, err := os.Create(path)
|
||||
defer file.Close()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
file.Write(content)
|
||||
return nil, err
|
||||
return &mcm.MachineInfo{Name: opParam.AuthCertName, Ip: opParam.AuthCertName}, err
|
||||
}
|
||||
|
||||
mi, sftpCli, err := m.GetMachineSftpCli(opParam)
|
||||
@@ -303,12 +305,12 @@ func (m *machineFileAppImpl) UploadFile(ctx context.Context, opParam *MachineFil
|
||||
if opParam.Protocol == entity.MachineProtocolRdp {
|
||||
path = m.GetRdpFilePath(contextx.GetLoginAccount(ctx), path)
|
||||
file, err := os.Create(path + filename)
|
||||
defer file.Close()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
io.Copy(file, reader)
|
||||
return nil, nil
|
||||
return &mcm.MachineInfo{Name: opParam.AuthCertName, Ip: opParam.AuthCertName}, nil
|
||||
}
|
||||
|
||||
mi, sftpCli, err := m.GetMachineSftpCli(opParam)
|
||||
@@ -364,7 +366,7 @@ func (m *machineFileAppImpl) UploadFiles(ctx context.Context, opParam *MachineFi
|
||||
}
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
return &mcm.MachineInfo{Name: opParam.AuthCertName, Ip: opParam.AuthCertName}, nil
|
||||
}
|
||||
|
||||
// 删除文件
|
||||
|
||||
@@ -89,7 +89,7 @@ func (m *machineTermOpAppImpl) TermConn(ctx context.Context, cli *mcm.Cli, wsCon
|
||||
LogCmd: cli.Info.EnableRecorder == 1,
|
||||
}
|
||||
|
||||
cmdConfs := m.machineCmdConfApp.GetCmdConfsByMachineTags(cli.Info.TagPath...)
|
||||
cmdConfs := m.machineCmdConfApp.GetCmdConfsByMachineTags(ctx, cli.Info.CodePath...)
|
||||
if len(cmdConfs) > 0 {
|
||||
createTsParam.CmdFilterFuncs = []mcm.CmdFilterFunc{func(cmd string) error {
|
||||
for _, cmdConf := range cmdConfs {
|
||||
|
||||
@@ -19,14 +19,6 @@ type MachineCronJob struct {
|
||||
SaveExecResType int `json:"saveExecResType"` // 记录执行结果类型
|
||||
}
|
||||
|
||||
// 计划任务与机器关联信息
|
||||
type MachineCronJobRelate struct {
|
||||
model.CreateModel
|
||||
|
||||
CronJobId uint64
|
||||
MachineId uint64
|
||||
}
|
||||
|
||||
// 机器任务执行记录
|
||||
type MachineCronJobExec struct {
|
||||
model.DeletedModel
|
||||
|
||||
@@ -12,14 +12,6 @@ type MachineCronJob interface {
|
||||
GetPageList(condition *entity.MachineCronJob, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error)
|
||||
}
|
||||
|
||||
type MachineCronJobRelate interface {
|
||||
base.Repo[*entity.MachineCronJobRelate]
|
||||
|
||||
GetMachineIds(cronJobId uint64) []uint64
|
||||
|
||||
GetCronJobIds(machineId uint64) []uint64
|
||||
}
|
||||
|
||||
type MachineCronJobExec interface {
|
||||
base.Repo[*entity.MachineCronJobExec]
|
||||
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
package persistence
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/machine/domain/entity"
|
||||
"mayfly-go/internal/machine/domain/repository"
|
||||
"mayfly-go/pkg/base"
|
||||
"mayfly-go/pkg/model"
|
||||
)
|
||||
|
||||
type machineCronJobRelateRepoImpl struct {
|
||||
base.RepoImpl[*entity.MachineCronJobRelate]
|
||||
}
|
||||
|
||||
func newMachineCronJobRelateRepo() repository.MachineCronJobRelate {
|
||||
return &machineCronJobRelateRepoImpl{base.RepoImpl[*entity.MachineCronJobRelate]{M: new(entity.MachineCronJobRelate)}}
|
||||
}
|
||||
|
||||
func (m *machineCronJobRelateRepoImpl) GetMachineIds(cronJobId uint64) []uint64 {
|
||||
var machineIds []uint64
|
||||
m.SelectByCondToAny(model.NewModelCond(&entity.MachineCronJobRelate{CronJobId: cronJobId}).Columns("machine_id"), &machineIds)
|
||||
return machineIds
|
||||
}
|
||||
|
||||
func (m *machineCronJobRelateRepoImpl) GetCronJobIds(machineId uint64) []uint64 {
|
||||
var cronJobIds []uint64
|
||||
m.SelectByCondToAny(model.NewModelCond(&entity.MachineCronJobRelate{MachineId: machineId}).Columns("cron_job_id"), &cronJobIds)
|
||||
return cronJobIds
|
||||
}
|
||||
@@ -10,7 +10,6 @@ func InitIoc() {
|
||||
ioc.Register(newMachineScriptRepo(), ioc.WithComponentName("MachineScriptRepo"))
|
||||
ioc.Register(newMachineCronJobRepo(), ioc.WithComponentName("MachineCronJobRepo"))
|
||||
ioc.Register(newMachineCronJobExecRepo(), ioc.WithComponentName("MachineCronJobExecRepo"))
|
||||
ioc.Register(newMachineCronJobRelateRepo(), ioc.WithComponentName("MachineCronJobRelateRepo"))
|
||||
ioc.Register(newMachineTermOpRepoImpl(), ioc.WithComponentName("MachineTermOpRepo"))
|
||||
ioc.Register(newMachineCmdConfRepo(), ioc.WithComponentName("MachineCmdConfRepo"))
|
||||
}
|
||||
|
||||
@@ -37,11 +37,4 @@ func Init() {
|
||||
me := event.Val.(*entity.Machine)
|
||||
return application.GetMachineScriptApp().DeleteByCond(ctx, &entity.MachineScript{MachineId: me.Id})
|
||||
})
|
||||
|
||||
global.EventBus.Subscribe(event.EventTopicDeleteMachine, "machineCronJob", func(ctx context.Context, event *eventbus.Event) error {
|
||||
me := event.Val.(*entity.Machine)
|
||||
var jobIds []uint64
|
||||
application.GetMachineCronJobApp().MachineRelateCronJobs(ctx, me.Id, jobIds)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ type MachineInfo struct {
|
||||
SshTunnelMachine *MachineInfo `json:"-"` // ssh隧道机器
|
||||
TempSshMachineId uint64 `json:"-"` // ssh隧道机器id,用于记录隧道机器id,连接出错后关闭隧道
|
||||
EnableRecorder int8 `json:"-"` // 是否启用终端回放记录
|
||||
TagPath []string `json:"tagPath"`
|
||||
CodePath []string `json:"codePath"`
|
||||
}
|
||||
|
||||
func (m *MachineInfo) UseSshTunnel() bool {
|
||||
|
||||
@@ -19,10 +19,6 @@ func InitMachineCronJobRouter(router *gin.RouterGroup) {
|
||||
// 获取机器任务列表
|
||||
req.NewGet("", cj.MachineCronJobs),
|
||||
|
||||
req.NewGet("/machine-ids", cj.GetRelateMachineIds),
|
||||
|
||||
req.NewGet("/cronjob-ids", cj.GetRelateCronJobIds),
|
||||
|
||||
req.NewPost("", cj.Save).Log(req.NewLogSave("保存机器计划任务")),
|
||||
|
||||
req.NewDelete(":ids", cj.Delete).Log(req.NewLogSave("删除机器计划任务")),
|
||||
|
||||
@@ -96,7 +96,7 @@ func (m *Mongo) Collections(rc *req.Ctx) {
|
||||
conn, err := m.MongoApp.GetMongoConn(m.GetMongoId(rc))
|
||||
biz.ErrIsNil(err)
|
||||
|
||||
global.EventBus.Publish(rc.MetaCtx, event.EventTopicResourceOp, conn.Info.TagPath[0])
|
||||
global.EventBus.Publish(rc.MetaCtx, event.EventTopicResourceOp, conn.Info.CodePath[0])
|
||||
|
||||
db := rc.Query("database")
|
||||
biz.NotEmpty(db, "database不能为空")
|
||||
|
||||
@@ -19,6 +19,6 @@ type Mongo struct {
|
||||
func (me *Mongo) ToMongoInfo(tagPath ...string) *mgm.MongoInfo {
|
||||
mongoInfo := new(mgm.MongoInfo)
|
||||
structx.Copy(mongoInfo, me)
|
||||
mongoInfo.TagPath = tagPath
|
||||
mongoInfo.CodePath = tagPath
|
||||
return mongoInfo
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ type MongoInfo struct {
|
||||
|
||||
Uri string `json:"-"`
|
||||
|
||||
TagPath []string `json:"tagPath"`
|
||||
CodePath []string `json:"codePath"`
|
||||
SshTunnelMachineId int `json:"-"` // ssh隧道机器id
|
||||
}
|
||||
|
||||
|
||||
@@ -16,10 +16,10 @@ func (r *Redis) RunCmd(rc *req.Ctx) {
|
||||
biz.IsTrue(len(cmdReq.Cmd) > 0, "redis命令不能为空")
|
||||
|
||||
redisConn := r.getRedisConn(rc)
|
||||
biz.ErrIsNilAppendErr(r.TagApp.CanAccess(rc.GetLoginAccount().Id, redisConn.Info.TagPath...), "%s")
|
||||
biz.ErrIsNilAppendErr(r.TagApp.CanAccess(rc.GetLoginAccount().Id, redisConn.Info.CodePath...), "%s")
|
||||
rc.ReqParam = collx.Kvs("redis", redisConn.Info, "cmd", cmdReq.Cmd)
|
||||
|
||||
global.EventBus.Publish(rc.MetaCtx, event.EventTopicResourceOp, redisConn.Info.TagPath[0])
|
||||
global.EventBus.Publish(rc.MetaCtx, event.EventTopicResourceOp, redisConn.Info.CodePath[0])
|
||||
|
||||
res, err := r.RedisApp.RunCmd(rc.MetaCtx, redisConn, runCmdParam)
|
||||
biz.ErrIsNil(err)
|
||||
|
||||
@@ -232,7 +232,7 @@ func (r *Redis) checkKeyAndGetRedisConn(rc *req.Ctx) (*rdm.RedisConn, string) {
|
||||
func (r *Redis) getRedisConn(rc *req.Ctx) *rdm.RedisConn {
|
||||
ri, err := r.RedisApp.GetRedisConn(getIdAndDbNum(rc))
|
||||
biz.ErrIsNil(err)
|
||||
biz.ErrIsNilAppendErr(r.TagApp.CanAccess(rc.GetLoginAccount().Id, ri.Info.TagPath...), "%s")
|
||||
biz.ErrIsNilAppendErr(r.TagApp.CanAccess(rc.GetLoginAccount().Id, ri.Info.CodePath...), "%s")
|
||||
return ri
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@ type Redis struct {
|
||||
Mode *string `json:"mode"`
|
||||
SshTunnelMachineId int `json:"sshTunnelMachineId"` // ssh隧道机器id
|
||||
Remark *string `json:"remark"`
|
||||
FlowProcdefKey string `json:"flowProcdefKey"`
|
||||
CreateTime *time.Time `json:"createTime"`
|
||||
Creator *string `json:"creator"`
|
||||
CreatorId *int64 `json:"creatorId"`
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
RedisRunWriteCmdFlowBizType = "redis_run_write_cmd_flow" // db sql exec flow biz type
|
||||
RedisRunWriteCmdFlowBizType = "redis_run_write_cmd_flow"
|
||||
)
|
||||
|
||||
func InitRedisFlowHandler() {
|
||||
|
||||
@@ -64,6 +64,7 @@ type redisAppImpl struct {
|
||||
base.AppImpl[*entity.Redis, repository.Redis]
|
||||
|
||||
tagApp tagapp.TagTree `inject:"TagTreeApp"`
|
||||
procdefApp flowapp.Procdef `inject:"ProcdefApp"`
|
||||
procinstApp flowapp.Procinst `inject:"ProcinstApp"`
|
||||
resourceAuthCertApp tagapp.ResourceAuthCert `inject:"ResourceAuthCertApp"`
|
||||
}
|
||||
@@ -150,7 +151,7 @@ func (r *redisAppImpl) SaveRedis(ctx context.Context, param *SaveRedisParam) err
|
||||
return errorx.NewBiz("该实例已存在")
|
||||
}
|
||||
// 如果修改了redis实例的库信息,则关闭旧库的连接
|
||||
if oldRedis.Db != re.Db || oldRedis.SshTunnelMachineId != re.SshTunnelMachineId || oldRedis.FlowProcdefKey != re.FlowProcdefKey {
|
||||
if oldRedis.Db != re.Db || oldRedis.SshTunnelMachineId != re.SshTunnelMachineId {
|
||||
for _, dbStr := range strings.Split(oldRedis.Db, ",") {
|
||||
db, _ := strconv.Atoi(dbStr)
|
||||
rdm.CloseConn(re.Id, db)
|
||||
@@ -238,8 +239,8 @@ func (r *redisAppImpl) RunCmd(ctx context.Context, redisConn *rdm.RedisConn, cmd
|
||||
}
|
||||
|
||||
// 开启工单流程,并且为写入命令,则开启对应审批流程
|
||||
if procdefKey := redisConn.Info.FlowProcdefKey; procdefKey != "" && rdm.IsWriteCmd(cmdParam.Cmd[0]) {
|
||||
_, err := r.procinstApp.StartProc(ctx, procdefKey, &flowapp.StarProcParam{
|
||||
if procdefId := r.procdefApp.GetProcdefIdByCodePath(ctx, redisConn.Info.CodePath...); procdefId != 0 && rdm.IsWriteCmd(cmdParam.Cmd[0]) {
|
||||
_, err := r.procinstApp.StartProc(ctx, procdefId, &flowapp.StarProcParam{
|
||||
BizType: RedisRunWriteCmdFlowBizType,
|
||||
BizKey: stringx.Rand(24),
|
||||
BizForm: jsonx.ToStr(cmdParam),
|
||||
|
||||
@@ -17,7 +17,6 @@ type Redis struct {
|
||||
Db string `orm:"column(database)" json:"db"`
|
||||
SshTunnelMachineId int `orm:"column(ssh_tunnel_machine_id)" json:"sshTunnelMachineId"` // ssh隧道机器id
|
||||
Remark string
|
||||
FlowProcdefKey *string `json:"flowProcdefKey"` // 审批流-流程定义key(有值则说明关键操作需要进行审批执行),使用指针为了方便更新空字符串(取消流程审批)
|
||||
}
|
||||
|
||||
// ToRedisInfo 转换为redisInfo进行连接
|
||||
@@ -27,6 +26,6 @@ func (r *Redis) ToRedisInfo(db int, authCert *tagentity.ResourceAuthCert, tagPat
|
||||
redisInfo.Username = authCert.Username
|
||||
redisInfo.Password = authCert.Ciphertext
|
||||
redisInfo.Db = db
|
||||
redisInfo.TagPath = tagPath
|
||||
redisInfo.CodePath = tagPath
|
||||
return redisInfo
|
||||
}
|
||||
|
||||
@@ -32,10 +32,8 @@ type RedisInfo struct {
|
||||
Password string `json:"-"`
|
||||
|
||||
Name string `json:"-"`
|
||||
TagPath []string `json:"tagPath"`
|
||||
CodePath []string `json:"codePath"`
|
||||
SshTunnelMachineId int `json:"-"`
|
||||
|
||||
FlowProcdefKey string `json:"flowProcdefKey"` // 工单流程定义key
|
||||
}
|
||||
|
||||
func (r *RedisInfo) Conn() (*RedisConn, error) {
|
||||
|
||||
@@ -12,6 +12,8 @@ import (
|
||||
"mayfly-go/pkg/utils/collx"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/may-fly/cast"
|
||||
)
|
||||
|
||||
type TagTree struct {
|
||||
@@ -20,8 +22,15 @@ type TagTree struct {
|
||||
}
|
||||
|
||||
func (p *TagTree) GetTagTree(rc *req.Ctx) {
|
||||
tagType := entity.TagType(rc.QueryInt("type"))
|
||||
accountTags := p.TagTreeApp.GetAccountTags(rc.GetLoginAccount().Id, &entity.TagTreeQuery{Type: tagType})
|
||||
tagTypesStr := rc.Query("type")
|
||||
var tagTypes []entity.TagType
|
||||
if tagTypesStr != "" {
|
||||
tagTypes = collx.ArrayMap[string, entity.TagType](strings.Split(tagTypesStr, ","), func(val string) entity.TagType {
|
||||
return entity.TagType(cast.ToInt8(val))
|
||||
})
|
||||
}
|
||||
|
||||
accountTags := p.TagTreeApp.GetAccountTags(rc.GetLoginAccount().Id, &entity.TagTreeQuery{Types: tagTypes})
|
||||
if len(accountTags) == 0 {
|
||||
rc.ResData = []any{}
|
||||
return
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"mayfly-go/internal/common/consts"
|
||||
"mayfly-go/internal/tag/domain/entity"
|
||||
"mayfly-go/internal/tag/domain/repository"
|
||||
"mayfly-go/internal/tag/infrastructure/cache"
|
||||
"mayfly-go/pkg/base"
|
||||
"mayfly-go/pkg/contextx"
|
||||
"mayfly-go/pkg/errorx"
|
||||
@@ -103,6 +104,8 @@ type tagTreeAppImpl struct {
|
||||
tagTreeRelateApp TagTreeRelate `inject:"TagTreeRelateApp"`
|
||||
}
|
||||
|
||||
var _ (TagTree) = (*tagTreeAppImpl)(nil)
|
||||
|
||||
// 注入TagTreeRepo
|
||||
func (p *tagTreeAppImpl) InjectTagTreeRepo(tagTreeRepo repository.TagTree) {
|
||||
p.Repo = tagTreeRepo
|
||||
@@ -253,6 +256,7 @@ func (p *tagTreeAppImpl) RelateTagsByCodeAndType(ctx context.Context, param *Rel
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -361,7 +365,8 @@ func (p *tagTreeAppImpl) ListByQuery(condition *entity.TagTreeQuery, toEntity an
|
||||
|
||||
func (p *tagTreeAppImpl) GetAccountTags(accountId uint64, query *entity.TagTreeQuery) []*entity.TagTree {
|
||||
tagResourceQuery := &entity.TagTreeQuery{
|
||||
Type: query.Type,
|
||||
Type: query.Type,
|
||||
Types: query.Types,
|
||||
}
|
||||
|
||||
var tagResources []*entity.TagTree
|
||||
@@ -383,27 +388,7 @@ func (p *tagTreeAppImpl) GetAccountTags(accountId uint64, query *entity.TagTreeQ
|
||||
if len(accountTagPaths) == 0 {
|
||||
accountTagPaths = tagPaths
|
||||
} else {
|
||||
queryPaths := collx.ArrayFilter[string](tagPaths, func(tagPath string) bool {
|
||||
for _, acPath := range accountTagPaths {
|
||||
// 查询条件: a/b/ 有权的:a/ 查询结果应该是 a/b/
|
||||
if strings.HasPrefix(tagPath, acPath) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
acPaths := collx.ArrayFilter[string](accountTagPaths, func(acPath string) bool {
|
||||
for _, tagPath := range tagPaths {
|
||||
// 查询条件: a/ 有权的:a/b/ 查询结果应该是 a/b/
|
||||
if strings.HasPrefix(acPath, tagPath) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
accountTagPaths = append(queryPaths, acPaths...)
|
||||
accountTagPaths = filterCodePaths(accountTagPaths, tagPaths)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -441,7 +426,12 @@ func (p *tagTreeAppImpl) ListTagPathByTypeAndCode(resourceType int8, resourceCod
|
||||
}
|
||||
|
||||
func (p *tagTreeAppImpl) ListTagByAccountId(accountId uint64) []string {
|
||||
return p.tagTreeRelateApp.GetTagPathsByAccountId(accountId)
|
||||
tagPaths, err := cache.GetAccountTagPaths(accountId)
|
||||
if err != nil {
|
||||
tagPaths = p.tagTreeRelateApp.GetTagPathsByAccountId(accountId)
|
||||
cache.SaveAccountTagPaths(accountId, tagPaths)
|
||||
}
|
||||
return tagPaths
|
||||
}
|
||||
|
||||
func (p *tagTreeAppImpl) CanAccess(accountId uint64, tagPath ...string) error {
|
||||
@@ -547,3 +537,50 @@ func (p *tagTreeAppImpl) deleteByIds(ctx context.Context, tagIds []uint64) error
|
||||
// 删除与标签有关联信息的记录(如团队关联的标签等)
|
||||
return p.tagTreeRelateApp.DeleteByCond(ctx, model.NewCond().In("tag_id", tagIds))
|
||||
}
|
||||
|
||||
// filterCodePaths 根据账号拥有的标签路径以及指定的标签路径,过滤出符合查询条件的标签路径
|
||||
func filterCodePaths(accountTagPaths []string, tagPaths []string) []string {
|
||||
var res []string
|
||||
queryPaths := collx.ArrayFilter[string](tagPaths, func(tagPath string) bool {
|
||||
for _, acPath := range accountTagPaths {
|
||||
// 查询条件: a/b/ 有权的:a/ 查询结果应该是: a/b/
|
||||
if strings.HasPrefix(tagPath, acPath) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
acPaths := collx.ArrayFilter[string](accountTagPaths, func(acPath string) bool {
|
||||
for _, tagPath := range tagPaths {
|
||||
// 查询条件: a/ 有权的:a/b/ 查询结果应该是: a/b/,如果以a/去查可能会查出无权的 a/c/相关联的数据
|
||||
if strings.HasPrefix(acPath, tagPath) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
res = append(queryPaths, acPaths...)
|
||||
return collx.ArrayDeduplicate(res)
|
||||
}
|
||||
|
||||
// hasConflictPath 判断标签路径中是否存在冲突路径,如不能同时存在tag1/tag2/tag3 tag1/ tag1/tag2等,因为拥有父级标签则拥有所有子标签资源等信息
|
||||
func hasConflictPath(codePaths []string) bool {
|
||||
if len(codePaths) == 0 {
|
||||
return false
|
||||
}
|
||||
seen := make(map[string]bool)
|
||||
for _, str := range codePaths {
|
||||
parts := strings.Split(str, entity.CodePathSeparator)
|
||||
var prefix string
|
||||
for _, part := range parts {
|
||||
prefix += part + entity.CodePathSeparator
|
||||
if seen[prefix] {
|
||||
return true
|
||||
}
|
||||
}
|
||||
seen[str] = true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -2,9 +2,11 @@ package application
|
||||
|
||||
import (
|
||||
"context"
|
||||
"mayfly-go/internal/common/consts"
|
||||
"mayfly-go/internal/tag/domain/entity"
|
||||
"mayfly-go/internal/tag/domain/repository"
|
||||
"mayfly-go/pkg/base"
|
||||
"mayfly-go/pkg/contextx"
|
||||
"mayfly-go/pkg/errorx"
|
||||
"mayfly-go/pkg/model"
|
||||
"mayfly-go/pkg/utils/collx"
|
||||
@@ -17,7 +19,7 @@ type TagTreeRelate interface {
|
||||
RelateTag(ctx context.Context, relateType entity.TagRelateType, relateId uint64, tagCodePaths ...string) error
|
||||
|
||||
// GetRelateIds 根据标签路径获取对应关联的id
|
||||
GetRelateIds(relateType entity.TagRelateType, tagPaths ...string) ([]uint64, error)
|
||||
GetRelateIds(ctx context.Context, relateType entity.TagRelateType, tagPaths ...string) ([]uint64, error)
|
||||
|
||||
// GetTagPathsByAccountId 根据账号id获取该账号可操作的标签code路径
|
||||
GetTagPathsByAccountId(accountId uint64) []string
|
||||
@@ -45,10 +47,16 @@ func (p *tagTreeRelateAppImpl) InjectTagTreeRelateRepo(tagTreeRelateRepo reposit
|
||||
}
|
||||
|
||||
func (tr *tagTreeRelateAppImpl) RelateTag(ctx context.Context, relateType entity.TagRelateType, relateId uint64, tagCodePaths ...string) error {
|
||||
if hasConflictPath(tagCodePaths) {
|
||||
return errorx.NewBiz("存在冲突的编号路径")
|
||||
}
|
||||
|
||||
var tags []*entity.TagTree
|
||||
tr.tagTreeApp.ListByQuery(&entity.TagTreeQuery{CodePaths: tagCodePaths}, &tags)
|
||||
if len(tags) != len(tagCodePaths) {
|
||||
return errorx.NewBiz("存在错误标签路径")
|
||||
if len(tagCodePaths) > 0 {
|
||||
tr.tagTreeApp.ListByQuery(&entity.TagTreeQuery{CodePaths: tagCodePaths}, &tags)
|
||||
if len(tags) != len(tagCodePaths) {
|
||||
return errorx.NewBiz("存在错误标签路径")
|
||||
}
|
||||
}
|
||||
|
||||
oldRelates, _ := tr.ListByCond(&entity.TagTreeRelate{RelateType: relateType, RelateId: relateId})
|
||||
@@ -83,9 +91,15 @@ func (tr *tagTreeRelateAppImpl) RelateTag(ctx context.Context, relateType entity
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tr *tagTreeRelateAppImpl) GetRelateIds(relateType entity.TagRelateType, tagPaths ...string) ([]uint64, error) {
|
||||
func (tr *tagTreeRelateAppImpl) GetRelateIds(ctx context.Context, relateType entity.TagRelateType, tagPaths ...string) ([]uint64, error) {
|
||||
la := contextx.GetLoginAccount(ctx)
|
||||
canAccessTagPaths := tagPaths
|
||||
if la != nil && la.Id != consts.AdminId {
|
||||
canAccessTagPaths = filterCodePaths(tr.tagTreeApp.ListTagByAccountId(la.Id), tagPaths)
|
||||
}
|
||||
|
||||
poisibleTagPaths := make([]string, 0)
|
||||
for _, tagPath := range tagPaths {
|
||||
for _, tagPath := range canAccessTagPaths {
|
||||
// 追加可能关联的标签路径,如tagPath = tag1/tag2/1|xxx/,需要获取所有关联的自身及父标签(tag1/ tag1/tag2/ tag1/tag2/1|xxx)
|
||||
poisibleTagPaths = append(poisibleTagPaths, entity.GetAllCodePath(tagPath)...)
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"mayfly-go/internal/tag/domain/entity"
|
||||
"mayfly-go/internal/tag/domain/repository"
|
||||
"mayfly-go/internal/tag/infrastructure/cache"
|
||||
"mayfly-go/pkg/base"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/contextx"
|
||||
@@ -90,6 +91,12 @@ func (p *teamAppImpl) SaveTeam(ctx context.Context, saveParam *SaveTeamParam) er
|
||||
}
|
||||
}
|
||||
|
||||
// 删除该团队关联账号的标签缓存
|
||||
teamMembers, _ := p.teamMemberRepo.SelectByCond(&entity.TeamMember{TeamId: team.Id})
|
||||
for _, tm := range teamMembers {
|
||||
cache.DelAccountTagPaths(tm.AccountId)
|
||||
}
|
||||
|
||||
// 保存团队关联的标签信息
|
||||
return p.tagTreeRelateApp.RelateTag(ctx, entity.TagRelateTypeTeam, team.Id, saveParam.CodePaths...)
|
||||
}
|
||||
|
||||
@@ -14,8 +14,10 @@ type TagTreeRelate struct {
|
||||
type TagRelateType int8
|
||||
|
||||
const (
|
||||
TagRelateTypeTeam TagRelateType = 1 // 关联团队
|
||||
TagRelateTypeMachineCmd TagRelateType = 2 // 关联机器命令配置
|
||||
TagRelateTypeTeam TagRelateType = 1 // 关联团队
|
||||
TagRelateTypeMachineCmd TagRelateType = 2 // 关联机器命令配置
|
||||
TagRelateTypeMachineCronJob TagRelateType = 3 // 关联机器定时任务配置
|
||||
TagRelateTypeFlowDef TagRelateType = 4 // 关联流程定义
|
||||
)
|
||||
|
||||
// 关联标签信息,如果要实现填充关联标签信息,则结构体需要实现该接口
|
||||
|
||||
26
server/internal/tag/infrastructure/cache/tag_tree.go
vendored
Normal file
26
server/internal/tag/infrastructure/cache/tag_tree.go
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
global_cache "mayfly-go/pkg/cache"
|
||||
"time"
|
||||
)
|
||||
|
||||
const AccountTagsKey = "mayfly:tag:account:%d"
|
||||
|
||||
func SaveAccountTagPaths(accountId uint64, tags []string) error {
|
||||
return global_cache.Set(fmt.Sprintf(AccountTagsKey, accountId), tags, 30*time.Minute)
|
||||
}
|
||||
|
||||
func GetAccountTagPaths(accountId uint64) ([]string, error) {
|
||||
var res []string
|
||||
if !global_cache.Get(fmt.Sprintf(AccountTagsKey, accountId), &res) {
|
||||
return nil, errors.New("不存在该值")
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func DelAccountTagPaths(accountId uint64) {
|
||||
global_cache.Del(fmt.Sprintf(AccountTagsKey, accountId))
|
||||
}
|
||||
@@ -39,7 +39,7 @@ func (p *tagTreeRepoImpl) SelectByCondition(condition *entity.TagTreeQuery, toEn
|
||||
params = append(params, condition.Type)
|
||||
}
|
||||
if len(condition.Types) > 0 {
|
||||
sql = sql + " AND p.type = IN (?)"
|
||||
sql = sql + " AND p.type IN (?)"
|
||||
params = append(params, condition.Types)
|
||||
}
|
||||
if len(condition.CodePathLikes) > 0 {
|
||||
|
||||
@@ -36,9 +36,6 @@ func T2022() *gormigrate.Migration {
|
||||
if err := tx.AutoMigrate(&entity.MachineCronJobExec{}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tx.AutoMigrate(&entity.MachineCronJobRelate{}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := tx.AutoMigrate(&entity2.DbInstance{}); err != nil {
|
||||
return err
|
||||
|
||||
21
server/pkg/cache/str_cache.go
vendored
21
server/pkg/cache/str_cache.go
vendored
@@ -6,7 +6,10 @@ import (
|
||||
"mayfly-go/pkg/rediscli"
|
||||
"mayfly-go/pkg/utils/anyx"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/may-fly/cast"
|
||||
)
|
||||
|
||||
var tm *TimedCache
|
||||
@@ -54,7 +57,7 @@ func Get[T any](key string, valPtr T) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// 如果系统有设置redis信息,则使用redis存,否则存于本机内存。duration == -1则为永久缓存
|
||||
// SetStr 如果系统有设置redis信息,则使用redis存,否则存于本机内存。duration == -1则为永久缓存
|
||||
func SetStr(key, value string, duration time.Duration) error {
|
||||
if !UseRedisCache() {
|
||||
checkCache()
|
||||
@@ -63,7 +66,7 @@ func SetStr(key, value string, duration time.Duration) error {
|
||||
return rediscli.Set(key, value, duration)
|
||||
}
|
||||
|
||||
// 如果系统有设置redis信息,则使用redis存,否则存于本机内存。duration == -1则为永久缓存
|
||||
// Set 如果系统有设置redis信息,则使用redis存,否则存于本机内存。duration == -1则为永久缓存
|
||||
func Set(key string, value any, duration time.Duration) error {
|
||||
strVal := anyx.ToString(value)
|
||||
if !UseRedisCache() {
|
||||
@@ -83,6 +86,20 @@ func Del(key string) {
|
||||
rediscli.Del(key)
|
||||
}
|
||||
|
||||
// DelByKeyPrefix 根据key前缀删除满足前缀的所有缓存
|
||||
func DelByKeyPrefix(keyPrefix string) error {
|
||||
if !UseRedisCache() {
|
||||
checkCache()
|
||||
for key := range tm.Items() {
|
||||
if strings.HasPrefix(cast.ToString(key), keyPrefix) {
|
||||
tm.Delete(key)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return rediscli.DelByKeyPrefix(keyPrefix)
|
||||
}
|
||||
|
||||
func UseRedisCache() bool {
|
||||
return rediscli.GetCli() != nil
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import "fmt"
|
||||
|
||||
const (
|
||||
AppName = "mayfly-go"
|
||||
Version = "v1.8.2"
|
||||
Version = "v1.8.3"
|
||||
)
|
||||
|
||||
func GetAppInfo() string {
|
||||
|
||||
@@ -30,8 +30,21 @@ func Set(key string, val string, expiration time.Duration) error {
|
||||
return cli.Set(context.TODO(), key, val, expiration).Err()
|
||||
}
|
||||
|
||||
func Del(key string) {
|
||||
cli.Del(context.TODO(), key)
|
||||
// Del 删除key
|
||||
func Del(key string) (int64, error) {
|
||||
return cli.Del(context.TODO(), key).Result()
|
||||
}
|
||||
|
||||
// DelByKeyPrefix 根据key前缀删除key
|
||||
func DelByKeyPrefix(keyPrefix string) error {
|
||||
res, err := cli.Keys(context.TODO(), keyPrefix+"*").Result()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, key := range res {
|
||||
Del(key)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func HSet(key string, field string, val any) {
|
||||
|
||||
@@ -40,7 +40,6 @@ CREATE TABLE `t_db` (
|
||||
`remark` varchar(191) COLLATE utf8mb4_bin DEFAULT NULL,
|
||||
`instance_id` bigint unsigned NOT NULL,
|
||||
`auth_cert_name` varchar(36) NULL COMMENT '授权凭证名',
|
||||
`flow_procdef_key` varchar(64) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '审批流-流程定义key(有值则说明关键操作需要进行审批执行)',
|
||||
`create_time` datetime DEFAULT NULL,
|
||||
`creator_id` bigint DEFAULT NULL,
|
||||
`creator` varchar(191) COLLATE utf8mb4_bin DEFAULT NULL,
|
||||
@@ -474,19 +473,6 @@ CREATE TABLE `t_machine_cron_job_exec` (
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='机器计划任务执行记录';
|
||||
|
||||
DROP TABLE IF EXISTS `t_machine_cron_job_relate`;
|
||||
CREATE TABLE `t_machine_cron_job_relate` (
|
||||
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
|
||||
`cron_job_id` bigint DEFAULT NULL,
|
||||
`machine_id` bigint DEFAULT NULL,
|
||||
`creator_id` bigint DEFAULT NULL,
|
||||
`creator` varchar(32) DEFAULT NULL,
|
||||
`create_time` datetime DEFAULT NULL,
|
||||
`is_deleted` tinyint NOT NULL DEFAULT 0,
|
||||
`delete_time` datetime DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='机器计划任务关联表';
|
||||
|
||||
DROP TABLE IF EXISTS `t_machine_term_op`;
|
||||
CREATE TABLE `t_machine_term_op` (
|
||||
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'id',
|
||||
@@ -556,7 +542,6 @@ CREATE TABLE `t_redis` (
|
||||
`mode` varchar(32) DEFAULT NULL,
|
||||
`ssh_tunnel_machine_id` bigint(20) DEFAULT NULL COMMENT 'ssh隧道的机器id',
|
||||
`remark` varchar(125) DEFAULT NULL,
|
||||
`flow_procdef_key` varchar(100) DEFAULT NULL COMMENT '工单流程定义key',
|
||||
`creator` varchar(32) DEFAULT NULL,
|
||||
`creator_id` bigint(32) DEFAULT NULL,
|
||||
`create_time` datetime DEFAULT NULL,
|
||||
|
||||
3
server/resources/script/sql/v1.8/v1.8.3.sql
Normal file
3
server/resources/script/sql/v1.8/v1.8.3.sql
Normal file
@@ -0,0 +1,3 @@
|
||||
DROP TABLE t_machine_cron_job_relate;
|
||||
ALTER TABLE t_db DROP COLUMN flow_procdef_key;
|
||||
ALTER TABLE t_redis DROP COLUMN flow_procdef_key;
|
||||
Reference in New Issue
Block a user