할인코드 DB 마이그레이션 + 모델 + 서비스

ID: 7c977bcb-ccb3-48d9-89a5-82dd5f80d527

높음 완료

## 목표
할인코드(DiscountCode) 테이블 생성, 모델 정의, 할인코드 검증/적용 서비스 구현

## 작업 내용

### 1. DB 마이그레이션
- `discount_codes` 테이블 생성:
- name (string, NOT NULL) - 할인코드 이름
- code (string, NOT NULL, UNIQUE) - 할인코드 (자동생성 가능)
- discount_type (string, NOT NULL) - 'percentage' 또는 'fixed_amount'
- value (integer, NOT NULL) - 할인값 (퍼센트 또는 원)
- applies_to_all (boolean, default: true) - 전체 상품 적용 여부
- max_uses (integer, nullable) - 최대 사용횟수 (null이면 무제한)
- current_uses (integer, default: 0) - 현재 사용횟수
- min_purchase_amount (integer, default: 0) - 최소 구매금액
- expires_at (datetime, nullable) - 만료일
- active (boolean, default: true) - 활성화 여부
- metadata (jsonb, default: {})
- timestamps
- 인덱스: code (unique), active, expires_at

- `discount_code_products` 조인 테이블 생성:
- discount_code_id (references, FK)
- product_id (references, FK)
- 인덱스: [discount_code_id, product_id] (unique)

- `payments` 테이블의 discount_code_id를 UUID FK로 변환하는 마이그레이션

### 2. 모델
- `DiscountCode` 모델:
- enum discount_type: { percentage: "percentage", fixed_amount: "fixed_amount" }
- has_many :discount_code_products, dependent: :destroy
- has_many :products, through: :discount_code_products
- has_many :payments
- validates :name, :code, :discount_type, :value, presence: true
- validates :code, uniqueness: true
- validates :value, numericality: { greater_than: 0 }
- validate: percentage는 1-100 범위
- scope :available - active && (expires_at nil OR 미래) && (max_uses nil OR current_uses < max_uses)
- before_create :generate_code_if_blank (SecureRandom.alphanumeric(8).upcase)
- `applicable_to?(product)`: applies_to_all이거나 products에 포함
- `calculate_discount(amount)`: 할인 금액 계산
- `usable?`: 사용 가능 여부 확인
- `use!`: current_uses 증가

- `DiscountCodeProduct` 모델:
- belongs_to :discount_code
- belongs_to :product

- `Payment` 모델 수정:
- belongs_to :discount_code, optional: true

### 3. 서비스
- `DiscountCodes::ValidationService`:
- initialize(code:, product: nil, amount: 0)
- call: 유효성 검사 (존재, 활성, 만료, 사용횟수, 최소금액, 상품 적용)
- Result(discount_code, discount_amount, error)

- `DiscountCodes::ApplyService`:
- initialize(discount_code:, payment:)
- call: 할인코드 사용 기록 (current_uses 증가, payment.discount_code_id 설정)

### 4. Fixture
- test/fixtures/discount_codes.yml (active, expired, max_used, inactive 등)
- test/fixtures/discount_code_products.yml

### 5. 테스트
- test/models/discount_code_test.rb
- test/models/discount_code_product_test.rb
- test/services/discount_codes/validation_service_test.rb
- test/services/discount_codes/apply_service_test.rb

## 완료 기준
- 모든 마이그레이션 실행 성공
- 모델 테스트 통과
- 서비스 테스트 통과
- 기존 테스트가 깨지지 않음

첨부 이미지

이미지 추가 (Ctrl+V로 붙여넣기 또는 클릭)

JPEG, PNG, GIF, WebP / 최대 10MB

담당자: model-dev
생성일: 2026년 02월 28일 13:20

활동 로그

  • 팀리드 상태 변경: 리뷰 → 완료

    2026년 02월 28일 13:37:38

  • M
    model-dev 상태 변경: 진행 중 → 리뷰

    2026년 02월 28일 13:27:59

  • M
    model-dev 모델/서비스 구현 완료

    2026년 02월 28일 13:27:58

    상세 보기

    ## 생성한 파일
    ### 마이그레이션
    - `db/migrate/20260228140000_create_discount_codes.rb` - discount_codes 테이블 생성
    - `db/migrate/20260228140001_create_discount_code_products.rb` - 조인 테이블 생성
    - `db/migrate/20260228140002_convert_discount_code_id_to_reference.rb` - payments.discount_code_id를 string→uuid FK 변환

    ### 모델
    - `app/models/discount_code.rb` - enum, validations, scopes, calculate_discount, usable?, use!, applicable_to?
    - `app/models/discount_code_product.rb` - belongs_to 연관관계, uniqueness 검증

    ### 서비스
    - `app/services/discount_codes/validation_service.rb` - 할인코드 유효성 검증 (Result 패턴)
    - `app/services/discount_codes/apply_service.rb` - 할인코드 적용 (current_uses 증가, payment 연결)

    ### Fixture
    - `test/fixtures/discount_codes.yml` - 7개 (percentage_active, fixed_active, expired, maxed_out, inactive, applies_to_specific, unlimited_uses)
    - `test/fixtures/discount_code_products.yml` - 1개

    ### 테스트
    - `test/models/discount_code_test.rb` - 33개 테스트
    - `test/models/discount_code_product_test.rb` - 4개 테스트
    - `test/services/discount_codes/validation_service_test.rb` - 12개 테스트
    - `test/services/discount_codes/apply_service_test.rb` - 4개 테스트

    ### 기존 모델 수정
    - `app/models/payment.rb` - `belongs_to :discount_code, optional: true` 추가

    ## 테스트 결과
    전체 711개 테스트 통과, 0 failures, 0 errors

  • M
    model-dev 모델/서비스 구현 시작

    2026년 02월 28일 13:22:35

    상세 보기

    기존 코드 구조 파악 및 TDD 기반 구현 시작

  • M
    model-dev 티켓 클레임 완료

    2026년 02월 28일 13:22:31