ブラウザ自動化 取得データ・DB保存設計
最終更新: 2026-02-25
1. キャプチャ対象レスポンス
BrowserAutomationService は、起動中のページで発生する xhr / fetch レスポンスのうち以下の条件を満たすものを取得します。
- HTTP ステータスが
200 - リソースタイプが
xhrまたはfetch - URL に以下のいずれかのキーワードを含む(
CONFIG.targetApiKeywords)
| キーワード | エンドポイント概要 |
|---|---|
battle_list | 戦績一覧 |
battle_detail | 試合詳細 |
battle_seasonsummary | シーズンサマリー統計 |
season_show | シーズン詳細データ(ScoreMap含む) |
login | 認証レスポンス |
キャプチャデータ構造
interface FetchResponseData {
url: string; // リクエストURL
status: number; // HTTPステータス
body: string; // レスポンス本文(text)
timestamp: string; // 取得時刻(ISO 8601)
contentType: string; // Content-Type
}2. ワークフロー別の取得フロー
2.1 試合終了ワークフロー(executeMatchEndWorkflow)
試合完了を検知した際に自動実行。
取得対象: battle_list → battle_detail
battle_list 取得
↓ BattleId を抽出
battle_detail 取得(BattleId 単位)
↓
DB 保存(saveBattleListItems + saveBattleDetails)2.2 シーズン付き一覧取得(fetchBattleListWithSeason)
取得対象: battle_list + battle_seasonsummary + season_show
battle_list 取得
battle_seasonsummary 取得 ← 並行
season_show 取得 ← 並行
↓
DB 保存(saveBattleListItems + saveSeasonSummary + saveSeasonShow)2.3 シーズンのみ取得(fetchSeasonOnly)
取得対象: battle_seasonsummary + season_show
battle_seasonsummary 取得
season_show 取得
↓
DB 保存(saveSeasonSummary + saveSeasonShow)2.4 全試合取得(executeFullWorkflow)
取得対象: battle_list + battle_detail(全件)+ battle_seasonsummary + season_show
3. API レスポンスの DB 保存設計
3.1 api_responses テーブル(生レスポンス保存)
全キャプチャレスポンスを saveApiResponse() で保存。
スキーマ:
CREATE TABLE IF NOT EXISTS api_responses (
id INTEGER PRIMARY KEY AUTOINCREMENT,
url TEXT NOT NULL,
status INTEGER NOT NULL,
content_type TEXT NOT NULL,
timestamp TEXT NOT NULL,
endpoint TEXT, -- URLの最後のパスセグメント(例: "battle_list")
body TEXT NOT NULL, -- レスポンス本文(生JSONテキスト)
is_json INTEGER NOT NULL DEFAULT 0,
json_code INTEGER, -- レスポンスの .code フィールド
json_msg TEXT, -- レスポンスの .msg フィールド
created_at TEXT DEFAULT CURRENT_TIMESTAMP
);取得 API:
db.getApiResponses({
endpoint: 'battle_list', // エンドポイントで絞り込み
status: 200, // ステータスで絞り込み
uids: ['1046101022'], // URLまたはbody内のUIDで絞り込み
limit: 200, // 件数制限(デフォルト200)
})3.2 season_summaries テーブル
battle_seasonsummary のレスポンスをパースして保存。
スキーマ:
CREATE TABLE IF NOT EXISTS season_summaries (
id INTEGER PRIMARY KEY AUTOINCREMENT,
captured_at TEXT NOT NULL, -- 取得時刻(ISO 8601)
captured_date TEXT NOT NULL, -- 日付(YYYY-MM-DD)← ユニークキー
t1_arena_score INTEGER,
t1_grade_dan_id INTEGER,
t1_mvp_cnt INTEGER,
t1_total_cnt INTEGER,
t1_win_rate TEXT,
t2_arena_score INTEGER,
t2_grade_dan_id INTEGER,
t2_mvp_cnt INTEGER,
t2_total_cnt INTEGER,
t2_win_rate TEXT,
raw_json TEXT
);
CREATE INDEX IF NOT EXISTS idx_season_summaries_date ON season_summaries(captured_date);保存ロジック(saveSeasonSummary):
| 条件 | 動作 |
|---|---|
| 同日レコードが存在しない | INSERT |
| 同日レコードが存在する | UPDATE(全フィールドを最新値で上書き) |
APIレスポンスからのマッピング:
| DBカラム | JSONパス |
|---|---|
t1_arena_score | data.T1.ArenaScore |
t1_grade_dan_id | data.T1.GradeDanId |
t1_mvp_cnt | data.T1.MvpCnt |
t1_total_cnt | data.T1.TotalCnt |
t1_win_rate | data.T1.WinRate |
t2_* | data.T2.*(同上) |
3.3 season_shows テーブル
season_show のレスポンスをパースして保存。ScoreMap・TopRoleList を含む詳細データ。
スキーマ:
CREATE TABLE IF NOT EXISTS season_shows (
id INTEGER PRIMARY KEY AUTOINCREMENT,
captured_at TEXT NOT NULL, -- 取得時刻(ISO 8601)
captured_date TEXT NOT NULL, -- 日付(YYYY-MM-DD)← ユニークキー
t1_max_score INTEGER,
t1_arena_score INTEGER,
t1_total_cnt INTEGER,
t1_win_cnt INTEGER,
t1_win_rate TEXT,
t1_score_map TEXT, -- JSON文字列 { "YYYYMMDD": score, ... }
t2_max_score INTEGER,
t2_arena_score INTEGER,
t2_total_cnt INTEGER,
t2_win_cnt INTEGER,
t2_win_rate TEXT,
t2_score_map TEXT, -- JSON文字列 { "YYYYMMDD": score, ... }
top_role_list TEXT, -- JSON文字列(TopRoleListの配列)
raw_json TEXT
);
CREATE INDEX IF NOT EXISTS idx_season_shows_date ON season_shows(captured_date);保存ロジック(saveSeasonShow):
| 条件 | 動作 |
|---|---|
| 同日レコードが存在しない | INSERT |
| 同日レコードが存在する | UPDATE(全フィールドを最新値で上書き) |
APIレスポンスからのマッピング:
| DBカラム | JSONパス |
|---|---|
t1_max_score | data.T1.MaxScore |
t1_arena_score | data.T1.ArenaScore |
t1_total_cnt | data.T1.TotalCnt |
t1_win_cnt | data.T1.WinCnt |
t1_win_rate | data.T1.WinRate |
t1_score_map | JSON.stringify(data.T1.ScoreMap) |
t2_* | data.T2.*(同上) |
top_role_list | JSON.stringify(data.TopRoleList) |
ScoreMap のデータ形式:
{
"20260101": 5200,
"20260102": 5314,
"20260103": 5489
}キー: YYYYMMDD(8桁日付文字列)、値: その日のスコア(integer)
TopRoleList の要素形式:
[
{
"TotalCnt": 42,
"WinCnt": 25,
"WinRate": "0.5952",
"Role": {
"english_name": "haruka",
"name_jp": "ハルカ"
}
}
]WinRate は 0.0 〜 1.0 の小数文字列。画面表示時は parseFloat * 100 で % 変換。
4. テーブル間の関係
api_responses ← 全レスポンスの生テキストを保持(監査・デバッグ用)
season_summaries ← battle_seasonsummary を正規化して保持(1日1件)
season_shows ← season_show を正規化して保持(1日1件)season_summaries / season_shows は api_responses のサブセットをパースして格納した派生テーブル。raw_json に元の全レスポンスも保持しているため、カラム追加なしに後からデータを参照可能。
5. UI での表示(SeasonAnalyticsPanel)
season_shows と season_summaries のデータは PlayerDashboardTab の シーズン解析 パネルに表示される。
| UI要素 | データソース |
|---|---|
| 日別スコア推移チャート | t1_score_map / t2_score_map を JSON.parse |
| 勝率推移チャート | season_summaries.t1_win_rate の時系列 |
| スコア推移(Arena/Max) | season_summaries.t1_arena_score + season_shows.t1_max_score |
| 最新サマリーカード | season_shows(最新1件)+ season_summaries(最新1件) |
| キャラ別勝率横棒グラフ | top_role_list を JSON.parse(TotalCnt 降順) |
6. レスポンスファイル保存(ResponseStorage)
レスポンス本文をファイル保存する場合(任意機能)、メタデータとして以下を保持:
| フィールド | 説明 |
|---|---|
id | 連番 |
url | リクエストURL |
timestamp | 取得時刻(ISO 8601) |
fileName | 保存ファイル名 |
fileSize | ファイルサイズ(bytes) |
saveReason | auto / manual |
memo | 任意メモ |
apiEndpoint | URLから推定したエンドポイント名 |
7. ログイン状態テスト(未ログイン / 別アカウント)
7.1 セッション保存の仕様
- ブラウザ自動化のログインセッションは
storageState.jsonで永続化される - 保存先は
app.getPath('userData')/browser-automation/storageState.json - Windows 例:
C:\Users\<ユーザー名>\AppData\Roaming\streaming-overlay-manager\browser-automation\storageState.json
- アプリ内の「プロファイル」(UID/プレイヤー名/ログDir)は、ブラウザログインセッションとは別管理
7.2 未ログイン状態の再現手順
- ブラウザ自動化を停止する
storageState.jsonを削除または退避する- リアルタイム解析タブでブラウザ自動化を起動する
- ゲームサイトにログインせず、ログイン状態バナーを確認する
期待結果:
⚠️ ログインが確認できませんでした。...が表示される
7.3 別アカウントでのテスト手順
- ブラウザ自動化を停止する
storageState.jsonを削除して既存セッションを初期化する- ブラウザ自動化を起動し、テスト対象の別アカウントでログインする
- ログイン状態バナーとワークフロー(テスト実行 / 全試合取得)を確認する
期待結果:
✅ ログイン済みです。が表示される- その後のAPI取得ワークフローが通常どおり実行できる