mirror of
https://gitee.com/dromara/mayfly-go
synced 2025-11-04 00:10:25 +08:00
feat: 支持关联多标签、计划任务立即执行、标签相关操作优化
This commit is contained in:
@@ -1,14 +1,11 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/common/consts"
|
||||
dbapp "mayfly-go/internal/db/application"
|
||||
dbentity "mayfly-go/internal/db/domain/entity"
|
||||
machineapp "mayfly-go/internal/machine/application"
|
||||
machineentity "mayfly-go/internal/machine/domain/entity"
|
||||
mongoapp "mayfly-go/internal/mongo/application"
|
||||
mongoentity "mayfly-go/internal/mongo/domain/entity"
|
||||
redisapp "mayfly-go/internal/redis/application"
|
||||
redisentity "mayfly-go/internal/redis/domain/entity"
|
||||
tagapp "mayfly-go/internal/tag/application"
|
||||
"mayfly-go/pkg/req"
|
||||
"mayfly-go/pkg/utils/collx"
|
||||
@@ -24,19 +21,12 @@ type Index struct {
|
||||
|
||||
func (i *Index) Count(rc *req.Ctx) {
|
||||
accountId := rc.GetLoginAccount().Id
|
||||
tagIds := i.TagApp.ListTagIdByAccountId(accountId)
|
||||
|
||||
var mongoNum int64
|
||||
var redisNum int64
|
||||
var dbNum int64
|
||||
var machienNum int64
|
||||
mongoNum := len(i.TagApp.GetAccountResourceCodes(accountId, consts.TagResourceTypeMongo, ""))
|
||||
machienNum := len(i.TagApp.GetAccountResourceCodes(accountId, consts.TagResourceTypeMachine, ""))
|
||||
dbNum := len(i.TagApp.GetAccountResourceCodes(accountId, consts.TagResourceTypeDb, ""))
|
||||
redisNum := len(i.TagApp.GetAccountResourceCodes(accountId, consts.TagResourceTypeRedis, ""))
|
||||
|
||||
if len(tagIds) > 0 {
|
||||
mongoNum = i.MongoApp.Count(&mongoentity.MongoQuery{TagIds: tagIds})
|
||||
machienNum = i.MachineApp.Count(&machineentity.MachineQuery{TagIds: tagIds})
|
||||
dbNum = i.DbApp.Count(&dbentity.DbQuery{TagIds: tagIds})
|
||||
redisNum = i.RedisApp.Count(&redisentity.RedisQuery{TagIds: tagIds})
|
||||
}
|
||||
rc.ResData = collx.M{
|
||||
"mongoNum": mongoNum,
|
||||
"machineNum": machienNum,
|
||||
|
||||
@@ -10,9 +10,14 @@ const (
|
||||
RedisConnExpireTime = 30 * time.Minute
|
||||
MongoConnExpireTime = 30 * time.Minute
|
||||
|
||||
/**** 开发测试使用 ****/
|
||||
// MachineConnExpireTime = 4 * time.Minute
|
||||
// DbConnExpireTime = 2 * time.Minute
|
||||
// RedisConnExpireTime = 2 * time.Minute
|
||||
// MongoConnExpireTime = 2 * time.Minute
|
||||
/**** 开发测试使用 ****/
|
||||
// MachineConnExpireTime = 4 * time.Minute
|
||||
// DbConnExpireTime = 2 * time.Minute
|
||||
// RedisConnExpireTime = 2 * time.Minute
|
||||
// MongoConnExpireTime = 2 * time.Minute
|
||||
|
||||
TagResourceTypeMachine = 1
|
||||
TagResourceTypeDb = 2
|
||||
TagResourceTypeRedis = 3
|
||||
TagResourceTypeMongo = 4
|
||||
)
|
||||
|
||||
@@ -3,6 +3,7 @@ package api
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"mayfly-go/internal/common/consts"
|
||||
"mayfly-go/internal/db/api/form"
|
||||
"mayfly-go/internal/db/api/vo"
|
||||
"mayfly-go/internal/db/application"
|
||||
@@ -41,29 +42,25 @@ func (d *Db) Dbs(rc *req.Ctx) {
|
||||
queryCond, page := ginx.BindQueryAndPage[*entity.DbQuery](rc.GinCtx, new(entity.DbQuery))
|
||||
|
||||
// 不存在可访问标签id,即没有可操作数据
|
||||
tagIds := d.TagApp.ListTagIdByAccountId(rc.GetLoginAccount().Id)
|
||||
if len(tagIds) == 0 {
|
||||
codes := d.TagApp.GetAccountResourceCodes(rc.GetLoginAccount().Id, consts.TagResourceTypeDb, queryCond.TagPath)
|
||||
if len(codes) == 0 {
|
||||
rc.ResData = model.EmptyPageResult[any]()
|
||||
return
|
||||
}
|
||||
queryCond.Codes = codes
|
||||
|
||||
queryCond.TagIds = tagIds
|
||||
res, err := d.DbApp.GetPageList(queryCond, page, new([]vo.DbListVO))
|
||||
biz.ErrIsNil(err)
|
||||
rc.ResData = res
|
||||
}
|
||||
|
||||
func (d *Db) DbTags(rc *req.Ctx) {
|
||||
rc.ResData = d.TagApp.ListTagByAccountIdAndResource(rc.GetLoginAccount().Id, new(entity.Db))
|
||||
}
|
||||
|
||||
func (d *Db) Save(rc *req.Ctx) {
|
||||
form := &form.DbForm{}
|
||||
db := ginx.BindJsonAndCopyTo[*entity.Db](rc.GinCtx, form, new(entity.Db))
|
||||
|
||||
rc.ReqParam = form
|
||||
|
||||
biz.ErrIsNil(d.DbApp.Save(rc.MetaCtx, db))
|
||||
biz.ErrIsNil(d.DbApp.Save(rc.MetaCtx, db, form.TagId...))
|
||||
}
|
||||
|
||||
func (d *Db) DeleteDb(rc *req.Ctx) {
|
||||
@@ -90,7 +87,7 @@ func (d *Db) ExecSql(rc *req.Ctx) {
|
||||
dbId := getDbId(g)
|
||||
dbConn, err := d.DbApp.GetDbConn(dbId, form.Db)
|
||||
biz.ErrIsNil(err)
|
||||
biz.ErrIsNilAppendErr(d.TagApp.CanAccess(rc.GetLoginAccount().Id, dbConn.Info.TagPath), "%s")
|
||||
biz.ErrIsNilAppendErr(d.TagApp.CanAccess(rc.GetLoginAccount().Id, dbConn.Info.TagPath...), "%s")
|
||||
|
||||
rc.ReqParam = fmt.Sprintf("%s\n-> %s", dbConn.Info.GetLogDesc(), form.Sql)
|
||||
biz.NotEmpty(form.Sql, "sql不能为空")
|
||||
@@ -161,7 +158,7 @@ func (d *Db) ExecSqlFile(rc *req.Ctx) {
|
||||
|
||||
dbConn, err := d.DbApp.GetDbConn(dbId, dbName)
|
||||
biz.ErrIsNil(err)
|
||||
biz.ErrIsNilAppendErr(d.TagApp.CanAccess(rc.GetLoginAccount().Id, dbConn.Info.TagPath), "%s")
|
||||
biz.ErrIsNilAppendErr(d.TagApp.CanAccess(rc.GetLoginAccount().Id, dbConn.Info.TagPath...), "%s")
|
||||
rc.ReqParam = fmt.Sprintf("filename: %s -> %s", filename, dbConn.Info.GetLogDesc())
|
||||
|
||||
defer func() {
|
||||
@@ -230,7 +227,7 @@ func (d *Db) ExecSqlFile(rc *req.Ctx) {
|
||||
}
|
||||
dbConn, err = d.DbApp.GetDbConn(dbId, stmtUse.DBName.String())
|
||||
biz.ErrIsNil(err)
|
||||
biz.ErrIsNilAppendErr(d.TagApp.CanAccess(laId, dbConn.Info.TagPath), "%s")
|
||||
biz.ErrIsNilAppendErr(d.TagApp.CanAccess(laId, dbConn.Info.TagPath...), "%s")
|
||||
execReq.DbConn = dbConn
|
||||
}
|
||||
// 需要记录执行记录
|
||||
@@ -270,7 +267,7 @@ func (d *Db) DumpSql(rc *req.Ctx) {
|
||||
la := rc.GetLoginAccount()
|
||||
db, err := d.DbApp.GetById(new(entity.Db), dbId)
|
||||
biz.ErrIsNil(err, "该数据库不存在")
|
||||
biz.ErrIsNilAppendErr(d.TagApp.CanAccess(la.Id, db.TagPath), "%s")
|
||||
biz.ErrIsNilAppendErr(d.TagApp.CanAccess(la.Id, d.TagApp.ListTagPathByResource(consts.TagResourceTypeDb, db.Code)...), "%s")
|
||||
|
||||
now := time.Now()
|
||||
filename := fmt.Sprintf("%s.%s.sql%s", db.Name, now.Format("20060102150405"), extName)
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
package form
|
||||
|
||||
type DbForm struct {
|
||||
Id uint64 `json:"id"`
|
||||
Name string `binding:"required" json:"name"`
|
||||
Database string `json:"database"`
|
||||
Remark string `json:"remark"`
|
||||
TagId uint64 `binding:"required" json:"tagId"`
|
||||
TagPath string `binding:"required" json:"tagPath"`
|
||||
InstanceId uint64 `binding:"required" json:"instanceId"`
|
||||
Id uint64 `json:"id"`
|
||||
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"`
|
||||
}
|
||||
|
||||
type DbSqlSaveForm struct {
|
||||
|
||||
@@ -4,11 +4,10 @@ import "time"
|
||||
|
||||
type DbListVO struct {
|
||||
Id *int64 `json:"id"`
|
||||
Code string `json:"code"`
|
||||
Name *string `json:"name"`
|
||||
Database *string `json:"database"`
|
||||
Remark *string `json:"remark"`
|
||||
TagId *int64 `json:"tagId"`
|
||||
TagPath *string `json:"tagPath"`
|
||||
|
||||
InstanceId *int64 `json:"instanceId"`
|
||||
InstanceName *string `json:"instanceName"`
|
||||
|
||||
@@ -2,11 +2,12 @@ package application
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/db/infrastructure/persistence"
|
||||
tagapp "mayfly-go/internal/tag/application"
|
||||
)
|
||||
|
||||
var (
|
||||
instanceApp Instance = newInstanceApp(persistence.GetInstanceRepo())
|
||||
dbApp Db = newDbApp(persistence.GetDbRepo(), persistence.GetDbSqlRepo(), instanceApp)
|
||||
dbApp Db = newDbApp(persistence.GetDbRepo(), persistence.GetDbSqlRepo(), instanceApp, tagapp.GetTagTreeApp())
|
||||
dbSqlExecApp DbSqlExec = newDbSqlExecApp(persistence.GetDbSqlExecRepo())
|
||||
dbSqlApp DbSql = newDbSqlApp(persistence.GetDbSqlRepo())
|
||||
)
|
||||
|
||||
@@ -2,13 +2,16 @@ package application
|
||||
|
||||
import (
|
||||
"context"
|
||||
"mayfly-go/internal/common/consts"
|
||||
"mayfly-go/internal/db/dbm"
|
||||
"mayfly-go/internal/db/domain/entity"
|
||||
"mayfly-go/internal/db/domain/repository"
|
||||
tagapp "mayfly-go/internal/tag/application"
|
||||
"mayfly-go/pkg/base"
|
||||
"mayfly-go/pkg/errorx"
|
||||
"mayfly-go/pkg/model"
|
||||
"mayfly-go/pkg/utils/collx"
|
||||
"mayfly-go/pkg/utils/stringx"
|
||||
"mayfly-go/pkg/utils/structx"
|
||||
"strings"
|
||||
)
|
||||
@@ -21,7 +24,7 @@ type Db interface {
|
||||
|
||||
Count(condition *entity.DbQuery) int64
|
||||
|
||||
Save(ctx context.Context, entity *entity.Db) error
|
||||
Save(ctx context.Context, entity *entity.Db, tagIds ...uint64) error
|
||||
|
||||
// 删除数据库信息
|
||||
Delete(ctx context.Context, id uint64) error
|
||||
@@ -32,10 +35,11 @@ type Db interface {
|
||||
GetDbConn(dbId uint64, dbName string) (*dbm.DbConn, error)
|
||||
}
|
||||
|
||||
func newDbApp(dbRepo repository.Db, dbSqlRepo repository.DbSql, dbInstanceApp Instance) Db {
|
||||
func newDbApp(dbRepo repository.Db, dbSqlRepo repository.DbSql, dbInstanceApp Instance, tagApp tagapp.TagTree) Db {
|
||||
app := &dbAppImpl{
|
||||
dbSqlRepo: dbSqlRepo,
|
||||
dbInstanceApp: dbInstanceApp,
|
||||
tagApp: tagApp,
|
||||
}
|
||||
app.Repo = dbRepo
|
||||
return app
|
||||
@@ -46,6 +50,7 @@ type dbAppImpl struct {
|
||||
|
||||
dbSqlRepo repository.DbSql
|
||||
dbInstanceApp Instance
|
||||
tagApp tagapp.TagTree
|
||||
}
|
||||
|
||||
// 分页获取数据库信息列表
|
||||
@@ -57,7 +62,7 @@ func (d *dbAppImpl) Count(condition *entity.DbQuery) int64 {
|
||||
return d.GetRepo().Count(condition)
|
||||
}
|
||||
|
||||
func (d *dbAppImpl) Save(ctx context.Context, dbEntity *entity.Db) error {
|
||||
func (d *dbAppImpl) Save(ctx context.Context, dbEntity *entity.Db, tagIds ...uint64) error {
|
||||
// 查找是否存在
|
||||
oldDb := &entity.Db{Name: dbEntity.Name, InstanceId: dbEntity.InstanceId}
|
||||
err := d.GetBy(oldDb)
|
||||
@@ -66,7 +71,15 @@ func (d *dbAppImpl) Save(ctx context.Context, dbEntity *entity.Db) error {
|
||||
if err == nil {
|
||||
return errorx.NewBiz("该实例下数据库名已存在")
|
||||
}
|
||||
return d.Insert(ctx, dbEntity)
|
||||
|
||||
resouceCode := stringx.Rand(16)
|
||||
dbEntity.Code = resouceCode
|
||||
|
||||
return d.Tx(ctx, func(ctx context.Context) error {
|
||||
return d.Insert(ctx, dbEntity)
|
||||
}, func(ctx context.Context) error {
|
||||
return d.tagApp.RelateResource(ctx, resouceCode, consts.TagResourceTypeDb, tagIds)
|
||||
})
|
||||
}
|
||||
|
||||
// 如果存在该库,则校验修改的库是否为该库
|
||||
@@ -94,7 +107,11 @@ func (d *dbAppImpl) Save(ctx context.Context, dbEntity *entity.Db) error {
|
||||
d.dbSqlRepo.DeleteByCond(ctx, &entity.DbSql{DbId: dbId, Db: v})
|
||||
}
|
||||
|
||||
return d.UpdateById(ctx, dbEntity)
|
||||
return d.Tx(ctx, func(ctx context.Context) error {
|
||||
return d.UpdateById(ctx, dbEntity)
|
||||
}, func(ctx context.Context) error {
|
||||
return d.tagApp.RelateResource(ctx, oldDb.Code, consts.TagResourceTypeDb, tagIds)
|
||||
})
|
||||
}
|
||||
|
||||
func (d *dbAppImpl) Delete(ctx context.Context, id uint64) error {
|
||||
@@ -138,11 +155,11 @@ func (d *dbAppImpl) GetDbConn(dbId uint64, dbName string) (*dbm.DbConn, error) {
|
||||
|
||||
// 密码解密
|
||||
instance.PwdDecrypt()
|
||||
return toDbInfo(instance, dbId, dbName, db.TagPath), nil
|
||||
return toDbInfo(instance, dbId, dbName, d.tagApp.ListTagPathByResource(consts.TagResourceTypeDb, db.Code)...), nil
|
||||
})
|
||||
}
|
||||
|
||||
func toDbInfo(instance *entity.DbInstance, dbId uint64, database string, tagPath string) *dbm.DbInfo {
|
||||
func toDbInfo(instance *entity.DbInstance, dbId uint64, database string, tagPath ...string) *dbm.DbInfo {
|
||||
di := new(dbm.DbInfo)
|
||||
di.Id = dbId
|
||||
di.Database = database
|
||||
|
||||
@@ -48,6 +48,7 @@ func (app *instanceAppImpl) Count(condition *entity.InstanceQuery) int64 {
|
||||
}
|
||||
|
||||
func (app *instanceAppImpl) TestConn(instanceEntity *entity.DbInstance) error {
|
||||
instanceEntity.Network = instanceEntity.GetNetwork()
|
||||
dbConn, err := toDbInfo(instanceEntity, 0, "", "").Conn()
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -20,7 +20,7 @@ type DbInfo struct {
|
||||
Params string
|
||||
Database string
|
||||
|
||||
TagPath string
|
||||
TagPath []string
|
||||
SshTunnelMachineId int
|
||||
}
|
||||
|
||||
|
||||
@@ -7,10 +7,9 @@ import (
|
||||
type Db struct {
|
||||
model.Model
|
||||
|
||||
Code string `orm:"column(code)" json:"code"`
|
||||
Name string `orm:"column(name)" json:"name"`
|
||||
Database string `orm:"column(database)" json:"database"`
|
||||
Remark string `json:"remark"`
|
||||
TagId uint64
|
||||
TagPath string
|
||||
InstanceId uint64
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ type DbQuery struct {
|
||||
Database string `orm:"column(database)" json:"database"`
|
||||
Remark string `json:"remark"`
|
||||
|
||||
Codes []string
|
||||
TagIds []uint64 `orm:"column(tag_id)"`
|
||||
TagPath string `form:"tagPath"`
|
||||
|
||||
|
||||
@@ -23,11 +23,9 @@ func (d *dbRepoImpl) GetDbList(condition *entity.DbQuery, pageParam *model.PageP
|
||||
Joins("JOIN t_db_instance inst ON db.instance_id = inst.id").
|
||||
Eq("db.instance_id", condition.InstanceId).
|
||||
Like("db.database", condition.Database).
|
||||
In("db.tag_id", condition.TagIds).
|
||||
RLike("db.tag_path", condition.TagPath).
|
||||
In("db.code", condition.Codes).
|
||||
Eq0("db."+model.DeletedColumn, model.ModelUndeleted).
|
||||
Eq0("inst."+model.DeletedColumn, model.ModelUndeleted).
|
||||
OrderByAsc("db.tag_path")
|
||||
Eq0("inst."+model.DeletedColumn, model.ModelUndeleted)
|
||||
|
||||
return gormx.PageQuery(qd, pageParam, toEntity)
|
||||
}
|
||||
|
||||
@@ -25,8 +25,6 @@ func InitDbRouter(router *gin.RouterGroup) {
|
||||
// 获取数据库列表
|
||||
req.NewGet("", d.Dbs),
|
||||
|
||||
req.NewGet("/tags", d.DbTags),
|
||||
|
||||
req.NewPost("", d.Save).Log(req.NewLogSave("db-保存数据库信息")),
|
||||
|
||||
req.NewDelete(":dbId", d.DeleteDb).Log(req.NewLogSave("db-删除数据库信息")),
|
||||
|
||||
@@ -7,11 +7,10 @@ type MachineForm struct {
|
||||
Port int `json:"port" binding:"required"` // 端口号
|
||||
|
||||
// 资产授权凭证信息列表
|
||||
AuthCertId int `json:"authCertId"`
|
||||
TagId uint64 `json:"tagId" binding:"required"`
|
||||
TagPath string `json:"tagPath" binding:"required"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
AuthCertId int `json:"authCertId"`
|
||||
TagId []uint64 `json:"tagId" binding:"required"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
|
||||
Remark string `json:"remark"`
|
||||
SshTunnelMachineId int `json:"sshTunnelMachineId"` // ssh隧道机器id
|
||||
|
||||
@@ -3,6 +3,7 @@ package api
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"mayfly-go/internal/common/consts"
|
||||
"mayfly-go/internal/machine/api/form"
|
||||
"mayfly-go/internal/machine/api/vo"
|
||||
"mayfly-go/internal/machine/application"
|
||||
@@ -17,34 +18,32 @@ import (
|
||||
"mayfly-go/pkg/req"
|
||||
"mayfly-go/pkg/utils/anyx"
|
||||
"mayfly-go/pkg/utils/collx"
|
||||
"mayfly-go/pkg/utils/stringx"
|
||||
"mayfly-go/pkg/ws"
|
||||
"os"
|
||||
"path"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gorilla/websocket"
|
||||
)
|
||||
|
||||
type Machine struct {
|
||||
MachineApp application.Machine
|
||||
TagApp tagapp.TagTree
|
||||
MachineApp application.Machine
|
||||
MachineTermOpApp application.MachineTermOp
|
||||
TagApp tagapp.TagTree
|
||||
}
|
||||
|
||||
func (m *Machine) Machines(rc *req.Ctx) {
|
||||
condition, pageParam := ginx.BindQueryAndPage(rc.GinCtx, new(entity.MachineQuery))
|
||||
|
||||
// 不存在可访问标签id,即没有可操作数据
|
||||
tagIds := m.TagApp.ListTagIdByAccountId(rc.GetLoginAccount().Id)
|
||||
if len(tagIds) == 0 {
|
||||
codes := m.TagApp.GetAccountResourceCodes(rc.GetLoginAccount().Id, consts.TagResourceTypeMachine, condition.TagPath)
|
||||
if len(codes) == 0 {
|
||||
rc.ResData = model.EmptyPageResult[any]()
|
||||
return
|
||||
}
|
||||
condition.TagIds = tagIds
|
||||
condition.Codes = codes
|
||||
|
||||
res, err := m.MachineApp.GetMachineList(condition, pageParam, new([]*vo.MachineVO))
|
||||
biz.ErrIsNil(err)
|
||||
@@ -67,10 +66,6 @@ func (m *Machine) Machines(rc *req.Ctx) {
|
||||
rc.ResData = res
|
||||
}
|
||||
|
||||
func (m *Machine) MachineTags(rc *req.Ctx) {
|
||||
rc.ResData = m.TagApp.ListTagByAccountIdAndResource(rc.GetLoginAccount().Id, new(entity.Machine))
|
||||
}
|
||||
|
||||
func (m *Machine) MachineStats(rc *req.Ctx) {
|
||||
cli, err := m.MachineApp.GetCli(GetMachineId(rc.GinCtx))
|
||||
biz.ErrIsNilAppendErr(err, "获取客户端连接失败: %s")
|
||||
@@ -85,7 +80,7 @@ func (m *Machine) SaveMachine(rc *req.Ctx) {
|
||||
machineForm.Password = "******"
|
||||
rc.ReqParam = machineForm
|
||||
|
||||
biz.ErrIsNil(m.MachineApp.Save(rc.MetaCtx, me))
|
||||
biz.ErrIsNil(m.MachineApp.Save(rc.MetaCtx, me, machineForm.TagId...))
|
||||
}
|
||||
|
||||
func (m *Machine) TestConn(rc *req.Ctx) {
|
||||
@@ -140,7 +135,7 @@ func (m *Machine) GetProcess(rc *req.Ctx) {
|
||||
|
||||
cli, err := m.MachineApp.GetCli(GetMachineId(rc.GinCtx))
|
||||
biz.ErrIsNilAppendErr(err, "获取客户端连接失败: %s")
|
||||
biz.ErrIsNilAppendErr(m.TagApp.CanAccess(rc.GetLoginAccount().Id, cli.Info.TagPath), "%s")
|
||||
biz.ErrIsNilAppendErr(m.TagApp.CanAccess(rc.GetLoginAccount().Id, cli.Info.TagPath...), "%s")
|
||||
|
||||
res, err := cli.Run(cmd)
|
||||
biz.ErrIsNilAppendErr(err, "获取进程信息失败: %s")
|
||||
@@ -154,7 +149,7 @@ func (m *Machine) KillProcess(rc *req.Ctx) {
|
||||
|
||||
cli, err := m.MachineApp.GetCli(GetMachineId(rc.GinCtx))
|
||||
biz.ErrIsNilAppendErr(err, "获取客户端连接失败: %s")
|
||||
biz.ErrIsNilAppendErr(m.TagApp.CanAccess(rc.GetLoginAccount().Id, cli.Info.TagPath), "%s")
|
||||
biz.ErrIsNilAppendErr(m.TagApp.CanAccess(rc.GetLoginAccount().Id, cli.Info.TagPath...), "%s")
|
||||
|
||||
res, err := cli.Run("sudo kill -9 " + pid)
|
||||
biz.ErrIsNil(err, "终止进程失败: %s", res)
|
||||
@@ -180,59 +175,35 @@ func (m *Machine) WsSSH(g *gin.Context) {
|
||||
|
||||
cli, err := m.MachineApp.GetCli(GetMachineId(g))
|
||||
biz.ErrIsNilAppendErr(err, "获取客户端连接失败: %s")
|
||||
biz.ErrIsNilAppendErr(m.TagApp.CanAccess(rc.GetLoginAccount().Id, cli.Info.TagPath), "%s")
|
||||
biz.ErrIsNilAppendErr(m.TagApp.CanAccess(rc.GetLoginAccount().Id, cli.Info.TagPath...), "%s")
|
||||
|
||||
cols := ginx.QueryInt(g, "cols", 80)
|
||||
rows := ginx.QueryInt(g, "rows", 40)
|
||||
|
||||
var recorder *mcm.Recorder
|
||||
if cli.Info.EnableRecorder == 1 {
|
||||
now := time.Now()
|
||||
// 回放文件路径为: 基础配置路径/机器id/操作日期/操作者账号/操作时间.cast
|
||||
recPath := fmt.Sprintf("%s/%d/%s/%s", config.GetMachine().TerminalRecPath, cli.Info.Id, now.Format("20060102"), rc.GetLoginAccount().Username)
|
||||
os.MkdirAll(recPath, 0766)
|
||||
fileName := path.Join(recPath, fmt.Sprintf("%s.cast", now.Format("20060102_150405")))
|
||||
f, err := os.OpenFile(fileName, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0766)
|
||||
biz.ErrIsNilAppendErr(err, "创建终端回放记录文件失败: %s")
|
||||
defer f.Close()
|
||||
recorder = mcm.NewRecorder(f)
|
||||
}
|
||||
|
||||
mts, err := mcm.NewTerminalSession(stringx.Rand(16), wsConn, cli, rows, cols, recorder)
|
||||
biz.ErrIsNilAppendErr(err, "\033[1;31m连接失败: %s\033[0m")
|
||||
|
||||
// 记录系统操作日志
|
||||
rc.WithLog(req.NewLogSave("机器-终端操作"))
|
||||
rc.ReqParam = cli.Info
|
||||
req.LogHandler(rc)
|
||||
|
||||
mts.Start()
|
||||
defer mts.Stop()
|
||||
err = m.MachineTermOpApp.TermConn(rc.MetaCtx, cli, wsConn, rows, cols)
|
||||
biz.ErrIsNilAppendErr(err, "\033[1;31m连接失败: %s\033[0m")
|
||||
}
|
||||
|
||||
// 获取机器终端回放记录的相应文件夹名或文件内容
|
||||
func (m *Machine) MachineRecDirNames(rc *req.Ctx) {
|
||||
readPath := rc.GinCtx.Query("path")
|
||||
biz.NotEmpty(readPath, "path不能为空")
|
||||
path_ := path.Join(config.GetMachine().TerminalRecPath, readPath)
|
||||
func (m *Machine) MachineTermOpRecords(rc *req.Ctx) {
|
||||
mid := GetMachineId(rc.GinCtx)
|
||||
res, err := m.MachineTermOpApp.GetPageList(&entity.MachineTermOp{MachineId: mid}, ginx.GetPageParam(rc.GinCtx), new([]entity.MachineTermOp))
|
||||
biz.ErrIsNil(err)
|
||||
rc.ResData = res
|
||||
}
|
||||
|
||||
// 如果是读取文件内容,则读取对应回放记录文件内容,否则读取文件夹名列表。小小偷懒一会不想再加个接口
|
||||
isFile := rc.GinCtx.Query("isFile")
|
||||
if isFile == "1" {
|
||||
bytes, err := os.ReadFile(path_)
|
||||
biz.ErrIsNilAppendErr(err, "还未有相应终端操作记录: %s")
|
||||
rc.ResData = base64.StdEncoding.EncodeToString(bytes)
|
||||
return
|
||||
}
|
||||
func (m *Machine) MachineTermOpRecord(rc *req.Ctx) {
|
||||
recId, _ := strconv.Atoi(rc.GinCtx.Param("recId"))
|
||||
termOp, err := m.MachineTermOpApp.GetById(new(entity.MachineTermOp), uint64(recId))
|
||||
biz.ErrIsNil(err)
|
||||
|
||||
files, err := os.ReadDir(path_)
|
||||
biz.ErrIsNilAppendErr(err, "还未有相应终端操作记录: %s")
|
||||
var names []string
|
||||
for _, f := range files {
|
||||
names = append(names, f.Name())
|
||||
}
|
||||
sort.Sort(sort.Reverse(sort.StringSlice(names)))
|
||||
rc.ResData = names
|
||||
bytes, err := os.ReadFile(path.Join(config.GetMachine().TerminalRecPath, termOp.RecordFilePath))
|
||||
biz.ErrIsNilAppendErr(err, "读取终端操作记录失败: %s")
|
||||
rc.ResData = base64.StdEncoding.EncodeToString(bytes)
|
||||
}
|
||||
|
||||
func GetMachineId(g *gin.Context) uint64 {
|
||||
|
||||
@@ -63,6 +63,12 @@ func (m *MachineCronJob) GetRelateCronJobIds(rc *req.Ctx) {
|
||||
rc.ResData = m.MachineCronJobApp.GetRelateMachineIds(uint64(ginx.QueryInt(rc.GinCtx, "machineId", -1)))
|
||||
}
|
||||
|
||||
func (m *MachineCronJob) RunCronJob(rc *req.Ctx) {
|
||||
cronJobKey := ginx.PathParam(rc.GinCtx, "key")
|
||||
biz.NotEmpty(cronJobKey, "cronJob key不能为空")
|
||||
m.MachineCronJobApp.RunCronJob(cronJobKey)
|
||||
}
|
||||
|
||||
func (m *MachineCronJob) CronJobExecs(rc *req.Ctx) {
|
||||
cond, pageParam := ginx.BindQueryAndPage[*entity.MachineCronJobExec](rc.GinCtx, new(entity.MachineCronJobExec))
|
||||
res, err := m.MachineCronJobApp.GetExecPageList(cond, pageParam, new([]entity.MachineCronJobExec))
|
||||
|
||||
@@ -69,7 +69,7 @@ func (m *MachineScript) RunMachineScript(rc *req.Ctx) {
|
||||
}
|
||||
cli, err := m.MachineApp.GetCli(machineId)
|
||||
biz.ErrIsNilAppendErr(err, "获取客户端连接失败: %s")
|
||||
biz.ErrIsNilAppendErr(m.TagApp.CanAccess(rc.GetLoginAccount().Id, cli.Info.TagPath), "%s")
|
||||
biz.ErrIsNilAppendErr(m.TagApp.CanAccess(rc.GetLoginAccount().Id, cli.Info.TagPath...), "%s")
|
||||
|
||||
res, err := cli.Run(script)
|
||||
// 记录请求参数
|
||||
|
||||
@@ -13,6 +13,7 @@ type AuthCertBaseVO struct {
|
||||
|
||||
type MachineVO struct {
|
||||
Id uint64 `json:"id"`
|
||||
Code string `json:"code"`
|
||||
Name string `json:"name"`
|
||||
Ip string `json:"ip"`
|
||||
Port int `json:"port"`
|
||||
@@ -28,8 +29,8 @@ type MachineVO struct {
|
||||
ModifierId *int64 `json:"modifierId"`
|
||||
Remark *string `json:"remark"`
|
||||
EnableRecorder int8 `json:"enableRecorder"`
|
||||
TagId uint64 `json:"tagId"`
|
||||
TagPath string `json:"tagPath"`
|
||||
// TagId uint64 `json:"tagId"`
|
||||
// TagPath string `json:"tagPath"`
|
||||
|
||||
HasCli bool `json:"hasCli" gorm:"-"`
|
||||
Stat map[string]any `json:"stat" gorm:"-"`
|
||||
|
||||
@@ -2,12 +2,14 @@ package application
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/machine/infrastructure/persistence"
|
||||
tagapp "mayfly-go/internal/tag/application"
|
||||
)
|
||||
|
||||
var (
|
||||
machineApp Machine = newMachineApp(
|
||||
persistence.GetMachineRepo(),
|
||||
GetAuthCertApp(),
|
||||
tagapp.GetTagTreeApp(),
|
||||
)
|
||||
|
||||
machineFileApp MachineFile = newMachineFileApp(
|
||||
@@ -28,6 +30,8 @@ var (
|
||||
persistence.GetMachineCronJobExecRepo(),
|
||||
GetMachineApp(),
|
||||
)
|
||||
|
||||
machineTermOpApp MachineTermOp = newMachineTermOpApp(persistence.GetMachineTermOpRepo())
|
||||
)
|
||||
|
||||
func GetMachineApp() Machine {
|
||||
@@ -49,3 +53,7 @@ func GetAuthCertApp() AuthCert {
|
||||
func GetMachineCronJobApp() MachineCronJob {
|
||||
return machineCropJobApp
|
||||
}
|
||||
|
||||
func GetMachineTermOpApp() MachineTermOp {
|
||||
return machineTermOpApp
|
||||
}
|
||||
|
||||
@@ -3,17 +3,20 @@ package application
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"mayfly-go/internal/common/consts"
|
||||
"mayfly-go/internal/machine/api/vo"
|
||||
"mayfly-go/internal/machine/domain/entity"
|
||||
"mayfly-go/internal/machine/domain/repository"
|
||||
"mayfly-go/internal/machine/infrastructure/cache"
|
||||
"mayfly-go/internal/machine/mcm"
|
||||
tagapp "mayfly-go/internal/tag/application"
|
||||
"mayfly-go/pkg/base"
|
||||
"mayfly-go/pkg/errorx"
|
||||
"mayfly-go/pkg/gormx"
|
||||
"mayfly-go/pkg/logx"
|
||||
"mayfly-go/pkg/model"
|
||||
"mayfly-go/pkg/scheduler"
|
||||
"mayfly-go/pkg/utils/stringx"
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
@@ -22,7 +25,7 @@ import (
|
||||
type Machine interface {
|
||||
base.App[*entity.Machine]
|
||||
|
||||
Save(ctx context.Context, m *entity.Machine) error
|
||||
Save(ctx context.Context, m *entity.Machine, tagIds ...uint64) error
|
||||
|
||||
// 测试机器连接
|
||||
TestConn(me *entity.Machine) error
|
||||
@@ -30,8 +33,6 @@ type Machine interface {
|
||||
// 调整机器状态
|
||||
ChangeStatus(ctx context.Context, id uint64, status int8) error
|
||||
|
||||
Count(condition *entity.MachineQuery) int64
|
||||
|
||||
Delete(ctx context.Context, id uint64) error
|
||||
|
||||
// 分页获取机器信息列表
|
||||
@@ -50,9 +51,10 @@ type Machine interface {
|
||||
GetMachineStats(machineId uint64) (*mcm.Stats, error)
|
||||
}
|
||||
|
||||
func newMachineApp(machineRepo repository.Machine, authCertApp AuthCert) Machine {
|
||||
func newMachineApp(machineRepo repository.Machine, authCertApp AuthCert, tagApp tagapp.TagTree) Machine {
|
||||
app := &machineAppImpl{
|
||||
authCertApp: authCertApp,
|
||||
tagApp: tagApp,
|
||||
}
|
||||
app.Repo = machineRepo
|
||||
return app
|
||||
@@ -62,6 +64,8 @@ type machineAppImpl struct {
|
||||
base.AppImpl[*entity.Machine, repository.Machine]
|
||||
|
||||
authCertApp AuthCert
|
||||
|
||||
tagApp tagapp.TagTree
|
||||
}
|
||||
|
||||
// 分页获取机器信息列表
|
||||
@@ -69,11 +73,7 @@ func (m *machineAppImpl) GetMachineList(condition *entity.MachineQuery, pagePara
|
||||
return m.GetRepo().GetMachineList(condition, pageParam, toEntity, orderBy...)
|
||||
}
|
||||
|
||||
func (m *machineAppImpl) Count(condition *entity.MachineQuery) int64 {
|
||||
return m.GetRepo().Count(condition)
|
||||
}
|
||||
|
||||
func (m *machineAppImpl) Save(ctx context.Context, me *entity.Machine) error {
|
||||
func (m *machineAppImpl) Save(ctx context.Context, me *entity.Machine, tagIds ...uint64) error {
|
||||
oldMachine := &entity.Machine{Ip: me.Ip, Port: me.Port, Username: me.Username}
|
||||
if me.SshTunnelMachineId > 0 {
|
||||
oldMachine.SshTunnelMachineId = me.SshTunnelMachineId
|
||||
@@ -85,9 +85,16 @@ func (m *machineAppImpl) Save(ctx context.Context, me *entity.Machine) error {
|
||||
if err == nil {
|
||||
return errorx.NewBiz("该机器信息已存在")
|
||||
}
|
||||
resouceCode := stringx.Rand(16)
|
||||
me.Code = resouceCode
|
||||
// 新增机器,默认启用状态
|
||||
me.Status = entity.MachineStatusEnable
|
||||
return m.Insert(ctx, me)
|
||||
|
||||
return m.Tx(ctx, func(ctx context.Context) error {
|
||||
return m.Insert(ctx, me)
|
||||
}, func(ctx context.Context) error {
|
||||
return m.tagApp.RelateResource(ctx, resouceCode, consts.TagResourceTypeMachine, tagIds)
|
||||
})
|
||||
}
|
||||
|
||||
// 如果存在该库,则校验修改的库是否为该库
|
||||
@@ -97,7 +104,11 @@ func (m *machineAppImpl) Save(ctx context.Context, me *entity.Machine) error {
|
||||
|
||||
// 关闭连接
|
||||
mcm.DeleteCli(me.Id)
|
||||
return m.UpdateById(ctx, me)
|
||||
return m.Tx(ctx, func(ctx context.Context) error {
|
||||
return m.UpdateById(ctx, me)
|
||||
}, func(ctx context.Context) error {
|
||||
return m.tagApp.RelateResource(ctx, oldMachine.Code, consts.TagResourceTypeMachine, tagIds)
|
||||
})
|
||||
}
|
||||
|
||||
func (m *machineAppImpl) TestConn(me *entity.Machine) error {
|
||||
@@ -214,7 +225,7 @@ func (m *machineAppImpl) toMachineInfo(me *entity.Machine) (*mcm.MachineInfo, er
|
||||
mi.Ip = me.Ip
|
||||
mi.Port = me.Port
|
||||
mi.Username = me.Username
|
||||
mi.TagPath = me.TagPath
|
||||
mi.TagPath = m.tagApp.ListTagPathByResource(consts.TagResourceTypeMachine, me.Code)
|
||||
mi.EnableRecorder = me.EnableRecorder
|
||||
|
||||
if me.UseAuthCert() {
|
||||
|
||||
@@ -44,6 +44,10 @@ type MachineCronJob interface {
|
||||
|
||||
// 初始化计划任务
|
||||
InitCronJob()
|
||||
|
||||
// 执行cron job
|
||||
// @param key cron job key
|
||||
RunCronJob(key string)
|
||||
}
|
||||
|
||||
type machineCropJobAppImpl struct {
|
||||
@@ -183,30 +187,7 @@ func (m *machineCropJobAppImpl) InitCronJob() {
|
||||
}
|
||||
}
|
||||
|
||||
func (m *machineCropJobAppImpl) addCronJob(mcj *entity.MachineCronJob) {
|
||||
var key string
|
||||
isDisable := mcj.Status == entity.MachineCronJobStatusDisable
|
||||
if mcj.Id == 0 {
|
||||
key = stringx.Rand(16)
|
||||
mcj.Key = key
|
||||
if isDisable {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
key = mcj.Key
|
||||
}
|
||||
|
||||
if isDisable {
|
||||
scheduler.RemoveByKey(key)
|
||||
return
|
||||
}
|
||||
|
||||
scheduler.AddFunByKey(key, mcj.Cron, func() {
|
||||
go m.runCronJob(key)
|
||||
})
|
||||
}
|
||||
|
||||
func (m *machineCropJobAppImpl) runCronJob(key string) {
|
||||
func (m *machineCropJobAppImpl) RunCronJob(key string) {
|
||||
// 简单使用redis分布式锁防止多实例同一时刻重复执行
|
||||
if lock := rediscli.NewLock(key, 30*time.Second); lock != nil {
|
||||
if !lock.Lock() {
|
||||
@@ -229,6 +210,28 @@ func (m *machineCropJobAppImpl) runCronJob(key string) {
|
||||
}
|
||||
}
|
||||
|
||||
func (m *machineCropJobAppImpl) addCronJob(mcj *entity.MachineCronJob) {
|
||||
var key string
|
||||
isDisable := mcj.Status == entity.MachineCronJobStatusDisable
|
||||
if mcj.Id == 0 {
|
||||
key = stringx.Rand(16)
|
||||
mcj.Key = key
|
||||
if isDisable {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
key = mcj.Key
|
||||
}
|
||||
|
||||
if isDisable {
|
||||
scheduler.RemoveByKey(key)
|
||||
return
|
||||
}
|
||||
|
||||
scheduler.AddFunByKey(key, mcj.Cron, func() {
|
||||
go m.RunCronJob(key)
|
||||
})
|
||||
}
|
||||
func (m *machineCropJobAppImpl) runCronJob0(mid uint64, cronJob *entity.MachineCronJob) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
|
||||
93
server/internal/machine/application/machine_term_op.go
Normal file
93
server/internal/machine/application/machine_term_op.go
Normal file
@@ -0,0 +1,93 @@
|
||||
package application
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"mayfly-go/internal/machine/config"
|
||||
"mayfly-go/internal/machine/domain/entity"
|
||||
"mayfly-go/internal/machine/domain/repository"
|
||||
"mayfly-go/internal/machine/mcm"
|
||||
"mayfly-go/pkg/base"
|
||||
"mayfly-go/pkg/contextx"
|
||||
"mayfly-go/pkg/errorx"
|
||||
"mayfly-go/pkg/model"
|
||||
"mayfly-go/pkg/utils/stringx"
|
||||
"os"
|
||||
"path"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
)
|
||||
|
||||
type MachineTermOp interface {
|
||||
base.App[*entity.MachineTermOp]
|
||||
|
||||
// 终端连接操作
|
||||
TermConn(ctx context.Context, cli *mcm.Cli, wsConn *websocket.Conn, rows, cols int) error
|
||||
|
||||
GetPageList(condition *entity.MachineTermOp, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error)
|
||||
}
|
||||
|
||||
func newMachineTermOpApp(machineTermOpRepo repository.MachineTermOp) MachineTermOp {
|
||||
return &machineTermOpAppImpl{
|
||||
base.AppImpl[*entity.MachineTermOp, repository.MachineTermOp]{Repo: machineTermOpRepo},
|
||||
}
|
||||
}
|
||||
|
||||
type machineTermOpAppImpl struct {
|
||||
base.AppImpl[*entity.MachineTermOp, repository.MachineTermOp]
|
||||
}
|
||||
|
||||
func (a *machineTermOpAppImpl) TermConn(ctx context.Context, cli *mcm.Cli, wsConn *websocket.Conn, rows, cols int) error {
|
||||
var recorder *mcm.Recorder
|
||||
var termOpRecord *entity.MachineTermOp
|
||||
|
||||
// 开启终端操作记录
|
||||
if cli.Info.EnableRecorder == 1 {
|
||||
now := time.Now()
|
||||
la := contextx.GetLoginAccount(ctx)
|
||||
|
||||
termOpRecord = new(entity.MachineTermOp)
|
||||
|
||||
termOpRecord.CreateTime = &now
|
||||
termOpRecord.Creator = la.Username
|
||||
termOpRecord.CreatorId = la.Id
|
||||
|
||||
termOpRecord.MachineId = cli.Info.Id
|
||||
termOpRecord.Username = cli.Info.Username
|
||||
|
||||
// 回放文件路径为: 基础配置路径/操作日期(202301)/day/hour/randstr.cast
|
||||
recRelPath := path.Join(now.Format("200601"), fmt.Sprintf("%d", now.Day()), fmt.Sprintf("%d", now.Hour()))
|
||||
// 文件绝对路径
|
||||
recAbsPath := path.Join(config.GetMachine().TerminalRecPath, recRelPath)
|
||||
os.MkdirAll(recAbsPath, 0766)
|
||||
filename := fmt.Sprintf("%s.cast", stringx.RandByChars(18, stringx.LowerChars))
|
||||
f, err := os.OpenFile(path.Join(recAbsPath, filename), os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0766)
|
||||
if err != nil {
|
||||
return errorx.NewBiz("创建终端回放记录文件失败: %s", err.Error())
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
termOpRecord.RecordFilePath = path.Join(recRelPath, filename)
|
||||
recorder = mcm.NewRecorder(f)
|
||||
}
|
||||
|
||||
mts, err := mcm.NewTerminalSession(stringx.Rand(16), wsConn, cli, rows, cols, recorder)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mts.Start()
|
||||
defer mts.Stop()
|
||||
|
||||
if termOpRecord != nil {
|
||||
now := time.Now()
|
||||
termOpRecord.EndTime = &now
|
||||
return a.Insert(ctx, termOpRecord)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *machineTermOpAppImpl) GetPageList(condition *entity.MachineTermOp, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error) {
|
||||
return a.GetRepo().GetPageList(condition, pageParam, toEntity)
|
||||
}
|
||||
@@ -8,14 +8,13 @@ import (
|
||||
type Machine struct {
|
||||
model.Model
|
||||
|
||||
Code string `json:"code"`
|
||||
Name string `json:"name"`
|
||||
Ip string `json:"ip"` // IP地址
|
||||
Port int `json:"port"` // 端口号
|
||||
Username string `json:"username"` // 用户名
|
||||
Password string `json:"password"` // 密码
|
||||
AuthCertId int `json:"authCertId"` // 授权凭证id
|
||||
TagId uint64
|
||||
TagPath string
|
||||
Ip string `json:"ip"` // IP地址
|
||||
Port int `json:"port"` // 端口号
|
||||
Username string `json:"username"` // 用户名
|
||||
Password string `json:"password"` // 密码
|
||||
AuthCertId int `json:"authCertId"` // 授权凭证id
|
||||
Status int8 `json:"status"` // 状态 1:启用;2:停用
|
||||
Remark string `json:"remark"` // 备注
|
||||
SshTunnelMachineId int `json:"sshTunnelMachineId"` // ssh隧道机器id
|
||||
|
||||
19
server/internal/machine/domain/entity/machine_term_op.go
Normal file
19
server/internal/machine/domain/entity/machine_term_op.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package entity
|
||||
|
||||
import (
|
||||
"mayfly-go/pkg/model"
|
||||
"time"
|
||||
)
|
||||
|
||||
type MachineTermOp struct {
|
||||
model.DeletedModel
|
||||
|
||||
MachineId uint64 `json:"machineId"`
|
||||
Username string `json:"username"`
|
||||
RecordFilePath string `json:"recordFilePath"` // 回放文件路径
|
||||
|
||||
CreateTime *time.Time `json:"createTime"`
|
||||
CreatorId uint64 `json:"creatorId"`
|
||||
Creator string `json:"creator"`
|
||||
EndTime *time.Time `json:"endTime"`
|
||||
}
|
||||
@@ -6,7 +6,8 @@ type MachineQuery struct {
|
||||
Status int8 `json:"status" form:"status"`
|
||||
Ip string `json:"ip" form:"ip"` // IP地址
|
||||
TagPath string `json:"tagPath" form:"tagPath"`
|
||||
TagIds []uint64
|
||||
|
||||
Codes []string
|
||||
}
|
||||
|
||||
type AuthCertQuery struct {
|
||||
|
||||
@@ -12,6 +12,4 @@ type Machine interface {
|
||||
|
||||
// 分页获取机器信息列表
|
||||
GetMachineList(condition *entity.MachineQuery, pageParam *model.PageParam, toEntity *[]*vo.MachineVO, orderBy ...string) (*model.PageResult[*[]*vo.MachineVO], error)
|
||||
|
||||
Count(condition *entity.MachineQuery) int64
|
||||
}
|
||||
|
||||
14
server/internal/machine/domain/repository/machine_term_op.go
Normal file
14
server/internal/machine/domain/repository/machine_term_op.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/machine/domain/entity"
|
||||
"mayfly-go/pkg/base"
|
||||
"mayfly-go/pkg/model"
|
||||
)
|
||||
|
||||
type MachineTermOp interface {
|
||||
base.Repo[*entity.MachineTermOp]
|
||||
|
||||
// 分页获取机器终端执行记录列表
|
||||
GetPageList(condition *entity.MachineTermOp, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error)
|
||||
}
|
||||
@@ -26,9 +26,7 @@ func (m *machineRepoImpl) GetMachineList(condition *entity.MachineQuery, pagePar
|
||||
Eq("status", condition.Status).
|
||||
Like("ip", condition.Ip).
|
||||
Like("name", condition.Name).
|
||||
In("tag_id", condition.TagIds).
|
||||
RLike("tag_path", condition.TagPath).
|
||||
OrderByAsc("tag_path")
|
||||
In("code", condition.Codes)
|
||||
|
||||
if condition.Ids != "" {
|
||||
// ,分割id转为id数组
|
||||
@@ -40,12 +38,3 @@ func (m *machineRepoImpl) GetMachineList(condition *entity.MachineQuery, pagePar
|
||||
|
||||
return gormx.PageQuery(qd, pageParam, toEntity)
|
||||
}
|
||||
|
||||
func (m *machineRepoImpl) Count(condition *entity.MachineQuery) int64 {
|
||||
where := make(map[string]any)
|
||||
if len(condition.TagIds) > 0 {
|
||||
where["tag_id"] = condition.TagIds
|
||||
}
|
||||
|
||||
return m.CountByCond(where)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
package persistence
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/machine/domain/entity"
|
||||
"mayfly-go/internal/machine/domain/repository"
|
||||
"mayfly-go/pkg/base"
|
||||
"mayfly-go/pkg/gormx"
|
||||
"mayfly-go/pkg/model"
|
||||
)
|
||||
|
||||
type machineTermOpRepoImpl struct {
|
||||
base.RepoImpl[*entity.MachineTermOp]
|
||||
}
|
||||
|
||||
func newMachineTermOpRepoImpl() repository.MachineTermOp {
|
||||
return &machineTermOpRepoImpl{base.RepoImpl[*entity.MachineTermOp]{M: new(entity.MachineTermOp)}}
|
||||
}
|
||||
|
||||
func (m *machineTermOpRepoImpl) GetPageList(condition *entity.MachineTermOp, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error) {
|
||||
qd := gormx.NewQuery(condition).WithCondModel(condition).WithOrderBy(orderBy...)
|
||||
return gormx.PageQuery(qd, pageParam, toEntity)
|
||||
}
|
||||
@@ -10,6 +10,7 @@ var (
|
||||
machineCropJobRepo repository.MachineCronJob = newMachineCronJobRepo()
|
||||
machineCropJobExecRepo repository.MachineCronJobExec = newMachineCronJobExecRepo()
|
||||
machineCronJobRelateRepo repository.MachineCronJobRelate = newMachineCropJobRelateRepo()
|
||||
machineTermOpRepo repository.MachineTermOp = newMachineTermOpRepoImpl()
|
||||
)
|
||||
|
||||
func GetMachineRepo() repository.Machine {
|
||||
@@ -39,3 +40,7 @@ func GetMachineCronJobExecRepo() repository.MachineCronJobExec {
|
||||
func GetMachineCronJobRelateRepo() repository.MachineCronJobRelate {
|
||||
return machineCronJobRelateRepo
|
||||
}
|
||||
|
||||
func GetMachineTermOpRepo() repository.MachineTermOp {
|
||||
return machineTermOpRepo
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ type MachineInfo struct {
|
||||
|
||||
SshTunnelMachine *MachineInfo `json:"-"` // ssh隧道机器
|
||||
EnableRecorder int8 `json:"-"` // 是否启用终端回放记录
|
||||
TagPath string `json:"tagPath"`
|
||||
TagPath []string `json:"tagPath"`
|
||||
}
|
||||
|
||||
func (m *MachineInfo) UseSshTunnel() bool {
|
||||
|
||||
@@ -140,6 +140,7 @@ type WsMsg struct {
|
||||
Rows int `json:"rows"`
|
||||
}
|
||||
|
||||
// 接收客户端ws发送过来的消息,并写入终端会话中。
|
||||
func (ts *TerminalSession) receiveWsMsg() {
|
||||
wsConn := ts.wsConn
|
||||
for {
|
||||
|
||||
@@ -11,8 +11,9 @@ import (
|
||||
|
||||
func InitMachineRouter(router *gin.RouterGroup) {
|
||||
m := &api.Machine{
|
||||
MachineApp: application.GetMachineApp(),
|
||||
TagApp: tagapp.GetTagTreeApp(),
|
||||
MachineApp: application.GetMachineApp(),
|
||||
MachineTermOpApp: application.GetMachineTermOpApp(),
|
||||
TagApp: tagapp.GetTagTreeApp(),
|
||||
}
|
||||
|
||||
machines := router.Group("machines")
|
||||
@@ -22,8 +23,6 @@ func InitMachineRouter(router *gin.RouterGroup) {
|
||||
reqs := [...]*req.Conf{
|
||||
req.NewGet("", m.Machines),
|
||||
|
||||
req.NewGet("/tags", m.MachineTags),
|
||||
|
||||
req.NewGet(":machineId/stats", m.MachineStats),
|
||||
|
||||
req.NewGet(":machineId/process", m.GetProcess),
|
||||
@@ -40,8 +39,11 @@ func InitMachineRouter(router *gin.RouterGroup) {
|
||||
|
||||
req.NewDelete(":machineId/close-cli", m.CloseCli).Log(req.NewLogSave("关闭机器客户端")).RequiredPermissionCode("machine:close-cli"),
|
||||
|
||||
// 获取机器终端回放记录的相应文件夹名或文件名,目前具有保存机器信息的权限标识才有权限查看终端回放
|
||||
req.NewGet("rec/names", m.MachineRecDirNames).RequiredPermission(saveMachineP),
|
||||
// 获取机器终端回放记录列表,目前具有保存机器信息的权限标识才有权限查看终端回放
|
||||
req.NewGet(":machineId/term-recs", m.MachineTermOpRecords).RequiredPermission(saveMachineP),
|
||||
|
||||
// 获取机器终端回放记录
|
||||
req.NewGet(":machineId/term-recs/:recId", m.MachineTermOpRecord).RequiredPermission(saveMachineP),
|
||||
}
|
||||
|
||||
req.BatchSetGroup(machines, reqs[:])
|
||||
|
||||
@@ -26,6 +26,8 @@ func InitMachineCronJobRouter(router *gin.RouterGroup) {
|
||||
|
||||
req.NewDelete(":ids", cj.Delete).Log(req.NewLogSave("删除机器计划任务")),
|
||||
|
||||
req.NewPost("/run/:key", cj.RunCronJob).Log(req.NewLogSave("手动执行计划任务")),
|
||||
|
||||
req.NewGet("/execs", cj.CronJobExecs),
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
package form
|
||||
|
||||
type Mongo struct {
|
||||
Id uint64 `json:"id"`
|
||||
Uri string `binding:"required" json:"uri"`
|
||||
SshTunnelMachineId int `json:"sshTunnelMachineId"` // ssh隧道机器id
|
||||
Name string `binding:"required" json:"name"`
|
||||
TagId uint64 `binding:"required" json:"tagId"`
|
||||
TagPath string `binding:"required" json:"tagPath"`
|
||||
Id uint64 `json:"id"`
|
||||
Uri string `binding:"required" json:"uri"`
|
||||
SshTunnelMachineId int `json:"sshTunnelMachineId"` // ssh隧道机器id
|
||||
Name string `binding:"required" json:"name"`
|
||||
TagId []uint64 `binding:"required" json:"tagId"`
|
||||
}
|
||||
|
||||
type MongoCommand struct {
|
||||
|
||||
@@ -2,6 +2,7 @@ package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"mayfly-go/internal/common/consts"
|
||||
"mayfly-go/internal/mongo/api/form"
|
||||
"mayfly-go/internal/mongo/application"
|
||||
"mayfly-go/internal/mongo/domain/entity"
|
||||
@@ -30,22 +31,18 @@ func (m *Mongo) Mongos(rc *req.Ctx) {
|
||||
queryCond, page := ginx.BindQueryAndPage[*entity.MongoQuery](rc.GinCtx, new(entity.MongoQuery))
|
||||
|
||||
// 不存在可访问标签id,即没有可操作数据
|
||||
tagIds := m.TagApp.ListTagIdByAccountId(rc.GetLoginAccount().Id)
|
||||
if len(tagIds) == 0 {
|
||||
codes := m.TagApp.GetAccountResourceCodes(rc.GetLoginAccount().Id, consts.TagResourceTypeMongo, queryCond.TagPath)
|
||||
if len(codes) == 0 {
|
||||
rc.ResData = model.EmptyPageResult[any]()
|
||||
return
|
||||
}
|
||||
queryCond.TagIds = tagIds
|
||||
queryCond.Codes = codes
|
||||
|
||||
res, err := m.MongoApp.GetPageList(queryCond, page, new([]entity.Mongo))
|
||||
biz.ErrIsNil(err)
|
||||
rc.ResData = res
|
||||
}
|
||||
|
||||
func (m *Mongo) MongoTags(rc *req.Ctx) {
|
||||
rc.ResData = m.TagApp.ListTagByAccountIdAndResource(rc.GetLoginAccount().Id, new(entity.Mongo))
|
||||
}
|
||||
|
||||
func (m *Mongo) TestConn(rc *req.Ctx) {
|
||||
form := &form.Mongo{}
|
||||
mongo := ginx.BindJsonAndCopyTo[*entity.Mongo](rc.GinCtx, form, new(entity.Mongo))
|
||||
@@ -63,7 +60,7 @@ func (m *Mongo) Save(rc *req.Ctx) {
|
||||
}(form.Uri)
|
||||
rc.ReqParam = form
|
||||
|
||||
biz.ErrIsNil(m.MongoApp.Save(rc.MetaCtx, mongo))
|
||||
biz.ErrIsNil(m.MongoApp.Save(rc.MetaCtx, mongo, form.TagId...))
|
||||
}
|
||||
|
||||
func (m *Mongo) DeleteMongo(rc *req.Ctx) {
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
package application
|
||||
|
||||
import "mayfly-go/internal/mongo/infrastructure/persistence"
|
||||
import (
|
||||
"mayfly-go/internal/mongo/infrastructure/persistence"
|
||||
tagapp "mayfly-go/internal/tag/application"
|
||||
)
|
||||
|
||||
var (
|
||||
mongoApp Mongo = newMongoAppImpl(persistence.GetMongoRepo())
|
||||
mongoApp Mongo = newMongoAppImpl(persistence.GetMongoRepo(), tagapp.GetTagTreeApp())
|
||||
)
|
||||
|
||||
func GetMongoApp() Mongo {
|
||||
|
||||
@@ -2,12 +2,15 @@ package application
|
||||
|
||||
import (
|
||||
"context"
|
||||
"mayfly-go/internal/common/consts"
|
||||
"mayfly-go/internal/mongo/domain/entity"
|
||||
"mayfly-go/internal/mongo/domain/repository"
|
||||
"mayfly-go/internal/mongo/mgm"
|
||||
tagapp "mayfly-go/internal/tag/application"
|
||||
"mayfly-go/pkg/base"
|
||||
"mayfly-go/pkg/errorx"
|
||||
"mayfly-go/pkg/model"
|
||||
"mayfly-go/pkg/utils/stringx"
|
||||
)
|
||||
|
||||
type Mongo interface {
|
||||
@@ -16,11 +19,9 @@ type Mongo interface {
|
||||
// 分页获取机器脚本信息列表
|
||||
GetPageList(condition *entity.MongoQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error)
|
||||
|
||||
Count(condition *entity.MongoQuery) int64
|
||||
|
||||
TestConn(entity *entity.Mongo) error
|
||||
|
||||
Save(ctx context.Context, entity *entity.Mongo) error
|
||||
Save(ctx context.Context, entity *entity.Mongo, tagIds ...uint64) error
|
||||
|
||||
// 删除数据库信息
|
||||
Delete(ctx context.Context, id uint64) error
|
||||
@@ -30,14 +31,18 @@ type Mongo interface {
|
||||
GetMongoConn(id uint64) (*mgm.MongoConn, error)
|
||||
}
|
||||
|
||||
func newMongoAppImpl(mongoRepo repository.Mongo) Mongo {
|
||||
return &mongoAppImpl{
|
||||
base.AppImpl[*entity.Mongo, repository.Mongo]{Repo: mongoRepo},
|
||||
func newMongoAppImpl(mongoRepo repository.Mongo, tagApp tagapp.TagTree) Mongo {
|
||||
app := &mongoAppImpl{
|
||||
tagApp: tagApp,
|
||||
}
|
||||
app.Repo = mongoRepo
|
||||
return app
|
||||
}
|
||||
|
||||
type mongoAppImpl struct {
|
||||
base.AppImpl[*entity.Mongo, repository.Mongo]
|
||||
|
||||
tagApp tagapp.TagTree
|
||||
}
|
||||
|
||||
// 分页获取数据库信息列表
|
||||
@@ -45,10 +50,6 @@ func (d *mongoAppImpl) GetPageList(condition *entity.MongoQuery, pageParam *mode
|
||||
return d.GetRepo().GetList(condition, pageParam, toEntity, orderBy...)
|
||||
}
|
||||
|
||||
func (d *mongoAppImpl) Count(condition *entity.MongoQuery) int64 {
|
||||
return d.GetRepo().Count(condition)
|
||||
}
|
||||
|
||||
func (d *mongoAppImpl) Delete(ctx context.Context, id uint64) error {
|
||||
mgm.CloseConn(id)
|
||||
return d.GetRepo().DeleteById(ctx, id)
|
||||
@@ -63,22 +64,45 @@ func (d *mongoAppImpl) TestConn(me *entity.Mongo) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *mongoAppImpl) Save(ctx context.Context, m *entity.Mongo) error {
|
||||
func (d *mongoAppImpl) Save(ctx context.Context, m *entity.Mongo, tagIds ...uint64) error {
|
||||
oldMongo := &entity.Mongo{Name: m.Name}
|
||||
err := d.GetBy(oldMongo)
|
||||
|
||||
if m.Id == 0 {
|
||||
return d.GetRepo().Insert(ctx, m)
|
||||
if err == nil {
|
||||
return errorx.NewBiz("该名称已存在")
|
||||
}
|
||||
|
||||
resouceCode := stringx.Rand(16)
|
||||
m.Code = resouceCode
|
||||
|
||||
return d.Tx(ctx, func(ctx context.Context) error {
|
||||
return d.Insert(ctx, m)
|
||||
}, func(ctx context.Context) error {
|
||||
return d.tagApp.RelateResource(ctx, resouceCode, consts.TagResourceTypeMongo, tagIds)
|
||||
})
|
||||
}
|
||||
|
||||
// 如果存在该库,则校验修改的库是否为该库
|
||||
if err == nil && oldMongo.Id != m.Id {
|
||||
return errorx.NewBiz("该名称已存在")
|
||||
}
|
||||
|
||||
// 先关闭连接
|
||||
mgm.CloseConn(m.Id)
|
||||
return d.GetRepo().UpdateById(ctx, m)
|
||||
return d.Tx(ctx, func(ctx context.Context) error {
|
||||
return d.UpdateById(ctx, m)
|
||||
}, func(ctx context.Context) error {
|
||||
return d.tagApp.RelateResource(ctx, oldMongo.Code, consts.TagResourceTypeMongo, tagIds)
|
||||
})
|
||||
}
|
||||
|
||||
func (d *mongoAppImpl) GetMongoConn(id uint64) (*mgm.MongoConn, error) {
|
||||
return mgm.GetMongoConn(id, func() (*mgm.MongoInfo, error) {
|
||||
mongo, err := d.GetById(new(entity.Mongo), id)
|
||||
me, err := d.GetById(new(entity.Mongo), id)
|
||||
if err != nil {
|
||||
return nil, errorx.NewBiz("mongo信息不存在")
|
||||
}
|
||||
return mongo.ToMongoInfo(), nil
|
||||
return me.ToMongoInfo(d.tagApp.ListTagPathByResource(consts.TagResourceTypeMongo, me.Code)...), nil
|
||||
})
|
||||
}
|
||||
|
||||
@@ -9,16 +9,16 @@ import (
|
||||
type Mongo struct {
|
||||
model.Model
|
||||
|
||||
Code string `orm:"column(code)" json:"code"`
|
||||
Name string `orm:"column(name)" json:"name"`
|
||||
Uri string `orm:"column(uri)" json:"uri"`
|
||||
SshTunnelMachineId int `orm:"column(ssh_tunnel_machine_id)" json:"sshTunnelMachineId"` // ssh隧道机器id
|
||||
TagId uint64 `json:"tagId"`
|
||||
TagPath string `json:"tagPath"`
|
||||
}
|
||||
|
||||
// 转换为mongoInfo进行连接
|
||||
func (me *Mongo) ToMongoInfo() *mgm.MongoInfo {
|
||||
func (me *Mongo) ToMongoInfo(tagPath ...string) *mgm.MongoInfo {
|
||||
mongoInfo := new(mgm.MongoInfo)
|
||||
structx.Copy(mongoInfo, me)
|
||||
mongoInfo.TagPath = tagPath
|
||||
return mongoInfo
|
||||
}
|
||||
|
||||
@@ -10,5 +10,5 @@ type MongoQuery struct {
|
||||
SshTunnelMachineId uint64 // ssh隧道机器id
|
||||
TagPath string `json:"tagPath" form:"tagPath"`
|
||||
|
||||
TagIds []uint64
|
||||
Codes []string
|
||||
}
|
||||
|
||||
@@ -11,6 +11,4 @@ type Mongo interface {
|
||||
|
||||
// 分页获取列表
|
||||
GetList(condition *entity.MongoQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error)
|
||||
|
||||
Count(condition *entity.MongoQuery) int64
|
||||
}
|
||||
|
||||
@@ -20,16 +20,6 @@ func newMongoRepo() repository.Mongo {
|
||||
func (d *mongoRepoImpl) GetList(condition *entity.MongoQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error) {
|
||||
qd := gormx.NewQuery(new(entity.Mongo)).
|
||||
Like("name", condition.Name).
|
||||
In("tag_id", condition.TagIds).
|
||||
RLike("tag_path", condition.TagPath).
|
||||
OrderByAsc("tag_path")
|
||||
In("code", condition.Codes)
|
||||
return gormx.PageQuery(qd, pageParam, toEntity)
|
||||
}
|
||||
|
||||
func (d *mongoRepoImpl) Count(condition *entity.MongoQuery) int64 {
|
||||
where := make(map[string]any)
|
||||
if len(condition.TagIds) > 0 {
|
||||
where["tag_id"] = condition.TagIds
|
||||
}
|
||||
return gormx.CountByCond(new(entity.Mongo), where)
|
||||
}
|
||||
|
||||
@@ -21,8 +21,8 @@ type MongoInfo struct {
|
||||
|
||||
Uri string `json:"-"`
|
||||
|
||||
TagPath string `json:"tagPath"`
|
||||
SshTunnelMachineId int `json:"-"` // ssh隧道机器id
|
||||
TagPath []string `json:"tagPath"`
|
||||
SshTunnelMachineId int `json:"-"` // ssh隧道机器id
|
||||
}
|
||||
|
||||
func (mi *MongoInfo) Conn() (*MongoConn, error) {
|
||||
|
||||
@@ -23,8 +23,6 @@ func InitMongoRouter(router *gin.RouterGroup) {
|
||||
// 获取所有mongo列表
|
||||
req.NewGet("", ma.Mongos),
|
||||
|
||||
req.NewGet("/tags", ma.MongoTags),
|
||||
|
||||
req.NewPost("/test-conn", ma.TestConn),
|
||||
|
||||
req.NewPost("", ma.Save).Log(req.NewLogSave("mongo-保存信息")),
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
package form
|
||||
|
||||
type Redis struct {
|
||||
Id uint64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Host string `json:"host" binding:"required"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
Mode string `json:"mode"`
|
||||
Db string `json:"db"`
|
||||
SshTunnelMachineId int `json:"sshTunnelMachineId"` // ssh隧道机器id
|
||||
TagId uint64 `binding:"required" json:"tagId"`
|
||||
TagPath string `binding:"required" json:"tagPath"`
|
||||
Remark string `json:"remark"`
|
||||
Id uint64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Host string `json:"host" binding:"required"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
Mode string `json:"mode"`
|
||||
Db string `json:"db"`
|
||||
SshTunnelMachineId int `json:"sshTunnelMachineId"` // ssh隧道机器id
|
||||
TagId []uint64 `binding:"required" json:"tagId"`
|
||||
Remark string `json:"remark"`
|
||||
}
|
||||
|
||||
type Rename struct {
|
||||
|
||||
@@ -2,6 +2,7 @@ package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"mayfly-go/internal/common/consts"
|
||||
"mayfly-go/internal/redis/api/form"
|
||||
"mayfly-go/internal/redis/api/vo"
|
||||
"mayfly-go/internal/redis/application"
|
||||
@@ -31,22 +32,18 @@ func (r *Redis) RedisList(rc *req.Ctx) {
|
||||
queryCond, page := ginx.BindQueryAndPage[*entity.RedisQuery](rc.GinCtx, new(entity.RedisQuery))
|
||||
|
||||
// 不存在可访问标签id,即没有可操作数据
|
||||
tagIds := r.TagApp.ListTagIdByAccountId(rc.GetLoginAccount().Id)
|
||||
if len(tagIds) == 0 {
|
||||
codes := r.TagApp.GetAccountResourceCodes(rc.GetLoginAccount().Id, consts.TagResourceTypeRedis, queryCond.TagPath)
|
||||
if len(codes) == 0 {
|
||||
rc.ResData = model.EmptyPageResult[any]()
|
||||
return
|
||||
}
|
||||
queryCond.TagIds = tagIds
|
||||
queryCond.Codes = codes
|
||||
|
||||
res, err := r.RedisApp.GetPageList(queryCond, page, new([]vo.Redis))
|
||||
biz.ErrIsNil(err)
|
||||
rc.ResData = res
|
||||
}
|
||||
|
||||
func (r *Redis) RedisTags(rc *req.Ctx) {
|
||||
rc.ResData = r.TagApp.ListTagByAccountIdAndResource(rc.GetLoginAccount().Id, new(entity.Redis))
|
||||
}
|
||||
|
||||
func (r *Redis) TestConn(rc *req.Ctx) {
|
||||
form := &form.Redis{}
|
||||
redis := ginx.BindJsonAndCopyTo[*entity.Redis](rc.GinCtx, form, new(entity.Redis))
|
||||
@@ -72,7 +69,7 @@ func (r *Redis) Save(rc *req.Ctx) {
|
||||
form.Password = "****"
|
||||
rc.ReqParam = form
|
||||
|
||||
biz.ErrIsNil(r.RedisApp.Save(rc.MetaCtx, redis))
|
||||
biz.ErrIsNil(r.RedisApp.Save(rc.MetaCtx, redis, form.TagId...))
|
||||
}
|
||||
|
||||
// 获取redis实例密码,由于数据库是加密存储,故提供该接口展示原文密码
|
||||
@@ -229,7 +226,7 @@ func (r *Redis) checkKeyAndGetRedisConn(rc *req.Ctx) (*rdm.RedisConn, string) {
|
||||
func (r *Redis) getRedisConn(rc *req.Ctx) *rdm.RedisConn {
|
||||
ri, err := r.RedisApp.GetRedisConn(getIdAndDbNum(rc.GinCtx))
|
||||
biz.ErrIsNil(err)
|
||||
biz.ErrIsNilAppendErr(r.TagApp.CanAccess(rc.GetLoginAccount().Id, ri.Info.TagPath), "%s")
|
||||
biz.ErrIsNilAppendErr(r.TagApp.CanAccess(rc.GetLoginAccount().Id, ri.Info.TagPath...), "%s")
|
||||
return ri
|
||||
}
|
||||
|
||||
|
||||
@@ -4,14 +4,13 @@ import "time"
|
||||
|
||||
type Redis struct {
|
||||
Id *int64 `json:"id"`
|
||||
Code *string `json:"code"`
|
||||
Name *string `json:"name"`
|
||||
Host *string `json:"host"`
|
||||
Db string `json:"db"`
|
||||
Mode *string `json:"mode"`
|
||||
SshTunnelMachineId int `json:"sshTunnelMachineId"` // ssh隧道机器id
|
||||
Remark *string `json:"remark"`
|
||||
TagId *uint64 `json:"tagId"`
|
||||
TagPath *string `json:"tagPath"`
|
||||
CreateTime *time.Time `json:"createTime"`
|
||||
Creator *string `json:"creator"`
|
||||
CreatorId *int64 `json:"creatorId"`
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
package application
|
||||
|
||||
import "mayfly-go/internal/redis/infrastructure/persistence"
|
||||
import (
|
||||
"mayfly-go/internal/redis/infrastructure/persistence"
|
||||
tagapp "mayfly-go/internal/tag/application"
|
||||
)
|
||||
|
||||
var (
|
||||
redisApp Redis = newRedisApp(persistence.GetRedisRepo())
|
||||
redisApp Redis = newRedisApp(persistence.GetRedisRepo(), tagapp.GetTagTreeApp())
|
||||
)
|
||||
|
||||
func GetRedisApp() Redis {
|
||||
|
||||
@@ -2,12 +2,15 @@ package application
|
||||
|
||||
import (
|
||||
"context"
|
||||
"mayfly-go/internal/common/consts"
|
||||
"mayfly-go/internal/redis/domain/entity"
|
||||
"mayfly-go/internal/redis/domain/repository"
|
||||
"mayfly-go/internal/redis/rdm"
|
||||
tagapp "mayfly-go/internal/tag/application"
|
||||
"mayfly-go/pkg/base"
|
||||
"mayfly-go/pkg/errorx"
|
||||
"mayfly-go/pkg/model"
|
||||
"mayfly-go/pkg/utils/stringx"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
@@ -18,12 +21,10 @@ type Redis interface {
|
||||
// 分页获取机器脚本信息列表
|
||||
GetPageList(condition *entity.RedisQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error)
|
||||
|
||||
Count(condition *entity.RedisQuery) int64
|
||||
|
||||
// 测试连接
|
||||
TestConn(re *entity.Redis) error
|
||||
|
||||
Save(ctx context.Context, re *entity.Redis) error
|
||||
Save(ctx context.Context, re *entity.Redis, tagIds ...uint64) error
|
||||
|
||||
// 删除数据库信息
|
||||
Delete(ctx context.Context, id uint64) error
|
||||
@@ -34,14 +35,18 @@ type Redis interface {
|
||||
GetRedisConn(id uint64, db int) (*rdm.RedisConn, error)
|
||||
}
|
||||
|
||||
func newRedisApp(redisRepo repository.Redis) Redis {
|
||||
return &redisAppImpl{
|
||||
base.AppImpl[*entity.Redis, repository.Redis]{Repo: redisRepo},
|
||||
func newRedisApp(redisRepo repository.Redis, tagApp tagapp.TagTree) Redis {
|
||||
app := &redisAppImpl{
|
||||
tagApp: tagApp,
|
||||
}
|
||||
app.Repo = redisRepo
|
||||
return app
|
||||
}
|
||||
|
||||
type redisAppImpl struct {
|
||||
base.AppImpl[*entity.Redis, repository.Redis]
|
||||
|
||||
tagApp tagapp.TagTree
|
||||
}
|
||||
|
||||
// 分页获取redis列表
|
||||
@@ -49,10 +54,6 @@ func (r *redisAppImpl) GetPageList(condition *entity.RedisQuery, pageParam *mode
|
||||
return r.GetRepo().GetRedisList(condition, pageParam, toEntity, orderBy...)
|
||||
}
|
||||
|
||||
func (r *redisAppImpl) Count(condition *entity.RedisQuery) int64 {
|
||||
return r.GetRepo().Count(condition)
|
||||
}
|
||||
|
||||
func (r *redisAppImpl) TestConn(re *entity.Redis) error {
|
||||
db := 0
|
||||
if re.Db != "" {
|
||||
@@ -67,7 +68,7 @@ func (r *redisAppImpl) TestConn(re *entity.Redis) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *redisAppImpl) Save(ctx context.Context, re *entity.Redis) error {
|
||||
func (r *redisAppImpl) Save(ctx context.Context, re *entity.Redis, tagIds ...uint64) error {
|
||||
// 查找是否存在该库
|
||||
oldRedis := &entity.Redis{Host: re.Host}
|
||||
if re.SshTunnelMachineId > 0 {
|
||||
@@ -80,7 +81,15 @@ func (r *redisAppImpl) Save(ctx context.Context, re *entity.Redis) error {
|
||||
return errorx.NewBiz("该实例已存在")
|
||||
}
|
||||
re.PwdEncrypt()
|
||||
return r.Insert(ctx, re)
|
||||
|
||||
resouceCode := stringx.Rand(16)
|
||||
re.Code = resouceCode
|
||||
|
||||
return r.Tx(ctx, func(ctx context.Context) error {
|
||||
return r.Insert(ctx, re)
|
||||
}, func(ctx context.Context) error {
|
||||
return r.tagApp.RelateResource(ctx, resouceCode, consts.TagResourceTypeRedis, tagIds)
|
||||
})
|
||||
}
|
||||
|
||||
// 如果存在该库,则校验修改的库是否为该库
|
||||
@@ -94,8 +103,13 @@ func (r *redisAppImpl) Save(ctx context.Context, re *entity.Redis) error {
|
||||
rdm.CloseConn(re.Id, db)
|
||||
}
|
||||
}
|
||||
|
||||
re.PwdEncrypt()
|
||||
return r.UpdateById(ctx, re)
|
||||
return r.Tx(ctx, func(ctx context.Context) error {
|
||||
return r.UpdateById(ctx, re)
|
||||
}, func(ctx context.Context) error {
|
||||
return r.tagApp.RelateResource(ctx, oldRedis.Code, consts.TagResourceTypeRedis, tagIds)
|
||||
})
|
||||
}
|
||||
|
||||
// 删除Redis信息
|
||||
@@ -122,6 +136,6 @@ func (r *redisAppImpl) GetRedisConn(id uint64, db int) (*rdm.RedisConn, error) {
|
||||
}
|
||||
re.PwdDecrypt()
|
||||
|
||||
return re.ToRedisInfo(db), nil
|
||||
return re.ToRedisInfo(db, r.tagApp.ListTagPathByResource(consts.TagResourceTypeRedis, re.Code)...), nil
|
||||
})
|
||||
}
|
||||
|
||||
@@ -13,6 +13,6 @@ type RedisQuery struct {
|
||||
SshTunnelMachineId int `orm:"column(ssh_tunnel_machine_id)" json:"sshTunnelMachineId"` // ssh隧道机器id
|
||||
Remark string
|
||||
|
||||
TagIds []uint64
|
||||
Codes []string
|
||||
TagPath string `form:"tagPath"`
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
type Redis struct {
|
||||
model.Model
|
||||
|
||||
Code string `orm:"column(code)" json:"code"`
|
||||
Name string `orm:"column(name)" json:"name"`
|
||||
Host string `orm:"column(host)" json:"host"`
|
||||
Mode string `json:"mode"`
|
||||
@@ -18,8 +19,6 @@ type Redis struct {
|
||||
Db string `orm:"column(database)" json:"db"`
|
||||
SshTunnelMachineId int `orm:"column(ssh_tunnel_machine_id)" json:"sshTunnelMachineId"` // ssh隧道机器id
|
||||
Remark string
|
||||
TagId uint64
|
||||
TagPath string
|
||||
}
|
||||
|
||||
func (r *Redis) PwdEncrypt() {
|
||||
@@ -33,9 +32,10 @@ func (r *Redis) PwdDecrypt() {
|
||||
}
|
||||
|
||||
// 转换为redisInfo进行连接
|
||||
func (re *Redis) ToRedisInfo(db int) *rdm.RedisInfo {
|
||||
func (re *Redis) ToRedisInfo(db int, tagPath ...string) *rdm.RedisInfo {
|
||||
redisInfo := new(rdm.RedisInfo)
|
||||
structx.Copy(redisInfo, re)
|
||||
redisInfo.Db = db
|
||||
redisInfo.TagPath = tagPath
|
||||
return redisInfo
|
||||
}
|
||||
|
||||
@@ -11,6 +11,4 @@ type Redis interface {
|
||||
|
||||
// 分页获取机器信息列表
|
||||
GetRedisList(condition *entity.RedisQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error)
|
||||
|
||||
Count(condition *entity.RedisQuery) int64
|
||||
}
|
||||
|
||||
@@ -20,17 +20,6 @@ func newRedisRepo() repository.Redis {
|
||||
func (r *redisRepoImpl) GetRedisList(condition *entity.RedisQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error) {
|
||||
qd := gormx.NewQuery(new(entity.Redis)).
|
||||
Like("host", condition.Host).
|
||||
In("tag_id", condition.TagIds).
|
||||
RLike("tag_path", condition.TagPath).
|
||||
OrderByAsc("tag_path")
|
||||
In("code", condition.Codes)
|
||||
return gormx.PageQuery(qd, pageParam, toEntity)
|
||||
}
|
||||
|
||||
func (r *redisRepoImpl) Count(condition *entity.RedisQuery) int64 {
|
||||
where := make(map[string]any)
|
||||
if len(condition.TagIds) > 0 {
|
||||
where["tag_id"] = condition.TagIds
|
||||
}
|
||||
|
||||
return gormx.CountByCond(new(entity.Redis), where)
|
||||
}
|
||||
|
||||
@@ -31,9 +31,9 @@ type RedisInfo struct {
|
||||
Username string `json:"-"`
|
||||
Password string `json:"-"`
|
||||
|
||||
Name string `json:"-"`
|
||||
TagPath string `json:"tagPath"`
|
||||
SshTunnelMachineId int `json:"-"`
|
||||
Name string `json:"-"`
|
||||
TagPath []string `json:"tagPath"`
|
||||
SshTunnelMachineId int `json:"-"`
|
||||
}
|
||||
|
||||
func (r *RedisInfo) Conn() (*RedisConn, error) {
|
||||
|
||||
@@ -26,8 +26,6 @@ func InitRedisRouter(router *gin.RouterGroup) {
|
||||
// 获取redis list
|
||||
req.NewGet("", rs.RedisList),
|
||||
|
||||
req.NewGet("/tags", rs.RedisTags),
|
||||
|
||||
req.NewPost("/test-conn", rs.TestConn),
|
||||
|
||||
req.NewPost("", rs.Save).Log(req.NewLogSave("redis-保存信息")),
|
||||
|
||||
@@ -43,9 +43,10 @@ func (m *syslogAppImpl) SaveFromReq(req *req.Ctx) {
|
||||
syslog.CreateTime = time.Now()
|
||||
syslog.Creator = lg.Username
|
||||
syslog.CreatorId = lg.Id
|
||||
syslog.Description = req.GetLogInfo().Description
|
||||
|
||||
if req.GetLogInfo().LogResp {
|
||||
logInfo := req.GetLogInfo()
|
||||
syslog.Description = logInfo.Description
|
||||
if logInfo.LogResp {
|
||||
respB, _ := json.Marshal(req.ResData)
|
||||
syslog.Resp = string(respB)
|
||||
}
|
||||
|
||||
@@ -2,34 +2,62 @@ package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"mayfly-go/internal/common/consts"
|
||||
"mayfly-go/internal/tag/api/vo"
|
||||
"mayfly-go/internal/tag/application"
|
||||
"mayfly-go/internal/tag/domain/entity"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/ginx"
|
||||
"mayfly-go/pkg/req"
|
||||
"mayfly-go/pkg/utils/collx"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/exp/maps"
|
||||
)
|
||||
|
||||
type TagTree struct {
|
||||
TagTreeApp application.TagTree
|
||||
}
|
||||
|
||||
func (p *TagTree) GetAccountTags(rc *req.Ctx) {
|
||||
tagPaths := p.TagTreeApp.ListTagByAccountId(rc.GetLoginAccount().Id)
|
||||
allTagPath := make([]string, 0)
|
||||
if len(tagPaths) > 0 {
|
||||
tags := p.TagTreeApp.ListTagByPath(tagPaths...)
|
||||
for _, v := range tags {
|
||||
allTagPath = append(allTagPath, v.CodePath)
|
||||
}
|
||||
}
|
||||
rc.ResData = allTagPath
|
||||
TagTreeApp application.TagTree
|
||||
TagResourceApp application.TagResource
|
||||
}
|
||||
|
||||
func (p *TagTree) GetTagTree(rc *req.Ctx) {
|
||||
var tagTrees vo.TagTreeVOS
|
||||
p.TagTreeApp.ListByQuery(new(entity.TagTreeQuery), &tagTrees)
|
||||
// 超管返回所有标签树
|
||||
if rc.GetLoginAccount().Id == consts.AdminId {
|
||||
var tagTrees vo.TagTreeVOS
|
||||
p.TagTreeApp.ListByQuery(new(entity.TagTreeQuery), &tagTrees)
|
||||
rc.ResData = tagTrees.ToTrees(0)
|
||||
return
|
||||
}
|
||||
|
||||
// 获取用户可以操作访问的标签路径
|
||||
tagPaths := p.TagTreeApp.ListTagByAccountId(rc.GetLoginAccount().Id)
|
||||
|
||||
rootTag := make(map[string][]string, 0)
|
||||
for _, accountTagPath := range tagPaths {
|
||||
root := strings.Split(accountTagPath, "/")[0] + entity.CodePathSeparator
|
||||
tags := rootTag[root]
|
||||
tags = append(tags, accountTagPath)
|
||||
rootTag[root] = tags
|
||||
|
||||
}
|
||||
|
||||
// 获取所有以root标签开头的子标签
|
||||
tags := p.TagTreeApp.ListTagByPath(maps.Keys(rootTag)...)
|
||||
tagTrees := make(vo.TagTreeVOS, 0)
|
||||
for _, tag := range tags {
|
||||
tagPath := tag.CodePath
|
||||
root := strings.Split(tagPath, "/")[0] + entity.CodePathSeparator
|
||||
// 获取用户可操作的标签路径列表
|
||||
accountTagPaths := rootTag[root]
|
||||
for _, accountTagPath := range accountTagPaths {
|
||||
if strings.HasPrefix(tagPath, accountTagPath) || strings.HasPrefix(accountTagPath, tagPath) {
|
||||
tagTrees = append(tagTrees, tag)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rc.ResData = tagTrees.ToTrees(0)
|
||||
}
|
||||
|
||||
@@ -46,7 +74,7 @@ func (p *TagTree) SaveTagTree(rc *req.Ctx) {
|
||||
tagTree := &entity.TagTree{}
|
||||
ginx.BindJsonAndValid(rc.GinCtx, tagTree)
|
||||
|
||||
rc.ReqParam = fmt.Sprintf("tagTreeId: %d, tagName: %s, codePath: %s", tagTree.Id, tagTree.Name, tagTree.CodePath)
|
||||
rc.ReqParam = fmt.Sprintf("tagTreeId: %d, tagName: %s, code: %s", tagTree.Id, tagTree.Name, tagTree.Code)
|
||||
|
||||
biz.ErrIsNil(p.TagTreeApp.Save(rc.MetaCtx, tagTree))
|
||||
}
|
||||
@@ -54,3 +82,23 @@ func (p *TagTree) SaveTagTree(rc *req.Ctx) {
|
||||
func (p *TagTree) DelTagTree(rc *req.Ctx) {
|
||||
biz.ErrIsNil(p.TagTreeApp.Delete(rc.MetaCtx, uint64(ginx.PathParamInt(rc.GinCtx, "id"))))
|
||||
}
|
||||
|
||||
// 获取用户可操作的资源标签路径
|
||||
func (p *TagTree) TagResources(rc *req.Ctx) {
|
||||
resourceType := int8(ginx.PathParamInt(rc.GinCtx, "rtype"))
|
||||
tagResources := p.TagTreeApp.GetAccountTagResources(rc.GetLoginAccount().Id, resourceType, "")
|
||||
tagPath2Resource := collx.ArrayToMap[entity.TagResource, string](tagResources, func(tagResource entity.TagResource) string {
|
||||
return tagResource.TagPath
|
||||
})
|
||||
|
||||
tagPaths := maps.Keys(tagPath2Resource)
|
||||
sort.Strings(tagPaths)
|
||||
rc.ResData = tagPaths
|
||||
}
|
||||
|
||||
// 资源标签关联信息查询
|
||||
func (p *TagTree) QueryTagResources(rc *req.Ctx) {
|
||||
var trs []*entity.TagResource
|
||||
p.TagResourceApp.ListByQuery(ginx.BindQuery(rc.GinCtx, new(entity.TagResourceQuery)), &trs)
|
||||
rc.ResData = trs
|
||||
}
|
||||
|
||||
@@ -1,28 +1,17 @@
|
||||
package vo
|
||||
|
||||
import "time"
|
||||
import (
|
||||
"mayfly-go/internal/tag/domain/entity"
|
||||
)
|
||||
|
||||
type TagTreeVO struct {
|
||||
Id int `json:"id"`
|
||||
Pid int `json:"pid"`
|
||||
Name string `json:"name"`
|
||||
Code string `json:"code"`
|
||||
CodePath string `json:"codePath"`
|
||||
Remark string `json:"remark"`
|
||||
Creator string `json:"creator"`
|
||||
CreateTime time.Time `json:"createTime"`
|
||||
Modifier string `json:"modifier"`
|
||||
UpdateTime time.Time `json:"updateTime"`
|
||||
}
|
||||
|
||||
type TagTreeVOS []TagTreeVO
|
||||
type TagTreeVOS []*entity.TagTree
|
||||
|
||||
type TagTreeItem struct {
|
||||
TagTreeVO
|
||||
*entity.TagTree
|
||||
Children []TagTreeItem `json:"children"`
|
||||
}
|
||||
|
||||
func (m *TagTreeVOS) ToTrees(pid int) []TagTreeItem {
|
||||
func (m *TagTreeVOS) ToTrees(pid uint64) []TagTreeItem {
|
||||
var resourceTree []TagTreeItem
|
||||
|
||||
list := m.findChildren(pid)
|
||||
@@ -31,15 +20,15 @@ func (m *TagTreeVOS) ToTrees(pid int) []TagTreeItem {
|
||||
}
|
||||
|
||||
for _, v := range list {
|
||||
Children := m.ToTrees(int(v.Id))
|
||||
Children := m.ToTrees(v.Id)
|
||||
resourceTree = append(resourceTree, TagTreeItem{v, Children})
|
||||
}
|
||||
|
||||
return resourceTree
|
||||
}
|
||||
|
||||
func (m *TagTreeVOS) findChildren(pid int) []TagTreeVO {
|
||||
child := []TagTreeVO{}
|
||||
func (m *TagTreeVOS) findChildren(pid uint64) []*entity.TagTree {
|
||||
child := []*entity.TagTree{}
|
||||
|
||||
for _, v := range *m {
|
||||
if v.Pid == pid {
|
||||
|
||||
@@ -1,21 +1,14 @@
|
||||
package application
|
||||
|
||||
import (
|
||||
dbapp "mayfly-go/internal/db/application"
|
||||
machineapp "mayfly-go/internal/machine/application"
|
||||
mongoapp "mayfly-go/internal/mongo/application"
|
||||
redisapp "mayfly-go/internal/redis/application"
|
||||
"mayfly-go/internal/tag/infrastructure/persistence"
|
||||
)
|
||||
|
||||
var (
|
||||
tagTreeApp TagTree = newTagTreeApp(
|
||||
persistence.GetTagTreeRepo(),
|
||||
GetTagResourceApp(),
|
||||
persistence.GetTagTreeTeamRepo(),
|
||||
machineapp.GetMachineApp(),
|
||||
redisapp.GetRedisApp(),
|
||||
dbapp.GetDbApp(),
|
||||
mongoapp.GetMongoApp(),
|
||||
)
|
||||
|
||||
teamApp Team = newTeamApp(
|
||||
@@ -23,6 +16,8 @@ var (
|
||||
persistence.GetTeamMemberRepo(),
|
||||
persistence.GetTagTreeTeamRepo(),
|
||||
)
|
||||
|
||||
tagResourceApp TagResource = newTagResourceApp(persistence.GetTagResourceRepo())
|
||||
)
|
||||
|
||||
func GetTagTreeApp() TagTree {
|
||||
@@ -32,3 +27,7 @@ func GetTagTreeApp() TagTree {
|
||||
func GetTeamApp() Team {
|
||||
return teamApp
|
||||
}
|
||||
|
||||
func GetTagResourceApp() TagResource {
|
||||
return tagResourceApp
|
||||
}
|
||||
|
||||
27
server/internal/tag/application/tag_resource.go
Normal file
27
server/internal/tag/application/tag_resource.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package application
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/tag/domain/entity"
|
||||
"mayfly-go/internal/tag/domain/repository"
|
||||
"mayfly-go/pkg/base"
|
||||
)
|
||||
|
||||
type TagResource interface {
|
||||
base.App[*entity.TagResource]
|
||||
|
||||
ListByQuery(condition *entity.TagResourceQuery, toEntity any)
|
||||
}
|
||||
|
||||
func newTagResourceApp(tagResourceRepo repository.TagResource) TagResource {
|
||||
tagResourceApp := &tagResourceAppImpl{}
|
||||
tagResourceApp.Repo = tagResourceRepo
|
||||
return tagResourceApp
|
||||
}
|
||||
|
||||
type tagResourceAppImpl struct {
|
||||
base.AppImpl[*entity.TagResource, repository.TagResource]
|
||||
}
|
||||
|
||||
func (tr *tagResourceAppImpl) ListByQuery(condition *entity.TagResourceQuery, toEntity any) {
|
||||
tr.Repo.SelectByCondition(condition, toEntity)
|
||||
}
|
||||
@@ -2,21 +2,16 @@ package application
|
||||
|
||||
import (
|
||||
"context"
|
||||
dbapp "mayfly-go/internal/db/application"
|
||||
dbentity "mayfly-go/internal/db/domain/entity"
|
||||
machineapp "mayfly-go/internal/machine/application"
|
||||
machineentity "mayfly-go/internal/machine/domain/entity"
|
||||
mongoapp "mayfly-go/internal/mongo/application"
|
||||
mongoentity "mayfly-go/internal/mongo/domain/entity"
|
||||
redisapp "mayfly-go/internal/redis/application"
|
||||
redisentity "mayfly-go/internal/redis/domain/entity"
|
||||
"mayfly-go/internal/common/consts"
|
||||
"mayfly-go/internal/tag/domain/entity"
|
||||
"mayfly-go/internal/tag/domain/repository"
|
||||
"mayfly-go/pkg/base"
|
||||
"mayfly-go/pkg/contextx"
|
||||
"mayfly-go/pkg/errorx"
|
||||
"mayfly-go/pkg/global"
|
||||
"mayfly-go/pkg/gormx"
|
||||
"mayfly-go/pkg/utils/collx"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/exp/maps"
|
||||
)
|
||||
|
||||
type TagTree interface {
|
||||
@@ -28,38 +23,41 @@ type TagTree interface {
|
||||
|
||||
Delete(ctx context.Context, id uint64) error
|
||||
|
||||
// 获取账号id拥有的可访问的标签id
|
||||
ListTagIdByAccountId(accountId uint64) []uint64
|
||||
// 获取指定账号有权限操作的资源信息列表
|
||||
// @param accountId 账号id
|
||||
// @param resourceType 资源类型
|
||||
// @param tagPath 访问指定的标签路径下关联的资源
|
||||
GetAccountTagResources(accountId uint64, resourceType int8, tagPath string) []entity.TagResource
|
||||
|
||||
// 获取以指定tagPath数组开头的所有标签id
|
||||
ListTagIdByPath(tagPath ...string) []uint64
|
||||
// 获取指定账号有权限操作的资源codes
|
||||
GetAccountResourceCodes(accountId uint64, resourceType int8, tagPath string) []string
|
||||
|
||||
// 关联资源
|
||||
// @resourceCode 资源唯一编号
|
||||
// @resourceType 资源类型
|
||||
// @tagIds 资源关联的标签
|
||||
RelateResource(ctx context.Context, resourceCode string, resourceType int8, tagIds []uint64) error
|
||||
|
||||
// 根据资源信息获取对应的标签路径列表
|
||||
ListTagPathByResource(resourceType int8, resourceCode string) []string
|
||||
|
||||
// 根据tagPath获取自身及其所有子标签信息
|
||||
ListTagByPath(tagPath ...string) []entity.TagTree
|
||||
ListTagByPath(tagPath ...string) []*entity.TagTree
|
||||
|
||||
// 根据账号id获取其可访问标签信息
|
||||
ListTagByAccountId(accountId uint64) []string
|
||||
|
||||
// 查询账号id可访问的资源相关联的标签信息
|
||||
// @param model对应资源的实体信息,如Machinie、Db等等
|
||||
ListTagByAccountIdAndResource(accountId uint64, model any) []string
|
||||
|
||||
// 账号是否有权限访问该标签关联的资源信息
|
||||
CanAccess(accountId uint64, tagPath string) error
|
||||
CanAccess(accountId uint64, tagPath ...string) error
|
||||
}
|
||||
|
||||
func newTagTreeApp(tagTreeRepo repository.TagTree,
|
||||
tagResourceApp TagResource,
|
||||
tagTreeTeamRepo repository.TagTreeTeam,
|
||||
machineApp machineapp.Machine,
|
||||
redisApp redisapp.Redis,
|
||||
dbApp dbapp.Db,
|
||||
mongoApp mongoapp.Mongo) TagTree {
|
||||
) TagTree {
|
||||
tagTreeApp := &tagTreeAppImpl{
|
||||
tagTreeTeamRepo: tagTreeTeamRepo,
|
||||
machineApp: machineApp,
|
||||
redisApp: redisApp,
|
||||
dbApp: dbApp,
|
||||
mongoApp: mongoApp,
|
||||
tagResourceApp: tagResourceApp,
|
||||
}
|
||||
tagTreeApp.Repo = tagTreeRepo
|
||||
return tagTreeApp
|
||||
@@ -69,13 +67,11 @@ type tagTreeAppImpl struct {
|
||||
base.AppImpl[*entity.TagTree, repository.TagTree]
|
||||
|
||||
tagTreeTeamRepo repository.TagTreeTeam
|
||||
machineApp machineapp.Machine
|
||||
redisApp redisapp.Redis
|
||||
mongoApp mongoapp.Mongo
|
||||
dbApp dbapp.Db
|
||||
tagResourceApp TagResource
|
||||
}
|
||||
|
||||
func (p *tagTreeAppImpl) Save(ctx context.Context, tag *entity.TagTree) error {
|
||||
accountId := contextx.GetLoginAccount(ctx).Id
|
||||
// 新建项目树节点信息
|
||||
if tag.Id == 0 {
|
||||
if strings.Contains(tag.Code, entity.CodePathSeparator) {
|
||||
@@ -86,10 +82,18 @@ func (p *tagTreeAppImpl) Save(ctx context.Context, tag *entity.TagTree) error {
|
||||
if err != nil {
|
||||
return errorx.NewBiz("父节点不存在")
|
||||
}
|
||||
if p.tagResourceApp.CountByCond(&entity.TagResource{TagId: tag.Pid}) > 0 {
|
||||
return errorx.NewBiz("该父标签已关联资源, 无法添加子标签")
|
||||
}
|
||||
|
||||
tag.CodePath = parentTag.CodePath + tag.Code + entity.CodePathSeparator
|
||||
} else {
|
||||
tag.CodePath = tag.Code + entity.CodePathSeparator
|
||||
}
|
||||
if err := p.CanAccess(accountId, tag.CodePath); err != nil {
|
||||
return errorx.NewBiz("无权添加该标签")
|
||||
}
|
||||
|
||||
// 判断该路径是否存在
|
||||
var hasLikeTags []entity.TagTree
|
||||
p.GetRepo().SelectByCondition(&entity.TagTreeQuery{CodePathLike: tag.CodePath}, &hasLikeTags)
|
||||
@@ -110,52 +114,114 @@ func (p *tagTreeAppImpl) ListByQuery(condition *entity.TagTreeQuery, toEntity an
|
||||
p.GetRepo().SelectByCondition(condition, toEntity)
|
||||
}
|
||||
|
||||
func (p *tagTreeAppImpl) ListTagIdByAccountId(accountId uint64) []uint64 {
|
||||
// 获取该账号可操作的标签路径
|
||||
return p.ListTagIdByPath(p.ListTagByAccountId(accountId)...)
|
||||
func (p *tagTreeAppImpl) GetAccountTagResources(accountId uint64, resourceType int8, tagPath string) []entity.TagResource {
|
||||
tagResourceQuery := &entity.TagResourceQuery{
|
||||
ResourceType: resourceType,
|
||||
}
|
||||
|
||||
var tagResources []entity.TagResource
|
||||
var accountTagPaths []string
|
||||
|
||||
if accountId != consts.AdminId {
|
||||
// 获取账号有权限操作的标签路径列表
|
||||
accountTagPaths = p.ListTagByAccountId(accountId)
|
||||
if len(accountTagPaths) == 0 {
|
||||
return tagResources
|
||||
}
|
||||
}
|
||||
|
||||
tagResourceQuery.TagPath = tagPath
|
||||
tagResourceQuery.TagPathLikes = accountTagPaths
|
||||
p.tagResourceApp.ListByQuery(tagResourceQuery, &tagResources)
|
||||
return tagResources
|
||||
}
|
||||
|
||||
func (p *tagTreeAppImpl) ListTagByPath(tagPaths ...string) []entity.TagTree {
|
||||
var tags []entity.TagTree
|
||||
func (p *tagTreeAppImpl) GetAccountResourceCodes(accountId uint64, resourceType int8, tagPath string) []string {
|
||||
tagResources := p.GetAccountTagResources(accountId, resourceType, tagPath)
|
||||
// resouce code去重
|
||||
code2Resource := collx.ArrayToMap[entity.TagResource, string](tagResources, func(val entity.TagResource) string {
|
||||
return val.ResourceCode
|
||||
})
|
||||
|
||||
return maps.Keys(code2Resource)
|
||||
}
|
||||
|
||||
func (p *tagTreeAppImpl) RelateResource(ctx context.Context, resourceCode string, resourceType int8, tagIds []uint64) error {
|
||||
var oldTagResources []*entity.TagResource
|
||||
p.tagResourceApp.ListByQuery(&entity.TagResourceQuery{ResourceType: resourceType, ResourceCode: resourceCode}, &oldTagResources)
|
||||
|
||||
var addTagIds, delTagIds []uint64
|
||||
if len(oldTagResources) == 0 {
|
||||
addTagIds = tagIds
|
||||
} else {
|
||||
oldTagIds := collx.ArrayMap[*entity.TagResource, uint64](oldTagResources, func(tr *entity.TagResource) uint64 {
|
||||
return tr.TagId
|
||||
})
|
||||
addTagIds, delTagIds, _ = collx.ArrayCompare[uint64](tagIds, oldTagIds, func(u1, u2 uint64) bool { return u1 == u2 })
|
||||
}
|
||||
|
||||
return p.Tx(ctx, func(ctx context.Context) error {
|
||||
if len(addTagIds) > 0 {
|
||||
addTagResource := make([]*entity.TagResource, 0)
|
||||
for _, tagId := range addTagIds {
|
||||
tag, err := p.GetById(new(entity.TagTree), tagId)
|
||||
if err != nil {
|
||||
return errorx.NewBiz("存在错误标签id")
|
||||
}
|
||||
addTagResource = append(addTagResource, &entity.TagResource{
|
||||
ResourceCode: resourceCode,
|
||||
ResourceType: resourceType,
|
||||
TagId: tagId,
|
||||
TagPath: tag.CodePath,
|
||||
})
|
||||
}
|
||||
if err := p.tagResourceApp.BatchInsert(ctx, addTagResource); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if len(delTagIds) > 0 {
|
||||
for _, tagId := range delTagIds {
|
||||
cond := &entity.TagResource{ResourceCode: resourceCode, ResourceType: resourceType, TagId: tagId}
|
||||
if err := p.tagResourceApp.DeleteByCond(ctx, cond); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (p *tagTreeAppImpl) ListTagPathByResource(resourceType int8, resourceCode string) []string {
|
||||
var trs []*entity.TagResource
|
||||
p.tagResourceApp.ListByQuery(&entity.TagResourceQuery{ResourceType: resourceType, ResourceCode: resourceCode}, &trs)
|
||||
return collx.ArrayMap(trs, func(tr *entity.TagResource) string {
|
||||
return tr.TagPath
|
||||
})
|
||||
}
|
||||
|
||||
func (p *tagTreeAppImpl) ListTagByPath(tagPaths ...string) []*entity.TagTree {
|
||||
var tags []*entity.TagTree
|
||||
p.GetRepo().SelectByCondition(&entity.TagTreeQuery{CodePathLikes: tagPaths}, &tags)
|
||||
return tags
|
||||
}
|
||||
|
||||
func (p *tagTreeAppImpl) ListTagIdByPath(tagPaths ...string) []uint64 {
|
||||
tagIds := make([]uint64, 0)
|
||||
if len(tagPaths) == 0 {
|
||||
return tagIds
|
||||
}
|
||||
|
||||
tags := p.ListTagByPath(tagPaths...)
|
||||
for _, v := range tags {
|
||||
tagIds = append(tagIds, v.Id)
|
||||
}
|
||||
return tagIds
|
||||
}
|
||||
|
||||
func (p *tagTreeAppImpl) ListTagByAccountId(accountId uint64) []string {
|
||||
return p.tagTreeTeamRepo.SelectTagPathsByAccountId(accountId)
|
||||
}
|
||||
|
||||
func (p *tagTreeAppImpl) ListTagByAccountIdAndResource(accountId uint64, entity any) []string {
|
||||
var res []string
|
||||
|
||||
tagIds := p.ListTagIdByAccountId(accountId)
|
||||
if len(tagIds) == 0 {
|
||||
return res
|
||||
func (p *tagTreeAppImpl) CanAccess(accountId uint64, tagPath ...string) error {
|
||||
if accountId == consts.AdminId {
|
||||
return nil
|
||||
}
|
||||
|
||||
global.Db.Model(entity).Distinct("tag_path").Where("tag_id in ?", tagIds).Scopes(gormx.UndeleteScope).Order("tag_path asc").Find(&res)
|
||||
return res
|
||||
}
|
||||
|
||||
func (p *tagTreeAppImpl) CanAccess(accountId uint64, tagPath string) error {
|
||||
tagPaths := p.ListTagByAccountId(accountId)
|
||||
// 判断该资源标签是否为该账号拥有的标签或其子标签
|
||||
for _, v := range tagPaths {
|
||||
if strings.HasPrefix(tagPath, v) {
|
||||
return nil
|
||||
for _, tp := range tagPath {
|
||||
if strings.HasPrefix(tp, v) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,21 +229,23 @@ func (p *tagTreeAppImpl) CanAccess(accountId uint64, tagPath string) error {
|
||||
}
|
||||
|
||||
func (p *tagTreeAppImpl) Delete(ctx context.Context, id uint64) error {
|
||||
tagIds := [1]uint64{id}
|
||||
if p.machineApp.Count(&machineentity.MachineQuery{TagIds: tagIds[:]}) > 0 {
|
||||
return errorx.NewBiz("请先删除该标签关联的机器信息")
|
||||
accountId := contextx.GetLoginAccount(ctx).Id
|
||||
tag, err := p.GetById(new(entity.TagTree), id)
|
||||
if err != nil {
|
||||
return errorx.NewBiz("该标签不存在")
|
||||
}
|
||||
if p.redisApp.Count(&redisentity.RedisQuery{TagIds: tagIds[:]}) > 0 {
|
||||
return errorx.NewBiz("请先删除该标签关联的redis信息")
|
||||
}
|
||||
if p.dbApp.Count(&dbentity.DbQuery{TagIds: tagIds[:]}) > 0 {
|
||||
return errorx.NewBiz("请先删除该标签关联的数据库信息")
|
||||
}
|
||||
if p.mongoApp.Count(&mongoentity.MongoQuery{TagIds: tagIds[:]}) > 0 {
|
||||
return errorx.NewBiz("请先删除该标签关联的Mongo信息")
|
||||
if err := p.CanAccess(accountId, tag.CodePath); err != nil {
|
||||
return errorx.NewBiz("您无权删除该标签")
|
||||
}
|
||||
|
||||
p.DeleteById(ctx, id)
|
||||
// 删除该标签关联的团队信息
|
||||
return p.tagTreeTeamRepo.DeleteByCond(ctx, &entity.TagTreeTeam{TagId: id})
|
||||
if p.tagResourceApp.CountByCond(&entity.TagResource{TagId: id}) > 0 {
|
||||
return errorx.NewBiz("请先移除该标签关联的资源")
|
||||
}
|
||||
|
||||
return p.Tx(ctx, func(ctx context.Context) error {
|
||||
return p.DeleteById(ctx, id)
|
||||
}, func(ctx context.Context) error {
|
||||
// 删除该标签关联的团队信息
|
||||
return p.tagTreeTeamRepo.DeleteByCond(ctx, &entity.TagTreeTeam{TagId: id})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -13,3 +13,16 @@ type TagTreeQuery struct {
|
||||
CodePathLike string // 标识符路径模糊查询
|
||||
CodePathLikes []string
|
||||
}
|
||||
|
||||
type TagResourceQuery struct {
|
||||
model.Model
|
||||
|
||||
TagPath string `json:"string"` // 标签路径
|
||||
TagId uint64 `json:"tagId" form:"tagId"`
|
||||
ResourceType int8 `json:"resourceType" form:"resourceType"` // 资源编码
|
||||
ResourceCode string `json:"resourceCode" form:"resourceCode"` // 资源编码
|
||||
ResourceCodes []string // 资源编码列表
|
||||
|
||||
TagPathLike string // 标签路径模糊查询
|
||||
TagPathLikes []string
|
||||
}
|
||||
|
||||
15
server/internal/tag/domain/entity/tag_resource.go
Normal file
15
server/internal/tag/domain/entity/tag_resource.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package entity
|
||||
|
||||
import (
|
||||
"mayfly-go/pkg/model"
|
||||
)
|
||||
|
||||
// 标签资源关联
|
||||
type TagResource struct {
|
||||
model.Model
|
||||
|
||||
TagId uint64 `json:"tagId"`
|
||||
TagPath string `json:"tagPath"` // 标签路径
|
||||
ResourceCode string `json:"resourceCode"` // 资源标识
|
||||
ResourceType int8 `json:"resourceType"` // 资源类型
|
||||
}
|
||||
12
server/internal/tag/domain/repository/tag_resource.go
Normal file
12
server/internal/tag/domain/repository/tag_resource.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/tag/domain/entity"
|
||||
"mayfly-go/pkg/base"
|
||||
)
|
||||
|
||||
type TagResource interface {
|
||||
base.Repo[*entity.TagResource]
|
||||
|
||||
SelectByCondition(condition *entity.TagResourceQuery, toEntity any, orderBy ...string)
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import "mayfly-go/internal/tag/domain/repository"
|
||||
var (
|
||||
tagTreeRepo repository.TagTree = newTagTreeRepo()
|
||||
tagTreeTeamRepo repository.TagTreeTeam = newTagTreeTeamRepo()
|
||||
tagResourceRepo repository.TagResource = newTagResourceRepo()
|
||||
teamRepo repository.Team = newTeamRepo()
|
||||
teamMemberRepo repository.TeamMember = newTeamMemberRepo()
|
||||
)
|
||||
@@ -17,6 +18,10 @@ func GetTagTreeTeamRepo() repository.TagTreeTeam {
|
||||
return tagTreeTeamRepo
|
||||
}
|
||||
|
||||
func GetTagResourceRepo() repository.TagResource {
|
||||
return tagResourceRepo
|
||||
}
|
||||
|
||||
func GetTeamRepo() repository.Team {
|
||||
return teamRepo
|
||||
}
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
package persistence
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/tag/domain/entity"
|
||||
"mayfly-go/internal/tag/domain/repository"
|
||||
"mayfly-go/pkg/base"
|
||||
"mayfly-go/pkg/gormx"
|
||||
)
|
||||
|
||||
type tagResourceRepoImpl struct {
|
||||
base.RepoImpl[*entity.TagResource]
|
||||
}
|
||||
|
||||
func newTagResourceRepo() repository.TagResource {
|
||||
return &tagResourceRepoImpl{base.RepoImpl[*entity.TagResource]{M: new(entity.TagResource)}}
|
||||
}
|
||||
|
||||
func (p *tagResourceRepoImpl) SelectByCondition(condition *entity.TagResourceQuery, toEntity any, orderBy ...string) {
|
||||
sql := "SELECT tr.resource_type, tr.resource_code, tr.tag_id, tr.tag_path FROM t_tag_resource tr WHERE tr.is_deleted = 0 "
|
||||
|
||||
params := make([]any, 0)
|
||||
|
||||
if condition.ResourceType != 0 {
|
||||
sql = sql + " AND tr.resource_type = ?"
|
||||
params = append(params, condition.ResourceType)
|
||||
}
|
||||
|
||||
if condition.ResourceCode != "" {
|
||||
sql = sql + " AND tr.resource_code = ?"
|
||||
params = append(params, condition.ResourceCode)
|
||||
}
|
||||
|
||||
if len(condition.ResourceCodes) > 0 {
|
||||
sql = sql + " AND tr.resource_code IN (?)"
|
||||
params = append(params, condition.ResourceCodes)
|
||||
}
|
||||
|
||||
if condition.TagId != 0 {
|
||||
sql = sql + " AND tr.tag_id = ?"
|
||||
params = append(params, condition.TagId)
|
||||
}
|
||||
if condition.TagPath != "" {
|
||||
sql = sql + " AND tr.tag_path = ?"
|
||||
params = append(params, condition.TagPath)
|
||||
}
|
||||
if condition.TagPathLike != "" {
|
||||
sql = sql + " AND tr.tag_path LIKE ?"
|
||||
params = append(params, condition.TagPathLike+"%")
|
||||
}
|
||||
if len(condition.TagPathLikes) > 0 {
|
||||
sql = sql + " AND ("
|
||||
for i, v := range condition.TagPathLikes {
|
||||
if i == 0 {
|
||||
sql = sql + "tr.tag_path LIKE ?"
|
||||
} else {
|
||||
sql = sql + " OR tr.tag_path LIKE ?"
|
||||
}
|
||||
params = append(params, v+"%")
|
||||
}
|
||||
sql = sql + ")"
|
||||
}
|
||||
|
||||
sql = sql + " ORDER BY tr.tag_path"
|
||||
gormx.GetListBySql2Model(sql, toEntity, params...)
|
||||
}
|
||||
@@ -1,12 +1,10 @@
|
||||
package persistence
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"mayfly-go/internal/tag/domain/entity"
|
||||
"mayfly-go/internal/tag/domain/repository"
|
||||
"mayfly-go/pkg/base"
|
||||
"mayfly-go/pkg/gormx"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type tagTreeRepoImpl struct {
|
||||
@@ -19,37 +17,40 @@ func newTagTreeRepo() repository.TagTree {
|
||||
|
||||
func (p *tagTreeRepoImpl) SelectByCondition(condition *entity.TagTreeQuery, toEntity any, orderBy ...string) {
|
||||
sql := "SELECT DISTINCT(p.id), p.pid, p.code, p.code_path, p.name, p.remark, p.create_time, p.creator, p.update_time, p.modifier FROM t_tag_tree p WHERE p.is_deleted = 0 "
|
||||
|
||||
params := make([]any, 0)
|
||||
if condition.Name != "" {
|
||||
sql = sql + " AND p.name LIKE '%" + condition.Name + "%'"
|
||||
sql = sql + " AND p.name LIKE ?"
|
||||
params = append(params, "%"+condition.Name+"%")
|
||||
}
|
||||
if condition.CodePath != "" {
|
||||
sql = fmt.Sprintf("%s AND p.code_path = '%s'", sql, condition.CodePath)
|
||||
sql = sql + " AND p.code_path = ?"
|
||||
params = append(params, condition.CodePath)
|
||||
}
|
||||
if len(condition.CodePaths) > 0 {
|
||||
strCodePaths := make([]string, 0)
|
||||
// 将字符串用''包裹
|
||||
for _, v := range condition.CodePaths {
|
||||
strCodePaths = append(strCodePaths, fmt.Sprintf("'%s'", v))
|
||||
}
|
||||
sql = fmt.Sprintf("%s AND p.code_path IN (%s)", sql, strings.Join(strCodePaths, ","))
|
||||
sql = sql + " AND p.code_path IN (?)"
|
||||
params = append(params, condition.CodePaths)
|
||||
}
|
||||
if condition.CodePathLike != "" {
|
||||
sql = fmt.Sprintf("%s AND p.code_path LIKE '%s'", sql, condition.CodePathLike+"%")
|
||||
sql = sql + " AND p.code_path LIKE ?"
|
||||
params = append(params, condition.CodePathLike+"%")
|
||||
}
|
||||
if condition.Pid != 0 {
|
||||
sql = fmt.Sprintf("%s AND p.pid = %d ", sql, condition.Pid)
|
||||
sql = sql + " AND p.pid = ?"
|
||||
params = append(params, condition.Pid)
|
||||
}
|
||||
if len(condition.CodePathLikes) > 0 {
|
||||
sql = sql + " AND ("
|
||||
for i, v := range condition.CodePathLikes {
|
||||
if i == 0 {
|
||||
sql = sql + fmt.Sprintf("p.code_path LIKE '%s'", v+"%")
|
||||
sql = sql + "p.code_path LIKE ?"
|
||||
} else {
|
||||
sql = sql + fmt.Sprintf(" OR p.code_path LIKE '%s'", v+"%")
|
||||
sql = sql + " OR p.code_path LIKE ?"
|
||||
}
|
||||
params = append(params, v+"%")
|
||||
}
|
||||
sql = sql + ")"
|
||||
}
|
||||
sql = sql + " ORDER BY p.code_path"
|
||||
gormx.GetListBySql2Model(sql, toEntity)
|
||||
gormx.GetListBySql2Model(sql, toEntity, params...)
|
||||
}
|
||||
|
||||
@@ -10,7 +10,8 @@ import (
|
||||
|
||||
func InitTagTreeRouter(router *gin.RouterGroup) {
|
||||
m := &api.TagTree{
|
||||
TagTreeApp: application.GetTagTreeApp(),
|
||||
TagTreeApp: application.GetTagTreeApp(),
|
||||
TagResourceApp: application.GetTagResourceApp(),
|
||||
}
|
||||
|
||||
tagTree := router.Group("/tag-trees")
|
||||
@@ -22,12 +23,13 @@ func InitTagTreeRouter(router *gin.RouterGroup) {
|
||||
// 根据条件获取标签
|
||||
req.NewGet("query", m.ListByQuery),
|
||||
|
||||
// 获取登录账号拥有的标签信息
|
||||
req.NewGet("account-has", m.GetAccountTags),
|
||||
|
||||
req.NewPost("", m.SaveTagTree).Log(req.NewLogSave("标签树-保存信息")).RequiredPermissionCode("tag:save"),
|
||||
|
||||
req.NewDelete(":id", m.DelTagTree).Log(req.NewLogSave("标签树-删除信息")).RequiredPermissionCode("tag:del"),
|
||||
|
||||
req.NewGet("/resources/:rtype/tag-paths", m.TagResources),
|
||||
|
||||
req.NewGet("/resources", m.QueryTagResources),
|
||||
}
|
||||
|
||||
req.BatchSetGroup(tagTree, reqs[:])
|
||||
|
||||
@@ -2,6 +2,9 @@ package base
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"mayfly-go/pkg/contextx"
|
||||
"mayfly-go/pkg/global"
|
||||
"mayfly-go/pkg/model"
|
||||
|
||||
"gorm.io/gorm"
|
||||
@@ -62,6 +65,9 @@ type App[T model.ModelI] interface {
|
||||
|
||||
// 根据指定条件统计model表的数量, cond为条件可以为map等
|
||||
CountByCond(cond any) int64
|
||||
|
||||
// 执行事务操作
|
||||
Tx(ctx context.Context, funcs ...func(context.Context) error) (err error)
|
||||
}
|
||||
|
||||
// 基础application接口实现
|
||||
@@ -162,3 +168,28 @@ func (ai *AppImpl[T, R]) ListByCondOrder(cond any, list any, order ...string) er
|
||||
func (ai *AppImpl[T, R]) CountByCond(cond any) int64 {
|
||||
return ai.GetRepo().CountByCond(cond)
|
||||
}
|
||||
|
||||
// 执行事务操作
|
||||
func (ai *AppImpl[T, R]) Tx(ctx context.Context, funcs ...func(context.Context) error) (err error) {
|
||||
tx := global.Db.Begin()
|
||||
dbCtx := contextx.WithDb(ctx, tx)
|
||||
|
||||
defer func() {
|
||||
// 移除当前已执行完成的的数据库事务实例
|
||||
contextx.RmDb(ctx)
|
||||
if r := recover(); r != nil {
|
||||
tx.Rollback()
|
||||
err = fmt.Errorf("%v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
for _, f := range funcs {
|
||||
err = f(dbCtx)
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
return
|
||||
}
|
||||
}
|
||||
err = tx.Commit().Error
|
||||
return
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"mayfly-go/pkg/contextx"
|
||||
"mayfly-go/pkg/gormx"
|
||||
"mayfly-go/pkg/model"
|
||||
"mayfly-go/pkg/utils/anyx"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
@@ -70,10 +71,13 @@ type Repo[T model.ModelI] interface {
|
||||
|
||||
// 基础repo接口
|
||||
type RepoImpl[T model.ModelI] struct {
|
||||
M any // 模型实例
|
||||
M T // 模型实例
|
||||
}
|
||||
|
||||
func (br *RepoImpl[T]) Insert(ctx context.Context, e T) error {
|
||||
if db := contextx.GetDb(ctx); db != nil {
|
||||
return br.InsertWithDb(ctx, db, e)
|
||||
}
|
||||
return gormx.Insert(br.setBaseInfo(ctx, e))
|
||||
}
|
||||
|
||||
@@ -82,6 +86,10 @@ func (br *RepoImpl[T]) InsertWithDb(ctx context.Context, db *gorm.DB, e T) error
|
||||
}
|
||||
|
||||
func (br *RepoImpl[T]) BatchInsert(ctx context.Context, es []T) error {
|
||||
if db := contextx.GetDb(ctx); db != nil {
|
||||
return br.BatchInsertWithDb(ctx, db, es)
|
||||
}
|
||||
|
||||
for _, e := range es {
|
||||
br.setBaseInfo(ctx, e)
|
||||
}
|
||||
@@ -97,6 +105,10 @@ func (br *RepoImpl[T]) BatchInsertWithDb(ctx context.Context, db *gorm.DB, es []
|
||||
}
|
||||
|
||||
func (br *RepoImpl[T]) UpdateById(ctx context.Context, e T) error {
|
||||
if db := contextx.GetDb(ctx); db != nil {
|
||||
return br.UpdateByIdWithDb(ctx, db, e)
|
||||
}
|
||||
|
||||
return gormx.UpdateById(br.setBaseInfo(ctx, e))
|
||||
}
|
||||
|
||||
@@ -109,6 +121,10 @@ func (br *RepoImpl[T]) Updates(cond any, udpateFields map[string]any) error {
|
||||
}
|
||||
|
||||
func (br *RepoImpl[T]) DeleteById(ctx context.Context, id uint64) error {
|
||||
if db := contextx.GetDb(ctx); db != nil {
|
||||
return br.DeleteByIdWithDb(ctx, db, id)
|
||||
}
|
||||
|
||||
return gormx.DeleteById(br.getModel(), id)
|
||||
}
|
||||
|
||||
@@ -117,6 +133,10 @@ func (br *RepoImpl[T]) DeleteByIdWithDb(ctx context.Context, db *gorm.DB, id uin
|
||||
}
|
||||
|
||||
func (br *RepoImpl[T]) DeleteByCond(ctx context.Context, cond any) error {
|
||||
if db := contextx.GetDb(ctx); db != nil {
|
||||
return br.DeleteByCondWithDb(ctx, db, cond)
|
||||
}
|
||||
|
||||
return gormx.DeleteByCond(br.getModel(), cond)
|
||||
}
|
||||
|
||||
@@ -152,8 +172,8 @@ func (br *RepoImpl[T]) CountByCond(cond any) int64 {
|
||||
}
|
||||
|
||||
// 获取表的模型实例
|
||||
func (br *RepoImpl[T]) getModel() any {
|
||||
biz.IsTrue(br.M != nil, "base.RepoImpl的M字段不能为空")
|
||||
func (br *RepoImpl[T]) getModel() T {
|
||||
biz.IsTrue(!anyx.IsBlank(br.M), "base.RepoImpl的M字段不能为空")
|
||||
return br.M
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import "fmt"
|
||||
|
||||
const (
|
||||
AppName = "mayfly-go"
|
||||
Version = "v1.5.4"
|
||||
Version = "v1.6.0"
|
||||
)
|
||||
|
||||
func GetAppInfo() string {
|
||||
|
||||
@@ -3,7 +3,10 @@ package contextx
|
||||
import (
|
||||
"context"
|
||||
"mayfly-go/pkg/model"
|
||||
"mayfly-go/pkg/utils/collx"
|
||||
"mayfly-go/pkg/utils/stringx"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type CtxKey string
|
||||
@@ -11,6 +14,7 @@ type CtxKey string
|
||||
const (
|
||||
LoginAccountKey CtxKey = "loginAccount"
|
||||
TraceIdKey CtxKey = "traceId"
|
||||
DbKey CtxKey = "db"
|
||||
)
|
||||
|
||||
func NewLoginAccount(la *model.LoginAccount) context.Context {
|
||||
@@ -44,3 +48,30 @@ func GetTraceId(ctx context.Context) string {
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// 将事务db放置context中,使用stack保存。以便多个方法调用实现方法内部各自的事务操作
|
||||
func WithDb(ctx context.Context, db *gorm.DB) context.Context {
|
||||
if dbStack, ok := ctx.Value(DbKey).(*collx.Stack[*gorm.DB]); ok {
|
||||
dbStack.Push(db)
|
||||
return ctx
|
||||
}
|
||||
dbStack := new(collx.Stack[*gorm.DB])
|
||||
dbStack.Push(db)
|
||||
|
||||
return context.WithValue(ctx, DbKey, dbStack)
|
||||
}
|
||||
|
||||
// 获取当前操作的栈顶事务数据库实例
|
||||
func GetDb(ctx context.Context) *gorm.DB {
|
||||
if dbStack, ok := ctx.Value(DbKey).(*collx.Stack[*gorm.DB]); ok {
|
||||
return dbStack.Top()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func RmDb(ctx context.Context) *gorm.DB {
|
||||
if dbStack, ok := ctx.Value(DbKey).(*collx.Stack[*gorm.DB]); ok {
|
||||
return dbStack.Pop()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -103,12 +103,8 @@ func ErrorRes(g *gin.Context, err any) {
|
||||
switch t := err.(type) {
|
||||
case errorx.BizError:
|
||||
g.JSON(http.StatusOK, model.Error(t))
|
||||
case error:
|
||||
g.JSON(http.StatusOK, model.ServerError())
|
||||
case string:
|
||||
g.JSON(http.StatusOK, model.ServerError())
|
||||
default:
|
||||
logx.Errorf("未知错误: %v", t)
|
||||
logx.ErrorTrace("服务器错误", t)
|
||||
g.JSON(http.StatusOK, model.ServerError())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,8 +106,17 @@ func Errorf(format string, args ...any) {
|
||||
}
|
||||
|
||||
// 错误记录,并将堆栈信息添加至msg里,默认记录10个堆栈信息
|
||||
func ErrorTrace(msg string, err error) {
|
||||
Log(context.Background(), slog.LevelError, fmt.Sprintf(msg+" %s\n%s", err.Error(), runtimex.StatckStr(2, 10)))
|
||||
func ErrorTrace(msg string, err any) {
|
||||
errMsg := ""
|
||||
switch t := err.(type) {
|
||||
case error:
|
||||
errMsg = t.Error()
|
||||
case string:
|
||||
errMsg = t
|
||||
default:
|
||||
errMsg = fmt.Sprintf("%v", t)
|
||||
}
|
||||
Log(context.Background(), slog.LevelError, fmt.Sprintf(msg+"\n%s\n%s", errMsg, runtimex.StatckStr(2, 10)))
|
||||
}
|
||||
|
||||
func ErrorWithFields(ctx context.Context, msg string, mapFields map[string]any) {
|
||||
|
||||
@@ -5,11 +5,12 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
IdColumn = "id"
|
||||
DeletedColumn = "is_deleted" // 删除字段
|
||||
DeleteTimeColumn = "delete_time"
|
||||
ModelDeleted int8 = 1
|
||||
ModelUndeleted int8 = 0
|
||||
IdColumn = "id"
|
||||
DeletedColumn = "is_deleted" // 删除字段
|
||||
DeleteTimeColumn = "delete_time"
|
||||
|
||||
ModelDeleted int8 = 1
|
||||
ModelUndeleted int8 = 0
|
||||
)
|
||||
|
||||
// 实体接口
|
||||
|
||||
@@ -21,6 +21,7 @@ type Ctx struct {
|
||||
|
||||
GinCtx *gin.Context // gin context
|
||||
ReqParam any // 请求参数,主要用于记录日志
|
||||
LogExtra any // 日志额外参数,主要用于系统日志定制化展示
|
||||
ResData any // 响应结果
|
||||
Err any // 请求错误
|
||||
timed int64 // 执行时间
|
||||
|
||||
41
server/pkg/utils/collx/stack.go
Normal file
41
server/pkg/utils/collx/stack.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package collx
|
||||
|
||||
type Stack[T any] struct {
|
||||
items []T
|
||||
}
|
||||
|
||||
// 入栈
|
||||
func (s *Stack[T]) Push(item T) {
|
||||
s.items = append(s.items, item)
|
||||
}
|
||||
|
||||
// 出栈
|
||||
func (s *Stack[T]) Pop() T {
|
||||
var item T
|
||||
if len(s.items) == 0 {
|
||||
return item
|
||||
}
|
||||
lastIndex := len(s.items) - 1
|
||||
item = s.items[lastIndex]
|
||||
s.items = s.items[:lastIndex]
|
||||
return item
|
||||
}
|
||||
|
||||
// 获取栈顶元素
|
||||
func (s *Stack[T]) Top() T {
|
||||
var item T
|
||||
if len(s.items) == 0 {
|
||||
return item
|
||||
}
|
||||
return s.items[len(s.items)-1]
|
||||
}
|
||||
|
||||
// 检查栈是否为空
|
||||
func (s *Stack[T]) IsEmpty() bool {
|
||||
return len(s.items) == 0
|
||||
}
|
||||
|
||||
// 返回栈的大小
|
||||
func (s *Stack[T]) Size() int {
|
||||
return len(s.items)
|
||||
}
|
||||
Binary file not shown.
@@ -43,8 +43,6 @@ CREATE TABLE `t_db` (
|
||||
`name` varchar(32) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '数据库实例名称',
|
||||
`database` varchar(1000) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '数据库,空格分割多个数据库',
|
||||
`remark` varchar(125) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '备注,描述等',
|
||||
`tag_id` bigint(20) DEFAULT NULL COMMENT '标签id',
|
||||
`tag_path` varchar(255) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '标签路径',
|
||||
`instance_id` bigint(20) NOT NULL COMMENT '数据库实例 ID',
|
||||
`create_time` datetime DEFAULT NULL,
|
||||
`creator_id` bigint(20) DEFAULT NULL,
|
||||
@@ -157,8 +155,6 @@ CREATE TABLE `t_machine` (
|
||||
`enable_recorder` tinyint(2) DEFAULT NULL COMMENT '是否启用终端回放记录',
|
||||
`status` tinyint(2) NOT NULL COMMENT '状态: 1:启用; -1:禁用',
|
||||
`remark` varchar(255) DEFAULT NULL,
|
||||
`tag_id` bigint(20) DEFAULT NULL COMMENT '标签id',
|
||||
`tag_path` varchar(255) DEFAULT NULL COMMENT '标签路径',
|
||||
`need_monitor` tinyint(2) DEFAULT NULL,
|
||||
`create_time` datetime NOT NULL,
|
||||
`creator` varchar(16) DEFAULT NULL,
|
||||
@@ -168,8 +164,7 @@ CREATE TABLE `t_machine` (
|
||||
`modifier_id` bigint(32) DEFAULT NULL,
|
||||
`is_deleted` tinyint(8) NOT NULL DEFAULT 0,
|
||||
`delete_time` datetime DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_path` (`tag_path`) USING BTREE
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='机器信息';
|
||||
|
||||
-- ----------------------------
|
||||
@@ -309,6 +304,21 @@ CREATE TABLE `t_machine_cron_job_relate` (
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='机器计划任务关联表';
|
||||
|
||||
DROP TABLE IF EXISTS `t_machine_term_op`;
|
||||
CREATE TABLE `t_machine_term_op` (
|
||||
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'id',
|
||||
`machine_id` bigint NOT NULL COMMENT '机器id',
|
||||
`username` varchar(60) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '登录用户名',
|
||||
`record_file_path` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '终端回放文件路径',
|
||||
`creator_id` bigint unsigned DEFAULT NULL,
|
||||
`creator` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL,
|
||||
`create_time` datetime NOT NULL,
|
||||
`end_time` datetime DEFAULT NULL,
|
||||
`is_deleted` tinyint DEFAULT '0',
|
||||
`delete_time` datetime DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='机器终端操作记录表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for t_mongo
|
||||
-- ----------------------------
|
||||
@@ -318,8 +328,6 @@ CREATE TABLE `t_mongo` (
|
||||
`name` varchar(36) NOT NULL COMMENT '名称',
|
||||
`uri` varchar(255) NOT NULL COMMENT '连接uri',
|
||||
`ssh_tunnel_machine_id` bigint(20) DEFAULT NULL COMMENT 'ssh隧道的机器id',
|
||||
`tag_id` bigint(20) DEFAULT NULL COMMENT '标签id',
|
||||
`tag_path` varchar(255) DEFAULT NULL COMMENT '标签路径',
|
||||
`create_time` datetime NOT NULL,
|
||||
`creator_id` bigint(20) DEFAULT NULL,
|
||||
`creator` varchar(36) DEFAULT NULL,
|
||||
@@ -351,8 +359,6 @@ CREATE TABLE `t_redis` (
|
||||
`mode` varchar(32) DEFAULT NULL,
|
||||
`ssh_tunnel_machine_id` bigint(20) DEFAULT NULL COMMENT 'ssh隧道的机器id',
|
||||
`remark` varchar(125) DEFAULT NULL,
|
||||
`tag_id` bigint(20) DEFAULT NULL COMMENT '标签id',
|
||||
`tag_path` varchar(255) DEFAULT NULL COMMENT '标签路径',
|
||||
`creator` varchar(32) DEFAULT NULL,
|
||||
`creator_id` bigint(32) DEFAULT NULL,
|
||||
`create_time` datetime DEFAULT NULL,
|
||||
@@ -361,8 +367,7 @@ CREATE TABLE `t_redis` (
|
||||
`update_time` datetime DEFAULT NULL,
|
||||
`is_deleted` tinyint(8) NOT NULL DEFAULT 0,
|
||||
`delete_time` datetime DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_tag_path` (`tag_path`)
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='redis信息';
|
||||
|
||||
-- ----------------------------
|
||||
@@ -763,6 +768,26 @@ CREATE TABLE `t_tag_tree_team` (
|
||||
KEY `idx_tag_id` (`tag_id`) USING BTREE
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=32 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='标签树团队关联信息';
|
||||
|
||||
DROP TABLE IF EXISTS `t_tag_resource`;
|
||||
CREATE TABLE `t_tag_resource` (
|
||||
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
|
||||
`tag_id` bigint NOT NULL,
|
||||
`tag_path` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL COMMENT '标签路径',
|
||||
`resource_code` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '资源编码',
|
||||
`resource_type` tinyint NOT NULL COMMENT '资源类型',
|
||||
`create_time` datetime NOT NULL,
|
||||
`creator_id` bigint NOT NULL,
|
||||
`creator` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
|
||||
`update_time` datetime NOT NULL,
|
||||
`modifier_id` bigint NOT NULL,
|
||||
`modifier` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
|
||||
`is_deleted` tinyint DEFAULT '0',
|
||||
`delete_time` datetime DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_tag_path` (`tag_path`(100)) USING BTREE,
|
||||
KEY `idx_resource_code` (`resource_code`) USING BTREE
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='标签资源关联表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of t_tag_tree_team
|
||||
-- ----------------------------
|
||||
|
||||
194
server/resources/script/sql/v1.6.0.sql
Normal file
194
server/resources/script/sql/v1.6.0.sql
Normal file
@@ -0,0 +1,194 @@
|
||||
begin;
|
||||
|
||||
DROP TABLE IF EXISTS `t_tag_resource`;
|
||||
CREATE TABLE `t_tag_resource` (
|
||||
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
|
||||
`tag_id` bigint NOT NULL,
|
||||
`tag_path` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL COMMENT '标签路径',
|
||||
`resource_code` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '资源编码',
|
||||
`resource_type` tinyint NOT NULL COMMENT '资源类型',
|
||||
`create_time` datetime NOT NULL,
|
||||
`creator_id` bigint NOT NULL,
|
||||
`creator` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
|
||||
`update_time` datetime NOT NULL,
|
||||
`modifier_id` bigint NOT NULL,
|
||||
`modifier` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
|
||||
`is_deleted` tinyint DEFAULT '0',
|
||||
`delete_time` datetime DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_tag_path` (`tag_path`(100)) USING BTREE,
|
||||
KEY `idx_resource_code` (`resource_code`) USING BTREE
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='标签资源关联表';
|
||||
|
||||
ALTER TABLE t_machine ADD COLUMN code varchar(32) NULL AFTER id;
|
||||
CREATE INDEX idx_code USING BTREE ON t_machine (code);
|
||||
UPDATE t_machine SET code = id;
|
||||
INSERT
|
||||
INTO
|
||||
t_tag_resource (`tag_id`,
|
||||
`tag_path`,
|
||||
`resource_code`,
|
||||
`resource_type`,
|
||||
`create_time`,
|
||||
`creator_id`,
|
||||
`creator`,
|
||||
`update_time`,
|
||||
`modifier_id`,
|
||||
`modifier`,
|
||||
`is_deleted`,
|
||||
`delete_time`)
|
||||
SELECT
|
||||
`tag_id`,
|
||||
`tag_path`,
|
||||
`code`,
|
||||
"1",
|
||||
'2023-08-30 15:04:07',
|
||||
1,
|
||||
'admin',
|
||||
'2023-08-30 15:04:07',
|
||||
1,
|
||||
'admin',
|
||||
0,
|
||||
NULL
|
||||
FROM
|
||||
t_machine
|
||||
WHERE
|
||||
is_deleted = 0;
|
||||
|
||||
|
||||
ALTER TABLE t_db ADD COLUMN code varchar(32) NULL AFTER id;
|
||||
CREATE INDEX idx_code USING BTREE ON t_db (code);
|
||||
UPDATE t_db SET code = id;
|
||||
INSERT
|
||||
INTO
|
||||
t_tag_resource (`tag_id`,
|
||||
`tag_path`,
|
||||
`resource_code`,
|
||||
`resource_type`,
|
||||
`create_time`,
|
||||
`creator_id`,
|
||||
`creator`,
|
||||
`update_time`,
|
||||
`modifier_id`,
|
||||
`modifier`,
|
||||
`is_deleted`,
|
||||
`delete_time`)
|
||||
SELECT
|
||||
`tag_id`,
|
||||
`tag_path`,
|
||||
`code`,
|
||||
"2",
|
||||
'2023-08-30 15:04:07',
|
||||
1,
|
||||
'admin',
|
||||
'2023-08-30 15:04:07',
|
||||
1,
|
||||
'admin',
|
||||
0,
|
||||
NULL
|
||||
FROM
|
||||
t_db
|
||||
WHERE
|
||||
is_deleted = 0;
|
||||
|
||||
|
||||
ALTER TABLE t_redis ADD COLUMN code varchar(32) NULL AFTER id;
|
||||
CREATE INDEX idx_code USING BTREE ON t_redis (code);
|
||||
UPDATE t_redis SET code = id;
|
||||
INSERT
|
||||
INTO
|
||||
t_tag_resource (`tag_id`,
|
||||
`tag_path`,
|
||||
`resource_code`,
|
||||
`resource_type`,
|
||||
`create_time`,
|
||||
`creator_id`,
|
||||
`creator`,
|
||||
`update_time`,
|
||||
`modifier_id`,
|
||||
`modifier`,
|
||||
`is_deleted`,
|
||||
`delete_time`)
|
||||
SELECT
|
||||
`tag_id`,
|
||||
`tag_path`,
|
||||
`code`,
|
||||
"3",
|
||||
'2023-08-30 15:04:07',
|
||||
1,
|
||||
'admin',
|
||||
'2023-08-30 15:04:07',
|
||||
1,
|
||||
'admin',
|
||||
0,
|
||||
NULL
|
||||
FROM
|
||||
t_redis
|
||||
WHERE
|
||||
is_deleted = 0;
|
||||
|
||||
|
||||
ALTER TABLE t_mongo ADD COLUMN code varchar(32) NULL AFTER id;
|
||||
CREATE INDEX idx_code USING BTREE ON t_mongo (code);
|
||||
UPDATE t_mongo SET code = id;
|
||||
INSERT
|
||||
INTO
|
||||
t_tag_resource (`tag_id`,
|
||||
`tag_path`,
|
||||
`resource_code`,
|
||||
`resource_type`,
|
||||
`create_time`,
|
||||
`creator_id`,
|
||||
`creator`,
|
||||
`update_time`,
|
||||
`modifier_id`,
|
||||
`modifier`,
|
||||
`is_deleted`,
|
||||
`delete_time`)
|
||||
SELECT
|
||||
`tag_id`,
|
||||
`tag_path`,
|
||||
`code`,
|
||||
"4",
|
||||
'2023-08-30 15:04:07',
|
||||
1,
|
||||
'admin',
|
||||
'2023-08-30 15:04:07',
|
||||
1,
|
||||
'admin',
|
||||
0,
|
||||
NULL
|
||||
FROM
|
||||
t_mongo
|
||||
WHERE
|
||||
is_deleted = 0;
|
||||
|
||||
ALTER TABLE t_machine DROP COLUMN tag_id;
|
||||
ALTER TABLE t_machine DROP COLUMN tag_path;
|
||||
|
||||
ALTER TABLE t_db DROP COLUMN tag_id;
|
||||
ALTER TABLE t_db DROP COLUMN tag_path;
|
||||
|
||||
ALTER TABLE t_redis DROP COLUMN tag_id;
|
||||
ALTER TABLE t_redis DROP COLUMN tag_path;
|
||||
|
||||
ALTER TABLE t_mongo DROP COLUMN tag_id;
|
||||
ALTER TABLE t_mongo DROP COLUMN tag_path;
|
||||
|
||||
-- 机器终端操作记录表
|
||||
DROP TABLE IF EXISTS `t_machine_term_op`;
|
||||
CREATE TABLE `t_machine_term_op` (
|
||||
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'id',
|
||||
`machine_id` bigint NOT NULL COMMENT '机器id',
|
||||
`username` varchar(60) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '登录用户名',
|
||||
`record_file_path` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '终端回放文件路径',
|
||||
`creator_id` bigint unsigned DEFAULT NULL,
|
||||
`creator` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL,
|
||||
`create_time` datetime NOT NULL,
|
||||
`end_time` datetime DEFAULT NULL,
|
||||
`is_deleted` tinyint DEFAULT '0',
|
||||
`delete_time` datetime DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='机器终端操作记录表';
|
||||
|
||||
commit;
|
||||
Reference in New Issue
Block a user