作者:fyupeng
技术专栏:☞ https://github.com/fyupeng
项目地址:☞ https://github.com/fyupeng/distributed-blog-system-api
项目预览地址:☞ 博客第六天
留给读者
你们可能想不到,其实博客已经最后一天了,因为前面该搭建的都搭建完了,后面只是业务代码,但为了能照顾大部分同学,我决定还是承诺应允大家做到从规范到代码,从代码到运行,从运行到项目部署,从部署到项目上线这几个过程。
一些业务中的规范直接按照项目来就可以了,就可以避免碰到许多坑了。
想入坑的有下面这么几个,大家可以不要看推文自己试试,能不能躺到坑位,嘟嘟噜。
-
打包不了的如何解决?用那个依赖就可以解决了?
-
mongodb
的bean
没法依赖,springboot
启动后报空指针异常 -
spring1.0
转2.0
遇到的配置文件信息变动,Redis
、MongoDB
随之变动出现的问题 -
高版本
swagger2
出现的页面不显示问题
所有分布式服务开启后,可以清晰地从Nacos
注册中心看到
swagger2
展示用户的接口
有一点电脑性能建议的,开启6
个进程很消耗内存,我直接飙到了12G
,电脑内存16G
,不知道你们能不能扛得住,CPU
要求比较低,我只有双内核,也就是4
个处理器。
重要的事说三遍,不要在项目直接配置数据库或注册中心,为了提供更好的调试和开发体验,将rpc
需要配置的信息以及博客自定义配置信息支持到Jar
启动外置中配置,一般推荐项目中resource
资源下配置信息只配置与用户名和数据密码敏感无关信息,这在支持外部配置的application.properties
文件中会重写覆盖,目前rpc-netty-framework
只支持配置文件整体覆盖,暂时没有实现局部key-value
信息覆盖。
在摸索数据库安全路上碰壁留下的许多经验,就送给大家吧,只要大家能支持我,我满足了。
先说安全可做的方面:
- 系统账户权限
由于数据库存储类软件,设计到存储功能权限,是需要系统赋予一个存储数据的权限的,所有需要申请账户,有别于系统的root
账户
默认会赋予存储的权限。
所以我们可以通过从以下几个方面去加强安全性
nologin
设置
怎么nologin
设置?
如果你玩过linux或者云服务器,你会发现home目录下会有几个用户目录,如下图
而如果想切换到对应的用户,例如使用命令su mongodb
,想要实现下面这种做法
只需要一行命令
usermod -s /sbin/nologin mysql
su mysql
This account is currently not available.
这样做的好处就是,如果数据库被攻击导致泄露用户信息,那数据库中涉及到系统的存储权限无法直接使用shell
方式登录,那系统中其他信息就相当安全了,不会因为数据库的问题而引发其他问题。
- 访问源地址限制
这一般在防火墙中设置,而如果使用云服务器设置就相对简单多了,就对阿里云来讲,进入到云控制台中,打开你的实例服务器的安全组,对某个端口设置源地址限制即可。
你可以通过这种方式设置数据库开放端口给内部其他服务器,而不直接开放到外网,这样即便暴露了数据库端口和用户登录信息,对方也无法登录。
当然如果你已经泄露过了用户信息,你不妨看看数据库之前是否开启过日志,或者从系统日志中入手,看看是哪个ip
对你的数据库进行了攻击,将其拉入黑名单即可。
- 端口设置
一般不设置默认端口,因为大多数情况下,数据库攻击手段是通过默认端口和默认用户登录信息进行攻击的,也有些脚本是获取公网中如GitHub
上泄露的用户信息进行攻击,这里提供一个工具,如果你有经常做笔记发布到github
或gitee
的习惯,推荐你使用这一款网页版的监控敏感信息:https://www.gitguardian.com/,有敏感信息会通过邮箱告知你,自己完全不用操心,只需在监控到敏感信息后,自己手动处理即可。
- 使用代理
使用代理是将设计到数据存储的服务器地址和端口隐藏不对外开发,而通过另一台服务器开发端口,由它来做代理服务。
做完这方面,可以看看架构上的设计,是如何巧妙通过代理、分布式、缓冲层、鉴权、反射调用来一步步加强安全和联系的。
1. 软件架构
软件架构说明
- 项目分布式前后端代理架构设计:
- 项目RPC架构设计:
下面就着手开发吧,相信各位都能以我的项目作为地基,二次开发出一个更有高度的项目!
2. 编写代码
代理服务器编写控制层代码,编写真实服务的接口,而不去实现接口
主要分为五个模块来编写代码,为什么呢,从设计模式的角度出发,能够解耦,也就是耦合性更低
- 控制层结构:
-
服务接口:
-
mapper层:
保留,没有用到,已经分离了,一开始测试时会用到,后面就删了
- pojo类:
- 公用包:
堆积这么多张图?是水文???
其实是想让大家看看进度,有没有跟不上进度或遗漏的地方
2.1 配置文件
application.properties
############################################################
#
# Server Port
#
############################################################
server.port=8080
############################################################
# Server - tomcat
############################################################
# Tomcat URI EncodingiJ1eK1gN4pE0pG1f
server.tomcat.uri-encoding=UTF-8
pagehelper.helperDialect=mysql
pagehelper.supportMethodsArguments=true
pagehelper.params=count=countSql
# springboot 1.5
#spring.http.multipart.maxFileSize=150Mb
#spring.http.multipart.maxRequestSize=1000Mb
# springboot 2.0
spring.servlet.multipart.max-file-size=150MB
spring.servlet.multipart.max-request-size=1000MB
############################################################
#
# Redis
#
############################################################
# Redis default use dataBase
#spring.redis.database=0
## Redis Host
spring.redis.host=localhost
## Redis Port
spring.redis.port=6379
# Redis password
spring.redis.password=your_redis_password
#spring.redis.pool.max-active=300
spring.redis.jedis.pool.max-active=300
#spring.redis.pool.max-wait=10000
spring.redis.jedis.pool.max-wait=10000
#spring.redis.pool.maxIdle=300
spring.redis.jedis.pool.max-idle=300
#spring.redis.pool.minIdle=6
spring.redis.jedis.pool.min-idle=6
spring.redis.timeout=0
logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<!--%date{HH:mm:ss.SSS} %c -->
<pattern>%date{HH:mm:ss.SSS} %c [%t] - %m%n</pattern>
</encoder>
</appender>
<!--<logger name="org.springframework.security.web.FilterChainProxy" level="DEBUG" additivity="false">
<appender-ref ref="STDOUT"/>
</logger>
<logger name="org.springframework.security.web.access.intercept.FilterSecurityInterceptor" level="DEBUG" additivity="false">
<appender-ref ref="STDOUT"/>
</logger>-->
<!--<logger name="org.springframework.security.web" level="DEBUG" additivity="false">
<appender-ref ref="STDOUT"/>
</logger>-->
<!-- <logger name="c" level="info" additivity="false">-->
<!-- <appender-ref ref="STDOUT"/>-->
<!-- </logger>-->
<root level="info">
<appender-ref ref="console"/>
</root>
</configuration>
resource.properties
cn.fyupeng.nacos.register-addr=localhost:8848
2.2 启动器和文档接口配置
- 启动器
package cn.fyupeng;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
@Slf4j
@SpringBootApplication
@ComponentScan(basePackages = {"cn.fyupeng", "org.n3r.idworker"})
public class FyupengBlogApplication {
public static void main(String[] args) {
SpringApplication.run(FyupengBlogApplication.class, args);
}
}
- 文件接口配置
package cn.fyupeng;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
@Configuration
@EnableSwagger2
public class Swagger2 {
@Bean
public Docket createWebApi(){
return new Docket(DocumentationType.SWAGGER_2)
.groupName("userApi")
.apiInfo(webApiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("cn.fyupeng.controller.user"))
//.paths(Predicates.and(PathSelectors.regex("/.*")))
.paths(PathSelectors.any())
.build();
}
@Bean
public Docket createAdminApi(){
return new Docket(DocumentationType.SWAGGER_2)
.groupName("adminApi")
.apiInfo(adminApiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("cn.fyupeng.controller.admin"))
// .paths(Predicates.and(PathSelectors.regex("/admin/.*")))
.paths(PathSelectors.any())
.build();
}
private ApiInfo webApiInfo(){
return new ApiInfoBuilder()
//设置页面标题
.title("使用swagger2构建RPC分布式博客管理平台后端user-api接口文档")
.contact(new Contact("distributed-blog-api - 仓库","git@github.com:fyupeng/distributed-blog-api/blob/main/README.md","fyp010311@163.com"))
.description("欢迎访问RPC分布式博客管理平台接口文档,本文档描述了博客服务接口定义")
.version("1.0.1")
.build();
}
private ApiInfo adminApiInfo(){
return new ApiInfoBuilder()
//设置页面标题
.title("使用swagger2构建RPC分布式博客管理平台后端admin-api接口文档")
.contact(new Contact("distributed-blog-api - 仓库","git@github.com:fyupeng/distributed-blog-api/blob/main/README.md","fyp010311@163.com"))
.description("欢迎访问RPC分布式博客管理平台接口文档,本文档描述了博客服务接口定义")
.version("1.0.1")
.build();
}
}
2.3 拦截器
package cn.fyupeng.interceptor;
import com.auth0.jwt.JWT;
import com.auth0.jwt.exceptions.JWTDecodeException;
import cn.fyupeng.controller.BasicController;
import cn.fyupeng.annotion.PassToken;
import cn.fyupeng.annotion.UserLoginToken;
import cn.fyupeng.pojo.User;
import cn.fyupeng.service.UserService;
import cn.fyupeng.utils.RedisUtils;
import cn.fyupeng.utils.TokenUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
/**
* @Auther: fyp
* @Date: 2022/8/18
* @Description:
* @Package: cn.fyupeng.controller.interceptor
* @Version: 1.0
*/
@Component
public class LoginInterceptor extends BasicController implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String token = request.getHeader("token");// 从 http 请求头中取出 token
// 如果不是映射到方法直接通过
if (!(handler instanceof HandlerMethod)) {
return true;
}
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod.getMethod();
//检查方法是否有passtoken注解,有则跳过认证,直接通过
if (method.isAnnotationPresent(PassToken.class)) {
PassToken passToken = method.getAnnotation(PassToken.class);
if (passToken.required()) {
return true;
}
}
//检查有没有需要用户权限的注解
if (method.isAnnotationPresent(UserLoginToken.class)) {
UserLoginToken userLoginToken = method.getAnnotation(UserLoginToken.class);
if (userLoginToken.required()) {
// 执行认证
if (token == null) {
throw new RuntimeException("无token,请重新登录");
}
// 获取 token 中的 user id
String userId;
String userName;
String password;
try {
userId = JWT.decode(token).getClaim("userId").asString();
userName = JWT.decode(token).getClaim("username").asString();
password = JWT.decode(token).getClaim("password").asString();
} catch (JWTDecodeException j) {
throw new RuntimeException("The token is incorrect, please do not create the token by illegal means");
}
//查询数据库,看看是否存在此用户,方法要自己写
UserService userServiceProxy = rpcClientProxy.getProxy(UserService.class);
// password 为 MD5 加密密文
User user = userServiceProxy.queryUserForLogin(userName, password);
if (user == null) {
throw new RuntimeException("User does not exist, please log in again");
}
// 验证 token
if (TokenUtils.verify(token)) {
String userRedisSession = RedisUtils.getUserRedisSession(userId);
if(redis.get(userRedisSession) != null)
return true;
return false;
} else {
throw new RuntimeException("Token expired or incorrect, please log in again");
}
}
}
throw new RuntimeException("Annotation without permission will not pass");
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
2.4 过滤器
Sensitiveilter
过滤类
package cn.fyupeng.filter;
import java.io.IOException;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
/**
* @Auther: fyp
* @Date: 2022/4/13
* @Description:
* @Package: com.crop.interceptor
* @Version: 1.0
*/
//敏感词过滤器:利用DFA算法 进行敏感词过滤
public class SensitiveFilter {
private SensitiveWordInit sensitiveWordInit;
//敏感词过滤器:利用DFA算法 进行敏感词过滤
private Map sensitiveWordMap = null;
// 最小匹配规则
public static int minMatchType = 1;
// 最大匹配规则
public static int maxMatchType = 2;
// 单例
private static SensitiveFilter instance = null;
// 构造函数,初始化敏感词库
private SensitiveFilter() throws IOException {
sensitiveWordMap = new SensitiveWordInit().initKeyWord();
}
// 获取单例
public static SensitiveFilter getInstance() throws IOException {
if (null == instance) {
instance = new SensitiveFilter();
}
return instance;
}
// 获取文字中的敏感词
public Set<String> getSensitiveWord(String txt, int matchType) {
Set<String> sensitiveWordList = new HashSet<String>();
for (int i = 0; i < txt.length(); i++) {
// 判断是否包含敏感字符
int length = CheckSensitiveWord(txt, i, matchType);
// 存在,加入list中
if (length > 0) {
sensitiveWordList.add(txt.substring(i, i + length));
// 减1的原因,是因为for会自增
i = i + length - 1;
}
}
return sensitiveWordList;
}
// 替换敏感字字符
public String replaceSensitiveWord(String txt, int matchType,
String replaceChar) {
String resultTxt = txt;
// 获取所有的敏感词
Set<String> set = getSensitiveWord(txt, matchType);
Iterator<String> iterator = set.iterator();
String word = null;
String replaceString = null;
while (iterator.hasNext()) {
word = iterator.next();
replaceString = getReplaceChars(replaceChar, word.length());
resultTxt = resultTxt.replaceAll(word, replaceString);
}
return resultTxt;
}
/**
* 获取替换字符串
*
* @param replaceChar
* @param length
* @return
*/
private String getReplaceChars(String replaceChar, int length) {
String resultReplace = replaceChar;
for (int i = 1; i < length; i++) {
resultReplace += replaceChar;
}
return resultReplace;
}
/**
* 检查文字中是否包含敏感字符,检查规则如下:<br>
* 如果存在,则返回敏感词字符的长度,不存在返回0
* @param txt
* @param beginIndex
* @param matchType
* @return
*/
public int CheckSensitiveWord(String txt, int beginIndex, int matchType) {
// 敏感词结束标识位:用于敏感词只有1位的情况
boolean flag = false;
// 匹配标识数默认为0
int matchFlag = 0;
Map nowMap = sensitiveWordMap;
for (int i = beginIndex; i < txt.length(); i++) {
char word = txt.charAt(i);
// 获取指定key
nowMap = (Map) nowMap.get(word);
// 存在,则判断是否为最后一个
if (nowMap != null) {
// 找到相应key,匹配标识+1
matchFlag++;
// 如果为最后一个匹配规则,结束循环,返回匹配标识数
if ("1".equals(nowMap.get("isEnd"))) {
// 结束标志位为true
flag = true;
// 最小规则,直接返回,最大规则还需继续查找
if (SensitiveFilter.minMatchType == matchType) {
break;
}
}
}
// 不存在,直接返回
else {
break;
}
}
if (SensitiveFilter.maxMatchType == matchType){
if(matchFlag < 2 || !flag){ //长度必须大于等于1,为词
matchFlag = 0;
}
}
if (SensitiveFilter.minMatchType == matchType){
if(matchFlag < 2 && !flag){ //长度必须大于等于1,为词
matchFlag = 0;
}
}
return matchFlag;
}
}
SensitiveWordInit
单词缓存类
package cn.fyupeng.filter;
import org.springframework.core.io.ClassPathResource;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* @Auther: fyp
* @Date: 2022/4/13
* @Description:
* @Package: com.crop.interceptor
* @Version: 1.0
*/
//屏蔽敏感词初始化
@SuppressWarnings({ "rawtypes", "unchecked" })
public class SensitiveWordInit {
// 字符编码
private String ENCODING = "UTF-8";
// 初始化敏感字库
public Map initKeyWord() throws IOException {
// 读取敏感词库 ,存入Set中
Set<String> wordSet = readSensitiveWordFile();
// 将敏感词库加入到HashMap中//确定有穷自动机DFA
return addSensitiveWordToHashMap(wordSet);
}
// 读取敏感词库 ,存入HashMap中
private Set<String> readSensitiveWordFile() throws IOException {
Set<String> wordSet = null;
ClassPathResource classPathResource = new ClassPathResource("static/SensitiveWordList.txt");
InputStream inputStream = classPathResource.getInputStream();
//敏感词库
try {
// 读取文件输入流
InputStreamReader read = new InputStreamReader(inputStream, ENCODING);
// 文件是否是文件 和 是否存在
wordSet = new HashSet<String>();
// StringBuffer sb = new StringBuffer();
// BufferedReader是包装类,先把字符读到缓存里,到缓存满了,再读入内存,提高了读的效率。
BufferedReader br = new BufferedReader(read);
String txt = null;
// 读取文件,将文件内容放入到set中
while ((txt = br.readLine()) != null) {
wordSet.add(txt);
}
br.close();
// 关闭文件流
read.close();
} catch (Exception e) {
e.printStackTrace();
}
return wordSet;
}
// 将HashSet中的敏感词,存入HashMap中
private Map addSensitiveWordToHashMap(Set<String> wordSet) {
// 初始化敏感词容器,减少扩容操作
Map wordMap = new HashMap(wordSet.size());
for (String word : wordSet) {
Map nowMap = wordMap;
for (int i = 0; i < word.length(); i++) {
// 转换成char型
char keyChar = word.charAt(i);
// 获取
Object tempMap = nowMap.get(keyChar);
// 如果存在该key,直接赋值
if (tempMap != null) {
nowMap = (Map) tempMap;
}
// 不存在则,则构建一个map,同时将isEnd设置为0,因为他不是最后一个
else {
// 设置标志位
Map<String, String> newMap = new HashMap<>();
newMap.put("isEnd", "0");
// 添加到集合
nowMap.put(keyChar, newMap);
nowMap = newMap;
}
// 最后一个
if (i == word.length() - 1) {
nowMap.put("isEnd", "1");
}
}
}
return wordMap;
}
}
2.5 配置类
InterceptorConfig
拦截器配置类
package cn.fyupeng.config;
import cn.fyupeng.interceptor.LoginInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* @Auther: fyp
* @Date: 2022/8/18
* @Description: 拦截器
* @Package: cn.fyupeng.controller.config
* @Version: 1.0
*/
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
@Autowired
private LoginInterceptor loginInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
String[] addPathPatterns= {
"/user/**",
"/admin/**"
};
String[] excludePathPatterns={
"/user/login",
"/user/regist"
};
registry.addInterceptor(loginInterceptor).addPathPatterns(addPathPatterns).excludePathPatterns(excludePathPatterns);
}
}
WebMvc
配置类
用于图片的url
映射获取
package cn.fyupeng.config;
import cn.fyupeng.controller.BasicController;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* @Auther: fyp
* @Date: 2022/8/29
* @Description:
* @Package: cn.fyupeng.config
* @Version: 1.0
*/
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler(BasicController.URL_SPACE).addResourceLocations("file:" + BasicController.FILE_SPACE);
WebMvcConfigurer.super.addResourceHandlers(registry);
}
}
2.6 自定义注解
PassToken
免校验注解
package cn.fyupeng.annotion;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @Auther: fyp
* @Date: 2022/8/18
* @Description: 通过Token注解
* @Package: cn.fyupeng.controller.annotion
* @Version: 1.0
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface PassToken {
boolean required() default true;
}
UserLoginToken
用户校验注解
package cn.fyupeng.annotion;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @Auther: fyp
* @Date: 2022/8/18
* @Description: 用户登录Token注解
* @Package: cn.fyupeng.controller.annotion
* @Version: 1.0
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface UserLoginToken {
boolean required() default true;
}
2.7 控制器
- 基类控制器
package cn.fyupeng.controller;
import cn.fyupeng.loadbalancer.RoundRobinLoadBalancer;
import cn.fyupeng.net.netty.client.NettyClient;
import cn.fyupeng.proxy.RpcClientProxy;
import cn.fyupeng.serializer.CommonSerializer;
import cn.fyupeng.utils.RedisOperator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RestController;
import java.io.File;
@RestController
public class BasicController {
private static final RoundRobinLoadBalancer roundRobinLoadBalancer = new RoundRobinLoadBalancer();
private static final NettyClient nettyClient = new NettyClient(roundRobinLoadBalancer, CommonSerializer.KRYO_SERIALIZER);
protected RpcClientProxy rpcClientProxy = new RpcClientProxy(nettyClient);
@Autowired
public RedisOperator redis;
public static final String DATA_NAME = "distributed-blog-data";
//文件保存的命名空间
public static final String FILE_SPACE =
System.getProperties().getProperty("user.home") + File.separator + "webapps" + File.separator + DATA_NAME + File.separator;
public static final String URL_SPACE =
"/" + DATA_NAME + "/" + "**";
//每页分页的记录数
public static final Integer ARTICLE_PAGE_SIZE = 6;
// 评论分页
public static final Integer COMMENT_PAGE_SIZE = 10;
// 图片分页
public static final Integer PICTURE_PAGE_SIZE = 10;
public static final Integer SEARCH_SIZE = 10;
public static final Long ONE_WEEK = 604800000L;
public static final Long TWO_WEEK = 1209600000L;
public static final Long ONE_MONTH = 2592000000L; // 30天
public static final Long ONE_YEAR = 31536000000L;
}
- 用户文章控制器
package cn.fyupeng.controller.user;
import cn.fyupeng.controller.BasicController;
import cn.fyupeng.annotion.PassToken;
import cn.fyupeng.annotion.UserLoginToken;
import cn.fyupeng.filter.SensitiveFilter;
import cn.fyupeng.pojo.Article;
import cn.fyupeng.pojo.Classfication;
import cn.fyupeng.pojo.vo.ArticleVO;
import cn.fyupeng.service.ArticleService;
import cn.fyupeng.service.ClassficationService;
import cn.fyupeng.service.TagService;
import cn.fyupeng.service.UserService;
import cn.fyupeng.utils.BlogJSONResult;
import cn.fyupeng.utils.PagedResult;
import cn.fyupeng.utils.RedisUtils;
import io.swagger.annotations.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.Cursor;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.sql.Timestamp;
import java.util.*;
/**
* @Auther: fyp
* @Date: 2022/4/2
* @Description: 文章 Controller
* @Package: com.crop.controller
* @Version: 1.0
*/
@CrossOrigin
@Slf4j
@RestController
@Api(value = "文章相关业务的接口", tags = {"文章相关业务的controller"})
@RequestMapping(value = "/user/article")
public class UserArticleController extends BasicController {
private TagService tagServiceProxy = rpcClientProxy.getProxy(TagService.class);
private UserService userServiceProxy = rpcClientProxy.getProxy(UserService.class);
private ArticleService articleServiceProxy = rpcClientProxy.getProxy(ArticleService.class);
private ClassficationService classficationServiceProxy = rpcClientProxy.getProxy(ClassficationService.class);
@PassToken
@PostMapping(value = "/getAllArticles")
@ApiOperation(value = "查找文章信息", notes = "查找文章信息的接口")
@ApiImplicitParams({
@ApiImplicitParam(name = "searchKey", value = "搜索关键字", required = true, dataType = "String", paramType = "query"),
@ApiImplicitParam(name = "page", value = "当前页", required = false, dataType = "String", paramType = "query"),
@ApiImplicitParam(name = "pageSize", value = "页大小", required = false, dataType = "String", paramType = "query"),
@ApiImplicitParam(name = "userId", value = "用户id", required = true, dataType = "String", paramType = "query")
})
public BlogJSONResult getAllArticles(String searchKey, Integer page, Integer pageSize, String userId) {
if (StringUtils.isBlank(userId) || StringUtils.isBlank(searchKey)) {
return BlogJSONResult.errorMsg("用户id和搜索关键字不能为空");
}
boolean userIsExist = userServiceProxy.queryUserIdIsExist(userId);
if (!userIsExist) {
return BlogJSONResult.errorMsg("用户id不存在");
}
//非法敏感词汇判断
SensitiveFilter filter = null;
try {
filter = SensitiveFilter.getInstance();
} catch (IOException e) {
e.printStackTrace();
}
int n = filter.CheckSensitiveWord(searchKey,0,1);
if(n > 0){ //存在非法字符
log.info("用户[{}]使用非法字符[{}]进行检索--",userId, searchKey);
Set<String> sensitiveWord = filter.getSensitiveWord(searchKey, 1);
return BlogJSONResult.errorMsg("捕捉敏感关键字: " + sensitiveWord);
}
// 进行 热度 维护
incrementArticleScore(searchKey);
boolean addTrue = addSearchHistory(userId, searchKey);
if (!addTrue) {
log.info("已存在key:{}",searchKey);
}
//前端不传该参时会初始化
if(page == null){
page = 1;
}
//前端不传该参时会初始化
if(pageSize == null){
pageSize = ARTICLE_PAGE_SIZE;
}
List<Article> articleList = null;
Classfication classfication = new Classfication();
classfication.setName(searchKey);
Classfication cf = classficationServiceProxy.queryClassfication(classfication);
Article article = new Article();
if (cf != null) {
article.setClassId(cf.getId());
} else {
article.setTitle(searchKey);
article.setSummary(searchKey);
article.setContent(searchKey);
}
// 优先 匹配 分类 id
// 第二 优先 匹配 标题
// 第三 匹配 摘要
// 第四 优先 匹配 内容
PagedResult pageResult = articleServiceProxy.queryArticleSelective(article, page,pageSize);
return BlogJSONResult.ok(pageResult);
}
/**
* 策略:
* 1. 查询范围:最近一周 数据
* 2. 排序方式:时间 最近排序
* @param page
* @param pageSize
* @return
*/
@PassToken
@PostMapping(value = "/getRecentArticles")
@ApiOperation(value = "获取最近文章", notes = "获取最近文章的接口")
@ApiImplicitParams({
@ApiImplicitParam(name = "page", value = "当前页", dataType = "String", paramType = "query"),
@ApiImplicitParam(name = "pageSize", value = "页大小", dataType = "String", paramType = "query"),
})
public BlogJSONResult getRecentArticles(Integer page, Integer pageSize) {
//前端不传该参时会初始化
if(page == null){
page = 1;
}
//前端不传该参时会初始化
if(pageSize == null){
pageSize = ARTICLE_PAGE_SIZE;
}
// 最大 匹配 一 礼拜前的 文章
Long timeDifference = ONE_WEEK; // 一周
PagedResult pageResult = articleServiceProxy.queryArticleByTime(timeDifference,page,pageSize);
return BlogJSONResult.ok(pageResult);
}
/**
* 策略:
* 1. 更新频率: 每小时
* 2. 更新策略: 用户搜索率最高(过滤敏感信息)
* @param page
* @param pageSize
* @return
*/
@PassToken
@PostMapping(value = "/getHotArticles")
@ApiOperation(value = "获取推荐文章", notes = "获取推荐文章的接口")
@ApiImplicitParams({
@ApiImplicitParam(name = "page", value = "当前页", dataType = "String", paramType = "query"),
@ApiImplicitParam(name = "pageSize", value = "页大小", dataType = "String", paramType = "query")
})
public BlogJSONResult getHotArticles(Integer page, Integer pageSize) {
List<String> hotSearchKeyList = getHotSearchKey(null, null);
随机从十条数据中拿出一条来搜索
//Random random = new Random();
//int index = random.nextInt(hotSearchKeyList.size());
/**
* 保证 1小时 之内查询的数据一致,1小时更新一次
*/
int seed = Calendar.HOUR_OF_DAY;
// 保证 不越过 数组长度
if (hotSearchKeyList.size() == 0) {
return BlogJSONResult.ok("缓存数据库中无缓存");
}
seed %= hotSearchKeyList.size();
String searchKey = hotSearchKeyList.get(seed);
//前端不传该参时会初始化
if(page == null){
page = 1;
}
//前端不传该参时会初始化
if(pageSize == null){
pageSize = ARTICLE_PAGE_SIZE;
}
List<Article> articleList = null;
Classfication classfication = new Classfication();
classfication.setName(searchKey);
Classfication cf = classficationServiceProxy.queryClassfication(classfication);
Article article = new Article();
if (cf != null) {
article.setClassId(cf.getId());
} else {
article.setTitle(searchKey);
article.setSummary(searchKey);
article.setContent(searchKey);
}
// 优先 匹配 分类 id
// 第二 优先 匹配 标题
// 第三 匹配 摘要
// 第四 优先 匹配 内容
PagedResult pageResult = articleServiceProxy.queryArticleSelective(article, page,pageSize);
return BlogJSONResult.ok(pageResult);
}
@UserLoginToken
@PostMapping(value = "/getSearchHistory")
@ApiOperation(value = "获取搜索历史记录", notes = "获取搜索历史记录的接口")
@ApiImplicitParam(name = "userId", value = "用户id",required = true, dataType = "String", paramType = "query")
public BlogJSONResult getSearchHistory(String userId) {
List<String> searchHistoryList = querySearchHistory(userId);
return BlogJSONResult.ok(searchHistoryList);
}
@UserLoginToken
@PostMapping(value = "/removeHistory")
@ApiOperation(value = "删除搜索历史记录", notes = "删除搜索历史记录的接口")
@ApiImplicitParams({
@ApiImplicitParam(name = "userId", value = "用户id",required = true, dataType = "String", paramType = "query"),
@ApiImplicitParam(name = "searchKey", value = "搜索关键字",required = true, dataType = "String", paramType = "query")
})
public BlogJSONResult removeHistory(String userId, String searchKey) {
if (StringUtils.isBlank(userId) || StringUtils.isBlank(searchKey)) {
return BlogJSONResult.errorMsg("用户id和搜索关键字不能为空");
}
Long history = delSearchHistory(userId, searchKey);
return BlogJSONResult.ok(history);
}
/**
* 添加个人 历史数据
* @param userId
* @param searchKey
* @return false: 已存在 - true : 添加成功
*/
private boolean addSearchHistory(String userId, String searchKey) {
String searchHistoryKey = RedisUtils.getSearchHistoryKey(userId);
boolean keyIsExist = redis.hasKey(searchHistoryKey);
if (keyIsExist) {
String hk = redis.hget(searchHistoryKey, searchKey);
// 关键字 key 存在
if (hk != null) {
return false;
// 关键 key 不存在
} else {
redis.hset(searchHistoryKey, searchKey, "1");
}
// 首次 会 创建 包含 userId 的 key
} else {
redis.hset(searchHistoryKey, searchKey, "1");
}
return true;
}
/**
* 获取个人 历史数据
* @param userId
* @return null : 没有数据 - List<String> ${ketList}
*/
private List<String> querySearchHistory(String userId) {
String searchHistoryKey = RedisUtils.getSearchHistoryKey(userId);
boolean keyExist = redis.hasKey(searchHistoryKey);
if (keyExist) {
Cursor<Map.Entry<Object, Object>> cursor = redis.hscan(searchHistoryKey);
List<String> keyList = new ArrayList<>();
while (cursor.hasNext()) {
Map.Entry<Object, Object> current = cursor.next();
String key = current.getKey().toString();
keyList.add(key);
}
return keyList;
}
return null;
}
/**
* 删除个人 历史数据
* @param userId
* @param searchKey
* @return
*/
private Long delSearchHistory(String userId, String searchKey) {
String searchHistoryKey = RedisUtils.getSearchHistoryKey(userId);
return redis.hdel(searchHistoryKey, searchKey);
}
private void incrementArticleScore(String searchKey) {
/**
* 规则: key: search-score
* value: java-1, linux-2 python-3
*/
String searchScoreKey = RedisUtils.getSearchScoreKey();
Long now = System.currentTimeMillis();
// 只需第一次设置 时间戳
if (redis.zScore(searchScoreKey, searchKey) == null) {
// 统计 时间戳
/**
* 规则: key: search-score:java
* value: 时间戳
*/
String keyWithSearchKey = RedisUtils.getSearchScoreKeyWithSearchKey(searchKey);
redis.set(keyWithSearchKey, String.valueOf(now));
}
// 统计 点击量
redis.zIncrementScore(searchScoreKey, searchKey, 1);
}
@PassToken
@PostMapping(value = "/getHotSearchKey")
@ApiOperation(value = "获取关键字热度", notes = "获取关键字热度的接口")
@ApiImplicitParams({
@ApiImplicitParam(name = "searchKey", value = "搜索关键字-缺省则匹配热度", dataType = "String", paramType = "query"),
@ApiImplicitParam(name = "size", value = "检索数-缺省为10", dataType = "String", paramType = "query")
})
public BlogJSONResult getHotPot(String searchKey, Integer size) {
if (StringUtils.isNotBlank(searchKey)) {
//非法敏感词汇判断
SensitiveFilter filter = null;
try {
filter = SensitiveFilter.getInstance();
} catch (IOException e) {
e.printStackTrace();
}
int n = filter.CheckSensitiveWord(searchKey,0,1);
if(n > 0){ //存在非法字符
log.info("使用非法字符[{}]进行检索热度--",searchKey);
Set<String> sensitiveWord = filter.getSensitiveWord(searchKey, 1);
return BlogJSONResult.ok(sensitiveWord);
}
}
List<String> hotSearchKeyList = getHotSearchKey(searchKey, size);
return BlogJSONResult.ok(hotSearchKeyList);
}
/**
* 搜索引擎
* @param searchKey
* @param size
* @return
*/
public List<String> getHotSearchKey(String searchKey, Integer size) {
if (size == null) {
size = SEARCH_SIZE;
}
Long now = System.currentTimeMillis();
String searchScoreKey = RedisUtils.getSearchScoreKey();
Set<String> allKeys = redis.zRevRangeByScore(searchScoreKey, 0, Double.MAX_VALUE);
List<String> resultList = new ArrayList<>();
if (!StringUtils.isBlank(searchKey)) {
for (String key : allKeys) {
// 在所有的 key 中包含 用户输入的 ${searchKey}
if (StringUtils.containsIgnoreCase(key, searchKey)) {
// 记录数 已达 期待数,停止检索
if (resultList.size() >= size) {
break;
}
// 规则:与 新增 关键字 时做的 时间戳 key 值相对应
String searchScoreKeyWithSearchKey = RedisUtils.getSearchScoreKeyWithSearchKey(key);
String timeStamp = redis.get(searchScoreKeyWithSearchKey);
if (timeStamp != null) {
Long time = Long.valueOf(timeStamp);
// 查询 最近一个礼拜的 数据
if ((now - time) < 604800000L) {
resultList.add(key);
} else {
redis.zset(searchScoreKey, key, 0);
}
}
}
}
} else {
for (String key : allKeys) {
// 在所有的 key 中包含 用户输入的 ${searchKey}
// 记录数 已达 期待数,停止检索
if (resultList.size() >= size) {
break;
}
// 规则:与 新增 关键字 时做的 时间戳 key 值相对应
String searchScoreKeyWithSearchKey = RedisUtils.getSearchScoreKeyWithSearchKey(key);
String timeStamp = redis.get(searchScoreKeyWithSearchKey);
if (timeStamp != null) {
Long time = Long.valueOf(timeStamp);
// 查询 最近一个礼拜的 数据
if ((now - time) < 604800000L) {
resultList.add(key);
} else {
redis.zset(searchScoreKey, key, 0);
}
}
}
}
return resultList;
}
@PassToken
@PostMapping(value = "/getArticleDetail")
@ApiOperation(value = "获取文章详细信息", notes = "获取文章详细信息的接口")
@ApiImplicitParams({
@ApiImplicitParam(name = "articleId", value = "文章id", required = true, dataType = "String", paramType = "query"),
//@ApiImplicitParam(name = "request",value = "请求", dataType = "HttpServletRequest", readOnly = true)
})
public BlogJSONResult getArticleDetail(String articleId, @ApiParam(hidden = true)HttpServletRequest request) {
if (StringUtils.isBlank(articleId)) {
return BlogJSONResult.errorMsg("文章id不能为空");
}
Article article = new Article();
article.setId(articleId);
ArticleVO articleVO = articleServiceProxy.queryArticleDetail(articleId);
// 文章 id 不存在
if (articleVO == null) {
return BlogJSONResult.ok("articleId不存在");
}
String articleView = RedisUtils.getIdView(articleVO.getId(), request);
String articleViewCount = RedisUtils.getIdViewCount(articleVO.getId());
//用户短时间 已 访问,redis.get(userView) 返回值是 对象,不能 通过 ""判断
if (redis.get(articleView) != null) {
return BlogJSONResult.ok(articleVO);
}
/**
* 统计量 一小时 内 同一个 ip 地址 只能算 1 次 阅读
*/
redis.set(articleView, "1", 60 * 60);
/**
* articleViewCount 如果 未 初始化,redis 会 初始化为 0
*/
redis.incr(articleViewCount,1);
return BlogJSONResult.ok(articleVO);
}
@UserLoginToken
@PostMapping(value = "/saveArticle")
@ApiOperation(value = "保存文章信息 - id 字段请忽略", notes = "保存文章信息的接口")
@ApiImplicitParam(name = "article", value = "文章", required = true, dataType = "Article", paramType = "body")
public BlogJSONResult saveArticle(@RequestBody Article article) {
if(StringUtils.isBlank(article.getUserId()) || StringUtils.isBlank(article.getTitle())
|| StringUtils.isBlank(article.getSummary() ) || StringUtils.isBlank(article.getClassId())
|| StringUtils.isBlank(article.getContent())){
return BlogJSONResult.errorMsg("用户id、标题、摘要、分类id和内容不能为空");
}
boolean userIdIsExist = userServiceProxy.queryUserIdIsExist(article.getUserId());
if (!userIdIsExist) {
return BlogJSONResult.errorMsg("用户id不存在");
}
boolean classficationIsExist = classficationServiceProxy.queryClassficationIdIsExist(article.getClassId());
if (!classficationIsExist) {
return BlogJSONResult.errorMsg("分类id不存在");
}
boolean saveIsTrue = articleServiceProxy.save(article);
return saveIsTrue ? BlogJSONResult.ok() : BlogJSONResult.errorMsg("内部错误导致保存失败");
}
@UserLoginToken
@PostMapping(value = "/updateArticle")
@ApiOperation(value = "更新文章信息", notes = "更新文章信息的接口")
@ApiImplicitParam(name = "article", value = "文章", required = true, dataType = "Article", paramType = "body")
public BlogJSONResult updateArticle(@RequestBody Article article) {
if (StringUtils.isBlank(article.getId()) || StringUtils.isBlank(article.getUserId())) {
return BlogJSONResult.errorMsg("articleId 和 userId 不能为空");
}
Article articleWithIdAndUserId = new Article();
articleWithIdAndUserId.setId(article.getId());
articleWithIdAndUserId.setUserId(article.getUserId());
// 文章id 属于 用户id
boolean articleIsUser = articleServiceProxy.queryArticleIsUser(articleWithIdAndUserId);
if (articleIsUser) {
Article ac = new Article();
ac.setId(article.getId());
ac.setUserId(article.getUserId());
// 文章 标题 要更新
if (StringUtils.isNotBlank(article.getTitle())) {
ac.setTitle(article.getTitle());
}
// 文章 摘要 要更新
if (StringUtils.isNotBlank(article.getSummary())) {
ac.setSummary(article.getSummary());
}
// 文章 内容 要更新
if (StringUtils.isNotBlank(article.getContent())) {
ac.setContent(article.getContent());
}
// 文章 分类 要更新
if (StringUtils.isNotBlank(article.getClassId())) {
boolean classficationIdIsExist = classficationServiceProxy.queryClassficationIdIsExist(article.getClassId());
if (!classficationIdIsExist)
return BlogJSONResult.errorMsg("分类id不存在");
ac.setClassId(article.getClassId());
}
boolean updateIsTrue = articleServiceProxy.saveWithIdAndUserId(ac);
return updateIsTrue ? BlogJSONResult.ok() : BlogJSONResult.errorMsg("内部错误更新失败");
}
return BlogJSONResult.errorMsg("用户 id 与 articleId 不存在或匹配失败");
}
@UserLoginToken
@PostMapping(value = "/removeArticle")
@ApiOperation(value = "删除文章", notes = "删除文章的接口")
@ApiImplicitParams({
@ApiImplicitParam(name = "articleId", value = "文章id", required = true, dataType = "String", paramType = "query"),
@ApiImplicitParam(name = "userId", value = "用户id", required = true, dataType = "String", paramType = "query")
})
public BlogJSONResult removeArticle(String articleId, String userId) {
if (StringUtils.isBlank(articleId) || StringUtils.isBlank(userId)) {
return BlogJSONResult.errorMsg("articleId或userId不能为空");
}
Article article = new Article();
article.setId(articleId);
article.setUserId(userId);
boolean articleIsUser = articleServiceProxy.queryArticleIsUser(article);
if (!articleIsUser) {
return BlogJSONResult.errorMsg("articleId不存在或者userId与commentId约束的userId不同");
}
/**
* 删除文章 以及删除 与 文章 id 关联的 其他表数据
*/
articleServiceProxy.removeArticle(articleId);
return BlogJSONResult.ok();
}
@PassToken
@PostMapping(value = "/getAllClassfications")
@ApiOperation(value = "获取文章分类信息", notes = "获取文章分类信息的接口")
public BlogJSONResult getAllClassfications() {
List<Classfication> classficationList = classficationServiceProxy.queryAllClassfications();
return BlogJSONResult.ok(classficationList);
}
}
- 用户控制器
package cn.fyupeng.controller.user;
import cn.fyupeng.controller.BasicController;
import cn.fyupeng.annotion.PassToken;
import cn.fyupeng.annotion.UserLoginToken;
import cn.fyupeng.pojo.User;
import cn.fyupeng.pojo.UserInfo;
import cn.fyupeng.pojo.vo.UserForUpdateVO;
import cn.fyupeng.pojo.vo.UserInfoVO;
import cn.fyupeng.service.UserService;
import cn.fyupeng.utils.BlogJSONResult;
import cn.fyupeng.utils.MD5Utils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
@CrossOrigin
@RestController
@RequestMapping(value = "/user")
@Api(value = "用户相关业务的接口", tags = {"用户相关业务的controller"})
public class UserController extends BasicController {
UserService userServiceProxy = rpcClientProxy.getProxy(UserService.class);
@PassToken
@GetMapping(value = "/pingNetWork")
@ApiOperation(value = "测试网络环境", notes = "测试网络环境的接口")
public BlogJSONResult pingNetWork() {
return BlogJSONResult.build(200, "ping successful!", null);
}
@PassToken
@ApiOperation(value = "查询用户信息", notes = "查询用户信息的接口")
@ApiImplicitParam(name = "userId", value = "用户id", required = true, dataType = "String", paramType = "query")
@PostMapping(value = "/query")
public BlogJSONResult query(String userId) {
if(StringUtils.isBlank(userId)){
return BlogJSONResult.errorMsg("用户id不能为空");
}
UserInfo userInfo = userServiceProxy.queryUserInfo(userId);
UserInfoVO userInfoVO = new UserInfoVO();
if (userInfo != null) {
BeanUtils.copyProperties(userInfo,userInfoVO);
User user = userServiceProxy.queryUser(userId);
userInfoVO.setPermission(user.getPermission());
}
return BlogJSONResult.ok(userInfoVO);
}
@UserLoginToken
@ApiOperation(value = "完善个人信息 - id字段请忽略", notes = "完善个人信息的接口")
@ApiImplicitParam(name = "userInfo", value = "用户详情", required = true, dataType = "UserInfo", paramType = "body")
@PostMapping(value = "/completeUserInfo")
public BlogJSONResult completeUserInfo(@RequestBody UserInfo userInfo) {
if (StringUtils.isBlank(userInfo.getUserId())) {
return BlogJSONResult.errorMsg("用户id不能为空");
}
UserInfo userInfoByUserId = userServiceProxy.queryUserInfo(userInfo.getUserId());
if (userInfoByUserId == null) {
return BlogJSONResult.errorMsg("用户id不存在");
}
// id 是唯一标识符,不可更改
userInfo.setId(userInfoByUserId.getId());
userServiceProxy.updateUserInfo(userInfo);
return BlogJSONResult.ok();
}
@UserLoginToken
@ApiOperation(value = "用户上传头像", notes = "用户上传头像的接口")
@ApiImplicitParam(name = "userId", value = "用户id", required = true, dataType = "String", paramType = "form")
@PostMapping(value = "/uploadFace", headers = "content-type=multipart/form-data")
public BlogJSONResult uploadFace(@RequestParam(value = "userId") String userId,
@RequestParam(value = "file") /* 这两个注解不能搭配使用,会导致 文件上传按钮失效*/
/*@ApiParam(value = "头像")*/ MultipartFile file) throws Exception{
if(StringUtils.isBlank(userId)){
return BlogJSONResult.errorMsg("用户id不能为空");
}
if (!userServiceProxy.queryUserIdIsExist(userId)) {
return BlogJSONResult.errorMsg("用户id不存在");
}
//保存到数据库中的相对路径
String uploadPathDB = "/" + userId + "/face";
FileOutputStream fileOutputStream = null;
InputStream inputStream = null;
try {
if(file != null){
String fileName = file.getOriginalFilename();
if (StringUtils.isNotBlank(fileName)) {
//文件上传的最终保存路径
String finalFacePath = FILE_SPACE + uploadPathDB + "/" + fileName;
//设置数据库保存的路径
uploadPathDB += ("/" + fileName);
File outFile = new File(finalFacePath);
//创建用户文件夹
if (outFile.getParentFile() != null && !outFile.getParentFile().isDirectory()) {
//创建父文件夹
outFile.getParentFile().mkdirs();
}
fileOutputStream = new FileOutputStream(outFile);
inputStream = file.getInputStream();
IOUtils.copy(inputStream, fileOutputStream);//把输入流赋值给输出流,就是把图片复制到输出流对应的路径下
}
}else {
return BlogJSONResult.errorMsg("上传出错1....");
}
} catch (IOException e) {
e.printStackTrace();
return BlogJSONResult.errorMsg("上传出错2....");
}finally {
if(fileOutputStream != null){
fileOutputStream.flush();
fileOutputStream.close();
}
}
/**
* User 与 UserInfo 是 一一对应的关系,UserInfo 有两个候选键 id 和 userId
*/
UserInfo userInfo = new UserInfo();
userInfo.setUserId(userId);
userInfo.setAvatar(uploadPathDB);
userServiceProxy.updateUserInfo(userInfo);
return BlogJSONResult.ok(uploadPathDB);
}
@UserLoginToken
@ApiOperation(value = "用户修改密码", notes = "用户修改密码的接口")
@ApiImplicitParam(name = "userVO", value = "用户id", required = true, dataType = "UserForUpdateVO", paramType = "body")
@PostMapping(value = "/updatePassword")
public BlogJSONResult updatePassword(@RequestBody UserForUpdateVO user) throws Exception {
if (StringUtils.isBlank(user.getUsername()) || StringUtils.isBlank(user.getOldPassword())) {
return BlogJSONResult.errorMsg("用户名和原密码不能为空");
}
if (StringUtils.isBlank(user.getNewPassword())) {
return BlogJSONResult.errorMsg("新密码不能为空");
}
User userResult = userServiceProxy.queryUserForLogin(user.getUsername(), MD5Utils.getMD5Str(user.getOldPassword()));
if (userResult == null) {
BlogJSONResult.errorMsg("用户名或原密码不正确");
}
User userForUpdate = new User();
userForUpdate.setId(userResult.getId());
userForUpdate.setUsername(user.getUsername());
userForUpdate.setPassword(MD5Utils.getMD5Str(user.getNewPassword()));
userForUpdate.setPermission(userResult.getPermission());
boolean updateTrue = userServiceProxy.updateUser(userForUpdate);
userForUpdate.setPassword("");
return updateTrue ? BlogJSONResult.ok(userForUpdate) : BlogJSONResult.errorMsg("修改失败");
}
}
- 用户标签控制器
package cn.fyupeng.controller.user;
import cn.fyupeng.annotion.UserLoginToken;
import cn.fyupeng.pojo.Articles2tags;
import cn.fyupeng.pojo.Tag;
import cn.fyupeng.pojo.vo.ArticleVO;
import cn.fyupeng.pojo.vo.Articles2tagsVO;
import cn.fyupeng.pojo.vo.TagVO;
import cn.fyupeng.service.*;
import cn.fyupeng.controller.BasicController;
import cn.fyupeng.utils.BlogJSONResult;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* @Auther: fyp
* @Date: 2022/4/8
* @Description:
* @Package: com.crop.user.controller
* @Version: 1.0
*/
@CrossOrigin
@Slf4j
@RestController
@Api(value = "标签相关业务的接口", tags = {"标签相关业务的controller"})
@RequestMapping(value = "/user/tag")
public class UserTagController extends BasicController {
private TagService tagServiceProxy = rpcClientProxy.getProxy(TagService.class);
private UserService userServiceProxy = rpcClientProxy.getProxy(UserService.class);
private ArticleService articleServiceProxy = rpcClientProxy.getProxy(ArticleService.class);
@UserLoginToken
@PostMapping(value = "/getTag")
@ApiOperation(value = "获取标签", notes = "获取标签的接口")
@ApiImplicitParam(name = "tagId", value = "标签id", required = true, dataType = "String", paramType = "query")
public BlogJSONResult getTag(String tagId) {
if (StringUtils.isBlank(tagId)) {
return BlogJSONResult.errorMsg("标签id不能为空");
}
Tag tagList = tagServiceProxy.queryTag(tagId);
return BlogJSONResult.ok(tagList);
}
@UserLoginToken
@PostMapping(value = "/getAllTags")
@ApiOperation(value = "获取所有标签", notes = "获取所有标签的接口")
@ApiImplicitParam(name = "userId", value = "用户id", required = true, dataType = "String", paramType = "query")
public BlogJSONResult getAllTags(String userId) {
Tag tagWithUserId = new Tag();
tagWithUserId.setUserId(userId);
List<TagVO> tagVOList = tagServiceProxy.queryAllTags(tagWithUserId);
return BlogJSONResult.ok(tagVOList);
}
@UserLoginToken
@PostMapping(value = "/saveTag")
@ApiOperation(value = "保存标签 - id字段请忽略", notes = "保存标签的接口")
@ApiImplicitParam(name = "tag", value = "标签", required = true, dataType = "Tag", paramType = "body")
public BlogJSONResult saveTag(@RequestBody Tag tag) {
if (StringUtils.isBlank(tag.getName()) || StringUtils.isBlank(tag.getUserId())) {
return BlogJSONResult.errorMsg("标签名或用户id不能为空");
}
tag.setId(null);
/**
* id 是候选键 - 标签名 + userId 也是候选键,能唯一 识别 标签
*/
if (tagServiceProxy.queryTagIsExist(tag)) {
return BlogJSONResult.errorMsg("标签名已存在");
}
boolean saveIsTrue = tagServiceProxy.saveTag(tag);
return saveIsTrue ? BlogJSONResult.ok(): BlogJSONResult.errorMsg("内部错误导致保存失败");
}
@UserLoginToken
@PostMapping(value = "/updateTag")
@ApiOperation(value = "更新标签 - userId字段请忽略", notes = "更新标签的接口")
@ApiImplicitParam(name = "tag", value = "标签", required = true, dataType = "Tag", paramType = "body")
public BlogJSONResult updateTag(@RequestBody Tag tag) {
if (StringUtils.isBlank(tag.getId()) || StringUtils.isBlank(tag.getName())) {
return BlogJSONResult.errorMsg("标签id或标签名不能为空");
}
// 用來 防止 其他 用户 对非本地 标签的 更改
if (StringUtils.isBlank(tag.getUserId())) {
return BlogJSONResult.errorMsg("用户id不能为空");
}
Tag tagWithIdAndUserId = new Tag();
tagWithIdAndUserId.setId(tag.getId());
tagWithIdAndUserId.setUserId(tag.getUserId());
if (!tagServiceProxy.queryTagIsExist(tagWithIdAndUserId)) {
return BlogJSONResult.errorMsg("该用户没有该标签");
}
boolean updateIsTrue = tagServiceProxy.updateTag(tag);
return updateIsTrue ? BlogJSONResult.ok() : BlogJSONResult.errorMsg("内部错误");
}
@UserLoginToken
@PostMapping(value = "/removeTag")
@ApiOperation(value = "删除标签 - 连同已标记的文章标签一并删除", notes = "删除标签签的接口")
@ApiImplicitParam(name = "tagId", value = "标签id", required = true, dataType = "String", paramType = "query")
public BlogJSONResult removeTag(String tagId) {
if (StringUtils.isBlank(tagId)) {
return BlogJSONResult.errorMsg("tagId不能为空");
}
// 1. 移除 Tag
// 2. 移除 Articles2Tags
boolean delTagIsTrue = tagServiceProxy.deleteTagAndArticleTagWithTagId(tagId);
if (!delTagIsTrue) {
return BlogJSONResult.errorMsg("标签id不存在或内部错误导致删除失败");
}
return BlogJSONResult.ok();
}
@UserLoginToken
@PostMapping(value = "/getArticleWithNoneTag")
@ApiOperation(value = "获取无标签文章", notes = "获取无标签文章的接口")
@ApiImplicitParam(name = "userId", value = "用户id", required = true, dataType = "String", paramType = "query")
public BlogJSONResult getArticleWithNoneTag(String userId) {
if (StringUtils.isBlank(userId)) {
return BlogJSONResult.errorMsg("用户id不能为空");
}
if (!userServiceProxy.queryUserIdIsExist(userId)) {
return BlogJSONResult.errorMsg("用户id不存在");
}
List<ArticleVO> articleVOList = articleServiceProxy.queryArticleWithNoneTagByUser(userId);
return BlogJSONResult.ok(articleVOList);
}
@UserLoginToken
@PostMapping(value = "/getArticleTag")
@ApiOperation(value = "获取标签文章", notes = "获取标签文章的接口")
@ApiImplicitParam(name = "tagId", value = "标签id", required = true, dataType = "String", paramType = "query")
public BlogJSONResult getArticleTag(String tagId) {
if (StringUtils.isBlank(tagId)) {
return BlogJSONResult.errorMsg("标签id不能为空");
}
Articles2tags articles2tagsWithTagId = new Articles2tags();
articles2tagsWithTagId.setTagId(tagId);
List<Articles2tagsVO> articles2tagsVOList = tagServiceProxy.queryArticleTag(articles2tagsWithTagId);
return BlogJSONResult.ok(articles2tagsVOList);
}
@UserLoginToken
@PostMapping(value = "/markArticleTag")
@ApiOperation(value = "标记文章标签 - id字段请忽略", notes = "标记文章标签的接口")
@ApiImplicitParam(name = "articles2tags", value = "文章标签关联", required = true, dataType = "Articles2tags", paramType = "body")
public BlogJSONResult markArticleTag(@RequestBody Articles2tags articles2tags) {
if (StringUtils.isBlank(articles2tags.getArticleId()) || StringUtils.isBlank(articles2tags.getTagId())) {
return BlogJSONResult.errorMsg("文章id或标签id不能为空");
}
boolean articleIsExist = articleServiceProxy.queryArticleIsExist(articles2tags.getArticleId());
Tag tagWithId = new Tag();
tagWithId.setId(articles2tags.getTagId());
boolean tagIsExist = tagServiceProxy.queryTagIsExist(tagWithId);
if (!articleIsExist || !tagIsExist) {
return BlogJSONResult.errorMsg("文章id或标签id不存在");
}
articles2tags.setId(null);
// 已标记 的 不能 重复标记 - 文章id和标签id 也是一组 候选键
if (tagServiceProxy.queryArticleTagIsExist(articles2tags)) {
return BlogJSONResult.errorMsg("关联id已存在,不可重复标记");
}
// 标记 Articles2Tags
boolean saveIsTrue = tagServiceProxy.saveArticleTag(articles2tags);
return saveIsTrue ? BlogJSONResult.ok() : BlogJSONResult.errorMsg("内部错误导致保存失败");
}
@UserLoginToken
@PostMapping(value = "/reMarkArticleTag")
@ApiOperation(value = "重新标记文章标签", notes = "重新标记文章标签的接口")
@ApiImplicitParam(name = "articles2tags", value = "文章标签关联", required = true, dataType = "Articles2tags", paramType = "body")
public BlogJSONResult reMarkArticleTag(@RequestBody Articles2tags articles2tags) {
if (StringUtils.isBlank(articles2tags.getId())) {
return BlogJSONResult.errorMsg("文章标签关联id不能为空");
}
// 重新标记 Articles2Tags
if (StringUtils.isBlank(articles2tags.getArticleId()) || StringUtils.isBlank(articles2tags.getTagId())) {
return BlogJSONResult.errorMsg("文章id或标签id不能为空");
}
boolean articleIsExist = articleServiceProxy.queryArticleIsExist(articles2tags.getArticleId());
Tag tagWithId = new Tag();
tagWithId.setId(articles2tags.getTagId());
boolean tagIsExist = tagServiceProxy.queryTagIsExist(tagWithId);
if (!articleIsExist || !tagIsExist) {
return BlogJSONResult.errorMsg("文章id或标签id不存在");
}
// 已标记 的 才可以更新
if (!tagServiceProxy.queryArticleTagIsExist(articles2tags.getId())) {
return BlogJSONResult.errorMsg("关联id不存在");
}
// 标记 Articles2Tags
boolean saveIsTrue = tagServiceProxy.updateArticleTag(articles2tags);
return saveIsTrue ? BlogJSONResult.ok() : BlogJSONResult.errorMsg("内部错误导致保存失败");
}
}
- 用户评论控制器
package cn.fyupeng.controller.user;
import cn.fyupeng.annotion.PassToken;
import cn.fyupeng.annotion.UserLoginToken;
import cn.fyupeng.enums.CommentStatus;
import cn.fyupeng.pojo.Comment;
import cn.fyupeng.pojo.User;
import cn.fyupeng.service.*;
import cn.fyupeng.utils.BlogJSONResult;
import cn.fyupeng.utils.PagedResult;
import cn.fyupeng.controller.BasicController;
import cn.fyupeng.service.ArticleService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.*;
/**
* @Auther: fyp
* @Date: 2022/4/8
* @Description:
* @Package: com.crop.user.controller
* @Version: 1.0
*/
@CrossOrigin
@Slf4j
@RestController
@Api(value = "评论相关业务的接口", tags = {"评论相关业务的controller"})
@RequestMapping(value = "/user/comment")
public class UserCommentController extends BasicController {
private TagService tagServiceProxyProxy = rpcClientProxy.getProxy(TagService.class);
private UserService userServiceProxy = rpcClientProxy.getProxy(UserService.class);
private ArticleService articleServiceProxy = rpcClientProxy.getProxy(ArticleService.class);
private ClassficationService classficationServiceProxy = rpcClientProxy.getProxy(ClassficationService.class);
private CommentService commentServiceProxy = rpcClientProxy.getProxy(CommentService.class);
@PassToken
@PostMapping(value = "/getAllComments")
@ApiOperation(value = "获取文章所有评论", notes = "获取文章所有评论的接口")
@ApiImplicitParams({
@ApiImplicitParam(name = "articleId", value = "文章id", required = true, dataType = "String", paramType = "query"),
@ApiImplicitParam(name = "page", value = "当前页", dataType = "Integer", paramType = "query"),
@ApiImplicitParam(name = "pageSize", value = "页数", dataType = "Integer", paramType = "query"),
@ApiImplicitParam(name = "sort", value = "排序-1-缺省[正序时间]-2[倒序时间]", dataType = "Integer", paramType = "query")
})
public BlogJSONResult getAllComments(String articleId, Integer page, Integer pageSize, Integer sort) {
if (StringUtils.isBlank(articleId)) {
return BlogJSONResult.errorMsg("文章id不能为空");
}
//前端不传该参时会初始化
if(page == null){
page = 1;
}
//前端不传该参时会初始化
if(pageSize == null){
pageSize = COMMENT_PAGE_SIZE;
}
if (sort == null) {
sort = 1;
}
PagedResult pageResult = commentServiceProxy.queryAllComments(articleId, page, pageSize, sort);
return BlogJSONResult.ok(pageResult);
}
@UserLoginToken
@PostMapping(value = "saveComment")
@ApiOperation(value = "发表文章评论", notes = "发表文章评论的接口")
@ApiImplicitParam(name = "comment", value = "评论", required = true, dataType = "Comment", paramType = "body")
public BlogJSONResult saveComment(@RequestBody Comment comment) {
if (StringUtils.isBlank(comment.getArticleId())) {
return BlogJSONResult.errorMsg("articleId不能为空");
}
if (StringUtils.isBlank(comment.getFromUserId())) {
return BlogJSONResult.errorMsg("留言者userId不能为空");
}
if (StringUtils.isBlank(comment.getComment())) {
return BlogJSONResult.errorMsg("评论内容comment不能为空");
}
// fateherCommentId 是可以为 null 但是 不能是 空串 或 空白串
if (comment.getFatherCommentId() != null && StringUtils.isBlank(comment.getFatherCommentId())) {
return BlogJSONResult.errorMsg("不允许fatherCommentId为空串");
}
// toUserId 是可以为 null 但是 不能是 空串 或 空白串
if (comment.getToUserId() != null && StringUtils.isBlank(comment.getToUserId())) {
return BlogJSONResult.errorMsg("不允许toUserId为空串");
}
if (StringUtils.isNotBlank(comment.getToUserId()) && comment.getToUserId().equals(comment.getFromUserId())) {
return BlogJSONResult.errorMsg("不能回复toUserId为fromUserId");
}
boolean articleIsExist = articleServiceProxy.queryArticleIsExist(comment.getArticleId());
boolean userIdIsExist = userServiceProxy.queryUserIdIsExist(comment.getFromUserId());
if (!articleIsExist || !userIdIsExist) {
return BlogJSONResult.errorMsg("文章id不存在或留言者id不存在");
}
// 父 评论 验证
if (StringUtils.isNotBlank(comment.getFatherCommentId())) {
boolean fatherCommentIdIsExist = commentServiceProxy.queryCommentIsExist(comment.getFatherCommentId());
if (!fatherCommentIdIsExist) {
return BlogJSONResult.errorMsg("父评论id不存在");
}
}
// 被 回复用户验证
if (StringUtils.isNotBlank(comment.getToUserId())) {
boolean toUserIsExist = userServiceProxy.queryUserIdIsExist(comment.getToUserId());
if (!toUserIsExist) {
return BlogJSONResult.errorMsg("被回复用户id不存在");
}
}
boolean saveIsTrue = commentServiceProxy.saveComment(comment);
return saveIsTrue ? BlogJSONResult.ok() : BlogJSONResult.errorMsg("内部错误导致保存失败");
}
@UserLoginToken
@PostMapping(value = "/updateMyComment")
@ApiOperation(value = "更新评论", notes = "更新评论的接口")
@ApiImplicitParams({
@ApiImplicitParam(name = "commentId", value = "评论id", required = true, dataType = "String", paramType = "query"),
@ApiImplicitParam(name = "userId", value = "用户id", required = true, dataType = "String", paramType = "query"),
@ApiImplicitParam(name = "content", value = "更新内容", required = true, dataType = "String", paramType = "query")
})
public BlogJSONResult updateMyComment(String commentId, String userId, String content) {
if (StringUtils.isBlank(commentId) || StringUtils.isBlank(userId)) {
return BlogJSONResult.errorMsg("commentId或userId不能为空");
}
Comment comment = commentServiceProxy.queryComment(commentId);
if (comment == null || !comment.getFromUserId().equals(userId)) {
return BlogJSONResult.errorMsg("commentId不存在或者userId与commentId约束的userId不同");
}
comment.setComment(content);
boolean commentIsUpdate = commentServiceProxy.updateComment(comment);
return commentIsUpdate ? BlogJSONResult.ok() : BlogJSONResult.errorMsg("内部错误导致更新失败");
}
/**
* 该方法 已被 弃用 建议使用 public com.crop.utils.BlogJSONResult rollbackMyComment(String commentId, String userId)
* @param commentId
* @param userId
* @return
*/
@Deprecated
@UserLoginToken
@PostMapping(value = "/removeMyComment")
@ApiOperation(value = "删除评论 - 已废弃", notes = "删除评论的接口")
@ApiImplicitParams({
@ApiImplicitParam(name = "commentId", value = "评论id", required = true, dataType = "String", paramType = "query"),
@ApiImplicitParam(name = "userId", value = "用户id", required = true, dataType = "String", paramType = "query")
})
public BlogJSONResult removeMyComment(String commentId, String userId) {
if (StringUtils.isBlank(commentId) || StringUtils.isBlank(userId)) {
return BlogJSONResult.errorMsg("commentId或userId不能为空");
}
Comment comment = commentServiceProxy.queryComment(commentId);
if (comment == null || !comment.getFromUserId().equals(userId)) {
return BlogJSONResult.errorMsg("commentId不存在或者userId与commentId约束的userId不同");
}
// 如果评论 已经被 追评,则不可撤销
boolean commentWithFatherCommentIsExist = commentServiceProxy.queryCommentWithFatherCommentIsExist(commentId);
if (commentWithFatherCommentIsExist) {
return BlogJSONResult.errorMsg("有子评论约束,普通用户无权限");
}
commentServiceProxy.removeCommentById(commentId);
return BlogJSONResult.ok();
}
@UserLoginToken
@PostMapping(value = "/rollbackMyComment")
@ApiOperation(value = "撤回评论", notes = "撤回评论的接口")
@ApiImplicitParams({
@ApiImplicitParam(name = "commentId", value = "评论id", required = true, dataType = "String", paramType = "query"),
@ApiImplicitParam(name = "userId", value = "用户id", required = true, dataType = "String", paramType = "query")
})
public BlogJSONResult rollbackMyComment(String commentId, String userId) {
if (StringUtils.isBlank(commentId)) {
return BlogJSONResult.errorMsg("评论id不能为空");
}
if (StringUtils.isBlank(userId)) {
return BlogJSONResult.errorMsg("用户id不能为空");
}
Comment comment = commentServiceProxy.queryComment(commentId);
if (comment == null) {
return BlogJSONResult.errorMsg("评论id不存在");
}
User user = userServiceProxy.queryUser(userId);
if (user == null) {
return BlogJSONResult.errorMsg("用户id不存在");
}
if (!comment.getFromUserId().equals(user.getId())) {
return BlogJSONResult.errorMsg("非本用户评论无权撤回");
}
commentServiceProxy.setCommentStatusWithFatherId(comment, CommentStatus.BLOCKED);
return BlogJSONResult.ok();
}
}
- 用户注册与登录控制器
package cn.fyupeng.controller.user;
import cn.fyupeng.annotion.PassToken;
import cn.fyupeng.controller.BasicController;
import cn.fyupeng.annotion.UserLoginToken;
import cn.fyupeng.pojo.User;
import cn.fyupeng.pojo.vo.UserVO;
import cn.fyupeng.service.UserService;
import cn.fyupeng.utils.BlogJSONResult;
import cn.fyupeng.utils.MD5Utils;
import cn.fyupeng.utils.RedisUtils;
import cn.fyupeng.utils.TokenUtils;
import com.auth0.jwt.JWT;
import com.sun.corba.se.spi.servicecontext.UEInfoServiceContext;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import jdk.nashorn.internal.parser.Token;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.web.bind.annotation.*;
/**
* @Auther: fyp
* @Date: 2022/8/17
* @Description: 注册和登录 控制器
* @Package: cn.fyupeng.controller
* @Version: 1.0
*/
@CrossOrigin
@RestController
@RequestMapping(value = "/user")
@Api(value = "用户注册登录的接口", tags = {"注册和登录的controller"})
public class UserRegisterAndLoginController extends BasicController {
private UserService userServiceProxy = rpcClientProxy.getProxy(UserService.class);
/**
* 拦截器不拦截注册,所以不需要注解 @PassToken
* @param user
* @return
* @throws Exception
*/
@ApiOperation(value = "用户注册", notes = "用户注册的接口")
@ApiImplicitParam(name = "user", value = "用户", required = true, dataType = "User", paramType = "body")
@PostMapping(value = "/regist")
public BlogJSONResult regist(@RequestBody User user) throws Exception {
//1. 判断用户名和密码必须不为空
if(StringUtils.isBlank(user.getUsername()) || StringUtils.isBlank(user.getPassword())){
return BlogJSONResult.errorMsg("用户名和密码不能为空");
}
//2. 判断用户是否存在
boolean usernameIsExist = userServiceProxy.queryUsernameIsExist(user.getUsername());
//3. 保存用户,注册信息
if(!usernameIsExist){
user.setPassword(MD5Utils.getMD5Str(user.getPassword()));
// 防止 被 注入
user.setPermission(2);
userServiceProxy.saveUser(user);
}else {
return BlogJSONResult.errorMsg("用户名已存在");
}
user.setPassword("");
//UserVO userVO = setUserRedisSessionToken(user);
return BlogJSONResult.ok(user);
}
public UserVO setUserRedisSessionToken(User userModel) throws Exception {
//String uniqueToken = UUID.randomUUID().toString();
String token = TokenUtils.token(userModel.getId(), userModel.getUsername(), userModel.getPassword());
String userRedisSession = RedisUtils.getUserRedisSession(userModel.getId());
redis.set(userRedisSession, token, 60 * 5);
UserVO usersVO = new UserVO();
BeanUtils.copyProperties(userModel, usersVO);
usersVO.setUserToken(token);
return usersVO;
}
/**
* 拦截器不拦截登录,所以不需要注解 @PassToken
* @param user
* @return
* @throws Exception
*/
@ApiOperation(value = "用户登录", notes = "用户登录的接口")
@ApiImplicitParam(name = "user", value = "用户", required = true, dataType = "User", paramType = "body")
@PostMapping(value = "/login")
public BlogJSONResult login(@RequestBody User user) throws Exception {
String username = user.getUsername();
String password = user.getPassword();
if(StringUtils.isBlank(username) || StringUtils.isBlank(password)){
return BlogJSONResult.errorMsg("用户名或密码不能为空....");
}
User userResult = userServiceProxy.queryUserForLogin(username, MD5Utils.getMD5Str(user.getPassword()));
if(userResult == null || userResult.getPermission() != 2){
return BlogJSONResult.errorMsg("用户名密码不正确,或为非用户登录");
}else {
UserVO usersVO = setUserRedisSessionToken(userResult);
usersVO.setPassword("");
return BlogJSONResult.ok(usersVO);
}
}
@UserLoginToken
@ApiOperation(value = "更新秘钥", notes = "用户更新秘钥的接口")
@ApiImplicitParam(name = "token", value = "秘钥", required = true, dataType = "String", paramType = "query")
@PostMapping(value = "/updateToken")
public BlogJSONResult updateToken(String token) throws Exception {
String userId;
String username;
String password;
System.out.println(token);
userId = JWT.decode(token).getClaim("userId").asString();
username = JWT.decode(token).getClaim("username").asString();
password = JWT.decode(token).getClaim("password").asString();
if(StringUtils.isBlank(username) || StringUtils.isBlank(password)){
return BlogJSONResult.errorMsg("用户名或密码不能为空....");
}
User userResult = userServiceProxy.queryUserForLogin(username, password);
if(userResult == null){
return BlogJSONResult.errorMsg("Illegal key, update failed");
}else {
User tokenUser = new User();
tokenUser.setId(userId);
tokenUser.setUsername(username);
tokenUser.setPassword(password);
UserVO usersVO = setUserRedisSessionToken(tokenUser);
usersVO.setPassword("");
return BlogJSONResult.ok(usersVO);
}
}
@UserLoginToken
@ApiOperation(value = "用户注销", notes = "用户注销的接口")
@ApiImplicitParam(name = "userId", value = "用户id", required = true, dataType = "String", paramType = "query")
@PostMapping(value = "/logout")
public BlogJSONResult logout(String userId) throws Exception {
String userRedisSession = RedisUtils.getUserRedisSession(userId);
redis.del(userRedisSession);
return BlogJSONResult.ok("注销成功");
}
}
- 管理注册与登录控制器
package cn.fyupeng.controller.admin;
import cn.fyupeng.annotion.PassToken;
import cn.fyupeng.annotion.UserLoginToken;
import cn.fyupeng.controller.BasicController;
import cn.fyupeng.pojo.User;
import cn.fyupeng.pojo.vo.UserVO;
import cn.fyupeng.service.UserService;
import cn.fyupeng.utils.BlogJSONResult;
import cn.fyupeng.utils.MD5Utils;
import cn.fyupeng.utils.RedisUtils;
import cn.fyupeng.utils.TokenUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.web.bind.annotation.*;
import java.util.UUID;
@CrossOrigin
@SuppressWarnings("all")
@RestController
@RequestMapping(value = "/admin")
@Api(value = "管理员注册登录的接口", tags = {"管理员注册和登录的controller"})
public class AdminRegistLoginController extends BasicController {
private UserService userServiceProxy = rpcClientProxy.getProxy(UserService.class);
@UserLoginToken
@ApiOperation(value = "管理员注册", notes = "用管理员注册的接口")
@ApiImplicitParam(name = "user", value = "用户", required = true, dataType = "User", paramType = "body")
@PostMapping(value = "/regist")
public BlogJSONResult regist(@RequestBody User user) throws Exception{
//1. 判断用户名和密码必须不为空
if(StringUtils.isBlank(user.getUsername()) || StringUtils.isBlank(user.getPassword())){
return BlogJSONResult.errorMsg("用户名和密码不能为空");
}
//2. 判断用户是否存在
boolean usernameIsExist = userServiceProxy.queryUsernameIsExist(user.getUsername());
//3. 保存用户,注册信息
if(!usernameIsExist){
user.setPassword(MD5Utils.getMD5Str(user.getPassword()));
// 防止 被 注入
user.setPermission(3);
userServiceProxy.saveUser(user);
}else {
return BlogJSONResult.errorMsg("用户名已存在");
}
user.setPassword("");
//String uniqueToken = UUID.randomUUID().toString();
//redis.set(USER_REDIS_SESSION + ":" + user.getId(), uniqueToken, 1000 * 60 * 30);
//
//UsersVO usersVO = new UsersVO();
//BeanUtils.copyProperties(user, usersVO);
//usersVO.setUserToken(uniqueToken);
//UserVO userVO = setUserRedisSessionToken(user);
user.setPassword("");
return BlogJSONResult.ok(user);
}
public UserVO setUserRedisSessionToken(User userModel) throws Exception {
//String uniqueToken = UUID.randomUUID().toString();
//userModel.getPassword() 已经是加密的了
String token = TokenUtils.token(userModel.getId(), userModel.getUsername(), userModel.getPassword());
String userRedisSession = RedisUtils.getUserRedisSession(userModel.getId());
redis.set(userRedisSession, token, 60 * 5);
UserVO usersVO = new UserVO();
BeanUtils.copyProperties(userModel, usersVO);
usersVO.setUserToken(token);
return usersVO;
}
@PassToken
@ApiOperation(value = "管理员登录", notes = "管理员登录的接口")
@ApiImplicitParam(name = "user", value = "用户", required = true, dataType = "User", paramType = "body")
@PostMapping(value = "/login")
public BlogJSONResult login(@RequestBody User user) throws Exception {
String username = user.getUsername();
String password = user.getPassword();
if(StringUtils.isBlank(username) || StringUtils.isBlank(password)){
return BlogJSONResult.ok("用户名或密码不能为空....");
}
User userResult = userServiceProxy.queryUserForLogin(username, MD5Utils.getMD5Str(user.getPassword()));
if(userResult == null || userResult.getPermission() != 3){
return BlogJSONResult.errorMsg("用户名密码不正确,或为非管理员登录");
}else {
UserVO usersVO = setUserRedisSessionToken(userResult);
userResult.setPassword("");
return BlogJSONResult.ok(usersVO);
}
}
@ApiOperation(value = "管理员注销", notes = "管理员注销的接口")
@ApiImplicitParam(name = "userId", value = "用户id", required = true, dataType = "String", paramType = "query")
@PostMapping(value = "/logout")
public BlogJSONResult logout(String userId) throws Exception {
String userRedisSession = RedisUtils.getAdminRedisSession(userId);
redis.del(userRedisSession);
return BlogJSONResult.ok("注销成功");
}
}
- 管理员文章控制器
package cn.fyupeng.controller.admin;
import cn.fyupeng.annotion.UserLoginToken;
import cn.fyupeng.controller.BasicController;
import cn.fyupeng.pojo.Article;
import cn.fyupeng.pojo.Classfication;
import cn.fyupeng.pojo.User;
import cn.fyupeng.service.ArticleService;
import cn.fyupeng.service.ClassficationService;
import cn.fyupeng.service.UserService;
import cn.fyupeng.service.TagService;
import cn.fyupeng.utils.BlogJSONResult;
import cn.fyupeng.utils.PagedResult;
import cn.fyupeng.utils.RedisUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
* @Auther: fyp
* @Date: 2022/4/5
* @Description:
* @Package: com.crop.admin.controller
* @Version: 1.0
*/
@CrossOrigin
@Slf4j
@RestController
@RequestMapping(value = "/admin/article")
@Api(value = "文章相关业务的接口", tags = {"文章相关业务的controller"})
public class AdminArticleController extends BasicController {
private TagService tagServiceProxy = rpcClientProxy.getProxy(TagService.class);
private UserService userServiceProxy = rpcClientProxy.getProxy(UserService.class);
private ArticleService articleServiceProxy = rpcClientProxy.getProxy(ArticleService.class);
private ClassficationService classficationServiceProxy = rpcClientProxy.getProxy(ClassficationService.class);
private static ScheduledExecutorService executor;
@UserLoginToken
@PostMapping(value = "/removeClassfication")
@ApiOperation(value = "删除文章分类 - 注意: 文章分类为所有用户公共的分类,方便查询,私有分类情使用标签", notes = "删除文章分类的接口")
@ApiImplicitParams({
@ApiImplicitParam(name = "classficationId", value = "文章分类id", required = true, dataType = "String", paramType = "query"),
@ApiImplicitParam(name = "userId", value = "用户id", required = true, dataType = "String", paramType = "query")
})
public BlogJSONResult removeClassfication(String classficationId, String userId) {
if (StringUtils.isBlank(userId)) {
return BlogJSONResult.errorMsg("用户id不能为空");
}
User user = userServiceProxy.queryUser(userId);
// 用户不存在 或 无权 删除
if (user == null || user.getPermission() != 3) {
return BlogJSONResult.errorMsg("用户不存在或你无权执行该操作");
}
Article article = new Article();
article.setClassId(classficationId);
PagedResult pagedResult = articleServiceProxy.queryArticleSelective(article, 1, 1);
if (pagedResult.getRecords() != 0) {
return BlogJSONResult.errorMsg("删除失败!存在文章绑定了分类id: " + classficationId);
}
boolean deleteClassficationIsTrue = classficationServiceProxy.deleteClassfication(classficationId);
return deleteClassficationIsTrue ? BlogJSONResult.ok() : BlogJSONResult.errorMsg("分类id不存在或内部错误");
}
@UserLoginToken
@PostMapping(value = "/updateClassfication")
@ApiOperation(value = "更新文章分类", notes = "更新文章分类的接口")
@ApiImplicitParams({
@ApiImplicitParam(name = "classfication", value = "文章分类", required = true, dataType = "Classfication", paramType = "body"),
@ApiImplicitParam(name = "userId", value = "用户id", required = true, dataType = "String", paramType = "query")
})
public BlogJSONResult updateClassfication(@RequestBody Classfication classfication, String userId) {
if (StringUtils.isBlank(userId)) {
return BlogJSONResult.errorMsg("用户id不能为空");
}
User user = userServiceProxy.queryUser(userId);
// 用户不存在 或 无权 删除
if (user == null || user.getPermission() != 3) {
return BlogJSONResult.errorMsg("用户不存在或你无权执行该操作");
}
boolean updateClassficationIsTrue = classficationServiceProxy.updateClassfication(classfication);
return updateClassficationIsTrue ? BlogJSONResult.ok() : BlogJSONResult.errorMsg("分类id不存在或内部错误导致更新失败");
}
@UserLoginToken
@PostMapping(value = "/saveClassfication")
@ApiOperation(value = "新建文章分类", notes = "新建文章分类的接口")
@ApiImplicitParams({
@ApiImplicitParam(name = "classficationName", value = "分类名", required = true, dataType = "String", paramType = "query"),
@ApiImplicitParam(name = "userId", value = "用户id", required = true, dataType = "String", paramType = "query")
})
public BlogJSONResult saveClassFication(String classficationName, String userId) {
if (StringUtils.isBlank(classficationName)) {
return BlogJSONResult.errorMsg("分类名不能为空");
}
User user = userServiceProxy.queryUser(userId);
// 用户不存在 或 无权 删除
if (user == null || user.getPermission() != 3) {
return BlogJSONResult.errorMsg("用户不存在或你无权执行该操作");
}
Classfication classfication = new Classfication();
classfication.setName(classficationName);
Classfication classficationIsExist = classficationServiceProxy.queryClassfication(classfication);
if (classficationIsExist != null) {
return BlogJSONResult.errorMsg("分类名已存在");
}
boolean saveIsTrue = classficationServiceProxy.saveClassfication(classfication);
return saveIsTrue ? BlogJSONResult.ok() : BlogJSONResult.errorMsg("内部错误导致保存失败");
}
@UserLoginToken
@PostMapping(value = "/startTimeTask")
@ApiOperation(value = "开启任务 - 自动更新阅读量", notes = "开启任务的接口")
@ApiImplicitParam(name = "userId", value = "用户id", required = true, dataType = "String", paramType = "query")
public BlogJSONResult startTimeTask(String userId) {
if (StringUtils.isBlank(userId)) {
return BlogJSONResult.errorMsg("用户Id不能为空");
}
User user = userServiceProxy.queryUser(userId);
if (user == null || user.getPermission() != 3) {
return BlogJSONResult.errorMsg("用户Id不存在或无权限");
}
if (executor != null && !executor.isShutdown()) {
return BlogJSONResult.errorMsg("任务已启动,无需重启");
}
executor = Executors.newScheduledThreadPool(2);
log.info("正在开启任务线程池...");
long initialDelay = 1 * 1000;
long fiveMinute = 5 * 60 * 1000;
executor.scheduleAtFixedRate(() -> {
/**
* 定时任务 - 更新 阅读量
*/
// 获取 所有用户 对Id的 阅读量
String viewCount = RedisUtils.getViewCount();
List<String> keys = redis.getKeysByPrefix(viewCount);
/**
* key格式: "crop:viewCount:2204057942HA6Z7C"
* 获取 articleId : 2204057942HA6Z7C
*/
List<String> articleIdKeys = new ArrayList<>();
Map<String, String> articleMap = new HashMap<>();
for (String k : keys) {
// 匹配 最后一个 : 到结束
String tempArticleId = k.substring(k.lastIndexOf(":") + 1);
articleIdKeys.add(tempArticleId);
}
List<String> articleIdCounts = redis.multiGet(keys);
for (int i = 0; i < articleIdKeys.size(); i++) {
articleMap.put(articleIdKeys.get(i), articleIdCounts.get(i));
}
articleServiceProxy.multiUpdateArticleReadCounts(articleIdKeys, articleMap);
log.info("完成一次周期任务 - 任务正常");
}, initialDelay, fiveMinute, TimeUnit.MILLISECONDS);
return BlogJSONResult.ok("任务启动成功");
}
@UserLoginToken
@PostMapping(value = "/stopTimeTask")
@ApiOperation(value = "关闭任务", notes = "关闭任务的接口")
@ApiImplicitParam(name = "userId", value = "用户id", required = true, dataType = "String", paramType = "query")
public BlogJSONResult stopTimeTask(String userId) {
if (StringUtils.isBlank(userId)) {
return BlogJSONResult.errorMsg("用户Id不能为空");
}
User user = userServiceProxy.queryUser(userId);
if (user == null || user.getPermission() != 3) {
return BlogJSONResult.errorMsg("用户Id不存在或无权限");
}
if (executor == null || executor.isTerminated()) {
return BlogJSONResult.errorMsg("任务未启动");
}
executor.shutdown();
try {
executor.awaitTermination(10, TimeUnit.SECONDS);
} catch (InterruptedException e) {
log.error("任务线程池关闭失败: ",e);
executor.shutdownNow();
//e.printStackTrace();
}
log.info("任务线程池已成功关闭");
return BlogJSONResult.ok("任务关闭成功");
}
}
- 管理员评论控制器
package cn.fyupeng.controller.admin;
import cn.fyupeng.annotion.UserLoginToken;
import cn.fyupeng.controller.BasicController;
import cn.fyupeng.enums.CommentStatus;
import cn.fyupeng.pojo.Comment;
import cn.fyupeng.pojo.User;
import cn.fyupeng.pojo.vo.CommentVO;
import cn.fyupeng.service.*;
import cn.fyupeng.utils.BlogJSONResult;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
/**
* @Auther: fyp
* @Date: 2022/4/8
* @Description:
* @Package: com.crop.admin.controller
* @Version: 1.0
*/
@CrossOrigin
@Slf4j
@RestController
@RequestMapping(value = "/admin/comment")
@Api(value = "评论相关业务的接口", tags = {"评论相关业务的controller"})
public class AdminCommentController extends BasicController {
private UserService userServiceProxy = rpcClientProxy.getProxy(UserService.class);
private CommentService commentServiceProxy = rpcClientProxy.getProxy(CommentService.class);
@UserLoginToken
@PostMapping(value = "removeComment")
@ApiOperation(value = "强制删除评论", notes = "强制删除评论的接口")
@ApiImplicitParams({
@ApiImplicitParam(name = "commentId", value = "评论id", required = true, dataType = "String", paramType = "query"),
@ApiImplicitParam(name = "userId", value = "用户id", required = true, dataType = "String", paramType = "query")
})
public BlogJSONResult removeComment(String commentId, String userId) {
if (StringUtils.isBlank(commentId) || StringUtils.isBlank(userId)) {
return BlogJSONResult.errorMsg("commentId或userId不能为空");
}
boolean commentIsExist = commentServiceProxy.queryCommentIsExist(commentId);
if (!commentIsExist) {
return BlogJSONResult.errorMsg("commentId不存在");
}
User identifyUser = userServiceProxy.queryUser(userId);
if (identifyUser == null || identifyUser.getPermission() != 3) {
return BlogJSONResult.errorMsg("用户Id不存在或无权限");
}
commentServiceProxy.removeCommentById(commentId);
commentServiceProxy.removeCommentWithFatherCommentId(commentId);
return BlogJSONResult.ok();
}
@UserLoginToken
@PostMapping(value = "/filterComments")
@ApiOperation(value = "过滤查询评论", notes = "过滤查询评论的接口")
@ApiImplicitParams({
@ApiImplicitParam(name = "aPattern", value = "文章匹配", dataType = "String", paramType = "query"),
@ApiImplicitParam(name = "cPattern", value = "评论匹配", dataType = "String", paramType = "query"),
@ApiImplicitParam(name = "userId", value = "用户id", dataType = "String", paramType = "query"),
@ApiImplicitParam(name = "startTime", value = "评论开始时间 - yyyy-MM-dd HH:mm:ss", required = true, dataType = "String", paramType = "query"),
@ApiImplicitParam(name = "endTime", value = "评论结束时间 - yyyy-MM-dd HH:mm:ss", required = true, dataType = "String", paramType = "query")
})
public BlogJSONResult filterComments(String aPattern, String cPattern, String userId, String startTime, String endTime) {
if (StringUtils.isBlank(aPattern) && StringUtils.isBlank(cPattern)) {
return BlogJSONResult.errorMsg("至少指定文章匹配或评论匹配");
}
if (!(StringUtils.isNotBlank(startTime) && StringUtils.isNotBlank(endTime))) {
return BlogJSONResult.errorMsg("必须指定时间跨度");
}
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date startDate = null;
Date endDate = null;
try {
startDate = sdf.parse(startTime);
endDate = sdf.parse(endTime);
} catch (ParseException e) {
e.printStackTrace();
return BlogJSONResult.errorMsg("时间格式不正确");
}
List<CommentVO> result = null;
if (StringUtils.isBlank(userId)) {
result = commentServiceProxy.queryAllComments(aPattern, cPattern, null, startDate, endDate);
} else {
result = commentServiceProxy.queryAllComments(aPattern, cPattern, userId, startDate, endDate);
}
return BlogJSONResult.ok(result);
}
@UserLoginToken
@PostMapping(value = "/setCommentStatus")
@ApiOperation(value = "屏蔽评论 - 1 正常 - 2 屏蔽", notes = "屏蔽评论的接口")
@ApiImplicitParams({
@ApiImplicitParam(name = "commentId", value = "评论id", dataType = "Integer", paramType = "query"),
@ApiImplicitParam(name = "status", value = "评论状态", dataType = "String", paramType = "query"),
@ApiImplicitParam(name = "userId", value = "用户id", dataType = "String", paramType = "query"),
})
public BlogJSONResult setCommentStatus(String commentId, Integer status, String userId) {
if (StringUtils.isBlank(commentId) || status == null) {
return BlogJSONResult.errorMsg("评论id或状态不能为空");
}
if (StringUtils.isBlank(userId)) {
return BlogJSONResult.errorMsg("用户id不能为空");
}
Comment comment = commentServiceProxy.queryComment(commentId);
if (comment == null) {
return BlogJSONResult.errorMsg("评论id不存在");
}
User user = userServiceProxy.queryUser(userId);
if (user == null || user.getPermission() == 2) {
return BlogJSONResult.errorMsg("用户id不存在或无权限访问");
}
if (status == 1) {
commentServiceProxy.setCommentStatusWithFatherId(comment, CommentStatus.NORMAL);
} else if(status == 2) {
commentServiceProxy.setCommentStatusWithFatherId(comment, CommentStatus.BLOCKED);
} else {
return BlogJSONResult.errorMsg("无其他状态可操作");
}
return BlogJSONResult.ok();
}
}
2.8 服务接口
- 文章服务
package cn.fyupeng.service;
import cn.fyupeng.pojo.Article;
import cn.fyupeng.pojo.vo.ArticleVO;
import cn.fyupeng.utils.PagedResult;
import java.util.List;
import java.util.Map;
/**
* @Auther: fyp
* @Date: 2022/4/2
* @Description:
* @Package: com.crop.service
* @Version: 1.0
*/
public interface ArticleService {
boolean queryArticleIsExist(String articleId);
boolean queryArticleIsUser(Article article);
boolean save(Article article);
PagedResult queryArticleSelective(Article article, Integer page, Integer pageSize);
ArticleVO queryArticleDetail(String articleId);
boolean saveWithIdAndUserId(Article article);
void multiUpdateArticleReadCounts(List<String> articleIdKeys, Map<String, String> articleMap);
void removeArticle(String articleId);
PagedResult queryArticleByTime(Long timeDifference, Integer page, Integer pageSize);
List<ArticleVO> queryArticleWithNoneTagByUser(String userId);
}
- 评论服务
package cn.fyupeng.service;
import cn.fyupeng.enums.CommentStatus;
import cn.fyupeng.pojo.Comment;
import cn.fyupeng.pojo.vo.CommentVO;
import cn.fyupeng.utils.PagedResult;
import java.util.Date;
import java.util.List;
/**
* @Auther: fyp
* @Date: 2022/4/3
* @Description:
* @Package: com.crop.service
* @Version: 1.0
*/
public interface CommentService {
boolean saveComment(Comment comment);
void removeCommentById(String commentId);
Comment queryComment(String commentId);
boolean queryCommentIsExist(String commentId);
boolean updateComment(Comment comment);
PagedResult queryAllComments(String articleId, Integer page, Integer pageSize, Integer sort);
boolean queryCommentWithFatherCommentIsExist(String commentId);
void removeCommentWithFatherCommentId(String fatherCommentId);
List<CommentVO> queryAllComments(String aPattern, String cPattern, String userId, Date startTime, Date endTime);
void setCommentStatusWithFatherId(Comment comment, CommentStatus commentStatus);
}
- 用户服务
package cn.fyupeng.service;
import cn.fyupeng.pojo.User;
import cn.fyupeng.pojo.UserInfo;
/**
* @Auther: fyp
* @Date: 2022/8/17
* @Description: 用户业务服务接口
* @Package: cn.fyupeng.service
* @Version: 1.0
*/
public interface UserService {
/**
* @Description: 判断用户名是否存在
* @param username
* @return
*/
public boolean queryUsernameIsExist(String username);
/**
* @Description: 用户登录,根据用户名和密码查询用户
* @param username
* @param password
* @return
*/
public User queryUserForLogin(String username, String password);
/**
* 查询用户信息
* @param userId
* @return
*/
User queryUser(String userId);
/**
* 查询用户详细信息
* @param userId
* @return
*/
public UserInfo queryUserInfo(String userId);
/**
* 查询用户是否存在
* @param userId
* @return
*/
boolean queryUserIdIsExist(String userId);
/**
* @Description: 用户修改信息
* @param user
*/
public boolean updateUser(User user);
/**
* @Description: 用户修改详细信息
* @param user
*/
/**
* @Description: 保存用户(注册用户)
* @param user
*/
public void saveUser(User user);
public void updateUserInfo(UserInfo user);
}
- 标签服务
package cn.fyupeng.service;
import cn.fyupeng.pojo.Articles2tags;
import cn.fyupeng.pojo.Tag;
import cn.fyupeng.pojo.vo.Articles2tagsVO;
import cn.fyupeng.pojo.vo.TagVO;
import java.util.List;
/**
* @Auther: fyp
* @Date: 2022/4/8
* @Description:
* @Package: com.crop.service
* @Version: 1.0
*/
public interface TagService {
boolean queryTagIsExist(Tag tag);
boolean queryArticleTagIsExist(String id);
boolean queryArticleTagIsExist(Articles2tags articles2tags);
List<TagVO> queryAllTags(Tag tag);
boolean saveTag(Tag tag);
boolean updateTag(Tag tag);
boolean deleteTag(String tagId);
void delArticleTag(String tagId);
boolean deleteTagAndArticleTagWithTagId(String tagId);
Tag queryTag(String tagId);
List<Articles2tagsVO> queryArticleTag(Articles2tags articles2tags);
boolean saveArticleTag(Articles2tags articles2tags);
boolean updateArticleTag(Articles2tags articles2tags);
boolean deleteTagAndArticleTagWithArticleId(String articleId);
}
- 分类服务
package cn.fyupeng.service;
import cn.fyupeng.pojo.Classfication;
import java.util.List;
/**
* @Auther: fyp
* @Date: 2022/4/3
* @Description:
* @Package: com.crop.service
* @Version: 1.0
*/
public interface ClassficationService {
boolean queryClassficationIdIsExist(String classId);
boolean saveClassfication(Classfication classfication);
Classfication queryClassfication(Classfication classfication);
List<Classfication> queryAllClassfications();
boolean deleteClassfication(String classficationId);
boolean updateClassfication(Classfication clssfication);
}
项目链接地址:https://github.com/fyupeng/distributed-blog-system-api
那么第六天的项目就到这里,大家觉得很赞不妨给个关注或者收藏吧,谢谢各位!