# 機能設計書 7-アカウント登録

## 概要

本ドキュメントは、Shopware 6 Storefrontにおけるアカウント登録機能の設計仕様を記述する。新規顧客がアカウントを作成する機能である。

### 本機能の処理概要

**業務上の目的・背景**：ECサイトにおいて、新規顧客がアカウントを作成することで、購入履歴の管理、住所の保存、お気に入り機能、会員価格の適用などのパーソナライズされたサービスを利用できるようになる。また、ゲストとしての注文も本機能で処理される。

**機能の利用シーン**：新規顧客がショップを訪問し、アカウントを作成したい場合に使用される。また、チェックアウト時にゲストとして注文する場合や、新規アカウントを作成しながら注文する場合にも利用される。

**主要な処理内容**：
1. 登録フォームの表示
2. 顧客情報（名前、メール、パスワード等）の入力
3. 請求先・配送先住所の入力
4. 入力データのバリデーション
5. 顧客レコードの作成
6. ダブルオプトイン設定時は確認メール送信
7. 自動ログイン
8. 登録イベントの発行

**関連システム・外部連携**：メール送信システム（ダブルオプトイン時）

**権限による制御**：未ログイン状態の顧客が使用可能。ゲストログイン中は通常のアカウントでのログインページにリダイレクト。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 12 | ログイン/登録ページ | 補助機能 | 新規アカウント作成（RegisterController::register） |
| 9 | チェックアウト登録ページ | 補助機能 | 新規顧客アカウントの作成（RegisterController::register） |

## 機能種別

データ登録 / 認証処理 / メール送信

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| salutationId | string | No | 敬称ID | UUID、存在確認 |
| firstName | string | Yes | 名 | NotBlank |
| lastName | string | Yes | 姓 | NotBlank |
| email | string | Yes | メールアドレス | Email形式、重複チェック（非ゲスト時） |
| emailConfirmation | string | Conditional | メール確認 | emailと一致（設定による） |
| password | string | Conditional | パスワード | パスワードポリシー（非ゲスト時） |
| passwordConfirmation | string | Conditional | パスワード確認 | passwordと一致（設定による） |
| createCustomerAccount | bool | No | アカウント作成フラグ | - |
| guest | bool | No | ゲストフラグ | - |
| accountType | string | No | アカウント種別 | private/business |
| billingAddress | DataBag | Yes | 請求先住所 | 住所バリデーション |
| shippingAddress | DataBag | No | 配送先住所 | 住所バリデーション（存在時） |
| differentShippingAddress | bool | No | 別配送先フラグ | - |
| vatIds | array | Conditional | VAT ID | 法人の場合は国によって必須 |
| acceptedDataProtection | bool | Conditional | データ保護同意 | 設定による |
| birthdayDay/Month/Year | int | No | 生年月日 | 有効な日付 |
| storefrontUrl | string | Yes | ストアフロントURL | Sales Channelドメイン一覧に存在 |

### 入力データソース

- 画面入力（登録フォーム）
- POSTリクエストボディ

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| customer | CustomerEntity | 作成された顧客エンティティ |
| token | string | 新しいセッショントークン（ダブルオプトインでない場合） |

### 出力先

- データベース（customer, customer_addressテーブル）
- セッション（コンテキストトークン）
- メール（ダブルオプトイン時）
- HTTPレスポンス（リダイレクト）

## 処理フロー

### 処理シーケンス

```
1. リクエスト受信（フォーム表示）
   └─ RegisterController::accountRegisterPage がGETリクエストを受け取る
   └─ ゲストログイン中はログアウトページへリダイレクト
   └─ ログイン済みはアカウントホームへリダイレクト

2. リクエスト受信（フォーム送信）
   └─ RegisterController::register がPOSTリクエストを受け取る

3. データ前処理
   └─ differentShippingAddressがない場合はshippingAddressを削除
   └─ storefrontUrl設定
   └─ アフィリエイトトラッキング情報の追加
   └─ guestフラグの設定（createCustomerAccountの逆）

4. 登録ルート呼び出し
   └─ RegisterRoute::register を実行

5. メールアドレスIDN変換
   └─ 国際化ドメイン名を適切にエンコード

6. バリデーション
   └─ 顧客情報バリデーション
   └─ 住所バリデーション
   └─ パスワードポリシーチェック（非ゲスト時）
   └─ メール重複チェック（非ゲスト時）
   └─ VAT ID検証（法人の場合）

7. 顧客データマッピング
   └─ 入力データを顧客エンティティ形式に変換
   └─ 顧客番号を採番

8. 住所データマッピング
   └─ 請求先住所のマッピング
   └─ 配送先住所のマッピング（存在時）

9. ダブルオプトイン処理
   └─ 設定に応じてハッシュを生成

10. 顧客作成
    └─ customerテーブルにINSERT

11. ダブルオプトイン時
    └─ 確認メールイベントを発行
    └─ 登録ページへリダイレクト

12. 自動ログイン処理
    └─ セッションコンテキストを更新
    └─ 顧客IDをコンテキストに設定

13. イベント発行
    └─ CustomerRegisterEvent または GuestCustomerRegisterEvent
    └─ CustomerLoginEvent

14. レスポンス生成
    └─ リダイレクト先へ遷移
```

### フローチャート

```mermaid
flowchart TD
    A[登録開始] --> B{ログイン済み?}
    B -->|ゲスト| C[ログアウトページへ]
    B -->|会員| D[アカウントホームへ]
    B -->|未ログイン| E[登録フォーム表示]
    E --> F[データ入力]
    F --> G[RegisterRoute::register]
    G --> H[バリデーション]
    H --> I{バリデーションOK?}
    I -->|No| J[エラー表示]
    I -->|Yes| K[顧客データマッピング]
    K --> L[住所データマッピング]
    L --> M{ダブルオプトイン?}
    M -->|Yes| N[ハッシュ生成]
    M -->|No| O[顧客作成]
    N --> O
    O --> P{ダブルオプトイン?}
    P -->|Yes| Q[確認メール送信]
    Q --> R[登録ページへ]
    P -->|No| S[セッション更新]
    S --> T[登録イベント発行]
    T --> U[ログインイベント発行]
    U --> V[リダイレクト先へ]
    J --> E
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-001 | メール重複禁止 | 同一メールアドレスでの登録は不可（非ゲスト時） | 登録時 |
| BR-002 | ゲストフラグ | createCustomerAccount=falseの場合はguest=true | 登録時 |
| BR-003 | ダブルオプトイン（会員） | core.loginRegistration.doubleOptInRegistration設定に従う | 会員登録時 |
| BR-004 | ダブルオプトイン（ゲスト） | core.loginRegistration.doubleOptInGuestOrder設定に従う | ゲスト注文時 |
| BR-005 | 法人VAT ID | 法人アカウントでVAT ID必須の国の場合は入力必須 | 法人登録時 |
| BR-006 | Sales Channel制限 | boundSalesChannelIdで顧客をSales Channelに紐付け可能 | 設定による |
| BR-007 | 顧客番号採番 | numberRangeから自動採番 | 登録時 |
| BR-008 | デフォルト敬称 | salutationIdが未指定の場合は「未指定」を使用 | 登録時 |

### 計算ロジック

- 顧客番号: `numberRangeValueGenerator.getValue('customer', context, salesChannelId)`
- ダブルオプトインハッシュ: `Uuid::randomHex()`
- 確認URL: `storefrontUrl + '/registration/confirm?em=' + sha1(email) + '&hash=' + hash`

## データベース操作仕様

### 操作別データベース影響一覧

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 顧客作成 | customer | INSERT | 新規顧客レコード作成 |
| 住所作成 | customer_address | INSERT | 請求先・配送先住所作成 |
| メール重複確認 | customer | SELECT | 同一メールの存在確認 |
| コンテキスト更新 | sales_channel_api_context | UPDATE | セッションに顧客IDを設定 |

### テーブル別操作詳細

#### customer

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | id, email | email = 入力値 | 重複チェック |
| INSERT | customer_number, sales_channel_id, language_id, group_id, salutation_id, first_name, last_name, email, password, title, affiliate_code, campaign_code, active, birthday, guest, first_login, default_billing_address_id, default_shipping_address_id, requested_group_id, bound_sales_channel_id, double_opt_in_registration, double_opt_in_email_sent_date, hash, account_type, company, vat_ids, custom_fields | 各フィールドの値 | 顧客作成 |

#### customer_address

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | id, customer_id, salutation_id, first_name, last_name, street, zipcode, city, country_id, country_state_id, company, department, title, phone_number, additional_address_line1, additional_address_line2, custom_fields | 各フィールドの値 | 住所作成 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | ConstraintViolationException | バリデーションエラー | フォームにエラー表示 |
| - | CustomerEmailUnique違反 | メールアドレス重複 | エラー表示 |
| - | CustomerVatIdentification違反 | VAT ID不正 | エラー表示 |
| FRAMEWORK__MISSING_REQUEST_PARAMETER | RoutingException | errorRouteパラメータなし | デフォルトルートを使用 |

### リトライ仕様

- バリデーションエラー時はフォームを再表示して修正可能

## トランザクション仕様

- 顧客と住所の作成は同一トランザクション内で実行
- コンテキスト更新は別トランザクション

## パフォーマンス要件

- 登録処理全体で1秒以内のレスポンスを目標
- 顧客番号採番は排他制御あり

## セキュリティ考慮事項

- パスワードはbcryptでハッシュ化して保存
- メールアドレスの重複チェックで既存顧客の情報漏洩を防止
- ダブルオプトインでスパム登録を防止
- CAPTCHAによるボット登録防止（設定による）
- データ保護同意チェック（設定による）

## 備考

- アフィリエイトトラッキング情報はセッションから取得
- requestedGroupIdで特定の顧客グループへの登録リクエストが可能
- カスタムフィールドはStoreApiCustomFieldMapperでマッピング

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

顧客エンティティと住所エンティティの構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | CustomerEntity.php | `src/Core/Checkout/Customer/CustomerEntity.php` | 顧客の全プロパティ |
| 1-2 | CustomerAddressEntity.php | `src/Core/Checkout/Customer/Aggregate/CustomerAddress/CustomerAddressEntity.php` | 住所のプロパティ |
| 1-3 | CustomerResponse.php | `src/Core/Checkout/Customer/SalesChannel/CustomerResponse.php` | レスポンス構造 |

#### Step 2: エントリーポイントを理解する

StorefrontのリクエストはRegisterControllerで受け付けられる。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | RegisterController.php | `src/Storefront/Controller/RegisterController.php` | accountRegisterPage()（76-112行目）、register()（197-244行目） |

**主要処理フロー**:
1. **84-90行目**: ゲスト・ログイン済みチェック
2. **92-96行目**: HTTPキャッシュ設定
3. **98-101行目**: ページデータ読み込みとフック実行
4. **209-216行目**: データ前処理（differentShippingAddress、storefrontUrl等）
5. **218-223行目**: registerRoute呼び出し
6. **224-237行目**: ConstraintViolationExceptionのキャッチとフォワード
7. **239-243行目**: ダブルオプトインの場合は登録ページへリダイレクト

#### Step 3: Store-APIルートを理解する

実際の登録ロジックはRegisterRouteに実装されている。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | RegisterRoute.php | `src/Core/Checkout/Customer/SalesChannel/RegisterRoute.php` | register()メソッド（101-267行目） |

**主要処理フロー**:
- **108行目**: EmailIdnConverter::encodeDataBag
- **110行目**: isGuestフラグ取得
- **116-135行目**: salutationId設定、名前の取得
- **137行目**: validateRegistrationData呼び出し
- **139-164行目**: 顧客・住所データのマッピング
- **178行目**: addDoubleOptInData
- **198行目**: customerRepository->create
- **205-220行目**: ダブルオプトインイベント発行
- **224-260行目**: 非ダブルオプトイン時のセッション処理とイベント発行

### プログラム呼び出し階層図

```
RegisterController::register (Storefront)
    │
    ├─ データ前処理
    │      ├─ differentShippingAddress処理
    │      ├─ storefrontUrl設定
    │      └─ prepareAffiliateTracking
    │
    └─ RegisterRoute::register (Store-API)
           │
           ├─ EmailIdnConverter::encodeDataBag
           │
           ├─ getDefaultSalutationId (salutationId未設定時)
           │
           ├─ validateRegistrationData
           │      ├─ accountValidationFactory.create
           │      ├─ addressValidationFactory.create
           │      ├─ passwordValidationFactory.create (非ゲスト時)
           │      └─ DataValidator.validate
           │
           ├─ mapCustomerData
           │      ├─ NumberRangeValueGenerator.getValue
           │      └─ DataMappingEvent (dispatch)
           │
           ├─ mapAddressData (請求先)
           │      └─ DataMappingEvent (dispatch)
           │
           ├─ mapAddressData (配送先、存在時)
           │      └─ DataMappingEvent (dispatch)
           │
           ├─ addDoubleOptInData
           │
           ├─ getBoundSalesChannelId
           │
           ├─ CustomerRepository.create
           │
           ├─ [ダブルオプトイン]
           │      ├─ getDoubleOptInEvent
           │      └─ CustomerDoubleOptInRegistrationEvent (dispatch)
           │
           └─ [非ダブルオプトイン]
                  ├─ SalesChannelContextPersister.replace
                  ├─ SalesChannelContextPersister.save
                  ├─ SalesChannelContextService.get
                  ├─ CustomerRegisterEvent (dispatch)
                  └─ CustomerLoginEvent (dispatch)
```

### データフロー図

```
[入力]                    [処理]                         [出力]

firstName, ───▶ RegisterController::register
lastName,            │
email,               ▼
password,      データ前処理
billingAddress,      │
...                  ▼
             RegisterRoute::register
                     │
                     ▼
             バリデーション
                     │
                     ▼
             顧客データマッピング
                     │
                     ▼
             住所データマッピング
                     │
                     ▼
             customer ───▶ [INSERT]
             customer_address ───▶ [INSERT]
                     │
               ┌─────┴─────┐
               ▼           ▼
          ダブルオプトイン  即時ログイン
               │           │
               ▼           ▼
          確認メール送信  セッション更新
               │           │
               ▼           ▼
          登録ページへ  CustomerRegisterEvent
                           CustomerLoginEvent
                           │
                           ▼
                       リダイレクト先
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| RegisterController.php | `src/Storefront/Controller/RegisterController.php` | コントローラ | 登録エンドポイント |
| RegisterRoute.php | `src/Core/Checkout/Customer/SalesChannel/RegisterRoute.php` | ルート | 登録のStore-APIルート |
| AbstractRegisterRoute.php | `src/Core/Checkout/Customer/SalesChannel/AbstractRegisterRoute.php` | 抽象クラス | 登録ルートの抽象定義 |
| CustomerEntity.php | `src/Core/Checkout/Customer/CustomerEntity.php` | エンティティ | 顧客データモデル |
| CustomerAddressEntity.php | `src/Core/Checkout/Customer/Aggregate/CustomerAddress/CustomerAddressEntity.php` | エンティティ | 住所データモデル |
| CustomerRegisterEvent.php | `src/Core/Checkout/Customer/Event/CustomerRegisterEvent.php` | イベント | 会員登録イベント |
| GuestCustomerRegisterEvent.php | `src/Core/Checkout/Customer/Event/GuestCustomerRegisterEvent.php` | イベント | ゲスト登録イベント |
| CustomerDoubleOptInRegistrationEvent.php | `src/Core/Checkout/Customer/Event/CustomerDoubleOptInRegistrationEvent.php` | イベント | ダブルオプトイン登録イベント |
| CustomerEmailUnique.php | `src/Core/Checkout/Customer/Validation/Constraint/CustomerEmailUnique.php` | バリデーション | メール重複チェック |
| CustomerZipCode.php | `src/Core/Checkout/Customer/Validation/Constraint/CustomerZipCode.php` | バリデーション | 郵便番号バリデーション |
