o2o-negosium-original/backend/common/database/model/models.py
민헌 5940f54d2b feat(backend): supplier_users 기반 인증 재구축 및 단일세션 토큰 저장
- tbl_account 예시 인증 제거, supplier_users 기반으로 일원화(generic 네이밍 재사용)
- DBType USER/PARTNER 분리, AccountStatus/UserRole/TokenType enum 추가
- 보호 요청 시 su_id DB 존재/활성 검증(stateless JWT 빈틈 보완), 공급사명(partner.suppliers) 응답 포함
- 로그인 시 단일 세션 access/refresh 토큰을 supplier_user_tokens 에 저장(재로그인 시 교체)
- greenlet 의존성 추가, 인증 e2e 테스트(test_auth.py) 재작성

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 11:16:33 +09:00

78 lines
6.1 KiB
Python

from sqlalchemy.orm import declarative_base
from sqlalchemy import Column, Integer, String, Boolean, DateTime, SmallInteger
from sqlalchemy.dialects.postgresql import UUID, JSONB
from sqlalchemy.sql import text
from common.enums import DBType
# 모든 ORM 모델의 베이스. insert 시 isinstance 체크에도 사용된다.
MAIN_BASE = declarative_base()
class supplier_users(MAIN_BASE):
# 이 프로젝트의 기본 유저. 실제 테이블은 negosium_db 의 supplier 스키마(supplier_users).
@staticmethod
def DBType():
return DBType.USER.value
__tablename__ = "supplier_users"
__table_args__ = {"schema": "supplier"}
# gen_random_uuid() 는 pgcrypto 확장 기준. 코드값(status/role/type)은 SMALLINT 정수 코드(앱 enum 매핑).
su_id = Column(UUID(as_uuid=True), primary_key=True, server_default=text("gen_random_uuid()")) # 유저 식별자(PK)
supplier_id = Column(UUID(as_uuid=True), nullable=False) # 소속 공급사(partner.suppliers.supplier_id)
id = Column(String(20), nullable=False) # 로그인 ID
password = Column(String(255), nullable=False) # 해시된 비밀번호이어야 함
name = Column(String(50), nullable=True) # 이름
email = Column(String(255), nullable=True) # 이메일
contact_number = Column(String(20), nullable=True) # 연락처
last_accessed_at = Column(DateTime(timezone=True), nullable=False) # 마지막 접속 시각
status = Column(SmallInteger, nullable=False, server_default=text("1")) # 상태: 1=active, 2=inactive
role = Column(SmallInteger, nullable=False, server_default=text("1")) # 권한: 1=user, 2=manager
created_at = Column(DateTime(timezone=True), nullable=False, server_default=text("(now() AT TIME ZONE 'utc')")) # 생성 시각(UTC)
updated_at = Column(DateTime(timezone=True), nullable=False, server_default=text("(now() AT TIME ZONE 'utc')")) # 수정 시각(UTC, 앱에서 갱신)
deleted = Column(Boolean, nullable=False, server_default=text("false")) # 소프트 삭제 여부
class suppliers(MAIN_BASE):
# partner.suppliers (공급사 회사). 공급사명(name) 조회용. partner 도메인이라 DBType 은 PARTNER.
@staticmethod
def DBType():
return DBType.PARTNER.value
__tablename__ = "suppliers"
__table_args__ = {"schema": "partner"}
supplier_id = Column(UUID(as_uuid=True), primary_key=True, server_default=text("gen_random_uuid()")) # 공급사 식별자(PK)
company_id = Column(UUID(as_uuid=True), nullable=False) # 소속 회사(company.companies.company_id)
user_id = Column(UUID(as_uuid=True), nullable=False) # 등록 유저(company.users.user_id)
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) # 담당자 연락처
priority = Column(String(10), nullable=True) # 우선순위 (고객사별 문자열 값 가능)
created_at = Column(DateTime(timezone=True), nullable=False, server_default=text("(now() AT TIME ZONE 'utc')")) # 생성 시각(UTC)
updated_at = Column(DateTime(timezone=True), nullable=False, server_default=text("(now() AT TIME ZONE 'utc')")) # 수정 시각(UTC, 앱에서 갱신)
deleted = Column(Boolean, nullable=False, server_default=text("false")) # 소프트 삭제 여부
class supplier_user_tokens(MAIN_BASE):
# 유저 인증 토큰. supplier_users 1 : N tokens.
@staticmethod
def DBType():
return DBType.USER.value
__tablename__ = "supplier_user_tokens"
__table_args__ = {"schema": "supplier"}
sut_id = Column(UUID(as_uuid=True), primary_key=True, server_default=text("gen_random_uuid()")) # 토큰 식별자(PK)
su_id = Column(UUID(as_uuid=True), nullable=False) # 소유 유저(supplier.supplier_users.su_id)
type = Column(SmallInteger, nullable=False) # 토큰 종류 (코드, 앱 enum 매핑)
token = Column(JSONB, nullable=False) # 토큰 본문(JSON)
issued_at = Column(DateTime(timezone=True), nullable=False) # 발급 시각
expired_at = Column(DateTime(timezone=True), nullable=False) # 만료 시각
created_at = Column(DateTime(timezone=True), nullable=False, server_default=text("(now() AT TIME ZONE 'utc')")) # 생성 시각(UTC)
updated_at = Column(DateTime(timezone=True), nullable=False, server_default=text("(now() AT TIME ZONE 'utc')")) # 수정 시각(UTC, 앱에서 갱신)
deleted = Column(Boolean, nullable=False, server_default=text("false")) # 소프트 삭제 여부