Blog Project Building (Path to Coden)

Keywords: Java Spring Boot


Front End Project Run Command

npm install
npm run build
npm run dev

Create a project that, if developed normally, first imports the following dependencies

<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>

If the project you created reports the following errors, please lower the SpringBoot version

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

1. Resource download and project building

  1. Download Front End Projects (in this case, go to QQ group) download)

  2. Build Project (Create Project with idea)
    1) idea create new xxxx in a series of operations
    2) Import Dependency

     a -----: parent and properties Import	```
    
    
	<!--parent Interpretation 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>

    <!--Issues defining some attributes-->
    <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--Import other dependencies
<?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>

  1. Create sub-module blog-api
    1) Why create sub-modules: convenient for future sub-module development
    2) Import Dependency
<?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>
            <!-- Exclude default 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>
<!--This is AOP Let's not talk more about the problem.-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
<!--This is the mailbox handling-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-mail</artifactId>
        </dependency>
  <!--This import comes in because the process is ongoing-->       
        <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 Formatting problem-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.76</version>
        </dependency>
<!--mysql Operation-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
<!--You can see json Medium Information-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
<!--with java.lang This package does something similar, Commons Lang This group API It also provides some basic, common operations and processing, such as automatic generation toString()Result, Auto-implementation hashCode()and equals()Methods, array operations, enumerations, date and time processing, and so on.-->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
        </dependency>
<!--StringUtils This is what's provided to sometimes verify what's empty.-->
        <dependency>
            <groupId>commons-collections</groupId>
            <artifactId>commons-collections</artifactId>
            <version>3.2.2</version>
        </dependency>
 <!--image Md5 Encrypt it-->        
        <dependency>
            <groupId>commons-codec</groupId>
            <artifactId>commons-codec</artifactId>
        </dependency>
<!--mybatis Configuration-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.3</version>
        </dependency>
   <!--Is about Data Annotated-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    <!--Classes for time processing-->
        <dependency>
            <groupId>joda-time</groupId>
            <artifactId>joda-time</artifactId>
            <version>2.10.10</version>
        </dependency>
  <!--Verification-->
        <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>
  1. Write application.properties file
server.port=8888

#Configuration Project Name
spring.application.name=blog

#Database Settings
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_


  1. Write startup class 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);
    }
}

  1. Then because of the introduction of Mybatis-plus above, all have the following configurations
    Create the config folder in your project, then create MybatisplusConfig.java and write the following code:
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 {
}

  1. Start BlogApplication Test

    Success will do, maybe failure, mostly maven dependencies are not right, compare carefully!

  2. Paging is definitely used in projects, and all Paging Plugins that use Mybatis are included.The following modifications were made to MybatisPlusConfig:

 //Mybatis-plus Paging Plugin
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
        mybatisPlusInterceptor.addInnerInterceptor( new PaginationInnerInterceptor());
        return mybatisPlusInterceptor;
    }

  1. Create a WebMmvConfig.java file
    Configure cross-domain issues here first, since the front and back ends are separated, and front-end front-end port access is cross-domain, all cross-domain issues to be configured.
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 {
   //For cross-domain configuration, why do I need this cross-domain configuration because, for example, my front-end port number is 8080 and my back-end interface is 8888
   @Override
   public void addCorsMappings(CorsRegistry registry) {

       //addMapping is all the files, allowedOrigins means that you can access them at that address
       registry.addMapping("/**").allowedOrigins("http://localhost:8080");
   }
}


πŸ˜‰πŸ˜‰πŸ˜‰πŸ˜‰πŸ˜‰πŸ˜‰πŸ˜‰πŸ˜‰πŸ˜‰πŸ˜‰πŸ˜‰πŸ˜‰πŸ˜‰πŸ˜‰πŸ˜‰πŸ˜‰πŸ˜‰πŸ˜‰πŸ˜‰πŸ˜‰πŸ˜‰πŸ˜‰πŸ˜‰πŸ˜‰πŸ˜‰πŸ˜‰πŸ˜‰πŸ˜‰πŸ˜‰πŸ˜‰πŸ˜‰πŸ˜‰πŸ˜‰πŸ˜‰πŸ˜‰πŸ˜‰πŸ˜‰πŸ˜‰πŸ˜‰πŸ˜‰πŸ˜‰πŸ˜‰πŸ˜‰πŸ˜‰πŸ˜‰πŸ˜‰πŸ˜‰πŸ˜‰πŸ˜‰πŸ˜‰πŸ˜‰πŸ˜‰πŸ˜‰πŸ˜‰πŸ˜‰πŸ˜‰πŸ˜‰πŸ˜‰πŸ˜‰πŸ˜‰πŸ˜‰πŸ˜‰πŸ˜‰πŸ˜‰πŸ˜‰πŸ˜‰πŸ˜‰πŸ˜‰πŸ˜‰πŸ˜‰πŸ˜‰πŸ˜‰

2. Functions

1. First Page Articles List Page - -1

1.1 Interface Description

Interface url:/articles
Request method: post request
Request parameters:

Parameter NameParameter typeExplain
pageintCurrent Page
pagesizeintNumber of displays per page
Return data:

~~~json
{
    "success": true,
    "code": 200,
    "msg": "success",
    "data": [
        {
            "id": 1,
            "title": "springboot Introduction and introductory cases",
            "summary": "adopt Spring Boot Only one service needs to be implemented Java Class, pack it into jar,And through`java -jar`The command will run.\r\n\r\n This is all in contrast to tradition Spring It has become very lightweight and simple to use.",
            "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 What's this",
            "summary": "Vue (pronunciation /vjuː/,Be similar to view) Is a progressive framework for building user interfaces.",
            "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 Relevant",
            "summary": "This section describes how to use it in your project 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 Table Structure

Now that we have data to return from the front-end and pages, our users must be related to classes for anything.

Article Data Table Returning Data
~~~sql
CREATE TABLE `blog`.`ms_article`  (
  `id` bigint(0) NOT NULL AUTO_INCREMENT,
  `comment_counts` int(0) NULL DEFAULT NULL COMMENT 'Number of comments',
  `create_date` bigint(0) NULL DEFAULT NULL COMMENT 'Creation Time',
  `summary` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 'brief introduction',
  `title` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 'Title',
  `view_counts` int(0) NULL DEFAULT NULL COMMENT 'Number of Browses',
  `weight` int(0) NOT NULL COMMENT 'isTop',
  `author_id` bigint(0) NULL DEFAULT NULL COMMENT 'author id',
  `body_id` bigint(0) NULL DEFAULT NULL COMMENT 'content id',
  `category_id` int(0) NULL DEFAULT NULL COMMENT 'category id',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 25 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
~~~

//Tag table, other items can be viewed by the article
~~~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;
~~~

//User Data Table
~~~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 'Account number',
  `admin` bit(1) NULL DEFAULT NULL COMMENT 'Is Administrator',
  `avatar` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 'Head portrait',
  `create_date` bigint(0) NULL DEFAULT NULL COMMENT 'Registration Time',
  `deleted` bit(1) NULL DEFAULT NULL COMMENT 'Delete or not',
  `email` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 'mailbox',
  `last_login` bigint(0) NULL DEFAULT NULL COMMENT 'Last Logon Time',
  `mobile_phone_number` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 'Cell-phone number',
  `nickname` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 'Nickname?',
  `password` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 'Password',
  `salt` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 'Encrypted salt',
  `status` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 'state',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 16 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;


1.3 Dao Development

com.lum.blog.dao.pojo.Article.java

package com.lum.blog.dao.pojo;

import lombok.Data;
/*
Blog Management
 */
@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;

    /**
     * Author id
     */
    private Long authorId;
    /**
     * Content id
     */
    private Long bodyId;
    /**
     *Category id
     */
    private Long categoryId;

    /**
     * Set Top
     */
    private int weight = Article_Common;


    /**
     * Creation Time
     */
    private Long createDate;
}




com.lum.blog.dao.pojo.SysUser.java

package com.lum.blog.dao.pojo;
import lombok.Data;

/*
user management
 */
@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;
/*
Label Management
 */
@Data
public class Tag {

    private Long id;

    private String avatar;

    private String tagName;

}


1.4 Creation of 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.5 apper created, it's Controller's time

Establish ArticleController,This represents the controller of the article class
package com.lum.blog.controller;

import com.lum.blog.vo.Result;
import com.lum.blog.vo.params.PageParams;
import org.springframework.web.bind.annotation.*;

//Interacting with json data
@RestController
@RequestMapping("articles")
public class ArticleController {

        //Why use a post request because the previous interface describes the post request
        //For the other parameters, set up the PageParams class below vo to specifically represent the parameters

        /**
         * First Page Articles List
         * @param pageParams
         * return Returns the class responsible for returning the data Result
         */
        @PostMapping
        public Result listArticle(@RequestBody PageParams pageParams){
            //For accepted parameter problems, here is RequestBody receive
            return articleService.listArticle(pageParams);
        }

}

1) This is supplemented by the parameter we passed in, which is the representative of the PageParms class
Create a directory for vo under com.lum.Blog and then create a class for PageParms

package com.lum.blog.vo.params;

import lombok.Data;

//Classes that bear the number and number of returned pages
@Data
public class PageParams {
	private int page = 1;

	private int pageSize = 10;
}


2) Classes of data Result returned

package com.lum.blog.vo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

//The class responsible for returning to the list of articles on the first page
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Result {
    //Represents success
    private boolean success;

    //Code on our behalf
    private int code;

    //Represents a message
    private String msg;

    //Represents data
    private Object data;


    //Represents Success
    public static Result success(Object data) {
        return new Result(true , 200, "success" ,data);
    }

    //On behalf of failure
    public static Result Fail(int code, String msg) {
        //No data to return, all data is null
        return new Result(false , code, msg,null);
    }
}

1) First we write the DAO layer and the table names in the database to correspond.
2) Then the XXX-Mapper layer is written to inherit BaseMapper<xxx class name>.
3) Then, instead of typically writing his xxxMapper.xml, we will configure xxxService+xxxServiceImpl if we want to write xxxMapper.xml as well as Mybatis

1.6 service

From the above, we can see that we have almost developed, but want to return the data, but the listArticle method does not have a Service to read the data, so we develop a Service and a method to read the data.

1) Start by writing this service layer, creating a service folder under src/main/java, and following ArticleService files and Impl folders

package com.lum.blog.service;

import com.lum.blog.vo.Result;
import com.lum.blog.vo.params.PageParams;

public interface ArticleService {
    /**
     * Paging Query Articles List
     * @param pageParams
     * @return
     */
    Result listArticle(PageParams pageParams);
}

2) Come back and write his Impl file, 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) {
        //1. This is a class of paged queries (representing the split mode), which are passed in the number of pages and the total number of pages
        Page<Article> page = new Page<Article>(pageParams.getPage(),pageParams.getPageSize());

        //2. LambdaQueryWrapper is provided by MybatisPlus, you can import this package as needed
        LambdaQueryWrapper<Article> queryWrapper = new LambdaQueryWrapper<>();

        //3. Here's sorting by font
        //queryWrapper.orderByDesc(Article::getWeight);
        //4. The setting here is to sort by time
        //queryWrapper.orderByDesc(Article::getCreateDate);
        //5. This method default Children orderByDesc(boolean condition, R column, R... columns) {is a variable length parameter
        queryWrapper.orderByDesc(Article::getWeight,Article::getCreateDate);

        // Because articleMapper inherits BaseMapper, all parameters set for the query and how the query is sorted
        Page<Article> articlePage = articleMapper.selectPage(page, queryWrapper);

        //This is the array of data we queried
        List<Article> records = articlePage.getRecords();

        //Because the data displayed on the page is not necessarily the same as the database, we need to do a conversion
       //Decoupling, vo interacting with page data will be accomplished by copying an array of lookup databases into articleVo
        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;
    }

    //The main point of this method is BeanUtils, which Spring provides, which is dedicated to copying and returns a copy of the same properties as Article and articlevo
    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;
    }
}

Where ArticleVo and TagVo need to be created

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;  //brief introduction

    private int commentCounts;

    private int ViewCounts;

    private int weight;   //Set Top

    private String createDate;  //Creation Time

    private String author;
    
//Not need it for now
//    private ArticleBodyVo body;

    private List<TagVo> tags;
    
//Not need it for now
//    private List<CategoryVo> categories;
}

package com.lum.blog.vo;

import lombok.Data;

@Data
public class TagVo {

    private Long id;

    private String tagName;
}

Data that vo interacts with pages should not be coupled with database mapping objects and should ideally be separated

Here's a complement to this articleVo, this class, because what we usually develop, it's time
Find the data in the database, and then find out it is not necessarily the same, to copy it the same, different return null

Summary: With the above configuration, the content of the page can be displayed

2. First Page Articles List Page - 2

Problem introduction: In the content display of the first page developed before, there is no label, author's information and so on below the article. To develop the content below

2.1 Implemented in ArticleServiceImpl

Think: Not all interfaces need labels, author information

Add two boolean isTag,isAuthor To make a judgment

Add code to 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;
    }

Add code to copy

//The main point of this method is BeanUtils, which Spring provides, which is dedicated to copying and returns a copy of the same properties as Article Vo
    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"));
        /*
        Not all interfaces require labels, author information
        Add two parameters boolean isTag,boolean isAuthor
         */
		//tagService needs to be developed
        if (isTag) {
            Long articleId = article.getId();
            articleVo.setTags(tagService.findTagsByArticleId(articleId));
        }
        //authorService needs to be developed
        if (isAuthor) {
            Long authorId = article.getAuthorId();
            articleVo.setAuthor(sysUserService.findUserById(authorId).getNickname());
        }
        return articleVo;
    }

2.2 Tag tag

Write TagService

package com.lum.blog.service;

import com.lum.blog.vo.TagVo;

import java.util.List;

public interface TagService {

    List<TagVo> findTagsByArticleId(Long articleId);
}

Write implementation class 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;

    /*copyList Pass tag*/
    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) {
        /* MyBatisPlus Unable to implement multi-table query */
        List<Tag> tags=tagMapper.findTagsByArticleId(articleId);
        return copyList(tags);
    }
}

Write 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> {
    /**
     * Query tag list based on article id
     * @param articleId
     * @return
     */
    List<Tag> findTagsByArticleId(Long articleId);
}

Create TagMapper.xml under resourse
Path is consistent with interface package (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" corresponds to articleId of List findTagsByArticleId(Long 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})
Query the id,auatar,tagName of a tag in an associated table

Hump naming can be turned on by mybatis-plus in application.properties

mybatis-plus.configuration.map-underscore-to-camel-case=true

This eliminates the need for an as alias for SQL statements.

Next, inject TagService into the ArticleImpl implementation

 if (isTag) {
            Long articleId = article.getId();
            articleVo.setTags(tagService.findTagsByArticleId(articleId));
        }

2.3 Author author

Set up interface SysUserService

package com.lum.blog.service;

import com.lum.blog.dao.pojo.SysUser;

public interface SysUserService {

    SysUser findUserById(Long id);


}

Write implementation class 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) {
        /*Prevent IDs from being empty*/
        SysUser sysUser = sysUserMapper.selectById(id);
        if (sysUser == null) {
            sysUser = new SysUser();
            sysUser.setNickname("Deer Ming");

        }
        return sysUser;
    }

}

Inject SysUserMapper and write a query

Inject SysUserService into the article implementation class ArticleServiceImpl

   if (isAuthor) {
            Long authorId = article.getAuthorId();
            articleVo.setAuthor(sysUserService.findUserById(authorId).getNickname());
        }

Write a workaround for empty situations in SysUserImpl

 public SysUser findUserById(Long id) {
        /*Prevent IDs from being empty*/
        SysUser sysUser = sysUserMapper.selectById(id);
        if (sysUser == null) {
            sysUser = new SysUser();
            sysUser.setNickname("lum");

        }
        return sysUser;
    }

Add the return value istag,isauthor to the copyList

 List<ArticleVo> articleVoList = copyList(records,true,true);
 for (Article record : records) {
            articleVoList.add(copy(record,isTag,isAuthor));
        }

Where is it reflected in the preceding code?😁

Testing

3. Home page - Hottest label

    1.Tags have the most articles
    2.Query by tag_id Grouping,technology,Row from large to small,Prefetch limit individual

3.1 Interface Description

Interface url:/tag/hot
Request method: Get
Request parameters: None
Return data:

{
	"successs":true
	"code"200
	"msg":"success"
	"data"[
		{
		"id":1,
		"tagName":"Hottest"
		}
	]
}

3.2 Encoding

Write controller first
Create 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;  //Hottest 6
        return tagService.hots(limit);
    }
}

Implementing the hots method in tagService

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);
}

First analyze the SQL statement

select tag_id
from ms_article_tag
group by tag_id
order by count(*) limit 2

TagServiceImpl implements Result hots(int limit)

 @Override
    public Result hots(int limit) {
        /**
         * 1.Tags have the most articles
         * 2.Query based on tag_id grouping, technology, sorting from large to small, top limit
         */
        List<Long> tagIds= tagMapper.findHotsTagId(limit);
        return null;
    }

Create findHostTagId(limit) method in TagMapper

 /**
     * Query the top hot label limit bar
     * @param limit
     * @return
     */
    List<Long> findHotsTagId(int limit);

Generate findHotsTagId in the resource file TagMapper and add==parameterType="int"==attribute and sql statement

<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>

At this point, the hottest tagid is queried, and tagName,Tag need to be queried according to tagid
Continue Result hot

 @Override
    public Result hots(int limit) {
        /**
         * 1.Tags have the most articles
         * 2.Query based on tag_id grouping, technology, sorting from large to small, top limit
         */
        List<Long> tagIds= tagMapper.findHotsTagId(limit);

        /*Determine if tagIds is empty*/
        if(CollectionUtils.isEmpty(tagIds)){
            return Result.success(Collections.emptyList());
        }
//        What you need are tagId and tagName tag objects
//        select * from tag where id in (1,2,3)
        List<Tag> tagList= tagMapper.findTagsByIds(tagIds);
        return Result.success(tagList);
    }

So you need to add a method in TagMapper.java

 /**
     * Query Tag objects based on tagId
     * @param tagIds
     * @return
     */
    List<Tag> findTagsByIds(List<Long> tagIds);

Add in resource 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 Testing

4. Unified exception handling

Create handler (under com.lum.blog.) package and create AllExceptionHandler class

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
//Intercept handling of AOP implementations for methods with @ControllerAdvice added
public class AllExceptionHandler {

    @ExceptionHandler(Exception.class)   //Do exception handling to handle Exception.class exceptions
    @ResponseBody                        //Return Json data
    public Result doExceptionHandler(Exception e){
        e.printStackTrace();
        return Result.fail(-999,"System exception");
    }
}

Add error code to controller code to test

5. Home - Hottest Articles

5.1 Interface Description

Interface url: /articles/hot
Request method: POST
Request parameters:

Return data:

{
  "success": true,
    "code": 200,
    "msg": "success",
    "data": [
        {
            "id": 1,
            "title": "springboot Introduction and introductory cases",
         },
         {
           "id": 2,
            "title": "springboot Introduction and introductory cases",
         }  
            ]
}

5.2 Controller

Add PostMapper to ArticleController to return to the hottest articles

  /**
     * Hottest articles on the first page
     * @return
     */
    @PostMapping("hot")
    public Result hotArticle(){
        int limit=5;
        return articleService.hotArticle(limit);
    }

5.3Service

Create the corresponding method in articleService. hotArticle(limit);

    Result hotArticle(int limit);

Implement Result hotArticle(int limit) in articleServiceImpl for sql query and copyList return

 @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);
        //select id,title from article order by view_counts desc limit 5
        List<Article> articles = articleMapper.selectList(queryWrapper);

        return Result.success(copyList(articles,false,false));
    }

5.4 Testing

6. Home page - latest article

6.1 Interface Description

Interface url: /articles/new
Request method: POST
Request parameters:

Return data:

{
  "success": true,
    "code": 200,
    "msg": "success",
    "data": [
        {
            "id": 1,
            "title": "springboot Introduction and introductory cases",
         },
         {
           "id": 2,
            "title": "springboot Introduction and introductory cases",
         }  
            ]
}

6.2 Controller

Add PostMapper to ArticleController to return to the hottest articles

  /**
     * Hottest articles on the first page
     * @return
     */
    @PostMapping("new")
    public Result newArticle(){
        int limit=5;
        return articleService.newArticle(limit);
    }

6.3Service

Create the corresponding method in articleService. hotArticle(limit);

    Result newArticle(int limit);

Implement Result hotArticle(int limit) in articleServiceImpl for sql query and copyList return

 @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);
        //select id,title from article order by CreateDate desc limit 5
        List<Article> articles = articleMapper.selectList(queryWrapper);

        return Result.success(copyList(articles,false,false));
    }

6.4 Testing

7. Home Page - Article Archive

Articles are filed according to the year and month of the date
Because the database create_date needs a time stamp of / 1000 for bigint type, then FROM_UNIXTIME for format conversion
The SQL statement is

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 Interface Description

Interface url: /articles/listArtchives
Request method: POST
Request parameters:

Return data:

{
  "success": true,
    "code": 200,
    "msg": "success",
    "data": [
        {
            "year": "2021",
            "mouth": "6",
            "count"2
         }
            ]
}

7.2 Controller

ArticleController class

/**
     * First Page Article Archive
     * @return
     */
    @PostMapping("listArchives")
    public Result listArchives(){
        return articleService.listArchives();
    }

7.3 Service

ArticleService

/**
     * Article Archives
     * @return
     */
    Result listArchives();

Implement in ArticleServiceImpl

  /*Article Archives*/
    @Override
    public Result listArchives() {
       List<Archives> archivesList = articleMapper.listArchives();
        return Result.success(archivesList);
    }

7.4 mapper

Because the data returned by the article archive is not the direct data of the database, temporary use, not a pojo object
So create dos packages to hold nonpersistent data

Create Archives Class Archive Information

package com.lum.blog.dao.dos;

import lombok.Data;

@Data
public class Archives {
    private Integer year;

    private Integer month;

    private Long count;
}

Create 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();
}

Create ArticleMapper.xml in resource pack mapper to implement 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 Testing

8 Login Function Implementation (JWT)

8.1 Interface Description

Interface url:/login

Request method: POST

Request parameters:

Parameter NameParameter typeExplain
accountstringAccount number
passwordstringPassword

Data returned

{
    "success": true,
    "code": 200,
    "msg": "success",
    "data": "token"
}

8.2JWT Technology Implementation

JSON Web Token (JWT),It is currently the most popular cross-domain authentication solution

JWT The essence is "de-centralized", where the data is stored on the client side.

jwt can generate an encrypted token to be issued to the client when the user logs on successfully.
When requesting resources and interfaces to log in, take the token with you, and the back end verifies that the token is legal.

jwt has three components:

  • Header, {"type": "JWT", "alg": "HS256"} Fixed

  • playload, which stores customized information such as user id, expiration time, etc., can be decrypted and sensitive information cannot be stored

  • The first two points of the visa, together with key encryption, are considered secure as long as the key is not lost.

    (alg:HS256 Is an algorithm)
    

jwt verification mainly verifies whether the visa part is legal

Import Dependent Packages

 <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>

Create Tool Class 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!@#$%';			//secret key

    public static String createToken(Long userId){
        Map<String, Object> claims = new HashMap<>();
        claims.put("userId",userId);
        JwtBuilder jwtBuilder = Jwts.builder()
                .signWith(SignatureAlgorithm.HS256,jwtToken)  //Issuance algorithm with jwtToken key
                .setClaims(claims)//body data, unique, self-set
                .setIssuedAt(new Date())  //Set issuance time
                .setExpiration(new Date(System.currentTimeMillis() + 24 * 60 * 60  *1000));  //Time available every day
        return jwtBuilder.compact();
    }

    public static Map<String,Object> checkToken(String token){
        try {
            Jwt parser = Jwts.parser().setSigningKey(jwtToken).parse(token);  //Resolve jwtToken
            return (Map<String, Object>) parser.getBody();
        }catch ( Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    /*Test jwt*/
    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

Create LoginController at the controller layer for login control

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 SysUserService sysUserService;
//    Not recommended, each Service has a separate business

    @Autowired
    private LoginService loginService;

    @PostMapping
    public Result login(@RequestBody LoginParam loginParam){
        //Logon Authentication User Access User Table
        return loginService.login(loginParam);

    }

}

Business needs to be written at the Service tier

8.4Service

Create LoginService to write business

package com.lum.blog.service;

import com.lum.blog.vo.Result;
import com.lum.blog.vo.params.LoginParam;

public interface LoginService {

    /**
     * Logon function
     * @param loginParam
     * @return
     */
    Result login(LoginParam loginParam);
}

Write login parameters in the param parameter package of the vo package Logon parameters

Implementing the login method in LoginServiceImpl

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;//User table required

    @Autowired
    private RedisTemplate<String,String> redisTemplate;

    private static final String salt="lum!@#";


    @Override
    public Result login(LoginParam loginParam) {

        /**
         * 1.Check parameter validity
         * 2.Queries exist based on the username and password zone user tables
         * 3.If there are no login failures
         * 4.If present, use jwt to generate token to return to front end
         * 5.token Put it in redis, redis maps token and user information, sets expiration time, verifies token is legal, and then verifies whether it exists in redis
         */
        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); //Password Salting
        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); //Expiration Time
        return Result.success(token);
    }
}

Here, first determine if the username or password is empty, and then return if it is empty Uniform error code

 if (StringUtils.isBlank(account)||StringUtils.isBlank(password)) {
            return Result.fail(ErrorCode.PARAMS_ERROR.getCode(),ErrorCode.PARAMS_ERROR.getMsg());
        }

Salt the password again

private static final String salt="lum!@#";

password = DigestUtils.md5Hex(password + salt); //Password Salting

redis are required to mediate caching and databases
Conduct application.properties redis configuration
Set token and expiration time

  redisTemplate.opsForValue().set("TOKEN_"+token, JSON.toJSONString(sysUser),1, TimeUnit.DAYS);
  

8.5, login parameters, redis configuration, unified error code

LoginParam login parameters

package com.lum.blog.vo.params;

import lombok.Data;

@Data
public class LoginParam {

    private String account;

    private String password;
}

redis configuration

#redis configuration
spring.redis.host=localhost
spring.redis.port=6379

Uniform error code

Create ErrorCode under vo package

package com.lum.blog.vo;

public enum ErrorCode {

    PARAMS_ERROR(10001,"Error in parameters"),
    ACCOUNT_PWD_NOT_EXIST(10002,"User password does not exist!"),
    TOKEN_ERROR(10003,"Token Wrongful"),
    ACCOUNT_EXIST(10004,"Account already exists"),
    NO_PERMISSION(70001,"No access rights"),
    SESSION_TIME_OUT(90001,"session time out"),
    NO_LOGIN(90002,"Not logged in"),;


    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 Testing

Use postman for testing, because after login, you need to jump pages, token authentication, there is an interface not completed, change ends will have problems,
When the token front end is acquired, it stores b5 in the storage, which is stored locally

token can also be queried using key * in redis-ci

9. Get user information after login

9.1 Interface Description

Interface url:/users/currentUser
Request method: Get
Request parameters:

Parameter NameParameter typeExplain
AuthorZationstringHeader Information (Token)

9.2 Controller

Create UserController for user information return

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;

    //users/currentUser
    @GetMapping("currentUser")
    public Result currentUser(@RequestHeader("Authorization") String token) {  //Request Header

        return sysUserService.findUserByToken(token);
    }


}

9.3 Service

Add the findUserByToken(String token) method to the SysUserService interface

/*Query user information based on token*/
    Result findUserByToken(String token);

Writing findUserByToken(String token) in the implementation class requires creating LoginUserVo in the vo package

 @Override
    public Result findUserByToken(String token) {
        /**
         * 1.According to token validity
         * Is it empty, resolution successful, redis present
         * 2.If the check fails, an error is returned
         * 3.If successful, return the corresponding result LoginUserVo
         *
         */
        SysUser sysUser = loginService.checkToken(token);
        if (sysUser == null) {
            return Result.fail(ErrorCode.TOKEN_ERROR.getCode(),ErrorCode.TOKEN_ERROR.getMsg());
        }
        //In theory, you should write a TokenService process, which is simple here
        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; //Head portrait
}

Write a LoginService interface to add a checkToken to check if the Token is empty, do not empty Continue to determine if the userJson in redis is empty, and do not return a user object for empty

 SysUser checkToken(String token);

Implement method checkToken in LoginServiceImpl

 @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;
    }

9.4 Testing

10 Log out

Front-end clears token, back-end clears data in redis

10.1 Interface Description

Interface url:/logout
Request method: Get
Request parameters:

Parameter NameParameter typeExplain
AuthorZationstringHeader Information (Token)

10.2 Controller

Create 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

Create logout method in LoginService interface

 /**
     * Log out
     * @param token
     * @return
     */
    Result logout(String token);

Complete method in implementation class
Remove token from redis

 @Override
    public Result logout(String token) {
        redisTemplate.delete("TOKEN_" + token);
        return Result.success(null);
    }

11. Registration

sso(single sign on) Single sign-on,Later put forward login registration,Individual services,Interfaces can be provided independently

11.1 Interface Description

Interface url:/register

Request method: POST

Request parameters:

Parameter NameParameter typeExplain
accountstringAccount number
passwordstringPassword
nicknamestringNickname?

Data returned

{
    "success": true,
    "code": 200,
    "msg": "success",
    "data": "token"
}

11.2 Controller

Create 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 adds attribute nickname

Add Method to LoginService Interface

    private String nickname;

  /**
     * register
     * @param loginParam
     * @return
     */
    Result register(LoginParam loginParam)

Implement register method in LoginServiceImpl

 @Override
    public Result register(LoginParam loginParam) {
        /**
         * 1.Judging whether a parameter is legal
         * 2.Determine whether an account exists
         * 3.No registered user exists for the account
         * 4.Generate token
         * 5.Save redis and return
         * 6.Note that with transactions, registered users need to roll back whenever there is any problem in the middle
         */
        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(), "Account already used in Europe");
        }

        sysUser=new SysUser();
        sysUser.setAccount(account);                                   //Account Name
        sysUser.setNickname(nickname);                                  //Nickname?
        sysUser.setPassword(DigestUtils.md5Hex(password+salt));  //Password salt md5
        sysUser.setCreateDate(System.currentTimeMillis());              //Creation Time
        sysUser.setLastLogin(System.currentTimeMillis());               //Last Logon Time
        sysUser.setAvatar("/static/img/logo.b3a48c0.png");              //Head portrait
        sysUser.setAdmin(1);                                             //Administrator privileges
        sysUser.setDeleted(0);                                             //False Delete
        sysUser.setSalt("");                                                //salt
        sysUser.setStatus("");                                              //state
        sysUser.setEmail("");                                               //mailbox
        this.sysUserService.save(sysUser);

//Pass token
        String token = JWTUtils.createToken(sysUser.getId());
        redisTemplate.opsForValue().set("TOKEN_"+token, JSON.toJSONString(sysUser),1, TimeUnit.DAYS);
        return Result.success(token);

    }

Because you need to find and write accounts to the database, you need to add a new method to SysUserService

/**
     * Query users based on account name
     * @param account
     * @return
     */
    SysUser findUserByAccount(String account);

    /**
     * Save User
     * @param sysUser
     */
    void save(SysUser sysUser);

Implement 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);
    }

Implement save(SysUser sysUser)

public void save(SysUser sysUser) {
        //Saving the user id is automatically generated
        //Generate distributed IDS by default, using the snowflake algorithm
        //mybatis-plus
        this.sysUserMapper.insert(sysUser);

    }

Open database transaction
Open @Transactional in SysUserServiceImpl

11.4 Testing


12 Logon Interceptor

Every time you access a resource that needs to be logged in, you need to make a judgment in the code. Once the login logic changes, the code changes, which is not appropriate
Can login judgment be unified then?
Yes, use an interceptor to intercept logins. If an interface is accessible only when login requirements are encountered, the interceptor returns directly and jumps to the login page.

1. Interceptors can be implemented in a variety of ways ( SpringSecurity,inherit HandlerInterceptor,Also shiro
	Can be achieved), here we choose inheritance HandlerInterceptor
2. There are two steps to this, first writing inheritance classes, and second writing inheritance classes in webMVC Inside Configuration

12.1 Interceptor Implementation

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 {
        //Execute before executing the Controller method (Handle)
        // What does prerepresent before
        /**
         * 1.You need to decide if the requested interface path is a HandleMethod(controller method)
         * 2.Determine if token is empty and not logged in
         * 3.Not empty, login validation loginService checkToken
         * 4.If the certification is released successfully
         */

        if (!(handler instanceof HandlerMethod)) {
            //Simply put, Handler is one of the methods in the controller
            //handle may be a RequestResourceHandle springboot program accessing static resources Default de-classpath static directory query
            return true;
        }
        Got to get it Token,Why, because when the front end comes in, we use@RequestHeader("Authorization") Incoming
        String token = request.getHeader("Authorization");
		//Log problem, need to import @slf4 under lombok
        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===================");


        /*token Empty, intercept*/
        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 json information (fastjson transforms)
            return false;
        }
        /*User empty, intercept*/
        SysUser sysUser = loginService.checkToken(token);
        if (sysUser == null){
            //Here is the information returned by the error
            Result result = Result.fail(ErrorCode.NO_LOGIN.getCode(),ErrorCode.NO_LOGIN.getMsg());
            //To tell the browser that we want to return this type
            response.setContentType("application/json;charset=utf8");
            //What's returned is a result type, only converting to JSON type
            response.getWriter().print(JSON.toJSONString(result));
            return false;
        }

        //Logon validation successful, release
        return true;
    }
}

12.2 Configure Interception Path

Configure Intercept Path in WebMvcConfig

//Interceptor Injection
@Autowired
    private LoginInterceptor loginInterceptor;
 @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //Configure Intercept Interface
//        All interfaces except login registration
//        registry.addInterceptor(loginInterceptor).addPathPatterns("/**").excludePathPatterns("/login").excludePathPatterns("/register");
        registry.addInterceptor(loginInterceptor)
                .addPathPatterns("/test");
    }

13. ThreadLocal saves user information

Want to controller How to get user information directly from?

What is ThreadLocal

I. ThreadLocal What's this

From the name we can see ThreadLocal Called thread variable, which means ThreadLocal The variable populated in belongs to the current thread and is isolated from other threads. ThreadLocal A copy of the variable is created in each thread, so each thread can access its own internal copy variable.

It's easy to understand literally, but it's not so easy from a practical point of view. As a point of common interview questions, there are plenty of scenarios to use:

1,When passing objects across layers, use ThreadLocal Multiple passes can be avoided, breaking the constraints between levels.

2,Data isolation between threads

3,Perform transactional operations to store thread transaction information.

4,Database connection, Session Session management.

13.1 Use ThreadLocal

Now create the UserThreadLocal class under the utils package

package com.lum.blog.utils;

import com.lum.blog.dao.pojo.SysUser;

public class UserThreadLocal {
    //This sentence means declare private
    private UserThreadLocal (){
    }
    //Instantiate a ThreadLocal class, that is, enable
    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();
    }

}

Now that you are saving user information
Modifications to LoginIntercept, which are added here since they are validated here

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 {
        //Execute before executing the Controller method (Handle)
        // What does prerepresent before
        /**
         * 1.You need to decide if the requested interface path is a HandleMethod(controller method)
         * 2.Determine if token is empty and not logged in
         * 3.Not empty, login validation loginService checkToken
         * 4.If the certification is released successfully
         */

        if (!(handler instanceof HandlerMethod)) {
            //Simply put, Handler is one of the methods in the controller
            //handle may be a RequestResourceHandle springboot program accessing static resources Default de-classpath static directory query
            return true;
        }
        Got to get it Token,Why, because when the front end comes in, we use@RequestHeader("Authorization") Incoming
        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===================");


        /*token Empty, intercept*/
        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 json information (fastjson transforms)
            return false;
        }
        /*User empty, intercept*/
        SysUser sysUser = loginService.checkToken(token);
        if (sysUser == null){
            //Here is the information returned by the error
            Result result = Result.fail(ErrorCode.NO_LOGIN.getCode(),ErrorCode.NO_LOGIN.getMsg());
            //To tell the browser that we want to return this type
            response.setContentType("application/json;charset=utf8");
            //What's returned is a result type, only converting to JSON type
            response.getWriter().print(JSON.toJSONString(result));
            return false;
        }

        //Logon validation successful, release
        //Get user information directly in the controller
        UserThreadLocal.put(sysUser);
        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        //If you don't delete information from ThreadLocal, you risk internal disclosure
        UserThreadLocal.remove();
    }
}


Remember to clear the information at the end to avoid memory leaks

  @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        //If you don't delete information from ThreadLocal, you risk internal disclosure
        UserThreadLocal.remove();
    }

13.2 Testing

Testing in 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 {
    /**
     * Used to test interceptors
     */
    @RequestMapping
    public Result test(){
        SysUser sysUser = UserThreadLocal.get();
        System.out.println(sysUser);
        return Result.success(null);
    }
}

What exactly does 13.3ThreadLocal (local thread) do?

  1. Let's say that, like one of our requests, when you start a process and you let it bind to your process, it will be deeply tied together (for the purpose of binding user information).
  2. Why delete it after that because once a memory leak is serious

What you need to know is that a thread can have multiple threads ThreadLocal


	Every last Thread Maintain one ThreadLocalMap, key For use**Weak reference**Of ThreadLocal Example,
value Is a copy of the thread variable.

	**Strong Reference**,With the most common reference, an object has a strong reference and will not be recycled by the garbage collector.When memory
 Not enough space, Java Virtual machines prefer to throw OutOfMemoryError Error, causes the program to terminate abnormally, and does not recycle this
 Species object.

	**If you want to disassociate a strong reference from an object, you can explicitly assign a reference to null,This allows
JVM The object is recycled at the appropriate time.**

	**Weak reference**,JVM When garbage collection occurs, objects associated with weak references are recycled regardless of whether there is enough memory.
stay java In the java.lang.ref.WeakReference Class to represent.

	The one above key For use**Weak reference**Of ThreadLocal Instance, when the one in our thread
ThreadLocal Was this a weak reference after being killed by the garbage collection mechanism? Key No more, but this is
Map Collection, Value Will always exist, all to be deleted manually

14. Article Details

14.1 Interface Description

Interface url:/article/view/(id)
Request method: POST
Request parameters:

Parameter NameParameter typeExplain
idlongArticle ID (path parameter)

Data returned

{
    "success": true,
    "code": 200,
    "msg": "success",
    "data": "token"
}

14.2 Tables and corresponding POJOs

CREATE TABLE `blog`.`ms_article_body`  (
  `id` bigint(0) NOT NULL AUTO_INCREMENT,
  #This is the content of the article
  `content` longtext CHARACTER SET utf8 COLLATE utf8_general_ci NULL,
  #Article Content Page
  `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;
}

Category table

//Category table
CREATE TABLE `blog`.`ms_category`  (
  `id` bigint(0) NOT NULL AUTO_INCREMENT,
  #Classified icons
  `avatar` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,
  #Name of category Icon
  `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

Add a method to find articles in ArticleConller

 /**
     * View article details
     * @param articleId
     * @return
     */
    @PostMapping("view/{id}")
    public  Result findArticleById(@PathVariable("id") Long articleId){
        return articleService.findArticleById(articleId);

    }

14.3 Service

What you need to add ArticleBodyVo and CategoryCo to articleVo to return the article

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;  //brief introduction

    private int commentCounts;

    private int ViewCounts;

    private int weight;   //Set Top

    private String createDate;  //Creation Time

    private String author;

    private ArticleBodyVo body;

    private List<TagVo> tags;

    private CategoryVo category;
}

Add Find Article Method Interface to articleService

/**
     * Query article details
     * @param articleId
     * @return
     */
    Result findArticleById(Long articleId);
 The following are all in ArticleServiceImpl Written in

Implement method in articleServiceImpl

 /*Article Details*/
    @Override
    public Result findArticleById(Long articleId) {
        /**
         * 1.Query article information based on id
         * 2.Make Association queries based on bodyId and categoryId
         */

        Article article = this.articleMapper.selectById(articleId);
        ArticleVo articleVo = copy(article, true, true,true,true);

        return Result.success(articleVo);
    }
/*Article Body Display*/
    private ArticleBodyVo findArticleBodyById(Long bodyId) {
        ArticleBody articleBody = articleBodyMapper.selectById(bodyId);
        ArticleBodyVo articleBodyVo = new ArticleBodyVo();
        articleBodyVo.setContent(articleBody.getContent());

        return articleBodyVo;
    }

Find details based on the article if you want to show part of the article

ArticleVo is required to display content
Overloaded cpoyList can display different article content based on parameters

 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) {
        List<ArticleVo> articleVoList = new ArrayList<>();
        for (Article record : records) {
            articleVoList.add(copy(record,isTag,isAuthor,isBody,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;
    }

Article articleVo shows copyList overloaded copy method

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;
    }

Don't forget to inject relationships

 @Autowired
    private ArticleMapper articleMapper;

    @Autowired
    private TagService tagService;

    @Autowired
    private SysUserService sysUserService;

    @Autowired
    private CategoryService categoryService;

    @Autowired
    private ArticleBodyMapper articleBodyMapper;

You need to add articleBodyMapper to get the body

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.4 Testing

15. Update of reading numbers

Use of thread pools

Question introduction

@Override
    public ArticleVo findArticleById(Long id) {
        Article article = articleMapper.selectById(id);

        //Having checked the articles and added reading numbers, is there any problem?
        //The answer is yes, it should have returned the data directly. At this time, an update operation was made, adding write lock to update the time, blocking other reading operations, and the new energy will be lower.
        //And the update increases the time-consuming of this interface, once the update has a problem, it can not affect our other things such as: look at the article or what
        //So how to optimize it?,-->All think of thread pools
        //You can throw the update operation into the thread pool without affecting it or having nothing to do with the main thread
        return copy(article,true,true,true,true);
    }

Configuration of Thread Pool
Create a new ThreadPoolConfig under com.lum.blog.config to open the thread pool and configure it accordingly

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;

/**
 * @author lum
 * @date 2021/9/3
 */
@Configuration
@EnableAsync
//Open multithreading

public class ThreadPoolConfig {
    @Bean("taskExecutor")
    public Executor asyncServiceExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        // Set Number of Thread Cores
        executor.setCorePoolSize(5);
        // Set maximum number of threads
        executor.setMaxPoolSize(20);
        // Configure Queue Size
        executor.setQueueCapacity(Integer.MAX_VALUE);
        // Set thread active time
        executor.setKeepAliveSeconds(60);
        // Set Thread Name
        executor.setThreadNamePrefix("Lum Blog");
        //Wait for all tasks and close threads after completion
        executor.setWaitForTasksToCompleteOnShutdown(true);
        //Perform Initialization
        executor.initialize();

        return executor;
    }
}


15.1 Controller

Article business, no controller

15.2 Service

Create a new ThreadService under com.lum.blog.service.Impl to complete the use of the thread pool

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;

/**
 * @author lum
 * @date 2021/9/3
 */

@Component
public class ThreadService {


    //Expect this operation to be performed in the thread pool without affecting the main thread


    @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());
        // Set a thread security for multithreaded conditions
        updateWrapper.eq(Article::getViewCounts,viewCounts);
        // update article set view_count = 100 where view_count =99 and id = 1
        articleMapper.update(articleUpdate, updateWrapper);

        try {
            Thread.sleep(2000);
            System.out.println("Update complete!");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
}

This is a bug that will occur
Update the number of views by updating the number of comments to 0
This is because in Article's pojo relational mapping to the database, the default value of the int base type is 0, which affects the sql statement
So don't use basic types in pojo

package com.lum.blog.dao.pojo;

import lombok.Data;
/*
Blog Management
 */
@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;

    /**
     * Author id
     */
    private Long authorId;
    /**
     * Content id
     */
    private Long bodyId;
    /**
     *Category id
     */
    private Long categoryId;

    /**
     * Set Top
     */
    private Integer weight;


    /**
     * Creation Time
     */
    private Long createDate;
}



16 Comments List

16.1 Interface Description

Interface url:/comments/article/(id)
Request method: GET
Request parameters:

Parameter NameParameter typeExplain
idlongArticle ID (path parameter)

Data returned

{
    "success": true,
    "code": 200,
    "msg": "success",
    "data": [
    {
    	"id":12,
    	"author":{
    	"nickname":"Zhao Yun",
    	"avatar":"",
    	"id":1
    	},
    	"content":"111"
    	"childrens":[],
    	"createDate":"2021-9-1 08:35",
    	"level":2,
    	"toUser":{
    		"id":12,
    		"nickname":"Zhao Yun",
    		"id":1
    	}
    }
    ],
    "createDate":"2021-9-1 08:35",
    "level":1,
    "toUser":null
    }
  ]
    
}

New Comment mapping relationship under pojo package

package com.lum.blog.dao.pojo;

import lombok.Data;

/**
 * @author lum
 * @date 2021/9/3
 */

@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;
}

Create two Vo s to display

CommentVo

package com.lum.blog.vo;

import lombok.Data;

import java.util.List;

/**
 * @author lum
 * @date 2021/9/3
 */
@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;

/**
 * @author lum
 * @date 2021/9/3
 */
@Data
public class UserVo {

    private String nickname;

    private String avatar;

    private Long id;
}

16.2 Controller

Create 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.*;

/**
 * @author lum
 */
@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

Create Interface CommentService

package com.lum.blog.service;

import com.lum.blog.vo.Result;

/**
 * @author lum
 * @date 2021/9/3
 */
public interface CommentsService {
    /**
     * Find comments based on article id
     * @param articleId
     * @return
     */
    Result commentsByArticleId(Long articleId);
}

CommentServiceImpl Implementation Method

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;

/**
 * @author lum
 * @date 2021/9/3
 */
@Service
public class CommentsServiceImpl implements CommentsService {

    @Autowired
    private CommentMapper commentMapper;

    @Autowired
    private SysUserService sysUserService;

    @Override
    public Result commentsByArticleId(Long articleId) {
        /*
          1.Query the comment list based on article id and from comment
          2.Query author information based on author id
          3.If level=1, query for subcomments,\
          4.If there is a query based on the comment id

         */

        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();
        //copy the same type into commentVo
        BeanUtils.copyProperties(comment, commentVo);
        //Time Formatting
        commentVo.setCreateDate(new DateTime(comment.getCreateDate()).toString("yyyy-MM-dd HH:mm"));
        //Author Information
        Long authorId = comment.getAuthorId();
        UserVo userVo = this.sysUserService.findUserVoById(authorId);
        commentVo.setAuthor(userVo);

        //Sub-comments
        Integer level = comment.getLevel();
        if (level == 1){
            Long id = comment.getId();
            List<CommentVo> commentVoList = findCommentByParentId(id);
            commentVo.setChildrens(commentVoList);
        }

        //To whom does toUser comment
        if (level > 1) {
            Long toUid = comment.getToUid();
            UserVo toUserVo = this.sysUserService.findUserVoById(toUid);
            commentVo.setToUser(toUserVo);

        }

        return commentVo;

    }

    //Subcomment Query
    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));

    }

}

The findUserVoById(Long id) method needs to be added to SysUserServiceImpl


 @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("empty");

        }
        UserVo userVo = new UserVo();
        BeanUtils.copyProperties(sysUser,userVo);
        return userVo;
    }

Testing

17. Comment function

17.1 Interface Description

Interface url:/comments/create/change
Request method: POST
Request parameters:

Parameter NameParameter typeExplain
articleidlongArticle id
contentlstringComments
parentlongParent comment id
toUseridlongCommented user id

Create CommentParam

package com.lum.blog.vo.params;

/**
 * @author lum
 * @date 2021/9/3
 */
@Data
public class CommentParam {
    
    private Long articleId;
    
    private String content;
    
    private Long parentId;
    
    private Long toUserId;
}

17.2 Need to join the login interceptor

Sign in to comment
WebMvcConfig

@Override
    public void addInterceptors(InterceptorRegistry registry) {
        //Configure Intercept Interface
//        All interfaces except login registration
//        registry.addInterceptor(loginInterceptor).addPathPatterns("/**").excludePathPatterns("/login").excludePathPatterns("/register");
        registry.addInterceptor(loginInterceptor)
                .addPathPatterns("/comments/create/change")
                .addPathPatterns("/test");
    }

Json serialization is required because Long front-end resolution of Distributed IDS is too long and will change the value
CommentVo

 @JsonSerialize(using = ToStringSerializer.class)
    private Long id;

17.3 Controller

Add interface in commentController

   @PostMapping("create/change")
    public Result comment(@RequestBody CommentParam commentParam){
        return commentsService.comment(commentParam);
    }


14.4 Service

CommentService

 /**
     * comment
     * @param commentParam
     * @return
     */
    Result comment(CommentParam commentParam);

Implementation Method CommentServiceImpl

 //Comment
    
    @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 id is empty, parent comment, otherwise child comment
        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);

        return Result.success(null);
    }

18. Writing articles

Three interfaces are required:

  1. Get all article categories
  2. Get all tags
  3. Publish articles

18.1 All Article Categories

Interface Description
Interface url:/categorys
Request method: GET
Request parameters:
|Parameter Name|Parameter Type|Description|

Controller

CategoryController

  //categroys
    @GetMapping
    public Result categories(){
        return categoryService.findAll();
    }

Service

CategroyService

//Find Categories
 Result findAll();

CategoryServiceImpl

 @Override
    public Result findAll() {
        LambdaQueryWrapper<Category> queryWrapper = new LambdaQueryWrapper<>();
        //queryWrapper.select(Category::getId,Category::getCategoryName);
        List<Category> categories = categoryMapper.selectList(queryWrapper);
        //Objects for page interaction
        return Result.success(copyList(categories));
    }

    public CategoryVo copy(Category category){
        CategoryVo categoryVo = new CategoryVo();
        BeanUtils.copyProperties(category,categoryVo);
        //categoryVo.setId(String.valueOf(category.getId()));
        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 All article labels

Interface Description
Interface url:/tags
Request method: GET
Request parameters:
|Parameter Name|Parameter Type|Description|

Controller

TagsController

 /**
     * All article labels
     * @return
     */
    @GetMapping
    public Result findAll() {
        return tagService.findAll();
    }

TagService

  /**
     * Query all article labels
     * @return
     */
    Result findAll();

TagsServiceImpl

    @Override
    public Result findAll() {
        List<Tag> tags = this.tagMapper.selectList(new LambdaQueryWrapper<>());
        return Result.success(copyList(tags));
    }

19. Publish articles

Interface Description

Interface url:/articles/publish
Request method: POST
Request parameters:

Parameter NameParameter typeExplain
titlestringArticle Title
idlongArticle ID (edited with value)
bodyobjectArticle Content
categoryjsonArticle Categories
summarystringArticle Overview
tagsjsonArticle Label

Create ArticleParam

@Data
public class ArticleParam {

    private Long id;

    private ArticleBodyParam body;

    private CategoryVo category;

    private String summary;

    private List<TagVo> tags;

    private String title;
}

Create ArticBodyParam

@Data
public class ArticleBodyParam {

    private String content;

    private String contentHtml;

}

Create article and tag associated table objects under pojo

@Data
public class ArticleTag {

    private Long id;

    private Long articleId;

    private Long tagId;

New ArticleTagMapper Interface

package com.lum.blog.dao.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.lum.blog.dao.pojo.ArticleTag;

/**
 * @author lum
 * @date 2021/9/4
 */
public interface ArticleTagMapper extends BaseMapper<ArticleTag> {
}

Serialize the id in ArticleVo id to prevent loss of previous segment resolution

 @JsonSerialize(using = ToStringSerializer.class)
    private Long id;

ArticleController

 @PostMapping("publish")
    public Result publish(@RequestBody ArticleParam articleParam){
        return articleService.publish(articleParam);
    }

ArticleService

   /**
     * Article Publishing
     * @param articleParam
     * @return
     */
    Result publish(ArticleParam articleParam);

ArticleServiceImpl

    @Autowired
    private ArticleTagMapper articleTagMapper;


    /**
     * 1.Publishing Articles for Article Objects
     * 2. Author id current logon user
     * 3. Tags Add tags to the associated table
     * 4. body Content Store article bodyId
     * @param articleParam
     * @return
     * This interface will be added to login interception
     */
    @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()));
        //An article id is generated after insertion
        this.articleMapper.insert(article);
        //tag
        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);
            }
        }
        //body
        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);
        //Convert id to string and put in map
        Map<String,String> map = new HashMap<>();
        map.put("id",article.getId().toString());
        return Result.success(map);
    }

Posted by siwelis on Fri, 03 Sep 2021 09:57:08 -0700