Files
lotteryLaravel/app/Services/LotterySettings.php
kang 0841fbed32 feat: 增强管理员功能与数据处理
- 在多个控制器中引入 agent_node_id,以支持基于代理节点的权限和数据过滤。
- 更新 AdminRole 和 AdminUser 模型,新增角色范围和代理节点相关功能,提升角色管理的灵活性。
- 在请求验证中添加 agent_node_id 字段,确保 API 接口支持代理节点的相关操作。
- 优化 LotterySettings 服务,支持批量写入设置,提升配置管理的效率。
- 更新仪表板和报告服务,增强数据统计功能,确保管理员能够获取更全面的统计信息。
2026-06-02 14:36:58 +08:00

210 lines
6.3 KiB
PHP
Raw Permalink 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
{
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
{
$fallback = (int) config('lottery.ui.format.currency.decimals', 2);
return max(0, min(12, (int) self::get('currency.display_decimals', $fallback)));
}
public static function currencyDecimalSeparator(): string
{
return (string) self::get(
'currency.decimal_separator',
(string) config('lottery.ui.format.currency.decimal_separator', '.')
);
}
public static function currencyThousandsSeparator(): string
{
return (string) self::get(
'currency.thousands_separator',
(string) config('lottery.ui.format.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;
}
}