hour < 12 ? now()->subDay()->startOfDay() : now()->startOfDay(); $from = $this->date($request->query('from')) ?? $latestStatisticsDate->copy()->subDays(13); $to = $this->date($request->query('to')) ?? $latestStatisticsDate->copy(); if ($from->greaterThan($to)) { [$from, $to] = [$to, $from]; } if ($from->diffInDays($to) > 90) { $from = $to->copy()->subDays(90); } $state = $this->repository->available() ? $this->repository->state() : null; $errorCode = trim((string) ($state['last_error_code'] ?? '')); $syncState = match (true) { ! $this->repository->available() => 'unavailable', $errorCode !== '' => 'failed', $state === null => 'pending', ($state['status'] ?? '') === 'running' => 'syncing', ($state['status'] ?? '') === 'succeeded' => 'succeeded', default => 'pending', }; $permissionRequired = DingWorklogMirrorRepository::isPermissionError($errorCode); $daily = []; $selectedDate = $this->date($request->query('date'))?->toDateString(); $showDetails = $request->boolean('details'); $submissions = []; $penalty = [ 'start_date' => null, 'end_date' => null, 'total_points' => '0', 'total_points_formatted' => '0', 'members' => [], ]; $penaltyError = null; if ($this->repository->available()) { try { $daily = $this->repository->dailySummary($from->toDateString(), $to->toDateString()); $selectedDate ??= $daily[0]['statistics_date'] ?? $latestStatisticsDate->toDateString(); if ($showDetails) { $submissions = array_map(static fn (object $row): array => [ 'report_id' => (string) $row->report_id, 'template_name' => (string) ($row->template_name ?? ''), 'creator_userid' => (string) ($row->creator_userid ?? ''), 'creator_name' => (string) ($row->creator_name ?? ''), 'submitted_at' => (string) $row->submitted_at, 'statistics_date' => (string) $row->statistics_date, ], $this->repository->submissionsForDate($selectedDate)); } } catch (Throwable $exception) { report($exception); $syncState = 'failed'; $errorCode = 'dingtalk_worklog_read_failed'; } try { $penalty = $this->penaltyCalculator->summary( $from->toDateString(), $to->toDateString(), now(), ); } catch (Throwable $exception) { report($exception); $penaltyError = '日志漏写扣分暂时无法计算。'; } } $currentDate = $latestStatisticsDate->toDateString(); $current = collect($daily)->firstWhere('statistics_date', $currentDate) ?? [ 'statistics_date' => $currentDate, 'submission_count' => 0, 'submitter_count' => 0, 'first_submitted_at' => null, 'last_submitted_at' => null, ]; return [ 'view' => 'business.pages.other.log-statistics', 'worklogFilters' => [ 'from' => $from->toDateString(), 'to' => $to->toDateString(), 'date' => $selectedDate, ], 'worklogState' => [ 'state' => $syncState, 'error_code' => $errorCode ?: null, 'permission_required' => $permissionRequired, 'permission_apply_url' => $permissionRequired ? $this->repository->permissionApplyUrl() : null, 'status' => $state['status'] ?? null, 'last_attempted_at' => $state['last_attempted_at'] ?? null, 'last_succeeded_at' => $state['last_succeeded_at'] ?? null, 'mirror_row_count' => (int) ($state['mirror_row_count'] ?? 0), ], 'worklogCurrent' => $current, 'worklogDaily' => $daily, 'worklogSubmissions' => $submissions, 'worklogShowDetails' => $showDetails, 'worklogPenalty' => $penalty, 'worklogPenaltyError' => $penaltyError, 'worklogDays' => (int) $from->diffInDays($to) + 1, 'worklogTotalSubmissions' => array_sum(array_column($daily, 'submission_count')), 'worklogTemplateName' => (string) config('dingtalk.worklog.display_name', '财务日报 / 日志'), 'worklogCutoffAt' => $latestStatisticsDate->copy()->addDay()->setTime(12, 0)->toDateTimeString(), ]; } private function date(mixed $value): ?Carbon { $value = trim((string) $value); if (preg_match('/^\d{4}-\d{2}-\d{2}$/', $value) !== 1) { return null; } try { return Carbon::createFromFormat('!Y-m-d', $value)->startOfDay(); } catch (Throwable) { return null; } } }