Files
lotteryLaravel/app/Services/LotterySettings.php

206 lines
6.1 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
namespace App\Services;
use App\Models\LotterySetting;
use Illuminate\Support\Facades\Cache;
/**
* 【配置中心】统一读入口:按 key 取运行期配置,默认带缓存。
*
* - 写入:控制台 / Seeder / 后续管理端可调 {@see put()}
* - 【重要】库里不存在的 key **不写入缓存**,避免长期命中默认值导致后台新增配置不生效。
*/
final class LotterySettings
{
private const CURRENCY_DISPLAY_DECIMALS = 2;
private const CURRENCY_DECIMAL_SEPARATOR = '.';
private const CURRENCY_THOUSANDS_SEPARATOR = ',';
public static function defaultCurrency(): string
{
$fallback = (string) config('lottery.default_currency', 'NPR');
$value = self::get('currency.default_code', $fallback);
return strtoupper(substr(trim((string) $value), 0, 16));
}
public static function drawTimezone(): string
{
return (string) self::get('draw.timezone', (string) config('lottery.draw.timezone', 'UTC'));
}
public static function drawIntervalMinutes(): int
{
$fallback = (int) config('lottery.draw.interval_minutes', 5);
return max(1, min(1440, (int) self::get('draw.interval_minutes', $fallback)));
}
public static function drawBufferDrawsAhead(): int
{
$fallback = (int) config('lottery.draw.buffer_draws_ahead', 8);
return max(1, (int) self::get('draw.buffer_draws_ahead', $fallback));
}
public static function drawBettingWindowSeconds(): int
{
$fallback = (int) config('lottery.draw.betting_window_seconds', 270);
return max(10, (int) self::get('draw.betting_window_seconds', $fallback));
}
public static function drawCloseBeforeDrawSeconds(): int
{
$fallback = (int) config('lottery.draw.close_before_draw_seconds', 30);
return max(5, (int) self::get('draw.close_before_draw_seconds', $fallback));
}
public static function drawRequireManualReview(): bool
{
$fallback = (bool) config('lottery.draw.require_manual_review', true);
return (bool) self::get('draw.require_manual_review', $fallback);
}
public static function drawCooldownMinutes(): int
{
$fallback = (int) config('lottery.draw.cooldown_minutes', 15);
return max(0, (int) self::get('draw.cooldown_minutes', $fallback));
}
public static function currencyDisplayDecimals(): int
{
return self::CURRENCY_DISPLAY_DECIMALS;
}
public static function currencyDecimalSeparator(): string
{
return self::CURRENCY_DECIMAL_SEPARATOR;
}
public static function currencyThousandsSeparator(): string
{
return self::CURRENCY_THOUSANDS_SEPARATOR;
}
public static function cacheTtlSeconds(): int
{
return max(5, (int) config('lottery.settings.cache_ttl_seconds', 60));
}
/** 取单个配置;若无行则返回 $default不写缓存。 */
public static function get(string $key, mixed $default = null): mixed
{
$cacheKey = self::cacheKey($key);
if (Cache::has($cacheKey)) {
return Cache::get($cacheKey);
}
/** @var LotterySetting|null $row */
$row = LotterySetting::query()->where('setting_key', $key)->first();
if ($row === null) {
return $default;
}
$value = self::normalizeValue($row->value_json);
Cache::put($cacheKey, $value, self::cacheTtlSeconds());
return $value;
}
/**
* 批量读取(逐项走 get已存在的键会受益于缓存
*
* @param array<string, mixed> $keysToDefault key => default
* @return array<string, mixed>
*/
public static function many(array $keysToDefault): array
{
$out = [];
foreach ($keysToDefault as $key => $def) {
$out[$key] = self::get($key, $def);
}
return $out;
}
/** 删单 key 缓存;写入 lottery_settings 后务必调用(或运维 `php artisan cache:clear` */
public static function forgetKey(string $key): void
{
Cache::forget(self::cacheKey($key));
}
/**
* 写入或更新一行配置并刷新该 key 的缓存。Seeder / 后续管理端共用)
*/
public static function put(
string $key,
mixed $value,
string $groupName = 'general',
?string $descriptionZh = null,
): void {
LotterySetting::query()->updateOrCreate(
['setting_key' => $key],
[
'value_json' => $value,
'group_name' => $groupName,
'description_zh' => $descriptionZh,
],
);
self::forgetKey($key);
// 写入后立即预热缓存,下一次 get 可走 Cache::has
Cache::put(self::cacheKey($key), self::normalizeValue($value), self::cacheTtlSeconds());
}
/**
* 批量写入(管理端一次保存多块配置,避免 N 次 HTTP + 审计)。
*
* @param list<array{key: string, value: mixed}> $items
*/
public static function putMany(array $items): void
{
if ($items === []) {
return;
}
$keys = array_values(array_unique(array_map(
static fn (array $item): string => (string) $item['key'],
$items,
)));
/** @var array<string, LotterySetting> $existing */
$existing = LotterySetting::query()
->whereIn('setting_key', $keys)
->get()
->keyBy('setting_key')
->all();
foreach ($items as $item) {
$key = (string) $item['key'];
$value = $item['value'];
$row = $existing[$key] ?? null;
self::put(
$key,
$value,
$row ? (string) $row->group_name : (explode('.', $key)[0] ?? 'general'),
$row ? $row->description_zh : null,
);
}
}
public static function cacheKey(string $key): string
{
return 'lottery_settings:'.$key;
}
private static function normalizeValue(mixed $value): mixed
{
return $value;
}
}