RechargeChannelLogoService.php 4.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
  1. <?php
  2. namespace App\Services\Payment;
  3. use App\Constants\Util;
  4. use App\Models\RechargeChannel;
  5. use App\Services\OperationAuditService;
  6. use Illuminate\Support\Facades\DB;
  7. use Illuminate\Support\Facades\Schema;
  8. use Illuminate\Support\Facades\Validator;
  9. use Illuminate\Validation\ValidationException;
  10. final class RechargeChannelLogoService
  11. {
  12. public static function normalize($logo): ?string
  13. {
  14. Validator::make(['logo' => $logo], ['logo' => ['nullable', 'string', 'max:2048']])->validate();
  15. $logo = trim((string)($logo ?? '')); $parts = parse_url($logo);
  16. $relative = str_starts_with($logo, '/') && !str_starts_with($logo, '//');
  17. $absolute = filter_var($logo, FILTER_VALIDATE_URL) && $parts && in_array(strtolower($parts['scheme'] ?? ''), ['http', 'https'], true)
  18. && !isset($parts['user']) && !isset($parts['pass']);
  19. if ($logo !== '' && (preg_match('/[\x00-\x20\x7f\\\\]/', $logo) || (!$relative && !$absolute))) {
  20. throw ValidationException::withMessages(['logo' => '请使用上传后的图片路径或HTTP/HTTPS图片地址']);
  21. }
  22. return $logo === '' ? null : $logo;
  23. }
  24. public static function configured(array $types): array
  25. {
  26. if (!Schema::hasTable('recharge_method_logos')) return [];
  27. return DB::table('recharge_method_logos')->whereIn('channel_type', $types)->pluck('logo', 'channel_type')->all();
  28. }
  29. public function options(): array
  30. {
  31. $rows = [];
  32. foreach (PaymentProviderCatalog::channelFormOptions(1)['types'] as $type) {
  33. $rows[$type['value']] = ['channel_type' => $type['value'],
  34. 'channel_name' => ($type['payment_company_label'] === '' ? '' : $type['payment_company_label'] . ' / ') . $type['label'], 'logo' => ''];
  35. }
  36. $fallback = [];
  37. foreach (RechargeChannel::where('data_type', 1)->orderBy('sort')->orderBy('id')->get() as $channel) {
  38. if (!isset($rows[$channel->type])) $rows[$channel->type] = ['channel_type' => $channel->type, 'channel_name' => $channel->name, 'logo' => ''];
  39. if (!isset($fallback[$channel->type]) && $channel->logo !== '') $fallback[$channel->type] = $channel->logo;
  40. }
  41. $configured = self::configured(array_keys($rows));
  42. foreach ($rows as $type => &$row) {
  43. $logo = array_key_exists($type, $configured) ? $configured[$type] : ($fallback[$type] ?? '');
  44. $row['logo'] = $logo ? (string)Util::ensureUrl($logo) : '';
  45. }
  46. unset($row);
  47. return ['total' => count($rows), 'data' => array_values($rows)];
  48. }
  49. public static function store(string $type, ?string $logo): void
  50. {
  51. if (Schema::hasTable('recharge_method_logos')) {
  52. $before = DB::table('recharge_method_logos')->where('channel_type', $type)->first();
  53. DB::table('recharge_method_logos')->updateOrInsert(['channel_type' => $type],
  54. ['logo' => $logo, 'created_at' => $before->created_at ?? now(), 'updated_at' => now()]);
  55. }
  56. RechargeChannel::where('data_type', 1)->where('type', $type)->update(['logo' => $logo]);
  57. }
  58. public function save(array $input): array
  59. {
  60. Validator::make($input, ['channel_type' => ['required', 'string', 'max:100'], 'logo' => ['present', 'nullable', 'string', 'max:2048']])->validate();
  61. $types = array_column(PaymentProviderCatalog::channelFormOptions(1)['types'], 'value');
  62. $type = $input['channel_type'];
  63. if (!in_array($type, $types, true) && !RechargeChannel::where('data_type', 1)->where('type', $type)->exists()) {
  64. throw ValidationException::withMessages(['channel_type' => '充值方式不存在']);
  65. }
  66. if (!Schema::hasTable('recharge_method_logos')) throw ValidationException::withMessages(['config' => '充值Logo配置未初始化,请先完成系统更新']);
  67. $logo = self::normalize($input['logo']);
  68. return DB::transaction(function () use ($type, $logo) {
  69. $before = DB::table('recharge_method_logos')->where('channel_type', $type)->lockForUpdate()->first();
  70. self::store($type, $logo);
  71. $result = ['channel_type' => $type, 'logo' => $logo ? (string)Util::ensureUrl($logo) : ''];
  72. OperationAuditService::record('recharge_channel_logo', null, 'save', (array)$before, $result);
  73. return $result;
  74. }, 3);
  75. }
  76. }