基于javaweb+mysql的springboot在线学习系统(java+springboot+mybatis+vue+mysql+maven+redis)

基于javaweb+mysql的springboot在线学习系统(java+springboot+mybatis+vue+mysql+maven+redis)

私信源码获取及调试交流

运行环境

Java≥8、MySQL≥5.7、Node.js≥10

开发工具

后端:eclipse/idea/myeclipse/sts等均可配置运行

前端:WebStorm/VSCode/HBuilderX等均可

适用

课程设计,大作业,毕业设计,项目练习,学习演示等

功能说明

基于javaweb的SpringBoot在线学习系统(java+springboot+mybatis+vue+mysql+maven+redis)

一、项目运行 环境配置:

Jdk1.8 + Mysql + HBuilderX(Webstorm也行)+ Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。

项目技术:

Spring + SpringBoot+ mybatis + Maven + Vue 等等组成,B/S模式 + Maven管理等等。

        if (map.isEmpty()) {
            return SystemUtil.getJSONString(200, "注册成功");
        } else {
            return SystemUtil.getJSONString(2008, (String) map.get("errMsg"));
        }
    }

    /**
     * 登出
     */
    @GetMapping("/logout")
    public void logout(@CookieValue("ticket") String ticket) {
        userService.logout(ticket);
    }
}

public class HttpIpUtils {

    /**
     * 真实ip地址
     */
    public static String getClientIp(HttpServletRequest request) {
        String ip = request.getHeader("X-Forwarded-For");
        if (!StringUtils.isEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)) {
            //多次反向代理后会有多个ip值,第一个ip才是真实ip
            int index = ip.indexOf(",");
            if (index != -1) {
                return ip.substring(0, index);
            } else {
                return ip;
            }
        }
        ip = request.getHeader("X-Real-IP");
        if (!StringUtils.isEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)) {
            return ip;
        }
        return request.getRemoteAddr();
    }

    /**
     * 获取用户真实IP地址,不使用request.getRemoteAddr(); 的原因是有可能用户使用了代理软件方式避免真实IP地址
     */

    /**
     * 下载笔记
     */
    @GetMapping("/download/{fileId}")
    public void downloadNote(@PathVariable("fileId") String fileId, String filename,
                             HttpServletResponse resp) {
        String filePath = fileUploadPath + "/" + fileId;
        try (
                // java 7 写在这里会自动添加finally代码块来关闭流
                FileInputStream fis = new FileInputStream(filePath);
                OutputStream os = resp.getOutputStream()
        ) {
            // 服务器存放路径
            resp.setCharacterEncoding("UTF-8");
            resp.setContentType("application/octet-stream"); //告诉浏览器输出内容为流
            resp.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, "UTF-8"));
            byte[] buffer = new byte[1024];
            int b = 0;
            while ((b = fis.read(buffer)) != -1) {
                os.write(buffer, 0, b);
            }
        } catch (Exception e) {
            logger.error("读取文件失败: " + e.getMessage());
        }
    }

    /**
     * 删除笔记
     */
    @DeleteMapping("/delete/{id}")
    public String delNote(@PathVariable("id") Integer id) {
        boolean isOk = noteService.removeById(id);
        int code = isOk ? 200 : 2400;
        String msg = isOk ? "删除成功!" : "删除失败!";
        return SystemUtil.getJSONString(code, msg);
    }
}

    public String getPostById(@PathVariable("id") Integer id) {
        Post post = postService.getById(id);
        //再查出当前帖子的用户信息一起传给前端
        User user = userService.getUserById(post.getUid());
        return SystemUtil.getJSONString(200, "", new HashMap<String, Object>() {{
            put("pInfo", post);
            put("uInfo", SystemUtil.handleUser(user));
        }});
    }

    /**
     * 更新某条帖子信息,传帖子id,content,pictures 这3个字段即可
     */
    @PostMapping("/update")
    public String updatePost(@RequestBody Post post) {
        boolean isOk = postService.updatePost(post);
        int code = isOk ? 200 : 2300;
        String msg = isOk ? "更新成功!" : "更新失败!";
        return SystemUtil.getJSONString(code, msg);
    }

    /**
     * 删除帖子,当前用户为登录的用户
     */
    @DeleteMapping("/del/{id}")
    public String delPost(@PathVariable("id") Integer id) {
        boolean isOk = postService.delPostById(id);
        int code = isOk ? 200 : 2300;
        String msg = isOk ? "删除帖子成功!" : "删除帖子失败!";
        return SystemUtil.getJSONString(code, msg);
    }
}

     * 添加评论
     */
    @PostMapping("/add")
    public String addComment(@RequestBody Comment comment) {
        boolean isOk = commentService.save(comment);
        //重新查询一下插入的评论信息
        Comment one = isOk ? commentService.getById(comment.getId()) : null;
        if (isOk) {
            //查询成功才往 post 表更新 replynum 字段
            postService.updateReplynumOfPost(1, comment.getPostId());
            return SystemUtil.getJSONString(200, "评论成功!", new HashMap<String, Object>() {{
                put("comment", one);
            }});
        }
        return SystemUtil.getJSONString(4000, "评论失败!");
    }

    /**
     * todo:暂时不做这个功能
     * 传评论 id 和 评论层级 level
     * 删除评论,传一个参数,评论id,还要对其子评论进行递归删除
     * 分情况讨论
     *  ①若 level 为0,则直接删除 id 为 id的那条评论 和 parentId 为 id 的所有记录
     *  ②若 level 为1,则拿出那条记录的 parentId,然后查出 parentId+(level=2)+(targetId=待删除level=1的评论的那个uid)
     *      另外加上直接删除该条评论
     *  ③若 level 为2,则直接删除 id 为 id的那条评论
     *  ④减去该帖子被删除的回复数
     */
    @PostMapping("/del/{id}")
    public String delComment(@PathVariable("id") Integer id) {
        boolean isOk = commentService.removeById(id);
        int code = isOk ? 200 : 2300;
        String msg = isOk ? "删除成功!" : "删除失败!";
        return SystemUtil.getJSONString(code, msg);
    }
}


@Slf4j
public class LoginTicketInterceptor implements HandlerInterceptor {

    @Resource
    private UserService userService;

    @Resource
    private HostHolder hostHolder;

    /**
     * 调用时间:Controller方法处理之前
     * 执行顺序:链式 Interceptor 情况下,Interceptor 按照声明的顺序一个接一个执行
     * 若返回 false,则中断执行,注意:不会进入 afterCompletion
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        log.info("LoginTicketInterceptor -> preHandle,url:" + request.getServletPath());
        // 从 cookie 中获取凭证
        String ticket = CookieUtil.getValue(request, "ticket");
        if (ticket != null) {
            // 查询凭证
            LoginTicket loginTicket = userService.findLoginTicket(ticket);
            // 检查凭证是否有效
            if (loginTicket != null && loginTicket.getStatus() == 0 && loginTicket.getExpired().after(new Date())) {
                User user = userService.getUserById(loginTicket.getUserId());
                // 在本次请求中持有用户,将用户暂存到某个线程中
                hostHolder.setUser(user);
                log.info("LoginTicketInterceptor -> preHandle,loginTicket:" + loginTicket);
                return true;
            }
        }
        ResponseUtil.out(response, new JSONObject() {{
            put("code", 2000);
            put("msg", "你还没有登录哦!");
    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    /**
     * 获取验证码
     */
    @GetMapping("/kaptcha")
    public void getKaptcha(HttpServletResponse response) {
        // 生成验证码(文本)
        String text = kaptchaProducer.createText();
        BufferedImage image = kaptchaProducer.createImage(text);

        // 验证码的归属
        String kaptchaOwner = SystemUtil.generateUUID();
        Cookie cookie = new Cookie("kaptchaOwner", kaptchaOwner);
        // 验证码有效时间为 60 s
        cookie.setMaxAge(60);
        // 整个项目都有效
        cookie.setPath("/");
        // 发送给客户端
        response.addCookie(cookie);
        // 将验证码存入Redis
        String redisKey = RedisKeyUtil.getKaptchaKey(kaptchaOwner);
        // redis 有效时间为 60s
        redisTemplate.opsForValue().set(redisKey, text, 60, TimeUnit.SECONDS);

        // 将图片输出给浏览器
        response.setContentType("image/png");
        try {
            OutputStream os = response.getOutputStream();
            ImageIO.write(image, "png", os);
        } catch (IOException e) {
            logger.error("响应验证码失败:" + e.getMessage());
        }
    }

    /**
     * 登录
     */
    @PostMapping("/login")
    public String login(String username, String password, String code, boolean rememberme, HttpServletResponse response,
                        @CookieValue("kaptchaOwner") String kaptchaOwner) {
        // kaptchaOwner 为 uuid
        // code 为验证码
        // 检查验证码
        String kaptcha = null;
public class PostController {

    private static final Logger logger = LoggerFactory.getLogger(PostController.class);

    private final static List<String> imgExtends = Arrays.asList(".jpg", ".png", ".jpeg", ".gif");

    @Value("${onlinelearning.path.upload.img}")
    private String imgUploadPath;

    @Value("${onlinelearning.path.domain}")
    private String domain;

    @Resource
    private PostService postService;

    @Resource
    private HostHolder hostHolder;

    @Resource
    private UserService userService;

    /**
     * 上传图片
     */
    @PostMapping("/imgUpload")
    public String uploadHeader(MultipartFile file) {
        String fileName;
        if (file == null || (fileName = file.getOriginalFilename()) == null) {
            return SystemUtil.getJSONString(2013, "您还没有选择图片!");
        }
        String suffix = fileName.substring(fileName.lastIndexOf("."));
        if (StringUtils.isBlank(suffix) || !imgExtends.contains(suffix.toLowerCase())) {
            return SystemUtil.getJSONString(2013, "图片格式不正确!");
        }
        // 生成随机文件名
        String newFileName = SystemUtil.generateUUID() + suffix;
        // 构建上传文件的存放 "文件夹" 路径
        File fileDir = new File(imgUploadPath);
        if (!fileDir.exists()) {
            // 递归生成文件夹
            fileDir.mkdirs();
        }
        // 确定文件存放的路径
        File dest = new File(fileDir.getAbsolutePath() + "/" + newFileName);
        try {
            // 存储文件
            file.transferTo(dest);
        } catch (IOException e) {

/**
 */
@RestController
@RequestMapping("/favorite")
public class FavoriteController {

    @Resource
    private FavoriteService favoriteService;

    @Resource
    private HostHolder hostHolder;

    /**
     * 添加或取消收藏,传个课程id即可,用户id从后台获取
     */
    @GetMapping("/change")
    public String addFavorite(Integer subjectId) {
        Map<String, Object> res = favoriteService.changeFavorite(subjectId, hostHolder.getUser().getId());
        return SystemUtil.getJSONString((Integer) res.get("code"), (String) res.get("msg"));
    }

    /**
     * 删除收藏
     */
    @DeleteMapping("/delete/{id}")
    public String delFavorite(@PathVariable("id") Integer id) {
        boolean isOk = favoriteService.delFavoriteById(id);
        int code = isOk ? 200 : 2300;
        String msg = isOk ? "删除成功!" : "删除失败!";
        return SystemUtil.getJSONString(code, msg);
    }

    /**
     * 分页查询出当前登录用户的收藏,当前登录的用户 uid 从 HostHolder 中取,可有模糊搜索共用一个接口
     */
    @GetMapping("/all")
    public String getPageOfFavorite(Integer pageIndex, String searchContent) {
        Integer uid = hostHolder.getUser().getId();

    @Resource
    private PostService postService;

    @Resource
    private HostHolder hostHolder;

    @Resource
    private UserService userService;

    /**
     * 上传图片
     */
    @PostMapping("/imgUpload")
    public String uploadHeader(MultipartFile file) {
        String fileName;
        if (file == null || (fileName = file.getOriginalFilename()) == null) {
            return SystemUtil.getJSONString(2013, "您还没有选择图片!");
        }
        String suffix = fileName.substring(fileName.lastIndexOf("."));
        if (StringUtils.isBlank(suffix) || !imgExtends.contains(suffix.toLowerCase())) {
            return SystemUtil.getJSONString(2013, "图片格式不正确!");
        }
        // 生成随机文件名
        String newFileName = SystemUtil.generateUUID() + suffix;
        // 构建上传文件的存放 "文件夹" 路径
        File fileDir = new File(imgUploadPath);
        if (!fileDir.exists()) {
            // 递归生成文件夹
            fileDir.mkdirs();
        }
        // 确定文件存放的路径
        File dest = new File(fileDir.getAbsolutePath() + "/" + newFileName);
        try {
            // 存储文件
            file.transferTo(dest);
        } catch (IOException e) {
            logger.error("上传图片失败: " + e.getMessage());
            throw new RuntimeException("上传图片失败,服务器发生异常!", e);
        }
        // 讨论区中的图片链接(web访问路径)
        // http://localhost:8006/post/imgLoader/xxx.png
        String headerUrl = domain + "/post/imgLoader/" + newFileName;
        return SystemUtil.getJSONString(200, "上传成功!", new HashMap<String, Object>() {{
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        String method = request.getMethod();
        StringBuffer paramsValue = new StringBuffer();
        Object paramsName = null;
        // get请求
        if (HttpMethod.GET.toString().equals(method)) {
            String queryString = request.getQueryString();
            if (!StringUtils.isEmpty(queryString)) {
                paramsName = JSON.parseObject(JSON.toJSONString(joinPoint.getSignature())).get("parameterNames");
                paramsValue.append(URLDecoder.decode(queryString, "UTF-8"));
            }
        } else {
            //其他请求
            Object[] paramsArray = joinPoint.getArgs();
            paramsName = JSON.parseObject(JSON.toJSONString(joinPoint.getSignature())).get("parameterNames");
            for (Object o : paramsArray) {
                paramsValue.append(o).append(" ");
            }
        }
        String ip = HttpIpUtils.getClientIp(request);
        log.info("URLParamName:" + paramsName);
        log.info("URLParamValue:" + paramsValue);
        log.info("URL:{},HTTP_METHOD:{},IP:{},Method:{}", request.getRequestURL().toString(), request.getMethod(), ip, joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
    }
}

/**
 */
@RestController
@RequestMapping("/comment")
    public void init(FilterConfig filterConfig) throws ServletException {
    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
        HttpServletResponse response = (HttpServletResponse) res;
        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Credentials", "true");
        response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, HEAD");
        response.setHeader("Access-Control-Max-Age", "3600");
        response.setHeader("Access-Control-Allow-Headers", "access-control-allow-origin, authority, content-type, version-info, X-Requested-With");
        chain.doFilter(req, res);
    }

    @Override
    public void destroy() {

    }
}

public class ResponseUtil {

    public static void out(HttpServletResponse response, Object r) {
        ObjectMapper mapper = new ObjectMapper();
        response.setStatus(HttpStatus.OK.value());
        response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
        try {
            mapper.writeValue(response.getWriter(), r);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

public class CookieUtil {
    public static String getValue(HttpServletRequest request, String name) {
        if (request == null || name == null) {
            throw new IllegalArgumentException("参数为空!");
        }
        // 遍历所有 cookie 值
        Cookie[] cookies = request.getCookies();

/**
 */
@RestController
@RequestMapping("/subject")
public class SubjectController {

    @Resource
    private SubjectService subjectService;

    /**
     * 获取所有课程列表
     */
    @GetMapping("/all")
    public String getAllSubject() {
        List<Subject> subjectList = subjectService.getAllSubject();
        return SystemUtil.getJSONString(200, "", new HashMap<String, Object>() {{
            put("subjectList", subjectList);
        }});
    }

    /**
     * 读取 execl 数据并插入到数据库中
     */
    @PostMapping("/add")
    public String addSubject(MultipartFile file) {
        subjectService.saveSubject(file, subjectService);
        return SystemUtil.getJSONString(200, "上传成功!");
    }
}


    // 在 controller 之后,模板之前执行
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    }

    /**
     * 调用前提:preHandle 返回 true
     * 调用时间:DispatcherServlet 进行视图的渲染之后
     * 作用:多用于清理资源
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion...");
        hostHolder.clear();
    }
}

//日志切面
@Slf4j
@Aspect
@Component
public class SysLogAspect {

    @Pointcut("execution(* com.gll.onlinelearning.controller..*.*(..))")
    public void log() {
    }

    @Before("log()")
    public void doBefore(JoinPoint joinPoint) throws UnsupportedEncodingException {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        String method = request.getMethod();
        if (map.isEmpty()) {
            return SystemUtil.getJSONString(200, "注册成功");
        } else {
            return SystemUtil.getJSONString(2008, (String) map.get("errMsg"));
        }
    }

    /**
     * 登出
     */
    @GetMapping("/logout")
    public void logout(@CookieValue("ticket") String ticket) {
        userService.logout(ticket);
    }
}

public class HttpIpUtils {

    /**
     * 真实ip地址
     */
    public static String getClientIp(HttpServletRequest request) {
        String ip = request.getHeader("X-Forwarded-For");
        if (!StringUtils.isEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)) {
            //多次反向代理后会有多个ip值,第一个ip才是真实ip
            int index = ip.indexOf(",");
            if (index != -1) {
                return ip.substring(0, index);
            } else {
                return ip;
            }
        }
        ip = request.getHeader("X-Real-IP");
        if (!StringUtils.isEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)) {
            return ip;
        }
        return request.getRemoteAddr();
    }

    /**
            resp.setCharacterEncoding("UTF-8");
            resp.setContentType("application/octet-stream"); //告诉浏览器输出内容为流
            resp.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, "UTF-8"));
            byte[] buffer = new byte[1024];
            int b = 0;
            while ((b = fis.read(buffer)) != -1) {
                os.write(buffer, 0, b);
            }
        } catch (Exception e) {
            logger.error("读取文件失败: " + e.getMessage());
        }
    }

    /**
     * 删除笔记
     */
    @DeleteMapping("/delete/{id}")
    public String delNote(@PathVariable("id") Integer id) {
        boolean isOk = noteService.removeById(id);
        int code = isOk ? 200 : 2400;
        String msg = isOk ? "删除成功!" : "删除失败!";
        return SystemUtil.getJSONString(code, msg);
    }
}

@RestController
public class LoginController {

            ImageIO.write(image, "png", os);
        } catch (IOException e) {
            logger.error("响应验证码失败:" + e.getMessage());
        }
    }

    /**
     * 登录
     */
    @PostMapping("/login")
    public String login(String username, String password, String code, boolean rememberme, HttpServletResponse response,
                        @CookieValue("kaptchaOwner") String kaptchaOwner) {
        // kaptchaOwner 为 uuid
        // code 为验证码
        // 检查验证码
        String kaptcha = null;
        // 若客户端传来的 cookie 没有失效,则从 redis 中取验证码
        if (StringUtils.isNotBlank(kaptchaOwner)) {
            // 验证码的 key 为 uuid, 值为 验证码
            String redisKey = RedisKeyUtil.getKaptchaKey(kaptchaOwner);
            kaptcha = (String) redisTemplate.opsForValue().get(redisKey);
        }
        if (StringUtils.isBlank(kaptcha) || StringUtils.isBlank(code) || !kaptcha.equalsIgnoreCase(code)) {
            return SystemUtil.getJSONString(2010, "验证码失效或不正确!");
        }
        // 检查账号和密码
        int expiredSeconds = rememberme ? SystemConstant.REMEMBER_EXPIRED_SECONDS : SystemConstant.DEFAULT_EXPIRED_SECONDS;
        Map<String, Object> map = userService.login(username, password, expiredSeconds);
        Map<String, Object> res = new HashMap<>();
        if (map.containsKey("ticket")) {
            Cookie cookie = new Cookie("ticket", map.get("ticket").toString());
            // cookie设置整个项目访问路径都有效
            cookie.setPath("/");
            cookie.setMaxAge(expiredSeconds);
            cookie.setHttpOnly(false);
            response.addCookie(cookie);
            res.put("userInfo", map.get("userInfo"));
            return SystemUtil.getJSONString(200, "登录成功!", res);
        } else {
            return SystemUtil.getJSONString(2011, (String) map.get("errMsg"));
        }
    }

        String fileName;
        if (file == null || (fileName = file.getOriginalFilename()) == null) {
            return SystemUtil.getJSONString(2013, "您还没有选择文件!");
        }
        String suffix = fileName.substring(fileName.lastIndexOf("."));
        if (StringUtils.isBlank(suffix)) {
            return SystemUtil.getJSONString(2013, "文件格式不正确!");
        }
        // 生成随机文件名
        String newFileName = SystemUtil.generateUUID() + suffix;
        // 构建上传文件的存放 "文件夹" 路径
        File fileDir = new File(fileUploadPath);
        if (!fileDir.exists()) {
            // 递归生成文件夹
            fileDir.mkdirs();
        }
        // 确定文件存放的路径
        File dest = new File(fileDir.getAbsolutePath() + "/" + newFileName);
        try {
            // 存储文件
            file.transferTo(dest);
        } catch (IOException e) {
            logger.error("上传文件失败: " + e.getMessage());
            throw new RuntimeException("上传文件失败,服务器发生异常!", e);
        }
        // 笔记下载路径
        // http://localhost:8006/note/download/xxxxx.pdf
        User user = hostHolder.getUser();
        String downloadUrl = domain + "/note/download/" + newFileName;
        Note note = new Note();
        note.setUid(user.getId());
        note.setUsername(user.getUsername());
        note.setFilename(fileName);
        note.setDownloadUrl(downloadUrl);
        noteService.save(note);
        return SystemUtil.getJSONString(200, "上传成功!");
    }

    /**
     * 下载笔记
     */
    @GetMapping("/download/{fileId}")
    public void downloadNote(@PathVariable("fileId") String fileId, String filename,
                             HttpServletResponse resp) {
        String filePath = fileUploadPath + "/" + fileId;
        try (
                // java 7 写在这里会自动添加finally代码块来关闭流
                FileInputStream fis = new FileInputStream(filePath);
                OutputStream os = resp.getOutputStream()
        ) {
            // 服务器存放路径

请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值