Learn the notes of fengmi mall

Keywords: Java Maven unit testing Project

Fengmi mall project

Create using Maven aggregation project (parent project of one Maven and child projects of multiple Maven),

You can add the following to the parent project pom.xml file:

<package>pom<package>

[the external chain image transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-na1kxe9e-163344668620) (C: \ users \ courageandlove \ appdata \ roaming \ typora \ user images \ image-20210812151308862. PNG)]

1. Build the project through Maven polymerization project:

1. Create a Maven parent project and modify its pom.xml file to delete some useless directories such as src

<packaging>pom<packaing>

2. Create multiple module s under the parent project, including (common,beans,mapper,service,api) and package them all into jar packages

pom.xml plus

<packaging>jar</packaging>

3. Since the mapper layer needs to call the beans layer (pojo), it needs to be in the pom.xml file, and then relevant dependencies can be added to the mapper.

   <dependencies>

<!--        mapper Need to use Beans So you need to add beans Dependence of-->
        <dependency>
            <groupId>org.example</groupId>
            <artifactId>beans</artifactId>
            <version>2.0.1</version>
        </dependency>
    </dependencies>

4. To create a service, you need to call mapper and common, and you need to add in the pom.xml file

 <dependency>
            <groupId>org.example</groupId>
            <artifactId>mapper</artifactId>
            <version>2.0.1</version>
        </dependency>
        <dependency>
            <groupId>org.example</groupId>
            <artifactId>common</artifactId>
            <version>2.0.1</version>
        </dependency>

5. The API layer needs to receive requests from the front end, so we need to create a SpringBoot project. You can create a Maven project and add related dependencies

6.api subproject, providing external interfaces

Generally speaking, the dependencies of the parent project can be referenced by the child project, and the child project can also add the required dependencies separately

Database design of fengmi mall

2. Software development steps

  • Ask questions

  • Feasibility analysis (Technology (generally realized by relevant personnel), cost, laws and regulations)

  • Outline design

    • System design (technology selection, architecture mode)
    • Database design
    • UI design
    • Business process design
  • detailed design

    • Implementation steps (implementation details of business process)
  • code

    • Implement the code according to the designed implementation steps
    • The development process uses unit testing
  • test

    • integration testing
    • Function test (cartridge)
    • Performance test (white box), high concurrency, stress test
  • Delivery / deployment implementation

    3. Database design process

  • Analyze the database entity (javaBean) according to the function

    • Product, order, shopping cart, user, address
  • Extract entity attributes

    • spu product (id, product name, product picture, product description...)

    • 1 min10 ... ...

    • sku(skuId, parameter, price, commodity id)

    • 101 memory 8g \ storage 128G 2999 1

    • 102 memory 12G \ storage 256G 3999 1

    • Address (name, address, telephone...)

[the external chain image transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-p8zp9mya-1633446686624) (C: \ users \ courageandlove \ appdata \ roaming \ typora \ typora user images \ image-20210814172548189. PNG)]

It can be known that the price depends on the change of parameters, and the parameters depend on the change of id, which does not meet the requirements of database design table. Two tables can be designed for implementation.

  • Use the third paradigm of the database to check whether the data items are reasonable
  • Analysis entity relationship diagram: E-R diagram (one to one, one to many)
  • Database modeling (three line diagram) modeling tool (PdMan)
  • Database and table creation sql

3. Data modeling tool PDMan

  • Create database tables (data tables) visually

  • The view shows the relationship between tables (diagram)

  • Export sql instruction (model - export DDL script)

  • Record database model version management

  • You can connect to the database and generate tables directly

    The difference between Spu and Sku

  • SPU (Standard Product Unit): the smallest unit of commodity information aggregation. Generally speaking, a commodity with the same attribute value can be called a SPU

    Product: glory 8 millet 10

  • sku(Stock Keeping Unit) is defined as the control minimum available unit that holds the minimum inventory

    sku glory 8 8G/128G 10

    sku glory 8 4G/124G 20

    Note: the design function of the order table: as long as the user clicks the order, the relevant information of the order table cannot be changed, so you need to take a snapshot of the address and commodity information, so it doesn't matter if you temporarily change the price information or others

    Shopping cart design:

    [the external chain image transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-xztkvkoc-1633446686626) (C: \ users \ courageandlove \ appdata \ roaming \ typora \ typora user images \ image-20210814213038578. PNG)]

[the external chain image transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-axmtdyiv-1633446686628) (C: \ users \ courageandlove \ appdata \ roaming \ typora \ typora user images \ image-20210814215338098. PNG)]

4. Fengcheng business process design

[the external chain image transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-bgyg8ooe-1633446686629) (C: \ users \ courageandlove \ appdata \ roaming \ typora \ typora user images \ image-2021081423033101. PNG)]

In enterprise development, when the project needs analysis, function analysis, database analysis and design are completed, the project team will allocate development tasks according to the functional modules in the project.

Everyone will be assigned different functions

4.1 user management 9 business process analysis

Monomer architecture: jump between page and control, synchronous request controller, and process control is completed by the controller

Front end and back end separation architecture: the front end and back end are developed and deployed. The front end can only send requests asynchronously, and the back end is only responsible for receiving requests and parameters, processing requests and returning results

[the external chain image transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-96peij8o-16334466830) (C: \ users \ courageandlove \ appdata \ roaming \ typora \ typera user images \ image-20210814230815317. PNG)]

[the external chain image transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-g0wixnwp-1633446686631) (C: \ users \ courageandlove \ appdata \ roaming \ typora \ user images \ image-20210815101028557. PNG)]

**The front end can send a request as shown in the figure: * * URL required, params

[the external chain image transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-tpnnwduu-1633446686631) (C: \ users \ courageandlove \ appdata \ roaming \ typora \ typora user images \ image-20210815102516744. PNG)]

5 interface introduction

Narrow sense: the method in the controller that can accept user requests

Standard definition: API(Application Programming interface) is an agreement for the connection of different components of a software system.

5.1 interface specification

As a back-end programmer, we should not only develop the interface program, but also write the interface specification

5.2swagger (automatically generate normative documents of server interface)

For the development of front-end and back-end separation rules, the back-end needs to write interface description documents, which will take a lot of time

swagger is a tool for generating normative documents of server interfaces and testing interfaces.

  • swagger effect
  • Generate interface specification document
  • Generate interface test tool

5.2.1 introducing related dependencies:

<!--        swagger2 Interface document generation tool-->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.9.2</version>
        </dependency>
<!--        swagger-ui-->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.9.2</version>
        </dependency>


5.2.2 create related configuration classes

You can test the relevant controller layer in the api module, create a SwaggerConfig class under the config package, add @ Configuration, @ EnableSwagger2 annotations, and then configure the relevant information

package com.qfedu.fmmall.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.w3c.dom.DocumentType;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

@Configuration
@EnableSwagger2
public class SwaggerConfig {
    /*
    * swagger Generate our interface document:
    * 1.You need to configure the information for generating documents
    * 2.Configure build rules
    *
    * */
    @Bean
    public Docket docket(){

//Create cover information object
        ApiInfoBuilder apiInfoBuilder=new ApiInfoBuilder();//Specifies the cover information in the generated document: document title, author, version
        apiInfoBuilder.title("<Back end interface description of fengmi mall")
                .description("This document details the backend interface specification of fengmi mall project")
                .version("v 2.0.1")
                .contact(new Contact("houge","www.houge.com","houge@hou.com"));


        ApiInfo apiInfo=apiInfoBuilder.build();


        Docket docket=new Docket(DocumentationType.SWAGGER_2) //Specify document style
                .apiInfo(apiInfo)
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.qfedu.fmmall.controller"))
//                After the path is defined, only the requests starting with user will be scanned. paths(PathSelectors.regex("/user /"))
//                PathSelectors.any() represents any request
                .paths(PathSelectors.any())
                .build();


        return docket;

    }




}

5.2.3 conduct relevant tests according to your configured port number

http://localhost:8080/swagger-ui.html

5.2.4 swagger provides a set of annotations to describe each interface in detail

@Api(value=" user management ",tags="Provides an interface for user login and registration")//This interface can be placed directly under the @ Controller annotation

@ApiOperation and apiimplicitparams ({@ apiimplicitparam (datatype = "", name = "username", value="",required=true), @ApiImplictParm}) are placed on the @ RequestMapping("/login") request to modify methods and parameters in methods.

[the external chain image transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-zcmssub7-1633446686632) (C: \ users \ courageandlove \ appdata \ roaming \ typora \ typora user images \ image-20210815170433850. PNG)]

 @ApiOperation("User login interface")
    @ApiImplicitParams({
            @ApiImplicitParam(dataType = "string",name = "username",value = "User login account",required = true),
            @ApiImplicitParam(dataType = "string",name = "password",value = "User login password",defaultValue = "111111",required = false)
    })
    @RequestMapping("/login")
//    @RequestParam can have default parameters
    public ResultVO login(@RequestParam("username") String name,@RequestParam(value = "password",defaultValue = "111111") String pwd){

        return userService.checkLogin(name,pwd);


    }
    @RequestMapping(value = "regist",metho

@ApiModel and @ ApiModelProperty when the interface parameter returns an object type, you need to add an annotation in the entity class (that is, the Beans Module is configured)

package com.qfedu.fmmall.entity;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor

@ApiModel(value = "User's buyer information",description = "Buyer related parameters")
public class User {

    @ApiModelProperty(name = "user id",required = false,dataType = "int")
    private Integer userId;

    @ApiModelProperty(dataType = "string",name = "Buyer name",required = true)
    private String  userName;
    @ApiModelProperty(dataType = "string",name = "Buyer password",required = true)
    private String userPwd;
    @ApiModelProperty(dataType = "string",name = "Real name of buyer",required = true)
    private String userRealname;
    @ApiModelProperty(dataType = "string",name = "User picture",required = true)
    private String userImg;


}
@ApiIgnore     Interface method annotation. The method added with this annotation will not be generated in the interface document

5.2.5 usage of swagger UI plug-in

1.api module adding dependency
<!--        swagger-ui plug-in unit-->
        <!-- https://mvnrepository.com/artifact/com.github.xiaoymin/swagger-bootstrap-ui -->
        <dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>swagger-bootstrap-ui</artifactId>
            <version>1.9.6</version>
        </dependency>


2. Access and then use it for relevant tests

http://ip:port/doc.html

1, Design and implementation of user management in fengmi mall

1. Creation of userdao interface:

package com.qfedu.fmmall.dao;

import com.qfedu.fmmall.entity.User;
import org.springframework.stereotype.Repository;

@Repository
public interface UserDao {

//    User registration
    public int insert(User user);

//   Authentication of login based on user name
    public User queryByName(String name);


}


2.UserMapper.xml

<?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.qfedu.fmmall.dao.UserDao">

 <resultMap id="userResultMap" type="User">
  <id column="user_id" property="userId"></id>

 <result column="username" property="userName"/>
 <result column="password" property="password"/>
 <result column="nickname" property="nickname"/>
 <result column="realname" property="realname"/>
 <result column="user_img" property="userImg"/>
 <result column="user_mobile " property="userMobile"/>
 <result column=" user_email" property="userEmail"/>
 <result column="user_sex " property="userSex"></result>
     <result column=" user_birth" property="userBirth"></result>
     <result column="user_regtime " property="userRegtime"></result>
     <result column="user_modtime " property="userModtime"></result>

 </resultMap>

    
    <select id="queryByName" resultType="User">

     select *from users where username=#{username}

 </select>
    <insert id="insert" parameterType="User">

        insert into users(username,password,user_regtime,user_modtime) values (#{username},
        #{password},#{userRegtime},#{userModtime})
    </insert>
    </mapper>

3.UserService

package com.qfedu.fmmall.service;

import com.qfedu.fmmall.entity.User;
import com.qfedu.fmmall.vo.ResultVO;

public interface UserService {
//    ResultVO is a custom class that responds to the front end.
    public ResultVO checkLogin(String username, String pwd);

//    User registration
    public ResultVO insert(String username, String pwd);
}

4.UserServiceimpl:

package com.qfedu.fmmall.service.impl;

import com.qfedu.fmmall.service.UserService;
import com.qfedu.fmmall.dao.UserDao;
import com.qfedu.fmmall.entity.User;
import com.qfedu.fmmall.utils.MD5Utils;
import com.qfedu.fmmall.vo.ResultVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;

@Service
@Transactional
//Make all threads use this object. Singleton mode is on by default
@Scope("singleton")
public class UserServiceimpl implements UserService {
    @Autowired
    private UserDao userDao;//You can add userDao to userDao. This won't be red, but it doesn't make any sense
    @Override
    public ResultVO checkLogin(String username, String pwd) {
//        Query user name
        User user = userDao.queryByName(username);
        if(user==null){
//            Incorrect user name

            return new ResultVO(10001,"Incorrect user name",null);



        }else {
            //The password is encrypted using MD5
            String md5Pwd = MD5Utils.md5(pwd);

            if(md5Pwd.equals(user.getPassword())){
//          Validation succeeded
                return  new ResultVO(200,"Login succeeded",user);
            }else {
                //Incorrect password
                return  new ResultVO(10001,"Password error",null);

            }


        }



    }
    @Transactional
    @Override
    public ResultVO insert(String username, String pwd) {
//        Determine whether this user is registered

//        With this lock, you can use this userServiceimpl for all registrations
        synchronized (this){
//            Encrypt the password with MD5
            String password = MD5Utils.md5(pwd);

            User user1 = userDao.queryByName(username);
//Indicates that the user name has not been registered and can be registered
            if (user1==null){
//One is the registration time, regtime, and the other is the modification time, modtime
                User user=new User(username,password,new Date(),new Date());
                int i = userDao.insert(user);
                if(i>0){
                    return new ResultVO(1000,"login was successful",null);
                }else {

                    return new ResultVO(1001,"login has failed",null);

                }


            }
//            Judge whether the user name has been registered, and then return the data to the front end, goodjob, none can influence you
            else {

                return new ResultVO(1001,"The user name has already been registered",null);
            }



        }


    }
}

5. UserController of API module:

package com.qfedu.fmmall.vo;


import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor

@ApiModel(value = "ResultVO object",description = "Response encapsulated data to the front end")
public class ResultVO {
//    Response to the status code of the front end
    @ApiModelProperty(dataType = "int",value = "Response status code")
    private  int code;

//    Respond to the prompt message to the front end
    @ApiModelProperty(dataType = "string",value = "Response message")
    private  String msg;
//Data in response to the front end
    @ApiModelProperty(dataType = "object",value = "Content of response data")
    private  Object data;
}

6.ResultVO is a class for data interaction with the front end

package com.qfedu.fmmall.vo;


import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor

@ApiModel(value = "ResultVO object",description = "Response encapsulated data to the front end")
public class ResultVO {
//    Response to the status code of the front end
    @ApiModelProperty(dataType = "int",value = "Response status code")
    private  int code;

//    Respond to the prompt message to the front end
    @ApiModelProperty(dataType = "string",value = "Response message")
    private  String msg;
//Data in response to the front end
    @ApiModelProperty(dataType = "object",value = "Content of response data")
    private  Object data;
}

7. In the MD5Utils class of the common module:

package com.qfedu.fmmall.utils;

import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

//MD5 generator
public class MD5Utils {
	public static String md5(String password){
		//Generate an md5 encryptor
		try {
			MessageDigest md = MessageDigest.getInstance("MD5");
			//Calculate the value of MD5
			md.update(password.getBytes());
			//BigInteger converts an 8-bit string into a 16 bit string. The resulting string is in the form of hash code value
			//BigInteger (parameter 1, parameter 2) parameter 1 is a positive number, 0 is 0 - 1 is a negative number
			return new BigInteger(1, md.digest()).toString(16);
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		}
		return null;
	}
}

2, Reverse engineering

According to the created table, generate entity classes, DAO layers and mapping files

Add Dependencies under Dependencies, which is a maven plug-in of Mybatis

<build>
    <plugins>
        <plugin>
            <groupId>org.mybatis.generator</groupId>
            <artifactId>mybatis-generator-maven-plugin</artifactId>
            <version>1.3.5</version>
            <configuration>
                <configurationFile>${basedir}/src/main/resources/generator/generatorConfig.xml</configurationFile>
            </configuration>
            

            <dependencies>
                <dependency>
                    <groupId>mysql</groupId>
                    <artifactId>mysql-connector-java</artifactId>
                    <version>5.1.46</version>
                </dependency>
                <dependency>
                    <groupId>tk.mybatis</groupId>
                    <artifactId>mapper</artifactId>
                    <version>4.1.5</version>
                </dependency>


            </dependencies>

  </plugin>

    </plugins>


</build>

7.1 reverse engineering configuration

Create generatorConfig.xml under the generator directory of resources

  1. The configuration of the database needs to be modified

  2. You need to modify the configuration of the generation location of POJO, mapper and mapper.xml files

  3. <!--%Indicates that all tables in the current database will be generated-->
            <table tableName="%"></table>
    
  4. <!-- Specify build Mapper Inheritance template for -->
           <plugin type="tk.mybatis.mapper.generator.MapperPlugin">
               <property name="mappers" value="com.hou.general.GeneralDao"/>
           </plugin>
      
    
  5. Specify the path to your Configuration generatorConfig.xml file

  6. Note that your file must have spaces or something

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
        PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
        "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">

<generatorConfiguration>
    <!-- introduce application.properties -->

<!--    <properties resource="application.properties" />-->

    <!-- MyBatis3Simple: Do not generate Example Related classes and methods defaultModelType="flat" -->
    <context id="MysqlContext" targetRuntime="MyBatis3Simple" >

        <property name="beginningDelimiter" value="`"/>
        <property name="endingDelimiter" value="`"/>

        <!-- Specify build Mapper Inheritance template for -->
        <plugin type="tk.mybatis.mapper.generator.MapperPlugin">
            <property name="mappers" value="com.qfedu.fmmall.general.GeneralDao"/>
        </plugin>

        <!--be careful context The documents in the shall be placed in order-->
        <commentGenerator>
            <property name="suppressDate" value="true"/>
            <!-- Remove automatically generated comments true: Yes: false:no -->
            <property name="suppressAllComments" value="true"/>
        </commentGenerator>


        <!-- jdbc Connection configuration -->
        <jdbcConnection driverClass="com.mysql.jdbc.Driver"
                        connectionURL="jdbc:mysql://localhost:3306/fmmall?characterEncoding=utf8"
                        userId="root"
                        password="roothouzhicong">
        </jdbcConnection>

        <javaTypeResolver>
            <!-- Whether to use bigDecimal, false The following types can be automatically converted( Long, Integer, Short, etc.) -->
            <property name="forceBigDecimals" value="false"/>
        </javaTypeResolver>

        <!-- Package name and location of the generated entity class, targetPackage Refers to the package name,targetProject The value is the path location-->
        <!-- For generated pojo Package,pojo Actually domain Entity-->
        <javaModelGenerator targetPackage="com.qfedu.fmmall.entity" targetProject="src/main/java">
            <property name="enableSubPackages" value="true"/>
            <property name="trimStrings" value="true"/>
        </javaModelGenerator>

        <!-- For generated mapper.xml Directory -->
        <sqlMapGenerator targetPackage="/" targetProject="src/main/resources/mappers"/>

        <!-- to configure mapper Corresponding java Mapping can also be called dao layer -->
        <javaClientGenerator targetPackage="com.qfedu.fmmall.dao" targetProject="src/main/java"
                             type="XMLMAPPER"/>

        <!--%Indicates that all tables in the current database will be inherited-->
        <table tableName="%"></table>

    </context>
</generatorConfiguration>

7.2 specify the path of generatorConfig.xml file in pom.xml file

Add this:

 <configuration>
                <configurationFile>${basedir}/src/main/resources/generator/generatorConfig.xml</configurationFile>
            </configuration>
        <plugin>
            <groupId>org.mybatis.generator</groupId>
            <artifactId>mybatis-generator-maven-plugin</artifactId>
            <version>1.3.5</version>
            <configuration>
                <configurationFile>${basedir}/src/main/resources/generator/generatorConfig.xml</configurationFile>
            </configuration>

            <dependencies>
                <dependency>
                    <groupId>mysql</groupId>
                    <artifactId>mysql-connector-java</artifactId>
                    <version>5.1.46</version>
                </dependency>
                <dependency>
                    <groupId>tk.mybatis</groupId>
                    <artifactId>mapper</artifactId>
                    <version>4.1.5</version>
                </dependency>


            </dependencies>


        </plugin>

3, Cross domain problem

[the external chain image transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-nkwuyikh-1633446686633) (C: \ users \ courageandlove \ appdata \ roaming \ typora \ typora user images \ image-20210817151030750. PNG)]

Solution:

[the external chain image transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-cmocsbxn-1633446686634) (C: \ users \ courageandlove \ appdata \ roaming \ typora \ typora user images \ image-20210817151350682. PNG)]

Back end solution: add @ CrossOrigin annotation to UserController

The front end requests cross domain login through Ajax:

		<form>
							<div class="user-name"  style="margin-top: 20px;">
								<label for="user"><span class="glyphicon glyphicon-user" aria-hidden="true"></span></label>
								<input type="text" name="username" id="userName" placeholder="mailbox/mobile phone/user name">
							</div>
							<div class="user-pass"  style="margin-top: 20px;">
								<label for="password"><span class="glyphicon glyphicon-lock" aria-hidden="true"></span></label>
								<input type="password" name="" id="userPwd" placeholder="Please input a password">
							</div>
						</form>




<input type="button" name="" id="submitBtn"  value="Sign in" class="am-btn am-btn-primary am-btn-sm">




<script src="static/js/jquery-1.7.2.min.js"></script>
<script type="text/javascript">

$("#submitBtn").click(function(){
	var name=$("#userName").val();
	var pwd=$('#userPwd').val();
  $.get("http://localhost:8080/user/login",{
	  username:name,
	  password:pwd,
  },function(res){
	  console.log(res);



  },"json"
 
  
  
  
  )



})

</script>





The front end uses JSONP settings, and the back end uses @ CrossOrigin annotation - set the response header to allow cross domain.

Debugger: you can add code debugger to the front end for related debugging. You can directly verify the front end

4, Value transfer of front page

Cookies and localstorage can transfer values between front-end pages

Cookie browser side cache file: the size is limited by the browser.

LocalStorage: to store more data

Difference: cookie s can transfer values with the background. localStorage can only store values in the front end, but the storage time is long.

4.1Cookie usage (custom encapsulating a js,cookie_utils.js)

var opertor="=";

function getCookieValue(keyStr){
	
	
	var s=window.document.cookie;
	var arr=s.split("; ");
for(var i=0;i<arr.length;i++){
	var str=arr[i];

	var k=str.split(opertor)[0];
	var v=str.split(opertor)[1];
	if(k==keyStr){

		value=v;
		break;
	}

	
}
return value;



}


function setCookieValue(key,value){

	document.cookie=key+opertor+value;



}

A page setting value:

function(res){
	  console.log(res);
	  if(res.code==1000){
// Get the data from the front end
		var userInfo=res.data;
		// Cookies and localstorage can transfer values between front-end pages
	setCookieValue("username",userInfo.username);
	setCookieValue("userImg",userInfo.userImg);

		window.location.href="index.html";
	  }else{

		$("#tips").html("<label style='color:red'>"+ res.msg +"</label>");




	  }

B page value:

var name=getCookieValue("username");
 var userImg=getCookieValue("userImg");
 console.log(name+userImg);

4.2localStorage

Page A:

	localStorage.setItem("user",JSON.stringify(userInfo));

Page B:

var jsonStr=localStorage.getItem("user");

// Convert json string to object
var userInfo=eval("("+jsonStr+")");


// Disappear the obtained value
localStorage.removeItem("user");
console.log(userInfo);

4.3 Vue login

data:{
		username:"",
		password:"",	
		tips:" ",
		colorStyle:"",
		isRight:false,
	


	},
	methods:{
		doSubmit:function() {
			// Verification successful

			if(vm.isRight){
				var url=baseUrl+"/user/login";
				axios.get(url,{	
					params:{
						username:vm.username,password:vm.password

					} }
				
					).then((res)=>{

				console.log(res);

					var vo=res.data;
					if(vo.code==1){
						window.location.href="index.html";
					}else{
						vm.tips="Wrong account or password";
					}



				});

			}else{
				vm.tips="Please enter the correct user name and password";
				vm.colorStyle="color:red"
			}

            //  1. Verify the data

            if(vm.username==" "){
                vm.tips="enter one user name";
                vm.colorStyle="color:red";


            }
             
         },
         checkInfo:function(){
            if(vm.username==""){
                vm.tips="enter one user name";
                this.colorStyle="color:red";
				vm.isRight=false;


            }else if(vm.username.length<6 ||vm.username.length>20){
                vm.tips="Account length must be 6-20";
                vm.colorStyle="color:red";
				vm.isRight=false;





            }else{
// Verify password
				if(vm.password==" "){
                vm.tips="Please input a password";
                this.colorStyle="color:red";
				vm.isRight=false;


            }else if(vm.password.length<6 ||vm.password.length>16){
				vm.tips="Password length is 6-16";
                this.colorStyle="color:red";

			}else{
				vm.tips=" ";
				vm.isRight=true;
			}



			}





         }







	}


from form (bind the form input with @ keyup):

	<form>
							<div class="user-name"  style="margin-top: 20px;">
								<label for="user"><span class="glyphicon glyphicon-user" aria-hidden="true"></span></label>
								<!-- @keyup Bind -->
								<input type="text" name="username" v-model="username" id="userName" @keyup="checkInfo" placeholder="mailbox/mobile phone/user name">
							</div>
							<div class="user-pass"  style="margin-top: 20px;">
								<label for="password"><span class="glyphicon glyphicon-lock" aria-hidden="true"></span></label>
								<input type="password" name="" v-model="password" id="userPwd" placeholder="Please input a password"@keyup="checkInfo">
							</div>
						</form>

5, Separation of front and back end development and user authentication

5.1 in a single project:

[the external chain image transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-dwoelsut-1633446686634) (C: \ users \ courageandlove \ appdata \ roaming \ typora \ user images \ image-20210823080556194. PNG)]

You can know that there are multiple sessions in each server, but the IDs are different. The session id can be stored in the Cookie, and then judge whether it is the same session

How do users authenticate in a single project?

In a single project, both the view resource and the controller are on the same server. The user's multiple requests can be verified based on the same session:

  1. The user logs in and stores the information in the session
  2. Judge whether the user can log in according to whether there is user information in the session.

5.2 front and rear end separation items

[the external chain image transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-w4nicszy-1633446686635) (C: \ users \ courageandlove \ appdata \ roaming \ typora \ typora user images \ image-20210823082009032. PNG)]

You can know that the token is used to realize user authentication. The token exists in the cookie (the same server can access the cookie), and then verify whether the token is correct

Implementation of user code based on token authentication

Introduced in the commons module

package com.qfedu.fmmall.utils;

import java.util.Base64;

//base64 encrypts and decrypts the email address code verification code when activating the email
//Decrypt the email address and code when it is returned
public class Base64Utils {
	//encryption
	public static String encode(String msg){
		return Base64.getEncoder().encodeToString(msg.getBytes());
	}
	//decrypt
	public static String decode(String msg){
		return new String(Base64.getDecoder().decode(msg));
	}
}

Login successfully generated token:UserController

package com.qfedu.fmmall.controller;


import com.qfedu.fmmall.entity.Users;
import com.qfedu.fmmall.service.UserService;
import com.qfedu.fmmall.vo.ResultVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

/*@Controller
@ResponseBody*/

@RestController
@RequestMapping("/user")
@CrossOrigin
@Api(value = "Provides an interface for user login and registration",tags = "user management ")
public class UserController {

    @Autowired
    private UserService userService;

//    @Adding this annotation to ApiIgnore will cause swagger to ignore this method
    @ApiOperation("User login interface")
    @ApiImplicitParams({
            @ApiImplicitParam(dataType = "string",name = "username",value = "User login account",required = true),
            @ApiImplicitParam(dataType = "string",name = "password",value = "User login password",required = true)
    })
    @RequestMapping("/login")
//    @RequestParam can have default parameters
    public ResultVO login(@RequestParam("username") String name,@RequestParam(value = "password") String pwd){

        return userService.checkLogin(name,pwd);


    }

    @ApiOperation(value = "User registration")
    @PostMapping("/regist")
    @ApiImplicitParams({
            @ApiImplicitParam(dataType = "string",name = "username",value = "User registered account",required = true),
            @ApiImplicitParam(dataType = "string",name = "password",value = "Password for user registration",required = true)
    })
//    The front end uses user to pass values, and the back end can use users to receive values
     public ResultVO register(@RequestBody Users users){

        return userService.insert(users.getUsername(),users.getPassword());

    }


}

Then encrypt the token in UserServiceimpl:

//If the login is successful, you need to generate a token (a token is a string generated according to the rules)
String token= Base64Util.encode(username+"roothouzhicong");

package com.qfedu.fmmall.service.impl;

import com.qfedu.fmmall.dao.UsersMapper;
import com.qfedu.fmmall.entity.Users;
import com.qfedu.fmmall.service.UserService;
import com.qfedu.fmmall.utils.MD5Utils;
import com.qfedu.fmmall.vo.ResultStatus;
import com.qfedu.fmmall.vo.ResultVO;
import org.apache.logging.log4j.util.Base64Util;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import tk.mybatis.mapper.entity.Example;

import java.util.Date;
import java.util.List;

@Service
@Transactional
//Make all threads use this object. Singleton mode is on by default
@Scope("singleton")
public class UserServiceimpl implements UserService {
    @Autowired
    private UsersMapper userDao;//You can add userDao to userDao. This won't be red, but it doesn't make any sense
    @Override
    public ResultVO checkLogin(String username, String pwd) {
//        Query user name

        Example example = new Example(Users.class);
        Example.Criteria criteria = example.createCriteria();
        criteria.andEqualTo("username",username);
        List<Users> users = userDao.selectByExample(example);

//
        if(users.size()==0){
//            Incorrect user name

            return new ResultVO(10001,"Incorrect user name",null);



        }else {
            //The password is encrypted using MD5
            String md5Pwd = MD5Utils.md5(pwd);
            System.out.println(users.get(0).getPassword());

            if(md5Pwd.equals(users.get(0).getPassword())){

//                If the login is successful, you need to generate a token (a token is a string generated according to the rules)
                String token= Base64Util.encode(username+"roothouzhicong");

                //          Validation succeeded
                return  new ResultVO(ResultStatus.OK,token,users.get(0));
            }else {
                //Incorrect password
                return  new ResultVO(ResultStatus.NO,"Password error",null);

            }


        }



    }
    @Transactional
    @Override
    public ResultVO insert(String username, String pwd) {
//        Determine whether this user is registered

//        With this lock, you can use this userServiceimpl for all registrations
        synchronized (this){
//            Encrypt the password with MD5
            String password = MD5Utils.md5(pwd);

            //        Query user name

            Example example = new Example(Users.class);
            Example.Criteria criteria = example.createCriteria();
            criteria.andEqualTo("username",username);
            List<Users> users = userDao.selectByExample(example);
//Indicates that the user name has not been registered and can be registered
            if (users.size()==0){
//One is the registration time, regtime, and the other is the modification time, modtime
                Users user=new Users(username,password,new Date(),new Date());
                int i = userDao.insert(user);
                if(i>0){
                    return new ResultVO(ResultStatus.OK,"login was successful",null);
                }else {

                    return new ResultVO(ResultStatus.NO,"login has failed",null);

                }


            }
//            Judge whether the user name has been registered, and then return the data to the front end, goodjob, none can influence you
            else {

                return new ResultVO(ResultStatus.NO,"The user name has already been registered",null);
            }



        }


    }
}

Front end setting token:

	doSubmit:function() {
			// Verification successful

			if(vm.isRight){
				var url=baseUrl+"/user/login";
				axios.get(url,{	
					params:{
						username:vm.username,password:vm.password

					} }
				
					).then((res)=>{

				console.log(res);

					var vo=res.data;

					console.log(vo);
					if(vo.code==1){
					
 // If the login is successful, the token is stored in the cookie
						setCookieValue("token",vo.msg);

						 window.location.href="index.html";
					}else{
						vm.tips="Wrong account or password";
					}



				});

The front-end shopping cart gets the token:

	<script type="text/javascript">
		// The shopController interface to access the shopping cart list when entering the shopping cart
		var baseUrl="http://localhost:8080/";    
		var vm=new Vue({
			el:"#app",
			data:{
				token:"",
			},
			created() {
				this.token=getCookieValue("token");
				console.log("token="+this.token);
				axios({
					method:"get",
					url:baseUrl+"shopcart/list",
					params:{
						token:this.token,
					}

				}).then(function (res) {
					console.log(res);
				});
			},
			




		})
		
		
		
		
		
		
		
		
		
		</script>

After logging in, you can get the token of the shopping cart. The front-end token is obtained with the package encapsulated in CookieUtils.js,

package com.qfedu.fmmall.controller;


import com.qfedu.fmmall.utils.Base64Utils;
import com.qfedu.fmmall.vo.ResultStatus;
import com.qfedu.fmmall.vo.ResultVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@CrossOrigin
@Api(value = "Provide interfaces related to shopping cart business",tags = "Shopping cart management interface")
@RequestMapping("/shopcart")
public class ShopCartController {

    @RequestMapping("/list")
    @ApiImplicitParam(dataType = "string",name = "token",value = "A sign of login",required = true)
    public ResultVO shopcartList(String token){
//        Check the entered token to see if it is the token logged in by the user
       //decrypt 
        String decode = Base64Utils.decode(token);
        if(token==null){
            return new ResultVO(ResultStatus.NO, "Please log in first", null);


        }else if(decode.endsWith("roothouzhicong")) {


            System.out.println("Interface related to shopping cart list------------");
            return new ResultVO(ResultStatus.OK, "success", null);


        }else {

            return new ResultVO(ResultStatus.NO, "Login has expired, please login again!!", null);

        }


    }
}

6, JWT(json Web Token) is a tool class encapsulated by others to generate relevant tokens

  1. The timeliness generated by user-defined token cannot be defined
  2. Poor safety

JWT structure:

[the external chain image transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-lzvjmruz-1633446686636) (C: \ users \ courageandlove \ appdata \ roaming \ typora \ user images \ image-20210823140849801. PNG)]

6.1 generate JWT

  • Add dependency:

    <!--        jwt generate -->
            <dependency>
                <groupId>com.auth0</groupId>
                <artifactId>java-jwt</artifactId>
                <version>3.10.3</version>
            </dependency>
    <!--        jjwt generate-->
            <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>
    
    

Userserviceimpl (login successful) generates a token:

 HashMap<String,Object> map=new HashMap<>();

                JwtBuilder builder= Jwts.builder();
                String token = builder.setSubject(username)   //The subject is the data carried in the token
                        .setIssuedAt(new Date()) //Set the generation time of the token
                        .setId(users.get(0).getUserId() + "") //Set the user id to tokenid
                        .setClaims(map)                         //map can store the user's role permission information
                        .setExpiration(new Date(System.currentTimeMillis() + 24 * 60 * 60 * 1000)) //Set the expiration time of the token
                        .signWith(SignatureAlgorithm.HS256, "houzhicong") //Set encryption method
                        .compact();

                //          Validation succeeded

The front-end ShopCart.html obtains the generated token through the Cookie:

	<script type="text/javascript">
		// The shopController interface to access the shopping cart list when entering the shopping cart
		var baseUrl="http://localhost:8080/";    
		var vm=new Vue({
			el:"#app",
			data:{
				token:"",
			},
			created() {
				this.token=getCookieValue("token");
				console.log("token="+this.token);
				axios({
					method:"get",
					url:baseUrl+"shopcart/list",
					Headers:{
						token:this.token,
					}

				}).then(function (res) {
					console.log(res);
				});
			},
			




		})
		
		
		
		
		
		
		
		
		
		</script>

The backend ShopController parses the Token:

package com.qfedu.fmmall.controller;


import com.qfedu.fmmall.utils.Base64Utils;
import com.qfedu.fmmall.vo.ResultStatus;
import com.qfedu.fmmall.vo.ResultVO;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.JwtParser;
import io.jsonwebtoken.Jwts;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@CrossOrigin
@Api(value = "Provide interfaces related to shopping cart business",tags = "Shopping cart management interface")
@RequestMapping("/shopcart")
public class ShopCartController {

    @RequestMapping("/list")
    @ApiImplicitParam(dataType = "string",name = "token",value = "A sign of login",required = true)
    public ResultVO shopcartList(String token){
//        Check the entered token to see if it is the token logged in by the user
//        String decode = Base64Utils.decode(token);
        if(token==null){
            return new ResultVO(ResultStatus.NO, "Please log in first", null);


        }else {
            JwtParser parser= Jwts.parser();
            parser.setSigningKey("houzhicong");//The parsing token must be consistent with the password generated when the token is generated

//            If the token is correct (the password is correct and within the validity period), it will be executed normally, otherwise an exception will be thrown
            try{


                Jws<Claims> claimsJws=parser.parseClaimsJws(token);

                Claims body=claimsJws.getBody();//Get user data in token
                String subject=body.getSubject();//Get the generated token setting subject
               String v1=body.get("key1",String.class);//Gets the value in the map of Claims stored when the token is generated
                return new ResultVO(ResultStatus.OK, "success", null);

            }catch (Exception e){
                return new ResultVO(ResultStatus.NO, "Login has expired, please login again!!", null);


            }



        }


    }
}

6.2 interception with interceptors

  1. Create a CheckTokenInterceptor
  2. Create an interceptor class InterceptorConfig
6.3.1 yes

package com.qfedu.fmmall.config;

import com.qfedu.fmmall.interceptor.CheckTokenInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
    @Autowired
    private CheckTokenInterceptor checkTokenInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new CheckTokenInterceptor())
                .addPathPatterns("/**")
                .excludePathPatterns("/user/**"
                ,"/doc.html"
                ,"/swagger-ui/**");
    }
}

6.3 using request header to pass token

The front end must carry a token request whenever it accesses a restricted resource. The token can be passed through the request line (params), the request header, and the request body (data), but it is used to using the header

axios passes the parameters in the value through the request header, using Headers instead of Params

axios({
					method:"get",
					url:baseUrl+"shopcart/list",
					Headers:{
						token:this.token,
					}

				}).then(function (res) {
					console.log(res);
				});
			},
			

6.3.1 the front end of the checktokeninterceptor class will send a pre insurance request (the second request can be made only after it passes), which needs to be released by the interceptor

package com.qfedu.fmmall.interceptor;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.qfedu.fmmall.vo.ResultStatus;
import com.qfedu.fmmall.vo.ResultVO;
import io.jsonwebtoken.*;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

@Configuration
public class CheckTokenInterceptor implements HandlerInterceptor {
//   You can see how it works


    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String token = request.getParameter("token");

//        System.out.println("token----------");

//        The front end will send a pre insurance request
        String method = request.getMethod();

        if("options".equalsIgnoreCase(method)){

            return true;
        }

        if(token==null){
//            Prompt the user to log in

            PrintWriter out = response.getWriter();
            ResultVO resultVO= new ResultVO(ResultStatus.NO, "Please log in first", null);
//         Draw out a method for
            doResponse(response,resultVO);


//            intercept
            return  false;

        }else {
//            Validate token

            try{

                JwtParser parser= Jwts.parser();
                parser.setSigningKey("houzhicong");
                Jws<Claims> claimsJws=parser.parseClaimsJws(token);
                return true;
            }catch (ExpiredJwtException e){
                ResultVO resultVO= new ResultVO(ResultStatus.NO, "Login expired, please login again", null);
                doResponse(response,resultVO);


                return false;
            }
            catch (UnsupportedJwtException e){
                ResultVO resultVO= new ResultVO(ResultStatus.NO, "Token Illegal, please respect yourself", null);
                doResponse(response,resultVO);


                return false;
            }

            catch (Exception e){
                ResultVO resultVO= new ResultVO(ResultStatus.NO, "Please log in first", null);
                doResponse(response,resultVO);


                return false;
            }





        }

    }

    private void doResponse(HttpServletResponse response,  ResultVO resultVO) throws IOException {
        response.setContentType("application/json");
        response.setCharacterEncoding("utf-8");
        PrintWriter out = response.getWriter();
//        Write resultVO in Json format
        String s = new ObjectMapper().writeValueAsString(resultVO);
        out.println(s);
        out.flush();
        out.close();


    }
}

Implementation of classified list of seven home pages

[the external chain image transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-wqm4qlh3-1633446686637) (C: \ users \ courageandlove \ appdata \ roaming \ typora \ typora user images \ image-20210823182623779. PNG)]

Draw a conclusion: when the amount of data is small, use one-time query, and when the amount of data is large, use multiple queries

Scheme 1: one-time query of three-level classification

  • Advantages only need one query
  • Disadvantages: the efficiency of database query is low, and the speed of page loading for the first time is slow

Scheme II:

  • First, only the first level classification is queried, and the user clicks / mouse to move to the first level classification to dynamically load the second level classification
  • Disadvantages: multiple connections to the database are required

7.1 interface implementation

[the external chain image transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-wfclktnv-1633446686638) (C: \ users \ courageandlove \ appdata \ roaming \ typora \ typora user images \ image-20210823204928346. PNG)]

sql statements queried at one time: the difference between inner join and left join. All data not associated to the left of left join will also be displayed

select 

c1.category_id 'category_id1',
c1.category_name 'category_name',
c1.category_level 'category_level',
c1.parent_id 'parent_id',
c1.category_icon 'category_icon',
c1.category_slogan 'category_slogan',
c1.category_pic 'category_pic',
c1.category_bg_color 'category_bg_color',

c2.category_id 'category_id2',
c2.category_name 'category_name2',
c2.category_level 'category_leve2',
c2.parent_id 'parent_id2',




c3.category_id 'category_id3',
c3.category_name 'category_name3',
c3.category_level 'category_leve3',
c3.parent_id 'parent_id3'

from category c1
left join category c2 on c2.parent_id=c1.category_id
left join category c3 on c3.parent_id=c2.category_id
where c1.category_level=1

select *from category c1
  inner join category c2 on c2.parent_id=c1.category_id
  left join category c3 on c3.parent_id=c2.category_id
   where c1.category_level=1
 
--Classified by parent parent_id Query level 1 parent_id=0
select *from category where parent_id=0,
  • Create a category information CategoryVO used to encapsulate the query

    In the Beans module, create a CategoryVO entity class under the entity package to encapsulate the response of Category and front-end data. This attribute is more than Category

    //The subcategory that implements the current classification in the entity class
    private List<CategoryVO> categories;
    
            public List<CategoryVO> getCategories() {
                return categories;
            }
    
            public void setCategories(List<CategoryVO> categories) {
                this.categories = categories;
            } 
    
  • Define a function in CategoryMapper

    package com.qfedu.fmmall.dao;
    
    import com.qfedu.fmmall.entity.Category;
    import com.qfedu.fmmall.entity.CategoryVO;
    import com.qfedu.fmmall.general.GeneralDao;
    
    import java.util.List;
    
    public interface CategoryMapper extends GeneralDao<Category> {
    //Using connection query to realize classification list query
        public List<CategoryVO> selectAllCategories();
        
        
        //    Subquery
        public List<CategoryVO> selectAllCategories2(int parentId);
    }
    
  • Mapping file configuration

    <?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.qfedu.fmmall.dao.CategoryMapper">
      <resultMap id="BaseResultMap" type="com.qfedu.fmmall.entity.Category">
        <!--
          WARNING - @mbg.generated
        -->
        <id column="category_id" jdbcType="VARCHAR" property="categoryId" />
        <result column="category_name" jdbcType="VARCHAR" property="categoryName" />
        <result column="category_level" jdbcType="INTEGER" property="categoryLevel" />
        <result column="parent_id" jdbcType="INTEGER" property="parentId" />
        <result column="category_icon" jdbcType="VARCHAR" property="categoryIcon" />
        <result column="category_slogan" jdbcType="VARCHAR" property="categorySlogan" />
        <result column="category_bg_color" jdbcType="VARCHAR" property="categoryBgColor" />
      </resultMap>
    
    
    
      <resultMap id="CategoryVoMap" type="com.qfedu.fmmall.entity.CategoryVO">
        <!--
          WARNING - @mbg.generated
        -->
        <id column="category_id1" jdbcType="VARCHAR" property="categoryId" />
        <result column="category_name1" jdbcType="VARCHAR" property="categoryName" />
        <result column="category_level1" jdbcType="INTEGER" property="categoryLevel" />
        <result column="parent_id1" jdbcType="INTEGER" property="parentId" />
        <result column="category_icon1" jdbcType="VARCHAR" property="categoryIcon" />
        <result column="category_slogan1" jdbcType="VARCHAR" property="categorySlogan" />
        <result column="category_bg_color1" jdbcType="VARCHAR" property="categoryBgColor" />
        <collection property="categories" ofType="com.qfedu.fmmall.entity.CategoryVO">
          <id column="category_id2" jdbcType="VARCHAR" property="categoryId" />
          <result column="category_name2" jdbcType="VARCHAR" property="categoryName" />
          <result column="category_level2" jdbcType="INTEGER" property="categoryLevel" />
          <result column="parent_id2" jdbcType="INTEGER" property="parentId" />
    
         <collection property="categories" ofType="com.qfedu.fmmall.entity.CategoryVO">
    
           <id column="category_id3" jdbcType="VARCHAR" property="categoryId" />
           <result column="category_name3" jdbcType="VARCHAR" property="categoryName" />
           <result column="category_level3" jdbcType="INTEGER" property="categoryLevel" />
           <result column="parent_id3" jdbcType="INTEGER" property="parentId" />
    
    
         </collection>
        </collection>
    
      </resultMap>
    
    
    
    
    
    
    
    
    
      <select id="selectAllCategories" resultMap="CategoryVoMap">
    
    select
    
    c1.category_id 'category_id1',
    c1.category_name 'category_name',
    c1.category_level 'category_level',
    c1.parent_id 'parent_id',
    c1.category_icon 'category_icon',
    c1.category_slogan 'category_slogan',
    c1.category_pic 'category_pic',
    c1.category_bg_color 'category_bg_color',
    
    c2.category_id 'category_id2',
    c2.category_name 'category_name2',
    c2.category_level 'category_leve2',
    c2.parent_id 'parent_id2',
    
    
    
    
    c3.category_id 'category_id3',
    c3.category_name 'category_name3',
    c3.category_level 'category_leve3',
    c3.parent_id 'parent_id3'
    
    from category c1
    left join category c2 on c2.parent_id=c1.category_id
    left join category c3 on c3.parent_id=c2.category_id
    where c1.category_level=1
    
    
      </select>
        
        
       
    
    
Use sub query to query the classification list:

```xml
    
<!--  Classification list query using sub query-->
  <resultMap id="CategoryMap2" type="com.qfedu.fmmall.entity.CategoryVO">
    <!--
      WARNING - @mbg.generated
    -->
    <id column="category_id" jdbcType="VARCHAR" property="categoryId" />
    <result column="category_name" jdbcType="VARCHAR" property="categoryName" />
    <result column="category_level" jdbcType="INTEGER" property="categoryLevel" />
    <result column="parent_id" jdbcType="INTEGER" property="parentId" />
    <result column="category_icon" jdbcType="VARCHAR" property="categoryIcon" />
    <result column="category_slogan" jdbcType="VARCHAR" property="categorySlogan" />
    <result column="category_bg_color" jdbcType="VARCHAR" property="categoryBgColor" />
    <collection property="categories" column="category_id" select="com.qfedu.fmmall.dao.CategoryMapper.selectAllCategories2"/>

<!--  there column="category_id"Will equal
//    Subquery
    public List<CategoryVO> selectAllCategories2(int parentId);Inside parentId;
-->

  </resultMap>






  <!--  Classification list query using sub query-->
  <select id="selectAllCategories2" resultMap="CategoryMap2">
    select

     category_id,
category_name,
category_level,
parent_id,
category_icon,
category_slogan,
category_pic,
category_bg_color


from category where parent_id=#{parentId};
  </select>


7.2 business layer implementation

CategoryService

package com.qfedu.fmmall.service;

import com.qfedu.fmmall.vo.ResultVO;

public interface CategoryService {

    public ResultVO queryAllCategory();
}

CategoryServiceimpl:

package com.qfedu.fmmall.service.impl;

import com.qfedu.fmmall.dao.CategoryMapper;
import com.qfedu.fmmall.entity.CategoryVO;
import com.qfedu.fmmall.service.CategoryService;
import com.qfedu.fmmall.vo.ResultStatus;
import com.qfedu.fmmall.vo.ResultVO;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;


@Service
public class CategoryServiceimpl implements CategoryService {
    @Resource
    private CategoryMapper categoryMapper;

    @Override
    public ResultVO queryAllCategory() {
        List<CategoryVO> categoryVOS = categoryMapper.selectAllCategories2(0);

        return new ResultVO(ResultStatus.OK,"success",categoryVOS);


    }
}

Control layer implementation

indexController implementation:

@Autowired
    private CategoryService categoryService;


    @RequestMapping("category-list")
    @ApiOperation("Commodity classification query interface")
    public ResultVO queryAllCategory(){

        return categoryService.queryAllCategory();
    }


8, Realization of commodity recommendation function

8.1 process analysis

[the external chain image transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-yol4wuqr-1633446686638) (C: \ users \ courageandlove \ appdata \ roaming \ typora \ typora user images \ image-20210828192719883. PNG)]

Recommend the latest products on the shelves

8.2 interface development

8.2.1 implementation of database
  • sql statement implementation

    -- Product recommendation query the latest shelf information
    select *from product order by create_time desc limit 0,3;
    -- Product picture query id Query product pictures
    select *from product_img where item_id=2;
    
    

    Create ProductVO under the sub project beans project (add this attribute private List imgs; because a product contains multiple tables)

    package com.qfedu.fmmall.entity;
    
    import javax.persistence.Column;
    import javax.persistence.Id;
    import java.util.Date;
    import java.util.List;
    
    public class ProductVO {
        /**
         * Commodity id
         */
        @Id
        @Column(name = "product_id")
        private Integer productId;
    
        /**
         * Trade name
         */
        @Column(name = "product_name")
        private String productName;
    
        /**
         * Commodity classification id
         */
        @Column(name = "category_id")
        private Integer categoryId;
    
        /**
         * First level classification foreign key id
         */
        @Column(name = "root_category_id")
        private Integer rootCategoryId;
    
        /**
         * sales volume
         */
        @Column(name = "sold_num")
        private Integer soldNum;
    
        /**
         * Commodity status
         */
        @Column(name = "product_status")
        private Integer productStatus;
    
        /**
         * Commodity content
         */
        private String content;
    
        /**
         * Creation time
         */
        @Column(name = "create_time")
        private Date createTime;
    
        /**
         * Update time
         */
        @Column(name = "update_time")
        private Date updateTime;
    
    
        private List<ProductImg> imgs;
    
        public List<ProductImg> getImgs() {
            return imgs;
        }
    
        public void setImgs(List<ProductImg> imgs) {
            this.imgs = imgs;
        }
    
        @Override
        public String toString() {
            return "ProductVO{" +
                    "imgs=" + imgs +
                    '}';
        }
    
        /**
         * Get product id
         *
         * @return product_id - Commodity id
         */
        public Integer getProductId() {
            return productId;
        }
    
        /**
         * Set item id
         *
         * @param productId Commodity id
         */
        public void setProductId(Integer productId) {
            this.productId = productId;
        }
    
        /**
         * Get product name
         *
         * @return product_name - Trade name
         */
        public String getProductName() {
            return productName;
        }
    
        /**
         * Set product name
         *
         * @param productName Trade name
         */
        public void setProductName(String productName) {
            this.productName = productName == null ? null : productName.trim();
        }
    
        /**
         * Get product category id
         *
         * @return category_id - Commodity classification id
         */
        public Integer getCategoryId() {
            return categoryId;
        }
    
        /**
         * Set product category id
         *
         * @param categoryId Commodity classification id
         */
        public void setCategoryId(Integer categoryId) {
            this.categoryId = categoryId;
        }
    
        /**
         * Get the first level classification foreign key id
         *
         * @return root_category_id - First level classification foreign key id
         */
        public Integer getRootCategoryId() {
            return rootCategoryId;
        }
    
        /**
         * Set the first level classification foreign key id
         *
         * @param rootCategoryId First level classification foreign key id
         */
        public void setRootCategoryId(Integer rootCategoryId) {
            this.rootCategoryId = rootCategoryId;
        }
    
        /**
         * Get sales
         *
         * @return sold_num - sales volume
         */
        public Integer getSoldNum() {
            return soldNum;
        }
    
        /**
         * Set sales volume
         *
         * @param soldNum sales volume
         */
        public void setSoldNum(Integer soldNum) {
            this.soldNum = soldNum;
        }
    
        /**
         * Get product status
         *
         * @return product_status - Commodity status
         */
        public Integer getProductStatus() {
            return productStatus;
        }
    
        /**
         * Set item status
         *
         * @param productStatus Commodity status
         */
        public void setProductStatus(Integer productStatus) {
            this.productStatus = productStatus;
        }
    
        /**
         * Get product content
         *
         * @return content - Commodity content
         */
        public String getContent() {
            return content;
        }
    
        /**
         * Set product content
         *
         * @param content Commodity content
         */
        public void setContent(String content) {
            this.content = content == null ? null : content.trim();
        }
    
        /**
         * Get creation time
         *
         * @return create_time - Creation time
         */
        public Date getCreateTime() {
            return createTime;
        }
    
        /**
         * Set creation time
         *
         * @param createTime Creation time
         */
        public void setCreateTime(Date createTime) {
            this.createTime = createTime;
        }
    
        /**
         * Get update time
         *
         * @return update_time - Update time
         */
        public Date getUpdateTime() {
            return updateTime;
        }
    
        /**
         * Set update time
         *
         * @param updateTime Update time
         */
        public void setUpdateTime(Date updateTime) {
            this.updateTime = updateTime;
        }
    }
    

    ProductMapper file:

    package com.qfedu.fmmall.dao;
    
    import com.qfedu.fmmall.entity.Product;
    import com.qfedu.fmmall.entity.ProductVO;
    import com.qfedu.fmmall.general.GeneralDao;
    
    import java.util.List;
    
    public interface ProductMapper extends GeneralDao<Product> {
    //    Query recommended product information
        public List<ProductVO> selectRecommendProducts();
    }
    

    ProductImgMapper file:

    package com.qfedu.fmmall.dao;
    
    import com.qfedu.fmmall.entity.ProductImg;
    import com.qfedu.fmmall.general.GeneralDao;
    
    import java.util.List;
    
    public interface ProductImgMapper extends GeneralDao<ProductImg> {
        public List<ProductImg> selectProductImgByProductId(int productId);
    }
    

    Implementation of ProductMapper.xml file

    Note the query statement:

     <collection property="imgs" column="product_id"
                    select="com.qfedu.fmmall.dao.ProductImgMapper.selectProductImgByProductId"></collection>
    
    <?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.qfedu.fmmall.dao.ProductMapper">
      <resultMap id="BaseResultMap" type="com.qfedu.fmmall.entity.Product">
        <!--
          WARNING - @mbg.generated
        -->
        <id column="product_id" jdbcType="INTEGER" property="productId" />
        <result column="product_name" jdbcType="VARCHAR" property="productName" />
        <result column="category_id" jdbcType="INTEGER" property="categoryId" />
        <result column="root_category_id" jdbcType="INTEGER" property="rootCategoryId" />
        <result column="sold_num" jdbcType="INTEGER" property="soldNum" />
        <result column="product_status" jdbcType="INTEGER" property="productStatus" />
        <result column="content" jdbcType="VARCHAR" property="content" />
        <result column="create_time" jdbcType="TIMESTAMP" property="createTime" />
        <result column="update_time" jdbcType="TIMESTAMP" property="updateTime" />
      </resultMap>
    
      <resultMap id="ProductVoMap" type="com.qfedu.fmmall.entity.ProductVO">
        <!--
          WARNING - @mbg.generated
        -->
        <id column="product_id" jdbcType="INTEGER" property="productId" />
        <result column="product_name" jdbcType="VARCHAR" property="productName" />
        <result column="category_id" jdbcType="INTEGER" property="categoryId" />
        <result column="root_category_id" jdbcType="INTEGER" property="rootCategoryId" />
        <result column="sold_num" jdbcType="INTEGER" property="soldNum" />
        <result column="product_status" jdbcType="INTEGER" property="productStatus" />
        <result column="content" jdbcType="VARCHAR" property="content" />
        <result column="create_time" jdbcType="TIMESTAMP" property="createTime" />
        <result column="update_time" jdbcType="TIMESTAMP" property="updateTime" />
        <collection property="imgs" column="product_id"
                    select="com.qfedu.fmmall.dao.ProductImgMapper.selectProductImgByProductId"></collection>
      </resultMap>
    
    
      <select id="selectRecommendProducts" resultMap="ProductVoMap">
    
    
    select
    product_id,
    product_name,
    category_id,
    root_category_id,
    sold_num,
    product_status,
    content,
    create_time,
    update_time
    
    
    from product order by create_time desc limit 0,3;
    
    
      </select>
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    </mapper>
    

    8.2.2 business layer implementation

    package com.qfedu.fmmall.service;
    
    import com.qfedu.fmmall.entity.ProductVO;
    import com.qfedu.fmmall.vo.ResultVO;
    
    import java.util.List;
    
    public interface ProductService {
        public ResultVO selectRecommendProducts();
    
    }
    
    

    8.2.3 realization of control layer

     @ApiOperation("Commodity recommendation query information interface")
        @RequestMapping(value = "/list-recommends",method = RequestMethod.GET)
        public ResultVO selectProductRecommend(){
            ResultVO resultVO = productService.selectRecommendProducts();
            return resultVO;
    
    
        }
    
    

9, Display of product details (relevant display in Introduction.html)

Click the commodity recommendation, commodity rotation chart and commodity list page. Click the commodity to enter the commodity details page.

  1. Get product id
  2. You can query the detailed information of commodities (basic information of commodities, commodity packages, commodity picture information.)
  3. Return commodity parameters to the front end

9.1 interface implementation

9.1.1 product details interface

Basic product information (product), product package (sku), product picture (product_img)

  • SQL

    -- According to commodity id Inquire about product details
    select *from product where product_id=3;
    -- According to commodity id Query product picture details
    select *from product_img where item_id=3;
    -- According to commodity id Query the package of current product
    select *from product_sku where product_id=3;
    
  • You can use subqueries to implement this related operation

  • dao interface implementation (obtain the detailed information of goods through three tables: product, product_img and product_sku)

    @Repository
    public interface ProductMapper extends GeneralDao<Product> {
    //    Query recommended product information
        public List<ProductVO> selectRecommendProducts();
    }
    
    
    package com.qfedu.fmmall.dao;
    
    import com.qfedu.fmmall.entity.ProductImg;
    import com.qfedu.fmmall.general.GeneralDao;
    import org.springframework.stereotype.Repository;
    
    import java.util.List;
    @Repository
    public interface ProductImgMapper extends GeneralDao<ProductImg> {
        public List<ProductImg> selectProductImgByProductId(int productId);
    }
    
    package com.qfedu.fmmall.dao;
    
    import com.qfedu.fmmall.entity.ProductSku;
    import com.qfedu.fmmall.general.GeneralDao;
    import org.springframework.stereotype.Repository;
    
    @Repository
    public interface ProductSkuMapper extends GeneralDao<ProductSku> {
    }
    
    
  • Business layer implementation

//Here, transactions are not required, but if some transactions are called, I will also join the transaction
//    The default isolation level for transactions is repeatable read
    @Transactional(propagation = Propagation.SUPPORTS)
    public ResultVO selectProductBasicInfo(String productId) {
//      1. Query the basic information of commodities



        Example example = new Example(Product.class);
        Example.Criteria criteria = example.createCriteria();
        criteria.andEqualTo("productId",productId);
        criteria.andEqualTo("productStatus",1);

        List<Product> products = productMapper.selectByExample(example);
//        System.out.println(products);


        if(products.size()>0){
            //      2. Query the picture information of goods
           Example example1 = new Example(ProductImg.class);
            Example.Criteria criteria1 = example1.createCriteria();
            criteria1.andEqualTo("itmeId",productId);
            List<ProductImg> productImgs = productImgMapperMapper.selectByExample(example1);
//            System.out.println(productImgs);


            //        3. Query the package information of goods

            Example example2 = new Example(ProductSku.class);
            Example.Criteria criteria2 = example2.createCriteria();
            criteria2.andEqualTo("productId",productId);
            criteria2.andEqualTo("status",1);
            List<ProductSku> productSkus = productSkuMapper.selectByExample(example2);
//            System.out.println(productSkus);


//            Put the details of all products into HashMap for use
            HashMap<String,Object> basicInfo=new HashMap<>();
            basicInfo.put("product",products.get(0));
            basicInfo.put("productImgs",productImgs);
            basicInfo.put("productSkus",productSkus);

            return new ResultVO(ResultStatus.OK,"success",basicInfo);





        }else {

            new ResultVO(ResultStatus.NO,"Failed to query commodity basic information",null);
        }
        return null;
    }
  • Control layer implementation (here, the detailed information of the product is put into the Object of ResultVO)
//    Product details query

    @RequestMapping(value = "/detail/{pid}",method = RequestMethod.GET)
    public ResultVO getProductBasicInfo(@PathVariable("pid") String productId){

        ResultVO resultVO = productService.selectProductBasicInfo(productId);
//        System.out.println(resultVO);

        return resultVO;

    }



10, Display the information of commodity evaluation (related linked table query through user and commodity comment table)

-- According to the comments id Query comment information, associate user table to query user information
select u.username,u.user_img,c.* from product_comments c
inner join users u 
on c.user_id=u.user_id
where c.product_id=3

10.1 create a new VO, ProductCommentsVO (one-to-one linked table query does not need to declare another entity class in the entity class)

package com.qfedu.fmmall.entity;

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

import javax.persistence.Column;
import javax.persistence.Table;
import java.util.Date;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class ProductCommentsVO {


    private Integer productId;
    private String productName;
    private Integer orderItemId;

    private String isannonymouns;
    private Integer commType;
    private Integer commLevel;
    private String commImgs;
    private String sepcName;
    private Integer replyStatus;
    private String replyContent;
    private Date replyTime;
    private Integer isShow;


//Used to encapsulate user data corresponding to comments
    private Integer userId;
    private String username;
    private String nickname;
    private String userImg;


}

Define the corresponding interface in Mapper:

package com.qfedu.fmmall.dao;

import com.qfedu.fmmall.entity.ProductComments;
import com.qfedu.fmmall.entity.ProductCommentsVO;
import com.qfedu.fmmall.general.GeneralDao;
import org.springframework.stereotype.Repository;

import java.util.List;


@Repository
public interface ProductCommentsMapper extends GeneralDao<ProductComments> {

    public List<ProductCommentsVO> selectCommentsByProductId(int productId);
}

11, Function realization of adding shopping cart

10.1 process analysis:

Click Add shopping cart -------- commodity, select package id, package attribute, quantity, token -------- to verify the token

[the external chain image transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-m6dhvlut-1633446686639) (C: \ users \ courageandlove \ appdata \ roaming \ typora \ typora user images \ image-20210917194652463. PNG)]

10.2 database related operations

  1. Add field sku_props

  2. [the external chain image transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-aok2tjfo-1633446686640) (C: \ users \ courageandlove \ appdata \ roaming \ typora \ typora user images \ image-20210917195750634. PNG)]

    shopping_cart

    After the table is generated, reverse engineering is used to regenerate the shopping table_ Related structure of cart table. Modify generalConfig.xml and change% to shopping_cart

    Note that * *% means that all tables are generated**

    <table tableName="shopping_cart"></table>
    

10.2.1 the front-end implementation of the purchased quantity adds + and - click events in vue's methods

	changeNum:function(m){
						if(m==-1 && this.num>1){
						this.num=this.num-1;

						}else if(m==1 && this.num<this.productSkus[this.currentSkuIndex].stock){
							this.num=parseInt(this.num)   +1;


						}


					},

For the binding of commodity quantity, v-model="num" can be used for two-way binding

<dd>
															<input id="min" class="am-btn am-btn-default"  type="button" value="-" @click="changeNum(-1)"/>
															<input id="text_box"  type="text" v-model="num" style="width:30px;" />
															<input id="add" class="am-btn am-btn-default"  type="button" value="+" @click="changeNum(1)" />
															<span id="stock1" class="tb-hidden">stock<span class="stock">{{productSkus[currentSkuIndex].stock}}</span>piece</span>
														</dd>

10.2.2 add a click event to the add shopping cart button

<li>
								<div class="clearfix tb-btn tb-btn-basket theme-login">
									<a id="LikBasket" title="add to cart" href="" @click="addShopCart()"><i></i>add to cart</a>
								</div>
							</li>

Put the relevant information of the added shopping cart into the cart class:

					addShopCart(){
					var uid=getCookieValue("userId");


						var propStr="";
						// Convert package properties to strings
						for(var key in this.chooseskuProps){
							propStr+=key+":"+this.chooseskuProps[key]+";";
}


						var cart={
									
									"cartNum": this.num,
									"cartTime": "",
									"productId": this.productId,
									"productPrice": this.productSkus[this.currentSkuIndex].sellPrice,
									"skuId": this.productSkus.skuId,
									"skuProps":propStr,
									"userId": uid
								};

						//Get token from cookie

						var token=getCookieValue("token");
						console.log("---token-------");
						console.log(token);

						// Put the information of the shopping cart into the database
						var url5=baesUrl+"shopcart/add";
						axios.post(
							{
								url:url5,
								methods:"post",
								headers:{
									token:token
								},
								data:cart
}


						).then( res=>{

							console.log("------res-----"+res);



						}

						);
					 


					}

12, User not logged in when adding shopping cart

12.1 add shopping cart user is not logged in, business processing method:

  1. When querying the product details page, you will be prompted to log in first and jump to the login page
  2. When you click Add shopping cart, the pop-up window will display login first, complete login, and click Add shopping cart
  3. Click Add shopping cart to jump to the login page. After login, you will jump to the product details page.

12.2 we use the third most difficult method to solve the problem

[the external chain image transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-yqqlurrz-1633446686641) (C: \ users \ courageandlove \ appdata \ roaming \ typora \ typora user images \ image-20210920173405055. PNG)]

12.3 prompt of success / failure of adding shopping cart using Layui

  • Introducing lay UI CDN

    <!-- introduce layui.css -->
    <link rel="stylesheet" href="//unpkg.com/layui@2.6.8/dist/css/layui.css">
     
    <!-- introduce layui.js -->
    <script src="//unpkg.com/layui@2.6.8/dist/layui.js">
          
    
    12.3.1 pop up window assembly for declaring layui
    [the external chain image transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-hxrsswp2-1633446686642) (C: \ users \ courageandlove \ appdata \ roaming \ typora \ typora user images \ image-20210922163219931. PNG)]

    12.3.2 prompt for success or failure

    [the external chain image transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-mh4x9lzj-1633446686642) (C: \ users \ courageandlove \ appdata \ roaming \ typora \ typora user images \ image-20210922163850890. PNG)]

List of shopping carts

flow chart:

[the external chain image transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-nmub7nop-1633446686643) (C: \ users \ courageandlove \ appdata \ roaming \ typora \ typora user images \ image-20210922164854113. PNG)]

step

  1. Get user user_id
  2. Via user_id to obtain the information of the shopping cart (including the name of the commodity and the information of the commodity picture)
  3. Return the shopping cart information data to the front end.
  4. That is, there are three tables: shopping_cart table, product table and product picture table (product_img queries the main map of products according to product id)

13.1 implementation of database dao interface

  1. sql statement

    select  c.*,p.product_name,i.url from shopping_cart c
    inner join product p
    inner join product_img i
    on c.product_id=p.product_id 
    and i.item_id=p.product_id
    where user_id=2 and i.is_main=1;
    
   
2. dao Interface

  ```java
    List<ShoppingCartVO>  selectShoppingCartByUserId(int userid);

13.2 POJO interface implementation

[the external chain image transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-hvwczarc-1633446686643) (C: \ users \ courageandlove \ appdata \ roaming \ typora \ user images \ image-20210922172106911. PNG)]

Pay attention to the database shopping_ These two fields do not need to be added to the cart table, but through product_img table is associated with the column attribute of the product table

13.3 modify shopping cart

[the external chain image transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-a4sycbbb-1633446686644) (C: \ users \ courageandlove \ appdata \ roaming \ typora \ typora user images \ image-20210924171559447. PNG)]

13.31 modify the number of shopping carts through this:

[the external chain image transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-gntrjxem-1633446686644) (C: \ users \ courageandlove \ appdata \ roaming \ typora \ typora user images \ image-20210924204100420. PNG)]

13.32 implementation of changnum function:
	
           methods:{
			   changeNum(event){
				   var oper=event.srcElement.dataset.oper;

				   var index=event.srcElement.dataset.id;
				   console.log(oper);
				   if(oper=="+"){
					  
						this.shoppingCartsSC[index].cartNum=parseInt(this.shoppingCartsSC[index].cartNum) +1;
					   

				   }else{

					if(this.shoppingCartsSC[index].cartNum>1){
					this.shoppingCartsSC[index].cartNum=parseInt(this.shoppingCartsSC[index].cartNum)-1;
  					}
					

				   }
				   //   Modified cartId and cartNum
					var cartId=this.shoppingCartsSC[index].cartId;
					var cartNum=this.shoppingCartsSC[index].cartNum;
					var url1=baseUrl+"shopcart/update/"+cartId+"/"+cartNum;
					axios({
						url:url1,
						method:"put",
						params:{
							token:this.token
						}

					}).then((res)=>{

						console.log(res);

})



			   },

// 			addNum:function(event){
// 				//This can print the value of the bound id
// 				console.log("--add"+event.srcElement.dataset.id);
// 			    var index=event.srcElement.dataset.id;
// 				this.shoppingCartsSC[index].cartNum=parseInt(this.shoppingCartsSC[index].cartNum) +1;

				
				
// 			},
			
// 			mulNum:function(event){
			
// 			    var index=event.srcElement.dataset.id;
// 				if(this.shoppingCartsSC[index].cartNum>1){
// 					this.shoppingCartsSC[index].cartNum=parseInt(this.shoppingCartsSC[index].cartNum)-1;
//   }
				
				
				
// 			}



		   }

XIV. Implementation of order submission and settlement function of shopping cart

14.1 implementation process analysis

[the external chain image transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-r7ezlbeb-1633446686645) (C: \ users \ courageandlove \ appdata \ roaming \ typora \ typora user images \ image-2021092421472021. PNG)]

15, Order submission and payment process

15.1 process analysis

[the external chain image transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-v5zo96td-1633446686646) (C: \ users \ courageandlove \ appdata \ roaming \ typora \ typora user images \ image-20210926112259576. PNG)]

15.2 implementation of order addition interface

15.3 database operation

  • Obtain the receiving address information according to the receiving id (tkmapper)
  • Query the details of the shopping cart according to the shopping cart ID (you need to query the commodity name, sku name, inventory, commodity picture and commodity price in association) -- to obtain the data for generating the commodity snapshot, just add an additional stock field in ShoppingCartVO, and then add the field to be queried in shopcartmapper.xml
  • Save order information (tkMapper)
  • Modify Inventory (tkMapper)
  • Save product snapshot (tkMapper)

15.4 serviceimpl layer implementation note: this method needs to add @ Transactional, that is, when the order is generated, the snapshot must also be generated

  1. Method to generate OrderId UUID.random().toString()
  2. Generate System.currentTimeMillis()+(new Random().nextInt(9999)+100) + "by timestamp
package com.qfedu.fmmall.service.impl;

import com.qfedu.fmmall.dao.OrdersItemMapper;
import com.qfedu.fmmall.dao.OrdersMapper;
import com.qfedu.fmmall.dao.ProductSkuMapper;
import com.qfedu.fmmall.dao.ShoppingCartMapper;
import com.qfedu.fmmall.entity.Orders;
import com.qfedu.fmmall.entity.OrdersItem;
import com.qfedu.fmmall.entity.ProductSku;
import com.qfedu.fmmall.entity.ShoppingCartVO;
import com.qfedu.fmmall.service.OrderService;
import com.qfedu.fmmall.vo.ResultStatus;
import com.qfedu.fmmall.vo.ResultVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import tk.mybatis.mapper.entity.Example;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.SQLException;
import java.util.*;

import static java.math.BigDecimal.*;

@Service

public class OrderServiceimpl implements OrderService{

    @Autowired
    private ShoppingCartMapper shoppingCartMapper;
    @Autowired
    private OrdersMapper ordersMapper;
    @Autowired
    private OrdersItemMapper ordersItemMapper;
    @Autowired
    private ProductSkuMapper productSkuMapper;
    /* userId 1(zhangsan) 3(houzhicong)
    *cids  "39,40,41"
    * @return
    *
    * */

//    int userId, String receiverName,
//    String receiverMobile,String address,
//    double price,int payType,String orderRemark use the Orders object to receive these


//    To save an order
//    1. Query the shopping cart details of the selected purchase
//    2. Verify inventory
//    3. Save order
//    4. Save order snapshot
//    5. The shopping cart record needs to be deleted if the purchase is successful
//    You can know that these four steps need to succeed or fail at the same time, which conforms to the operation (ACID) of a transaction
    @Transactional
    public Map<String,String> addOrder(List<Integer> cids, Orders orders) throws  SQLException{
         Map<String,String> map=new HashMap<>();
//        Query the detailed record of shopping cart (including inventory) according to cids
        List<ShoppingCartVO> shoppingCartVOList = shoppingCartMapper.selectShoppingcartByids(cids);


//        Verify inventory
        boolean f=true;

        String untitled="";
        for (ShoppingCartVO sc :shoppingCartVOList
                ) {
            if(Integer.valueOf(sc.getCartNum())>sc.getStock()){
                f=false;

            }

//            Get all product names to split and splice them into strings
            untitled=untitled+sc.getProductName()+",";

        }
        if(f){
//            Indicates that the inventory is sufficient to save
//            1. Userid 2 untitled name 3 recipient address, name, telephone, address
//            4. Total price 5. Payment method
//            6. Time when the order was created
//            7. Order initial status 1 to be paid
        orders.setStatus(1);
        orders.setUntitled(untitled);
        orders.setCreateTime(new Date());
        orders.setCancelTime(new Date());
        orders.setDeliveryTime(new Date());

//        Generate order number
            String orderId = UUID.randomUUID().toString().replace("-", "");
            orders.setOrderId(orderId);


//            Save order
            int i=ordersMapper.insert(orders);
            if(i>0){


//               Orderitem generates a product snapshot
//                List<OrdersItem> ordersItemList=new ArrayList<>();
                for (ShoppingCartVO sc :shoppingCartVOList) {
//                    Number of the generated order

                    int cnum=Integer.valueOf(sc.getCartNum());
                  String itemid=System.currentTimeMillis()+(new Random().nextInt(9999)+100)+"";
                    String itemid1 = itemid.substring(1, 10);


//                 Note that double needs to be converted to Bigdecimal type


//                public OrdersItem(Integer orderId, Integer productId,
//                String productName,
//                String productImg, Integer skuId, String skuName,
//                BigDecimal productPrice, Integer buyCounts,
//                BigDecimal totalAmount, Date basketDate, Date buyTime,
//                Integer isComment)
                int itemid2=Integer.parseInt(itemid1);
                OrdersItem ordersItem=  new OrdersItem();
                ordersItem.setOrderId(itemid2);
                ordersItem.setProductId(Integer.valueOf(sc.getProductId()));
                ordersItem.setProductName(sc.getProductName());
                ordersItem.setProductImg(sc.getProductImg());
                ordersItem.setSkuId(Integer.valueOf(sc.getSkuId()));
                    System.out.println(sc.getSkuName());
                    ordersItem.setSkuName(sc.getSkuName());
                System.out.println(sc.getSellPrice());
                ordersItem.setProductPrice(new BigDecimal(String.valueOf(sc.getProductPrice())));

                ordersItem.setBuyCounts(cnum);
                ordersItem.setTotalAmount(sc.getProductPrice());
                ordersItem.setBasketDate(new Date());
                ordersItem.setBuyTime(new Date());
                ordersItem.setIsComment(0);

//                ordersItemList.add(ordersItem);
                      int m=ordersItemMapper.insert(ordersItem);

                    }

//                int j = ordersItemMapper.insertList(ordersItemList);
//  Deduct inventory???
//                Modify inventory according to package Id
                for (ShoppingCartVO sc :shoppingCartVOList
                ) {
                    String skuId = sc.getSkuId();
                    int newStock=sc.getStock()-Integer.valueOf(sc.getCartNum());

//                    Example example = new Example(ProductSku.class);
//                    Example.Criteria criteria = example.createCriteria();
//                    criteria.andEqualTo("skuId",skuId);


//                    ProductSku productSku = productSkuMapper.selectByPrimaryKey(skuId);
                    ProductSku productSku=new ProductSku();
                    productSku.setSkuId(skuId);
                    productSku.setStock(String.valueOf(newStock));
//                    productSku.setSkuImg(null);

                    productSkuMapper.updateByPrimaryKeySelective(productSku);

                }

//  Successfully deleted shopping cart record after saving order
                for (Integer cid:cids
                     ) {
                    shoppingCartMapper.deleteByPrimaryKey(cid);

                }

                 map.put("orderId",orderId);
                 map.put("productNames",untitled);

              return   map;


            }



        }else{
//            Insufficient
   return null;

        }

        return null;
    }
}

swagger error reporting solution

For input String :""

Add log configuration in application.yml:

logging:
  level:
    io.swagger.models.parameters.AbstractSerializableParameter: error

XVI. Query of commodity classification information

16.1 process analysis

[the external chain image transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-ckofaxhr-1633446686646) (C: \ users \ courageandlove \ appdata \ roaming \ typora \ typora user images \ image-20210929154617257. PNG)]

16.2 interface development

16.2.1 query commodity interface by category

Posted by dave007 on Tue, 05 Oct 2021 10:07:30 -0700