| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697 |
- <?php
- namespace App\Services;
- use App\Models\Banner;
- use App\Support\ContentLink;
- use Illuminate\Support\Facades\DB;
- class BannerVisibilityService
- {
- /** Positive numeric links are activity IDs; empty/zero and other links are unbound. */
- public static function activityId($link): ?string
- {
- $link = trim((string) $link);
- if ($link === '' || !ctype_digit($link)) {
- return null;
- }
- $id = ltrim($link, '0');
- return $id === '' ? null : $id;
- }
- /** Only activity-type banners bind an activity; rows saved before click_type existed fall back to the link. */
- public static function boundActivityId(?string $clickType, $link): ?string
- {
- return $clickType === null || $clickType === 'activity' ? self::activityId($link) : null;
- }
- /** A stored click type wins; otherwise infer it from a legacy single `link` value. */
- public static function clickType(?string $clickType, $link): string
- {
- if (in_array($clickType, ['none', 'activity', 'external', 'internal'], true)) return $clickType;
- $link = trim((string) $link);
- if (self::activityId($link) !== null) return 'activity';
- if ($link === '' || ltrim($link, '0') === '') return 'none';
- return str_starts_with($link, '/') && !str_starts_with($link, '//') ? 'internal' : 'external';
- }
- /** Bound banners follow the activity; only unbound banners use the stored manual setting. */
- public static function decorate(iterable $banners, ?int $now = null): array
- {
- $now = $now ?? time();
- $items = [];
- $activityIds = [];
- foreach ($banners as $banner) {
- $item = $banner instanceof Banner ? $banner->toArray() : $banner;
- $item['status'] = (int) $item['status'];
- $item['activity_id'] = self::boundActivityId($item['click_type'] ?? null, $item['link'] ?? null);
- $item = array_replace($item, self::links($item));
- $item['can_manual_toggle'] = $item['activity_id'] === null;
- $item['manual_status'] = $item['can_manual_toggle'] ? $item['status'] : null;
- if ($item['activity_id'] !== null) {
- $activityIds[$item['activity_id']] = $item['activity_id'];
- }
- $items[] = $item;
- }
- // Read raw timestamps; ActivityReward's date accessors discard the time of day.
- $activities = empty($activityIds) ? collect() : DB::table('activity_rewards')
- ->whereIn('id', array_values($activityIds))
- ->get(['id', 'status', 'start_time', 'end_time'])->keyBy('id');
- foreach ($items as &$item) {
- $activity = $item['activity_id'] === null ? null : $activities->get($item['activity_id']);
- $reason = null;
- if ($item['activity_id'] !== null) {
- if ($activity === null) {
- $reason = 'activity_missing';
- } elseif ((int) $activity->status !== 1) {
- $reason = 'activity_offline';
- } elseif ((int) $activity->start_time > $now) {
- $reason = 'activity_not_started';
- } elseif ((int) $activity->end_time <= $now) {
- $reason = 'activity_ended';
- }
- } elseif ($item['manual_status'] !== 1) {
- $reason = 'manual';
- }
- $item['effective_status'] = $reason === null ? 1 : 0;
- $item['status'] = $item['effective_status'];
- $item['offline_reason'] = $reason;
- }
- unset($item);
- return $items;
- }
- /** Normalized jump fields for the admin form; internal rows always expose both platform paths. */
- public static function links(array $row): array
- {
- $type = self::clickType($row['click_type'] ?? null, $row['link'] ?? null);
- if ($type === 'activity') {
- return ['click_type' => $type, 'link' => self::activityId($row['link'] ?? null), 'link_pc' => null, 'link_h5' => null];
- }
- return ContentLink::normalize(['click_type' => $type] + $row);
- }
- }
|