mirror of
https://gitee.com/dromara/mayfly-go
synced 2025-11-04 00:10:25 +08:00
feat: i18n
This commit is contained in:
@@ -32,9 +32,9 @@ require (
|
||||
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.28.0 // ssh
|
||||
golang.org/x/crypto v0.29.0 // ssh
|
||||
golang.org/x/oauth2 v0.23.0
|
||||
golang.org/x/sync v0.8.0
|
||||
golang.org/x/sync v0.9.0
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
// gorm
|
||||
@@ -93,8 +93,8 @@ require (
|
||||
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.26.0 // indirect
|
||||
golang.org/x/text v0.19.0 // indirect
|
||||
golang.org/x/sys v0.27.0 // indirect
|
||||
golang.org/x/text v0.20.0 // indirect
|
||||
google.golang.org/protobuf v1.34.1 // indirect
|
||||
modernc.org/libc v1.22.5 // indirect
|
||||
modernc.org/mathutil v1.5.0 // indirect
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"fmt"
|
||||
"mayfly-go/internal/auth/api/form"
|
||||
"mayfly-go/internal/auth/config"
|
||||
"mayfly-go/internal/common/utils"
|
||||
"mayfly-go/internal/auth/imsg"
|
||||
msgapp "mayfly-go/internal/msg/application"
|
||||
sysapp "mayfly-go/internal/sys/application"
|
||||
sysentity "mayfly-go/internal/sys/domain/entity"
|
||||
@@ -33,12 +33,13 @@ type AccountLogin struct {
|
||||
// @router /auth/accounts/login [post]
|
||||
func (a *AccountLogin) Login(rc *req.Ctx) {
|
||||
loginForm := req.BindJsonAndValid(rc, new(form.LoginForm))
|
||||
ctx := rc.MetaCtx
|
||||
|
||||
accountLoginSecurity := config.GetAccountLoginSecurity()
|
||||
// 判断是否有开启登录验证码校验
|
||||
if accountLoginSecurity.UseCaptcha {
|
||||
// 校验验证码
|
||||
biz.IsTrue(captcha.Verify(loginForm.Cid, loginForm.Captcha), "验证码错误")
|
||||
biz.IsTrueI(ctx, captcha.Verify(loginForm.Cid, loginForm.Captcha), imsg.ErrCaptchaErr)
|
||||
}
|
||||
|
||||
username := loginForm.Username
|
||||
@@ -47,7 +48,7 @@ func (a *AccountLogin) Login(rc *req.Ctx) {
|
||||
rc.ReqParam = collx.Kvs("username", username, "ip", clientIp)
|
||||
|
||||
originPwd, err := cryptox.DefaultRsaDecrypt(loginForm.Password, true)
|
||||
biz.ErrIsNilAppendErr(err, "解密密码错误: %s")
|
||||
biz.ErrIsNilAppendErr(err, "decryption password error: %s")
|
||||
|
||||
account := &sysentity.Account{Username: username}
|
||||
err = a.AccountApp.GetByCond(model.NewModelCond(account).Columns("Id", "Name", "Username", "Password", "Status", "LastLoginTime", "LastLoginIp", "OtpSecret"))
|
||||
@@ -56,17 +57,17 @@ func (a *AccountLogin) Login(rc *req.Ctx) {
|
||||
nowFailCount := cache.GetInt(failCountKey)
|
||||
loginFailCount := accountLoginSecurity.LoginFailCount
|
||||
loginFailMin := accountLoginSecurity.LoginFailMin
|
||||
biz.IsTrue(nowFailCount < loginFailCount, "登录失败超过%d次, 请%d分钟后再试", loginFailCount, loginFailMin)
|
||||
biz.IsTrueI(ctx, nowFailCount < loginFailCount, imsg.ErrLoginRestrict, "failCount", loginFailCount, "min", loginFailMin)
|
||||
|
||||
if err != nil || !cryptox.CheckPwdHash(originPwd, account.Password) {
|
||||
nowFailCount++
|
||||
cache.SetStr(failCountKey, strconv.Itoa(nowFailCount), time.Minute*time.Duration(loginFailMin))
|
||||
panic(errorx.NewBiz(fmt.Sprintf("用户名或密码错误【当前登录失败%d次】", nowFailCount)))
|
||||
panic(errorx.NewBizI(ctx, imsg.ErrLoginFail, "failCount", nowFailCount))
|
||||
}
|
||||
|
||||
// 校验密码强度(新用户第一次登录密码与账号名一致)
|
||||
biz.IsTrueBy(utils.CheckAccountPasswordLever(originPwd), errorx.NewBizCode(401, "您的密码安全等级较低,请修改后重新登录"))
|
||||
rc.ResData = LastLoginCheck(account, accountLoginSecurity, clientIp)
|
||||
// biz.IsTrueBy(utils.CheckAccountPasswordLever(originPwd), errorx.NewBizCode(401, "您的密码安全等级较低,请修改后重新登录"))
|
||||
rc.ResData = LastLoginCheck(ctx, account, accountLoginSecurity, clientIp)
|
||||
}
|
||||
|
||||
type OtpVerifyInfo struct {
|
||||
@@ -82,15 +83,16 @@ type OtpVerifyInfo struct {
|
||||
func (a *AccountLogin) OtpVerify(rc *req.Ctx) {
|
||||
otpVerify := new(form.OtpVerfiy)
|
||||
req.BindJsonAndValid(rc, otpVerify)
|
||||
ctx := rc.MetaCtx
|
||||
|
||||
tokenKey := fmt.Sprintf("otp:token:%s", otpVerify.OtpToken)
|
||||
otpInfo := new(OtpVerifyInfo)
|
||||
ok := cache.Get(tokenKey, otpInfo)
|
||||
biz.IsTrue(ok, "otpToken错误或失效, 请重新登陆获取")
|
||||
biz.IsTrueI(ctx, ok, imsg.ErrOtpTokenInvalid)
|
||||
|
||||
failCountKey := fmt.Sprintf("account:otp:failcount:%d", otpInfo.AccountId)
|
||||
failCount := cache.GetInt(failCountKey)
|
||||
biz.IsTrue(failCount < 5, "双因素校验失败超过5次, 请10分钟后再试")
|
||||
biz.IsTrueI(ctx, failCount < 5, imsg.ErrOtpCheckRestrict)
|
||||
|
||||
otpStatus := otpInfo.OptStatus
|
||||
accessToken := otpInfo.AccessToken
|
||||
@@ -99,7 +101,7 @@ func (a *AccountLogin) OtpVerify(rc *req.Ctx) {
|
||||
|
||||
if !otp.Validate(otpVerify.Code, otpSecret) {
|
||||
cache.SetStr(failCountKey, strconv.Itoa(failCount+1), time.Minute*time.Duration(10))
|
||||
panic(errorx.NewBiz("双因素认证授权码不正确"))
|
||||
panic(errorx.NewBizI(ctx, imsg.ErrOtpCheckFail))
|
||||
}
|
||||
|
||||
// 如果是未注册状态,则更新account表的otpSecret信息
|
||||
@@ -112,7 +114,7 @@ func (a *AccountLogin) OtpVerify(rc *req.Ctx) {
|
||||
|
||||
la := &sysentity.Account{Username: otpInfo.Username}
|
||||
la.Id = accountId
|
||||
go saveLogin(la, getIpAndRegion(rc))
|
||||
go saveLogin(ctx, la, getIpAndRegion(rc))
|
||||
|
||||
cache.Del(tokenKey)
|
||||
rc.ResData = collx.Kvs("token", accessToken, "refresh_token", otpInfo.RefreshToken)
|
||||
@@ -120,7 +122,7 @@ func (a *AccountLogin) OtpVerify(rc *req.Ctx) {
|
||||
|
||||
func (a *AccountLogin) RefreshToken(rc *req.Ctx) {
|
||||
refreshToken := rc.Query("refresh_token")
|
||||
biz.NotEmpty(refreshToken, "refresh_token不能为空")
|
||||
biz.NotEmpty(refreshToken, "refresh_token cannot be empty")
|
||||
|
||||
accountId, username, err := req.ParseToken(refreshToken)
|
||||
biz.IsTrueBy(err == nil, errorx.PermissionErr)
|
||||
|
||||
@@ -4,12 +4,14 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"mayfly-go/internal/auth/config"
|
||||
"mayfly-go/internal/auth/imsg"
|
||||
msgapp "mayfly-go/internal/msg/application"
|
||||
msgentity "mayfly-go/internal/msg/domain/entity"
|
||||
sysapp "mayfly-go/internal/sys/application"
|
||||
sysentity "mayfly-go/internal/sys/domain/entity"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/cache"
|
||||
"mayfly-go/pkg/i18n"
|
||||
"mayfly-go/pkg/otp"
|
||||
"mayfly-go/pkg/req"
|
||||
"mayfly-go/pkg/utils/collx"
|
||||
@@ -27,8 +29,8 @@ const (
|
||||
)
|
||||
|
||||
// 最后的登录校验(共用)。校验通过返回登录成功响应结果map
|
||||
func LastLoginCheck(account *sysentity.Account, accountLoginSecurity *config.AccountLoginSecurity, loginIp string) map[string]any {
|
||||
biz.IsTrue(account.IsEnable(), "该账号不可用")
|
||||
func LastLoginCheck(ctx context.Context, account *sysentity.Account, accountLoginSecurity *config.AccountLoginSecurity, loginIp string) map[string]any {
|
||||
biz.IsTrueI(ctx, account.IsEnable(), imsg.ErrAccountNotAvailable)
|
||||
username := account.Username
|
||||
|
||||
res := collx.M{
|
||||
@@ -42,7 +44,7 @@ func LastLoginCheck(account *sysentity.Account, accountLoginSecurity *config.Acc
|
||||
otpStatus := OtpStatusNone
|
||||
// 访问系统使用的token
|
||||
accessToken, refreshToken, err := req.CreateToken(account.Id, username)
|
||||
biz.ErrIsNilAppendErr(err, "token创建失败: %s")
|
||||
biz.ErrIsNilAppendErr(err, "token create failed: %s")
|
||||
|
||||
// 若系统配置中设置开启otp双因素校验,则进行otp校验
|
||||
if accountLoginSecurity.UseOtp {
|
||||
@@ -56,7 +58,7 @@ func LastLoginCheck(account *sysentity.Account, accountLoginSecurity *config.Acc
|
||||
res["refresh_token"] = refreshToken
|
||||
// 不进行otp二次校验则直接返回accessToken
|
||||
// 保存登录消息
|
||||
go saveLogin(account, loginIp)
|
||||
go saveLogin(ctx, account, loginIp)
|
||||
}
|
||||
|
||||
// 赋值otp状态
|
||||
@@ -80,7 +82,7 @@ func useOtp(account *sysentity.Account, otpIssuer, accessToken string, refreshTo
|
||||
AccountName: account.Username,
|
||||
Issuer: otpIssuer,
|
||||
})
|
||||
biz.ErrIsNilAppendErr(err, "otp生成失败: %s")
|
||||
biz.ErrIsNilAppendErr(err, "otp generate failed: %s")
|
||||
otpUrl = key.URL()
|
||||
otpSecret = key.Secret()
|
||||
}
|
||||
@@ -104,7 +106,7 @@ func getIpAndRegion(rc *req.Ctx) string {
|
||||
}
|
||||
|
||||
// 保存更新账号登录信息
|
||||
func saveLogin(account *sysentity.Account, ip string) {
|
||||
func saveLogin(ctx context.Context, account *sysentity.Account, ip string) {
|
||||
// 更新账号最后登录时间
|
||||
now := time.Now()
|
||||
updateAccount := &sysentity.Account{LastLoginTime: &now}
|
||||
@@ -116,7 +118,7 @@ func saveLogin(account *sysentity.Account, ip string) {
|
||||
// 创建登录消息
|
||||
loginMsg := &msgentity.Msg{
|
||||
RecipientId: int64(account.Id),
|
||||
Msg: fmt.Sprintf("于[%s]-[%s]登录", ip, timex.DefaultFormat(now)),
|
||||
Msg: i18n.TC(ctx, imsg.LoginMsg, "ip", ip, "time", timex.DefaultFormat(now)),
|
||||
Type: 1,
|
||||
}
|
||||
loginMsg.CreateTime = &now
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"fmt"
|
||||
"mayfly-go/internal/auth/api/form"
|
||||
"mayfly-go/internal/auth/config"
|
||||
"mayfly-go/internal/auth/imsg"
|
||||
msgapp "mayfly-go/internal/msg/application"
|
||||
sysapp "mayfly-go/internal/sys/application"
|
||||
sysentity "mayfly-go/internal/sys/domain/entity"
|
||||
@@ -40,12 +41,12 @@ func (a *LdapLogin) GetLdapEnabled(rc *req.Ctx) {
|
||||
// @router /auth/ldap/login [post]
|
||||
func (a *LdapLogin) Login(rc *req.Ctx) {
|
||||
loginForm := req.BindJsonAndValid(rc, new(form.LoginForm))
|
||||
|
||||
ctx := rc.MetaCtx
|
||||
accountLoginSecurity := config.GetAccountLoginSecurity()
|
||||
// 判断是否有开启登录验证码校验
|
||||
if accountLoginSecurity.UseCaptcha {
|
||||
// 校验验证码
|
||||
biz.IsTrue(captcha.Verify(loginForm.Cid, loginForm.Captcha), "验证码错误")
|
||||
biz.IsTrueI(ctx, captcha.Verify(loginForm.Cid, loginForm.Captcha), imsg.ErrCaptchaErr)
|
||||
}
|
||||
|
||||
username := loginForm.Username
|
||||
@@ -54,27 +55,27 @@ func (a *LdapLogin) Login(rc *req.Ctx) {
|
||||
rc.ReqParam = collx.Kvs("username", username, "ip", clientIp)
|
||||
|
||||
originPwd, err := cryptox.DefaultRsaDecrypt(loginForm.Password, true)
|
||||
biz.ErrIsNilAppendErr(err, "解密密码错误: %s")
|
||||
biz.ErrIsNilAppendErr(err, "decryption password error: %s")
|
||||
// LDAP 用户本地密码为空,不允许本地登录
|
||||
biz.NotEmpty(originPwd, "密码不能为空")
|
||||
biz.NotEmpty(originPwd, "password cannot be empty")
|
||||
|
||||
failCountKey := fmt.Sprintf("account:login:failcount:%s", username)
|
||||
nowFailCount := cache.GetInt(failCountKey)
|
||||
loginFailCount := accountLoginSecurity.LoginFailCount
|
||||
loginFailMin := accountLoginSecurity.LoginFailMin
|
||||
biz.IsTrue(nowFailCount < loginFailCount, "登录失败超过%d次, 请%d分钟后再试", loginFailCount, loginFailMin)
|
||||
biz.IsTrueI(ctx, nowFailCount < loginFailCount, imsg.ErrLoginRestrict, "failCount", loginFailCount, "min", loginFailMin)
|
||||
|
||||
var account *sysentity.Account
|
||||
cols := []string{"Id", "Name", "Username", "Password", "Status", "LastLoginTime", "LastLoginIp", "OtpSecret"}
|
||||
account, err = a.getOrCreateUserWithLdap(username, originPwd, cols...)
|
||||
account, err = a.getOrCreateUserWithLdap(ctx, username, originPwd, cols...)
|
||||
|
||||
if err != nil {
|
||||
nowFailCount++
|
||||
cache.SetStr(failCountKey, strconv.Itoa(nowFailCount), time.Minute*time.Duration(loginFailMin))
|
||||
panic(errorx.NewBiz(fmt.Sprintf("用户名或密码错误【当前登录失败%d次】", nowFailCount)))
|
||||
panic(errorx.NewBizI(ctx, imsg.ErrLoginFail, "failCount", nowFailCount))
|
||||
}
|
||||
|
||||
rc.ResData = LastLoginCheck(account, accountLoginSecurity, clientIp)
|
||||
rc.ResData = LastLoginCheck(ctx, account, accountLoginSecurity, clientIp)
|
||||
}
|
||||
|
||||
func (a *LdapLogin) getUser(userName string, cols ...string) (*sysentity.Account, error) {
|
||||
@@ -95,10 +96,10 @@ func (a *LdapLogin) createUser(userName, displayName string) {
|
||||
biz.ErrIsNil(a.AccountApp.Update(context.TODO(), account))
|
||||
}
|
||||
|
||||
func (a *LdapLogin) getOrCreateUserWithLdap(userName string, password string, cols ...string) (*sysentity.Account, error) {
|
||||
func (a *LdapLogin) getOrCreateUserWithLdap(ctx context.Context, userName string, password string, cols ...string) (*sysentity.Account, error) {
|
||||
userInfo, err := Authenticate(userName, password)
|
||||
if err != nil {
|
||||
return nil, errors.New("用户名密码错误")
|
||||
return nil, errorx.NewBizI(ctx, imsg.ErrUsernameOrPwdErr)
|
||||
}
|
||||
|
||||
account, err := a.getUser(userName, cols...)
|
||||
@@ -121,7 +122,7 @@ type UserInfo struct {
|
||||
func Authenticate(username, password string) (*UserInfo, error) {
|
||||
ldapConf := config.GetLdapLogin()
|
||||
if !ldapConf.Enable {
|
||||
return nil, errors.Errorf("未启用 LDAP 登录")
|
||||
return nil, errors.Errorf("LDAP login is not enabled")
|
||||
}
|
||||
conn, err := Connect(ldapConf)
|
||||
if err != nil {
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"mayfly-go/internal/auth/application"
|
||||
"mayfly-go/internal/auth/config"
|
||||
"mayfly-go/internal/auth/domain/entity"
|
||||
"mayfly-go/internal/auth/imsg"
|
||||
msgapp "mayfly-go/internal/msg/application"
|
||||
sysapp "mayfly-go/internal/sys/application"
|
||||
sysentity "mayfly-go/internal/sys/domain/entity"
|
||||
@@ -52,42 +53,42 @@ func (a *Oauth2Login) OAuth2Callback(rc *req.Ctx) {
|
||||
client, oauth := a.getOAuthClient()
|
||||
|
||||
code := rc.Query("code")
|
||||
biz.NotEmpty(code, "code不能为空")
|
||||
biz.NotEmpty(code, "code cannot be empty")
|
||||
|
||||
state := rc.Query("state")
|
||||
biz.NotEmpty(state, "state不能为空")
|
||||
biz.NotEmpty(state, "state canot be empty")
|
||||
|
||||
stateAction := cache.GetStr("oauth2:state:" + state)
|
||||
biz.NotEmpty(stateAction, "state已过期, 请重新登录")
|
||||
|
||||
token, err := client.Exchange(rc, code)
|
||||
biz.ErrIsNilAppendErr(err, "获取OAuth2 accessToken失败: %s")
|
||||
biz.ErrIsNilAppendErr(err, "get OAuth2 accessToken fail: %s")
|
||||
|
||||
// 获取用户信息
|
||||
httpCli := client.Client(rc.GetRequest().Context(), token)
|
||||
resp, err := httpCli.Get(oauth.ResourceURL)
|
||||
biz.ErrIsNilAppendErr(err, "获取用户信息失败: %s")
|
||||
biz.ErrIsNilAppendErr(err, "get user info error: %s")
|
||||
defer resp.Body.Close()
|
||||
|
||||
b, err := io.ReadAll(resp.Body)
|
||||
biz.ErrIsNilAppendErr(err, "读取响应的用户信息失败: %s")
|
||||
biz.ErrIsNilAppendErr(err, "failed to read the response user information: %s")
|
||||
|
||||
// UserIdentifier格式为 type:fieldPath。如:string:user.username 或 number:user.id
|
||||
userIdTypeAndFieldPath := strings.Split(oauth.UserIdentifier, ":")
|
||||
biz.IsTrue(len(userIdTypeAndFieldPath) == 2, "oauth2配置属性'UserIdentifier'不符合规则")
|
||||
biz.IsTrue(len(userIdTypeAndFieldPath) == 2, "oauth2 configuration property 'UserIdentifier' is not compliant")
|
||||
|
||||
// 解析用户唯一标识
|
||||
userIdFieldPath := userIdTypeAndFieldPath[1]
|
||||
userId := ""
|
||||
if userIdTypeAndFieldPath[0] == "string" {
|
||||
userId, err = jsonx.GetStringByBytes(b, userIdFieldPath)
|
||||
biz.ErrIsNilAppendErr(err, "解析用户唯一标识失败: %s")
|
||||
biz.ErrIsNilAppendErr(err, "failed to resolve the user unique identity: %s")
|
||||
} else {
|
||||
intUserId, err := jsonx.GetIntByBytes(b, userIdFieldPath)
|
||||
biz.ErrIsNilAppendErr(err, "解析用户唯一标识失败: %s")
|
||||
biz.ErrIsNilAppendErr(err, "failed to resolve the user unique identity: %s")
|
||||
userId = fmt.Sprintf("%d", intUserId)
|
||||
}
|
||||
biz.NotBlank(userId, "用户唯一标识字段值不能为空")
|
||||
biz.NotBlank(userId, "the user unique identification field value cannot be null")
|
||||
|
||||
// 判断是登录还是绑定
|
||||
if stateAction == "login" {
|
||||
@@ -95,23 +96,23 @@ func (a *Oauth2Login) OAuth2Callback(rc *req.Ctx) {
|
||||
} else if sAccountId, ok := strings.CutPrefix(stateAction, "bind:"); ok {
|
||||
// 绑定
|
||||
accountId, err := strconv.ParseUint(sAccountId, 10, 64)
|
||||
biz.ErrIsNilAppendErr(err, "绑定用户失败: %s")
|
||||
biz.ErrIsNilAppendErr(err, "failed to bind user: %s")
|
||||
|
||||
account := new(sysentity.Account)
|
||||
account.Id = accountId
|
||||
err = a.AccountApp.GetByCond(model.NewModelCond(account).Columns("username"))
|
||||
biz.ErrIsNilAppendErr(err, "该账号不存在")
|
||||
biz.ErrIsNilAppendErr(err, "this account does not exist")
|
||||
rc.ReqParam = collx.Kvs("username", account.Username, "type", "bind")
|
||||
|
||||
err = a.Oauth2App.GetOAuthAccount(&entity.Oauth2Account{
|
||||
AccountId: accountId,
|
||||
}, "account_id", "identity")
|
||||
biz.IsTrue(err != nil, "该账号已被其他用户绑定")
|
||||
biz.IsTrue(err != nil, "the account has been linked by another user")
|
||||
|
||||
err = a.Oauth2App.GetOAuthAccount(&entity.Oauth2Account{
|
||||
Identity: userId,
|
||||
}, "account_id", "identity")
|
||||
biz.IsTrue(err != nil, "您已绑定其他账号")
|
||||
biz.IsTrue(err != nil, "you are bound to another account")
|
||||
|
||||
now := time.Now()
|
||||
err = a.Oauth2App.BindOAuthAccount(&entity.Oauth2Account{
|
||||
@@ -120,14 +121,14 @@ func (a *Oauth2Login) OAuth2Callback(rc *req.Ctx) {
|
||||
CreateTime: &now,
|
||||
UpdateTime: &now,
|
||||
})
|
||||
biz.ErrIsNilAppendErr(err, "绑定用户失败: %s")
|
||||
biz.ErrIsNilAppendErr(err, "failed to bind user: %s")
|
||||
res := collx.M{
|
||||
"action": "oauthBind",
|
||||
"bind": true,
|
||||
}
|
||||
rc.ResData = res
|
||||
} else {
|
||||
panic(errorx.NewBiz("state不合法"))
|
||||
panic(errorx.NewBiz("state is invalid"))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,12 +137,12 @@ func (a *Oauth2Login) doLoginAction(rc *req.Ctx, userId string, oauth *config.Oa
|
||||
// 查询用户是否存在
|
||||
oauthAccount := &entity.Oauth2Account{Identity: userId}
|
||||
err := a.Oauth2App.GetOAuthAccount(oauthAccount, "account_id", "identity")
|
||||
|
||||
ctx := rc.MetaCtx
|
||||
var accountId uint64
|
||||
isFirst := false
|
||||
// 不存在,进行注册
|
||||
if err != nil {
|
||||
biz.IsTrue(oauth.AutoRegister, "系统未开启自动注册, 请先让管理员添加对应账号")
|
||||
biz.IsTrueI(ctx, oauth.AutoRegister, imsg.ErrOauth2NoAutoRegister)
|
||||
now := time.Now()
|
||||
account := &sysentity.Account{
|
||||
Model: model.Model{
|
||||
@@ -163,7 +164,7 @@ func (a *Oauth2Login) doLoginAction(rc *req.Ctx, userId string, oauth *config.Oa
|
||||
CreateTime: &now,
|
||||
UpdateTime: &now,
|
||||
})
|
||||
biz.ErrIsNilAppendErr(err, "绑定用户失败: %s")
|
||||
biz.ErrIsNilAppendErr(err, "failed to bind user: %s")
|
||||
accountId = account.Id
|
||||
isFirst = true
|
||||
} else {
|
||||
@@ -172,12 +173,12 @@ func (a *Oauth2Login) doLoginAction(rc *req.Ctx, userId string, oauth *config.Oa
|
||||
|
||||
// 进行登录
|
||||
account, err := a.AccountApp.GetById(accountId, "Id", "Name", "Username", "Password", "Status", "LastLoginTime", "LastLoginIp", "OtpSecret")
|
||||
biz.ErrIsNilAppendErr(err, "获取用户信息失败: %s")
|
||||
biz.ErrIsNilAppendErr(err, "get user info error: %s")
|
||||
|
||||
clientIp := getIpAndRegion(rc)
|
||||
rc.ReqParam = collx.Kvs("username", account.Username, "ip", clientIp, "type", "login")
|
||||
|
||||
res := LastLoginCheck(account, config.GetAccountLoginSecurity(), clientIp)
|
||||
res := LastLoginCheck(ctx, account, config.GetAccountLoginSecurity(), clientIp)
|
||||
res["action"] = "oauthLogin"
|
||||
res["isFirstOauth2Login"] = isFirst
|
||||
rc.ResData = res
|
||||
@@ -185,8 +186,8 @@ func (a *Oauth2Login) doLoginAction(rc *req.Ctx, userId string, oauth *config.Oa
|
||||
|
||||
func (a *Oauth2Login) getOAuthClient() (*oauth2.Config, *config.Oauth2Login) {
|
||||
oath2LoginConfig := config.GetOauth2Login()
|
||||
biz.IsTrue(oath2LoginConfig.Enable, "请先配置oauth2或启用oauth2登录")
|
||||
biz.IsTrue(oath2LoginConfig.ClientId != "", "oauth2 clientId不能为空")
|
||||
biz.IsTrue(oath2LoginConfig.Enable, "please configure oauth2 or enable oauth2 login first")
|
||||
biz.IsTrue(oath2LoginConfig.ClientId != "", "oauth2 clientId cannot be empty")
|
||||
|
||||
client := &oauth2.Config{
|
||||
ClientID: oath2LoginConfig.ClientId,
|
||||
|
||||
21
server/internal/auth/imsg/en.go
Normal file
21
server/internal/auth/imsg/en.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package imsg
|
||||
|
||||
import "mayfly-go/pkg/i18n"
|
||||
|
||||
var En = map[i18n.MsgId]string{
|
||||
LogAccountLogin: "Account Login",
|
||||
LogOauth2Callback: "Oauth2 Callback",
|
||||
LogOauth2Unbind: "Oauth2 Unbind",
|
||||
LogLdapLogin: "LDAP Login",
|
||||
|
||||
ErrCaptchaErr: "Captcha error",
|
||||
ErrLoginRestrict: "login failed more than {{.failCount}} times, try again in {{.min}} minutes",
|
||||
ErrLoginFail: "Wrong username or password [Login failed {{.failCount}} times]",
|
||||
ErrOtpTokenInvalid: "otpToken error or invalid, please login again to obtain",
|
||||
ErrOtpCheckRestrict: "Two-factor validation failed more than 5 times. Try again in 10 minutes",
|
||||
ErrOtpCheckFail: "Two-factor authentication authorization code is incorrect",
|
||||
ErrAccountNotAvailable: "Account is not available",
|
||||
LoginMsg: "Log in to [{{.ip}}]-[{{.time}}]",
|
||||
ErrUsernameOrPwdErr: "Wrong username or password",
|
||||
ErrOauth2NoAutoRegister: "the system does not enable automatic registration, please ask the administrator to add the corresponding account first",
|
||||
}
|
||||
29
server/internal/auth/imsg/imsg.go
Normal file
29
server/internal/auth/imsg/imsg.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package imsg
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/common/consts"
|
||||
"mayfly-go/pkg/i18n"
|
||||
)
|
||||
|
||||
func init() {
|
||||
i18n.AppendLangMsg(i18n.Zh_CN, Zh_CN)
|
||||
i18n.AppendLangMsg(i18n.En, En)
|
||||
}
|
||||
|
||||
const (
|
||||
LogAccountLogin = iota + consts.ImsgNumAuth
|
||||
LogOauth2Callback
|
||||
LogOauth2Unbind
|
||||
LogLdapLogin
|
||||
|
||||
ErrCaptchaErr
|
||||
ErrLoginRestrict
|
||||
ErrLoginFail
|
||||
ErrOtpTokenInvalid
|
||||
ErrOtpCheckRestrict
|
||||
ErrOtpCheckFail
|
||||
ErrAccountNotAvailable
|
||||
LoginMsg
|
||||
ErrUsernameOrPwdErr
|
||||
ErrOauth2NoAutoRegister
|
||||
)
|
||||
21
server/internal/auth/imsg/zh_cn.go
Normal file
21
server/internal/auth/imsg/zh_cn.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package imsg
|
||||
|
||||
import "mayfly-go/pkg/i18n"
|
||||
|
||||
var Zh_CN = map[i18n.MsgId]string{
|
||||
LogAccountLogin: "账号登录",
|
||||
LogOauth2Callback: "Oauth2回调",
|
||||
LogOauth2Unbind: "Oauth2解绑",
|
||||
LogLdapLogin: "LDAP登录",
|
||||
|
||||
ErrCaptchaErr: "验证码错误",
|
||||
ErrLoginRestrict: "登录失败超过{{.failCount}}次, 请{{.min}}分钟后再试",
|
||||
ErrLoginFail: "用户名或密码错误【当前登录失败{{.failCount}}次】",
|
||||
ErrOtpTokenInvalid: "otpToken错误或失效, 请重新登陆获取",
|
||||
ErrOtpCheckRestrict: "双因素校验失败超过5次, 请10分钟后再试",
|
||||
ErrOtpCheckFail: "双因素认证授权码不正确",
|
||||
ErrAccountNotAvailable: "账号不可用",
|
||||
LoginMsg: "于[{{.ip}}]-[{{.time}}]登录",
|
||||
ErrUsernameOrPwdErr: "用户名或密码错误",
|
||||
ErrOauth2NoAutoRegister: "系统未开启自动注册, 请先让管理员添加对应账号",
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package router
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/auth/api"
|
||||
"mayfly-go/internal/auth/imsg"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/ioc"
|
||||
"mayfly-go/pkg/req"
|
||||
@@ -24,7 +25,7 @@ func Init(router *gin.RouterGroup) {
|
||||
reqs := [...]*req.Conf{
|
||||
|
||||
// 用户账号密码登录
|
||||
req.NewPost("/accounts/login", accountLogin.Login).Log(req.NewLogSave("用户登录")).DontNeedToken(),
|
||||
req.NewPost("/accounts/login", accountLogin.Login).Log(req.NewLogSaveI(imsg.LogAccountLogin)).DontNeedToken(),
|
||||
|
||||
req.NewGet("/accounts/refreshToken", accountLogin.RefreshToken).DontNeedToken(),
|
||||
|
||||
@@ -44,15 +45,15 @@ func Init(router *gin.RouterGroup) {
|
||||
req.NewGet("/oauth2/bind", oauth2Login.OAuth2Bind),
|
||||
|
||||
// oauth2回调地址
|
||||
req.NewGet("/oauth2/callback", oauth2Login.OAuth2Callback).Log(req.NewLogSave("oauth2回调")).DontNeedToken(),
|
||||
req.NewGet("/oauth2/callback", oauth2Login.OAuth2Callback).Log(req.NewLogSaveI(imsg.LogOauth2Callback)).DontNeedToken(),
|
||||
|
||||
req.NewGet("/oauth2/status", oauth2Login.Oauth2Status),
|
||||
|
||||
req.NewGet("/oauth2/unbind", oauth2Login.Oauth2Unbind).Log(req.NewLogSave("oauth2解绑")),
|
||||
req.NewGet("/oauth2/unbind", oauth2Login.Oauth2Unbind).Log(req.NewLogSaveI(imsg.LogOauth2Unbind)),
|
||||
|
||||
// LDAP 登录
|
||||
req.NewGet("/ldap/enabled", ldapLogin.GetLdapEnabled).DontNeedToken(),
|
||||
req.NewPost("/ldap/login", ldapLogin.Login).Log(req.NewLogSave("LDAP 登录")).DontNeedToken(),
|
||||
req.NewPost("/ldap/login", ldapLogin.Login).Log(req.NewLogSaveI(imsg.LogLdapLogin)).DontNeedToken(),
|
||||
}
|
||||
|
||||
req.BatchSetGroup(rg, reqs[:])
|
||||
|
||||
@@ -20,4 +20,14 @@ const (
|
||||
ResourceTypeDb int8 = 2
|
||||
ResourceTypeRedis int8 = 3
|
||||
ResourceTypeMongo int8 = 4
|
||||
|
||||
// imsg起始编号
|
||||
ImsgNumSys = 10000
|
||||
ImsgNumAuth = 20000
|
||||
ImsgNumTag = 30000
|
||||
ImsgNumFlow = 40000
|
||||
ImsgNumMachine = 50000
|
||||
ImsgNumDb = 60000
|
||||
ImsgNumRedis = 70000
|
||||
ImsgNumMongo = 80000
|
||||
)
|
||||
|
||||
@@ -10,19 +10,16 @@ func CheckAccountPasswordLever(ps string) bool {
|
||||
if len(ps) < 8 {
|
||||
return false
|
||||
}
|
||||
num := `[0-9]{1}`
|
||||
a_z := `[a-zA-Z]{1}`
|
||||
symbol := `[!@#~$%^&*()+|_.,]{1}`
|
||||
if b, err := regexp.MatchString(num, ps); !b || err != nil {
|
||||
return false
|
||||
}
|
||||
if b, err := regexp.MatchString(a_z, ps); !b || err != nil {
|
||||
return false
|
||||
}
|
||||
if b, err := regexp.MatchString(symbol, ps); !b || err != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
// 包含大写字母
|
||||
charRegex := regexp.MustCompile(`[a-zA-Z]`)
|
||||
// 包含数字
|
||||
digitRegex := regexp.MustCompile(`[0-9]`)
|
||||
// 包含特殊符号
|
||||
specialCharRegex := regexp.MustCompile(`[!@#$%^&*(),.?":{}|<>]`)
|
||||
|
||||
return charRegex.MatchString(ps) &&
|
||||
digitRegex.MatchString(ps) &&
|
||||
specialCharRegex.MatchString(ps)
|
||||
}
|
||||
|
||||
// 使用config.yml的aes.key进行密码加密
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"mayfly-go/internal/db/dbm/dbi"
|
||||
"mayfly-go/internal/db/dbm/sqlparser"
|
||||
"mayfly-go/internal/db/domain/entity"
|
||||
"mayfly-go/internal/db/imsg"
|
||||
"mayfly-go/internal/event"
|
||||
msgapp "mayfly-go/internal/msg/application"
|
||||
msgdto "mayfly-go/internal/msg/application/dto"
|
||||
@@ -18,6 +19,7 @@ import (
|
||||
tagentity "mayfly-go/internal/tag/domain/entity"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/global"
|
||||
"mayfly-go/pkg/i18n"
|
||||
"mayfly-go/pkg/logx"
|
||||
"mayfly-go/pkg/model"
|
||||
"mayfly-go/pkg/req"
|
||||
@@ -92,9 +94,7 @@ func (d *Db) DeleteDb(rc *req.Ctx) {
|
||||
|
||||
ctx := rc.MetaCtx
|
||||
for _, v := range ids {
|
||||
dbId := cast.ToUint64(v)
|
||||
biz.NotBlank(dbId, "存在错误dbId")
|
||||
biz.ErrIsNil(d.DbApp.Delete(ctx, dbId))
|
||||
biz.ErrIsNil(d.DbApp.Delete(ctx, cast.ToUint64(v)))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,10 +110,10 @@ func (d *Db) ExecSql(rc *req.Ctx) {
|
||||
|
||||
global.EventBus.Publish(rc.MetaCtx, event.EventTopicResourceOp, dbConn.Info.CodePath[0])
|
||||
sqlStr, err := cryptox.AesDecryptByLa(form.Sql, rc.GetLoginAccount())
|
||||
biz.ErrIsNilAppendErr(err, "sql解码失败: %s")
|
||||
biz.ErrIsNilAppendErr(err, "sql decoding failure: %s")
|
||||
|
||||
rc.ReqParam = fmt.Sprintf("%s %s\n-> %s", dbConn.Info.GetLogDesc(), form.ExecId, sqlStr)
|
||||
biz.NotEmpty(form.Sql, "sql不能为空")
|
||||
biz.NotEmpty(form.Sql, "sql cannot be empty")
|
||||
|
||||
execReq := &application.DbSqlExecReq{
|
||||
DbId: dbId,
|
||||
@@ -146,9 +146,9 @@ type progressMsg struct {
|
||||
// 执行sql文件
|
||||
func (d *Db) ExecSqlFile(rc *req.Ctx) {
|
||||
multipart, err := rc.GetRequest().MultipartReader()
|
||||
biz.ErrIsNilAppendErr(err, "读取sql文件失败: %s")
|
||||
biz.ErrIsNilAppendErr(err, "failed to read sql file: %s")
|
||||
file, err := multipart.NextPart()
|
||||
biz.ErrIsNilAppendErr(err, "读取sql文件失败: %s")
|
||||
biz.ErrIsNilAppendErr(err, "failed to read sql file: %s")
|
||||
defer file.Close()
|
||||
filename := file.FileName()
|
||||
dbId := getDbId(rc)
|
||||
@@ -166,14 +166,14 @@ func (d *Db) ExecSqlFile(rc *req.Ctx) {
|
||||
if len(errInfo) > 300 {
|
||||
errInfo = errInfo[:300] + "..."
|
||||
}
|
||||
d.MsgApp.CreateAndSend(rc.GetLoginAccount(), msgdto.ErrSysMsg("sql脚本执行失败", fmt.Sprintf("[%s][%s]执行失败: [%s]", filename, dbConn.Info.GetLogDesc(), errInfo)).WithClientId(clientId))
|
||||
d.MsgApp.CreateAndSend(rc.GetLoginAccount(), msgdto.ErrSysMsg(i18n.T(imsg.SqlScriptRunFail), fmt.Sprintf("[%s][%s] execution failure: [%s]", filename, dbConn.Info.GetLogDesc(), errInfo)).WithClientId(clientId))
|
||||
}
|
||||
}()
|
||||
|
||||
executedStatements := 0
|
||||
progressId := stringx.Rand(32)
|
||||
laId := rc.GetLoginAccount().Id
|
||||
defer ws.SendJsonMsg(ws.UserId(laId), clientId, msgdto.InfoSysMsg("sql脚本执行进度", &progressMsg{
|
||||
defer ws.SendJsonMsg(ws.UserId(laId), clientId, msgdto.InfoSysMsg(i18n.T(imsg.SqlScripRunProgress), &progressMsg{
|
||||
Id: progressId,
|
||||
Title: filename,
|
||||
ExecutedStatements: executedStatements,
|
||||
@@ -185,7 +185,7 @@ func (d *Db) ExecSqlFile(rc *req.Ctx) {
|
||||
err = sqlparser.SQLSplit(file, func(sql string) error {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
ws.SendJsonMsg(ws.UserId(laId), clientId, msgdto.InfoSysMsg("sql脚本执行进度", &progressMsg{
|
||||
ws.SendJsonMsg(ws.UserId(laId), clientId, msgdto.InfoSysMsg(i18n.T(imsg.SqlScripRunProgress), &progressMsg{
|
||||
Id: progressId,
|
||||
Title: filename,
|
||||
ExecutedStatements: executedStatements,
|
||||
@@ -200,7 +200,7 @@ func (d *Db) ExecSqlFile(rc *req.Ctx) {
|
||||
})
|
||||
|
||||
biz.ErrIsNilAppendErr(err, "%s")
|
||||
d.MsgApp.CreateAndSend(rc.GetLoginAccount(), msgdto.SuccessSysMsg("sql脚本执行成功", fmt.Sprintf("sql脚本执行完成:%s", rc.ReqParam)).WithClientId(clientId))
|
||||
d.MsgApp.CreateAndSend(rc.GetLoginAccount(), msgdto.SuccessSysMsg(i18n.T(imsg.SqlScriptRunSuccess), fmt.Sprintf("sql脚本执行完成:%s", rc.ReqParam)).WithClientId(clientId))
|
||||
}
|
||||
|
||||
// 数据库dump
|
||||
@@ -243,9 +243,9 @@ func (d *Db) DumpSql(rc *req.Ctx) {
|
||||
defer func() {
|
||||
msg := anyx.ToString(recover())
|
||||
if len(msg) > 0 {
|
||||
msg = "数据库导出失败: " + msg
|
||||
msg = "DB dump error: " + msg
|
||||
rc.GetWriter().Write([]byte(msg))
|
||||
d.MsgApp.CreateAndSend(la, msgdto.ErrSysMsg("数据库导出失败", msg))
|
||||
d.MsgApp.CreateAndSend(la, msgdto.ErrSysMsg(i18n.T(imsg.DbDumpErr), msg))
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -263,26 +263,26 @@ func (d *Db) DumpSql(rc *req.Ctx) {
|
||||
|
||||
func (d *Db) TableInfos(rc *req.Ctx) {
|
||||
res, err := d.getDbConn(rc).GetMetadata().GetTables()
|
||||
biz.ErrIsNilAppendErr(err, "获取表信息失败: %s")
|
||||
biz.ErrIsNilAppendErr(err, "get table error: %s")
|
||||
rc.ResData = res
|
||||
}
|
||||
|
||||
func (d *Db) TableIndex(rc *req.Ctx) {
|
||||
tn := rc.Query("tableName")
|
||||
biz.NotEmpty(tn, "tableName不能为空")
|
||||
biz.NotEmpty(tn, "tableName cannot be empty")
|
||||
res, err := d.getDbConn(rc).GetMetadata().GetTableIndex(tn)
|
||||
biz.ErrIsNilAppendErr(err, "获取表索引信息失败: %s")
|
||||
biz.ErrIsNilAppendErr(err, "get table index error: %s")
|
||||
rc.ResData = res
|
||||
}
|
||||
|
||||
// @router /api/db/:dbId/c-metadata [get]
|
||||
func (d *Db) ColumnMA(rc *req.Ctx) {
|
||||
tn := rc.Query("tableName")
|
||||
biz.NotEmpty(tn, "tableName不能为空")
|
||||
biz.NotEmpty(tn, "tableName cannot be empty")
|
||||
|
||||
dbi := d.getDbConn(rc)
|
||||
res, err := dbi.GetMetadata().GetColumns(tn)
|
||||
biz.ErrIsNilAppendErr(err, "获取数据库列信息失败: %s")
|
||||
biz.ErrIsNilAppendErr(err, "get column metadata error: %s")
|
||||
rc.ResData = res
|
||||
}
|
||||
|
||||
@@ -330,9 +330,9 @@ func (d *Db) HintTables(rc *req.Ctx) {
|
||||
|
||||
func (d *Db) GetTableDDL(rc *req.Ctx) {
|
||||
tn := rc.Query("tableName")
|
||||
biz.NotEmpty(tn, "tableName不能为空")
|
||||
biz.NotEmpty(tn, "tableName cannot be empty")
|
||||
res, err := d.getDbConn(rc).GetMetadata().GetTableDDL(tn, false)
|
||||
biz.ErrIsNilAppendErr(err, "获取表ddl失败: %s")
|
||||
biz.ErrIsNilAppendErr(err, "get table DDL error: %s")
|
||||
rc.ResData = res
|
||||
}
|
||||
|
||||
@@ -343,7 +343,7 @@ func (d *Db) GetVersion(rc *req.Ctx) {
|
||||
|
||||
func (d *Db) GetSchemas(rc *req.Ctx) {
|
||||
res, err := d.getDbConn(rc).GetMetadata().GetSchemas()
|
||||
biz.ErrIsNilAppendErr(err, "获取schemas失败: %s")
|
||||
biz.ErrIsNilAppendErr(err, "get schemas error: %s")
|
||||
rc.ResData = res
|
||||
}
|
||||
|
||||
@@ -352,24 +352,24 @@ func (d *Db) CopyTable(rc *req.Ctx) {
|
||||
copy := req.BindJsonAndCopyTo[*dbi.DbCopyTable](rc, form, new(dbi.DbCopyTable))
|
||||
|
||||
conn, err := d.DbApp.GetDbConn(form.Id, form.Db)
|
||||
biz.ErrIsNilAppendErr(err, "拷贝表失败: %s")
|
||||
biz.ErrIsNilAppendErr(err, "copy table error: %s")
|
||||
|
||||
err = conn.GetDialect().CopyTable(copy)
|
||||
if err != nil {
|
||||
logx.Errorf("拷贝表失败: %s", err.Error())
|
||||
logx.Errorf("copy table error: %s", err.Error())
|
||||
}
|
||||
biz.ErrIsNilAppendErr(err, "拷贝表失败: %s")
|
||||
biz.ErrIsNilAppendErr(err, "copy table error: %s")
|
||||
}
|
||||
|
||||
func getDbId(rc *req.Ctx) uint64 {
|
||||
dbId := rc.PathParamInt("dbId")
|
||||
biz.IsTrue(dbId > 0, "dbId错误")
|
||||
biz.IsTrue(dbId > 0, "dbId error")
|
||||
return uint64(dbId)
|
||||
}
|
||||
|
||||
func getDbName(rc *req.Ctx) string {
|
||||
db := rc.Query("db")
|
||||
biz.NotEmpty(db, "db不能为空")
|
||||
biz.NotEmpty(db, "db cannot be empty")
|
||||
return db
|
||||
}
|
||||
|
||||
|
||||
@@ -9,8 +9,9 @@ import (
|
||||
"mayfly-go/pkg/req"
|
||||
"mayfly-go/pkg/utils/cryptox"
|
||||
"mayfly-go/pkg/utils/stringx"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/may-fly/cast"
|
||||
)
|
||||
|
||||
type DataSyncTask struct {
|
||||
@@ -37,7 +38,7 @@ func (d *DataSyncTask) SaveTask(rc *req.Ctx) {
|
||||
|
||||
// 解码base64 sql
|
||||
sqlStr, err := cryptox.AesDecryptByLa(task.DataSql, rc.GetLoginAccount())
|
||||
biz.ErrIsNilAppendErr(err, "sql解码失败: %s")
|
||||
biz.ErrIsNilAppendErr(err, "sql decoding failure: %s")
|
||||
sql := stringx.TrimSpaceAndBr(sqlStr)
|
||||
task.DataSql = sql
|
||||
form.DataSql = sql
|
||||
@@ -52,9 +53,7 @@ func (d *DataSyncTask) DeleteTask(rc *req.Ctx) {
|
||||
ids := strings.Split(taskId, ",")
|
||||
|
||||
for _, v := range ids {
|
||||
value, err := strconv.Atoi(v)
|
||||
biz.ErrIsNilAppendErr(err, "string类型转换为int异常: %s")
|
||||
biz.ErrIsNil(d.DataSyncTaskApp.Delete(rc.MetaCtx, uint64(value)))
|
||||
biz.ErrIsNil(d.DataSyncTaskApp.Delete(rc.MetaCtx, cast.ToUint64(v)))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,7 +64,7 @@ func (d *DataSyncTask) ChangeStatus(rc *req.Ctx) {
|
||||
|
||||
if task.Status == entity.DataSyncTaskStatusEnable {
|
||||
task, err := d.DataSyncTaskApp.GetById(task.Id)
|
||||
biz.ErrIsNil(err, "该任务不存在")
|
||||
biz.ErrIsNil(err, "task not found")
|
||||
d.DataSyncTaskApp.AddCronJob(rc.MetaCtx, task)
|
||||
} else {
|
||||
d.DataSyncTaskApp.RemoveCronJobById(task.Id)
|
||||
@@ -98,6 +97,6 @@ func (d *DataSyncTask) GetTask(rc *req.Ctx) {
|
||||
|
||||
func (d *DataSyncTask) getTaskId(rc *req.Ctx) uint64 {
|
||||
instanceId := rc.PathParamInt("taskId")
|
||||
biz.IsTrue(instanceId > 0, "instanceId 错误")
|
||||
biz.IsTrue(instanceId > 0, "instanceId error")
|
||||
return uint64(instanceId)
|
||||
}
|
||||
|
||||
@@ -14,8 +14,9 @@ import (
|
||||
"mayfly-go/pkg/model"
|
||||
"mayfly-go/pkg/req"
|
||||
"mayfly-go/pkg/utils/collx"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/may-fly/cast"
|
||||
)
|
||||
|
||||
type Instance struct {
|
||||
@@ -85,7 +86,7 @@ func (d *Instance) SaveInstance(rc *req.Ctx) {
|
||||
func (d *Instance) GetInstance(rc *req.Ctx) {
|
||||
dbId := getInstanceId(rc)
|
||||
dbEntity, err := d.InstanceApp.GetById(dbId)
|
||||
biz.ErrIsNil(err, "获取数据库实例错误")
|
||||
biz.ErrIsNilAppendErr(err, "get db instance failed: %s")
|
||||
rc.ResData = dbEntity
|
||||
}
|
||||
|
||||
@@ -97,11 +98,7 @@ func (d *Instance) DeleteInstance(rc *req.Ctx) {
|
||||
ids := strings.Split(idsStr, ",")
|
||||
|
||||
for _, v := range ids {
|
||||
value, err := strconv.Atoi(v)
|
||||
biz.ErrIsNilAppendErr(err, "删除数据库实例失败: %s")
|
||||
instanceId := uint64(value)
|
||||
err = d.InstanceApp.Delete(rc.MetaCtx, instanceId)
|
||||
biz.ErrIsNilAppendErr(err, "删除数据库实例失败: %s")
|
||||
biz.ErrIsNilAppendErr(d.InstanceApp.Delete(rc.MetaCtx, cast.ToUint64(v)), "delete db instance failed: %s")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,6 +129,6 @@ func (d *Instance) GetDbServer(rc *req.Ctx) {
|
||||
|
||||
func getInstanceId(rc *req.Ctx) uint64 {
|
||||
instanceId := rc.PathParamInt("instanceId")
|
||||
biz.IsTrue(instanceId > 0, "instanceId 错误")
|
||||
biz.IsTrue(instanceId > 0, "instanceId error")
|
||||
return uint64(instanceId)
|
||||
}
|
||||
|
||||
@@ -9,17 +9,19 @@ import (
|
||||
"mayfly-go/internal/db/dbm/dbi"
|
||||
"mayfly-go/internal/db/dbm/sqlparser"
|
||||
"mayfly-go/internal/db/domain/entity"
|
||||
"mayfly-go/internal/db/imsg"
|
||||
fileapp "mayfly-go/internal/file/application"
|
||||
msgapp "mayfly-go/internal/msg/application"
|
||||
msgdto "mayfly-go/internal/msg/application/dto"
|
||||
tagapp "mayfly-go/internal/tag/application"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/i18n"
|
||||
"mayfly-go/pkg/model"
|
||||
"mayfly-go/pkg/req"
|
||||
"mayfly-go/pkg/utils/anyx"
|
||||
"mayfly-go/pkg/utils/collx"
|
||||
"mayfly-go/pkg/utils/stringx"
|
||||
"mayfly-go/pkg/ws"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -67,12 +69,9 @@ func (d *DbTransferTask) DeleteTask(rc *req.Ctx) {
|
||||
rc.ReqParam = taskId
|
||||
ids := strings.Split(taskId, ",")
|
||||
|
||||
uids := make([]uint64, len(ids))
|
||||
for _, v := range ids {
|
||||
value, err := strconv.Atoi(v)
|
||||
biz.ErrIsNilAppendErr(err, "string类型转换为int异常: %s")
|
||||
uids = append(uids, uint64(value))
|
||||
}
|
||||
uids := collx.ArrayMap[string, uint64](ids, func(val string) uint64 {
|
||||
return cast.ToUint64(val)
|
||||
})
|
||||
|
||||
biz.ErrIsNil(d.DbTransferTask.DeleteById(rc.MetaCtx, uids...))
|
||||
}
|
||||
@@ -83,7 +82,7 @@ func (d *DbTransferTask) ChangeStatus(rc *req.Ctx) {
|
||||
_ = d.DbTransferTask.UpdateById(rc.MetaCtx, task)
|
||||
|
||||
task, err := d.DbTransferTask.GetById(task.Id)
|
||||
biz.ErrIsNil(err, "该任务不存在")
|
||||
biz.ErrIsNil(err, "task not found")
|
||||
d.DbTransferTask.AddCronJob(rc.MetaCtx, task)
|
||||
|
||||
// 记录请求日志
|
||||
@@ -127,10 +126,10 @@ func (d *DbTransferTask) FileRun(rc *req.Ctx) {
|
||||
rc.ReqParam = fm
|
||||
|
||||
tFile, err := d.DbTransferFile.GetById(fm.Id)
|
||||
biz.IsTrue(tFile != nil && err == nil, "文件不存在")
|
||||
biz.IsTrue(tFile != nil && err == nil, "file not found")
|
||||
|
||||
targetDbConn, err := d.DbApp.GetDbConn(fm.TargetDbId, fm.TargetDbName)
|
||||
biz.ErrIsNilAppendErr(err, "连接目标数据库失败: %s")
|
||||
biz.ErrIsNilAppendErr(err, "failed to connect to the target database: %s")
|
||||
biz.ErrIsNilAppendErr(d.TagApp.CanAccess(rc.GetLoginAccount().Id, targetDbConn.Info.CodePath...), "%s")
|
||||
|
||||
defer func() {
|
||||
@@ -139,7 +138,7 @@ func (d *DbTransferTask) FileRun(rc *req.Ctx) {
|
||||
if len(errInfo) > 300 {
|
||||
errInfo = errInfo[:300] + "..."
|
||||
}
|
||||
d.MsgApp.CreateAndSend(rc.GetLoginAccount(), msgdto.ErrSysMsg("sql脚本执行失败", fmt.Sprintf("[%s][%s]执行失败: [%s]", tFile.FileKey, targetDbConn.Info.GetLogDesc(), errInfo)).WithClientId(fm.ClientId))
|
||||
d.MsgApp.CreateAndSend(rc.GetLoginAccount(), msgdto.ErrSysMsg(i18n.T(imsg.SqlScriptRunFail), fmt.Sprintf("[%s][%s] run failed: [%s]", tFile.FileKey, targetDbConn.Info.GetLogDesc(), errInfo)).WithClientId(fm.ClientId))
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -160,13 +159,13 @@ func (d *DbTransferTask) fileRun(la *model.LoginAccount, fm *form.DbTransferFile
|
||||
defer ticker.Stop()
|
||||
|
||||
if err != nil {
|
||||
biz.ErrIsNilAppendErr(err, "连接目标数据库失败: %s")
|
||||
biz.ErrIsNilAppendErr(err, "failed to connect to the target database: %s")
|
||||
}
|
||||
|
||||
err = sqlparser.SQLSplit(reader, func(sql string) error {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
ws.SendJsonMsg(ws.UserId(laId), fm.ClientId, msgdto.InfoSqlProgressMsg("sql脚本执行进度", &progressMsg{
|
||||
ws.SendJsonMsg(ws.UserId(laId), fm.ClientId, msgdto.InfoSqlProgressMsg(i18n.T(imsg.SqlScripRunProgress), &progressMsg{
|
||||
Id: progressId,
|
||||
Title: filename,
|
||||
ExecutedStatements: executedStatements,
|
||||
@@ -180,8 +179,8 @@ func (d *DbTransferTask) fileRun(la *model.LoginAccount, fm *form.DbTransferFile
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
biz.ErrIsNilAppendErr(err, "执行sql失败: %s")
|
||||
biz.ErrIsNilAppendErr(err, "sql execution failed: %s")
|
||||
}
|
||||
|
||||
d.MsgApp.CreateAndSend(la, msgdto.SuccessSysMsg("sql脚本执行成功", fmt.Sprintf("sql脚本执行完成:%s", filename)).WithClientId(fm.ClientId))
|
||||
d.MsgApp.CreateAndSend(la, msgdto.SuccessSysMsg(i18n.T(imsg.SqlScriptRunSuccess), fmt.Sprintf("sql execution successfully: %s", filename)).WithClientId(fm.ClientId))
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"mayfly-go/internal/db/dbm/dbi"
|
||||
"mayfly-go/internal/db/domain/entity"
|
||||
"mayfly-go/internal/db/domain/repository"
|
||||
"mayfly-go/internal/db/imsg"
|
||||
tagapp "mayfly-go/internal/tag/application"
|
||||
tagdto "mayfly-go/internal/tag/application/dto"
|
||||
tagentity "mayfly-go/internal/tag/domain/entity"
|
||||
@@ -76,13 +77,13 @@ func (d *dbAppImpl) SaveDb(ctx context.Context, dbEntity *entity.Db) error {
|
||||
|
||||
authCert, err := d.resourceAuthCertApp.GetAuthCert(dbEntity.AuthCertName)
|
||||
if err != nil {
|
||||
return errorx.NewBiz("授权凭证不存在")
|
||||
return errorx.NewBiz("ac not found")
|
||||
}
|
||||
|
||||
err = d.GetByCond(oldDb)
|
||||
if dbEntity.Id == 0 {
|
||||
if err == nil {
|
||||
return errorx.NewBiz("该实例下数据库名已存在")
|
||||
return errorx.NewBizI(ctx, imsg.ErrDbNameExist)
|
||||
}
|
||||
dbEntity.Code = stringx.Rand(10)
|
||||
|
||||
@@ -104,13 +105,13 @@ func (d *dbAppImpl) SaveDb(ctx context.Context, dbEntity *entity.Db) error {
|
||||
|
||||
// 如果存在该库,则校验修改的库是否为该库
|
||||
if err == nil && oldDb.Id != dbEntity.Id {
|
||||
return errorx.NewBiz("该实例下数据库名已存在")
|
||||
return errorx.NewBizI(ctx, imsg.ErrDbNameExist)
|
||||
}
|
||||
|
||||
dbId := dbEntity.Id
|
||||
old, err := d.GetById(dbId)
|
||||
if err != nil {
|
||||
return errorx.NewBiz("该数据库不存在")
|
||||
return errorx.NewBiz("db not found")
|
||||
}
|
||||
|
||||
oldDbs := strings.Split(old.Database, " ")
|
||||
@@ -149,7 +150,7 @@ func (d *dbAppImpl) SaveDb(ctx context.Context, dbEntity *entity.Db) error {
|
||||
func (d *dbAppImpl) Delete(ctx context.Context, id uint64) error {
|
||||
db, err := d.GetById(id)
|
||||
if err != nil {
|
||||
return errorx.NewBiz("该数据库不存在")
|
||||
return errorx.NewBiz("db not found")
|
||||
}
|
||||
dbs := strings.Split(db.Database, " ")
|
||||
for _, v := range dbs {
|
||||
@@ -178,12 +179,12 @@ func (d *dbAppImpl) GetDbConn(dbId uint64, dbName string) (*dbi.DbConn, error) {
|
||||
return dbm.GetDbConn(dbId, dbName, func() (*dbi.DbInfo, error) {
|
||||
db, err := d.GetById(dbId)
|
||||
if err != nil {
|
||||
return nil, errorx.NewBiz("数据库信息不存在")
|
||||
return nil, errorx.NewBiz("db not found")
|
||||
}
|
||||
|
||||
instance, err := d.dbInstanceApp.GetById(db.InstanceId)
|
||||
if err != nil {
|
||||
return nil, errorx.NewBiz("数据库实例不存在")
|
||||
return nil, errorx.NewBiz("db instance not found")
|
||||
}
|
||||
|
||||
di, err := d.dbInstanceApp.ToDbInfo(instance, db.AuthCertName, dbName)
|
||||
@@ -195,7 +196,7 @@ func (d *dbAppImpl) GetDbConn(dbId uint64, dbName string) (*dbi.DbConn, error) {
|
||||
|
||||
checkDb := di.GetDatabase()
|
||||
if db.GetDatabaseMode == entity.DbGetDatabaseModeAssign && !strings.Contains(" "+db.Database+" ", " "+checkDb+" ") {
|
||||
return nil, errorx.NewBiz("未配置数据库【%s】的操作权限", dbName)
|
||||
return nil, errorx.NewBizI(context.Background(), imsg.ErrDbNotAccess, "dbName", dbName)
|
||||
}
|
||||
|
||||
return di, nil
|
||||
@@ -210,10 +211,10 @@ func (d *dbAppImpl) GetDbConnByInstanceId(instanceId uint64) (*dbi.DbConn, error
|
||||
|
||||
dbs, err := d.ListByCond(&entity.Db{InstanceId: instanceId}, "id", "database")
|
||||
if err != nil {
|
||||
return nil, errorx.NewBiz("获取数据库列表失败")
|
||||
return nil, errorx.NewBiz("failed to get database list")
|
||||
}
|
||||
if len(dbs) == 0 {
|
||||
return nil, errorx.NewBiz("实例[%d]未配置数据库, 请先进行配置", instanceId)
|
||||
return nil, errorx.NewBiz("DB instance [%d] Database is not configured, please configure it first", instanceId)
|
||||
}
|
||||
|
||||
// 使用该实例关联的已配置数据库中的第一个库进行连接并返回
|
||||
@@ -239,10 +240,10 @@ func (d *dbAppImpl) DumpDb(ctx context.Context, reqParam *dto.DumpDb) error {
|
||||
}
|
||||
|
||||
writer.WriteString("\n-- ----------------------------")
|
||||
writer.WriteString("\n-- 导出平台: mayfly-go")
|
||||
writer.WriteString(fmt.Sprintf("\n-- 导出时间: %s ", time.Now().Format("2006-01-02 15:04:05")))
|
||||
writer.WriteString(fmt.Sprintf("\n-- 导出数据库: %s ", dbName))
|
||||
writer.WriteString(fmt.Sprintf("\n-- 数据库方言: %s ", cmp.Or(reqParam.TargetDbType, dbConn.Info.Type)))
|
||||
writer.WriteString("\n-- Dump Platform: mayfly-go")
|
||||
writer.WriteString(fmt.Sprintf("\n-- Dump Time: %s ", time.Now().Format("2006-01-02 15:04:05")))
|
||||
writer.WriteString(fmt.Sprintf("\n-- Dump DB: %s ", dbName))
|
||||
writer.WriteString(fmt.Sprintf("\n-- DB Dialect: %s ", cmp.Or(reqParam.TargetDbType, dbConn.Info.Type)))
|
||||
writer.WriteString("\n-- ----------------------------\n\n")
|
||||
|
||||
// 获取目标元数据,仅生成sql,用于生成建表语句和插入数据,不能用于查询
|
||||
@@ -261,28 +262,28 @@ func (d *dbAppImpl) DumpDb(ctx context.Context, reqParam *dto.DumpDb) error {
|
||||
srcMeta := dbConn.GetMetadata()
|
||||
srcDialect := dbConn.GetDialect()
|
||||
if len(tables) == 0 {
|
||||
log("获取可导出的表信息...")
|
||||
log("Gets the table information that can be export...")
|
||||
ti, err := srcMeta.GetTables()
|
||||
if err != nil {
|
||||
log(fmt.Sprintf("获取表信息失败 %s", err.Error()))
|
||||
log(fmt.Sprintf("Failed to get table info %s", err.Error()))
|
||||
}
|
||||
biz.ErrIsNil(err)
|
||||
tables = make([]string, len(ti))
|
||||
for i, table := range ti {
|
||||
tables[i] = table.TableName
|
||||
}
|
||||
log(fmt.Sprintf("获取到%d张表", len(tables)))
|
||||
log(fmt.Sprintf("Get %d tables", len(tables)))
|
||||
}
|
||||
if len(tables) == 0 {
|
||||
log("不存在可导出的表, 结束导出")
|
||||
return errorx.NewBiz("不存在可导出的表")
|
||||
log("No table to export. End export")
|
||||
return errorx.NewBiz("there is no table to export")
|
||||
}
|
||||
|
||||
log("查询列信息...")
|
||||
log("Querying column information...")
|
||||
// 查询列信息,后面生成建表ddl和insert都需要列信息
|
||||
columns, err := srcMeta.GetColumns(tables...)
|
||||
if err != nil {
|
||||
log(fmt.Sprintf("查询列信息失败:%s", err.Error()))
|
||||
log(fmt.Sprintf("Failed to query column information: %s", err.Error()))
|
||||
}
|
||||
biz.ErrIsNil(err)
|
||||
|
||||
@@ -301,18 +302,18 @@ func (d *dbAppImpl) DumpDb(ctx context.Context, reqParam *dto.DumpDb) error {
|
||||
|
||||
// 遍历获取每个表的信息
|
||||
for _, tableName := range tables {
|
||||
log(fmt.Sprintf("获取表[%s]信息...", tableName))
|
||||
log(fmt.Sprintf("Get table [%s] information...", tableName))
|
||||
quoteTableName := targetDialect.QuoteIdentifier(tableName)
|
||||
|
||||
// 查询表信息,主要是为了查询表注释
|
||||
tbs, err := srcMeta.GetTables(tableName)
|
||||
if err != nil {
|
||||
log(fmt.Sprintf("获取表[%s]信息失败: %s", tableName, err.Error()))
|
||||
log(fmt.Sprintf("Failed to get table [%s] information: %s", tableName, err.Error()))
|
||||
return err
|
||||
}
|
||||
if len(tbs) <= 0 {
|
||||
log(fmt.Sprintf("获取表[%s]信息失败: 没有查询到表信息", tableName))
|
||||
return errorx.NewBiz(fmt.Sprintf("获取表信息失败:%s", tableName))
|
||||
log(fmt.Sprintf("Failed to get table [%s] information: No table information was retrieved", tableName))
|
||||
return errorx.NewBiz(fmt.Sprintf("Failed to get table information: %s", tableName))
|
||||
}
|
||||
tabInfo := dbi.Table{
|
||||
TableName: tableName,
|
||||
@@ -321,8 +322,8 @@ func (d *dbAppImpl) DumpDb(ctx context.Context, reqParam *dto.DumpDb) error {
|
||||
|
||||
// 生成表结构信息
|
||||
if reqParam.DumpDDL {
|
||||
log(fmt.Sprintf("生成表[%s]DDL...", tableName))
|
||||
writer.WriteString(fmt.Sprintf("\n-- ----------------------------\n-- 表结构: %s \n-- ----------------------------\n", tableName))
|
||||
log(fmt.Sprintf("Generate table [%s] DDL...", tableName))
|
||||
writer.WriteString(fmt.Sprintf("\n-- ----------------------------\n-- Table structure: %s \n-- ----------------------------\n", tableName))
|
||||
tbDdlArr := targetDialect.GenerateTableDDL(columnMap[tableName], tabInfo, true)
|
||||
for _, ddl := range tbDdlArr {
|
||||
writer.WriteString(ddl + ";\n")
|
||||
@@ -332,7 +333,7 @@ func (d *dbAppImpl) DumpDb(ctx context.Context, reqParam *dto.DumpDb) error {
|
||||
// 生成insert sql,数据在索引前,加速insert
|
||||
if reqParam.DumpData {
|
||||
log(fmt.Sprintf("生成表[%s]DML...", tableName))
|
||||
writer.WriteString(fmt.Sprintf("\n-- ----------------------------\n-- 表数据: %s \n-- ----------------------------\n", tableName))
|
||||
writer.WriteString(fmt.Sprintf("\n-- ----------------------------\n-- Data: %s \n-- ----------------------------\n", tableName))
|
||||
|
||||
dumpHelper.BeforeInsert(writer, quoteTableName)
|
||||
// 获取列信息
|
||||
@@ -356,17 +357,17 @@ func (d *dbAppImpl) DumpDb(ctx context.Context, reqParam *dto.DumpDb) error {
|
||||
dumpHelper.AfterInsert(writer, tableName, columnMap[tableName])
|
||||
}
|
||||
|
||||
log(fmt.Sprintf("获取表[%s]索引信息...", tableName))
|
||||
log(fmt.Sprintf("Get table [%s] index information...", tableName))
|
||||
indexs, err := srcMeta.GetTableIndex(tableName)
|
||||
if err != nil {
|
||||
log(fmt.Sprintf("获取表[%s]索引信息失败:%s", tableName, err.Error()))
|
||||
log(fmt.Sprintf("Failed to get table [%s] index information: %s", tableName, err.Error()))
|
||||
return err
|
||||
}
|
||||
|
||||
if len(indexs) > 0 {
|
||||
// 最后添加索引
|
||||
log(fmt.Sprintf("生成表[%s]索引...", tableName))
|
||||
writer.WriteString(fmt.Sprintf("\n-- ----------------------------\n-- 表索引: %s \n-- ----------------------------\n", tableName))
|
||||
log(fmt.Sprintf("Generate table [%s] index...", tableName))
|
||||
writer.WriteString(fmt.Sprintf("\n-- ----------------------------\n-- Table Index: %s \n-- ----------------------------\n", tableName))
|
||||
sqlArr := targetDialect.GenerateIndexDDL(indexs, tabInfo)
|
||||
for _, sqlStr := range sqlArr {
|
||||
writer.WriteString(sqlStr + ";\n")
|
||||
|
||||
@@ -104,9 +104,9 @@ func (app *dataSyncAppImpl) AddCronJob(ctx context.Context, taskEntity *entity.D
|
||||
if taskEntity.Status == entity.DataSyncTaskStatusEnable {
|
||||
taskId := taskEntity.Id
|
||||
scheduler.AddFunByKey(key, taskEntity.TaskCron, func() {
|
||||
logx.Infof("开始执行同步任务: %d", taskId)
|
||||
logx.Infof("start the data synchronization task: %d", taskId)
|
||||
if err := app.RunCronJob(ctx, taskId); err != nil {
|
||||
logx.Errorf("定时执行数据同步任务失败: %s", err.Error())
|
||||
logx.Errorf("the data synchronization task failed to execute at a scheduled time: %s", err.Error())
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -130,15 +130,15 @@ func (app *dataSyncAppImpl) RunCronJob(ctx context.Context, id uint64) error {
|
||||
// 查询最新的任务信息
|
||||
task, err := app.GetById(id)
|
||||
if err != nil {
|
||||
return errorx.NewBiz("任务不存在")
|
||||
return errorx.NewBiz("task not found")
|
||||
}
|
||||
if task.RunningState == entity.DataSyncTaskRunStateRunning {
|
||||
return errorx.NewBiz("该任务正在执行中")
|
||||
return errorx.NewBiz("the task is in progress")
|
||||
}
|
||||
// 开始运行时,修改状态为运行中
|
||||
app.changeRunningState(id, entity.DataSyncTaskRunStateRunning)
|
||||
|
||||
logx.InfofContext(ctx, "开始执行数据同步任务:%s => %s", task.TaskName, task.TaskKey)
|
||||
logx.InfofContext(ctx, "start the data synchronization task: %s => %s", task.TaskName, task.TaskKey)
|
||||
|
||||
go func() {
|
||||
// 通过占位符格式化sql
|
||||
@@ -147,7 +147,7 @@ func (app *dataSyncAppImpl) RunCronJob(ctx context.Context, id uint64) error {
|
||||
if task.UpdFieldVal != "0" && task.UpdFieldVal != "" && task.UpdField != "" {
|
||||
srcConn, err := app.dbApp.GetDbConn(uint64(task.SrcDbId), task.SrcDbName)
|
||||
if err != nil {
|
||||
logx.ErrorfContext(ctx, "数据源连接不可用, %s", err.Error())
|
||||
logx.ErrorfContext(ctx, "data source connection unavailable: %s", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
@@ -180,7 +180,7 @@ func (app *dataSyncAppImpl) RunCronJob(ctx context.Context, id uint64) error {
|
||||
|
||||
log, err := app.doDataSync(ctx, sql, task)
|
||||
if err != nil {
|
||||
log.ErrText = fmt.Sprintf("执行失败: %s", err.Error())
|
||||
log.ErrText = fmt.Sprintf("execution failure: %s", err.Error())
|
||||
logx.ErrorContext(ctx, log.ErrText)
|
||||
log.Status = entity.DataSyncTaskStateFail
|
||||
app.endRunning(task, log)
|
||||
@@ -202,17 +202,17 @@ func (app *dataSyncAppImpl) doDataSync(ctx context.Context, sql string, task *en
|
||||
// 获取源数据库连接
|
||||
srcConn, err := app.dbApp.GetDbConn(uint64(task.SrcDbId), task.SrcDbName)
|
||||
if err != nil {
|
||||
return syncLog, errorx.NewBiz("连接源数据库失败: %s", err.Error())
|
||||
return syncLog, errorx.NewBiz("failed to connect to the source database: %s", err.Error())
|
||||
}
|
||||
|
||||
// 获取目标数据库连接
|
||||
targetConn, err := app.dbApp.GetDbConn(uint64(task.TargetDbId), task.TargetDbName)
|
||||
if err != nil {
|
||||
return syncLog, errorx.NewBiz("连接目标数据库失败: %s", err.Error())
|
||||
return syncLog, errorx.NewBiz("failed to connect to the target database: %s", err.Error())
|
||||
}
|
||||
targetDbTx, err := targetConn.Begin()
|
||||
if err != nil {
|
||||
return syncLog, errorx.NewBiz("开启目标数据库事务失败: %s", err.Error())
|
||||
return syncLog, errorx.NewBiz("failed to start the target database transaction: %s", err.Error())
|
||||
}
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
@@ -227,7 +227,7 @@ func (app *dataSyncAppImpl) doDataSync(ctx context.Context, sql string, task *en
|
||||
var fieldMap []map[string]string
|
||||
err = json.Unmarshal([]byte(task.FieldMap), &fieldMap)
|
||||
if err != nil {
|
||||
return syncLog, errorx.NewBiz("解析字段映射json出错: %s", err.Error())
|
||||
return syncLog, errorx.NewBiz("there was an error parsing the field map json: %s", err.Error())
|
||||
}
|
||||
var updFieldType dbi.DataType
|
||||
|
||||
@@ -265,7 +265,7 @@ func (app *dataSyncAppImpl) doDataSync(ctx context.Context, sql string, task *en
|
||||
}
|
||||
|
||||
// 记录当前已同步的数据量
|
||||
syncLog.ErrText = fmt.Sprintf("本次任务执行中,已同步:%d条", total)
|
||||
syncLog.ErrText = fmt.Sprintf("during the execution of this task, %d has been synchronized", total)
|
||||
logx.InfoContext(ctx, syncLog.ErrText)
|
||||
syncLog.ResNum = total
|
||||
app.saveLog(syncLog)
|
||||
@@ -292,14 +292,14 @@ func (app *dataSyncAppImpl) doDataSync(ctx context.Context, sql string, task *en
|
||||
// 如果是mssql,暂不手动提交事务,否则报错 mssql: The COMMIT TRANSACTION request has no corresponding BEGIN TRANSACTION.
|
||||
if err := targetDbTx.Commit(); err != nil {
|
||||
if targetConn.Info.Type != dbi.DbTypeMssql {
|
||||
return syncLog, errorx.NewBiz("数据同步-目标数据库事务提交失败: %s", err.Error())
|
||||
return syncLog, errorx.NewBiz("data synchronization - The target database transaction failed to commit: %s", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
logx.InfofContext(ctx, "同步任务:[%s],执行完毕,保存记录成功:[%d]条", task.TaskName, total)
|
||||
logx.InfofContext(ctx, "synchronous task: [%s], finished execution, save records successfully: [%d]", task.TaskName, total)
|
||||
|
||||
// 保存执行成功日志
|
||||
syncLog.ErrText = fmt.Sprintf("本次任务执行成功,新数据:%d 条", total)
|
||||
syncLog.ErrText = fmt.Sprintf("the synchronous task was executed successfully. New data: %d", total)
|
||||
syncLog.Status = entity.DataSyncTaskStateSuccess
|
||||
syncLog.ResNum = total
|
||||
app.endRunning(task, syncLog)
|
||||
@@ -381,7 +381,7 @@ func (app *dataSyncAppImpl) srcData2TargetDb(srcRes []map[string]any, fieldMap [
|
||||
// 运行过程中,判断状态是否为已关闭,是则结束运行,否则继续运行
|
||||
taskParam, _ := app.GetById(task.Id)
|
||||
if taskParam.RunningState == entity.DataSyncTaskRunStateStop {
|
||||
return errorx.NewBiz("该任务已被手动终止")
|
||||
return errorx.NewBiz("the task has been terminated manually")
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -415,7 +415,7 @@ func (app *dataSyncAppImpl) saveLog(log *entity.DataSyncLog) {
|
||||
func (app *dataSyncAppImpl) InitCronJob() {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
logx.ErrorTrace("数据同步任务初始化失败: %s", err.(error))
|
||||
logx.ErrorTrace("the data synchronization task failed to initialize: %s", err.(error))
|
||||
}
|
||||
}()
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"mayfly-go/internal/db/dbm/dbi"
|
||||
"mayfly-go/internal/db/domain/entity"
|
||||
"mayfly-go/internal/db/domain/repository"
|
||||
"mayfly-go/internal/db/imsg"
|
||||
tagapp "mayfly-go/internal/tag/application"
|
||||
tagdto "mayfly-go/internal/tag/application/dto"
|
||||
tagentity "mayfly-go/internal/tag/domain/entity"
|
||||
@@ -49,8 +50,6 @@ type instanceAppImpl struct {
|
||||
tagApp tagapp.TagTree `inject:"TagTreeApp"`
|
||||
resourceAuthCertApp tagapp.ResourceAuthCert `inject:"ResourceAuthCertApp"`
|
||||
dbApp Db `inject:"DbApp"`
|
||||
backupApp *DbBackupApp `inject:"DbBackupApp"`
|
||||
restoreApp *DbRestoreApp `inject:"DbRestoreApp"`
|
||||
}
|
||||
|
||||
var _ (Instance) = (*instanceAppImpl)(nil)
|
||||
@@ -90,7 +89,7 @@ func (app *instanceAppImpl) SaveDbInstance(ctx context.Context, instance *dto.Sa
|
||||
tagCodePaths := instance.TagCodePaths
|
||||
|
||||
if len(authCerts) == 0 {
|
||||
return 0, errorx.NewBiz("授权凭证信息不能为空")
|
||||
return 0, errorx.NewBiz("ac cannot be empty")
|
||||
}
|
||||
|
||||
// 查找是否存在该库
|
||||
@@ -103,7 +102,7 @@ func (app *instanceAppImpl) SaveDbInstance(ctx context.Context, instance *dto.Sa
|
||||
err := app.GetByCond(oldInstance)
|
||||
if instanceEntity.Id == 0 {
|
||||
if err == nil {
|
||||
return 0, errorx.NewBiz("该数据库实例已存在")
|
||||
return 0, errorx.NewBizI(ctx, imsg.ErrDbInstExist)
|
||||
}
|
||||
instanceEntity.Code = stringx.Rand(10)
|
||||
|
||||
@@ -126,13 +125,13 @@ func (app *instanceAppImpl) SaveDbInstance(ctx context.Context, instance *dto.Sa
|
||||
// 如果存在该库,则校验修改的库是否为该库
|
||||
if err == nil {
|
||||
if oldInstance.Id != instanceEntity.Id {
|
||||
return 0, errorx.NewBiz("该数据库实例已存在")
|
||||
return 0, errorx.NewBizI(ctx, imsg.ErrDbInstExist)
|
||||
}
|
||||
} else {
|
||||
// 根据host等未查到旧数据,则需要根据id重新获取,因为后续需要使用到code
|
||||
oldInstance, err = app.GetById(instanceEntity.Id)
|
||||
if err != nil {
|
||||
return 0, errorx.NewBiz("该数据库实例不存在")
|
||||
return 0, errorx.NewBiz("db instance not found")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,23 +159,7 @@ func (app *instanceAppImpl) SaveDbInstance(ctx context.Context, instance *dto.Sa
|
||||
func (app *instanceAppImpl) Delete(ctx context.Context, instanceId uint64) error {
|
||||
instance, err := app.GetById(instanceId)
|
||||
if err != nil {
|
||||
return errorx.NewBiz("获取数据库实例错误,数据库实例ID为: %d", instance.Id)
|
||||
}
|
||||
|
||||
restore := &entity.DbRestore{
|
||||
DbInstanceId: instanceId,
|
||||
}
|
||||
err = app.restoreApp.restoreRepo.GetByCond(restore)
|
||||
if err == nil {
|
||||
return errorx.NewBiz("不能删除数据库实例【%s】,请先删除关联的数据库恢复任务。", instance.Name)
|
||||
}
|
||||
|
||||
backup := &entity.DbBackup{
|
||||
DbInstanceId: instanceId,
|
||||
}
|
||||
err = app.backupApp.backupRepo.GetByCond(backup)
|
||||
if err == nil {
|
||||
return errorx.NewBiz("不能删除数据库实例【%s】,请先删除关联的数据库备份任务。", instance.Name)
|
||||
return errorx.NewBiz("db instnace not found")
|
||||
}
|
||||
|
||||
dbs, _ := app.dbApp.ListByCond(&entity.Db{
|
||||
@@ -227,13 +210,13 @@ func (app *instanceAppImpl) GetDatabases(ed *entity.DbInstance, authCert *tagent
|
||||
func (app *instanceAppImpl) GetDatabasesByAc(acName string) ([]string, error) {
|
||||
ac, err := app.resourceAuthCertApp.GetAuthCert(acName)
|
||||
if err != nil {
|
||||
return nil, errorx.NewBiz("该授权凭证不存在")
|
||||
return nil, errorx.NewBiz("db ac not found")
|
||||
}
|
||||
|
||||
instance := &entity.DbInstance{Code: ac.ResourceCode}
|
||||
err = app.GetByCond(instance)
|
||||
if err != nil {
|
||||
return nil, errorx.NewBiz("不存在该授权凭证对应的数据库实例信息")
|
||||
return nil, errorx.NewBiz("the db instance information for this ac does not exist")
|
||||
}
|
||||
|
||||
return app.getDatabases(instance, ac)
|
||||
@@ -285,7 +268,7 @@ func (m *instanceAppImpl) genDbInstanceResourceTag(me *entity.DbInstance, authCe
|
||||
InstanceId: me.Id,
|
||||
})
|
||||
if err != nil {
|
||||
logx.Errorf("获取实例关联的数据库失败: %v", err)
|
||||
logx.Errorf("failed to retrieve the database associated with the instance: %v", err)
|
||||
}
|
||||
|
||||
authCertName2DbTags := make(map[string][]*tagdto.ResourceTag)
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"mayfly-go/internal/db/dbm/sqlparser/sqlstmt"
|
||||
"mayfly-go/internal/db/domain/entity"
|
||||
"mayfly-go/internal/db/domain/repository"
|
||||
"mayfly-go/internal/db/imsg"
|
||||
flowapp "mayfly-go/internal/flow/application"
|
||||
flowentity "mayfly-go/internal/flow/domain/entity"
|
||||
"mayfly-go/pkg/contextx"
|
||||
@@ -202,7 +203,7 @@ func (d *dbSqlExecAppImpl) FlowBizHandle(ctx context.Context, bizHandleParam *fl
|
||||
|
||||
execSqlBizForm, err := jsonx.To(procinst.BizForm, new(FlowDbExecSqlBizForm))
|
||||
if err != nil {
|
||||
return nil, errorx.NewBiz("业务表单信息解析失败: %s", err.Error())
|
||||
return nil, errorx.NewBiz("failed to parse the business form information: %s", err.Error())
|
||||
}
|
||||
|
||||
dbConn, err := d.dbApp.GetDbConn(execSqlBizForm.DbId, execSqlBizForm.DbName)
|
||||
@@ -225,7 +226,7 @@ func (d *dbSqlExecAppImpl) FlowBizHandle(ctx context.Context, bizHandleParam *fl
|
||||
// 存在一条错误的sql,则表示业务处理失败
|
||||
for _, er := range execRes {
|
||||
if er.ErrorMsg != "" {
|
||||
return execRes, errorx.NewBiz("存在执行错误的sql")
|
||||
return execRes, errorx.NewBizI(ctx, imsg.ErrExistRunFailSql)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -264,7 +265,7 @@ func (d *dbSqlExecAppImpl) doSelect(ctx context.Context, sqlExecParam *sqlExecPa
|
||||
|
||||
if procdef := sqlExecParam.Procdef; procdef != nil {
|
||||
if needStartProc := procdef.MatchCondition(DbSqlExecFlowBizType, collx.Kvs("stmtType", "select")); needStartProc {
|
||||
return nil, errorx.NewBiz("该操作需要提交工单审批执行")
|
||||
return nil, errorx.NewBizI(ctx, imsg.ErrNeedSubmitWorkTicket)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -287,10 +288,10 @@ func (d *dbSqlExecAppImpl) doSelect(ctx context.Context, sqlExecParam *sqlExecPa
|
||||
// 如果配置为0,则不校验分页参数
|
||||
if needCheckLimit && maxCount != 0 {
|
||||
if limit == nil {
|
||||
return nil, errorx.NewBiz("请完善分页信息后执行")
|
||||
return nil, errorx.NewBizI(ctx, imsg.ErrNoLimitStmt)
|
||||
}
|
||||
if limit.RowCount > maxCount {
|
||||
return nil, errorx.NewBiz("查询结果集数需小于系统配置的%d条", maxCount)
|
||||
return nil, errorx.NewBizI(ctx, imsg.ErrLimitInvalid, "count", maxCount)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -304,7 +305,7 @@ func (d *dbSqlExecAppImpl) doSelect(ctx context.Context, sqlExecParam *sqlExecPa
|
||||
!strings.Contains(selectSql, " top ") {
|
||||
// 判断是不是count语句
|
||||
if !strings.Contains(selectSql, "count(") {
|
||||
return nil, errorx.NewBiz("请完善分页信息后执行")
|
||||
return nil, errorx.NewBizI(ctx, imsg.ErrNoLimitStmt)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -319,7 +320,7 @@ func (d *dbSqlExecAppImpl) doOtherRead(ctx context.Context, sqlExecParam *sqlExe
|
||||
|
||||
if procdef := sqlExecParam.Procdef; procdef != nil {
|
||||
if needStartProc := procdef.MatchCondition(DbSqlExecFlowBizType, collx.Kvs("stmtType", "read")); needStartProc {
|
||||
return nil, errorx.NewBiz("该操作需要提交工单审批执行")
|
||||
return nil, errorx.NewBizI(ctx, imsg.ErrNeedSubmitWorkTicket)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -332,7 +333,7 @@ func (d *dbSqlExecAppImpl) doExecDDL(ctx context.Context, sqlExecParam *sqlExecP
|
||||
|
||||
if procdef := sqlExecParam.Procdef; procdef != nil {
|
||||
if needStartProc := procdef.MatchCondition(DbSqlExecFlowBizType, collx.Kvs("stmtType", "ddl")); needStartProc {
|
||||
return nil, errorx.NewBiz("该操作需要提交工单审批执行")
|
||||
return nil, errorx.NewBizI(ctx, imsg.ErrNeedSubmitWorkTicket)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -344,7 +345,7 @@ func (d *dbSqlExecAppImpl) doUpdate(ctx context.Context, sqlExecParam *sqlExecPa
|
||||
|
||||
if procdef := sqlExecParam.Procdef; procdef != nil {
|
||||
if needStartProc := procdef.MatchCondition(DbSqlExecFlowBizType, collx.Kvs("stmtType", "update")); needStartProc {
|
||||
return nil, errorx.NewBiz("该操作需要提交工单审批执行")
|
||||
return nil, errorx.NewBizI(ctx, imsg.ErrNeedSubmitWorkTicket)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -364,7 +365,7 @@ func (d *dbSqlExecAppImpl) doUpdate(ctx context.Context, sqlExecParam *sqlExecPa
|
||||
tableSources := updatestmt.TableSources.TableSources
|
||||
// 不支持多表更新记录旧值
|
||||
if len(tableSources) != 1 {
|
||||
logx.ErrorContext(ctx, "Update SQL - 记录旧值只支持单表更新")
|
||||
logx.ErrorContext(ctx, "Update SQL - logging old values only supports single-table updates")
|
||||
return d.doExec(ctx, dbConn, sqlExecParam.Sql)
|
||||
}
|
||||
|
||||
@@ -378,21 +379,21 @@ func (d *dbSqlExecAppImpl) doUpdate(ctx context.Context, sqlExecParam *sqlExecPa
|
||||
}
|
||||
|
||||
if tableName == "" {
|
||||
logx.ErrorContext(ctx, "Update SQL - 获取表名失败")
|
||||
logx.ErrorContext(ctx, "Update SQL - failed to get table name")
|
||||
return d.doExec(ctx, dbConn, sqlExecParam.Sql)
|
||||
}
|
||||
execRecord.Table = tableName
|
||||
|
||||
whereStr := updatestmt.Where.GetText()
|
||||
if whereStr == "" {
|
||||
logx.ErrorContext(ctx, "Update SQL - 不存在where条件")
|
||||
logx.ErrorContext(ctx, "Update SQL - there is no where condition")
|
||||
return d.doExec(ctx, dbConn, sqlExecParam.Sql)
|
||||
}
|
||||
|
||||
// 获取表主键列名,排除使用别名
|
||||
primaryKey, err := dbConn.GetMetadata().GetPrimaryKey(tableName)
|
||||
if err != nil {
|
||||
logx.ErrorfContext(ctx, "Update SQL - 获取主键列失败: %s", err.Error())
|
||||
logx.ErrorfContext(ctx, "Update SQL - failed to get primary key column: %s", err.Error())
|
||||
return d.doExec(ctx, dbConn, sqlExecParam.Sql)
|
||||
}
|
||||
|
||||
@@ -416,12 +417,12 @@ func (d *dbSqlExecAppImpl) doUpdate(ctx context.Context, sqlExecParam *sqlExecPa
|
||||
nowRec++
|
||||
res = append(res, row)
|
||||
if nowRec == maxRec {
|
||||
return errorx.NewBiz(fmt.Sprintf("Update SQL -超出更新最大查询条数限制: %d", maxRec))
|
||||
return errorx.NewBiz(fmt.Sprintf("Update SQL - the maximum number of updated queries is exceeded: %d", maxRec))
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
logx.ErrorfContext(ctx, "Update SQL - 获取更新旧值失败: %s", err.Error())
|
||||
logx.ErrorfContext(ctx, "Update SQL - failed to get the updated old value: %s", err.Error())
|
||||
return d.doExec(ctx, dbConn, sqlExecParam.Sql)
|
||||
}
|
||||
execRecord.OldValue = jsonx.ToStr(res)
|
||||
@@ -432,7 +433,7 @@ func (d *dbSqlExecAppImpl) doUpdate(ctx context.Context, sqlExecParam *sqlExecPa
|
||||
func (d *dbSqlExecAppImpl) doDelete(ctx context.Context, sqlExecParam *sqlExecParam) (*DbSqlExecRes, error) {
|
||||
if procdef := sqlExecParam.Procdef; procdef != nil {
|
||||
if needStartProc := procdef.MatchCondition(DbSqlExecFlowBizType, collx.Kvs("stmtType", "delete")); needStartProc {
|
||||
return nil, errorx.NewBiz("该操作需要提交工单审批执行")
|
||||
return nil, errorx.NewBizI(ctx, imsg.ErrNeedSubmitWorkTicket)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -453,7 +454,7 @@ func (d *dbSqlExecAppImpl) doDelete(ctx context.Context, sqlExecParam *sqlExecPa
|
||||
tableSources := deletestmt.TableSources.TableSources
|
||||
// 不支持多表删除记录旧值
|
||||
if len(tableSources) != 1 {
|
||||
logx.ErrorContext(ctx, "Delete SQL - 记录旧值只支持单表删除")
|
||||
logx.ErrorContext(ctx, "Delete SQL - logging old values only supports single-table deletion")
|
||||
return d.doExec(ctx, dbConn, sqlExecParam.Sql)
|
||||
}
|
||||
|
||||
@@ -467,14 +468,14 @@ func (d *dbSqlExecAppImpl) doDelete(ctx context.Context, sqlExecParam *sqlExecPa
|
||||
}
|
||||
|
||||
if tableName == "" {
|
||||
logx.ErrorContext(ctx, "Delete SQL - 获取表名失败")
|
||||
logx.ErrorContext(ctx, "Delete SQL - failed to get table name")
|
||||
return d.doExec(ctx, dbConn, sqlExecParam.Sql)
|
||||
}
|
||||
execRecord.Table = tableName
|
||||
|
||||
whereStr := deletestmt.Where.GetText()
|
||||
if whereStr == "" {
|
||||
logx.ErrorContext(ctx, "Delete SQL - 不存在where条件")
|
||||
logx.ErrorContext(ctx, "Delete SQL - there is no where condition")
|
||||
return d.doExec(ctx, dbConn, sqlExecParam.Sql)
|
||||
}
|
||||
|
||||
@@ -489,7 +490,7 @@ func (d *dbSqlExecAppImpl) doDelete(ctx context.Context, sqlExecParam *sqlExecPa
|
||||
func (d *dbSqlExecAppImpl) doInsert(ctx context.Context, sqlExecParam *sqlExecParam) (*DbSqlExecRes, error) {
|
||||
if procdef := sqlExecParam.Procdef; procdef != nil {
|
||||
if needStartProc := procdef.MatchCondition(DbSqlExecFlowBizType, collx.Kvs("stmtType", "insert")); needStartProc {
|
||||
return nil, errorx.NewBiz("该操作需要提交工单审批执行")
|
||||
return nil, errorx.NewBizI(ctx, imsg.ErrNeedSubmitWorkTicket)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -119,7 +119,7 @@ func (app *dbTransferAppImpl) AddCronJob(ctx context.Context, taskEntity *entity
|
||||
|
||||
taskId := taskEntity.Id
|
||||
scheduler.AddFunByKey(key, taskEntity.Cron, func() {
|
||||
logx.Infof("开始执行同步任务: %d", taskId)
|
||||
logx.Infof("start the synchronization task: %d", taskId)
|
||||
logId, _ := app.CreateLog(ctx, taskId)
|
||||
app.Run(ctx, taskId, logId)
|
||||
})
|
||||
@@ -175,10 +175,10 @@ func (app *dbTransferAppImpl) InitCronJob() {
|
||||
|
||||
func (app *dbTransferAppImpl) CreateLog(ctx context.Context, taskId uint64) (uint64, error) {
|
||||
logId, err := app.logApp.CreateLog(ctx, &sysapp.CreateLogReq{
|
||||
Description: "DBMS-执行数据迁移",
|
||||
Description: "DBMS - Execution DB Transfer",
|
||||
ReqParam: collx.Kvs("taskId", taskId),
|
||||
Type: sysentity.SyslogTypeRunning,
|
||||
Resp: "开始执行数据迁移...",
|
||||
Resp: "Data transfer starts...",
|
||||
})
|
||||
return logId, err
|
||||
}
|
||||
@@ -186,12 +186,12 @@ func (app *dbTransferAppImpl) CreateLog(ctx context.Context, taskId uint64) (uin
|
||||
func (app *dbTransferAppImpl) Run(ctx context.Context, taskId uint64, logId uint64) {
|
||||
task, err := app.GetById(taskId)
|
||||
if err != nil {
|
||||
logx.Errorf("创建DBMS-执行数据迁移日志失败:%v", err)
|
||||
logx.Errorf("Create DBMS- Failed to perform data transfer log: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if app.IsRunning(taskId) {
|
||||
logx.Error("[%d]该任务正在运行中...", taskId)
|
||||
logx.Error("[%d] the task is running...", taskId)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -200,7 +200,7 @@ func (app *dbTransferAppImpl) Run(ctx context.Context, taskId uint64, logId uint
|
||||
task.LogId = logId
|
||||
task.RunningState = entity.DbTransferTaskRunStateRunning
|
||||
if err = app.UpdateById(ctx, task); err != nil {
|
||||
logx.Errorf("更新任务执行状态失败")
|
||||
logx.Errorf("failed to update task execution status")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -211,7 +211,7 @@ func (app *dbTransferAppImpl) Run(ctx context.Context, taskId uint64, logId uint
|
||||
// 获取源库表信息
|
||||
srcConn, err := app.dbApp.GetDbConn(uint64(task.SrcDbId), task.SrcDbName)
|
||||
if err != nil {
|
||||
app.EndTransfer(ctx, logId, taskId, "获取源库连接失败", err, nil)
|
||||
app.EndTransfer(ctx, logId, taskId, "failed to obtain source db connection", err, nil)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -220,14 +220,14 @@ func (app *dbTransferAppImpl) Run(ctx context.Context, taskId uint64, logId uint
|
||||
if task.CheckedKeys == "all" {
|
||||
tables, err = srcConn.GetMetadata().GetTables()
|
||||
if err != nil {
|
||||
app.EndTransfer(ctx, logId, taskId, "获取源表信息失败", err, nil)
|
||||
app.EndTransfer(ctx, logId, taskId, "failed to get source table information", err, nil)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
tableNames := strings.Split(task.CheckedKeys, ",")
|
||||
tables, err = srcConn.GetMetadata().GetTables(tableNames...)
|
||||
if err != nil {
|
||||
app.EndTransfer(ctx, logId, taskId, "获取源表信息失败", err, nil)
|
||||
app.EndTransfer(ctx, logId, taskId, "failed to get source table information", err, nil)
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -240,7 +240,7 @@ func (app *dbTransferAppImpl) Run(ctx context.Context, taskId uint64, logId uint
|
||||
defer app.logApp.Flush(logId, true)
|
||||
app.transfer2Db(ctx, taskId, logId, task, srcConn, start, tables)
|
||||
} else {
|
||||
app.EndTransfer(ctx, logId, taskId, "迁移模式出错,目前仅支持迁移到文件或数据库", err, nil)
|
||||
app.EndTransfer(ctx, logId, taskId, "error in transfer mode, only migrating to files or databases is currently supported", err, nil)
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -249,16 +249,16 @@ func (app *dbTransferAppImpl) transfer2Db(ctx context.Context, taskId uint64, lo
|
||||
// 获取目标库表信息
|
||||
targetConn, err := app.dbApp.GetDbConn(uint64(task.TargetDbId), task.TargetDbName)
|
||||
if err != nil {
|
||||
app.EndTransfer(ctx, logId, taskId, "获取目标库连接失败", err, nil)
|
||||
app.EndTransfer(ctx, logId, taskId, "failed to get target db connection", err, nil)
|
||||
return
|
||||
}
|
||||
// 迁移表
|
||||
if err = app.transferDbTables(ctx, logId, task, srcConn, targetConn, tables); err != nil {
|
||||
app.EndTransfer(ctx, logId, taskId, "迁移表失败", err, nil)
|
||||
app.EndTransfer(ctx, logId, taskId, "transfer table failed", err, nil)
|
||||
return
|
||||
}
|
||||
|
||||
app.EndTransfer(ctx, logId, taskId, fmt.Sprintf("执行迁移完成,执行迁移任务[taskId = %d]完成, 耗时:%v", taskId, time.Since(start)), nil, nil)
|
||||
app.EndTransfer(ctx, logId, taskId, fmt.Sprintf("execute transfer task [taskId = %d] complete, time: %v", taskId, time.Since(start)), nil, nil)
|
||||
}
|
||||
|
||||
func (app *dbTransferAppImpl) transfer2File(ctx context.Context, taskId uint64, logId uint64, task *entity.DbTransferTask, srcConn *dbi.DbConn, start time.Time, tables []dbi.Table) {
|
||||
@@ -276,7 +276,7 @@ func (app *dbTransferAppImpl) transfer2File(ctx context.Context, taskId uint64,
|
||||
filename := fmt.Sprintf("dtf_%s_%s.sql", task.TaskName, timex.TimeNo())
|
||||
fileKey, writer, saveFileFunc, err := app.fileApp.NewWriter(ctx, "", filename)
|
||||
if err != nil {
|
||||
app.EndTransfer(ctx, logId, taskId, "创建文件失败", err, nil)
|
||||
app.EndTransfer(ctx, logId, taskId, "create file error", err, nil)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -286,8 +286,8 @@ func (app *dbTransferAppImpl) transfer2File(ctx context.Context, taskId uint64,
|
||||
tableNames = append(tableNames, table.TableName)
|
||||
}
|
||||
// 2、把源库数据迁移到文件
|
||||
app.Log(ctx, logId, fmt.Sprintf("开始迁移表数据到文件: %s", filename))
|
||||
app.Log(ctx, logId, fmt.Sprintf("目标库文件语言类型: %s", task.TargetFileDbType))
|
||||
app.Log(ctx, logId, fmt.Sprintf("start transfer table data to files: %s", filename))
|
||||
app.Log(ctx, logId, fmt.Sprintf("dialect type of target db file: %s", task.TargetFileDbType))
|
||||
|
||||
go func() {
|
||||
var err error
|
||||
@@ -310,12 +310,12 @@ func (app *dbTransferAppImpl) transfer2File(ctx context.Context, taskId uint64,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
app.EndTransfer(ctx, logId, taskId, "数据库迁移失败", err, nil)
|
||||
app.EndTransfer(ctx, logId, taskId, "db transfer to file failed", err, nil)
|
||||
tFile.Status = entity.DbTransferFileStatusFail
|
||||
_ = app.transferFileApp.UpdateById(ctx, tFile)
|
||||
return
|
||||
}
|
||||
app.EndTransfer(ctx, logId, taskId, "数据库迁移完成", err, nil)
|
||||
app.EndTransfer(ctx, logId, taskId, "database transfer complete", err, nil)
|
||||
|
||||
tFile.Status = entity.DbTransferFileStatusSuccess
|
||||
tFile.FileKey = fileKey
|
||||
@@ -326,11 +326,11 @@ func (app *dbTransferAppImpl) transfer2File(ctx context.Context, taskId uint64,
|
||||
func (app *dbTransferAppImpl) Stop(ctx context.Context, taskId uint64) error {
|
||||
task, err := app.GetById(taskId)
|
||||
if err != nil {
|
||||
return errorx.NewBiz("任务不存在")
|
||||
return errorx.NewBiz("task not found")
|
||||
}
|
||||
|
||||
if task.RunningState != entity.DbTransferTaskRunStateRunning {
|
||||
return errorx.NewBiz("该任务未在执行")
|
||||
return errorx.NewBiz("the task is not being executed")
|
||||
}
|
||||
task.RunningState = entity.DbTransferTaskRunStateStop
|
||||
if err = app.UpdateById(ctx, task); err != nil {
|
||||
@@ -351,14 +351,14 @@ func (app *dbTransferAppImpl) transferDbTables(ctx context.Context, logId uint64
|
||||
}
|
||||
|
||||
if len(tableNames) == 0 {
|
||||
return errorx.NewBiz("没有需要迁移的表")
|
||||
return errorx.NewBiz("there are no tables to migrate")
|
||||
}
|
||||
srcDialect := srcConn.GetDialect()
|
||||
srcMetadata := srcConn.GetMetadata()
|
||||
// 查询源表列信息
|
||||
columns, err := srcMetadata.GetColumns(tableNames...)
|
||||
if err != nil {
|
||||
return errorx.NewBiz("获取源表列信息失败")
|
||||
return errorx.NewBiz("failed to get the source table column information")
|
||||
}
|
||||
|
||||
// 以表名分组,存放每个表的列信息
|
||||
@@ -394,35 +394,35 @@ func (app *dbTransferAppImpl) transferDbTables(ctx context.Context, logId uint64
|
||||
}
|
||||
|
||||
// 通过公共列信息生成目标库的建表语句,并执行目标库建表
|
||||
app.Log(ctx, logId, fmt.Sprintf("开始创建目标表: 表名:%s", tbName))
|
||||
app.Log(ctx, logId, fmt.Sprintf("start creating the target table: %s", tbName))
|
||||
|
||||
sqlArr := targetDialect.GenerateTableDDL(targetCols, tableMap[tbName], true)
|
||||
for _, sqlStr := range sqlArr {
|
||||
_, err := targetConn.Exec(sqlStr)
|
||||
if err != nil {
|
||||
return errorx.NewBiz(fmt.Sprintf("创建目标表失败: 表名:%s, error: %s", tbName, err.Error()))
|
||||
return errorx.NewBiz(fmt.Sprintf("failed to create target table: %s, error: %s", tbName, err.Error()))
|
||||
}
|
||||
}
|
||||
app.Log(ctx, logId, fmt.Sprintf("创建目标表成功: 表名:%s", tbName))
|
||||
app.Log(ctx, logId, fmt.Sprintf("target table created successfully: %s", tbName))
|
||||
|
||||
// 迁移数据
|
||||
app.Log(ctx, logId, fmt.Sprintf("开始迁移数据: 表名:%s", tbName))
|
||||
app.Log(ctx, logId, fmt.Sprintf("start transfer data: %s", tbName))
|
||||
total, err := app.transferData(ctx, logId, task.Id, tbName, targetCols, srcConn, targetConn)
|
||||
if err != nil {
|
||||
return errorx.NewBiz(fmt.Sprintf("迁移数据失败: 表名:%s, error: %s", tbName, err.Error()))
|
||||
return errorx.NewBiz(fmt.Sprintf("failed to transfer => table: %s, error: %s", tbName, err.Error()))
|
||||
}
|
||||
app.Log(ctx, logId, fmt.Sprintf("迁移数据成功: 表名:%s, 数据:%d 条", tbName, total))
|
||||
app.Log(ctx, logId, fmt.Sprintf("successfully transfer data => table: %s, data: %d entries", tbName, total))
|
||||
|
||||
// 有些数据库迁移完数据之后,需要更新表自增序列为当前表最大值
|
||||
targetDialect.UpdateSequence(tbName, targetCols)
|
||||
|
||||
// 迁移索引信息
|
||||
app.Log(ctx, logId, fmt.Sprintf("开始迁移索引: 表名:%s", tbName))
|
||||
app.Log(ctx, logId, fmt.Sprintf("start transfer index => table: %s", tbName))
|
||||
err = app.transferIndex(ctx, tableMap[tbName], srcConn, targetConn)
|
||||
if err != nil {
|
||||
return errorx.NewBiz(fmt.Sprintf("迁移索引失败: 表名:%s, error: %s", tbName, err.Error()))
|
||||
return errorx.NewBiz(fmt.Sprintf("failed to transfer index => table: %s, error: %s", tbName, err.Error()))
|
||||
}
|
||||
app.Log(ctx, logId, fmt.Sprintf("迁移索引成功: 表名:%s", tbName))
|
||||
app.Log(ctx, logId, fmt.Sprintf("successfully transfer index => table: %s", tbName))
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -440,7 +440,7 @@ func (app *dbTransferAppImpl) transferData(ctx context.Context, logId uint64, ta
|
||||
srcDialect := srcConn.GetDialect()
|
||||
srcConverter := srcDialect.GetDataHelper()
|
||||
targetDialect := targetConn.GetDialect()
|
||||
logExtraKey := fmt.Sprintf("`%s` 当前已迁移数据量: ", tableName)
|
||||
logExtraKey := fmt.Sprintf("`%s` amount of transfer data currently: ", tableName)
|
||||
|
||||
// 游标查询源表数据,并批量插入目标表
|
||||
_, err = srcConn.WalkTableRows(context.Background(), tableName, func(row map[string]any, columns []*dbi.QueryColumn) error {
|
||||
@@ -455,7 +455,7 @@ func (app *dbTransferAppImpl) transferData(ctx context.Context, logId uint64, ta
|
||||
if total%batchSize == 0 {
|
||||
err = app.transfer2Target(taskId, targetConn, targetColumns, result, targetDialect, tableName)
|
||||
if err != nil {
|
||||
logx.ErrorfContext(ctx, "批量插入目标表数据失败: %v", err)
|
||||
logx.ErrorfContext(ctx, "batch insert data to target table failed: %v", err)
|
||||
return err
|
||||
}
|
||||
result = result[:0]
|
||||
@@ -472,7 +472,7 @@ func (app *dbTransferAppImpl) transferData(ctx context.Context, logId uint64, ta
|
||||
if len(result) > 0 {
|
||||
err = app.transfer2Target(taskId, targetConn, targetColumns, result, targetDialect, tableName)
|
||||
if err != nil {
|
||||
logx.ErrorfContext(ctx, "批量插入目标表数据失败,表名:%s error: %v", tableName, err)
|
||||
logx.ErrorfContext(ctx, "batch insert data to target table failed => table: %s, error: %v", tableName, err)
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
@@ -483,7 +483,7 @@ func (app *dbTransferAppImpl) transferData(ctx context.Context, logId uint64, ta
|
||||
|
||||
func (app *dbTransferAppImpl) transfer2Target(taskId uint64, targetConn *dbi.DbConn, targetColumns []dbi.Column, result []map[string]any, targetDialect dbi.Dialect, tbName string) error {
|
||||
if !app.IsRunning(taskId) {
|
||||
return errorx.NewBiz("迁移终止")
|
||||
return errorx.NewBiz("transfer stopped")
|
||||
}
|
||||
|
||||
tx, err := targetConn.Begin()
|
||||
@@ -533,7 +533,7 @@ func (app *dbTransferAppImpl) transfer2Target(taskId uint64, targetConn *dbi.DbC
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
tx.Rollback()
|
||||
logx.Errorf("批量插入目标表数据失败: %v", r)
|
||||
logx.Errorf("batch insert data to target table failed: %v", r)
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -545,7 +545,7 @@ func (app *dbTransferAppImpl) transferIndex(ctx context.Context, tableInfo dbi.T
|
||||
// 查询源表索引信息
|
||||
indexs, err := srcConn.GetMetadata().GetTableIndex(tableInfo.TableName)
|
||||
if err != nil {
|
||||
logx.Error("获取索引信息失败", err)
|
||||
logx.Errorf("failed to get index information: %s", err)
|
||||
return err
|
||||
}
|
||||
if len(indexs) == 0 {
|
||||
@@ -564,22 +564,22 @@ func (app *dbTransferAppImpl) transferIndex(ctx context.Context, tableInfo dbi.T
|
||||
}
|
||||
|
||||
func (d *dbTransferAppImpl) TimerDeleteTransferFile() {
|
||||
logx.Debug("开始定时删除迁移文件...")
|
||||
logx.Debug("start deleting transfer files periodically...")
|
||||
scheduler.AddFun("@every 100m", func() {
|
||||
dts, err := d.ListByCond(model.NewCond().Eq("mode", entity.DbTransferTaskModeFile).Ge("file_save_days", 1))
|
||||
if err != nil {
|
||||
logx.Errorf("定时获取数据库迁移至文件任务失败: %s", err.Error())
|
||||
logx.Errorf("the task to periodically get database transfer to file failed: %s", err.Error())
|
||||
return
|
||||
}
|
||||
for _, dt := range dts {
|
||||
needDelFiles, err := d.transferFileApp.ListByCond(model.NewCond().Eq("task_id", dt.Id).Le("create_time", time.Now().AddDate(0, 0, -dt.FileSaveDays)))
|
||||
if err != nil {
|
||||
logx.Errorf("定时获取迁移文件失败: %s", err.Error())
|
||||
logx.Errorf("failed to obtain the transfer file periodically: %s", err.Error())
|
||||
continue
|
||||
}
|
||||
for _, nf := range needDelFiles {
|
||||
if err := d.transferFileApp.Delete(context.Background(), nf.Id); err != nil {
|
||||
logx.Errorf("定时删除迁移文件失败: %s", err.Error())
|
||||
logx.Errorf("failed to delete transfer files periodically: %s", err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ func (d *DbInfo) GetLogDesc() string {
|
||||
// 连接数据库
|
||||
func (dbInfo *DbInfo) Conn(meta Meta) (*DbConn, error) {
|
||||
if meta == nil {
|
||||
return nil, errorx.NewBiz("数据库元信息接口不能为空")
|
||||
return nil, errorx.NewBiz("the database meta information interface cannot be empty")
|
||||
}
|
||||
|
||||
// 赋值Meta,方便后续获取dialect等
|
||||
@@ -78,14 +78,14 @@ func (dbInfo *DbInfo) Conn(meta Meta) (*DbConn, error) {
|
||||
|
||||
conn, err := meta.GetSqlDb(dbInfo)
|
||||
if err != nil {
|
||||
logx.Errorf("连接db失败: %s:%d/%s, err:%s", dbInfo.Host, dbInfo.Port, database, err.Error())
|
||||
return nil, errorx.NewBiz(fmt.Sprintf("数据库连接失败: %s", err.Error()))
|
||||
logx.Errorf("db connection failed: %s:%d/%s, err:%s", dbInfo.Host, dbInfo.Port, database, err.Error())
|
||||
return nil, errorx.NewBiz(fmt.Sprintf("db connection failed: %s", err.Error()))
|
||||
}
|
||||
|
||||
err = conn.Ping()
|
||||
if err != nil {
|
||||
logx.Errorf("db ping失败: %s:%d/%s, err:%s", dbInfo.Host, dbInfo.Port, database, err.Error())
|
||||
return nil, errorx.NewBiz(fmt.Sprintf("数据库连接失败: %s", err.Error()))
|
||||
logx.Errorf("db ping failed: %s:%d/%s, err:%s", dbInfo.Host, dbInfo.Port, database, err.Error())
|
||||
return nil, errorx.NewBiz(fmt.Sprintf("db connection failed: %s", err.Error()))
|
||||
}
|
||||
|
||||
dbc := &DbConn{Id: GetDbConnId(dbInfo.Id, database), Info: dbInfo}
|
||||
@@ -97,7 +97,7 @@ func (dbInfo *DbInfo) Conn(meta Meta) (*DbConn, error) {
|
||||
// 设置闲置连接数
|
||||
conn.SetMaxIdleConns(1)
|
||||
dbc.db = conn
|
||||
logx.Infof("连接db: %s:%d/%s", dbInfo.Host, dbInfo.Port, database)
|
||||
logx.Infof("db connection: %s:%d/%s", dbInfo.Host, dbInfo.Port, database)
|
||||
|
||||
return dbc, nil
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package dbi
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"mayfly-go/internal/db/dbm/sqlparser"
|
||||
"mayfly-go/internal/db/dbm/sqlparser/pgsql"
|
||||
@@ -12,6 +11,8 @@ import (
|
||||
pq "gitee.com/liuzongyang/libpq"
|
||||
)
|
||||
|
||||
const DefaultQuoter = `"`
|
||||
|
||||
const (
|
||||
// -1. 无操作
|
||||
DuplicateStrategyNone = -1
|
||||
@@ -31,9 +32,6 @@ type DbCopyTable struct {
|
||||
// BaseDialect 基础dialect,在DefaultDialect 都有默认的实现方法
|
||||
type BaseDialect interface {
|
||||
|
||||
// GetIdentifierQuoteString 用于引用 SQL 标识符(关键字)的字符串
|
||||
GetIdentifierQuoteString() string
|
||||
|
||||
// QuoteIdentifier quotes an "identifier" (e.g. a table or a column name) to be
|
||||
// used as part of an SQL statement. For example:
|
||||
//
|
||||
@@ -106,33 +104,16 @@ type DefaultDialect struct {
|
||||
|
||||
var _ (BaseDialect) = (*DefaultDialect)(nil)
|
||||
|
||||
func (dd *DefaultDialect) GetIdentifierQuoteString() string {
|
||||
return `"`
|
||||
}
|
||||
|
||||
func (dx *DefaultDialect) QuoteIdentifier(name string) string {
|
||||
quoter := dx.GetIdentifierQuoteString()
|
||||
// 兼容mssql
|
||||
if quoter == "[" {
|
||||
return fmt.Sprintf("[%s]", name)
|
||||
}
|
||||
|
||||
end := strings.IndexRune(name, 0)
|
||||
if end > -1 {
|
||||
name = name[:end]
|
||||
}
|
||||
return quoter + strings.Replace(name, quoter, quoter+quoter, -1) + quoter
|
||||
return DefaultQuoter + strings.Replace(name, DefaultQuoter, DefaultQuoter+DefaultQuoter, -1) + DefaultQuoter
|
||||
}
|
||||
|
||||
func (dx *DefaultDialect) RemoveQuote(name string) string {
|
||||
quoter := dx.GetIdentifierQuoteString()
|
||||
|
||||
// 兼容mssql
|
||||
if quoter == "[" {
|
||||
return strings.Trim(name, "[]")
|
||||
}
|
||||
|
||||
return strings.ReplaceAll(name, quoter, "")
|
||||
return strings.ReplaceAll(name, DefaultQuoter, "")
|
||||
}
|
||||
|
||||
func (dd *DefaultDialect) QuoteEscape(str string) string {
|
||||
|
||||
@@ -25,7 +25,7 @@ type Meta interface {
|
||||
// GetSqlDb 根据数据库信息获取sql.DB
|
||||
GetSqlDb(*DbInfo) (*sql.DB, error)
|
||||
|
||||
// GetDialect 获取数据库方言, 若一些接口(如 GetIdentifierQuoteString)不需要DbConn,则可以传nil
|
||||
// GetDialect 获取数据库方言, 若一些接口(如QuoteIdentifier)不需要DbConn,则可以传nil
|
||||
GetDialect(*DbConn) Dialect
|
||||
|
||||
// GetMetadata 获取元数据信息接口
|
||||
|
||||
@@ -201,7 +201,7 @@ func GetLocalSql(file, key string) string {
|
||||
}
|
||||
|
||||
bytes, err := metasql.ReadFile(file)
|
||||
biz.ErrIsNilAppendErr(err, "获取sql meta文件内容失败: %s")
|
||||
biz.ErrIsNilAppendErr(err, "failed to get the contents of the sql meta file: %s")
|
||||
allSql := string(bytes)
|
||||
|
||||
sqls := strings.Split(allSql, "---------------------------------------")
|
||||
|
||||
@@ -362,8 +362,12 @@ func (md *MssqlDialect) GenerateIndexDDL(indexs []dbi.Index, tableInfo dbi.Table
|
||||
return sqls
|
||||
}
|
||||
|
||||
func (md *MssqlDialect) GetIdentifierQuoteString() string {
|
||||
return "["
|
||||
func (dx *MssqlDialect) QuoteIdentifier(name string) string {
|
||||
return fmt.Sprintf("[%s]", name)
|
||||
}
|
||||
|
||||
func (dx *MssqlDialect) RemoveQuote(name string) string {
|
||||
return strings.Trim(name, "[]")
|
||||
}
|
||||
|
||||
func (md *MssqlDialect) GetDataHelper() dbi.DataHelper {
|
||||
|
||||
@@ -11,6 +11,8 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
const Quoter = "`"
|
||||
|
||||
type MysqlDialect struct {
|
||||
dbi.DefaultDialect
|
||||
|
||||
@@ -188,8 +190,16 @@ func (md *MysqlDialect) genColumnBasicSql(column dbi.Column) string {
|
||||
return columnSql
|
||||
}
|
||||
|
||||
func (md *MysqlDialect) GetIdentifierQuoteString() string {
|
||||
return "`"
|
||||
func (dx *MysqlDialect) QuoteIdentifier(name string) string {
|
||||
end := strings.IndexRune(name, 0)
|
||||
if end > -1 {
|
||||
name = name[:end]
|
||||
}
|
||||
return Quoter + strings.Replace(name, Quoter, Quoter+Quoter, -1) + Quoter
|
||||
}
|
||||
|
||||
func (dx *MysqlDialect) RemoveQuote(name string) string {
|
||||
return strings.ReplaceAll(name, Quoter, "")
|
||||
}
|
||||
|
||||
func (md *MysqlDialect) QuoteLiteral(literal string) string {
|
||||
|
||||
@@ -32,14 +32,17 @@ func (v *MysqlVisitor) VisitSqlStatements(ctx *mysqlparser.SqlStatementsContext)
|
||||
}
|
||||
|
||||
func (v *MysqlVisitor) VisitSqlStatement(ctx *mysqlparser.SqlStatementContext) interface{} {
|
||||
if ctx.DmlStatement() != nil {
|
||||
if c := ctx.DmlStatement(); c != nil {
|
||||
return ctx.DmlStatement().Accept(v)
|
||||
}
|
||||
if ctx.DdlStatement() != nil {
|
||||
if c := ctx.DdlStatement(); c != nil {
|
||||
return ctx.DdlStatement().Accept(v)
|
||||
}
|
||||
if ctx.AdministrationStatement() != nil {
|
||||
return ctx.AdministrationStatement().Accept(v)
|
||||
if c := ctx.AdministrationStatement(); c != nil {
|
||||
return c.Accept(v)
|
||||
}
|
||||
if c := ctx.UtilityStatement(); c != nil {
|
||||
return c.Accept(v)
|
||||
}
|
||||
|
||||
return sqlstmt.NewNode(ctx.GetParser(), ctx)
|
||||
@@ -81,6 +84,16 @@ func (v *MysqlVisitor) VisitAdministrationStatement(ctx *mysqlparser.Administrat
|
||||
return sqlstmt.NewNode(ctx.GetParser(), ctx)
|
||||
}
|
||||
|
||||
func (v *MysqlVisitor) VisitUtilityStatement(ctx *mysqlparser.UtilityStatementContext) interface{} {
|
||||
if c := ctx.SimpleDescribeStatement(); c != nil {
|
||||
return c.Accept(v)
|
||||
}
|
||||
if c := ctx.FullDescribeStatement(); c != nil {
|
||||
return c.Accept(v)
|
||||
}
|
||||
return sqlstmt.NewNode(ctx.GetParser(), ctx)
|
||||
}
|
||||
|
||||
func (v *MysqlVisitor) VisitSimpleSelect(ctx *mysqlparser.SimpleSelectContext) interface{} {
|
||||
sss := new(sqlstmt.SimpleSelectStmt)
|
||||
sss.Node = sqlstmt.NewNode(ctx.GetParser(), ctx)
|
||||
|
||||
5
server/internal/db/imsg/en.go
Normal file
5
server/internal/db/imsg/en.go
Normal file
@@ -0,0 +1,5 @@
|
||||
package imsg
|
||||
|
||||
import "mayfly-go/pkg/i18n"
|
||||
|
||||
var En = map[i18n.MsgId]string{}
|
||||
52
server/internal/db/imsg/imsg.go
Normal file
52
server/internal/db/imsg/imsg.go
Normal file
@@ -0,0 +1,52 @@
|
||||
package imsg
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/common/consts"
|
||||
"mayfly-go/pkg/i18n"
|
||||
)
|
||||
|
||||
func init() {
|
||||
i18n.AppendLangMsg(i18n.Zh_CN, Zh_CN)
|
||||
i18n.AppendLangMsg(i18n.En, En)
|
||||
}
|
||||
|
||||
const (
|
||||
// db inst
|
||||
LogDbInstSave = iota + consts.ImsgNumDb
|
||||
LogDbInstDelete
|
||||
|
||||
ErrDbInstExist
|
||||
|
||||
// db
|
||||
LogDbSave
|
||||
LogDbDelete
|
||||
LogDbRunSql
|
||||
LogDbRunSqlFile
|
||||
LogDbDump
|
||||
|
||||
SqlScriptRunFail
|
||||
SqlScriptRunSuccess
|
||||
SqlScripRunProgress
|
||||
DbDumpErr
|
||||
ErrDbNameExist
|
||||
ErrDbNotAccess
|
||||
|
||||
ErrExistRunFailSql
|
||||
ErrNeedSubmitWorkTicket
|
||||
ErrNoLimitStmt
|
||||
ErrLimitInvalid
|
||||
|
||||
// db transfer
|
||||
LogDtsSave
|
||||
LogDtsDelete
|
||||
LogDtsChangeStatus
|
||||
LogDtsRun
|
||||
LogDtsStop
|
||||
LogDtsDeleteFile
|
||||
LogDtsRunSqlFile
|
||||
|
||||
// data sync
|
||||
LogDataSyncSave
|
||||
LogDataSyncDelete
|
||||
LogDataSyncChangeStatus
|
||||
)
|
||||
42
server/internal/db/imsg/zh_cn.go
Normal file
42
server/internal/db/imsg/zh_cn.go
Normal file
@@ -0,0 +1,42 @@
|
||||
package imsg
|
||||
|
||||
import "mayfly-go/pkg/i18n"
|
||||
|
||||
var Zh_CN = map[i18n.MsgId]string{
|
||||
LogDbInstSave: "DB-保存数据库实例",
|
||||
LogDbInstDelete: "DB-删除数据库实例",
|
||||
|
||||
ErrDbInstExist: "该数据库实例已存在",
|
||||
|
||||
// db
|
||||
LogDbSave: "DB-保存数据库",
|
||||
LogDbDelete: "DB-删除数据库",
|
||||
LogDbRunSql: "DB-运行SQL",
|
||||
LogDbDump: "DB-导出数据库",
|
||||
|
||||
SqlScriptRunFail: "sql脚本执行失败",
|
||||
SqlScriptRunSuccess: "sql脚本执行成功",
|
||||
SqlScripRunProgress: "sql脚本执行进度",
|
||||
DbDumpErr: "数据库导出失败",
|
||||
ErrDbNameExist: "该实例下数据库名已存在",
|
||||
ErrDbNotAccess: "未配置数据库【{{.dbName}}】的操作权限",
|
||||
|
||||
ErrExistRunFailSql: "存在执行错误的sql",
|
||||
ErrNeedSubmitWorkTicket: "该操作需要提交工单审批执行",
|
||||
ErrNoLimitStmt: "请完善分页信息后执行",
|
||||
ErrLimitInvalid: "查询结果集数需小于系统配置的{{.count}}条",
|
||||
|
||||
// db transfer
|
||||
LogDtsSave: "dts-保存数据迁移任务",
|
||||
LogDtsDelete: "dts-删除数据迁移任务",
|
||||
LogDtsChangeStatus: "dts-启停任务",
|
||||
LogDtsRun: "dts-执行数据迁移任务",
|
||||
LogDtsStop: "dts-终止数据迁移任务",
|
||||
LogDtsDeleteFile: "dts-删除迁移文件",
|
||||
LogDtsRunSqlFile: "dts-执行sql文件",
|
||||
|
||||
// data sync
|
||||
LogDataSyncSave: "datasync-保存数据同步任务",
|
||||
LogDataSyncDelete: "datasync-删除数据同步任务",
|
||||
LogDataSyncChangeStatus: "datasync-启停任务",
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package router
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/db/api"
|
||||
"mayfly-go/internal/db/imsg"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/ioc"
|
||||
"mayfly-go/pkg/req"
|
||||
@@ -24,9 +25,9 @@ func InitDbRouter(router *gin.RouterGroup) {
|
||||
// 获取数据库列表
|
||||
req.NewGet("", d.Dbs),
|
||||
|
||||
req.NewPost("", d.Save).Log(req.NewLogSave("db-保存数据库信息")),
|
||||
req.NewPost("", d.Save).Log(req.NewLogSaveI(imsg.LogDbSave)),
|
||||
|
||||
req.NewDelete(":dbId", d.DeleteDb).Log(req.NewLogSave("db-删除数据库信息")),
|
||||
req.NewDelete(":dbId", d.DeleteDb).Log(req.NewLogSaveI(imsg.LogDbDelete)),
|
||||
|
||||
req.NewGet(":dbId/t-create-ddl", d.GetTableDDL),
|
||||
|
||||
@@ -34,11 +35,11 @@ func InitDbRouter(router *gin.RouterGroup) {
|
||||
|
||||
req.NewGet(":dbId/pg/schemas", d.GetSchemas),
|
||||
|
||||
req.NewPost(":dbId/exec-sql", d.ExecSql).Log(req.NewLog("db-执行Sql")),
|
||||
req.NewPost(":dbId/exec-sql", d.ExecSql).Log(req.NewLogI(imsg.LogDbRunSql)),
|
||||
|
||||
req.NewPost(":dbId/exec-sql-file", d.ExecSqlFile).Log(req.NewLogSave("db-执行Sql文件")).RequiredPermissionCode("db:sqlscript:run"),
|
||||
req.NewPost(":dbId/exec-sql-file", d.ExecSqlFile).Log(req.NewLogSaveI(imsg.LogDbRunSqlFile)).RequiredPermissionCode("db:sqlscript:run"),
|
||||
|
||||
req.NewGet(":dbId/dump", d.DumpSql).Log(req.NewLogSave("db-导出sql文件")).NoRes(),
|
||||
req.NewGet(":dbId/dump", d.DumpSql).Log(req.NewLogSaveI(imsg.LogDbDump)).NoRes(),
|
||||
|
||||
req.NewGet(":dbId/t-infos", d.TableInfos),
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ package router
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/db/api"
|
||||
"mayfly-go/internal/db/imsg"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/ioc"
|
||||
"mayfly-go/pkg/req"
|
||||
@@ -22,16 +23,16 @@ func InitDbDataSyncRouter(router *gin.RouterGroup) {
|
||||
req.NewGet(":taskId/logs", d.Logs).RequiredPermissionCode("db:sync:log"),
|
||||
|
||||
// 保存任务 /datasync/save
|
||||
req.NewPost("save", d.SaveTask).Log(req.NewLogSave("datasync-保存数据同步任务信息")).RequiredPermissionCode("db:sync:save"),
|
||||
req.NewPost("save", d.SaveTask).Log(req.NewLogSaveI(imsg.LogDataSyncSave)).RequiredPermissionCode("db:sync:save"),
|
||||
|
||||
// 获取单个详情 /datasync/:taskId
|
||||
req.NewGet(":taskId", d.GetTask),
|
||||
|
||||
// 删除任务 /datasync/:taskId/del
|
||||
req.NewDelete(":taskId/del", d.DeleteTask).Log(req.NewLogSave("datasync-删除数据同步任务信息")).RequiredPermissionCode("db:sync:del"),
|
||||
req.NewDelete(":taskId/del", d.DeleteTask).Log(req.NewLogSaveI(imsg.LogDataSyncDelete)).RequiredPermissionCode("db:sync:del"),
|
||||
|
||||
// 启停用任务 /datasync/status
|
||||
req.NewPost(":taskId/status", d.ChangeStatus).Log(req.NewLogSave("datasync-启停任务")).RequiredPermissionCode("db:sync:status"),
|
||||
req.NewPost(":taskId/status", d.ChangeStatus).Log(req.NewLogSaveI(imsg.LogDataSyncChangeStatus)).RequiredPermissionCode("db:sync:status"),
|
||||
|
||||
// 立即执行任务 /datasync/run
|
||||
req.NewPost(":taskId/run", d.Run),
|
||||
|
||||
@@ -2,6 +2,7 @@ package router
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/db/api"
|
||||
"mayfly-go/internal/db/imsg"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/ioc"
|
||||
"mayfly-go/pkg/req"
|
||||
@@ -20,27 +21,27 @@ func InitDbTransferRouter(router *gin.RouterGroup) {
|
||||
req.NewGet("", d.Tasks),
|
||||
|
||||
// 保存任务 /datasync/save
|
||||
req.NewPost("save", d.SaveTask).Log(req.NewLogSave("dts-保存数据迁移任务信息")).RequiredPermissionCode("db:transfer:save"),
|
||||
req.NewPost("save", d.SaveTask).Log(req.NewLogSaveI(imsg.LogDtsSave)).RequiredPermissionCode("db:transfer:save"),
|
||||
|
||||
// 删除任务 /datasync/:taskId/del
|
||||
req.NewDelete(":taskId/del", d.DeleteTask).Log(req.NewLogSave("dts-删除数据迁移任务信息")).RequiredPermissionCode("db:transfer:del"),
|
||||
req.NewDelete(":taskId/del", d.DeleteTask).Log(req.NewLogSaveI(imsg.LogDtsDelete)).RequiredPermissionCode("db:transfer:del"),
|
||||
|
||||
// 启停用任务 /datasync/status
|
||||
req.NewPost(":taskId/status", d.ChangeStatus).Log(req.NewLogSave("dts-启停任务")).RequiredPermissionCode("db:transfer:status"),
|
||||
req.NewPost(":taskId/status", d.ChangeStatus).Log(req.NewLogSaveI(imsg.LogDtsChangeStatus)).RequiredPermissionCode("db:transfer:status"),
|
||||
|
||||
// 立即执行任务 /datasync/run
|
||||
req.NewPost(":taskId/run", d.Run).Log(req.NewLog("dts-执行数据迁移任务")).RequiredPermissionCode("db:transfer:run"),
|
||||
req.NewPost(":taskId/run", d.Run).Log(req.NewLogI(imsg.LogDtsRun)).RequiredPermissionCode("db:transfer:run"),
|
||||
|
||||
// 停止正在执行中的任务
|
||||
req.NewPost(":taskId/stop", d.Stop).Log(req.NewLogSave("dts-终止数据迁移任务")).RequiredPermissionCode("db:transfer:run"),
|
||||
req.NewPost(":taskId/stop", d.Stop).Log(req.NewLogSaveI(imsg.LogDtsStop)).RequiredPermissionCode("db:transfer:run"),
|
||||
|
||||
// 导出文件管理-列表
|
||||
req.NewGet("/files/:taskId", d.Files),
|
||||
|
||||
// 导出文件管理-删除
|
||||
req.NewPost("/files/del/:fileId", d.FileDel).Log(req.NewLogSave("dts-删除迁移文件")).RequiredPermissionCode("db:transfer:files:del"),
|
||||
req.NewPost("/files/del/:fileId", d.FileDel).Log(req.NewLogSaveI(imsg.LogDtsDeleteFile)).RequiredPermissionCode("db:transfer:files:del"),
|
||||
|
||||
req.NewPost("/files/run", d.FileRun).Log(req.NewLogSave("dts-执行sql文件")).RequiredPermissionCode("db:transfer:files:run"),
|
||||
req.NewPost("/files/run", d.FileRun).Log(req.NewLogSaveI(imsg.LogDtsRunSqlFile)).RequiredPermissionCode("db:transfer:files:run"),
|
||||
}
|
||||
|
||||
req.BatchSetGroup(instances, reqs[:])
|
||||
|
||||
@@ -2,6 +2,7 @@ package router
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/db/api"
|
||||
"mayfly-go/internal/db/imsg"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/ioc"
|
||||
"mayfly-go/pkg/req"
|
||||
@@ -21,7 +22,7 @@ func InitInstanceRouter(router *gin.RouterGroup) {
|
||||
|
||||
req.NewPost("/test-conn", d.TestConn),
|
||||
|
||||
req.NewPost("", d.SaveInstance).Log(req.NewLogSave("db-保存数据库实例信息")),
|
||||
req.NewPost("", d.SaveInstance).Log(req.NewLogSaveI(imsg.LogDbInstSave)),
|
||||
|
||||
req.NewGet(":instanceId", d.GetInstance),
|
||||
|
||||
@@ -33,7 +34,7 @@ func InitInstanceRouter(router *gin.RouterGroup) {
|
||||
|
||||
req.NewGet(":instanceId/server-info", d.GetDbServer),
|
||||
|
||||
req.NewDelete(":instanceId", d.DeleteInstance).Log(req.NewLogSave("db-删除数据库实例")),
|
||||
req.NewDelete(":instanceId", d.DeleteInstance).Log(req.NewLogSaveI(imsg.LogDbInstDelete)),
|
||||
}
|
||||
|
||||
req.BatchSetGroup(instances, reqs[:])
|
||||
|
||||
@@ -15,7 +15,7 @@ type File struct {
|
||||
|
||||
func (f *File) GetFileByKeys(rc *req.Ctx) {
|
||||
keysStr := rc.PathParam("keys")
|
||||
biz.NotEmpty(keysStr, "keys不能为空")
|
||||
biz.NotEmpty(keysStr, "keys cannot be empty")
|
||||
|
||||
var files []vo.SimpleFile
|
||||
err := f.FileApp.ListByCondToAny(model.NewCond().In("file_key", strings.Split(keysStr, ",")), &files)
|
||||
@@ -25,7 +25,7 @@ func (f *File) GetFileByKeys(rc *req.Ctx) {
|
||||
|
||||
func (f *File) GetFileContent(rc *req.Ctx) {
|
||||
key := rc.PathParam("key")
|
||||
biz.NotEmpty(key, "key不能为空")
|
||||
biz.NotEmpty(key, "key cannot be empty")
|
||||
|
||||
filename, reader, err := f.FileApp.GetReader(rc.MetaCtx, key)
|
||||
if err != nil {
|
||||
@@ -38,9 +38,9 @@ func (f *File) GetFileContent(rc *req.Ctx) {
|
||||
|
||||
func (f *File) Upload(rc *req.Ctx) {
|
||||
multipart, err := rc.GetRequest().MultipartReader()
|
||||
biz.ErrIsNilAppendErr(err, "读取文件失败: %s")
|
||||
biz.ErrIsNilAppendErr(err, "read file error: %s")
|
||||
file, err := multipart.NextPart()
|
||||
biz.ErrIsNilAppendErr(err, "读取文件失败: %s")
|
||||
biz.ErrIsNilAppendErr(err, "read file error: %s")
|
||||
defer file.Close()
|
||||
|
||||
fileKey, err := f.FileApp.Upload(rc.MetaCtx, rc.Query("fileKey"), file.FileName(), file)
|
||||
|
||||
@@ -114,7 +114,7 @@ func (f *fileAppImpl) NewWriter(ctx context.Context, canEmptyFileKey string, fil
|
||||
if e != nil {
|
||||
err := *e
|
||||
if err != nil {
|
||||
logx.Errorf("写入文件业务逻辑处理失败: %s", err.Error())
|
||||
logx.Errorf("the write file business logic failed: %s", err.Error())
|
||||
// 删除已经创建的文件
|
||||
f.remove(ctx, file)
|
||||
return err
|
||||
@@ -133,7 +133,7 @@ func (f *fileAppImpl) NewWriter(ctx context.Context, canEmptyFileKey string, fil
|
||||
func (f *fileAppImpl) GetReader(ctx context.Context, fileKey string) (string, io.ReadCloser, error) {
|
||||
file := &entity.File{FileKey: fileKey}
|
||||
if err := f.GetByCond(file); err != nil {
|
||||
return "", nil, errorx.NewBiz("文件不存在")
|
||||
return "", nil, errorx.NewBiz("file not found")
|
||||
}
|
||||
r, err := os.Open(filepath.Join(config.GetFileConfig().BasePath, file.Path))
|
||||
return file.Filename, r, err
|
||||
@@ -142,7 +142,7 @@ func (f *fileAppImpl) GetReader(ctx context.Context, fileKey string) (string, io
|
||||
func (f *fileAppImpl) Remove(ctx context.Context, fileKey string) error {
|
||||
file := &entity.File{FileKey: fileKey}
|
||||
if err := f.GetByCond(file); err != nil {
|
||||
return errorx.NewBiz("文件不存在")
|
||||
return errorx.NewBiz("file not found")
|
||||
}
|
||||
f.DeleteById(ctx, file.Id)
|
||||
return f.remove(ctx, file)
|
||||
@@ -172,7 +172,7 @@ func (f *fileAppImpl) newWriter(filename string) (string, io.WriteCloser, error)
|
||||
|
||||
func (f *fileAppImpl) remove(ctx context.Context, file *entity.File) error {
|
||||
if err := os.Remove(filepath.Join(config.GetFileConfig().BasePath, file.Path)); err != nil {
|
||||
logx.ErrorfContext(ctx, "删除旧文件[%s] 失败: %s", file.Path, err.Error())
|
||||
logx.ErrorfContext(ctx, "failed to delete old file [%s]: %s", file.Path, err.Error())
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -11,8 +11,9 @@ import (
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/req"
|
||||
"mayfly-go/pkg/utils/collx"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/may-fly/cast"
|
||||
)
|
||||
|
||||
type Procdef struct {
|
||||
@@ -55,8 +56,6 @@ func (p *Procdef) Delete(rc *req.Ctx) {
|
||||
ids := strings.Split(idsStr, ",")
|
||||
|
||||
for _, v := range ids {
|
||||
value, err := strconv.Atoi(v)
|
||||
biz.ErrIsNilAppendErr(err, "string类型转换为int异常: %s")
|
||||
biz.ErrIsNilAppendErr(p.ProcdefApp.DeleteProcdef(rc.MetaCtx, uint64(value)), "删除失败:%s")
|
||||
biz.ErrIsNilAppendErr(p.ProcdefApp.DeleteProcdef(rc.MetaCtx, cast.ToUint64(v)), "delete error: %s")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ func (p *Procinst) ProcinstCancel(rc *req.Ctx) {
|
||||
|
||||
func (p *Procinst) GetProcinstDetail(rc *req.Ctx) {
|
||||
pi, err := p.ProcinstApp.GetById(uint64(rc.PathParamInt("id")))
|
||||
biz.ErrIsNil(err, "流程实例不存在")
|
||||
biz.ErrIsNil(err, "procinst not found")
|
||||
pivo := new(vo.ProcinstVO)
|
||||
structx.Copy(pivo, pi)
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ func FlowBizHandle(ctx context.Context, bizHandleParam *BizHandleParam) (any, er
|
||||
flowBizType := bizHandleParam.Procinst.BizType
|
||||
if handler, ok := handlers[flowBizType]; !ok {
|
||||
logx.Warnf("flow biz handler not found: bizType=%s", flowBizType)
|
||||
return nil, errorx.NewBiz("业务处理器不存在")
|
||||
return nil, errorx.NewBiz("flow biz handler not found")
|
||||
} else {
|
||||
return handler.FlowBizHandle(ctx, bizHandleParam)
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"mayfly-go/internal/flow/application/dto"
|
||||
"mayfly-go/internal/flow/domain/entity"
|
||||
"mayfly-go/internal/flow/domain/repository"
|
||||
"mayfly-go/internal/flow/imsg"
|
||||
tagapp "mayfly-go/internal/tag/application"
|
||||
tagentity "mayfly-go/internal/tag/domain/entity"
|
||||
"mayfly-go/pkg/base"
|
||||
@@ -58,12 +59,12 @@ func (p *procdefAppImpl) SaveProcdef(ctx context.Context, defParam *dto.SaveProc
|
||||
|
||||
if def.Id == 0 {
|
||||
if p.GetByCond(&entity.Procdef{DefKey: def.DefKey}) == nil {
|
||||
return errorx.NewBiz("该流程实例key已存在")
|
||||
return errorx.NewBizI(ctx, imsg.ErrProcdefKeyExist)
|
||||
}
|
||||
} else {
|
||||
// 防止误修改key
|
||||
def.DefKey = ""
|
||||
if err := p.canModify(def.Id); err != nil {
|
||||
if err := p.canModify(ctx, def.Id); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -76,7 +77,7 @@ func (p *procdefAppImpl) SaveProcdef(ctx context.Context, defParam *dto.SaveProc
|
||||
}
|
||||
|
||||
func (p *procdefAppImpl) DeleteProcdef(ctx context.Context, defId uint64) error {
|
||||
if err := p.canModify(defId); err != nil {
|
||||
if err := p.canModify(ctx, defId); err != nil {
|
||||
return err
|
||||
}
|
||||
return p.DeleteById(ctx, defId)
|
||||
@@ -105,12 +106,12 @@ func (p *procdefAppImpl) GetProcdefByResource(ctx context.Context, resourceType
|
||||
}
|
||||
|
||||
// 判断该流程实例是否可以执行修改操作
|
||||
func (p *procdefAppImpl) canModify(prodefId uint64) error {
|
||||
func (p *procdefAppImpl) canModify(ctx context.Context, prodefId uint64) error {
|
||||
if activeInstCount := p.procinstApp.CountByCond(&entity.Procinst{ProcdefId: prodefId, Status: entity.ProcinstStatusActive}); activeInstCount > 0 {
|
||||
return errorx.NewBiz("存在运行中的流程实例,无法操作")
|
||||
return errorx.NewBizI(ctx, imsg.ErrExistProcinstRunning)
|
||||
}
|
||||
if suspInstCount := p.procinstApp.CountByCond(&entity.Procinst{ProcdefId: prodefId, Status: entity.ProcinstStatusSuspended}); suspInstCount > 0 {
|
||||
return errorx.NewBiz("存在挂起中的流程实例,无法操作")
|
||||
return errorx.NewBizI(ctx, imsg.ErrExistProcinstSuspended)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -6,9 +6,11 @@ import (
|
||||
"mayfly-go/internal/flow/application/dto"
|
||||
"mayfly-go/internal/flow/domain/entity"
|
||||
"mayfly-go/internal/flow/domain/repository"
|
||||
"mayfly-go/internal/flow/imsg"
|
||||
"mayfly-go/pkg/base"
|
||||
"mayfly-go/pkg/contextx"
|
||||
"mayfly-go/pkg/errorx"
|
||||
"mayfly-go/pkg/i18n"
|
||||
"mayfly-go/pkg/logx"
|
||||
"mayfly-go/pkg/model"
|
||||
"mayfly-go/pkg/utils/anyx"
|
||||
@@ -65,11 +67,11 @@ func (p *procinstAppImpl) GetProcinstTasks(condition *entity.ProcinstTaskQuery,
|
||||
func (p *procinstAppImpl) StartProc(ctx context.Context, procdefId uint64, reqParam *dto.StarProc) (*entity.Procinst, error) {
|
||||
procdef, err := p.procdefApp.GetById(procdefId)
|
||||
if err != nil {
|
||||
return nil, errorx.NewBiz("流程实例[%d]不存在", procdefId)
|
||||
return nil, errorx.NewBiz("procdef not found")
|
||||
}
|
||||
|
||||
if procdef.Status != entity.ProcdefStatusEnable {
|
||||
return nil, errorx.NewBiz("该流程定义非启用状态")
|
||||
return nil, errorx.NewBizI(ctx, imsg.ErrProcdefNotEnable)
|
||||
}
|
||||
|
||||
bizKey := reqParam.BizKey
|
||||
@@ -99,22 +101,22 @@ func (p *procinstAppImpl) StartProc(ctx context.Context, procdefId uint64, reqPa
|
||||
func (p *procinstAppImpl) CancelProc(ctx context.Context, procinstId uint64) error {
|
||||
procinst, err := p.GetById(procinstId)
|
||||
if err != nil {
|
||||
return errorx.NewBiz("流程不存在")
|
||||
return errorx.NewBiz("procinst not found")
|
||||
}
|
||||
|
||||
la := contextx.GetLoginAccount(ctx)
|
||||
if la == nil {
|
||||
return errorx.NewBiz("未登录")
|
||||
return errorx.NewBiz("no login")
|
||||
}
|
||||
if procinst.CreatorId != la.Id {
|
||||
return errorx.NewBiz("只能取消自己创建的流程")
|
||||
return errorx.NewBizI(ctx, imsg.ErrProcinstCancelSelf)
|
||||
}
|
||||
procinst.Status = entity.ProcinstStatusCancelled
|
||||
procinst.BizStatus = entity.ProcinstBizStatusNo
|
||||
procinst.SetEnd()
|
||||
|
||||
return p.Tx(ctx, func(ctx context.Context) error {
|
||||
return p.cancelInstTasks(ctx, procinstId, "流程已取消")
|
||||
return p.cancelInstTasks(ctx, procinstId, i18n.T(imsg.ErrProcinstCancelled))
|
||||
}, func(ctx context.Context) error {
|
||||
return p.Save(ctx, procinst)
|
||||
}, func(ctx context.Context) error {
|
||||
@@ -239,13 +241,13 @@ func (p *procinstAppImpl) triggerProcinstStatusChangeEvent(ctx context.Context,
|
||||
if procinst.Status != entity.ProcinstStatusCompleted {
|
||||
procinst.Status = entity.ProcinstStatusTerminated
|
||||
procinst.SetEnd()
|
||||
p.cancelInstTasks(ctx, procinst.Id, "业务处理失败")
|
||||
p.cancelInstTasks(ctx, procinst.Id, i18n.T(imsg.ErrBizHandlerFail))
|
||||
}
|
||||
procinst.BizStatus = entity.ProcinstBizStatusFail
|
||||
if procinst.BizHandleRes == "" {
|
||||
procinst.BizHandleRes = err.Error()
|
||||
} else {
|
||||
logx.Errorf("流程业务[%s]处理失败: %v", procinst.BizKey, err.Error())
|
||||
logx.Errorf("process business [%s] processing failed: %v", procinst.BizKey, err.Error())
|
||||
}
|
||||
return p.UpdateById(ctx, procinst)
|
||||
}
|
||||
@@ -265,12 +267,12 @@ func (p *procinstAppImpl) triggerProcinstStatusChangeEvent(ctx context.Context,
|
||||
func (p *procinstAppImpl) getAndValidInstTask(ctx context.Context, instTaskId uint64) (*entity.ProcinstTask, error) {
|
||||
instTask, err := p.procinstTaskRepo.GetById(instTaskId)
|
||||
if err != nil {
|
||||
return nil, errorx.NewBiz("流程实例任务不存在")
|
||||
return nil, errorx.NewBiz("procinst not found")
|
||||
}
|
||||
|
||||
la := contextx.GetLoginAccount(ctx)
|
||||
if instTask.Assignee != fmt.Sprintf("%d", la.Id) {
|
||||
return nil, errorx.NewBiz("当前用户不是任务处理人,无法完成任务")
|
||||
return nil, errorx.NewBiz("the current user is not a task handler and cannot complete the task")
|
||||
}
|
||||
|
||||
return instTask, nil
|
||||
|
||||
24
server/internal/flow/imsg/en.go
Normal file
24
server/internal/flow/imsg/en.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package imsg
|
||||
|
||||
import "mayfly-go/pkg/i18n"
|
||||
|
||||
var En = map[i18n.MsgId]string{
|
||||
LogProcdefSave: "ProcDef - Save",
|
||||
LogProcdefDelete: "ProcDef - Delete",
|
||||
|
||||
ErrProcdefKeyExist: "the process instance key already exists",
|
||||
ErrExistProcinstRunning: "There is a running process instance that cannot be manipulated",
|
||||
ErrExistProcinstSuspended: "There is a pending process instance that cannot be manipulated",
|
||||
|
||||
// procinst
|
||||
LogProcinstStart: "Process - Start",
|
||||
LogProcinstCancel: "Process - Cancel",
|
||||
LogCompleteTask: "Process - Completion of task",
|
||||
LogRejectTask: "Process - Task rejection",
|
||||
LogBackTask: "Process - Task rejection",
|
||||
|
||||
ErrProcdefNotEnable: "The process defines a non-enabled state",
|
||||
ErrProcinstCancelSelf: "You can only cancel processes you initiated",
|
||||
ErrProcinstCancelled: "Process has been cancelled",
|
||||
ErrBizHandlerFail: "Business process failure",
|
||||
}
|
||||
32
server/internal/flow/imsg/imsg.go
Normal file
32
server/internal/flow/imsg/imsg.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package imsg
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/common/consts"
|
||||
"mayfly-go/pkg/i18n"
|
||||
)
|
||||
|
||||
func init() {
|
||||
i18n.AppendLangMsg(i18n.Zh_CN, Zh_CN)
|
||||
i18n.AppendLangMsg(i18n.En, En)
|
||||
}
|
||||
|
||||
const (
|
||||
LogProcdefSave = iota + consts.ImsgNumFlow
|
||||
LogProcdefDelete
|
||||
|
||||
ErrProcdefKeyExist
|
||||
ErrExistProcinstRunning
|
||||
ErrExistProcinstSuspended
|
||||
|
||||
// procinst
|
||||
LogProcinstStart
|
||||
LogProcinstCancel
|
||||
LogCompleteTask
|
||||
LogRejectTask
|
||||
LogBackTask
|
||||
|
||||
ErrProcdefNotEnable
|
||||
ErrProcinstCancelSelf
|
||||
ErrProcinstCancelled
|
||||
ErrBizHandlerFail
|
||||
)
|
||||
24
server/internal/flow/imsg/zh_cn.go
Normal file
24
server/internal/flow/imsg/zh_cn.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package imsg
|
||||
|
||||
import "mayfly-go/pkg/i18n"
|
||||
|
||||
var Zh_CN = map[i18n.MsgId]string{
|
||||
LogProcdefSave: "流程定义-保存",
|
||||
LogProcdefDelete: "流程定义-删除",
|
||||
|
||||
ErrProcdefKeyExist: "该流程实例key已存在",
|
||||
ErrExistProcinstRunning: "存在运行中的流程实例,无法操作",
|
||||
ErrExistProcinstSuspended: "存在挂起中的流程实例,无法操作",
|
||||
|
||||
// procinst
|
||||
LogProcinstStart: "流程-启动",
|
||||
LogProcinstCancel: "流程-取消",
|
||||
LogCompleteTask: "流程-任务完成",
|
||||
LogRejectTask: "流程-任务拒绝",
|
||||
LogBackTask: "流程-任务驳回",
|
||||
|
||||
ErrProcdefNotEnable: "该流程定义非启用状态",
|
||||
ErrProcinstCancelSelf: "只能取消自己发起的流程",
|
||||
ErrProcinstCancelled: "流程已取消",
|
||||
ErrBizHandlerFail: "业务处理失败",
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package router
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/flow/api"
|
||||
"mayfly-go/internal/flow/imsg"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/ioc"
|
||||
"mayfly-go/pkg/req"
|
||||
@@ -20,9 +21,9 @@ func InitProcdefouter(router *gin.RouterGroup) {
|
||||
|
||||
req.NewGet("/:resourceType/:resourceCode", p.GetProcdef),
|
||||
|
||||
req.NewPost("", p.Save).Log(req.NewLogSave("流程定义-保存")).RequiredPermissionCode("flow:procdef:save"),
|
||||
req.NewPost("", p.Save).Log(req.NewLogSaveI(imsg.LogProcdefSave)).RequiredPermissionCode("flow:procdef:save"),
|
||||
|
||||
req.NewDelete(":id", p.Delete).Log(req.NewLogSave("流程定义-删除")).RequiredPermissionCode("flow:procdef:del"),
|
||||
req.NewDelete(":id", p.Delete).Log(req.NewLogSaveI(imsg.LogProcdefDelete)).RequiredPermissionCode("flow:procdef:del"),
|
||||
}
|
||||
|
||||
req.BatchSetGroup(reqGroup, reqs[:])
|
||||
|
||||
@@ -2,6 +2,7 @@ package router
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/flow/api"
|
||||
"mayfly-go/internal/flow/imsg"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/ioc"
|
||||
"mayfly-go/pkg/req"
|
||||
@@ -20,17 +21,17 @@ func InitProcinstRouter(router *gin.RouterGroup) {
|
||||
|
||||
req.NewGet("/:id", p.GetProcinstDetail),
|
||||
|
||||
req.NewPost("/start", p.ProcinstStart).Log(req.NewLogSave("流程-启动")),
|
||||
req.NewPost("/start", p.ProcinstStart).Log(req.NewLogSaveI(imsg.LogProcinstStart)),
|
||||
|
||||
req.NewPost("/:id/cancel", p.ProcinstCancel).Log(req.NewLogSave("流程-取消")),
|
||||
req.NewPost("/:id/cancel", p.ProcinstCancel).Log(req.NewLogSaveI(imsg.LogProcinstCancel)),
|
||||
|
||||
req.NewGet("/tasks", p.GetTasks),
|
||||
|
||||
req.NewPost("/tasks/complete", p.CompleteTask).Log(req.NewLogSave("流程-任务完成")),
|
||||
req.NewPost("/tasks/complete", p.CompleteTask).Log(req.NewLogSaveI(imsg.LogCompleteTask)),
|
||||
|
||||
req.NewPost("/tasks/reject", p.RejectTask).Log(req.NewLogSave("流程-任务拒绝")),
|
||||
req.NewPost("/tasks/reject", p.RejectTask).Log(req.NewLogSaveI(imsg.LogRejectTask)),
|
||||
|
||||
req.NewPost("/tasks/back", p.BackTask).Log(req.NewLogSave("流程-任务驳回")),
|
||||
req.NewPost("/tasks/back", p.BackTask).Log(req.NewLogSaveI(imsg.LogBackTask)),
|
||||
}
|
||||
|
||||
req.BatchSetGroup(reqGroup, reqs[:])
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"mayfly-go/internal/machine/application/dto"
|
||||
"mayfly-go/internal/machine/domain/entity"
|
||||
"mayfly-go/internal/machine/guac"
|
||||
"mayfly-go/internal/machine/imsg"
|
||||
"mayfly-go/internal/machine/mcm"
|
||||
tagapp "mayfly-go/internal/tag/application"
|
||||
tagentity "mayfly-go/internal/tag/domain/entity"
|
||||
@@ -85,7 +86,7 @@ func (m *Machine) Machines(rc *req.Ctx) {
|
||||
|
||||
func (m *Machine) SimpleMachieInfo(rc *req.Ctx) {
|
||||
machineCodesStr := rc.Query("codes")
|
||||
biz.NotEmpty(machineCodesStr, "codes不能为空")
|
||||
biz.NotEmpty(machineCodesStr, "codes cannot be empty")
|
||||
|
||||
var vos []vo.SimpleMachineVO
|
||||
m.MachineApp.ListByCondToAny(model.NewCond().In("code", strings.Split(machineCodesStr, ",")), &vos)
|
||||
@@ -94,7 +95,7 @@ func (m *Machine) SimpleMachieInfo(rc *req.Ctx) {
|
||||
|
||||
func (m *Machine) MachineStats(rc *req.Ctx) {
|
||||
cli, err := m.MachineApp.GetCli(GetMachineId(rc))
|
||||
biz.ErrIsNilAppendErr(err, "获取客户端连接失败: %s")
|
||||
biz.ErrIsNilAppendErr(err, "connection error: %s")
|
||||
rc.ResData = cli.GetAllStats()
|
||||
}
|
||||
|
||||
@@ -116,7 +117,7 @@ func (m *Machine) TestConn(rc *req.Ctx) {
|
||||
machineForm := new(form.MachineForm)
|
||||
me := req.BindJsonAndCopyTo(rc, machineForm, new(entity.Machine))
|
||||
// 测试连接
|
||||
biz.ErrIsNilAppendErr(m.MachineApp.TestConn(me, machineForm.AuthCerts[0]), "该机器无法连接: %s")
|
||||
biz.ErrIsNilAppendErr(m.MachineApp.TestConn(me, machineForm.AuthCerts[0]), "connection error: %s")
|
||||
}
|
||||
|
||||
func (m *Machine) ChangeStatus(rc *req.Ctx) {
|
||||
@@ -132,9 +133,7 @@ func (m *Machine) DeleteMachine(rc *req.Ctx) {
|
||||
ids := strings.Split(idsStr, ",")
|
||||
|
||||
for _, v := range ids {
|
||||
value, err := strconv.Atoi(v)
|
||||
biz.ErrIsNilAppendErr(err, "string类型转换为int异常: %s")
|
||||
m.MachineApp.Delete(rc.MetaCtx, uint64(value))
|
||||
m.MachineApp.Delete(rc.MetaCtx, cast.ToUint64(v))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,30 +156,30 @@ func (m *Machine) GetProcess(rc *req.Ctx) {
|
||||
cmd += "| head -n " + fmt.Sprintf("%d", count)
|
||||
|
||||
cli, err := m.MachineApp.GetCli(GetMachineId(rc))
|
||||
biz.ErrIsNilAppendErr(err, "获取客户端连接失败: %s")
|
||||
biz.ErrIsNilAppendErr(err, "connection error: %s")
|
||||
biz.ErrIsNilAppendErr(m.TagApp.CanAccess(rc.GetLoginAccount().Id, cli.Info.CodePath...), "%s")
|
||||
|
||||
res, err := cli.Run(cmd)
|
||||
biz.ErrIsNilAppendErr(err, "获取进程信息失败: %s")
|
||||
biz.ErrIsNil(err)
|
||||
rc.ResData = res
|
||||
}
|
||||
|
||||
// 终止进程
|
||||
func (m *Machine) KillProcess(rc *req.Ctx) {
|
||||
pid := rc.Query("pid")
|
||||
biz.NotEmpty(pid, "进程id不能为空")
|
||||
biz.NotEmpty(pid, "pid cannot be empty")
|
||||
|
||||
cli, err := m.MachineApp.GetCli(GetMachineId(rc))
|
||||
biz.ErrIsNilAppendErr(err, "获取客户端连接失败: %s")
|
||||
biz.ErrIsNilAppendErr(err, "connection error: %s")
|
||||
biz.ErrIsNilAppendErr(m.TagApp.CanAccess(rc.GetLoginAccount().Id, cli.Info.CodePath...), "%s")
|
||||
|
||||
res, err := cli.Run("sudo kill -9 " + pid)
|
||||
biz.ErrIsNil(err, "终止进程失败: %s", res)
|
||||
biz.ErrIsNil(err, "kill fail: %s", res)
|
||||
}
|
||||
|
||||
func (m *Machine) GetUsers(rc *req.Ctx) {
|
||||
cli, err := m.MachineApp.GetCli(GetMachineId(rc))
|
||||
biz.ErrIsNilAppendErr(err, "获取客户端连接失败: %s")
|
||||
biz.ErrIsNilAppendErr(err, "connection error: %s")
|
||||
res, err := cli.GetUsers()
|
||||
biz.ErrIsNil(err)
|
||||
rc.ResData = res
|
||||
@@ -188,7 +187,7 @@ func (m *Machine) GetUsers(rc *req.Ctx) {
|
||||
|
||||
func (m *Machine) GetGroups(rc *req.Ctx) {
|
||||
cli, err := m.MachineApp.GetCli(GetMachineId(rc))
|
||||
biz.ErrIsNilAppendErr(err, "获取客户端连接失败: %s")
|
||||
biz.ErrIsNilAppendErr(err, "connection error: %s")
|
||||
res, err := cli.GetGroups()
|
||||
biz.ErrIsNil(err)
|
||||
rc.ResData = res
|
||||
@@ -204,17 +203,17 @@ func (m *Machine) WsSSH(g *gin.Context) {
|
||||
wsConn.Close()
|
||||
}
|
||||
}()
|
||||
biz.ErrIsNilAppendErr(err, "升级websocket失败: %s")
|
||||
biz.ErrIsNilAppendErr(err, "Upgrade websocket fail: %s")
|
||||
wsConn.WriteMessage(websocket.TextMessage, []byte("Connecting to host..."))
|
||||
|
||||
// 权限校验
|
||||
rc := req.NewCtxWithGin(g).WithRequiredPermission(req.NewPermission("machine:terminal"))
|
||||
if err = req.PermissionHandler(rc); err != nil {
|
||||
panic(errorx.NewBiz(mcm.GetErrorContentRn("您没有权限操作该机器终端,请重新登录后再试~")))
|
||||
panic(errorx.NewBiz(mcm.GetErrorContentRn("You do not have permission to operate the machine terminal, please log in again and try again ~")))
|
||||
}
|
||||
|
||||
cli, err := m.MachineApp.NewCli(GetMachineAc(rc))
|
||||
biz.ErrIsNilAppendErr(err, mcm.GetErrorContentRn("获取客户端连接失败: %s"))
|
||||
biz.ErrIsNilAppendErr(err, mcm.GetErrorContentRn("connection error: %s"))
|
||||
defer cli.Close()
|
||||
biz.ErrIsNilAppendErr(m.TagApp.CanAccess(rc.GetLoginAccount().Id, cli.Info.CodePath...), mcm.GetErrorContentRn("%s"))
|
||||
|
||||
@@ -224,12 +223,12 @@ func (m *Machine) WsSSH(g *gin.Context) {
|
||||
rows := rc.QueryIntDefault("rows", 32)
|
||||
|
||||
// 记录系统操作日志
|
||||
rc.WithLog(req.NewLogSave("机器-终端操作"))
|
||||
rc.WithLog(req.NewLogSaveI(imsg.LogMachineTerminalOp))
|
||||
rc.ReqParam = cli.Info
|
||||
req.LogHandler(rc)
|
||||
|
||||
err = m.MachineTermOpApp.TermConn(rc.MetaCtx, cli, wsConn, rows, cols)
|
||||
biz.ErrIsNilAppendErr(err, mcm.GetErrorContentRn("连接失败: %s"))
|
||||
biz.ErrIsNilAppendErr(err, mcm.GetErrorContentRn("connect fail: %s"))
|
||||
}
|
||||
|
||||
func (m *Machine) MachineTermOpRecords(rc *req.Ctx) {
|
||||
@@ -263,7 +262,7 @@ func (m *Machine) WsGuacamole(g *gin.Context) {
|
||||
|
||||
rc := req.NewCtxWithGin(g).WithRequiredPermission(req.NewPermission("machine:terminal"))
|
||||
if err = req.PermissionHandler(rc); err != nil {
|
||||
panic(errorx.NewBiz(mcm.GetErrorContentRn("您没有权限操作该机器终端,请重新登录后再试~")))
|
||||
panic(errorx.NewBiz(mcm.GetErrorContentRn("You do not have permission to operate the machine terminal, please log in again and try again ~")))
|
||||
}
|
||||
|
||||
ac := GetMachineAc(rc)
|
||||
@@ -344,12 +343,12 @@ func (m *Machine) WsGuacamole(g *gin.Context) {
|
||||
|
||||
func GetMachineId(rc *req.Ctx) uint64 {
|
||||
machineId, _ := strconv.Atoi(rc.PathParam("machineId"))
|
||||
biz.IsTrue(machineId != 0, "machineId错误")
|
||||
biz.IsTrue(machineId != 0, "machineId error")
|
||||
return uint64(machineId)
|
||||
}
|
||||
|
||||
func GetMachineAc(rc *req.Ctx) string {
|
||||
ac := rc.PathParam("ac")
|
||||
biz.IsTrue(ac != "", "authCertName错误")
|
||||
biz.IsTrue(ac != "", "authCertName error")
|
||||
return ac
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ func (m *MachineCronJob) Delete(rc *req.Ctx) {
|
||||
|
||||
func (m *MachineCronJob) RunCronJob(rc *req.Ctx) {
|
||||
cronJobKey := rc.PathParam("key")
|
||||
biz.NotEmpty(cronJobKey, "cronJob key不能为空")
|
||||
biz.NotEmpty(cronJobKey, "cronJob key cannot be empty")
|
||||
m.MachineCronJobApp.RunCronJob(cronJobKey)
|
||||
}
|
||||
|
||||
|
||||
@@ -10,11 +10,13 @@ import (
|
||||
"mayfly-go/internal/machine/application/dto"
|
||||
"mayfly-go/internal/machine/config"
|
||||
"mayfly-go/internal/machine/domain/entity"
|
||||
"mayfly-go/internal/machine/imsg"
|
||||
"mayfly-go/internal/machine/mcm"
|
||||
msgapp "mayfly-go/internal/msg/application"
|
||||
msgdto "mayfly-go/internal/msg/application/dto"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/errorx"
|
||||
"mayfly-go/pkg/i18n"
|
||||
"mayfly-go/pkg/logx"
|
||||
"mayfly-go/pkg/req"
|
||||
"mayfly-go/pkg/utils/anyx"
|
||||
@@ -73,43 +75,45 @@ func (m *MachineFile) CreateFile(rc *req.Ctx) {
|
||||
var mi *mcm.MachineInfo
|
||||
var err error
|
||||
if opForm.Type == dir {
|
||||
attrs["type"] = "目录"
|
||||
attrs["type"] = "Folder"
|
||||
mi, err = m.MachineFileApp.MkDir(rc.MetaCtx, opForm.MachineFileOp)
|
||||
} else {
|
||||
attrs["type"] = "文件"
|
||||
attrs["type"] = "File"
|
||||
mi, err = m.MachineFileApp.CreateFile(rc.MetaCtx, opForm.MachineFileOp)
|
||||
}
|
||||
attrs["machine"] = mi
|
||||
rc.ReqParam = attrs
|
||||
biz.ErrIsNilAppendErr(err, "创建目录失败: %s")
|
||||
biz.ErrIsNil(err)
|
||||
}
|
||||
|
||||
func (m *MachineFile) ReadFileContent(rc *req.Ctx) {
|
||||
opForm := req.BindQuery(rc, new(dto.MachineFileOp))
|
||||
readPath := opForm.Path
|
||||
ctx := rc.MetaCtx
|
||||
|
||||
// 特殊处理rdp文件
|
||||
if opForm.Protocol == entity.MachineProtocolRdp {
|
||||
path := m.MachineFileApp.GetRdpFilePath(rc.GetLoginAccount(), opForm.Path)
|
||||
fi, err := os.Stat(path)
|
||||
biz.ErrIsNilAppendErr(err, "读取文件内容失败: %s")
|
||||
biz.IsTrue(fi.Size() < max_read_size, "文件超过1m,请使用下载查看")
|
||||
biz.ErrIsNil(err)
|
||||
biz.IsTrueI(ctx, fi.Size() < max_read_size, imsg.ErrFileTooLargeUseDownload)
|
||||
datas, err := os.ReadFile(path)
|
||||
biz.ErrIsNilAppendErr(err, "读取文件内容失败: %s")
|
||||
biz.ErrIsNil(err)
|
||||
rc.ResData = string(datas)
|
||||
return
|
||||
}
|
||||
|
||||
sftpFile, mi, err := m.MachineFileApp.ReadFile(rc.MetaCtx, opForm)
|
||||
rc.ReqParam = collx.Kvs("machine", mi, "path", readPath)
|
||||
biz.ErrIsNilAppendErr(err, "打开文件失败: %s")
|
||||
biz.ErrIsNil(err)
|
||||
defer sftpFile.Close()
|
||||
|
||||
fileInfo, _ := sftpFile.Stat()
|
||||
filesize := fileInfo.Size()
|
||||
biz.IsTrueI(ctx, filesize < max_read_size, imsg.ErrFileTooLargeUseDownload)
|
||||
|
||||
biz.IsTrue(filesize < max_read_size, "文件超过1m,请使用下载查看")
|
||||
datas, err := io.ReadAll(sftpFile)
|
||||
biz.ErrIsNilAppendErr(err, "读取文件内容失败: %s")
|
||||
biz.ErrIsNil(err)
|
||||
|
||||
rc.ResData = string(datas)
|
||||
}
|
||||
@@ -136,7 +140,7 @@ func (m *MachineFile) DownloadFile(rc *req.Ctx) {
|
||||
|
||||
sftpFile, mi, err := m.MachineFileApp.ReadFile(rc.MetaCtx, opForm)
|
||||
rc.ReqParam = collx.Kvs("machine", mi, "path", readPath)
|
||||
biz.ErrIsNilAppendErr(err, "打开文件失败: %s")
|
||||
biz.ErrIsNilAppendErr(err, "open file error: %s")
|
||||
defer sftpFile.Close()
|
||||
|
||||
rc.Download(sftpFile, fileName)
|
||||
@@ -148,7 +152,7 @@ func (m *MachineFile) GetDirEntry(rc *req.Ctx) {
|
||||
rc.ReqParam = fmt.Sprintf("path: %s", readPath)
|
||||
|
||||
fis, err := m.MachineFileApp.ReadDir(rc.MetaCtx, opForm)
|
||||
biz.ErrIsNilAppendErr(err, "读取目录失败: %s")
|
||||
biz.ErrIsNilAppendErr(err, "read dir error: %s")
|
||||
|
||||
fisVO := make([]vo.MachineFileInfo, 0)
|
||||
for _, fi := range fis {
|
||||
@@ -202,7 +206,7 @@ func (m *MachineFile) WriteFileContent(rc *req.Ctx) {
|
||||
|
||||
mi, err := m.MachineFileApp.WriteFileContent(rc.MetaCtx, opForm.MachineFileOp, []byte(opForm.Content))
|
||||
rc.ReqParam = collx.Kvs("machine", mi, "path", path)
|
||||
biz.ErrIsNilAppendErr(err, "打开文件失败: %s")
|
||||
biz.ErrIsNilAppendErr(err, "open file error: %s")
|
||||
}
|
||||
|
||||
func (m *MachineFile) UploadFile(rc *req.Ctx) {
|
||||
@@ -212,10 +216,12 @@ func (m *MachineFile) UploadFile(rc *req.Ctx) {
|
||||
authCertName := rc.PostForm("authCertName")
|
||||
|
||||
fileheader, err := rc.FormFile("file")
|
||||
biz.ErrIsNilAppendErr(err, "读取文件失败: %s")
|
||||
biz.ErrIsNilAppendErr(err, "read form file error: %s")
|
||||
|
||||
ctx := rc.MetaCtx
|
||||
|
||||
maxUploadFileSize := config.GetMachine().UploadMaxFileSize
|
||||
biz.IsTrue(fileheader.Size <= maxUploadFileSize, "文件大小不能超过%d字节", maxUploadFileSize)
|
||||
biz.IsTrueI(ctx, fileheader.Size <= maxUploadFileSize, imsg.ErrUploadFileOutOfLimit, "size", maxUploadFileSize)
|
||||
|
||||
file, _ := fileheader.Open()
|
||||
defer file.Close()
|
||||
@@ -223,8 +229,8 @@ func (m *MachineFile) UploadFile(rc *req.Ctx) {
|
||||
la := rc.GetLoginAccount()
|
||||
defer func() {
|
||||
if anyx.ToString(recover()) != "" {
|
||||
logx.Errorf("文件上传失败: %s", err)
|
||||
m.MsgApp.CreateAndSend(la, msgdto.ErrSysMsg("文件上传失败", fmt.Sprintf("执行文件上传失败:\n<-e : %s", err)))
|
||||
logx.Errorf("upload file error: %s", err)
|
||||
m.MsgApp.CreateAndSend(la, msgdto.ErrSysMsg(i18n.TC(ctx, imsg.ErrFileUploadFail), fmt.Sprintf("%s: \n<-e : %s", i18n.TC(ctx, imsg.ErrFileUploadFail), err)))
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -235,11 +241,11 @@ func (m *MachineFile) UploadFile(rc *req.Ctx) {
|
||||
Path: path,
|
||||
}
|
||||
|
||||
mi, err := m.MachineFileApp.UploadFile(rc.MetaCtx, opForm, fileheader.Filename, file)
|
||||
mi, err := m.MachineFileApp.UploadFile(ctx, opForm, fileheader.Filename, file)
|
||||
rc.ReqParam = collx.Kvs("machine", mi, "path", fmt.Sprintf("%s/%s", path, fileheader.Filename))
|
||||
biz.ErrIsNilAppendErr(err, "创建文件失败: %s")
|
||||
biz.ErrIsNilAppendErr(err, "upload file error: %s")
|
||||
// 保存消息并发送文件上传成功通知
|
||||
m.MsgApp.CreateAndSend(la, msgdto.SuccessSysMsg("文件上传成功", fmt.Sprintf("[%s]文件已成功上传至 %s[%s:%s]", fileheader.Filename, mi.Name, mi.Ip, path)))
|
||||
m.MsgApp.CreateAndSend(la, msgdto.SuccessSysMsg(i18n.TC(ctx, imsg.MsgUploadFileSuccess), fmt.Sprintf("[%s] -> %s[%s:%s]", fileheader.Filename, mi.Name, mi.Ip, path)))
|
||||
}
|
||||
|
||||
type FolderFile struct {
|
||||
@@ -249,18 +255,19 @@ type FolderFile struct {
|
||||
|
||||
func (m *MachineFile) UploadFolder(rc *req.Ctx) {
|
||||
mf, err := rc.MultipartForm()
|
||||
biz.ErrIsNilAppendErr(err, "获取表单信息失败: %s")
|
||||
biz.ErrIsNilAppendErr(err, "get multipart form error: %s")
|
||||
basePath := mf.Value["basePath"][0]
|
||||
biz.NotEmpty(basePath, "基础路径不能为空")
|
||||
biz.NotEmpty(basePath, "basePath cannot be empty")
|
||||
|
||||
fileheaders := mf.File["files"]
|
||||
biz.IsTrue(len(fileheaders) > 0, "文件不能为空")
|
||||
biz.IsTrue(len(fileheaders) > 0, "files cannot be empty")
|
||||
allFileSize := collx.ArrayReduce(fileheaders, 0, func(i int64, fh *multipart.FileHeader) int64 {
|
||||
return i + fh.Size
|
||||
})
|
||||
|
||||
ctx := rc.MetaCtx
|
||||
maxUploadFileSize := config.GetMachine().UploadMaxFileSize
|
||||
biz.IsTrue(allFileSize <= maxUploadFileSize, "文件夹总大小不能超过%d字节", maxUploadFileSize)
|
||||
biz.IsTrueI(ctx, allFileSize <= maxUploadFileSize, imsg.ErrUploadFileOutOfLimit, "size", maxUploadFileSize)
|
||||
|
||||
paths := mf.Value["paths"]
|
||||
authCertName := mf.Value["authCertName"][0]
|
||||
@@ -275,7 +282,7 @@ func (m *MachineFile) UploadFolder(rc *req.Ctx) {
|
||||
}
|
||||
|
||||
if protocol == entity.MachineProtocolRdp {
|
||||
m.MachineFileApp.UploadFiles(rc.MetaCtx, opForm, basePath, fileheaders, paths)
|
||||
m.MachineFileApp.UploadFiles(ctx, opForm, basePath, fileheaders, paths)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -295,7 +302,7 @@ func (m *MachineFile) UploadFolder(rc *req.Ctx) {
|
||||
dir := filepath.Dir(path)
|
||||
// 目录已建,则无需重复建
|
||||
if !mkdirs[dir] {
|
||||
biz.ErrIsNilAppendErr(sftpCli.MkdirAll(basePath+"/"+dir), "创建目录失败: %s")
|
||||
biz.ErrIsNilAppendErr(sftpCli.MkdirAll(basePath+"/"+dir), "create dir error: %s")
|
||||
mkdirs[dir] = true
|
||||
}
|
||||
folderFiles[i] = FolderFile{
|
||||
@@ -321,10 +328,10 @@ func (m *MachineFile) UploadFolder(rc *req.Ctx) {
|
||||
wg.Done()
|
||||
if err := recover(); err != nil {
|
||||
isSuccess = false
|
||||
logx.Errorf("文件上传失败: %s", err)
|
||||
logx.Errorf("upload file error: %s", err)
|
||||
switch t := err.(type) {
|
||||
case *errorx.BizError:
|
||||
m.MsgApp.CreateAndSend(la, msgdto.ErrSysMsg("文件上传失败", fmt.Sprintf("执行文件上传失败:\n<-e errCode: %d, errMsg: %s", t.Code(), t.Error())))
|
||||
m.MsgApp.CreateAndSend(la, msgdto.ErrSysMsg(i18n.TC(ctx, imsg.ErrFileUploadFail), fmt.Sprintf("%s: \n<-e errCode: %d, errMsg: %s", i18n.TC(ctx, imsg.ErrFileUploadFail), t.Code(), t.Error())))
|
||||
}
|
||||
}
|
||||
}()
|
||||
@@ -335,10 +342,10 @@ func (m *MachineFile) UploadFolder(rc *req.Ctx) {
|
||||
file, _ := fileHeader.Open()
|
||||
defer file.Close()
|
||||
|
||||
logx.Debugf("上传文件夹: dir=%s -> filename=%s", dir, fileHeader.Filename)
|
||||
logx.Debugf("upload folder: dir=%s -> filename=%s", dir, fileHeader.Filename)
|
||||
|
||||
createfile, err := sftpCli.Create(fmt.Sprintf("%s/%s/%s", basePath, dir, fileHeader.Filename))
|
||||
biz.ErrIsNilAppendErr(err, "创建文件失败: %s")
|
||||
biz.ErrIsNilAppendErr(err, "create file error: %s")
|
||||
defer createfile.Close()
|
||||
io.Copy(createfile, file)
|
||||
}
|
||||
@@ -349,7 +356,7 @@ func (m *MachineFile) UploadFolder(rc *req.Ctx) {
|
||||
wg.Wait()
|
||||
if isSuccess {
|
||||
// 保存消息并发送文件上传成功通知
|
||||
m.MsgApp.CreateAndSend(la, msgdto.SuccessSysMsg("文件上传成功", fmt.Sprintf("[%s]文件夹已成功上传至 %s[%s:%s]", folderName, mi.Name, mi.Ip, basePath)))
|
||||
m.MsgApp.CreateAndSend(la, msgdto.SuccessSysMsg(i18n.TC(ctx, imsg.MsgUploadFileSuccess), fmt.Sprintf("[%s] -> %s[%s:%s]", folderName, mi.Name, mi.Ip, basePath)))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -358,13 +365,13 @@ func (m *MachineFile) RemoveFile(rc *req.Ctx) {
|
||||
|
||||
mi, err := m.MachineFileApp.RemoveFile(rc.MetaCtx, opForm.MachineFileOp, opForm.Paths...)
|
||||
rc.ReqParam = collx.Kvs("machine", mi, "path", opForm)
|
||||
biz.ErrIsNilAppendErr(err, "删除文件失败: %s")
|
||||
biz.ErrIsNilAppendErr(err, "remove file error: %s")
|
||||
}
|
||||
|
||||
func (m *MachineFile) CopyFile(rc *req.Ctx) {
|
||||
opForm := req.BindJsonAndValid(rc, new(form.CopyFileForm))
|
||||
mi, err := m.MachineFileApp.Copy(rc.MetaCtx, opForm.MachineFileOp, opForm.ToPath, opForm.Paths...)
|
||||
biz.ErrIsNilAppendErr(err, "文件拷贝失败: %s")
|
||||
biz.ErrIsNilAppendErr(err, "file copy error: %s")
|
||||
rc.ReqParam = collx.Kvs("machine", mi, "cp", opForm)
|
||||
}
|
||||
|
||||
@@ -372,14 +379,14 @@ func (m *MachineFile) MvFile(rc *req.Ctx) {
|
||||
opForm := req.BindJsonAndValid(rc, new(form.CopyFileForm))
|
||||
mi, err := m.MachineFileApp.Mv(rc.MetaCtx, opForm.MachineFileOp, opForm.ToPath, opForm.Paths...)
|
||||
rc.ReqParam = collx.Kvs("machine", mi, "mv", opForm)
|
||||
biz.ErrIsNilAppendErr(err, "文件移动失败: %s")
|
||||
biz.ErrIsNilAppendErr(err, "file move error: %s")
|
||||
}
|
||||
|
||||
func (m *MachineFile) Rename(rc *req.Ctx) {
|
||||
renameForm := req.BindJsonAndValid(rc, new(form.RenameForm))
|
||||
mi, err := m.MachineFileApp.Rename(rc.MetaCtx, renameForm.MachineFileOp, renameForm.Newname)
|
||||
rc.ReqParam = collx.Kvs("machine", mi, "rename", renameForm)
|
||||
biz.ErrIsNilAppendErr(err, "文件重命名失败: %s")
|
||||
biz.ErrIsNilAppendErr(err, "file rename error: %s")
|
||||
}
|
||||
|
||||
func getFileType(fm fs.FileMode) string {
|
||||
@@ -394,6 +401,6 @@ func getFileType(fm fs.FileMode) string {
|
||||
|
||||
func GetMachineFileId(rc *req.Ctx) uint64 {
|
||||
fileId := rc.PathParamInt("fileId")
|
||||
biz.IsTrue(fileId != 0, "fileId错误")
|
||||
biz.IsTrue(fileId != 0, "fileId error")
|
||||
return uint64(fileId)
|
||||
}
|
||||
|
||||
@@ -51,29 +51,29 @@ func (m *MachineScript) RunMachineScript(rc *req.Ctx) {
|
||||
scriptId := GetMachineScriptId(rc)
|
||||
ac := GetMachineAc(rc)
|
||||
ms, err := m.MachineScriptApp.GetById(scriptId, "MachineId", "Name", "Script")
|
||||
biz.ErrIsNil(err, "该脚本不存在")
|
||||
biz.ErrIsNil(err, "script not found")
|
||||
|
||||
script := ms.Script
|
||||
// 如果有脚本参数,则用脚本参数替换脚本中的模板占位符参数
|
||||
if params := rc.Query("params"); params != "" {
|
||||
script, err = stringx.TemplateParse(ms.Script, jsonx.ToMap(params))
|
||||
biz.ErrIsNilAppendErr(err, "脚本模板参数解析失败: %s")
|
||||
biz.ErrIsNilAppendErr(err, "failed to parse the script template parameter: %s")
|
||||
}
|
||||
cli, err := m.MachineApp.GetCliByAc(ac)
|
||||
biz.ErrIsNilAppendErr(err, "获取客户端连接失败: %s")
|
||||
biz.ErrIsNilAppendErr(err, "connection error: %s")
|
||||
biz.ErrIsNilAppendErr(m.TagApp.CanAccess(rc.GetLoginAccount().Id, cli.Info.CodePath...), "%s")
|
||||
|
||||
res, err := cli.Run(script)
|
||||
// 记录请求参数
|
||||
rc.ReqParam = collx.Kvs("machine", cli.Info, "scriptId", scriptId, "name", ms.Name)
|
||||
if res == "" {
|
||||
biz.ErrIsNilAppendErr(err, "执行命令失败:%s")
|
||||
biz.ErrIsNilAppendErr(err, "failed to execute: %s")
|
||||
}
|
||||
rc.ResData = res
|
||||
}
|
||||
|
||||
func GetMachineScriptId(rc *req.Ctx) uint64 {
|
||||
scriptId := rc.PathParamInt("scriptId")
|
||||
biz.IsTrue(scriptId > 0, "scriptId错误")
|
||||
biz.IsTrue(scriptId > 0, "scriptId error")
|
||||
return uint64(scriptId)
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"mayfly-go/internal/machine/application/dto"
|
||||
"mayfly-go/internal/machine/domain/entity"
|
||||
"mayfly-go/internal/machine/domain/repository"
|
||||
"mayfly-go/internal/machine/imsg"
|
||||
"mayfly-go/internal/machine/infrastructure/cache"
|
||||
"mayfly-go/internal/machine/mcm"
|
||||
tagapp "mayfly-go/internal/tag/application"
|
||||
@@ -85,7 +86,7 @@ func (m *machineAppImpl) SaveMachine(ctx context.Context, param *dto.SaveMachine
|
||||
resourceType := tagentity.TagTypeMachine
|
||||
|
||||
if len(authCerts) == 0 {
|
||||
return errorx.NewBiz("授权凭证信息不能为空")
|
||||
return errorx.NewBiz("ac cannot be empty")
|
||||
}
|
||||
|
||||
oldMachine := &entity.Machine{
|
||||
@@ -95,7 +96,7 @@ func (m *machineAppImpl) SaveMachine(ctx context.Context, param *dto.SaveMachine
|
||||
}
|
||||
|
||||
if me.SshTunnelMachineId > 0 {
|
||||
if err := m.checkSshTunnelMachine(me.Ip, me.Port, me.SshTunnelMachineId, nil); err != nil {
|
||||
if err := m.checkSshTunnelMachine(ctx, me.Ip, me.Port, me.SshTunnelMachineId, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -103,7 +104,7 @@ func (m *machineAppImpl) SaveMachine(ctx context.Context, param *dto.SaveMachine
|
||||
err := m.GetByCond(oldMachine)
|
||||
if me.Id == 0 {
|
||||
if err == nil {
|
||||
return errorx.NewBiz("该机器信息已存在")
|
||||
return errorx.NewBizI(ctx, imsg.ErrMachineExist)
|
||||
}
|
||||
|
||||
// 新增机器,默认启用状态
|
||||
@@ -130,7 +131,7 @@ func (m *machineAppImpl) SaveMachine(ctx context.Context, param *dto.SaveMachine
|
||||
|
||||
// 如果存在该库,则校验修改的库是否为该库
|
||||
if err == nil && oldMachine.Id != me.Id {
|
||||
return errorx.NewBiz("该机器信息已存在")
|
||||
return errorx.NewBizI(ctx, imsg.ErrMachineExist)
|
||||
}
|
||||
// 如果调整了SshTunnelMachineId Ip port等会查不到旧数据,故需要根据id获取旧信息将code赋值给标签进行关联
|
||||
if oldMachine.Code == "" {
|
||||
@@ -197,7 +198,7 @@ func (m *machineAppImpl) ChangeStatus(ctx context.Context, id uint64, status int
|
||||
func (m *machineAppImpl) Delete(ctx context.Context, id uint64) error {
|
||||
machine, err := m.GetById(id)
|
||||
if err != nil {
|
||||
return errorx.NewBiz("机器信息不存在")
|
||||
return errorx.NewBiz("machine not found")
|
||||
}
|
||||
// 关闭连接
|
||||
mcm.DeleteCli(id)
|
||||
@@ -258,24 +259,24 @@ func (m *machineAppImpl) GetSshTunnelMachine(machineId int) (*mcm.SshTunnelMachi
|
||||
}
|
||||
|
||||
func (m *machineAppImpl) TimerUpdateStats() {
|
||||
logx.Debug("开始定时收集并缓存服务器状态信息...")
|
||||
logx.Debug("start collecting and caching machine state information periodically...")
|
||||
scheduler.AddFun("@every 2m", func() {
|
||||
machineIds, _ := m.ListByCond(model.NewModelCond(&entity.Machine{Status: entity.MachineStatusEnable, Protocol: entity.MachineProtocolSsh}).Columns("id"))
|
||||
for _, ma := range machineIds {
|
||||
go func(mid uint64) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
logx.ErrorTrace(fmt.Sprintf("定时获取机器[id=%d]状态信息失败", mid), err.(error))
|
||||
logx.ErrorTrace(fmt.Sprintf("failed to get machine [id=%d] status information on time", mid), err.(error))
|
||||
}
|
||||
}()
|
||||
logx.Debugf("定时获取机器[id=%d]状态信息开始", mid)
|
||||
logx.Debugf("time to get machine [id=%d] status information start", mid)
|
||||
cli, err := m.GetCli(mid)
|
||||
if err != nil {
|
||||
logx.Errorf("定时获取机器[id=%d]状态信息失败, 获取机器cli失败: %s", mid, err.Error())
|
||||
logx.Errorf("failed to get machine [id=%d] status information periodically, failed to get machine cli: %s", mid, err.Error())
|
||||
return
|
||||
}
|
||||
cache.SaveMachineStats(mid, cli.GetAllStats())
|
||||
logx.Debugf("定时获取机器[id=%d]状态信息结束", mid)
|
||||
logx.Debugf("time to get the machine [id=%d] status information end", mid)
|
||||
}(ma.Id)
|
||||
}
|
||||
})
|
||||
@@ -296,7 +297,7 @@ func (m *machineAppImpl) ToMachineInfoByAc(authCertName string) (*mcm.MachineInf
|
||||
Code: authCert.ResourceCode,
|
||||
}
|
||||
if err := m.GetByCond(machine); err != nil {
|
||||
return nil, errorx.NewBiz("该授权凭证关联的机器信息不存在")
|
||||
return nil, errorx.NewBiz("the machine information associated with the authorization credential does not exist")
|
||||
}
|
||||
|
||||
return m.toMi(machine, authCert)
|
||||
@@ -315,10 +316,10 @@ func (m *machineAppImpl) ToMachineInfoById(machineId uint64) (*mcm.MachineInfo,
|
||||
func (m *machineAppImpl) getMachineAndAuthCert(machineId uint64) (*entity.Machine, *tagentity.ResourceAuthCert, error) {
|
||||
me, err := m.GetById(machineId)
|
||||
if err != nil {
|
||||
return nil, nil, errorx.NewBiz("[%d]机器信息不存在", machineId)
|
||||
return nil, nil, errorx.NewBiz("[%d] machine not found", machineId)
|
||||
}
|
||||
if me.Status != entity.MachineStatusEnable && me.Protocol == 1 {
|
||||
return nil, nil, errorx.NewBiz("[%s]该机器已被停用", me.Code)
|
||||
return nil, nil, errorx.NewBiz("[%s] machine has been disable", me.Code)
|
||||
}
|
||||
|
||||
authCert, err := m.resourceAuthCertApp.GetResourceAuthCert(tagentity.TagTypeMachine, me.Code)
|
||||
@@ -375,7 +376,7 @@ func (m *machineAppImpl) genMachineResourceTag(me *entity.Machine, authCerts []*
|
||||
}
|
||||
|
||||
// checkSshTunnelMachine 校验ssh隧道机器是否存在循环隧道
|
||||
func (m *machineAppImpl) checkSshTunnelMachine(ip string, port int, sshTunnelMachineId int, visited map[string]bool) error {
|
||||
func (m *machineAppImpl) checkSshTunnelMachine(ctx context.Context, ip string, port int, sshTunnelMachineId int, visited map[string]bool) error {
|
||||
if visited == nil {
|
||||
visited = make(map[string]bool)
|
||||
}
|
||||
@@ -387,11 +388,11 @@ func (m *machineAppImpl) checkSshTunnelMachine(ip string, port int, sshTunnelMac
|
||||
}
|
||||
|
||||
if visited[fmt.Sprintf("%s:%d", stm.Ip, stm.Port)] {
|
||||
return errorx.NewBiz("存在循环隧道,请重新选择隧道机器")
|
||||
return errorx.NewBizI(ctx, imsg.ErrSshTunnelCircular)
|
||||
}
|
||||
|
||||
if stm.SshTunnelMachineId > 0 {
|
||||
return m.checkSshTunnelMachine(stm.Ip, stm.Port, stm.SshTunnelMachineId, visited)
|
||||
return m.checkSshTunnelMachine(ctx, stm.Ip, stm.Port, stm.SshTunnelMachineId, visited)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ func (m *machineCmdConfAppImpl) SaveCmdConf(ctx context.Context, cmdConfParam *d
|
||||
func (m *machineCmdConfAppImpl) DeleteCmdConf(ctx context.Context, id uint64) error {
|
||||
_, err := m.GetById(id)
|
||||
if err != nil {
|
||||
return errorx.NewBiz("该命令配置不存在")
|
||||
return errorx.NewBiz("cmd config not found")
|
||||
}
|
||||
|
||||
return m.Tx(ctx, func(ctx context.Context) error {
|
||||
@@ -71,7 +71,7 @@ func (m *machineCmdConfAppImpl) GetCmdConfsByMachineTags(ctx context.Context, ta
|
||||
var cmds []*MachineCmd
|
||||
cmdConfIds, err := m.tagTreeRelateApp.GetRelateIds(ctx, tagentity.TagRelateTypeMachineCmd, tagPaths...)
|
||||
if err != nil {
|
||||
logx.Errorf("获取命令配置信息失败: %s", err.Error())
|
||||
logx.Errorf("failed to get cmd config: %s", err.Error())
|
||||
return cmds
|
||||
}
|
||||
if len(cmdConfIds) == 0 {
|
||||
@@ -82,7 +82,7 @@ func (m *machineCmdConfAppImpl) GetCmdConfsByMachineTags(ctx context.Context, ta
|
||||
for _, cmdConf := range cmdConfs {
|
||||
for _, cmd := range cmdConf.Cmds {
|
||||
if p, err := regexp.Compile(cmd); err != nil {
|
||||
logx.Errorf("命令配置[%s],正则编译失败", cmd)
|
||||
logx.Errorf("cmd config [%s], regex compilation failed", cmd)
|
||||
} else {
|
||||
cmds = append(cmds, &MachineCmd{CmdRegexp: p})
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ func (m *machineCronJobAppImpl) SaveMachineCronJob(ctx context.Context, param *d
|
||||
} else {
|
||||
oldMcj, err := m.GetById(mcj.Id)
|
||||
if err != nil {
|
||||
return errorx.NewBiz("该计划任务不存在")
|
||||
return errorx.NewBiz("cronjob not found")
|
||||
}
|
||||
mcj.Key = oldMcj.Key
|
||||
}
|
||||
@@ -102,7 +102,7 @@ func (m *machineCronJobAppImpl) Delete(ctx context.Context, id uint64) {
|
||||
func (m *machineCronJobAppImpl) InitCronJob() {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
logx.ErrorTrace("机器计划任务初始化失败: %s", err.(error))
|
||||
logx.ErrorTrace("the machine cronjob failed to initialize: %v", err.(error))
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -193,9 +193,9 @@ func (m *machineCronJobAppImpl) runCronJob0(mid uint64, cronJob *entity.MachineC
|
||||
if res == "" {
|
||||
res = err.Error()
|
||||
}
|
||||
logx.Errorf("机器:[%d]执行[%s]计划任务失败: %s", mid, cronJob.Name, res)
|
||||
logx.Errorf("machine[%d] failed to execute cronjob[%s]: %s", mid, cronJob.Name, res)
|
||||
} else {
|
||||
logx.Debugf("机器:[%d]执行[%s]计划任务成功, 执行结果: %s", mid, cronJob.Name, res)
|
||||
logx.Debugf("machine[%d] successfully executed cronjob[%s], execution result: %s", mid, cronJob.Name, res)
|
||||
}
|
||||
}
|
||||
execRes.Res = res
|
||||
|
||||
@@ -105,7 +105,7 @@ func (m *machineFileAppImpl) GetMachineFile(condition *entity.MachineFile, cols
|
||||
func (m *machineFileAppImpl) Save(ctx context.Context, mf *entity.MachineFile) error {
|
||||
_, err := m.machineApp.GetById(mf.MachineId, "Name")
|
||||
if err != nil {
|
||||
return errorx.NewBiz("该机器不存在")
|
||||
return errorx.NewBiz("machine not found")
|
||||
}
|
||||
|
||||
if mf.Id != 0 {
|
||||
@@ -177,7 +177,7 @@ func (m *machineFileAppImpl) GetDirSize(ctx context.Context, opParam *dto.Machin
|
||||
//du: cannot access ‘/proc/19087/fdinfo/3’: No such file or directory\n
|
||||
//18G /\n
|
||||
if res == "" {
|
||||
return "", errorx.NewBiz("获取目录大小失败: %s", err.Error())
|
||||
return "", errorx.NewBiz("failed to get directory size: %s", err.Error())
|
||||
}
|
||||
strs := strings.Split(res, "\n")
|
||||
res = strs[len(strs)-2]
|
||||
@@ -243,7 +243,7 @@ func (m *machineFileAppImpl) CreateFile(ctx context.Context, opParam *dto.Machin
|
||||
}
|
||||
file, err := sftpCli.Create(path)
|
||||
if err != nil {
|
||||
return nil, errorx.NewBiz("创建文件失败: %s", err.Error())
|
||||
return nil, errorx.NewBiz("failed to create file: %s", err.Error())
|
||||
}
|
||||
defer file.Close()
|
||||
return mi, err
|
||||
@@ -383,7 +383,7 @@ func (m *machineFileAppImpl) RemoveFile(ctx context.Context, opParam *dto.Machin
|
||||
if err == nil {
|
||||
return minfo, nil
|
||||
}
|
||||
logx.Errorf("使用命令rm删除文件失败: %s", res)
|
||||
logx.Errorf("failed to delete the file using the command rm: %s", res)
|
||||
|
||||
sftpCli, err := mcli.GetSftpCli()
|
||||
if err != nil {
|
||||
@@ -408,13 +408,13 @@ func (m *machineFileAppImpl) Copy(ctx context.Context, opParam *dto.MachineFileO
|
||||
// 打开源文件
|
||||
srcFile, err := os.Open(srcPath)
|
||||
if err != nil {
|
||||
fmt.Println("Error opening source file:", err)
|
||||
logx.Errorf("error opening source file: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
// 创建目标文件
|
||||
destFile, err := os.Create(targetPath)
|
||||
if err != nil {
|
||||
fmt.Println("Error creating destination file:", err)
|
||||
logx.Errorf("error creating destination file: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
io.Copy(destFile, srcFile)
|
||||
|
||||
@@ -44,7 +44,7 @@ func (m *machineScriptAppImpl) Save(ctx context.Context, ms *entity.MachineScrip
|
||||
if machineId := ms.MachineId; machineId != Common_Script_Machine_Id {
|
||||
_, err := m.machineApp.GetById(machineId, "Name")
|
||||
if err != nil {
|
||||
return errorx.NewBiz("该机器不存在")
|
||||
return errorx.NewBiz("machine not found")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"mayfly-go/internal/machine/config"
|
||||
"mayfly-go/internal/machine/domain/entity"
|
||||
"mayfly-go/internal/machine/domain/repository"
|
||||
"mayfly-go/internal/machine/imsg"
|
||||
"mayfly-go/internal/machine/mcm"
|
||||
"mayfly-go/pkg/base"
|
||||
"mayfly-go/pkg/contextx"
|
||||
@@ -67,7 +68,7 @@ func (m *machineTermOpAppImpl) TermConn(ctx context.Context, cli *mcm.Cli, wsCon
|
||||
|
||||
fileKey, wc, saveFileFunc, err := m.fileApp.NewWriter(ctx, "", fmt.Sprintf("mto_%d_%s.cast", termOpRecord.MachineId, timex.TimeNo()))
|
||||
if err != nil {
|
||||
return errorx.NewBiz("创建终端回放记录文件失败: %s", err.Error())
|
||||
return errorx.NewBiz("failed to create a terminal playback log file: %v", err)
|
||||
}
|
||||
defer saveFileFunc(&err)
|
||||
|
||||
@@ -90,7 +91,7 @@ func (m *machineTermOpAppImpl) TermConn(ctx context.Context, cli *mcm.Cli, wsCon
|
||||
createTsParam.CmdFilterFuncs = []mcm.CmdFilterFunc{func(cmd string) error {
|
||||
for _, cmdConf := range cmdConfs {
|
||||
if cmdConf.CmdRegexp.Match([]byte(cmd)) {
|
||||
return errorx.NewBiz("该命令已被禁用...")
|
||||
return errorx.NewBizI(ctx, imsg.TerminalCmdDisable)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@@ -119,7 +120,7 @@ func (m *machineTermOpAppImpl) GetPageList(condition *entity.MachineTermOp, page
|
||||
}
|
||||
|
||||
func (m *machineTermOpAppImpl) TimerDeleteTermOp() {
|
||||
logx.Debug("开始定时删除机器终端回放记录...")
|
||||
logx.Debug("start deleting machine terminal playback records every hour...")
|
||||
scheduler.AddFun("@every 60m", func() {
|
||||
startDate := time.Now().AddDate(0, 0, -config.GetMachine().TermOpSaveDays)
|
||||
cond := &entity.MachineTermOpQuery{
|
||||
@@ -132,7 +133,7 @@ func (m *machineTermOpAppImpl) TimerDeleteTermOp() {
|
||||
|
||||
for _, termOp := range termOps {
|
||||
if err := m.DeleteTermOp(termOp); err != nil {
|
||||
logx.Warnf("删除终端操作记录失败: %s", err.Error())
|
||||
logx.Warnf("failed to delete terminal playback record: %s", err.Error())
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -101,7 +101,7 @@ func WsToGuacd(ws *websocket.Conn, tunnel Tunnel, guacd io.Writer) {
|
||||
for {
|
||||
_, data, err := ws.ReadMessage()
|
||||
if err != nil {
|
||||
logx.Warnf("Error reading message from ws: %v", err)
|
||||
logx.Warnf("error reading message from ws: %v", err)
|
||||
_ = tunnel.Close()
|
||||
return
|
||||
}
|
||||
@@ -112,7 +112,7 @@ func WsToGuacd(ws *websocket.Conn, tunnel Tunnel, guacd io.Writer) {
|
||||
}
|
||||
|
||||
if _, err = guacd.Write(data); err != nil {
|
||||
logx.Warnf("Failed writing to guacd: %v", err)
|
||||
logx.Warnf("failed writing to guacd: %v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -124,7 +124,7 @@ func GuacdToWs(ws *websocket.Conn, tunnel Tunnel, guacd InstructionReader) {
|
||||
for {
|
||||
ins, err := guacd.ReadSome()
|
||||
if err != nil {
|
||||
logx.Warnf("Error reading message from guacd: %v", err)
|
||||
logx.Warnf("error reading message from guacd: %v", err)
|
||||
_ = tunnel.Close()
|
||||
return
|
||||
}
|
||||
@@ -135,7 +135,7 @@ func GuacdToWs(ws *websocket.Conn, tunnel Tunnel, guacd InstructionReader) {
|
||||
}
|
||||
logx.Debugf("guacd msg: %s", string(ins))
|
||||
if _, err = buf.Write(ins); err != nil {
|
||||
logx.Warnf("Failed to buffer guacd to ws: %v", err)
|
||||
logx.Warnf("failed to buffer guacd to ws: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -145,7 +145,7 @@ func GuacdToWs(ws *websocket.Conn, tunnel Tunnel, guacd InstructionReader) {
|
||||
if errors.Is(err, websocket.ErrCloseSent) {
|
||||
return
|
||||
}
|
||||
logx.Warnf("Failed sending message to ws: %v", err)
|
||||
logx.Warnf("failed sending message to ws: %v", err)
|
||||
return
|
||||
}
|
||||
buf.Reset()
|
||||
|
||||
35
server/internal/machine/imsg/en.go
Normal file
35
server/internal/machine/imsg/en.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package imsg
|
||||
|
||||
import "mayfly-go/pkg/i18n"
|
||||
|
||||
var En = map[i18n.MsgId]string{
|
||||
LogMachineSave: "Machine - Save",
|
||||
LogMachineDelete: "Machine - Delete",
|
||||
LogMachineChangeStatus: "Machine - Change Status",
|
||||
LogMachineKillProcess: "Machine - Kill Process",
|
||||
LogMachineTerminalOp: "Machine - Open Terminal",
|
||||
|
||||
ErrMachineExist: "The machine information already exists",
|
||||
ErrSshTunnelCircular: "Circular tunnel exists, please select tunnel machine again",
|
||||
|
||||
// file
|
||||
LogMachineFileConfSave: "Machine - New file config",
|
||||
LogMachineFileConfDelete: "Machine - Delete file Config",
|
||||
LogMachineFileRead: "Machine - Reading file contents",
|
||||
LogMachineFileDownload: "Machine - File Download",
|
||||
LogMachineFileModify: "Machine - Modifying file contents",
|
||||
LogMachineFileCreate: "Machine - Create a file or directory",
|
||||
LogMachineFileUpload: "Machine - File Upload",
|
||||
LogMachineFileUploadFolder: "Machine - Folder Upload",
|
||||
LogMachineFileDelete: "Machine - Delete a file or directory",
|
||||
LogMachineFileCopy: "Machine - Copy File",
|
||||
LogMachineFileMove: "Machine - Move File",
|
||||
LogMachineFileRename: "Machine - Rename File",
|
||||
|
||||
ErrFileTooLargeUseDownload: "The file is over 1m, please use download to view",
|
||||
ErrUploadFileOutOfLimit: "The file size cannot exceed {{.size}} bytes",
|
||||
ErrFileUploadFail: "File upload failure",
|
||||
MsgUploadFileSuccess: "File uploaded successfully",
|
||||
|
||||
TerminalCmdDisable: "This command has been disabled...",
|
||||
}
|
||||
43
server/internal/machine/imsg/imsg.go
Normal file
43
server/internal/machine/imsg/imsg.go
Normal file
@@ -0,0 +1,43 @@
|
||||
package imsg
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/common/consts"
|
||||
"mayfly-go/pkg/i18n"
|
||||
)
|
||||
|
||||
func init() {
|
||||
i18n.AppendLangMsg(i18n.Zh_CN, Zh_CN)
|
||||
i18n.AppendLangMsg(i18n.En, En)
|
||||
}
|
||||
|
||||
const (
|
||||
LogMachineSave = iota + consts.ImsgNumMachine
|
||||
LogMachineDelete
|
||||
LogMachineChangeStatus
|
||||
LogMachineKillProcess
|
||||
LogMachineTerminalOp
|
||||
|
||||
ErrMachineExist
|
||||
ErrSshTunnelCircular
|
||||
|
||||
// file
|
||||
LogMachineFileConfSave
|
||||
LogMachineFileConfDelete
|
||||
LogMachineFileRead
|
||||
LogMachineFileDownload
|
||||
LogMachineFileModify
|
||||
LogMachineFileCreate
|
||||
LogMachineFileUpload
|
||||
LogMachineFileUploadFolder
|
||||
LogMachineFileDelete
|
||||
LogMachineFileCopy
|
||||
LogMachineFileMove
|
||||
LogMachineFileRename
|
||||
|
||||
ErrFileTooLargeUseDownload
|
||||
ErrUploadFileOutOfLimit
|
||||
ErrFileUploadFail
|
||||
MsgUploadFileSuccess
|
||||
|
||||
TerminalCmdDisable
|
||||
)
|
||||
35
server/internal/machine/imsg/zh_cn.go
Normal file
35
server/internal/machine/imsg/zh_cn.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package imsg
|
||||
|
||||
import "mayfly-go/pkg/i18n"
|
||||
|
||||
var Zh_CN = map[i18n.MsgId]string{
|
||||
LogMachineSave: "机器-保存",
|
||||
LogMachineDelete: "机器-删除",
|
||||
LogMachineChangeStatus: "机器-调整状态",
|
||||
LogMachineKillProcess: "机器-终止进程",
|
||||
LogMachineTerminalOp: "机器-终端操作",
|
||||
|
||||
ErrMachineExist: "该机器信息已存在",
|
||||
ErrSshTunnelCircular: "存在循环隧道,请重新选择隧道机器",
|
||||
|
||||
// file
|
||||
LogMachineFileConfSave: "机器-新增文件配置",
|
||||
LogMachineFileConfDelete: "机器-删除文件配置",
|
||||
LogMachineFileRead: "机器-读取文件内容",
|
||||
LogMachineFileDownload: "机器-文件下载",
|
||||
LogMachineFileModify: "机器-修改文件内容",
|
||||
LogMachineFileCreate: "机器-创建文件or目录",
|
||||
LogMachineFileUpload: "机器-文件上传",
|
||||
LogMachineFileUploadFolder: "机器-文件夹上传",
|
||||
LogMachineFileDelete: "机器-删除文件or文件夹",
|
||||
LogMachineFileCopy: "机器-拷贝文件",
|
||||
LogMachineFileMove: "机器-移动文件",
|
||||
LogMachineFileRename: "机器-文件重命名",
|
||||
|
||||
ErrFileTooLargeUseDownload: "该文件超过1m,请使用下载查看",
|
||||
ErrUploadFileOutOfLimit: "文件大小不能超过{{.size}}字节",
|
||||
ErrFileUploadFail: "文件上传失败",
|
||||
MsgUploadFileSuccess: "文件上传成功",
|
||||
|
||||
TerminalCmdDisable: "该命令已被禁用...",
|
||||
}
|
||||
@@ -21,14 +21,14 @@ type Cli struct {
|
||||
// GetSftpCli 获取sftp client
|
||||
func (c *Cli) GetSftpCli() (*sftp.Client, error) {
|
||||
if c.sshClient == nil {
|
||||
return nil, errorx.NewBiz("请先进行机器客户端连接")
|
||||
return nil, errorx.NewBiz("please connect to the machine client first")
|
||||
}
|
||||
sftpclient := c.sftpClient
|
||||
// 如果sftpClient为nil,则连接
|
||||
if sftpclient == nil {
|
||||
sc, serr := sftp.NewClient(c.sshClient)
|
||||
if serr != nil {
|
||||
return nil, errorx.NewBiz("获取sftp client失败: %s", serr.Error())
|
||||
return nil, errorx.NewBiz("failed to obtain the sftp client: %s", serr.Error())
|
||||
}
|
||||
sftpclient = sc
|
||||
c.sftpClient = sftpclient
|
||||
@@ -40,12 +40,12 @@ func (c *Cli) GetSftpCli() (*sftp.Client, error) {
|
||||
// GetSession 获取session
|
||||
func (c *Cli) GetSession() (*ssh.Session, error) {
|
||||
if c.sshClient == nil {
|
||||
return nil, errorx.NewBiz("请先进行机器客户端连接")
|
||||
return nil, errorx.NewBiz("please connect to the machine client first")
|
||||
}
|
||||
session, err := c.sshClient.NewSession()
|
||||
if err != nil {
|
||||
logx.Errorf("获取机器客户端session失败: %s", err.Error())
|
||||
return nil, errorx.NewBiz("获取会话失败, 请稍后重试...")
|
||||
logx.Errorf("failed to retrieve the machine client session: %s", err.Error())
|
||||
return nil, errorx.NewBiz("the acquisition session failed, please try again later...")
|
||||
}
|
||||
return session, nil
|
||||
}
|
||||
@@ -98,7 +98,7 @@ func (c *Cli) GetAllStats() *Stats {
|
||||
stats := new(Stats)
|
||||
res, err := c.Run(StatsShell)
|
||||
if err != nil {
|
||||
logx.Errorf("执行机器[id=%d, name=%s]运行状态信息脚本失败: %s", c.Info.Id, c.Info.Name, err.Error())
|
||||
logx.Errorf("failed to execute machine [id=%d, name=%s] running status information script: %s", c.Info.Id, c.Info.Name, err.Error())
|
||||
return stats
|
||||
}
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@ func GetMachineCliById(machineId uint64) (*Cli, error) {
|
||||
if machineCli != nil {
|
||||
return machineCli, nil
|
||||
}
|
||||
return nil, errors.New("不存在该机器id的连接")
|
||||
return nil, errors.New("no connection exists for this machine id")
|
||||
}
|
||||
|
||||
// 删除指定机器缓存客户端,并关闭客户端连接
|
||||
|
||||
@@ -46,12 +46,12 @@ func (mi *MachineInfo) GetTunnelId() string {
|
||||
|
||||
// 连接
|
||||
func (mi *MachineInfo) Conn() (*Cli, error) {
|
||||
logx.Infof("[%s]机器连接:%s:%d", mi.Name, mi.Ip, mi.Port)
|
||||
logx.Infof("the machine[%s] is connecting: %s:%d", mi.Name, mi.Ip, mi.Port)
|
||||
|
||||
// 如果使用了ssh隧道,则修改机器ip port为暴露的ip port
|
||||
err := mi.IfUseSshTunnelChangeIpPort(false)
|
||||
if err != nil {
|
||||
return nil, errorx.NewBiz("ssh隧道连接失败: %s", err.Error())
|
||||
return nil, errorx.NewBiz("ssh tunnel connection failed: %s", err.Error())
|
||||
}
|
||||
|
||||
cli := &Cli{Info: mi}
|
||||
|
||||
@@ -28,7 +28,7 @@ var (
|
||||
type CheckSshTunnelMachineHasUseFunc func(int) bool
|
||||
|
||||
func startCheckUse() {
|
||||
logx.Info("开启定时检测ssh隧道机器是否还有被使用")
|
||||
logx.Info("start periodically checking if the ssh tunnel machine is still in use")
|
||||
// 每十分钟检查一次隧道机器是否还有被使用
|
||||
scheduler.AddFun("@every 10m", func() {
|
||||
if !mutex.TryLock() {
|
||||
@@ -37,7 +37,7 @@ func startCheckUse() {
|
||||
defer mutex.Unlock()
|
||||
// 遍历隧道机器,都未被使用将会被关闭
|
||||
for mid, sshTunnelMachine := range sshTunnelMachines {
|
||||
logx.Debugf("开始定时检查ssh隧道机器[%d]是否还有被使用...", mid)
|
||||
logx.Debugf("periodically check if the ssh tunnel machine [%d] is still in use...", mid)
|
||||
hasUse := false
|
||||
for _, checkUseFunc := range checkSshTunnelMachineHasUseFuncs {
|
||||
// 如果一个在使用则返回不关闭,不继续后续检查
|
||||
@@ -129,10 +129,10 @@ func (stm *SshTunnelMachine) Close() {
|
||||
}
|
||||
|
||||
if stm.SshClient != nil {
|
||||
logx.Infof("ssh隧道机器[%d]未被使用, 关闭隧道...", stm.machineId)
|
||||
logx.Infof("ssh tunnel machine [%d] is not in use, close tunnel...", stm.machineId)
|
||||
err := stm.SshClient.Close()
|
||||
if err != nil {
|
||||
logx.Errorf("关闭ssh隧道机器[%d]发生错误: %s", stm.machineId, err.Error())
|
||||
logx.Errorf("error in closing ssh tunnel machine [%d]: %s", stm.machineId, err.Error())
|
||||
}
|
||||
}
|
||||
delete(sshTunnelMachines, stm.machineId)
|
||||
@@ -159,7 +159,7 @@ func GetSshTunnelMachine(machineId int, getMachine func(uint64) (*MachineInfo, e
|
||||
}
|
||||
sshTunnelMachine = &SshTunnelMachine{SshClient: sshClient, machineId: machineId, tunnels: map[string]*Tunnel{}}
|
||||
|
||||
logx.Infof("初次连接ssh隧道机器[%d][%s:%d]", machineId, me.Ip, me.Port)
|
||||
logx.Infof("connect to the ssh tunnel machine for the first time[%d][%s:%d]", machineId, me.Ip, me.Port)
|
||||
sshTunnelMachines[machineId] = sshTunnelMachine
|
||||
|
||||
// 如果实用了隧道机器且还没开始定时检查是否还被实用,则执行定时任务检测隧道是否还被使用
|
||||
|
||||
@@ -106,7 +106,7 @@ func (r TerminalSession) Stop() {
|
||||
if r.terminal != nil {
|
||||
if err := r.terminal.Close(); err != nil {
|
||||
if err != io.EOF {
|
||||
logx.Errorf("关闭机器ssh终端失败: %s", err.Error())
|
||||
logx.Errorf("failed to close the machine ssh terminal: %s", err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -129,7 +129,7 @@ func (ts TerminalSession) readFromTerminal() {
|
||||
rn, size, err := ts.terminal.ReadRune()
|
||||
if err != nil {
|
||||
if err != io.EOF {
|
||||
logx.Error("机器ssh终端读取消息失败: ", err)
|
||||
logx.Error("the machine ssh terminal failed to read the message: ", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -156,7 +156,7 @@ func (ts TerminalSession) writeToWebsocket() {
|
||||
|
||||
s := string(buf)
|
||||
if err := ts.WriteToWs(s); err != nil {
|
||||
logx.Error("机器ssh终端发送消息至websocket失败: ", err)
|
||||
logx.Error("the machine ssh endpoint failed to send a message to the websocket: ", err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -197,14 +197,14 @@ func (ts *TerminalSession) receiveWsMsg() {
|
||||
// read websocket msg
|
||||
_, wsData, err := ts.wsConn.ReadMessage()
|
||||
if err != nil {
|
||||
logx.Debugf("机器ssh终端读取websocket消息失败: %s", err.Error())
|
||||
logx.Debugf("the machine ssh terminal failed to read the websocket message: %s", err.Error())
|
||||
return
|
||||
}
|
||||
// 解析消息
|
||||
msgObj, err := parseMsg(wsData)
|
||||
if err != nil {
|
||||
ts.WriteToWs(GetErrorContentRn("消息内容解析失败..."))
|
||||
logx.Error("机器ssh终端消息解析失败: ", err)
|
||||
ts.WriteToWs(GetErrorContentRn("failed to parse the message content..."))
|
||||
logx.Error("machine ssh terminal message parsing failed: ", err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -228,13 +228,13 @@ func (ts *TerminalSession) receiveWsMsg() {
|
||||
|
||||
_, err := ts.terminal.Write([]byte(msgObj.Msg))
|
||||
if err != nil {
|
||||
logx.Errorf("写入数据至ssh终端失败: %s", err)
|
||||
ts.WriteToWs(GetErrorContentRn(fmt.Sprintf("写入数据至ssh终端失败: %s", err.Error())))
|
||||
logx.Errorf("failed to write data to the ssh terminal: %s", err)
|
||||
ts.WriteToWs(GetErrorContentRn(fmt.Sprintf("failed to write data to the ssh terminal: %s", err.Error())))
|
||||
}
|
||||
case Ping:
|
||||
_, err := ts.terminal.SshSession.SendRequest("ping", true, nil)
|
||||
if err != nil {
|
||||
ts.WriteToWs(GetErrorContentRn("终端连接已断开..."))
|
||||
ts.WriteToWs(GetErrorContentRn("the terminal connection has been disconnected..."))
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -254,7 +254,7 @@ func parseMsg(msg []byte) (*WsMsg, error) {
|
||||
// 查找第一个 "|" 的位置
|
||||
index := strings.Index(msgStr, MsgSplit)
|
||||
if index == -1 {
|
||||
return nil, errorx.NewBiz("消息内容不符合指定规则")
|
||||
return nil, errorx.NewBiz("the message content does not conform to the specified rules")
|
||||
}
|
||||
|
||||
// 获取消息类型, 提取第一个 "|" 之前的内容
|
||||
|
||||
@@ -2,6 +2,7 @@ package router
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/machine/api"
|
||||
"mayfly-go/internal/machine/imsg"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/ioc"
|
||||
"mayfly-go/pkg/req"
|
||||
@@ -35,15 +36,15 @@ func InitMachineRouter(router *gin.RouterGroup) {
|
||||
|
||||
req.NewGet(":machineId/groups", m.GetGroups),
|
||||
|
||||
req.NewDelete(":machineId/process", m.KillProcess).Log(req.NewLogSave("终止进程")).RequiredPermissionCode("machine:killprocess"),
|
||||
req.NewPost("", m.SaveMachine).Log(req.NewLogSaveI(imsg.LogMachineSave)).RequiredPermission(saveMachineP),
|
||||
|
||||
req.NewPost("", m.SaveMachine).Log(req.NewLogSave("保存机器信息")).RequiredPermission(saveMachineP),
|
||||
req.NewDelete(":machineId", m.DeleteMachine).Log(req.NewLogSaveI(imsg.LogMachineSave)),
|
||||
|
||||
req.NewPost("test-conn", m.TestConn),
|
||||
|
||||
req.NewPut(":machineId/:status", m.ChangeStatus).Log(req.NewLogSave("调整机器状态")).RequiredPermission(saveMachineP),
|
||||
req.NewPut(":machineId/:status", m.ChangeStatus).Log(req.NewLogSaveI(imsg.LogMachineChangeStatus)).RequiredPermission(saveMachineP),
|
||||
|
||||
req.NewDelete(":machineId", m.DeleteMachine).Log(req.NewLogSave("删除机器")),
|
||||
req.NewDelete(":machineId/process", m.KillProcess).Log(req.NewLogSaveI(imsg.LogMachineKillProcess)).RequiredPermissionCode("machine:killprocess"),
|
||||
|
||||
// 获取机器终端回放记录列表,目前具有保存机器信息的权限标识才有权限查看终端回放
|
||||
req.NewGet(":machineId/term-recs", m.MachineTermOpRecords).RequiredPermission(saveMachineP),
|
||||
|
||||
@@ -2,6 +2,7 @@ package router
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/machine/api"
|
||||
"mayfly-go/internal/machine/imsg"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/ioc"
|
||||
"mayfly-go/pkg/req"
|
||||
@@ -19,13 +20,13 @@ func InitMachineFileRouter(router *gin.RouterGroup) {
|
||||
// 获取指定机器文件列表
|
||||
req.NewGet(":machineId/files", mf.MachineFiles),
|
||||
|
||||
req.NewPost(":machineId/files", mf.SaveMachineFiles).Log(req.NewLogSave("机器-新增文件配置")).RequiredPermissionCode("machine:file:add"),
|
||||
req.NewPost(":machineId/files", mf.SaveMachineFiles).Log(req.NewLogSaveI(imsg.LogMachineFileConfSave)).RequiredPermissionCode("machine:file:add"),
|
||||
|
||||
req.NewDelete(":machineId/files/:fileId", mf.DeleteFile).Log(req.NewLogSave("机器-删除文件配置")).RequiredPermissionCode("machine:file:del"),
|
||||
req.NewDelete(":machineId/files/:fileId", mf.DeleteFile).Log(req.NewLogSaveI(imsg.LogMachineFileConfDelete)).RequiredPermissionCode("machine:file:del"),
|
||||
|
||||
req.NewGet(":machineId/files/:fileId/read", mf.ReadFileContent).Log(req.NewLogSave("机器-读取文件内容")),
|
||||
req.NewGet(":machineId/files/:fileId/read", mf.ReadFileContent).Log(req.NewLogSaveI(imsg.LogMachineFileRead)),
|
||||
|
||||
req.NewGet(":machineId/files/:fileId/download", mf.DownloadFile).NoRes().Log(req.NewLogSave("机器-文件下载")),
|
||||
req.NewGet(":machineId/files/:fileId/download", mf.DownloadFile).NoRes().Log(req.NewLogSaveI(imsg.LogMachineFileDownload)),
|
||||
|
||||
req.NewGet(":machineId/files/:fileId/read-dir", mf.GetDirEntry),
|
||||
|
||||
@@ -33,21 +34,21 @@ func InitMachineFileRouter(router *gin.RouterGroup) {
|
||||
|
||||
req.NewGet(":machineId/files/:fileId/file-stat", mf.GetFileStat),
|
||||
|
||||
req.NewPost(":machineId/files/:fileId/write", mf.WriteFileContent).Log(req.NewLogSave("机器-修改文件内容")).RequiredPermissionCode("machine:file:write"),
|
||||
req.NewPost(":machineId/files/:fileId/write", mf.WriteFileContent).Log(req.NewLogSaveI(imsg.LogMachineFileModify)).RequiredPermissionCode("machine:file:write"),
|
||||
|
||||
req.NewPost(":machineId/files/:fileId/create-file", mf.CreateFile).Log(req.NewLogSave("机器-创建文件or目录")),
|
||||
req.NewPost(":machineId/files/:fileId/create-file", mf.CreateFile).Log(req.NewLogSaveI(imsg.LogMachineFileCreate)),
|
||||
|
||||
req.NewPost(":machineId/files/:fileId/upload", mf.UploadFile).Log(req.NewLogSave("机器-文件上传")).RequiredPermissionCode("machine:file:upload"),
|
||||
req.NewPost(":machineId/files/:fileId/upload", mf.UploadFile).Log(req.NewLogSaveI(imsg.LogMachineFileUpload)).RequiredPermissionCode("machine:file:upload"),
|
||||
|
||||
req.NewPost(":machineId/files/:fileId/upload-folder", mf.UploadFolder).Log(req.NewLogSave("机器-文件夹上传")).RequiredPermissionCode("machine:file:upload"),
|
||||
req.NewPost(":machineId/files/:fileId/upload-folder", mf.UploadFolder).Log(req.NewLogSaveI(imsg.LogMachineFileUploadFolder)).RequiredPermissionCode("machine:file:upload"),
|
||||
|
||||
req.NewPost(":machineId/files/:fileId/remove", mf.RemoveFile).Log(req.NewLogSave("机器-删除文件or文件夹")).RequiredPermissionCode("machine:file:rm"),
|
||||
req.NewPost(":machineId/files/:fileId/remove", mf.RemoveFile).Log(req.NewLogSaveI(imsg.LogMachineFileDelete)).RequiredPermissionCode("machine:file:rm"),
|
||||
|
||||
req.NewPost(":machineId/files/:fileId/cp", mf.CopyFile).Log(req.NewLogSave("机器-拷贝文件")).RequiredPermissionCode("machine:file:rm"),
|
||||
req.NewPost(":machineId/files/:fileId/cp", mf.CopyFile).Log(req.NewLogSaveI(imsg.LogMachineFileCopy)).RequiredPermissionCode("machine:file:rm"),
|
||||
|
||||
req.NewPost(":machineId/files/:fileId/mv", mf.MvFile).Log(req.NewLogSave("机器-移动文件")).RequiredPermissionCode("machine:file:rm"),
|
||||
req.NewPost(":machineId/files/:fileId/mv", mf.MvFile).Log(req.NewLogSaveI(imsg.LogMachineFileMove)).RequiredPermissionCode("machine:file:rm"),
|
||||
|
||||
req.NewPost(":machineId/files/:fileId/rename", mf.Rename).Log(req.NewLogSave("机器-文件重命名")).RequiredPermissionCode("machine:file:write"),
|
||||
req.NewPost(":machineId/files/:fileId/rename", mf.Rename).Log(req.NewLogSaveI(imsg.LogMachineFileRename)).RequiredPermissionCode("machine:file:write"),
|
||||
}
|
||||
|
||||
req.BatchSetGroup(machineFile, reqs[:])
|
||||
|
||||
@@ -16,9 +16,9 @@ import (
|
||||
"mayfly-go/pkg/req"
|
||||
"mayfly-go/pkg/utils/collx"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/may-fly/cast"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
@@ -55,7 +55,7 @@ func (m *Mongo) Mongos(rc *req.Ctx) {
|
||||
func (m *Mongo) TestConn(rc *req.Ctx) {
|
||||
form := &form.Mongo{}
|
||||
mongo := req.BindJsonAndCopyTo[*entity.Mongo](rc, form, new(entity.Mongo))
|
||||
biz.ErrIsNilAppendErr(m.MongoApp.TestConn(mongo), "连接失败: %s")
|
||||
biz.ErrIsNilAppendErr(m.MongoApp.TestConn(mongo), "connection error: %s")
|
||||
}
|
||||
|
||||
func (m *Mongo) Save(rc *req.Ctx) {
|
||||
@@ -78,9 +78,7 @@ func (m *Mongo) DeleteMongo(rc *req.Ctx) {
|
||||
ids := strings.Split(idsStr, ",")
|
||||
|
||||
for _, v := range ids {
|
||||
value, err := strconv.Atoi(v)
|
||||
biz.ErrIsNilAppendErr(err, "string类型转换为int异常: %s")
|
||||
m.MongoApp.Delete(rc.MetaCtx, uint64(value))
|
||||
m.MongoApp.Delete(rc.MetaCtx, cast.ToUint64(v))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,7 +86,7 @@ func (m *Mongo) Databases(rc *req.Ctx) {
|
||||
conn, err := m.MongoApp.GetMongoConn(m.GetMongoId(rc))
|
||||
biz.ErrIsNil(err)
|
||||
res, err := conn.Cli.ListDatabases(context.TODO(), bson.D{})
|
||||
biz.ErrIsNilAppendErr(err, "获取mongo所有库信息失败: %s")
|
||||
biz.ErrIsNilAppendErr(err, "get mongo dbs error: %s")
|
||||
rc.ResData = res
|
||||
}
|
||||
|
||||
@@ -99,10 +97,10 @@ func (m *Mongo) Collections(rc *req.Ctx) {
|
||||
global.EventBus.Publish(rc.MetaCtx, event.EventTopicResourceOp, conn.Info.CodePath[0])
|
||||
|
||||
db := rc.Query("database")
|
||||
biz.NotEmpty(db, "database不能为空")
|
||||
biz.NotEmpty(db, "database cannot be empty")
|
||||
ctx := context.TODO()
|
||||
res, err := conn.Cli.Database(db).ListCollectionNames(ctx, bson.D{})
|
||||
biz.ErrIsNilAppendErr(err, "获取库集合信息失败: %s")
|
||||
biz.ErrIsNilAppendErr(err, "get db collections error: %s")
|
||||
rc.ResData = res
|
||||
}
|
||||
|
||||
@@ -132,7 +130,7 @@ func (m *Mongo) RunCommand(rc *req.Ctx) {
|
||||
commands,
|
||||
).Decode(&bm)
|
||||
|
||||
biz.ErrIsNilAppendErr(err, "执行命令失败: %s")
|
||||
biz.ErrIsNilAppendErr(err, "command execution failed: %s")
|
||||
rc.ResData = bm
|
||||
}
|
||||
|
||||
@@ -145,7 +143,7 @@ func (m *Mongo) FindCommand(rc *req.Ctx) {
|
||||
|
||||
limit := commandForm.Limit
|
||||
if limit != 0 {
|
||||
biz.IsTrue(limit <= 100, "limit不能超过100")
|
||||
biz.IsTrue(limit <= 100, "the limit cannot exceed 100")
|
||||
}
|
||||
opts := options.Find().SetSort(commandForm.Sort).
|
||||
SetSkip(commandForm.Skip).
|
||||
@@ -163,7 +161,7 @@ func (m *Mongo) FindCommand(rc *req.Ctx) {
|
||||
}
|
||||
|
||||
cur, err := cli.Database(commandForm.Database).Collection(commandForm.Collection).Find(ctx, commandForm.Filter, opts)
|
||||
biz.ErrIsNilAppendErr(err, "命令执行失败: %s")
|
||||
biz.ErrIsNilAppendErr(err, "command execution failed: %s")
|
||||
|
||||
var res []bson.M
|
||||
cur.All(ctx, &res)
|
||||
@@ -188,7 +186,7 @@ func (m *Mongo) UpdateByIdCommand(rc *req.Ctx) {
|
||||
}
|
||||
|
||||
res, err := conn.Cli.Database(commandForm.Database).Collection(commandForm.Collection).UpdateByID(context.TODO(), docId, commandForm.Update)
|
||||
biz.ErrIsNilAppendErr(err, "命令执行失败: %s")
|
||||
biz.ErrIsNilAppendErr(err, "command execution failed: %s")
|
||||
|
||||
rc.ResData = res
|
||||
}
|
||||
@@ -211,7 +209,7 @@ func (m *Mongo) DeleteByIdCommand(rc *req.Ctx) {
|
||||
}
|
||||
|
||||
res, err := conn.Cli.Database(commandForm.Database).Collection(commandForm.Collection).DeleteOne(context.TODO(), bson.D{{Key: "_id", Value: docId}})
|
||||
biz.ErrIsNilAppendErr(err, "命令执行失败: %s")
|
||||
biz.ErrIsNilAppendErr(err, "command execution failed: %s")
|
||||
rc.ResData = res
|
||||
}
|
||||
|
||||
@@ -223,13 +221,13 @@ func (m *Mongo) InsertOneCommand(rc *req.Ctx) {
|
||||
rc.ReqParam = collx.Kvs("mongo", conn.Info, "cmd", commandForm)
|
||||
|
||||
res, err := conn.Cli.Database(commandForm.Database).Collection(commandForm.Collection).InsertOne(context.TODO(), commandForm.Doc)
|
||||
biz.ErrIsNilAppendErr(err, "命令执行失败: %s")
|
||||
biz.ErrIsNilAppendErr(err, "command execution failed: %s")
|
||||
rc.ResData = res
|
||||
}
|
||||
|
||||
// 获取请求路径上的mongo id
|
||||
func (m *Mongo) GetMongoId(rc *req.Ctx) uint64 {
|
||||
dbId := rc.PathParamInt("id")
|
||||
biz.IsTrue(dbId > 0, "mongoId错误")
|
||||
biz.IsTrue(dbId > 0, "mongoId error")
|
||||
return uint64(dbId)
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"mayfly-go/internal/common/consts"
|
||||
"mayfly-go/internal/mongo/domain/entity"
|
||||
"mayfly-go/internal/mongo/domain/repository"
|
||||
"mayfly-go/internal/mongo/imsg"
|
||||
"mayfly-go/internal/mongo/mgm"
|
||||
tagapp "mayfly-go/internal/tag/application"
|
||||
tagdto "mayfly-go/internal/tag/application/dto"
|
||||
@@ -52,7 +53,7 @@ func (d *mongoAppImpl) GetPageList(condition *entity.MongoQuery, pageParam *mode
|
||||
func (d *mongoAppImpl) Delete(ctx context.Context, id uint64) error {
|
||||
mongoEntity, err := d.GetById(id)
|
||||
if err != nil {
|
||||
return errorx.NewBiz("mongo信息不存在")
|
||||
return errorx.NewBiz("mongo not found")
|
||||
}
|
||||
|
||||
mgm.CloseConn(id)
|
||||
@@ -83,7 +84,7 @@ func (d *mongoAppImpl) SaveMongo(ctx context.Context, m *entity.Mongo, tagCodePa
|
||||
|
||||
if m.Id == 0 {
|
||||
if err == nil {
|
||||
return errorx.NewBiz("该信息已存在")
|
||||
return errorx.NewBizI(ctx, imsg.ErrMongoInfoExist)
|
||||
}
|
||||
// 生成随机编号
|
||||
m.Code = stringx.Rand(10)
|
||||
@@ -104,7 +105,7 @@ func (d *mongoAppImpl) SaveMongo(ctx context.Context, m *entity.Mongo, tagCodePa
|
||||
|
||||
// 如果存在该库,则校验修改的库是否为该库
|
||||
if err == nil && oldMongo.Id != m.Id {
|
||||
return errorx.NewBiz("该信息已存在")
|
||||
return errorx.NewBizI(ctx, imsg.ErrMongoInfoExist)
|
||||
}
|
||||
// 如果调整了ssh等会查不到旧数据,故需要根据id获取旧信息将code赋值给标签进行关联
|
||||
if oldMongo.Code == "" {
|
||||
@@ -137,7 +138,7 @@ func (d *mongoAppImpl) GetMongoConn(id uint64) (*mgm.MongoConn, error) {
|
||||
return mgm.GetMongoConn(id, func() (*mgm.MongoInfo, error) {
|
||||
me, err := d.GetById(id)
|
||||
if err != nil {
|
||||
return nil, errorx.NewBiz("mongo信息不存在")
|
||||
return nil, errorx.NewBiz("mongo not found")
|
||||
}
|
||||
return me.ToMongoInfo(d.tagApp.ListTagPathByTypeAndCode(consts.ResourceTypeMongo, me.Code)...), nil
|
||||
})
|
||||
|
||||
14
server/internal/mongo/imsg/en.go
Normal file
14
server/internal/mongo/imsg/en.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package imsg
|
||||
|
||||
import "mayfly-go/pkg/i18n"
|
||||
|
||||
var En = map[i18n.MsgId]string{
|
||||
LogMongoSave: "Mongo - Save",
|
||||
LogMongoDelete: "Mongo-Delete",
|
||||
LogMongoRunCmd: "Mongo - Run Cmd",
|
||||
LogUpdateDocs: "Mongo - Update Documents",
|
||||
LogDelDocs: "Mongo - Delete Documents",
|
||||
LogInsertDocs: "Mongo - Insert Documents",
|
||||
|
||||
ErrMongoInfoExist: "that information already exists",
|
||||
}
|
||||
22
server/internal/mongo/imsg/imsg.go
Normal file
22
server/internal/mongo/imsg/imsg.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package imsg
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/common/consts"
|
||||
"mayfly-go/pkg/i18n"
|
||||
)
|
||||
|
||||
func init() {
|
||||
i18n.AppendLangMsg(i18n.Zh_CN, Zh_CN)
|
||||
i18n.AppendLangMsg(i18n.En, En)
|
||||
}
|
||||
|
||||
const (
|
||||
LogMongoSave = iota + consts.ImsgNumMongo
|
||||
LogMongoDelete
|
||||
LogMongoRunCmd
|
||||
LogUpdateDocs
|
||||
LogDelDocs
|
||||
LogInsertDocs
|
||||
|
||||
ErrMongoInfoExist
|
||||
)
|
||||
14
server/internal/mongo/imsg/zh_cn.go
Normal file
14
server/internal/mongo/imsg/zh_cn.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package imsg
|
||||
|
||||
import "mayfly-go/pkg/i18n"
|
||||
|
||||
var Zh_CN = map[i18n.MsgId]string{
|
||||
LogMongoSave: "Mongo-保存",
|
||||
LogMongoDelete: "Mongo-删除",
|
||||
LogMongoRunCmd: "Mongo-执行命令",
|
||||
LogUpdateDocs: "Mongo-更新文档",
|
||||
LogDelDocs: "Mongo-删除文档",
|
||||
LogInsertDocs: "Mongo-插入文档",
|
||||
|
||||
ErrMongoInfoExist: "该信息已存在",
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package router
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/mongo/api"
|
||||
"mayfly-go/internal/mongo/imsg"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/ioc"
|
||||
"mayfly-go/pkg/req"
|
||||
@@ -28,9 +29,9 @@ func InitMongoRouter(router *gin.RouterGroup) {
|
||||
|
||||
req.NewPost("/test-conn", ma.TestConn),
|
||||
|
||||
req.NewPost("", ma.Save).Log(req.NewLogSave("mongo-保存信息")),
|
||||
req.NewPost("", ma.Save).Log(req.NewLogSaveI(imsg.LogMongoSave)),
|
||||
|
||||
req.NewDelete(":id", ma.DeleteMongo).Log(req.NewLogSave("mongo-删除信息")),
|
||||
req.NewDelete(":id", ma.DeleteMongo).Log(req.NewLogSaveI(imsg.LogMongoDelete)),
|
||||
|
||||
// 获取mongo下的所有数据库
|
||||
req.NewGet(":id/databases", ma.Databases),
|
||||
@@ -39,18 +40,18 @@ func InitMongoRouter(router *gin.RouterGroup) {
|
||||
req.NewGet(":id/collections", ma.Collections),
|
||||
|
||||
// mongo runCommand
|
||||
req.NewPost(":id/run-command", ma.RunCommand).Log(req.NewLogSave("mongo-runCommand")),
|
||||
req.NewPost(":id/run-command", ma.RunCommand).Log(req.NewLogSaveI(imsg.LogMongoRunCmd)),
|
||||
|
||||
// 执行mongo find命令
|
||||
req.NewPost(":id/command/find", ma.FindCommand),
|
||||
|
||||
req.NewPost(":id/command/update-by-id", ma.UpdateByIdCommand).RequiredPermission(saveDataPerm).Log(req.NewLogSave("mongo-更新文档")),
|
||||
req.NewPost(":id/command/update-by-id", ma.UpdateByIdCommand).RequiredPermission(saveDataPerm).Log(req.NewLogSaveI(imsg.LogUpdateDocs)),
|
||||
|
||||
// 执行mongo delete by id命令
|
||||
req.NewPost(":id/command/delete-by-id", ma.DeleteByIdCommand).RequiredPermission(req.NewPermission("mongo:data:del")).Log(req.NewLogSave("mongo-删除文档")),
|
||||
req.NewPost(":id/command/delete-by-id", ma.DeleteByIdCommand).RequiredPermission(req.NewPermission("mongo:data:del")).Log(req.NewLogSaveI(imsg.LogDelDocs)),
|
||||
|
||||
// 执行mongo insert 命令
|
||||
req.NewPost(":id/command/insert", ma.InsertOneCommand).RequiredPermission(saveDataPerm).Log(req.NewLogSave("mogno-插入文档")),
|
||||
req.NewPost(":id/command/insert", ma.InsertOneCommand).RequiredPermission(saveDataPerm).Log(req.NewLogSaveI(imsg.LogInsertDocs)),
|
||||
}
|
||||
|
||||
req.BatchSetGroup(m, reqs[:])
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
func (r *Redis) RunCmd(rc *req.Ctx) {
|
||||
var cmdReq form.RunCmdForm
|
||||
runCmdParam := req.BindJsonAndCopyTo(rc, &cmdReq, new(dto.RunCmd))
|
||||
biz.IsTrue(len(cmdReq.Cmd) > 0, "redis命令不能为空")
|
||||
biz.IsTrue(len(cmdReq.Cmd) > 0, "redis cmd cannot be empty")
|
||||
|
||||
redisConn := r.getRedisConn(rc)
|
||||
biz.ErrIsNilAppendErr(r.TagApp.CanAccess(rc.GetLoginAccount().Id, redisConn.Info.CodePath...), "%s")
|
||||
|
||||
@@ -98,7 +98,7 @@ func (r *Redis) KeyInfo(rc *req.Ctx) {
|
||||
cmd := ri.GetCmdable()
|
||||
ctx := context.Background()
|
||||
ttl, err := cmd.TTL(ctx, key).Result()
|
||||
biz.ErrIsNilAppendErr(err, "ttl失败: %s")
|
||||
biz.ErrIsNilAppendErr(err, "ttl error: %s")
|
||||
|
||||
ttlInt := -1
|
||||
if ttl != -1 {
|
||||
@@ -106,7 +106,7 @@ func (r *Redis) KeyInfo(rc *req.Ctx) {
|
||||
}
|
||||
|
||||
typeRes, err := cmd.Type(ctx, key).Result()
|
||||
biz.ErrIsNilAppendErr(err, "获取key type失败: %s")
|
||||
biz.ErrIsNilAppendErr(err, "get key type error: %s")
|
||||
|
||||
rc.ResData = &vo.KeyInfo{
|
||||
Key: key,
|
||||
@@ -118,7 +118,7 @@ func (r *Redis) KeyInfo(rc *req.Ctx) {
|
||||
func (r *Redis) TtlKey(rc *req.Ctx) {
|
||||
ri, key := r.checkKeyAndGetRedisConn(rc)
|
||||
ttl, err := ri.GetCmdable().TTL(context.Background(), key).Result()
|
||||
biz.ErrIsNilAppendErr(err, "ttl失败: %s")
|
||||
biz.ErrIsNilAppendErr(err, "ttl error: %s")
|
||||
|
||||
if ttl == -1 {
|
||||
rc.ResData = -1
|
||||
|
||||
@@ -18,9 +18,9 @@ import (
|
||||
"mayfly-go/pkg/req"
|
||||
"mayfly-go/pkg/utils/collx"
|
||||
"mayfly-go/pkg/utils/stringx"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/may-fly/cast"
|
||||
"github.com/redis/go-redis/v9"
|
||||
)
|
||||
|
||||
@@ -104,9 +104,7 @@ func (r *Redis) DeleteRedis(rc *req.Ctx) {
|
||||
ids := strings.Split(idsStr, ",")
|
||||
|
||||
for _, v := range ids {
|
||||
value, err := strconv.Atoi(v)
|
||||
biz.ErrIsNilAppendErr(err, "string类型转换为int异常: %s")
|
||||
r.RedisApp.Delete(rc.MetaCtx, uint64(value))
|
||||
r.RedisApp.Delete(rc.MetaCtx, cast.ToUint64(v))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,7 +121,7 @@ func (r *Redis) RedisInfo(rc *req.Ctx) {
|
||||
redisCli = ri.Cli
|
||||
} else if mode == rdm.ClusterMode {
|
||||
host := rc.Query("host")
|
||||
biz.NotEmpty(host, "集群模式host信息不能为空")
|
||||
biz.NotEmpty(host, "the cluster mode host info cannot be empty")
|
||||
clusterClient := ri.ClusterCli
|
||||
// 遍历集群的master节点找到该redis client
|
||||
clusterClient.ForEachMaster(ctx, func(ctx context.Context, client *redis.Client) error {
|
||||
@@ -141,7 +139,7 @@ func (r *Redis) RedisInfo(rc *req.Ctx) {
|
||||
return nil
|
||||
})
|
||||
}
|
||||
biz.NotNil(redisCli, "该实例不在该集群中")
|
||||
biz.NotNil(redisCli, "the instance is not in the cluster")
|
||||
}
|
||||
|
||||
var res string
|
||||
@@ -151,7 +149,7 @@ func (r *Redis) RedisInfo(rc *req.Ctx) {
|
||||
res, err = redisCli.Info(ctx, section).Result()
|
||||
}
|
||||
|
||||
biz.ErrIsNilAppendErr(err, "获取redis info失败: %s")
|
||||
biz.ErrIsNilAppendErr(err, "get redis info error: %s")
|
||||
|
||||
datas := strings.Split(res, "\r\n")
|
||||
i := 0
|
||||
@@ -190,7 +188,7 @@ func (r *Redis) RedisInfo(rc *req.Ctx) {
|
||||
func (r *Redis) ClusterInfo(rc *req.Ctx) {
|
||||
ri, err := r.RedisApp.GetRedisConn(uint64(rc.PathParamInt("id")), 0)
|
||||
biz.ErrIsNil(err)
|
||||
biz.IsEquals(ri.Info.Mode, rdm.ClusterMode, "非集群模式")
|
||||
biz.IsEquals(ri.Info.Mode, rdm.ClusterMode, "non-cluster mode")
|
||||
info, _ := ri.ClusterCli.ClusterInfo(context.Background()).Result()
|
||||
nodesStr, _ := ri.ClusterCli.ClusterNodes(context.Background()).Result()
|
||||
|
||||
@@ -234,7 +232,7 @@ func (r *Redis) ClusterInfo(rc *req.Ctx) {
|
||||
// 校验查询参数中的key为必填项,并返回redis实例
|
||||
func (r *Redis) checkKeyAndGetRedisConn(rc *req.Ctx) (*rdm.RedisConn, string) {
|
||||
key := rc.Query("key")
|
||||
biz.NotEmpty(key, "key不能为空")
|
||||
biz.NotEmpty(key, "key cannot be empty")
|
||||
return r.getRedisConn(rc), key
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"mayfly-go/internal/redis/application/dto"
|
||||
"mayfly-go/internal/redis/domain/entity"
|
||||
"mayfly-go/internal/redis/domain/repository"
|
||||
"mayfly-go/internal/redis/imsg"
|
||||
"mayfly-go/internal/redis/rdm"
|
||||
tagapp "mayfly-go/internal/tag/application"
|
||||
tagdto "mayfly-go/internal/tag/application/dto"
|
||||
@@ -56,7 +57,6 @@ type redisAppImpl struct {
|
||||
|
||||
tagApp tagapp.TagTree `inject:"TagTreeApp"`
|
||||
procdefApp flowapp.Procdef `inject:"ProcdefApp"`
|
||||
procinstApp flowapp.Procinst `inject:"ProcinstApp"`
|
||||
resourceAuthCertApp tagapp.ResourceAuthCert `inject:"ResourceAuthCertApp"`
|
||||
}
|
||||
|
||||
@@ -102,7 +102,7 @@ func (r *redisAppImpl) SaveRedis(ctx context.Context, param *dto.SaveRedis) erro
|
||||
|
||||
if re.Id == 0 {
|
||||
if err == nil {
|
||||
return errorx.NewBiz("该实例已存在")
|
||||
return errorx.NewBizI(ctx, imsg.ErrRedisInfoExist)
|
||||
}
|
||||
// 生成随机编号
|
||||
re.Code = stringx.Rand(10)
|
||||
@@ -129,7 +129,7 @@ func (r *redisAppImpl) SaveRedis(ctx context.Context, param *dto.SaveRedis) erro
|
||||
|
||||
// 如果存在该库,则校验修改的库是否为该库
|
||||
if err == nil && oldRedis.Id != re.Id {
|
||||
return errorx.NewBiz("该实例已存在")
|
||||
return errorx.NewBizI(ctx, imsg.ErrRedisInfoExist)
|
||||
}
|
||||
// 如果修改了redis实例的库信息,则关闭旧库的连接
|
||||
if oldRedis.Db != re.Db || oldRedis.SshTunnelMachineId != re.SshTunnelMachineId {
|
||||
@@ -173,7 +173,7 @@ func (r *redisAppImpl) SaveRedis(ctx context.Context, param *dto.SaveRedis) erro
|
||||
func (r *redisAppImpl) Delete(ctx context.Context, id uint64) error {
|
||||
re, err := r.GetById(id)
|
||||
if err != nil {
|
||||
return errorx.NewBiz("该redis信息不存在")
|
||||
return errorx.NewBiz("redis not found")
|
||||
}
|
||||
// 如果存在连接,则关闭所有库连接信息
|
||||
for _, dbStr := range strings.Split(re.Db, ",") {
|
||||
@@ -204,7 +204,7 @@ func (r *redisAppImpl) GetRedisConn(id uint64, db int) (*rdm.RedisConn, error) {
|
||||
// 缓存不存在,则回调获取redis信息
|
||||
re, err := r.GetById(id)
|
||||
if err != nil {
|
||||
return nil, errorx.NewBiz("redis信息不存在")
|
||||
return nil, errorx.NewBiz("redis not found")
|
||||
}
|
||||
authCert, err := r.resourceAuthCertApp.GetResourceAuthCert(tagentity.TagTypeRedis, re.Code)
|
||||
if err != nil {
|
||||
@@ -216,7 +216,7 @@ func (r *redisAppImpl) GetRedisConn(id uint64, db int) (*rdm.RedisConn, error) {
|
||||
|
||||
func (r *redisAppImpl) RunCmd(ctx context.Context, redisConn *rdm.RedisConn, cmdParam *dto.RunCmd) (any, error) {
|
||||
if redisConn == nil {
|
||||
return nil, errorx.NewBiz("redis连接不存在")
|
||||
return nil, errorx.NewBiz("redis connection not exist")
|
||||
}
|
||||
|
||||
// 开启工单流程,则校验该流程是否需要校验
|
||||
@@ -227,7 +227,7 @@ func (r *redisAppImpl) RunCmd(ctx context.Context, redisConn *rdm.RedisConn, cmd
|
||||
cmdType = "write"
|
||||
}
|
||||
if needStartProc := procdef.MatchCondition(RedisRunCmdFlowBizType, collx.Kvs("cmdType", cmdType, "cmd", cmd)); needStartProc {
|
||||
return nil, errorx.NewBiz("该操作需要提交工单审批执行")
|
||||
return nil, errorx.NewBizI(ctx, imsg.ErrSubmitFlowRunCmd)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -258,7 +258,7 @@ func (r *redisAppImpl) FlowBizHandle(ctx context.Context, bizHandleParam *flowap
|
||||
|
||||
runCmdParam, err := jsonx.To(procinst.BizForm, new(FlowRedisRunCmdBizForm))
|
||||
if err != nil {
|
||||
return nil, errorx.NewBiz("业务表单信息解析失败: %s", err.Error())
|
||||
return nil, errorx.NewBiz("failed to parse the business form information: %s", err.Error())
|
||||
}
|
||||
|
||||
redisConn, err := r.GetRedisConn(runCmdParam.Id, runCmdParam.Db)
|
||||
@@ -283,7 +283,7 @@ func (r *redisAppImpl) FlowBizHandle(ctx context.Context, bizHandleParam *flowap
|
||||
})
|
||||
|
||||
if hasErr {
|
||||
return handleRes, errorx.NewBiz("存在命令执行失败")
|
||||
return handleRes, errorx.NewBizI(ctx, imsg.ErrHasRunFailCmd)
|
||||
}
|
||||
return handleRes, nil
|
||||
}
|
||||
|
||||
13
server/internal/redis/imsg/en.go
Normal file
13
server/internal/redis/imsg/en.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package imsg
|
||||
|
||||
import "mayfly-go/pkg/i18n"
|
||||
|
||||
var En = map[i18n.MsgId]string{
|
||||
LogRedisSave: "Redis - Save",
|
||||
LogRedisDelete: "Redis - Delete",
|
||||
LogRedisRunCmd: "Redis - Run Cmd",
|
||||
|
||||
ErrRedisInfoExist: "The Redis information already exists",
|
||||
ErrSubmitFlowRunCmd: "This operation needs to submit a work ticket for approval",
|
||||
ErrHasRunFailCmd: "A command failed to execute",
|
||||
}
|
||||
21
server/internal/redis/imsg/imsg.go
Normal file
21
server/internal/redis/imsg/imsg.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package imsg
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/common/consts"
|
||||
"mayfly-go/pkg/i18n"
|
||||
)
|
||||
|
||||
func init() {
|
||||
i18n.AppendLangMsg(i18n.Zh_CN, Zh_CN)
|
||||
i18n.AppendLangMsg(i18n.En, En)
|
||||
}
|
||||
|
||||
const (
|
||||
LogRedisSave = iota + consts.ImsgNumRedis
|
||||
LogRedisDelete
|
||||
LogRedisRunCmd
|
||||
|
||||
ErrRedisInfoExist
|
||||
ErrSubmitFlowRunCmd
|
||||
ErrHasRunFailCmd
|
||||
)
|
||||
13
server/internal/redis/imsg/zh_cn.go
Normal file
13
server/internal/redis/imsg/zh_cn.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package imsg
|
||||
|
||||
import "mayfly-go/pkg/i18n"
|
||||
|
||||
var Zh_CN = map[i18n.MsgId]string{
|
||||
LogRedisSave: "Redis-保存",
|
||||
LogRedisDelete: "Redis-删除",
|
||||
LogRedisRunCmd: "Redis-执行命令",
|
||||
|
||||
ErrRedisInfoExist: "该Redis信息已存在",
|
||||
ErrSubmitFlowRunCmd: "该操作需要提交工单审批执行",
|
||||
ErrHasRunFailCmd: "存在执行失败的命令",
|
||||
}
|
||||
@@ -50,7 +50,7 @@ func (r *RedisConn) Close() {
|
||||
mode := r.Info.Mode
|
||||
if mode == StandaloneMode || mode == SentinelMode {
|
||||
if err := r.Cli.Close(); err != nil {
|
||||
logx.Errorf("关闭redis单机实例[%s]连接失败: %s", r.Id, err.Error())
|
||||
logx.Errorf("close redis standalone instance [%s] connection failed: %s", r.Id, err.Error())
|
||||
}
|
||||
r.Cli = nil
|
||||
return
|
||||
@@ -58,7 +58,7 @@ func (r *RedisConn) Close() {
|
||||
|
||||
if mode == ClusterMode {
|
||||
if err := r.ClusterCli.Close(); err != nil {
|
||||
logx.Errorf("关闭redis集群实例[%s]连接失败: %s", r.Id, err.Error())
|
||||
logx.Errorf("close redis cluster instance [%s] connection failed: %s", r.Id, err.Error())
|
||||
}
|
||||
r.ClusterCli = nil
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
var connCache = cache.NewTimedCache(consts.RedisConnExpireTime, 5*time.Second).
|
||||
WithUpdateAccessTime(true).
|
||||
OnEvicted(func(key any, value any) {
|
||||
logx.Info(fmt.Sprintf("删除redis连接缓存 id = %s", key))
|
||||
logx.Info(fmt.Sprintf("remove the redis connection cache id = %s", key))
|
||||
value.(*RedisConn).Close()
|
||||
})
|
||||
|
||||
|
||||
@@ -70,10 +70,10 @@ func (re *RedisInfo) connStandalone() (*RedisConn, error) {
|
||||
_, e := cli.Ping(context.Background()).Result()
|
||||
if e != nil {
|
||||
cli.Close()
|
||||
return nil, errorx.NewBiz("redis连接失败: %s", e.Error())
|
||||
return nil, errorx.NewBiz("redis standalone connection failed: %s", e.Error())
|
||||
}
|
||||
|
||||
logx.Infof("连接redis standalone: %s/%d", re.Host, re.Db)
|
||||
logx.Infof("redis standalone connection: %s/%d", re.Host, re.Db)
|
||||
|
||||
rc := &RedisConn{Id: getConnId(re.Id, re.Db), Info: re}
|
||||
rc.Cli = cli
|
||||
@@ -95,10 +95,10 @@ func (re *RedisInfo) connCluster() (*RedisConn, error) {
|
||||
_, e := cli.Ping(context.Background()).Result()
|
||||
if e != nil {
|
||||
cli.Close()
|
||||
return nil, errorx.NewBiz("redis集群连接失败: %s", e.Error())
|
||||
return nil, errorx.NewBiz("redis cluster connection failed: %s", e.Error())
|
||||
}
|
||||
|
||||
logx.Infof("连接redis cluster: %s/%d", re.Host, re.Db)
|
||||
logx.Infof("redis cluster connection: %s/%d", re.Host, re.Db)
|
||||
|
||||
rc := &RedisConn{Id: getConnId(re.Id, re.Db), Info: re}
|
||||
rc.ClusterCli = cli
|
||||
@@ -128,10 +128,10 @@ func (re *RedisInfo) connSentinel() (*RedisConn, error) {
|
||||
_, e := cli.Ping(context.Background()).Result()
|
||||
if e != nil {
|
||||
cli.Close()
|
||||
return nil, errorx.NewBiz("redis sentinel连接失败: %s", e.Error())
|
||||
return nil, errorx.NewBiz("redis sentinel connection failed: %s", e.Error())
|
||||
}
|
||||
|
||||
logx.Infof("连接redis sentinel: %s/%d", re.Host, re.Db)
|
||||
logx.Infof("redis sentinel connection: %s/%d", re.Host, re.Db)
|
||||
|
||||
rc := &RedisConn{Id: getConnId(re.Id, re.Db), Info: re}
|
||||
rc.Cli = cli
|
||||
|
||||
@@ -2,6 +2,7 @@ package router
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/redis/api"
|
||||
"mayfly-go/internal/redis/imsg"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/ioc"
|
||||
"mayfly-go/pkg/req"
|
||||
@@ -26,15 +27,15 @@ func InitRedisRouter(router *gin.RouterGroup) {
|
||||
|
||||
req.NewPost("/test-conn", rs.TestConn),
|
||||
|
||||
req.NewPost("", rs.Save).Log(req.NewLogSave("redis-保存信息")),
|
||||
req.NewPost("", rs.Save).Log(req.NewLogSaveI(imsg.LogRedisSave)),
|
||||
|
||||
req.NewDelete(":id", rs.DeleteRedis).Log(req.NewLogSave("redis-删除信息")),
|
||||
req.NewDelete(":id", rs.DeleteRedis).Log(req.NewLogSaveI(imsg.LogRedisDelete)),
|
||||
|
||||
req.NewGet("/:id/info", rs.RedisInfo),
|
||||
|
||||
req.NewGet(":id/cluster-info", rs.ClusterInfo),
|
||||
|
||||
req.NewPost(":id/:db/run-cmd", rs.RunCmd).Log(req.NewLogSave("redis-runCmd")),
|
||||
req.NewPost(":id/:db/run-cmd", rs.RunCmd).Log(req.NewLogSaveI(imsg.LogRedisRunCmd)),
|
||||
|
||||
// 获取指定redis keys
|
||||
req.NewPost(":id/:db/scan", rs.ScanKeys),
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"mayfly-go/internal/sys/application"
|
||||
"mayfly-go/internal/sys/consts"
|
||||
"mayfly-go/internal/sys/domain/entity"
|
||||
"mayfly-go/internal/sys/imsg"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/contextx"
|
||||
"mayfly-go/pkg/model"
|
||||
@@ -15,7 +16,6 @@ import (
|
||||
"mayfly-go/pkg/utils/collx"
|
||||
"mayfly-go/pkg/utils/cryptox"
|
||||
"mayfly-go/pkg/utils/structx"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -62,28 +62,30 @@ func (a *Account) GetPermissions(rc *req.Ctx) {
|
||||
}
|
||||
|
||||
func (a *Account) ChangePassword(rc *req.Ctx) {
|
||||
ctx := rc.MetaCtx
|
||||
|
||||
form := req.BindJsonAndValid(rc, new(form.AccountChangePasswordForm))
|
||||
|
||||
originOldPwd, err := cryptox.DefaultRsaDecrypt(form.OldPassword, true)
|
||||
biz.ErrIsNilAppendErr(err, "解密旧密码错误: %s")
|
||||
biz.ErrIsNilAppendErr(err, "Wrong to decrypt old password: %s")
|
||||
|
||||
account := &entity.Account{Username: form.Username}
|
||||
err = a.AccountApp.GetByCond(model.NewModelCond(account).Columns("Id", "Username", "Password", "Status"))
|
||||
biz.ErrIsNil(err, "旧密码错误")
|
||||
biz.IsTrue(cryptox.CheckPwdHash(originOldPwd, account.Password), "旧密码错误")
|
||||
biz.IsTrue(account.IsEnable(), "该账号不可用")
|
||||
biz.ErrIsNilI(ctx, err, imsg.ErrOldPasswordWrong)
|
||||
biz.IsTrueI(ctx, cryptox.CheckPwdHash(originOldPwd, account.Password), imsg.ErrOldPasswordWrong)
|
||||
biz.IsTrue(account.IsEnable(), "This account is not available")
|
||||
|
||||
originNewPwd, err := cryptox.DefaultRsaDecrypt(form.NewPassword, true)
|
||||
biz.ErrIsNilAppendErr(err, "解密新密码错误: %s")
|
||||
biz.IsTrue(utils.CheckAccountPasswordLever(originNewPwd), "密码强度必须8位以上且包含字⺟⼤⼩写+数字+特殊符号")
|
||||
biz.ErrIsNilAppendErr(err, "Wrong to decrypt new password: %s")
|
||||
biz.IsTrueI(ctx, utils.CheckAccountPasswordLever(originNewPwd), imsg.ErrAccountPasswordNotFollowRule)
|
||||
|
||||
updateAccount := new(entity.Account)
|
||||
updateAccount.Id = account.Id
|
||||
updateAccount.Password = cryptox.PwdHash(originNewPwd)
|
||||
biz.ErrIsNil(a.AccountApp.Update(rc.MetaCtx, updateAccount), "更新账号密码失败")
|
||||
biz.ErrIsNilAppendErr(a.AccountApp.Update(ctx, updateAccount), "failed to update account password: %s")
|
||||
|
||||
// 赋值loginAccount 主要用于记录操作日志,因为操作日志保存请求上下文没有该信息不保存日志
|
||||
contextx.WithLoginAccount(rc.MetaCtx, &model.LoginAccount{
|
||||
contextx.WithLoginAccount(ctx, &model.LoginAccount{
|
||||
Id: account.Id,
|
||||
Username: account.Username,
|
||||
})
|
||||
@@ -103,19 +105,20 @@ func (a *Account) UpdateAccount(rc *req.Ctx) {
|
||||
// 账号id为登录者账号
|
||||
updateAccount.Id = rc.GetLoginAccount().Id
|
||||
|
||||
ctx := rc.MetaCtx
|
||||
if updateAccount.Password != "" {
|
||||
biz.IsTrue(utils.CheckAccountPasswordLever(updateAccount.Password), "密码强度必须8位以上且包含字⺟⼤⼩写+数字+特殊符号")
|
||||
biz.IsTrueI(ctx, utils.CheckAccountPasswordLever(updateAccount.Password), imsg.ErrAccountPasswordNotFollowRule)
|
||||
updateAccount.Password = cryptox.PwdHash(updateAccount.Password)
|
||||
}
|
||||
|
||||
oldAcc, err := a.AccountApp.GetById(updateAccount.Id)
|
||||
biz.ErrIsNil(err, "账号信息不存在")
|
||||
biz.ErrIsNilAppendErr(err, "Account does not exist: %s")
|
||||
// 账号创建十分钟内允许修改用户名(兼容oauth2首次登录修改用户名),否则不允许修改
|
||||
if oldAcc.CreateTime.Add(10 * time.Minute).Before(time.Now()) {
|
||||
// 禁止更新用户名,防止误传被更新
|
||||
updateAccount.Username = ""
|
||||
}
|
||||
biz.ErrIsNil(a.AccountApp.Update(rc.MetaCtx, updateAccount))
|
||||
biz.ErrIsNil(a.AccountApp.Update(ctx, updateAccount))
|
||||
}
|
||||
|
||||
/** 后台账号操作 **/
|
||||
@@ -149,7 +152,7 @@ func (a *Account) SimpleAccounts(rc *req.Ctx) {
|
||||
func (a *Account) AccountDetail(rc *req.Ctx) {
|
||||
accountId := uint64(rc.PathParamInt("id"))
|
||||
account, err := a.AccountApp.GetById(accountId)
|
||||
biz.ErrIsNil(err, "账号不存在")
|
||||
biz.ErrIsNilAppendErr(err, "Account does not exist: %s")
|
||||
accountvo := new(vo.SimpleAccountVO)
|
||||
structx.Copy(accountvo, account)
|
||||
|
||||
@@ -164,17 +167,21 @@ func (a *Account) SaveAccount(rc *req.Ctx) {
|
||||
|
||||
form.Password = "*****"
|
||||
rc.ReqParam = form
|
||||
ctx := rc.MetaCtx
|
||||
|
||||
if account.Id == 0 {
|
||||
biz.NotEmpty(account.Password, "password is required")
|
||||
biz.IsTrueI(ctx, utils.CheckAccountPasswordLever(account.Password), imsg.ErrAccountPasswordNotFollowRule)
|
||||
account.Password = cryptox.PwdHash(account.Password)
|
||||
biz.ErrIsNil(a.AccountApp.Create(rc.MetaCtx, account))
|
||||
} else {
|
||||
if account.Password != "" {
|
||||
biz.IsTrue(utils.CheckAccountPasswordLever(account.Password), "密码强度必须8位以上且包含字⺟⼤⼩写+数字+特殊符号")
|
||||
biz.IsTrueI(ctx, utils.CheckAccountPasswordLever(account.Password), imsg.ErrAccountPasswordNotFollowRule)
|
||||
account.Password = cryptox.PwdHash(account.Password)
|
||||
}
|
||||
// 更新操作不允许修改用户名、防止误传更新
|
||||
account.Username = ""
|
||||
biz.ErrIsNil(a.AccountApp.Update(rc.MetaCtx, account))
|
||||
biz.ErrIsNil(a.AccountApp.Update(ctx, account))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -196,9 +203,7 @@ func (a *Account) DeleteAccount(rc *req.Ctx) {
|
||||
ids := strings.Split(idsStr, ",")
|
||||
|
||||
for _, v := range ids {
|
||||
value, err := strconv.Atoi(v)
|
||||
biz.ErrIsNilAppendErr(err, "string类型转换为int异常: %s")
|
||||
biz.ErrIsNilAppendErr(a.AccountApp.Delete(rc.MetaCtx, uint64(value)), "删除失败:%s")
|
||||
biz.ErrIsNil(a.AccountApp.Delete(rc.MetaCtx, cast.ToUint64(v)))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,6 @@ import (
|
||||
|
||||
func GenerateCaptcha(rc *req.Ctx) {
|
||||
id, image, err := captcha.Generate()
|
||||
biz.ErrIsNilAppendErr(err, "获取验证码错误: %s")
|
||||
biz.ErrIsNilAppendErr(err, "failed to generate the CAPTCHA: %s")
|
||||
rc.ResData = collx.M{"base64Captcha": image, "cid": id}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,9 @@ import (
|
||||
"mayfly-go/internal/sys/application"
|
||||
"mayfly-go/internal/sys/domain/entity"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/config"
|
||||
"mayfly-go/pkg/req"
|
||||
"mayfly-go/pkg/utils/collx"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
@@ -22,7 +24,7 @@ func (c *Config) Configs(rc *req.Ctx) {
|
||||
|
||||
func (c *Config) GetConfigValueByKey(rc *req.Ctx) {
|
||||
key := rc.Query("key")
|
||||
biz.NotEmpty(key, "key不能为空")
|
||||
biz.NotEmpty(key, "key cannot be empty")
|
||||
|
||||
config := c.ConfigApp.GetConfig(key)
|
||||
// 判断是否为公开配置
|
||||
@@ -40,3 +42,9 @@ func (c *Config) SaveConfig(rc *req.Ctx) {
|
||||
rc.ReqParam = form
|
||||
biz.ErrIsNil(c.ConfigApp.Save(rc.MetaCtx, config))
|
||||
}
|
||||
|
||||
// GetServerConfig 获取当前系统启动配置
|
||||
func (c *Config) GetServerConfig(rc *req.Ctx) {
|
||||
conf := config.Conf
|
||||
rc.ResData = collx.Kvs("i18n", conf.Server.Lang)
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package form
|
||||
|
||||
type AccountCreateForm struct {
|
||||
Id uint64 `json:"id"`
|
||||
Name string `json:"name" binding:"required,max=16" msg:"required=姓名不能为空,max=姓名最大长度不能超过16位"`
|
||||
Name string `json:"name" binding:"required,max=16" msg:"required=name cannot be blank,max=The maximum length of a name cannot exceed 16 characters"`
|
||||
Username string `json:"username" binding:"pattern=account_username"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ func (r *Resource) GetAllResourceTree(rc *req.Ctx) {
|
||||
|
||||
func (r *Resource) GetById(rc *req.Ctx) {
|
||||
res, err := r.ResourceApp.GetById(uint64(rc.PathParamInt("id")))
|
||||
biz.ErrIsNil(err, "该资源不存在")
|
||||
biz.ErrIsNil(err, "The resource does not exist")
|
||||
rc.ResData = res
|
||||
}
|
||||
|
||||
@@ -63,3 +63,10 @@ func (r *Resource) Sort(rc *req.Ctx) {
|
||||
r.ResourceApp.Sort(rc.MetaCtx, sortE)
|
||||
}
|
||||
}
|
||||
|
||||
// GetResourceRoles
|
||||
func (r *Resource) GetResourceRoles(rc *req.Ctx) {
|
||||
rrs, err := r.ResourceApp.GetResourceRoles(uint64(rc.PathParamInt("id")))
|
||||
biz.ErrIsNil(err)
|
||||
rc.ResData = rrs
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/req"
|
||||
"mayfly-go/pkg/utils/collx"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/may-fly/cast"
|
||||
@@ -50,9 +49,7 @@ func (r *Role) DelRole(rc *req.Ctx) {
|
||||
ids := strings.Split(idsStr, ",")
|
||||
|
||||
for _, v := range ids {
|
||||
value, err := strconv.Atoi(v)
|
||||
biz.ErrIsNilAppendErr(err, "string类型转换为int异常: %s")
|
||||
r.RoleApp.DeleteRole(rc.MetaCtx, uint64(value))
|
||||
biz.ErrIsNil(r.RoleApp.DeleteRole(rc.MetaCtx, cast.ToUint64(v)))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,7 +76,7 @@ func (r *Role) SaveResource(rc *req.Ctx) {
|
||||
return cast.ToUint64(val)
|
||||
})
|
||||
|
||||
biz.ErrIsNilAppendErr(r.RoleApp.SaveRoleResource(rc.MetaCtx, form.Id, newIds), "保存角色资源失败: %s")
|
||||
biz.ErrIsNilAppendErr(r.RoleApp.SaveRoleResource(rc.MetaCtx, form.Id, newIds), "save role resource failed: %s")
|
||||
}
|
||||
|
||||
// 查看角色关联的用户
|
||||
|
||||
@@ -20,7 +20,7 @@ func (s *System) ConnectWs(g *gin.Context) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
errInfo := anyx.ToString(err)
|
||||
logx.Errorf("websocket连接失败: %s", errInfo)
|
||||
logx.Errorf("websocket connect error: %s", errInfo)
|
||||
if wsConn != nil {
|
||||
wsConn.WriteMessage(websocket.TextMessage, []byte(errInfo))
|
||||
wsConn.Close()
|
||||
@@ -28,14 +28,14 @@ func (s *System) ConnectWs(g *gin.Context) {
|
||||
}
|
||||
}()
|
||||
|
||||
biz.ErrIsNilAppendErr(err, "%s")
|
||||
biz.ErrIsNil(err)
|
||||
clientId := g.Query("clientId")
|
||||
biz.NotEmpty(clientId, "clientId不能为空")
|
||||
biz.NotEmpty(clientId, "clientId cannot be empty")
|
||||
|
||||
// 权限校验
|
||||
rc := req.NewCtxWithGin(g)
|
||||
err = req.PermissionHandler(rc)
|
||||
biz.ErrIsNil(err, "sys websocket没有权限连接")
|
||||
biz.ErrIsNil(err, "sys-websocket connect without permission")
|
||||
|
||||
// 登录账号信息
|
||||
la := rc.GetLoginAccount()
|
||||
|
||||
@@ -4,10 +4,10 @@ import (
|
||||
"context"
|
||||
"mayfly-go/internal/sys/domain/entity"
|
||||
"mayfly-go/internal/sys/domain/repository"
|
||||
"mayfly-go/internal/sys/imsg"
|
||||
"mayfly-go/pkg/base"
|
||||
"mayfly-go/pkg/errorx"
|
||||
"mayfly-go/pkg/model"
|
||||
"mayfly-go/pkg/utils/cryptox"
|
||||
)
|
||||
|
||||
type Account interface {
|
||||
@@ -39,10 +39,8 @@ func (a *accountAppImpl) GetPageList(condition *entity.AccountQuery, pageParam *
|
||||
|
||||
func (a *accountAppImpl) Create(ctx context.Context, account *entity.Account) error {
|
||||
if a.GetByCond(&entity.Account{Username: account.Username}) == nil {
|
||||
return errorx.NewBiz("该账号用户名已存在")
|
||||
return errorx.NewBizI(ctx, imsg.ErrUsernameExist)
|
||||
}
|
||||
// 默认密码为账号用户名
|
||||
account.Password = cryptox.PwdHash(account.Username)
|
||||
account.Status = entity.AccountEnable
|
||||
return a.Insert(ctx, account)
|
||||
}
|
||||
@@ -52,7 +50,7 @@ func (a *accountAppImpl) Update(ctx context.Context, account *entity.Account) er
|
||||
unAcc := &entity.Account{Username: account.Username}
|
||||
err := a.GetByCond(unAcc)
|
||||
if err == nil && unAcc.Id != account.Id {
|
||||
return errorx.NewBiz("该用户名已存在")
|
||||
return errorx.NewBizI(ctx, imsg.ErrUsernameExist)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ func (a *configAppImpl) Save(ctx context.Context, config *entity.Config) error {
|
||||
|
||||
oldConfig := a.GetConfig(config.Key)
|
||||
if oldConfig.Permission != "all" && !strings.Contains(oldConfig.Permission, config.Modifier) {
|
||||
return errorx.NewBiz("您无权修改该配置")
|
||||
return errorx.NewBiz("You do not have permission to modify the configuration")
|
||||
}
|
||||
|
||||
if err := a.UpdateById(ctx, config); err != nil {
|
||||
@@ -67,7 +67,7 @@ func (a *configAppImpl) GetConfig(key string) *entity.Config {
|
||||
}
|
||||
|
||||
if err := a.GetByCond(model.NewModelCond(config).Columns("Id", "Key", "Value", "Permission")); err != nil {
|
||||
logx.Warnf("不存在key = [%s] 的系统配置", key)
|
||||
logx.Warnf("There is no system configuration with key = [%s]", key)
|
||||
} else {
|
||||
cache.SetStr(SysConfigKeyPrefix+key, jsonx.ToStr(config), -1)
|
||||
}
|
||||
|
||||
12
server/internal/sys/application/dto/dto.go
Normal file
12
server/internal/sys/application/dto/dto.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package dto
|
||||
|
||||
import "time"
|
||||
|
||||
type ResourceRole struct {
|
||||
RoleId uint64 `json:"roleId"`
|
||||
RoleName string `json:"roleName"`
|
||||
RoleCode string `json:"roleCode"`
|
||||
RoleStatus int `json:"roleStatus"` // 角色状态
|
||||
AllocateTime *time.Time `json:"allocateTime"` // 分配时间
|
||||
Assigner string `json:"assigner"` // 分配人
|
||||
}
|
||||
@@ -3,8 +3,10 @@ package application
|
||||
import (
|
||||
"context"
|
||||
"mayfly-go/internal/common/consts"
|
||||
"mayfly-go/internal/sys/application/dto"
|
||||
"mayfly-go/internal/sys/domain/entity"
|
||||
"mayfly-go/internal/sys/domain/repository"
|
||||
"mayfly-go/internal/sys/imsg"
|
||||
"mayfly-go/pkg/base"
|
||||
"mayfly-go/pkg/errorx"
|
||||
"mayfly-go/pkg/model"
|
||||
@@ -26,14 +28,19 @@ type Resource interface {
|
||||
Sort(ctx context.Context, re *entity.Resource) error
|
||||
|
||||
GetAccountResources(accountId uint64, toEntity any) error
|
||||
|
||||
GetResourceRoles(resourceId uint64) ([]*dto.ResourceRole, error)
|
||||
}
|
||||
|
||||
type resourceAppImpl struct {
|
||||
base.AppImpl[*entity.Resource, repository.Resource]
|
||||
|
||||
roleResourceRepo repository.RoleResource `inject:"RoleResourceRepo"`
|
||||
roleApp Role `inject:"RoleApp"`
|
||||
}
|
||||
|
||||
var _ (Resource) = (*resourceAppImpl)(nil)
|
||||
|
||||
// 注入ResourceRepo
|
||||
func (r *resourceAppImpl) InjectResourceRepo(repo repository.Resource) {
|
||||
r.Repo = repo
|
||||
@@ -45,11 +52,11 @@ func (r *resourceAppImpl) Save(ctx context.Context, resource *entity.Resource) e
|
||||
if resource.Code != "" {
|
||||
oldRes, err := r.GetById(resource.Id, "Code")
|
||||
if err != nil {
|
||||
return errorx.NewBiz("更新失败, 该资源不存在")
|
||||
return errorx.NewBiz("Resource does not exist")
|
||||
}
|
||||
// 如果修改了code,则校验新code是否存在
|
||||
if oldRes.Code != resource.Code {
|
||||
if err := r.checkCode(resource.Code); err != nil {
|
||||
if err := r.checkCode(ctx, resource.Code); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -62,7 +69,7 @@ func (r *resourceAppImpl) Save(ctx context.Context, resource *entity.Resource) e
|
||||
if pid := resource.Pid; pid != 0 {
|
||||
pResource, err := r.GetById(uint64(pid))
|
||||
if err != nil {
|
||||
return errorx.NewBiz("该父资源不存在")
|
||||
return errorx.NewBiz("pid does not exist")
|
||||
}
|
||||
resource.UiPath = pResource.UiPath + ui + entity.ResourceUiPathSp
|
||||
} else {
|
||||
@@ -72,7 +79,7 @@ func (r *resourceAppImpl) Save(ctx context.Context, resource *entity.Resource) e
|
||||
if resource.Status == 0 {
|
||||
resource.Status = entity.ResourceStatusEnable
|
||||
}
|
||||
if err := r.checkCode(resource.Code); err != nil {
|
||||
if err := r.checkCode(ctx, resource.Code); err != nil {
|
||||
return err
|
||||
}
|
||||
resource.Weight = int(time.Now().Unix())
|
||||
@@ -82,7 +89,7 @@ func (r *resourceAppImpl) Save(ctx context.Context, resource *entity.Resource) e
|
||||
func (r *resourceAppImpl) ChangeStatus(ctx context.Context, resourceId uint64, status int8) error {
|
||||
resource, err := r.GetById(resourceId)
|
||||
if err != nil {
|
||||
return errorx.NewBiz("资源不存在")
|
||||
return errorx.NewBiz("Resource does not exist")
|
||||
}
|
||||
resource.Status = status
|
||||
return r.GetRepo().UpdateByUiPathLike(resource)
|
||||
@@ -91,7 +98,7 @@ func (r *resourceAppImpl) ChangeStatus(ctx context.Context, resourceId uint64, s
|
||||
func (r *resourceAppImpl) Sort(ctx context.Context, sortResource *entity.Resource) error {
|
||||
resource, err := r.GetById(sortResource.Id)
|
||||
if err != nil {
|
||||
return errorx.NewBiz("资源不存在")
|
||||
return errorx.NewBiz("Resource does not exist")
|
||||
}
|
||||
|
||||
// 未改变父节点,则更新排序值即可
|
||||
@@ -117,7 +124,7 @@ func (r *resourceAppImpl) Sort(ctx context.Context, sortResource *entity.Resourc
|
||||
if sortResource.Pid != 0 {
|
||||
newParentResource, err := r.GetById(uint64(sortResource.Pid))
|
||||
if err != nil {
|
||||
return errorx.NewBiz("父资源不存在")
|
||||
return errorx.NewBiz("pid does not exist")
|
||||
}
|
||||
newParentResourceUiPath = newParentResource.UiPath
|
||||
}
|
||||
@@ -148,12 +155,12 @@ func (r *resourceAppImpl) Sort(ctx context.Context, sortResource *entity.Resourc
|
||||
return r.UpdateByCond(ctx, updateMap, condition)
|
||||
}
|
||||
|
||||
func (r *resourceAppImpl) checkCode(code string) error {
|
||||
func (r *resourceAppImpl) checkCode(ctx context.Context, code string) error {
|
||||
if strings.Contains(code, ",") {
|
||||
return errorx.NewBiz("code不能包含','")
|
||||
return errorx.NewBizI(ctx, imsg.ErrResourceCodeInvalid)
|
||||
}
|
||||
if r.CountByCond(&entity.Resource{Code: code}) != 0 {
|
||||
return errorx.NewBiz("该code已存在")
|
||||
return errorx.NewBizI(ctx, imsg.ErrResourceCodeExist)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -161,7 +168,7 @@ func (r *resourceAppImpl) checkCode(code string) error {
|
||||
func (r *resourceAppImpl) Delete(ctx context.Context, id uint64) error {
|
||||
resource, err := r.GetById(id)
|
||||
if err != nil {
|
||||
return errorx.NewBiz("资源不存在")
|
||||
return errorx.NewBiz("Resource does not exist")
|
||||
}
|
||||
|
||||
// 删除当前节点及其所有子节点
|
||||
@@ -185,3 +192,29 @@ func (r *resourceAppImpl) GetAccountResources(accountId uint64, toEntity any) er
|
||||
|
||||
return r.GetRepo().GetAccountResources(accountId, toEntity)
|
||||
}
|
||||
|
||||
func (r *resourceAppImpl) GetResourceRoles(resourceId uint64) ([]*dto.ResourceRole, error) {
|
||||
rr, err := r.roleApp.GetResourceRoles(resourceId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
roleId2Rr := collx.ArrayToMap[*entity.RoleResource, uint64](rr, func(val *entity.RoleResource) uint64 { return val.RoleId })
|
||||
|
||||
roleIds := collx.MapKeys(roleId2Rr)
|
||||
roles, err := r.roleApp.GetByIds(roleIds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return collx.ArrayMap[*entity.Role, *dto.ResourceRole](roles, func(val *entity.Role) *dto.ResourceRole {
|
||||
role := roleId2Rr[val.Id]
|
||||
return &dto.ResourceRole{
|
||||
RoleId: val.Id,
|
||||
RoleName: val.Name,
|
||||
RoleCode: val.Code,
|
||||
RoleStatus: val.Status,
|
||||
AllocateTime: role.CreateTime,
|
||||
Assigner: role.Creator,
|
||||
}
|
||||
}), nil
|
||||
}
|
||||
|
||||
@@ -28,6 +28,8 @@ type Role interface {
|
||||
|
||||
GetRoleResources(roleId uint64, toEntity any)
|
||||
|
||||
GetResourceRoles(resourceId uint64) ([]*entity.RoleResource, error)
|
||||
|
||||
// 保存角色资源关联记录
|
||||
SaveRoleResource(ctx context.Context, roleId uint64, resourceIds []uint64) error
|
||||
|
||||
@@ -48,6 +50,8 @@ type roleAppImpl struct {
|
||||
roleResourceRepo repository.RoleResource `inject:"RoleResourceRepo"`
|
||||
}
|
||||
|
||||
var _ (Role) = (*roleAppImpl)(nil)
|
||||
|
||||
func (r *roleAppImpl) InjectRoleRepo(repo repository.Role) {
|
||||
r.Repo = repo
|
||||
}
|
||||
@@ -90,6 +94,10 @@ func (m *roleAppImpl) GetRoleResources(roleId uint64, toEntity any) {
|
||||
m.roleResourceRepo.GetRoleResources(roleId, toEntity)
|
||||
}
|
||||
|
||||
func (m *roleAppImpl) GetResourceRoles(resourceId uint64) ([]*entity.RoleResource, error) {
|
||||
return m.roleResourceRepo.SelectByCond(&entity.RoleResource{ResourceId: resourceId})
|
||||
}
|
||||
|
||||
func (m *roleAppImpl) SaveRoleResource(ctx context.Context, roleId uint64, resourceIds []uint64) error {
|
||||
oIds := m.GetRoleResourceIds(roleId)
|
||||
|
||||
@@ -129,7 +137,7 @@ func (m *roleAppImpl) RelateAccountRole(ctx context.Context, accountId, roleId u
|
||||
|
||||
err := m.accountRoleRepo.GetByCond(accountRole)
|
||||
if err == nil {
|
||||
return errorx.NewBiz("该用户已拥有该权限")
|
||||
return errorx.NewBiz("The user already owns the role")
|
||||
}
|
||||
|
||||
la := contextx.GetLoginAccount(ctx)
|
||||
|
||||
34
server/internal/sys/imsg/en.go
Normal file
34
server/internal/sys/imsg/en.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package imsg
|
||||
|
||||
import "mayfly-go/pkg/i18n"
|
||||
|
||||
var En = map[i18n.MsgId]string{
|
||||
// account
|
||||
LogAccountCreate: "Create an account",
|
||||
LogChangePassword: "Change password",
|
||||
LogAccountChangeStatus: "Change account status",
|
||||
LogResetOtpSecret: "Reset otp secret",
|
||||
LogAccountDelete: "Deleting an account",
|
||||
LogAssignUserRoles: "Assigning user roles",
|
||||
|
||||
ErrUsernameExist: "Username is already exists",
|
||||
ErrOldPasswordWrong: "Old password wrong",
|
||||
ErrAccountPasswordNotFollowRule: "Password must be at least 8 characters long and contain upper/lower case + number + special symbols",
|
||||
|
||||
// role
|
||||
LogRoleSave: "Save role",
|
||||
LogRoleDelete: "Deleting role",
|
||||
LogAssignRoleResource: "Role association menu permissions",
|
||||
|
||||
// menu resource
|
||||
LogResourceSave: "Save menu permissions",
|
||||
LogResourceDelete: "Deleting menu permissions",
|
||||
LogChangeResourceStatus: "Change menu permissions status",
|
||||
LogSortResource: "Sort menu permissions",
|
||||
|
||||
ErrResourceCodeInvalid: "code cannot contain ','",
|
||||
ErrResourceCodeExist: "The code already exists",
|
||||
|
||||
// sysconf
|
||||
LogSaveSysConfig: "Save system configuration",
|
||||
}
|
||||
42
server/internal/sys/imsg/imsg.go
Normal file
42
server/internal/sys/imsg/imsg.go
Normal file
@@ -0,0 +1,42 @@
|
||||
package imsg
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/common/consts"
|
||||
"mayfly-go/pkg/i18n"
|
||||
)
|
||||
|
||||
func init() {
|
||||
i18n.AppendLangMsg(i18n.Zh_CN, Zh_CN)
|
||||
i18n.AppendLangMsg(i18n.En, En)
|
||||
}
|
||||
|
||||
const (
|
||||
// account
|
||||
LogAccountCreate = iota + consts.ImsgNumSys
|
||||
LogChangePassword
|
||||
LogAccountChangeStatus
|
||||
LogResetOtpSecret
|
||||
LogAccountDelete
|
||||
LogAssignUserRoles
|
||||
|
||||
ErrUsernameExist
|
||||
ErrOldPasswordWrong
|
||||
ErrAccountPasswordNotFollowRule
|
||||
|
||||
// role
|
||||
LogRoleSave
|
||||
LogRoleDelete
|
||||
LogAssignRoleResource
|
||||
|
||||
// menu resource
|
||||
LogResourceSave
|
||||
LogResourceDelete
|
||||
LogChangeResourceStatus
|
||||
LogSortResource
|
||||
|
||||
ErrResourceCodeInvalid
|
||||
ErrResourceCodeExist
|
||||
|
||||
// sysconf
|
||||
LogSaveSysConfig
|
||||
)
|
||||
34
server/internal/sys/imsg/zh_cn.go
Normal file
34
server/internal/sys/imsg/zh_cn.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package imsg
|
||||
|
||||
import "mayfly-go/pkg/i18n"
|
||||
|
||||
var Zh_CN = map[i18n.MsgId]string{
|
||||
// account
|
||||
LogAccountCreate: "创建账号",
|
||||
LogChangePassword: "修改密码",
|
||||
LogAccountChangeStatus: "修改账号状态",
|
||||
LogResetOtpSecret: "重置OTP密钥",
|
||||
LogAccountDelete: "删除账号",
|
||||
LogAssignUserRoles: "关联用户角色",
|
||||
|
||||
ErrUsernameExist: "用户名已存在",
|
||||
ErrOldPasswordWrong: "旧密码错误",
|
||||
ErrAccountPasswordNotFollowRule: "密码强度必须8个字符以上且包含字⺟⼤⼩写+数字+特殊符号",
|
||||
|
||||
// role
|
||||
LogRoleSave: "保存角色",
|
||||
LogRoleDelete: "删除角色",
|
||||
LogAssignRoleResource: "角色关联菜单权限",
|
||||
|
||||
// menu resource
|
||||
LogResourceSave: "保存菜单权限",
|
||||
LogResourceDelete: "删除菜单权限",
|
||||
LogChangeResourceStatus: "修改菜单权限状态",
|
||||
LogSortResource: "菜单权限排序",
|
||||
|
||||
ErrResourceCodeInvalid: "code不能包含','",
|
||||
ErrResourceCodeExist: "该编号已存在",
|
||||
|
||||
// sysconf
|
||||
LogSaveSysConfig: "保存系统配置",
|
||||
}
|
||||
@@ -29,38 +29,48 @@ func (r *resourceRepoImpl) UpdateByUiPathLike(resource *entity.Resource) error {
|
||||
}
|
||||
|
||||
func (r *resourceRepoImpl) GetAccountResources(accountId uint64, toEntity any) error {
|
||||
sql := `SELECT
|
||||
m.id,
|
||||
m.pid,
|
||||
m.weight,
|
||||
m.name,
|
||||
m.code,
|
||||
m.meta,
|
||||
m.type,
|
||||
m.status
|
||||
FROM
|
||||
t_sys_resource m
|
||||
WHERE
|
||||
m.status = 1 AND m.is_deleted = 0
|
||||
AND m.id IN (
|
||||
SELECT DISTINCT
|
||||
( rmb.resource_id )
|
||||
FROM
|
||||
t_sys_account_role p
|
||||
JOIN t_sys_role r ON p.role_Id = r.id
|
||||
AND p.account_id = ? AND p.is_deleted = 0
|
||||
AND r.STATUS = 1 AND r.is_deleted = 0
|
||||
JOIN t_sys_role_resource rmb ON rmb.role_id = r.id AND rmb.is_deleted = 0 UNION
|
||||
SELECT
|
||||
r.id
|
||||
FROM
|
||||
t_sys_resource r
|
||||
JOIN t_sys_role_resource rr ON r.id = rr.resource_id
|
||||
JOIN t_sys_role ro ON rr.role_id = ro.id
|
||||
AND ro.status = 1 AND ro.code LIKE 'COMMON%' AND ro.is_deleted = 0 AND rr.is_deleted = 0
|
||||
)
|
||||
ORDER BY
|
||||
m.pid ASC,
|
||||
m.weight ASC`
|
||||
sql := `
|
||||
SELECT
|
||||
m.id,
|
||||
m.pid,
|
||||
m.weight,
|
||||
m.name,
|
||||
m.code,
|
||||
m.meta,
|
||||
m.type,
|
||||
m.status
|
||||
FROM
|
||||
t_sys_resource m
|
||||
JOIN (
|
||||
SELECT DISTINCT rmb.resource_id
|
||||
FROM
|
||||
t_sys_account_role p
|
||||
JOIN t_sys_role r ON p.role_Id = r.id
|
||||
JOIN t_sys_role_resource rmb ON rmb.role_id = r.id
|
||||
WHERE
|
||||
p.account_id = ?
|
||||
AND p.is_deleted = 0
|
||||
AND r.status = 1
|
||||
AND r.is_deleted = 0
|
||||
AND rmb.is_deleted = 0
|
||||
UNION
|
||||
SELECT
|
||||
r.id
|
||||
FROM
|
||||
t_sys_resource r
|
||||
JOIN t_sys_role_resource rr ON r.id = rr.resource_id
|
||||
JOIN t_sys_role ro ON rr.role_id = ro.id
|
||||
WHERE
|
||||
ro.status = 1
|
||||
AND ro.code LIKE 'COMMON%'
|
||||
AND ro.is_deleted = 0
|
||||
AND rr.is_deleted = 0
|
||||
) AS subquery ON m.id = subquery.resource_id
|
||||
WHERE
|
||||
m.status = 1
|
||||
AND m.is_deleted = 0
|
||||
ORDER BY
|
||||
m.pid ASC,
|
||||
m.weight ASC;`
|
||||
return r.SelectBySql(sql, toEntity, accountId)
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user