PetCareHub 全端寵物照護平台:智慧化與多端協作的微服務架構實踐
引言
在現代社會,寵物已成為許多家庭不可或缺的一份子。然而,日益複雜的寵物照護需求,往往讓飼主面臨時間管理與專業知識的挑戰。手動規劃照護任務既耗時又容易出錯,且難以根據寵物個體差異進行精確調整。PetCareHub 作為一個全端智慧寵物照護解決方案,旨在透過整合多項前沿技術,協助飼主輕鬆規劃、記錄並優化每日寵物照護任務,最終提升寵物福祉與飼主體驗。
您可以透過以下 GitHub 連結檢閱本專案的原始碼:https://github.com/BpsEason/petcarehub.git
主要亮點與解決的問題
PetCareHub 的核心設計理念與所選技術棧,旨在解決傳統寵物照護管理中常見的痛點,並提供創新性的解決方案:
AI 驅動推薦:告別經驗主義,實現科學化照護
解決問題:傳統寵物照護依賴飼主個人經驗或單一資訊來源,可能導致餵食過量/不足、運動不當或忽略潛在健康問題。這對不熟悉寵物習性的新手飼主尤其困擾。
方案:透過獨立的 Flask AI 微服務,我們利用機器學習模型分析寵物的品種、年齡、體重、活動量、健康記錄等多維度數據。這使得系統能夠動態生成個性化、科學化的照護建議(如精確的餵食量、最佳洗澡頻率、建議運動時長等),將照護從經驗主義提升到數據驅動的智慧決策。
多端同步:打破平台壁壘,實現無縫管理
解決問題:飼主通常需要在不同設備(手機、電腦)上管理寵物資訊,數據不同步或體驗不一致會造成困擾。
方案:以 Laravel REST API 作為統一的數據核心和業務邏輯層,無論是 Vue 3 網頁端的詳盡儀表板,還是 Flutter 原生 App 的便捷移動體驗,所有數據都能即時同步,確保飼主在任何設備上都能獲得一致、即時的資訊與操作能力,實現真正的「隨時隨地」管理。
模組化微服務架構:提升開發效率與系統彈性
解決問題:單體應用程式隨著功能增長,變得龐大、難以維護和擴展,不同團隊間的開發進度也容易相互影響。
方案:將系統拆分為多個職責明確、獨立部署的微服務(如 Laravel API、Flask AI Service)。這種架構允許團隊獨立開發、測試與部署各自的服務,顯著提升了開發效率。同時,當特定服務的負載增加時,可以針對性地擴展,避免了整體系統的瓶頸,大大增強了系統的彈性與可維護性。
CI/CD 與自動化測試:保障品質,加速迭代
解決問題:手動測試耗時耗力且容易出錯,導致發布週期長、潛在 bug 多,降低了產品可靠性。
方案:整合 GitHub Actions 實現了端到端的自動化建構、測試與部署(CI/CD)流程。每次程式碼提交都會自動觸發單元測試、整合測試和功能測試。這不僅極大減少了人工錯誤,保障了程式碼品質,更使得新功能能夠快速、頻繁且可靠地部署到生產環境,加速產品迭代。
事件驅動與訊息佇列:解耦服務,優化資源利用
解決問題:同步調用會導致服務間強耦合,一個服務的延遲可能影響整個鏈路,且不適合處理耗時或高併發的任務。
方案:透過 RabbitMQ 訊息佇列作為各微服務之間的非同步通訊中介。例如,當飼主更新了寵物數據時,Laravel API 可以發送一個事件到 RabbitMQ,AI 服務訂閱此事件並非同步地進行數據分析和模型更新。這種事件驅動模式有效解耦了服務,提高了系統的響應速度、吞吐量和容錯能力,並能更高效地利用計算資源處理背景任務。
系統架構概覽
PetCareHub 的設計理念體現在其高效率的系統架構中,確保了其高度可擴展性、彈性和可維護性。其核心設計理念是將不同職責的服務獨立部署,並透過標準化的介面進行溝通。
架構組件說明:
用戶端 (CLIENTS):
Flutter App:原生行動應用程式,提供 iOS/Android 平台上的即時互動與通知。
Vue 3 Web UI:網頁版管理介面,提供全面的儀表板與任務管理功能。
API Gateway (GATEWAY):
Nginx:作為反向代理和負載均衡器,負責接收所有來自用戶端的請求,並將其智能路由至正確的後端服務(Laravel API 或 Flask AI Service),同時處理靜態檔案服務和潛在的 SSL 終止。
主後端服務 (BACKEND):
Laravel API:作為核心業務邏輯層,提供寵物、任務、用戶等資源的 RESTful API 服務。它負責處理認證、授權、資料驗證、業務規則執行以及與其他基礎設施服務的互動。
MySQL 資料庫:持久化儲存所有結構化資料,包括用戶資訊、寵物資料、照護任務記錄等。
Redis 快取:高性能的記憶體資料儲存,用於會話管理、熱點資料快取及提高 API 響應速度。
RabbitMQ 訊息佇列:作為訊息代理,實現各微服務之間的非同步通訊,例如處理排程任務、發送通知、觸發數據分析等,提升系統的解耦與吞吐量。
AI 微服務 (AI_SERVICE):
Flask AI Service:獨立的 Python 微服務,專門負責執行 AI 推薦演算法。它接收來自 Laravel API 的請求(如寵物健康數據、歷史行為),透過內建模型分析後,回傳個性化的照護建議。
資料流詳解:
PetCareHub 的資料流設計確保了各組件間高效且可靠的通訊:
發送請求 (用戶端 -> API Gateway):Flutter App 和 Vue 3 Web UI 向 Nginx API Gateway 發送 HTTP/REST 請求,請求應用程式功能或數據。
路由至 API (API Gateway -> 主後端服務):Nginx 根據請求路徑(例如
/api/v1
)將請求轉發至 Laravel API 服務。基礎服務互動 (主後端服務):Laravel API 根據業務需求,與後端基礎設施互動:
3.1
透過 ORM (如 Eloquent) 查詢或更新 MySQL 資料庫。3.2
透過 Redis 客戶端讀取或寫入 Redis 快取,以加速數據存取或管理會話。3.3
透過 AMQP 協議向 RabbitMQ 訊息佇列發送任務(例如:觸發通知、數據分析)或消費訊息。
呼叫 AI 建議 (主後端服務 -> AI 微服務):當需要智慧建議時,Laravel API 會向 Flask AI Service 發送一個 HTTP 請求,傳遞相關的寵物數據和用戶行為。
回傳建議結果 (AI 微服務 -> 主後端服務):Flask AI Service 處理完數據,生成智慧建議後,將結果回傳給 Laravel API。
組裝 JSON 回應 (主後端服務 -> API Gateway):Laravel API 將業務處理結果與 AI 建議整合,組裝成最終的 JSON 格式回應,並回傳給 Nginx。
回傳最終結果 (API Gateway -> 用戶端):Nginx 將最終的 JSON 回應轉發給發起請求的 Flutter App 或 Vue 3 Web UI。
核心功能
任務排程與提醒:靈活設定寵物照護任務的週期與時間,並提供及時提醒。
照護記錄與歷史查詢:詳細記錄寵物餵食、活動、醫療等歷史數據,方便追蹤健康趨勢。
AI 智慧建議:根據寵物的品種、年齡、體重及歷史數據,提供個性化的餵食量、洗澡頻率、運動時長等建議。
團隊協作:支援多用戶帳號與權限管理,方便多位家庭成員或專業人士共同參與寵物照護。
API 文件化:提供完整的 OpenAPI/Swagger 規範,便於第三方整合或內部服務間的協作。
關鍵程式碼範例與註解
本節將展示 PetCareHub 專案中各個關鍵服務之間如何協作,並提供程式碼範例與詳細註解,幫助讀者深入理解其實現細節。我們將以「建立新寵物」API 為例,展示從路由定義到多端呼叫的完整流程。
Laravel API 後端:路由定義 (routes/api.php
)
Laravel API 負責定義所有後端資源的存取路徑。以下是如何定義一個用於創建寵物資源的 POST 路由。
<?php
use App\Http\Controllers\PetController;
use Illuminate\Support\Facades\Route;
// 主 API 路由群組
Route::prefix('v1')->group(function () {
// 定義 POST /pets 路由,用於建立新的寵物資源
// 當接收到 POST /api/v1/pets 請求時,會呼叫 PetController 的 store 方法
Route::post('/pets', [PetController::class, 'store']);
// 其他寵物相關的 RESTful API 路由 (GET, PUT, DELETE /pets/{id})
// Route::apiResource('pets', PetController::class); // 簡化寫法,若要包含所有 CRUD 操作
});
Laravel API 後端:控制器邏輯 (app/Http/Controllers/PetController.php
)
PetController
中的 store
方法負責處理接收到的建立寵物請求,執行數據驗證並將寵物資訊存入資料庫。
<?php
namespace App\Http\Controllers;
use App\Models\Pet;
use Illuminate\Http\Request;
class PetController extends Controller
{
/**
* 處理建立新寵物的請求。
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\JsonResponse
*/
public function store(Request $request)
{
// 建議在此處加入請求驗證 (例如使用 Form Request)
// $validatedData = $request->validate([
// 'name' => 'required|string|max:255',
// 'species' => 'required|string|max:255',
// 'user_id' => 'required|exists:users,id',
// // ... 其他欄位驗證
// ]);
// 建立新的寵物實例並存入資料庫
$pet = Pet::create($request->all());
// 返回 JSON 格式的回應,狀態碼 201 表示資源已建立
return response()->json($pet, 201);
}
}
Vue 3 前端:呼叫 API (frontend/src/api/pets.js
)
前端應用程式透過 fetch
API 向 Laravel 後端發送請求來建立寵物。此範例展示了如何構造請求頭、發送 JSON 數據並處理回應。
// frontend/src/api/pets.js
/**
* 建立新的寵物。
*
* @param {Object} petData 包含寵物資訊的物件 (例如: { name: '小黃', species: 'dog' })
* @param {string} token 用戶的認證 token (e.g., Sanctum Token)
* @returns {Promise<Object>} 伺服器回傳的寵物資料
* @throws {Error} 當 API 呼叫失敗時
*/
export async function createPet(petData, token) {
// 從 Vite 環境變數取得 API 基礎 URL
const apiBaseUrl = import.meta.env.VITE_API_BASE_URL || '/api/v1';
const response = await fetch(`${apiBaseUrl}/pets`, {
method: 'POST', // 使用 POST 方法來建立資源
headers: {
'Content-Type': 'application/json', // 設定請求內容為 JSON 格式
'Authorization': `Bearer ${token}`, // 帶入認證 token
},
body: JSON.stringify(petData), // 將 JavaScript 物件轉換為 JSON 字串並作為請求體發送
});
// 檢查 HTTP 回應狀態碼是否成功 (2xx)
if (!response.ok) {
// 如果回應不成功,嘗試解析錯誤訊息
const errorData = await response.json().catch(() => ({ message: '未知錯誤' }));
throw new Error(`建立寵物失敗: ${response.status} - ${errorData.message || response.statusText}`);
}
// 成功時,解析回傳的 JSON 資料
return response.json();
}
Flask AI 微服務:建議端點 (ai-service/app.py
)
AI 微服務提供一個端點,接收寵物相關數據,並透過內建的 AI 模型生成照護建議。
# ai-service/app.py
from flask import Flask, jsonify, request
import pandas as pd
import numpy as np
import lightgbm as lgb
import os
app = Flask(__name__)
# 模擬一個簡單的 LightGBM 模型 (在實際生產環境中,模型會從檔案載入)
# 此處為快速啟動服務而建立的假模型
def create_dummy_model():
# 建立一些假資料來訓練模型,用於演示
data = {
'pet_age_months': [1, 5, 12, 24, 60, 90, 120, 3, 30, 72],
'pet_weight_kg': [1, 3, 10, 20, 15, 8, 25, 2, 12, 18],
'is_dog': [1, 0, 1, 1, 0, 1, 0, 0, 1, 0],
'is_cat': [0, 1, 0, 0, 1, 0, 1, 1, 0, 1],
'task_type': [0, 1, 0, 1, 0, 1, 0, 1, 0, 1] # 0: 餵食/清潔, 1: 散步/體檢
}
df = pd.DataFrame(data)
X = df[['pet_age_months', 'pet_weight_kg', 'is_dog', 'is_cat']]
y = df['task_type']
model = lgb.LGBMClassifier(objective='binary', n_estimators=10)
model.fit(X, y)
return model
model = create_dummy_model()
print("AI 虛擬模型已載入。") # 輸出訊息到控制台
@app.route('/recommendations', methods=['POST'])
def get_recommendations():
"""
接收寵物數據並回傳照護建議。
"""
data = request.json
pet_age_months = data.get('pet_age_months', 0)
pet_weight_kg = data.get('pet_weight_kg', 0)
species = data.get('species', 'unknown').lower()
# 輸入數據驗證
if not isinstance(pet_age_months, (int, float)) or not isinstance(pet_weight_kg, (int, float)):
return jsonify({'error': '年齡或體重輸入型態無效'}), 400
if species not in ['dog', 'cat', 'other']:
return jsonify({'error': '物種無效。必須是 dog, cat 或 other。'}), 400
is_dog = 1 if species == 'dog' else 0
is_cat = 1 if species == 'cat' else 0
try:
# 將輸入數據轉換為模型所需的 DataFrame 格式
input_data = pd.DataFrame([[pet_age_months, pet_weight_kg, is_dog, is_cat]],
columns=['pet_age_months', 'pet_weight_kg', 'is_dog', 'is_cat'])
# 使用模型進行預測,獲取每個類別的機率
prediction_proba = model.predict_proba(input_data)[0]
# 根據預測機率和業務規則生成推薦列表
recommended_tasks = []
if prediction_proba[1] > 0.5: # 假設 class 1 代表「散步/體檢」等活動型任務
recommended_tasks.append("定期散步與運動")
else: # 假設 class 0 代表「餵食/清潔」等居家型任務
recommended_tasks.append("監控飲食與飲水")
# 根據寵物年齡和物種的簡單規則補充推薦
if pet_age_months < 6: # 幼寵
recommended_tasks.append("幼寵疫苗接種提醒")
recommended_tasks.append("社會化訓練")
elif pet_age_months > 84: # 7歲以上為老年寵物 (84 個月 = 7 年)
recommended_tasks.append("老年寵物健康檢查")
recommended_tasks.append("關節保健品補充")
if species == 'cat':
recommended_tasks.append("每日清潔貓砂")
recommended_tasks.append("提供抓板防止破壞家具")
elif species == 'dog':
recommended_tasks.append("定期梳毛")
recommended_tasks.append("每月洗澡")
# 去除重複的推薦項目並排序
recommended_tasks = sorted(list(set(recommended_tasks)))
# 返回 JSON 格式的回應
return jsonify({
'pet_age_months': pet_age_months,
'species': species,
'predicted_task_prob_class_1': float(prediction_proba[1]), # 類別 1 的預測機率
'recommendations': recommended_tasks
})
except Exception as e:
# 處理模型預測或數據處理過程中的錯誤
return jsonify({'error': str(e), 'message': 'AI 預測失敗。'}), 500
if __name__ == '__main__':
# 在主入口點運行 Flask 應用程式,監聽所有網絡接口 (0.0.0.0) 和 5000 端口
app.run(host='0.0.0.0', port=5000)
Flutter 行動應用程式:API 呼叫 (lib/services/api_service.dart
)
Flutter App 透過 dio
庫與後端 API 互動。以下展示了一個用於創建寵物的方法範例。
// lib/services/api_service.dart
import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart'; // 導入 @protected 註解
class ApiService {
// 將 Dio 實例標記為 protected,以便在測試中被 Mockito 替換
@protected
final Dio _dio;
final String baseUrl;
// 預設建構函式,初始化 Dio 實例
ApiService(this.baseUrl) : _dio = Dio();
// 用於測試的命名建構函式,允許注入 Mock Dio 實例
ApiService.withDio(this.baseUrl, this._dio);
/// 呼叫 API 建立新寵物。
/// [petData] 包含寵物資訊的 Map。
/// [token] 用戶的認證 Token。
/// 成功時回傳創建的寵物資料。
/// 失敗時拋出 Exception。
Future<Map<String, dynamic>> createPet(Map<String, dynamic> petData, String token) async {
try {
final response = await _dio.post(
'$baseUrl/pets', // 寵物建立 API 路徑
data: petData, // 請求體數據
options: Options(
headers: {
'Authorization': 'Bearer $token', // 認證頭
'Content-Type': 'application/json', // 內容類型
},
),
);
if (response.statusCode == 201) {
return response.data; // 返回創建的寵物資料
} else {
throw Exception('無法建立寵物:狀態碼 ${response.statusCode}');
}
} on DioException catch (e) {
// 處理 Dio 特定的錯誤 (例如網路錯誤、回應錯誤)
print('Dio 錯誤建立寵物:$e');
if (e.response != null) {
print('錯誤回應數據:${e.response?.data}');
if (e.response?.statusCode == 401) {
throw Exception('未經授權:無效的 Token');
} else if (e.response?.statusCode == 422) {
throw Exception('驗證失敗:${e.response?.data['message']}');
}
}
throw Exception('網路錯誤或 API 呼叫失敗:${e.message}');
} catch (e) {
// 處理其他未預期的錯誤
print('通用錯誤建立寵物:$e');
rethrow; // 重新拋出錯誤
}
}
/// 呼叫 API 取得任務列表。
/// [token] 用戶的認證 Token。
/// 成功時回傳任務列表。
/// 失敗時拋出 Exception。
Future<List<Map<String, dynamic>>> fetchTasks(String token) async {
try {
final response = await _dio.get(
'$baseUrl/tasks', // 任務列表 API 路徑
options: Options(
headers: {
'Authorization': 'Bearer $token', // 認證頭
'Content-Type': 'application/json',
},
),
);
if (response.statusCode == 200) {
return List<Map<String, dynamic>>.from(response.data);
} else {
throw Exception('無法載入任務:狀態碼 ${response.statusCode}');
}
} on DioException catch (e) {
print('Dio 錯誤取得任務:$e');
if (e.response != null) {
print('錯誤回應數據:${e.response?.data}');
if (e.response?.statusCode == 401) {
throw Exception('未經授權:請重新登入');
}
}
throw Exception('網路錯誤或 API 呼叫失敗:${e.message}');
} catch (e) {
print('通用錯誤取得任務:$e');
rethrow;
}
}
}
技術棧
後端 API:Laravel 10+ (PHP)
網頁前端:Vue 3 + Pinia + Vite
行動端:Flutter + Dart
AI 推薦:Flask (Python) + Scikit-learn / LightGBM
API Gateway: Nginx
訊息佇列: RabbitMQ
資料庫: MySQL
快取/MQ: Redis
容器化:Docker & Docker Compose
CI/CD:GitHub Actions
結語
PetCareHub 專案展示了現代 Web 開發中多服務協作與智慧化應用的潛力。透過精心設計的微服務架構和選定的高效能技術棧,PetCareHub 不僅能為飼主提供便捷高效的寵物照護工具,更為未來的功能擴展與技術演進奠定了堅實的基礎,助力打造更美好的寵物生活。
沒有留言:
張貼留言