第二次結對作業

chenoojkk發表於2024-10-10
這個作業屬於哪個課程 https://edu.cnblogs.com/campus/fzu/SE2024
這個作業要求在哪裡 https://edu.cnblogs.com/campus/fzu/SE2024/homework/13281
這個作業的目標 將上次結對作業的內容用程式碼實現
姓名及學號 陳家凱 102202126
結對成員及學號 許煊宇 102202113
Github倉庫地址 https://github.com/chenoojkk/102202126-102202113

具體分工

登入註冊以及聊天功能:102202113

專案釋出以及搜尋功能:102202126

主要的幾個功能分工完成,剩下的一些細節則合作完成。

PSP表格

Personal Software Process Stages 預估耗時(小時) 實際耗時(小時)
Planning 計劃 2 1.5
Development 開發 2 2
Analysis 需求分析 (包括學習新技術) 20 20
Design Spec 生成設計文件 1 1
Design Review 設計複審 1 1
Coding Standard 程式碼規範 5 5
Design 具體設計 10 10
Coding 具體編碼 30 35
Code Review 程式碼複審 2 2
Test 測試(自我測試,修改程式碼,提交修改) 1 1
Reporting 報告 1 1
Test Report 測試報告 1 1
Size Measurement 計算工作量 1 1
Postmortem & Process Improvement Plan 事後總結, 並提出過程改進計劃 0.5 0.5
合計 77.5 82

解題思路描述與設計實現說明

  • 程式碼實現思路,文字描述(部分)

    以下是實現聊天功能的程式碼思路:

    1. 頁面佈局

    chat.wxml 檔案中定義聊天介面的佈局,包括訊息顯示區域和輸入框。使用 scroll-view 元件實現訊息的滾動顯示,使用 input 元件實現訊息輸入框,並新增傳送按鈕。

    2. 樣式設計

    chat.wxss 檔案中定義頁面的樣式,使頁面美觀且易於使用。包括訊息氣泡的樣式、頭像的樣式、輸入框和傳送按鈕的樣式等。

    3. 資料繫結

    chat.js 檔案中,透過 onLoadonShow 方法從全域性資料中獲取使用者資訊和聊天記錄,並繫結到頁面上。使用 setData 方法將資料繫結到頁面的 data 物件中。

    4. 資料庫操作

    chat.js 檔案中,透過 wx.cloud.database 方法從資料庫中獲取聊天記錄和好友資訊,並更新到頁面上。使用 watch 方法實時監聽聊天記錄的變化,並更新頁面顯示。

    5. 事件處理

    chat.js 檔案中定義輸入框的輸入事件和傳送按鈕的點選事件,並實現相應的處理函式。輸入事件用於實時更新輸入框的內容,傳送按鈕的點選事件用於將輸入框的內容傳送到資料庫,並更新聊天記錄。

​ 以下是實現 publishsubject 功能的程式碼思路:

1.頁面佈局

​ 在 publishsubject.wxml檔案中定義表單佈局,包括專案名稱、專案描述、專案合夥人和上傳圖片的輸入 框, 以及提交按鈕。

2.樣式設計

​ 在 publishsubject.wxss 檔案中定義頁面的樣式,使頁面美觀且易於使用。包括輸入框、按鈕和圖片預覽 的樣式。

3.資料繫結

​ 在 publishsubject.js 檔案中,透過 onLoad 方法初始化頁面資料,並繫結到頁面上。使用 setData 方 法將資料繫結到頁面的 data 物件中。

4.資料處理

​ 在 publishsubject.js 檔案中定義輸入框的輸入事件、圖片選擇事件和提交按鈕的點選事件,並實現相應 的處理函式。輸入事件用於實時更新輸入框的內容,圖片選擇事件用於選擇並預覽圖片,提交按鈕的點選事 件用於提交表單資料。

5.資料庫操作

​ 在 publishsubject.js 檔案中,透過 wx.cloud.database 方法將表單資料儲存到雲資料庫中,並在提交 後清空輸入框內容和圖片預覽。

  • 關鍵實現的流程圖或資料流圖

  • 貼出你認為重要的/有價值的程式碼片段,並解釋

    projectintro 頁面的主要功能是展示專案詳情,並提供申請加入專案和聯絡專案釋出人的功能。以下是對該頁面的詳細解釋:

    1. 引入工具模組
    const utils = require("../../utils/util");
    

    引入工具模組,用於格式化時間等操作。

    2. 頁面初始化
    Page({
      data: {
        project: {}
      },
      onLoad(options) {
        const eventChannel = this.getOpenerEventChannel();
        eventChannel.on('sendProjectData', (data) => {
          this.setData({
            project: data.project
          });
        });
      },
    

    頁面載入時,透過事件通道接收並設定專案資料。

    3. 申請加入專案
      applyToJoin() {
        const app = getApp();
        const userInfo = app.globalData.userInfo;
        const project = this.data.project;
    
        if (!project._id) {
          wx.showToast({
            icon: "none",
            title: '專案ID不存在',
          });
          return;
        }
    
        const db = wx.cloud.database();
    
        db.collection('chat_user').where({
          account_id: project.user
        }).get({
          success(res) {
            if (res.data.length > 0) {
              const userB = res.data[0];
              const newMessage = {
                id: userInfo._id,
                text: `${userInfo.account_id} 申請加入專案 ${project.projectName}`,
                time: utils.formatTime(new Date())
              };
    
              db.collection('chat_record').where({
                userA_id: userInfo._id,
                userB_id: userB._id,
              }).get({
                success(res) {
                  if (res.data.length > 0) {
                    const recordId = res.data[0]._id;
                    const record = res.data[0].record;
                    record.push(newMessage);
    
                    db.collection('chat_record').doc(recordId).update({
                      data: {
                        record: record
                      },
                      success(res) {
                        wx.showToast({
                          title: '申請已傳送!',
                          icon: 'success',
                          duration: 2000
                        });
                      },
                      fail(err) {
                        wx.showToast({
                          title: '申請傳送失敗',
                          icon: 'none',
                          duration: 2000
                        });
                      }
                    });
                  } else {
                    db.collection('chat_record').where({
                      userA_id: userB._id,
                      userB_id: userInfo._id,
                    }).get({
                      success(res) {
                        if (res.data.length > 0) {
                          const recordId = res.data[0]._id;
                          const record = res.data[0].record;
                          record.push(newMessage);
    
                          db.collection('chat_record').doc(recordId).update({
                            data: {
                              record: record
                            },
                            success(res) {
                              wx.showToast({
                                title: '申請已傳送!',
                                icon: 'success',
                                duration: 2000
                              });
                            },
                            fail(err) {
                              wx.showToast({
                                title: '申請傳送失敗',
                                icon: 'none',
                                duration: 2000
                              });
                            }
                          });
                        } else {
                          const newRecord = {
                            userA_id: userInfo._id,
                            userA_account_id: userInfo.account_id,
                            userA_avatarUrl: userInfo.avatarUrl,
                            userB_id: userB._id,
                            userB_account_id: userB.account_id,
                            userB_avatarUrl: userB.avatarUrl,
                            projectId: project._id,
                            record: [newMessage],
                            friend_status: false
                          };
    
                          db.collection('chat_record').add({
                            data: newRecord,
                            success(res) {
                              wx.showToast({
                                title: '申請已傳送!',
                                icon: 'success',
                                duration: 2000
                              });
                            },
                            fail(err) {
                              wx.showToast({
                                title: '申請傳送失敗',
                                icon: 'none',
                                duration: 2000
                              });
                            }
                          });
                        }
                      },
                      fail(err) {
                        wx.showToast({
                          title: '申請傳送失敗',
                          icon: 'none',
                          duration: 2000
                        });
                      }
                    });
                  }
                },
                fail(err) {
                  wx.showToast({
                    title: '申請傳送失敗',
                    icon: 'none',
                    duration: 2000
                  });
                }
              });
            } else {
              wx.showToast({
                title: '專案釋出人資訊未找到',
                icon: 'none',
                duration: 2000
              });
            }
          },
          fail(err) {
            wx.showToast({
              title: '獲取專案釋出人資訊失敗',
              icon: 'none',
              duration: 2000
            });
          }
        });
      },
    

    該函式用於申請加入專案。首先獲取當前使用者資訊和專案資料,然後查詢專案釋出人的資訊,並向其傳送申請訊息。如果聊天記錄已存在,則更新記錄;否則,建立新記錄。

    4. 聯絡專案釋出人
      contactUs() {
        const app = getApp();
        const userInfo = app.globalData.userInfo;
        const project = this.data.project;
        const db = wx.cloud.database();
    
        db.collection('chat_user').where({
          account_id: project.user
        }).get({
          success(res) {
            if (res.data.length > 0) {
              const userB = res.data[0];
    
              db.collection('chat_record').where({
                userA_id: userInfo._id,
                userB_id: userB._id,
                friend_status: true
              }).get({
                success(res) {
                  if (res.data.length > 0) {
                    wx.navigateTo({
                      url: '/pages/chat/chat?id=' + res.data[0]._id
                    });
                  } else {
                    const newMessage = {
                      id: userInfo._id,
                      text: `${userInfo.account_id} 申請新增你為好友`,
                      time: new Date()
                    };
    
                    const newRecord = {
                      userA_id: userInfo._id,
                      userA_account_id: userInfo.account_id,
                      userA_avatarUrl: userInfo.avatarUrl,
                      userB_id: userB._id,
                      userB_account_id: userB.account_id,
                      userB_avatarUrl: userB.avatarUrl,
                      projectId: project._id,
                      record: [newMessage],
                      friend_status: false
                    };
    
                    db.collection('chat_record').add({
                      data: newRecord,
                      success(res) {
                        wx.showToast({
                          title: '好友申請已傳送!',
                          icon: 'success',
                          duration: 2000
                        });
                      },
                      fail(err) {
                        wx.showToast({
                          title: '好友申請傳送失敗',
                          icon: 'none',
                          duration: 2000
                        });
                      }
                    });
                  }
                },
                fail(err) {
                  wx.showToast({
                    title: '檢查好友狀態失敗',
                    icon: 'none',
                    duration: 2000
                  });
                }
              });
            } else {
              wx.showToast({
                title: '專案釋出人資訊未找到',
                icon: 'none',
                duration: 2000
              });
            }
          },
          fail(err) {
            wx.showToast({
              title: '獲取專案釋出人資訊失敗',
              icon: 'none',
              duration: 2000
            });
          }
        });
      }
    });
    

    該函式用於聯絡專案釋出人。首先獲取當前使用者資訊和專案資料,然後查詢專案釋出人的資訊。如果雙方已是好友,則跳轉到聊天頁面;否則,傳送好友申請。

附加特點設計與展示

homepage.js 檔案中的 loadProjects() 函式實現了將專案按照新到舊的順序排列。以下是對該函式的詳細解釋:

  1. 獲取資料庫例項

    const db = wx.cloud.database();
    

    透過 wx.cloud.database() 方法獲取雲資料庫例項。

  2. 查詢專案並按上傳時間降序排列

    db.collection('projects').orderBy('uploadTime', 'desc').get({
    

    使用 orderBy('uploadTime', 'desc') 方法按上傳時間降序排列專案。這樣,最新上傳的專案會排在最前面。

  3. 處理查詢結果

    success: res => {
      this.setData({
        results: res.data,
        noResults: false // 確保在載入專案時不顯示“未找到相關專案”提示
      });
      wx.stopPullDownRefresh(); // 停止下拉重新整理
    },
    

    在查詢成功的回撥函式中,將查詢到的專案資料儲存在頁面的資料物件 results 中,並確保不顯示“未找到相關專案”的提示。同時,呼叫 wx.stopPullDownRefresh() 方法停止下拉重新整理。

  4. 處理查詢失敗

    fail: err => {
      console.error('載入專案失敗:', err);
      wx.stopPullDownRefresh(); // 停止下拉重新整理
    }
    

    在查詢失敗的回撥函式中,輸出錯誤資訊到控制檯,並呼叫 wx.stopPullDownRefresh() 方法停止下拉重新整理。

完整的 loadProjects() 函式如下:

loadProjects() {
  const db = wx.cloud.database();
  db.collection('projects').orderBy('uploadTime', 'desc').get({
    success: res => {
      this.setData({
        results: res.data,
        noResults: false // 確保在載入專案時不顯示“未找到相關專案”提示
      });
      wx.stopPullDownRefresh(); // 停止下拉重新整理
    },
    fail: err => {
      console.error('載入專案失敗:', err);
      wx.stopPullDownRefresh(); // 停止下拉重新整理
    }
  });
}

成果展示如下:

原本的首頁:

釋出新專案:

新發布的專案展示在專案列表的上方,且在搜尋框中顯示新發布的專案名稱:

目錄結構

# CDCP

## 目錄結構

│  .eslintrc.js
│  app.js
│  app.json
│  app.wxss
│  LICENSE
│  package-lock.json
│  package.json
│  project.config.json
│  project.private.config.json
│  README.md
│  sitemap.json
│   
├─.idea
│  │  .gitignore
│  │  CDCP.iml
│  │  misc.xml
│  │  modules.xml
│  │  workspace.xml
│  │  
│  └─inspectionProfiles
│          profiles_settings.xml
│          
├─images
│  │  1.png
│  │  2.png
│  │  3.png
│  │  no_message.png
│  │  
│  └─icon
│          friends.png
│          index.png
│          message.png
│          unknown.png
│          user.png
│          
├─pages
│  ├─changeInformation
│  │      changeInformation.js
│  │      changeInformation.json
│  │      changeInformation.wxml
│  │      changeInformation.wxss
│  │      # 修改資訊頁面
│  │      # 使用者可以在此頁面修改個人資訊
│  │      
│  ├─chat
│  │      chat.js
│  │      chat.json
│  │      chat.wxml
│  │      chat.wxss
│  │      # 聊天頁面
│  │      # 使用者可以在此頁面與其他使用者進行聊天
│  │      
│  ├─friends
│  │      friends.js
│  │      friends.json
│  │      friends.wxml
│  │      friends.wxss
│  │      # 好友頁面
│  │      # 使用者可以在此頁面檢視和管理好友列表
│  │      
│  ├─homepage
│  │      homepage.js
│  │      homepage.json
│  │      homepage.wxml
│  │      homepage.wxss
│  │      # 首頁
│  │      # 顯示專案列表和導航到其他頁面的入口
│  │      
│  ├─IdentityAuthentication
│  │  │  IdentityAuthentication.js
│  │  │  IdentityAuthentication.json
│  │  │  IdentityAuthentication.wxml
│  │  │  IdentityAuthentication.wxss
│  │  │  # 身份認證頁面
│  │  │  # 使用者可以在此頁面進行身份認證
│  │  │  
│  │  ├─student
│  │  │      student.js
│  │  │      student.json
│  │  │      student.wxml
│  │  │      student.wxss
│  │  │      # 學生認證頁面
│  │  │      # 學生使用者可以在此頁面進行身份認證
│  │  │      
│  │  └─teacher
│  │          teacher.js
│  │          teacher.json
│  │          teacher.wxml
│  │          teacher.wxss
│  │          # 教師認證頁面
│  │          # 教師使用者可以在此頁面進行身份認證
│  │          
│  ├─login
│  │      login.js
│  │      login.json
│  │      login.wxml
│  │      login.wxss
│  │      # 登入頁面
│  │      # 使用者可以在此頁面進行登入操作
│  │      
│  ├─message
│  │      message.js
│  │      message.json
│  │      message.wxml
│  │      message.wxss
│  │      # 訊息頁面
│  │      # 使用者可以在此頁面檢視和管理訊息
│  │      
│  ├─mp
│  │      mp.js
│  │      mp.json
│  │      mp.wxml
│  │      mp.wxss
│  │      # 使用者釋出的專案詳情頁面
│  │ 
│  │      
│  ├─myprojects
│  │      myprojects.js
│  │      myprojects.json
│  │      myprojects.wxml
│  │      myprojects.wxss
│  │      # 我的專案頁面
│  │      # 使用者可以在此頁面檢視和管理自己的專案
│  │      
│  ├─projectintro
│  │      projectintro.js
│  │      projectintro.json
│  │      projectintro.wxml
│  │      projectintro.wxss
│  │      # 專案介紹頁面
│  │      # 顯示專案的詳細資訊
│  │      
│  ├─publishsubject
│  │      publishsubject.js
│  │      publishsubject.json
│  │      publishsubject.wxml
│  │      publishsubject.wxss
│  │      # 釋出專案頁面
│  │      # 使用者可以在此頁面釋出新專案
│  │      
│  ├─register
│  │      register.js
│  │      register.json
│  │      register.wxml
│  │      register.wxss
│  │      # 註冊頁面
│  │      # 使用者可以在此頁面進行註冊操作
│  │      
│  ├─searchinfo
│  │      searchinfo.js
│  │      searchinfo.json
│  │      searchinfo.wxml
│  │      searchinfo.wxss
│  │      # 搜尋頁面
│  │      # 使用者可以在此頁面搜尋專案資訊
│  │      
│  └─user
│          user.js
│          user.json
│          user.wxml
│          user.wxss
│          # 使用者頁面
│          # 顯示使用者的個人資訊和設定
│          
└─utils
        util.js
        # 工具模組
        # 包含一些通用的工具函式

單元測試

選用的測試工具:使用 jest 作為測試框架

使用步驟
  1. 在專案中安裝了 jest npm install --save-dev jest

  2. 在專案根目錄下建立一個 tests 資料夾,並在其中建立一個 homepage.test.js 檔案。 在homepage.test.js檔案中,編寫以下測試程式碼

    // __tests__/homepage.test.js
    const wx = {
      cloud: {
        database: jest.fn().mockReturnValue({
          collection: jest.fn().mockReturnValue({
            orderBy: jest.fn().mockReturnThis(),
            limit: jest.fn().mockReturnThis(),
            get: jest.fn().mockImplementation(({ success, fail }) => {
              success({ data: [{ projectName: 'Test Project', uploadTime: new Date() }] });
            })
          })
        })
      },
      stopPullDownRefresh: jest.fn(),
      navigateTo: jest.fn()
    };
    
    global.wx = wx;
    
    const homepage = require('../pages/homepage/homepage.js');
    
    describe('homepage.js', () => {
      beforeEach(() => {
        homepage.setData = jest.fn();
      });
    
      test('loadProjects should load projects and set data', () => {
        homepage.loadProjects();
        expect(homepage.setData).toHaveBeenCalledWith({
          results: [{ projectName: 'Test Project', uploadTime: new Date() }],
          noResults: false
        });
        expect(wx.stopPullDownRefresh).toHaveBeenCalled();
      });
    
      test('loadLatestProject should load the latest project and set data', () => {
        homepage.loadLatestProject();
        expect(homepage.setData).toHaveBeenCalledWith({
          latestProjectName: 'Test Project'
        });
      });
    });
    
  3. package.json 檔案中新增以下指令碼

    "scripts": {
      "test": "jest"
    }
    
  4. 執行測試 npm test

構造測試資料的思路以及未來應對策略
  1. 覆蓋常見情況

    • 正常情況:測試函式在正常情況下的行為,例如資料庫查詢成功並返回預期資料。
    • 邊界情況:測試函式在邊界條件下的行為,例如資料庫查詢返回空資料或只有一條資料。
    • 異常情況:測試函式在異常情況下的行為,例如資料庫查詢失敗或網路錯誤。
  2. 模擬外部依賴

    • 使用 jest.fn() 模擬外部依賴(如 wx 物件及其方法),確保測試環境與實際執行環境一致。
  3. 驗證函式行為

    • 驗證函式是否正確處理資料並更新頁面狀態。
    • 驗證函式是否正確處理錯誤並輸出錯誤資訊。
  4. 考慮未來擴充套件

    • 編寫靈活的測試程式碼,便於將來新增新的測試用例。
    • 使用描述性強的測試名稱,便於測試人員理解測試目的。

給出Github的程式碼簽入記錄截圖

遇到的程式碼模組異常或結對困難及解決方法

模組異常:homepage首頁的搜素框功能無法響應搜尋 ,可以在資料庫進行projectName的對比,但無法在首頁顯示搜尋結果

解決方法:發現在homepage存在多個元件重疊,故無法正常顯示搜尋結果。在主頁的搜尋框功能中,新建跳轉頁面Searchifo,在此頁面重新執行搜尋功能程式碼,解決問題。

評價你的隊友

結伴隊友:102202113許煊宇

值得學習的地方:

  1. 技術能力:他對JavaScript和相關框架(如WeChat小程式)的掌握非常熟練,能夠快速解決各種技術難題。
  2. 團隊合作:他在團隊合作中表現出色,能夠積極溝通,分享自己的見解和經驗,幫助團隊成員共同進步。

需要改進的地方:

  1. 程式碼質量:他編寫的程式碼簡潔、清晰、易於維護,我需要向他學習如何編寫高質量的程式碼。
  2. 問題解決能力:他在面對複雜問題時,總能冷靜分析,找到有效的解決方案。我需要學習他這種解決問題的思路和方法。

相關文章