1、商品业务管理
1.1 VUE过滤器用法
/* 定义过滤器 */
/* Vue.filter("定义过滤器名称",function(参数){
过滤器需要添加return
}) */
Vue.filter("priceFormat",function(price){
return (price / 100).toFixed(2)
})
1.2 商品新增操作
1.2.1 页面跳转
根据用户请求地址实现页面跳转:
import Vue from 'vue'
import VueRouter from 'vue-router'
// 引入指定的路由组件
import Login from '../components/Login.vue'
import ElementUI from '../components/ElementUI.vue'
import Home from '../components/Home.vue'
import User from '../components/user/user.vue'
import Item from '../components/items/Item.vue'
import Welcome from '../components/Welcome.vue'
import ItemCat from '../components/items/ItemCat.vue'
import AddItem from '../components/items/addItem.vue'
//使用路由机制
Vue.use(VueRouter)
const routes = [
{path: '/', redirect: '/login'},
{path: '/login', component: Login},
{path: '/elementUI', component: ElementUI},
{path: '/home',component: Home, redirect: '/welcome', children:[
{path: '/welcome',component: Welcome},
{path: '/user',component: User},
{path: '/item',component: Item},
{path: '/itemCat',component: ItemCat},
{path: '/item/addItem',component: AddItem}
]}
]
1.2.2 商品新增业务说明(一)
商品分为(item/itemDesc),目前只完成商品基本信息的提交即可
1.2.3 页面JS分析
/* 添加商品按钮 */
async addItemBtn(){
//console.log(this.addItemForm)
//1.完成表单校验
this.$refs.addItemFormRef.validate( valid => {
if(!valid) return this.$message.error("请输入商品必填项")
})
//2.完成商品参数的封装
//2.0 将商品价格扩大100倍
this.addItemForm.price = this.addItemForm.price * 100
//2.1 将商品图片的数据转化为字符串
this.addItemForm.images = this.addItemForm.images.join(",")
//2.5 实现商品数据提交
let submitAddItem = {
item : this.addItemForm,
itemDesc: this.itemDesc
}
console.log(submitAddItem)
let {data: result} = await this.$http.post("/item/saveItem",submitAddItem)
if(result.status !== 200) return this.$message.error("商品添加失败")
this.$message.success("商品添加成功")
//2.5添加完成之后,将数据重定向到商品展现页面
this.$router.push("/item")
}
}
1.2.4 编辑ItemVO对象
1.2.5 商品新增业务接口文档
- 请求路径:http://localhost:8091/item/saveItem
- 请求类型:POST
- 前端传递参数分析
{
item: {
images: "/2021/05/20/da0c1d4781c1499399f090da8b60f359.jpg,/2021/05/20/2ac1c34776a7465887eb019655354c3c.jpg"
itemCatId: 560
num: "100"
price: 718800
sellPoint: "【华为官方直供,至高12期免息0首付,原装正品】送华为原装无线充+运动蓝牙耳机+蓝牙音箱+三合一多功能数据线+钢化膜等!"
title: "华为P40 Pro 5G手机【12期免息可选送豪礼】全网通智能手机"
},
itemDesc: {
itemDesc: "<ul><li>品牌: <a href=https://list.jd.com/list.html"....... "
}
}
- 请求参数:使用ItemVO对象接收
参数名称 | 参数类型 | 参数说明 | 备注 |
---|---|---|---|
item | Item | 商品基本信息对象封装 | 不能为null |
itemDesc | ItemDesc | 商品详情信息 | 不能为null |
- ImageVO参数详解:
- Item对象
参数名称 | 参数类型 | 参数说明 | 备注 |
---|---|---|---|
title | String | 商品标题信息 | 不能为null |
sellPoint | String | 商品卖点信息 | 不能为null |
price | Integer | 商品价格信息 | 不能为null 需要将数据扩大100倍 |
num | Integer | 商品数量信息 | 不能为null |
images | String | 商品图片地址信息 | 不能为null |
itemCatId | Integer | 商品父级分类ID | 不能为null |
status | Boolean | 商品状态信息 | 不能为null |
- itemDesc对象
为了降低商品提交代码的耦合性,将大字段信息详情,采用ItemDesc对象进行封装
参数名称 | 参数类型 | 参数说明 | 备注 |
---|---|---|---|
id | Integer | 商品Id信息 | 因为Item和ItemDesc是一对一关系 所以需要依赖Item对象的Id值 |
itemDesc | String | 商品详情信息 | 内部包含了大量的html语句 |
- 返回值结果:
参数名称 | 参数说明 | 备注 |
---|---|---|
status | 状态信息 | 200表示服务器请求成功 201表示服务器异常 |
msg | 服务器返回的提示信息 | 可以为null |
data | 服务器返回的业务数据 | 可以为null |
1.3 商品详情说明
1.3.1 引入富文本编辑器
说明:富文本用户操作的都是html代码片段
/*1、导入JS*/
/* 导入富文本编辑器 */
import VueQuillEditor from 'vue-quill-editor'
/* 导入富文本编辑器对应的样式 */
import 'quill/dist/quill.core.css' // import styles
import 'quill/dist/quill.snow.css' // for snow theme
import 'quill/dist/quill.bubble.css' // for bubble theme
/*2、页面JS*/
<el-tab-pane label="商品详情" name="2">
<!-- 定义富文本编辑器-->
<quill-editor ref="myQuillEditor" v-model="itemDesc.itemDesc">
</quill-editor>
<!-- 定义添加商品按钮-->
<el-button type="primary" class="addItemBtnClass" @click="addItemBtn">添加商品</el-button>
</el-tab-pane>
1.3.2 Item和ItemDesc关系
Item表: 主要封装了商品的基本信息.
ItemDesc表: 主要封装商品详情信息(大字段—html代码片段)
原因: 如果用户频繁的查询大字段 则影响效率. 所以将商品信息分为item和itemDesc
关联关系: item.id = itemDesc.id ID的值一致的.
1.3.4 编辑ItemController
1.3.5 编辑ItemServiceImpl
/**
* 完成商品入库操作
* @param itemVO
* 问题分析:
* 1.item入库之后,才会有主键信息. 对象理论上的ID=null
* 2.itemDesc入库时,必须获取与Item.id一样的数据.
* 如何解决:
* 设定主键自动回显功能!!!!!
* 如何设计:
* 开启主键自增 主键回显的配置 Mybatis原生操作
* <insert id="xxxx" useGeneratedKeys="true" keyColumn="主键字段" keyProperty="主键属性">
*
* </insert>
* MybatisPlus:
* MP在完成入库操作时,自动的实现了数据的回显功能. 所以ID是有值的.
* 知识: 哪种情况会有自动的回显功能!!!!!
* BUG: 由于测试数据可能会出现重复的现象. 需要提前删除多余的记录
*/
@Override
@Transactional
public void saveItem(ItemVO itemVO)
{
// 1、获取Item对象信息
Item item = itemVO.getItem();
// 2、设值默认状态
item.setStatus(true);
// 3、商品入库,主键自增
itemMapper.insert(item);
// 4、获取商品详情
ItemDesc itemDesc = itemVO.getItemDesc();
//如何保证item和ItemDesc的ID一致?
itemDesc.setId(item.getId());
itemDescMapper.insert(itemDesc);
}
1.3.6 BUG说明
如果出现主键重复,会报入库异常.则需要提前删除多余测试数据
1.4 文件上传操作
1.4.1 官网API说明
<!-- 图片上传操作
file-list="fileList" 双向数据绑定 控制图片的数量数组结构[],
:on-preview="handlePreview" 点击图片时候调用的函数
:on-remove="handleRemove" 当用户点击删除按钮时,触发函数
multiple 配置多选
drag 是否启用拖拽
action="图片提交的地址信息"
-->
<el-upload
class="upload-demo"
action="https://jsonplaceholder.typicode.com/posts/"
:on-preview="handlePreview"
:on-remove="handleRemove"
:file-list="fileList"
list-type="picture"
multiple
drag>
<el-button size="small" type="primary">点击上传</el-button>
<div slot="tip" class="el-upload__tip">只能上传jpg/png文件,且不超过500kb</div>
</el-upload>
1.4.2 文件上传的业务接口文档
- 请求路径: http://localhost:8091/file/upload
- 请求类型:POST
- 请求参数:
参数名称 | 参数说明 | 备注 |
---|---|---|
file | 文件上传的名称 | file中携带的是二进制信息 |
- 返回值结果:
参数名称 | 参数说明 | 备注 |
---|---|---|
status | 状态信息 | 200表示服务器请求成功 201表示服务器异常 |
msg | 服务器返回的提示信息 | 可以为null |
data | 服务器返回的业务数据 | 返回ImageVO对象 |
- ImageVO对象说明
参数名称 | 参数类型 | 参数说明 | 备注 |
---|---|---|---|
virtualPath | String | 图片实际路径 不包含磁盘信息 | 例如: 2021/11/11/a.jpg 不需要写磁盘地址 |
urlPath | String | 图片url访问地址 | http://image.jt.com/2021/11/11/a.jpg 需要指定域名地址 |
fileName | String | 文件上传后的文件名称 | UUID.type |
1.4.3 封装ImageVO对象
@Data
@Accessors(chain = true)
public class ImageVO
{
private String virtualPath; // 图片实际路径 不包含磁盘信息
private String urlPath; // 图片url访问地址
private String fileName; // 文件上传后的文件名称
}
1.4.4 文件上传入门案例
/**
* 业务: 文件上传入门案例
* URL: http://localhost:8091/file/upload
* 参数: file=[101001010111]
* 返回值: SysResult对象(ImageVO)
* 知识回顾: 字节流/字符流/缓存流 默认的语法复杂
* 高级API: SpringMVC 专门针对与流,开发了一个高级API
* 文件上传步骤:
* 1.获取文件上传名称
* 2.准备文件上传的目录
* 3.准备全文件的路径 目录/文件名称
* 4.实现上传
*/
@PostMapping("/upload")
public SysResult upload(MultipartFile file) throws Exception
{
// 1、动态获取文件名称
String fileName = file.getOriginalFilename();
//2.准备文件目录 Linux系统不能识别\
String dirPath = "H:/images";
File dirfile = new File(dirPath);
if (!dirfile.exists())
{
//应该创建一个新目录 创建多级目录
dirfile.mkdirs();
}
//3.拼接文件路径
String filePath = "H:/images/" + fileName;
//4.实现文件上传
file.transferTo(new File(filePath));
return SysResult.souccess();
}
2、正则表达式
正则表达式,又称规则表达式。(英语:Regular Expression,在代码中常简写为regex、regexp或RE),计算机科学的一个概念。正则表达式通常被用来检索、替换那些符合某个模式(规则)的文本。
许多程序设计语言都支持利用正则表达式进行字符串操作。例如,在Perl中就内建了一个功能强大的正则表达式引擎。正则表达式这个概念最初是由Unix中的工具软件(例如sed和grep)普及开的。正则表达式通常缩写成“regex”,单数有regexp、regex,复数有regexps、regexes、regexen。
语法:
匹配确定的次数:
例子: a{5} a出现5次
a{5,} a出现至少5次 >=5
a{5,8} a出现只能 5-8次
匹配任意字符
匹配字符区间范围
[xyz] 该字符只能取值 x/y/z中的一个 匹配单个字符
^ xyz 该字符除了xyz之外的其他字符.
[a-z] 该字符必须 a-z的区间中的一个
[0-9] 该字符必须 0-9的区间中的一个
分组结构:
(png|jpg|gif) 字符只能匹配png|jpg|gif中的一个 匹配的是字符串
2.1.1 编辑FileController
/**
* 需求分析: 文件上传完成之后,需要返回ImageVO对象
* @param file
* @return
* @throws IOException
*/
@PostMapping("/upload")
public SysResult upload(MultipartFile file) throws IOException
{
ImageVO imageVO = fileService.upload(file);
//不成功 应该返回null
if (imageVO == null)
{
return SysResult.fail();
}
return SysResult.souccess(imageVO);
}
2.1.2 编辑FileServiceImpl(部分)
/**
* 封装图片地址
*/
private String localDir = "H:/images"; //本地磁盘目录
private String preurlpath = "http://image.jt.com";
/**
* 1.校验文件上传的类型 jpg|png|gif
* 2.应该校验文件是否为恶意程序. 木马.exe.jpg
* 3.为了提高检索效率 应该分目录存储. 1.hash方式 xx/xx/xx/xx 分布不均
* 2.日期格式 yyyy/MM/dd 目录不断增长
* 4.防止文件重名 UUID.jpg
* @param file
* @return
*/
@Override
public ImageVO upload(MultipartFile file)
{
// 1、图片类型的校验 正则表达式
String FileName = file.getOriginalFilename();
// 将所有文件转化为小写字母
FileName = FileName.toLowerCase();
// 判断上传的图片是否满足
if (!FileName.matches("^.+\\.(png|jpg|gif)$"))
{
return null;
}
// 2、校验文件是否为恶意程序 判断依据 属性宽度和高度 aa.exe.jpg
try {
//该对象是用来专门操作图片的API
BufferedImage bufferedImage = ImageIO.read(file.getInputStream());
// 根据高度宽度判断
int height = bufferedImage.getHeight();
int width = bufferedImage.getWidth();
//如果有一项为0 则表示一定不是图片
if (height == 0 || width == 0) {
return null;
}
//3.分目录存储文件 /yyyy/MM/dd
//3.1 准备文件根目录
String dateDir = new SimpleDateFormat("/yyyy/MM/dd/").format(new Date());
//拼接文件目录 F:/images/2021/MM/dd/
String dirPath = localDir + dateDir;
File dirFile = new File(dirPath);
//3.2 判断是否需要创建目录
if (!dirFile.exists()) {
dirFile.mkdirs();
}
//4.防止文件重名 UUID.后缀
String uuid = UUID.randomUUID().toString().replace("-", "");
//获取.的下标位置
int index = FileName.indexOf(".");
//截取文件类型
String fileType = FileName.substring(index);
//拼接新文件路径
String realFileName = uuid + fileType;
// 5、文件上传
//5.1 准备文件的全路径 文件目录/文件名称
String realFilePath = dirPath + realFileName;
file.transferTo(new File(realFilePath));
// 6、封装返回值结果
String virtualPath = dateDir + realFileName;
// String urlPath = "https://gameplus-platform.cdn.bcebos.com/gameplus-platform/upload/file/source/a0da491aac4024c8b468614b1f3f2a3b.jpg";
//封装URL地址 协议名称://域名:端口号/图片虚拟地址
String urlPath = preurlpath + virtualPath;
ImageVO imageVO = new ImageVO();
imageVO.setVirtualPath(virtualPath);
imageVO.setUrlPath(urlPath);
imageVO.setFileName(realFileName);
return imageVO;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
@Override
public void deleteFile(String virtualPath)
{
//1.准备文件的全路径 本地路径 + 虚拟地址
String urlPath = localDir + virtualPath;
// 2、将路径封装为对象
File file = new File(urlPath);
// 3、实现删除
file.delete();
}
}
3 代理机制
3.1 反向代理
3.1.1 反向代理概念
反向代理服务器位于用户与目标服务器之间,但是对于用户而言,反向代理服务器就相当于目标服务器,即用户直接访问反向代理服务器就可以获得目标服务器的资源。同时,用户不需要知道目标服务器的地址,也无须在用户端作任何设定。反向代理服务器通常可用来作为Web加速,即使用反向代理作为Web服务器的前置机来降低网络和服务器的负载,提高访问效率
3.1.2 反向代理特点
- 代理服务器介于用户和服务器之间.
- 用户以为反向代理服务器就是目标服务器.
- 用户不清楚真实的服务器到底是谁!
- 反向代理服务器保护了目标服务器的信息 所以也称之为"服务器端代理".
3.1.3
3.2 正向代理
3.2.1 正向代理概念
正向代理,意思是一个位于客户端和原始服务器(origin server)之间的服务器,为了从原始服务器取得内容,客户端向代理发送一个请求并指定目标(原始服务器),然后代理向原始服务器转交请求并将获得的内容返回给客户端。客户端才能使用正向代理。
3.2.2 正向代理特点
- 正向代理服务器 介于用户和服务器之间
- 用户将请求发送给代理服务器,并且指定目标服务器.
- 目标服务器以为是代理服务器访问的,保护了用户的信息,所以也称之为 “客户端代理”
3.2.3 正向和反向代理特点
4 Nginx
4.1 Nginx服务器介绍
Nginx (engine x) 是一个高性能的HTTP和反向代理web服务器,同时也提供了IMAP/POP3/SMTP服务。Nginx是由伊戈尔·赛索耶夫为俄罗斯访问量第二的Rambler.ru站点(俄文:Рамблер)开发的,第一个公开版本0.1.0发布于2004年10月4日。
其将源代码以类BSD许可证的形式发布,因它的稳定性、丰富的功能集、简单的配置文件和低系统资源的消耗而闻名。2011年6月1日,nginx 1.0.4发布。
Nginx是一款轻量级的Web 服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器,在BSD-like 协议下发行。其特点是占有内存少,并发能力强,事实上nginx的并发能力在同类型的网页服务器中表现较好,中国大陆使用nginx网站用户有:百度、京东、新浪、网易、腾讯、淘宝等。
4.2 Nginx特点
- 占有内存少 不超过2M tomcat服务器200M java语言
- 并发能力强 3-5万次/秒 220-250个/秒 C语言
4.2.1 Nginx下载
URL:http://nginx.org/en/download.html
4.3 Nginx安装说明
4.3.1 nginx路径说明
路径问题:
nginx是c语言开发的所以对中文不友好. 额外需要注意空格
端口问题:
- Nginx运行的端口是80. 80端口不能被其它的服务占用.
- 如果80端口被其它的服务占用,则通过dos命令 kill 杀死进程.
占用端口:
http协议默认端口号80端口.
https协议默认端口号443端口
4.3.2 nginx进程项说明
nginx 每次启动都会有2个进程. 一个主进程, 一个是守护进程
主进程: 主要提供反向代理服务. 占用内存空间大
守护进程: 防止主进程意外关闭的.
如果需要关闭nginx 则先关闭守护 再关闭主进程.
4.3.2 nginx 命令(必须掌握)
说明: nginx的命令的执行 需要在nginx的根目录中运行
- 启动nginx start nginx
- 重启nginx nginx -s reload
- 关闭nginx nginx -s stop
注意事项: nginx的运行只能启动一次,如果启动多次则会产生多余项,影响程序的正常运行
4.4 Nginx 反向代理说明
4.4.1 反向代理入门案例
#1. 每个反向代理服务 都是一个server{}
#2. listen nginx监听用户请求端口 默认80
#3. server_name 拦截服务的名称/域名名称
#4. location {} 开始执行反向代理
#5. / 拦截的路径 拦截所有的请求
#6. root 代表反向代理的是一个目录
#7. index 代表访问的默认的页面
http{
server {
listen 80;
server_name localhost;
location / {
root html;
index index.html index.htm;
}
}
#一个http协议 多个server
}
4.4.2 图片回显
磁盘地址: H:\images\2021\10\15\a.jpg
网络地址: http://image.jt.com\2021\10\15\a.jpg
代理核心: http://image.jt.com 映射到 H:\images
4.4.3 实现域名代理
# 配置图片代理 记得保存
server {
listen 80;
server_name image.jt.com;
location / {
root E:/images;
}
}
注意事项:
- start nginx 命令 如果配置文件有错, 不会提示.
- nginx -s reload 重启命令 会有错误提示
4.4.4 图片回显原理
4.5 修改hosts文件
位置: C:\Windows\System32\drivers\etc
修改权限:
- 如果勾选只读,则去掉即可
- 选择用户勾选权限
- 修改hosts文件
#配置图片服务器
127.0.0.1 image.jt.com
#配置后端服务器
127.0.0.1 manage.jt.com
#配置前端服务器
127.0.0.1 www.jt.com
- 页面展现效果
4.5 关于图片回显问题说明
4.5.1 关于hosts文件说明
如果用户访问image.jt.com 不能跳转服务器,显示链接异常,则一般是hosts问题. 重复检查hosts
4.5.2 关于404报错说明
如果请求出现404 则说明 磁盘地址与域名地址 不一致, 需要检查代码 确定位置.
注意事项: nginx服务器 修改完成之后,记得重启