一、简介
本次项目要求:
1、所有功能通过图形化界面操作,可以是桌面应用,可以是网站(编程语言和技术不限);
2、用户注册功能。用户提供手机号码,点击注册将收到一个注册码,用户可使用该注册码完成注册;
3、用户完成注册后,界面提示设置密码,用户输入两次密码匹配后设置密码成功。密码6-10位,必须含大小写字母和数字。用户在登录状态下可修改密码,输入正确的原密码,再输入两次相同的新密码后修改密码成功;
4、密码设置成功后,跳转到选择界面,界面显示小学、初中和高中三个选项,用户点击其中之一后,提示用户输入需要生成的题目数量;
5、用户输入题目数量后,生成一张试卷(同一张卷子不能有相同题目,题目全部为选择题),界面显示第一题的题干和四个选项,用户选择四个选项中的一个后提交,界面显示第二题,...,直至最后一题;
6、最后一题提交后,界面显示分数,分数根据答对的百分比计算;
7、用户在分数界面可选择退出或继续做题;
8、小初高数学题目要求见个人项目。
二、代码复用情况
我用的是java语言,对于图形化设计比C++方便,所以我们本次的结对编程项目大体采用JAVA。我在处理小初高数学题目的代码部分逻辑有些混乱,而队友的结构思路清晰,所以我们在本次项目的题目生成部分采用他在C++的思路,并且注册登录方延续个人项目的文件流读写操作,开始是准备使用数据库,奈何个人技术不支持~——~。
三、分工合作
我本人比较擅长写图形化界面UI的部分,所以所有显示界面都是由我来完成;队友和我一块负责后端代码实现,他实现题目的生成以及得分的判定,我负责手机验证码的发送与接受,注册登录方面
四、项目的构建流程、
(如图所示)
五、代码分析
上图是我们本次项目所用到的类的定义
- 首先是登录界面LoginUI,通过JFrame构建页面并向内添加登录和注册的按钮,并将其对应的监听器绑定按钮
public void loginUI(){
jframe = new JFrame();
jlabel1 = new JLabel("手机号:");
jlabel2 = new JLabel("密码:");
//实例化FlowLayout流式布局类的对象,指定对齐方式为居中对齐,组件之间的间隔为5个像素
flowLayout = new FlowLayout(FlowLayout.CENTER, 10,10);
jTextField = new JTextField();
jPasswordField = new JPasswordField();
jbutton1 = new JButton();
jbutton2 = new JButton();
init();
}
//类中定义初始化界面的方法
public void init() {
//设置窗体对象的属性值,标题,大小,显示位置,关闭操作,布局,禁止调整大小
jframe.setTitle("小初高数学学习软件"); //设置标题
jframe.setSize(400, 500); //设置窗体大小
jframe.setDefaultCloseOperation(3); //设置窗体关闭操作;3表示关闭窗体退出程序
jframe.setLocationRelativeTo(null); //设置窗体相对与另一个组件的居中位置,,参数null表示相对于屏幕正*
jframe.setResizable(false); //设置禁止调整窗体大小
jframe.setLayout(flowLayout); //实例化流式布局对象
jframe.setVisible(true);//设置窗体可见 JLabel jlabelIcon = new JLabel("**********************中小学数学学习系统**********************");
//设置标签大小——setSize(, )方法只对窗体有效,如果想设置组件大小只能用Dimension
//将jlabelICon标签添加到窗体上
jframe.add(jlabelIcon); //实例化JLabel标签对象,显示“账号:”
//将jlabelName标签添加到窗体上
jframe.add(jlabel1); //实例化JTextField标签对象
Dimension dimension1 = new Dimension(300, 30);
//jtextName.serSize()方法支队*容器有效,其他组件使用无效
jTextField.setPreferredSize(dimension1); //设置除了*容器组件 其他组件的大小
jframe.add(jTextField); //实例化JLabel标签对象,显示“密码:”
//将jlabelPass标签添加到窗体上
jframe.add(jlabel2); //实例化JPasswordField
//设置大小
jPasswordField.setPreferredSize(dimension1);
//添加jPasswordField到窗体上
jframe.add(jPasswordField); //设置按钮显示内容
Dimension dimension2 = new Dimension(100, 30);
jbutton1.setText("登录");
//设置按钮大小
jbutton1.setSize(dimension2);
jframe.add(jbutton1); Dimension dimension3 = new Dimension(100, 30);
jbutton2.setText("注册");
//设置按钮大小
jbutton2.setSize(dimension3);
jframe.add(jbutton2); //登录
SignListener signListener = new SignListener();
signListener.setSignListener(jTextField, jPasswordField);
jbutton1.addActionListener(signListener); //注册
EnrollListener enrollListener = new EnrollListener();
jbutton2.addActionListener(enrollListener); jframe.setVisible(true);//设置窗体可见 } - 登录功能,FunctionUI里实现了出题,修改密码,重新登陆的功能,并用相应地监听器绑定
public void functionUI(){
jframe = new JFrame();
flowLayout = new FlowLayout(FlowLayout.CENTER, 10,10);
jlabel1 = new JLabel("请选择如下功能:");
jlabel2 = new JLabel("————————————————————————");
jbutton1 = new JButton();
jbutton2 = new JButton();
jbutton3 = new JButton(); jframe.setTitle("功能界面"); //设置标题
jframe.setSize(300, 300); //设置窗体大小
jframe.setDefaultCloseOperation(3); //设置窗体关闭操作;3表示关闭窗体退出程序
jframe.setLocationRelativeTo(null); //设置窗体相对与另一个组件的居中位置,,参数null表示相对于屏幕正*
jframe.setResizable(false); //设置禁止调整窗体大小
jframe.setLayout(flowLayout); //实例化流式布局对象
jframe.add(jlabel1);
jframe.add(jlabel2);
jframe.setVisible(true);//设置窗体可见 //设置按钮显示内容
Dimension dimension1 = new Dimension(200, 50); jbutton1.setText("出题");
//设置按钮大小
jbutton1.setSize(dimension1);
jframe.add(jbutton1); jbutton2.setText("重新登录");
//设置按钮大小
jbutton2.setSize(dimension1);
jframe.add(jbutton2); jbutton3.setText("修改密码");
//设置按钮大小
jbutton3.setSize(dimension1);
jframe.add(jbutton3); SetProblemListener setProblemListener = new SetProblemListener();
jbutton1.addActionListener(setProblemListener); ResignListener resignListener = new ResignListener();
jbutton2.addActionListener(resignListener); ResetPassword resetPassword = new ResetPassword();
resetPassword.setResetPW(jTextField);
jbutton3.addActionListener(resetPassword); } - 注册功能实现,使用腾讯云平台,注册账号后在pom.xml里添加腾讯云的依赖,然后通过其函数以及自己的id可以发送验证码(只需修改XXX部分就好)
public void ReceiveMassages(String number, String templateParam){
try {
/* 必要步骤:
* 实例化一个认证对象,入参需要传入腾讯云账户密钥对secretId,secretKey。
* 这里采用的是从环境变量读取的方式,需要在环境变量中先设置这两个值。
* 你也可以直接在代码中写死密钥对,但是小心不要将代码复制、上传或者分享给他人,
* 以免泄露密钥对危及你的财产安全。
* CAM密匙查询: https://console.cloud.tencent.com/cam/capi*/
Credential cred = new Credential("XXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXX"); // 实例化一个http选项,可选,没有特殊需求可以跳过
HttpProfile httpProfile = new HttpProfile();
// 设置代理
// httpProfile.setProxyHost("真实代理ip");
// httpProfile.setProxyPort(真实代理端口);
/* SDK默认使用POST方法。
* 如果你一定要使用GET方法,可以在这里设置。GET方法无法处理一些较大的请求 */
httpProfile.setReqMethod("POST");
/* SDK有默认的超时时间,非必要请不要进行调整
* 如有需要请在代码中查阅以获取最新的默认值 */
httpProfile.setConnTimeout(60);
/* SDK会自动指定域名。通常是不需要特地指定域名的,但是如果你访问的是金融区的服务
* 则必须手动指定域名,例如sms的上海金融区域名: sms.ap-shanghai-fsi.tencentcloudapi.com */
httpProfile.setEndpoint("sms.tencentcloudapi.com"); /* 非必要步骤:
* 实例化一个客户端配置对象,可以指定超时时间等配置 */
ClientProfile clientProfile = new ClientProfile();
/* SDK默认用TC3-HMAC-SHA256进行签名
* 非必要请不要修改这个字段 */
clientProfile.setSignMethod("HmacSHA256");
clientProfile.setHttpProfile(httpProfile);
/* 实例化要请求产品(以sms为例)的client对象
* 第二个参数是地域信息,可以直接填写字符串ap-guangzhou,或者引用预设的常量 */
SmsClient client = new SmsClient(cred, "ap-guangzhou",clientProfile);
/* 实例化一个请求对象,根据调用的接口和实际情况,可以进一步设置请求参数
* 你可以直接查询SDK源码确定接口有哪些属性可以设置
* 属性可能是基本类型,也可能引用了另一个数据结构
* 推荐使用IDE进行开发,可以方便的跳转查阅各个接口和数据结构的文档说明 */
SendSmsRequest req = new SendSmsRequest(); /* 填充请求参数,这里request对象的成员变量即对应接口的入参
* 你可以通过官网接口文档或跳转到request对象的定义处查看请求参数的定义
* 基本类型的设置:
* 帮助链接:
* 短信控制台: https://console.cloud.tencent.com/smsv2
* sms helper: https://cloud.tencent.com/document/product/382/3773 */ /* 短信应用ID: 短信SdkAppId在 [短信控制台] 添加应用后生成的实际SdkAppId,示例如1400006666 */
String sdkAppId = "XXXXXXXXXX";
req.setSmsSdkAppId(sdkAppId); /* 短信签名内容: 使用 UTF-8 编码,必须填写已审核通过的签名,签名信息可登录 [短信控制台] 查看 */
String signName = "XXXXXXXX";
req.setSignName(signName); /* 国际/港澳台短信 SenderId: 国内短信填空,默认未开通,如需开通请联系 [sms helper] */
String senderid = "";
req.setSenderId(senderid); /* 用户的 session 内容: 可以携带用户侧 ID 等上下文信息,server 会原样返回 */
String sessionContext = "xxx";
req.setSessionContext(sessionContext); /* 短信号码扩展号: 默认未开通,如需开通请联系 [sms helper] */
String extendCode = "";
req.setExtendCode(extendCode); /* 模板 ID: 必须填写已审核通过的模板 ID。模板ID可登录 [短信控制台] 查看 */
String templateId = "1146916";
req.setTemplateId(templateId); /* 下发手机号码,采用 E.164 标准,+[国家或地区码][手机号]
* 示例如:+8613711112222, 其中前面有一个+号 ,86为国家码,13711112222为手机号,最多不要超过200个手机号 */
String[] phoneNumberSet = {number};
req.setPhoneNumberSet(phoneNumberSet); /* 模板参数: 若无模板参数,则设置为空 */
String[] templateParamSet = {templateParam, "2"};
req.setTemplateParamSet(templateParamSet); /* 通过 client 对象调用 SendSms 方法发起请求。注意请求方法名与请求对象是对应的
* 返回的 res 是一个 SendSmsResponse 类的实例,与请求对象对应 */
SendSmsResponse res = client.SendSms(req); // 输出json格式的字符串回包
System.out.println(SendSmsResponse.toJsonString(res)); // 也可以取出单个值,你可以通过官网接口文档或跳转到response对象的定义处查看返回字段的定义
System.out.println(res.getRequestId()); } catch (TencentCloudSDKException e) {
e.printStackTrace();
}
}
六、问题与解决
1.手机验证码模块:这个阶段我们在使用腾讯云的函数时老是报错,而且我们都是按照流程走的,当时一脸懵,后来看了好多教程才发现,在pom.xml里的依赖版本不一样,基本上每个教程的版本都有所变化,所以我们在腾讯云上查询了最新的版本后解决此问题
2.修改密码模块:我们注册后都是通过文件流保存在了txt文档里,每次登录要从文档里读取数据并进行判断,而修改密码也是需要判断当前号码对应的原密码是否为文档里存储的密码,并且要将新修改的密码存到文档里并将号码之前的记录删除,本来我们想着是直接将文档里对应位置的密码直接删除,但是删除完后向文档里添加新的密码时会重复出现两次,查阅资料后发现我们写入文档函数中文件流没有close(),而在下一次写在本文档内时会重复写两次,所以我们对写入函数进行修改后解决问题。
3.得分模块:我们当时把重心放在计算式子的得分函数上,而忽略了判断题目的数量这部分,导致出现式子已经全部算完并记录得分后,还会出现0得分的现象,这导致后续在显示得分数时与做对题目数量不匹配,所以我们在其中加了限制了条件,当题目数超过给定的题目数量后会自动停止
七、总结
本次结对项目对于我和我队友这俩菜鸡来说还是挺有挑战性的,在平时的代码书写也强制自己规范一点,这次项目过程中,体会到了小团队合作的益处,如果我自己一个人的话绝对完不成,俩人分工合作效率翻倍,并且还提高了沟通协调能力,虽然最后功能还是没有实现完全,但是这种经过自己努力思考和交流得出的成品还是值得骄傲的!