validate([ 'page' => ['sometimes', 'integer', 'min:1'], 'per_page' => ['sometimes', 'integer', 'min:1', 'max:100'], 'mobile' => ['sometimes', 'nullable', 'string', 'max:20'], 'status' => ['sometimes', 'nullable', 'string', Rule::in([ '', AudienceSmsLog::STATUS_PENDING, AudienceSmsLog::STATUS_SENDING, AudienceSmsLog::STATUS_DELIVERED, AudienceSmsLog::STATUS_DROPPED, AudienceSmsLog::STATUS_FAILED, ])], 'scene' => ['sometimes', 'nullable', 'string', Rule::in([ '', AudienceSmsLog::SCENE_PRE_REGISTER, AudienceSmsLog::SCENE_APPROVED, AudienceSmsLog::SCENE_AUDIENCE_LOGIN, AudienceSmsLog::SCENE_PARTICIPANT_LOGIN, ])], ]); $service->syncFromLogApi($competition); $query = $this->buildQuery($competition, $filters); $perPage = min((int) ($filters['per_page'] ?? 15), 100); $paginator = $query->paginate($perPage); $paginator->getCollection()->transform(fn (AudienceSmsLog $row) => $this->toArray($row)); return response()->json($paginator); } public function sync(Competition $competition, AudienceSmsLogService $service): JsonResponse { return response()->json($service->syncFromLogApi($competition, true)); } public function resend( Competition $competition, AudienceSmsLog $audience_sms_log, AudienceSmsLogService $service, ): JsonResponse { $created = $service->resend($competition, $audience_sms_log); return response()->json($this->toArray($created)); } /** * @param array $filters * @return Builder */ private function buildQuery(Competition $competition, array $filters): Builder { $query = AudienceSmsLog::query() ->where('competition_id', $competition->id) ->orderByDesc('sent_at') ->orderByDesc('id'); $mobile = trim((string) ($filters['mobile'] ?? '')); if ($mobile !== '') { $query->where('mobile', 'like', '%'.$mobile.'%'); } $status = trim((string) ($filters['status'] ?? '')); if ($status === AudienceSmsLog::STATUS_FAILED) { $query->whereIn('status', [AudienceSmsLog::STATUS_FAILED, AudienceSmsLog::STATUS_DROPPED]); } elseif ($status !== '') { $query->where('status', $status); } $scene = trim((string) ($filters['scene'] ?? '')); if ($scene !== '') { $query->where('scene', $scene); } return $query; } /** * @return array */ private function toArray(AudienceSmsLog $row): array { return [ 'id' => $row->id, 'competition_id' => $row->competition_id, 'audience_registration_id' => $row->audience_registration_id, 'scene' => $row->scene, 'scene_label' => AudienceSmsLog::sceneLabel((string) $row->scene), 'mobile' => $row->mobile, 'template_id' => $row->template_id, 'content' => $row->content, 'send_id' => $row->send_id, 'status' => $row->status, 'status_label' => $this->statusLabel($row->status), 'fee' => $row->fee, 'report_state' => $row->report_state, 'dropped_reason' => $row->dropped_reason, 'can_resend' => $row->canResend(), 'sent_at' => BizDateTime::toIso8601($row->sent_at), 'delivered_at' => BizDateTime::toIso8601($row->delivered_at), ]; } private function statusLabel(string $status): string { return match ($status) { AudienceSmsLog::STATUS_DELIVERED => '发送成功', AudienceSmsLog::STATUS_DROPPED, AudienceSmsLog::STATUS_FAILED => '发送失败', AudienceSmsLog::STATUS_SENDING => '发送中', default => '待回执', }; } }