fix(realestate): 네이버 매물 dong_code를 dong명에서 유도(cortarNo 부재)→ market_deals 매칭·판정 복구

네이버 article에 cortarNo가 없는 경우 listing.dong_code가 None으로 저장되어
get_market_deals_for가 시세를 못 찾아 모든 매물이 '보류'(판정 불가) 상태였다.
dong명은 항상 정확히 태깅되므로 lawd_code(dong)으로 5자리 시군구코드를 유도해
market_deals와 매칭시킨다.

- internal_router.listings_ingest: dong_code 없으면 batch.dong에서 lawd_code로 보정
- db.upsert_listing: ON CONFLICT UPDATE SET에 dong_code 추가(재유입 시 기존 None행 갱신)
- listing_matcher.run_listing_matching: get_market_deals_for 호출 전 dong_code 없으면
  listing.dong에서 즉시 유도(기존 저장된 None행도 재실행만으로 판정 복구)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EqCYBhvTcdeCTUDX3RhWx9
This commit is contained in:
2026-07-10 14:11:51 +09:00
parent 4097c95286
commit f676116336
5 changed files with 63 additions and 3 deletions

View File

@@ -977,7 +977,8 @@ def upsert_listing(data: Dict[str, Any]) -> tuple:
ON CONFLICT(article_no) DO UPDATE SET
deposit=excluded.deposit, monthly_rent=excluded.monthly_rent, sale_price=excluded.sale_price,
floor=excluded.floor, complex_name=excluded.complex_name, address=excluded.address,
url=excluded.url, posted_at=excluded.posted_at, raw_json=excluded.raw_json
url=excluded.url, posted_at=excluded.posted_at, raw_json=excluded.raw_json,
dong_code=excluded.dong_code
""", d)
row = conn.execute("SELECT * FROM listings WHERE article_no=?", (d["article_no"],)).fetchone()
return dict(row), is_new

View File

@@ -8,7 +8,7 @@ from pydantic import BaseModel
from .auth import verify_internal_key
from .db import get_listing_criteria, upsert_listing
from .lawd_codes import naver_cortar
from .lawd_codes import naver_cortar, lawd_code
from .listing_collector import _parse_naver_article
from .listing_matcher import run_listing_matching
from .notifier import notify_new_listings
@@ -55,6 +55,9 @@ def listings_ingest(body: ListingsIngest, background_tasks: BackgroundTasks):
if not d.get("article_no"):
continue
d["dong"] = batch.dong
# 네이버 article에 cortarNo가 없는 경우가 실사용에서 확인됨 → dong명에서
# 5자리 시군구코드(lawd_code)를 유도(market_deals는 이 5자리를 쓴다).
d["dong_code"] = d.get("dong_code") or lawd_code(batch.dong)
_, is_new = upsert_listing(d)
received += 1
if is_new:

View File

@@ -5,6 +5,7 @@ import statistics
from .db import (get_listing_criteria, get_market_deals_for, bulk_upsert_listing_matches,
_conn)
from . import finance_rules
from .lawd_codes import lawd_code
logger = logging.getLogger("realestate-lab")
@@ -96,7 +97,10 @@ def run_listing_matching() -> None:
budget = finance_rules.estimate_purchase_budget(
equity, income, reg, criteria.get("is_first_home"), criteria.get("is_homeless"))
m = match_listing(criteria, l, budget)
deals = get_market_deals_for(l.get("dong_code"), l.get("complex_name"),
# 네이버 cortarNo 부재 등으로 기존 저장된 listing.dong_code가 None일 수 있음 —
# dong명에서 5자리 lawd_code를 유도해 즉시 판정(보류 고착 방지).
dc = l.get("dong_code") or lawd_code(l.get("dong"))
deals = get_market_deals_for(dc, l.get("complex_name"),
l.get("area_exclusive"), "전세" if is_rent else "매매",
conn=conn)
rec = {"listing_id": l["id"], "category": "임차" if is_rent else "매매",