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
-
The configuration of the database needs to be modified
-
You need to modify the configuration of the generation location of POJO, mapper and mapper.xml files
-
<!--%Indicates that all tables in the current database will be generated--> <table tableName="%"></table>
-
<!-- Specify build Mapper Inheritance template for --> <plugin type="tk.mybatis.mapper.generator.MapperPlugin"> <property name="mappers" value="com.hou.general.GeneralDao"/> </plugin>
-
Specify the path to your Configuration generatorConfig.xml file
-
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:
- The user logs in and stores the information in the session
- 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
- The timeliness generated by user-defined token cannot be defined
- 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
- Create a CheckTokenInterceptor
- 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.
- Get product id
- You can query the detailed information of commodities (basic information of commodities, commodity packages, commodity picture information.)
- 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
-
Add field sku_props
-
[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:
- When querying the product details page, you will be prompted to log in first and jump to the login page
- When you click Add shopping cart, the pop-up window will display login first, complete login, and click Add shopping cart
- 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
- Get user user_id
- Via user_id to obtain the information of the shopping cart (including the name of the commodity and the information of the commodity picture)
- Return the shopping cart information data to the front end.
- 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
-
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
- Method to generate OrderId UUID.random().toString()
- 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)]