這個作業屬於哪個課程 | 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
檔案中,透過onLoad
和onShow
方法從全域性資料中獲取使用者資訊和聊天記錄,並繫結到頁面上。使用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()
函式實現了將專案按照新到舊的順序排列。以下是對該函式的詳細解釋:
-
獲取資料庫例項:
const db = wx.cloud.database();
透過
wx.cloud.database()
方法獲取雲資料庫例項。 -
查詢專案並按上傳時間降序排列:
db.collection('projects').orderBy('uploadTime', 'desc').get({
使用
orderBy('uploadTime', 'desc')
方法按上傳時間降序排列專案。這樣,最新上傳的專案會排在最前面。 -
處理查詢結果:
success: res => { this.setData({ results: res.data, noResults: false // 確保在載入專案時不顯示“未找到相關專案”提示 }); wx.stopPullDownRefresh(); // 停止下拉重新整理 },
在查詢成功的回撥函式中,將查詢到的專案資料儲存在頁面的資料物件
results
中,並確保不顯示“未找到相關專案”的提示。同時,呼叫wx.stopPullDownRefresh()
方法停止下拉重新整理。 -
處理查詢失敗:
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 作為測試框架
使用步驟
-
在專案中安裝了 jest
npm install --save-dev jest
-
在專案根目錄下建立一個 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' }); }); });
-
在
package.json
檔案中新增以下指令碼"scripts": { "test": "jest" }
-
執行測試
npm test
構造測試資料的思路以及未來應對策略
-
覆蓋常見情況:
- 正常情況:測試函式在正常情況下的行為,例如資料庫查詢成功並返回預期資料。
- 邊界情況:測試函式在邊界條件下的行為,例如資料庫查詢返回空資料或只有一條資料。
- 異常情況:測試函式在異常情況下的行為,例如資料庫查詢失敗或網路錯誤。
-
模擬外部依賴:
- 使用
jest.fn()
模擬外部依賴(如wx
物件及其方法),確保測試環境與實際執行環境一致。
- 使用
-
驗證函式行為:
- 驗證函式是否正確處理資料並更新頁面狀態。
- 驗證函式是否正確處理錯誤並輸出錯誤資訊。
-
考慮未來擴充套件:
- 編寫靈活的測試程式碼,便於將來新增新的測試用例。
- 使用描述性強的測試名稱,便於測試人員理解測試目的。
給出Github
的程式碼簽入記錄截圖
遇到的程式碼模組異常或結對困難及解決方法
模組異常:homepage首頁的搜素框功能無法響應搜尋 ,可以在資料庫進行projectName
的對比,但無法在首頁顯示搜尋結果
解決方法:發現在homepage存在多個元件重疊,故無法正常顯示搜尋結果。在主頁的搜尋框功能中,新建跳轉頁面Searchifo
,在此頁面重新執行搜尋功能程式碼,解決問題。
評價你的隊友
結伴隊友:102202113許煊宇
值得學習的地方:
- 技術能力:他對JavaScript和相關框架(如WeChat小程式)的掌握非常熟練,能夠快速解決各種技術難題。
- 團隊合作:他在團隊合作中表現出色,能夠積極溝通,分享自己的見解和經驗,幫助團隊成員共同進步。
需要改進的地方:
- 程式碼質量:他編寫的程式碼簡潔、清晰、易於維護,我需要向他學習如何編寫高質量的程式碼。
- 問題解決能力:他在面對複雜問題時,總能冷靜分析,找到有效的解決方案。我需要學習他這種解決問題的思路和方法。