130 lines
4.2 KiB
PHP
130 lines
4.2 KiB
PHP
<?php
|
|
|
|
namespace App\Services\Admin\Log;
|
|
|
|
use App\Repositories\Admin\Log\MemberLoginLogRepository;
|
|
use Illuminate\Support\Facades\Schema;
|
|
|
|
final class MemberLoginLogService
|
|
{
|
|
public function __construct(
|
|
private readonly MemberLoginLogRepository $repo,
|
|
) {}
|
|
|
|
public function indexData(array $query = []): array
|
|
{
|
|
$currentYear = (int)date('Y');
|
|
$years = $this->availableYears(2018, $currentYear);
|
|
|
|
$year = (int)($query['year'] ?? $currentYear);
|
|
if (!in_array($year, $years, true)) {
|
|
// 테이블 없거나 허용 범위 밖이면 최신년도 fallback
|
|
$year = !empty($years) ? max($years) : $currentYear;
|
|
}
|
|
|
|
$filters = [
|
|
'year' => $year,
|
|
'date_from' => $this->safeDate($query['date_from'] ?? ''),
|
|
'date_to' => $this->safeDate($query['date_to'] ?? ''),
|
|
|
|
'mem_no' => $this->safeInt($query['mem_no'] ?? null),
|
|
|
|
'sf' => $this->safeEnum($query['sf'] ?? '', ['s','f']), // 성공/실패
|
|
'conn' => $this->safeEnum($query['conn'] ?? '', ['1','2']), // pc/mobile
|
|
|
|
'ip4' => $this->safeIpPrefix($query['ip4'] ?? ''),
|
|
'ip4_c' => $this->safeIpPrefix($query['ip4_c'] ?? ''),
|
|
|
|
'error_code'=> $this->safeStr($query['error_code'] ?? '', 10),
|
|
|
|
'platform' => $this->safeStr($query['platform'] ?? '', 30),
|
|
'browser' => $this->safeStr($query['browser'] ?? '', 40),
|
|
];
|
|
|
|
// 기간 역전 방지
|
|
if ($filters['date_from'] && $filters['date_to']) {
|
|
if (strcmp($filters['date_from'], $filters['date_to']) > 0) {
|
|
[$filters['date_from'], $filters['date_to']] = [$filters['date_to'], $filters['date_from']];
|
|
}
|
|
}
|
|
|
|
$page = $this->repo->paginateByYear($year, $filters, 30);
|
|
|
|
// 화면 가공(라벨/링크 등)
|
|
$items = [];
|
|
foreach ($page->items() as $it) {
|
|
$r = is_array($it) ? $it : (array)$it;
|
|
|
|
$sf = (string)($r['sf'] ?? 's');
|
|
$conn = (string)($r['conn'] ?? '1');
|
|
|
|
$items[] = array_merge($r, [
|
|
'sf_label' => $sf === 'f' ? '실패' : '성공',
|
|
'sf_badge' => $sf === 'f' ? 'badge--bad' : 'badge--ok',
|
|
|
|
'conn_label' => $conn === '2' ? 'M' : 'PC',
|
|
'conn_badge' => $conn === '2' ? 'badge--mvno' : 'badge--muted',
|
|
|
|
'mem_no_int' => (int)($r['mem_no'] ?? 0),
|
|
'mem_link' => ((int)($r['mem_no'] ?? 0) > 0) ? ('/members/' . (int)$r['mem_no']) : null,
|
|
|
|
// 실패가 아니면 에러코드 화면에서 비워도 됨(뷰에서 처리)
|
|
]);
|
|
}
|
|
|
|
return [
|
|
'years' => $years,
|
|
'filters' => $filters,
|
|
'page' => $page,
|
|
'items' => $items,
|
|
];
|
|
}
|
|
|
|
private function availableYears(int $from, int $to): array
|
|
{
|
|
$out = [];
|
|
for ($y = $from; $y <= $to; $y++) {
|
|
$t = 'mem_login_' . $y;
|
|
if (Schema::hasTable($t)) $out[] = $y;
|
|
}
|
|
// 없으면 그냥 범위라도 반환(운영/개발에서 초기 셋업 대비)
|
|
return !empty($out) ? $out : [$to];
|
|
}
|
|
|
|
private function safeStr(mixed $v, int $max): string
|
|
{
|
|
$s = trim((string)$v);
|
|
if ($s === '') return '';
|
|
if (mb_strlen($s) > $max) $s = mb_substr($s, 0, $max);
|
|
return $s;
|
|
}
|
|
|
|
private function safeEnum(mixed $v, array $allowed): string
|
|
{
|
|
$s = trim((string)$v);
|
|
return in_array($s, $allowed, true) ? $s : '';
|
|
}
|
|
|
|
private function safeInt(mixed $v): ?int
|
|
{
|
|
if ($v === null || $v === '') return null;
|
|
if (!is_numeric($v)) return null;
|
|
$n = (int)$v;
|
|
return $n >= 0 ? $n : null;
|
|
}
|
|
|
|
private function safeDate(mixed $v): ?string
|
|
{
|
|
$s = trim((string)$v);
|
|
if ($s === '') return null;
|
|
return preg_match('/^\d{4}-\d{2}-\d{2}$/', $s) ? $s : null;
|
|
}
|
|
|
|
private function safeIpPrefix(mixed $v): string
|
|
{
|
|
$s = trim((string)$v);
|
|
if ($s === '') return '';
|
|
return preg_match('/^[0-9.]+$/', $s) ? $s : '';
|
|
}
|
|
}
|