fix: [dashboard] 주간 날씨 목요일 데이터 누락 수정

- 기상청 중기예보 18시 발표는 D+5부터만 제공, 06시는 D+4부터 제공
- 여러 발표시각 데이터를 병합하여 빈 필드 보충 (최신 우선)
- 단기예보 TMN/TMX 없는 날은 중기예보 icon/pop 우선 사용
This commit is contained in:
김보곤
2026-02-22 19:04:43 +09:00
parent 14c9b787e9
commit f9665dd558

View File

@@ -86,25 +86,30 @@ private function buildForecast(): array
$midTmx = $midTa["taMax{$i}"] ?? null;
$midWf = $midLand["wf{$i}Am"] ?? ($midLand["wf{$i}"] ?? '');
$midPop = max($midLand["rnSt{$i}Am"] ?? 0, $midLand["rnSt{$i}Pm"] ?? 0, $midLand["rnSt{$i}"] ?? 0);
$midIcon = ! empty($midWf) ? $this->midWeatherToIcon($midWf) : null;
if ($shortData) {
// 단기예보 우선, 기온/강수확률 없으면 중기로 보충
// 단기예보에 TMN/TMX가 있으면 완전한 데이터로 판단
$shortComplete = $shortData && ($shortData['tmn'] !== null || $shortData['tmx'] !== null);
if ($shortComplete) {
// 단기예보 우선, 기온만 없으면 중기로 보충
$forecasts[] = [
'date' => $date,
'tmn' => $shortData['tmn'] ?? $midTmn,
'tmx' => $shortData['tmx'] ?? $midTmx,
'icon' => $shortData['icon'] ?? (! empty($midWf) ? $this->midWeatherToIcon($midWf) : null),
'icon' => $shortData['icon'] ?? $midIcon,
'weather_text' => $shortData['weather_text'] ?: $midWf,
'pop' => $shortData['pop'] ?? $midPop,
];
} else {
// 중기예보 우선 (단기예보가 없거나 TMN/TMX 미포함)
$forecasts[] = [
'date' => $date,
'tmn' => $midTmn,
'tmx' => $midTmx,
'icon' => ! empty($midWf) ? $this->midWeatherToIcon($midWf) : null,
'weather_text' => $midWf,
'pop' => $midPop,
'tmn' => $midTmn ?? ($shortData['tmn'] ?? null),
'tmx' => $midTmx ?? ($shortData['tmx'] ?? null),
'icon' => $midIcon ?? ($shortData['icon'] ?? null),
'weather_text' => ! empty($midWf) ? $midWf : ($shortData['weather_text'] ?? ''),
'pop' => $midPop > 0 ? $midPop : ($shortData['pop'] ?? 0),
];
}
}
@@ -201,51 +206,55 @@ private function fetchShortForecast(): array
}
/**
* 중기기온 API 호출 (18시 발표 실패 시 06시 fallback)
* 중기기온 API 호출 (최신 발표 우선, 이전 발표로 빈 필드 보충)
*
* 기상청 중기예보 API 특성:
* - 06시 발표: D+4 ~ D+10 제공 (D+3 없음)
* - 18시 발표: D+5 ~ D+10 제공 (D+3, D+4 없음)
* → 18시에는 최신 데이터가 D+5부터만 있으므로, 06시 데이터로 D+4를 보충
*/
private function fetchMidTemperature(): array
{
$tmFcList = $this->getMidBaseTmFcCandidates();
foreach ($tmFcList as $tmFc) {
$body = $this->callApi(self::BASE_MID_TA, [
'pageNo' => 1,
'numOfRows' => 10,
'regId' => self::MID_TA_REG_ID,
'tmFc' => $tmFc,
]);
$items = $body ? data_get($body, 'response.body.items.item', []) : [];
if (! empty($items)) {
return $items[0];
}
}
return [];
return $this->fetchMidMerged(self::BASE_MID_TA, self::MID_TA_REG_ID);
}
/**
* 중기날씨 API 호출 (18시 발표 실패 시 06시 fallback)
* 중기날씨 API 호출 (최신 발표 우선, 이전 발표로 빈 필드 보충)
*/
private function fetchMidLandForecast(): array
{
$tmFcList = $this->getMidBaseTmFcCandidates();
return $this->fetchMidMerged(self::BASE_MID_LAND, self::MID_LAND_REG_ID);
}
foreach ($tmFcList as $tmFc) {
$body = $this->callApi(self::BASE_MID_LAND, [
/**
* 중기예보 데이터를 여러 발표시각에서 병합 (최신 우선)
*/
private function fetchMidMerged(string $baseUrl, string $regId): array
{
$merged = [];
foreach ($this->getMidBaseTmFcCandidates() as $tmFc) {
$body = $this->callApi($baseUrl, [
'pageNo' => 1,
'numOfRows' => 10,
'regId' => self::MID_LAND_REG_ID,
'regId' => $regId,
'tmFc' => $tmFc,
]);
$items = $body ? data_get($body, 'response.body.items.item', []) : [];
if (! empty($items)) {
return $items[0];
if (empty($items)) {
continue;
}
// 최신 발표 데이터 우선, 이전 발표로 빈 필드만 보충
foreach ($items[0] as $key => $value) {
if ($value !== null && $value !== '' && ! isset($merged[$key])) {
$merged[$key] = $value;
}
}
}
return [];
return $merged;
}
/**