# 画面設計書 46-購読者インポート

## 概要

本ドキュメントは、QuickerSite CMSのバックサイト（管理画面）における「購読者インポート」画面の設計書です。

### 本画面の処理概要

購読者（サブスクライバー）を一括でインポートするための画面です。CSVテキスト形式（名前とメールアドレスのペア、またはメールアドレスのみ）を入力し、選択したメーリングリスト（カテゴリ）に対して一括登録できます。また、任意のテキストやHTMLからメールアドレスを抽出する機能も備えています。

**業務上の目的・背景**：ニュースレターマーケティングでは、既存の顧客リストや他システムからの購読者データを効率的に取り込む必要があります。本画面は、大量の購読者データを一括登録することで、手作業での入力を排除し、データ移行やリスト構築を効率化します。既存の購読者（同一メールアドレス）は重複登録されないため、安全に何度でもインポートを実行できます。

**画面へのアクセス方法**：購読者一覧画面（bs_newsletterSubscribers.asp）の「Import Subscribers」ボタン、またはニュースレター一覧画面からアクセスします。customer.bCanImportSubscribersがtrueの場合のみアクセス可能です。falseの場合はニュースレター一覧画面にリダイレクトされます。

**主要な操作・処理内容**：
1. インポート形式の選択：メールアドレスのみ、または「名前,メール」形式を選択
2. データの入力：テキストエリアに改行区切りでデータを入力
3. メーリングリストの選択：インポート先のカテゴリを1つ以上選択
4. アクティブ/非アクティブの指定：インポート時の購読状態を指定
5. メールアドレス抽出：任意のテキストから有効なメールアドレスを抽出
6. インポート実行：データを検証しながら一括登録

**画面遷移**：
- 遷移元：購読者一覧画面（bs_newsletterSubscribers.asp）、ニュースレター一覧画面（bs_newsletterList.asp）
- 遷移先：購読者一覧画面（bs_newsletterSubscribers.asp）、ニュースレター一覧画面（bs_newsletterList.asp）

**権限による表示制御**：secondAdmin.bNewsletter権限がない場合はアクセス不可。customer.bCanImportSubscribersがfalseの場合は一覧画面にリダイレクト。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 70 | 購読者インポート | 主機能 | CSVファイルからの購読者一括インポート |

## 画面種別

登録（一括インポート）

## URL/ルーティング

```
/asp/bs_newsletterImport.asp
```

## 入出力項目

| 項目名 | 項目ID | 型 | 必須 | 入力/出力 | 説明 |
|--------|--------|-----|------|-----------|------|
| インポート形式 | importType | radio | - | 入力 | "1"=メールのみ、"0"=名前,メール形式 |
| データ | pairs | textarea | - | 入力 | 改行区切りのインポートデータ |
| カテゴリ | cat | checkbox | 必須 | 入力 | インポート先カテゴリ（複数選択可） |
| 非アクティブインポート | bimportinactive | checkbox | - | 入力 | チェック時は非アクティブでインポート |
| アクションボタン | btnaction | submit | - | 入力 | "Import" / "Extract valid email addresses" |

## 表示項目

| 項目名 | 表示位置 | 説明 |
|--------|---------|------|
| Subscribersボタン | 上部 | 購読者一覧画面へのリンク |
| Newsletter homeボタン | 上部 | ニュースレター一覧画面へのリンク |
| インポート形式ラジオボタン | 左列 | メールのみ / 名前,メール形式の選択 |
| データ入力テキストエリア | 左列 | 10行75列のテキストエリア |
| カテゴリチェックボックス | 右列 | 全カテゴリを表示 |
| 非アクティブインポートチェック | 右列 | 「Import as inactive」 |
| 必須項目説明 | 中央下 | 「(*) mandatory」の表示 |
| Extract valid email addressesボタン | 下部 | メールアドレス抽出ボタン |
| Importボタン | 下部 | インポート実行ボタン |
| インポート結果メッセージ | 上部 | 「X subscriptions were added」または「No subscriptions were added」 |

## イベント仕様

### 1-Importボタン押下

**処理フロー**：
1. カテゴリが選択されているか検証（未選択時はerr_mandatoryエラー）
2. bimportinactiveの値でactiveorinactive変数を設定
3. pairsテキストエリアの内容をvbcrlfで分割
4. 選択された各カテゴリについてループ
5. 各行についてループ
   - importTypeに応じて名前とメールを抽出
   - checkEmailSyntax()でメール形式検証
   - 既存チェック（同一カテゴリ・同一メール）
   - 存在しなければINSERT
6. sCounter変数でインポート件数をカウント
7. 結果メッセージを表示

### 2-Extract valid email addressesボタン押下

**処理フロー**：
1. pairsテキストエリアの内容をprivatebotクラスに渡す
2. pb.quickSearch()でメールアドレスを正規表現抽出
3. 抽出結果をテキストエリアに表示（元のテキストを置換）

## データベース更新仕様

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| インポート（既存チェック） | tblNewsletterCategorySubscriber | SELECT | 重複確認 |
| インポート（新規登録） | tblNewsletterCategorySubscriber | INSERT | 購読者登録 |

### テーブル別更新項目詳細

#### tblNewsletterCategorySubscriber

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | * | iCategoryID AND sEmail（重複チェック） | 存在確認 |
| INSERT | sName | 入力データまたは空文字 | 名前 |
| INSERT | sEmail | 入力メールアドレス（小文字化） | メール |
| INSERT | sKey | generatePassword * 3 | 購読解除キー |
| INSERT | iCategoryID | 選択されたカテゴリID | カテゴリ |
| INSERT | iCustomerID | cId | 顧客ID |
| INSERT | bActive | activeorinactive変数 | アクティブ状態 |
| INSERT | dAdded | date() | 登録日 |

## メッセージ仕様

| メッセージID | 種別 | 表示条件 | メッセージ内容 |
|-------------|------|---------|--------------|
| err_mandatory | エラー | カテゴリ未選択時 | 必須項目エラー |
| (テキスト) | 成功 | インポート成功時 | "X subscriptions were added" |
| (テキスト) | 情報 | 0件インポート時 | "No subscriptions were added - no new name-email pairs were found" |

## 例外処理

| 例外条件 | 処理内容 |
|---------|---------|
| アクセス権限なし | secondAdmin.bNewsletter権限チェックに失敗した場合、エラー表示 |
| bCanImportSubscribersがfalse | ニュースレター一覧画面にリダイレクト |
| カテゴリ未選択 | err_mandatoryエラーメッセージ表示 |
| メール形式不正 | checkEmailSyntax()失敗時、その行をスキップ |
| 既存メール | 重複チェックでヒット時、その行をスキップ |
| 処理中エラー | on error resume nextで継続処理 |

## 備考

- カテゴリが1つしかない場合は自動的にチェックされる
- 重複チェックは同一カテゴリ内のみ（別カテゴリへの登録は可能）
- sKeyは24文字のランダム文字列（generatePassword * 3）
- メールアドレスは小文字に正規化される
- 「Extract valid email addresses」機能はpb.aspのprivatebotクラスを使用
- on error resume nextにより、個別の行でエラーが発生しても処理は継続

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | newsletterCategory.asp | `asp/includes/newsletterCategory.asp` | cls_newsletterCategoryクラス、購読者登録時のカラム構成を理解 |
| 1-2 | pb.asp | `asp/includes/pb.asp` | privatebotクラス、quickSearch()によるメールアドレス抽出処理 |

**読解のコツ**: importType変数により、入力データの解析方法が変わることに注意。"1"はメールのみ、"0"はカンマ区切りの名前,メール形式。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | bs_newsletterImport.asp | `asp/bs_newsletterImport.asp` | 画面表示のメインファイル。インポート処理のネストしたループ構造を確認 |

**主要処理フロー**:
1. **4行目**: 権限・bCanImportSubscribersチェック
2. **13-65行目**: インポート処理
   - **13行目**: btnaction="Import"判定
   - **15-17行目**: カテゴリ必須チェック
   - **20-24行目**: アクティブ/非アクティブ設定
   - **25行目**: split(pairs, vbcrlf)でデータ分割
   - **27-64行目**: カテゴリ×データ行の二重ループ
     - **31-37行目**: importTypeに応じたデータ抽出
     - **40-41行目**: メール形式検証と重複チェック
     - **43-56行目**: INSERT処理
3. **67-72行目**: メールアドレス抽出処理（Extract）

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

```
bs_newsletterImport.asp（画面表示）
    │
    ├─ begin.asp（初期化）
    │
    ├─ bs_security.asp（認証チェック）
    │      └─ logon.hasaccess(secondAdmin.bNewsletter)
    │
    ├─ pb.asp（メールアドレス抽出）
    │      └─ privatebot.quickSearch()
    │
    ├─ customer.NewsletterCategories
    │      └─ for each NCループでチェックボックス生成
    │
    ├─ インポート処理
    │      ├─ split(pairs, vbcrlf)
    │      │
    │      └─ for each iCat（カテゴリループ）
    │             └─ for ai=lbound to ubound（行ループ）
    │                    ├─ split(arrPairs(ai), ",")
    │                    ├─ checkEmailSyntax(sEmail)
    │                    ├─ SELECT重複チェック
    │                    └─ INSERT（db.getDynamicRS, AddNew）
    │
    └─ includes/footer.asp
```

### データフロー図

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

pairsテキストエリア ──────▶ split(vbcrlf) ──────────────────▶ arrPairs配列
                                │
                                ├─ importType="1"
                                │      └─ sEmail = arrPair(0)
                                │
                                └─ importType="0"
                                       ├─ sName = arrPair(0)
                                       └─ sEmail = arrPair(1)

sEmail ───────────────────▶ checkEmailSyntax() ─────────────▶ 検証結果
                                │
                                └─ SELECT重複チェック
                                       │
                                       └─ rs.eof=true
                                              │
                                              └─ INSERT ─────▶ tblNewsletterCategorySubscriber
                                                    ├─ sName
                                                    ├─ sEmail
                                                    ├─ sKey (generatePassword*3)
                                                    ├─ iCategoryID
                                                    ├─ iCustomerID
                                                    ├─ bActive
                                                    └─ dAdded

pairsテキスト ────────────▶ privatebot.quickSearch() ───────▶ 抽出メールリスト
（Extract実行時）
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| bs_newsletterImport.asp | `asp/bs_newsletterImport.asp` | ソース | 購読者インポート画面 |
| bs_newsletterSubscribers.asp | `asp/bs_newsletterSubscribers.asp` | ソース | 購読者一覧画面 |
| bs_newsletterList.asp | `asp/bs_newsletterList.asp` | ソース | ニュースレター一覧画面 |
| newsletterCategory.asp | `asp/includes/newsletterCategory.asp` | ソース | カテゴリクラス定義 |
| pb.asp | `asp/includes/pb.asp` | ソース | privatebotクラス（メール抽出） |
| customer.asp | `asp/includes/customer.asp` | ソース | 顧客クラス（NewsletterCategories） |
| bs_security.asp | `asp/bs_security.asp` | ソース | 認証・認可処理 |
| begin.asp | `asp/begin.asp` | ソース | 共通初期化処理 |
| header.asp | `asp/includes/header.asp` | テンプレート | 共通ヘッダー |
| footer.asp | `asp/includes/footer.asp` | テンプレート | 共通フッター |
