From aa393590b250fa40f0524a1fc22f9bb79609614e Mon Sep 17 00:00:00 2001 From: "meilin.huang" <954537473@qq.com> Date: Thu, 13 Feb 2025 21:11:23 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E7=B3=BB=E7=BB=9F=E5=8D=87=E7=BA=A7?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E6=95=B0=E6=8D=AE=E5=BA=93=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E8=BF=81=E7=A7=BB=EF=BC=8C=E9=81=BF=E5=85=8D=E6=89=8B=E5=8A=A8?= =?UTF-8?q?=E6=89=A7=E8=A1=8C=E5=8D=87=E7=BA=A7=E8=84=9A=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build_release.sh | 4 +- frontend/package.json | 18 +- frontend/src/common/config.ts | 4 +- frontend/src/common/utils/object.ts | 64 + frontend/src/common/utils/string.ts | 9 + frontend/src/i18n/index.ts | 1 + frontend/src/views/flow/ProcdefEdit.vue | 2 +- frontend/src/views/ops/db/api.ts | 2 +- .../ops/db/component/table/DbTableData.vue | 12 +- frontend/vite.config.ts | 7 +- server/config.yml.example | 2 - server/go.mod | 36 +- server/internal/auth/domain/entity/oauth2.go | 8 +- .../internal/db/application/db_data_sync.go | 11 +- server/internal/db/domain/entity/db.go | 14 +- .../internal/db/domain/entity/db_data_sync.go | 54 +- .../internal/db/domain/entity/db_instance.go | 17 +- server/internal/db/domain/entity/db_sql.go | 11 +- .../internal/db/domain/entity/db_sql_exec.go | 20 +- .../internal/db/domain/entity/db_transfer.go | 47 +- .../db/domain/entity/db_transfer_file.go | 15 +- server/internal/file/domain/entity/file.go | 6 +- server/internal/flow/domain/entity/procdef.go | 12 +- .../internal/flow/domain/entity/procinst.go | 40 +- .../internal/machine/domain/entity/machine.go | 18 +- .../machine/domain/entity/machine_cmd_conf.go | 12 +- .../machine/domain/entity/machine_cronjob.go | 26 +- .../machine/domain/entity/machine_file.go | 10 +- .../machine/domain/entity/machine_script.go | 13 +- .../machine/domain/entity/machine_term_op.go | 16 +- server/internal/mongo/domain/entity/mongo.go | 8 +- server/internal/msg/domain/entity/msg.go | 8 +- server/internal/redis/domain/entity/redis.go | 14 +- server/internal/sys/api/form/resource.go | 2 +- server/internal/sys/domain/entity/account.go | 12 +- server/internal/sys/domain/entity/config.go | 12 +- server/internal/sys/domain/entity/resource.go | 14 +- server/internal/sys/domain/entity/role.go | 30 +- server/internal/sys/domain/entity/syslog.go | 10 +- server/internal/tag/api/form/auth_cert.go | 2 +- .../tag/application/resouce_auth_cert.go | 11 +- .../tag/domain/entity/resource_auth_cert.go | 16 +- .../tag/domain/entity/resource_op_log.go | 6 +- server/internal/tag/domain/entity/tag_tree.go | 10 +- .../tag/domain/entity/tag_tree_relate.go | 6 +- server/internal/tag/domain/entity/team.go | 8 +- .../internal/tag/domain/entity/team_member.go | 6 +- server/migration/migration.go | 53 + server/migration/migrations/init.go | 1447 +++++++++++++++++ server/migration/migrations/v1_9.go | 42 + server/migrations/2022.go | 102 -- server/migrations/20230720.go | 21 - server/migrations/20231115.go | 32 - server/migrations/init.go | 74 - server/pkg/config/mysql.go | 19 +- server/pkg/model/model.go | 30 +- server/pkg/starter/run.go | 6 +- server/resources/data/mayfly-go.sqlite | Bin 335872 -> 0 bytes server/resources/script/sql/mayfly-go.sql | 16 +- server/resources/script/sql/v1.5/v1.5.3.sql | 8 - server/resources/script/sql/v1.5/v1.5.4.sql | 2 - server/resources/script/sql/v1.6/v1.6.0.sql | 194 --- server/resources/script/sql/v1.7/v1.7.0.sql | 240 --- server/resources/script/sql/v1.7/v1.7.1.sql | 3 - server/resources/script/sql/v1.7/v1.7.2.sql | 12 - server/resources/script/sql/v1.7/v1.7.3.sql | 8 - server/resources/script/sql/v1.7/v1.7.4.sql | 92 -- server/resources/script/sql/v1.8/v1.8.0.sql | 247 --- server/resources/script/sql/v1.8/v1.8.1.sql | 150 -- server/resources/script/sql/v1.8/v1.8.2.sql | 84 - server/resources/script/sql/v1.8/v1.8.3.sql | 3 - server/resources/script/sql/v1.8/v1.8.4.sql | 4 - server/resources/script/sql/v1.8/v1.8.5.sql | 6 - server/resources/script/sql/v1.8/v1.8.6.sql | 7 - server/resources/script/sql/v1.9/v1.9.0.sql | 72 - server/resources/script/sql/v1.9/v1.9.1.sql | 121 -- server/resources/script/sql/v1.9/v1.9.2.sql | 3 - server/resources/script/sql/v1.9/v1.9.3.sql | 8 - 78 files changed, 1965 insertions(+), 1827 deletions(-) create mode 100644 server/migration/migration.go create mode 100644 server/migration/migrations/init.go create mode 100644 server/migration/migrations/v1_9.go delete mode 100644 server/migrations/2022.go delete mode 100644 server/migrations/20230720.go delete mode 100644 server/migrations/20231115.go delete mode 100644 server/migrations/init.go delete mode 100644 server/resources/data/mayfly-go.sqlite delete mode 100644 server/resources/script/sql/v1.5/v1.5.3.sql delete mode 100644 server/resources/script/sql/v1.5/v1.5.4.sql delete mode 100644 server/resources/script/sql/v1.6/v1.6.0.sql delete mode 100644 server/resources/script/sql/v1.7/v1.7.0.sql delete mode 100644 server/resources/script/sql/v1.7/v1.7.1.sql delete mode 100644 server/resources/script/sql/v1.7/v1.7.2.sql delete mode 100644 server/resources/script/sql/v1.7/v1.7.3.sql delete mode 100644 server/resources/script/sql/v1.7/v1.7.4.sql delete mode 100644 server/resources/script/sql/v1.8/v1.8.0.sql delete mode 100644 server/resources/script/sql/v1.8/v1.8.1.sql delete mode 100644 server/resources/script/sql/v1.8/v1.8.2.sql delete mode 100644 server/resources/script/sql/v1.8/v1.8.3.sql delete mode 100644 server/resources/script/sql/v1.8/v1.8.4.sql delete mode 100644 server/resources/script/sql/v1.8/v1.8.5.sql delete mode 100644 server/resources/script/sql/v1.8/v1.8.6.sql delete mode 100644 server/resources/script/sql/v1.9/v1.9.0.sql delete mode 100644 server/resources/script/sql/v1.9/v1.9.1.sql delete mode 100644 server/resources/script/sql/v1.9/v1.9.2.sql delete mode 100644 server/resources/script/sql/v1.9/v1.9.3.sql diff --git a/build_release.sh b/build_release.sh index 6948760a..e25f723f 100755 --- a/build_release.sh +++ b/build_release.sh @@ -75,14 +75,12 @@ function build() { # fi if [ "${copyDocScript}" == "1" ] ; then - echo_green "Copy resources such as scripts [config.yml.example、mayfly-go.sql、mayfly-go.sqlite、readme.txt、startup.sh、shutdown.sh]" + echo_green "Copy resources such as scripts [config.yml.example、readme.txt、startup.sh、shutdown.sh]" cp ${server_folder}/config.yml.example ${toFolder} cp ${server_folder}/readme.txt ${toFolder} cp ${server_folder}/readme_en.txt ${toFolder} cp ${server_folder}/resources/script/startup.sh ${toFolder} cp ${server_folder}/resources/script/shutdown.sh ${toFolder} - cp ${server_folder}/resources/script/sql/mayfly-go.sql ${toFolder} - cp ${server_folder}/resources/data/mayfly-go.sqlite ${toFolder} fi echo_yellow ">>>>>>>>>>>>>>>>>>> ${os}-${arch} - Bundle build complete <<<<<<<<<<<<<<<<<<<<\n" diff --git a/frontend/package.json b/frontend/package.json index 6279a6ec..cd863234 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -11,15 +11,15 @@ }, "dependencies": { "@element-plus/icons-vue": "^2.3.1", - "@vueuse/core": "^12.4.0", - "asciinema-player": "^3.8.1", + "@vueuse/core": "^12.5.0", + "asciinema-player": "^3.9.0", "axios": "^1.6.2", "clipboard": "^2.0.11", "cropperjs": "^1.6.1", "crypto-js": "^4.2.0", "dayjs": "^1.11.13", "echarts": "^5.6.0", - "element-plus": "^2.9.3", + "element-plus": "^2.9.4", "js-base64": "^3.7.7", "jsencrypt": "^3.3.2", "lodash": "^4.17.21", @@ -28,16 +28,16 @@ "monaco-sql-languages": "^0.12.2", "monaco-themes": "^0.4.4", "nprogress": "^0.2.0", - "pinia": "^2.3.0", + "pinia": "^2.3.1", "qrcode.vue": "^3.6.0", "screenfull": "^6.0.2", "sortablejs": "^1.15.6", - "splitpanes": "^3.1.5", - "sql-formatter": "^15.4.5", + "splitpanes": "^3.1.8", + "sql-formatter": "^15.4.10", "trzsz": "^1.1.5", "uuid": "^9.0.1", "vue": "^3.5.13", - "vue-i18n": "^11.0.1", + "vue-i18n": "^11.1.0", "vue-router": "^4.5.0", "vuedraggable": "^4.1.0", "xterm": "^5.3.0", @@ -60,9 +60,9 @@ "eslint": "^8.35.0", "eslint-plugin-vue": "^9.31.0", "prettier": "^3.2.5", - "sass": "^1.83.4", + "sass": "^1.84.0", "typescript": "^5.7.3", - "vite": "^6.0.7", + "vite": "^6.1.0", "vite-plugin-progress": "0.0.7", "vue-eslint-parser": "^9.4.3" }, diff --git a/frontend/src/common/config.ts b/frontend/src/common/config.ts index 855e674b..a638a2ec 100644 --- a/frontend/src/common/config.ts +++ b/frontend/src/common/config.ts @@ -1,4 +1,4 @@ -function getBaseApiUrl() { +export function getBaseApiUrl() { let path = window.location.pathname; if (path == '/') { return window.location.host; @@ -15,7 +15,7 @@ const config = { baseWsUrl: `${(window as any).globalConfig.BaseWsUrl || `${location.protocol == 'https:' ? 'wss:' : 'ws:'}//${getBaseApiUrl()}`}/api`, // 系统版本 - version: 'v1.9.2', + version: 'v1.9.3', }; export default config; diff --git a/frontend/src/common/utils/object.ts b/frontend/src/common/utils/object.ts index 8d6087fe..1ad663d2 100644 --- a/frontend/src/common/utils/object.ts +++ b/frontend/src/common/utils/object.ts @@ -54,3 +54,67 @@ export function getValueByPath(obj: any, path: string) { return result; } + +/** + * 根据字段路径设置字段值,若路径不存在,则建对应的路径对象信息 + * @param obj 对象 + * @param path 字段路径 + * @param value 字段值 + */ +export function setValueByPath(obj: any, path: string[], value: any) { + for (let i = 0; i < path.length - 1; i++) { + const key = path[i]; + if (!obj[key]) { + obj[key] = {}; + } + obj = obj[key]; + } + obj[path[path.length - 1]] = value; +} + +/** + * 使用递归函数进行深度克隆,并支持通过回调函数进行指定字段值的调整 + * + * @param obj 要克隆的对象 + * @param callback 回调函数,在每个字段被克隆之前调用,可以调整字段的值 + * @param hash 用于处理循环引用的 WeakMap + * @returns 深度克隆后的对象 + */ +export function deepClone( + obj: any, + callback: (key: string | number, value: any) => any = (key: string | number, value: any) => value, + hash = new WeakMap() +): any { + if (Object(obj) !== obj) return obj; // 基本数据类型直接返回 + if (hash.has(obj)) return hash.get(obj); // 处理循环引用 + + let result: any; + + if (obj instanceof Set) { + result = new Set(); + hash.set(obj, result); + obj.forEach((val) => result.add(deepClone(val, callback, hash))); + } else if (obj instanceof Map) { + result = new Map(); + hash.set(obj, result); + obj.forEach((val, key) => result.set(key, deepClone(val, callback, hash))); + } else if (obj instanceof Date) { + result = new Date(obj.getTime()); + } else if (obj instanceof RegExp) { + result = new RegExp(obj); + } else if (typeof obj === 'object') { + result = Array.isArray(obj) ? [] : {}; + hash.set(obj, result); + for (let key in obj) { + if (obj.hasOwnProperty(key)) { + let value = obj[key]; + value = callback(key, value); + result[key] = deepClone(value, callback, hash); + } + } + } else { + result = obj; + } + + return result; +} diff --git a/frontend/src/common/utils/string.ts b/frontend/src/common/utils/string.ts index c7d06b92..000434cf 100644 --- a/frontend/src/common/utils/string.ts +++ b/frontend/src/common/utils/string.ts @@ -203,6 +203,15 @@ export function randomPassword(length = 10) { return password.join(''); } +export function randomString(length = 8) { + const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + let result = ''; + for (let i = 0; i < length; i++) { + result += getRandomChar(chars); + } + return result; +} + function getRandomChar(charSet: string) { const randomIndex = Math.floor(Math.random() * charSet.length); return charSet[randomIndex]; diff --git a/frontend/src/i18n/index.ts b/frontend/src/i18n/index.ts index 1c3892fd..5a0f5e7a 100644 --- a/frontend/src/i18n/index.ts +++ b/frontend/src/i18n/index.ts @@ -42,6 +42,7 @@ function initI18n() { // https://vue-i18n.intlify.dev/guide/essentials/fallback.html#explicit-fallback-with-one-locale return createI18n({ legacy: false, + globalInjection: true, // 在所有组件中都可以使用 $i18n $t $rt $d $n $tm silentTranslationWarn: true, missingWarn: false, silentFallbackWarn: true, diff --git a/frontend/src/views/flow/ProcdefEdit.vue b/frontend/src/views/flow/ProcdefEdit.vue index 1562680e..0169eb2d 100755 --- a/frontend/src/views/flow/ProcdefEdit.vue +++ b/frontend/src/views/flow/ProcdefEdit.vue @@ -57,7 +57,7 @@ diff --git a/frontend/src/views/ops/db/api.ts b/frontend/src/views/ops/db/api.ts index dcce5e46..b43e2ea5 100644 --- a/frontend/src/views/ops/db/api.ts +++ b/frontend/src/views/ops/db/api.ts @@ -95,7 +95,7 @@ const encryptField = async (param: any, field: string) => { if (process.env.NODE_ENV === 'development') { console.log(param[field]); } - // 使用rsa公钥加密sql + // 使用aes加密sql param['_encrypted'] = 1; param[field] = AesEncrypt(param[field]); // console.log('解密结果', DesDecrypt(param[field])); diff --git a/frontend/src/views/ops/db/component/table/DbTableData.vue b/frontend/src/views/ops/db/component/table/DbTableData.vue index 8227ee4f..aaa4918d 100644 --- a/frontend/src/views/ops/db/component/table/DbTableData.vue +++ b/frontend/src/views/ops/db/component/table/DbTableData.vue @@ -235,14 +235,22 @@ const cmHeaderDesc = new ContextmenuItem('desc', 'db.desc') const cmHeaderFixed = new ContextmenuItem('fixed', 'db.fixed') .withIcon('Paperclip') .withOnClick((data: any) => { - data.fixed = true; + state.columns.forEach((column: any) => { + if (column.dataKey == data.dataKey) { + column.fixed = true; + } + }); }) .withHideFunc((data: any) => data.fixed); const cmHeaderCancelFixed = new ContextmenuItem('cancelFixed', 'db.cancelFiexd') .withIcon('Minus') .withOnClick((data: any) => { - data.fixed = false; + state.columns.forEach((column: any) => { + if (column.dataKey == data.dataKey) { + column.fixed = false; + } + }); }) .withHideFunc((data: any) => !data.fixed); diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index 5264e60f..1c9ff88f 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -11,6 +11,8 @@ const pathResolve = (dir: string): any => { const { VITE_PORT, VITE_OPEN, VITE_PUBLIC_PATH, VITE_EDITOR } = loadEnv(); +const isProd = process.env.NODE_ENV === 'production'; + const alias: Record = { '@': pathResolve('src/'), }; @@ -28,7 +30,7 @@ const viteConfig: UserConfig = { resolve: { alias, }, - base: process.env.NODE_ENV === 'production' ? VITE_PUBLIC_PATH : './', + base: isProd ? VITE_PUBLIC_PATH : './', optimizeDeps: { include: ['element-plus/es/locale/lang/zh-cn'], }, @@ -63,6 +65,9 @@ const viteConfig: UserConfig = { }, }, }, + esbuild: { + drop: isProd ? ['console', 'debugger'] : [], + }, define: { __VUE_I18N_LEGACY_API__: JSON.stringify(false), __VUE_I18N_FULL_INSTALL__: JSON.stringify(false), diff --git a/server/config.yml.example b/server/config.yml.example index f5d639ba..93f9460f 100644 --- a/server/config.yml.example +++ b/server/config.yml.example @@ -23,8 +23,6 @@ aes: key: 1111111111111111 # 若存在mysql配置,优先使用mysql mysql: - # 自动升级数据库 - auto-migration: false host: mysql:3306 username: root password: 111049 diff --git a/server/go.mod b/server/go.mod index 84ea6efc..3ec2890b 100644 --- a/server/go.mod +++ b/server/go.mod @@ -1,6 +1,6 @@ module mayfly-go -go 1.23.0 +go 1.24 require ( gitee.com/chunanyong/dm v1.8.18 @@ -9,7 +9,7 @@ require ( github.com/emirpasic/gods v1.18.1 github.com/gin-gonic/gin v1.10.0 github.com/glebarez/sqlite v1.11.0 - github.com/go-gormigrate/gormigrate/v2 v2.1.0 + github.com/go-gormigrate/gormigrate/v2 v2.1.3 github.com/go-ldap/ldap/v3 v3.4.8 github.com/go-playground/locales v0.14.1 github.com/go-playground/universal-translator v0.18.1 @@ -20,21 +20,21 @@ require ( github.com/gorilla/websocket v1.5.3 github.com/lionsoul2014/ip2region/binding/golang v0.0.0-20230712084735-068dc2aee82d github.com/may-fly/cast v1.7.1 - github.com/microsoft/go-mssqldb v1.7.2 - github.com/mojocn/base64Captcha v1.3.6 // 验证码 + github.com/microsoft/go-mssqldb v1.8.0 + github.com/mojocn/base64Captcha v1.3.8 // 验证码 github.com/pkg/errors v0.9.1 github.com/pkg/sftp v1.13.7 github.com/pquerna/otp v1.4.0 github.com/redis/go-redis/v9 v9.7.0 github.com/robfig/cron/v3 v3.0.1 // 定时任务 - github.com/sijms/go-ora/v2 v2.8.22 - github.com/stretchr/testify v1.9.0 + github.com/sijms/go-ora/v2 v2.8.23 + github.com/stretchr/testify v1.10.0 github.com/tidwall/gjson v1.18.0 github.com/veops/go-ansiterm v0.0.5 go.mongodb.org/mongo-driver v1.16.0 // mongo - golang.org/x/crypto v0.32.0 // ssh - golang.org/x/oauth2 v0.25.0 - golang.org/x/sync v0.10.0 + golang.org/x/crypto v0.33.0 // ssh + golang.org/x/oauth2 v0.26.0 + golang.org/x/sync v0.11.0 gopkg.in/natefinch/lumberjack.v2 v2.2.1 gopkg.in/yaml.v3 v3.0.1 // gorm @@ -71,15 +71,15 @@ require ( github.com/kr/fs v0.1.0 // indirect github.com/leodido/go-urn v1.4.0 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/mattn/go-runewidth v0.0.15 // indirect - github.com/mattn/go-sqlite3 v1.14.17 // indirect + github.com/mattn/go-runewidth v0.0.16 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/montanaflynn/stats v0.7.1 // indirect github.com/pelletier/go-toml/v2 v2.2.2 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect - github.com/rivo/uniseg v0.4.3 // indirect + github.com/rivo/uniseg v0.4.7 // indirect + github.com/rogpeppe/go-internal v1.11.0 // indirect github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.1 // indirect github.com/tjfoc/gmsm v1.4.1 // indirect @@ -89,12 +89,12 @@ require ( github.com/xdg-go/scram v1.1.2 // indirect github.com/xdg-go/stringprep v1.0.4 // indirect github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect - golang.org/x/arch v0.8.0 // indirect - golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e // indirect - golang.org/x/image v0.13.0 // indirect - golang.org/x/net v0.25.0 // indirect - golang.org/x/sys v0.29.0 // indirect - golang.org/x/text v0.21.0 // indirect + golang.org/x/arch v0.14.0 // indirect + golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac // indirect + golang.org/x/image v0.23.0 // indirect + golang.org/x/net v0.33.0 // indirect + golang.org/x/sys v0.30.0 // indirect + golang.org/x/text v0.22.0 // indirect google.golang.org/protobuf v1.34.1 // indirect modernc.org/libc v1.22.5 // indirect modernc.org/mathutil v1.5.0 // indirect diff --git a/server/internal/auth/domain/entity/oauth2.go b/server/internal/auth/domain/entity/oauth2.go index 9daabf88..0a4545f1 100644 --- a/server/internal/auth/domain/entity/oauth2.go +++ b/server/internal/auth/domain/entity/oauth2.go @@ -8,11 +8,11 @@ import ( type Oauth2Account struct { model.DeletedModel - AccountId uint64 `json:"accountId" gorm:"column:account_id;index:account_id,unique"` - Identity string `json:"identity" gorm:"column:identity;index:identity,unique"` + AccountId uint64 `json:"accountId" gorm:"not null;column:account_id;index:account_id,unique;comment:账号ID"` + Identity string `json:"identity" gorm:"size:64;column:identity;index:idx_identity,unique;comment:身份标识"` - CreateTime *time.Time `json:"createTime"` - UpdateTime *time.Time `json:"updateTime"` + CreateTime *time.Time `json:"createTime" gorm:"not null;"` + UpdateTime *time.Time `json:"updateTime" gorm:"not null;"` } func (Oauth2Account) TableName() string { diff --git a/server/internal/db/application/db_data_sync.go b/server/internal/db/application/db_data_sync.go index 789ba265..c1e71a41 100644 --- a/server/internal/db/application/db_data_sync.go +++ b/server/internal/db/application/db_data_sync.go @@ -159,7 +159,7 @@ func (app *dataSyncAppImpl) RunCronJob(ctx context.Context, id uint64) error { break } } - return errorx.NewBiz("get column data type") + return errorx.NewBiz("get column data type... ignore~") }) updSql = fmt.Sprintf("and %s > %s", task.UpdField, updFieldDataType.DataType.SQLValue(task.UpdFieldVal)) @@ -394,7 +394,7 @@ func (app *dataSyncAppImpl) saveLog(log *entity.DataSyncLog) { func (app *dataSyncAppImpl) InitCronJob() { defer func() { if err := recover(); err != nil { - logx.ErrorTrace("the data synchronization task failed to initialize: %s", err.(error)) + logx.ErrorTrace("the data synchronization task failed to initialize", err) } }() @@ -410,7 +410,12 @@ func (app *dataSyncAppImpl) InitCronJob() { cond.Status = entity.DataSyncTaskStatusEnable jobs := new([]entity.DataSyncTask) - pr, _ := app.GetPageList(cond, pageParam, jobs) + pr, err := app.GetPageList(cond, pageParam, jobs) + if err != nil { + logx.ErrorTrace("the data synchronization task failed to initialize", err) + return + } + total := pr.Total add := 0 diff --git a/server/internal/db/domain/entity/db.go b/server/internal/db/domain/entity/db.go index bdba70ef..541d379d 100644 --- a/server/internal/db/domain/entity/db.go +++ b/server/internal/db/domain/entity/db.go @@ -7,13 +7,13 @@ import ( type Db struct { model.Model - Code string `orm:"column(code)" json:"code"` - Name string `orm:"column(name)" json:"name"` - GetDatabaseMode DbGetDatabaseMode `json:"getDatabaseMode"` // 获取数据库方式 - Database string `orm:"column(database)" json:"database"` - Remark string `json:"remark"` - InstanceId uint64 - AuthCertName string `json:"authCertName"` + Code string `json:"code" gorm:"size:32;not null;index:idx_code"` + Name string `json:"name" gorm:"size:255;not null;"` + GetDatabaseMode DbGetDatabaseMode `json:"getDatabaseMode" gorm:"comment:库名获取方式(-1.实时获取、1.指定库名)"` // 获取数据库方式 + Database string `json:"database" gorm:"size:2000;"` + Remark string `json:"remark" gorm:"size:255;"` + InstanceId uint64 `json:"instanceId" gorm:"not null;"` + AuthCertName string `json:"authCertName" gorm:"size:255;"` } type DbGetDatabaseMode int8 diff --git a/server/internal/db/domain/entity/db_data_sync.go b/server/internal/db/domain/entity/db_data_sync.go index 23b996fe..13a13deb 100644 --- a/server/internal/db/domain/entity/db_data_sync.go +++ b/server/internal/db/domain/entity/db_data_sync.go @@ -5,34 +5,35 @@ import ( "time" ) +// DataSyncTask 数据同步 type DataSyncTask struct { model.Model // 基本信息 - TaskName string `orm:"column(task_name)" json:"taskName"` // 任务名 - TaskCron string `orm:"column(task_cron)" json:"taskCron"` // 任务Cron表达式 - Status int8 `orm:"column(status)" json:"status"` // 状态 1启用 2禁用 - TaskKey string `orm:"column(key)" json:"taskKey"` // 任务唯一标识 - RecentState int8 `orm:"column(recent_state)" json:"recentState"` // 最近执行状态 1成功 -1失败 - RunningState int8 `orm:"column(running_state)" json:"runningState"` // 运行时状态 1运行中、2待运行、3已停止 + TaskName string `json:"taskName" gorm:"not null;size:255;comment:任务名"` // 任务名 + TaskCron string `json:"taskCron" gorm:"not null;size:50;comment:任务Cron表达式"` // 任务Cron表达式 + Status int8 `json:"status" gorm:"not null;default:1;comment:状态 1启用 2禁用"` // 状态 1启用 2禁用 + TaskKey string `json:"taskKey" gorm:"size:100;comment:任务唯一标识"` // 任务唯一标识 + RecentState int8 `json:"recentState" gorm:"not null;default:0;comment:最近执行状态 1成功 -1失败"` // 最近执行状态 1成功 -1失败 + RunningState int8 `json:"runningState" gorm:"not null;default:2;comment:运行时状态 1运行中、2待运行、3已停止"` // 运行时状态 1运行中、2待运行、3已停止 // 源数据库信息 - SrcDbId int64 `orm:"column(src_db_id)" json:"srcDbId"` - SrcDbName string `orm:"column(src_db_name)" json:"srcDbName"` - SrcTagPath string `orm:"column(src_tag_path)" json:"srcTagPath"` - DataSql string `orm:"column(data_sql)" json:"dataSql"` // 数据源查询sql - PageSize int `orm:"column(page_size)" json:"pageSize"` // 配置分页sql查询的条数 - UpdField string `orm:"column(upd_field)" json:"updField"` // 更新字段, 选择由哪个字段为更新字段,查询数据源的时候会带上这个字段,如:where update_time > {最近更新的最大值} - UpdFieldVal string `orm:"column(upd_field_val)" json:"updFieldVal"` // 更新字段当前值 - UpdFieldSrc string `orm:"column(upd_field_src)" json:"updFieldSrc"` // 更新值来源, 如select name as user_name from user; 则updFieldSrc的值为user_name + SrcDbId int64 `json:"srcDbId" gorm:"not null;comment:源数据库ID"` // 源数据库ID + SrcDbName string `json:"srcDbName" gorm:"size:100;comment:源数据库名"` // 源数据库名 + SrcTagPath string `json:"srcTagPath" gorm:"size:200;comment:源数据库tag路径"` // 源数据库tag路径 + DataSql string `json:"dataSql" gorm:"not null;type:text;comment:数据查询sql"` // 数据源查询sql + PageSize int `json:"pageSize" gorm:"not null;comment:数据同步分页大小"` // 配置分页sql查询的条数 + UpdField string `json:"updField" gorm:"not null;size:100;default:'id';comment:更新字段,默认'id'"` // 更新字段, 选择由哪个字段为更新字段,查询数据源的时候会带上这个字段,如:where update_time > {最近更新的最大值} + UpdFieldVal string `json:"updFieldVal" gorm:"size:100;comment:当前更新值"` // 更新字段当前值 + UpdFieldSrc string `json:"updFieldSrc" gorm:"comment:更新值来源, 如select name as user_name from user; 则updFieldSrc的值为user_name"` // 更新值来源, 如select name as user_name from user; 则updFieldSrc的值为user_name // 目标数据库信息 - TargetDbId int64 `orm:"column(target_db_id)" json:"targetDbId"` - TargetDbName string `orm:"column(target_db_name)" json:"targetDbName"` - TargetTagPath string `orm:"column(target_tag_path)" json:"targetTagPath"` - TargetTableName string `orm:"column(target_table_name)" json:"targetTableName"` - FieldMap string `orm:"column(field_map)" json:"fieldMap"` // 字段映射json - DuplicateStrategy int `orm:"column(duplicate_strategy)" json:"duplicateStrategy"` // 冲突策略 -1:无,1:忽略,2:覆盖 + TargetDbId int64 `json:"targetDbId" gorm:"not null;comment:目标数据库ID"` // 目标数据库ID + TargetDbName string `json:"targetDbName" gorm:"size:150;comment:目标数据库名"` // 目标数据库名 + TargetTagPath string `json:"targetTagPath" gorm:"size:255;comment:目标数据库tag路径"` // 目标数据库tag路径 + TargetTableName string `json:"targetTableName" gorm:"size:150;comment:目标数据库表名"` // 目标数据库表名 + FieldMap string `json:"fieldMap" gorm:"type:text;comment:字段映射json"` // 字段映射json + DuplicateStrategy int `json:"duplicateStrategy" gorm:"not null;default:-1;comment:唯一键冲突策略 -1:无,1:忽略,2:覆盖"` // 冲突策略 -1:无,1:忽略,2:覆盖 } func (d *DataSyncTask) TableName() string { @@ -41,12 +42,13 @@ func (d *DataSyncTask) TableName() string { type DataSyncLog struct { model.IdModel - TaskId uint64 `orm:"column(task_id)" json:"taskId"` // 任务表id - CreateTime *time.Time `orm:"column(create_time)" json:"createTime"` - DataSqlFull string `orm:"column(data_sql_full)" json:"dataSqlFull"` // 执行的完整sql - ResNum int `orm:"column(res_num)" json:"resNum"` // 收到数据条数 - ErrText string `orm:"column(err_text)" json:"errText"` // 错误日志 - Status int8 `orm:"column(status)" json:"status"` // 状态:1.成功 -1.失败 + + CreateTime *time.Time `json:"createTime" gorm:"not null;"` // 创建时间 + TaskId uint64 `json:"taskId" gorm:"not null;comment:同步任务表id"` // 任务表id + DataSqlFull string `json:"dataSqlFull" gorm:"not null;type:text;comment:执行的完整sql"` // 执行的完整sql + ResNum int `json:"resNum" gorm:"comment:收到数据条数"` // 收到数据条数 + ErrText string `json:"errText" gorm:"type:text;comment:日志"` // 日志 + Status int8 `json:"status" gorm:"not null;default:1;comment:状态:1.成功 0.失败"` // 状态:1.成功 0.失败 } func (d *DataSyncLog) TableName() string { diff --git a/server/internal/db/domain/entity/db_instance.go b/server/internal/db/domain/entity/db_instance.go index 2e94f0da..c12cfa25 100644 --- a/server/internal/db/domain/entity/db_instance.go +++ b/server/internal/db/domain/entity/db_instance.go @@ -5,18 +5,19 @@ import ( "mayfly-go/pkg/model" ) +// DbInstance 数据库实例信息 type DbInstance struct { model.Model - Code string `json:"code"` - Name string `json:"name"` - Type string `json:"type"` // 类型,mysql oracle等 - Host string `json:"host"` + Code string `json:"code" gorm:"size:32;not null;"` + Name string `json:"name" gorm:"size:32;not null;"` + Type string `json:"type" gorm:"size:32;not null;"` // 类型,mysql oracle等 + Host string `json:"host" gorm:"size:255;not null;"` Port int `json:"port"` - Network string `json:"network"` - Extra *string `json:"extra"` // 连接需要的其他额外参数(json格式), 如oracle需要sid等 - Params *string `json:"params"` // 使用指针类型,可更新为零值(空字符串) - Remark *string `json:"remark"` + Network string `json:"network" gorm:"size:20;"` + Extra *string `json:"extra" gorm:"size:1000;comment:连接需要的额外参数,如oracle数据库需要sid等"` // 连接需要的其他额外参数(json格式), 如oracle需要sid等 + Params *string `json:"params" gorm:"size:255;comment:其他连接参数"` // 使用指针类型,可更新为零值(空字符串) + Remark *string `json:"remark" gorm:"size:255;"` SshTunnelMachineId int `json:"sshTunnelMachineId"` // ssh隧道机器id } diff --git a/server/internal/db/domain/entity/db_sql.go b/server/internal/db/domain/entity/db_sql.go index a2b8b128..253590c8 100644 --- a/server/internal/db/domain/entity/db_sql.go +++ b/server/internal/db/domain/entity/db_sql.go @@ -4,12 +4,13 @@ import ( "mayfly-go/pkg/model" ) +// DbSql 用户保存的数据库sql type DbSql struct { model.Model `orm:"-"` - DbId uint64 `json:"dbId"` - Db string `json:"db"` - Type int `json:"type"` // 类型 - Sql string `json:"sql"` - Name string `json:"name"` + DbId uint64 `json:"dbId" gorm:"not null;"` + Db string `json:"db" gorm:"size:100;not null;"` + Type int `json:"type" gorm:"not null;"` // 类型 + Sql string `json:"sql" gorm:"size:4000;comment:sql语句"` + Name string `json:"name" gorm:"size:255;not null;comment:sql模板名"` } diff --git a/server/internal/db/domain/entity/db_sql_exec.go b/server/internal/db/domain/entity/db_sql_exec.go index e29b21c8..e50d2fd7 100644 --- a/server/internal/db/domain/entity/db_sql_exec.go +++ b/server/internal/db/domain/entity/db_sql_exec.go @@ -8,17 +8,17 @@ import ( type DbSqlExec struct { model.Model `orm:"-"` - DbId uint64 `json:"dbId"` - Db string `json:"db"` - Table string `json:"table"` - Type int8 `json:"type"` // 类型 - Sql string `json:"sql"` // 执行的sql - OldValue string `json:"oldValue"` - Remark string `json:"remark"` - Status int8 `json:"status"` // 执行状态 - Res string `json:"res"` // 执行结果 + DbId uint64 `json:"dbId" gorm:"not null;"` + Db string `json:"db" gorm:"size:150;not null;"` + Table string `json:"table" gorm:"size:150;"` + Type int8 `json:"type" gorm:"not null;"` // 类型 + Sql string `json:"sql" gorm:"size:5000;not null;"` // 执行的sql + OldValue string `json:"oldValue" gorm:"size:5000;"` + Remark string `json:"remark" gorm:"size:255;"` + Status int8 `json:"status"` // 执行状态 + Res string `json:"res" gorm:"size:1000;"` // 执行结果 - FlowBizKey string `json:"flowBizKey"` // 流程业务key + FlowBizKey string `json:"flowBizKey" gorm:"size:50;index:idx_flow_biz_key;comment:流程关联的业务key"` // 流程业务key } const ( diff --git a/server/internal/db/domain/entity/db_transfer.go b/server/internal/db/domain/entity/db_transfer.go index aa51604e..a6c8854b 100644 --- a/server/internal/db/domain/entity/db_transfer.go +++ b/server/internal/db/domain/entity/db_transfer.go @@ -7,34 +7,33 @@ import ( type DbTransferTask struct { model.Model - RunningState int8 `orm:"column(running_state)" json:"runningState"` // 运行状态 + TaskName string `json:"taskName" gorm:"size:255;not null;"` // 任务名称 + TaskKey string `json:"taskKey" gorm:"size:100;not null;"` // 定时任务唯一uuid key + CronAble int8 `json:"cronAble" gorm:"default:-1;not null;"` // 是否定时 1是 -1否 + Cron string `json:"cron" gorm:"size:32;"` // 定时任务cron表达式 + Mode int8 `json:"mode"` // 数据迁移方式,1、迁移到数据库 2、迁移到文件 + TargetFileDbType string `json:"targetFileDbType" gorm:"size:32;"` // 目标文件数据库类型 + FileSaveDays int `json:"fileSaveDays"` // 文件保存天数 + Status int8 `json:"status"` // 启用状态 1启用 -1禁用 + RunningState int8 `json:"runningState"` // 运行状态 LogId uint64 `json:"logId"` - TaskName string `orm:"column(task_name)" json:"taskName"` // 任务名称 - Status int8 `orm:"column(status)" json:"status"` // 启用状态 1启用 -1禁用 - CronAble int8 `orm:"column(cron_able)" json:"cronAble"` // 是否定时 1是 -1否 - Cron string `orm:"column(cron)" json:"cron"` // 定时任务cron表达式 - Mode int8 `orm:"column(mode)" json:"mode"` // 数据迁移方式,1、迁移到数据库 2、迁移到文件 - TargetFileDbType string `orm:"column(target_file_db_type)" json:"targetFileDbType"` // 目标文件数据库类型 - FileSaveDays int `json:"fileSaveDays"` // 文件保存天数 - TaskKey string `orm:"column(key)" json:"taskKey"` // 定时任务唯一uuid key - CheckedKeys string `orm:"column(checked_keys)" json:"checkedKeys"` // 选中需要迁移的表 - DeleteTable int `orm:"column(delete_table)" json:"deleteTable"` // 创建表前是否删除表 - NameCase int `orm:"column(name_case)" json:"nameCase"` // 表名、字段大小写转换 1无 2大写 3小写 - Strategy int `orm:"column(strategy)" json:"strategy"` // 迁移策略 1全量 2增量 + CheckedKeys string `json:"checkedKeys" gorm:"type:text;"` // 选中需要迁移的表 + DeleteTable int8 `json:"deleteTable"` // 创建表前是否删除表 + NameCase int8 `json:"nameCase"` // 表名、字段大小写转换 1无 2大写 3小写 + Strategy int8 `json:"strategy"` // 迁移策略 1全量 2增量 - SrcDbId int64 `orm:"column(src_db_id)" json:"srcDbId"` // 源库id - SrcDbName string `orm:"column(src_db_name)" json:"srcDbName"` // 源库名 - SrcTagPath string `orm:"column(src_tag_path)" json:"srcTagPath"` // 源库tagPath - SrcDbType string `orm:"column(src_db_type)" json:"srcDbType"` // 源库类型 - SrcInstName string `orm:"column(src_inst_name)" json:"srcInstName"` // 源库实例名 - - TargetDbId int `orm:"column(target_db_id)" json:"targetDbId"` // 目标库id - TargetDbName string `orm:"column(target_db_name)" json:"targetDbName"` // 目标库名 - TargetDbType string `orm:"column(target_tag_path)" json:"targetDbType"` // 目标库类型 - TargetInstName string `orm:"column(target_db_type)" json:"targetInstName"` // 目标库实例名 - TargetTagPath string `orm:"column(target_inst_name)" json:"targetTagPath"` // 目标库tagPath + SrcDbId int64 `json:"srcDbId" gorm:"not null;"` // 源库id + SrcDbName string `json:"srcDbName" gorm:"size:255;not null;"` // 源库名 + SrcTagPath string `json:"srcTagPath" gorm:"size:255;"` // 源库tagPath + SrcDbType string `json:"srcDbType" gorm:"size:32;not null;"` // 源库类型 + SrcInstName string `json:"srcInstName" gorm:"size:255;"` // 源库实例名 + TargetDbId int `json:"targetDbId" gorm:"not null;"` // 目标库id + TargetDbName string `json:"targetDbName" gorm:"size:255;not null;"` // 目标库名 + TargetDbType string `json:"targetDbType" gorm:"size:32;not null;"` // 目标库类型 + TargetInstName string `json:"targetInstName" gorm:"size:255;"` // 目标库实例名 + TargetTagPath string `json:"targetTagPath" gorm:"size:255;"` // 目标库tagPath } func (d *DbTransferTask) TableName() string { diff --git a/server/internal/db/domain/entity/db_transfer_file.go b/server/internal/db/domain/entity/db_transfer_file.go index 51fc72b7..dcdac857 100644 --- a/server/internal/db/domain/entity/db_transfer_file.go +++ b/server/internal/db/domain/entity/db_transfer_file.go @@ -5,15 +5,16 @@ import ( "time" ) +// DbTransferFile 数据库迁移文件管理 type DbTransferFile struct { model.IdModel - IsDeleted int8 `orm:"column(is_deleted)" json:"-"` // 是否删除 1是 0否 - CreateTime *time.Time `orm:"column(create_time)" json:"createTime"` // 创建时间,默认当前时间戳 - Status int8 `orm:"column(status)" json:"status"` // 状态 1、执行中 2、执行成功 3、执行失败 - TaskId uint64 `orm:"column(task_id)" json:"taskId"` // 迁移任务ID - LogId uint64 `orm:"column(log_id)" json:"logId"` // 日志ID - FileDbType string `orm:"column(file_db_type)" json:"fileDbType"` // sql文件数据库类型 - FileKey string `orm:"column(file_key)" json:"fileKey"` // 文件 + IsDeleted int8 `json:"-" gorm:"default:0;"` // 是否删除 1是 0否 + CreateTime *time.Time `json:"createTime"` // 创建时间,默认当前时间戳 + Status int8 `json:"status" gorm:"default:1;comment:状态 1、执行中 2、执行成功 3、执行失败"` // 状态 1、执行中 2、执行成功 3、执行失败 + TaskId uint64 `json:"taskId" gorm:"comment:迁移任务ID"` // 迁移任务ID + LogId uint64 `json:"logId" gorm:"comment:日志ID"` // 日志ID + FileDbType string `json:"fileDbType" gorm:"size:32;comment:sql文件数据库类型"` // sql文件数据库类型 + FileKey string `json:"fileKey" gorm:"size:50;comment:文件"` // 文件 } func (d *DbTransferFile) TableName() string { diff --git a/server/internal/file/domain/entity/file.go b/server/internal/file/domain/entity/file.go index 8bd2e9f5..79362ae8 100644 --- a/server/internal/file/domain/entity/file.go +++ b/server/internal/file/domain/entity/file.go @@ -5,9 +5,9 @@ import "mayfly-go/pkg/model" type File struct { model.Model - FileKey string `json:"fikeKey"` // 文件key - Filename string `json:"filename"` // 文件名 - Path string `json:"path" ` // 文件路径 + FileKey string `json:"fikeKey" gorm:"size:32;not null;"` // 文件key + Filename string `json:"filename" gorm:"size:255;not null;"` // 文件名 + Path string `json:"path" gorm:"size:500;"` // 文件路径 Size int64 `json:"size"` } diff --git a/server/internal/flow/domain/entity/procdef.go b/server/internal/flow/domain/entity/procdef.go index ec0866c2..1d0a110e 100644 --- a/server/internal/flow/domain/entity/procdef.go +++ b/server/internal/flow/domain/entity/procdef.go @@ -14,12 +14,12 @@ import ( type Procdef struct { model.Model - Name string `json:"name" form:"name"` // 名称 - DefKey string `json:"defKey" form:"defKey"` // - Tasks string `json:"tasks"` // 审批节点任务信息 - Status ProcdefStatus `json:"status"` // 状态 - Condition *string `json:"condition"` // 触发审批的条件(计算结果返回1则需要启用该流程) - Remark *string `json:"remark"` + Name string `json:"name" form:"name" gorm:"size:150;comment:流程名称"` // 名称 + DefKey string `json:"defKey" form:"defKey" gorm:"not null;size:100;comment:流程定义key"` // + Tasks string `json:"tasks" gorm:"not null;size:3000;comment:审批节点任务信息"` // 审批节点任务信息 + Status ProcdefStatus `json:"status" gorm:"comment:状态"` // 状态 + Condition *string `json:"condition" gorm:"type:text;comment:触发审批的条件(计算结果返回1则需要启用该流程)"` // 触发审批的条件(计算结果返回1则需要启用该流程) + Remark *string `json:"remark" gorm:"size:255;"` } func (p *Procdef) TableName() string { diff --git a/server/internal/flow/domain/entity/procinst.go b/server/internal/flow/domain/entity/procinst.go index e6a79da7..007239b6 100644 --- a/server/internal/flow/domain/entity/procinst.go +++ b/server/internal/flow/domain/entity/procinst.go @@ -10,19 +10,19 @@ import ( type Procinst struct { model.Model - ProcdefId uint64 `json:"procdefId"` // 流程定义id - ProcdefName string `json:"procdefName"` // 流程定义名称 + ProcdefId uint64 `json:"procdefId" gorm:"not null;index:idx_procdef_id;comment:流程定义id"` // 流程定义id + ProcdefName string `json:"procdefName" gorm:"not null;size:64;comment:流程定义名称"` // 流程定义名称 - BizType string `json:"bizType"` // 业务类型 - BizKey string `json:"bizKey"` // 业务key - BizForm string `json:"bizForm"` // 业务表单 - BizStatus ProcinstBizStatus `json:"bizStatus"` // 业务状态 - BizHandleRes string `json:"bizHandleRes"` // 业务处理结果 - TaskKey string `json:"taskKey"` // 当前任务key - Status ProcinstStatus `json:"status"` // 状态 - Remark string `json:"remark"` - EndTime *time.Time `json:"endTime"` - Duration int64 `json:"duration"` // 持续时间(开始到结束) + BizType string `json:"bizType" gorm:"not null;size:64;comment:关联业务类型"` // 业务类型 + BizKey string `json:"bizKey" gorm:"not null;size:64;comment:关联业务key"` // 业务key + BizForm string `json:"bizForm" gorm:"type:text;comment:业务form"` // 业务表单 + BizStatus ProcinstBizStatus `json:"bizStatus" gorm:"comment:业务状态"` // 业务状态 + BizHandleRes string `json:"bizHandleRes" gorm:"size:4000;comment:关联的业务处理结果"` // 业务处理结果 + TaskKey string `json:"taskKey" gorm:"size:100;comment:当前任务key"` // 当前任务key + Status ProcinstStatus `json:"status" gorm:"comment:状态"` // 状态 + Remark string `json:"remark" gorm:"size:255;"` + EndTime *time.Time `json:"endTime" gorm:"comment:结束时间"` + Duration int64 `json:"duration" gorm:"comment:流程持续时间(开始到结束)"` // 持续时间(开始到结束) } func (a *Procinst) TableName() string { @@ -68,15 +68,15 @@ const ( type ProcinstTask struct { model.Model - ProcinstId uint64 `json:"procinstId"` // 流程实例id - TaskKey string `json:"taskKey"` // 当前任务key - TaskName string `json:"taskName"` // 当前任务名称 - Assignee string `json:"assignee"` // 分配到该任务的用户 + ProcinstId uint64 `json:"procinstId" gorm:"not null;index:idx_procinst_id;comment:流程实例id"` // 流程实例id + TaskKey string `json:"taskKey" gorm:"not null;size:64;comment:任务key"` // 当前任务key + TaskName string `json:"taskName" gorm:"size:64;comment:任务名称"` // 当前任务名称 + Assignee string `json:"assignee" gorm:"size:64;comment:分配到该任务的用户"` // 分配到该任务的用户 - Status ProcinstTaskStatus `json:"status"` // 状态 - Remark string `json:"remark"` - EndTime *time.Time `json:"endTime"` - Duration int64 `json:"duration"` // 持续时间(开始到结束) + Status ProcinstTaskStatus `json:"status" ` // 状态 + Remark string `json:"remark" gorm:"size:255;"` + EndTime *time.Time `json:"endTime" gorm:"comment:结束时间"` + Duration int64 `json:"duration" gorm:"comment:任务持续时间(开始到结束)"` // 持续时间(开始到结束) } func (a *ProcinstTask) TableName() string { diff --git a/server/internal/machine/domain/entity/machine.go b/server/internal/machine/domain/entity/machine.go index 38d442f9..91ee1e18 100644 --- a/server/internal/machine/domain/entity/machine.go +++ b/server/internal/machine/domain/entity/machine.go @@ -8,15 +8,15 @@ type Machine struct { model.Model model.ExtraData - Code string `json:"code"` - Name string `json:"name"` - Protocol int `json:"protocol"` // 连接协议 1.ssh 2.rdp - Ip string `json:"ip"` // IP地址 - Port int `json:"port"` // 端口号 - Status int8 `json:"status"` // 状态 1:启用;2:停用 - Remark string `json:"remark"` // 备注 - SshTunnelMachineId int `json:"sshTunnelMachineId"` // ssh隧道机器id - EnableRecorder int8 `json:"enableRecorder"` // 是否启用终端回放记录 + Code string `json:"code" gorm:"size:32;comment:code"` // code + Name string `json:"name" gorm:"size:32"` // 名称 + Protocol int `json:"protocol" gorm:"default:1;comment:连接协议 1.ssh 2.rdp"` // 连接协议 1.ssh 2.rdp + Ip string `json:"ip" gorm:"not null;size:100;comment:IP地址"` // IP地址 + Port int `json:"port" gorm:"not null;comment:端口号"` // 端口号 + Status int8 `json:"status" gorm:"not null;default:1;comment:状态 1:启用;2:停用"` // 状态 1:启用;2:停用 + Remark string `json:"remark" gorm:"comment:备注"` // 备注 + SshTunnelMachineId int `json:"sshTunnelMachineId" gorm:"comment:ssh隧道机器id"` // ssh隧道机器id + EnableRecorder int8 `json:"enableRecorder" gorm:"comment:是否启用终端回放记录"` // 是否启用终端回放记录 } const ( diff --git a/server/internal/machine/domain/entity/machine_cmd_conf.go b/server/internal/machine/domain/entity/machine_cmd_conf.go index 125c3414..42387802 100644 --- a/server/internal/machine/domain/entity/machine_cmd_conf.go +++ b/server/internal/machine/domain/entity/machine_cmd_conf.go @@ -4,13 +4,13 @@ import ( "mayfly-go/pkg/model" ) -// 机器命令过滤配置 +// MachineCmdConf 机器命令过滤配置 type MachineCmdConf struct { model.Model - Name string `json:"name"` - Cmds model.Slice[string] `json:"cmds"` // 命令配置 - Status int8 `json:"execCmds"` // 状态 - Stratege string `json:"stratege"` // 策略,空禁用 - Remark string `json:"remark"` // 备注 + Name string `json:"name" gorm:"size:100;comment:名称"` // 名称 + Cmds model.Slice[string] `json:"cmds" gorm:"type:varchar(500);comment:命令配置"` // 命令配置 + Status int8 `json:"status" gorm:"comment:状态"` // 状态 + Stratege string `json:"stratege" gorm:"size:100;comment:策略"` // 策略,空禁用 + Remark string `json:"remark" gorm:"size:50;comment:备注"` // 备注 } diff --git a/server/internal/machine/domain/entity/machine_cronjob.go b/server/internal/machine/domain/entity/machine_cronjob.go index 43ac2209..eb2d04e9 100644 --- a/server/internal/machine/domain/entity/machine_cronjob.go +++ b/server/internal/machine/domain/entity/machine_cronjob.go @@ -9,24 +9,24 @@ import ( 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"` // 记录执行结果类型 + Name string `json:"name" form:"name" gorm:"not null;size:255;comment:名称"` // 名称 + Key string `json:"key" gorm:"not null;size:32;comment:key"` // key + Cron string `json:"cron" gorm:"not null;size:255;comment:cron表达式"` // cron表达式 + Script string `json:"script" gorm:"type:text;comment:脚本内容"` // 任务内容 + Status int `json:"status" form:"status" gorm:"comment:状态"` // 状态 + Remark string `json:"remark" gorm:"size:255;comment:备注"` // 备注 + LastExecTime *time.Time `json:"lastExecTime" gorm:"comment:最后执行时间"` // 最后执行时间 + SaveExecResType int `json:"saveExecResType" gorm:"comment:保存执行记录类型"` // 记录执行结果类型 } -// 机器任务执行记录 +// MachineCronJobExec 机器任务执行记录 type MachineCronJobExec struct { model.DeletedModel - CronJobId uint64 `json:"cronJobId" form:"cronJobId"` - MachineCode string `json:"machineCode" form:"machineCode"` - Status int `json:"status" form:"status"` // 执行状态 - Res string `json:"res"` // 执行结果 + CronJobId uint64 `json:"cronJobId" form:"cronJobId" gorm:"not null;"` + MachineCode string `json:"machineCode" form:"machineCode" gorm:"size:50;"` + Status int `json:"status" form:"status"` // 执行状态 + Res string `json:"res" gorm:"size:4000;"` // 执行结果 ExecTime time.Time `json:"execTime"` } diff --git a/server/internal/machine/domain/entity/machine_file.go b/server/internal/machine/domain/entity/machine_file.go index 5e69c6c6..627d1693 100644 --- a/server/internal/machine/domain/entity/machine_file.go +++ b/server/internal/machine/domain/entity/machine_file.go @@ -5,10 +5,8 @@ import "mayfly-go/pkg/model" type MachineFile struct { model.Model - Name string `json:"name"` - // 机器id - MachineId uint64 `json:"machineId"` - Type int `json:"type"` - // 路径 - Path string `json:"path"` + Name string `json:"name" gorm:"not null;size:50;comment:机器文件配置(linux一切皆文件,故也可以表示目录)"` // 机器文件配置(linux一切皆文件,故也可以表示目录) + MachineId uint64 `json:"machineId" gorm:"not null;comment:机器id"` // 机器id + Type string `json:"type" gorm:"not null;size:32;comment:1:目录;2:文件"` // 1:目录;2:文件 + Path string `json:"path" gorm:"not null;size:150;comment:路径"` // 路径 } diff --git a/server/internal/machine/domain/entity/machine_script.go b/server/internal/machine/domain/entity/machine_script.go index 3ed50419..7e4a678f 100644 --- a/server/internal/machine/domain/entity/machine_script.go +++ b/server/internal/machine/domain/entity/machine_script.go @@ -4,10 +4,11 @@ import "mayfly-go/pkg/model" type MachineScript struct { model.Model - Name string `json:"name"` - MachineId uint64 `json:"machineId"` // 机器id - Type int `json:"type"` - Description string `json:"description"` // 脚本描述 - Params string `json:"params"` // 参数列表json - Script string `json:"script" gorm:"column:script;type:text"` // 脚本内容 + + Name string `json:"name" gorm:"not null;size:255;comment:脚本名"` // 脚本名 + MachineId uint64 `json:"machineId" gorm:"not null;comment:机器id[0:公共]"` // 机器id + Type int `json:"type" gorm:"comment:脚本类型[1: 有结果;2:无结果;3:实时交互]"` // 脚本类型[1: 有结果;2:无结果;3:实时交互] + Description string `json:"description" gorm:"size:255;comment:脚本描述"` // 脚本描述 + Params string `json:"params" gorm:"size:500;comment:脚本入参"` // 参数列表json + Script string `json:"script" gorm:"type:text;comment:脚本内容"` // 脚本内容 } diff --git a/server/internal/machine/domain/entity/machine_term_op.go b/server/internal/machine/domain/entity/machine_term_op.go index dc6630de..ed7fd626 100644 --- a/server/internal/machine/domain/entity/machine_term_op.go +++ b/server/internal/machine/domain/entity/machine_term_op.go @@ -8,13 +8,13 @@ import ( type MachineTermOp struct { model.DeletedModel - MachineId uint64 `json:"machineId"` - Username string `json:"username"` - FileKey string `json:"fileKey"` // 文件key - ExecCmds string `json:"execCmds"` // 执行的命令 + MachineId uint64 `json:"machineId" gorm:"not null;comment:机器id"` // 机器id + Username string `json:"username" gorm:"size:60;comment:登录用户名"` // 登录用户名 + FileKey string `json:"fileKey" gorm:"size:36;comment:文件"` // 文件key + ExecCmds string `json:"execCmds" gorm:"type:text;comment:执行的命令记录"` // 执行的命令 - CreateTime *time.Time `json:"createTime"` - CreatorId uint64 `json:"creatorId"` - Creator string `json:"creator"` - EndTime *time.Time `json:"endTime"` + CreateTime *time.Time `json:"createTime" gorm:"not null;comment:创建时间"` // 创建时间 + CreatorId uint64 `json:"creatorId" gorm:"comment:创建人ID"` + Creator string `json:"creator" gorm:"size:50;comment:创建人"` // 创建人 + EndTime *time.Time `json:"endTime" gorm:"comment:结束时间"` // 结束时间 } diff --git a/server/internal/mongo/domain/entity/mongo.go b/server/internal/mongo/domain/entity/mongo.go index 7855ec29..c89b2dc2 100644 --- a/server/internal/mongo/domain/entity/mongo.go +++ b/server/internal/mongo/domain/entity/mongo.go @@ -9,10 +9,10 @@ import ( type Mongo struct { model.Model - Code string `orm:"column(code)" json:"code"` - Name string `orm:"column(name)" json:"name"` - Uri string `orm:"column(uri)" json:"uri"` - SshTunnelMachineId int `orm:"column(ssh_tunnel_machine_id)" json:"sshTunnelMachineId"` // ssh隧道机器id + Code string `json:"code" gorm:"size:32;comment:code"` // code + Name string `json:"name" gorm:"not null;size:50;comment:名称"` // 名称 + Uri string `json:"uri" gorm:"not null;size:255;comment:连接uri"` // 连接uri + SshTunnelMachineId int `json:"sshTunnelMachineId" gorm:"comment:ssh隧道的机器id"` // ssh隧道机器id } // 转换为mongoInfo进行连接 diff --git a/server/internal/msg/domain/entity/msg.go b/server/internal/msg/domain/entity/msg.go index 85f7aab6..fa45b545 100644 --- a/server/internal/msg/domain/entity/msg.go +++ b/server/internal/msg/domain/entity/msg.go @@ -10,11 +10,11 @@ type Msg struct { CreateTime *time.Time `json:"createTime"` CreatorId uint64 `json:"creatorId"` - Creator string `json:"creator"` + Creator string `json:"creator" gorm:"size:50"` - Type int `json:"type"` - Msg string `json:"msg"` - RecipientId int64 `json:"recipientId"` // 接受者id + Type int8 `json:"type"` + Msg string `json:"msg" gorm:"size:2000"` + RecipientId int64 `json:"recipientId"` // 接收人id,-1为所有接收 } func (a *Msg) TableName() string { diff --git a/server/internal/redis/domain/entity/redis.go b/server/internal/redis/domain/entity/redis.go index 372cf67f..131d9b5a 100644 --- a/server/internal/redis/domain/entity/redis.go +++ b/server/internal/redis/domain/entity/redis.go @@ -12,13 +12,13 @@ import ( type Redis struct { model.Model - Code string `orm:"column(code)" json:"code"` - Name string `orm:"column(name)" json:"name"` - Host string `orm:"column(host)" json:"host"` - Mode string `json:"mode"` - Db string `orm:"column(database)" json:"db"` - SshTunnelMachineId int `orm:"column(ssh_tunnel_machine_id)" json:"sshTunnelMachineId"` // ssh隧道机器id - Remark string + Code string `json:"code" gorm:"size:32;not null;"` // code + Name string `json:"name" gorm:"size:255;not null;"` // 名称 + Host string `json:"host" gorm:"size:255;not null;"` // 主机地址 + Mode string `json:"mode" gorm:"size:32;"` // 模式 + Db string `json:"db" gorm:"size:64;comment:库号: 多个库用,分割"` // 库号: 多个库用,分割 + SshTunnelMachineId int `json:"sshTunnelMachineId" gorm:"comment:ssh隧道的机器id"` // ssh隧道机器id + Remark string `json:"remark" gorm:"size:255;"` } // ToRedisInfo 转换为redisInfo进行连接 diff --git a/server/internal/sys/api/form/resource.go b/server/internal/sys/api/form/resource.go index b660d28e..cd3b194c 100644 --- a/server/internal/sys/api/form/resource.go +++ b/server/internal/sys/api/form/resource.go @@ -1,7 +1,7 @@ package form type ResourceForm struct { - Pid int `json:"pid"` + Pid int64 `json:"pid"` Id int `json:"id"` Code string `json:"code" binding:"required"` Name string `json:"name" binding:"required"` diff --git a/server/internal/sys/domain/entity/account.go b/server/internal/sys/domain/entity/account.go index 6fa0dbdc..ac02f881 100644 --- a/server/internal/sys/domain/entity/account.go +++ b/server/internal/sys/domain/entity/account.go @@ -11,13 +11,13 @@ import ( type Account struct { model.Model - Name string `json:"name"` - Username string `json:"username"` - Password string `json:"-"` - Status AccountStatus `json:"status"` + Name string `json:"name" gorm:"size:30;not null;"` + Username string `json:"username" gorm:"size:30;not null;"` + Password string `json:"-" gorm:"size:64;not null;"` + Status AccountStatus `json:"status" gorm:"not null;"` LastLoginTime *time.Time `json:"lastLoginTime"` - LastLoginIp string `json:"lastLoginIp"` - OtpSecret string `json:"-"` + LastLoginIp string `json:"lastLoginIp" gorm:"size:50;"` + OtpSecret string `json:"-" gorm:"size:100;"` } func (a *Account) TableName() string { diff --git a/server/internal/sys/domain/entity/config.go b/server/internal/sys/domain/entity/config.go index f7ecc4ff..bc5ef1d6 100644 --- a/server/internal/sys/domain/entity/config.go +++ b/server/internal/sys/domain/entity/config.go @@ -13,12 +13,12 @@ const ( type Config struct { model.Model - Name string `json:"name"` // 配置名 - Key string `json:"key"` // 配置key - Params string `json:"params" gorm:"column:params;type:varchar(1500)"` - Value string `json:"value" gorm:"column:value;type:varchar(1500)"` - Remark string `json:"remark"` - Permission string `json:"permission"` // 可操作该配置的权限 + Name string `json:"name" gorm:"size:60;not null;"` // 配置名 + Key string `json:"key" gorm:"size:60;not null;"` // 配置key + Params string `json:"params" gorm:"size:1500"` + Value string `json:"value" gorm:"size:1500"` + Remark string `json:"remark" gorm:"size:255"` + Permission string `json:"permission" gorm:"size:255;comment:操作权限"` // 可操作该配置的权限 } func (a *Config) TableName() string { diff --git a/server/internal/sys/domain/entity/resource.go b/server/internal/sys/domain/entity/resource.go index 66748d0a..b7466bda 100644 --- a/server/internal/sys/domain/entity/resource.go +++ b/server/internal/sys/domain/entity/resource.go @@ -4,14 +4,14 @@ import "mayfly-go/pkg/model" type Resource struct { model.Model - Pid int `json:"pid"` - UiPath string `json:"ui_path"` // 唯一标识路径 - Type int8 `json:"type"` // 1:菜单路由;2:资源(按钮等) - Status int8 `json:"status"` // 1:可用;-1:不可用 - Code string `json:"code"` - Name string `json:"name"` + Pid int64 `json:"pid" gorm:"not null;comment:父节点id;"` + UiPath string `json:"ui_path" gorm:"size:300;not null;comment:唯一标识路径;"` // 唯一标识路径 + Type int8 `json:"type" gorm:"not null;comment:1:菜单路由;2:资源(按钮等);"` // 1:菜单路由;2:资源(按钮等) + Status int8 `json:"status" gorm:"not null;comment:状态;1:可用,-1:禁用;"` // 1:可用;-1:不可用 + Code string `json:"code" gorm:"size:300;comment:菜单路由为path,其他为唯一标识;"` + Name string `json:"name" gorm:"size:255;not null;"` Weight int `json:"weight"` - Meta string `json:"meta"` + Meta string `json:"meta" gorm:"size:500;"` } func (a *Resource) TableName() string { diff --git a/server/internal/sys/domain/entity/role.go b/server/internal/sys/domain/entity/role.go index acd64474..54726194 100644 --- a/server/internal/sys/domain/entity/role.go +++ b/server/internal/sys/domain/entity/role.go @@ -12,11 +12,11 @@ const ( type Role struct { model.Model - Status int `json:"status"` // 1:可用;-1:不可用 - Name string `json:"name"` - Remark string `json:"remark"` - Code string `json:"code"` - Type int `json:"type"` + Status int `json:"status" gorm:"not null;"` // 1:可用;-1:不可用 + Name string `json:"name" gorm:"size:32;not null;"` + Remark string `json:"remark" gorm:"size:255;not null;"` + Code string `json:"code" gorm:"size:64;not null;"` + Type int8 `json:"type" gorm:"not null;comment:类型:1:公共角色;2:特殊角色;"` } func (a *Role) TableName() string { @@ -27,11 +27,11 @@ func (a *Role) TableName() string { type RoleResource struct { model.DeletedModel - RoleId uint64 `json:"roleId"` - ResourceId uint64 `json:"resourceId"` - CreateTime *time.Time `json:"createTime"` - CreatorId uint64 `json:"creatorId"` - Creator string `json:"creator"` + RoleId uint64 `json:"roleId" gorm:"not null;"` + ResourceId uint64 `json:"resourceId" gorm:"not null;"` + CreateTime *time.Time `json:"createTime" gorm:"not null;"` + CreatorId uint64 `json:"creatorId" gorm:"not null;"` + Creator string `json:"creator" gorm:"size:32;not null;"` } func (a *RoleResource) TableName() string { @@ -42,11 +42,11 @@ func (a *RoleResource) TableName() string { type AccountRole struct { model.DeletedModel - AccountId uint64 `json:"accountId"` - RoleId uint64 `json:"roleId"` - CreateTime *time.Time `json:"createTime"` - CreatorId uint64 `json:"creatorId"` - Creator string `json:"creator"` + AccountId uint64 `json:"accountId" gorm:"not null;"` + RoleId uint64 `json:"roleId" gorm:"not null;"` + CreateTime *time.Time `json:"createTime" gorm:"not null;"` + CreatorId uint64 `json:"creatorId" gorm:"not null;"` + Creator string `json:"creator" gorm:"size:32;not null;"` } func (a *AccountRole) TableName() string { diff --git a/server/internal/sys/domain/entity/syslog.go b/server/internal/sys/domain/entity/syslog.go index 778f04e0..e055fceb 100644 --- a/server/internal/sys/domain/entity/syslog.go +++ b/server/internal/sys/domain/entity/syslog.go @@ -8,11 +8,11 @@ import ( type SysLog struct { model.CreateModel - Type int8 `json:"type"` - Description string `json:"description"` - ReqParam string `json:"reqParam" gorm:"column:req_param;type:varchar(1000)"` // 请求参数 - Resp string `json:"resp" gorm:"column:resp;type:varchar(10000)"` // 响应结构 - Extra string `json:"extra"` // 日志额外信息 + Type int8 `json:"type" gorm:"not null;"` + Description string `json:"description" gorm:"size:255;"` + ReqParam string `json:"reqParam" gorm:"size:2000"` // 请求参数 + Resp string `json:"resp" gorm:"type:text;"` // 响应结构 + Extra string `json:"extra" gorm:"type:text;"` // 日志额外信息 } func (a *SysLog) TableName() string { diff --git a/server/internal/tag/api/form/auth_cert.go b/server/internal/tag/api/form/auth_cert.go index 1a947d36..a4cd1122 100644 --- a/server/internal/tag/api/form/auth_cert.go +++ b/server/internal/tag/api/form/auth_cert.go @@ -8,7 +8,7 @@ import ( // 授权凭证 type AuthCertForm struct { Id uint64 `json:"id"` - Name string `json:"name" binding:"required"` // 名称 + Name string `json:"name"` // 名称 ResourceCode string `json:"resourceCode"` // 资源编号 ResourceType int8 `json:"resourceType"` // 资源类型 Username string `json:"username"` // 用户名 diff --git a/server/internal/tag/application/resouce_auth_cert.go b/server/internal/tag/application/resouce_auth_cert.go index 7ac1bfec..971bbf11 100644 --- a/server/internal/tag/application/resouce_auth_cert.go +++ b/server/internal/tag/application/resouce_auth_cert.go @@ -96,7 +96,7 @@ func (r *resourceAuthCertAppImpl) RelateAuthCert(ctx context.Context, params *dt // 密文加密 if err := resourceAuthCert.CiphertextEncrypt(); err != nil { - return errorx.NewBiz(err.Error()) + return err } } @@ -309,9 +309,14 @@ func (r *resourceAuthCertAppImpl) FillAuthCert(resourceType int8, resources ...e // addAuthCert 添加授权凭证 func (r *resourceAuthCertAppImpl) addAuthCert(ctx context.Context, rac *entity.ResourceAuthCert) error { - if r.CountByCond(&entity.ResourceAuthCert{Name: rac.Name}) > 0 { - return errorx.NewBizI(ctx, imsg.ErrAcNameExist, "acName", rac.Name) + if rac.Name == "" { + rac.Name = stringx.Rand(10) + } else { + if r.CountByCond(&entity.ResourceAuthCert{Name: rac.Name}) > 0 { + return errorx.NewBizI(ctx, imsg.ErrAcNameExist, "acName", rac.Name) + } } + // 公共凭证 if rac.Type == entity.AuthCertTypePublic { rac.ResourceCode = "-" diff --git a/server/internal/tag/domain/entity/resource_auth_cert.go b/server/internal/tag/domain/entity/resource_auth_cert.go index 01fc1c63..a21d13b0 100644 --- a/server/internal/tag/domain/entity/resource_auth_cert.go +++ b/server/internal/tag/domain/entity/resource_auth_cert.go @@ -15,15 +15,15 @@ type ResourceAuthCert struct { model.Model model.ExtraData - Name string `json:"name"` // 名称(全局唯一) + Name string `json:"name" gorm:"size:50;comment:账号名称"` // 名称(全局唯一) - ResourceCode string `json:"resourceCode"` // 资源编号 - ResourceType int8 `json:"resourceType"` // 资源类型 - Type AuthCertType `json:"type"` // 凭证类型 - Username string `json:"username"` // 用户名 - Ciphertext string `json:"ciphertext"` // 密文 - CiphertextType AuthCertCiphertextType `json:"ciphertextType"` // 密文类型 - Remark string `json:"remark"` // 备注 + ResourceCode string `json:"resourceCode" gorm:"size:36;comment:资源编码"` // 资源编号 + ResourceType int8 `json:"resourceType" gorm:"not null;comment:资源类型"` // 资源类型 + Type AuthCertType `json:"type" gorm:"comment:凭证类型"` // 凭证类型 + Username string `json:"username" gorm:"size:100;comment:用户名"` // 用户名 + Ciphertext string `json:"ciphertext" gorm:"size:5000;comment:密文内容"` // 密文 + CiphertextType AuthCertCiphertextType `json:"ciphertextType" gorm:"not null;comment:密文类型(-1.公共授权凭证 1.密码 2.秘钥)"` // 密文类型 + Remark string `json:"remark" gorm:"size:255;comment:备注"` // 备注 } // CiphertextEncrypt 密文加密 diff --git a/server/internal/tag/domain/entity/resource_op_log.go b/server/internal/tag/domain/entity/resource_op_log.go index 25f05aa2..dcc83426 100644 --- a/server/internal/tag/domain/entity/resource_op_log.go +++ b/server/internal/tag/domain/entity/resource_op_log.go @@ -6,7 +6,7 @@ import "mayfly-go/pkg/model" type ResourceOpLog struct { model.CreateModel - CodePath string `json:"codePath"` // 标签路径 - ResourceCode string `json:"resourceCode"` // 资源编号 - ResourceType int8 `json:"relateType"` // 资源类型 + CodePath string `json:"codePath" gorm:"size:255;not null;"` // 标签路径 + ResourceCode string `json:"resourceCode" gorm:"size:50;not null;"` // 资源编号 + ResourceType int8 `json:"relateType" gorm:"not null;"` // 资源类型 } diff --git a/server/internal/tag/domain/entity/tag_tree.go b/server/internal/tag/domain/entity/tag_tree.go index f4859c73..d2760012 100644 --- a/server/internal/tag/domain/entity/tag_tree.go +++ b/server/internal/tag/domain/entity/tag_tree.go @@ -14,11 +14,11 @@ import ( type TagTree struct { model.Model - Type TagType `json:"type"` // 类型: -1.普通标签; 其他值则为对应的资源类型 - Code string `json:"code"` // 标识编码, 若类型不为-1,则为对应资源编码 - CodePath string `json:"codePath"` // 标识路径,tag1/tag2/tagType1|tagCode/tagType2|yyycode/,非普通标签类型段含有标签类型 - Name string `json:"name"` // 名称 - Remark string `json:"remark"` // 备注说明 + Type TagType `json:"type" gorm:"not null;default:-1;comment:类型: -1.普通标签; 1机器 2db 3redis 4mongo"` // 类型: -1.普通标签; 其他值则为对应的资源类型 + Code string `json:"code" gorm:"not null;size:50;comment:标识符"` // 标识编码, 若类型不为-1,则为对应资源编码 + CodePath string `json:"codePath" gorm:"not null;size:800;comment:标识符路径"` // 标识路径,tag1/tag2/tagType1|tagCode/tagType2|yyycode/,非普通标签类型段含有标签类型 + Name string `json:"name" gorm:"size:50;comment:名称"` // 名称 + Remark string `json:"remark" gorm:"size:255;"` // 备注说明 } type TagType int8 diff --git a/server/internal/tag/domain/entity/tag_tree_relate.go b/server/internal/tag/domain/entity/tag_tree_relate.go index 649fe912..2c157e20 100644 --- a/server/internal/tag/domain/entity/tag_tree_relate.go +++ b/server/internal/tag/domain/entity/tag_tree_relate.go @@ -6,9 +6,9 @@ import "mayfly-go/pkg/model" type TagTreeRelate struct { model.Model - TagId uint64 `json:"tagId"` - RelateId uint64 `json:"relateId"` // 关联的id - RelateType TagRelateType `json:"relateType"` // 关联的类型 + TagId uint64 `json:"tagId" gorm:"not null;index:idx_tag_id;comment:标签树id"` // 标签树id + RelateId uint64 `json:"relateId" gorm:"not null;comment:关联的资源id"` // 关联的资源id + RelateType TagRelateType `json:"relateType" gorm:"not null;comment:关联类型"` // 关联的类型 } type TagRelateType int8 diff --git a/server/internal/tag/domain/entity/team.go b/server/internal/tag/domain/entity/team.go index d0f3418c..b42aa41b 100644 --- a/server/internal/tag/domain/entity/team.go +++ b/server/internal/tag/domain/entity/team.go @@ -8,8 +8,8 @@ import ( type Team struct { model.Model - Name string `json:"name"` // 名称 - ValidityStartDate *model.JsonTime `json:"validityStartDate"` // 生效开始时间 - ValidityEndDate *model.JsonTime `json:"validityEndDate"` // 生效结束时间 - Remark string `json:"remark"` // 备注说明 + Name string `json:"name" gorm:"not null;size:36;comment:名称"` // 名称 + ValidityStartDate *model.JsonTime `json:"validityStartDate" gorm:"comment:生效开始时间"` // 生效开始时间 + ValidityEndDate *model.JsonTime `json:"validityEndDate" gorm:"comment:生效结束时间"` // 生效结束时间 + Remark string `json:"remark" gorm:"size:255;"` // 备注说明 } diff --git a/server/internal/tag/domain/entity/team_member.go b/server/internal/tag/domain/entity/team_member.go index 8a677fca..27a69b6f 100644 --- a/server/internal/tag/domain/entity/team_member.go +++ b/server/internal/tag/domain/entity/team_member.go @@ -6,7 +6,7 @@ import "mayfly-go/pkg/model" type TeamMember struct { model.Model - TeamId uint64 `json:"teamId"` - AccountId uint64 `json:"accountId"` - Username string `json:"username"` + TeamId uint64 `json:"teamId" gorm:"not null;"` + AccountId uint64 `json:"accountId" gorm:"not null;"` + Username string `json:"username" gorm:"size:50;not null;"` } diff --git a/server/migration/migration.go b/server/migration/migration.go new file mode 100644 index 00000000..2f822a07 --- /dev/null +++ b/server/migration/migration.go @@ -0,0 +1,53 @@ +package migration + +import ( + "mayfly-go/migration/migrations" + "mayfly-go/pkg/logx" + "mayfly-go/pkg/rediscli" + "time" + + "github.com/go-gormigrate/gormigrate/v2" + "gorm.io/gorm" +) + +// RunMigrations 数据库迁移操作 +func RunMigrations(db *gorm.DB) error { + logx.Info("start to run migrations") + // 添加分布式锁, 防止多个服务同时执行迁移 + lock := rediscli.NewLock("mayfly:db:migrations", 1*time.Minute) + if lock != nil { + if !lock.Lock() { + return nil + } + defer lock.UnLock() + } + + err := run(db, + migrations.Init, + migrations.V1_9, + ) + + if err == nil { + logx.Info("migrations run success") + } + return err +} + +func run(db *gorm.DB, fs ...func() []*gormigrate.Migration) error { + var ms []*gormigrate.Migration + for _, f := range fs { + ms = append(ms, f()...) + } + + m := gormigrate.New(db, &gormigrate.Options{ + TableName: "migrations", + IDColumnName: "id", + IDColumnSize: 300, + UseTransaction: true, + ValidateUnknownMigrations: true, + }, ms) + if err := m.Migrate(); err != nil { + return err + } + return nil +} diff --git a/server/migration/migrations/init.go b/server/migration/migrations/init.go new file mode 100644 index 00000000..b321dfa5 --- /dev/null +++ b/server/migration/migrations/init.go @@ -0,0 +1,1447 @@ +package migrations + +import ( + authentity "mayfly-go/internal/auth/domain/entity" + dbentity "mayfly-go/internal/db/domain/entity" + fileentity "mayfly-go/internal/file/domain/entity" + flowentity "mayfly-go/internal/flow/domain/entity" + machineentity "mayfly-go/internal/machine/domain/entity" + mongoentity "mayfly-go/internal/mongo/domain/entity" + msgentity "mayfly-go/internal/msg/domain/entity" + redisentity "mayfly-go/internal/redis/domain/entity" + sysentity "mayfly-go/internal/sys/domain/entity" + tagentity "mayfly-go/internal/tag/domain/entity" + "mayfly-go/pkg/model" + "time" + + "github.com/go-gormigrate/gormigrate/v2" + "gorm.io/gorm" +) + +func Init() []*gormigrate.Migration { + return []*gormigrate.Migration{ + { + ID: "20250212-v1.9.2-init", + Migrate: func(tx *gorm.DB) error { + entities := [...]any{ + new(sysentity.Account), + new(sysentity.Config), + new(sysentity.SysLog), + new(sysentity.Role), + new(sysentity.AccountRole), + new(sysentity.RoleResource), + new(sysentity.Resource), + + new(authentity.Oauth2Account), + + new(fileentity.File), + + new(msgentity.Msg), + + new(tagentity.TagTree), // 标签树 + new(tagentity.Team), // 团队信息 + new(tagentity.TeamMember), // 团队成员 + new(tagentity.ResourceAuthCert), // 资源授权凭证 + new(tagentity.TagTreeRelate), // 与标签树有关联关系的表 + new(tagentity.ResourceOpLog), // 资源操作记录 + + new(flowentity.Procdef), + new(flowentity.Procinst), + new(flowentity.ProcinstTask), + + new(machineentity.Machine), + new(machineentity.MachineFile), + new(machineentity.MachineMonitor), + new(machineentity.MachineScript), + new(machineentity.MachineCronJob), + new(machineentity.MachineCronJobExec), + new(machineentity.MachineCmdConf), + + new(dbentity.DbInstance), + new(dbentity.Db), + new(dbentity.DbSql), + new(dbentity.DbSqlExec), + new(dbentity.DataSyncTask), + new(dbentity.DataSyncLog), + new(dbentity.DbTransferTask), + new(dbentity.DbTransferFile), + + new(mongoentity.Mongo), + + new(redisentity.Redis), + } + + for _, e := range entities { + if err := tx.AutoMigrate(e); err != nil { + return err + } + } + + // 如果存在账号数据,则不进行初始化系统数据 + var count int64 + tx.Model(&sysentity.Account{}).Count(&count) + if count > 0 { + return nil + } + + // 初始化管理员账号 + if err := initAccount(tx); err != nil { + return err + } + + if err := initRole(tx); err != nil { + return err + } + + if err := initSysConfig(tx); err != nil { + return err + } + + // 初始化菜单权限资源 + if err := initResource(tx); err != nil { + return err + } + + if err := initTag(tx); err != nil { + return err + } + + if err := initMachine(tx); err != nil { + return err + } + + return nil + }, + Rollback: func(tx *gorm.DB) error { + return nil + }}, + } +} + +func initAccount(tx *gorm.DB) error { + account := &sysentity.Account{ + Username: "admin", + Name: "管理员", + Password: "$2a$10$w3Wky2U.tinvR7c/s0aKPuwZsIu6pM1/DMJalwBDMbE6niHIxVrrm", // admin123. + Status: 1, + } + + account.Id = 1 + + now := time.Now() + account.CreateTime = &now + account.UpdateTime = &now + account.CreatorId = 1 + account.ModifierId = 1 + account.Creator = "admin" + account.Modifier = "admin" + + return tx.Create(account).Error +} + +func initRole(tx *gorm.DB) error { + role := &sysentity.Role{ + Name: "公共角色", + Code: "COMMON", + Status: 1, + Remark: "所有账号基础角色", + } + + role.Id = 1 + + now := time.Now() + role.CreateTime = &now + role.UpdateTime = &now + role.CreatorId = 1 + role.ModifierId = 1 + role.Creator = "admin" + role.Modifier = "admin" + + return tx.Create(role).Error +} + +func initSysConfig(tx *gorm.DB) error { + configs := []*sysentity.Config{ + { + Name: "system.sysconf.accountLoginConf", + Key: "AccountLoginSecurity", + Params: `[{"name":"system.sysconf.useCaptcha","model":"useCaptcha","placeholder":"system.sysconf.useCaptchaPlaceholder","options":"true,false"},{"name":"system.sysconf.useOtp","model":"useOtp","placeholder":"system.sysconf.useOtpPlaceholder","options":"true,false"},{"name":"system.sysconf.otpIssuer","model":"otpIssuer","placeholder":""},{"name":"system.sysconf.loginFailCount","model":"loginFailCount","placeholder":"system.sysconf.loginFailCountPlaceholder"},{"name":"system.sysconf.loginFainMin","model":"loginFailMin","placeholder":"system.sysconf.loginFailMinPlaceholder"}]`, + Value: `{"useCaptcha":"true","useOtp":"false","loginFailCount":"5","loginFailMin":"10","otpIssuer":"mayfly-go"}`, + Remark: "system.sysconf.accountLoginConfRemark", + Permission: "all", + }, + { + Name: "system.sysconf.oauth2LoginConf", + Key: "Oauth2Login", + Params: `[{"name":"system.sysconf.oauth2Enable","model":"enable","placeholder":"system.sysconf.oauth2EnablePlaceholder","options":"true,false"},{"name":"system.sysconf.name","model":"name","placeholder":"system.sysconf.namePlaceholder"},{"name":"system.sysconf.clientId","model":"clientId","placeholder":"system.sysconf.clientIdPlaceholder"},{"name":"system.sysconf.clientSecret","model":"clientSecret","placeholder":"system.sysconf.clientSecretPlaceholder"},{"name":"system.sysconf.authorizationUrl","model":"authorizationURL","placeholder":"system.sysconf.authorizationUrlPlaceholder"},{"name":"system.sysconf.accessTokenUrl","model":"accessTokenURL","placeholder":"system.sysconf.accessTokenUrlPlaceholder"},{"name":"system.sysconf.redirectUrl","model":"redirectURL","placeholder":"system.sysconf.redirectUrlPlaceholder"},{"name":"system.sysconf.scope","model":"scopes","placeholder":"system.sysconf.scopePlaceholder"},{"name":"system.sysconf.resourceUrl","model":"resourceURL","placeholder":"system.sysconf.resourceUrlPlaceholder"},{"name":"system.sysconf.userId","model":"userIdentifier","placeholder":"system.sysconf.userIdPlaceholder"},{"name":"system.sysconf.autoRegister","model":"autoRegister","placeholder":"","options":"true,false"}]`, + Value: ``, + Remark: "system.sysconf.oauth2LoginConfRemark", + Permission: "admin,", + }, + { + Name: "system.sysconf.ldapLoginConf", + Key: "LdapLogin", + Params: `[{"name":"system.sysconf.ldapEnable","model":"enable","placeholder":"system.sysconf.dapEnablePlaceholder","options":"true,false"},{"name":"system.sysconf.host","model":"host","placeholder":"system.sysconf.host"},{"name":"system.sysconf.port","model":"port","placeholder":"system.sysconf.port"},{"name":"system.sysconf.bindDN","model":"bindDN","placeholder":"system.sysconf.bindDnPlaceholder"},{"name":"system.sysconf.bindPwd","model":"bindPwd","placeholder":"system.sysconf.bindPwdPlaceholder"},{"name":"system.sysconf.baseDN","model":"baseDN","placeholder":"system.sysconf.baseDnPlaceholder"},{"name":"system.sysconf.userFilter","model":"userFilter","placeholder":"system.sysconf.userFilerPlaceholder"},{"name":"system.sysconf.uidMap","model":"uidMap","placeholder":"system.sysconf.uidMapPlaceholder"},{"name":"system.sysconf.udnMap","model":"udnMap","placeholder":"system.sysconf.udnMapPlaceholder"},{"name":"system.sysconf.emailMap","model":"emailMap","placeholder":"system.sysconf.emailMapPlaceholder"},{"name":"system.sysconf.skipTlsVerfify","model":"skipTLSVerify","placeholder":"system.sysconf.skipTlsVerfifyPlaceholder","options":"true,false"},{"name":"system.sysconf.securityProtocol","model":"securityProtocol","placeholder":"system.sysconf.securityProtocolPlaceholder","options":"Null,StartTLS,LDAPS"}]`, + Value: ``, + Remark: "system.sysconf.ldapLoginConfRemark", + Permission: "admin,", + }, + { + Name: "system.sysconf.systemConf", + Key: "SysStyleConfig", + Params: `[{"model":"logoIcon","name":"system.sysconf.logoIcon","placeholder":"system.sysconf.logoIconPlaceholder","required":false},{"model":"title","name":"system.sysconf.title","placeholder":"system.sysconf.titlePlaceholder","required":false},{"model":"viceTitle","name":"system.sysconf.viceTitle","placeholder":"system.sysconf.viceTitlePlaceholder","required":false},{"model":"useWatermark","name":"system.sysconf.useWatermark","placeholder":"system.sysconf.useWatermarkPlaceholder","options":"true,false","required":false},{"model":"watermarkContent","name":"system.sysconf.watermarkContent","placeholder":"system.sysconf.watermarkContentPlaceholder","required":false}]`, + Value: `{"title":"mayfly-go","viceTitle":"mayfly-go","logoIcon":"","useWatermark":"true","watermarkContent":""}`, + Remark: "system.sysconf.systemConfRemark", + Permission: "all", + }, + { + Name: "system.sysconf.machineConf", + Key: "MachineConfig", + Params: `[{"name":"system.sysconf.uploadMaxFileSize","model":"uploadMaxFileSize","placeholder":"system.sysconf.uploadMaxFileSizePlaceholder"},{"model":"termOpSaveDays","name":"system.sysconf.termOpSaveDays","placeholder":"system.sysconf.termOpSaveDaysPlaceholder"},{"model":"guacdHost","name":"system.sysconf.guacdHost","placeholder":"system.sysconf.guacdHostPlaceholder","required":false},{"name":"system.sysconf.guacdPort","model":"guacdPort","placeholder":"system.sysconf.guacdPortPlaceholder","required":false},{"model":"guacdFilePath","name":"system.sysconf.guacdFilePath","placeholder":"system.sysconf.guacdFilePathPlaceholder"}]`, + Value: `{"uploadMaxFileSize":"1000MB","termOpSaveDays":"30","guacdHost":"","guacdPort":"","guacdFilePath":"./guacd/rdp-file"}`, + Remark: "system.sysconf.machineConfRemark", + Permission: "all", + }, + { + Name: "system.sysconf.dbmsConf", + Key: "DbmsConfig", + Params: `[{"model":"querySqlSave","name":"system.sysconf.recordQuerySql","placeholder":"system.sysconf.recordQuerySqlPlaceholder","options":"true,false"},{"model":"maxResultSet","name":"system.sysconf.maxResultSet","placeholder":"system.sysconf.maxResultSetPlaceholder","options":""},{"model":"sqlExecTl","name":"system.sysconf.sqlExecLimt","placeholder":"system.sysconf.sqlExecLimtPlaceholder"}]`, + Value: `{"querySqlSave":"false","maxResultSet":"0","sqlExecTl":"60"}`, + Remark: "system.sysconf.dbmsConfRemark", + Permission: "admin,", + }, + { + Name: "system.sysconf.fileConf", + Key: "FileConfig", + Params: `[{"model":"basePath","name":"system.sysconf.basePath","placeholder":"system.sysconf.baesPathPlaceholder"}]`, + Value: `{"basePath":"./file"}`, + Remark: "system.sysconf.fileConfRemark", + Permission: "admin,", + }, + } + + now := time.Now() + for _, res := range configs { + res.CreateTime = &now + res.CreatorId = 1 + res.Creator = "admin" + res.UpdateTime = &now + res.ModifierId = 1 + res.Modifier = "admin" + if err := tx.Create(res).Error; err != nil { + return err + } + } + return nil +} + +func initTag(tx *gorm.DB) error { + now := time.Now() + + tag := &tagentity.TagTree{ + Name: "默认", + Code: "default", + CodePath: "default/", + Type: -1, + Remark: "默认标签", + } + + tag.Id = 1 + tag.CreateTime = &now + tag.UpdateTime = &now + tag.CreatorId = 1 + tag.ModifierId = 1 + tag.Creator = "admin" + tag.Modifier = "admin" + + team := &tagentity.Team{ + Name: "default_team", + ValidityStartDate: &model.JsonTime{Time: now}, + ValidityEndDate: &model.JsonTime{Time: now.AddDate(20, 0, 0)}, + Remark: "默认团队", + } + team.Id = 1 + team.CreateTime = &now + team.UpdateTime = &now + team.CreatorId = 1 + team.ModifierId = 1 + team.Creator = "admin" + team.Modifier = "admin" + + teamMember := &tagentity.TeamMember{ + TeamId: 1, + AccountId: 1, + Username: "admin", + } + teamMember.CreateTime = &now + teamMember.UpdateTime = &now + teamMember.CreatorId = 1 + teamMember.ModifierId = 1 + teamMember.Creator = "admin" + teamMember.Modifier = "admin" + + tagRelate := &tagentity.TagTreeRelate{ + TagId: 1, + RelateId: 1, + RelateType: 1, + } + tagRelate.CreateTime = &now + tagRelate.UpdateTime = &now + tagRelate.CreatorId = 1 + tagRelate.ModifierId = 1 + tagRelate.Creator = "admin" + tagRelate.Modifier = "admin" + + tx.Create(team) + tx.Create(teamMember) + tx.Create(tagRelate) + return tx.Create(tag).Error +} + +func initMachine(tx *gorm.DB) error { + machineScripts := []*machineentity.MachineScript{ + { + Name: "disk-mem", + Script: `df -h`, + Type: 1, + MachineId: 9999999, + }, + { + Name: "test_params", + Script: `echo {{.processName}}`, + Type: 1, + Params: `[{\"name\": \"pname\",\"model\": \"processName\", \"placeholder\": \"enter processName\"}]`, + MachineId: 9999999, + }, + { + Name: "top", + Script: `top`, + Type: 3, + MachineId: 9999999, + }, + } + + now := time.Now() + for _, mc := range machineScripts { + mc.CreateTime = &now + mc.CreatorId = 1 + mc.Creator = "admin" + mc.UpdateTime = &now + mc.ModifierId = 1 + mc.Modifier = "admin" + if err := tx.Create(mc).Error; err != nil { + return err + } + } + + return nil +} + +func initResource(tx *gorm.DB) error { + resources := []*sysentity.Resource{ + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 1}}}}, + Pid: 0, + UiPath: "Aexqq77l/", + Name: "menu.index", + Code: "/home", + Type: 1, + Meta: `{"component":"home/Home","icon":"HomeFilled","isAffix":true,"routeName":"Home"}`, + Weight: 10000000, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 2}}}}, + Pid: 0, + UiPath: "12sSjal1/", + Name: "menu.machine", + Code: "/machine", + Type: 1, + Meta: `{"icon":"Monitor","isKeepAlive":true,"redirect":"machine/list","routeName":"Machine"}`, + Weight: 49999998, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 3}}}}, + Pid: 2, + UiPath: "12sSjal1/lskeiql1/", + Name: "menu.machineList", + Code: "machines", + Type: 1, + Meta: `{"component":"ops/machine/MachineList","icon":"Monitor","isKeepAlive":true,"routeName":"MachineList"}`, + Weight: 20000000, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 4}}}}, + Pid: 0, + UiPath: "Xlqig32x/", + Name: "menu.system", + Code: "/sys", + Type: 1, + Meta: `{"icon":"Setting","isKeepAlive":true,"redirect":"/sys/resources","routeName":"sys"}`, + Weight: 60000001, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 5}}}}, + Pid: 4, + UiPath: "Xlqig32x/UGxla231/", + Name: "menu.menuPermission", + Code: "resources", + Type: 1, + Meta: `{"component":"system/resource/ResourceList","icon":"Menu","isKeepAlive":true,"routeName":"ResourceList"}`, + Weight: 9999998, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 11}}}}, + Pid: 4, + UiPath: "Xlqig32x/lxqSiae1/", + Name: "menu.role", + Code: "roles", + Type: 1, + Meta: `{"component":"system/role/RoleList","icon":"icon menu/role","isKeepAlive":true,"routeName":"RoleList"}`, + Weight: 10000001, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 12}}}}, + Pid: 3, + UiPath: "12sSjal1/lskeiql1/Alw1Xkq3/", + Name: "menu.machineTerminal", + Code: "machine:terminal", + Type: 2, + Weight: 40000000, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 14}}}}, + Pid: 4, + UiPath: "Xlqig32x/sfslfel/", + Name: "menu.account", + Code: "accounts", + Type: 1, + Meta: `{"component":"system/account/AccountList","icon":"User","isKeepAlive":true,"routeName":"AccountList"}`, + Weight: 9999999, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 15}}}}, + Pid: 3, + UiPath: "12sSjal1/lskeiql1/Lsew24Kx/", + Name: "menu.machineFileConf", + Code: "machine:file", + Type: 2, + Weight: 50000000, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 16}}}}, + Pid: 3, + UiPath: "12sSjal1/lskeiql1/exIsqL31/", + Name: "menu.machineCreate", + Code: "machine:add", + Type: 2, + Weight: 10000000, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 17}}}}, + Pid: 3, + UiPath: "12sSjal1/lskeiql1/Liwakg2x/", + Name: "menu.machineEdit", + Code: "machine:update", + Type: 2, + Weight: 20000000, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 18}}}}, + Pid: 3, + UiPath: "12sSjal1/lskeiql1/Lieakenx/", + Name: "menu.machineDelete", + Code: "machine:del", + Type: 2, + Weight: 30000000, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 19}}}}, + Pid: 14, + UiPath: "Xlqig32x/sfslfel/UUiex2xA/", + Name: "menu.accountRoleAllocation", + Code: "account:saveRoles", + Type: 2, + Weight: 50000001, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 20}}}}, + Pid: 11, + UiPath: "Xlqig32x/lxqSiae1/EMq2Kxq3/", + Name: "menu.roleMenuPermissionAllocation", + Code: "role:saveResources", + Type: 2, + Weight: 40000002, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 21}}}}, + Pid: 14, + UiPath: "Xlqig32x/sfslfel/Uexax2xA/", + Name: "menu.accountDelete", + Code: "account:del", + Type: 2, + Weight: 20000000, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 22}}}}, + Pid: 11, + UiPath: "Xlqig32x/lxqSiae1/Elxq2Kxq3/", + Name: "menu.roleDelete", + Code: "role:del", + Type: 2, + Weight: 40000001, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 23}}}}, + Pid: 11, + UiPath: "Xlqig32x/lxqSiae1/342xKxq3/", + Name: "menu.roleAdd", + Code: "role:add", + Type: 2, + Weight: 19999999, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 24}}}}, + Pid: 11, + UiPath: "Xlqig32x/lxqSiae1/LexqKxq3/", + Name: "menu.roleEdit", + Code: "role:update", + Type: 2, + Weight: 40000000, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 25}}}}, + Pid: 5, + UiPath: "Xlqig32x/UGxla231/Elxq23XK/", + Name: "menu.menuPermissionAdd", + Code: "resource:add", + Type: 2, + Weight: 10000000, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 26}}}}, + Pid: 5, + UiPath: "Xlqig32x/UGxla231/eloq23XK/", + Name: "menu.menuPermissionDelete", + Code: "resource:delete", + Type: 2, + Weight: 30000001, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 27}}}}, + Pid: 5, + UiPath: "Xlqig32x/UGxla231/JExq23XK/", + Name: "menu.menuPermissionEdit", + Code: "resource:update", + Type: 2, + Weight: 30000000, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 28}}}}, + Pid: 5, + UiPath: "Xlqig32x/UGxla231/Elex13XK/", + Name: "menu.menuPermissionEnableDisable", + Code: "resource:changeStatus", + Type: 2, + Weight: 40000000, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 29}}}}, + Pid: 14, + UiPath: "Xlqig32x/sfslfel/xlawx2xA/", + Name: "menu.accountAdd", + Code: "account:add", + Type: 2, + Weight: 19999999, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 30}}}}, + Pid: 14, + UiPath: "Xlqig32x/sfslfel/32xax2xA/", + Name: "menu.accountEdit", + Code: "account:update", + Type: 2, + Weight: 19999999, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 31}}}}, + Pid: 14, + UiPath: "Xlqig32x/sfslfel/eubale13/", + Name: "menu.accountBase", + Code: "account", + Type: 2, + Weight: 9999999, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 32}}}}, + Pid: 5, + UiPath: "Xlqig32x/UGxla231/321q23XK/", + Name: "menu.menuPermissionBase", + Code: "resource", + Type: 2, + Weight: 9999999, + }, + + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 33}}}}, + Pid: 11, + UiPath: "Xlqig32x/lxqSiae1/908xKxq3/", + Name: "menu.roleBase", + Code: "role", + Type: 2, + Weight: 9999999, + }, + + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 34}}}}, + Pid: 14, + UiPath: "Xlqig32x/sfslfel/32alx2xA/", + Name: "menu.accountEnableDisable", + Code: "account:changeStatus", + Type: 2, + Weight: 50000000, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 36}}}}, + Pid: 0, + UiPath: "dbms23ax/", + Name: "menu.dbms", + Code: "/dbms", + Type: 1, + Meta: `{"icon":"Coin","isKeepAlive":true,"routeName":"DBMS"}`, + Weight: 49999999, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 37}}}}, + Pid: 3, + UiPath: "12sSjal1/lskeiql1/Keiqkx4L/", + Name: "menu.machineFileConfCreate", + Code: "machine:addFile", + Type: 2, + Weight: 60000000, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 38}}}}, + Pid: 36, + UiPath: "dbms23ax/exaeca2x/", + Name: "menu.dbDataOp", + Code: "sql-exec", + Type: 1, + Meta: `{"component":"ops/db/SqlExec","icon":"Coin","isKeepAlive":true,"routeName":"SqlExec"}`, + Weight: 10000000, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 39}}}}, + Pid: 0, + UiPath: "sl3as23x/", + Name: "menu.personalCenter", + Code: "/personal", + Type: 1, + Meta: `{"component":"personal/index","icon":"UserFilled","isHide":true,"isKeepAlive":true,"routeName":"Personal"}`, + Weight: 19999999, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 40}}}}, + Pid: 3, + UiPath: "12sSjal1/lskeiql1/Keal2Xke/", + Name: "menu.machineFileCreate", + Code: "machine:file:add", + Type: 2, + Weight: 70000000, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 41}}}}, + Pid: 3, + UiPath: "12sSjal1/lskeiql1/Ihfs2xaw/", + Name: "menu.machineFileDelete", + Code: "machine:file:del", + Type: 2, + Weight: 80000000, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 42}}}}, + Pid: 3, + UiPath: "12sSjal1/lskeiql1/3ldkxJDx/", + Name: "menu.machineFileWrite", + Code: "machine:file:write", + Type: 2, + Weight: 90000000, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 43}}}}, + Pid: 3, + UiPath: "12sSjal1/lskeiql1/Ljewix43/", + Name: "menu.machineFileUpload", + Code: "machine:file:upload", + Type: 2, + Weight: 100000000, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 44}}}}, + Pid: 3, + UiPath: "12sSjal1/lskeiql1/L12wix43/", + Name: "menu.machineFileConfDelete", + Code: "machine:file:rm", + Type: 2, + Weight: 69999999, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 45}}}}, + Pid: 3, + UiPath: "12sSjal1/lskeiql1/Ljewisd3/", + Name: "menu.machineScriptSave", + Code: "machine:script:save", + Type: 2, + Weight: 120000000, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 46}}}}, + Pid: 3, + UiPath: "12sSjal1/lskeiql1/Ljeew43/", + Name: "menu.machineScriptDelete", + Code: "machine:script:del", + Type: 2, + Weight: 130000000, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 47}}}}, + Pid: 3, + UiPath: "12sSjal1/lskeiql1/ODewix43/", + Name: "menu.machineScriptRun", + Code: "machine:script:run", + Type: 2, + Weight: 140000000, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 54}}}}, + Pid: 135, + UiPath: "dbms23ax/X0f4BxT0/leix3Axl/", + Name: "menu.dbSave", + Code: "db:save", + Type: 2, + Weight: 1693041086, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 55}}}}, + Pid: 135, + UiPath: "dbms23ax/X0f4BxT0/ygjL3sxA/", + Name: "menu.dbDelete", + Code: "db:del", + Type: 2, + Weight: 1693041086, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 57}}}}, + Pid: 3, + UiPath: "12sSjal1/lskeiql1/OJewex43/", + Name: "menu.machineBase", + Code: "machine", + Type: 2, + Weight: 9999999, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 58}}}}, + Pid: 135, + UiPath: "dbms23ax/X0f4BxT0/AceXe321/", + Name: "menu.dbBase", + Code: "db", + Type: 2, + Weight: 1693041085, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 59}}}}, + Pid: 38, + UiPath: "dbms23ax/exaeca2x/ealcia23/", + Name: "menu.dbDataOpBase", + Code: "db:exec", + Type: 2, + Weight: 10000000, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 60}}}}, + Pid: 0, + UiPath: "RedisXq4/", + Name: "menu.redis", + Code: "/redis", + Type: 1, + Meta: `{"icon":"icon redis/redis","isKeepAlive":true,"routeName":"RDS"}`, + Weight: 50000001, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 61}}}}, + Pid: 60, + UiPath: "RedisXq4/Exitx4al/", + Name: "menu.redisDataOp", + Code: "data-operation", + Type: 1, + Meta: `{"component":"ops/redis/DataOperation","icon":"icon redis/redis","isKeepAlive":true,"routeName":"DataOperation"}`, + Weight: 10000000, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 62}}}}, + Pid: 61, + UiPath: "RedisXq4/Exitx4al/LSjie321/", + Name: "menu.redisDataOpBase", + Code: "redis:data", + Type: 2, + Weight: 10000000, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 63}}}}, + Pid: 60, + UiPath: "RedisXq4/Eoaljc12/", + Name: "menu.redisManage", + Code: "manage", + Type: 1, + Meta: `{"component":"ops/redis/RedisList","icon":"icon redis/redis","isKeepAlive":true,"routeName":"RedisList"}`, + Weight: 20000000, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 64}}}}, + Pid: 63, + UiPath: "RedisXq4/Eoaljc12/IoxqAd31/", + Name: "menu.redisManageBase", + Code: "redis:manage", + Type: 2, + Weight: 10000000, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 71}}}}, + Pid: 61, + UiPath: "RedisXq4/Exitx4al/IUlxia23/", + Name: "menu.redisDataOpSave", + Code: "redis:data:save", + Type: 2, + Weight: 29999999, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 72}}}}, + Pid: 3, + UiPath: "12sSjal1/lskeiql1/LIEwix43/", + Name: "menu.machineKillprocess", + Code: "machine:killprocess", + Type: 2, + Weight: 49999999, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 79}}}}, + Pid: 0, + UiPath: "Mongo452/", + Name: "menu.mongo", + Code: "/mongo", + Type: 1, + Meta: `{"icon":"icon mongo/mongo","isKeepAlive":true,"routeName":"Mongo"}`, + Weight: 50000002, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 80}}}}, + Pid: 79, + UiPath: "Mongo452/eggago31/", + Name: "menu.mongoDataOp", + Code: "mongo-data-operation", + Type: 1, + Meta: `{"component":"ops/mongo/MongoDataOp","icon":"icon mongo/mongo","isKeepAlive":true,"routeName":"MongoDataOp"}`, + Weight: 10000000, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 81}}}}, + Pid: 80, + UiPath: "Mongo452/eggago31/egjglal3/", + Name: "menu.mongoDataOpBase", + Code: "mongo:base", + Type: 2, + Weight: 10000000, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 82}}}}, + Pid: 79, + UiPath: "Mongo452/ghxagl43/", + Name: "menu.mongoManage", + Code: "mongo-manage", + Type: 1, + Meta: `{"component":"ops/mongo/MongoList","icon":"icon mongo/mongo","isKeepAlive":true,"routeName":"MongoList"}`, + Weight: 20000000, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 83}}}}, + Pid: 82, + UiPath: "Mongo452/ghxagl43/egljbla3/", + Name: "menu.mongoManageBase", + Code: "mongo:manage:base", + Type: 2, + Weight: 10000000, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 84}}}}, + Pid: 4, + UiPath: "Xlqig32x/exlaeAlx/", + Name: "menu.opLog", + Code: "syslogs", + Type: 1, + Meta: `{"component":"system/syslog/SyslogList","icon":"Tickets","routeName":"SyslogList"}`, + Weight: 20000000, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 85}}}}, + Pid: 84, + UiPath: "Xlqig32x/exlaeAlx/3xlqeXql/", + Name: "menu.opLogBase", + Code: "syslog", + Type: 2, + Weight: 10000000, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 87}}}}, + Pid: 4, + UiPath: "Xlqig32x/Ulxaee23/", + Name: "menu.sysConf", + Code: "configs", + Type: 1, + Meta: `{"component":"system/config/ConfigList","icon":"Setting","isKeepAlive":true,"routeName":"ConfigList"}`, + Weight: 10000002, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 88}}}}, + Pid: 87, + UiPath: "Xlqig32x/Ulxaee23/exlqguA3/", + Name: "menu.sysConfBase", + Code: "config:base", + Type: 2, + Weight: 10000000, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 93}}}}, + Pid: 0, + UiPath: "Tag3fhad/", + Name: "menu.tag", + Code: "/tag", + Type: 1, + Meta: `{"icon":"CollectionTag","isKeepAlive":true,"routeName":"Tag"}`, + Weight: 20000001, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 94}}}}, + Pid: 93, + UiPath: "Tag3fhad/glxajg23/", + Name: "menu.tagTree", + Code: "tag-trees", + Type: 1, + Meta: `{"component":"ops/tag/TagTreeList","icon":"CollectionTag","isKeepAlive":true,"routeName":"TagTreeList"}`, + Weight: 10000000, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 95}}}}, + Pid: 93, + UiPath: "Tag3fhad/Bjlag32x/", + Name: "menu.team", + Code: "teams", + Type: 1, + Meta: `{"component":"ops/tag/TeamList","icon":"UserFilled","isKeepAlive":true,"routeName":"TeamList"}`, + Weight: 20000000, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 96}}}}, + Pid: 94, + UiPath: "Tag3fhad/glxajg23/gkxagt23/", + Name: "menu.tagSave", + Code: "tag:save", + Type: 2, + Weight: 10000000, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 97}}}}, + Pid: 95, + UiPath: "Tag3fhad/Bjlag32x/GJslag32/", + Name: "menu.teamSave", + Code: "team:save", + Type: 2, + Weight: 10000000, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 98}}}}, + Pid: 94, + UiPath: "Tag3fhad/glxajg23/xjgalte2/", + Name: "menu.tagDelete", + Code: "tag:del", + Type: 2, + Weight: 20000000, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 99}}}}, + Pid: 95, + UiPath: "Tag3fhad/Bjlag32x/Gguca23x/", + Name: "menu.teamDelete", + Code: "team:del", + Type: 2, + Weight: 20000000, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 100}}}}, + Pid: 95, + UiPath: "Tag3fhad/Bjlag32x/Lgidsq32/", + Name: "menu.teamMemberAdd", + Code: "team:member:save", + Type: 2, + Weight: 30000000, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 101}}}}, + Pid: 95, + UiPath: "Tag3fhad/Bjlag32x/Lixaue3G/", + Name: "menu.teamMemberDelete", + Code: "team:member:del", + Type: 2, + Weight: 40000000, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 102}}}}, + Pid: 95, + UiPath: "Tag3fhad/Bjlag32x/Oygsq3xg/", + Name: "menu.teamTagSave", + Code: "team:tag:save", + Type: 2, + Weight: 50000000, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 103}}}}, + Pid: 93, + UiPath: "Tag3fhad/exahgl32/", + Name: "menu.authorization", + Code: "authcerts", + Type: 1, + Meta: `{"component":"ops/tag/AuthCertList","icon":"Ticket","isKeepAlive":true,"routeName":"AuthCertList"}`, + Weight: 19999999, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 104}}}}, + Pid: 103, + UiPath: "Tag3fhad/exahgl32/egxahg24/", + Name: "menu.authorizationBase", + Code: "authcert", + Type: 2, + Weight: 10000000, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 105}}}}, + Pid: 103, + UiPath: "Tag3fhad/exahgl32/yglxahg2/", + Name: "menu.authorizationSave", + Code: "authcert:save", + Type: 2, + Weight: 20000000, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 106}}}}, + Pid: 103, + UiPath: "Tag3fhad/exahgl32/Glxag234/", + Name: "menu.authorizationDelete", + Code: "authcert:del", + Type: 2, + Weight: 30000000, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 108}}}}, + Pid: 61, + UiPath: "RedisXq4/Exitx4al/Gxlagheg/", + Name: "menu.redisDataOpDelete", + Code: "redis:data:del", + Type: 2, + Weight: 30000000, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 128}}}}, + Pid: 87, + UiPath: "Xlqig32x/Ulxaee23/MoOWr2N0/", + Name: "menu.sysConfSave", + Code: "config:save", + Type: 2, + Weight: 1687315135, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 130}}}}, + Pid: 2, + UiPath: "12sSjal1/W9XKiabq/", + Name: "menu.machineCronJob", + Code: "/machine/cron-job", + Type: 1, + Meta: `{"component":"ops/machine/cronjob/CronJobList","icon":"AlarmClock","isKeepAlive":true,"routeName":"CronJobList"}`, + Weight: 1689646396, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 131}}}}, + Pid: 130, + UiPath: "12sSjal1/W9XKiabq/gEOqr2pD/", + Name: "menu.machineCronJobSvae", + Code: "machine:cronjob:save", + Type: 2, + Weight: 1689860087, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 132}}}}, + Pid: 130, + UiPath: "12sSjal1/W9XKiabq/zxXM23i0/", + Name: "menu.machineCronJobDelete", + Code: "machine:cronjob:del", + Type: 2, + Weight: 1689860102, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 133}}}}, + Pid: 80, + UiPath: "Mongo452/eggago31/xvpKk36u/", + Name: "menu.mongoDataOpSave", + Code: "mongo:data:save", + Type: 2, + Weight: 1692674943, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 134}}}}, + Pid: 80, + UiPath: "Mongo452/eggago31/3sblw1Wb/", + Name: "menu.mongoDataOpDelete", + Code: "mongo:data:del", + Type: 2, + Weight: 1692674964, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 135}}}}, + Pid: 36, + UiPath: "dbms23ax/X0f4BxT0/", + Name: "menu.dbInstance", + Code: "instances", + Type: 1, + Meta: `{"component":"ops/db/InstanceList","icon":"Coin","isKeepAlive":true,"routeName":"InstanceList"}`, + Weight: 1693040706, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 136}}}}, + Pid: 135, + UiPath: "dbms23ax/X0f4BxT0/D23fUiBr/", + Name: "menu.dbInstanceSave", + Code: "db:instance:save", + Type: 2, + Weight: 1693041001, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 137}}}}, + Pid: 135, + UiPath: "dbms23ax/X0f4BxT0/mJlBeTCs/", + Name: "menu.dbInstanceBase", + Code: "db:instance", + Type: 2, + Weight: 1693041000, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 138}}}}, + Pid: 135, + UiPath: "dbms23ax/X0f4BxT0/Sgg8uPwz/", + Name: "menu.dbInstanceDelete", + Code: "db:instance:del", + Type: 2, + Weight: 1693041084, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 150}}}}, + Pid: 36, + UiPath: "Jra0n7De/", + Name: "menu.dbDataSync", + Code: "sync", + Type: 1, + Meta: `{"component":"ops/db/SyncTaskList","icon":"Refresh","isKeepAlive":true,"routeName":"SyncTaskList"}`, + Weight: 1693040707, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 151}}}}, + Pid: 150, + UiPath: "Jra0n7De/uAnHZxEV/", + Name: "menu.dbDataSync", + Code: "db:sync", + Type: 2, + Weight: 1703641202, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 152}}}}, + Pid: 150, + UiPath: "Jra0n7De/zvAMo2vk/", + Name: "menu.dbDataSyncSave", + Code: "db:sync:save", + Type: 2, + Weight: 1703641320, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 153}}}}, + Pid: 150, + UiPath: "Jra0n7De/pLOA2UYz/", + Name: "menu.dbDataSyncDelete", + Code: "db:sync:del", + Type: 2, + Weight: 1703641342, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 154}}}}, + Pid: 150, + UiPath: "Jra0n7De/VBt68CDx/", + Name: "menu.dbDataSyncChangeStatus", + Code: "db:sync:status", + Type: 2, + Weight: 1703641364, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 155}}}}, + Pid: 150, + UiPath: "Jra0n7De/PigmSGVg/", + Name: "menu.dbDataSyncLog", + Code: "db:sync:log", + Type: 2, + Weight: 1704266866, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 1707206386}}}}, + Pid: 2, + UiPath: "PDPt6217/", + Name: "menu.machineOp", + Code: "machines-op", + Type: 1, + Meta: `{"component":"ops/machine/MachineOp","icon":"Monitor","isKeepAlive":true,"routeName":"MachineOp"}`, + Weight: 1, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 1707206421}}}}, + Pid: 1707206386, + UiPath: "PDPt6217/kQXTYvuM/", + Name: "menu.machineOpBase", + Code: "machine-op", + Type: 2, + Weight: 1707206421, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 1708910975}}}}, + Pid: 0, + UiPath: "6egfEVYr/", + Name: "menu.flow", + Code: "/flow", + Type: 1, + Meta: `{"icon":"List","isKeepAlive":true,"routeName":"flow"}`, + Weight: 60000000, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 1708911264}}}}, + Pid: 1708910975, + UiPath: "6egfEVYr/fw0Hhvye/", + Name: "menu.flowProcDef", + Code: "procdefs", + Type: 1, + Meta: `{"component":"flow/ProcdefList","icon":"List","isKeepAlive":true,"routeName":"ProcdefList"}`, + Weight: 1708911264, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 1709045735}}}}, + Pid: 1708910975, + UiPath: "6egfEVYr/3r3hHEub/", + Name: "menu.myTask", + Code: "procinst-tasks", + Type: 1, + Meta: `{"component":"flow/ProcinstTaskList","icon":"Tickets","isKeepAlive":true,"routeName":"ProcinstTaskList"}`, + Weight: 1708911263, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 1709103180}}}}, + Pid: 1708910975, + UiPath: "6egfEVYr/oNCIbynR/", + Name: "menu.myFlow", + Code: "procinsts", + Type: 1, + Meta: `{"component":"flow/ProcinstList","icon":"Tickets","isKeepAlive":true,"routeName":"ProcinstList"}`, + Weight: 1708911263, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 1709194669}}}}, + Pid: 36, + UiPath: "SmLcpu6c/", + Name: "menu.dbTransfer", + Code: "transfer", + Type: 1, + Meta: `{"component":"ops/db/DbTransferList","icon":"Switch","isKeepAlive":true,"routeName":"DbTransferList"}`, + Weight: 1709194669, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 1709194694}}}}, + Pid: 1709194669, + UiPath: "SmLcpu6c/A9vAm4J8/", + Name: "menu.dbTransferBase", + Code: "db:transfer", + Type: 2, + Weight: 1709194694, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 1709196697}}}}, + Pid: 1709194669, + UiPath: "SmLcpu6c/5oJwPzNb/", + Name: "menu.dbTransferSave", + Code: "db:transfer:save", + Type: 2, + Weight: 1709196697, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 1709196707}}}}, + Pid: 1709194669, + UiPath: "SmLcpu6c/L3ybnAEW/", + Name: "menu.dbTransferDelete", + Code: "db:transfer:del", + Type: 2, + Weight: 1709196707, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 1709196723}}}}, + Pid: 1709194669, + UiPath: "SmLcpu6c/hGiLN1VT/", + Name: "menu.dbTransferChangeStatus", + Code: "db:transfer:status", + Type: 2, + Weight: 1709196723, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 1709196737}}}}, + Pid: 1709194669, + UiPath: "SmLcpu6c/CZhNIbWg/", + Name: "menu.dbTransferRunLog", + Code: "db:transfer:log", + Type: 2, + Weight: 1709196737, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 1709196755}}}}, + Pid: 1709194669, + UiPath: "SmLcpu6c/b6yHt6V2/", + Name: "menu.dbTransferRun", + Code: "db:transfer:run", + Type: 2, + Weight: 1709196736, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 1709208339}}}}, + Pid: 1708911264, + UiPath: "6egfEVYr/fw0Hhvye/r9ZMTHqC/", + Name: "menu.flowProcDefSave", + Code: "flow:procdef:save", + Type: 2, + Weight: 1709208339, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 1709208354}}}}, + Pid: 1708911264, + UiPath: "6egfEVYr/fw0Hhvye/b4cNf3iq/", + Name: "menu.flowProcDefDelete", + Code: "flow:procdef:del", + Type: 2, + Weight: 1709208354, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 1712717290}}}}, + Pid: 0, + UiPath: "tLb8TKLB/", + Name: "menu.noPagePermission", + Code: "empty", + Type: 1, + Meta: `{"component":"empty","icon":"Menu","isHide":true,"isKeepAlive":true,"routeName":"empty"}`, + Weight: 60000002, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 1712717337}}}}, + Pid: 1712717290, + UiPath: "tLb8TKLB/m2abQkA8/", + Name: "menu.authcertShowciphertext", + Code: "authcert:showciphertext", + Type: 2, + Weight: 1712717337, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 1713875842}}}}, + Pid: 2, + UiPath: "12sSjal1/UnWIUhW0/", + Name: "menu.machineSecurityConfig", + Code: "security", + Type: 1, + Meta: `{"component":"ops/machine/security/SecurityConfList","icon":"Setting","isKeepAlive":true,"routeName":"SecurityConfList"}`, + Weight: 1713875842, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 1714031981}}}}, + Pid: 1713875842, + UiPath: "12sSjal1/UnWIUhW0/tEzIKecl/", + Name: "menu.machineSecurityCmdSvae", + Code: "cmdconf:save", + Type: 2, + Weight: 1714031981, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 1714032002}}}}, + Pid: 1713875842, + UiPath: "12sSjal1/UnWIUhW0/0tJwC3Gf/", + Name: "menu.machineSecurityCmdDelete", + Code: "cmdconf:del", + Type: 2, + Weight: 1714032002, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 1724376022}}}}, + Pid: 1709194669, + UiPath: "SmLcpu6c/HIURtJJA/", + Name: "menu.dbTransferFileDelete", + Code: "db:transfer:files:del", + Type: 2, + Weight: 1724376022, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 1724395850}}}}, + Pid: 1709194669, + UiPath: "SmLcpu6c/FmqK4azt/", + Name: "menu.dbTransferFileDownload", + Code: "db:transfer:files:down", + Type: 2, + Weight: 1724395850, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 1724398262}}}}, + Pid: 1709194669, + UiPath: "SmLcpu6c/btVtrbhk/", + Name: "menu.dbTransferFileShow", + Code: "db:transfer:files", + Type: 2, + Weight: 1724376021, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 1724998419}}}}, + Pid: 1709194669, + UiPath: "SmLcpu6c/qINungml/", + Name: "menu.dbTransferFileRun", + Code: "db:transfer:files:run", + Type: 2, + Weight: 1724998419, + }, + { + Model: model.Model{CreateModel: model.CreateModel{DeletedModel: model.DeletedModel{IdModel: model.IdModel{Id: 1729668131}}}}, + Pid: 38, + UiPath: "dbms23ax/exaeca2x/TGFPA3Ez/", + Name: "menu.dbDataOpSqlScriptRun", + Code: "db:sqlscript:run", + Type: 2, + Weight: 1729668131, + }, + } + + now := time.Now() + for _, res := range resources { + res.Status = 1 + res.CreateTime = &now + res.CreatorId = 1 + res.Creator = "admin" + res.UpdateTime = &now + res.ModifierId = 1 + res.Modifier = "admin" + if err := tx.Create(res).Error; err != nil { + return err + } + } + + return nil +} diff --git a/server/migration/migrations/v1_9.go b/server/migration/migrations/v1_9.go new file mode 100644 index 00000000..62cf5612 --- /dev/null +++ b/server/migration/migrations/v1_9.go @@ -0,0 +1,42 @@ +package migrations + +import ( + machineentity "mayfly-go/internal/machine/domain/entity" + sysentity "mayfly-go/internal/sys/domain/entity" + + "github.com/go-gormigrate/gormigrate/v2" + "gorm.io/gorm" +) + +func V1_9() []*gormigrate.Migration { + var migrations []*gormigrate.Migration + migrations = append(migrations, V1_9_3()...) + return migrations +} + +func V1_9_3() []*gormigrate.Migration { + return []*gormigrate.Migration{ + { + ID: "20250213-v1.9.3-addMachineExtra-updateMenuIcon", + Migrate: func(tx *gorm.DB) error { + tx.Migrator().AddColumn(&machineentity.Machine{}, "extra") + + // 更新菜单图标 + resourceModel := &sysentity.Resource{} + tx.Model(resourceModel).Where("id = ?", 11).Update("meta", `{"component":"system/role/RoleList","icon":"icon menu/role","isKeepAlive":true,"routeName":"RoleList"}`) + tx.Model(resourceModel).Where("id = ?", 14).Update("meta", `{"component":"system/account/AccountList","icon":"User","isKeepAlive":true,"routeName":"AccountList"}`) + tx.Model(resourceModel).Where("id = ?", 150).Update("meta", `{"component":"ops/db/SyncTaskList","icon":"Refresh","isKeepAlive":true,"routeName":"SyncTaskList"}`) + tx.Model(resourceModel).Where("id = ?", 60).Update("meta", `{"icon":"icon redis/redis","isKeepAlive":true,"routeName":"RDS"}`) + tx.Model(resourceModel).Where("id = ?", 61).Update("meta", `{"component":"ops/redis/DataOperation","icon":"icon redis/redis","isKeepAlive":true,"routeName":"DataOperation"}`) + tx.Model(resourceModel).Where("id = ?", 63).Update("meta", `{"component":"ops/redis/RedisList","icon":"icon redis/redis","isKeepAlive":true,"routeName":"RedisList"}`) + tx.Model(resourceModel).Where("id = ?", 79).Update("meta", `{"icon":"icon mongo/mongo","isKeepAlive":true,"routeName":"Mongo"}`) + tx.Model(resourceModel).Where("id = ?", 80).Update("meta", `{"component":"ops/mongo/MongoDataOp","icon":"icon mongo/mongo","isKeepAlive":true,"routeName":"MongoDataOp"}`) + tx.Model(resourceModel).Where("id = ?", 82).Update("meta", `{"component":"ops/mongo/MongoList","icon":"icon mongo/mongo","isKeepAlive":true,"routeName":"MongoList"}`) + return nil + }, + Rollback: func(tx *gorm.DB) error { + return nil + }, + }, + } +} diff --git a/server/migrations/2022.go b/server/migrations/2022.go deleted file mode 100644 index 776e9bb4..00000000 --- a/server/migrations/2022.go +++ /dev/null @@ -1,102 +0,0 @@ -package migrations - -import ( - entity2 "mayfly-go/internal/db/domain/entity" - "mayfly-go/internal/machine/domain/entity" - entity3 "mayfly-go/internal/mongo/domain/entity" - entity6 "mayfly-go/internal/msg/domain/entity" - entity4 "mayfly-go/internal/redis/domain/entity" - entity5 "mayfly-go/internal/sys/domain/entity" - entity7 "mayfly-go/internal/tag/domain/entity" - - "github.com/go-gormigrate/gormigrate/v2" - "gorm.io/gorm" -) - -// T2022 TODO 在此之前的数据库表结构初始化, 目前先使用mayfly-go.sql文件初始化数据库结构 -func T2022() *gormigrate.Migration { - return &gormigrate.Migration{ - ID: "2022", - Migrate: func(tx *gorm.DB) error { - if err := tx.AutoMigrate(&entity.Machine{}); err != nil { - return err - } - if err := tx.AutoMigrate(&entity.MachineFile{}); err != nil { - return err - } - if err := tx.AutoMigrate(&entity.MachineMonitor{}); err != nil { - return err - } - if err := tx.AutoMigrate(&entity.MachineScript{}); err != nil { - return err - } - if err := tx.AutoMigrate(&entity.MachineCronJob{}); err != nil { - return err - } - if err := tx.AutoMigrate(&entity.MachineCronJobExec{}); err != nil { - return err - } - - if err := tx.AutoMigrate(&entity2.DbInstance{}); err != nil { - return err - } - if err := tx.AutoMigrate(&entity2.Db{}); err != nil { - return err - } - if err := tx.AutoMigrate(&entity2.DbSql{}); err != nil { - return err - } - if err := tx.AutoMigrate(&entity2.DbSqlExec{}); err != nil { - return err - } - - if err := tx.AutoMigrate(&entity3.Mongo{}); err != nil { - return err - } - - if err := tx.AutoMigrate(&entity4.Redis{}); err != nil { - return err - } - - if err := tx.AutoMigrate(&entity5.Account{}); err != nil { - return err - } - if err := tx.AutoMigrate(&entity5.AccountRole{}); err != nil { - return err - } - if err := tx.AutoMigrate(&entity5.Config{}); err != nil { - return err - } - if err := tx.AutoMigrate(&entity5.SysLog{}); err != nil { - return err - } - if err := tx.AutoMigrate(&entity5.Resource{}); err != nil { - return err - } - if err := tx.AutoMigrate(&entity5.Role{}); err != nil { - return err - } - if err := tx.AutoMigrate(&entity5.RoleResource{}); err != nil { - return err - } - - if err := tx.AutoMigrate(&entity6.Msg{}); err != nil { - return err - } - if err := tx.AutoMigrate(&entity7.TagTree{}); err != nil { - return err - } - if err := tx.AutoMigrate(&entity7.Team{}); err != nil { - return err - } - if err := tx.AutoMigrate(&entity7.TeamMember{}); err != nil { - return err - } - - return nil - }, - Rollback: func(tx *gorm.DB) error { - return nil - }, - } -} diff --git a/server/migrations/20230720.go b/server/migrations/20230720.go deleted file mode 100644 index 70f6fc8a..00000000 --- a/server/migrations/20230720.go +++ /dev/null @@ -1,21 +0,0 @@ -package migrations - -import ( - authentity "mayfly-go/internal/auth/domain/entity" - - "github.com/go-gormigrate/gormigrate/v2" - "gorm.io/gorm" -) - -// T20230720 三方登录表 -func T20230720() *gormigrate.Migration { - return &gormigrate.Migration{ - ID: "20230720", - Migrate: func(tx *gorm.DB) error { - return tx.AutoMigrate(&authentity.Oauth2Account{}) - }, - Rollback: func(tx *gorm.DB) error { - return nil - }, - } -} diff --git a/server/migrations/20231115.go b/server/migrations/20231115.go deleted file mode 100644 index 352cffd9..00000000 --- a/server/migrations/20231115.go +++ /dev/null @@ -1,32 +0,0 @@ -package migrations - -import ( - "github.com/go-gormigrate/gormigrate/v2" - "gorm.io/gorm" - "mayfly-go/internal/db/domain/entity" -) - -func T20231125() *gormigrate.Migration { - return &gormigrate.Migration{ - ID: "20231115", - Migrate: func(tx *gorm.DB) error { - entities := [...]any{ - new(entity.DbBackup), - new(entity.DbBackupHistory), - new(entity.DbRestore), - new(entity.DbRestoreHistory), - new(entity.DbBinlog), - new(entity.DbBinlogHistory), - } - for _, e := range entities { - if err := tx.AutoMigrate(e); err != nil { - return err - } - } - return nil - }, - Rollback: func(tx *gorm.DB) error { - return nil - }, - } -} diff --git a/server/migrations/init.go b/server/migrations/init.go deleted file mode 100644 index 08178ae3..00000000 --- a/server/migrations/init.go +++ /dev/null @@ -1,74 +0,0 @@ -package migrations - -import ( - "mayfly-go/internal/sys/domain/entity" - "mayfly-go/pkg/config" - "mayfly-go/pkg/model" - "mayfly-go/pkg/rediscli" - "time" - - "github.com/go-gormigrate/gormigrate/v2" - "gorm.io/gorm" -) - -// RunMigrations 数据库迁移操作 -func RunMigrations(db *gorm.DB) error { - // 添加分布式锁, 防止多个服务同时执行迁移 - lock := rediscli.NewLock("mayfly:db:migrations", 1*time.Minute) - if lock != nil { - if !lock.Lock() { - return nil - } - defer lock.UnLock() - } - - if !config.Conf.Mysql.AutoMigration { - return nil - } - - return run(db, - // T2022, - // T20230720, - T20231125, - ) -} - -func run(db *gorm.DB, fs ...func() *gormigrate.Migration) error { - var ms []*gormigrate.Migration - for _, f := range fs { - ms = append(ms, f()) - } - m := gormigrate.New(db, &gormigrate.Options{ - TableName: "migrations", - IDColumnName: "id", - IDColumnSize: 200, - UseTransaction: true, - ValidateUnknownMigrations: true, - }, ms) - if err := m.Migrate(); err != nil { - return err - } - return nil -} - -func insertResource(tx *gorm.DB, res *entity.Resource) error { - now := time.Now() - res.CreateTime = &now - res.CreatorId = 1 - res.Creator = "admin" - res.UpdateTime = &now - res.ModifierId = 1 - res.Modifier = "admin" - if err := tx.Save(res).Error; err != nil { - return err - } - - return tx.Save(&entity.RoleResource{ - DeletedModel: model.DeletedModel{}, - RoleId: 1, - ResourceId: res.Id, - CreateTime: &now, - CreatorId: 1, - Creator: "admin", - }).Error -} diff --git a/server/pkg/config/mysql.go b/server/pkg/config/mysql.go index 5c06ca81..d706ae71 100644 --- a/server/pkg/config/mysql.go +++ b/server/pkg/config/mysql.go @@ -3,16 +3,15 @@ package config import "mayfly-go/pkg/logx" type Mysql struct { - AutoMigration bool `mapstructure:"auto-migration" json:"autoMigration" yaml:"auto-migration"` - Host string `mapstructure:"path" json:"host" yaml:"host"` - Config string `mapstructure:"config" json:"config" yaml:"config"` - Dbname string `mapstructure:"db-name" json:"dbname" yaml:"db-name"` - Username string `mapstructure:"username" json:"username" yaml:"username"` - Password string `mapstructure:"password" json:"password" yaml:"password"` - MaxIdleConns int `mapstructure:"max-idle-conns" json:"maxIdleConns" yaml:"max-idle-conns"` - MaxOpenConns int `mapstructure:"max-open-conns" json:"maxOpenConns" yaml:"max-open-conns"` - LogMode bool `mapstructure:"log-mode" json:"logMode" yaml:"log-mode"` - LogZap string `mapstructure:"log-zap" json:"logZap" yaml:"log-zap"` + Host string `mapstructure:"path" json:"host" yaml:"host"` + Config string `mapstructure:"config" json:"config" yaml:"config"` + Dbname string `mapstructure:"db-name" json:"dbname" yaml:"db-name"` + Username string `mapstructure:"username" json:"username" yaml:"username"` + Password string `mapstructure:"password" json:"password" yaml:"password"` + MaxIdleConns int `mapstructure:"max-idle-conns" json:"maxIdleConns" yaml:"max-idle-conns"` + MaxOpenConns int `mapstructure:"max-open-conns" json:"maxOpenConns" yaml:"max-open-conns"` + LogMode bool `mapstructure:"log-mode" json:"logMode" yaml:"log-mode"` + LogZap string `mapstructure:"log-zap" json:"logZap" yaml:"log-zap"` } func (m *Mysql) Default() { diff --git a/server/pkg/model/model.go b/server/pkg/model/model.go index 3e6b4f08..e7f20d24 100644 --- a/server/pkg/model/model.go +++ b/server/pkg/model/model.go @@ -43,7 +43,7 @@ type ModelI interface { } type IdModel struct { - Id uint64 `json:"id"` + Id uint64 `json:"id" gorm:"primarykey;AUTO_INCREMENT"` } func (m *IdModel) SetId(id uint64) { @@ -69,7 +69,7 @@ func (m *IdModel) LogicDelete() bool { // 含有删除字段模型 type DeletedModel struct { IdModel - IsDeleted int8 `json:"-" gorm:"column:is_deleted;default:0"` + IsDeleted int8 `json:"-" gorm:"column:is_deleted;not null;default:0;"` DeleteTime *time.Time `json:"-"` } @@ -87,9 +87,9 @@ func (m *DeletedModel) LogicDelete() bool { // CreateModelNLD 含有创建等信息,但不包含逻辑删除信息 type CreateModelNLD struct { IdModel - CreateTime *time.Time `json:"createTime"` - CreatorId uint64 `json:"creatorId"` - Creator string `json:"creator"` + CreateTime *time.Time `json:"createTime" gorm:"not null;"` + CreatorId uint64 `json:"creatorId" gorm:"not null;"` + Creator string `json:"creator" gorm:"size:32;not null;"` } func (m *CreateModelNLD) FillBaseInfo(idGenType IdGenType, account *LoginAccount) { @@ -109,9 +109,9 @@ func (m *CreateModelNLD) FillBaseInfo(idGenType IdGenType, account *LoginAccount // 含有删除、创建字段模型 type CreateModel struct { DeletedModel - CreateTime *time.Time `json:"createTime"` - CreatorId uint64 `json:"creatorId"` - Creator string `json:"creator"` + CreateTime *time.Time `json:"createTime" gorm:"not null;"` + CreatorId uint64 `json:"creatorId" gorm:"not null;"` + Creator string `json:"creator" gorm:"size:32;not null;"` } func (m *CreateModel) FillBaseInfo(idGenType IdGenType, account *LoginAccount) { @@ -132,9 +132,9 @@ func (m *CreateModel) FillBaseInfo(idGenType IdGenType, account *LoginAccount) { type ModelNLD struct { CreateModelNLD - UpdateTime *time.Time `json:"updateTime"` - ModifierId uint64 `json:"modifierId"` - Modifier string `json:"modifier"` + UpdateTime *time.Time `json:"updateTime" gorm:"not null;"` + ModifierId uint64 `json:"modifierId" gorm:"not null;"` + Modifier string `json:"modifier" gorm:"size:32;not null;"` } // 设置基础信息. 如创建时间,修改时间,创建者,修改者信息 @@ -164,9 +164,9 @@ func (m *ModelNLD) FillBaseInfo(idGenType IdGenType, account *LoginAccount) { type Model struct { CreateModel - UpdateTime *time.Time `json:"updateTime"` - ModifierId uint64 `json:"modifierId"` - Modifier string `json:"modifier"` + UpdateTime *time.Time `json:"updateTime" gorm:"not null;"` + ModifierId uint64 `json:"modifierId" gorm:"not null;"` + Modifier string `json:"modifier" gorm:"size:32;not null;"` } // 设置基础信息. 如创建时间,修改时间,创建者,修改者信息 @@ -223,7 +223,7 @@ func (s Slice[T]) Value() (driver.Value, error) { // 带有额外其他信息字段的结构体 type ExtraData struct { - Extra Map[string, any] `json:"extra"` + Extra Map[string, any] `json:"extra" gorm:"type:varchar(1000)"` } // SetExtraValue 设置额外信息字段值 diff --git a/server/pkg/starter/run.go b/server/pkg/starter/run.go index da3c9818..26a635ac 100644 --- a/server/pkg/starter/run.go +++ b/server/pkg/starter/run.go @@ -3,7 +3,7 @@ package starter import ( "context" "mayfly-go/initialize" - "mayfly-go/migrations" + "mayfly-go/migration" "mayfly-go/pkg/config" "mayfly-go/pkg/global" "mayfly-go/pkg/logx" @@ -37,8 +37,8 @@ func RunWebServer() { initRedis() // 数据库升级操作 - if err := migrations.RunMigrations(global.Db); err != nil { - logx.Panicf("数据库升级失败: %v", err) + if err := migration.RunMigrations(global.Db); err != nil { + logx.Panicf("db migration failed: %v", err) } // 参数校验器初始化、如错误提示中文转译等 diff --git a/server/resources/data/mayfly-go.sqlite b/server/resources/data/mayfly-go.sqlite deleted file mode 100644 index 7aff96fb483da3f4387dfbb8b925af6acdd6bc17..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 335872 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 mGDVg^bGsiehoaf*D=U=s5(QqHF;1O?FCsY4oG4