227 lines
7.6 KiB
Dart
227 lines
7.6 KiB
Dart
import 'dart:io';
|
|
import 'package:cloud_firestore/cloud_firestore.dart';
|
|
import 'package:firebase_storage/firebase_storage.dart';
|
|
import 'package:firebase_auth/firebase_auth.dart';
|
|
import 'package:uuid/uuid.dart';
|
|
import '../models/pet_model.dart';
|
|
import '../models/schedule_model.dart';
|
|
import '../utils/log_manager.dart';
|
|
|
|
class FirestoreService {
|
|
final FirebaseFirestore _db = FirebaseFirestore.instance;
|
|
final FirebaseStorage _storage = FirebaseStorage.instance;
|
|
final FirebaseAuth _auth = FirebaseAuth.instance;
|
|
|
|
// 반려동물 등록
|
|
Future<void> registerPet(Pet pet, File? imageFile) async {
|
|
try {
|
|
String? imageUrl;
|
|
|
|
// 1. 이미지 업로드 (이미지가 있는 경우)
|
|
if (imageFile != null) {
|
|
final String fileName =
|
|
'${pet.id}_${DateTime.now().millisecondsSinceEpoch}.jpg';
|
|
final Reference storageRef = _storage
|
|
.ref()
|
|
.child('pet_images')
|
|
.child(fileName);
|
|
|
|
LogManager().addLog(
|
|
'[Storage] Starting upload to ${storageRef.fullPath}',
|
|
);
|
|
LogManager().addLog('[Storage] Bucket: ${_storage.bucket}');
|
|
|
|
final TaskSnapshot snapshot = await storageRef.putFile(imageFile);
|
|
|
|
if (snapshot.state == TaskState.success) {
|
|
LogManager().addLog(
|
|
'[Storage] Upload success. Validating metadata...',
|
|
);
|
|
// 잠시 대기 (consistency 이슈 방지)
|
|
await Future.delayed(const Duration(milliseconds: 500));
|
|
imageUrl = await storageRef.getDownloadURL();
|
|
LogManager().addLog('[Storage] Download URL retrieved: $imageUrl');
|
|
} else {
|
|
throw Exception('이미지 업로드 실패 (상태: ${snapshot.state})');
|
|
}
|
|
}
|
|
|
|
// 2. Pet 객체에 이미지 URL 업데이트 (새로운 객체 생성)
|
|
Pet petWithImage = Pet(
|
|
id: pet.id,
|
|
ownerId: pet.ownerId,
|
|
name: pet.name,
|
|
species: pet.species,
|
|
breed: pet.breed,
|
|
gender: pet.gender,
|
|
isNeutered: pet.isNeutered,
|
|
birthDate: pet.birthDate,
|
|
isDateUnknown: pet.isDateUnknown,
|
|
registrationNumber: pet.registrationNumber,
|
|
profileImageUrl: imageUrl, // 이미지 URL 설정
|
|
weight: pet.weight, // 체중 추가
|
|
diseases: pet.diseases,
|
|
pastDiseases: pet.pastDiseases,
|
|
healthConcerns: pet.healthConcerns,
|
|
createdAt: pet.createdAt,
|
|
);
|
|
|
|
// 3. Firestore에 저장
|
|
await _db.collection('pets').doc(pet.id).set(petWithImage.toMap());
|
|
|
|
LogManager().addLog('[Firestore] Pet registered successfully: ${pet.id}');
|
|
} catch (e) {
|
|
LogManager().addLog('[Firestore] Error registering pet: $e');
|
|
throw Exception('반려동물 등록 실패: $e');
|
|
}
|
|
}
|
|
|
|
// 반려동물 정보 수정
|
|
Future<void> updatePet(Pet pet, File? newImageFile) async {
|
|
try {
|
|
String? imageUrl = pet.profileImageUrl;
|
|
|
|
// 1. 새 이미지가 있다면 업로드 및 기존 이미지 삭제(선택사항)
|
|
if (newImageFile != null) {
|
|
// 기존 이미지가 있다면 삭제할 수도 있겠지만, 여기선 덮어쓰거나 새로 올림
|
|
// 간단히 새로 올리고 URL 교체
|
|
final String fileName =
|
|
'${pet.id}_${DateTime.now().millisecondsSinceEpoch}.jpg';
|
|
final Reference storageRef = _storage
|
|
.ref()
|
|
.child('pet_images')
|
|
.child(fileName);
|
|
|
|
LogManager().addLog('[Storage] Uploading new image...');
|
|
await storageRef.putFile(newImageFile);
|
|
imageUrl = await storageRef.getDownloadURL();
|
|
}
|
|
|
|
// 2. Pet 객체 업데이트
|
|
Pet updatedPet = Pet(
|
|
id: pet.id, // ID 유지
|
|
ownerId: pet.ownerId, // Owner ID 유지
|
|
name: pet.name,
|
|
species: pet.species,
|
|
breed: pet.breed,
|
|
gender: pet.gender,
|
|
isNeutered: pet.isNeutered,
|
|
birthDate: pet.birthDate,
|
|
isDateUnknown: pet.isDateUnknown,
|
|
registrationNumber: pet.registrationNumber,
|
|
profileImageUrl: imageUrl,
|
|
weight: pet.weight,
|
|
diseases: pet.diseases,
|
|
pastDiseases: pet.pastDiseases,
|
|
healthConcerns: pet.healthConcerns,
|
|
createdAt: pet.createdAt, // 생성일 유지
|
|
);
|
|
|
|
// 3. Firestore 업데이트
|
|
await _db.collection('pets').doc(pet.id).set(updatedPet.toMap());
|
|
|
|
LogManager().addLog('[Firestore] Pet updated successfully: ${pet.id}');
|
|
} catch (e) {
|
|
LogManager().addLog('[Firestore] Error updating pet: $e');
|
|
throw Exception('반려동물 수정 실패: $e');
|
|
}
|
|
}
|
|
|
|
// 현재 로그인한 사용자의 ID 가져오기
|
|
String? getCurrentUserId() {
|
|
return _auth.currentUser?.uid;
|
|
}
|
|
|
|
// 반려동물 리스트 스트림 가져오기
|
|
Stream<List<Pet>> getPets(String userId) {
|
|
return _db
|
|
.collection('pets')
|
|
.where('ownerId', isEqualTo: userId)
|
|
.orderBy('createdAt', descending: true) // 최신 등록순
|
|
.snapshots()
|
|
.map((snapshot) {
|
|
return snapshot.docs.map((doc) => Pet.fromMap(doc.data())).toList();
|
|
});
|
|
}
|
|
|
|
// 새 Pet ID 생성
|
|
String generatePetId() {
|
|
return const Uuid().v4();
|
|
}
|
|
|
|
// 일정 추가
|
|
Future<void> addSchedule(Schedule schedule) async {
|
|
try {
|
|
await _db.collection('schedules').doc(schedule.id).set(schedule.toMap());
|
|
} catch (e) {
|
|
LogManager().addLog('[Firestore] Error adding schedule: $e');
|
|
throw Exception('일정 추가 실패: $e');
|
|
}
|
|
}
|
|
|
|
// 일정 조회 (특정 반려동물, 특정 날짜)
|
|
// 날짜는 년,월,일 만 비교를 위해 range query 사용 (startOfDay ~ endOfDay)
|
|
Stream<List<Schedule>> getSchedules(String petId, DateTime date) {
|
|
DateTime startOfDay = DateTime(date.year, date.month, date.day);
|
|
DateTime endOfDay = DateTime(date.year, date.month, date.day, 23, 59, 59);
|
|
|
|
return _db
|
|
.collection('schedules')
|
|
.where('petId', isEqualTo: petId)
|
|
.where('date', isGreaterThanOrEqualTo: Timestamp.fromDate(startOfDay))
|
|
.where('date', isLessThanOrEqualTo: Timestamp.fromDate(endOfDay))
|
|
.snapshots()
|
|
.map((snapshot) {
|
|
return snapshot.docs
|
|
.map((doc) => Schedule.fromMap(doc.data()))
|
|
.toList();
|
|
});
|
|
}
|
|
|
|
// 월간 일정 조회 (도장 모드용) - 해당 월의 모든 일정 가져오기
|
|
Stream<List<Schedule>> getMonthlySchedules(String petId, DateTime month) {
|
|
DateTime startOfMonth = DateTime(month.year, month.month, 1);
|
|
DateTime endOfMonth = DateTime(month.year, month.month + 1, 0, 23, 59, 59);
|
|
|
|
return _db
|
|
.collection('schedules')
|
|
.where('petId', isEqualTo: petId)
|
|
.where('date', isGreaterThanOrEqualTo: Timestamp.fromDate(startOfMonth))
|
|
.where('date', isLessThanOrEqualTo: Timestamp.fromDate(endOfMonth))
|
|
.snapshots()
|
|
.map((snapshot) {
|
|
return snapshot.docs
|
|
.map((doc) => Schedule.fromMap(doc.data()))
|
|
.toList();
|
|
});
|
|
}
|
|
|
|
// 일정 수정
|
|
Future<void> updateSchedule(Schedule schedule) async {
|
|
try {
|
|
await _db
|
|
.collection('schedules')
|
|
.doc(schedule.id)
|
|
.update(schedule.toMap());
|
|
} catch (e) {
|
|
LogManager().addLog('[Firestore] Error updating schedule: $e');
|
|
throw Exception('일정 수정 실패: $e');
|
|
}
|
|
}
|
|
|
|
// 일정 삭제
|
|
Future<void> deleteSchedule(String scheduleId) async {
|
|
try {
|
|
await _db.collection('schedules').doc(scheduleId).delete();
|
|
} catch (e) {
|
|
LogManager().addLog('[Firestore] Error deleting schedule: $e');
|
|
throw Exception('일정 삭제 실패: $e');
|
|
}
|
|
}
|
|
|
|
// ID 생성
|
|
String generateScheduleId() {
|
|
return const Uuid().v4();
|
|
}
|
|
}
|