format(DB::table('contact_settings')->where('id', 1)->first()); } public function save(array $input): array { $data = Validator::make($input, [ 'emails' => ['present', 'array', 'max:' . self::MAX_ITEMS], 'emails.*' => ['nullable', 'string', 'max:254'], 'domains' => ['present', 'array', 'max:' . self::MAX_ITEMS], 'domains.*' => ['nullable', 'string', 'max:2048'], ], [ 'emails.present' => '请提交邮箱数组', 'emails.array' => '邮箱必须为数组', 'emails.max' => '邮箱最多' . self::MAX_ITEMS . '个', 'emails.*.string' => '每个邮箱必须为字符串', 'emails.*.max' => '每个邮箱最多254个字符', 'domains.present' => '请提交域名数组', 'domains.array' => '域名必须为数组', 'domains.max' => '域名最多' . self::MAX_ITEMS . '个', 'domains.*.string' => '每个域名必须为字符串', 'domains.*.max' => '每个域名最多2048个字符', ])->validate(); foreach (['emails' => '邮箱', 'domains' => '域名'] as $field => $label) { if (array_values($data[$field]) !== $data[$field]) { throw ValidationException::withMessages([$field => $label . '必须为顺序数组']); } } $emails = []; foreach ($data['emails'] as $index => $email) { $email = trim((string) $email); if ($email === '') continue; if (!filter_var($email, FILTER_VALIDATE_EMAIL)) { throw ValidationException::withMessages(['emails.' . $index => '邮箱格式不正确:' . $email]); } if (!in_array(strtolower($email), array_map('strtolower', $emails), true)) $emails[] = $email; } $domains = []; foreach ($data['domains'] as $index => $domain) { if (trim((string) $domain) === '') continue; $url = SiteConfigurationService::normalizeDomain((string) $domain, 'domains.' . $index); if (!in_array($url, $domains, true)) $domains[] = $url; } $payload = ['emails' => $emails, 'domains' => $domains]; return DB::transaction(function () use ($payload) { DB::table('contact_settings')->insertOrIgnore(['id' => 1, 'emails' => '[]', 'domains' => '[]', 'created_at' => now(), 'updated_at' => now()]); $before = $this->format(DB::table('contact_settings')->where('id', 1)->lockForUpdate()->first()); if ($before !== $payload) { DB::table('contact_settings')->where('id', 1)->update([ 'emails' => json_encode($payload['emails'], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR), 'domains' => json_encode($payload['domains'], JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR), 'updated_at' => now(), ]); OperationAuditService::record('contact_configuration', 1, 'update', $before, $payload); } return $payload; }, 3); } private function format(?object $row): array { return ['emails' => $this->list($row->emails ?? null), 'domains' => $this->list($row->domains ?? null)]; } private function list(?string $value): array { if ($value === null || trim($value) === '') return []; $items = json_decode($value, true, 512, JSON_THROW_ON_ERROR); if (!is_array($items) || array_values($items) !== $items || count(array_filter($items, 'is_string')) !== count($items)) { throw new \RuntimeException('联系我们配置格式异常'); } return $items; } }