Skip to content

📊 データベース設計ドキュメント

このドキュメントでは、ログ解析機能のDBスキーマと実装方針についてまとめてるよ!✨


🛠️ 技術スタック

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

📁 テーブル設計

1. matches - 試合テーブル

カラム名説明
idTEXT PRIMARY KEY試合ID(log 由来は timestamp ベース、browser 由来は BattleId と同値)
battle_idTEXT, NULL 許可, UNIQUE(NULL 重複可)戦績サイト由来の BattleId。log 由来 match では NULL から始まり、saveBattleDetails で照合成功時に書き戻される(spec: data-integration-improvement A1)
timestampTEXT試合開始日時(ISO8601)
dateTEXT日付(YYYY-MM-DD形式)
timeTEXT時刻(HH:MM:SS形式、後付けマイグレーション)
typeTEXT試合タイプ。log 由来は Rank_2v2 / Normal_1v1 等、browser 由来は mapBattleTypeId() 経由の 2v2 / 1v1 / Unknown(spec: data-integration-improvement A2)
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

2. 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

戦績詳細カラム(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

注記:

  • ログ解析データには戦績詳細情報(スコア、K/D/A等)が含まれないため、これらのカラムはNULL許容
  • ブラウザ自動化機能でAPI連携時に取得・更新される

3. bans - BANテーブル

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

4. picks - ピックテーブル

カラム名説明
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)。UI でバッジ表示。spec: A8
candidate_character_idsTEXT JSONfallback 時の候補キャラ ID 配列。UI で「候補: A, B, C」と表示。

5. log_files - ログファイル管理テーブル

カラム名説明
idINTEGER PRIMARY KEY自動採番ID
file_pathTEXT UNIQUEログファイルパス
file_nameTEXTファイル名
file_sizeINTEGERファイルサイズ(bytes)
match_countINTEGER検出した試合数
imported_atTEXTインポート日時
statusTEXTステータス('pending', 'imported', 'error')

🔌 IPC API設計

ログ解析・インポート系

typescript
// ログファイルを解析してDBに保存
db.analyzeAndSaveLog(filePath: string, myUid?: string): Promise<AnalysisResult>

// 解析済みログ一覧を取得
db.getLogFiles(): Promise<LogFileInfo[]>

// ログファイルを削除(関連試合データも削除)
db.deleteLogFile(fileId: number): Promise<void>

試合データ取得系

typescript
// 試合一覧を取得(フィルタ・ページング対応)
db.getMatches(options?: GetMatchesOptions): Promise<MatchData[]>

// 試合詳細を取得
db.getMatchDetail(matchId: string): Promise<MatchWithDetails | null>

統計データ取得系

typescript
// プレイヤー統計を取得
db.getPlayerStats(uid?: string): Promise<PlayerStats[]>

// キャラ統計を取得
db.getCharacterStats(type?: 'pick' | 'ban'): Promise<CharacterStats[]>

// ダッシュボードサマリーを取得
db.getDashboardSummary(uid?: string): Promise<DashboardSummary>

// 勝率推移を取得
db.getWinRateTrend(uid: string, days?: number): Promise<WinRateTrend[]>

📂 ファイル構造

src/
  components/
    control/
      LogAnalysisTab.tsx       # ログ解析タブUI(新規)
      LogFileList.tsx          # ログファイル一覧(新規)
      MatchList.tsx            # 試合一覧(新規)
      MatchDetail.tsx          # 試合詳細(新規)
      StatsDashboard.tsx       # 統計ダッシュボード(新規)
  utils/
    logAnalyzer/
      index.ts                 # フロント用ログ解析ユーティリティ
      types.ts                 # 共通型定義
      LogParser.ts             # ログパーサー(analyze-logから移植)
      StatisticsCalculator.ts  # 統計計算(analyze-logから移植)

electron/
  database.ts                  # SQLite DB操作(拡張)
  preload.ts                   # IPC公開(拡張)
  main.ts                      # IPCハンドラ登録(拡張)

🔄 処理フロー

ログインポートフロー

1. ユーザーがログファイルを選択
2. フロント: ファイル読み込み → LogParser で解析(プレビュー表示)
3. ユーザーが「インポート」ボタンを押す
4. フロント→IPC→メイン: 解析結果をDBに保存
5. DB: matches, players, bans, picks, log_files に INSERT
6. 完了通知をフロントに返す

統計表示フロー

1. ユーザーが「統計」タブを開く
2. フロント→IPC→メイン: getDashboardSummary(uid) を呼び出し
3. DB: 集計クエリを実行
4. 結果をフロントに返す → グラフ/テーブル表示

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

1. 試合終了を検出(リアルタイムログ監視 or 手動トリガー)
2. BrowserAutomationService: ブラウザを起動してログイン
3. 戦績ページ(battle/index)に遷移
4. battle_list API レスポンスをキャプチャ
5. 最新試合の battle_detail ページに遷移
   URL: /pages/record/vs[BattleType]-detail/index?BattleId=...&...
6. battle_detail API レスポンスをキャプチャ
7. DB: 既存の players レコードを UPDATE(戦績詳細カラムを埋める)
8. フロントエンドに通知 → UI更新

データソースの違い:

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

🎯 今後の拡張候補

ログ解析機能

  • [ ] キャラ別勝率の可視化(グラフ)
  • [ ] 特定プレイヤーとの対戦履歴
  • [ ] ログの自動監視・自動インポート
  • [ ] データエクスポート機能(CSV/JSON)

戦績詳細機能(API連携)

  • [x] playersテーブルに戦績詳細カラムを追加
  • [ ] K/D/A統計の計算・表示
  • [ ] スコア推移グラフ
  • [ ] 平均ダメージ・MVP率の表示
  • [ ] 試合終了後の自動データ取得

迷ったことあったら、あーしに聞いてね💖