Skip to content

Database Design ​

WingStats Manager の SQLite スキーマと、electron/database/ 配下に分割された repository パターン の構成。

技術スタック ​

項目選定
DBSQLite (better-sqlite3)
ORMなし(生 SQL + prepared statements)
配置場所Electron メインプロセス (electron/database/)
アクセスIPC 経由で renderer から呼び出し

Repository 構成 ​

code-split-and-coverage spec で electron/database.ts (旧 868 行) を repository に分割し、その後の機能追加で増えて、今は Repository 8 つと StatsService。electron/database.ts は薄い facade(MatchDatabase)として残り、内部で各 repository を組み立てる。

electron/database/
├── DatabaseConnection.ts           # 接続管理 + マイグレーション
├── MatchRepository.ts              # matches / players / bans / picks
├── ProfileRepository.ts            # profiles
├── StatsService.ts                 # 集計の facade(stats/ に委譲)
├── BattleListRepository.ts         # browser 自動化由来の battle_list を matches に保存
├── SeasonRepository.ts             # シーズン情報 (season_summaries / season_shows)
├── ApiResponseRepository.ts        # API レスポンスの生キャッシュ
├── MilestoneRepository.ts          # 記念スナップショット (milestone_snapshots)
├── CharacterStatsRepository.ts     # 所持キャラ戦績スナップショット
├── SeasonFetchStateRepository.ts   # 全シーズン取得の進捗 (season_fetch_state)
├── matchesSavedListeners.ts        # 試合保存後に呼ぶ聞き手
├── match/                          # 試合詳細・起動時取り込み・移行などの純関数
├── stats/                          # StatsCalculator / ApiResponseWriter / MatchReconciler / 対戦・詳細クエリ
└── types.ts

Repository とテーブルの対応 ​

Repository主な責務扱うテーブル
DatabaseConnection接続 + マイグレーション + トランザクション(全)
MatchRepository試合の保存 / 取得 / 削除matches, players, bans, picks
ProfileRepositoryプロファイル管理profiles
StatsService統計集計と、API 詳細の書き戻し・ログ/API 試合の突き合わせ(stats/ に委譲)matches, players, bans, picks
BattleListRepositorybrowser 自動化の battle_list を matches / players に保存。API だけの重複行は子テーブルごと消すmatches, players(削除時は bans, picks も)
SeasonRepositoryシーズン管理season_summaries, season_shows
ApiResponseRepositoryAPI レスポンスの生キャッシュapi_responses
MilestoneRepository記念スナップショット(節目ごとに 1 行追記)milestone_snapshots
CharacterStatsRepository所持キャラ戦績スナップショット(追記のみ)character_stat_snapshots, character_stat_entries, character_ownership_entries
SeasonFetchStateRepository全シーズン取得の進捗(role_id × season_code)season_fetch_state

純関数クエリ層 ​

electron/database/match/matchDetailQueries.ts は Database インスタンスを引数に取る純関数群 (fetchMatchPlayers / fetchMatchBans / fetchMatchPicks / fetchMatchDetail)。MatchRepository から利用する。テストでは in-memory better-sqlite3 を渡せる。

テーブル設計 ​

matches - 試合テーブル ​

カラム名型説明
idTEXT PRIMARY KEY試合 ID(log 由来は timestamp ベース、browser 由来は BattleId と同値)
battle_idTEXT, NULL 許可, UNIQUE戦績サイト由来の BattleId。log 由来は saveBattleDetails の照合成功時に書き戻し
timestampTEXT試合開始日時(ISO8601)
dateTEXT日付(YYYY-MM-DD)
timeTEXT時刻(HH:MM:SS、後付けマイグレーション)
typeTEXT試合タイプ(log 由来: Rank_2v2 等 / browser 由来: mapBattleTypeId() 経由)
durationINTEGER試合時間(秒)
log_fileTEXT元ログファイルパス
created_atTEXTDB 登録日時

照合フォールバック階段(BrowserAutomationService が battle_detail を保存する経路):

  1. findMatchByBattleId(BattleId) — matches.battle_id 直引き(決定的)
  2. findMatchByOpponentUids(opponents, StartTime) — 対戦相手 UID 集合一致 + 最近接時刻
  3. miss なら saveBattleDetails(matchId 未指定) — 時刻 ±5 分窓 + UID 一致による UPDATE

players - プレイヤー結果テーブル ​

基本カラム ​

カラム名型説明
idINTEGER PRIMARY KEY自動採番 ID
match_idTEXT試合 ID(FK: matches.id)
uidTEXTプレイヤー UID
nameTEXTプレイヤー名
teamTEXTチーム('A' or 'B')
resultTEXT結果('Win' or 'Lose')
character_idINTEGER使用キャラ ID
available_character_idsTEXT (JSON 配列)所持キャラ 4 枠(spec: available-chars)

戦績詳細カラム(API 連携時に取得) ​

カラム名型説明対応する JSON
kill_countINTEGER撃破数BattleInfo.BeatCnt
death_countINTEGER被撃破数BattleInfo.BeatedCnt
damageINTEGER与ダメージBattleInfo.ExportDamage
score_beforeINTEGER試合前スコアUserInfo.ScoreBefore
score_afterINTEGER試合後スコアUserInfo.ScoreAfter
is_mvpINTEGERMVP 判定(0/1)UserInfo.IsMvp
rank_idINTEGER段位 IDUserInfo.DaDuanId
awake_kill_countINTEGER覚醒撃破数BattleInfo.BeatAwakeCnt
friendly_kill_countINTEGER味方撃破数BattleInfo.BeatFreidnCnt
debuff_clear_countINTEGERデバフ解除数BattleInfo.Debarrass
avatar_urlTEXTアバター URLUserInfo.Avatar

ログ解析データには戦績詳細が含まれないため、これらは NULL 許容。BrowserAutomationService の API 連携時に UPDATE される。

bans - BAN テーブル ​

カラム名型説明
idINTEGER PRIMARY KEY自動採番 ID
match_idTEXT試合 ID(FK: matches.id)
character_idINTEGERBAN されたキャラ ID
banner_uidTEXTBAN したプレイヤー UID
banner_nameTEXTBAN したプレイヤー名
teamTEXTチーム('A' or 'B')

picks - PICK テーブル ​

カラム名型説明
idINTEGER PRIMARY KEY自動採番 ID
match_idTEXT試合 ID(FK: matches.id)
character_idINTEGERピックされたキャラ ID
player_uidTEXTプレイヤー UID
player_nameTEXTプレイヤー名
teamTEXTチーム('A' or 'B')
is_candidate_fallbackINTEGER候補絞り込み fallback で決まった Pick かどうか(0/1)
candidate_character_idsTEXT (JSON 配列)fallback 時の候補キャラ ID 配列

命名規約: snake_case ↔ camelCase ​

DB 列名は snake_case、TypeScript 側は camelCase。SELECT 時に AS 句で変換する。

sql
SELECT character_id AS characterId, player_uid AS playerUid FROM picks

これを怠ると、UI 側で「データなし」表示になる典型バグが発生する。

IPC API ​

renderer (window.electronAPI.db.*) から呼ぶ主要メソッド。詳細は Electron Main と electron/preload/api/db.ts を参照。

ts
db.startupImportPreview()           // 監視フォルダの未取り込みログをプレビュー
db.startupImportRun()                // プレビュー分を解析して DB に保存
db.getMatches(options?)
db.getMatchDetail(matchId)
db.saveMatchesSkipExisting(matches)  // 解析済み試合をまとめて保存(既存 ID はスキップ)
db.getCharacterStatsByUid(uid)
db.getOpponentAvailableCharacterStats(uid)
db.getWinRateTrend(uid, days?)
db.deleteMatch(matchId)
db.reconcileLogApiMatches(options?)  // log 由来 / API 由来の試合を照合

処理フロー ​

ログインポートフロー ​

戦績詳細取得フロー(ブラウザ自動化) ​

データソースの違い:

  • ログ解析: 試合基本情報、チーム構成、BAN/PICK、勝敗のみ
  • API 連携: 上記 + K/D/A、ダメージ、スコア変動、MVP 等の詳細戦績

ファイル構造 ​

electron/
├── database.ts                 # facade(MatchDatabase)
├── database/                   # 中身は「Repository 構成」の図を参照
│   ├── DatabaseConnection.ts
│   ├── *Repository.ts          # Repository 8 つ
│   ├── StatsService.ts
│   ├── matchesSavedListeners.ts
│   ├── match/
│   ├── stats/
│   ├── types.ts
│   └── __tests__/
└── preload/api/db.ts           # 公開 API

テスト戦略 ​

  • In-memory better-sqlite3: repository テストは :memory: で接続を作り、各テストで fresh schema を立てる
  • 純関数クエリ層 (matchDetailQueries.ts) は DB インスタンスを DI で受けるため、in-memory DB を直接渡せる
  • 既存テスト: electron/database/__tests__/, electron/database/match/__tests__/

今後の拡張候補 ​

  • [ ] K/D/A 統計の計算・表示
  • [ ] スコア推移グラフ
  • [ ] 平均ダメージ・MVP 率の表示
  • [ ] データエクスポート機能(CSV / JSON)
  • [ ] log の自動監視・自動インポート(spec: realtime-log-monitoring)

Last verified: 2026-06-01 / commit 12bb761f