fix(realestate-lab): 코드 리뷰 이슈 수정 — 신규 추적, 보안, 비동기, 매칭 상태 보존

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-06 08:43:27 +09:00
parent 3b118725ca
commit bdfcdee5fd
4 changed files with 58 additions and 23 deletions

View File

@@ -160,14 +160,19 @@ def _ann_row_to_dict(r) -> Dict[str, Any]:
return {c: r[c] for c in r.keys()}
def upsert_announcement(data: Dict[str, Any]) -> Dict[str, Any]:
"""공고 upsert — house_manage_no + pblanc_no 기준."""
def upsert_announcement(data: Dict[str, Any]) -> tuple:
"""공고 upsert — house_manage_no + pblanc_no 기준. Returns (dict, is_new: bool)."""
status = compute_status(
data.get("receipt_start", ""),
data.get("receipt_end", ""),
data.get("winner_date", ""),
)
with _conn() as conn:
exists = conn.execute(
"SELECT 1 FROM announcements WHERE house_manage_no = ? AND pblanc_no = ?",
(data["house_manage_no"], data["pblanc_no"]),
).fetchone()
is_new = exists is None
conn.execute("""
INSERT INTO announcements (
house_manage_no, pblanc_no, house_nm, house_secd, house_dtl_secd,
@@ -220,7 +225,7 @@ def upsert_announcement(data: Dict[str, Any]) -> Dict[str, Any]:
"SELECT * FROM announcements WHERE house_manage_no = ? AND pblanc_no = ?",
(data["house_manage_no"], data["pblanc_no"]),
).fetchone()
return _ann_row_to_dict(row)
return _ann_row_to_dict(row), is_new
def get_announcements(
@@ -243,25 +248,24 @@ def get_announcements(
conditions.append("a.house_secd = ?")
params.append(house_type)
join_clause = ""
if matched_only:
join_clause = "INNER JOIN match_results m ON m.announcement_id = a.id"
conditions.append("a.id IN (SELECT announcement_id FROM match_results)")
where = f"WHERE {' AND '.join(conditions)}" if conditions else ""
order_map = {"date": "a.rcrit_date DESC", "score": "a.id DESC", "price": "a.id ASC"}
order = order_map.get(sort, "a.rcrit_date DESC")
if matched_only and sort == "score":
order = "m.match_score DESC"
order = "(SELECT MAX(match_score) FROM match_results WHERE announcement_id = a.id) DESC"
offset = (page - 1) * size
with _conn() as conn:
total = conn.execute(
f"SELECT COUNT(*) FROM announcements a {join_clause} {where}", params
f"SELECT COUNT(*) FROM announcements a {where}", params
).fetchone()[0]
rows = conn.execute(
f"SELECT a.* FROM announcements a {join_clause} {where} ORDER BY {order} LIMIT ? OFFSET ?",
f"SELECT a.* FROM announcements a {where} ORDER BY {order} LIMIT ? OFFSET ?",
params + [size, offset],
).fetchall()
return {
@@ -292,11 +296,23 @@ def create_announcement(data: Dict[str, Any]) -> Dict[str, Any]:
data["house_manage_no"] = data.get("house_manage_no", f"MANUAL-{uuid.uuid4().hex[:8]}")
data["pblanc_no"] = data.get("pblanc_no", "00")
data["source"] = "manual"
return upsert_announcement(data)
result, _ = upsert_announcement(data)
return result
ANNOUNCEMENT_COLUMNS = {
"house_nm", "house_secd", "house_dtl_secd", "rent_secd",
"region_code", "region_name", "address", "total_units",
"rcrit_date", "receipt_start", "receipt_end", "spsply_start", "spsply_end",
"gnrl_rank1_start", "gnrl_rank1_end", "winner_date",
"contract_start", "contract_end", "homepage_url", "pblanc_url",
"constructor", "developer", "move_in_month",
"is_speculative_area", "is_price_cap", "contact",
}
def update_announcement(ann_id: int, data: Dict[str, Any]) -> Optional[Dict[str, Any]]:
fields = {k: v for k, v in data.items() if v is not None}
fields = {k: v for k, v in data.items() if v is not None and k in ANNOUNCEMENT_COLUMNS}
if not fields:
return get_announcement(ann_id)
@@ -335,7 +351,8 @@ def update_all_statuses():
"""모든 진행중 공고의 status를 날짜 기반으로 재계산."""
with _conn() as conn:
rows = conn.execute(
"SELECT id, receipt_start, receipt_end, winner_date FROM announcements WHERE status != '완료'"
"SELECT id, receipt_start, receipt_end, winner_date FROM announcements "
"WHERE status != '완료' AND (receipt_start IS NOT NULL OR receipt_end IS NOT NULL OR winner_date IS NOT NULL)"
).fetchall()
for r in rows:
new_status = compute_status(r["receipt_start"], r["receipt_end"], r["winner_date"])
@@ -402,10 +419,20 @@ def get_profile() -> Optional[Dict[str, Any]]:
return _profile_row_to_dict(r) if r else None
PROFILE_COLUMNS = {
"name", "age", "is_homeless", "is_householder",
"subscription_months", "subscription_amount", "family_members",
"has_dependents", "children_count", "is_newlywed", "marriage_months",
"has_newborn", "is_first_home", "income_level",
"preferred_regions", "preferred_types",
"min_area", "max_area", "max_price",
}
def upsert_profile(data: Dict[str, Any]) -> Dict[str, Any]:
updates = {}
for k, v in data.items():
if v is None:
if v is None or k not in PROFILE_COLUMNS:
continue
if isinstance(v, bool):
updates[k] = 1 if v else 0