基于SpringBoot的高校信息管理系统

一、视频展示

https://www.bilibili.com/video/BV1pU4y1J7wc

二、项目介绍

1、技术与工具

开发工具 IntelliJ IDEA
数据库 MySQL 5.7
前端技术 Jquery 、 Bootstrap 、echarts、thymeleaf
后台技术 SpringBoot、MyBatis

2、项目目录介绍

基于SpringBoot的高校信息管理系统_第1张图片

 具体介绍如下:

(1)程序代码的存放目录——src下的java

component 登录拦截器
config 静态资源拦截
controller 负责在页面和程序之间传输数据的,做页面的跳转
dao 负责对数据向数据库增删改查的操作
entity 实体类。一般与数据库中的属性值基本保持一致
service 负责业务模块的应用逻辑设计
utils 工具包。包括验证码、分页插件等

(2)资源文件的存放目录——resources

Mapper 存放Mabtis的映射文件
static 存放静态资源文件,包括图片、css文件、js文件
tempaltes 存放前端所有的页面。

(3)测试类的目录——test 

(4)maven的配置文件——pom.xml 

三、关键技术

1、分页插件

需求:系统中的数据可能会有多条。当数据量较少的时候可以在一页上展示,但是如果数据量很大,则应该使用分页的方式来显示数据。

基于SpringBoot的高校信息管理系统_第2张图片

(1)分页插件的依赖包

        
		
			com.github.pagehelper
			pagehelper
			4.1.6
		

(2)分页插件的工具类

Page类

public class Page {

    private int start; //开始页数
    private int count; //每页显示个数
    private int total; //总个数
    private String param; //参数

    private static final int defaultCount = 5; //默认每页显示5条

    public int getStart() {
        return start;
    }
    public void setStart(int start) {
        this.start = start;
    }
    public int getCount() {
        return count;
    }
    public void setCount(int count) {
        this.count = count;
    }

    public Page (){
        count = defaultCount;
    }
    public Page(int start, int count) {
        this();
        this.start = start;
        this.count = count;
    }

    public boolean isHasPreviouse(){
        if(start==0)
            return false;
        return true;
    }
    public boolean isHasNext(){
        if(start==getLast())
            return false;
        return true;
    }

    public int getTotalPage(){
        int totalPage;
        // 假设总数是50,是能够被5整除的,那么就有10页
        if (0 == total % count)
            totalPage = total /count;
            // 假设总数是51,不能够被5整除的,那么就有11页
        else
            totalPage = total / count + 1;

        if(0==totalPage)
            totalPage = 1;
        return totalPage;

    }

    public int getLast(){
        int last;
        // 假设总数是50,是能够被5整除的,那么最后一页的开始就是45
        if (0 == total % count)
            last = total - count;
            // 假设总数是51,不能够被5整除的,那么最后一页的开始就是50
        else
            last = total - total % count;
        last = last<0?0:last;
        return last;
    }

    @Override
    public String toString() {
        return "Page [start=" + start + ", count=" + count + ", total=" + total + ", getStart()=" + getStart()
                + ", getCount()=" + getCount() + ", isHasPreviouse()=" + isHasPreviouse() + ", isHasNext()="
                + isHasNext() + ", getTotalPage()=" + getTotalPage() + ", getLast()=" + getLast() + "]";
    }
    public int getTotal() {
        return total;
    }
    public void setTotal(int total) {
        this.total = total;
    }
    public String getParam() {
        return param;
    }
    public void setParam(String param) {
        this.param = param;
    }

}

PageHelperConfig类

@Configuration
public class PageHelperConfig {
 
    @Bean
    public PageHelper pageHelper() {
        PageHelper pageHelper = new PageHelper();
        Properties p = new Properties();
        p.setProperty("offsetAsPageNum", "true");
        p.setProperty("rowBoundsWithCount", "true");
        p.setProperty("reasonable", "true");
        pageHelper.setProperties(p);
        return pageHelper;
    }
}

(3)分页插件在业务逻辑中的使用方式

以查询所有学生信息为例演示分页插件的使用:

@RequestMapping("/studentList")
    public String studentList(Model model, Page page, String classNo){
        //分页插件
        PageHelper.offsetPage(page.getStart(),page.getCount());
        //结果集
        List studentList = new ArrayList<>();
        //如果班级号为空,则表明是查询所有学生信息的请求,反之则是根据班级号查询该班所有学生信息
        if(classNo != null){
            studentList = studentService.selectByCondition("", "", classNo);
        }else{
            studentList = studentService.selectAll();
        }
        //根据学号得到每个学生的选课数
        for (Student student : studentList){
            List stringList = studentService.selectClassCount(student.getStuNo());
            student.setClassCount(stringList.size());
        }
        //得到记录总数
        int total = (int) new PageInfo<>(studentList).getTotal();
        //设置页的总数
        page.setTotal(total);
        //向页面返回数据
        model.addAttribute("page",page);
        model.addAttribute("studentList", studentList);
        return "student/student";
    }

(4)页面显示

头像 学号 姓名 性别 班级 手机号 选课数量 操作
点击查看头像 [[${student.classCount}]]

2、用户头像的上传与存储

需求:学生的个人信息中包含了头像,上传的头像存储在本地目录。同样,在页面也需显示出来。

基于SpringBoot的高校信息管理系统_第3张图片

 (1)页面上传头像

...

(2)存储头像

@RequestMapping("/addStudent")
    public String addStudent(RedirectAttributes attributes, Student student, MultipartFile file, String province, String city, String district){
        //学生头像
        String filePath = "D:/Java/项目/MyDo/schoolmanagement/src/main/resources/static/userImg";
        String fileName = file.getOriginalFilename();
        File targetFile = new File(filePath, fileName);
        targetFile.getParentFile().mkdirs();
        try {
            file.transferTo(targetFile);
        } catch (IOException e) {
            e.printStackTrace();
        }
        student.setIcon("/userImg/"+fileName);
        //学生地址
        String address = province + ":" + city + ":" + district;
        student.setAddress(address);
        //插入数据库
        studentService.insertStudent(student);
        attributes.addFlashAttribute("msg", "添加成功");
        return "redirect:/student/studentList";
    }

(3)更换头像

@RequestMapping("/modifyStudent")
    public String modifyStudent(RedirectAttributes attributes, Student student, MultipartFile file, String province, String city, String district){
        //学生头像。如果页面提交过来的不为空,则替换,如果为空,则仍然是源头像
        if (!file.isEmpty()) {
            /**
             * 上传图片
             */
            //图像存放路径
            String filePath = "D:/Java/项目/MyDo/schoolmanagement/src/main/resources/static/userImg";
            //图片名称
            String fileName = file.getOriginalFilename();
            File targetFile = new File(filePath, fileName);
            //创建文件路径
            targetFile.getParentFile().mkdirs();
            targetFile.delete();
            try {
                file.transferTo(targetFile);
            } catch (IOException e) {
                e.printStackTrace();
            }
            student.setIcon("/userImg/"+fileName);
        }else {
            //设置源头像
            Student searchStudent = studentService.selectById(student.getId());
            student.setIcon(searchStudent.getIcon());
        }
        //学生地址
        String address = province + ":" + city + ":" + district;
        student.setAddress(address);
        studentService.updateStudent(student);
        attributes.addFlashAttribute("msg", "修改成功");
        return "redirect:/student/studentList";
    }

3、登录验证码

需求:在用户登陆系统时,需要输入验证码,并在后台完成验证。

基于SpringBoot的高校信息管理系统_第4张图片

 (1)页面逻辑

更换验证码
//获取验证码
    function getVerify() {
        $("#imgVerify").attr("src", '/getVerify?' + Math.random());//jquery方式
    }
   function modifyLabel(){
        $(".tip-area").css("display", "none");
    }

(2)工具类

RandomValidateCodeUtil类



import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.Random;

public class RandomValidateCodeUtil {
 
 
    public static final String RANDOMCODEKEY= "RANDOMVALIDATECODEKEY";//放到session中的key
    private String randString = "0123456789";//随机产生只有数字的字符串 private String
    //private String randString = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";//随机产生只有字母的字符串
    //private String randString = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";//随机产生数字与字母组合的字符串
    private int width = 95;// 图片宽
    private int height = 25;// 图片高
    private int lineSize = 40;// 干扰线数量
    private int stringNum = 4;// 随机产生字符数量
 
    private static final Logger logger = LoggerFactory.getLogger(RandomValidateCodeUtil.class);
 
    private Random random = new Random();
 
    /**
     * 获得字体
     */
    private Font getFont() {
        return new Font("Fixedsys", Font.CENTER_BASELINE, 18);
    }
 
    /**
     * 获得颜色
     */
    private Color getRandColor(int fc, int bc) {
        if (fc > 255)
            fc = 255;
        if (bc > 255)
            bc = 255;
        int r = fc + random.nextInt(bc - fc - 16);
        int g = fc + random.nextInt(bc - fc - 14);
        int b = fc + random.nextInt(bc - fc - 18);
        return new Color(r, g, b);
    }
 
    /**
     * 生成随机图片
     */
    public void getRandcode(HttpServletRequest request, HttpServletResponse response) {
        HttpSession session = request.getSession();
        // BufferedImage类是具有缓冲区的Image类,Image类是用于描述图像信息的类
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_BGR);
        Graphics g = image.getGraphics();// 产生Image对象的Graphics对象,改对象可以在图像上进行各种绘制操作
        g.fillRect(0, 0, width, height);//图片大小
        g.setFont(new Font("Times New Roman", Font.ROMAN_BASELINE, 18));//字体大小
        g.setColor(getRandColor(110, 133));//字体颜色
        // 绘制干扰线
        for (int i = 0; i <= lineSize; i++) {
            drowLine(g);
        }
        // 绘制随机字符
        String randomString = "";
        for (int i = 1; i <= stringNum; i++) {
            randomString = drowString(g, randomString, i);
        }
        logger.info(randomString);
        //将生成的随机字符串保存到session中
        session.removeAttribute(RANDOMCODEKEY);
        session.setAttribute(RANDOMCODEKEY, randomString);
        g.dispose();
        try {
            // 将内存中的图片通过流动形式输出到客户端
            ImageIO.write(image, "JPEG", response.getOutputStream());
        } catch (Exception e) {
            logger.error("将内存中的图片通过流动形式输出到客户端失败>>>>   ", e);
        }
 
    }
 
    /**
     * 绘制字符串
     */
    private String drowString(Graphics g, String randomString, int i) {
        g.setFont(getFont());
        g.setColor(new Color(random.nextInt(101), random.nextInt(111), random
                .nextInt(121)));
        String rand = String.valueOf(getRandomString(random.nextInt(randString
                .length())));
        randomString += rand;
        g.translate(random.nextInt(3), random.nextInt(3));
        g.drawString(rand, 13 * i, 16);
        return randomString;
    }
 
    /**
     * 绘制干扰线
     */
    private void drowLine(Graphics g) {
        int x = random.nextInt(width);
        int y = random.nextInt(height);
        int xl = random.nextInt(13);
        int yl = random.nextInt(15);
        g.drawLine(x, y, x + xl, y + yl);
    }
 
    /**
     * 获取随机的字符
     */
    public String getRandomString(int num) {
        return String.valueOf(randString.charAt(num));
    }
}

Picverigyaction类



import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


@RestController
public class Picverifyaction {
    private final static Logger logger = LoggerFactory.getLogger(Picverifyaction.class);
 
    /**
     * 生成验证码
     */
    @RequestMapping(value = "/getVerify")
    public void getVerify(HttpServletRequest request, HttpServletResponse response) {
        try {
            response.setContentType("image/jpeg");//设置相应类型,告诉浏览器输出的内容为图片
            response.setHeader("Pragma", "No-cache");//设置响应头信息,告诉浏览器不要缓存此内容
            response.setHeader("Cache-Control", "no-cache");
            response.setDateHeader("Expire", 0);
            RandomValidateCodeUtil randomValidateCode = new RandomValidateCodeUtil();
            randomValidateCode.getRandcode(request, response);//输出验证码图片方法
        } catch (Exception e) {
            logger.error("获取验证码失败>>>>   ", e);
        }
    }
 

 
}

(3)登录处理

@PostMapping("/login")
    public ModelAndView login(User user, @RequestParam String verifyInput, HttpSession session, RedirectAttributes attributes){
        //验证码
        String random = (String) session.getAttribute("RANDOMVALIDATECODEKEY");
        ModelAndView mv = new ModelAndView();
        //验证码校验
        if (random == null) {
            System.out.println("验证码错误");
            mv.addObject("msg", "验证码错误");
            mv.setViewName("index");
            return mv;
        }
        //判断验证码是否相等
        if(random.equals(verifyInput)){
            //根据用户名和密码查询用户
            User loginUser = userService.selectByNameAndPwd(user);
            if(loginUser != null){
                //查询成功,判断身份是否相等
                if(loginUser.getIdentity() != user.getIdentity()){
                    //不相等,则提示信息
                    mv.addObject("msg", "你不是"+(user.getIdentity() == 1 ? "管理员" : "用户"));
                    mv.setViewName("index");
                }else{
                    //相等,登陆成功
                    // session.setAttribute("loginUsername", user.getName());
                    session.setAttribute("user", user);
                    mv.setViewName("main");
                }
            } else{
                //查询失败,提示未注册
                mv.addObject("msg", "当前用户未注册");
                mv.setViewName("index");
            }
        }else{
            //验证码错误。,返回登录页
            System.out.println("验证码错误");
            mv.addObject("msg", "验证码错误");
            mv.setViewName("index");
        }
        // mv.setViewName("main");
        return mv;
    }

4、Hutool导出数据

需求:页面显示了信息列表,如果需要导出列表数据,需要点击导出按钮。

基于SpringBoot的高校信息管理系统_第5张图片

(1)页面

添加 导出

(2)后台处理请求

    @RequestMapping("/exportTeacherData")
    public void exportTeacherData(HttpServletResponse response) throws UnsupportedEncodingException {
        //从数据库中查询到数据
        List teacherList = teacherService.selectAll();
        for(Teacher teacher : teacherList){
            teacher.setClassCount(curriculumService.selectByTeacherNo(teacher.getTeacherNo()).size());
        }
        // 通过工具类创建writer,默认创建xls格式
        ExcelWriter writer = ExcelUtil.getWriter();
        //自定义标题别名
        writer.addHeaderAlias("teacherNo", "教工号");
        writer.addHeaderAlias("name", "教师姓名");
        writer.addHeaderAlias("age", "年龄");
        writer.addHeaderAlias("gender", "性别(0 男 1 女)");
        writer.addHeaderAlias("professor", "职称");
        writer.addHeaderAlias("phone", "手机号");
        writer.addHeaderAlias("email", "邮箱");
        writer.addHeaderAlias("classCount", "带课数");
        //只导出设置了别名的列
        writer.setOnlyAlias(true);
        // 合并单元格后的标题行,使用默认标题样式
        writer.merge(7, "教师信息");
        // 一次性写出内容,使用默认样式,强制输出标题
        writer.write(teacherList, true);
        //out为OutputStream,需要写出到的目标流
        //response为HttpServletResponse对象
        response.setContentType("application/vnd.ms-excel;charset=utf-8");
        //dateString.xls是弹出下载对话框的文件名,用日期作为文件名称
        Date currentTime = new Date();
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String dateString = formatter.format(currentTime);
        response.setHeader("Content-Disposition","attachment;filename="+dateString+".xls");
        ServletOutputStream out= null;
        try {
            out = response.getOutputStream();
            writer.flush(out, true);
        }
        catch (IOException e) {
            e.printStackTrace();
        }
        finally {
            // 关闭writer,释放内存
            writer.close();
        }
        //此处记得关闭输出Servlet流
        IoUtil.close(out);
    }

5、webjars使用

(1)介绍

        WebJars是将客户端(浏览器)资源(JavaScript,Css等)打成jar包文件,以对资源进行统一依赖管理。WebJars的jar包部署在Maven中央仓库上。

(2)目的

统一管理静态资源

(3)实现方式(以Bootstrap、jquery、echart为例)

pom.xml

        
			org.webjars
			bootstrap
			3.3.5
		
		
			org.webjars
			jquery
			3.6.0
		
        
			org.webjars.bower
			echarts
			4.7.0
		

页面中使用:

		
		
		

		
		

6、公共请求的页面跳转

需求:在后台逻辑中有的请求只是为了跳转页面,通过设置公共请求对这些跳转请求进行统一处理。如下:

    /**
     * 跳转至学生分析页面
     * @return
     */
    @RequestMapping("/studentAnalysis")
    public String studentScore(){
        return "/student/studentAnalysis";
    }

处理公共请求的映射方法:

/**
     * 公共请求的处理
     * @param request
     * @return
     */
    @RequestMapping(value = "/toPage",method = RequestMethod.GET)
    public  String toPage(HttpServletRequest request){
        //获取请求参数
        String url= request.getParameter("url");
        return  url;
    }

使用方式:在请求页面时携带url参数

教师页面:点击按钮,跳转至添加教师页面。

添加

顶部菜单栏的退出功能:

7、session的使用

需求:用户登陆成功之后,在顶部菜单栏显示欢迎XXX登录。

登录逻辑中的处理:

session.setAttribute("user", user);

 页面中使用session:

  • 欢迎[[${session.user.name}]]登录
  • 8、echarts与后台的交互

    需求:对信息以图表化形式显示在页面上。

    基于SpringBoot的高校信息管理系统_第6张图片

     (1)页面

    
    
    	
    		
    		课程分析
    		
    		
    		
    
    		
    		
    	
    	
    		
    		
    `

    (2)后台处理 

    /**
         * 课程等级
         * @return
         */
        @RequestMapping("/curriculumGrade")
        public Map curriculumGrade(){
            List curriculumList = curriculumService.selectAll();
            String[] names = new String[curriculumList.size()];
            int[] grades = new int[curriculumList.size()];
            int i = 0;
            for(Curriculum curriculum :curriculumList){
                names[i] = curriculum.getClassName();
                grades[i] = curriculum.getClassGrade();
                i++;
            }
            Map map = new HashMap<>();
            map.put("names", names);
            map.put("grades", grades);
            return map;
        }
    
        /**
         * 课程课时
         * @return
         */
        @RequestMapping("/curriculumTime")
        public Map curriculumTime(){
            List curriculumList = curriculumService.selectAll();
            Map myMap = new HashMap<>();
            for(Curriculum curriculum : curriculumList){
                if(myMap.containsKey(curriculum.getClassHour())){
                    myMap.put(curriculum.getClassHour(), myMap.get(curriculum.getClassHour())+1);
                }else{
                    myMap.put(curriculum.getClassHour(), 1);
                }
            }
            System.out.println(myMap);
            int[] hours = new int[myMap.size()];
            int[] counts = new int[myMap.size()];
            int i = 0;
            for (Map.Entry entry : myMap.entrySet()) {
                hours[i] = entry.getKey();
                counts[i] = entry.getValue();
                i++;
            }
            Map map = new HashMap<>();
            map.put("hours", hours);
            map.put("counts", counts);
            return map;
        }

    9、省市区三级联动

    需求:添加学生的时候,选择学生的家庭住址

    基于SpringBoot的高校信息管理系统_第7张图片

    页面实现:

    
    
    
    
    
    

    10、日期控件

    需求:学生生日的选择通过日历控件。

    基于SpringBoot的高校信息管理系统_第8张图片

     页面中的定义:

    $("#date").datetime({
    		type: "date",
    		value: [2019, 9, 31],
    		success: function(res) {
    			console.log(res)
    		}
    	})

     注:在省市区三级联动和日历控件中用了自定义的css以及一些js文件,可在源码中查看,这里不再罗列。

    11、thymeleaf提取公共页面

    需求:每个页面都有顶部菜单栏,因此提取作为公共部分,然后在所需引入即可。

    topbar.html:

    
    
    
        
        Title
    
    
    
    
    
    

    footer.html:

    
    
    
        
        $Title$
    
    
    

    版权所有:YHT China    电话:029-12345678
    地址:陕西省西安市     邮编:123456

    这两个文件都在templates的commons文件下。

    引入公共部分:

    12、MD5加密

    需求:对用户的密码进行MD5加密,提高安全性。

    (1)工具类MD5Utils

    public class MD5Utils {
        public static String stringToMD5(String plainText) {
            byte[] secretBytes = null;
            try {
                secretBytes = MessageDigest.getInstance("md5").digest(
                        plainText.getBytes());
            } catch (NoSuchAlgorithmException e) {
                throw new RuntimeException("没有这个md5算法!");
            }
            String md5code = new BigInteger(1, secretBytes).toString(16);
            for (int i = 0; i < 32 - md5code.length(); i++) {
                md5code = "0" + md5code;
            }
            return md5code;
        }
    }

    (2)注册时MD5加密

     user.setPassword(MD5Utils.stringToMD5(user.getPassword()));

    13、RedirectAttributes

    需求:在重定向之后需要携带参数跳转页面。

    如下:在添加班级之后将提示信息传递到classList页面。

    /**
         * 添加班级信息
         * @param classEntity
         * @param attributes
         * @return
         */
        @RequestMapping("/addClass")
        public String addCurriculum(ClassEntity classEntity, RedirectAttributes attributes){
            //插入数据
            classService.insertClass(classEntity);
            attributes.addFlashAttribute("msg", "添加成功");
            return "redirect:/class/classAllList";
        }
    

    四、总结

    一定要仔细,特别是复制粘贴的时候。在这个项目中,其实出现的问题都是因为自己不仔细,所以才出现的。

    五、代码地址

    https://github.com/MyBestThought/schoolmanagement

    欢迎大家指点!!!

    你可能感兴趣的:(SpringBoot,java,intellij,idea,spring,boot,bootstrap,echarts)