← 一覧に戻る

Phase 2a 運用基盤: バジェット管理 + 利用データ計測

2026年3月15日 05:47 更新
MD から自動変換されたページです。内容について質問があれば右下の ? ボタンからどうぞ。

Context

クローズドベータ(月¥5,000予算)を安全に運用するために、2つの仕組みが必要:

  1. バジェット管理: 月間通話コストを把握し、予算超過で新規受付を自動停止
  2. 利用データ計測: Phase 2.5 のシグナル判断(D7継続率・無料上限到達率)に必要なファネルデータを集計

どちらも既存テーブル(reminders / users)から集計。新テーブルは作らない。

TDD イテレーション

Iteration 36: バジェット計算の純関数(src/budget.ts

新ファイル src/budget.ts + src/budget.test.ts

// 定数
export const CALL_COST_YEN = 13;
export const NUMBER_MONTHLY_FEE_YEN = 165;

// 月間コスト = リマインダー通話数 × ¥13 + ¥165
export function calculateMonthlyCost(reminderCalls: number): number

// コスト >= 実効予算 なら超過
// 実効予算 = budgetYen × SAFETY_MARGIN_RATIO(認証コール分のバッファ)
export const SAFETY_MARGIN_RATIO = 0.8;
export function isBudgetExceeded(cost: number, budgetYen: number): boolean

認証コールの扱い: verification call は既存データから正確にカウントできない(verification_request_count は認証成功時にリセットされ、再試行や番号変更も追跡不能)。新テーブル不要の方針のため、SAFETY_MARGIN_RATIO = 0.8(予算の20%を認証コール用に留保)で安全側に倒す。Admin stats では tracked(リマインダー通話のみ)と effective budget(80%適用後)を明示する。

テストケース:

Iteration 37: バジェット超過時にリマインダー登録 + 認証コールを拒否

変更ファイル: src/index.ts, wrangler.toml

Iteration 38: /admin/stats エンドポイント(認証 + バジェット JSON)

変更ファイル: src/index.ts

Iteration 39: /admin/stats にファネルメトリクス追加

変更ファイル: src/index.ts

既存テーブルから集計するクエリ:

メトリクス 説明 クエリ
phoneSubmitted 電話番号を送信したユーザー数 SELECT COUNT(*) FROM users
verified 電話認証を完了したユーザー数 SELECT COUNT(*) FROM users WHERE phone_verified = 1
hasUsed 1回以上リマインダーを作成したユーザー数 SELECT COUNT(DISTINCT line_user_id) FROM reminders
retainedD7 登録7日以上前のユーザーで、登録日+7日以降にもリマインダーを作成した数 下記参照
quotaReached 無料枠(3回)に到達したユーザー数(オーナー/VIP除外) 下記参照

注意: phoneSubmitted は友達追加しただけのユーザーを含まない(users テーブルは電話番号送信時に初めてINSERTされるため)。真のファネル起点(友達追加数)の計測には follow イベントのハンドリングが必要(Phase 2a 機能実装の「LINE イベント型の厳密化」で対応予定)。

quotaReached クエリ(実際の制限ロジックと完全に一致):

実際のコード: effectiveCalls = (month_key === currentMonth ? calls_this_month : 0) + pendingCount >= 3 → 月跨ぎ時は calls_this_month を0にリセット。オーナー/VIPは免除。

quotaReached は JS で計算する(オーナー/VIPのフィルタリングに env var が必要なため、純SQLでは完結しない):

  1. 全 verified ユーザーと pending 件数を取得
  2. JS でオーナー/VIP を除外
  3. effectiveCalls + pendingCount >= 3 でフィルタ
  4. カウントを返す

レスポンスに funnel フィールドを追加:

{
  "budget": { ... },
  "funnel": {
    "phoneSubmitted": 50,
    "verified": 35,
    "hasUsed": 28,
    "retainedD7": 12,
    "quotaReached": 8
  }
}

関連ファイル

ファイル 役割
src/index.ts webhook + cron + admin エンドポイント
src/index.test.ts 統合テスト
src/budget.ts バジェット計算純関数(新規)
src/budget.test.ts バジェット計算テスト(新規)
wrangler.toml env var 追加
schema.sql 変更なし

設計判断

検証

tsc --noEmit && pnpm test

全4イテレーション完了後、~/bin/codex-full review で Codex レビュー → LGTM まで修正。

📝 質問モード — テキストを選択してね
✓ 質問を送信しました