561 lines
14 KiB
Lua
561 lines
14 KiB
Lua
-- redis_conn.lua
|
||
local redis_conn = {}
|
||
redis_conn.__index = redis_conn
|
||
|
||
--[[
|
||
转义 Redis 参数,处理包含空格等特殊字符的值
|
||
@param value 要转义的值
|
||
@return 返回转义后的字符串
|
||
]]
|
||
local function escape_value(value)
|
||
if value == nil then
|
||
return ""
|
||
end
|
||
-- 将值转换为字符串
|
||
local str = tostring(value)
|
||
-- 如果包含空格、引号或特殊字符,使用双引号包裹并转义内部引号
|
||
if string.find(str, "[%s\"'\\]") then
|
||
-- 转义反斜杠和双引号
|
||
str = string.gsub(str, "\\", "\\\\")
|
||
str = string.gsub(str, "\"", "\\\"")
|
||
return "\"" .. str .. "\""
|
||
end
|
||
return str
|
||
end
|
||
--[[
|
||
还原 Redis 字符串(去转义)
|
||
@param value 被转义的字符串
|
||
@return 返回还原后的字符串
|
||
]]
|
||
local function unescape_value(value)
|
||
if value == nil then
|
||
return nil
|
||
end
|
||
local str = tostring(value)
|
||
-- 如果是用双引号包住的,去掉包裹并恢复转义
|
||
if #str >= 2 and string.sub(str,1,1) == "\"" and string.sub(str,-1,-1) == "\"" then
|
||
str = string.sub(str,2,-2)
|
||
str = string.gsub(str, "\\\"", "\"")
|
||
str = string.gsub(str, "\\\\", "\\")
|
||
return str
|
||
end
|
||
return str
|
||
end
|
||
|
||
--[[
|
||
对数组进行去转义处理
|
||
@param arr 数组
|
||
@return 返回去转义后的数组
|
||
]]
|
||
local function unescape_array(arr)
|
||
if arr == nil or type(arr) ~= "table" then
|
||
return arr
|
||
end
|
||
local result = {}
|
||
for i, v in ipairs(arr) do
|
||
result[i] = unescape_value(v)
|
||
end
|
||
return result
|
||
end
|
||
|
||
--[[
|
||
构建 Redis 命令字符串
|
||
@param cmd 命令名称
|
||
@param ... 命令参数
|
||
@return 返回构建好的命令字符串
|
||
]]
|
||
local function build_command(cmd, ...)
|
||
local args = {...}
|
||
local parts = {cmd}
|
||
for i, arg in ipairs(args) do
|
||
if arg ~= nil then
|
||
table.insert(parts, escape_value(arg))
|
||
end
|
||
end
|
||
return table.concat(parts, " ")
|
||
end
|
||
|
||
--[[
|
||
创建一个新的 fw_redis_conn 对象
|
||
@return 返回一个新的 fw_redis_conn 对象
|
||
]]
|
||
function redis_conn.new(__module)
|
||
local instance = setmetatable({}, redis_conn)
|
||
instance.module = __module
|
||
return instance
|
||
end
|
||
--[[
|
||
关闭
|
||
]]
|
||
function redis_conn:close()
|
||
return self.module:close()
|
||
end
|
||
--[[
|
||
执行 Redis 命令
|
||
@param cmd Redis 命令字符串
|
||
@return 返回命令执行结果
|
||
]]
|
||
function redis_conn:command(cmd)
|
||
return self.module:command(cmd)
|
||
end
|
||
--[[
|
||
执行 Redis 命令
|
||
@param args 命令参数数组,会自动将参数转换为字符串
|
||
@return 返回命令执行结果
|
||
]]
|
||
function redis_conn:command_ex(args)
|
||
if args == nil or type(args) ~= "table" then
|
||
return nil
|
||
end
|
||
local str_args = {}
|
||
for i, arg in ipairs(args) do
|
||
if arg == nil then
|
||
table.insert(str_args, "")
|
||
elseif type(arg) == "number" then
|
||
-- 判断是否为整数:整数不转成小数
|
||
if arg % 1 == 0 then
|
||
-- 是整数,使用 %d 格式,避免转成小数
|
||
table.insert(str_args, string.format("%d", arg))
|
||
else
|
||
-- 是浮点数,直接转字符串
|
||
table.insert(str_args, tostring(arg))
|
||
end
|
||
else
|
||
-- 其他类型(字符串、布尔等)直接转字符串
|
||
table.insert(str_args, tostring(arg))
|
||
end
|
||
end
|
||
return self.module:command_ex(str_args)
|
||
end
|
||
-- ==================== String 操作 ====================
|
||
|
||
--[[
|
||
GET 命令:获取键的值
|
||
@param key 键名
|
||
@return 返回键的值,不存在返回 nil
|
||
]]
|
||
function redis_conn:get(key)
|
||
local result = self:command(build_command("GET", key))
|
||
return unescape_value(result)
|
||
end
|
||
|
||
--[[
|
||
SET 命令:设置键值对
|
||
@param key 键名
|
||
@param value 值(可以是字符串、数字等,会自动处理空格)
|
||
@param options 可选参数,如 "EX seconds" 或 "PX milliseconds" 或 "NX" 或 "XX"
|
||
@return 返回执行结果
|
||
]]
|
||
function redis_conn:set(key, value, options)
|
||
local args = {"SET", key, value}
|
||
if options then
|
||
-- 如果 options 是字符串,需要拆分(如 "EX 10" 或 "NX")
|
||
if type(options) == "string" then
|
||
for word in string.gmatch(options, "%S+") do
|
||
table.insert(args, word)
|
||
end
|
||
else
|
||
table.insert(args, options)
|
||
end
|
||
end
|
||
return self:command_ex(args)
|
||
end
|
||
|
||
--[[
|
||
SETEX 命令:设置键值对并设置过期时间(秒)
|
||
@param key 键名
|
||
@param seconds 过期时间(秒)
|
||
@param value 值
|
||
@return 返回执行结果
|
||
]]
|
||
function redis_conn:setex(key, seconds, value)
|
||
return self:command_ex({"SETEX", key, seconds, value})
|
||
end
|
||
|
||
--[[
|
||
SETNX 命令:当键不存在时设置键值对
|
||
@param key 键名
|
||
@param value 值
|
||
@return 返回 1 表示设置成功,0 表示键已存在
|
||
]]
|
||
function redis_conn:setnx(key, value)
|
||
return self:command_ex({"SETNX", key, value})
|
||
end
|
||
|
||
--[[
|
||
DEL 命令:删除一个或多个键
|
||
@param ... 键名列表
|
||
@return 返回删除的键数量
|
||
]]
|
||
function redis_conn:del(...)
|
||
local args = {"DEL", ...}
|
||
return self:command_ex(args)
|
||
end
|
||
|
||
--[[
|
||
EXISTS 命令:检查键是否存在
|
||
@param key 键名
|
||
@return 返回 1 表示存在,0 表示不存在
|
||
]]
|
||
function redis_conn:exists(key)
|
||
return self:command_ex({"EXISTS", key})
|
||
end
|
||
|
||
--[[
|
||
KEYS 命令:查找所有匹配给定模式的键
|
||
@param pattern 匹配模式,支持通配符:* 匹配任意字符,? 匹配单个字符,[] 匹配指定字符
|
||
@return 返回匹配的键数组
|
||
注意:在生产环境中应谨慎使用,建议使用 SCAN 命令替代
|
||
]]
|
||
function redis_conn:keys(pattern)
|
||
local result = self:command_ex({"KEYS", pattern})
|
||
return unescape_array(result)
|
||
end
|
||
|
||
--[[
|
||
EXPIRE 命令:设置键的过期时间(秒)
|
||
@param key 键名
|
||
@param seconds 过期时间(秒)
|
||
@return 返回 1 表示设置成功,0 表示键不存在或设置失败
|
||
]]
|
||
function redis_conn:expire(key, seconds)
|
||
return self:command_ex({"EXPIRE", key, seconds})
|
||
end
|
||
|
||
--[[
|
||
TTL 命令:获取键的剩余过期时间(秒)
|
||
@param key 键名
|
||
@return 返回剩余秒数,-1 表示永不过期,-2 表示键不存在
|
||
]]
|
||
function redis_conn:ttl(key)
|
||
return self:command_ex({"TTL", key})
|
||
end
|
||
|
||
--[[
|
||
INCR 命令:将键的值加 1
|
||
@param key 键名
|
||
@return 返回增加后的值
|
||
]]
|
||
function redis_conn:incr(key)
|
||
return self:command_ex({"INCR", key})
|
||
end
|
||
|
||
--[[
|
||
DECR 命令:将键的值减 1
|
||
@param key 键名
|
||
@return 返回减少后的值
|
||
]]
|
||
function redis_conn:decr(key)
|
||
return self:command_ex({"DECR", key})
|
||
end
|
||
|
||
--[[
|
||
INCRBY 命令:将键的值增加指定数值
|
||
@param key 键名
|
||
@param increment 增量
|
||
@return 返回增加后的值
|
||
]]
|
||
function redis_conn:incrby(key, increment)
|
||
return self:command_ex({"INCRBY", key, increment})
|
||
end
|
||
|
||
--[[
|
||
DECRBY 命令:将键的值减少指定数值
|
||
@param key 键名
|
||
@param decrement 减量
|
||
@return 返回减少后的值
|
||
]]
|
||
function redis_conn:decrby(key, decrement)
|
||
return self:command_ex({"DECRBY", key, decrement})
|
||
end
|
||
|
||
-- ==================== Hash 操作 ====================
|
||
|
||
--[[
|
||
HGET 命令:获取哈希表中字段的值
|
||
@param key 键名
|
||
@param field 字段名
|
||
@return 返回字段的值,不存在返回 nil
|
||
]]
|
||
function redis_conn:hget(key, field)
|
||
local result = self:command_ex({"HGET", key, field})
|
||
return unescape_value(result)
|
||
end
|
||
|
||
--[[
|
||
HSET 命令:设置哈希表中字段的值
|
||
@param key 键名
|
||
@param field 字段名
|
||
@param value 值
|
||
@return 返回 1 表示新建字段,0 表示更新字段
|
||
]]
|
||
function redis_conn:hset(key, field, value)
|
||
return self:command_ex({"HSET", key, field, value})
|
||
end
|
||
|
||
--[[
|
||
HDEL 命令:删除哈希表中的一个或多个字段
|
||
@param key 键名
|
||
@param ... 字段名列表
|
||
@return 返回删除的字段数量
|
||
]]
|
||
function redis_conn:hdel(key, ...)
|
||
local args = {"HDEL", key, ...}
|
||
return self:command_ex(args)
|
||
end
|
||
|
||
--[[
|
||
HEXISTS 命令:检查哈希表中字段是否存在
|
||
@param key 键名
|
||
@param field 字段名
|
||
@return 返回 1 表示存在,0 表示不存在
|
||
]]
|
||
function redis_conn:hexists(key, field)
|
||
return self:command_ex({"HEXISTS", key, field})
|
||
end
|
||
|
||
--[[
|
||
HGETALL 命令:获取哈希表中所有字段和值
|
||
@param key 键名
|
||
@return 返回字段和值的数组
|
||
]]
|
||
function redis_conn:hgetall(key)
|
||
local result = self:command_ex({"HGETALL", key})
|
||
return unescape_array(result)
|
||
end
|
||
|
||
--[[
|
||
HKEYS 命令:获取哈希表中所有字段名
|
||
@param key 键名
|
||
@return 返回字段名数组
|
||
]]
|
||
function redis_conn:hkeys(key)
|
||
local result = self:command_ex({"HKEYS", key})
|
||
return unescape_array(result)
|
||
end
|
||
|
||
--[[
|
||
HVALS 命令:获取哈希表中所有值
|
||
@param key 键名
|
||
@return 返回值数组
|
||
]]
|
||
function redis_conn:hvals(key)
|
||
local result = self:command_ex({"HVALS", key})
|
||
return unescape_array(result)
|
||
end
|
||
|
||
--[[
|
||
HLEN 命令:获取哈希表中字段数量
|
||
@param key 键名
|
||
@return 返回字段数量
|
||
]]
|
||
function redis_conn:hlen(key)
|
||
return self:command_ex({"HLEN", key})
|
||
end
|
||
|
||
-- ==================== List 操作 ====================
|
||
|
||
--[[
|
||
LPUSH 命令:从列表左端插入一个或多个值
|
||
@param key 键名
|
||
@param ... 值列表
|
||
@return 返回插入后列表的长度
|
||
]]
|
||
function redis_conn:lpush(key, ...)
|
||
local args = {"LPUSH", key, ...}
|
||
return self:command_ex(args)
|
||
end
|
||
|
||
--[[
|
||
RPUSH 命令:从列表右端插入一个或多个值
|
||
@param key 键名
|
||
@param ... 值列表
|
||
@return 返回插入后列表的长度
|
||
]]
|
||
function redis_conn:rpush(key, ...)
|
||
local args = {"RPUSH", key, ...}
|
||
return self:command_ex(args)
|
||
end
|
||
|
||
--[[
|
||
LPOP 命令:从列表左端弹出一个值
|
||
@param key 键名
|
||
@return 返回弹出的值,列表为空返回 nil
|
||
]]
|
||
function redis_conn:lpop(key)
|
||
local result = self:command_ex({"LPOP", key})
|
||
return unescape_value(result)
|
||
end
|
||
|
||
--[[
|
||
RPOP 命令:从列表右端弹出一个值
|
||
@param key 键名
|
||
@return 返回弹出的值,列表为空返回 nil
|
||
]]
|
||
function redis_conn:rpop(key)
|
||
local result = self:command_ex({"RPOP", key})
|
||
return unescape_value(result)
|
||
end
|
||
|
||
--[[
|
||
LLEN 命令:获取列表长度
|
||
@param key 键名
|
||
@return 返回列表长度
|
||
]]
|
||
function redis_conn:llen(key)
|
||
return self:command_ex({"LLEN", key})
|
||
end
|
||
|
||
--[[
|
||
LRANGE 命令:获取列表指定范围内的元素
|
||
@param key 键名
|
||
@param start 起始索引(从 0 开始)
|
||
@param stop 结束索引(-1 表示最后一个元素)
|
||
@return 返回元素数组
|
||
]]
|
||
function redis_conn:lrange(key, start, stop)
|
||
local result = self:command_ex({"LRANGE", key, start, stop})
|
||
return unescape_array(result)
|
||
end
|
||
|
||
-- ==================== Set 操作 ====================
|
||
|
||
--[[
|
||
SADD 命令:向集合添加一个或多个成员
|
||
@param key 键名
|
||
@param ... 成员列表
|
||
@return 返回添加的成员数量
|
||
]]
|
||
function redis_conn:sadd(key, ...)
|
||
local args = {"SADD", key, ...}
|
||
return self:command_ex(args)
|
||
end
|
||
|
||
--[[
|
||
SREM 命令:从集合移除一个或多个成员
|
||
@param key 键名
|
||
@param ... 成员列表
|
||
@return 返回移除的成员数量
|
||
]]
|
||
function redis_conn:srem(key, ...)
|
||
local args = {"SREM", key, ...}
|
||
return self:command_ex(args)
|
||
end
|
||
|
||
--[[
|
||
SMEMBERS 命令:获取集合所有成员
|
||
@param key 键名
|
||
@return 返回成员数组
|
||
]]
|
||
function redis_conn:smembers(key)
|
||
local result = self:command_ex({"SMEMBERS", key})
|
||
return unescape_array(result)
|
||
end
|
||
|
||
--[[
|
||
SISMEMBER 命令:检查成员是否在集合中
|
||
@param key 键名
|
||
@param member 成员
|
||
@return 返回 1 表示存在,0 表示不存在
|
||
]]
|
||
function redis_conn:sismember(key, member)
|
||
return self:command_ex({"SISMEMBER", key, member})
|
||
end
|
||
|
||
--[[
|
||
SCARD 命令:获取集合成员数量
|
||
@param key 键名
|
||
@return 返回成员数量
|
||
]]
|
||
function redis_conn:scard(key)
|
||
return self:command_ex({"SCARD", key})
|
||
end
|
||
|
||
-- ==================== Sorted Set 操作 ====================
|
||
|
||
--[[
|
||
ZADD 命令:向有序集合添加一个或多个成员
|
||
@param key 键名
|
||
@param score 分数
|
||
@param member 成员
|
||
@param ... 可选的更多 score-member 对
|
||
@return 返回添加的成员数量
|
||
]]
|
||
function redis_conn:zadd(key, score, member, ...)
|
||
local args = {"ZADD", key, score, member, ...}
|
||
return self:command_ex(args)
|
||
end
|
||
|
||
--[[
|
||
ZRANGE 命令:获取有序集合指定范围内的成员
|
||
@param key 键名
|
||
@param start 起始索引
|
||
@param stop 结束索引
|
||
@param withscores 是否包含分数,默认为 false
|
||
@return 返回成员数组(如果 withscores 为 true,则包含分数)
|
||
]]
|
||
function redis_conn:zrange(key, start, stop, withscores)
|
||
local args = {"ZRANGE", key, start, stop}
|
||
if withscores then
|
||
table.insert(args, "WITHSCORES")
|
||
end
|
||
local result = self:command_ex(args)
|
||
if result == nil or type(result) ~= "table" then
|
||
return result
|
||
end
|
||
-- 如果 withscores 为 true,结果是 [member1, score1, member2, score2, ...]
|
||
-- 只处理成员部分(奇数索引),分数部分保持不变
|
||
if withscores then
|
||
local unescaped = {}
|
||
for i = 1, #result do
|
||
if i % 2 == 1 then
|
||
-- 成员(奇数索引)
|
||
unescaped[i] = unescape_value(result[i])
|
||
else
|
||
-- 分数(偶数索引)
|
||
unescaped[i] = result[i]
|
||
end
|
||
end
|
||
return unescaped
|
||
else
|
||
return unescape_array(result)
|
||
end
|
||
end
|
||
|
||
--[[
|
||
ZREM 命令:从有序集合移除一个或多个成员
|
||
@param key 键名
|
||
@param ... 成员列表
|
||
@return 返回移除的成员数量
|
||
]]
|
||
function redis_conn:zrem(key, ...)
|
||
local args = {"ZREM", key, ...}
|
||
return self:command_ex(args)
|
||
end
|
||
|
||
--[[
|
||
ZCARD 命令:获取有序集合成员数量
|
||
@param key 键名
|
||
@return 返回成员数量
|
||
]]
|
||
function redis_conn:zcard(key)
|
||
return self:command_ex({"ZCARD", key})
|
||
end
|
||
|
||
--[[
|
||
ZSCORE 命令:获取有序集合成员的分数
|
||
@param key 键名
|
||
@param member 成员
|
||
@return 返回分数,成员不存在返回 nil
|
||
]]
|
||
function redis_conn:zscore(key, member)
|
||
local result = self:command_ex({"ZSCORE", key, member})
|
||
-- 分数通常是数字,但为了统一处理,也进行去转义
|
||
if result ~= nil then
|
||
return unescape_value(result)
|
||
end
|
||
return result
|
||
end
|
||
|
||
return redis_conn
|