iT邦幫忙

2025 iThome 鐵人賽

DAY 6
0
自我挑戰組

30 天用 Vibe Coding 打造多角色復健追蹤應用系列 第 6

Day 6 第二階段:核心 API 開發 (ㄧ)資料庫Schema

  • 分享至 

  • xImage
  •  

還記得當初的開發計劃表嗎:

Phase 1: Core Infrastructure & Setup ✅ COMPLETED

Phase 2: Core API Development ⏳ NEXT

Phase 3: Role-Specific Features

Phase 4: Calendar & UI Components

Phase 5: Advanced Features & Polish

現在進行到Phase 2
由於第一階段它一次全部都跑完了,
一時有點難吸收,這次我請他調整執行各階段時,
都要一步一步來,而且要加上說明他做了什麼。

第一步:資料庫模型與架構設計

  • 目的與原因: 為所有數據實體建立 MongoDB 架構,這是 API 數據基礎。

  • 實施細節:

    • 建立使用者模型 (User model),支援病患 (Patient)、物理治療師 (Physiotherapist)、醫生 (Doctor) 三種角色。
    • 建立復健任務模型 (RehabTask model),用於復健練習與排程。
    • 建立進度模型 (Progress model),追蹤病患練習完成情況。
    • 建立評論模型 (Comment model),記錄醫護人員的意見反饋。
    • 建立手術模型 (Surgery model),記錄醫生的手術紀錄。
  • 需建立的檔案:

    • src/main/js/models/User.js
    • src/main/js/models/RehabTask.js
    • src/main/js/models/Progress.js
    • src/main/js/models/Comment.js
    • src/main/js/models/Surgery.js

    Claude生成的內容比想像中的複雜許多,我想將資料庫拆成兩天的文章來解析,
    今天就先針對User 以及 RehabTask兩個Model來介紹。

    1. 使用者模型 (User)

    此模型是整個系統的基石,專為支援三種主要角色而設計:病患物理治療師醫生

    • 多角色支援:一個模型就能管理所有使用者類型,並能儲存如病患與醫護人員之間的關係等專屬資訊。
    • 高安全性:內建 bcrypt 密碼雜湊功能,確保所有密碼在儲存到資料庫前都已加密,有效防止資料外洩。
    • 虛擬屬性 (Virtuals)fullName 會即時合併使用者的名和姓;age 會根據出生日期動態計算年齡,這些屬性都不會實際儲存在資料庫中。
    // Virtual for full name
    userSchema.virtual('fullName').get(function() {
      return `${this.firstName} ${this.lastName}`;
    });
    
    // Virtual for age calculation
    userSchema.virtual('age').get(function() {
      if (!this.dateOfBirth) return null;
      const today = new Date();
      const birthDate = new Date(this.dateOfBirth);
      let age = today.getFullYear() - birthDate.getFullYear();
      const monthDiff = today.getMonth() - birthDate.getMonth();
      if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birthDate.getDate())) {
        age--;
      }
      return age;
    });
    
    • 預儲存中介軟體 (Pre-save Middleware):在儲存密碼到資料庫前,會自動將明文密碼進行雜湊處理,以保障安全性。
    // Pre-save middleware to hash password
    userSchema.pre('save', async function(next) {
      // Only hash the password if it has been modified (or is new)
      if (!this.isModified('password')) return next();
    
      try {
        // Hash password with cost of 12
        const saltRounds = parseInt(process.env.BCRYPT_ROUNDS) || 12;
        this.password = await bcrypt.hash(this.password, saltRounds);
        next();
      } catch (error) {
        next(error);
      }
    });
    
    • 實例方法 (Instance Methods)comparePassword 能夠安全地比對使用者輸入的密碼是否正確;generateAuthToken 則用於登入後生成 JWT token,方便身分驗證。
    // Instance method to compare password
    userSchema.methods.comparePassword = async function(candidatePassword) {
      try {
        return await bcrypt.compare(candidatePassword, this.password);
      } catch (error) {
        throw new Error('Password comparison failed');
      }
    };
    // Instance method to generate JWT token (to be implemented in auth service)
    userSchema.methods.generateAuthToken = function() {
      // This will be implemented in the auth service
      return {
        userId: this._id,
        role: this.role,
        email: this.email
      };
    };
    
    • 靜態方法 (Static Methods)findByEmailfindByRole 允許你透過電子郵件或角色快速查詢使用者資料,提高程式碼的簡潔性。
    // Static method to find user by email
    userSchema.statics.findByEmail = function(email) {
      return this.findOne({ email: email.toLowerCase() });
    };
    
    // Static method to find users by role
    userSchema.statics.findByRole = function(role) {
      return this.find({ role, isActive: true });
    };
    
    • 索引與序列化 (Indexes & Serialization):為常用查詢欄位(如 emailrole)建立索引,提升查詢效能。同時,在傳送資料給前端時,自動移除敏感的密碼資訊。
    // Index for better query performance
    userSchema.index({ email: 1 });
    userSchema.index({ role: 1 });
    userSchema.index({ 'assignedPatients': 1 });
    userSchema.index({ 'assignedProviders.providerId': 1 });
    
    // Ensure virtual fields are serialized
    userSchema.set('toJSON', {
      virtuals: true,
      transform: function(doc, ret) {
        delete ret.password;
        delete ret.__v;
        return ret;
      }
    });
    

    2 RehabTask 模型

    RehabTask 不僅是儲存復健任務的資料結構,它還內建多項智能功能,確保數據的完整性、效率和自動化。許多設置如同User使用的功能,以下就不列出程式碼。

    1. 數據完整性與驗證

    • 嚴格的欄位限制:使用 requiredmaxlengthenum 等,確保像任務標題、描述和類別等資料都符合預設格式,避免無效數據。
    • 自定義邏輯:內建的驗證器會自動檢查排程的結束日期是否晚於開始日期,確保排程邏輯正確。

    2. 虛擬屬性

    這些屬性不會儲存在資料庫中,但能即時提供重要資訊。

    • completionPercentage:動態計算任務的完成百分比。
    • daysRemaining:顯示任務剩餘天數。
    • isOverdue:判斷任務是否已過期。

    3. 自動化與方法

    這些功能將常見的業務邏輯自動化,讓程式碼更簡潔。

    • pre-save 中介軟體:在每次儲存前,自動更新任務狀態,例如當總會話數完成時,將狀態設為「已完成」。
    • markSessionCompleted:這個方法會自動增加任務的完成次數,並更新依從性百分比。

    4. 靜態方法與效能

    這些是模型層級的查詢工具,優化了數據檢索。

    • findActiveTasksForPatientfindTasksByTherapist:提供快速且預先載入相關資訊的查詢,減少後端額外的資料庫請求,大幅提升效能。

上一篇
Day 5 分析 Server.js
下一篇
Day 7 第二階段:核心 API 開發 (ㄧ)資料庫Schema-2
系列文
30 天用 Vibe Coding 打造多角色復健追蹤應用8
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言