Initial commit: AutonetSellCar platform with deployment system
- Frontend: Next.js 14 with TypeScript - Backend: FastAPI with SQLAlchemy - Agent: Carmodoo sync agent - Deployment: Docker Compose based staging/production setup - Scripts: Automated deployment with rollback support 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
81
backend/app/schemas/__init__.py
Normal file
81
backend/app/schemas/__init__.py
Normal file
@@ -0,0 +1,81 @@
|
||||
from .car import (
|
||||
CarMakerCreate, CarMakerResponse,
|
||||
CarModelCreate, CarModelResponse,
|
||||
CarCreate, CarUpdate, CarResponse, CarListResponse,
|
||||
CarImageCreate, CarImageResponse,
|
||||
)
|
||||
from .user import UserCreate, UserUpdate, UserResponse, Token, CarViewResponse, PurchaseViewRequest
|
||||
from .inquiry import (
|
||||
InquiryCreate, InquiryResponse,
|
||||
InquiryMessageCreate, InquiryMessageResponse,
|
||||
InquiryWithMessages, InquiryListResponse,
|
||||
AdminInquiryRespond, AdminInquiryUpdateStatus,
|
||||
)
|
||||
from .hero_banner import (
|
||||
HeroBannerCreate, HeroBannerUpdate, HeroBannerResponse,
|
||||
HeroBannerListResponse, HeroBannerLocalizedResponse,
|
||||
HeroBannerSettingsUpdate, HeroBannerSettingsResponse,
|
||||
)
|
||||
from .translation import (
|
||||
TranslationCreate, TranslationUpdate, TranslationResponse,
|
||||
TranslationListResponse, TranslationBulkRequest, TranslationBulkResponse,
|
||||
)
|
||||
from .vehicle_request import (
|
||||
VehicleRequestCreate, VehicleRequestResponse,
|
||||
RequestVehicleCreate, RequestVehicleResponse, RequestVehicleApprove,
|
||||
PurchasedVehicleCreate, PurchasedVehicleResponse, PurchasedVehicleUpdateStatus,
|
||||
VehicleRequestWithVehicles,
|
||||
)
|
||||
from .dealer import (
|
||||
DealerApplicationCreate, DealerApplicationResponse,
|
||||
DealerApplicationApprove, DealerApplicationReject,
|
||||
DealerInfoResponse, DealerPublicInfo,
|
||||
)
|
||||
from .vehicle_share import (
|
||||
VehicleShareCreate, VehicleShareResponse, VehicleSharePublic,
|
||||
ShareRewardResponse, ShareRewardSummary,
|
||||
)
|
||||
from .withdrawal import (
|
||||
WithdrawalRequestCreate, WithdrawalRequestResponse,
|
||||
WithdrawalProcess, WithdrawalBalance,
|
||||
)
|
||||
from .referral import (
|
||||
ReferralRewardResponse, ReferralStats,
|
||||
ReferralSettingsResponse, ReferralSettingsUpdate,
|
||||
)
|
||||
from .notification import (
|
||||
NotificationCreate, NotificationResponse,
|
||||
NotificationListResponse, NotificationMarkRead,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"CarMakerCreate", "CarMakerResponse",
|
||||
"CarModelCreate", "CarModelResponse",
|
||||
"CarCreate", "CarUpdate", "CarResponse", "CarListResponse",
|
||||
"CarImageCreate", "CarImageResponse",
|
||||
"UserCreate", "UserUpdate", "UserResponse", "Token", "CarViewResponse", "PurchaseViewRequest",
|
||||
"InquiryCreate", "InquiryResponse",
|
||||
"InquiryMessageCreate", "InquiryMessageResponse",
|
||||
"InquiryWithMessages", "InquiryListResponse",
|
||||
"AdminInquiryRespond", "AdminInquiryUpdateStatus",
|
||||
"HeroBannerCreate", "HeroBannerUpdate", "HeroBannerResponse",
|
||||
"HeroBannerListResponse", "HeroBannerLocalizedResponse",
|
||||
"HeroBannerSettingsUpdate", "HeroBannerSettingsResponse",
|
||||
"TranslationCreate", "TranslationUpdate", "TranslationResponse",
|
||||
"TranslationListResponse", "TranslationBulkRequest", "TranslationBulkResponse",
|
||||
"VehicleRequestCreate", "VehicleRequestResponse",
|
||||
"RequestVehicleCreate", "RequestVehicleResponse", "RequestVehicleApprove",
|
||||
"PurchasedVehicleCreate", "PurchasedVehicleResponse", "PurchasedVehicleUpdateStatus",
|
||||
"VehicleRequestWithVehicles",
|
||||
"DealerApplicationCreate", "DealerApplicationResponse",
|
||||
"DealerApplicationApprove", "DealerApplicationReject",
|
||||
"DealerInfoResponse", "DealerPublicInfo",
|
||||
"VehicleShareCreate", "VehicleShareResponse", "VehicleSharePublic",
|
||||
"ShareRewardResponse", "ShareRewardSummary",
|
||||
"WithdrawalRequestCreate", "WithdrawalRequestResponse",
|
||||
"WithdrawalProcess", "WithdrawalBalance",
|
||||
"ReferralRewardResponse", "ReferralStats",
|
||||
"ReferralSettingsResponse", "ReferralSettingsUpdate",
|
||||
"NotificationCreate", "NotificationResponse",
|
||||
"NotificationListResponse", "NotificationMarkRead",
|
||||
]
|
||||
185
backend/app/schemas/car.py
Normal file
185
backend/app/schemas/car.py
Normal file
@@ -0,0 +1,185 @@
|
||||
from pydantic import BaseModel
|
||||
from typing import Optional, List, Any
|
||||
from datetime import datetime
|
||||
from decimal import Decimal
|
||||
|
||||
|
||||
# CarSpecification Schema
|
||||
class CarSpecificationResponse(BaseModel):
|
||||
id: int
|
||||
car_id: int
|
||||
manufacturer: Optional[str] = None
|
||||
model_name: Optional[str] = None
|
||||
grade: Optional[str] = None
|
||||
model_year: Optional[str] = None
|
||||
displacement: Optional[int] = None
|
||||
fuel_type: Optional[str] = None
|
||||
transmission: Optional[str] = None
|
||||
drive_type: Optional[str] = None
|
||||
max_power: Optional[str] = None
|
||||
max_torque: Optional[str] = None
|
||||
fuel_efficiency: Optional[str] = None
|
||||
body_type: Optional[str] = None
|
||||
door_count: Optional[int] = None
|
||||
seating_capacity: Optional[int] = None
|
||||
length: Optional[int] = None
|
||||
width: Optional[int] = None
|
||||
height: Optional[int] = None
|
||||
wheelbase: Optional[int] = None
|
||||
curb_weight: Optional[int] = None
|
||||
safety_options: Optional[List[str]] = None
|
||||
comfort_options: Optional[List[str]] = None
|
||||
exterior_options: Optional[List[str]] = None
|
||||
interior_options: Optional[List[str]] = None
|
||||
raw_data: Optional[Any] = None
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
# CarMaker Schemas
|
||||
class CarMakerCreate(BaseModel):
|
||||
code: str
|
||||
name: str
|
||||
name_en: Optional[str] = None
|
||||
|
||||
|
||||
class CarMakerResponse(BaseModel):
|
||||
id: int
|
||||
code: str
|
||||
name: str
|
||||
name_en: Optional[str] = None
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
# CarModel Schemas
|
||||
class CarModelCreate(BaseModel):
|
||||
code: str
|
||||
maker_id: int
|
||||
name: str
|
||||
name_en: Optional[str] = None
|
||||
|
||||
|
||||
class CarModelResponse(BaseModel):
|
||||
id: int
|
||||
code: str
|
||||
maker_id: int
|
||||
name: str
|
||||
name_en: Optional[str] = None
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
# CarImage Schemas
|
||||
class CarImageCreate(BaseModel):
|
||||
url: Optional[str] = None
|
||||
local_path: Optional[str] = None
|
||||
is_main: bool = False
|
||||
sort_order: int = 0
|
||||
|
||||
|
||||
class CarImageResponse(BaseModel):
|
||||
id: int
|
||||
url: Optional[str] = None
|
||||
local_path: Optional[str] = None
|
||||
is_main: bool
|
||||
sort_order: int
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
# Car Schemas
|
||||
class CarCreate(BaseModel):
|
||||
source: str = "carmodoo"
|
||||
source_id: str
|
||||
source_key: Optional[str] = None
|
||||
maker_code: Optional[str] = None
|
||||
model_code: Optional[str] = None
|
||||
car_name: Optional[str] = None
|
||||
year: Optional[int] = None
|
||||
month: Optional[int] = None
|
||||
mileage: Optional[int] = None
|
||||
price_krw: Optional[int] = None
|
||||
price_usd: Optional[Decimal] = None
|
||||
fuel: Optional[str] = None
|
||||
transmission: Optional[str] = None
|
||||
color: Optional[str] = None
|
||||
displacement: Optional[int] = None
|
||||
car_number: Optional[str] = None
|
||||
seize_count: int = 0
|
||||
collateral_count: int = 0
|
||||
check_num: Optional[str] = None
|
||||
dealer_name: Optional[str] = None
|
||||
dealer_phone: Optional[str] = None
|
||||
shop_name: Optional[str] = None
|
||||
memo: Optional[str] = None
|
||||
images: List[CarImageCreate] = []
|
||||
options: List[str] = []
|
||||
|
||||
|
||||
class CarUpdate(BaseModel):
|
||||
car_name: Optional[str] = None
|
||||
year: Optional[int] = None
|
||||
month: Optional[int] = None
|
||||
mileage: Optional[int] = None
|
||||
price_krw: Optional[int] = None
|
||||
margin_krw: Optional[int] = None
|
||||
margin_mn: Optional[int] = None
|
||||
price_usd: Optional[Decimal] = None
|
||||
fuel: Optional[str] = None
|
||||
transmission: Optional[str] = None
|
||||
color: Optional[str] = None
|
||||
status: Optional[str] = None
|
||||
is_displayed: Optional[bool] = None
|
||||
|
||||
|
||||
class CarResponse(BaseModel):
|
||||
id: int
|
||||
source: str
|
||||
source_id: str
|
||||
car_name: Optional[str] = None
|
||||
year: Optional[int] = None
|
||||
month: Optional[int] = None
|
||||
mileage: Optional[int] = None
|
||||
price_krw: Optional[int] = None
|
||||
margin_krw: Optional[int] = 0
|
||||
margin_mn: Optional[int] = 0
|
||||
final_price_krw: Optional[int] = None # Computed: price_krw + margin_krw (for Korean users)
|
||||
final_price_mn: Optional[int] = None # Computed: price_krw + margin_mn (for Mongolian users)
|
||||
price_usd: Optional[Decimal] = None
|
||||
is_displayed: bool = False
|
||||
fuel: Optional[str] = None
|
||||
transmission: Optional[str] = None
|
||||
color: Optional[str] = None
|
||||
displacement: Optional[int] = None
|
||||
car_number: Optional[str] = None
|
||||
seize_count: int
|
||||
collateral_count: int
|
||||
check_num: Optional[str] = None
|
||||
dealer_name: Optional[str] = None
|
||||
dealer_description: Optional[str] = None
|
||||
dealer_description_en: Optional[str] = None
|
||||
dealer_description_mn: Optional[str] = None
|
||||
dealer_description_ru: Optional[str] = None
|
||||
status: str
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
|
||||
maker: Optional[CarMakerResponse] = None
|
||||
model: Optional[CarModelResponse] = None
|
||||
images: List[CarImageResponse] = []
|
||||
specification: Optional[CarSpecificationResponse] = None
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
class CarListResponse(BaseModel):
|
||||
total: int
|
||||
page: int
|
||||
page_size: int
|
||||
cars: List[CarResponse]
|
||||
80
backend/app/schemas/dealer.py
Normal file
80
backend/app/schemas/dealer.py
Normal file
@@ -0,0 +1,80 @@
|
||||
from pydantic import BaseModel
|
||||
from typing import Optional
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
class DealerApplicationCreate(BaseModel):
|
||||
"""Schema for creating a dealer application"""
|
||||
business_name: str
|
||||
business_number: Optional[str] = None
|
||||
real_name: str
|
||||
id_number: Optional[str] = None # Will be encrypted before storage
|
||||
phone: str
|
||||
bank_name: str
|
||||
bank_account: str
|
||||
account_holder: str
|
||||
photo_url: Optional[str] = None
|
||||
|
||||
|
||||
class DealerApplicationResponse(BaseModel):
|
||||
"""Schema for dealer application response"""
|
||||
id: int
|
||||
user_id: int
|
||||
business_name: str
|
||||
business_number: Optional[str] = None
|
||||
real_name: str
|
||||
phone: str
|
||||
bank_name: str
|
||||
bank_account: str
|
||||
account_holder: str
|
||||
photo_url: Optional[str] = None
|
||||
status: str
|
||||
rejected_reason: Optional[str] = None
|
||||
applied_at: datetime
|
||||
approved_at: Optional[datetime] = None
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
class DealerApplicationApprove(BaseModel):
|
||||
"""Schema for approving a dealer application"""
|
||||
pass # No additional fields needed
|
||||
|
||||
|
||||
class DealerApplicationReject(BaseModel):
|
||||
"""Schema for rejecting a dealer application"""
|
||||
reason: str
|
||||
|
||||
|
||||
class DealerInfoResponse(BaseModel):
|
||||
"""Schema for dealer info response"""
|
||||
id: int
|
||||
user_id: int
|
||||
dealer_code: str
|
||||
dealer_card_url: Optional[str] = None
|
||||
business_name: str
|
||||
real_name: str
|
||||
phone: str
|
||||
photo_url: Optional[str] = None
|
||||
total_commission_earned: float
|
||||
total_withdrawn: float
|
||||
pending_withdrawal: float
|
||||
is_active: bool
|
||||
created_at: datetime
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
class DealerPublicInfo(BaseModel):
|
||||
"""Public dealer info for displaying in lists"""
|
||||
id: int
|
||||
dealer_code: str
|
||||
business_name: str
|
||||
real_name: str
|
||||
photo_url: Optional[str] = None
|
||||
is_active: bool
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
101
backend/app/schemas/hero_banner.py
Normal file
101
backend/app/schemas/hero_banner.py
Normal file
@@ -0,0 +1,101 @@
|
||||
from pydantic import BaseModel, HttpUrl
|
||||
from typing import Optional
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
# ==================== Hero Banner Settings ====================
|
||||
|
||||
class HeroBannerSettingsBase(BaseModel):
|
||||
slide_interval: int = 3000
|
||||
animation_type: str = "film-strip"
|
||||
image_width: int = 500
|
||||
image_height: int = 300
|
||||
auto_play: bool = True
|
||||
|
||||
|
||||
class HeroBannerSettingsUpdate(BaseModel):
|
||||
slide_interval: Optional[int] = None
|
||||
animation_type: Optional[str] = None
|
||||
image_width: Optional[int] = None
|
||||
image_height: Optional[int] = None
|
||||
auto_play: Optional[bool] = None
|
||||
|
||||
|
||||
class HeroBannerSettingsResponse(HeroBannerSettingsBase):
|
||||
id: int
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
# ==================== Hero Banner ====================
|
||||
|
||||
class HeroBannerBase(BaseModel):
|
||||
title_ko: Optional[str] = None
|
||||
title_en: Optional[str] = None
|
||||
title_mn: Optional[str] = None
|
||||
subtitle_ko: Optional[str] = None
|
||||
subtitle_en: Optional[str] = None
|
||||
subtitle_mn: Optional[str] = None
|
||||
image_url: str
|
||||
link_url: Optional[str] = None
|
||||
car_id: Optional[int] = None
|
||||
is_active: bool = True
|
||||
display_order: int = 0
|
||||
|
||||
|
||||
class HeroBannerCreate(HeroBannerBase):
|
||||
pass
|
||||
|
||||
|
||||
class HeroBannerUpdate(BaseModel):
|
||||
title_ko: Optional[str] = None
|
||||
title_en: Optional[str] = None
|
||||
title_mn: Optional[str] = None
|
||||
subtitle_ko: Optional[str] = None
|
||||
subtitle_en: Optional[str] = None
|
||||
subtitle_mn: Optional[str] = None
|
||||
image_url: Optional[str] = None
|
||||
link_url: Optional[str] = None
|
||||
car_id: Optional[int] = None
|
||||
is_active: Optional[bool] = None
|
||||
display_order: Optional[int] = None
|
||||
|
||||
|
||||
class HeroBannerResponse(HeroBannerBase):
|
||||
id: int
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
class HeroBannerListResponse(BaseModel):
|
||||
id: int
|
||||
title_ko: Optional[str] = None
|
||||
title_en: Optional[str] = None
|
||||
image_url: str
|
||||
link_url: Optional[str] = None
|
||||
car_id: Optional[int] = None
|
||||
is_active: bool
|
||||
display_order: int
|
||||
created_at: datetime
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
# 다국어 지원 응답 (Public API용)
|
||||
class HeroBannerLocalizedResponse(BaseModel):
|
||||
id: int
|
||||
title: Optional[str] = None
|
||||
subtitle: Optional[str] = None
|
||||
image_url: str
|
||||
link_url: Optional[str] = None
|
||||
car_id: Optional[int] = None
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
67
backend/app/schemas/inquiry.py
Normal file
67
backend/app/schemas/inquiry.py
Normal file
@@ -0,0 +1,67 @@
|
||||
from pydantic import BaseModel
|
||||
from typing import Optional, List
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
class InquiryCreate(BaseModel):
|
||||
category: str = "general"
|
||||
subject: Optional[str] = None
|
||||
message: str
|
||||
contact_email: Optional[str] = None
|
||||
contact_phone: Optional[str] = None
|
||||
car_id: Optional[int] = None # For backward compatibility
|
||||
|
||||
|
||||
class InquiryResponse(BaseModel):
|
||||
id: int
|
||||
user_id: Optional[int] = None
|
||||
car_id: Optional[int] = None
|
||||
category: Optional[str] = "general"
|
||||
subject: Optional[str] = None
|
||||
message: str
|
||||
contact_email: Optional[str] = None
|
||||
contact_phone: Optional[str] = None
|
||||
status: str
|
||||
admin_response: Optional[str] = None
|
||||
responded_at: Optional[datetime] = None
|
||||
responded_by: Optional[int] = None
|
||||
created_at: datetime
|
||||
updated_at: Optional[datetime] = None
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
class InquiryMessageCreate(BaseModel):
|
||||
message: str
|
||||
|
||||
|
||||
class InquiryMessageResponse(BaseModel):
|
||||
id: int
|
||||
inquiry_id: int
|
||||
user_id: int
|
||||
message: str
|
||||
is_admin: bool
|
||||
created_at: datetime
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
class InquiryWithMessages(BaseModel):
|
||||
inquiry: InquiryResponse
|
||||
messages: List[InquiryMessageResponse]
|
||||
|
||||
|
||||
class InquiryListResponse(BaseModel):
|
||||
inquiries: List[InquiryResponse]
|
||||
total: int
|
||||
|
||||
|
||||
class AdminInquiryRespond(BaseModel):
|
||||
message: str
|
||||
status: Optional[str] = None # Can update status with response
|
||||
|
||||
|
||||
class AdminInquiryUpdateStatus(BaseModel):
|
||||
status: str
|
||||
44
backend/app/schemas/notification.py
Normal file
44
backend/app/schemas/notification.py
Normal file
@@ -0,0 +1,44 @@
|
||||
from pydantic import BaseModel
|
||||
from typing import Optional, List
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
class NotificationCreate(BaseModel):
|
||||
"""Create notification schema"""
|
||||
user_id: int
|
||||
notification_type: str
|
||||
title: str
|
||||
message: str
|
||||
link: Optional[str] = None
|
||||
related_id: Optional[int] = None
|
||||
related_type: Optional[str] = None
|
||||
|
||||
|
||||
class NotificationResponse(BaseModel):
|
||||
"""Notification response schema"""
|
||||
id: int
|
||||
user_id: int
|
||||
notification_type: str
|
||||
title: str
|
||||
message: str
|
||||
link: Optional[str] = None
|
||||
related_id: Optional[int] = None
|
||||
related_type: Optional[str] = None
|
||||
is_read: bool
|
||||
read_at: Optional[datetime] = None
|
||||
created_at: datetime
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
class NotificationListResponse(BaseModel):
|
||||
"""Notification list with unread count"""
|
||||
notifications: List[NotificationResponse]
|
||||
unread_count: int
|
||||
total: int
|
||||
|
||||
|
||||
class NotificationMarkRead(BaseModel):
|
||||
"""Mark notifications as read"""
|
||||
notification_ids: List[int]
|
||||
41
backend/app/schemas/referral.py
Normal file
41
backend/app/schemas/referral.py
Normal file
@@ -0,0 +1,41 @@
|
||||
from pydantic import BaseModel
|
||||
from typing import Optional
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
class ReferralRewardResponse(BaseModel):
|
||||
"""레퍼럴 보상 응답 스키마"""
|
||||
id: int
|
||||
referrer_id: int
|
||||
referred_user_id: int
|
||||
payment_amount: float
|
||||
reward_amount: float
|
||||
status: str
|
||||
created_at: datetime
|
||||
credited_at: Optional[datetime] = None
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
class ReferralStats(BaseModel):
|
||||
"""레퍼럴 통계 스키마"""
|
||||
total_referrals: int # 총 추천한 회원 수
|
||||
total_rewards_earned: float # 총 보상 금액
|
||||
total_rewards_credited: float # 적립된 보상 금액
|
||||
total_rewards_pending: float # 대기 중인 보상 금액
|
||||
available_for_withdrawal: float # 출금 가능 금액
|
||||
|
||||
|
||||
class ReferralSettingsResponse(BaseModel):
|
||||
"""레퍼럴 설정 응답 스키마"""
|
||||
referral_reward_enabled: bool
|
||||
referral_reward_percent: float
|
||||
referral_reward_type: str # one_time / recurring
|
||||
|
||||
|
||||
class ReferralSettingsUpdate(BaseModel):
|
||||
"""레퍼럴 설정 업데이트 스키마"""
|
||||
referral_reward_enabled: Optional[bool] = None
|
||||
referral_reward_percent: Optional[float] = None
|
||||
referral_reward_type: Optional[str] = None
|
||||
37
backend/app/schemas/settings.py
Normal file
37
backend/app/schemas/settings.py
Normal file
@@ -0,0 +1,37 @@
|
||||
from pydantic import BaseModel
|
||||
from typing import Optional
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
class SystemSettingsUpdate(BaseModel):
|
||||
"""시스템 설정 수정용 스키마"""
|
||||
search_page_size: Optional[int] = None
|
||||
korea_margin_percent: Optional[float] = None
|
||||
mongolia_margin_percent: Optional[float] = None
|
||||
cc_per_usdc: Optional[int] = None
|
||||
cc_per_view: Optional[int] = None
|
||||
cc_signup_bonus: Optional[int] = None
|
||||
cars_per_cc: Optional[int] = None
|
||||
cache_ttl_hours: Optional[int] = None
|
||||
container_logistics_usd: Optional[int] = None
|
||||
shoring_cost_usd: Optional[int] = None
|
||||
|
||||
|
||||
class SystemSettingsResponse(BaseModel):
|
||||
"""시스템 설정 응답 스키마"""
|
||||
id: int
|
||||
search_page_size: int
|
||||
korea_margin_percent: float
|
||||
mongolia_margin_percent: float
|
||||
cc_per_usdc: int
|
||||
cc_per_view: int
|
||||
cc_signup_bonus: int
|
||||
cars_per_cc: int
|
||||
cache_ttl_hours: int
|
||||
container_logistics_usd: int
|
||||
shoring_cost_usd: int
|
||||
created_at: Optional[datetime] = None
|
||||
updated_at: Optional[datetime] = None
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
52
backend/app/schemas/translation.py
Normal file
52
backend/app/schemas/translation.py
Normal file
@@ -0,0 +1,52 @@
|
||||
from pydantic import BaseModel
|
||||
from typing import Optional, List
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
class TranslationCreate(BaseModel):
|
||||
source_text: str
|
||||
category: str
|
||||
text_en: Optional[str] = None
|
||||
text_mn: Optional[str] = None
|
||||
text_ru: Optional[str] = None
|
||||
|
||||
|
||||
class TranslationUpdate(BaseModel):
|
||||
source_text: Optional[str] = None
|
||||
category: Optional[str] = None
|
||||
text_en: Optional[str] = None
|
||||
text_mn: Optional[str] = None
|
||||
text_ru: Optional[str] = None
|
||||
|
||||
|
||||
class TranslationResponse(BaseModel):
|
||||
id: int
|
||||
source_text: str
|
||||
category: str
|
||||
text_en: Optional[str] = None
|
||||
text_mn: Optional[str] = None
|
||||
text_ru: Optional[str] = None
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
class TranslationListResponse(BaseModel):
|
||||
total: int
|
||||
page: int
|
||||
page_size: int
|
||||
translations: List[TranslationResponse]
|
||||
|
||||
|
||||
class TranslationBulkRequest(BaseModel):
|
||||
"""Bulk translation lookup request"""
|
||||
texts: List[str]
|
||||
category: Optional[str] = None
|
||||
lang: str = "en"
|
||||
|
||||
|
||||
class TranslationBulkResponse(BaseModel):
|
||||
"""Returns a dictionary mapping source text to translated text"""
|
||||
translations: dict # {source_text: translated_text}
|
||||
62
backend/app/schemas/user.py
Normal file
62
backend/app/schemas/user.py
Normal file
@@ -0,0 +1,62 @@
|
||||
from pydantic import BaseModel, EmailStr
|
||||
from typing import Optional
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
class UserCreate(BaseModel):
|
||||
email: EmailStr
|
||||
password: str
|
||||
name: Optional[str] = None
|
||||
phone: Optional[str] = None
|
||||
country: str = "Mongolia"
|
||||
referred_by: Optional[str] = None # Referral code of the user who referred
|
||||
|
||||
|
||||
class UserUpdate(BaseModel):
|
||||
"""Schema for updating user profile"""
|
||||
name: Optional[str] = None
|
||||
phone: Optional[str] = None
|
||||
country: Optional[str] = None
|
||||
|
||||
|
||||
class UserResponse(BaseModel):
|
||||
id: int
|
||||
email: str
|
||||
name: Optional[str] = None
|
||||
phone: Optional[str] = None
|
||||
country: str
|
||||
is_active: bool
|
||||
is_admin: bool = False
|
||||
is_dealer: bool = False
|
||||
cc_balance: float = 0.0 # Float to support fractional CC (e.g., 0.1 CC)
|
||||
referral_code: Optional[str] = None # User's unique referral code
|
||||
email_verified: bool = False
|
||||
phone_verified: bool = False
|
||||
created_at: datetime
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
class CarViewResponse(BaseModel):
|
||||
id: int
|
||||
user_id: int
|
||||
car_id: int
|
||||
cc_paid: int
|
||||
created_at: datetime
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
class PurchaseViewRequest(BaseModel):
|
||||
car_id: int
|
||||
|
||||
|
||||
class Token(BaseModel):
|
||||
access_token: str
|
||||
token_type: str = "bearer"
|
||||
|
||||
|
||||
class TokenData(BaseModel):
|
||||
email: Optional[str] = None
|
||||
122
backend/app/schemas/vehicle_request.py
Normal file
122
backend/app/schemas/vehicle_request.py
Normal file
@@ -0,0 +1,122 @@
|
||||
from pydantic import BaseModel
|
||||
from typing import Optional, List, Any
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
# Vehicle Request Schemas
|
||||
class VehicleRequestCreate(BaseModel):
|
||||
maker_code: str
|
||||
maker_name: Optional[str] = None
|
||||
model_code: str
|
||||
model_name: Optional[str] = None
|
||||
grade_code: Optional[str] = None
|
||||
grade_name: Optional[str] = None
|
||||
year_from: Optional[int] = None
|
||||
year_to: Optional[int] = None
|
||||
mileage_min: Optional[int] = None
|
||||
mileage_max: Optional[int] = None
|
||||
fuel: Optional[str] = None
|
||||
displacement_min: Optional[int] = None
|
||||
displacement_max: Optional[int] = None
|
||||
|
||||
|
||||
class VehicleRequestResponse(BaseModel):
|
||||
id: int
|
||||
user_id: int
|
||||
maker_code: Optional[str]
|
||||
maker_name: Optional[str]
|
||||
model_code: Optional[str]
|
||||
model_name: Optional[str]
|
||||
grade_code: Optional[str]
|
||||
grade_name: Optional[str]
|
||||
year_from: Optional[int]
|
||||
year_to: Optional[int]
|
||||
mileage_min: Optional[int]
|
||||
mileage_max: Optional[int]
|
||||
fuel: Optional[str]
|
||||
displacement_min: Optional[int]
|
||||
displacement_max: Optional[int]
|
||||
status: str
|
||||
admin_reviewed_at: Optional[datetime]
|
||||
created_at: datetime
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
# Request Vehicle (Admin recommended) Schemas
|
||||
class RequestVehicleCreate(BaseModel):
|
||||
request_id: int
|
||||
car_data: dict
|
||||
is_approved: bool = False
|
||||
|
||||
|
||||
class RequestVehicleResponse(BaseModel):
|
||||
id: int
|
||||
request_id: int
|
||||
car_data: dict
|
||||
is_approved: bool
|
||||
approved_at: Optional[datetime]
|
||||
created_at: datetime
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
class RequestVehicleApprove(BaseModel):
|
||||
vehicle_ids: List[int]
|
||||
|
||||
|
||||
# Purchased Vehicle Schemas
|
||||
class PurchasedVehicleCreate(BaseModel):
|
||||
car_name: str
|
||||
car_data: Optional[dict] = None
|
||||
car_image: Optional[str] = None
|
||||
vehicle_price_krw: int
|
||||
domestic_cost_krw: int
|
||||
shipping_cost_usd: int
|
||||
total_cost_krw: int
|
||||
car_type: str # small, compact
|
||||
selected_dealer_id: Optional[int] = None # Selected dealer for commission split
|
||||
|
||||
|
||||
class PurchasedVehicleResponse(BaseModel):
|
||||
id: int
|
||||
user_id: int
|
||||
car_name: Optional[str]
|
||||
car_data: Optional[dict]
|
||||
car_image: Optional[str]
|
||||
vehicle_price_krw: Optional[int]
|
||||
domestic_cost_krw: Optional[int]
|
||||
shipping_cost_usd: Optional[int]
|
||||
total_cost_krw: Optional[int]
|
||||
car_type: Optional[str]
|
||||
selected_dealer_id: Optional[int] = None
|
||||
dealer_commission_krw: Optional[int] = 0
|
||||
platform_commission_krw: Optional[int] = 0
|
||||
commission_paid: bool = False
|
||||
commission_paid_at: Optional[datetime] = None
|
||||
shipping_status: int
|
||||
status_updated_at: Optional[datetime]
|
||||
current_location: Optional[str]
|
||||
estimated_arrival: Optional[datetime]
|
||||
purchased_at: datetime
|
||||
delivered_at: Optional[datetime]
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
class PurchasedVehicleUpdateStatus(BaseModel):
|
||||
shipping_status: int # 1-7: 구매완료, 인천항, 텐진항, 자먼우드, 울란바토르, 통관, 배송완료
|
||||
current_location: Optional[str] = None
|
||||
estimated_arrival: Optional[datetime] = None
|
||||
|
||||
|
||||
# List response with request and approved vehicles
|
||||
class VehicleRequestWithVehicles(BaseModel):
|
||||
request: VehicleRequestResponse
|
||||
approved_vehicles: List[RequestVehicleResponse]
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
69
backend/app/schemas/vehicle_share.py
Normal file
69
backend/app/schemas/vehicle_share.py
Normal file
@@ -0,0 +1,69 @@
|
||||
from pydantic import BaseModel
|
||||
from typing import Optional
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
class VehicleShareCreate(BaseModel):
|
||||
"""Schema for creating a vehicle share"""
|
||||
request_vehicle_id: int
|
||||
markup_amount_krw: float = 0
|
||||
|
||||
|
||||
class VehicleShareResponse(BaseModel):
|
||||
"""Schema for vehicle share response"""
|
||||
id: int
|
||||
user_id: int
|
||||
request_vehicle_id: int
|
||||
share_code: str
|
||||
original_price_krw: float
|
||||
markup_amount_krw: float
|
||||
shared_price_krw: float
|
||||
view_count: int
|
||||
is_purchased: bool
|
||||
purchased_by_user_id: Optional[int] = None
|
||||
created_at: datetime
|
||||
expires_at: Optional[datetime] = None
|
||||
purchased_at: Optional[datetime] = None
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
class VehicleSharePublic(BaseModel):
|
||||
"""Public schema for shared vehicle (for viewing shared link)"""
|
||||
id: int
|
||||
share_code: str
|
||||
shared_price_krw: float
|
||||
view_count: int
|
||||
is_purchased: bool
|
||||
created_at: datetime
|
||||
# Vehicle info will be added separately
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
class ShareRewardResponse(BaseModel):
|
||||
"""Schema for share reward response"""
|
||||
id: int
|
||||
user_id: int
|
||||
vehicle_share_id: int
|
||||
markup_amount: float
|
||||
reward_amount: float
|
||||
tax_amount: float
|
||||
net_amount: float
|
||||
status: str
|
||||
withdrawn_at: Optional[datetime] = None
|
||||
created_at: datetime
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
class ShareRewardSummary(BaseModel):
|
||||
"""Summary of user's share rewards"""
|
||||
total_rewards: float
|
||||
total_withdrawn: float
|
||||
pending_amount: float
|
||||
available_for_withdrawal: float
|
||||
reward_count: int
|
||||
44
backend/app/schemas/withdrawal.py
Normal file
44
backend/app/schemas/withdrawal.py
Normal file
@@ -0,0 +1,44 @@
|
||||
from pydantic import BaseModel
|
||||
from typing import Optional
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
class WithdrawalRequestCreate(BaseModel):
|
||||
"""Schema for creating a withdrawal request"""
|
||||
amount: float
|
||||
bank_name: str
|
||||
bank_account: str
|
||||
account_holder: str
|
||||
|
||||
|
||||
class WithdrawalRequestResponse(BaseModel):
|
||||
"""Schema for withdrawal request response"""
|
||||
id: int
|
||||
user_id: int
|
||||
amount: float
|
||||
tax_withheld: float
|
||||
net_amount: float
|
||||
bank_name: str
|
||||
bank_account: str
|
||||
account_holder: str
|
||||
status: str
|
||||
admin_note: Optional[str] = None
|
||||
requested_at: datetime
|
||||
processed_at: Optional[datetime] = None
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
class WithdrawalProcess(BaseModel):
|
||||
"""Schema for processing a withdrawal (admin)"""
|
||||
status: str # approved, completed, rejected
|
||||
admin_note: Optional[str] = None
|
||||
|
||||
|
||||
class WithdrawalBalance(BaseModel):
|
||||
"""Schema for user's withdrawal balance"""
|
||||
total_earned: float # Total earnings (dealer commission + share rewards)
|
||||
total_withdrawn: float # Total already withdrawn
|
||||
pending_withdrawal: float # Currently pending withdrawal requests
|
||||
available_balance: float # Available for withdrawal
|
||||
Reference in New Issue
Block a user