毕业设计学习锋迷商城的的笔记(手写后台商品管理,分类管理,用户,地址管理系统)
- https://player.bilibili.com/player.html?aid=641370075)(image-https://img-blog.csdnimg.cn/img_convert/6fc48aff3f62b207e20ca7badb21395a.png)(title-毕业设计SpringBoot+Vue+ElementUI商城系统实现(有后台))]
论文地址
商城论文地址
后台代码视频实现讲解思路
[video(video-zgz2Gybc-1667056389114)(type-bilibili)(url-https://player.bilibili.com/player.html?aid=816242633)(image-https://img-blog.csdnimg.cn/img_convert/69a030b2c0a14349f093065947c707ae.jpeg)(title-毕业设计锋迷商城手敲后台管理,实现逻辑讲解,代码讲解)]
b站地址
1. 商品管理
2.商品分类管理
3.商品地址管理
4.用户中心管理
4. 用户权限管理
5.订单管理
5.1
6.商品品牌管理
锋迷商城项目
使用Maven聚合项目进行创建(一个maven的父项目多个maven的子项目),
可以在父项目pom.xml文件中加上:
pom [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aKHt478Q-1633568521449)(C:\Users\CourageAndLove\AppData\Roaming\Typora\typora-user-images\image-20210812151308862.png)]
1.通过Maven聚合工程搭建项目:
1. 创建一个Maven的父项目,然后修改它的pom.xml文件,可以删除src等一些没有用的目录
pom 2.在父项目下面创建多个module,包括(common,beans,mapper,service,api)把它们全部打包成jar包
pom.xml加上
jar 3.由于mapper层需要调用beans层(pojo),需要在pom.xml文件中,然后可以在mapper中加入相关的依赖。
org.example beans 2.0.1 4.创建service需要调用mapper,和common,需要在pom.xml文件中加上
org.example mapper 2.0.1 org.example common 2.0.1 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引入相关的依赖:
io.springfox springfox-swagger2 2.9.2 io.springfox springfox-swagger-ui 2.9.2 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.配置生成规则 * * */ @Bean public 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加入依赖
com.github.xiaoymin swagger-bootstrap-ui 1.9.6 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" ?>
insert into users(username,password,user_regtime,user_modtime) values (#{username}, #{password},#{userRegtime},#{userModtime}) 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 { @Autowired private UserDao userDao;//可以在UserDao上面加上userDao,这个不会报红,但是没有什么意义 @Override public 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 @Override public ResultVO insert(String username, String pwd) { // 判断这个用户是否被注册 // 加上这个锁可以使用所有的注册都用这个userServiceimpl synchronized (this){ // 把密码进行MD5的加密 String password = MD5Utils.md5(pwd); User user1 = userDao.queryByName(username); //表示用户名没有被注册过,可以进行注册 if (user1==null){ //一个是注册时间,regtime,一个是修改时间modtime User 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 you else { 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插件
org.mybatis.generator mybatis-generator-maven-plugin 1.3.5 ${basedir}/src/main/resources/generator/generatorConfig.xml mysql mysql-connector-java 5.1.46 tk.mybatis mapper 4.1.5 7.1逆向工程配置
在resources的generator目录下面创建generatorConfig.xml
-
需要修改数据库的配置
-
需要修改pojo,mapper,Mapper.xml文件生成位置的配置
-
-
-
指定你的用Configuration generatorConfig.xml文件的路径
-
注意一下你的文件一定想要有空格什么东西的
<?xml version="1.0" encoding="UTF-8"?>
7.2在pom.xml文件中指定generatorConfig.xml文件的路径
加上了这个:
${basedir}/src/main/resources/generator/generatorConfig.xml org.mybatis.generator mybatis-generator-maven-plugin 1.3.5 ${basedir}/src/main/resources/generator/generatorConfig.xml mysql mysql-connector-java 5.1.46 tk.mybatis mapper 4.1.5 三、跨域问题
解决方案:
后端解决办法:在UserController加上@CrossOrigin注解前端通过Ajax请求跨域登录:
前端使用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
A页面设置值:
function(res){ console.log(res); if(res.code==1000){ // 获取前端传过来的数据 data var userInfo=res.data; // cookie和localstorage可以进行前端的页面之间的传值 setCookieValue("username",userInfo.username); setCookieValue("userImg",userInfo.userImg); window.location.href="index.html"; }else{ $("#tips").html(""); }
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进行表单的输入的绑定):
五、前后端分离开发用户认证的问题
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 { @Autowired private 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 { @Autowired private UsersMapper userDao;//可以在UserDao上面加上userDao,这个不会报红,但是没有什么意义 @Override public ResultVO checkLogin(String username, String pwd) { // 查询用户名 Example example = new Example(Users.class); Example.Criteria criteria = example.createCriteria(); criteria.andEqualTo("username",username); List
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 @Override public ResultVO insert(String username, String pwd) { // 判断这个用户是否被注册 // 加上这个锁可以使用所有的注册都用这个userServiceimpl synchronized (this){ // 把密码进行MD5的加密 String password = MD5Utils.md5(pwd); // 查询用户名 Example example = new Example(Users.class); Example.Criteria criteria = example.createCriteria(); criteria.andEqualTo("username",username); List users = userDao.selectByExample(example); //表示用户名没有被注册过,可以进行注册 if (users.size()==0){ //一个是注册时间,regtime,一个是修改时间modtime Users 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 you else { 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:
登录进行来可以把购物车获取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
-
添加依赖:
com.auth0 java-jwt 3.10.3 io.jsonwebtoken jjwt 0.9.1
UserServiceimpl(登录成功)生成token:
HashMap
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:
后端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
claimsJws=parser.parseClaimsJws(token); Claims body=claimsJws.getBody();//获取token中的用户数据 String subject=body.getSubject();//获取生成token设置subject String 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 { @Autowired private CheckTokenInterceptor checkTokenInterceptor; @Override public 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 可以看到它的方法 @Override public 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 { // 验证token try{ JwtParser parser= Jwts.parser(); parser.setSigningKey("houzhicong"); Jws
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格式的resultVO String 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 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
--根据父级分类的parent_id进行查询 1 级 parent_id=0 select *from category where parent_id=0,
-
创建用于封装查询的类别信息CategoryVO
在Beans模块中entity包下面创建一个CategoryVO实体类用于封装Category和前端 进行数据的响应,相对于Category多了这个属性
//实体类中实现当前分类的子分类 private List
categories; public List getCategories() { return categories; } public void setCategories(List 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
{ //使用连接查询实现分类列表查询 public List selectAllCategories(); // 子查询 public List selectAllCategories2(int parentId); } -
映射文件配置
<?xml version="1.0" encoding="UTF-8"?>
使用子查询实现分类列表的查询: ```xml
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 { @Resource private CategoryMapper categoryMapper; @Override public ResultVO queryAllCategory() { List
categoryVOS = categoryMapper.selectAllCategories2(0); return new ResultVO(ResultStatus.OK,"success",categoryVOS); } } 控制层实现
indexController实现:
@Autowired private 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
imgs; public List getImgs() { return imgs; } public void setImgs(List imgs) { this.imgs = imgs; } @Override public 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
{ // 查询推荐商品信息 public List 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
{ public List selectProductImgByProductId(int productId); } ProductMapper.xml文件实现
注意一下子查询 的语句:
<?xml version="1.0" encoding="UTF-8"?>
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
{ // 查询推荐商品信息 public List 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 { public List 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 { } -
业务层实现
//这里为不需要事务,但是如果某些事务如果调用了我也加入到事务中来 // 事务默认的隔离级别是可重复读 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
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 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 productSkus = productSkuMapper.selectByExample(example2); // System.out.println(productSkus); // 把所有的商品的详情信息放入HashMap当中进行使用 HashMap 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
{ public List selectCommentsByProductId(int productId); } 十一、添加购物车的功能实现
10.1流程分析:
点击添加购物车---------商品、选择套餐id,套餐属性,数量,token-------------进行token的校验
10.2数据库的相关的操作
-
增加字段sku_props
表生成之后 用逆向工程重新生成shopping_cart表的相关的结构。修改generalConfig.xml,把%修改成shopping_cart
注意一下%表示生成所有的表
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
进行商品数量的绑定可以用 v-model="num"进行双向绑定
- 库存{{productSkus[currentSkuIndex].stock}}件
10.2.2给加入购物车这个按钮添加点击事件
把相关的添加的购物车的信息放入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中获取token var 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
2. data中定义相关数据
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. 分页的表格
序号 订单商品 订单状态 时间 操作 {{index+1}} {{order.untitled}} 待付款 待发货 待收货 待评价 已完成 已关闭 {{order.createTime}} 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下面):
org.springframework.boot spring-boot-maven-plugin 2.0.1.RELEASE com.qfedu.fmmall.ApiApplication repackage 然后再次运行上面的命令: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
-
-