Skip to content

Commit de2ee61

Browse files
authored
feat: 약속 확정 기능 구현 (#56)
약속 확정을 위한 모델 수정 및 약속 확정기능 구현입니다. 약속 확정 후, 참여자 캘린더에 일정 추가하는 로직구현이 필요합니다.
1 parent 8ad39cf commit de2ee61

File tree

6 files changed

+114
-3
lines changed

6 files changed

+114
-3
lines changed

backend/app/models/appointment_model.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,12 @@ class Appointments(Base):
2121
name = Column(String(255), nullable=False)
2222
creator_id = Column(String(255))
2323
max_participants = Column(Integer, nullable=False)
24-
status = Column(Enum("VOTING", "CONFIRMED", "CANCELED"), nullable=False)
24+
status = Column(Enum("VOTING", "CONFIRMED"), nullable=False)
2525
invite_link = Column(String(255), unique=True)
26+
confirmed_date = Column(Date, nullable=True)
27+
confirmed_start_time = Column(String(5), nullable=True)
28+
confirmed_end_time = Column(String(5), nullable=True)
29+
confirmed_at = Column(DateTime, nullable=True)
2630
created_at = Column(DateTime, default=datetime.now)
2731

2832
appointment_dates = relationship(
@@ -37,7 +41,9 @@ class AppointmentDates(Base):
3741
__tablename__ = "appointment_dates"
3842

3943
id = Column(Integer, primary_key=True, autoincrement=True)
40-
appointment_id = Column(Integer, ForeignKey("appointments.id"), nullable=False)
44+
appointment_id = Column(
45+
Integer, ForeignKey("appointments.id", ondelete="CASCADE"), nullable=False
46+
)
4147
candidate_date = Column(Date, nullable=False)
4248
__table_args__ = (UniqueConstraint("appointment_id", "candidate_date"),)
4349

backend/app/models/user_model.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from sqlalchemy import Column, String, TEXT, DateTime
22
from app.db.base import Base
33

4+
45
class User(Base):
56
__tablename__ = "users"
67

backend/app/routes/appointment_route.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
AppointmentDetailResponse,
1616
AppointmentListResponse,
1717
SyncMySchedulesResponse,
18+
ConfirmAppointmentRequest,
19+
ConfirmAppointmentResponse,
1820
)
1921
from app.utils.jwt import get_current_user
2022

@@ -246,3 +248,37 @@ async def sync_my_schedules(
246248
raise HTTPException(status_code=400, detail=str(e))
247249
except Exception as e:
248250
raise HTTPException(status_code=500, detail=f"일정 동기화 실패: {str(e)}")
251+
252+
253+
@router.post("/{invite_code}/confirm", response_model=ConfirmAppointmentResponse)
254+
async def confirm_appointment(
255+
invite_code: str,
256+
request: ConfirmAppointmentRequest,
257+
db: AsyncSession = Depends(get_db),
258+
current_user: dict = Depends(get_current_user),
259+
):
260+
# 약속 확정
261+
try:
262+
appointment = await AppointmentService.confirm_appointment(
263+
invite_code=invite_code,
264+
confirmed_date=request.confirmed_date,
265+
confirmed_start_time=request.confirmed_start_time,
266+
confirmed_end_time=request.confirmed_end_time,
267+
user_id=current_user["sub"],
268+
db=db,
269+
)
270+
271+
return ConfirmAppointmentResponse(
272+
id=appointment.id,
273+
name=appointment.name,
274+
status=appointment.status,
275+
confirmed_date=appointment.confirmed_date,
276+
confirmed_start_time=appointment.confirmed_start_time,
277+
confirmed_end_time=appointment.confirmed_end_time,
278+
confirmed_at=appointment.confirmed_at,
279+
)
280+
281+
except ValueError as e:
282+
raise HTTPException(status_code=400, detail=str(e))
283+
except Exception as e:
284+
raise HTTPException(status_code=500, detail=f"약속 확정 실패: {str(e)}")

backend/app/schema/appointment_schema.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,3 +120,30 @@ class SyncMySchedulesResponse(BaseModel):
120120
total_appointments: int
121121
updated_count: int
122122
failed_count: int
123+
124+
125+
class ConfirmAppointmentRequest(BaseModel):
126+
confirmed_date: date
127+
confirmed_start_time: str
128+
confirmed_end_time: str
129+
130+
@validator("confirmed_start_time", "confirmed_end_time")
131+
def validate_time_format(cls, v):
132+
try:
133+
datetime.strptime(v, "%H:%M")
134+
except ValueError:
135+
raise ValueError("시간 형식은 HH:MM이어야 합니다 (예: 09:00)")
136+
return v
137+
138+
139+
class ConfirmAppointmentResponse(BaseModel):
140+
id: int
141+
name: str
142+
status: str
143+
confirmed_date: Optional[date]
144+
confirmed_start_time: Optional[str]
145+
confirmed_end_time: Optional[str]
146+
confirmed_at: Optional[datetime]
147+
148+
class Config:
149+
from_attributes = True

backend/app/services/appointment_service.py

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -344,7 +344,7 @@ async def calculate_optimal_times(
344344
def _filter_slots_by_time_range(
345345
slots: List[dict], time_range_start: str = None, time_range_end: str = None
346346
) -> List[dict]:
347-
#저장된 가용 시간 슬롯을 시간대 필터로 제한
347+
# 저장된 가용 시간 슬롯을 시간대 필터로 제한
348348
if not time_range_start and not time_range_end:
349349
return slots
350350

@@ -390,6 +390,45 @@ def _filter_slots_by_time_range(
390390

391391
return filtered_slots
392392

393+
@staticmethod
394+
async def confirm_appointment(
395+
invite_code: str,
396+
confirmed_date: str,
397+
confirmed_start_time: str,
398+
confirmed_end_time: str,
399+
user_id: str,
400+
db: AsyncSession,
401+
) -> Appointments:
402+
from datetime import datetime
403+
404+
# 약속 조회
405+
appointment = await AppointmentService.get_appointment_by_invite_code(
406+
invite_code, db
407+
)
408+
if not appointment:
409+
raise ValueError("존재하지 않는 약속입니다")
410+
411+
# 생성자 확인
412+
if appointment.creator_id != user_id:
413+
raise ValueError("약속 생성자만 확정할 수 있습니다")
414+
415+
# 이미 확정된 약속인지 확인
416+
if appointment.status == "CONFIRMED":
417+
raise ValueError("이미 확정된 약속입니다")
418+
419+
420+
# 약속 확정
421+
appointment.status = "CONFIRMED"
422+
appointment.confirmed_date = confirmed_date
423+
appointment.confirmed_start_time = confirmed_start_time
424+
appointment.confirmed_end_time = confirmed_end_time
425+
appointment.confirmed_at = datetime.now()
426+
427+
await db.commit()
428+
await db.refresh(appointment)
429+
430+
return appointment
431+
393432
@staticmethod
394433
async def get_my_appointments(user_id: str, db: AsyncSession) -> List[Appointments]:
395434
# 내가 참여한 약속 목록 조회

backend/app/utils/jwt.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,15 @@ def create_access_token(data: dict, expires_delta: Optional[timedelta] = None):
1818
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
1919
return encoded_jwt
2020

21+
2122
def verify_token(token: str):
2223
try:
2324
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
2425
return payload
2526
except JWTError:
2627
return None
2728

29+
2830
def get_current_user(token=Depends(security)):
2931
# JWT 토큰에서 현재 사용자 정보 추출
3032
credentials_exception = HTTPException(

0 commit comments

Comments
 (0)