여러 줄 주석이 설명보다 경위(예전·실측·지적)를 적고 있어 읽는 사람이 결론을 찾기 어려웠다. - ts·tsx·js·mjs·css·py 478개: 여러 줄 주석은 첫 문장 한 줄로, 과거형·날짜 문장은 삭제 - 주석 위치는 TypeScript 파서·파이썬 tokenize/ast 로 찾는다 — 문자열 안의 # · /* 는 건드리지 않는다 - eslint·ts·noqa·type: ignore 같은 지시 주석은 그대로 둔다 파이썬 275개 정리 전후 AST 동일, TS 298개 주석 뺀 토큰 동일(빈 JSX 주석 10곳만 차이). site·frontend·admin tsc, site vitest 105 passed Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
83 lines
3.3 KiB
Python
83 lines
3.3 KiB
Python
"""예약 요청 메일 — 발행본 폼이 보낸 것을 사장님에게 전달한다."""
|
|
from sqlalchemy import select
|
|
|
|
from common.database.db_session_manager import DB_SESSION_MNG
|
|
from common.database.model.models import places, sites, users
|
|
from common.enums import DBWRType, SiteStatus
|
|
from common.logger import LOG
|
|
from services import mail_service
|
|
|
|
NOT_AVAILABLE = "지금은 온라인 예약 요청을 받을 수 없습니다. 전화로 문의해 주세요."
|
|
SENT = "예약 요청을 보냈습니다. 사장님이 확인 후 연락드립니다."
|
|
|
|
|
|
class BookingRequestService:
|
|
async def send(self, body):
|
|
from router.v1.site.booking_request import ResBookingRequest
|
|
|
|
target = await self._target(body.place_id)
|
|
if not target:
|
|
return ResBookingRequest(success=False, message=NOT_AVAILABLE)
|
|
|
|
place_name, owner_email = target
|
|
if not mail_service.is_configured() or not mail_service.is_valid_address(owner_email):
|
|
LOG.w("[booking-request] 받는 주소나 SMTP 설정이 없어 전달하지 못했다")
|
|
return ResBookingRequest(success=False, message=NOT_AVAILABLE)
|
|
|
|
reply_to = body.email if body.email and mail_service.is_valid_address(body.email) else None
|
|
ok = mail_service.send(
|
|
to=owner_email,
|
|
subject=f"[예약 요청] {place_name} — {body.name} 님",
|
|
text=_body(place_name, body),
|
|
reply_to=reply_to,
|
|
)
|
|
if not ok:
|
|
return ResBookingRequest(success=False, message=NOT_AVAILABLE)
|
|
|
|
LOG.i(f"[booking-request] 전달 완료 place={body.place_id}")
|
|
return ResBookingRequest(success=True, message=SENT)
|
|
|
|
async def _target(self, place_id) -> tuple[str, str] | None:
|
|
"""(상호명, 사장님 이메일)."""
|
|
def query(session):
|
|
return session.execute(
|
|
select(places.name, users.email)
|
|
.join(sites, sites.place_id == places.place_id)
|
|
.join(users, users.user_id == places.owner_user_id)
|
|
.where(
|
|
places.place_id == place_id,
|
|
places.deleted == False, # noqa: E712
|
|
sites.deleted == False, # noqa: E712
|
|
sites.status == SiteStatus.PUBLISHED.value,
|
|
)
|
|
)
|
|
|
|
result = await DB_SESSION_MNG.execute_lambda(places.DBType(), DBWRType.DB_READ.value, query)
|
|
row = result.first() if result is not None else None
|
|
if not row or not row[1]:
|
|
return None
|
|
return str(row[0] or ""), str(row[1])
|
|
|
|
|
|
def _body(place_name: str, body) -> str:
|
|
lines = [
|
|
f"{place_name} 예약 요청이 도착했습니다.",
|
|
"",
|
|
f"성함 {body.name}",
|
|
f"연락처 {body.phone}",
|
|
]
|
|
if body.email:
|
|
lines.append(f"이메일 {body.email}")
|
|
if body.stay:
|
|
lines.append(f"일정 {body.stay}")
|
|
if body.guests:
|
|
lines.append(f"인원 {body.guests}")
|
|
if body.message:
|
|
lines += ["", "요청사항", body.message.strip()]
|
|
lines += [
|
|
"",
|
|
"— 이 메일은 발행 사이트의 예약 요청 폼에서 자동으로 보냈습니다.",
|
|
" 예약이 확정된 것은 아니며, 손님에게 직접 연락하셔야 합니다.",
|
|
]
|
|
return "\n".join(lines)
|