├── .gitignore ├── .mvn └── wrapper │ ├── maven-wrapper.jar │ └── maven-wrapper.properties ├── LICENSE ├── README.md ├── README_IMG ├── 系统功能模块.png └── 项目打成jar包.png ├── mrs.sql ├── mvnw ├── mvnw.cmd ├── pom.xml └── src ├── main ├── java │ └── cn │ │ └── zjw │ │ └── mrs │ │ ├── MovieRecommendationSystemApplication.java │ │ ├── config │ │ ├── CorsConfig.java │ │ ├── ExecutorConfig.java │ │ ├── MySqlDataModelConfig.java │ │ ├── MybatisPlusConfig.java │ │ ├── OssClientConfig.java │ │ ├── RedisConfig.java │ │ └── SecurityConfig.java │ │ ├── controller │ │ ├── AuthController.java │ │ ├── CommentController.java │ │ ├── MovieController.java │ │ ├── RecommendationController.java │ │ ├── UserController.java │ │ └── UserPreferenceController.java │ │ ├── entity │ │ ├── Comment.java │ │ ├── LoginUser.java │ │ ├── Movie.java │ │ ├── MovieFeature.java │ │ ├── MovieRegion.java │ │ ├── MovieType.java │ │ ├── Preference.java │ │ ├── Recommendation.java │ │ ├── RegionLike.java │ │ ├── Result.java │ │ ├── SameLikes.java │ │ ├── TypeLike.java │ │ ├── User.java │ │ └── UserLike.java │ │ ├── enums │ │ ├── HttpCodeEnum.java │ │ ├── LikedStatusEnum.java │ │ ├── RecommendationTypeEnum.java │ │ ├── RegionEnum.java │ │ ├── SexEnum.java │ │ └── TypeEnum.java │ │ ├── filter │ │ └── JwtAuthenticationTokenFilter.java │ │ ├── handler │ │ ├── AccessDeniedHandlerImpl.java │ │ └── AuthenticationEntryPointImpl.java │ │ ├── mapper │ │ ├── CommentMapper.java │ │ ├── MovieFeatureMapper.java │ │ ├── MovieMapper.java │ │ ├── MovieRegionMapper.java │ │ ├── MovieTypeMapper.java │ │ ├── RecommendationMapper.java │ │ ├── RegionLikeMapper.java │ │ ├── SameLikesMapper.java │ │ ├── TypeLikeMapper.java │ │ ├── UserLikeMapper.java │ │ └── UserMapper.java │ │ ├── service │ │ ├── AuthService.java │ │ ├── CommentService.java │ │ ├── MovieFeatureService.java │ │ ├── MovieRegionService.java │ │ ├── MovieService.java │ │ ├── MovieTypeService.java │ │ ├── OssService.java │ │ ├── RecommendationService.java │ │ ├── RegionLikeService.java │ │ ├── SameLikesService.java │ │ ├── TypeLikeService.java │ │ ├── UserLikeRedisService.java │ │ ├── UserLikeService.java │ │ ├── UserService.java │ │ └── impl │ │ │ ├── AuthServiceImpl.java │ │ │ ├── CommentServiceImpl.java │ │ │ ├── MovieFeatureServiceImpl.java │ │ │ ├── MovieRegionServiceImpl.java │ │ │ ├── MovieServiceImpl.java │ │ │ ├── MovieTypeServiceImpl.java │ │ │ ├── OssServiceImpl.java │ │ │ ├── RecommendationServiceImpl.java │ │ │ ├── RegionLikeServiceImpl.java │ │ │ ├── SameLikesServiceImpl.java │ │ │ ├── TypeLikeServiceImpl.java │ │ │ ├── UserDetailsServiceImpl.java │ │ │ ├── UserLikeRedisServiceImpl.java │ │ │ ├── UserLikeServiceImpl.java │ │ │ └── UserServiceImpl.java │ │ ├── utils │ │ ├── Base64DecodedMultipartFile.java │ │ ├── FastJsonRedisSerializer.java │ │ ├── FileUtil.java │ │ ├── JwtUtil.java │ │ ├── MailUtil.java │ │ ├── MyDataModel.java │ │ ├── PicUrlUtil.java │ │ ├── RecommendationUtil.java │ │ ├── RedisCache.java │ │ ├── RedisKeyUtil.java │ │ └── WebUtils.java │ │ └── vo │ │ ├── comment │ │ ├── CommentMovieVo.java │ │ └── CommentStripVo.java │ │ ├── movie │ │ ├── MovieCardVo.java │ │ ├── MovieStripVo.java │ │ ├── RecommendedMovieVo.java │ │ ├── ReviewedMovieStripVo.java │ │ └── relation │ │ │ ├── CategoryVo.java │ │ │ ├── LinkVo.java │ │ │ └── NodeVo.java │ │ └── user │ │ ├── LoginUserVo.java │ │ └── UserInfoVo.java └── resources │ ├── application.yml │ ├── mapper │ ├── CommentMapper.xml │ ├── MovieFeatureMapper.xml │ ├── MovieMapper.xml │ ├── MovieRegionMapper.xml │ ├── MovieTypeMapper.xml │ ├── RecommendationMapper.xml │ ├── RegionLikeMapper.xml │ ├── SameLikesMapper.xml │ ├── TypeLikeMapper.xml │ ├── UserLikeMapper.xml │ └── UserMapper.xml │ └── static │ └── assets │ └── StopWords.txt └── test └── java └── cn └── zjw └── mrs ├── CommentTest.java ├── JiebaTest.java ├── MailTest.java ├── MovieRecommendationSystemApplicationTests.java ├── MovieTest.java ├── RecommendTest.java ├── RedisTest.java ├── UserLikeTest.java └── UserMapperTest.java /.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | !**/src/main/**/build/ 30 | !**/src/test/**/build/ 31 | 32 | ### VS Code ### 33 | .vscode/ 34 | -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junweizeng/movie_recommendation_system_server/ef700cde27920be226f10d5e453ef154ea685396/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.4/apache-maven-3.8.4-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Junwei Zeng 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 电影推荐系统(后端) 2 | 3 | ## Star History 4 | 5 | [![Star History Chart](https://api.star-history.com/svg?repos=junweizeng/movie_recommendation_system_server&type=Date)](https://www.star-history.com/#junweizeng/movie_recommendation_system_server&Date) 6 | 7 | ## 前言 8 | 9 | 总项目名称:`电影推荐系统` 10 | 11 | 项目采用`前后端分离`: 12 | 13 | 1. 前端: 14 | - 仓库地址:[movie_recommendation_system_vue](https://github.com/jun-wei-zeng/movie_recommendation_system_vue) 15 | - 技术栈:`Vue3` + `Element Plus` + `axios`等 16 | 2. 后端: 17 | - 仓库地址:[movie_recommendation_system_server](https://github.com/jun-wei-zeng/movie_recommendation_system_server) 18 | - 技术栈:`Spring Boot` + `Spring Security` + `Redis` + `MyBatis-Plus`等 19 | 3. 数据爬虫: 20 | - 简介:爬取项目所需的电影基本信息数据和用户评价数据等并存储。 21 | - 仓库地址:[douban_movie_spider_mrs](https://github.com/jun-wei-zeng/douban_movie_spider_mrs/tree/master) 22 | - 技术栈:`requests` + `lxml` 23 | 24 | 系统功能模块总览: 25 | 26 | ![系统功能模块.png](README_IMG/系统功能模块.png) 27 | 28 | ## 1. 项目打包部署 29 | 30 | ### 1.1 项目打包 31 | 32 | IDEA右边栏中选择`Maven` → 按住`Ctrl` → 选择`Lifecycle`下的`clean`和`package` → 点击上方的`绿色运行按钮` → 等待项目打包成jar包 → 打包好的jar包会在项目的`target`目录中 33 | 34 | ![项目打成jar包.png](README_IMG/项目打成jar包.png) 35 | 36 | ### 1.2 项目部署 37 | 38 | 1. 将打包好的`项目jar包`上传到自己的服务器上。 39 | 40 | 2. 通过以下命令时项目在服务器后台运行,并且输出日志到`out.txt`文件(可修改): 41 | 42 | >nohup java -jar 项目名.jar >out.txt & 43 | 44 | ## 2. 后端功能实现 45 | 46 | 1. 用户登录、注册、个人信息修改等接口实现。 47 | 2. 基于内容推荐和基于用户协同过滤推荐等推荐算法实现,整合两种算法实现混合式推荐,并解决冷启动问题。 48 | 3. 查询各种电影信息接口实现(如电影搜索、推荐给用户的电影信息查询等)。 49 | 4. 用户点赞功接口实现(点赞信息暂存,再按时持久化到数据库)。 50 | 5. ... 51 | -------------------------------------------------------------------------------- /README_IMG/系统功能模块.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junweizeng/movie_recommendation_system_server/ef700cde27920be226f10d5e453ef154ea685396/README_IMG/系统功能模块.png -------------------------------------------------------------------------------- /README_IMG/项目打成jar包.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junweizeng/movie_recommendation_system_server/ef700cde27920be226f10d5e453ef154ea685396/README_IMG/项目打成jar包.png -------------------------------------------------------------------------------- /mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM https://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM set title of command window 39 | title %0 40 | @REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' 41 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 42 | 43 | @REM set %HOME% to equivalent of $HOME 44 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 45 | 46 | @REM Execute a user defined script before this one 47 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 48 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 49 | if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* 50 | if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* 51 | :skipRcPre 52 | 53 | @setlocal 54 | 55 | set ERROR_CODE=0 56 | 57 | @REM To isolate internal variables from possible post scripts, we use another setlocal 58 | @setlocal 59 | 60 | @REM ==== START VALIDATION ==== 61 | if not "%JAVA_HOME%" == "" goto OkJHome 62 | 63 | echo. 64 | echo Error: JAVA_HOME not found in your environment. >&2 65 | echo Please set the JAVA_HOME variable in your environment to match the >&2 66 | echo location of your Java installation. >&2 67 | echo. 68 | goto error 69 | 70 | :OkJHome 71 | if exist "%JAVA_HOME%\bin\java.exe" goto init 72 | 73 | echo. 74 | echo Error: JAVA_HOME is set to an invalid directory. >&2 75 | echo JAVA_HOME = "%JAVA_HOME%" >&2 76 | echo Please set the JAVA_HOME variable in your environment to match the >&2 77 | echo location of your Java installation. >&2 78 | echo. 79 | goto error 80 | 81 | @REM ==== END VALIDATION ==== 82 | 83 | :init 84 | 85 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 86 | @REM Fallback to current working directory if not found. 87 | 88 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 89 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 90 | 91 | set EXEC_DIR=%CD% 92 | set WDIR=%EXEC_DIR% 93 | :findBaseDir 94 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 95 | cd .. 96 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 97 | set WDIR=%CD% 98 | goto findBaseDir 99 | 100 | :baseDirFound 101 | set MAVEN_PROJECTBASEDIR=%WDIR% 102 | cd "%EXEC_DIR%" 103 | goto endDetectBaseDir 104 | 105 | :baseDirNotFound 106 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 107 | cd "%EXEC_DIR%" 108 | 109 | :endDetectBaseDir 110 | 111 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 112 | 113 | @setlocal EnableExtensions EnableDelayedExpansion 114 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 115 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 116 | 117 | :endReadAdditionalConfig 118 | 119 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 120 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 121 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 122 | 123 | set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" 124 | 125 | FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( 126 | IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B 127 | ) 128 | 129 | @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 130 | @REM This allows using the maven wrapper in projects that prohibit checking in binary data. 131 | if exist %WRAPPER_JAR% ( 132 | if "%MVNW_VERBOSE%" == "true" ( 133 | echo Found %WRAPPER_JAR% 134 | ) 135 | ) else ( 136 | if not "%MVNW_REPOURL%" == "" ( 137 | SET DOWNLOAD_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" 138 | ) 139 | if "%MVNW_VERBOSE%" == "true" ( 140 | echo Couldn't find %WRAPPER_JAR%, downloading it ... 141 | echo Downloading from: %DOWNLOAD_URL% 142 | ) 143 | 144 | powershell -Command "&{"^ 145 | "$webclient = new-object System.Net.WebClient;"^ 146 | "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ 147 | "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ 148 | "}"^ 149 | "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ 150 | "}" 151 | if "%MVNW_VERBOSE%" == "true" ( 152 | echo Finished downloading %WRAPPER_JAR% 153 | ) 154 | ) 155 | @REM End of extension 156 | 157 | @REM Provide a "standardized" way to retrieve the CLI args that will 158 | @REM work with both Windows and non-Windows executions. 159 | set MAVEN_CMD_LINE_ARGS=%* 160 | 161 | %MAVEN_JAVA_EXE% ^ 162 | %JVM_CONFIG_MAVEN_PROPS% ^ 163 | %MAVEN_OPTS% ^ 164 | %MAVEN_DEBUG_OPTS% ^ 165 | -classpath %WRAPPER_JAR% ^ 166 | "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ 167 | %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 168 | if ERRORLEVEL 1 goto error 169 | goto end 170 | 171 | :error 172 | set ERROR_CODE=1 173 | 174 | :end 175 | @endlocal & set ERROR_CODE=%ERROR_CODE% 176 | 177 | if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost 178 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 179 | if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" 180 | if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" 181 | :skipRcPost 182 | 183 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 184 | if "%MAVEN_BATCH_PAUSE%"=="on" pause 185 | 186 | if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% 187 | 188 | cmd /C exit /B %ERROR_CODE% 189 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 2.6.6 9 | 10 | 11 | cn.zjw 12 | mrs 13 | 0.0.1-SNAPSHOT 14 | movie_recommendation_system 15 | movie_recommendation_system 16 | 17 | 1.8 18 | 19 | 20 | 21 | org.springframework.boot 22 | spring-boot-starter-web 23 | 24 | 25 | org.mybatis.spring.boot 26 | mybatis-spring-boot-starter 27 | 2.2.2 28 | 29 | 30 | org.springframework.boot 31 | spring-boot-starter-security 32 | 33 | 34 | org.springframework.boot 35 | spring-boot-starter-mail 36 | 37 | 38 | 39 | org.springframework.boot 40 | spring-boot-devtools 41 | runtime 42 | true 43 | 44 | 45 | mysql 46 | mysql-connector-java 47 | runtime 48 | 49 | 50 | org.springframework.boot 51 | spring-boot-configuration-processor 52 | true 53 | 54 | 55 | org.projectlombok 56 | lombok 57 | true 58 | 59 | 60 | org.springframework.boot 61 | spring-boot-starter-test 62 | test 63 | 64 | 65 | com.baomidou 66 | mybatis-plus-boot-starter 67 | 3.5.1 68 | 69 | 70 | cn.hutool 71 | hutool-all 72 | 5.8.21 73 | 74 | 75 | 76 | 77 | org.springframework.boot 78 | spring-boot-starter-data-redis 79 | 80 | 81 | 82 | com.alibaba 83 | fastjson 84 | 1.2.83 85 | 86 | 87 | 88 | io.jsonwebtoken 89 | jjwt 90 | 0.9.1 91 | 92 | 93 | 94 | com.aliyun.oss 95 | aliyun-sdk-oss 96 | 3.14.0 97 | 98 | 99 | 100 | org.nd4j 101 | nd4j-native-platform 102 | 1.0.0-beta7 103 | 104 | 105 | 106 | org.apache.mahout 107 | mahout-core 108 | 0.9 109 | 110 | 111 | org.apache.mahout 112 | mahout-integration 113 | 0.9 114 | 115 | 116 | 117 | 118 | mysql 119 | mysql-connector-java 120 | 8.0.28 121 | 122 | 123 | 124 | 125 | com.huaban 126 | jieba-analysis 127 | 1.0.2 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | org.springframework.boot 136 | spring-boot-maven-plugin 137 | 138 | 139 | 140 | org.projectlombok 141 | lombok 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/MovieRecommendationSystemApplication.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.scheduling.annotation.EnableScheduling; 6 | 7 | /** 8 | * @author zjw 9 | */ 10 | @SpringBootApplication 11 | @EnableScheduling 12 | public class MovieRecommendationSystemApplication { 13 | 14 | public static void main(String[] args) { 15 | SpringApplication.run(MovieRecommendationSystemApplication.class, args); 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/config/CorsConfig.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.config; 2 | 3 | import org.springframework.context.annotation.Configuration; 4 | import org.springframework.web.servlet.config.annotation.CorsRegistry; 5 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 6 | 7 | /** 8 | * @author zjw 9 | * @Classname CorsConfig 10 | * @Date 2022/4/7 22:31 11 | * @Description 12 | */ 13 | @Configuration 14 | public class CorsConfig implements WebMvcConfigurer { 15 | @Override 16 | public void addCorsMappings(CorsRegistry registry) { 17 | // 设置允许跨域的路径 18 | registry.addMapping("/**") 19 | // 设置允许跨域请求的域名 20 | .allowedOriginPatterns("*") 21 | // 是否允许cookie 22 | .allowCredentials(true) 23 | // 设置允许的请求方式 24 | .allowedMethods("GET", "POST", "DELETE", "PUT") 25 | // 设置允许的header属性 26 | .allowedHeaders("*") 27 | // 跨域允许时间 28 | .maxAge(3600); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/config/ExecutorConfig.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.config; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | import org.springframework.scheduling.annotation.EnableAsync; 8 | import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; 9 | 10 | import java.util.concurrent.Executor; 11 | import java.util.concurrent.ThreadPoolExecutor; 12 | 13 | /** 14 | * @author zjw 15 | * @Classname ExecutorConfig 16 | * @Date 2022/4/23 20:39 17 | * @Description 18 | */ 19 | @Configuration 20 | @EnableAsync 21 | public class ExecutorConfig { 22 | 23 | private static final Logger logger = LoggerFactory.getLogger(ExecutorConfig.class); 24 | 25 | private static final int CPU_NUM = Runtime.getRuntime().availableProcessors(); 26 | 27 | private static final int QUEUE_CAPACITY = 99999; 28 | 29 | @Bean 30 | public Executor asyncServiceExecutor() { 31 | logger.info("start asyncServiceExecutor"); 32 | ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); 33 | // 配置核心线程数 34 | executor.setCorePoolSize(CPU_NUM + 1); 35 | // 配置最大线程数 36 | executor.setMaxPoolSize(CPU_NUM + 1); 37 | // 配置队列大小 38 | executor.setQueueCapacity(QUEUE_CAPACITY); 39 | // 配置线程池中的线程的名称前缀 40 | executor.setThreadNamePrefix("async-service-"); 41 | 42 | // rejection-policy:当pool已经达到max size的时候,如何处理新任务 43 | // CALLER_RUNS:不在新线程中执行任务,而是由调用者所在的线程来执行 44 | executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); 45 | // 执行初始化 46 | executor.initialize(); 47 | return executor; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/config/MySqlDataModelConfig.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.config; 2 | 3 | import com.mysql.cj.jdbc.MysqlDataSource; 4 | import org.apache.mahout.cf.taste.impl.model.jdbc.MySQLJDBCDataModel; 5 | import org.apache.mahout.cf.taste.model.DataModel; 6 | import org.apache.mahout.cf.taste.model.JDBCDataModel; 7 | import org.springframework.beans.factory.annotation.Value; 8 | import org.springframework.context.annotation.Bean; 9 | 10 | 11 | /** 12 | * @author zjw 13 | * @Classname MySqlDataModelConfig 14 | * @Date 2022/5/1 20:11 15 | * @Description 16 | */ 17 | 18 | public class MySqlDataModelConfig { 19 | 20 | @Value("${spring.datasource.username}") 21 | private String username; 22 | @Value("${spring.datasource.password}") 23 | private String password; 24 | 25 | @Bean 26 | public DataModel getMySqlDataModel() { 27 | MysqlDataSource dataSource = new MysqlDataSource(); 28 | JDBCDataModel dataModel = null; 29 | 30 | try { 31 | dataSource.setServerName("rm-bp11602preg16q6g4jo.mysql.rds.aliyuncs.com"); 32 | dataSource.setUser(username); 33 | dataSource.setPassword(password); 34 | dataSource.setServerTimezone("GMT%2B8"); 35 | dataSource.setUseSSL(false); 36 | dataSource.setCharacterEncoding("utf-8"); 37 | 38 | System.out.println("hello hello hello 1111111"); 39 | System.out.println(dataSource); 40 | System.out.println(); 41 | 42 | dataModel = new MySQLJDBCDataModel(dataSource, 43 | "comment", 44 | "uid", 45 | "mid", 46 | "score", 47 | "time"); 48 | 49 | System.out.println("hello hello hello 1111111"); 50 | System.out.println(dataModel); 51 | System.out.println(); 52 | } catch (Exception e) { 53 | e.printStackTrace(); 54 | } 55 | 56 | return dataModel; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/config/MybatisPlusConfig.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.config; 2 | 3 | import com.baomidou.mybatisplus.annotation.DbType; 4 | import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; 5 | import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor; 6 | import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; 7 | import org.mybatis.spring.annotation.MapperScan; 8 | import org.springframework.context.annotation.Bean; 9 | import org.springframework.context.annotation.Configuration; 10 | 11 | /** 12 | * @author zjw 13 | * @Classname MybatisPlusConfig 14 | * @Date 2022/4/7 10:59 15 | * @Description 16 | * 17 | * MapperScan 用于扫描指定包下的Mapper接口 18 | */ 19 | @Configuration 20 | @MapperScan("cn.zjw.mrs.mapper") 21 | public class MybatisPlusConfig { 22 | 23 | @Bean 24 | public MybatisPlusInterceptor mybatisPlusInterceptor() { 25 | MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); 26 | // 添加分页插件 27 | interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); 28 | // 添加乐观锁插件 29 | interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); 30 | return interceptor; 31 | } 32 | } -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/config/OssClientConfig.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.config; 2 | 3 | import com.aliyun.oss.OSSClient; 4 | import com.aliyun.oss.OSSClientBuilder; 5 | import org.springframework.beans.factory.annotation.Value; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.context.annotation.Configuration; 8 | 9 | /** 10 | * @author zjw 11 | * @Classname OssClientConfig 12 | * @Date 2022/4/17 13:48 13 | * @Description 14 | */ 15 | @Configuration 16 | public class OssClientConfig { 17 | @Value("${aliyun.oss.endpoint}") 18 | private String endpoint ; 19 | @Value("${aliyun.oss.accessKeyId}") 20 | private String accessKeyId ; 21 | @Value("${aliyun.oss.accessKeySecret}") 22 | private String accessKeySecret; 23 | 24 | @Bean 25 | public OSSClient createOssClient() { 26 | return (OSSClient) new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/config/RedisConfig.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.config; 2 | 3 | import cn.zjw.mrs.utils.FastJsonRedisSerializer; 4 | import com.alibaba.fastjson.parser.ParserConfig; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | import org.springframework.data.redis.connection.RedisConnectionFactory; 8 | import org.springframework.data.redis.core.RedisTemplate; 9 | import org.springframework.data.redis.serializer.StringRedisSerializer; 10 | 11 | /** 12 | * @author zjw 13 | * @Classname RedisConfig 14 | * @Date 2022/5/22 18:26 15 | * @Description 16 | */ 17 | @Configuration 18 | public class RedisConfig { 19 | 20 | /** 21 | * 配置redis,解决redis序列化后乱码问题 22 | */ 23 | @Bean 24 | public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) { 25 | RedisTemplate redisTemplate = new RedisTemplate(); 26 | redisTemplate.setConnectionFactory(redisConnectionFactory); 27 | FastJsonRedisSerializer fastJsonRedisSerializer = new FastJsonRedisSerializer(Object.class); 28 | ParserConfig.getGlobalInstance().setAutoTypeSupport(true); 29 | redisTemplate.setValueSerializer(fastJsonRedisSerializer); 30 | redisTemplate.setHashValueSerializer(fastJsonRedisSerializer); 31 | redisTemplate.setKeySerializer(new StringRedisSerializer()); 32 | redisTemplate.setHashKeySerializer(new StringRedisSerializer()); 33 | redisTemplate.afterPropertiesSet(); 34 | return redisTemplate; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/config/SecurityConfig.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.config; 2 | 3 | import cn.zjw.mrs.filter.JwtAuthenticationTokenFilter; 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.context.annotation.Configuration; 6 | import org.springframework.security.authentication.AuthenticationManager; 7 | import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; 8 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 9 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 10 | import org.springframework.security.config.http.SessionCreationPolicy; 11 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 12 | import org.springframework.security.crypto.password.PasswordEncoder; 13 | import org.springframework.security.web.AuthenticationEntryPoint; 14 | import org.springframework.security.web.access.AccessDeniedHandler; 15 | import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; 16 | 17 | import javax.annotation.Resource; 18 | 19 | /** 20 | * @author zjw 21 | * @Classname SecurityConfig 22 | * @Date 2022/4/11 16:34 23 | * @Description 24 | */ 25 | @Configuration 26 | @EnableGlobalMethodSecurity(prePostEnabled = true) 27 | public class SecurityConfig extends WebSecurityConfigurerAdapter { 28 | 29 | /** 30 | * 创建BCryptPasswordEncoder注入容器 31 | * 配置完后,系统默认使用BCryptPasswordEncoder 32 | * @return 33 | */ 34 | @Bean 35 | public PasswordEncoder passwordEncoder() { 36 | return new BCryptPasswordEncoder(); 37 | } 38 | 39 | @Resource 40 | private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter; 41 | 42 | @Resource 43 | private AuthenticationEntryPoint authenticationEntryPoint; 44 | 45 | @Resource 46 | private AccessDeniedHandler accessDeniedHandler; 47 | 48 | @Override 49 | protected void configure(HttpSecurity http) throws Exception { 50 | http 51 | //关闭csrf 52 | .csrf().disable() 53 | //不通过Session获取SecurityContext 54 | .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) 55 | .and() 56 | .authorizeRequests() 57 | // 不需要登录可以直接访问 58 | .antMatchers("/movie", 59 | "/movie/info", 60 | "/movie/recommend", 61 | "/user/register", 62 | "/movie/match/name", 63 | "/movie/most/watched", 64 | "/movie/highest/score", 65 | "/user/check/username", 66 | "/check/auth/code", 67 | "/check/username/exists", 68 | "/mail/auth/code/under/logout", 69 | "/find/password").permitAll() 70 | // 对于登录接口 允许匿名访问 71 | .antMatchers("/user/login").anonymous() 72 | // 除上面外的所有请求全部需要鉴权认证 73 | .anyRequest().authenticated(); 74 | 75 | // 添加过滤器,把token校验过滤器添加到过滤器链中 76 | http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class); 77 | 78 | // 配置异常处理器 79 | http.exceptionHandling() 80 | // 配置认证失败处理器 81 | .authenticationEntryPoint(authenticationEntryPoint) 82 | // 配置授权失败处理器 83 | .accessDeniedHandler(accessDeniedHandler); 84 | 85 | //允许跨域 86 | http.cors(); 87 | } 88 | 89 | @Bean 90 | @Override 91 | public AuthenticationManager authenticationManagerBean() throws Exception { 92 | return super.authenticationManagerBean(); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/controller/AuthController.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.controller; 2 | 3 | import cn.hutool.captcha.generator.RandomGenerator; 4 | import cn.zjw.mrs.entity.LoginUser; 5 | import cn.zjw.mrs.entity.Result; 6 | import cn.zjw.mrs.entity.User; 7 | import cn.zjw.mrs.service.AuthService; 8 | import cn.zjw.mrs.service.UserService; 9 | import cn.zjw.mrs.utils.RedisCache; 10 | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; 11 | import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; 12 | import lombok.extern.slf4j.Slf4j; 13 | import org.springframework.security.core.Authentication; 14 | import org.springframework.web.bind.annotation.*; 15 | 16 | import javax.annotation.Resource; 17 | import java.util.HashMap; 18 | import java.util.Map; 19 | import java.util.Objects; 20 | import java.util.Random; 21 | 22 | /** 23 | * @author zjw 24 | * @Classname AuthController 25 | * @Date 2022/5/14 15:12 26 | * @Description 27 | */ 28 | 29 | @RestController 30 | @Slf4j 31 | public class AuthController { 32 | 33 | @Resource 34 | private AuthService authService; 35 | 36 | @Resource 37 | private RedisCache redisCache; 38 | 39 | @Resource 40 | private UserService userService; 41 | 42 | /** 43 | * 请求发送邮箱验证码 44 | * @param data 目标邮箱 45 | * @param authentication 用户身份信息 46 | * @return 发送成功与否 47 | */ 48 | @PostMapping("/mail/auth/code") 49 | public Result sendMailAuthCode(@RequestBody Map data, Authentication authentication) { 50 | String mail = data.get("mail"); 51 | 52 | LoginUser loginUser = (LoginUser) authentication.getPrincipal(); 53 | String username = loginUser.getUsername(); 54 | 55 | authService.sendMailAuthCode(username, mail); 56 | return Result.success("验证码已发送"); 57 | } 58 | 59 | /** 60 | * 在未登录状态下发送邮箱验证码 61 | * @param data 请求数据,包括用户名和邮箱号码 62 | * @return 发送成功与否 63 | */ 64 | @PostMapping("/mail/auth/code/under/logout") 65 | public Result sendMailAuthCodeUnderLogout(@RequestBody Map data) { 66 | String username = data.get("username"); 67 | String mail = data.get("mail"); 68 | 69 | boolean isMailBelongToUser = authService.judgeMailBelongToUser(username, mail); 70 | if (!isMailBelongToUser) { 71 | return Result.error("输入邮箱有误(输入的账号和邮箱不对应)"); 72 | } 73 | 74 | authService.sendMailAuthCode(username, mail); 75 | return Result.success("验证码已发送"); 76 | } 77 | 78 | /** 79 | * 判断验证码是否有误 80 | * @param data post传递的数据,包括用户名和验证码 81 | * @return 正误信息 82 | */ 83 | @PostMapping("/check/auth/code") 84 | public Result judgeAuthCode(@RequestBody Map data) { 85 | String username = data.get("username"); 86 | String authCode = data.get("authCode"); 87 | 88 | int judge = authService.judgeAuthCode(username, authCode); 89 | if (judge == -1) { 90 | return Result.error("验证码已过期"); 91 | } else if (judge == 1) { 92 | return Result.success("输入的验证码正确"); 93 | } else { 94 | return Result.error("输入的验证码有误"); 95 | } 96 | } 97 | 98 | /** 99 | * 判断用户是否存在于数据库中 100 | * @param username 带判断的用户名 101 | * @return 检查结果 102 | */ 103 | @GetMapping("/check/username/exists") 104 | public Result judgeUsernameExists(@RequestParam String username) { 105 | boolean isUsernameExists = authService.judgeUsernameExists(username); 106 | if (!isUsernameExists) { 107 | return Result.error("账号不存在"); 108 | } else { 109 | return Result.success("账号验证通过"); 110 | } 111 | } 112 | 113 | /** 114 | * 再次校验验证码,防止技术人员直接通过请求找回密码 115 | * 校验成功后,修改原密码,并返回修改后的密码 116 | * @param data 请求数据,包含用户名和验证码 117 | * @return 错误信息或正确密码 118 | */ 119 | @PostMapping("/find/password") 120 | public Result judgeAndFindPassword(@RequestBody Map data) { 121 | String username = data.get("username"); 122 | String authCode = data.get("authCode"); 123 | // 1. 再次校验验证码,防止技术人员直接通过请求找回密码 124 | int judge = authService.judgeAuthCode(username, authCode); 125 | if (judge == -1) { 126 | return Result.error("验证码已过期"); 127 | } else if (judge == 1) { 128 | // 2. 校验成功后,生成一个随机新密码,替换原密码 129 | RandomGenerator randomGenerator = new RandomGenerator( 130 | "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", 8); 131 | String newPassword = randomGenerator.generate(); 132 | log.info("新密码" + newPassword); 133 | userService.updatePassword(username, "", newPassword, true); 134 | // 3. 将新密码最为结果返回 135 | Map res = new HashMap<>(1); 136 | res.put("password", newPassword); 137 | return Result.success(res); 138 | } else { 139 | return Result.error("输入的验证码有误"); 140 | } 141 | } 142 | 143 | /** 144 | * 判断验证码 并 修改用于邮箱 145 | * @param data 数据,包括验证码和邮箱号码 146 | * @param authentication 用户身份信息 147 | * @return 修改结果 148 | */ 149 | @PutMapping("/user/update/mail") 150 | public Result judgeAuthCodeAndUpdateUserMail(@RequestBody Map data, 151 | Authentication authentication) { 152 | String authCode = data.get("authCode"); 153 | String mail = data.get("mail"); 154 | 155 | LoginUser loginUser = (LoginUser) authentication.getPrincipal(); 156 | String username = loginUser.getUser().getUsername(); 157 | 158 | int judge = authService.judgeAuthCode(username, authCode); 159 | // 如果redis中的验证码为空,则说明验证码失效了 160 | if (judge == -1) { 161 | return Result.error("验证码已失效"); 162 | } 163 | // 判断验证码是否与用户输入验证码是否一致 164 | if (judge == 1) { 165 | User user = userService.getOne(new LambdaQueryWrapper().eq(User::getMail, mail)); 166 | // 判断邮箱是否被其他用户注册过 167 | if (!Objects.isNull(user)) { 168 | return Result.error("该邮箱已被注册"); 169 | } 170 | 171 | // 更新完成后,将redis中的验证码删除 172 | redisCache.deleteObject("authCode:" + username); 173 | userService.update(new LambdaUpdateWrapper().set(User::getMail, mail).eq(User::getUsername, username)); 174 | return Result.success("邮箱修改成功"); 175 | } else { 176 | return Result.error("输入的验证码有误"); 177 | } 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/controller/CommentController.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.controller; 2 | 3 | import cn.zjw.mrs.entity.Comment; 4 | import cn.zjw.mrs.entity.LoginUser; 5 | import cn.zjw.mrs.entity.Result; 6 | import cn.zjw.mrs.service.CommentService; 7 | import cn.zjw.mrs.service.RecommendationService; 8 | import cn.zjw.mrs.service.UserLikeRedisService; 9 | import cn.zjw.mrs.service.UserLikeService; 10 | import cn.zjw.mrs.vo.comment.CommentMovieVo; 11 | import cn.zjw.mrs.vo.comment.CommentStripVo; 12 | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; 13 | import lombok.extern.slf4j.Slf4j; 14 | import org.springframework.security.core.Authentication; 15 | import org.springframework.web.bind.annotation.*; 16 | 17 | import javax.annotation.Resource; 18 | import java.security.Principal; 19 | import java.util.*; 20 | 21 | /** 22 | * @author zjw 23 | * @Classname CommentController 24 | * @Date 2022/4/14 19:20 25 | * @Description 26 | */ 27 | @RestController 28 | @RequestMapping("/comment") 29 | @Slf4j 30 | public class CommentController { 31 | @Resource 32 | private CommentService commentService; 33 | 34 | @Resource 35 | private RecommendationService recommendationService; 36 | 37 | @Resource 38 | private UserLikeService userLikeService; 39 | 40 | @Resource 41 | private UserLikeRedisService userLikeRedisService; 42 | 43 | @PostMapping 44 | private Result addComment(@RequestBody Comment comment, Principal principal, Authentication authentication) { 45 | LoginUser loginUser = (LoginUser) authentication.getPrincipal(); 46 | Long uid = loginUser.getUser().getId(); 47 | 48 | int update = commentService.addComment(comment, principal.getName()); 49 | switch (update) { 50 | case -1: return Result.error("评价更新失败,请稍后重试(┬┬﹏┬┬)"); 51 | case 1: { 52 | recommendationService.updateRecommendation(uid); 53 | return Result.success("评价更新成功(‾◡◝)"); 54 | } 55 | case -2: return Result.error("评价失败(┬┬﹏┬┬)"); 56 | default: { 57 | recommendationService.updateRecommendation(uid); 58 | return Result.success("评价成功(‾◡◝)"); 59 | } 60 | } 61 | } 62 | 63 | @GetMapping("/own") 64 | private Result getOwnComment(@RequestParam Long mid, Authentication authentication) { 65 | LoginUser loginUser = (LoginUser) authentication.getPrincipal(); 66 | Long uid = loginUser.getUser().getId(); 67 | CommentStripVo ownComment = commentService.getOwnComment(uid, mid); 68 | if (Objects.isNull(ownComment)) { 69 | return Result.error("该用户还未评论"); 70 | } 71 | return Result.success(ownComment); 72 | } 73 | 74 | @GetMapping("/more") 75 | private Result getMoreCommentsByMovieId(@RequestParam Long mid, 76 | @RequestParam(defaultValue = "0") int currentPage, 77 | @RequestParam(defaultValue = "10") int pageSize, 78 | Authentication authentication) { 79 | // 限制请求数量,防止懂技术的人,通过接口一次性获取到所有的记录 80 | pageSize = Math.min(20, pageSize); 81 | 82 | List comments = commentService.getMoreCommentsByMovieId(mid, currentPage, pageSize); 83 | if (Objects.isNull(comments)) { 84 | return Result.error("该电影下暂无评论"); 85 | } 86 | 87 | LoginUser loginUser = (LoginUser) authentication.getPrincipal(); 88 | long uid = loginUser.getUser().getId(); 89 | for (CommentStripVo comment: comments) { 90 | // 获取点赞状态 91 | int status = userLikeService.getUserLikeStatus(comment.getId(), uid); 92 | comment.setStatus(status); 93 | } 94 | return Result.success(comments); 95 | } 96 | 97 | @GetMapping("/movie/moments") 98 | private Result getMoreOwnCommentMovieMoments( 99 | @RequestParam(defaultValue = "1") Integer currentPage, 100 | @RequestParam(defaultValue = "10") Integer pageSize, 101 | Authentication authentication) { 102 | // 限制请求数量,防止懂技术的人,通过接口一次性获取到所有的记录 103 | pageSize = Math.min(20, pageSize); 104 | 105 | LoginUser loginUser = (LoginUser) authentication.getPrincipal(); 106 | Long uid = loginUser.getUser().getId(); 107 | 108 | Map page = commentService.getMoreOwnCommentMovieMoments(uid, currentPage, pageSize); 109 | 110 | return Result.success(page); 111 | } 112 | 113 | @DeleteMapping("/remove") 114 | private Result removeOwnComment(@RequestBody String mid, Authentication authentication) { 115 | LoginUser loginUser = (LoginUser) authentication.getPrincipal(); 116 | Long uid = loginUser.getUser().getId(); 117 | int delete = commentService.removeOwnComment(uid, Long.valueOf(mid)); 118 | if (delete == 0) { 119 | return Result.error("短评删除失败(┬┬﹏┬┬)"); 120 | } 121 | 122 | recommendationService.updateRecommendation(uid); 123 | return Result.success("短评删除成功(‾◡◝)"); 124 | } 125 | 126 | @GetMapping("/word/cloud/data") 127 | private Result getCommentsWordCloudData(@RequestParam long mid) { 128 | List> res = commentService.getCommentsWordCloudData(mid); 129 | return Result.success(res); 130 | } 131 | 132 | @PutMapping("/like") 133 | private Result likeComment(@RequestBody Map data, Authentication authentication) { 134 | long cid = Long.parseLong(data.get("cid")); 135 | long status = Long.parseLong(data.get("status")); 136 | LoginUser loginUser = (LoginUser) authentication.getPrincipal(); 137 | Long uid = loginUser.getUser().getId(); 138 | 139 | if (status == 1) { 140 | userLikeRedisService.likeComment(cid, uid); 141 | userLikeRedisService.increaseLikedCount(cid); 142 | } else { 143 | userLikeRedisService.unlikeComment(cid, uid); 144 | userLikeRedisService.decreaseLikedCount(cid); 145 | } 146 | return Result.success(); 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/controller/MovieController.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.controller; 2 | 3 | import cn.zjw.mrs.entity.LoginUser; 4 | import cn.zjw.mrs.entity.Movie; 5 | import cn.zjw.mrs.entity.Result; 6 | import cn.zjw.mrs.service.MovieService; 7 | import cn.zjw.mrs.utils.PicUrlUtil; 8 | import cn.zjw.mrs.vo.movie.MovieCardVo; 9 | import cn.zjw.mrs.vo.movie.ReviewedMovieStripVo; 10 | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; 11 | import org.nd4j.linalg.api.ops.impl.reduce.same.Max; 12 | import org.springframework.security.core.Authentication; 13 | import org.springframework.web.bind.annotation.GetMapping; 14 | import org.springframework.web.bind.annotation.RequestMapping; 15 | import org.springframework.web.bind.annotation.RequestParam; 16 | import org.springframework.web.bind.annotation.RestController; 17 | 18 | import javax.annotation.Resource; 19 | import java.util.HashMap; 20 | import java.util.List; 21 | import java.util.Map; 22 | 23 | /** 24 | * @author zjw 25 | * @Classname MovieController 26 | * @Date 2022/4/7 12:58 27 | * @Description 28 | */ 29 | @RestController 30 | @RequestMapping("/movie") 31 | public class MovieController { 32 | @Resource 33 | private MovieService movieService; 34 | 35 | /** 36 | * 获取一整页电影信息列表 37 | * @param currentPage 当前页码 38 | * @param pageSize 每页电影数量 39 | * @param type 电影类型 40 | * @param region 电影地区 41 | * @param search 搜索关键字 42 | * @return 电影列表 43 | */ 44 | @GetMapping 45 | public Result getPagesByTypeAndRegion(@RequestParam(defaultValue = "1") Integer currentPage, 46 | @RequestParam(defaultValue = "12") Integer pageSize, 47 | @RequestParam(defaultValue = "全部") String type, 48 | @RequestParam(defaultValue = "全部") String region, 49 | @RequestParam(defaultValue = "") String search) { 50 | // 限制请求数量,防止懂技术的人,通过接口一次性获取到所有的记录 51 | pageSize = Math.min(20, pageSize); 52 | Page page = movieService.getPageMovies(currentPage, pageSize, type, region, search); 53 | return Result.success(page); 54 | } 55 | 56 | /** 57 | * 通过电影id获取这部电影的详细信息 58 | * @param id 电影id 59 | * @return 电影详情 60 | */ 61 | @GetMapping("/info") 62 | public Result getMovieInfo(@RequestParam Integer id) { 63 | Movie movie = movieService.getById(id); 64 | movie.setPic(PicUrlUtil.getFullMoviePicUrl(movie.getPic())); 65 | return Result.success(movie); 66 | } 67 | 68 | /** 69 | * 通过电影id获取“喜欢这部电影的人也喜欢...”的推荐电影 70 | * @param id 待查询的电影id 71 | * @return 推荐电影列表 72 | */ 73 | @GetMapping( "/recommend") 74 | public Result getRecommendedMovies(@RequestParam Long id) { 75 | Long did = movieService.getById(id).getDid(); 76 | List movies = movieService.getRecommendedMoviesByMovieId(did); 77 | Map> res = new HashMap<>(1); 78 | res.put("movies", movies); 79 | return Result.success(res); 80 | } 81 | 82 | /** 83 | * 获取用户id为uid的用户评价过的所有电影的基本信息 84 | * @return 评价过的电影条目 85 | */ 86 | @GetMapping("/reviewed") 87 | public Result getAllReviewedMovies( 88 | @RequestParam(defaultValue = "1") Integer currentPage, 89 | @RequestParam(defaultValue = "10") Integer pageSize, 90 | Authentication authentication) { 91 | // 限制请求数量,防止懂技术的人,通过接口一次性获取到所有的记录 92 | pageSize = Math.min(20, pageSize); 93 | 94 | LoginUser loginUser = (LoginUser) authentication.getPrincipal(); 95 | Long uid = loginUser.getUser().getId(); 96 | Map page = movieService.getMoreReviewedMoviesByUserId(uid, currentPage, pageSize); 97 | return Result.success(page); 98 | } 99 | 100 | /** 101 | * 获取搜索关键字模糊匹配的电影名称列表 102 | * @param keywords 搜索关键字 103 | * @return 模糊匹配查询到的电影名称列表 104 | */ 105 | @GetMapping("/match/name") 106 | public Result getMatchMovieName(@RequestParam(defaultValue = "我打赌你什么都查不到") String keywords) { 107 | List names = movieService.getMatchMovieName(keywords); 108 | return Result.success(names); 109 | } 110 | 111 | /** 112 | * 获取最多人看过的(评论过的)电影列表 113 | * @return 最多人看过的(评论过的)电影列表 114 | */ 115 | @GetMapping("/most/watched") 116 | public Result getMostWatchedMovies() { 117 | List movies = movieService.getMostWatchedMovies(); 118 | return Result.success(movies); 119 | } 120 | 121 | /** 122 | * 获得评分最高的前n部电影列表 123 | * @return 评分最高的前n部电影列表 124 | */ 125 | @GetMapping("/highest/score") 126 | public Result getHighestRatedMovies() { 127 | List movies = movieService.getHighestRatedMovies(); 128 | return Result.success(movies); 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/controller/RecommendationController.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.controller; 2 | 3 | import cn.zjw.mrs.entity.LoginUser; 4 | import cn.zjw.mrs.entity.Result; 5 | import cn.zjw.mrs.service.RecommendationService; 6 | import cn.zjw.mrs.vo.movie.RecommendedMovieVo; 7 | import org.springframework.security.core.Authentication; 8 | import org.springframework.web.bind.annotation.GetMapping; 9 | import org.springframework.web.bind.annotation.RequestMapping; 10 | import org.springframework.web.bind.annotation.RestController; 11 | 12 | import javax.annotation.Resource; 13 | import java.util.List; 14 | import java.util.Map; 15 | 16 | /** 17 | * @author zjw 18 | * @Classname RecommendationController 19 | * @Date 2022/4/24 0:18 20 | * @Description 21 | */ 22 | @RestController 23 | @RequestMapping("/recommendation") 24 | public class RecommendationController { 25 | 26 | @Resource 27 | private RecommendationService recommendationService; 28 | 29 | @GetMapping 30 | public Result getRecommendedMovies(Authentication authentication) { 31 | LoginUser loginUser = (LoginUser) authentication.getPrincipal(); 32 | Long uid = loginUser.getUser().getId(); 33 | List recommendedMovies = recommendationService.getRecommendedMoviesByUserId(uid); 34 | 35 | return Result.success(recommendedMovies); 36 | } 37 | 38 | /** 39 | * 获取看过的电影和推荐电影之间的联系,用于绘制Echarts关系图 40 | * @return 看过的电影和推荐电影之间的联系 41 | */ 42 | @GetMapping("/relations") 43 | public Result getLinksBetweenWatchedMoviesAndRecommendedMovies(Authentication authentication) { 44 | LoginUser loginUser = (LoginUser) authentication.getPrincipal(); 45 | Long uid = loginUser.getUser().getId(); 46 | Map> res = recommendationService.getLinksBetweenWatchedMoviesAndRecommendedMovies(uid); 47 | return Result.success(res); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/controller/UserController.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.controller; 2 | 3 | import cn.zjw.mrs.entity.LoginUser; 4 | import cn.zjw.mrs.entity.Result; 5 | import cn.zjw.mrs.entity.User; 6 | import cn.zjw.mrs.service.OssService; 7 | import cn.zjw.mrs.service.UserService; 8 | import cn.zjw.mrs.utils.Base64DecodedMultipartFile; 9 | import cn.zjw.mrs.vo.user.UserInfoVo; 10 | import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; 11 | import org.apache.logging.log4j.util.Strings; 12 | import org.springframework.security.core.Authentication; 13 | import org.springframework.web.bind.annotation.*; 14 | import org.springframework.web.multipart.MultipartFile; 15 | 16 | import javax.annotation.Resource; 17 | import java.security.Principal; 18 | import java.util.Map; 19 | 20 | /** 21 | * @author zjw 22 | * @Classname UserController 23 | * @Date 2022/4/10 22:47 24 | * @Description 25 | */ 26 | 27 | @RestController 28 | @RequestMapping("/user") 29 | public class UserController { 30 | 31 | @Resource 32 | UserService userService; 33 | 34 | @Resource 35 | OssService ossService; 36 | 37 | @PostMapping("/login") 38 | public Result login(@RequestBody User user) { 39 | return userService.login(user); 40 | } 41 | 42 | @PostMapping("/logout") 43 | public Result logout() { 44 | return userService.logout(); 45 | } 46 | 47 | @PostMapping("/register") 48 | public Result register(@RequestBody User user) { 49 | return userService.register(user); 50 | } 51 | 52 | @PostMapping("/update/password") 53 | public Result updatePassword(@RequestBody Map password, Principal principal) { 54 | int update = userService.updatePassword(principal.getName(), 55 | password.get("prePassword"), 56 | password.get("newPassword"), 57 | false); 58 | if (update == -1) { 59 | return Result.error("输入的原密码不正确(┬┬﹏┬┬)"); 60 | } else if (update == 0) { 61 | return Result.error("密码修改失败(┬┬﹏┬┬)"); 62 | } 63 | return Result.success("密码修改成功(‾◡◝)"); 64 | } 65 | 66 | @PostMapping("/judge") 67 | public Result isLogin() { 68 | return Result.success(); 69 | } 70 | 71 | @GetMapping("/info") 72 | public Result getUserInfo(Authentication authentication) { 73 | LoginUser loginUser = (LoginUser) authentication.getPrincipal(); 74 | UserInfoVo userInfo = userService.getUserInfo(loginUser.getUser().getId()); 75 | return Result.success(userInfo); 76 | } 77 | 78 | @PutMapping("/update/nickname") 79 | public Result updateUserNickname(@RequestBody String nickname) { 80 | if (userService.updateNickname(nickname) == 0) { 81 | return Result.error("昵称更新失败(┬┬﹏┬┬)"); 82 | } 83 | return Result.success("昵称更新成功(‾◡◝)"); 84 | } 85 | 86 | @PutMapping("/update/sex") 87 | public Result updateUserSex(@RequestBody String sex, Principal principal) { 88 | if (userService.updateSex(sex, principal.getName()) == 0) { 89 | return Result.error("性别更新失败(┬┬﹏┬┬)"); 90 | } 91 | return Result.success("性别更新成功(‾◡◝)", null); 92 | } 93 | 94 | @PostMapping("/update/avatar") 95 | public Result updateUserAvatar(@RequestBody String avatar, Principal principal) { 96 | if (Strings.isBlank(avatar)) { 97 | return Result.error("头像更新失败(┬┬﹏┬┬)"); 98 | } 99 | // base64转MultipartFile文件 100 | MultipartFile avatarFile = Base64DecodedMultipartFile.base64ToMultipart(avatar); 101 | boolean isSuccess = ossService.updateAvatar(principal.getName(), avatarFile); 102 | if (!isSuccess) { 103 | return Result.error("头像上传失败(┬┬﹏┬┬)"); 104 | } 105 | return Result.success("头像修改成功(‾◡◝)", null); 106 | } 107 | 108 | @GetMapping("/get/mail") 109 | public Result getUserMail(Principal principal) { 110 | String username = principal.getName(); 111 | User user = userService.getOne(new LambdaUpdateWrapper().eq(User::getUsername, username)); 112 | return Result.success("成功", user.getMail()); 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/controller/UserPreferenceController.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.controller; 2 | 3 | import cn.zjw.mrs.entity.LoginUser; 4 | import cn.zjw.mrs.entity.Result; 5 | import cn.zjw.mrs.service.*; 6 | import org.springframework.security.core.Authentication; 7 | import org.springframework.web.bind.annotation.*; 8 | 9 | import javax.annotation.Resource; 10 | import java.util.List; 11 | import java.util.Map; 12 | 13 | /** 14 | * @author zjw 15 | * @Classname UserPreferenceController 16 | * @Date 2022/4/20 16:14 17 | * @Description 18 | */ 19 | @RestController 20 | @RequestMapping("/user/like") 21 | public class UserPreferenceController { 22 | @Resource 23 | private RegionLikeService regionLikeService; 24 | 25 | @Resource 26 | private TypeLikeService typeLikeService; 27 | 28 | @Resource 29 | private UserService userService; 30 | 31 | @Resource 32 | private RecommendationService recommendationService; 33 | 34 | @GetMapping 35 | public Result getTypesAndRegions(Authentication authentication) { 36 | LoginUser loginUser = (LoginUser) authentication.getPrincipal(); 37 | Map> typesAndRegions = userService.getTypesAndRegions(loginUser.getUser().getId()); 38 | return Result.success(typesAndRegions); 39 | } 40 | 41 | @PostMapping("/update/types") 42 | public Result updateUserTypeLike(@RequestBody int[] types, Authentication authentication) { 43 | LoginUser loginUser = (LoginUser) authentication.getPrincipal(); 44 | Long id = loginUser.getUser().getId(); 45 | typeLikeService.updateUserTypeLike(id, types); 46 | recommendationService.updateRecommendation(id); 47 | return Result.success("电影类型喜好更新成功(‾◡◝)"); 48 | } 49 | 50 | @PostMapping("/update/regions") 51 | public Result updateUserRegionLike(@RequestBody int[] regions, Authentication authentication) { 52 | LoginUser loginUser = (LoginUser) authentication.getPrincipal(); 53 | Long id = loginUser.getUser().getId(); 54 | regionLikeService.updateUserRegionLike(id, regions); 55 | recommendationService.updateRecommendation(id); 56 | return Result.success("电影地区喜好更新成功(‾◡◝)"); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/entity/Comment.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableField; 5 | import com.baomidou.mybatisplus.annotation.TableId; 6 | import com.baomidou.mybatisplus.annotation.TableName; 7 | import java.io.Serializable; 8 | import java.sql.Timestamp; 9 | import java.util.Date; 10 | 11 | import com.fasterxml.jackson.annotation.JsonFormat; 12 | import lombok.Data; 13 | 14 | /** 15 | * @author zjw 16 | * @TableName comment 17 | */ 18 | @TableName(value ="comment") 19 | @Data 20 | public class Comment implements Serializable { 21 | /** 22 | * 评论id 23 | */ 24 | @TableId(type = IdType.AUTO) 25 | private Long id; 26 | 27 | /** 28 | * 用户ID 29 | */ 30 | private Long uid; 31 | 32 | /** 33 | * 电影ID 34 | */ 35 | private Long mid; 36 | 37 | /** 38 | * 短评 39 | */ 40 | private String comment; 41 | 42 | /** 43 | * 评分 44 | */ 45 | private Integer score; 46 | 47 | /** 48 | * 评价时间 49 | * 注解用于转化时间戳 50 | */ 51 | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") 52 | private Timestamp time; 53 | 54 | /** 55 | * 赞同数 56 | */ 57 | private Integer agree; 58 | 59 | /** 60 | * 0表示系统 1表示豆瓣 61 | */ 62 | private Integer type; 63 | 64 | /** 65 | * 用户名 66 | */ 67 | private String nickname; 68 | 69 | @TableField(exist = false) 70 | private static final long serialVersionUID = 1L; 71 | } -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/entity/LoginUser.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.entity; 2 | 3 | import com.alibaba.fastjson.annotation.JSONField; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | import org.springframework.security.core.GrantedAuthority; 8 | import org.springframework.security.core.authority.SimpleGrantedAuthority; 9 | import org.springframework.security.core.userdetails.UserDetails; 10 | 11 | import java.util.Collection; 12 | import java.util.List; 13 | import java.util.stream.Collectors; 14 | 15 | /** 16 | * @author zjw 17 | * @Classname LoginUser 18 | * @Date 2022/4/11 16:24 19 | * @Description 20 | */ 21 | @Data 22 | @NoArgsConstructor 23 | public class LoginUser implements UserDetails { 24 | 25 | private User user; 26 | 27 | private List permissions; 28 | 29 | public LoginUser(User user, List permissions) { 30 | this.user = user; 31 | this.permissions = permissions; 32 | } 33 | 34 | @JSONField(serialize = false) 35 | private List authorities; 36 | 37 | @Override 38 | public Collection getAuthorities() { 39 | if(authorities!=null){ 40 | return authorities; 41 | } 42 | // 把permissions中String类型的权限信息封装成SimpleGrantedAuthority对象 43 | authorities = permissions.stream() 44 | .map(SimpleGrantedAuthority::new) 45 | .collect(Collectors.toList()); 46 | return authorities; 47 | } 48 | 49 | @Override 50 | public String getPassword() { 51 | return user.getPassword(); 52 | } 53 | 54 | @Override 55 | public String getUsername() { 56 | return user.getUsername(); 57 | } 58 | 59 | @Override 60 | public boolean isAccountNonExpired() { 61 | return true; 62 | } 63 | 64 | @Override 65 | public boolean isAccountNonLocked() { 66 | return true; 67 | } 68 | 69 | @Override 70 | public boolean isCredentialsNonExpired() { 71 | return true; 72 | } 73 | 74 | @Override 75 | public boolean isEnabled() { 76 | return true; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/entity/MovieFeature.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableField; 5 | import com.baomidou.mybatisplus.annotation.TableId; 6 | import com.baomidou.mybatisplus.annotation.TableName; 7 | import java.io.Serializable; 8 | import lombok.Data; 9 | 10 | /** 11 | * @author zjw 12 | * @TableName movie_feature 13 | */ 14 | @TableName(value ="movie_feature") 15 | @Data 16 | public class MovieFeature implements Serializable { 17 | /** 18 | * 电影id 19 | */ 20 | @TableId 21 | private Long mid; 22 | 23 | /** 24 | * 电影特征矩阵 25 | */ 26 | private String matrix; 27 | 28 | @TableField(exist = false) 29 | private static final long serialVersionUID = 1L; 30 | } -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/entity/MovieRegion.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableField; 5 | import com.baomidou.mybatisplus.annotation.TableId; 6 | import com.baomidou.mybatisplus.annotation.TableName; 7 | import java.io.Serializable; 8 | import lombok.Data; 9 | 10 | /** 11 | * @author zjw 12 | * @TableName movie_region 13 | */ 14 | @TableName(value ="movie_region") 15 | @Data 16 | public class MovieRegion implements Serializable { 17 | /** 18 | * 电影ID 19 | */ 20 | private Long mid; 21 | 22 | /** 23 | * 地区ID 24 | */ 25 | private Integer rid; 26 | 27 | /** 28 | * 程度 29 | */ 30 | private Integer degree; 31 | 32 | @TableField(exist = false) 33 | private static final long serialVersionUID = 1L; 34 | } -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/entity/MovieType.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableField; 5 | import com.baomidou.mybatisplus.annotation.TableId; 6 | import com.baomidou.mybatisplus.annotation.TableName; 7 | import java.io.Serializable; 8 | import lombok.Data; 9 | 10 | /** 11 | * @author zjw 12 | * @TableName movie_type 13 | */ 14 | @TableName(value ="movie_type") 15 | @Data 16 | public class MovieType implements Serializable { 17 | /** 18 | * 电影ID 19 | */ 20 | private Long mid; 21 | 22 | /** 23 | * 类型ID 24 | */ 25 | private Integer tid; 26 | 27 | /** 28 | * 程度 29 | */ 30 | private Integer degree; 31 | 32 | @TableField(exist = false) 33 | private static final long serialVersionUID = 1L; 34 | } -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/entity/Preference.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.entity; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * @author zjw 7 | * @Classname Preference 8 | * @Date 2022/5/2 11:11 9 | * @Description 10 | */ 11 | @Data 12 | public class Preference { 13 | 14 | private Long uid; 15 | 16 | private Long mid; 17 | 18 | private Integer score; 19 | 20 | private Integer cnt; 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/entity/Recommendation.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.TableField; 4 | import com.baomidou.mybatisplus.annotation.TableName; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | 9 | import java.io.Serializable; 10 | 11 | /** 12 | * @author zjw 13 | * @TableName user_recommendation 14 | */ 15 | @TableName(value ="recommendation") 16 | @Data 17 | @AllArgsConstructor 18 | @NoArgsConstructor 19 | public class Recommendation implements Serializable { 20 | /** 21 | * 用户id 22 | */ 23 | private Long uid; 24 | 25 | /** 26 | * 电影id 27 | */ 28 | private Long mid; 29 | 30 | /** 31 | * 推荐指数 32 | */ 33 | private Double idx; 34 | 35 | /** 36 | * 推荐类型(1表示基于内容,2表示协同过滤,0表示随机推荐) 37 | */ 38 | private Integer type; 39 | 40 | @TableField(exist = false) 41 | private static final long serialVersionUID = 1L; 42 | } -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/entity/RegionLike.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.TableField; 4 | import com.baomidou.mybatisplus.annotation.TableName; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | 9 | import java.io.Serializable; 10 | 11 | /** 12 | * @author zjw 13 | * @TableName region_like 14 | */ 15 | @TableName(value ="region_like") 16 | @Data 17 | @AllArgsConstructor 18 | @NoArgsConstructor 19 | public class RegionLike implements Serializable { 20 | /** 21 | * 用户id 22 | */ 23 | private Long uid; 24 | 25 | /** 26 | * 电影地区id 27 | */ 28 | private Integer rid; 29 | 30 | /** 31 | * 喜爱程度 32 | */ 33 | private Integer degree; 34 | 35 | @TableField(exist = false) 36 | private static final long serialVersionUID = 1L; 37 | } -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/entity/Result.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.entity; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | /** 8 | * @author zjw 9 | * @Classname Result 10 | * @Date 2022/4/7 16:18 11 | * @Description 12 | */ 13 | @Data 14 | @NoArgsConstructor 15 | @AllArgsConstructor 16 | public class Result { 17 | /** 18 | * 状态码 19 | */ 20 | private Integer code; 21 | /** 22 | * 提示信息,如果有错误时,前端可以获取该字段进行提示 23 | */ 24 | private String msg; 25 | /** 26 | * 查询到的结果数据 27 | */ 28 | private Object data; 29 | 30 | public Result(Integer code, String msg) { 31 | this.code = code; 32 | this.msg = msg; 33 | } 34 | 35 | public static Result success() { 36 | return new Result<>(200, "成功", null); 37 | } 38 | 39 | public static Result success(T data) { 40 | return new Result<>(200, "成功", data); 41 | } 42 | 43 | public static Result success(String msg) { 44 | return new Result<>(200, msg, null); 45 | } 46 | 47 | public static Result success(String msg, T data) { 48 | return new Result<>(200, msg, data); 49 | } 50 | 51 | public static Result error() { 52 | return new Result<>(500, "失败", null); 53 | } 54 | 55 | public static Result error(String msg) { 56 | return new Result<>(500, msg, null); 57 | } 58 | 59 | public static Result error(String msg, T data) { 60 | return new Result<>(500, msg, data); 61 | } 62 | 63 | public static Result error(int code, String msg) { 64 | return new Result<>(code, msg); 65 | } 66 | } -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/entity/SameLikes.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.TableField; 4 | import com.baomidou.mybatisplus.annotation.TableName; 5 | import lombok.Data; 6 | 7 | import java.io.Serializable; 8 | 9 | /** 10 | * @author zjw 11 | * @TableName same_likes 12 | */ 13 | @TableName(value ="same_likes") 14 | @Data 15 | public class SameLikes implements Serializable { 16 | /** 17 | * 电影ID 18 | */ 19 | private Long did; 20 | 21 | /** 22 | * 喜欢这部电影的人也喜欢的电影ID 23 | */ 24 | private Long sid; 25 | 26 | @TableField(exist = false) 27 | private static final long serialVersionUID = 1L; 28 | } -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/entity/TypeLike.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.TableField; 4 | import com.baomidou.mybatisplus.annotation.TableName; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | 9 | import java.io.Serializable; 10 | 11 | /** 12 | * 13 | * @author zjw 14 | * @TableName type_like 15 | */ 16 | @TableName(value ="type_like") 17 | @Data 18 | @AllArgsConstructor 19 | @NoArgsConstructor 20 | public class TypeLike implements Serializable { 21 | /** 22 | * 用户id 23 | */ 24 | private Long uid; 25 | 26 | /** 27 | * 电影类型id 28 | */ 29 | private Integer tid; 30 | 31 | /** 32 | * 喜爱程度 33 | */ 34 | private Integer degree; 35 | 36 | @TableField(exist = false) 37 | private static final long serialVersionUID = 1L; 38 | } -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/entity/User.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.entity; 2 | 3 | import cn.zjw.mrs.enums.SexEnum; 4 | import com.baomidou.mybatisplus.annotation.IdType; 5 | import com.baomidou.mybatisplus.annotation.TableField; 6 | import com.baomidou.mybatisplus.annotation.TableId; 7 | import com.baomidou.mybatisplus.annotation.TableName; 8 | import com.fasterxml.jackson.annotation.JsonFormat; 9 | import lombok.Data; 10 | 11 | import java.io.Serializable; 12 | import java.sql.Timestamp; 13 | 14 | /** 15 | * @author zjw 16 | * @TableName user 17 | */ 18 | @TableName(value ="user") 19 | @Data 20 | public class User implements Serializable { 21 | /** 22 | * 用户id 23 | */ 24 | @TableId(type = IdType.AUTO) 25 | private Long id; 26 | 27 | /** 28 | * 用户名 29 | */ 30 | private String username; 31 | 32 | /** 33 | * 昵称 34 | */ 35 | private String nickname; 36 | 37 | /** 38 | * 密码 39 | */ 40 | private String password; 41 | 42 | /** 43 | * 头像 44 | */ 45 | private String avatar; 46 | 47 | /** 48 | * 性别(0表示女,1表示男,2表示保密) 49 | */ 50 | private SexEnum sex; 51 | 52 | /** 53 | * 状态 54 | */ 55 | private Integer state; 56 | 57 | /** 58 | * 创建时间 59 | */ 60 | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") 61 | private Timestamp createTime; 62 | 63 | /** 64 | * 邮箱 65 | */ 66 | private String mail; 67 | 68 | @TableField(exist = false) 69 | private static final long serialVersionUID = 1L; 70 | } -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/entity/UserLike.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableField; 5 | import com.baomidou.mybatisplus.annotation.TableId; 6 | import com.baomidou.mybatisplus.annotation.TableName; 7 | import java.io.Serializable; 8 | import lombok.Data; 9 | 10 | /** 11 | * 12 | * @author zjw 13 | * @TableName user_like 14 | */ 15 | @TableName(value ="user_like") 16 | @Data 17 | public class UserLike implements Serializable { 18 | /** 19 | * ID号 20 | */ 21 | @TableId(type = IdType.AUTO) 22 | private Long id; 23 | 24 | /** 25 | * 被点赞的评论id 26 | */ 27 | private Long cid; 28 | 29 | /** 30 | * 点赞的用户id 31 | */ 32 | private Long uid; 33 | 34 | /** 35 | * 点赞状态(0表示未点赞,1表示已点赞) 36 | */ 37 | private Integer status; 38 | 39 | @TableField(exist = false) 40 | private static final long serialVersionUID = 1L; 41 | 42 | public UserLike(Long cid, Long uid, Integer status) { 43 | this.cid = cid; 44 | this.uid = uid; 45 | this.status = status; 46 | } 47 | } -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/enums/HttpCodeEnum.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.enums; 2 | 3 | import lombok.Getter; 4 | 5 | /** 6 | * @author zjw 7 | * @Classname HttpCodeEnum 8 | * @Date 2022/5/16 12:47 9 | * @Description 10 | */ 11 | @Getter 12 | public enum HttpCodeEnum { 13 | /** 14 | * 状态码 - 信息 15 | */ 16 | SUCCESS(200,"操作成功"), 17 | NEED_LOGIN(401,"需要登录后操作"), 18 | NO_OPERATOR_AUTH(403,"无权限操作"), 19 | SYSTEM_ERROR(500,"出现错误"), 20 | USERNAME_EXIST(501,"用户名已存在"), 21 | PHONE_NUMBER_EXIST(502,"手机号已存在"), 22 | EMAIL_EXIST(503, "邮箱已存在"), 23 | REQUIRE_USERNAME(504, "必需填写用户名"), 24 | LOGIN_ERROR(505,"用户名或密码错误"); 25 | 26 | final int code; 27 | final String msg; 28 | 29 | HttpCodeEnum(int code, String msg){ 30 | this.code = code; 31 | this.msg = msg; 32 | } 33 | 34 | public int getCode() { 35 | return code; 36 | } 37 | 38 | public String getMsg() { 39 | return msg; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/enums/LikedStatusEnum.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.enums; 2 | 3 | import lombok.Getter; 4 | 5 | /** 6 | * @author zjw 7 | * @Classname LikedStatusEnum 8 | * @Date 2022/5/22 16:51 9 | * @Description 10 | */ 11 | @Getter 12 | public enum LikedStatusEnum { 13 | /** 14 | * 用户点赞的状态 15 | */ 16 | LIKE(1, "点赞"), 17 | UNLIKE(0, "取消点赞/未点赞"),; 18 | 19 | private final Integer code; 20 | 21 | private final String msg; 22 | 23 | LikedStatusEnum(Integer code, String msg) { 24 | this.code = code; 25 | this.msg = msg; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/enums/RecommendationTypeEnum.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.enums; 2 | 3 | import com.baomidou.mybatisplus.annotation.EnumValue; 4 | import lombok.Getter; 5 | 6 | /** 7 | * @author zjw 8 | * @Classname RecommendationTypeEnum 9 | * @Date 2022/5/28 16:34 10 | * @Description 11 | */ 12 | @Getter 13 | public enum RecommendationTypeEnum { 14 | /** 15 | * 1表示基于内容,2表示协同过滤,0表示随机推荐 16 | */ 17 | RANDOM(0, "随机推荐"), 18 | CONTENT_BASED(1, "基于内容推荐"), 19 | USER_BASED_CF(2, "基于用户协同过滤推荐"); 20 | 21 | /** 22 | * 推荐类型代码 23 | */ 24 | @EnumValue 25 | private final Integer typeCode; 26 | /** 27 | * 推荐类型名称 28 | */ 29 | private final String typeName; 30 | 31 | RecommendationTypeEnum(Integer typeCode, String typeName) { 32 | this.typeCode = typeCode; 33 | this.typeName = typeName; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/enums/RegionEnum.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.enums; 2 | 3 | import com.baomidou.mybatisplus.annotation.EnumValue; 4 | import lombok.Getter; 5 | 6 | /** 7 | * @author zjw 8 | * @Classname RegionEnum 9 | * @Date 2022/4/12 22:48 10 | * @Description 11 | */ 12 | @Getter 13 | public enum RegionEnum { 14 | // 地区标识 —— 地区名称 15 | MAINLAND_CHINA(0, "中国大陆"), 16 | USA(1, "美国"), 17 | HONG_KONG(2, "中国香港"), 18 | CHINESE_TAIWAN(3, "中国台湾"), 19 | JAPAN(4, "日本"), 20 | KOREA(5, "韩国"), 21 | UK(6, "英国"), 22 | FRANCE(7, "法国"), 23 | GERMANY(8, "德国"), 24 | ITALY(9, "意大利"), 25 | SPAIN(10, "西班牙"), 26 | INDIA(11, "印度"), 27 | THAILAND(12, "泰国"), 28 | RUSSIA(13, "俄罗斯"), 29 | IRAN(14, "伊朗"), 30 | CANADA(15, "加拿大"), 31 | AUSTRALIA(16, "澳大利亚"), 32 | IRELAND(17, "爱尔兰"), 33 | SWEDEN(18, "瑞典"), 34 | BRAZIL(19, "巴西"), 35 | DENMARK(20, "丹麦"); 36 | 37 | /** 38 | * 地区标识 39 | * 将注解所标识的属性的值存储到数据库中 40 | */ 41 | @EnumValue 42 | private final Integer region; 43 | /** 44 | * 地区名称 45 | */ 46 | private final String regionName; 47 | 48 | RegionEnum(Integer region, String regionName) { 49 | this.region = region; 50 | this.regionName = regionName; 51 | } 52 | 53 | /** 54 | * 通过地区名称查找地区标识 55 | * @param regionName 地区名称 56 | * @return 地区标识 57 | */ 58 | public static Integer findRegionByRegionName (String regionName) { 59 | for (RegionEnum regionEnum : RegionEnum.values()) { 60 | if (regionEnum.getRegionName().equals(regionName)) { 61 | return regionEnum.getRegion(); 62 | } 63 | } 64 | return -1; 65 | } 66 | 67 | /** 68 | * 通过地区标识查找地区名称 69 | * @param region 地区标识 70 | * @return 地区名称 71 | */ 72 | public static String findRegionNameByRegion (Integer region) { 73 | for (RegionEnum regionEnum : RegionEnum.values()) { 74 | if (regionEnum.getRegion().equals(region)) { 75 | return regionEnum.getRegionName(); 76 | } 77 | } 78 | throw new IllegalArgumentException("region is invalid"); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/enums/SexEnum.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.enums; 2 | 3 | import com.baomidou.mybatisplus.annotation.EnumValue; 4 | import lombok.Getter; 5 | 6 | /** 7 | * @author zjw 8 | * @Classname SexEnum 9 | * @Date 2022/4/11 23:40 10 | * @Description 11 | */ 12 | @Getter 13 | public enum SexEnum { 14 | // 男 15 | MALE(1, "男"), 16 | // 女 17 | FEMALE(0, "女"), 18 | // 保密 19 | SECRET(2, "保密"); 20 | 21 | /** 22 | * 性别标识 23 | * 将注解所标识的属性的值存储到数据库中 24 | */ 25 | @EnumValue 26 | private final Integer sex; 27 | /** 28 | * 性别名称 29 | */ 30 | private final String sexName; 31 | 32 | SexEnum(Integer sex, String sexName) { 33 | this.sex = sex; 34 | this.sexName = sexName; 35 | } 36 | 37 | /** 38 | * 通过性别名称查找性别标识 39 | * @param sexName 性别名称 40 | * @return 性别标识 41 | */ 42 | public static Integer findSexBySexName (String sexName) { 43 | for (SexEnum sexEnum : SexEnum.values()) { 44 | if (sexEnum.getSexName().equals(sexName)) { 45 | return sexEnum.getSex(); 46 | } 47 | } 48 | throw new IllegalArgumentException("sexName is invalid"); 49 | } 50 | 51 | /** 52 | * 通过性别标识查找性别名称 53 | * @param sex 性别标识 54 | * @return 性别名称 55 | */ 56 | public static String findSexNameBySex (Integer sex) { 57 | for (SexEnum sexEnum : SexEnum.values()) { 58 | if (sexEnum.getSex().equals(sex)) { 59 | return sexEnum.getSexName(); 60 | } 61 | } 62 | throw new IllegalArgumentException("sex is invalid"); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/enums/TypeEnum.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.enums; 2 | 3 | import com.baomidou.mybatisplus.annotation.EnumValue; 4 | import lombok.Getter; 5 | 6 | /** 7 | * @author zjw 8 | * @Classname TypeEnum 9 | * @Date 2022/4/12 22:48 10 | * @Description 11 | */ 12 | @Getter 13 | public enum TypeEnum { 14 | // 类型标识 —— 类型名称 15 | DRAMA(0, "剧情"), 16 | COMEDY(1, "喜剧"), 17 | ACTION(2, "动作"), 18 | LOVE(3, "爱情"), 19 | SCIENCE_FICTION(4, "科幻"), 20 | ANIMATION(5, "动画"), 21 | SUSPENSE(6, "悬疑"), 22 | THRILLER(7, "惊悚"), 23 | HORROR(8, "恐怖"), 24 | CRIME(9, "犯罪"), 25 | MUSIC(10, "音乐"), 26 | SONG_AND_DANCE(11, "歌舞"), 27 | BIOGRAPHY(12, "传记"), 28 | HISTORY(13, "历史"), 29 | WAR(14, "战争"), 30 | WEST(15, "西部"), 31 | FANTASY(16, "奇幻"), 32 | ADVENTURE(17, "冒险"), 33 | DISASTER(18, "灾难"), 34 | MARTIAL(19, "武侠"), 35 | OTHER(20, "其他"); 36 | 37 | /** 38 | * 类型标识 39 | * 将注解所标识的属性的值存储到数据库中 40 | */ 41 | @EnumValue 42 | private final Integer type; 43 | /** 44 | * 类型名称 45 | */ 46 | private final String typeName; 47 | 48 | TypeEnum(Integer type, String typeName) { 49 | this.type = type; 50 | this.typeName = typeName; 51 | } 52 | 53 | /** 54 | * 通过类型名称查找类型标识 55 | * @param typeName 类型名称 56 | * @return 类型标识 57 | */ 58 | public static Integer findTypeByTypeName (String typeName) { 59 | for (TypeEnum typeEnum : TypeEnum.values()) { 60 | if (typeEnum.getTypeName().equals(typeName)) { 61 | return typeEnum.getType(); 62 | } 63 | } 64 | return -1; 65 | } 66 | 67 | /** 68 | * 通过类型标识查找类型名称 69 | * @param type 类型标识 70 | * @return 类型名称 71 | */ 72 | public static String findRegionNameByRegion (Integer type) { 73 | for (TypeEnum typeEnum : TypeEnum.values()) { 74 | if (typeEnum.getType().equals(type)) { 75 | return typeEnum.getTypeName(); 76 | } 77 | } 78 | throw new IllegalArgumentException("type is invalid"); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/filter/JwtAuthenticationTokenFilter.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.filter; 2 | 3 | import cn.zjw.mrs.entity.LoginUser; 4 | import cn.zjw.mrs.utils.JwtUtil; 5 | import cn.zjw.mrs.utils.RedisCache; 6 | 7 | import io.jsonwebtoken.Claims; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; 10 | import org.springframework.security.core.context.SecurityContextHolder; 11 | import org.springframework.stereotype.Component; 12 | import org.springframework.util.StringUtils; 13 | import org.springframework.web.filter.OncePerRequestFilter; 14 | 15 | import javax.servlet.FilterChain; 16 | import javax.servlet.ServletException; 17 | import javax.servlet.http.HttpServletRequest; 18 | import javax.servlet.http.HttpServletResponse; 19 | import java.io.IOException; 20 | import java.util.Objects; 21 | 22 | /** 23 | * @author zjw 24 | * @Classname JwtAuthenticationTokenFilter 25 | * @Date 2022/4/11 19:50 26 | * @Description 定义jwt认证过滤器 27 | */ 28 | 29 | @Component 30 | public class JwtAuthenticationTokenFilter extends OncePerRequestFilter { 31 | 32 | @Autowired 33 | private RedisCache redisCache; 34 | 35 | @Override 36 | protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { 37 | // 获取token 38 | String token = request.getHeader("token"); 39 | if (!StringUtils.hasText(token)) { 40 | // 放行 41 | filterChain.doFilter(request, response); 42 | return; 43 | } 44 | // 解析token 45 | String userid; 46 | try { 47 | Claims claims = JwtUtil.parseJwt(token); 48 | userid = claims.getSubject(); 49 | } catch (Exception e) { 50 | e.printStackTrace(); 51 | throw new RuntimeException("非法token"); 52 | } 53 | // 从redis中获取用户信息 54 | String redisKey = "login:" + userid; 55 | LoginUser loginUser = redisCache.getCacheObject(redisKey); 56 | if(Objects.isNull(loginUser)){ 57 | throw new RuntimeException("用户未登录"); 58 | } 59 | // 存入SecurityContextHolder 60 | // 获取权限信息封装到Authentication中 61 | UsernamePasswordAuthenticationToken authenticationToken = 62 | new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities()); 63 | SecurityContextHolder.getContext().setAuthentication(authenticationToken); 64 | // 放行 65 | filterChain.doFilter(request, response); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/handler/AccessDeniedHandlerImpl.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.handler; 2 | 3 | import cn.zjw.mrs.entity.Result; 4 | import cn.zjw.mrs.utils.WebUtils; 5 | import com.alibaba.fastjson.JSON; 6 | import org.springframework.http.HttpStatus; 7 | import org.springframework.security.access.AccessDeniedException; 8 | import org.springframework.security.web.access.AccessDeniedHandler; 9 | import org.springframework.stereotype.Component; 10 | 11 | import javax.servlet.ServletException; 12 | import javax.servlet.http.HttpServletRequest; 13 | import javax.servlet.http.HttpServletResponse; 14 | import java.io.IOException; 15 | 16 | /** 17 | * @author zjw 18 | * @Classname AccessDeniedHandlerImpl 19 | * @Date 2022/4/11 21:31 20 | * @Description 自定义异常处理,当授权失败时会处理异常 21 | */ 22 | 23 | @Component 24 | public class AccessDeniedHandlerImpl implements AccessDeniedHandler { 25 | @Override 26 | public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException { 27 | Result result = new Result(HttpStatus.FORBIDDEN.value(), "您的权限不足"); 28 | String json = JSON.toJSONString(result); 29 | // 处理异常 30 | WebUtils.renderString(response, json); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/handler/AuthenticationEntryPointImpl.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.handler; 2 | 3 | import cn.zjw.mrs.entity.Result; 4 | import cn.zjw.mrs.enums.HttpCodeEnum; 5 | import cn.zjw.mrs.utils.WebUtils; 6 | import com.alibaba.fastjson.JSON; 7 | import org.springframework.http.HttpStatus; 8 | import org.springframework.security.authentication.BadCredentialsException; 9 | import org.springframework.security.authentication.InsufficientAuthenticationException; 10 | import org.springframework.security.authentication.InternalAuthenticationServiceException; 11 | import org.springframework.security.core.AuthenticationException; 12 | import org.springframework.security.web.AuthenticationEntryPoint; 13 | import org.springframework.stereotype.Component; 14 | 15 | import javax.servlet.ServletException; 16 | import javax.servlet.http.HttpServletRequest; 17 | import javax.servlet.http.HttpServletResponse; 18 | import java.io.IOException; 19 | import java.util.Arrays; 20 | 21 | /** 22 | * @author zjw 23 | * @Classname AuthenticationEntryPointImpl 24 | * @Date 2022/4/11 21:22 25 | * @Description 自定义异常处理,当认证失败时会处理异常 26 | */ 27 | @Component 28 | public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint { 29 | @Override 30 | public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException { 31 | authException.printStackTrace(); 32 | Result result = null; 33 | if(authException instanceof InternalAuthenticationServiceException || authException instanceof BadCredentialsException){ 34 | result = Result.error(HttpCodeEnum.LOGIN_ERROR.getCode(), HttpCodeEnum.LOGIN_ERROR.getMsg()); 35 | }else if(authException instanceof InsufficientAuthenticationException){ 36 | result = Result.error(HttpCodeEnum.NEED_LOGIN.getCode(), HttpCodeEnum.NEED_LOGIN.getMsg()); 37 | }else{ 38 | result = Result.error(HttpCodeEnum.SYSTEM_ERROR.getCode(), "认证或授权失败"); 39 | } 40 | String json = JSON.toJSONString(result); 41 | // 处理异常 42 | WebUtils.renderString(response, json); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/mapper/CommentMapper.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.mapper; 2 | 3 | import cn.zjw.mrs.entity.Comment; 4 | import cn.zjw.mrs.entity.Preference; 5 | import cn.zjw.mrs.vo.comment.CommentMovieVo; 6 | import cn.zjw.mrs.vo.comment.CommentStripVo; 7 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 8 | import org.springframework.stereotype.Repository; 9 | 10 | import java.util.List; 11 | 12 | /** 13 | * @author 95758 14 | * @description 针对表【comment】的数据库操作Mapper 15 | * @createDate 2022-04-14 19:19:45 16 | * @Entity cn.zjw.mrs.entity.Comment 17 | */ 18 | @Repository 19 | public interface CommentMapper extends BaseMapper { 20 | /** 21 | * 获取某个用户评价某部电影的评价信息 22 | * @param uid 用户id 23 | * @param mid 电影id 24 | * @return 评价信息 25 | */ 26 | CommentStripVo selectOwnCommentByUidAndMid(Long uid, Long mid); 27 | 28 | /** 29 | * 获取某部电影下的所有评价信息 30 | * @param mid 电影id 31 | * @param currentPage 当前页数 32 | * @param pageSize 每页评论数 33 | * @return 所有评价信息 34 | */ 35 | List selectMoreCommentsByMovieId(Long mid, int currentPage, int pageSize); 36 | 37 | /** 38 | * 获取某个用户的所有评价动态 39 | * @param uid 用户id 40 | * @param currentIndex 从第几条开始取 41 | * @param pageSize 每页条数 42 | * @return 所有评价动态 43 | */ 44 | List selectOwnCommentMovieMoments(Long uid, Integer currentIndex, Integer pageSize); 45 | 46 | /** 47 | * 获取用户所有的偏好 48 | * @return 49 | */ 50 | List selectAllPreferences(); 51 | } 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/mapper/MovieFeatureMapper.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.mapper; 2 | 3 | import cn.zjw.mrs.entity.MovieFeature; 4 | import cn.zjw.mrs.entity.MovieRegion; 5 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * @author 95758 11 | * @description 针对表【movie_feature】的数据库操作Mapper 12 | * @createDate 2022-04-22 10:29:18 13 | * @Entity cn.zjw.mrs.entity.MovieFeature 14 | */ 15 | public interface MovieFeatureMapper extends BaseMapper { 16 | /** 17 | * 查询用户id为uid的用户,所有没有评价过的评分超过5分的电影矩阵列表 18 | * @param uid 用户id 19 | * @return 所有没有评价过的评分超过5分的电影矩阵列表 20 | */ 21 | List selectAllMovieFeaturesWhereUserNotWatchedAndScoreMoreThanFive(Long uid); 22 | 23 | /** 24 | * 获取用户id为uid的用户,其推荐电影列表中每部电影的特征矩阵 25 | * @param uid 用户id 26 | * @param num 取多少部电影 27 | * @return 推荐电影列表中每部电影的特征矩阵 28 | */ 29 | List selectRecommendedMoviesFeaturesByUserId(Long uid, Integer num); 30 | 31 | /** 32 | * 获取用户id为uid的用户,其看过的电影列表(最近看过的前num部电影)中每部电影的特征矩阵 33 | * @param uid 用户id 34 | * @param num 最近评价过的num部电影 35 | * @return 看过的电影列表中每部电影的特征矩阵 36 | */ 37 | List selectWatchedMoviesFeaturesByUserId(Long uid, Integer num); 38 | } 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/mapper/MovieMapper.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.mapper; 2 | 3 | import cn.zjw.mrs.entity.Movie; 4 | import cn.zjw.mrs.vo.movie.MovieCardVo; 5 | import cn.zjw.mrs.vo.movie.MovieStripVo; 6 | import cn.zjw.mrs.vo.movie.ReviewedMovieStripVo; 7 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 8 | import org.springframework.stereotype.Repository; 9 | 10 | import java.util.List; 11 | 12 | 13 | /** 14 | * @author 95758 15 | * @description 针对表【movie】的数据库操作Mapper 16 | * @createDate 2022-04-07 19:01:20 17 | * @Entity cn.zjw.mrs.entity.Movie 18 | */ 19 | @Repository 20 | public interface MovieMapper extends BaseMapper { 21 | 22 | /** 23 | * 查询某部电影的“喜欢这部电影的人也喜欢。。”的电影列表 24 | * @param id 电影id 25 | * @return “喜欢这部电影的人也喜欢。。”电影列表 26 | */ 27 | List selectRecommendedMoviesByMovieId(Long id); 28 | 29 | /** 30 | * 查询某个用户评价过的所有电影基本信息 31 | * @param uid 用户id 32 | * @param currentIndex 从第几条开始取 33 | * @param pageSize 每页条数 34 | * @return 评价过的所有电影基本信息 35 | */ 36 | List selectMoreReviewedMoviesByUserId(Long uid, Integer currentIndex, Integer pageSize); 37 | 38 | /** 39 | * 查询某个用户最近评价过的若干电影基本信息 40 | * @param uid 用户id 41 | * @param num 最近评价的前num部电影 42 | * @return 评价过的若干电影基本信息 43 | */ 44 | List selectSomeReviewedMoviesByUserId(Long uid, Integer num); 45 | 46 | /** 47 | * 查询最多人看过的(评价过)的电影列表(前num部) 48 | * @param num 要查询的电影数量 49 | * @return 最多人看过的(评价过)的电影列表 50 | */ 51 | List selectMostWatchedMovies(Integer num); 52 | 53 | /** 54 | * 查询评分最高的前n部电影列表 55 | * @param num 要查询的电影数量 56 | * @return 评分最高的前n部电影列表 57 | */ 58 | List selectHighestRatedMovies(Integer num); 59 | } 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/mapper/MovieRegionMapper.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.mapper; 2 | 3 | import cn.zjw.mrs.entity.MovieRegion; 4 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * @author 95758 10 | * @description 针对表【movie_region】的数据库操作Mapper 11 | * @createDate 2022-04-21 22:58:21 12 | * @Entity cn.zjw.mrs.entity.MovieRegion 13 | */ 14 | public interface MovieRegionMapper extends BaseMapper { 15 | /** 16 | * 获取用户id为uid的用户,其推荐电影列表中每部电影的地区 17 | * @param uid 用户id 18 | * @return 推荐电影列表中每部电影的地区 19 | */ 20 | List selectRecommendedMoviesRegionsByUserId(Long uid); 21 | 22 | /** 23 | * 获取用户id为uid的用户,其看过的电影列表(最近看过的前num部电影)中每部电影的地区 24 | * @param uid 用户id 25 | * @param num 最近评价过的num部电影 26 | * @return 看过的电影列表中每部电影的地区 27 | */ 28 | List selectWatchedMoviesRegionsByUserId(Long uid, Integer num); 29 | } 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/mapper/MovieTypeMapper.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.mapper; 2 | 3 | import cn.zjw.mrs.entity.MovieType; 4 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * @author zjw 10 | * @description 针对表【movie_type】的数据库操作Mapper 11 | * @createDate 2022-04-21 22:58:26 12 | * @Entity cn.zjw.mrs.entity.MovieType 13 | */ 14 | public interface MovieTypeMapper extends BaseMapper { 15 | /** 16 | * 获取用户id为uid的用户,其推荐电影列表中每部电影的类型 17 | * @param uid 用户id 18 | * @return 推荐电影列表中每部电影的类型 19 | */ 20 | List selectRecommendedMoviesTypesByUserId(Long uid); 21 | 22 | /** 23 | * 获取用户id为uid的用户,其看过的电影列表(最近看过的前num部电影)中每部电影的类型 24 | * @param uid 用户id 25 | * @param num 最近评价过的num部电影 26 | * @return 看过的电影列表中每部电影的类型 27 | */ 28 | List selectWatchedMoviesTypesByUserId(Long uid, Integer num); 29 | } 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/mapper/RecommendationMapper.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.mapper; 2 | 3 | import cn.zjw.mrs.entity.Recommendation; 4 | import cn.zjw.mrs.vo.movie.RecommendedMovieVo; 5 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * @author 95758 11 | * @description 针对表【recommendation】的数据库操作Mapper 12 | * @createDate 2022-04-24 00:04:20 13 | * @Entity cn.zjw.mrs.entity.Recommendation 14 | */ 15 | public interface RecommendationMapper extends BaseMapper { 16 | 17 | /** 18 | * 获取用户id为uid的电影推荐列表 19 | * @param num 推荐指数排名前n部电影 20 | * @param uid 用户id 21 | * @return 电影推荐列表 22 | */ 23 | List selectRecommendedMoviesByUserId(Long uid, Integer num); 24 | } 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/mapper/RegionLikeMapper.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.mapper; 2 | 3 | import cn.zjw.mrs.entity.RegionLike; 4 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 5 | 6 | /** 7 | * @author 95758 8 | * @description 针对表【region_like】的数据库操作Mapper 9 | * @createDate 2022-04-20 16:13:15 10 | * @Entity cn.zjw.mrs.entity.RegionLike 11 | */ 12 | public interface RegionLikeMapper extends BaseMapper { 13 | 14 | } 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/mapper/SameLikesMapper.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.mapper; 2 | 3 | import cn.zjw.mrs.entity.SameLikes; 4 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 5 | 6 | /** 7 | * @author 95758 8 | * @description 针对表【same_likes】的数据库操作Mapper 9 | * @createDate 2022-04-23 17:44:19 10 | * @Entity cn.zjw.mrs.entity.SameLikes 11 | */ 12 | public interface SameLikesMapper extends BaseMapper { 13 | 14 | } 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/mapper/TypeLikeMapper.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.mapper; 2 | 3 | import cn.zjw.mrs.entity.TypeLike; 4 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 5 | 6 | /** 7 | * @author 95758 8 | * @description 针对表【type_like】的数据库操作Mapper 9 | * @createDate 2022-04-20 16:13:21 10 | * @Entity cn.zjw.mrs.entity.TypeLike 11 | */ 12 | public interface TypeLikeMapper extends BaseMapper { 13 | 14 | } 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/mapper/UserLikeMapper.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.mapper; 2 | 3 | import cn.zjw.mrs.entity.UserLike; 4 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 5 | 6 | /** 7 | * @author 95758 8 | * @description 针对表【user_like】的数据库操作Mapper 9 | * @createDate 2022-05-22 16:43:36 10 | * @Entity cn.zjw.mrs.entity.UserLike 11 | */ 12 | public interface UserLikeMapper extends BaseMapper { 13 | 14 | } 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/mapper/UserMapper.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.mapper; 2 | 3 | import cn.zjw.mrs.entity.User; 4 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 5 | import org.springframework.stereotype.Repository; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * @author 95758 11 | * @description 针对表【user】的数据库操作Mapper 12 | * @createDate 2022-04-10 22:46:38 13 | * @Entity cn.zjw.mrs.entity.User 14 | */ 15 | @Repository 16 | public interface UserMapper extends BaseMapper { 17 | /** 18 | * 获取用户的类型喜好 19 | * @param id 用户id 20 | * @return 类型喜好 21 | */ 22 | List selectUserTypes(Long id); 23 | 24 | /** 25 | * 获取用户的地区喜好 26 | * @param id 用户id 27 | * @return 地区喜好 28 | */ 29 | List selectUserRegions(Long id); 30 | } 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/service/AuthService.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.service; 2 | 3 | import cn.zjw.mrs.entity.Result; 4 | import org.springframework.web.bind.annotation.RequestParam; 5 | 6 | /** 7 | * @author zjw 8 | * @Classname AuthService 9 | * @Date 2022/5/10 18:08 10 | * @Description 11 | */ 12 | public interface AuthService { 13 | 14 | /** 15 | * 发送邮件验证码 16 | * @param username 用户名 17 | * @param mail 待接收的邮箱号码 18 | */ 19 | void sendMailAuthCode(String username, String mail); 20 | 21 | /** 22 | * 校验验证码 23 | * @param username 用户名 24 | * @param authCode 待校验的验证码 25 | * @return 校验结果。 26 | * -1表示验证码已过期; 27 | * 1表示输入的验证码正确; 28 | * 0表示输入的验证码有误; 29 | */ 30 | int judgeAuthCode(String username, String authCode); 31 | 32 | /** 33 | * 判断邮箱是否属于用户 34 | * @param username 用户名 35 | * @param mail 邮箱 36 | * @return true表示邮箱属于用户;false表示邮箱不属于用户。 37 | */ 38 | boolean judgeMailBelongToUser(String username, String mail); 39 | 40 | /** 41 | * 判断用户是否存在于数据库中 42 | * @param username 待判断的用户名 43 | * @return true表示存在;false表示不存在。 44 | */ 45 | boolean judgeUsernameExists(String username); 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/service/CommentService.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.service; 2 | 3 | import cn.zjw.mrs.entity.Comment; 4 | import cn.zjw.mrs.vo.comment.CommentMovieVo; 5 | import cn.zjw.mrs.vo.comment.CommentStripVo; 6 | import com.baomidou.mybatisplus.extension.service.IService; 7 | import org.springframework.stereotype.Repository; 8 | 9 | import java.util.List; 10 | import java.util.Map; 11 | 12 | /** 13 | * @author zjw 14 | * @description 针对表【comment】的数据库操作Service 15 | * @createDate 2022-04-14 19:19:45 16 | */ 17 | @Repository 18 | public interface CommentService extends IService { 19 | /** 20 | * 添加评价信息,如果评价信息已经存在,则更新评价信息 21 | * @param comment 评价信息 22 | * @param username 用户账号(唯一标识) 23 | * @return 24 | * -1表示评价更新失败; 25 | * 1表示评论更新成功; 26 | * -2表示评价失败; 27 | * 2表示评论成功 28 | */ 29 | Integer addComment(Comment comment, String username); 30 | 31 | /** 32 | * 获取某个用户对某部电影的评价信息 33 | * @param uid 用户id 34 | * @param mid 电影id 35 | * @return 对电影的评价信息 36 | */ 37 | CommentStripVo getOwnComment(Long uid, Long mid); 38 | 39 | /** 40 | * 获取某部电影下的若干评价信息 41 | * @param mid 电影id 42 | * @param currentPage 当前页数 43 | * @param pageSize 每页评论数 44 | * @return 若干电影评价信息 45 | */ 46 | List getMoreCommentsByMovieId(Long mid, int currentPage, int pageSize); 47 | 48 | /** 49 | * 获取指定用户的电影评价动态 50 | * @param uid 用户id 51 | * @param currentPage 当前页码 52 | * @param pageSize 每页条数 53 | * @return 电影评价动态条目 54 | */ 55 | Map getMoreOwnCommentMovieMoments(Long uid, Integer currentPage, Integer pageSize); 56 | 57 | /** 58 | * 删除用户对某部电影的评论 59 | * @param uid 用户id 60 | * @param mid 电影id 61 | * @return 删除结果 62 | */ 63 | int removeOwnComment(Long uid, Long mid); 64 | 65 | /** 66 | * 获取电影id为mid的电影下,评论词云图绘制所需的数据 67 | * @param mid 电影id 68 | * @return 词云图绘制所需数据 69 | */ 70 | List> getCommentsWordCloudData(long mid); 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/service/MovieFeatureService.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.service; 2 | 3 | import cn.zjw.mrs.entity.MovieFeature; 4 | import com.baomidou.mybatisplus.extension.service.IService; 5 | 6 | /** 7 | * @author 95758 8 | * @description 针对表【movie_feature】的数据库操作Service 9 | * @createDate 2022-04-22 10:29:18 10 | */ 11 | public interface MovieFeatureService extends IService { 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/service/MovieRegionService.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.service; 2 | 3 | import cn.zjw.mrs.entity.MovieRegion; 4 | import com.baomidou.mybatisplus.extension.service.IService; 5 | 6 | /** 7 | * @author 95758 8 | * @description 针对表【movie_region】的数据库操作Service 9 | * @createDate 2022-04-21 22:58:21 10 | */ 11 | public interface MovieRegionService extends IService { 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/service/MovieService.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.service; 2 | 3 | import cn.zjw.mrs.entity.Movie; 4 | import cn.zjw.mrs.entity.Result; 5 | import cn.zjw.mrs.vo.movie.MovieCardVo; 6 | import cn.zjw.mrs.vo.movie.MovieStripVo; 7 | import cn.zjw.mrs.vo.movie.ReviewedMovieStripVo; 8 | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; 9 | import com.baomidou.mybatisplus.extension.service.IService; 10 | 11 | import java.util.List; 12 | import java.util.Map; 13 | 14 | /** 15 | * @author zjw 16 | * @description 针对表【movie】的数据库操作Service 17 | * @createDate 2022-04-07 19:01:20 18 | */ 19 | public interface MovieService extends IService { 20 | 21 | /** 22 | * 返回电影分页结果 23 | * @param currentPage 当前页码 24 | * @param pageSize 每页电影数量 25 | * @param type 电影类型 26 | * @param region 电影地区 27 | * @param search 搜索关键字 28 | * @return 分页结果-电影列表 29 | */ 30 | Page getPageMovies(Integer currentPage, Integer pageSize, String type, String region, String search); 31 | 32 | /** 33 | * 通过did去数据库中查找”喜欢这部电影的人也喜欢。。。“的电影条目 34 | * @param did 电影did 35 | * @return ”喜欢这部电影的人也喜欢。。。”电影条目 36 | */ 37 | List getRecommendedMoviesByMovieId(Long did); 38 | 39 | /** 40 | * 获取用户id为uid的用户评价过的电影列表(按页数和每页条数获取) 41 | * @param uid 用户id 42 | * @param currentPage 当前页数 43 | * @param pageSize 每页条数 44 | * @return 评价过的电影条目和总数量 45 | */ 46 | Map getMoreReviewedMoviesByUserId(Long uid, Integer currentPage, Integer pageSize); 47 | 48 | /** 49 | * 获取搜索关键字模糊匹配的电影名称列表 50 | * @param keywords 搜索关键字 51 | * @return 电影名称列表 52 | */ 53 | List getMatchMovieName(String keywords); 54 | 55 | /** 56 | * 获取最多人看过的(评论过的)电影列表 57 | * @return 最多人看过的(评论过的)电影列表 58 | */ 59 | List getMostWatchedMovies(); 60 | 61 | /** 62 | * 获得评分最高的前n部电影列表 63 | * @return 评分最高的前n部电影列表 64 | */ 65 | List getHighestRatedMovies(); 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/service/MovieTypeService.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.service; 2 | 3 | import cn.zjw.mrs.entity.MovieType; 4 | import com.baomidou.mybatisplus.extension.service.IService; 5 | 6 | /** 7 | * @author 95758 8 | * @description 针对表【movie_type】的数据库操作Service 9 | * @createDate 2022-04-21 22:58:26 10 | */ 11 | public interface MovieTypeService extends IService { 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/service/OssService.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.service; 2 | 3 | import cn.zjw.mrs.entity.Result; 4 | import org.springframework.web.multipart.MultipartFile; 5 | 6 | /** 7 | * @author zjw 8 | * @Classname OssService 9 | * @Date 2022/4/17 13:50 10 | * @Description 11 | */ 12 | public interface OssService { 13 | /** 14 | * 上传头像到oss服务器上,并将图片路径存入数据库中 15 | * @param username 用户名 16 | * @param uploadFile 待上传头像 17 | * @return 上传结果 18 | */ 19 | boolean updateAvatar(String username, MultipartFile uploadFile); 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/service/RecommendationService.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.service; 2 | 3 | import cn.hutool.core.lang.Pair; 4 | import cn.zjw.mrs.entity.Recommendation; 5 | import cn.zjw.mrs.vo.movie.RecommendedMovieVo; 6 | import com.baomidou.mybatisplus.extension.service.IService; 7 | import org.apache.mahout.cf.taste.recommender.RecommendedItem; 8 | 9 | import java.util.List; 10 | import java.util.Map; 11 | 12 | /** 13 | * @author 95758 14 | * @description 针对表【recommendation】的数据库操作Service 15 | * @createDate 2022-04-24 00:04:20 16 | */ 17 | public interface RecommendationService extends IService { 18 | /** 19 | * 解决用户冷启动问题,用户注册是调用 20 | * @param uid 用户id 21 | */ 22 | void solveColdStart(long uid); 23 | 24 | /** 25 | * 随机电影推荐,从100部电影中随机获取num部电影推荐给用户 26 | * @param uid 用户id 27 | * @param num 获取随机电影数量 28 | * @return 推荐结果 29 | */ 30 | List randomRecommended(Long uid, Integer num); 31 | 32 | /** 33 | * 通过用户id获取数据库中的电影推荐列表 34 | * @param uid 用户id 35 | * @return 电影推荐列表 36 | */ 37 | List getRecommendedMoviesByUserId(Long uid); 38 | 39 | /** 40 | * 获取用户id为uid的用户,看过的电影和推荐电影之间的联系,用于绘制Echarts关系图 41 | * @param uid 用户id 42 | * @return 看过的电影和推荐电影之间的联系 43 | */ 44 | Map> getLinksBetweenWatchedMoviesAndRecommendedMovies(Long uid); 45 | 46 | /** 47 | * 更新指定用户推荐结果 48 | * @param uid 用户id 49 | */ 50 | void updateRecommendation(Long uid); 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/service/RegionLikeService.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.service; 2 | 3 | import cn.zjw.mrs.entity.RegionLike; 4 | import com.baomidou.mybatisplus.extension.service.IService; 5 | 6 | /** 7 | * @author zjw 8 | * @description 针对表【region_like】的数据库操作Service 9 | * @createDate 2022-04-20 16:13:15 10 | */ 11 | public interface RegionLikeService extends IService { 12 | /** 13 | * 更新用户电影地区喜好 14 | * @param id 用户id 15 | * @param regions 用户的电影地区喜好 16 | * @return 更新结果 17 | */ 18 | int updateUserRegionLike(Long id, int[] regions); 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/service/SameLikesService.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.service; 2 | 3 | import cn.zjw.mrs.entity.SameLikes; 4 | import com.baomidou.mybatisplus.extension.service.IService; 5 | 6 | /** 7 | * @author 95758 8 | * @description 针对表【same_likes】的数据库操作Service 9 | * @createDate 2022-04-23 17:44:19 10 | */ 11 | public interface SameLikesService extends IService { 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/service/TypeLikeService.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.service; 2 | 3 | import cn.zjw.mrs.entity.TypeLike; 4 | import com.baomidou.mybatisplus.extension.service.IService; 5 | 6 | /** 7 | * @author zjw 8 | * @description 针对表【type_like】的数据库操作Service 9 | * @createDate 2022-04-20 16:13:21 10 | */ 11 | public interface TypeLikeService extends IService { 12 | /** 13 | * 更新用户电影类型喜好 14 | * @param id 用户id 15 | * @param types 用户的电影类型喜好 16 | * @return 更新结果 17 | */ 18 | int updateUserTypeLike(Long id, int[] types); 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/service/UserLikeRedisService.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.service; 2 | 3 | import cn.zjw.mrs.entity.Comment; 4 | import cn.zjw.mrs.entity.UserLike; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * @author zjw 10 | * @Classname RedisService 11 | * @Date 2022/5/22 16:33 12 | * @Description 13 | */ 14 | public interface UserLikeRedisService { 15 | 16 | /** 17 | * 获取用户(uid)是否点赞过评论(cid) 18 | * @param cid 评论id 19 | * @param uid 用户id 20 | * @return 点赞状态(1表示已点赞,0表示未点赞,redis中不存在则返回-1) 21 | */ 22 | int getUserLikeStatusFromRedis(long cid, long uid); 23 | 24 | /** 25 | * 点赞。状态为1 26 | * @param cid 被点赞的评论id 27 | * @param uid 点赞的用户id 28 | */ 29 | void likeComment(long cid, long uid); 30 | 31 | /** 32 | * 取消点赞。将状态改变为0 33 | * @param cid 被点赞的评论id 34 | * @param uid 点赞的用户id 35 | */ 36 | void unlikeComment(long cid, long uid); 37 | 38 | /** 39 | * 从Redis中删除一条点赞数据 40 | * @param cid 被点赞的评论id 41 | * @param uid 点赞的用户id 42 | */ 43 | void deleteLikedFromRedis(long cid, long uid); 44 | 45 | /** 46 | * 获取redis中的某条评论的点赞数 47 | * @param cid 评论id 48 | * @return 点赞数 49 | */ 50 | int getUserLikeCountFromRedis(long cid); 51 | 52 | /** 53 | * 该评论的点赞数加1 54 | * @param cid 被点赞的评论id 55 | */ 56 | void increaseLikedCount(long cid); 57 | 58 | /** 59 | * 该用户的点赞数减1 60 | * @param cid 被点赞的评论id 61 | */ 62 | void decreaseLikedCount(long cid); 63 | 64 | /** 65 | * 获取Redis中存储的所有点赞数据 66 | * @return Redis中存储的所有点赞数据 67 | */ 68 | List getAllLikedDataFromRedis(); 69 | 70 | /** 71 | * 获取Redis中存储的所有点赞数量 72 | * @return Redis中存储的所有点赞数量 73 | */ 74 | List getAllLikedCountFromRedis(); 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/service/UserLikeService.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.service; 2 | 3 | import cn.zjw.mrs.entity.UserLike; 4 | import cn.zjw.mrs.vo.comment.CommentStripVo; 5 | import com.baomidou.mybatisplus.extension.service.IService; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * @author 95758 11 | * @description 针对表【user_like】的数据库操作Service 12 | * @createDate 2022-05-22 16:43:36 13 | */ 14 | public interface UserLikeService extends IService { 15 | /** 16 | * 将Redis里的点赞数据存入数据库中 17 | */ 18 | void transLikedFromRedis2Database(); 19 | 20 | /** 21 | * 将Redis中的点赞数量数据存入数据库 22 | */ 23 | void transLikedCountFromRedis2Database(); 24 | 25 | /** 26 | * 判断用户(uid)是否点赞过评论(cid) 27 | * 先查redis记录,如果为空(即-1),则继续查数据库,如果还是没有记录,则返回0;否则返回1 28 | * @param cid 评论id 29 | * @param uid 用户id 30 | * @return 点赞状态(1表示已点赞,0表示未点赞) 31 | */ 32 | int getUserLikeStatus(long cid, long uid); 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/service/UserService.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.service; 2 | 3 | import cn.zjw.mrs.entity.Result; 4 | import cn.zjw.mrs.entity.User; 5 | import cn.zjw.mrs.vo.user.UserInfoVo; 6 | import com.baomidou.mybatisplus.extension.service.IService; 7 | 8 | import java.util.List; 9 | import java.util.Map; 10 | 11 | /** 12 | * @author zjw 13 | * @description 针对表【user】的数据库操作Service 14 | * @createDate 2022-04-10 22:46:38 15 | */ 16 | public interface UserService extends IService { 17 | 18 | /** 19 | * 用户登录 20 | * @param user 用户登录表单 21 | * @return 登录结果 22 | */ 23 | Result login(User user); 24 | 25 | /** 26 | * 用户登出 27 | * @return 登出结果 28 | */ 29 | Result logout(); 30 | 31 | /** 32 | * 用户注册 33 | * @param user 注册表单信息 34 | * @return 注册结果 35 | */ 36 | Result register(User user); 37 | 38 | /** 39 | * 获取用户基本信息 40 | * @param id 用户id 41 | * @return 用户基本信息 42 | */ 43 | UserInfoVo getUserInfo(Long id); 44 | 45 | /** 46 | * 获取用户的类型喜好和地区喜好 47 | * @param id 用户id 48 | * @return 用户的类型喜好和地区喜好 49 | */ 50 | Map> getTypesAndRegions(Long id); 51 | 52 | /** 53 | * 更新用户昵称 54 | * 若用户昵称更新成功,同时更新评论表中该用户的昵称,返回成功信息 55 | * 否则,返回失败信息 56 | * @param nickname 新用户昵称 57 | * @return 更新成功或失败信息 58 | */ 59 | int updateNickname(String nickname); 60 | 61 | /** 62 | * 更新用户性别 63 | * @param sexName 修改后的性别 64 | * @param username 用户账号,唯一标识 65 | * @return 更新成功或失败信息 66 | */ 67 | int updateSex(String sexName, String username); 68 | 69 | /** 70 | * 更新密码 71 | * @param username 用户名 72 | * @param prePassword 原密码 73 | * @param newPassword 新密码 74 | * @param isFindPassword 是否为找回密码,如果是找回密码则跳过原密码检测 75 | * @return 更新结果(-1表示用户输入的原密码不正确,0表示修改失败,1表示修改成功) 76 | */ 77 | int updatePassword(String username, String prePassword, String newPassword, boolean isFindPassword); 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/service/impl/AuthServiceImpl.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.service.impl; 2 | 3 | import cn.hutool.captcha.generator.RandomGenerator; 4 | import cn.zjw.mrs.entity.Result; 5 | import cn.zjw.mrs.entity.User; 6 | import cn.zjw.mrs.mapper.UserMapper; 7 | import cn.zjw.mrs.service.AuthService; 8 | import cn.zjw.mrs.service.UserService; 9 | import cn.zjw.mrs.utils.MailUtil; 10 | import cn.zjw.mrs.utils.RedisCache; 11 | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; 12 | import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; 13 | import org.slf4j.Logger; 14 | import org.slf4j.LoggerFactory; 15 | import org.springframework.stereotype.Service; 16 | import org.springframework.web.bind.annotation.GetMapping; 17 | import org.springframework.web.bind.annotation.RequestParam; 18 | 19 | import javax.annotation.Resource; 20 | import java.util.Objects; 21 | import java.util.concurrent.TimeUnit; 22 | 23 | /** 24 | * @author zjw 25 | * @Classname MailServiceImpl 26 | * @Date 2022/5/10 18:09 27 | * @Description 28 | */ 29 | @Service 30 | public class AuthServiceImpl implements AuthService { 31 | private final Logger logger = LoggerFactory.getLogger(this.getClass()); 32 | 33 | @Resource 34 | private RedisCache redisCache; 35 | 36 | @Resource 37 | private MailUtil mailUtil; 38 | 39 | @Resource 40 | private UserMapper userMapper; 41 | 42 | @Override 43 | public void sendMailAuthCode(String username, String mail) { 44 | // 1.自定义纯数字的验证码(随机6位数字,可重复) 45 | RandomGenerator randomGenerator = new RandomGenerator("0123456789", 6); 46 | String authCode = randomGenerator.generate(); 47 | 48 | // 2.将验证码暂存入redis中,并且设置5分钟内有效 49 | String redisKey = "authCode:" + username; 50 | redisCache.setCacheObject(redisKey, authCode); 51 | redisCache.expire(redisKey, 5, TimeUnit.MINUTES); 52 | 53 | // 3.发送邮件给相应用户 54 | String subject = "【电影推荐系统】验证码" + authCode; 55 | String content = "尊敬的用户,您好:
" 56 | + "  本次请求的邮件验证码为: " 57 | + authCode + ",本验证码 5 分钟内有效,请及时输入。(请勿泄露此验证码)
" 58 | + "  如非本人操作,请忽略该邮件。
  (这是一封通过自动发送的邮件,请不要直接回复)"; 59 | mailUtil.sendHtmlMail(mail, subject, content); 60 | } 61 | 62 | @Override 63 | public int judgeAuthCode(String username, String authCode) { 64 | String redisKey = "authCode:" + username; 65 | String realAuthCode = redisCache.getCacheObject(redisKey); 66 | if (realAuthCode == null) { 67 | return -1; 68 | } else if (authCode.equals(realAuthCode)) { 69 | return 1; 70 | } else { 71 | return 0; 72 | } 73 | } 74 | 75 | @Override 76 | public boolean judgeMailBelongToUser(String username, String mail) { 77 | User user = userMapper.selectOne(new LambdaUpdateWrapper().eq(User::getUsername, username)); 78 | // 如果user不为空,user中的mail不为空,且user中的mail和传入mail相等,返回true 79 | return !Objects.isNull(user) && user.getMail() != null && user.getMail().equals(mail); 80 | } 81 | 82 | @Override 83 | public boolean judgeUsernameExists(String username) { 84 | User user = userMapper.selectOne(new LambdaQueryWrapper().eq(User::getUsername, username)); 85 | return !Objects.isNull(user); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/service/impl/MovieFeatureServiceImpl.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.service.impl; 2 | 3 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 4 | import cn.zjw.mrs.entity.MovieFeature; 5 | import cn.zjw.mrs.service.MovieFeatureService; 6 | import cn.zjw.mrs.mapper.MovieFeatureMapper; 7 | import org.springframework.stereotype.Service; 8 | 9 | /** 10 | * @author zjw 11 | * @description 针对表【movie_feature】的数据库操作Service实现 12 | * @createDate 2022-04-22 10:29:18 13 | */ 14 | @Service 15 | public class MovieFeatureServiceImpl extends ServiceImpl 16 | implements MovieFeatureService{ 17 | 18 | } 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/service/impl/MovieRegionServiceImpl.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.service.impl; 2 | 3 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 4 | import cn.zjw.mrs.entity.MovieRegion; 5 | import cn.zjw.mrs.service.MovieRegionService; 6 | import cn.zjw.mrs.mapper.MovieRegionMapper; 7 | import org.springframework.stereotype.Service; 8 | 9 | /** 10 | * @author zjw 11 | * @description 针对表【movie_region】的数据库操作Service实现 12 | * @createDate 2022-04-21 22:58:21 13 | */ 14 | @Service 15 | public class MovieRegionServiceImpl extends ServiceImpl 16 | implements MovieRegionService{ 17 | 18 | } 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/service/impl/MovieServiceImpl.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.service.impl; 2 | 3 | import cn.zjw.mrs.utils.PicUrlUtil; 4 | import cn.zjw.mrs.vo.movie.MovieCardVo; 5 | import cn.zjw.mrs.vo.movie.ReviewedMovieStripVo; 6 | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; 7 | import com.baomidou.mybatisplus.core.toolkit.StringUtils; 8 | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; 9 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 10 | import cn.zjw.mrs.entity.Movie; 11 | import cn.zjw.mrs.service.MovieService; 12 | import cn.zjw.mrs.mapper.MovieMapper; 13 | import org.springframework.stereotype.Service; 14 | 15 | import javax.annotation.Resource; 16 | import java.awt.event.MouseEvent; 17 | import java.util.ArrayList; 18 | import java.util.HashMap; 19 | import java.util.List; 20 | import java.util.Map; 21 | 22 | /** 23 | * @author zjw 24 | * @description 针对表【movie】的数据库操作Service实现 25 | * @createDate 2022-04-07 19:01:20 26 | */ 27 | @Service 28 | public class MovieServiceImpl extends ServiceImpl 29 | implements MovieService{ 30 | private static final String TOTAL = "全部"; 31 | 32 | @Resource 33 | MovieMapper movieMapper; 34 | 35 | @Override 36 | public Page getPageMovies(Integer currentPage, Integer pageSize, String type, String region, String search) { 37 | Page page = new Page<>(currentPage, pageSize); 38 | LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>(); 39 | // select用于选取电影中的某几个字段 40 | lambdaQueryWrapper.select(Movie::getId, Movie::getDid, Movie::getName, Movie::getDirectors, Movie::getActors, 41 | Movie::getTypes, Movie::getRegions, Movie::getScore, Movie::getPic) 42 | // 模糊匹配电影类型 43 | .like(!type.equals(TOTAL), Movie::getTypes, type) 44 | // 模糊匹配电影地区 45 | .like(!region.equals(TOTAL), Movie::getRegions, region) 46 | // 模糊匹配电影名 47 | .like(StringUtils.isNotEmpty(search), Movie::getName, search); 48 | movieMapper.selectPage(page, lambdaQueryWrapper); 49 | 50 | List movies = page.getRecords(); 51 | for (Movie movie: movies) { 52 | movie.setPic(PicUrlUtil.getFullMoviePicUrl(movie.getPic())); 53 | } 54 | return page; 55 | } 56 | 57 | @Override 58 | public List getRecommendedMoviesByMovieId(Long did) { 59 | List movies = movieMapper.selectRecommendedMoviesByMovieId(did); 60 | for (MovieCardVo movie: movies) { 61 | movie.setPic(PicUrlUtil.getFullMoviePicUrl(movie.getPic())); 62 | } 63 | return movies; 64 | } 65 | 66 | @Override 67 | public Map getMoreReviewedMoviesByUserId(Long uid, Integer currentPage, Integer pageSize) { 68 | Integer currentIndex = (currentPage - 1) * pageSize; 69 | List reviewedMovies = movieMapper.selectMoreReviewedMoviesByUserId(uid, currentIndex, pageSize); 70 | for (ReviewedMovieStripVo movie: reviewedMovies) { 71 | movie.setPic(PicUrlUtil.getFullMoviePicUrl(movie.getPic())); 72 | } 73 | 74 | // 获取用户评价过的电影总条目数 75 | List totalMovies = movieMapper.selectMoreReviewedMoviesByUserId(uid, 0, 10000); 76 | Integer total = totalMovies.size(); 77 | 78 | Map page = new HashMap<>(2); 79 | page.put("records", reviewedMovies); 80 | page.put("total", total); 81 | return page; 82 | } 83 | 84 | @Override 85 | public List getMatchMovieName(String keywords) { 86 | List movies = movieMapper.selectList(new LambdaQueryWrapper().like(Movie::getName, keywords)); 87 | int len = Math.min(movies.size(), 10); 88 | List res = new ArrayList<>(); 89 | for (int i = 0; i < len; i++) { 90 | res.add(movies.get(i).getName()); 91 | } 92 | return res; 93 | } 94 | 95 | @Override 96 | public List getMostWatchedMovies() { 97 | List movies = movieMapper.selectMostWatchedMovies(10); 98 | for (MovieCardVo movie: movies) { 99 | movie.setPic(PicUrlUtil.getFullMoviePicUrl(movie.getPic())); 100 | } 101 | return movies; 102 | } 103 | 104 | @Override 105 | public List getHighestRatedMovies() { 106 | List movies = movieMapper.selectHighestRatedMovies(10); 107 | for (MovieCardVo movie: movies) { 108 | movie.setPic(PicUrlUtil.getFullMoviePicUrl(movie.getPic())); 109 | } 110 | return movies; 111 | } 112 | } 113 | 114 | 115 | 116 | 117 | -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/service/impl/MovieTypeServiceImpl.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.service.impl; 2 | 3 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 4 | import cn.zjw.mrs.entity.MovieType; 5 | import cn.zjw.mrs.service.MovieTypeService; 6 | import cn.zjw.mrs.mapper.MovieTypeMapper; 7 | import org.springframework.stereotype.Service; 8 | 9 | /** 10 | * @author zjw 11 | * @description 针对表【movie_type】的数据库操作Service实现 12 | * @createDate 2022-04-21 22:58:26 13 | */ 14 | @Service 15 | public class MovieTypeServiceImpl extends ServiceImpl 16 | implements MovieTypeService{ 17 | 18 | } 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/service/impl/OssServiceImpl.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.service.impl; 2 | 3 | import cn.hutool.core.date.DateUtil; 4 | import cn.hutool.core.lang.UUID; 5 | import cn.hutool.core.util.IdUtil; 6 | import cn.zjw.mrs.entity.Result; 7 | import cn.zjw.mrs.entity.User; 8 | import cn.zjw.mrs.mapper.UserMapper; 9 | import cn.zjw.mrs.service.OssService; 10 | import com.aliyun.oss.ClientException; 11 | import com.aliyun.oss.OSSClient; 12 | import com.aliyun.oss.OSSException; 13 | import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; 14 | import org.springframework.beans.factory.annotation.Autowired; 15 | import org.springframework.beans.factory.annotation.Value; 16 | import org.springframework.stereotype.Service; 17 | import org.springframework.web.multipart.MultipartFile; 18 | 19 | import javax.annotation.Resource; 20 | import javax.management.ObjectName; 21 | import java.io.ByteArrayInputStream; 22 | import java.io.IOException; 23 | 24 | /** 25 | * @author zjw 26 | * @Classname OssServiceImpl 27 | * @Date 2022/4/17 13:51 28 | * @Description 29 | */ 30 | @Service 31 | public class OssServiceImpl implements OssService { 32 | @Value("${aliyun.oss.maxSize}") 33 | private int maxSize; 34 | 35 | @Value("${aliyun.oss.bucketName}") 36 | private String bucketName; 37 | 38 | @Value("${aliyun.oss.dir.prefix}") 39 | private String dirPrefix; 40 | 41 | @Resource 42 | private OSSClient ossClient; 43 | 44 | @Resource 45 | private UserMapper userMapper; 46 | 47 | private String getAvatarObjectName() { 48 | return DateUtil.today() + '/' + IdUtil.simpleUUID() + ".webp"; 49 | } 50 | 51 | private String formatPath(String objectName){ 52 | return "https://" + bucketName + "." + ossClient.getEndpoint().getHost() + "/" + objectName; 53 | } 54 | 55 | @Override 56 | public boolean updateAvatar(String username, MultipartFile uploadFile) { 57 | String dirSuffix = getAvatarObjectName(); 58 | String objectName = dirPrefix + dirSuffix; 59 | try { 60 | // 上传头像到oss 61 | ossClient.putObject(bucketName, objectName, uploadFile.getInputStream()); 62 | } catch (OSSException oe) { 63 | System.out.println("Caught an OSSException, which means your request made it to OSS, " 64 | + "but was rejected with an error response for some reason."); 65 | System.out.println("Error Message:" + oe.getErrorMessage()); 66 | System.out.println("Error Code:" + oe.getErrorCode()); 67 | System.out.println("Request ID:" + oe.getRequestId()); 68 | System.out.println("Host ID:" + oe.getHostId()); 69 | return false; 70 | } catch (ClientException ce) { 71 | System.out.println("Caught an ClientException, which means the client encountered " 72 | + "a serious internal problem while trying to communicate with OSS, " 73 | + "such as not being able to access the network."); 74 | System.out.println("Error Message:" + ce.getMessage()); 75 | return false; 76 | } catch (IOException e) { 77 | e.printStackTrace(); 78 | return false; 79 | } 80 | 81 | // 头像上传到oss后,将路径存入数据库中 82 | userMapper.update(null, new LambdaUpdateWrapper() 83 | .set(User::getAvatar, dirSuffix).eq(User::getUsername, username)); 84 | return true; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/service/impl/RegionLikeServiceImpl.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.service.impl; 2 | 3 | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; 4 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 5 | import cn.zjw.mrs.entity.RegionLike; 6 | import cn.zjw.mrs.service.RegionLikeService; 7 | import cn.zjw.mrs.mapper.RegionLikeMapper; 8 | import org.springframework.stereotype.Service; 9 | 10 | import javax.annotation.Resource; 11 | 12 | /** 13 | * @author zjw 14 | * @description 针对表【region_like】的数据库操作Service实现 15 | * @createDate 2022-04-20 16:13:15 16 | */ 17 | @Service 18 | public class RegionLikeServiceImpl extends ServiceImpl 19 | implements RegionLikeService{ 20 | 21 | @Resource 22 | private RegionLikeMapper regionLikeMapper; 23 | 24 | @Override 25 | public int updateUserRegionLike(Long id, int[] regions) { 26 | int len = Math.min(regions.length, 5); 27 | regionLikeMapper.delete(new LambdaQueryWrapper().eq(RegionLike::getUid, id)); 28 | int cnt = 0; 29 | for (int i = 0; i < len; ++ i) { 30 | cnt += regionLikeMapper.insert(new RegionLike(id, regions[i], i)); 31 | } 32 | return cnt; 33 | } 34 | } 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/service/impl/SameLikesServiceImpl.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.service.impl; 2 | 3 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 4 | import cn.zjw.mrs.entity.SameLikes; 5 | import cn.zjw.mrs.service.SameLikesService; 6 | import cn.zjw.mrs.mapper.SameLikesMapper; 7 | import org.springframework.stereotype.Service; 8 | 9 | /** 10 | * @author zjw 11 | * @description 针对表【same_likes】的数据库操作Service实现 12 | * @createDate 2022-04-23 17:44:19 13 | */ 14 | @Service 15 | public class SameLikesServiceImpl extends ServiceImpl 16 | implements SameLikesService{ 17 | 18 | } 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/service/impl/TypeLikeServiceImpl.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.service.impl; 2 | 3 | import cn.zjw.mrs.entity.RegionLike; 4 | import cn.zjw.mrs.mapper.RegionLikeMapper; 5 | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; 6 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 7 | import cn.zjw.mrs.entity.TypeLike; 8 | import cn.zjw.mrs.service.TypeLikeService; 9 | import cn.zjw.mrs.mapper.TypeLikeMapper; 10 | import org.springframework.stereotype.Service; 11 | 12 | import javax.annotation.Resource; 13 | 14 | /** 15 | * @author zjw 16 | * @description 针对表【type_like】的数据库操作Service实现 17 | * @createDate 2022-04-20 16:13:21 18 | */ 19 | @Service 20 | public class TypeLikeServiceImpl extends ServiceImpl 21 | implements TypeLikeService{ 22 | 23 | @Resource 24 | private TypeLikeMapper typeLikeMapper; 25 | 26 | @Override 27 | public int updateUserTypeLike(Long id, int[] types) { 28 | int len = Math.min(types.length, 5); 29 | typeLikeMapper.delete(new LambdaQueryWrapper().eq(TypeLike::getUid, id)); 30 | int cnt = 0; 31 | for (int i = 0; i < len; ++ i) { 32 | cnt += typeLikeMapper.insert(new TypeLike(id, types[i], i)); 33 | } 34 | return cnt; 35 | } 36 | } 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/service/impl/UserDetailsServiceImpl.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.service.impl; 2 | 3 | import cn.zjw.mrs.entity.LoginUser; 4 | import cn.zjw.mrs.entity.User; 5 | import cn.zjw.mrs.mapper.UserMapper; 6 | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | 9 | import org.springframework.security.core.userdetails.UserDetails; 10 | import org.springframework.security.core.userdetails.UserDetailsService; 11 | import org.springframework.security.core.userdetails.UsernameNotFoundException; 12 | import org.springframework.stereotype.Service; 13 | 14 | import javax.annotation.Resource; 15 | import java.util.ArrayList; 16 | import java.util.Arrays; 17 | import java.util.List; 18 | import java.util.Objects; 19 | 20 | /** 21 | * @author zjw 22 | * @Classname UserDetailsServiceImpl 23 | * @Date 2022/4/11 16:15 24 | * @Description 25 | */ 26 | @Service 27 | public class UserDetailsServiceImpl implements UserDetailsService { 28 | @Resource 29 | UserMapper userMapper; 30 | 31 | @Override 32 | public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { 33 | // 查询用户信息 34 | LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); 35 | queryWrapper.eq(User::getUsername, username); 36 | User user = userMapper.selectOne(queryWrapper); 37 | // 如果查询不到用户,则抛出异常提示 38 | if (Objects.isNull(user)) { 39 | throw new RuntimeException("用户名或密码错误"); 40 | } 41 | 42 | // 查询对应的权限信息 43 | List list = new ArrayList<>(Arrays.asList("admin", "tourist")); 44 | 45 | // 把数据封装成UserDetails返回 46 | return new LoginUser(user, list); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/service/impl/UserLikeRedisServiceImpl.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.service.impl; 2 | 3 | import cn.zjw.mrs.entity.Comment; 4 | import cn.zjw.mrs.entity.UserLike; 5 | import cn.zjw.mrs.enums.LikedStatusEnum; 6 | import cn.zjw.mrs.service.UserLikeRedisService; 7 | import cn.zjw.mrs.utils.RedisCache; 8 | import cn.zjw.mrs.utils.RedisKeyUtil; 9 | import org.springframework.data.redis.core.Cursor; 10 | import org.springframework.stereotype.Service; 11 | 12 | import javax.annotation.Resource; 13 | import java.util.ArrayList; 14 | import java.util.List; 15 | import java.util.Map; 16 | import java.util.Objects; 17 | 18 | /** 19 | * @author zjw 20 | * @Classname RedisServiceImpl 21 | * @Date 2022/5/22 16:44 22 | * @Description 23 | */ 24 | @Service 25 | public class UserLikeRedisServiceImpl implements UserLikeRedisService { 26 | @Resource 27 | private RedisCache redisCache; 28 | 29 | @Override 30 | public int getUserLikeStatusFromRedis(long cid, long uid) { 31 | String key = RedisKeyUtil.getLikedKey(cid, uid); 32 | Integer status = redisCache.getCacheMapValue(RedisKeyUtil.MAP_KEY_USER_LIKED, key); 33 | if (!Objects.isNull(status)) { 34 | return status; 35 | } else { 36 | return -1; 37 | } 38 | } 39 | 40 | @Override 41 | public void likeComment(long cid, long uid) { 42 | String key = RedisKeyUtil.getLikedKey(cid, uid); 43 | redisCache.setCacheMapValue(RedisKeyUtil.MAP_KEY_USER_LIKED, key, LikedStatusEnum.LIKE.getCode()); 44 | } 45 | 46 | @Override 47 | public void unlikeComment(long cid, long uid) { 48 | String key = RedisKeyUtil.getLikedKey(cid, uid); 49 | redisCache.setCacheMapValue(RedisKeyUtil.MAP_KEY_USER_LIKED, key, LikedStatusEnum.UNLIKE.getCode()); 50 | } 51 | 52 | @Override 53 | public void deleteLikedFromRedis(long cid, long uid) { 54 | String key = RedisKeyUtil.getLikedKey(cid, uid); 55 | redisCache.delCacheMapValue(RedisKeyUtil.MAP_KEY_USER_LIKED, key); 56 | } 57 | 58 | @Override 59 | public int getUserLikeCountFromRedis(long cid) { 60 | Integer count = redisCache.getCacheMapValue(RedisKeyUtil.MAP_KEY_USER_LIKED_COUNT, String.valueOf(cid)); 61 | if (!Objects.isNull(count)) { 62 | return count; 63 | } 64 | return 0; 65 | } 66 | 67 | @Override 68 | public void increaseLikedCount(long cid) { 69 | redisCache.increaseCacheMapValue(RedisKeyUtil.MAP_KEY_USER_LIKED_COUNT, String.valueOf(cid), 1); 70 | } 71 | 72 | @Override 73 | public void decreaseLikedCount(long cid) { 74 | redisCache.increaseCacheMapValue(RedisKeyUtil.MAP_KEY_USER_LIKED_COUNT, String.valueOf(cid), -1); 75 | } 76 | 77 | @Override 78 | public List getAllLikedDataFromRedis() { 79 | Cursor> cursor = redisCache.getAllCacheMapValueCursor(RedisKeyUtil.MAP_KEY_USER_LIKED); 80 | List res = new ArrayList<>(); 81 | while (cursor.hasNext()) { 82 | Map.Entry next = cursor.next(); 83 | String key = (String) next.getKey(); 84 | // 分离出cid 和 uid 85 | String[] split = key.split("::"); 86 | Long cid = Long.valueOf(split[0]); 87 | Long uid = Long.valueOf(split[1]); 88 | Integer value = (Integer) next.getValue(); 89 | 90 | // 组装UserLike对象,并放入res中 91 | UserLike userLike = new UserLike(cid, uid, value); 92 | res.add(userLike); 93 | 94 | // 存到res后,从Redis中删除 95 | redisCache.delCacheMapValue(RedisKeyUtil.MAP_KEY_USER_LIKED, key); 96 | } 97 | return res; 98 | } 99 | 100 | @Override 101 | public List getAllLikedCountFromRedis() { 102 | Cursor> cursor = 103 | redisCache.getAllCacheMapValueCursor(RedisKeyUtil.MAP_KEY_USER_LIKED_COUNT); 104 | List res = new ArrayList<>(); 105 | while (cursor.hasNext()) { 106 | Map.Entry next = cursor.next(); 107 | String key = (String) next.getKey(); 108 | 109 | // 组装Comment对象,并放入res中 110 | Comment comment = new Comment(); 111 | comment.setId(Long.valueOf(key)); 112 | comment.setAgree((Integer) next.getValue()); 113 | res.add(comment); 114 | 115 | // 从redis中删除这条记录 116 | redisCache.delCacheMapValue(RedisKeyUtil.MAP_KEY_USER_LIKED_COUNT, key); 117 | } 118 | return res; 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/service/impl/UserLikeServiceImpl.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.service.impl; 2 | 3 | import cn.zjw.mrs.entity.Comment; 4 | import cn.zjw.mrs.mapper.CommentMapper; 5 | import cn.zjw.mrs.service.UserLikeRedisService; 6 | import cn.zjw.mrs.utils.RedisCache; 7 | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; 8 | import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; 9 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 10 | import cn.zjw.mrs.entity.UserLike; 11 | import cn.zjw.mrs.service.UserLikeService; 12 | import cn.zjw.mrs.mapper.UserLikeMapper; 13 | import org.springframework.scheduling.annotation.Async; 14 | import org.springframework.scheduling.annotation.Scheduled; 15 | import org.springframework.stereotype.Service; 16 | 17 | import javax.annotation.Resource; 18 | import java.util.List; 19 | import java.util.Objects; 20 | 21 | /** 22 | * @author 95758 23 | * @description 针对表【user_like】的数据库操作Service实现 24 | * @createDate 2022-05-22 16:43:36 25 | */ 26 | @Service 27 | public class UserLikeServiceImpl extends ServiceImpl 28 | implements UserLikeService{ 29 | 30 | @Resource 31 | private RedisCache redisCache; 32 | 33 | @Resource 34 | private UserLikeRedisService userLikeRedisService; 35 | 36 | @Resource 37 | private UserLikeMapper userLikeMapper; 38 | 39 | @Resource 40 | private CommentMapper commentMapper; 41 | 42 | @Override 43 | @Async("asyncServiceExecutor") 44 | @Scheduled(fixedRate = 1000 * 60 * 60) 45 | public void transLikedFromRedis2Database() { 46 | List list = userLikeRedisService.getAllLikedDataFromRedis(); 47 | for (UserLike like : list) { 48 | UserLike userLike = userLikeMapper.selectOne(new LambdaUpdateWrapper() 49 | .eq(UserLike::getCid, like.getCid()) 50 | .eq(UserLike::getUid, like.getUid())); 51 | if (userLike == null){ 52 | // 没有记录,直接存入 53 | userLikeMapper.insert(like); 54 | } else { 55 | // 有记录,需要更新 56 | userLikeMapper.update(null, new LambdaUpdateWrapper() 57 | .eq(UserLike::getCid, like.getCid()) 58 | .eq(UserLike::getUid, like.getUid()) 59 | .set(UserLike::getStatus, like.getStatus())); 60 | } 61 | } 62 | } 63 | 64 | @Override 65 | @Async("asyncServiceExecutor") 66 | @Scheduled(fixedRate = 1000 * 60 * 60) 67 | public void transLikedCountFromRedis2Database() { 68 | List list = userLikeRedisService.getAllLikedCountFromRedis(); 69 | for (Comment comment : list) { 70 | Comment pre = commentMapper.selectOne(new LambdaQueryWrapper().eq(Comment::getId, comment.getId())); 71 | Integer preAgree = pre.getAgree(); 72 | // 评论总点赞数 = 数据库中原来的点赞数 + redis评论点赞数 73 | Integer totalAgree = preAgree + comment.getAgree(); 74 | // 更新评论点赞数 75 | commentMapper.update(null, new LambdaUpdateWrapper() 76 | .eq(Comment::getId, comment.getId()) 77 | .set(Comment::getAgree, totalAgree)); 78 | } 79 | } 80 | 81 | @Override 82 | public int getUserLikeStatus(long cid, long uid) { 83 | int redisStatus = userLikeRedisService.getUserLikeStatusFromRedis(cid, uid); 84 | // 如果查询redis,返回-1,表示无记录,则取数据库点赞表中查询 85 | if (redisStatus == -1) { 86 | UserLike userLike = userLikeMapper.selectOne(new LambdaQueryWrapper() 87 | .eq(UserLike::getCid, cid) 88 | .eq(UserLike::getUid, uid)); 89 | // 如果点赞表中不存在记录,则说明未点赞,返回0 90 | if (Objects.isNull(userLike)) { 91 | return 0; 92 | } 93 | // 记录存在,则直接返回点赞状态值即可 94 | return userLike.getStatus(); 95 | } 96 | return redisStatus; 97 | } 98 | } 99 | 100 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/service/impl/UserServiceImpl.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.service.impl; 2 | 3 | import cn.zjw.mrs.entity.*; 4 | import cn.zjw.mrs.enums.SexEnum; 5 | import cn.zjw.mrs.mapper.CommentMapper; 6 | import cn.zjw.mrs.mapper.UserMapper; 7 | import cn.zjw.mrs.service.RecommendationService; 8 | import cn.zjw.mrs.service.UserService; 9 | import cn.zjw.mrs.utils.JwtUtil; 10 | import cn.zjw.mrs.utils.PicUrlUtil; 11 | import cn.zjw.mrs.utils.RedisCache; 12 | import cn.zjw.mrs.vo.user.LoginUserVo; 13 | import cn.zjw.mrs.vo.user.UserInfoVo; 14 | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; 15 | import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; 16 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 17 | import org.springframework.security.authentication.AuthenticationManager; 18 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; 19 | import org.springframework.security.core.Authentication; 20 | import org.springframework.security.core.context.SecurityContextHolder; 21 | import org.springframework.security.crypto.password.PasswordEncoder; 22 | import org.springframework.stereotype.Service; 23 | 24 | import javax.annotation.Resource; 25 | import java.util.HashMap; 26 | import java.util.List; 27 | import java.util.Map; 28 | import java.util.Objects; 29 | 30 | /** 31 | * @author zjw 32 | * @description 针对表【user】的数据库操作Service实现 33 | * @createDate 2022-04-10 22:46:38 34 | */ 35 | @Service 36 | public class UserServiceImpl extends ServiceImpl 37 | implements UserService{ 38 | 39 | @Resource 40 | private AuthenticationManager authenticationManager; 41 | 42 | @Resource 43 | private RedisCache redisCache; 44 | 45 | @Resource 46 | private UserMapper userMapper; 47 | 48 | @Resource 49 | private CommentMapper commentMapper; 50 | 51 | @Resource 52 | private RecommendationService recommendationService; 53 | 54 | @Resource 55 | private PasswordEncoder passwordEncoder; 56 | 57 | @Override 58 | public Result login(User user) { 59 | // AuthenticationManager authenticate进行用户认证 60 | UsernamePasswordAuthenticationToken authenticationToken = 61 | new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword()); 62 | Authentication authentication = authenticationManager.authenticate(authenticationToken); 63 | // 如果认证没有通过,给出相应的提示 64 | if (Objects.isNull(authentication)) { 65 | throw new RuntimeException("登录失败"); 66 | } 67 | // 如果认证通过了,使用userid生成一个jwt,jwt存入Result返回 68 | LoginUser loginUser = (LoginUser) authentication.getPrincipal(); 69 | String userId = loginUser.getUser().getId().toString(); 70 | String jwt = JwtUtil.createJwt(userId); 71 | 72 | // 把完整的用户信息存入redis,userid作为可以 73 | redisCache.setCacheObject("login:" + userId, loginUser); 74 | 75 | // 把User转化成UserInfoVo 76 | UserInfoVo userInfoVo = new UserInfoVo(); 77 | userInfoVo.setId(loginUser.getUser().getId()); 78 | userInfoVo.setNickname(loginUser.getUser().getNickname()); 79 | userInfoVo.setSex(loginUser.getUser().getSex().getSexName()); 80 | userInfoVo.setAvatar(loginUser.getUser().getAvatar()); 81 | // 把token和userInfo封装 返回给前端 82 | LoginUserVo vo = new LoginUserVo(jwt, userInfoVo); 83 | 84 | return Result.success("登录成功", vo); 85 | } 86 | 87 | @Override 88 | public Result logout() { 89 | // 获取SecurityHolder中的用户id 90 | Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); 91 | LoginUser loginUser = (LoginUser) authentication.getPrincipal(); 92 | Long userId = loginUser.getUser().getId(); 93 | // 删除redis中的userid值 94 | redisCache.deleteObject("login:" + userId); 95 | return Result.success("注销成功", "注销成功"); 96 | } 97 | 98 | @Override 99 | public Result register(User user) { 100 | // 先判断用户名是否已存在 101 | LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); 102 | queryWrapper.eq(User::getUsername, user.getUsername()); 103 | User res = userMapper.selectOne(queryWrapper); 104 | System.out.println("测试:" + res); 105 | if (!Objects.isNull(res)) { 106 | return Result.error("用户名已存在"); 107 | } 108 | // 对密码进行BCrypt加密后存入数据库中 109 | user.setPassword(passwordEncoder.encode(user.getPassword())); 110 | user.setNickname(user.getUsername()); 111 | user.setSex(SexEnum.SECRET); 112 | userMapper.insert(user); 113 | 114 | // 解决用户冷启动问题 115 | User newUser = userMapper.selectOne(new LambdaQueryWrapper().eq(User::getUsername, user.getUsername())); 116 | Long id = newUser.getId(); 117 | recommendationService.solveColdStart(id); 118 | 119 | return Result.success("注册成功"); 120 | } 121 | 122 | @Override 123 | public UserInfoVo getUserInfo(Long id) { 124 | User user = userMapper.selectOne(new LambdaQueryWrapper().eq(User::getId, id)); 125 | return new UserInfoVo( 126 | user.getId(), 127 | user.getUsername(), 128 | user.getNickname(), 129 | PicUrlUtil.getFullAvatarUrl(user.getAvatar()), 130 | user.getSex().getSexName()); 131 | } 132 | 133 | @Override 134 | public Map> getTypesAndRegions(Long id) { 135 | List types = userMapper.selectUserTypes(id); 136 | List regions = userMapper.selectUserRegions(id); 137 | 138 | Map> res = new HashMap<>(2); 139 | res.put("types", types); 140 | res.put("regions", regions); 141 | return res; 142 | } 143 | 144 | @Override 145 | public int updateNickname(String nickname) { 146 | // 获取SecurityHolder中的用户id 147 | Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); 148 | LoginUser loginUser = (LoginUser) authentication.getPrincipal(); 149 | Long uid = loginUser.getUser().getId(); 150 | 151 | int update = userMapper.update(null, new LambdaUpdateWrapper() 152 | .set(User::getNickname, nickname).eq(User::getId, uid)); 153 | if (update == 0) { 154 | return 0; 155 | } 156 | 157 | // 将评论中所有id为uid的记录的nickname改为更新后的nickname 158 | commentMapper.update(null, new LambdaUpdateWrapper() 159 | .set(Comment::getNickname, nickname).eq(Comment::getUid, uid)); 160 | return 1; 161 | } 162 | 163 | @Override 164 | public int updateSex(String sexName, String username) { 165 | int update = userMapper.update(null, new LambdaUpdateWrapper() 166 | .set(User::getSex, SexEnum.findSexBySexName(sexName)).eq(User::getUsername, username)); 167 | if (update == 0) { 168 | return 0; 169 | } 170 | return 1; 171 | } 172 | 173 | @Override 174 | public int updatePassword(String username, String prePassword, String newPassword, boolean isFindPassword) { 175 | User user = userMapper.selectOne(new LambdaQueryWrapper().eq(User::getUsername, username)); 176 | String userPassword = user.getPassword(); 177 | // 将数据库中的密码 与 用户输入的密码 进行匹配 178 | boolean isMatches = passwordEncoder.matches(prePassword, userPassword); 179 | // 匹配失败返回-1 180 | if (!isMatches && !isFindPassword) { 181 | return -1; 182 | } 183 | 184 | // 对新密码进行BCrypt加密后再存入数据库 185 | newPassword = passwordEncoder.encode(newPassword); 186 | user.setPassword(newPassword); 187 | return userMapper.update(null, 188 | new LambdaUpdateWrapper().set(User::getPassword, newPassword).eq(User::getUsername, username)); 189 | } 190 | } 191 | 192 | 193 | 194 | 195 | -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/utils/Base64DecodedMultipartFile.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.utils; 2 | 3 | import org.springframework.web.multipart.MultipartFile; 4 | import sun.misc.BASE64Decoder; 5 | 6 | import java.io.*; 7 | import java.util.Random; 8 | 9 | /** 10 | * @author zjw 11 | * @Classname BASE64DecodedMultipartFile 12 | * @Date 2022/4/18 0:52 13 | * @Description 14 | */ 15 | public class Base64DecodedMultipartFile implements MultipartFile { 16 | 17 | private final byte[] imgContent; 18 | private final String header; 19 | 20 | public Base64DecodedMultipartFile(byte[] imgContent, String header) { 21 | this.imgContent = imgContent; 22 | this.header = header.split(";")[0]; 23 | } 24 | 25 | @Override 26 | public String getName() { 27 | // implementation depends on your requirements 28 | return System.currentTimeMillis() + Math.random() + "." + "webp"; 29 | } 30 | 31 | @Override 32 | public String getOriginalFilename() { 33 | // implementation depends on your requirements 34 | return System.currentTimeMillis() + new Random().nextInt() + "." + "webp"; 35 | } 36 | 37 | @Override 38 | public String getContentType() { 39 | // implementation depends on your requirements 40 | return header.split(":")[1]; 41 | } 42 | 43 | @Override 44 | public boolean isEmpty() { 45 | return imgContent == null || imgContent.length == 0; 46 | } 47 | 48 | @Override 49 | public long getSize() { 50 | return imgContent.length; 51 | } 52 | 53 | @Override 54 | public byte[] getBytes() throws IOException { 55 | return imgContent; 56 | } 57 | 58 | @Override 59 | public InputStream getInputStream() throws IOException { 60 | return new ByteArrayInputStream(imgContent); 61 | } 62 | 63 | @Override 64 | public void transferTo(File dest) throws IOException, IllegalStateException { 65 | new FileOutputStream(dest).write(imgContent); 66 | } 67 | 68 | /** 69 | * base64转MultipartFile文件 70 | * 71 | * @param base64 base64字符串 72 | * @return MultipartFile文件 73 | */ 74 | public static MultipartFile base64ToMultipart(String base64) { 75 | try { 76 | String[] baseStrs = base64.split(","); 77 | 78 | BASE64Decoder decoder = new BASE64Decoder(); 79 | byte[] b = new byte[0]; 80 | b = decoder.decodeBuffer(baseStrs[1]); 81 | 82 | for (int i = 0; i < b.length; ++i) { 83 | if (b[i] < 0) { 84 | b[i] += 256; 85 | } 86 | } 87 | 88 | return new Base64DecodedMultipartFile(b, baseStrs[0]); 89 | } catch (IOException e) { 90 | e.printStackTrace(); 91 | return null; 92 | } 93 | } 94 | 95 | /** 96 | * 保存MultipartFile到本地 97 | * @param fileContent 源文件 98 | * @param dirPath 本地路径 99 | */ 100 | public static void approvalFile(MultipartFile fileContent, String dirPath){ 101 | OutputStream os = null; 102 | InputStream inputStream = null; 103 | String fileName = null; 104 | try { 105 | inputStream = fileContent.getInputStream(); 106 | fileName = fileContent.getOriginalFilename(); 107 | } catch (IOException e) { 108 | e.printStackTrace(); 109 | } 110 | try { 111 | String path = dirPath; 112 | // 2、保存到临时文件 113 | // 1K的数据缓冲 114 | byte[] bs = new byte[1024]; 115 | // 读取到的数据长度 116 | int len; 117 | // 输出的文件流保存到本地文件 118 | File tempFile = new File(path); 119 | if (!tempFile.exists()) { 120 | tempFile.mkdirs(); 121 | } 122 | os = new FileOutputStream(tempFile.getPath() + File.separator + fileName); 123 | // 开始读取 124 | while ((len = inputStream.read(bs)) != -1) { 125 | os.write(bs, 0, len); 126 | } 127 | } catch (IOException e) { 128 | e.printStackTrace(); 129 | } catch (Exception e) { 130 | e.printStackTrace(); 131 | } finally { 132 | // 完毕,关闭所有链接 133 | try { 134 | os.close(); 135 | inputStream.close(); 136 | } catch (IOException e) { 137 | e.printStackTrace(); 138 | } 139 | } 140 | } 141 | } -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/utils/FastJsonRedisSerializer.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.utils; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.alibaba.fastjson.serializer.SerializerFeature; 5 | import com.fasterxml.jackson.databind.JavaType; 6 | import com.fasterxml.jackson.databind.ObjectMapper; 7 | import com.fasterxml.jackson.databind.type.TypeFactory; 8 | import org.springframework.data.redis.serializer.RedisSerializer; 9 | import org.springframework.data.redis.serializer.SerializationException; 10 | import com.alibaba.fastjson.parser.ParserConfig; 11 | import org.springframework.util.Assert; 12 | import java.nio.charset.Charset; 13 | 14 | /** 15 | * Redis使用FastJson序列化 16 | * 17 | * @author zjw 18 | */ 19 | public class FastJsonRedisSerializer implements RedisSerializer { 20 | 21 | public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8"); 22 | 23 | private Class clazz; 24 | 25 | static { 26 | ParserConfig.getGlobalInstance().setAutoTypeSupport(true); 27 | } 28 | 29 | public FastJsonRedisSerializer(Class clazz) { 30 | super(); 31 | this.clazz = clazz; 32 | } 33 | 34 | @Override 35 | public byte[] serialize(T t) throws SerializationException { 36 | if (t == null) { 37 | return new byte[0]; 38 | } 39 | return JSON.toJSONString(t, SerializerFeature.WriteClassName).getBytes(DEFAULT_CHARSET); 40 | } 41 | 42 | @Override 43 | public T deserialize(byte[] bytes) throws SerializationException { 44 | if (bytes == null || bytes.length <= 0) { 45 | return null; 46 | } 47 | String str = new String(bytes, DEFAULT_CHARSET); 48 | 49 | return JSON.parseObject(str, clazz); 50 | } 51 | 52 | 53 | protected JavaType getJavaType(Class clazz) { 54 | return TypeFactory.defaultInstance().constructType(clazz); 55 | } 56 | } -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/utils/FileUtil.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.utils; 2 | 3 | import org.springframework.core.io.ClassPathResource; 4 | 5 | import java.io.BufferedReader; 6 | import java.io.IOException; 7 | import java.io.InputStream; 8 | import java.io.InputStreamReader; 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | 12 | /** 13 | * @author zjw 14 | * @Classname FileUtil 15 | * @Date 2022/5/20 22:45 16 | * @Description 17 | */ 18 | public class FileUtil { 19 | /** 20 | * 读取StopWords.txt文件中的停用词 21 | * @return 读取结果 22 | */ 23 | public static List readStopWords() { 24 | List stopWords = new ArrayList<>(); 25 | 26 | ClassPathResource classPathResource = new ClassPathResource("static/assets/StopWords.txt"); 27 | try { 28 | InputStream inputStream = classPathResource.getInputStream(); 29 | BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); 30 | while(reader.ready()) { 31 | String line = reader.readLine(); 32 | stopWords.add(line); 33 | } 34 | } catch (IOException e) { 35 | e.printStackTrace(); 36 | } 37 | return stopWords; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/utils/JwtUtil.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.utils; 2 | 3 | import io.jsonwebtoken.Claims; 4 | import io.jsonwebtoken.JwtBuilder; 5 | import io.jsonwebtoken.Jwts; 6 | import io.jsonwebtoken.SignatureAlgorithm; 7 | 8 | import javax.crypto.SecretKey; 9 | import javax.crypto.spec.SecretKeySpec; 10 | import java.util.Base64; 11 | import java.util.Date; 12 | import java.util.UUID; 13 | 14 | /** 15 | * @author zjw 16 | * JWT工具类 17 | */ 18 | public class JwtUtil { 19 | 20 | /** 21 | * 有效期为: 3天 22 | */ 23 | public static final Long JWT_TTL = 3 * 24 * 60 * 60 *1000L; 24 | 25 | /** 26 | * 设置秘钥明文 27 | */ 28 | public static final String JWT_KEY = "zjw"; 29 | 30 | public static String getUuid(){ 31 | return UUID.randomUUID().toString().replaceAll("-", ""); 32 | } 33 | 34 | /** 35 | * 生成jtw 36 | * @param subject token中要存放的数据(json格式) 37 | */ 38 | public static String createJwt(String subject) { 39 | // 设置过期时间 40 | JwtBuilder builder = getJwtBuilder(subject, null, getUuid()); 41 | return builder.compact(); 42 | } 43 | 44 | /** 45 | * 生成jtw 46 | * @param subject token中要存放的数据(json格式) 47 | * @param ttlMillis token超时时间 48 | */ 49 | public static String createJwt(String subject, Long ttlMillis) { 50 | // 设置过期时间 51 | JwtBuilder builder = getJwtBuilder(subject, ttlMillis, getUuid()); 52 | return builder.compact(); 53 | } 54 | 55 | private static JwtBuilder getJwtBuilder(String subject, Long ttlMillis, String uuid) { 56 | SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256; 57 | SecretKey secretKey = generalKey(); 58 | long nowMillis = System.currentTimeMillis(); 59 | Date now = new Date(nowMillis); 60 | if(ttlMillis == null){ 61 | ttlMillis = JwtUtil.JWT_TTL; 62 | } 63 | long expMillis = nowMillis + ttlMillis; 64 | Date expDate = new Date(expMillis); 65 | return Jwts.builder() 66 | // 唯一的ID 67 | .setId(uuid) 68 | // 主题 可以是JSON数据 69 | .setSubject(subject) 70 | // 签发者 71 | .setIssuer("sg") 72 | // 签发时间 73 | .setIssuedAt(now) 74 | // 使用HS256对称加密算法签名, 第二个参数为秘钥 75 | .signWith(signatureAlgorithm, secretKey) 76 | .setExpiration(expDate); 77 | } 78 | 79 | /** 80 | * 创建token 81 | */ 82 | public static String createJwt(String id, String subject, Long ttlMillis) { 83 | // 设置过期时间 84 | JwtBuilder builder = getJwtBuilder(subject, ttlMillis, id); 85 | return builder.compact(); 86 | } 87 | 88 | /** 89 | * 生成加密后的秘钥 secretKey 90 | * @return 加密后的秘钥 secretKey 91 | */ 92 | public static SecretKey generalKey() { 93 | byte[] encodedKey = Base64.getDecoder().decode(JwtUtil.JWT_KEY); 94 | return new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES"); 95 | } 96 | 97 | /** 98 | * 解析jwt 99 | * 100 | * @param jwt token 101 | * @return 解析结果 102 | */ 103 | public static Claims parseJwt(String jwt) throws Exception { 104 | SecretKey secretKey = generalKey(); 105 | return Jwts.parser() 106 | .setSigningKey(secretKey) 107 | .parseClaimsJws(jwt) 108 | .getBody(); 109 | } 110 | 111 | 112 | } -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/utils/MailUtil.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.utils; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import org.springframework.beans.factory.annotation.Value; 6 | import org.springframework.core.io.FileSystemResource; 7 | import org.springframework.mail.SimpleMailMessage; 8 | import org.springframework.mail.javamail.JavaMailSender; 9 | import org.springframework.mail.javamail.MimeMessageHelper; 10 | import org.springframework.stereotype.Component; 11 | 12 | import javax.annotation.Resource; 13 | import javax.mail.MessagingException; 14 | import javax.mail.internet.MimeMessage; 15 | import java.io.File; 16 | 17 | /** 18 | * @author zjw 19 | * @Classname MailUtil 20 | * @Date 2022/5/17 16:00 21 | * @Description 22 | */ 23 | @Component 24 | public class MailUtil { 25 | private final Logger logger = LoggerFactory.getLogger(this.getClass()); 26 | 27 | @Resource 28 | private JavaMailSender mailSender; 29 | 30 | @Value("${spring.mail.from}") 31 | private String from; 32 | 33 | 34 | /** 35 | * 发送文本邮件 36 | * @param to 收件人 37 | * @param subject 主题 38 | * @param content 内容 39 | */ 40 | public void sendSimpleMail(String to, String subject, String content) { 41 | // 创建SimpleMailMessage对象 42 | SimpleMailMessage message = new SimpleMailMessage(); 43 | // 邮件发送人 44 | message.setFrom(from); 45 | // 邮件接收人 46 | message.setTo(to); 47 | // 邮件主题 48 | message.setSubject(subject); 49 | // 邮件内容 50 | message.setText(content); 51 | // 发送邮件 52 | mailSender.send(message); 53 | } 54 | 55 | /** 56 | * 发送HTML邮件 57 | * @param to 收件人 58 | * @param subject 主题 59 | * @param content 内容 60 | */ 61 | public void sendHtmlMail(String to, String subject, String content) { 62 | //获取MimeMessage对象 63 | MimeMessage message = mailSender.createMimeMessage(); 64 | MimeMessageHelper messageHelper; 65 | try { 66 | messageHelper = new MimeMessageHelper(message, true); 67 | // 邮件发送人 68 | messageHelper.setFrom(from); 69 | // 邮件接收人 70 | messageHelper.setTo(to); 71 | // 邮件主题 72 | message.setSubject(subject); 73 | // 邮件内容,html格式 74 | messageHelper.setText(content, true); 75 | // 发送 76 | mailSender.send(message); 77 | // 日志信息 78 | logger.info("邮件已经发送。"); 79 | } catch (MessagingException e) { 80 | logger.error("发送邮件时发生异常!", e); 81 | } 82 | } 83 | 84 | /** 85 | * 发送带附件的邮件 86 | * @param to 收件人 87 | * @param subject 主题 88 | * @param content 内容 89 | * @param filePath 附件 90 | */ 91 | public void sendAttachmentsMail(String to, String subject, String content, String filePath) { 92 | MimeMessage message = mailSender.createMimeMessage(); 93 | try { 94 | MimeMessageHelper helper = new MimeMessageHelper(message, true); 95 | helper.setFrom(from); 96 | helper.setTo(to); 97 | helper.setSubject(subject); 98 | helper.setText(content, true); 99 | 100 | FileSystemResource file = new FileSystemResource(new File(filePath)); 101 | String fileName = filePath.substring(filePath.lastIndexOf(File.separator)); 102 | helper.addAttachment(fileName, file); 103 | mailSender.send(message); 104 | //日志信息 105 | logger.info("邮件已经发送。"); 106 | } catch (MessagingException e) { 107 | logger.error("发送邮件时发生异常!", e); 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/utils/MyDataModel.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.utils; 2 | 3 | import com.mysql.cj.jdbc.MysqlDataSource; 4 | import org.apache.catalina.realm.DataSourceRealm; 5 | import org.apache.mahout.cf.taste.impl.model.file.FileDataModel; 6 | import org.apache.mahout.cf.taste.impl.model.jdbc.ConnectionPoolDataSource; 7 | import org.apache.mahout.cf.taste.impl.model.jdbc.MySQLJDBCDataModel; 8 | import org.apache.mahout.cf.taste.model.JDBCDataModel; 9 | 10 | /** 11 | * @author zjw 12 | * @Classname MyDataModel 13 | * @Date 2022/5/1 22:13 14 | * @Description 15 | */ 16 | public class MyDataModel { 17 | 18 | public static JDBCDataModel myDataModel() { 19 | MysqlDataSource dataSource = new MysqlDataSource(); 20 | JDBCDataModel dataModel = null; 21 | try { 22 | dataSource.setServerName("rm-xxx.mysql.rds.aliyuncs.com"); 23 | dataSource.setUser("xxx"); 24 | dataSource.setPassword("xxx"); 25 | dataSource.setDatabaseName("mrs"); 26 | dataSource.setServerTimezone("GMT%2B8"); 27 | dataSource.setCharacterEncoding("utf-8"); 28 | 29 | System.out.println("1111"); 30 | // ConnectionPoolDataSource connectionPool = new ConnectionPoolDataSource(dataSource); 31 | 32 | dataModel = new MySQLJDBCDataModel(dataSource, 33 | "comment", 34 | "uid", 35 | "mid", 36 | "score", 37 | "time"); 38 | 39 | System.out.println("2222"); 40 | } catch (Exception e) { 41 | e.printStackTrace(); 42 | } 43 | return dataModel; 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/utils/PicUrlUtil.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.utils; 2 | 3 | import cn.zjw.mrs.entity.LoginUser; 4 | import org.apache.logging.log4j.util.Strings; 5 | 6 | /** 7 | * @author zjw 8 | * @Classname PicUrlUtil 9 | * @Date 2022/4/18 13:30 10 | * @Description 11 | */ 12 | public class PicUrlUtil { 13 | /** 14 | * 获取完整图片路径 15 | * 由于数据库中的图片url路径为oss存储路径的后缀,所以返回给前端前需要将路径补全 16 | * @param avatar 头像路径后缀 17 | * @return 完整头像url 18 | */ 19 | public static String getFullAvatarUrl(String avatar) { 20 | String avatarUrl = ""; 21 | if (Strings.isNotBlank(avatar)) { 22 | avatarUrl = "https://mrs-zjw.oss-cn-hangzhou.aliyuncs.com/mrs/avatar/" + avatar; 23 | } 24 | return avatarUrl; 25 | } 26 | 27 | /** 28 | * 获取完整图片路径 29 | * 由于数据库中的图片url路径为oss存储路径的后缀,所以返回给前端前需要将路径补全 30 | * @param pic 电影海报路径后缀 31 | * @return 完整电影海报url 32 | */ 33 | public static String getFullMoviePicUrl(String pic) { 34 | String avatarUrl = ""; 35 | if (Strings.isNotBlank(pic)) { 36 | avatarUrl = "https://mrs-zjw.oss-cn-hangzhou.aliyuncs.com/mrs/movie/" + pic; 37 | } 38 | return avatarUrl; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/utils/RecommendationUtil.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.utils; 2 | 3 | import cn.zjw.mrs.enums.RecommendationTypeEnum; 4 | 5 | import java.util.HashSet; 6 | import java.util.Set; 7 | 8 | /** 9 | * @author zjw 10 | * @Classname RecommendationUtil 11 | * @Date 2022/5/28 20:29 12 | * @Description 13 | */ 14 | public class RecommendationUtil { 15 | 16 | /** 17 | * 随机指定范围内N个不重复的数; 18 | * 利用HashSet的特征,只能存放不同的值; 19 | * 生成的随机数在[min, max)之间。 20 | * @param min 指定范围最小值 21 | * @param max 指定范围最大值 22 | * @param n 随机数个数 23 | * @param set 随机数结果集 24 | */ 25 | public static void randomSet(int min, int max, int n, Set set) { 26 | if (n > (max - min + 1) || max < min) { 27 | return; 28 | } 29 | for (int i = 0; i < n; i++) { 30 | // [0, 1) => [min, max) 31 | int num = (int) (Math.random() * (max - min) + min) ; 32 | // 将不同的数存入HashSet中 33 | set.add(num); 34 | } 35 | int setSize = set.size(); 36 | // 如果存入的数小于指定生成的个数,则调用递归再生成剩余个数的随机数,如此循环,直到达到指定大小 37 | if (setSize < n) { 38 | // 递归 39 | randomSet(min, max, n - setSize, set); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/utils/RedisCache.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.utils; 2 | 3 | import org.springframework.data.redis.core.*; 4 | import org.springframework.stereotype.Component; 5 | 6 | import javax.annotation.Resource; 7 | import java.util.*; 8 | import java.util.concurrent.TimeUnit; 9 | 10 | /** 11 | * @author zjw 12 | */ 13 | @SuppressWarnings(value = { "unchecked", "rawtypes" }) 14 | @Component 15 | public class RedisCache { 16 | @Resource 17 | public RedisTemplate redisTemplate; 18 | 19 | /** 20 | * 缓存基本的对象,Integer、String、实体类等 21 | * 22 | * @param key 缓存的键值 23 | * @param value 缓存的值 24 | */ 25 | public void setCacheObject(final String key, final T value) { 26 | redisTemplate.opsForValue().set(key, value); 27 | } 28 | 29 | /** 30 | * 缓存基本的对象,Integer、String、实体类等 31 | * 32 | * @param key 缓存的键值 33 | * @param value 缓存的值 34 | * @param timeout 时间 35 | * @param timeUnit 时间颗粒度 36 | */ 37 | public void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit) { 38 | redisTemplate.opsForValue().set(key, value, timeout, timeUnit); 39 | } 40 | 41 | /** 42 | * 设置有效时间 43 | * 44 | * @param key Redis键 45 | * @param timeout 超时时间 46 | * @return true=设置成功;false=设置失败 47 | */ 48 | public boolean expire(final String key, final long timeout) { 49 | return expire(key, timeout, TimeUnit.SECONDS); 50 | } 51 | 52 | /** 53 | * 设置有效时间 54 | * 55 | * @param key Redis键 56 | * @param timeout 超时时间 57 | * @param unit 时间单位 58 | * @return true=设置成功;false=设置失败 59 | */ 60 | public boolean expire(final String key, final long timeout, final TimeUnit unit) { 61 | return Boolean.TRUE.equals(redisTemplate.expire(key, timeout, unit)); 62 | } 63 | 64 | /** 65 | * 获得缓存的基本对象。 66 | * 67 | * @param key 缓存键值 68 | * @return 缓存键值对应的数据 69 | */ 70 | public T getCacheObject(final String key) { 71 | ValueOperations operation = redisTemplate.opsForValue(); 72 | return operation.get(key); 73 | } 74 | 75 | /** 76 | * 删除单个对象 77 | * 78 | * @param key 79 | */ 80 | public boolean deleteObject(final String key) { 81 | return Boolean.TRUE.equals(redisTemplate.delete(key)); 82 | } 83 | 84 | /** 85 | * 删除集合对象 86 | * 87 | * @param collection 多个对象 88 | * @return 89 | */ 90 | public long deleteObject(final Collection collection) { 91 | return redisTemplate.delete(collection); 92 | } 93 | 94 | /** 95 | * 缓存List数据 96 | * 97 | * @param key 缓存的键值 98 | * @param dataList 待缓存的List数据 99 | * @return 缓存的对象 100 | */ 101 | public long setCacheList(final String key, final List dataList) { 102 | Long count = redisTemplate.opsForList().rightPushAll(key, dataList); 103 | return count == null ? 0 : count; 104 | } 105 | 106 | /** 107 | * 获得缓存的list对象 108 | * 109 | * @param key 缓存的键值 110 | * @return 缓存键值对应的数据 111 | */ 112 | public List getCacheList(final String key) { 113 | return redisTemplate.opsForList().range(key, 0, -1); 114 | } 115 | 116 | /** 117 | * 缓存Set 118 | * 119 | * @param key 缓存键值 120 | * @param dataSet 缓存的数据 121 | * @return 缓存数据的对象 122 | */ 123 | public BoundSetOperations setCacheSet(final String key, final Set dataSet) { 124 | BoundSetOperations setOperation = redisTemplate.boundSetOps(key); 125 | Iterator it = dataSet.iterator(); 126 | while (it.hasNext()) 127 | { 128 | setOperation.add(it.next()); 129 | } 130 | return setOperation; 131 | } 132 | 133 | /** 134 | * 获得缓存的set 135 | * 136 | * @param key 137 | * @return 138 | */ 139 | public Set getCacheSet(final String key) { 140 | return redisTemplate.opsForSet().members(key); 141 | } 142 | 143 | /** 144 | * 缓存Map 145 | * 146 | * @param key 147 | * @param dataMap 148 | */ 149 | public void setCacheMap(final String key, final Map dataMap) { 150 | if (dataMap != null) { 151 | redisTemplate.opsForHash().putAll(key, dataMap); 152 | } 153 | } 154 | 155 | /** 156 | * 获得缓存的Map 157 | * 158 | * @param key 159 | * @return 160 | */ 161 | public Map getCacheMap(final String key) { 162 | return redisTemplate.opsForHash().entries(key); 163 | } 164 | 165 | /** 166 | * 往Hash中存入数据 167 | * 168 | * @param key Redis键 169 | * @param hKey Hash键 170 | * @param value 值 171 | */ 172 | public void setCacheMapValue(final String key, final String hKey, final T value) { 173 | redisTemplate.opsForHash().put(key, hKey, value); 174 | } 175 | 176 | /** 177 | * 获取Hash中的数据 178 | * 179 | * @param key Redis键 180 | * @param hKey Hash键 181 | * @return Hash中的对象 182 | */ 183 | public T getCacheMapValue(final String key, final String hKey) { 184 | HashOperations opsForHash = redisTemplate.opsForHash(); 185 | return opsForHash.get(key, hKey); 186 | } 187 | 188 | /** 189 | * 删除Hash中的数据 190 | * 191 | * @param key Redis键 192 | * @param hKey Hash键 193 | */ 194 | public void delCacheMapValue(final String key, final String hKey) { 195 | HashOperations hashOperations = redisTemplate.opsForHash(); 196 | hashOperations.delete(key, hKey); 197 | } 198 | 199 | /** 200 | * Hash中的数据值增加num 201 | * @param key Redis键 202 | * @param hKey Hash键 203 | * @param num 增量 204 | */ 205 | public void increaseCacheMapValue(final String key, final String hKey, final int num) { 206 | redisTemplate.opsForHash().increment(key, hKey, num ); 207 | } 208 | 209 | /** 210 | * 获取多个Hash中的数据 211 | * 212 | * @param key Redis键 213 | * @param hKeys Hash键集合 214 | * @return Hash对象集合 215 | */ 216 | public List getMultiCacheMapValue(final String key, final Collection hKeys) { 217 | return redisTemplate.opsForHash().multiGet(key, hKeys); 218 | } 219 | 220 | /** 221 | * 获取Redis键为key的所有Hash数据,返回遍历游标Cursor 222 | * @param key Redis键 223 | * @return 遍历游标 224 | */ 225 | public Cursor> getAllCacheMapValueCursor(final String key) { 226 | return redisTemplate.opsForHash().scan(key, ScanOptions.NONE); 227 | } 228 | 229 | /** 230 | * 获得缓存的基本对象列表 231 | * 232 | * @param pattern 字符串前缀 233 | * @return 对象列表 234 | */ 235 | public Collection keys(final String pattern) { 236 | return redisTemplate.keys(pattern); 237 | } 238 | } -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/utils/RedisKeyUtil.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.utils; 2 | 3 | /** 4 | * @author zjw 5 | * @Classname RedisKeyUtil 6 | * @Date 2022/5/22 16:46 7 | * @Description 8 | */ 9 | public class RedisKeyUtil { 10 | /** 11 | * 保存用户点赞数据的key 12 | */ 13 | public static final String MAP_KEY_USER_LIKED = "MAP_USER_LIKED"; 14 | /** 15 | * 保存用户被点赞数量的key 16 | */ 17 | public static final String MAP_KEY_USER_LIKED_COUNT = "MAP_USER_LIKED_COUNT"; 18 | 19 | /** 20 | * 拼接被点赞的用户id和点赞的人的id作为key。格式 222222::333333 21 | * @param cid 被点赞的评论id 22 | * @param uid 点赞的用户id 23 | * @return cid::uid 24 | */ 25 | public static String getLikedKey(long cid, long uid){ 26 | return cid + "::" + uid; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/utils/WebUtils.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.utils; 2 | 3 | import javax.servlet.http.HttpServletResponse; 4 | import java.io.IOException; 5 | 6 | 7 | /** 8 | * @author zjw 9 | */ 10 | public class WebUtils { 11 | /** 12 | * 将字符串渲染到客户端 13 | * 14 | * @param response 渲染对象 15 | * @param string 待渲染的字符串 16 | * @return null 17 | */ 18 | public static String renderString(HttpServletResponse response, String string) { 19 | try { 20 | response.setStatus(200); 21 | response.setContentType("application/json"); 22 | response.setCharacterEncoding("utf-8"); 23 | response.getWriter().print(string); 24 | } 25 | catch (IOException e) { 26 | e.printStackTrace(); 27 | } 28 | return null; 29 | } 30 | } -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/vo/comment/CommentMovieVo.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.vo.comment; 2 | 3 | import cn.zjw.mrs.vo.movie.MovieStripVo; 4 | import lombok.Data; 5 | 6 | /** 7 | * @author zjw 8 | * @Classname CommentMovieVo 9 | * @Date 2022/4/18 15:21 10 | * @Description 11 | */ 12 | @Data 13 | public class CommentMovieVo { 14 | 15 | private CommentStripVo commentStripVo; 16 | 17 | private MovieStripVo movieStripVo; 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/vo/comment/CommentStripVo.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.vo.comment; 2 | 3 | import com.fasterxml.jackson.annotation.JsonFormat; 4 | import lombok.Data; 5 | 6 | import java.sql.Timestamp; 7 | 8 | /** 9 | * @author zjw 10 | * @Classname OwnCommentVo 11 | * @Date 2022/4/14 22:28 12 | * @Description 13 | */ 14 | @Data 15 | public class CommentStripVo { 16 | private long id; 17 | 18 | private int score; 19 | 20 | private String comment; 21 | 22 | /** 23 | * 注解用于转化时间戳 24 | */ 25 | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") 26 | private Timestamp time; 27 | 28 | private int agree; 29 | 30 | /** 31 | * 1表示豆瓣评论,0表示系统评论 32 | */ 33 | private int type; 34 | 35 | private String nickname; 36 | 37 | private String avatar; 38 | 39 | /** 40 | * 1表示已点赞,0表示未点赞 41 | */ 42 | private Integer status; 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/vo/movie/MovieCardVo.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.vo.movie; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * @author zjw 7 | * @Classname MovieCardVo 8 | * @Date 2022/4/13 14:35 9 | * @Description 电影卡片 10 | */ 11 | @Data 12 | public class MovieCardVo { 13 | private Long id; 14 | 15 | private String name; 16 | 17 | private double score; 18 | 19 | private String pic; 20 | 21 | private Long num; 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/vo/movie/MovieStripVo.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.vo.movie; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * @author zjw 7 | * @Classname MovieStripVo 8 | * @Date 2022/4/18 14:46 9 | * @Description 电影长条 10 | */ 11 | @Data 12 | public class MovieStripVo { 13 | private Long id; 14 | 15 | private Long did; 16 | 17 | private String name; 18 | 19 | private double score; 20 | 21 | private String pic; 22 | 23 | private String directors; 24 | 25 | private String actors; 26 | 27 | private String regions; 28 | 29 | private String types; 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/vo/movie/RecommendedMovieVo.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.vo.movie; 2 | 3 | import com.fasterxml.jackson.annotation.JsonFormat; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | import java.sql.Timestamp; 9 | 10 | /** 11 | * @author zjw 12 | * @Classname RecommendedMovieVo 13 | * @Date 2022/4/24 0:22 14 | * @Description 15 | */ 16 | @Data 17 | @AllArgsConstructor 18 | @NoArgsConstructor 19 | public class RecommendedMovieVo { 20 | private Long id; 21 | 22 | private Long did; 23 | 24 | private String name; 25 | 26 | private Double score; 27 | 28 | private String pic; 29 | 30 | private String directors; 31 | 32 | private String actors; 33 | 34 | private String regions; 35 | 36 | private String types; 37 | 38 | /** 39 | * 推荐指数 40 | */ 41 | private Double idx; 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/vo/movie/ReviewedMovieStripVo.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.vo.movie; 2 | 3 | import com.fasterxml.jackson.annotation.JsonFormat; 4 | import lombok.Data; 5 | 6 | import java.sql.Timestamp; 7 | 8 | /** 9 | * @author zjw 10 | * @Classname ReviewedMovieStripVo 11 | * @Date 2022/4/18 21:32 12 | * @Description 评价过的电影 13 | */ 14 | @Data 15 | public class ReviewedMovieStripVo { 16 | private Long id; 17 | 18 | private Long did; 19 | 20 | private String name; 21 | 22 | /** 23 | * 电影评分 24 | */ 25 | private double score; 26 | 27 | /** 28 | * 用户评分 29 | */ 30 | private double userScore; 31 | 32 | private String pic; 33 | 34 | private String directors; 35 | 36 | private String actors; 37 | 38 | private String regions; 39 | 40 | private String types; 41 | 42 | /** 43 | * 评价时间 44 | * 注解用于转化时间戳 45 | */ 46 | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") 47 | private Timestamp time; 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/vo/movie/relation/CategoryVo.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.vo.movie.relation; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | /** 8 | * @author zjw 9 | * @Classname CategoryVo 10 | * @Date 2022/4/23 18:16 11 | * @Description 12 | */ 13 | @Data 14 | @AllArgsConstructor 15 | @NoArgsConstructor 16 | public class CategoryVo { 17 | 18 | private String name; 19 | 20 | private String base; 21 | 22 | public CategoryVo(String name) { 23 | this.name = name; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/vo/movie/relation/LinkVo.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.vo.movie.relation; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | /** 8 | * @author zjw 9 | * @Classname LinkVo 10 | * @Date 2022/4/23 17:54 11 | * @Description 12 | */ 13 | @Data 14 | @AllArgsConstructor 15 | @NoArgsConstructor 16 | public class LinkVo { 17 | 18 | /** 19 | * 起点 20 | */ 21 | private String source; 22 | 23 | /** 24 | * 终点 25 | */ 26 | private String target; 27 | 28 | /** 29 | * 相似指数 30 | */ 31 | private Double idx; 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/vo/movie/relation/NodeVo.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.vo.movie.relation; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | /** 8 | * @author zjw 9 | * @Classname NodeVo 10 | * @Date 2022/4/23 18:01 11 | * @Description 12 | */ 13 | @Data 14 | @AllArgsConstructor 15 | @NoArgsConstructor 16 | public class NodeVo { 17 | private Long id; 18 | private Long mid; 19 | private String name; 20 | private String value; 21 | private Integer category; 22 | private String types; 23 | private String regions; 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/vo/user/LoginUserVo.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.vo.user; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | /** 8 | * @author zjw 9 | * @Classname LoginUserVo 10 | * @Date 2022/4/12 13:49 11 | * @Description 12 | */ 13 | @Data 14 | @NoArgsConstructor 15 | @AllArgsConstructor 16 | public class LoginUserVo { 17 | 18 | private String token; 19 | private UserInfoVo userInfo; 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/cn/zjw/mrs/vo/user/UserInfoVo.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs.vo.user; 2 | 3 | import cn.zjw.mrs.enums.SexEnum; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | /** 9 | * @author zjw 10 | * @Classname UserInfoVo 11 | * @Date 2022/4/12 13:53 12 | * @Description 13 | */ 14 | @Data 15 | @AllArgsConstructor 16 | @NoArgsConstructor 17 | public class UserInfoVo { 18 | private Long id; 19 | 20 | private String username; 21 | 22 | private String nickname; 23 | 24 | private String avatar; 25 | 26 | private String sex; 27 | } 28 | -------------------------------------------------------------------------------- /src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8888 3 | 4 | spring: 5 | # 配置数据源信息 6 | datasource: 7 | # 配置数据源类型 8 | type: com.zaxxer.hikari.HikariDataSource 9 | # 配置连接数据库信息 10 | driver-class-name: com.mysql.cj.jdbc.Driver 11 | url: jdbc:mysql://rm-xxx.mysql.rds.aliyuncs.com:3306/mrs?serverTimezone=GMT%2B8&characterEncoding=utf-8&useSSL=false 12 | username: xxx 13 | password: xxx 14 | mail: 15 | # 发送邮件服务器 16 | host: smtp.qq.com 17 | # 发送邮件的用户名 18 | username: xxx@qq.com 19 | # 发送邮件的授权码 20 | password: mwdtcymfstbubiii 21 | # 编码格式 22 | default-encoding: UTF-8 23 | # 使用的协议 24 | protocol: smtp 25 | # 发送邮件的地址 26 | from: xxx@qq.com 27 | properties.mail.smtp.port: 465 28 | properties.mail.smtp.starttls.enable: true 29 | properties.mail.smtp.starttls.required: true 30 | properties.mail.smtp.ssl.enable: true 31 | 32 | 33 | # 配置MyBatis日志 34 | mybatis-plus: 35 | configuration: 36 | log-impl: org.apache.ibatis.logging.stdout.StdOutImpl 37 | type-enums-package: cn.zjw.mrs.enums 38 | 39 | aliyun: 40 | oss: 41 | # oss对外服务的访问域名 42 | endpoint: oss-cn-hangzhou.aliyuncs.com 43 | # 访问身份验证中用到用户标识 44 | accessKeyId: xxx 45 | # 用户用于加密签名字符串和oss用来验证签名字符串的密钥 46 | accessKeySecret: xxx 47 | # oss的存储空间 48 | bucketName: mrs-zjw 49 | # 上传文件大小(M) 50 | maxSize: 3 51 | # 上传文件夹路径前缀 52 | dir: 53 | prefix: mrs/avatar/ 54 | -------------------------------------------------------------------------------- /src/main/resources/mapper/CommentMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | uid,mid,comment, 20 | score,time,agree, 21 | type,user_name 22 | 23 | 24 | 29 | 30 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 81 | 82 | -------------------------------------------------------------------------------- /src/main/resources/mapper/MovieFeatureMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | mid,matrix 14 | 15 | 22 | 23 | 29 | 30 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /src/main/resources/mapper/MovieMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | id,did,name, 34 | year,directors,writers, 35 | actors,types,regions, 36 | languages,release_date,runtime, 37 | alias,imdb,score, 38 | num,five,four, 39 | three,two,one, 40 | introduction,pic 41 | 42 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 69 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 89 | 94 | 95 | -------------------------------------------------------------------------------- /src/main/resources/mapper/MovieRegionMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | mid,rid,degree 15 | 16 | 22 | 29 | 30 | -------------------------------------------------------------------------------- /src/main/resources/mapper/MovieTypeMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | mid,tid,degree 15 | 16 | 22 | 23 | 30 | 31 | -------------------------------------------------------------------------------- /src/main/resources/mapper/RecommendationMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | uid,mid,idx 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 36 | 37 | -------------------------------------------------------------------------------- /src/main/resources/mapper/RegionLikeMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | uid,rid,degree 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/main/resources/mapper/SameLikesMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | did,sid 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/main/resources/mapper/TypeLikeMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | uid,tid,degree 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/main/resources/mapper/UserLikeMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | id,cid,uid, 16 | status 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/main/resources/mapper/UserMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | id,username,nickname, 19 | password,avatar,sex, 20 | state 21 | 22 | 29 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /src/test/java/cn/zjw/mrs/CommentTest.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs; 2 | 3 | import cn.zjw.mrs.entity.Preference; 4 | import cn.zjw.mrs.mapper.CommentMapper; 5 | import cn.zjw.mrs.vo.comment.CommentMovieVo; 6 | import cn.zjw.mrs.vo.comment.CommentStripVo; 7 | import net.sf.jsqlparser.Model; 8 | import org.apache.mahout.cf.taste.impl.model.GenericDataModel; 9 | import org.apache.mahout.cf.taste.impl.model.GenericItemPreferenceArray; 10 | import org.apache.mahout.cf.taste.impl.model.GenericUserPreferenceArray; 11 | import org.apache.mahout.cf.taste.model.DataModel; 12 | import org.apache.mahout.cf.taste.model.PreferenceArray; 13 | import org.junit.jupiter.api.Test; 14 | import org.springframework.beans.factory.annotation.Autowired; 15 | import org.springframework.boot.test.context.SpringBootTest; 16 | 17 | import java.util.List; 18 | 19 | /** 20 | * @Classname CommentTest 21 | * @Date 2022/4/14 22:09 22 | * @Created by zjw 23 | * @Description 24 | */ 25 | @SpringBootTest 26 | public class CommentTest { 27 | 28 | @Autowired 29 | private CommentMapper commentMapper; 30 | 31 | @Test 32 | public void getOwnCommentTest() { 33 | CommentStripVo commentStripVo = commentMapper.selectOwnCommentByUidAndMid((long) 11, (long) 5); 34 | System.out.println(commentStripVo); 35 | } 36 | 37 | @Test 38 | public void getCommentsByMovieId() { 39 | List commentStripVo = commentMapper.selectMoreCommentsByMovieId((long) 500, 5, 5); 40 | System.out.println(commentStripVo); 41 | } 42 | 43 | @Test 44 | public void getCommentMovieMoments() { 45 | List commentMovieVos = commentMapper.selectOwnCommentMovieMoments((long) 10, 0, 10); 46 | for (CommentMovieVo commentStripVo: commentMovieVos) { 47 | System.out.println(commentStripVo.getCommentStripVo().toString()); 48 | System.out.println(commentStripVo.getMovieStripVo().toString()); 49 | System.out.println(); 50 | } 51 | System.out.println(commentMovieVos); 52 | } 53 | 54 | @Test 55 | public void getAllPreferences() { 56 | List preferences = commentMapper.selectAllPreferences(); 57 | for (Preference preference : preferences) { 58 | System.out.println(preference); 59 | } 60 | for (int i = 0; i < preferences.size(); i ++ ) { 61 | int cnt = preferences.get(i).getCnt(); 62 | PreferenceArray preferenceArray = new GenericUserPreferenceArray(cnt); 63 | preferenceArray.setUserID(0, preferences.get(i).getUid()); 64 | for (int j = 0; j < cnt; j ++ ) { 65 | System.out.println(preferences.get(i + j).getMid() + " " + preferences.get(i + j).getScore()); 66 | preferenceArray.setItemID(j, preferences.get(i + j).getMid()); 67 | preferenceArray.setValue(j, preferences.get(i + j).getScore()); 68 | } 69 | System.out.println(preferenceArray); 70 | 71 | i += cnt - 1; 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/test/java/cn/zjw/mrs/JiebaTest.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs; 2 | 3 | import com.huaban.analysis.jieba.JiebaSegmenter; 4 | import org.junit.jupiter.api.Test; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | 7 | /** 8 | * @author zjw 9 | * @Classname JiebaTest 10 | * @Date 2022/5/20 10:48 11 | * @Description 12 | */ 13 | @SpringBootTest 14 | public class JiebaTest { 15 | 16 | @Test 17 | public void testDemo() { 18 | JiebaSegmenter jiebaSegmenter = new JiebaSegmenter(); 19 | String[] sentences = 20 | new String[] {"这是一个伸手不见五指的黑夜。我叫孙悟空,我爱北京,我爱Python和C++。", "我不喜欢日本和服。", "雷猴回归人间。", 21 | "工信处女干事每月经过下属科室都要亲口交代24口交换机等技术性器件的安装工作", "结果婚的和尚未结过婚的"}; 22 | for (String sentence : sentences) { 23 | System.out.println(jiebaSegmenter.process(sentence, JiebaSegmenter.SegMode.INDEX).toString()); 24 | } 25 | } 26 | 27 | public static void main(String[] args) { 28 | JiebaSegmenter jiebaSegmenter = new JiebaSegmenter(); 29 | String[] sentences = 30 | new String[] {"这是一个伸手不见五指的黑夜。我叫孙悟空,我爱北京,我爱Python和C++。", "我不喜欢日本和服。", "雷猴回归人间。", 31 | "工信处女干事每月经过下属科室都要亲口交代24口交换机等技术性器件的安装工作", "结果婚的和尚未结过婚的"}; 32 | for (String sentence : sentences) { 33 | // 去掉两边的空格 34 | sentence = sentence.trim(); 35 | // 将所有标点符号换成空格 36 | sentence = sentence.replaceAll("\\p{P}", " "); 37 | System.out.println(jiebaSegmenter.process(sentence, JiebaSegmenter.SegMode.SEARCH).toString()); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/test/java/cn/zjw/mrs/MailTest.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs; 2 | import cn.zjw.mrs.service.AuthService; 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | import javax.annotation.Resource; 7 | 8 | /** 9 | * @author zjw 10 | * @Classname MailTest 11 | * @Date 2022/5/10 18:12 12 | * @Description 13 | */ 14 | @SpringBootTest 15 | public class MailTest { 16 | 17 | @Resource 18 | private AuthService authService; 19 | 20 | @Test 21 | public void sendMailTest() { 22 | authService.sendMailAuthCode("123", "xxx@qq.com"); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/test/java/cn/zjw/mrs/MovieRecommendationSystemApplicationTests.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class MovieRecommendationSystemApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/test/java/cn/zjw/mrs/MovieTest.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs; 2 | 3 | import cn.zjw.mrs.entity.MovieRegion; 4 | import cn.zjw.mrs.entity.MovieType; 5 | import cn.zjw.mrs.mapper.MovieMapper; 6 | import cn.zjw.mrs.mapper.MovieRegionMapper; 7 | import cn.zjw.mrs.mapper.MovieTypeMapper; 8 | import cn.zjw.mrs.vo.movie.MovieStripVo; 9 | import cn.zjw.mrs.vo.movie.ReviewedMovieStripVo; 10 | import com.jayway.jsonpath.internal.function.text.Length; 11 | import org.junit.jupiter.api.Test; 12 | import org.springframework.boot.test.context.SpringBootTest; 13 | 14 | import javax.annotation.Resource; 15 | import java.util.List; 16 | 17 | /** 18 | * @Classname MovieTest 19 | * @Date 2022/4/18 21:29 20 | * @Created by zjw 21 | * @Description 22 | */ 23 | @SpringBootTest 24 | public class MovieTest { 25 | 26 | @Resource 27 | private MovieMapper movieMapper; 28 | 29 | @Resource 30 | private MovieTypeMapper movieTypeMapper; 31 | 32 | @Resource 33 | private MovieRegionMapper movieRegionMapper; 34 | 35 | @Test 36 | public void selectAllReviewedMoviesByUserId() { 37 | List movies = movieMapper.selectMoreReviewedMoviesByUserId(10L, 1, 10); 38 | for (ReviewedMovieStripVo movie : movies) { 39 | System.out.println(movie.toString()); 40 | } 41 | } 42 | 43 | @Test 44 | public void testMovieTypeAndRegionMapper() { 45 | List movieTypes = movieTypeMapper.selectRecommendedMoviesTypesByUserId(10L); 46 | List movieTypes1 = movieTypeMapper.selectWatchedMoviesTypesByUserId(10L, 30); 47 | System.out.println(movieTypes); 48 | System.out.println(movieTypes.size()); 49 | System.out.println(); 50 | System.out.println(movieTypes1); 51 | System.out.println(movieTypes1.size()); 52 | 53 | List movieRegions = movieRegionMapper.selectRecommendedMoviesRegionsByUserId(10L); 54 | List movieRegions1 = movieRegionMapper.selectWatchedMoviesRegionsByUserId(10L, 30); 55 | System.out.println(movieRegions); 56 | System.out.println(movieRegions.size()); 57 | System.out.println(); 58 | System.out.println(movieRegions1); 59 | System.out.println(movieRegions1.size()); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/test/java/cn/zjw/mrs/RecommendTest.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs; 2 | 3 | import cn.zjw.mrs.service.RecommendationService; 4 | import org.apache.mahout.cf.taste.recommender.RecommendedItem; 5 | import org.junit.jupiter.api.Test; 6 | import org.springframework.boot.test.context.SpringBootTest; 7 | 8 | import javax.annotation.Resource; 9 | import java.util.List; 10 | 11 | /** 12 | * @author zjw 13 | * @Classname RecommendTest 14 | * @Date 2022/4/21 22:19 15 | * @Description 16 | */ 17 | @SpringBootTest 18 | public class RecommendTest { 19 | @Resource 20 | private RecommendationService service; 21 | 22 | @Test 23 | public void testUpdateRecommendation() { 24 | service.updateRecommendation(10L); 25 | } 26 | 27 | @Test 28 | public void testUserBasedRecommender() { 29 | // List recommendedItems = service.getUserBasedMovieRecommendationResult(10, 100); 30 | // recommendedItems.forEach(System.out::println); 31 | System.out.println("test testUserBasedRecommender------------"); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/test/java/cn/zjw/mrs/RedisTest.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs; 2 | 3 | import cn.zjw.mrs.utils.RedisCache; 4 | import org.junit.jupiter.api.Test; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | 7 | import javax.annotation.Resource; 8 | 9 | /** 10 | * @author zjw 11 | * @Classname RedisTest 12 | * @Date 2022/5/14 14:23 13 | * @Description 14 | */ 15 | @SpringBootTest 16 | public class RedisTest { 17 | 18 | @Resource 19 | private RedisCache redisCache; 20 | 21 | @Test 22 | public void test1() throws InterruptedException { 23 | redisCache.setCacheObject("test1", "123"); 24 | redisCache.expire("test1", 10); 25 | Thread.sleep(5000); 26 | String res = redisCache.getCacheObject("test1"); 27 | System.out.println("5========" + res); 28 | 29 | Thread.sleep(20000); 30 | res = redisCache.getCacheObject("test1"); 31 | System.out.println("20========" + res); 32 | 33 | System.out.println("结束了"); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/test/java/cn/zjw/mrs/UserLikeTest.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs; 2 | 3 | import cn.zjw.mrs.service.UserLikeRedisService; 4 | import cn.zjw.mrs.service.UserLikeService; 5 | import org.junit.jupiter.api.Test; 6 | import org.springframework.boot.test.context.SpringBootTest; 7 | 8 | import javax.annotation.Resource; 9 | 10 | /** 11 | * @author zjw 12 | * @Classname UserLikeTest 13 | * @Date 2022/5/22 19:39 14 | * @Description 15 | */ 16 | @SpringBootTest 17 | public class UserLikeTest { 18 | 19 | @Resource 20 | private UserLikeRedisService userLikeRedisService; 21 | 22 | @Resource 23 | private UserLikeService userLikeService; 24 | 25 | @Test 26 | public void test() { 27 | userLikeRedisService.likeComment(3367, 21); 28 | userLikeRedisService.increaseLikedCount(10); 29 | 30 | userLikeRedisService.unlikeComment(20, 10); 31 | userLikeRedisService.decreaseLikedCount(20); 32 | 33 | int status = userLikeRedisService.getUserLikeStatusFromRedis(30, 40); 34 | System.out.println("点赞状态:" + status); 35 | 36 | status = userLikeRedisService.getUserLikeStatusFromRedis(3367, 21); 37 | System.out.println("点赞状态:" + status); 38 | 39 | status = userLikeRedisService.getUserLikeStatusFromRedis(20, 10); 40 | System.out.println("点赞状态:" + status); 41 | 42 | // List userLikes = redisService.getLikedDataFromRedis(); 43 | // userLikes.forEach(System.out::println); 44 | // 45 | // List comments = redisService.getLikedCountFromRedis(); 46 | // comments.forEach(System.out::println); 47 | 48 | // userLikeService.transLikedFromRedis2Database(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/test/java/cn/zjw/mrs/UserMapperTest.java: -------------------------------------------------------------------------------- 1 | package cn.zjw.mrs; 2 | 3 | import cn.zjw.mrs.entity.User; 4 | import cn.zjw.mrs.enums.SexEnum; 5 | import cn.zjw.mrs.mapper.CommentMapper; 6 | import cn.zjw.mrs.mapper.MovieMapper; 7 | import cn.zjw.mrs.mapper.UserMapper; 8 | import cn.zjw.mrs.vo.movie.MovieCardVo; 9 | import org.junit.jupiter.api.Test; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.boot.test.context.SpringBootTest; 12 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 13 | 14 | import java.util.List; 15 | 16 | /** 17 | * @Classname UserMapperTest 18 | * @Date 2022/4/11 16:12 19 | * @Created by zjw 20 | * @Description 21 | */ 22 | 23 | @SpringBootTest 24 | public class UserMapperTest { 25 | 26 | @Autowired 27 | private UserMapper userMapper; 28 | 29 | @Autowired 30 | private MovieMapper movieMapper; 31 | 32 | @Autowired 33 | private CommentMapper commentMapper; 34 | 35 | @Test 36 | public void testUserMapper() { 37 | List users = userMapper.selectList(null); 38 | System.out.println(users); 39 | } 40 | 41 | 42 | @Test 43 | public void TestBCryptPasswordEncoder() { 44 | BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); 45 | String encode = passwordEncoder.encode("root"); 46 | String encode2 = passwordEncoder.encode("12345"); 47 | System.out.println(encode); 48 | System.out.println(encode2); 49 | 50 | boolean matches = passwordEncoder.matches("12345", "$2a$10$W041517SRn1RXfCBQqra4.HxkvP99CBCYs2OdPfZc6j2X3/KyxjCW"); 51 | System.out.println(matches); 52 | } 53 | 54 | @Test 55 | public void TestEnum() { 56 | User user = new User(); 57 | user.setUsername("Enum"); 58 | user.setSex(SexEnum.FEMALE); 59 | userMapper.insert(user); 60 | } 61 | 62 | @Test 63 | public void TestGetUserTypes() { 64 | List types = userMapper.selectUserTypes((long) 10); 65 | System.out.println(types); 66 | 67 | List regions = userMapper.selectUserRegions((long) 10); 68 | System.out.println(regions); 69 | } 70 | 71 | @Test 72 | public void TestGetRecommendedMovies() { 73 | List movies = movieMapper.selectRecommendedMoviesByMovieId(1295644L); 74 | for (MovieCardVo movie : movies) { 75 | System.out.println(movie); 76 | } 77 | } 78 | } 79 | --------------------------------------------------------------------------------