# 帳票設計書 4-Usage Report (Web UI)

## 概要

本ドキュメントは、opencode Web Console上のワークスペース利用状況テーブルの設計書である。

### 本帳票の処理概要

本帳票は、ワークスペースにおけるAI利用状況（日時、モデル、トークン使用量、コスト、セッションID）をページネーション付きテーブル形式で表示するWebコンポーネントを提供する。

**業務上の目的・背景**：クラウドベースのAIサービス利用状況を可視化し、コスト管理と利用分析を可能にするために必要である。管理者やユーザーがワークスペース単位でのリソース消費を把握し、予算管理や利用最適化の判断材料とすることができる。サブスクリプションとBYOK（Bring Your Own Key）の課金形態の違いも明確に表示される。

**帳票の利用シーン**：Web Consoleにログインしてワークスペースの利用状況を確認したい場合、特定期間のコスト詳細を分析したい場合、各セッションの使用量を追跡したい場合に利用される。経理部門での請求確認や、開発チームでの利用状況モニタリングに特に有用である。

**主要な出力内容**：
1. 利用日時（ローカルタイムゾーン表示）
2. 使用モデル名
3. 入力トークン数（詳細ブレイクダウン：入力、キャッシュ読み取り、キャッシュ書き込み）
4. 出力トークン数（詳細ブレイクダウン：出力、推論）
5. コスト（サブスクリプション/BYOK表示）
6. セッションID（末尾8文字）

**帳票の出力タイミング**：ワークスペースページ（`/workspace/[id]`）にアクセスした時点で自動的に表示される。ページネーションで追加データを取得可能。

**帳票の利用者**：ワークスペース管理者、開発者、経理担当者

## 帳票種別

利用明細 / 一覧表

## 利用画面

| 画面No | 画面名 | URL/ルーティング | 出力操作 |
|--------|--------|-----------------|---------|
| WS-001 | ワークスペース詳細 | `/workspace/[id]` | ページアクセス時自動表示 |

## 出力形式

### 基本仕様

| 項目 | 内容 |
|-----|------|
| ファイル形式 | HTML（Webテーブル） |
| 用紙サイズ | 該当なし |
| 向き | 該当なし |
| ファイル名 | 該当なし |
| 出力方法 | ブラウザ表示 |
| 文字コード | UTF-8 |

### ページネーション

| 項目 | 内容 |
|-----|------|
| 1ページあたり件数 | 50件（PAGE_SIZE定数） |
| ページ移動 | 前へ/次へボタン |
| 現在ページ表示 | なし（内部state管理） |

## 帳票レイアウト

### レイアウト概要

テーブル形式で利用履歴を一覧表示する。

```
┌─────────────────────────────────────────────────────────────────┐
│  Usage                                                          │
│  View your workspace usage.                                     │
├─────────────────────────────────────────────────────────────────┤
│ Date        │ Model    │ Input  │ Output │ Cost     │ Session  │
├─────────────┼──────────┼────────┼────────┼──────────┼──────────┤
│ Jan 1, 10AM │ claude.. │ 12345  │ 6789   │ $0.0123  │ abc12345 │
│ Jan 1, 9AM  │ gpt-5..  │ 9876   │ 5432   │ Sub $0.. │ def67890 │
│    ...      │   ...    │  ...   │  ...   │   ...    │   ...    │
├─────────────────────────────────────────────────────────────────┤
│              [<]                                     [>]         │
└─────────────────────────────────────────────────────────────────┘
```

### テーブルヘッダー

| No | 項目名 | データキー | 説明 |
|----|-------|----------|------|
| 1 | Date | timeCreated | 利用日時 |
| 2 | Model | model | 使用AIモデル |
| 3 | Input | inputTokens + cacheTokens | 入力トークン数（合計） |
| 4 | Output | outputTokens + reasoningTokens | 出力トークン数（合計） |
| 5 | Cost | cost | 利用コスト |
| 6 | Session | sessionID | セッション識別子 |

### テーブルボディ

| No | 項目名 | 説明 | データ取得元 | 表示形式 |
|----|-------|------|-------------|---------|
| 1 | Date | 利用日時 | usage.timeCreated | `formatDateForTable()` (例: Jan 1, 10:30 AM) |
| 2 | Model | モデル名 | usage.model | そのまま表示 |
| 3 | Input | 入力トークン合計 | 計算値 | 数値 + ブレイクダウンボタン |
| 4 | Output | 出力トークン合計 | 計算値 | 数値 + ブレイクダウンボタン |
| 5 | Cost | コスト | usage.cost | $x.xxxx形式（課金形態表示付き） |
| 6 | Session | セッションID | usage.sessionID | 末尾8文字（例: abc12345） |

### 入力トークンブレイクダウン（ポップアップ）

| No | 項目名 | 説明 | データ取得元 | 表示形式 |
|----|-------|------|-------------|---------|
| 1 | Input | 純粋入力トークン | usage.inputTokens | 数値 |
| 2 | Cache Read | キャッシュ読み取り | usage.cacheReadTokens | 数値（null時0） |
| 3 | Cache Write | キャッシュ書き込み（Claude限定） | usage.cacheWrite5mTokens | 数値（null時0） |

### 出力トークンブレイクダウン（ポップアップ）

| No | 項目名 | 説明 | データ取得元 | 表示形式 |
|----|-------|------|-------------|---------|
| 1 | Output | 純粋出力トークン | usage.outputTokens | 数値 |
| 2 | Reasoning | 推論トークン | usage.reasoningTokens | 数値（null時0） |

## 出力条件

### 抽出条件

| 条件名 | 説明 | 必須 |
|-------|------|-----|
| workspaceID | ワークスペースID | Yes |
| page | ページ番号（0始まり） | Yes |

### ソート順

| 優先度 | 項目 | 昇順/降順 |
|-------|------|---------|
| 1 | timeCreated | 降順（最新順、サーバー側で設定） |

### ページネーション条件

| 条件 | 動作 |
|-----|------|
| page === 0 | 「前へ」ボタン無効 |
| usage.length < PAGE_SIZE | 「次へ」ボタン無効 |

## データベース参照仕様

### 参照テーブル一覧

| テーブル名 | 用途 | 結合条件 |
|-----------|------|---------|
| usage | 利用履歴取得 | workspaceID = 指定ID |

### テーブル別参照項目詳細

#### usage

| 参照項目（カラム名） | 帳票項目との対応 | 取得条件 | 備考 |
|-------------------|----------------|---------|------|
| timeCreated | Date | workspaceID = 指定ID | - |
| model | Model | - | - |
| inputTokens | Input計算用 | - | - |
| cacheReadTokens | Input計算用 | - | null許容 |
| cacheWrite5mTokens | Input計算用（Claude） | - | null許容 |
| cacheWrite1hTokens | Input計算用 | - | null許容 |
| outputTokens | Output計算用 | - | - |
| reasoningTokens | Output計算用 | - | null許容 |
| cost | Cost | - | 1/100,000,000単位 |
| sessionID | Session | - | null許容 |
| enrichment.plan | 課金形態判定 | - | "sub" / "byok" / null |

## 計算仕様

### 計算項目一覧

| 項目名 | 計算式 | 端数処理 | 備考 |
|-------|-------|---------|------|
| totalInputTokens | inputTokens + (cacheReadTokens ?? 0) + (cacheWrite5mTokens ?? 0) + (cacheWrite1hTokens ?? 0) | なし | 入力トークン合計 |
| totalOutputTokens | outputTokens + (reasoningTokens ?? 0) | なし | 出力トークン合計 |
| displayCost | (cost ?? 0) / 100000000 | toFixed(4) | ドル表示用 |

## 処理フロー

### 出力フロー

```mermaid
flowchart TD
    A[コンポーネントマウント] --> B[createAsync: queryUsageInfo呼び出し]
    B --> C[サーバー側: Billing.usages取得]
    C --> D[createEffect: storeにusageセット]
    D --> E{hasResults?}
    E -->|Yes| F[テーブル表示]
    E -->|No| G[空状態表示]
    F --> H[各行レンダリング]
    H --> I{ブレイクダウンボタンクリック?}
    I -->|Yes| J[ポップアップ表示]
    I -->|No| K[待機]
    J --> K
    K --> L{ページネーションクリック?}
    L -->|前へ| M[goPrev: page-1でデータ取得]
    L -->|次へ| N[goNext: page+1でデータ取得]
    L -->|No| O[待機]
    M --> D
    N --> D
```

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 表示メッセージ | 対処方法 |
|----------|---------|--------------|---------|
| データなし | usage配列が空 | "No usage data yet." (i18n) | 空状態UIを表示 |
| 認証エラー | withActorで認証失敗 | - | ログイン画面へリダイレクト |

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定データ件数 | 1ページ50件 |
| 目標出力時間 | 1秒以内 |
| 同時アクセス数 | サーバー設定依存 |

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

- withActorによるワークスペースアクセス権限確認
- セッションIDは末尾8文字のみ表示（完全IDは非表示）
- コスト情報はワークスペースメンバーのみ閲覧可能

## 備考

- Claudeモデル使用時のみキャッシュ書き込み（5分TTL）が表示される
- 日付表示はUTCではなくローカルタイムゾーンで表示
- ブレイクダウンポップアップは外側クリックで閉じる
- i18nによる多言語対応（日本語/英語等）

---

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

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

### 推奨読解順序

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

まず、APIから取得されるデータ構造を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | usage-section.tsx | `packages/console/app/src/routes/workspace/[id]/usage-section.tsx` | getUsageInfo関数（13-18行）の戻り値型を確認 |

**読解のコツ**: SolidJSのcreateAsync/queryパターンを理解し、サーバーサイドデータ取得の仕組みを把握する。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | usage-section.tsx | `packages/console/app/src/routes/workspace/[id]/usage-section.tsx` | UsageSection関数コンポーネント（22-217行） |

**主要処理フロー**:
1. **23-31行**: 初期化（params, i18n, createAsync, createStore）
2. **47-49行**: ページネーション状態（hasResults, canGoPrev, canGoNext）
3. **51-57行**: トークン計算関数（calculateTotalInputTokens, calculateTotalOutputTokens）
4. **74-216行**: JSXレンダリング

#### Step 3: 計算ロジックを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | usage-section.tsx | `packages/console/app/src/routes/workspace/[id]/usage-section.tsx` | calculateTotalInputTokens（51-53行）、calculateTotalOutputTokens（55-57行） |

**主要処理フロー**:
- **51-53行**: 入力トークン合計 = inputTokens + cacheReadTokens + cacheWrite5mTokens + cacheWrite1hTokens
- **55-57行**: 出力トークン合計 = outputTokens + reasoningTokens

#### Step 4: UIパーツを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | usage-section.tsx | `packages/console/app/src/routes/workspace/[id]/usage-section.tsx` | ブレイクダウンポップアップ（117-148行, 151-175行）、コスト表示（177-195行） |

**主要処理フロー**:
- **177-195行**: 課金形態による表示分岐（sub/byok/通常）
- **139-146行**: Claudeモデル判定によるキャッシュ書き込み表示

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

```
UsageSection (usage-section.tsx:22)
    │
    ├─ createAsync(queryUsageInfo) - 初回データ取得
    │      │
    │      └─ getUsageInfo(workspaceID, page) - サーバー関数
    │             └─ withActor() - 認証ラッパー
    │                    └─ Billing.usages(page, PAGE_SIZE)
    │
    ├─ createEffect() - データ更新時のstore反映
    │
    ├─ calculateTotalInputTokens(usage) - 入力トークン計算
    │
    ├─ calculateTotalOutputTokens(usage) - 出力トークン計算
    │
    ├─ goPrev() / goNext() - ページネーション
    │      └─ getUsageInfo(workspaceID, page±1)
    │
    └─ formatDateForTable() / formatDateUTC() - 日付フォーマット
           (common.tsx)
```

### データフロー図

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

workspaceID ──────────▶ getUsageInfo()
    │                        │
page (0始まり) ──────────▶   │
                            ▼
                    Billing.usages() ───────▶ usage[]
                            │
                            ▼
                    createStore({page, usage})
                            │
                ┌───────────┴───────────┐
                ▼                       ▼
        calculateTotalInput()   calculateTotalOutput()
                │                       │
                └───────────┬───────────┘
                            ▼
                    テーブルレンダリング
                            │
        ┌─────────┬─────────┼─────────┬─────────┐
        ▼         ▼         ▼         ▼         ▼
      Date      Model    Input    Output     Cost
                         (popup)  (popup)   (type)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| usage-section.tsx | `packages/console/app/src/routes/workspace/[id]/usage-section.tsx` | ソース | 利用状況テーブルコンポーネント |
| common.tsx | `packages/console/app/src/routes/workspace/common.tsx` | ソース | 日付フォーマット関数、共通ユーティリティ |
| usage-section.module.css | `packages/console/app/src/routes/workspace/[id]/usage-section.module.css` | スタイル | コンポーネントスタイル |
| billing.js | `@opencode-ai/console-core/billing.js` | ソース | Billing.usages APIコール |
| auth.withActor.ts | `packages/console/app/src/context/auth.withActor.ts` | ソース | 認証ラッパー |
| i18n.ts | `packages/console/app/src/context/i18n.ts` | ソース | 国際化コンテキスト |
