# 🧠 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)** | - 単品登録が多い
- メモが詳細(個人的感想)
- 静かに味わう傾向 | 🍶 しっぽり派
「一人でじっくり味わう」 | **データソース**: ```dart 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解析のまま放置
- メモが感覚的(「美味しい」「好き」)
- 感想タグが多い | 💭 感覚派
「直感で選ぶ、雰囲気重視」 | **データソース**: ```dart 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)** | - お気に入り率が高い
- 「思い出」「雰囲気」などのメモ
- ギフトセットが多い | ❤️ 感情派
「心で感じて選ぶ、ストーリー重視」 | **データソース**: ```dart 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)** | - 不規則な登録(気が向いたら)
- 同じ銘柄を複数回登録
- 削除も気軽 | 🌀 気まぐれ派
「その時の気分で楽しむ」 | **データソース**: ```dart 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 - 「晩酌隊長」 ```dart class MBTIType { final String code; // "ESTJ" final String title; // "晩酌隊長" final String subtitle; // "みんなで楽しむリーダー" final String description; // 長文 final String emoji; // "🍻👔" final List strengths; // ["計画性", "社交性"] final List weaknesses; // ["融通が利かない"] final List recommendedSakes; // ["辛口純米", "本醸造"] // 相性マトリクス(16x16) final Map 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 衝突) ### サンプル相性 ```dart // 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` ```dart class MBTIDiagnosisService { /// メイン診断関数 MBTIResult diagnose(List 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 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 _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画面に診断ボタン追加 ```dart // lib/screens/soul_screen.dart ElevatedButton.icon( icon: Icon(LucideIcons.brain), label: Text('MBTI診断'), onPressed: () => _showMBTIDiagnosis(context, ref), ) ``` #### 2-2. 診断結果ダイアログ ```dart 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. 相性マトリクス画面 ```dart 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) ```dart // lib/models/user_profile.dart に追加 @HiveField(10) String? mbtiType; // "ESTJ" @HiveField(11) DateTime? mbtiDiagnosedAt; ``` --- ## 📊 データ要件 ### 新規Provider(要実装) ```dart // メニュー使用履歴 final menuHistoryProvider = StateNotifierProvider>(...); // 削除履歴(J/P判定用) final deletionHistoryProvider = StateNotifierProvider(...); ``` --- ## 🎯 成功指標 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. **統計ダッシュボード** - 「日本で最も多いタイプはENFP(35%)」などの集計(匿名化必須) --- ## ⚠️ 注意事項 1. **医学的根拠なし**: MBTIは科学的根拠が薄いため、「エンタメ診断」として位置づける 2. **プライバシー**: タイプ結果は端末内のみ保存、外部送信しない(GDPRコンプライアンス) 3. **16タイプの定義の精度**: 初期はシンプルなルールベース、将来的にAIで改善 --- ## 📝 補足: 既存Shuko診断との統合 **両方を残す理由**: - **Shuko診断**: 味の好み(What) - **MBTI診断**: 飲酒スタイル(How) **UI上の配置**: ``` Soul画面 ├── プロフィール ├── 🍶 Shuko診断結果(「辛口サムライ」) ├── 🧠 MBTI診断結果(「ESTJ - 晩酌隊長」) └── バッジケース ```