Add waiting system application files
- Add main application files (main.py, models.py, schemas.py, etc.) - Add routers for all features (waiting, attendance, members, etc.) - Add HTML templates for admin and user interfaces - Add migration scripts and utility files - Add Docker configuration - Add documentation files - Add .gitignore to exclude database and cache files 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
409
schemas.py
Normal file
409
schemas.py
Normal file
@@ -0,0 +1,409 @@
|
||||
from pydantic import BaseModel, Field
|
||||
from datetime import datetime, date, time
|
||||
from typing import Optional, List, Dict
|
||||
|
||||
# Store Settings
|
||||
class StoreSettingsBase(BaseModel):
|
||||
store_name: str
|
||||
display_classes_count: int = 3
|
||||
list_direction: str = "vertical"
|
||||
rows_per_class: int = 1
|
||||
admin_password: str = "1234"
|
||||
max_waiting_limit: int = 50
|
||||
use_max_waiting_limit: bool = True
|
||||
block_last_class_registration: bool = False
|
||||
auto_register_member: bool = False
|
||||
business_day_start: int = 5 # 영업일 기준 시간 (0~23)
|
||||
auto_closing: bool = True # 영업일 변경 시 자동 마감 및 리셋 여부
|
||||
closing_action: str = "reset"
|
||||
|
||||
# 출석 횟수 표시 설정
|
||||
attendance_count_type: str = "days"
|
||||
attendance_lookback_days: int = 30
|
||||
|
||||
# 대기현황판 표시 설정
|
||||
show_waiting_number: bool = True
|
||||
mask_customer_name: bool = False
|
||||
name_display_length: int = 0 # 이름 표시 자릿수 (0 = 전체 표시)
|
||||
show_order_number: bool = True
|
||||
board_display_order: str = "number,name,order"
|
||||
|
||||
# 폰트 설정
|
||||
manager_font_family: str = "Nanum Gothic"
|
||||
manager_font_size: str = "15px"
|
||||
board_font_family: str = "Nanum Gothic"
|
||||
board_font_size: str = "24px"
|
||||
|
||||
# 대기접수 키패드 설정
|
||||
keypad_style: str = "modern" # modern, bold, dark, colorful
|
||||
|
||||
keypad_font_size: str = "large" # small, medium, large, xlarge
|
||||
|
||||
# 개점 설정
|
||||
daily_opening_rule: str = "strict"
|
||||
|
||||
# 대기접수 완료 모달 설정
|
||||
waiting_modal_timeout: int = 5
|
||||
show_member_name_in_waiting_modal: bool = True
|
||||
show_new_member_text_in_waiting_modal: bool = True
|
||||
enable_waiting_voice_alert: bool = False
|
||||
waiting_voice_message: Optional[str] = None
|
||||
waiting_voice_name: Optional[str] = None
|
||||
waiting_voice_rate: float = 1.0
|
||||
waiting_voice_pitch: float = 1.0
|
||||
|
||||
# 대기관리자 화면 레이아웃 설정
|
||||
waiting_manager_max_width: Optional[int] = None
|
||||
|
||||
class StoreSettingsCreate(StoreSettingsBase):
|
||||
pass
|
||||
|
||||
class StoreSettingsUpdate(BaseModel):
|
||||
store_name: Optional[str] = None
|
||||
display_classes_count: Optional[int] = None
|
||||
display_count: Optional[int] = 5
|
||||
list_direction: Optional[str] = None
|
||||
rows_per_class: Optional[int] = None
|
||||
admin_password: Optional[str] = None
|
||||
max_waiting_limit: Optional[int] = None
|
||||
use_max_waiting_limit: Optional[bool] = None
|
||||
block_last_class_registration: Optional[bool] = None
|
||||
auto_register_member: Optional[bool] = None
|
||||
business_day_start: Optional[int] = 0
|
||||
auto_closing: Optional[bool] = True
|
||||
closing_action: Optional[str] = "reset"
|
||||
|
||||
# 출석 횟수 표시 설정
|
||||
attendance_count_type: Optional[str] = None
|
||||
attendance_lookback_days: Optional[int] = None
|
||||
|
||||
# 대기현황판 표시 설정
|
||||
show_waiting_number: Optional[bool] = None
|
||||
mask_customer_name: Optional[bool] = None
|
||||
name_display_length: Optional[int] = None
|
||||
show_order_number: Optional[bool] = None
|
||||
board_display_order: Optional[str] = None
|
||||
|
||||
# 폰트 설정
|
||||
manager_font_family: Optional[str] = None
|
||||
manager_font_size: Optional[str] = None
|
||||
board_font_family: Optional[str] = None
|
||||
board_font_size: Optional[str] = None
|
||||
|
||||
# 대기접수 키패드 설정
|
||||
keypad_style: Optional[str] = None
|
||||
keypad_font_size: Optional[str] = None
|
||||
|
||||
# 개점 설정
|
||||
daily_opening_rule: Optional[str] = None
|
||||
|
||||
# 대기접수 완료 모달 설정
|
||||
waiting_modal_timeout: Optional[int] = None
|
||||
show_member_name_in_waiting_modal: Optional[bool] = None
|
||||
show_new_member_text_in_waiting_modal: Optional[bool] = None
|
||||
enable_waiting_voice_alert: Optional[bool] = None
|
||||
waiting_voice_message: Optional[str] = None
|
||||
waiting_voice_name: Optional[str] = None
|
||||
waiting_voice_rate: Optional[float] = None
|
||||
waiting_voice_pitch: Optional[float] = None
|
||||
|
||||
# 대기관리자 화면 레이아웃 설정
|
||||
waiting_manager_max_width: Optional[int] = None
|
||||
|
||||
class StoreSettings(StoreSettingsBase):
|
||||
id: int
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
# Daily Closing
|
||||
class DailyClosingBase(BaseModel):
|
||||
business_date: date
|
||||
|
||||
class DailyClosingCreate(DailyClosingBase):
|
||||
pass
|
||||
|
||||
class DailyClosing(DailyClosingBase):
|
||||
id: int
|
||||
opening_time: Optional[datetime]
|
||||
closing_time: Optional[datetime]
|
||||
is_closed: bool
|
||||
total_waiting: int
|
||||
total_attended: int
|
||||
total_cancelled: int
|
||||
created_at: datetime
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
# Class Info
|
||||
class ClassInfoBase(BaseModel):
|
||||
class_number: int
|
||||
class_name: str
|
||||
start_time: time
|
||||
end_time: time
|
||||
max_capacity: int = 10
|
||||
is_active: bool = True
|
||||
weekday_schedule: Optional[Dict[str, bool]] = None
|
||||
class_type: str = 'all' # weekday, weekend, all
|
||||
|
||||
class ClassInfoCreate(ClassInfoBase):
|
||||
weekday_schedule: Dict[str, bool] = Field(default_factory=lambda: {
|
||||
"mon": True,
|
||||
"tue": True,
|
||||
"wed": True,
|
||||
"thu": True,
|
||||
"fri": True,
|
||||
"sat": True,
|
||||
"sun": True
|
||||
})
|
||||
|
||||
class ClassInfoUpdate(BaseModel):
|
||||
class_number: Optional[int] = None
|
||||
class_name: Optional[str] = None
|
||||
start_time: Optional[time] = None
|
||||
end_time: Optional[time] = None
|
||||
max_capacity: Optional[int] = None
|
||||
is_active: Optional[bool] = None
|
||||
weekday_schedule: Optional[Dict[str, bool]] = None
|
||||
class_type: Optional[str] = None
|
||||
|
||||
class ClassInfo(ClassInfoBase):
|
||||
id: int
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
current_count: Optional[int] = 0 # 현재 대기자 수
|
||||
weekday_schedule: Dict[str, bool] = Field(default_factory=lambda: {
|
||||
"mon": True,
|
||||
"tue": True,
|
||||
"wed": True,
|
||||
"thu": True,
|
||||
"fri": True,
|
||||
"sat": True,
|
||||
"sun": True
|
||||
}) # 응답에서는 항상 존재
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
# Member
|
||||
class MemberBase(BaseModel):
|
||||
name: str
|
||||
phone: str = Field(..., pattern=r'^010\d{8}$')
|
||||
barcode: Optional[str] = None
|
||||
|
||||
class MemberCreate(MemberBase):
|
||||
pass
|
||||
|
||||
class MemberUpdate(BaseModel):
|
||||
name: Optional[str] = None
|
||||
phone: Optional[str] = None
|
||||
barcode: Optional[str] = None
|
||||
|
||||
class Member(MemberBase):
|
||||
id: int
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
class MemberBulkCreate(BaseModel):
|
||||
members: List[MemberBase]
|
||||
|
||||
# Waiting List
|
||||
class WaitingListBase(BaseModel):
|
||||
phone: str = Field(..., pattern=r'^010\d{8}$')
|
||||
name: Optional[str] = None
|
||||
|
||||
class WaitingListCreate(WaitingListBase):
|
||||
class_id: Optional[int] = None
|
||||
person_count: Optional[int] = 1
|
||||
|
||||
class WaitingListResponse(BaseModel):
|
||||
id: int
|
||||
waiting_number: int
|
||||
class_id: int
|
||||
class_name: str
|
||||
class_order: int
|
||||
phone: str
|
||||
name: Optional[str]
|
||||
status: str
|
||||
registered_at: datetime
|
||||
message: str
|
||||
last_month_attendance_count: int = 0
|
||||
is_new_member: bool = False
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
class WaitingList(BaseModel):
|
||||
id: int
|
||||
business_date: date
|
||||
waiting_number: int
|
||||
phone: str
|
||||
name: Optional[str]
|
||||
class_id: int
|
||||
class_order: int
|
||||
member_id: Optional[int]
|
||||
is_empty_seat: bool = False
|
||||
status: str
|
||||
registered_at: datetime
|
||||
attended_at: Optional[datetime]
|
||||
cancelled_at: Optional[datetime]
|
||||
call_count: int
|
||||
last_called_at: Optional[datetime]
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
class WaitingListDetail(WaitingList):
|
||||
class_info: ClassInfo
|
||||
member: Optional[Member] = None
|
||||
|
||||
class WaitingBoardItem(BaseModel):
|
||||
id: int # 대기자 고유 ID
|
||||
waiting_number: int
|
||||
display_name: str # 이름 또는 폰번호 뒷자리 4자리
|
||||
class_id: int
|
||||
class_name: str
|
||||
class_order: int
|
||||
is_empty_seat: bool = False
|
||||
status: str
|
||||
|
||||
class WaitingBoard(BaseModel):
|
||||
store_name: str
|
||||
business_date: date
|
||||
classes: List[ClassInfo]
|
||||
waiting_list: List[WaitingBoardItem]
|
||||
|
||||
# Waiting Management
|
||||
class WaitingStatusUpdate(BaseModel):
|
||||
status: str # attended, cancelled
|
||||
|
||||
class WaitingOrderUpdate(BaseModel):
|
||||
direction: str # up, down
|
||||
|
||||
class WaitingClassUpdate(BaseModel):
|
||||
target_class_id: int
|
||||
|
||||
class BatchAttendance(BaseModel):
|
||||
class_id: int
|
||||
|
||||
class EmptySeatInsert(BaseModel):
|
||||
waiting_id: int # 이 대기자 뒤에 빈 좌석 삽입
|
||||
|
||||
# Statistics
|
||||
class DailyStatistics(BaseModel):
|
||||
business_date: date
|
||||
total_waiting: int
|
||||
total_attended: int
|
||||
total_cancelled: int
|
||||
total_no_show: int
|
||||
attendance_rate: float
|
||||
class_statistics: List[dict]
|
||||
|
||||
# Franchise
|
||||
class FranchiseBase(BaseModel):
|
||||
name: str
|
||||
code: str
|
||||
|
||||
class FranchiseCreate(FranchiseBase):
|
||||
pass
|
||||
|
||||
class FranchiseUpdate(BaseModel):
|
||||
name: Optional[str] = None
|
||||
code: Optional[str] = None
|
||||
member_type: Optional[str] = None
|
||||
is_active: Optional[bool] = None
|
||||
|
||||
class Franchise(FranchiseBase):
|
||||
id: int
|
||||
member_type: str
|
||||
is_active: bool
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
# Store
|
||||
class StoreBase(BaseModel):
|
||||
franchise_id: int
|
||||
name: str
|
||||
code: str
|
||||
|
||||
class StoreCreate(BaseModel):
|
||||
name: str
|
||||
# code는 자동 생성되므로 입력받지 않음
|
||||
|
||||
class StoreUpdate(BaseModel):
|
||||
name: Optional[str] = None
|
||||
code: Optional[str] = None
|
||||
is_active: Optional[bool] = None
|
||||
|
||||
class Store(StoreBase):
|
||||
id: int
|
||||
is_active: bool
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
# User
|
||||
class UserBase(BaseModel):
|
||||
username: str
|
||||
role: str # system_admin, franchise_admin, store_admin
|
||||
franchise_id: Optional[int] = None
|
||||
store_id: Optional[int] = None
|
||||
|
||||
class UserCreate(UserBase):
|
||||
password: str # 평문 비밀번호 (해싱되어 저장됨)
|
||||
managed_store_ids: Optional[List[int]] = []
|
||||
|
||||
class UserUpdate(BaseModel):
|
||||
username: Optional[str] = None
|
||||
password: Optional[str] = None
|
||||
role: Optional[str] = None
|
||||
franchise_id: Optional[int] = None
|
||||
store_id: Optional[int] = None
|
||||
is_active: Optional[bool] = None
|
||||
managed_store_ids: Optional[List[int]] = None
|
||||
|
||||
class User(UserBase):
|
||||
id: int
|
||||
is_active: bool
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
managed_stores: List[Store] = []
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
class UserLogin(BaseModel):
|
||||
username: str
|
||||
password: str
|
||||
|
||||
class Token(BaseModel):
|
||||
access_token: str
|
||||
token_type: str
|
||||
|
||||
class TokenData(BaseModel):
|
||||
username: Optional[str] = None
|
||||
|
||||
# System Admin Response Schemas
|
||||
class UserListResponse(User):
|
||||
franchise_name: Optional[str] = None
|
||||
store_name: Optional[str] = None
|
||||
|
||||
class StoreListResponse(Store):
|
||||
franchise_name: Optional[str] = None
|
||||
|
||||
class MemberListResponse(Member):
|
||||
franchise_name: Optional[str] = None
|
||||
store_name: Optional[str] = None
|
||||
Reference in New Issue
Block a user