Files
mayfly-go/server/internal/redis/application/redis.go

286 lines
8.3 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package application
import (
"context"
"mayfly-go/internal/common/consts"
flowapp "mayfly-go/internal/flow/application"
flowentity "mayfly-go/internal/flow/domain/entity"
"mayfly-go/internal/redis/domain/entity"
"mayfly-go/internal/redis/domain/repository"
"mayfly-go/internal/redis/rdm"
tagapp "mayfly-go/internal/tag/application"
tagentity "mayfly-go/internal/tag/domain/entity"
"mayfly-go/pkg/base"
"mayfly-go/pkg/errorx"
"mayfly-go/pkg/logx"
"mayfly-go/pkg/model"
"mayfly-go/pkg/utils/jsonx"
"mayfly-go/pkg/utils/stringx"
"strconv"
"strings"
"github.com/may-fly/cast"
"github.com/redis/go-redis/v9"
)
type SaveRedisParam struct {
Redis *entity.Redis
AuthCert *tagentity.ResourceAuthCert
TagCodePaths []string
}
type RunCmdParam struct {
Id uint64 `json:"id"`
Db int `json:"db"`
Cmd []any `json:"cmd"`
Remark string
}
type Redis interface {
base.App[*entity.Redis]
flowapp.FlowBizHandler
// 分页获取机器脚本信息列表
GetPageList(condition *entity.RedisQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error)
// 测试连接
TestConn(re *SaveRedisParam) error
SaveRedis(ctx context.Context, param *SaveRedisParam) error
// 删除数据库信息
Delete(ctx context.Context, id uint64) error
// 获取数据库连接实例
// id: 数据库实例id
// db: 库号
GetRedisConn(id uint64, db int) (*rdm.RedisConn, error)
// 执行redis命令
RunCmd(ctx context.Context, redisConn *rdm.RedisConn, cmdParam *RunCmdParam) (any, error)
}
type redisAppImpl struct {
base.AppImpl[*entity.Redis, repository.Redis]
tagApp tagapp.TagTree `inject:"TagTreeApp"`
procdefApp flowapp.Procdef `inject:"ProcdefApp"`
procinstApp flowapp.Procinst `inject:"ProcinstApp"`
resourceAuthCertApp tagapp.ResourceAuthCert `inject:"ResourceAuthCertApp"`
}
// 注入RedisRepo
func (r *redisAppImpl) InjectRedisRepo(repo repository.Redis) {
r.Repo = repo
}
// 分页获取redis列表
func (r *redisAppImpl) GetPageList(condition *entity.RedisQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error) {
return r.GetRepo().GetRedisList(condition, pageParam, toEntity, orderBy...)
}
func (r *redisAppImpl) TestConn(param *SaveRedisParam) error {
db := 0
re := param.Redis
if re.Db != "" {
db = cast.ToInt(strings.Split(re.Db, ",")[0])
}
authCert := param.AuthCert
if authCert.Id != 0 {
// 密文可能被清除,故需要重新获取
authCert, _ = r.resourceAuthCertApp.GetAuthCert(authCert.Name)
} else {
if authCert.CiphertextType == tagentity.AuthCertCiphertextTypePublic {
publicAuthCert, err := r.resourceAuthCertApp.GetAuthCert(authCert.Ciphertext)
if err != nil {
return err
}
authCert = publicAuthCert
}
}
rc, err := re.ToRedisInfo(db, authCert).Conn()
if err != nil {
return err
}
rc.Close()
return nil
}
func (r *redisAppImpl) SaveRedis(ctx context.Context, param *SaveRedisParam) error {
re := param.Redis
tagCodePaths := param.TagCodePaths
// 查找是否存在该库
oldRedis := &entity.Redis{
Host: re.Host,
SshTunnelMachineId: re.SshTunnelMachineId,
}
err := r.GetByCond(oldRedis)
if re.Id == 0 {
if err == nil {
return errorx.NewBiz("该实例已存在")
}
if r.CountByCond(&entity.Redis{Code: re.Code}) > 0 {
return errorx.NewBiz("该编码已存在")
}
return r.Tx(ctx, func(ctx context.Context) error {
return r.Insert(ctx, re)
}, func(ctx context.Context) error {
return r.tagApp.SaveResourceTag(ctx, &tagapp.SaveResourceTagParam{
ResourceTag: &tagapp.ResourceTag{
Type: tagentity.TagTypeRedis,
Code: re.Code,
Name: re.Name,
},
ParentTagCodePaths: tagCodePaths,
})
}, func(ctx context.Context) error {
return r.resourceAuthCertApp.RelateAuthCert(ctx, &tagapp.RelateAuthCertParam{
ResourceCode: re.Code,
ResourceType: tagentity.TagTypeRedis,
AuthCerts: []*tagentity.ResourceAuthCert{param.AuthCert},
})
})
}
// 如果存在该库,则校验修改的库是否为该库
if err == nil && oldRedis.Id != re.Id {
return errorx.NewBiz("该实例已存在")
}
// 如果修改了redis实例的库信息则关闭旧库的连接
if oldRedis.Db != re.Db || oldRedis.SshTunnelMachineId != re.SshTunnelMachineId {
for _, dbStr := range strings.Split(oldRedis.Db, ",") {
db, _ := strconv.Atoi(dbStr)
rdm.CloseConn(re.Id, db)
}
}
// 如果调整了ssh等会查不到旧数据故需要根据id获取旧信息将code赋值给标签进行关联
if oldRedis.Code == "" {
oldRedis, _ = r.GetById(re.Id)
}
re.Code = ""
return r.Tx(ctx, func(ctx context.Context) error {
return r.UpdateById(ctx, re)
}, func(ctx context.Context) error {
if oldRedis.Name != re.Name {
if err := r.tagApp.UpdateTagName(ctx, tagentity.TagTypeMachine, oldRedis.Code, re.Name); err != nil {
return err
}
}
return r.tagApp.SaveResourceTag(ctx, &tagapp.SaveResourceTagParam{
ResourceTag: &tagapp.ResourceTag{
Type: tagentity.TagTypeRedis,
Code: oldRedis.Code,
Name: re.Name,
},
ParentTagCodePaths: tagCodePaths,
})
}, func(ctx context.Context) error {
return r.resourceAuthCertApp.RelateAuthCert(ctx, &tagapp.RelateAuthCertParam{
ResourceCode: oldRedis.Code,
ResourceType: tagentity.TagTypeRedis,
AuthCerts: []*tagentity.ResourceAuthCert{param.AuthCert},
})
})
}
// 删除Redis信息
func (r *redisAppImpl) Delete(ctx context.Context, id uint64) error {
re, err := r.GetById(id)
if err != nil {
return errorx.NewBiz("该redis信息不存在")
}
// 如果存在连接,则关闭所有库连接信息
for _, dbStr := range strings.Split(re.Db, ",") {
db, _ := strconv.Atoi(dbStr)
rdm.CloseConn(re.Id, db)
}
return r.Tx(ctx, func(ctx context.Context) error {
return r.DeleteById(ctx, id)
}, func(ctx context.Context) error {
return r.tagApp.SaveResourceTag(ctx, &tagapp.SaveResourceTagParam{
ResourceTag: &tagapp.ResourceTag{
Type: tagentity.TagTypeRedis,
Code: re.Code,
},
})
}, func(ctx context.Context) error {
return r.resourceAuthCertApp.RelateAuthCert(ctx, &tagapp.RelateAuthCertParam{
ResourceCode: re.Code,
ResourceType: tagentity.TagTypeRedis,
})
})
}
// 获取数据库连接实例
func (r *redisAppImpl) GetRedisConn(id uint64, db int) (*rdm.RedisConn, error) {
return rdm.GetRedisConn(id, db, func() (*rdm.RedisInfo, error) {
// 缓存不存在则回调获取redis信息
re, err := r.GetById(id)
if err != nil {
return nil, errorx.NewBiz("redis信息不存在")
}
authCert, err := r.resourceAuthCertApp.GetResourceAuthCert(tagentity.TagTypeRedis, re.Code)
if err != nil {
return nil, err
}
return re.ToRedisInfo(db, authCert, r.tagApp.ListTagPathByTypeAndCode(consts.ResourceTypeRedis, re.Code)...), nil
})
}
func (r *redisAppImpl) RunCmd(ctx context.Context, redisConn *rdm.RedisConn, cmdParam *RunCmdParam) (any, error) {
if redisConn == nil {
return nil, errorx.NewBiz("redis连接不存在")
}
// 开启工单流程,并且为写入命令,则开启对应审批流程
if procdefId := r.procdefApp.GetProcdefIdByCodePath(ctx, redisConn.Info.CodePath...); procdefId != 0 && rdm.IsWriteCmd(cmdParam.Cmd[0]) {
_, err := r.procinstApp.StartProc(ctx, procdefId, &flowapp.StarProcParam{
BizType: RedisRunWriteCmdFlowBizType,
BizKey: stringx.Rand(24),
BizForm: jsonx.ToStr(cmdParam),
Remark: cmdParam.Remark,
})
if err != nil {
return nil, err
}
return nil, nil
}
res, err := redisConn.RunCmd(ctx, cmdParam.Cmd...)
// 获取的key不存在不报错
if err == redis.Nil {
return nil, nil
}
return res, err
}
func (r *redisAppImpl) FlowBizHandle(ctx context.Context, bizHandleParam *flowapp.BizHandleParam) error {
bizKey := bizHandleParam.BizKey
procinstStatus := bizHandleParam.ProcinstStatus
logx.Debugf("RedisRunWriteCmd FlowBizHandle -> bizKey: %s, procinstStatus: %s", bizKey, flowentity.ProcinstStatusEnum.GetDesc(procinstStatus))
// 流程非完成状态,不处理
if procinstStatus != flowentity.ProcinstStatusCompleted {
return nil
}
runCmdParam, err := jsonx.To(bizHandleParam.BizForm, new(RunCmdParam))
if err != nil {
return errorx.NewBiz("业务表单信息解析失败: %s", err.Error())
}
redisConn, err := r.GetRedisConn(runCmdParam.Id, runCmdParam.Db)
if err != nil {
return err
}
_, err = redisConn.RunCmd(ctx, runCmdParam.Cmd...)
return err
}