mirror of
https://gitee.com/dromara/mayfly-go
synced 2025-11-03 16:00:25 +08:00
refactor: 数据库实例与凭证关联至标签&其他问题修复重构等
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/common/consts"
|
||||
"mayfly-go/internal/db/application"
|
||||
tagapp "mayfly-go/internal/tag/application"
|
||||
tagentity "mayfly-go/internal/tag/domain/entity"
|
||||
"mayfly-go/pkg/req"
|
||||
"mayfly-go/pkg/utils/collx"
|
||||
)
|
||||
@@ -15,9 +15,10 @@ type Dashbord struct {
|
||||
|
||||
func (m *Dashbord) Dashbord(rc *req.Ctx) {
|
||||
accountId := rc.GetLoginAccount().Id
|
||||
dbNum := len(m.TagTreeApp.GetAccountTagCodes(accountId, consts.ResourceTypeDb, ""))
|
||||
|
||||
tagCodePaths := m.TagTreeApp.GetAccountTagCodePaths(accountId, tagentity.TagTypeDbName, "")
|
||||
|
||||
rc.ResData = collx.M{
|
||||
"dbNum": dbNum,
|
||||
"dbNum": len(tagCodePaths),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ func (d *Db) Dbs(rc *req.Ctx) {
|
||||
queryCond, page := req.BindQueryAndPage[*entity.DbQuery](rc, new(entity.DbQuery))
|
||||
|
||||
// 不存在可访问标签id,即没有可操作数据
|
||||
codes := d.TagApp.GetAccountTagCodes(rc.GetLoginAccount().Id, consts.ResourceTypeDb, queryCond.TagPath)
|
||||
codes := d.TagApp.GetAccountTagCodes(rc.GetLoginAccount().Id, int8(tagentity.TagTypeDbName), queryCond.TagPath)
|
||||
if len(codes) == 0 {
|
||||
rc.ResData = model.EmptyPageResult[any]()
|
||||
return
|
||||
@@ -56,7 +56,7 @@ func (d *Db) Dbs(rc *req.Ctx) {
|
||||
biz.ErrIsNil(err)
|
||||
|
||||
// 填充标签信息
|
||||
d.TagApp.FillTagInfo(tagentity.TagType(consts.ResourceTypeDb), collx.ArrayMap(dbvos, func(dbvo *vo.DbListVO) tagentity.ITagResource {
|
||||
d.TagApp.FillTagInfo(tagentity.TagTypeDbName, collx.ArrayMap(dbvos, func(dbvo *vo.DbListVO) tagentity.ITagResource {
|
||||
return dbvo
|
||||
})...)
|
||||
|
||||
@@ -69,7 +69,7 @@ func (d *Db) Save(rc *req.Ctx) {
|
||||
|
||||
rc.ReqParam = form
|
||||
|
||||
biz.ErrIsNil(d.DbApp.SaveDb(rc.MetaCtx, db, form.TagId...))
|
||||
biz.ErrIsNil(d.DbApp.SaveDb(rc.MetaCtx, db))
|
||||
}
|
||||
|
||||
func (d *Db) DeleteDb(rc *req.Ctx) {
|
||||
@@ -81,7 +81,7 @@ func (d *Db) DeleteDb(rc *req.Ctx) {
|
||||
for _, v := range ids {
|
||||
dbId := cast.ToUint64(v)
|
||||
biz.NotBlank(dbId, "存在错误dbId")
|
||||
d.DbApp.Delete(ctx, dbId)
|
||||
biz.ErrIsNil(d.DbApp.Delete(ctx, dbId))
|
||||
// 删除该库的sql执行记录
|
||||
d.DbSqlExecApp.DeleteBy(ctx, &entity.DbSqlExec{DbId: dbId})
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
tagapp "mayfly-go/internal/tag/application"
|
||||
tagentity "mayfly-go/internal/tag/domain/entity"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/model"
|
||||
"mayfly-go/pkg/req"
|
||||
"mayfly-go/pkg/utils/collx"
|
||||
"strconv"
|
||||
@@ -20,6 +21,7 @@ type Instance struct {
|
||||
InstanceApp application.Instance `inject:"DbInstanceApp"`
|
||||
DbApp application.Db `inject:""`
|
||||
ResourceAuthCertApp tagapp.ResourceAuthCert `inject:""`
|
||||
TagApp tagapp.TagTree `inject:"TagTreeApp"`
|
||||
}
|
||||
|
||||
// Instances 获取数据库实例信息
|
||||
@@ -27,15 +29,30 @@ type Instance struct {
|
||||
func (d *Instance) Instances(rc *req.Ctx) {
|
||||
queryCond, page := req.BindQueryAndPage[*entity.InstanceQuery](rc, new(entity.InstanceQuery))
|
||||
|
||||
tagCodePaths := d.TagApp.GetAccountTagCodePaths(rc.GetLoginAccount().Id, tagentity.TagTypeDbAuthCert, queryCond.TagPath)
|
||||
// 不存在可操作的数据库,即没有可操作数据
|
||||
if len(tagCodePaths) == 0 {
|
||||
rc.ResData = model.EmptyPageResult[any]()
|
||||
return
|
||||
}
|
||||
|
||||
dbInstCodes := tagentity.GetCodeByPath(tagentity.TagTypeDb, tagCodePaths...)
|
||||
queryCond.Codes = dbInstCodes
|
||||
|
||||
var instvos []*vo.InstanceListVO
|
||||
res, err := d.InstanceApp.GetPageList(queryCond, page, &instvos)
|
||||
biz.ErrIsNil(err)
|
||||
|
||||
// 填充授权凭证信息
|
||||
d.ResourceAuthCertApp.FillAuthCert(consts.ResourceTypeDb, collx.ArrayMap(instvos, func(vos *vo.InstanceListVO) tagentity.IAuthCert {
|
||||
d.ResourceAuthCertApp.FillAuthCertByAcNames(tagentity.GetCodeByPath(tagentity.TagTypeDbAuthCert, tagCodePaths...), collx.ArrayMap(instvos, func(vos *vo.InstanceListVO) tagentity.IAuthCert {
|
||||
return vos
|
||||
})...)
|
||||
|
||||
// 填充标签信息
|
||||
d.TagApp.FillTagInfo(tagentity.TagType(consts.ResourceTypeDb), collx.ArrayMap(instvos, func(insvo *vo.InstanceListVO) tagentity.ITagResource {
|
||||
return insvo
|
||||
})...)
|
||||
|
||||
rc.ResData = res
|
||||
}
|
||||
|
||||
@@ -53,10 +70,13 @@ func (d *Instance) SaveInstance(rc *req.Ctx) {
|
||||
instance := req.BindJsonAndCopyTo[*entity.DbInstance](rc, form, new(entity.DbInstance))
|
||||
|
||||
rc.ReqParam = form
|
||||
biz.ErrIsNil(d.InstanceApp.SaveDbInstance(rc.MetaCtx, &application.SaveDbInstanceParam{
|
||||
DbInstance: instance,
|
||||
AuthCerts: form.AuthCerts,
|
||||
}))
|
||||
id, err := d.InstanceApp.SaveDbInstance(rc.MetaCtx, &application.SaveDbInstanceParam{
|
||||
DbInstance: instance,
|
||||
AuthCerts: form.AuthCerts,
|
||||
TagCodePaths: form.TagCodePaths,
|
||||
})
|
||||
biz.ErrIsNil(err)
|
||||
rc.ResData = id
|
||||
}
|
||||
|
||||
// GetInstance 获取数据库实例密码,由于数据库是加密存储,故提供该接口展示原文密码
|
||||
@@ -86,14 +106,9 @@ func (d *Instance) DeleteInstance(rc *req.Ctx) {
|
||||
|
||||
// 获取数据库实例的所有数据库名
|
||||
func (d *Instance) GetDatabaseNames(rc *req.Ctx) {
|
||||
instanceId := getInstanceId(rc)
|
||||
authCertName := rc.Query("authCertName")
|
||||
biz.NotEmpty(authCertName, "授权凭证名不能为空")
|
||||
|
||||
instance, err := d.InstanceApp.GetById(new(entity.DbInstance), instanceId)
|
||||
biz.ErrIsNil(err, "获取数据库实例错误")
|
||||
|
||||
res, err := d.InstanceApp.GetDatabases(instance, authCertName)
|
||||
form := &form.InstanceDbNamesForm{}
|
||||
instance := req.BindJsonAndCopyTo[*entity.DbInstance](rc, form, new(entity.DbInstance))
|
||||
res, err := d.InstanceApp.GetDatabases(instance, form.AuthCert)
|
||||
biz.ErrIsNil(err)
|
||||
rc.ResData = res
|
||||
}
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
package form
|
||||
|
||||
type DbForm struct {
|
||||
Id uint64 `json:"id"`
|
||||
Code string `binding:"required" json:"code"`
|
||||
Name string `binding:"required" json:"name"`
|
||||
Database string `json:"database"`
|
||||
Remark string `json:"remark"`
|
||||
TagId []uint64 `binding:"required" json:"tagId"`
|
||||
InstanceId uint64 `binding:"required" json:"instanceId"`
|
||||
AuthCertName string `json: "authCertName"`
|
||||
FlowProcdefKey string `json:"flowProcdefKey"`
|
||||
Id uint64 `json:"id"`
|
||||
Code string `binding:"required" json:"code"`
|
||||
Name string `binding:"required" json:"name"`
|
||||
Database string `json:"database"`
|
||||
Remark string `json:"remark"`
|
||||
InstanceId uint64 `binding:"required" json:"instanceId"`
|
||||
AuthCertName string `json:"authCertName"`
|
||||
FlowProcdefKey string `json:"flowProcdefKey"`
|
||||
}
|
||||
|
||||
type DbSqlSaveForm struct {
|
||||
|
||||
@@ -14,5 +14,15 @@ type InstanceForm struct {
|
||||
Remark string `json:"remark"`
|
||||
SshTunnelMachineId int `json:"sshTunnelMachineId"`
|
||||
|
||||
AuthCerts []*tagentity.ResourceAuthCert `json:"authCerts" binding:"required"` // 资产授权凭证信息列表
|
||||
AuthCerts []*tagentity.ResourceAuthCert `json:"authCerts" binding:"required"` // 资产授权凭证信息列表
|
||||
TagCodePaths []string `binding:"required" json:"tagCodePaths"`
|
||||
}
|
||||
|
||||
type InstanceDbNamesForm struct {
|
||||
Type string `binding:"required" json:"type"` // 类型,mysql oracle等
|
||||
Host string `binding:"required" json:"host"`
|
||||
Port int `json:"port"`
|
||||
Params string `json:"params"`
|
||||
SshTunnelMachineId int `json:"sshTunnelMachineId"`
|
||||
AuthCert *tagentity.ResourceAuthCert `json:"authCert" binding:"required"` // 资产授权凭证信息
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
|
||||
type InstanceListVO struct {
|
||||
tagentity.AuthCerts // 授权凭证信息
|
||||
tagentity.ResourceTags
|
||||
|
||||
Id *int64 `json:"id"`
|
||||
Code string `json:"code"`
|
||||
|
||||
@@ -3,7 +3,6 @@ package application
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"mayfly-go/internal/common/consts"
|
||||
"mayfly-go/internal/db/dbm"
|
||||
"mayfly-go/internal/db/dbm/dbi"
|
||||
"mayfly-go/internal/db/domain/entity"
|
||||
@@ -28,7 +27,7 @@ type Db interface {
|
||||
|
||||
Count(condition *entity.DbQuery) int64
|
||||
|
||||
SaveDb(ctx context.Context, entity *entity.Db, tagIds ...uint64) error
|
||||
SaveDb(ctx context.Context, entity *entity.Db) error
|
||||
|
||||
// 删除数据库信息
|
||||
Delete(ctx context.Context, id uint64) error
|
||||
@@ -49,9 +48,10 @@ type Db interface {
|
||||
type dbAppImpl struct {
|
||||
base.AppImpl[*entity.Db, repository.Db]
|
||||
|
||||
dbSqlRepo repository.DbSql `inject:"DbSqlRepo"`
|
||||
dbInstanceApp Instance `inject:"DbInstanceApp"`
|
||||
tagApp tagapp.TagTree `inject:"TagTreeApp"`
|
||||
dbSqlRepo repository.DbSql `inject:"DbSqlRepo"`
|
||||
dbInstanceApp Instance `inject:"DbInstanceApp"`
|
||||
tagApp tagapp.TagTree `inject:"TagTreeApp"`
|
||||
resourceAuthCertApp tagapp.ResourceAuthCert `inject:"ResourceAuthCertApp"`
|
||||
}
|
||||
|
||||
// 注入DbRepo
|
||||
@@ -68,11 +68,16 @@ func (d *dbAppImpl) Count(condition *entity.DbQuery) int64 {
|
||||
return d.GetRepo().Count(condition)
|
||||
}
|
||||
|
||||
func (d *dbAppImpl) SaveDb(ctx context.Context, dbEntity *entity.Db, tagIds ...uint64) error {
|
||||
func (d *dbAppImpl) SaveDb(ctx context.Context, dbEntity *entity.Db) error {
|
||||
// 查找是否存在
|
||||
oldDb := &entity.Db{Name: dbEntity.Name, InstanceId: dbEntity.InstanceId}
|
||||
err := d.GetBy(oldDb)
|
||||
|
||||
authCert, err := d.resourceAuthCertApp.GetAuthCert(dbEntity.AuthCertName)
|
||||
if err != nil {
|
||||
return errorx.NewBiz("授权凭证不存在")
|
||||
}
|
||||
|
||||
err = d.GetBy(oldDb)
|
||||
if dbEntity.Id == 0 {
|
||||
if err == nil {
|
||||
return errorx.NewBiz("该实例下数据库名已存在")
|
||||
@@ -85,10 +90,15 @@ func (d *dbAppImpl) SaveDb(ctx context.Context, dbEntity *entity.Db, tagIds ...u
|
||||
return d.Tx(ctx, func(ctx context.Context) error {
|
||||
return d.Insert(ctx, dbEntity)
|
||||
}, func(ctx context.Context) error {
|
||||
return d.tagApp.SaveResourceTag(ctx, &tagapp.SaveResourceTagParam{
|
||||
Code: dbEntity.Code,
|
||||
Type: tagentity.TagTypeDb,
|
||||
ParentTagIds: tagIds,
|
||||
// 将库关联至指定数据库授权凭证下
|
||||
return d.tagApp.RelateTagsByCodeAndType(ctx, &tagapp.RelateTagsByCodeAndTypeParam{
|
||||
Tags: []*tagapp.ResourceTag{{
|
||||
Code: dbEntity.Code,
|
||||
Type: tagentity.TagTypeDbName,
|
||||
Name: dbEntity.Name,
|
||||
}},
|
||||
ParentTagCode: authCert.Name,
|
||||
ParentTagType: tagentity.TagTypeDbAuthCert,
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -125,11 +135,15 @@ func (d *dbAppImpl) SaveDb(ctx context.Context, dbEntity *entity.Db, tagIds ...u
|
||||
return d.Tx(ctx, func(ctx context.Context) error {
|
||||
return d.UpdateById(ctx, dbEntity)
|
||||
}, func(ctx context.Context) error {
|
||||
return d.tagApp.SaveResourceTag(ctx, &tagapp.SaveResourceTagParam{
|
||||
Code: old.Code,
|
||||
Type: tagentity.TagTypeDb,
|
||||
ParentTagIds: tagIds,
|
||||
})
|
||||
if old.Name != dbEntity.Name {
|
||||
if err := d.tagApp.UpdateTagName(ctx, tagentity.TagTypeDbName, old.Code, dbEntity.Name); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if authCert.Name != old.AuthCertName {
|
||||
return d.tagApp.UpdateParentTagCode(ctx, tagentity.TagTypeDbName, old.Code, authCert.Name, authCert.Username)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
@@ -152,9 +166,9 @@ func (d *dbAppImpl) Delete(ctx context.Context, id uint64) error {
|
||||
// 删除该库下用户保存的所有sql信息
|
||||
return d.dbSqlRepo.DeleteByCond(ctx, &entity.DbSql{DbId: id})
|
||||
}, func(ctx context.Context) error {
|
||||
return d.tagApp.SaveResourceTag(ctx, &tagapp.SaveResourceTagParam{
|
||||
Code: db.Code,
|
||||
Type: tagentity.TagTypeDb,
|
||||
return d.tagApp.DeleteTagByParam(ctx, &tagapp.DelResourceTagParam{
|
||||
ResourceCode: db.Code,
|
||||
ResourceType: tagentity.TagTypeDbName,
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -175,7 +189,7 @@ func (d *dbAppImpl) GetDbConn(dbId uint64, dbName string) (*dbi.DbConn, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
di.TagPath = d.tagApp.ListTagPathByTypeAndCode(consts.ResourceTypeDb, db.Code)
|
||||
di.TagPath = d.tagApp.ListTagPathByTypeAndCode(int8(tagentity.TagTypeDbName), db.Code)
|
||||
di.Id = db.Id
|
||||
|
||||
if db.FlowProcdefKey != nil {
|
||||
@@ -264,9 +278,11 @@ func (d *dbAppImpl) DumpDb(ctx context.Context, reqParam *DumpDbReq) error {
|
||||
writer.TryFlush()
|
||||
// 查询表信息,主要是为了查询表注释
|
||||
tbs, err := dbMeta.GetTables(tableName)
|
||||
biz.ErrIsNil(err)
|
||||
if err != nil || tbs == nil || len(tbs) <= 0 {
|
||||
panic(errorx.NewBiz(fmt.Sprintf("获取表信息失败:%s", tableName)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(tbs) <= 0 {
|
||||
return errorx.NewBiz(fmt.Sprintf("获取表信息失败:%s", tableName))
|
||||
}
|
||||
tabInfo := dbi.Table{
|
||||
TableName: tableName,
|
||||
@@ -293,7 +309,7 @@ func (d *dbAppImpl) DumpDb(ctx context.Context, reqParam *DumpDbReq) error {
|
||||
quoteColNames = append(quoteColNames, dbMeta.QuoteIdentifier(col.ColumnName))
|
||||
}
|
||||
|
||||
_ = dbConn.WalkTableRows(ctx, quoteTableName, func(row map[string]any, _ []*dbi.QueryColumn) error {
|
||||
_, _ = dbConn.WalkTableRows(ctx, quoteTableName, func(row map[string]any, _ []*dbi.QueryColumn) error {
|
||||
rowValues := make([]string, len(columnMap[tableName]))
|
||||
for i, col := range columnMap[tableName] {
|
||||
rowValues[i] = dataHelper.WrapValue(row[col.ColumnName], dataHelper.GetDataType(string(col.DataType)))
|
||||
@@ -309,7 +325,9 @@ func (d *dbAppImpl) DumpDb(ctx context.Context, reqParam *DumpDbReq) error {
|
||||
}
|
||||
|
||||
indexs, err := dbMeta.GetTableIndex(tableName)
|
||||
biz.ErrIsNil(err)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(indexs) > 0 {
|
||||
// 最后添加索引
|
||||
|
||||
@@ -243,7 +243,7 @@ func (app *dataSyncAppImpl) doDataSync(ctx context.Context, sql string, task *en
|
||||
updFieldName = strings.Split(task.UpdField, ".")[1]
|
||||
}
|
||||
|
||||
err = srcConn.WalkQueryRows(context.Background(), sql, func(row map[string]any, columns []*dbi.QueryColumn) error {
|
||||
_, err = srcConn.WalkQueryRows(context.Background(), sql, func(row map[string]any, columns []*dbi.QueryColumn) error {
|
||||
if len(queryColumns) == 0 {
|
||||
queryColumns = columns
|
||||
|
||||
|
||||
@@ -13,15 +13,18 @@ import (
|
||||
"mayfly-go/pkg/base"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/errorx"
|
||||
"mayfly-go/pkg/logx"
|
||||
"mayfly-go/pkg/model"
|
||||
"mayfly-go/pkg/utils/collx"
|
||||
"mayfly-go/pkg/utils/structx"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type SaveDbInstanceParam struct {
|
||||
DbInstance *entity.DbInstance
|
||||
AuthCerts []*tagentity.ResourceAuthCert
|
||||
DbInstance *entity.DbInstance
|
||||
AuthCerts []*tagentity.ResourceAuthCert
|
||||
TagCodePaths []string
|
||||
}
|
||||
|
||||
type Instance interface {
|
||||
@@ -34,13 +37,13 @@ type Instance interface {
|
||||
|
||||
TestConn(instanceEntity *entity.DbInstance, authCert *tagentity.ResourceAuthCert) error
|
||||
|
||||
SaveDbInstance(ctx context.Context, instance *SaveDbInstanceParam) error
|
||||
SaveDbInstance(ctx context.Context, instance *SaveDbInstanceParam) (uint64, error)
|
||||
|
||||
// Delete 删除数据库信息
|
||||
Delete(ctx context.Context, id uint64) error
|
||||
|
||||
// GetDatabases 获取数据库实例的所有数据库列表
|
||||
GetDatabases(entity *entity.DbInstance, authCertName string) ([]string, error)
|
||||
GetDatabases(entity *entity.DbInstance, authCert *tagentity.ResourceAuthCert) ([]string, error)
|
||||
|
||||
// ToDbInfo 根据实例与授权凭证返回对应的DbInfo
|
||||
ToDbInfo(instance *entity.DbInstance, authCertName string, database string) (*dbi.DbInfo, error)
|
||||
@@ -49,6 +52,7 @@ type Instance interface {
|
||||
type instanceAppImpl struct {
|
||||
base.AppImpl[*entity.DbInstance, repository.Instance]
|
||||
|
||||
tagApp tagapp.TagTree `inject:"TagTreeApp"`
|
||||
resourceAuthCertApp tagapp.ResourceAuthCert `inject:"ResourceAuthCertApp"`
|
||||
dbApp Db `inject:"DbApp"`
|
||||
backupApp *DbBackupApp `inject:"DbBackupApp"`
|
||||
@@ -93,15 +97,16 @@ func (app *instanceAppImpl) TestConn(instanceEntity *entity.DbInstance, authCert
|
||||
return nil
|
||||
}
|
||||
|
||||
func (app *instanceAppImpl) SaveDbInstance(ctx context.Context, instance *SaveDbInstanceParam) error {
|
||||
func (app *instanceAppImpl) SaveDbInstance(ctx context.Context, instance *SaveDbInstanceParam) (uint64, error) {
|
||||
instanceEntity := instance.DbInstance
|
||||
// 默认tcp连接
|
||||
instanceEntity.Network = instanceEntity.GetNetwork()
|
||||
resourceType := consts.ResourceTypeDb
|
||||
authCerts := instance.AuthCerts
|
||||
tagCodePaths := instance.TagCodePaths
|
||||
|
||||
if len(authCerts) == 0 {
|
||||
return errorx.NewBiz("授权凭证信息不能为空")
|
||||
return 0, errorx.NewBiz("授权凭证信息不能为空")
|
||||
}
|
||||
|
||||
// 查找是否存在该库
|
||||
@@ -114,13 +119,13 @@ func (app *instanceAppImpl) SaveDbInstance(ctx context.Context, instance *SaveDb
|
||||
err := app.GetBy(oldInstance)
|
||||
if instanceEntity.Id == 0 {
|
||||
if err == nil {
|
||||
return errorx.NewBiz("该数据库实例已存在")
|
||||
return 0, errorx.NewBiz("该数据库实例已存在")
|
||||
}
|
||||
if app.CountByCond(&entity.DbInstance{Code: instanceEntity.Code}) > 0 {
|
||||
return errorx.NewBiz("该编码已存在")
|
||||
return 0, errorx.NewBiz("该编码已存在")
|
||||
}
|
||||
|
||||
return app.Tx(ctx, func(ctx context.Context) error {
|
||||
return instanceEntity.Id, app.Tx(ctx, func(ctx context.Context) error {
|
||||
return app.Insert(ctx, instanceEntity)
|
||||
}, func(ctx context.Context) error {
|
||||
return app.resourceAuthCertApp.RelateAuthCert(ctx, &tagapp.RelateAuthCertParam{
|
||||
@@ -128,15 +133,19 @@ func (app *instanceAppImpl) SaveDbInstance(ctx context.Context, instance *SaveDb
|
||||
ResourceType: tagentity.TagType(resourceType),
|
||||
AuthCerts: authCerts,
|
||||
})
|
||||
}, func(ctx context.Context) error {
|
||||
return app.tagApp.SaveResourceTag(ctx, &tagapp.SaveResourceTagParam{
|
||||
ResourceTag: app.genDbInstanceResourceTag(instanceEntity, authCerts),
|
||||
ParentTagCodePaths: tagCodePaths,
|
||||
})
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
// 如果存在该库,则校验修改的库是否为该库
|
||||
if err == nil && oldInstance.Id != instanceEntity.Id {
|
||||
return errorx.NewBiz("该数据库实例已存在")
|
||||
return 0, errorx.NewBiz("该数据库实例已存在")
|
||||
}
|
||||
return app.Tx(ctx, func(ctx context.Context) error {
|
||||
return oldInstance.Id, app.Tx(ctx, func(ctx context.Context) error {
|
||||
return app.UpdateById(ctx, instanceEntity)
|
||||
}, func(ctx context.Context) error {
|
||||
return app.resourceAuthCertApp.RelateAuthCert(ctx, &tagapp.RelateAuthCertParam{
|
||||
@@ -144,6 +153,16 @@ func (app *instanceAppImpl) SaveDbInstance(ctx context.Context, instance *SaveDb
|
||||
ResourceType: tagentity.TagType(resourceType),
|
||||
AuthCerts: authCerts,
|
||||
})
|
||||
}, func(ctx context.Context) error {
|
||||
if instanceEntity.Name != oldInstance.Name {
|
||||
if err := app.tagApp.UpdateTagName(ctx, tagentity.TagTypeDb, oldInstance.Code, instanceEntity.Name); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return app.tagApp.SaveResourceTag(ctx, &tagapp.SaveResourceTagParam{
|
||||
ResourceTag: app.genDbInstanceResourceTag(instanceEntity, authCerts),
|
||||
ParentTagCodePaths: tagCodePaths,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -200,16 +219,31 @@ func (app *instanceAppImpl) Delete(ctx context.Context, instanceId uint64) error
|
||||
ResourceCode: instance.Code,
|
||||
ResourceType: tagentity.TagType(consts.ResourceTypeDb),
|
||||
})
|
||||
}, func(ctx context.Context) error {
|
||||
return app.tagApp.DeleteTagByParam(ctx, &tagapp.DelResourceTagParam{
|
||||
ResourceCode: instance.Code,
|
||||
ResourceType: tagentity.TagType(consts.ResourceTypeDb),
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func (app *instanceAppImpl) GetDatabases(ed *entity.DbInstance, authCertName string) ([]string, error) {
|
||||
ed.Network = ed.GetNetwork()
|
||||
dbi, err := app.ToDbInfo(ed, authCertName, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
func (app *instanceAppImpl) GetDatabases(ed *entity.DbInstance, authCert *tagentity.ResourceAuthCert) ([]string, error) {
|
||||
if authCert.Id != 0 {
|
||||
// 密文可能被清除,故需要重新获取
|
||||
authCert, _ = app.resourceAuthCertApp.GetAuthCert(authCert.Name)
|
||||
} else {
|
||||
if authCert.CiphertextType == tagentity.AuthCertCiphertextTypePublic {
|
||||
publicAuthCert, err := app.resourceAuthCertApp.GetAuthCert(authCert.Ciphertext)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authCert = publicAuthCert
|
||||
}
|
||||
}
|
||||
|
||||
ed.Network = ed.GetNetwork()
|
||||
dbi := app.toDbInfoByAc(ed, authCert, "")
|
||||
|
||||
dbConn, err := dbm.Conn(dbi)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -238,3 +272,41 @@ func (app *instanceAppImpl) toDbInfoByAc(instance *entity.DbInstance, ac *tagent
|
||||
di.Password = ac.Ciphertext
|
||||
return di
|
||||
}
|
||||
|
||||
func (m *instanceAppImpl) genDbInstanceResourceTag(me *entity.DbInstance, authCerts []*tagentity.ResourceAuthCert) *tagapp.ResourceTag {
|
||||
authCertTags := collx.ArrayMap[*tagentity.ResourceAuthCert, *tagapp.ResourceTag](authCerts, func(val *tagentity.ResourceAuthCert) *tagapp.ResourceTag {
|
||||
return &tagapp.ResourceTag{
|
||||
Code: val.Name,
|
||||
Name: val.Username,
|
||||
Type: tagentity.TagTypeDbAuthCert,
|
||||
}
|
||||
})
|
||||
|
||||
var dbs []*entity.Db
|
||||
if err := m.dbApp.ListByCond(&entity.Db{
|
||||
InstanceId: me.Id,
|
||||
}, &dbs); err != nil {
|
||||
logx.Errorf("获取实例关联的数据库失败: %v", err)
|
||||
}
|
||||
|
||||
authCertName2DbTags := make(map[string][]*tagapp.ResourceTag)
|
||||
for _, db := range dbs {
|
||||
authCertName2DbTags[db.AuthCertName] = append(authCertName2DbTags[db.AuthCertName], &tagapp.ResourceTag{
|
||||
Code: db.Code,
|
||||
Name: db.Name,
|
||||
Type: tagentity.TagTypeDbName,
|
||||
})
|
||||
}
|
||||
|
||||
// 将数据库挂至授权凭证下
|
||||
for _, ac := range authCertTags {
|
||||
ac.Children = authCertName2DbTags[ac.Code]
|
||||
}
|
||||
|
||||
return &tagapp.ResourceTag{
|
||||
Code: me.Code,
|
||||
Type: tagentity.TagTypeDb,
|
||||
Name: me.Name,
|
||||
Children: authCertTags,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -294,7 +294,7 @@ func (d *dbSqlExecAppImpl) doUpdate(ctx context.Context, update *sqlparser.Updat
|
||||
maxRec := 200
|
||||
nowRec := 0
|
||||
res := make([]map[string]any, 0)
|
||||
err = dbConn.WalkQueryRows(ctx, selectSql, func(row map[string]any, columns []*dbi.QueryColumn) error {
|
||||
_, err = dbConn.WalkQueryRows(ctx, selectSql, func(row map[string]any, columns []*dbi.QueryColumn) error {
|
||||
nowRec++
|
||||
res = append(res, row)
|
||||
if nowRec == maxRec {
|
||||
|
||||
@@ -277,7 +277,7 @@ func (app *dbTransferAppImpl) transferData(ctx context.Context, logId uint64, ta
|
||||
logExtraKey := fmt.Sprintf("`%s` 当前已迁移数据量: ", tableName)
|
||||
|
||||
// 游标查询源表数据,并批量插入目标表
|
||||
err = srcConn.WalkTableRows(context.Background(), tableName, func(row map[string]any, columns []*dbi.QueryColumn) error {
|
||||
_, err = srcConn.WalkTableRows(context.Background(), tableName, func(row map[string]any, columns []*dbi.QueryColumn) error {
|
||||
total++
|
||||
rawValue := map[string]any{}
|
||||
for _, column := range columns {
|
||||
|
||||
@@ -39,12 +39,7 @@ func (d *DbConn) Query(querySql string, args ...any) ([]*QueryColumn, []map[stri
|
||||
// 依次返回 列信息数组(顺序),结果map,错误
|
||||
func (d *DbConn) QueryContext(ctx context.Context, querySql string, args ...any) ([]*QueryColumn, []map[string]any, error) {
|
||||
result := make([]map[string]any, 0, 16)
|
||||
var queryColumns []*QueryColumn
|
||||
|
||||
err := d.WalkQueryRows(ctx, querySql, func(row map[string]any, columns []*QueryColumn) error {
|
||||
if len(queryColumns) == 0 {
|
||||
queryColumns = columns
|
||||
}
|
||||
cols, err := d.WalkQueryRows(ctx, querySql, func(row map[string]any, columns []*QueryColumn) error {
|
||||
result = append(result, row)
|
||||
return nil
|
||||
}, args...)
|
||||
@@ -53,7 +48,7 @@ func (d *DbConn) QueryContext(ctx context.Context, querySql string, args ...any)
|
||||
return nil, nil, wrapSqlError(err)
|
||||
}
|
||||
|
||||
return queryColumns, result, nil
|
||||
return cols, result, nil
|
||||
}
|
||||
|
||||
// 将查询结果映射至struct,可具体参考sqlx库
|
||||
@@ -73,12 +68,12 @@ func (d *DbConn) Query2Struct(execSql string, dest any) error {
|
||||
}
|
||||
|
||||
// WalkQueryRows 游标方式遍历查询结果集, walkFn返回error不为nil, 则跳出遍历并取消查询
|
||||
func (d *DbConn) WalkQueryRows(ctx context.Context, querySql string, walkFn WalkQueryRowsFunc, args ...any) error {
|
||||
func (d *DbConn) WalkQueryRows(ctx context.Context, querySql string, walkFn WalkQueryRowsFunc, args ...any) ([]*QueryColumn, error) {
|
||||
return walkQueryRows(ctx, d.db, querySql, walkFn, args...)
|
||||
}
|
||||
|
||||
// WalkTableRows 游标方式遍历指定表的结果集, walkFn返回error不为nil, 则跳出遍历并取消查询
|
||||
func (d *DbConn) WalkTableRows(ctx context.Context, tableName string, walkFn WalkQueryRowsFunc) error {
|
||||
func (d *DbConn) WalkTableRows(ctx context.Context, tableName string, walkFn WalkQueryRowsFunc) ([]*QueryColumn, error) {
|
||||
return d.WalkQueryRows(ctx, fmt.Sprintf("SELECT * FROM %s", tableName), walkFn)
|
||||
}
|
||||
|
||||
@@ -152,13 +147,13 @@ func (d *DbConn) Close() {
|
||||
}
|
||||
|
||||
// 游标方式遍历查询rows, walkFn error不为nil, 则跳出遍历
|
||||
func walkQueryRows(ctx context.Context, db *sql.DB, selectSql string, walkFn WalkQueryRowsFunc, args ...any) error {
|
||||
func walkQueryRows(ctx context.Context, db *sql.DB, selectSql string, walkFn WalkQueryRowsFunc, args ...any) ([]*QueryColumn, error) {
|
||||
cancelCtx, cancelFunc := context.WithCancel(ctx)
|
||||
defer cancelFunc()
|
||||
|
||||
rows, err := db.QueryContext(cancelCtx, selectSql, args...)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
// rows对象一定要close掉,如果出错,不关掉则会很迅速的达到设置最大连接数,
|
||||
// 后面的链接过来直接报错或拒绝,实际上也没有起效果
|
||||
@@ -166,7 +161,7 @@ func walkQueryRows(ctx context.Context, db *sql.DB, selectSql string, walkFn Wal
|
||||
|
||||
colTypes, err := rows.ColumnTypes()
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
lenCols := len(colTypes)
|
||||
// 列名用于前端表头名称按照数据库与查询字段顺序显示
|
||||
@@ -189,7 +184,7 @@ func walkQueryRows(ctx context.Context, db *sql.DB, selectSql string, walkFn Wal
|
||||
for rows.Next() {
|
||||
// 不Scan也会导致等待,该链接实际处于未工作的状态,然后也会导致连接数迅速达到最大
|
||||
if err := rows.Scan(scans...); err != nil {
|
||||
return err
|
||||
return cols, err
|
||||
}
|
||||
// 每行数据
|
||||
rowData := make(map[string]any, lenCols)
|
||||
@@ -200,11 +195,11 @@ func walkQueryRows(ctx context.Context, db *sql.DB, selectSql string, walkFn Wal
|
||||
if err = walkFn(rowData, cols); err != nil {
|
||||
logx.ErrorfContext(ctx, "[%s]游标遍历查询结果集出错, 退出遍历: %s", selectSql, err.Error())
|
||||
cancelFunc()
|
||||
return err
|
||||
return cols, err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
return cols, nil
|
||||
}
|
||||
|
||||
// 将查询的值转为对应列类型的实际值,不全部转为字符串
|
||||
|
||||
@@ -2,10 +2,12 @@ package entity
|
||||
|
||||
// InstanceQuery 数据库实例查询
|
||||
type InstanceQuery struct {
|
||||
Id uint64 `json:"id" form:"id"`
|
||||
Name string `json:"name" form:"name"`
|
||||
Code string `json:"code" form:"code"`
|
||||
Host string `json:"host" form:"host"`
|
||||
Id uint64 `json:"id" form:"id"`
|
||||
Name string `json:"name" form:"name"`
|
||||
Code string `json:"code" form:"code"`
|
||||
Host string `json:"host" form:"host"`
|
||||
TagPath string `json:"host" form:"tagPath"`
|
||||
Codes []string
|
||||
}
|
||||
|
||||
type DataSyncTaskQuery struct {
|
||||
|
||||
@@ -22,6 +22,7 @@ func (d *instanceRepoImpl) GetInstanceList(condition *entity.InstanceQuery, page
|
||||
Eq("id", condition.Id).
|
||||
Eq("host", condition.Host).
|
||||
Like("name", condition.Name).
|
||||
Like("code", condition.Code)
|
||||
Like("code", condition.Code).
|
||||
In("code", condition.Codes)
|
||||
return gormx.PageQuery(qd, pageParam, toEntity)
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ func InitInstanceRouter(router *gin.RouterGroup) {
|
||||
req.NewGet(":instanceId", d.GetInstance),
|
||||
|
||||
// 获取数据库实例的所有数据库名
|
||||
req.NewGet(":instanceId/databases", d.GetDatabaseNames),
|
||||
req.NewPost("/databases", d.GetDatabaseNames),
|
||||
|
||||
req.NewGet(":instanceId/server-info", d.GetDbServer),
|
||||
|
||||
|
||||
@@ -9,21 +9,17 @@ import (
|
||||
)
|
||||
|
||||
type Dashbord struct {
|
||||
ResourceAuthCertApp tagapp.ResourceAuthCert `inject:""`
|
||||
MachineApp application.Machine `inject:""`
|
||||
TagTreeApp tagapp.TagTree `inject:""`
|
||||
MachineApp application.Machine `inject:""`
|
||||
}
|
||||
|
||||
func (m *Dashbord) Dashbord(rc *req.Ctx) {
|
||||
accountId := rc.GetLoginAccount().Id
|
||||
|
||||
machienAuthCerts := m.ResourceAuthCertApp.GetAccountAuthCert(accountId, tagentity.TagTypeMachineAuthCert)
|
||||
machineCodes := collx.ArrayMap(machienAuthCerts, func(ac *tagentity.ResourceAuthCert) string {
|
||||
return ac.ResourceCode
|
||||
})
|
||||
|
||||
machienNum := len(collx.ArrayDeduplicate(machineCodes))
|
||||
tagCodePaths := m.TagTreeApp.GetAccountTagCodePaths(accountId, tagentity.TagTypeMachineAuthCert, "")
|
||||
machineCodes := tagentity.GetCodeByPath(tagentity.TagTypeMachine, tagCodePaths...)
|
||||
|
||||
rc.ResData = collx.M{
|
||||
"machineNum": machienNum,
|
||||
"machineNum": len(machineCodes),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,8 +10,8 @@ type MachineForm struct {
|
||||
Ip string `json:"ip" binding:"required"` // IP地址
|
||||
Port int `json:"port" binding:"required"` // 端口号
|
||||
|
||||
TagId []uint64 `json:"tagId" binding:"required"`
|
||||
AuthCerts []*tagentity.ResourceAuthCert `json:"authCerts" binding:"required"` // 资产授权凭证信息列表
|
||||
TagCodePaths []string `json:"tagCodePaths" binding:"required"`
|
||||
AuthCerts []*tagentity.ResourceAuthCert `json:"authCerts" binding:"required"` // 资产授权凭证信息列表
|
||||
|
||||
Remark string `json:"remark"`
|
||||
SshTunnelMachineId int `json:"sshTunnelMachineId"` // ssh隧道机器id
|
||||
|
||||
@@ -42,16 +42,14 @@ type Machine struct {
|
||||
func (m *Machine) Machines(rc *req.Ctx) {
|
||||
condition, pageParam := req.BindQueryAndPage(rc, new(entity.MachineQuery))
|
||||
|
||||
authCerts := m.ResourceAuthCertApp.GetAccountAuthCert(rc.GetLoginAccount().Id, tagentity.TagTypeMachineAuthCert, condition.TagPath)
|
||||
// 不存在可操作的授权凭证,即没有可操作数据
|
||||
if len(authCerts) == 0 {
|
||||
tagCodePaths := m.TagApp.GetAccountTagCodePaths(rc.GetLoginAccount().Id, tagentity.TagTypeMachineAuthCert, condition.TagPath)
|
||||
// 不存在可操作的机器-授权凭证标签,即没有可操作数据
|
||||
if len(tagCodePaths) == 0 {
|
||||
rc.ResData = model.EmptyPageResult[any]()
|
||||
return
|
||||
}
|
||||
|
||||
machineCodes := collx.ArrayMap(authCerts, func(ac *tagentity.ResourceAuthCert) string {
|
||||
return ac.ResourceCode
|
||||
})
|
||||
machineCodes := tagentity.GetCodeByPath(tagentity.TagTypeMachine, tagCodePaths...)
|
||||
condition.Codes = collx.ArrayDeduplicate(machineCodes)
|
||||
|
||||
var machinevos []*vo.MachineVO
|
||||
@@ -68,7 +66,7 @@ func (m *Machine) Machines(rc *req.Ctx) {
|
||||
})...)
|
||||
|
||||
// 填充授权凭证信息
|
||||
m.ResourceAuthCertApp.FillAuthCertByAcs(authCerts, collx.ArrayMap(machinevos, func(mvo *vo.MachineVO) tagentity.IAuthCert {
|
||||
m.ResourceAuthCertApp.FillAuthCertByAcNames(tagentity.GetCodeByPath(tagentity.TagTypeMachineAuthCert, tagCodePaths...), collx.ArrayMap(machinevos, func(mvo *vo.MachineVO) tagentity.IAuthCert {
|
||||
return mvo
|
||||
})...)
|
||||
|
||||
@@ -99,9 +97,9 @@ func (m *Machine) SaveMachine(rc *req.Ctx) {
|
||||
rc.ReqParam = machineForm
|
||||
|
||||
biz.ErrIsNil(m.MachineApp.SaveMachine(rc.MetaCtx, &application.SaveMachineParam{
|
||||
Machine: me,
|
||||
TagIds: machineForm.TagId,
|
||||
AuthCerts: machineForm.AuthCerts,
|
||||
Machine: me,
|
||||
TagCodePaths: machineForm.TagCodePaths,
|
||||
AuthCerts: machineForm.AuthCerts,
|
||||
}))
|
||||
}
|
||||
|
||||
|
||||
@@ -17,12 +17,13 @@ import (
|
||||
"mayfly-go/pkg/logx"
|
||||
"mayfly-go/pkg/model"
|
||||
"mayfly-go/pkg/scheduler"
|
||||
"mayfly-go/pkg/utils/collx"
|
||||
)
|
||||
|
||||
type SaveMachineParam struct {
|
||||
Machine *entity.Machine
|
||||
TagIds []uint64
|
||||
AuthCerts []*tagentity.ResourceAuthCert
|
||||
Machine *entity.Machine
|
||||
TagCodePaths []string
|
||||
AuthCerts []*tagentity.ResourceAuthCert
|
||||
}
|
||||
|
||||
type Machine interface {
|
||||
@@ -81,7 +82,7 @@ func (m *machineAppImpl) GetMachineList(condition *entity.MachineQuery, pagePara
|
||||
|
||||
func (m *machineAppImpl) SaveMachine(ctx context.Context, param *SaveMachineParam) error {
|
||||
me := param.Machine
|
||||
tagIds := param.TagIds
|
||||
tagCodePaths := param.TagCodePaths
|
||||
authCerts := param.AuthCerts
|
||||
resourceType := tagentity.TagTypeMachine
|
||||
|
||||
@@ -107,29 +108,20 @@ func (m *machineAppImpl) SaveMachine(ctx context.Context, param *SaveMachinePara
|
||||
// 新增机器,默认启用状态
|
||||
me.Status = entity.MachineStatusEnable
|
||||
|
||||
if err := m.Tx(ctx, func(ctx context.Context) error {
|
||||
return m.Tx(ctx, func(ctx context.Context) error {
|
||||
return m.Insert(ctx, me)
|
||||
}, func(ctx context.Context) error {
|
||||
return m.tagApp.SaveResourceTag(ctx, &tagapp.SaveResourceTagParam{
|
||||
Code: me.Code,
|
||||
Type: resourceType,
|
||||
ParentTagIds: tagIds,
|
||||
})
|
||||
}, func(ctx context.Context) error {
|
||||
return m.resourceAuthCertApp.RelateAuthCert(ctx, &tagapp.RelateAuthCertParam{
|
||||
ResourceCode: me.Code,
|
||||
ResourceType: resourceType,
|
||||
AuthCerts: authCerts,
|
||||
})
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 事务问题,导致可能删除或新增的标签数据不可见,造成数据错误,故单独执行。若万一失败,可授权凭证管理处添加
|
||||
return m.resourceAuthCertApp.RelateAuthCert2ResourceTag(ctx, &tagapp.RelateAuthCertParam{
|
||||
ResourceCode: me.Code,
|
||||
ResourceType: resourceType,
|
||||
AuthCerts: authCerts,
|
||||
}, func(ctx context.Context) error {
|
||||
return m.tagApp.SaveResourceTag(ctx, &tagapp.SaveResourceTagParam{
|
||||
ResourceTag: m.genMachineResourceTag(me, authCerts),
|
||||
ParentTagCodePaths: tagCodePaths,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -146,29 +138,23 @@ func (m *machineAppImpl) SaveMachine(ctx context.Context, param *SaveMachinePara
|
||||
mcm.DeleteCli(me.Id)
|
||||
// 防止误传修改
|
||||
me.Code = ""
|
||||
if err := m.Tx(ctx, func(ctx context.Context) error {
|
||||
return m.Tx(ctx, func(ctx context.Context) error {
|
||||
return m.UpdateById(ctx, me)
|
||||
}, func(ctx context.Context) error {
|
||||
return m.tagApp.SaveResourceTag(ctx, &tagapp.SaveResourceTagParam{
|
||||
Code: oldMachine.Code,
|
||||
Type: resourceType,
|
||||
ParentTagIds: tagIds,
|
||||
})
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return m.Tx(ctx, func(ctx context.Context) error {
|
||||
return m.resourceAuthCertApp.RelateAuthCert(ctx, &tagapp.RelateAuthCertParam{
|
||||
ResourceCode: oldMachine.Code,
|
||||
ResourceType: resourceType,
|
||||
AuthCerts: authCerts,
|
||||
})
|
||||
}, func(ctx context.Context) error {
|
||||
return m.resourceAuthCertApp.RelateAuthCert2ResourceTag(ctx, &tagapp.RelateAuthCertParam{
|
||||
ResourceCode: oldMachine.Code,
|
||||
ResourceType: resourceType,
|
||||
AuthCerts: authCerts,
|
||||
if oldMachine.Name != me.Name {
|
||||
if err := m.tagApp.UpdateTagName(ctx, tagentity.TagTypeMachine, oldMachine.Code, me.Name); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return m.tagApp.SaveResourceTag(ctx, &tagapp.SaveResourceTagParam{
|
||||
ResourceTag: m.genMachineResourceTag(oldMachine, authCerts),
|
||||
ParentTagCodePaths: tagCodePaths,
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -230,19 +216,16 @@ func (m *machineAppImpl) Delete(ctx context.Context, id uint64) error {
|
||||
return m.DeleteById(ctx, id)
|
||||
}, func(ctx context.Context) error {
|
||||
return m.tagApp.SaveResourceTag(ctx, &tagapp.SaveResourceTagParam{
|
||||
Code: machine.Code,
|
||||
Type: resourceType,
|
||||
ResourceTag: &tagapp.ResourceTag{
|
||||
Code: machine.Code,
|
||||
Type: tagentity.TagTypeMachine,
|
||||
},
|
||||
})
|
||||
}, func(ctx context.Context) error {
|
||||
return m.resourceAuthCertApp.RelateAuthCert(ctx, &tagapp.RelateAuthCertParam{
|
||||
ResourceCode: machine.Code,
|
||||
ResourceType: resourceType,
|
||||
})
|
||||
}, func(ctx context.Context) error {
|
||||
return m.resourceAuthCertApp.RelateAuthCert2ResourceTag(ctx, &tagapp.RelateAuthCertParam{
|
||||
ResourceCode: machine.Code,
|
||||
ResourceType: resourceType,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -362,6 +345,7 @@ func (m *machineAppImpl) toMi(me *entity.Machine, authCert *tagentity.ResourceAu
|
||||
mi.EnableRecorder = me.EnableRecorder
|
||||
mi.Protocol = me.Protocol
|
||||
|
||||
mi.AuthCertName = authCert.Name
|
||||
mi.Username = authCert.Username
|
||||
mi.Password = authCert.Ciphertext
|
||||
mi.Passphrase = authCert.GetExtraString(tagentity.ExtraKeyPassphrase)
|
||||
@@ -377,3 +361,20 @@ func (m *machineAppImpl) toMi(me *entity.Machine, authCert *tagentity.ResourceAu
|
||||
}
|
||||
return mi, nil
|
||||
}
|
||||
|
||||
func (m *machineAppImpl) genMachineResourceTag(me *entity.Machine, authCerts []*tagentity.ResourceAuthCert) *tagapp.ResourceTag {
|
||||
authCertTags := collx.ArrayMap[*tagentity.ResourceAuthCert, *tagapp.ResourceTag](authCerts, func(val *tagentity.ResourceAuthCert) *tagapp.ResourceTag {
|
||||
return &tagapp.ResourceTag{
|
||||
Code: val.Name,
|
||||
Name: val.Username,
|
||||
Type: tagentity.TagTypeMachineAuthCert,
|
||||
}
|
||||
})
|
||||
|
||||
return &tagapp.ResourceTag{
|
||||
Code: me.Code,
|
||||
Type: tagentity.TagTypeMachine,
|
||||
Name: me.Name,
|
||||
Children: authCertTags,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,11 +2,11 @@ package form
|
||||
|
||||
type Mongo struct {
|
||||
Id uint64 `json:"id"`
|
||||
Code uint64 `json:"code" binding:"required"`
|
||||
Code string `json:"code" binding:"required"`
|
||||
Uri string `binding:"required" json:"uri"`
|
||||
SshTunnelMachineId int `json:"sshTunnelMachineId"` // ssh隧道机器id
|
||||
Name string `binding:"required" json:"name"`
|
||||
TagId []uint64 `binding:"required" json:"tagId"`
|
||||
TagCodePaths []string `binding:"required" json:"tagCodePaths"`
|
||||
}
|
||||
|
||||
type MongoCommand struct {
|
||||
|
||||
@@ -67,7 +67,7 @@ func (m *Mongo) Save(rc *req.Ctx) {
|
||||
}(form.Uri)
|
||||
rc.ReqParam = form
|
||||
|
||||
biz.ErrIsNil(m.MongoApp.SaveMongo(rc.MetaCtx, mongo, form.TagId...))
|
||||
biz.ErrIsNil(m.MongoApp.SaveMongo(rc.MetaCtx, mongo, form.TagCodePaths...))
|
||||
}
|
||||
|
||||
func (m *Mongo) DeleteMongo(rc *req.Ctx) {
|
||||
|
||||
@@ -21,7 +21,7 @@ type Mongo interface {
|
||||
|
||||
TestConn(entity *entity.Mongo) error
|
||||
|
||||
SaveMongo(ctx context.Context, entity *entity.Mongo, tagIds ...uint64) error
|
||||
SaveMongo(ctx context.Context, entity *entity.Mongo, tagCodePaths ...string) error
|
||||
|
||||
// 删除数据库信息
|
||||
Delete(ctx context.Context, id uint64) error
|
||||
@@ -59,10 +59,10 @@ func (d *mongoAppImpl) Delete(ctx context.Context, id uint64) error {
|
||||
return d.DeleteById(ctx, id)
|
||||
},
|
||||
func(ctx context.Context) error {
|
||||
return d.tagApp.SaveResourceTag(ctx, &tagapp.SaveResourceTagParam{
|
||||
return d.tagApp.SaveResourceTag(ctx, &tagapp.SaveResourceTagParam{ResourceTag: &tagapp.ResourceTag{
|
||||
Type: tagentity.TagTypeMongo,
|
||||
Code: mongoEntity.Code,
|
||||
})
|
||||
}})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ func (d *mongoAppImpl) TestConn(me *entity.Mongo) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *mongoAppImpl) SaveMongo(ctx context.Context, m *entity.Mongo, tagIds ...uint64) error {
|
||||
func (d *mongoAppImpl) SaveMongo(ctx context.Context, m *entity.Mongo, tagCodePaths ...string) error {
|
||||
oldMongo := &entity.Mongo{Name: m.Name, SshTunnelMachineId: m.SshTunnelMachineId}
|
||||
err := d.GetBy(oldMongo)
|
||||
|
||||
@@ -91,9 +91,11 @@ func (d *mongoAppImpl) SaveMongo(ctx context.Context, m *entity.Mongo, tagIds ..
|
||||
return d.Insert(ctx, m)
|
||||
}, func(ctx context.Context) error {
|
||||
return d.tagApp.SaveResourceTag(ctx, &tagapp.SaveResourceTagParam{
|
||||
Type: tagentity.TagTypeMongo,
|
||||
Code: m.Code,
|
||||
ParentTagIds: tagIds,
|
||||
ResourceTag: &tagapp.ResourceTag{
|
||||
Type: tagentity.TagTypeMongo,
|
||||
Code: m.Code,
|
||||
},
|
||||
ParentTagCodePaths: tagCodePaths,
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -113,10 +115,18 @@ func (d *mongoAppImpl) SaveMongo(ctx context.Context, m *entity.Mongo, tagIds ..
|
||||
return d.Tx(ctx, func(ctx context.Context) error {
|
||||
return d.UpdateById(ctx, m)
|
||||
}, func(ctx context.Context) error {
|
||||
if oldMongo.Name != m.Name {
|
||||
if err := d.tagApp.UpdateTagName(ctx, tagentity.TagTypeMachine, oldMongo.Code, m.Name); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return d.tagApp.SaveResourceTag(ctx, &tagapp.SaveResourceTagParam{
|
||||
Type: tagentity.TagTypeMongo,
|
||||
Code: oldMongo.Code,
|
||||
ParentTagIds: tagIds,
|
||||
ResourceTag: &tagapp.ResourceTag{
|
||||
Type: tagentity.TagTypeMongo,
|
||||
Code: oldMongo.Code,
|
||||
},
|
||||
ParentTagCodePaths: tagCodePaths,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ type Redis struct {
|
||||
Mode string `json:"mode"`
|
||||
Db string `json:"db"`
|
||||
SshTunnelMachineId int `json:"sshTunnelMachineId"` // ssh隧道机器id
|
||||
TagId []uint64 `binding:"required" json:"tagId"`
|
||||
TagCodePaths []string `binding:"required" json:"tagCodePaths"`
|
||||
Remark string `json:"remark"`
|
||||
FlowProcdefKey string `json:"flowProcdefKey"` // 审批流-流程定义key(有值则说明关键操作需要进行审批执行),使用指针为了方便更新空字符串(取消流程审批)
|
||||
}
|
||||
|
||||
@@ -55,8 +55,7 @@ func (r *Redis) TestConn(rc *req.Ctx) {
|
||||
redis := req.BindJsonAndCopyTo[*entity.Redis](rc, form, new(entity.Redis))
|
||||
|
||||
biz.ErrIsNil(r.RedisApp.TestConn(&application.SaveRedisParam{
|
||||
Redis: redis,
|
||||
TagIds: form.TagId,
|
||||
Redis: redis,
|
||||
AuthCert: &tagentity.ResourceAuthCert{
|
||||
Name: fmt.Sprintf("redis_%s_ac", redis.Code),
|
||||
Username: form.Username,
|
||||
@@ -76,8 +75,8 @@ func (r *Redis) Save(rc *req.Ctx) {
|
||||
rc.ReqParam = form
|
||||
|
||||
redisParam := &application.SaveRedisParam{
|
||||
Redis: redis,
|
||||
TagIds: form.TagId,
|
||||
Redis: redis,
|
||||
TagCodePaths: form.TagCodePaths,
|
||||
AuthCert: &tagentity.ResourceAuthCert{
|
||||
Name: fmt.Sprintf("redis_%s_ac", redis.Code),
|
||||
Username: form.Username,
|
||||
|
||||
@@ -24,9 +24,9 @@ import (
|
||||
)
|
||||
|
||||
type SaveRedisParam struct {
|
||||
Redis *entity.Redis
|
||||
AuthCert *tagentity.ResourceAuthCert
|
||||
TagIds []uint64
|
||||
Redis *entity.Redis
|
||||
AuthCert *tagentity.ResourceAuthCert
|
||||
TagCodePaths []string
|
||||
}
|
||||
|
||||
type RunCmdParam struct {
|
||||
@@ -109,7 +109,7 @@ func (r *redisAppImpl) TestConn(param *SaveRedisParam) error {
|
||||
|
||||
func (r *redisAppImpl) SaveRedis(ctx context.Context, param *SaveRedisParam) error {
|
||||
re := param.Redis
|
||||
tagIds := param.TagIds
|
||||
tagCodePaths := param.TagCodePaths
|
||||
// 查找是否存在该库
|
||||
oldRedis := &entity.Redis{
|
||||
Host: re.Host,
|
||||
@@ -129,9 +129,12 @@ func (r *redisAppImpl) SaveRedis(ctx context.Context, param *SaveRedisParam) err
|
||||
return r.Insert(ctx, re)
|
||||
}, func(ctx context.Context) error {
|
||||
return r.tagApp.SaveResourceTag(ctx, &tagapp.SaveResourceTagParam{
|
||||
Type: tagentity.TagTypeRedis,
|
||||
Code: re.Code,
|
||||
ParentTagIds: tagIds,
|
||||
ResourceTag: &tagapp.ResourceTag{
|
||||
Type: tagentity.TagTypeRedis,
|
||||
Code: re.Code,
|
||||
Name: re.Name,
|
||||
},
|
||||
ParentTagCodePaths: tagCodePaths,
|
||||
})
|
||||
}, func(ctx context.Context) error {
|
||||
return r.resourceAuthCertApp.RelateAuthCert(ctx, &tagapp.RelateAuthCertParam{
|
||||
@@ -162,10 +165,18 @@ func (r *redisAppImpl) SaveRedis(ctx context.Context, param *SaveRedisParam) err
|
||||
return r.Tx(ctx, func(ctx context.Context) error {
|
||||
return r.UpdateById(ctx, re)
|
||||
}, func(ctx context.Context) error {
|
||||
if oldRedis.Name != re.Name {
|
||||
if err := r.tagApp.UpdateTagName(ctx, tagentity.TagTypeMachine, oldRedis.Code, re.Name); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return r.tagApp.SaveResourceTag(ctx, &tagapp.SaveResourceTagParam{
|
||||
Type: tagentity.TagTypeRedis,
|
||||
Code: oldRedis.Code,
|
||||
ParentTagIds: tagIds,
|
||||
ResourceTag: &tagapp.ResourceTag{
|
||||
Type: tagentity.TagTypeRedis,
|
||||
Code: oldRedis.Code,
|
||||
Name: re.Name,
|
||||
},
|
||||
ParentTagCodePaths: tagCodePaths,
|
||||
})
|
||||
}, func(ctx context.Context) error {
|
||||
return r.resourceAuthCertApp.RelateAuthCert(ctx, &tagapp.RelateAuthCertParam{
|
||||
@@ -192,8 +203,10 @@ func (r *redisAppImpl) Delete(ctx context.Context, id uint64) error {
|
||||
return r.DeleteById(ctx, id)
|
||||
}, func(ctx context.Context) error {
|
||||
return r.tagApp.SaveResourceTag(ctx, &tagapp.SaveResourceTagParam{
|
||||
Type: tagentity.TagTypeRedis,
|
||||
Code: re.Code,
|
||||
ResourceTag: &tagapp.ResourceTag{
|
||||
Type: tagentity.TagTypeRedis,
|
||||
Code: re.Code,
|
||||
},
|
||||
})
|
||||
}, func(ctx context.Context) error {
|
||||
return r.resourceAuthCertApp.RelateAuthCert(ctx, &tagapp.RelateAuthCertParam{
|
||||
|
||||
9
server/internal/tag/api/form/tag.go
Normal file
9
server/internal/tag/api/form/tag.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package form
|
||||
|
||||
import "mayfly-go/internal/tag/domain/entity"
|
||||
|
||||
type TagTree struct {
|
||||
*entity.TagTree
|
||||
|
||||
Pid uint64 `json:"pid"`
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package api
|
||||
import (
|
||||
"fmt"
|
||||
"mayfly-go/internal/common/consts"
|
||||
"mayfly-go/internal/tag/api/form"
|
||||
"mayfly-go/internal/tag/api/vo"
|
||||
"mayfly-go/internal/tag/application"
|
||||
"mayfly-go/internal/tag/domain/entity"
|
||||
@@ -15,8 +16,6 @@ import (
|
||||
|
||||
type TagTree struct {
|
||||
TagTreeApp application.TagTree `inject:""`
|
||||
|
||||
ResourceAuthCertApp application.ResourceAuthCert `inject:""`
|
||||
}
|
||||
|
||||
func (p *TagTree) GetTagTree(rc *req.Ctx) {
|
||||
@@ -71,12 +70,12 @@ func (p *TagTree) ListByQuery(rc *req.Ctx) {
|
||||
}
|
||||
|
||||
func (p *TagTree) SaveTagTree(rc *req.Ctx) {
|
||||
tagTree := &entity.TagTree{}
|
||||
req.BindJsonAndValid(rc, tagTree)
|
||||
tagForm := &form.TagTree{}
|
||||
tagTree := req.BindJsonAndCopyTo(rc, tagForm, new(entity.TagTree))
|
||||
|
||||
rc.ReqParam = fmt.Sprintf("tagTreeId: %d, tagName: %s, code: %s", tagTree.Id, tagTree.Name, tagTree.Code)
|
||||
|
||||
biz.ErrIsNil(p.TagTreeApp.Save(rc.MetaCtx, tagTree))
|
||||
biz.ErrIsNil(p.TagTreeApp.SaveTag(rc.MetaCtx, tagForm.Pid, tagTree))
|
||||
}
|
||||
|
||||
func (p *TagTree) DelTagTree(rc *req.Ctx) {
|
||||
@@ -103,14 +102,12 @@ func (p *TagTree) CountTagResource(rc *req.Ctx) {
|
||||
tagPath := rc.Query("tagPath")
|
||||
accountId := rc.GetLoginAccount().Id
|
||||
|
||||
machienAuthCerts := p.ResourceAuthCertApp.GetAccountAuthCert(accountId, entity.TagTypeMachineAuthCert, tagPath)
|
||||
machineCodes := collx.ArrayMap(machienAuthCerts, func(ac *entity.ResourceAuthCert) string {
|
||||
return ac.ResourceCode
|
||||
})
|
||||
machineCodes := entity.GetCodeByPath(entity.TagTypeMachine, p.TagTreeApp.GetAccountTagCodePaths(accountId, entity.TagTypeMachineAuthCert, tagPath)...)
|
||||
dbCodes := entity.GetCodeByPath(entity.TagTypeDb, p.TagTreeApp.GetAccountTagCodePaths(accountId, entity.TagTypeDbName, tagPath)...)
|
||||
|
||||
rc.ResData = collx.M{
|
||||
"machine": len(collx.ArrayDeduplicate(machineCodes)),
|
||||
"db": len(p.TagTreeApp.GetAccountTagCodes(accountId, consts.ResourceTypeDb, tagPath)),
|
||||
"machine": len(machineCodes),
|
||||
"db": len(dbCodes),
|
||||
"redis": len(p.TagTreeApp.GetAccountTagCodes(accountId, consts.ResourceTypeRedis, tagPath)),
|
||||
"mongo": len(p.TagTreeApp.GetAccountTagCodes(accountId, consts.ResourceTypeMongo, tagPath)),
|
||||
}
|
||||
|
||||
@@ -2,38 +2,38 @@ package vo
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/tag/domain/entity"
|
||||
"mayfly-go/pkg/utils/collx"
|
||||
)
|
||||
|
||||
type TagTreeVOS []*entity.TagTree
|
||||
|
||||
type TagTreeItem struct {
|
||||
*entity.TagTree
|
||||
Children []TagTreeItem `json:"children"`
|
||||
Children []*TagTreeItem `json:"children"`
|
||||
}
|
||||
|
||||
func (m *TagTreeVOS) ToTrees(pid uint64) []TagTreeItem {
|
||||
var resourceTree []TagTreeItem
|
||||
|
||||
list := m.findChildren(pid)
|
||||
if len(list) == 0 {
|
||||
return resourceTree
|
||||
func (m *TagTreeVOS) ToTrees(pid uint64) []*TagTreeItem {
|
||||
var ttis []*TagTreeItem
|
||||
if len(*m) == 0 {
|
||||
return ttis
|
||||
}
|
||||
|
||||
for _, v := range list {
|
||||
Children := m.ToTrees(v.Id)
|
||||
resourceTree = append(resourceTree, TagTreeItem{v, Children})
|
||||
}
|
||||
ttis = collx.ArrayMap(*m, func(tr *entity.TagTree) *TagTreeItem { return &TagTreeItem{TagTree: tr} })
|
||||
tagMap := collx.ArrayToMap(ttis, func(item *TagTreeItem) string {
|
||||
return item.CodePath
|
||||
})
|
||||
|
||||
return resourceTree
|
||||
}
|
||||
|
||||
func (m *TagTreeVOS) findChildren(pid uint64) []*entity.TagTree {
|
||||
child := []*entity.TagTree{}
|
||||
|
||||
for _, v := range *m {
|
||||
if v.Pid == pid {
|
||||
child = append(child, v)
|
||||
for _, node := range ttis {
|
||||
// 根节点
|
||||
if node.IsRoot() {
|
||||
continue
|
||||
}
|
||||
parentCodePath := node.GetParentPath(0)
|
||||
parentNode := tagMap[parentCodePath]
|
||||
if parentNode != nil {
|
||||
parentNode.Children = append(parentNode.Children, node)
|
||||
}
|
||||
}
|
||||
return child
|
||||
|
||||
return collx.ArrayFilter(ttis, func(tti *TagTreeItem) bool { return tti.IsRoot() })
|
||||
}
|
||||
|
||||
@@ -26,9 +26,6 @@ type ResourceAuthCert interface {
|
||||
// RelateAuthCert 关联资源授权凭证信息
|
||||
RelateAuthCert(ctx context.Context, param *RelateAuthCertParam) error
|
||||
|
||||
// RelateAuthCert2ResourceTag 关联授权凭证标签至指定资源标签下
|
||||
RelateAuthCert2ResourceTag(ctx context.Context, param *RelateAuthCertParam) error
|
||||
|
||||
// SaveAuthCert 保存授权凭证信息
|
||||
SaveAuthCert(ctx context.Context, rac *entity.ResourceAuthCert) error
|
||||
|
||||
@@ -40,9 +37,6 @@ type ResourceAuthCert interface {
|
||||
// GetResourceAuthCert 获取资源授权凭证,默认获取特权账号,若没有则返回第一个
|
||||
GetResourceAuthCert(resourceType entity.TagType, resourceCode string) (*entity.ResourceAuthCert, error)
|
||||
|
||||
// GetAccountAuthCert 获取账号有权限操作的授权凭证信息
|
||||
GetAccountAuthCert(accountId uint64, authCertTagType entity.TagType, tagPath ...string) []*entity.ResourceAuthCert
|
||||
|
||||
// FillAuthCertByAcs 根据授权凭证列表填充资源的授权凭证信息
|
||||
// @param authCerts 授权凭证列表
|
||||
// @param resources 实现了entity.IAuthCert接口的资源信息
|
||||
@@ -52,6 +46,9 @@ type ResourceAuthCert interface {
|
||||
// @param resourceType 资源类型
|
||||
// @param resources 实现了entity.IAuthCert接口的资源信息
|
||||
FillAuthCert(resourceType int8, resources ...entity.IAuthCert)
|
||||
|
||||
// FillAuthCertByAcNames 根据授权凭证名称填充资源对应的凭证信息
|
||||
FillAuthCertByAcNames(authCertNames []string, resources ...entity.IAuthCert)
|
||||
}
|
||||
|
||||
type resourceAuthCertAppImpl struct {
|
||||
@@ -166,7 +163,7 @@ func (r *resourceAuthCertAppImpl) RelateAuthCert(ctx context.Context, params *Re
|
||||
|
||||
// 如果修改了用户名,且该凭证关联至标签,则需要更新对应的标签名(资源授权凭证类型的标签名为username)
|
||||
if oldAuthCert.Username != unmodifyAc.Username && acTagType != 0 {
|
||||
r.updateAuthCertTagName(ctx, unmodify, acTagType, unmodifyAc.Username)
|
||||
r.tagTreeApp.UpdateTagName(ctx, acTagType, unmodify, unmodifyAc.Username)
|
||||
}
|
||||
logx.DebugfContext(ctx, "RelateAuthCert[%d-%s]-更新授权凭证-[%v]", resourceType, resourceCode, unmodify)
|
||||
if err := r.UpdateById(ctx, unmodifyAc); err != nil {
|
||||
@@ -178,17 +175,6 @@ func (r *resourceAuthCertAppImpl) RelateAuthCert(ctx context.Context, params *Re
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *resourceAuthCertAppImpl) RelateAuthCert2ResourceTag(ctx context.Context, param *RelateAuthCertParam) error {
|
||||
return r.tagTreeApp.RelateTagsByCodeAndType(ctx, &RelateTagsByCodeAndTypeParam{
|
||||
ParentTagCode: param.ResourceCode,
|
||||
ParentTagType: param.ResourceType,
|
||||
TagType: GetResourceAuthCertTagType(param.ResourceType),
|
||||
Tags: collx.ArrayMap(param.AuthCerts, func(rac *entity.ResourceAuthCert) ITag {
|
||||
return rac
|
||||
}),
|
||||
})
|
||||
}
|
||||
|
||||
func (r *resourceAuthCertAppImpl) SaveAuthCert(ctx context.Context, rac *entity.ResourceAuthCert) error {
|
||||
if rac.Id == 0 {
|
||||
return r.addAuthCert(ctx, rac)
|
||||
@@ -258,27 +244,6 @@ func (r *resourceAuthCertAppImpl) GetResourceAuthCert(resourceType entity.TagTyp
|
||||
return r.decryptAuthCert(resourceAuthCerts[0])
|
||||
}
|
||||
|
||||
func (r *resourceAuthCertAppImpl) GetAccountAuthCert(accountId uint64, authCertTagType entity.TagType, tagPath ...string) []*entity.ResourceAuthCert {
|
||||
// 获取用户有权限操作的授权凭证资源标签
|
||||
tagQuery := &entity.TagTreeQuery{
|
||||
Type: authCertTagType,
|
||||
CodePathLikes: tagPath,
|
||||
}
|
||||
authCertTags := r.tagTreeApp.GetAccountTags(accountId, tagQuery)
|
||||
|
||||
// 获取所有授权凭证名称
|
||||
authCertNames := collx.ArrayMap(authCertTags, func(tag *entity.TagTree) string {
|
||||
return tag.Code
|
||||
})
|
||||
|
||||
var authCerts []*entity.ResourceAuthCert
|
||||
r.ListByWheres(collx.M{
|
||||
"name in ?": collx.ArrayDeduplicate(authCertNames),
|
||||
}, &authCerts)
|
||||
|
||||
return authCerts
|
||||
}
|
||||
|
||||
func (r *resourceAuthCertAppImpl) FillAuthCertByAcs(authCerts []*entity.ResourceAuthCert, resources ...entity.IAuthCert) {
|
||||
if len(resources) == 0 || len(authCerts) == 0 {
|
||||
return
|
||||
@@ -305,6 +270,12 @@ func (r *resourceAuthCertAppImpl) FillAuthCertByAcs(authCerts []*entity.Resource
|
||||
}
|
||||
}
|
||||
|
||||
func (r *resourceAuthCertAppImpl) FillAuthCertByAcNames(authCertNames []string, resources ...entity.IAuthCert) {
|
||||
var acs []*entity.ResourceAuthCert
|
||||
r.ListByWheres(collx.M{"name in ?": authCertNames}, &acs)
|
||||
r.FillAuthCertByAcs(acs, resources...)
|
||||
}
|
||||
|
||||
func (r *resourceAuthCertAppImpl) FillAuthCert(resourceType int8, resources ...entity.IAuthCert) {
|
||||
if len(resources) == 0 {
|
||||
return
|
||||
@@ -336,17 +307,17 @@ func (r *resourceAuthCertAppImpl) addAuthCert(ctx context.Context, rac *entity.R
|
||||
// 资源对应的授权凭证标签类型,若为0则说明该资源不需要关联至资源tagTree
|
||||
authCertTagType := GetResourceAuthCertTagType(entity.TagType(resourceType))
|
||||
|
||||
var resourceTagIds []uint64
|
||||
var resourceTagCodePaths []string
|
||||
// 如果该资源存在对应的授权凭证标签类型,则说明需要关联至tagTree,否则直接从授权凭证库中验证资源编号是否正确即可(一个资源最少有一个授权凭证)
|
||||
if authCertTagType != 0 {
|
||||
// 获取资源编号对应的资源标签信息
|
||||
var resourceTags []*entity.TagTree
|
||||
r.tagTreeApp.ListByCond(&entity.TagTree{Type: entity.TagType(resourceType), Code: resourceCode}, &resourceTags)
|
||||
// 资源标签id(相当于父tag id)
|
||||
resourceTagIds = collx.ArrayMap(resourceTags, func(tag *entity.TagTree) uint64 {
|
||||
return tag.Id
|
||||
// 资源标签tagPath(相当于父tag)
|
||||
resourceTagCodePaths = collx.ArrayMap(resourceTags, func(tag *entity.TagTree) string {
|
||||
return tag.CodePath
|
||||
})
|
||||
if len(resourceTagIds) == 0 {
|
||||
if len(resourceTagCodePaths) == 0 {
|
||||
return errorx.NewBiz("资源标签不存在[%s], 请检查资源编号是否正确", resourceCode)
|
||||
}
|
||||
} else {
|
||||
@@ -362,13 +333,15 @@ func (r *resourceAuthCertAppImpl) addAuthCert(ctx context.Context, rac *entity.R
|
||||
|
||||
return r.Tx(ctx, func(ctx context.Context) error {
|
||||
// 若存在需要关联到的资源标签,则关联到对应的资源标签下
|
||||
if len(resourceTagIds) > 0 {
|
||||
logx.DebugfContext(ctx, "[%d-%s]-授权凭证标签[%d-%s]关联至所属资源标签下[%v]", resourceType, resourceCode, authCertTagType, rac.Name, resourceTagIds)
|
||||
if len(resourceTagCodePaths) > 0 {
|
||||
logx.DebugfContext(ctx, "[%d-%s]-授权凭证标签[%d-%s]关联至所属资源标签下[%v]", resourceType, resourceCode, authCertTagType, rac.Name, resourceTagCodePaths)
|
||||
return r.tagTreeApp.SaveResourceTag(ctx, &SaveResourceTagParam{
|
||||
Code: rac.Name,
|
||||
Type: GetResourceAuthCertTagType(entity.TagType(resourceType)),
|
||||
Name: rac.Username,
|
||||
ParentTagIds: resourceTagIds,
|
||||
ResourceTag: &ResourceTag{
|
||||
Code: rac.Name,
|
||||
Type: GetResourceAuthCertTagType(entity.TagType(resourceType)),
|
||||
Name: rac.Username,
|
||||
},
|
||||
ParentTagCodePaths: resourceTagCodePaths,
|
||||
})
|
||||
}
|
||||
return nil
|
||||
@@ -406,7 +379,7 @@ func (r *resourceAuthCertAppImpl) updateAuthCert(ctx context.Context, rac *entit
|
||||
if rac.Username != oldRac.Username && rac.ResourceType == int8(entity.TagTypeMachine) {
|
||||
authCertTagType := GetResourceAuthCertTagType(entity.TagType(oldRac.ResourceType))
|
||||
if authCertTagType != 0 {
|
||||
if err := r.updateAuthCertTagName(ctx, oldRac.Name, authCertTagType, rac.Username); err != nil {
|
||||
if err := r.tagTreeApp.UpdateTagName(ctx, authCertTagType, oldRac.Name, rac.Username); err != nil {
|
||||
return errorx.NewBiz("同步更新授权凭证标签名称失败")
|
||||
}
|
||||
}
|
||||
@@ -425,11 +398,6 @@ func (r *resourceAuthCertAppImpl) updateAuthCert(ctx context.Context, rac *entit
|
||||
return r.UpdateById(ctx, rac)
|
||||
}
|
||||
|
||||
// updateAuthCertTagName 同步更新授权凭证的标签名,防止标签展示不一致
|
||||
func (r *resourceAuthCertAppImpl) updateAuthCertTagName(ctx context.Context, authCertName string, autyCertTagType entity.TagType, newTagName string) error {
|
||||
return r.tagTreeApp.UpdateByWheres(ctx, &entity.TagTree{Name: newTagName}, collx.M{"code = ?": authCertName, "type = ?": autyCertTagType})
|
||||
}
|
||||
|
||||
// 解密授权凭证信息
|
||||
func (r *resourceAuthCertAppImpl) decryptAuthCert(authCert *entity.ResourceAuthCert) (*entity.ResourceAuthCert, error) {
|
||||
if authCert.CiphertextType == entity.AuthCertCiphertextTypePublic {
|
||||
@@ -458,6 +426,10 @@ func GetResourceAuthCertTagType(resourceType entity.TagType) entity.TagType {
|
||||
return entity.TagTypeMachineAuthCert
|
||||
}
|
||||
|
||||
if resourceType == entity.TagTypeDb {
|
||||
return entity.TagTypeDbAuthCert
|
||||
}
|
||||
|
||||
// 该资源不存在对应的授权凭证标签,即tag_tree不关联该资源的授权凭证
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -14,30 +14,26 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// 标签接口,实现了该接口的结构体默认都可以当成标签树的一种标签
|
||||
type ITag interface {
|
||||
// 资源标签code
|
||||
GetCode() string
|
||||
// 保存资源标签参数
|
||||
type SaveResourceTagParam struct {
|
||||
ParentTagCodePaths []string // 关联标签,空数组则为删除该资源绑定的标签
|
||||
|
||||
// 资源标签名
|
||||
GetName() string
|
||||
ResourceTag *ResourceTag // 资源标签信息
|
||||
}
|
||||
|
||||
// 保存标签参数
|
||||
type SaveResourceTagParam struct {
|
||||
type ResourceTag struct {
|
||||
Code string
|
||||
Name string
|
||||
Type entity.TagType
|
||||
Name string
|
||||
|
||||
ParentTagIds []uint64 // 关联标签,空数组则为删除该资源绑定的标签
|
||||
Children []*ResourceTag // 子资源标签
|
||||
}
|
||||
|
||||
type RelateTagsByCodeAndTypeParam struct {
|
||||
ParentTagCode string // 父标签编号
|
||||
ParentTagType entity.TagType // 父标签类型
|
||||
|
||||
TagType entity.TagType // 要关联的标签类型
|
||||
Tags []ITag // 要关联的标签数组
|
||||
Tags []*ResourceTag // 要关联的标签数组
|
||||
}
|
||||
|
||||
type DelResourceTagParam struct {
|
||||
@@ -45,8 +41,6 @@ type DelResourceTagParam struct {
|
||||
ResourceCode string
|
||||
ResourceType entity.TagType
|
||||
|
||||
Pid uint64 //父标签 pid
|
||||
|
||||
// 要删除的子节点类型,若存在值,则为删除资源标签下的指定类型的子标签
|
||||
ChildType entity.TagType
|
||||
}
|
||||
@@ -56,37 +50,46 @@ type TagTree interface {
|
||||
|
||||
ListByQuery(condition *entity.TagTreeQuery, toEntity any)
|
||||
|
||||
Save(ctx context.Context, tt *entity.TagTree) error
|
||||
SaveTag(ctx context.Context, pid uint64, tt *entity.TagTree) error
|
||||
|
||||
Delete(ctx context.Context, id uint64) error
|
||||
|
||||
// 获取指定账号有权限操作的标签列表
|
||||
// GetAccountTags 获取指定账号有权限操作的标签列表
|
||||
// @param accountId 账号id
|
||||
// @param query 查询条件
|
||||
GetAccountTags(accountId uint64, query *entity.TagTreeQuery) []*entity.TagTree
|
||||
|
||||
// 获取指定账号有权限操作的标签codes
|
||||
// GetAccountTagCodes 获取指定账号有权限操作的标签codes
|
||||
GetAccountTagCodes(accountId uint64, resourceType int8, tagPath string) []string
|
||||
|
||||
// GetAccountTagCodePaths 获取指定账号有权限操作的codePaths
|
||||
GetAccountTagCodePaths(accountId uint64, tagType entity.TagType, tagPath string) []string
|
||||
|
||||
// SaveResourceTag 保存资源类型标签
|
||||
SaveResourceTag(ctx context.Context, param *SaveResourceTagParam) error
|
||||
|
||||
// RelateTagsByCodeAndType 将指定标签数组关联至满足指定标签类型和标签code的标签下
|
||||
RelateTagsByCodeAndType(ctx context.Context, param *RelateTagsByCodeAndTypeParam) error
|
||||
|
||||
// UpdateTagName 根据标签类型与code更新对应标签名
|
||||
UpdateTagName(ctx context.Context, tagType entity.TagType, tagCode string, tagName string) error
|
||||
|
||||
// UpdateParentTagCode 更新指定类型标签的所有父标签编号
|
||||
UpdateParentTagCode(ctx context.Context, tagType entity.TagType, tagCode string, newParentCode string, newParentName string) error
|
||||
|
||||
// DeleteTagByParam 删除标签,会删除该标签下所有子标签信息以及团队关联的标签信息
|
||||
DeleteTagByParam(ctx context.Context, param *DelResourceTagParam) error
|
||||
|
||||
// 根据标签类型和标签code获取对应的标签路径列表
|
||||
ListTagPathByTypeAndCode(resourceType int8, resourceCode string) []string
|
||||
|
||||
// 根据账号id获取其可访问标签信息
|
||||
// ListTagByAccountId 根据账号id获取其可访问标签信息
|
||||
ListTagByAccountId(accountId uint64) []string
|
||||
|
||||
// 账号是否有权限访问该标签关联的资源信息
|
||||
// CanAccess 账号是否有权限访问该标签关联的资源信息
|
||||
CanAccess(accountId uint64, tagPath ...string) error
|
||||
|
||||
// 填充资源的标签信息
|
||||
// FillTagInfo 填充资源的标签信息
|
||||
FillTagInfo(resourceTagType entity.TagType, resources ...entity.ITagResource)
|
||||
}
|
||||
|
||||
@@ -101,15 +104,15 @@ func (p *tagTreeAppImpl) InjectTagTreeRepo(tagTreeRepo repository.TagTree) {
|
||||
p.Repo = tagTreeRepo
|
||||
}
|
||||
|
||||
func (p *tagTreeAppImpl) Save(ctx context.Context, tag *entity.TagTree) error {
|
||||
func (p *tagTreeAppImpl) SaveTag(ctx context.Context, pid uint64, tag *entity.TagTree) error {
|
||||
accountId := contextx.GetLoginAccount(ctx).Id
|
||||
// 新建项目树节点信息
|
||||
if tag.Id == 0 {
|
||||
if strings.Contains(tag.Code, entity.CodePathSeparator) {
|
||||
return errorx.NewBiz("标识符不能包含'/'")
|
||||
}
|
||||
if tag.Pid != 0 {
|
||||
parentTag, err := p.GetById(new(entity.TagTree), tag.Pid)
|
||||
if pid != 0 {
|
||||
parentTag, err := p.GetById(new(entity.TagTree), pid)
|
||||
if err != nil {
|
||||
return errorx.NewBiz("父节点不存在")
|
||||
}
|
||||
@@ -214,129 +217,59 @@ func (p *tagTreeAppImpl) GetAccountTagCodes(accountId uint64, resourceType int8,
|
||||
return collx.MapKeys(code2Resource)
|
||||
}
|
||||
|
||||
func (p *tagTreeAppImpl) GetAccountTagCodePaths(accountId uint64, tagType entity.TagType, tagPath string) []string {
|
||||
tagResources := p.GetAccountTags(accountId, &entity.TagTreeQuery{Type: tagType, CodePathLikes: []string{tagPath}})
|
||||
// resouce code去重
|
||||
code2Resource := collx.ArrayToMap[*entity.TagTree, string](tagResources, func(val *entity.TagTree) string {
|
||||
return val.CodePath
|
||||
})
|
||||
|
||||
return collx.MapKeys(code2Resource)
|
||||
}
|
||||
|
||||
func (p *tagTreeAppImpl) SaveResourceTag(ctx context.Context, param *SaveResourceTagParam) error {
|
||||
code := param.Code
|
||||
tagType := entity.TagType(param.Type)
|
||||
name := param.Name
|
||||
tagIds := param.ParentTagIds
|
||||
code := param.ResourceTag.Code
|
||||
tagType := param.ResourceTag.Type
|
||||
parentTagCodePaths := param.ParentTagCodePaths
|
||||
|
||||
if code == "" {
|
||||
return errorx.NewBiz("资源编号不能为空")
|
||||
return errorx.NewBiz("保存资源标签失败: 资源编号不能为空")
|
||||
}
|
||||
if tagType == 0 {
|
||||
return errorx.NewBiz("资源类型不能为空")
|
||||
return errorx.NewBiz("保存资源标签失败:资源类型不能为空")
|
||||
}
|
||||
|
||||
// 如果tagIds为空数组,则为删除该资源标签
|
||||
if len(tagIds) == 0 {
|
||||
if len(parentTagCodePaths) == 0 {
|
||||
return p.DeleteTagByParam(ctx, &DelResourceTagParam{
|
||||
ResourceType: tagType,
|
||||
ResourceCode: code,
|
||||
})
|
||||
}
|
||||
|
||||
if name == "" {
|
||||
name = code
|
||||
// 获取所有关联的子标签
|
||||
var parentTags []*entity.TagTree
|
||||
p.ListByWheres(collx.M{
|
||||
"code_path in ?": parentTagCodePaths,
|
||||
}, &parentTags)
|
||||
if len(parentTags) == 0 || len(parentTags) != len(parentTagCodePaths) {
|
||||
return errorx.NewBiz("保存资源标签失败: 存在错误的关联标签")
|
||||
}
|
||||
|
||||
newTags := p.toTags(parentTags, param.ResourceTag)
|
||||
|
||||
var oldParentTagTree []*entity.TagTree
|
||||
p.ListByCond(&entity.TagTree{Type: tagType, Code: code}, &oldParentTagTree)
|
||||
|
||||
// 该资源对应的旧资源标签信息
|
||||
var oldTagTree []*entity.TagTree
|
||||
p.ListByCond(&entity.TagTree{Type: tagType, Code: code}, &oldTagTree)
|
||||
|
||||
var addTagIds, delTagIds []uint64
|
||||
if len(oldTagTree) == 0 {
|
||||
addTagIds = tagIds
|
||||
} else {
|
||||
oldTagIds := collx.ArrayMap(oldTagTree, func(tag *entity.TagTree) uint64 {
|
||||
return tag.Pid
|
||||
})
|
||||
addTagIds, delTagIds, _ = collx.ArrayCompare[uint64](tagIds, oldTagIds)
|
||||
}
|
||||
|
||||
if len(addTagIds) > 0 {
|
||||
addTagResource := make([]*entity.TagTree, 0)
|
||||
for _, tagId := range addTagIds {
|
||||
tag, err := p.GetById(new(entity.TagTree), tagId)
|
||||
if err != nil {
|
||||
return errorx.NewBiz("存在错误标签id")
|
||||
}
|
||||
addTagResource = append(addTagResource, &entity.TagTree{
|
||||
Pid: tagId,
|
||||
Code: code,
|
||||
Type: tagType,
|
||||
Name: name,
|
||||
CodePath: fmt.Sprintf("%s%d%s%s%s", tag.CodePath, tagType, entity.CodePathResourceSeparator, code, entity.CodePathSeparator), // tag1/tag2/1|resourceCode1/11|resourceCode2
|
||||
})
|
||||
|
||||
}
|
||||
if err := p.BatchInsert(ctx, addTagResource); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if len(delTagIds) > 0 {
|
||||
for _, tagId := range delTagIds {
|
||||
if err := p.DeleteTagByParam(ctx, &DelResourceTagParam{
|
||||
ResourceType: tagType,
|
||||
ResourceCode: code,
|
||||
Pid: tagId,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *tagTreeAppImpl) RelateTagsByCodeAndType(ctx context.Context, param *RelateTagsByCodeAndTypeParam) error {
|
||||
parentTagCode := param.ParentTagCode
|
||||
parentTagType := param.ParentTagType
|
||||
tagType := param.TagType
|
||||
|
||||
// 如果资源为,则表示清楚关联
|
||||
if len(param.Tags) == 0 {
|
||||
// 删除该资源下的所有指定类型的资源
|
||||
return p.DeleteTagByParam(ctx, &DelResourceTagParam{
|
||||
ResourceCode: parentTagCode,
|
||||
ResourceType: param.ParentTagType,
|
||||
ChildType: tagType,
|
||||
})
|
||||
}
|
||||
|
||||
// 获取满足指定编号与类型的所有标签信息
|
||||
var parentTags []*entity.TagTree
|
||||
p.ListByCond(&entity.TagTree{Type: parentTagType, Code: parentTagCode}, &parentTags)
|
||||
// 标签id(相当于需要关联的标签数组的父tag id)
|
||||
parentTagIds := collx.ArrayMap(parentTags, func(tag *entity.TagTree) uint64 {
|
||||
return tag.Id
|
||||
})
|
||||
|
||||
if len(parentTagIds) == 0 {
|
||||
return errorx.NewBiz("不存在满足[type=%d, code=%s]的标签", parentTagType, parentTagCode)
|
||||
}
|
||||
|
||||
var oldChildrenTags []*entity.TagTree
|
||||
// 获取该资源的所有旧的该类型的子标签
|
||||
p.ListByQuery(&entity.TagTreeQuery{
|
||||
CodePathLikes: collx.ArrayMap[*entity.TagTree, string](parentTags, func(val *entity.TagTree) string { return val.CodePath }),
|
||||
Type: tagType,
|
||||
}, &oldChildrenTags)
|
||||
|
||||
// 组合新的授权凭证资源标签
|
||||
newTags := make([]*entity.TagTree, 0)
|
||||
for _, resourceTag := range parentTags {
|
||||
for _, resource := range param.Tags {
|
||||
tagCode := resource.GetCode()
|
||||
newTags = append(newTags, &entity.TagTree{
|
||||
Pid: resourceTag.Id,
|
||||
Type: tagType,
|
||||
Code: tagCode,
|
||||
CodePath: fmt.Sprintf("%s%d%s%s%s", resourceTag.CodePath, tagType, entity.CodePathResourceSeparator, tagCode, entity.CodePathSeparator),
|
||||
Name: resource.GetName(),
|
||||
})
|
||||
}
|
||||
if len(oldParentTagTree) > 0 {
|
||||
// 获取所有旧的子标签
|
||||
p.ListByQuery(&entity.TagTreeQuery{
|
||||
CodePathLikes: collx.ArrayMap[*entity.TagTree, string](oldParentTagTree, func(val *entity.TagTree) string {
|
||||
return val.CodePath
|
||||
}),
|
||||
}, &oldChildrenTags)
|
||||
}
|
||||
|
||||
// 旧的codePath -> tag
|
||||
@@ -348,8 +281,7 @@ func (p *tagTreeAppImpl) RelateTagsByCodeAndType(ctx context.Context, param *Rel
|
||||
addCodePaths, delCodePaths, _ = collx.ArrayCompare(collx.MapKeys(newCodePath2Tag), collx.MapKeys(oldCodePath2Tag))
|
||||
|
||||
if len(addCodePaths) > 0 {
|
||||
logx.DebugfContext(ctx, "RelateTags2CodeAndType[%d-%s]-新增标签[%v]", parentTagType, parentTagCode, addCodePaths)
|
||||
|
||||
logx.DebugfContext(ctx, "SaveResourceTag-新增标签[%v]", addCodePaths)
|
||||
addTags := make([]*entity.TagTree, 0)
|
||||
for _, addCodePath := range addCodePaths {
|
||||
addTags = append(addTags, newCodePath2Tag[addCodePath])
|
||||
@@ -360,19 +292,97 @@ func (p *tagTreeAppImpl) RelateTagsByCodeAndType(ctx context.Context, param *Rel
|
||||
}
|
||||
|
||||
if len(delCodePaths) > 0 {
|
||||
logx.DebugfContext(ctx, "RelateTags2CodeAndType[%d-%s]-删除标签[%v]", parentTagType, parentTagCode, delCodePaths)
|
||||
logx.DebugfContext(ctx, "SaveResourceTag-删除标签[%v]", delCodePaths)
|
||||
// 删除team关联的标签
|
||||
if err := p.tagTreeTeamRepo.DeleteByWheres(ctx, collx.M{"tag_path in ?": delCodePaths}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := p.DeleteByWheres(ctx, collx.M{"code_path in ?": delCodePaths}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for _, delCodePath := range delCodePaths {
|
||||
oldTag := oldCodePath2Tag[delCodePath]
|
||||
if oldTag == nil {
|
||||
continue
|
||||
}
|
||||
if err := p.DeleteTagByParam(ctx, &DelResourceTagParam{
|
||||
Id: oldTag.Id,
|
||||
}); err != nil {
|
||||
return err
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *tagTreeAppImpl) RelateTagsByCodeAndType(ctx context.Context, param *RelateTagsByCodeAndTypeParam) error {
|
||||
parentTagCode := param.ParentTagCode
|
||||
parentTagType := param.ParentTagType
|
||||
|
||||
// 获取满足指定编号与类型的所有标签信息
|
||||
var parentTags []*entity.TagTree
|
||||
p.ListByCond(&entity.TagTree{Type: parentTagType, Code: parentTagCode}, &parentTags)
|
||||
// 标签codePaths(相当于需要关联的标签数组的父tag)
|
||||
parentTagCodePaths := collx.ArrayMap(parentTags, func(tag *entity.TagTree) string {
|
||||
return tag.CodePath
|
||||
})
|
||||
|
||||
if len(parentTagCodePaths) == 0 {
|
||||
return errorx.NewBiz("不存在满足[type=%d, code=%s]的标签", parentTagType, parentTagCode)
|
||||
}
|
||||
|
||||
for _, tag := range param.Tags {
|
||||
if err := (p.SaveResourceTag(ctx, &SaveResourceTagParam{
|
||||
ResourceTag: tag,
|
||||
ParentTagCodePaths: parentTagCodePaths,
|
||||
})); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *tagTreeAppImpl) UpdateTagName(ctx context.Context, tagType entity.TagType, tagCode string, tagName string) error {
|
||||
return p.UpdateByWheres(ctx, &entity.TagTree{Name: tagName}, collx.Kvs("code = ?", tagCode, "type = ?", tagType))
|
||||
}
|
||||
|
||||
func (p *tagTreeAppImpl) UpdateParentTagCode(ctx context.Context, tagType entity.TagType, tagCode string, newParentCode string, newParentName string) error {
|
||||
// 获取资源编号对应的资源标签信息
|
||||
var resourceTags []*entity.TagTree
|
||||
p.ListByCond(&entity.TagTree{Type: tagType, Code: tagCode}, &resourceTags)
|
||||
if len(resourceTags) == 0 {
|
||||
logx.WarnfContext(ctx, "UpdateParentTagCode-[%d-%s]标签信息不存在", tagType, tagCode)
|
||||
return nil
|
||||
}
|
||||
|
||||
// 获取该资源编号对应的所有父资源标签信息
|
||||
var resourceParentTags []*entity.TagTree
|
||||
p.ListByWheres(collx.Kvs("code_path in ?", collx.ArrayMap(resourceTags, func(tag *entity.TagTree) string {
|
||||
return tag.GetParentPath(0)
|
||||
})), &resourceParentTags)
|
||||
|
||||
// 获取该资源编号对应的所有子资源标签信息
|
||||
var resourceChildrenTags []*entity.TagTree
|
||||
p.ListByQuery(&entity.TagTreeQuery{CodePathLikes: collx.ArrayMap(resourceTags, func(tag *entity.TagTree) string {
|
||||
return tag.CodePath
|
||||
})}, &resourceChildrenTags)
|
||||
|
||||
// 更新父标签的codepath
|
||||
for _, tag := range resourceChildrenTags {
|
||||
pathSection := entity.GetTagPathSections(tag.CodePath)
|
||||
for i, ps := range pathSection {
|
||||
if ps.Type == tagType && ps.Code == tagCode {
|
||||
// 将父标签编号修改为对应的新编号
|
||||
pathSection[i-1].Code = newParentCode
|
||||
}
|
||||
}
|
||||
tag.CodePath = pathSection.ToCodePath()
|
||||
p.UpdateById(ctx, tag)
|
||||
}
|
||||
|
||||
// 更新资源标签的code与codePath,貌似这段可以不用,先留着
|
||||
for _, parentTag := range resourceParentTags {
|
||||
pathSection := entity.GetTagPathSections(parentTag.CodePath)
|
||||
pathSection[len(pathSection)-1].Code = newParentCode
|
||||
newCodePath := pathSection.ToCodePath()
|
||||
// 若新的父级标签路径已存在,则不更新该父标签,避免出现重复的标签路径
|
||||
if p.CountByCond(&entity.TagTree{CodePath: newCodePath}) > 0 {
|
||||
continue
|
||||
}
|
||||
parentTag.CodePath = newCodePath
|
||||
parentTag.Code = newParentCode
|
||||
parentTag.Name = newParentName
|
||||
p.UpdateById(ctx, parentTag)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -381,12 +391,12 @@ func (p *tagTreeAppImpl) RelateTagsByCodeAndType(ctx context.Context, param *Rel
|
||||
func (p *tagTreeAppImpl) DeleteTagByParam(ctx context.Context, param *DelResourceTagParam) error {
|
||||
// 获取资源编号对应的资源标签信息
|
||||
var resourceTags []*entity.TagTree
|
||||
cond := &entity.TagTree{Type: param.ResourceType, Code: param.ResourceCode, Pid: param.Pid}
|
||||
cond := &entity.TagTree{Type: param.ResourceType, Code: param.ResourceCode}
|
||||
cond.Id = param.Id
|
||||
p.ListByCond(cond, &resourceTags)
|
||||
|
||||
if len(resourceTags) == 0 {
|
||||
logx.DebugfContext(ctx, "TagTreeApp.DeleteResource[%d-%s]不存在可删除的标签", param.ResourceType, param.ResourceCode)
|
||||
logx.DebugfContext(ctx, "TagTreeApp.DeleteTagByParam[%d-%s]不存在可删除的标签", param.ResourceType, param.ResourceCode)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -405,7 +415,7 @@ func (p *tagTreeAppImpl) DeleteTagByParam(ctx context.Context, param *DelResourc
|
||||
childrenTagIds := collx.ArrayMap(childrenTag, func(item *entity.TagTree) uint64 {
|
||||
return item.Id
|
||||
})
|
||||
// 删除所有code_path下的子标签
|
||||
// 删除code_path下的所有子标签
|
||||
if err := p.DeleteByWheres(ctx, collx.M{
|
||||
"id in ?": childrenTagIds,
|
||||
}); err != nil {
|
||||
@@ -413,7 +423,9 @@ func (p *tagTreeAppImpl) DeleteTagByParam(ctx context.Context, param *DelResourc
|
||||
}
|
||||
|
||||
// 删除team关联的标签
|
||||
return p.tagTreeTeamRepo.DeleteByWheres(ctx, collx.M{"tag_id in ?": childrenTagIds})
|
||||
if err := p.tagTreeTeamRepo.DeleteByWheres(ctx, collx.M{"tag_id in ?": childrenTagIds}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -464,7 +476,7 @@ func (p *tagTreeAppImpl) FillTagInfo(resourceTagType entity.TagType, resources .
|
||||
|
||||
for _, tr := range tagResources {
|
||||
// 赋值标签信息
|
||||
resourceCode2Resouce[tr.Code].SetTagInfo(entity.ResourceTag{TagId: tr.Pid, TagPath: tr.GetTagPath()})
|
||||
resourceCode2Resouce[tr.Code].SetTagInfo(entity.ResourceTag{CodePath: tr.CodePath})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -478,14 +490,52 @@ func (p *tagTreeAppImpl) Delete(ctx context.Context, id uint64) error {
|
||||
return errorx.NewBiz("您无权删除该标签")
|
||||
}
|
||||
|
||||
if p.CountByCond(&entity.TagTree{Pid: id}) > 0 {
|
||||
return errorx.NewBiz("请先移除该标签关联的资源")
|
||||
}
|
||||
|
||||
return p.Tx(ctx, func(ctx context.Context) error {
|
||||
return p.DeleteById(ctx, id)
|
||||
return p.DeleteTagByParam(ctx, &DelResourceTagParam{
|
||||
Id: id,
|
||||
})
|
||||
}, func(ctx context.Context) error {
|
||||
// 删除该标签关联的团队信息
|
||||
return p.tagTreeTeamRepo.DeleteByCond(ctx, &entity.TagTreeTeam{TagId: id})
|
||||
})
|
||||
}
|
||||
|
||||
func (p *tagTreeAppImpl) toTags(parentTags []*entity.TagTree, param *ResourceTag) []*entity.TagTree {
|
||||
tags := make([]*entity.TagTree, 0)
|
||||
|
||||
// 递归函数,将标签及其子标签展开为一个扁平数组
|
||||
var flattenTags func(parentTag *entity.TagTree, tag *ResourceTag)
|
||||
flattenTags = func(parentTag *entity.TagTree, resourceTagParam *ResourceTag) {
|
||||
if resourceTagParam == nil {
|
||||
return
|
||||
}
|
||||
|
||||
tagType := resourceTagParam.Type
|
||||
tagCode := resourceTagParam.Code
|
||||
tagName := resourceTagParam.Name
|
||||
if tagName == "" {
|
||||
tagName = tagCode
|
||||
}
|
||||
|
||||
tag := &entity.TagTree{
|
||||
Code: tagCode,
|
||||
CodePath: fmt.Sprintf("%s%d%s%s%s", parentTag.CodePath, tagType, entity.CodePathResourceSeparator, tagCode, entity.CodePathSeparator), // tag1/tag2/1|resourceCode1/11|resourceCode2/
|
||||
Type: tagType,
|
||||
Name: tagName,
|
||||
}
|
||||
// 将当前标签加入数组
|
||||
tags = append(tags, tag)
|
||||
|
||||
// 递归处理子标签
|
||||
for _, child := range resourceTagParam.Children {
|
||||
flattenTags(tag, child)
|
||||
}
|
||||
}
|
||||
|
||||
for _, parentTag := range parentTags {
|
||||
// 开始展开标签
|
||||
flattenTags(parentTag, param)
|
||||
}
|
||||
|
||||
return tags
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import "mayfly-go/pkg/model"
|
||||
type TagTreeQuery struct {
|
||||
model.Model
|
||||
|
||||
Pid uint64
|
||||
Type TagType `json:"type"`
|
||||
Code string `json:"code"` // 标识
|
||||
Codes []string
|
||||
|
||||
@@ -97,16 +97,6 @@ func (m *ResourceAuthCert) GetExtraString(key string) string {
|
||||
return cast.ToString(m.Extra[key])
|
||||
}
|
||||
|
||||
// IResourceTag接口 授权凭证名 -> 资源标签code
|
||||
func (m *ResourceAuthCert) GetCode() string {
|
||||
return m.Name
|
||||
}
|
||||
|
||||
// IResourceTag接口 授权凭证用户名 -> 资源标签名
|
||||
func (m *ResourceAuthCert) GetName() string {
|
||||
return m.Username
|
||||
}
|
||||
|
||||
// HasChanged 与指定授权凭证比较是否有变更
|
||||
func (m *ResourceAuthCert) HasChanged(rac *ResourceAuthCert) bool {
|
||||
if rac == nil {
|
||||
|
||||
@@ -1,19 +1,22 @@
|
||||
package entity
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"mayfly-go/internal/common/consts"
|
||||
"mayfly-go/pkg/model"
|
||||
"mayfly-go/pkg/utils/collx"
|
||||
"strings"
|
||||
|
||||
"github.com/may-fly/cast"
|
||||
)
|
||||
|
||||
// 标签树
|
||||
type TagTree struct {
|
||||
model.Model
|
||||
|
||||
Pid uint64 `json:"pid"`
|
||||
Type TagType `json:"type"` // 类型: -1.普通标签; 其他值则为对应的资源类型
|
||||
Code string `json:"code"` // 标识编码, 若类型不为-1,则为对应资源编码
|
||||
CodePath string `json:"codePath"` // 标识路径
|
||||
CodePath string `json:"codePath"` // 标识路径,tag1/tag2/tagType1|tagCode/tagType2|yyycode/,非普通标签类型段含有标签类型
|
||||
Name string `json:"name"` // 名称
|
||||
Remark string `json:"remark"` // 备注说明
|
||||
}
|
||||
@@ -28,18 +31,22 @@ const (
|
||||
|
||||
TagTypeTag TagType = -1
|
||||
TagTypeMachine TagType = TagType(consts.ResourceTypeMachine)
|
||||
TagTypeDb TagType = TagType(consts.ResourceTypeDb)
|
||||
TagTypeDb TagType = TagType(consts.ResourceTypeDb) // 数据库实例
|
||||
TagTypeRedis TagType = TagType(consts.ResourceTypeRedis)
|
||||
TagTypeMongo TagType = TagType(consts.ResourceTypeMongo)
|
||||
|
||||
// ----- (单独声明各个资源的授权凭证类型而不统一使用一个授权凭证类型是为了获取登录账号的授权凭证标签(ResourceAuthCertApp.GetAccountAuthCert)时,避免查出所有资源的授权凭证)
|
||||
|
||||
TagTypeMachineAuthCert TagType = 11 // 机器-授权凭证
|
||||
|
||||
TagTypeDbAuthCert TagType = 21 // 数据库-授权凭证
|
||||
TagTypeDbName TagType = 22 // 数据库名
|
||||
)
|
||||
|
||||
// GetRootCode 获取根路径信息
|
||||
func (pt *TagTree) GetRootCode() string {
|
||||
return strings.Split(pt.CodePath, CodePathSeparator)[0]
|
||||
func (pt *TagTree) IsRoot() bool {
|
||||
// 去除路径两端可能存在的斜杠
|
||||
path := strings.Trim(pt.CodePath, "/")
|
||||
return len(strings.Split(path, "/")) == 1
|
||||
}
|
||||
|
||||
// GetParentPath 获取父标签路径, 如CodePath = test/test1/test2/ -> index = 0 => test/test1/ index = 1 => test/
|
||||
@@ -98,13 +105,11 @@ type ITagResource interface {
|
||||
|
||||
// 资源关联的标签信息
|
||||
type ResourceTag struct {
|
||||
TagId uint64 `json:"tagId" gorm:"-"`
|
||||
TagPath string `json:"tagPath" gorm:"-"` // 标签路径
|
||||
CodePath string `json:"codePath" gorm:"-"` // 标签路径
|
||||
}
|
||||
|
||||
func (r *ResourceTag) SetTagInfo(rt ResourceTag) {
|
||||
r.TagId = rt.TagId
|
||||
r.TagPath = rt.TagPath
|
||||
r.CodePath = rt.CodePath
|
||||
}
|
||||
|
||||
// 资源标签列表
|
||||
@@ -118,3 +123,66 @@ func (r *ResourceTags) SetTagInfo(rt ResourceTag) {
|
||||
}
|
||||
r.Tags = append(r.Tags, rt)
|
||||
}
|
||||
|
||||
// GetCodeByPath 从codePaths中提取指定标签类型的所有tagCode并去重
|
||||
// 如:codePaths = tag1/tag2/1|xxxcode/11|yyycode/, tagType = 1 -> xxxcode, tagType = 11 -> yyycode
|
||||
func GetCodeByPath(tagType TagType, codePaths ...string) []string {
|
||||
var codes []string
|
||||
for _, codePath := range codePaths {
|
||||
// tag1/tag2/1|xxxcode/11|yyycode,根据 /tagType|resourceCode进行切割
|
||||
splStrs := strings.Split(codePath, fmt.Sprintf("%s%d%s", CodePathSeparator, tagType, CodePathResourceSeparator))
|
||||
if len(splStrs) < 2 {
|
||||
continue
|
||||
}
|
||||
|
||||
codes = append(codes, strings.Split(splStrs[1], CodePathSeparator)[0])
|
||||
}
|
||||
|
||||
return collx.ArrayDeduplicate[string](codes)
|
||||
}
|
||||
|
||||
// TagPathSection 标签路径段
|
||||
type TagPathSection struct {
|
||||
Type TagType `json:"type"` // 类型: -1.普通标签; 其他值则为对应的资源类型
|
||||
Code string `json:"code"` // 标识编码, 若类型不为-1,则为对应资源编码
|
||||
}
|
||||
|
||||
type TagPathSections []*TagPathSection // 标签段数组
|
||||
|
||||
// 转为codePath
|
||||
func (tps TagPathSections) ToCodePath() string {
|
||||
return strings.Join(collx.ArrayMap(tps, func(tp *TagPathSection) string {
|
||||
if tp.Type == TagTypeTag {
|
||||
return tp.Code
|
||||
}
|
||||
return fmt.Sprintf("%d%s%s", tp.Type, CodePathResourceSeparator, tp.Code)
|
||||
}), CodePathSeparator) + CodePathSeparator
|
||||
}
|
||||
|
||||
// GetTagPathSections 根据标签路径获取路径段落
|
||||
func GetTagPathSections(codePath string) TagPathSections {
|
||||
codePath = strings.TrimSuffix(codePath, CodePathSeparator)
|
||||
var sections TagPathSections
|
||||
|
||||
codes := strings.Split(codePath, CodePathSeparator)
|
||||
for _, code := range codes {
|
||||
typeAndCode := strings.Split(code, CodePathResourceSeparator)
|
||||
var tagType TagType
|
||||
var tagCode string
|
||||
|
||||
if len(typeAndCode) < 2 {
|
||||
tagType = TagTypeTag
|
||||
tagCode = typeAndCode[0]
|
||||
} else {
|
||||
tagType = TagType(cast.ToInt(typeAndCode[0]))
|
||||
tagCode = typeAndCode[1]
|
||||
}
|
||||
|
||||
sections = append(sections, &TagPathSection{
|
||||
Type: tagType,
|
||||
Code: tagCode,
|
||||
})
|
||||
}
|
||||
|
||||
return sections
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ func newTagTreeRepo() repository.TagTree {
|
||||
}
|
||||
|
||||
func (p *tagTreeRepoImpl) SelectByCondition(condition *entity.TagTreeQuery, toEntity any, orderBy ...string) {
|
||||
sql := "SELECT DISTINCT(p.id), p.pid, p.type, p.code, p.code_path, p.name, p.remark, p.create_time, p.creator, p.update_time, p.modifier FROM t_tag_tree p WHERE p.is_deleted = 0 "
|
||||
sql := "SELECT DISTINCT(p.id), p.type, p.code, p.code_path, p.name, p.remark, p.create_time, p.creator, p.update_time, p.modifier FROM t_tag_tree p WHERE p.is_deleted = 0 "
|
||||
|
||||
params := make([]any, 0)
|
||||
if condition.Name != "" {
|
||||
@@ -39,10 +39,6 @@ func (p *tagTreeRepoImpl) SelectByCondition(condition *entity.TagTreeQuery, toEn
|
||||
sql = sql + " AND p.code_path LIKE ?"
|
||||
params = append(params, condition.CodePathLike+"%")
|
||||
}
|
||||
if condition.Pid != 0 {
|
||||
sql = sql + " AND p.pid = ?"
|
||||
params = append(params, condition.Pid)
|
||||
}
|
||||
if condition.Type != 0 {
|
||||
sql = sql + " AND p.type = ?"
|
||||
params = append(params, condition.Type)
|
||||
|
||||
@@ -4,7 +4,7 @@ import "fmt"
|
||||
|
||||
const (
|
||||
AppName = "mayfly-go"
|
||||
Version = "v1.8.0"
|
||||
Version = "v1.8.1"
|
||||
)
|
||||
|
||||
func GetAppInfo() string {
|
||||
|
||||
145
server/resources/script/sql/v1.8/v1.8.1.sql
Normal file
145
server/resources/script/sql/v1.8/v1.8.1.sql
Normal file
@@ -0,0 +1,145 @@
|
||||
-- 关联数据库实例至标签
|
||||
INSERT
|
||||
INTO
|
||||
t_tag_tree (code_path,
|
||||
code,
|
||||
type,
|
||||
NAME,
|
||||
create_time,
|
||||
creator_id,
|
||||
creator,
|
||||
update_time,
|
||||
modifier_id,
|
||||
modifier,
|
||||
is_deleted )
|
||||
SELECT
|
||||
DISTINCT t1.newCodePath,
|
||||
t1.tagCode,
|
||||
100,
|
||||
t1.tagName,
|
||||
DATE_FORMAT( NOW(), '%Y-%m-%d %H:%i:%s' ),
|
||||
1,
|
||||
'admin',
|
||||
DATE_FORMAT( NOW(), '%Y-%m-%d %H:%i:%s' ),
|
||||
1,
|
||||
'admin',
|
||||
0
|
||||
FROM
|
||||
(
|
||||
SELECT
|
||||
ttt.code_path,
|
||||
ttt2.code_path as p_code_path,
|
||||
td.name,
|
||||
td.auth_cert_name,
|
||||
tdi.code tagCode,
|
||||
tdi.name tagName,
|
||||
CONCAT(ttt2.code_path, '2|', tdi.code, '/') newCodePath
|
||||
FROM
|
||||
t_tag_tree ttt
|
||||
JOIN t_db td ON
|
||||
ttt.code = td.code
|
||||
AND ttt.type = 2
|
||||
AND td.is_deleted = 0
|
||||
JOIN t_tag_tree ttt2 ON
|
||||
ttt.pid = ttt2.id
|
||||
JOIN t_db_instance tdi ON
|
||||
tdi.id = td.instance_id) AS t1;
|
||||
|
||||
|
||||
-- 关联数据库实例的授权凭证信息至标签
|
||||
INSERT
|
||||
INTO
|
||||
t_tag_tree (code_path,
|
||||
code,
|
||||
type,
|
||||
NAME,
|
||||
create_time,
|
||||
creator_id,
|
||||
creator,
|
||||
update_time,
|
||||
modifier_id,
|
||||
modifier,
|
||||
is_deleted )
|
||||
SELECT
|
||||
DISTINCT t1.newCodePath,
|
||||
t1.tagCode,
|
||||
21,
|
||||
t1.tagName,
|
||||
DATE_FORMAT( NOW(), '%Y-%m-%d %H:%i:%s' ),
|
||||
1,
|
||||
'admin',
|
||||
DATE_FORMAT( NOW(), '%Y-%m-%d %H:%i:%s' ),
|
||||
1,
|
||||
'admin',
|
||||
0
|
||||
FROM
|
||||
(
|
||||
SELECT
|
||||
ttt.code_path,
|
||||
ttt2.code_path as p_code_path,
|
||||
td.name,
|
||||
td.auth_cert_name,
|
||||
trac.name tagCode,
|
||||
trac.username tagName,
|
||||
CONCAT(ttt2.code_path, '2|', tdi.code, '/21|', trac.name, '/') as newCodePath
|
||||
FROM
|
||||
t_tag_tree ttt
|
||||
JOIN t_db td ON
|
||||
ttt.code = td.code
|
||||
AND ttt.type = 2
|
||||
AND td.is_deleted = 0
|
||||
JOIN t_tag_tree ttt2 ON
|
||||
ttt.pid = ttt2.id
|
||||
JOIN t_db_instance tdi ON
|
||||
tdi.id = td.instance_id
|
||||
JOIN t_resource_auth_cert trac ON
|
||||
trac.name = td.auth_cert_name
|
||||
AND trac.is_deleted = 0) AS t1;
|
||||
|
||||
-- 关联数据库至标签
|
||||
INSERT
|
||||
INTO
|
||||
t_tag_tree (code_path,
|
||||
code,
|
||||
type,
|
||||
NAME,
|
||||
create_time,
|
||||
creator_id,
|
||||
creator,
|
||||
update_time,
|
||||
modifier_id,
|
||||
modifier,
|
||||
is_deleted )
|
||||
SELECT
|
||||
DISTINCT t1.newCodePath,
|
||||
t1.tagCode,
|
||||
22,
|
||||
t1.name,
|
||||
DATE_FORMAT( NOW(), '%Y-%m-%d %H:%i:%s' ),
|
||||
1,
|
||||
'admin',
|
||||
DATE_FORMAT( NOW(), '%Y-%m-%d %H:%i:%s' ),
|
||||
1,
|
||||
'admin',
|
||||
0
|
||||
FROM
|
||||
(
|
||||
SELECT
|
||||
ttt.code_path,
|
||||
ttt.code_path as p_code_path,
|
||||
td.name ,
|
||||
td.auth_cert_name,
|
||||
td.code tagCode,
|
||||
td.name tagName,
|
||||
CONCAT(ttt.code_path, '22|', td.code, '/') newCodePath
|
||||
FROM
|
||||
t_tag_tree ttt
|
||||
join t_db td on
|
||||
ttt.code = td.auth_cert_name
|
||||
and ttt.type = 21
|
||||
and td.is_deleted = 0) as t1;
|
||||
|
||||
UPDATE t_tag_tree SET is_deleted = 0, delete_time = NOW() WHERE `type` = 2;
|
||||
UPDATE t_tag_tree SET `type` = 2 WHERE `type` = 100;
|
||||
|
||||
ALTER TABLE t_tag_tree DROP COLUMN pid;
|
||||
Reference in New Issue
Block a user