PostgreSQL下用于操作Redis的存储过程

1. 依耐关系(首先需要安装Redis_fdw)

关于Redis_fdw 详见:https://pgxn.org/dist/redis_fdw/

CREATE EXTENSION IF NOT EXISTS redis_fdw;
CREATE SERVER redis_server FOREIGN DATA WRAPPER redis_fdw OPTIONS (address '127.0.0.1', port '6379');
CREATE USER MAPPING FOR PUBLIC SERVER redis_server OPTIONS (password '');

2. 操作方法、使用范列

  • structure_redis 存储过程可以用于某个表的触发器中,或其他数据逻辑操作的存储过程中
  • 当数据层某个表的数据发生变化时运行该存储过程快速创建、更新或删除Redis数据,应用层不需要去操作Redis,只需要读取Redis即可
  • 会在数据库中生成一个"public"."police_redis_foreign"的数据表,用于存放Redis服务器数据库MAP(0~15),请自行规划好Redis数据存放位置
  • Redis服务器数据库15位置处,KEY 值为indexedDBStorage_foreignList:redisForeign存放着"public"."police_redis_foreign"的数据表的数据,方便应用层查找调用数据
  • RedisKEY值生成规则:storage_store:pk

2.1 清空Redis缓存

SELECT structure_redis(json_build_object(
    'type', 'empty',
    'foreign', '外部表名称(不指定则清空所有)',
    'redis', 'police_redis(默认)'
);

2.2. 创建或刷新一条JSON对象形式的Redis缓存记录,用于单一数据的缓存

  • 2.2.1 将指定表tablewhere查询结果的第一行记录以JSON对象形式进行Redis数据缓存

SELECT structure_redis(
    json_build_object(
        'pk', 4,                                    -- 数据主键值
        'type', 'info',                             -- 数据类型, 必须指定为`info`
        'databases', 1,                             -- 指定Redis的databases,范围:0~15, 默认:0
        'storage', 'sessionStorage',                -- 前端存储位置, 用于构造Redis的Key,默认indexedDBStorage 
        'store', 'userInfo',                        -- 前端存储store, 用于构造Redis的Key
        'table', 'police_view_user',                -- 数据查询表名
        'where', 'uid=4'                            -- 数据查询条件
    )
);
  • 2.2.2 将指定表table的主键primary等于pk的查询结果的第一行记录以JSON对象形式进行Redis数据缓存

SELECT structure_redis(
    json_build_object(
        'pk', 4,                                    -- 数据主键值
        'type', 'info',                             -- 数据类型, 必须指定为`info`
        'databases', 1,                             -- 指定Redis的databases,范围:0~15, 默认:0
        'storage', 'sessionStorage',                -- 前端存储位置, 用于构造Redis的Key,默认indexedDBStorage
        'store', 'userInfo',                        -- 前端存储store, 用于构造Redis的Key
        'table', 'police_view_user',                -- 数据查询表名
        'primary', 'uid'                            -- 查询主键列名
    )
);

2.3 将指定表tablewhere查询结果以JSON数组对象形式创建或刷新一条Redis缓存记录,用于数据列表的缓存

SELECT structure_redis(
    json_build_object(
        'pk', 'policerList',                            -- 数据PK值(此处并非主键值,用以标注唯一性)
        'type', 'list',                                 -- 数据类型,可以不必指定,如果指定必须指定为`list`
        'databases', 1,                                 -- 指定Redis的databases,范围:0~15, 默认:0
        'storage', 'indexedDBStorage',                  -- 前端存储位置, 用于构造Redis的Key,默认indexedDBStorage
        'store', 'userList',                            -- 前端存储store, 用于构造Redis的Key
        'table', 'police_view_user_list',               -- 数据查询表名
        'where', 'user_group::JSONB @> json_build_array(''police'')::JSONB'    -- 数据查询条件
    )
);

2.4 将指定数据data进行Redis数据缓存

SELECT structure_redis(
    json_build_object(
        'pk', 4,
        'type', 'info',                             -- 数据类型, 必须指定为`info`
        'databases', 1,                             -- 指定Redis的databases,范围:0~15, 默认:0
        'storage', 'sessionStorage',
        'store', 'userInfo',
        'data', '需要进行Redis缓存的数据'
    )
);

2.5 删除一条指定pkRedis缓存记录

SELECT structure_redis(
    json_build_object(
        'pk', 4,
        'storage', 'sessionStorage',
        'store', 'userInfo'
    )
);

3 参数说明

参数参数说明可选值类型是否必填默认值
type数据或操作类型list、info、remove、emptyENUMfalselist
schemas缓存分区,可用于按用户组或项目功能进行数据可见性的分区NULLstringfalseapp
storage前端本地存储方式NULLstring除完全清空Redis外均必填NULL
store前端本地存储store名NULLstring除完全清空Redis外均必填NULL
table缓存所需的数据库表名NULLstring缓存数据时未指定data情况下必填NULL
where缓存所需的数据库查询方法NULLstringfalseNULL
data所需缓存的数据NULLJSON缓存数据时未指定table情况下必填NULL
dbindexRedis数据库ID,该缓存数据在Redis中的位置NULLnumberfalse0
pk当缓存列表数据时为list;当缓存info数据时为数据主键NULLstring or number缓存数据类型为info时必填list
method数据请求方法get、post、put、delete、optionsENUMfalseget
restfulRestFul请求路径NULLstring新增缓存数据时必填NULL
route数据Route请求方式所需的参数NULLJSONfalseNULL

4. 数据表和视图

DROP TYPE IF EXISTS methods;
CREATE TYPE methods AS ENUM('get', 'post', 'put', 'delete', 'options');
DROP TYPE IF EXISTS data_type;
CREATE TYPE data_type AS ENUM('list', 'info', 'empty', 'remove');

CREATE TABLE "public"."base_redis_foreigns" (
    "foreigns" VARCHAR(150) NOT NULL,
    "storage" VARCHAR(50) NOT NULL,
    "store" VARCHAR(50) NOT NULL,
    "table" VARCHAR(200) DEFAULT NULL,
    "where" VARCHAR(200) DEFAULT NULL,
    "type" data_type DEFAULT 'list',
    "dbindex" SMALLINT DEFAULT 0,
    "numbers" INTEGER DEFAULT 0,
    "datetime" TIMESTAMP(3) WITHOUT TIME ZONE NOT NULL DEFAULT now(),
    PRIMARY KEY ("foreigns")
);
COMMENT ON TABLE "public"."base_redis_foreigns" IS 'Redis外部表数据';
COMMENT ON COLUMN "public"."base_redis_foreigns"."foreigns" IS 'Redis外部表名';
COMMENT ON COLUMN "public"."base_redis_foreigns"."storage" IS '前端本地存储方式';
COMMENT ON COLUMN "public"."base_redis_foreigns"."store" IS '前端本地存储store名';
COMMENT ON COLUMN "public"."base_redis_foreigns"."table" IS '数据库查询表名';
COMMENT ON COLUMN "public"."base_redis_foreigns"."where" IS '数据库查询方法';
COMMENT ON COLUMN "public"."base_redis_foreigns"."type" IS '数据缓存类型';
COMMENT ON COLUMN "public"."base_redis_foreigns"."dbindex" IS 'Redis数据库id';
COMMENT ON COLUMN "public"."base_redis_foreigns"."numbers" IS '数据缓存数量';
COMMENT ON COLUMN "public"."base_redis_foreigns"."datetime" IS '最后更新时间';

CREATE TABLE "public"."base_redis_map" (
    "id" SERIAL8,
    "key" VARCHAR(150) NOT NULL,
    "foreigns" VARCHAR(150) NOT NULL,
    "method" methods DEFAULT 'get',
    "restful" VARCHAR(200) DEFAULT NULL,
    "route" JSON DEFAULT NULL,
    "datetime" TIMESTAMP(3) WITHOUT TIME ZONE NOT NULL DEFAULT now(),
    PRIMARY KEY ("id")
);
COMMENT ON TABLE "public"."base_redis_map" IS 'Redis数据请求方式MAP';
COMMENT ON COLUMN "public"."base_redis_map"."id" IS '自增主键';
COMMENT ON COLUMN "public"."base_redis_map"."key" IS 'Redis缓存KEY键名';
COMMENT ON COLUMN "public"."base_redis_map"."foreigns" IS 'Redis外部表名';
COMMENT ON COLUMN "public"."base_redis_map"."method" IS '数据请求方式';
COMMENT ON COLUMN "public"."base_redis_map"."restful" IS '数据请求RestFul路径';
COMMENT ON COLUMN "public"."base_redis_map"."route" IS '数据请求Route方式参数';
COMMENT ON COLUMN "public"."base_redis_map"."datetime" IS '最后更新时间';

DROP VIEW IF EXISTS "public"."base_redis_map_view";
CREATE VIEW "public"."base_redis_map_view" AS
    SELECT
        M."key",
        M."foreigns",
        F."storage",
        F."store",
        F."table",
        F."where",
        F."type",
        F."dbindex",
        F."numbers",
        M."method",
        M."restful",
        M."route"
    FROM "public"."base_redis_map" AS M, "public"."base_redis_foreigns" AS F
    WHERE M.foreigns = F.foreigns;

5. 存储过程

CREATE OR REPLACE FUNCTION structure_redis(
    IN redis JSON
)RETURNS JSON
AS $$
DECLARE
    redis_table_schemas VARCHAR(50) := 'public';              -- 请自行根据实际情况配置数据库的schemas
    redis_table_prefix VARCHAR(50) := 'police';                 -- 请自行根据实际情况配置数据库表名前缀
    redis_table_name VARCHAR(50) := 'redis_foreign';        -- 请自行根据实际情况配置数据库表名
    redis_table VARCHAR(150);
    pk_val VARCHAR(100);
    storage_val VARCHAR(50);
    store_val VARCHAR(50);
    redis_key VARCHAR(100);
    foreign_table VARCHAR(100) DEFAULT NULL;
    foreign_table_array VARCHAR(100)[];
    table_val VARCHAR(100) DEFAULT NULL;
    where_val   VARCHAR(250) DEFAULT NULL;
    type_val VARCHAR(50) DEFAULT 'list';
    redis_table_num INTEGER;
    foreign_table_num INTEGER;
    redis_key_num INTEGER;
    databases SMALLINT DEFAULT 0;
    executesql TEXT;
    data_val JSON DEFAULT NULL;
BEGIN
    IF (json_extract_path_text(redis, 'type') IS NOT NULL) THEN
        type_val := json_extract_path_text(redis, 'type');
    END IF;
    IF (json_extract_path_text(redis, 'redis') IS NOT NULL) THEN
        redis_table := json_extract_path_text(redis, 'redis');
    END IF;
    IF (json_extract_path_text(redis, 'foreign') IS NOT NULL) THEN
        foreign_table := lower(json_extract_path_text(redis, 'foreign'));
    END IF;
    redis_table := '"'||redis_table_schemas||'"."'||redis_table_prefix||'_'||redis_table_name||'"';
    SELECT COUNT(*) INTO redis_table_num FROM information_schema.tables WHERE table_type = 'BASE TABLE' AND table_name = redis_table_prefix||'_'||redis_table_name;
    IF(redis_table_num = 0) THEN
        executesql := 'CREATE TABLE '||redis_table||' (' ||
                                    '"foreigns" VARCHAR(150) NOT NULL,' ||
                                    '"storage" VARCHAR(50) NOT NULL,' ||
                                    '"store" VARCHAR(50) NOT NULL,' ||
                                    '"tables" VARCHAR(200) DEFAULT NULL,' ||
                                    '"databases" SMALLINT DEFAULT 0,' ||
                                    '"numbers" INTEGER DEFAULT 0,' ||
                                    '"datetime" TIMESTAMP(3) WITHOUT TIME ZONE NOT NULL DEFAULT now(),' ||
                                    'PRIMARY KEY ("foreigns")'
                                    ');' ||
                                    'COMMENT ON TABLE ' || redis_table || ' IS ''开发日志记录'';' ||
                                    'COMMENT ON COLUMN ' || redis_table || '."foreigns" IS ''Redis外部表名'';' ||
                                    'COMMENT ON COLUMN ' || redis_table || '."storage" IS ''前端本地存储方式'';' ||
                                    'COMMENT ON COLUMN ' || redis_table || '."store" IS ''前端本地存储store名'';' ||
                                    'COMMENT ON COLUMN ' || redis_table || '."tables" IS ''数据库查询表名'';' ||
                                    'COMMENT ON COLUMN ' || redis_table || '."databases" IS ''Redis数据库id'';' ||
                                    'COMMENT ON COLUMN ' || redis_table || '."numbers" IS ''数据缓存数量'';' ||
                                    'COMMENT ON COLUMN ' || redis_table || '."datetime" IS ''最后更新时间'';';
        EXECUTE executesql;
    END IF;
    IF(lower(type_val) = 'empty') THEN
        IF(foreign_table IS NULL) THEN
            SELECT array_agg(table_name::VARCHAR) INTO foreign_table_array FROM information_schema.tables WHERE table_type = 'FOREIGN TABLE' AND table_name LIKE 'redis_%';
            IF(foreign_table_array IS NOT NULL) THEN
                FOREACH foreign_table IN ARRAY foreign_table_array
                LOOP
                    executesql := 'DELETE FROM '||redis_table||' WHERE foreigns = $1;';
                    EXECUTE executesql USING foreign_table;
                    executesql := 'DELETE FROM "' || foreign_table || '";';
                    EXECUTE executesql;
                    executesql := 'DROP FOREIGN TABLE IF EXISTS "' || foreign_table || '";';
                    EXECUTE executesql;
                END LOOP;
            END IF;
            executesql := 'DROP TABLE '||redis_table||';';
            EXECUTE executesql USING foreign_table;
            RETURN json_build_object('type', 'Success', 'message', '服务器中所有的Redis缓存已经被清空!', 'code', 200);
        ELSE
            executesql := 'DELETE FROM '||redis_table||' WHERE foreigns = $1;';
            EXECUTE executesql USING foreign_table;
            executesql := 'DELETE FROM "' || foreign_table || '";';
            EXECUTE executesql;
            executesql := 'DROP FOREIGN TABLE IF EXISTS "' || foreign_table || '";';
            EXECUTE executesql;
            RETURN json_build_object('type', 'Success', 'message', '服务器中【' || foreign_table || '】的Redis缓存已经被清空!', 'code', 200);
        END IF;
    ELSE
        IF (json_extract_path_text(redis, 'pk') IS NOT NULL) THEN
            pk_val := json_extract_path_text(redis, 'pk');
        ELSE
            RETURN json_build_object('type', 'Error', 'message', 'Redis缓存pk参数不能为空!', 'code', 230);
        END IF;
        IF (json_extract_path_text(redis, 'storage') IS NOT NULL) THEN
            storage_val := json_extract_path_text(redis, 'storage');
        ELSE
            RETURN json_build_object('type', 'Error', 'message', 'Redis缓存storage参数不能为空!', 'code', 230);
        END IF;
        IF (json_extract_path_text(redis, 'store') IS NOT NULL) THEN
            store_val := json_extract_path_text(redis, 'store');
        ELSE
            RETURN json_build_object('type', 'Error', 'message', 'Redis缓存store参数不能为空!', 'code', 230);
        END IF;
        IF (json_extract_path_text(redis, 'table') IS NOT NULL) THEN
            table_val := json_extract_path_text(redis, 'table');
        END IF;
        IF (json_extract_path_text(redis, 'primary') IS NOT NULL) THEN
            where_val := json_extract_path_text(redis, 'primary') || ' = ' || pk_val;
        ELSE
            IF (json_extract_path_text(redis, 'where') IS NOT NULL) THEN
                where_val := json_extract_path_text(redis, 'where');
            END IF;
        END IF;
        IF (json_extract_path_text(redis, 'data') IS NOT NULL) THEN
            data_val := json_extract_path_text(redis, 'data');
        END IF;
        IF (json_extract_path_text(redis, 'databases') IS NOT NULL) THEN
            databases := json_extract_path_text(redis, 'databases');
        END IF;
        IF(foreign_table IS NULL) THEN
            foreign_table := 'redis_' || storage_val || '_' || store_val;
        END IF;
        SELECT COUNT(*) INTO foreign_table_num FROM information_schema.tables WHERE table_type = 'FOREIGN TABLE' AND table_name = foreign_table;
        if(foreign_table_num = 0) THEN
            executesql := 'CREATE FOREIGN TABLE "' || foreign_table || '" ("key" text, "val" json) SERVER redis_server OPTIONS ( database ''' || databases || ''');';
            EXECUTE executesql;
            executesql := 'INSERT INTO '||redis_table||' ("foreigns", "storage", "store", "tables", "databases") VALUES($1, $2, $3, $4, $5);';
            EXECUTE executesql USING foreign_table, storage_val, store_val, table_val, databases;
        ELSE
            executesql := 'SELECT databases FROM '||redis_table||' WHERE foreigns = $1;';
            EXECUTE executesql INTO databases USING foreign_table;
        END IF;
        redis_key := storage_val || '_' || store_val || ':' || pk_val;
        executesql := 'SELECT COUNT(*) FROM "' || foreign_table || '" WHERE "key" = $1;';
        EXECUTE executesql INTO redis_key_num USING redis_key;
        IF (table_val IS NULL AND where_val IS NULL AND data_val IS NULL) THEN
            IF (redis_key_num > 0) THEN
                executesql := 'UPDATE "'||redis_table||' SET numbers = numbers - 1, datetime = now() WHERE foreigns = $1;';
                EXECUTE executesql USING foreign_table;
                executesql := 'DELETE FROM "' || foreign_table || '" WHERE "key" = $1;';
                EXECUTE executesql USING redis_key;
                IF(pk_val != 'redisForeign') THEN
                    PERFORM structure_redis(json_build_object(
                                                                        'pk', 'redisForeign',
                                                                        'databases', 15,
                                                                        'storage', 'indexedDBStorage',
                                                                        'store', 'foreignList',
                                                                        'table', redis_table_prefix||'_'||redis_table_name
                                                                ));
                END IF;
                RETURN json_build_object('type', 'Success', 'message', '删除Redis缓存['||redis_key||']成功!', 'databases', databases, 'code', 230);
            ELSE
                RETURN json_build_object('type', 'Error', 'message', '未发现['||redis_key||']的Redis缓存!', 'code', 230);
            END IF;
        ELSE
            IF(table_val IS NULL AND data_val IS NULL)THEN
                RETURN json_build_object('type', 'Error', 'message', 'Redis缓存table参数和data参数不能同时为空!', 'code', 230);
            ELSE
                IF (table_val IS NOT NULL) THEN
                    IF(where_val IS NULL) THEN
                        IF(lower(type_val) = 'info') THEN
                            executesql := 'SELECT json_agg(' || table_val || ')->0 FROM "' || table_val || '" LIMIT 1;';
                        ELSE
                            executesql := 'SELECT json_agg(' || table_val || ') FROM "' || table_val || '";';
                        END IF;
                    ELSE
                        IF(lower(type_val) = 'info') THEN
                            executesql := 'SELECT json_agg(' || table_val || ')->0 FROM "' || table_val || '" WHERE ' || where_val || ' LIMIT 1;';
                        ELSE
                            executesql := 'SELECT json_agg(' || table_val || ') FROM "' || table_val || '" WHERE ' || where_val || ';';
                        END IF;
                    END IF;
                    EXECUTE executesql INTO data_val;
                END IF;
            END IF;
            IF(lower(type_val) = 'list') THEN
                IF(data_val IS NULL) THEN
                    data_val := json_build_array();
                END IF;
            ELSE
                IF(data_val IS NULL) THEN
                    data_val := json_build_object();
                END IF;
            END IF;
            IF (redis_key_num > 0) THEN
                executesql := 'UPDATE "' || foreign_table || '" SET "val" = $1 WHERE "key" = $2;';
                EXECUTE executesql USING data_val, redis_key;
                executesql := 'UPDATE '|| redis_table || ' SET datetime = now() WHERE foreigns = $1;';
                EXECUTE executesql USING foreign_table;
                IF(pk_val != 'redisForeign') THEN
                    PERFORM structure_redis(json_build_object(
                                                                        'pk', 'redisForeign',
                                                                        'databases', 15,
                                                                        'storage', 'indexedDBStorage',
                                                                        'store', 'foreignList',
                                                                        'table', redis_table_prefix||'_'||redis_table_name
                                                                ));
                END IF;
                RETURN json_build_object(
                        'type', 'Success',
                        'message', '更新Redis缓存['||redis_key||']成功!',
                        'code', 200,
                        'debug', json_build_object(
                                'pk', pk_val,
                                'table', table_val,
                                'where', where_val
                        ),
                        'redis', json_build_array(
                                json_build_object(
                                        'pk', pk_val,
                                        'databases', databases,
                                        'storage', storage_val,
                                        'store', store_val,
                                        'table', store_val,
                                        'type', type_val,
                                        'key', redis_key
                                )
                        )
                );
            ELSE
                executesql := 'INSERT INTO "' || foreign_table || '" ("key", "val") VALUES ($1, $2);';
                EXECUTE executesql USING redis_key, data_val;
                executesql := 'UPDATE '||redis_table||' SET numbers = numbers + 1, datetime = now() WHERE foreigns = $1;';
                EXECUTE executesql USING foreign_table;
                IF(pk_val != 'redisForeign') THEN
                    PERFORM structure_redis(json_build_object(
                                                                        'pk', 'redisForeign',
                                                                        'databases', 15,
                                                                        'storage', 'indexedDBStorage',
                                                                        'store', 'foreignList',
                                                                        'table', redis_table_prefix||'_'||redis_table_name
                                                                ));
                END IF;
                RETURN json_build_object(
                        'type', 'Success',
                        'message', '添加Redis缓存['||redis_key||']成功!',
                        'code', 230,
                        'debug', json_build_object(
                                'pk', pk_val,
                                'table', table_val,
                                'where', where_val
                        ),
                        'redis', json_build_array(
                                json_build_object(
                                        'pk', pk_val,
                                        'databases', databases,
                                        'storage', storage_val,
                                        'store', store_val,
                                        'table', store_val,
                                        'type', type_val,
                                        'key', redis_key
                                )
                        )
                );
            END IF;
        END IF;
    END IF;
    EXCEPTION WHEN OTHERS THEN
    RETURN json_build_object('type', 'Error', 'message', '系统Redis缓存操作失败!', 'error', replace(SQLERRM, '"', '`'), 'sqlstate', SQLSTATE);
END;
$$ LANGUAGE plpgsql;

4. 基于ThinkPHP 5.1Redis 操作模型

  • A. 调用方法

    • A.1 清空所有Redis数据

    $redisData = model\Base::structureRedis(['type'  => 'empty']);
    
    • A.2 删除指定RedisKEY数据

    删除KEYsessionStorage_userInfo:5Redis数据

    $redisData = model\Base::structureRedis([
                      'pk'        => 5,
                      'type'      => 'empty',
                      'storage'   => 'sessionStorage',
                      'store'     => 'userInfo'
    ]);
    
    • A.3 获取指定Redis数据,如果不存在则根据tablewhere创建Redis数据

    获取KEY值为sessionStorage_userInfo:5Redis数据,如果该数据不存在则根据tablewhere创建Redis数据

    $return = model\Base::structureRedis([
                      'pk'        => 5,
                      'type'      => 'info',
                      'databases' => 1,
                      'storage'   => 'sessionStorage',
                      'store'     => 'userInfo',
                      'table'     => 'public_user',
                      'where'     => 'uid = 5'
                  ]);
    
    • A.4 获取指定Redis数据,如果不存在则根据tableprimary创建Redis数据

    获取KEY值为sessionStorage_userInfo:5Redis数据,如果该数据不存在则根据tableprimary创建Redis数据

    $return = model\Base::structureRedis([
                      'pk'        => 5,
                      'type'      => 'info',
                      'databases' => 1,
                      'storage'   => 'sessionStorage',
                      'store'     => 'userInfo',
                      'table'     => 'public_user',
                      'primary'   => 'uid'
                  ]);
    
    • A.5 获取指定Redis数据,强制根据tableprimary创建Redis数据

    强制根据tableprimary创建Redis数据后, 返回KEY值为sessionStorage_userInfo:5Redis数据

    $return = model\Base::structureRedis([
                      'pk'        => 5,
                      'type'      => 'info',
                      'databases' => 1,
                      'storage'   => 'sessionStorage',
                      'store'     => 'userInfo',
                      'table'     => 'public_user',
                      'where'     => 'uid = 5'
                  ], true);
    
  • B. 程序代码

namespace app\redis\model;

use think\Model;
use think\Db;

class Base extends Model{
     /**
     * # 数组数据按条件筛选方法
     * @param $array    待筛选的数组
     * @param $filter   筛选条件: ['key' => '筛选键值名', 'val' => '筛选值', 'operator' => '筛选运算符']
     * @return mixed    返回匹配的数组值
     */
    protected static function arrayFilter($array, $filter){
        $filter = array_filter($array, function($val) use ($filter){
            switch($filter['operator']){
                case 'regexp':
                    return preg_match($filter['val'], $val[$filter['key']]);
                case 'between':
                    return $val[$filter['key']] >= $filter['val'][0] && $val[$filter['key']] <= $filter['val'][1];
                case '<':
                    return $val[$filter['key']] < $filter['val'];
                case '<=':
                    return $val[$filter['key']] <= $filter['val'];
                case '>':
                    return $val[$filter['key']] > $filter['val'];
                case '>=':
                    return $val[$filter['key']] >= $filter['val'];
                case '=':
                default:
                    return $val[$filter['key']] == $filter['val'];
            }
        });
        foreach(array_keys($filter) as $v){
            $return[] = $filter[$v];
        }
        return $return;
    }

    /**
     * # 运行数据库存储过程
     * @param       $name       存储过程名
     * @param array $param      存储过程参数数组或字符串
     * @param string $type      数据链接类型,默认:thinkphp,可选:adodb
     * @return mixed
     */
    protected static function callProcedure($name, $param = array(), $type = 'thinkphp'){
        if($type == 'adodb'){
            $db_conf = config('database.');
            $db = ADONewConnection($db_conf['type']);
            $db->Connect($db_conf['hostname'], $db_conf['username'], $db_conf['password'], $db_conf['database']);
            $db->setFetchMode(ADODB_FETCH_ASSOC);
            if(is_array($param))
                $logic = $db->getAll("SELECT ".$name."('".json_encode($param)."');");
            else
                $logic = $db->getAll("SELECT ".$name."('".$param."');");
        }else{
            if(is_array($param))
                $logic = Db::query("SELECT ".$name."('".json_encode($param)."');");
            else
                $logic = Db::query("SELECT ".$name."('".$param."');");
        }
        return json_decode($logic[0][$name], true);
    }

    /**
     * # 链接Redis服务器
     * @param int $dbindex  链接数据库index,默认:0,范围:0~15
     * @return \Redis
     */
    protected static function connectRedis($dbindex = 0){
        $redis = new \Redis();
        $conf = config('redis.');
        $redis->connect($conf['host'], $conf['port'], $conf['timeout']);
        $redis->auth($conf['password']);//登录验证密码,返回【true | false】
        $redis->select($dbindex);
        return $redis;
    }

    /**
     * # 操作redis方法
     * @param      $param       操作参数
     * @param bool $refresh     是否强制刷新
     * @return array|mixed
     */
    public static function structureRedis($param, $refresh = false){
        if(array_key_exists('storage', $param) && array_key_exists('store', $param)){
            $redis = self::connectRedis(15);
            $map = $redis->get('indexedDBStorage_foreignList:redisForeign');
            $filter = self::arrayFilter(json_decode($map, true), ['key' => 'foreigns', 'val' => 'redis_'.$param['storage'].'_'.$param['store']]);
            if(count($filter[0]) > 0){
                $foreign = array_merge($param, $filter[0]);
                if(array_key_exists('tables', $foreign)) $foreign['table'] = $foreign['tables'];
            }else{
                $foreign = $param;
                $foreign['databases'] = (array_key_exists('databases', $param))?$param['databases']:0;
            }
            $redis = self::connectRedis($foreign['databases']);
            if(array_key_exists('pk', $foreign)){
                $key = $foreign['storage'].'_'.$foreign['store'].':'.$foreign['pk'];
            }else{
                if(array_key_exists('key', $foreign)){
                    $key = $foreign['key'];
                }else{
                    return ['type' => 'Error', 'message' => '参数不正确,缺少pk或者key参数', 'param' => $foreign];
                }
            }
            if(!$redis->get($key) || $refresh){
                if(array_key_exists('table', $foreign) || array_key_exists('data', $foreign)){
                    $logic = self::callProcedure('structure_redis', $foreign);
                    if(strtolower($logic['type']) == 'error') return $logic;
                }else{
                    return ['type' => 'Error', 'message' => '参数不正确,缺少table或者data参数', 'param' => $foreign];
                }
            }
            return json_decode($redis->get($key), true);
        }else{
            if(array_key_exists('type', $param) && $param['type'] == 'empty'){
                $logic = self::callProcedure('structure_redis', $param);
                return json_decode($logic, true);
            }else{
                return ['type' => 'Error', 'message' => '参数不正确,缺少storage或者store参数', 'param' => $param];
            }
        }
    }
}
    原文作者:GadflyBSD
    原文地址: https://www.jianshu.com/p/b284c369e79f
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞