feat(admin): 密码校验统一与后台 API/设置增强
Some checks failed
lotterLaravel CI / test (push) Has been cancelled
lotterLaravel E2E / e2e-api (push) Has been cancelled

- 管理端账号/代理/站点等密码最短长度统一为 6 位
- LotterySettings、审计日志展示、币种与报表接口等小修复
- 补充设置批量更新、注单、校验错误等相关测试
This commit is contained in:
2026-06-26 14:39:22 +08:00
parent 8d5ff0a7ae
commit 3f04b4ebe3
28 changed files with 283 additions and 69 deletions

View File

@@ -13,49 +13,106 @@ use Illuminate\Support\Facades\Cache;
*/
final class LotterySettings
{
/** 仅由 config/.env 控制,后台不可写入;旧 DB 行不再生效。 */
public const ENV_LOCKED_KEYS = [
'currency.default_code',
'draw.timezone',
'draw.interval_minutes',
'draw.betting_window_seconds',
'draw.close_before_draw_seconds',
'draw.buffer_draws_ahead',
];
private const CURRENCY_DISPLAY_DECIMALS = 2;
private const CURRENCY_DECIMAL_SEPARATOR = '.';
private const CURRENCY_THOUSANDS_SEPARATOR = ',';
public static function isEnvLocked(string $key): bool
{
return in_array($key, self::ENV_LOCKED_KEYS, true);
}
public static function defaultCurrency(): string
{
$fallback = (string) config('lottery.default_currency', 'NPR');
$value = self::get('currency.default_code', $fallback);
$value = (string) config('lottery.default_currency', 'NPR');
return strtoupper(substr(trim((string) $value), 0, 16));
return strtoupper(substr(trim($value), 0, 16));
}
public static function drawTimezone(): string
{
return (string) self::get('draw.timezone', (string) config('lottery.draw.timezone', 'UTC'));
return (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)));
return max(1, min(1440, (int) config('lottery.draw.interval_minutes', 5)));
}
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));
return max(1, (int) config('lottery.draw.buffer_draws_ahead', 8));
}
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));
return max(10, (int) config('lottery.draw.betting_window_seconds', 270));
}
public static function drawCloseBeforeDrawSeconds(): int
{
$fallback = (int) config('lottery.draw.close_before_draw_seconds', 30);
return max(5, (int) config('lottery.draw.close_before_draw_seconds', 30));
}
return max(5, (int) self::get('draw.close_before_draw_seconds', $fallback));
/**
* 后台「部署参数」只读快照:供运营查看当前生效值与对应 env 变量名。
*
* @return array{
* items: list<array{key: string, label_key: string, value: mixed, env_var: string}>
* }
*/
public static function deploymentSnapshot(): array
{
return [
'items' => [
[
'key' => 'currency.default_code',
'label_key' => 'default_currency',
'value' => self::defaultCurrency(),
'env_var' => 'LOTTERY_DEFAULT_CURRENCY',
],
[
'key' => 'draw.timezone',
'label_key' => 'draw_timezone',
'value' => self::drawTimezone(),
'env_var' => 'LOTTERY_DRAW_TIMEZONE',
],
[
'key' => 'draw.interval_minutes',
'label_key' => 'draw_interval_minutes',
'value' => self::drawIntervalMinutes(),
'env_var' => 'LOTTERY_DRAW_INTERVAL_MINUTES',
],
[
'key' => 'draw.betting_window_seconds',
'label_key' => 'draw_betting_window_seconds',
'value' => self::drawBettingWindowSeconds(),
'env_var' => 'LOTTERY_DRAW_BETTING_WINDOW_SECONDS',
],
[
'key' => 'draw.close_before_draw_seconds',
'label_key' => 'draw_close_before_draw_seconds',
'value' => self::drawCloseBeforeDrawSeconds(),
'env_var' => 'LOTTERY_DRAW_CLOSE_BEFORE_SECONDS',
],
[
'key' => 'draw.buffer_draws_ahead',
'label_key' => 'draw_buffer_draws_ahead',
'value' => self::drawBufferDrawsAhead(),
'env_var' => 'LOTTERY_DRAW_BUFFER_AHEAD',
],
],
];
}
public static function drawRequireManualReview(): bool