ponshu-room-lite/docs/MBTI_DIAGNOSIS_SPECIFICATIO...

15 KiB
Raw Blame History

🧠 MBTI診断機能: 仕様書

作成日: 2026-01-22 ステータス: Phase 2.0 候補機能 目的: ユーザーの飲酒スタイルをMBTI風の16タイプに分類し、「飲み友達としての相性」を可視化


📊 概要

既存のShuko診断との違い

項目 Shuko診断 (既存) MBTI診断 (新規)
軸の数 5軸味覚統計 4軸行動・嗜好
タイプ数 6タイプ 16タイプ
判定基準 味覚データの平均値 飲酒行動パターン
相性機能 なし 16x16の相性マトリクス
パーソナリティ 味の好み キャラクター性
UI表示 Soul画面の一部 専用ダイアログ/画面

🎯 4つの軸定義

1. E/I外向/内向) - 飲酒シーンの好み

タイプ 判定基準 特徴
E (Extrovert) - セット商品が多い
- メニュー作成機能を使用
- 複数人での飲酒シーンを想定
🍻 ワイワイ派
「みんなで楽しむのが好き」
I (Introvert) - 単品登録が多い
- メモが詳細(個人的感想)
- 静かに味わう傾向
🍶 しっぽり派
「一人でじっくり味わう」

データソース:

final totalSets = items.where((item) => item.itemType == ItemType.set).length;
final hasUsedMenu = ref.read(menuHistoryProvider).isNotEmpty; // 要実装
final avgMemoLength = items.map((i) => i.userData.memo?.length ?? 0).average;

final isExtrovert = (totalSets > totalItems * 0.3) || hasUsedMenu;

2. S/N現実/直感) - 情報の重視度

タイプ 判定基準 特徴
S (Sensing) - スペック情報を細かく編集
- 精米歩合・酒米・酵母を記録
- 写真を複数枚撮影
📊 データ派
「スペック重視で選ぶ」
N (Intuition) - AI解析のまま放置
- メモが感覚的(「美味しい」「好き」)
- 感想タグが多い
💭 感覚派
「直感で選ぶ、雰囲気重視」

データソース:

final editedSpecsCount = items.where((i) => i.metadata.isUserEdited).length;
final avgPhotosPerItem = items.map((i) => i.displayData.imagePaths.length).average;
final hasDetailedMemos = items.where((i) =>
  i.userData.memo?.contains(RegExp(r'精米|酒米|酵母|度数')) ?? false
).length;

final isSensing = (editedSpecsCount > totalItems * 0.4) ||
                  (avgPhotosPerItem > 2) ||
                  (hasDetailedMemos > totalItems * 0.3);

3. T/F思考/感情) - 評価基準

タイプ 判定基準 特徴
T (Thinking) - 味覚チャートを重視
- 数値的な比較をする
- 「コスパ」「酒米の違い」など論理的メモ
🧮 論理派
「データで比較、納得して選ぶ」
F (Feeling) - お気に入り率が高い
- 「思い出」「雰囲気」などのメモ
- ギフトセットが多い
❤️ 感情派
「心で感じて選ぶ、ストーリー重視」

データソース:

final favoriteRatio = items.where((i) => i.userData.isFavorite).length / totalItems;
final emotionalKeywords = ['思い出', '感動', '美味しかった', 'また飲みたい', 'プレゼント'];
final emotionalMemoCount = items.where((i) =>
  emotionalKeywords.any((kw) => i.userData.memo?.contains(kw) ?? false)
).length;

final isFeeling = (favoriteRatio > 0.6) || (emotionalMemoCount > totalItems * 0.4);

4. J/P計画/柔軟) - 購入・管理スタイル

タイプ 判定基準 特徴
J (Judging) - 定期的に登録(週次パターン)
- 都道府県コンプリート志向
- バックアップを頻繁に実施
📅 計画派
「コレクション管理、制覇が目標」
P (Perceiving) - 不規則な登録(気が向いたら)
- 同じ銘柄を複数回登録
- 削除も気軽
🌀 気まぐれ派
「その時の気分で楽しむ」

データソース:

final registrationDates = items.map((i) => i.metadata.createdAt).toList();
final hasWeeklyPattern = _detectWeeklyPattern(registrationDates); // 要実装

final prefectureSet = items.map((i) => i.displayData.prefecture).toSet();
final prefectureCoverage = prefectureSet.length / 47; // 全都道府県中の割合

final deletedCount = ref.read(deletionHistoryProvider).length; // 要実装

final isJudging = hasWeeklyPattern || (prefectureCoverage > 0.3);

🎨 16タイプの定義

サンプル: ESTJ - 「晩酌隊長」

class MBTIType {
  final String code;        // "ESTJ"
  final String title;       // "晩酌隊長"
  final String subtitle;    // "みんなで楽しむリーダー"
  final String description; // 長文
  final String emoji;       // "🍻👔"
  final List<String> strengths;  // ["計画性", "社交性"]
  final List<String> weaknesses; // ["融通が利かない"]
  final List<String> recommendedSakes; // ["辛口純米", "本醸造"]

  // 相性マトリクス16x16
  final Map<String, Compatibility> compatibility;
}

enum Compatibility {
  perfect,    // ⭐⭐⭐⭐⭐ 完璧
  great,      // ⭐⭐⭐⭐ 良好
  good,       // ⭐⭐⭐ まあまあ
  challenging // ⭐⭐ 要努力
}

16タイプ一覧ドラフト

コード タイトル 特徴
ESTJ 晩酌隊長 🍻👔 みんなで楽しむリーダー、定番の辛口を好む
ESFJ おもてなし名人 🎁💕 人を喜ばせるのが好き、甘口・フルーティー
ENTJ 日本酒ソムリエ 🏆📊 データ重視、希少銘柄を追求
ENFJ 宴会プロデューサー 🎉🌸 雰囲気作りが上手、華やかな銘柄を選ぶ
ISTJ 伝統の守護者 ⛩️📖 正統派志向、山廃・生酛を好む
ISFJ ほっこり杜氏 🏡🍶 優しい味わい、地元の酒を大切に
INTJ 酒マイスター 🧪🔬 研究熱心、醸造技術に興味
INFJ ポエティック酒人 📚 ストーリー重視、蔵元の想いに共感
ESTP 冒険家 🚀🎲 新しい銘柄に積極的、季節限定が好き
ESFP パーティーキング 🎊🎤 楽しさ優先、スパークリング日本酒も
ENTP トレンドハンター 💡🌐 話題の酒を追う、実験的な銘柄
ENFP ロマンチスト 🌈💫 感性で選ぶ、ラベルデザインも重視
ISTP 職人気質 🔨⚙️ 静かに味わう、渋い銘柄を好む
ISFP アーティスト 🎨🍃 美しさ重視、吟醸香を楽しむ
INTP 理論派 🤔💭 分析好き、製法の違いを追求
INFP 夢想家 🌙🌌 想像力豊か、物語性のある酒

🧩 相性マトリクス設計

基本ルール

  1. 同じタイプ: (共感しやすい)
  2. 1文字違い: (理解できる)
  3. 2文字違い: (補完関係 or 衝突)
  4. 正反対4文字違い: or (化学反応 or 衝突)

サンプル相性

// ESTJ晩酌隊長の相性
compatibility: {
  'ESTJ': Compatibility.perfect,    // 同志
  'ISTJ': Compatibility.great,      // 共通の価値観
  'ESFJ': Compatibility.great,      // 補完関係
  'ENTP': Compatibility.challenging, // 計画 vs 柔軟で衝突
  'INFP': Compatibility.challenging, // 正反対だが学びあり
}

💻 実装方針

Phase 1: コア診断ロジック (6h)

新規ファイル: lib/services/mbti_diagnosis_service.dart

class MBTIDiagnosisService {
  /// メイン診断関数
  MBTIResult diagnose(List<SakeItem> items, WidgetRef ref) {
    if (items.length < 5) {
      return MBTIResult.insufficient(); // 最低5本必要
    }

    final e_i = _determineEI(items, ref);
    final s_n = _determineSN(items);
    final t_f = _determineTF(items);
    final j_p = _determineJP(items, ref);

    final code = '${e_i ? "E" : "I"}${s_n ? "S" : "N"}${t_f ? "T" : "F"}${j_p ? "J" : "P"}';
    final type = MBTITypes.getType(code);

    return MBTIResult(
      type: type,
      confidence: _calculateConfidence(items.length),
      sampleSize: items.length,
    );
  }

  bool _determineEI(List<SakeItem> items, WidgetRef ref) {
    final totalSets = items.where((i) => i.itemType == ItemType.set).length;
    final setRatio = totalSets / items.length;

    // TODO: メニュー使用履歴を確認
    // final hasUsedMenu = ref.read(menuHistoryProvider).isNotEmpty;

    return setRatio > 0.25; // 25%以上がセット → E
  }

  // ... 他の軸も同様に実装
}

class MBTITypes {
  static final Map<String, MBTIType> _types = {
    'ESTJ': MBTIType(
      code: 'ESTJ',
      title: '晩酌隊長',
      emoji: '🍻👔',
      description: 'みんなで楽しむリーダータイプ。計画的にお酒を楽しみ、定番の辛口を好む傾向があります。',
      strengths: ['計画性', '社交性', 'リーダーシップ'],
      weaknesses: ['融通が利かない', '新しいものに懐疑的'],
      recommendedSakes: ['辛口純米酒', '本醸造', '山廃仕込み'],
      compatibility: {
        'ESTJ': Compatibility.perfect,
        'ISTJ': Compatibility.great,
        // ... 16タイプ分
      },
    ),
    // ... 他の15タイプ
  };

  static MBTIType getType(String code) => _types[code]!;
}

Phase 2: UI実装 (4h)

2-1. Soul画面に診断ボタン追加

// lib/screens/soul_screen.dart
ElevatedButton.icon(
  icon: Icon(LucideIcons.brain),
  label: Text('MBTI診断'),
  onPressed: () => _showMBTIDiagnosis(context, ref),
)

2-2. 診断結果ダイアログ

class MBTIDiagnosisDialog extends StatelessWidget {
  final MBTIResult result;

  @override
  Widget build(BuildContext context) {
    return Dialog(
      child: Padding(
        padding: EdgeInsets.all(24),
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            // タイプバッジ
            Container(
              padding: EdgeInsets.all(16),
              decoration: BoxDecoration(
                color: _getTypeColor(result.type.code),
                borderRadius: BorderRadius.circular(16),
              ),
              child: Column(
                children: [
                  Text(result.type.emoji, style: TextStyle(fontSize: 48)),
                  Text(result.type.code, style: TextStyle(fontSize: 32, fontWeight: FontWeight.bold)),
                  Text(result.type.title, style: TextStyle(fontSize: 20)),
                ],
              ),
            ),

            SizedBox(height: 16),

            // 説明
            Text(result.type.description),

            SizedBox(height: 16),

            // 強み・弱み
            Row(
              children: [
                Expanded(
                  child: _buildTraitList('強み', result.type.strengths, Colors.green),
                ),
                Expanded(
                  child: _buildTraitList('弱み', result.type.weaknesses, Colors.orange),
                ),
              ],
            ),

            SizedBox(height: 16),

            // おすすめの日本酒
            _buildRecommendations(result.type.recommendedSakes),

            SizedBox(height: 16),

            // 相性を見るボタン
            ElevatedButton.icon(
              icon: Icon(LucideIcons.users),
              label: Text('飲み友達との相性を見る'),
              onPressed: () => _showCompatibility(context, result.type),
            ),
          ],
        ),
      ),
    );
  }
}

2-3. 相性マトリクス画面

class CompatibilityMatrixScreen extends StatelessWidget {
  final MBTIType myType;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('飲み友達との相性')),
      body: GridView.builder(
        gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
          crossAxisCount: 4,
          childAspectRatio: 1,
        ),
        itemCount: 16,
        itemBuilder: (context, index) {
          final type = MBTITypes.allTypes[index];
          final compat = myType.compatibility[type.code]!;

          return GestureDetector(
            onTap: () => _showCompatibilityDetail(context, type, compat),
            child: Card(
              color: _getCompatibilityColor(compat),
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  Text(type.code, style: TextStyle(fontWeight: FontWeight.bold)),
                  Text(type.emoji),
                  _buildStars(compat),
                ],
              ),
            ),
          );
        },
      ),
    );
  }
}

Phase 3: データ永続化 (2h)

// lib/models/user_profile.dart に追加
@HiveField(10)
String? mbtiType; // "ESTJ"

@HiveField(11)
DateTime? mbtiDiagnosedAt;

📊 データ要件

新規Provider要実装

// メニュー使用履歴
final menuHistoryProvider = StateNotifierProvider<MenuHistory, List<String>>(...);

// 削除履歴J/P判定用
final deletionHistoryProvider = StateNotifierProvider<DeletionHistory, int>(...);

🎯 成功指標

  1. 診断実行率: 5本以上登録したユーザーの70%が診断を実行
  2. 相性確認率: 診断後、60%のユーザーが相性マトリクスを確認
  3. SNSシェア率: 診断結果の30%がシェアされる(将来機能)

🚀 実装スケジュール

Phase 内容 工数 優先度
1 コア診断ロジック 6h 🔴
2 UI実装ダイアログ/画面) 4h 🔴
3 データ永続化 2h 🟡
4 相性マトリクス詳細 3h 🟢
5 SNSシェア機能 4h 🟢

合計: 19h (Phase 1-3で12h、Phase 2.0に推奨)


🔮 将来的な拡張

  1. AIによる相性解説

    • Geminiに「ESTJとINFPの相性について、日本酒の楽しみ方の違いを説明」と質問
  2. グループ診断

    • 複数人でQRコードをスキャンし、グループ全体の相性を可視化
  3. MBTI別レコメンド

    • 「あなたのタイプにおすすめの銘柄」をAIが提案
  4. 統計ダッシュボード

    • 「日本で最も多いタイプはENFP35%)」などの集計(匿名化必須)

⚠️ 注意事項

  1. 医学的根拠なし: MBTIは科学的根拠が薄いため、「エンタメ診断」として位置づける
  2. プライバシー: タイプ結果は端末内のみ保存、外部送信しないGDPRコンプライアンス
  3. 16タイプの定義の精度: 初期はシンプルなルールベース、将来的にAIで改善

📝 補足: 既存Shuko診断との統合

両方を残す理由:

  • Shuko診断: 味の好みWhat
  • MBTI診断: 飲酒スタイルHow

UI上の配置:

Soul画面
├── プロフィール
├── 🍶 Shuko診断結果「辛口サムライ」
├── 🧠 MBTI診断結果「ESTJ - 晩酌隊長」)
└── バッジケース