243 lines
12 KiB
Python
243 lines
12 KiB
Python
import uuid
|
|
|
|
from sqlalchemy.orm import declarative_base
|
|
from sqlalchemy import Column, Integer, SmallInteger, BigInteger, Numeric, Float, String, Boolean, DateTime
|
|
from sqlalchemy.dialects.postgresql import UUID, JSONB
|
|
from sqlalchemy.sql import text
|
|
|
|
from common.enums import DBType, UserStatus, UserRole, CompanyStatus
|
|
|
|
# 모든 ORM 모델의 베이스. insert 시 isinstance 체크에도 사용된다.
|
|
MAIN_BASE = declarative_base()
|
|
|
|
|
|
# 공통 mixin
|
|
# DB 계약(_DBTypeMixin)과 ERD 공통 컬럼(MainTableMixin)을 분리해 둔다.
|
|
def _utc_now_sql():
|
|
return text("(now() AT TIME ZONE 'utc')")
|
|
|
|
|
|
class _DBTypeMixin:
|
|
"""모델이 자신이 속한 논리 DB 를 알려준다 (람다 실행 시 DBType 으로 세션 선택)."""
|
|
|
|
@staticmethod
|
|
def DBType():
|
|
return DBType.MAIN.value
|
|
|
|
|
|
# ERD 공통 컬럼
|
|
class MainTableMixin(_DBTypeMixin):
|
|
created_at = Column(DateTime, nullable=False, server_default=_utc_now_sql())
|
|
updated_at = Column(DateTime, nullable=False, server_default=_utc_now_sql(), onupdate=_utc_now_sql())
|
|
deleted = Column(Boolean, nullable=False, server_default=text("false"), default=False)
|
|
|
|
|
|
# ERD 도메인 모델
|
|
class companies(MainTableMixin, MAIN_BASE):
|
|
__tablename__ = "companies"
|
|
__table_args__ = {"schema": "company"}
|
|
|
|
company_id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
|
name = Column(String(100), nullable=False)
|
|
business_number = Column(String(30), nullable=True)
|
|
code = Column(Integer, nullable=True) # 내부 인덱스용
|
|
representative_name = Column(String(50), nullable=True)
|
|
email = Column(String(255), nullable=True)
|
|
contact_number = Column(String(20), nullable=True)
|
|
website_url = Column(String(255), nullable=True)
|
|
industry = Column(SmallInteger, nullable=True) # 업종 코드 (스키마 SMALLINT)
|
|
status = Column(SmallInteger, nullable=False, default=CompanyStatus.ACTIVE.value) # CompanyStatus
|
|
|
|
|
|
class users(MainTableMixin, MAIN_BASE):
|
|
__tablename__ = "users"
|
|
__table_args__ = {"schema": "company"}
|
|
|
|
user_id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
|
company_id = Column(UUID(as_uuid=True), nullable=False, index=True)
|
|
id = Column(String(20), nullable=False, unique=True, index=True) # 로그인 아이디
|
|
password = Column(String(255), nullable=False) # bcrypt 해시 (ERD VARCHAR(30)→255 확장)
|
|
name = Column(String(50), nullable=True)
|
|
email = Column(String(255), nullable=True)
|
|
contact_number = Column(String(20), nullable=True)
|
|
last_accessed_at = Column(DateTime, nullable=False, server_default=_utc_now_sql())
|
|
status = Column(SmallInteger, nullable=False, default=UserStatus.ACTIVE.value)
|
|
role = Column(SmallInteger, nullable=False, default=UserRole.USER.value)
|
|
|
|
|
|
class items(MainTableMixin, MAIN_BASE):
|
|
__tablename__ = "items"
|
|
__table_args__ = {"schema": "partner"}
|
|
|
|
item_id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
|
company_id = Column(UUID(as_uuid=True), nullable=False, index=True)
|
|
user_id = Column(UUID(as_uuid=True), nullable=False, index=True) # 등록한 유저
|
|
|
|
name = Column(String(100), nullable=False)
|
|
code = Column(String(30), nullable=True)
|
|
category = Column(String(255), nullable=True)
|
|
category_type = Column(Integer, nullable=False, default=1) # 카테고리 탐색용 자동 증가 숫자
|
|
|
|
image_url = Column(String(255), nullable=True)
|
|
model_name = Column(String(100), nullable=True)
|
|
spec = Column(String(255), nullable=True)
|
|
manufacturer = Column(String(50), nullable=True)
|
|
made_in = Column(String(100), nullable=True)
|
|
|
|
price = Column(BigInteger, nullable=True) # 금액(원), 스키마 BIGINT
|
|
internet_lowest_price_yn = Column(Boolean, nullable=False, default=False) # 최저가 솔루션 원자성 보존용
|
|
|
|
moq = Column(String(50), nullable=True) # 최소 주문 수량
|
|
lead_time = Column(SmallInteger, nullable=True) # 주문 후 배송 도착까지 시간
|
|
quantity_unit = Column(String(50), nullable=True) # 단위 라벨(자유입력): EA/BOX/SET/ROLL ...
|
|
delivery_type = Column(SmallInteger, nullable=True) # 배송 유형 코드
|
|
vat_yn = Column(Boolean, nullable=True)
|
|
delivery_fee_yn = Column(Boolean, nullable=True)
|
|
|
|
|
|
class suppliers(MainTableMixin, MAIN_BASE):
|
|
__tablename__ = "suppliers"
|
|
__table_args__ = {"schema": "partner"}
|
|
|
|
supplier_id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
|
company_id = Column(UUID(as_uuid=True), nullable=False, index=True)
|
|
user_id = Column(UUID(as_uuid=True), nullable=False, index=True) # 등록한 유저
|
|
|
|
name = Column(String(100), nullable=False)
|
|
code = Column(String(20), nullable=True)
|
|
manager_name = Column(String(50), nullable=True)
|
|
manager_email = Column(String(255), nullable=True)
|
|
manager_contact_number = Column(String(20), nullable=True) # ERD 오타(manger) 교정
|
|
priority = Column(String(10), nullable=True) # True/False 가 아닌 string value 가능
|
|
|
|
|
|
class nego_cards(MainTableMixin, MAIN_BASE):
|
|
__tablename__ = "nego_cards"
|
|
__table_args__ = {"schema": "card"}
|
|
|
|
nego_card_id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
|
user_id = Column(UUID(as_uuid=True), nullable=True, index=True) # 등록 유저(o2o 기본 카드는 NULL)
|
|
name = Column(String(20), nullable=True) # 카드명
|
|
number = Column(String(10), nullable=True) # 식별번호(카드코드)
|
|
script = Column(String(255), nullable=True) # 협상 스크립트(평문 미리보기)
|
|
edit_script = Column(JSONB, nullable=True) # 편집된 스크립트(Slate JSON)
|
|
|
|
|
|
class wild_cards(MainTableMixin, MAIN_BASE):
|
|
__tablename__ = "wild_cards"
|
|
__table_args__ = {"schema": "card"}
|
|
|
|
wild_card_id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
|
user_id = Column(UUID(as_uuid=True), nullable=True, index=True) # 등록 유저(o2o 기본 카드는 NULL)
|
|
name = Column(String(20), nullable=True) # 카드명
|
|
number = Column(String(10), nullable=True) # 식별번호(카드코드)
|
|
script = Column(String(255), nullable=True) # 협상 스크립트(평문 미리보기)
|
|
edit_script = Column(JSONB, nullable=True) # 편집된 스크립트(Slate JSON)
|
|
condition = Column(String(255), nullable=True) # 사용 조건(트리거)
|
|
available = Column(Boolean, nullable=False, default=False) # 수동 협상 적용 여부(ACTIVE/INACTIVE 매핑)
|
|
memo = Column(String(255), nullable=True) # 자유 메모
|
|
|
|
|
|
class versions(MainTableMixin, MAIN_BASE):
|
|
__tablename__ = "versions"
|
|
|
|
version_id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
|
user_id = Column(UUID(as_uuid=True), nullable=False, index=True) # 버전 생성 유저
|
|
code = Column(Integer, nullable=False, default=0) # 빠른 조회용
|
|
name = Column(String(10), nullable=False) # 버전명
|
|
|
|
|
|
class version_nego_cards(MainTableMixin, MAIN_BASE):
|
|
__tablename__ = "version_nego_cards"
|
|
|
|
vnc_id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
|
version_id = Column(UUID(as_uuid=True), nullable=False, index=True) # versions.version_id
|
|
nego_card_id = Column(UUID(as_uuid=True), nullable=False, index=True) # nego_cards.nego_card_id
|
|
|
|
|
|
class version_wild_cards(MainTableMixin, MAIN_BASE):
|
|
__tablename__ = "version_wild_cards"
|
|
|
|
vwc_id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
|
version_id = Column(UUID(as_uuid=True), nullable=False, index=True) # versions.version_id
|
|
wild_card_id = Column(UUID(as_uuid=True), nullable=False, index=True) # wild_cards.wild_card_id
|
|
|
|
|
|
class quotation_settings(MainTableMixin, MAIN_BASE):
|
|
__tablename__ = "quotation_settings"
|
|
__table_args__ = {"schema": "quotation"}
|
|
|
|
qt_setting_id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
|
user_id = Column(UUID(as_uuid=True), nullable=True, index=True) # 설정 소유 유저
|
|
target_margin_rate = Column(Numeric(8, 6), nullable=False)
|
|
anchoring_value = Column(Numeric(8, 6), nullable=False, default=0.01)
|
|
card_count = Column(Integer, nullable=False, default=3) # 한 협상 내 협상카드 사용 횟수
|
|
|
|
|
|
class quotations(MainTableMixin, MAIN_BASE):
|
|
__tablename__ = "quotations"
|
|
__table_args__ = {"schema": "quotation"}
|
|
|
|
qt_id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
|
user_id = Column(UUID(as_uuid=True), nullable=False, index=True)
|
|
qt_setting_id = Column(UUID(as_uuid=True), nullable=False, index=True)
|
|
version_id = Column(UUID(as_uuid=True), nullable=False, index=True)
|
|
|
|
name = Column(String(50), nullable=False)
|
|
number = Column(String(30), nullable=False)
|
|
type = Column(SmallInteger, nullable=False) # QuotationType: 1=renego(1:1) / 2=requote(1:N)
|
|
round = Column(Integer, nullable=False, default=1) # 재견적 진행 시 증가
|
|
status = Column(SmallInteger, nullable=False) # 진행 상태 코드
|
|
start_time = Column(DateTime, nullable=False)
|
|
end_time = Column(DateTime, nullable=False)
|
|
|
|
manager_name = Column(String(50), nullable=True)
|
|
manager_email = Column(String(255), nullable=True)
|
|
manager_contact_number = Column(String(20), nullable=True)
|
|
memo = Column(String(100), nullable=True)
|
|
|
|
iteration = Column(Integer, nullable=False, default=0)
|
|
preferred_sp_yn = Column(Boolean, nullable=True)
|
|
preferred_sp_id = Column(UUID(as_uuid=True), nullable=True)
|
|
preferred_sp_name = Column(String(20), nullable=True)
|
|
equal_bid_yn = Column(Boolean, nullable=True)
|
|
equal_bid_data = Column(JSONB, nullable=True)
|
|
|
|
|
|
class sessions(MainTableMixin, MAIN_BASE):
|
|
__tablename__ = "sessions"
|
|
__table_args__ = {"schema": "negotiation"}
|
|
|
|
session_id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
|
quotation_id = Column(UUID(as_uuid=True), nullable=False, index=True) # 소속 견적(quotations.qt_id)
|
|
item_id = Column(UUID(as_uuid=True), nullable=False, index=True) # 대상 상품(items.item_id)
|
|
supplier_id = Column(UUID(as_uuid=True), nullable=False, index=True) # 대상 공급사(suppliers.supplier_id)
|
|
|
|
qt_number = Column(String(30), nullable=False) # 견적번호 스냅샷
|
|
qt_round = Column(Integer, nullable=False) # 견적 라운드 스냅샷
|
|
qt_type = Column(SmallInteger, nullable=False) # QuotationType 스냅샷
|
|
target_price = Column(BigInteger, nullable=False) # 목표가(원)
|
|
status = Column(SmallInteger, nullable=False) # SessionStatus 코드
|
|
bid_price = Column(BigInteger, nullable=True) # 입찰가(원)
|
|
bid_at = Column(DateTime, nullable=True) # 입찰 시각
|
|
end_time = Column(DateTime, nullable=False) # 세션 종료 시각
|
|
reject_reason = Column(String(255), nullable=True)
|
|
reject_price = Column(BigInteger, nullable=True)
|
|
reject_delivery_type = Column(SmallInteger, nullable=True) # DeliveryType 코드
|
|
|
|
|
|
class chats(MainTableMixin, MAIN_BASE):
|
|
__tablename__ = "chats"
|
|
__table_args__ = {"schema": "negotiation"}
|
|
|
|
chat_id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
|
session_id = Column(UUID(as_uuid=True), nullable=False, index=True) # 소속 세션(sessions.session_id)
|
|
card_id = Column(UUID(as_uuid=True), nullable=True) # 사용된 카드(nego_cards.nego_card_id)
|
|
|
|
seq = Column(Integer, nullable=False, default=1) # 세션 내 메시지 순번(프로토콜 index)
|
|
sender = Column(SmallInteger, nullable=False) # ChatSender 코드(1=bot, 2=partner)
|
|
target_price = Column(BigInteger, nullable=False) # 제시 목표가(원)
|
|
card_used_yn = Column(Boolean, nullable=True) # 카드 사용 여부
|
|
indicator_value = Column(Numeric(8, 6), nullable=True)
|
|
card_type = Column(SmallInteger, nullable=True) # 1=nego_card, 2=wild_card
|