基于javaweb+mysql的springboot在线考试平台(java+springboot+ssm+mysql+maven+thymeleaf+html+redis)
运行环境
Java≥8、MySQL≥5.7
开发工具
eclipse/idea/myeclipse/sts等均可配置运行
适用
课程设计,大作业,毕业设计,项目练习,学习演示等
功能说明
基于javaweb+mysql的SpringBoot在线考试平台(java+springboot+ssm+mysql+maven+thymeleaf+html+redis)
一、项目简述
功能列表 考试前台 /系统登录:学生、教师、管理员登录 /门户首页:无需认证访问 /在线考试:需认证即可查看当前考除目 /题库中心:需认证查看题库 /成绩查询:需认证查询成绩 /留言板:需认证留言 管理后台 /考:署理:发布考试,考试列表,课程管理,题库管 理,成绩管理,成绩情况 /权限管理:学院管理,班级管理,用户管理,角色管 理,资源管理 4网站管理:基础信息,友链管理,评论管理,标签管理 /系统管理:在线用户 /上传管理:云存储配置 /运维管理:数据监控
二、项目运行
环境配置: Jdk1.8 + Tomcat8.5 + mysql + Eclispe (IntelliJ IDEA,Eclispe,MyEclispe,Sts 都支持)
项目技术: JSP +SpringBoot + MyBatis + Redis+ Thymeleaf+ Druid+ JQuery + SLF4J+ Fileupload + maven等等
* @return
*/
@PostMapping("/delete")
@ResponseBody
public ResponseVo delete(Integer id) {
//验证该学院是否存在班级
Classes classes = instituteService.validateByInsIds(new Integer[] {id});
if(classes.getCounts() > 0) {
return ResultUtil.error("无法删除,该学院下存在班级");
}else {
int i = instituteService.deleteBatch(new Integer[] {id});
if(i > 0) {
return ResultUtil.success("删除学院信息成功");
}else {
return ResultUtil.error("删除学院信息失败");
}
}
}
@PostMapping("/batch/delete")
@ResponseBody
public ResponseVo deleteBatch(@RequestParam("ids[]") Integer[]ids) {
//验证该学院下是否存在班级
Classes classes = instituteService.validateByInsIds(ids);
if(classes.getCounts() > 0) {
return ResultUtil.error("无法批量删除,该学院下存在班级");
}else {
int i = instituteService.deleteBatch(ids);
if(i > 0) {
return ResultUtil.success("批量删除学院信息成功");
}else {
return ResultUtil.error("批量删除学院信息失败");
}
}
}
}
return ResultUtil.error("改资源存在下级资源,无法删除!");
}
int a = permissionService.updateStatus(permissionId,CoreConst.STATUS_INVALID);
if (a > 0) {
shiroService.updatePermission();
return ResultUtil.success("删除权限成功");
} else {
return ResultUtil.error("删除权限失败");
}
} catch (Exception e) {
logger.error(String.format("PermissionController.deletePermission%s", e));
throw e;
}
}
/*权限详情*/
@GetMapping("/edit")
public String detail(Model model, String permissionId) {
Permission permission = permissionService.findByPermissionId(permissionId);
if(null!=permission){
if(permission.getParentId().equals(CoreConst.TOP_MENU_ID)){
model.addAttribute("parentName", CoreConst.TOP_MENU_NAME);
}else{
Permission parent = permissionService.findById(permission.getParentId());
model.addAttribute("parentName", parent.getName());
}
}
model.addAttribute("permission", permission);
return "permission/detail";
}
/*编辑权限*/
@ResponseBody
@PostMapping("/edit")
public ResponseVo editPermission(@ModelAttribute("permission")Permission permission){
int a = permissionService.updateByPermissionId(permission);
if (a > 0) {
shiroService.updatePermission();
return ResultUtil.success("编辑权限成功");
} else {
return ResultUtil.error("编辑权限失败");
}
}
}
}
/**
* 批阅试卷
* @param grade
* @return
*/
@GetMapping("/mark")
public String mark(Model model, Integer id) {
Grade grade = gradeService.selectByPrimaryKey(id);
List<String> answerStr = Arrays.asList(grade.getAnswerJson().split("~_~"));
Examination examination = examService.queryByExamId(grade.getExamId());
long examTime = (examination.getEndTime().getTime()-examination.getStartTime().getTime())/(1000*60);
examination.setExamTime(examTime);
List<Question> questions = examination.getQuestions();
for(int i = 0; i < questions.size(); i++) {
Question question = questions.get(i);
question.setStuAnswer(answerStr.get(i));
}
User user = userService.selectByUserId(grade.getUserId());
grade.setExamination(examination);
grade.setUser(user);
model.addAttribute("grade", grade);
return "grade/mark";
}
@PostMapping("/mark")
@ResponseBody
public ResponseVo mark(Grade grade) {
try {
Grade obj = gradeService.selectById(grade.getId());
grade.setResult(obj.getAutoResult()+grade.getManulResult());
if(grade.getResult() == 0) {
grade.setStatus(CoreConst.EXAM_END);
}
int userScore = grade.getResult();
int examScore = obj.getExamination().getTotalScore();
float score = (float)userScore/examScore;
if(score < 0.6) {
grade.setStatus(CoreConst.EXAM_END);
}else if(score >= 0.9) {
grade.setStatus(CoreConst.EXAM_FINE);
}else if(score >= 0.8) {
grade.setStatus(CoreConst.EXAM_GOOD);
}else if(score >= 0.6) {
grade.setStatus(CoreConst.EXAM_STANDARD);
}
gradeService.updateNotNull(grade);
return ResultUtil.success("批阅成功");
} catch (Exception e) {
return ResultUtil.success("批阅失败");
}
}
public ResponseVo add(User userForm, String confirmPassword, Integer passwordType){
String username = userForm.getUsername();
User user = userService.selectByUsername(username);
if (null != user) {
return ResultUtil.error("该学号已存在");
}
if(passwordType == 0) {
String password = userForm.getPassword();
//判断两次输入密码是否相等
if (confirmPassword != null && password != null) {
if (!confirmPassword.equals(password)) {
return ResultUtil.error("两次密码不一致");
}
}
}else {
userForm.setPassword(CoreConst.DEFAULT_PASSWORD);
}
userForm.setUserId(UUIDUtil.getUniqueIdByUUId());
userForm.setImg(CoreConst.DEFAULT_IMG);
userForm.setStatus(CoreConst.STATUS_VALID);
Date date = new Date();
userForm.setCreateTime(date);
userForm.setUpdateTime(date);
userForm.setLastLoginTime(date);
PasswordHelper.encryptPassword(userForm);
int num = userService.register(userForm);
if(num > 0){
return ResultUtil.success("添加用户成功");
}else {
return ResultUtil.error("添加用户失败");
}
}
/**编辑用户详情*/
@GetMapping("/edit")
public String userDetail(Model model, String userId){
User user = userService.selectByUserId(userId);
List<Classes> classes = classesService.selectAll();
List<String> grades = userService.selectGradeList();
model.addAttribute("user", user);
model.addAttribute("classes", classes);
model.addAttribute("grades", grades);
return "user/userDetail";
}
/**编辑用户*/
@PostMapping("/edit")
@ResponseBody
for(int i = 0; i < questions.size(); i++) {
Question question = questions.get(i);
question.setStuAnswer(answerStr.get(i));
}
User user = userService.selectByUserId(grade.getUserId());
grade.setExamination(examination);
grade.setUser(user);
model.addAttribute("grade", grade);
return "grade/detail";
}
/**
* 单个删除成绩
* @param id
* @return
*/
@PostMapping("/delete")
@ResponseBody
public ResponseVo delete(Integer id) {
int i = gradeService.deleteBatch(new Integer[] {id});
if(i > 0) {
return ResultUtil.success("删除成绩成功");
}else {
return ResultUtil.error("删除成绩失败");
}
}
/**
* 批量删除成绩
* @param ids
* @return
*/
@PostMapping("/batch/delete")
@ResponseBody
public ResponseVo deleteBatch(@RequestParam("ids[]") Integer[] ids) {
int i = gradeService.deleteBatch(ids);
if(i > 0) {
return ResultUtil.success("批量删除成绩成功");
}else {
return ResultUtil.error("批量删除成绩失败");
}
ShiroRealm realm = (ShiroRealm)securityManager.getRealms().iterator().next();
for (SimplePrincipalCollection simplePrincipalCollection : list) {
realm.clearCachedAuthenticationInfo(simplePrincipalCollection);
}
}
/**
* 根据userId 清除当前session存在的用户的权限缓存
* @param userIds 已经修改了权限的userId
*/
public void clearAuthorizationByUserId(List<String> userIds){
if(null == userIds || userIds.size() == 0) {
return ;
}
List<SimplePrincipalCollection> list = getSpcListByUserIds(userIds);
RealmSecurityManager securityManager =
(RealmSecurityManager) SecurityUtils.getSecurityManager();
ShiroRealm realm = (ShiroRealm)securityManager.getRealms().iterator().next();
for (SimplePrincipalCollection simplePrincipalCollection : list) {
realm.clearCachedAuthorizationInfo(simplePrincipalCollection);
}
}
/**
* 根据用户id获取所有spc
* @param userIds 已经修改了权限的userId
*/
private List<SimplePrincipalCollection> getSpcListByUserIds(List<String> userIds){
//获取所有session
Collection<Session> sessions = redisSessionDAO.getActiveSessions();
//定义返回
List<SimplePrincipalCollection> list = new ArrayList<SimplePrincipalCollection>();
for (Session session:sessions){
//获取session登录信息。
Object obj = session.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY);
if(null != obj && obj instanceof SimplePrincipalCollection){
//强转
SimplePrincipalCollection spc = (SimplePrincipalCollection)obj;
//判断用户,匹配用户ID。
obj = spc.getPrimaryPrincipal();
if(null != obj && obj instanceof User){
User user = (User) obj;
System.out.println("user:"+user);
//比较用户ID,符合即加入集合
if(null != user && userIds.contains(user.getUserId())){
list.add(spc);
}
}
}
}
return list;
}
}
* @return
*/
@PostMapping("/batch/delete")
@ResponseBody
public ResponseVo deleteBatch(@RequestParam("ids[]") Integer[] ids) {
int i = gradeService.deleteBatch(ids);
if(i > 0) {
return ResultUtil.success("批量删除成绩成功");
}else {
return ResultUtil.error("批量删除成绩失败");
}
}
}
@Controller
@RequestMapping("/permission")
public class PermissionController{
private static final Logger logger = LoggerFactory.getLogger(PermissionController.class);
/**1:全部资源,2:菜单资源*/
Love love = new Love();
love.setSupId(supId);
love.setLoveType(loveType);
love.setUserId(user.getUserId());
love.setUserIp(ip);
love.setCreateTime(date);
love.setUpdateTime(date);
loveService.insert(love);
return ResultUtil.success("点赞成功");
}else {
return ResultUtil.error("您已经点过赞了");
}
}
/**
* 个人主页
* @param model
* @param userId
* @return
*/
@GetMapping("/exam/personInfo")
public String personal(Model model) {
if(SecurityUtils.getSubject().isAuthenticated()) {
User user = (User)SecurityUtils.getSubject().getPrincipal();
List<Classes> classes = classesService.selectAll();
User userInfo = userService.selectByUserId(user.getUserId());
model.addAttribute("classes", classes);
model.addAttribute("user", userInfo);
return "index/personInfo";
}else {
return "redirect:/login";
}
}
/**编辑用户资料*/
@PostMapping("/exam/updatePersonal")
@ResponseBody
public ResponseVo updatePersonal(User user){
int a = userService.updateByUserId(user);
if(a > 0) {
return ResultUtil.success("保存个人信息成功");
}else {
return ResultUtil.error("修改个人信息失败");
}
}
/**
* 修改密码
* @param changePasswordVo
* @return
*/
@PostMapping("/exam/changePassword")
@ResponseBody
@PostMapping("/batch/delete")
@ResponseBody
public ResponseVo batchDeleteUser(String userIdStr) {
String[] userIds = userIdStr.split(",");
List<String> userIdsList = Arrays.asList(userIds);
int a = userService.deleteBatch(userIdsList);
if (a > 0) {
return ResultUtil.success("批量删除用户成功");
} else {
return ResultUtil.error("批量删除用户失败");
}
}
/**分配角色列表查询*/
@PostMapping("/assign/role/list")
@ResponseBody
public Map<String,Object> assignRoleList(String userId){
List<Role> roleList = roleService.selectRoles(new Role());
Set<String> hasRoles = roleService.findRoleByUserId(userId);
Map<String, Object> jsonMap = new HashMap<>(2);
jsonMap.put("rows", roleList);
jsonMap.put("hasRoles",hasRoles);
return jsonMap;
}
/**保存分配角色*/
@PostMapping("/assign/role")
@ResponseBody
public ResponseVo assignRole(String userId, String roleIdStr){
List<String> userIdList = new ArrayList<>();
userIdList.add(userId);
String[] roleIds = roleIdStr.split(",");
List<String> roleIdsList = Arrays.asList(roleIds);
ResponseVo responseVo = userService.addAssignRole(userIdList,roleIdsList);
shiroRealm.clearAuthorizationByUserId(userIdList);
return responseVo;
}
/**批量分配角色列表查询*/
@PostMapping("/batch/assign/role/list")
@ResponseBody
public Map<String,Object> batchAssignRoleList(String userId){
List<Role> roleList = roleService.selectRoles(new Role());
Set<String> hasRoles = roleService.findRoleByUserId(userId);
Map<String, Object> jsonMap = new HashMap<>(2);
@Controller
@RequestMapping("/upload")
public class UploadController{
private static final Logger logger = LoggerFactory.getLogger(UploadController.class);
@Autowired
private SysConfigService sysConfigService;
@Autowired
private QuestionService questionService;
@Autowired
private UserService userService;
//腾讯云对象存储
@ResponseBody
@PostMapping(value = "/upload")
public UploadResponse upload(@RequestParam(value = "file", required = false) MultipartFile file) throws Exception {
return ResultUtil.error("无法批量删除,该班级中还有学生");
}else {
int j = classesService.deleteBatch(ids);
if(j > 0) {
return ResultUtil.success("批量删除班级信息成功");
}else {
return ResultUtil.error("批量删除班级信息失败");
}
}
}
}
@RestController
logger.error(String.format("UploadController.upload%s", e));
throw e;
}
}
//上传到七牛云图床
/*@ResponseBody
@PostMapping(value = "/upload")
public UploadResponse upload(@RequestParam(value = "file", required = false) MultipartFile file) throws Exception{
if (file == null || file.isEmpty()) {
throw new UploadFileNotFoundException(UploadResponse.Error.FILENOTFOUND);
}
try {
String originalFilename = file.getOriginalFilename();
String suffix = originalFilename.substring(originalFilename.lastIndexOf(".")).toLowerCase();
String value = sysConfigService.selectAll().get(SysConfigKey.CLOUD_STORAGE_CONFIG.getValue());
Gson gson = new Gson();
CloudStorageConfigVo cloudStorageConfig = gson.fromJson(value,CloudStorageConfigVo.class);
String dir =cloudStorageConfig.getQiniuPrefix();
String md5 = MD5.getMessageDigest(file.getBytes());
String filePath = String.format("%1$s/%2$s%3$s", dir, md5, suffix);
ResponseVo responseVo = QiNiuYunUtil.writeFile(cloudStorageConfig,filePath,file.getBytes());
String qiniuDomain = cloudStorageConfig.getQiniuDomain();
String url = String.format("%1$s/%2$s", qiniuDomain, filePath);
if(responseVo.getStatus().equals(CoreConst.SUCCESS_CODE)){
return new UploadResponse(url,originalFilename, suffix, url, CoreConst.SUCCESS_CODE);
}else{
return new UploadResponse(originalFilename, CoreConst.FAIL_CODE,responseVo.getMsg());
}
} catch (Exception e) {
logger.error(String.format("UploadController.upload%s", e));
throw e;
}
}*/
@ResponseBody
@PostMapping(value = "/importExcel")
public UploadResponse importExcel(@RequestParam(value = "file", required = false) MultipartFile file) throws IOException {
if (file == null || file.isEmpty()) {
throw new UploadFileNotFoundException(UploadResponse.Error.FILENOTFOUND);
}
try {
ResponseVo responseVo = questionService.importExcel(file);
String originalFilename = file.getOriginalFilename();
String suffix = originalFilename.substring(originalFilename.lastIndexOf(".")).toLowerCase();
String md5 = MD5.getMessageDigest(file.getBytes());
String url = String.format("%1$s/%2$s", md5, suffix);
if(responseVo.getStatus().equals(CoreConst.SUCCESS_CODE)){
List<Subject> subjects = subjectService.selectSubjects(subject);
List<String> grades = userService.selectGradeList();
List<Classes> classes = classesService.selectAll();
model.addAttribute("subjects", subjects);
model.addAttribute("classes", classes);
model.addAttribute("grades", grades);
return "exam/publish";
}
@PostMapping("/add")
@ResponseBody
public ResponseVo add(Examination examination , Integer[]question) {
try {
User user = (User)SecurityUtils.getSubject().getPrincipal();
examination.setUserId(user.getUserId());
examination.setAuthor(user.getNickname());
Examination exam = examService.insertExam(examination);
examQuestionService.insertList(exam.getId(),question);
return ResultUtil.success("发布考试成功");
} catch (Exception e) {
return ResultUtil.error("发布考试失败");
}
}
@GetMapping("/edit")
public String edit(Model model, Integer id) {
Examination examination = examService.selectById(id);
model.addAttribute("examination", examination);
Subject subject = new Subject();
subject.setStatus(CoreConst.STATUS_INVALID);
List<Subject> subjects = subjectService.selectSubjects(subject);
List<String> grades = userService.selectGradeList();
List<Classes> classes = classesService.selectAll();
model.addAttribute("subjects", subjects);
model.addAttribute("classes", classes);
model.addAttribute("grades", grades);
List<ExamQuestion> examQuestions = examQuestionService.selectByExamId(id);
List<Integer> questionIds = new ArrayList<>();
for(ExamQuestion examQuestion : examQuestions) {
questionIds.add(examQuestion.getQuestionId());
@Controller
@RequestMapping("/online/user")
public class OnlineUserController {
@Autowired
private UserService userService;
// 在线用户列表
@PostMapping("/list")
@ResponseBody
public PageResultVo onlineUsers(UserOnlineVo user, Integer limit, Integer offset){
List<UserOnlineVo> userList = userService.selectOnlineUsers(user);
PageInfo<UserOnlineVo> pages = new PageInfo<>(userList);
int endIndex = (offset+limit) > userList.size() ? userList.size() : (offset+limit);
return ResultUtil.table(userList.subList(offset,endIndex),(long)userList.size(),pages);
}
// 强制踢出用户
@PostMapping("/kickout")
@ResponseBody
public ResponseVo kickout(String sessionId,String username) {
try {
if(SecurityUtils.getSubject().getSession().getId().equals(sessionId)){
return ResultUtil.error("不能踢出自己");
}
userService.kickout(sessionId,username);
return ResultUtil.success("踢出用户成功");
} catch (Exception e) {
return ResultUtil.error("踢出用户失败");
}
}
saveRequest(request);
Map<String, String> resultMap = new HashMap<String, String>();
//判断是不是Ajax请求
if ("XMLHttpRequest".equalsIgnoreCase(((HttpServletRequest) request).getHeader("X-Requested-With"))) {
resultMap.put("user_status", "300");
resultMap.put("message", "您已经在其他地方登录,请重新登录!");
//输出json串
out(response, resultMap);
}else{
//重定向
WebUtils.issueRedirect(request, response, kickoutUrl);
}
return false;
}
return true;
}
private void out(ServletResponse hresponse, Map<String, String> resultMap)
throws IOException {
try {
hresponse.setCharacterEncoding("UTF-8");
PrintWriter out = hresponse.getWriter();
out.println(JSON.toJSONString(resultMap));
out.flush();
out.close();
} catch (Exception e) {
System.err.println("KickoutSessionFilter.class 输出JSON异常,可以忽略。");
}
}
}
user.getPassword(),
ByteSource.Util.bytes(user.getCredentialsSalt()),
getName()
);
return authenticationInfo;
}
/**清除认证信息*/
public void removeCachedAuthenticationInfo(List<String> userIds) {
if(null == userIds || userIds.size() == 0) {
return ;
}
List<SimplePrincipalCollection> list = getSpcListByUserIds(userIds);
RealmSecurityManager securityManager =
(RealmSecurityManager) SecurityUtils.getSecurityManager();
ShiroRealm realm = (ShiroRealm)securityManager.getRealms().iterator().next();
for (SimplePrincipalCollection simplePrincipalCollection : list) {
realm.clearCachedAuthenticationInfo(simplePrincipalCollection);
}
}
/**
* 根据userId 清除当前session存在的用户的权限缓存
* @param userIds 已经修改了权限的userId
*/
public void clearAuthorizationByUserId(List<String> userIds){
if(null == userIds || userIds.size() == 0) {
return ;
}
List<SimplePrincipalCollection> list = getSpcListByUserIds(userIds);
RealmSecurityManager securityManager =
(RealmSecurityManager) SecurityUtils.getSecurityManager();
ShiroRealm realm = (ShiroRealm)securityManager.getRealms().iterator().next();
for (SimplePrincipalCollection simplePrincipalCollection : list) {
realm.clearCachedAuthorizationInfo(simplePrincipalCollection);
}
}
/**
* 根据用户id获取所有spc
* @param userIds 已经修改了权限的userId
*/
private List<SimplePrincipalCollection> getSpcListByUserIds(List<String> userIds){
//获取所有session
Collection<Session> sessions = redisSessionDAO.getActiveSessions();
//定义返回
List<SimplePrincipalCollection> list = new ArrayList<SimplePrincipalCollection>();
for (Session session:sessions){
//获取session登录信息。
Object obj = session.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY);
if(null != obj && obj instanceof SimplePrincipalCollection){
* @param vo
* @param supId
* @param loveType
* @return
*/
@PostMapping("/exam/love")
@ResponseBody
public ResponseVo love(HttpServletRequest request, LoveConditionVo vo, Integer supId,Integer loveType) {
Date date = new Date();
String ip = IpUtil.getIpAddr(request);
User user = (User)SecurityUtils.getSubject().getPrincipal();
vo.setSupId(supId);
vo.setLoveType(loveType);
vo.setUserId(user.getUserId());
vo.setUserIp(ip);
Love loves = loveService.findByCondition(vo);
if(loves == null) {
Love love = new Love();
love.setSupId(supId);
love.setLoveType(loveType);
love.setUserId(user.getUserId());
love.setUserIp(ip);
love.setCreateTime(date);
love.setUpdateTime(date);
loveService.insert(love);
return ResultUtil.success("点赞成功");
}else {
return ResultUtil.error("您已经点过赞了");
}
}
/**
* 个人主页
* @param model
* @param userId
* @return
*/
@GetMapping("/exam/personInfo")
public String personal(Model model) {
if(SecurityUtils.getSubject().isAuthenticated()) {
User user = (User)SecurityUtils.getSubject().getPrincipal();
List<Classes> classes = classesService.selectAll();
User userInfo = userService.selectByUserId(user.getUserId());
model.addAttribute("classes", classes);
model.addAttribute("user", userInfo);
return "index/personInfo";
}else {
return "redirect:/login";
}
}
/**编辑用户资料*/
@PostMapping("/exam/updatePersonal")