| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687 |
- <?php
- namespace App\Support;
- use Illuminate\Support\Facades\Validator;
- use Illuminate\Validation\Rule;
- use Illuminate\Validation\ValidationException;
- /** Shared announcement/activity link contract. */
- final class ContentLink
- {
- public const FIELDS = ['click_type', 'link', 'link_pc', 'link_h5'];
- public static function normalize(array $row): array
- {
- $type = $row['click_type'] ?? 'none';
- $type = in_array($type, ['none', 'external', 'internal'], true) ? $type : 'none';
- return [
- 'click_type' => $type,
- 'link' => $type !== 'none' ? ($row['link'] ?? null) : null,
- 'link_pc' => $type === 'internal' ? ($row['link_pc'] ?? $row['link'] ?? null) : null,
- 'link_h5' => $type === 'internal' ? ($row['link_h5'] ?? $row['link'] ?? null) : null,
- ];
- }
- public static function forSave(array $input, array $previous = []): array
- {
- $before = self::normalize($previous);
- $type = $input['click_type'] ?? $before['click_type'];
- // An explicitly empty click type is invalid, rather than an omitted field.
- if (array_key_exists('click_type', $input)) $type = $input['click_type'];
- Validator::make(['click_type' => $type], ['click_type' => ['required', 'string', Rule::in(array_keys(AnnouncementCatalog::CLICKS))]],
- ['click_type.in' => '点击方式仅支持无链接、外站链接、站内路径'])->validate();
- $result = ['click_type' => $type, 'link' => null, 'link_pc' => null, 'link_h5' => null];
- if ($type === 'none') return $result;
- if ($type === 'external') {
- $result['link'] = array_key_exists('link', $input) ? $input['link'] : ($before['click_type'] === $type ? $before['link'] : null);
- self::validateLink('link', $result['link'], $type);
- return $result;
- }
- $legacy = array_key_exists('link', $input) && !array_key_exists('link_pc', $input) && !array_key_exists('link_h5', $input);
- foreach (['link_pc' => 'PC', 'link_h5' => 'H5'] as $field => $label) {
- $result[$field] = $legacy ? $input['link'] : (array_key_exists($field, $input) ? $input[$field] : ($before['click_type'] === $type ? $before[$field] : null));
- self::validateLink($field, $result[$field], $type, $label);
- }
- // Keep old callers functional; new dual-path submissions have no shared internal target.
- if ($legacy) $result['link'] = $input['link'];
- elseif (!array_key_exists('link_pc', $input) && !array_key_exists('link_h5', $input) && $before['click_type'] === $type) $result['link'] = $before['link'];
- return $result;
- }
- public static function forPlatform(array $row, ?string $platform): array
- {
- $links = self::normalize($row);
- if ($links['click_type'] === 'internal') {
- $links['link'] = match ($platform) {
- 'PC' => $links['link_pc'], 'H5', 'APP' => $links['link_h5'],
- default => $links['link_pc'] === $links['link_h5'] ? $links['link_pc'] : null,
- };
- }
- return array_intersect_key($links, ['click_type' => true, 'link' => true]);
- }
- public static function requestPlatform(): string
- {
- $platform = request()->header('Platform');
- return is_string($platform) ? (ActivityEligibility::normalizePlatform($platform) ?? 'PC') : 'PC';
- }
- public static function valid(string $value, string $type): bool
- {
- if ($value === '' || preg_match('/[\x00-\x20\x7f\\\\]/', $value)) return false;
- if ($type === 'internal') return str_starts_with($value, '/') && !str_starts_with($value, '//');
- $parts = parse_url($value);
- return filter_var($value, FILTER_VALIDATE_URL) && $parts && in_array(strtolower($parts['scheme'] ?? ''), ['http', 'https'], true)
- && !isset($parts['user']) && !isset($parts['pass']);
- }
- private static function validateLink(string $field, $value, string $type, string $label = ''): void
- {
- Validator::make([$field => $value], [$field => ['required', 'string', 'max:2048']],
- [$field . '.required' => $type === 'internal' ? '请填写' . $label . '站内相对路径' : '请填写跳转链接'])->validate();
- if (!self::valid($value, $type)) throw ValidationException::withMessages([
- $field => $type === 'internal' ? '请填写有效的' . $label . '站内相对路径' : '请填写有效的HTTP/HTTPS外站链接',
- ]);
- }
- }
|