diff --git a/realestate-lab/app/db.py b/realestate-lab/app/db.py index d22aed4..aa81e65 100644 --- a/realestate-lab/app/db.py +++ b/realestate-lab/app/db.py @@ -449,6 +449,81 @@ def upsert_model(data: Dict[str, Any]): """, data) +# ── 청약 가점 계산 ─────────────────────────────────────────────────────────── + +def calculate_subscription_points(profile: Dict[str, Any]) -> Dict[str, Any]: + """청약 가점제 점수 계산 (총 84점 만점). + + 1. 무주택기간 (0~32점): 만 30세부터 기산, 연 2점 + 2. 부양가족 수 (0~35점): 인당 5점, 6명+ 만점 + 3. 청약통장 가입기간 (0~17점): 6개월 미만 1점 ~ 15년+ 17점 + """ + result = { + "homeless_duration": {"score": 0, "max": 32, "detail": ""}, + "dependents": {"score": 0, "max": 35, "detail": ""}, + "subscription_period": {"score": 0, "max": 17, "detail": ""}, + "total": 0, + "max_total": 84, + } + + if not profile: + return result + + # 1. 무주택기간 (만 30세부터 기산, 연 2점, 최대 32점) + age = profile.get("age") or 0 + is_homeless = profile.get("is_homeless", False) + if is_homeless and age >= 30: + homeless_years = age - 30 + score = min(homeless_years * 2, 32) + # 1년 미만도 2점 + if homeless_years == 0: + score = 2 + result["homeless_duration"]["score"] = score + result["homeless_duration"]["detail"] = f"만 {age}세, 무주택 약 {homeless_years}년" + elif is_homeless and age < 30: + result["homeless_duration"]["score"] = 0 + result["homeless_duration"]["detail"] = f"만 {age}세 (30세 미만, 기간 미산정)" + else: + result["homeless_duration"]["detail"] = "유주택자" + + # 2. 부양가족 수 (인당 5점, 최대 35점) + family_members = profile.get("family_members") or 0 + dependents = max(family_members - 1, 0) # 본인 제외 + dep_score = min(dependents * 5, 35) + result["dependents"]["score"] = dep_score + result["dependents"]["detail"] = f"{dependents}명" if dependents > 0 else "0명 (본인만)" + + # 3. 청약통장 가입기간 (6개월 미만 1점, 이후 1년마다 +1점, 최대 17점) + months = profile.get("subscription_months") or 0 + if months <= 0: + sub_score = 0 + sub_detail = "미가입" + elif months < 6: + sub_score = 1 + sub_detail = f"{months}개월 (6개월 미만)" + else: + years = months / 12 + # 6개월~1년 = 2점, 1~2년 = 3점, ..., 14~15년 = 16점, 15년+ = 17점 + sub_score = min(int(years) + 2, 17) + if years < 1: + sub_score = 2 + if years >= 1: + y = int(years) + sub_detail = f"{y}년 {months - y*12}개월" + else: + sub_detail = f"{months}개월" + result["subscription_period"]["score"] = sub_score + result["subscription_period"]["detail"] = sub_detail + + result["total"] = ( + result["homeless_duration"]["score"] + + result["dependents"]["score"] + + result["subscription_period"]["score"] + ) + + return result + + # ── user_profile CRUD ──────────────────────────────────────────────────────── def _profile_row_to_dict(r) -> Dict[str, Any]: @@ -468,7 +543,11 @@ def _profile_row_to_dict(r) -> Dict[str, Any]: def get_profile() -> Optional[Dict[str, Any]]: with _conn() as conn: r = conn.execute("SELECT * FROM user_profile WHERE id = 1").fetchone() - return _profile_row_to_dict(r) if r else None + if not r: + return None + profile = _profile_row_to_dict(r) + profile["subscription_points"] = calculate_subscription_points(profile) + return profile PROFILE_COLUMNS = { @@ -512,7 +591,9 @@ def upsert_profile(data: Dict[str, Any]) -> Dict[str, Any]: vals, ) row = conn.execute("SELECT * FROM user_profile WHERE id = 1").fetchone() - return _profile_row_to_dict(row) + profile = _profile_row_to_dict(row) + profile["subscription_points"] = calculate_subscription_points(profile) + return profile # ── match_results CRUD ─────────────────────────────────────────────────────── @@ -536,10 +617,18 @@ def save_match_result(data: Dict[str, Any]): def get_matches(page: int = 1, size: int = 20) -> Dict[str, Any]: offset = (page - 1) * size with _conn() as conn: + # 프로필 가점 계산 + profile_row = conn.execute("SELECT * FROM user_profile WHERE id = 1").fetchone() + points = None + if profile_row: + profile = _profile_row_to_dict(profile_row) + points = calculate_subscription_points(profile) + total = conn.execute("SELECT COUNT(*) FROM match_results").fetchone()[0] rows = conn.execute(""" SELECT m.*, a.house_nm, a.region_name, a.address, a.status as ann_status, - a.receipt_start, a.receipt_end, a.winner_date, a.pblanc_url + a.receipt_start, a.receipt_end, a.winner_date, a.pblanc_url, + a.house_secd, a.is_speculative_area FROM match_results m JOIN announcements a ON a.id = m.announcement_id ORDER BY m.is_new DESC, m.match_score DESC @@ -552,7 +641,13 @@ def get_matches(page: int = 1, size: int = 20) -> Dict[str, Any]: d["match_reasons"] = json.loads(d["match_reasons"]) if d["match_reasons"] else [] d["eligible_types"] = json.loads(d["eligible_types"]) if d["eligible_types"] else [] items.append(d) - return {"items": items, "total": total, "page": page, "size": size} + return { + "items": items, + "total": total, + "page": page, + "size": size, + "my_points": points, + } def mark_match_read(match_id: int) -> bool: