SimulationSettings.php 3.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182
  1. <?php
  2. namespace App\Services\Simulation;
  3. use App\Constants\HttpStatus;
  4. use App\Support\MemberAudience;
  5. use Exception;
  6. use Illuminate\Support\Facades\DB;
  7. /** 模拟管理的设置(config 表 simulation_* 键)以及每个写操作都要过的两道检查。 */
  8. final class SimulationSettings
  9. {
  10. /** 键 => [默认值, 最小, 最大];最小为 null 的是字符串。 */
  11. public const KEYS = [
  12. 'simulation_enabled' => ['0', 0, 1],
  13. 'simulation_max_concurrency' => ['2', 1, 10],
  14. 'simulation_rate_per_minute' => ['30', 1, 120],
  15. 'simulation_max_members_per_task' => ['20', 1, 200],
  16. 'simulation_max_members_total' => ['500', 1, 5000],
  17. 'simulation_max_amount' => ['10000', 1, 1000000],
  18. 'simulation_initial_balance' => ['0', 0, 1000000],
  19. 'simulation_wager_bypass' => ['1', 0, 1],
  20. 'simulation_default_domain' => ['sim.local', null, null],
  21. ];
  22. public static function all(): array
  23. {
  24. $stored = DB::table('config')->whereIn('field', array_keys(self::KEYS))->pluck('val', 'field');
  25. $out = [];
  26. foreach (self::KEYS as $key => [$default]) $out[$key] = (string) ($stored[$key] ?? $default);
  27. return $out;
  28. }
  29. public static function get(string $key): string
  30. {
  31. return self::all()[$key];
  32. }
  33. public static function save(array $input): array
  34. {
  35. $before = self::all();
  36. foreach (self::KEYS as $key => [, $min, $max]) {
  37. if (!array_key_exists($key, $input)) continue;
  38. $value = trim((string) $input[$key]);
  39. if ($min === null) {
  40. if ($value === '' || strlen($value) > 100) throw new Exception('域名格式不正确', HttpStatus::CUSTOM_ERROR);
  41. } elseif (!is_numeric($value) || $value < $min || $value > $max) {
  42. throw new Exception("{$key} 应在 {$min} 到 {$max} 之间", HttpStatus::CUSTOM_ERROR);
  43. }
  44. $exists = DB::table('config')->where('field', $key)->exists();
  45. if ($exists) DB::table('config')->where('field', $key)->update(['val' => $value]);
  46. else DB::table('config')->insert(['field' => $key, 'val' => $value]);
  47. }
  48. return ['before' => $before, 'after' => self::all()];
  49. }
  50. public static function enabled(): bool
  51. {
  52. return self::get('simulation_enabled') === '1';
  53. }
  54. public static function assertEnabled(): void
  55. {
  56. if (!MemberAudience::enabled()) throw new Exception('用户类型字段尚未迁移,无法使用模拟管理', HttpStatus::CUSTOM_ERROR);
  57. if (!self::enabled()) throw new Exception('模拟功能未开启,请先在“模拟设置”中开启', HttpStatus::CUSTOM_ERROR);
  58. }
  59. /** 写操作的目标必须是后台模拟会员,返回该会员。 */
  60. public static function member($memberId): object
  61. {
  62. $user = DB::table('users')->where('member_id', (string) $memberId)->where('user_type', MemberAudience::TYPE_SIMULATED)->first();
  63. if (!$user) throw new Exception('只能操作模拟会员', HttpStatus::CUSTOM_ERROR);
  64. return $user;
  65. }
  66. public static function assertAmount($amount): string
  67. {
  68. if (!is_numeric($amount) || $amount <= 0) throw new Exception('金额格式不正确', HttpStatus::CUSTOM_ERROR);
  69. if ($amount > (float) self::get('simulation_max_amount')) throw new Exception('金额超过模拟设置的单次上限', HttpStatus::CUSTOM_ERROR);
  70. return number_format((float) $amount, 2, '.', '');
  71. }
  72. }