From b5f7f3aaef25488788bf3b549089a94c7bce5cae Mon Sep 17 00:00:00 2001 From: herenshan112 Date: Tue, 14 Nov 2023 13:23:47 +0800 Subject: [PATCH] =?UTF-8?q?redis=E6=95=B0=E6=8D=AE=E5=BA=93=E8=BF=81?= =?UTF-8?q?=E7=A7=BB=E6=A8=A1=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../datamanagement/redisController/redis.go | 464 +++++++++++++ .../redisController/runredis.go | 622 ++++++++++++++++++ .../datamanagement/redisController/type.go | 96 +++ api/version1/entry.go | 2 + apirouter/entry.go | 24 +- apirouter/v1/redisRouter/type.go | 23 + initialization/route/initRoute.go | 3 + 7 files changed, 1223 insertions(+), 11 deletions(-) create mode 100644 api/version1/datamanagement/redisController/redis.go create mode 100644 api/version1/datamanagement/redisController/runredis.go create mode 100644 api/version1/datamanagement/redisController/type.go create mode 100644 apirouter/v1/redisRouter/type.go diff --git a/api/version1/datamanagement/redisController/redis.go b/api/version1/datamanagement/redisController/redis.go new file mode 100644 index 0000000..af98c1e --- /dev/null +++ b/api/version1/datamanagement/redisController/redis.go @@ -0,0 +1,464 @@ +package redisController + +import ( + "appPlatform/overall/publicmethod" + "context" + "fmt" + "sort" + "strconv" + + "github.com/gin-gonic/gin" + "github.com/go-redis/redis/v8" +) + +/* +* +@ 作者: 秦东 +@ 时间: 2023-11-13 14:27:45 +@ 功能: 测试数据库链接 +@ 参数 + + # + +@ 返回值 + + # + +@ 方法原型 + + # +*/ +func (a *ApiMethod) TestRedisLink(c *gin.Context) { + var redisConfig RedisConfig + err := c.ShouldBindJSON(&redisConfig) + if err != nil { + publicmethod.Result(10001, err, c) + return + } + if redisConfig.Ip == "" { + redisConfig.Ip = "127.0.0.1" + } + if redisConfig.Port == 0 { + redisConfig.Port = 6379 + } + var redisDbConst RunOneRedis + redisDbConst.Ip = redisConfig.Ip + redisDbConst.Port = redisConfig.Port + redisDbConst.Pwd = redisConfig.Pwd + redisDbConst.DbName = 10 + redisDb := redisDbConst.OpenRedis() + + // pingLink, err := redisDb.Ping(context.Background()).Result() + _, err = redisDb.Ping(context.Background()).Result() + + redisRunDb := InitRedis(redisDb) + redisCont, redisErr := redisRunDb.ConfigGet("databases") + // sendData := publicmethod.MapOut[string]() + // sendData["linkUrl"] = redisDb + // sendData["pingLink"] = pingLink + // sendData["err"] = err + // sendData["redisErr"] = redisErr + // sendData["redisCont"] = len(redisCont) + + var sendInfo RedisLinkState + if err == nil { + sendInfo.IsTrue = 1 + } else { + sendInfo.IsTrue = 2 + publicmethod.Result(10001, sendInfo, c) + return + } + if redisErr != nil { + sendInfo.DbNumber = 16 + } else { + sendInfo.DbNumber = 16 + if len(redisCont) == 2 { + for k, v := range redisCont { + if k == 1 { + if val, isOk := v.(string); isOk { + sendInfo.DbNumber, _ = strconv.Atoi(val) + } + + } + } + } + } + var redisListCont CountRedisKeyNumber + for i := 0; i < sendInfo.DbNumber; i++ { + syncSeting.Add(1) + go redisListCont.TallyRedisKeys(redisConfig.Ip, redisConfig.Pwd, redisConfig.Port, i) + } + syncSeting.Wait() + //根据维度序号排序 + redisList := redisListCont.RedisList + sort.Slice(redisList, func(i, j int) bool { + return redisList[i].DbName < redisList[j].DbName + }) + sendInfo.RedisList = redisList + // sendData["redisListCont"] = sendInfo + // sendData["redisListContlen"] = len(sendInfo) + publicmethod.Result(0, sendInfo, c) +} + +/* +* +@ 作者: 秦东 +@ 时间: 2023-11-14 10:19:13 +@ 功能: 获取Redis有多少个数据库 +@ 参数 + + # + +@ 返回值 + + # + +@ 方法原型 + + # +*/ +func GainRedisDbNumber(redisDb *redis.Client) (dbNumber int) { + redisRunDb := InitRedis(redisDb) + redisCont, redisErr := redisRunDb.ConfigGet("databases") + if redisErr != nil { + dbNumber = 16 + } else { + dbNumber = 16 + if len(redisCont) == 2 { + for k, v := range redisCont { + if k == 1 { + if val, isOk := v.(string); isOk { + dbNumber, _ = strconv.Atoi(val) + } + + } + } + } + } + return +} + +/* +* +@ 作者: 秦东 +@ 时间: 2023-11-13 16:32:45 +@ 功能: 计算Redis数据库键个数 +@ 参数 + + #ip Redis数据库地址 + #port 端口 + #pwd 密码 + #dbId 数据库值 + +@ 返回值 + + # + +@ 方法原型 + + # +*/ +func (c *CountRedisKeyNumber) TallyRedisKeys(ip, pwd string, port, dbId int) { + var redisDbConst RunOneRedis + redisDbConst.Ip = ip + redisDbConst.Port = port + redisDbConst.Pwd = pwd + redisDbConst.DbName = dbId + redisDb := redisDbConst.OpenRedis() + redisRunDb := InitRedis(redisDb) + val, err := redisRunDb.Keys("*") + // fmt.Printf("%v---->%v\n", dbId, val) + if err == nil { + var redisInfo RedisKeyNumber + redisInfo.DbName = dbId + redisInfo.DbNumber = len(val) + c.RedisList = append(c.RedisList, redisInfo) + } + defer syncSeting.Done() +} + +/* +* +@ 作者: 秦东 +@ 时间: 2023-11-13 16:11:18 +@ 功能: 打开Redis +@ 参数 + + # + +@ 返回值 + + # + +@ 方法原型 + + # +*/ +func (r *RunOneRedis) OpenRedis() (redisDb *redis.Client) { + linkUrl := fmt.Sprintf("%v:%v", r.Ip, r.Port) + redisDb = redis.NewClient(&redis.Options{ + Addr: linkUrl, + Password: r.Pwd, + DB: r.DbName, + }) + return +} + +/* +* +@ 作者: 秦东 +@ 时间: 2023-11-14 08:04:08 +@ 功能: 迁移数据库 +@ 参数 + + # + +@ 返回值 + + # + +@ 方法原型 + + # +*/ +func (a *ApiMethod) MoveOldRedisToNewRedis(c *gin.Context) { + // var callBalackMsg MoveRedisMsg + var requestData MoveRedis + err := c.ShouldBindJSON(&requestData) + if err != nil { + publicmethod.Result(10001, err, c) + return + } + if requestData.OriginRedis.State != 1 || requestData.TargetRedis.State != 1 { + publicmethod.Result(10001, err, c, "请先测试源Redis与目标Redis是否链接成功!") + return + } + if requestData.OriginRedis.Ip == "" || requestData.TargetRedis.Ip == "" { + publicmethod.Result(10001, err, c, "请检查源RedisIP与目标RedisIP是否已填写!") + return + } + if requestData.OriginRedis.Port == 0 { + requestData.OriginRedis.Port = 6379 + } + if requestData.TargetRedis.Port == 0 { + requestData.TargetRedis.Port = 6379 + } + // //源Redis链接 + var redisDbOriginConst RunOneRedis + redisDbOriginConst.Ip = requestData.OriginRedis.Ip + redisDbOriginConst.Port = requestData.OriginRedis.Port + redisDbOriginConst.Pwd = requestData.OriginRedis.Pwd + redisDbOriginConst.DbName = 0 + redisDbOrigin := redisDbOriginConst.OpenRedis() + _, err = redisDbOrigin.Ping(context.Background()).Result() + if err != nil { + publicmethod.Result(10001, err, c, "源Redis链接失败!") + return + } + // //目标Redis链接 + // var redisDbTargetConst RunOneRedis + // redisDbTargetConst.Ip = requestData.TargetRedis.Ip + // redisDbTargetConst.Port = requestData.TargetRedis.Port + // redisDbTargetConst.Pwd = requestData.TargetRedis.Pwd + // redisDbTargetConst.DbName = 10 + // redisDbTarget := redisDbTargetConst.OpenRedis() + // _, err = redisDbTarget.Ping(context.Background()).Result() + // if err != nil { + // publicmethod.Result(10001, err, c, "目标Redis链接失败!") + // return + // } + var redisMsg NewCopyOldRedisInfo + if len(requestData.DbList) > 0 { + for _, v := range requestData.DbList { + syncSeting.Add(1) + go redisMsg.NewsMoveOldRedis(requestData.OriginRedis, requestData.TargetRedis, v) + } + } else { + dbNum := GainRedisDbNumber(redisDbOrigin) + for i := 0; i < dbNum; i++ { + syncSeting.Add(1) + go redisMsg.NewsMoveOldRedis(requestData.OriginRedis, requestData.TargetRedis, i) + } + } + syncSeting.Wait() + redisList := redisMsg.MsgList + sort.Slice(redisList, func(i, j int) bool { + return redisList[i].DbName < redisList[j].DbName + }) + publicmethod.Result(0, redisList, c) +} + +/* +* +@ 作者: 秦东 +@ 时间: 2023-11-14 08:30:52 +@ 功能: 数据迁移 +@ 参数 + + # + +@ 返回值 + + # + +@ 方法原型 + + # +*/ +func (n *NewCopyOldRedisInfo) NewsMoveOldRedis(oldRedis, newRedis RedisLink, dbName int) { + // fmt.Printf("dbName--->%v\n", dbName) + var msgCont MoveRedisMsg + msgCont.DbName = dbName + var redisDbOriginConst RunOneRedis + redisDbOriginConst.Ip = oldRedis.Ip + redisDbOriginConst.Port = oldRedis.Port + redisDbOriginConst.Pwd = oldRedis.Pwd + redisDbOriginConst.DbName = dbName + redisDbOrigin := redisDbOriginConst.OpenRedis() + _, err := redisDbOrigin.Ping(context.Background()).Result() + if err == nil { + //目标Redis链接 + var redisDbTargetConst RunOneRedis + redisDbTargetConst.Ip = newRedis.Ip + redisDbTargetConst.Port = newRedis.Port + redisDbTargetConst.Pwd = newRedis.Pwd + redisDbTargetConst.DbName = dbName + redisDbTarget := redisDbTargetConst.OpenRedis() + _, err = redisDbTarget.Ping(context.Background()).Result() + if err == nil { + oldRunDb := InitRedis(redisDbOrigin) + val, err := oldRunDb.Keys("*") + if err != nil { + var keyMsg MsgListInfo + keyMsg.Keys = "源数据库" + keyMsg.State = 2 + keyMsg.Msg = err + msgCont.MsgList = append(msgCont.MsgList, keyMsg) + } else { + if len(val) > 1 { + newRunDb := InitRedis(redisDbTarget) + for _, v := range val { + var keyMsg MsgListInfo + keyMsg.Keys = v + vInfo, vErr := oldRunDb.KeyType(v) + // fmt.Printf("%v: %v\n", v, vInfo) + if vErr == nil { + switch vInfo { + case "string": //字符串 + _, oldVal := oldRunDb.Get(v) + oldTTl, _ := oldRunDb.TTl(v) + if oldTTl < 0 { + oldTTl = 0 + } + keyMsg.State = 1 + newRunDb.SetRedisTime(oldTTl) + newRunDb.Set(v, oldVal) + keyMsg.Msg = "迁移完成!" + // fmt.Printf("%v:-------------->1--->%v--->%v--->%v\n", v, oldTTl, oldTTl < 0, oldVal) + case "list": //列表 + oldVal, oldErr := oldRunDb.Lrange(v, 0, -1) + if oldErr == nil { + oldTTl, _ := oldRunDb.TTl(v) + if oldTTl < 0 { + oldTTl = 0 + } + newRunDb.SetRedisTime(oldTTl) + for _, ov := range oldVal { + newRunDb.Rpushx(v, ov) + } + keyMsg.Msg = "迁移完成!" + keyMsg.State = 1 + } else { + keyMsg.Msg = oldErr + keyMsg.State = 2 + } + // fmt.Printf("%v:-------------->2\n", v) + case "set": //集合 + oldVal, isTrue := oldRunDb.Smembers(v) + if isTrue == nil { + oldTTl, _ := oldRunDb.TTl(v) + if oldTTl < 0 { + oldTTl = 0 + } + newRunDb.SetRedisTime(oldTTl) + newRunDb.Sadd(v, oldVal) + keyMsg.Msg = "迁移完成!" + keyMsg.State = 1 + } else { + keyMsg.Msg = "键不存在!" + keyMsg.State = 2 + } + // fmt.Printf("%v:-------------->3\n", v) + case "zset": //有序集 + oldVal, isTrue := oldRunDb.Zrange(v, 0, -1) + if isTrue == nil { + oldTTl, _ := oldRunDb.TTl(v) + if oldTTl < 0 { + oldTTl = 0 + } + newRunDb.SetRedisTime(oldTTl) + newRunDb.Zadd(v, oldVal) + keyMsg.Msg = "迁移完成!" + keyMsg.State = 1 + } else { + keyMsg.Msg = "键不存在!" + keyMsg.State = 2 + } + // fmt.Printf("%v:-------------->4\n", v) + case "hash": //哈希表 + oldVal, isTrue := oldRunDb.HashGetAll(v) + if isTrue { + oldTTl, _ := oldRunDb.TTl(v) + if oldTTl < 0 { + oldTTl = 0 + } + newRunDb.SetRedisTime(oldTTl) + newVal := publicmethod.MapOut[string]() + for ni, nv := range oldVal { + newVal[ni] = nv + } + newRunDb.HashMsetAdd(v, newVal) + keyMsg.Msg = "迁移完成!" + keyMsg.State = 1 + } else { + keyMsg.Msg = "键不存在!" + keyMsg.State = 2 + } + // fmt.Printf("%v:-------------->5\n", v) + default: + keyMsg.Msg = "键不存在!" + keyMsg.State = 2 + // fmt.Printf("%v:-------------->6\n", v) + } + } else { + keyMsg.Msg = vErr + keyMsg.State = 2 + } + msgCont.MsgList = append(msgCont.MsgList, keyMsg) + } + } else { + var keyMsg MsgListInfo + keyMsg.Keys = "源数据库" + keyMsg.Msg = "没有要迁移的数据!" + keyMsg.State = 2 + msgCont.MsgList = append(msgCont.MsgList, keyMsg) + } + } + } else { + var keyMsg MsgListInfo + keyMsg.Keys = "目标数据库" + keyMsg.Msg = err + keyMsg.State = 2 + msgCont.MsgList = append(msgCont.MsgList, keyMsg) + } + } else { + var keyMsg MsgListInfo + keyMsg.Keys = "源数据库" + keyMsg.Msg = err + keyMsg.State = 2 + msgCont.MsgList = append(msgCont.MsgList, keyMsg) + } + + n.MsgList = append(n.MsgList, msgCont) + defer syncSeting.Done() +} diff --git a/api/version1/datamanagement/redisController/runredis.go b/api/version1/datamanagement/redisController/runredis.go new file mode 100644 index 0000000..5fb2309 --- /dev/null +++ b/api/version1/datamanagement/redisController/runredis.go @@ -0,0 +1,622 @@ +package redisController + +import ( + "appPlatform/overall" + "context" + "fmt" + "time" + + "github.com/go-redis/redis/v8" +) + +type RedisStoreType struct { + Expiration time.Duration + PreKey string + Context context.Context + RedisDb *redis.Client +} + +// 启动redis +func InitRedis(redisClient *redis.Client) *RedisStoreType { + var redisStoreType RedisStoreType + redisStoreType.Expiration = time.Second * 300 + redisStoreType.PreKey = fmt.Sprintf("%v:", overall.CONSTANT_CONFIG.RedisPrefixStr.PreFix) + redisStoreType.Context = context.Background() + redisStoreType.RedisDb = redisClient + return &redisStoreType +} + +// 设置键前缀 +func (r *RedisStoreType) SetRedisPrefix(prefix string) { + r.PreKey = prefix +} + +// 设置过期时间 +func (r *RedisStoreType) SetRedisTime(timeDate int64) { + r.Expiration = time.Second * time.Duration(timeDate) +} +func (r *RedisStoreType) SetRedisTimes(timeDate time.Duration) { + r.Expiration = time.Second * timeDate +} + +// 获取数据库配置 +func (r *RedisStoreType) ConfigGet(key string) ([]interface{}, error) { + err := r.RedisDb.ConfigGet(r.Context, key) + return err.Val(), err.Err() +} + +// 设置字符串 +func (r *RedisStoreType) Set(key string, value string) bool { + err := r.RedisDb.Set(r.Context, key, value, r.Expiration).Err() + // fmt.Printf("设置字符串:%v\n", err) + + if err != nil { + return false + } + return true +} + +// 获取字符串 +func (r *RedisStoreType) Get(key string) (bool, string) { + err := r.RedisDb.Get(r.Context, key) + if err.Err() != nil { + return false, "" + } + return true, err.Val() +} + +/* +*Keys 命令用于查找所有符合给定模式 pattern 的 key 。。 + */ +func (r *RedisStoreType) Keys(key string) (val []string, err error) { + val, err = r.RedisDb.Keys(r.Context, key).Result() + return +} + +/* +*以秒为单位返回 key 的剩余过期时间 + */ +func (r *RedisStoreType) TTl(key string) (val int64, err error) { + redisVal := r.RedisDb.TTL(r.Context, key) + err = redisVal.Err() + val = redisVal.Val().Microseconds() / 1000000 + return +} +func (r *RedisStoreType) PTTl(key string) (val time.Duration, err error) { + redisVal := r.RedisDb.PTTL(r.Context, key) + err = redisVal.Err() + val = time.Duration(redisVal.Val().Microseconds()) + return +} + +/* +*返回数据键类型 + */ +func (r *RedisStoreType) KeyType(key string) (val string, err error) { + redisObject := r.RedisDb.Type(r.Context, key) + err = redisObject.Err() + val = redisObject.Val() + return +} + +// 删除键 +func (r *RedisStoreType) DelKey(key string) bool { + err := r.RedisDb.Del(r.Context, key).Err() + if err != nil { + return false + } + return true +} + +//哈希操作 +/* +获取单个哈希键值 +@hashName 集合名称 +@hashKey 哈希键 +callback + errs 状态 + hashVal 获得值 +*/ +func (r *RedisStoreType) HashGet(hashName, hashKey string) (errs bool, hashVal string) { + err := r.RedisDb.HGet(r.Context, hashName, hashKey) + if err.Err() != nil { + return false, "" + } + return true, err.Val() +} + +/* +为哈希表中的字段赋值 。 单一设置 +@hashName 集合名称 +@hashKey 哈希键 +@hashVal 要添加的值 +*/ +func (r *RedisStoreType) HashSet(hashName, hashKey, hashVal string) bool { + err := r.RedisDb.HSet(r.Context, hashName, hashKey, hashVal).Err() + if err != nil { + return false + } + if r.Expiration == 0 { + r.RedisDb.Persist(r.Context, hashName) + } else { + r.RedisDb.PExpire(r.Context, hashName, r.Expiration) + } + // global.GVA_REDIS.PExpire(r.Context, hashName, r.Expiration) + return true +} + +/* +同时将多个 field-value (字段-值)对设置到哈希表中。 +@hashName 集合名称 +@hashVal 要添加的键与值 +*/ +func (r *RedisStoreType) HashMsetAdd(hashName string, hashVal map[string]interface{}) bool { + // rdb := RedisInit() + err := r.RedisDb.HMSet(r.Context, hashName, hashVal).Err() + // fmt.Printf("错误sss=========》%v=====2====》%v\n", err, hashVal) + // err := rdb.HMSet(ctx, "userfg", hashVal).Err() + if err != nil { + return false + } + if r.Expiration == 0 { + r.RedisDb.Persist(r.Context, hashName) + } else { + r.RedisDb.PExpire(r.Context, hashName, r.Expiration) + } + + return true +} +func (r *RedisStoreType) HashMsetAddinterface(hashName string, hashVal interface{}) bool { + // rdb := RedisInit() + err := r.RedisDb.HMSet(r.Context, hashName, hashVal).Err() + // fmt.Printf("错误sss=========》%v=====2====》%v\n", err, hashVal) + // err := rdb.HMSet(ctx, "userfg", hashVal).Err() + if err != nil { + return false + } + if r.Expiration == 0 { + r.RedisDb.Persist(r.Context, hashName) + } else { + r.RedisDb.PExpire(r.Context, hashName, r.Expiration) + } + + return true +} +func (r *RedisStoreType) HashMsetAddAry(hashName string, hashVal []map[string]interface{}) bool { + // rdb := RedisInit() + err := r.RedisDb.HMSet(r.Context, hashName, hashVal).Err() + // err := rdb.HMSet(ctx, "userfg", hashVal).Err() + if err != nil { + return false + } + if r.Expiration == 0 { + r.RedisDb.Persist(r.Context, hashName) + } else { + r.RedisDb.PExpire(r.Context, hashName, r.Expiration) + } + // global.GVA_REDIS.PExpire(r.Context, hashName, r.Expiration) + // fmt.Printf("错误sss=========》%v\n", hashVal) + return true +} + +/* +返回哈希表中,所有的字段和值。 +@hashName 集合名称 +@hashKey 哈希键 +*/ +func (r *RedisStoreType) HashGetAll(hashName string) (map[string]string, bool) { + // rdb := RedisInit() + // fmt.Printf("strKEy:===>%v\n", hashName) + val, err := r.RedisDb.HGetAll(r.Context, hashName).Result() + // fmt.Printf("strKEy:==1=>%v==1=>%v\n", val, err) + if err != nil { + return val, false + } + if len(val) == 0 { + return val, false + } + return val, true +} + +//Redis 列表(List) + +/* +* +Linsert 命令用于在列表的元素前或者后插入元素。当指定元素不存在于列表中时,不执行任何操作。 +当列表不存在时,被视为空列表,不执行任何操作。 +如果 key 不是列表类型,返回一个错误。 +@key 列表 +@op 插入状态 (1:在pivot之后;2:在pivot之前) +@pivot 定位值 +@value 要插入值 +*/ +func (r *RedisStoreType) Linsert(key string, op int, pivot, value interface{}) (int64, bool) { + BeforeOrAfter := "BEFORE" + if op != 1 { + BeforeOrAfter = "AFTER" + } + linsert, linsertErr := r.RedisDb.LInsert(r.Context, key, BeforeOrAfter, pivot, value).Result() + if linsertErr != nil { + return 0, false + } + if r.Expiration == 0 { + r.RedisDb.Persist(r.Context, key) + } else { + r.RedisDb.PExpire(r.Context, key, r.Expiration) + } + return linsert, true +} + +/* +* +Lindex 命令用于通过索引获取列表中的元素。你也可以使用负数下标,以 -1 表示列表的最后一个元素, -2 表示列表的倒数第二个元素,以此类推。 +@key 列表 +@index 索引 +*/ +func (r *RedisStoreType) Lindex(key string, index int64) (val string, err error) { + val, err = r.RedisDb.LIndex(r.Context, key, index).Result() + return +} + +/* +* +Llen 命令用于返回列表的长度。 如果列表 key 不存在,则 key 被解释为一个空列表,返回 0 。 如果 key 不是列表类型,返回一个错误。 +@key 列表 +*/ +func (r *RedisStoreType) Llen(key string) (val int64, err error) { + val, err = r.RedisDb.LLen(r.Context, key).Result() + return +} + +/* +* +Lpop 命令用于移除并返回列表的第一个元素。 +@key 列表 +*/ +func (r *RedisStoreType) Lpop(key string) (val string, err error) { + val, err = r.RedisDb.LPop(r.Context, key).Result() + return +} + +/* +* +Lpush 命令将一个或多个值插入到列表头部。 如果 key 不存在,一个空列表会被创建并执行 LPUSH 操作。 当 key 存在但不是列表类型时,返回一个错误。 +@key 列表 +@value 要插入的字符串 +*/ +func (r *RedisStoreType) Lpush(key string, value ...interface{}) (val int64, err error) { + val, err = r.RedisDb.LPush(r.Context, key, value).Result() + if r.Expiration == 0 { + r.RedisDb.Persist(r.Context, key) + } else { + r.RedisDb.PExpire(r.Context, key, r.Expiration) + } + return +} + +/* +* +Lpushx 将一个值插入到已存在的列表头部,列表不存在时操作无效。 +@key 列表 +@value 要插入的字符串 +*/ +func (r *RedisStoreType) Lpushx(key, value string) (val int64, err error) { + val, err = r.RedisDb.LPushX(r.Context, key, value).Result() + if r.Expiration == 0 { + r.RedisDb.Persist(r.Context, key) + } else { + r.RedisDb.PExpire(r.Context, key, r.Expiration) + } + return +} + +/* +* +Lrange 返回列表中指定区间内的元素,区间以偏移量 START 和 END 指定。 其中 0 表示列表的第一个元素, 1 表示列表的第二个元素,以此类推。 你也可以使用负数下标,以 -1 表示列表的最后一个元素, -2 表示列表的倒数第二个元素,以此类推。 +@key 列表 +@start 起始值 +@stop 结束值 +*/ +func (r *RedisStoreType) Lrange(key string, start, stop int64) (val []string, err error) { + val, err = r.RedisDb.LRange(r.Context, key, start, stop).Result() + return +} + +/* +* +Lrem 根据参数 COUNT 的值,移除列表中与参数 VALUE 相等的元素。 +COUNT 的值可以是以下几种: + + count > 0 : 从表头开始向表尾搜索,移除与 VALUE 相等的元素,数量为 COUNT 。 + count < 0 : 从表尾开始向表头搜索,移除与 VALUE 相等的元素,数量为 COUNT 的绝对值。 + count = 0 : 移除表中所有与 VALUE 相等的值。 + +@start = COUNT +@key 列表 +*/ +func (r *RedisStoreType) Lrem(key string, start int64, value ...interface{}) (val int64, err error) { + val, err = r.RedisDb.LRem(r.Context, key, start, value).Result() + return +} + +/* +* +Redis Lset 通过索引来设置元素的值。 + +当索引参数超出范围,或对一个空列表进行 LSET 时,返回一个错误。 +@key 列表 +@indexes 索引值 +*/ +func (r *RedisStoreType) Lset(key string, indexes int64, value ...interface{}) (val string, err error) { + val, err = r.RedisDb.LSet(r.Context, key, indexes, value).Result() + if r.Expiration == 0 { + r.RedisDb.Persist(r.Context, key) + } else { + r.RedisDb.PExpire(r.Context, key, r.Expiration) + } + return +} + +/* +* +Ltrim 对一个列表进行修剪(trim),就是说,让列表只保留指定区间内的元素,不在指定区间之内的元素都将被删除。 + +下标 0 表示列表的第一个元素,以 1 表示列表的第二个元素,以此类推。 你也可以使用负数下标,以 -1 表示列表的最后一个元素, -2 表示列表的倒数第二个元素,以此类推。 +@key 列表 +@start 起始值 +@stop 结束值 +*/ +func (r *RedisStoreType) Ltrim(key string, start, stop int64) (val string, err error) { + val, err = r.RedisDb.LTrim(r.Context, key, start, stop).Result() + return +} + +/* +* +Rpop 命令用于移除列表的最后一个元素,返回值为移除的元素。 +@key 列表 +*/ +func (r *RedisStoreType) Rpop(key string) (val string, err error) { + val, err = r.RedisDb.RPop(r.Context, key).Result() + return +} + +/* +* +Rpoplpush 命令用于移除列表的最后一个元素,并将该元素添加到另一个列表并返回。 +@sourceKey 源列表 +@newKey 目标列表 +*/ +func (r *RedisStoreType) Rpoplpush(sourceKey, newKey string) (val string, err error) { + val, err = r.RedisDb.RPopLPush(r.Context, sourceKey, newKey).Result() + return +} + +/* +* +Rpush 命令用于将一个或多个值插入到列表的尾部(最右边)。 +如果列表不存在,一个空列表会被创建并执行 RPUSH 操作。 当列表存在但不是列表类型时,返回一个错误。 +@key 列表 +@value 要插入的字符串 +*/ +func (r *RedisStoreType) Rpush(key string, value ...interface{}) (val int64, err error) { + val, err = r.RedisDb.RPush(r.Context, key, value).Result() + if r.Expiration == 0 { + r.RedisDb.Persist(r.Context, key) + } else { + r.RedisDb.PExpire(r.Context, key, r.Expiration) + } + return +} + +/* +* +Rpushx 命令用于将一个值插入到已存在的列表尾部(最右边)。如果列表不存在,操作无效。 +@key 列表 +@value 要插入的字符串 +*/ +func (r *RedisStoreType) Rpushx(key string, value ...interface{}) (val int64, err error) { + val, err = r.RedisDb.RPushX(r.Context, key, value).Result() + if r.Expiration == 0 { + r.RedisDb.Persist(r.Context, key) + } else { + r.RedisDb.PExpire(r.Context, key, r.Expiration) + } + return +} + +/* +* +Blpop 命令移出并获取列表的第一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。 +@key 列表 +*/ +func (r *RedisStoreType) Blpop(key string) (val []string, err error) { + val, err = r.RedisDb.BLPop(r.Context, r.Expiration, key).Result() + return +} + +/* +* +Brpop 命令移出并获取列表的最后一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。 +@key 列表 +*/ +func (r *RedisStoreType) Brpop(key string) (val []string, err error) { + val, err = r.RedisDb.BRPop(r.Context, r.Expiration, key).Result() + return +} + +/* +* +Brpoplpush 命令从列表中取出最后一个元素,并插入到另外一个列表的头部; 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。 +@source 源列表 +@destination 目标列表 +*/ +func (r *RedisStoreType) Brpoplpush(source, destination string) (val string, err error) { + val, err = r.RedisDb.BRPopLPush(r.Context, source, destination, r.Expiration).Result() + return +} + +/* +Redis 键(key) +Redis 键命令用于管理 redis 的键。 +*/ + +/* +Redis SCAN 命令 +Redis Scan 命令用于迭代数据库中的数据库键。 + +SCAN 命令是一个基于游标的迭代器,每次被调用之后, 都会向用户返回一个新的游标, 用户在下次迭代时需要使用这个新游标作为 SCAN 命令的游标参数, 以此来延续之前的迭代过程。 + +SCAN 返回一个包含两个元素的数组, 第一个元素是用于进行下一次迭代的新游标, 而第二个元素则是一个数组, 这个数组中包含了所有被迭代的元素。如果新游标返回 0 表示迭代已结束。 + +相关命令: + + SSCAN 命令用于迭代集合键中的元素。 + HSCAN 命令用于迭代哈希键中的键值对。 + ZSCAN 命令用于迭代有序集合中的元素(包括元素成员和元素分值)。 +*/ + +func (r *RedisStoreType) Scan(cursor uint64, match string, count int64) (keys []string, cursores uint64, err error) { + keys, cursores, err = r.RedisDb.Scan(r.Context, cursor, match, count).Result() + return +} + +/* +集合操作 +*/ +/** +@ 作者: 秦东 +@ 时间: 2023-11-14 09:44:32 +@ 功能: Redis Smembers 命令返回集合中的所有的成员。 不存在的集合 key 被视为空集合。 +@ 参数 + #key 键 +@ 返回值 + # +@ 方法原型 + # +*/ +func (r *RedisStoreType) Smembers(key string) ([]string, error) { + redisClient := r.RedisDb.SMembers(r.Context, key) + return redisClient.Val(), redisClient.Err() +} + +/* +* +@ 作者: 秦东 +@ 时间: 2023-11-14 09:47:54 +@ 功能: Redis Sadd 命令将一个或多个成员元素加入到集合中,已经存在于集合的成员元素将被忽略。 + + 假如集合 key 不存在,则创建一个只包含添加的元素作成员的集合。 + + 当集合 key 不是集合类型时,返回一个错误。 + + 注意:在 Redis2.4 版本以前, SADD 只接受单个成员值。 + +@ 参数 + + # + +@ 返回值 + + # + +@ 方法原型 + + # +*/ +func (r *RedisStoreType) Sadd(key string, val []string) (msg []string) { + if len(val) > 0 { + for _, v := range val { + redisClient := r.RedisDb.SAdd(r.Context, key, v) + if redisClient.Err() == nil { + msg = append(msg, fmt.Sprintf("集合%v添加%v成功", key, v)) + } else { + msg = append(msg, fmt.Sprintf("集合%v添加%v失败", key, v)) + } + } + if r.Expiration == 0 { + r.RedisDb.Persist(r.Context, key) + } else { + r.RedisDb.PExpire(r.Context, key, r.Expiration) + } + } + return +} + +/* +*有序集合 + */ +/** +@ 作者: 秦东 +@ 时间: 2023-11-14 10:02:56 +@ 功能: Redis Zrange 返回有序集中,指定区间内的成员。 + + 其中成员的位置按分数值递增(从小到大)来排序。 + + 具有相同分数值的成员按字典序(lexicographical order )来排列。 + + 如果你需要成员按 + + 值递减(从大到小)来排列,请使用 ZREVRANGE 命令。 + + 下标参数 start 和 stop 都以 0 为底,也就是说,以 0 表示有序集第一个成员,以 1 表示有序集第二个成员,以此类推。 + + 你也可以使用负数下标,以 -1 表示最后一个成员, -2 表示倒数第二个成员,以此类推。 +@ 参数 + # +@ 返回值 + # +@ 方法原型 + # +*/ +func (r *RedisStoreType) Zrange(key string, start, stop int64) ([]string, error) { + redisClient := r.RedisDb.ZRange(r.Context, key, start, stop) + return redisClient.Val(), redisClient.Err() +} + +/* +* +@ 作者: 秦东 +@ 时间: 2023-11-14 10:05:51 +@ 功能: Redis Zadd 命令用于将一个或多个成员元素及其分数值加入到有序集当中。 + + 如果某个成员已经是有序集的成员,那么更新这个成员的分数值,并通过重新插入这个成员元素,来保证该成员在正确的位置上。 + + 分数值可以是整数值或双精度浮点数。 + + 如果有序集合 key 不存在,则创建一个空的有序集并执行 ZADD 操作。 + + 当 key 存在但不是有序集类型时,返回一个错误。 + + 注意: 在 Redis 2.4 版本以前, ZADD 每次只能添加一个元素。 + +@ 参数 + + # + +@ 返回值 + + # + +@ 方法原型 + + # +*/ +func (r *RedisStoreType) Zadd(key string, val []string) (msg []string) { + if len(val) > 0 { + for _, v := range val { + redisClient := r.RedisDb.ZAdd(r.Context, key, &redis.Z{1, v}) + if redisClient.Err() == nil { + msg = append(msg, fmt.Sprintf("有序集合%v添加%v成功", key, v)) + } else { + msg = append(msg, fmt.Sprintf("有序集合%v添加%v失败", key, v)) + } + } + if r.Expiration == 0 { + r.RedisDb.Persist(r.Context, key) + } else { + r.RedisDb.PExpire(r.Context, key, r.Expiration) + } + } + return +} diff --git a/api/version1/datamanagement/redisController/type.go b/api/version1/datamanagement/redisController/type.go new file mode 100644 index 0000000..a7a70b0 --- /dev/null +++ b/api/version1/datamanagement/redisController/type.go @@ -0,0 +1,96 @@ +package redisController + +import ( + "appPlatform/overall/publicmethod" + "sync" + + "github.com/gin-gonic/gin" +) + +/* +* +@ 作者: 秦东 +@ 时间: 2023-11-13 13:56:53 +@ 功能: Redis数据控制器相关操作 +@ 参数 + + # + +@ 返回值 + + # + +@ 方法原型 + + # +*/ +type ApiMethod struct{} + +// Redis数据控制器入口 +func (a *ApiMethod) Index(c *gin.Context) { + outputCont := publicmethod.MapOut[string]() + outputCont["index"] = "Redis数据控制器入口" + publicmethod.Result(0, outputCont, c) +} + +// 协程设置 +var syncSeting = sync.WaitGroup{} + +// 链接基础配置 +type RedisConfig struct { + Ip string `json:"ip"` + Port int `json:"port"` + Pwd string `json:"pwd"` +} + +// 判断链接是否接通 +type RedisLink struct { + RedisConfig + State int `json:"state"` +} + +// 执行单一库 +type RunOneRedis struct { + RedisConfig + DbName int `json:"dbName"` +} + +// 数据库连通情况 +type RedisLinkState struct { + IsTrue int `json:"isTrue"` + DbNumber int `json:"dbNumber"` + RedisList []RedisKeyNumber `json:"redisList"` +} + +// redis数据库数据键个数 +type RedisKeyNumber struct { + DbName int `json:"dbName"` + DbNumber int `json:"dbNumber"` +} + +// 计算各个redis数据库数据键个数 +type CountRedisKeyNumber struct { + RedisList []RedisKeyNumber `json:"redisList"` +} + +// 迁移Redis +type MoveRedis struct { + OriginRedis RedisLink `json:"originRedis"` //源 + TargetRedis RedisLink `json:"targetRedis"` //目标 + DbList []int `json:"dblist"` //要迁移的库 +} + +// 迁移Redis执行信息 +type MoveRedisMsg struct { + DbName int `json:"dbName"` + MsgList []MsgListInfo `json:"msgList"` +} +type MsgListInfo struct { + Keys string `json:"keys"` + State int `json:"state"` + Msg interface{} `json:"msg"` +} + +type NewCopyOldRedisInfo struct { + MsgList []MoveRedisMsg `json:"msgList"` +} diff --git a/api/version1/entry.go b/api/version1/entry.go index 32eaa71..f56251a 100644 --- a/api/version1/entry.go +++ b/api/version1/entry.go @@ -2,6 +2,7 @@ package version1 import ( "appPlatform/api/version1/customerform" + "appPlatform/api/version1/datamanagement/redisController" "appPlatform/api/version1/dict" "appPlatform/api/version1/grantpowers" matrixapi "appPlatform/api/version1/matrixApi" @@ -24,6 +25,7 @@ type ApiEntry struct { TaskManagementApi taskmanagement.ApiMethod TaskFlowApi taskflow.ApiMethod NewsClassApi newsclass.ApiMethod //新闻类 + RedisManagApi redisController.ApiMethod } var AppApiEntry = new(ApiEntry) diff --git a/apirouter/entry.go b/apirouter/entry.go index 1bbdfdb..2c93122 100644 --- a/apirouter/entry.go +++ b/apirouter/entry.go @@ -10,23 +10,25 @@ import ( menusrouters "appPlatform/apirouter/v1/menusRouters" "appPlatform/apirouter/v1/newsclassrouter" "appPlatform/apirouter/v1/public" + "appPlatform/apirouter/v1/redisRouter" "appPlatform/apirouter/v1/taskrouter" userrouters "appPlatform/apirouter/v1/userRouters" ) // 路由结构 type RouterGroup struct { - ShiyanApi apishiyan.ApiRouter - UserRouter userrouters.ApiRouter - MenusRouter menusrouters.ApiRouter - DictRouter dictrouters.ApiRouter - GrantPowerRouter grantsystempower.ApiRouter - MatrixApiRouter matrixrouters.ApiRouter - PublicRouters public.ApiRouter - SignCodeRouter authenticationroute.ApiRouter - CustomerFormRouter customerformrouter.ApiRouter - TaskRouter taskrouter.ApiRouter - NewsClassApiRouter newsclassrouter.ApiRouter + ShiyanApi apishiyan.ApiRouter + UserRouter userrouters.ApiRouter + MenusRouter menusrouters.ApiRouter + DictRouter dictrouters.ApiRouter + GrantPowerRouter grantsystempower.ApiRouter + MatrixApiRouter matrixrouters.ApiRouter + PublicRouters public.ApiRouter + SignCodeRouter authenticationroute.ApiRouter + CustomerFormRouter customerformrouter.ApiRouter + TaskRouter taskrouter.ApiRouter + NewsClassApiRouter newsclassrouter.ApiRouter + RedisClassApiRouter redisRouter.ApiRouter } var RouterGroupEntry = new(RouterGroup) diff --git a/apirouter/v1/redisRouter/type.go b/apirouter/v1/redisRouter/type.go new file mode 100644 index 0000000..53945c9 --- /dev/null +++ b/apirouter/v1/redisRouter/type.go @@ -0,0 +1,23 @@ +package redisRouter + +import ( + "appPlatform/api/version1" + + "github.com/gin-gonic/gin" +) + +// Redis控制器路由类 +type ApiRouter struct{} + +// 权限管理PC端 +func (a *ApiRouter) RouterGroup(router *gin.RouterGroup) { + apiRouter := router.Group("redis") + + var methodBinding = version1.AppApiEntry.RedisManagApi + { + apiRouter.GET("", methodBinding.Index) //入口 + apiRouter.POST("", methodBinding.Index) //入口 + apiRouter.POST("testRedisLink", methodBinding.TestRedisLink) //测试数据库链接 + apiRouter.POST("moveOldRedisToNewRedis", methodBinding.MoveOldRedisToNewRedis) //迁移数据库 + } +} diff --git a/initialization/route/initRoute.go b/initialization/route/initRoute.go index 5e10fa6..828c07f 100644 --- a/initialization/route/initRoute.go +++ b/initialization/route/initRoute.go @@ -70,6 +70,9 @@ func InitialRouter() *gin.Engine { //注册新闻类接口 newsClassApiRouter := apirouter.RouterGroupEntry.NewsClassApiRouter newsClassApiRouter.RouterGroup(VerifyIdentity) + //注册Redis数据管理接口 + redisClassApiRouter := apirouter.RouterGroupEntry.RedisClassApiRouter + redisClassApiRouter.RouterGroup(VerifyIdentity) } //验证身份接口 无需鉴权Url(主要web端使用) VerifyIdentityWeb := router.Group("")