$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外站链接', ]); } }