1、项目功能演示
DC00025【含文档】基于springboot短视频推荐管理系统协同过滤算法视频推荐系统javaweb开发程序设计vue
2、项目功能描述
短视频推荐系统分为用户和系统管理员两个角色
2.1 用户角色
1、用户登录、用户注册
2、视频中心:信息查看、视频收藏、点赞、踩、评论
3、公告信息:信息查看、收藏、点赞
4、个人信息、修改密码、我的收藏
2.2 管理员角色
1、系统登录
2、个人中心:修改密码、个人信息修改
3、用户管理
4、视频中心管理
5、视频分类管理
6、系统管理:轮播图管理、公告管理、公告信息分类管理
3、项目运行截图
4、项目核心代码
4.1 公共处理类
package com.utils;import java.util.Random;
import java.util.ArrayList;
import org.springframework.stereotype.Component;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.Cell;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import org.apache.poi.ss.usermodel.DateUtil;
import java.util.Objects;
import com.alibaba.fastjson.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;@Component
public class CommonUtil {/*** 获取随机字符串** @param num* @return*/public static String getRandomString(Integer num) {String base = "abcdefghijklmnopqrstuvwxyz0123456789";Random random = new Random();StringBuffer sb = new StringBuffer();for (int i = 0; i < num; i++) {int number = random.nextInt(base.length());sb.append(base.charAt(number));}return sb.toString();}/*** 获取随机验证码** @param num* @return*/public static String getRandomNumber(Integer num) {String base = "0123456789";Random random = new Random();StringBuffer sb = new StringBuffer();for (int i = 0; i < num; i++) {int number = random.nextInt(base.length());sb.append(base.charAt(number));}return sb.toString();}public static String getCellValue(Cell cell) {String resultValue = "";// 判空if (Objects.isNull(cell)) {return resultValue;}// 拿到单元格类型int cellType = cell.getCellType();switch (cellType) {// 字符串类型case Cell.CELL_TYPE_STRING:resultValue = StringUtils.isEmpty(cell.getStringCellValue()) ? "" : cell.getStringCellValue().trim();break;// 布尔类型case Cell.CELL_TYPE_BOOLEAN:resultValue = String.valueOf(cell.getBooleanCellValue());break;// 数值类型case Cell.CELL_TYPE_NUMERIC:/*** format 的值可能为以下这些 yyyyMMddHHmmss* yyyy-MM-dd----- 14* yyyy年m月d日----- 31* yyyy年m月--------57* m月d日 -----------58* HH:mm-----------20* h时mm分 --------- 32*/Object val = cell.getNumericCellValue();// POI Excel 日期格式转换String formatDate = "";switch (cell.getCellStyle().getDataFormat()){case 14:formatDate = "yyyy-MM-dd";break;case 20:formatDate = "HH:mm";break;case 21:formatDate = "HH:mm:ss";break;case 31:formatDate = "yyyy年MM月dd日";break;case 32:formatDate = "HH时mm分";break;case 33:formatDate = "HH时mm分mm秒";break;case 57:formatDate = "yyyy年MM月";break;case 58:formatDate = "MM月dd日";break;case 176:formatDate = "yyyy-MM-dd HH:mm:ss";break;}if(!"".equals(formatDate)){resultValue = new SimpleDateFormat(formatDate).format(DateUtil.getJavaDate((Double) val));}else{resultValue = new DecimalFormat("#.######").format(cell.getNumericCellValue());}break;// 取空串default:break;}return resultValue;}}
4.2 加密解密处理类
package com.utils;import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.util.Base64;import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;import cn.hutool.crypto.digest.DigestUtil;public class EncryptUtil {/*** md5算法* @param text明文* @param key密钥* @return 密文*/// 带秘钥加密public static String md5(String text) {if(text==null) return null;// 加密后的字符串String md5str = DigestUtil.md5Hex(text);return md5str;}/*** SHA-256算法* @param text* @return* @throws Exception*/public static String sha256(String text) {if(text==null) return null;StringBuilder stringBuilder = new StringBuilder();try {//获取SHA-256算法实例MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");//计算散列值byte[] digest = messageDigest.digest(text.getBytes());//将byte数组转换为15进制字符串for (byte b : digest) {stringBuilder.append(Integer.toHexString((b & 0xFF) | 0x100), 1, 3);}} catch (NoSuchAlgorithmException e) {// TODO Auto-generated catch blocke.printStackTrace();}return stringBuilder.toString();}private static final String DES_ALGORITHM = "DES";/*** DES加密** @param data 待加密的数据* @param key 密钥,长度必须为8位* @return 加密后的数据,使用Base64编码*/public static String desEncrypt(String text) {if(text==null) return null;try {String key = "12345678";// 根据密钥生成密钥规范KeySpec keySpec = new DESKeySpec(key.getBytes());// 根据密钥规范生成密钥工厂SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance(DES_ALGORITHM);// 根据密钥工厂和密钥规范生成密钥SecretKey secretKey = secretKeyFactory.generateSecret(keySpec);// 根据加密算法获取加密器Cipher cipher = Cipher.getInstance(DES_ALGORITHM);// 初始化加密器,设置加密模式和密钥cipher.init(Cipher.ENCRYPT_MODE, secretKey);// 加密数据byte[] encryptedData = cipher.doFinal(text.getBytes());// 对加密后的数据进行Base64编码return Base64.getEncoder().encodeToString(encryptedData);} catch (InvalidKeyException e) {e.printStackTrace();} catch (NoSuchAlgorithmException e) {e.printStackTrace();} catch (InvalidKeySpecException e) {e.printStackTrace();} catch (NoSuchPaddingException e) {e.printStackTrace();} catch (IllegalBlockSizeException e) {e.printStackTrace();} catch (BadPaddingException e) {e.printStackTrace();}return null;}/*** DES解密** @param encryptedData 加密后的数据,使用Base64编码* @param key 密钥,长度必须为8位* @return 解密后的数据*/public static String desDecrypt(String text) {if(text==null) return null;try {String key = "12345678";// 根据密钥生成密钥规范KeySpec keySpec = new DESKeySpec(key.getBytes());// 根据密钥规范生成密钥工厂SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance(DES_ALGORITHM);// 根据密钥工厂和密钥规范生成密钥SecretKey secretKey = secretKeyFactory.generateSecret(keySpec);// 对加密后的数据进行Base64解码byte[] decodedData = Base64.getDecoder().decode(text);// 根据加密算法获取解密器Cipher cipher = Cipher.getInstance(DES_ALGORITHM);// 初始化解密器,设置解密模式和密钥cipher.init(Cipher.DECRYPT_MODE, secretKey);// 解密数据byte[] decryptedData = cipher.doFinal(decodedData);// 将解密后的数据转换为字符串return new String(decryptedData);} catch (InvalidKeyException e) {e.printStackTrace();} catch (NoSuchAlgorithmException e) {e.printStackTrace();} catch (InvalidKeySpecException e) {e.printStackTrace();} catch (NoSuchPaddingException e) {e.printStackTrace();} catch (IllegalBlockSizeException e) {e.printStackTrace();} catch (BadPaddingException e) {e.printStackTrace();}return null;}private static final String AES_ALGORITHM = "AES";// AES加密模式为CBC,填充方式为PKCS5Paddingprivate static final String AES_TRANSFORMATION = "AES/CBC/PKCS5Padding";// AES密钥为16位private static final String AES_KEY = "1234567890123456";// AES初始化向量为16位private static final String AES_IV = "abcdefghijklmnop";/*** AES加密** @param data 待加密的数据* @return 加密后的数据,使用Base64编码*/public static String aesEncrypt(String text) {if(text==null) return null;try {// 将AES密钥转换为SecretKeySpec对象SecretKeySpec secretKeySpec = new SecretKeySpec(AES_KEY.getBytes(), AES_ALGORITHM);// 将AES初始化向量转换为IvParameterSpec对象IvParameterSpec ivParameterSpec = new IvParameterSpec(AES_IV.getBytes());// 根据加密算法获取加密器Cipher cipher = Cipher.getInstance(AES_TRANSFORMATION);// 初始化加密器,设置加密模式、密钥和初始化向量cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);// 加密数据byte[] encryptedData = cipher.doFinal(text.getBytes(StandardCharsets.UTF_8));// 对加密后的数据使用Base64编码return Base64.getEncoder().encodeToString(encryptedData);} catch (InvalidKeyException e) {e.printStackTrace();} catch (NoSuchAlgorithmException e) {e.printStackTrace();} catch (NoSuchPaddingException e) {e.printStackTrace();} catch (InvalidAlgorithmParameterException e) {e.printStackTrace();} catch (IllegalBlockSizeException e) {e.printStackTrace();} catch (BadPaddingException e) {e.printStackTrace();}return null;}/*** AES解密** @param encryptedData 加密后的数据,使用Base64编码* @return 解密后的数据*/public static String aesDecrypt(String text) {if(text==null) return null;try {// 将AES密钥转换为SecretKeySpec对象SecretKeySpec secretKeySpec = new SecretKeySpec(AES_KEY.getBytes(), AES_ALGORITHM);// 将AES初始化向量转换为IvParameterSpec对象IvParameterSpec ivParameterSpec = new IvParameterSpec(AES_IV.getBytes());// 根据加密算法获取解密器Cipher cipher = Cipher.getInstance(AES_TRANSFORMATION);// 初始化解密器,设置解密模式、密钥和初始化向量cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);// 对加密后的数据使用Base64解码byte[] decodedData = Base64.getDecoder().decode(text);// 解密数据byte[] decryptedData = cipher.doFinal(decodedData);// 返回解密后的数据return new String(decryptedData, StandardCharsets.UTF_8);} catch (InvalidKeyException e) {e.printStackTrace();} catch (NoSuchAlgorithmException e) {e.printStackTrace();} catch (NoSuchPaddingException e) {e.printStackTrace();} catch (InvalidAlgorithmParameterException e) {e.printStackTrace();} catch (IllegalBlockSizeException e) {e.printStackTrace();} catch (BadPaddingException e) {e.printStackTrace();}return null;}}
4.3 基于用户的协同过滤算法
package com.utils;/**
* 类说明 : 基于用户的协同过滤算法
*/import java.util.*;
import java.util.stream.Collectors;public class UserBasedCollaborativeFiltering {private Map<String, Map<String, Double>> userRatings;private Map<String, List<String>> itemUsers;private Map<String, Integer> userIndex;private Map<Integer, String> indexUser;private Long[][] sparseMatrix;public UserBasedCollaborativeFiltering(Map<String, Map<String, Double>> userRatings) {this.userRatings = userRatings;this.itemUsers = new HashMap<>();this.userIndex = new HashMap<>();//辅助存储每一个用户的用户索引index映射:user->indexthis.indexUser = new HashMap<>();//辅助存储每一个索引index对应的用户映射:index->user// 构建物品-用户倒排表int keyIndex = 0;for (String user : userRatings.keySet()) {Map<String, Double> ratings = userRatings.get(user);for (String item : ratings.keySet()) {if (!itemUsers.containsKey(item)) {itemUsers.put(item, new ArrayList<>());}itemUsers.get(item).add(user);}//用户ID与稀疏矩阵建立对应关系this.userIndex.put(user,keyIndex);this.indexUser.put(keyIndex,user);keyIndex++;}int N = userRatings.size();this.sparseMatrix=new Long[N][N];//建立用户稀疏矩阵,用于用户相似度计算【相似度矩阵】for(int i=0;i<N;i++){for(int j=0;j<N;j++)this.sparseMatrix[i][j]=(long)0;}for(String item : itemUsers.keySet()) {List<String> userList = itemUsers.get(item);for(String u1 : userList) {for(String u2 : userList) {if(u1.equals(u2)){continue;}this.sparseMatrix[this.userIndex.get(u1)][this.userIndex.get(u2)]+=1;}}}}public double calculateSimilarity(String user1, String user2) {//计算用户之间的相似度【余弦相似性】Integer id1 = this.userIndex.get(user1);Integer id2 = this.userIndex.get(user2);if(id1==null || id2==null) return 0.0;return this.sparseMatrix[id1][id2]/Math.sqrt(userRatings.get(indexUser.get(id1)).size()*userRatings.get(indexUser.get(id2)).size());}public List<String> recommendItems(String targetUser, int numRecommendations) {// 计算目标用户与其他用户的相似度Map<String, Double> userSimilarities = new HashMap<>();for (String user : userRatings.keySet()) {if (!user.equals(targetUser)) {double similarity = calculateSimilarity(targetUser, user);userSimilarities.put(user, similarity);}}// 根据相似度进行排序List<Map.Entry<String, Double>> sortedSimilarities = new ArrayList<>(userSimilarities.entrySet());sortedSimilarities.sort(Map.Entry.comparingByValue(Comparator.reverseOrder()));// 选择相似度最高的K个用户List<String> similarUsers = new ArrayList<>();for (int i = 0; i < numRecommendations; i++) {if (i < sortedSimilarities.size()) {similarUsers.add(sortedSimilarities.get(i).getKey());} else {break;}}// 获取相似用户喜欢的物品,并进行推荐Map<String, Double> recommendations = new HashMap<>();for (String user : similarUsers) {Map<String, Double> ratings = userRatings.get(user);for (String item : ratings.keySet()) {if (userRatings.get(targetUser)!=null && !userRatings.get(targetUser).containsKey(item)) {recommendations.put(item, ratings.get(item));}}}// 排序推荐物品LinkedHashMap<String, Double> sortedRecommendations = new LinkedHashMap<>(recommendations);// 取前N个推荐物品int numItems = Math.min(numRecommendations, sortedRecommendations.size());sortedRecommendations = sortedRecommendations.entrySet().stream().sorted((Map.Entry.<String, Double>comparingByValue().reversed())).limit(numItems).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));List<String> result = new ArrayList<String>();result.addAll(sortedRecommendations.keySet());return result;}}
4.4 SQL过滤
package com.utils;import org.apache.commons.lang3.StringUtils;import com.entity.EIException;/*** SQL过滤*/
public class SQLFilter {/*** SQL注入过滤* @param str 待验证的字符串*/public static String sqlInject(String str){if(StringUtils.isBlank(str)){return null;}//去掉'|"|;|\字符str = StringUtils.replace(str, "'", "");str = StringUtils.replace(str, "\"", "");str = StringUtils.replace(str, ";", "");str = StringUtils.replace(str, "\\", "");//转换成小写str = str.toLowerCase();//非法字符String[] keywords = {"master", "truncate", "insert", "select", "delete", "update", "declare", "alter", "drop"};//判断是否包含非法字符for(String keyword : keywords){if(str.indexOf(keyword) != -1){throw new EIException("包含非法字符");}}return str;}
}
4.5 分页工具类
package com.utils;import java.io.Serializable;
import java.util.List;
import java.util.Map;import com.baomidou.mybatisplus.plugins.Page;/*** 分页工具类*/
public class PageUtils implements Serializable {private static final long serialVersionUID = 1L;//总记录数private long total;//每页记录数private int pageSize;//总页数private long totalPage;//当前页数private int currPage;//列表数据private List<?> list;/*** 分页* @param list 列表数据* @param totalCount 总记录数* @param pageSize 每页记录数* @param currPage 当前页数*/public PageUtils(List<?> list, int totalCount, int pageSize, int currPage) {this.list = list;this.total = totalCount;this.pageSize = pageSize;this.currPage = currPage;this.totalPage = (int)Math.ceil((double)totalCount/pageSize);}/*** 分页*/public PageUtils(Page<?> page) {this.list = page.getRecords();this.total = page.getTotal();this.pageSize = page.getSize();this.currPage = page.getCurrent();this.totalPage = page.getPages();}/** 空数据的分页*/public PageUtils(Map<String, Object> params) {Page page =new Query(params).getPage();new PageUtils(page);}public int getPageSize() {return pageSize;}public void setPageSize(int pageSize) {this.pageSize = pageSize;}public int getCurrPage() {return currPage;}public void setCurrPage(int currPage) {this.currPage = currPage;}public List<?> getList() {return list;}public void setList(List<?> list) {this.list = list;}public long getTotalPage() {return totalPage;}public void setTotalPage(long totalPage) {this.totalPage = totalPage;}public long getTotal() {return total;}public void setTotal(long total) {this.total = total;}}
5、项目包含
6、项目获取
6.1 方式一
私聊或者扫描下方名片获取项目文件。
6.2 方式二
点击此处直接获取项目文件。