Files
AutonetSellCar/backend/app/models/user.py
AutonetSellCar Deploy 1f0dcb1ddb 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>
2025-12-30 13:24:39 +09:00

139 lines
6.6 KiB
Python

from sqlalchemy import Column, Integer, String, Boolean, DateTime, ForeignKey, Float
from sqlalchemy.orm import relationship
from sqlalchemy.sql import func
import uuid
import hashlib
from ..database import Base
def generate_referral_code():
"""Generate a unique 8-character referral code"""
unique_id = uuid.uuid4().hex
return hashlib.sha256(unique_id.encode()).hexdigest()[:8].upper()
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True, index=True)
email = Column(String(255), unique=True, nullable=False, index=True)
password_hash = Column(String(255), nullable=False)
name = Column(String(100))
phone = Column(String(50))
country = Column(String(50), default="Mongolia")
is_active = Column(Boolean, default=True)
is_admin = Column(Boolean, default=False)
is_dealer = Column(Boolean, default=False) # Dealer status
cc_balance = Column(Float, default=3.0) # CC coin balance, 3 free on signup
referral_code = Column(String(8), unique=True, index=True) # Unique referral code for sharing
referred_by = Column(String(8), nullable=True) # Referral code of the user who referred this user
# Email verification
email_verified = Column(Boolean, default=False)
email_verified_at = Column(DateTime(timezone=True), nullable=True)
# Phone verification
phone_verified = Column(Boolean, default=False)
phone_verified_at = Column(DateTime(timezone=True), nullable=True)
# Account withdrawal/deletion
withdrawal_requested_at = Column(DateTime(timezone=True), nullable=True) # 탈퇴 요청 시각
withdrawal_reason = Column(String(500), nullable=True) # 탈퇴 사유
deleted_at = Column(DateTime(timezone=True), nullable=True) # 실제 삭제 시각 (soft delete)
created_at = Column(DateTime(timezone=True), server_default=func.now())
# Note: foreign_keys specified as string to avoid circular import
inquiries = relationship("Inquiry", back_populates="user", primaryjoin="User.id == Inquiry.user_id")
car_views = relationship("CarView", back_populates="user")
performance_check_views = relationship("PerformanceCheckView", back_populates="user")
charge_history = relationship("ChargeHistory", back_populates="user", primaryjoin="User.id == ChargeHistory.user_id")
dealer_application = relationship("DealerApplication", back_populates="user", uselist=False)
dealer_info = relationship("DealerInfo", back_populates="user", uselist=False)
class VerificationCode(Base):
"""Store temporary verification codes for email and phone"""
__tablename__ = "verification_codes"
id = Column(Integer, primary_key=True, index=True)
user_id = Column(Integer, ForeignKey("users.id"), nullable=True) # Nullable for pre-registration
email = Column(String(255), nullable=True, index=True) # For email verification
phone = Column(String(50), nullable=True, index=True) # For phone verification
code = Column(String(10), nullable=False) # 6-digit code
code_type = Column(String(20), nullable=False) # 'email' or 'phone'
purpose = Column(String(50), default="verification") # 'verification', 'password_reset'
attempts = Column(Integer, default=0) # Failed verification attempts
max_attempts = Column(Integer, default=5)
expires_at = Column(DateTime(timezone=True), nullable=False)
verified_at = Column(DateTime(timezone=True), nullable=True)
created_at = Column(DateTime(timezone=True), server_default=func.now())
class CarView(Base):
"""Track which cars a user has purchased (paid CC to view full details)"""
__tablename__ = "car_views"
id = Column(Integer, primary_key=True, index=True)
user_id = Column(Integer, ForeignKey("users.id"), nullable=False)
car_id = Column(Integer, ForeignKey("cars.id"), nullable=False)
cc_paid = Column(Integer, default=1) # CC paid for this view
created_at = Column(DateTime(timezone=True), server_default=func.now())
user = relationship("User", back_populates="car_views")
car = relationship("Car", back_populates="views")
class PerformanceCheckView(Base):
"""Track which performance checks a user has purchased (paid 0.1 CC to view)"""
__tablename__ = "performance_check_views"
id = Column(Integer, primary_key=True, index=True)
user_id = Column(Integer, ForeignKey("users.id"), nullable=False)
car_id = Column(Integer, ForeignKey("cars.id"), nullable=False)
cc_paid = Column(Float, default=0.1) # CC paid for this view (0.1 CC)
created_at = Column(DateTime(timezone=True), server_default=func.now())
user = relationship("User", back_populates="performance_check_views")
class ChargeHistory(Base):
"""Track CC charge history for users"""
__tablename__ = "charge_history"
id = Column(Integer, primary_key=True, index=True)
user_id = Column(Integer, ForeignKey("users.id"), nullable=False)
package_id = Column(Integer, ForeignKey("cc_packages.id"), nullable=True) # CC package purchased
amount = Column(Integer, nullable=False) # Amount in selected currency
amount_usd = Column(Integer, nullable=True) # Amount in USD (for backwards compatibility)
cc_amount = Column(Integer, nullable=False) # CC received
bonus_cc = Column(Integer, default=0) # Bonus CC received
currency = Column(String(10), default="USD") # USD, USDC, KRW
payment_method = Column(String(50), default="stripe") # stripe, manual, usdc, bank_transfer
# Stripe fields
stripe_session_id = Column(String(200), nullable=True) # Stripe Checkout Session ID
stripe_payment_intent_id = Column(String(200), nullable=True) # Stripe Payment Intent ID
# Legacy fields
transaction_id = Column(String(100), nullable=True) # External transaction ID (crypto tx hash)
wallet_address = Column(String(100), nullable=True) # User's wallet address for refunds
admin_note = Column(String(500), nullable=True) # Admin notes
status = Column(String(20), default="pending") # pending, completed, failed, cancelled
verified_at = Column(DateTime(timezone=True), nullable=True)
verified_by = Column(Integer, ForeignKey("users.id"), nullable=True)
created_at = Column(DateTime(timezone=True), server_default=func.now())
user = relationship("User", back_populates="charge_history", foreign_keys=[user_id])
# Payment settings constants
class PaymentSettings:
USDC_WALLET_ADDRESS = "0x1234567890abcdef1234567890abcdef12345678" # Platform USDC receiving address
USDC_NETWORK = "Polygon" # Default network (Polygon for low fees)
MIN_CHARGE_USD = 10
MAX_CHARGE_USD = 10000
SUPPORTED_CURRENCIES = ["USD", "USDC", "KRW"]
SUPPORTED_METHODS = ["card", "usdc", "bank_transfer"]