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

463 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 🧠 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)** | - セット商品が多い<br>- メニュー作成機能を使用<br>- 複数人での飲酒シーンを想定 | 🍻 ワイワイ派<br>「みんなで楽しむのが好き」 |
| **I (Introvert)** | - 単品登録が多い<br>- メモが詳細(個人的感想)<br>- 静かに味わう傾向 | 🍶 しっぽり派<br>「一人でじっくり味わう」 |
**データソース**:
```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)** | - スペック情報を細かく編集<br>- 精米歩合・酒米・酵母を記録<br>- 写真を複数枚撮影 | 📊 データ派<br>「スペック重視で選ぶ」 |
| **N (Intuition)** | - AI解析のまま放置<br>- メモが感覚的(「美味しい」「好き」)<br>- 感想タグが多い | 💭 感覚派<br>「直感で選ぶ、雰囲気重視」 |
**データソース**:
```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)** | - 味覚チャートを重視<br>- 数値的な比較をする<br>- 「コスパ」「酒米の違い」など論理的メモ | 🧮 論理派<br>「データで比較、納得して選ぶ」 |
| **F (Feeling)** | - お気に入り率が高い<br>- 「思い出」「雰囲気」などのメモ<br>- ギフトセットが多い | ❤️ 感情派<br>「心で感じて選ぶ、ストーリー重視」 |
**データソース**:
```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)** | - 定期的に登録(週次パターン)<br>- 都道府県コンプリート志向<br>- バックアップを頻繁に実施 | 📅 計画派<br>「コレクション管理、制覇が目標」 |
| **P (Perceiving)** | - 不規則な登録(気が向いたら)<br>- 同じ銘柄を複数回登録<br>- 削除も気軽 | 🌀 気まぐれ派<br>「その時の気分で楽しむ」 |
**データソース**:
```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<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 衝突)
### サンプル相性
```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<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画面に診断ボタン追加
```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<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 - 晩酌隊長」)
└── バッジケース
```