fix(realestate-lab): 최종 리뷰 이슈 수정 — FK CASCADE, 단일 연결, 동시성 가드

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-06 08:49:05 +09:00
parent bdfcdee5fd
commit afc159c84d
3 changed files with 41 additions and 35 deletions

View File

@@ -2,7 +2,7 @@ import json
import logging
from typing import Dict, Any, List
from .db import get_profile, save_match_result, _conn
from .db import get_profile, _conn
logger = logging.getLogger("realestate-lab")
@@ -30,7 +30,8 @@ def _check_eligible_types(profile: Dict[str, Any], ann: Dict[str, Any]) -> List[
elif is_homeless:
eligible.append("일반2순위")
# 특별공급
# 특별공급 — 신혼부부
# NOTE: 소득기준 검증은 향후 구현 예정 (income_level 필드 활용)
if profile.get("is_newlywed") and is_homeless:
eligible.append("특별-신혼부부")
@@ -117,7 +118,7 @@ def run_matching():
"""프로필 기반 매칭을 실행하여 결과를 저장한다."""
profile = get_profile()
if not profile:
logger.info("매칭 스킵: 프로필이 설정되지 않음")
logger.info("프로필 미설정 — 매칭 건너뜀")
return
with _conn() as conn:
@@ -125,32 +126,35 @@ def run_matching():
"SELECT * FROM announcements WHERE status IN ('청약예정', '청약중')"
).fetchall()
saved = 0
for row in anns:
ann = {c: row[c] for c in row.keys()}
models_rows = conn.execute(
for ann_row in anns:
ann = {c: ann_row[c] for c in ann_row.keys()}
models = conn.execute(
"SELECT * FROM announcement_models WHERE house_manage_no = ? AND pblanc_no = ?",
(ann["house_manage_no"], ann["pblanc_no"]),
).fetchall()
models = [{c: m[c] for c in m.keys()} for m in models_rows]
result = _compute_score(profile, ann, models)
model_list = [dict(m) for m in models]
result = _compute_score(profile, ann, model_list)
if result["match_score"] > 0:
save_match_result({
"announcement_id": ann["id"],
"model_id": None,
"match_score": result["match_score"],
"match_reasons": result["match_reasons"],
"eligible_types": result["eligible_types"],
})
saved += 1
conn.execute("""
INSERT INTO match_results (announcement_id, model_id, match_score, match_reasons, eligible_types, is_new)
VALUES (?, ?, ?, ?, ?, 1)
ON CONFLICT(announcement_id, model_id) DO UPDATE SET
match_score=excluded.match_score,
match_reasons=excluded.match_reasons,
eligible_types=excluded.eligible_types
""", (
ann["id"],
None,
result["match_score"],
json.dumps(result["match_reasons"]),
json.dumps(result["eligible_types"]),
))
# 완료/결과발표 공고의 매칭 결과 정리
# Clean up stale match results for completed announcements
conn.execute(
"DELETE FROM match_results WHERE announcement_id NOT IN "
"(SELECT id FROM announcements WHERE status IN ('청약예정', '청약중'))"
)
logger.info("매칭 완료: %d건 공고 중 %d건 매칭됨", len(anns), saved)
logger.info("매칭 완료")