005 start developing blog admin module Chapter 1

Start developing blog admin module Chapter 1

Build table

Create a table according to the permission table relationship in Chapter 003 The sql file will be uploaded later

Generate basic addition, deletion, modification and query according to the code generator

The basic coding is not written here. There is no technical content and it takes a lot of time
The code generator uses the code generator provided by Renren open source, Everyone open source , others can be viewed by clicking the link
Click the link, and then click Ren generator

Download code

Unzip the downloaded package, and then copy the code to the project

Note that the copied path, like other projects, is a sub project
It should be noted that

  • After the project is copied in, the probability idea still cannot be recognized as maven project. As before, click add as maven project
  • Note the version of JDK. I use version 11, so I need to modify it. Most people may use jdk1 8 if this version is used, it does not need to be modified. The code generator also uses 1.8
  • Because our parent project is only a project used for aggregation and does not have inherited functions (I think dependency management is too cumbersome), the default code generator pom file depends on the parent project to obtain dependencies from the local warehouse instead of the parent project
<parent>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-parent</artifactId>
	<version>2.2.6.RELEASE</version>
	<relativePath/> <!-- Newly added -->
</parent>

Code generator structure


We mainly modify two, the first is application YML, the second is generator Properties, these two files. If you don't understand the contents of the file, you can only say what we need to modify

application.yml

  • Considering the port number, 80 is used by default. It is recommended to change it to other port numbers. I change it here to 12001
  • The database I use is the mysql database, so I definitely need to modify the user name and password of the database. Others will not be modified. Change the user name and password to the account password of my own database

generator.properties

This file is mainly the package name and author of the generated code. Check it yourself
I modify it here as follows:

  • mainPath=com. amnesia. my_ The path of blog is the same for all modules
  • package=com.amnesia.my_blog package name
  • moduleName=admin is changed to its own module name. At present, it is admin, and the subsequent core modules will be changed to core
  • author=amnesia author name
  • email=1211441748@qq.com Mailbox
  • tablePrefix = # table prefix (the class name will not contain the table prefix). The default is tablePrefix=tb_, Because the table we built has no prefix, we need to change the table prefix to empty in this place
    Other information is type conversion information, which is not recommended to be modified
    The third file that needs to be modified is the controller under the template file that needs to be modified in this project java. VM file, remove all the annotations about shiro and the imported classes. The current permission framework of this project uses spirng security
  • import org.apache.shiro.authz.annotation.RequiresPermissions;
  • @RequiresPermissions(" m o d u l e N a m e : {moduleName}: moduleName:{pathName}:list")
  • @RequiresPermissions(" m o d u l e N a m e : {moduleName}: moduleName:{pathName}:info")
  • @RequiresPermissions(" m o d u l e N a m e : {moduleName}: moduleName:{pathName}:save")
  • @RequiresPermissions(" m o d u l e N a m e : {moduleName}: moduleName:{pathName}:update")
  • @RequiresPermissions(" m o d u l e N a m e : {moduleName}: moduleName:{pathName}:delete")
    To delete the above 6 lines, you can copy them directly and find and delete them in the file If the subsequent permission framework uses shiro or directly uses the background management template of Renren open source, the third file may not be modified
    After the above changes, you can start the code generator project
    The above database connection must be correct, or the content will be empty after the project is started
    Start, access

    Click Ren Ren fast on the left and the multi selection box on the right to select the table. If a page is incomplete, you can click the bottom box and change it to 30,50100 per page. Click generate code to download the generated code

Unzip the downloaded file directory as shown in the figure above The sql file can be ignored. It is mainly about shiro permissions. It is not used in the current project. The main folder is the most common directory structure. As for the views file under src, it is mainly vue components, which will be used in the front-end page of development later I won't introduce it here, and I don't use these components. You can delete them
Copy all the files in the admin file to the project. At this time, the project will report a large number of errors. Now deal with these errors

Dealing with problems

Add a common dependency to the pom file. This process is generally handled when creating a project

		<dependency>
			<groupId>com.amnesia.my_blog.common</groupId>
			<artifactId>blog-common</artifactId>
			<version>0.0.1-SNAPSHOT</version>
		</dependency>

Create a uniform return result object R in the common project

Object code R

import org.apache.http.HttpStatus;
import java.util.HashMap;
import java.util.Map;
public class R<T> extends HashMap<String, Object> {
	private static final long serialVersionUID = 1L;

	private T data;
	public T getData(){
		return data;
	}
	public void setData(T data){
		this.data = data;
	}
	
	public R() {
		put("code", 0);
		put("msg", "success");
	}
	
	public static R error() {
		return error(HttpStatus.SC_INTERNAL_SERVER_ERROR, "Unknown exception, please contact the administrator");
	}
	
	public static R error(String msg) {
		return error(HttpStatus.SC_INTERNAL_SERVER_ERROR, msg);
	}
	
	public static R error(int code, String msg) {
		R r = new R();
		r.put("code", code);
		r.put("msg", msg);
		return r;
	}

	public static R ok(String msg) {
		R r = new R();
		r.put("msg", msg);
		return r;
	}
	
	public static R ok(Map<String, Object> map) {
		R r = new R();
		r.putAll(map);
		return r;
	}
	
	public static R ok() {
		return new R();
	}

	@Override
	public R put(String key, Object value) {
		super.put(key, value);
		return this;
	}
	public Integer getCode(){
		return (Integer) this.get("code");
	}
}

The Http status code is used, so the following dependencies need to be introduced into the pom file under the common module, and the R file will no longer report errors

		<dependency>
			<groupId>org.apache.httpcomponents</groupId>
			<artifactId>httpcore</artifactId>
			<version>4.4.13</version>
			<scope>compile</scope>
		</dependency>

Create a paging tool class in the common project

PageUtils object code

import com.baomidou.mybatisplus.core.metadata.IPage;
import java.io.Serializable;
import java.util.List;
public class PageUtils implements Serializable {
	private static final long serialVersionUID = 1L;
	/**
	 * Total records
	 */
	private int totalCount;
	/**
	 * Records per page
	 */
	private int pageSize;
	/**
	 * PageCount 
	 */
	private int totalPage;
	/**
	 * Current number of pages
	 */
	private int currPage;
	/**
	 * List data
	 */
	private List<?> list;
	
	/**
	 * paging
	 * @param list        List data
	 * @param totalCount  Total records
	 * @param pageSize    Records per page
	 * @param currPage    Current number of pages
	 */
	public PageUtils(List<?> list, int totalCount, int pageSize, int currPage) {
		this.list = list;
		this.totalCount = totalCount;
		this.pageSize = pageSize;
		this.currPage = currPage;
		this.totalPage = (int)Math.ceil((double)totalCount/pageSize);
	}

	/**
	 * paging
	 */
	public PageUtils(IPage<?> page) {
		this.list = page.getRecords();
		this.totalCount = (int)page.getTotal();
		this.pageSize = (int)page.getSize();
		this.currPage = (int)page.getCurrent();
		this.totalPage = (int)page.getPages();
	}

	/**
	 * Total number
	 * @return
	 */
	public int getTotalCount() {
		return totalCount;
	}

	public void setTotalCount(int totalCount) {
		this.totalCount = totalCount;
	}

	public int getPageSize() {
		return pageSize;
	}

	public void setPageSize(int pageSize) {
		this.pageSize = pageSize;
	}

	public int getTotalPage() {
		return totalPage;
	}

	public void setTotalPage(int totalPage) {
		this.totalPage = totalPage;
	}

	public int getCurrPage() {
		return currPage;
	}

	public void setCurrPage(int currPage) {
		this.currPage = currPage;
	}

	public List<?> getList() {
		return list;
	}

	public void setList(List<?> list) {
		this.list = list;
	}
	
}

The paging interface of mybatis plus is used. Therefore, the dependency of mybatis plus is introduced into the pom file. The second dependency is mysql driven dependency. I use version 8 here, so I need to formulate a version This place mainly considers the path of mysql driver package after version 8.0. If there is a problem, Baidu will not report an error in the PageUtils file

<!--        Import mybatis-plus-->
		<dependency>
			<groupId>com.baomidou</groupId>
			<artifactId>mybatis-plus-boot-starter</artifactId>
			<version>3.3.1</version>
		</dependency>
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>8.0.18</version>
		</dependency>

Create a Query object in the common project

Query object code

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.beecome.common.xss.SQLFilter;
import org.apache.commons.lang.StringUtils;

import java.util.Map;

/**
 * Query parameters
 *
 * @author Mark sunlightcs@gmail.com
 */
public class Query<T> {

    public IPage<T> getPage(Map<String, Object> params) {
        return this.getPage(params, null, false);
    }

    public IPage<T> getPage(Map<String, Object> params, String defaultOrderField, boolean isAsc) {
        //Paging parameters
        long curPage = 1;
        long limit = 10;

        if(params.get(Constant.PAGE) != null){
            curPage = Long.parseLong((String)params.get(Constant.PAGE));
        }
        if(params.get(Constant.LIMIT) != null){
            limit = Long.parseLong((String)params.get(Constant.LIMIT));
        }

        //Paging object
        Page<T> page = new Page<>(curPage, limit);

        //Paging parameters
        params.put(Constant.PAGE, page);

        //sort field
        //Prevent SQL injection (because sidx and order are sorted by splicing SQL, there will be SQL injection risk)
        String orderField = SQLFilter.sqlInject((String)params.get(Constant.ORDER_FIELD));
        String order = (String)params.get(Constant.ORDER);


        //Front end field sorting
        if(StringUtils.isNotEmpty(orderField) && StringUtils.isNotEmpty(order)){
            if(Constant.ASC.equalsIgnoreCase(order)) {
                return  page.addOrder(OrderItem.asc(orderField));
            }else {
                return page.addOrder(OrderItem.desc(orderField));
            }
        }

        //If there is no sort field, no sort is performed
        if(StringUtils.isBlank(defaultOrderField)){
            return page;
        }

        //Default sort
        if(isAsc) {
            page.addOrder(OrderItem.asc(defaultOrderField));
        }else {
            page.addOrder(OrderItem.desc(defaultOrderField));
        }

        return page;
    }

Query object uses other classes, so create Constant class, SQLFilter class,
Constant object code

public class Constant {
	/** Super administrator ID */
	public static final int SUPER_ADMIN = 1;
    /**
     * Current page number
     */
    public static final String PAGE = "page";
    /**
     * Number of records per page
     */
    public static final String LIMIT = "limit";
    /**
     * sort field
     */
    public static final String ORDER_FIELD = "sidx";
    /**
     * sort order
     */
    public static final String ORDER = "order";
    /**
     *  Ascending order
     */
    public static final String ASC = "asc";
	/**
	 * Menu type
	 * 
	 * @author chenshun
	 * @email sunlightcs@gmail.com
	 * @date 2016 November 15, 2014 1:24:29 PM
	 */
    public enum MenuType {
        /**
         * catalogue
         */
    	CATALOG(0),
        /**
         * menu
         */
        MENU(1),
        /**
         * Button
         */
        BUTTON(2);

        private int value;

        MenuType(int value) {
            this.value = value;
        }

        public int getValue() {
            return value;
        }
    }
    
    /**
     * Scheduled task status
     * 
     * @author chenshun
     * @email sunlightcs@gmail.com
     * @date 2016 12:07:22 am, December 3
     */
    public enum ScheduleStatus {
        /**
         * normal
         */
    	NORMAL(0),
        /**
         * suspend
         */
    	PAUSE(1);

        private int value;

        ScheduleStatus(int value) {
            this.value = value;
        }
        
        public int getValue() {
            return value;
        }
    }

    /**
     * Cloud service provider
     */
    public enum CloudService {
        /**
         * Seven cattle cloud
         */
        QINIU(1),
        /**
         * Alibaba cloud
         */
        ALIYUN(2),
        /**
         * Tencent cloud
         */
        QCLOUD(3);

        private int value;

        CloudService(int value) {
            this.value = value;
        }

        public int getValue() {
            return value;
        }
    }

}

SQLFilter object code

import com.amnesia.my_blog.common.exception.RRException;
import org.apache.commons.lang.StringUtils;

/**
 * SQL filter
 *
 * @author Mark sunlightcs@gmail.com
 */
public class SQLFilter {

    /**
     * SQL Injection filtration
     * @param str  String to be verified
     */
    public static String sqlInject(String str){
        if(StringUtils.isBlank(str)){
            return null;
        }
        //Remove the '| | | \ character
        str = StringUtils.replace(str, "'", "");
        str = StringUtils.replace(str, "\"", "");
        str = StringUtils.replace(str, ";", "");
        str = StringUtils.replace(str, "\\", "");

        //Convert to lowercase
        str = str.toLowerCase();

        //Illegal character
        String[] keywords = {"master", "truncate", "insert", "select", "delete", "update", "declare", "alter", "drop"};

        //Determine whether illegal characters are included
        for(String keyword : keywords){
            if(str.indexOf(keyword) != -1){
                throw new RRException("Contains illegal characters");
            }
        }

        return str;
    }

The SQLFilter object uses other classes, so the RRException class is created
RRException object code

public class RRException extends RuntimeException {
	private static final long serialVersionUID = 1L;
	
    private String msg;
    private int code = 500;
    
    public RRException(String msg) {
		super(msg);
		this.msg = msg;
	}
	
	public RRException(String msg, Throwable e) {
		super(msg, e);
		this.msg = msg;
	}
	
	public RRException(String msg, int code) {
		super(msg);
		this.msg = msg;
		this.code = code;
	}
	
	public RRException(String msg, int code, Throwable e) {
		super(msg, e);
		this.msg = msg;
		this.code = code;
	}

	public String getMsg() {
		return msg;
	}

	public void setMsg(String msg) {
		this.msg = msg;
	}

	public int getCode() {
		return code;
	}

	public void setCode(int code) {
		this.code = code;
	}
}

The above also uses the StringUtils class, which is in commons Lang, so import dependencies in pom

		<dependency>
			<groupId>commons-lang</groupId>
			<artifactId>commons-lang</artifactId>
			<version>2.6</version>
			<scope>compile</scope>
		</dependency>

SQLFilter class will no longer report errors, Query class will no longer report errors, and other classes will no longer report errors

Custom exception handling

However, an RRException class was created during the above writing process, but it is actually useless at present, because this class inherits the runtime exception class. This class mainly handles runtime exceptions and corresponds to the processing results, but it is not intercepted now
Therefore, it is necessary to use @ ControllerAdvice to handle the exceptions of the methods annotated by @ RequestMapping in the controller to realize custom exception handling. Therefore, the common module needs to modify the springboot launcher to a web launcher,

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter</artifactId>
		</dependency>
		Change to
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

Therefore, it can be found that common can actually be modified into the inheritance project of the whole project. All projects obtain dependencies from this project, but individuals prefer to centralize the dependencies and do not need to remove them through tags. Therefore, those with obsessive-compulsive disorder in this place can modify the project structure themselves
Create the project's exception class BlogException

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

@Data
@AllArgsConstructor  //Generate parametric construction method
@NoArgsConstructor   //Generate parameterless constructs
public class BlogException extends RuntimeException {
    private Integer code;//Status code
    private String msg;//Abnormal information
}

Create global exception class
Abnormal conditions will be matched from top to bottom, and the matching will stop and continue downward

import com.amnesia.my_blog.common.utils.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

@ControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
    //Specific exception arithmeticexception class
    @ExceptionHandler(ArithmeticException.class)
    @ResponseBody //To return data
    public R error(ArithmeticException e) {
        e.printStackTrace();
        return R.error();
    }
    //Custom exception
    @ExceptionHandler(BlogException.class)
    @ResponseBody //To return data
    public R error(BlogException e) {
        log.error(e.getMessage());
        e.printStackTrace();
        return R.error().put("code",e.getCode()).put("msg",e.getMsg());
    }
	//Custom exception
    @ExceptionHandler(RRException.class)
    @ResponseBody //To return data
    public R error(RRException e) {
        log.error(e.getMessage());
        e.printStackTrace();
        return R.error().put("code",e.getCode()).put("msg",e.getMsg());
    }
    //All exceptions class
    @ExceptionHandler(Exception.class)
    @ResponseBody //To return data
    public R error(Exception e) {
        log.error(e.getMessage());
        e.printStackTrace();
        return R.error().put("code",-1).put("msg","Program exception");
    }
}

Create the basic configuration file application yml

server:
  port: 12100
spring:
  application:
    name: blog-admin
  jackson:
    date-format: yyyy-MM-dd HH:mm:ss
    time-zone: GMT+8
  profiles:
    active: dev
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  mapper-locations: classpath:/mapper/**/*.xml

Create the development environment configuration file application-dev.yml

Note the connection driver of mysql, and the difference between version 5 and version 8

spring:
  datasource:
    url: jdbc:mysql://192.168.2.200:3306/mfll_admin
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver

Start the project and find no other problems

Configure knife4j

Add dependencies in the pom file of the commonm module

<dependency>
    <groupId>com.github.xiaoymin</groupId>
    <artifactId>knife4j-spring-boot-starter</artifactId>
    <version>2.0.7</version>
</dependency>

Create knife4j configuration class
Knife4jConfig object code

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2WebMvc;

/**
 * @author Amnesia
 * @date 2021/7/31 6:25 afternoon
 */
@Configuration
@EnableSwagger2WebMvc
public class Knife4jConfig {

    @Bean
    public Docket defaultApi2() {
        Docket docket=new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(new ApiInfoBuilder()
                        .description("Blog interface document")
                        .termsOfServiceUrl("www.amnesia.top")
                        .contact( new Contact("amnesia","www.amnesia.top","1211441748@qq.com"))
                        .version("1.0")
                        .build())
                //Group name
                .groupName("2.X edition")
                .select()
                //The Controller scan package path is specified here
                .apis(RequestHandlerSelectors.any())
                .paths(PathSelectors.any())
                .build();
        return docket;
    }
}

Then add @ ComponentScan("com.amnesia.my_blog") on the startup class

Keywords: blog

Added by AdB on Mon, 03 Jan 2022 06:09:18 +0200