-
系统实现
-
注册模块的实现
用户在填写数据的时候必须与注册页面上的验证相匹配否则会注册失败,注册页面的表单验证是通过验证的,用户名的长度必须在6到18之间,邮箱必须带有@符号,密码和密码确认必须相同,你输入的密码,系统会根据你输入密码的强度给出指定的值,电话号码和身份证号码必须要求输入格式与生活相符合,当你前台验证通过的时候你点击注册,表单会将你输入的值通过name值传递给后台并保存到数据库中。
用户注册界面如下图所示。
图5-2用户注册界面
用户注册逻辑代码如下:
/**
* 注册
* @return
*/
@PostMapping("register")
public Map<String, Object> signUp(HttpServletRequest request) throws IOException {
// 查询用户
Map<String, String> query = new HashMap<>();
Map<String,Object> map = service.readBody(request.getReader());
query.put("username",String.valueOf(map.get("username")));
List list = service.selectBaseList(service.select(query, new HashMap<>()));
if (list.size()>0){
return error(30000, "用户已存在");
}
map.put("password",service.encryption(String.valueOf(map.get("password"))));
service.insert(map);
return success(1);
}
public Map<String,Object> readBody(BufferedReader reader){
BufferedReader br = null;
StringBuilder sb = new StringBuilder("");
try{
br = reader;
String str;
while ((str = br.readLine()) != null){
sb.append(str);
}
br.close();
String json = sb.toString();
return JSONObject.parseObject(json, Map.class);
}catch (IOException e){
e.printStackTrace();
}finally{
if (null != br){
try{
br.close();
}catch (IOException e){
e.printStackTrace();
}
}
}
return null;
}
public void insert(Map<String,Object> body){
E entity = JSON.parseObject(JSON.toJSONString(body),eClass);
baseMapper.insert(entity);
log.info("[{}] - 插入操作:{}",entity);
}
-
- 登录模块的实现
主要由两部分组成,登录前的登录界面以及登录后的用户功能界面。登录界面,要求用户输入用户名和密码,当用户名和密码其中一个输入为空时,给出提示“用户名,密码不能为空”。获取用户名和密码后到数据库中查找,如果用户名存在,以及对应的密码正确,则登录成功,否则登录失败。登录失败后给出提示,并把焦点停在文本框中。登录成功后将该次会话的全局变量username设置为用户名。登录成功后进入会员的功能模块,主要有会员基本信息修改,已经发布考试信息管理,发布信息,和退出功能。退出功能是清除全局变量username的值,并跳回到首页。
用户登录界面如下图所示。
图5-4用户登录界面
用户登录的逻辑代码如下所示。
/**
* 登录
* @param data
* @param httpServletRequest
* @return
*/
@PostMapping("login")
public Map<String, Object> login(@RequestBody Map<String, String> data, HttpServletRequest httpServletRequest) {
log.info("[执行登录接口]");
String username = data.get("username");
String email = data.get("email");
String phone = data.get("phone");
String password = data.get("password");
List resultList = null;
QueryWrapper wrapper = new QueryWrapper<User>();
Map<String, String> map = new HashMap<>();
if(username != null && "".equals(username) == false){
map.put("username", username);
resultList = service.selectBaseList(service.select(map, new HashMap<>()));
}
else if(email != null && "".equals(email) == false){
map.put("email", email);
resultList = service.selectBaseList(service.select(map, new HashMap<>()));
}
else if(phone != null && "".equals(phone) == false){
map.put("phone", phone);
resultList = service.selectBaseList(service.select(map, new HashMap<>()));
}else{
return error(30000, "账号或密码不能为空");
}
if (resultList == null || password == null) {
return error(30000, "账号或密码不能为空");
}
//判断是否有这个用户
if (resultList.size()<=0){
return error(30000,"用户不存在");
}
User byUsername = (User) resultList.get(0);
Map<String, String> groupMap = new HashMap<>();
groupMap.put("name",byUsername.getUserGroup());
List groupList = userGroupService.selectBaseList(userGroupService.select(groupMap, new HashMap<>()));
if (groupList.size()<1){
return error(30000,"用户组不存在");
}
UserGroup userGroup = (UserGroup) groupList.get(0);
//查询用户审核状态
if (!StringUtils.isEmpty(userGroup.getSourceTable())){
String res = service.selectExamineState(userGroup.getSourceTable(),byUsername.getUserId());
if (res==null){
return error(30000,"用户不存在");
}
if (!res.equals("已通过")){
return error(30000,"该用户审核未通过");
}
}
//查询用户状态
if (byUsername.getState()!=1){
return error(30000,"用户非可用状态,不能登录");
}
String md5password = service.encryption(password);
if (byUsername.getPassword().equals(md5password)) {
// 存储Token到数据库
AccessToken accessToken = new AccessToken();
accessToken.setToken(UUID.randomUUID().toString().replaceAll("-", ""));
accessToken.setUser_id(byUsername.getUserId());
tokenService.save(accessToken);
// 返回用户信息
JSONObject user = JSONObject.parseObject(JSONObject.toJSONString(byUsername));
user.put("token", accessToken.getToken());
JSONObject ret = new JSONObject();
ret.put("obj",user);
return success(ret);
} else {
return error(30000, "账号或密码不正确");
}
}
public String select(Map<String,String> query,Map<String,String> config){
StringBuffer sql = new StringBuffer("select ");
sql.append(config.get(FindConfig.FIELD) == null || "".equals(config.get(FindConfig.FIELD)) ? "*" : config.get(FindConfig.FIELD)).append(" ");
sql.append("from ").append("`").append(table).append("`").append(toWhereSql(query, "0".equals(config.get(FindConfig.LIKE))));
if (config.get(FindConfig.GROUP_BY) != null && !"".equals(config.get(FindConfig.GROUP_BY))){
sql.append("group by ").append(config.get(FindConfig.GROUP_BY)).append(" ");
}
if (config.get(FindConfig.ORDER_BY) != null && !"".equals(config.get(FindConfig.ORDER_BY))){
sql.append("order by ").append(config.get(FindConfig.ORDER_BY)).append(" ");
}
if (config.get(FindConfig.PAGE) != null && !"".equals(config.get(FindConfig.PAGE))){
int page = config.get(FindConfig.PAGE) != null && !"".equals(config.get(FindConfig.PAGE)) ? Integer.parseInt(config.get(FindConfig.PAGE)) : 1;
int limit = config.get(FindConfig.SIZE) != null && !"".equals(config.get(FindConfig.SIZE)) ? Integer.parseInt(config.get(FindConfig.SIZE)) : 10;
sql.append(" limit ").append( (page-1)*limit ).append(" , ").append(limit);
}
log.info("[{}] - 查询操作,sql: {}",table,sql);
return sql.toString();
}
public List selectBaseList(String select) {
List<Map<String,Object>> mapList = baseMapper.selectBaseList(select);
List<E> list = new ArrayList<>();
for (Map<String,Object> map:mapList) {
list.add(JSON.parseObject(JSON.toJSONString(map),eClass));
}
return list;
}
-
- 用户资料修改模块的实现
用户登录/注册成功之后可以修改自己的基本信息。修改页面的表单中每一个input的name值都要与实体类中的参数相匹配,在用户点击修改页面的时候,如果改后用户名与数据库里面重复了,页面会提示该用户名已经存在了,否则通过Id来查询用户,并将用户的信息修改为表单提交的数据。
-
-
用户管理模块的实现
在线考试,在线考试页面中通过查询名称、答题时长、总分、状态对在线考试进行添加或者删除操作,如图5-5所示。
图5-5在线考试界面
在线考试的逻辑代码如下:
@PostMapping("/add")
@Transactional
public Map<String, Object> add(HttpServletRequest request) throws IOException {
service.insert(service.readBody(request.getReader()));
return success(1);
}
@Transactional
public Map<String, Object> addMap(Map<String,Object> map){
service.insert(map);
return success(1);
}
科目管理,在科目管理页面中通过查询填科目、考试开始时间、考试名称、开始结束时间对科目进行添加或者删除操作,如图5-6所示。
图5-6科目管理界面
科目管理界面逻辑代码如下:
@RequestMapping(value = "/del")
@Transactional
public Map<String, Object> del(HttpServletRequest request) {
service.delete(service.readQuery(request), service.readConfig(request));
return success(1);
}
通知公告管理,在通知公告管理页面中通过查询标题,发布人、相关附件、内容对公告进行添加或者删除操作,如图5-7所示。
图5-7考试资讯管理界面
通知公告管理界面逻辑代码如下:
@RequestMapping("/get_obj")
public Map<String, Object> obj(HttpServletRequest request) {
List resultList = service.selectBaseList(service.select(service.readQuery(request), service.readConfig(request)));
if (resultList.size() > 0) {
JSONObject jsonObject = new JSONObject();
jsonObject.put("obj",resultList.get(0));
return success(jsonObject);
} else {
return success(null);
}
}
5.5教师管理模块的实现
教师登录进入系统可以查看教师信息首页,内容管理(在线考试)、更多管理(通知公告、试卷统计)进行详细操作,如图5-8所示。
图5-8教师界面
在线考试,在线考试页面中通过查询名称、答题时长、总分、状态对在线考试进行添加或者删除操作,如图5-9所示。
图5-9在线考试管理界面
图5-10修改密码界面
修改密码界面逻辑代码如下:
<mapper namespace="com.project.demo.dao.base.BaseMapper">
<select id="selectBaseList" resultType="java.util.LinkedHashMap">
${select}
</select>
<select id="selectBaseCount" resultType="Integer">
${count}
</select>
<select id="selectBaseOne" resultType="Object">
${select}
</select>
<update id="updateBaseSql">
${sql}
</update>
<delete id="deleteBaseSql">
${sql}
</delete>
</mapper>
试卷统计,在试卷统计页面中通过查询试卷名称、最高分、最低分、教师工号、平均分、考试人数对试卷统计进行添加或者删除操作,如图5-11所示。
图5-11试卷统计界面
图5-12试卷统计添加界面
试卷统计管理界面逻辑代码如下:
@PostMapping("/upload")
public Map<String, Object> upload(@RequestParam(value = "file",required=false) MultipartFile file,HttpServletRequest request) {
log.info("进入方法");
if (file.isEmpty()) {
return error(30000, "没有选择文件");
}
try {
//判断有没路径,没有则创建
String filePath = request.getSession().getServletContext().getRealPath("\\") +"upload\\";
// String filePath = System.getProperty("user.dir") + "\\target\\classes\\static\\upload\\";
File targetDir = new File(filePath);
if (!targetDir.exists() && !targetDir.isDirectory()) {
if (targetDir.mkdirs()) {
log.info("创建目录成功");
} else {
log.error("创建目录失败");
}
}
// String path = ResourceUtils.getURL("classpath:").getPath() + "static/upload/";
// String filePath = path.replace('/', '\\').substring(1, path.length());
String fileName = file.getOriginalFilename();
int lastIndexOf = fileName.lastIndexOf(".");
//获取文件的后缀名 .jpg
String suffix = fileName.substring(lastIndexOf);
fileName = IdWorker.getId()+suffix;
File dest = new File(filePath + fileName);
log.info("文件路径:{}", dest.getPath());
log.info("文件名:{}", dest.getName());
file.transferTo(dest);
JSONObject jsonObject = new JSONObject();
jsonObject.put("url", "/api/upload/" + fileName);
return success(jsonObject);
} catch (IOException e) {
log.info("上传失败:{}", e.getMessage());
}
return error(30000, "上传失败");
}
5.6管理员管理模块的实现
管理员登录进入系统可以查看管理员信息首页,用户管理(管理员、注册用户、教师)、内容管理(在线考试)、更多管理(科目管理、通知公告、试卷统计)进行详细操作,如图5-13所示。
图5-13管理员页面
科目管理,在科目管理页面中通过查询科目、考试开始时间、考试名称、开始结束时间对试卷统计进行、查询添加或者删除操作,如图5-14所示。
图5-14科目管理界面
图5-15科目添加界面
通知公告管理,在公告管理页面中通过查询标题、相关附件、内容、发布人对通知公告进行、查询添加或者删除操作,如图5-15所示。
图5-16通知公告管理界面
图5-17通知公告添加界面
试卷统计,在试卷统计页面中通过查询试卷名称、最高分、最低分、教师工号、平均分、考试人数对试卷统计进行添加或者删除操作,如图5-18所示。
图5-18试卷统计管理界面
-
系统测试
-
测试目的
对任何系统而言,测试都是必不可少的环节,测试可以发现系统存在的很多问题,所有的软件上线之前,都应该进行充足的测试之后才能保证上线后不会Bug频发,或者是功能不满足需求等问题的发生。下面分别从单元测试,功能测试和用例测试来对系统进行测试以保证系统的稳定性和可靠性。
-
-
功能测试
下表是考试管理功能的测试用例,检测了考试管理中对考试信息的增加,删除,修改,查询操作是否成功运行。观察系统的响应情况,得出该功能也达到了设计目标,系统运行正确。
前置条件;用户登录系统。
表6-1 在线考试管理的测试用例
功能描述 |
用于考试管理 |
测试目的 |
检测在线考试管理时的各种操作的运行情况 |
测试数据以及操作 |
预期结果 |
实际结果 |
点击添加在线考试,必填项合法输入,点击保存 |
提示添加成功 |
与预期结果一致 |
点击添加在线考试,必填项输入不合法,点击保存 |
提示必填项不能为空 |
与预期结果一致 |
点击修改在线考试,必填项修改为空,点击保存 |
提示必填项不能为空 |
与预期结果一致 |
点击修改在线考试,必填项输入不合法,点击保存 |
提示必填项不能为空 |
与预期结果一致 |
点击删除在线考试,选择在线考试删除 |
提示删除成功 |
与预期结果一致 |
点击搜索在线考试,输入存在的在线考试名 |
查找出在线考试 |
与预期结果一致 |
点击搜索在线考试,输入不存在的在线考试名 |
不显示在线考试 |
与预期结果一致 |
下表是试卷统计管理功能的测试用例,检测了试卷统计管理中对试卷统计信息的增加,删除,修改,查询操作是否成功运行。观察系统的响应情况,得出该功能也达到了设计目标,系统运行正确。
前置条件;用户登录系统。
表6-2 试卷统计管理的测试用例
功能描述 |
用于试卷统计管理 |
测试目的 |
检测试卷统计管理时的各种操作的运行情况 |
测试数据以及操作 |
预期结果 |
实际结果 |
点击添加试卷统计,必填项合法输入,点击保存 |
提示添加成功 |
与预期结果一致 |
点击添加试卷统计,必填项输入不合法,点击保存 |
提示必填项不能为空 |
与预期结果一致 |
点击修改试卷统计,必填项修改为空,点击保存 |
提示必填项不能为空 |
与预期结果一致 |
点击修改试卷统计,必填项输入不合法,点击保存 |
提示必填项不能为空 |
与预期结果一致 |
点击删除试卷统计,选择试卷考试删除 |
提示删除成功 |
与预期结果一致 |
点击搜索试卷统计,输入存在的试卷考试名 |
查找出试卷 |
与预期结果一致 |
点击搜索试卷统计,输入不存在的试卷考统计 |
不显示试卷 |
与预期结果一致 |
下表是科目分类管理功能的测试用例,检测了科目分类管理中分类列表的操作是否成功运行。观察系统的响应情况,得出该功能也达到了设计目标,系统运行正确。
前置条件;用户登录系统。
表6-3 科目分类管理的测试用例
功能描述 |
用于科目分类管理 |
测试目的 |
检测科目分类管理时各种操作的情况 |
测试数据以及操作 |
预期结果 |
实际结果 |
未选择分类,点击提交 |
提示请选择分类 |
与预期结果一致 |
未选择列表,点击提交 |
提示请选择列表 |
与预期结果一致 |
-
-
性能测试
使用阿里云PTS(Performance Testing Service)性能测试服务对线上系统进行压力测试。线上服务器环境为:1核心CPU,1G内存,1Mbps公网带宽,Centos7.0操作系统。
压测过程中使用了2台并发机器,每台机器20个用户并发,对系统主页,登录,数据查询和数据维护等模块进行并发访问,测试结果是有40个用户并发时,数据管理相关页面的响应时间甚至达到了7s,通过查看服务器出网流量发现已经达到1381kb/s,可以看出服务器的带宽已经达到峰值,如果系统使用5Mbps的带宽,系统的响应时间和TPS将会大大增加。在整个测试的过程中,CPU的使用率占用仅8%,也提现出带宽瓶颈对系统的影响非常严重。
本次设计历时3个月。在这个毕业设计中,它离不开指导教师的指导,使事情基本顺利。指导老师无论是在毕业设计历经中,还是在论文做完中都给了了我特别大的助益。另1个方面,教师认真负责的工作姿态,谨慎的教学精神厚重的理论水准都使我获益匪浅。他勤恳谨慎的教学育人学习姿态也给我留下了特别特别深的感觉。我从老师那里学到了很多东西。在理论和实践中,我的技能得到了特别大的提高。在此,特向教师表示由衷的感激。
经过对该毕业设计的全部研究和开发,我的系统研发经历了从需求分析到实现详细功能,再到最终测试和维护的特殊进展。让我对系统研发有了更深层次的认识。如今我的动手本领单独处理疑惑的本领也获取到了特别大的演练学习增多,这是这次毕业设计最好的收获。