You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
434 lines
11 KiB
434 lines
11 KiB
<?php
|
|
// +----------------------------------------------------------------------
|
|
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
|
// +----------------------------------------------------------------------
|
|
// | Copyright (c) 2016~2020 https://www.crmeb.com All rights reserved.
|
|
// +----------------------------------------------------------------------
|
|
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
|
// +----------------------------------------------------------------------
|
|
// | Author: CRMEB Team <admin@crmeb.com>
|
|
// +----------------------------------------------------------------------
|
|
|
|
namespace crmeb\services;
|
|
|
|
use think\facade\Cache as CacheStatic;
|
|
|
|
/**
|
|
* crmeb 缓存类
|
|
* Class CacheService
|
|
* @package crmeb\services
|
|
* @mixin \Redis
|
|
*/
|
|
class CacheService
|
|
{
|
|
|
|
//副屏相关
|
|
const CASHIER_AUX_SCREEN_TAG = 'auxScreen';
|
|
|
|
/**
|
|
* 标签名
|
|
* @var string
|
|
*/
|
|
protected static $globalCacheName = '_cached_1515146130';
|
|
|
|
/**
|
|
* 缓存队列key
|
|
* @var string[]
|
|
*/
|
|
protected static $redisQueueKey = [
|
|
0 => 'product',
|
|
1 => 'seckill',
|
|
2 => 'bargain',
|
|
3 => 'combination',
|
|
4 => 'integral',
|
|
5 => 'discounts',
|
|
6 => 'lottery'
|
|
];
|
|
|
|
/**
|
|
* 过期时间
|
|
* @var int
|
|
*/
|
|
protected static $expire;
|
|
|
|
/**
|
|
* 获取缓存过期时间
|
|
* @param int|null $expire
|
|
* @return int
|
|
*/
|
|
protected static function getExpire(int $expire = null): int
|
|
{
|
|
if (self::$expire) {
|
|
return (int)self::$expire;
|
|
}
|
|
$expire = !is_null($expire) ? $expire : SystemConfigService::get('cache_config', null, true);
|
|
if (!is_int($expire))
|
|
$expire = (int)$expire;
|
|
return self::$expire = $expire;
|
|
}
|
|
|
|
/**
|
|
* 判断缓存是否存在
|
|
* @param string $name
|
|
* @return bool
|
|
* @throws \Psr\SimpleCache\InvalidArgumentException
|
|
*/
|
|
public static function has(string $name): bool
|
|
{
|
|
try {
|
|
return CacheStatic::has($name);
|
|
} catch (\Throwable $e) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 写入缓存
|
|
* @param string $name 缓存名称
|
|
* @param mixed $value 缓存值
|
|
* @param int $expire 缓存时间,为0读取系统缓存时间
|
|
* @return bool
|
|
*/
|
|
public static function set(string $name, $value, int $expire = null): bool
|
|
{
|
|
try {
|
|
return self::handler()->set($name, $value, $expire ?? self::getExpire($expire));
|
|
} catch (\Throwable $e) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 如果不存在则写入缓存
|
|
* @param string $name
|
|
* @param bool $default
|
|
* @return mixed
|
|
*/
|
|
public static function get(string $name, $default = false, int $expire = null)
|
|
{
|
|
try {
|
|
return self::handler()->remember($name, $default, $expire ?? self::getExpire($expire));
|
|
} catch (\Throwable $e) {
|
|
try {
|
|
if (is_callable($default)) {
|
|
return $default();
|
|
} else {
|
|
return $default;
|
|
}
|
|
} catch (\Throwable $e) {
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 如果不存在则写入缓存
|
|
* @param string $name
|
|
* @param bool $default
|
|
* @return mixed
|
|
*/
|
|
public static function remember(string $name, $default = false, int $expire = null)
|
|
{
|
|
try {
|
|
return self::handler()->remember($name, $default, $expire ?? self::getExpire($expire));
|
|
} catch (\Throwable $e) {
|
|
try {
|
|
if (is_callable($default)) {
|
|
return $default();
|
|
} else {
|
|
return $default;
|
|
}
|
|
} catch (\Throwable $e) {
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 删除缓存
|
|
* @param string $name
|
|
* @return bool
|
|
* @throws \Psr\SimpleCache\InvalidArgumentException
|
|
*/
|
|
public static function delete(string $name)
|
|
{
|
|
return CacheStatic::delete($name);
|
|
}
|
|
|
|
/**
|
|
* 缓存句柄
|
|
*
|
|
* @return \think\cache\TagSet|CacheStatic
|
|
*/
|
|
public static function handler(?string $cacheName = null)
|
|
{
|
|
return CacheStatic::tag($cacheName ?: self::$globalCacheName);
|
|
}
|
|
|
|
/**
|
|
* 清空缓存池
|
|
* @return bool
|
|
*/
|
|
public static function clear()
|
|
{
|
|
return self::handler()->clear();
|
|
}
|
|
|
|
/**
|
|
* Redis缓存句柄
|
|
*
|
|
* @return \think\cache\TagSet|CacheStatic
|
|
*/
|
|
public static function redisHandler(string $type = null)
|
|
{
|
|
if ($type) {
|
|
return CacheStatic::store('redis')->tag($type);
|
|
} else {
|
|
return CacheStatic::store('redis');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 放入令牌桶
|
|
* @param string $key
|
|
* @param array $value
|
|
* @param string $type
|
|
* @return bool
|
|
*/
|
|
public static function setTokenBucket(string $key, $value, $expire = null, string $type = 'admin')
|
|
{
|
|
try {
|
|
$redisCahce = self::redisHandler($type);
|
|
return $redisCahce->set($key, $value, $expire);
|
|
} catch (\Throwable $e) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 清除所有令牌桶
|
|
* @param string $type
|
|
* @return bool
|
|
*/
|
|
public static function clearTokenAll(string $type = 'admin')
|
|
{
|
|
try {
|
|
return self::redisHandler($type)->clear();
|
|
} catch (\Throwable $e) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 清除令牌桶
|
|
* @param string $type
|
|
* @return bool
|
|
*/
|
|
public static function clearToken(string $key)
|
|
{
|
|
try {
|
|
return self::redisHandler()->delete($key);
|
|
} catch (\Throwable $e) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 查看令牌是否存在
|
|
* @param string $key
|
|
* @return bool
|
|
*/
|
|
public static function hasToken(string $key)
|
|
{
|
|
try {
|
|
return self::redisHandler()->has($key);
|
|
} catch (\Throwable $e) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 获取token令牌桶
|
|
* @param string $key
|
|
* @return mixed|null
|
|
* @throws \Psr\SimpleCache\InvalidArgumentException
|
|
*/
|
|
public static function getTokenBucket(string $key)
|
|
{
|
|
try {
|
|
return self::redisHandler()->get($key, null);
|
|
} catch (\Throwable $e) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 获取指定分数区间的成员
|
|
* @param $key
|
|
* @param int $start
|
|
* @param int $end
|
|
* @param array $options
|
|
* @return mixed
|
|
*/
|
|
public static function zRangeByScore($key, $start = '-inf', $end = '+inf', array $options = [])
|
|
{
|
|
return self::redisHandler()->zRangeByScore($key, $start, $end, $options);
|
|
}
|
|
|
|
|
|
/**
|
|
* 设置redis入库队列
|
|
* @param string $unique
|
|
* @param int $number
|
|
* @param int $type
|
|
* @param bool $isPush true :重置 false:累加
|
|
* @return bool
|
|
*/
|
|
public static function setStock(string $unique, int $number, int $type = 1, bool $isPush = true)
|
|
{
|
|
if (!$unique || !$number) return false;
|
|
$name = (self::$redisQueueKey[$type] ?? '') . '_' . $type . '_' . $unique;
|
|
/** @var self $cache */
|
|
$cache = self::redisHandler();
|
|
$res = true;
|
|
if ($isPush) {
|
|
$cache->del($name);
|
|
}
|
|
$data = [];
|
|
for ($i = 1; $i <= $number; $i++) {
|
|
$data[] = $i;
|
|
}
|
|
$res = $res && $cache->lPush($name, ...$data);
|
|
return $res;
|
|
}
|
|
|
|
/**
|
|
* 弹出redis队列中的库存条数
|
|
* @param string $unique
|
|
* @param int $number
|
|
* @param int $type
|
|
* @return bool
|
|
*/
|
|
public static function popStock(string $unique, int $number, int $type = 1)
|
|
{
|
|
if (!$unique || !$number) return false;
|
|
$name = (self::$redisQueueKey[$type] ?? '') . '_' . $type . '_' . $unique;
|
|
/** @var self $cache */
|
|
$cache = self::redisHandler();
|
|
$res = true;
|
|
if ($number > $cache->lLen($name)) {
|
|
return false;
|
|
}
|
|
for ($i = 1; $i <= $number; $i++) {
|
|
$res = $res && $cache->lPop($name);
|
|
}
|
|
|
|
return $res;
|
|
}
|
|
|
|
/**
|
|
* 是否有库存|返回库存
|
|
* @param string $unique
|
|
* @param int $number
|
|
* @param int $type
|
|
* @return bool
|
|
*/
|
|
public static function checkStock(string $unique, int $number = 0, int $type = 1)
|
|
{
|
|
$name = (self::$redisQueueKey[$type] ?? '') . '_' . $type . '_' . $unique;
|
|
if ($number) {
|
|
return self::redisHandler()->lLen($name) >= $number;
|
|
} else {
|
|
return self::redisHandler()->lLen($name);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 检查锁
|
|
* @param int $uid
|
|
* @param int $timeout
|
|
* @return bool
|
|
* @author 等风来
|
|
* @email 136327134@qq.com
|
|
* @date 2022/11/22
|
|
*/
|
|
public static function setMutex(string $key, int $timeout = 10)
|
|
{
|
|
$curTime = time();
|
|
$readMutexKey = "redis:mutex:{$key}";
|
|
$mutexRes = self::redisHandler()->handler()->setnx($readMutexKey, $curTime + $timeout);
|
|
if ($mutexRes) {
|
|
return true;
|
|
}
|
|
//就算意外退出,下次进来也会检查key,防止死锁
|
|
$time = self::redisHandler()->handler()->get($readMutexKey);
|
|
if ($curTime > $time) {
|
|
self::redisHandler()->handler()->del($readMutexKey);
|
|
return self::redisHandler()->handler()->setnx($readMutexKey, $curTime + $timeout);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* 删除锁
|
|
* @param $uid
|
|
* @author 等风来
|
|
* @email 136327134@qq.com
|
|
* @date 2022/11/22
|
|
*/
|
|
public static function delMutex(string $key)
|
|
{
|
|
$readMutexKey = "redis:mutex:{$key}";
|
|
self::redisHandler()->handler()->del($readMutexKey);
|
|
}
|
|
|
|
/**
|
|
* 数据库锁
|
|
* @param $key
|
|
* @param $fn
|
|
* @param int $ex
|
|
* @return bool|mixed
|
|
*/
|
|
public static function lock($key, $fn = [], int $ex = 10)
|
|
{
|
|
$service = app()->make(LockService::class);
|
|
if ($fn instanceof \Closure) {
|
|
return $service->exec($key, $fn, $ex);
|
|
} else {
|
|
return $service->lock($key, 1, $ex);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 销毁锁
|
|
* @param $key
|
|
* @return bool
|
|
*/
|
|
public static function unLock($key)
|
|
{
|
|
return app()->make(LockService::class)->unlock($key);
|
|
}
|
|
|
|
/**
|
|
* 魔术方法
|
|
* @param $name
|
|
* @param $arguments
|
|
* @return mixed
|
|
*/
|
|
public static function __callStatic($name, $arguments)
|
|
{
|
|
return self::redisHandler()->{$name}(...$arguments);
|
|
}
|
|
|
|
/**
|
|
* 魔术方法
|
|
* @param $name
|
|
* @param $arguments
|
|
* @return mixed
|
|
*/
|
|
public function __call($name, $arguments)
|
|
{
|
|
return self::redisHandler()->{$name}(...$arguments);
|
|
}
|
|
|
|
}
|
|
|