From 7a2affe10a0b894b7ddfbd79ee87507ad309281f Mon Sep 17 00:00:00 2001 From: hbyang Date: Fri, 12 Jun 2026 10:25:56 +0900 Subject: [PATCH] =?UTF-8?q?feat(settle):=20=ED=82=A5=EC=98=A4=ED=94=84+3h?= =?UTF-8?q?=20=EB=8C=80=EA=B8=B0=20=EC=A0=9C=EA=B1=B0=20=E2=80=94=20?= =?UTF-8?q?=EC=86=8C=EC=8A=A4=EA=B0=80=20FINISHED=20=EC=A3=BC=EB=A9=B4=20?= =?UTF-8?q?=EC=A6=89=EC=8B=9C=20=EC=A0=95=EC=82=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit settle 게이트를 '킥오프 지남'으로 변경. football-data가 FINISHED 보고 시 다음 폴링(최대 5분) 내 자동 종료·채점·메일. 경기 중(IN_PLAY)엔 자동 스킵. Co-Authored-By: Claude Opus 4.8 (1M context) --- backend/app/worker.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/backend/app/worker.py b/backend/app/worker.py index 43d4685..a6a09c6 100644 --- a/backend/app/worker.py +++ b/backend/app/worker.py @@ -129,19 +129,20 @@ async def generate_ai_predictions(only_missing: bool = True) -> None: # ── 2.5) 결과 자동 정산 (관리자 입력 불필요) ──────────────── async def settle_matches() -> None: - """킥오프 + result_settle_hours 경과한 미정산 경기를 외부 스코어로 자동 채점. + """킥오프가 지난 미정산 경기를, 소스가 FINISHED 로 보고하는 즉시 채점·종료. - 외부 소스(openfootball/football-data)에서 확정 스코어를 수집해 apply_result - 호출 → 채점·유저 포인트 집계·finished_at 기록. 직후 결과 메일을 발송한다. - 소스에 스코어가 아직 없으면 다음 틱에 재시도. + 킥오프 이후의 미정산 경기를 폴링하되, 외부 소스(football-data)가 FINISHED 로 + 확정 스코어를 줄 때만 apply_result → 채점·유저 포인트 집계·finished_at·메일. + 경기 중(IN_PLAY)에는 FINISHED 가 아니라 결과에 안 잡혀 자동 스킵된다(조기 종료 없음). + 아직 FINISHED 가 아니면 다음 틱(5분)에 재시도 → 종료 후 최대 1틱 내 반영. """ now = now_utc() - cutoff = now - timedelta(hours=settings.result_settle_hours) async with SessionLocal() as db: pending = ( await db.execute(select(Match).where(Match.result_outcome.is_(None))) ).scalars().all() - due = [m.match_id for m in pending if ensure_aware(m.kickoff_at) <= cutoff] + # 킥오프가 지난 경기만 (소스가 FINISHED 줄 수 있는 시점) + due = [m.match_id for m in pending if ensure_aware(m.kickoff_at) <= now] if due: results = await fetch_results() @@ -163,7 +164,7 @@ async def settle_matches() -> None: settled += 1 log.info("settle: %s 자동 정산 %d-%d", mid, sc[0], sc[1]) if settled == 0: - log.info("settle: %d경기 정산 대기 — 외부 스코어 아직 없음", len(due)) + log.info("settle: %d경기 진행/대기 — 아직 FINISHED 아님", len(due)) await maybe_send_result_emails() @@ -284,14 +285,13 @@ async def main() -> None: sched.start() log.info( "scheduling-server started — schedule sync daily %02d:%02d · status every %ss · " - "AI gen daily %02d:%02d %s · auto-settle 킥오프+%dh (every %ss) · 맞춘사람만=%s", + "AI gen daily %02d:%02d %s · auto-settle on FINISHED (poll every %ss) · 맞춘사람만=%s", settings.schedule_sync_hour, settings.schedule_sync_minute, settings.status_tick_seconds, settings.ai_generate_hour, settings.ai_generate_minute, settings.timezone, - settings.result_settle_hours, settings.settle_tick_seconds, settings.result_email_correct_only, )