244 lines
6.6 KiB
Python
244 lines
6.6 KiB
Python
from datetime import datetime
|
|
from enum import Enum
|
|
from typing import TYPE_CHECKING, Optional
|
|
|
|
from sqlalchemy import BigInteger, DateTime, ForeignKey, Index, Integer, String, func
|
|
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
|
|
|
from app.database.session import Base
|
|
|
|
if TYPE_CHECKING:
|
|
from app.backoffice.admin.models import Admin
|
|
from app.user.models import User
|
|
|
|
|
|
class ChargeRequestStatus(str, Enum):
|
|
PENDING = "pending"
|
|
APPROVED = "approved"
|
|
REJECTED = "rejected"
|
|
CANCELLED = "cancelled"
|
|
|
|
|
|
class CreditTransactionType(str, Enum):
|
|
CHARGE = "charge"
|
|
CONSUME = "consume"
|
|
REFUND = "refund"
|
|
ADMIN_ADJUST = "admin_adjust"
|
|
|
|
|
|
class CreditChargeRequest(Base):
|
|
__tablename__ = "credit_charge_request"
|
|
__table_args__ = (
|
|
Index("idx_credit_request_user_uuid", "user_uuid"),
|
|
Index("idx_credit_request_status", "status"),
|
|
Index("idx_credit_request_created_at", "created_at"),
|
|
Index("idx_credit_request_status_created", "status", "created_at"),
|
|
{
|
|
"mysql_engine": "InnoDB",
|
|
"mysql_charset": "utf8mb4",
|
|
"mysql_collate": "utf8mb4_unicode_ci",
|
|
},
|
|
)
|
|
|
|
id: Mapped[int] = mapped_column(
|
|
BigInteger,
|
|
primary_key=True,
|
|
nullable=False,
|
|
autoincrement=True,
|
|
comment="고유 식별자",
|
|
)
|
|
|
|
user_uuid: Mapped[str] = mapped_column(
|
|
String(36),
|
|
ForeignKey("user.user_uuid", ondelete="CASCADE"),
|
|
nullable=False,
|
|
comment="사용자 UUID (user.user_uuid 참조)",
|
|
)
|
|
|
|
requested_amount: Mapped[int] = mapped_column(
|
|
Integer,
|
|
nullable=False,
|
|
comment="요청 크레딧 수량 (양수)",
|
|
)
|
|
|
|
message: Mapped[Optional[str]] = mapped_column(
|
|
String(500),
|
|
nullable=True,
|
|
comment="사용자 요청 메시지",
|
|
)
|
|
|
|
status: Mapped[str] = mapped_column(
|
|
String(20),
|
|
nullable=False,
|
|
default=ChargeRequestStatus.PENDING,
|
|
server_default="pending",
|
|
comment="처리 상태 (pending/approved/rejected/cancelled)",
|
|
)
|
|
|
|
admin_id: Mapped[Optional[int]] = mapped_column(
|
|
BigInteger,
|
|
ForeignKey("admin.id", ondelete="SET NULL"),
|
|
nullable=True,
|
|
comment="처리한 백오피스 관리자 ID",
|
|
)
|
|
|
|
admin_note: Mapped[Optional[str]] = mapped_column(
|
|
String(1000),
|
|
nullable=True,
|
|
comment="관리자 메모",
|
|
)
|
|
|
|
processed_at: Mapped[Optional[datetime]] = mapped_column(
|
|
DateTime,
|
|
nullable=True,
|
|
comment="처리 일시",
|
|
)
|
|
|
|
created_at: Mapped[datetime] = mapped_column(
|
|
DateTime,
|
|
nullable=False,
|
|
server_default=func.now(),
|
|
comment="요청 일시",
|
|
)
|
|
|
|
updated_at: Mapped[datetime] = mapped_column(
|
|
DateTime,
|
|
nullable=False,
|
|
server_default=func.now(),
|
|
onupdate=func.now(),
|
|
comment="수정 일시",
|
|
)
|
|
|
|
user: Mapped["User"] = relationship(
|
|
"User",
|
|
foreign_keys=[user_uuid],
|
|
primaryjoin="CreditChargeRequest.user_uuid == User.user_uuid",
|
|
back_populates="credit_requests",
|
|
lazy="noload",
|
|
)
|
|
|
|
transactions: Mapped[list["CreditTransaction"]] = relationship(
|
|
"CreditTransaction",
|
|
back_populates="charge_request",
|
|
lazy="noload",
|
|
)
|
|
|
|
admin: Mapped[Optional["Admin"]] = relationship(
|
|
"Admin",
|
|
foreign_keys=[admin_id],
|
|
primaryjoin="CreditChargeRequest.admin_id == Admin.id",
|
|
lazy="selectin",
|
|
)
|
|
|
|
def __repr__(self) -> str:
|
|
return (
|
|
f"<CreditChargeRequest("
|
|
f"id={self.id}, user_uuid='{self.user_uuid}', "
|
|
f"amount={self.requested_amount}, status='{self.status}'"
|
|
f")>"
|
|
)
|
|
|
|
|
|
class CreditTransaction(Base):
|
|
__tablename__ = "credit_transaction"
|
|
__table_args__ = (
|
|
Index("idx_credit_tx_user_uuid", "user_uuid"),
|
|
Index("idx_credit_tx_user_uuid_created", "user_uuid", "created_at"),
|
|
Index("idx_credit_tx_type", "type"),
|
|
Index("idx_credit_tx_related_request", "related_request_id"),
|
|
{
|
|
"mysql_engine": "InnoDB",
|
|
"mysql_charset": "utf8mb4",
|
|
"mysql_collate": "utf8mb4_unicode_ci",
|
|
},
|
|
)
|
|
|
|
id: Mapped[int] = mapped_column(
|
|
BigInteger,
|
|
primary_key=True,
|
|
nullable=False,
|
|
autoincrement=True,
|
|
comment="고유 식별자",
|
|
)
|
|
|
|
user_uuid: Mapped[str] = mapped_column(
|
|
String(36),
|
|
ForeignKey("user.user_uuid", ondelete="CASCADE"),
|
|
nullable=False,
|
|
comment="사용자 UUID",
|
|
)
|
|
|
|
amount: Mapped[int] = mapped_column(
|
|
Integer,
|
|
nullable=False,
|
|
comment="변경 크레딧 수량 (충전 양수, 차감 음수)",
|
|
)
|
|
|
|
balance_after: Mapped[int] = mapped_column(
|
|
Integer,
|
|
nullable=False,
|
|
comment="변경 직후 잔액",
|
|
)
|
|
|
|
type: Mapped[str] = mapped_column(
|
|
String(20),
|
|
nullable=False,
|
|
comment="변경 유형 (charge/consume/refund/admin_adjust)",
|
|
)
|
|
|
|
reason: Mapped[Optional[str]] = mapped_column(
|
|
String(255),
|
|
nullable=True,
|
|
comment="변경 사유",
|
|
)
|
|
|
|
admin_id: Mapped[Optional[int]] = mapped_column(
|
|
BigInteger,
|
|
ForeignKey("admin.id", ondelete="SET NULL"),
|
|
nullable=True,
|
|
comment="처리 관리자 ID (관리자 충전/차감 시)",
|
|
)
|
|
|
|
related_request_id: Mapped[Optional[int]] = mapped_column(
|
|
BigInteger,
|
|
ForeignKey("credit_charge_request.id", ondelete="SET NULL"),
|
|
nullable=True,
|
|
comment="연관 충전 요청 ID",
|
|
)
|
|
|
|
created_at: Mapped[datetime] = mapped_column(
|
|
DateTime,
|
|
nullable=False,
|
|
server_default=func.now(),
|
|
comment="변경 일시",
|
|
)
|
|
|
|
user: Mapped["User"] = relationship(
|
|
"User",
|
|
foreign_keys=[user_uuid],
|
|
primaryjoin="CreditTransaction.user_uuid == User.user_uuid",
|
|
back_populates="credit_transactions",
|
|
lazy="noload",
|
|
)
|
|
|
|
charge_request: Mapped[Optional[CreditChargeRequest]] = relationship(
|
|
"CreditChargeRequest",
|
|
back_populates="transactions",
|
|
lazy="noload",
|
|
)
|
|
|
|
admin: Mapped[Optional["Admin"]] = relationship(
|
|
"Admin",
|
|
foreign_keys=[admin_id],
|
|
primaryjoin="CreditTransaction.admin_id == Admin.id",
|
|
lazy="selectin",
|
|
)
|
|
|
|
def __repr__(self) -> str:
|
|
return (
|
|
f"<CreditTransaction("
|
|
f"id={self.id}, user_uuid='{self.user_uuid}', "
|
|
f"amount={self.amount}, type='{self.type}'"
|
|
f")>"
|
|
)
|