【JAVA Backend Development】Part1-- St. Regis Food Delivery Project

1. Project environment construction

Development language Java
development tools IDEA
database MYSQL/8.0
use frame springboot+vue
technology stack used MybatisPlus, lombok, fastjson, druid database
JDK version jdk1.8

1.1 Database environment construction

Create a database: reggie, and execute the sql file to import 11 tables. The corresponding information of each table is as follows:

insert image description here

1.2 maven environment construction

Create a maven project and name it reggie_take_out. Check whether the maven warehouse in the project settings is correct and whether the JRE in the Runner is correct, and finally check whether the JKD in the project settings is correct. See the figure below for detailed steps:

insert image description here

insert image description here

insert image description here

2. Project development

2.1 Project framework construction

2.1.1, import the dependent pom file, and write the configuration file application.yml.

pom file :

<?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>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.4.5</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.wang</groupId>
    <artifactId>reggie_take_out</artifactId>
    <version>1.0-SNAPSHOT</version>
    <properties>
        <java.version>1.8</java.version>
    </properties>

    <!--配置依赖-->
    <dependencies>

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

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <scope>compile</scope>
        </dependency>

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.2</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.20</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.76</version>
        </dependency>

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

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.23</version>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.4.5</version>
            </plugin>
        </plugins>
    </build>

</project>

application.yml configuration file:

server:
  port: 8080
spring:
  application:
    name: reggie_take_out
  datasource:
    druid:
      driver-class-name: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://localhost:3306/reggie?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowPublicKeyRetrieval=true
      username: root
      password: root
mybatis-plus:
  configuration:
    #在映射实体或者属性时,将数据库中表名和字段名中的下划线去掉,按照驼峰命名法映射
    map-underscore-to-camel-case: true
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  global-config:
    db-config:
      id-type: ASSIGN_ID

2.1.2 Write the project startup class

After injecting dependencies, we need to write a springboot project startup class ReggieApplication , the code is as follows:

@Slf4j//日志注解
@SpringBootApplication
public class ReggieApplication {
    
    
    public static void main(String[] args) {
    
    
        SpringApplication.run(ReggieApplication.class,args);
        log.info("项目启动成功。。。");
    }
}

In this step, an empty springboot is built. You can click the startup class to see if the project can run normally. If you can, you can proceed to the next step! !

2.1.3 Import static resources and perform static resource mapping.

1. Assign the static resources corresponding to the project (静态资源的获取方式见文末)to the resource directory. And because under normal circumstances, only the static resources under the resource directory static and templates can be directly accessed by the project, so here we need to write a configuration class that allows us to access static resources for resource mapping.

2. Create a config directory, store our configuration classes, and write the WebMvcConfig.java file:


@Slf4j
@Configuration
public class WebMvcConfig extends WebMvcConfigurationSupport {
    
    
    /**
     *@Author wjq
     *@Date 2022/6/23
     *@Version v1.0
     *@Description 设置静态资源映射
     */
    @Override
    protected void addResourceHandlers(ResourceHandlerRegistry registry) {
    
    
        registry.addResourceHandler("/backend/**").addResourceLocations("classpath:/backend/");
        registry.addResourceHandler("/front/**").addResourceLocations("classpath:/front/");
        log.info("开始静态资源映射。。。");
    }
}

At this point, the basic framework of the project has been built, and the next step is to analyze and develop specific functions.

2.2 Background login function development

According to the video explanation, the complete function development is generally divided into three steps: requirement analysis -> code development -> function test. But in the process of our learning, functional testing is actually to check whether the code can achieve the corresponding functions and has no bugs. Therefore, this step will not be repeated in this paper.

insert image description here

2.2.1 Demand Analysis

1. View the login page src/main/resources/backend/page/login/login.html src/main/resources/backend/page/login/login.htmlsrc / main / reso u rces / back k e n d / p a g e / l o g in / l o g in . h t m l , obtain the information required for the login page, and execute the process.

2. View the information used for login

insert image description here

3. Analyze the model of the data Employee, and check the information we use for login.

2.2.2 Code development


  1. Import the entity class Employee and map it with the table Employee. (Login must have a user, which corresponds to Table 1 Employee in the data table)

  2. Use MybatisPlus to write the mapper file EmployeeMapper.

    @Mapper
    public interface EmployeeMapper  extends BaseMapper<Employee> {
    }
    
    
  3. Use MybatisPlus to write service interface and interface implementation class.

  4. Import end result returns class R.

    
    /**
     *@Author wjq
     *@Date 2022/6/23
     *@Version v1.0
     *@Description 通用返回结果类,服务端相应的数据最终都会封装成该类
     */
    @Data
    public class R<T> {
          
          
    
        private Integer code; //编码:1成功,0和其它数字为失败
    
        private String msg; //错误信息
    
        private T data; //数据
    
        private Map map = new HashMap(); //动态数据
    
        public static <T> R<T> success(T object) {
          
          
            R<T> r = new R<T>();
            r.data = object;
            r.code = 1;
            return r;
        }
    
        public static <T> R<T> error(String msg) {
          
          
            R r = new R();
            r.msg = msg;
            r.code = 0;
            return r;
        }
    
        public R<T> add(String key, Object value) {
          
          
            this.map.put(key, value);
            return this;
        }
    
    }
    
    
  5. Write the Controller file, write the login method in the controller class, and the processing logic of the login method.

    a, Encrypt the password password submitted by the page with md5

    b. Query the database according to the username username submitted by the page

    c, if there is no query, return the login failure result

    d, Password comparison, if inconsistent, return login failure result

    e, Check the status of the employee, if it is disabled, return the result that the employee is disabled

    f, the login is successful, store the employee id in the Session and return the successful login result

    insert image description here


@Slf4j
@RestController
@RequestMapping("/employee")
public class EmployeeController {
    
    

    @Autowired
    private EmployeeService employeeService;

    /**
     *@Author wjq
     *@Date 2022/6/24
     *@Version v1.0
     *@Description 管理元登录功能
     */
    @PostMapping("/login")
    public R<Employee> login(HttpServletRequest request, @RequestBody Employee employee){
    
    

        //1、将页面提交的密码password进行md5加密处理
        String password = employee.getPassword();
        password = DigestUtils.md5DigestAsHex(password.getBytes());

        //2、根据页面提交的用户名username查询数据库
        LambdaQueryWrapper<Employee> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Employee::getUsername,employee.getUsername());
        Employee emp = employeeService.getOne(queryWrapper);

        //3、如果没有查询到则返回登录失败结果
        if(emp == null){
    
    
            return R.error("登录失败");
        }

        //4、密码比对,如果不一致则返回登录失败结果
        if(!emp.getPassword().equals(password)){
    
    
            return R.error("登录失败");
        }

        //5、查看员工状态,如果为已禁用状态,则返回员工已禁用结果
        if(emp.getStatus() == 0){
    
    
            return R.error("账号已禁用");
        }

        //6、登录成功,将员工id存入Session并返回登录成功结果
        request.getSession().setAttribute("employee",emp.getId());
        return R.success(emp);
    }

}

2.3 Background exit function development


2.3.1 Demand Analysis

insert image description here

1. After logging in, click the logout icon to return to the login page.

2,打开 s r c / m a i n / r e s o u r c e s / b a c k e n d / i n d e x . h t m l src/main/resources/backend/index.html src / main / reso urces / back e n d / in d e x . h t m l file, see that the exit button will trigger the method logout .

 <img src="images/icons/[email protected]" class="outLogin" alt="退出" @click="logout" />

3. Analyze the logout method, which will call the function logoutApi(). Open this function and find that it will post to /employee/logout /employee/logout/ em p oyee / l o g o u t , so we only need to write the exit method in the controller and return a general class R.

          logout() {
            logoutApi().then((res)=>{
              if(res.code === 1){
                localStorage.removeItem('userInfo')
                window.location.href = '/backend/page/login/login.html'
              }
            })
          },
function logoutApi(){
  return $axios({
    'url': '/employee/logout',
    'method': 'post',
  })
}

2.3.2 Code development

1. Clear the ID of the currently logged-in employee saved in the Session.

2. Return the generic class R.

    /**
     * 员工退出
     * @param request
     * @return
     */
    @PostMapping("/logout")
    public R<String> logout(HttpServletRequest request){
    
    
        //清理Session中保存的当前登录员工的id
        request.getSession().removeAttribute("employee");
        return R.success("退出成功");
    }

2.4 Improve the login function

2.4.1 Requirements Analysis

1. When we directly access http://localhost:8080/backend/index.html http://localhost:8080/backend/index.htmlhttp://localhost:8080/ back e n d / in d e x . h t m l page, you can log in directly without an account number and password, which does not meet actual needs . The actual requirement is: only when the user enters the correct account number and password can the login be completed!

2. There are two ways to realize this function: use filters and interceptors to judge whether the user has completed login in the filters and interceptors, and jump to the login page if not.

As shown in the figure below: it is a normal requirement for us to log in with the account and password for the first time. But when we launched it, we can directly access the index page, which is a very serious problem.

insert image description here

2.4.2 Code development

  1. Create a custom filter LoginCheckFilter to implement Filter and override the doFilter method
@WebFilter(filterName = "loginCheckFilter",urlPatterns = "/*")
@Slf4j
public class LoginCheckFilter implements Filter {
    
    
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
    
    
    }
	
}
  1. Adding the annotation @ServletComponentScan to the startup class is equivalent to turning on the switch of the filter.

  2. Improve the processing logic a of the filter
    to obtain the URI of this request.
    b. Define the request paths that do not need to be processed and put them in the String array.
    ​ c, to determine whether this request needs to be processed, implemented in the function check, using AntPathMatcherthe class. If no treatment is required, it will be released directly.

      //路径匹配器,支持通配符
    public static final AntPathMatcher PATH_MATCHER = new AntPathMatcher();  
    public boolean check(String[] urls,String requestURI){
    
    
        for (String url : urls) {
    
    
            boolean match = PATH_MATCHER.match(url, requestURI);
            if(match){
    
    
                return true;
            }
        }
        return false;
    }

​ d, judge the login status, if it is already logged in, it will be released directly.
​ e, if not logged in, return the result of not logged in, and respond data to the client page through the output stream.

4. After we finish writing the filter, in the front-end page, pass the file jrequest.js jrequest.jsThe following method in j re q u es t . j s responds to the filter. After reading the code, we can find that it still needs to obtain a general type R at the end, which just corresponds to the process e in step 3, and we need to send the client to the front end Page response data.

  service.interceptors.response.use(res => {
    
    
      if (res.data.code === 0 && res.data.msg === 'NOTLOGIN') {
    
    // 返回登录页面
        console.log('---/backend/page/login/login.html---')
        localStorage.removeItem('userInfo')
        window.top.location.href = '/backend/page/login/login.html'
      } else {
    
    
        return res.data
      }
    },

The integrated filter code, LoginCheckFilter class is as follows:

/**
 * 检查用户是否已经完成登录*/


@WebFilter(filterName = "loginCheckFilter",urlPatterns = "/*")
@Slf4j
public class LoginCheckFilter implements Filter {
    
    
    //路径匹配器,支持通配符
    public static final AntPathMatcher PATH_MATCHER = new AntPathMatcher();

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
    
    
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;

        //1、获取本次请求的URI
        String requestURI = request.getRequestURI();// /backend/index.html

        log.info("拦截到请求:{}",requestURI);

        //定义不需要处理的请求路径
        String[] urls = new String[]{
    
    
                "/employee/login",
                "/employee/logout",
                "/backend/**",
                "/front/**"
        };


        //2、判断本次请求是否需要处理
        boolean check = check(urls, requestURI);

        //3、如果不需要处理,则直接放行
        if(check){
    
    
            log.info("本次请求{}不需要处理",requestURI);
            filterChain.doFilter(request,response);
            return;
        }

        //4、判断登录状态,如果已登录,则直接放行
        if(request.getSession().getAttribute("employee") != null){
    
    
            log.info("用户已登录,用户id为:{}",request.getSession().getAttribute("employee"));
            filterChain.doFilter(request,response);
            return;
        }

        log.info("用户未登录");
        //5、如果未登录则返回未登录结果,通过输出流方式向客户端页面响应数据
        response.getWriter().write(JSON.toJSONString(R.error("NOTLOGIN")));
        return;

    }

/**
     * 路径匹配,检查本次请求是否需要放行
     * @param urls
     * @param requestURI
     * @return*/


    public boolean check(String[] urls,String requestURI){
    
    
        for (String url : urls) {
    
    
            boolean match = PATH_MATCHER.match(url, requestURI);
            if(match){
    
    
                return true;
            }
        }
        return false;
    }
}

3. Summary

At this point, the overall framework of the project and the basic login and launch functions have been completed. Look at the detailed catalog of the project you built to see whether each module and its function are clear at a glance. The next function development is also gradually completed according to the above process. .

insert image description here

Note: This blog is to learn from "Dark Horse Programmer" and build a detailed process record of the project. The database sql files, static resource files and detailed codes required in this article can all be obtained by following the official account Dark Horse Programmer and replying to "Regis Takeaway".

Guess you like

Origin blog.csdn.net/qq_44085437/article/details/125491411