项目编号:BS-PT-090
一,环境介绍
语言环境:Java: jdk1.8
数据库:Mysql: mysql5.7
应用服务器:Tomcat: tomcat8.5.31
开发工具:IDEA或eclipse
开发技术:Springboot+Mybatis+Vue+ElmentUI
二,项目简介
在中国有很多网络小说的爱好者存在,它们有的是重度的小网络小说的忠实读者,有的是把它当成业余休闲的一种娱乐方式,但现在网络的发达也带来了信息的爆发式增长,这就引起很多阅读者搜索合适网络小说的障碍,如何在一个小说平台中找到适合自己的小说,是一个值得研究的问题。
为帮助用户在风络小说平台中快速找到自己喜欢和适合的小说作品,本项目主要基于协同过滤算法来进行个性化的推荐,将平台用户喜欢的或符合用户个性特征的小说主动的推荐给用户,让用户不必为在海量的信息中寻找有用信息而烦恼。
基于协同过滤算法来实现的个性化网络小说推荐系统的设计与开发,主要使用协同过滤算法来给用户进行相应的小说推荐。协同过滤算法一般有三种常见的模式,一个基于物品的相似性,也就是item-based,通过寻找相似的物品来进行推荐,比如你买一个手机,那么会推荐手机膜等附加物品。第二个是基于用户的相似性,也就是user-based,通过寻找用户相似来发现同类用户的喜好,从而来进行推荐。第三个是基于混合模式来实现,也就是mode-based,主要通过用户对物品的操作行为来实现推荐,比如用户经常浏览某类的小说,或对小说进行点赞和收藏行为,那系统就以些为依据来为用户推荐网络小说。
本项目主要围绕网络小说的在线阅读、管理和信息推荐来进行研发实现,使得用户在本平台中可以快速的寻找到自己喜欢或有用的网络小说。整个系统基于IDEA集成开发工具开发实现,使用JAVA语言中的Springboot框架来构建后台服务接口,采用VUE来构建系统的前端页面,利用MYSQL进行基本的业务数据存储。
本项目是基于用户特征的个性化网络小说推荐系统,它的主要功能实现在于为用户进行合适的网络小说的推荐,采用的协同过滤算法的模型为混合CF算法,也即根据用户对于网络小说的操作性行为来进行进行综合评分后进行相关性推荐。所以在设计系统功能不仅仅是推荐,还要有一些基础的数据来源,所以像网络小说信息的管理,用户对网络小说的浏览、点赞、收藏等操作也是必不可少的。经过对相关类似网站的考察与观摩,设计了系统的相关业务功能模块。
前端用户的主要功能包含有:用户在线注册与登陆、平台发布的网络小说的信息浏览、用户交互功能包含点赞、收藏与评论操作,平台论坛的发贴交流、系统公告的查看,小说资讯的相关阅读,个性化小说的系统推荐以及个人中心管理。具体的系统功能结构图如下图1所示:
后台管理用户的主要功能包含有:用户信息管理模块、个人信息管理模块、交流论坛数据管理、小说信息管理、小说资讯信息管理、小说分类管理、小说资讯管理、公告管理等。
三,系统展示
用户注册登录
前端用户进入网络小说平台后可以在线进行注册,填写相关信息后提交到服务器平台,系统基于前后端分离的模式开发实现,前端用户的数据通过异步请求以JSON的数据格式传输到服务器,服务器接受数据后进行解析,然后使用Mybatis-plus框架的保存方法将数据保存至数据库表 user 表中,下面展示用户注册的基本界面,如图1所示:
小说推荐展示
用户登陆系统前端首页,会看到系统给当前用户推荐的小说信息,推荐采用的算法为当前比较流行的协同过滤算法,使用的是混合CF模式,即根据用户对小说的操作行来判断用户对哪类数据感兴趣,从而进行相应的数据推荐。
用户论坛交流
平台为用户提供了一个在线交流的小型论坛功能,用户登陆后可以在论坛中发贴交流,用户可以回贴,浏览等相关操作,具体论坛交流如下图所示:
小说资讯浏览
小说资讯主要是平台发布的一些行业内的相关新闻信息,用户可以在线浏览和查看,同时可以根据相关关键字进行模糊搜索,也可以根据相关类型进行筛选,同时可以根据结果指定的几个方式进行排序,比如根据热度(浏览量)、更新时间等进行相关的降序或升序操作。
个人中心
个人中心是系统为当前用户提供的个人信息管理操作功能,可以管理个人的基本资料,修改密码,以及查看自己的收藏记录信息等,具体个人中心管理功能的展示如下图所示
后台用户管理
后台的用户管理模块主要为管理平台用户而设计的管理模块,在此操作界面可以根据用户名来查询用户信息,采用模糊查询的匹配方式实现,可以实现在线添加用户信息,对用户信息进行修改和删除等操作
小说管理
平台管理员在后台对小说信息进行相关的管理操作,主要实现对小说的信息添加,修改和查询、删除操作,同时可以查看小说详情介绍与用户对小说的评价信息,小说文本可以进行文件上传或添加小说的超连接信息,使得前端用户可以进行小说的在线下载操作,
资讯管理
平台管理员在后台对小说资讯信息进行相关的管理操作,主要实现对小说的资讯信息添加,修改和查询、删除操作,同时可以查看小说资讯详情介绍与用户对小说资讯的评价信息,具体展示如图所示
公告管理
平台管理员在后台对平台公告信息进行相关的管理操作,主要实现对公告的信息添加,修改和查询、删除操作,同时可以查看公告详情介绍,具体展示如图所示:
四,核心代码展示
package com.project.demo.controller.base; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.project.demo.service.base.BaseService; import lombok.Setter; import lombok.extern.slf4j.Slf4j; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.multipart.MultipartFile; import javax.persistence.Query; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.File; import java.io.IOException; import java.util.HashMap; import java.util.List; import java.util.Map; /** */ @Slf4j public class BaseController<E, S extends BaseService<E>> { @Setter protected S service; @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); } @PostMapping("/set") @Transactional public Map<String, Object> set(HttpServletRequest request) throws IOException { service.update(service.readQuery(request), service.readConfig(request), service.readBody(request.getReader())); return success(1); } @RequestMapping(value = "/del") @Transactional public Map<String, Object> del(HttpServletRequest request) { service.delete(service.readQuery(request), service.readConfig(request)); return success(1); } @RequestMapping("/get_obj") public Map<String, Object> obj(HttpServletRequest request) { Query select = service.select(service.readQuery(request), service.readConfig(request)); List resultList = select.getResultList(); if (resultList.size() > 0) { JSONObject jsonObject = new JSONObject(); jsonObject.put("obj",resultList.get(0)); return success(jsonObject); } else { return success(null); } } @RequestMapping("/get_list") public Map<String, Object> getList(HttpServletRequest request) { Map<String, Object> map = service.selectToPage(service.readQuery(request), service.readConfig(request)); return success(map); } @RequestMapping("/list_group") public Map<String, Object> listGroup(HttpServletRequest request) { Map<String, Object> map = service.selectToList(service.readQuery(request), service.readConfig(request)); return success(map); } @RequestMapping("/bar_group") public Map<String, Object> barGroup(HttpServletRequest request) { Map<String, Object> map = service.selectBarGroup(service.readQuery(request), service.readConfig(request)); return success(map); } @RequestMapping(value = {"/count_group", "/count"}) public Map<String, Object> count(HttpServletRequest request) { Query count = service.count(service.readQuery(request), service.readConfig(request)); return success(count.getResultList()); } @RequestMapping(value = {"/sum_group", "/sum"}) public Map<String, Object> sum(HttpServletRequest request) { Query count = service.sum(service.readQuery(request), service.readConfig(request)); return success(count.getResultList()); } @RequestMapping(value = {"/avg_group", "/avg"}) public Map<String, Object> avg(HttpServletRequest request) { Query count = service.avg(service.readQuery(request), service.readConfig(request)); return success(count.getResultList()); } @PostMapping("/upload") public Map<String, Object> upload(@RequestParam("file") MultipartFile file) { log.info("进入方法"); if (file.isEmpty()) { return error(30000, "没有选择文件"); } try { //判断有没路径,没有则创建 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(); 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, "上传失败"); } @PostMapping("/import_db") public Map<String, Object> importDb(@RequestParam("file") MultipartFile file) throws IOException { service.importDb(file); return success(1); } @RequestMapping("/export_db") public void exportDb(HttpServletRequest request, HttpServletResponse response) throws IOException { HSSFWorkbook sheets = service.exportDb(service.readQuery(request), service.readConfig(request)); response.setContentType("application/octet-stream"); response.setHeader("Content-disposition", "attachment;filename=employee.xls"); response.flushBuffer(); sheets.write(response.getOutputStream()); sheets.close(); } public Map<String, Object> success(Object o) { Map<String, Object> map = new HashMap<>(); if (o == null) { map.put("result", null); return map; } if (o instanceof List) { if (((List) o).size() == 1) { o = ((List) o).get(0); map.put("result", o); }else { String jsonString = JSONObject.toJSONString(o); JSONArray objects = service.covertArray(JSONObject.parseArray(jsonString)); map.put("result", objects); } } else if (o instanceof Integer || o instanceof String) { map.put("result", o); } else { String jsonString = JSONObject.toJSONString(o); JSONObject jsonObject = JSONObject.parseObject(jsonString); JSONObject j = service.covertObject(jsonObject); map.put("result", j); } return map; } public Map<String, Object> error(Integer code, String message) { Map<String, Object> map = new HashMap<>(); map.put("error", new HashMap<String, Object>(4) {{ put("code", code); put("message", message); }}); return map; } }
package com.project.demo.controller; import com.alibaba.fastjson.JSONObject; import com.project.demo.entity.AccessToken; import com.project.demo.entity.User; import com.project.demo.entity.UserGroup; import com.project.demo.service.AccessTokenService; import com.project.demo.service.UserGroupService; import com.project.demo.service.UserService; import com.project.demo.controller.base.BaseController; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.*; import javax.persistence.Query; import javax.servlet.http.HttpServletRequest; import java.io.IOException; import java.util.*; /** * 用户账户:用于保存用户登录信息(User)表控制层 */ @Slf4j @RestController @RequestMapping("user") public class UserController extends BaseController<User, UserService> { /** * 服务对象 */ @Autowired public UserController(UserService service) { setService(service); } /** * Token服务 */ @Autowired private AccessTokenService tokenService; @Autowired private UserGroupService userGroupService; /** * 注册 * @param user * @return */ @PostMapping("register") public Map<String, Object> signUp(@RequestBody User user) { // 查询用户 Map<String, String> query = new HashMap<>(); query.put("username",user.getUsername()); List list = service.select(query, new HashMap<>()).getResultList(); if (list.size()>0){ return error(30000, "用户已存在"); } user.setUserId(null); user.setPassword(service.encryption(user.getPassword())); service.save(user); return success(1); } /** * 找回密码 * @param form * @return */ @PostMapping("forget_password") public Map<String, Object> forgetPassword(@RequestBody User form,HttpServletRequest request) { JSONObject ret = new JSONObject(); String username = form.getUsername(); String code = form.getCode(); String password = form.getPassword(); // 判断条件 if(code == null || code.length() == 0){ return error(30000, "验证码不能为空"); } if(username == null || username.length() == 0){ return error(30000, "用户名不能为空"); } if(password == null || password.length() == 0){ return error(30000, "密码不能为空"); } // 查询用户 Map<String, String> query = new HashMap<>(); query.put("username",username); Query select = service.select(query, service.readConfig(request)); List list = select.getResultList(); if (list.size() > 0) { User o = (User) list.get(0); JSONObject query2 = new JSONObject(); JSONObject form2 = new JSONObject(); // 修改用户密码 query2.put("user_id",o.getUserId()); form2.put("password",service.encryption(password)); service.update(query, service.readConfig(request), form2); return success(1); } return error(70000,"用户不存在"); } /** * 登录 * @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; Map<String, String> map = new HashMap<>(); if(username != null && "".equals(username) == false){ map.put("username", username); resultList = service.select(map, new HashMap<>()).getResultList(); } else if(email != null && "".equals(email) == false){ map.put("email", email); resultList = service.select(map, new HashMap<>()).getResultList(); } else if(phone != null && "".equals(phone) == false){ map.put("phone", phone); resultList = service.select(map, new HashMap<>()).getResultList(); }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.select(groupMap, new HashMap<>()).getResultList(); if (groupList.size()<1){ return error(30000,"用户组不存在"); } UserGroup userGroup = (UserGroup) groupList.get(0); //查询用户审核状态 if (!StringUtils.isEmpty(userGroup.getSourceTable())){ String sql = "select examine_state from "+ userGroup.getSourceTable() +" WHERE user_id = " + byUsername.getUserId(); String res = String.valueOf(service.runCountSql(sql).getSingleResult()); 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, "账号或密码不正确"); } } /** * 修改密码 * @param data * @param request * @return */ @PostMapping("change_password") public Map<String, Object> change_password(@RequestBody Map<String, String> data, HttpServletRequest request){ // 根据Token获取UserId String token = request.getHeader("x-auth-token"); Integer userId = tokenGetUserId(token); // 根据UserId和旧密码获取用户 Map<String, String> query = new HashMap<>(); String o_password = data.get("o_password"); query.put("user_id" ,String.valueOf(userId)); query.put("password" ,service.encryption(o_password)); Query ret = service.count(query, service.readConfig(request)); List list = ret.getResultList(); Object s = list.get(0); int count = Integer.parseInt(list.get(0).toString()); if(count > 0){ // 修改密码 Map<String,Object> form = new HashMap<>(); form.put("password",service.encryption(data.get("password"))); service.update(query,service.readConfig(request),form); return success(1); } return error(10000,"密码修改失败!"); } /** * 登录态 * @param request * @return */ @GetMapping("state") public Map<String, Object> state(HttpServletRequest request) { JSONObject ret = new JSONObject(); // 获取状态 String token = request.getHeader("x-auth-token"); // 根据登录态获取用户ID Integer userId = tokenGetUserId(token); log.info("[返回userId] {}",userId); if(userId == null || userId == 0){ return error(10000,"用户未登录!"); } // 根据用户ID获取用户 Map<String,String> query = new HashMap<>(); query.put("user_id" ,String.valueOf(userId)); // 根据用户ID获取 Query select = service.select(query,service.readConfig(request)); List resultList = select.getResultList(); if (resultList.size() > 0) { JSONObject user = JSONObject.parseObject(JSONObject.toJSONString(resultList.get(0))); user.put("token",token); ret.put("obj",user); return success(ret); } else { return error(10000,"用户未登录!"); } } /** * 登录态 * @param request * @return */ @GetMapping("quit") public Map<String, Object> quit(HttpServletRequest request) { String token = request.getHeader("x-auth-token"); JSONObject ret = new JSONObject(); Map<String, String> query = new HashMap<>(16); query.put("token", token); try{ tokenService.delete(query,service.readConfig(request)); }catch (Exception e){ e.printStackTrace(); } return success("退出登录成功!"); } /** * 获取登录用户ID * @param token * @return */ public Integer tokenGetUserId(String token) { log.info("[获取的token] {}",token); // 根据登录态获取用户ID if(token == null || "".equals(token)){ return 0; } Map<String, String> query = new HashMap<>(16); query.put("token", token); AccessToken byToken = tokenService.findOne(query); if(byToken == null){ return 0; } return byToken.getUser_id(); } /** * 重写add * @return */ @PostMapping("/add") @Transactional public Map<String, Object> add(HttpServletRequest request) throws IOException { Map<String,Object> map = service.readBody(request.getReader()); map.put("password",service.encryption(String.valueOf(map.get("password")))); service.insert(map); return success(1); } }
五,相关作品展示
基于Java开发、Python开发、PHP开发、C#开发等相关语言开发的实战项目
基于Nodejs、Vue等前端技术开发的前端实战项目
基于微信小程序和安卓APP应用开发的相关作品
基于51单片机等嵌入式物联网开发应用
基于各类算法实现的AI智能应用
基于大数据实现的各类数据管理和推荐系统