145 lines
4.4 KiB
Dart
145 lines
4.4 KiB
Dart
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<String, int> tasteStats;
|
||
|
||
@HiveField(2)
|
||
final List<String> 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<String, double>? 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<String, double> 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<String, int>? tasteStats,
|
||
List<String>? flavorTags,
|
||
double? sweetnessScore,
|
||
double? bodyScore,
|
||
String? type,
|
||
double? alcoholContent,
|
||
int? polishingRatio,
|
||
double? sakeMeterValue,
|
||
String? riceVariety,
|
||
String? yeast,
|
||
String? manufacturingYearMonth,
|
||
String? qrCodeUrl,
|
||
Map<String, double>? 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, // ✅ 追加
|
||
);
|
||
}
|
||
}
|