文章目录
Gitee地址 :文章些许混乱,以库为准😰
修改数据库ms_comment的articleId为bigInt,不然文章id会超出范围
前端项目运行命令
npm install
npm run build
npm run dev
创建一个项目,如果是平时开发的,首先都要导入如下依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.0</version>
<relativePath/>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
如果创建的项目报如下错误的话,请你降低SpringBoot版本
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2021-08-15 20:58:31.515 ERROR 4684 --- [ main] o.s.boot.SpringApplication : Application run failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'spring.sql.init-org.springframework.boot.autoconfigure.sql.init.SqlInitializationProperties': Lookup method resolution failed; nested exception is java.lang.IllegalStateException: Failed to introspect Class [org.springframework.boot.autoconfigure.sql.init.SqlInitializationProperties] from ClassLoader [sun.misc.Launcher$AppClassLoader@18b4aac2]
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.determineCandidateConstructors(AutowiredAnnotationBeanPostProcessor.java:289) ~[spring-beans-5.3.7.jar:5.3.7]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.determineConstructorsFromBeanPostProcessors(AbstractAutowireCapableBeanFactory.java:1284) ~[spring-beans-5.3.7.jar:5.3.7]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1201) ~[spring-beans-5.3.7.jar:5.3.7]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:564) ~[spring-beans-5.3.7.jar:5.3.7]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:524) ~[spring-beans-5.3.7.jar:5.3.7]
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335) ~[spring-beans-5.3.7.jar:5.3.7]
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-5.3.7.jar:5.3.7]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333) ~[spring-beans-5.3.7.jar:5.3.7]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208) ~[spring-beans-5.3.7.jar:5.3.7]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:944) ~[spring-beans-5.3.7.jar:5.3.7]
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:918) ~[spring-context-5.3.7.jar:5.3.7]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:583) ~[spring-context-5.3.7.jar:5.3.7]
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:145) ~[spring-boot-2.5.0.jar:2.5.0]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:758) [spring-boot-2.5.0.jar:2.5.0]
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:438) [spring-boot-2.5.0.jar:2.5.0]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:337) [spring-boot-2.5.0.jar:2.5.0]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1336) [spring-boot-2.5.0.jar:2.5.0]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1325) [spring-boot-2.5.0.jar:2.5.0]
at cn.mldn.admin.AdminApp.main(AdminApp.java:11) [classes/:na]
Caused by: java.lang.IllegalStateException: Failed to introspect Class [org.springframework.boot.autoconfigure.sql.init.SqlInitializationProperties] from ClassLoader [sun.misc.Launcher$AppClassLoader@18b4aac2]
at org.springframework.util.ReflectionUtils.getDeclaredMethods(ReflectionUtils.java:481) ~[spring-core-5.3.7.jar:5.3.7]
at org.springframework.util.ReflectionUtils.doWithLocalMethods(ReflectionUtils.java:321) ~[spring-core-5.3.7.jar:5.3.7]
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.determineCandidateConstructors(AutowiredAnnotationBeanPostProcessor.java:267) ~[spring-beans-5.3.7.jar:5.3.7]
... 18 common frames omitted
Caused by: java.lang.NoClassDefFoundError: org/springframework/boot/sql/init/DatabaseInitializationMode
at java.lang.Class.getDeclaredMethods0(Native Method) ~[na:1.8.0_241]
at java.lang.Class.privateGetDeclaredMethods(Class.java:2701) ~[na:1.8.0_241]
at java.lang.Class.getDeclaredMethods(Class.java:1975) ~[na:1.8.0_241]
at org.springframework.util.ReflectionUtils.getDeclaredMethods(ReflectionUtils.java:463) ~[spring-core-5.3.7.jar:5.3.7]
... 20 common frames omitted
Caused by: java.lang.ClassNotFoundException: org.springframework.boot.sql.init.DatabaseInitializationMode
at java.net.URLClassLoader.findClass(URLClassLoader.java:382) ~[na:1.8.0_241]
at java.lang.ClassLoader.loadClass(ClassLoader.java:418) ~[na:1.8.0_241]
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:355) ~[na:1.8.0_241]
at java.lang.ClassLoader.loadClass(ClassLoader.java:351) ~[na:1.8.0_241]
... 24 common frames omitted
一.资源下载和项目搭建
-
下载前端项目(这个的话,可以去QQ群下载) -
搭建项目(用idea创建项目) 1)idea create new xxxx在里面一系列的操作 2)导入依赖 a -----:parent和properties的导入 ```
<!--parent的解释https://blog.csdn.net/niceyoo/article/details/91852502-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.0</version>
<relativePath/>
</parent>
<!--定义一些属性的问题-->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
b--导入其他的依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.lum</groupId>
<artifactId>blog-parent</artifactId>
<version>1.0-SNAPSHOT</version>
<modules>
<module>blog</module>
</modules>
<packaging>pom</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.0</version>
<relativePath/>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.76</version>
</dependency>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.2</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/joda-time/joda-time -->
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.10.10</version>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
- 创建子模块blog-api
1)为什么创建子模块:方便以后分模块开发 2)导入依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>blog-parent</artifactId>
<groupId>com.lum</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>blog</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<!-- 排除 默认使用的logback -->
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- log4j2 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
<!--这个就是AOP的问题了撒,不用多说-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!--这个是邮箱的处理-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<!--这个导入进来就是进程一直在进行-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--JSON的格式问题了-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.76</version>
</dependency>
<!--mysql的操作了-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--可以看到json中信息-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<!--跟java.lang这个包的作用类似,Commons Lang这一组API也是提供一些基础的、通用的操作和处理,如自动生成toString()的结果、自动实现hashCode()和equals()方法、数组操作、枚举、日期和时间的处理等等。-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<!--StringUtils就是这个提供的,用来有时候验证什么是否为空呀-->
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.2</version>
</dependency>
<!--像Md5加密呀-->
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
</dependency>
<!--mybatis的配置-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.3</version>
</dependency>
<!--是关于Data注解的-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!--时间处理的类-->
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.10.10</version>
</dependency>
<!--验证-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<dependency>
<groupId>com.qiniu</groupId>
<artifactId>qiniu-java-sdk</artifactId>
<version>[7.7.0, 7.7.99]</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-extension</artifactId>
<version>3.4.2</version>
</dependency>
<dependency>
<groupId>commons-configuration</groupId>
<artifactId>commons-configuration</artifactId>
<version>1.10</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.9</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
<version>2.5.4</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot</artifactId>
<version>2.5.4</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.8</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.3.8</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.73</version>
</dependency>
</dependencies>
</project>
- 编写application.properties文件
server.port=8888
#配置项目名称
spring.application.name=blog
server.servlet.context-path=/api
#数据库的设置
spring.datasource.url=jdbc:mysql://localhost:3306/blog?useUnicode=true&characterEncoding=UTF-8&serverTimeZone=UTC
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#Mybaties-plus
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
mybatis-plus.global-config.db-config.table-prefix=ms_
- 编写启动类BlogApplication.java
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class BlogApplication {
public static void main(String[] args) {
SpringApplication.run(BlogApplication.class);
}
}
- 然后因为引入了上面的Mybatis-plus,所有有如下配置
在项目中创建config文件夹,然后创建MybatisplusConfig.java并且写如下代码:
package com.lum.blog.config;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@MapperScan("com.lum.blog.mapper")
public class MyBatiesPlusConfig {
}
- 启动BlogApplication测试
成功即可,可能会失败,大多是maven依赖关系没有对,仔细比对! - 在项目中,肯定要用到分页的,所有要用到Mybatis的分页插件。对MybatisPlusConfig做出如下修改:
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
mybatisPlusInterceptor.addInnerInterceptor( new PaginationInnerInterceptor());
return mybatisPlusInterceptor;
}
- 创建WebMmvConfig.java文件
这里先配置跨域的问题,因为前端和后端是分离了的,前端前端端口访问是跨域的,所有要配置跨域的问题。
package com.lum.blog.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedOrigins("http://localhost:8080");
}
}
😉😉😉😉😉😉😉😉😉😉😉😉😉😉😉😉😉😉😉😉😉😉😉😉😉😉😉😉😉😉😉😉😉😉😉😉😉😉😉😉😉😉😉😉😉😉😉😉😉😉😉😉😉😉😉😉😉😉😉😉😉😉😉😉😉😉😉😉😉😉😉😉
二、功能
1.首页文章列表页- - -1
1.1接口说明
接口url:/articles 请求方式:post请求 请求参数:
参数名称 | 参数类型 | 说明 |
---|
page | int | 当前页 | pagesize | int | 每页显示的数量 |
返回数据:
~~~json
{
"success": true,
"code": 200,
"msg": "success",
"data": [
{
"id": 1,
"title": "springboot介绍以及入门案例",
"summary": "通过Spring Boot实现的服务,只需要依靠一个Java类,把它打包成jar,并通过`java -jar`命令就可以运行起来。\r\n\r\n这一切相较于传统Spring应用来说,已经变得非常的轻便、简单。",
"commentCounts": 2,
"viewCounts": 54,
"weight": 1,
"createDate": "2021-06-26 15:58",
"author": "lum",
"body": null,
"tags": [
{
"id": 5,
"avatar": null,
"tagName": "444"
},
{
"id": 7,
"avatar": null,
"tagName": "22"
},
{
"id": 8,
"avatar": null,
"tagName": "11"
}
],
"categorys": null
},
{
"id": 9,
"title": "Vue.js 是什么",
"summary": "Vue (读音 /vju?/,类似于 view) 是一套用于构建用户界面的渐进式框架。",
"commentCounts": 0,
"viewCounts": 3,
"weight": 0,
"createDate": "2609-06-27 11:25",
"author": "12",
"body": null,
"tags": [
{
"id": 7,
"avatar": null,
"tagName": "22"
}
],
"categorys": null
},
{
"id": 10,
"title": "Element相关",
"summary": "本节将介绍如何在项目中使用 Element。",
"commentCounts": 0,
"viewCounts": 3,
"weight": 0,
"createDate": "2609-06-27 11:25",
"author": "12",
"body": null,
"tags": [
{
"id": 5,
"avatar": null,
"tagName": "444"
},
{
"id": 6,
"avatar": null,
"tagName": "33"
},
{
"id": 7,
"avatar": null,
"tagName": "22"
},
{
"id": 8,
"avatar": null,
"tagName": "11"
}
],
"categorys": null
}
]
}
1.2表结构 既然我们有了前端和页面要返回的数据,那我们的用户什么的,肯定都要和类有关系
返回数据的文章数据表
~~~sql
CREATE TABLE `blog`.`ms_article` (
`id` bigint(0) NOT NULL AUTO_INCREMENT,
`comment_counts` int(0) NULL DEFAULT NULL COMMENT '评论数量',
`create_date` bigint(0) NULL DEFAULT NULL COMMENT '创建时间',
`summary` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '简介',
`title` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '标题',
`view_counts` int(0) NULL DEFAULT NULL COMMENT '浏览数量',
`weight` int(0) NOT NULL COMMENT '是否置顶',
`author_id` bigint(0) NULL DEFAULT NULL COMMENT '作者id',
`body_id` bigint(0) NULL DEFAULT NULL COMMENT '内容id',
`category_id` int(0) NULL DEFAULT NULL COMMENT '类别id',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 25 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
~~~
~~~sql
CREATE TABLE `blog`.`ms_tag` (
`id` bigint(0) NOT NULL AUTO_INCREMENT,
`article_id` bigint(0) NOT NULL,
`tag_id` bigint(0) NOT NULL,
PRIMARY KEY (`id`) USING BTREE,
INDEX `article_id`(`article_id`) USING BTREE,
INDEX `tag_id`(`tag_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
~~~
~~~sql
CREATE TABLE `blog`.`ms_sys_user` (
`id` bigint(0) NOT NULL AUTO_INCREMENT,
`account` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '账号',
`admin` bit(1) NULL DEFAULT NULL COMMENT '是否管理员',
`avatar` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '头像',
`create_date` bigint(0) NULL DEFAULT NULL COMMENT '注册时间',
`deleted` bit(1) NULL DEFAULT NULL COMMENT '是否删除',
`email` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '邮箱',
`last_login` bigint(0) NULL DEFAULT NULL COMMENT '最后登录时间',
`mobile_phone_number` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '手机号',
`nickname` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '昵称',
`password` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '密码',
`salt` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '加密盐',
`status` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '状态',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 16 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
1.3 Dao开发 com.lum.blog.dao.pojo.Article.java
package com.lum.blog.dao.pojo;
import lombok.Data;
@Data
public class Article {
public static final int Article_TOP = 1;
public static final int Article_Common = 0;
private Long id;
private String title;
private String summary;
private int commentCounts;
private int viewCounts;
private Long authorId;
private Long bodyId;
private Long categoryId;
private int weight = Article_Common;
private Long createDate;
}
com.lum.blog.dao.pojo.SysUser.java
package com.lum.blog.dao.pojo;
import lombok.Data;
@Data
public class SysUser {
private Long id;
private String account;
private Integer admin;
private String avatar;
private Long createDate;
private Integer deleted;
private String email;
private Long lastLogin;
private String mobilePhoneNumber;
private String nickname;
private String password;
private String salt;
private String status;
}
com.lum.blog.dao.pojo.Tag.java
package com.lum.blog.dao.pojo;
import lombok.Data;
@Data
public class Tag {
private Long id;
private String avatar;
private String tagName;
}
1.4对应的Mapper的创建
package com.lum.blog.dao.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.lum.blog.dao.pojo.Article;
public interface ArticleMapper extends BaseMapper<Article> {
}
*******************************************************************
package com.lum.blog.dao.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.lum.blog.dao.pojo.SysUser;
public interface SysUserMapper extends BaseMapper<SysUser> {
}
*******************************************************************
package com.lum.blog.dao.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.lum.blog.dao.pojo.Tag;
public interface TagMapper extends BaseMapper<Tag> {
}
1.5mapper创建好了,该Controller了
创建ArticleController,这个代表的是文章类的控制器
package com.lum.blog.controller;
import com.lum.blog.vo.Result;
import com.lum.blog.vo.params.PageParams;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("articles")
public class ArticleController {
@PostMapping
public Result listArticle(@RequestBody PageParams pageParams){
return articleService.listArticle(pageParams);
}
}
1)这里就有补充了,我们传入的参数,是PageParms类代表,在 com.lum.Blog下面创建vo的目录,然后创建PageParms的类
package com.lum.blog.vo.params;
import lombok.Data;
@Data
public class PageParams {
private int page = 1;
private int pageSize = 10;
}
2)返回的数据Result的类
package com.lum.blog.vo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Result {
private boolean success;
private int code;
private String msg;
private Object data;
public static Result success(Object data) {
return new Result(true , 200, "success" ,data);
}
public static Result Fail(int code, String msg) {
return new Result(false , code, msg,null);
}
}
1)首先我们编写的DAO层和数据库里面的表名字要对应起来。 2)然后就是编写的XXX-Mapper层层要继承BaseMapper<xxx类名>。 3)然后就是我们一般不再编写他的xxxMapper.xml而是要配置的是 xxxService+xxxServiceImpl,如果要编写xxxMapper.xml和Mybatis一样的配置
1.6 service 从上面中,我们就可以看出,我们已经开发差不多了,但是想返回数据了,但是listArticle这个方法却没有Service来读取数据,所以来开发Service和数据读取的方法
1)首先来编写这个Service层,在src/main/java下面建立service文件夹并且在下面ArticleService文件和Impl文件夹
package com.lum.blog.service;
import com.lum.blog.vo.Result;
import com.lum.blog.vo.params.PageParams;
public interface ArticleService {
Result listArticle(PageParams pageParams);
}
2)再来编写他的Impl文件ArticleServiceImpl文件
package com.lum.blog.service.Impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.lum.blog.dao.mapper.ArticleMapper;
import com.lum.blog.dao.pojo.Article;
import com.lum.blog.service.ArticleService;
import com.lum.blog.vo.ArticleVo;
import com.lum.blog.vo.Result;
import com.lum.blog.vo.params.PageParams;
import org.joda.time.DateTime;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
@Service
public class ArticleServiceImpl implements ArticleService {
@Autowired
private ArticleMapper articleMapper;
@Override
public Result listArticle(PageParams pageParams) {
Page<Article> page = new Page<Article>(pageParams.getPage(),pageParams.getPageSize());
LambdaQueryWrapper<Article> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.orderByDesc(Article::getWeight,Article::getCreateDate);
Page<Article> articlePage = articleMapper.selectPage(page, queryWrapper);
List<Article> records = articlePage.getRecords();
List<ArticleVo> articleVoList = copyList(records);
return Result.success(articleVoList);
}
private List<ArticleVo> copyList(List<Article> records) {
List<ArticleVo> articleVoList = new ArrayList<>();
for (Article record : records) {
articleVoList.add(copy(record));
}
return articleVoList;
}
private ArticleVo copy(Article article) {
ArticleVo articleVo = new ArticleVo();
BeanUtils.copyProperties(article,articleVo);
articleVo.setCreateDate(new DateTime(article.getCreateDate()).toString("yyyy-MM-dd HH:mm"));
return articleVo;
}
}
其中需要创建ArticleVo和TagVo
package com.lum.blog.vo;
import lombok.Data;
import java.util.List;
@Data
public class ArticleVo {
private Long id;
private String title;
private String summary;
private int commentCounts;
private int ViewCounts;
private int weight;
private String createDate;
private String author;
private List<TagVo> tags;
}
package com.lum.blog.vo;
import lombok.Data;
@Data
public class TagVo {
private Long id;
private String tagName;
}
vo和页面交互的数据不应该和数据库映射对象进行耦合,最好分开
这里补充一下这个articleVo,这个类,因为平时我们开发出来的东西,到时候要 到数据库里面找数据嘛,然后找出来不一定一样,要把它一样的拷贝,不一样的返回null
总结:有了以上的配置之后,页面的内容就可以展示了
2. 首页文章列表页----2
问题引入:在之前开发的首页内容显示中,文章下面是没有标签,作者的信息等内容的,要开发下面有内容
2.1在ArticleServiceImpl中实现 思考:并不是所有的接口都需要标签,作者信息
增加两个boolean isTag,isAuthor来进行判断
在copyList中增加代码
private List<ArticleVo> copyList(List<Article> records,boolean isTag,boolean isAuthor) {
List<ArticleVo> articleVoList = new ArrayList<>();
for (Article record : records) {
articleVoList.add(copy(record,isTag,isAuthor));
}
return articleVoList;
}
在copy中增加代码
private ArticleVo copy(Article article,boolean isTag,boolean isAuthor) {
ArticleVo articleVo = new ArticleVo();
BeanUtils.copyProperties(article,articleVo);
articleVo.setCreateDate(new DateTime(article.getCreateDate()).toString("yyyy-MM-dd HH:mm"));
if (isTag) {
Long articleId = article.getId();
articleVo.setTags(tagService.findTagsByArticleId(articleId));
}
if (isAuthor) {
Long authorId = article.getAuthorId();
articleVo.setAuthor(sysUserService.findUserById(authorId).getNickname());
}
return articleVo;
}
2.2标签tag 编写TagService
package com.lum.blog.service;
import com.lum.blog.vo.TagVo;
import java.util.List;
public interface TagService {
List<TagVo> findTagsByArticleId(Long articleId);
}
编写实现类TagServiceImpl
package com.lum.blog.service.Impl;
import com.lum.blog.dao.mapper.TagMapper;
import com.lum.blog.dao.pojo.Tag;
import com.lum.blog.service.TagService;
import com.lum.blog.vo.TagVo;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
@Service
public class TagServiceImpl implements TagService {
@Autowired
private TagMapper tagMapper;
public TagVo copy(Tag tag){
TagVo tagVo = new TagVo();
BeanUtils.copyProperties(tag,tagVo);
return tagVo;
}
public List<TagVo> copyList(List<Tag> tagList) {
List<TagVo> tagVoList = new ArrayList<>();
for (Tag tag : tagList) {
tagVoList.add(copy(tag));
}
return tagVoList;
}
@Override
public List<TagVo> findTagsByArticleId(Long articleId) {
List<Tag> tags=tagMapper.findTagsByArticleId(articleId);
return copyList(tags);
}
}
编写TagMapper
package com.lum.blog.dao.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.lum.blog.dao.pojo.Tag;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface TagMapper extends BaseMapper<Tag> {
List<Tag> findTagsByArticleId(Long articleId);
}
在resourse下建立TagMapper.xml 路径与接口包一致(com.lum.blog.dao.mapper)
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.lum.blog.dao.mapper.TagMapper">
<!-- List<Tag> findTagsByArticleId(Long articleId);-->
<select id="findTagsByArticleId" parameterType="long" resultType="com.lum.blog.dao.pojo.Tag">
select id,avatar,tag_name as tagName from ms_tag
where id in
(select tag_id from ms_article_tag where article_id=#{articleId})
</select>
</mapper>
parameterType="long"对应List findTagsByArticleId(Long articleId)的articleId
select id,avatar,tag_name as tagName from ms_tag where id in (select tag_id from ms_article_tag where article_id=#{articleId}) 在关联表中查询标签的id,auatar,tagName
可以在application.properties中mybatis-plus开启驼峰命名
mybatis-plus.configuration.map-underscore-to-camel-case=true
这样SQL语句就不需要as别名。
接下来将TagService注入到ArticleImpl中实现
if (isTag) {
Long articleId = article.getId();
articleVo.setTags(tagService.findTagsByArticleId(articleId));
}
2.3作者author 建立接口SysUserService
package com.lum.blog.service;
import com.lum.blog.dao.pojo.SysUser;
public interface SysUserService {
SysUser findUserById(Long id);
}
编写实现类SysUserServiceImpl
package com.lum.blog.service.Impl;
import com.lum.blog.dao.mapper.SysUserMapper;
import com.lum.blog.dao.pojo.SysUser;
import com.lum.blog.service.SysUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class SysUserServiceImpl implements SysUserService {
@Autowired
private SysUserMapper sysUserMapper;
@Override
public SysUser findUserById(Long id) {
SysUser sysUser = sysUserMapper.selectById(id);
if (sysUser == null) {
sysUser = new SysUser();
sysUser.setNickname("鹿鸣");
}
return sysUser;
}
}
注入SysUserMapper,然后编写查询
将SysUserService注入到文章实现类ArticleServiceImpl中
if (isAuthor) {
Long authorId = article.getAuthorId();
articleVo.setAuthor(sysUserService.findUserById(authorId).getNickname());
}
在SysUserImpl中编写如果出现空的情况处理办法
public SysUser findUserById(Long id) {
SysUser sysUser = sysUserMapper.selectById(id);
if (sysUser == null) {
sysUser = new SysUser();
sysUser.setNickname("lum");
}
return sysUser;
}
在copyList中加入返回值istag,isauthor
List<ArticleVo> articleVoList = copyList(records,true,true);
for (Article record : records) {
articleVoList.add(copy(record,isTag,isAuthor));
}
在前方代码中已经有体现在哪加😁
进行测试
3.首页-最热标签
1.标签所拥有的文章数量最多
2.查询 根据tag_id分组,技术,从大到小排,取前limit个
3.1 接口说明 接口url:/tag/hot 请求方式:Get 请求参数:无 返回数据:
{
"successs":true
"code"200
"msg":"success"
"data"[
{
"id":1,
"tagName":"最热"
}
]
}
3.2编码 先写controller 创建TagsController
package com.lum.blog.controller;
import com.lum.blog.service.TagService;
import com.lum.blog.vo.Result;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("tags")
public class TagsController {
@Autowired
private TagService tagService;
@GetMapping("hot")
public Result hot() {
int limit =6;
return tagService.hots(limit);
}
}
在tagService实现hots方法
package com.lum.blog.service;
import com.lum.blog.vo.Result;
import com.lum.blog.vo.TagVo;
import java.util.List;
public interface TagService {
List<TagVo> findTagsByArticleId(Long articleId);
Result hots(int limit);
}
首先分析SQL语句
select tag_id
from ms_article_tag
group by tag_id
order by count(*) limit 2
TagServiceImpl实现 Result hots(int limit)
@Override
public Result hots(int limit) {
List<Long> tagIds= tagMapper.findHotsTagId(limit);
return null;
}
在TagMapper中创建findHostTagId(limit)方法
List<Long> findHotsTagId(int limit);
在资源文件TagMapper中生成findHotsTagId并添加==parameterType=“int”==属性以及sql语句
<select id="findHotsTagId" parameterType="int" resultType="java.lang.Long">
select tag_id
from ms_article_tag
group by tag_id
order by count(*) limit 6
</select>
这时查询出最热的tagId,需要根据tagid查询tagName,Tag 继续Result hot
@Override
public Result hots(int limit) {
List<Long> tagIds= tagMapper.findHotsTagId(limit);
if(CollectionUtils.isEmpty(tagIds)){
return Result.success(Collections.emptyList());
}
List<Tag> tagList= tagMapper.findTagsByIds(tagIds);
return Result.success(tagList);
}
所以需要在TagMapper.java中添加方法
List<Tag> findTagsByIds(List<Long> tagIds);
在资源TagMapper.xml中添加
<!-- List<Tag> findTagsByIds(List<Long> tagIds);-->
<select id="findTagsByIds" parameterType="list" resultType="com.lum.blog.dao.pojo.Tag">
select id,tag_name from ms_tag
where id in
<foreach collection="tagIds" item="tagId" separator="," open="(" close=")">
#{tagId}
</foreach>
</select>
3.3进行测试
4.统一异常处理
创建handler(com.lum.blog下.)包并创建AllExceptionHandler类
package com.lum.blog.handler;
import com.lum.blog.vo.Result;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
@ControllerAdvice
public class AllExceptionHandler {
@ExceptionHandler(Exception.class)
@ResponseBody
public Result doExceptionHandler(Exception e){
e.printStackTrace();
return Result.fail(-999,"系统异常");
}
}
在controller代码中加入错误代码,进行测试
5.首页-最热文章
5.1接口说明 接口url: /articles/hot 请求方式:POST 请求参数:
返回数据:
{
"success": true,
"code": 200,
"msg": "success",
"data": [
{
"id": 1,
"title": "springboot介绍以及入门案例",
},
{
"id": 2,
"title": "springboot介绍以及入门案例",
}
]
}
5.2 Controller 在ArticleController中增加PostMapper,返回最热的文章
@PostMapping("hot")
public Result hotArticle(){
int limit=5;
return articleService.hotArticle(limit);
}
5.3Service
在articleService创建对应的方法.hotArticle(limit);
Result hotArticle(int limit);
在articleServiceImpl中实现 Result hotArticle(int limit) 进行sql查询并copyList返回
@Override
public Result hotArticle(int limit) {
LambdaQueryWrapper<Article> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.orderByDesc(Article::getViewCounts);
queryWrapper.select(Article::getId,Article::getTitle);
queryWrapper.last("limit"+limit);
List<Article> articles = articleMapper.selectList(queryWrapper);
return Result.success(copyList(articles,false,false));
}
5.4进行测试
6.首页-最新文章
6.1接口说明 接口url: /articles/new 请求方式:POST 请求参数:
返回数据:
{
"success": true,
"code": 200,
"msg": "success",
"data": [
{
"id": 1,
"title": "springboot介绍以及入门案例",
},
{
"id": 2,
"title": "springboot介绍以及入门案例",
}
]
}
6.2 Controller 在ArticleController中增加PostMapper,返回最热的文章
@PostMapping("new")
public Result newArticle(){
int limit=5;
return articleService.newArticle(limit);
}
6.3Service
在articleService创建对应的方法.hotArticle(limit);
Result newArticle(int limit);
在articleServiceImpl中实现 Result hotArticle(int limit) 进行sql查询并copyList返回
@Override
public Result newArticle(int limit) {
LambdaQueryWrapper<Article> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.orderByDesc(Article::getCreateDate);
queryWrapper.select(Article::getId,Article::getTitle);
queryWrapper.last("limit"+limit);
List<Article> articles = articleMapper.selectList(queryWrapper);
return Result.success(copyList(articles,false,false));
}
6.4 进行测试
7.首页-文章归档
文章根据日期的年月进行归档处理 因为数据库的create_date为bigint类型需要/1000得到时间戳,再经过FROM_UNIXTIME进行格式转换 SQL语句为
select year(FROM_UNIXTIME(create_date/1000)) year,month(FROM_UNIXTIME(create_date/1000)) month, count(*) count from ms_article group by year,month;
7.1 接口说明 接口url: /articles/listArtchives 请求方式:POST 请求参数:
返回数据:
{
"success": true,
"code": 200,
"msg": "success",
"data": [
{
"year": "2021",
"mouth": "6",
"count"2
}
]
}
7.2 Controller ArticleController类
@PostMapping("listArchives")
public Result listArchives(){
return articleService.listArchives();
}
7.3 Service
ArticleService
Result listArchives();
在ArticleServiceImpl中实现
@Override
public Result listArchives() {
List<Archives> archivesList = articleMapper.listArchives();
return Result.success(archivesList);
}
7.4 mapper
因为文章归档返回的数据不是数据库的直接数据,临时使用,不属于pojo对象 所以创建dos包存放非持久化数据 创建Archives类归档信息
package com.lum.blog.dao.dos;
import lombok.Data;
@Data
public class Archives {
private Integer year;
private Integer month;
private Long count;
}
创建Articlemapper
package com.lum.blog.dao.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.lum.blog.dao.dos.Archives;
import com.lum.blog.dao.pojo.Article;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface ArticleMapper extends BaseMapper<Article> {
List<Archives> listArchives();
}
在资源包mapper创建ArticleMapper.xml实现listArchives()
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.lum.blog.dao.mapper.ArticleMapper">
<select id="listArchives" resultType="com.lum.blog.dao.dos.Archives">
select year(FROM_UNIXTIME(create_date/1000)) year,month(FROM_UNIXTIME(create_date/1000)) month, count(*) count
from ms_article
group by year,month;
</select>
</mapper>
7.5 进行测试
8 登录功能的实现(JWT)
8.1接口说明 接口url:/login
请求方式:POST
请求参数:
参数名称 | 参数类型 | 说明 |
---|
account | string | 账号 | password | string | 密码 |
返回的数据
{
"success": true,
"code": 200,
"msg": "success",
"data": "token"
}
8.2JWT技术实现 JSON Web Token (JWT),它是目前最流行的跨域身份验证解决方案
JWT的精髓在于:“去中心化”,数据是保存在客户端的。
jwt可以生成一个加密token,作为用户登陆的令牌,当用户登陆成功后,发放给客户端. 请求需要登陆的资源和接口时,将token携带,后端验证token是否合法.
jwt有三部分组成:
-
Header,{“type”:“JWT”,“alg”:“HS256”}固定 -
playload,存放自定义信息 比如,用户id,过期时间等,可以被解密,不能存放敏感信息 -
签证 前两点加上密钥加密组成,只要密钥不丢失,可以认为是安全的 (alg:HS256是算法)
jwt验证,主要就是验证签证部分,是否合法
导入依赖包
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
创建工具类JWTUtils
package com.lum.blog.utils;
import io.jsonwebtoken.Jwt;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
public class JWTUtils {
private static final String jwtToken = "12345Lum!@#$%";
public static String createToken(Long userId){
Map<String, Object> claims = new HashMap<>();
claims.put("userId",userId);
JwtBuilder jwtBuilder = Jwts.builder()
.signWith(SignatureAlgorithm.HS256,jwtToken)
.setClaims(claims)
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + 24 * 60 * 60 *1000));
return jwtBuilder.compact();
}
public static Map<String,Object> checkToken(String token){
try {
Jwt parser = Jwts.parser().setSigningKey(jwtToken).parse(token);
return (Map<String, Object>) parser.getBody();
}catch ( Exception e) {
e.printStackTrace();
}
return null;
}
public static void main(String[] args) {
String token = JWTUtils.createToken(100L);
System.out.println(token);
Map<String, Object> map = JWTUtils.checkToken(token);
System.out.println(map.get("userId"));
}
}
8.3Controller 在controller层创建LoginController进行登陆控制
package com.lum.blog.controller;
import com.lum.blog.service.LoginService;
import com.lum.blog.vo.Result;
import com.lum.blog.vo.params.LoginParam;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/login")
public class LoginController {
@Autowired
private LoginService loginService;
@PostMapping
public Result login(@RequestBody LoginParam loginParam){
return loginService.login(loginParam);
}
}
需要在Service层编写业务
8.4Service
创建LoginService编写业务
package com.lum.blog.service;
import com.lum.blog.vo.Result;
import com.lum.blog.vo.params.LoginParam;
public interface LoginService {
Result login(LoginParam loginParam);
}
在vo包的param参数包编写登录用到的参数登录参数
在LoginServiceImpl中实现login方法
package com.lum.blog.service.Impl;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.lum.blog.dao.pojo.SysUser;
import com.lum.blog.service.LoginService;
import com.lum.blog.service.SysUserService;
import com.lum.blog.utils.JWTUtils;
import com.lum.blog.vo.ErrorCode;
import com.lum.blog.vo.Result;
import com.lum.blog.vo.params.LoginParam;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;
@Service
public class LoginServiceImpl implements LoginService {
@Autowired
private SysUserService sysUserService;
@Autowired
private RedisTemplate<String,String> redisTemplate;
private static final String salt="lum!@#";
@Override
public Result login(LoginParam loginParam) {
String account = loginParam.getAccount();
String password = loginParam.getPassword();
if (StringUtils.isBlank(account)||StringUtils.isBlank(password)) {
return Result.fail(ErrorCode.PARAMS_ERROR.getCode(),ErrorCode.PARAMS_ERROR.getMsg());
}
password = DigestUtils.md5Hex(password + salt);
SysUser sysUser = sysUserService.findUser(account,password);
if (sysUser == null) {
return Result.fail(ErrorCode.ACCOUNT_PWD_NOT_EXIST.getCode(), ErrorCode.ACCOUNT_PWD_NOT_EXIST.getMsg());
}
String token = JWTUtils.createToken(sysUser.getId());
redisTemplate.opsForValue().set("TOKEN_"+token, JSON.toJSONString(sysUser),1, TimeUnit.DAYS);
return Result.success(token);
}
}
在这里先判断用户名或者密码是否为空,空的话返回统一错误码
if (StringUtils.isBlank(account)||StringUtils.isBlank(password)) {
return Result.fail(ErrorCode.PARAMS_ERROR.getCode(),ErrorCode.PARAMS_ERROR.getMsg());
}
再对密码进行加盐处理
private static final String salt="lum!@#";
password = DigestUtils.md5Hex(password + salt);
需要用到redis来作缓存和数据库的中介 进行application.properties中redis配置 设置token和过期时间
redisTemplate.opsForValue().set("TOKEN_"+token, JSON.toJSONString(sysUser),1, TimeUnit.DAYS);
8.5,登录参数,redis配置,统一错误码
LoginParam登录参数
package com.lum.blog.vo.params;
import lombok.Data;
@Data
public class LoginParam {
private String account;
private String password;
}
redis配置
#redis配置
spring.redis.host=localhost
spring.redis.port=6379
统一错误码
在vo包下创建ErrorCode
package com.lum.blog.vo;
public enum ErrorCode {
PARAMS_ERROR(10001,"参数有误"),
ACCOUNT_PWD_NOT_EXIST(10002,"用户密码不存在喔!"),
TOKEN_ERROR(10003,"Token不合法"),
ACCOUNT_EXIST(10004,"账号已存在"),
NO_PERMISSION(70001,"无访问权限"),
SESSION_TIME_OUT(90001,"会话超时"),
NO_LOGIN(90002,"未登录"),;
private int code;
private String msg;
ErrorCode(int code, String msg) {
this.code = code;
this.msg = msg;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
}
8.6 进行测试
使用postman进行测试,因为登录后,需要跳转页面,进行token认证,有接口未完成,换端会出问题, token前端获取到之后,会存储storage中b5,本地存储
redis-ci中使用 key * 也可查询token
9.登录后获取用户信息
9.1接口说明 接口url:/users/currentUser 请求方式:Get 请求参数:
参数名称 | 参数类型 | 说明 |
---|
AuthorZation | string | 头部信息(Token) |
9.2 Controller 创建UserController进行用户信息返回
package com.lum.blog.controller;
import com.lum.blog.service.SysUserService;
import com.lum.blog.vo.Result;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("users")
public class UserController {
@Autowired
private SysUserService sysUserService;
@GetMapping("currentUser")
public Result currentUser(@RequestHeader("Authorization") String token) {
return sysUserService.findUserByToken(token);
}
}
9.3 Service 在SysUserService接口添加findUserByToken(String token)方法
Result findUserByToken(String token);
在实现类中编写findUserByToken(String token),需要在vo包创建LoginUserVo
@Override
public Result findUserByToken(String token) {
SysUser sysUser = loginService.checkToken(token);
if (sysUser == null) {
return Result.fail(ErrorCode.TOKEN_ERROR.getCode(),ErrorCode.TOKEN_ERROR.getMsg());
}
LoginUserVo loginUserVo = new LoginUserVo();
loginUserVo.setId(sysUser.getId());
loginUserVo.setNickname(sysUser.getNickname());
loginUserVo.setAccount(sysUser.getAccount());
loginUserVo.setAvatar(sysUser.getAvatar());
return Result.success(loginUserVo);
}
LoginUserVo
package com.lum.blog.vo;
import lombok.Data;
@Data
public class LoginUserVo {
private Long id;
private String account;
private String nickname;
private String avatar;
}
编写LoginService接口添加checkToken检查Token是否为空,不为空继续判断redis中userJson是否为空,不为空返回user对象
SysUser checkToken(String token);
在LoginServiceImpl中实现方法checkToken
@Override
public SysUser checkToken(String token) {
if (StringUtils.isBlank(token)){
return null;
}
Map<String, Object> checkToken = JWTUtils.checkToken(token);
if (checkToken == null) {
return null;
}
String userJson = redisTemplate.opsForValue().get("TOKEN_" + token);
if(StringUtils.isBlank(userJson)){
return null;
}
SysUser sysUser = JSON.parseObject(userJson, SysUser.class);
return sysUser;
}
在SysUserServiceImpl中添加查找用户
@Override
public SysUser findUser(String account, String password) {
LambdaQueryWrapper<SysUser> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(SysUser::getAccount,account);
queryWrapper.eq(SysUser::getPassword,password);
queryWrapper.select(SysUser::getAccount,SysUser::getId,SysUser::getAvatar,SysUser::getNickname);
queryWrapper.last("limit 1");
return sysUserMapper.selectOne(queryWrapper);
}
9.4进行测试
10 退出登录
前端清除token,后端清除redis中数据 10.1接口说明 接口url:/logout 请求方式:Get 请求参数:
参数名称 | 参数类型 | 说明 |
---|
AuthorZation | string | 头部信息(Token) |
10.2 Controller 创建LogoutController
package com.lum.blog.controller;
import com.lum.blog.service.LoginService;
import com.lum.blog.vo.Result;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/logout")
public class LogoutController {
@Autowired
private LoginService loginService;
@GetMapping
public Result logout(@RequestHeader("Authorization") String token){
return loginService.logout(token);
}
}
10.3Service 在LoginService接口中创建logout方法
Result logout(String token);
在实现类中完成方法 将redis中的token删除即可
@Override
public Result logout(String token) {
redisTemplate.delete("TOKEN_" + token);
return Result.success(null);
}
11.注册
sso(single sign on) 单点登录,后期把登录注册提出去,单独服务,可以独立提供接口
11.1接口说明 接口url:/register
请求方式:POST
请求参数:
参数名称 | 参数类型 | 说明 |
---|
account | string | 账号 | password | string | 密码 | nickname | string | 昵称 |
返回的数据
{
"success": true,
"code": 200,
"msg": "success",
"data": "token"
}
11.2 Controller 创建RegisterController
package com.lum.blog.controller;
import com.lum.blog.service.LoginService;
import com.lum.blog.vo.Result;
import com.lum.blog.vo.params.LoginParam;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("register")
public class RegisterController {
@Autowired
private LoginService loginService;
@PostMapping
public Result register(@RequestBody LoginParam loginParam) {
return loginService.register(loginParam);
}
}
11.3 Service LoginParam增加属性nickname
在LoginService接口中增加方法
private String nickname;
Result register(LoginParam loginParam)
在LoginServiceImpl中实现register方法
@Override
public Result register(LoginParam loginParam) {
String account = loginParam.getAccount();
String password = loginParam.getPassword();
String nickname = loginParam.getNickname();
if (StringUtils.isBlank(account)
||StringUtils.isBlank(nickname)
||StringUtils.isBlank(password)
){
return Result.fail(ErrorCode.PARAMS_ERROR.getCode(),ErrorCode.PARAMS_ERROR.getMsg());
}
SysUser sysUser = sysUserService.findUserByAccount(account);
if (sysUser != null) {
return Result.fail(ErrorCode.ACCOUNT_EXIST.getCode(), "账号已经被用了欧");
}
sysUser=new SysUser();
sysUser.setAccount(account);
sysUser.setNickname(nickname);
sysUser.setPassword(DigestUtils.md5Hex(password+salt));
sysUser.setCreateDate(System.currentTimeMillis());
sysUser.setLastLogin(System.currentTimeMillis());
sysUser.setAvatar("/static/img/logo.b3a48c0.png");
sysUser.setAdmin(1);
sysUser.setDeleted(0);
sysUser.setSalt("");
sysUser.setStatus("");
sysUser.setEmail("");
this.sysUserService.save(sysUser);
String token = JWTUtils.createToken(sysUser.getId());
redisTemplate.opsForValue().set("TOKEN_"+token, JSON.toJSONString(sysUser),1, TimeUnit.DAYS);
return Result.success(token);
}
因为需要查找账户和将账户写入数据库,需要在SysUserService新增方法
SysUser findUserByAccount(String account);
void save(SysUser sysUser);
实现findUserByAccount(String account)
@Override
public SysUser findUserByAccount(String account) {
LambdaQueryWrapper<SysUser> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(SysUser::getAccount,account);
queryWrapper.last("limit 1");
return this.sysUserMapper.selectOne(queryWrapper);
}
实现save(SysUser sysUser)
public void save(SysUser sysUser) {
this.sysUserMapper.insert(sysUser);
}
开启数据库事务 在SysUserServiceImpl开启 @Transactional
11.4进行测试
12 登录拦截器
每次访问需要登录的资源的时候,都需要代码中进行判断,一旦登录逻辑有所改变,代码都得进行改变,不合适 那么可以统一进行登录判断么 可以,使用拦截器,进行登录拦截,如果遇到登录需求才能访问的接口,拦截器直接返回,并跳转登录页面.
1. 拦截器的实现有多种方式(SpringSecurity,继承HandlerInterceptor,还有shiro
都可以实现),这里我们选择的是继承HandlerInterceptor
2. 这种方式有两步,第一编写继承类,第二步在webMVC里面配置即可
12.1 拦截器实现
package com.lum.blog.handler;
import com.alibaba.fastjson.JSON;
import com.lum.blog.dao.pojo.SysUser;
import com.lum.blog.service.LoginService;
import com.lum.blog.vo.ErrorCode;
import com.lum.blog.vo.Result;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {
@Autowired
private LoginService loginService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (!(handler instanceof HandlerMethod)) {
return true;
}
得去拿Token,为什么这样呢,因为前端传东西过来的时候是,我们用@RequestHeader("Authorization") 传过来的
String token = request.getHeader("Authorization");
log.info("=============request start=================");
String requestURI = request.getRequestURI();
log.info("request uri:{}",requestURI);
log.info("request method:{}",request.getMethod());
log.info("token:{}",token);
log.info("=============request end===================");
if(StringUtils.isBlank(token)){
Result result = Result.fail(ErrorCode.NO_LOGIN.getCode(),ErrorCode.NO_LOGIN.getMsg());
response.setContentType("application/json;charset=utf8");
response.getWriter().print(JSON.toJSONString(result));
return false;
}
SysUser sysUser = loginService.checkToken(token);
if (sysUser == null){
Result result = Result.fail(ErrorCode.NO_LOGIN.getCode(),ErrorCode.NO_LOGIN.getMsg());
response.setContentType("application/json;charset=utf8");
response.getWriter().print(JSON.toJSONString(result));
return false;
}
return true;
}
}
12.2 配置拦截路径 WebMvcConfig中配置拦截路径
@Autowired
private LoginInterceptor loginInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginInterceptor)
.addPathPatterns("/test");
}
13. ThreadLocal保存用户信息
想在controller中直接获取用户信息怎么获取?
什么是ThreadLocal
**ThreadLocal是什么**
从名字我们就可以看到ThreadLocal叫做线程变量,意思是ThreadLocal中填充的变量属于当前线程,该变量对其他线程而言是隔离的。ThreadLocal为变量在每个线程中都创建了一个副本,那么每个线程可以访问自己内部的副本变量。
从字面意思来看非常容易理解,但是从实际使用的角度来看,就没那么容易了,作为一个面试常问的点,使用场景那也是相当的丰富:
1、在进行对象跨层传递的时候,使用ThreadLocal可以避免多次传递,打破层次间的约束。
2、线程间数据隔离
3、进行事务操作,用于存储线程事务信息。
4、数据库连接,Session会话管理。
13.1使用ThreadLocal 现在utils包下创建UserThreadLocal类
package com.lum.blog.utils;
import com.lum.blog.dao.pojo.SysUser;
public class UserThreadLocal {
private UserThreadLocal (){
}
private static final ThreadLocal<SysUser> LOCAL = new ThreadLocal<>();
public static void put(SysUser sysUser){
LOCAL.set(sysUser);
}
public static SysUser get(){
return LOCAL.get();
}
public static void remove() {
LOCAL.remove();
}
}
既然是保存用户信息 对LoginIntercept修改,既然在这里验证,就在这里进行添加
package com.lum.blog.handler;
import com.alibaba.fastjson.JSON;
import com.lum.blog.dao.pojo.SysUser;
import com.lum.blog.service.LoginService;
import com.lum.blog.utils.UserThreadLocal;
import com.lum.blog.vo.ErrorCode;
import com.lum.blog.vo.Result;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {
@Autowired
private LoginService loginService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (!(handler instanceof HandlerMethod)) {
return true;
}
得去拿Token,为什么这样呢,因为前端传东西过来的时候是,我们用@RequestHeader("Authorization") 传过来的
String token = request.getHeader("Authorization");
log.info("=============request start=================");
String requestURI = request.getRequestURI();
log.info("request uri:{}",requestURI);
log.info("request method:{}",request.getMethod());
log.info("token:{}",token);
log.info("=============request end===================");
if(StringUtils.isBlank(token)){
Result result = Result.fail(ErrorCode.NO_LOGIN.getCode(),ErrorCode.NO_LOGIN.getMsg());
response.setContentType("application/json;charset=utf8");
response.getWriter().print(JSON.toJSONString(result));
return false;
}
SysUser sysUser = loginService.checkToken(token);
if (sysUser == null){
Result result = Result.fail(ErrorCode.NO_LOGIN.getCode(),ErrorCode.NO_LOGIN.getMsg());
response.setContentType("application/json;charset=utf8");
response.getWriter().print(JSON.toJSONString(result));
return false;
}
UserThreadLocal.put(sysUser);
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
UserThreadLocal.remove();
}
}
记得在最后清除信息以免内存泄漏
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
UserThreadLocal.remove();
}
13.2 进行测试 在TsetCroller中进行测试
package com.lum.blog.controller;
import com.lum.blog.dao.pojo.SysUser;
import com.lum.blog.utils.UserThreadLocal;
import com.lum.blog.vo.Result;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("test")
public class TestController {
@RequestMapping
public Result test(){
SysUser sysUser = UserThreadLocal.get();
System.out.println(sysUser);
return Result.success(null);
}
}
13.3ThreadLocal(本地的线程)到底有什么用
- 这样说吧,就比如我们的一个请求,当你启动某一个进程的时候,你让他和你对应的进程进行绑定的话,会深入的绑定到一起(以达到绑定用户信息的目的)。
- 为什么在那个后面一定要删除,因为一旦内存泄漏是很严重的
这要知道的是一个线程可以存在多个ThreadLocal
每一个Thread维护一个ThreadLocalMap, key为使用**弱引用**的ThreadLocal实例,
value为线程变量的副本。
**强引用**,使用最普遍的引用,一个对象具有强引用,不会被垃圾回收器回收。当内存
空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不回收这
种对象。
**如果想取消强引用和某个对象之间的关联,可以显式地将引用赋值为null,这样可以使
JVM在合适的时间就会回收该对象。**
**弱引用**,JVM进行垃圾回收时,无论内存是否充足,都会回收被弱引用关联的对象。
在java中,用java.lang.ref.WeakReference类来表示。
上面的那个 key为使用**弱引用**的ThreadLocal实例,当我们的线程中的那个
ThreadLocal被垃圾回收机制干掉之后,是不是这个弱引用的Key不存在了,但是这个是
Map集合呀,Value会永远的存在,所有要手动的删除
14.文章详情
14.1 接口说明 接口url:/articles/view/(id) 请求方式:POST 请求参数:
参数名称 | 参数类型 | 说明 |
---|
id | long | 文章id(路径参数) |
返回的数据
{
"success": true,
"code": 200,
"msg": "success",
"data": "token"
}
14.2 涉及的表及对应的pojo
CREATE TABLE `blog`.`ms_article_body` (
`id` bigint(0) NOT NULL AUTO_INCREMENT,
#这个是文章内容
`content` longtext CHARACTER SET utf8 COLLATE utf8_general_ci NULL,
#文章内容页面
`content_html` longtext CHARACTER SET utf8 COLLATE utf8_general_ci NULL,
`article_id` bigint(0) NOT NULL,
PRIMARY KEY (`id`) USING BTREE,
INDEX `article_id`(`article_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 38 CHARACTER SET = utf8 COLLATE = utf8_general_ci
ROW_FORMAT = Dynamic;
ArticleBody
package com.lum.blog.dao.pojo;
import lombok.Data;
@Data
public class ArticleBody {
private Long id;
private String content;
private String contentHtml;
private Long articleId;
}
类别表
CREATE TABLE `blog`.`ms_category` (
`id` bigint(0) NOT NULL AUTO_INCREMENT,
#分类的图标
`avatar` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,
#分类图标的名字
`category_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,
`description` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 6 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
Category
package com.lum.blog.dao.pojo;
import lombok.Data;
@Data
public class Category {
private Long id;
private String avatar;
private String categoryName;
private String description;
}
14.3Controller 在ArticleConller中添加方法查找文章
@PostMapping("view/{id}")
public Result findArticleById(@PathVariable("id") Long articleId){
return articleService.findArticleById(articleId);
}
14.4 Service 返回文章的哪些内容需要用到articleVo中添加ArticleBodyVo和CategoryVo
package com.lum.blog.vo;
import lombok.Data;
import java.util.List;
@Data
public class ArticleVo {
private Long id;
private String title;
private String summary;
private int commentCounts;
private int ViewCounts;
private int weight;
private String createDate;
private String author;
private ArticleBodyVo body;
private List<TagVo> tags;
private CategoryVo category;
}
package com.lum.blog.vo;
import lombok.Data;
@Data
public class CategoryVo {
private Long id;
private String avatar;
private String categoryName;
private String description;
}
在articleService中添加查找文章方法接口
Result findArticleById(Long articleId);
以下全在ArticleServiceImpl中编写
在articleServiceImpl中实现方法
@Override
public Result findArticleById(Long articleId) {
Article article = this.articleMapper.selectById(articleId);
ArticleVo articleVo = copy(article, true, true,true,true);
return Result.success(articleVo);
}
private ArticleBodyVo findArticleBodyById(Long bodyId) {
ArticleBody articleBody = articleBodyMapper.selectById(bodyId);
ArticleBodyVo articleBodyVo = new ArticleBodyVo();
articleBodyVo.setContent(articleBody.getContent());
return articleBodyVo;
}
想要显示文章部分内容就要根据文章找到详情
想要显示内容需要ArticleVo 重载cpoyList可以根据参数显示不同文章内容
private List<ArticleVo> copyList(List<Article> records,boolean isTag,boolean isAuthor) {
List<ArticleVo> articleVoList = new ArrayList<>();
for (Article record : records) {
articleVoList.add(copy(record,isTag,isAuthor,false,false));
}
return articleVoList;
}
private List<ArticleVo> copyList(List<Article> records,boolean isTag,boolean isAuthor,boolean isBody,boolean isCategory) {
List<ArticleVo> articleVoList = new ArrayList<>();
for (Article record : records) {
articleVoList.add(copy(record,isTag,isAuthor,isBody,isCategory));
}
return articleVoList;
}
文章articleVo显示 copyList重载后的copy方法
private ArticleVo copy(Article article,boolean isTag,boolean isAuthor,boolean isBody,boolean isCategory) {
ArticleVo articleVo = new ArticleVo();
BeanUtils.copyProperties(article,articleVo);
articleVo.setCreateDate(new DateTime(article.getCreateDate()).toString("yyyy-MM-dd HH:mm"));
if (isTag) {
Long articleId = article.getId();
articleVo.setTags(tagService.findTagsByArticleId(articleId));
}
if (isAuthor) {
Long authorId = article.getAuthorId();
articleVo.setAuthor(sysUserService.findUserById(authorId).getNickname());
}
if(isBody){
Long bodyId = article.getBodyId();
articleVo.setBody(findArticleBodyById(bodyId));
}
if(isCategory){
Long categoryId = article.getCategoryId();
articleVo.setCategory(categoryService.findCategoryById(categoryId));
}
return articleVo;
}
需要创建CategoryService
package com.lum.blog.service;
import com.lum.blog.vo.CategoryVo;
import com.lum.blog.vo.Result;
public interface CategoryService {
CategoryVo findCategoryById(Long categoryId);
Result findAll();
Result findAllDetail();
Result categoryDetailById(Long id);
}
实现CategoryServiceImpl
package com.lum.blog.service.Impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.lum.blog.dao.mapper.CategoryMapper;
import com.lum.blog.dao.pojo.Category;
import com.lum.blog.service.CategoryService;
import com.lum.blog.vo.CategoryVo;
import com.lum.blog.vo.Result;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
@Service
public class CategoryServiceImpl implements CategoryService {
@Autowired
private CategoryMapper categoryMapper;
@Override
public CategoryVo findCategoryById(Long categoryId) {
Category category = categoryMapper.selectById(categoryId);
CategoryVo categoryVo = new CategoryVo();
BeanUtils.copyProperties(category, categoryVo);
return categoryVo;
}
不要忘记注入关系
@Autowired
private ArticleMapper articleMapper;
@Autowired
private TagService tagService;
@Autowired
private SysUserService sysUserService;
@Autowired
private CategoryService categoryService;
@Autowired
private ArticleBodyMapper articleBodyMapper;
想要文章体需要添加articleBodyMapper
package com.lum.blog.dao.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.lum.blog.dao.pojo.ArticleBody;
public interface ArticleBodyMapper extends BaseMapper<ArticleBody> {
}
14.5 进行测试
15. 阅读数的更新
线程池的使用 问题介绍
@Override
public ArticleVo findArticleById(Long id) {
Article article = articleMapper.selectById(id);
return copy(article,true,true,true,true);
}
线程池的配置 com.lum.blog.config下新建ThreadPoolConfig开启线程池并进行相关配置
package com.lum.blog.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
@Configuration
@EnableAsync
public class ThreadPoolConfig {
@Bean("taskExecutor")
public Executor asyncServiceExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(20);
executor.setQueueCapacity(Integer.MAX_VALUE);
executor.setKeepAliveSeconds(60);
executor.setThreadNamePrefix("Lum博客");
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.initialize();
return executor;
}
}
15.1 Controller 属于文章业务,无controller 15.2 Service
在com.lum.blog.service.Impl下新建ThreadService完成线程池的使用
package com.lum.blog.service.Impl;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.lum.blog.dao.mapper.ArticleMapper;
import com.lum.blog.dao.pojo.Article;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
@Component
public class ThreadService {
@Async("taskExecutor")
public void updateArticleViewCount(ArticleMapper articleMapper, Article article) {
int viewCounts = article.getViewCounts();
Article articleUpdate = new Article();
articleUpdate.setViewCounts(viewCounts+1);
LambdaUpdateWrapper<Article> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.eq(Article::getId,article.getId());
updateWrapper.eq(Article::getViewCounts,viewCounts);
articleMapper.update(articleUpdate, updateWrapper);
try {
Thread.sleep(2000);
System.out.println("更新完成!");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
这是会出现一个bug 更新阅次数时会吧评论数更新为0 这是因为在Article的pojo与数据库的关系映中,int基本类型的默认值为0,会影响sql语句 所以在pojo中不要使用基本类型
package com.lum.blog.dao.pojo;
import lombok.Data;
@Data
public class Article {
public static final Integer Article_TOP = 1;
public static final Integer Article_Common = 0;
private Long id;
private String title;
private String summary;
private Integer commentCounts;
private Integer viewCounts;
private Long authorId;
private Long bodyId;
private Long categoryId;
private Integer weight;
private Long createDate;
}
16 评论列表
16.1 接口说明 接口url:/comments/article/(id) 请求方式:GET 请求参数:
参数名称 | 参数类型 | 说明 |
---|
id | long | 文章id(路径参数) |
返回的数据
{
"success": true,
"code": 200,
"msg": "success",
"data": [
{
"id":12,
"author":{
"nickname":"赵云",
"avatar":"",
"id":1
},
"content":"111"
"childrens":[],
"createDate":"2021-9-1 08:35",
"level":2,
"toUser":{
"id":12,
"nickname":"赵云",
"id":1
}
}
],
"createDate":"2021-9-1 08:35",
"level":1,
"toUser":null
}
]
}
新建pojo包下Comment映射关系
package com.lum.blog.dao.pojo;
import lombok.Data;
@Data
public class Comment {
private Long id;
private String content;
private Long createDate;
private Long articleId;
private Long authorId;
private Long parentId;
private Long toUid;
private Integer level;
}
创建两个Vo用来显示
CommentVo
package com.lum.blog.vo;
import lombok.Data;
import java.util.List;
@Data
public class CommentVo {
private Long id;
private UserVo author;
private String content;
private List<CommentVo> childrens;
private String createDate;
private Integer level;
private UserVo toUser;
}
UserVo
package com.lum.blog.vo;
import lombok.Data;
@Data
public class UserVo {
private String nickname;
private String avatar;
private Long id;
}
16.2 Controller 创建CommentController
package com.lum.blog.controller;
import com.lum.blog.service.CommentsService;
import com.lum.blog.vo.Result;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("comments")
public class CommentController {
@Autowired
private CommentsService commentsService;
@GetMapping("article/{id}")
public Result findArticleById(@PathVariable("id") Long articleId){
return commentsService.commentsByArticleId(articleId);
}
}
16.3 Service 创建接口CommentService
package com.lum.blog.service;
import com.lum.blog.vo.Result;
public interface CommentsService {
Result commentsByArticleId(Long articleId);
}
CommentServiceImpl实现方法
package com.lum.blog.service.Impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.lum.blog.dao.mapper.CommentMapper;
import com.lum.blog.dao.pojo.Comment;
import com.lum.blog.service.CommentsService;
import com.lum.blog.service.SysUserService;
import com.lum.blog.vo.CommentVo;
import com.lum.blog.vo.Result;
import com.lum.blog.vo.UserVo;
import org.joda.time.DateTime;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
@Service
public class CommentsServiceImpl implements CommentsService {
@Autowired
private CommentMapper commentMapper;
@Autowired
private SysUserService sysUserService;
@Override
public Result commentsByArticleId(Long articleId) {
LambdaQueryWrapper<Comment> queryWrapper=new LambdaQueryWrapper<>();
queryWrapper.eq(Comment::getArticleId,articleId);
queryWrapper.eq(Comment::getLevel,1);
List<Comment> comments = commentMapper.selectList(queryWrapper);
List<CommentVo> commentVoList= copyList(comments);
return Result.success(commentVoList);
}
private List<CommentVo> copyList(List<Comment> comments) {
List<CommentVo> commentVoList = new ArrayList<>();
for (Comment comment : comments) {
commentVoList.add(copy(comment));
}
return commentVoList;
}
private CommentVo copy(Comment comment) {
CommentVo commentVo = new CommentVo();
BeanUtils.copyProperties(comment, commentVo);
commentVo.setCreateDate(new DateTime(comment.getCreateDate()).toString("yyyy-MM-dd HH:mm"));
Long authorId = comment.getAuthorId();
UserVo userVo = this.sysUserService.findUserVoById(authorId);
commentVo.setAuthor(userVo);
Integer level = comment.getLevel();
if (level == 1){
Long id = comment.getId();
List<CommentVo> commentVoList = findCommentByParentId(id);
commentVo.setChildrens(commentVoList);
}
if (level > 1) {
Long toUid = comment.getToUid();
UserVo toUserVo = this.sysUserService.findUserVoById(toUid);
commentVo.setToUser(toUserVo);
}
return commentVo;
}
private List<CommentVo> findCommentByParentId(Long id) {
LambdaQueryWrapper<Comment> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(Comment::getParentId,id);
queryWrapper.eq(Comment::getLevel,2);
return copyList(commentMapper.selectList(queryWrapper));
}
}
需要向SysUserServiceImpl中增加findUserVoById(Long id)方法
@Override
public UserVo findUserVoById(Long id) {
SysUser sysUser = sysUserMapper.selectById(id);
if (sysUser == null) {
sysUser = new SysUser();
sysUser.setId(1L);
sysUser.setAvatar("/static/img/logo.b3a48c0.png");
sysUser.setNickname("空");
}
UserVo userVo = new UserVo();
BeanUtils.copyProperties(sysUser,userVo);
userVo.setId(String.valueOf(sysUser.getId()));
return userVo;
}
进行测试
17.评论功能
17.1 接口说明
接口url:/comments/create/change 请求方式: POST 请求参数:
参数名称 | 参数类型 | 说明 |
---|
articleid | long | 文章id | content | lstring | 评论内容 | parent | long | 父评论id | toUserid | long | 被评论用户id |
创建CommentParam
package com.lum.blog.vo.params;
@Data
public class CommentParam {
private Long articleId;
private String content;
private Long parentId;
private Long toUserId;
}
17.2 需要加入到登录拦截器中 登录后才可以评论 WebMvcConfig
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginInterceptor)
.addPathPatterns("/comments/create/change")
.addPathPatterns("/test");
}
因为分布式id 的Long过长前端解析精度损失,会将值改变,需要进行Json序列化 CommentVo
@JsonSerialize(using = ToStringSerializer.class)
private Long id;
17.3 Controller 在commentController中添加接口
@PostMapping("create/change")
public Result comment(@RequestBody CommentParam commentParam){
return commentsService.comment(commentParam);
}
17.4 Service CommentService
Result comment(CommentParam commentParam);
实现方法CommentServiceImpl
@Override
public Result comment(CommentParam commentParam) {
SysUser sysUser = UserThreadLocal.get();
Comment comment = new Comment();
comment.setArticleId(commentParam.getArticleId());
comment.setAuthorId(sysUser.getId());
comment.setContent(commentParam.getContent());
comment.setCreateDate(System.currentTimeMillis());
Long parent = commentParam.getParentId();
if (parent == null || parent == 0) {
comment.setLevel(1);
} else {
comment.setLevel(2);
}
comment.setParentId(parent == null ? 0 : parent);
Long toUserId = commentParam.getToUserId();
comment.setToUid(toUserId == null ? 0 : toUserId);
this.commentMapper.insert(comment);
return Result.success(null);
}
18.写文章
需要三个接口:
- 获取所有文章类别
- 获取所有标签
- 发布文章
18.1 所有文章分类
接口说明 接口url:/categorys 请求方式: GET 请求参数: |参数名称| 参数类型 |说明|
Controller
CategoryController
@GetMapping
public Result categories(){
return categoryService.findAll();
}
Service
CategroyService
Result findAll();
CategoryServiceImpl
@Override
public Result findAll() {
LambdaQueryWrapper<Category> queryWrapper = new LambdaQueryWrapper<>();
List<Category> categories = categoryMapper.selectList(queryWrapper);
return Result.success(copyList(categories));
}
public CategoryVo copy(Category category){
CategoryVo categoryVo = new CategoryVo();
BeanUtils.copyProperties(category,categoryVo);
return categoryVo;
}
public List<CategoryVo> copyList(List<Category> categoryList){
List<CategoryVo> categoryVoList = new ArrayList<>();
for (Category category : categoryList) {
categoryVoList.add(copy(category));
}
return categoryVoList;
}
18.2 所有文章标签 接口说明 接口url:/tags 请求方式: GET 请求参数: |参数名称| 参数类型 |说明|
Controller
TagsController
@GetMapping
public Result findAll() {
return tagService.findAll();
}
TagService
Result findAll();
TagsServiceImpl
@Override
public Result findAll() {
List<Tag> tags = this.tagMapper.selectList(new LambdaQueryWrapper<>());
return Result.success(copyList(tags));
}
19.发布文章
接口说明
接口url:/articles/publish 请求方式: POST 请求参数:
参数名称 | 参数类型 | 说明 |
---|
title | string | 文章标题 | id | long | 文章id(编辑有值) | body | object | 文章内容 | category | json | 文章类别 | summary | string | 文章概述 | tags | json | 文章标签 |
创建ArticleParam
@Data
public class ArticleParam {
private Long id;
private ArticleBodyParam body;
private CategoryVo category;
private String summary;
private List<TagVo> tags;
private String title;
}
创建ArticBodyParam
@Data
public class ArticleBodyParam {
private String content;
private String contentHtml;
}
创建pojo下article和tag关联表对象
@Data
public class ArticleTag {
private Long id;
private Long articleId;
private Long tagId;
新建ArticleTagMapper接口
package com.lum.blog.dao.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.lum.blog.dao.pojo.ArticleTag;
public interface ArticleTagMapper extends BaseMapper<ArticleTag> {
}
将ArticleVo中id进行序列化,防止前段解析损失
@JsonSerialize(using = ToStringSerializer.class)
private Long id;
ArticleController
@PostMapping("publish")
public Result publish(@RequestBody ArticleParam articleParam){
return articleService.publish(articleParam);
}
ArticleService
Result publish(ArticleParam articleParam);
ArticleServiceImpl
@Autowired
private ArticleTagMapper articleTagMapper;
@Override
public Result publish(ArticleParam articleParam) {
SysUser sysUser = UserThreadLocal.get();
Article article = new Article();
article.setAuthorId(sysUser.getId());
article.setWeight(Article.Article_Common);
article.setViewCounts(0);
article.setTitle(articleParam.getTitle());
article.setSummary(articleParam.getSummary());
article.setCommentCounts(0);
article.setCreateDate(System.currentTimeMillis());
article.setCategoryId((articleParam.getCategory().getId()));
this.articleMapper.insert(article);
List<TagVo> tags = articleParam.getTags();
if (tags != null){
for (TagVo tag : tags) {
Long articleId = article.getId();
ArticleTag articleTag = new ArticleTag();
articleTag.setTagId((tag.getId()));
articleTag.setArticleId(articleId);
articleTagMapper.insert(articleTag);
}
}
ArticleBody articleBody = new ArticleBody();
articleBody.setArticleId(article.getId());
articleBody.setContent(articleParam.getBody().getContent());
articleBody.setContentHtml(articleParam.getBody().getContentHtml());
articleBodyMapper.insert(articleBody);
article.setBodyId(articleBody.getId());
articleMapper.updateById(article);
Map<String,String> map = new HashMap<>();
map.put("id",article.getId().toString());
return Result.success(map);
}
20 AOP日志
创建commom.aop包 创建LogAnnotation自定义注解
package com.lum.blog.common.aop;
import java.lang.annotation.*;
@Target({
ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LogAnnotation {
String module() default "";
String operator() default "";
}
同时创建LogAspect用来定义切入点及日志
package com.lum.blog.common.aop;
import com.alibaba.fastjson.JSON;
import com.lum.blog.utils.HttpContextUtils;
import com.lum.blog.utils.IpUtils;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
@Component
@Aspect
@Slf4j
public class LogAspect {
@Pointcut("@annotation(com.lum.blog.common.aop.LogAnnotation)")
public void pt(){
}
@Around("pt()")
public Object log(ProceedingJoinPoint joinPoint) throws Throwable {
long beginTime = System.currentTimeMillis();
Object result = joinPoint.proceed();
long time = System.currentTimeMillis() - beginTime;
recordLog(joinPoint, time);
return result;
}
private void recordLog(ProceedingJoinPoint joinPoint, long time) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
LogAnnotation logAnnotation = method.getAnnotation(LogAnnotation.class);
log.info("=====================log start================================");
log.info("module:{}",logAnnotation.module());
log.info("operation:{}",logAnnotation.operator());
String className = joinPoint.getTarget().getClass().getName();
String methodName = signature.getName();
log.info("request method:{}",className + "." + methodName + "()");
Object[] args = joinPoint.getArgs();
String params = JSON.toJSONString(args[0]);
log.info("params:{}",params);
HttpServletRequest request = HttpContextUtils.getHttpServletRequest();
log.info("ip:{}", IpUtils.getIpAddr(request));
log.info("excute time : {} ms",time);
log.info("=====================log end================================");
}
}
接下来就可以到Controller任意方法中添加注解来进行记录
@PostMapping
@LogAnnotation(module = "文章",operator = "获取文章列表")
public Result listArticle(@RequestBody PageParams pageParams){
return articleService.listArticle(pageParams);
}
21 文章图片上传
接口名称 接口url:/upload 请求方式: POST 请求参数:
参数名称 | 参数类型 | 说明 |
---|
image | file | 上传的文件名称 |
Controller
新建UploadController
package com.lum.blog.controller;
import com.lum.blog.utils.QiniuUtils;
import com.lum.blog.vo.Result;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.util.UUID;
@RestController
@RequestMapping("upload")
public class UploadController {
@Autowired
private QiniuUtils qiniuUtils;
@PostMapping
public Result upload(@RequestParam("image") MultipartFile file) {
String originalFilename = file.getOriginalFilename();
String fileName = UUID.randomUUID().toString() + "." + StringUtils.substringAfterLast(originalFilename, ".");
boolean upload = qiniuUtils.upload(file, fileName);
if (upload){
return Result.success(QiniuUtils.url + fileName);
}
return Result.fail(20001,"上传失败");
}
}
在appliation.properties中配置上传文件大小
#上传文件的最大值
spring.servlet.multipart.max-request-size=20MB
#单个文件的最大值
spring.servlet.multipart.max-file-size=2MB
使用七牛云存储 maven导包
<dependency>
<groupId>com.qiniu</groupId>
<artifactId>qiniu-java-sdk</artifactId>
<version>[7.7.0, 7.7.99]</version>
</dependency>
新建七牛Utils
package com.lum.blog.utils;
import com.alibaba.fastjson.JSON;
import com.qiniu.http.Response;
import com.qiniu.storage.Configuration;
import com.qiniu.storage.Region;
import com.qiniu.storage.UploadManager;
import com.qiniu.storage.model.DefaultPutRet;
import com.qiniu.util.Auth;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
@Component
public class QiniuUtils {
public static final String url = "https://qywco434a.hd-bkt.clouddn.com/";
@Value("${qiniu.accessKey}")
private String accessKey;
@Value("${qiniu.accessSecretKey}")
private String accessSecretKey;
public boolean upload(MultipartFile file,String fileName){
Configuration cfg = new Configuration(Region.huabei());
UploadManager uploadManager = new UploadManager(cfg);
String bucket = "lumblog";
try {
byte[] uploadBytes = file.getBytes();
Auth auth = Auth.create(accessKey, accessSecretKey);
String upToken = auth.uploadToken(bucket);
Response response = uploadManager.put(uploadBytes, fileName, upToken);
DefaultPutRet putRet = JSON.parseObject(response.bodyString(), DefaultPutRet.class);
return true;
} catch (Exception ex) {
ex.printStackTrace();
}
return false;
}
}
22.导航-文章分类
接口名称 接口url:/categorys/detail 请求方式: GET 请求参数:
CategoryController
@GetMapping("detail")
public Result categoriesDetail(){
return categoryService.findAllDetail();
}
CategoryService
Result findAllDetail();
CategoryServiceImpl
@Override
public Result findAllDetail() {
LambdaQueryWrapper<Category> queryWrapper = new LambdaQueryWrapper<>();
List<Category> categories = categoryMapper.selectList(queryWrapper);
return Result.success(copyList(categories));
}
与findAll区别在于 findAll只查询id和分类名 findAllDetail查询全部信息
23 导航-标签
接口说明
接口url:/categorys/detail 请求方式: GET 请求参数:
23.2 TagVo新增avatar属性
package com.lum.blog.vo;
import lombok.Data;
@Data
public class TagVo {
private Long id;
private String tagName;
private String avatar;
}
CategroyVo
@Data
public class CategoryVo {
private Long id;
private String avatar;
private String categoryName;
private String description;
TagsController
@GetMapping("detail")
public Result findAllDetail() {
return tagService.findAllDetail();
}
TagService
Result findAllDetail();
TagServiceImpl 更改之前findAll方法,增加条件只查询id和tagName 需要设么查询什么
@Override
public Result findAll() {
LambdaQueryWrapper<Tag> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.select(Tag::getId,Tag::getTagName);
List<Tag> tags = this.tagMapper.selectList(queryWrapper);
return Result.success(copyList(tags));
}
@Override
public Result findAllDetail() {
LambdaQueryWrapper<Tag> queryWrapper = new LambdaQueryWrapper<>();
List<Tag> tags = this.tagMapper.selectList(queryWrapper);
return Result.success(copyList(tags));
}
24 分类文章列表
接口名称 接口url:/categorys/detail/{id} 请求方式: GET 请求参数:
CategoryController
@GetMapping("detail/{id}")
public Result categoryDetailById(@PathVariable("id") Long id){
return categoryService.categoryDetailById(id);
}
CategoryService
Result categoryDetailById(Long id);
CategoryServiceImpl
@Override
public Result categoryDetailById(Long id) {
Category category = categoryMapper.selectById(id);
return Result.success(copy(category));
}
因为CategoryVo的tagId属性改为String CategoryVo copy代码修改
public CategoryVo copy(Category category){
CategoryVo categoryVo = new CategoryVo();
BeanUtils.copyProperties(category,categoryVo);
categoryVo.setId(String.valueOf(category.getId()));
return categoryVo;
}
需要增加ArticleServiceImpl中的ListArticle条件实现根据CategoryId分类
PageParam
package com.lum.blog.vo.params;
import lombok.Data;
@Data
public class PageParams {
private int page = 1;
private int pageSize = 10;
private Long categoryId;
private Long tagId;
}
ArticleServiceImpl
if(pageParams.getCategoryId() != null){
queryWrapper.eq(Article::getCategoryId,pageParams.getCategoryId());
}
25.标签文章列表
接口名称 接口url:/tags/detail/{id} 请求方式: GET 请求参数:
TagsController
@GetMapping("detail/{id}")
public Result findDetailById(@PathVariable("id") Long id) {
return tagService.findDetailById(id);
}
tagService
Result findDetailById(Long id);
tagServiceimpl
因为tagVo的tagId属性改为String tagVo copy方法修改
public TagVo copy(Tag tag){
TagVo tagVo = new TagVo();
BeanUtils.copyProperties(tag,tagVo);
tagVo.setId(String.valueOf(tag.getId()));
return tagVo;
}
@Override
public Result findDetailById(Long id) {
Tag tag = tagMapper.selectById(id);
return Result.success(copy(tag));
}
标签对应文章 在ArticleServiceImpl增加查询文章列表条件(listArticle)
List<Long> articleIdList = new ArrayList<>();
if (pageParams.getTagId() !=null) {
LambdaQueryWrapper<ArticleTag> articleTagLambdaQueryWrapper = new LambdaQueryWrapper<>();
articleTagLambdaQueryWrapper.eq(ArticleTag::getTagId,pageParams.getTagId());
List<ArticleTag> articleTags = articleTagMapper.selectList(articleTagLambdaQueryWrapper);
for (ArticleTag articleTag : articleTags) {
articleIdList.add(articleTag.getArticleId());
}
if (articleIdList.size() > 0){
queryWrapper.in(Article::getId,articleIdList);
}
}
26 导航栏-文章归档
在PageParam中添加属性
private String year;
private String month;
public String getMonth(){
if (this.month != null && this.month.length() == 1){
return "0"+this.month;
}
return this.month;
}
因为需要按时间查询文章列表,所以需要修改articleServiceImpl中的 listArticle 查询条件
@Override
public Result listArticle(PageParams pageParams) {
Page<Article> page = new Page<>(pageParams.getPage(),pageParams.getPageSize());
IPage<Article> articleIPage = articleMapper.listAticle(
page,
pageParams.getCategoryId(),
pageParams.getTagId(),
pageParams.getYear(),
pageParams.getMonth());
List<Article> recordes = articleIPage.getRecords();
return Result.success(copyList(recordes,true,true));
}
将之前的注释掉
在articlemapper.java中增加mybatis-plus分页查询文章属性
IPage<Article> listAticle(Page<Article> page,
Long categoryId,
Long tagId,
String year,
String month);
修改articleMapper.xml中对应的sql语句 增加articleMap映射语句,属性和数据库对应
因为开启了驼峰命名映射ResultMap,所以可删除映射表,两种方法都可以
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.lum.blog.dao.mapper.ArticleMapper">
<resultMap id="articleMap" type="com.lum.blog.dao.pojo.Article">
<id column="id" property="id" />
<result column="author_id" property="authorId"/>
<result column="comment_counts" property="commentCounts"/>
<result column="create_date" property="createDate"/>
<result column="summary" property="summary"/>
<result column="title" property="title"/>
<result column="view_counts" property="viewCounts"/>
<result column="weight" property="weight"/>
<result column="body_id" property="bodyId"/>
<result column="category_id" property="categoryId"/>
</resultMap>
<select id="listArchives" resultType="com.lum.blog.dao.dos.Archives">
select FROM_UNIXTIME(create_date/1000,'%Y') as year,
FROM_UNIXTIME(create_date/1000,'%m') as month,
count(*) as count
from ms_article
group by year,month
</select>
<select id="listAticle" resultMap="articleMap" resultType="com.lum.blog.dao.pojo.Article">
select * from ms_article
<where>
1=1
<if test="categoryId != null">
and category_id=#{categoryId}
</if>
<if test="tagId != null">
and id in (select article_id from ms_article_tag where tag_id=#{tagId})
</if>
<if test="year != null and year.length>0 and month != null and month.length>0">
and (From_UNIXTIME(create_date/1000,'%Y') = #{year} and From_UNIXTIME(create_date/1000,'%m') = #{month})
</if>
</where>
order by weight desc,create_date desc
</select>
</mapper>
防止解析误差修改tagVo中id和categoryVo中id为String类型
修改相关copy方法代码.getid用String.valueOf()转换
|