毕业设计学习锋迷商城的的笔记(自己设计并手写后台商品管理,分类管理,用户,地址管理系统,订单管理,微信支付(内网穿透))
文章目录
- 自己添加的后端管理页面
- 视频演示效果
- 论文地址
- 后台代码视频实现讲解思路
- 1. 商品管理
- 2.商品分类管理
- 3.商品地址管理
- 4.权限管理系统
- 权限管理系统文章地址
- 5.订单管理
- 5.1
- 6.商品品牌管理
- 锋迷商城项目
- 1.通过Maven聚合工程搭建项目:
- 1. 创建一个Maven的父项目,然后修改它的pom.xml文件,可以删除src等一些没有用的目录
- 2.在父项目下面创建多个module,包括(common,beans,mapper,service,api)把它们全部打包成jar包
- 3.由于mapper层需要调用beans层(pojo),需要在pom.xml文件中,然后可以在mapper中加入相关的依赖。
- 4.创建service需要调用mapper,和common,需要在pom.xml文件中加上
- 5.api层需要接收前端的请求所以需要我们创建一个SpringBoot工程,你可以创建一个Maven工程,然后加入相关的依赖
- 6.api子工程,对外提供接口
- 锋迷商城数据库设计
- 2.软件开发步骤
- 3.数据库设计流程
- 3.数据建模工具PDMan
- Spu和Sku的区别
- 4.锋城业务流程设计
- 4.1用户管理9业务流程分析
- 5接口介绍
- 5.1接口规范
- 5.2Swagger(自动生成服务器接口的规范性文档)
- 5.2.1引入相关的依赖:
- 5.2.2 创建相关的配置类
- 5.2.3根据你的配置的端口号进行相关的测试
- 5.2.4 swagger提供了一套注解对每个接口进行详细的说明
- 5.2.5swagger-ui插件使用
- 1.api的module加入依赖
- 2.进行访问,然后可以使用它进行相关的测试
- 一、锋迷商城设计及实现用户管理
- 1.UserDao接口的创建:
- 2.UserMapper.xml
- 3.UserService
- 4.UserServiceimpl:
- 5.api模块的UserController:
- 6.ResultVO一个和前端进行数据交互的类
- 7.在common模块的MD5Utils类:
- 二、逆向工程
- 7.1逆向工程配置
- 7.2在pom.xml文件中指定generatorConfig.xml文件的路径
- 三、跨域问题
- 四、前端页面的传值
- 4.1Cookie使用(自定义封装一个js,cookie_utils.js)
- 4.2localStorage
- 4.3Vue实现登录
- 五、前后端分离开发用户认证的问题
- 5.1单体项目中:
- 5.2前后端分离项目中
- 基于token认证的用户代码实现
- 六、JWT(json Web Token)一个别人封装好的工具类生成相关的token
- 6.1生成JWT
- 6.2使用拦截器进行拦截
- 6.3使用请求头进行传递token
- axios通过请求头传值 里面的参数用Headers 不用Params
- 6.3.1 **CheckTokenInterceptor类** 前端会发送预险性请求(只有它通过以后才可以进行第二次请求),需要拦截器进行放行
- 七首页的分类列表的的实现
- 7.1接口实现
- 7.2业务层实现
- 控制层实现
- 八、商品的推荐功能实现
- 8.1 流程分析
- 8.2接口开发
- 8.2.1数据库的实现
- ProductMapper文件:
- ProductImgMapper文件:
- ProductMapper.xml文件实现
- 8.2.2业务层实现
- 8.2.3控制层实现
- 九、商品详情显示(在Introduction.html进行相关的显示)
- 9.1接口实现
- 9.1.1 商品详情接口
- 十、显示商品评价的信息(通过用户和商品评论表进行相关的连表查询)
- 10.1 新建的VO,ProductCommentsVO (一对一的连表查询可以不用在实体类中声明另一个实体类)
- 十一、添加购物车的功能实现
- 10.1流程分析:
- 10.2数据库的相关的操作
- 10.2.1 购买的数量的前端实现在vue的methods中添加+和-的点击事件
- 10.2.2给加入购物车这个按钮添加点击事件
- 十二、添加购物车时候用户未登录
- 12.1 添加购物车用户未登录,业务处理方式:
- 12.2我们使用第三种难度最大的来
- 12.3使用Layui进行添加购物车成功/失败的提示
- 12.3.1声明layui的弹窗组件
- 12.3.2成功失败进行提示
- 十三购物车的列表
- 13.1数据库dao接口的实现
- 13.2pojo接口实现
- 13.3修改购物车
- 13.31通过这个进行购物车数量的修改:
- 13.32changNum函数进行实现:
- 十四购物车提交订单结算功能实现
- 14.1实现流程分析
- 十五、订单提交及支付流程
- 15.1流程分析
- 15.2订单添加接口实现
- 15.3数据库操作
- 15.4serviceimpl层实现 注意:这个方法需要加上@Transactional,也就是订单生成的时候,快照也必须生成
- 15.5 微信支付
- swagger报错解决
- 十六 商品个人中心订单信息的查询
- 16.1 流程分析
- sql语句
- OrderMapper.xml文件:
- 16.2 接口开发
- 16.2.1 Service层接口开发
- 16.2.2 Controller层实现
- 16.2.3前端使用Vue+ElementUI实现分页功能
- 1. 引入cdn
- 2. data中定义相关数据
- 3.分页的相关的方法
- 4. 分页的表格
- Linux的常用 的命令的复习
- Linux系统安装jdk
- Linux安装Tomcat
- 如果你的项目是部署在Tomcat上面的,你可以把你的项目打成war包,放在tomcat的weapp目录下面,运行tomcat即可进行该项目
- Linux安装mysql(在线安装)
- 20.锋迷项目的后端云部署
- 20.1企业项目当中需要进行修改的东西:
- **20.11在你的Linux中建立一个目录用来存放你的jar包:**
- 21.前端的项目的部署在云服务器上面
- Tomcat作为前端项目的弊端
- 1.前端会有大量的请求,Tomcat的弊端(难以满足高并发的,大约是2000-3000,使用Niginx可以提高高并发的承受,大约2万)
- 22.Nginx
- 23.前端项目部署在Nginx上面
- 24.Linux安装Nginx(在线安装)
- 24.1 安装编译工具(nginx安装之前需要编译)
- 24.2安装PCRE
- 24.3安装SSL库
- 24.4安装zlib库
- 24.5下载Nginx
自己添加的后端管理页面
视频演示效果
毕业设计SpringBoot+Vue+ElementUI商城系统实现(有后台)
论文地址
商城论文地址
后台代码视频实现讲解思路
毕业设计锋迷商城手敲后台管理,实现逻辑讲解,代码讲解
b站地址
1. 商品管理
2.商品分类管理
3.商品地址管理
4.权限管理系统
权限管理系统文章地址
5.订单管理
5.1
6.商品品牌管理
锋迷商城项目
使用Maven聚合项目进行创建(一个maven的父项目多个maven的子项目),
可以在父项目pom.xml文件中加上:
<package>pom<package>
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aKHt478Q-1633568521449)(C:\Users\CourageAndLove\AppData\Roaming\Typora\typora-user-images\image-20210812151308862.png)]
1.通过Maven聚合工程搭建项目:
1. 创建一个Maven的父项目,然后修改它的pom.xml文件,可以删除src等一些没有用的目录
<packaging>pom<packaing>
2.在父项目下面创建多个module,包括(common,beans,mapper,service,api)把它们全部打包成jar包
pom.xml加上
<packaging>jar</packaging>
3.由于mapper层需要调用beans层(pojo),需要在pom.xml文件中,然后可以在mapper中加入相关的依赖。
<dependencies><!-- mapper需要用到Beans所以需要加上beans的依赖--><dependency><groupId>org.example</groupId><artifactId>beans</artifactId><version>2.0.1</version></dependency></dependencies>
4.创建service需要调用mapper,和common,需要在pom.xml文件中加上
<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.api层需要接收前端的请求所以需要我们创建一个SpringBoot工程,你可以创建一个Maven工程,然后加入相关的依赖
6.api子工程,对外提供接口
总的说父项目的所以依赖可以被子项目引用,子项目也可以单独的添加所需的依赖
锋迷商城数据库设计
2.软件开发步骤
提出问题
可行性分析(技术(一般可以相关人员实现),成本,法律法规)
概要设计
- 系统设计(技术选型,架构模式)
- 数据库设计
- UI设计
- 业务流程设计
详细设计
- 实现步骤(业务流程的实现细节)
编码
- 根据设计好的实现步骤进行代码实现
- 开发过程使用单元测试
测试
- 集成测试
- 功能测试(墨盒)
- 性能测试(白盒)高并发,压力测试
交付/部署实施
3.数据库设计流程
根据功能分析出数据库实体(javaBean)
- 商品,订单,购物车,用户,地址…
提取实体属性
spu商品(id,商品名称,商品图片,商品描述…)
1 min10 … …
sku(skuId, 参数 , 价格 商品id
101 内存8G\存储128G 2999 1
102 内存12G\存储256G 3999 1
地址(姓名,地址,电话…)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-P8zP9MYA-1633446686624)(C:\Users\CourageAndLove\AppData\Roaming\Typora\typora-user-images\image-20210814172548189.png)]
可以知道价格的依赖于参数的改变而改变,参数依赖于id改变,不满足数据库设计表的要求,可以设计两张表进行实现。
- 使用数据库的第三范式进行检查数据项是否合理
- 分析实体关系图:E-R图 (一对一,一对多)
- 数据库建模(三线图)建模工具(PdMan)
- 建库建表-sql
3.数据建模工具PDMan
可视化创建数据库表(数据表)
视图显示表之间的关系(关系图)
导出sql指令(模型—导出DDL脚本)
记录数据库模型版本管理
可以连接数据库直接生成表
Spu和Sku的区别
spu(Standard Product Unit):商品信息聚合的最小 单位。通俗讲属性值,特性相同的商品可以称为一个SPU.
产品: 荣耀8 小米10
sku(Stock Keeping Unit)最小存货单元,定义为保存最小库存的控制最小可用单元
sku 荣耀8 8G/128G 10
sku 荣耀8 4G/124G 20
注意一下 :订单表的设计功能:因为订单表只要用户一下订单,订单表的相关信息就不可以进行改变 ,所以需要进行地址的的快照 ,和商品信息的快照,这样就算你临时改变了价格的信息或者其他的也没有关系
购物车的设计:
4.锋城业务流程设计
在企业开发中,当完成项目的需求分析,功能分析,数据库分析与设计后,项目组就会按照项目中的功能模块进行开发任务的分配。
每个人会被分配不同的功能
4.1用户管理9业务流程分析
单体架构:页面和控制之间可以进行跳转,同步请求控制器,流程控制由控制器来完成
前后端分离架构:前端和后端开发开发和部署,前端只能通过异步发送请求,后端只负责接收请求及参数,处理请求,返回结果
**前端可以发送如图所示的请求:**需要url,params
5接口介绍
狭义:的理解:就是控制器中可以接受用户请求的方法
标准定义:API(Application Programming interface)应用程序编程接口,就是软件系统不同组成部分衔接的约定。
5.1接口规范
作为后端程序员不仅要完成接口程序的开发,还要编写接口的说明文档—接口规范
5.2Swagger(自动生成服务器接口的规范性文档)
前后端分离规开发,后端需要编写接口说明文档,会耗费比较多的时间
swagger是一个用于生成服务器接口的的规范性文档,并且能够对接口进行测试的工具。
- swagger作用
- 生成接口规范性文档
- 生成接口测试工具
5.2.1引入相关的依赖:
<!-- swagger2接口文档生成工具--><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 创建相关的配置类
可以在api这个module中进行相关的controller层的测试,建立一个config包下面的SwaggerConfig类进行相关的测试,加上@Configuration,@EnableSwagger2注解,然后进行配置相关的信息
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生成我们的接口文档:* 1.需要配置生成文档的信息* 2.配置生成规则** */@Beanpublic Docket docket(){//创建封面信息对象ApiInfoBuilder apiInfoBuilder=new ApiInfoBuilder();//指定生成文档中的封面信息:文档标题,作者,版本apiInfoBuilder.title("《锋迷商城》后端接口说明").description("此文档详细说明了锋迷商城项目后端接口规范").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) //指定文档风格.apiInfo(apiInfo).select().apis(RequestHandlerSelectors.basePackage("com.qfedu.fmmall.controller"))
// 定义了path之后只会为user开头的请求进行扫描 .paths(PathSelectors.regex("/user/"))
// PathSelectors.any()表示任何的请求.paths(PathSelectors.any()).build();return docket;}}
5.2.3根据你的配置的端口号进行相关的测试
http://localhost:8080/swagger-ui.html
5.2.4 swagger提供了一套注解对每个接口进行详细的说明
@Api(value=" 用户管理",tags="提供用户的登录和注册的接口")//这个接口可以直接放在@Controller注解下面
@ApiOperation 和ApiImplicitParams({ @ApiImplicitParam(dataType=“”,name=“username”,value=“”,required=true), @ApiImplictParm}) 这两个注解放在@RequestMapping(“/login”)请求之上,用来修饰方法和方法中的参数。
@ApiOperation("用户登录的接口")@ApiImplicitParams({@ApiImplicitParam(dataType = "string",name = "username",value = "用户登录的账号",required = true),@ApiImplicitParam(dataType = "string",name = "password",value = "用户登录的密码",defaultValue = "111111",required = false)})@RequestMapping("/login")
// @RequestParam可以有默认的参数public ResultVO login(@RequestParam("username") String name,@RequestParam(value = "password",defaultValue = "111111") String pwd){return userService.checkLogin(name,pwd);}@RequestMapping(value = "regist",metho
@ApiModel 和@ApiModelProperty当接口参数返回一个对象类型时,需要在实体类中添加注解说明(也就是Beans这个Module进行相关的配置)
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 = "用户的买家信息",description = "买家的相关的参数")
public class User {@ApiModelProperty(name = "用户id",required = false,dataType = "int")private Integer userId;@ApiModelProperty(dataType = "string",name = "买家姓名",required = true)private String userName;@ApiModelProperty(dataType = "string",name = "买家密码",required = true)private String userPwd;@ApiModelProperty(dataType = "string",name = "买家真实姓名",required = true)private String userRealname;@ApiModelProperty(dataType = "string",name = "用户图片",required = true)private String userImg;}
@ApiIgnore 接口方法注解,添加此注解的方法将不会生成到接口文档中
5.2.5swagger-ui插件使用
1.api的module加入依赖
<!-- swagger-ui插件--><!-- 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.进行访问,然后可以使用它进行相关的测试
http://ip:port/doc.html
一、锋迷商城设计及实现用户管理
1.UserDao接口的创建:
package com.qfedu.fmmall.dao;import com.qfedu.fmmall.entity.User;
import org.springframework.stereotype.Repository;@Repository
public interface UserDao {// 用户注册public int insert(User user);// 根据用户名进行登录的验证public User queryByName(String name);}
2.UserMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//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是一个响应给前端的自定义的一个类。public ResultVO checkLogin(String username, String pwd);// 用户注册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
//使所有的线程都用这个对象,单例模式默认是开启的
@Scope("singleton")
public class UserServiceimpl implements UserService {@Autowiredprivate UserDao userDao;//可以在UserDao上面加上userDao,这个不会报红,但是没有什么意义@Overridepublic ResultVO checkLogin(String username, String pwd) {
// 查询用户名User user = userDao.queryByName(username);if(user==null){
// 用户名不正确return new ResultVO(10001,"用户名不正确",null);}else {//密码使用MD5进行加密String md5Pwd = MD5Utils.md5(pwd);if(md5Pwd.equals(user.getPassword())){
// 验证成功return new ResultVO(200,"登录成功",user);}else {//密码不正确return new ResultVO(10001,"密码错误",null);}}}@Transactional@Overridepublic ResultVO insert(String username, String pwd) {
// 判断这个用户是否被注册// 加上这个锁可以使用所有的注册都用这个userServiceimplsynchronized (this){
// 把密码进行MD5的加密String password = MD5Utils.md5(pwd);User user1 = userDao.queryByName(username);
//表示用户名没有被注册过,可以进行注册if (user1==null){
//一个是注册时间,regtime,一个是修改时间modtimeUser user=new User(username,password,new Date(),new Date());int i = userDao.insert(user);if(i>0){return new ResultVO(1000,"注册成功",null);}else {return new ResultVO(1001,"注册失败",null);}}
// 判断一下用户名是否已经被注册,然后把数据返回前端,goodjob,Noone can influence youelse {return new ResultVO(1001,"用户名已经被注册",null);}}}
}
5.api模块的UserController:
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对象",description = "响应封装的数据给前端")
public class ResultVO {
// 响应给前端的状态码@ApiModelProperty(dataType = "int",value = "响应的状态码")private int code;// 响应给前端的提示消息@ApiModelProperty(dataType = "string",value = "响应的消息")private String msg;
//响应给前端的数据@ApiModelProperty(dataType = "object",value = "响应数据的内容")private Object data;
}
6.ResultVO一个和前端进行数据交互的类
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对象",description = "响应封装的数据给前端")
public class ResultVO {
// 响应给前端的状态码@ApiModelProperty(dataType = "int",value = "响应的状态码")private int code;// 响应给前端的提示消息@ApiModelProperty(dataType = "string",value = "响应的消息")private String msg;
//响应给前端的数据@ApiModelProperty(dataType = "object",value = "响应数据的内容")private Object data;
}
7.在common模块的MD5Utils类:
package com.qfedu.fmmall.utils;import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;//MD5 生成器
public class MD5Utils {public static String md5(String password){//生成一个md5加密器try {MessageDigest md = MessageDigest.getInstance("MD5");//计算MD5 的值md.update(password.getBytes());//BigInteger 将8位的字符串 转成16位的字符串 得到的字符串形式是哈希码值//BigInteger(参数1,参数2) 参数1 是 1为正数 0为0 -1为负数return new BigInteger(1, md.digest()).toString(16);} catch (NoSuchAlgorithmException e) {e.printStackTrace();}return null;}
}
二、逆向工程
根据创建好的表,生成实体类,和DAO层、映射文件
在Dependencies下面加入依赖,这个依赖是一个Mybatis的maven插件
<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逆向工程配置
在resources的generator目录下面创建generatorConfig.xml
需要修改数据库的配置
需要修改pojo,mapper,Mapper.xml文件生成位置的配置
<!--%表示当前这个数据库里面的所有的表都会被生成--><table tableName="%"></table>
<!-- 指定生成 Mapper 的继承模板 --><plugin type="tk.mybatis.mapper.generator.MapperPlugin"><property name="mappers" value="com.hou.general.GeneralDao"/></plugin>
指定你的用Configuration generatorConfig.xml文件的路径
注意一下你的文件一定想要有空格什么东西的
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfigurationPUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN""http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd"><generatorConfiguration><!-- 引入 application.properties --><!-- <properties resource="application.properties" />--><!-- MyBatis3Simple:不生成 Example相关类及方法 defaultModelType="flat" --><context id="MysqlContext" targetRuntime="MyBatis3Simple" ><property name="beginningDelimiter" value="`"/><property name="endingDelimiter" value="`"/><!-- 指定生成 Mapper 的继承模板 --><plugin type="tk.mybatis.mapper.generator.MapperPlugin"><property name="mappers" value="com.qfedu.fmmall.general.GeneralDao"/></plugin><!--注意context内的文件要按序放--><commentGenerator><property name="suppressDate" value="true"/><!-- 是否去除自动生成的注释 true:是 : false:否 --><property name="suppressAllComments" value="true"/></commentGenerator><!-- jdbc 连接配置 --><jdbcConnection driverClass="com.mysql.jdbc.Driver"connectionURL="jdbc:mysql://localhost:3306/fmmall?characterEncoding=utf8"userId="root"password="roothouzhicong"></jdbcConnection><javaTypeResolver><!-- 是否使用bigDecimal, false可自动转化以下类型(Long, Integer, Short, etc.) --><property name="forceBigDecimals" value="false"/></javaTypeResolver><!-- 生成实体类的包名和位置 ,targetPackage指的是包名,targetProject值得是路径位置--><!-- 对于生成的pojo所在包,pojo其实就是domain Entity--><javaModelGenerator targetPackage="com.qfedu.fmmall.entity" targetProject="src/main/java"><property name="enableSubPackages" value="true"/><property name="trimStrings" value="true"/></javaModelGenerator><!-- 对于生成的mapper.xml所在目录 --><sqlMapGenerator targetPackage="/" targetProject="src/main/resources/mappers"/><!-- 配置mapper对应的java映射 也可以叫dao层 --><javaClientGenerator targetPackage="com.qfedu.fmmall.dao" targetProject="src/main/java"type="XMLMAPPER"/><!--%表示当前这个数据库里面的所有的表都会被继承--><table tableName="%"></table></context>
</generatorConfiguration>
7.2在pom.xml文件中指定generatorConfig.xml文件的路径
加上了这个:
<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>
三、跨域问题
解决方案:
后端解决办法:在UserController加上@CrossOrigin注解
前端通过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="邮箱/手机/用户名"></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="请输入密码"></div></form><input type="button" name="" id="submitBtn" value="登 录" 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>
前端使用JSONP设置,后端使用@CrossOrigin注解解决—设置响应头header允许跨域。
debugger;前端 可以加上代码debugger进行相关的调试。可以直接进行前端的校验
四、前端页面的传值
cookie和localstorage可以进行前端的页面之间的传值
Cookie浏览器端的缓存文件:大小受浏览器的限制。
LocalStorage:为了存储更大容量的数据
区别:cookie可以和后台进行传值,localStorage只可以在前端存储值,但是存储的时间长。
4.1Cookie使用(自定义封装一个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页面设置值:
function(res){console.log(res);if(res.code==1000){
// 获取前端传过来的数据 datavar userInfo=res.data;// cookie和localstorage可以进行前端的页面之间的传值setCookieValue("username",userInfo.username);setCookieValue("userImg",userInfo.userImg);window.location.href="index.html";}else{$("#tips").html("<label style='color:red'>"+ res.msg +"</label>");}
B页面取值:
var name=getCookieValue("username");var userImg=getCookieValue("userImg");console.log(name+userImg);
4.2localStorage
A页面:
localStorage.setItem("user",JSON.stringify(userInfo));
B页面:
var jsonStr=localStorage.getItem("user");// 把json串转换为对象
var userInfo=eval("("+jsonStr+")");// 把取到的值消失
localStorage.removeItem("user");
console.log(userInfo);
4.3Vue实现登录
data:{username:"",password:"", tips:" ",colorStyle:"",isRight:false,},methods:{doSubmit:function() {// 校验成功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="账号或者密码错误";}});}else{vm.tips="请输入正确的用户名和密码";vm.colorStyle="color:red"}// 1.进行数据的校验if(vm.username==" "){vm.tips="请输入用户名";vm.colorStyle="color:red";}},checkInfo:function(){if(vm.username==""){vm.tips="请输入用户名";this.colorStyle="color:red";vm.isRight=false;}else if(vm.username.length<6 ||vm.username.length>20){vm.tips="账号长度必须为6-20";vm.colorStyle="color:red";vm.isRight=false;}else{
// 校验密码if(vm.password==" "){vm.tips="请输入密码";this.colorStyle="color:red";vm.isRight=false;}else if(vm.password.length<6 ||vm.password.length>16){vm.tips="密码长度为6-16";this.colorStyle="color:red";}else{vm.tips=" ";vm.isRight=true;}}}}
from表单(用@keyup进行表单的输入的绑定):
<form><div class="user-name" style="margin-top: 20px;"><label for="user"><span class="glyphicon glyphicon-user" aria-hidden="true"></span></label><!-- @keyup进行绑定 --><input type="text" name="username" v-model="username" id="userName" @keyup="checkInfo" placeholder="邮箱/手机/用户名"></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="请输入密码"@keyup="checkInfo"></div></form>
五、前后端分离开发用户认证的问题
5.1单体项目中:
可以知道每台服务器中存在多个Session,只是id不相同,Cookie中可以存放sessionId,然后判断是是不是同一个session
在单体项目中用户怎么认证的?
在单体项目中视图资源和控制器都在同一台服务器,用户的多次请求老师基于同一个会话,可以基于session进行会话的验证:
- 用户登录将信息存放在session中
- 根据session中是否有用户信息来判断用户是否可以进行登录。
5.2前后端分离项目中
可以知道使用token实现用户验证,token存在于cookie中(同一台服务器可以访问cookie),然后验证token是否正确
基于token认证的用户代码实现
在commons模块中引入
package com.qfedu.fmmall.utils;import java.util.Base64;//base64 加密 解密 激活邮件的时候 为 邮箱地址 code验证码 进行加密
//当 回传回来后 进行邮箱地址 和 code 的解密
public class Base64Utils {//加密public static String encode(String msg){return Base64.getEncoder().encodeToString(msg.getBytes());}//解密public static String decode(String msg){return new String(Base64.getDecoder().decode(msg));}
}
登录成功生成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 = "提供用户的登录和注册的接口",tags = "用户管理")
public class UserController {@Autowiredprivate UserService userService;// @ApiIgnore加上这个注解会swagger忽略这个方法@ApiOperation("用户登录的接口")@ApiImplicitParams({@ApiImplicitParam(dataType = "string",name = "username",value = "用户登录的账号",required = true),@ApiImplicitParam(dataType = "string",name = "password",value = "用户登录的密码",required = true)})@RequestMapping("/login")
// @RequestParam可以有默认的参数public ResultVO login(@RequestParam("username") String name,@RequestParam(value = "password") String pwd){return userService.checkLogin(name,pwd);}@ApiOperation(value = "用户注册")@PostMapping("/regist")@ApiImplicitParams({@ApiImplicitParam(dataType = "string",name = "username",value = "用户注册的账号",required = true),@ApiImplicitParam(dataType = "string",name = "password",value = "用户注册的密码",required = true)})
// 前端用user传值,后端可以用users 接收public ResultVO register(@RequestBody Users users){return userService.insert(users.getUsername(),users.getPassword());}}
然后在UserServiceimpl中进行token的加密:
// 如果登录成功,则需要生成令牌token(token就是按照规则生成的字符串)
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
//使所有的线程都用这个对象,单例模式默认是开启的
@Scope("singleton")
public class UserServiceimpl implements UserService {@Autowiredprivate UsersMapper userDao;//可以在UserDao上面加上userDao,这个不会报红,但是没有什么意义@Overridepublic ResultVO checkLogin(String username, String pwd) {
// 查询用户名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){
// 用户名不正确return new ResultVO(10001,"用户名不正确",null);}else {//密码使用MD5进行加密String md5Pwd = MD5Utils.md5(pwd);System.out.println(users.get(0).getPassword());if(md5Pwd.equals(users.get(0).getPassword())){// 如果登录成功,则需要生成令牌token(token就是按照规则生成的字符串)String token= Base64Util.encode(username+"roothouzhicong");// 验证成功return new ResultVO(ResultStatus.OK,token,users.get(0));}else {//密码不正确return new ResultVO(ResultStatus.NO,"密码错误",null);}}}@Transactional@Overridepublic ResultVO insert(String username, String pwd) {
// 判断这个用户是否被注册// 加上这个锁可以使用所有的注册都用这个userServiceimplsynchronized (this){
// 把密码进行MD5的加密String password = MD5Utils.md5(pwd);// 查询用户名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){
//一个是注册时间,regtime,一个是修改时间modtimeUsers user=new Users(username,password,new Date(),new Date());int i = userDao.insert(user);if(i>0){return new ResultVO(ResultStatus.OK,"注册成功",null);}else {return new ResultVO(ResultStatus.NO,"注册失败",null);}}
// 判断一下用户名是否已经被注册,然后把数据返回前端,goodjob,Noone can influence youelse {return new ResultVO(ResultStatus.NO,"用户名已经被注册",null);}}}
}
前端设置token:
doSubmit:function() {// 校验成功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){// 如果登录成功就把token存储到时cookie中setCookieValue("token",vo.msg);window.location.href="index.html";}else{vm.tips="账号或者密码错误";}});
前端的购物车获取token:
<script type="text/javascript">// 进入购物车时要访问购物车列表的接口shopController接口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>
登录进行来可以把购物车获取token,前端的token用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 = "提供购物车业务相关的接口",tags = "购物车管理接口")
@RequestMapping("/shopcart")
public class ShopCartController {@RequestMapping("/list")@ApiImplicitParam(dataType = "string",name = "token",value = "登录的一个标志",required = true)public ResultVO shopcartList(String token){
// 校验输入的token看看是否是用户自己登录的token//解密String decode = Base64Utils.decode(token);if(token==null){return new ResultVO(ResultStatus.NO, "请先登录", null);}else if(decode.endsWith("roothouzhicong")) {System.out.println("购物车列表相关的接口------------");return new ResultVO(ResultStatus.OK, "success", null);}else {return new ResultVO(ResultStatus.NO, "登录已经过期,请重新登录!!", null);}}
}
六、JWT(json Web Token)一个别人封装好的工具类生成相关的token
- 用自定义的token生成的时效性不可以进行定义
- 安全性较差
JWT结构:
6.1生成JWT
添加依赖:
<!-- jwt生成 --><dependency><groupId>com.auth0</groupId><artifactId>java-jwt</artifactId><version>3.10.3</version></dependency> <!-- jjwt生成--><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.1</version></dependency>
UserServiceimpl(登录成功)生成token:
HashMap<String,Object> map=new HashMap<>();JwtBuilder builder= Jwts.builder();String token = builder.setSubject(username) //主题就是token中携带的数据.setIssuedAt(new Date()) //设置token的生成时间.setId(users.get(0).getUserId() + "") //设置用户的id为tokenid.setClaims(map) //map中可以存放用户的角色权限信息.setExpiration(new Date(System.currentTimeMillis() + 24 * 60 * 60 * 1000)) //设置token的过期时间.signWith(SignatureAlgorithm.HS256, "houzhicong") //设置加密的方式.compact();// 验证成功
前端ShopCart.html通过Cookie获取生成的token:
<script type="text/javascript">// 进入购物车时要访问购物车列表的接口shopController接口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>
后端ShopController进行解析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 = "提供购物车业务相关的接口",tags = "购物车管理接口")
@RequestMapping("/shopcart")
public class ShopCartController {@RequestMapping("/list")@ApiImplicitParam(dataType = "string",name = "token",value = "登录的一个标志",required = true)public ResultVO shopcartList(String token){
// 校验输入的token看看是否是用户自己登录的token
// String decode = Base64Utils.decode(token);if(token==null){return new ResultVO(ResultStatus.NO, "请先登录", null);}else {JwtParser parser= Jwts.parser();parser.setSigningKey("houzhicong");//解析token 必须和生成token时候生成的密码一致// 如果token正确(密码正确,有效期内) 则正常执行,否则抛出异常try{Jws<Claims> claimsJws=parser.parseClaimsJws(token);Claims body=claimsJws.getBody();//获取token中的用户数据String subject=body.getSubject();//获取生成token设置subjectString v1=body.get("key1",String.class);//获取生成token时存储的Claims的map中的值return new ResultVO(ResultStatus.OK, "success", null);}catch (Exception e){return new ResultVO(ResultStatus.NO, "登录已经过期,请重新登录!!", null);}}}
}
6.2使用拦截器进行拦截
- 创建一个CheckTokenInterceptor
- 创建一个拦截器的类 InterceptorConfig
6.3.1有
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 {@Autowiredprivate CheckTokenInterceptor checkTokenInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new CheckTokenInterceptor()).addPathPatterns("/**").excludePathPatterns("/user/**","/doc.html","/swagger-ui/**");}
}
6.3使用请求头进行传递token
前端但凡访问受限资源,都必须携带token请求,token可以通过请求行(params),请求头(header),以及请求体(data)传递,但习惯使用header传递
axios通过请求头传值 里面的参数用Headers 不用Params
axios({method:"get",url:baseUrl+"shopcart/list",Headers:{token:this.token,}}).then(function (res) {console.log(res);});},
6.3.1 CheckTokenInterceptor类 前端会发送预险性请求(只有它通过以后才可以进行第二次请求),需要拦截器进行放行
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 {
// 打ov 可以看到它的方法@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {String token = request.getParameter("token");// System.out.println("token----------");// 前端会发送预险性请求String method = request.getMethod();if("options".equalsIgnoreCase(method)){return true;}if(token==null){
// 提示用户进行登录PrintWriter out = response.getWriter();ResultVO resultVO= new ResultVO(ResultStatus.NO, "请先登录", null);
// 抽出一个方法进行doResponse(response,resultVO);// 拦截return false;}else {
// 验证tokentry{JwtParser parser= Jwts.parser();parser.setSigningKey("houzhicong");Jws<Claims> claimsJws=parser.parseClaimsJws(token);return true;}catch (ExpiredJwtException e){ResultVO resultVO= new ResultVO(ResultStatus.NO, "登录过期,请重新登录", null);doResponse(response,resultVO);return false;}catch (UnsupportedJwtException e){ResultVO resultVO= new ResultVO(ResultStatus.NO, "Token不合法,请自重", null);doResponse(response,resultVO);return false;}catch (Exception e){ResultVO resultVO= new ResultVO(ResultStatus.NO, "请先登录", 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();
// 写上Json格式的resultVOString s = new ObjectMapper().writeValueAsString(resultVO);out.println(s);out.flush();out.close();}
}
七首页的分类列表的的实现
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wQm4qlh3-1633446686637)(C:\Users\CourageAndLove\AppData\Roaming\Typora\typora-user-images\image-20210823182623779.png)]
得出结论:数据量较少的情况,使用一次性查询,数据量较多使用多次查询
方案一:一次性查询三级分类
- 优点只需一查询
- 缺点:数据库查询效率较低,页面首次加载的速度较慢
方案二:
- 先只查询一级分类,用户点击/鼠标移动到一级分类,动态加载二级分类
- 缺点:需要多次连接数据库
7.1接口实现
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WFClkTnv-1633446686638)(C:\Users\CourageAndLove\AppData\Roaming\Typora\typora-user-images\image-20210823204928346.png)]
一次性查询出来的sql语句:inner join 和left join 的区别 left join 左边没有关联的数据也会全部显示
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 c1inner join category c2 on c2.parent_id=c1.category_idleft join category c3 on c3.parent_id=c2.category_idwhere c1.category_level=1
--根据父级分类的parent_id进行查询 1 级 parent_id=0
select *from category where parent_id=0,
创建用于封装查询的类别信息CategoryVO
在Beans模块中entity包下面创建一个CategoryVO实体类用于封装Category和前端 进行数据的响应,相对于Category多了这个属性
//实体类中实现当前分类的子分类 private List<CategoryVO> categories;public List<CategoryVO> getCategories() {return categories;}public void setCategories(List<CategoryVO> categories) {this.categories = categories;}
在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> { //使用连接查询实现分类列表查询public List<CategoryVO> selectAllCategories();// 子查询public List<CategoryVO> selectAllCategories2(int parentId); }
映射文件配置
<?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">selectc1.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>
使用子查询实现分类列表的查询:```xml<!-- 使用子查询实现的分类列表查询--><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"/><!-- 这里的column="category_id"将等于
// 子查询public List<CategoryVO> selectAllCategories2(int parentId);里面的parentId;
--></resultMap><!-- 使用子查询实现的分类列表查询--><select id="selectAllCategories2" resultMap="CategoryMap2">selectcategory_id,
category_name,
category_level,
parent_id,
category_icon,
category_slogan,
category_pic,
category_bg_colorfrom category where parent_id=#{parentId};</select>
7.2业务层实现
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 {@Resourceprivate CategoryMapper categoryMapper;@Overridepublic ResultVO queryAllCategory() {List<CategoryVO> categoryVOS = categoryMapper.selectAllCategories2(0);return new ResultVO(ResultStatus.OK,"success",categoryVOS);}
}
控制层实现
indexController实现:
@Autowiredprivate CategoryService categoryService;@RequestMapping("category-list")@ApiOperation("商品分类查询接口")public ResultVO queryAllCategory(){return categoryService.queryAllCategory();}
八、商品的推荐功能实现
8.1 流程分析
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yol4wuqR-1633446686638)(C:\Users\CourageAndLove\AppData\Roaming\Typora\typora-user-images\image-20210828192719883.png)]
推荐最新上架的商品
8.2接口开发
8.2.1数据库的实现
sql语句实现
-- 商品推荐查询最新上架信息 select *from product order by create_time desc limit 0,3; -- 商品图片查询 根据商品id查询商品图片 select *from product_img where item_id=2;
在子工程beans工程下面创建ProductVO (加上了这个属性 private List imgs;因为一个商品包含多张表)
package com.qfedu.fmmall.entity;import javax.persistence.Column; import javax.persistence.Id; import java.util.Date; import java.util.List;public class ProductVO {/*** 商品id*/@Id@Column(name = "product_id")private Integer productId;/*** 商品名称*/@Column(name = "product_name")private String productName;/*** 商品分类id*/@Column(name = "category_id")private Integer categoryId;/*** 一级分类外键id*/@Column(name = "root_category_id")private Integer rootCategoryId;/*** 销量*/@Column(name = "sold_num")private Integer soldNum;/*** 商品状态*/@Column(name = "product_status")private Integer productStatus;/*** 商品内容*/private String content;/*** 创建时间*/@Column(name = "create_time")private Date createTime;/*** 更新时间*/@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;}@Overridepublic String toString() {return "ProductVO{" +"imgs=" + imgs +'}';}/*** 获取商品id** @return product_id - 商品id*/public Integer getProductId() {return productId;}/*** 设置商品id** @param productId 商品id*/public void setProductId(Integer productId) {this.productId = productId;}/*** 获取商品名称** @return product_name - 商品名称*/public String getProductName() {return productName;}/*** 设置商品名称** @param productName 商品名称*/public void setProductName(String productName) {this.productName = productName == null ? null : productName.trim();}/*** 获取商品分类id** @return category_id - 商品分类id*/public Integer getCategoryId() {return categoryId;}/*** 设置商品分类id** @param categoryId 商品分类id*/public void setCategoryId(Integer categoryId) {this.categoryId = categoryId;}/*** 获取一级分类外键id** @return root_category_id - 一级分类外键id*/public Integer getRootCategoryId() {return rootCategoryId;}/*** 设置一级分类外键id** @param rootCategoryId 一级分类外键id*/public void setRootCategoryId(Integer rootCategoryId) {this.rootCategoryId = rootCategoryId;}/*** 获取销量** @return sold_num - 销量*/public Integer getSoldNum() {return soldNum;}/*** 设置销量** @param soldNum 销量*/public void setSoldNum(Integer soldNum) {this.soldNum = soldNum;}/*** 获取商品状态** @return product_status - 商品状态*/public Integer getProductStatus() {return productStatus;}/*** 设置商品状态** @param productStatus 商品状态*/public void setProductStatus(Integer productStatus) {this.productStatus = productStatus;}/*** 获取商品内容** @return content - 商品内容*/public String getContent() {return content;}/*** 设置商品内容** @param content 商品内容*/public void setContent(String content) {this.content = content == null ? null : content.trim();}/*** 获取创建时间** @return create_time - 创建时间*/public Date getCreateTime() {return createTime;}/*** 设置创建时间** @param createTime 创建时间*/public void setCreateTime(Date createTime) {this.createTime = createTime;}/*** 获取更新时间** @return update_time - 更新时间*/public Date getUpdateTime() {return updateTime;}/*** 设置更新时间** @param updateTime 更新时间*/public void setUpdateTime(Date updateTime) {this.updateTime = updateTime;} }
ProductMapper文件:
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> { // 查询推荐商品信息public List<ProductVO> selectRecommendProducts(); }
ProductImgMapper文件:
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); }
ProductMapper.xml文件实现
注意一下子查询 的语句:
<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_timefrom product order by create_time desc limit 0,3;</select></mapper>
8.2.2业务层实现
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控制层实现
@ApiOperation("商品推荐查询信息接口")@RequestMapping(value = "/list-recommends",method = RequestMethod.GET)public ResultVO selectProductRecommend(){ResultVO resultVO = productService.selectRecommendProducts();return resultVO;}
九、商品详情显示(在Introduction.html进行相关的显示)
点击商品推荐,商品轮播图,商品的列表页面点击商品,就会进入到商品的详情页面。
- 获取商品id
- 可以查询商品的详情信息(商品的基本信息,商品套餐,商品图片信息。)
- 将商品参数返回给前端
9.1接口实现
9.1.1 商品详情接口
商品基本信息(product),商品套餐(sku),商品图片(product_img)
SQL
-- 根据商品id查询商品详情 select *from product where product_id=3; -- 根据商品id查询商品图片详情 select *from product_img where item_id=3; -- 根据商品id查询当前商品的套餐 select *from product_sku where product_id=3;
可以用子查询实现这个相关的操作
dao接口实现(通过product,product_img,product_sku三张表获取商品的详情信息)
@Repository public interface ProductMapper extends GeneralDao<Product> { // 查询推荐商品信息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> { }
业务层实现
//这里为不需要事务,但是如果某些事务如果调用了我也加入到事务中来
// 事务默认的隔离级别是可重复读 repeateable read@Transactional(propagation = Propagation.SUPPORTS)public ResultVO selectProductBasicInfo(String productId) {
// 1.查询商品的基本信息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.查询商品的图片信息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.查询商品的套餐信息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);// 把所有的商品的详情信息放入HashMap当中进行使用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,"查询商品基本信息失败",null);}return null;}
- 控制层实现(这边把商品的详情的信息放到了ResultVO的Object中)
// 商品详情查询@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;}
十、显示商品评价的信息(通过用户和商品评论表进行相关的连表查询)
-- 根据评论的id查询评论信息,关联用户表查询用户信息
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 新建的VO,ProductCommentsVO (一对一的连表查询可以不用在实体类中声明另一个实体类)
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;//用于封装评论对应的用户数据private Integer userId;private String username;private String nickname;private String userImg;}
在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);
}
十一、添加购物车的功能实现
10.1流程分析:
点击添加购物车---------商品、选择套餐id,套餐属性,数量,token-------------进行token的校验
10.2数据库的相关的操作
增加字段sku_props
表生成之后 用逆向工程重新生成shopping_cart表的相关的结构。修改generalConfig.xml,把%修改成shopping_cart
注意一下**%表示生成所有的表**
<table tableName="shopping_cart"></table>
10.2.1 购买的数量的前端实现在vue的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;}},
进行商品数量的绑定可以用 v-model="num"进行双向绑定
<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">库存<span class="stock">{{productSkus[currentSkuIndex].stock}}</span>件</span></dd>
10.2.2给加入购物车这个按钮添加点击事件
<li><div class="clearfix tb-btn tb-btn-basket theme-login"><a id="LikBasket" title="加入购物车" href="" @click="addShopCart()"><i></i>加入购物车</a></div></li>
把相关的添加的购物车的信息放入cart这个类中:
addShopCart(){var uid=getCookieValue("userId");var propStr="";// 套餐属性转换成字符串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};//从cookie中获取tokenvar token=getCookieValue("token");console.log("---token-------");console.log(token);// 把购物车的信息放入数据库中var url5=baesUrl+"shopcart/add";axios.post({url:url5,methods:"post",headers:{token:token},data:cart
}).then( res=>{console.log("------res-----"+res);});}
十二、添加购物车时候用户未登录
12.1 添加购物车用户未登录,业务处理方式:
- 查询商品详情页面的时候,就提示先登录,跳转到登录页面
- 当点击添加购物车,弹窗显示先登录,完成登录,点击添加购物车
- 点击添加购物车,会跳转到登录页面,登录完成之后会跳转到商品详情页面。
12.2我们使用第三种难度最大的来
12.3使用Layui进行添加购物车成功/失败的提示
引入lay-ui cdn
<!-- 引入 layui.css --> <link rel="stylesheet" href="//unpkg.com/layui@2.6.8/dist/css/layui.css"><!-- 引入 layui.js --> <script src="//unpkg.com/layui@2.6.8/dist/layui.js">
12.3.1声明layui的弹窗组件
12.3.2成功失败进行提示
十三购物车的列表
流程图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NmuB7noP-1633446686643)(C:\Users\CourageAndLove\AppData\Roaming\Typora\typora-user-images\image-20210922164854113.png)]
步骤
- 获取用户user_id
- 通过user_id获取购物车的信息(包括商品的名字,商品图片的信息)
- 将购物车信息数据返回给前端 。
- 也就是有三张表,购物车表(shopping_cart),商品表(product),商品图片表(product_img 根据商品id查询商品主图)
13.1数据库dao接口的实现
sql语句
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接口```javaList<ShoppingCartVO> selectShoppingCartByUserId(int userid);
13.2pojo接口实现
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HvWCzaRC-1633446686643)(C:\Users\CourageAndLove\AppData\Roaming\Typora\typora-user-images\image-20210922172106911.png)]
注意一下数据库的shopping_cart表不需要加上这两个字段,是通过product_img表和product表的column属性来进行关联的
13.3修改购物车
13.31通过这个进行购物车数量的修改:
13.32changNum函数进行实现:
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;}}// 修改的cartId和cartNumvar 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){
// // 这个可以打印绑定的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;
// }// }}
十四购物车提交订单结算功能实现
14.1实现流程分析
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-r7eZLbEB-1633446686645)(C:\Users\CourageAndLove\AppData\Roaming\Typora\typora-user-images\image-20210924214722021.png)]
十五、订单提交及支付流程
15.1流程分析
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-v5zo96Td-1633446686646)(C:\Users\CourageAndLove\AppData\Roaming\Typora\typora-user-images\image-20210926112259576.png)]
15.2订单添加接口实现
15.3数据库操作
- 根据收货id获取收货的地址信息(tkmapper)
- 根据购物车ID,查询购物车详情(需要关联查询商品名称,sku名称,库存,商品图片,商品价格)----》 获取生成商品快照的数据 只需在ShoppingCartVO中多加上一个stock字段就好,然后在ShoppCartMapper.xml加上需要查询的这个字段
- 保存订单信息(tkMapper)
- 修改库存(tkMapper)
- 保存商品快照(tkMapper)
15.4serviceimpl层实现 注意:这个方法需要加上@Transactional,也就是订单生成的时候,快照也必须生成
- 生成OrderId的方法 UUID.random().toString()
- 通过时间戳生成System.currentTimeMillis()+(new Random().nextInt(9999)+100)+“”
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.*;@Servicepublic class OrderServiceimpl implements OrderService{@Autowiredprivate ShoppingCartMapper shoppingCartMapper;@Autowiredprivate OrdersMapper ordersMapper;@Autowiredprivate OrdersItemMapper ordersItemMapper;@Autowiredprivate 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 把这些用Orders对象来接收// 保存订单的步骤
// 1.查询选中购买的购物车详情
// 2. 校验库存
// 3.保存订单
// 4.保存订单快照
// 5.购买成功需要删除购物车记录
// 可以知道这四个步骤需要同时成功或者同时失败,符合一个事务的操作(ACID)@Transactionalpublic Map<String,String> addOrder(List<Integer> cids, Orders orders) throws SQLException{Map<String,String> map=new HashMap<>();
// 根据cids查询购物车的详情记录(包括库存)List<ShoppingCartVO> shoppingCartVOList = shoppingCartMapper.selectShoppingcartByids(cids);// 校验库存boolean f=true;String untitled="";for (ShoppingCartVO sc :shoppingCartVOList) {if(Integer.valueOf(sc.getCartNum())>sc.getStock()){f=false;}// 获取所有的商品名称,以,分割拼接成字符串untitled=untitled+sc.getProductName()+",";}if(f){
// 表示库存充足进行保存
// 1.userId 2 untitled名称 3 收件人地址,姓名,电话,地址
// 4. 总价格 5.支付方式
// 6.创建 订单的时间
// 7.订单初始状态 1 待支付orders.setStatus(1);orders.setUntitled(untitled);orders.setCreateTime(new Date());orders.setCancelTime(new Date());orders.setDeliveryTime(new Date());// 生成订单编号String orderId = UUID.randomUUID().toString().replace("-", "");orders.setOrderId(orderId);// 保存订单int i=ordersMapper.insert(orders);if(i>0){// ordersItem 生成商品快照
// List<OrdersItem> ordersItemList=new ArrayList<>();for (ShoppingCartVO sc :shoppingCartVOList) {
// 生成订单的编号int cnum=Integer.valueOf(sc.getCartNum());String itemid=System.currentTimeMillis()+(new Random().nextInt(9999)+100)+"";String itemid1 = itemid.substring(1, 10);// 注意一下double需要转换为Bigdecimal类型// 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);
// 扣减库存???
// 根据套餐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);}// 保存订单成功 删除购物车记录for (Integer cid:cids) {shoppingCartMapper.deleteByPrimaryKey(cid);}map.put("orderId",orderId);map.put("productNames",untitled);return map;}}else{
// 不足return null;}return null;}
}
15.5 微信支付
微信支付
swagger报错解决
For input String :“”
在application.yml加上日志的配置:
logging:level:io.swagger.models.parameters.AbstractSerializableParameter: error
十六 商品个人中心订单信息的查询
16.1 流程分析
- 根据用户id进行订单信息的查询
- 可以关联进行查询订单快照
sql语句
<select id="selectOrders" resultMap="OrdertMap1">select o.order_id,o.user_id,o.untitled,o.receiver_name,o.receiver_mobile,o.receiver_address,o.total_amount,o.actual_amount,o.pay_type,o.order_remark,o.status,o.delivery_type,o.delivery_flow_id,o.order_freight,o.delete_status,o.create_time,o.update_time,o.pay_time,o.delivery_time,o.finish_time,o.cancel_time,o.close_typefrom orders owhere o.user_id=#{userId}<if test="status!=null">and o.status=#{status}</if>limit #{start},#{limit}</select>
OrderMapper.xml文件:
<resultMap id="OrdertMap1" type="com.qfedu.fmmall.entity.OrdersVO"><!--WARNING - @mbg.generated--><result column="order_id" jdbcType="VARCHAR" property="orderId" /><result column="user_id" jdbcType="VARCHAR" property="userId" /><result column="untitled" jdbcType="VARCHAR" property="untitled" /><result column="receiver_name" jdbcType="VARCHAR" property="receiverName" /><result column="receiver_mobile" jdbcType="VARCHAR" property="receiverMobile" /><result column="receiver_address" jdbcType="VARCHAR" property="receiverAddress" /><result column="total_amount" jdbcType="DECIMAL" property="totalAmount" /><result column="actual_amount" jdbcType="DECIMAL" property="actualAmount" /><result column="pay_type" jdbcType="VARCHAR" property="payType" /><result column="order_remark" jdbcType="VARCHAR" property="orderRemark" /><result column="status" jdbcType="INTEGER" property="status" /><result column="delivery_type" jdbcType="VARCHAR" property="deliveryType" /><result column="delivery_flow_id" jdbcType="VARCHAR" property="deliveryFlowId" /><result column="order_freight" jdbcType="DECIMAL" property="orderFreight" /><result column="delete_status" jdbcType="INTEGER" property="deleteStatus" /><result column="create_time" jdbcType="TIMESTAMP" property="createTime" /><result column="update_time" jdbcType="TIMESTAMP" property="updateTime" /><result column="pay_time" jdbcType="TIMESTAMP" property="payTime" /><result column="delivery_time" jdbcType="TIMESTAMP" property="deliveryTime" /><result column="finish_time" jdbcType="TIMESTAMP" property="finishTime" /><result column="cancel_time" jdbcType="TIMESTAMP" property="cancelTime" /><result column="close_type" jdbcType="INTEGER" property="closeType" /><!-- 子查询实现 根据OrdersVO的orderId进行子查询--><collection column="ordersItems" property="order_id" select="com.qfedu.fmmall.dao.OrdersItemMapper.selectOrderItemsByOrderId"/></resultMap><select id="selectOrders" resultMap="OrdertMap1">select o.order_id,o.user_id,o.untitled,o.receiver_name,o.receiver_mobile,o.receiver_address,o.total_amount,o.actual_amount,o.pay_type,o.order_remark,o.status,o.delivery_type,o.delivery_flow_id,o.order_freight,o.delete_status,o.create_time,o.update_time,o.pay_time,o.delivery_time,o.finish_time,o.cancel_time,o.close_typefrom orders owhere o.user_id=#{userId}<if test="status!=null">and o.status=#{status}</if>limit #{start},#{limit}</select>
16.2 接口开发
package com.qfedu.fmmall.dao;import com.qfedu.fmmall.entity.Orders;
import com.qfedu.fmmall.entity.OrdersVO;
import com.qfedu.fmmall.general.GeneralDao;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;import java.util.List;@Repository
public interface OrdersMapper extends GeneralDao<Orders> {List<OrdersVO> selectOrders(@Param("userId") String userId,@Param("status") String status,@Param("start")int start,@Param("limit")int limit);
}
16.2.1 Service层接口开发
@Overridepublic ResultVO selectOrders(String userId, String status, int pageNum, int limit) {int start=(pageNum-1)*limit;List<OrdersVO> ordersVOS = ordersMapper.selectOrders(userId, status, pageNum, limit);
// 2.查询总记录数Example example = new Example(Orders.class);Example.Criteria criteria = example.createCriteria();criteria.andLike("userId",userId);if(status!=null&&!"".equals(status)){criteria.andLike("status",status);}
// 2.查询总记录数int count=ordersMapper.selectCountByExample(example);
// 查询总页数int pageCount=count%limit==0?count/limit:count/limit+1;// 3.封装数据PageHelper<OrdersVO> ordersVOPageHelper = new PageHelper<>(count, pageCount, ordersVOS);return new ResultVO(ResultStatus.OK,"订单查询成功",ordersVOPageHelper);}
16.2.2 Controller层实现
package com.qfedu.fmmall.controller;import com.github.wxpay.sdk.WXPay;
import com.qfedu.fmmall.config.MyPayConfig;
import com.qfedu.fmmall.entity.Orders;
import com.qfedu.fmmall.service.OrderService;
import com.qfedu.fmmall.vo.ResultStatus;
import com.qfedu.fmmall.vo.ResultVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;@RestController
@CrossOrigin
@RequestMapping("/order")
@Api(value = "提供订单相关的接口",tags = "订单管理")
public class OrderController {@Autowiredprivate OrderService orderService;@PostMapping("/add/{cids}")public ResultVO add(@PathVariable("cids") List<Integer> cids,@RequestBody Orders orders){ResultVO resultVO=null;// 测试用的OrderIdtry {Map<String, String> orderInfo = orderService.addOrder(cids, orders);String orderId=orderInfo.get("orderId");// 订单快照创建成功,申请支付链接HashMap<String,String> data=new HashMap<>();
// 设置当前订单信息data.put("body",orderInfo.get("productNames")); //商品描述data.put("out_trade_no",orderId);//使用当前用户订单编号作为当前支付交易的交易编号data.put("fee_type","CNY"); //支付币种data.put("total_fee", orders.getActualAmount()+""); //支付金额data.put("trade_type","NATIVE");//交易类型// 必填选项 用于设置支付完成时的回调方法接口data.put("notify_url","/pay/success");WXPay wxPay=new WXPay(new MyPayConfig());Map<String, String> resp = wxPay.unifiedOrder(data);// 把微信支付平台生成的链接获取到orderInfo.put("payUrl",resp.get("code_url"));resultVO=new ResultVO(ResultStatus.OK,"提交订单成功!",orderInfo);System.out.println(resp);// code_url -> weixin://wxpay/bizpayurl?pr=Iv5Fsq6zz} catch (SQLException e) {resultVO= new ResultVO(ResultStatus.NO,"下单失败",null);} catch (Exception e) {e.printStackTrace();}return resultVO;}// 订单分页查询
@RequestMapping(value = "/list",method = RequestMethod.GET)@ApiImplicitParams({@ApiImplicitParam(dataType = "string",name = "userId",value = "用户Id",required = true),@ApiImplicitParam(dataType = "string",name = "status",value = "订单状态",required = false),@ApiImplicitParam(dataType = "int",name = "pageNum",value = "当前页数",required = true),@ApiImplicitParam(dataType = "int",name = "limit",value = "页数大小",required = false),
})
public ResultVO selectOrders(@RequestHeader("token")String token, String userId, String status, int pageNum, int limit) {ResultVO resultVO = orderService.selectOrders(userId, status, pageNum, limit);return resultVO;}}
16.2.3前端使用Vue+ElementUI实现分页功能
1. 引入cdn
<!-- 引入样式 -->
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css"><script src="static/js/cookie_utils.js"></script><!-- element-ui需要引入vue.js --><script src="static/js/vue.min.js"></script><!-- 引入组件库 -->
<script src="https://unpkg.com/element-ui/lib/index.js"></script><script src="static/js/axios.min.js"></script><script src="static/js/utils.js"></script>
2. data中定义相关数据
<script type="text/javascript">var baseUrl="http://localhost:8080/";var vm=new Vue({el:"#app",data:{token:"",username:"",pageNum:1,limit:6,userId:"",orders:[],count:0,status:null,},created(){this.token=getCookieValue("token");this.userId=getCookieValue("userId");this.username=getCookieValue("username");// 加载页面,请求订单信息var url1=baseUrl+"order/list";axios({url:url1,method:"get",headers:{token:this.token},params:{userId:this.userId,pageNum:this.pageNum,limit:this.limit}}).then((res)=>{console.log(res.data);if(res.data.code==1){this.orders=res.data.data.data;console.log(this.orders);this.count=res.data.data.count;}});},methods: {// 通过订单状态进行查询queryByStatus(status){this.status=status;console.log(this.status);// 重新按照状态查询一次// 加载页面,请求订单信息var url1=baseUrl+"order/list";axios({url:url1,method:"get",headers:{token:this.token},params:{userId:this.userId,pageNum:this.pageNum,limit:this.limit,status:this.status}}).then((res)=>{console.log(res.data);if(res.data.code==1){this.orders=res.data.data.data;console.log(this.orders);this.count=res.data.data.count;}});},gotoComment:function(event){var index=event.srcElement.dataset.index;console.log(index);var order=this.orders[index];localStorage.setItem("order",JSON.stringify(order));location.href="user-comment.html";}},})</script>
3.分页的相关的方法
// 通过分页进行查询pager(page){this.pageNum=page;//重新加载页面// 加载页面,请求订单信息// -------------分页查询按照状态进行查询var obj={userId:this.userId,pageNum:this.pageNum,limit:this.limit};if(this.status!=null){obj.status=this.status;}// ------------var url1=baseUrl+"order/list";axios({url:url1,method:"get",headers:{token:this.token},params:obj}).then((res)=>{console.log(res.data);if(res.data.code==1){this.orders=res.data.data.data;console.log(this.orders);this.count=res.data.data.count;}});},
4. 分页的表格
<table class="table"><tr><td>序号</td><td>订单商品</td><td>订单状态</td><td>时间</td><td>操作</td></tr><template v-for="order,index in this.orders"><tr><td>{{index+1}}</td><td>{{order.untitled}}</td><td><span v-if="order.status=='1'">待付款</span><span v-else-if="order.status=='2'">待发货</span><span v-else-if="order.status=='3'">待收货</span><span v-else-if="order.status=='4'">待评价</span><span v-else-if="order.status=='5'">已完成</span><span v-else-if="order.status=='6'">已关闭</span></td><td>{{order.createTime}}</td><td><template v-if="order.status=='1'"><button class="btn btn-success btn-xs">去支付</button ><button class="btn btn-danger btn-xs">取消订单</button></template><template v-if="order.status=='2'"><button class="btn btn-danger btn-xs">取消订单</button></template><template v-if="order.status=='3'"><button class="btn btn-success btn-xs">确认收货</button > </template><template v-if="order.status=='4'"><button class="btn btn-success btn-xs" @click="gotoComment" :data-index="index">去评价</button ></template><template v-if="order.status=='6'" || v-if="order.status=='5'" ><button class="btn btn-danger btn-xs">删除</button ></template> </td></tr></template><tr><td colspan="5"><!-- 分页 --><el-paginationbackgroundlayout="prev, pager, next":current-page="pageNum":page-size="limit":total="count" @current-change="pager">
</el-pagination></td></tr></table>
Linux的常用 的命令的复习
linux中没有盘符,根路径用 "/"表示
rm -rf 你的目录的名字
linux的系统结构和相关的目录结构
bin,sbin(超级管理员的命令目录),etc(系统配置文件),lib/lib4(系统所需的依赖库),boot(系统启动的相关的文件),
目录 | 说明 |
---|---|
bin | 系统文件 |
sbin | 超级管理员系统命令 |
boot | 系统启动相关的目录 |
etc | 系统配置文件 |
lib/lib4 | 存放系统所需的依赖库 |
home | 一般用户所在的文件夹 |
root | 超级管理员目录(root用户目录) |
media | 媒体(光驱) |
mnt | 挂载(u盘,移动硬盘) |
tmp/opt | 临时的文件存储目录,比如日志在tmp或者opt目录下面 |
usr | 用户目录,我们通常安装的软件,用户的一些文件都在此目录下面 |
run srv sys var proc dev | 系统相关目录 |
ls -a #可以显示隐藏文件
Linux系统安装jdk
通过SecureFx上传你的linux版本的jdk
进行解压tar -zxcf 你的压缩包的名字
在/etc/profile目录进行环境变量的配置。
加上如下的配置:
#JAVA_HOME export JAVA_HOME=/opt/module/jdk1.8.0_144 export PATH=$PATH:$JAVA_HOME/bin
Linux安装Tomcat
通过Secure在windows中上传你的Tomcat包,
进行解压到指定的目录
在它的bin目录下面进行启动
./startup.sh
不使用了可以关闭tomcat
lsof -i:8080 kill -9 PID
如果你的项目是部署在Tomcat上面的,你可以把你的项目打成war包,放在tomcat的weapp目录下面,运行tomcat即可进行该项目
Linux安装mysql(在线安装)
通过如下的指定进行安装:
wget http://dev.mysql.com/get/mysql57-community-release-el7-10.noarch.rpm
然后使用下面指定(如果没有权限需要su root):
rpm -ivh mysql57-community-release-el7-10.noarch.rpm
正式安装mysql服务:
yum -y install mysql-community-server
报错:
Error: Package: mysql-community-server-5.7.36-1.el7.x86_64 (mysql57-community)
Requires: systemd
使用命令看你是否安装了MySQL
rpm -qa|grep -i mysql
把已经安装好的mysql加入开机启动:
systemctl enable mysqld
进行启动mysql服务:
systemctl start mysqld
20.锋迷项目的后端云部署
20.1企业项目当中需要进行修改的东西:
- application.yml文件中需要改变连接的url;改成你的数据库的服务器的ip地址,比如localhost变为你的ip地址
- 如果你有微信支付的回调接口
data.put("notify_url","/pay/success");
,变成你的云主机的地址。
在Maven中先进行clean,然后进行package进行打包的操作,在api中找到你的打包的jar包进行。
20.11在你的Linux中建立一个目录用来存放你的jar包:
运行这个命令进行运行你的项目:
java -jar api-2.0.1.jar
api-2.0.1.jar为你的jar包的名字。
如果报错: no main manifest attribute, in api-2.0.1.jar
可以在你的pom.xml文件中加上这个配置(在dependencies下面):
<build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><version>2.0.1.RELEASE</version><configuration><mainClass>com.qfedu.fmmall.ApiApplication</mainClass></configuration><executions><execution><goals><goal>repackage</goal></goals></execution></executions></plugin></plugins></build>
然后再次运行上面的命令:java -jar 你的jar包名字。
启动成功如图所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gmOreCEF-1640608196271)(C:\Users\CourageAndLove\AppData\Roaming\Typora\typora-user-images\image-20211226182330211.png)]
这样当你ctrl+c你的服务就会停止。
使用这个指令可以使你的服务一直开着:
java -jar api-2.0.1.jar &
注意一下:你的依赖的引入不可以重复
21.前端的项目的部署在云服务器上面
我们的云主机有安装Tomcat,可以部署在Tomcat上面:
由于上面的Tomcat8080已经启动了,我们可以修改一下它的conf目录下面的server.xml文件:
cd /opt/module/apache-tomcat-8.5.73/
cd conf
#可以查出8080在server.xml的哪行
cat -n server.xml |grep 8080
#可以在server.xml中编辑69行
vim +69 server.xml
1. 修改所有的请求的localhost地址为你的云服务器的地址
2.上传Tomcat到你的Linux的服务器中,进行解压
3.把你的前端的项目从windows中上传到Linux的Tomcat的webapp目录下面。
4.到bin目录下面进行启动命令为:
./startup.sh
5.通过路径进行访问你的前端的项目。
#可以查看你的Linux的服务器的ip
ifconfig
#路径在加上你的Tomcat的端口号9999
http://192.168.48.5:9999/fmall-static/index.html
访问成功的截图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xuUhtOgj-1640608196273)(C:\Users\CourageAndLove\AppData\Roaming\Typora\typora-user-images\image-20211227092944455.png)]
Tomcat作为前端项目的弊端
1.前端会有大量的请求,Tomcat的弊端(难以满足高并发的,大约是2000-3000,使用Niginx可以提高高并发的承受,大约2万)
- Tomcat的核心价值在于能够便于执行java程序,而不是处理并发
- 结论:tomcat不适合部署前端项目
22.Nginx
它是一个高性能的Http和反向代理web服务器
- Nginx是基于http协议的请求和响应(部署web项目) —静态资源
- 可以作为反向代理服务器 ----负载均衡(代理服务器)
高性能体现:
- 稳定性好,可以7*24不间断的运行
- 配置简洁
- 可以承受高并发(5w+)
23.前端项目部署在Nginx上面
安装nginx
将前端项目fmall-static拷贝到nginx的根目录
修改nginx/conf里面的nginx.conf文件:
location /{ root fmall-static; index index.html index.htm; }
24.Linux安装Nginx(在线安装)
24.1 安装编译工具(nginx安装之前需要编译)
yum install -y gcc gcc-c++
24.2安装PCRE
# 1.下载 wget http://downloads.sourceforge.net/project/pcre/pcre/8.35/pcre-8.35.tar.gz# 2.解压 tar -zxvf pcre-8.35.tar.gz# 3.进入目录 cd pre-8.35# 4.配置 ./configure# 5.编译安装 make && make install
24.3安装SSL库
cd /opt/software wget http://www.openssl.org/source/openssl-1.0.1j.tar.gz tar -zxvf openssl-1.0.1j.tar.gz# 4.配置 ./configure# 5.编译安装 make && make install
24.4安装zlib库
wget http://zlib.net/zlib-1.2.11.tar.gz tar -zxvf zlib-1.2.11.tar.gz -C ../module/ cd zlib-1.2.11/ ./configure make && make install
24.5下载Nginx
可以本地上传或者在线下载
wget https://nginx.org/download/nginx-1.16.1.tar.gz cd nginx-1.16.1/./configure --prefix=nginx-1.16.1/ --with-http_stub_status_module --with-http_ssl_module --with-pcre=../pcre-8.35/make && make install
成功:
Configuration summary
- using PCRE library: …/…/pcre-8.35/
- using system OpenSSL library
- using system zlib library
nginx path prefix: “…/…/nginx-1.16.1/”
nginx binary file: “…/…/nginx-1.16.1//sbin/nginx”
nginx modules path: “…/…/nginx-1.16.1//modules”
nginx configuration prefix: “…/…/nginx-1.16.1//conf”
nginx configuration file: “…/…/nginx-1.16.1//conf/nginx.conf”
nginx pid file: “…/…/nginx-1.16.1//logs/nginx.pid”
nginx error log file: “…/…/nginx-1.16.1//logs/error.log”
nginx http access log file: “…/…/nginx-1.16.1//logs/access.log”
nginx http client request body temporary files: “client_body_temp”
然后make && make install以后没有报错出现下面的信息:
test -d ‘nginx-1.16.1//logs’
|| mkdir -p ‘nginx-1.16.1//logs’
make[1]: Leaving directory `/opt/module/nginx-1.16.1’
查看成功的nginx:ll
[root@hadoop102 nginx-1.16.1]# ll
total 16
drwxr-xr-x. 2 root root 4096 Dec 25 07:10 conf
drwxr-xr-x. 2 root root 4096 Dec 25 07:10 html
drwxr-xr-x. 2 root root 4096 Dec 25 07:10 logs
drwxr-xr-x. 2 root root 4096 Dec 25 07:10 sbin
启动nginx:
cd /opt/software/nginx-1.16.1/nginx-1.16.1/sbin
./nginx
然后报错:
./nginx: error while loading shared libraries: libssl.so.1.1: cannot open shared object file: No such file or directory
解决办法:
find / -name *libcrypto*
找到这个路径:/usr/local/lib64/libcrypto.so.1.1
cd /usr/local/lib64/
查看防火墙的状态和关闭防火墙:
#查看防火墙状态
/etc/init.d/iptables status
#关闭防火墙service iptables stop#重新启动防火墙service iptables restart
报错:
error while loading shared libraries: libssl.so.1.1: cannot open shared object file: No such file or directory
解决:
ln -s /usr/local/lib64/libssl.so.1.1 /usr/lib64/libssl.so.1.1
ln -s /usr/local/lib64/libcrypto.so.1.1 /usr/lib64/libcrypto.so.1.1
毕业设计学习锋迷商城的的笔记(自己设计并手写后台商品管理,分类管理,用户,地址管理系统,订单管理,微信支付(内网穿透))相关推荐
- 毕业设计学习锋迷商城笔记
文章目录 自己添加的后端管理页面 视频演示效果 1. 商品管理 2.商品分类管理 3.商品地址管理 4.用户中心管理 4. 用户权限管理 5.订单管理 6.商品品牌管理 锋迷商城项目 1.通过Mave ...
- Java电商 《锋迷商城》分布式笔记
目录 ⼀.锋迷商城在互联⽹环境下存在的问题 1.1 ⾼并发带来的服务器访问压⼒问题 1.2 ⾼并发带来的业务处理问题 1.3 系统迭代带来的架构问题 1.4 业务实现问题 1.5 如何解决以上问题? ...
- 基于TensorFlow深度学习框架,运用python搭建LeNet-5卷积神经网络模型和mnist手写数字识别数据集,设计一个手写数字识别软件。
本软件是基于TensorFlow深度学习框架,运用LeNet-5卷积神经网络模型和mnist手写数字识别数据集所设计的手写数字识别软件. 具体实现如下: 1.读入数据:运用TensorFlow深度学习 ...
- 锋迷商城项目介绍(一)
锋迷商城项目介绍 项目模块 一.用户管理 二.首页功能实现 三.商品详情 四.购物车 五.收货地址管理 六.提交订单 七.用户中心 八.订单管理 九.评价管理 十.中心首页数据显示 项目介绍 一.项目 ...
- 《锋迷商城》——首页:轮播图
<锋迷商城>系列项目 链接: <一> 项目搭建 链接: <二>数据库的创建 链接: <三>业务流程设计 链接: <四>业务流程实现:用户管理 ...
- 锋迷商城项目数据库设计(四)
锋迷商城项目数据库设计 一.用户表 users 二.用户地址表 user_addr 三.登录历史表 user_login_history 四.轮播图 index_img 五.商品分类 category ...
- 《锋迷商城》——用户认证
<锋迷商城>系列项目 链接: <一> 项目搭建 链接: <二>数据库的创建 链接: <三>业务流程设计 链接: <四>业务流程实现:用户管理 ...
- 支付宝异步回调步骤+内网穿透++雷神商城项目
支付宝异步回调步骤+内网穿透++雷神商城项目 下面记录的步骤是基于雷神的谷粒商城项目,自己跟着该项目去做.项目大致方案是把商城业务分成数个微服务,访问所有微服务必须先过网关微服务:其次,利用nginx ...
- 《深度学习之TensorFlow》reading notes(3)—— MNIST手写数字识别之二
文章目录 模型保存 模型读取 测试模型 搭建测试模型 使用模型 模型可视化 本文是在上一篇文章 <深度学习之TensorFlow>reading notes(2)-- MNIST手写数字识 ...
最新文章
- Dart Metadata 使用
- [ffmpeg]安装
- Python内置数据类型之list
- updatebyprimarykeyselective返回什么是成功_嫦娥五号发射升空成功!!!
- 功率谱 幅值谱_语音合成中的Mel谱和MFCC谱无区别
- 解决element-ui的el-select组件文字超过宽度时不出现横向滚动条问题
- Dart教程(三):类的定义和使用
- rank函数怎么用oracle,Oracle学习教程:rank函数的使用
- spring cloud gateway filters学习
- 人才太缺!神州优车明修开放平台暗圈AI人才(附自动驾驶思路)
- 【PHP+微信开发】实现微信对账单处理
- 小米游戏本退出安全模式/win10安全模式密码
- ui设计和平面设计区别,平面设计好还是ui好
- 整合支付-alipay
- 阿翔编程学-webservice过滤器
- java简单代码逻辑实现数学黑洞6174
- java获取网络图片的宽和高
- 基于牛顿方法在直流微电网潮流研究(Matlab代码实现)
- An Overview of the Tesseract OCR Engine译文
- 控制计算机仿真答案,控制系统计算机仿真参考答案.doc
热门文章
- 解决 pymysql.err.OperationalError: (1054, “Unknown column ‘xxx‘ in ‘where clause‘“)
- 干货 :浅谈云音乐活动数据分析体系
- 什么是软件精灵?软件精灵功能介绍
- 已知三个点坐标求 三角形面积 || 求任意多边形面积公式||判断点在直线的左侧还是右侧
- 达观数据比赛 第一天任务
- 达观数据搜索引擎排序实践
- 《深入理解MySQL主从原理32讲》推荐篇
- LTE学习笔记六:MIMO多天线技术
- 中国银行网上银行新增B2B电子商务功能
- [读论文] Monocular 3D Object Reconstruction with GAN inversion (ECCV2022)