├── .gitignore ├── LICENSE ├── README.md ├── images └── api-doc.png ├── pom.xml ├── sql └── blog_db.sql └── src ├── main ├── java │ └── cn │ │ └── poile │ │ └── blog │ │ ├── BlogApplication.java │ │ ├── biz │ │ ├── AsyncService.java │ │ ├── EmailService.java │ │ └── OauthService.java │ │ ├── common │ │ ├── constant │ │ │ ├── ArticleStatusEnum.java │ │ │ ├── CommonConstant.java │ │ │ ├── ErrorEnum.java │ │ │ ├── RedisConstant.java │ │ │ ├── RoleConstant.java │ │ │ └── UserConstant.java │ │ ├── exception │ │ │ ├── ApiException.java │ │ │ └── BadMobileCodeException.java │ │ ├── filter │ │ │ └── AuthorizationTokenFilter.java │ │ ├── limiter │ │ │ ├── ExtraLimiter.java │ │ │ ├── Limit.java │ │ │ ├── SmsLimiter.java │ │ │ ├── annotation │ │ │ │ └── RateLimiter.java │ │ │ └── aspect │ │ │ │ └── RateLimiterAspect.java │ │ ├── oauth │ │ │ ├── GiteeThirdAuth.java │ │ │ ├── GithubThirdAuth.java │ │ │ ├── OauthConstant.java │ │ │ ├── QQThirdAuth.java │ │ │ ├── ThirdAuthToken.java │ │ │ └── ThirdAuthUser.java │ │ ├── oss │ │ │ ├── AbstractStorage.java │ │ │ ├── AliStorage.java │ │ │ ├── LocalStorage.java │ │ │ ├── NeteaseStorage.java │ │ │ ├── PageStorageObject.java │ │ │ ├── QiniuStorage.java │ │ │ ├── Storage.java │ │ │ ├── StorageAutoConfiguration.java │ │ │ ├── StorageFactory.java │ │ │ ├── StorageObject.java │ │ │ └── StorageProperties.java │ │ ├── response │ │ │ └── ApiResponse.java │ │ ├── security │ │ │ ├── AuthenticationToken.java │ │ │ ├── MobileCodeAuthenticationProvider.java │ │ │ ├── MobileCodeAuthenticationToken.java │ │ │ ├── PrimaryKeyAuthenticationProvider.java │ │ │ ├── PrimaryKeyAuthenticationToken.java │ │ │ ├── RedisTokenStore.java │ │ │ ├── SecurityIgnoreProperties.java │ │ │ ├── ServerSecurityContext.java │ │ │ └── WebSecurityConfig.java │ │ ├── sms │ │ │ ├── AliSmsCodeService.java │ │ │ ├── BaseSmsCodeService.java │ │ │ ├── SendResult.java │ │ │ ├── SmsAutoConfiguration.java │ │ │ ├── SmsCodeService.java │ │ │ ├── SmsServiceProperties.java │ │ │ └── TencentSmsCodeService.java │ │ ├── util │ │ │ ├── AbstractListWarp.java │ │ │ ├── DateUtil.java │ │ │ ├── HttpClientUtil.java │ │ │ ├── IpUtil.java │ │ │ ├── RandomValueStringGenerator.java │ │ │ └── ValidateUtil.java │ │ └── validator │ │ │ ├── IsImageValidator.java │ │ │ ├── IsPhoneValidator.java │ │ │ ├── ListSizeValidator.java │ │ │ ├── YearMonthFormatValidator.java │ │ │ └── annotation │ │ │ ├── IsImage.java │ │ │ ├── IsPhone.java │ │ │ ├── ListSize.java │ │ │ └── YearMonthFormat.java │ │ ├── config │ │ ├── CorsConfig.java │ │ ├── DruidDataSourceConfig.java │ │ ├── ExceptionHandle.java │ │ ├── LimiterConfig.java │ │ ├── MybatisPlusConfig.java │ │ ├── RedisConfig.java │ │ └── SwaggerConfig.java │ │ ├── controller │ │ ├── ArticleCollectController.java │ │ ├── ArticleCommentController.java │ │ ├── ArticleController.java │ │ ├── ArticleLikeController.java │ │ ├── ArticleReplyController.java │ │ ├── ArticleTagController.java │ │ ├── AuthenticationController.java │ │ ├── BaseController.java │ │ ├── CategoryController.java │ │ ├── ClientController.java │ │ ├── FileController.java │ │ ├── FriendLinkController.java │ │ ├── LeaveMessageController.java │ │ ├── SmsController.java │ │ ├── TagController.java │ │ ├── UserController.java │ │ └── model │ │ │ ├── dto │ │ │ ├── AccessTokenDTO.java │ │ │ ├── ArticlePageQueryDTO.java │ │ │ ├── CategoryNodeDTO.java │ │ │ └── PreArtAndNextArtDTO.java │ │ │ └── request │ │ │ ├── AddCategoryRequest.java │ │ │ ├── ArticleRequest.java │ │ │ ├── UpdateUserRequest.java │ │ │ └── UserRegisterRequest.java │ │ ├── entity │ │ ├── Article.java │ │ ├── ArticleCollect.java │ │ ├── ArticleComment.java │ │ ├── ArticleLike.java │ │ ├── ArticleReply.java │ │ ├── ArticleTag.java │ │ ├── Category.java │ │ ├── Client.java │ │ ├── FriendLink.java │ │ ├── LeaveMessage.java │ │ ├── OauthUser.java │ │ ├── Tag.java │ │ └── User.java │ │ ├── mapper │ │ ├── ArticleCollectMapper.java │ │ ├── ArticleCommentMapper.java │ │ ├── ArticleLikeMapper.java │ │ ├── ArticleMapper.java │ │ ├── ArticleReplyMapper.java │ │ ├── ArticleTagMapper.java │ │ ├── CategoryMapper.java │ │ ├── ClientMapper.java │ │ ├── FriendLinkMapper.java │ │ ├── LeaveMessageMapper.java │ │ ├── OauthUserMapper.java │ │ ├── TagMapper.java │ │ └── UserMapper.java │ │ ├── mybatis │ │ └── MybatisPlusGenerator.java │ │ ├── service │ │ ├── ArticleRecommendService.java │ │ ├── AuthenticationService.java │ │ ├── IArticleCollectService.java │ │ ├── IArticleCommentService.java │ │ ├── IArticleLikeService.java │ │ ├── IArticleReplyService.java │ │ ├── IArticleService.java │ │ ├── IArticleTagService.java │ │ ├── ICategoryService.java │ │ ├── IClientService.java │ │ ├── IFriendLinkService.java │ │ ├── ILeaveMessageService.java │ │ ├── IOauthUserService.java │ │ ├── ITagService.java │ │ ├── IUserService.java │ │ └── impl │ │ │ ├── ArticleCollectServiceImpl.java │ │ │ ├── ArticleCommentServiceImpl.java │ │ │ ├── ArticleLikeServiceImpl.java │ │ │ ├── ArticleRecommendServiceImpl.java │ │ │ ├── ArticleReplyServiceImpl.java │ │ │ ├── ArticleServiceImpl.java │ │ │ ├── ArticleTagServiceImpl.java │ │ │ ├── AuthenticationServiceImpl.java │ │ │ ├── CategoryServiceImpl.java │ │ │ ├── ClientServiceImpl.java │ │ │ ├── FriendLinkServiceImpl.java │ │ │ ├── LeaveMessageServiceImpl.java │ │ │ ├── OauthUserServiceImpl.java │ │ │ ├── TagServiceImpl.java │ │ │ ├── UserDetailsServiceImpl.java │ │ │ └── UserServiceImpl.java │ │ ├── vo │ │ ├── ArticleArchivesVo.java │ │ ├── ArticleCategoryStatisticsVo.java │ │ ├── ArticleCommentVo.java │ │ ├── ArticleReplyVo.java │ │ ├── ArticleTagStatisticsVo.java │ │ ├── ArticleVo.java │ │ ├── CustomUserDetails.java │ │ ├── LeaveMessageReplyVo.java │ │ ├── LeaveMessageVo.java │ │ └── UserVo.java │ │ └── wrapper │ │ └── ArticlePageQueryWrapper.java └── resources │ ├── application-dev.yml │ ├── application-prod.yml │ ├── application.yml │ ├── logback-spring.xml │ ├── mapper │ ├── ArticleCollectMapper.xml │ ├── ArticleCommentMapper.xml │ ├── ArticleLikeMapper.xml │ ├── ArticleMapper.xml │ ├── ArticleReplyMapper.xml │ ├── ArticleTagMapper.xml │ ├── CategoryMapper.xml │ ├── ClientMapper.xml │ ├── FriendLinkMapper.xml │ ├── LeaveMessageMapper.xml │ ├── OauthUserMapper.xml │ ├── TagMapper.xml │ └── UserMapper.xml │ ├── scripts │ └── redis │ │ └── limit.lua │ ├── support │ └── http │ │ └── resources │ │ └── js │ │ └── common.js │ └── templates │ ├── article_comment.html │ ├── article_reply.html │ ├── email.html │ ├── message.html │ └── message_reply.html └── test └── java └── cn └── poile └── blog └── BlogApplicationTest.java /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Example user template template 3 | ### Example user template 4 | 5 | # IntelliJ project files 6 | .idea 7 | *.iml 8 | out 9 | gen### Java template 10 | # Compiled class file 11 | *.class 12 | 13 | # Log file 14 | *.log 15 | 16 | # BlueJ files 17 | *.ctxt 18 | 19 | # Mobile Tools for Java (J2ME) 20 | .mtj.tmp/ 21 | 22 | # Package Files # 23 | *.jar 24 | *.war 25 | *.nar 26 | *.ear 27 | *.zip 28 | *.tar.gz 29 | *.rar 30 | 31 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 32 | hs_err_pid* 33 | 34 | /target/ 35 | /logs/ 36 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## 前言 2 | 3 | 做开发也有挺长一段时间了,都挺忙的,平时也会看些书,或者做一些笔记,有时间就逛逛Github,刷刷掘金什么的,看看别人的见解,涨涨见识,看到好的文章也会选择收藏起来。逐渐地,我也意识到自己也该写写总结,写写一些自己的想法了。稍加思索后,冷静识破。于是决定自己写一个博客API系统,至于为啥是博客API系统而不是博客系统主要是基于两个方面的考虑:一是博客API是提供博客接口,前端页面可另外写,页面设计、风格可自由发挥,不强依赖后台服务,即便不会后台的伙伴也可以基于博客API自己搭建一个自己心仪的博客。二是个人平时开发也大都是前后端分离的开发模式,还是比较喜欢前后端分离的这种开发模式。 4 | 出于对开源社区的敬意,将项目开源出来大家共同学习,希望对大家有所帮助。当然,本人水平有限,也欢迎大家指出存在的缺陷或不严谨的地方。 5 | 6 | > 前端项目地址:https://github.com/copoile/blog-web.git 7 | 8 | 9 | 10 | ## 简介 11 | 12 | 项目为java语言编写的一个博客API系统,上手简单,配置灵活,有完整的接口说明文档,接口丰富,接口具备认证授权、鉴权、参数校验、限流等功能。认证方式采用token认证方式,并且区分客户端。 热点数据使用redis缓存,数据库使用mysql。项目接入第三方阿里云短信服务,此服务需到阿里云平台开通。 13 | 文件存储方面,项目提供4种选择,分别为本地存储、阿里云对象存储、网易云对象存储、七牛云对象存储,至于使用哪种看个人的选择,需要注意的是本地存储需要配置代理(如nginx)进行读取,这么做主要是基于两方面的考虑,一是tomcat相对其他静态文件服务器而言并不是很擅长读取静态文件。二是前端项目也需要一个静态文件服务器,可以用前端静态文件服务器代理读取。 14 | 15 | > 使用第三方文件存储主要是为了分担服务器带宽压力。将静态文件放到1MB带宽的服务器上,读取的时候你将体会到什么叫龟速。 16 | 17 | ![](./images/api-doc.png) 18 | 19 | 20 | 21 | ## 技术架构 22 | 23 | 采用SpringBoot2.0、MyBatis-Plus、Security等框架。 24 | 25 | 26 | 27 | ## 功能 28 | 29 | ``` 30 | - 账号注册 31 | - 账号登录 / 手机号登录 32 | - 第三方登录 / QQ / Github / Gitee 33 | - 个人信息 34 | - 绑定邮箱 / 绑定手机号 35 | - 修改密码 / 重置密码 36 | - 文章编辑 37 | - 文章管理 38 | - 文章推荐 39 | - 用户管理 40 | - 分类管理 41 | - 标签管理 42 | - 收藏管理 43 | - 博客友链 44 | - 客户端管理 45 | - 首页 46 | - 分类 47 | - 归档 48 | - 标签 49 | - 友链 50 | - 留言 51 | - 文章 / 收藏 / 点赞 / 评论 / 回复 / 相关推荐 52 | - 其他 53 | ``` 54 | 55 | 56 | 57 | ## 开发环境 58 | 59 | - **JDK 1.8 +** 60 | - **Maven 3.5 +** 61 | - **IntelliJ IDEA ULTIMATE 2018.2 +** (*注意:建议使用 IDEA 开发,同时保证安装 `lombok` 插件,如果是eclipse也要确保安装了`lombok` 插件*) 62 | - **Redis 3.0 +** 63 | - **Mysql 5.7 +** 64 | 65 | 66 | 67 | ## 在线文档 68 | 69 | 国内: http://copoile.gitee.io/blog-doc 70 | 国外: https://copoile.github.io 71 | 72 | ## 线上 73 | 目前网站已正式上线,如需查看完整效果可查看网站: 74 | [https://www.poile.cn](https://www.poile.cn) 75 | 76 | > 觉得不错的话,多支持下low逼弟弟哦~ 77 | -------------------------------------------------------------------------------- /images/api-doc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/copoile/blog-api/4554dae27f3194f0a8ed53aef380179256d3c193/images/api-doc.png -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/BlogApplication.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog; 2 | 3 | import org.mybatis.spring.annotation.MapperScan; 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.boot.autoconfigure.SpringBootApplication; 6 | import org.springframework.cache.annotation.EnableCaching; 7 | import org.springframework.scheduling.annotation.EnableAsync; 8 | 9 | /** 10 | * 博客api应用入口 11 | * @author: yaohw 12 | * @create: 2019-10-23 10:44 13 | **/ 14 | @EnableAsync 15 | @EnableCaching 16 | @SpringBootApplication 17 | @MapperScan("cn.poile.blog.mapper") 18 | public class BlogApplication { 19 | public static void main(String[] args) { 20 | SpringApplication.run(BlogApplication.class,args); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/biz/AsyncService.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.biz; 2 | 3 | import lombok.extern.log4j.Log4j2; 4 | import org.springframework.scheduling.annotation.Async; 5 | import org.springframework.stereotype.Component; 6 | 7 | import java.util.function.Function; 8 | 9 | /** 10 | * 通用异步服务 11 | * @author: yaohw 12 | * @create: 2019-12-03 15:39 13 | **/ 14 | @Log4j2 15 | @Component 16 | public class AsyncService { 17 | 18 | @Async 19 | public void runAsync(Function function) { 20 | try { 21 | function.apply(Boolean.TRUE); 22 | } catch (Exception e) { 23 | log.error("异步任务执行错误:{0}",e); 24 | } 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/biz/EmailService.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.biz; 2 | 3 | import lombok.extern.log4j.Log4j2; 4 | import org.apache.commons.lang3.ArrayUtils; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.beans.factory.annotation.Value; 7 | import org.springframework.mail.javamail.JavaMailSender; 8 | import org.springframework.mail.javamail.MimeMessageHelper; 9 | import org.springframework.scheduling.annotation.Async; 10 | import org.springframework.stereotype.Service; 11 | import org.thymeleaf.TemplateEngine; 12 | import org.thymeleaf.context.Context; 13 | 14 | import javax.mail.internet.MimeMessage; 15 | import java.util.Map; 16 | 17 | /** 18 | * 邮件服务 19 | * @author: yaohw 20 | * @create: 2019-11-07 19:21 21 | **/ 22 | @Service 23 | @Log4j2 24 | public class EmailService { 25 | 26 | @Value("${spring.mail.username}") 27 | private String from; 28 | 29 | @Value("${spring.mail.jndi-name}") 30 | private String personal; 31 | 32 | @Autowired 33 | private TemplateEngine templateEngine; 34 | 35 | @Autowired 36 | private JavaMailSender mailSender; 37 | 38 | /** 39 | * 异步发送Html邮件 40 | * @param to 发送给谁 41 | * @param subject 主题 42 | * @param template html模板名 43 | * @param params 模板参数 44 | * @param cc 抄送到 45 | */ 46 | @Async 47 | public void asyncSendHtmlMail(String to, String subject, String template, Map params, String... cc) { 48 | try { 49 | MimeMessage message = mailSender.createMimeMessage(); 50 | MimeMessageHelper helper = new MimeMessageHelper(message, true); 51 | helper.setFrom(from, personal); 52 | helper.setTo(to); 53 | helper.setSubject(subject); 54 | Context context = new Context(); 55 | context.setVariables(params); 56 | String emailContent = templateEngine.process(template, context); 57 | helper.setText(emailContent, true); 58 | if (ArrayUtils.isNotEmpty(cc)) { 59 | helper.setCc(cc); 60 | } 61 | mailSender.send(message); 62 | log.info("邮件成功发送到:{}",to); 63 | } catch (Exception e) { 64 | log.error("发送邮件失败:{0}", e); 65 | } 66 | } 67 | 68 | /** 69 | * 发送Html邮件 70 | * @param to 发送给谁 71 | * @param subject 主题 72 | * @param template html模板名 73 | * @param params 模板参数 74 | * @param cc 抄送到 75 | */ 76 | public void sendHtmlMail(String to, String subject, String template, Map params, String... cc) { 77 | asyncSendHtmlMail(to, subject, template, params, cc); 78 | } 79 | 80 | /** 81 | * 异步发送Html邮件 82 | * @param to 发送给谁 83 | * @param subject 主题 84 | * @param template html模板名 85 | * @param params 模板参数 86 | */ 87 | @Async 88 | public void asyncSendHtmlMail(String to, String subject, String template, Map params) { 89 | String[] cc = new String[0]; 90 | asyncSendHtmlMail(to, subject, template, params,cc); 91 | } 92 | 93 | /** 94 | * 发送Html邮件 95 | * @param to 发送给谁 96 | * @param subject 主题 97 | * @param template html 模板 98 | * @param params 模板参数 99 | */ 100 | public void sendHtmlMail(String to, String subject, String template, Map params) { 101 | asyncSendHtmlMail(to, subject, template, params); 102 | } 103 | 104 | } 105 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/common/constant/ArticleStatusEnum.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.common.constant; 2 | 3 | /** 4 | * @author: yaohw 5 | * @create: 2019-11-15 16:30 6 | **/ 7 | public enum ArticleStatusEnum { 8 | /** 9 | * 正常 10 | */ 11 | NORMAL(0, "正常"), 12 | /** 13 | * 待发布 14 | */ 15 | NOT_PUBLISH(1, "待发布"), 16 | /** 17 | * 丢弃(回收站) 18 | */ 19 | DISCARD(2, "丢弃(回收站)"); 20 | 21 | private Integer status; 22 | 23 | private String message; 24 | 25 | ArticleStatusEnum(int status, String message) { 26 | this.status = status; 27 | this.message = message; 28 | } 29 | 30 | public Integer getStatus() { 31 | return status; 32 | } 33 | 34 | public void setStatus(Integer status) { 35 | this.status = status; 36 | } 37 | 38 | public String getMessage() { 39 | return message; 40 | } 41 | 42 | public void setMessage(String message) { 43 | this.message = message; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/common/constant/CommonConstant.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.common.constant; 2 | 3 | /** 4 | * @author: yaohw 5 | * @create: 2019-11-25 15:15 6 | **/ 7 | public class CommonConstant { 8 | 9 | /** 10 | * 已删除 11 | */ 12 | public static final int DELETED = 1; 13 | 14 | /** 15 | * 未删除 16 | */ 17 | public static final int NOT_DELETED = 0; 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/common/constant/ErrorEnum.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.common.constant; 2 | 3 | /** 4 | * 错误枚举 5 | * @author: yaohw 6 | * @create: 2019-10-25 17:15 7 | **/ 8 | public enum ErrorEnum { 9 | /** 10 | * 成功 11 | */ 12 | SUCCESS(0,"成功"), 13 | 14 | /** 15 | * 用户名或密码错误 16 | */ 17 | BAD_CREDENTIALS(1001,"用户名或密码错误"), 18 | 19 | /** 20 | * 用户不存在 21 | */ 22 | USER_NOT_FOUND(1002,"用户不存在"), 23 | 24 | /** 25 | * 账号禁用 26 | */ 27 | ACCOUNT_DISABLE(1003,"账号已禁用"), 28 | 29 | /** 30 | * 账号已过期 31 | */ 32 | ACCOUNT_EXPIRED(1004,"账号已过期"), 33 | 34 | /** 35 | * 账号已锁定 36 | */ 37 | ACCOUNT_LOCKED(1005,"账号已锁定"), 38 | 39 | /** 40 | * 凭证已过期 41 | */ 42 | CREDENTIALS_EXPIRED(1006,"凭证已过期"), 43 | 44 | /** 45 | * 不允许访问 46 | */ 47 | ACCESS_DENIED(1007,"不允许访问"), 48 | 49 | /** 50 | * 无权限访问 51 | */ 52 | PERMISSION_DENIED(1008,"无权限访问"), 53 | 54 | /** 55 | * 凭证无效或已过期 56 | */ 57 | CREDENTIALS_INVALID(1009,"凭证无效或已过期"), 58 | 59 | /** 60 | * 刷新凭证无效或已过期 61 | */ 62 | REFRESH_CREDENTIALS_INVALID(1010,"刷新凭证无效或已过期"), 63 | 64 | /** 65 | * 无效请求 66 | */ 67 | INVALID_REQUEST(1011,"无效请求或请求不接受"), 68 | 69 | /** 70 | * 接口限流 71 | */ 72 | REQUEST_LIMIT(1012,"接口访问次数限制"), 73 | 74 | /** 75 | * 系统异常 76 | */ 77 | SYSTEM_ERROR(1013,"系统异常"); 78 | 79 | /** 80 | * 错误码 81 | */ 82 | private Integer errorCode; 83 | 84 | /** 85 | * 错误信息 86 | */ 87 | private String errorMsg; 88 | 89 | ErrorEnum(int errorCode, String errorMsg) { 90 | this.errorCode = errorCode; 91 | this.errorMsg = errorMsg; 92 | } 93 | 94 | public int getErrorCode() { 95 | return errorCode; 96 | } 97 | 98 | public void setErrorCode(int errorCode) { 99 | this.errorCode = errorCode; 100 | } 101 | 102 | public String getErrorMsg() { 103 | return errorMsg; 104 | } 105 | 106 | public void setErrorMsg(String errorMsg) { 107 | this.errorMsg = errorMsg; 108 | } 109 | } 110 | 111 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/common/constant/RedisConstant.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.common.constant; 2 | 3 | /** 4 | * @author: yaohw 5 | * @create: 2019-10-30 15:20 6 | **/ 7 | public class RedisConstant { 8 | 9 | /** 10 | * 短信验证码 key前缀 11 | */ 12 | public static final String SMS_CODE = "sms:code:"; 13 | 14 | /** 15 | * 文章浏览 key前缀 16 | */ 17 | public static final String ART_VIEW = "art:view:"; 18 | 19 | 20 | /** 21 | * 限流前缀 22 | */ 23 | public static final String REDIS_LIMIT_KEY_PREFIX = "limit:"; 24 | 25 | /** 26 | * 短信限流key前缀(命名空间) 27 | */ 28 | public static final String SMS_LIMIT_NAME ="sms"; 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/common/constant/RoleConstant.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.common.constant; 2 | 3 | /** 4 | * 角色常量 5 | * @author yaohw 6 | * @create: 2019-10-04 21:43 7 | */ 8 | public class RoleConstant { 9 | /** 10 | * 管理员 11 | */ 12 | public static final String ADMIN = "admin"; 13 | 14 | /** 15 | * 普通用户 16 | */ 17 | public static final String ORDINARY = "ordinary"; 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/common/constant/UserConstant.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.common.constant; 2 | 3 | /** 4 | * 用户相关常量 5 | * @author: yaohw 6 | * @create: 2019-11-05 17:43 7 | **/ 8 | public class UserConstant { 9 | 10 | /** 11 | * 用户状态-正常 12 | */ 13 | public static final int STATUS_NORMAL = 0; 14 | 15 | /** 16 | * 用户性别-男 17 | */ 18 | public static final int GENDER_MALE = 1; 19 | 20 | /** 21 | * 用户类型-管理员 22 | */ 23 | public static final int ADMIN = 1; 24 | 25 | /** 26 | * 用户类型-普通用户 27 | */ 28 | public static final int ORDINARY = 0; 29 | 30 | 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/common/exception/ApiException.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.common.exception; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * 自定义异常 7 | * @author: yaohw 8 | * @create: 2019-10-24 17:16 9 | **/ 10 | @Data 11 | public class ApiException extends RuntimeException{ 12 | 13 | private int errorCode; 14 | 15 | private String errorMsg; 16 | 17 | public ApiException(int errorCode,String errorMsg) { 18 | super(errorMsg); 19 | this.errorCode = errorCode; 20 | this.errorMsg = errorMsg; 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/common/exception/BadMobileCodeException.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.common.exception; 2 | 3 | import org.springframework.security.core.AuthenticationException; 4 | 5 | /** 6 | * 手机号验证码认证 验证码不正确异常 7 | * @author: yaohw 8 | * @create: 2019-11-07 16:58 9 | **/ 10 | public class BadMobileCodeException extends AuthenticationException { 11 | 12 | public BadMobileCodeException(String msg, Throwable t) { 13 | super(msg, t); 14 | } 15 | 16 | public BadMobileCodeException(String msg) { 17 | super(msg); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/common/filter/AuthorizationTokenFilter.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.common.filter; 2 | 3 | import cn.poile.blog.common.constant.ErrorEnum; 4 | import cn.poile.blog.common.response.ApiResponse; 5 | import cn.poile.blog.common.security.AuthenticationToken; 6 | import cn.poile.blog.common.security.RedisTokenStore; 7 | import com.alibaba.fastjson.JSON; 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.web.filter.OncePerRequestFilter; 13 | 14 | import javax.servlet.FilterChain; 15 | import javax.servlet.ServletException; 16 | import javax.servlet.http.HttpServletRequest; 17 | import javax.servlet.http.HttpServletResponse; 18 | import java.io.IOException; 19 | 20 | /** 21 | * AuthenticationToken 校验过滤器 22 | * @author: yaohw 23 | * @create: 2019-10-29 19:47 24 | **/ 25 | @Component 26 | public class AuthorizationTokenFilter extends OncePerRequestFilter { 27 | 28 | @Autowired 29 | private RedisTokenStore tokenStore; 30 | 31 | private static final String AUTHORIZATION_HEADER = "Authorization"; 32 | 33 | private static final String TOKEN_TYPE = "Bearer"; 34 | 35 | @Override 36 | protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException { 37 | final String authorization = httpServletRequest.getHeader(AUTHORIZATION_HEADER); 38 | if (authorization != null && authorization.startsWith(TOKEN_TYPE)) { 39 | String accessToken = authorization.substring(7); 40 | if (!accessToken.isEmpty()) { 41 | AuthenticationToken cacheAuthenticationToken = tokenStore.readByAccessToken(accessToken); 42 | if (cacheAuthenticationToken == null) { 43 | httpServletResponse.setCharacterEncoding("UTF-8"); 44 | httpServletResponse.setContentType("application/json; charset=utf-8"); 45 | httpServletResponse.getWriter().print(JSON.toJSON(createErrorResponse())); 46 | return; 47 | } 48 | UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(cacheAuthenticationToken.getPrincipal(), null, cacheAuthenticationToken.getPrincipal().getAuthorities()); 49 | authentication.setDetails(cacheAuthenticationToken); 50 | SecurityContextHolder.getContext().setAuthentication(authentication); 51 | } 52 | } 53 | filterChain.doFilter(httpServletRequest, httpServletResponse); 54 | } 55 | 56 | private ApiResponse createErrorResponse() { 57 | ApiResponse response = new ApiResponse(); 58 | response.setCode(ErrorEnum.CREDENTIALS_INVALID.getErrorCode()); 59 | response.setMessage(ErrorEnum.CREDENTIALS_INVALID.getErrorMsg()); 60 | return response; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/common/limiter/ExtraLimiter.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.common.limiter; 2 | 3 | import cn.poile.blog.common.limiter.annotation.RateLimiter; 4 | import org.aspectj.lang.ProceedingJoinPoint; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * 附加接口限流接口 10 | * @author: yaohw 11 | * @create: 2019-11-22 17:30 12 | **/ 13 | public interface ExtraLimiter { 14 | 15 | /** 16 | * 附加自定义限流 17 | * @param rateLimiter 限流注解 18 | * @param point aop 切面point 19 | * @return 限流对象列表 20 | */ 21 | List limit(RateLimiter rateLimiter, ProceedingJoinPoint point); 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/common/limiter/Limit.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.common.limiter; 2 | 3 | import lombok.Data; 4 | 5 | import java.util.concurrent.TimeUnit; 6 | 7 | /** 8 | * @author: yaohw 9 | * @create: 2019-11-23 10:20 10 | **/ 11 | @Data 12 | public class Limit { 13 | 14 | /** 15 | * redis 限流key 16 | */ 17 | private String key; 18 | 19 | /** 20 | * 限流次数 21 | */ 22 | private long max; 23 | 24 | /** 25 | * 限流时间 26 | */ 27 | private long timeout; 28 | 29 | /** 30 | * 时间单位 31 | */ 32 | private TimeUnit timeUnit; 33 | 34 | /** 35 | * 限流信息 36 | */ 37 | private String message; 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/common/limiter/annotation/RateLimiter.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.common.limiter.annotation; 2 | 3 | import org.springframework.core.annotation.AliasFor; 4 | 5 | import java.lang.annotation.*; 6 | import java.util.concurrent.TimeUnit; 7 | 8 | /** 9 | * 接口限流注解 10 | * @author: yaohw 11 | * @create: 2019/11/5 10:28 下午 12 | */ 13 | @Target(ElementType.METHOD) 14 | @Retention(RetentionPolicy.RUNTIME) 15 | @Documented 16 | public @interface RateLimiter { 17 | 18 | long DEFAULT_REQUEST = 10; 19 | 20 | /** 21 | * 只启动附加 22 | * @return 23 | */ 24 | boolean onlyExtra() default false; 25 | 26 | /** 27 | * 名称 28 | */ 29 | String name(); 30 | 31 | /** 32 | * max 最大请求数 33 | */ 34 | @AliasFor("max") long value() default DEFAULT_REQUEST; 35 | 36 | /** 37 | * max 最大请求数 38 | */ 39 | @AliasFor("value") long max() default DEFAULT_REQUEST; 40 | 41 | /** 42 | * 限流key,支持SpEL 43 | */ 44 | String key() default ""; 45 | 46 | /** 47 | * 在多久时常内 48 | */ 49 | long timeout() default 60; 50 | 51 | /** 52 | * 超时时间单位 默认为 秒 53 | */ 54 | TimeUnit timeUnit() default TimeUnit.SECONDS; 55 | 56 | /** 57 | * 是否拼接ip 58 | * @return 59 | */ 60 | boolean appendIp() default false; 61 | 62 | /** 63 | * 自定义附加限流器bean名称 64 | * @return 65 | */ 66 | String extra() default ""; 67 | 68 | /** 69 | * 限流信息 70 | * @return 71 | */ 72 | String message() default "接口调用频繁,请稍后再试"; 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/common/oauth/GiteeThirdAuth.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.common.oauth; 2 | 3 | import cn.poile.blog.common.constant.ErrorEnum; 4 | import cn.poile.blog.common.exception.ApiException; 5 | import cn.poile.blog.common.util.HttpClientUtil; 6 | import com.alibaba.fastjson.JSON; 7 | import com.alibaba.fastjson.JSONObject; 8 | import lombok.extern.log4j.Log4j2; 9 | import org.apache.commons.lang3.StringUtils; 10 | import org.springframework.beans.factory.annotation.Value; 11 | import org.springframework.stereotype.Service; 12 | 13 | import java.util.HashMap; 14 | import java.util.Map; 15 | 16 | /** 17 | * gitee 第三方登录 18 | * @author: yaohw 19 | * @create: 2020-05-21 16:18 20 | **/ 21 | @Log4j2 22 | @Service 23 | public class GiteeThirdAuth{ 24 | 25 | @Value("${oauth.gitee.clientId}") 26 | public String clientId; 27 | 28 | @Value("${oauth.gitee.clientSecret}") 29 | public String clientSecret; 30 | 31 | @Value("${oauth.gitee.redirect_uri}") 32 | public String redirect; 33 | 34 | /** 35 | * 获取第三方用户信息 36 | * 37 | * @param accessToken 38 | * @return 39 | */ 40 | public ThirdAuthUser getUserInfoByToken(String accessToken) { 41 | Map params = new HashMap<>(2); 42 | params.put("access_token", accessToken); 43 | String result = HttpClientUtil.doGet(OauthConstant.GITEE_ACCESS_USER_URL, params); 44 | if (StringUtils.isBlank(result)) { 45 | throw new ApiException(ErrorEnum.SYSTEM_ERROR.getErrorCode(),"获取第三方token出错"); 46 | } 47 | JSONObject jsonObject = JSON.parseObject(result); 48 | ThirdAuthUser thirdAuthUser = new ThirdAuthUser(); 49 | thirdAuthUser.setUuid(jsonObject.getString("id")); 50 | thirdAuthUser.setNickname(jsonObject.getString("name")); 51 | thirdAuthUser.setAvatar(jsonObject.getString("avatar_url")); 52 | thirdAuthUser.setEmail(jsonObject.getString("email")); 53 | return thirdAuthUser; 54 | } 55 | 56 | /** 57 | * 获取第三方用户信息 58 | * 59 | * @param code 60 | 61 | * @return 62 | */ 63 | public ThirdAuthUser getUserInfoByCode(String code) { 64 | ThirdAuthToken authToken = getAuthToken(code); 65 | return getUserInfoByToken(authToken.getAccessToken()); 66 | } 67 | 68 | /** 69 | * 获取第三方token信息 70 | * 71 | * @param code 72 | * @return 73 | */ 74 | public ThirdAuthToken getAuthToken(String code) { 75 | Map params = new HashMap<>(8); 76 | params.put("code",code); 77 | params.put("client_id", clientId); 78 | params.put("client_secret", clientSecret); 79 | params.put("grant_type","authorization_code"); 80 | params.put("redirect_uri", redirect); 81 | String result = HttpClientUtil.doPost(OauthConstant.GITEE_ACCESS_TOKE_URL, params); 82 | if (StringUtils.isBlank(result)) { 83 | throw new ApiException(ErrorEnum.SYSTEM_ERROR.getErrorCode(),"获取第三方token出错"); 84 | } 85 | JSONObject jsonObject = JSON.parseObject(result); 86 | ThirdAuthToken thirdAuthToken = new ThirdAuthToken(); 87 | thirdAuthToken.setAccessToken(jsonObject.getString("access_token")); 88 | return thirdAuthToken; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/common/oauth/GithubThirdAuth.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.common.oauth; 2 | 3 | import cn.poile.blog.common.constant.ErrorEnum; 4 | import cn.poile.blog.common.exception.ApiException; 5 | import cn.poile.blog.common.util.HttpClientUtil; 6 | import com.alibaba.fastjson.JSON; 7 | import com.alibaba.fastjson.JSONObject; 8 | import lombok.extern.log4j.Log4j2; 9 | import org.apache.commons.lang3.StringUtils; 10 | import org.springframework.beans.factory.annotation.Value; 11 | import org.springframework.stereotype.Service; 12 | 13 | import java.util.HashMap; 14 | import java.util.Map; 15 | 16 | import static cn.poile.blog.common.oauth.OauthConstant.GITHUB_ACCESS_TOKE_URL; 17 | import static cn.poile.blog.common.oauth.OauthConstant.GITHUB_ACCESS_USER_URL; 18 | 19 | /** 20 | * github 第三方登录 21 | * @author: yaohw 22 | * @create: 2020-05-20 11:49 23 | **/ 24 | @Log4j2 25 | @Service 26 | public class GithubThirdAuth{ 27 | 28 | 29 | @Value("${oauth.github.clientId}") 30 | public String clientId; 31 | 32 | @Value("${oauth.github.clientSecret}") 33 | public String clientSecret; 34 | 35 | /** 36 | * 获取第三方用户信息 37 | * 38 | * @param accessToken 39 | * @return 40 | */ 41 | public ThirdAuthUser getUserInfoByToken(String accessToken) { 42 | Map params = new HashMap<>(2); 43 | params.put("access_token",accessToken); 44 | String result = HttpClientUtil.doGet(GITHUB_ACCESS_USER_URL, params); 45 | if (StringUtils.isBlank(result)) { 46 | throw new ApiException(ErrorEnum.SYSTEM_ERROR.getErrorCode(),"获取第三方用户信息出错"); 47 | } 48 | log.info("请求结果:" + result); 49 | JSONObject jsonObject = JSON.parseObject(result); 50 | ThirdAuthUser thirdAuthUser = new ThirdAuthUser(); 51 | thirdAuthUser.setUuid(jsonObject.getString("id")); 52 | thirdAuthUser.setNickname(jsonObject.getString("name")); 53 | thirdAuthUser.setAvatar(jsonObject.getString("avatar_url")); 54 | thirdAuthUser.setEmail(jsonObject.getString("email")); 55 | return thirdAuthUser; 56 | } 57 | 58 | 59 | /** 60 | * 获取第三方用户信息 61 | * 62 | * @param code 63 | * @return 64 | */ 65 | public ThirdAuthUser getUserInfoByCode(String code) { 66 | ThirdAuthToken thirdAuthToken = getAuthToken(code); 67 | return getUserInfoByToken(thirdAuthToken.getAccessToken()); 68 | } 69 | 70 | /** 71 | * 获取第三方token信息 72 | * 73 | * @param code 74 | * @return 75 | */ 76 | public ThirdAuthToken getAuthToken(String code) { 77 | Map params = new HashMap<>(4); 78 | params.put("client_id",clientId); 79 | params.put("client_secret",clientSecret); 80 | params.put("code",code); 81 | String result = HttpClientUtil.doGet(GITHUB_ACCESS_TOKE_URL, params); 82 | if (StringUtils.isBlank(result)) { 83 | throw new ApiException(ErrorEnum.SYSTEM_ERROR.getErrorCode(),"获取第三方token出错"); 84 | } 85 | ThirdAuthToken thirdAuthToken = new ThirdAuthToken(); 86 | thirdAuthToken.setAccessToken(getAccessToken(result)); 87 | return thirdAuthToken; 88 | } 89 | 90 | /** 91 | * 获取 access_token 92 | * @param result 93 | * @return 94 | */ 95 | private String getAccessToken(String result) { 96 | // result 97 | // access_token=aa5a59cd212b2c0f3c1f285822b2085f52fe3850&scope=user%3Aemail&token_type=bearer 98 | try { 99 | return result.split("&")[0].split("=")[1]; 100 | }catch (ArrayIndexOutOfBoundsException e) { 101 | log.error("获取access_token异常", e); 102 | throw new ApiException(ErrorEnum.SYSTEM_ERROR.getErrorCode(),"获取第三方token出错"); 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/common/oauth/OauthConstant.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.common.oauth; 2 | 3 | /** 4 | * @author: yaohw 5 | * @create: 2020-05-20 11:57 6 | **/ 7 | public class OauthConstant { 8 | 9 | /** 10 | * QQ 获取accessToken 链接 11 | */ 12 | public static final String QQ_ACCESS_TOKE_URL = "https://graph.qq.com/oauth2.0/token"; 13 | 14 | /** 15 | * QQ 获取 openid 链接 16 | */ 17 | public static final String QQ_ACCESS_OPENID_URL = "https://graph.qq.com/oauth2.0/me"; 18 | 19 | /** 20 | * QQ 获取用户信息 链接 21 | */ 22 | public static final String QQ_ACCESS_USER_URL = "https://graph.qq.com/user/get_user_info"; 23 | 24 | /** 25 | * github 获取accessToken 链接 26 | */ 27 | public static final String GITHUB_ACCESS_TOKE_URL = "https://github.com/login/oauth/access_token"; 28 | 29 | /** 30 | * github 获取用户信息 链接 31 | */ 32 | public static final String GITHUB_ACCESS_USER_URL = "https://api.github.com/user"; 33 | 34 | /** 35 | * gitee 获取accessToken 链接 36 | */ 37 | public static final String GITEE_ACCESS_TOKE_URL = "https://gitee.com/oauth/token"; 38 | 39 | /** 40 | * gitee 获取用户信息 链接 41 | */ 42 | public static final String GITEE_ACCESS_USER_URL = "https://gitee.com/api/v5/user"; 43 | 44 | /** 45 | * 认证类型QQ 46 | */ 47 | public static final int OAUTH_TYPE_QQ = 1; 48 | 49 | /** 50 | * 认证类型github 51 | */ 52 | public static final int OAUTH_TYPE_GITHUB = 2; 53 | 54 | /** 55 | * 认证类型gitee 56 | */ 57 | public static final int OAUTH_TYPE_GITEE = 3; 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/common/oauth/ThirdAuthToken.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.common.oauth; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * 第三次token 7 | * @author: yaohw 8 | * @create: 2020-05-20 11:37 9 | **/ 10 | @Data 11 | public class ThirdAuthToken { 12 | private String accessToken; 13 | private int expire; 14 | private String refreshToken; 15 | private String uid; 16 | private String openId; 17 | private String accessCode; 18 | private String unionId; 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/common/oauth/ThirdAuthUser.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.common.oauth; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * 第三方用户信息 7 | * @author: yaohw 8 | * @create: 2020-05-20 11:12 9 | **/ 10 | @Data 11 | public class ThirdAuthUser { 12 | 13 | /** 14 | * 第三方用户唯一id 15 | */ 16 | private String uuid; 17 | 18 | /** 19 | * 用户名 20 | */ 21 | private String username; 22 | 23 | /** 24 | * 昵称 25 | */ 26 | private String nickname; 27 | 28 | /** 29 | * 用户头像 30 | */ 31 | private String avatar; 32 | 33 | /** 34 | * 用户网址 35 | */ 36 | private String blog; 37 | 38 | /** 39 | * 所在公司 40 | */ 41 | private String company; 42 | 43 | /** 44 | * 位置 45 | */ 46 | private String location; 47 | 48 | /** 49 | * 用户邮箱 50 | */ 51 | private String email; 52 | 53 | /** 54 | * 用户备注 55 | */ 56 | private String remark; 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/common/oss/AbstractStorage.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.common.oss; 2 | 3 | /** 4 | * @author: yaohw 5 | * @create: 2019-11-02 15:50 6 | **/ 7 | public abstract class AbstractStorage implements Storage{ 8 | 9 | /** 10 | * 获取文件名 如 1134664743.png 11 | * @param fullPath 完整路径。如 http://qiniu.poile.cn/1134664743.png 12 | * @return 13 | */ 14 | protected String getFileNameFromFullPath(String fullPath) { 15 | if (!fullPath.isEmpty()) { 16 | return fullPath.substring(fullPath.lastIndexOf("/") + 1); 17 | } 18 | return null; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/common/oss/AliStorage.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.common.oss; 2 | 3 | import cn.poile.blog.common.constant.ErrorEnum; 4 | import cn.poile.blog.common.exception.ApiException; 5 | import com.aliyun.oss.OSSClient; 6 | import com.aliyun.oss.model.ObjectMetadata; 7 | import lombok.extern.log4j.Log4j2; 8 | import org.apache.commons.lang3.StringUtils; 9 | 10 | import java.io.ByteArrayInputStream; 11 | import java.io.InputStream; 12 | 13 | /** 14 | * 阿里云 存储 15 | * @author: yaohw 16 | * @create: 2019-11-02 11:39 17 | **/ 18 | @Log4j2 19 | public class AliStorage extends AbstractStorage{ 20 | 21 | private OSSClient client; 22 | 23 | private String endpoint; 24 | 25 | private String bucket; 26 | 27 | public AliStorage(StorageProperties.Ali ali) { 28 | this.bucket = ali.getBucket(); 29 | this.endpoint = ali.getEndpoint(); 30 | client = new OSSClient(this.endpoint,ali.getAccessKeyId(),ali.getAccessKeyIdSecret()); 31 | } 32 | 33 | /** 34 | * 文件上传 35 | * 36 | * @param bytes 文件字节数组 37 | * @param path 文件路径 38 | * @param contentType 文件类型 39 | * @return http地址 40 | */ 41 | @Override 42 | public String upload(byte[] bytes, String path, String contentType) { 43 | return upload(new ByteArrayInputStream(bytes), path,contentType); 44 | } 45 | 46 | /** 47 | * 文件上传 48 | * 49 | * @param inputStream 字节流 50 | * @param path 文件路径 51 | * @param contentType 文件类型 52 | * @return http地址 53 | */ 54 | @Override 55 | public String upload(InputStream inputStream, String path, String contentType) { 56 | ObjectMetadata metadata = new ObjectMetadata(); 57 | metadata.setContentType(contentType); 58 | client.putObject(bucket,path,inputStream,metadata); 59 | return "https://" + bucket + "." + endpoint + "/" + path; 60 | } 61 | 62 | /** 63 | * 删除文件 64 | * 65 | * @param fullPath 文件完整路径 66 | * @return 是否删除成功 67 | */ 68 | @Override 69 | public boolean delete(String fullPath) { 70 | if (StringUtils.isBlank(fullPath)) { 71 | return false; 72 | } 73 | try { 74 | client.deleteObject(bucket , getFileNameFromFullPath(fullPath)); 75 | } catch (Exception ex) { 76 | log.error("删除文件失败:{0}",ex); 77 | throw new ApiException(ErrorEnum.SYSTEM_ERROR.getErrorCode(),"删除文件失败"); 78 | } 79 | return true; 80 | } 81 | 82 | /** 83 | * 分页获取文件对象列表 84 | * 85 | * @param nextMarker 下一个marker 86 | * @param size 87 | * @return 88 | */ 89 | @Override 90 | public PageStorageObject page(String nextMarker, int size) { 91 | return new PageStorageObject(); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/common/oss/LocalStorage.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.common.oss; 2 | 3 | import cn.poile.blog.common.constant.ErrorEnum; 4 | import cn.poile.blog.common.exception.ApiException; 5 | import lombok.extern.log4j.Log4j2; 6 | import org.apache.commons.io.FileUtils; 7 | import org.apache.commons.lang3.StringUtils; 8 | 9 | import java.io.ByteArrayInputStream; 10 | import java.io.File; 11 | import java.io.IOException; 12 | import java.io.InputStream; 13 | 14 | /** 15 | * 本地 存储 16 | * @author: yaohw 17 | * @create: 2019-10-31 19:50 18 | **/ 19 | @Log4j2 20 | public class LocalStorage extends AbstractStorage{ 21 | 22 | private String path; 23 | 24 | private String proxy; 25 | 26 | public LocalStorage(StorageProperties.Local local) { 27 | this.path = local.getPath(); 28 | this.proxy = local.getProxy(); 29 | init(); 30 | } 31 | 32 | private void init () { 33 | File file = new File(path); 34 | try { 35 | FileUtils.forceMkdir(file); 36 | } catch (IOException ex) { 37 | log.error("创建本地存储文件目录失败:{0}",ex); 38 | } 39 | } 40 | 41 | /** 42 | * 文件上传 43 | * 44 | * @param bytes 件字节数组 45 | * @param name 文件路径 46 | * @param contentType 文件类型 47 | * @return http地址 48 | */ 49 | @Override 50 | public String upload(byte[] bytes, String name, String contentType) { 51 | return upload(new ByteArrayInputStream(bytes),name,contentType); 52 | } 53 | 54 | /** 55 | * 文件上传 56 | * 57 | * @param inputStream 字节流 58 | * @param name 文件名 59 | * @param contentType 文件类型 60 | * @return http地址 61 | */ 62 | @Override 63 | public String upload(InputStream inputStream, String name, String contentType) { 64 | File file = new File(path + name ); 65 | try { 66 | FileUtils.copyInputStreamToFile(inputStream, file); 67 | } catch (IOException ex) { 68 | log.error("本地文件存储失败:{0}",ex); 69 | throw new ApiException(ErrorEnum.SYSTEM_ERROR.getErrorCode(),"上传文件失败"); 70 | } 71 | return proxy + name; 72 | } 73 | 74 | /** 75 | * 删除文件 76 | * 77 | * @param fullPath 文件完整路径 78 | * @return 是否删除成功 79 | */ 80 | @Override 81 | public boolean delete(String fullPath) { 82 | if (StringUtils.isBlank(fullPath)) { 83 | return false; 84 | } 85 | return FileUtils.deleteQuietly(new File(path + getFileNameFromFullPath(fullPath))); 86 | } 87 | 88 | /** 89 | * 分页获取文件对象列表 90 | * @param nextMarker 91 | * @param size 92 | * @return 93 | */ 94 | @Override 95 | public PageStorageObject page(String nextMarker, int size) { 96 | return new PageStorageObject(); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/common/oss/PageStorageObject.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.common.oss; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import lombok.Data; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * 文件分页page 10 | * @author: yaohw 11 | * @create: 2020-05-06 15:58 12 | **/ 13 | @Data 14 | @JsonInclude(JsonInclude.Include.NON_NULL) 15 | public class PageStorageObject { 16 | 17 | /** 18 | * 数据 19 | */ 20 | private List records; 21 | 22 | /** 23 | * 下一个标记 24 | */ 25 | private String nextMarker; 26 | 27 | /** 28 | * 当前标记 29 | */ 30 | private String currentMarker; 31 | 32 | /** 33 | * 是否加载完成 34 | */ 35 | private boolean loadedAll; 36 | 37 | /** 38 | * 数量 39 | */ 40 | private int size; 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/common/oss/Storage.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.common.oss; 2 | 3 | import com.baomidou.mybatisplus.core.metadata.IPage; 4 | 5 | import java.io.InputStream; 6 | 7 | /** 8 | * 存储 9 | * @author: yaohw 10 | * @create: 2019-10-30 17:39 11 | **/ 12 | public interface Storage { 13 | 14 | /** 15 | * 文件上传 16 | * @param bytes 文件字节数组 17 | * @param path 文件路径 18 | * @param contentType 文件类型 19 | * @return http地址 20 | */ 21 | String upload(byte[] bytes,String path,String contentType); 22 | 23 | /** 24 | * 文件上传 25 | * @param inputStream 字节流 26 | * @param path 文件路径 27 | * @param contentType 文件类型 28 | * @return http地址 29 | */ 30 | String upload(InputStream inputStream,String path,String contentType); 31 | 32 | /** 33 | * 删除文件 34 | * @param fullPath 文件路径 35 | * @return 是否删除成功 36 | */ 37 | boolean delete(String fullPath); 38 | 39 | /** 40 | * 分页获取文件对象列表 41 | * @param nextMarker 下一个marker 42 | * @param size 43 | * @return 44 | */ 45 | PageStorageObject page(String nextMarker, int size); 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/common/oss/StorageAutoConfiguration.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.common.oss; 2 | 3 | import lombok.extern.log4j.Log4j2; 4 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 5 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.context.annotation.Configuration; 8 | 9 | /** 10 | * oss 存储自动配置 11 | * @author: yaohw 12 | * @create: 2019-10-31 10:50 13 | **/ 14 | @Log4j2 15 | @Configuration 16 | @EnableConfigurationProperties({StorageProperties.class}) 17 | public class StorageAutoConfiguration { 18 | 19 | @Bean 20 | @ConditionalOnMissingBean(name = {"storage"}) 21 | public Storage storage(StorageProperties properties) { 22 | return StorageFactory.build(properties); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/common/oss/StorageObject.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.common.oss; 2 | 3 | import com.fasterxml.jackson.annotation.JsonFormat; 4 | import lombok.Data; 5 | 6 | import java.util.Date; 7 | 8 | /** 9 | * 存储对象 10 | * @author: yaohw 11 | * @create: 2020-05-06 14:44 12 | **/ 13 | @Data 14 | public class StorageObject { 15 | 16 | /** 17 | * 文件名 18 | */ 19 | private String name; 20 | 21 | /** 22 | * 路径 23 | */ 24 | private String path; 25 | 26 | /** 27 | * 链接 28 | */ 29 | private String url; 30 | 31 | /** 32 | * 时间 33 | */ 34 | @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone = "GMT+8") 35 | private Date date; 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/common/response/ApiResponse.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.common.response; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import io.swagger.annotations.ApiModel; 5 | import io.swagger.annotations.ApiModelProperty; 6 | import lombok.*; 7 | 8 | /** 9 | * @author: yaohw 10 | * @create: 2019-10-25 16:17 11 | **/ 12 | @ApiModel("api响应json对象") 13 | @Data 14 | @AllArgsConstructor 15 | @NoArgsConstructor 16 | @RequiredArgsConstructor() 17 | @JsonInclude(JsonInclude.Include.NON_NULL) 18 | public class ApiResponse { 19 | 20 | @NonNull 21 | @ApiModelProperty("响应码") 22 | private int code; 23 | @NonNull 24 | @ApiModelProperty("响应信息") 25 | private String message; 26 | 27 | @ApiModelProperty("响应数据") 28 | private T data; 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/common/security/AuthenticationToken.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.common.security; import cn.poile.blog.vo.CustomUserDetails; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import lombok.experimental.Accessors; import java.io.Serializable; /** * @author: yaohw * @create: 2019-10-28 18:00 **/ @Data @Accessors(chain = true) @ApiModel(value="AuthenticationToken", description="AuthenticationToken") public class AuthenticationToken implements Serializable { @ApiModelProperty("accessToken") private String accessToken; @ApiModelProperty("token类型:Bearer") private String tokenType = "Bearer"; @ApiModelProperty("refreshToken") private String refreshToken; @ApiModelProperty(hidden = true,value = "用户信息") private CustomUserDetails principal; /* @ApiModelProperty(hidden = true,value = "客户端id") private String clientId;*/ } -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/common/security/MobileCodeAuthenticationToken.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.common.security; 2 | 3 | import org.springframework.security.authentication.AbstractAuthenticationToken; 4 | import org.springframework.security.core.GrantedAuthority; 5 | 6 | import java.util.Collection; 7 | 8 | /** 9 | * 手机号短信验证码 AuthenticationToken 10 | * @author: yaohw 11 | * @create: 2019-11-07 16:48 12 | **/ 13 | public class MobileCodeAuthenticationToken extends AbstractAuthenticationToken { 14 | 15 | private final Object principal; 16 | private Object credentials; 17 | 18 | public MobileCodeAuthenticationToken(Object principal, Object credentials) { 19 | super(null); 20 | this.principal = principal; 21 | this.credentials = credentials; 22 | this.setAuthenticated(false); 23 | } 24 | 25 | public MobileCodeAuthenticationToken(Object principal, Object credentials, Collection authorities) { 26 | super(authorities); 27 | this.principal = principal; 28 | this.credentials = credentials; 29 | super.setAuthenticated(true); 30 | } 31 | 32 | @Override 33 | public Object getCredentials() { 34 | return this.credentials; 35 | } 36 | 37 | @Override 38 | public Object getPrincipal() { 39 | return this.principal; 40 | } 41 | 42 | @Override 43 | public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException { 44 | if (isAuthenticated) { 45 | throw new IllegalArgumentException("Cannot set this token to trusted - use constructor which takes a GrantedAuthority userIdList instead"); 46 | } else { 47 | super.setAuthenticated(false); 48 | } 49 | } 50 | 51 | @Override 52 | public void eraseCredentials() { 53 | super.eraseCredentials(); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/common/security/PrimaryKeyAuthenticationToken.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.common.security; 2 | 3 | import org.springframework.security.authentication.AbstractAuthenticationToken; 4 | import org.springframework.security.core.GrantedAuthority; 5 | 6 | import java.util.Collection; 7 | 8 | /** 9 | * @author: yaohw 10 | * @create: 2020-05-20 15:45 11 | **/ 12 | public class PrimaryKeyAuthenticationToken extends AbstractAuthenticationToken { 13 | 14 | 15 | private final Object principal; 16 | private Object credentials; 17 | 18 | public PrimaryKeyAuthenticationToken(Object principal) { 19 | super(null); 20 | this.principal = principal; 21 | this.credentials = principal; 22 | this.setAuthenticated(false); 23 | } 24 | 25 | public PrimaryKeyAuthenticationToken(Object principal, Object credentials, Collection authorities) { 26 | super(authorities); 27 | this.principal = principal; 28 | this.credentials = credentials; 29 | super.setAuthenticated(true); 30 | } 31 | 32 | 33 | @Override 34 | public Object getCredentials() { 35 | return this.credentials; 36 | } 37 | 38 | @Override 39 | public Object getPrincipal() { 40 | return this.principal; 41 | } 42 | 43 | @Override 44 | public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException { 45 | if (isAuthenticated) { 46 | throw new IllegalArgumentException("Cannot set this token to trusted - use constructor which takes a GrantedAuthority userIdList instead"); 47 | } else { 48 | super.setAuthenticated(false); 49 | } 50 | } 51 | 52 | @Override 53 | public void eraseCredentials() { 54 | super.eraseCredentials(); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/common/security/SecurityIgnoreProperties.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.common.security; 2 | 3 | import lombok.Data; 4 | import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; 5 | import org.springframework.boot.context.properties.ConfigurationProperties; 6 | import org.springframework.context.annotation.Configuration; 7 | 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | /** 12 | * 忽略security校验配置 13 | * @author: yaohw 14 | * @create: 2019-11-08 15:04 15 | **/ 16 | @Data 17 | @Configuration 18 | @ConditionalOnExpression("!'${ignore}'.isEmpty()") 19 | @ConfigurationProperties(prefix = "ignore") 20 | public class SecurityIgnoreProperties { 21 | 22 | private List list = new ArrayList<>(); 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/common/security/ServerSecurityContext.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.common.security; 2 | 3 | import cn.poile.blog.common.constant.ErrorEnum; 4 | import cn.poile.blog.common.exception.ApiException; 5 | import cn.poile.blog.vo.CustomUserDetails; 6 | import org.springframework.security.core.Authentication; 7 | import org.springframework.security.core.context.SecurityContext; 8 | import org.springframework.security.core.context.SecurityContextHolder; 9 | 10 | /** 11 | * 服务安全上下文 12 | * 13 | * @author: yaohw 14 | * @create: 2019-11-03 15:00 15 | **/ 16 | public class ServerSecurityContext { 17 | /** 18 | * 获取当前用户相信信息 19 | * 20 | * @return 21 | */ 22 | public static CustomUserDetails getUserDetail(boolean throwEx) { 23 | SecurityContext context = SecurityContextHolder.getContext(); 24 | Authentication authentication = context.getAuthentication(); 25 | if (authentication == null && throwEx) { 26 | throw new ApiException(ErrorEnum.CREDENTIALS_INVALID.getErrorCode(), ErrorEnum.CREDENTIALS_INVALID.getErrorMsg()); 27 | } 28 | if (authentication == null) { 29 | return null; 30 | } 31 | Object principal = authentication.getPrincipal(); 32 | if (principal == null && throwEx) { 33 | throw new ApiException(ErrorEnum.CREDENTIALS_INVALID.getErrorCode(), ErrorEnum.CREDENTIALS_INVALID.getErrorMsg()); 34 | } 35 | String noneUser = "anonymousUser"; 36 | if (noneUser.equals(principal)) { 37 | return null; 38 | } 39 | return (CustomUserDetails) principal; 40 | } 41 | 42 | /** 43 | * 获取认证信息 44 | * 45 | * @return org.springframework.security.core.Authentication 46 | */ 47 | public static Authentication getAuthentication() { 48 | SecurityContext context = SecurityContextHolder.getContext(); 49 | return context.getAuthentication(); 50 | } 51 | 52 | /** 53 | * 获取AuthenticationToken 54 | * 55 | * @return cn.poile.blog.common.security.AuthenticationToken 56 | */ 57 | public static AuthenticationToken getAuthenticationToken(boolean throwEx) { 58 | SecurityContext context = SecurityContextHolder.getContext(); 59 | Authentication authentication = context.getAuthentication(); 60 | if (authentication == null && throwEx) { 61 | throw new ApiException(ErrorEnum.CREDENTIALS_INVALID.getErrorCode(), ErrorEnum.CREDENTIALS_INVALID.getErrorMsg()); 62 | } 63 | if (authentication == null) { 64 | return null; 65 | } 66 | Object details = authentication.getDetails(); 67 | if (details == null && throwEx) { 68 | throw new ApiException(ErrorEnum.CREDENTIALS_INVALID.getErrorCode(), ErrorEnum.CREDENTIALS_INVALID.getErrorMsg()); 69 | } 70 | return (AuthenticationToken) details; 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/common/sms/BaseSmsCodeService.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.common.sms; 2 | 3 | import cn.poile.blog.common.constant.RedisConstant; 4 | import lombok.Data; 5 | import org.apache.commons.lang3.StringUtils; 6 | import org.springframework.beans.BeansException; 7 | import org.springframework.beans.factory.InitializingBean; 8 | import org.springframework.context.ApplicationContext; 9 | import org.springframework.context.ApplicationContextAware; 10 | import org.springframework.data.redis.core.StringRedisTemplate; 11 | import org.springframework.util.Assert; 12 | 13 | import java.util.Iterator; 14 | import java.util.Map; 15 | import java.util.Set; 16 | import java.util.concurrent.TimeUnit; 17 | 18 | /** 19 | * @author: yaohw 20 | * @create: 2019/11/4 11:20 下午 21 | */ 22 | @Data 23 | public abstract class BaseSmsCodeService implements SmsCodeService, InitializingBean, ApplicationContextAware { 24 | 25 | private StringRedisTemplate redisTemplate; 26 | 27 | private ApplicationContext applicationContext; 28 | 29 | /** 30 | * 短信验证码有效时间 31 | */ 32 | private long expire = 300L; 33 | 34 | @Override 35 | public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { 36 | this.applicationContext = applicationContext; 37 | } 38 | 39 | @Override 40 | public void afterPropertiesSet() { 41 | if (this.redisTemplate == null) { 42 | this.redisTemplate = applicationContext.getBean(StringRedisTemplate.class); 43 | } 44 | Assert.notNull(this.redisTemplate, "There is no one available StringRedisTemplate bean"); 45 | } 46 | 47 | /** 48 | * 发送短信验证码,这个提供外部调用的 49 | * 发送短信成功后缓存短信验证码 50 | * 51 | * @param mobile 52 | * @return 53 | */ 54 | @Override 55 | public boolean sendSmsCode(long mobile) { 56 | SendResult sendResult = handleSendSmsCode(mobile); 57 | String code = sendResult.getCode(); 58 | boolean smsSuccess = sendResult.isSuccess(); 59 | if (!StringUtils.isBlank(code) && smsSuccess) { 60 | cacheSmsCode(mobile, code); 61 | return true; 62 | } 63 | return false; 64 | } 65 | 66 | /** 67 | * 发送短信验证码实现 68 | * 69 | * @param mobile 手机号 70 | * @return 71 | */ 72 | protected abstract SendResult handleSendSmsCode(long mobile); 73 | 74 | /** 75 | * 缓存短信验证码 76 | * 77 | * @param mobile 78 | * @param code 79 | */ 80 | @Override 81 | public void cacheSmsCode(long mobile, String code) { 82 | redisTemplate.opsForValue().set(RedisConstant.SMS_CODE + mobile, code, expire, TimeUnit.SECONDS); 83 | } 84 | 85 | /** 86 | * 校验短信验证码 87 | * 88 | * @param mobile 89 | * @param code 90 | * @return 91 | */ 92 | @Override 93 | public boolean checkSmsCode(long mobile, String code) { 94 | String cacheCode = redisTemplate.opsForValue().get(RedisConstant.SMS_CODE + mobile); 95 | return !StringUtils.isBlank(cacheCode) && cacheCode.equals(code); 96 | } 97 | 98 | /** 99 | * 删除缓存短信验证码 100 | * @param mobile 101 | * @return 102 | */ 103 | @Override 104 | public boolean deleteSmsCode(long mobile) { 105 | redisTemplate.delete(RedisConstant.SMS_CODE + mobile); 106 | return true; 107 | } 108 | 109 | /** 110 | * 获取随机6位数验证码 111 | * 112 | * @return 113 | */ 114 | protected String createCode() { 115 | int random = (int) ((Math.random() * 9 + 1) * 100000); 116 | return String.valueOf(random); 117 | } 118 | 119 | 120 | } 121 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/common/sms/SendResult.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.common.sms; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | 6 | /** 7 | * 短信发送结果 8 | * @author: yaohw 9 | * @create: 2019-12-03 16:43 10 | **/ 11 | @Data 12 | @AllArgsConstructor 13 | public class SendResult { 14 | 15 | /** 16 | * 是否发送成功 17 | */ 18 | private boolean success; 19 | 20 | /** 21 | * 发送的验证码 22 | */ 23 | private String code; 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/common/sms/SmsAutoConfiguration.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.common.sms; 2 | 3 | import lombok.extern.log4j.Log4j2; 4 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 5 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.context.annotation.Configuration; 8 | 9 | /** 10 | * 短信验证码服务自动配置,默认阿里云短信服务 11 | * @author: yaohw 12 | * @create: 2019-11-05 10:36 13 | **/ 14 | @Log4j2 15 | @Configuration 16 | @EnableConfigurationProperties({SmsServiceProperties.class}) 17 | public class SmsAutoConfiguration { 18 | 19 | @Bean 20 | @ConditionalOnMissingBean 21 | public SmsCodeService smsService(SmsServiceProperties properties) { 22 | int type = properties.getType(); 23 | if (type == 1) { 24 | return new AliSmsCodeService(properties); 25 | } 26 | return new TencentSmsCodeService(properties); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/common/sms/SmsCodeService.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.common.sms; 2 | 3 | /** 4 | * 短信服务 5 | * @author: yaohw 6 | * @create: 2019/11/4 10:34 下午 7 | */ 8 | public interface SmsCodeService { 9 | /** 10 | * 发送短信验证码 11 | * @param mobile 12 | * @return 13 | */ 14 | boolean sendSmsCode(long mobile); 15 | 16 | /** 17 | * 缓存短信验证码 18 | * @param mobile 19 | * @param code 20 | */ 21 | void cacheSmsCode(long mobile,String code); 22 | 23 | /** 24 | * 校验短信验证码 25 | * @param mobile 26 | * @param code 27 | * @return 28 | */ 29 | boolean checkSmsCode(long mobile,String code); 30 | 31 | /** 32 | * 删除验证码 33 | * @param mobile 34 | * @return 35 | */ 36 | boolean deleteSmsCode(long mobile); 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/common/sms/SmsServiceProperties.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.common.sms; 2 | 3 | import lombok.Data; 4 | import org.springframework.boot.context.properties.ConfigurationProperties; 5 | 6 | /** 7 | * @author: yaohw 8 | * @create: 2019-11-05 15:40 9 | **/ 10 | @Data 11 | @ConfigurationProperties(prefix = "sms",ignoreInvalidFields = true) 12 | public class SmsServiceProperties { 13 | 14 | private int type = 1; 15 | 16 | private long expire = 300L; 17 | 18 | private long dayMax = 10L; 19 | 20 | private final SmsServiceProperties.Ali ali = new SmsServiceProperties.Ali(); 21 | 22 | private final SmsServiceProperties.Tencent tencent = new SmsServiceProperties.Tencent(); 23 | 24 | public SmsServiceProperties() { 25 | } 26 | 27 | @Data 28 | public static class Ali { 29 | private String regionId = "cn-hangzhou"; 30 | private String accessKeyId; 31 | private String accessKeySecret; 32 | private String signName; 33 | private String templateCode; 34 | } 35 | 36 | @Data 37 | public static class Tencent { 38 | private String appId; 39 | private String appKey; 40 | private String templateId; 41 | private String signName; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/common/sms/TencentSmsCodeService.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.common.sms; 2 | 3 | import cn.poile.blog.common.constant.ErrorEnum; 4 | import cn.poile.blog.common.exception.ApiException; 5 | import com.github.qcloudsms.SmsSingleSender; 6 | import com.github.qcloudsms.SmsSingleSenderResult; 7 | import lombok.extern.log4j.Log4j2; 8 | 9 | import java.util.ArrayList; 10 | 11 | /** 12 | * 腾讯短信服务 13 | * @author: yaohw 14 | * @create: 2020-05-18 10:38 15 | **/ 16 | @Log4j2 17 | public class TencentSmsCodeService extends BaseSmsCodeService{ 18 | 19 | private String appId; 20 | private String appKey; 21 | private String templateId; 22 | private String signName; 23 | 24 | 25 | public TencentSmsCodeService (SmsServiceProperties properties) { 26 | 27 | } 28 | 29 | /** 30 | * 发送短信验证码实现 31 | * 32 | * @param mobile 手机号 33 | * @return 34 | */ 35 | @Override 36 | protected SendResult handleSendSmsCode(long mobile) { 37 | SmsSingleSender sender = new SmsSingleSender(Integer.parseInt(appId), appKey); 38 | ArrayList params = new ArrayList<>(); 39 | String code = createCode(); 40 | params.add(code); 41 | // 默认只能发送中国大陆的短信86 42 | try { 43 | SmsSingleSenderResult result = sender.sendWithParam("86", Long.toString(mobile), Integer.parseInt(templateId), params, signName, "", ""); 44 | if (result.result != 0) { 45 | throw new ApiException(ErrorEnum.SYSTEM_ERROR.getErrorCode(), result.errMsg); 46 | } 47 | return new SendResult(true,code); 48 | } catch (Exception e) { 49 | log.error("发送短信失败:{0}", e); 50 | throw new ApiException(ErrorEnum.SYSTEM_ERROR.getErrorCode(), "短信发送失败"); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/common/util/AbstractListWarp.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.common.util; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /** 7 | * @author: yaohw 8 | * @create: 2019-11-14 17:51 9 | **/ 10 | public abstract class AbstractListWarp { 11 | 12 | 13 | public abstract T warp(S source); 14 | 15 | /** 16 | * list集合转换 17 | * @param sourceList 18 | * @return 19 | */ 20 | public List warpList(List sourceList) { 21 | List targetList = new ArrayList<>(); 22 | if (sourceList != null && !sourceList.isEmpty()) { 23 | for (S source : sourceList) { 24 | targetList.add(warp(source)); 25 | } 26 | } 27 | return targetList; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/common/util/DateUtil.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.common.util; 2 | 3 | import java.time.chrono.IsoChronology; 4 | 5 | /** 6 | * @author: yaohw 7 | * @create: 2019-11-27 17:07 8 | **/ 9 | public class DateUtil { 10 | 11 | /** 12 | * 获取一个月最大天数 13 | * @param year 14 | * @param month 15 | * @return 16 | */ 17 | public static int getMaxDayOfMonth(int year, int month) { 18 | int dom; 19 | switch (month) { 20 | case 2: 21 | dom = (IsoChronology.INSTANCE.isLeapYear(year) ? 29 : 28); 22 | break; 23 | case 4: 24 | case 6: 25 | case 9: 26 | case 11: 27 | dom = 30; 28 | break; 29 | default: 30 | dom = 31; 31 | } 32 | return dom; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/common/util/IpUtil.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.common.util; 2 | 3 | import lombok.extern.log4j.Log4j2; 4 | import org.apache.commons.lang3.StringUtils; 5 | import org.springframework.web.context.request.RequestContextHolder; 6 | import org.springframework.web.context.request.ServletRequestAttributes; 7 | 8 | import javax.servlet.http.HttpServletRequest; 9 | 10 | /** 11 | * @author: yaohw 12 | * @create: 2019-11-23 10:12 13 | **/ 14 | @Log4j2 15 | public class IpUtil { 16 | 17 | private final static String UNKNOWN = "unknown"; 18 | private final static int MAX_LENGTH = 15; 19 | private final static String COMMA = ","; 20 | 21 | /** 22 | * 获取IP地址 23 | * 使用Nginx等反向代理软件, 则不能通过request.getRemoteAddr()获取IP地址 24 | * 如果使用了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串IP地址,X-Forwarded-For中第一个非unknown的有效IP字符串,则为真实IP地址 25 | */ 26 | public static String getIpAddress() { 27 | HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); 28 | String ip = null; 29 | try { 30 | ip = request.getHeader("x-forwarded-for"); 31 | if (StringUtils.isBlank(ip) || UNKNOWN.equalsIgnoreCase(ip)) { 32 | ip = request.getHeader("Proxy-Client-IP"); 33 | } 34 | if (StringUtils.isBlank(ip) || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) { 35 | ip = request.getHeader("WL-Proxy-Client-IP"); 36 | } 37 | if (StringUtils.isBlank(ip) || UNKNOWN.equalsIgnoreCase(ip)) { 38 | ip = request.getHeader("HTTP_CLIENT_IP"); 39 | } 40 | if (StringUtils.isBlank(ip) || UNKNOWN.equalsIgnoreCase(ip)) { 41 | ip = request.getHeader("HTTP_X_FORWARDED_FOR"); 42 | } 43 | if (StringUtils.isBlank(ip) || UNKNOWN.equalsIgnoreCase(ip)) { 44 | ip = request.getRemoteAddr(); 45 | 46 | } 47 | } catch (Exception e) { 48 | log.error("IPUtils ERROR ", e); 49 | } 50 | // 使用代理,则获取第一个IP地址 51 | if (!StringUtils.isBlank(ip) && ip.length() > MAX_LENGTH) { 52 | if (ip.indexOf(COMMA) > 0) { 53 | ip = ip.substring(0, ip.indexOf(COMMA)); 54 | } 55 | } 56 | return ip; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/common/util/RandomValueStringGenerator.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.common.util; 2 | 3 | import java.security.SecureRandom; 4 | import java.util.Random; 5 | 6 | /** 7 | * 随机字符串生成器 8 | * 9 | * @author: yaohw 10 | * @create: 2019-11-12 14:38 11 | **/ 12 | public class RandomValueStringGenerator { 13 | 14 | private static final char[] DEFAULT_CODE = "1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" 15 | .toCharArray(); 16 | 17 | private Random random = new SecureRandom(); 18 | 19 | private int length; 20 | 21 | 22 | public RandomValueStringGenerator() { 23 | this(10); 24 | } 25 | 26 | 27 | public RandomValueStringGenerator(int length) { 28 | this.length = length; 29 | } 30 | 31 | public String generate() { 32 | byte[] verifierBytes = new byte[length]; 33 | random.nextBytes(verifierBytes); 34 | return getCodeString(verifierBytes); 35 | } 36 | 37 | private String getCodeString(byte[] verifierBytes) { 38 | char[] chars = new char[verifierBytes.length]; 39 | for (int i = 0; i < verifierBytes.length; i++) { 40 | chars[i] = DEFAULT_CODE[((verifierBytes[i] & 0xFF) % DEFAULT_CODE.length)]; 41 | } 42 | return new String(chars); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/common/util/ValidateUtil.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.common.util; 2 | 3 | import java.util.regex.Pattern; 4 | 5 | /** 6 | * 校验工具类 7 | * @author: yaohw 8 | * @create: 2019-10-28 18:51 9 | **/ 10 | public class ValidateUtil { 11 | 12 | /** 13 | * 验证是否手机号 14 | * @param s 15 | * @return boolean 16 | */ 17 | public static boolean validateMobile(Object s) { 18 | String str = String.valueOf(s); 19 | String regexp = "^([1][3,4,5,6,7,8,9]\\d{9})$"; 20 | return Pattern.matches(regexp, str); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/common/validator/IsImageValidator.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.common.validator; 2 | 3 | import cn.poile.blog.common.validator.annotation.IsImage; 4 | import org.springframework.web.multipart.MultipartFile; 5 | 6 | import javax.validation.ConstraintValidator; 7 | import javax.validation.ConstraintValidatorContext; 8 | import java.util.Arrays; 9 | import java.util.List; 10 | 11 | /** 12 | * @author: yaohw 13 | * @create: 2019-11-13 11:03 14 | **/ 15 | public class IsImageValidator implements ConstraintValidator { 16 | 17 | private boolean required; 18 | 19 | private static final String[] IMAGE_CONTENT_TYPE_ARRAY = {"image/bmp", "image/gif", "image/jpeg","image/png", "image/jpg", "image/webp"}; 20 | 21 | 22 | 23 | @Override 24 | public boolean isValid(MultipartFile value, ConstraintValidatorContext context) { 25 | if (required) { 26 | String contentType = value.getContentType(); 27 | List imageContentTypeList = Arrays.asList(IMAGE_CONTENT_TYPE_ARRAY); 28 | return imageContentTypeList.contains(contentType); 29 | } 30 | return true; 31 | } 32 | 33 | @Override 34 | public void initialize(IsImage constraintAnnotation) { 35 | required = constraintAnnotation.required(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/common/validator/IsPhoneValidator.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.common.validator; 2 | 3 | import cn.poile.blog.common.validator.annotation.IsPhone; 4 | 5 | import javax.validation.ConstraintValidator; 6 | import javax.validation.ConstraintValidatorContext; 7 | import java.util.regex.Pattern; 8 | 9 | /** 10 | * @author: yaohw 11 | */ 12 | public class IsPhoneValidator implements ConstraintValidator { 13 | 14 | private boolean required; 15 | 16 | @Override 17 | public void initialize(IsPhone ca) { 18 | required = ca.required(); 19 | } 20 | 21 | @Override 22 | public boolean isValid(Object o, ConstraintValidatorContext constraintValidatorContext) { 23 | String mobile = "" + o; 24 | if (!"".equals(mobile) && required) { 25 | String regexp = "^[1][3,4,5,6,7,8,9]\\d{9}$"; 26 | return Pattern.matches(regexp, mobile); 27 | } 28 | return true; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/common/validator/ListSizeValidator.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.common.validator; 2 | 3 | import cn.poile.blog.common.validator.annotation.ListSize; 4 | 5 | import javax.validation.ConstraintValidator; 6 | import javax.validation.ConstraintValidatorContext; 7 | import java.util.List; 8 | 9 | /** 10 | * @author: yaohw 11 | * @create: 2019-11-15 15:25 12 | **/ 13 | public class ListSizeValidator implements ConstraintValidator { 14 | 15 | private int min = -1; 16 | 17 | private int max = -1; 18 | 19 | private boolean required; 20 | 21 | 22 | @Override 23 | public boolean isValid(List value, ConstraintValidatorContext context) { 24 | int size = value.size(); 25 | if (required) { 26 | if (min != -1 && max != -1) { 27 | return (size > min && size < max); 28 | } else if (min == -1) { 29 | return size <= max; 30 | } 31 | return size >= min; 32 | } 33 | return true; 34 | } 35 | 36 | 37 | @Override 38 | public void initialize(ListSize constraintAnnotation) { 39 | this.required = constraintAnnotation.required(); 40 | this.min = constraintAnnotation.min(); 41 | this.max = constraintAnnotation.max(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/common/validator/YearMonthFormatValidator.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.common.validator; 2 | 3 | import cn.poile.blog.common.validator.annotation.YearMonthFormat; 4 | import org.apache.commons.lang3.StringUtils; 5 | 6 | import javax.validation.ConstraintValidator; 7 | import javax.validation.ConstraintValidatorContext; 8 | import java.time.LocalDate; 9 | import java.util.regex.Pattern; 10 | 11 | /** 12 | * @author: yaohw 13 | * @create: 2019-11-28 09:24 14 | **/ 15 | public class YearMonthFormatValidator implements ConstraintValidator{ 16 | 17 | private boolean required; 18 | 19 | @Override 20 | public boolean isValid(String value, ConstraintValidatorContext context) { 21 | if (required && !StringUtils.isBlank(value)) { 22 | String regexp = "[1-9][0-9]{3}[-](0[1-9]|1[0-2])"; 23 | return Pattern.matches(regexp, value); 24 | } 25 | return true; 26 | } 27 | 28 | @Override 29 | public void initialize(YearMonthFormat constraintAnnotation) { 30 | this.required = constraintAnnotation.required(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/common/validator/annotation/IsImage.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.common.validator.annotation; 2 | 3 | import cn.poile.blog.common.validator.IsImageValidator; 4 | 5 | import javax.validation.Constraint; 6 | import javax.validation.Payload; 7 | import java.lang.annotation.*; 8 | 9 | /** 10 | * 是否类型文件类型为图片 11 | * @author: yaohw 12 | * @create: 2019-11-13 11:02 13 | **/ 14 | @Documented 15 | @Target({ElementType.FIELD, ElementType.PARAMETER}) 16 | @Retention(RetentionPolicy.RUNTIME) 17 | @Constraint(validatedBy = IsImageValidator.class) 18 | public @interface IsImage { 19 | boolean required() default true; 20 | String message() default "文件格式不正确,只限bmp,gif,jpeg,jpeg,png,webp格式"; 21 | Class[] groups() default {}; 22 | Class[] payload() default {}; 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/common/validator/annotation/IsPhone.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.common.validator.annotation; 2 | 3 | import cn.poile.blog.common.validator.IsPhoneValidator; 4 | 5 | import javax.validation.Constraint; 6 | import javax.validation.Payload; 7 | import java.lang.annotation.*; 8 | 9 | /** 10 | * 是否手机号注解 11 | * @author: yaohw 12 | * @create: 2019-10-04 09:57 13 | */ 14 | @Documented 15 | @Target({ElementType.FIELD, ElementType.PARAMETER}) 16 | @Retention(RetentionPolicy.RUNTIME) 17 | @Constraint(validatedBy = IsPhoneValidator.class) 18 | public @interface IsPhone { 19 | boolean required() default true; 20 | String message() default "手机号格式不正确"; 21 | Class[] groups() default {}; 22 | Class[] payload() default {}; 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/common/validator/annotation/ListSize.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.common.validator.annotation; 2 | 3 | /** 4 | * @author: yaohw 5 | * @create: 2019-11-15 15:23 6 | **/ 7 | 8 | import cn.poile.blog.common.validator.ListSizeValidator; 9 | 10 | import javax.validation.Constraint; 11 | import javax.validation.Payload; 12 | import java.lang.annotation.*; 13 | 14 | /** 15 | * 集合长度 16 | * @author: yaohw 17 | * @create: 2019-11-13 11:02 18 | **/ 19 | @Documented 20 | @Target({ElementType.FIELD, ElementType.PARAMETER}) 21 | @Retention(RetentionPolicy.RUNTIME) 22 | @Constraint(validatedBy = ListSizeValidator.class) 23 | public @interface ListSize { 24 | boolean required() default true; 25 | int min() default -1; 26 | int max() default -1; 27 | String message(); 28 | Class[] groups() default {}; 29 | Class[] payload() default {}; 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/common/validator/annotation/YearMonthFormat.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.common.validator.annotation; 2 | 3 | import cn.poile.blog.common.validator.YearMonthFormatValidator; 4 | 5 | import javax.validation.Constraint; 6 | import javax.validation.Payload; 7 | import java.lang.annotation.*; 8 | 9 | /** 10 | * yyyy-mm,年月格式校验 11 | * @author: yaohw 12 | * @create: 2019-11-28 09:23 13 | **/ 14 | @Documented 15 | @Target({ElementType.FIELD, ElementType.PARAMETER}) 16 | @Retention(RetentionPolicy.RUNTIME) 17 | @Constraint(validatedBy = YearMonthFormatValidator.class) 18 | public @interface YearMonthFormat { 19 | boolean required() default true; 20 | String message() default "年月格式不正确"; 21 | Class[] groups() default {}; 22 | Class[] payload() default {}; 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/config/CorsConfig.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.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 | * 跨域配置 9 | * @author: yaohw 10 | * @create: 2019-10-30 17:17 11 | **/ 12 | @Configuration 13 | public class CorsConfig implements WebMvcConfigurer { 14 | 15 | @Override 16 | public void addCorsMappings(CorsRegistry registry) { 17 | registry.addMapping("/**") 18 | .allowedOrigins("*") 19 | .allowCredentials(true) 20 | .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") 21 | .maxAge(3600); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/config/DruidDataSourceConfig.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.config; 2 | 3 | import com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties; 4 | import com.alibaba.druid.support.http.StatViewServlet; 5 | import com.alibaba.druid.support.http.WebStatFilter; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.boot.web.servlet.FilterRegistrationBean; 8 | import org.springframework.boot.web.servlet.ServletRegistrationBean; 9 | import org.springframework.context.annotation.Bean; 10 | import org.springframework.context.annotation.Configuration; 11 | 12 | import java.util.HashMap; 13 | import java.util.Map; 14 | 15 | /** 16 | * 阿里数据库连接池配置、监控配置 17 | * @author: yaohw 18 | * @create: 2019-10-23 16:07 19 | **/ 20 | @Configuration 21 | public class DruidDataSourceConfig { 22 | 23 | 24 | @Autowired 25 | private DruidStatProperties druidStatProperties; 26 | 27 | 28 | /** 29 | * 配置Druid监控的StatViewServlet和WebStatFilter 30 | */ 31 | @Bean 32 | public ServletRegistrationBean druidServlet(){ 33 | ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean<>(); 34 | servletRegistrationBean.setServlet(new StatViewServlet()); 35 | //监控页面http://127.0.0.1:8081/druid/sql.html 36 | servletRegistrationBean.addUrlMappings("/druid/*"); 37 | Map initParameters = new HashMap(3); 38 | initParameters.put("loginUsername", druidStatProperties.getStatViewServlet().getLoginUsername()); 39 | initParameters.put("loginPassword", druidStatProperties.getStatViewServlet().getLoginPassword()); 40 | initParameters.put("resetEnable", "true"); 41 | servletRegistrationBean.setInitParameters(initParameters); 42 | return servletRegistrationBean; 43 | } 44 | 45 | @Bean 46 | public FilterRegistrationBean filterRegistrationBean(){ 47 | FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean<>(); 48 | filterRegistrationBean.setFilter(new WebStatFilter()); 49 | filterRegistrationBean.addUrlPatterns("/*"); 50 | filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*"); 51 | return filterRegistrationBean; 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/config/LimiterConfig.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.config; 2 | 3 | import cn.poile.blog.common.limiter.ExtraLimiter; 4 | import cn.poile.blog.common.limiter.SmsLimiter; 5 | import cn.poile.blog.common.sms.SmsServiceProperties; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.stereotype.Component; 8 | 9 | /** 10 | * @author: yaohw 11 | * @create: 2019-11-23 11:17 12 | **/ 13 | @Component 14 | public class LimiterConfig { 15 | 16 | 17 | @Bean("smsLimiter") 18 | public ExtraLimiter limiter(SmsServiceProperties properties) { 19 | SmsLimiter smsLimiter = new SmsLimiter(); 20 | smsLimiter.setDayMax(properties.getDayMax()); 21 | return smsLimiter; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/config/MybatisPlusConfig.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.config; 2 | 3 | import com.baomidou.mybatisplus.core.injector.ISqlInjector; 4 | import com.baomidou.mybatisplus.extension.injector.LogicSqlInjector; 5 | import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor; 6 | import org.mybatis.spring.annotation.MapperScan; 7 | import org.springframework.context.annotation.Bean; 8 | import org.springframework.context.annotation.Configuration; 9 | import org.springframework.transaction.annotation.EnableTransactionManagement; 10 | 11 | /** 12 | * @author: yaohw 13 | * @create: 2019-10-23 16:51 14 | **/ 15 | @EnableTransactionManagement 16 | @Configuration 17 | public class MybatisPlusConfig { 18 | 19 | /** 20 | * mybatis-plus分页插件
21 | * 文档:http://mp.baomidou.com
22 | */ 23 | @Bean 24 | public PaginationInterceptor paginationInterceptor() { 25 | PaginationInterceptor paginationInterceptor = new PaginationInterceptor(); 26 | paginationInterceptor.setDialectType("mysql"); 27 | return paginationInterceptor; 28 | } 29 | 30 | /** 31 | * 逻辑删除 32 | * 3.1.1开始不再需要这一步 33 | * @return 34 | */ 35 | @Bean 36 | public ISqlInjector sqlInjector() { 37 | return new LogicSqlInjector(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/config/SwaggerConfig.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.config; 2 | 3 | import org.springframework.beans.factory.annotation.Value; 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.context.annotation.Configuration; 6 | import springfox.documentation.builders.ApiInfoBuilder; 7 | import springfox.documentation.builders.ParameterBuilder; 8 | import springfox.documentation.builders.PathSelectors; 9 | import springfox.documentation.builders.RequestHandlerSelectors; 10 | import springfox.documentation.schema.ModelRef; 11 | import springfox.documentation.service.ApiInfo; 12 | import springfox.documentation.service.Contact; 13 | import springfox.documentation.service.Parameter; 14 | import springfox.documentation.spi.DocumentationType; 15 | import springfox.documentation.spring.web.plugins.Docket; 16 | import springfox.documentation.swagger2.annotations.EnableSwagger2; 17 | 18 | import java.util.ArrayList; 19 | import java.util.List; 20 | 21 | /** 22 | * @author: yaohw 23 | * @create: 2019-10-31 14:26 24 | **/ 25 | @Configuration 26 | @EnableSwagger2 27 | public class SwaggerConfig { 28 | /** 29 | * 是否开启swagger,默认开启 30 | */ 31 | @Value(value = "${swagger.enabled:true}") 32 | private Boolean swaggerEnabled; 33 | 34 | @Bean 35 | public Docket createRestApi() { 36 | ParameterBuilder parameterBuilder = new ParameterBuilder(); 37 | List parameterList = new ArrayList<>(); 38 | parameterBuilder.name("Authorization").description("格式:Bearer access_token") 39 | .modelRef(new ModelRef("string")).parameterType("header") 40 | .required(true).build(); 41 | parameterList.add(parameterBuilder.build()); 42 | return new Docket(DocumentationType.SWAGGER_2) 43 | .apiInfo(apiInfo()) 44 | .enable(swaggerEnabled) 45 | .select() 46 | .apis(RequestHandlerSelectors.basePackage("cn.poile.blog")) 47 | .paths(PathSelectors.any()) 48 | .build().globalOperationParameters(parameterList); 49 | } 50 | 51 | private ApiInfo apiInfo() { 52 | return new ApiInfoBuilder() 53 | .title("BLOG RESTFUL API") 54 | .description("BLOG RESTFUL API") 55 | .contact(new Contact("yaohw","http://www.poile.cn/api","yaohw484@gmail.com")) 56 | .version("2.0") 57 | .build(); 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/controller/ArticleCollectController.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.controller; 2 | 3 | 4 | import cn.poile.blog.common.response.ApiResponse; 5 | import cn.poile.blog.service.ArticleRecommendService; 6 | import cn.poile.blog.service.IArticleCollectService; 7 | import cn.poile.blog.vo.ArticleVo; 8 | import com.baomidou.mybatisplus.core.metadata.IPage; 9 | import io.swagger.annotations.Api; 10 | import io.swagger.annotations.ApiOperation; 11 | import io.swagger.annotations.ApiParam; 12 | import org.springframework.beans.factory.annotation.Autowired; 13 | import org.springframework.web.bind.annotation.*; 14 | 15 | import cn.poile.blog.controller.BaseController; 16 | 17 | import javax.validation.constraints.NotNull; 18 | 19 | /** 20 | *

21 | * 文章收藏表 前端控制器 22 | *

23 | * 24 | * @author yaohw 25 | * @since 2019-12-04 26 | */ 27 | @RestController 28 | @RequestMapping("/article/collect") 29 | @Api(tags = "文章收藏服务", value = "/article/collect") 30 | public class ArticleCollectController extends BaseController { 31 | 32 | @Autowired 33 | private ArticleRecommendService articleRecommendService; 34 | 35 | @Autowired 36 | private IArticleCollectService articleCollectService; 37 | 38 | @PostMapping("/add") 39 | @ApiOperation(value = "新增收藏",notes = "需要accessToken") 40 | public ApiResponse add(@ApiParam(value = "文章id") @NotNull(message = "文章id不能为空") @RequestParam(value = "articleId") Integer articleId) { 41 | articleCollectService.add(articleId); 42 | articleRecommendService.asyncRefresh(articleId); 43 | return createResponse(); 44 | } 45 | 46 | @DeleteMapping("/delete") 47 | @ApiOperation(value = "删除收藏",notes = "需要accessToken") 48 | public ApiResponse delete(@ApiParam("文章id") @NotNull(message = "文章id不能为空") @RequestParam("articleId") Integer articleId) { 49 | articleCollectService.delete(articleId); 50 | articleRecommendService.asyncRefresh(articleId); 51 | return createResponse(); 52 | } 53 | 54 | @GetMapping("/page") 55 | @ApiOperation(value = "分页查询收藏文章",notes = "需要accessToken") 56 | public ApiResponse> page( 57 | @ApiParam("当前页,默认值:1") @RequestParam(value = "current", required = false, defaultValue = "1") long current, 58 | @ApiParam("每页数量,默认值为:5") @RequestParam(value = "size", required = false, defaultValue = "5") long size) { 59 | return createResponse(articleCollectService.page(current,size)); 60 | } 61 | 62 | @GetMapping("/collected/{articleId}") 63 | @ApiOperation(value = "查询文章是否已收藏,1:是,0:否",notes = "需要accessToken") 64 | public ApiResponse collected(@ApiParam("文章id")@PathVariable("articleId") Integer articleId) { 65 | return createResponse(articleCollectService.collected(articleId)); 66 | } 67 | 68 | 69 | 70 | 71 | 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/controller/ArticleCommentController.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.controller; 2 | 3 | 4 | import cn.poile.blog.common.response.ApiResponse; 5 | import cn.poile.blog.service.IArticleCommentService; 6 | import cn.poile.blog.vo.ArticleCommentVo; 7 | import com.baomidou.mybatisplus.core.metadata.IPage; 8 | import io.swagger.annotations.Api; 9 | import io.swagger.annotations.ApiOperation; 10 | import io.swagger.annotations.ApiParam; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.web.bind.annotation.*; 13 | 14 | import javax.validation.constraints.NotBlank; 15 | import javax.validation.constraints.NotNull; 16 | import java.util.List; 17 | 18 | /** 19 | *

20 | * 文章评论表 前端控制器 21 | *

22 | * 23 | * @author yaohw 24 | * @since 2019-12-03 25 | */ 26 | @RestController 27 | @RequestMapping("/article/comment") 28 | @Api(tags = "文章评论服务", value = "/article/comment") 29 | public class ArticleCommentController extends BaseController { 30 | 31 | @Autowired 32 | private IArticleCommentService articleCommentService; 33 | 34 | 35 | @PostMapping("/add") 36 | @ApiOperation(value = "新增文章评论",notes = "需要accessToken") 37 | public ApiResponse add(@ApiParam(value = "文章id") @NotNull(message = "文章id不能为空") @RequestParam(value = "articleId") Integer articleId, 38 | @ApiParam(value = "评论内容") @NotBlank(message = "评论内容不能为空") @RequestParam(value = "content") String content) { 39 | articleCommentService.add(articleId,content); 40 | articleCommentService.asyncRefreshRecommendAndSendCommentMail(articleId,content); 41 | return createResponse(); 42 | } 43 | 44 | @DeleteMapping("/delete") 45 | @ApiOperation(value = "删除文章评论",notes = "逻辑删除,需要accessToken,管理员或评论发表者可删除") 46 | public ApiResponse delete(@ApiParam(value = "评论id") @NotNull(message = "评论id不能为空") @RequestParam(value = "commentId") Integer commentId) { 47 | articleCommentService.delete(commentId); 48 | return createResponse(); 49 | } 50 | 51 | @GetMapping("/page") 52 | @ApiOperation(value = "分页获取文章评论及回复列表",notes = "包括评论者和回复者信息") 53 | public ApiResponse> page( 54 | @ApiParam("页码") @RequestParam(value = "current", required = false, defaultValue = "1") long current, 55 | @ApiParam("每页数量") @RequestParam(value = "size", required = false, defaultValue = "5") long size, 56 | @ApiParam(value = "文章id") @NotNull(message = "文章id不能为空") @RequestParam(value = "articleId") Integer articleId) { 57 | return createResponse(articleCommentService.selectCommentAndReplyList(current,size,articleId)); 58 | } 59 | 60 | @GetMapping("/latest") 61 | @ApiOperation(value = "最新文章评论列表",notes = "包括文章信息和评论者信息") 62 | public ApiResponse> latest( 63 | @ApiParam("数量限制,默认值:5") @RequestParam(value = "limit", required = false, defaultValue = "5") long limit) { 64 | return createResponse(articleCommentService.selectLatestComment(limit)); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/controller/ArticleLikeController.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.controller; 2 | 3 | 4 | import cn.poile.blog.common.response.ApiResponse; 5 | import cn.poile.blog.service.ArticleRecommendService; 6 | import cn.poile.blog.service.IArticleLikeService; 7 | import io.swagger.annotations.Api; 8 | import io.swagger.annotations.ApiOperation; 9 | import io.swagger.annotations.ApiParam; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.web.bind.annotation.*; 12 | 13 | import javax.validation.constraints.NotNull; 14 | import java.util.List; 15 | 16 | /** 17 | *

18 | * 文章点赞表 前端控制器 19 | *

20 | * 21 | * @author yaohw 22 | * @since 2019-12-02 23 | */ 24 | @RestController 25 | @RequestMapping("/article/like") 26 | @Api(tags = "文章点赞服务", value = "/article/like") 27 | public class ArticleLikeController extends BaseController { 28 | 29 | @Autowired 30 | private IArticleLikeService articleLikeService; 31 | 32 | @Autowired 33 | private ArticleRecommendService articleRecommendService; 34 | 35 | 36 | @GetMapping("/liked/{articleId}") 37 | @ApiOperation(value = "查询文章是否已点赞",notes = "1:是,0:否") 38 | public ApiResponse liked(@ApiParam("文章id") @PathVariable("articleId") Integer articleId) { 39 | return createResponse(articleLikeService.liked(articleId)); 40 | } 41 | 42 | @PostMapping("/add") 43 | @ApiOperation(value = "文章点赞") 44 | public ApiResponse like(@ApiParam("文章id") @RequestParam("articleId") Integer articleId) { 45 | articleLikeService.like(articleId); 46 | articleRecommendService.asyncRefresh(articleId); 47 | return createResponse(); 48 | } 49 | 50 | @DeleteMapping("/cancel") 51 | @ApiOperation(value = "取消文章点赞") 52 | public ApiResponse cancel(@ApiParam("文章id") @NotNull(message = "文章id不能为空") @RequestParam("articleId") Integer articleId) { 53 | articleLikeService.cancel(articleId); 54 | articleRecommendService.asyncRefresh(articleId); 55 | return createResponse(); 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/controller/ArticleReplyController.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.controller; 2 | 3 | 4 | import cn.poile.blog.common.response.ApiResponse; 5 | import cn.poile.blog.service.IArticleReplyService; 6 | import io.swagger.annotations.Api; 7 | import io.swagger.annotations.ApiOperation; 8 | import io.swagger.annotations.ApiParam; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.web.bind.annotation.*; 11 | 12 | import cn.poile.blog.controller.BaseController; 13 | 14 | import javax.validation.constraints.NotBlank; 15 | import javax.validation.constraints.NotNull; 16 | 17 | /** 18 | *

19 | * 文章回复表 前端控制器 20 | *

21 | * 22 | * @author yaohw 23 | * @since 2019-12-03 24 | */ 25 | @RestController 26 | @RequestMapping("/article/reply") 27 | @Api(tags = "文章评论回复服务",value = "/article/reply") 28 | public class ArticleReplyController extends BaseController { 29 | 30 | @Autowired 31 | private IArticleReplyService articleReplyService; 32 | 33 | @PostMapping("/add") 34 | @ApiOperation(value = "新增文章评论回复",notes = "需要accessToken") 35 | public ApiResponse add(@ApiParam(value = "文章id") @NotNull(message = "文章id不能为空") @RequestParam(value = "articleId") Integer articleId, 36 | @ApiParam(value = "评论id") @NotNull(message = "评论id不能为空") @RequestParam(value = "commentId") Integer commentId, 37 | @ApiParam(value = "被回复者id") @NotNull(message = "被回复者id不能为空") @RequestParam(value = "toUserId") Integer toUserId, 38 | @ApiParam(value = "回复内容") @NotBlank(message = "回复内容不能为空") @RequestParam(value = "content") String content) { 39 | articleReplyService.add(articleId, commentId, toUserId, content); 40 | articleReplyService.asyncSendMail(articleId,toUserId,content); 41 | return createResponse(); 42 | } 43 | 44 | @DeleteMapping("/delete") 45 | @ApiOperation(value = "删除回复",notes = "逻辑删除,需要accessToken,管理员或回复者可删除") 46 | public ApiResponse delete(@ApiParam(value = "回复id") @NotNull(message = "回复id不能为空") @RequestParam(value = "replyId") Integer replyId) { 47 | articleReplyService.delete(replyId); 48 | return createResponse(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/controller/ArticleTagController.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.controller; 2 | 3 | 4 | import org.springframework.web.bind.annotation.RequestMapping; 5 | 6 | import org.springframework.web.bind.annotation.RestController; 7 | import cn.poile.blog.controller.BaseController; 8 | 9 | /** 10 | *

11 | * 文章-标签 关联表 前端控制器 12 | *

13 | * 14 | * @author yaohw 15 | * @since 2019-11-15 16 | */ 17 | @RestController 18 | @RequestMapping("/articleTag") 19 | public class ArticleTagController extends BaseController { 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/controller/BaseController.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.controller; 2 | 3 | import cn.poile.blog.common.constant.ErrorEnum; 4 | import cn.poile.blog.common.response.ApiResponse; 5 | import org.springframework.validation.annotation.Validated; 6 | 7 | /** 8 | * @author: yaohw 9 | * @create: 2019-10-23 12:36 10 | **/ 11 | @Validated 12 | public class BaseController { 13 | 14 | private ApiResponse init() { 15 | return new ApiResponse<>(); 16 | } 17 | 18 | protected ApiResponse createResponse() { 19 | ApiResponse response = init(); 20 | response.setCode(ErrorEnum.SUCCESS.getErrorCode()); 21 | response.setMessage(ErrorEnum.SUCCESS.getErrorMsg()); 22 | return response; 23 | } 24 | 25 | protected ApiResponse createResponse(T body) { 26 | ApiResponse response = createResponse(); 27 | response.setData(body); 28 | return response; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/controller/CategoryController.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.controller; 2 | 3 | 4 | import cn.poile.blog.common.response.ApiResponse; 5 | import cn.poile.blog.controller.model.dto.CategoryNodeDTO; 6 | import cn.poile.blog.controller.model.request.AddCategoryRequest; 7 | import cn.poile.blog.entity.Category; 8 | import cn.poile.blog.service.ICategoryService; 9 | import com.baomidou.mybatisplus.core.metadata.IPage; 10 | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; 11 | import io.swagger.annotations.Api; 12 | import io.swagger.annotations.ApiOperation; 13 | import io.swagger.annotations.ApiParam; 14 | import org.springframework.beans.factory.annotation.Autowired; 15 | import org.springframework.security.access.prepost.PreAuthorize; 16 | import org.springframework.validation.annotation.Validated; 17 | import org.springframework.web.bind.annotation.*; 18 | 19 | import java.util.List; 20 | 21 | /** 22 | *

23 | * 目录分类表 前端控制器 24 | *

25 | * 26 | * @author yaohw 27 | * @since 2019-11-14 28 | */ 29 | @RestController 30 | @RequestMapping("/category") 31 | @Api(tags = "分类服务",value = "/category") 32 | public class CategoryController extends BaseController { 33 | 34 | @Autowired 35 | private ICategoryService categoryService; 36 | 37 | 38 | @PostMapping("/add") 39 | @PreAuthorize("hasAuthority('admin')") 40 | @ApiOperation(value = "新增分类",notes = "需要accessToken,需要管理员权限") 41 | public ApiResponse add(@Validated @RequestBody AddCategoryRequest request) { 42 | categoryService.add(request); 43 | return createResponse(); 44 | } 45 | 46 | 47 | @GetMapping("/tree") 48 | @ApiOperation(value = "获取分类树",notes = "数据结构为树型结构,需要accessToken,需要管理员权限") 49 | public ApiResponse> tree() { 50 | return createResponse(categoryService.getCategoryNodeTree()); 51 | } 52 | 53 | @GetMapping("/list") 54 | @ApiOperation(value = "获取分类列表",notes = "不分上下级,返回所有分类(已删除除外)") 55 | public ApiResponse> list() { 56 | return createResponse(categoryService.list()); 57 | } 58 | 59 | @GetMapping("/page") 60 | @ApiOperation(value = "分页获取分类",notes = "需要accessToken,需要管理员权限") 61 | public ApiResponse> page( @ApiParam("页码") @RequestParam(value = "current", required = false, defaultValue = "1") long current, 62 | @ApiParam("每页数量") @RequestParam(value = "size", required = false, defaultValue = "5") long size) { 63 | Page page = new Page<>(current, size); 64 | return createResponse(categoryService.page(page)); 65 | } 66 | 67 | 68 | @PostMapping("/update") 69 | @PreAuthorize("hasAuthority('admin')") 70 | @ApiOperation(value = "修改分类名",notes = "需要accessToken,需要管理员权限") 71 | public ApiResponse update(@ApiParam("分类id") @RequestParam("id") int id, 72 | @ApiParam("标签名") @RequestParam(value = "name") String name) { 73 | categoryService.updateCategoryById(id,name); 74 | return createResponse(); 75 | } 76 | 77 | 78 | @DeleteMapping("/delete/{id}") 79 | @PreAuthorize("hasAuthority('admin')") 80 | @ApiOperation(value = "删除分类",notes = "需要accessToken,逻辑删除,需要管理员权限,若存在子类,则不允许删除") 81 | public ApiResponse delete(@ApiParam("分类id") @PathVariable("id") int id) { 82 | categoryService.delete(id); 83 | return createResponse(); 84 | } 85 | 86 | } 87 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/controller/ClientController.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.controller; 2 | 3 | 4 | import cn.poile.blog.common.constant.ErrorEnum; 5 | import cn.poile.blog.common.exception.ApiException; 6 | import cn.poile.blog.common.response.ApiResponse; 7 | import cn.poile.blog.entity.Client; 8 | import cn.poile.blog.service.IClientService; 9 | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; 10 | import com.baomidou.mybatisplus.core.metadata.IPage; 11 | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; 12 | import io.swagger.annotations.Api; 13 | import io.swagger.annotations.ApiOperation; 14 | import io.swagger.annotations.ApiParam; 15 | import org.springframework.beans.factory.annotation.Autowired; 16 | import org.springframework.security.access.prepost.PreAuthorize; 17 | import org.springframework.validation.annotation.Validated; 18 | import org.springframework.web.bind.annotation.*; 19 | 20 | /** 21 | *

22 | * 客户端表 前端控制器 23 | *

24 | * 25 | * @author yaohw 26 | * @since 2019-12-06 27 | */ 28 | @RestController 29 | @RequestMapping("/client") 30 | @Api(tags = "客户端服务",value = "/client") 31 | public class ClientController extends BaseController { 32 | 33 | @Autowired 34 | private IClientService clientService; 35 | 36 | @GetMapping("/page") 37 | @PreAuthorize("hasAuthority('admin')") 38 | @ApiOperation(value = "分页获取客户端列表",notes = "需要accessToken,需要管理员权限") 39 | public ApiResponse> page(@ApiParam("页码") @RequestParam(value = "current", required = false, defaultValue = "1") long current, 40 | @ApiParam("每页数量") @RequestParam(value = "size", required = false, defaultValue = "5") long size) { 41 | return createResponse(clientService.page(new Page<>(current,size))); 42 | } 43 | 44 | @DeleteMapping("/delete/{id}") 45 | @ApiOperation(value = "删除客户端",notes = "需要accessToken,需要管理员权限") 46 | public ApiResponse delete(@ApiParam("id") @PathVariable(value = "id") int id) { 47 | clientService.removeById(id); 48 | clientService.clearCache(); 49 | return createResponse(); 50 | } 51 | 52 | @PostMapping("/save") 53 | @ApiOperation(value = "新增或更新客户端,id为null时新增",notes = "需要accessToken,需要管理员权限") 54 | public ApiResponse save(@Validated @RequestBody Client client) { 55 | validateExist(client); 56 | clientService.saveOrUpdate(client); 57 | clientService.clearCache(); 58 | return createResponse(); 59 | } 60 | 61 | /** 62 | * 校验是否已存在 63 | * @param client 64 | */ 65 | private void validateExist(Client client) { 66 | if (client.getId() == null) { 67 | QueryWrapper queryWrapper = new QueryWrapper<>(); 68 | queryWrapper.lambda().eq(Client::getClientId,client.getClientId()); 69 | int count = clientService.count(queryWrapper); 70 | if (count != 0) { 71 | throw new ApiException(ErrorEnum.INVALID_REQUEST.getErrorCode(),"客户端已存在"); 72 | } 73 | } 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/controller/FileController.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.controller; 2 | 3 | import cn.poile.blog.common.oss.PageStorageObject; 4 | import cn.poile.blog.common.oss.Storage; 5 | import cn.poile.blog.common.response.ApiResponse; 6 | import io.swagger.annotations.Api; 7 | import io.swagger.annotations.ApiOperation; 8 | import io.swagger.annotations.ApiParam; 9 | import lombok.extern.log4j.Log4j2; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.security.access.prepost.PreAuthorize; 12 | import org.springframework.web.bind.annotation.*; 13 | import org.springframework.web.multipart.MultipartFile; 14 | 15 | import javax.validation.constraints.NotNull; 16 | import java.io.IOException; 17 | 18 | /** 19 | * @author: yaohw 20 | * @create: 2019-10-31 14:13 21 | **/ 22 | @RestController 23 | @RequestMapping("/file") 24 | @Log4j2 25 | @Api(tags = "文件存储服务",value = "file") 26 | public class FileController extends BaseController { 27 | 28 | @Autowired 29 | private Storage storage; 30 | 31 | @PostMapping("/upload") 32 | @PreAuthorize("hasAuthority('admin')") 33 | @ApiOperation(value = "上传文件",notes = "需要accessToken,需要管理员权限") 34 | public ApiResponse upload(@NotNull @RequestParam("file") MultipartFile file) throws IOException { 35 | String filename = file.getOriginalFilename(); 36 | String contentType = file.getContentType(); 37 | String extension = filename.substring(filename.lastIndexOf(".") + 1); 38 | String name = System.currentTimeMillis() + "." +extension; 39 | String fullPath = storage.upload(file.getInputStream(),name,contentType); 40 | return createResponse(fullPath); 41 | } 42 | 43 | @DeleteMapping("/delete") 44 | @PreAuthorize("hasAuthority('admin')") 45 | @ApiOperation(value = "删除文件",notes = "需要accessToken,需要管理员权限") 46 | public ApiResponse delete(@ApiParam("文件全路径") @NotNull @RequestParam("fullPath")String fullPath){ 47 | storage.delete(fullPath); 48 | return createResponse(); 49 | } 50 | 51 | @GetMapping("/page") 52 | @PreAuthorize("hasAuthority('admin')") 53 | @ApiOperation(value = "分页分类列表", notes = "需要accessToken,需要管理员权限") 54 | public ApiResponse page(@ApiParam("marker标记") @RequestParam(value = "marker", required = false) String marker, 55 | @ApiParam("每页数量") @RequestParam(value = "size", required = false, defaultValue = "5") int size) { 56 | return createResponse(storage.page(marker,size)); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/controller/FriendLinkController.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.controller; 2 | 3 | 4 | import cn.poile.blog.common.oss.Storage; 5 | import cn.poile.blog.common.response.ApiResponse; 6 | import cn.poile.blog.entity.FriendLink; 7 | import cn.poile.blog.service.IFriendLinkService; 8 | import com.baomidou.mybatisplus.core.metadata.IPage; 9 | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; 10 | import io.swagger.annotations.Api; 11 | import io.swagger.annotations.ApiOperation; 12 | import io.swagger.annotations.ApiParam; 13 | import org.apache.commons.lang3.StringUtils; 14 | import org.springframework.beans.factory.annotation.Autowired; 15 | import org.springframework.web.bind.annotation.*; 16 | 17 | import java.util.List; 18 | 19 | /** 20 | *

21 | * 友链表 前端控制器 22 | *

23 | * 24 | * @author yaohw 25 | * @since 2019-12-02 26 | */ 27 | @RestController 28 | @RequestMapping("/friend/link") 29 | @Api(tags = "友链服务", value = "/friend/link") 30 | public class FriendLinkController extends BaseController { 31 | 32 | @Autowired 33 | private IFriendLinkService friendLinkService; 34 | 35 | @Autowired 36 | private Storage storage; 37 | 38 | @PostMapping("/save") 39 | @ApiOperation(value = "新增或更新友链", notes = "id不为空时更新,需要accessToken,需要管理员权限") 40 | public ApiResponse add(@RequestBody FriendLink friendLink) { 41 | friendLinkService.saveOrUpdate(friendLink); 42 | return createResponse(); 43 | } 44 | 45 | @GetMapping("/list") 46 | @ApiOperation(value = "获取友链列表") 47 | public ApiResponse> list() { 48 | return createResponse(friendLinkService.list()); 49 | } 50 | 51 | @GetMapping("/page") 52 | @ApiOperation(value = "分页获取友链列表") 53 | public ApiResponse> page( 54 | @ApiParam("页码") @RequestParam(value = "current", required = false, defaultValue = "1") long current, 55 | @ApiParam("每页数量") @RequestParam(value = "size", required = false, defaultValue = "5") long size) { 56 | Page page = new Page<>(current, size); 57 | return createResponse(friendLinkService.page(page)); 58 | } 59 | 60 | @DeleteMapping("/delete/{id}") 61 | @ApiOperation(value = "删除友链") 62 | public ApiResponse delete(@ApiParam("友链id") @PathVariable(value = "id") int id) { 63 | FriendLink friendLink = friendLinkService.getById(id); 64 | if (friendLink != null && StringUtils.isNotBlank(friendLink.getIcon())) { 65 | deleteIcon(friendLink.getIcon()); 66 | } 67 | friendLinkService.removeById(id); 68 | return createResponse(); 69 | } 70 | 71 | /** 72 | * 删除icon 73 | * @param path 74 | */ 75 | private void deleteIcon(String path) { 76 | storage.delete(path); 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/controller/LeaveMessageController.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.controller; 2 | 3 | import cn.poile.blog.common.response.ApiResponse; 4 | import cn.poile.blog.entity.LeaveMessage; 5 | import cn.poile.blog.service.ILeaveMessageService; 6 | import cn.poile.blog.vo.ArticleCommentVo; 7 | import cn.poile.blog.vo.LeaveMessageVo; 8 | import com.baomidou.mybatisplus.core.metadata.IPage; 9 | import io.swagger.annotations.Api; 10 | import io.swagger.annotations.ApiOperation; 11 | import io.swagger.annotations.ApiParam; 12 | import org.springframework.beans.factory.annotation.Autowired; 13 | import org.springframework.web.bind.annotation.*; 14 | 15 | import javax.validation.constraints.NotBlank; 16 | import javax.validation.constraints.NotNull; 17 | import java.util.List; 18 | 19 | /** 20 | *

21 | * 留言表 前端控制器 22 | *

23 | * 24 | * @author yaohw 25 | * @since 2019-12-05 26 | */ 27 | @RestController 28 | @RequestMapping("/leave/message") 29 | @Api(tags = "留言服务",value = "/leave/message") 30 | public class LeaveMessageController extends BaseController { 31 | 32 | 33 | @Autowired 34 | private ILeaveMessageService messageService; 35 | 36 | @PostMapping("/add") 37 | @ApiOperation(value = "新增留言",notes = "需要accessToken") 38 | public ApiResponse add(@ApiParam("留言内容") @NotBlank(message = "内容不能为空") @RequestParam("content") String content) { 39 | messageService.add(content); 40 | return createResponse(); 41 | } 42 | 43 | @PostMapping("/reply") 44 | @ApiOperation(value = "留言回复",notes = "需要accessToken") 45 | public ApiResponse reply( 46 | @ApiParam("父id") @NotNull(message = "pid不能为空") @RequestParam(value = "pid") Integer pid, 47 | @ApiParam("被回复者id") @NotNull(message = "被回复者id不能为空") @RequestParam(value = "toUserId") Integer toUserId, 48 | @ApiParam("回复内容") @NotBlank(message = "内容不能为空") @RequestParam("content") String content) { 49 | messageService.reply(pid,toUserId,content); 50 | return createResponse(); 51 | } 52 | 53 | @GetMapping("/page") 54 | @ApiOperation(value = "分页获取留言及回复列表",notes = "包括留言者和回复者信息") 55 | public ApiResponse> page( 56 | @ApiParam("页码") @RequestParam(value = "current", required = false, defaultValue = "1") long current, 57 | @ApiParam("每页数量") @RequestParam(value = "size", required = false, defaultValue = "5") long size) { 58 | return createResponse(messageService.page(current,size)); 59 | } 60 | 61 | @DeleteMapping("/delete/{id}") 62 | @ApiOperation(value = "删除留言或留言回复",notes = "需要accessToken,本人和管理员可删除") 63 | public ApiResponse delete(@ApiParam("id") @PathVariable("id") Integer id) { 64 | messageService.delete(id); 65 | return createResponse(); 66 | } 67 | 68 | @GetMapping("/latest") 69 | @ApiOperation(value = "最新留言列表",notes = "包括留言者") 70 | public ApiResponse> latest( 71 | @ApiParam("数量限制,默认值:5") @RequestParam(value = "limit", required = false, defaultValue = "5") long limit) { 72 | return createResponse(messageService.selectLatest(limit)); 73 | } 74 | 75 | 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/controller/SmsController.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.controller; 2 | 3 | import cn.poile.blog.biz.EmailService; 4 | import cn.poile.blog.common.constant.RedisConstant; 5 | import cn.poile.blog.common.limiter.annotation.RateLimiter; 6 | import cn.poile.blog.common.response.ApiResponse; 7 | import cn.poile.blog.common.sms.SmsCodeService; 8 | import cn.poile.blog.common.validator.annotation.IsPhone; 9 | import io.swagger.annotations.Api; 10 | import io.swagger.annotations.ApiOperation; 11 | import io.swagger.annotations.ApiParam; 12 | import lombok.extern.log4j.Log4j2; 13 | import org.springframework.beans.factory.annotation.Autowired; 14 | import org.springframework.web.bind.annotation.PostMapping; 15 | import org.springframework.web.bind.annotation.RequestMapping; 16 | import org.springframework.web.bind.annotation.RequestParam; 17 | import org.springframework.web.bind.annotation.RestController; 18 | 19 | import javax.validation.constraints.NotNull; 20 | 21 | /** 22 | * @author: yaohw 23 | * @create: 2019-11-05 09:34 24 | **/ 25 | @Log4j2 26 | @RestController 27 | @RequestMapping("/sms") 28 | @Api(tags = "短信验证码服务",value = "/sms") 29 | public class SmsController extends BaseController{ 30 | 31 | @Autowired 32 | private SmsCodeService smsCodeService; 33 | 34 | @Autowired 35 | private EmailService emailService; 36 | 37 | @PostMapping("/send") 38 | @RateLimiter(name = RedisConstant.SMS_LIMIT_NAME,max = 1,key = "#mobile", timeout = 120L, extra = "smsLimiter") 39 | @ApiOperation(value = "发送短信验证码",notes = "验证码有效时5分钟;同一手机号每天只能发10次;同一ip每天只能发10次;同一手机号限流120s一次") 40 | public ApiResponse sendSmsCode(@ApiParam("手机号") @NotNull @IsPhone @RequestParam long mobile) { 41 | smsCodeService.sendSmsCode(mobile); 42 | return createResponse(); 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/controller/TagController.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.controller; 2 | 3 | 4 | import cn.poile.blog.common.response.ApiResponse; 5 | import cn.poile.blog.entity.Tag; 6 | import cn.poile.blog.service.ITagService; 7 | import com.baomidou.mybatisplus.core.metadata.IPage; 8 | import io.swagger.annotations.Api; 9 | import io.swagger.annotations.ApiOperation; 10 | import io.swagger.annotations.ApiParam; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.security.access.prepost.PreAuthorize; 13 | import org.springframework.validation.annotation.Validated; 14 | import org.springframework.web.bind.annotation.*; 15 | 16 | import javax.validation.constraints.NotBlank; 17 | import java.util.List; 18 | 19 | /** 20 | *

21 | * 标签表 前端控制器 22 | *

23 | * 24 | * @author yaohw 25 | * @since 2019-11-14 26 | */ 27 | @RestController 28 | @RequestMapping("/tag") 29 | @Api(tags = "标签服务", value = "/tag") 30 | public class TagController extends BaseController { 31 | 32 | @Autowired 33 | private ITagService tagService; 34 | 35 | @PostMapping("/add") 36 | @PreAuthorize("hasAuthority('admin')") 37 | @ApiOperation(value = "添加标签", notes = "需要accessToken,需要管理员权限") 38 | public ApiResponse addTag(@NotBlank(message = "标签名不能为空") @RequestParam("tagName") String tagName) { 39 | tagService.addTag(tagName); 40 | return createResponse(); 41 | } 42 | 43 | 44 | @GetMapping("/page") 45 | @PreAuthorize("hasAuthority('admin')") 46 | @ApiOperation(value = "分页查询标签", notes = "需要accessToken,需要管理员权限") 47 | public ApiResponse> page(@ApiParam("当前页") @RequestParam(value = "current", required = false, defaultValue = "1") long current, 48 | @ApiParam("每页数量") @RequestParam(value = "size", required = false, defaultValue = "5") long size, 49 | @ApiParam("标签名关键字,可空") @RequestParam(value = "tagName", required = false) String tagName) { 50 | return createResponse(tagService.selectTagPage(current, size, tagName)); 51 | } 52 | 53 | 54 | @GetMapping("/list") 55 | @ApiOperation(value = "获取标签列表", notes = "不需要accessToken") 56 | public ApiResponse> list(@ApiParam("标签名关键字,可空") @RequestParam(value = "tagName", required = false) String tagName) { 57 | return createResponse(tagService.selectTagList(tagName)); 58 | } 59 | 60 | 61 | @PostMapping("/update") 62 | @PreAuthorize("hasAuthority('admin')") 63 | @ApiOperation(value = "修改标签名",notes = "需要accessToken,需要管理员权限") 64 | public ApiResponse update(@ApiParam("标签id") @RequestParam("id") int id, 65 | @ApiParam("标签名") @RequestParam(value = "name") String name) { 66 | tagService.update(id,name); 67 | return createResponse(); 68 | } 69 | 70 | 71 | @DeleteMapping("/delete/{id}") 72 | @PreAuthorize("hasAuthority('admin')") 73 | @ApiOperation(value = "删除标签",notes = "需要accessToken,需要管理员权限,逻辑删除") 74 | public ApiResponse delete(@ApiParam("标签id") @PathVariable("id") int id) { 75 | tagService.delete(id); 76 | return createResponse(); 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/controller/model/dto/AccessTokenDTO.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.controller.model.dto; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import io.swagger.annotations.ApiModel; 6 | import io.swagger.annotations.ApiModelProperty; 7 | import lombok.Data; 8 | import lombok.experimental.Accessors; 9 | 10 | /** 11 | * @author: yaohw 12 | * @create: 2019-11-04 17:03 13 | **/ 14 | @Data 15 | @Accessors(chain = true) 16 | @JsonInclude(JsonInclude.Include.NON_NULL) 17 | @ApiModel(value="AccessTokenDTO", description="AccessTokenDTO") 18 | public class AccessTokenDTO { 19 | 20 | @JsonProperty("access_token") 21 | @ApiModelProperty("access_token") 22 | private String accessToken; 23 | 24 | @JsonProperty("token_type") 25 | @ApiModelProperty("token类型:Bearer") 26 | private String tokenType; 27 | 28 | @JsonProperty("refresh_token") 29 | @ApiModelProperty("refresh_token") 30 | private String refreshToken; 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/controller/model/dto/ArticlePageQueryDTO.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.controller.model.dto; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | /** 8 | * @author: yaohw 9 | * @create: 2019-11-28 19:17 10 | **/ 11 | @Data 12 | @NoArgsConstructor 13 | @AllArgsConstructor 14 | public class ArticlePageQueryDTO { 15 | 16 | private Long current; 17 | 18 | private Long size; 19 | 20 | private Integer categoryId; 21 | 22 | private Integer tagId; 23 | 24 | private String yearMonth; 25 | 26 | private String title; 27 | 28 | private String sort; 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/controller/model/dto/CategoryNodeDTO.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.controller.model.dto; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import io.swagger.annotations.ApiModelProperty; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | 9 | import java.util.List; 10 | 11 | /** 12 | * 分类节点 13 | * @author: yaohw 14 | * @create: 2019-11-14 17:38 15 | **/ 16 | @Data 17 | @NoArgsConstructor 18 | @AllArgsConstructor 19 | @JsonInclude(JsonInclude.Include.NON_NULL) 20 | public class CategoryNodeDTO { 21 | 22 | @ApiModelProperty(value = "id") 23 | private Integer id; 24 | 25 | @ApiModelProperty(value = "名称") 26 | private String name; 27 | 28 | @ApiModelProperty(value = "父类id") 29 | private Integer parentId; 30 | 31 | @ApiModelProperty(value = "子目录") 32 | private List children; 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/controller/model/dto/PreArtAndNextArtDTO.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.controller.model.dto; 2 | 3 | import cn.poile.blog.entity.Article; 4 | import lombok.Data; 5 | 6 | /** 7 | * 上一篇和下一篇文章DTO 8 | * @author: yaohw 9 | * @create: 2019-11-26 19:21 10 | **/ 11 | @Data 12 | public class PreArtAndNextArtDTO { 13 | 14 | /** 15 | * 上一篇 16 | */ 17 | private Article pre; 18 | 19 | /** 20 | * 下一篇 21 | */ 22 | private Article next; 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/controller/model/request/AddCategoryRequest.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.controller.model.request; 2 | 3 | import io.swagger.annotations.ApiModel; 4 | import io.swagger.annotations.ApiModelProperty; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | 9 | import javax.validation.constraints.NotBlank; 10 | import javax.validation.constraints.NotNull; 11 | 12 | /** 13 | * @author: yaohw 14 | * @create: 2019-11-14 17:15 15 | **/ 16 | 17 | @Data 18 | @NoArgsConstructor 19 | @AllArgsConstructor 20 | @ApiModel(value = "添加分类json",description = "添加分类") 21 | public class AddCategoryRequest { 22 | 23 | @NotBlank(message = "分类名称不能为空") 24 | @ApiModelProperty(value = "名称") 25 | private String name; 26 | 27 | @NotNull(message = "parentId不能为空") 28 | @ApiModelProperty(value = "父类id,添加根目录分类时值为0") 29 | private Integer parentId; 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/controller/model/request/ArticleRequest.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.controller.model.request; 2 | 3 | import cn.poile.blog.common.validator.annotation.ListSize; 4 | import com.baomidou.mybatisplus.annotation.IdType; 5 | import com.baomidou.mybatisplus.annotation.TableId; 6 | import io.swagger.annotations.ApiModel; 7 | import io.swagger.annotations.ApiModelProperty; 8 | import lombok.AllArgsConstructor; 9 | import lombok.Data; 10 | import lombok.NoArgsConstructor; 11 | 12 | import javax.validation.constraints.NotBlank; 13 | import javax.validation.constraints.NotNull; 14 | import java.util.List; 15 | 16 | /** 17 | * @author: yaohw 18 | * @create: 2019-11-15 15:08 19 | **/ 20 | @Data 21 | @NoArgsConstructor 22 | @AllArgsConstructor 23 | @ApiModel(value = "文章请求json",description = "文章请求体") 24 | public class ArticleRequest { 25 | 26 | @ApiModelProperty(value = "文章id,编辑时不可空") 27 | @TableId(value = "id", type = IdType.AUTO) 28 | private Integer id; 29 | 30 | @NotNull(message = "是否原创标识不能为空") 31 | @ApiModelProperty(value = "是否原创,1:是,0:否") 32 | private Integer original; 33 | 34 | @NotNull(message = "状态不能为空") 35 | @ApiModelProperty(value = "状态,0:发布,1:保存") 36 | private Integer status; 37 | 38 | @NotNull(message = "文章分类id不能为空") 39 | @ApiModelProperty(value = "文章分类id") 40 | private Integer categoryId; 41 | 42 | @NotBlank(message = "文章标题不能为空") 43 | @ApiModelProperty(value = "文章标题") 44 | private String title; 45 | 46 | @NotBlank(message = "文章摘要不能为空") 47 | @ApiModelProperty(value = "文章摘要") 48 | private String summary; 49 | 50 | @NotBlank(message = "文章内容不能为空") 51 | @ApiModelProperty(value = "文章内容") 52 | private String content; 53 | 54 | @ApiModelProperty(value = "文章内容") 55 | private String htmlContent; 56 | 57 | @NotBlank(message = "文章封面不能为空") 58 | @ApiModelProperty(value = "文章封面") 59 | private String cover; 60 | 61 | @ListSize(max = 4,message = "一篇文章最多只允许添加4个标签") 62 | @ApiModelProperty(value = "文章标签id列表") 63 | private List tagIds; 64 | 65 | @ApiModelProperty(value = "转载地址,转载非空") 66 | private String reproduce; 67 | 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/controller/model/request/UpdateUserRequest.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.controller.model.request; 2 | 3 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 4 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 5 | import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer; 6 | import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer; 7 | import io.swagger.annotations.ApiModel; 8 | import io.swagger.annotations.ApiModelProperty; 9 | import lombok.AllArgsConstructor; 10 | import lombok.Data; 11 | import lombok.NoArgsConstructor; 12 | 13 | import javax.validation.constraints.NotNull; 14 | import java.time.LocalDate; 15 | 16 | /** 17 | * @author: yaohw 18 | * @create: 2019-11-08 16:04 19 | **/ 20 | @Data 21 | @NoArgsConstructor 22 | @AllArgsConstructor 23 | @ApiModel(value = "用户更新json",description = "用户更新") 24 | public class UpdateUserRequest { 25 | 26 | @ApiModelProperty(value = "用户id") 27 | @NotNull(message = "用户id不能为空") 28 | private Integer userId; 29 | 30 | @ApiModelProperty(value = "昵称") 31 | private String nickname; 32 | 33 | @ApiModelProperty(value = "性别,1:男,0:女,默认为1") 34 | private Integer gender; 35 | 36 | @JsonDeserialize(using = LocalDateDeserializer.class) 37 | @JsonSerialize(using = LocalDateSerializer.class) 38 | @ApiModelProperty(value = "生日") 39 | private LocalDate birthday; 40 | 41 | @ApiModelProperty(value = "简介|个性签名") 42 | private String brief; 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/controller/model/request/UserRegisterRequest.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.controller.model.request; 2 | 3 | import cn.poile.blog.common.validator.annotation.IsPhone; 4 | import io.swagger.annotations.ApiModel; 5 | import io.swagger.annotations.ApiModelProperty; 6 | import lombok.AllArgsConstructor; 7 | import lombok.Data; 8 | import lombok.NoArgsConstructor; 9 | import org.hibernate.validator.constraints.Length; 10 | 11 | import javax.validation.constraints.NotBlank; 12 | import javax.validation.constraints.NotNull; 13 | import javax.validation.constraints.Pattern; 14 | /** 15 | * @author: yaohw 16 | * @create: 2019-10-04 22:13 17 | */ 18 | @Data 19 | @NoArgsConstructor 20 | @AllArgsConstructor 21 | @ApiModel(value = "用户注册请json",description = "用户注册") 22 | public class UserRegisterRequest { 23 | 24 | @NotBlank(message = "用户名不能为空") 25 | @ApiModelProperty("用户名只能字母开头,允许2-16字节,允许字母数字下划线") 26 | @Pattern(regexp = "^[a-zA-Z][a-zA-Z0-9_]{1,15}$", message = "用户名只能字母开头,允许2-16字节,允许字母数字下划线") 27 | private String username; 28 | 29 | @NotBlank(message = "密码不能为空") 30 | @Length(min = 6,message = "密码至少6位数") 31 | @ApiModelProperty("密码") 32 | private String password; 33 | 34 | @NotNull(message = "手机号不能为空") 35 | @IsPhone 36 | @ApiModelProperty("手机号") 37 | private long mobile; 38 | 39 | @NotBlank(message = "验证码不能为空") 40 | @ApiModelProperty("验证码") 41 | private String code; 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/entity/Article.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableId; 5 | import com.baomidou.mybatisplus.annotation.TableLogic; 6 | import com.fasterxml.jackson.annotation.JsonFormat; 7 | import com.fasterxml.jackson.annotation.JsonIgnore; 8 | import com.fasterxml.jackson.annotation.JsonInclude; 9 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 10 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 11 | import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer; 12 | import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; 13 | import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer; 14 | import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; 15 | import io.swagger.annotations.ApiModel; 16 | import io.swagger.annotations.ApiModelProperty; 17 | import lombok.Data; 18 | import lombok.EqualsAndHashCode; 19 | import lombok.experimental.Accessors; 20 | 21 | import java.io.Serializable; 22 | import java.time.LocalDateTime; 23 | 24 | /** 25 | *

26 | * 文章表 27 | *

28 | * 29 | * @author yaohw 30 | * @since 2019-11-15 31 | */ 32 | @Data 33 | @Accessors(chain = true) 34 | @EqualsAndHashCode(of = "id") 35 | @JsonInclude(JsonInclude.Include.NON_NULL) 36 | @ApiModel(value="Article对象", description="文章表") 37 | public class Article implements Serializable { 38 | 39 | private static final long serialVersionUID = 1L; 40 | 41 | @ApiModelProperty(value = "文章id") 42 | @TableId(value = "id", type = IdType.AUTO) 43 | private Integer id; 44 | 45 | @ApiModelProperty(value = "是否原创,1:是,0:否") 46 | private Integer original; 47 | 48 | @JsonIgnore 49 | @ApiModelProperty(value = "用户id") 50 | private Integer userId; 51 | 52 | @ApiModelProperty(value = "分类名称-冗余字段") 53 | private String categoryName; 54 | 55 | @ApiModelProperty(value = "文章分类id") 56 | private Integer categoryId; 57 | 58 | @ApiModelProperty(value = "文章标题") 59 | private String title; 60 | 61 | @ApiModelProperty(value = "文章摘要") 62 | private String summary; 63 | 64 | @ApiModelProperty(value = "文章内容") 65 | private String content; 66 | 67 | @ApiModelProperty(value = "文章富文本内容") 68 | private String htmlContent; 69 | 70 | @ApiModelProperty(value = "文章封面") 71 | private String cover; 72 | 73 | @ApiModelProperty(value = "文章状态:0为正常,1为待发布,2为回收站") 74 | private Integer status; 75 | 76 | @ApiModelProperty(value = "文章浏览次数") 77 | private Integer viewCount; 78 | 79 | @ApiModelProperty(value = "评论数-冗余字段") 80 | private Integer commentCount; 81 | 82 | @ApiModelProperty(value = "点赞数-冗余字段") 83 | private Integer likeCount; 84 | 85 | @ApiModelProperty(value = "收藏数-冗余字段") 86 | private Integer collectCount; 87 | 88 | @ApiModelProperty(value = "发布时间") 89 | @JsonSerialize(using = LocalDateTimeSerializer.class) 90 | @JsonDeserialize(using = LocalDateTimeDeserializer.class) 91 | @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone = "GMT+8") 92 | private LocalDateTime publishTime; 93 | 94 | @ApiModelProperty(value = "更新时间") 95 | @JsonSerialize(using = LocalDateTimeSerializer.class) 96 | @JsonDeserialize(using = LocalDateTimeDeserializer.class) 97 | @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone = "GMT+8") 98 | private LocalDateTime updateTime; 99 | 100 | @TableLogic 101 | @JsonIgnore 102 | @ApiModelProperty(value = "是否已删除,1:是,0:否") 103 | private Integer deleted; 104 | 105 | @ApiModelProperty(value = "转载地址") 106 | private String reproduce; 107 | 108 | 109 | } 110 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/entity/ArticleCollect.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableId; 5 | import java.io.Serializable; 6 | import io.swagger.annotations.ApiModel; 7 | import io.swagger.annotations.ApiModelProperty; 8 | import lombok.Data; 9 | import lombok.EqualsAndHashCode; 10 | import lombok.experimental.Accessors; 11 | 12 | /** 13 | *

14 | * 文章收藏表 15 | *

16 | * 17 | * @author yaohw 18 | * @since 2019-12-04 19 | */ 20 | @Data 21 | @EqualsAndHashCode(callSuper = false) 22 | @Accessors(chain = true) 23 | @ApiModel(value="ArticleCollect对象", description="文章收藏表") 24 | public class ArticleCollect implements Serializable { 25 | 26 | private static final long serialVersionUID = 1L; 27 | 28 | @ApiModelProperty(value = "id") 29 | @TableId(value = "id", type = IdType.AUTO) 30 | private Integer id; 31 | 32 | @ApiModelProperty(value = "用户id") 33 | private Integer userId; 34 | 35 | @ApiModelProperty(value = "文章id") 36 | private Integer articleId; 37 | 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/entity/ArticleComment.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableId; 5 | import java.time.LocalDateTime; 6 | import java.io.Serializable; 7 | 8 | import com.baomidou.mybatisplus.annotation.TableLogic; 9 | import com.fasterxml.jackson.annotation.JsonFormat; 10 | import com.fasterxml.jackson.annotation.JsonIgnore; 11 | import com.fasterxml.jackson.annotation.JsonInclude; 12 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 13 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 14 | import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; 15 | import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; 16 | import io.swagger.annotations.ApiModel; 17 | import io.swagger.annotations.ApiModelProperty; 18 | import lombok.Data; 19 | import lombok.EqualsAndHashCode; 20 | import lombok.experimental.Accessors; 21 | 22 | /** 23 | *

24 | * 文章评论表 25 | *

26 | * 27 | * @author yaohw 28 | * @since 2019-12-03 29 | */ 30 | @Data 31 | @Accessors(chain = true) 32 | @EqualsAndHashCode(callSuper = false) 33 | @JsonInclude(JsonInclude.Include.NON_NULL) 34 | @ApiModel(value="ArticleComment对象", description="文章评论表") 35 | public class ArticleComment implements Serializable { 36 | 37 | private static final long serialVersionUID = 1L; 38 | 39 | @ApiModelProperty(value = "id") 40 | @TableId(value = "id", type = IdType.AUTO) 41 | private Integer id; 42 | 43 | @ApiModelProperty(value = "文章id") 44 | private Integer articleId; 45 | 46 | @ApiModelProperty(value = "评论者id") 47 | private Integer fromUserId; 48 | 49 | @ApiModelProperty(value = "评论内容") 50 | private String content; 51 | 52 | @ApiModelProperty(value = "评论时间") 53 | @JsonSerialize(using = LocalDateTimeSerializer.class) 54 | @JsonDeserialize(using = LocalDateTimeDeserializer.class) 55 | @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone = "GMT+8") 56 | private LocalDateTime commentTime; 57 | 58 | @TableLogic 59 | @JsonIgnore 60 | @ApiModelProperty(value = "是否已删除,1:是,0:否") 61 | private Integer deleted; 62 | 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/entity/ArticleLike.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableId; 5 | import java.io.Serializable; 6 | 7 | import com.fasterxml.jackson.annotation.JsonIgnore; 8 | import io.swagger.annotations.ApiModel; 9 | import io.swagger.annotations.ApiModelProperty; 10 | import lombok.Data; 11 | import lombok.EqualsAndHashCode; 12 | import lombok.experimental.Accessors; 13 | 14 | /** 15 | *

16 | * 文章点赞表 17 | *

18 | * 19 | * @author yaohw 20 | * @since 2019-12-02 21 | */ 22 | @Data 23 | @EqualsAndHashCode(callSuper = false) 24 | @Accessors(chain = true) 25 | @ApiModel(value="ArticleLike对象", description="文章点赞表") 26 | public class ArticleLike implements Serializable { 27 | 28 | private static final long serialVersionUID = 1L; 29 | 30 | 31 | @JsonIgnore 32 | @ApiModelProperty(value = "id") 33 | @TableId(value = "id", type = IdType.AUTO) 34 | private Integer id; 35 | 36 | @JsonIgnore 37 | @ApiModelProperty(value = "文章id") 38 | private Integer articleId; 39 | 40 | @ApiModelProperty(value = "用户id") 41 | private Integer userId; 42 | 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/entity/ArticleReply.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableId; 5 | import java.time.LocalDateTime; 6 | import java.io.Serializable; 7 | 8 | import com.baomidou.mybatisplus.annotation.TableLogic; 9 | import com.fasterxml.jackson.annotation.JsonFormat; 10 | import com.fasterxml.jackson.annotation.JsonIgnore; 11 | import com.fasterxml.jackson.annotation.JsonInclude; 12 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 13 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 14 | import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; 15 | import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; 16 | import io.swagger.annotations.ApiModel; 17 | import io.swagger.annotations.ApiModelProperty; 18 | import lombok.Data; 19 | import lombok.EqualsAndHashCode; 20 | import lombok.experimental.Accessors; 21 | 22 | /** 23 | *

24 | * 文章回复表 25 | *

26 | * 27 | * @author yaohw 28 | * @since 2019-12-03 29 | */ 30 | @Data 31 | @Accessors(chain = true) 32 | @EqualsAndHashCode(callSuper = false) 33 | @JsonInclude(JsonInclude.Include.NON_NULL) 34 | @ApiModel(value="ArticleReply对象", description="文章回复表") 35 | public class ArticleReply implements Serializable { 36 | 37 | private static final long serialVersionUID = 1L; 38 | 39 | @ApiModelProperty(value = "id") 40 | @TableId(value = "id", type = IdType.AUTO) 41 | private Integer id; 42 | 43 | @ApiModelProperty(value = "文章id") 44 | private Integer articleId; 45 | 46 | @ApiModelProperty(value = "评论id") 47 | private Integer commentId; 48 | 49 | @ApiModelProperty(value = "评论者id") 50 | private Integer fromUserId; 51 | 52 | @ApiModelProperty(value = "被评论者id") 53 | private Integer toUserId; 54 | 55 | @ApiModelProperty(value = "回复内容") 56 | private String content; 57 | 58 | @ApiModelProperty(value = "回复时间") 59 | @JsonSerialize(using = LocalDateTimeSerializer.class) 60 | @JsonDeserialize(using = LocalDateTimeDeserializer.class) 61 | @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone = "GMT+8") 62 | private LocalDateTime replyTime; 63 | 64 | @TableLogic 65 | @JsonIgnore 66 | @ApiModelProperty(value = "是否已删除,1:是,0:否") 67 | private Integer deleted; 68 | 69 | 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/entity/ArticleTag.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableId; 5 | import java.io.Serializable; 6 | 7 | import com.fasterxml.jackson.annotation.JsonInclude; 8 | import io.swagger.annotations.ApiModel; 9 | import io.swagger.annotations.ApiModelProperty; 10 | import lombok.*; 11 | import lombok.experimental.Accessors; 12 | 13 | /** 14 | *

15 | * 文章-标签 关联表 16 | *

17 | * 18 | * @author yaohw 19 | * @since 2019-11-15 20 | */ 21 | @Data 22 | @NoArgsConstructor 23 | @AllArgsConstructor 24 | @Accessors(chain = true) 25 | @RequiredArgsConstructor 26 | @EqualsAndHashCode 27 | @JsonInclude(JsonInclude.Include.NON_NULL) 28 | @ApiModel(value="ArticleTag对象", description="文章-标签 关联表") 29 | public class ArticleTag implements Serializable { 30 | 31 | 32 | private static final long serialVersionUID = 1L; 33 | 34 | @EqualsAndHashCode.Exclude 35 | @ApiModelProperty(value = "id") 36 | @TableId(value = "id", type = IdType.AUTO) 37 | private Integer id; 38 | 39 | @NonNull 40 | @ApiModelProperty(value = "文章id") 41 | private Integer articleId; 42 | 43 | @NonNull 44 | @ApiModelProperty(value = "标签id") 45 | private Integer tagId; 46 | 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/entity/Category.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableId; 5 | import java.io.Serializable; 6 | 7 | import com.baomidou.mybatisplus.annotation.TableLogic; 8 | import com.fasterxml.jackson.annotation.JsonIgnore; 9 | import com.fasterxml.jackson.annotation.JsonInclude; 10 | import io.swagger.annotations.ApiModel; 11 | import io.swagger.annotations.ApiModelProperty; 12 | import lombok.Data; 13 | import lombok.EqualsAndHashCode; 14 | import lombok.experimental.Accessors; 15 | 16 | /** 17 | *

18 | * 分类表 19 | *

20 | * 21 | * @author yaohw 22 | * @since 2019-11-14 23 | */ 24 | @Data 25 | @Accessors(chain = true) 26 | @EqualsAndHashCode(callSuper = false) 27 | @JsonInclude(JsonInclude.Include.NON_NULL) 28 | @ApiModel(value="Category对象", description="分类表") 29 | public class Category implements Serializable { 30 | 31 | private static final long serialVersionUID = 1L; 32 | 33 | @ApiModelProperty(value = "id") 34 | @TableId(value = "id", type = IdType.AUTO) 35 | private Integer id; 36 | 37 | @ApiModelProperty(value = "名称") 38 | private String name; 39 | 40 | @ApiModelProperty(value = "父类id") 41 | private Integer parentId; 42 | 43 | @JsonIgnore 44 | @TableLogic 45 | @ApiModelProperty(value = "是否已删除,1:是,0:否") 46 | private Integer deleted; 47 | 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/entity/Client.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableId; 5 | import java.io.Serializable; 6 | 7 | import com.fasterxml.jackson.annotation.JsonInclude; 8 | import com.fasterxml.jackson.annotation.JsonProperty; 9 | import io.swagger.annotations.ApiModel; 10 | import io.swagger.annotations.ApiModelProperty; 11 | import lombok.Data; 12 | import lombok.EqualsAndHashCode; 13 | import lombok.experimental.Accessors; 14 | 15 | import javax.validation.constraints.NotBlank; 16 | 17 | /** 18 | *

19 | * 客户端表 20 | *

21 | * 22 | * @author yaohw 23 | * @since 2019-12-06 24 | */ 25 | @Data 26 | @Accessors(chain = true) 27 | @EqualsAndHashCode(callSuper = false) 28 | @JsonInclude(JsonInclude.Include.NON_NULL) 29 | @ApiModel(value="Client对象", description="客户端表") 30 | public class Client implements Serializable { 31 | 32 | private static final long serialVersionUID = 1L; 33 | 34 | @ApiModelProperty(value = "id") 35 | @TableId(value = "id", type = IdType.AUTO) 36 | private Integer id; 37 | 38 | @NotBlank(message = "客户端ID不能为空") 39 | @ApiModelProperty(value = "客户端id,客户端唯一标识") 40 | private String clientId; 41 | 42 | @NotBlank(message = "客户端秘钥不能为空") 43 | @ApiModelProperty(value = "客户端秘钥") 44 | private String clientSecret; 45 | 46 | @ApiModelProperty(value = "access_token有效时长") 47 | private Long accessTokenExpire; 48 | 49 | @ApiModelProperty(value = "refresh_token有效时长") 50 | private Long refreshTokenExpire; 51 | 52 | @ApiModelProperty(value = "是否启用refresh_token,1:是,0:否") 53 | private Integer enableRefreshToken; 54 | 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/entity/FriendLink.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableId; 5 | import java.io.Serializable; 6 | 7 | import com.fasterxml.jackson.annotation.JsonInclude; 8 | import io.swagger.annotations.ApiModel; 9 | import io.swagger.annotations.ApiModelProperty; 10 | import lombok.Data; 11 | import lombok.EqualsAndHashCode; 12 | import lombok.experimental.Accessors; 13 | 14 | import javax.validation.constraints.NotBlank; 15 | 16 | /** 17 | *

18 | * 友链表 19 | *

20 | * 21 | * @author yaohw 22 | * @since 2019-12-02 23 | */ 24 | @Data 25 | @Accessors(chain = true) 26 | @EqualsAndHashCode(callSuper = false) 27 | @JsonInclude(JsonInclude.Include.NON_NULL) 28 | @ApiModel(value="FriendChain对象", description="友链表") 29 | public class FriendLink implements Serializable { 30 | 31 | private static final long serialVersionUID = 1L; 32 | 33 | @ApiModelProperty(value = "id") 34 | @TableId(value = "id", type = IdType.AUTO) 35 | private Integer id; 36 | 37 | @ApiModelProperty(value = "名称") 38 | @NotBlank(message = "名称不能为空") 39 | private String name; 40 | 41 | @ApiModelProperty(value = "链接") 42 | @NotBlank(message = "链接不能为空") 43 | private String url; 44 | 45 | @ApiModelProperty(value = "图标") 46 | private String icon; 47 | 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/entity/LeaveMessage.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableId; 5 | import java.time.LocalDateTime; 6 | import java.io.Serializable; 7 | 8 | import com.baomidou.mybatisplus.annotation.TableLogic; 9 | import com.fasterxml.jackson.annotation.JsonInclude; 10 | import io.swagger.annotations.ApiModel; 11 | import io.swagger.annotations.ApiModelProperty; 12 | import lombok.Data; 13 | import lombok.EqualsAndHashCode; 14 | import lombok.experimental.Accessors; 15 | 16 | /** 17 | *

18 | * 留言表 19 | *

20 | * 21 | * @author yaohw 22 | * @since 2019-12-05 23 | */ 24 | @Data 25 | @Accessors(chain = true) 26 | @EqualsAndHashCode(callSuper = false) 27 | @JsonInclude(JsonInclude.Include.NON_NULL) 28 | @ApiModel(value="LeaveMessage对象", description="留言表") 29 | public class LeaveMessage implements Serializable { 30 | 31 | private static final long serialVersionUID = 1L; 32 | 33 | @ApiModelProperty(value = "id") 34 | @TableId(value = "id", type = IdType.AUTO) 35 | private Integer id; 36 | 37 | @ApiModelProperty(value = "父id") 38 | private Integer pid; 39 | 40 | @ApiModelProperty(value = "留言者id") 41 | private Integer fromUserId; 42 | 43 | @ApiModelProperty(value = "被回复者id") 44 | private Integer toUserId; 45 | 46 | @ApiModelProperty(value = "内容") 47 | private String content; 48 | 49 | @ApiModelProperty(value = "时间") 50 | private LocalDateTime createTime; 51 | 52 | @TableLogic 53 | @ApiModelProperty(value = "是否删除,1:是,0:否") 54 | private Integer deleted; 55 | 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/entity/OauthUser.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableId; 5 | import java.time.LocalDateTime; 6 | import java.io.Serializable; 7 | import io.swagger.annotations.ApiModel; 8 | import io.swagger.annotations.ApiModelProperty; 9 | import lombok.Data; 10 | import lombok.EqualsAndHashCode; 11 | import lombok.experimental.Accessors; 12 | 13 | /** 14 | *

15 | * 第三方登录关联表 16 | *

17 | * 18 | * @author yaohw 19 | * @since 2020-05-20 20 | */ 21 | @Data 22 | @EqualsAndHashCode(callSuper = false) 23 | @Accessors(chain = true) 24 | @ApiModel(value="OauthUser对象", description="第三方登录关联表") 25 | public class OauthUser implements Serializable { 26 | 27 | private static final long serialVersionUID = 1L; 28 | 29 | @TableId(value = "id", type = IdType.AUTO) 30 | private Integer id; 31 | 32 | @ApiModelProperty(value = "第三方平台的用户唯一id") 33 | private String uuid; 34 | 35 | @ApiModelProperty(value = "用户id") 36 | private Integer userId; 37 | 38 | @ApiModelProperty(value = "认证类型,1:qq,2:github,3:微信,4:gitee") 39 | private Integer type; 40 | 41 | @ApiModelProperty(value = "创建时间") 42 | private LocalDateTime createTime; 43 | 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/entity/Tag.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableId; 5 | import com.baomidou.mybatisplus.annotation.TableLogic; 6 | import com.fasterxml.jackson.annotation.JsonIgnore; 7 | import com.fasterxml.jackson.annotation.JsonInclude; 8 | import io.swagger.annotations.ApiModel; 9 | import io.swagger.annotations.ApiModelProperty; 10 | import lombok.Data; 11 | import lombok.EqualsAndHashCode; 12 | import lombok.experimental.Accessors; 13 | 14 | import javax.validation.constraints.NotBlank; 15 | import javax.validation.constraints.Null; 16 | import java.io.Serializable; 17 | import java.time.LocalDateTime; 18 | 19 | /** 20 | *

21 | * 标签表 22 | *

23 | * 24 | * @author yaohw 25 | * @since 2019-11-14 26 | */ 27 | @Data 28 | @Accessors(chain = true) 29 | @EqualsAndHashCode(callSuper = false) 30 | @JsonInclude(JsonInclude.Include.NON_NULL) 31 | @ApiModel(value="Tag对象", description="标签表") 32 | public class Tag implements Serializable { 33 | 34 | private static final long serialVersionUID = 1L; 35 | 36 | @Null(message = "不需要传id") 37 | @ApiModelProperty(value = "id") 38 | @TableId(value = "id", type = IdType.AUTO) 39 | private Integer id; 40 | 41 | @NotBlank(message = "标签名不能为空") 42 | @ApiModelProperty(value = "标签名") 43 | private String name; 44 | 45 | @TableLogic 46 | @ApiModelProperty(value = "是否已删除,1:是,0:否") 47 | private Integer deleted; 48 | 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/entity/User.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableId; 5 | import com.fasterxml.jackson.annotation.JsonFormat; 6 | import com.fasterxml.jackson.annotation.JsonIgnore; 7 | import com.fasterxml.jackson.annotation.JsonInclude; 8 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 9 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 10 | import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; 11 | import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; 12 | import io.swagger.annotations.ApiModel; 13 | import io.swagger.annotations.ApiModelProperty; 14 | import lombok.Data; 15 | import lombok.EqualsAndHashCode; 16 | import lombok.experimental.Accessors; 17 | 18 | import java.io.Serializable; 19 | import java.time.LocalDate; 20 | import java.time.LocalDateTime; 21 | 22 | /** 23 | *

24 | * 用户表 25 | *

26 | * 27 | * @author yaohw 28 | * @since 2019-10-23 29 | */ 30 | @Data 31 | @Accessors(chain = true) 32 | @EqualsAndHashCode(callSuper = false) 33 | @JsonInclude(JsonInclude.Include.NON_NULL) 34 | @ApiModel(value="User对象", description="用户表") 35 | public class User implements Serializable { 36 | 37 | private static final long serialVersionUID = 1L; 38 | 39 | @ApiModelProperty(value = "用户id") 40 | @TableId(value = "id", type = IdType.AUTO) 41 | private Integer id; 42 | 43 | @ApiModelProperty(value = "用户名") 44 | private String username; 45 | 46 | @JsonIgnore 47 | @ApiModelProperty(value = "密码") 48 | private String password; 49 | 50 | @ApiModelProperty(value = "手机号") 51 | private Long mobile; 52 | 53 | @ApiModelProperty(value = "昵称") 54 | private String nickname; 55 | 56 | @ApiModelProperty(value = "性别,1:男,0:女,默认为1") 57 | private Integer gender; 58 | 59 | @ApiModelProperty(value = "生日") 60 | @JsonFormat(pattern="yyyy-MM-dd",timezone = "GMT+8") 61 | private LocalDate birthday; 62 | 63 | @ApiModelProperty(value = "电子邮箱") 64 | private String email; 65 | 66 | @ApiModelProperty(value = "简介|个性签名") 67 | private String brief; 68 | 69 | @ApiModelProperty(value = "用户头像") 70 | private String avatar; 71 | 72 | @ApiModelProperty(value = "状态,0:正常,1:锁定,2:禁用,3:过期") 73 | private Integer status; 74 | 75 | @ApiModelProperty(value = "是否管理员,1:是,0:否") 76 | private Integer admin; 77 | 78 | @ApiModelProperty(value = "创建时间") 79 | @JsonDeserialize(using = LocalDateTimeDeserializer.class) 80 | @JsonSerialize(using = LocalDateTimeSerializer.class) 81 | @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone = "GMT+8") 82 | private LocalDateTime createTime; 83 | 84 | 85 | } 86 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/mapper/ArticleCollectMapper.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.mapper; 2 | 3 | import cn.poile.blog.entity.ArticleCollect; 4 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 5 | 6 | /** 7 | *

8 | * 文章收藏表 Mapper 接口 9 | *

10 | * 11 | * @author yaohw 12 | * @since 2019-12-04 13 | */ 14 | public interface ArticleCollectMapper extends BaseMapper { 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/mapper/ArticleCommentMapper.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.mapper; 2 | 3 | import cn.poile.blog.entity.ArticleComment; 4 | import cn.poile.blog.vo.ArticleCommentVo; 5 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 6 | import org.apache.ibatis.annotations.Param; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | *

12 | * 文章评论表 Mapper 接口 13 | *

14 | * 15 | * @author yaohw 16 | * @since 2019-12-03 17 | */ 18 | public interface ArticleCommentMapper extends BaseMapper { 19 | 20 | /** 21 | * 查询文章评论及回复列表,包括评论者和回复者信息 22 | * @param articleId 23 | * @param offset 24 | * @param limit 25 | * @return 26 | */ 27 | List selectCommentAndReplyList(@Param("offset") long offset,@Param("limit") long limit,@Param("articleId") Integer articleId); 28 | 29 | /** 30 | * 查询最新评论,包括评论者和文章信息 31 | * @param limit 32 | * @return 33 | */ 34 | List selectLatestComment(@Param("limit") long limit); 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/mapper/ArticleLikeMapper.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.mapper; 2 | 3 | import cn.poile.blog.entity.ArticleLike; 4 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 5 | 6 | /** 7 | *

8 | * 文章点赞表 Mapper 接口 9 | *

10 | * 11 | * @author yaohw 12 | * @since 2019-12-02 13 | */ 14 | public interface ArticleLikeMapper extends BaseMapper { 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/mapper/ArticleMapper.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.mapper; 2 | 3 | import cn.poile.blog.entity.Article; 4 | import cn.poile.blog.vo.ArticleArchivesVo; 5 | import cn.poile.blog.vo.ArticleCategoryStatisticsVo; 6 | import cn.poile.blog.vo.ArticleTagStatisticsVo; 7 | import cn.poile.blog.vo.ArticleVo; 8 | import cn.poile.blog.wrapper.ArticlePageQueryWrapper; 9 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 10 | import org.apache.ibatis.annotations.Param; 11 | 12 | import java.util.List; 13 | 14 | /** 15 | *

16 | * 文章表 Mapper 接口 17 | *

18 | * 19 | * @author yaohw 20 | * @since 2019-11-15 21 | */ 22 | public interface ArticleMapper extends BaseMapper
{ 23 | 24 | /** 25 | * 分页查询 26 | * @param queryWrapper 27 | * @return 28 | */ 29 | List selectArticleVoPage(ArticlePageQueryWrapper queryWrapper); 30 | 31 | 32 | /** 33 | * 分页查询计数 34 | * @param status 状态 35 | * @param categoryId 分类id 36 | * @param tagId 标签id 37 | * @param start 开始日期 38 | * @param end 结束日期 39 | * @param title 标题关键字 40 | * @return 41 | */ 42 | Integer selectPageCount(@Param("status") Integer status, 43 | @Param("categoryId") Integer categoryId, 44 | @Param("tagId") Integer tagId, 45 | @Param("start") String start, 46 | @Param("end")String end, 47 | @Param("title") String title); 48 | 49 | /** 50 | * 根据文章id查询 51 | * @param id 文章id 52 | * @param status 53 | * @return 54 | */ 55 | ArticleVo selectArticleVoById(@Param("id") int id,@Param("status") Integer status); 56 | 57 | /** 58 | * 查询上一篇和下一篇 59 | * @param id 60 | * @return 61 | */ 62 | List
selectPreAndNext(@Param("id") int id); 63 | 64 | 65 | /** 66 | * 文章归档计数 67 | * @return 68 | */ 69 | Integer selectArticleArchivesCount(); 70 | 71 | /** 72 | * 文章归档 73 | * @param offset 74 | * @param limit 75 | * @return 76 | */ 77 | List selectArticleArchives(@Param("offset") long offset,@Param("limit") long limit); 78 | 79 | /** 80 | * 按分类计数文章数 81 | * @return 82 | */ 83 | List selectCategoryStatistic(); 84 | 85 | /** 86 | * 按标签计数文章数 87 | * @return 88 | */ 89 | List selectTagStatistic(); 90 | 91 | /** 92 | * 标签列表查询文章列表 93 | * @param tagList 94 | * @param limit 95 | * @return 96 | */ 97 | List selectByTagList(@Param("tagList") List tagList,@Param("limit") long limit ); 98 | 99 | /** 100 | * 点赞数自增 101 | * @param id 102 | */ 103 | void likeCountIncrement(@Param("id") int id); 104 | 105 | /** 106 | * 点赞数自减 107 | * @param id 108 | */ 109 | void likeCountDecrement(@Param("id") int id); 110 | 111 | /** 112 | * 评论数自增 113 | * @param id 114 | */ 115 | void commentCountIncrement(@Param("id") int id); 116 | 117 | /** 118 | * 评论数自减 119 | * @param id 120 | */ 121 | void commentCountDecrement(@Param("id") int id); 122 | 123 | /** 124 | * 收藏数自增 125 | * @param id 126 | */ 127 | void collectCountIncrement(@Param("id") int id); 128 | 129 | /** 130 | * 收藏数自减 131 | * @param id 132 | */ 133 | void collectCountDecrement(@Param("id") int id); 134 | 135 | /** 136 | * 分页查询用户收藏文章 137 | * @param offset 138 | * @param limit 139 | * @param userId 140 | * @return 141 | */ 142 | List selectCollectByUserId(@Param("offset") long offset,@Param("limit") long limit,@Param("userId") Integer userId); 143 | } 144 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/mapper/ArticleReplyMapper.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.mapper; 2 | 3 | import cn.poile.blog.entity.ArticleReply; 4 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 5 | 6 | /** 7 | *

8 | * 文章回复表 Mapper 接口 9 | *

10 | * 11 | * @author yaohw 12 | * @since 2019-12-03 13 | */ 14 | public interface ArticleReplyMapper extends BaseMapper { 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/mapper/ArticleTagMapper.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.mapper; 2 | 3 | import cn.poile.blog.entity.ArticleTag; 4 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 5 | 6 | /** 7 | *

8 | * 文章-标签 关联表 Mapper 接口 9 | *

10 | * 11 | * @author yaohw 12 | * @since 2019-11-15 13 | */ 14 | public interface ArticleTagMapper extends BaseMapper { 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/mapper/CategoryMapper.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.mapper; 2 | 3 | import cn.poile.blog.entity.Category; 4 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 5 | 6 | /** 7 | *

8 | * 目录分类表 Mapper 接口 9 | *

10 | * 11 | * @author yaohw 12 | * @since 2019-11-14 13 | */ 14 | public interface CategoryMapper extends BaseMapper { 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/mapper/ClientMapper.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.mapper; 2 | 3 | import cn.poile.blog.entity.Client; 4 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 5 | 6 | /** 7 | *

8 | * 客户端表 Mapper 接口 9 | *

10 | * 11 | * @author yaohw 12 | * @since 2019-12-06 13 | */ 14 | public interface ClientMapper extends BaseMapper { 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/mapper/FriendLinkMapper.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.mapper; 2 | 3 | import cn.poile.blog.entity.FriendLink; 4 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 5 | 6 | /** 7 | *

8 | * 友链表 Mapper 接口 9 | *

10 | * 11 | * @author yaohw 12 | * @since 2019-12-02 13 | */ 14 | public interface FriendLinkMapper extends BaseMapper { 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/mapper/LeaveMessageMapper.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.mapper; 2 | 3 | import cn.poile.blog.entity.LeaveMessage; 4 | import cn.poile.blog.vo.LeaveMessageVo; 5 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 6 | import org.apache.ibatis.annotations.Param; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | *

12 | * 留言表 Mapper 接口 13 | *

14 | * 15 | * @author yaohw 16 | * @since 2019-12-05 17 | */ 18 | public interface LeaveMessageMapper extends BaseMapper { 19 | 20 | /** 21 | * 查询留言及留言回复列表,包括留言者和回复者信息 22 | * @param offset 23 | * @param limit 24 | * @return 25 | */ 26 | List selectLeaveMessageAndReplyList(@Param("offset") long offset, @Param("limit") long limit); 27 | 28 | /** 29 | * 最新留言 30 | * @param limit 31 | * @return 32 | */ 33 | List selectLatest(@Param("limit") long limit); 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/mapper/OauthUserMapper.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.mapper; 2 | 3 | import cn.poile.blog.entity.OauthUser; 4 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 5 | 6 | /** 7 | *

8 | * 第三方登录关联表 Mapper 接口 9 | *

10 | * 11 | * @author yaohw 12 | * @since 2020-05-20 13 | */ 14 | public interface OauthUserMapper extends BaseMapper { 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/mapper/TagMapper.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.mapper; 2 | 3 | import cn.poile.blog.entity.Tag; 4 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 5 | 6 | /** 7 | *

8 | * 标签表 Mapper 接口 9 | *

10 | * 11 | * @author yaohw 12 | * @since 2019-11-14 13 | */ 14 | public interface TagMapper extends BaseMapper { 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/mapper/UserMapper.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.mapper; 2 | 3 | import cn.poile.blog.entity.User; 4 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 5 | 6 | /** 7 | *

8 | * 用户表 Mapper 接口 9 | *

10 | * 11 | * @author yaohw 12 | * @since 2019-10-23 13 | */ 14 | public interface UserMapper extends BaseMapper { 15 | 16 | 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/mybatis/MybatisPlusGenerator.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.mybatis; 2 | 3 | import com.baomidou.mybatisplus.core.toolkit.StringPool; 4 | import com.baomidou.mybatisplus.generator.AutoGenerator; 5 | import com.baomidou.mybatisplus.generator.InjectionConfig; 6 | import com.baomidou.mybatisplus.generator.config.*; 7 | import com.baomidou.mybatisplus.generator.config.converts.MySqlTypeConvert; 8 | import com.baomidou.mybatisplus.generator.config.po.TableInfo; 9 | import com.baomidou.mybatisplus.generator.config.rules.DbColumnType; 10 | import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy; 11 | import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine; 12 | 13 | import java.util.ArrayList; 14 | import java.util.List; 15 | 16 | /** 17 | * mybatis-plus代码生成器 18 | * @author: yaohw 19 | * @create: 2019-10-23 11:42 20 | **/ 21 | public class MybatisPlusGenerator { 22 | 23 | public static void main(String[] args) { 24 | AutoGenerator autoGenerator = new AutoGenerator(); 25 | // 全局配置 26 | GlobalConfig gc = new GlobalConfig(); 27 | gc.setOutputDir("F:/github-repos/ucs-blog/src/main/java"); 28 | gc.setAuthor("yaohw"); 29 | gc.setOpen(false); 30 | gc.setSwagger2(true); 31 | gc.isSwagger2(); 32 | autoGenerator.setGlobalConfig(gc); 33 | // 数据源配置 34 | DataSourceConfig dataSource = new DataSourceConfig(); 35 | dataSource.setUrl("jdbc:mysql://localhost:3306/blog_db?useSSL=false"); 36 | dataSource.setDriverName("com.mysql.jdbc.Driver"); 37 | dataSource.setUsername("root"); 38 | dataSource.setPassword("root"); 39 | dataSource.setTypeConvert(new MySqlTypeConvert() { 40 | @Override 41 | public DbColumnType processTypeConvert(GlobalConfig globalConfig, String fieldType) { 42 | String tinyint = "tinyint"; 43 | if (fieldType.toLowerCase().contains(tinyint)) { 44 | return DbColumnType.INTEGER; 45 | } 46 | return (DbColumnType) super.processTypeConvert(globalConfig, fieldType); 47 | } 48 | }); 49 | autoGenerator.setDataSource(dataSource); 50 | // 包名配置 51 | PackageConfig pc = new PackageConfig(); 52 | pc.setParent("cn.poile.blog"); 53 | autoGenerator.setPackageInfo(pc); 54 | InjectionConfig cfg = new InjectionConfig() { 55 | @Override 56 | public void initMap() { 57 | // to do nothing 58 | } 59 | }; 60 | String templatePath = "/templates/mapper.xml.ftl"; 61 | List focList = new ArrayList<>(); 62 | // 自定义配置会被优先输出 63 | focList.add(new FileOutConfig(templatePath) { 64 | @Override 65 | public String outputFile(TableInfo tableInfo) { 66 | return "F:/github-repos/ucs-blog" + "/src/main/resources/mapper/" 67 | + "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML; 68 | } 69 | }); 70 | cfg.setFileOutConfigList(focList); 71 | autoGenerator.setCfg(cfg); 72 | // 配置模板 73 | TemplateConfig templateConfig = new TemplateConfig(); 74 | templateConfig.setXml(null); 75 | autoGenerator.setTemplate(templateConfig); 76 | // 策略配置 77 | StrategyConfig strategy = new StrategyConfig(); 78 | strategy.setNaming(NamingStrategy.underline_to_camel); 79 | strategy.setColumnNaming(NamingStrategy.underline_to_camel); 80 | strategy.setEntityLombokModel(true); 81 | strategy.setRestControllerStyle(true); 82 | strategy.setInclude("article_collect"); 83 | // 设置 84 | // strategy.setInclude("") 85 | // 公共父类 86 | strategy.setSuperControllerClass("cn.poile.blog.controller.BaseController"); 87 | autoGenerator.setStrategy(strategy); 88 | autoGenerator.setTemplateEngine(new FreemarkerTemplateEngine()); 89 | autoGenerator.execute(); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/service/ArticleRecommendService.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.service; 2 | 3 | import cn.poile.blog.vo.ArticleVo; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * 文章推荐服务接口 9 | * @author: yaohw 10 | * @create: 2019-11-29 11:57 11 | **/ 12 | public interface ArticleRecommendService { 13 | 14 | /** 15 | * 新增推荐 16 | * @param articleId 17 | * @param score 分数 18 | */ 19 | void add(Integer articleId,Double score); 20 | 21 | /** 22 | * 获取推荐列表 23 | * @return 24 | */ 25 | List list(); 26 | 27 | /** 28 | * 从推荐中移除 29 | * @param articleId 30 | */ 31 | void remove(Integer articleId); 32 | 33 | /** 34 | * 异步刷新 35 | * @param articleId 36 | */ 37 | void asyncRefresh(Integer articleId); 38 | 39 | /** 40 | * 刷新 41 | * @param articleId 42 | */ 43 | void refresh(Integer articleId); 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/service/AuthenticationService.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.service; 2 | 3 | import cn.poile.blog.common.security.AuthenticationToken; 4 | import cn.poile.blog.entity.Client; 5 | 6 | /** 7 | * @author: yaohw 8 | * @create: 2019-10-28 17:51 9 | **/ 10 | public interface AuthenticationService { 11 | /** 12 | * 用户名或手机号密码认证 13 | * @param s 手机号或用户名 14 | * @param password 密码 15 | * @param client 客户端 16 | * @return cn.poile.blog.vo.TokenVo 17 | */ 18 | AuthenticationToken usernameOrMobilePasswordAuthenticate(String s, String password,Client client); 19 | 20 | /** 21 | * 手机号验证码认证 22 | * @param mobile 手机号 23 | * @param code 验证码 24 | * @param client 客户端 25 | * @return 26 | */ 27 | AuthenticationToken mobileCodeAuthenticate(long mobile,String code,Client client); 28 | 29 | /** 30 | * 移除 accessToken 相关 31 | * @param accessToken accessToken 32 | * @param client 客户端 33 | */ 34 | void remove(String accessToken,Client client); 35 | 36 | /** 37 | * 刷新 accessToken 38 | * @param refreshToken refreshToken 39 | * @param client 客户端 40 | * @return 41 | */ 42 | AuthenticationToken refreshAccessToken(String refreshToken, Client client); 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/service/IArticleCollectService.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.service; 2 | 3 | import cn.poile.blog.entity.ArticleCollect; 4 | import cn.poile.blog.vo.ArticleVo; 5 | import com.baomidou.mybatisplus.core.metadata.IPage; 6 | import com.baomidou.mybatisplus.extension.service.IService; 7 | 8 | /** 9 | *

10 | * 文章收藏表 服务类 11 | *

12 | * 13 | * @author yaohw 14 | * @since 2019-12-04 15 | */ 16 | public interface IArticleCollectService extends IService { 17 | 18 | /** 19 | * 新增收藏 20 | * @param articleId 21 | */ 22 | void add(Integer articleId); 23 | 24 | /** 25 | * 删除收藏 26 | * @param articleId 27 | */ 28 | void delete(Integer articleId); 29 | 30 | /** 31 | * 分页查询用户收藏文章 32 | * @param current 33 | * @param size 34 | * @return 35 | */ 36 | IPage page(long current, long size); 37 | 38 | /** 39 | * 文章是否收藏 40 | * @param articleId 41 | * @return 42 | */ 43 | Integer collected(Integer articleId); 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/service/IArticleCommentService.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.service; 2 | 3 | import cn.poile.blog.entity.ArticleComment; 4 | import cn.poile.blog.vo.ArticleCommentVo; 5 | import com.baomidou.mybatisplus.core.metadata.IPage; 6 | import com.baomidou.mybatisplus.extension.service.IService; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | *

12 | * 文章评论表 服务类 13 | *

14 | * 15 | * @author yaohw 16 | * @since 2019-12-03 17 | */ 18 | public interface IArticleCommentService extends IService { 19 | 20 | /** 21 | * 新增文章评论 22 | * @param articleId 23 | * @param content 24 | */ 25 | void add(Integer articleId,String content); 26 | 27 | /** 28 | * 删除评论 29 | * @param commentId 30 | */ 31 | void delete(Integer commentId); 32 | 33 | /** 34 | * 分页查询文章评论及回复列表,包括评论者和回复者信息 35 | * @param current 36 | * @param size 37 | * @param articleId 38 | * @return 39 | */ 40 | IPage selectCommentAndReplyList(long current, long size, Integer articleId); 41 | 42 | /** 43 | * 查询最新评论,包括评论者和文章信息 44 | * @param limit 45 | * @return 46 | */ 47 | List selectLatestComment(long limit); 48 | 49 | /** 50 | * 异步刷新推荐列表中的评论数、发送评论提醒邮箱 51 | * @param articleId 52 | * @param content 53 | * @return 54 | */ 55 | void asyncRefreshRecommendAndSendCommentMail(Integer articleId,String content); 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/service/IArticleLikeService.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.service; 2 | 3 | import cn.poile.blog.entity.ArticleLike; 4 | import com.baomidou.mybatisplus.extension.service.IService; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | *

10 | * 文章点赞表 服务类 11 | *

12 | * 13 | * @author yaohw 14 | * @since 2019-12-02 15 | */ 16 | public interface IArticleLikeService extends IService { 17 | 18 | /** 19 | * 查询文章是否已点赞 20 | * 21 | * @param articleId 22 | * @return 1:是,0:否 23 | */ 24 | Integer liked(Integer articleId); 25 | 26 | /** 27 | * 文章点赞 28 | * @param articleId 29 | */ 30 | void like(Integer articleId); 31 | 32 | /** 33 | * 取消文章点赞 34 | * @param articleId 35 | */ 36 | void cancel(Integer articleId); 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/service/IArticleReplyService.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.service; 2 | 3 | import cn.poile.blog.entity.ArticleReply; 4 | import com.baomidou.mybatisplus.extension.service.IService; 5 | 6 | /** 7 | *

8 | * 文章回复表 服务类 9 | *

10 | * 11 | * @author yaohw 12 | * @since 2019-12-03 13 | */ 14 | public interface IArticleReplyService extends IService { 15 | 16 | /** 17 | * 新增文章评论回复 18 | * @param articleId 19 | * @param commentId 20 | * @param toUserId 21 | * @param content 22 | */ 23 | void add(Integer articleId,Integer commentId,Integer toUserId,String content); 24 | 25 | /** 26 | * 删除回复 27 | * @param replyId 28 | */ 29 | void delete(Integer replyId); 30 | 31 | /** 32 | * 异步发送回复提醒邮箱 33 | * @param articleId 34 | * @param toUserId 35 | * @param content 36 | * @return 37 | */ 38 | void asyncSendMail(Integer articleId,Integer toUserId,String content); 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/service/IArticleTagService.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.service; 2 | 3 | import cn.poile.blog.entity.ArticleTag; 4 | import com.baomidou.mybatisplus.extension.service.IService; 5 | 6 | /** 7 | *

8 | * 文章-标签 关联表 服务类 9 | *

10 | * 11 | * @author yaohw 12 | * @since 2019-11-15 13 | */ 14 | public interface IArticleTagService extends IService { 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/service/ICategoryService.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.service; 2 | 3 | import cn.poile.blog.controller.model.dto.CategoryNodeDTO; 4 | import cn.poile.blog.controller.model.request.AddCategoryRequest; 5 | import cn.poile.blog.entity.Category; 6 | import com.baomidou.mybatisplus.extension.service.IService; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | *

12 | * 目录分类表 服务类 13 | *

14 | * 15 | * @author yaohw 16 | * @since 2019-11-14 17 | */ 18 | public interface ICategoryService extends IService { 19 | 20 | /** 21 | * 新增分类 22 | * @param request 23 | */ 24 | void add(AddCategoryRequest request); 25 | 26 | /** 27 | * 分类目录树 28 | * @return 29 | */ 30 | List getCategoryNodeTree(); 31 | 32 | /** 33 | * 修改 34 | * @param id 35 | * @param name 36 | */ 37 | void updateCategoryById(int id, String name); 38 | 39 | /** 40 | * 删除分类 41 | * @param id 42 | */ 43 | void delete(int id); 44 | 45 | /** 46 | * 获取子元素对应父元素列表,顺序为 node3 node2 root 47 | * @param categoryId 48 | * @return 49 | */ 50 | List parentList(Integer categoryId); 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/service/IClientService.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.service; 2 | 3 | import cn.poile.blog.entity.Client; 4 | import com.baomidou.mybatisplus.extension.service.IService; 5 | 6 | /** 7 | *

8 | * 客户端表 服务类 9 | *

10 | * 11 | * @author yaohw 12 | * @since 2019-12-06 13 | */ 14 | public interface IClientService extends IService { 15 | 16 | /** 17 | * 根据客户端id获取客户端 18 | * @param clientId 19 | * @return 20 | */ 21 | Client getClientByClientId(String clientId); 22 | 23 | /** 24 | * 清空缓存 25 | */ 26 | void clearCache(); 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/service/IFriendLinkService.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.service; 2 | 3 | import cn.poile.blog.entity.FriendLink; 4 | import com.baomidou.mybatisplus.extension.service.IService; 5 | 6 | /** 7 | *

8 | * 友链表 服务类 9 | *

10 | * 11 | * @author yaohw 12 | * @since 2019-12-02 13 | */ 14 | public interface IFriendLinkService extends IService { 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/service/ILeaveMessageService.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.service; 2 | 3 | import cn.poile.blog.entity.LeaveMessage; 4 | import cn.poile.blog.vo.LeaveMessageVo; 5 | import com.baomidou.mybatisplus.core.metadata.IPage; 6 | import com.baomidou.mybatisplus.extension.service.IService; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | *

12 | * 留言表 服务类 13 | *

14 | * 15 | * @author yaohw 16 | * @since 2019-12-05 17 | */ 18 | public interface ILeaveMessageService extends IService { 19 | 20 | /** 21 | * 新增留言 22 | * 23 | * @param content 24 | */ 25 | void add(String content); 26 | 27 | /** 28 | * 留言回复 29 | * 30 | * @param pid 31 | * @param toUserId 32 | * @param content 33 | */ 34 | void reply(Integer pid, Integer toUserId, String content); 35 | 36 | /** 37 | * 分页获取留言及回复列表 38 | * @param current 39 | * @param size 40 | * @return 41 | */ 42 | IPage page(long current,long size); 43 | 44 | /** 45 | * 删除(本人和管理可删除) 46 | * @param id 47 | */ 48 | void delete(Integer id); 49 | 50 | /** 51 | * 最新留言 52 | * @param limit 53 | * @return 54 | */ 55 | List selectLatest(long limit); 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/service/IOauthUserService.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.service; 2 | 3 | import cn.poile.blog.entity.OauthUser; 4 | import com.baomidou.mybatisplus.extension.service.IService; 5 | 6 | /** 7 | *

8 | * 第三方登录关联表 服务类 9 | *

10 | * 11 | * @author yaohw 12 | * @since 2020-05-20 13 | */ 14 | public interface IOauthUserService extends IService { 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/service/ITagService.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.service; 2 | 3 | import cn.poile.blog.entity.Tag; 4 | import com.baomidou.mybatisplus.core.metadata.IPage; 5 | import com.baomidou.mybatisplus.extension.service.IService; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | *

11 | * 标签表 服务类 12 | *

13 | * 14 | * @author yaohw 15 | * @since 2019-11-14 16 | */ 17 | public interface ITagService extends IService { 18 | /** 19 | * 新增标签 20 | * @param tagName 21 | * @return void 22 | */ 23 | void addTag(String tagName); 24 | 25 | /** 26 | * 分页查询标签 27 | * @param current 当前页 28 | * @param size 每页数量 29 | * @param tagName 标签名模糊查询 30 | * @return com.baomidou.mybatisplus.core.metadata.IPage 31 | */ 32 | IPage selectTagPage(long current,long size,String tagName); 33 | 34 | /** 35 | * 标签列表 36 | * @param tagName 37 | * @return java.util.List 38 | */ 39 | List selectTagList(String tagName); 40 | 41 | 42 | /** 43 | * 修改标签 44 | * @param id 45 | * @param tagName 46 | */ 47 | void update(int id,String tagName); 48 | 49 | /** 50 | * 删除标签 51 | * @param id 52 | */ 53 | void delete(int id); 54 | 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/service/IUserService.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.service; 2 | 3 | import cn.poile.blog.controller.model.request.UpdateUserRequest; 4 | import cn.poile.blog.controller.model.request.UserRegisterRequest; 5 | import cn.poile.blog.entity.User; 6 | import cn.poile.blog.vo.UserVo; 7 | import com.baomidou.mybatisplus.core.metadata.IPage; 8 | import com.baomidou.mybatisplus.extension.service.IService; 9 | import org.springframework.web.multipart.MultipartFile; 10 | 11 | /** 12 | *

13 | * 用户表 服务类 14 | *

15 | * 16 | * @author yaohw 17 | * @since 2019-10-23 18 | */ 19 | public interface IUserService extends IService { 20 | 21 | /** 22 | * 根据用户名或手机号查询用户信息 23 | * 24 | * @param username 25 | * @param mobile 26 | * @return cn.poile.blog.entity.User 27 | */ 28 | UserVo selectUserVoByUsernameOtherwiseMobile(String username, Long mobile); 29 | 30 | /** 31 | * 根据用户id获取用户 32 | * 33 | * @param id 34 | * @return 35 | */ 36 | UserVo selectUserVoById(Integer id); 37 | 38 | /** 39 | * 用户注册 40 | * 41 | * @param request 42 | */ 43 | void register(UserRegisterRequest request); 44 | 45 | /** 46 | * 更新用户信息 47 | * 48 | * @param request 49 | */ 50 | void update(UpdateUserRequest request); 51 | 52 | /** 53 | * 发送邮箱验证链接 54 | * 55 | * @param email 56 | * @return void 57 | */ 58 | void validateEmail(String email); 59 | 60 | /** 61 | * 绑定邮箱 62 | * 63 | * @param code 64 | * @return void 65 | */ 66 | void bindEmail(String code); 67 | 68 | /** 69 | * 更新头像 70 | * 71 | * @param file 72 | * @return void 73 | */ 74 | void updateAvatar(MultipartFile file); 75 | 76 | /** 77 | * 修改密码 78 | * 79 | * @param oldPassword 80 | * @param newPassword 81 | * @return void 82 | */ 83 | void updatePassword(String oldPassword, String newPassword); 84 | 85 | /** 86 | * 重置密码 87 | * 88 | * @param mobile 89 | * @param code 90 | * @param password 91 | * @return void 92 | */ 93 | void resetPassword(long mobile, String code, String password); 94 | 95 | /** 96 | * 更换手机号 验证手机号 97 | * 98 | * @param mobile 99 | * @param code 100 | * @return void 101 | */ 102 | void validateMobile(long mobile, String code); 103 | 104 | /** 105 | * 更换手机号 重新绑定 106 | * 107 | * @param mobile 108 | * @param code 109 | * @return void 110 | */ 111 | void rebindMobile(long mobile, String code); 112 | 113 | /** 114 | * 分页查询用户 115 | * 116 | * @param current 117 | * @param size 118 | * @param username 119 | * @param nickname 120 | * @return 121 | */ 122 | IPage page(long current, long size, String username, String nickname); 123 | 124 | /** 125 | * 修改用户状态 126 | * 127 | * @param userId 128 | * @param status 129 | */ 130 | void status(Integer userId, Integer status); 131 | 132 | /** 133 | * 绑定手机号 - 用于原手机号为空的情况 134 | * @param mobile 135 | * @param code 136 | */ 137 | void bindMobile(long mobile, String code); 138 | 139 | /** 140 | * 绑定用户名 - 用于用户名为空的情况 141 | * @param username 142 | */ 143 | void bindUsername(String username); 144 | } 145 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/service/impl/ArticleLikeServiceImpl.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.service.impl; 2 | 3 | import cn.poile.blog.common.constant.ErrorEnum; 4 | import cn.poile.blog.common.exception.ApiException; 5 | import cn.poile.blog.common.security.ServerSecurityContext; 6 | import cn.poile.blog.entity.ArticleLike; 7 | import cn.poile.blog.mapper.ArticleLikeMapper; 8 | import cn.poile.blog.service.IArticleLikeService; 9 | import cn.poile.blog.service.IArticleService; 10 | import cn.poile.blog.vo.CustomUserDetails; 11 | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; 12 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 13 | import lombok.extern.log4j.Log4j2; 14 | import org.springframework.beans.factory.annotation.Autowired; 15 | import org.springframework.stereotype.Service; 16 | import org.springframework.transaction.annotation.Transactional; 17 | 18 | /** 19 | *

20 | * 文章点赞表 服务实现类 21 | *

22 | * 23 | * @author yaohw 24 | * @since 2019-12-02 25 | */ 26 | @Log4j2 27 | @Service 28 | public class ArticleLikeServiceImpl extends ServiceImpl implements IArticleLikeService { 29 | 30 | @Autowired 31 | private IArticleService articleService; 32 | 33 | /** 34 | * 查询文章是否已点赞 35 | * 36 | * @param articleId 37 | * @return 1:是,0:否 38 | */ 39 | @Override 40 | public Integer liked(Integer articleId) { 41 | QueryWrapper queryWrapper = new QueryWrapper<>(); 42 | CustomUserDetails userDetail = ServerSecurityContext.getUserDetail(true); 43 | queryWrapper.lambda().eq(ArticleLike::getArticleId, articleId).eq(ArticleLike::getUserId,userDetail.getId()); 44 | return count(queryWrapper); 45 | } 46 | 47 | /** 48 | * 文章点赞 49 | * 50 | * @param articleId 51 | */ 52 | @Override 53 | @Transactional(rollbackFor = Exception.class) 54 | public void like(Integer articleId) { 55 | QueryWrapper queryWrapper = new QueryWrapper<>(); 56 | CustomUserDetails userDetail = ServerSecurityContext.getUserDetail(true); 57 | Integer userId = userDetail.getId(); 58 | queryWrapper.lambda().eq(ArticleLike::getArticleId, articleId).eq(ArticleLike::getUserId,userId); 59 | int count = count(queryWrapper); 60 | if (count != 0) { 61 | throw new ApiException(ErrorEnum.INVALID_REQUEST.getErrorCode(),"文章已点赞,不可重复点赞"); 62 | } 63 | ArticleLike like = new ArticleLike(); 64 | like.setArticleId(articleId); 65 | like.setUserId(userId); 66 | save(like); 67 | articleService.likeCountIncrement(articleId); 68 | 69 | } 70 | 71 | /** 72 | * 取消文章点赞 73 | * 74 | * @param articleId 75 | */ 76 | @Override 77 | @Transactional(rollbackFor = Exception.class) 78 | public void cancel(Integer articleId) { 79 | QueryWrapper queryWrapper = new QueryWrapper<>(); 80 | CustomUserDetails userDetail = ServerSecurityContext.getUserDetail(true); 81 | queryWrapper.lambda().eq(ArticleLike::getArticleId, articleId).eq(ArticleLike::getUserId,userDetail.getId()); 82 | int count = count(queryWrapper); 83 | if (count == 0) { 84 | throw new ApiException(ErrorEnum.INVALID_REQUEST.getErrorCode(),"文章未点赞"); 85 | } 86 | remove(queryWrapper); 87 | articleService.likeCountDecrement(articleId); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/service/impl/ArticleTagServiceImpl.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.service.impl; 2 | 3 | import cn.poile.blog.entity.ArticleTag; 4 | import cn.poile.blog.mapper.ArticleTagMapper; 5 | import cn.poile.blog.service.IArticleTagService; 6 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 7 | import org.springframework.stereotype.Service; 8 | 9 | /** 10 | *

11 | * 文章-标签 关联表 服务实现类 12 | *

13 | * 14 | * @author yaohw 15 | * @since 2019-11-15 16 | */ 17 | @Service 18 | public class ArticleTagServiceImpl extends ServiceImpl implements IArticleTagService { 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/service/impl/AuthenticationServiceImpl.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.service.impl; 2 | 3 | import cn.poile.blog.common.security.AuthenticationToken; 4 | import cn.poile.blog.common.security.MobileCodeAuthenticationToken; 5 | import cn.poile.blog.common.security.RedisTokenStore; 6 | import cn.poile.blog.common.sms.SmsCodeService; 7 | import cn.poile.blog.entity.Client; 8 | import cn.poile.blog.service.AuthenticationService; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.security.authentication.AuthenticationManager; 11 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; 12 | import org.springframework.security.core.Authentication; 13 | import org.springframework.stereotype.Service; 14 | 15 | /** 16 | * @author: yaohw 17 | * @create: 2019-10-28 18:27 18 | **/ 19 | @Service 20 | public class AuthenticationServiceImpl implements AuthenticationService { 21 | 22 | @Autowired 23 | private AuthenticationManager authenticationManager; 24 | 25 | @Autowired 26 | private RedisTokenStore tokenStore; 27 | 28 | @Autowired 29 | private SmsCodeService smsCodeService; 30 | 31 | /** 32 | * 用户名或手机号密码认证 33 | * @param s 手机号或用户名 34 | * @param password 密码 35 | * @param client 36 | * @return 37 | */ 38 | @Override 39 | public AuthenticationToken usernameOrMobilePasswordAuthenticate(String s, String password, Client client) { 40 | UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(s, password); 41 | Authentication authenticate = authenticationManager.authenticate(authenticationToken); 42 | return tokenStore.storeToken(authenticate,client); 43 | } 44 | 45 | /** 46 | * 手机号验证码认证 47 | * @param mobile 48 | * @param code 49 | * @param client 客户端 50 | * @return 51 | */ 52 | @Override 53 | public AuthenticationToken mobileCodeAuthenticate(long mobile, String code,Client client) { 54 | MobileCodeAuthenticationToken authenticationToken = new MobileCodeAuthenticationToken(mobile, code); 55 | Authentication authenticate = authenticationManager.authenticate(authenticationToken); 56 | AuthenticationToken storeAccessToken = tokenStore.storeToken(authenticate,client); 57 | smsCodeService.deleteSmsCode(mobile); 58 | return storeAccessToken; 59 | } 60 | 61 | /** 62 | * 移除 accessToken 相关 63 | * @param accessToken 64 | * @param client 客户端 65 | */ 66 | @Override 67 | public void remove(String accessToken,Client client) { 68 | tokenStore.remove(accessToken,client); 69 | } 70 | 71 | /** 72 | * 刷新accessToken 73 | * @param refreshToken 74 | * @param client 客户端 75 | * @return 76 | */ 77 | @Override 78 | public AuthenticationToken refreshAccessToken(String refreshToken,Client client) { 79 | return tokenStore.refreshAuthToken(refreshToken,client); 80 | } 81 | 82 | 83 | } 84 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/service/impl/ClientServiceImpl.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.service.impl; 2 | 3 | import cn.poile.blog.entity.Client; 4 | import cn.poile.blog.mapper.ClientMapper; 5 | import cn.poile.blog.service.IClientService; 6 | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; 7 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 8 | import lombok.extern.log4j.Log4j2; 9 | import org.springframework.cache.annotation.CacheEvict; 10 | import org.springframework.cache.annotation.Cacheable; 11 | import org.springframework.stereotype.Service; 12 | 13 | /** 14 | *

15 | * 客户端表 服务实现类 16 | *

17 | * 18 | * @author yaohw 19 | * @since 2019-12-06 20 | */ 21 | @Service 22 | @Log4j2 23 | public class ClientServiceImpl extends ServiceImpl implements IClientService { 24 | 25 | /** 26 | * 根据客户端id获取客户端 27 | * 28 | * @param clientId 29 | * @return 30 | */ 31 | @Override 32 | @Cacheable(value = "client", key = "#clientId") 33 | public Client getClientByClientId(String clientId) { 34 | QueryWrapper queryWrapper = new QueryWrapper<>(); 35 | queryWrapper.lambda().eq(Client::getClientId,clientId); 36 | return getOne(queryWrapper,false); 37 | } 38 | 39 | /** 40 | * 清空缓存 41 | */ 42 | @Override 43 | @CacheEvict(value = "client",allEntries = true) 44 | public void clearCache() { 45 | log.info("清空client缓存"); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/service/impl/FriendLinkServiceImpl.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.service.impl; 2 | 3 | import cn.poile.blog.entity.FriendLink; 4 | import cn.poile.blog.mapper.FriendLinkMapper; 5 | import cn.poile.blog.service.IFriendLinkService; 6 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 7 | import org.springframework.stereotype.Service; 8 | 9 | /** 10 | *

11 | * 友链表 服务实现类 12 | *

13 | * 14 | * @author yaohw 15 | * @since 2019-12-02 16 | */ 17 | @Service 18 | public class FriendLinkServiceImpl extends ServiceImpl implements IFriendLinkService { 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/service/impl/OauthUserServiceImpl.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.service.impl; 2 | 3 | import cn.poile.blog.entity.OauthUser; 4 | import cn.poile.blog.mapper.OauthUserMapper; 5 | import cn.poile.blog.service.IOauthUserService; 6 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 7 | import org.springframework.stereotype.Service; 8 | 9 | /** 10 | *

11 | * 第三方登录关联表 服务实现类 12 | *

13 | * 14 | * @author yaohw 15 | * @since 2020-05-20 16 | */ 17 | @Service 18 | public class OauthUserServiceImpl extends ServiceImpl implements IOauthUserService { 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/service/impl/TagServiceImpl.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.service.impl; 2 | 3 | import cn.poile.blog.common.constant.CommonConstant; 4 | import cn.poile.blog.common.constant.ErrorEnum; 5 | import cn.poile.blog.common.exception.ApiException; 6 | import cn.poile.blog.entity.Tag; 7 | import cn.poile.blog.mapper.TagMapper; 8 | import cn.poile.blog.service.ITagService; 9 | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; 10 | import com.baomidou.mybatisplus.core.metadata.IPage; 11 | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; 12 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 13 | import org.apache.commons.lang3.StringUtils; 14 | import org.springframework.stereotype.Service; 15 | import org.springframework.transaction.annotation.Transactional; 16 | 17 | import java.util.List; 18 | 19 | /** 20 | *

21 | * 标签表 服务实现类 22 | *

23 | * 24 | * @author yaohw 25 | * @since 2019-11-14 26 | */ 27 | @Service 28 | public class TagServiceImpl extends ServiceImpl implements ITagService { 29 | 30 | /** 31 | * 新增标签 32 | * 33 | * @param tagName 34 | * @return void 35 | */ 36 | @Override 37 | @Transactional(rollbackFor = Exception.class) 38 | public void addTag(String tagName) { 39 | Tag daoTag = selectByTagName(tagName); 40 | if (daoTag != null) { 41 | throw new ApiException(ErrorEnum.INVALID_REQUEST.getErrorCode(),"标签已存在"); 42 | } 43 | Tag tag = new Tag(); 44 | tag.setName(tagName); 45 | tag.setDeleted(CommonConstant.NOT_DELETED); 46 | save(tag); 47 | } 48 | 49 | /** 50 | * 分页查询标签 51 | * @param current 当前页 52 | * @param size 每页数量 53 | * @param tagName 标签名 54 | * @return com.baomidou.mybatisplus.core.metadata.IPage 55 | */ 56 | @Override 57 | public IPage selectTagPage(long current,long size,String tagName) { 58 | Page page = new Page<>(current,size); 59 | if (StringUtils.isBlank(tagName)) { 60 | return page(page); 61 | } 62 | QueryWrapper queryWrapper = new QueryWrapper<>(); 63 | queryWrapper.lambda().like(Tag::getName,tagName); 64 | return page(page,queryWrapper); 65 | } 66 | 67 | /** 68 | * 标签列表 69 | * @param tagName 70 | * @return java.util.List 71 | */ 72 | @Override 73 | public List selectTagList(String tagName) { 74 | QueryWrapper queryWrapper = new QueryWrapper<>(); 75 | if (StringUtils.isNotBlank(tagName)) { 76 | queryWrapper.lambda().like(Tag::getName,tagName); 77 | } 78 | return list(queryWrapper); 79 | } 80 | 81 | 82 | /** 83 | * 修改标签 84 | * @param id 85 | * @param tagName 86 | */ 87 | @Override 88 | public void update(int id, String tagName) { 89 | Tag daoTag = selectByTagName(tagName); 90 | if (daoTag != null && daoTag.getName().equals(tagName)) { 91 | throw new ApiException(ErrorEnum.INVALID_REQUEST.getErrorCode(),"标签已存在"); 92 | } 93 | Tag tag = new Tag(); 94 | tag.setName(tagName); 95 | tag.setId(id); 96 | updateById(tag); 97 | } 98 | 99 | /** 100 | * 删除标签 101 | * 102 | * @param id 103 | */ 104 | @Override 105 | public void delete(int id) { 106 | removeById(id); 107 | } 108 | 109 | /** 110 | * 根据标签名查询 111 | * @param tagName 112 | * @return cn.poile.blog.entity.Tag 113 | */ 114 | private Tag selectByTagName(String tagName){ 115 | QueryWrapper queryWrapper = new QueryWrapper<>(); 116 | queryWrapper.lambda().eq(Tag::getName,tagName); 117 | return getOne(queryWrapper,false); 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/service/impl/UserDetailsServiceImpl.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.service.impl; 2 | 3 | import cn.poile.blog.common.util.ValidateUtil; 4 | import cn.poile.blog.service.IUserService; 5 | import cn.poile.blog.vo.CustomUserDetails; 6 | import cn.poile.blog.vo.UserVo; 7 | import org.springframework.beans.BeanUtils; 8 | import org.springframework.beans.factory.annotation.Autowired; 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 | /** 15 | * @author: yaohw 16 | * @create: 2019-10-24 16:40 17 | **/ 18 | @Service 19 | public class UserDetailsServiceImpl implements UserDetailsService { 20 | 21 | @Autowired 22 | private IUserService userService; 23 | 24 | @Override 25 | public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { 26 | boolean isMobile = ValidateUtil.validateMobile(username); 27 | UserVo userVo; 28 | if (isMobile) { 29 | userVo = userService.selectUserVoByUsernameOtherwiseMobile(null,Long.parseLong(username)); 30 | } else { 31 | userVo = userService.selectUserVoByUsernameOtherwiseMobile(username,null); 32 | } 33 | if (userVo == null) { 34 | throw new UsernameNotFoundException("user not found:" + username); 35 | } 36 | UserDetails userDetails = new CustomUserDetails(); 37 | BeanUtils.copyProperties(userVo,userDetails); 38 | return userDetails; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/vo/ArticleArchivesVo.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.vo; 2 | 3 | import com.baomidou.mybatisplus.annotation.TableField; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import io.swagger.annotations.ApiModel; 6 | import io.swagger.annotations.ApiModelProperty; 7 | import lombok.Data; 8 | 9 | /** 10 | * 归档 11 | * @author: yaohw 12 | * @create: 2019-11-27 11:11 13 | **/ 14 | @Data 15 | @JsonInclude(JsonInclude.Include.NON_NULL) 16 | @ApiModel(value = "ArticleArchivesVo对象",description = "文章归档") 17 | public class ArticleArchivesVo { 18 | 19 | @ApiModelProperty(value = "年月,格式yyyy-mm") 20 | private String yearMonth; 21 | 22 | @TableField(value = "article_count") 23 | @ApiModelProperty(value = "数量") 24 | private long articleCount; 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/vo/ArticleCategoryStatisticsVo.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.vo; 2 | 3 | import cn.poile.blog.entity.Category; 4 | import com.baomidou.mybatisplus.annotation.TableField; 5 | import com.fasterxml.jackson.annotation.JsonInclude; 6 | import io.swagger.annotations.ApiModel; 7 | import io.swagger.annotations.ApiModelProperty; 8 | import lombok.Data; 9 | import lombok.EqualsAndHashCode; 10 | import lombok.experimental.Accessors; 11 | 12 | /** 13 | * 文章分类统计 14 | * @author: yaohw 15 | * @create: 2019-11-28 10:45 16 | **/ 17 | @Data 18 | @Accessors(chain = true) 19 | @EqualsAndHashCode(callSuper = false) 20 | @JsonInclude(JsonInclude.Include.NON_NULL) 21 | @ApiModel(value="ArticleCategoriesVo对象", description="文章分类计数") 22 | public class ArticleCategoryStatisticsVo extends Category { 23 | 24 | @TableField(value = "article_count") 25 | @ApiModelProperty("分类文章数量") 26 | private int articleCount; 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/vo/ArticleCommentVo.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.vo; 2 | 3 | import cn.poile.blog.entity.Article; 4 | import cn.poile.blog.entity.User; 5 | import com.baomidou.mybatisplus.annotation.IdType; 6 | import com.baomidou.mybatisplus.annotation.TableId; 7 | import com.fasterxml.jackson.annotation.JsonFormat; 8 | import com.fasterxml.jackson.annotation.JsonInclude; 9 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 10 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 11 | import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; 12 | import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; 13 | import io.swagger.annotations.ApiModel; 14 | import io.swagger.annotations.ApiModelProperty; 15 | import lombok.Data; 16 | import lombok.EqualsAndHashCode; 17 | import lombok.experimental.Accessors; 18 | import java.time.LocalDateTime; 19 | import java.util.List; 20 | 21 | /** 22 | * @author: yaohw 23 | * @create: 2019-12-03 20:06 24 | **/ 25 | @Data 26 | @Accessors(chain = true) 27 | @EqualsAndHashCode(callSuper = false) 28 | @JsonInclude(JsonInclude.Include.NON_NULL) 29 | @ApiModel(value="ArticleCommentVo", description="文章评论Vo") 30 | public class ArticleCommentVo { 31 | 32 | @ApiModelProperty(value = "id") 33 | @TableId(value = "id", type = IdType.AUTO) 34 | private Integer id; 35 | 36 | @ApiModelProperty(value = "评论内容") 37 | private String content; 38 | 39 | @ApiModelProperty(value = "评论时间") 40 | @JsonSerialize(using = LocalDateTimeSerializer.class) 41 | @JsonDeserialize(using = LocalDateTimeDeserializer.class) 42 | @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone = "GMT+8") 43 | private LocalDateTime commentTime; 44 | 45 | @ApiModelProperty(value = "文章") 46 | private Article article; 47 | 48 | @ApiModelProperty(value = "评论者id") 49 | private User fromUser; 50 | 51 | @ApiModelProperty(value = "评论回复列表") 52 | private List replyList; 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/vo/ArticleReplyVo.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.vo; 2 | 3 | import cn.poile.blog.entity.User; 4 | import com.baomidou.mybatisplus.annotation.IdType; 5 | import com.baomidou.mybatisplus.annotation.TableId; 6 | import com.fasterxml.jackson.annotation.JsonFormat; 7 | import com.fasterxml.jackson.annotation.JsonInclude; 8 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 9 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 10 | import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; 11 | import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; 12 | import io.swagger.annotations.ApiModel; 13 | import io.swagger.annotations.ApiModelProperty; 14 | import lombok.Data; 15 | import lombok.EqualsAndHashCode; 16 | import lombok.experimental.Accessors; 17 | 18 | import java.time.LocalDateTime; 19 | 20 | /** 21 | * @author: yaohw 22 | * @create: 2019-12-03 20:11 23 | **/ 24 | @Data 25 | @Accessors(chain = true) 26 | @EqualsAndHashCode(callSuper = false) 27 | @JsonInclude(JsonInclude.Include.NON_NULL) 28 | @ApiModel(value="ArticleReplyVo", description="文章评论回复Vo") 29 | public class ArticleReplyVo { 30 | 31 | @ApiModelProperty(value = "id") 32 | @TableId(value = "id", type = IdType.AUTO) 33 | private Integer id; 34 | 35 | @ApiModelProperty(value = "回复内容") 36 | private String content; 37 | 38 | @ApiModelProperty(value = "回复时间") 39 | @JsonSerialize(using = LocalDateTimeSerializer.class) 40 | @JsonDeserialize(using = LocalDateTimeDeserializer.class) 41 | @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone = "GMT+8") 42 | private LocalDateTime replyTime; 43 | 44 | @ApiModelProperty(value = "评论者") 45 | private User fromUser; 46 | 47 | @ApiModelProperty(value = "被评论者") 48 | private User toUser; 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/vo/ArticleTagStatisticsVo.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.vo; 2 | 3 | import cn.poile.blog.entity.Tag; 4 | import com.baomidou.mybatisplus.annotation.TableField; 5 | import com.fasterxml.jackson.annotation.JsonInclude; 6 | import io.swagger.annotations.ApiModel; 7 | import io.swagger.annotations.ApiModelProperty; 8 | import lombok.Data; 9 | import lombok.EqualsAndHashCode; 10 | import lombok.experimental.Accessors; 11 | 12 | /** 13 | * @author: yaohw 14 | * @create: 2019-11-28 15:07 15 | **/ 16 | @Data 17 | @Accessors(chain = true) 18 | @EqualsAndHashCode(callSuper = false) 19 | @JsonInclude(JsonInclude.Include.NON_NULL) 20 | @ApiModel(value="ArticleTagStatisticsVo对象", description="文章标签计数") 21 | public class ArticleTagStatisticsVo extends Tag { 22 | 23 | @TableField(value = "article_count") 24 | @ApiModelProperty("分类文章数量") 25 | private int articleCount; 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/vo/ArticleVo.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.vo; 2 | 3 | import cn.poile.blog.entity.Article; 4 | import cn.poile.blog.entity.Category; 5 | import cn.poile.blog.entity.Tag; 6 | import cn.poile.blog.entity.User; 7 | import com.fasterxml.jackson.annotation.JsonInclude; 8 | import io.swagger.annotations.ApiModel; 9 | import io.swagger.annotations.ApiModelProperty; 10 | import lombok.Data; 11 | import lombok.EqualsAndHashCode; 12 | import lombok.experimental.Accessors; 13 | 14 | import java.util.List; 15 | 16 | /** 17 | * 文章详细对象 18 | * @author: yaohw 19 | * @create: 2019-11-25 11:10 20 | **/ 21 | @Data 22 | @Accessors(chain = true) 23 | @EqualsAndHashCode(callSuper = true) 24 | @JsonInclude(JsonInclude.Include.NON_NULL) 25 | @ApiModel(value="ArticleVo对象", description="文章详细对象") 26 | public class ArticleVo extends Article { 27 | 28 | @ApiModelProperty("作者") 29 | private User user; 30 | 31 | @ApiModelProperty("标签列表") 32 | private List tagList; 33 | 34 | @ApiModelProperty("分类列表,顺序:root node2 node3") 35 | private List categoryList; 36 | 37 | @ApiModelProperty("上一篇") 38 | private Article previous; 39 | 40 | @ApiModelProperty("下一篇") 41 | private Article next; 42 | 43 | @ApiModelProperty("推荐分数") 44 | private Double recommendScore; 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/vo/CustomUserDetails.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.vo; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import lombok.Data; 6 | import lombok.EqualsAndHashCode; 7 | import lombok.experimental.Accessors; 8 | import org.springframework.security.core.GrantedAuthority; 9 | import org.springframework.security.core.userdetails.UserDetails; 10 | import org.springframework.util.CollectionUtils; 11 | 12 | import java.util.Collection; 13 | import java.util.Collections; 14 | import java.util.stream.Collectors; 15 | 16 | /** 17 | * @author: yaohw 18 | * @create: 2019-10-24 16:45 19 | **/ 20 | @Data 21 | @Accessors(chain = true) 22 | @EqualsAndHashCode(callSuper = false) 23 | @JsonInclude(JsonInclude.Include.NON_NULL) 24 | public class CustomUserDetails extends UserVo implements UserDetails { 25 | 26 | @Override 27 | @JsonIgnore 28 | public Collection getAuthorities() { 29 | if (!CollectionUtils.isEmpty(roles)) { 30 | return roles.stream().map(this::createAuthority).collect(Collectors.toSet()); 31 | } 32 | return Collections.emptyList(); 33 | } 34 | 35 | private GrantedAuthority createAuthority(String authority) { 36 | return (()->authority); 37 | } 38 | 39 | @Override 40 | @JsonIgnore 41 | public boolean isAccountNonExpired() { 42 | return !getStatus().equals(3); 43 | } 44 | 45 | @Override 46 | @JsonIgnore 47 | public boolean isAccountNonLocked() { 48 | return !getStatus().equals(1); 49 | } 50 | 51 | @Override 52 | @JsonIgnore 53 | public boolean isCredentialsNonExpired() { 54 | return true; 55 | } 56 | 57 | @Override 58 | @JsonIgnore 59 | public boolean isEnabled() { 60 | return !getStatus().equals(2); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/vo/LeaveMessageReplyVo.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.vo; 2 | 3 | import cn.poile.blog.entity.User; 4 | import com.baomidou.mybatisplus.annotation.IdType; 5 | import com.baomidou.mybatisplus.annotation.TableId; 6 | import com.fasterxml.jackson.annotation.JsonFormat; 7 | import com.fasterxml.jackson.annotation.JsonInclude; 8 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 9 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 10 | import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; 11 | import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; 12 | import io.swagger.annotations.ApiModel; 13 | import io.swagger.annotations.ApiModelProperty; 14 | import lombok.Data; 15 | import lombok.EqualsAndHashCode; 16 | import lombok.experimental.Accessors; 17 | 18 | import java.time.LocalDateTime; 19 | 20 | /** 21 | * @author: yaohw 22 | * @create: 2019-12-06 10:05 23 | **/ 24 | @Data 25 | @Accessors(chain = true) 26 | @EqualsAndHashCode(callSuper = false) 27 | @JsonInclude(JsonInclude.Include.NON_NULL) 28 | @ApiModel(value="LeaveMessageReplyVo对象", description="留言回复") 29 | public class LeaveMessageReplyVo { 30 | 31 | @ApiModelProperty(value = "id") 32 | private Integer id; 33 | 34 | @ApiModelProperty(value = "内容") 35 | private String content; 36 | 37 | @ApiModelProperty(value = "时间") 38 | @JsonSerialize(using = LocalDateTimeSerializer.class) 39 | @JsonDeserialize(using = LocalDateTimeDeserializer.class) 40 | @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone = "GMT+8") 41 | private LocalDateTime createTime; 42 | 43 | @ApiModelProperty(value = "回复者") 44 | private User fromUser; 45 | 46 | @ApiModelProperty(value = "被回复者") 47 | private User toUser; 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/vo/LeaveMessageVo.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.vo; 2 | 3 | import cn.poile.blog.entity.User; 4 | import com.baomidou.mybatisplus.annotation.IdType; 5 | import com.baomidou.mybatisplus.annotation.TableId; 6 | import com.fasterxml.jackson.annotation.JsonFormat; 7 | import com.fasterxml.jackson.annotation.JsonInclude; 8 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 9 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 10 | import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; 11 | import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; 12 | import io.swagger.annotations.ApiModel; 13 | import io.swagger.annotations.ApiModelProperty; 14 | import lombok.Data; 15 | import lombok.EqualsAndHashCode; 16 | import lombok.experimental.Accessors; 17 | 18 | import java.time.LocalDateTime; 19 | import java.util.List; 20 | 21 | /** 22 | * @author: yaohw 23 | * @create: 2019-12-06 10:01 24 | **/ 25 | @Data 26 | @Accessors(chain = true) 27 | @EqualsAndHashCode(callSuper = false) 28 | @JsonInclude(JsonInclude.Include.NON_NULL) 29 | @ApiModel(value="LeaveMessageVo对象", description="留言") 30 | public class LeaveMessageVo { 31 | 32 | @ApiModelProperty(value = "id") 33 | private Integer id; 34 | 35 | @ApiModelProperty(value = "内容") 36 | private String content; 37 | 38 | @ApiModelProperty(value = "时间") 39 | @JsonSerialize(using = LocalDateTimeSerializer.class) 40 | @JsonDeserialize(using = LocalDateTimeDeserializer.class) 41 | @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone = "GMT+8") 42 | private LocalDateTime createTime; 43 | 44 | @ApiModelProperty(value = "留言者") 45 | private User fromUser; 46 | 47 | @ApiModelProperty(value = "回复列表") 48 | private List replyList; 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/vo/UserVo.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.vo; 2 | 3 | import cn.poile.blog.entity.User; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import io.swagger.annotations.ApiModel; 6 | import io.swagger.annotations.ApiModelProperty; 7 | import lombok.Data; 8 | import lombok.EqualsAndHashCode; 9 | import lombok.experimental.Accessors; 10 | 11 | import java.util.List; 12 | 13 | /** 14 | * @author: yaohw 15 | * @create: 2019-10-24 16:50 16 | **/ 17 | @Data 18 | @Accessors(chain = true) 19 | @EqualsAndHashCode(callSuper = false) 20 | @JsonInclude(JsonInclude.Include.NON_NULL) 21 | @ApiModel(value="UserVo对象", description="用户详细信息") 22 | public class UserVo extends User { 23 | 24 | /** 25 | * 角色列表 26 | */ 27 | @ApiModelProperty(value = "角色列表") 28 | protected List roles; 29 | 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/cn/poile/blog/wrapper/ArticlePageQueryWrapper.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog.wrapper; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | /** 8 | * @author: yaohw 9 | * @create: 2019-11-28 19:41 10 | **/ 11 | @Data 12 | @AllArgsConstructor 13 | @NoArgsConstructor 14 | public class ArticlePageQueryWrapper { 15 | 16 | private Integer status; 17 | 18 | private Long offset; 19 | 20 | private Long limit; 21 | 22 | private Integer categoryId; 23 | 24 | private Integer tagId; 25 | 26 | private String title; 27 | 28 | private String orderBy; 29 | 30 | private String start; 31 | 32 | private String end; 33 | } 34 | -------------------------------------------------------------------------------- /src/main/resources/application-prod.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 9090 3 | spring: 4 | task: 5 | execution: 6 | pool: 7 | max-size: 16 8 | core-size: 8 9 | keep-alive: 60s 10 | queue-capacity: 100 11 | allow-core-thread-timeout: true 12 | redis: 13 | host: 127.0.0.1 14 | port: 6379 15 | password: 16 | timeout: 3000 17 | lettuce: 18 | pool: 19 | max-active: 8 20 | max-idle: 8 21 | min-idle: 0 22 | max-wait: -1ms 23 | datasource: 24 | type: com.alibaba.druid.pool.DruidDataSource 25 | driver-class-name: com.mysql.jdbc.Driver 26 | url: jdbc:mysql://193.112.43.235:3306/blog_db?useSSL=false&u-seUnicode=true&characterEncoding=utf-8 27 | username: root 28 | password: Ab452637! 29 | druid: 30 | initial-size: 5 31 | min-idle: 5 32 | maxActive: 20 33 | maxWait: 60000 34 | timeBetweenEvictionRunsMillis: 60000 35 | minEvictableIdleTimeMillis: 300000 36 | validationQuery: SELECT 1 FROM DUAL 37 | testWhileIdle: true 38 | testOnBorrow: false 39 | testOnReturn: false 40 | poolPreparedStatements: true 41 | maxPoolPreparedStatementPerConnectionSize: 20 42 | filter: 43 | slf4j: 44 | enabled: true 45 | wall: 46 | enabled: true 47 | stat: 48 | enabled: true 49 | connectionProperties: druid.stat.mergeSql\=true;druid.stat.slowSqlMillis\=5000 50 | web-stat-filter: 51 | enabled: true 52 | url-pattern: "/*" 53 | exclusions: "*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*" 54 | stat-view-servlet: 55 | url-pattern: "/druid/*" 56 | reset-enable: false 57 | login-username: admin 58 | login-password: yaohw321! 59 | mail: 60 | host: smtp.163.com 61 | port: 465 62 | username: 15625295093@163.com 63 | password: 64 | protocol: smtp 65 | default-encoding: UTF-8 66 | jndi-name: 个人悦读分享 67 | properties: 68 | mail: 69 | smtp: 70 | ssl: 71 | enable: true 72 | 73 | 74 | ## oss存储配置 ## 75 | oss: 76 | type: 3 77 | netease: 78 | accessKey: 2bec4d8797e64f99b967c88fa0d08d39 79 | secretKey: ca8bb777179949a4b0767108bdf032e8 80 | endpoint: nos-eastchina1.126.net 81 | bucket: poile-img 82 | 83 | sms: 84 | type: 1 85 | expire: 300 86 | day_max: 10 87 | ali: 88 | regionId: cn-hangzhou 89 | accessKeyId: LTAI4FvoP9o1tH 90 | accessKeySecret: YjUrQ9sTEWwGY6Ys1o 91 | signName: 个人悦读分享 92 | templateCode: SMS_176942058 93 | 94 | mail: 95 | check: http://www.poile.cn/email/verify 96 | article: http://www.poile.cn/article/#/ 97 | message: http://www.poile.cn/message 98 | 99 | # 生产环境禁用swagger 100 | swagger: 101 | enabled: true 102 | 103 | 104 | -------------------------------------------------------------------------------- /src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | profiles: 3 | active: 4 | - prod 5 | application: 6 | name: blog-api 7 | servlet: 8 | multipart: 9 | enabled: true 10 | file-size-threshold: 0 11 | location: /var/tmp 12 | max-request-size: 5MB 13 | max-file-size: 5MB 14 | thymeleaf: 15 | suffix: .html 16 | cache: true 17 | encoding: UTF-8 18 | 19 | # 忽略安全校验url列表,不可修改 20 | ignore: 21 | list: 22 | - /actuator/** 23 | - /v2/api-docs 24 | - /swagger/api-docs 25 | - /swagger-resources/** 26 | - /swagger-ui.html 27 | - /webjars/** 28 | - /druid/** 29 | - /sms/** 30 | - /account/login 31 | - /mobile/login 32 | - /logout 33 | - /refresh_access_token 34 | - /user/register 35 | - /user/password/reset 36 | - /user/email/bind 37 | - /user/password/reset 38 | - /category/tree 39 | - /category/list 40 | - /tag/list 41 | - /article/published/page 42 | - /article/view/** 43 | - /article/increment_view/** 44 | - /article/archives/page 45 | - /article/category/statistic 46 | - /article/tag/statistic 47 | - /article/recommend/list 48 | - /article/interrelated/list 49 | - /article/count 50 | - /article/like/list 51 | - /friend/link/page 52 | - /friend/link/list 53 | - /article/comment/page 54 | - /article/comment/latest 55 | - /leave/message/page 56 | - /leave/message/latest 57 | - /oauth 58 | 59 | # mybatis-plus配置,不可修改 60 | mybatis-plus: 61 | mapper-locations: 62 | - classpath:/mapper/*.xml 63 | type-aliases-package: cn.poile.bolg.entity 64 | global-config: 65 | db-config: 66 | # 配置逻辑删除,1:是,0:否 67 | logic-not-delete-value: 0 68 | logic-delete-value: 1 69 | -------------------------------------------------------------------------------- /src/main/resources/logback-spring.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | %d{yyyy-MM-dd HH:mm:ss.SSS} ----> [%thread] ---> %-5level %logger{50} - %msg%n 12 | 13 | 14 | %d{yyyy-MM-dd HH:mm:ss.SSS} ==== [%thread] ==== %-5level %logger{50} - %msg%n 15 | 16 | 17 | 18 | 19 | 20 | ${LOG_HOME}/${name}.log 21 | 22 | ${LOG_HOME}/${name}-%d{yyyy-MM-dd}-%i.log 23 | 365 24 | 25 | 100MB 26 | 27 | 28 | 29 | 30 | %d{yyyy-MM-dd HH:mm:ss.SSS} [ %thread ] - [ %-5level ] [ %logger{50} : %line ] - %msg%n 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /src/main/resources/mapper/ArticleCollectMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/main/resources/mapper/ArticleLikeMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/main/resources/mapper/ArticleReplyMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/main/resources/mapper/ArticleTagMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/main/resources/mapper/CategoryMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/main/resources/mapper/ClientMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/main/resources/mapper/FriendLinkMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/main/resources/mapper/OauthUserMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/main/resources/mapper/TagMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/main/resources/mapper/UserMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/main/resources/scripts/redis/limit.lua: -------------------------------------------------------------------------------- 1 | -- 下标从 1 开始 2 | local key = KEYS[1] 3 | local now = tonumber(ARGV[1]) 4 | local ttl = tonumber(ARGV[2]) 5 | local expired = tonumber(ARGV[3]) 6 | -- 最大访问量 7 | local max = tonumber(ARGV[4]) 8 | 9 | -- 清除过期的数据 10 | -- 移除指定分数区间内的所有元素,expired 即已经过期的 score 11 | -- 根据当前时间毫秒数 - 超时毫秒数,得到过期时间 expired 12 | redis.call('zremrangebyscore', key, 0, expired) 13 | 14 | -- 获取 zset 中的当前元素个数 15 | local current = tonumber(redis.call('zcard', key)) 16 | local next = current + 1 17 | 18 | if next > max then 19 | -- 达到限流大小 返回 0 20 | return 0; 21 | else 22 | -- 往 zset 中添加一个值、得分均为当前时间戳的元素,[value,score] 23 | redis.call("zadd", key, now, now) 24 | -- 每次访问均重新设置 zset 的过期时间,单位毫秒 25 | redis.call("pexpire", key, ttl) 26 | return next 27 | end 28 | -------------------------------------------------------------------------------- /src/test/java/cn/poile/blog/BlogApplicationTest.java: -------------------------------------------------------------------------------- 1 | package cn.poile.blog; 2 | 3 | import lombok.extern.log4j.Log4j2; 4 | import org.junit.Test; 5 | import org.junit.runner.RunWith; 6 | import org.springframework.boot.test.context.SpringBootTest; 7 | import org.springframework.test.context.junit4.SpringRunner; 8 | 9 | /** 10 | * @author: yaohw 11 | * @create: 2019-10-23 18:47 12 | **/ 13 | //@RunWith(SpringRunner.class) 14 | //@SpringBootTest 15 | @Log4j2 16 | public class BlogApplicationTest { 17 | 18 | @Test 19 | public void test() { 20 | 21 | } 22 | 23 | } 24 | --------------------------------------------------------------------------------