refactor: 标签不可移动,资源选择优化等

This commit is contained in:
meilin.huang
2025-10-07 15:41:19 +08:00
parent c4d52ce47a
commit 4ac57cd140
23 changed files with 172 additions and 258 deletions

View File

@@ -11,23 +11,23 @@
}, },
"dependencies": { "dependencies": {
"@element-plus/icons-vue": "^2.3.2", "@element-plus/icons-vue": "^2.3.2",
"@logicflow/core": "^2.1.1", "@logicflow/core": "^2.1.2",
"@logicflow/extension": "^2.1.2", "@logicflow/extension": "^2.1.3",
"@vueuse/core": "^13.9.0", "@vueuse/core": "^13.9.0",
"@xterm/addon-fit": "^0.10.0", "@xterm/addon-fit": "^0.10.0",
"@xterm/addon-search": "^0.15.0", "@xterm/addon-search": "^0.15.0",
"@xterm/addon-web-links": "^0.11.0", "@xterm/addon-web-links": "^0.11.0",
"@xterm/xterm": "^5.5.0", "@xterm/xterm": "^5.5.0",
"asciinema-player": "^3.10.0", "asciinema-player": "^3.11.0",
"axios": "^1.6.2", "axios": "^1.6.2",
"clipboard": "^2.0.11", "clipboard": "^2.0.11",
"crypto-js": "^4.2.0", "crypto-js": "^4.2.0",
"dayjs": "^1.11.18", "dayjs": "^1.11.18",
"echarts": "^6.0.0", "echarts": "^6.0.0",
"element-plus": "^2.11.2", "element-plus": "^2.11.4",
"js-base64": "^3.7.8", "js-base64": "^3.7.8",
"jsencrypt": "^3.5.4", "jsencrypt": "^3.5.4",
"monaco-editor": "^0.53.0", "monaco-editor": "^0.54.0",
"monaco-sql-languages": "^0.15.1", "monaco-sql-languages": "^0.15.1",
"monaco-themes": "^0.4.7", "monaco-themes": "^0.4.7",
"nprogress": "^0.2.0", "nprogress": "^0.2.0",
@@ -38,13 +38,13 @@
"sql-formatter": "^15.6.8", "sql-formatter": "^15.6.8",
"trzsz": "^1.1.5", "trzsz": "^1.1.5",
"uuid": "^13.0.0", "uuid": "^13.0.0",
"vue": "^v3.6.0-alpha.2", "vue": "^v3.5.22",
"vue-i18n": "^11.1.12", "vue-i18n": "^11.1.12",
"vue-router": "^4.5.1", "vue-router": "^4.5.1",
"vuedraggable": "^4.1.0" "vuedraggable": "^4.1.0"
}, },
"devDependencies": { "devDependencies": {
"@tailwindcss/vite": "^4.1.13", "@tailwindcss/vite": "^4.1.14",
"@types/crypto-js": "^4.2.2", "@types/crypto-js": "^4.2.2",
"@types/node": "^22.13.14", "@types/node": "^22.13.14",
"@types/nprogress": "^0.2.0", "@types/nprogress": "^0.2.0",
@@ -56,11 +56,11 @@
"autoprefixer": "^10.4.21", "autoprefixer": "^10.4.21",
"code-inspector-plugin": "^1.0.4", "code-inspector-plugin": "^1.0.4",
"eslint": "^9.29.0", "eslint": "^9.29.0",
"eslint-plugin-vue": "^10.4.0", "eslint-plugin-vue": "^10.5.0",
"postcss": "^8.5.6", "postcss": "^8.5.6",
"prettier": "^3.6.1", "prettier": "^3.6.1",
"sass": "^1.92.1", "sass": "^1.93.2",
"tailwindcss": "^4.1.13", "tailwindcss": "^4.1.14",
"typescript": "^5.9.2", "typescript": "^5.9.2",
"vite": "npm:rolldown-vite@latest", "vite": "npm:rolldown-vite@latest",
"vite-plugin-progress": "0.0.7", "vite-plugin-progress": "0.0.7",

View File

@@ -15,7 +15,7 @@ const config = {
baseWsUrl: `${(window as any).globalConfig.BaseWsUrl || `${location.protocol == 'https:' ? 'wss:' : 'ws:'}//${getBaseApiUrl()}`}/api`, baseWsUrl: `${(window as any).globalConfig.BaseWsUrl || `${location.protocol == 'https:' ? 'wss:' : 'ws:'}//${getBaseApiUrl()}`}/api`,
// 系统版本 // 系统版本
version: 'v1.10.3', version: 'v1.10.4',
}; };
export default config; export default config;

View File

@@ -34,7 +34,6 @@ const props = defineProps({
}, },
placement: { placement: {
type: String, type: String,
required: true,
default: 'top', default: 'top',
}, },
}); });

View File

@@ -7,6 +7,7 @@ export default {
tagTips1: '1. Used to group assets', tagTips1: '1. Used to group assets',
tagTips2: '2. Can be allocated in team management for resource isolation', tagTips2: '2. Can be allocated in team management for resource isolation',
tagTips3: '3. Team members who own a parent tag have access to resources that manipulate their own or child tag associations', tagTips3: '3. Team members who own a parent tag have access to resources that manipulate their own or child tag associations',
tagTips4: '4. Right-click nodes to edit or add child tags',
machine: 'Machine', machine: 'Machine',
db: 'Db', db: 'Db',
code: 'Code', code: 'Code',

View File

@@ -7,6 +7,7 @@ export default {
tagTips1: '1. 用于将资产进行归类', tagTips1: '1. 用于将资产进行归类',
tagTips2: '2. 可在团队管理中进行分配,用于资源隔离', tagTips2: '2. 可在团队管理中进行分配,用于资源隔离',
tagTips3: '3. 拥有父标签的团队成员可访问操作其自身或子标签关联的资源', tagTips3: '3. 拥有父标签的团队成员可访问操作其自身或子标签关联的资源',
tagTips4: '4. 右击节点可进行编辑或添加子标签操作',
machine: '机器', machine: '机器',
db: '数据库', db: '数据库',
es: 'ES', es: 'ES',

View File

@@ -1,7 +1,7 @@
<template> <template>
<el-form :model="bizForm" ref="formRef" :rules="rules" label-width="auto"> <el-form :model="bizForm" ref="formRef" :rules="rules" label-width="auto">
<el-form-item prop="id" label="DB" required> <el-form-item prop="id" label="DB" required>
<TagTreeResourceSelect <ResourceSelect
v-bind="$attrs" v-bind="$attrs"
v-model="selectRedis" v-model="selectRedis"
@change="changeRedis" @change="changeRedis"
@@ -9,7 +9,7 @@
:tag-path-node-type="NodeTypeTagPath" :tag-path-node-type="NodeTypeTagPath"
:placeholder="$t('flow.selectRedisPlaceholder')" :placeholder="$t('flow.selectRedisPlaceholder')"
> >
</TagTreeResourceSelect> </ResourceSelect>
</el-form-item> </el-form-item>
<el-form-item prop="cmd" label="CMD" required> <el-form-item prop="cmd" label="CMD" required>
@@ -21,12 +21,13 @@
<script lang="ts" setup> <script lang="ts" setup>
import { computed, ref } from 'vue'; import { computed, ref } from 'vue';
import { TagResourceTypeEnum } from '@/common/commonEnum'; import { TagResourceTypeEnum } from '@/common/commonEnum';
import TagTreeResourceSelect from '@/views/ops/component/TagTreeResourceSelect.vue'; import ResourceSelect from '@/views/ops/resource/ResourceSelect.vue';
import { NodeType, TagTreeNode } from '@/views/ops/component/tag'; import { NodeType, TagTreeNode } from '@/views/ops/component/tag';
import { redisApi } from '@/views/ops/redis/api'; import { redisApi } from '@/views/ops/redis/api';
import { sleep } from '@/common/utils/loading'; import { sleep } from '@/common/utils/loading';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { Rules } from '@/common/rule'; import { Rules } from '@/common/rule';
import { RedisIcon } from '@/views/ops/redis/resource';
const { t } = useI18n(); const { t } = useI18n();
@@ -52,7 +53,7 @@ const NodeTypeTagPath = new NodeType(TagTreeNode.TagPath).withLoadNodesFunc(asyn
await sleep(100); await sleep(100);
return redisInfos.map((x: any) => { return redisInfos.map((x: any) => {
x.tagPath = parentNode.key; x.tagPath = parentNode.key;
return new TagTreeNode(`${x.code}`, x.name, NodeTypeRedis).withParams(x); return new TagTreeNode(`${x.code}`, x.name, NodeTypeRedis).withParams(x).withIcon(RedisIcon);
}); });
}); });
@@ -61,15 +62,18 @@ const NodeTypeRedis = new NodeType(1).withLoadNodesFunc(async (parentNode: TagTr
const redisInfo = parentNode.params; const redisInfo = parentNode.params;
let dbs: TagTreeNode[] = redisInfo.db.split(',').map((x: string) => { let dbs: TagTreeNode[] = redisInfo.db.split(',').map((x: string) => {
return new TagTreeNode(x, `db${x}`, 2 as any).withIsLeaf(true).withParams({ return new TagTreeNode(x, `db${x}`, 2 as any)
id: redisInfo.id, .withIsLeaf(true)
db: x, .withParams({
name: `db${x}`, id: redisInfo.id,
keys: 0, db: x,
tagPath: redisInfo.tagPath, name: `db${x}`,
redisName: redisInfo.name, keys: 0,
code: redisInfo.code, tagPath: redisInfo.tagPath,
}); redisName: redisInfo.name,
code: redisInfo.code,
})
.withIcon({ name: 'Coin', color: '#67c23a' });
}); });
if (redisInfo.mode == 'cluster') { if (redisInfo.mode == 'cluster') {

View File

@@ -8,7 +8,7 @@
<el-form :model="form" ref="dbForm" :rules="rules" label-position="top" label-width="auto"> <el-form :model="form" ref="dbForm" :rules="rules" label-position="top" label-width="auto">
<el-tabs v-model="tabActiveName"> <el-tabs v-model="tabActiveName">
<el-tab-pane :label="$t('common.basic')" :name="basicTab"> <el-tab-pane :label="$t('common.basic')" :name="basicTab">
<el-row gutter="10"> <el-row :gutter="10">
<el-col :span="12"> <el-col :span="12">
<el-form-item prop="taskName" :label="$t('db.taskName')" required> <el-form-item prop="taskName" :label="$t('db.taskName')" required>
<el-input v-model.trim="form.taskName" auto-complete="off" /> <el-input v-model.trim="form.taskName" auto-complete="off" />
@@ -59,7 +59,7 @@
<monaco-editor height="200px" class="task-sql" language="sql" v-model="form.dataSql" /> <monaco-editor height="200px" class="task-sql" language="sql" v-model="form.dataSql" />
</el-form-item> </el-form-item>
<el-row gutter="10"> <el-row :gutter="10">
<el-col :span="12"> <el-col :span="12">
<el-form-item prop="targetTableName" :label="$t('db.targetDbTable')" required> <el-form-item prop="targetTableName" :label="$t('db.targetDbTable')" required>
<el-select v-model="form.targetTableName" filterable> <el-select v-model="form.targetTableName" filterable>
@@ -80,7 +80,7 @@
</el-col> </el-col>
</el-row> </el-row>
<el-row gutter="10"> <el-row :gutter="10">
<el-col :span="12"> <el-col :span="12">
<FormItemTooltip :label="$t('db.updateField')" prop="updField" :tooltip="$t('db.updateFieldTips')"> <FormItemTooltip :label="$t('db.updateField')" prop="updField" :tooltip="$t('db.updateFieldTips')">
<el-input v-model.trim="form.updField" :placeholder="$t('db.updateFiledPlaceholder')" auto-complete="off" /> <el-input v-model.trim="form.updField" :placeholder="$t('db.updateFiledPlaceholder')" auto-complete="off" />
@@ -94,7 +94,7 @@
</el-col> </el-col>
</el-row> </el-row>
<el-row gutter="10"> <el-row :gutter="10">
<el-col :span="12"> <el-col :span="12">
<FormItemTooltip :label="$t('db.fieldValueSrc')" prop="updFieldSrc" :tooltip="$t('db.fieldValueSrcTips')"> <FormItemTooltip :label="$t('db.fieldValueSrc')" prop="updFieldSrc" :tooltip="$t('db.fieldValueSrcTips')">
<el-input v-model.trim="form.updFieldSrc" :placeholder="$t('db.fieldValueSrcPlaceholder')" auto-complete="off" /> <el-input v-model.trim="form.updFieldSrc" :placeholder="$t('db.fieldValueSrcPlaceholder')" auto-complete="off" />
@@ -320,7 +320,7 @@ watch(dialogVisible, async (newValue: boolean) => {
state.tabActiveName = 'basic'; state.tabActiveName = 'basic';
const propsData = props.data as any; const propsData = props.data as any;
if (!propsData?.id) { if (!propsData?.id) {
let d = {} as FormData; let d = { taskCron: '' } as FormData;
Object.assign(d, basicFormData); Object.assign(d, basicFormData);
state.form = d; state.form = d;
return; return;
@@ -416,6 +416,7 @@ const refreshPreviewInsertSql = () => {
const onSelectSrcDb = async (params: any) => { const onSelectSrcDb = async (params: any) => {
// 初始化数据源 // 初始化数据源
params.databases = params.dbs; // 数据源里需要这个值 params.databases = params.dbs; // 数据源里需要这个值
console.log(params.dbs);
state.srcDbInst = await DbInst.getOrNewInst(params); state.srcDbInst = await DbInst.getOrNewInst(params);
registerDbCompletionItemProvider(params.id, params.db, params.dbs, params.type); registerDbCompletionItemProvider(params.id, params.db, params.dbs, params.type);
}; };

View File

@@ -1,31 +1,23 @@
<template> <template>
<TagTreeResourceSelect <ResourceSelect v-bind="$attrs" v-model="selectNode" @change="changeNode" :resource-type="TagResourceTypePath.Db" :tag-path-node-type="NodeTypeDbInst">
v-bind="$attrs"
v-model="selectNode"
@change="changeNode"
:resource-type="TagResourceTypePath.Db"
:tag-path-node-type="NodeTypeTagPath"
>
<template #iconPrefix> <template #iconPrefix>
<SvgIcon v-if="dbType && getDbDialect(dbType)" :name="getDbDialect(dbType).getInfo().icon" :size="18" /> <SvgIcon v-if="dbType && getDbDialect(dbType)" :name="getDbDialect(dbType).getInfo().icon" :size="18" />
</template> </template>
<template #prefix="{ data }"> </ResourceSelect>
<SvgIcon v-if="data.type.value == SqlExecNodeType.DbInst" :name="getDbDialect(data.params.type).getInfo().icon" :size="18" />
<SvgIcon v-if="data.icon" :name="data.icon.name" :color="data.icon.color" />
</template>
</TagTreeResourceSelect>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { TagResourceTypePath } from '@/common/commonEnum'; import { computed } from 'vue';
import { TagResourceTypeEnum, TagResourceTypePath } from '@/common/commonEnum';
import { NodeType, TagTreeNode } from '@/views/ops/component/tag'; import { NodeType, TagTreeNode } from '@/views/ops/component/tag';
import { dbApi } from '@/views/ops/db/api'; import { dbApi } from '@/views/ops/db/api';
import { sleep } from '@/common/utils/loading'; import { sleep } from '@/common/utils/loading';
import SvgIcon from '@/components/svgIcon/index.vue'; import { getDbDialect, schemaDbTypes } from '@/views/ops/db/dialect';
import { getDbDialect, noSchemaTypes } from '@/views/ops/db/dialect'; import ResourceSelect from '@/views/ops/resource/ResourceSelect.vue';
import TagTreeResourceSelect from '../../component/TagTreeResourceSelect.vue'; import NodeDbInst from '@/views/ops/db/resource/NodeDbInst.vue';
import { computed } from 'vue'; import NodeDb from '@/views/ops/db/resource/NodeDb.vue';
import { DbInst } from '../db'; import { DbIcon, SchemaIcon } from '@/views/ops/db/resource';
import { DbInst } from '@/views/ops/db/db';
const dbId = defineModel<number>('dbId'); const dbId = defineModel<number>('dbId');
const instName = defineModel<string>('instName'); const instName = defineModel<string>('instName');
@@ -35,20 +27,6 @@ const dbType = defineModel<string>('dbType');
const emits = defineEmits(['selectDb']); const emits = defineEmits(['selectDb']);
/**
* 树节点类型
*/
class SqlExecNodeType {
static DbInst = 1;
static Db = 2;
static TableMenu = 3;
static SqlMenu = 4;
static Table = 5;
static Sql = 6;
static PgSchemaMenu = 7;
static PgSchema = 8;
}
const selectNode = computed({ const selectNode = computed({
get: () => { get: () => {
return dbName.value ? `${tagPath.value} > ${instName.value} > ${dbName.value}` : ''; return dbName.value ? `${tagPath.value} > ${instName.value} > ${dbName.value}` : '';
@@ -58,90 +36,94 @@ const selectNode = computed({
}, },
}); });
const DbIcon = { const NodeTypeDbInst = new NodeType(TagResourceTypeEnum.DbInstance.value).withLoadNodesFunc(async (parentNode: TagTreeNode) => {
name: 'Coin', const tagPath = parentNode.key;
color: '#67c23a',
};
// pgsql schema icon const dbInstancesRes = await dbApi.instances.request({ tagPath, pageSize: 100 });
const SchemaIcon = { const dbInstances = dbInstancesRes.list;
name: 'List', if (!dbInstances) {
color: '#67c23a',
};
const NodeTypeTagPath = new NodeType(TagTreeNode.TagPath).withLoadNodesFunc(async (parentNode: TagTreeNode) => {
const dbInfoRes = await dbApi.dbs.request({ tagPath: parentNode.key });
const dbInfos = dbInfoRes.list;
if (!dbInfos) {
return []; return [];
} }
// 防止过快加载会出现一闪而过,对眼睛不好 // 防止过快加载会出现一闪而过,对眼睛不好
await sleep(100); await sleep(100);
return dbInfos?.map((x: any) => { return dbInstances?.map((x: any) => {
x.tagPath = parentNode.key; x.tagPath = tagPath;
return new TagTreeNode(`${parentNode.key}.${x.id}`, x.name, NodeTypeDbInst).withParams(x); return TagTreeNode.new(parentNode, `${x.code}`, x.name, NodeTypeDbConf).withParams(x).withNodeComponent(NodeDbInst);
}); });
}); });
/** mysql类型的数据库没有schema层 */ const NodeTypeDbConf = new NodeType(TagResourceTypeEnum.Db.value).withLoadNodesFunc(async (parentNode: TagTreeNode) => {
const noSchemaType = (type: string) => { const params = parentNode.params;
return noSchemaTypes.includes(type);
};
// 数据库实例节点类型 const tagPath = params.tagPath;
const NodeTypeDbInst = new NodeType(SqlExecNodeType.DbInst).withLoadNodesFunc(async (parentNode: TagTreeNode) => { const authCerts = {} as any;
for (let authCert of params.authCerts) {
authCerts[authCert.name] = authCert;
}
const dbInfoRes = await dbApi.dbs.request({
tagPath: `${tagPath}${TagResourceTypeEnum.DbInstance.value}|${params.code}`,
});
const dbInfos = dbInfoRes.list;
if (!dbInfos) {
return [];
}
return dbInfos?.map((x: any) => {
x.tagPath = tagPath;
x.username = authCerts[x.authCertName]?.username;
return TagTreeNode.new(parentNode, `${x.code}`, x.name, NodeTypeDbs).withParams(x).withIcon(DbIcon).withNodeComponent(NodeDb);
});
});
// 数据库列表名类型
const NodeTypeDbs = new NodeType(222).withLoadNodesFunc(async (parentNode: TagTreeNode) => {
const params = parentNode.params; const params = parentNode.params;
const dbs = (await DbInst.getDbNames(params))?.sort(); const dbs = (await DbInst.getDbNames(params))?.sort();
let fn: NodeType; const hasSchema = schemaDbTypes.includes(params.type);
if (noSchemaType(params.type)) { const nodeType = hasSchema ? NodeTypeDbSchema : NodeTypeNoSchemaDb;
fn = MysqlNodeTypes;
} else {
fn = PgNodeTypes;
}
return dbs.map((x: any) => { return dbs.map((x: any) => {
let tagTreeNode = new TagTreeNode(`${parentNode.key}.${x}`, `${x}`, fn) return TagTreeNode.new(parentNode, `${parentNode.key}.${x}`, x, nodeType)
.withParams({ .withParams({
tagPath: params.tagPath, tagPath: params.tagPath,
id: params.id, id: params.id,
code: params.code,
instanceId: params.instanceId,
name: params.name, name: params.name,
type: params.type, type: params.type,
host: `${params.host}:${params.port}`, host: `${params.host}:${params.port}`,
dbs: dbs, dbs: dbs,
db: x, db: x,
code: params.code,
}) })
.withIcon(DbIcon); .withIcon(DbIcon)
if (noSchemaType(params.type)) { .withIsLeaf(!hasSchema);
tagTreeNode.isLeaf = true;
}
return tagTreeNode;
}); });
}); });
// 数据库节点 // 数据库节点
const PgNodeTypes = new NodeType(SqlExecNodeType.Db).withLoadNodesFunc(async (parentNode: TagTreeNode) => { const NodeTypeDbSchema = new NodeType(2).withLoadNodesFunc(async (parentNode: TagTreeNode) => {
// pg类数据库会多一层schema
const params = parentNode.params; const params = parentNode.params;
params.parentKey = parentNode.key;
const { id, db } = params; const { id, db } = params;
const schemaNames = await dbApi.pgSchemas.request({ id, db }); const schemaNames = await dbApi.pgSchemas.request({ id, db });
const dbs = schemaNames.map((x: any) => `${db}/${x}`);
return schemaNames.map((sn: any) => { return schemaNames.map((sn: any) => {
// 将db变更为 db/schema; // 将db变更为 db/schema;
const nParams = { ...params }; const nParams = { ...params };
nParams.schema = sn; nParams.schema = sn;
nParams.db = nParams.db + '/' + sn; nParams.db = nParams.db + '/' + sn;
nParams.dbs = schemaNames; nParams.dbs = dbs;
let tagTreeNode = new TagTreeNode(`${params.id}.${params.db}.schema.${sn}`, sn, NodeTypePostgresSchema).withParams(nParams).withIcon(SchemaIcon); return TagTreeNode.new(parentNode, `${params.id}.${params.db}.schema.${sn}`, sn, NodeTypePostgresSchema)
tagTreeNode.isLeaf = true; .withParams(nParams)
return tagTreeNode; .withIcon(SchemaIcon)
.withIsLeaf(true);
}); });
}); });
const MysqlNodeTypes = new NodeType(SqlExecNodeType.Db);
// postgres schema模式 // postgres schema模式
const NodeTypePostgresSchema = new NodeType(SqlExecNodeType.PgSchema); const NodeTypePostgresSchema = new NodeType(99);
const NodeTypeNoSchemaDb = new NodeType(99);
const changeNode = (nodeData: TagTreeNode) => { const changeNode = (nodeData: TagTreeNode) => {
const params = nodeData.params; const params = nodeData.params;

View File

@@ -16,18 +16,18 @@ const NodeDbInst = defineAsyncComponent(() => import('./NodeDbInst.vue'));
const NodeDb = defineAsyncComponent(() => import('./NodeDb.vue')); const NodeDb = defineAsyncComponent(() => import('./NodeDb.vue'));
const NodeDbTable = defineAsyncComponent(() => import('./NodeDbTable.vue')); const NodeDbTable = defineAsyncComponent(() => import('./NodeDbTable.vue'));
const DbIcon = { export const DbIcon = {
name: ResourceTypeEnum.Db.extra.icon, name: ResourceTypeEnum.Db.extra.icon,
color: ResourceTypeEnum.Db.extra.iconColor, color: ResourceTypeEnum.Db.extra.iconColor,
}; };
// pgsql schema icon // pgsql schema icon
const SchemaIcon = { export const SchemaIcon = {
name: 'List', name: 'List',
color: '#67c23a', color: '#67c23a',
}; };
const TableIcon = { export const TableIcon = {
name: 'icon db/table', name: 'icon db/table',
color: '#409eff', color: '#409eff',
}; };

View File

@@ -33,13 +33,9 @@
<el-descriptions-item :span="3" :label="$t('tag.relateTag')"><ResourceTags :tags="detailDialog.data.tags" /></el-descriptions-item> <el-descriptions-item :span="3" :label="$t('tag.relateTag')"><ResourceTags :tags="detailDialog.data.tags" /></el-descriptions-item>
<el-descriptions-item :span="3" label="Host">{{ detailDialog.data.host }}</el-descriptions-item> <el-descriptions-item :span="3" :label="$t('docker.addr')">{{ detailDialog.data.addr }}</el-descriptions-item>
<el-descriptions-item :span="3" label="DB">{{ detailDialog.data.db }}</el-descriptions-item>
<el-descriptions-item :span="3" :label="$t('common.remark')">{{ detailDialog.data.remark }}</el-descriptions-item> <el-descriptions-item :span="3" :label="$t('common.remark')">{{ detailDialog.data.remark }}</el-descriptions-item>
<el-descriptions-item :span="3" :label="$t('machine.sshTunnel')">
{{ detailDialog.data.sshTunnelMachineId > 0 ? $t('common.yes') : $t('common.no') }}
</el-descriptions-item>
<el-descriptions-item :span="2" :label="$t('common.createTime')">{{ formatDate(detailDialog.data.createTime) }} </el-descriptions-item> <el-descriptions-item :span="2" :label="$t('common.createTime')">{{ formatDate(detailDialog.data.createTime) }} </el-descriptions-item>
<el-descriptions-item :span="1" :label="$t('common.creator')">{{ detailDialog.data.creator }}</el-descriptions-item> <el-descriptions-item :span="1" :label="$t('common.creator')">{{ detailDialog.data.creator }}</el-descriptions-item>

View File

@@ -8,7 +8,7 @@
<el-row class="mb-2 ml-4"> <el-row class="mb-2 ml-4">
<el-breadcrumb separator-icon="ArrowRight"> <el-breadcrumb separator-icon="ArrowRight">
<el-breadcrumb-item v-for="path in filePathNav" :key="path"> <el-breadcrumb-item v-for="path in filePathNav" :key="path">
<el-link @click="setFiles(path.path)" style="font-weight: bold">{{ path.name }}</el-link> <el-link @click="setFiles(path.path)" class="!cursor-pointer !font-bold">{{ path.name }}</el-link>
</el-breadcrumb-item> </el-breadcrumb-item>
</el-breadcrumb> </el-breadcrumb>
</el-row> </el-row>

View File

@@ -4,7 +4,7 @@ import { ResourceTypeEnum, TagResourceTypeEnum } from '@/common/commonEnum';
import { redisApi } from '../api'; import { redisApi } from '../api';
import { sleep } from '@/common/utils/loading'; import { sleep } from '@/common/utils/loading';
const RedisIcon = { export const RedisIcon = {
name: ResourceTypeEnum.Redis.extra.icon, name: ResourceTypeEnum.Redis.extra.icon,
color: ResourceTypeEnum.Redis.extra.iconColor, color: ResourceTypeEnum.Redis.extra.iconColor,
}; };

View File

@@ -2,7 +2,7 @@
<div <div
:id="props.node.key" :id="props.node.key"
class="w-full node-container flex items-center cursor-pointer select-none" class="w-full node-container flex items-center cursor-pointer select-none"
:class="props.data.type.nodeDblclickFunc ? 'select-none' : ''" :class="props.data.type?.nodeDblclickFunc ? 'select-none' : ''"
@mouseenter="showActions = true" @mouseenter="showActions = true"
@mouseleave="showActions = false" @mouseleave="showActions = false"
> >
@@ -55,7 +55,7 @@ import { ContextmenuItem } from '@/components/contextmenu';
import { ResourceOpCtx, TagTreeNode } from '@/views/ops/component/tag'; import { ResourceOpCtx, TagTreeNode } from '@/views/ops/component/tag';
import { ResourceOpCtxKey } from '@/views/ops/resource/resource'; import { ResourceOpCtxKey } from '@/views/ops/resource/resource';
const resourceOpCtx: ResourceOpCtx | undefined = inject(ResourceOpCtxKey); const resourceOpCtx: ResourceOpCtx | undefined = inject(ResourceOpCtxKey, undefined);
const props = defineProps({ const props = defineProps({
node: { node: {

View File

@@ -18,28 +18,18 @@
<slot name="iconPrefix" :node="node" :data="data" /> <slot name="iconPrefix" :node="node" :data="data" />
</template> </template>
<template #default="{ node, data }"> <template #default="{ node, data }">
<span> <component v-if="data.nodeComponent" :is="data.nodeComponent" :node="node" :data="data" />
<span v-if="data.type.value == TagTreeNode.TagPath"> <BaseTreeNode v-else :node="node" :data="data" />
<tag-info :tag-path="data.label" />
</span>
<slot v-else :node="node" :data="data" name="prefix"></slot>
<span class="ml-0.5" :title="data.labelRemark">
<slot name="label" :data="data"> {{ data.label }}</slot>
</span>
<slot :node="node" :data="data" name="suffix"></slot>
</span>
</template> </template>
</el-tree-select> </el-tree-select>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, reactive, ref, toRefs, watch } from 'vue'; import { onMounted, reactive, ref, toRefs, watch } from 'vue';
import { NodeType, TagTreeNode } from './tag';
import TagInfo from './TagInfo.vue'; import { NodeType, TagTreeNode } from '@/views/ops/component/tag';
import { tagApi } from '../tag/api'; import { tagApi } from '@/views/ops/tag/api';
import BaseTreeNode from '@/views/ops/resource/BaseTreeNode.vue';
const props = defineProps({ const props = defineProps({
resourceType: { resourceType: {

View File

@@ -16,8 +16,11 @@
<template #content> <template #content>
{{ $t('tag.tagTips1') }} {{ $t('tag.tagTips1') }}
<br /> <br />
{{ $t('tag.tagTips2') }} <br /> {{ $t('tag.tagTips2') }}
<br />
{{ $t('tag.tagTips3') }} {{ $t('tag.tagTips3') }}
<br />
{{ $t('tag.tagTips4') }}
</template> </template>
<SvgIcon class="ml-1" name="question-filled" /> <SvgIcon class="ml-1" name="question-filled" />
</el-tooltip> </el-tooltip>
@@ -37,10 +40,6 @@
@node-contextmenu="onNodeContextmenu" @node-contextmenu="onNodeContextmenu"
@node-click="onTreeNodeClick" @node-click="onTreeNodeClick"
:default-expanded-keys="defaultExpandedKeys" :default-expanded-keys="defaultExpandedKeys"
draggable
:allow-drop="allowDrop"
:allow-drag="allowDrag"
@node-drop="onNodeDrop"
:expand-on-click-node="false" :expand-on-click-node="false"
:filter-node-method="filterNode" :filter-node-method="filterNode"
> >
@@ -279,7 +278,7 @@ watch(filterTag, (val) => {
watch( watch(
() => state.currentTag, () => state.currentTag,
(val: any) => { (val: any) => {
if (val.type == TagResourceTypeEnum.Tag.value) { if (val?.type == TagResourceTypeEnum.Tag.value) {
tagApi.countTagResource.request({ tagPath: val.codePath }).then((res: any) => { tagApi.countTagResource.request({ tagPath: val.codePath }).then((res: any) => {
state.resourceCount = res; state.resourceCount = res;
}); });
@@ -289,82 +288,6 @@ watch(
} }
); );
const allowDrop = (draggingNode: any, dropNode: any, type: any) => {
// 不允许同层级移动
if (type != 'inner') {
return false;
}
const dropNodeData = dropNode.data;
const draggingNodeData = draggingNode.data;
const dropTagType = dropNodeData.type;
const draggingTagType = draggingNodeData.type;
// 目标节点只允许为标签类型
if (dropTagType != TagResourceTypeEnum.Tag.value) {
return false;
}
// 目标节点下没有子节点
if (!dropNodeData.children) {
// 都为标签类型允许移动
if (dropTagType == draggingTagType && dropTagType == TagResourceTypeEnum.Tag.value) {
return true;
}
// 目标节点为标签,允许移动
if (dropTagType == TagResourceTypeEnum.Tag.value) {
return true;
}
return false;
}
for (let child of dropNodeData.children) {
// 当前移动节点若在目标节点下有相同code则不允许移动
if (draggingNodeData.code == child.code) {
return false;
}
const childType = child.type;
// 移动节点非标签类型时(资源标签),并且子节点存在标签类型,则不允许移动,因为资源只允许放在叶子标签类型下
if (draggingTagType != TagResourceTypeEnum.Tag.value && childType == TagResourceTypeEnum.Tag.value) {
return false;
}
// 移动节点为标签类型时(资源标签),并且子节点存在资源类型,则不允许移动
if (draggingTagType == TagResourceTypeEnum.Tag.value && childType != TagResourceTypeEnum.Tag.value) {
return false;
}
}
return true;
};
const allowDrag = (node: any) => {
const tagType = node.data.type;
return (
tagType == TagResourceTypeEnum.Tag.value ||
tagType == TagResourceTypeEnum.DbInstance.value ||
tagType == TagResourceTypeEnum.Redis.value ||
tagType == TagResourceTypeEnum.Machine.value ||
tagType == TagResourceTypeEnum.Mongo.value
);
};
const onNodeDrop = async (draggingNode: any, dropNode: any) => {
const draggingData = draggingNode.data;
const dropData = dropNode.data;
try {
await tagApi.movingTag.request({
fromPath: draggingData.codePath,
toPath: dropData.codePath,
});
} finally {
search();
}
};
const onTabChange = () => { const onTabChange = () => {
setNowTabData(); setNowTabData();
}; };

View File

@@ -6,21 +6,21 @@ require (
gitee.com/chunanyong/dm v1.8.20 gitee.com/chunanyong/dm v1.8.20
gitee.com/liuzongyang/libpq v1.10.11 gitee.com/liuzongyang/libpq v1.10.11
github.com/antlr4-go/antlr/v4 v4.13.1 github.com/antlr4-go/antlr/v4 v4.13.1
github.com/docker/docker v28.4.0+incompatible github.com/docker/docker v28.5.0+incompatible
github.com/docker/go-connections v0.6.0 github.com/docker/go-connections v0.6.0
github.com/gin-gonic/gin v1.10.1 github.com/gin-gonic/gin v1.11.0
github.com/glebarez/sqlite v1.11.0 github.com/glebarez/sqlite v1.11.0
github.com/go-gormigrate/gormigrate/v2 v2.1.5 github.com/go-gormigrate/gormigrate/v2 v2.1.5
github.com/go-ldap/ldap/v3 v3.4.11 github.com/go-ldap/ldap/v3 v3.4.12
github.com/go-playground/locales v0.14.1 github.com/go-playground/locales v0.14.1
github.com/go-playground/universal-translator v0.18.1 github.com/go-playground/universal-translator v0.18.1
github.com/go-playground/validator/v10 v10.27.0 github.com/go-playground/validator/v10 v10.28.0
github.com/go-sql-driver/mysql v1.9.3 github.com/go-sql-driver/mysql v1.9.3
github.com/golang-jwt/jwt/v5 v5.3.0 github.com/golang-jwt/jwt/v5 v5.3.0
github.com/google/uuid v1.6.0 github.com/google/uuid v1.6.0
github.com/gorilla/websocket v1.5.3 github.com/gorilla/websocket v1.5.3
github.com/lionsoul2014/ip2region/binding/golang v0.0.0-20250630080345-f9402614f6ba github.com/lionsoul2014/ip2region/binding/golang v0.0.0-20250930013652-2d71241a3bb9
github.com/microsoft/go-mssqldb v1.9.2 github.com/microsoft/go-mssqldb v1.9.3
github.com/mojocn/base64Captcha v1.3.8 // github.com/mojocn/base64Captcha v1.3.8 //
github.com/opencontainers/image-spec v1.1.1 github.com/opencontainers/image-spec v1.1.1
github.com/pkg/errors v0.9.1 github.com/pkg/errors v0.9.1
@@ -49,10 +49,12 @@ require (
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/boombuler/barcode v1.1.0 // indirect github.com/boombuler/barcode v1.1.0 // indirect
github.com/bytedance/sonic v1.14.0 // indirect github.com/bytedance/gopkg v0.1.3 // indirect
github.com/bytedance/sonic v1.14.1 // indirect
github.com/bytedance/sonic/loader v0.3.0 // indirect github.com/bytedance/sonic/loader v0.3.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/cloudwego/base64x v0.1.5 // indirect github.com/clipperhouse/uax29/v2 v2.2.0 // indirect
github.com/cloudwego/base64x v0.1.6 // indirect
github.com/containerd/errdefs v1.0.0 // indirect github.com/containerd/errdefs v1.0.0 // indirect
github.com/containerd/errdefs/pkg v0.3.0 // indirect github.com/containerd/errdefs/pkg v0.3.0 // indirect
github.com/containerd/log v0.1.0 // indirect github.com/containerd/log v0.1.0 // indirect
@@ -62,13 +64,14 @@ require (
github.com/docker/go-units v0.5.0 // indirect github.com/docker/go-units v0.5.0 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect github.com/dustin/go-humanize v1.0.1 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/gabriel-vasile/mimetype v1.4.9 // indirect github.com/gabriel-vasile/mimetype v1.4.10 // indirect
github.com/gin-contrib/sse v1.1.0 // indirect github.com/gin-contrib/sse v1.1.0 // indirect
github.com/glebarez/go-sqlite v1.22.0 // indirect github.com/glebarez/go-sqlite v1.22.0 // indirect
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 // indirect github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 // indirect
github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect
github.com/goccy/go-json v0.10.5 // indirect github.com/goccy/go-json v0.10.5 // indirect
github.com/goccy/go-yaml v1.18.0 // indirect
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect
github.com/golang-sql/sqlexp v0.1.0 // indirect github.com/golang-sql/sqlexp v0.1.0 // indirect
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
@@ -81,7 +84,7 @@ require (
github.com/kr/fs v0.1.0 // indirect github.com/kr/fs v0.1.0 // indirect
github.com/leodido/go-urn v1.4.0 // indirect github.com/leodido/go-urn v1.4.0 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect github.com/mattn/go-runewidth v0.0.19 // indirect
github.com/moby/docker-image-spec v1.3.1 // indirect github.com/moby/docker-image-spec v1.3.1 // indirect
github.com/moby/sys/atomicwriter v0.1.0 // indirect github.com/moby/sys/atomicwriter v0.1.0 // indirect
github.com/moby/term v0.5.2 // indirect github.com/moby/term v0.5.2 // indirect
@@ -92,9 +95,10 @@ require (
github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/pelletier/go-toml/v2 v2.2.4 // indirect github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/quic-go/qpack v0.5.1 // indirect
github.com/quic-go/quic-go v0.55.0 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/rivo/uniseg v0.4.7 // indirect github.com/tidwall/match v1.2.0 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.1 // indirect github.com/tidwall/pretty v1.2.1 // indirect
github.com/tjfoc/gmsm v1.4.1 // indirect github.com/tjfoc/gmsm v1.4.1 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
@@ -103,24 +107,26 @@ require (
github.com/xdg-go/scram v1.1.2 // indirect github.com/xdg-go/scram v1.1.2 // indirect
github.com/xdg-go/stringprep v1.0.4 // indirect github.com/xdg-go/stringprep v1.0.4 // indirect
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 // indirect
go.opentelemetry.io/otel v1.37.0 // indirect go.opentelemetry.io/otel v1.38.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.37.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.37.0 // indirect
go.opentelemetry.io/otel/metric v1.37.0 // indirect go.opentelemetry.io/otel/metric v1.38.0 // indirect
go.opentelemetry.io/otel/sdk v1.37.0 // indirect go.opentelemetry.io/otel/trace v1.38.0 // indirect
go.opentelemetry.io/otel/trace v1.37.0 // indirect go.uber.org/mock v0.6.0 // indirect
golang.org/x/arch v0.19.0 // indirect golang.org/x/arch v0.21.0 // indirect
golang.org/x/exp v0.0.0-20250718183923-645b1fa84792 // indirect golang.org/x/exp v0.0.0-20251002181428-27f1f14c8bb9 // indirect
golang.org/x/image v0.29.0 // indirect golang.org/x/image v0.31.0 // indirect
golang.org/x/net v0.43.0 // indirect golang.org/x/mod v0.28.0 // indirect
golang.org/x/net v0.44.0 // indirect
golang.org/x/sys v0.36.0 // indirect golang.org/x/sys v0.36.0 // indirect
golang.org/x/text v0.29.0 // indirect golang.org/x/text v0.29.0 // indirect
google.golang.org/protobuf v1.36.6 // indirect golang.org/x/tools v0.37.0 // indirect
modernc.org/libc v1.66.4 // indirect google.golang.org/protobuf v1.36.10 // indirect
modernc.org/libc v1.66.10 // indirect
modernc.org/mathutil v1.7.1 // indirect modernc.org/mathutil v1.7.1 // indirect
modernc.org/memory v1.11.0 // indirect modernc.org/memory v1.11.0 // indirect
modernc.org/sqlite v1.38.1 // indirect modernc.org/sqlite v1.39.0 // indirect
) )
replace google.golang.org/genproto => google.golang.org/genproto v0.0.0-20250603155806-513f23925822 replace google.golang.org/genproto => google.golang.org/genproto v0.0.0-20250603155806-513f23925822

View File

@@ -370,9 +370,11 @@ func (app *dataSyncAppImpl) saveLog(log *entity.DataSyncLog) {
} }
func (app *dataSyncAppImpl) InitCronJob() { func (app *dataSyncAppImpl) InitCronJob() {
ctx := contextx.NewTraceId()
defer func() { defer func() {
if err := recover(); err != nil { if err := recover(); err != nil {
logx.ErrorTrace("the data synchronization task failed to initialize", err) logx.ErrorTraceContext(ctx, "the data synchronization task failed to initialize", err)
} }
}() }()
@@ -380,11 +382,11 @@ func (app *dataSyncAppImpl) InitCronJob() {
_ = app.UpdateByCond(context.TODO(), &entity.DataSyncTask{RunningState: entity.DataSyncTaskRunStateReady}, &entity.DataSyncTask{RunningState: entity.DataSyncTaskRunStateRunning}) _ = app.UpdateByCond(context.TODO(), &entity.DataSyncTask{RunningState: entity.DataSyncTaskRunStateReady}, &entity.DataSyncTask{RunningState: entity.DataSyncTaskRunStateRunning})
if err := app.CursorByCond(&entity.DataSyncTaskQuery{Status: entity.DataSyncTaskStatusEnable}, func(dst *entity.DataSyncTask) error { if err := app.CursorByCond(&entity.DataSyncTaskQuery{Status: entity.DataSyncTaskStatusEnable}, func(dst *entity.DataSyncTask) error {
app.addCronJob(contextx.NewTraceId(), dst)
app.MarkStop(dst.Id) app.MarkStop(dst.Id)
app.addCronJob(ctx, dst)
return nil return nil
}); err != nil { }); err != nil {
logx.ErrorTrace("the db data sync task failed to initialize: %v", err) logx.ErrorTraceContext(ctx, "the db data sync task failed to initialize: %v", err)
} }
} }
@@ -415,13 +417,13 @@ func (app *dataSyncAppImpl) addCronJob(ctx context.Context, taskEntity *entity.D
// 根据状态添加新的任务 // 根据状态添加新的任务
if taskEntity.Status == entity.DataSyncTaskStatusEnable { if taskEntity.Status == entity.DataSyncTaskStatusEnable {
taskId := taskEntity.Id taskId := taskEntity.Id
logx.Infof("start add the data sync task job: %s, cron[%s]", taskEntity.TaskName, taskEntity.TaskCron) logx.InfofContext(ctx, "start add the data sync task job: %s, cron[%s]", taskEntity.TaskName, taskEntity.TaskCron)
if err := scheduler.AddFunByKey(key, taskEntity.TaskCron, func() { if err := scheduler.AddFunByKey(key, taskEntity.TaskCron, func() {
if err := app.Run(context.Background(), taskId); err != nil { if err := app.Run(context.Background(), taskId); err != nil {
logx.Errorf("the data sync task failed to execute at a scheduled time: %s", err.Error()) logx.ErrorfContext(ctx, "the data sync task failed to execute at a scheduled time: %s", err.Error())
} }
}); err != nil { }); err != nil {
logx.ErrorTrace("add db data sync job failed", err) logx.ErrorTraceContext(ctx, "add db data sync job failed", err)
} }
} }
} }

View File

@@ -4,7 +4,7 @@ import "fmt"
const ( const (
AppName = "mayfly-go" AppName = "mayfly-go"
Version = "v1.10.3" Version = "v1.10.4"
) )
func GetAppInfo() string { func GetAppInfo() string {

View File

@@ -87,6 +87,7 @@ func (a *Account) GetPermissions(rc *req.Ctx) {
var resources vo.AccountResourceVOList var resources vo.AccountResourceVOList
// 获取账号菜单资源 // 获取账号菜单资源
biz.ErrIsNil(a.resourceApp.GetAccountResources(account.Id, &resources)) biz.ErrIsNil(a.resourceApp.GetAccountResources(account.Id, &resources))
biz.IsTrue(len(resources) > 0, "no permission")
// 菜单树与权限code数组 // 菜单树与权限code数组
var menus vo.AccountResourceVOList var menus vo.AccountResourceVOList
var permissions []string var permissions []string

View File

@@ -121,8 +121,8 @@ func Errorf(format string, args ...any) {
Log(context.Background(), slog.LevelError, fmt.Sprintf(format, args...)) Log(context.Background(), slog.LevelError, fmt.Sprintf(format, args...))
} }
// 错误记录并将堆栈信息添加至msg里默认记录10个堆栈信息 // ErrorTraceContext 错误记录并将堆栈信息添加至msg里默认记录10个堆栈信息
func ErrorTrace(msg string, err any) { func ErrorTraceContext(ctx context.Context, msg string, err any) {
errMsg := "" errMsg := ""
switch t := err.(type) { switch t := err.(type) {
case error: case error:
@@ -132,7 +132,12 @@ func ErrorTrace(msg string, err any) {
default: default:
errMsg = fmt.Sprintf("%v", t) errMsg = fmt.Sprintf("%v", t)
} }
Log(context.Background(), slog.LevelError, fmt.Sprintf(msg+"\n%s\n%s", errMsg, runtimex.StackStr(2, 20))) Log(ctx, slog.LevelError, fmt.Sprintf(msg+"\n%s\n%s", errMsg, runtimex.StackStr(2, 20)))
}
// ErrorTrace 错误记录并将堆栈信息添加至msg里默认记录10个堆栈信息
func ErrorTrace(msg string, err any) {
ErrorTraceContext(context.Background(), msg, err)
} }
func ErrorWithFields(ctx context.Context, msg string, mapFields map[string]any) { func ErrorWithFields(ctx context.Context, msg string, mapFields map[string]any) {

View File

@@ -49,8 +49,9 @@ func Error(bizerr *errorx.BizError) *Result {
} }
// 返回服务器错误Result // 返回服务器错误Result
func ServerError() *Result { func ServerError(msg string) *Result {
return Error(errorx.ServerError) serverErr := errorx.NewBizCode(errorx.ServerError.Code(), msg)
return Error(serverErr)
} }
func TokenError() *Result { func TokenError() *Result {

View File

@@ -1,7 +1,9 @@
package req package req
import ( import (
"cmp"
"context" "context"
"fmt"
"mayfly-go/pkg/biz" "mayfly-go/pkg/biz"
"mayfly-go/pkg/contextx" "mayfly-go/pkg/contextx"
"mayfly-go/pkg/errorx" "mayfly-go/pkg/errorx"
@@ -109,8 +111,8 @@ func (rc *Ctx) res() {
case *errorx.BizError: case *errorx.BizError:
rc.JSONRes(http.StatusOK, model.Error(t)) rc.JSONRes(http.StatusOK, model.Error(t))
default: default:
logx.ErrorTrace("服务器错误", t) logx.ErrorTrace("server error", t)
rc.JSONRes(http.StatusOK, model.ServerError()) rc.JSONRes(http.StatusOK, model.ServerError(fmt.Sprintf("server error [%d-%s]", errorx.ServerError.Code(), cmp.Or(contextx.GetTraceId(rc.MetaCtx), "none"))))
} }
return return
} }

View File

@@ -53,7 +53,7 @@ func Ip2Region(ip string) string {
} }
// 2、用全局的 vIndex 创建带 VectorIndex 缓存的查询对象。 // 2、用全局的 vIndex 创建带 VectorIndex 缓存的查询对象。
searcher, err := xdb.NewWithVectorIndex(ip2RegionDbPath, vectorIndex) searcher, err := xdb.NewWithVectorIndex(xdb.IPv4, ip2RegionDbPath, vectorIndex)
if err != nil { if err != nil {
logx.Errorf("failed to create searcher with vector index: %s\n", err) logx.Errorf("failed to create searcher with vector index: %s\n", err)
return "" return ""