软件工程第二次结对作业
这个作业属于哪个课程 | 软件工程 |
---|---|
这个作业要求在哪里 | 作业要求链接 |
这个作业的目标 | 根据上次作业的原型,设计一个微信小程序 |
学号 | 102202111 |
合作伙伴 | 102202103王文豪 |
Github仓库地址 | https://github.com/noen-hh/102202111-102202103.git |
(小程序还在审核中,等审核完就可以扫码体验)
1.具体分工
王文豪(前端开发):
1,负责WXML和WXSS进行主页、项目、项目创建与发布模块页面布局与设计,包括导航栏、项目列表等关键UI组件。
2,实现交互效果,如按钮点击、动态页面跳转等。
刘哲睿(后端开发):
1,负责管理用户的注册登录信息,确保用户登录信息不会丢失。
2,负责编写和维护微信云函数,实现获取项目详情、申请加入/退出项目、删除项目以及更新项目状态等功能。
3,设计和优化数据库结构,确保数据的高效存储与检索,通过数据库实现项目的增删改查。
2.PSP表格
PSP2.1 | Personal Software Process Stages | 预估耗时(小时) | 实际耗时(小时) |
---|---|---|---|
Planning | 计划 | 3 | 4 |
Estimate | 估计这个任务需要多少时间 | 70 | 102 |
Development | 开发 | 24 | 40 |
Analysis | 需求分析 (包括学习新技术) | 4 | 6 |
Design Spec | 生成设计文档 | 2 | 2 |
Design Review | 设计复审 | 1 | 2 |
Coding Standard | 代码规范 (为目前的开发制定合适的规范) | 3 | 4 |
Design | 具体设计 | 8 | 10 |
Coding | 具体编码 | 8 | 10 |
Code Review | 代码复审 | 4 | 6 |
Test | 测试(自我测试,修改代码,提交修改) | 5 | 6 |
Reporting | 报告 | 2 | 3 |
Test Repor | 测试报告 | 2 | 3 |
Size Measurement | 计算工作量 | 2 | 3 |
Postmortem&Process Improvement Plan | 事后总结, 并提出过程改进计划 | 2 | 3 |
合计 | 70 | 102 |
3.解题思路描述与设计实现说明
-
代码实现思路,文字描述
通过微信开发者工具创建一个可供多人交流合作的项目平台小程序,在该项目平台上,用户在注册一个账号登陆后,可以实现自己项目的创建与发布,参加他人的项目,修改与删除自己的项目。首先,用户登陆进入后处在主页中,主页中可以查看已发布的项目,顶部附带搜索框可按项目名称关键词进行查找,点击相应的项目跳转进入后可了解项目详情,如项目的具体任务,参加该项目所需的技能,项目内已参加的人数等。根据个人的意愿可在相应的项目内进行项目申请,项目的发布人将会受到申请者的申请,并根据自身意愿进行回应。在主页下方的导航栏中,点击“项目”即可跳转到“我的项目”,用户可以看到自己已经参加的项目并且进行查看,同时可以对项目进行删除操作,页面右下角有个项目创建按钮,用户可以根据自己的自身情况选择是否创建自己的项目。点击导航栏中的“我的”前往个人中心,可对自身的个人信息进行查看和修改,并且附带与客服对话的功能。
-
实现的方法
-
云函数
-
数据库
-
前端:使用WXML和WXSS设计页面布局和样式,使用JavaScript编写页面逻辑,处理用户交互和数据绑定
-
后端:编写后端业务逻辑,处理数据的增删改查。
-
关键功能实现
-
注册登录
-
项目创建发布,申请,修改,删除
-
查看他人信息
-
关键实现的流程图
-
我们认为重要的/有价值的代码片段
- 登陆页面:
// 验证密码是否匹配
if (password !== confirmPassword) {
wx.showToast({
title: '密码不一致',
icon: 'none'
});
return;
}
// 调用后端API进行注册
// ...
}
Page({
data: {
phone: '',
password: ''
},
// 绑定手机号输入
bindPhoneInput: function(e) {
this.setData({
phone: e.detail.value
});
},
// 绑定密码输入
bindPasswordInput: function(e) {
this.setData({
password: e.detail.value
});
},
// 提交登录
submitLogin: function() {
const { phone, password } = this.data;
// 输入验证
if (!phone || !password) {
wx.showToast({
title: '请输入手机号和密码',
icon: 'none'
});
return;
}
// 从云数据库中读取用户数据
const db = wx.cloud.database();
db.collection('user').where({
phone: phone
}).get()
.then(res => {
if (res.data.length > 0) {
// 找到用户,进行密码匹配
const user = res.data[0];
if (user.password === password) {
wx.showToast({
title: '登录成功',
});
wx.navigateTo({
url: '/pages/home/home' // 登录成功后跳转到主页
});
} else {
wx.showToast({
title: '密码错误',
icon: 'none'
});
}
} else {
wx.showToast({
title: '用户不存在',
icon: 'none'
});
}
})
.catch(err => {
wx.showToast({
title: '查询失败',
icon: 'none'
});
console.error(err);
});
}
});
-
代码解释:类似于注册功能,使用 data 对象存储用户输入的手机号和密码,在 submitLogin 函数中,检查手机号和密码是否已填写,如果任一字段为空,显示提示信息。登录主要靠云数据库查询,查询云数据库中是否存在匹配的手机号。
-
项目搜索:
// 处理搜索框输入事件
onSearchInput(e) {
this.setData({
searchText: e.detail.value,
});
},
// 处理搜索按钮点击事件
onSearch() {
const { searchText, works } = this.data;
// 如果搜索框为空,展示所有项目
if (!searchText) {
this.setData({ filteredWorks: works });
return;
}
const filteredWorks = works.filter(work =>
this.matchTitle(work.title, searchText) // 使用匹配函数
);
this.setData({ filteredWorks });
},
// 匹配项目名称的函数
matchTitle(title, searchText) {
let matchCount = 0;
for (let char of searchText) {
if (title.includes(char)) {
matchCount++;
}
if (matchCount >= 2) return true;
}
return false;
},
代码解释:当用户点击搜索按钮时,onSearch 函数会被触发。这个函数首先检查搜索框是否为空。如果为空,则将所有项目设置为过滤后的项目列表。如果不为空,则使用 filter 方法和 matchTitle 函数来过滤项目列表,只保留那些项目名称中包含搜索关键词的项目。
- 获取项目详情:
addProject: function() {
// 跳转到添加项目的页面
wx.navigateTo({
url: '/pages/project/project'
});
},
goToProjectDetail: function(e) {
// 从事件对象获取项目ID
const projectId = e.currentTarget.dataset.id;
// 跳转到项目详情页面
wx.navigateTo({
url: '/pages/project-detail/project-detail?id=${projectId}'
});
}
代码解释:使用 wx.navigateTo 方法可以实现页面的跳转,触发事件的元素中获取 data-id 属性,该属性包含了项目的ID,然后使用这个ID来构建跳转URL。
项目申请:
applyForProject: function() {
// 处理项目申请逻辑
wx.showModal({
title: '申请项目',
content: '您确定要申请加入这个项目吗?',
success: function(res) {
if (res.confirm) {
console.log('用户点击确定');
// 发起网络请求提交申请
wx.showToast({
title: '申请已提交',
icon: 'success',
duration: 2000
});
// 可以在这里添加跳转逻辑,例如返回项目列表或到其他页面
wx.reLaunch({
url: '/pages/home/home'
});
} else if (res.cancel) {
console.log('用户点击取消');
}
}
});
}
代码解释:通过一个模态对话框询问用户是否确定要申请加入某个项目,并根据不同的用户响应执行不同的操作。
-
附加特点设计与展示
- 在register.js中通过 bindPhoneInput、bindPasswordInput 和 bindConfirmPasswordInput 函数,将用户在输入框中输入的内容实时更新到页面的 data 对象中。在 submitRegister 函数中,首先检查所有字段是否已填写。如果有任何字段为空,显示提示信息并终止进一步的操作,使用正则表达式 ^1\d{10}$ 验证手机号是否为11位数字,且以1开头。
- 以下是代码:
Page({
data: {
phone: '',
password: '',
confirmPassword: ''
},
// 绑定手机号输入
bindPhoneInput: function(e) {
this.setData({
phone: e.detail.value
});
},
// 绑定密码输入
bindPasswordInput: function(e) {
this.setData({
password: e.detail.value
});
},
// 绑定确认密码输入
bindConfirmPasswordInput: function(e) {
this.setData({
confirmPassword: e.detail.value
});
},
// 提交注册
submitRegister: function() {
const { phone, password, confirmPassword } = this.data;
// 验证输入
if (!phone || !password || !confirmPassword) {
wx.showToast({
title: '请填写所有字段',
icon: 'none'
});
return;
}
// 验证手机号码
if (!/^1\d{10}$/.test(phone)) {
wx.showToast({
title: '手机号必须为11位',
icon: 'none'
});
return;
}
-
实现成果展示
-
目录结构
miniprogram
├── images/ # 存放图片资源的目录
│ ├── project.png
│ ├── avatar.png
│ └── 其他图片文件...
├── pages/ # 存放小程序页面文件的目录
│ ├── home/ # 主页
│ │ ├── home.js
│ │ ├── home.json
│ │ ├── home.wxss
│ │ └── home.wxml
│ ├── register/ # 注册页面
│ │ ├── register.js
│ │ ├── register.json
│ │ ├── register.wxss
│ │ └── register.wxml
│ ├── chat/ # 聊天页面
│ │ ├── chat.js
│ │ ├── chat.json
│ │ ├── chat.wxss
│ │ └── chat.wxml
│ ├── login/ # 登录页面
│ │ ├── login.js
│ │ ├── login.json
│ │ ├── login.wxss
│ │ └── login.wxml
│ ├── profile/ # 用户个人资料页面
│ │ ├── profile.js
│ │ ├── profile.json
│ │ ├── profile.wxss
│ │ └── profile.wxml
│ ├── project/ # 项目相关页面
│ │ ├── project.js
│ │ ├── project.json
│ │ ├── project.wxss
│ │ └── project.wxml
│ ├── project_1/ # 项目1页面
│ │ ├── project_1.js
│ │ ├── project_1.json
│ │ ├── project_1.wxss
│ │ └── project_1.wxml
│ ├── project_2/ # 项目2页面
│ │ ├── project_2.js
│ │ ├── project_2.json
│ │ ├── project_2.wxss
│ │ └── project_2.wxml
│ ├── project-detail/ # 项目详情页面
│ │ ├── project-detail.js
│ │ ├── project-detail.json
│ │ ├── project-detail.wxss
│ │ └── project-detail.wxml
│ └── utils/ # 工具函数目录
│ ├── utils.js
│ └── typings/ # TypeScript 类型声明文件
│ └── index.d.ts
├── app.json # 小程序全局配置文件
├── app.ts # 小程序入口文件(TypeScript)
├── app.wxss # 小程序全局样式文件
├── envList.js # 环境变量配置文件
└── .gitignore # Git 忽略文件配置
-
单元测试
-
学习单元测试
理解概念:了解什么是单元测试,以及它在软件开发中的重要性。
阅读文档:学习测试框架的官方文档,了解如何编写测试用例。
观看教程:通过在线教程和课程学习单元测试的最佳实践。
实践操作:在实际项目中编写单元测试,从简单到复杂逐步掌握。
代码审查:学习他人的测试代码,理解不同的测试策略和方法。 -
使用步骤
1,在项目中安装了 jest,npm init -y npm install --save-dev jest
2,部分单元测试代码
// projectPublish.test.js
const { publishProject } = require('./projectPublish');
describe('publishProject', () => {
test('should publish a project successfully', async () => {
const projectData = {
name: '新项目',
summary: '这是一个新项目的简介',
};
const result = await publishProject(projectData);
expect(result.success).toBe(true);
expect(result.message).toBe('项目发布成功');
});
test('should throw an error if project name or summary is missing', () => {
expect(() => publishProject({ name: '新项目' })))
.toThrow('项目名称和简介不能为空');
expect(() => publishProject({ summary: '这是一个新项目的简介' })))
.toThrow('项目名称和简介不能为空');
});
});
-
构造测试数据的思路
1,边界值:测试数据的边界值,例如数组的空值、最大长度、最小长度等。
2,异常值:测试异常或错误输入,例如无效的输入、负数等。
3,典型值:测试正常使用情况下的典型值。
4,组合值:测试不同数据组合的情况。
5,依赖关系:如果函数之间存在依赖关系,构造测试数据时需要考虑这些依赖。 -
Github的代码签入记录截图
- 请合理记录commit信息
-
遇到的代码模块异常或结对困难及解决方法
- 问题描述
在开发微信小程序项目时,数据库出现了某些问题,导致数据查询异常,用户和项目数据无法正确存储及读取。 - 做过哪些尝试
检查数据库连接:确保数据库服务正在运行,并且小程序的数据库连接配置是正确的。 - 是否解决
已解决 - 有何收获
问题解决能力:提升了定位和解决复杂问题的能力。
沟通协作:在结对编程中,我们学会了更好地与队友沟通和协作,共同面对挑战。 -
评价你的队友
结对队友:102202111刘哲睿
- 值得学习的地方
1.技术能力:他对JavaScript和相关框架(如WeChat小程序)的掌握非常熟练,能够快速解决各种技术难题。
2.团队合作:他在团队合作中表现出色,能够积极沟通,分享自己的见解和经验,帮助团队成员共同进步。 - 需要改进的地方
1.在项目开发过程中,他没有很好地管理时间,提前规划每个阶段的任务,导致整体的开发效率较慢。
2.在编写代码时,他有时没有充分利用已有的代码模块,导致一些功能的重复编写。提高代码的复用性可以减少工作量并降低出错率.