项目编号:L-BS-SP-01
一,环境介绍
语言环境:Java: jdk1.8
数据库:Mysql: mysql5.7
应用服务器:Tomcat: tomcat8.5.31
开发工具:IDEA或eclipse
二,项目简介
2.1 技术介绍
- 前端采用Vue、Element UI、nuxt
- 基础平台采用的是RuoYi-Vue
- 后端采用Spring Boot、Spring Security、Redis & Jwt。
- 权限认证使用Jwt,支持多终端认证系统。
- 支持加载动态权限菜单,多方式轻松权限控制。
- 高效率开发,使用代码生成器可以一键生成前后端代码。
- vue-simple-uploader:一款基于 simple-uploader.js 开发的适用于 Vue.js 的分片上传插件
- vue-video-player:一款基于 video.js 开发的视频播放组件
- 集成了奇文网盘qiwen-file
- 后台管理系统模块增加了资源管理模块、媒资管理模块、站点配置模块、博客模块、问答模块
- 集成plumelog日志,计划去除原sysLog日志。日志统一在plumelog上查看
2.2 项目介绍
该平台是一款基于RuoYi-Vue前后端分离基础平台打造的开源电影视频平台。该平台集成了奇文网盘,涵盖了电影、博客、问答等多个模块,致力于为用户提供丰富多样的视频内容以及互动交流的社区体验。后台和门户均采用了Vue+SpringBoot技术栈,保证了系统的高效稳定运行。
功能描述:
一、用户端功能
- 首页:展示最新上映的电影、热门电影推荐、精彩博文、热门问答等内容,为用户提供一站式浏览体验。
- 电影:提供电影详情页,包括电影简介、演员表、剧照、相关推荐等信息,支持在线观看和下载。
- 演员:展示演员个人资料、参演作品、获奖记录等信息,方便用户了解关注的演员动态。
- 文章:允许用户发表和浏览博客文章,分享观影心得、影评、行业动态等内容。
- 问答:提供一个互动问答社区,用户可以提问、回答问题,分享知识和经验。
- 热门:展示平台上的热门电影、文章、问答等内容,方便用户了解平台上的热门话题和趋势。
- 登录与注册:支持用户注册和登录功能,保护用户个人信息和观影记录。
- 搜索:提供全站搜索功能,方便用户快速找到感兴趣的电影、文章或问答。
二、后台管理系统模块
- 资源管理模块:负责管理平台上的电影资源,包括电影的上传、编辑、删除等功能,确保电影资源的准确性和实时性。
- 媒资管理模块:负责管理平台上的视频资源,包括视频的上传、编辑、审核等功能,确保视频内容的质量和合规性。
- 站点配置模块:允许管理员配置站点信息,如站点名称、域名、主题、样式等,实现平台的个性化定制。
- 博客模块:支持管理员对博客文章进行管理,包括文章的审核、编辑、删除等功能,维护平台内容的质量和价值。
- 问答模块:允许管理员对问答内容进行管理,包括问题的审核、回答、删除等操作,保证问答社区的健康和活跃度。
三,系统展示
3.1 门户截图
3.2 后台截图
四,核心代码展示
4.1 演员Service业务层处理
/** * 演员Service业务层处理 */ @Service public class ActorServiceImpl extends ServiceImpl<ActorMapper, Actor> implements IActorService { @Autowired private ActorMapper actorMapper; /** * 查询演员列表 * * @param actor 演员 * @return 演员 */ @Override public List<Actor> selectActorList(Actor actor) { return actorMapper.selectActorList(actor); } /** * 查询热门演员 * * @param top * @return */ @Override public List<Actor> selectHotActorList(int top) { return actorMapper.selectHotActorList(top); } @Override public List<Actor> selectNotSelectedList(Actor actor, Long[] ids) { return actorMapper.selectNotSelectedListByIds(actor, ids); } }
4.2 电影Service业务层处理
/** * 电影Service业务层处理 */ @Service public class MovieServiceImpl extends ServiceImpl<MovieMapper, Movie> implements IMovieService { @Autowired private MovieMapper movieMapper; @Autowired private VideoMapper videoMapper; @Autowired private IVideoService videoService; @Autowired private SysUserMapper sysUserMapper; @Autowired private MovieActorMapper movieActorMapper; @Autowired private TokenUtil tokenUtil; @Autowired private IWebConfigService webConfigService; @Autowired JustAuthConfig justAuthConfig; /** * 查询电影 * * @param movieId 电影ID * @return 电影 */ @Override public MovieVO selectMovieById(Long movieId) { Movie movie = movieMapper.selectById(movieId); MovieVO movieVO = new MovieVO(); if (movie != null) { BeanUtils.copyProperties(movie, movieVO); String publishBy = movieVO.getPublishBy(); if (StringUtils.isNotEmpty(publishBy)) { SysUser sysUser = sysUserMapper.selectUserById(Long.valueOf(publishBy)); movieVO.setPublishUsername(sysUser != null ? sysUser.getNickName() : null); } movieVO.setVideoList(videoService.selectVideoByMovieId(movieId)); //查询演员和导演信息 MovieActorVO movieActorQuery = new MovieActorVO(); movieActorQuery.setMovieId(movieVO.getMovieId()); movieActorQuery.setType(MovieActorType.ACTOR.getValue()); List<MovieActorVO> actorList = movieActorMapper.selectMovieActorList(movieActorQuery); movieActorQuery.setType(MovieActorType.DIRECTOR.getValue()); List<MovieActorVO> directorList = movieActorMapper.selectMovieActorList(movieActorQuery); movieVO.setDirectorList(directorList); movieVO.setActorList(actorList); } return movieVO; } @Override public MovieVO selectWebMovieById(Long movieId) { MovieVO movieVO = movieMapper.selectWebMovieById(movieId); movieVO.setVideoList(videoService.selectVideoByMovieId(movieId)); return movieVO; } /** * 查询电影列表 * * @param movie 电影 * @return 电影 */ @Override public List<MovieVO> selectMovieList(Movie movie) { return movieMapper.selectMovieList(movie); } /** * 新增电影 * * @param movieVO 电影 * @return 结果 */ @Transactional @Override public int insertMovie(MovieVO movieVO) { // 初始化数据 this.initData(movieVO); int rows = movieMapper.insert(movieVO); if (rows > 0) { this.insertVideo(movieVO); this.insertActor(movieVO, MovieActorType.ACTOR); this.insertActor(movieVO, MovieActorType.DIRECTOR); String url = "movie/" + movieVO.getMovieId(); String qrcodePath = generateQr(url); movieVO.setQrcodePath(qrcodePath); movieMapper.updateById(movieVO); } return rows; } /** * 初始化数据 * * @param movieVO */ private void initData(MovieVO movieVO) { String title = movieVO.getTitle(); if (StringUtils.isNotBlank(title)) { String en = PinyinUtil.getPinyin(title, ""); String letter = PinyinUtil.getFirstLetter(title, ""); movieVO.setEn(en); movieVO.setLetter(letter.toUpperCase()); } // 设置视频总长度 List<Video> videoList = movieVO.getVideoList(); if (videoList == null || videoList.size() == 0) { return; } long totalVideoLength = 0; for (Video video : videoList) { String length = video.getLength(); if (StringUtils.isNotEmpty(length)) { totalVideoLength += Long.valueOf(length); } } movieVO.setTotalVideoLength(totalVideoLength); } /** * @param movieVO * @Desc 更新演员 */ private void insertActor(MovieVO movieVO, MovieActorType movieActorType) { Long movieId = movieVO.getMovieId(); List<MovieActorVO> actorList = movieVO.getActorList(); List<MovieActorVO> directorList = movieVO.getDirectorList(); List<MovieActorVO> insertList = null; switch (movieActorType) { case ACTOR: insertList = actorList; break; case DIRECTOR: insertList = directorList; break; default: return; } if (StringUtils.isNotNull(insertList)) { for (MovieActorVO movieActorVO : insertList) { MovieActor movieActor = new MovieActor(); BeanUtils.copyProperties(movieActorVO, movieActor); movieActor.setMovieId(movieId); movieActor.setType(movieActorType.getValue()); movieActorMapper.insertMovieActor(movieActor); } } } /** * 修改电影 * * @param movieVO 电影 * @return 结果 */ @Transactional @Override public int updateMovie(MovieVO movieVO) { videoMapper.deleteByMovieId(movieVO.getMovieId()); // 初始化数据 this.initData(movieVO); this.insertVideo(movieVO); Map movieActorMap = new HashMap<>(); movieActorMap.put("movie_id", movieVO.getMovieId()); movieActorMapper.deleteByMap(movieActorMap); this.insertActor(movieVO, MovieActorType.ACTOR); this.insertActor(movieVO, MovieActorType.DIRECTOR); if (StringUtils.isBlank(movieVO.getQrcodePath())) { String url = "movie/" + movieVO.getMovieId(); String qrcodePath = generateQr(url); movieVO.setQrcodePath(qrcodePath); } return movieMapper.updateById(movieVO); } /** * 删除电影信息 * * @param movieId 电影ID * @return 结果 */ @Override @Transactional public int deleteMovieById(Long movieId) { videoMapper.deleteByMovieId(movieId); return movieMapper.deleteById(movieId); } @Override public List<MovieVO> selectHotMovieList(int top) { return movieMapper.selectHotMovieList(top); } @Override public int removeActorList(Long[] actorIds) { return movieActorMapper.deleteMovieActorByIds(actorIds); } @Override public List<MovieVO> getListByActorId(Long actorId) { return movieMapper.selectListByActorId(actorId); } @Override public List<Movie> getSameTypeMovieList(Long movieId) { MovieVO movieVO = this.selectMovieById(movieId); Integer categoryId = movieVO.getCategoryId(); Page<Movie> page = new Page<>(1, 12); QueryWrapper<Movie> queryWrapper = new QueryWrapper<>(); queryWrapper.eq("status", 0); queryWrapper.eq("category_id", categoryId); queryWrapper.notIn("movie_id", movieId); queryWrapper.select("movie_id", "images", "title", "type", "country", "tag_id", "publish_by", "publish_time", "open_comment", "rate", "category_id", "category_name"); queryWrapper.orderByDesc("create_time"); return movieMapper.selectPage(page, queryWrapper).getRecords(); } /** * 新增电影视频信息 * * @param movieVO 电影对象 */ public void insertVideo(MovieVO movieVO) { List<Video> videoList = movieVO.getVideoList(); Long movieId = movieVO.getMovieId(); if (StringUtils.isNotNull(videoList)) { List<Video> list = new ArrayList<Video>(); for (Video video : videoList) { video.setMovieId(movieId); list.add(video); } if (list.size() > 0) { videoMapper.batchVideo(list); } } } @Override public List<MovieVO> selectWebMovieList(MovieVO movieVO) { return movieMapper.selectWebMovieList(movieVO); } @Override public List<MovieVO> search(SearchParamVO searchParamVO) { List<MovieVO> movieVOList = movieMapper.searchWebMovieList(searchParamVO); String keyword = searchParamVO.getKeyword(); movieVOList.forEach(item -> { // 给标题、简介、分类 设置高亮 item.setTitle(getHitCode(item.getTitle(), keyword)); item.setDescription(getHitCode(item.getDescription(), keyword)); item.setCategoryName(getHitCode(item.getCategoryName(), keyword)); }); return movieVOList; } /** * 添加高亮 * * @param str 截取字符串 * @param keyword 关键字 * @return 返回添加了高亮的字符串 */ private String getHitCode(String str, String keyword) { if (StringUtils.isAnyBlank(str, keyword)) { return ""; } String startStr = "<span style = 'color:red'>"; String endStr = "</span>"; // 判断关键字是否直接是搜索的内容,否者直接返回 if (str.equalsIgnoreCase(keyword)) { return startStr + str + endStr; } String lowerCaseStr = str.toLowerCase(); String lowerKeyword = keyword.toLowerCase(); String[] lowerCaseArray = lowerCaseStr.split(lowerKeyword); boolean isEndWith = lowerCaseStr.endsWith(lowerKeyword); // 计算分割后的字符串位置 int count = 0; List<Map<String, Integer>> list = new ArrayList<>(); List<Map<String, Integer>> keyList = new ArrayList<>(); for (int index = 0; index < lowerCaseArray.length; index++) { // 将切割出来的存储map Map<String, Integer> map = new HashMap<>(); Map<String, Integer> keyMap = new HashMap<>(); map.put("startIndex", count); int len = lowerCaseArray[index].length(); count += len; map.put("endIndex", count); list.add(map); if (index < lowerCaseArray.length - 1 || isEndWith) { // 将keyword存储map keyMap.put("startIndex", count); count += keyword.length(); keyMap.put("endIndex", count); keyList.add(keyMap); } } // 截取切割对象 List<String> arrayList = new ArrayList<>(); for (Map<String, Integer> item : list) { Integer start = item.get("startIndex"); Integer end = item.get("endIndex"); String itemStr = str.substring(start, end); arrayList.add(itemStr); } // 截取关键字 List<String> keyArrayList = new ArrayList<>(); for (Map<String, Integer> item : keyList) { Integer start = item.get("startIndex"); Integer end = item.get("endIndex"); String itemStr = str.substring(start, end); keyArrayList.add(itemStr); } StringBuilder sb = new StringBuilder(); for (int index = 0; index < arrayList.size(); index++) { sb.append(arrayList.get(index)); if (index < arrayList.size() - 1 || isEndWith) { sb.append(startStr); sb.append(keyArrayList.get(index)); sb.append(endStr); } } return sb.toString(); } /** * 生成二维码 * * @param url * @return */ private String generateQr(String url) { String profile = RuoYiConfig.getProfile(); WebConfig webConfig = webConfigService.getWebConfig(); String logoURL = webConfig.getLogo(); String logoPath = profile + logoURL; QrConfig qrConfig = QrConfig.create() .setWidth(300) .setHeight(300); File logoFile = new File(logoPath); if (logoFile.exists()) { qrConfig.setImg(logoFile); } String qrRootPath = RuoYiConfig.getQrPath(); String websiteUrl = justAuthConfig.getWebsiteUrl(); String fileName = DateUtils.datePath() + "/" + IdUtils.fastUUID().replace("-", "") + ".jpg"; String realQrPath = qrRootPath + File.separator + fileName; File realQrFile = new File(realQrPath); if (!realQrFile.exists()) { if (!realQrFile.getParentFile().exists()) { realQrFile.getParentFile().mkdirs(); } } File file = QrCodeUtil.generate(websiteUrl + url, qrConfig, FileUtil.file(realQrFile)); int dirLastIndex = RuoYiConfig.getProfile().length() + 1; String currentDir = StringUtils.substring(qrRootPath, dirLastIndex); if (file.exists()) { return "/" + currentDir + "/" + fileName; } else { return ""; } } }
五,相关作品展示
基于Java开发、Python开发、PHP开发、C#开发等相关语言开发的实战项目
基于Nodejs、Vue等前端技术开发的前端实战项目
基于微信小程序和安卓APP应用开发的相关作品
基于51单片机等嵌入式物联网开发应用
基于各类算法实现的AI智能应用
基于大数据实现的各类数据管理和推荐系统