ContactConfigurationService.php 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. <?php
  2. namespace App\Services;
  3. use Illuminate\Support\Facades\DB;
  4. use Illuminate\Support\Facades\Validator;
  5. use Illuminate\Validation\ValidationException;
  6. /** 联系我们:后台维护多个邮箱与多个域名,前台原样展示 */
  7. class ContactConfigurationService
  8. {
  9. public const MAX_ITEMS = 50;
  10. public function get(): array
  11. {
  12. return $this->format(DB::table('contact_settings')->where('id', 1)->first());
  13. }
  14. public function save(array $input): array
  15. {
  16. $data = Validator::make($input, [
  17. 'emails' => ['present', 'array', 'max:' . self::MAX_ITEMS],
  18. 'emails.*' => ['nullable', 'string', 'max:254'],
  19. 'domains' => ['present', 'array', 'max:' . self::MAX_ITEMS],
  20. 'domains.*' => ['nullable', 'string', 'max:2048'],
  21. ], [
  22. 'emails.present' => '请提交邮箱数组', 'emails.array' => '邮箱必须为数组', 'emails.max' => '邮箱最多' . self::MAX_ITEMS . '个',
  23. 'emails.*.string' => '每个邮箱必须为字符串', 'emails.*.max' => '每个邮箱最多254个字符',
  24. 'domains.present' => '请提交域名数组', 'domains.array' => '域名必须为数组', 'domains.max' => '域名最多' . self::MAX_ITEMS . '个',
  25. 'domains.*.string' => '每个域名必须为字符串', 'domains.*.max' => '每个域名最多2048个字符',
  26. ])->validate();
  27. foreach (['emails' => '邮箱', 'domains' => '域名'] as $field => $label) {
  28. if (array_values($data[$field]) !== $data[$field]) {
  29. throw ValidationException::withMessages([$field => $label . '必须为顺序数组']);
  30. }
  31. }
  32. $emails = [];
  33. foreach ($data['emails'] as $index => $email) {
  34. $email = trim((string) $email);
  35. if ($email === '') continue;
  36. if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
  37. throw ValidationException::withMessages(['emails.' . $index => '邮箱格式不正确:' . $email]);
  38. }
  39. if (!in_array(strtolower($email), array_map('strtolower', $emails), true)) $emails[] = $email;
  40. }
  41. $domains = [];
  42. foreach ($data['domains'] as $index => $domain) {
  43. if (trim((string) $domain) === '') continue;
  44. $url = SiteConfigurationService::normalizeDomain((string) $domain, 'domains.' . $index);
  45. if (!in_array($url, $domains, true)) $domains[] = $url;
  46. }
  47. $payload = ['emails' => $emails, 'domains' => $domains];
  48. return DB::transaction(function () use ($payload) {
  49. DB::table('contact_settings')->insertOrIgnore(['id' => 1, 'emails' => '[]', 'domains' => '[]', 'created_at' => now(), 'updated_at' => now()]);
  50. $before = $this->format(DB::table('contact_settings')->where('id', 1)->lockForUpdate()->first());
  51. if ($before !== $payload) {
  52. DB::table('contact_settings')->where('id', 1)->update([
  53. 'emails' => json_encode($payload['emails'], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR),
  54. 'domains' => json_encode($payload['domains'], JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR),
  55. 'updated_at' => now(),
  56. ]);
  57. OperationAuditService::record('contact_configuration', 1, 'update', $before, $payload);
  58. }
  59. return $payload;
  60. }, 3);
  61. }
  62. private function format(?object $row): array
  63. {
  64. return ['emails' => $this->list($row->emails ?? null), 'domains' => $this->list($row->domains ?? null)];
  65. }
  66. private function list(?string $value): array
  67. {
  68. if ($value === null || trim($value) === '') return [];
  69. $items = json_decode($value, true, 512, JSON_THROW_ON_ERROR);
  70. if (!is_array($items) || array_values($items) !== $items || count(array_filter($items, 'is_string')) !== count($items)) {
  71. throw new \RuntimeException('联系我们配置格式异常');
  72. }
  73. return $items;
  74. }
  75. }