import 'package:hive/hive.dart'; import 'sake_taste_stats.dart'; part 'hidden_specs.g.dart'; @HiveType(typeId: 12) class HiddenSpecs extends HiveObject { @HiveField(0) final String? description; @HiveField(1) final Map tasteStats; @HiveField(2) final List flavorTags; @HiveField(3) final double? sweetnessScore; @HiveField(4) final double? bodyScore; // Future specs @HiveField(5) final String? type; // 特定名称 @HiveField(6) final double? alcoholContent; @HiveField(7) final int? polishingRatio; @HiveField(8) final double? sakeMeterValue; // 日本酒度 @HiveField(9) final String? riceVariety; @HiveField(10) final String? yeast; @HiveField(11) final String? manufacturingYearMonth; @HiveField(12) final String? qrCodeUrl; // ✅ 6軸フレーバーチャート(さけのわデータ) @HiveField(13) final Map? sakenowaFlavorChart; // {f1, f2, f3, f4, f5, f6} @HiveField(14) final int? sakenowaBrandId; // さけのわ銘柄ID(マッチング用) // Helper getter to convert generic Map to typed SakeTasteStats SakeTasteStats get sakeTasteStats { if (tasteStats.isEmpty) { return SakeTasteStats(aroma: 0, bitterness: 0, sweetness: 0, acidity: 0, body: 0); } return SakeTasteStats.fromMap(tasteStats); } /// ✅ アクティブな味覚データ取得(6軸優先、フォールバックで5軸) /// さけのわデータがあればそれを使用、なければGemini解析結果 Map get activeTasteData { if (sakenowaFlavorChart != null && sakenowaFlavorChart!.isNotEmpty) { // さけのわ6軸を五味5軸に変換して返す final f1 = sakenowaFlavorChart!['f1'] ?? 0.5; final f2 = sakenowaFlavorChart!['f2'] ?? 0.5; final f3 = sakenowaFlavorChart!['f3'] ?? 0.5; final f4 = sakenowaFlavorChart!['f4'] ?? 0.5; final f5 = sakenowaFlavorChart!['f5'] ?? 0.5; final f6 = sakenowaFlavorChart!['f6'] ?? 0.5; return { 'aroma': f1, // 華やか → 香り 'sweetness': (f2 + f4) / 2, // 芳醇・穏やか → 甘み 'acidity': f5, // 軽快 → 酸味 'bitterness': f6, // ドライ → キレ 'body': f3, // 重厚 → コク }; } // フォールバック: Gemini五味チャート return tasteStats.map((k, v) => MapEntry(k, v.toDouble() / 5)); } /// ✅ 6軸フレーバーの有無を判定 bool get hasSakenowaFlavorChart => sakenowaFlavorChart != null && sakenowaFlavorChart!.isNotEmpty; HiddenSpecs({ this.description, this.tasteStats = const {}, this.flavorTags = const [], this.sweetnessScore, this.bodyScore, this.type, this.alcoholContent, this.polishingRatio, this.sakeMeterValue, this.riceVariety, this.yeast, this.manufacturingYearMonth, this.qrCodeUrl, this.sakenowaFlavorChart, // ✅ 追加 this.sakenowaBrandId, // ✅ 追加 }); HiddenSpecs copyWith({ String? description, Map? tasteStats, List? flavorTags, double? sweetnessScore, double? bodyScore, String? type, double? alcoholContent, int? polishingRatio, double? sakeMeterValue, String? riceVariety, String? yeast, String? manufacturingYearMonth, String? qrCodeUrl, Map? sakenowaFlavorChart, // ✅ 追加 int? sakenowaBrandId, // ✅ 追加 }) { return HiddenSpecs( description: description ?? this.description, tasteStats: tasteStats ?? this.tasteStats, flavorTags: flavorTags ?? this.flavorTags, sweetnessScore: sweetnessScore ?? this.sweetnessScore, bodyScore: bodyScore ?? this.bodyScore, type: type ?? this.type, alcoholContent: alcoholContent ?? this.alcoholContent, polishingRatio: polishingRatio ?? this.polishingRatio, sakeMeterValue: sakeMeterValue ?? this.sakeMeterValue, riceVariety: riceVariety ?? this.riceVariety, yeast: yeast ?? this.yeast, manufacturingYearMonth: manufacturingYearMonth ?? this.manufacturingYearMonth, qrCodeUrl: qrCodeUrl ?? this.qrCodeUrl, sakenowaFlavorChart: sakenowaFlavorChart ?? this.sakenowaFlavorChart, // ✅ 追加 sakenowaBrandId: sakenowaBrandId ?? this.sakenowaBrandId, // ✅ 追加 ); } }