Skip to content

Latest commit

ย 

History

History
1073 lines (960 loc) ยท 61.2 KB

File metadata and controls

1073 lines (960 loc) ยท 61.2 KB

Finders ERD

ํ•„๋ฆ„ ํ˜„์ƒ์†Œ ์˜ˆ์•ฝ ์„œ๋น„์Šค ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์„ค๊ณ„์„œ v3.2.0 | 2026-02-11


ํ…Œ์ด๋ธ” ๋ชฉ๋ก (38๊ฐœ)

๋„๋ฉ”์ธ ํ…Œ์ด๋ธ” ์„ค๋ช…
member member ํšŒ์› Base (Joined Table ์ƒ์†)
member_user User ์ „์šฉ (์†Œ์…œ ๋กœ๊ทธ์ธ ์‚ฌ์šฉ์ž, ํฌ๋ ˆ๋”ง ๊ด€๋ จ)
member_owner Owner ์ „์šฉ (ํ˜„์ƒ์†Œ ์‚ฌ์žฅ๋‹˜, ์ •์‚ฐ ๊ณ„์ขŒ)
member_admin Admin ์ „์šฉ (๊ด€๋ฆฌ์ž, ์ถ”ํ›„ ํ™•์žฅ)
social_account ์†Œ์…œ ๋กœ๊ทธ์ธ ์—ฐ๋™ - User ์ „์šฉ (์นด์นด์˜ค/์• ํ”Œ)
member_address ๋ฐฐ์†ก์ง€ ์ฃผ์†Œ - User ์ „์šฉ
member_device FCM ๋””๋ฐ”์ด์Šค ํ† ํฐ โ›” ๋ฐ๋ชจ๋ฐ์ด ์ „ ๋ฏธ๊ตฌํ˜„
member_agreement ์•ฝ๊ด€ ๋™์˜ ์ด๋ ฅ
terms ์•ฝ๊ด€ ๋ฒ„์ „ ๊ด€๋ฆฌ
terms_social_mapping ์•ฝ๊ด€-์†Œ์…œ ํƒœ๊ทธ ๋งคํ•‘ (์นด์นด์˜ค ๋™์˜ ํ•ญ๋ชฉ ์—ฐ๋™)
credit_history AI ํฌ๋ ˆ๋”ง ์ถฉ์ „/์‚ฌ์šฉ ๋‚ด์—ญ - User ์ „์šฉ
favorite_photo_lab ๊ด€์‹ฌ ํ˜„์ƒ์†Œ
store photo_lab ํ˜„์ƒ์†Œ ์ •๋ณด
photo_lab_image ํ˜„์ƒ์†Œ ์ด๋ฏธ์ง€
photo_lab_tag ํ˜„์ƒ์†Œ-ํƒœ๊ทธ ์กฐ์ธ ํ…Œ์ด๋ธ”
tag ํ˜„์ƒ์†Œ ํƒœ๊ทธ
photo_lab_notice ํ˜„์ƒ์†Œ ๊ณต์ง€์‚ฌํ•ญ
photo_lab_business_hour ์˜์—…์‹œ๊ฐ„
photo_lab_document ์‚ฌ์—…์ž ์ฆ๋น™ ์„œ๋ฅ˜
region ์ง€์—ญ (์‹œ/๋„, ์‹œ/๊ตฐ/๊ตฌ)
reservation reservation ์˜ˆ์•ฝ ์ •๋ณด
reservation_slot ์˜ˆ์•ฝ ์Šฌ๋กฏ (์ •์› ๊ด€๋ฆฌ)
photo development_order ํ˜„์ƒ ์ฃผ๋ฌธ
scanned_photo ์Šค์บ”๋œ ์‚ฌ์ง„
print_order ์ธํ™” ์ฃผ๋ฌธ
print_order_item ์ธํ™” ์ฃผ๋ฌธ ์ƒ์„ธ
print_order_photo ์ธํ™” ๋Œ€์ƒ ์‚ฌ์ง„ + ์ˆ˜๋Ÿ‰ (์ฃผ๋ฌธ-์Šค์บ”์‚ฌ์ง„ ๋งคํ•‘)
delivery ๋ฐฐ์†ก ์ •๋ณด
photo_restoration AI ์‚ฌ์ง„ ๋ณต์› ์š”์ฒญ
community post ๊ฒŒ์‹œ๊ธ€/๋ฆฌ๋ทฐ (์ž๊ฐ€ํ˜„์ƒ ์—ฌ๋ถ€ ํฌํ•จ)
post_image ๊ฒŒ์‹œ๊ธ€ ์ด๋ฏธ์ง€
search_history ์ตœ๊ทผ ๊ฒ€์ƒ‰์–ด ์ด๋ ฅ
comments ๋Œ“๊ธ€
post_like ์ข‹์•„์š”
inquiry inquiry 1:1 ๋ฌธ์˜
inquiry_image ๋ฌธ์˜ ์ฒจ๋ถ€ ์ด๋ฏธ์ง€
inquiry_reply ๋ฌธ์˜ ๋‹ต๋ณ€
common notice ๊ณต์ง€์‚ฌํ•ญ โ›” ๋ฐ๋ชจ๋ฐ์ด ์ „ ๋ฏธ๊ตฌํ˜„
promotion ํ”„๋กœ๋ชจ์…˜/๋ฐฐ๋„ˆ โ›” ๋ฐ๋ชจ๋ฐ์ด ์ „ ๋ฏธ๊ตฌํ˜„
notification ์•Œ๋ฆผ โ›” ๋ฐ๋ชจ๋ฐ์ด ์ „ ๋ฏธ๊ตฌํ˜„
payment ๊ฒฐ์ œ ์ •๋ณด (ํฌํŠธ์› V2)

ERD ๊ด€๊ณ„๋„

member (Base: Joined Table ์ƒ์†)
   โ”‚
   โ”œโ”€โ”€ 1:1 โ”€ member_user โ”€โ”ฌโ”€ 1:N โ”€ social_account (์†Œ์…œ ๋กœ๊ทธ์ธ)
   โ”‚                      โ”œโ”€ 1:N โ”€ member_address (๋ฐฐ์†ก์ง€)
   โ”‚                      โ””โ”€ 1:N โ”€ credit_history (ํฌ๋ ˆ๋”ง ๋‚ด์—ญ)
   โ”‚
   โ”œโ”€โ”€ 1:1 โ”€ member_owner โ”€โ”€โ”€ 1:N โ”€ photo_lab (ํ˜„์ƒ์†Œ ์šด์˜)
   โ”‚
   โ””โ”€โ”€ 1:1 โ”€ member_admin (์ถ”ํ›„ ํ™•์žฅ)

member โ”€โ”ฌโ”€ 1:N โ”€ member_device (FCM ํ† ํฐ, ๊ณตํ†ต) โ›” ๋ฏธ๊ตฌํ˜„
        โ”œโ”€ 1:N โ”€ member_agreement โ”€โ”€โ”€ N:1 โ”€ terms โ”€โ”€โ”€ 1:N โ”€ terms_social_mapping (์†Œ์…œ ํƒœ๊ทธ ๋งคํ•‘)
        โ”œโ”€ 1:N โ”€ reservation โ”€โ”€โ”€ N:1 โ”€ reservation_slot (์Šฌ๋กฏ ์ •์› ๊ด€๋ฆฌ)
        โ”‚    โ””โ”€โ”€ 0..1:1 โ”€ development_order โ”€โ”ฌโ”€ 1:N โ”€ scanned_photo
        โ”‚                                    โ””โ”€ 0..1:N โ”€ print_order โ”€โ”ฌโ”€ 1:N โ”€ print_order_item
        โ”‚                                                             โ””โ”€ 0..1:1 โ”€ delivery
        โ”œโ”€ 1:N โ”€ development_order (ํ˜„์žฅ ์ฃผ๋ฌธ, ์˜ˆ์•ฝ ์—†์ด)
        โ”œโ”€ 1:N โ”€ print_order (ํ˜„์žฅ ์ฃผ๋ฌธ, ํ˜„์ƒ ์—†์ด)
        โ”œโ”€ 1:N โ”€ photo_restoration (AI ๋ณต์›, ํฌ๋ ˆ๋”ง ์‚ฌ์šฉ)
        โ”œโ”€ 1:N โ”€ post โ”€โ”ฌโ”€ 1:N โ”€ post_image
        โ”‚              โ”œโ”€ 1:N โ”€ comments
        โ”‚              โ””โ”€ 1:N โ”€ post_like
        โ”œโ”€ 1:N โ”€ favorite_photo_lab
        โ”œโ”€ 1:N โ”€ inquiry โ”€โ”ฌโ”€ 1:N โ”€ inquiry_image
        โ”‚                 โ””โ”€ 1:N โ”€ inquiry_reply
        โ”œโ”€ 1:N โ”€ notification โ›” ๋ฏธ๊ตฌํ˜„
        โ””โ”€ 1:N โ”€ payment (๊ฒฐ์ œ, ํฌ๋ ˆ๋”ง ๊ตฌ๋งค)

photo_lab โ”€โ”ฌโ”€ 1:N โ”€ photo_lab_image
           โ”œโ”€ N:N โ”€ tag (via photo_lab_tag ์กฐ์ธ ํ…Œ์ด๋ธ”)
           โ”œโ”€ 1:N โ”€ photo_lab_notice
           โ”œโ”€ 1:N โ”€ photo_lab_business_hour
           โ”œโ”€ 1:N โ”€ photo_lab_document (์ฆ๋น™์„œ๋ฅ˜)
           โ”œโ”€ 1:N โ”€ reservation_slot (์‹œ๊ฐ„๋Œ€๋ณ„ ์Šฌ๋กฏ)
           โ”œโ”€ 1:N โ”€ reservation
           โ”œโ”€ 1:N โ”€ development_order (ํ˜„์žฅ ์ฃผ๋ฌธ)
           โ””โ”€ 1:N โ”€ print_order (ํ˜„์žฅ ์ฃผ๋ฌธ)

Enum ์ •์˜

// ํšŒ์› (Joined Table ์ƒ์†)
MemberType:USER,OWNER,ADMIN           // role ์ปฌ๋Ÿผ (discriminator)
MemberStatus:ACTIVE,SUSPENDED,WITHDRAWN
SocialProvider:KAKAO,APPLE             // User ์ „์šฉ
DeviceType:IOS,ANDROID,WEB            // โ›” ๋ฏธ๊ตฌํ˜„

// ํ˜„์ƒ์†Œ
PhotoLabStatus:PENDING,ACTIVE,SUSPENDED,CLOSED
NoticeType:GENERAL,EVENT,POLICY        // photo_lab_notice.notice_type
DocumentType:BUSINESS_LICENSE,BUSINESS_PERMIT

// ์˜ˆ์•ฝ/์ฃผ๋ฌธ
ReservationStatus:RESERVED,COMPLETED,CANCELED   // โš ๏ธ CANCELED (D 1๊ฐœ)
DevelopmentOrderStatus:RECEIVED,DEVELOPING,SCANNING,COMPLETED
PrintOrderStatus:PENDING,CONFIRMED,PRINTING,READY,SHIPPED,COMPLETED
ReceiptMethod:PICKUP,DELIVERY
DeliveryStatus:PENDING,PREPARING,SHIPPED,IN_TRANSIT,DELIVERED

// ์ธํ™” ์˜ต์…˜ (photo.enums.print ํŒจํ‚ค์ง€)
FilmType:SLIDE,COLOR_NEG,BLACK_WHITE              // extra(basePrice) ๋ฉ”์„œ๋“œ ํฌํ•จ
PaperType:ECO_GLOSSY_260,ECO_LUSTER_255,EPSON_SEMIGLOSSY_250  // extraPrice ํฌํ•จ
PrintMethod:INKJET,CPRINT                          // extraPrice ํฌํ•จ
PrintSize:SIZE_5x7,SIZE_6x8,SIZE_8x10,SIZE_8x12,A4,SIZE_10x15,SIZE_11x14  // basePrice ํฌํ•จ
FrameType:WHITE_FRAME,NO_FRAME                     // extraPrice ํฌํ•จ

// ์ปค๋ฎค๋‹ˆํ‹ฐ
CommunityStatus:ACTIVE,HIDDEN,DELETED  // Post, Comment ๊ณต์šฉ (์ฝ”๋“œ: CommunityStatus)

// ๋ฌธ์˜
InquiryStatus:PENDING,ANSWERED,CLOSED

// ๊ณต์ง€/์•Œ๋ฆผ โ›” ๋ฏธ๊ตฌํ˜„
NotificationType:ORDER,RESERVATION,COMMUNITY,MARKETING,NOTICE
PromotionType:BANNER,POPUP,EVENT

// ์•ฝ๊ด€
TermsType:SERVICE,PRIVACY,LOCATION,SERVICE_INFO,MARKETING  // โš ๏ธ ์ฝ”๋“œ ๊ธฐ์ค€ (SERVICE_INFO ํฌํ•จ)

// AI ๋ณต์›
RestorationStatus:PENDING,PROCESSING,COMPLETED,FAILED
FeedbackRating:GOOD,BAD
RestorationTier:PREMIUM                // ๋‹ค์ค‘ ๋ชจ๋ธ ์ง€์› (creditCost, modelIdentifier ํฌํ•จ)

// ๊ฒฐ์ œ/ํฌ๋ ˆ๋”ง (v2.4.0 ํฌํŠธ์› V2 ์ „ํ™˜)
PaymentStatus:                         // ํฌํŠธ์› V2 ํ‘œ์ค€
  READY,                               // ๊ฒฐ์ œ ๋Œ€๊ธฐ
  PENDING,                             // ๊ฒฐ์ œ ์ง„ํ–‰ ์ค‘
  VIRTUAL_ACCOUNT_ISSUED,              // ๊ฐ€์ƒ๊ณ„์ขŒ ๋ฐœ๊ธ‰๋จ
  PAID,                                // ๊ฒฐ์ œ ์™„๋ฃŒ
  FAILED,                              // ๊ฒฐ์ œ ์‹คํŒจ
  PARTIAL_CANCELLED,                   // ๋ถ€๋ถ„ ์ทจ์†Œ
  CANCELLED                            // ์ „์•ก ์ทจ์†Œ

PaymentMethod:                         // ํฌํŠธ์› V2 ๊ฒฐ์ œ์ˆ˜๋‹จ
  CARD, TRANSFER, VIRTUAL_ACCOUNT, EASY_PAY

PgProvider:                            // PG์‚ฌ/๊ฐ„ํŽธ๊ฒฐ์ œ ์ œ๊ณต์ž (ํ˜„์žฌ: KCP + ๊ฐ„ํŽธ๊ฒฐ์ œ 3์ข…)
  KCP,                                 // ๋ฉ”์ธ PG์‚ฌ (์นด๋“œ, ๊ณ„์ขŒ์ด์ฒด, ๊ฐ€์ƒ๊ณ„์ขŒ)
  KAKAOPAY, NAVERPAY, TOSSPAY         // ๊ฐ„ํŽธ๊ฒฐ์ œ
  // ํ™•์žฅ ๊ฐ€๋Šฅ: TOSSPAYMENTS, INICIS, NICE ๋“ฑ

OrderType:CREDIT_PURCHASE,DEVELOPMENT_ORDER,PRINT_ORDER
CreditHistoryType:SIGNUP_BONUS,REFRESH,PURCHASE,USE,REFUND
CreditRelatedType:PHOTO_RESTORATION,PAYMENT  // credit_history.related_type

GCS (Cloud Storage) ๊ทœ์น™

์ด๋ฏธ์ง€/ํŒŒ์ผ ์ €์žฅ์†Œ ๊ทœ์น™. DB์—๋Š” object_path(๊ฒฝ๋กœ)๋งŒ ์ €์žฅํ•˜๊ณ , API ์‘๋‹ต ์‹œ URL ๋ณ€ํ™˜.

๋ฒ„ํ‚ท ๊ตฌ์„ฑ

๋ฒ„ํ‚ท ์šฉ๋„ ์ ‘๊ทผ ๋ฐฉ์‹
finders-487717-public ๊ณต๊ฐœ ์ด๋ฏธ์ง€ (ํ˜„์ƒ์†Œ, ๊ฒŒ์‹œ๊ธ€, ํ”„๋กœํ•„ ๋“ฑ) ์ง์ ‘ URL
finders-487717-private ๋น„๊ณต๊ฐœ ํŒŒ์ผ (์Šค์บ” ์‚ฌ์ง„, ์„œ๋ฅ˜, AI ๋ณต์›) Signed URL (1์‹œ๊ฐ„)

๊ฒฝ๋กœ ๊ทœ์น™

ํ…Œ์ด๋ธ” ์ปฌ๋Ÿผ ๋ฒ„ํ‚ท ๊ฒฝ๋กœ ํŒจํ„ด
member profile_image public profiles/{memberId}/{uuid}.{ext}
photo_lab_image object_path public photo-labs/{photoLabId}/images/{uuid}.{ext}
photo_lab qr_code_url public photo-labs/{photoLabId}/qr.png
photo_lab_document object_path private photo-labs/{photoLabId}/documents/{documentType}/{uuid}.{ext}
photo_lab_notice - - ์ด๋ฏธ์ง€ ์—†์Œ (ํ…์ŠคํŠธ๋งŒ)
scanned_photo object_path private temp/orders/{developmentOrderId}/scans/{uuid}.{ext}
post_image object_path public posts/{postId}/{uuid}.{ext}
inquiry_image object_path public inquiries/{inquiryId}/{uuid}.{ext}
photo_restoration original_path private restorations/{memberId}/original/{uuid}.{ext}
photo_restoration mask_path private restorations/{memberId}/mask/{uuid}.{ext}
photo_restoration restored_path private restorations/{memberId}/restored/{uuid}.{ext}
promotion object_path public promotions/{promotionId}/{uuid}.{ext}

์ž๋™ ์‚ญ์ œ (Lifecycle)

temp/ prefix๊ฐ€ ๋ถ™์€ ๋ชจ๋“  ํŒŒ์ผ์€ 30์ผ ํ›„ ์ž๋™ ์‚ญ์ œ (GCS Lifecycle ์ •์ฑ…)

์šฉ๋„ ๋ฒ„ํ‚ท ๊ฒฝ๋กœ ํŒจํ„ด
์ž„์‹œ ์—…๋กœ๋“œ public temp/{memberId}/{uuid}.{ext}
์Šค์บ” ์‚ฌ์ง„ private temp/orders/{developmentOrderId}/scans/{uuid}.{ext}
  • ์ž„์‹œ ์—…๋กœ๋“œ: ์—”ํ‹ฐํ‹ฐ ์—ฐ๊ฒฐ ์‹œ ์˜๊ตฌ ๊ฒฝ๋กœ๋กœ ์ด๋™
  • ์Šค์บ” ์‚ฌ์ง„: ๊ณ ๊ฐ์ด 30์ผ ๋‚ด ๋‹ค์šด๋กœ๋“œ ํ•„์š”

API ์‘๋‹ต ์ฒ˜๋ฆฌ

// public ๋ฒ„ํ‚ท: ์ง์ ‘ URL ๋ฐ˜ํ™˜
"https://storage.googleapis.com/finders-487717-public/profiles/123/abc.jpg"

// private ๋ฒ„ํ‚ท: Signed URL ๋ฐ˜ํ™˜ (1์‹œ๊ฐ„ ์œ ํšจ)
        "https://storage.googleapis.com/finders-487717-private/temp/orders/456/scans/def.jpg?X-Goog-Signature=..."

MySQL DDL

-- ============================================
-- Finders Database Schema (MySQL 8.0+)
-- ============================================

CREATE
DATABASE IF NOT EXISTS finders
    DEFAULT CHARACTER SET utf8mb4
    COLLATE utf8mb4_unicode_ci;

USE
finders;

-- ============================================
-- 1. MEMBER
-- ============================================

CREATE TABLE member
(                                            -- Base ํ…Œ์ด๋ธ” (๊ณตํ†ต ํ•„๋“œ๋งŒ)
    id                 BIGINT      NOT NULL AUTO_INCREMENT,
    role               VARCHAR(20) NOT NULL, -- Discriminator: USER, OWNER, ADMIN (JPA๊ฐ€ ์ž๋™ ๊ด€๋ฆฌ)
    name               VARCHAR(20) NOT NULL, -- ์‹ค๋ช…
    email              VARCHAR(100) NULL,    -- ์นด์นด์˜ค ๋น„์ฆˆ์•ฑ ํ†ตํ•ด์„œ ๊ฐ€์ ธ์˜ด
    phone              VARCHAR(20) NULL,     -- ์นด์นด์˜ค ๋น„์ฆˆ์•ฑ ํ†ตํ•ด์„œ ๊ฐ€์ ธ์˜ด
    status             VARCHAR(20) NOT NULL DEFAULT 'ACTIVE',
    refresh_token_hash VARCHAR(500) NULL,    -- JWT Refresh Token Hash (๋ชจ๋“  ์—ญํ•  ๊ณตํ†ต)
    created_at         DATETIME    NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at         DATETIME    NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    deleted_at         DATETIME NULL,
    PRIMARY KEY (id),
    INDEX              idx_member_role (role),
    CONSTRAINT chk_member_role CHECK (role IN ('USER', 'OWNER', 'ADMIN')),
    CONSTRAINT chk_member_status CHECK (status IN ('ACTIVE', 'SUSPENDED', 'WITHDRAWN'))
) ENGINE=InnoDB COMMENT='ํšŒ์› Base (Joined Table ์ƒ์†)';

CREATE TABLE member_user
(                                                             -- User ์ „์šฉ ํ…Œ์ด๋ธ” (์†Œ์…œ ๋กœ๊ทธ์ธ ์‚ฌ์šฉ์ž)
    member_id             BIGINT      NOT NULL,               -- PK & FK โ†’ member.id
    nickname              VARCHAR(20) NOT NULL,               -- ์นด์นด์˜ค ๋น„์ฆˆ์•ฑ ํ†ตํ•ด์„œ ๊ฐ€์ ธ์˜ค๊ฑฐ๋‚˜ ์ž…๋ ฅ (๋‹‰๋„ค์ž„)
    profile_image         VARCHAR(500) NULL,                  -- ์นด์นด์˜ค ๋น„์ฆˆ์•ฑ ํ†ตํ•ด์„œ ๊ฐ€์ ธ์˜ด
    credit_balance         INT UNSIGNED    NOT NULL DEFAULT 3, -- ๋ณด์œ  ํฌ๋ ˆ๋”ง (ํšŒ์›๊ฐ€์ž… ์‹œ 3๊ฐœ ์ง€๊ธ‰)
    last_credit_refresh_at DATETIME NULL,                      -- ๋งˆ์ง€๋ง‰ ๋ฌด๋ฃŒ ํฌ๋ ˆ๋”ง ๋ฆฌํ”„๋ ˆ์‹œ ์ผ์‹œ
    PRIMARY KEY (member_id),
    UNIQUE KEY uk_member_user_nickname (nickname),
    CONSTRAINT fk_member_user FOREIGN KEY (member_id) REFERENCES member (id) ON DELETE CASCADE
) ENGINE=InnoDB COMMENT='User ์ „์šฉ (์†Œ์…œ ๋กœ๊ทธ์ธ, ํฌ๋ ˆ๋”ง)';

CREATE TABLE member_owner
(                                              -- Owner ์ „์šฉ ํ…Œ์ด๋ธ” (ํ˜„์ƒ์†Œ ์‚ฌ์žฅ๋‹˜)
    member_id           BIGINT       NOT NULL, -- PK & FK โ†’ member.id
    password_hash       VARCHAR(255) NOT NULL, -- BCrypt ํ•ด์‹œ (์ด๋ฉ”์ผ/๋น„๋ฐ€๋ฒˆํ˜ธ ๋กœ๊ทธ์ธ)
    business_number     VARCHAR(20) NULL,      -- ์‚ฌ์—…์ž ๋ฒˆํ˜ธ
    bank_name           VARCHAR(50) NULL,      -- ์€ํ–‰๋ช…
    bank_account_number VARCHAR(50) NULL,      -- ๊ณ„์ขŒ๋ฒˆํ˜ธ
    bank_account_holder VARCHAR(50) NULL,      -- ์˜ˆ๊ธˆ์ฃผ
    PRIMARY KEY (member_id),
    CONSTRAINT fk_member_owner FOREIGN KEY (member_id) REFERENCES member (id) ON DELETE CASCADE
) ENGINE=InnoDB COMMENT='Owner ์ „์šฉ (์‚ฌ์—…์ž ์ •๋ณด, ์ •์‚ฐ ๊ณ„์ขŒ)';

CREATE TABLE member_admin
(                                        -- Admin ์ „์šฉ ํ…Œ์ด๋ธ” (๊ด€๋ฆฌ์ž)
    member_id     BIGINT       NOT NULL, -- PK & FK โ†’ member.id
    password_hash VARCHAR(255) NOT NULL, -- BCrypt ํ•ด์‹œ (์ด๋ฉ”์ผ/๋น„๋ฐ€๋ฒˆํ˜ธ ๋กœ๊ทธ์ธ)
    PRIMARY KEY (member_id),
    CONSTRAINT fk_member_admin FOREIGN KEY (member_id) REFERENCES member (id) ON DELETE CASCADE
) ENGINE=InnoDB COMMENT='Admin ์ „์šฉ';

CREATE TABLE social_account
(                                       -- User(role='USER') ์ „์šฉ
    id           BIGINT       NOT NULL AUTO_INCREMENT,
    member_id    BIGINT       NOT NULL, -- FK (User๋งŒ ์—ฐ๊ฒฐ, Owner/Admin์€ ์†Œ์…œ ๋กœ๊ทธ์ธ ๋ฏธ์‚ฌ์šฉ)
    provider     VARCHAR(20)  NOT NULL, -- KAKAO, APPLE
    provider_id  VARCHAR(100) NOT NULL,
    social_email VARCHAR(100) NULL,
    created_at   DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at   DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (id),
    UNIQUE KEY uk_social_provider (provider, provider_id),
    INDEX        idx_social_member (member_id),
    CONSTRAINT fk_social_member FOREIGN KEY (member_id) REFERENCES member (id),
    CONSTRAINT chk_social_provider CHECK (provider IN ('KAKAO', 'APPLE'))
) ENGINE=InnoDB COMMENT='์†Œ์…œ ๊ณ„์ • (User ์ „์šฉ)';

CREATE TABLE member_address
(
    id             BIGINT       NOT NULL AUTO_INCREMENT,
    member_id      BIGINT       NOT NULL,               -- FK โ†’ member_user
    address_name   VARCHAR(50)  NOT NULL,               -- ๋ฐฐ์†ก์ง€ ์ด๋ฆ„
    zipcode        VARCHAR(10)  NOT NULL,               -- ์šฐํŽธ๋ฒˆํ˜ธ
    address        VARCHAR(200) NOT NULL,               -- 'ex) ์„œ์šธ ์„œ์ดˆ๊ตฌ ๊ณ ๋ฌด๋ž˜๋กœ 89
    address_detail VARCHAR(100) NULL,                   -- 'ex) 3003๋™ 303ํ˜ธ
    is_default     BOOLEAN      NOT NULL DEFAULT FALSE, -- ๊ธฐ๋ณธ ๋ฐฐ์†ก์ง€ ์—ฌ๋ถ€
    created_at     DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at     DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    deleted_at     DATETIME NULL,
    PRIMARY KEY (id),
    INDEX          idx_address_member (member_id, is_default),
    CONSTRAINT fk_address_member FOREIGN KEY (member_id) REFERENCES member (id)
) ENGINE=InnoDB COMMENT='๋ฐฐ์†ก์ง€';

CREATE TABLE terms
(                                                      -- ์•ฝ๊ด€ ๋ฒ„์ „ ๊ด€๋ฆฌ
    id             BIGINT       NOT NULL AUTO_INCREMENT,
    type           VARCHAR(20)  NOT NULL,              -- TERMS, PRIVACY, MARKETING, LOCATION
    version        VARCHAR(20)  NOT NULL,              -- ๋ฒ„์ „ (์˜ˆ: 1.0, 1.1, 2.0)
    title          VARCHAR(200) NOT NULL,              -- ์•ฝ๊ด€ ์ œ๋ชฉ
    content        TEXT         NOT NULL,              -- ์•ฝ๊ด€ ๋‚ด์šฉ
    is_required    BOOLEAN      NOT NULL DEFAULT TRUE, -- ํ•„์ˆ˜ ๋™์˜ ์—ฌ๋ถ€
    is_active      BOOLEAN      NOT NULL DEFAULT TRUE, -- ํ˜„์žฌ ํ™œ์„ฑ ๋ฒ„์ „ ์—ฌ๋ถ€
    effective_date DATE         NOT NULL,              -- ์‹œํ–‰์ผ
    created_at     DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at     DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (id),
    UNIQUE KEY uk_terms_version (type, version),
    INDEX          idx_terms_active (type, is_active),
    CONSTRAINT chk_terms_type CHECK (type IN ('SERVICE', 'PRIVACY', 'LOCATION', 'SERVICE_INFO', 'MARKETING'))
) ENGINE=InnoDB COMMENT='์•ฝ๊ด€ ๋ฒ„์ „';

CREATE TABLE member_agreement
(                                 -- ํšŒ์›๋ณ„ ์•ฝ๊ด€ ๋™์˜ ์ด๋ ฅ
    id         BIGINT   NOT NULL AUTO_INCREMENT,
    member_id  BIGINT   NOT NULL, -- FK
    terms_id   BIGINT   NOT NULL, -- FK (์•ฝ๊ด€ ๋ฒ„์ „ ์ฐธ์กฐ)
    is_agreed  BOOLEAN  NOT NULL,
    agreed_at  DATETIME NOT NULL,
    created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (id),
    INDEX      idx_agreement_member (member_id),
    INDEX      idx_agreement_terms (terms_id),
    CONSTRAINT fk_agreement_member FOREIGN KEY (member_id) REFERENCES member (id),
    CONSTRAINT fk_agreement_terms FOREIGN KEY (terms_id) REFERENCES terms (id)
) ENGINE=InnoDB COMMENT='์•ฝ๊ด€ ๋™์˜ ์ด๋ ฅ';

CREATE TABLE terms_social_mapping
(                                        -- ์•ฝ๊ด€๊ณผ ์†Œ์…œ ๋กœ๊ทธ์ธ ํƒœ๊ทธ ๋งคํ•‘ (์นด์นด์˜ค ๋™์˜ ํ•ญ๋ชฉ ์—ฐ๋™)
    id         BIGINT       NOT NULL AUTO_INCREMENT,
    terms_id   BIGINT       NOT NULL,    -- FK โ†’ terms
    provider   VARCHAR(20)  NOT NULL,    -- KAKAO, APPLE (SocialProvider)
    social_tag VARCHAR(100) NOT NULL,    -- ์†Œ์…œ ์ธก ํƒœ๊ทธ (์˜ˆ: "service_policy", "privacy_policy")
    created_at DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (id),
    CONSTRAINT fk_tsm_terms FOREIGN KEY (terms_id) REFERENCES terms (id)
) ENGINE=InnoDB COMMENT='์•ฝ๊ด€-์†Œ์…œ ํƒœ๊ทธ ๋งคํ•‘';

CREATE TABLE member_device
(                                       -- FCM(Firebase) ๋””๋ฐ”์ด์Šค ํ† ํฐ -- ๋ฐ๋ชจ๋ฐ์ด ์ „๊นŒ์ง€ ์ ˆ๋Œ€ ๊ฐœ๋ฐœ ๊ธˆ์ง€
    id           BIGINT       NOT NULL AUTO_INCREMENT,
    member_id    BIGINT       NOT NULL, -- FK
    device_token VARCHAR(500) NOT NULL, -- FCM ํ† ํฐ
    device_type  VARCHAR(20)  NOT NULL, -- IOS, ANDROID, WEB
    device_name  VARCHAR(100) NULL,     -- ๋””๋ฐ”์ด์Šค ์ด๋ฆ„ (์˜ˆ: iPhone 15, Galaxy S24)
    is_active    BOOLEAN      NOT NULL DEFAULT TRUE,
    last_used_at DATETIME NULL,         -- ๋งˆ์ง€๋ง‰ ์‚ฌ์šฉ ์‹œ๊ฐ
    created_at   DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at   DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (id),
    UNIQUE KEY uk_device_token (device_token),
    INDEX        idx_device_member (member_id, is_active),
    CONSTRAINT fk_device_member FOREIGN KEY (member_id) REFERENCES member (id),
    CONSTRAINT chk_device_type CHECK (device_type IN ('IOS', 'ANDROID', 'WEB'))
) ENGINE=InnoDB COMMENT='FCM ๋””๋ฐ”์ด์Šค ํ† ํฐ';

CREATE TABLE credit_history
(                                           -- AI ํฌ๋ ˆ๋”ง ์ถฉ์ „/์‚ฌ์šฉ ๋‚ด์—ญ
    id            BIGINT      NOT NULL AUTO_INCREMENT,
    member_id     BIGINT      NOT NULL,
    type          VARCHAR(20) NOT NULL,     -- SIGNUP_BONUS, REFRESH, PURCHASE, USE, REFUND
    amount        INT         NOT NULL,     -- ๋ณ€๋™๋Ÿ‰ (+3, -1 ๋“ฑ)
    balance_after INT UNSIGNED    NOT NULL, -- ๋ณ€๋™ ํ›„ ์ž”์•ก
    related_type  VARCHAR(50) NULL,         -- PHOTO_RESTORATION, PAYMENT
    related_id    BIGINT NULL,              -- ๊ด€๋ จ ์—”ํ‹ฐํ‹ฐ ID
    description   VARCHAR(200) NULL,        -- ์„ค๋ช… (์˜ˆ: "ํšŒ์›๊ฐ€์ž… ๋ณด๋„ˆ์Šค", "AI ๋ณต์› ์‚ฌ์šฉ")
    created_at    DATETIME    NOT NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (id),
    INDEX         idx_credit_history_member (member_id, created_at DESC),
    INDEX         idx_credit_history_type (type),
    CONSTRAINT fk_credit_history_member FOREIGN KEY (member_id) REFERENCES member (id),
    CONSTRAINT chk_credit_type CHECK (type IN ('SIGNUP_BONUS', 'REFRESH', 'PURCHASE', 'USE', 'REFUND'))
) ENGINE=InnoDB COMMENT='ํฌ๋ ˆ๋”ง ๋‚ด์—ญ';

CREATE TABLE favorite_photo_lab
( -- ๋ณ„ ํ‘œ์‹œ
    id           BIGINT   NOT NULL AUTO_INCREMENT,
    member_id    BIGINT   NOT NULL,
    photo_lab_id BIGINT   NOT NULL,
    created_at   DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (id),
    UNIQUE KEY uk_favorite_lab (member_id, photo_lab_id),
    CONSTRAINT fk_fav_lab_member FOREIGN KEY (member_id) REFERENCES member (id),
    CONSTRAINT fk_fav_lab_lab FOREIGN KEY (photo_lab_id) REFERENCES photo_lab (id)
) ENGINE=InnoDB COMMENT='๊ด€์‹ฌ ํ˜„์ƒ์†Œ';

-- ============================================
-- 2. STORE (ํ˜„์ƒ์†Œ)
-- ============================================

CREATE TABLE photo_lab
(
    id                        BIGINT       NOT NULL AUTO_INCREMENT,
    owner_id                  BIGINT       NOT NULL,               -- FK โ†’ member_owner.member_id (Owner ์ „์šฉ)
    region_id                 BIGINT       NOT NULL,               -- FK region.id (์‹œ/๊ตฐ/๊ตฌ)
    name                      VARCHAR(100) NOT NULL,
    description               TEXT NULL,
    phone                     VARCHAR(20) NULL,
    zipcode                   VARCHAR(10) NULL,                    -- ์šฐํŽธ๋ฒˆํ˜ธ
    address                   VARCHAR(200) NOT NULL,               -- 'ex) ์„œ์šธ ์„œ์ดˆ๊ตฌ ๊ณ ๋ฌด๋ž˜๋กœ 89
    address_detail            VARCHAR(100) NULL,                   -- 'ex) 3003๋™ 303ํ˜ธ
    latitude                  DECIMAL(10, 8) NULL,
    longitude                 DECIMAL(11, 8) NULL,
    work_count                INT UNSIGNED    NOT NULL DEFAULT 0,  -- ์ž‘์—… ์ด ํšŸ์ˆ˜
    post_count                INT UNSIGNED    NOT NULL DEFAULT 0,  -- ์ž‘์—… ๊ฒฐ๊ณผ๋ฌผ์— ํ•„์š”ํ•จ
    reservation_count         INT UNSIGNED  NOT NULL DEFAULT 0,    -- ์˜ˆ์•ฝ ์™„๋ฃŒ ์‹œ์ ์— ์—…๋ฐ์ดํŠธ
    review_count              INT UNSIGNED  NOT NULL DEFAULT 0,
    avg_work_time             INT UNSIGNED    NULL,                -- ์ฃผ๋ฌธ ์™„๋ฃŒ ์‹œ์ ์—(development_order.status = 'COMPLETED') ๋ฐฑ์—”๋“œ์—์„œ ์ง์ ‘ ๊ณ„์‚ฐํ•ด์„œ ์ €์žฅ
    status                    VARCHAR(20)  NOT NULL DEFAULT 'PENDING',
    is_delivery_available     BOOLEAN      NOT NULL DEFAULT FALSE, -- TRUE: ๋ฐฐ์†ก๊ฐ€๋Šฅ, FALSE: ๋ฐฐ์†ก ๋ถˆ๊ฐ€๋Šฅ
    max_reservations_per_hour INT UNSIGNED NOT NULL DEFAULT 3,     -- ์‹œ๊ฐ„๋‹น ์ตœ๋Œ€ ์˜ˆ์•ฝ ๊ฐ€๋Šฅ ์ˆ˜ (Owner ์กฐ์ • ๊ฐ€๋Šฅ)
    load_base_roll            INT UNSIGNED NOT NULL DEFAULT 1,     -- ๋ถ€ํ•˜ ๊ณ„์‚ฐ ๊ธฐ์ค€ ๋กค ์ˆ˜
    load_add_minutes          INT UNSIGNED NOT NULL DEFAULT 120,   -- ์ถ”๊ฐ€ ๋กค๋‹น ๋ถ„ (์˜ˆ์•ฝ ๋Œ€๊ธฐ ์‹œ๊ฐ„ ๊ณ„์‚ฐ)
    qr_code_url               VARCHAR(500) NULL,                   -- QR ์ฝ”๋“œ ์ด๋ฏธ์ง€ URL (ํ˜„์žฅ ์ฃผ๋ฌธ์šฉ)
    created_at                DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at                DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    deleted_at                DATETIME NULL,
    PRIMARY KEY (id),
    INDEX                     idx_lab_status (status),
    INDEX                     idx_lab_location (latitude, longitude),
    FULLTEXT                  INDEX ft_lab_name (name),
    CONSTRAINT fk_lab_owner FOREIGN KEY (owner_id) REFERENCES member (id),
    CONSTRAINT fk_lab_region FOREIGN KEY (region_id) REFERENCES region (id),
    CONSTRAINT chk_lab_status CHECK (status IN ('PENDING', 'ACTIVE', 'SUSPENDED', 'CLOSED'))
) ENGINE=InnoDB COMMENT='ํ˜„์ƒ์†Œ';

CREATE TABLE photo_lab_image
(                                                      -- ํ˜„์ƒ์†Œ๋งˆ๋‹ค ์ด๋ฏธ์ง€ ๊ฐœ์ˆ˜๊ฐ€ ์—ฌ๋Ÿฌ ๊ฐœ์ด๋ฏ€๋กœ ๋”ฐ๋กœ ๋ถ„๋ฆฌ
    id            BIGINT       NOT NULL AUTO_INCREMENT,
    photo_lab_id  BIGINT       NOT NULL,               -- FK
    object_path   VARCHAR(500) NOT NULL,               -- GCS object path (์˜ˆ: photo-labs/123/images/abc.jpg)
    display_order INT          NOT NULL DEFAULT 0,
    is_main       BOOLEAN      NOT NULL DEFAULT FALSE, -- ๋Œ€ํ‘œ ์ด๋ฏธ์ง€ ์—ฌ๋ถ€
    created_at    DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (id),
    INDEX         idx_lab_image (photo_lab_id, is_main),
    CONSTRAINT fk_lab_image FOREIGN KEY (photo_lab_id) REFERENCES photo_lab (id) ON DELETE CASCADE
) ENGINE=InnoDB COMMENT='ํ˜„์ƒ์†Œ ์ด๋ฏธ์ง€';

CREATE TABLE photo_lab_tag
(                                   -- join table
    id           BIGINT   NOT NULL AUTO_INCREMENT,
    photo_lab_id BIGINT   NOT NULL, -- FK
    tag_id       BIGINT   NOT NULL, -- FK
    created_at   DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at   DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (id),
    UNIQUE KEY uk_lab_tag (photo_lab_id, tag_id),
    INDEX        idx_photo_lab_tag_photo_lab_id (photo_lab_id),
    INDEX        idx_photo_lab_tag_tag_id (tag_id),
    CONSTRAINT fk_photo_lab_tag_photo_lab_id
        FOREIGN KEY (photo_lab_id) REFERENCES photo_lab (id) ON DELETE CASCADE,
    CONSTRAINT fk_photo_lab_tag_tag_id
        FOREIGN KEY (tag_id) REFERENCES tag (id) ON DELETE CASCADE
) ENGINE=InnoDB COMMENT='ํ˜„์ƒ์†Œ-ํƒœ๊ทธ ์กฐ์ธ ํ…Œ์ด๋ธ”';

CREATE TABLE tag
(
    id         BIGINT      NOT NULL AUTO_INCREMENT,
    name       VARCHAR(50) NOT NULL, -- ํƒœ๊ทธ ์ด๋ฆ„
    created_at DATETIME    NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at DATETIME    NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (id),
    UNIQUE KEY uk_tag_name (name)
) ENGINE=InnoDB COMMENT='ํ˜„์ƒ์†Œ ํƒœ๊ทธ';

CREATE TABLE photo_lab_notice
(
    id           BIGINT       NOT NULL AUTO_INCREMENT,
    photo_lab_id BIGINT       NOT NULL,                   -- FK
    title        VARCHAR(200) NOT NULL,
    content      TEXT         NOT NULL,
    notice_type  VARCHAR(20)  NOT NULL DEFAULT 'GENERAL', -- ENUM ์ฒ˜๋ฆฌ GENERAL: ์ผ๋ฐ˜๊ณต์ง€, EVENT: ์ด๋ฒคํŠธํ–‰์‚ฌ๊ณต์ง€
    start_date   DATE NULL,                               -- yyyy-MM-dd, 2025-12-23
    end_date     DATE NULL,
    is_active    BOOLEAN      NOT NULL DEFAULT TRUE,
    created_at   DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at   DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (id),
    INDEX        idx_lab_notice (photo_lab_id),
    CONSTRAINT fk_lab_notice FOREIGN KEY (photo_lab_id) REFERENCES photo_lab (id) ON DELETE CASCADE,
    CONSTRAINT chk_lab_notice_type CHECK (notice_type IN ('GENERAL', 'EVENT', 'POLICY'))
) ENGINE=InnoDB COMMENT='ํ˜„์ƒ์†Œ ๊ณต์ง€';

CREATE TABLE photo_lab_business_hour
(                                                    -- ํ˜„์ƒ์†Œ์˜ ๊ฐ ์š”์ผ๋งˆ๋‹ค์˜ ์ผ์ • ๋“ฑ๋ก 1:N ๊ด€๊ณ„
    id           BIGINT      NOT NULL AUTO_INCREMENT,
    photo_lab_id BIGINT      NOT NULL,
    day_of_week  VARCHAR(10) NOT NULL,               -- MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY (java.time.DayOfWeek) 
    open_time    TIME NULL,                          -- HH:mm:ss (์˜ˆ: 09:00:00) 
    close_time   TIME NULL,
    is_closed    BOOLEAN     NOT NULL DEFAULT FALSE, -- FALSE: ์˜์—…์ผ, TRUE: ํœด๋ฌด์ผ
    created_at   DATETIME    NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at   DATETIME    NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (id),
    UNIQUE KEY uk_lab_hour (photo_lab_id, day_of_week),
    CONSTRAINT fk_lab_hour FOREIGN KEY (photo_lab_id) REFERENCES photo_lab (id) ON DELETE CASCADE,
    CONSTRAINT chk_day_of_week CHECK (day_of_week IN
                                      ('SUNDAY', 'MONDAY', 'TUESDAY', 'WEDNESDAY', 'THURSDAY', 'FRIDAY', 'SATURDAY'))
) ENGINE=InnoDB COMMENT='์˜์—…์‹œ๊ฐ„';

CREATE TABLE photo_lab_document
(                                        -- GCP Cloud Storage์— bucket ๋งŒ๋“ค์–ด์„œ ์ €์žฅํ•ด์•ผ ํ•  ๋“ฏ
    id            BIGINT       NOT NULL AUTO_INCREMENT,
    photo_lab_id  BIGINT       NOT NULL,
    document_type VARCHAR(30)  NOT NULL, -- BUSINESS_LICENSE, BUSINESS_PERMIT
    object_path   VARCHAR(500) NOT NULL, -- GCS object path (์˜ˆ: photo-labs/123/documents/BUSINESS_LICENSE/abc.pdf)
    -- ํŒŒ์ผ์˜ ๋ฒ„์ „๊ด€๋ฆฌ๊ฐ€ ํ•„์š”ํ•˜๋‹ค. ๊ด€๋ฆฌ์ž ์ž…์žฅ์—์„œ ์ƒ๊ฐํ•ด๋ณด๋ฉด, approved, rejeceted, pending
    file_name     VARCHAR(200) NULL,
    verified_at   DATETIME NULL,         -- ๊ฒ€์ฆ ์™„๋ฃŒ ์ผ์‹œ
    created_at    DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (id),
    INDEX         idx_lab_doc (photo_lab_id, document_type),
    CONSTRAINT fk_lab_doc FOREIGN KEY (photo_lab_id) REFERENCES photo_lab (id) ON DELETE CASCADE,
    CONSTRAINT chk_doc_type CHECK (document_type IN ('BUSINESS_LICENSE', 'BUSINESS_PERMIT'))
) ENGINE=InnoDB COMMENT='์‚ฌ์—…์ž ์ฆ๋น™ ์„œ๋ฅ˜';

CREATE TABLE region
(
    id                BIGINT       NOT NULL AUTO_INCREMENT,
    region_name       VARCHAR(50)  NOT NULL COMMENT '์ง€์—ญ๋ช…',
    parent_region_id  BIGINT       NULL COMMENT '์ƒ์œ„ ์ง€์—ญ ID (์ตœ์ƒ์œ„ ์ง€์—ญ์€ NULL)',
    created_at        DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at        DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    deleted_at        DATETIME     NULL,
    PRIMARY KEY (id),
    INDEX idx_region_parent (parent_region_id),
    UNIQUE KEY uk_region_name_parent (region_name, parent_region_id),
    CONSTRAINT fk_region_parent_region
        FOREIGN KEY (parent_region_id) REFERENCES region (id)
) ENGINE=InnoDB COMMENT='์ง€์—ญ (๊ณ„์ธต ๊ตฌ์กฐ: ์ƒ์œ„/ํ•˜์œ„)';

-- ============================================
-- 3. RESERVATION
-- ============================================

CREATE TABLE reservation
(
    id               BIGINT      NOT NULL AUTO_INCREMENT,
    member_id        BIGINT      NOT NULL,               -- FK โ†’ member_user
    slot_id          BIGINT      NOT NULL,               -- FK โ†’ reservation_slot (๋‚ ์งœ/์‹œ๊ฐ„์€ ์Šฌ๋กฏ์„ ํ†ตํ•ด ์ฐธ์กฐ)
    photo_lab_id     BIGINT      NOT NULL,               -- FK โ†’ photo_lab
    status           VARCHAR(20) NOT NULL DEFAULT 'RESERVED',
    -- ์ž‘์—… ๋‚ด์šฉ ์„ ํƒ (๋‹ค์ค‘ ์„ ํƒ ๊ฐ€๋Šฅ)
    is_develop       BOOLEAN     NOT NULL DEFAULT FALSE, -- ํ˜„์ƒ
    is_scan          BOOLEAN     NOT NULL DEFAULT FALSE, -- ์Šค์บ”
    is_print         BOOLEAN     NOT NULL DEFAULT FALSE, -- ์ธํ™”
    roll_count       INT UNSIGNED    NOT NULL DEFAULT 1,
    request_message  VARCHAR(500) NULL,
    created_at       DATETIME    NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at       DATETIME    NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    deleted_at       DATETIME NULL,
    PRIMARY KEY (id),
    INDEX            idx_reservation_lab_status (photo_lab_id, status),
    CONSTRAINT fk_reservation_member FOREIGN KEY (member_id) REFERENCES member (id),
    CONSTRAINT fk_reservation_slot FOREIGN KEY (slot_id) REFERENCES reservation_slot (id),
    CONSTRAINT fk_reservation_lab FOREIGN KEY (photo_lab_id) REFERENCES photo_lab (id),
    CONSTRAINT chk_reservation_status CHECK (status IN ('RESERVED', 'COMPLETED', 'CANCELED'))
) ENGINE=InnoDB COMMENT='์˜ˆ์•ฝ';

CREATE TABLE reservation_slot
(
    id               BIGINT   NOT NULL AUTO_INCREMENT,
    photo_lab_id     BIGINT   NOT NULL,
    reservation_date DATE     NOT NULL,
    reservation_time TIME     NOT NULL,
    max_capacity     INT      NOT NULL,
    reserved_count   INT      NOT NULL DEFAULT 0,
    created_at       DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at       DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    deleted_at       DATETIME NULL,
    PRIMARY KEY (id),

    UNIQUE KEY uk_slot_lab_date_time (photo_lab_id, reservation_date, reservation_time),
    INDEX            idx_slot_lab_date (photo_lab_id, reservation_date),

    CONSTRAINT fk_slot_lab
        FOREIGN KEY (photo_lab_id) REFERENCES photo_lab (id) ON DELETE CASCADE
) ENGINE=InnoDB COMMENT='์˜ˆ์•ฝ ์Šฌ๋กฏ(์ •์› ๊ด€๋ฆฌ)';



-- ============================================
-- 4. PHOTO (ํ˜„์ƒ/์Šค์บ”/์ธํ™”)
-- ============================================

CREATE TABLE development_order
(                                                      -- ํ˜„์ƒ/์Šค์บ” ์„ธํŠธ๋กœ
    id             BIGINT      NOT NULL AUTO_INCREMENT,
    reservation_id BIGINT NULL,                        -- ์˜ˆ์•ฝ ์—†์ด ํ˜„์žฅ ์ฃผ๋ฌธ ๊ฐ€๋Šฅ (QR ์Šค์บ”)
    photo_lab_id   BIGINT      NOT NULL,               -- ํ˜„์ƒ์†Œ (์˜ˆ์•ฝ ์—†์„ ๋•Œ ํ•„์ˆ˜)
    member_id      BIGINT      NOT NULL,
    order_code     VARCHAR(20) NOT NULL,
    status         VARCHAR(20) NOT NULL DEFAULT 'RECEIVED',
    total_photos   INT UNSIGNED    NOT NULL DEFAULT 0, -- ์Šค์บ” ์ง„ํ–‰๋ฅ ์€ scanned_photo COUNT๋กœ ๊ณ„์‚ฐ
    total_price    INT UNSIGNED    NOT NULL DEFAULT 0, -- ์ฃผ๋ฌธ ์‹œ์  ๊ฐ€๊ฒฉ ์Šค๋ƒ…์ƒท (ํ˜„์ƒ+์Šค์บ” ํŒจํ‚ค์ง€)
    completed_at   DATETIME NULL,
    created_at     DATETIME    NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at     DATETIME    NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (id),
    UNIQUE KEY uk_dev_order_code (order_code),
    INDEX          idx_dev_order_member (member_id, status),
    INDEX          idx_dev_order_lab (photo_lab_id, status),
    CONSTRAINT fk_dev_order_reservation FOREIGN KEY (reservation_id) REFERENCES reservation (id),
    CONSTRAINT fk_dev_order_lab FOREIGN KEY (photo_lab_id) REFERENCES photo_lab (id),
    CONSTRAINT fk_dev_order_member FOREIGN KEY (member_id) REFERENCES member (id),
    is_develop     BOOLEAN     NOT NULL DEFAULT FALSE,
    is_scan        BOOLEAN     NOT NULL DEFAULT FALSE,
    is_print       BOOLEAN     NOT NULL DEFAULT FALSE,
    roll_count     INT UNSIGNED NOT NULL DEFAULT 1,

    CONSTRAINT chk_dev_status CHECK (status IN ('RECEIVED', 'DEVELOPING', 'SCANNING', 'COMPLETED'))
) ENGINE=InnoDB COMMENT='ํ˜„์ƒ ์ฃผ๋ฌธ';

CREATE TABLE scanned_photo
(                                        -- 1:N ๊ด€๊ณ„
    id            BIGINT       NOT NULL AUTO_INCREMENT,
    order_id      BIGINT       NOT NULL, -- FK development_order
    object_path   VARCHAR(500) NOT NULL, -- GCS object path (์˜ˆ: temp/orders/123/scans/abc.jpg)
    file_name     VARCHAR(200) NULL,
    display_order INT          NOT NULL DEFAULT 0,
    created_at    DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (id),
    INDEX         idx_scanned_order (order_id),
    CONSTRAINT fk_scanned_order FOREIGN KEY (order_id) REFERENCES development_order (id)
) ENGINE=InnoDB COMMENT='์Šค์บ” ์‚ฌ์ง„';

CREATE TABLE print_order
(
    id             BIGINT      NOT NULL AUTO_INCREMENT,
    dev_order_id   BIGINT NULL,          -- ํ˜„์ƒ ์—†์ด ์ธํ™”๋งŒ ์š”์ฒญ ๊ฐ€๋Šฅ
    photo_lab_id   BIGINT      NOT NULL, -- ํ˜„์ƒ์†Œ
    member_id      BIGINT      NOT NULL,
    order_code     VARCHAR(20) NOT NULL,
    status         VARCHAR(20) NOT NULL DEFAULT 'PENDING',
    total_price    INT UNSIGNED    NOT NULL DEFAULT 0,
    receipt_method VARCHAR(20) NOT NULL DEFAULT 'PICKUP',
    estimated_at   DATETIME NULL,
    completed_at   DATETIME NULL,
    deposit_receipt_object_path VARCHAR(500) NULL,
    depositor_name              VARCHAR(50)  NULL,
    deposit_bank_name           VARCHAR(50)  NULL,
    payment_submitted_at        DATETIME     NULL,

    created_at     DATETIME    NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at     DATETIME    NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (id),
    UNIQUE KEY uk_print_order_code (order_code),
    INDEX          idx_print_order_member (member_id, status),
    INDEX          idx_print_order_lab (photo_lab_id, status),
    CONSTRAINT fk_print_order_dev FOREIGN KEY (dev_order_id) REFERENCES development_order (id),
    CONSTRAINT fk_print_order_lab FOREIGN KEY (photo_lab_id) REFERENCES photo_lab (id),
    CONSTRAINT fk_print_order_member FOREIGN KEY (member_id) REFERENCES member (id),
    CONSTRAINT chk_print_status CHECK (status IN ('PENDING', 'CONFIRMED', 'PRINTING', 'READY', 'SHIPPED', 'COMPLETED')),
    CONSTRAINT chk_receipt_method CHECK (receipt_method IN ('PICKUP', 'DELIVERY'))
) ENGINE=InnoDB COMMENT='์ธํ™” ์ฃผ๋ฌธ';

CREATE TABLE print_order_item
(
    id             BIGINT      NOT NULL AUTO_INCREMENT,
    print_order_id BIGINT      NOT NULL, -- FK

    film_type      VARCHAR(20) NOT NULL,
    paper_type     VARCHAR(30) NOT NULL,
    print_method   VARCHAR(20) NOT NULL,
    size           VARCHAR(20) NOT NULL,
    frame_type     VARCHAR(20) NOT NULL,

    unit_price     INT UNSIGNED NOT NULL,
    total_price    INT UNSIGNED NOT NULL,

    created_at     DATETIME    NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at     DATETIME    NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,

    PRIMARY KEY (id),
    INDEX          idx_print_item_order (print_order_id),
    CONSTRAINT fk_print_item_order FOREIGN KEY (print_order_id) REFERENCES print_order (id)
) ENGINE=InnoDB COMMENT='์ธํ™” ์˜ต์…˜/๊ฐ€๊ฒฉ ์ƒ์„ธ';

CREATE TABLE print_order_photo
(
    id              BIGINT   NOT NULL AUTO_INCREMENT,
    print_order_id  BIGINT   NOT NULL,
    scanned_photo_id BIGINT  NOT NULL,
    quantity        INT      NOT NULL,

    created_at      DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at      DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,

    PRIMARY KEY (id),
    UNIQUE KEY uk_pop_order_photo (print_order_id, scanned_photo_id),

    CONSTRAINT fk_pop_print_order FOREIGN KEY (print_order_id) REFERENCES print_order (id),
    CONSTRAINT fk_pop_scanned_photo FOREIGN KEY (scanned_photo_id) REFERENCES scanned_photo (id)
) ENGINE=InnoDB COMMENT='์ธํ™” ์ฃผ๋ฌธ-์Šค์บ”์‚ฌ์ง„ ๋งคํ•‘(์ˆ˜๋Ÿ‰ ํฌํ•จ)';


CREATE TABLE delivery
(
    id              BIGINT       NOT NULL AUTO_INCREMENT,
    print_order_id  BIGINT       NOT NULL,
    recipient_name  VARCHAR(50)  NOT NULL,
    phone           VARCHAR(20)  NOT NULL,
    zipcode         VARCHAR(10)  NOT NULL,
    address         VARCHAR(200) NOT NULL,
    address_detail  VARCHAR(100) NULL,
    status          VARCHAR(20)  NOT NULL DEFAULT 'PENDING',
    tracking_number VARCHAR(50) NULL,
    carrier         VARCHAR(50) NULL,
    delivery_fee    INT UNSIGNED    NOT NULL DEFAULT 0,
    shipped_at      DATETIME NULL,
    delivered_at    DATETIME NULL,
    created_at      DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at      DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (id),
    UNIQUE KEY uk_delivery_order (print_order_id),
    INDEX           idx_delivery_status (status),
    CONSTRAINT fk_delivery_order FOREIGN KEY (print_order_id) REFERENCES print_order (id),
    CONSTRAINT chk_delivery_status CHECK (status IN ('PENDING', 'PREPARING', 'SHIPPED', 'IN_TRANSIT', 'DELIVERED'))
) ENGINE=InnoDB COMMENT='๋ฐฐ์†ก';

CREATE TABLE photo_restoration
(                                                                    -- Replicate AI ์‚ฌ์šฉ
    id                      BIGINT       NOT NULL AUTO_INCREMENT,
    member_id               BIGINT       NOT NULL,
    original_path           VARCHAR(500) NOT NULL,                   -- ์›๋ณธ ์ด๋ฏธ์ง€ GCS ๊ฒฝ๋กœ
    mask_path               VARCHAR(500) NULL,                       -- ๋งˆ์Šคํฌ ์ด๋ฏธ์ง€ GCS ๊ฒฝ๋กœ (๋ชจ๋ธ์— ๋”ฐ๋ผ ๋ถˆํ•„์š”)
    restored_path           VARCHAR(500) NULL,                       -- ๋ณต์›๋œ ์ด๋ฏธ์ง€ GCS ๊ฒฝ๋กœ
    restored_width          INT NULL,                                -- ๋ณต์›๋œ ์ด๋ฏธ์ง€ ๋„ˆ๋น„ (ํ”ฝ์…€)
    restored_height         INT NULL,                                -- ๋ณต์›๋œ ์ด๋ฏธ์ง€ ๋†’์ด (ํ”ฝ์…€)
    status                  VARCHAR(20)  NOT NULL DEFAULT 'PENDING', -- PENDING, PROCESSING, COMPLETED, FAILED
    replicate_prediction_id VARCHAR(100) NULL,                       -- Replicate API prediction ID (ํ•˜์œ„ํ˜ธํ™˜)
    provider_job_id         VARCHAR(100) NULL,                       -- AI ์ œ๊ณต์ž ์ž‘์—… ID (๋ฒ”์šฉ)
    provider_name           VARCHAR(30)  NULL,                       -- AI ์ œ๊ณต์ž ์ด๋ฆ„ (REPLICATE ๋“ฑ)
    -- ํฌ๋ ˆ๋”ง ๊ด€๋ จ
    credit_used             INT UNSIGNED    NOT NULL DEFAULT 1,      -- ์‚ฌ์šฉ๋œ ํฌ๋ ˆ๋”ง ์ˆ˜ (๋ณต์› ์™„๋ฃŒ ์‹œ ์ฐจ๊ฐ)
    -- ์—๋Ÿฌ ์ •๋ณด
    error_message           VARCHAR(500) NULL,                       -- ์‹คํŒจ ์‹œ ์—๋Ÿฌ ๋ฉ”์‹œ์ง€
    -- ํ”ผ๋“œ๋ฐฑ (AI ํ’ˆ์งˆ ๊ฐœ์„ ์šฉ)
    feedback_rating         VARCHAR(10) NULL,                        -- GOOD, BAD
    feedback_comment        VARCHAR(500) NULL,                       -- ํ”ผ๋“œ๋ฐฑ ์ฝ”๋ฉ˜ํŠธ (์„ ํƒ)
    created_at              DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at              DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (id),
    INDEX                   idx_restoration_member (member_id, status),
    INDEX                   idx_restoration_prediction (replicate_prediction_id),
    CONSTRAINT fk_restoration_member FOREIGN KEY (member_id) REFERENCES member (id),
    CONSTRAINT chk_restoration_status CHECK (status IN ('PENDING', 'PROCESSING', 'COMPLETED', 'FAILED')),
    CONSTRAINT chk_feedback_rating CHECK (feedback_rating IS NULL OR feedback_rating IN ('GOOD', 'BAD'))
) ENGINE=InnoDB COMMENT='AI ์‚ฌ์ง„ ๋ณต์›';

-- ============================================
-- 5. COMMUNITY
-- ============================================

CREATE TABLE post
(
    id                BIGINT      NOT NULL AUTO_INCREMENT,
    member_id         BIGINT      NOT NULL,               -- FK
    photo_lab_id      BIGINT NULL,                        -- FK (ํ˜„์ƒ์†Œ ์ด์šฉ ์‹œ)
    is_self_developed BOOLEAN     NOT NULL DEFAULT FALSE, -- TRUE: ์ž๊ฐ€ํ˜„์ƒ, FALSE: ํ˜„์ƒ์†Œ ์ด์šฉ
    title             VARCHAR(30) NOT NULL,               -- ๊ฒŒ์‹œ๊ธ€ ์ œ๋ชฉ
    content           TEXT        NOT NULL,
    lab_review        VARCHAR(300) NULL,                  -- ํ˜„์ƒ์†Œ ๋ฆฌ๋ทฐ (ํ˜„์ƒ์†Œ ์ด์šฉ ์‹œ)
    like_count        INT UNSIGNED    NOT NULL DEFAULT 0,
    comment_count     INT UNSIGNED    NOT NULL DEFAULT 0,
    status            VARCHAR(20) NOT NULL DEFAULT 'ACTIVE',
    created_at        DATETIME    NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at        DATETIME    NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    deleted_at        DATETIME NULL,
    PRIMARY KEY (id),
    INDEX             idx_post_member (member_id, status),
    INDEX             idx_post_lab (photo_lab_id),
    INDEX             idx_post_created (created_at DESC),
    FULLTEXT          INDEX ft_post_content (title, content),
    CONSTRAINT fk_post_member FOREIGN KEY (member_id) REFERENCES member (id),
    CONSTRAINT fk_post_lab FOREIGN KEY (photo_lab_id) REFERENCES photo_lab (id),
    CONSTRAINT chk_post_status CHECK (status IN ('ACTIVE', 'HIDDEN', 'DELETED')), -- CommunityStatus enum
    CONSTRAINT chk_post_lab_required CHECK (
        (is_self_developed = TRUE AND photo_lab_id IS NULL) OR
        (is_self_developed = FALSE AND photo_lab_id IS NOT NULL)
        )
) ENGINE=InnoDB COMMENT='๊ฒŒ์‹œ๊ธ€/๋ฆฌ๋ทฐ';

CREATE TABLE post_image
(                                        -- 1:N
    id            BIGINT       NOT NULL AUTO_INCREMENT,
    post_id       BIGINT       NOT NULL, -- FK
    object_path   VARCHAR(500) NOT NULL, -- GCS object path (์˜ˆ: posts/123/abc.jpg)
    display_order INT          NOT NULL DEFAULT 0,
    width         INT          NOT NULL, -- ์ด๋ฏธ์ง€ ๋„ˆ๋น„ (ํ”ฝ์…€)
    height        INT          NOT NULL, -- ์ด๋ฏธ์ง€ ๋†’์ด (ํ”ฝ์…€)
    created_at    DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (id),
    INDEX         idx_post_image (post_id),
    CONSTRAINT fk_post_image FOREIGN KEY (post_id) REFERENCES post (id) ON DELETE CASCADE
) ENGINE=InnoDB COMMENT='๊ฒŒ์‹œ๊ธ€ ์ด๋ฏธ์ง€';

CREATE TABLE comments
( -- ๋Œ“๊ธ€
    id         BIGINT        NOT NULL AUTO_INCREMENT,
    post_id    BIGINT        NOT NULL,
    member_id  BIGINT        NOT NULL,
    content    VARCHAR(1000) NOT NULL,
    status     VARCHAR(20)   NOT NULL DEFAULT 'ACTIVE',
    created_at DATETIME      NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at DATETIME      NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    deleted_at DATETIME NULL,
    PRIMARY KEY (id),
    INDEX      idx_comments_post (post_id, created_at),
    CONSTRAINT fk_comments_post FOREIGN KEY (post_id) REFERENCES post (id),
    CONSTRAINT fk_comments_member FOREIGN KEY (member_id) REFERENCES member (id),
    CONSTRAINT chk_comments_status CHECK (status IN ('ACTIVE', 'HIDDEN', 'DELETED')) -- CommunityStatus enum
) ENGINE=InnoDB COMMENT='๋Œ“๊ธ€';

CREATE TABLE post_like
( -- ํ•˜ํŠธ ํ‘œ์‹œ
    id         BIGINT   NOT NULL AUTO_INCREMENT,
    post_id    BIGINT   NOT NULL,
    member_id  BIGINT   NOT NULL,
    created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (id),
    UNIQUE KEY uk_post_like (post_id, member_id),
    CONSTRAINT fk_like_post FOREIGN KEY (post_id) REFERENCES post (id) ON DELETE CASCADE,
    CONSTRAINT fk_like_member FOREIGN KEY (member_id) REFERENCES member (id)
) ENGINE=InnoDB COMMENT='์ข‹์•„์š”';

CREATE TABLE search_history (
    id          BIGINT  NOT NULL AUTO_INCREMENT,
    member_id   BIGINT  NOT NULL,
    keyword     VARCHAR(100) NOT NULL,
    object_path VARCHAR(500) NULL,
    width       INT NULL,
    height      INT NULL,
    created_at  DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at  DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (id),
    INDEX       idx_search_history_member (member_id, updated_at DESC),
    CONSTRAINT  fk_search_history_member FOREIGN KEY (member_id) REFERENCES member (id) ON DELETE CASCADE
) ENGINE=InnoDB COMMENT='์ตœ๊ทผ ๊ฒ€์ƒ‰์–ด ์ด๋ ฅ';

-- ============================================
-- 6. INQUIRY
-- ============================================

CREATE TABLE inquiry
(
    id           BIGINT       NOT NULL AUTO_INCREMENT,
    member_id    BIGINT       NOT NULL,
    photo_lab_id BIGINT NULL,
    title        VARCHAR(200) NOT NULL,
    content      TEXT         NOT NULL,
    status       VARCHAR(20)  NOT NULL DEFAULT 'PENDING',
    created_at   DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at   DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (id),
    INDEX        idx_inquiry_member (member_id, status),
    INDEX        idx_inquiry_lab (photo_lab_id),
    CONSTRAINT fk_inquiry_member FOREIGN KEY (member_id) REFERENCES member (id),
    CONSTRAINT fk_inquiry_lab FOREIGN KEY (photo_lab_id) REFERENCES photo_lab (id),
    CONSTRAINT chk_inquiry_status CHECK (status IN ('PENDING', 'ANSWERED', 'CLOSED'))
) ENGINE=InnoDB COMMENT='1:1 ๋ฌธ์˜';

CREATE TABLE inquiry_image
(                                        -- ๋ฌธ์˜ ์ฒจ๋ถ€ ์ด๋ฏธ์ง€ (์ตœ๋Œ€ 5๊ฐœ)
    id            BIGINT       NOT NULL AUTO_INCREMENT,
    inquiry_id    BIGINT       NOT NULL, -- FK
    object_path   VARCHAR(500) NOT NULL, -- GCS object path (์˜ˆ: inquiries/123/abc.jpg)
    display_order INT          NOT NULL DEFAULT 0,
    created_at    DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at    DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (id),
    INDEX         idx_inquiry_image (inquiry_id),
    CONSTRAINT fk_inquiry_image FOREIGN KEY (inquiry_id) REFERENCES inquiry (id) ON DELETE CASCADE
) ENGINE=InnoDB COMMENT='๋ฌธ์˜ ์ด๋ฏธ์ง€';

CREATE TABLE inquiry_reply
(
    id         BIGINT   NOT NULL AUTO_INCREMENT,
    inquiry_id BIGINT   NOT NULL,
    replier_id BIGINT   NOT NULL, -- ๋‹ต๋ณ€์ž (ADMIN: ์„œ๋น„์Šค ๋ฌธ์˜, OWNER: ํ˜„์ƒ์†Œ ๋ฌธ์˜)
    content    TEXT     NOT NULL,
    created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (id),
    INDEX      idx_reply_inquiry (inquiry_id),
    CONSTRAINT fk_reply_inquiry FOREIGN KEY (inquiry_id) REFERENCES inquiry (id),
    CONSTRAINT fk_reply_replier FOREIGN KEY (replier_id) REFERENCES member (id)
) ENGINE=InnoDB COMMENT='๋ฌธ์˜ ๋‹ต๋ณ€';

-- ============================================
-- 7. COMMON
-- ============================================

CREATE TABLE notice
(                                                    -- ์ „์ฒด ํšŒ์› ๊ณต์ง€์‚ฌํ•ญ ๊ฒŒ์‹œํŒ -- โ›” ๋ฐ๋ชจ๋ฐ์ด ์ „ ๋ฏธ๊ตฌํ˜„
    id          BIGINT       NOT NULL AUTO_INCREMENT,
    title       VARCHAR(200) NOT NULL,
    content     TEXT         NOT NULL,
    notice_type VARCHAR(20)  NOT NULL DEFAULT 'GENERAL',
    is_pinned   BOOLEAN      NOT NULL DEFAULT FALSE, -- ๊ณ ์ •๋˜์—ˆ๋Š”์ง€
    view_count  INT UNSIGNED    NOT NULL DEFAULT 0,  -- ๊ณต์ง€์— view count๊ฐ€ ํ•„์š”ํ• ๊นŒ..?
    created_at  DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at  DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    deleted_at  DATETIME NULL,
    PRIMARY KEY (id),
    INDEX       idx_notice_pinned (is_pinned DESC, created_at DESC),
    CONSTRAINT chk_notice_type CHECK (notice_type IN ('GENERAL', 'EVENT', 'POLICY'))
) ENGINE=InnoDB COMMENT='๊ณต์ง€์‚ฌํ•ญ';

CREATE TABLE promotion
(                                         -- ๋ฉ”์ธํŽ˜์ด์ง€ ํ”„๋กœ๋ชจ์…˜ ๋ฐฐ๋„ˆ ๋ถ€๋ถ„ -- ๋ฐ๋ชจ๋ฐ์ด ์ „๊นŒ์ง€ ์ ˆ๋Œ€ ๊ฐœ๋ฐœ ๊ธˆ์ง€
    id             BIGINT       NOT NULL AUTO_INCREMENT,
    photo_lab_id   BIGINT       NOT NULL,
    title          VARCHAR(200) NOT NULL,
    description    VARCHAR(500) NULL,
    object_path    VARCHAR(500) NOT NULL, -- GCS object path (์˜ˆ: promotions/123/abc.jpg)
    promotion_type VARCHAR(20)  NOT NULL DEFAULT 'BANNER',
    display_order  INT          NOT NULL DEFAULT 0,
    start_date     DATETIME     NOT NULL,
    end_date       DATETIME     NOT NULL,
    is_active      BOOLEAN      NOT NULL DEFAULT TRUE,
    created_at     DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at     DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (id),
    INDEX          idx_promotion_active (is_active, start_date, end_date),
    CONSTRAINT fk_promotion_lab FOREIGN KEY (photo_lab_id) REFERENCES photo_lab (id),
    CONSTRAINT chk_promotion_type CHECK (promotion_type IN ('BANNER', 'POPUP', 'EVENT'))
) ENGINE=InnoDB COMMENT='ํ”„๋กœ๋ชจ์…˜';

CREATE TABLE notification
( -- ํšŒ์›๋ณ„ ๊ฐœ์ธ ์•Œ๋ฆผ(์•ฑ ํ‘ธ์‹œ/ ์•Œ๋ฆผ์„ผํ„ฐ) -- ๋ฐ๋ชจ๋ฐ์ด ์ „๊นŒ์ง€ ์ ˆ๋Œ€ ๊ฐœ๋ฐœ ๊ธˆ์ง€
    id                BIGINT       NOT NULL AUTO_INCREMENT,
    member_id         BIGINT       NOT NULL,
    title             VARCHAR(100) NOT NULL,
    content           VARCHAR(500) NOT NULL,
    notification_type VARCHAR(20)  NOT NULL,
    related_id        BIGINT NULL COMMENT '๊ด€๋ จ ์—”ํ‹ฐํ‹ฐ ID',
    related_type      VARCHAR(50) NULL COMMENT '๊ด€๋ จ ์—”ํ‹ฐํ‹ฐ ํƒ€์ž…',
    is_read           BOOLEAN      NOT NULL DEFAULT FALSE,
    created_at        DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (id),
    INDEX             idx_notification_member (member_id, is_read, created_at DESC),
    CONSTRAINT fk_notification_member FOREIGN KEY (member_id) REFERENCES member (id),
    CONSTRAINT chk_notification_type CHECK (notification_type IN
                                            ('ORDER', 'RESERVATION', 'COMMUNITY', 'MARKETING', 'NOTICE'))
) ENGINE=InnoDB COMMENT='์•Œ๋ฆผ';

CREATE TABLE payment
(                                                -- ํฌํŠธ์› V2 ๊ฒฐ์ œ ์—ฐ๋™
    id                 BIGINT       NOT NULL AUTO_INCREMENT,
    member_id          BIGINT       NOT NULL,
    -- ์ฃผ๋ฌธ ์ •๋ณด
    order_type         VARCHAR(20)  NOT NULL,    -- CREDIT_PURCHASE, DEVELOPMENT_ORDER, PRINT_ORDER
    related_order_id   BIGINT NULL,              -- development_order.id ๋˜๋Š” print_order.id (ํฌ๋ ˆ๋”ง ๊ตฌ๋งค ์‹œ NULL)
    payment_id         VARCHAR(64)  NOT NULL,    -- ๊ฒฐ์ œ ๊ฑด ID (์šฐ๋ฆฌ๊ฐ€ ์ƒ์„ฑ, ํฌํŠธ์›์— ์ „๋‹ฌ)
    order_name         VARCHAR(100) NOT NULL,    -- ์ฃผ๋ฌธ๋ช… (์˜ˆ: "AI ๋ณต์› ํฌ๋ ˆ๋”ง 10๊ฐœ")
    -- ๊ธˆ์•ก ์ •๋ณด
    amount             INT UNSIGNED    NOT NULL, -- ๊ฒฐ์ œ ์š”์ฒญ ๊ธˆ์•ก
    credit_amount      INT UNSIGNED    NULL,     -- ๊ตฌ๋งคํ•œ ํฌ๋ ˆ๋”ง ์ˆ˜ (CREDIT_PURCHASE ์‹œ)
    -- ํฌํŠธ์› V2 (์Šน์ธ ํ›„ ์ €์žฅ)
    transaction_id     VARCHAR(100) NULL,        -- ํฌํŠธ์› ์ฑ„๋ฒˆ ID (V1์˜ imp_uid์— ํ•ด๋‹น)
    pg_tx_id           VARCHAR(100) NULL,        -- PG์‚ฌ ๊ฑฐ๋ž˜ ID
    pg_provider        VARCHAR(30) NULL,         -- PG์‚ฌ (TOSSPAYMENTS, KCP, KAKAOPAY ๋“ฑ)
    -- ๊ฒฐ์ œ ์ˆ˜๋‹จ
    method             VARCHAR(30) NULL,         -- CARD, TRANSFER, VIRTUAL_ACCOUNT, MOBILE, EASY_PAY
    status             VARCHAR(30)  NOT NULL DEFAULT 'READY',
    -- ์นด๋“œ ์ •๋ณด (CARD, EASY_PAY ๊ฒฐ์ œ ์‹œ)
    card_company       VARCHAR(20) NULL,         -- ์นด๋“œ์‚ฌ (์‚ผ์„ฑ, ํ˜„๋Œ€ ๋“ฑ)
    card_number        VARCHAR(20) NULL,         -- ๋งˆ์Šคํ‚น๋œ ์นด๋“œ๋ฒˆํ˜ธ (1234****5678)
    approve_no         VARCHAR(20) NULL,         -- ์Šน์ธ๋ฒˆํ˜ธ (ํ™˜๋ถˆ/๋ถ„์Ÿ ์‹œ ํ•„์ˆ˜)
    installment_months INT NULL,                 -- ํ• ๋ถ€ ๊ฐœ์›”์ˆ˜ (0=์ผ์‹œ๋ถˆ)
    receipt_url        VARCHAR(500) NULL,        -- ์˜์ˆ˜์ฆ URL
    -- ์‹œ๊ฐ„ ์ •๋ณด
    requested_at       DATETIME NULL,            -- ๊ฒฐ์ œ ์š”์ฒญ ์‹œ๊ฐ
    paid_at            DATETIME NULL,            -- ๊ฒฐ์ œ ์™„๋ฃŒ ์‹œ๊ฐ
    -- ์‹คํŒจ/์ทจ์†Œ ์ •๋ณด
    fail_code          VARCHAR(50) NULL,
    fail_message       VARCHAR(200) NULL,
    cancelled_at       DATETIME NULL,
    cancel_reason      VARCHAR(200) NULL,
    cancel_amount      INT UNSIGNED    NULL,     -- ์ทจ์†Œ ๊ธˆ์•ก (๋ถ€๋ถ„์ทจ์†Œ ๋Œ€์‘)
    -- ๊ณตํ†ต
    created_at         DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at         DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (id),
    UNIQUE KEY uk_payment_id (payment_id),
    UNIQUE KEY uk_transaction_id (transaction_id),
    INDEX              idx_payment_member (member_id, status),
    INDEX              idx_payment_order_type (order_type, related_order_id),
    INDEX              idx_payment_status (status),
    CONSTRAINT fk_payment_member FOREIGN KEY (member_id) REFERENCES member (id),
    CONSTRAINT chk_payment_status CHECK (status IN (
                                                    'READY', 'PENDING', 'VIRTUAL_ACCOUNT_ISSUED', 'PAID',
                                                    'FAILED', 'PARTIAL_CANCELLED', 'CANCELLED'
        )),
    CONSTRAINT chk_order_type CHECK (order_type IN ('CREDIT_PURCHASE', 'DEVELOPMENT_ORDER', 'PRINT_ORDER')),
    CONSTRAINT chk_payment_method CHECK (method IS NULL OR method IN (
        'CARD', 'TRANSFER', 'VIRTUAL_ACCOUNT', 'EASY_PAY'
    )),
    CONSTRAINT chk_payment_data CHECK (
        (order_type = 'CREDIT_PURCHASE' AND credit_amount IS NOT NULL AND related_order_id IS NULL) OR
        (order_type IN ('DEVELOPMENT_ORDER', 'PRINT_ORDER') AND related_order_id IS NOT NULL AND credit_amount IS NULL)
        )
) ENGINE=InnoDB COMMENT='๊ฒฐ์ œ (ํฌํŠธ์› V2)';

ํ™”๋ฉด-ํ…Œ์ด๋ธ” ๋งคํ•‘

ํ™”๋ฉด ํ…Œ์ด๋ธ”
CM-020~022 ๋กœ๊ทธ์ธ/๊ฐ€์ž… member, member_user, social_account, member_agreement
HM-010 ๋ฉ”์ธ ํ™ˆ photo_lab, promotion
HM-021~025 ์‚ฌ์ง„ ๋ณต์› photo_restoration, credit_history, member_user (credit_balance)
PL-010~011 ํ˜„์ƒ์†Œ ํƒ์ƒ‰ photo_lab, photo_lab_tag, tag, region
PL-020~021 ํ˜„์ƒ์†Œ ์ƒ์„ธ/์˜ˆ์•ฝ photo_lab_*, reservation, payment
CO-020~030 ์‚ฌ์ง„์ˆ˜๋‹ค post (์ž๊ฐ€ํ˜„์ƒ ์—ฌ๋ถ€), post_image, comment, post_like
PM-000~018 ํ˜„์ƒ๊ด€๋ฆฌ development_order, scanned_photo, print_order, print_order_item, delivery, payment
UR-010~025 ๋งˆ์ดํŽ˜์ด์ง€ member, member_user, social_account
UR-030~040 ๊ด€์‹ฌ๋ชฉ๋ก favorite_photo_lab, post_like
UR-060~062 ๋ฐฐ์†ก์ง€ member_address
UR-070~081 ๊ณต์ง€/๋ฌธ์˜ notice, inquiry, inquiry_reply
์•Œ๋ฆผ ์„ผํ„ฐ notification
์‚ฌ์—…์ž ๋“ฑ๋ก member_owner, photo_lab, photo_lab_document

๋ณ€๊ฒฝ ์ด๋ ฅ

๋ฒ„์ „ ๋‚ ์งœ ๋ณ€๊ฒฝ ๋‚ด์šฉ
3.0.0 2026-01-18 ๋ฒ„์ „ ์ฒด๊ณ„ ์žฌ์ •๋น„: ์ด์ „ ๋ณ€๊ฒฝ ์ด๋ ฅ ์•„์นด์ด๋น™ (Git ํžˆ์Šคํ† ๋ฆฌ ์ฐธ์กฐ), ํ˜„์žฌ ์Šคํ‚ค๋งˆ ๊ธฐ์ค€ ๋ฉ”์ด์ € ๋ฒ„์ „ ์„ค์ • (35๊ฐœ ํ…Œ์ด๋ธ”)
3.0.1 2026-01-19 ์ธํ™” ๋„๋ฉ”์ธ ์Šคํ‚ค๋งˆ ๋ฐ˜์˜: development_order์— ์ž‘์—… ์„ ํƒ ์ปฌ๋Ÿผ(is_develop/is_scan/is_print/roll_count) ์ถ”๊ฐ€, print_order์— ์ž…๊ธˆ ์บก์ฒ˜/์ž…๊ธˆ์ž/์€ํ–‰/์ œ์ถœ์‹œ๊ฐ ์ปฌ๋Ÿผ ์ถ”๊ฐ€, print_order_item ๊ตฌ์กฐ๋ฅผ ์˜ต์…˜ ๋‹จ์œ„๋กœ ๋ณ€๊ฒฝ(film_type/paper_type/print_method/size/frame_type), ์ธํ™” ๋Œ€์ƒ ์‚ฌ์ง„ ๋งคํ•‘ ํ…Œ์ด๋ธ” print_order_photo ์ถ”๊ฐ€
3.0.2 2026-01-23 photo_lab ํ…Œ์ด๋ธ” ๋‚ด review_count ์ถ”๊ฐ€, search_history ํ…Œ์ด๋ธ” ์‹ ๊ทœ ์ƒ์„ฑ
3.0.3 2026-01-25 region ํ…Œ์ด๋ธ” ์นผ๋Ÿผ๋ช… ๋ณ€๊ฒฝ sido -> parentRegion, sigungu -> regionName
3.1.0 2026-02-10 Token โ†’ Credit ์šฉ์–ด ๋ฆฌํŒฉํ† ๋ง: token_history โ†’ credit_history, token_balance โ†’ credit_balance, token_amount โ†’ credit_amount, token_used โ†’ credit_used, TOKEN_PURCHASE โ†’ CREDIT_PURCHASE
3.2.0 2026-02-11 ERD-์ฝ”๋“œ ์ „๋ฉด ๋™๊ธฐํ™”: reservation ๊ตฌ์กฐ ๋ณ€๊ฒฝ (slot_id FK, status: RESERVED/COMPLETED/CANCELED), reservation_slot ํ…Œ์ด๋ธ” ๋ชฉ๋ก ์ถ”๊ฐ€, terms_social_mapping ํ…Œ์ด๋ธ” ์‹ ๊ทœ ์ถ”๊ฐ€, TermsType CHECK ์ˆ˜์ • (SERVICE_INFO/MARKETING), photo_lab์— load_base_roll/load_add_minutes ์ถ”๊ฐ€, photo_restoration์— provider_job_id/provider_name ์ถ”๊ฐ€ ๋ฐ mask_path nullable ๋ณ€๊ฒฝ, post.title VARCHAR(30), post.lab_review VARCHAR(300), member_address์—์„œ recipient_name/phone ์ œ๊ฑฐ, Enum ์ •์˜ ์ฝ”๋“œ ๊ธฐ์ค€ ์ „๋ฉด ์ •๋น„ (CommunityStatus, CreditRelatedType, FilmType, PrintSize, FrameType, RestorationTier ๋“ฑ), notice/promotion/notification/member_device ๋ฐ๋ชจ๋ฐ์ด ์ „ ๋ฏธ๊ตฌํ˜„ ํ‘œ๊ธฐ

์ฐธ๊ณ : v2.5.1 ์ด์ „ ๋ณ€๊ฒฝ ์ด๋ ฅ์€ Git ์ปค๋ฐ‹ ํžˆ์Šคํ† ๋ฆฌ์—์„œ ํ™•์ธ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.