feat: 增强风险池 Redis 操作,添加 TTL 支持并更新相关 Lua 脚本;新增 API 异常响应测试

This commit is contained in:
2026-06-09 17:06:05 +08:00
parent a0c3b8a1ff
commit 5bd7517ce9
6 changed files with 116 additions and 8 deletions

View File

@@ -151,9 +151,24 @@ final class RiskPoolService
$pool = $this->firstOrMakePool($drawId, $number4d);
$key = $this->redisPoolKey($drawId, $number4d);
Redis::eval($this->initLua(), 1, $key, (int) $pool->total_cap_amount, (int) $pool->locked_amount, (int) $pool->version);
Redis::eval(
$this->initLua(),
1,
$key,
(int) $pool->total_cap_amount,
(int) $pool->locked_amount,
(int) $pool->version,
$this->redisPoolTtlSeconds(),
);
$result = $this->normalizeLuaResult(Redis::eval($this->acquireLua(), 1, $key, $amount, (int) $pool->version));
$result = $this->normalizeLuaResult(Redis::eval(
$this->acquireLua(),
1,
$key,
$amount,
(int) $pool->version,
$this->redisPoolTtlSeconds(),
));
if (($result['code'] ?? null) !== 'OK') {
throw new TicketOperationException('risk_sold_out', ErrorCode::RiskPoolSoldOut->value);
}
@@ -211,6 +226,7 @@ final class RiskPoolService
$locked,
$remaining,
(int) $pool->version,
$this->redisPoolTtlSeconds(),
);
}
@@ -228,11 +244,17 @@ final class RiskPoolService
return "risk_pool:draw:{$drawId}:number:{$number4d}";
}
private function redisPoolTtlSeconds(): int
{
return max(60, (int) config('lottery.risk_pool.redis_ttl_seconds', 86400));
}
private function initLua(): string
{
return <<<'LUA'
if redis.call('EXISTS', KEYS[1]) == 0 then
redis.call('HMSET', KEYS[1], 'total', ARGV[1], 'locked', ARGV[2], 'remaining', ARGV[1] - ARGV[2], 'version', ARGV[3])
redis.call('EXPIRE', KEYS[1], tonumber(ARGV[4]))
end
return 1
LUA;
@@ -242,6 +264,7 @@ LUA;
{
return <<<'LUA'
redis.call('HMSET', KEYS[1], 'total', ARGV[1], 'locked', ARGV[2], 'remaining', ARGV[3], 'version', ARGV[4])
redis.call('EXPIRE', KEYS[1], tonumber(ARGV[5]))
return 1
LUA;
}
@@ -268,6 +291,7 @@ end
local locked = redis.call('HINCRBY', KEYS[1], 'locked', amount)
remaining = redis.call('HINCRBY', KEYS[1], 'remaining', -amount)
version = redis.call('HINCRBY', KEYS[1], 'version', 1)
redis.call('EXPIRE', KEYS[1], tonumber(ARGV[3]))
return {'OK', remaining, locked, version}
LUA;
}
@@ -283,6 +307,7 @@ if locked < releaseAmount then
end
redis.call('HINCRBY', KEYS[1], 'locked', -releaseAmount)
redis.call('HINCRBY', KEYS[1], 'remaining', releaseAmount)
redis.call('EXPIRE', KEYS[1], tonumber(ARGV[2]))
return releaseAmount
LUA;
}
@@ -336,7 +361,13 @@ LUA;
private function releaseRedisLocks(int $drawId, array $locks): void
{
foreach ($locks as $lock) {
Redis::eval($this->releaseLua(), 1, $this->redisPoolKey($drawId, $lock['number_4d']), (int) $lock['amount']);
Redis::eval(
$this->releaseLua(),
1,
$this->redisPoolKey($drawId, $lock['number_4d']),
(int) $lock['amount'],
$this->redisPoolTtlSeconds(),
);
}
}

View File

@@ -14,13 +14,15 @@ use Illuminate\Http\Request;
*/
final class ApiResponse
{
private const JSON_FLAGS = JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES;
public static function success(mixed $data = null, ?string $msg = null, int $code = 0, ?Request $request = null): JsonResponse
{
return response()->json([
'code' => $code,
'msg' => $msg ?? ApiMessage::successMessage($request),
'data' => $data,
]);
], 200, [], self::JSON_FLAGS);
}
public static function error(string $msg, int $code, mixed $data = null, int $httpStatus = 400): JsonResponse
@@ -29,6 +31,6 @@ final class ApiResponse
'code' => $code,
'msg' => $msg,
'data' => $data,
], $httpStatus);
], $httpStatus, [], self::JSON_FLAGS);
}
}