mirror of
https://gitee.com/dromara/mayfly-go
synced 2025-11-02 23:40:24 +08:00
feat: redis支持工单流程审批
This commit is contained in:
@@ -17,7 +17,7 @@
|
||||
"countup.js": "^2.8.0",
|
||||
"cropperjs": "^1.6.1",
|
||||
"echarts": "^5.5.0",
|
||||
"element-plus": "^2.5.6",
|
||||
"element-plus": "^2.6.0",
|
||||
"js-base64": "^3.7.5",
|
||||
"jsencrypt": "^3.3.2",
|
||||
"lodash": "^4.17.21",
|
||||
|
||||
@@ -74,7 +74,7 @@
|
||||
trigger="click"
|
||||
>
|
||||
<div v-for="(item, index) in tableColumns" :key="index">
|
||||
<el-checkbox v-model="item.show" :label="item.label" :true-label="true" :false-label="false" />
|
||||
<el-checkbox v-model="item.show" :label="item.label" :true-value="true" :false-value="false" />
|
||||
</div>
|
||||
<template #reference>
|
||||
<el-button icon="Operation" circle :size="props.size"></el-button>
|
||||
|
||||
@@ -48,9 +48,6 @@ export function useApiFetch<T>(api: Api, params: any = null, reqOptions: Request
|
||||
}
|
||||
|
||||
let paramsValue = unref(params);
|
||||
if (api.beforeHandler) {
|
||||
paramsValue = api.beforeHandler(paramsValue);
|
||||
}
|
||||
|
||||
let apiUrl = url;
|
||||
// 简单判断该url是否是restful风格
|
||||
@@ -58,6 +55,10 @@ export function useApiFetch<T>(api: Api, params: any = null, reqOptions: Request
|
||||
apiUrl = templateResolve(apiUrl, paramsValue);
|
||||
}
|
||||
|
||||
if (api.beforeHandler) {
|
||||
paramsValue = api.beforeHandler(paramsValue);
|
||||
}
|
||||
|
||||
if (paramsValue) {
|
||||
const method = options.method?.toLowerCase();
|
||||
// post和put使用json格式传参
|
||||
|
||||
@@ -13,7 +13,10 @@
|
||||
<enum-tag :enums="FlowBizType" :value="procinst.bizType"></enum-tag>
|
||||
</el-descriptions-item>
|
||||
|
||||
<el-descriptions-item label="发起人">{{ procinst.creator }}</el-descriptions-item>
|
||||
<el-descriptions-item label="发起人">
|
||||
<AccountInfo :account-id="procinst.creatorId" :username="procinst.creator" />
|
||||
<!-- {{ procinst.creator }} -->
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="发起时间">{{ dateFormat(procinst.createTime) }}</el-descriptions-item>
|
||||
|
||||
<div v-if="procinst.duration">
|
||||
@@ -41,7 +44,14 @@
|
||||
|
||||
<div>
|
||||
<el-divider content-position="left">业务信息</el-divider>
|
||||
<component v-if="procinst.bizType" ref="keyValueRef" :is="bizComponents[procinst.bizType]" :biz-key="procinst.bizKey"> </component>
|
||||
<component
|
||||
v-if="procinst.bizType"
|
||||
ref="keyValueRef"
|
||||
:is="bizComponents[procinst.bizType]"
|
||||
:biz-key="procinst.bizKey"
|
||||
:biz-form="procinst.bizForm"
|
||||
>
|
||||
</component>
|
||||
</div>
|
||||
|
||||
<div v-if="props.instTaskId">
|
||||
@@ -80,8 +90,10 @@ import { dateFormat } from '@/common/utils/date';
|
||||
import ProcdefTasks from './components/ProcdefTasks.vue';
|
||||
import { formatTime } from '@/common/utils/format';
|
||||
import EnumTag from '@/components/enumtag/EnumTag.vue';
|
||||
import AccountInfo from '@/views/system/account/components/AccountInfo.vue';
|
||||
|
||||
const DbSqlExecBiz = defineAsyncComponent(() => import('./flowbiz/DbSqlExecBiz.vue'));
|
||||
const RedisRunWriteCmdBiz = defineAsyncComponent(() => import('./flowbiz/RedisRunWriteCmdBiz.vue'));
|
||||
|
||||
const props = defineProps({
|
||||
procinstId: {
|
||||
@@ -104,6 +116,7 @@ const emit = defineEmits(['cancel', 'val-change']);
|
||||
// 业务组件
|
||||
const bizComponents = shallowReactive({
|
||||
db_sql_exec_flow: DbSqlExecBiz,
|
||||
redis_run_write_cmd_flow: RedisRunWriteCmdBiz,
|
||||
});
|
||||
|
||||
const state = reactive({
|
||||
|
||||
@@ -52,10 +52,10 @@ import { formatTime } from '@/common/utils/format';
|
||||
|
||||
const searchItems = [SearchItem.select('status', '流程状态').withEnum(ProcinstStatus), SearchItem.select('bizType', '业务类型').withEnum(FlowBizType)];
|
||||
const columns = [
|
||||
TableColumn.new('procdefName', '流程名'),
|
||||
TableColumn.new('bizType', '业务').typeTag(FlowBizType),
|
||||
TableColumn.new('remark', '备注'),
|
||||
TableColumn.new('creator', '发起人'),
|
||||
TableColumn.new('procdefName', '流程名'),
|
||||
TableColumn.new('status', '流程状态').typeTag(ProcinstStatus),
|
||||
TableColumn.new('bizStatus', '业务状态').typeTag(ProcinstBizStatus),
|
||||
TableColumn.new('createTime', '发起时间').isTime(),
|
||||
|
||||
@@ -41,12 +41,12 @@ import { formatTime } from '@/common/utils/format';
|
||||
|
||||
const searchItems = [SearchItem.select('status', '任务状态').withEnum(ProcinstTaskStatus), SearchItem.select('bizType', '业务类型').withEnum(FlowBizType)];
|
||||
const columns = [
|
||||
TableColumn.new('procinst.procdefName', '流程名'),
|
||||
TableColumn.new('procinst.bizType', '业务').typeTag(FlowBizType),
|
||||
TableColumn.new('procinst.remark', '备注'),
|
||||
TableColumn.new('procinst.creator', '发起人'),
|
||||
TableColumn.new('procinst.status', '流程状态').typeTag(ProcinstStatus),
|
||||
TableColumn.new('status', '任务状态').typeTag(ProcinstTaskStatus),
|
||||
TableColumn.new('procinst.procdefName', '流程名'),
|
||||
TableColumn.new('taskName', '当前节点'),
|
||||
TableColumn.new('procinst.createTime', '发起时间').isTime(),
|
||||
TableColumn.new('createTime', '开始时间').isTime(),
|
||||
|
||||
@@ -30,4 +30,5 @@ export const ProcinstTaskStatus = {
|
||||
|
||||
export const FlowBizType = {
|
||||
DbSqlExec: EnumValue.of('db_sql_exec_flow', 'DBMS-执行SQL'),
|
||||
RedisRunWriteCmd: EnumValue.of('redis_run_write_cmd_flow', 'Redis-执行write命令'),
|
||||
};
|
||||
|
||||
80
mayfly_go_web/src/views/flow/flowbiz/RedisRunWriteCmdBiz.vue
Executable file
80
mayfly_go_web/src/views/flow/flowbiz/RedisRunWriteCmdBiz.vue
Executable file
@@ -0,0 +1,80 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-descriptions :column="3" border>
|
||||
<el-descriptions-item :span="1" label="名称">{{ redis?.name }}</el-descriptions-item>
|
||||
<el-descriptions-item :span="1" label="id">{{ redis?.id }}</el-descriptions-item>
|
||||
<el-descriptions-item :span="1" label="用户名">{{ redis?.username }}</el-descriptions-item>
|
||||
|
||||
<el-descriptions-item :span="3" label="关联标签"><ResourceTags :tags="redis.tags" /></el-descriptions-item>
|
||||
|
||||
<el-descriptions-item :span="1" label="主机">{{ `${redis?.host}` }}</el-descriptions-item>
|
||||
<el-descriptions-item :span="1" label="库">{{ state.db }}</el-descriptions-item>
|
||||
<el-descriptions-item :span="1" label="mode">
|
||||
{{ redis.mode }}
|
||||
</el-descriptions-item>
|
||||
|
||||
<el-descriptions-item :span="3" label="执行Cmd">
|
||||
<el-input type="textarea" disabled v-model="cmd" rows="5" />
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { toRefs, reactive, watch, onMounted } from 'vue';
|
||||
import ResourceTags from '@/views/ops/component/ResourceTags.vue';
|
||||
import { redisApi } from '@/views/ops/redis/api';
|
||||
|
||||
const props = defineProps({
|
||||
// 业务表单
|
||||
bizForm: {
|
||||
type: [String],
|
||||
default: '',
|
||||
},
|
||||
});
|
||||
|
||||
const state = reactive({
|
||||
cmd: '',
|
||||
db: 0,
|
||||
redis: {} as any,
|
||||
});
|
||||
|
||||
const { cmd, redis } = toRefs(state);
|
||||
|
||||
onMounted(() => {
|
||||
parseRunCmdForm(props.bizForm);
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.bizForm,
|
||||
(newValue: any) => {
|
||||
parseRunCmdForm(newValue);
|
||||
}
|
||||
);
|
||||
|
||||
const parseRunCmdForm = async (bizForm: string) => {
|
||||
if (!bizForm) {
|
||||
return;
|
||||
}
|
||||
const form = JSON.parse(bizForm);
|
||||
|
||||
const cmds = form.cmd.map((item: any, index: number) => {
|
||||
if (index === 0) {
|
||||
return item; // 第一个元素直接返回原值
|
||||
}
|
||||
if (typeof item === 'string') {
|
||||
return `'${item}'`; // 字符串加单引号
|
||||
}
|
||||
return item; // 其他类型直接返回
|
||||
});
|
||||
state.cmd = cmds.join(' ');
|
||||
state.db = form.db;
|
||||
|
||||
const res = await redisApi.redisList.request({ id: form.id });
|
||||
if (!res.list) {
|
||||
return;
|
||||
}
|
||||
state.redis = res.list?.[0];
|
||||
};
|
||||
</script>
|
||||
<style lang="scss"></style>
|
||||
@@ -183,6 +183,8 @@
|
||||
<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>
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { h, render, VNode } from 'vue';
|
||||
import { h, render } from 'vue';
|
||||
import SqlExecDialog from './SqlExecDialog.vue';
|
||||
|
||||
export type SqlExecProps = {
|
||||
@@ -11,27 +11,21 @@ export type SqlExecProps = {
|
||||
cancelCallback?: Function;
|
||||
};
|
||||
|
||||
const boxId = 'sql-exec-dialog-id';
|
||||
|
||||
let boxInstance: VNode;
|
||||
|
||||
const SqlExecBox = (props: SqlExecProps): void => {
|
||||
if (!boxInstance) {
|
||||
const container = document.createElement('div');
|
||||
container.id = boxId;
|
||||
// 创建 虚拟dom
|
||||
boxInstance = h(SqlExecDialog);
|
||||
// 将虚拟dom渲染到 container dom 上
|
||||
render(boxInstance, container);
|
||||
// 最后将 container 追加到 body 上
|
||||
document.body.appendChild(container);
|
||||
}
|
||||
|
||||
const boxVue = boxInstance.component;
|
||||
if (boxVue) {
|
||||
// 调用open方法显示弹框,注意不能使用boxVue.ctx来调用组件函数(build打包后ctx会获取不到)
|
||||
boxVue.exposed?.open(props);
|
||||
}
|
||||
const propsCancelFn = props.cancelCallback;
|
||||
// 包装取消回调函数,新增销毁组件代码
|
||||
props.cancelCallback = () => {
|
||||
propsCancelFn && propsCancelFn();
|
||||
setTimeout(() => {
|
||||
// 销毁组件
|
||||
render(null, document.body);
|
||||
}, 500);
|
||||
};
|
||||
const vnode = h(SqlExecDialog, {
|
||||
...props,
|
||||
visible: true,
|
||||
});
|
||||
render(vnode, document.body);
|
||||
};
|
||||
|
||||
export default SqlExecBox;
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-dialog title="待执行SQL" v-model="dialogVisible" :show-close="false" width="600px" @close="cancel">
|
||||
<el-dialog title="待执行SQL" v-model="dialogVisible" :show-close="false" width="600px">
|
||||
<monaco-editor height="300px" class="codesql" language="sql" v-model="sqlValue" />
|
||||
<el-input @keyup.enter="runSql" ref="remarkInputRef" v-model="remark" placeholder="请输入执行备注" class="mt5" />
|
||||
|
||||
<div v-if="state.flowProcdefKey">
|
||||
<div v-if="props.flowProcdefKey">
|
||||
<el-divider content-position="left">审批节点</el-divider>
|
||||
<procdef-tasks :procdef-key="state.flowProcdefKey" />
|
||||
<procdef-tasks :procdef-key="props.flowProcdefKey" />
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
@@ -20,7 +20,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { toRefs, ref, nextTick, reactive } from 'vue';
|
||||
import { toRefs, ref, reactive, onMounted } from 'vue';
|
||||
import { dbApi } from '@/views/ops/db/api';
|
||||
import { ElDialog, ElButton, ElInput, ElMessage, InputInstance, ElDivider } from 'element-plus';
|
||||
// import base style
|
||||
@@ -30,39 +30,24 @@ import { format as sqlFormatter } from 'sql-formatter';
|
||||
import { SqlExecProps } from './SqlExecBox';
|
||||
import ProcdefTasks from '@/views/flow/components/ProcdefTasks.vue';
|
||||
|
||||
const props = defineProps({
|
||||
visible: {
|
||||
type: Boolean,
|
||||
},
|
||||
dbId: {
|
||||
type: [Number],
|
||||
},
|
||||
db: {
|
||||
type: String,
|
||||
},
|
||||
sql: {
|
||||
type: String,
|
||||
},
|
||||
});
|
||||
const props = withDefaults(defineProps<SqlExecProps>(), {});
|
||||
|
||||
const remarkInputRef = ref<InputInstance>();
|
||||
const state = reactive({
|
||||
dialogVisible: false,
|
||||
sqlValue: '',
|
||||
dbId: 0,
|
||||
db: '',
|
||||
flowProcdefKey: '' as any,
|
||||
remark: '',
|
||||
btnLoading: false,
|
||||
});
|
||||
|
||||
const { dialogVisible, sqlValue, remark, btnLoading } = toRefs(state);
|
||||
|
||||
state.sqlValue = props.sql as any;
|
||||
let runSuccessCallback: any;
|
||||
let cancelCallback: any;
|
||||
let runSuccess: boolean = false;
|
||||
|
||||
onMounted(() => {
|
||||
open();
|
||||
});
|
||||
|
||||
/**
|
||||
* 执行sql
|
||||
*/
|
||||
@@ -75,14 +60,14 @@ const runSql = async () => {
|
||||
try {
|
||||
state.btnLoading = true;
|
||||
const res = await dbApi.sqlExec.request({
|
||||
id: state.dbId,
|
||||
db: state.db,
|
||||
id: props.dbId,
|
||||
db: props.db,
|
||||
remark: state.remark,
|
||||
sql: state.sqlValue.trim(),
|
||||
});
|
||||
|
||||
// 存在流程审批
|
||||
if (state.flowProcdefKey) {
|
||||
if (props.flowProcdefKey) {
|
||||
runSuccess = false;
|
||||
ElMessage.success('工单提交成功');
|
||||
return;
|
||||
@@ -101,10 +86,9 @@ const runSql = async () => {
|
||||
runSuccess = false;
|
||||
} finally {
|
||||
if (runSuccess) {
|
||||
if (runSuccessCallback) {
|
||||
runSuccessCallback();
|
||||
if (props.runSuccessCallback) {
|
||||
props.runSuccessCallback();
|
||||
}
|
||||
// cancel();
|
||||
}
|
||||
state.btnLoading = false;
|
||||
cancel();
|
||||
@@ -113,34 +97,20 @@ const runSql = async () => {
|
||||
|
||||
const cancel = () => {
|
||||
state.dialogVisible = false;
|
||||
// 没有执行成功,并且取消回调函数存在,则执行
|
||||
if (!runSuccess && cancelCallback) {
|
||||
cancelCallback();
|
||||
}
|
||||
props.cancelCallback && props.cancelCallback();
|
||||
setTimeout(() => {
|
||||
state.dbId = 0;
|
||||
state.sqlValue = '';
|
||||
state.remark = '';
|
||||
runSuccessCallback = null;
|
||||
cancelCallback = null;
|
||||
runSuccess = false;
|
||||
}, 200);
|
||||
};
|
||||
|
||||
const open = (props: SqlExecProps) => {
|
||||
runSuccessCallback = props.runSuccessCallback;
|
||||
cancelCallback = props.cancelCallback;
|
||||
props.dbType = props.dbType || 'mysql';
|
||||
state.sqlValue = sqlFormatter(props.sql, { language: props.dbType });
|
||||
state.dbId = props.dbId;
|
||||
state.db = props.db;
|
||||
state.flowProcdefKey = props.flowProcdefKey;
|
||||
const open = () => {
|
||||
state.sqlValue = sqlFormatter(props.sql, { language: props.dbType || 'mysql' });
|
||||
state.dialogVisible = true;
|
||||
nextTick(() => {
|
||||
setTimeout(() => {
|
||||
remarkInputRef.value?.focus();
|
||||
});
|
||||
});
|
||||
setTimeout(() => {
|
||||
remarkInputRef.value?.focus();
|
||||
}, 200);
|
||||
};
|
||||
|
||||
defineExpose({ open });
|
||||
|
||||
@@ -742,7 +742,6 @@ const onExitEditMode = (rowData: any, column: any, rowIndex = 0) => {
|
||||
};
|
||||
|
||||
const submitUpdateFields = async () => {
|
||||
debugger;
|
||||
const dbInst = getNowDbInst();
|
||||
if (cellUpdateMap.size == 0) {
|
||||
return;
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
<el-checkbox
|
||||
v-model="item.show"
|
||||
:label="`${!item.columnComment ? item.columnName : item.columnName + ' [' + item.columnComment + ']'}`"
|
||||
:true-label="true"
|
||||
:false-label="false"
|
||||
:true-value="true"
|
||||
:false-value="false"
|
||||
size="small"
|
||||
/>
|
||||
</div>
|
||||
@@ -55,7 +55,7 @@
|
||||
title="展示配置"
|
||||
trigger="click"
|
||||
>
|
||||
<el-checkbox v-model="dbConfig.showColumnComment" label="显示字段备注" :true-label="true" :false-label="false" size="small" />
|
||||
<el-checkbox v-model="dbConfig.showColumnComment" label="显示字段备注" :true-value="true" :false-value="false" size="small" />
|
||||
<template #reference>
|
||||
<el-link type="primary" icon="setting" :underline="false"></el-link>
|
||||
</template>
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
|
||||
<el-tab-pane label="其他配置" name="other">
|
||||
<el-form-item prop="enableRecorder" label="终端回放">
|
||||
<el-checkbox v-model="form.enableRecorder" :true-label="1" :false-label="-1"></el-checkbox>
|
||||
<el-checkbox v-model="form.enableRecorder" :true-value="1" :false-value="-1"></el-checkbox>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item prop="sshTunnelMachineId" label="SSH隧道">
|
||||
|
||||
@@ -141,7 +141,7 @@
|
||||
<div class="key-detail card pd5">
|
||||
<el-tabs @tab-remove="removeDataTab" v-model="state.activeName">
|
||||
<el-tab-pane closable v-for="dt in state.dataTabs" :key="dt.key" :label="dt.label" :name="dt.key">
|
||||
<key-detail :redisId="scanParam.id" :db="scanParam.db" :key-info="dt.keyInfo" @change-key="searchKey()" @del-key="delKey" />
|
||||
<key-detail :redis="redisInst" :key-info="dt.keyInfo" @change-key="searchKey()" @del-key="delKey" />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
@@ -178,7 +178,7 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { redisApi } from './api';
|
||||
import { ref, defineAsyncComponent, toRefs, reactive, onMounted, nextTick } from 'vue';
|
||||
import { ref, defineAsyncComponent, toRefs, reactive, onMounted, nextTick, Ref } from 'vue';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import { isTrue, notBlank, notNull } from '@/common/assert';
|
||||
import { copyToClipboard } from '@/common/utils/string';
|
||||
@@ -189,6 +189,7 @@ import { Contextmenu, ContextmenuItem } from '@/components/contextmenu';
|
||||
import { sleep } from '@/common/utils/loading';
|
||||
import { TagResourceTypeEnum } from '@/common/commonEnum';
|
||||
import { Splitpanes, Pane } from 'splitpanes';
|
||||
import { RedisInst } from './redis';
|
||||
|
||||
const KeyDetail = defineAsyncComponent(() => import('./KeyDetail.vue'));
|
||||
|
||||
@@ -240,6 +241,7 @@ const NodeTypeRedis = new NodeType(RedisNodeType.Redis).withLoadNodesFunc(async
|
||||
return new TagTreeNode(x, `db${x}`, NodeTypeDb).withIsLeaf(true).withParams({
|
||||
id: redisInfo.id,
|
||||
db: x,
|
||||
flowProcdefKey: redisInfo.flowProcdefKey,
|
||||
name: `db${x}`,
|
||||
keys: 0,
|
||||
});
|
||||
@@ -269,6 +271,11 @@ const NodeTypeDb = new NodeType(RedisNodeType.Db).withNodeClickFunc((nodeData: T
|
||||
resetScanParam();
|
||||
state.scanParam.id = nodeData.params.id;
|
||||
state.scanParam.db = nodeData.params.db;
|
||||
|
||||
redisInst.value.id = nodeData.params.id;
|
||||
redisInst.value.db = Number.parseInt(nodeData.params.db);
|
||||
redisInst.value.flowProcdefKey = nodeData.params.flowProcdefKey;
|
||||
|
||||
scan();
|
||||
});
|
||||
|
||||
@@ -281,6 +288,7 @@ const treeProps = {
|
||||
const defaultCount = 250;
|
||||
|
||||
const keyTreeRef: any = ref(null);
|
||||
const redisInst: Ref<RedisInst> = ref(new RedisInst());
|
||||
|
||||
const state = reactive({
|
||||
tags: [],
|
||||
@@ -506,15 +514,11 @@ const flushDb = () => {
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
redisApi.flushDb
|
||||
.request({
|
||||
id: state.scanParam.id,
|
||||
db: state.scanParam.db,
|
||||
})
|
||||
.then(() => {
|
||||
ElMessage.success('清除成功!');
|
||||
searchKey();
|
||||
});
|
||||
// FLUSHDB [ASYNC | SYNC]
|
||||
redisInst.value.runCmd(['FLUSHDB']).then(() => {
|
||||
ElMessage.success('清除成功!');
|
||||
searchKey();
|
||||
});
|
||||
})
|
||||
.catch(() => {});
|
||||
};
|
||||
@@ -526,19 +530,9 @@ const cancelNewKey = () => {
|
||||
|
||||
const newKey = async () => {
|
||||
const keyInfo = state.newKeyDialog.keyInfo;
|
||||
const keyType = keyInfo.type;
|
||||
const key = keyInfo.key;
|
||||
notBlank(key, '键名不能为空');
|
||||
|
||||
if (keyType == 'string') {
|
||||
await redisApi.setString.request({
|
||||
id: state.scanParam.id,
|
||||
db: state.scanParam.db,
|
||||
key: key,
|
||||
value: '',
|
||||
});
|
||||
}
|
||||
|
||||
showKeyDetail(
|
||||
{
|
||||
...keyInfo,
|
||||
@@ -565,11 +559,8 @@ const delKey = (key: string) => {
|
||||
type: 'warning',
|
||||
})
|
||||
.then(async () => {
|
||||
await redisApi.delKey.request({
|
||||
key,
|
||||
id: state.scanParam.id,
|
||||
db: state.scanParam.db,
|
||||
});
|
||||
// DEL key [key ...]
|
||||
await redisInst.value.runCmd(['DEL', key]);
|
||||
ElMessage.success('删除成功!');
|
||||
searchKey();
|
||||
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
<!-- key info -->
|
||||
<key-header
|
||||
ref="keyHeader"
|
||||
:redis-id="redisId"
|
||||
:db="db"
|
||||
:redis="props.redis"
|
||||
:key-info="state.keyInfo"
|
||||
@refresh-content="refreshContent"
|
||||
@del-key="delKey"
|
||||
@@ -15,7 +14,7 @@
|
||||
</key-header>
|
||||
|
||||
<!-- key content -->
|
||||
<component ref="keyValueRef" :is="components[componentName]" :redis-id="redisId" :db="db" :key-info="keyInfo"> </component>
|
||||
<component ref="keyValueRef" :is="components[componentName]" :redis="props.redis" :key-info="keyInfo"> </component>
|
||||
</el-container>
|
||||
</div>
|
||||
</template>
|
||||
@@ -23,6 +22,7 @@
|
||||
import { defineAsyncComponent, watch, ref, shallowReactive, reactive, computed, onMounted } from 'vue';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import KeyHeader from './KeyHeader.vue';
|
||||
import { RedisInst } from './redis';
|
||||
|
||||
const KeyValueString = defineAsyncComponent(() => import('./KeyValueString.vue'));
|
||||
const KeyValueHash = defineAsyncComponent(() => import('./KeyValueHash.vue'));
|
||||
@@ -41,11 +41,9 @@ const components = shallowReactive({
|
||||
const keyValueRef = ref(null) as any;
|
||||
|
||||
const props = defineProps({
|
||||
redisId: {
|
||||
type: Number,
|
||||
},
|
||||
db: {
|
||||
type: Number,
|
||||
redis: {
|
||||
type: RedisInst,
|
||||
required: true,
|
||||
},
|
||||
keyInfo: {
|
||||
type: [Object],
|
||||
@@ -55,7 +53,6 @@ const props = defineProps({
|
||||
const emit = defineEmits(['update:visible', 'changeKey', 'delKey']);
|
||||
|
||||
const state = reactive({
|
||||
redisId: 0,
|
||||
keyInfo: {} as any,
|
||||
});
|
||||
|
||||
|
||||
@@ -47,13 +47,12 @@ import { reactive, watch, toRefs, onMounted } from 'vue';
|
||||
import { redisApi } from './api';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import { formatTime } from '@/common/utils/format';
|
||||
import { RedisInst } from './redis';
|
||||
|
||||
const props = defineProps({
|
||||
redisId: {
|
||||
type: Number,
|
||||
},
|
||||
db: {
|
||||
type: Number,
|
||||
redis: {
|
||||
type: RedisInst,
|
||||
required: true,
|
||||
},
|
||||
keyInfo: {
|
||||
type: [Object],
|
||||
@@ -63,7 +62,6 @@ const props = defineProps({
|
||||
const emit = defineEmits(['refreshContent', 'delKey', 'changeKey']);
|
||||
|
||||
const state = reactive({
|
||||
redisId: 0,
|
||||
keyInfo: {
|
||||
key: '',
|
||||
type: '',
|
||||
@@ -85,8 +83,8 @@ onMounted(() => {
|
||||
|
||||
const refreshKey = async () => {
|
||||
const ttl = await redisApi.keyTtl.request({
|
||||
id: props.redisId,
|
||||
db: props.db,
|
||||
id: props.redis.id,
|
||||
db: props.redis.db,
|
||||
key: state.oldKey,
|
||||
});
|
||||
state.keyInfo.timed = ttl;
|
||||
@@ -101,12 +99,8 @@ const renameKey = async () => {
|
||||
if (!state.oldKey || state.ki.key == state.oldKey) {
|
||||
return;
|
||||
}
|
||||
await redisApi.renameKey.request({
|
||||
id: props.redisId,
|
||||
db: props.db,
|
||||
newKey: state.ki.key,
|
||||
key: state.oldKey,
|
||||
});
|
||||
// RENAME key newkey
|
||||
await props.redis.runCmd(['RENAME', state.oldKey, state.ki.key]);
|
||||
ElMessage.success('设置成功');
|
||||
emit('changeKey');
|
||||
};
|
||||
@@ -131,22 +125,15 @@ const ttlKey = async () => {
|
||||
return;
|
||||
}
|
||||
|
||||
await redisApi.expireKey.request({
|
||||
id: props.redisId,
|
||||
db: props.db,
|
||||
key: state.ki.key,
|
||||
seconds: state.ki.timed,
|
||||
});
|
||||
// EXPIRE key seconds [NX | XX | GT | LT]
|
||||
await props.redis.runCmd(['EXPIRE', state.ki.key, state.ki.timed]);
|
||||
ElMessage.success('设置成功');
|
||||
emit('changeKey');
|
||||
};
|
||||
|
||||
const persistKey = async () => {
|
||||
await redisApi.persistKey.request({
|
||||
id: props.redisId,
|
||||
db: props.db,
|
||||
key: state.keyInfo.key,
|
||||
});
|
||||
// PERSIST key
|
||||
await props.redis.runCmd(['PERSIST', state.keyInfo.key]);
|
||||
ElMessage.success('设置成功');
|
||||
emit('changeKey');
|
||||
};
|
||||
|
||||
@@ -52,21 +52,15 @@
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted, reactive, toRefs } from 'vue';
|
||||
import { redisApi } from './api';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { notBlank } from '@/common/assert';
|
||||
import FormatViewer from './FormatViewer.vue';
|
||||
import { RedisInst } from './redis';
|
||||
|
||||
const props = defineProps({
|
||||
redisId: {
|
||||
type: [Number],
|
||||
require: true,
|
||||
default: 0,
|
||||
},
|
||||
db: {
|
||||
type: [Number],
|
||||
require: true,
|
||||
default: 0,
|
||||
redis: {
|
||||
type: RedisInst,
|
||||
required: true,
|
||||
},
|
||||
keyInfo: {
|
||||
type: [Object],
|
||||
@@ -76,8 +70,6 @@ const props = defineProps({
|
||||
const formatViewerRef = ref(null) as any;
|
||||
|
||||
const state = reactive({
|
||||
redisId: 0,
|
||||
db: 0,
|
||||
key: '',
|
||||
scanParam: {
|
||||
cursor: 0,
|
||||
@@ -98,8 +90,6 @@ const state = reactive({
|
||||
const { hashValues, total, loadMoreDisable, editDialog } = toRefs(state);
|
||||
|
||||
onMounted(() => {
|
||||
state.redisId = props.redisId;
|
||||
state.db = props.db;
|
||||
state.key = props.keyInfo?.key;
|
||||
initData();
|
||||
});
|
||||
@@ -118,16 +108,15 @@ const hscan = async (resetTableData = false, resetCursor = false) => {
|
||||
state.scanParam.cursor = 0;
|
||||
}
|
||||
|
||||
const scanRes = await redisApi.hscan.request({
|
||||
...getBaseReqParam(),
|
||||
match: getScanMatch(),
|
||||
...state.scanParam,
|
||||
});
|
||||
state.scanParam.cursor = scanRes.cursor;
|
||||
state.loadMoreDisable = scanRes.cursor == 0;
|
||||
state.total = scanRes.keySize;
|
||||
props.redis.runCmd(['HLEN', state.key]).then((res) => (state.total = res));
|
||||
|
||||
// HSCAN key cursor [MATCH pattern] [COUNT count]
|
||||
// 返回值 [coursor, keys:[]]
|
||||
let scanRes = await props.redis.runCmd(['HSCAN', state.key, state.scanParam.cursor, 'MATCH', getScanMatch(), 'COUNT', state.scanParam.count]);
|
||||
state.scanParam.cursor = scanRes[0];
|
||||
state.loadMoreDisable = state.scanParam.cursor == 0;
|
||||
const keys = scanRes[1];
|
||||
|
||||
const keys = scanRes.keys;
|
||||
const hashValue = [];
|
||||
const fieldCount = keys.length / 2;
|
||||
let nextFieldIndex = 0;
|
||||
@@ -143,10 +132,7 @@ const hscan = async (resetTableData = false, resetCursor = false) => {
|
||||
};
|
||||
|
||||
const hdel = async (field: any, index: any) => {
|
||||
await redisApi.hdel.request({
|
||||
...getBaseReqParam(),
|
||||
field,
|
||||
});
|
||||
await props.redis.runCmd(['HDEL', state.key, field]);
|
||||
|
||||
ElMessage.success('删除成功');
|
||||
state.hashValues.splice(index, 1);
|
||||
@@ -161,57 +147,25 @@ const showEditDialog = (row: any) => {
|
||||
};
|
||||
|
||||
const confirmEditData = async () => {
|
||||
const param = getBaseReqParam();
|
||||
|
||||
const field = state.editDialog.field;
|
||||
notBlank(field, 'field不能为空');
|
||||
|
||||
// 存在数据行,则说明为修改,则要先删除旧数据后新增
|
||||
const dataRow = state.editDialog.dataRow;
|
||||
if (dataRow) {
|
||||
await redisApi.hdel.request({
|
||||
...param,
|
||||
field: dataRow.field,
|
||||
});
|
||||
}
|
||||
|
||||
// 获取hash value内容并新增
|
||||
const value = formatViewerRef.value.getContent();
|
||||
const res = await redisApi.hset.request({
|
||||
...param,
|
||||
value: [
|
||||
{
|
||||
field,
|
||||
value: value,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const res = await props.redis.runCmd(['HSET', state.key, field, value]);
|
||||
ElMessage.success('保存成功');
|
||||
if (dataRow) {
|
||||
state.editDialog.dataRow.value = value;
|
||||
state.editDialog.dataRow.field = field;
|
||||
// 响应0则为被覆盖,则重新scan
|
||||
if (res == 0) {
|
||||
hscan(true, true);
|
||||
} else {
|
||||
// 响应0则为被覆盖,则重新scan
|
||||
if (res == 0) {
|
||||
hscan(true, true);
|
||||
} else {
|
||||
state.hashValues.unshift({ value, field });
|
||||
state.total++;
|
||||
}
|
||||
state.hashValues.unshift({ value, field });
|
||||
state.total++;
|
||||
}
|
||||
state.editDialog.visible = false;
|
||||
state.editDialog.dataRow = null;
|
||||
};
|
||||
|
||||
const getBaseReqParam = () => {
|
||||
return {
|
||||
id: state.redisId,
|
||||
db: state.db,
|
||||
key: state.key,
|
||||
};
|
||||
};
|
||||
|
||||
defineExpose({ initData });
|
||||
</script>
|
||||
<style lang="scss">
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-button @click="showEditDialog(null)" icon="plus" size="small" plain type="primary" class="mb10">添加新行</el-button>
|
||||
<el-button @click="showEditDialog(null, -1)" icon="plus" size="small" plain type="primary" class="mb10">添加新行</el-button>
|
||||
<el-table size="small" border :data="values" height="450" min-height="300" stripe>
|
||||
<el-table-column type="index" :label="'ID (Total: ' + total + ')'" sortable width="100"> </el-table-column>
|
||||
<el-table-column resizable sortable prop="value" label="value" show-overflow-tooltip min-width="200"> </el-table-column>
|
||||
<el-table-column label="操作">
|
||||
<template #default="scope">
|
||||
<el-link @click="showEditDialog(scope.row)" :underline="false" type="primary" icon="edit" plain></el-link>
|
||||
<el-link @click="showEditDialog(scope.row, scope.$index)" :underline="false" type="primary" icon="edit" plain></el-link>
|
||||
<el-popconfirm title="确定删除?" @confirm="lrem(scope.row, scope.$index)">
|
||||
<template #reference>
|
||||
<el-link v-auth="'redis:data:del'" :underline="false" type="danger" icon="delete" size="small" plain class="ml5"></el-link>
|
||||
@@ -38,20 +38,14 @@
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive, toRefs, onMounted } from 'vue';
|
||||
import { redisApi } from './api';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import FormatViewer from './FormatViewer.vue';
|
||||
import { RedisInst } from './redis';
|
||||
|
||||
const props = defineProps({
|
||||
redisId: {
|
||||
type: [Number],
|
||||
require: true,
|
||||
default: 0,
|
||||
},
|
||||
db: {
|
||||
type: [Number],
|
||||
require: true,
|
||||
default: 0,
|
||||
redis: {
|
||||
type: RedisInst,
|
||||
required: true,
|
||||
},
|
||||
keyInfo: {
|
||||
type: [Object],
|
||||
@@ -61,8 +55,6 @@ const props = defineProps({
|
||||
const formatViewerRef = ref(null) as any;
|
||||
|
||||
const state = reactive({
|
||||
redisId: 0,
|
||||
db: 0,
|
||||
key: '',
|
||||
pageNum: 1,
|
||||
pageSize: 50,
|
||||
@@ -70,17 +62,15 @@ const state = reactive({
|
||||
values: [] as any,
|
||||
loadMoreDisable: false,
|
||||
editDialog: {
|
||||
index: -1,
|
||||
visible: false,
|
||||
content: '',
|
||||
dataRow: null as any,
|
||||
},
|
||||
});
|
||||
|
||||
const { total, values, loadMoreDisable, editDialog } = toRefs(state);
|
||||
|
||||
onMounted(() => {
|
||||
state.redisId = props.redisId;
|
||||
state.db = props.db;
|
||||
state.key = props.keyInfo?.key;
|
||||
initData();
|
||||
});
|
||||
@@ -93,14 +83,12 @@ const initData = () => {
|
||||
const getListValue = async (resetTableData = false) => {
|
||||
const pageNum = state.pageNum;
|
||||
const pageSize = state.pageSize;
|
||||
const res = await redisApi.getListValue.request({
|
||||
...getBaseReqParam(),
|
||||
start: (pageNum - 1) * pageSize,
|
||||
stop: pageNum * pageSize - 1,
|
||||
});
|
||||
state.total = res.len;
|
||||
|
||||
const datas = res.list.map((x: any) => {
|
||||
props.redis.runCmd(['LLEN', state.key]).then((res) => (state.total = res));
|
||||
|
||||
// LRANGE key start stop
|
||||
const res = await props.redis.runCmd(['LRANGE', state.key, (pageNum - 1) * pageSize, pageNum * pageSize - 1]);
|
||||
const datas = res.map((x: any) => {
|
||||
return {
|
||||
value: x,
|
||||
};
|
||||
@@ -114,71 +102,41 @@ const getListValue = async (resetTableData = false) => {
|
||||
state.loadMoreDisable = state.values.length === state.total;
|
||||
};
|
||||
|
||||
// const lset = async (row: any, rowIndex: number) => {
|
||||
// await redisApi.setListValue.request({
|
||||
// ...getBaseReqParam(),
|
||||
// index: (state.pageNum - 1) * state.pageSize + rowIndex,
|
||||
// value: row.value,
|
||||
// });
|
||||
// ElMessage.success('数据保存成功');
|
||||
// };
|
||||
|
||||
const showEditDialog = (row: any) => {
|
||||
state.editDialog.dataRow = row;
|
||||
const showEditDialog = (row: any, index = -1) => {
|
||||
state.editDialog.index = index;
|
||||
state.editDialog.content = row ? row.value : '';
|
||||
state.editDialog.visible = true;
|
||||
};
|
||||
|
||||
const confirmEditData = async () => {
|
||||
const param = getBaseReqParam();
|
||||
|
||||
// 存在数据行,则说明为修改,则要先删除旧数据后新增
|
||||
const dataRow = state.editDialog.dataRow;
|
||||
if (dataRow) {
|
||||
await redisApi.lrem.request({
|
||||
member: state.editDialog.dataRow.value,
|
||||
count: 1,
|
||||
...param,
|
||||
});
|
||||
}
|
||||
|
||||
const index = state.editDialog.index;
|
||||
// 获取list member内容并新增
|
||||
const member = formatViewerRef.value.getContent();
|
||||
await redisApi.saveListValue.request({
|
||||
value: [member],
|
||||
...param,
|
||||
});
|
||||
try {
|
||||
// 索引=-1 说明是新增
|
||||
if (index == -1) {
|
||||
// RPUSH key element [element ...]
|
||||
await props.redis.runCmd(['RPUSH', state.key, member]);
|
||||
} else {
|
||||
// LSET key index element
|
||||
await props.redis.runCmd(['LSET', state.key, index, member]);
|
||||
}
|
||||
|
||||
ElMessage.success('保存成功');
|
||||
if (dataRow) {
|
||||
state.editDialog.dataRow.value = member;
|
||||
} else {
|
||||
state.values.push({ value: member });
|
||||
state.total++;
|
||||
ElMessage.success('保存成功');
|
||||
initData();
|
||||
} finally {
|
||||
state.editDialog.visible = false;
|
||||
}
|
||||
state.editDialog.visible = false;
|
||||
state.editDialog.dataRow = null;
|
||||
};
|
||||
|
||||
const lrem = async (row: any, index: any) => {
|
||||
await redisApi.lrem.request({
|
||||
...getBaseReqParam(),
|
||||
member: row.value,
|
||||
count: 1,
|
||||
});
|
||||
// LREM key count element
|
||||
await props.redis.runCmd(['LREM', state.key, 1, row.value]);
|
||||
ElMessage.success('删除成功');
|
||||
state.values.splice(index, 1);
|
||||
state.total--;
|
||||
};
|
||||
|
||||
const getBaseReqParam = () => {
|
||||
return {
|
||||
id: state.redisId,
|
||||
db: state.db,
|
||||
key: state.key,
|
||||
};
|
||||
};
|
||||
|
||||
defineExpose({ initData });
|
||||
</script>
|
||||
<style lang="scss"></style>
|
||||
|
||||
@@ -48,20 +48,14 @@
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive, toRefs, onMounted } from 'vue';
|
||||
import { redisApi } from './api';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import FormatViewer from './FormatViewer.vue';
|
||||
import { RedisInst } from './redis';
|
||||
|
||||
const props = defineProps({
|
||||
redisId: {
|
||||
type: [Number],
|
||||
require: true,
|
||||
default: 0,
|
||||
},
|
||||
db: {
|
||||
type: [Number],
|
||||
require: true,
|
||||
default: 0,
|
||||
redis: {
|
||||
type: RedisInst,
|
||||
required: true,
|
||||
},
|
||||
keyInfo: {
|
||||
type: [Object],
|
||||
@@ -71,8 +65,6 @@ const props = defineProps({
|
||||
const formatViewerRef = ref(null) as any;
|
||||
|
||||
const state = reactive({
|
||||
redisId: 0,
|
||||
db: 0,
|
||||
key: '',
|
||||
|
||||
filterValue: '',
|
||||
@@ -94,8 +86,6 @@ const state = reactive({
|
||||
const { total, setDatas, loadMoreDisable, editDialog } = toRefs(state);
|
||||
|
||||
onMounted(() => {
|
||||
state.redisId = props.redisId;
|
||||
state.db = props.db;
|
||||
state.key = props.keyInfo?.key;
|
||||
initData();
|
||||
});
|
||||
@@ -114,26 +104,24 @@ const sscanData = async (resetDatas = true, resetCursor = false) => {
|
||||
if (resetCursor) {
|
||||
state.scanParam.cursor = 0;
|
||||
}
|
||||
const res = await redisApi.sscan.request({
|
||||
...getBaseReqParam(),
|
||||
match: getScanMatch(),
|
||||
...state.scanParam,
|
||||
});
|
||||
|
||||
// SSCAN key cursor [MATCH pattern] [COUNT count]
|
||||
// 响应[cursor, vals[]]
|
||||
const res = await props.redis.runCmd(['SSCAN', state.key, state.scanParam.cursor, 'MATCH', getScanMatch(), 'COUNT', state.scanParam.count]);
|
||||
if (resetDatas) {
|
||||
state.setDatas = [];
|
||||
}
|
||||
res.keys.forEach((x: any) => {
|
||||
res[1].forEach((x: any) => {
|
||||
state.setDatas.push({
|
||||
value: x,
|
||||
});
|
||||
});
|
||||
state.scanParam.cursor = res.cursor;
|
||||
state.loadMoreDisable = res.cursor == 0;
|
||||
state.scanParam.cursor = res[0];
|
||||
state.loadMoreDisable = state.scanParam.cursor == 0;
|
||||
};
|
||||
|
||||
const getTotal = () => {
|
||||
redisApi.scard.request(getBaseReqParam()).then((res) => {
|
||||
// SCARD key
|
||||
props.redis.runCmd(['SCARD', state.key]).then((res) => {
|
||||
state.total = res;
|
||||
});
|
||||
};
|
||||
@@ -145,23 +133,16 @@ const showEditDialog = (row: any) => {
|
||||
};
|
||||
|
||||
const confirmEditData = async () => {
|
||||
const param = getBaseReqParam();
|
||||
|
||||
// 存在数据行,则说明为修改,则要先删除旧数据后新增
|
||||
const dataRow = state.editDialog.dataRow;
|
||||
if (dataRow) {
|
||||
await redisApi.srem.request({
|
||||
member: state.editDialog.dataRow.value,
|
||||
...param,
|
||||
});
|
||||
await props.redis.runCmd(['SREM', state.key, state.editDialog.dataRow.value]);
|
||||
}
|
||||
|
||||
// 获取set member内容并新增
|
||||
const member = formatViewerRef.value.getContent();
|
||||
await redisApi.sadd.request({
|
||||
member,
|
||||
...param,
|
||||
});
|
||||
// SADD key member [member ...]
|
||||
await props.redis.runCmd(['SADD', state.key, member]);
|
||||
|
||||
ElMessage.success('保存成功');
|
||||
if (dataRow) {
|
||||
@@ -175,23 +156,13 @@ const confirmEditData = async () => {
|
||||
};
|
||||
|
||||
const srem = async (row: any, index: any) => {
|
||||
await redisApi.srem.request({
|
||||
...getBaseReqParam(),
|
||||
member: row.value,
|
||||
});
|
||||
// SREM key member [member ...]
|
||||
await props.redis.runCmd(['SREM', state.key, row.value]);
|
||||
ElMessage.success('删除成功');
|
||||
state.setDatas.splice(index, 1);
|
||||
state.total--;
|
||||
};
|
||||
|
||||
const getBaseReqParam = () => {
|
||||
return {
|
||||
id: state.redisId,
|
||||
db: state.db,
|
||||
key: state.key,
|
||||
};
|
||||
};
|
||||
|
||||
defineExpose({ initData });
|
||||
</script>
|
||||
<style lang="scss"></style>
|
||||
|
||||
@@ -12,21 +12,15 @@
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, watch, reactive, toRefs, onMounted } from 'vue';
|
||||
import { redisApi } from './api';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { notEmpty } from '@/common/assert';
|
||||
import FormatViewer from './FormatViewer.vue';
|
||||
import { RedisInst } from './redis';
|
||||
|
||||
const props = defineProps({
|
||||
redisId: {
|
||||
type: [Number],
|
||||
require: true,
|
||||
default: 0,
|
||||
},
|
||||
db: {
|
||||
type: [Number],
|
||||
require: true,
|
||||
default: 0,
|
||||
redis: {
|
||||
type: RedisInst,
|
||||
required: true,
|
||||
},
|
||||
keyInfo: {
|
||||
type: [Object],
|
||||
@@ -36,8 +30,6 @@ const props = defineProps({
|
||||
const formatViewerRef = ref(null) as any;
|
||||
|
||||
const state = reactive({
|
||||
redisId: 0,
|
||||
db: 0,
|
||||
key: '',
|
||||
keyInfo: {
|
||||
key: '',
|
||||
@@ -61,8 +53,6 @@ watch(props, (newVal) => {
|
||||
});
|
||||
|
||||
const setProps = (val: any) => {
|
||||
state.redisId = val.redisId;
|
||||
state.db = val.db;
|
||||
state.key = val.keyInfo?.key;
|
||||
initData();
|
||||
};
|
||||
@@ -73,7 +63,7 @@ const initData = () => {
|
||||
|
||||
const getStringValue = async () => {
|
||||
if (state.key) {
|
||||
state.string.value = await redisApi.getString.request(getBaseReqParam());
|
||||
state.string.value = await props.redis.runCmd(['GET', state.key]);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -81,21 +71,10 @@ const saveValue = async () => {
|
||||
state.string.value = formatViewerRef.value.getContent();
|
||||
notEmpty(state.string.value, 'value不能为空');
|
||||
|
||||
await redisApi.setString.request({
|
||||
...getBaseReqParam(),
|
||||
value: state.string.value,
|
||||
});
|
||||
await props.redis.runCmd(['SET', state.key, state.string.value]);
|
||||
ElMessage.success('数据保存成功');
|
||||
};
|
||||
|
||||
const getBaseReqParam = () => {
|
||||
return {
|
||||
id: state.redisId,
|
||||
db: state.db,
|
||||
key: state.key,
|
||||
};
|
||||
};
|
||||
|
||||
defineExpose({ initData });
|
||||
</script>
|
||||
<style lang="scss">
|
||||
|
||||
@@ -52,20 +52,14 @@
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive, toRefs, onMounted } from 'vue';
|
||||
import { redisApi } from './api';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import FormatViewer from './FormatViewer.vue';
|
||||
import { RedisInst } from './redis';
|
||||
|
||||
const props = defineProps({
|
||||
redisId: {
|
||||
type: [Number],
|
||||
require: true,
|
||||
default: 0,
|
||||
},
|
||||
db: {
|
||||
type: [Number],
|
||||
require: true,
|
||||
default: 0,
|
||||
redis: {
|
||||
type: RedisInst,
|
||||
required: true,
|
||||
},
|
||||
keyInfo: {
|
||||
type: [Object],
|
||||
@@ -75,8 +69,6 @@ const props = defineProps({
|
||||
const formatViewerRef = ref(null) as any;
|
||||
|
||||
const state = reactive({
|
||||
redisId: 0,
|
||||
db: 0,
|
||||
key: '',
|
||||
filterValue: '',
|
||||
scanCursor: 0,
|
||||
@@ -96,8 +88,6 @@ const state = reactive({
|
||||
const { total, values, loadMoreDisable, editDialog } = toRefs(state);
|
||||
|
||||
onMounted(() => {
|
||||
state.redisId = props.redisId;
|
||||
state.db = props.db;
|
||||
state.key = props.keyInfo?.key;
|
||||
initData();
|
||||
});
|
||||
@@ -105,7 +95,7 @@ onMounted(() => {
|
||||
const initData = async () => {
|
||||
state.pageNum = 1;
|
||||
state.filterValue = '';
|
||||
await getTotal();
|
||||
getTotal();
|
||||
await zrevrange(true);
|
||||
};
|
||||
|
||||
@@ -120,17 +110,14 @@ const loadDatas = (resetTableData = false) => {
|
||||
const zrevrange = async (resetTableData = false) => {
|
||||
const pageNum = state.pageNum;
|
||||
const pageSize = state.pageSize;
|
||||
const res = await redisApi.zrevrange.request({
|
||||
...getBaseReqParam(),
|
||||
start: (pageNum - 1) * pageSize,
|
||||
stop: pageNum * pageSize - 1,
|
||||
});
|
||||
// ZREVRANGE key start stop [WITHSCORES]
|
||||
const res = await props.redis.runCmd(['ZREVRANGE', state.key, (pageNum - 1) * pageSize, pageNum * pageSize - 1, 'WITHSCORES']);
|
||||
|
||||
const vs = [];
|
||||
for (let member of res) {
|
||||
vs.push({
|
||||
score: member.Score,
|
||||
value: member.Member,
|
||||
value: member[0],
|
||||
score: member[1],
|
||||
});
|
||||
}
|
||||
if (resetTableData) {
|
||||
@@ -150,14 +137,11 @@ const zscanData = async (resetTableData = true, resetCursor = false) => {
|
||||
if (resetCursor) {
|
||||
state.scanCursor = 0;
|
||||
}
|
||||
const res = await redisApi.zscan.request({
|
||||
...getBaseReqParam(),
|
||||
match: getScanMatch(),
|
||||
cursor: state.scanCursor,
|
||||
count: state.pageSize,
|
||||
});
|
||||
// ZSCAN key cursor [MATCH pattern] [COUNT count]
|
||||
// 响应[coursor, vals[]]
|
||||
const res = await props.redis.runCmd(['ZSCAN', state.key, state.scanCursor, 'MATCH', getScanMatch(), 'COUNT', state.pageSize]);
|
||||
|
||||
const keys = res.keys;
|
||||
const keys = res[1];
|
||||
const vs = [];
|
||||
const memCount = keys.length / 2;
|
||||
let nextMemndex = 0;
|
||||
@@ -171,20 +155,15 @@ const zscanData = async (resetTableData = true, resetCursor = false) => {
|
||||
state.values.push(...vs);
|
||||
}
|
||||
|
||||
state.scanCursor = res.cursor;
|
||||
state.loadMoreDisable = res.cursor == 0;
|
||||
state.scanCursor = res[0];
|
||||
state.loadMoreDisable = state.scanCursor == 0;
|
||||
};
|
||||
|
||||
const getTotal = () => {
|
||||
redisApi.zcard
|
||||
.request({
|
||||
id: state.redisId,
|
||||
db: state.db,
|
||||
key: state.key,
|
||||
})
|
||||
.then((res) => {
|
||||
state.total = res;
|
||||
});
|
||||
// ZCARD key
|
||||
props.redis.runCmd(['ZCARD', state.key]).then((res) => {
|
||||
state.total = res;
|
||||
});
|
||||
};
|
||||
|
||||
const showEditDialog = (row: any) => {
|
||||
@@ -195,25 +174,18 @@ const showEditDialog = (row: any) => {
|
||||
};
|
||||
|
||||
const confirmEditData = async () => {
|
||||
const param = getBaseReqParam();
|
||||
|
||||
// 存在数据行,则说明为修改,则要先删除旧数据后新增
|
||||
const dataRow = state.editDialog.dataRow;
|
||||
if (dataRow) {
|
||||
await redisApi.zrem.request({
|
||||
member: state.editDialog.dataRow.value,
|
||||
...param,
|
||||
});
|
||||
// ZREM key member [member ...]
|
||||
await props.redis.runCmd(['ZREM', state.key, state.editDialog.dataRow.value]);
|
||||
}
|
||||
|
||||
const score = state.editDialog.score;
|
||||
// 获取zset member内容并新增
|
||||
const member = formatViewerRef.value.getContent();
|
||||
await redisApi.zadd.request({
|
||||
score,
|
||||
member,
|
||||
...param,
|
||||
});
|
||||
// ZADD key [NX | XX] [GT | LT] [CH] [INCR] score member [score member...]
|
||||
await props.redis.runCmd(['ZADD', state.key, score, member]);
|
||||
|
||||
ElMessage.success('保存成功');
|
||||
if (dataRow) {
|
||||
@@ -228,23 +200,12 @@ const confirmEditData = async () => {
|
||||
};
|
||||
|
||||
const zrem = async (row: any, index: any) => {
|
||||
await redisApi.zrem.request({
|
||||
...getBaseReqParam(),
|
||||
member: row.value,
|
||||
});
|
||||
await props.redis.runCmd(['ZREM', state.key, row.value]);
|
||||
ElMessage.success('删除成功');
|
||||
state.values.splice(index, 1);
|
||||
state.total--;
|
||||
};
|
||||
|
||||
const getBaseReqParam = () => {
|
||||
return {
|
||||
id: state.redisId,
|
||||
db: state.db,
|
||||
key: state.key,
|
||||
};
|
||||
};
|
||||
|
||||
defineExpose({ initData });
|
||||
</script>
|
||||
<style lang="scss"></style>
|
||||
|
||||
@@ -71,6 +71,8 @@
|
||||
<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">
|
||||
@@ -99,6 +101,7 @@ import { ElMessage } from 'element-plus';
|
||||
import { RsaEncrypt } from '@/common/rsa';
|
||||
import TagTreeSelect from '../component/TagTreeSelect.vue';
|
||||
import SshTunnelSelect from '../component/SshTunnelSelect.vue';
|
||||
import ProcdefSelectFormItem from '@/views/flow/components/ProcdefSelectFormItem.vue';
|
||||
|
||||
const props = defineProps({
|
||||
visible: {
|
||||
@@ -170,6 +173,7 @@ const state = reactive({
|
||||
db: '',
|
||||
remark: '',
|
||||
sshTunnelMachineId: -1,
|
||||
flowProcdefKey: '',
|
||||
},
|
||||
submitForm: {} as any,
|
||||
dbList: [0],
|
||||
|
||||
@@ -127,7 +127,7 @@
|
||||
|
||||
<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>
|
||||
|
||||
@@ -13,40 +13,125 @@ export const redisApi = {
|
||||
keyInfo: Api.newGet('/redis/{id}/{db}/key-info'),
|
||||
keyTtl: Api.newGet('/redis/{id}/{db}/key-ttl'),
|
||||
keyMemuse: Api.newGet('/redis/{id}/{db}/key-memuse'),
|
||||
renameKey: Api.newPost('/redis/{id}/{db}/rename-key'),
|
||||
expireKey: Api.newPost('/redis/{id}/{db}/expire-key'),
|
||||
persistKey: Api.newDelete('/redis/{id}/{db}/persist-key'),
|
||||
|
||||
// 获取权限列表
|
||||
// 获取key列表
|
||||
scan: Api.newPost('/redis/{id}/{db}/scan'),
|
||||
getString: Api.newGet('/redis/{id}/{db}/string-value'),
|
||||
setString: Api.newPost('/redis/{id}/{db}/string-value'),
|
||||
getHashValue: Api.newGet('/redis/{id}/{db}/hash-value'),
|
||||
hscan: Api.newGet('/redis/{id}/{db}/hscan'),
|
||||
hget: Api.newGet('/redis/{id}/{db}/hget'),
|
||||
hset: Api.newPost('/redis/{id}/{db}/hset'),
|
||||
hdel: Api.newDelete('/redis/{id}/{db}/hdel'),
|
||||
saveHashValue: Api.newPost('/redis/{id}/{db}/hash-value'),
|
||||
|
||||
getSetValue: Api.newGet('/redis/{id}/{db}/set-value'),
|
||||
scard: Api.newGet('/redis/{id}/{db}/scard'),
|
||||
sscan: Api.newPost('/redis/{id}/{db}/sscan'),
|
||||
sadd: Api.newPost('/redis/{id}/{db}/sadd'),
|
||||
srem: Api.newPost('/redis/{id}/{db}/srem'),
|
||||
saveSetValue: Api.newPost('/redis/{id}/{db}/set-value'),
|
||||
|
||||
del: Api.newDelete('/redis/{id}/{db}/scan/{cursor}/{count}'),
|
||||
delKey: Api.newDelete('/redis/{id}/{db}/key'),
|
||||
flushDb: Api.newDelete('/redis/{id}/{db}/flushdb'),
|
||||
|
||||
lrem: Api.newPost('/redis/{id}/{db}/lrem'),
|
||||
getListValue: Api.newGet('/redis/{id}/{db}/list-value'),
|
||||
saveListValue: Api.newPost('/redis/{id}/{db}/list-value'),
|
||||
setListValue: Api.newPost('/redis/{id}/{db}/list-value/lset'),
|
||||
|
||||
zcard: Api.newGet('/redis/{id}/{db}/zcard'),
|
||||
zscan: Api.newGet('/redis/{id}/{db}/zscan'),
|
||||
zrevrange: Api.newGet('/redis/{id}/{db}/zrevrange'),
|
||||
zadd: Api.newPost('/redis/{id}/{db}/zadd'),
|
||||
zrem: Api.newPost('/redis/{id}/{db}/zrem'),
|
||||
runCmd: Api.newPost('/redis/{id}/{db}/run-cmd'),
|
||||
};
|
||||
|
||||
export function splitargs(line: string) {
|
||||
var ret = [] as any;
|
||||
if (!line || typeof line.length !== 'number') {
|
||||
return ret;
|
||||
}
|
||||
|
||||
var len = line.length;
|
||||
var pos = 0;
|
||||
while (true) {
|
||||
// skip blanks
|
||||
while (pos < len && isspace(line[pos])) {
|
||||
pos += 1;
|
||||
}
|
||||
|
||||
if (pos === len) {
|
||||
break;
|
||||
}
|
||||
|
||||
var inq = false; // if we are in "quotes"
|
||||
var insq = false; // if we are in "single quotes"
|
||||
var done = false;
|
||||
var current = '';
|
||||
|
||||
while (!done) {
|
||||
var c = line[pos];
|
||||
if (inq) {
|
||||
if (c === '\\' && pos + 1 < len) {
|
||||
pos += 1;
|
||||
|
||||
switch (line[pos]) {
|
||||
case 'n':
|
||||
c = '\n';
|
||||
break;
|
||||
case 'r':
|
||||
c = '\r';
|
||||
break;
|
||||
case 't':
|
||||
c = '\t';
|
||||
break;
|
||||
case 'b':
|
||||
c = '\b';
|
||||
break;
|
||||
case 'a':
|
||||
c = 'a';
|
||||
break;
|
||||
default:
|
||||
c = line[pos];
|
||||
break;
|
||||
}
|
||||
current += c;
|
||||
} else if (c === '"') {
|
||||
// closing quote must be followed by a space or
|
||||
// nothing at all.
|
||||
if (pos + 1 < len && !isspace(line[pos + 1])) {
|
||||
throw new Error("Expect '\"' followed by a space or nothing, got '" + line[pos + 1] + "'.");
|
||||
}
|
||||
done = true;
|
||||
} else if (pos === len) {
|
||||
throw new Error('Unterminated quotes.');
|
||||
} else {
|
||||
current += c;
|
||||
}
|
||||
} else if (insq) {
|
||||
if (c === '\\' && line[pos + 1] === "'") {
|
||||
pos += 1;
|
||||
current += "'";
|
||||
} else if (c === "'") {
|
||||
// closing quote must be followed by a space or
|
||||
// nothing at all.
|
||||
if (pos + 1 < len && !isspace(line[pos + 1])) {
|
||||
throw new Error('Expect "\'" followed by a space or nothing, got "' + line[pos + 1] + '".');
|
||||
}
|
||||
done = true;
|
||||
} else if (pos === len) {
|
||||
throw new Error('Unterminated quotes.');
|
||||
} else {
|
||||
current += c;
|
||||
}
|
||||
} else {
|
||||
if (pos === len) {
|
||||
done = true;
|
||||
} else {
|
||||
switch (c) {
|
||||
case ' ':
|
||||
case '\n':
|
||||
case '\r':
|
||||
case '\t':
|
||||
done = true;
|
||||
break;
|
||||
case '"':
|
||||
inq = true;
|
||||
break;
|
||||
case "'":
|
||||
insq = true;
|
||||
break;
|
||||
default:
|
||||
current += c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (pos < len) {
|
||||
pos += 1;
|
||||
}
|
||||
}
|
||||
ret.push(current);
|
||||
current = '';
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
function isspace(ch: string) {
|
||||
return ch === ' ' || ch === '\t' || ch === '\n' || ch === '\r' || ch === '\v' || ch === '\f';
|
||||
}
|
||||
|
||||
28
mayfly_go_web/src/views/ops/redis/components/CmdExecBox.ts
Normal file
28
mayfly_go_web/src/views/ops/redis/components/CmdExecBox.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { h, render } from 'vue';
|
||||
import CmdExecDialog from './CmdExecDialog.vue';
|
||||
|
||||
export type CmdExecProps = {
|
||||
id: number;
|
||||
db: number | string;
|
||||
cmd: any[];
|
||||
flowProcdefKey?: string;
|
||||
visible?: boolean;
|
||||
runSuccessFn?: Function;
|
||||
cancelFn?: Function;
|
||||
};
|
||||
|
||||
const showCmdExecBox = (props: CmdExecProps): void => {
|
||||
const propsCancelFn = props.cancelFn;
|
||||
props.cancelFn = () => {
|
||||
propsCancelFn && propsCancelFn();
|
||||
setTimeout(() => {
|
||||
// 销毁组件
|
||||
render(null, document.body);
|
||||
}, 500);
|
||||
};
|
||||
|
||||
const vnode = h(CmdExecDialog, { ...props, visible: true });
|
||||
render(vnode, document.body);
|
||||
};
|
||||
|
||||
export default showCmdExecBox;
|
||||
@@ -0,0 +1,95 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-dialog title="待执行cmd" v-model="dialogVisible" :show-close="false" width="600px" @close="cancel">
|
||||
<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">
|
||||
<el-divider content-position="left">审批节点</el-divider>
|
||||
<procdef-tasks :procdef-key="props.flowProcdefKey" />
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
<el-button @click="runCmd" type="primary" :loading="btnLoading">执 行</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { toRefs, ref, reactive, onMounted } from 'vue';
|
||||
import { ElDialog, ElButton, ElInput, ElMessage, InputInstance, ElDivider } from 'element-plus';
|
||||
|
||||
import ProcdefTasks from '@/views/flow/components/ProcdefTasks.vue';
|
||||
import { redisApi } from '../api';
|
||||
import { CmdExecProps } from './CmdExecBox';
|
||||
|
||||
const props = withDefaults(defineProps<CmdExecProps>(), {});
|
||||
|
||||
const remarkInputRef = ref<InputInstance>();
|
||||
const state = reactive({
|
||||
dialogVisible: false,
|
||||
flowProcdefKey: '' as any,
|
||||
cmdStr: '',
|
||||
remark: '',
|
||||
btnLoading: false,
|
||||
});
|
||||
|
||||
const { dialogVisible, remark, btnLoading } = toRefs(state);
|
||||
|
||||
onMounted(() => {
|
||||
show(props);
|
||||
});
|
||||
|
||||
const show = (props: CmdExecProps) => {
|
||||
const cmdArr = props.cmd.map((item: any, index: number) => {
|
||||
if (index === 0) {
|
||||
return item; // 第一个元素直接返回原值
|
||||
}
|
||||
if (typeof item === 'string') {
|
||||
return `'${item}'`; // 字符串加单引号
|
||||
}
|
||||
return item; // 其他类型直接返回
|
||||
});
|
||||
state.cmdStr = cmdArr.join(' ');
|
||||
|
||||
state.dialogVisible = props.visible || true;
|
||||
setTimeout(() => {
|
||||
remarkInputRef.value?.focus();
|
||||
}, 200);
|
||||
};
|
||||
|
||||
/**
|
||||
* 执行cmd
|
||||
*/
|
||||
const runCmd = async () => {
|
||||
if (!state.remark) {
|
||||
ElMessage.error('请输入执行备注信息');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
state.btnLoading = true;
|
||||
await redisApi.runCmd.request({
|
||||
id: props.id,
|
||||
db: props.db,
|
||||
cmd: props.cmd,
|
||||
remark: state.remark,
|
||||
});
|
||||
props.runSuccessFn && props.runSuccessFn();
|
||||
ElMessage.success('工单提交成功');
|
||||
} finally {
|
||||
state.btnLoading = false;
|
||||
cancel();
|
||||
}
|
||||
};
|
||||
|
||||
const cancel = () => {
|
||||
state.dialogVisible = false;
|
||||
props.cancelFn && props.cancelFn();
|
||||
};
|
||||
</script>
|
||||
<style lang="scss"></style>
|
||||
138
mayfly_go_web/src/views/ops/redis/redis.ts
Normal file
138
mayfly_go_web/src/views/ops/redis/redis.ts
Normal file
@@ -0,0 +1,138 @@
|
||||
import { redisApi } from './api';
|
||||
import showCmdExecBox from './components/CmdExecBox';
|
||||
|
||||
export class RedisInst {
|
||||
/**
|
||||
* 实例id
|
||||
*/
|
||||
id: number;
|
||||
|
||||
/**
|
||||
* db
|
||||
*/
|
||||
db: number;
|
||||
|
||||
/**
|
||||
* 流程定义key,若存在则需要审批执行
|
||||
*/
|
||||
flowProcdefKey: string;
|
||||
|
||||
/**
|
||||
* 执行命令
|
||||
* @param cmd 命令列表如:['SET', 'key', 'value']
|
||||
* @returns 执行结果
|
||||
*/
|
||||
async runCmd(cmd: any[]) {
|
||||
// 工单流程定义存在,并且为写入命令时,弹窗输入工单相关信息并提交
|
||||
if (this.flowProcdefKey && writeCmd[cmd[0].toUpperCase()]) {
|
||||
showCmdExecBox({
|
||||
id: this.id,
|
||||
db: this.db,
|
||||
flowProcdefKey: this.flowProcdefKey,
|
||||
cmd,
|
||||
});
|
||||
// 报错,阻止后续继续执行
|
||||
throw new Error('提交工单执行');
|
||||
}
|
||||
|
||||
return await redisApi.runCmd.request({
|
||||
id: this.id,
|
||||
db: this.db,
|
||||
cmd,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const writeCmd = {
|
||||
APPEND: 'APPEND key value',
|
||||
BLMOVE: 'BLMOVE source destination LEFT|RIGHT LEFT|RIGHT timeout',
|
||||
BLPOP: 'BLPOP key [key ...] timeout',
|
||||
BRPOP: 'BRPOP key [key ...] timeout',
|
||||
BRPOPLPUSH: 'BRPOPLPUSH source destination timeout',
|
||||
BZPOPMAX: 'BZPOPMAX key [key ...] timeout',
|
||||
BZPOPMIN: 'BZPOPMIN key [key ...] timeout',
|
||||
COPY: 'COPY source destination [DB destination-db] [REPLACE]',
|
||||
DECR: 'DECR key',
|
||||
DECRBY: 'DECRBY key decrement',
|
||||
DEL: 'DEL key [key ...]',
|
||||
EVAL: 'EVAL script numkeys key [key ...] arg [arg ...]',
|
||||
EVALSHA: 'EVALSHA sha1 numkeys key [key ...] arg [arg ...]',
|
||||
EXPIRE: 'EXPIRE key seconds',
|
||||
EXPIREAT: 'EXPIREAT key timestamp',
|
||||
FLUSHALL: 'FLUSHALL',
|
||||
FLUSHDB: 'FLUSHDB',
|
||||
GEOADD: 'GEOADD key [NX|XX] [CH] longitude latitude member [longitude latitude member ...]',
|
||||
GETDEL: 'GETDEL key',
|
||||
GETSET: 'GETSET key value',
|
||||
HDEL: 'HDEL key field [field ...]',
|
||||
HINCRBY: 'HINCRBY key field increment',
|
||||
HINCRBYFLOAT: 'HINCRBYFLOAT key field increment',
|
||||
HMSET: 'HMSET key field value [field value ...]',
|
||||
HSET: 'HSET key field value',
|
||||
HSETNX: 'HSETNX key field value',
|
||||
INCR: 'INCR key',
|
||||
INCRBY: 'INCRBY key increment',
|
||||
INCRBYFLOAT: 'INCRBYFLOAT key increment',
|
||||
LINSERT: 'LINSERT key BEFORE|AFTER pivot value',
|
||||
LMOVE: 'LMOVE source destination LEFT|RIGHT LEFT|RIGHT',
|
||||
LPOP: 'LPOP key',
|
||||
LPUSH: 'LPUSH key value [value ...]',
|
||||
LPUSHX: 'LPUSHX key value',
|
||||
LREM: 'LREM key count value',
|
||||
LSET: 'LSET key index value',
|
||||
LTRIM: 'LTRIM key start stop',
|
||||
MIGRATE: 'MIGRATE host port key destination-db timeout',
|
||||
MOVE: 'MOVE key db',
|
||||
MSET: 'MSET key value [key value ...]',
|
||||
MSETNX: 'MSETNX key value [key value ...]',
|
||||
PERSIST: 'PERSIST key',
|
||||
PEXPIRE: 'PEXPIRE key milliseconds',
|
||||
PEXPIREAT: 'PEXPIREAT key milliseconds-timestamp',
|
||||
PSETEX: 'PSETEX key milliseconds value',
|
||||
PUBLISH: 'PUBLISH channel message',
|
||||
RENAME: 'RENAME key newkey',
|
||||
RENAMENX: 'RENAMENX key newkey',
|
||||
RESTORE: 'RESTORE key ttl serialized-value',
|
||||
RPOP: 'RPOP key',
|
||||
RPOPLPUSH: 'RPOPLPUSH source destination',
|
||||
RPUSH: 'RPUSH key value [value ...]',
|
||||
RPUSHX: 'RPUSHX key value',
|
||||
SADD: 'SADD key member [member ...]',
|
||||
SCRIPT: ['SCRIPT EXISTS script [script ...]', 'SCRIPT FLUSH', 'SCRIPT KILL', 'SCRIPT LOAD script'],
|
||||
SDIFFSTORE: 'SDIFFSTORE destination key [key ...]',
|
||||
SET: 'SET key value',
|
||||
SETBIT: 'SETBIT key offset value',
|
||||
SETEX: 'SETEX key seconds value',
|
||||
SETNX: 'SETNX key value',
|
||||
SETRANGE: 'SETRANGE key offset value',
|
||||
SINTERSTORE: 'SINTERSTORE destination key [key ...]',
|
||||
SMOVE: 'SMOVE source destination member',
|
||||
SORT: 'SORT key [BY pattern] [LIMIT offset count] [GET pattern [GET pattern ...]] [ASC|DESC] [ALPHA] [STORE destination]',
|
||||
SPOP: 'SPOP key',
|
||||
SREM: 'SREM key member [member ...]',
|
||||
SUNIONSTORE: 'SUNIONSTORE destination key [key ...]',
|
||||
SWAPDB: 'SWAPDB index1 index2',
|
||||
UNLINK: 'UNLINK key [key ...]',
|
||||
XADD: 'XADD key ID field string [field string ...]',
|
||||
XDEL: 'XDEL key ID [ID ...]',
|
||||
XGROUP: [
|
||||
'XGROUP CREATE key groupname id|$ [MKSTREAM]',
|
||||
'XGROUP CREATECONSUMER key groupname consumername',
|
||||
'XGROUP DELCONSUMER key groupname consumername',
|
||||
'XGROUP DESTROY key groupname',
|
||||
'XGROUP SETID key groupname id|$',
|
||||
],
|
||||
XTRIM: 'XTRIM key MAXLEN [~] count',
|
||||
ZADD: 'ZADD key score member [score] [member]',
|
||||
ZDIFFSTORE: 'ZDIFFSTORE destination numkeys key [key ...]',
|
||||
ZINCRBY: 'ZINCRBY key increment member',
|
||||
ZINTERSTORE: 'ZINTERSTORE destination numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX]',
|
||||
ZPOPMAX: 'ZPOPMAX key [count]',
|
||||
ZPOPMIN: 'ZPOPMIN key [count]',
|
||||
ZRANGESTORE: 'ZRANGESTORE dst src min max [BYSCORE|BYLEX] [REV] [LIMIT offset count]',
|
||||
ZREM: 'ZREM key member [member ...]',
|
||||
ZREMRANGEBYLEX: 'ZREMRANGEBYLEX key min max',
|
||||
ZREMRANGEBYRANK: 'ZREMRANGEBYRANK key start stop',
|
||||
ZREMRANGEBYSCORE: 'ZREMRANGEBYSCORE key min max',
|
||||
ZUNIONSTORE: 'ZUNIONSTORE destination numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX]',
|
||||
};
|
||||
@@ -27,7 +27,7 @@ export function keysToTree(keys: any, separator: string = ':', openStatus: any =
|
||||
return formatTreeData(tree, '', separator, openStatus, forceCut);
|
||||
}
|
||||
|
||||
export function keysToList(keys: any, separator: string = ':', openStatus: any = null, forceCut = 20000) {
|
||||
export function keysToList(keys: any) {
|
||||
return keys.map((x: string) => {
|
||||
return {
|
||||
key: x,
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-popover v-if="props.accountId" @show="getAccountInfo(props.accountId)" placement="top-start" title="账号信息" :width="400" trigger="click">
|
||||
<template #reference>
|
||||
<el-link type="primary">{{ props.username }}</el-link>
|
||||
</template>
|
||||
<el-descriptions v-loading="loading" :column="2" border>
|
||||
<el-descriptions-item label="username">{{ account.username }}</el-descriptions-item>
|
||||
<el-descriptions-item label="姓名">
|
||||
{{ account.name }}
|
||||
</el-descriptions-item>
|
||||
|
||||
<el-descriptions-item :span="2" label="角色">
|
||||
<el-tag v-for="role in account.roles" :key="role.code" class="ml5">
|
||||
{{ role.roleName }}
|
||||
</el-tag>
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-popover>
|
||||
<span v-else>{{ props.username }}</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { reactive, toRefs } from 'vue';
|
||||
import { accountApi } from '../../api';
|
||||
const props = defineProps({
|
||||
accountId: {
|
||||
type: [Number],
|
||||
},
|
||||
username: {
|
||||
type: [String],
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
const state = reactive({
|
||||
account: {} as any,
|
||||
loading: false,
|
||||
});
|
||||
|
||||
const { account, loading } = toRefs(state);
|
||||
|
||||
const getAccountInfo = async (id: number) => {
|
||||
try {
|
||||
state.loading = true;
|
||||
state.account = await accountApi.getAccountDetail.request({ id });
|
||||
} finally {
|
||||
state.loading = false;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss"></style>
|
||||
@@ -25,6 +25,7 @@ export const roleApi = {
|
||||
export const accountApi = {
|
||||
list: Api.newGet('/sys/accounts'),
|
||||
querySimple: Api.newGet('/sys/accounts/simple'),
|
||||
getAccountDetail: Api.newGet('/sys/accounts/{id}'),
|
||||
save: Api.newPost('/sys/accounts'),
|
||||
update: Api.newPut('/sys/accounts/{id}'),
|
||||
del: Api.newDelete('/sys/accounts/{id}'),
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
<template>
|
||||
<div>
|
||||
<page-table :page-api="logApi.list" :search-items="searchItems" v-model:query-form="query" :columns="columns"> </page-table>
|
||||
<page-table :page-api="logApi.list" :search-items="searchItems" v-model:query-form="query" :columns="columns">
|
||||
<template #creator="{ data }">
|
||||
<account-info :account-id="data.creatorId" :username="data.creator" />
|
||||
</template>
|
||||
</page-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -11,6 +15,7 @@ import PageTable from '@/components/pagetable/PageTable.vue';
|
||||
import { TableColumn } from '@/components/pagetable';
|
||||
import { LogTypeEnum } from '../enums';
|
||||
import { OptionsApi, SearchItem } from '@/components/SearchForm';
|
||||
import AccountInfo from '../account/components/AccountInfo.vue';
|
||||
|
||||
const searchItems = [
|
||||
SearchItem.select('creatorId', '操作人')
|
||||
@@ -33,7 +38,7 @@ const searchItems = [
|
||||
];
|
||||
|
||||
const columns = [
|
||||
TableColumn.new('creator', '操作人'),
|
||||
TableColumn.new('creator', '操作人').isSlot().noShowOverflowTooltip(),
|
||||
TableColumn.new('createTime', '操作时间').isTime(),
|
||||
TableColumn.new('type', '结果').typeTag(LogTypeEnum),
|
||||
TableColumn.new('description', '描述'),
|
||||
|
||||
@@ -16,7 +16,7 @@ var (
|
||||
initFuncs = make([]InitFunc, 0)
|
||||
)
|
||||
|
||||
// 添加初始化ioc函数,由各个默认自行添加
|
||||
// 添加初始化ioc函数,由各个默认自行添加(直接init方法中ioc.Register注册不会打印ioc相关日志)
|
||||
func AddInitIocFunc(initIocFunc InitIocFunc) {
|
||||
initIocFuncs = append(initIocFuncs, initIocFunc)
|
||||
}
|
||||
@@ -37,7 +37,7 @@ func InitOther() {
|
||||
// 为所有注册的实例注入其依赖的其他组件实例
|
||||
biz.ErrIsNil(ioc.InjectComponents())
|
||||
|
||||
// 调用各个默认的初始化函数
|
||||
// 调用各个模块的初始化函数
|
||||
for _, initFunc := range initFuncs {
|
||||
go initFunc()
|
||||
}
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
package application
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/auth/infrastructure/persistence"
|
||||
"mayfly-go/pkg/ioc"
|
||||
)
|
||||
|
||||
func InitIoc() {
|
||||
persistence.Init()
|
||||
|
||||
ioc.Register(new(oauth2AppImpl), ioc.WithComponentName("Oauth2App"))
|
||||
}
|
||||
|
||||
@@ -4,6 +4,6 @@ import (
|
||||
"mayfly-go/pkg/ioc"
|
||||
)
|
||||
|
||||
func Init() {
|
||||
func InitIoc() {
|
||||
ioc.Register(newAuthAccountRepo(), ioc.WithComponentName("Oauth2AccountRepo"))
|
||||
}
|
||||
|
||||
@@ -3,10 +3,14 @@ package init
|
||||
import (
|
||||
"mayfly-go/initialize"
|
||||
"mayfly-go/internal/auth/application"
|
||||
"mayfly-go/internal/auth/infrastructure/persistence"
|
||||
"mayfly-go/internal/auth/router"
|
||||
)
|
||||
|
||||
func init() {
|
||||
initialize.AddInitIocFunc(application.InitIoc)
|
||||
initialize.AddInitIocFunc(func() {
|
||||
persistence.InitIoc()
|
||||
application.InitIoc()
|
||||
})
|
||||
initialize.AddInitRouterFunc(router.Init)
|
||||
}
|
||||
|
||||
@@ -124,7 +124,7 @@ func (d *Db) ExecSql(rc *req.Ctx) {
|
||||
for _, s := range sqls {
|
||||
s = stringx.TrimSpaceAndBr(s)
|
||||
// 多条执行,暂不支持查询语句
|
||||
if isMulti {
|
||||
if isMulti && len(s) > 10 {
|
||||
biz.IsTrue(!strings.HasPrefix(strings.ToLower(s[:10]), "select"), "多条语句执行暂不不支持select语句")
|
||||
}
|
||||
|
||||
|
||||
@@ -2,14 +2,11 @@ package application
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"mayfly-go/internal/db/infrastructure/persistence"
|
||||
"mayfly-go/pkg/ioc"
|
||||
"sync"
|
||||
)
|
||||
|
||||
func InitIoc() {
|
||||
persistence.Init()
|
||||
|
||||
ioc.Register(new(instanceAppImpl), ioc.WithComponentName("DbInstanceApp"))
|
||||
ioc.Register(new(dbAppImpl), ioc.WithComponentName("DbApp"))
|
||||
ioc.Register(new(dbSqlExecAppImpl), ioc.WithComponentName("DbSqlExecApp"))
|
||||
|
||||
@@ -97,7 +97,6 @@ func (d *dbSqlExecAppImpl) Exec(ctx context.Context, execSqlReq *DbSqlExecReq) (
|
||||
// 如果配置为0,则不校验分页参数
|
||||
maxCount := config.GetDbQueryMaxCount()
|
||||
if maxCount != 0 {
|
||||
|
||||
if !strings.Contains(lowerSql, "limit") &&
|
||||
// 兼容oracle rownum分页
|
||||
!strings.Contains(lowerSql, "rownum") &&
|
||||
@@ -118,10 +117,11 @@ func (d *dbSqlExecAppImpl) Exec(ctx context.Context, execSqlReq *DbSqlExecReq) (
|
||||
} else {
|
||||
execRes, execErr = d.doExec(ctx, execSqlReq, dbSqlExecRecord)
|
||||
}
|
||||
|
||||
d.saveSqlExecLog(isSelect, dbSqlExecRecord)
|
||||
if execErr != nil {
|
||||
return nil, execErr
|
||||
}
|
||||
d.saveSqlExecLog(isSelect, dbSqlExecRecord)
|
||||
return execRes, nil
|
||||
}
|
||||
|
||||
@@ -152,10 +152,13 @@ func (d *dbSqlExecAppImpl) Exec(ctx context.Context, execSqlReq *DbSqlExecReq) (
|
||||
return execRes, nil
|
||||
}
|
||||
|
||||
func (d *dbSqlExecAppImpl) FlowBizHandle(ctx context.Context, procinstStatus flowentity.ProcinstStatus, bizKey string) error {
|
||||
func (d *dbSqlExecAppImpl) FlowBizHandle(ctx context.Context, bizHandleParam *flowapp.BizHandleParam) error {
|
||||
bizKey := bizHandleParam.BizKey
|
||||
procinstStatus := bizHandleParam.ProcinstStatus
|
||||
|
||||
logx.Debugf("DbSqlExec FlowBizHandle -> bizKey: %s, procinstStatus: %s", bizKey, flowentity.ProcinstStatusEnum.GetDesc(procinstStatus))
|
||||
// 流程挂起不处理
|
||||
if procinstStatus == flowentity.ProcinstSuspended {
|
||||
if procinstStatus == flowentity.ProcinstStatusSuspended {
|
||||
return nil
|
||||
}
|
||||
dbSqlExec := &entity.DbSqlExec{FlowBizKey: bizKey}
|
||||
@@ -164,7 +167,7 @@ func (d *dbSqlExecAppImpl) FlowBizHandle(ctx context.Context, procinstStatus flo
|
||||
return nil
|
||||
}
|
||||
|
||||
if procinstStatus != flowentity.ProcinstCompleted {
|
||||
if procinstStatus != flowentity.ProcinstStatusCompleted {
|
||||
dbSqlExec.Status = entity.DbSqlExecStatusNo
|
||||
dbSqlExec.Res = fmt.Sprintf("流程%s", flowentity.ProcinstStatusEnum.GetDesc(procinstStatus))
|
||||
return d.dbSqlExecRepo.UpdateById(ctx, dbSqlExec)
|
||||
@@ -364,7 +367,7 @@ func (d *dbSqlExecAppImpl) doExec(ctx context.Context, execSqlReq *DbSqlExecReq,
|
||||
if err != nil {
|
||||
execRes = err.Error()
|
||||
dbSqlExecRecord.Status = entity.DbSqlExecStatusFail
|
||||
dbSqlExecRecord.Res = err.Error()
|
||||
dbSqlExecRecord.Res = execRes
|
||||
} else {
|
||||
dbSqlExecRecord.Res = fmt.Sprintf("执行成功,影响条数: %d", rowsAffected)
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"mayfly-go/pkg/ioc"
|
||||
)
|
||||
|
||||
func Init() {
|
||||
func InitIoc() {
|
||||
ioc.Register(NewInstanceRepo(), ioc.WithComponentName("DbInstanceRepo"))
|
||||
ioc.Register(newDbRepo(), ioc.WithComponentName("DbRepo"))
|
||||
ioc.Register(newDbSqlRepo(), ioc.WithComponentName("DbSqlRepo"))
|
||||
|
||||
@@ -3,11 +3,16 @@ package init
|
||||
import (
|
||||
"mayfly-go/initialize"
|
||||
"mayfly-go/internal/db/application"
|
||||
"mayfly-go/internal/db/infrastructure/persistence"
|
||||
"mayfly-go/internal/db/router"
|
||||
)
|
||||
|
||||
func init() {
|
||||
initialize.AddInitIocFunc(application.InitIoc)
|
||||
initialize.AddInitIocFunc(func() {
|
||||
persistence.InitIoc()
|
||||
application.InitIoc()
|
||||
})
|
||||
|
||||
initialize.AddInitRouterFunc(router.Init)
|
||||
initialize.AddInitFunc(application.Init)
|
||||
initialize.AddTerminateFunc(Terminate)
|
||||
|
||||
@@ -12,6 +12,7 @@ type ProcinstVO struct {
|
||||
|
||||
BizType string `json:"bizType"` // 业务类型
|
||||
BizKey string `json:"bizKey"` // 业务key
|
||||
BizForm string `json:"bizForm"` // 业务form
|
||||
BizStatus int8 `json:"bizStatus"` // 业务状态
|
||||
BizHandleRes string `json:"bizHandleRes"` // 业务处理结果
|
||||
TaskKey string `json:"taskKey"` // 当前任务key
|
||||
@@ -21,6 +22,7 @@ type ProcinstVO struct {
|
||||
Duration int64 `json:"duration"` // 持续时间(开始到结束)
|
||||
|
||||
Creator string `json:"creator"`
|
||||
CreatorId uint64 `json:"creatorId"`
|
||||
CreateTime *time.Time `json:"createTime"`
|
||||
UpdateTime *time.Time `json:"updateTime"`
|
||||
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
package application
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/flow/infrastructure/persistence"
|
||||
"mayfly-go/pkg/ioc"
|
||||
)
|
||||
|
||||
func InitIoc() {
|
||||
persistence.Init()
|
||||
|
||||
ioc.Register(new(procdefAppImpl), ioc.WithComponentName("ProcdefApp"))
|
||||
ioc.Register(new(procinstAppImpl), ioc.WithComponentName("ProcinstApp"))
|
||||
}
|
||||
|
||||
@@ -7,18 +7,19 @@ import (
|
||||
"mayfly-go/pkg/logx"
|
||||
)
|
||||
|
||||
// 流程业务处理函数(流程结束后会根据流程业务类型获取该函数进行处理)
|
||||
// @param procinstStatus 流程实例状态
|
||||
// @param bizKey 业务key,可为业务数据对应的主键
|
||||
// type FlowBizHandlerFunc func(ctx context.Context, procinstStatus entity.ProcinstStatus, bizKey string) error
|
||||
type BizHandleParam struct {
|
||||
BizType string //业务类型
|
||||
BizKey string // 业务key
|
||||
BizForm string // 业务表单信息
|
||||
ProcinstStatus entity.ProcinstStatus // 业务状态
|
||||
}
|
||||
|
||||
// 业务流程处理器(流程状态变更后会根据流程业务类型获取对应的处理器进行回调处理)
|
||||
type FlowBizHandler interface {
|
||||
|
||||
// 业务流程处理函数
|
||||
// @param procinstStatus 流程实例状态
|
||||
// @param bizKey 业务key,可为业务数据对应的主键
|
||||
FlowBizHandle(ctx context.Context, procinstStatus entity.ProcinstStatus, bizKey string) error
|
||||
// @param bizHandleParam 业务处理信息,可获取实例状态、关联业务key等信息
|
||||
FlowBizHandle(ctx context.Context, bizHandleParam *BizHandleParam) error
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -32,11 +33,12 @@ func RegisterBizHandler(flowBizType string, handler FlowBizHandler) {
|
||||
}
|
||||
|
||||
// 流程业务处理
|
||||
func FlowBizHandle(ctx context.Context, flowBizType string, bizKey string, procinstStatus entity.ProcinstStatus) error {
|
||||
func FlowBizHandle(ctx context.Context, bizHandleParam *BizHandleParam) error {
|
||||
flowBizType := bizHandleParam.BizType
|
||||
if handler, ok := handlers[flowBizType]; !ok {
|
||||
logx.Warnf("flow biz handler not found: bizType=%s", flowBizType)
|
||||
return errorx.NewBiz("业务流程处理器不存在")
|
||||
return errorx.NewBiz("业务处理器不存在")
|
||||
} else {
|
||||
return handler.FlowBizHandle(ctx, procinstStatus, bizKey)
|
||||
return handler.FlowBizHandle(ctx, bizHandleParam)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ type StarProcParam struct {
|
||||
BizType string // 业务类型
|
||||
BizKey string // 业务key
|
||||
Remark string // 备注
|
||||
BizForm string // 业务表单信息
|
||||
}
|
||||
|
||||
type CompleteProcinstTaskParam struct {
|
||||
|
||||
@@ -65,10 +65,10 @@ func (p *procdefAppImpl) DeleteProcdef(ctx context.Context, defId uint64) error
|
||||
|
||||
// 判断该流程实例是否可以执行修改操作
|
||||
func (p *procdefAppImpl) canModify(prodefId uint64) error {
|
||||
if activeInstCount := p.procinstApp.CountByCond(&entity.Procinst{ProcdefId: prodefId, Status: entity.ProcinstActive}); activeInstCount > 0 {
|
||||
if activeInstCount := p.procinstApp.CountByCond(&entity.Procinst{ProcdefId: prodefId, Status: entity.ProcinstStatusActive}); activeInstCount > 0 {
|
||||
return errorx.NewBiz("存在运行中的流程实例,无法操作")
|
||||
}
|
||||
if suspInstCount := p.procinstApp.CountByCond(&entity.Procinst{ProcdefId: prodefId, Status: entity.ProcinstSuspended}); suspInstCount > 0 {
|
||||
if suspInstCount := p.procinstApp.CountByCond(&entity.Procinst{ProcdefId: prodefId, Status: entity.ProcinstStatusSuspended}); suspInstCount > 0 {
|
||||
return errorx.NewBiz("存在挂起中的流程实例,无法操作")
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -68,11 +68,12 @@ func (p *procinstAppImpl) StartProc(ctx context.Context, procdefKey string, reqP
|
||||
procinst := &entity.Procinst{
|
||||
BizType: reqParam.BizType,
|
||||
BizKey: reqParam.BizKey,
|
||||
BizForm: reqParam.BizForm,
|
||||
BizStatus: entity.ProcinstBizStatusWait,
|
||||
ProcdefId: procdef.Id,
|
||||
ProcdefName: procdef.Name,
|
||||
Remark: reqParam.Remark,
|
||||
Status: entity.ProcinstActive,
|
||||
Status: entity.ProcinstStatusActive,
|
||||
}
|
||||
|
||||
task := p.getNextTask(procdef, "")
|
||||
@@ -97,7 +98,7 @@ func (p *procinstAppImpl) CancelProc(ctx context.Context, procinstId uint64) err
|
||||
if procinst.CreatorId != la.Id {
|
||||
return errorx.NewBiz("只能取消自己创建的流程")
|
||||
}
|
||||
procinst.Status = entity.ProcinstCancelled
|
||||
procinst.Status = entity.ProcinstStatusCancelled
|
||||
procinst.BizStatus = entity.ProcinstBizStatusNo
|
||||
procinst.SetEnd()
|
||||
|
||||
@@ -130,7 +131,7 @@ func (p *procinstAppImpl) CompleteTask(ctx context.Context, instTaskId uint64, r
|
||||
// 获取下一实例审批任务
|
||||
task := p.getNextTask(procdef, instTask.TaskKey)
|
||||
if task == nil {
|
||||
procinst.Status = entity.ProcinstCompleted
|
||||
procinst.Status = entity.ProcinstStatusCompleted
|
||||
procinst.SetEnd()
|
||||
} else {
|
||||
procinst.TaskKey = task.TaskKey
|
||||
@@ -165,7 +166,7 @@ func (p *procinstAppImpl) RejectTask(ctx context.Context, instTaskId uint64, rem
|
||||
procinst := new(entity.Procinst)
|
||||
p.GetById(procinst, instTask.ProcinstId)
|
||||
// 更新流程实例为终止状态,无法重新提交
|
||||
procinst.Status = entity.ProcinstTerminated
|
||||
procinst.Status = entity.ProcinstStatusTerminated
|
||||
procinst.BizStatus = entity.ProcinstBizStatusNo
|
||||
procinst.SetEnd()
|
||||
|
||||
@@ -192,7 +193,7 @@ func (p *procinstAppImpl) BackTask(ctx context.Context, instTaskId uint64, remar
|
||||
p.GetById(procinst, instTask.ProcinstId)
|
||||
|
||||
// 更新流程实例为挂起状态,等待重新提交
|
||||
procinst.Status = entity.ProcinstSuspended
|
||||
procinst.Status = entity.ProcinstStatusSuspended
|
||||
|
||||
return p.Tx(ctx, func(ctx context.Context) error {
|
||||
return p.UpdateById(ctx, procinst)
|
||||
@@ -219,11 +220,17 @@ func (p *procinstAppImpl) cancelInstTasks(ctx context.Context, procinstId uint64
|
||||
|
||||
// 触发流程实例状态改变事件
|
||||
func (p *procinstAppImpl) triggerProcinstStatusChangeEvent(ctx context.Context, procinst *entity.Procinst) error {
|
||||
err := FlowBizHandle(ctx, procinst.BizType, procinst.BizKey, procinst.Status)
|
||||
err := FlowBizHandle(ctx, &BizHandleParam{
|
||||
BizType: procinst.BizType,
|
||||
BizKey: procinst.BizKey,
|
||||
BizForm: procinst.BizForm,
|
||||
ProcinstStatus: procinst.Status,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
// 业务处理错误,非完成状态则终止流程
|
||||
if procinst.Status != entity.ProcinstCompleted {
|
||||
procinst.Status = entity.ProcinstTerminated
|
||||
if procinst.Status != entity.ProcinstStatusCompleted {
|
||||
procinst.Status = entity.ProcinstStatusTerminated
|
||||
procinst.SetEnd()
|
||||
p.cancelInstTasks(ctx, procinst.Id, "业务处理失败")
|
||||
}
|
||||
@@ -233,7 +240,7 @@ func (p *procinstAppImpl) triggerProcinstStatusChangeEvent(ctx context.Context,
|
||||
}
|
||||
|
||||
// 处理成功,并且状态为完成,则更新业务状态为成功
|
||||
if procinst.Status == entity.ProcinstCompleted {
|
||||
if procinst.Status == entity.ProcinstStatusCompleted {
|
||||
procinst.BizStatus = entity.ProcinstBizStatusSuccess
|
||||
procinst.BizHandleRes = "success"
|
||||
return p.UpdateById(ctx, procinst)
|
||||
|
||||
@@ -15,6 +15,7 @@ type Procinst struct {
|
||||
|
||||
BizType string `json:"bizType"` // 业务类型
|
||||
BizKey string `json:"bizKey"` // 业务key
|
||||
BizForm string `json:"bizForm"` // 业务表单
|
||||
BizStatus ProcinstBizStatus `json:"bizStatus"` // 业务状态
|
||||
BizHandleRes string `json:"bizHandleRes"` // 业务处理结果
|
||||
TaskKey string `json:"taskKey"` // 当前任务key
|
||||
@@ -38,19 +39,19 @@ func (a *Procinst) SetEnd() {
|
||||
type ProcinstStatus int8
|
||||
|
||||
const (
|
||||
ProcinstActive ProcinstStatus = 1 // 流程实例正在执行中,当前有活动任务等待执行或者正在运行的流程节点
|
||||
ProcinstCompleted ProcinstStatus = 2 // 流程实例已经成功执行完成,没有剩余任务或者等待事件
|
||||
ProcinstSuspended ProcinstStatus = -1 // 流程实例被挂起,暂停执行,可能被驳回等待修改重新提交
|
||||
ProcinstTerminated ProcinstStatus = -2 // 流程实例被终止,可能是由于某种原因如被拒绝等导致流程无法正常执行
|
||||
ProcinstCancelled ProcinstStatus = -3 // 流程实例被取消,通常是用户手动操作取消了流程的执行
|
||||
ProcinstStatusActive ProcinstStatus = 1 // 流程实例正在执行中,当前有活动任务等待执行或者正在运行的流程节点
|
||||
ProcinstStatusCompleted ProcinstStatus = 2 // 流程实例已经成功执行完成,没有剩余任务或者等待事件
|
||||
ProcinstStatusSuspended ProcinstStatus = -1 // 流程实例被挂起,暂停执行,可能被驳回等待修改重新提交
|
||||
ProcinstStatusTerminated ProcinstStatus = -2 // 流程实例被终止,可能是由于某种原因如被拒绝等导致流程无法正常执行
|
||||
ProcinstStatusCancelled ProcinstStatus = -3 // 流程实例被取消,通常是用户手动操作取消了流程的执行
|
||||
)
|
||||
|
||||
var ProcinstStatusEnum = enumx.NewEnum[ProcinstStatus]("流程状态").
|
||||
Add(ProcinstActive, "执行中").
|
||||
Add(ProcinstCompleted, "完成").
|
||||
Add(ProcinstSuspended, "挂起").
|
||||
Add(ProcinstTerminated, "终止").
|
||||
Add(ProcinstCancelled, "取消")
|
||||
Add(ProcinstStatusActive, "执行中").
|
||||
Add(ProcinstStatusCompleted, "完成").
|
||||
Add(ProcinstStatusSuspended, "挂起").
|
||||
Add(ProcinstStatusTerminated, "终止").
|
||||
Add(ProcinstStatusCancelled, "取消")
|
||||
|
||||
type ProcinstBizStatus int8
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"mayfly-go/pkg/ioc"
|
||||
)
|
||||
|
||||
func Init() {
|
||||
func InitIoc() {
|
||||
ioc.Register(newProcdefRepo(), ioc.WithComponentName("ProcdefRepo"))
|
||||
ioc.Register(newProcinstRepo(), ioc.WithComponentName("ProcinstRepo"))
|
||||
ioc.Register(newProcinstTaskRepo(), ioc.WithComponentName("ProcinstTaskRepo"))
|
||||
|
||||
@@ -3,10 +3,14 @@ package init
|
||||
import (
|
||||
"mayfly-go/initialize"
|
||||
"mayfly-go/internal/flow/application"
|
||||
"mayfly-go/internal/flow/infrastructure/persistence"
|
||||
"mayfly-go/internal/flow/router"
|
||||
)
|
||||
|
||||
func init() {
|
||||
initialize.AddInitIocFunc(application.InitIoc)
|
||||
initialize.AddInitIocFunc(func() {
|
||||
persistence.InitIoc()
|
||||
application.InitIoc()
|
||||
})
|
||||
initialize.AddInitRouterFunc(router.Init)
|
||||
}
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
package application
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/machine/infrastructure/persistence"
|
||||
"mayfly-go/pkg/ioc"
|
||||
)
|
||||
|
||||
func InitIoc() {
|
||||
persistence.Init()
|
||||
|
||||
ioc.Register(new(machineAppImpl), ioc.WithComponentName("MachineApp"))
|
||||
ioc.Register(new(machineFileAppImpl), ioc.WithComponentName("MachineFileApp"))
|
||||
ioc.Register(new(machineScriptAppImpl), ioc.WithComponentName("MachineScriptApp"))
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"mayfly-go/pkg/ioc"
|
||||
)
|
||||
|
||||
func Init() {
|
||||
func InitIoc() {
|
||||
ioc.Register(newMachineRepo(), ioc.WithComponentName("MachineRepo"))
|
||||
ioc.Register(newMachineFileRepo(), ioc.WithComponentName("MachineFileRepo"))
|
||||
ioc.Register(newMachineScriptRepo(), ioc.WithComponentName("MachineScriptRepo"))
|
||||
|
||||
@@ -6,13 +6,17 @@ import (
|
||||
"mayfly-go/internal/common/consts"
|
||||
"mayfly-go/internal/machine/application"
|
||||
"mayfly-go/internal/machine/domain/entity"
|
||||
"mayfly-go/internal/machine/infrastructure/persistence"
|
||||
"mayfly-go/internal/machine/router"
|
||||
"mayfly-go/pkg/eventbus"
|
||||
"mayfly-go/pkg/global"
|
||||
)
|
||||
|
||||
func init() {
|
||||
initialize.AddInitIocFunc(application.InitIoc)
|
||||
initialize.AddInitIocFunc(func() {
|
||||
persistence.InitIoc()
|
||||
application.InitIoc()
|
||||
})
|
||||
initialize.AddInitRouterFunc(router.Init)
|
||||
initialize.AddInitFunc(Init)
|
||||
}
|
||||
|
||||
@@ -70,6 +70,12 @@ func checkClientAvailability(interval time.Duration) {
|
||||
if cli.Info == nil {
|
||||
continue
|
||||
}
|
||||
if cli.sshClient == nil {
|
||||
continue
|
||||
}
|
||||
if cli.sshClient.Conn == nil {
|
||||
continue
|
||||
}
|
||||
if _, _, err := cli.sshClient.Conn.SendRequest("ping", true, nil); err != nil {
|
||||
logx.Errorf("machine[%s] cache client is not available: %s", cli.Info.Name, err.Error())
|
||||
DeleteCli(cli.Info.Id)
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
package application
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/mongo/infrastructure/persistence"
|
||||
"mayfly-go/pkg/ioc"
|
||||
)
|
||||
|
||||
func InitIoc() {
|
||||
persistence.Init()
|
||||
|
||||
ioc.Register(new(mongoAppImpl), ioc.WithComponentName("MongoApp"))
|
||||
}
|
||||
|
||||
@@ -4,6 +4,6 @@ import (
|
||||
"mayfly-go/pkg/ioc"
|
||||
)
|
||||
|
||||
func Init() {
|
||||
func InitIoc() {
|
||||
ioc.Register(newMongoRepo(), ioc.WithComponentName("MongoRepo"))
|
||||
}
|
||||
|
||||
@@ -3,10 +3,14 @@ package init
|
||||
import (
|
||||
"mayfly-go/initialize"
|
||||
"mayfly-go/internal/mongo/application"
|
||||
"mayfly-go/internal/mongo/infrastructure/persistence"
|
||||
"mayfly-go/internal/mongo/router"
|
||||
)
|
||||
|
||||
func init() {
|
||||
initialize.AddInitIocFunc(application.InitIoc)
|
||||
initialize.AddInitIocFunc(func() {
|
||||
persistence.InitIoc()
|
||||
application.InitIoc()
|
||||
})
|
||||
initialize.AddInitRouterFunc(router.Init)
|
||||
}
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
package application
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/msg/infrastructure/persistence"
|
||||
"mayfly-go/pkg/ioc"
|
||||
)
|
||||
|
||||
func InitIoc() {
|
||||
persistence.Init()
|
||||
|
||||
ioc.Register(new(msgAppImpl), ioc.WithComponentName("MsgApp"))
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,6 @@ import (
|
||||
"mayfly-go/pkg/ioc"
|
||||
)
|
||||
|
||||
func Init() {
|
||||
func InitIoc() {
|
||||
ioc.Register(newMsgRepo(), ioc.WithComponentName("MsgRepo"))
|
||||
}
|
||||
|
||||
@@ -3,10 +3,14 @@ package init
|
||||
import (
|
||||
"mayfly-go/initialize"
|
||||
"mayfly-go/internal/msg/application"
|
||||
"mayfly-go/internal/msg/infrastructure/persistence"
|
||||
"mayfly-go/internal/msg/router"
|
||||
)
|
||||
|
||||
func init() {
|
||||
initialize.AddInitIocFunc(application.InitIoc)
|
||||
initialize.AddInitIocFunc(func() {
|
||||
persistence.InitIoc()
|
||||
application.InitIoc()
|
||||
})
|
||||
initialize.AddInitRouterFunc(router.Init)
|
||||
}
|
||||
|
||||
19
server/internal/redis/api/cmd.go
Normal file
19
server/internal/redis/api/cmd.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/redis/api/form"
|
||||
"mayfly-go/internal/redis/application"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/req"
|
||||
)
|
||||
|
||||
func (r *Redis) RunCmd(rc *req.Ctx) {
|
||||
var cmdReq form.RunCmdForm
|
||||
runCmdParam := req.BindJsonAndCopyTo(rc, &cmdReq, new(application.RunCmdParam))
|
||||
biz.IsTrue(len(cmdReq.Cmd) > 0, "redis命令不能为空")
|
||||
rc.ReqParam = cmdReq
|
||||
|
||||
res, err := r.RedisApp.RunCmd(rc.MetaCtx, r.getRedisConn(rc), runCmdParam)
|
||||
biz.ErrIsNil(err)
|
||||
rc.ResData = res
|
||||
}
|
||||
@@ -11,16 +11,7 @@ type Redis struct {
|
||||
SshTunnelMachineId int `json:"sshTunnelMachineId"` // ssh隧道机器id
|
||||
TagId []uint64 `binding:"required" json:"tagId"`
|
||||
Remark string `json:"remark"`
|
||||
}
|
||||
|
||||
type Rename struct {
|
||||
Key string `binding:"required" json:"key"`
|
||||
NewKey string `binding:"required" json:"newKey"`
|
||||
}
|
||||
|
||||
type Expire struct {
|
||||
Key string `binding:"required" json:"key"`
|
||||
Seconds int64 `binding:"required" json:"seconds"`
|
||||
FlowProcdefKey string `json:"flowProcdefKey"` // 审批流-流程定义key(有值则说明关键操作需要进行审批执行),使用指针为了方便更新空字符串(取消流程审批)
|
||||
}
|
||||
|
||||
type KeyInfo struct {
|
||||
@@ -28,33 +19,6 @@ type KeyInfo struct {
|
||||
Timed int64 `json:"timed"`
|
||||
}
|
||||
|
||||
type StringValue struct {
|
||||
KeyInfo
|
||||
Value any `binding:"required" json:"value"`
|
||||
}
|
||||
|
||||
type HashValue struct {
|
||||
KeyInfo
|
||||
Value []map[string]any `binding:"required" json:"value"`
|
||||
}
|
||||
|
||||
type SetValue struct {
|
||||
KeyInfo
|
||||
Value []any `binding:"required" json:"value"`
|
||||
}
|
||||
|
||||
type ListValue struct {
|
||||
KeyInfo
|
||||
Value []any `binding:"required" json:"value"`
|
||||
}
|
||||
|
||||
// list lset命令参数入参
|
||||
type ListSetValue struct {
|
||||
Key string `binding:"required" json:"key"`
|
||||
Index int64 `json:"index"`
|
||||
Value any `binding:"required" json:"value"`
|
||||
}
|
||||
|
||||
type RedisScanForm struct {
|
||||
Cursor map[string]uint64 `json:"cursor"`
|
||||
Match string `json:"match"`
|
||||
@@ -68,19 +32,9 @@ type ScanForm struct {
|
||||
Count int64 `json:"count"`
|
||||
}
|
||||
|
||||
type SmemberOption struct {
|
||||
Key string `json:"key"`
|
||||
Member any `json:"member"`
|
||||
}
|
||||
|
||||
type LRemOption struct {
|
||||
Key string `json:"key"`
|
||||
Count int64 `json:"count"`
|
||||
Member any `json:"member"`
|
||||
}
|
||||
|
||||
type ZAddOption struct {
|
||||
Key string `json:"key"`
|
||||
Score float64 `json:"score"`
|
||||
Member any `json:"member"`
|
||||
type RunCmdForm struct {
|
||||
Id uint64 `json:"id"`
|
||||
Db int `json:"db"`
|
||||
Cmd []any `json:"cmd"`
|
||||
Remark string `json:"remark"`
|
||||
}
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"mayfly-go/internal/redis/api/form"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/req"
|
||||
"mayfly-go/pkg/utils/collx"
|
||||
"time"
|
||||
)
|
||||
|
||||
func (r *Redis) Hscan(rc *req.Ctx) {
|
||||
ri, key := r.checkKeyAndGetRedisConn(rc)
|
||||
count := rc.QueryIntDefault("count", 10)
|
||||
match := rc.Query("match")
|
||||
cursor := rc.QueryIntDefault("cursor", 0)
|
||||
contextTodo := context.TODO()
|
||||
|
||||
cmdable := ri.GetCmdable()
|
||||
keys, nextCursor, err := cmdable.HScan(contextTodo, key, uint64(cursor), match, int64(count)).Result()
|
||||
biz.ErrIsNilAppendErr(err, "hcan err: %s")
|
||||
keySize, err := cmdable.HLen(contextTodo, key).Result()
|
||||
biz.ErrIsNilAppendErr(err, "hlen err: %s")
|
||||
|
||||
rc.ResData = collx.M{
|
||||
"keys": keys,
|
||||
"cursor": nextCursor,
|
||||
"keySize": keySize,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Redis) Hdel(rc *req.Ctx) {
|
||||
ri, key := r.checkKeyAndGetRedisConn(rc)
|
||||
field := rc.Query("field")
|
||||
|
||||
rc.ReqParam = collx.Kvs("redis", ri.Info, "key", key, "field", field)
|
||||
delRes, err := ri.GetCmdable().HDel(context.TODO(), key, field).Result()
|
||||
biz.ErrIsNilAppendErr(err, "hdel err: %s")
|
||||
rc.ResData = delRes
|
||||
}
|
||||
|
||||
func (r *Redis) Hget(rc *req.Ctx) {
|
||||
ri, key := r.checkKeyAndGetRedisConn(rc)
|
||||
field := rc.Query("field")
|
||||
|
||||
res, err := ri.GetCmdable().HGet(context.TODO(), key, field).Result()
|
||||
biz.ErrIsNilAppendErr(err, "hget err: %s")
|
||||
rc.ResData = res
|
||||
}
|
||||
|
||||
func (r *Redis) Hset(rc *req.Ctx) {
|
||||
hashValue := req.BindJsonAndValid(rc, new(form.HashValue))
|
||||
|
||||
hv := hashValue.Value[0]
|
||||
ri := r.getRedisConn(rc)
|
||||
rc.ReqParam = collx.Kvs("redis", ri.Info, "hash", hv)
|
||||
|
||||
res, err := ri.GetCmdable().HSet(context.TODO(), hashValue.Key, hv["field"].(string), hv["value"]).Result()
|
||||
biz.ErrIsNilAppendErr(err, "hset失败: %s")
|
||||
rc.ResData = res
|
||||
}
|
||||
|
||||
func (r *Redis) SaveHashValue(rc *req.Ctx) {
|
||||
hashValue := req.BindJsonAndValid(rc, new(form.HashValue))
|
||||
|
||||
ri := r.getRedisConn(rc)
|
||||
rc.ReqParam = collx.Kvs("redis", ri.Info, "hash", hashValue)
|
||||
cmd := ri.GetCmdable()
|
||||
|
||||
key := hashValue.Key
|
||||
contextTodo := context.TODO()
|
||||
for _, v := range hashValue.Value {
|
||||
res := cmd.HSet(contextTodo, key, v["field"].(string), v["value"])
|
||||
biz.ErrIsNilAppendErr(res.Err(), "保存hash值失败: %s")
|
||||
}
|
||||
if hashValue.Timed != 0 && hashValue.Timed != -1 {
|
||||
cmd.Expire(context.TODO(), key, time.Second*time.Duration(hashValue.Timed))
|
||||
}
|
||||
}
|
||||
@@ -7,10 +7,8 @@ import (
|
||||
"mayfly-go/internal/redis/rdm"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/req"
|
||||
"mayfly-go/pkg/utils/collx"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/redis/go-redis/v9"
|
||||
)
|
||||
@@ -133,39 +131,3 @@ func (r *Redis) MemoryUsage(rc *req.Ctx) {
|
||||
ri, key := r.checkKeyAndGetRedisConn(rc)
|
||||
rc.ResData = ri.GetCmdable().MemoryUsage(context.Background(), key).Val()
|
||||
}
|
||||
|
||||
func (r *Redis) DeleteKey(rc *req.Ctx) {
|
||||
ri, key := r.checkKeyAndGetRedisConn(rc)
|
||||
rc.ReqParam = collx.Kvs("redis", ri.Info, "key", key)
|
||||
ri.GetCmdable().Del(context.Background(), key)
|
||||
}
|
||||
|
||||
func (r *Redis) RenameKey(rc *req.Ctx) {
|
||||
form := req.BindJsonAndValid(rc, new(form.Rename))
|
||||
|
||||
ri := r.getRedisConn(rc)
|
||||
rc.ReqParam = collx.Kvs("redis", ri.Info, "rename", form)
|
||||
ri.GetCmdable().Rename(context.Background(), form.Key, form.NewKey)
|
||||
}
|
||||
|
||||
func (r *Redis) ExpireKey(rc *req.Ctx) {
|
||||
form := req.BindJsonAndValid(rc, new(form.Expire))
|
||||
|
||||
ri := r.getRedisConn(rc)
|
||||
rc.ReqParam = collx.Kvs("redis", ri.Info, "expire", form)
|
||||
ri.GetCmdable().Expire(context.Background(), form.Key, time.Duration(form.Seconds)*time.Second)
|
||||
}
|
||||
|
||||
// 移除过期时间
|
||||
func (r *Redis) PersistKey(rc *req.Ctx) {
|
||||
ri, key := r.checkKeyAndGetRedisConn(rc)
|
||||
rc.ReqParam = collx.Kvs("redis", ri.Info, "key", key)
|
||||
ri.GetCmdable().Persist(context.Background(), key)
|
||||
}
|
||||
|
||||
// 清空库
|
||||
func (r *Redis) FlushDb(rc *req.Ctx) {
|
||||
ri := r.getRedisConn(rc)
|
||||
rc.ReqParam = ri.Info
|
||||
ri.GetCmdable().FlushDB(context.Background())
|
||||
}
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"mayfly-go/internal/redis/api/form"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/req"
|
||||
"mayfly-go/pkg/utils/collx"
|
||||
)
|
||||
|
||||
func (r *Redis) GetListValue(rc *req.Ctx) {
|
||||
ri, key := r.checkKeyAndGetRedisConn(rc)
|
||||
ctx := context.TODO()
|
||||
cmdable := ri.GetCmdable()
|
||||
|
||||
len, err := cmdable.LLen(ctx, key).Result()
|
||||
biz.ErrIsNilAppendErr(err, "获取list长度失败: %s")
|
||||
|
||||
start := rc.QueryIntDefault("start", 0)
|
||||
stop := rc.QueryIntDefault("stop", 10)
|
||||
res, err := cmdable.LRange(ctx, key, int64(start), int64(stop)).Result()
|
||||
biz.ErrIsNilAppendErr(err, "获取list值失败: %s")
|
||||
|
||||
rc.ResData = collx.M{
|
||||
"len": len,
|
||||
"list": res,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Redis) Lrem(rc *req.Ctx) {
|
||||
option := req.BindJsonAndValid(rc, new(form.LRemOption))
|
||||
|
||||
cmd := r.getRedisConn(rc).GetCmdable()
|
||||
res, err := cmd.LRem(context.TODO(), option.Key, int64(option.Count), option.Member).Result()
|
||||
biz.ErrIsNilAppendErr(err, "lrem失败: %s")
|
||||
rc.ResData = res
|
||||
}
|
||||
|
||||
func (r *Redis) SaveListValue(rc *req.Ctx) {
|
||||
listValue := req.BindJsonAndValid(rc, new(form.ListValue))
|
||||
|
||||
cmd := r.getRedisConn(rc).GetCmdable()
|
||||
|
||||
key := listValue.Key
|
||||
ctx := context.TODO()
|
||||
for _, v := range listValue.Value {
|
||||
cmd.RPush(ctx, key, v)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Redis) Lset(rc *req.Ctx) {
|
||||
listSetValue := req.BindJsonAndValid(rc, new(form.ListSetValue))
|
||||
|
||||
ri := r.getRedisConn(rc)
|
||||
|
||||
_, err := ri.GetCmdable().LSet(context.TODO(), listSetValue.Key, listSetValue.Index, listSetValue.Value).Result()
|
||||
biz.ErrIsNilAppendErr(err, "list set失败: %s")
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"mayfly-go/internal/redis/api/form"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/req"
|
||||
"mayfly-go/pkg/utils/collx"
|
||||
"time"
|
||||
)
|
||||
|
||||
func (r *Redis) GetSetValue(rc *req.Ctx) {
|
||||
ri, key := r.checkKeyAndGetRedisConn(rc)
|
||||
res, err := ri.GetCmdable().SMembers(context.TODO(), key).Result()
|
||||
biz.ErrIsNilAppendErr(err, "获取set值失败: %s")
|
||||
rc.ResData = res
|
||||
}
|
||||
|
||||
func (r *Redis) SaveSetValue(rc *req.Ctx) {
|
||||
keyvalue := req.BindJsonAndValid(rc, new(form.SetValue))
|
||||
|
||||
cmd := r.getRedisConn(rc).GetCmdable()
|
||||
|
||||
key := keyvalue.Key
|
||||
// 简单处理->先删除,后新增
|
||||
cmd.Del(context.TODO(), key)
|
||||
cmd.SAdd(context.TODO(), key, keyvalue.Value...)
|
||||
|
||||
if keyvalue.Timed != -1 {
|
||||
cmd.Expire(context.TODO(), key, time.Second*time.Duration(keyvalue.Timed))
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Redis) Scard(rc *req.Ctx) {
|
||||
ri, key := r.checkKeyAndGetRedisConn(rc)
|
||||
|
||||
total, err := ri.GetCmdable().SCard(context.TODO(), key).Result()
|
||||
biz.ErrIsNilAppendErr(err, "scard失败: %s")
|
||||
rc.ResData = total
|
||||
}
|
||||
|
||||
func (r *Redis) Sscan(rc *req.Ctx) {
|
||||
scan := req.BindJsonAndValid(rc, new(form.ScanForm))
|
||||
|
||||
cmd := r.getRedisConn(rc).GetCmdable()
|
||||
keys, cursor, err := cmd.SScan(context.TODO(), scan.Key, scan.Cursor, scan.Match, scan.Count).Result()
|
||||
biz.ErrIsNilAppendErr(err, "sscan失败: %s")
|
||||
rc.ResData = collx.M{
|
||||
"keys": keys,
|
||||
"cursor": cursor,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Redis) Sadd(rc *req.Ctx) {
|
||||
option := req.BindJsonAndValid(rc, new(form.SmemberOption))
|
||||
|
||||
cmd := r.getRedisConn(rc).GetCmdable()
|
||||
|
||||
res, err := cmd.SAdd(context.TODO(), option.Key, option.Member).Result()
|
||||
biz.ErrIsNilAppendErr(err, "sadd失败: %s")
|
||||
rc.ResData = res
|
||||
}
|
||||
|
||||
func (r *Redis) Srem(rc *req.Ctx) {
|
||||
option := req.BindJsonAndValid(rc, new(form.SmemberOption))
|
||||
|
||||
cmd := r.getRedisConn(rc).GetCmdable()
|
||||
res, err := cmd.SRem(context.TODO(), option.Key, option.Member).Result()
|
||||
biz.ErrIsNilAppendErr(err, "srem失败: %s")
|
||||
rc.ResData = res
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"mayfly-go/internal/redis/api/form"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/req"
|
||||
"mayfly-go/pkg/utils/collx"
|
||||
"time"
|
||||
)
|
||||
|
||||
func (r *Redis) GetStringValue(rc *req.Ctx) {
|
||||
ri, key := r.checkKeyAndGetRedisConn(rc)
|
||||
str, err := ri.GetCmdable().Get(context.TODO(), key).Result()
|
||||
biz.ErrIsNilAppendErr(err, "获取字符串值失败: %s")
|
||||
rc.ResData = str
|
||||
}
|
||||
|
||||
func (r *Redis) SaveStringValue(rc *req.Ctx) {
|
||||
keyValue := req.BindJsonAndValid(rc, new(form.StringValue))
|
||||
|
||||
ri := r.getRedisConn(rc)
|
||||
cmd := ri.GetCmdable()
|
||||
rc.ReqParam = collx.Kvs("redis", ri.Info, "string", keyValue)
|
||||
|
||||
str, err := cmd.Set(context.TODO(), keyValue.Key, keyValue.Value, time.Second*time.Duration(keyValue.Timed)).Result()
|
||||
biz.ErrIsNilAppendErr(err, "保存字符串值失败: %s")
|
||||
rc.ResData = str
|
||||
}
|
||||
@@ -15,6 +15,7 @@ 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"`
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"mayfly-go/internal/redis/api/form"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/req"
|
||||
"mayfly-go/pkg/utils/collx"
|
||||
|
||||
"github.com/redis/go-redis/v9"
|
||||
)
|
||||
|
||||
func (r *Redis) ZCard(rc *req.Ctx) {
|
||||
ri, key := r.checkKeyAndGetRedisConn(rc)
|
||||
|
||||
total, err := ri.GetCmdable().ZCard(context.TODO(), key).Result()
|
||||
biz.ErrIsNilAppendErr(err, "zcard失败: %s")
|
||||
rc.ResData = total
|
||||
}
|
||||
|
||||
func (r *Redis) ZScan(rc *req.Ctx) {
|
||||
ri, key := r.checkKeyAndGetRedisConn(rc)
|
||||
|
||||
cursor := uint64(rc.QueryIntDefault("cursor", 0))
|
||||
match := rc.QueryDefault("match", "*")
|
||||
count := rc.QueryIntDefault("count", 50)
|
||||
|
||||
keys, cursor, err := ri.GetCmdable().ZScan(context.TODO(), key, cursor, match, int64(count)).Result()
|
||||
biz.ErrIsNilAppendErr(err, "sscan失败: %s")
|
||||
rc.ResData = collx.M{
|
||||
"keys": keys,
|
||||
"cursor": cursor,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Redis) ZRevRange(rc *req.Ctx) {
|
||||
ri, key := r.checkKeyAndGetRedisConn(rc)
|
||||
start := rc.QueryIntDefault("start", 0)
|
||||
stop := rc.QueryIntDefault("stop", 50)
|
||||
|
||||
res, err := ri.GetCmdable().ZRevRangeWithScores(context.TODO(), key, int64(start), int64(stop)).Result()
|
||||
biz.ErrIsNilAppendErr(err, "ZRevRange失败: %s")
|
||||
rc.ResData = res
|
||||
}
|
||||
|
||||
func (r *Redis) ZRem(rc *req.Ctx) {
|
||||
option := req.BindJsonAndValid(rc, new(form.SmemberOption))
|
||||
|
||||
cmd := r.getRedisConn(rc).GetCmdable()
|
||||
res, err := cmd.ZRem(context.TODO(), option.Key, option.Member).Result()
|
||||
biz.ErrIsNilAppendErr(err, "zrem失败: %s")
|
||||
rc.ResData = res
|
||||
}
|
||||
|
||||
func (r *Redis) ZAdd(rc *req.Ctx) {
|
||||
option := req.BindJsonAndValid(rc, new(form.ZAddOption))
|
||||
|
||||
cmd := r.getRedisConn(rc).GetCmdable()
|
||||
zm := redis.Z{
|
||||
Score: option.Score,
|
||||
Member: option.Member,
|
||||
}
|
||||
res, err := cmd.ZAdd(context.TODO(), option.Key, zm).Result()
|
||||
biz.ErrIsNilAppendErr(err, "zadd失败: %s")
|
||||
rc.ResData = res
|
||||
}
|
||||
@@ -1,12 +1,13 @@
|
||||
package application
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/redis/infrastructure/persistence"
|
||||
"mayfly-go/pkg/ioc"
|
||||
)
|
||||
|
||||
func InitIoc() {
|
||||
persistence.Init()
|
||||
|
||||
ioc.Register(new(redisAppImpl), ioc.WithComponentName("RedisApp"))
|
||||
}
|
||||
|
||||
func Init() {
|
||||
InitRedisFlowHandler()
|
||||
}
|
||||
|
||||
14
server/internal/redis/application/flow.go
Normal file
14
server/internal/redis/application/flow.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package application
|
||||
|
||||
import (
|
||||
flowapp "mayfly-go/internal/flow/application"
|
||||
"mayfly-go/pkg/ioc"
|
||||
)
|
||||
|
||||
const (
|
||||
RedisRunWriteCmdFlowBizType = "redis_run_write_cmd_flow" // db sql exec flow biz type
|
||||
)
|
||||
|
||||
func InitRedisFlowHandler() {
|
||||
flowapp.RegisterBizHandler(RedisRunWriteCmdFlowBizType, ioc.Get[Redis]("RedisApp"))
|
||||
}
|
||||
@@ -3,20 +3,34 @@ package application
|
||||
import (
|
||||
"context"
|
||||
"mayfly-go/internal/common/consts"
|
||||
flowapp "mayfly-go/internal/flow/application"
|
||||
flowentity "mayfly-go/internal/flow/domain/entity"
|
||||
"mayfly-go/internal/redis/domain/entity"
|
||||
"mayfly-go/internal/redis/domain/repository"
|
||||
"mayfly-go/internal/redis/rdm"
|
||||
tagapp "mayfly-go/internal/tag/application"
|
||||
"mayfly-go/pkg/base"
|
||||
"mayfly-go/pkg/errorx"
|
||||
"mayfly-go/pkg/logx"
|
||||
"mayfly-go/pkg/model"
|
||||
"mayfly-go/pkg/utils/jsonx"
|
||||
"mayfly-go/pkg/utils/stringx"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/redis/go-redis/v9"
|
||||
)
|
||||
|
||||
type RunCmdParam struct {
|
||||
Id uint64 `json:"id"`
|
||||
Db int `json:"db"`
|
||||
Cmd []any `json:"cmd"`
|
||||
Remark string
|
||||
}
|
||||
|
||||
type Redis interface {
|
||||
base.App[*entity.Redis]
|
||||
flowapp.FlowBizHandler
|
||||
|
||||
// 分页获取机器脚本信息列表
|
||||
GetPageList(condition *entity.RedisQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error)
|
||||
@@ -33,12 +47,16 @@ type Redis interface {
|
||||
// id: 数据库实例id
|
||||
// db: 库号
|
||||
GetRedisConn(id uint64, db int) (*rdm.RedisConn, error)
|
||||
|
||||
// 执行redis命令
|
||||
RunCmd(ctx context.Context, redisConn *rdm.RedisConn, cmdParam *RunCmdParam) (any, error)
|
||||
}
|
||||
|
||||
type redisAppImpl struct {
|
||||
base.AppImpl[*entity.Redis, repository.Redis]
|
||||
|
||||
tagApp tagapp.TagTree `inject:"TagTreeApp"`
|
||||
tagApp tagapp.TagTree `inject:"TagTreeApp"`
|
||||
procinstApp flowapp.Procinst `inject:"ProcinstApp"`
|
||||
}
|
||||
|
||||
// 注入RedisRepo
|
||||
@@ -96,7 +114,7 @@ func (r *redisAppImpl) SaveRedis(ctx context.Context, re *entity.Redis, tagIds .
|
||||
return errorx.NewBiz("该实例已存在")
|
||||
}
|
||||
// 如果修改了redis实例的库信息,则关闭旧库的连接
|
||||
if oldRedis.Db != re.Db || oldRedis.SshTunnelMachineId != re.SshTunnelMachineId {
|
||||
if oldRedis.Db != re.Db || oldRedis.SshTunnelMachineId != re.SshTunnelMachineId || oldRedis.FlowProcdefKey != re.FlowProcdefKey {
|
||||
for _, dbStr := range strings.Split(oldRedis.Db, ",") {
|
||||
db, _ := strconv.Atoi(dbStr)
|
||||
rdm.CloseConn(re.Id, db)
|
||||
@@ -151,3 +169,54 @@ func (r *redisAppImpl) GetRedisConn(id uint64, db int) (*rdm.RedisConn, error) {
|
||||
return re.ToRedisInfo(db, r.tagApp.ListTagPathByResource(consts.TagResourceTypeRedis, re.Code)...), nil
|
||||
})
|
||||
}
|
||||
|
||||
func (r *redisAppImpl) RunCmd(ctx context.Context, redisConn *rdm.RedisConn, cmdParam *RunCmdParam) (any, error) {
|
||||
if redisConn == nil {
|
||||
return nil, errorx.NewBiz("redis连接不存在")
|
||||
}
|
||||
|
||||
// 开启工单流程,并且为写入命令,则开启对应审批流程
|
||||
if procdefKey := redisConn.Info.FlowProcdefKey; procdefKey != "" && rdm.IsWriteCmd(cmdParam.Cmd[0]) {
|
||||
_, err := r.procinstApp.StartProc(ctx, procdefKey, &flowapp.StarProcParam{
|
||||
BizType: RedisRunWriteCmdFlowBizType,
|
||||
BizKey: stringx.Rand(24),
|
||||
BizForm: jsonx.ToStr(cmdParam),
|
||||
Remark: cmdParam.Remark,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
res, err := redisConn.RunCmd(ctx, cmdParam.Cmd...)
|
||||
// 获取的key不存在,不报错
|
||||
if err == redis.Nil {
|
||||
return nil, nil
|
||||
}
|
||||
return res, err
|
||||
}
|
||||
|
||||
func (r *redisAppImpl) FlowBizHandle(ctx context.Context, bizHandleParam *flowapp.BizHandleParam) error {
|
||||
bizKey := bizHandleParam.BizKey
|
||||
procinstStatus := bizHandleParam.ProcinstStatus
|
||||
|
||||
logx.Debugf("RedisRunWriteCmd FlowBizHandle -> bizKey: %s, procinstStatus: %s", bizKey, flowentity.ProcinstStatusEnum.GetDesc(procinstStatus))
|
||||
// 流程非完成状态,不处理
|
||||
if procinstStatus != flowentity.ProcinstStatusCompleted {
|
||||
return nil
|
||||
}
|
||||
|
||||
runCmdParam, err := jsonx.To(bizHandleParam.BizForm, new(RunCmdParam))
|
||||
if err != nil {
|
||||
return errorx.NewBiz("业务表单信息解析失败: %s", err.Error())
|
||||
}
|
||||
|
||||
redisConn, err := r.GetRedisConn(runCmdParam.Id, runCmdParam.Db)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = redisConn.RunCmd(ctx, runCmdParam.Cmd...)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -1,17 +1,11 @@
|
||||
package entity
|
||||
|
||||
import "mayfly-go/pkg/model"
|
||||
|
||||
type RedisQuery struct {
|
||||
model.Model
|
||||
Id uint64 `form:"id"`
|
||||
Name string `orm:"column(name)" json:"name" form:"name"`
|
||||
Host string `orm:"column(host)" json:"host" form:"host"`
|
||||
|
||||
Name string `orm:"column(name)" json:"name"`
|
||||
Host string `orm:"column(host)" json:"host"`
|
||||
Mode string `json:"mode"`
|
||||
Password string `orm:"column(password)" json:"-"`
|
||||
Db string `orm:"column(database)" json:"db"`
|
||||
SshTunnelMachineId int `orm:"column(ssh_tunnel_machine_id)" json:"sshTunnelMachineId"` // ssh隧道机器id
|
||||
Remark string
|
||||
SshTunnelMachineId int `orm:"column(ssh_tunnel_machine_id)" json:"sshTunnelMachineId"` // ssh隧道机器id
|
||||
|
||||
Codes []string
|
||||
TagPath string `form:"tagPath"`
|
||||
|
||||
@@ -20,6 +20,7 @@ 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(有值则说明关键操作需要进行审批执行),使用指针为了方便更新空字符串(取消流程审批)
|
||||
}
|
||||
|
||||
func (r *Redis) PwdEncrypt() error {
|
||||
|
||||
@@ -4,6 +4,6 @@ import (
|
||||
"mayfly-go/pkg/ioc"
|
||||
)
|
||||
|
||||
func Init() {
|
||||
func InitIoc() {
|
||||
ioc.Register(newRedisRepo(), ioc.WithComponentName("RedisRepo"))
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ func newRedisRepo() repository.Redis {
|
||||
// 分页获取机器信息列表
|
||||
func (r *redisRepoImpl) GetRedisList(condition *entity.RedisQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error) {
|
||||
qd := gormx.NewQuery(new(entity.Redis)).
|
||||
Eq("id", condition.Id).
|
||||
Like("host", condition.Host).
|
||||
In("code", condition.Codes)
|
||||
return gormx.PageQuery(qd, pageParam, toEntity)
|
||||
|
||||
@@ -3,10 +3,16 @@ package init
|
||||
import (
|
||||
"mayfly-go/initialize"
|
||||
"mayfly-go/internal/redis/application"
|
||||
"mayfly-go/internal/redis/infrastructure/persistence"
|
||||
"mayfly-go/internal/redis/router"
|
||||
)
|
||||
|
||||
func init() {
|
||||
initialize.AddInitIocFunc(application.InitIoc)
|
||||
initialize.AddInitIocFunc(func() {
|
||||
persistence.InitIoc()
|
||||
application.InitIoc()
|
||||
})
|
||||
|
||||
initialize.AddInitRouterFunc(router.Init)
|
||||
initialize.AddInitFunc(application.Init)
|
||||
}
|
||||
|
||||
100
server/internal/redis/rdm/cmd.go
Normal file
100
server/internal/redis/rdm/cmd.go
Normal file
@@ -0,0 +1,100 @@
|
||||
package rdm
|
||||
|
||||
import (
|
||||
"mayfly-go/pkg/utils/anyx"
|
||||
)
|
||||
|
||||
// write cmd
|
||||
var writeCmd = map[string]string{
|
||||
"APPEND": "APPEND key value",
|
||||
"BLMOVE": "BLMOVE source destination LEFT|RIGHT LEFT|RIGHT timeout",
|
||||
"BLPOP": "BLPOP key [key ...] timeout",
|
||||
"BRPOP": "BRPOP key [key ...] timeout",
|
||||
"BRPOPLPUSH": "BRPOPLPUSH source destination timeout",
|
||||
"BZPOPMAX": "BZPOPMAX key [key ...] timeout",
|
||||
"BZPOPMIN": "BZPOPMIN key [key ...] timeout",
|
||||
"COPY": "COPY source destination [DB destination-db] [REPLACE]",
|
||||
"DECR": "DECR key",
|
||||
"DECRBY": "DECRBY key decrement",
|
||||
"DEL": "DEL key [key ...]",
|
||||
"EVAL": "EVAL script numkeys key [key ...] arg [arg ...]",
|
||||
"EVALSHA": "EVALSHA sha1 numkeys key [key ...] arg [arg ...]",
|
||||
"EXPIRE": "EXPIRE key seconds",
|
||||
"EXPIREAT": "EXPIREAT key timestamp",
|
||||
"FLUSHALL": "FLUSHALL",
|
||||
"FLUSHDB": "FLUSHDB",
|
||||
"GEOADD": "GEOADD key [NX|XX] [CH] longitude latitude member [longitude latitude member ...]",
|
||||
"GETDEL": "GETDEL key",
|
||||
"GETSET": "GETSET key value",
|
||||
"HDEL": "HDEL key field [field ...]",
|
||||
"HINCRBY": "HINCRBY key field increment",
|
||||
"HINCRBYFLOAT": "HINCRBYFLOAT key field increment",
|
||||
"HMSET": "HMSET key field value [field value ...]",
|
||||
"HSET": "HSET key field value",
|
||||
"HSETNX": "HSETNX key field value",
|
||||
"INCR": "INCR key",
|
||||
"INCRBY": "INCRBY key increment",
|
||||
"INCRBYFLOAT": "INCRBYFLOAT key increment",
|
||||
"LINSERT": "LINSERT key BEFORE|AFTER pivot value",
|
||||
"LMOVE": "LMOVE source destination LEFT|RIGHT LEFT|RIGHT",
|
||||
"LPOP": "LPOP key",
|
||||
"LPUSH": "LPUSH key value [value ...]",
|
||||
"LPUSHX": "LPUSHX key value",
|
||||
"LREM": "LREM key count value",
|
||||
"LSET": "LSET key index value",
|
||||
"LTRIM": "LTRIM key start stop",
|
||||
"MIGRATE": "MIGRATE host port key destination-db timeout",
|
||||
"MOVE": "MOVE key db",
|
||||
"MSET": "MSET key value [key value ...]",
|
||||
"MSETNX": "MSETNX key value [key value ...]",
|
||||
"PERSIST": "PERSIST key",
|
||||
"PEXPIRE": "PEXPIRE key milliseconds",
|
||||
"PEXPIREAT": "PEXPIREAT key milliseconds-timestamp",
|
||||
"PSETEX": "PSETEX key milliseconds value",
|
||||
"PUBLISH": "PUBLISH channel message",
|
||||
"RENAME": "RENAME key newkey",
|
||||
"RENAMENX": "RENAMENX key newkey",
|
||||
"RESTORE": "RESTORE key ttl serialized-value",
|
||||
"RPOP": "RPOP key",
|
||||
"RPOPLPUSH": "RPOPLPUSH source destination",
|
||||
"RPUSH": "RPUSH key value [value ...]",
|
||||
"RPUSHX": "RPUSHX key value",
|
||||
"SADD": "SADD key member [member ...]",
|
||||
"SCRIPT": "SCRIPT EXISTS script [script ...], 'SCRIPT FLUSH', 'SCRIPT KILL', SCRIPT LOAD script",
|
||||
"SDIFFSTORE": "SDIFFSTORE destination key [key ...]",
|
||||
"SET": "SET key value",
|
||||
"SETBIT": "SETBIT key offset value",
|
||||
"SETEX": "SETEX key seconds value",
|
||||
"SETNX": "SETNX key value",
|
||||
"SETRANGE": "SETRANGE key offset value",
|
||||
"SINTERSTORE": "SINTERSTORE destination key [key ...]",
|
||||
"SMOVE": "SMOVE source destination member",
|
||||
"SORT": "SORT key [BY pattern] [LIMIT offset count] [GET pattern [GET pattern ...]] [ASC|DESC] [ALPHA] [STORE destination]",
|
||||
"SPOP": "SPOP key",
|
||||
"SREM": "SREM key member [member ...]",
|
||||
"SUNIONSTORE": "SUNIONSTORE destination key [key ...]",
|
||||
"SWAPDB": "SWAPDB index1 index2",
|
||||
"UNLINK": "UNLINK key [key ...]",
|
||||
"XADD": "XADD key ID field string [field string ...]",
|
||||
"XDEL": "XDEL key ID [ID ...]",
|
||||
"XGROUP": "XGROUP CREATE key groupname id|$ [MKSTREAM], XGROUP CREATECONSUMER key groupname consumername, XGROUP DELCONSUMER key groupname consumername, XGROUP DESTROY key groupname, XGROUP SETID key groupname id|$",
|
||||
"XTRIM": "XTRIM key MAXLEN [~] count",
|
||||
"ZADD": "ZADD key score member [score] [member]",
|
||||
"ZDIFFSTORE": "ZDIFFSTORE destination numkeys key [key ...]",
|
||||
"ZINCRBY": "ZINCRBY key increment member",
|
||||
"ZINTERSTORE": "ZINTERSTORE destination numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX]",
|
||||
"ZPOPMAX": "ZPOPMAX key [count]",
|
||||
"ZPOPMIN": "ZPOPMIN key [count]",
|
||||
"ZRANGESTORE": "ZRANGESTORE dst src min max [BYSCORE|BYLEX] [REV] [LIMIT offset count]",
|
||||
"ZREM": "ZREM key member [member ...]",
|
||||
"ZREMRANGEBYLEX": "ZREMRANGEBYLEX key min max",
|
||||
"ZREMRANGEBYRANK": "ZREMRANGEBYRANK key start stop",
|
||||
"ZREMRANGEBYSCORE": "ZREMRANGEBYSCORE key min max",
|
||||
"ZUNIONSTORE": "ZUNIONSTORE destination numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX]",
|
||||
}
|
||||
|
||||
// 判断命令是否写命令
|
||||
func IsWriteCmd(cmd any) bool {
|
||||
_, ok := writeCmd[anyx.ConvString(cmd)]
|
||||
return ok
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package rdm
|
||||
|
||||
import (
|
||||
"context"
|
||||
"mayfly-go/pkg/errorx"
|
||||
"mayfly-go/pkg/logx"
|
||||
|
||||
"github.com/redis/go-redis/v9"
|
||||
@@ -32,6 +33,19 @@ func (r *RedisConn) Scan(cursor uint64, match string, count int64) ([]string, ui
|
||||
return r.GetCmdable().Scan(context.Background(), cursor, match, count).Result()
|
||||
}
|
||||
|
||||
// 执行redis命令
|
||||
// 如: SET str value命令则args为['SET', 'str', 'val']
|
||||
func (r *RedisConn) RunCmd(ctx context.Context, args ...any) (any, error) {
|
||||
redisMode := r.Info.Mode
|
||||
if redisMode == "" || redisMode == StandaloneMode || r.Info.Mode == SentinelMode {
|
||||
return r.Cli.Do(ctx, args...).Result()
|
||||
}
|
||||
if redisMode == ClusterMode {
|
||||
return r.ClusterCli.Do(ctx, args...).Result()
|
||||
}
|
||||
return nil, errorx.NewBiz("redis mode error")
|
||||
}
|
||||
|
||||
func (r *RedisConn) Close() {
|
||||
mode := r.Info.Mode
|
||||
if mode == StandaloneMode || mode == SentinelMode {
|
||||
|
||||
@@ -34,6 +34,8 @@ type RedisInfo struct {
|
||||
Name string `json:"-"`
|
||||
TagPath []string `json:"tagPath"`
|
||||
SshTunnelMachineId int `json:"-"`
|
||||
|
||||
FlowProcdefKey string // 工单流程定义key
|
||||
}
|
||||
|
||||
func (r *RedisInfo) Conn() (*RedisConn, error) {
|
||||
|
||||
@@ -18,11 +18,6 @@ func InitRedisRouter(router *gin.RouterGroup) {
|
||||
dashbord := new(api.Dashbord)
|
||||
biz.ErrIsNil(ioc.Inject(dashbord))
|
||||
|
||||
// 保存数据权限
|
||||
saveDataP := req.NewPermission("redis:data:save")
|
||||
// 删除数据权限
|
||||
deleteDataP := req.NewPermission("redis:data:del")
|
||||
|
||||
reqs := [...]*req.Conf{
|
||||
req.NewGet("dashbord", dashbord.Dashbord),
|
||||
|
||||
@@ -41,6 +36,8 @@ func InitRedisRouter(router *gin.RouterGroup) {
|
||||
|
||||
req.NewGet(":id/cluster-info", rs.ClusterInfo),
|
||||
|
||||
req.NewPost(":id/:db/run-cmd", rs.RunCmd).Log(req.NewLogSave("redis-runCmd")),
|
||||
|
||||
// 获取指定redis keys
|
||||
req.NewPost(":id/:db/scan", rs.ScanKeys),
|
||||
|
||||
@@ -49,69 +46,7 @@ func InitRedisRouter(router *gin.RouterGroup) {
|
||||
req.NewGet(":id/:db/key-ttl", rs.TtlKey),
|
||||
|
||||
req.NewGet(":id/:db/key-memuse", rs.MemoryUsage),
|
||||
|
||||
req.NewDelete(":id/:db/key", rs.DeleteKey).Log(req.NewLogSave("redis-删除key")).RequiredPermission(deleteDataP),
|
||||
|
||||
req.NewPost(":id/:db/rename-key", rs.RenameKey).Log(req.NewLogSave("redis-重命名key")).RequiredPermission(saveDataP),
|
||||
|
||||
req.NewPost(":id/:db/expire-key", rs.ExpireKey).Log(req.NewLogSave("redis-设置key过期时间")).RequiredPermission(saveDataP),
|
||||
|
||||
req.NewDelete(":id/:db/persist-key", rs.PersistKey).Log(req.NewLogSave("redis-移除key过期时间")).RequiredPermission(saveDataP),
|
||||
|
||||
req.NewDelete(":id/:db/flushdb", rs.FlushDb).Log(req.NewLogSave("redis-flushdb")).RequiredPermission(deleteDataP),
|
||||
|
||||
// 获取string类型值
|
||||
req.NewGet(":id/:db/string-value", rs.GetStringValue),
|
||||
|
||||
// 设置string类型值
|
||||
req.NewPost(":id/:db/string-value", rs.SaveStringValue).Log(req.NewLogSave("redis-setString")).RequiredPermission(saveDataP),
|
||||
|
||||
// ———————————————— hash操作 ————————————————
|
||||
req.NewGet(":id/:db/hscan", rs.Hscan),
|
||||
|
||||
req.NewGet(":id/:db/hget", rs.Hget),
|
||||
|
||||
req.NewPost(":id/:db/hset", rs.Hset).Log(req.NewLogSave("redis-hset")).RequiredPermission(saveDataP),
|
||||
|
||||
req.NewDelete(":id/:db/hdel", rs.Hdel).Log(req.NewLogSave("redis-hdel")).RequiredPermission(deleteDataP),
|
||||
|
||||
// 设置hash类型值
|
||||
req.NewPost(":id/:db/hash-value", rs.SaveHashValue).Log(req.NewLogSave("redis-setHashValue")).RequiredPermission(saveDataP),
|
||||
|
||||
// --------------- set操作 ----------------
|
||||
req.NewGet(":id/:db/set-value", rs.GetSetValue),
|
||||
|
||||
req.NewPost(":id/:db/set-value", rs.SaveSetValue).RequiredPermission(saveDataP),
|
||||
|
||||
req.NewGet(":id/:db/scard", rs.Scard),
|
||||
|
||||
req.NewPost(":id/:db/sscan", rs.Sscan),
|
||||
|
||||
req.NewPost(":id/:db/sadd", rs.Sadd).RequiredPermission(saveDataP),
|
||||
|
||||
req.NewPost(":id/:db/srem", rs.Srem).RequiredPermission(deleteDataP),
|
||||
|
||||
// --------------- list操作 ----------------
|
||||
req.NewGet(":id/:db/list-value", rs.GetListValue),
|
||||
|
||||
req.NewPost(":id/:db/list-value", rs.SaveListValue).RequiredPermission(saveDataP),
|
||||
|
||||
req.NewPost(":id/:db/list-value/lset", rs.Lset).RequiredPermission(saveDataP),
|
||||
|
||||
req.NewPost(":id/:db/lrem", rs.Lrem).RequiredPermission(deleteDataP),
|
||||
|
||||
// --------------- zset操作 ----------------
|
||||
req.NewGet(":id/:db/zcard", rs.ZCard),
|
||||
|
||||
req.NewGet(":id/:db/zscan", rs.ZScan),
|
||||
|
||||
req.NewGet(":id/:db/zrevrange", rs.ZRevRange),
|
||||
|
||||
req.NewPost(":id/:db/zrem", rs.ZRem).Log(req.NewLogSave("redis-zrem")).RequiredPermission(deleteDataP),
|
||||
|
||||
req.NewPost(":id/:db/zadd", rs.ZAdd).RequiredPermission(saveDataP),
|
||||
}
|
||||
|
||||
req.BatchSetGroup(redis, reqs[:])
|
||||
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
"mayfly-go/pkg/utils/collx"
|
||||
"mayfly-go/pkg/utils/conv"
|
||||
"mayfly-go/pkg/utils/cryptox"
|
||||
"mayfly-go/pkg/utils/structx"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -143,6 +144,18 @@ func (a *Account) SimpleAccounts(rc *req.Ctx) {
|
||||
rc.ResData = res
|
||||
}
|
||||
|
||||
// 获取账号详情
|
||||
func (a *Account) AccountDetail(rc *req.Ctx) {
|
||||
accountId := uint64(rc.PathParamInt("id"))
|
||||
account, err := a.AccountApp.GetById(new(entity.Account), accountId)
|
||||
biz.ErrIsNil(err, "账号不存在")
|
||||
accountvo := new(vo.SimpleAccountVO)
|
||||
structx.Copy(accountvo, account)
|
||||
|
||||
accountvo.Roles = a.getAccountRoles(accountId)
|
||||
rc.ResData = accountvo
|
||||
}
|
||||
|
||||
// @router /accounts
|
||||
func (a *Account) SaveAccount(rc *req.Ctx) {
|
||||
form := &form.AccountCreateForm{}
|
||||
|
||||
@@ -19,6 +19,8 @@ type SimpleAccountVO struct {
|
||||
Id uint64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Username string `json:"username"`
|
||||
|
||||
Roles []*AccountRoleVO `json:"roles" gorm:"-"`
|
||||
}
|
||||
|
||||
// 账号角色信息
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
package application
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/sys/infrastructure/persistence"
|
||||
"mayfly-go/pkg/ioc"
|
||||
)
|
||||
|
||||
func InitIoc() {
|
||||
persistence.Init()
|
||||
|
||||
ioc.Register(new(accountAppImpl), ioc.WithComponentName("AccountApp"))
|
||||
ioc.Register(new(roleAppImpl), ioc.WithComponentName("RoleApp"))
|
||||
ioc.Register(new(configAppImpl), ioc.WithComponentName("ConfigApp"))
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"mayfly-go/pkg/ioc"
|
||||
)
|
||||
|
||||
func Init() {
|
||||
func InitIoc() {
|
||||
ioc.Register(newAccountRepo(), ioc.WithComponentName("AccountRepo"))
|
||||
ioc.Register(newRoleRepo(), ioc.WithComponentName("RoleRepo"))
|
||||
ioc.Register(newAccountRoleRepo(), ioc.WithComponentName("AccountRoleRepo"))
|
||||
|
||||
@@ -3,10 +3,14 @@ package init
|
||||
import (
|
||||
"mayfly-go/initialize"
|
||||
"mayfly-go/internal/sys/application"
|
||||
"mayfly-go/internal/sys/infrastructure/persistence"
|
||||
"mayfly-go/internal/sys/router"
|
||||
)
|
||||
|
||||
func init() {
|
||||
initialize.AddInitIocFunc(application.InitIoc)
|
||||
initialize.AddInitIocFunc(func() {
|
||||
persistence.InitIoc()
|
||||
application.InitIoc()
|
||||
})
|
||||
initialize.AddInitRouterFunc(router.Init)
|
||||
}
|
||||
|
||||
@@ -37,6 +37,9 @@ func InitAccountRouter(router *gin.RouterGroup) {
|
||||
// 获取用户列表信息(只包含最基础信息)
|
||||
req.NewGet("/simple", a.SimpleAccounts),
|
||||
|
||||
// 根据账号id获取账号基础信息
|
||||
req.NewGet("/:id", a.AccountDetail),
|
||||
|
||||
req.NewPost("", a.SaveAccount).Log(req.NewLogSave("保存账号信息")).RequiredPermission(addAccountPermission),
|
||||
|
||||
req.NewPut("change-status/:id/:status", a.ChangeStatus).Log(req.NewLogSave("修改账号状态")).RequiredPermission(addAccountPermission),
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
package application
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/tag/infrastructure/persistence"
|
||||
"mayfly-go/pkg/ioc"
|
||||
)
|
||||
|
||||
func InitIoc() {
|
||||
persistence.Init()
|
||||
|
||||
ioc.Register(new(tagTreeAppImpl), ioc.WithComponentName("TagTreeApp"))
|
||||
ioc.Register(new(teamAppImpl), ioc.WithComponentName("TeamApp"))
|
||||
ioc.Register(new(tagResourceAppImpl), ioc.WithComponentName("TagResourceApp"))
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"mayfly-go/pkg/ioc"
|
||||
)
|
||||
|
||||
func Init() {
|
||||
func InitIoc() {
|
||||
ioc.Register(newTagTreeRepo(), ioc.WithComponentName("TagTreeRepo"))
|
||||
ioc.Register(newTagTreeTeamRepo(), ioc.WithComponentName("TagTreeTeamRepo"))
|
||||
ioc.Register(newTagResourceRepo(), ioc.WithComponentName("TagResourceRepo"))
|
||||
|
||||
@@ -3,10 +3,14 @@ package init
|
||||
import (
|
||||
"mayfly-go/initialize"
|
||||
"mayfly-go/internal/tag/application"
|
||||
"mayfly-go/internal/tag/infrastructure/persistence"
|
||||
"mayfly-go/internal/tag/router"
|
||||
)
|
||||
|
||||
func init() {
|
||||
initialize.AddInitIocFunc(application.InitIoc)
|
||||
initialize.AddInitIocFunc(func() {
|
||||
persistence.InitIoc()
|
||||
application.InitIoc()
|
||||
})
|
||||
initialize.AddInitRouterFunc(router.Init)
|
||||
}
|
||||
|
||||
@@ -617,6 +617,7 @@ CREATE TABLE IF NOT EXISTS "t_redis" (
|
||||
"mode" text(32),
|
||||
"ssh_tunnel_machine_id" integer(20),
|
||||
"remark" text(125),
|
||||
"flow_procdef_key" text(64),
|
||||
"creator" text(32),
|
||||
"creator_id" integer(32),
|
||||
"create_time" datetime,
|
||||
@@ -1024,6 +1025,7 @@ CREATE TABLE "t_flow_procinst" (
|
||||
"status" tinyint DEFAULT NULL ,
|
||||
"biz_type" text(64) NOT NULL ,
|
||||
"biz_key" text(64) NOT NULL ,
|
||||
"biz_form" text(5000) DEFAULT NULL ,
|
||||
"biz_status" tinyint DEFAULT NULL ,
|
||||
"biz_handle_res" text(100) DEFAULT NULL ,
|
||||
"remark" text(191) DEFAULT NULL,
|
||||
|
||||
@@ -531,6 +531,7 @@ 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,
|
||||
@@ -1045,8 +1046,9 @@ CREATE TABLE `t_flow_procinst` (
|
||||
`status` tinyint DEFAULT NULL COMMENT '状态',
|
||||
`biz_type` varchar(64) COLLATE utf8mb4_bin NOT NULL COMMENT '关联业务类型',
|
||||
`biz_key` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL COMMENT '关联业务key',
|
||||
`biz_form` text DEFAULT NULL COMMENT '业务form',
|
||||
`biz_status` tinyint DEFAULT NULL COMMENT '业务状态',
|
||||
`biz_handle_res` varchar(100) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '关联的业务处理结果',
|
||||
`biz_handle_res` varchar(1000) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '关联的业务处理结果',
|
||||
`remark` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL,
|
||||
`end_time` datetime DEFAULT NULL COMMENT '结束时间',
|
||||
`duration` bigint DEFAULT NULL COMMENT '流程持续时间(开始到结束)',
|
||||
|
||||
@@ -35,8 +35,9 @@ CREATE TABLE `t_flow_procinst` (
|
||||
`status` tinyint DEFAULT NULL COMMENT '状态',
|
||||
`biz_type` varchar(64) COLLATE utf8mb4_bin NOT NULL COMMENT '关联业务类型',
|
||||
`biz_key` varchar(64) NOT NULL COMMENT '关联业务key',
|
||||
`biz_form` text DEFAULT NULL COMMENT '业务form',
|
||||
`biz_status` tinyint DEFAULT NULL COMMENT '业务状态',
|
||||
`biz_handle_res` varchar(100) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '关联的业务处理结果',
|
||||
`biz_handle_res` varchar(1000) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '关联的业务处理结果',
|
||||
`remark` varchar(191) DEFAULT NULL,
|
||||
`end_time` datetime DEFAULT NULL COMMENT '结束时间',
|
||||
`duration` bigint DEFAULT NULL COMMENT '流程持续时间(开始到结束)',
|
||||
@@ -80,6 +81,7 @@ ALTER TABLE t_db_sql_exec ADD flow_biz_key varchar(64) NULL COMMENT '工单流
|
||||
ALTER TABLE t_db_sql_exec ADD res varchar(1000) NULL COMMENT '执行结果';
|
||||
|
||||
ALTER TABLE t_db ADD flow_procdef_key varchar(64) NULL COMMENT '审批流-流程定义key(有值则说明关键操作需要进行审批执行)';
|
||||
ALTER TABLE t_redis ADD flow_procdef_key varchar(64) NULL COMMENT '审批流-流程定义key(有值则说明关键操作需要进行审批执行)';
|
||||
|
||||
-- 历史执行记录调整为成功状态
|
||||
UPDATE t_db_sql_exec SET status = 2;
|
||||
|
||||
Reference in New Issue
Block a user