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:
138
backend/app/models/user.py
Normal file
138
backend/app/models/user.py
Normal file
@@ -0,0 +1,138 @@
|
||||
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"]
|
||||
Reference in New Issue
Block a user