2025年8月10日 星期日

PetCareHub全端寵物照護平台:Laravel Flutter Flask 智慧化與多端協作的微服務架構實踐

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 的資料流設計確保了各組件間高效且可靠的通訊:

  1. 發送請求 (用戶端 -> API Gateway):Flutter App 和 Vue 3 Web UI 向 Nginx API Gateway 發送 HTTP/REST 請求,請求應用程式功能或數據。

  2. 路由至 API (API Gateway -> 主後端服務):Nginx 根據請求路徑(例如 /api/v1)將請求轉發至 Laravel API 服務。

  3. 基礎服務互動 (主後端服務):Laravel API 根據業務需求,與後端基礎設施互動:

    • 3.1 透過 ORM (如 Eloquent) 查詢或更新 MySQL 資料庫

    • 3.2 透過 Redis 客戶端讀取或寫入 Redis 快取,以加速數據存取或管理會話。

    • 3.3 透過 AMQP 協議向 RabbitMQ 訊息佇列發送任務(例如:觸發通知、數據分析)或消費訊息。

  4. 呼叫 AI 建議 (主後端服務 -> AI 微服務):當需要智慧建議時,Laravel API 會向 Flask AI Service 發送一個 HTTP 請求,傳遞相關的寵物數據和用戶行為。

  5. 回傳建議結果 (AI 微服務 -> 主後端服務):Flask AI Service 處理完數據,生成智慧建議後,將結果回傳給 Laravel API。

  6. 組裝 JSON 回應 (主後端服務 -> API Gateway):Laravel API 將業務處理結果與 AI 建議整合,組裝成最終的 JSON 格式回應,並回傳給 Nginx。

  7. 回傳最終結果 (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 不僅能為飼主提供便捷高效的寵物照護工具,更為未來的功能擴展與技術演進奠定了堅實的基礎,助力打造更美好的寵物生活。

沒有留言:

張貼留言

熱門文章