diff --git a/mayfly_go_web/package.json b/mayfly_go_web/package.json index f52401cf..53a22a50 100644 --- a/mayfly_go_web/package.json +++ b/mayfly_go_web/package.json @@ -10,16 +10,16 @@ }, "dependencies": { "@element-plus/icons-vue": "^2.1.0", - "asciinema-player": "^3.3.0", + "asciinema-player": "^3.5.0", "axios": "^1.4.0", "countup.js": "^2.0.7", "cropperjs": "^1.5.11", "echarts": "^5.4.0", - "element-plus": "^2.3.7", + "element-plus": "^2.3.8", "jsencrypt": "^3.3.1", "lodash": "^4.17.21", - "mitt": "^3.0.0", - "monaco-editor": "^0.39.0", + "mitt": "^3.0.1", + "monaco-editor": "^0.40.0", "monaco-sql-languages": "^0.11.0", "monaco-themes": "^0.4.4", "nprogress": "^0.2.0", @@ -30,7 +30,7 @@ "sql-formatter": "^12.1.2", "vue": "^3.3.4", "vue-clipboard3": "^1.0.1", - "vue-router": "^4.2.2", + "vue-router": "^4.2.4", "xterm": "^5.2.1", "xterm-addon-fit": "^0.7.0" }, @@ -50,7 +50,7 @@ "sass": "^1.62.0", "sass-loader": "^13.2.0", "typescript": "^5.0.2", - "vite": "^4.3.9", + "vite": "^4.4.2", "vue-eslint-parser": "^9.1.1" }, "browserslist": [ diff --git a/mayfly_go_web/src/common/config.ts b/mayfly_go_web/src/common/config.ts index 361623b3..de294290 100644 --- a/mayfly_go_web/src/common/config.ts +++ b/mayfly_go_web/src/common/config.ts @@ -11,7 +11,7 @@ const config = { baseWsUrl: `${(window as any).globalConfig.BaseWsUrl || `${location.protocol == 'https:' ? 'wss:' : 'ws:'}//${getBaseApiUrl()}`}/api`, // 系统版本 - version: 'v1.4.4', + version: 'v1.5.0', }; export default config; diff --git a/mayfly_go_web/src/components/pagetable/PageTable.vue b/mayfly_go_web/src/components/pagetable/PageTable.vue index ab2e4092..97a1e913 100644 --- a/mayfly_go_web/src/components/pagetable/PageTable.vue +++ b/mayfly_go_web/src/components/pagetable/PageTable.vue @@ -130,11 +130,40 @@ + @@ -234,6 +263,7 @@ const state = reactive({ loadingData: false, // 输入框宽度 inputWidth: "200px" as any, + formatVal: '', // 格式化后的值 }) const { @@ -243,6 +273,7 @@ const { queryForm, loadingData, inputWidth, + formatVal, } = toRefs(state) watch(() => props.queryForm, (newValue: any) => { @@ -283,6 +314,15 @@ onMounted(() => { } }) +const formatText = (data: any)=> { + state.formatVal = ''; + try { + state.formatVal = JSON.stringify(JSON.parse(data), null, 4); + } catch (e) { + state.formatVal = data; + } +} + const getRowQueryItem = (row: number) => { // 第一行需要加个查询等按钮列 if (row === 1) { diff --git a/mayfly_go_web/src/components/pagetable/index.ts b/mayfly_go_web/src/components/pagetable/index.ts index fa726810..a5d32fda 100644 --- a/mayfly_go_web/src/components/pagetable/index.ts +++ b/mayfly_go_web/src/components/pagetable/index.ts @@ -68,6 +68,11 @@ export class TableColumn { */ show: boolean = true; + /** + * 是否展示美化按钮(主要用于美化json文本等) + */ + isBeautify: boolean = false; + constructor(prop: string, label: string) { this.prop = prop; this.label = label; @@ -125,6 +130,16 @@ export class TableColumn { return this; } + typeText(): TableColumn { + this.type = 'text'; + return this; + } + + typeJson(): TableColumn { + this.type = 'jsonText'; + return this; + } + /** * 标识该列为插槽 * @returns this @@ -176,6 +191,11 @@ export class TableColumn { return this; } + canBeautify(): TableColumn { + this.isBeautify = true; + return this; + } + /** * 自动计算最小宽度 * @param str 字符串 diff --git a/mayfly_go_web/src/views/ops/db/DbList.vue b/mayfly_go_web/src/views/ops/db/DbList.vue index 1df8c935..477be69c 100644 --- a/mayfly_go_web/src/views/ops/db/DbList.vue +++ b/mayfly_go_web/src/views/ops/db/DbList.vue @@ -278,7 +278,6 @@ import config from '@/common/config'; import { getSession } from '@/common/utils/storage'; import { isTrue } from '@/common/assert'; import { Search as SearchIcon } from '@element-plus/icons-vue'; -import { tagApi } from '../tag/api'; import { dateFormat } from '@/common/utils/date'; import TagInfo from '../component/TagInfo.vue'; import PageTable from '@/components/pagetable/PageTable.vue'; @@ -354,8 +353,8 @@ const state = reactive({ TableColumn.new('table', '表'), TableColumn.new('type', '类型').typeTag(DbSqlExecTypeEnum).setAddWidth(10), TableColumn.new('creator', '执行人'), - TableColumn.new('sql', 'SQL'), - TableColumn.new('oldValue', '原值'), + TableColumn.new('sql', 'SQL').canBeautify(), + TableColumn.new('oldValue', '原值').canBeautify(), TableColumn.new('createTime', '执行时间').isTime(), TableColumn.new('remark', '备注'), TableColumn.new('action', '操作').isSlot().setMinWidth(100).fixedRight().alignCenter(), @@ -496,7 +495,7 @@ const showInfo = (info: any) => { }; const getTags = async () => { - state.tags = await tagApi.getAccountTags.request(null); + state.tags = await dbApi.dbTags.request(null); }; const editDb = async (data: any) => { diff --git a/mayfly_go_web/src/views/ops/db/api.ts b/mayfly_go_web/src/views/ops/db/api.ts index 5a4d5889..4d03e9db 100644 --- a/mayfly_go_web/src/views/ops/db/api.ts +++ b/mayfly_go_web/src/views/ops/db/api.ts @@ -3,6 +3,7 @@ import Api from '@/common/Api'; export const dbApi = { // 获取权限列表 dbs: Api.newGet('/dbs'), + dbTags: Api.newGet('/dbs/tags'), saveDb: Api.newPost('/dbs'), getAllDatabase: Api.newPost('/dbs/databases'), getDbPwd: Api.newGet('/dbs/{id}/pwd'), diff --git a/mayfly_go_web/src/views/ops/machine/MachineList.vue b/mayfly_go_web/src/views/ops/machine/MachineList.vue index d8610d78..c8915150 100644 --- a/mayfly_go_web/src/views/ops/machine/MachineList.vue +++ b/mayfly_go_web/src/views/ops/machine/MachineList.vue @@ -150,7 +150,6 @@ import { ref, toRefs, reactive, onMounted, defineAsyncComponent } from 'vue'; import { useRouter } from 'vue-router'; import { ElMessage, ElMessageBox } from 'element-plus'; import { machineApi } from './api'; -import { tagApi } from '../tag/api'; import { dateFormat } from '@/common/utils/date'; import TagInfo from '../component/TagInfo.vue'; import PageTable from '@/components/pagetable/PageTable.vue'; @@ -300,7 +299,7 @@ const closeCli = async (row: any) => { }; const getTags = async () => { - state.tags = await tagApi.getAccountTags.request(null); + state.tags = await machineApi.tagList.request(null); }; const openFormDialog = async (machine: any) => { diff --git a/mayfly_go_web/src/views/ops/machine/api.ts b/mayfly_go_web/src/views/ops/machine/api.ts index 5378227c..7f9283bf 100644 --- a/mayfly_go_web/src/views/ops/machine/api.ts +++ b/mayfly_go_web/src/views/ops/machine/api.ts @@ -3,6 +3,7 @@ import Api from '@/common/Api'; export const machineApi = { // 获取权限列表 list: Api.newGet('/machines'), + tagList: Api.newGet('/machines/tags'), getMachinePwd: Api.newGet('/machines/{id}/pwd'), info: Api.newGet('/machines/{id}/sysinfo'), stats: Api.newGet('/machines/{id}/stats'), @@ -46,3 +47,12 @@ export const authCertApi = { save: Api.newPost('/sys/authcerts'), delete: Api.newDelete('/sys/authcerts/{id}'), }; + +export const cronJobApi = { + list: Api.newGet('/machine-cronjobs'), + relateMachineIds: Api.newGet('/machine-cronjobs/machine-ids'), + relateCronJobIds: Api.newGet('/machine-cronjobs/cronjob-ids'), + save: Api.newPost('/machine-cronjobs'), + delete: Api.newDelete('/machine-cronjobs/{id}'), + execList: Api.newGet('/machine-cronjobs/execs'), +}; diff --git a/mayfly_go_web/src/views/ops/machine/cronjob/CronJobEdit.vue b/mayfly_go_web/src/views/ops/machine/cronjob/CronJobEdit.vue new file mode 100644 index 00000000..e21af1eb --- /dev/null +++ b/mayfly_go_web/src/views/ops/machine/cronjob/CronJobEdit.vue @@ -0,0 +1,189 @@ + + + + diff --git a/mayfly_go_web/src/views/ops/machine/cronjob/CronJobExecList.vue b/mayfly_go_web/src/views/ops/machine/cronjob/CronJobExecList.vue new file mode 100644 index 00000000..25ed24ec --- /dev/null +++ b/mayfly_go_web/src/views/ops/machine/cronjob/CronJobExecList.vue @@ -0,0 +1,166 @@ + + + + + diff --git a/mayfly_go_web/src/views/ops/machine/cronjob/CronJobList.vue b/mayfly_go_web/src/views/ops/machine/cronjob/CronJobList.vue new file mode 100644 index 00000000..540fee43 --- /dev/null +++ b/mayfly_go_web/src/views/ops/machine/cronjob/CronJobList.vue @@ -0,0 +1,150 @@ + + + + + diff --git a/mayfly_go_web/src/views/ops/machine/enums.ts b/mayfly_go_web/src/views/ops/machine/enums.ts index 3e6a1a2f..345d7d15 100644 --- a/mayfly_go_web/src/views/ops/machine/enums.ts +++ b/mayfly_go_web/src/views/ops/machine/enums.ts @@ -24,3 +24,22 @@ export const AuthMethodEnum = { Password: EnumValue.of(1, '密码').tagTypeSuccess(), PrivateKey: EnumValue.of(2, '秘钥'), }; + +// 计划任务状态 +export const CronJobStatusEnum = { + Enable: EnumValue.of(1, '启用').tagTypeSuccess(), + Disable: EnumValue.of(-1, '禁用').tagTypeDanger(), +}; + +// 计划任务保存执行结果类型 +export const CronJobSaveExecResTypeEnum = { + No: EnumValue.of(-1, '不记录').tagTypeDanger(), + OnError: EnumValue.of(1, '错误时记录').tagTypeWarning(), + Yes: EnumValue.of(2, '记录').tagTypeSuccess(), +}; + +// 计划任务执行记录状态 +export const CronJobExecStatusEnum = { + Error: EnumValue.of(-1, '错误').tagTypeDanger(), + Success: EnumValue.of(1, '成功').tagTypeSuccess(), +}; diff --git a/mayfly_go_web/src/views/ops/mongo/MongoList.vue b/mayfly_go_web/src/views/ops/mongo/MongoList.vue index 51a8ab59..884184fb 100644 --- a/mayfly_go_web/src/views/ops/mongo/MongoList.vue +++ b/mayfly_go_web/src/views/ops/mongo/MongoList.vue @@ -178,7 +178,6 @@ import { mongoApi } from './api'; import { ref, toRefs, reactive, onMounted } from 'vue'; import { ElMessage, ElMessageBox } from 'element-plus'; -import { tagApi } from '../tag/api'; import MongoEdit from './MongoEdit.vue'; import { formatByteSize } from '@/common/utils/format'; import TagInfo from '../component/TagInfo.vue'; @@ -363,7 +362,7 @@ const search = async () => { }; const getTags = async () => { - state.tags = await tagApi.getAccountTags.request(null); + state.tags = await mongoApi.mongoTags.request(null); }; const editMongo = async (data: any) => { diff --git a/mayfly_go_web/src/views/ops/mongo/api.ts b/mayfly_go_web/src/views/ops/mongo/api.ts index 81ad27cf..0f0fc8aa 100644 --- a/mayfly_go_web/src/views/ops/mongo/api.ts +++ b/mayfly_go_web/src/views/ops/mongo/api.ts @@ -2,6 +2,7 @@ import Api from '@/common/Api'; export const mongoApi = { mongoList: Api.newGet('/mongos'), + mongoTags: Api.newGet('/mongos/tags'), saveMongo: Api.newPost('/mongos'), deleteMongo: Api.newDelete('/mongos/{id}'), databases: Api.newGet('/mongos/{id}/databases'), diff --git a/mayfly_go_web/src/views/ops/redis/RedisEdit.vue b/mayfly_go_web/src/views/ops/redis/RedisEdit.vue index efc708f7..a33ebd70 100644 --- a/mayfly_go_web/src/views/ops/redis/RedisEdit.vue +++ b/mayfly_go_web/src/views/ops/redis/RedisEdit.vue @@ -25,6 +25,9 @@ type="textarea" > + + + { }; const getTags = async () => { - state.tags = await tagApi.getAccountTags.request(null); + state.tags = await redisApi.redisTags.request(null); }; const editRedis = async (data: any) => { diff --git a/mayfly_go_web/src/views/ops/redis/api.ts b/mayfly_go_web/src/views/ops/redis/api.ts index 815b033d..702d0104 100644 --- a/mayfly_go_web/src/views/ops/redis/api.ts +++ b/mayfly_go_web/src/views/ops/redis/api.ts @@ -2,6 +2,7 @@ import Api from '@/common/Api'; export const redisApi = { redisList: Api.newGet('/redis'), + redisTags: Api.newGet('/redis/tags'), getRedisPwd: Api.newGet('/redis/{id}/pwd'), redisInfo: Api.newGet('/redis/{id}/info'), clusterInfo: Api.newGet('/redis/{id}/cluster-info'), diff --git a/mayfly_go_web/src/views/system/config/ConfigList.vue b/mayfly_go_web/src/views/system/config/ConfigList.vue index 23564917..87fc0aca 100755 --- a/mayfly_go_web/src/views/system/config/ConfigList.vue +++ b/mayfly_go_web/src/views/system/config/ConfigList.vue @@ -80,7 +80,7 @@ const perms = { const columns = ref([ TableColumn.new('name', '配置项'), TableColumn.new('key', '配置key'), - TableColumn.new('value', '配置值'), + TableColumn.new('value', '配置值').canBeautify(), TableColumn.new('remark', '备注'), TableColumn.new('modifier', '更新账号'), TableColumn.new('updateTime', '更新时间').isTime(), diff --git a/mayfly_go_web/src/views/system/syslog/SyslogList.vue b/mayfly_go_web/src/views/system/syslog/SyslogList.vue index 4b641538..09ad77d4 100755 --- a/mayfly_go_web/src/views/system/syslog/SyslogList.vue +++ b/mayfly_go_web/src/views/system/syslog/SyslogList.vue @@ -55,7 +55,7 @@ const state = reactive({ TableColumn.new('createTime', '操作时间').isTime(), TableColumn.new('type', '结果').typeTag(LogTypeEnum), TableColumn.new('description', '描述'), - TableColumn.new('reqParam', '操作信息'), + TableColumn.new('reqParam', '操作信息').canBeautify(), TableColumn.new('resp', '响应信息'), ], total: 0, diff --git a/mayfly_go_web/yarn.lock b/mayfly_go_web/yarn.lock index 6bac575f..772d80af 100644 --- a/mayfly_go_web/yarn.lock +++ b/mayfly_go_web/yarn.lock @@ -34,115 +34,115 @@ resolved "https://registry.npmmirror.com/@element-plus/icons-vue/-/icons-vue-2.1.0.tgz#7ad90d08a8c0d5fd3af31c4f73264ca89614397a" integrity sha512-PSBn3elNoanENc1vnCfh+3WA9fimRC7n+fWkf3rE5jvv+aBohNHABC/KAR5KWPecxWxDTVT1ERpRbOMRcOV/vA== -"@esbuild/android-arm64@0.17.12": - version "0.17.12" - resolved "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.17.12.tgz#15a8e2b407d03989b899e325151dc2e96d19c620" - integrity sha512-WQ9p5oiXXYJ33F2EkE3r0FRDFVpEdcDiwNX3u7Xaibxfx6vQE0Sb8ytrfQsA5WO6kDn6mDfKLh6KrPBjvkk7xA== +"@esbuild/android-arm64@0.18.11": + version "0.18.11" + resolved "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.18.11.tgz#fa6f0cc7105367cb79cc0a8bf32bf50cb1673e45" + integrity sha512-snieiq75Z1z5LJX9cduSAjUr7vEI1OdlzFPMw0HH5YI7qQHDd3qs+WZoMrWYDsfRJSq36lIA6mfZBkvL46KoIw== -"@esbuild/android-arm@0.17.12": - version "0.17.12" - resolved "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.17.12.tgz#677a09297e1f4f37aba7b4fc4f31088b00484985" - integrity sha512-E/sgkvwoIfj4aMAPL2e35VnUJspzVYl7+M1B2cqeubdBhADV4uPon0KCc8p2G+LqSJ6i8ocYPCqY3A4GGq0zkQ== +"@esbuild/android-arm@0.18.11": + version "0.18.11" + resolved "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.18.11.tgz#ae84a410696c9f549a15be94eaececb860bacacb" + integrity sha512-q4qlUf5ucwbUJZXF5tEQ8LF7y0Nk4P58hOsGk3ucY0oCwgQqAnqXVbUuahCddVHfrxmpyewRpiTHwVHIETYu7Q== -"@esbuild/android-x64@0.17.12": - version "0.17.12" - resolved "https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.17.12.tgz#b292729eef4e0060ae1941f6a021c4d2542a3521" - integrity sha512-m4OsaCr5gT+se25rFPHKQXARMyAehHTQAz4XX1Vk3d27VtqiX0ALMBPoXZsGaB6JYryCLfgGwUslMqTfqeLU0w== +"@esbuild/android-x64@0.18.11": + version "0.18.11" + resolved "https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.18.11.tgz#0e58360bbc789ad0d68174d32ba20e678c2a16b6" + integrity sha512-iPuoxQEV34+hTF6FT7om+Qwziv1U519lEOvekXO9zaMMlT9+XneAhKL32DW3H7okrCOBQ44BMihE8dclbZtTuw== -"@esbuild/darwin-arm64@0.17.12": - version "0.17.12" - resolved "https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.17.12.tgz#efa35318df931da05825894e1787b976d55adbe3" - integrity sha512-O3GCZghRIx+RAN0NDPhyyhRgwa19MoKlzGonIb5hgTj78krqp9XZbYCvFr9N1eUxg0ZQEpiiZ4QvsOQwBpP+lg== +"@esbuild/darwin-arm64@0.18.11": + version "0.18.11" + resolved "https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.18.11.tgz#fcdcd2ef76ca656540208afdd84f284072f0d1f9" + integrity sha512-Gm0QkI3k402OpfMKyQEEMG0RuW2LQsSmI6OeO4El2ojJMoF5NLYb3qMIjvbG/lbMeLOGiW6ooU8xqc+S0fgz2w== -"@esbuild/darwin-x64@0.17.12": - version "0.17.12" - resolved "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.17.12.tgz#e7b54bb3f6dc81aadfd0485cd1623c648157e64d" - integrity sha512-5D48jM3tW27h1qjaD9UNRuN+4v0zvksqZSPZqeSWggfMlsVdAhH3pwSfQIFJwcs9QJ9BRibPS4ViZgs3d2wsCA== +"@esbuild/darwin-x64@0.18.11": + version "0.18.11" + resolved "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.18.11.tgz#c5ac602ec0504a8ff81e876bc8a9811e94d69d37" + integrity sha512-N15Vzy0YNHu6cfyDOjiyfJlRJCB/ngKOAvoBf1qybG3eOq0SL2Lutzz9N7DYUbb7Q23XtHPn6lMDF6uWbGv9Fw== -"@esbuild/freebsd-arm64@0.17.12": - version "0.17.12" - resolved "https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.12.tgz#99a18a8579d6299c449566fe91d9b6a54cf2a591" - integrity sha512-OWvHzmLNTdF1erSvrfoEBGlN94IE6vCEaGEkEH29uo/VoONqPnoDFfShi41Ew+yKimx4vrmmAJEGNoyyP+OgOQ== +"@esbuild/freebsd-arm64@0.18.11": + version "0.18.11" + resolved "https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.11.tgz#7012fb06ee3e6e0d5560664a65f3fefbcc46db2e" + integrity sha512-atEyuq6a3omEY5qAh5jIORWk8MzFnCpSTUruBgeyN9jZq1K/QI9uke0ATi3MHu4L8c59CnIi4+1jDKMuqmR71A== -"@esbuild/freebsd-x64@0.17.12": - version "0.17.12" - resolved "https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.17.12.tgz#0e090190fede307fb4022f671791a50dd5121abd" - integrity sha512-A0Xg5CZv8MU9xh4a+7NUpi5VHBKh1RaGJKqjxe4KG87X+mTjDE6ZvlJqpWoeJxgfXHT7IMP9tDFu7IZ03OtJAw== +"@esbuild/freebsd-x64@0.18.11": + version "0.18.11" + resolved "https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.18.11.tgz#c5de1199f70e1f97d5c8fca51afa9bf9a2af5969" + integrity sha512-XtuPrEfBj/YYYnAAB7KcorzzpGTvOr/dTtXPGesRfmflqhA4LMF0Gh/n5+a9JBzPuJ+CGk17CA++Hmr1F/gI0Q== -"@esbuild/linux-arm64@0.17.12": - version "0.17.12" - resolved "https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.17.12.tgz#7fe2a69f8a1a7153fa2b0f44aabcadb59475c7e0" - integrity sha512-cK3AjkEc+8v8YG02hYLQIQlOznW+v9N+OI9BAFuyqkfQFR+DnDLhEM5N8QRxAUz99cJTo1rLNXqRrvY15gbQUg== +"@esbuild/linux-arm64@0.18.11": + version "0.18.11" + resolved "https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.18.11.tgz#2a6d3a74e0b8b5f294e22b4515b29f76ebd42660" + integrity sha512-c6Vh2WS9VFKxKZ2TvJdA7gdy0n6eSy+yunBvv4aqNCEhSWVor1TU43wNRp2YLO9Vng2G+W94aRz+ILDSwAiYog== -"@esbuild/linux-arm@0.17.12": - version "0.17.12" - resolved "https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.17.12.tgz#b87c76ebf1fe03e01fd6bb5cfc2f3c5becd5ee93" - integrity sha512-WsHyJ7b7vzHdJ1fv67Yf++2dz3D726oO3QCu8iNYik4fb5YuuReOI9OtA+n7Mk0xyQivNTPbl181s+5oZ38gyA== +"@esbuild/linux-arm@0.18.11": + version "0.18.11" + resolved "https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.18.11.tgz#5175bd61b793b436e4aece6328aa0d9be07751e1" + integrity sha512-Idipz+Taso/toi2ETugShXjQ3S59b6m62KmLHkJlSq/cBejixmIydqrtM2XTvNCywFl3VC7SreSf6NV0i6sRyg== -"@esbuild/linux-ia32@0.17.12": - version "0.17.12" - resolved "https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.17.12.tgz#9e9357090254524d32e6708883a47328f3037858" - integrity sha512-jdOBXJqcgHlah/nYHnj3Hrnl9l63RjtQ4vn9+bohjQPI2QafASB5MtHAoEv0JQHVb/xYQTFOeuHnNYE1zF7tYw== +"@esbuild/linux-ia32@0.18.11": + version "0.18.11" + resolved "https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.18.11.tgz#20ee6cfd65a398875f321a485e7b2278e5f6f67b" + integrity sha512-S3hkIF6KUqRh9n1Q0dSyYcWmcVa9Cg+mSoZEfFuzoYXXsk6196qndrM+ZiHNwpZKi3XOXpShZZ+9dfN5ykqjjw== -"@esbuild/linux-loong64@0.17.12": - version "0.17.12" - resolved "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.17.12.tgz#9deb605f9e2c82f59412ddfefb4b6b96d54b5b5b" - integrity sha512-GTOEtj8h9qPKXCyiBBnHconSCV9LwFyx/gv3Phw0pa25qPYjVuuGZ4Dk14bGCfGX3qKF0+ceeQvwmtI+aYBbVA== +"@esbuild/linux-loong64@0.18.11": + version "0.18.11" + resolved "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.18.11.tgz#8e7b251dede75083bf44508dab5edce3f49d052b" + integrity sha512-MRESANOoObQINBA+RMZW+Z0TJWpibtE7cPFnahzyQHDCA9X9LOmGh68MVimZlM9J8n5Ia8lU773te6O3ILW8kw== -"@esbuild/linux-mips64el@0.17.12": - version "0.17.12" - resolved "https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.17.12.tgz#6ef170b974ddf5e6acdfa5b05f22b6e9dfd2b003" - integrity sha512-o8CIhfBwKcxmEENOH9RwmUejs5jFiNoDw7YgS0EJTF6kgPgcqLFjgoc5kDey5cMHRVCIWc6kK2ShUePOcc7RbA== +"@esbuild/linux-mips64el@0.18.11": + version "0.18.11" + resolved "https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.18.11.tgz#a3125eb48538ac4932a9d05089b157f94e443165" + integrity sha512-qVyPIZrXNMOLYegtD1u8EBccCrBVshxMrn5MkuFc3mEVsw7CCQHaqZ4jm9hbn4gWY95XFnb7i4SsT3eflxZsUg== -"@esbuild/linux-ppc64@0.17.12": - version "0.17.12" - resolved "https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.17.12.tgz#1638d3d4acf1d34aaf37cf8908c2e1cefed16204" - integrity sha512-biMLH6NR/GR4z+ap0oJYb877LdBpGac8KfZoEnDiBKd7MD/xt8eaw1SFfYRUeMVx519kVkAOL2GExdFmYnZx3A== +"@esbuild/linux-ppc64@0.18.11": + version "0.18.11" + resolved "https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.18.11.tgz#842abadb7a0995bd539adee2be4d681b68279499" + integrity sha512-T3yd8vJXfPirZaUOoA9D2ZjxZX4Gr3QuC3GztBJA6PklLotc/7sXTOuuRkhE9W/5JvJP/K9b99ayPNAD+R+4qQ== -"@esbuild/linux-riscv64@0.17.12": - version "0.17.12" - resolved "https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.17.12.tgz#135b6e9270a8e2de2b9094bb21a287517df520ef" - integrity sha512-jkphYUiO38wZGeWlfIBMB72auOllNA2sLfiZPGDtOBb1ELN8lmqBrlMiucgL8awBw1zBXN69PmZM6g4yTX84TA== +"@esbuild/linux-riscv64@0.18.11": + version "0.18.11" + resolved "https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.18.11.tgz#7ce6e6cee1c72d5b4d2f4f8b6fcccf4a9bea0e28" + integrity sha512-evUoRPWiwuFk++snjH9e2cAjF5VVSTj+Dnf+rkO/Q20tRqv+644279TZlPK8nUGunjPAtQRCj1jQkDAvL6rm2w== -"@esbuild/linux-s390x@0.17.12": - version "0.17.12" - resolved "https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.17.12.tgz#21e40830770c5d08368e300842bde382ce97d615" - integrity sha512-j3ucLdeY9HBcvODhCY4b+Ds3hWGO8t+SAidtmWu/ukfLLG/oYDMaA+dnugTVAg5fnUOGNbIYL9TOjhWgQB8W5g== +"@esbuild/linux-s390x@0.18.11": + version "0.18.11" + resolved "https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.18.11.tgz#98fbc794363d02ded07d300df2e535650b297b96" + integrity sha512-/SlRJ15XR6i93gRWquRxYCfhTeC5PdqEapKoLbX63PLCmAkXZHY2uQm2l9bN0oPHBsOw2IswRZctMYS0MijFcg== -"@esbuild/linux-x64@0.17.12": - version "0.17.12" - resolved "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.17.12.tgz#76c1c199871d48e1aaa47a762fb9e0dca52e1f7a" - integrity sha512-uo5JL3cgaEGotaqSaJdRfFNSCUJOIliKLnDGWaVCgIKkHxwhYMm95pfMbWZ9l7GeW9kDg0tSxcy9NYdEtjwwmA== +"@esbuild/linux-x64@0.18.11": + version "0.18.11" + resolved "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.18.11.tgz#f8458ec8cf74c8274e4cacd00744d8446cac52eb" + integrity sha512-xcncej+wF16WEmIwPtCHi0qmx1FweBqgsRtEL1mSHLFR6/mb3GEZfLQnx+pUDfRDEM4DQF8dpXIW7eDOZl1IbA== -"@esbuild/netbsd-x64@0.17.12": - version "0.17.12" - resolved "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.17.12.tgz#c7c3b3017a4b938c76c35f66af529baf62eac527" - integrity sha512-DNdoRg8JX+gGsbqt2gPgkgb00mqOgOO27KnrWZtdABl6yWTST30aibGJ6geBq3WM2TIeW6COs5AScnC7GwtGPg== +"@esbuild/netbsd-x64@0.18.11": + version "0.18.11" + resolved "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.18.11.tgz#a7b2f991b8293748a7be42eac1c4325faf0c7cca" + integrity sha512-aSjMHj/F7BuS1CptSXNg6S3M4F3bLp5wfFPIJM+Km2NfIVfFKhdmfHF9frhiCLIGVzDziggqWll0B+9AUbud/Q== -"@esbuild/openbsd-x64@0.17.12": - version "0.17.12" - resolved "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.17.12.tgz#05d04217d980e049001afdbeacbb58d31bb5cefb" - integrity sha512-aVsENlr7B64w8I1lhHShND5o8cW6sB9n9MUtLumFlPhG3elhNWtE7M1TFpj3m7lT3sKQUMkGFjTQBrvDDO1YWA== +"@esbuild/openbsd-x64@0.18.11": + version "0.18.11" + resolved "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.18.11.tgz#3e50923de84c54008f834221130fd23646072b2f" + integrity sha512-tNBq+6XIBZtht0xJGv7IBB5XaSyvYPCm1PxJ33zLQONdZoLVM0bgGqUrXnJyiEguD9LU4AHiu+GCXy/Hm9LsdQ== -"@esbuild/sunos-x64@0.17.12": - version "0.17.12" - resolved "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.17.12.tgz#cf3862521600e4eb6c440ec3bad31ed40fb87ef3" - integrity sha512-qbHGVQdKSwi0JQJuZznS4SyY27tYXYF0mrgthbxXrZI3AHKuRvU+Eqbg/F0rmLDpW/jkIZBlCO1XfHUBMNJ1pg== +"@esbuild/sunos-x64@0.18.11": + version "0.18.11" + resolved "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.18.11.tgz#ae47a550b0cd395de03606ecfba03cc96c7c19e2" + integrity sha512-kxfbDOrH4dHuAAOhr7D7EqaYf+W45LsAOOhAet99EyuxxQmjbk8M9N4ezHcEiCYPaiW8Dj3K26Z2V17Gt6p3ng== -"@esbuild/win32-arm64@0.17.12": - version "0.17.12" - resolved "https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.17.12.tgz#43dd7fb5be77bf12a1550355ab2b123efd60868e" - integrity sha512-zsCp8Ql+96xXTVTmm6ffvoTSZSV2B/LzzkUXAY33F/76EajNw1m+jZ9zPfNJlJ3Rh4EzOszNDHsmG/fZOhtqDg== +"@esbuild/win32-arm64@0.18.11": + version "0.18.11" + resolved "https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.18.11.tgz#05d364582b7862d7fbf4698ef43644f7346dcfcc" + integrity sha512-Sh0dDRyk1Xi348idbal7lZyfSkjhJsdFeuC13zqdipsvMetlGiFQNdO+Yfp6f6B4FbyQm7qsk16yaZk25LChzg== -"@esbuild/win32-ia32@0.17.12": - version "0.17.12" - resolved "https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.17.12.tgz#9940963d0bff4ea3035a84e2b4c6e41c5e6296eb" - integrity sha512-FfrFjR4id7wcFYOdqbDfDET3tjxCozUgbqdkOABsSFzoZGFC92UK7mg4JKRc/B3NNEf1s2WHxJ7VfTdVDPN3ng== +"@esbuild/win32-ia32@0.18.11": + version "0.18.11" + resolved "https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.18.11.tgz#a3372095a4a1939da672156a3c104f8ce85ee616" + integrity sha512-o9JUIKF1j0rqJTFbIoF4bXj6rvrTZYOrfRcGyL0Vm5uJ/j5CkBD/51tpdxe9lXEDouhRgdr/BYzUrDOvrWwJpg== -"@esbuild/win32-x64@0.17.12": - version "0.17.12" - resolved "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.17.12.tgz#3a11d13e9a5b0c05db88991b234d8baba1f96487" - integrity sha512-JOOxw49BVZx2/5tW3FqkdjSD/5gXYeVGPDcB0lvap0gLQshkh1Nyel1QazC+wNxus3xPlsYAgqU1BUmrmCvWtw== +"@esbuild/win32-x64@0.18.11": + version "0.18.11" + resolved "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.18.11.tgz#6526c7e1b40d5b9f0a222c6b767c22f6fb97aa57" + integrity sha512-rQI4cjLHd2hGsM1LqgDI7oOCYbQ6IBOVsX9ejuRMSze0GqXUG2ekwiKkiBU1pRGSeCqFFHxTrcEydB2Hyoz9CA== "@eslint/eslintrc@^2.0.0": version "2.0.0" @@ -583,10 +583,10 @@ array-union@^2.1.0: resolved "https://registry.npm.taobao.org/array-union/download/array-union-2.1.0.tgz?cache=0&sync_timestamp=1614624262896&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Farray-union%2Fdownload%2Farray-union-2.1.0.tgz" integrity sha1-t5hCCtvrHego2ErNii4j0+/oXo0= -asciinema-player@^3.3.0: - version "3.3.0" - resolved "https://registry.npmmirror.com/asciinema-player/-/asciinema-player-3.3.0.tgz#45616fa8dc3950c2be12b51d0a365ea2b9a845c2" - integrity sha512-4uyCGe83+5gZ06jgIGyV4vl0TS3egBgW0NXyCpDuDDvmzDGHEG8OICChrTecmTvajgLyq8YQet9nI7SYkVt8vQ== +asciinema-player@^3.5.0: + version "3.5.0" + resolved "https://registry.npmmirror.com/asciinema-player/-/asciinema-player-3.5.0.tgz#a4d1c01b56b72dfb6834e9ff90fee5c9652c7dae" + integrity sha512-o4B2AscBuCZo4+JB9TBGrfZ7GQL99wsbm08WwmuNJTPd1lyLQJq8wgacnBsdvb2sC0K875ScYr8T5XmfeH/6dg== dependencies: "@babel/runtime" "^7.21.0" solid-js "^1.3.0" @@ -804,10 +804,10 @@ echarts@^5.4.0: tslib "2.3.0" zrender "5.4.0" -element-plus@^2.3.7: - version "2.3.7" - resolved "https://registry.npmmirror.com/element-plus/-/element-plus-2.3.7.tgz#544a127f0e65f51715e3b24ec3ebf545c46859cd" - integrity sha512-h6TxclbaLUJxg/Bv5j/ZKsK+K5yadQliw5+R30HWyE69pXlqXTX24oYx+yw3pA4Dy+lqEDi5501FQ0CORk3OSA== +element-plus@^2.3.8: + version "2.3.8" + resolved "https://registry.npmmirror.com/element-plus/-/element-plus-2.3.8.tgz#46032abe1a712abfb65932f146ee19281312a9cf" + integrity sha512-yHQR0/tG2LvPkpGUt7Te/hPmP2XW/BytBNUbx+EFO54VnGCOE3upmQcVffNp1PLgwg9sthYDXontUWpnpmLPJw== dependencies: "@ctrl/tinycolor" "^3.4.1" "@element-plus/icons-vue" "^2.0.6" @@ -825,33 +825,33 @@ element-plus@^2.3.7: memoize-one "^6.0.0" normalize-wheel-es "^1.2.0" -esbuild@^0.17.5: - version "0.17.12" - resolved "https://registry.npmmirror.com/esbuild/-/esbuild-0.17.12.tgz#2ad7523bf1bc01881e9d904bc04e693bd3bdcf2f" - integrity sha512-bX/zHl7Gn2CpQwcMtRogTTBf9l1nl+H6R8nUbjk+RuKqAE3+8FDulLA+pHvX7aA7Xe07Iwa+CWvy9I8Y2qqPKQ== +esbuild@^0.18.10: + version "0.18.11" + resolved "https://registry.npmmirror.com/esbuild/-/esbuild-0.18.11.tgz#cbf94dc3359d57f600a0dbf281df9b1d1b4a156e" + integrity sha512-i8u6mQF0JKJUlGR3OdFLKldJQMMs8OqM9Cc3UCi9XXziJ9WERM5bfkHaEAy0YAvPRMgqSW55W7xYn84XtEFTtA== optionalDependencies: - "@esbuild/android-arm" "0.17.12" - "@esbuild/android-arm64" "0.17.12" - "@esbuild/android-x64" "0.17.12" - "@esbuild/darwin-arm64" "0.17.12" - "@esbuild/darwin-x64" "0.17.12" - "@esbuild/freebsd-arm64" "0.17.12" - "@esbuild/freebsd-x64" "0.17.12" - "@esbuild/linux-arm" "0.17.12" - "@esbuild/linux-arm64" "0.17.12" - "@esbuild/linux-ia32" "0.17.12" - "@esbuild/linux-loong64" "0.17.12" - "@esbuild/linux-mips64el" "0.17.12" - "@esbuild/linux-ppc64" "0.17.12" - "@esbuild/linux-riscv64" "0.17.12" - "@esbuild/linux-s390x" "0.17.12" - "@esbuild/linux-x64" "0.17.12" - "@esbuild/netbsd-x64" "0.17.12" - "@esbuild/openbsd-x64" "0.17.12" - "@esbuild/sunos-x64" "0.17.12" - "@esbuild/win32-arm64" "0.17.12" - "@esbuild/win32-ia32" "0.17.12" - "@esbuild/win32-x64" "0.17.12" + "@esbuild/android-arm" "0.18.11" + "@esbuild/android-arm64" "0.18.11" + "@esbuild/android-x64" "0.18.11" + "@esbuild/darwin-arm64" "0.18.11" + "@esbuild/darwin-x64" "0.18.11" + "@esbuild/freebsd-arm64" "0.18.11" + "@esbuild/freebsd-x64" "0.18.11" + "@esbuild/linux-arm" "0.18.11" + "@esbuild/linux-arm64" "0.18.11" + "@esbuild/linux-ia32" "0.18.11" + "@esbuild/linux-loong64" "0.18.11" + "@esbuild/linux-mips64el" "0.18.11" + "@esbuild/linux-ppc64" "0.18.11" + "@esbuild/linux-riscv64" "0.18.11" + "@esbuild/linux-s390x" "0.18.11" + "@esbuild/linux-x64" "0.18.11" + "@esbuild/netbsd-x64" "0.18.11" + "@esbuild/openbsd-x64" "0.18.11" + "@esbuild/sunos-x64" "0.18.11" + "@esbuild/win32-arm64" "0.18.11" + "@esbuild/win32-ia32" "0.18.11" + "@esbuild/win32-x64" "0.18.11" escape-html@^1.0.3: version "1.0.3" @@ -1409,15 +1409,15 @@ minimatch@^3.0.5, minimatch@^3.1.2: dependencies: brace-expansion "^1.1.7" -mitt@^3.0.0: - version "3.0.0" - resolved "https://registry.npmmirror.com/mitt/download/mitt-3.0.0.tgz" - integrity sha512-7dX2/10ITVyqh4aOSVI9gdape+t9l2/8QxHrFmUXu4EEUpdlxl6RudZUPZoc+zuY2hk1j7XxVroIVIan/pD/SQ== +mitt@^3.0.1: + version "3.0.1" + resolved "https://registry.npmmirror.com/mitt/-/mitt-3.0.1.tgz#ea36cf0cc30403601ae074c8f77b7092cdab36d1" + integrity sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw== -monaco-editor@^0.39.0: - version "0.39.0" - resolved "https://registry.npmmirror.com/monaco-editor/-/monaco-editor-0.39.0.tgz#3cf8e3718d6aac347d374516a6837d1c13d967d2" - integrity sha512-zhbZ2Nx93tLR8aJmL2zI1mhJpsl87HMebNBM6R8z4pLfs8pj604pIVIVwyF1TivcfNtIPpMXL+nb3DsBmE/x6Q== +monaco-editor@^0.40.0: + version "0.40.0" + resolved "https://registry.npmmirror.com/monaco-editor/-/monaco-editor-0.40.0.tgz#d10834e15ad50a15ec61fd01892e508464ebe2fe" + integrity sha512-1wymccLEuFSMBvCk/jT1YDW/GuxMLYwnFwF9CDyYCxoTw2Pt379J3FUhwy9c43j51JdcxVPjwk0jm0EVDsBS2g== monaco-sql-languages@^0.11.0: version "0.11.0" @@ -1558,10 +1558,10 @@ picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3: resolved "https://registry.nlark.com/picomatch/download/picomatch-2.3.0.tgz?cache=0&sync_timestamp=1621648246651&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fpicomatch%2Fdownload%2Fpicomatch-2.3.0.tgz" integrity sha1-8fBh3o9qS/AiiS4tEoI0+5gwKXI= -pinia@^2.1.3: - version "2.1.3" - resolved "https://registry.npmmirror.com/pinia/-/pinia-2.1.3.tgz#50c70c7b4c94c109fade0ed4122231cbba72f8c5" - integrity sha512-XNA/z/ye4P5rU1pieVmh0g/hSuDO98/a5UC8oSP0DNdvt6YtetJNHTrXwpwsQuflkGT34qKxAEcp7lSxXNjf/A== +pinia@^2.1.4: + version "2.1.4" + resolved "https://registry.npmmirror.com/pinia/-/pinia-2.1.4.tgz#a642adfe6208e10c36d3dc16184a91064788142a" + integrity sha512-vYlnDu+Y/FXxv1ABo1vhjC+IbqvzUdiUC3sfDRrRyY2CQSrqqaa+iiHmqtARFxJVqWQMCJfXx1PBvFs9aJVLXQ== dependencies: "@vue/devtools-api" "^6.5.0" vue-demi ">=0.14.5" @@ -1575,10 +1575,10 @@ postcss@^8.1.10: picocolors "^1.0.0" source-map-js "^1.0.1" -postcss@^8.4.23: - version "8.4.23" - resolved "https://registry.npmmirror.com/postcss/-/postcss-8.4.23.tgz#df0aee9ac7c5e53e1075c24a3613496f9e6552ab" - integrity sha512-bQ3qMcpF6A/YjR55xtoTr0jGOlnPOKAIMdOWiv0EIT6HVPEaJiJB4NLljSbiHoC2RX7DN5Uvjtpbg1NPdwv1oA== +postcss@^8.4.24: + version "8.4.25" + resolved "https://registry.npmmirror.com/postcss/-/postcss-8.4.25.tgz#4a133f5e379eda7f61e906c3b1aaa9b81292726f" + integrity sha512-7taJ/8t2av0Z+sQEvNzCkpDynl0tX3uJMCODi6nT3PfASC7dYCWV9aQ+uiCf+KBD4SEFcu+GvJdGdwzQ6OSjCw== dependencies: nanoid "^3.3.6" picocolors "^1.0.0" @@ -1666,10 +1666,10 @@ rimraf@^3.0.2: dependencies: glob "^7.1.3" -rollup@^3.21.0: - version "3.21.3" - resolved "https://registry.npmmirror.com/rollup/-/rollup-3.21.3.tgz#b3f1920a9d35a9de70f120a1d085753e41997941" - integrity sha512-VnPfEG51nIv2xPLnZaekkuN06q9ZbnyDcLkaBdJa/W7UddyhOfMP2yOPziYQfeY7k++fZM8FdQIummFN5y14kA== +rollup@^3.25.2: + version "3.26.2" + resolved "https://registry.npmmirror.com/rollup/-/rollup-3.26.2.tgz#2e76a37606cb523fc9fef43e6f59c93f86d95e7c" + integrity sha512-6umBIGVz93er97pMgQO08LuH3m6PUb3jlDUUGFsNJB6VgTCUaDFpupf5JfU30529m/UKOgmiX+uY6Sx8cOYpLA== optionalDependencies: fsevents "~2.3.2" @@ -1855,14 +1855,14 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" -vite@^4.3.9: - version "4.3.9" - resolved "https://registry.npmmirror.com/vite/-/vite-4.3.9.tgz#db896200c0b1aa13b37cdc35c9e99ee2fdd5f96d" - integrity sha512-qsTNZjO9NoJNW7KnOrgYwczm0WctJ8m/yqYAMAK9Lxt4SoySUfS5S8ia9K7JHpa3KEeMfyF8LoJ3c5NeBJy6pg== +vite@^4.4.2: + version "4.4.2" + resolved "https://registry.npmmirror.com/vite/-/vite-4.4.2.tgz#acd47de771c498aec80e4900f30133d9529b278a" + integrity sha512-zUcsJN+UvdSyHhYa277UHhiJ3iq4hUBwHavOpsNUGsTgjBeoBlK8eDt+iT09pBq0h9/knhG/SPrZiM7cGmg7NA== dependencies: - esbuild "^0.17.5" - postcss "^8.4.23" - rollup "^3.21.0" + esbuild "^0.18.10" + postcss "^8.4.24" + rollup "^3.25.2" optionalDependencies: fsevents "~2.3.2" @@ -1909,10 +1909,10 @@ vue-eslint-parser@^9.1.1: lodash "^4.17.21" semver "^7.3.6" -vue-router@^4.2.2: - version "4.2.2" - resolved "https://registry.npmmirror.com/vue-router/-/vue-router-4.2.2.tgz#b0097b66d89ca81c0986be03da244c7b32a4fd81" - integrity sha512-cChBPPmAflgBGmy3tBsjeoe3f3VOSG6naKyY5pjtrqLGbNEXdzCigFUHgBvp9e3ysAtFtEx7OLqcSDh/1Cq2TQ== +vue-router@^4.2.4: + version "4.2.4" + resolved "https://registry.npmmirror.com/vue-router/-/vue-router-4.2.4.tgz#382467a7e2923e6a85f015d081e1508052c191b9" + integrity sha512-9PISkmaCO02OzPVOMq2w82ilty6+xJmQrarYZDkjZBfl4RvYAlt4PKnEX21oW4KTtWfa9OuO/b3qk1Od3AEdCQ== dependencies: "@vue/devtools-api" "^6.5.0" diff --git a/server/go.mod b/server/go.mod index 3be482c8..b8fd9fe4 100644 --- a/server/go.mod +++ b/server/go.mod @@ -8,6 +8,7 @@ require ( github.com/golang-jwt/jwt/v5 v5.0.0 github.com/gorilla/websocket v1.5.0 github.com/lib/pq v1.10.7 + github.com/lionsoul2014/ip2region/binding/golang v0.0.0-20230712084735-068dc2aee82d github.com/mojocn/base64Captcha v1.3.5 // 验证码 github.com/pkg/sftp v1.13.5 github.com/pquerna/otp v1.4.0 diff --git a/server/initialize/initialize.go b/server/initialize/initialize.go new file mode 100644 index 00000000..7ebaf724 --- /dev/null +++ b/server/initialize/initialize.go @@ -0,0 +1,7 @@ +package initialize + +import machineInit "mayfly-go/internal/machine/initialize" + +func InitOther() { + machineInit.Init() +} diff --git a/server/internal/db/api/db.go b/server/internal/db/api/db.go index 59e4a41a..6900597b 100644 --- a/server/internal/db/api/db.go +++ b/server/internal/db/api/db.go @@ -48,6 +48,10 @@ func (d *Db) Dbs(rc *req.Ctx) { rc.ResData = d.DbApp.GetPageList(queryCond, page, new([]vo.SelectDataDbVO)) } +func (d *Db) DbTags(rc *req.Ctx) { + rc.ResData = d.TagApp.ListTagByAccountIdAndResource(rc.LoginAccount.Id, new(entity.Db)) +} + func (d *Db) Save(rc *req.Ctx) { form := &form.DbForm{} db := ginx.BindJsonAndCopyTo[*entity.Db](rc.GinCtx, form, new(entity.Db)) diff --git a/server/internal/db/router/db.go b/server/internal/db/router/db.go index cb969548..43f12900 100644 --- a/server/internal/db/router/db.go +++ b/server/internal/db/router/db.go @@ -24,6 +24,8 @@ func InitDbRouter(router *gin.RouterGroup) { // 获取数据库列表 req.NewGet("", d.Dbs), + req.NewGet("/tags", d.DbTags), + req.NewPost("", d.Save).Log(req.NewLogSave("db-保存数据库信息")), // 获取数据库实例的所有数据库名 diff --git a/server/internal/machine/api/auth_cert.go b/server/internal/machine/api/auth_cert.go index e1d32dbe..a223c2f8 100644 --- a/server/internal/machine/api/auth_cert.go +++ b/server/internal/machine/api/auth_cert.go @@ -8,7 +8,6 @@ import ( "mayfly-go/pkg/biz" "mayfly-go/pkg/ginx" "mayfly-go/pkg/req" - "mayfly-go/pkg/utils" "strconv" "strings" ) @@ -34,12 +33,8 @@ func (ac *AuthCert) AuthCerts(rc *req.Ctx) { } func (c *AuthCert) SaveAuthCert(rc *req.Ctx) { - g := rc.GinCtx acForm := &form.AuthCertForm{} - ginx.BindJsonAndValid(g, acForm) - - ac := new(entity.AuthCert) - utils.Copy(ac, acForm) + ac := ginx.BindJsonAndCopyTo(rc.GinCtx, acForm, new(entity.AuthCert)) // 脱敏记录日志 acForm.Passphrase = "***" diff --git a/server/internal/machine/api/form/form.go b/server/internal/machine/api/form/form.go index 4d32b734..c890db8c 100644 --- a/server/internal/machine/api/form/form.go +++ b/server/internal/machine/api/form/form.go @@ -63,9 +63,14 @@ type AuthCertForm struct { Remark string `json:"remark"` } -// 资产授权凭证信息 -type AssetAuthCertForm struct { - AuthCertId uint64 `json:"authCertId"` - TagId uint64 `json:"tagId"` - TagPath string `json:"tagPath" binding:"required"` +// 机器记录任务 +type MachineCronJobForm struct { + Id uint64 `json:"id"` + Name string `json:"name" binding:"required"` + Cron string `json:"cron" binding:"required"` // cron + Script string `json:"script" binding:"required"` + Status int `json:"status" binding:"required"` + SaveExecResType int `json:"saveExecResType" binding:"required"` + MachineIds []uint64 `json:"machineIds"` + Remark string `json:"remark"` } diff --git a/server/internal/machine/api/machine.go b/server/internal/machine/api/machine.go index 02e83b0c..bc69aa31 100644 --- a/server/internal/machine/api/machine.go +++ b/server/internal/machine/api/machine.go @@ -55,6 +55,10 @@ func (m *Machine) Machines(rc *req.Ctx) { rc.ResData = res } +func (m *Machine) MachineTags(rc *req.Ctx) { + rc.ResData = m.TagApp.ListTagByAccountIdAndResource(rc.LoginAccount.Id, new(entity.Machine)) +} + func (m *Machine) MachineStats(rc *req.Ctx) { stats := m.MachineApp.GetCli(GetMachineId(rc.GinCtx)).GetAllStats() rc.ResData = stats diff --git a/server/internal/machine/api/machine_cronjob.go b/server/internal/machine/api/machine_cronjob.go new file mode 100644 index 00000000..bed26aec --- /dev/null +++ b/server/internal/machine/api/machine_cronjob.go @@ -0,0 +1,67 @@ +package api + +import ( + "mayfly-go/internal/machine/api/form" + "mayfly-go/internal/machine/api/vo" + "mayfly-go/internal/machine/application" + "mayfly-go/internal/machine/domain/entity" + "strconv" + "strings" + + "mayfly-go/pkg/biz" + "mayfly-go/pkg/ginx" + "mayfly-go/pkg/req" + "mayfly-go/pkg/scheduler" +) + +type MachineCronJob struct { + MachineCronJobApp application.MachineCronJob +} + +func (m *MachineCronJob) MachineCronJobs(rc *req.Ctx) { + cond, pageParam := ginx.BindQueryAndPage(rc.GinCtx, new(entity.MachineCronJob)) + + vos := new([]*vo.MachineCronJobVO) + pr := m.MachineCronJobApp.GetPageList(cond, pageParam, vos) + for _, mcj := range *vos { + mcj.Running = scheduler.ExistKey(mcj.Key) + } + + rc.ResData = pr +} + +func (m *MachineCronJob) Save(rc *req.Ctx) { + jobForm := new(form.MachineCronJobForm) + mcj := ginx.BindJsonAndCopyTo[*entity.MachineCronJob](rc.GinCtx, jobForm, new(entity.MachineCronJob)) + rc.ReqParam = jobForm + mcj.SetBaseInfo(rc.LoginAccount) + cronJobId := m.MachineCronJobApp.Save(mcj) + + // 关联机器 + m.MachineCronJobApp.CronJobRelateMachines(cronJobId, jobForm.MachineIds, rc.LoginAccount) +} + +func (m *MachineCronJob) Delete(rc *req.Ctx) { + idsStr := ginx.PathParam(rc.GinCtx, "ids") + rc.ReqParam = idsStr + ids := strings.Split(idsStr, ",") + + for _, v := range ids { + value, err := strconv.Atoi(v) + biz.ErrIsNilAppendErr(err, "string类型转换为int异常: %s") + m.MachineCronJobApp.Delete(uint64(value)) + } +} + +func (m *MachineCronJob) GetRelateMachineIds(rc *req.Ctx) { + rc.ResData = m.MachineCronJobApp.GetRelateMachineIds(uint64(ginx.QueryInt(rc.GinCtx, "cronJobId", -1))) +} + +func (m *MachineCronJob) GetRelateCronJobIds(rc *req.Ctx) { + rc.ResData = m.MachineCronJobApp.GetRelateMachineIds(uint64(ginx.QueryInt(rc.GinCtx, "machineId", -1))) +} + +func (m *MachineCronJob) CronJobExecs(rc *req.Ctx) { + cond, pageParam := ginx.BindQueryAndPage[*entity.MachineCronJobExec](rc.GinCtx, new(entity.MachineCronJobExec)) + rc.ResData = m.MachineCronJobApp.GetExecPageList(cond, pageParam, new([]entity.MachineCronJobExec)) +} diff --git a/server/internal/machine/api/vo/vo.go b/server/internal/machine/api/vo/vo.go index 18c4bf94..7f717eef 100644 --- a/server/internal/machine/api/vo/vo.go +++ b/server/internal/machine/api/vo/vo.go @@ -43,6 +43,19 @@ type MachineScriptVO struct { MachineId *uint64 `json:"machineId"` } +// 机器记录任务 +type MachineCronJobVO struct { + Id uint64 `json:"id"` + Key string `json:"key"` + Name string `json:"name"` + Cron string `json:"cron"` // cron + Script string `json:"script"` + Status int `json:"status"` + SaveExecResType int `json:"saveExecResType"` + Remark string `json:"remark"` + Running bool `json:"running" gorm:"-"` // 是否运行中 +} + type MachineFileVO struct { Id *int64 `json:"id"` Name *string `json:"name"` diff --git a/server/internal/machine/application/application.go b/server/internal/machine/application/application.go index af00cb23..5db6a6d3 100644 --- a/server/internal/machine/application/application.go +++ b/server/internal/machine/application/application.go @@ -15,6 +15,13 @@ var ( persistence.GetMachineRepo(), GetAuthCertApp(), ) + + machineCropJobApp MachineCronJob = newMachineCronJobApp( + persistence.GetMachineCronJobRepo(), + persistence.GetMachineCronJobRelateRepo(), + persistence.GetMachineCronJobExecRepo(), + GetMachineApp(), + ) ) func GetMachineApp() Machine { @@ -32,3 +39,7 @@ func GetMachineScriptApp() MachineScript { func GetAuthCertApp() AuthCert { return authCertApp } + +func GetMachineCronJobApp() MachineCronJob { + return machineCropJobApp +} diff --git a/server/internal/machine/application/machine.go b/server/internal/machine/application/machine.go index 47009495..d9027fe2 100644 --- a/server/internal/machine/application/machine.go +++ b/server/internal/machine/application/machine.go @@ -114,17 +114,15 @@ func (m *machineAppImpl) Delete(id uint64) { gormx.Tx( func(db *gorm.DB) error { // 删除machine表信息 - return db.Delete(new(entity.Machine), "id = ?", id).Error + return gormx.DeleteByIdWithDb(db, new(entity.Machine), id) }, func(db *gorm.DB) error { // 删除machine_file - machineFile := &entity.MachineFile{MachineId: id} - return db.Where(machineFile).Delete(machineFile).Error + return gormx.DeleteByConditionWithDb(db, &entity.MachineFile{MachineId: id}) }, func(db *gorm.DB) error { // 删除machine_script - machineScript := &entity.MachineScript{MachineId: id} - return db.Where(machineScript).Delete(machineScript).Error + return gormx.DeleteByConditionWithDb(db, &entity.MachineScript{MachineId: id}) }, ) } diff --git a/server/internal/machine/application/machine_cronjob.go b/server/internal/machine/application/machine_cronjob.go new file mode 100644 index 00000000..51b9722f --- /dev/null +++ b/server/internal/machine/application/machine_cronjob.go @@ -0,0 +1,266 @@ +package application + +import ( + "mayfly-go/internal/machine/domain/entity" + "mayfly-go/internal/machine/domain/repository" + "mayfly-go/pkg/global" + "mayfly-go/pkg/model" + "mayfly-go/pkg/scheduler" + "mayfly-go/pkg/utils" + "time" +) + +type MachineCronJob interface { + // 分页获取机器任务列表信息 + GetPageList(condition *entity.MachineCronJob, pageParam *model.PageParam, toEntity any, orderBy ...string) *model.PageResult[any] + + // 获取分页执行结果列表 + GetExecPageList(condition *entity.MachineCronJobExec, pageParam *model.PageParam, toEntity any, orderBy ...string) *model.PageResult[any] + + // 根据id获取 + GetById(id uint64, cols ...string) *entity.MachineCronJob + + Save(entity *entity.MachineCronJob) uint64 + + Delete(id uint64) + + // 获取管理的机器列表id + GetRelateMachineIds(cronJobId uint64) []uint64 + + // 获取机器关联的计划任务列表 + GetRelateCronJobIds(machineId uint64) []uint64 + + // 计划任务关联机器 + CronJobRelateMachines(cronJobId uint64, machineIds []uint64, la *model.LoginAccount) + + // 机器关联计划任务 + MachineRelateCronJobs(machineId uint64, cronJobs []uint64, la *model.LoginAccount) + + // 初始化计划任务 + InitCronJob() +} + +type machineCropJobAppImpl struct { + machineCropJobRepo repository.MachineCronJob + machineCropJobRelateRepo repository.MachineCronJobRelate + machineCropJobExecRepo repository.MachineCronJobExec + machineApp Machine +} + +func newMachineCronJobApp( + machineCropJobRepo repository.MachineCronJob, + machineCropJobRelateRepo repository.MachineCronJobRelate, + machineCropJobExecRepo repository.MachineCronJobExec, + machineApp Machine, +) MachineCronJob { + return &machineCropJobAppImpl{ + machineCropJobRepo: machineCropJobRepo, + machineCropJobRelateRepo: machineCropJobRelateRepo, + machineCropJobExecRepo: machineCropJobExecRepo, + machineApp: machineApp, + } +} + +// 分页获取机器脚本任务列表 +func (m *machineCropJobAppImpl) GetPageList(condition *entity.MachineCronJob, pageParam *model.PageParam, toEntity any, orderBy ...string) *model.PageResult[any] { + return m.machineCropJobRepo.GetPageList(condition, pageParam, toEntity, orderBy...) +} + +// 获取分页执行结果列表 +func (m *machineCropJobAppImpl) GetExecPageList(condition *entity.MachineCronJobExec, pageParam *model.PageParam, toEntity any, orderBy ...string) *model.PageResult[any] { + return m.machineCropJobExecRepo.GetPageList(condition, pageParam, toEntity, orderBy...) +} + +// 根据id获取 +func (m *machineCropJobAppImpl) GetById(id uint64, cols ...string) *entity.MachineCronJob { + return m.machineCropJobRepo.GetById(id, cols...) +} + +// 保存机器任务信息 +func (m *machineCropJobAppImpl) Save(mcj *entity.MachineCronJob) uint64 { + // 更新操作 + if mcj.Id != 0 { + m.machineCropJobRepo.UpdateById(mcj) + // 处理最新的计划任务 + m.addCronJob(m.GetById(mcj.Id)) + return mcj.Id + } + + m.addCronJob(mcj) + m.machineCropJobRepo.Insert(mcj) + return mcj.Id +} + +func (m *machineCropJobAppImpl) Delete(id uint64) { + m.machineCropJobRepo.Delete(id) + m.machineCropJobExecRepo.Delete(&entity.MachineCronJobExec{CronJobId: id}) + m.machineCropJobRelateRepo.Delete(&entity.MachineCronJobRelate{CronJobId: id}) +} + +func (m *machineCropJobAppImpl) GetRelateMachineIds(cronJobId uint64) []uint64 { + return m.machineCropJobRelateRepo.GetMachineIds(cronJobId) +} + +func (m *machineCropJobAppImpl) GetRelateCronJobIds(machineId uint64) []uint64 { + return m.machineCropJobRelateRepo.GetCronJobIds(machineId) +} + +func (m *machineCropJobAppImpl) CronJobRelateMachines(cronJobId uint64, machineIds []uint64, la *model.LoginAccount) { + oldMachineIds := m.machineCropJobRelateRepo.GetMachineIds(cronJobId) + addIds, delIds, _ := utils.ArrayCompare[uint64](machineIds, oldMachineIds, func(u1, u2 uint64) bool { return u1 == u2 }) + addVals := make([]*entity.MachineCronJobRelate, 0) + + now := time.Now() + for _, addId := range addIds { + addVals = append(addVals, &entity.MachineCronJobRelate{ + MachineId: addId, + CronJobId: cronJobId, + Creator: la.Username, + CreatorId: la.Id, + CreateTime: &now, + }) + } + m.machineCropJobRelateRepo.BatchInsert(addVals) + + for _, delId := range delIds { + m.machineCropJobRelateRepo.Delete(&entity.MachineCronJobRelate{CronJobId: cronJobId, MachineId: delId}) + } +} + +func (m *machineCropJobAppImpl) MachineRelateCronJobs(machineId uint64, cronJobs []uint64, la *model.LoginAccount) { + oldCronIds := m.machineCropJobRelateRepo.GetCronJobIds(machineId) + addIds, delIds, _ := utils.ArrayCompare[uint64](cronJobs, oldCronIds, func(u1, u2 uint64) bool { return u1 == u2 }) + addVals := make([]*entity.MachineCronJobRelate, 0) + + now := time.Now() + for _, addId := range addIds { + addVals = append(addVals, &entity.MachineCronJobRelate{ + MachineId: machineId, + CronJobId: addId, + Creator: la.Username, + CreatorId: la.Id, + CreateTime: &now, + }) + } + m.machineCropJobRelateRepo.BatchInsert(addVals) + + for _, delId := range delIds { + m.machineCropJobRelateRepo.Delete(&entity.MachineCronJobRelate{CronJobId: delId, MachineId: machineId}) + } +} + +func (m *machineCropJobAppImpl) InitCronJob() { + defer func() { + if err := recover(); err != nil { + global.Log.Errorf("机器计划任务初始化失败: %s", err.(error).Error()) + } + }() + + pageParam := &model.PageParam{ + PageSize: 2, + PageNum: 1, + } + cond := new(entity.MachineCronJob) + cond.Status = entity.MachineCronJobStatusEnable + mcjs := new([]entity.MachineCronJob) + + pr := m.GetPageList(cond, pageParam, mcjs) + total := pr.Total + add := 0 + + for { + for _, mcj := range *mcjs { + m.addCronJob(&mcj) + add++ + } + if add >= int(total) { + return + } + + pageParam.PageNum = pageParam.PageNum + 1 + m.GetPageList(cond, pageParam, mcjs) + } +} + +func (m *machineCropJobAppImpl) addCronJob(mcj *entity.MachineCronJob) { + var key string + isDisable := mcj.Status == entity.MachineCronJobStatusDisable + if mcj.Id == 0 { + key = utils.RandString(16) + mcj.Key = key + if isDisable { + return + } + } else { + key = mcj.Key + } + + if isDisable { + scheduler.RemoveByKey(key) + return + } + + scheduler.AddFunByKey(key, mcj.Cron, func() { + m.runCronJob(key) + }) +} + +func (m *machineCropJobAppImpl) runCronJob(key string) { + cronJob := new(entity.MachineCronJob) + cronJob.Key = key + err := m.machineCropJobRepo.GetBy(cronJob) + // 不存在或禁用,则移除该任务 + if err != nil || cronJob.Status == entity.MachineCronJobStatusDisable { + scheduler.RemoveByKey(key) + } + + machienIds := m.machineCropJobRelateRepo.GetMachineIds(cronJob.Id) + for _, machineId := range machienIds { + go m.runCronJob0(machineId, cronJob) + } +} + +func (m *machineCropJobAppImpl) runCronJob0(mid uint64, cronJob *entity.MachineCronJob) { + defer func() { + if err := recover(); err != nil { + res := err.(error).Error() + m.machineCropJobExecRepo.Insert(&entity.MachineCronJobExec{ + MachineId: mid, + CronJobId: cronJob.Id, + ExecTime: time.Now(), + Status: entity.MachineCronJobExecStatusError, + Res: res, + }) + global.Log.Errorf("机器:[%d]执行[%s]计划任务失败: %s", mid, cronJob.Name, res) + } + }() + + res, err := m.machineApp.GetCli(uint64(mid)).Run(cronJob.Script) + if err != nil { + if res == "" { + res = err.Error() + } + global.Log.Errorf("机器:[%d]执行[%s]计划任务失败: %s", mid, cronJob.Name, res) + } else { + global.Log.Debugf("机器:[%d]执行[%s]计划任务成功, 执行结果: %s", mid, cronJob.Name, res) + } + + if cronJob.SaveExecResType == entity.SaveExecResTypeNo || + (cronJob.SaveExecResType == entity.SaveExecResTypeOnError && err == nil) { + return + } + + execRes := &entity.MachineCronJobExec{ + MachineId: mid, + CronJobId: cronJob.Id, + ExecTime: time.Now(), + Res: res, + } + if err == nil { + execRes.Status = entity.MachineCronJobExecStatusSuccess + } else { + execRes.Status = entity.MachineCronJobExecStatusError + } + // 保存执行记录 + m.machineCropJobExecRepo.Insert(execRes) +} diff --git a/server/internal/machine/application/machine_script.go b/server/internal/machine/application/machine_script.go index 78b2f63c..986de88b 100644 --- a/server/internal/machine/application/machine_script.go +++ b/server/internal/machine/application/machine_script.go @@ -35,11 +35,6 @@ type machineScriptAppImpl struct { const Common_Script_Machine_Id = 9999999 -// // 实现类单例 -// var MachineScriptApp MachineScript = &machineScriptAppImpl{ -// machineRepo: persistence.MachineDao, -// machineScriptRepo: persistence.MachineScriptDao} - // 分页获取机器脚本信息列表 func (m *machineScriptAppImpl) GetPageList(condition *entity.MachineScript, pageParam *model.PageParam, toEntity any, orderBy ...string) *model.PageResult[any] { return m.machineScriptRepo.GetPageList(condition, pageParam, toEntity, orderBy...) diff --git a/server/internal/machine/domain/entity/machine_cronjob.go b/server/internal/machine/domain/entity/machine_cronjob.go new file mode 100644 index 00000000..3b900250 --- /dev/null +++ b/server/internal/machine/domain/entity/machine_cronjob.go @@ -0,0 +1,54 @@ +package entity + +import ( + "mayfly-go/pkg/model" + "time" +) + +// 机器任务配置 +type MachineCronJob struct { + model.Model + + Name string `json:"name" form:"name"` + Key string `json:"key"` + Cron string `json:"cron"` // cron表达式 + Script string `json:"script"` // 任务内容 + Status int `json:"status" form:"status"` + Remark string `json:"remark"` // 备注 + LastExecTime *time.Time `json:"lastExecTime"` + SaveExecResType int `json:"saveExecResType"` // 记录执行结果类型 +} + +// 计划任务与机器关联信息 +type MachineCronJobRelate struct { + model.DeletedModel + + CronJobId uint64 + MachineId uint64 + Creator string + CreatorId uint64 + CreateTime *time.Time +} + +// 机器任务执行记录 +type MachineCronJobExec struct { + model.DeletedModel + + CronJobId uint64 `json:"cronJobId" form:"cronJobId"` + MachineId uint64 `json:"machineId" form:"machineId"` + Status int `json:"status" form:"status"` // 执行状态 + Res string `json:"res"` // 执行结果 + ExecTime time.Time `json:"execTime"` +} + +const ( + MachineCronJobStatusEnable = 1 + MachineCronJobStatusDisable = -1 + + MachineCronJobExecStatusSuccess = 1 + MachineCronJobExecStatusError = -1 + + SaveExecResTypeNo = -1 // 不记录执行日志 + SaveExecResTypeOnError = 1 // 执行错误时记录日志 + SaveExecResTypeYes = 2 // 记录日志 +) diff --git a/server/internal/machine/domain/entity/machine_file.go b/server/internal/machine/domain/entity/machine_file.go index dc4efb46..5e69c6c6 100644 --- a/server/internal/machine/domain/entity/machine_file.go +++ b/server/internal/machine/domain/entity/machine_file.go @@ -4,6 +4,7 @@ import "mayfly-go/pkg/model" type MachineFile struct { model.Model + Name string `json:"name"` // 机器id MachineId uint64 `json:"machineId"` diff --git a/server/internal/machine/domain/entity/machine_task_config.go b/server/internal/machine/domain/entity/machine_task_config.go deleted file mode 100644 index 318e2d79..00000000 --- a/server/internal/machine/domain/entity/machine_task_config.go +++ /dev/null @@ -1,16 +0,0 @@ -package entity - -import "mayfly-go/pkg/model" - -// 机器任务配置 -type MachineTaskConfig struct { - model.Model - - Name string `json:"name"` - Cron string `json:"cron"` // cron表达式 - Script string `json:"script"` // 任务内容 - Status string `json:"status"` - EnableNotify int `json:"enableNotify"` // 是否启用通知 - NotifyTemplate string `json:"notifyTemplate"` // 通知模板 - Remark string `json:"remark"` // 备注 -} diff --git a/server/internal/machine/domain/entity/query.go b/server/internal/machine/domain/entity/query.go index b1044749..ce07db8e 100644 --- a/server/internal/machine/domain/entity/query.go +++ b/server/internal/machine/domain/entity/query.go @@ -1,6 +1,7 @@ package entity type MachineQuery struct { + Ids string `json:"ids" form:"ids"` Name string `json:"name" form:"name"` Ip string `json:"ip" form:"ip"` // IP地址 TagPath string `json:"tagPath" form:"tagPath"` diff --git a/server/internal/machine/domain/repository/machine_cronjob.go b/server/internal/machine/domain/repository/machine_cronjob.go new file mode 100644 index 00000000..76e07fe3 --- /dev/null +++ b/server/internal/machine/domain/repository/machine_cronjob.go @@ -0,0 +1,42 @@ +package repository + +import ( + "mayfly-go/internal/machine/domain/entity" + "mayfly-go/pkg/model" +) + +type MachineCronJob interface { + GetPageList(condition *entity.MachineCronJob, pageParam *model.PageParam, toEntity any, orderBy ...string) *model.PageResult[any] + + // 根据条件获取 + GetBy(condition *entity.MachineCronJob, cols ...string) error + + // 根据id获取 + GetById(id uint64, cols ...string) *entity.MachineCronJob + + Delete(id uint64) + + Insert(entity *entity.MachineCronJob) + + UpdateById(entity *entity.MachineCronJob) +} + +type MachineCronJobRelate interface { + GetList(condition *entity.MachineCronJobRelate) []entity.MachineCronJobRelate + + GetMachineIds(cronJobId uint64) []uint64 + + GetCronJobIds(machineId uint64) []uint64 + + Delete(condition *entity.MachineCronJobRelate) + + BatchInsert(mcjrs []*entity.MachineCronJobRelate) +} + +type MachineCronJobExec interface { + GetPageList(condition *entity.MachineCronJobExec, pageParam *model.PageParam, toEntity any, orderBy ...string) *model.PageResult[any] + + Insert(mcje *entity.MachineCronJobExec) + + Delete(m *entity.MachineCronJobExec) +} diff --git a/server/internal/machine/domain/repository/machine_task_config.go b/server/internal/machine/domain/repository/machine_task_config.go deleted file mode 100644 index 9c50c516..00000000 --- a/server/internal/machine/domain/repository/machine_task_config.go +++ /dev/null @@ -1,22 +0,0 @@ -package repository - -import ( - "mayfly-go/internal/machine/domain/entity" - "mayfly-go/pkg/model" -) - -type MachineTaskConfig interface { - GetPageList(condition *entity.MachineTaskConfig, pageParam *model.PageParam, toEntity any, orderBy ...string) *model.PageResult[any] - - // 根据条件获取 - GetBy(condition *entity.MachineTaskConfig, cols ...string) error - - // 根据id获取 - GetById(id uint64, cols ...string) *entity.MachineTaskConfig - - Delete(id uint64) - - Create(entity *entity.MachineTaskConfig) - - UpdateById(entity *entity.MachineTaskConfig) -} diff --git a/server/internal/machine/infrastructure/persistence/machine.go b/server/internal/machine/infrastructure/persistence/machine.go index 5709ef60..e0c4a9a1 100644 --- a/server/internal/machine/infrastructure/persistence/machine.go +++ b/server/internal/machine/infrastructure/persistence/machine.go @@ -7,6 +7,9 @@ import ( "mayfly-go/pkg/biz" "mayfly-go/pkg/gormx" "mayfly-go/pkg/model" + "mayfly-go/pkg/utils" + "strconv" + "strings" ) type machineRepoImpl struct{} @@ -23,6 +26,15 @@ func (m *machineRepoImpl) GetMachineList(condition *entity.MachineQuery, pagePar In("tag_id", condition.TagIds). RLike("tag_path", condition.TagPath). OrderByAsc("tag_path") + + if condition.Ids != "" { + // ,分割id转为id数组 + qd.In("id", utils.ArrayMap[string, uint64](strings.Split(condition.Ids, ","), func(val string) uint64 { + id, _ := strconv.Atoi(val) + return uint64(id) + })) + } + return gormx.PageQuery(qd, pageParam, toEntity) } diff --git a/server/internal/machine/infrastructure/persistence/machine_cronjob.go b/server/internal/machine/infrastructure/persistence/machine_cronjob.go new file mode 100644 index 00000000..590e6bfd --- /dev/null +++ b/server/internal/machine/infrastructure/persistence/machine_cronjob.go @@ -0,0 +1,46 @@ +package persistence + +import ( + "mayfly-go/internal/machine/domain/entity" + "mayfly-go/internal/machine/domain/repository" + "mayfly-go/pkg/biz" + "mayfly-go/pkg/gormx" + "mayfly-go/pkg/model" +) + +type machineCropJobRepoImpl struct{} + +func newMachineCronJobRepo() repository.MachineCronJob { + return new(machineCropJobRepoImpl) +} + +// 分页获取机器信息列表 +func (m *machineCropJobRepoImpl) GetPageList(condition *entity.MachineCronJob, pageParam *model.PageParam, toEntity any, orderBy ...string) *model.PageResult[any] { + qd := gormx.NewQuery(condition).Like("name", condition.Name).Eq("status", condition.Status).WithOrderBy(orderBy...) + return gormx.PageQuery(qd, pageParam, toEntity) +} + +func (m *machineCropJobRepoImpl) GetBy(cond *entity.MachineCronJob, cols ...string) error { + return gormx.GetBy(cond, cols...) +} + +func (m *machineCropJobRepoImpl) GetById(id uint64, cols ...string) *entity.MachineCronJob { + res := new(entity.MachineCronJob) + if err := gormx.GetById(res, id, cols...); err == nil { + return res + } else { + return nil + } +} + +func (m *machineCropJobRepoImpl) Delete(id uint64) { + biz.ErrIsNil(gormx.DeleteById(new(entity.MachineCronJob), id), "删除失败") +} + +func (m *machineCropJobRepoImpl) Insert(entity *entity.MachineCronJob) { + gormx.Insert(entity) +} + +func (m *machineCropJobRepoImpl) UpdateById(entity *entity.MachineCronJob) { + gormx.UpdateById(entity) +} diff --git a/server/internal/machine/infrastructure/persistence/machine_cronjob_exec.go b/server/internal/machine/infrastructure/persistence/machine_cronjob_exec.go new file mode 100644 index 00000000..3533de09 --- /dev/null +++ b/server/internal/machine/infrastructure/persistence/machine_cronjob_exec.go @@ -0,0 +1,28 @@ +package persistence + +import ( + "mayfly-go/internal/machine/domain/entity" + "mayfly-go/internal/machine/domain/repository" + "mayfly-go/pkg/gormx" + "mayfly-go/pkg/model" +) + +type machineCropJobExecRepoImpl struct{} + +func newMachineCronJobExecRepo() repository.MachineCronJobExec { + return new(machineCropJobExecRepoImpl) +} + +// 分页获取机器信息列表 +func (m *machineCropJobExecRepoImpl) GetPageList(condition *entity.MachineCronJobExec, pageParam *model.PageParam, toEntity any, orderBy ...string) *model.PageResult[any] { + qd := gormx.NewQuery(condition).WithCondModel(condition).WithOrderBy(orderBy...) + return gormx.PageQuery(qd, pageParam, toEntity) +} + +func (m *machineCropJobExecRepoImpl) Insert(entity *entity.MachineCronJobExec) { + gormx.Insert(entity) +} + +func (m *machineCropJobExecRepoImpl) Delete(mcje *entity.MachineCronJobExec) { + gormx.DeleteByCondition(mcje) +} diff --git a/server/internal/machine/infrastructure/persistence/machine_cronjob_relate.go b/server/internal/machine/infrastructure/persistence/machine_cronjob_relate.go new file mode 100644 index 00000000..da01b541 --- /dev/null +++ b/server/internal/machine/infrastructure/persistence/machine_cronjob_relate.go @@ -0,0 +1,39 @@ +package persistence + +import ( + "mayfly-go/internal/machine/domain/entity" + "mayfly-go/internal/machine/domain/repository" + "mayfly-go/pkg/gormx" +) + +type machineCropJobRelateRepoImpl struct{} + +func newMachineCropJobRelateRepo() repository.MachineCronJobRelate { + return new(machineCropJobRelateRepoImpl) +} + +func (m *machineCropJobRelateRepoImpl) GetList(condition *entity.MachineCronJobRelate) []entity.MachineCronJobRelate { + list := new([]entity.MachineCronJobRelate) + gormx.ListByOrder(condition, list) + return *list +} + +func (m *machineCropJobRelateRepoImpl) GetMachineIds(cronJobId uint64) []uint64 { + var machineIds []uint64 + gormx.ListBy(&entity.MachineCronJobRelate{CronJobId: cronJobId}, &machineIds, "machine_id") + return machineIds +} + +func (m *machineCropJobRelateRepoImpl) GetCronJobIds(machineId uint64) []uint64 { + var cronJobIds []uint64 + gormx.ListBy(&entity.MachineCronJobRelate{MachineId: machineId}, &cronJobIds, "cron_job_id") + return cronJobIds +} + +func (m *machineCropJobRelateRepoImpl) Delete(condition *entity.MachineCronJobRelate) { + gormx.DeleteByCondition(condition) +} + +func (m *machineCropJobRelateRepoImpl) BatchInsert(mcjrs []*entity.MachineCronJobRelate) { + gormx.BatchInsert(mcjrs) +} diff --git a/server/internal/machine/infrastructure/persistence/machine_file.go b/server/internal/machine/infrastructure/persistence/machine_file.go index 84968c54..1b948c69 100644 --- a/server/internal/machine/infrastructure/persistence/machine_file.go +++ b/server/internal/machine/infrastructure/persistence/machine_file.go @@ -30,7 +30,6 @@ func (m *machineFileRepoImpl) GetById(id uint64, cols ...string) *entity.Machine ms := new(entity.MachineFile) if err := gormx.GetById(ms, id, cols...); err != nil { return nil - } return ms } diff --git a/server/internal/machine/infrastructure/persistence/persistence.go b/server/internal/machine/infrastructure/persistence/persistence.go index 54cf2e94..984a5155 100644 --- a/server/internal/machine/infrastructure/persistence/persistence.go +++ b/server/internal/machine/infrastructure/persistence/persistence.go @@ -3,10 +3,13 @@ package persistence import "mayfly-go/internal/machine/domain/repository" var ( - machineRepo repository.Machine = newMachineRepo() - machineFileRepo repository.MachineFile = newMachineFileRepo() - machineScriptRepo repository.MachineScript = newMachineScriptRepo() - authCertRepo = newAuthCertRepo() + machineRepo repository.Machine = newMachineRepo() + machineFileRepo repository.MachineFile = newMachineFileRepo() + machineScriptRepo repository.MachineScript = newMachineScriptRepo() + authCertRepo repository.AuthCert = newAuthCertRepo() + machineCropJobRepo repository.MachineCronJob = newMachineCronJobRepo() + machineCropJobExecRepo repository.MachineCronJobExec = newMachineCronJobExecRepo() + machineCronJobRelateRepo repository.MachineCronJobRelate = newMachineCropJobRelateRepo() ) func GetMachineRepo() repository.Machine { @@ -24,3 +27,15 @@ func GetMachineScriptRepo() repository.MachineScript { func GetAuthCertRepo() repository.AuthCert { return authCertRepo } + +func GetMachineCronJobRepo() repository.MachineCronJob { + return machineCropJobRepo +} + +func GetMachineCronJobExecRepo() repository.MachineCronJobExec { + return machineCropJobExecRepo +} + +func GetMachineCronJobRelateRepo() repository.MachineCronJobRelate { + return machineCronJobRelateRepo +} diff --git a/server/internal/machine/initialize/init.go b/server/internal/machine/initialize/init.go new file mode 100644 index 00000000..cb6e1f81 --- /dev/null +++ b/server/internal/machine/initialize/init.go @@ -0,0 +1,7 @@ +package initialize + +import "mayfly-go/internal/machine/application" + +func Init() { + application.GetMachineCronJobApp().InitCronJob() +} diff --git a/server/internal/machine/router/machine.go b/server/internal/machine/router/machine.go index b03ca757..f9c3415c 100644 --- a/server/internal/machine/router/machine.go +++ b/server/internal/machine/router/machine.go @@ -22,6 +22,8 @@ func InitMachineRouter(router *gin.RouterGroup) { reqs := [...]*req.Conf{ req.NewGet("", m.Machines), + req.NewGet("/tags", m.MachineTags), + req.NewGet(":machineId/stats", m.MachineStats), req.NewGet(":machineId/process", m.GetProcess), diff --git a/server/internal/machine/router/machine_cronjob.go b/server/internal/machine/router/machine_cronjob.go new file mode 100644 index 00000000..dd093bb3 --- /dev/null +++ b/server/internal/machine/router/machine_cronjob.go @@ -0,0 +1,33 @@ +package router + +import ( + "mayfly-go/internal/machine/api" + "mayfly-go/internal/machine/application" + "mayfly-go/pkg/req" + + "github.com/gin-gonic/gin" +) + +func InitMachineCronJobRouter(router *gin.RouterGroup) { + cronjobs := router.Group("machine-cronjobs") + cj := &api.MachineCronJob{ + MachineCronJobApp: application.GetMachineCronJobApp(), + } + + reqs := [...]*req.Conf{ + // 获取机器任务列表 + req.NewGet("", cj.MachineCronJobs), + + req.NewGet("/machine-ids", cj.GetRelateMachineIds), + + req.NewGet("/cronjob-ids", cj.GetRelateCronJobIds), + + req.NewPost("", cj.Save).Log(req.NewLogSave("保存机器计划任务")), + + req.NewDelete(":ids", cj.Delete).Log(req.NewLogSave("删除机器计划任务")), + + req.NewGet("/execs", cj.CronJobExecs), + } + + req.BatchSetGroup(cronjobs, reqs[:]) +} diff --git a/server/internal/machine/router/router.go b/server/internal/machine/router/router.go index 6259486c..eb2ff17f 100644 --- a/server/internal/machine/router/router.go +++ b/server/internal/machine/router/router.go @@ -7,4 +7,5 @@ func Init(router *gin.RouterGroup) { InitMachineFileRouter(router) InitMachineScriptRouter(router) InitAuthCertRouter(router) + InitMachineCronJobRouter(router) } diff --git a/server/internal/mongo/api/mongo.go b/server/internal/mongo/api/mongo.go index 5878acf7..dbfff7f4 100644 --- a/server/internal/mongo/api/mongo.go +++ b/server/internal/mongo/api/mongo.go @@ -39,6 +39,10 @@ func (m *Mongo) Mongos(rc *req.Ctx) { rc.ResData = m.MongoApp.GetPageList(queryCond, page, new([]entity.Mongo)) } +func (m *Mongo) MongoTags(rc *req.Ctx) { + rc.ResData = m.TagApp.ListTagByAccountIdAndResource(rc.LoginAccount.Id, new(entity.Mongo)) +} + func (m *Mongo) Save(rc *req.Ctx) { form := &form.Mongo{} mongo := ginx.BindJsonAndCopyTo[*entity.Mongo](rc.GinCtx, form, new(entity.Mongo)) diff --git a/server/internal/mongo/router/mongo.go b/server/internal/mongo/router/mongo.go index a6fb59cd..909d2617 100644 --- a/server/internal/mongo/router/mongo.go +++ b/server/internal/mongo/router/mongo.go @@ -21,6 +21,8 @@ func InitMongoRouter(router *gin.RouterGroup) { // 获取所有mongo列表 req.NewGet("", ma.Mongos), + req.NewGet("/tags", ma.MongoTags), + req.NewPost("", ma.Save).Log(req.NewLogSave("mongo-保存信息")), req.NewDelete(":id", ma.DeleteMongo).Log(req.NewLogSave("mongo-删除信息")), diff --git a/server/internal/msg/domain/entity/msg.go b/server/internal/msg/domain/entity/msg.go index 552d7cdf..85f7aab6 100644 --- a/server/internal/msg/domain/entity/msg.go +++ b/server/internal/msg/domain/entity/msg.go @@ -1,11 +1,13 @@ package entity import ( + "mayfly-go/pkg/model" "time" ) type Msg struct { - Id uint64 `json:"id"` + model.DeletedModel + CreateTime *time.Time `json:"createTime"` CreatorId uint64 `json:"creatorId"` Creator string `json:"creator"` diff --git a/server/internal/redis/api/form/redis.go b/server/internal/redis/api/form/redis.go index b1156112..543bdb07 100644 --- a/server/internal/redis/api/form/redis.go +++ b/server/internal/redis/api/form/redis.go @@ -4,6 +4,7 @@ type Redis struct { Id uint64 `json:"id"` Name string `json:"name"` Host string `json:"host" binding:"required"` + Username string `json:"username"` Password string `json:"password"` Mode string `json:"mode"` Db string `json:"db"` diff --git a/server/internal/redis/api/redis.go b/server/internal/redis/api/redis.go index bc72e2d2..0f5464bc 100644 --- a/server/internal/redis/api/redis.go +++ b/server/internal/redis/api/redis.go @@ -38,6 +38,10 @@ func (r *Redis) RedisList(rc *req.Ctx) { rc.ResData = r.RedisApp.GetPageList(queryCond, page, new([]vo.Redis)) } +func (r *Redis) RedisTags(rc *req.Ctx) { + rc.ResData = r.TagApp.ListTagByAccountIdAndResource(rc.LoginAccount.Id, new(entity.Redis)) +} + func (r *Redis) Save(rc *req.Ctx) { form := &form.Redis{} redis := ginx.BindJsonAndCopyTo[*entity.Redis](rc.GinCtx, form, new(entity.Redis)) diff --git a/server/internal/redis/application/redis.go b/server/internal/redis/application/redis.go index 41fe8d05..c77098ee 100644 --- a/server/internal/redis/application/redis.go +++ b/server/internal/redis/application/redis.go @@ -186,6 +186,7 @@ func getRedisCient(re *entity.Redis, db int) *RedisInstance { redisOptions := &redis.Options{ Addr: re.Host, + Username: re.Username, Password: re.Password, // no password set DB: db, // use default DB DialTimeout: 8 * time.Second, @@ -204,6 +205,7 @@ func getRedisClusterClient(re *entity.Redis) *RedisInstance { redisClusterOptions := &redis.ClusterOptions{ Addrs: strings.Split(re.Host, ","), + Username: re.Username, Password: re.Password, DialTimeout: 8 * time.Second, } @@ -221,6 +223,7 @@ func getRedisSentinelCient(re *entity.Redis, db int) *RedisInstance { sentinelOptions := &redis.FailoverOptions{ MasterName: masterNameAndHosts[0], SentinelAddrs: strings.Split(masterNameAndHosts[1], ","), + Username: re.Username, Password: re.Password, // no password set SentinelPassword: re.Password, // 哨兵节点密码需与redis节点密码一致 DB: db, // use default DB diff --git a/server/internal/redis/domain/entity/redis.go b/server/internal/redis/domain/entity/redis.go index bc03c1b2..4a569527 100644 --- a/server/internal/redis/domain/entity/redis.go +++ b/server/internal/redis/domain/entity/redis.go @@ -11,6 +11,7 @@ type Redis struct { Name string `orm:"column(name)" json:"name"` Host string `orm:"column(host)" json:"host"` Mode string `json:"mode"` + Username string `json:"username"` Password string `orm:"column(password)" json:"-"` Db string `orm:"column(database)" json:"db"` SshTunnelMachineId int `orm:"column(ssh_tunnel_machine_id)" json:"sshTunnelMachineId"` // ssh隧道机器id diff --git a/server/internal/redis/router/redis.go b/server/internal/redis/router/redis.go index c404ef5b..51298a82 100644 --- a/server/internal/redis/router/redis.go +++ b/server/internal/redis/router/redis.go @@ -26,6 +26,8 @@ func InitRedisRouter(router *gin.RouterGroup) { // 获取redis list req.NewGet("", rs.RedisList), + req.NewGet("/tags", rs.RedisTags), + req.NewPost("", rs.Save).Log(req.NewLogSave("redis-保存信息")), req.NewGet(":id/pwd", rs.GetRedisPwd), diff --git a/server/internal/sys/api/account.go b/server/internal/sys/api/account.go index 8ab85b58..426f39fe 100644 --- a/server/internal/sys/api/account.go +++ b/server/internal/sys/api/account.go @@ -41,8 +41,7 @@ type Account struct { // @router /accounts/login [post] func (a *Account) Login(rc *req.Ctx) { - loginForm := &form.LoginForm{} - ginx.BindJsonAndValid(rc.GinCtx, loginForm) + loginForm := ginx.BindJsonAndValid(rc.GinCtx, new(form.LoginForm)) accountLoginSecurity := a.ConfigApp.GetConfig(entity.ConfigKeyAccountLoginSecurity).ToAccountLoginSecurity() // 判断是否有开启登录验证码校验 @@ -52,7 +51,8 @@ func (a *Account) Login(rc *req.Ctx) { } username := loginForm.Username - clientIp := rc.GinCtx.ClientIP() + + clientIp := getIpAndRegion(rc) rc.ReqParam = fmt.Sprintf("username: %s | ip: %s", username, clientIp) originPwd, err := utils.DefaultRsaDecrypt(loginForm.Password, true) @@ -121,7 +121,7 @@ func (a *Account) Login(rc *req.Ctx) { // 不进行otp二次校验则直接返回accessToken token = accessToken // 保存登录消息 - go a.saveLogin(account, clientIp) + go a.saveLogin(account, getIpAndRegion(rc)) } // 赋值otp状态 @@ -130,6 +130,12 @@ func (a *Account) Login(rc *req.Ctx) { rc.ResData = res } +// 获取ip与归属地信息 +func getIpAndRegion(rc *req.Ctx) string { + clientIp := rc.GinCtx.ClientIP() + return fmt.Sprintf("%s %s", clientIp, utils.Ip2Region(clientIp)) +} + type OtpVerifyInfo struct { AccountId uint64 Username string @@ -261,7 +267,7 @@ func (a *Account) saveLogin(account *entity.Account, ip string) { // 创建登录消息 loginMsg := &msgentity.Msg{ RecipientId: int64(account.Id), - Msg: fmt.Sprintf("于[%s]-[%s]登录", ip, now.Format("2006-01-02 15:04:05")), + Msg: fmt.Sprintf("于[%s]-[%s]登录", ip, utils.DefaultTimeFormat(now)), Type: 1, } loginMsg.CreateTime = &now @@ -373,34 +379,27 @@ func (a *Account) SaveRoles(rc *req.Ctx) { aid := uint64(form.Id) rc.ReqParam = form - // 将,拼接的字符串进行切割 - idsStr := strings.Split(form.RoleIds, ",") - var newIds []any - for _, v := range idsStr { - id, _ := strconv.Atoi(v) - newIds = append(newIds, uint64(id)) - } + // 将,拼接的字符串进行切割并转换 + newIds := utils.ArrayMap[string, uint64](strings.Split(form.RoleIds, ","), func(val string) uint64 { + id, _ := strconv.Atoi(val) + return uint64(id) + }) - // 将[]uint64转为[]any oIds := a.RoleApp.GetAccountRoleIds(uint64(form.Id)) - var oldIds []any - for _, v := range oIds { - oldIds = append(oldIds, v) - } - addIds, delIds, _ := utils.ArrayCompare(newIds, oldIds, func(i1, i2 any) bool { - return i1.(uint64) == i2.(uint64) + addIds, delIds, _ := utils.ArrayCompare(newIds, oIds, func(i1, i2 uint64) bool { + return i1 == i2 }) createTime := time.Now() creator := rc.LoginAccount.Username creatorId := rc.LoginAccount.Id for _, v := range addIds { - rr := &entity.AccountRole{AccountId: aid, RoleId: v.(uint64), CreateTime: &createTime, CreatorId: creatorId, Creator: creator} + rr := &entity.AccountRole{AccountId: aid, RoleId: v, CreateTime: &createTime, CreatorId: creatorId, Creator: creator} a.RoleApp.SaveAccountRole(rr) } for _, v := range delIds { - a.RoleApp.DeleteAccountRole(aid, v.(uint64)) + a.RoleApp.DeleteAccountRole(aid, v) } } diff --git a/server/internal/sys/api/role.go b/server/internal/sys/api/role.go index 44e3946e..ba1b8a6b 100644 --- a/server/internal/sys/api/role.go +++ b/server/internal/sys/api/role.go @@ -7,6 +7,7 @@ import ( "mayfly-go/internal/sys/domain/entity" "mayfly-go/pkg/biz" "mayfly-go/pkg/ginx" + "mayfly-go/pkg/model" "mayfly-go/pkg/req" "mayfly-go/pkg/utils" "strconv" @@ -65,40 +66,37 @@ func (r *Role) RoleResource(rc *req.Ctx) { // 保存角色资源 func (r *Role) SaveResource(rc *req.Ctx) { - g := rc.GinCtx - var form form.RoleResourceForm - ginx.BindJsonAndValid(g, &form) + ginx.BindJsonAndValid(rc.GinCtx, &form) rid := uint64(form.Id) rc.ReqParam = form - // 将,拼接的字符串进行切割 - idsStr := strings.Split(form.ResourceIds, ",") - var newIds []any - for _, v := range idsStr { - id, _ := strconv.Atoi(v) - newIds = append(newIds, uint64(id)) - } + // 将,拼接的字符串进行切割并转换 + newIds := utils.ArrayMap[string, uint64](strings.Split(form.ResourceIds, ","), func(val string) uint64 { + id, _ := strconv.Atoi(val) + return uint64(id) + }) - // 将[]uint64转为[]any oIds := r.RoleApp.GetRoleResourceIds(uint64(form.Id)) - var oldIds []any - for _, v := range oIds { - oldIds = append(oldIds, v) - } - addIds, delIds, _ := utils.ArrayCompare(newIds, oldIds, func(i1, i2 any) bool { - return i1.(uint64) == i2.(uint64) + addIds, delIds, _ := utils.ArrayCompare(newIds, oIds, func(i1, i2 uint64) bool { + return i1 == i2 }) createTime := time.Now() creator := rc.LoginAccount.Username creatorId := rc.LoginAccount.Id + undeleted := model.ModelUndeleted + + addVals := make([]*entity.RoleResource, 0) for _, v := range addIds { - rr := &entity.RoleResource{RoleId: rid, ResourceId: v.(uint64), CreateTime: &createTime, CreatorId: creatorId, Creator: creator} - r.RoleApp.SaveRoleResource(rr) + rr := &entity.RoleResource{RoleId: rid, ResourceId: v, CreateTime: &createTime, CreatorId: creatorId, Creator: creator} + rr.IsDeleted = undeleted + addVals = append(addVals, rr) } + r.RoleApp.SaveRoleResource(addVals) + for _, v := range delIds { - r.RoleApp.DeleteRoleResource(rid, v.(uint64)) + r.RoleApp.DeleteRoleResource(rid, v) } } diff --git a/server/internal/sys/application/resource.go b/server/internal/sys/application/resource.go index cbd5be50..5a04a8e4 100644 --- a/server/internal/sys/application/resource.go +++ b/server/internal/sys/application/resource.go @@ -4,7 +4,6 @@ import ( "mayfly-go/internal/sys/domain/entity" "mayfly-go/internal/sys/domain/repository" "mayfly-go/pkg/biz" - "mayfly-go/pkg/global" "mayfly-go/pkg/gormx" "mayfly-go/pkg/utils" "strings" @@ -137,7 +136,7 @@ func (r *resourceAppImpl) Sort(sortResource *entity.Resource) { } condition := new(entity.Resource) condition.Id = sortResource.Id - global.Db.Model(condition).Updates(updateMap) + gormx.Updates(condition, updateMap) } func (r *resourceAppImpl) checkCode(code string) { diff --git a/server/internal/sys/application/role.go b/server/internal/sys/application/role.go index 68dfa98f..8ea4a373 100644 --- a/server/internal/sys/application/role.go +++ b/server/internal/sys/application/role.go @@ -20,7 +20,7 @@ type Role interface { GetRoleResources(roleId uint64, toEntity any) // 保存角色资源关联记录 - SaveRoleResource(rr *entity.RoleResource) + SaveRoleResource(rr []*entity.RoleResource) // 删除角色资源关联记录 DeleteRoleResource(roleId uint64, resourceId uint64) @@ -76,7 +76,7 @@ func (m *roleAppImpl) GetRoleResources(roleId uint64, toEntity any) { m.roleRepo.GetRoleResources(roleId, toEntity) } -func (m *roleAppImpl) SaveRoleResource(rr *entity.RoleResource) { +func (m *roleAppImpl) SaveRoleResource(rr []*entity.RoleResource) { m.roleRepo.SaveRoleResource(rr) } diff --git a/server/internal/sys/domain/entity/role.go b/server/internal/sys/domain/entity/role.go index db7b895d..acd64474 100644 --- a/server/internal/sys/domain/entity/role.go +++ b/server/internal/sys/domain/entity/role.go @@ -25,7 +25,8 @@ func (a *Role) TableName() string { // 角色资源 type RoleResource struct { - Id uint64 `json:"id"` + model.DeletedModel + RoleId uint64 `json:"roleId"` ResourceId uint64 `json:"resourceId"` CreateTime *time.Time `json:"createTime"` @@ -39,7 +40,8 @@ func (a *RoleResource) TableName() string { // 账号角色 type AccountRole struct { - Id uint64 `json:"id"` + model.DeletedModel + AccountId uint64 `json:"accountId"` RoleId uint64 `json:"roleId"` CreateTime *time.Time `json:"createTime"` diff --git a/server/internal/sys/domain/entity/syslog.go b/server/internal/sys/domain/entity/syslog.go index 9a851b97..65f14aa3 100644 --- a/server/internal/sys/domain/entity/syslog.go +++ b/server/internal/sys/domain/entity/syslog.go @@ -1,10 +1,14 @@ package entity -import "time" +import ( + "mayfly-go/pkg/model" + "time" +) // 系统操作日志 type SysLog struct { - Id uint64 `json:"id"` + model.DeletedModel + CreateTime time.Time `json:"createTime"` CreatorId uint64 `json:"creatorId"` Creator string `json:"creator"` diff --git a/server/internal/sys/domain/repository/role.go b/server/internal/sys/domain/repository/role.go index 760cc3a4..cf2c5d08 100644 --- a/server/internal/sys/domain/repository/role.go +++ b/server/internal/sys/domain/repository/role.go @@ -15,7 +15,7 @@ type Role interface { GetRoleResources(roleId uint64, toEntity any) - SaveRoleResource(rr *entity.RoleResource) + SaveRoleResource(rr []*entity.RoleResource) DeleteRoleResource(roleId uint64, resourceId uint64) diff --git a/server/internal/sys/infrastructure/persistence/resource.go b/server/internal/sys/infrastructure/persistence/resource.go index 5a9bb11a..15b40c0b 100644 --- a/server/internal/sys/infrastructure/persistence/resource.go +++ b/server/internal/sys/infrastructure/persistence/resource.go @@ -35,7 +35,7 @@ func (r *resourceRepoImpl) GetByCondition(condition *entity.Resource, cols ...st } func (r *resourceRepoImpl) GetChildren(uiPath string) []entity.Resource { - sql := "SELECT id, ui_path FROM t_sys_resource WHERE ui_path LIKE ?" + sql := "SELECT id, ui_path FROM t_sys_resource WHERE ui_path LIKE ? AND is_deleted = 0" var rs []entity.Resource gormx.GetListBySql2Model(sql, &rs, uiPath+"%") return rs @@ -59,23 +59,23 @@ func (r *resourceRepoImpl) GetAccountResources(accountId uint64, toEntity any) { FROM t_sys_resource m WHERE - m.status = 1 + m.status = 1 AND m.is_deleted = 0 AND m.id IN ( SELECT DISTINCT ( rmb.resource_id ) FROM t_sys_account_role p JOIN t_sys_role r ON p.role_Id = r.id - AND p.account_id = ? - AND r.STATUS = 1 - JOIN t_sys_role_resource rmb ON rmb.role_id = r.id UNION + AND p.account_id = ? AND p.is_deleted = 0 + AND r.STATUS = 1 AND r.is_deleted = 0 + JOIN t_sys_role_resource rmb ON rmb.role_id = r.id AND rmb.is_deleted = 0 UNION SELECT r.id FROM t_sys_resource r JOIN t_sys_role_resource rr ON r.id = rr.resource_id JOIN t_sys_role ro ON rr.role_id = ro.id - AND ro.status = 1 AND ro.code LIKE 'COMMON%' + AND ro.status = 1 AND ro.code LIKE 'COMMON%' AND ro.is_deleted = 0 AND rr.is_deleted = 0 ) ORDER BY m.pid ASC, diff --git a/server/internal/sys/infrastructure/persistence/role.go b/server/internal/sys/infrastructure/persistence/role.go index e54d179c..ca4129c8 100644 --- a/server/internal/sys/infrastructure/persistence/role.go +++ b/server/internal/sys/infrastructure/persistence/role.go @@ -41,13 +41,13 @@ func (m *roleRepoImpl) GetRoleResources(roleId uint64, toEntity any) { sql := "select rr.creator AS creator, rr.create_time AS CreateTime, rr.resource_id AS id, r.pid AS pid, " + "r.name AS name, r.type AS type, r.status AS status " + "FROM t_sys_role_resource rr JOIN t_sys_resource r ON rr.resource_id = r.id " + - "WHERE rr.role_id = ? " + + "WHERE rr.role_id = ? AND rr.is_deleted = 0 AND r.is_deleted = 0 " + "ORDER BY r.pid ASC, r.weight ASC" gormx.GetListBySql2Model(sql, toEntity, roleId) } -func (m *roleRepoImpl) SaveRoleResource(rr *entity.RoleResource) { - gormx.Insert(rr) +func (m *roleRepoImpl) SaveRoleResource(rr []*entity.RoleResource) { + gormx.BatchInsert(rr) } func (m *roleRepoImpl) DeleteRoleResource(roleId uint64, resourceId uint64) { @@ -78,7 +78,7 @@ func (m *roleRepoImpl) DeleteAccountRole(accountId, roleId uint64) { // 获取账号角色信息列表 func (m *roleRepoImpl) GetAccountRoles(accountId uint64, toEntity any) { sql := "SELECT r.status, r.name, ar.create_time AS CreateTime, ar.creator AS creator " + - "FROM t_sys_role r JOIN t_sys_account_role ar ON r.id = ar.role_id AND ar.account_id = ? " + + "FROM t_sys_role r JOIN t_sys_account_role ar ON r.id = ar.role_id AND ar.account_id = ? AND r.is_deleted = 0 AND ar.is_deleted = 0 " + "ORDER BY ar.create_time DESC" gormx.GetListBySql2Model(sql, toEntity, accountId) } diff --git a/server/internal/tag/api/team.go b/server/internal/tag/api/team.go index b1537bce..af3d8a62 100644 --- a/server/internal/tag/api/team.go +++ b/server/internal/tag/api/team.go @@ -123,24 +123,15 @@ func (p *Team) SaveTags(rc *req.Ctx) { // 将[]uint64转为[]any oIds := p.TeamApp.ListTagIds(teamId) - var oldIds []any - for _, v := range oIds { - oldIds = append(oldIds, v) - } - - var newIds []any - for _, v := range form.TagIds { - newIds = append(newIds, v) - } // 比较新旧两合集 - addIds, delIds, _ := utils.ArrayCompare(newIds, oldIds, func(i1, i2 any) bool { - return i1.(uint64) == i2.(uint64) + addIds, delIds, _ := utils.ArrayCompare(form.TagIds, oIds, func(i1, i2 uint64) bool { + return i1 == i2 }) loginAccount := rc.LoginAccount for _, v := range addIds { - tagId := v.(uint64) + tagId := v tag := p.TagApp.GetById(tagId) biz.NotNil(tag, "存在非法标签id") @@ -149,7 +140,7 @@ func (p *Team) SaveTags(rc *req.Ctx) { p.TeamApp.SaveTag(ptt) } for _, v := range delIds { - p.TeamApp.DeleteTag(teamId, v.(uint64)) + p.TeamApp.DeleteTag(teamId, v) } rc.ReqParam = form diff --git a/server/internal/tag/application/tag_tree.go b/server/internal/tag/application/tag_tree.go index b50a8f96..575623e1 100644 --- a/server/internal/tag/application/tag_tree.go +++ b/server/internal/tag/application/tag_tree.go @@ -12,6 +12,8 @@ import ( "mayfly-go/internal/tag/domain/entity" "mayfly-go/internal/tag/domain/repository" "mayfly-go/pkg/biz" + "mayfly-go/pkg/global" + "mayfly-go/pkg/gormx" "strings" ) @@ -36,6 +38,10 @@ type TagTree interface { // 根据账号id获取其可访问标签信息 ListTagByAccountId(accountId uint64) []string + // 查询账号id可访问的资源相关联的标签信息 + // @param model对应资源的实体信息,如Machinie、Db等等 + ListTagByAccountIdAndResource(accountId uint64, model any) []string + // 账号是否有权限访问该标签关联的资源信息 CanAccess(accountId uint64, tagPath string) error } @@ -127,6 +133,18 @@ func (p *tagTreeAppImpl) ListTagByAccountId(accountId uint64) []string { return p.tagTreeTeamRepo.SelectTagPathsByAccountId(accountId) } +func (p *tagTreeAppImpl) ListTagByAccountIdAndResource(accountId uint64, entity any) []string { + var res []string + + tagIds := p.ListTagIdByAccountId(accountId) + if len(tagIds) == 0 { + return res + } + + global.Db.Model(entity).Distinct("tag_path").Where("tag_id in ?", tagIds).Scopes(gormx.UndeleteScope).Order("tag_path asc").Find(&res) + return res +} + func (p *tagTreeAppImpl) CanAccess(accountId uint64, tagPath string) error { tagPaths := p.ListTagByAccountId(accountId) // 判断该资源标签是否为该账号拥有的标签或其子标签 diff --git a/server/internal/tag/infrastructure/persistence/tag_tree.go b/server/internal/tag/infrastructure/persistence/tag_tree.go index cb67096d..13b82e08 100644 --- a/server/internal/tag/infrastructure/persistence/tag_tree.go +++ b/server/internal/tag/infrastructure/persistence/tag_tree.go @@ -16,7 +16,7 @@ func newTagTreeRepo() repository.TagTree { } func (p *tagTreeRepoImpl) SelectByCondition(condition *entity.TagTreeQuery, toEntity any, orderBy ...string) { - sql := "SELECT DISTINCT(p.id), p.pid, p.code, p.code_path, p.name, p.remark, p.create_time, p.creator, p.update_time, p.modifier FROM t_tag_tree p WHERE 1 = 1 " + sql := "SELECT DISTINCT(p.id), p.pid, p.code, p.code_path, p.name, p.remark, p.create_time, p.creator, p.update_time, p.modifier FROM t_tag_tree p WHERE p.is_deleted = 0 " if condition.Name != "" { sql = sql + " AND p.name LIKE '%" + condition.Name + "%'" } diff --git a/server/internal/tag/infrastructure/persistence/tag_tree_team.go b/server/internal/tag/infrastructure/persistence/tag_tree_team.go index f5168ecf..7bbe775f 100644 --- a/server/internal/tag/infrastructure/persistence/tag_tree_team.go +++ b/server/internal/tag/infrastructure/persistence/tag_tree_team.go @@ -27,6 +27,6 @@ func (p *tagTreeTeamRepoImpl) DeleteBy(condition *entity.TagTreeTeam) { func (p *tagTreeTeamRepoImpl) SelectTagPathsByAccountId(accountId uint64) []string { var res []string - gormx.GetListBySql2Model("SELECT DISTINCT(t1.tag_path) FROM t_tag_tree_team t1 JOIN t_team_member t2 ON t1.team_id = t2.team_id WHERE t2.account_id = ? ORDER BY t1.tag_path", &res, accountId) + gormx.GetListBySql2Model("SELECT DISTINCT(t1.tag_path) FROM t_tag_tree_team t1 JOIN t_team_member t2 ON t1.team_id = t2.team_id WHERE t2.account_id = ? AND t1.is_deleted = 0 AND t2.is_deleted = 0 ORDER BY t1.tag_path", &res, accountId) return res } diff --git a/server/internal/tag/infrastructure/persistence/team_member.go b/server/internal/tag/infrastructure/persistence/team_member.go index 731085f6..9b5e9a4c 100644 --- a/server/internal/tag/infrastructure/persistence/team_member.go +++ b/server/internal/tag/infrastructure/persistence/team_member.go @@ -23,13 +23,15 @@ func (p *teamMemberRepoImpl) Save(pm *entity.TeamMember) { } func (p *teamMemberRepoImpl) GetPageList(condition *entity.TeamMember, pageParam *model.PageParam, toEntity any) *model.PageResult[any] { - qd := gormx.NewQuery(new(entity.TeamMember)). - Select("t_team_member.*, a.name"). - Joins("JOIN t_sys_account a ON t_team_member.account_id = a.id AND a.status = 1"). - Eq("account_id", condition.AccountId). - Eq("team_id", condition.TeamId). + qd := gormx.NewQueryWithTableName("t_team_member t"). + Select("t.*, a.name"). + Joins("JOIN t_sys_account a ON t.account_id = a.id AND a.status = 1"). + Eq("a.account_id", condition.AccountId). + Eq0("a.is_deleted", model.ModelUndeleted). + Eq("t.team_id", condition.TeamId). + Eq0("t.is_deleted", model.ModelUndeleted). Like("a.username", condition.Username). - OrderByDesc("t_team_member.id") + OrderByDesc("t.id") return gormx.PageQuery(qd, pageParam, toEntity) } diff --git a/server/mayfly-go.sql b/server/mayfly-go.sql index 4fd99faa..3061c88d 100644 --- a/server/mayfly-go.sql +++ b/server/mayfly-go.sql @@ -26,6 +26,8 @@ CREATE TABLE `t_db` ( `update_time` datetime DEFAULT NULL, `modifier_id` bigint(20) DEFAULT NULL, `modifier` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, + `is_deleted` tinyint(8) NOT NULL DEFAULT 0, + `delete_time` datetime DEFAULT NULL, PRIMARY KEY (`id`), KEY `idx_path` (`tag_path`) USING BTREE ) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='数据库资源信息表'; @@ -53,6 +55,8 @@ CREATE TABLE `t_db_sql` ( `update_time` datetime NOT NULL, `modifier_id` bigint(20) DEFAULT NULL, `modifier` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, + `is_deleted` tinyint(8) NOT NULL DEFAULT 0, + `delete_time` datetime DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='数据库sql信息'; @@ -81,6 +85,8 @@ CREATE TABLE `t_db_sql_exec` ( `update_time` datetime NOT NULL, `modifier` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL, `modifier_id` bigint(20) NOT NULL, + `is_deleted` tinyint(8) NOT NULL DEFAULT 0, + `delete_time` datetime DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='数据库sql执行记录表'; @@ -104,6 +110,8 @@ CREATE TABLE `t_auth_cert` ( `update_time` datetime NOT NULL, `modifier` varchar(12) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL, `modifier_id` bigint NOT NULL, + `is_deleted` tinyint(8) NOT NULL DEFAULT 0, + `delete_time` datetime DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='授权凭证'; @@ -133,6 +141,8 @@ CREATE TABLE `t_machine` ( `update_time` datetime NOT NULL, `modifier` varchar(12) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, `modifier_id` bigint(32) DEFAULT NULL, + `is_deleted` tinyint(8) NOT NULL DEFAULT 0, + `delete_time` datetime DEFAULT NULL, PRIMARY KEY (`id`), KEY `idx_path` (`tag_path`) USING BTREE ) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='机器信息'; @@ -159,6 +169,8 @@ CREATE TABLE `t_machine_file` ( `modifier` varchar(45) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, `create_time` datetime NOT NULL, `update_time` datetime DEFAULT NULL, + `is_deleted` tinyint(8) NOT NULL DEFAULT 0, + `delete_time` datetime DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=39 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='机器文件'; @@ -179,6 +191,8 @@ CREATE TABLE `t_machine_monitor` ( `mem_rate` float(255,2) DEFAULT NULL, `sys_load` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, `create_time` datetime NOT NULL, + `is_deleted` tinyint(8) NOT NULL DEFAULT 0, + `delete_time` datetime DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; @@ -206,6 +220,8 @@ CREATE TABLE `t_machine_script` ( `modifier` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, `create_time` datetime DEFAULT NULL, `update_time` datetime DEFAULT NULL, + `is_deleted` tinyint(8) NOT NULL DEFAULT 0, + `delete_time` datetime DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=27 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='机器脚本'; @@ -213,13 +229,61 @@ CREATE TABLE `t_machine_script` ( -- Records of t_machine_script -- ---------------------------- BEGIN; -INSERT INTO `t_machine_script` VALUES (1, 'sys_info', 9999999, '# 获取系统cpu信息\nfunction get_cpu_info() {\n Physical_CPUs=$(grep \"physical id\" /proc/cpuinfo | sort | uniq | wc -l)\n Virt_CPUs=$(grep \"processor\" /proc/cpuinfo | wc -l)\n CPU_Kernels=$(grep \"cores\" /proc/cpuinfo | uniq | awk -F \': \' \'{print $2}\')\n CPU_Type=$(grep \"model name\" /proc/cpuinfo | awk -F \': \' \'{print $2}\' | sort | uniq)\n CPU_Arch=$(uname -m)\n echo -e \'\\n-------------------------- CPU信息 --------------------------\'\n cat < func (q *QueryCond) Gt(column string, val any) *QueryCond { - q.Cond(consts.Gt, column, val) + q.Cond(consts.Gt, column, val, true) return q } // Ge 大于等于 >= func (q *QueryCond) Ge(column string, val any) *QueryCond { - q.Cond(consts.Ge, column, val) + q.Cond(consts.Ge, column, val, true) return q } // Lt 小于 < func (q *QueryCond) Lt(column string, val any) *QueryCond { - q.Cond(consts.Lt, column, val) + q.Cond(consts.Lt, column, val, true) return q } // Le 小于等于 <= func (q *QueryCond) Le(column string, val any) *QueryCond { - q.Cond(consts.Le, column, val) + q.Cond(consts.Le, column, val, true) return q } -func (q *QueryCond) Cond(cond, column string, val any) *QueryCond { +func (q *QueryCond) Cond(cond, column string, val any, skipBlank bool) *QueryCond { // 零值跳过 - if utils.IsBlank(val) { + if skipBlank && utils.IsBlank(val) { return q } q.columns = append(q.columns, fmt.Sprintf("%s %s ?", column, cond)) diff --git a/server/pkg/model/model.go b/server/pkg/model/model.go index abe5171b..aea99702 100644 --- a/server/pkg/model/model.go +++ b/server/pkg/model/model.go @@ -4,8 +4,25 @@ import ( "time" ) -type Model struct { +const ( + IdColumn = "id" + DeletedColumn = "is_deleted" // 删除字段 + DeleteTimeColumn = "delete_time" + ModelDeleted int8 = 1 + ModelUndeleted int8 = 0 +) + +// 含有删除字段模型 +type DeletedModel struct { Id uint64 `json:"id"` + IsDeleted int8 `json:"-"` + DeleteTime *time.Time `json:"-"` +} + +// 基础实体模型,数据表最基础字段,每张表必备字段 +type Model struct { + DeletedModel + CreateTime *time.Time `json:"createTime"` CreatorId uint64 `json:"creatorId"` Creator string `json:"creator"` @@ -19,6 +36,7 @@ func (m *Model) SetBaseInfo(account *LoginAccount) { nowTime := time.Now() isCreate := m.Id == 0 if isCreate { + m.IsDeleted = ModelUndeleted m.CreateTime = &nowTime } m.UpdateTime = &nowTime diff --git a/server/pkg/scheduler/scheduler.go b/server/pkg/scheduler/scheduler.go index da394b1b..d41b2cad 100644 --- a/server/pkg/scheduler/scheduler.go +++ b/server/pkg/scheduler/scheduler.go @@ -2,6 +2,8 @@ package scheduler import ( "mayfly-go/pkg/biz" + "mayfly-go/pkg/global" + "sync" "github.com/robfig/cron/v3" ) @@ -10,7 +12,10 @@ func init() { Start() } -var cronService = cron.New() +var ( + cronService = cron.New() + key2IdMap sync.Map +) func Start() { cronService.Start() @@ -20,16 +25,40 @@ func Stop() { cronService.Stop() } +// 根据任务id移除 func Remove(id cron.EntryID) { cronService.Remove(id) } +// 根据任务key移除 +func RemoveByKey(key string) { + global.Log.Debugf("移除cron任务 => [key = %s]", key) + id, ok := key2IdMap.Load(key) + if ok { + Remove(id.(cron.EntryID)) + key2IdMap.Delete(key) + } +} + func GetCron() *cron.Cron { return cronService } +// 添加任务 func AddFun(spec string, cmd func()) cron.EntryID { id, err := cronService.AddFunc(spec, cmd) biz.ErrIsNilAppendErr(err, "添加任务失败: %s") return id } + +// 根据key添加定时任务 +func AddFunByKey(key, spec string, cmd func()) { + global.Log.Debugf("添加cron任务 => [key = %s]", key) + RemoveByKey(key) + key2IdMap.Store(key, AddFun(spec, cmd)) +} + +func ExistKey(key string) bool { + _, ok := key2IdMap.Load(key) + return ok +} diff --git a/server/pkg/starter/web-server.go b/server/pkg/starter/web-server.go index bc6cac19..3c7d51bc 100644 --- a/server/pkg/starter/web-server.go +++ b/server/pkg/starter/web-server.go @@ -19,6 +19,9 @@ func runWebServer() { // 注册路由 web := initialize.InitRouter() + // 初始化其他需要启动时运行的方法 + initialize.InitOther() + server := config.Conf.Server port := server.GetPort() global.Log.Infof("Listening and serving HTTP on %s", port) diff --git a/server/pkg/utils/array.go b/server/pkg/utils/array.go index 44910088..0f9bee9d 100644 --- a/server/pkg/utils/array.go +++ b/server/pkg/utils/array.go @@ -6,8 +6,8 @@ import ( // 数组比较 // 依次返回,新增值,删除值,以及不变值 -func ArrayCompare(newArr []any, oldArr []any, compareFun func(any, any) bool) ([]any, []any, []any) { - var unmodifierValue []any +func ArrayCompare[T any](newArr []T, oldArr []T, compareFun func(T, T) bool) ([]T, []T, []T) { + var unmodifierValue []T ni, oi := 0, 0 for { if ni >= len(newArr) { @@ -61,3 +61,23 @@ func ArrContains[T comparable](arr []T, el T) bool { } return false } + +// 数组转为map +// @param keyFunc key的主键 +func Array2Map[T any, K comparable](arr []T, keyFunc func(val T) K) map[K]T { + res := make(map[K]T, len(arr)) + for _, val := range arr { + key := keyFunc(val) + res[key] = val + } + return res +} + +// 数组映射,即将一数组元素通过映射函数转换为另一数组 +func ArrayMap[T any, K comparable](arr []T, mapFunc func(val T) K) []K { + res := make([]K, len(arr)) + for i, val := range arr { + res[i] = mapFunc(val) + } + return res +} diff --git a/server/pkg/utils/net.go b/server/pkg/utils/net.go index 0a5d4fdc..72f0fee1 100644 --- a/server/pkg/utils/net.go +++ b/server/pkg/utils/net.go @@ -1,6 +1,11 @@ package utils -import "net" +import ( + "mayfly-go/pkg/global" + "net" + + "github.com/lionsoul2014/ip2region/binding/golang/xdb" +) // GetAvailablePort 获取可用端口 func GetAvailablePort() (int, error) { @@ -19,3 +24,47 @@ func GetAvailablePort() (int, error) { }(l) return l.Addr().(*net.TCPAddr).Port, nil } + +var ( + // ip2region数据所在路径,可在(https://gitee.com/lionsoul/ip2region/tree/master/data)处下载 + ip2RegionDbPath = "./ip2region.xdb" + useIp2Region = true + vectorIndex []byte +) + +// 获取ip归属地信息 +func Ip2Region(ip string) string { + if !useIp2Region { + return "" + } + + if vectorIndex == nil { + // 1、从 dbPath 加载 VectorIndex 缓存,把下述 vIndex 变量全局到内存里面。 + vIndex, err := xdb.LoadVectorIndexFromFile(ip2RegionDbPath) + // 第一次加载失败,则默认调整为不使用ip2Region + if err != nil { + global.Log.Errorf("failed to load ip2region vector index from `%s`: %s\n", ip2RegionDbPath, err) + useIp2Region = false + return "" + } + + vectorIndex = vIndex + } + + // 2、用全局的 vIndex 创建带 VectorIndex 缓存的查询对象。 + searcher, err := xdb.NewWithVectorIndex(ip2RegionDbPath, vectorIndex) + if err != nil { + global.Log.Errorf("failed to create searcher with vector index: %s\n", err) + return "" + } + + defer searcher.Close() + + // do the search + region, err := searcher.SearchByStr(ip) + if err != nil { + global.Log.Warnf("failed to SearchIP(%s): %s\n", ip, err) + return "" + } + return region +}