第二次结对作业

这个作业属于哪个课程 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. 问题解决能力:他在面对复杂问题时,总能冷静分析,找到有效的解决方案。我需要学习他这种解决问题的思路和方法。
posted @ 2024-10-10 20:11  chenoojkk  阅读(24)  评论(0编辑  收藏  举报