mirror of
				https://gitee.com/dromara/mayfly-go
				synced 2025-11-04 08:20:25 +08:00 
			
		
		
		
	fix: fixed some minor issues
This commit is contained in:
		@@ -1,5 +1,5 @@
 | 
				
			|||||||
# 构建前端资源
 | 
					# 构建前端资源
 | 
				
			||||||
FROM m.daocloud.io/docker.io/node:18-bookworm-slim as fe-builder
 | 
					FROM m.daocloud.io/docker.io/node:18-bookworm-slim AS fe-builder
 | 
				
			||||||
 | 
					
 | 
				
			||||||
WORKDIR /mayfly
 | 
					WORKDIR /mayfly
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -10,7 +10,7 @@ RUN yarn config set registry 'https://registry.npmmirror.com' && \
 | 
				
			|||||||
    yarn build
 | 
					    yarn build
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# 构建后端资源
 | 
					# 构建后端资源
 | 
				
			||||||
FROM m.daocloud.io/docker.io/golang:1.23 as be-builder
 | 
					FROM m.daocloud.io/docker.io/golang:1.23 AS be-builder
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ENV GOPROXY https://goproxy.cn
 | 
					ENV GOPROXY https://goproxy.cn
 | 
				
			||||||
WORKDIR /mayfly
 | 
					WORKDIR /mayfly
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -56,6 +56,7 @@ function build() {
 | 
				
			|||||||
    if [ "${os}" == "windows" ];then
 | 
					    if [ "${os}" == "windows" ];then
 | 
				
			||||||
        execFileName="${execFileName}.exe"
 | 
					        execFileName="${execFileName}.exe"
 | 
				
			||||||
    fi
 | 
					    fi
 | 
				
			||||||
 | 
					    go mod tidy
 | 
				
			||||||
    CGO_ENABLE=0 GOOS=${os} GOARCH=${arch} go build -ldflags=-w -o ${execFileName} main.go
 | 
					    CGO_ENABLE=0 GOOS=${os} GOARCH=${arch} go build -ldflags=-w -o ${execFileName} main.go
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if [ -d ${toFolder} ] ; then
 | 
					    if [ -d ${toFolder} ] ; then
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,7 +11,7 @@
 | 
				
			|||||||
    },
 | 
					    },
 | 
				
			||||||
    "dependencies": {
 | 
					    "dependencies": {
 | 
				
			||||||
        "@element-plus/icons-vue": "^2.3.1",
 | 
					        "@element-plus/icons-vue": "^2.3.1",
 | 
				
			||||||
        "@vueuse/core": "^11.2.0",
 | 
					        "@vueuse/core": "^11.3.0",
 | 
				
			||||||
        "asciinema-player": "^3.8.1",
 | 
					        "asciinema-player": "^3.8.1",
 | 
				
			||||||
        "axios": "^1.6.2",
 | 
					        "axios": "^1.6.2",
 | 
				
			||||||
        "clipboard": "^2.0.11",
 | 
					        "clipboard": "^2.0.11",
 | 
				
			||||||
@@ -28,7 +28,7 @@
 | 
				
			|||||||
        "monaco-sql-languages": "^0.12.2",
 | 
					        "monaco-sql-languages": "^0.12.2",
 | 
				
			||||||
        "monaco-themes": "^0.4.4",
 | 
					        "monaco-themes": "^0.4.4",
 | 
				
			||||||
        "nprogress": "^0.2.0",
 | 
					        "nprogress": "^0.2.0",
 | 
				
			||||||
        "pinia": "^2.2.6",
 | 
					        "pinia": "^2.2.7",
 | 
				
			||||||
        "qrcode.vue": "^3.5.1",
 | 
					        "qrcode.vue": "^3.5.1",
 | 
				
			||||||
        "screenfull": "^6.0.2",
 | 
					        "screenfull": "^6.0.2",
 | 
				
			||||||
        "sortablejs": "^1.15.3",
 | 
					        "sortablejs": "^1.15.3",
 | 
				
			||||||
@@ -38,7 +38,7 @@
 | 
				
			|||||||
        "uuid": "^9.0.1",
 | 
					        "uuid": "^9.0.1",
 | 
				
			||||||
        "vue": "^3.5.13",
 | 
					        "vue": "^3.5.13",
 | 
				
			||||||
        "vue-i18n": "^10.0.4",
 | 
					        "vue-i18n": "^10.0.4",
 | 
				
			||||||
        "vue-router": "^4.4.5",
 | 
					        "vue-router": "^4.5.0",
 | 
				
			||||||
        "xterm": "^5.3.0",
 | 
					        "xterm": "^5.3.0",
 | 
				
			||||||
        "xterm-addon-fit": "^0.8.0",
 | 
					        "xterm-addon-fit": "^0.8.0",
 | 
				
			||||||
        "xterm-addon-search": "^0.13.0",
 | 
					        "xterm-addon-search": "^0.13.0",
 | 
				
			||||||
@@ -52,16 +52,16 @@
 | 
				
			|||||||
        "@types/sortablejs": "^1.15.8",
 | 
					        "@types/sortablejs": "^1.15.8",
 | 
				
			||||||
        "@typescript-eslint/eslint-plugin": "^6.7.4",
 | 
					        "@typescript-eslint/eslint-plugin": "^6.7.4",
 | 
				
			||||||
        "@typescript-eslint/parser": "^6.7.4",
 | 
					        "@typescript-eslint/parser": "^6.7.4",
 | 
				
			||||||
        "@vitejs/plugin-vue": "^5.2.0",
 | 
					        "@vitejs/plugin-vue": "^5.2.1",
 | 
				
			||||||
        "@vue/compiler-sfc": "^3.5.13",
 | 
					        "@vue/compiler-sfc": "^3.5.13",
 | 
				
			||||||
        "code-inspector-plugin": "^0.4.5",
 | 
					        "code-inspector-plugin": "^0.4.5",
 | 
				
			||||||
        "dotenv": "^16.3.1",
 | 
					        "dotenv": "^16.3.1",
 | 
				
			||||||
        "eslint": "^8.35.0",
 | 
					        "eslint": "^8.35.0",
 | 
				
			||||||
        "eslint-plugin-vue": "^9.28.0",
 | 
					        "eslint-plugin-vue": "^9.31.0",
 | 
				
			||||||
        "prettier": "^3.2.5",
 | 
					        "prettier": "^3.2.5",
 | 
				
			||||||
        "sass": "^1.81.0",
 | 
					        "sass": "^1.81.0",
 | 
				
			||||||
        "typescript": "^5.6.3",
 | 
					        "typescript": "^5.7.2",
 | 
				
			||||||
        "vite": "^5.4.11",
 | 
					        "vite": "^6.0.1",
 | 
				
			||||||
        "vue-eslint-parser": "^9.4.3"
 | 
					        "vue-eslint-parser": "^9.4.3"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "browserslist": [
 | 
					    "browserslist": [
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -196,6 +196,9 @@ watch(
 | 
				
			|||||||
    (newValue: any) => {
 | 
					    (newValue: any) => {
 | 
				
			||||||
        if (!monacoEditorIns.hasTextFocus()) {
 | 
					        if (!monacoEditorIns.hasTextFocus()) {
 | 
				
			||||||
            state.languageMode = props.language;
 | 
					            state.languageMode = props.language;
 | 
				
			||||||
 | 
					            if (newValue == null) {
 | 
				
			||||||
 | 
					                newValue = '';
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
            monacoEditorIns?.setValue(newValue);
 | 
					            monacoEditorIns?.setValue(newValue);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -47,7 +47,7 @@ export default {
 | 
				
			|||||||
        copy: 'Copy',
 | 
					        copy: 'Copy',
 | 
				
			||||||
        pleaseInput: 'Please enter {label}',
 | 
					        pleaseInput: 'Please enter {label}',
 | 
				
			||||||
        pleaseSelect: 'Please select {label}',
 | 
					        pleaseSelect: 'Please select {label}',
 | 
				
			||||||
        formValidationError: 'Please fill in the form information correctly',
 | 
					        formValidationError: 'Please check the form',
 | 
				
			||||||
        createTitle: 'Create {name}',
 | 
					        createTitle: 'Create {name}',
 | 
				
			||||||
        editTitle: 'Edit {name}',
 | 
					        editTitle: 'Edit {name}',
 | 
				
			||||||
        detailTitle: '{name} Details',
 | 
					        detailTitle: '{name} Details',
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,7 +19,6 @@ export default {
 | 
				
			|||||||
        newTabOpen: 'New tab opens',
 | 
					        newTabOpen: 'New tab opens',
 | 
				
			||||||
        redisSelectErr: 'Select redis first',
 | 
					        redisSelectErr: 'Select redis first',
 | 
				
			||||||
        flushDbTips: 'Make sure to clear all keys of the [{db}] library?',
 | 
					        flushDbTips: 'Make sure to clear all keys of the [{db}] library?',
 | 
				
			||||||
        keyNotEmpty: 'The Key cannot be empty',
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // info
 | 
					        // info
 | 
				
			||||||
        redisInfoTitle: 'Redis server information',
 | 
					        redisInfoTitle: 'Redis server information',
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -47,7 +47,7 @@ export default {
 | 
				
			|||||||
        copy: '复制',
 | 
					        copy: '复制',
 | 
				
			||||||
        pleaseInput: '请输入{label}',
 | 
					        pleaseInput: '请输入{label}',
 | 
				
			||||||
        pleaseSelect: '请选择{label}',
 | 
					        pleaseSelect: '请选择{label}',
 | 
				
			||||||
        formValidationError: '请正确填写表单信息',
 | 
					        formValidationError: '信息填写有误,请检查',
 | 
				
			||||||
        createTitle: '创建{name}',
 | 
					        createTitle: '创建{name}',
 | 
				
			||||||
        editTitle: '编辑{name}',
 | 
					        editTitle: '编辑{name}',
 | 
				
			||||||
        detailTitle: '{name}详情',
 | 
					        detailTitle: '{name}详情',
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,7 +17,6 @@ export default {
 | 
				
			|||||||
        newTabOpen: '新tab打开',
 | 
					        newTabOpen: '新tab打开',
 | 
				
			||||||
        redisSelectErr: '请先选择redis',
 | 
					        redisSelectErr: '请先选择redis',
 | 
				
			||||||
        flushDbTips: '确定清空[{db}]库的所有key?',
 | 
					        flushDbTips: '确定清空[{db}]库的所有key?',
 | 
				
			||||||
        keyNotEmpty: 'Key不能为空',
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // info
 | 
					        // info
 | 
				
			||||||
        redisInfoTitle: 'Redis服务器信息',
 | 
					        redisInfoTitle: 'Redis服务器信息',
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -303,8 +303,10 @@ const onRunSql = async (newTab = false) => {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    const sqls = splitSql(sql);
 | 
					    const sqls = splitSql(sql);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (sqls.length == 1) {
 | 
				
			||||||
 | 
					        const oneSql = sqls[0];
 | 
				
			||||||
        // 简单截取前十个字符
 | 
					        // 简单截取前十个字符
 | 
				
			||||||
    const sqlPrefix = sql.slice(0, 10).toLowerCase();
 | 
					        const sqlPrefix = oneSql.slice(0, 10).toLowerCase();
 | 
				
			||||||
        const nonQuery =
 | 
					        const nonQuery =
 | 
				
			||||||
            sqlPrefix.startsWith('update') ||
 | 
					            sqlPrefix.startsWith('update') ||
 | 
				
			||||||
            sqlPrefix.startsWith('insert') ||
 | 
					            sqlPrefix.startsWith('insert') ||
 | 
				
			||||||
@@ -312,8 +314,6 @@ const onRunSql = async (newTab = false) => {
 | 
				
			|||||||
            sqlPrefix.startsWith('alter') ||
 | 
					            sqlPrefix.startsWith('alter') ||
 | 
				
			||||||
            sqlPrefix.startsWith('drop') ||
 | 
					            sqlPrefix.startsWith('drop') ||
 | 
				
			||||||
            sqlPrefix.startsWith('create');
 | 
					            sqlPrefix.startsWith('create');
 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (sqls.length == 1) {
 | 
					 | 
				
			||||||
        let execRemark;
 | 
					        let execRemark;
 | 
				
			||||||
        if (nonQuery) {
 | 
					        if (nonQuery) {
 | 
				
			||||||
            const res: any = await ElMessageBox.prompt(t('db.enterExecRemarkTips'), 'Tip', {
 | 
					            const res: any = await ElMessageBox.prompt(t('db.enterExecRemarkTips'), 'Tip', {
 | 
				
			||||||
@@ -323,7 +323,7 @@ const onRunSql = async (newTab = false) => {
 | 
				
			|||||||
            });
 | 
					            });
 | 
				
			||||||
            execRemark = res.value;
 | 
					            execRemark = res.value;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        runSql(sql, execRemark, newTab);
 | 
					        runSql(oneSql, execRemark, newTab);
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
        let isFirst = true;
 | 
					        let isFirst = true;
 | 
				
			||||||
        for (let s of sqls) {
 | 
					        for (let s of sqls) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -483,7 +483,6 @@ const setTableColumns = (columns: any) => {
 | 
				
			|||||||
        x.dataType = dbDialect.getDataType(x.columnType);
 | 
					        x.dataType = dbDialect.getDataType(x.columnType);
 | 
				
			||||||
        x.dataTypeSubscript = ColumnTypeSubscript[x.dataType];
 | 
					        x.dataTypeSubscript = ColumnTypeSubscript[x.dataType];
 | 
				
			||||||
        x.remark = `${x.columnType} ${x.columnComment ? ' |  ' + x.columnComment : ''}`;
 | 
					        x.remark = `${x.columnType} ${x.columnComment ? ' |  ' + x.columnComment : ''}`;
 | 
				
			||||||
        console.log(x);
 | 
					 | 
				
			||||||
        return {
 | 
					        return {
 | 
				
			||||||
            ...x,
 | 
					            ...x,
 | 
				
			||||||
            key: columnName,
 | 
					            key: columnName,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,7 +6,7 @@
 | 
				
			|||||||
                <tag-tree
 | 
					                <tag-tree
 | 
				
			||||||
                    class="machine-terminal-tree"
 | 
					                    class="machine-terminal-tree"
 | 
				
			||||||
                    ref="tagTreeRef"
 | 
					                    ref="tagTreeRef"
 | 
				
			||||||
                    :resource-type="TagResourceTypeEnum.Machine.value"
 | 
					                    :resource-type="TagResourceTypePath.MachineAuthCert"
 | 
				
			||||||
                    :tag-path-node-type="NodeTypeTagPath"
 | 
					                    :tag-path-node-type="NodeTypeTagPath"
 | 
				
			||||||
                    :default-expanded-keys="state.defaultExpendKey"
 | 
					                    :default-expanded-keys="state.defaultExpendKey"
 | 
				
			||||||
                >
 | 
					                >
 | 
				
			||||||
@@ -168,7 +168,7 @@ import { useRouter } from 'vue-router';
 | 
				
			|||||||
import { getMachineTerminalSocketUrl, machineApi } from './api';
 | 
					import { getMachineTerminalSocketUrl, machineApi } from './api';
 | 
				
			||||||
import { formatDate } from '@/common/utils/format';
 | 
					import { formatDate } from '@/common/utils/format';
 | 
				
			||||||
import { hasPerms } from '@/components/auth/auth';
 | 
					import { hasPerms } from '@/components/auth/auth';
 | 
				
			||||||
import { TagResourceTypeEnum } from '@/common/commonEnum';
 | 
					import { TagResourceTypeEnum, TagResourceTypePath } from '@/common/commonEnum';
 | 
				
			||||||
import { NodeType, TagTreeNode, getTagTypeCodeByPath } from '../component/tag';
 | 
					import { NodeType, TagTreeNode, getTagTypeCodeByPath } from '../component/tag';
 | 
				
			||||||
import TagTree from '../component/TagTree.vue';
 | 
					import TagTree from '../component/TagTree.vue';
 | 
				
			||||||
import { Pane, Splitpanes } from 'splitpanes';
 | 
					import { Pane, Splitpanes } from 'splitpanes';
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -160,7 +160,7 @@
 | 
				
			|||||||
        <div style="text-align: center; margin-top: 10px"></div>
 | 
					        <div style="text-align: center; margin-top: 10px"></div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <el-dialog :title="$t('redis.addKey')" v-model="newKeyDialog.visible" width="500px" :destroy-on-close="true" :close-on-click-modal="false">
 | 
					        <el-dialog :title="$t('redis.addKey')" v-model="newKeyDialog.visible" width="500px" :destroy-on-close="true" :close-on-click-modal="false">
 | 
				
			||||||
            <el-form ref="keyForm" label-width="auto">
 | 
					            <el-form ref="keyForm" label-width="auto" :rules="keyFormRules" :model="newKeyDialog.keyInfo">
 | 
				
			||||||
                <el-form-item prop="key" label="Key" required>
 | 
					                <el-form-item prop="key" label="Key" required>
 | 
				
			||||||
                    <el-input v-model.trim="newKeyDialog.keyInfo.key"></el-input>
 | 
					                    <el-input v-model.trim="newKeyDialog.keyInfo.key"></el-input>
 | 
				
			||||||
                </el-form-item>
 | 
					                </el-form-item>
 | 
				
			||||||
@@ -187,9 +187,9 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
<script lang="ts" setup>
 | 
					<script lang="ts" setup>
 | 
				
			||||||
import { redisApi } from './api';
 | 
					import { redisApi } from './api';
 | 
				
			||||||
import { ref, defineAsyncComponent, toRefs, reactive, onMounted, nextTick, Ref, watch } from 'vue';
 | 
					import { ref, defineAsyncComponent, toRefs, reactive, onMounted, nextTick, Ref, watch, useTemplateRef } from 'vue';
 | 
				
			||||||
import { ElMessage, ElMessageBox } from 'element-plus';
 | 
					import { ElMessageBox } from 'element-plus';
 | 
				
			||||||
import { isTrue, notBlank, notNull } from '@/common/assert';
 | 
					import { isTrue, notNull } from '@/common/assert';
 | 
				
			||||||
import { copyToClipboard } from '@/common/utils/string';
 | 
					import { copyToClipboard } from '@/common/utils/string';
 | 
				
			||||||
import { TagTreeNode, NodeType, getTagTypeCodeByPath } from '../component/tag';
 | 
					import { TagTreeNode, NodeType, getTagTypeCodeByPath } from '../component/tag';
 | 
				
			||||||
import TagTree from '../component/TagTree.vue';
 | 
					import TagTree from '../component/TagTree.vue';
 | 
				
			||||||
@@ -202,13 +202,21 @@ import { RedisInst } from './redis';
 | 
				
			|||||||
import { useAutoOpenResource } from '@/store/autoOpenResource';
 | 
					import { useAutoOpenResource } from '@/store/autoOpenResource';
 | 
				
			||||||
import { storeToRefs } from 'pinia';
 | 
					import { storeToRefs } from 'pinia';
 | 
				
			||||||
import { useI18n } from 'vue-i18n';
 | 
					import { useI18n } from 'vue-i18n';
 | 
				
			||||||
import { useI18nDeleteConfirm, useI18nDeleteSuccessMsg, useI18nOperateSuccessMsg } from '@/hooks/useI18n';
 | 
					import { useI18nDeleteConfirm, useI18nDeleteSuccessMsg, useI18nFormValidate, useI18nOperateSuccessMsg, useI18nPleaseInput } from '@/hooks/useI18n';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const KeyDetail = defineAsyncComponent(() => import('./KeyDetail.vue'));
 | 
					const KeyDetail = defineAsyncComponent(() => import('./KeyDetail.vue'));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const { t } = useI18n();
 | 
					const { t } = useI18n();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const contextmenuRef = ref();
 | 
					const keyFormRules = {
 | 
				
			||||||
 | 
					    key: [
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            required: true,
 | 
				
			||||||
 | 
					            message: useI18nPleaseInput('Key'),
 | 
				
			||||||
 | 
					            trigger: ['change', 'blur'],
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const cmCopyKey = new ContextmenuItem('copyValue', 'Copy')
 | 
					const cmCopyKey = new ContextmenuItem('copyValue', 'Copy')
 | 
				
			||||||
    .withIcon('CopyDocument')
 | 
					    .withIcon('CopyDocument')
 | 
				
			||||||
@@ -301,8 +309,11 @@ const treeProps = {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
const defaultCount = 250;
 | 
					const defaultCount = 250;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const contextmenuRef = ref();
 | 
				
			||||||
const keyTreeRef: any = ref(null);
 | 
					const keyTreeRef: any = ref(null);
 | 
				
			||||||
const tagTreeRef: any = ref(null);
 | 
					const tagTreeRef: any = ref(null);
 | 
				
			||||||
 | 
					const keyFormRef = useTemplateRef('keyForm');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const redisInst: Ref<RedisInst> = ref(new RedisInst());
 | 
					const redisInst: Ref<RedisInst> = ref(new RedisInst());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const state = reactive({
 | 
					const state = reactive({
 | 
				
			||||||
@@ -573,9 +584,9 @@ const cancelNewKey = () => {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const newKey = async () => {
 | 
					const newKey = async () => {
 | 
				
			||||||
 | 
					    await useI18nFormValidate(keyFormRef);
 | 
				
			||||||
    const keyInfo = state.newKeyDialog.keyInfo;
 | 
					    const keyInfo = state.newKeyDialog.keyInfo;
 | 
				
			||||||
    const key = keyInfo.key;
 | 
					    const key = keyInfo.key;
 | 
				
			||||||
    notBlank(key, t('redis.keyNotEmpty'));
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    showKeyDetail(
 | 
					    showKeyDetail(
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -370,12 +370,9 @@ func (p *tagTreeAppImpl) GetAccountTags(accountId uint64, query *entity.TagTreeQ
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	codePathLikes := accountTagPaths
 | 
						codePathLikes := accountTagPaths
 | 
				
			||||||
	needFilterAccountTagPaths := accountTagPaths
 | 
						needFilterAccountTagPaths := make(map[string][]string, 0)
 | 
				
			||||||
 | 
					 | 
				
			||||||
	needFilter := false
 | 
					 | 
				
			||||||
	typePaths := query.TypePaths
 | 
						typePaths := query.TypePaths
 | 
				
			||||||
	if len(typePaths) > 0 {
 | 
						if len(typePaths) > 0 {
 | 
				
			||||||
		needFilterAccountTagPaths = make([]string, 0)
 | 
					 | 
				
			||||||
		codePathLikes = []string{}
 | 
							codePathLikes = []string{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		for _, typePath := range typePaths {
 | 
							for _, typePath := range typePaths {
 | 
				
			||||||
@@ -391,14 +388,13 @@ func (p *tagTreeAppImpl) GetAccountTags(accountId uint64, query *entity.TagTreeQ
 | 
				
			|||||||
				return cast.ToString(int8(tt)) + entity.CodePathResourceSeparator + "%"
 | 
									return cast.ToString(int8(tt)) + entity.CodePathResourceSeparator + "%"
 | 
				
			||||||
			}), entity.CodePathSeparator) + entity.CodePathSeparator
 | 
								}), entity.CodePathSeparator) + entity.CodePathSeparator
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// 根据用户拥有的标签路径,赋值要过滤匹配的标签路径条件
 | 
								// 根据用户拥有的标签路径,赋值要过滤匹配的标签类型路径
 | 
				
			||||||
			for _, accountTag := range accountTagPaths {
 | 
								for _, accountTag := range accountTagPaths {
 | 
				
			||||||
				accountTagCodePath := entity.CodePath(accountTag)
 | 
									accountTagCodePath := entity.CodePath(accountTag)
 | 
				
			||||||
				// 标签路径,不包含资源段,如tag1/tag2/1|xxx => tag1/tag2/
 | 
									// 标签路径,不包含资源段,如tag1/tag2/1|xxx => tag1/tag2/
 | 
				
			||||||
				tagPath := accountTagCodePath.GetTag()
 | 
									tagPath := accountTagCodePath.GetTag()
 | 
				
			||||||
				// 纯纯的标签类型(不包含资源段),则直接在该标签路径上补上对应的子资源类型匹配表达式
 | 
									// 纯纯的标签类型(不包含资源段),则直接在该标签路径上补上对应的子资源类型匹配表达式
 | 
				
			||||||
				if tagPath == accountTagCodePath {
 | 
									if tagPath == accountTagCodePath {
 | 
				
			||||||
					needFilterAccountTagPaths = append(needFilterAccountTagPaths, accountTag)
 | 
					 | 
				
			||||||
					// 查询标签类型为标签时,特殊处理
 | 
										// 查询标签类型为标签时,特殊处理
 | 
				
			||||||
					if len(childOrderTypes) == 1 && childOrderTypes[0] == entity.TagTypeTag {
 | 
										if len(childOrderTypes) == 1 && childOrderTypes[0] == entity.TagTypeTag {
 | 
				
			||||||
						codePathLikes = append(codePathLikes, accountTag)
 | 
											codePathLikes = append(codePathLikes, accountTag)
 | 
				
			||||||
@@ -410,7 +406,7 @@ func (p *tagTreeAppImpl) GetAccountTags(accountId uint64, query *entity.TagTreeQ
 | 
				
			|||||||
					continue
 | 
										continue
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				// 将用户有权限操作的标签如  tag1/tag2/1|xxx 替换为tag1/tag2/1|%,并与需要查询的资源类型进行匹配
 | 
									// 将用户有权限操作的标签如  tag1/tag2/type|code 替换为tag1/tag2/type|%,并与需要查询的资源类型进行匹配
 | 
				
			||||||
				accountTagCodePathSections := accountTagCodePath.GetPathSections()
 | 
									accountTagCodePathSections := accountTagCodePath.GetPathSections()
 | 
				
			||||||
				for _, section := range accountTagCodePathSections {
 | 
									for _, section := range accountTagCodePathSections {
 | 
				
			||||||
					if section.Type == entity.TagTypeTag {
 | 
										if section.Type == entity.TagTypeTag {
 | 
				
			||||||
@@ -419,21 +415,33 @@ func (p *tagTreeAppImpl) GetAccountTags(accountId uint64, query *entity.TagTreeQ
 | 
				
			|||||||
					section.Code = "%"
 | 
										section.Code = "%"
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// tag1/tag2/type1|%/type2|%
 | 
				
			||||||
				codePathLike := string(tagPath) + childOrderTypesMatch
 | 
									codePathLike := string(tagPath) + childOrderTypesMatch
 | 
				
			||||||
				if entity.CodePath(accountTagCodePathSections.ToCodePath()).CanAccess(codePathLike) {
 | 
									accountMatchPath := accountTagCodePathSections.ToCodePath()
 | 
				
			||||||
					codePathLikes = append(codePathLikes, codePathLike)
 | 
									// 用户有权限操作该标签则直接添加即可
 | 
				
			||||||
					needFilterAccountTagPaths = append(needFilterAccountTagPaths, accountTag)
 | 
									if entity.CodePath(accountMatchPath).CanAccess(codePathLike) {
 | 
				
			||||||
 | 
										codePathLikes = append(codePathLikes, accountTag)
 | 
				
			||||||
 | 
										continue
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// 如用户分配了: "default/type1|code1/type2|code2/type3|code3/", 需要查询的codePathLike为: default/type1|%/type2|%/,即用户分配的标签路径是查询的子节点。
 | 
				
			||||||
 | 
									// 若需要获取所有子节点,则codePathLike 使用default/type1|code1/type2|code2/去查。否则需要单独再去查一遍
 | 
				
			||||||
 | 
									if strings.HasPrefix(accountMatchPath, codePathLike) {
 | 
				
			||||||
 | 
										actualMatchCodePath := accountTagCodePathSections[len(entity.CodePath(codePathLike).GetPathSections())-1].Path
 | 
				
			||||||
 | 
										needFilterAccountTagPaths[actualMatchCodePath] = append(needFilterAccountTagPaths[actualMatchCodePath], accountTag)
 | 
				
			||||||
 | 
										if query.GetAllChildren {
 | 
				
			||||||
 | 
											codePathLikes = append(codePathLikes, actualMatchCodePath)
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// 去重处理
 | 
							// 去重处理
 | 
				
			||||||
		codePathLikes = collx.ArrayDeduplicate(codePathLikes)
 | 
							codePathLikes = collx.ArrayDeduplicate(codePathLikes)
 | 
				
			||||||
		needFilter = true
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// 账号权限经过处理为空,则说明没有用户可以操作的标签,直接返回即可
 | 
						// 账号权限经过处理为空,则说明没有用户可以操作的标签,直接返回即可
 | 
				
			||||||
	if needFilter && len(needFilterAccountTagPaths) == 0 {
 | 
						if len(codePathLikes) == 0 {
 | 
				
			||||||
		return tagResources
 | 
							return tagResources
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -441,11 +449,28 @@ func (p *tagTreeAppImpl) GetAccountTags(accountId uint64, query *entity.TagTreeQ
 | 
				
			|||||||
	tagResourceQuery.CodePathLikes = codePathLikes
 | 
						tagResourceQuery.CodePathLikes = codePathLikes
 | 
				
			||||||
	p.ListByQuery(tagResourceQuery, &tagResources)
 | 
						p.ListByQuery(tagResourceQuery, &tagResources)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if needFilter {
 | 
						// 不是获取所有子节点,则需要额外查询需要过滤的节点信息。如用户分配了default/2|db_local/5|db_local_root/22|cWMpm6137g/标签,但是typePath为default/2|%/5|%/
 | 
				
			||||||
 | 
						// 由于不是获取所有子节点,则会被追加Type进行过滤,故获取不到default/2|db_local/5|db_local_root/的信息,需要额外查询
 | 
				
			||||||
 | 
						if !query.GetAllChildren && len(needFilterAccountTagPaths) > 0 {
 | 
				
			||||||
 | 
							var otherTags []*dto.SimpleTagTree
 | 
				
			||||||
 | 
							p.ListByQuery(&entity.TagTreeQuery{
 | 
				
			||||||
 | 
								CodePaths: collx.MapKeys(needFilterAccountTagPaths),
 | 
				
			||||||
 | 
							}, &otherTags)
 | 
				
			||||||
 | 
							tagResources = append(tagResources, otherTags...)
 | 
				
			||||||
 | 
							// 清空,因为不是获取所有子节点,so 后续不需要进行过滤
 | 
				
			||||||
 | 
							clear(needFilterAccountTagPaths)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if len(needFilterAccountTagPaths) > 0 {
 | 
				
			||||||
		tagResources = collx.ArrayFilter(tagResources, func(tr *dto.SimpleTagTree) bool {
 | 
							tagResources = collx.ArrayFilter(tagResources, func(tr *dto.SimpleTagTree) bool {
 | 
				
			||||||
			return slices.ContainsFunc(needFilterAccountTagPaths, func(accountTagPath string) bool {
 | 
								for codePathLike, accountTags := range needFilterAccountTagPaths {
 | 
				
			||||||
				return entity.CodePath(accountTagPath).CanAccess(tr.CodePath)
 | 
									if strings.HasPrefix(tr.CodePath, codePathLike) {
 | 
				
			||||||
 | 
										return slices.ContainsFunc(accountTags, func(accountTag string) bool {
 | 
				
			||||||
 | 
											return entity.CodePath(accountTag).CanAccess(tr.CodePath)
 | 
				
			||||||
					})
 | 
										})
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								return true
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -25,6 +25,13 @@ func TestTagPathMatch(t *testing.T) {
 | 
				
			|||||||
	codePathLike := "default/2|%/22|%/"
 | 
						codePathLike := "default/2|%/22|%/"
 | 
				
			||||||
	accountCodePath := "default/2|db_local/5|db_local_root/"
 | 
						accountCodePath := "default/2|db_local/5|db_local_root/"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//  strings.HasPrefix(resourceTagPath, accountPath)
 | 
				
			||||||
 | 
						// resourceTagPath -> default/2|%/5|%/
 | 
				
			||||||
 | 
						// account  -> default/2|%/5|%/22|%/    "default/2|db_local/5|db_local_root/22|cWMpm6137g/"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// resourceTagPath -> default/2|%/5|%/
 | 
				
			||||||
 | 
						// account  -> default/2|%/    "default/2|db_local/"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	sections := entity.CodePath(accountCodePath).GetPathSections()
 | 
						sections := entity.CodePath(accountCodePath).GetPathSections()
 | 
				
			||||||
	for _, section := range sections {
 | 
						for _, section := range sections {
 | 
				
			||||||
		if section.Type == entity.TagTypeTag {
 | 
							if section.Type == entity.TagTypeTag {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -198,6 +198,12 @@ func (tps PathSections) ToCodePath() string {
 | 
				
			|||||||
	}), CodePathSeparator) + CodePathSeparator
 | 
						}), CodePathSeparator) + CodePathSeparator
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (tps PathSections) GetSection(tagType TagType) []*PathSection {
 | 
				
			||||||
 | 
						return collx.ArrayFilter(tps, func(tp *PathSection) bool {
 | 
				
			||||||
 | 
							return tp.Type == tagType
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetCodesByCodePaths 从codePaths中提取指定标签类型的所有tagCode并去重
 | 
					// GetCodesByCodePaths 从codePaths中提取指定标签类型的所有tagCode并去重
 | 
				
			||||||
// 如:codePaths = tag1/tag2/1|xxxcode/11|yyycode/,  tagType = 1 -> xxxcode,  tagType = 11 -> yyycode
 | 
					// 如:codePaths = tag1/tag2/1|xxxcode/11|yyycode/,  tagType = 1 -> xxxcode,  tagType = 11 -> yyycode
 | 
				
			||||||
func GetCodesByCodePaths(tagType TagType, codePaths ...string) []string {
 | 
					func GetCodesByCodePaths(tagType TagType, codePaths ...string) []string {
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user