지역 정보(맛집·관광지·축제)는 행정구역 코드(local_contents.region_code) 단위로 캐시돼 "그 시군구에 있는 것"을 줬다. 양양군 업장 옆 5km 속초 관광지는 빠지고 같은 군 반대편 30km 맛집이 붙는 구조라, 캔버스의 지역 정보 섹션은 늘 "준비 중"이었다. 업장 좌표로 TourAPI 를 직접 물어 업장 단위(place_contents)에 담고, 캔버스는 스크린샷으로 받은 형식(도보 시간 필터 + 카드 캐러셀)으로 통일했다.
64 lines
3.0 KiB
Python
64 lines
3.0 KiB
Python
from sqlalchemy import and_, select, update
|
|
from sqlalchemy.dialects.postgresql import insert as pg_insert
|
|
|
|
from common.database.db_session_manager import DB_SESSION_MNG
|
|
from common.database.model.models import place_contents
|
|
from common.utils.gtime import GTime
|
|
|
|
|
|
class PlaceContentCRUD:
|
|
"""업장 반경 주변정보(place_contents). 키는 place_id 다."""
|
|
|
|
async def list_by_place(self, db, place_id, *, include_hidden: bool = True):
|
|
conds = [place_contents.place_id == place_id, place_contents.deleted == False] # noqa: E712
|
|
if not include_hidden:
|
|
conds.append(place_contents.hidden == False) # noqa: E712
|
|
return await DB_SESSION_MNG.execute(
|
|
db,
|
|
select(place_contents).where(and_(*conds))
|
|
.order_by(place_contents.content_type.asc(), place_contents.has_image.desc(), place_contents.distance_m.asc()),
|
|
)
|
|
|
|
async def upsert(self, db, values: dict):
|
|
"""(place_id, content_type, external_id) 로 삽입/갱신. ★ hidden 은 건드리지 않는다 —
|
|
운영자가 숨긴 것을 재수집이 되살리면 안 된다."""
|
|
stmt = pg_insert(place_contents).values(**values)
|
|
stmt = stmt.on_conflict_do_update(
|
|
index_elements=[place_contents.place_id, place_contents.content_type, place_contents.external_id],
|
|
index_where=(place_contents.deleted == False), # noqa: E712
|
|
set_={
|
|
"title": stmt.excluded.title,
|
|
"body": stmt.excluded.body,
|
|
"distance_m": stmt.excluded.distance_m,
|
|
"has_image": stmt.excluded.has_image,
|
|
"display_end_at": stmt.excluded.display_end_at,
|
|
"collected_at": stmt.excluded.collected_at,
|
|
"updated_at": GTime.UTC(),
|
|
},
|
|
)
|
|
return await DB_SESSION_MNG.add(db, stmt)
|
|
|
|
async def soft_delete_missing(self, db, place_id, keep: set[tuple[int, str]]):
|
|
"""이번 응답에 없는 행을 소프트 삭제. keep = {(content_type, external_id)}."""
|
|
err, rows = await DB_SESSION_MNG.execute(
|
|
db,
|
|
select(place_contents.place_content_id, place_contents.content_type, place_contents.external_id)
|
|
.where(place_contents.place_id == place_id, place_contents.deleted == False), # noqa: E712
|
|
)
|
|
gone = [r.place_content_id for r in (rows or []) if (int(r.content_type), r.external_id) not in keep]
|
|
if not gone:
|
|
return err, 0
|
|
return await DB_SESSION_MNG.add_with_rowcount(
|
|
db,
|
|
update(place_contents).where(place_contents.place_content_id.in_(gone))
|
|
.values(deleted=True, updated_at=GTime.UTC()),
|
|
)
|
|
|
|
async def set_hidden(self, db, place_content_id, hidden: bool):
|
|
return await DB_SESSION_MNG.add_with_rowcount(
|
|
db,
|
|
update(place_contents).where(
|
|
place_contents.place_content_id == place_content_id, place_contents.deleted == False # noqa: E712
|
|
).values(hidden=hidden, updated_at=GTime.UTC()),
|
|
)
|