# 05. Data Model

## 1. 모델링 원칙

1. `Region/LocalGov`가 모든 도메인의 중심이다.
2. 체험, 숙소, 상품, 답례품은 공통 카탈로그 개념으로 묶되, 각 도메인별 상세 테이블을 둔다.
3. 기부는 결제와 다르며 e음 검증/영수증/포인트 상태를 별도 추적한다.
4. 모든 운영자 변경은 감사 로그를 남긴다.
5. 레거시 id와 신규 id를 함께 보관해 마이그레이션과 리다이렉트를 지원한다.

## 2. 핵심 엔티티

```text
User
Region
LocalGov
CatalogItem
TravelProduct
DonationGift
DonationCampaign
Donation
DonationPoint
Reservation
Order
ContentItem
RecommendationProfile
Partner
Supplier
AdminUser
EumIntegrationLog
Settlement
AuditLog
LegacyMapping
```

## 3. 주요 테이블 초안

### User

| 필드 | 설명 |
|---|---|
| id | 내부 사용자 id |
| email/phone | 로그인 식별자 |
| name | 본인확인 후 이름 |
| ci_hash | 본인확인 연계 식별자 해시 |
| resident_region_code | 검증된 주소지 지역 코드 |
| verification_status | none/pending/verified/expired |
| created_at/updated_at | 생성/수정 시각 |

### LocalGov

| 필드 | 설명 |
|---|---|
| id | 내부 지자체 id |
| region_id | 상위 Region |
| name | 지자체명 |
| province_code | 시도 코드 |
| city_code | 시군구 코드 |
| eum_code | 고향사랑e음 연동 코드 |
| donation_enabled | 기부 가능 여부 |
| contract_status | 미계약/계약/중지 |
| slug | URL slug |

### CatalogItem

| 필드 | 설명 |
|---|---|
| id | 공통 카탈로그 id |
| item_type | travel/gift/local_product |
| local_gov_id | 지역 |
| title | 제목 |
| summary | 요약 |
| status | draft/review/published/suspended |
| primary_image_id | 대표 이미지 |
| tags | 검색/추천 태그 |
| legacy_source | nolgofarm/ilovegohyang 등 |
| legacy_id | 기존 id |

### TravelProduct

| 필드 | 설명 |
|---|---|
| catalog_item_id | CatalogItem 참조 |
| travel_type | experience/stay/camping/farm/group |
| address | 주소 |
| geo | 위경도 |
| price_policy | 가격/옵션 정책 |
| booking_policy | 예약 가능일/취소 정책 |
| capacity | 인원 |
| supplier_id | 공급자 |

### DonationGift

| 필드 | 설명 |
|---|---|
| catalog_item_id | CatalogItem 참조 |
| local_gov_id | 답례품 제공 지자체 |
| required_donation_amount | 필요 기부금액 |
| gift_value | 답례품 평가액 |
| point_cost | 필요 포인트 |
| shipping_policy | 배송 정책 |
| stock_status | 재고 상태 |
| supplier_id | 공급자 |

검증 규칙:

- `gift_value <= required_donation_amount * 0.3`
- `local_gov_id`는 포인트 사용 가능 지자체와 일치해야 한다.

### DonationCampaign

| 필드 | 설명 |
|---|---|
| id | 캠페인 id |
| local_gov_id | 지자체 |
| title | 캠페인명 |
| category | 재난/아동/어르신/환경 등 |
| goal_amount | 목표금액 |
| raised_amount | 현재 모금액 |
| start_at/end_at | 사업 기간 |
| status | draft/review/open/closed/reporting/completed |
| fund_usage | 사용처 |
| story_content_id | 상세 스토리 |
| report_content_id | 결과보고 |

### Donation

| 필드 | 설명 |
|---|---|
| id | 기부 id |
| user_id | 기부자 |
| local_gov_id | 기부 지자체 |
| campaign_id | 지정기부인 경우 |
| amount | 기부금액 |
| donation_type | general/designated |
| tax_credit_estimate | 예상 세액공제 |
| status | started/verifying/payment_pending/submitted/confirmed/failed/cancelled |
| idempotency_key | 중복 방지 키 |
| eum_receipt_id | e음 영수증/접수 id |
| created_at/confirmed_at | 시각 |

### DonationPoint

| 필드 | 설명 |
|---|---|
| id | 포인트 id |
| user_id | 사용자 |
| local_gov_id | 지자체 |
| donation_id | 원 기부 |
| amount | 생성 포인트 |
| used_amount | 사용 포인트 |
| expires_at | 유효기간 |
| status | active/used/expired/cancelled |

### ContentItem

| 필드 | 설명 |
|---|---|
| id | 콘텐츠 id |
| content_type | story/festival/policy/news/report/guide |
| title | 제목 |
| body | 본문 |
| linked_local_gov_id | 연결 지역 |
| linked_catalog_item_id | 연결 상품 |
| linked_campaign_id | 연결 캠페인 |
| ai_generated | AI 초안 여부 |
| legal_review_required | 법령 검수 필요 여부 |
| status | draft/review/published/archived |

## 4. 관계 요약

```text
Region 1:N LocalGov
LocalGov 1:N CatalogItem
CatalogItem 1:0..1 TravelProduct
CatalogItem 1:0..1 DonationGift
LocalGov 1:N DonationCampaign
User 1:N Donation
Donation 1:0..1 DonationPoint
User 1:N Reservation
User 1:N Order
ContentItem N:1 LocalGov
ContentItem N:0..1 CatalogItem
ContentItem N:0..1 DonationCampaign
```

## 5. 레거시 매핑

LegacyMapping:

| 필드 | 설명 |
|---|---|
| id | 매핑 id |
| source_system | nolgofarm/ilovegohyang/productOld 등 |
| source_table | 기존 테이블/도메인 |
| source_id | 기존 id |
| source_url | 기존 URL |
| target_entity | 신규 엔티티명 |
| target_id | 신규 id |
| redirect_url | 신규 URL |
| migration_status | pending/migrated/failed/ignored |

## 6. 계산 규칙

### 세액공제 예상액

```text
if amount <= 100000:
  credit = amount
else if amount <= 200000:
  credit = 100000 + (amount - 100000) * 0.44
else:
  credit = 100000 + 100000 * 0.44 + (amount - 200000) * 0.165
```

특별재난지역은 선포일로부터 3개월 내 기부 시 20만원 초과분 33%를 적용한다.

### 답례품 한도

```text
max_gift_value = donation_amount * 0.3
```

### 기부 가능 지역

```text
user.resident_province != target.province
AND user.resident_city != target.city
```

실제 판정은 고향사랑e음 검증 결과를 최종값으로 한다.

