mirror of
https://gitee.com/dromara/mayfly-go
synced 2025-11-02 15:30:25 +08:00
fix: i18n & other optimizations
This commit is contained in:
@@ -27,7 +27,7 @@ export function useI18nPleaseSelect(labelI18nKey: string) {
|
||||
* @returns
|
||||
*/
|
||||
export async function useI18nDeleteConfirm(name: string = '') {
|
||||
return useI18nConfirm('common.deleteConfirm', { name });
|
||||
return useI18nConfirm('common.deleteConfirm2', { name });
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -51,7 +51,8 @@ export default {
|
||||
createTitle: 'Create {name}',
|
||||
editTitle: 'Edit {name}',
|
||||
detailTitle: '{name} Details',
|
||||
deleteConfirm: 'This operation will delete [{name}]. Do you want to continue?',
|
||||
deleteConfirm: 'Sure to delete?',
|
||||
deleteConfirm2: 'This operation will delete [{name}]. Do you want to continue?',
|
||||
saveSuccess: 'save successfully',
|
||||
deleteSuccess: 'delete successfully',
|
||||
operateSuccess: 'operate successfully',
|
||||
|
||||
@@ -73,6 +73,21 @@ export default {
|
||||
newTabRunSql: 'NewTab Run SQL',
|
||||
formatSql: 'Format SQL',
|
||||
|
||||
alias: 'Alias',
|
||||
tableName: 'Name',
|
||||
addColumn: 'Add Column',
|
||||
addDefaultColumn: 'Add Default Column',
|
||||
addIndex: 'Add Index',
|
||||
length: 'Length',
|
||||
numScale: 'Scale',
|
||||
defaultValue: 'Default Value',
|
||||
notNull: 'Not Null',
|
||||
primaryKey: 'Pri',
|
||||
autoIncrement: 'Auto Incr',
|
||||
unique: 'Unique',
|
||||
uniqueIndex: 'Unique Index',
|
||||
normalIndex: 'Normal Index',
|
||||
|
||||
execTime: 'execution time',
|
||||
oneClickCopy: 'One click copy',
|
||||
asc: 'Asc',
|
||||
|
||||
@@ -51,7 +51,8 @@ export default {
|
||||
createTitle: '创建{name}',
|
||||
editTitle: '编辑{name}',
|
||||
detailTitle: '{name}详情',
|
||||
deleteConfirm: '此操作将删除【{name}】, 是否继续?',
|
||||
deleteConfirm: '确定删除?',
|
||||
deleteConfirm2: '此操作将删除【{name}】, 是否继续?',
|
||||
saveSuccess: '保存成功',
|
||||
deleteSuccess: '删除成功',
|
||||
operateSuccess: '操作成功',
|
||||
|
||||
@@ -72,6 +72,21 @@ export default {
|
||||
newTabRunSql: '新标签执行SQL',
|
||||
formatSql: '格式化SQL',
|
||||
|
||||
alias: '别名',
|
||||
tableName: '表名',
|
||||
addColumn: '添加列',
|
||||
addDefaultColumn: '添加默认列',
|
||||
addIndex: '添加索引',
|
||||
length: '长度',
|
||||
numScale: '精度',
|
||||
defaultValue: '默认值',
|
||||
notNull: '非空',
|
||||
primaryKey: '主键',
|
||||
autoIncrement: '自增',
|
||||
unique: '唯一',
|
||||
uniqueIndex: '唯一索引',
|
||||
normalIndex: '普通索引',
|
||||
|
||||
execTime: '执行时间',
|
||||
oneClickCopy: '一键复制',
|
||||
asc: '升序',
|
||||
|
||||
@@ -112,7 +112,7 @@ const perms = {
|
||||
|
||||
const searchItems = [
|
||||
SearchItem.input('keyword', 'common.keyword').withPlaceholder('db.keywordPlaceholder'),
|
||||
getTagPathSearchItem(TagResourceTypeEnum.Db.value),
|
||||
getTagPathSearchItem(TagResourceTypeEnum.DbAuthCert.value),
|
||||
];
|
||||
|
||||
const columns = ref([
|
||||
|
||||
@@ -155,7 +155,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onBeforeUnmount, onMounted, reactive, ref, toRefs, watch } from 'vue';
|
||||
import { onBeforeUnmount, onMounted, reactive, ref, toRefs, watch, Ref } from 'vue';
|
||||
import { ElInput, ElMessage } from 'element-plus';
|
||||
import { copyToClipboard } from '@/common/utils/string';
|
||||
import { DbInst, DbThemeConfig } from '@/views/ops/db/db';
|
||||
@@ -239,9 +239,11 @@ const cmHeaderFixed = new ContextmenuItem('fixed', 'db.fixed')
|
||||
})
|
||||
.withHideFunc((data: any) => data.fixed);
|
||||
|
||||
const cmHeaderCancenFixed = new ContextmenuItem('cancelFixed', 'db.cancelFiexd')
|
||||
const cmHeaderCancelFixed = new ContextmenuItem('cancelFixed', 'db.cancelFiexd')
|
||||
.withIcon('Minus')
|
||||
.withOnClick((data: any) => (data.fixed = false))
|
||||
.withOnClick((data: any) => {
|
||||
data.fixed = false;
|
||||
})
|
||||
.withHideFunc((data: any) => !data.fixed);
|
||||
|
||||
/** 表数据 contextmenu items **/
|
||||
@@ -253,7 +255,7 @@ const cmDataCopyCell = new ContextmenuItem('copyValue', 'common.copy')
|
||||
})
|
||||
.withHideFunc(() => {
|
||||
// 选中多条则隐藏该复制按钮
|
||||
return selectionRowsMap.size > 1;
|
||||
return selectionRowsMap.value.size > 1;
|
||||
});
|
||||
|
||||
const cmDataDel = new ContextmenuItem('deleteData', 'common.delete')
|
||||
@@ -307,7 +309,7 @@ class UpdatedRow {
|
||||
/**
|
||||
* 修改到的列信息, columnName -> tablecelldata
|
||||
*/
|
||||
columnsMap: Map<string, TableCellData> = new Map();
|
||||
columnsMap = new Map<string, TableCellData>();
|
||||
}
|
||||
|
||||
class TableCellData {
|
||||
@@ -319,16 +321,16 @@ class TableCellData {
|
||||
|
||||
let dbDialect: DbDialect = null as any;
|
||||
|
||||
let nowSortColumn = null as any;
|
||||
let nowSortColumn = ref(null) as any;
|
||||
|
||||
// 当前正在更新的单元格
|
||||
let nowUpdateCell: NowUpdateCell = null as any;
|
||||
let nowUpdateCell: Ref<NowUpdateCell> = ref(null) as any;
|
||||
|
||||
// 选中的数据, key->rowIndex value->primaryKeyValue
|
||||
const selectionRowsMap: Map<number, any> = new Map();
|
||||
const selectionRowsMap = ref(new Map<number, any>());
|
||||
|
||||
// 更新单元格 key-> rowIndex value -> 更新行
|
||||
const cellUpdateMap: Map<number, UpdatedRow> = new Map();
|
||||
const cellUpdateMap = ref(new Map<number, UpdatedRow>());
|
||||
|
||||
// 数据加载时间计时器
|
||||
const { pause, resume } = useIntervalFn(() => {
|
||||
@@ -467,8 +469,8 @@ const formatDataValues = (datas: any) => {
|
||||
|
||||
const setTableData = (datas: any) => {
|
||||
tableRef.value?.scrollTo({ scrollLeft: 0, scrollTop: 0 });
|
||||
selectionRowsMap.clear();
|
||||
cellUpdateMap.clear();
|
||||
selectionRowsMap.value.clear();
|
||||
cellUpdateMap.value.clear();
|
||||
formatDataValues(datas);
|
||||
state.datas = datas;
|
||||
setTableColumns(props.columns);
|
||||
@@ -520,7 +522,7 @@ const cancelLoading = async () => {
|
||||
* @param colIndex ci
|
||||
*/
|
||||
const canEdit = (rowIndex: number, colIndex: number) => {
|
||||
return state.table && nowUpdateCell?.rowIndex == rowIndex && nowUpdateCell?.colIndex == colIndex;
|
||||
return state.table && nowUpdateCell.value?.rowIndex == rowIndex && nowUpdateCell.value?.colIndex == colIndex;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -529,7 +531,7 @@ const canEdit = (rowIndex: number, colIndex: number) => {
|
||||
* @param columnName cn
|
||||
*/
|
||||
const isUpdated = (rowIndex: number, columnName: string) => {
|
||||
return cellUpdateMap.get(rowIndex)?.columnsMap.get(columnName);
|
||||
return cellUpdateMap.value.get(rowIndex)?.columnsMap.get(columnName);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -537,7 +539,7 @@ const isUpdated = (rowIndex: number, columnName: string) => {
|
||||
* @param rowIndex
|
||||
*/
|
||||
const isSelection = (rowIndex: number): boolean => {
|
||||
return selectionRowsMap.get(rowIndex);
|
||||
return selectionRowsMap.value.get(rowIndex);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -549,16 +551,14 @@ const isSelection = (rowIndex: number): boolean => {
|
||||
const selectionRow = (rowIndex: number, rowData: any, isMultiple = false) => {
|
||||
if (isMultiple) {
|
||||
// 如果重复点击,则取消改选中数据
|
||||
if (selectionRowsMap.get(rowIndex)) {
|
||||
selectionRowsMap.delete(rowIndex);
|
||||
triggerRefresh();
|
||||
if (selectionRowsMap.value.get(rowIndex)) {
|
||||
selectionRowsMap.value.delete(rowIndex);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
selectionRowsMap.clear();
|
||||
selectionRowsMap.value.clear();
|
||||
}
|
||||
selectionRowsMap.set(rowIndex, rowData);
|
||||
triggerRefresh();
|
||||
selectionRowsMap.value.set(rowIndex, rowData);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -584,7 +584,7 @@ const headerContextmenuClick = (event: any, data: any) => {
|
||||
const { clientX, clientY } = event;
|
||||
state.contextmenu.dropdown.x = clientX;
|
||||
state.contextmenu.dropdown.y = clientY;
|
||||
state.contextmenu.items = [cmHeaderAsc, cmHeaderDesc, cmHeaderFixed, cmHeaderCancenFixed];
|
||||
state.contextmenu.items = [cmHeaderAsc, cmHeaderDesc, cmHeaderFixed, cmHeaderCancelFixed];
|
||||
contextmenuRef.value.openContextmenu(data);
|
||||
};
|
||||
|
||||
@@ -606,7 +606,7 @@ const dataContextmenuClick = (event: any, rowIndex: number, column: any, data: a
|
||||
* 表排序字段变更
|
||||
*/
|
||||
const onTableSortChange = async (sort: any) => {
|
||||
nowSortColumn = sort;
|
||||
nowSortColumn.value = sort;
|
||||
cancelUpdateFields();
|
||||
emits('sortChange', sort);
|
||||
};
|
||||
@@ -615,7 +615,7 @@ const onTableSortChange = async (sort: any) => {
|
||||
* 执行删除数据事件
|
||||
*/
|
||||
const onDeleteData = async () => {
|
||||
const deleteDatas = Array.from(selectionRowsMap.values());
|
||||
const deleteDatas = Array.from(selectionRowsMap.value.values());
|
||||
const db = state.db;
|
||||
const dbInst = getNowDbInst();
|
||||
dbInst.promptExeSql(db, await dbInst.genDeleteByPrimaryKeysSql(db, state.table, deleteDatas as any), null, () => {
|
||||
@@ -624,7 +624,7 @@ const onDeleteData = async () => {
|
||||
};
|
||||
|
||||
const onEditRowData = () => {
|
||||
const selectionDatas = Array.from(selectionRowsMap.values());
|
||||
const selectionDatas = Array.from(selectionRowsMap.value.values());
|
||||
if (selectionDatas.length > 1) {
|
||||
ElMessage.warning(t('db.onlySelectOneData'));
|
||||
return;
|
||||
@@ -636,14 +636,14 @@ const onEditRowData = () => {
|
||||
};
|
||||
|
||||
const onGenerateInsertSql = async () => {
|
||||
const selectionDatas = Array.from(selectionRowsMap.values());
|
||||
const selectionDatas = Array.from(selectionRowsMap.value.values());
|
||||
state.genTxtDialog.txt = await getNowDbInst().genInsertSql(state.db, state.table, selectionDatas);
|
||||
state.genTxtDialog.title = 'SQL';
|
||||
state.genTxtDialog.visible = true;
|
||||
};
|
||||
|
||||
const onGenerateJson = async () => {
|
||||
const selectionDatas = Array.from(selectionRowsMap.values());
|
||||
const selectionDatas = Array.from(selectionRowsMap.value.values());
|
||||
// 按列字段重新排序对象key
|
||||
const jsonObj = [];
|
||||
for (let selectionData of selectionDatas) {
|
||||
@@ -689,8 +689,7 @@ const onEnterEditMode = (rowData: any, column: any, rowIndex = 0, columnIndex =
|
||||
return;
|
||||
}
|
||||
|
||||
triggerRefresh();
|
||||
nowUpdateCell = {
|
||||
nowUpdateCell.value = {
|
||||
rowIndex: rowIndex,
|
||||
colIndex: columnIndex,
|
||||
oldValue: rowData[column.dataKey],
|
||||
@@ -702,21 +701,20 @@ const onExitEditMode = (rowData: any, column: any, rowIndex = 0) => {
|
||||
if (!nowUpdateCell) {
|
||||
return;
|
||||
}
|
||||
const oldValue = nowUpdateCell.oldValue;
|
||||
const oldValue = nowUpdateCell.value.oldValue;
|
||||
const newValue = rowData[column.dataKey];
|
||||
|
||||
// 未改变单元格值
|
||||
if (oldValue == newValue) {
|
||||
nowUpdateCell = null as any;
|
||||
triggerRefresh();
|
||||
nowUpdateCell.value = null as any;
|
||||
return;
|
||||
}
|
||||
|
||||
let updatedRow = cellUpdateMap.get(rowIndex);
|
||||
let updatedRow = cellUpdateMap.value.get(rowIndex);
|
||||
if (!updatedRow) {
|
||||
updatedRow = new UpdatedRow();
|
||||
updatedRow.rowData = rowData;
|
||||
cellUpdateMap.set(rowIndex, updatedRow);
|
||||
cellUpdateMap.value.set(rowIndex, updatedRow);
|
||||
}
|
||||
|
||||
const columnName = column.dataKey;
|
||||
@@ -724,7 +722,7 @@ const onExitEditMode = (rowData: any, column: any, rowIndex = 0) => {
|
||||
if (cellData) {
|
||||
// 多次修改情况,可能又修改回原值,则移除该修改单元格
|
||||
if (cellData.oldValue == newValue) {
|
||||
cellUpdateMap.delete(rowIndex);
|
||||
cellUpdateMap.value.delete(rowIndex);
|
||||
}
|
||||
} else {
|
||||
cellData = new TableCellData();
|
||||
@@ -732,21 +730,20 @@ const onExitEditMode = (rowData: any, column: any, rowIndex = 0) => {
|
||||
updatedRow.columnsMap.set(columnName, cellData);
|
||||
}
|
||||
|
||||
nowUpdateCell = null as any;
|
||||
triggerRefresh();
|
||||
nowUpdateCell.value = null as any;
|
||||
changeUpdatedField();
|
||||
};
|
||||
|
||||
const submitUpdateFields = async () => {
|
||||
const dbInst = getNowDbInst();
|
||||
if (cellUpdateMap.size == 0) {
|
||||
if (cellUpdateMap.value.size == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const db = state.db;
|
||||
let res = '';
|
||||
|
||||
for (let updateRow of cellUpdateMap.values()) {
|
||||
for (let updateRow of cellUpdateMap.value.values()) {
|
||||
const rowData = { ...updateRow.rowData };
|
||||
let updateColumnValue: any = {};
|
||||
|
||||
@@ -763,14 +760,13 @@ const submitUpdateFields = async () => {
|
||||
}
|
||||
|
||||
dbInst.promptExeSql(db, res, null, () => {
|
||||
triggerRefresh();
|
||||
cellUpdateMap.clear();
|
||||
cellUpdateMap.value.clear();
|
||||
changeUpdatedField();
|
||||
});
|
||||
};
|
||||
|
||||
const cancelUpdateFields = () => {
|
||||
const updateRows = cellUpdateMap.values();
|
||||
const updateRows = cellUpdateMap.value.values();
|
||||
// 恢复原值
|
||||
for (let updateRow of updateRows) {
|
||||
const rowData = updateRow.rowData;
|
||||
@@ -778,12 +774,12 @@ const cancelUpdateFields = () => {
|
||||
rowData[k] = v.oldValue;
|
||||
});
|
||||
}
|
||||
cellUpdateMap.clear();
|
||||
cellUpdateMap.value.clear();
|
||||
changeUpdatedField();
|
||||
};
|
||||
|
||||
const changeUpdatedField = () => {
|
||||
emits('changeUpdatedField', cellUpdateMap);
|
||||
emits('changeUpdatedField', cellUpdateMap.value);
|
||||
};
|
||||
|
||||
const rowClass = (row: any) => {
|
||||
@@ -819,18 +815,6 @@ const getFormatTimeValue = (dataType: DataType, originValue: string): string =>
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 触发响应式实时刷新,否则需要滑动或移动才能使样式实时生效
|
||||
*/
|
||||
const triggerRefresh = () => {
|
||||
// 改变columns等属性值,才能触发slot中的if条件等, 暂不知为啥
|
||||
if (state.columns[0].opTimes) {
|
||||
state.columns[0].opTimes = state.columns[0].opTimes + 1;
|
||||
} else {
|
||||
state.columns[0].opTimes = 1;
|
||||
}
|
||||
};
|
||||
|
||||
const scrollLeftValue = ref(0);
|
||||
const onTableScroll = (param: any) => {
|
||||
scrollLeftValue.value = param.scrollLeft;
|
||||
|
||||
@@ -7,21 +7,27 @@
|
||||
<el-form label-position="left" ref="formRef" :model="tableData" label-width="80px">
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item prop="tableName" label="表名">
|
||||
<el-form-item prop="tableName" :label="$t('db.tableName')">
|
||||
<el-input style="width: 80%" v-model="tableData.tableName" size="small"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item prop="tableComment" label="备注">
|
||||
<el-form-item prop="tableComment" :label="$t('db.comment')">
|
||||
<el-input style="width: 80%" v-model="tableData.tableComment" size="small"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-tabs v-model="activeName">
|
||||
<el-tab-pane label="字段" name="1">
|
||||
<el-tab-pane :label="$t('db.column')" name="1">
|
||||
<el-table ref="tableRef" :data="tableData.fields.res" :max-height="tableData.height">
|
||||
<el-table-column :prop="item.prop" :label="item.label" v-for="item in tableData.fields.colNames" :key="item.prop" :width="item.width">
|
||||
<el-table-column
|
||||
:prop="item.prop"
|
||||
:label="$t(item.label)"
|
||||
v-for="item in tableData.fields.colNames"
|
||||
:key="item.prop"
|
||||
:width="item.width"
|
||||
>
|
||||
<template #default="scope">
|
||||
<el-input v-if="item.prop === 'name'" size="small" v-model="scope.row.name" />
|
||||
|
||||
@@ -35,7 +41,7 @@
|
||||
<span v-if="pgsqlType.dataType === pgsqlType.udtName"
|
||||
>{{ pgsqlType.dataType }}{{ pgsqlType.desc && ':' + pgsqlType.desc }}</span
|
||||
>
|
||||
<span v-else>{{ pgsqlType.dataType }},别名:{{ pgsqlType.udtName }} {{ pgsqlType.desc }}</span>
|
||||
<span v-else>{{ pgsqlType.dataType }},{{ $t('db.alias') }}: {{ pgsqlType.udtName }} {{ pgsqlType.desc }}</span>
|
||||
</el-option>
|
||||
</el-select>
|
||||
|
||||
@@ -58,20 +64,20 @@
|
||||
|
||||
<el-input v-else-if="item.prop === 'remark'" size="small" v-model="scope.row.remark" />
|
||||
|
||||
<el-popconfirm v-else-if="item.prop === 'action'" title="确定删除?" @confirm="deleteRow(scope.$index)">
|
||||
<el-popconfirm v-else-if="item.prop === 'action'" :title="$t('common.delete')" @confirm="deleteRow(scope.$index)">
|
||||
<template #reference>
|
||||
<el-link type="danger" plain size="small" :underline="false">删除</el-link>
|
||||
<el-link type="danger" plain size="small" :underline="false">{{ $t('common.delete') }}</el-link>
|
||||
</template>
|
||||
</el-popconfirm>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-row style="margin-top: 20px">
|
||||
<el-button @click="addDefaultRows()" link type="warning" icon="plus">添加默认列</el-button>
|
||||
<el-button @click="addRow()" link type="primary" icon="plus">添加列</el-button>
|
||||
<el-button @click="addDefaultRows()" link type="warning" icon="plus">{{ $t('db.addDefaultColumn') }}</el-button>
|
||||
<el-button @click="addRow()" link type="primary" icon="plus">{{ $t('db.addColumn') }}</el-button>
|
||||
</el-row>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="索引" name="2">
|
||||
<el-tab-pane :label="$t('db.index')" name="2">
|
||||
<el-table :data="tableData.indexs.res" :max-height="tableData.height">
|
||||
<el-table-column :prop="item.prop" :label="item.label" v-for="item in tableData.indexs.colNames" :key="item.prop">
|
||||
<template #default="scope">
|
||||
@@ -84,7 +90,6 @@
|
||||
collapse-tags
|
||||
collapse-tags-tooltip
|
||||
filterable
|
||||
placeholder="请选择字段"
|
||||
@change="indexChanges(scope.row)"
|
||||
style="width: 100%"
|
||||
>
|
||||
@@ -100,9 +105,9 @@
|
||||
|
||||
<el-input v-if="item.prop === 'indexComment'" size="small" v-model="scope.row.indexComment"> </el-input>
|
||||
|
||||
<el-popconfirm v-else-if="item.prop === 'action'" title="确定删除?" @confirm="deleteIndex(scope.$index)">
|
||||
<el-popconfirm v-else-if="item.prop === 'action'" :title="$t('common.deleteConfirm')" @confirm="deleteIndex(scope.$index)">
|
||||
<template #reference>
|
||||
<el-link type="danger" plain size="small" :underline="false">删除</el-link>
|
||||
<el-link type="danger" plain size="small" :underline="false">{{ $t('common.delete') }}</el-link>
|
||||
</template>
|
||||
</el-popconfirm>
|
||||
</template>
|
||||
@@ -110,14 +115,14 @@
|
||||
</el-table>
|
||||
|
||||
<el-row style="margin-top: 20px">
|
||||
<el-button @click="addIndex()" link type="primary" icon="plus">添加索引</el-button>
|
||||
<el-button @click="addIndex()" link type="primary" icon="plus">{{ $t('db.addIndex') }}</el-button>
|
||||
</el-row>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="cancel()">取消</el-button>
|
||||
<el-button :loading="btnloading" @click="submit()" type="primary">保存</el-button>
|
||||
<el-button @click="cancel()">{{ $t('common.cancel') }}</el-button>
|
||||
<el-button :loading="btnloading" @click="submit()" type="primary">{{ $t('common.save') }}</el-button>
|
||||
</template>
|
||||
</el-drawer>
|
||||
</template>
|
||||
@@ -129,6 +134,9 @@ import SqlExecBox from '../sqleditor/SqlExecBox';
|
||||
import { DbType, getDbDialect, IndexDefinition, RowDefinition } from '../../dialect/index';
|
||||
import { DbInst } from '../../db';
|
||||
import DrawerHeader from '@/components/drawer-header/DrawerHeader.vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const props = defineProps({
|
||||
visible: {
|
||||
@@ -177,52 +185,52 @@ const state = reactive({
|
||||
colNames: [
|
||||
{
|
||||
prop: 'name',
|
||||
label: '字段名称',
|
||||
label: 'db.columnName',
|
||||
width: 200,
|
||||
},
|
||||
{
|
||||
prop: 'type',
|
||||
label: '字段类型',
|
||||
label: 'common.type',
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
prop: 'length',
|
||||
label: '长度',
|
||||
label: 'db.length',
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
prop: 'numScale',
|
||||
label: '小数精度',
|
||||
label: 'db.numScale',
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
prop: 'value',
|
||||
label: '默认值',
|
||||
label: 'db.defaultValue',
|
||||
width: 120,
|
||||
},
|
||||
|
||||
{
|
||||
prop: 'notNull',
|
||||
label: '非空',
|
||||
label: 'db.notNull',
|
||||
width: 60,
|
||||
},
|
||||
{
|
||||
prop: 'pri',
|
||||
label: '主键',
|
||||
label: 'db.primaryKey',
|
||||
width: 60,
|
||||
},
|
||||
{
|
||||
prop: 'auto_increment',
|
||||
label: '自增',
|
||||
label: 'db.autoIncrement',
|
||||
width: 60,
|
||||
},
|
||||
{
|
||||
prop: 'remark',
|
||||
label: '备注',
|
||||
label: 'db.comment',
|
||||
},
|
||||
{
|
||||
prop: 'action',
|
||||
label: '操作',
|
||||
label: 'common.operation',
|
||||
width: 70,
|
||||
},
|
||||
] as ColName[],
|
||||
@@ -233,27 +241,27 @@ const state = reactive({
|
||||
colNames: [
|
||||
{
|
||||
prop: 'indexName',
|
||||
label: '索引名',
|
||||
label: 'db.indexName',
|
||||
},
|
||||
{
|
||||
prop: 'columnNames',
|
||||
label: '列名',
|
||||
label: 'db.columnName',
|
||||
},
|
||||
{
|
||||
prop: 'unique',
|
||||
label: '唯一',
|
||||
label: 'db.unique',
|
||||
},
|
||||
{
|
||||
prop: 'indexType',
|
||||
label: '类型',
|
||||
label: 'common.type',
|
||||
},
|
||||
{
|
||||
prop: 'indexComment',
|
||||
label: '备注',
|
||||
label: 'db.comment',
|
||||
},
|
||||
{
|
||||
prop: 'action',
|
||||
label: '操作',
|
||||
label: 'common.operation',
|
||||
},
|
||||
],
|
||||
columns: [{ name: '', remark: '' }],
|
||||
@@ -336,7 +344,7 @@ const deleteIndex = (index: any) => {
|
||||
const submit = async () => {
|
||||
let sql = genSql();
|
||||
if (!sql) {
|
||||
ElMessage.warning('没有更改');
|
||||
ElMessage.warning(t('db.noChange'));
|
||||
return;
|
||||
}
|
||||
SqlExecBox({
|
||||
@@ -472,10 +480,10 @@ const indexChanges = (row: any) => {
|
||||
}
|
||||
|
||||
let suffix = row.unique ? 'udx' : 'idx';
|
||||
let commentSuffix = row.unique ? '唯一索引' : '普通索引';
|
||||
let commentSuffix = row.unique ? t('db.uniqueIndex') : t('db.normalIndex');
|
||||
// 以表名为前缀
|
||||
row.indexName = `${tableData.value.tableName}_${name}_${suffix}`.replaceAll(' ', '');
|
||||
row.indexComment = `${tableData.value.tableName}表(${name.replaceAll('_', ',')})${commentSuffix}`;
|
||||
row.indexComment = `${tableData.value.tableName} ${t('db.table')} (${name.replaceAll('_', ',')})${commentSuffix}`;
|
||||
};
|
||||
|
||||
const disableEditIncr = () => {
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
</div>
|
||||
|
||||
<el-divider content-position="left">{{ $t('common.other') }}</el-divider>
|
||||
<el-form-item prop="enableRecorder" :label="$t('machine.sshTunnel')">
|
||||
<el-form-item prop="enableRecorder" :label="$t('machine.terminalPlayback')">
|
||||
<el-checkbox v-model="form.enableRecorder" :true-value="1" :false-value="-1"></el-checkbox>
|
||||
</el-form-item>
|
||||
|
||||
|
||||
@@ -185,22 +185,19 @@
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column prop="mode" :label="$t('machine.attribute')" width="110"> </el-table-column>
|
||||
<el-table-column
|
||||
v-if="$props.protocol == MachineProtocolEnum.Ssh.value"
|
||||
prop="username"
|
||||
:label="$t('machine.user')"
|
||||
min-width="70"
|
||||
show-overflow-tooltip
|
||||
>
|
||||
|
||||
<el-table-column v-if="$props.protocol == MachineProtocolEnum.Ssh.value" :label="$t('machine.user')" min-width="70" show-overflow-tooltip>
|
||||
<template #default="scope">
|
||||
{{ userMap.get(scope.row.uid)?.uname || scope.row.uid }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
v-if="$props.protocol == MachineProtocolEnum.Ssh.value"
|
||||
prop="groupname"
|
||||
:label="$t('machine.group')"
|
||||
min-width="70"
|
||||
show-overflow-tooltip
|
||||
>
|
||||
|
||||
<el-table-column v-if="$props.protocol == MachineProtocolEnum.Ssh.value" :label="$t('machine.group')" min-width="70" show-overflow-tooltip>
|
||||
<template #default="scope">
|
||||
{{ groupMap.get(scope.row.gid)?.gname || scope.row.gid }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column prop="modTime" :label="$t('machine.modificationTime')" width="160" sortable> </el-table-column>
|
||||
|
||||
<el-table-column :width="130">
|
||||
@@ -303,7 +300,7 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, onMounted, reactive, ref, toRefs } from 'vue';
|
||||
import { ElInput, ElMessage, ElMessageBox } from 'element-plus';
|
||||
import { ElInput, ElMessage } from 'element-plus';
|
||||
import { machineApi } from '../api';
|
||||
|
||||
import { joinClientParams } from '@/common/request';
|
||||
@@ -334,8 +331,8 @@ const folderUploadRef: any = ref();
|
||||
|
||||
const folderType = 'd';
|
||||
|
||||
const userMap = new Map<number, any>();
|
||||
const groupMap = new Map<number, any>();
|
||||
const userMap = ref(new Map<number, any>());
|
||||
const groupMap = ref(new Map<number, any>());
|
||||
|
||||
// 路径分隔符
|
||||
const pathSep = '/';
|
||||
@@ -382,13 +379,13 @@ onMounted(async () => {
|
||||
if (props.protocol == MachineProtocolEnum.Ssh.value) {
|
||||
machineApi.users.request({ id: machineId }).then((res: any) => {
|
||||
for (let user of res) {
|
||||
userMap.set(user.uid, user);
|
||||
userMap.value.set(user.uid, user);
|
||||
}
|
||||
});
|
||||
|
||||
machineApi.groups.request({ id: machineId }).then((res: any) => {
|
||||
for (let group of res) {
|
||||
groupMap.set(group.gid, group);
|
||||
groupMap.value.set(group.gid, group);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -565,11 +562,6 @@ const lsFile = async (path: string) => {
|
||||
path,
|
||||
});
|
||||
for (const file of res) {
|
||||
if (props.protocol == MachineProtocolEnum.Ssh.value) {
|
||||
file.username = userMap.get(file.uid)?.uname || file.uid;
|
||||
file.groupname = groupMap.get(file.gid)?.gname || file.gid;
|
||||
}
|
||||
|
||||
const type = file.type;
|
||||
if (type == folderType) {
|
||||
file.isFolder = true;
|
||||
|
||||
@@ -121,10 +121,10 @@
|
||||
|
||||
<el-dialog width="400px" :title="$t('mongo.createDbAndColl')" v-model="createDbDialog.visible" :destroy-on-close="true">
|
||||
<el-form :model="createDbDialog.form" label-width="auto">
|
||||
<el-form-item prop="dbName" :title="$t('mongo.dbName')" required>
|
||||
<el-form-item prop="dbName" :label="$t('mongo.dbName')" required>
|
||||
<el-input v-model="createDbDialog.form.dbName" clearable></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="collectionName" :title="$t('mongo.collName')" required>
|
||||
<el-form-item prop="collectionName" :label="$t('mongo.collName')" required>
|
||||
<el-input v-model="createDbDialog.form.collectionName" clearable></el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
@@ -16,10 +16,10 @@ const (
|
||||
// RedisConnExpireTime = 2 * time.Minute
|
||||
// MongoConnExpireTime = 2 * time.Minute
|
||||
|
||||
ResourceTypeMachine int8 = 1
|
||||
ResourceTypeDb int8 = 2
|
||||
ResourceTypeRedis int8 = 3
|
||||
ResourceTypeMongo int8 = 4
|
||||
ResourceTypeMachine int8 = 1
|
||||
ResourceTypeDbInstance int8 = 2
|
||||
ResourceTypeRedis int8 = 3
|
||||
ResourceTypeMongo int8 = 4
|
||||
|
||||
// imsg起始编号
|
||||
ImsgNumSys = 10000
|
||||
|
||||
@@ -16,7 +16,7 @@ type Dashbord struct {
|
||||
func (m *Dashbord) Dashbord(rc *req.Ctx) {
|
||||
accountId := rc.GetLoginAccount().Id
|
||||
|
||||
tagCodePaths := m.TagTreeApp.GetAccountTagCodePaths(accountId, tagentity.TagTypeDbName, "")
|
||||
tagCodePaths := m.TagTreeApp.GetAccountTags(accountId, &tagentity.TagTreeQuery{Types: collx.AsArray(tagentity.TagTypeDb)}).GetCodePaths()
|
||||
|
||||
rc.ResData = collx.M{
|
||||
"dbNum": len(tagCodePaths),
|
||||
|
||||
@@ -48,12 +48,15 @@ func (d *Db) Dbs(rc *req.Ctx) {
|
||||
queryCond, page := req.BindQueryAndPage[*entity.DbQuery](rc, new(entity.DbQuery))
|
||||
|
||||
// 不存在可访问标签id,即没有可操作数据
|
||||
codes := d.TagApp.GetAccountTagCodes(rc.GetLoginAccount().Id, int8(tagentity.TagTypeDbName), queryCond.TagPath)
|
||||
if len(codes) == 0 {
|
||||
tags := d.TagApp.GetAccountTags(rc.GetLoginAccount().Id, &tagentity.TagTreeQuery{
|
||||
Types: collx.AsArray(tagentity.TagTypeDb),
|
||||
CodePathLikes: collx.AsArray(queryCond.TagPath),
|
||||
})
|
||||
if len(tags) == 0 {
|
||||
rc.ResData = model.EmptyPageResult[any]()
|
||||
return
|
||||
}
|
||||
queryCond.Codes = codes
|
||||
queryCond.Codes = tags.GetCodes()
|
||||
|
||||
var dbvos []*vo.DbListVO
|
||||
res, err := d.DbApp.GetPageList(queryCond, page, &dbvos)
|
||||
|
||||
@@ -31,14 +31,18 @@ type Instance struct {
|
||||
func (d *Instance) Instances(rc *req.Ctx) {
|
||||
queryCond, page := req.BindQueryAndPage[*entity.InstanceQuery](rc, new(entity.InstanceQuery))
|
||||
|
||||
tagCodePaths := d.TagApp.GetAccountTagCodePaths(rc.GetLoginAccount().Id, tagentity.TagTypeDbAuthCert, queryCond.TagPath)
|
||||
tags := d.TagApp.GetAccountTags(rc.GetLoginAccount().Id, &tagentity.TagTreeQuery{
|
||||
Types: collx.AsArray(tagentity.TagTypeDbAuthCert),
|
||||
CodePathLikes: collx.AsArray(queryCond.TagPath),
|
||||
})
|
||||
// 不存在可操作的数据库,即没有可操作数据
|
||||
if len(tagCodePaths) == 0 {
|
||||
if len(tags) == 0 {
|
||||
rc.ResData = model.EmptyPageResult[any]()
|
||||
return
|
||||
}
|
||||
|
||||
dbInstCodes := tagentity.GetCodeByPath(tagentity.TagTypeDb, tagCodePaths...)
|
||||
tagCodePaths := tags.GetCodePaths()
|
||||
dbInstCodes := tagentity.GetCodesByCodePaths(tagentity.TagTypeDbInstance, tagCodePaths...)
|
||||
queryCond.Codes = dbInstCodes
|
||||
|
||||
var instvos []*vo.InstanceListVO
|
||||
@@ -46,12 +50,12 @@ func (d *Instance) Instances(rc *req.Ctx) {
|
||||
biz.ErrIsNil(err)
|
||||
|
||||
// 填充授权凭证信息
|
||||
d.ResourceAuthCertApp.FillAuthCertByAcNames(tagentity.GetCodeByPath(tagentity.TagTypeDbAuthCert, tagCodePaths...), collx.ArrayMap(instvos, func(vos *vo.InstanceListVO) tagentity.IAuthCert {
|
||||
d.ResourceAuthCertApp.FillAuthCertByAcNames(tagentity.GetCodesByCodePaths(tagentity.TagTypeDbAuthCert, tagCodePaths...), collx.ArrayMap(instvos, func(vos *vo.InstanceListVO) tagentity.IAuthCert {
|
||||
return vos
|
||||
})...)
|
||||
|
||||
// 填充标签信息
|
||||
d.TagApp.FillTagInfo(tagentity.TagType(consts.ResourceTypeDb), collx.ArrayMap(instvos, func(insvo *vo.InstanceListVO) tagentity.ITagResource {
|
||||
d.TagApp.FillTagInfo(tagentity.TagType(consts.ResourceTypeDbInstance), collx.ArrayMap(instvos, func(insvo *vo.InstanceListVO) tagentity.ITagResource {
|
||||
return insvo
|
||||
})...)
|
||||
|
||||
|
||||
@@ -94,7 +94,7 @@ func (d *dbAppImpl) SaveDb(ctx context.Context, dbEntity *entity.Db) error {
|
||||
return d.tagApp.RelateTagsByCodeAndType(ctx, &tagdto.RelateTagsByCodeAndType{
|
||||
Tags: []*tagdto.ResourceTag{{
|
||||
Code: dbEntity.Code,
|
||||
Type: tagentity.TagTypeDbName,
|
||||
Type: tagentity.TagTypeDb,
|
||||
Name: dbEntity.Name,
|
||||
}},
|
||||
ParentTagCode: authCert.Name,
|
||||
@@ -136,12 +136,12 @@ func (d *dbAppImpl) SaveDb(ctx context.Context, dbEntity *entity.Db) error {
|
||||
return d.UpdateById(ctx, dbEntity)
|
||||
}, func(ctx context.Context) error {
|
||||
if old.Name != dbEntity.Name {
|
||||
if err := d.tagApp.UpdateTagName(ctx, tagentity.TagTypeDbName, old.Code, dbEntity.Name); err != nil {
|
||||
if err := d.tagApp.UpdateTagName(ctx, tagentity.TagTypeDb, old.Code, dbEntity.Name); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if authCert.Name != old.AuthCertName {
|
||||
return d.tagApp.ChangeParentTag(ctx, tagentity.TagTypeDbName, old.Code, tagentity.TagTypeDbAuthCert, authCert.Name)
|
||||
return d.tagApp.ChangeParentTag(ctx, tagentity.TagTypeDb, old.Code, tagentity.TagTypeDbAuthCert, authCert.Name)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
@@ -170,7 +170,7 @@ func (d *dbAppImpl) Delete(ctx context.Context, id uint64) error {
|
||||
}, func(ctx context.Context) error {
|
||||
return d.tagApp.DeleteTagByParam(ctx, &tagdto.DelResourceTag{
|
||||
ResourceCode: db.Code,
|
||||
ResourceType: tagentity.TagTypeDbName,
|
||||
ResourceType: tagentity.TagTypeDb,
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -191,7 +191,7 @@ func (d *dbAppImpl) GetDbConn(dbId uint64, dbName string) (*dbi.DbConn, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
di.CodePath = d.tagApp.ListTagPathByTypeAndCode(int8(tagentity.TagTypeDbName), db.Code)
|
||||
di.CodePath = d.tagApp.ListTagPathByTypeAndCode(int8(tagentity.TagTypeDb), db.Code)
|
||||
di.Id = db.Id
|
||||
|
||||
checkDb := di.GetDatabase()
|
||||
|
||||
@@ -84,7 +84,7 @@ func (app *instanceAppImpl) SaveDbInstance(ctx context.Context, instance *dto.Sa
|
||||
instanceEntity := instance.DbInstance
|
||||
// 默认tcp连接
|
||||
instanceEntity.Network = instanceEntity.GetNetwork()
|
||||
resourceType := consts.ResourceTypeDb
|
||||
resourceType := consts.ResourceTypeDbInstance
|
||||
authCerts := instance.AuthCerts
|
||||
tagCodePaths := instance.TagCodePaths
|
||||
|
||||
@@ -145,7 +145,7 @@ func (app *instanceAppImpl) SaveDbInstance(ctx context.Context, instance *dto.Sa
|
||||
})
|
||||
}, func(ctx context.Context) error {
|
||||
if instanceEntity.Name != oldInstance.Name {
|
||||
if err := app.tagApp.UpdateTagName(ctx, tagentity.TagTypeDb, oldInstance.Code, instanceEntity.Name); err != nil {
|
||||
if err := app.tagApp.UpdateTagName(ctx, tagentity.TagTypeDbInstance, oldInstance.Code, instanceEntity.Name); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -172,12 +172,12 @@ func (app *instanceAppImpl) Delete(ctx context.Context, instanceId uint64) error
|
||||
// 删除该实例关联的授权凭证信息
|
||||
return app.resourceAuthCertApp.RelateAuthCert(ctx, &tagdto.RelateAuthCert{
|
||||
ResourceCode: instance.Code,
|
||||
ResourceType: tagentity.TagType(consts.ResourceTypeDb),
|
||||
ResourceType: tagentity.TagType(consts.ResourceTypeDbInstance),
|
||||
})
|
||||
}, func(ctx context.Context) error {
|
||||
return app.tagApp.DeleteTagByParam(ctx, &tagdto.DelResourceTag{
|
||||
ResourceCode: instance.Code,
|
||||
ResourceType: tagentity.TagType(consts.ResourceTypeDb),
|
||||
ResourceType: tagentity.TagType(consts.ResourceTypeDbInstance),
|
||||
})
|
||||
}, func(ctx context.Context) error {
|
||||
// 删除所有库配置
|
||||
@@ -276,7 +276,7 @@ func (m *instanceAppImpl) genDbInstanceResourceTag(me *entity.DbInstance, authCe
|
||||
authCertName2DbTags[db.AuthCertName] = append(authCertName2DbTags[db.AuthCertName], &tagdto.ResourceTag{
|
||||
Code: db.Code,
|
||||
Name: db.Name,
|
||||
Type: tagentity.TagTypeDbName,
|
||||
Type: tagentity.TagTypeDb,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -287,7 +287,7 @@ func (m *instanceAppImpl) genDbInstanceResourceTag(me *entity.DbInstance, authCe
|
||||
|
||||
return &tagdto.ResourceTag{
|
||||
Code: me.Code,
|
||||
Type: tagentity.TagTypeDb,
|
||||
Type: tagentity.TagTypeDbInstance,
|
||||
Name: me.Name,
|
||||
Children: authCertTags,
|
||||
}
|
||||
|
||||
@@ -16,8 +16,8 @@ type Dashbord struct {
|
||||
func (m *Dashbord) Dashbord(rc *req.Ctx) {
|
||||
accountId := rc.GetLoginAccount().Id
|
||||
|
||||
tagCodePaths := m.TagTreeApp.GetAccountTagCodePaths(accountId, tagentity.TagTypeMachineAuthCert, "")
|
||||
machineCodes := tagentity.GetCodeByPath(tagentity.TagTypeMachine, tagCodePaths...)
|
||||
tagCodePaths := m.TagTreeApp.GetAccountTags(accountId, &tagentity.TagTreeQuery{Types: collx.AsArray(tagentity.TagTypeMachineAuthCert)}).GetCodePaths()
|
||||
machineCodes := tagentity.GetCodesByCodePaths(tagentity.TagTypeMachine, tagCodePaths...)
|
||||
|
||||
rc.ResData = collx.M{
|
||||
"machineNum": len(machineCodes),
|
||||
|
||||
@@ -43,14 +43,18 @@ type Machine struct {
|
||||
func (m *Machine) Machines(rc *req.Ctx) {
|
||||
condition, pageParam := req.BindQueryAndPage(rc, new(entity.MachineQuery))
|
||||
|
||||
tagCodePaths := m.TagApp.GetAccountTagCodePaths(rc.GetLoginAccount().Id, tagentity.TagTypeMachineAuthCert, condition.TagPath)
|
||||
tags := m.TagApp.GetAccountTags(rc.GetLoginAccount().Id, &tagentity.TagTreeQuery{
|
||||
Types: collx.AsArray(tagentity.TagTypeMachineAuthCert),
|
||||
CodePathLikes: collx.AsArray(condition.TagPath),
|
||||
})
|
||||
// 不存在可操作的机器-授权凭证标签,即没有可操作数据
|
||||
if len(tagCodePaths) == 0 {
|
||||
if len(tags) == 0 {
|
||||
rc.ResData = model.EmptyPageResult[any]()
|
||||
return
|
||||
}
|
||||
|
||||
machineCodes := tagentity.GetCodeByPath(tagentity.TagTypeMachine, tagCodePaths...)
|
||||
tagCodePaths := tags.GetCodePaths()
|
||||
machineCodes := tagentity.GetCodesByCodePaths(tagentity.TagTypeMachine, tagCodePaths...)
|
||||
condition.Codes = collx.ArrayDeduplicate(machineCodes)
|
||||
|
||||
var machinevos []*vo.MachineVO
|
||||
@@ -67,7 +71,7 @@ func (m *Machine) Machines(rc *req.Ctx) {
|
||||
})...)
|
||||
|
||||
// 填充授权凭证信息
|
||||
m.ResourceAuthCertApp.FillAuthCertByAcNames(tagentity.GetCodeByPath(tagentity.TagTypeMachineAuthCert, tagCodePaths...), collx.ArrayMap(machinevos, func(mvo *vo.MachineVO) tagentity.IAuthCert {
|
||||
m.ResourceAuthCertApp.FillAuthCertByAcNames(tagentity.GetCodesByCodePaths(tagentity.TagTypeMachineAuthCert, tagCodePaths...), collx.ArrayMap(machinevos, func(mvo *vo.MachineVO) tagentity.IAuthCert {
|
||||
return mvo
|
||||
})...)
|
||||
|
||||
|
||||
@@ -151,7 +151,7 @@ func (m *machineCronJobAppImpl) RunCronJob(key string) {
|
||||
|
||||
relateCodePaths := m.tagTreeRelateApp.GetTagPathsByRelate(tagentity.TagRelateTypeMachineCronJob, cronJob.Id)
|
||||
var machineTags []tagentity.TagTree
|
||||
m.tagTreeApp.ListByQuery(&tagentity.TagTreeQuery{CodePathLikes: relateCodePaths, Type: tagentity.TagTypeMachine}, &machineTags)
|
||||
m.tagTreeApp.ListByQuery(&tagentity.TagTreeQuery{CodePathLikes: relateCodePaths, Types: []tagentity.TagType{tagentity.TagTypeMachine}}, &machineTags)
|
||||
machines, _ := m.machineApp.ListByCond(model.NewCond().In("code", collx.ArrayMap(machineTags, func(tag tagentity.TagTree) string {
|
||||
return tag.Code
|
||||
})), "id")
|
||||
|
||||
@@ -31,5 +31,7 @@ var En = map[i18n.MsgId]string{
|
||||
ErrFileUploadFail: "File upload failure",
|
||||
MsgUploadFileSuccess: "File uploaded successfully",
|
||||
|
||||
TerminalCmdDisable: "This command has been disabled...",
|
||||
LogMachineSecurityCmdSave: "Machine - Security - Save command configuration",
|
||||
LogMachineSecurityCmdDelete: "Machine - Security - Delete command configuration",
|
||||
TerminalCmdDisable: "This command has been disabled...",
|
||||
}
|
||||
|
||||
@@ -39,5 +39,9 @@ const (
|
||||
ErrFileUploadFail
|
||||
MsgUploadFileSuccess
|
||||
|
||||
// security
|
||||
LogMachineSecurityCmdSave
|
||||
LogMachineSecurityCmdDelete
|
||||
|
||||
TerminalCmdDisable
|
||||
)
|
||||
|
||||
@@ -31,5 +31,7 @@ var Zh_CN = map[i18n.MsgId]string{
|
||||
ErrFileUploadFail: "文件上传失败",
|
||||
MsgUploadFileSuccess: "文件上传成功",
|
||||
|
||||
TerminalCmdDisable: "该命令已被禁用...",
|
||||
LogMachineSecurityCmdSave: "机器-安全-保存命令配置",
|
||||
LogMachineSecurityCmdDelete: "机器-安全-删除命令配置",
|
||||
TerminalCmdDisable: "该命令已被禁用...",
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package router
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/machine/api"
|
||||
"mayfly-go/internal/machine/imsg"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/ioc"
|
||||
"mayfly-go/pkg/req"
|
||||
@@ -18,9 +19,9 @@ func InitMachineCmdConfRouter(router *gin.RouterGroup) {
|
||||
reqs := [...]*req.Conf{
|
||||
req.NewGet("", mcc.MachineCmdConfs),
|
||||
|
||||
req.NewPost("", mcc.Save).Log(req.NewLogSave("机器命令配置-保存")).RequiredPermissionCode("cmdconf:save"),
|
||||
req.NewPost("", mcc.Save).Log(req.NewLogSaveI(imsg.LogMachineSecurityCmdSave)).RequiredPermissionCode("cmdconf:save"),
|
||||
|
||||
req.NewDelete(":id", mcc.Delete).Log(req.NewLogSave("机器命令配置-删除")).RequiredPermissionCode("cmdconf:del"),
|
||||
req.NewDelete(":id", mcc.Delete).Log(req.NewLogSaveI(imsg.LogMachineSecurityCmdDelete)).RequiredPermissionCode("cmdconf:del"),
|
||||
}
|
||||
|
||||
req.BatchSetGroup(mccs, reqs[:])
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/common/consts"
|
||||
tagapp "mayfly-go/internal/tag/application"
|
||||
tagentity "mayfly-go/internal/tag/domain/entity"
|
||||
"mayfly-go/pkg/req"
|
||||
"mayfly-go/pkg/utils/collx"
|
||||
)
|
||||
@@ -13,7 +13,9 @@ type Dashbord struct {
|
||||
|
||||
func (m *Dashbord) Dashbord(rc *req.Ctx) {
|
||||
accountId := rc.GetLoginAccount().Id
|
||||
mongoNum := len(m.TagTreeApp.GetAccountTagCodes(accountId, consts.ResourceTypeMongo, ""))
|
||||
mongoNum := len(m.TagTreeApp.GetAccountTags(accountId, &tagentity.TagTreeQuery{Types: []tagentity.TagType{
|
||||
tagentity.TagTypeMongo,
|
||||
}}))
|
||||
|
||||
rc.ResData = collx.M{
|
||||
"mongoNum": mongoNum,
|
||||
|
||||
@@ -33,12 +33,15 @@ func (m *Mongo) Mongos(rc *req.Ctx) {
|
||||
queryCond, page := req.BindQueryAndPage[*entity.MongoQuery](rc, new(entity.MongoQuery))
|
||||
|
||||
// 不存在可访问标签id,即没有可操作数据
|
||||
codes := m.TagApp.GetAccountTagCodes(rc.GetLoginAccount().Id, consts.ResourceTypeMongo, queryCond.TagPath)
|
||||
if len(codes) == 0 {
|
||||
tags := m.TagApp.GetAccountTags(rc.GetLoginAccount().Id, &tagentity.TagTreeQuery{
|
||||
Types: []tagentity.TagType{tagentity.TagTypeMongo},
|
||||
CodePathLikes: []string{queryCond.TagPath},
|
||||
})
|
||||
if len(tags) == 0 {
|
||||
rc.ResData = model.EmptyPageResult[any]()
|
||||
return
|
||||
}
|
||||
queryCond.Codes = codes
|
||||
queryCond.Codes = tags.GetCodes()
|
||||
|
||||
var mongovos []*vo.Mongo
|
||||
res, err := m.MongoApp.GetPageList(queryCond, page, &mongovos)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/common/consts"
|
||||
tagapp "mayfly-go/internal/tag/application"
|
||||
tagentity "mayfly-go/internal/tag/domain/entity"
|
||||
"mayfly-go/pkg/req"
|
||||
"mayfly-go/pkg/utils/collx"
|
||||
)
|
||||
@@ -13,7 +13,9 @@ type Dashbord struct {
|
||||
|
||||
func (m *Dashbord) Dashbord(rc *req.Ctx) {
|
||||
accountId := rc.GetLoginAccount().Id
|
||||
redisNum := len(m.TagTreeApp.GetAccountTagCodes(accountId, consts.ResourceTypeRedis, ""))
|
||||
redisNum := len(m.TagTreeApp.GetAccountTags(accountId, &tagentity.TagTreeQuery{
|
||||
Types: collx.AsArray(tagentity.TagTypeRedis),
|
||||
}))
|
||||
|
||||
rc.ResData = collx.M{
|
||||
"redisNum": redisNum,
|
||||
|
||||
@@ -33,12 +33,15 @@ func (r *Redis) RedisList(rc *req.Ctx) {
|
||||
queryCond, page := req.BindQueryAndPage[*entity.RedisQuery](rc, new(entity.RedisQuery))
|
||||
|
||||
// 不存在可访问标签id,即没有可操作数据
|
||||
codes := r.TagApp.GetAccountTagCodes(rc.GetLoginAccount().Id, consts.ResourceTypeRedis, queryCond.TagPath)
|
||||
if len(codes) == 0 {
|
||||
tags := r.TagApp.GetAccountTags(rc.GetLoginAccount().Id, &tagentity.TagTreeQuery{
|
||||
Types: collx.AsArray(tagentity.TagTypeRedis),
|
||||
CodePathLikes: collx.AsArray(queryCond.TagPath),
|
||||
})
|
||||
if len(tags) == 0 {
|
||||
rc.ResData = model.EmptyPageResult[any]()
|
||||
return
|
||||
}
|
||||
queryCond.Codes = codes
|
||||
queryCond.Codes = tags.GetCodes()
|
||||
|
||||
var redisvos []*vo.Redis
|
||||
res, err := r.RedisApp.GetPageList(queryCond, page, &redisvos)
|
||||
|
||||
@@ -2,7 +2,6 @@ package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"mayfly-go/internal/common/consts"
|
||||
"mayfly-go/internal/tag/api/form"
|
||||
"mayfly-go/internal/tag/api/vo"
|
||||
"mayfly-go/internal/tag/application"
|
||||
@@ -54,7 +53,7 @@ func (p *TagTree) complteTags(resourceTags []*dto.SimpleTagTree) []*dto.SimpleTa
|
||||
// 如tagPath = tag1/tag2/tag3/ 需要转为该路径所关联的所有标签路径即 tag1/ tag1/tag2/ tag1/tag2/tag3/三个相关联标签,才可以构造成一棵树
|
||||
allTagPaths := make([]string, 0)
|
||||
for _, tagPath := range collx.MapKeys(codePath2Tag) {
|
||||
allTagPaths = append(allTagPaths, entity.GetAllCodePath(tagPath)...)
|
||||
allTagPaths = append(allTagPaths, entity.CodePath(tagPath).GetAllPath()...)
|
||||
}
|
||||
allTagPaths = collx.ArrayDeduplicate(allTagPaths)
|
||||
|
||||
@@ -82,7 +81,7 @@ func (p *TagTree) ListByQuery(rc *req.Ctx) {
|
||||
cond.CodePaths = strings.Split(tagPaths, ",")
|
||||
}
|
||||
cond.Id = uint64(rc.QueryInt("id"))
|
||||
cond.Type = entity.TagType(rc.QueryInt("type"))
|
||||
cond.Types = collx.AsArray(entity.TagType(rc.QueryInt("type")))
|
||||
codes := rc.Query("codes")
|
||||
if codes != "" {
|
||||
cond.Codes = strings.Split(codes, ",")
|
||||
@@ -117,10 +116,10 @@ func (p *TagTree) MovingTag(rc *req.Ctx) {
|
||||
func (p *TagTree) TagResources(rc *req.Ctx) {
|
||||
resourceType := int8(rc.PathParamInt("rtype"))
|
||||
accountId := rc.GetLoginAccount().Id
|
||||
tagResources := p.TagTreeApp.GetAccountTags(accountId, &entity.TagTreeQuery{Type: entity.TagType(resourceType)})
|
||||
tagResources := p.TagTreeApp.GetAccountTags(accountId, &entity.TagTreeQuery{Types: collx.AsArray(entity.TagType(resourceType))})
|
||||
|
||||
tagPath2Resource := collx.ArrayToMap[*dto.SimpleTagTree, string](tagResources, func(tagResource *dto.SimpleTagTree) string {
|
||||
return entity.GetTagPath(tagResource.CodePath)
|
||||
return string(entity.CodePath(tagResource.CodePath).GetTag())
|
||||
})
|
||||
|
||||
tagPaths := collx.MapKeys(tagPath2Resource)
|
||||
@@ -133,14 +132,27 @@ func (p *TagTree) CountTagResource(rc *req.Ctx) {
|
||||
tagPath := rc.Query("tagPath")
|
||||
accountId := rc.GetLoginAccount().Id
|
||||
|
||||
machineCodes := entity.GetCodeByPath(entity.TagTypeMachine, p.TagTreeApp.GetAccountTagCodePaths(accountId, entity.TagTypeMachineAuthCert, tagPath)...)
|
||||
dbCodes := entity.GetCodeByPath(entity.TagTypeDb, p.TagTreeApp.GetAccountTagCodePaths(accountId, entity.TagTypeDbName, tagPath)...)
|
||||
machineCodes := entity.GetCodesByCodePaths(entity.TagTypeMachine, p.TagTreeApp.GetAccountTags(accountId, &entity.TagTreeQuery{
|
||||
Types: collx.AsArray(entity.TagTypeMachineAuthCert),
|
||||
CodePathLikes: collx.AsArray(tagPath),
|
||||
}).GetCodePaths()...)
|
||||
|
||||
dbCodes := entity.GetCodesByCodePaths(entity.TagTypeDbInstance, p.TagTreeApp.GetAccountTags(accountId, &entity.TagTreeQuery{
|
||||
Types: collx.AsArray(entity.TagTypeDb),
|
||||
CodePathLikes: collx.AsArray(tagPath),
|
||||
}).GetCodePaths()...)
|
||||
|
||||
rc.ResData = collx.M{
|
||||
"machine": len(machineCodes),
|
||||
"db": len(dbCodes),
|
||||
"redis": len(p.TagTreeApp.GetAccountTagCodes(accountId, consts.ResourceTypeRedis, tagPath)),
|
||||
"mongo": len(p.TagTreeApp.GetAccountTagCodes(accountId, consts.ResourceTypeMongo, tagPath)),
|
||||
"redis": len(p.TagTreeApp.GetAccountTags(accountId, &entity.TagTreeQuery{
|
||||
Types: collx.AsArray(entity.TagTypeRedis),
|
||||
CodePathLikes: collx.AsArray(tagPath),
|
||||
}).GetCodes()),
|
||||
"mongo": len(p.TagTreeApp.GetAccountTags(accountId, &entity.TagTreeQuery{
|
||||
Types: collx.AsArray(entity.TagTypeMongo),
|
||||
CodePathLikes: collx.AsArray(tagPath),
|
||||
}).GetCodes()),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ func (m *TagTreeVOS) ToTrees(pid uint64) []*TagTreeItem {
|
||||
if node.Root {
|
||||
continue
|
||||
}
|
||||
parentCodePath := node.GetParentPath(0)
|
||||
parentCodePath := node.GetParentPath()
|
||||
parentNode := tagMap[parentCodePath]
|
||||
if parentNode != nil {
|
||||
parentNode.Children = append(parentNode.Children, node)
|
||||
|
||||
@@ -2,6 +2,7 @@ package dto
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/tag/domain/entity"
|
||||
"mayfly-go/pkg/utils/collx"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@@ -48,11 +49,33 @@ type SimpleTagTree struct {
|
||||
|
||||
func (pt *SimpleTagTree) IsRoot() bool {
|
||||
// 去除路径两端可能存在的斜杠
|
||||
path := strings.Trim(pt.CodePath, "/")
|
||||
path := strings.Trim(string(pt.CodePath), "/")
|
||||
return len(strings.Split(path, "/")) == 1
|
||||
}
|
||||
|
||||
// GetParentPath 获取父标签路径, 如CodePath = test/test1/test2/ -> index = 0 => test/test1/ index = 1 => test/
|
||||
func (pt *SimpleTagTree) GetParentPath(index int) string {
|
||||
return entity.GetParentPath(pt.CodePath, index)
|
||||
// GetParentPath 获取父标签路径, 如CodePath = test/test1/test2/ -> test/test1/
|
||||
func (pt *SimpleTagTree) GetParentPath() string {
|
||||
return string(entity.CodePath(pt.CodePath).GetParent(0))
|
||||
}
|
||||
|
||||
type SimpleTagTrees []*SimpleTagTree
|
||||
|
||||
// GetCodes 获取code数组
|
||||
func (ts SimpleTagTrees) GetCodes() []string {
|
||||
// resouce code去重
|
||||
code2Resource := collx.ArrayToMap[*SimpleTagTree, string](ts, func(val *SimpleTagTree) string {
|
||||
return val.Code
|
||||
})
|
||||
|
||||
return collx.MapKeys(code2Resource)
|
||||
}
|
||||
|
||||
// GetCodePaths 获取codePath数组
|
||||
func (ts SimpleTagTrees) GetCodePaths() []string {
|
||||
// codepath去重
|
||||
codepath2Resource := collx.ArrayToMap[*SimpleTagTree, string](ts, func(val *SimpleTagTree) string {
|
||||
return val.CodePath
|
||||
})
|
||||
|
||||
return collx.MapKeys(codepath2Resource)
|
||||
}
|
||||
|
||||
@@ -451,7 +451,7 @@ func GetResourceAuthCertTagType(resourceType entity.TagType) entity.TagType {
|
||||
return entity.TagTypeMachineAuthCert
|
||||
}
|
||||
|
||||
if resourceType == entity.TagTypeDb {
|
||||
if resourceType == entity.TagTypeDbInstance {
|
||||
return entity.TagTypeDbAuthCert
|
||||
}
|
||||
|
||||
|
||||
@@ -48,13 +48,7 @@ type TagTree interface {
|
||||
// GetAccountTags 获取指定账号有权限操作的标签列表
|
||||
// @param accountId 账号id
|
||||
// @param query 查询条件
|
||||
GetAccountTags(accountId uint64, query *entity.TagTreeQuery) []*dto.SimpleTagTree
|
||||
|
||||
// GetAccountTagCodes 获取指定账号有权限操作的标签codes
|
||||
GetAccountTagCodes(accountId uint64, resourceType int8, tagPath string) []string
|
||||
|
||||
// GetAccountTagCodePaths 获取指定账号有权限操作的codePaths
|
||||
GetAccountTagCodePaths(accountId uint64, tagType entity.TagType, tagPath string) []string
|
||||
GetAccountTags(accountId uint64, query *entity.TagTreeQuery) dto.SimpleTagTrees
|
||||
|
||||
// 根据标签类型和标签code获取对应的标签路径列表
|
||||
ListTagPathByTypeAndCode(resourceType int8, resourceCode string) []string
|
||||
@@ -111,7 +105,7 @@ func (p *tagTreeAppImpl) SaveTag(ctx context.Context, pid uint64, tag *entity.Ta
|
||||
|
||||
// 判断该路径是否存在
|
||||
var hasLikeTags []entity.TagTree
|
||||
p.GetRepo().SelectByCondition(&entity.TagTreeQuery{CodePathLike: tag.CodePath}, &hasLikeTags)
|
||||
p.GetRepo().SelectByCondition(&entity.TagTreeQuery{CodePathLikes: []string{tag.CodePath}}, &hasLikeTags)
|
||||
if len(hasLikeTags) > 0 {
|
||||
return errorx.NewBizI(ctx, imsg.ErrTagCodePathLikeExist)
|
||||
}
|
||||
@@ -257,16 +251,16 @@ func (p *tagTreeAppImpl) ChangeParentTag(ctx context.Context, tagType entity.Tag
|
||||
|
||||
// 更新父标签的codepath
|
||||
for _, tag := range resourceChildrenTags {
|
||||
pathSection := entity.GetTagPathSections(tag.CodePath)
|
||||
for i, ps := range pathSection {
|
||||
pathSections := entity.CodePath(tag.CodePath).GetPathSections()
|
||||
for i, ps := range pathSections {
|
||||
if ps.Type == tagType && ps.Code == tagCode {
|
||||
// 将父标签编号修改为对应的新编号与类型
|
||||
pathSection[i-1].Code = newParentCode
|
||||
pathSection[i-1].Type = parentTagType
|
||||
pathSections[i-1].Code = newParentCode
|
||||
pathSections[i-1].Type = parentTagType
|
||||
}
|
||||
}
|
||||
|
||||
tag.CodePath = pathSection.ToCodePath()
|
||||
tag.CodePath = pathSections.ToCodePath()
|
||||
if err := p.UpdateById(ctx, tag); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -288,10 +282,10 @@ func (p *tagTreeAppImpl) MovingTag(ctx context.Context, fromTagPath string, toTa
|
||||
|
||||
// 获取要移动标签的所有子标签
|
||||
var childrenTags []*entity.TagTree
|
||||
p.ListByQuery(&entity.TagTreeQuery{CodePathLike: fromTagPath}, &childrenTags)
|
||||
p.ListByQuery(&entity.TagTreeQuery{CodePathLikes: []string{fromTagPath}}, &childrenTags)
|
||||
|
||||
// 获取父路径, 若fromTagPath=tag1/tag2/1|xxx则返回 tag1/tag2/
|
||||
fromParentPath := entity.GetParentPath(fromTagPath, 0)
|
||||
fromParentPath := string(entity.CodePath(fromTagPath).GetParent(0))
|
||||
for _, childTag := range childrenTags {
|
||||
// 替换path,若childPath = tag1/tag2/1|xxx/11|yyy, toTagPath=tag3/tag4则替换为tag3/tag4/1|xxx/11|yyy/
|
||||
childTag.CodePath = strings.Replace(childTag.CodePath, fromParentPath, toTagPath, 1)
|
||||
@@ -339,10 +333,10 @@ func (p *tagTreeAppImpl) ListByQuery(condition *entity.TagTreeQuery, toEntity an
|
||||
p.GetRepo().SelectByCondition(condition, toEntity)
|
||||
}
|
||||
|
||||
func (p *tagTreeAppImpl) GetAccountTags(accountId uint64, query *entity.TagTreeQuery) []*dto.SimpleTagTree {
|
||||
func (p *tagTreeAppImpl) GetAccountTags(accountId uint64, query *entity.TagTreeQuery) dto.SimpleTagTrees {
|
||||
types := query.Types
|
||||
tagResourceQuery := &entity.TagTreeQuery{
|
||||
Type: query.Type,
|
||||
Types: query.Types,
|
||||
Types: types,
|
||||
}
|
||||
|
||||
var tagResources []*dto.SimpleTagTree
|
||||
@@ -360,7 +354,7 @@ func (p *tagTreeAppImpl) GetAccountTags(accountId uint64, query *entity.TagTreeQ
|
||||
tagPaths := collx.ArrayRemoveBlank(query.CodePathLikes)
|
||||
// 如果需要查询指定标签下的资源标签,则需要与用户拥有的权限进行过滤,避免越权
|
||||
if len(tagPaths) > 0 {
|
||||
// admin 则直接赋值需要获取的标签
|
||||
// 为空则说明为admin 则直接赋值需要获取的标签
|
||||
if len(accountTagPaths) == 0 {
|
||||
accountTagPaths = tagPaths
|
||||
} else {
|
||||
@@ -374,26 +368,6 @@ func (p *tagTreeAppImpl) GetAccountTags(accountId uint64, query *entity.TagTreeQ
|
||||
return tagResources
|
||||
}
|
||||
|
||||
func (p *tagTreeAppImpl) GetAccountTagCodes(accountId uint64, resourceType int8, tagPath string) []string {
|
||||
tagResources := p.GetAccountTags(accountId, &entity.TagTreeQuery{Type: entity.TagType(resourceType), CodePathLikes: []string{tagPath}})
|
||||
// resouce code去重
|
||||
code2Resource := collx.ArrayToMap[*dto.SimpleTagTree, string](tagResources, func(val *dto.SimpleTagTree) string {
|
||||
return val.Code
|
||||
})
|
||||
|
||||
return collx.MapKeys(code2Resource)
|
||||
}
|
||||
|
||||
func (p *tagTreeAppImpl) GetAccountTagCodePaths(accountId uint64, tagType entity.TagType, tagPath string) []string {
|
||||
tagResources := p.GetAccountTags(accountId, &entity.TagTreeQuery{Type: tagType, CodePathLikes: []string{tagPath}})
|
||||
// resouce code去重
|
||||
code2Resource := collx.ArrayToMap[*dto.SimpleTagTree, string](tagResources, func(val *dto.SimpleTagTree) string {
|
||||
return val.CodePath
|
||||
})
|
||||
|
||||
return collx.MapKeys(code2Resource)
|
||||
}
|
||||
|
||||
func (p *tagTreeAppImpl) ListTagPathByTypeAndCode(resourceType int8, resourceCode string) []string {
|
||||
trs, _ := p.ListByCond(&entity.TagTree{Type: entity.TagType(resourceType), Code: resourceCode})
|
||||
return collx.ArrayMap(trs, func(tr *entity.TagTree) string {
|
||||
@@ -439,13 +413,13 @@ func (p *tagTreeAppImpl) FillTagInfo(resourceTagType entity.TagType, resources .
|
||||
|
||||
// 获取所有资源code关联的标签列表信息
|
||||
var tagResources []*entity.TagTree
|
||||
p.ListByQuery(&entity.TagTreeQuery{Codes: collx.MapKeys(resourceCode2Resouce), Type: resourceTagType}, &tagResources)
|
||||
p.ListByQuery(&entity.TagTreeQuery{Codes: collx.MapKeys(resourceCode2Resouce), Types: []entity.TagType{resourceTagType}}, &tagResources)
|
||||
|
||||
for _, tr := range tagResources {
|
||||
// 赋值标签信息
|
||||
resource := resourceCode2Resouce[tr.Code]
|
||||
if resource != nil {
|
||||
resource.SetTagInfo(entity.ResourceTag{TagId: tr.Id, CodePath: tr.GetTagPath()})
|
||||
resource.SetTagInfo(entity.ResourceTag{TagId: tr.Id, CodePath: string(entity.CodePath(tr.CodePath).GetTag())})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,7 +102,7 @@ func (tr *tagTreeRelateAppImpl) GetRelateIds(ctx context.Context, relateType ent
|
||||
poisibleTagPaths := make([]string, 0)
|
||||
for _, tagPath := range canAccessTagPaths {
|
||||
// 追加可能关联的标签路径,如tagPath = tag1/tag2/1|xxx/,需要获取所有关联的自身及父标签(tag1/ tag1/tag2/ tag1/tag2/1|xxx)
|
||||
poisibleTagPaths = append(poisibleTagPaths, entity.GetAllCodePath(tagPath)...)
|
||||
poisibleTagPaths = append(poisibleTagPaths, entity.CodePath(tagPath).GetAllPath()...)
|
||||
}
|
||||
return tr.tagTreeRelateRepo.SelectRelateIdsByTagPaths(relateType, poisibleTagPaths...)
|
||||
}
|
||||
|
||||
@@ -5,12 +5,10 @@ import "mayfly-go/pkg/model"
|
||||
type TagTreeQuery struct {
|
||||
model.Model
|
||||
|
||||
Type TagType `json:"type"`
|
||||
Types []TagType
|
||||
Codes []string
|
||||
CodePaths []string // 标识路径
|
||||
Name string `json:"name"` // 名称
|
||||
CodePathLike string // 标识符路径模糊查询
|
||||
CodePathLikes []string
|
||||
}
|
||||
|
||||
|
||||
@@ -29,25 +29,20 @@ const (
|
||||
// 标签路径资源段分隔符
|
||||
CodePathResourceSeparator = "|"
|
||||
|
||||
TagTypeTag TagType = -1
|
||||
TagTypeMachine TagType = TagType(consts.ResourceTypeMachine)
|
||||
TagTypeDb TagType = TagType(consts.ResourceTypeDb) // 数据库实例
|
||||
TagTypeRedis TagType = TagType(consts.ResourceTypeRedis)
|
||||
TagTypeMongo TagType = TagType(consts.ResourceTypeMongo)
|
||||
TagTypeTag TagType = -1
|
||||
TagTypeMachine TagType = TagType(consts.ResourceTypeMachine)
|
||||
TagTypeDbInstance TagType = TagType(consts.ResourceTypeDbInstance) // 数据库实例
|
||||
TagTypeRedis TagType = TagType(consts.ResourceTypeRedis)
|
||||
TagTypeMongo TagType = TagType(consts.ResourceTypeMongo)
|
||||
|
||||
// ----- (单独声明各个资源的授权凭证类型而不统一使用一个授权凭证类型是为了获取登录账号的授权凭证标签(ResourceAuthCertApp.GetAccountAuthCert)时,避免查出所有资源的授权凭证)
|
||||
|
||||
TagTypeMachineAuthCert TagType = 11 // 机器-授权凭证
|
||||
|
||||
TagTypeDbAuthCert TagType = 21 // 数据库-授权凭证
|
||||
TagTypeDbName TagType = 22 // 数据库名
|
||||
TagTypeDb TagType = 22 // 数据库名
|
||||
)
|
||||
|
||||
// GetTagPath 获取标签段路径,不获取对应资源相关路径
|
||||
func (pt *TagTree) GetTagPath() string {
|
||||
return GetTagPath(pt.CodePath)
|
||||
}
|
||||
|
||||
// 标签接口资源,如果要实现资源结构体填充标签信息,则资源结构体需要实现该接口
|
||||
type ITagResource interface {
|
||||
// 获取资源code
|
||||
@@ -80,10 +75,13 @@ func (r *ResourceTags) SetTagInfo(rt ResourceTag) {
|
||||
r.Tags = append(r.Tags, rt)
|
||||
}
|
||||
|
||||
// GetTagPath 获取标签段路径,不获取对应资源相关路径
|
||||
func GetTagPath(codePath string) string {
|
||||
// CodePath 标签编号路径 如: tag1/tag2/resourceType1|xxxcode/resourceType2|yyycode/
|
||||
type CodePath string
|
||||
|
||||
// GetTag 获取标签段路径,不获取对应资源相关路径
|
||||
func (codePath CodePath) GetTag() CodePath {
|
||||
// 以 资源分隔符"|" 符号对字符串进行分割
|
||||
parts := strings.Split(codePath, CodePathResourceSeparator)
|
||||
parts := strings.Split(string(codePath), CodePathResourceSeparator)
|
||||
if len(parts) < 2 {
|
||||
return codePath
|
||||
}
|
||||
@@ -96,31 +94,15 @@ func GetTagPath(codePath string) string {
|
||||
|
||||
// 如果找到最后一个 "/" 符号,则截取子串
|
||||
if lastSlashIndex != -1 {
|
||||
return substringBeforeNumber[:lastSlashIndex+1]
|
||||
return CodePath(substringBeforeNumber[:lastSlashIndex+1])
|
||||
}
|
||||
|
||||
return codePath
|
||||
}
|
||||
|
||||
// GetCodeByPath 从codePaths中提取指定标签类型的所有tagCode并去重
|
||||
// 如:codePaths = tag1/tag2/1|xxxcode/11|yyycode/, tagType = 1 -> xxxcode, tagType = 11 -> yyycode
|
||||
func GetCodeByPath(tagType TagType, codePaths ...string) []string {
|
||||
var codes []string
|
||||
for _, codePath := range codePaths {
|
||||
// tag1/tag2/1|xxxcode/11|yyycode,根据 /tagType|resourceCode进行切割
|
||||
splStrs := strings.Split(codePath, fmt.Sprintf("%s%d%s", CodePathSeparator, tagType, CodePathResourceSeparator))
|
||||
if len(splStrs) < 2 {
|
||||
continue
|
||||
}
|
||||
|
||||
codes = append(codes, strings.Split(splStrs[1], CodePathSeparator)[0])
|
||||
}
|
||||
|
||||
return collx.ArrayDeduplicate[string](codes)
|
||||
}
|
||||
|
||||
// GetParentPath 获取父标签路径, 如CodePath = test/test1/test2/ -> index = 0 => test/test1/ index = 1 => test/
|
||||
func GetParentPath(codePath string, index int) string {
|
||||
// GetParent 获取父标签编号路径, 如CodePath = test/test1/test2/ -> parentLevel = 0 => test/test1/ parentLevel = 1 => test/
|
||||
func (cp CodePath) GetParent(parentLevel int) CodePath {
|
||||
codePath := string(cp)
|
||||
// 去除末尾的斜杠
|
||||
codePath = strings.TrimSuffix(codePath, CodePathSeparator)
|
||||
|
||||
@@ -128,61 +110,29 @@ func GetParentPath(codePath string, index int) string {
|
||||
paths := strings.Split(codePath, CodePathSeparator)
|
||||
|
||||
// 确保索引在有效范围内
|
||||
if index < 0 {
|
||||
index = 0
|
||||
} else if index > len(paths)-2 {
|
||||
index = len(paths) - 2
|
||||
if parentLevel < 0 {
|
||||
parentLevel = 0
|
||||
} else if parentLevel > len(paths)-2 {
|
||||
parentLevel = len(paths) - 2
|
||||
}
|
||||
|
||||
// 按索引拼接父标签路径
|
||||
parentPath := strings.Join(paths[:len(paths)-index-1], CodePathSeparator)
|
||||
parentPath := strings.Join(paths[:len(paths)-parentLevel-1], CodePathSeparator)
|
||||
|
||||
return parentPath + CodePathSeparator
|
||||
return CodePath(parentPath + CodePathSeparator)
|
||||
}
|
||||
|
||||
// GetAllCodePath 根据标签路径获取所有相关的标签codePath,如 test1/test2/ -> test1/ test1/test2/
|
||||
func GetAllCodePath(codePath string) []string {
|
||||
// 去除末尾的斜杠
|
||||
// GetPathSections 根据标签编号路径获取路径段落
|
||||
func (cp CodePath) GetPathSections() PathSections {
|
||||
codePath := string(cp)
|
||||
codePath = strings.TrimSuffix(codePath, CodePathSeparator)
|
||||
|
||||
// 使用 Split 方法将路径按斜杠分割成切片
|
||||
paths := strings.Split(codePath, CodePathSeparator)
|
||||
|
||||
var result []string
|
||||
var partialPath string
|
||||
for _, path := range paths {
|
||||
partialPath += path + CodePathSeparator
|
||||
result = append(result, partialPath)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// TagPathSection 标签路径段
|
||||
type TagPathSection struct {
|
||||
Type TagType `json:"type"` // 类型: -1.普通标签; 其他值则为对应的资源类型
|
||||
Code string `json:"code"` // 标识编码, 若类型不为-1,则为对应资源编码
|
||||
}
|
||||
|
||||
type TagPathSections []*TagPathSection // 标签段数组
|
||||
|
||||
// 转为codePath
|
||||
func (tps TagPathSections) ToCodePath() string {
|
||||
return strings.Join(collx.ArrayMap(tps, func(tp *TagPathSection) string {
|
||||
if tp.Type == TagTypeTag {
|
||||
return tp.Code
|
||||
}
|
||||
return fmt.Sprintf("%d%s%s", tp.Type, CodePathResourceSeparator, tp.Code)
|
||||
}), CodePathSeparator) + CodePathSeparator
|
||||
}
|
||||
|
||||
// GetTagPathSections 根据标签路径获取路径段落
|
||||
func GetTagPathSections(codePath string) TagPathSections {
|
||||
codePath = strings.TrimSuffix(codePath, CodePathSeparator)
|
||||
var sections TagPathSections
|
||||
var sections PathSections
|
||||
|
||||
codes := strings.Split(codePath, CodePathSeparator)
|
||||
path := ""
|
||||
for _, code := range codes {
|
||||
path += code + CodePathSeparator
|
||||
|
||||
typeAndCode := strings.Split(code, CodePathResourceSeparator)
|
||||
var tagType TagType
|
||||
var tagCode string
|
||||
@@ -195,11 +145,66 @@ func GetTagPathSections(codePath string) TagPathSections {
|
||||
tagCode = typeAndCode[1]
|
||||
}
|
||||
|
||||
sections = append(sections, &TagPathSection{
|
||||
sections = append(sections, &PathSection{
|
||||
Type: tagType,
|
||||
Code: tagCode,
|
||||
Path: path,
|
||||
})
|
||||
}
|
||||
|
||||
return sections
|
||||
}
|
||||
|
||||
// GetCode 从codePath中提取指定标签类型的code
|
||||
// 如:codePath = tag1/tag2/1|xxxcode/11|yyycode/, tagType = 1 -> xxxcode, tagType = 11 -> yyycode
|
||||
func (cp CodePath) GetCode(tagType TagType) string {
|
||||
type2Section := collx.ArrayToMap(cp.GetPathSections(), func(section *PathSection) TagType {
|
||||
return section.Type
|
||||
})
|
||||
section := type2Section[tagType]
|
||||
if section == nil {
|
||||
return ""
|
||||
}
|
||||
return section.Code
|
||||
}
|
||||
|
||||
// GetAllPath 根据codePath获取所有相关的标签codePath,如 test1/test2/ -> test1/ test1/test2/
|
||||
func (cp CodePath) GetAllPath() []string {
|
||||
return collx.ArrayMap(cp.GetPathSections(), func(section *PathSection) string {
|
||||
return section.Path
|
||||
})
|
||||
}
|
||||
|
||||
// PathSection 标签路径段
|
||||
type PathSection struct {
|
||||
Type TagType `json:"type"` // 类型: -1.普通标签; 其他值则为对应的资源类型
|
||||
Code string `json:"code"` // 编码, 若类型不为-1,则为对应资源编码
|
||||
Path string `json:"path"` // 当前路径段对应的完整编号路径
|
||||
}
|
||||
|
||||
type PathSections []*PathSection // 标签段数组
|
||||
|
||||
// ToCodePath 转为codePath
|
||||
func (tps PathSections) ToCodePath() string {
|
||||
return strings.Join(collx.ArrayMap(tps, func(tp *PathSection) string {
|
||||
if tp.Type == TagTypeTag {
|
||||
return tp.Code
|
||||
}
|
||||
return fmt.Sprintf("%d%s%s", tp.Type, CodePathResourceSeparator, tp.Code)
|
||||
}), CodePathSeparator) + CodePathSeparator
|
||||
}
|
||||
|
||||
// GetCodesByCodePaths 从codePaths中提取指定标签类型的所有tagCode并去重
|
||||
// 如:codePaths = tag1/tag2/1|xxxcode/11|yyycode/, tagType = 1 -> xxxcode, tagType = 11 -> yyycode
|
||||
func GetCodesByCodePaths(tagType TagType, codePaths ...string) []string {
|
||||
var codes []string
|
||||
for _, codePath := range codePaths {
|
||||
code := CodePath(codePath).GetCode(tagType)
|
||||
if code == "" {
|
||||
continue
|
||||
}
|
||||
codes = append(codes, code)
|
||||
}
|
||||
|
||||
return collx.ArrayDeduplicate[string](codes)
|
||||
}
|
||||
|
||||
@@ -2,22 +2,37 @@ package entity
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGetPathSection(t *testing.T) {
|
||||
fromPath := "tag1/tag2/1|xx/"
|
||||
childPath := "tag1/tag2/1|xx/11|yy/"
|
||||
toPath := "tag3/"
|
||||
parentSection := GetTagPathSections(GetParentPath(fromPath, 0))
|
||||
|
||||
childSection := GetTagPathSections(childPath)
|
||||
res := toPath + childSection[len(GetTagPathSections(fromPath)):].ToCodePath()
|
||||
res1 := toPath + childSection[len(parentSection):].ToCodePath()
|
||||
|
||||
pPath := GetParentPath(fromPath, 0)
|
||||
r := strings.Replace(childPath, pPath, toPath, 1)
|
||||
r1 := strings.Replace(fromPath, pPath, toPath, 1)
|
||||
fmt.Println(res, res1, r, r1)
|
||||
func TestGetTag(t *testing.T) {
|
||||
cp := CodePath("tag1/tag2/1|xx/11|yy/111|zz/")
|
||||
v := cp.GetTag()
|
||||
pv := cp.GetParent(0)
|
||||
av := cp.GetAllPath()
|
||||
ps := cp.GetPathSections()
|
||||
code := cp.GetCode(TagType(11))
|
||||
fmt.Println(v, pv, av, ps, code)
|
||||
}
|
||||
|
||||
// func TestGetPathSection(t *testing.T) {
|
||||
// fromPath := "tag1/tag2/1|xx/"
|
||||
// childPath := "tag1/tag2/1|xx/11|yy/"
|
||||
// toPath := "tag3/"
|
||||
// parentSection := GetTagPathSections(GetParentPath(fromPath, 0))
|
||||
|
||||
// childSection := GetTagPathSections(childPath)
|
||||
// res := toPath + childSection[len(GetTagPathSections(fromPath)):].ToCodePath()
|
||||
// res1 := toPath + childSection[len(parentSection):].ToCodePath()
|
||||
|
||||
// pPath := GetParentPath(fromPath, 0)
|
||||
// r := strings.Replace(childPath, pPath, toPath, 1)
|
||||
// r1 := strings.Replace(fromPath, pPath, toPath, 1)
|
||||
// fmt.Println(res, res1, r, r1)
|
||||
// }
|
||||
|
||||
// func TestGetPathSection2(t *testing.T) {
|
||||
// tagpath := "tag1/tag2/1|xx/11|yy/"
|
||||
// sections := GetTagPathSections(GetParentPath(tagpath, 0))
|
||||
// fmt.Println(sections)
|
||||
// }
|
||||
|
||||
@@ -20,8 +20,6 @@ func (p *tagTreeRepoImpl) SelectByCondition(condition *entity.TagTreeQuery, toEn
|
||||
Eq("id", condition.Id).
|
||||
In("code", condition.Codes).
|
||||
In("code_path", condition.CodePaths).
|
||||
RLike("code_path", condition.CodePathLike).
|
||||
Eq("type", condition.Type).
|
||||
In("type", condition.Types).
|
||||
OrderByAsc("type").OrderByAsc("code_path")
|
||||
|
||||
|
||||
@@ -5,6 +5,11 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// AsArray 将可变参数列表为数组
|
||||
func AsArray[T comparable](el ...T) []T {
|
||||
return el
|
||||
}
|
||||
|
||||
// 数组比较
|
||||
// 依次返回,新增值,删除值,以及不变值
|
||||
func ArrayCompare[T comparable](newArr []T, oldArr []T) ([]T, []T, []T) {
|
||||
|
||||
@@ -163,9 +163,9 @@ func (manager *ClientManager) HeartbeatTimer() {
|
||||
}
|
||||
if err := cli.Ping(); err != nil {
|
||||
manager.CloseClient(cli)
|
||||
logx.Debugf("WS发送心跳失败: uid=%v, cid=%s, usercount=%d", userId, cli.ClientId, Manager.Count())
|
||||
logx.Debugf("WS - failed to send heartbeat: uid=%v, cid=%s, usercount=%d", userId, cli.ClientId, Manager.Count())
|
||||
} else {
|
||||
logx.Debugf("WS发送心跳成功: uid=%v, cid=%s", userId, cli.ClientId)
|
||||
logx.Debugf("WS - send heartbeat successfully: uid=%v, cid=%s", userId, cli.ClientId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user