2.9.7 商品模块功能实现

编程入门 行业动态 更新时间:2024-10-25 00:24:06

2.9.7 商品<a href=https://www.elefans.com/category/jswz/34/1771428.html style=模块功能实现"/>

2.9.7 商品模块功能实现


文章目录

  • 1.商品分类数据展示
    • 1.1 页面JS分析
    • 1.2 业务接口文档
    • 1.3 商品分类表结构说明
    • 1.4 编辑ItemCatController
    • 1.5 编辑ItemCatService
    • 1.6 上述案例分析
    • 1.7 采用数据结构优化代码
    • 1.8 代码具体实现
  • 2.商品分类新增实现
    • 2.1 页面JS分析
    • 2.2 商品分类新增接口文档
    • 2.3 编辑ItemCatController
    • 2.4 编辑ItemCatService
  • 3.商品分类删除操作
    • 3.1 删除业务接口
    • 3.2 前端页面JS
    • 3.3 编辑ItemCatController
    • 3.4 编辑ItemCatService
  • 4.修改商品分类
    • 4.1 状态修改操作
      • 4.1.1 页面JS分析
      • 4.1.2 业务接口文档
      • 4.1.3 编辑ItemCatController
      • 4.1.4 编辑ItemCatService
    • 4.2 商品分类修改操作
      • 4.2.1 页面JS分析
      • 4.2.2 页面接口文档
      • 4.2.3 编辑ItemCatController
      • 4.2.4 编辑ItemCatService
    • 4.3 debug断点调试
  • 5.商品模块业务实现
    • 5.1 商品页面跳转
    • 5.2 数据自动填充
      • 5.2.1 业务分析
      • 5.2.2 添加自动填充注解
      • 5.2.3 配置自动填充类
    • 5.3 商品页面展示
      • 5.3.1 item 表设计
      • 5.3.2 编辑Item POJO
      • 5.3.3 编辑层级代码结构
      • 5.4 完成商品列表展现
      • 5.4.1 页面分析
      • 5.4.2 接口文档说明
      • 5.4.3 编辑ItemController
      • 5.4.4 编辑ItemService
      • 5.4.5 编辑分页配置类
      • 5.4.6 页面效果展现
    • 5.5 商品状态的修改
      • 5.5.1 页面JS分析
      • 5.5.2 业务接口文档
      • 5.5.3 编辑ItemController
      • 5.5.4 编辑ItemService
    • 5.6 商品删除操作
      • 5.6.1 页面分析
      • 5.6.2 业务接口文档
      • 5.6.3 编辑ItemController
      • 5.6.4 编辑ItemService
    • 5.7 商品新增操作
      • 5.7.1 实现页面跳转
      • 5.7.2 新增页面JS
      • 5.7.3 业务接口说明
      • 5.7.4 编辑ItemController
      • 5.7.5 编辑ItemService
  • 6.商品文件上传
    • 6.1 商品详情入库
      • 6.1.1 富文本编辑器
      • 6.1.2 引入步骤
      • 6.1.3 关于ItemDesc 的说明
      • 6.1.4 编辑ItemService
      • 6.1.5 数据库表字段类型设置
    • 6.2 实现商品图片上传
      • 6.2.1 官网说明
      • 6.2.2 图片上传项目说明
      • 6.2.4 图片上传接口文档说明
      • 6.2.5 编辑ImageVO
      • 6.2.6 编辑FileController
    • 6.3 正则表达式(复习)
      • 6.3.1 正则表达式说明
      • 6.3.2 匹配不确定次数
      • 6.3.3 匹配固定次数
      • 6.3.4 匹配取值区间
      • 6.3.5 分组匹配
      • 6.3.6 正则案例练习
    • 6.4 文件上传实现
      • 6.4.1 编辑FileController
      • 6.4.2 编辑FileService
    • 6.5 文件删除操作
      • 6.5.1 文件删除JS
      • 6.5.2 文件删除业务接口
      • 6.5.3 编辑FileController
      • 6.5.4 编辑FileService
    • 6.6 图片路径封装
      • 6.6.1 路径分析
      • 6.6.2 页面URL地址封装
    • 6.7动态为属性赋值
      • 6.7.1 业务需求
      • 6.7.2 编辑properties配置文件
      • 6.7.3 属性动态赋值
  • 7.拓展案例-实现商品列表中的修改功能
      • 6.2.1 编辑页面


1.商品分类数据展示

1.1 页面JS分析

  1. 生命周期函数
	//定义初始化函数created() {//默认获取商品分类列表数据this.findItemCatList()},
  1. 获取数据函数说明
async findItemCatList() {const {data: result} = await this.$http.get("/itemCat/findItemCatList/3")if (result.status !== 200) return this.$message.error("获取商品分类列表失败!!")this.itemCatList = result.data},

我们这里使用restfull的风格将菜单列表等级封装到了请求参数列表中。

1.2 业务接口文档

  • 请求路径: /itemCat/findItemCatList/{level}
  • 请求类型: get
  • 请求参数: level
参数名称参数说明备注
level查询级别1查询一级分类 2查询1-2 级商品分类 3查询1-2-3级商品分类
  • 业务说明: 查询3级分类菜单数据 要求三层结构嵌套
  • 返回值: SysResult对象
参数名称参数说明备注
status状态信息200表示服务器请求成功 201表示服务器异常
msg服务器返回的提示信息可以为null
data服务器返回的业务数据3级商品分类信息S

此处代码逻辑涉及到封装及嵌套,有部分数据结构的内容,需要多进行理解。

1.3 商品分类表结构说明

  1. 表结构

首先左侧的id是从1开始计数的,parent_id是从0开始的,也就是说,所有的一级菜单,其父级均为0,我们完成SQL时如果指定其他字段查询一级菜单会很困难,而我们使用parent_id=0 作为条件则很容易就可以得到所有的一级菜单。

同样的,如果要获取二级菜单,则需要使用其父级的id来作为指定的条件进行查询,例如查询汽车用品的二级菜单,需要使 汽车用品子级的parent_id= 汽车用品的ID。

  1. sql案例练习
/*所有的一级菜单 parent_id=0*/
SELECT * FROM item_cat WHERE parent_id = 0
/*查询个护化妆的二级菜单*/
SELECT * FROM item_cat WHERE parent_id = 249
/*查询香水彩妆的三级菜单*/
SELECT * FROM item_cat WHERE parent_id = 281

小结: 商品分类表,通过parent_id 来指定父子级关系,其中包含嵌套关系,想要获取三级菜单,需要先获取二级菜单,想要获取二级菜单则需要先获取一级菜单。

1.4 编辑ItemCatController

@RestController
@CrossOrigin
@RequestMapping("/itemCat")
public class ItemCatController {@Autowiredprivate ItemCatService itemCatService;/*** 需求: 查询商品分类信息* 参数: /{level}   1一级  2 一二级  3 一二三级* url: /itemCat/findItemCatList/{level}  restFul* 返回值: SysResult(3级列表信息)*/@GetMapping("findItemCatList/{level}")public SysResult findItemCatList(@PathVariable Integer level){List<ItemCat> itemCatList = itemCatService.findItemCatList(level);return SysResult.success(itemCatList);}}

我们这里再介绍一下方法参数列表中常用到的三种注解:

  • @RequestBody主要用来接收前端传递给后端的json字符串中的数据的,前端传入的json字符串Java无法直接使用,需要通过此注解解析为对象属性进行使用。
  • @PathVariable 映射 URL 绑定的占位符,就像当前案例,level的值通过resutfull的形式直接在URL中传递到后端,我们需要使用此注解解析其具体的值交给Java代码进行使用。
  • @param 持久层封装mybatis数据,当获取到的值的key与数据库不对应时,使用此注解标记其具体值映射数据库中的哪个字段。

1.5 编辑ItemCatService

package com.jt.service;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.jt.mapper.ItemCatMapper;
import com.jt.pojo.ItemCat;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.util.List;@Service
public class ItemCatServiceImpl implements ItemCatService{@Autowiredprivate ItemCatMapper itemCatMapper;/*** 步骤1.查询一级菜单列表* @param level* @return*/@Overridepublic List<ItemCat> findItemCatList(Integer level) {//1.查询一级菜单QueryWrapper queryWrapper = new QueryWrapper();queryWrapper.eq("parent_id",0);List<ItemCat> oneList = itemCatMapper.selectList(queryWrapper);//2.查询二级菜单 二级数据是一级数据的子级 封装到一级数据中.for(ItemCat oneItemCat : oneList){int oneId = oneItemCat.getId(); //一级对象ID//清空原始条件  必须有queryWrapper.clear();queryWrapper.eq("parent_id",oneId);List<ItemCat> twoList = itemCatMapper.selectList(queryWrapper);for(ItemCat twoItemCat : twoList){//获取二级分类IDint twoId = twoItemCat.getId();//3.查询三级列表信息queryWrapper.clear();queryWrapper.eq("parent_id",twoId);List<ItemCat> threeList = itemCatMapper.selectList(queryWrapper);//将三级列表 封装到二级对象中twoItemCat.setChildren(threeList);}//将二级数据封装到一级对象中oneItemCat.setChildren(twoList);}return oneList;}
}

1.6 上述案例分析

  1. 上述的案例 采用多级循环的方式. 将来会耗费服务器资源 100次 内层100次 总循环1万次. 暂时可以接受
  2. 上述的代码 频繁访问数据库.导致数据库压力增大.严重时可能导致数据库服务器宕机. 不能接受
    优化策略: 降低数据库访问的次数

1.7 采用数据结构优化代码

思路:

  1. 用户查询所有的数据库信息. (1-2-3所有数据)
  2. 数据结构 Map<k,v> key唯一的, value可以任意类型.
    思路: Map<parentId,List>
    例子:
  3. Map<0,List<一级ItemCat对象>>
  4. Map<249,List<二级ItemCat对象>>
  5. Map<281,List<三级ItemCat对象>>
    利用map 封装父子关系.

1.8 代码具体实现

@Service
public class ItemCatServiceImpl implements ItemCatService{@Autowiredprivate ItemCatMapper itemCatMapper;/*** 利用Map集合封装所有的数据库记录* 封装数据:*      1.遍历所有的数据信息.*      2.获取每一个parentId的值.* 例子:*      1.{id=1,parentId=0,name="张三"}*      2.{id=2,parentId=0,name="李四"}*      3.{id=3,parentId=1,name="王五"}*      Map= {*          key : value*          0   : List[张三对象,李四对象.....],*          1   : List[王五对象......]*      }* @return*/public Map<Integer,List<ItemCat>> getMap(){Map<Integer,List<ItemCat>> map = new HashMap<>();//1.查询所有的数据库信息List<ItemCat> itemCatList = itemCatMapper.selectList(null);//2.将数据封装到map集合中for (ItemCat itemCat : itemCatList){Integer key = itemCat.getParentId(); //获取parentId当做key//3.判断map集合中是否有值.if(map.containsKey(key)){//有值: 获取List集合,将自己追加到其中map.get(key).add(itemCat);}else{//没值: 添加数据.将自己作为第一个元素填充List<ItemCat> list = new ArrayList<>();list.add(itemCat);map.put(key,list);}}return map;}@Overridepublic List<ItemCat> findItemCatList(Integer level) {long startTime = System.currentTimeMillis();Map<Integer,List<ItemCat>> map = getMap();//根据level获取子级信息if(level == 1){ //只获取一级列表信息return map.get(0);}if(level == 2){ //获取一级和二级数据return getTwoList(map);}List<ItemCat> oneList = getThreeList(map);long endTime = System.currentTimeMillis();System.out.println("优化前的耗时: 500ms,优化后耗时:"+(endTime - startTime)+"ms");return oneList;}//获取三级列表信息  先获取1级数据,再获取2级数据.再获取3级数据private List<ItemCat> getThreeList(Map<Integer, List<ItemCat>> map) {//1.调用2级菜单方法.List<ItemCat> oneList = getTwoList(map);//2.实现思路 遍历一级集合,获取二级数据. 封装三级菜单for(ItemCat oneItemCat : oneList){//2.1 获取二级数据List<ItemCat> twoList = oneItemCat.getChildren();if(twoList == null || twoList.size()==0){//判断二级集合是否为null.如果为null,表示没有二级菜单.continue;}for (ItemCat twoItemCat : twoList){int twoId = twoItemCat.getId();List<ItemCat> threeList = map.get(twoId);twoItemCat.setChildren(threeList);}}return oneList;}//通过map集合 获取一级二级菜单信息.private List<ItemCat> getTwoList(Map<Integer, List<ItemCat>> map) {List<ItemCat> oneList = map.get(0);//获取二级信息,应该先遍历一级集合for (ItemCat oneItemCat : oneList){int oneId = oneItemCat.getId();//根据一级Id,获取二级集合List<ItemCat> twoList = map.get(oneId);oneItemCat.setChildren(twoList);}return oneList;}/*** 步骤1.查询一级菜单列表* 步骤2.通过一级菜单列表查询二级菜单列表* 步骤3.通过二级菜单列表查询三级菜单列表* @param level* @return*//* @Overridepublic List<ItemCat> findItemCatList(Integer level) {long startTime = System.currentTimeMillis();//1.查询一级菜单QueryWrapper queryWrapper = new QueryWrapper();queryWrapper.eq("parent_id",0);List<ItemCat> oneList = itemCatMapper.selectList(queryWrapper);//2.查询二级菜单 二级数据是一级数据的子级 封装到一级数据中.for(ItemCat oneItemCat : oneList){int oneId = oneItemCat.getId(); //一级对象ID//清空原始条件  必须有queryWrapper.clear();queryWrapper.eq("parent_id",oneId);List<ItemCat> twoList = itemCatMapper.selectList(queryWrapper);for(ItemCat twoItemCat : twoList){//获取二级分类IDint twoId = twoItemCat.getId();//查询三级列表信息queryWrapper.clear();queryWrapper.eq("parent_id",twoId);List<ItemCat> threeList = itemCatMapper.selectList(queryWrapper);//将三级列表 封装到二级对象中twoItemCat.setChildren(threeList);}//将二级数据封装到一级对象中oneItemCat.setChildren(twoList);}long endTime = System.currentTimeMillis();System.out.println("耗时:"+ (endTime - startTime)+"ms");return oneList;}*/
}

当前案例改造只是解决了多次查询数据库导致的缓慢问题,将多次查询改为单次查询,将查询结构封装到Map集合中,后续使用进行解析Map集合进行数据处理,当然,实际的业务中具体实现还有更好的方式,大家后期学习数据结构后可以有更高效率的处理方式。

2.商品分类新增实现

2.1 页面JS分析

  1. 页面JS分析
 		//定义商品分类新增对象itemCatForm: {name: '', //定义商品分类名称parentId: 0, //默认父级ID=0level: 1 //默认是一级菜单},async addItemCatForm() {//先将整个表单进行校验this.$refs.itemCatFormRef.validate(async validate => {if (!validate) returnconst {data: result} = await this.$http.post("/itemCat/saveItemCat", this.itemCatForm)if (result.status !== 200) return this.$message.error("新增商品分类失败")this.$message.success("新增商品分类成功!!!")//新增成功,则刷新分类列表信息this.findItemCatList();this.addItemCatDialogVisible = false})},

前端页面中,我们将新增商品分类设置了默认为一级菜单,所以当新增一级菜单时不用选择父级,当新增二级菜单时,需要选择一级菜单为父级。

2.2 商品分类新增接口文档

  • 请求路径: /itemCat/saveItemCat
  • 请求类型: post
  • 请求参数: 表单数据
参数名称参数说明备注
name商品分类名称不能为null
parentId用户父级ID不能为null
level分类级别1 2 3 商品分类级别
  • 返回值: SysResult对象
参数名称参数说明备注
status状态信息200表示服务器请求成功 201表示服务器异常
msg服务器返回的提示信息可以为null
data服务器返回的业务数据可以为null

2.3 编辑ItemCatController

/*** 业务需求:  实现商品分类新增* URL:  /itemCat/saveItemCat* 类型: post* 参数: {"name":"AAAAAA","parentId":0,"level":1} json串* 返回值: SysResult对象*/@PostMapping("/saveItemCat")public SysResult saveItem(@RequestBody ItemCat itemCat){itemCatService.saveItem(itemCat);return SysResult.success();}

2.4 编辑ItemCatService

	@Override@Transactional  //事务控制public void saveItem(ItemCat itemCat) {Date date = new Date();itemCat.setStatus(true).setCreated(date).setUpdated(date);//启动itemCatMapper.insert(itemCat);}

3.商品分类删除操作

3.1 删除业务接口

  • 请求路径: /itemCat/deleteItemCat
  • 请求类型: delete
  • 业务描述: 当删除节点为父级时,应该删除自身和所有的子节点
  • 请求参数:
参数名称参数说明备注
ID用户ID号不能为null
level商品分类级别 一级,二级,三级
  • 返回值结果 SysResult对象
参数名称参数说明备注
status状态信息200表示服务器请求成功 201表示服务器异常
msg服务器返回的提示信息可以为null
data服务器返回的业务数据可以为null

3.2 前端页面JS

1. 页面JS
<el-button type="danger" icon="el-icon-delete" @click="deleteItemCatBtn(scope.row)">删除</el-button>2. 发起Ajax请求
//传递分类idconst {data: result} = await this.$http.delete("/itemCat/deleteItemCat",{params:{id:itemCat.id,level:itemCat.level}})if(result.status !== 200) return this.$message.error("删除商品分类失败")this.$message.success("删除数据成功")//删除成功之后,刷新页面数据this.findItemCatList()          

3.3 编辑ItemCatController

 /*** 完成商品分类的删除操作* 1. 编辑URL: /itemCat/deleteItemCat* 2. 参数: id/level* 3. 返回值: SysResult()*/@DeleteMapping("/deleteItemCat")public SysResult deleteItemCat(Integer id,Integer level){itemCatService.deleteItemCat(id,level);return SysResult.success();}

3.4 编辑ItemCatService

//删除商品分类数据@Override@Transactionalpublic void deleteItemCat(Integer id, Integer level) {//判断是否为3级菜单if(level == 3){itemCatMapper.deleteById(id);}if(level == 2){//如果是二级,应该先获取三级数据之后删除,再删除自己//delete from item_cat where parent_id=#{id} or id = #{id}QueryWrapper<ItemCat> queryWrapper = new QueryWrapper<>();queryWrapper.eq("parent_id",id).or().eq("id",id);itemCatMapper.delete(queryWrapper);}/*** 如何删除一级菜单?*  1.获取二级ID*  终极sql: delete from item_cat where parent_id in (twoIds)*          or  id in (twoIds)*          or  id = #{id}*/if(level == 1){QueryWrapper<ItemCat> queryWrapper = new QueryWrapper();queryWrapper.eq("parent_id",id);List twoIds = itemCatMapper.selectObjs(queryWrapper);//清空数据queryWrapper.clear();//规则: 如果2级菜单有值,才会删除 2级和三级queryWrapper.in(twoIds.size()>0,"parent_id",twoIds).or().in(twoIds.size()>0,"id",twoIds).or().eq("id",id);itemCatMapper.delete(queryWrapper);}}

4.修改商品分类

4.1 状态修改操作

4.1.1 页面JS分析

1. 页面JS函数
updateStatus(scope.row)2. 页面JS//根据ID修改状态信息async updateStatus(itemCat) {const {data: result} = await this.$http.put(`/itemCat/status/${itemCat.id}/${itemCat.status}`)if (result.status !== 200) return this.$message.error("修改状态失败")this.$message.success("状态修改成功")},

4.1.2 业务接口文档

  • 请求路径: /itemCat/status/{id}/{status}
    • 请求类型: put
    • 请求参数:
参数名称参数说明备注
ID用户ID号不能为null
status用户的状态信息不能为null
  • 返回值: SysResult对象
参数名称参数说明备注
status状态信息200表示服务器请求成功 201表示服务器异常
msg服务器返回的提示信息可以为null
data服务器返回的业务数据可以为null

4.1.3 编辑ItemCatController

/*** 实现商品分类状态修改* URL: /itemCat/status/{id}/{status}* 请求类型: PUT/POST* 请求参数: id/status* 返回值: SysResult对象*/@PutMapping("/status/{id}/{status}")public  SysResult updateStatus(ItemCat itemCat){itemCatService.updateStatus(itemCat);return SysResult.success();}

4.1.4 编辑ItemCatService

	@Override@Transactionalpublic void updateStatus(ItemCat itemCat) {//id/statusitemCat.setUpdated(new Date());itemCatMapper.updateById(itemCat);}

4.2 商品分类修改操作

4.2.1 页面JS分析

	1.指定修改的按钮<el-button type="success" icon="el-icon-edit" @click="updateItemCatBtn(scope.row)">编辑</el-button>2. 数据的回显//由于有层级关系,所有修改只能修改名称updateItemCatBtn(itemCat) {this.updateItemCatForm = itemCatthis.updateItemCatDialogVisible = true},3. 修改页面的JS<el-dialog title="修改商品分类" :visible.sync="updateItemCatDialogVisible" width="50%"><!-- 定义分类表单 --><el-form :model="updateItemCatForm" :rules="rules" ref="upDateItemCatForm" label-width="100px"><el-form-item label="分类名称:" prop="name"><el-input v-model="updateItemCatForm.name"></el-input></el-form-item></el-form><span slot="footer" class="dialog-footer"><el-button @click="updateItemCatDialogVisible = false">取 消</el-button><el-button type="primary" @click="updateItemCat">确 定</el-button></span></el-dialog>4. 修改按钮的JSasync updateItemCat() {//修改商品分类信息const {data: result} =await this.$http.put('/itemCat/updateItemCat', this.updateItemCatForm)if (result.status !== 200) return this.$message.error("更新商品分类失败")this.$message.success("更新商品分类成功")this.findItemCatList();this.updateItemCatDialogVisible = false;},

4.2.2 页面接口文档

  • 请求路径: /itemCat/updateItemCat
  • 请求类型: put
  • 请求参数: 表单数据 ItemCat对象
  • 返回值: SysResult对象
参数名称参数说明备注
status状态信息200表示服务器请求成功 201表示服务器异常
msg服务器返回的提示信息可以为null
data服务器返回的业务数据可以为null

数据解析:

4.2.3 编辑ItemCatController

/*** 修改商品分类名称* URL: /itemCat/updateItemCat* 参数: 整个form表单  JSON串* 返回值: SysResult对象*/@PutMapping("/updateItemCat")public SysResult updateItemCat(@RequestBody ItemCat itemCat){itemCatService.updateItemCat(itemCat);return SysResult.success();}

4.2.4 编辑ItemCatService

 //由于页面只修改的name名称.所以sql也只修改name/updated@Override@Transactionalpublic void updateItemCat(ItemCat itemCat) {//用户只修改name,updated by idItemCat temp = new ItemCat();temp.setId(itemCat.getId()).setName(itemCat.getName()).setUpdated(new Date());itemCatMapper.updateById(temp);}

4.3 debug断点调试

5.商品模块业务实现

5.1 商品页面跳转

  1. 编辑index.js的路由文件
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 ItemCat from '../components/items/ItemCat.vue'
import Item from '../components/items/Item.vue'
//使用路由机制
Vue.use(VueRouter)
const routes = [{path: '/', redirect: '/login'},{path: '/login', component: Login},{path: '/elementUI', component: ElementUI},{path: '/home', component: Home, children: [{path: '/user', component: User},{path: '/itemCat', component: ItemCat},{path: '/item', component: Item}]}
]
  1. 页面效果

5.2 数据自动填充

5.2.1 业务分析

说明: 如果每次新增/更新时 都需要添加创建时间/修改时间 这样做比较繁琐.能否通过框架自动的实现数据填充.

MyBatis-PLUS提供了自动填充功能,根据提供的实现类,其自动实现数据的填充:

5.2.2 添加自动填充注解

//pojo基类,完成2个任务,2个日期,实现序列化
@Data
@Accessors(chain=true)
public class BasePojo implements Serializable{@TableField(fill = FieldFill.INSERT) 		//新增操作时,实现自动填充private Date created;	//表示入库时需要赋值@TableField(fill = FieldFill.INSERT_UPDATE) //新增/修改操作时,自动填充private Date updated;	//表示入库/更新时赋值.
}

5.2.3 配置自动填充类

配置类我们一般都写在config包下:

@Component
public class MyMetaObjectHandler implements MetaObjectHandler {//框架用法说明: MP根据实现类,自动的完成数据的注入. MP框架自动调用//metaObject: 指定默认的规则@Overridepublic void insertFill(MetaObject metaObject) {Date date = new Date();this.setFieldValByName("created", date, metaObject);this.setFieldValByName("updated", date, metaObject);}@Overridepublic void updateFill(MetaObject metaObject) {this.setFieldValByName("updated", new Date(), metaObject);}
}

然后我们在接口实现类中关于更新和新增操作的类都进行优化,将时间填充注释掉后进行测试:

5.3 商品页面展示

5.3.1 item 表设计

我们在表设计中对其价格进行了特殊设计,在原基础上扩大了100倍,在数据库中整数运算比浮点数运算要快,所以这里使用整数进行存储,在页面展示时,再对其进行浮点数转化即可。

还有其图片是存储的其图片的具体路径,而不是图片本身的文件,这样可以有效减轻数据库数据量的压力。

5.3.2 编辑Item POJO

@Data
@Accessors(chain = true)
@TableName("item")
public class Item extends BasePojo{@TableId(type= IdType.AUTO) private Integer id;         //商品Id号private String title;       //商品标题信息private String sellPoint;   //卖点信息private Integer price;      //商品价格private Integer num;        //商品数量private String images;       //商品图片private Integer itemCatId;  //商品分类ID号private Boolean status;     //状态信息    0 下架 1 上架
}

5.3.3 编辑层级代码结构

5.4 完成商品列表展现

5.4.1 页面分析

//1. 生命周期函数created() {//1.获取商品列表数据this.getItemList()},//2. 调用 this.getItemList()async getItemList() {const {data: result} =await this.$http.get("/item/getItemList", {params: this.queryItemInfo})if (result.status !== 200) return this.$message.error("商品列表查询失败")this.itemList = result.data.rowsthis.total = result.data.total},

5.4.2 接口文档说明

  • 请求路径: /item/getItemList?query=&pageNum=1&pageSize=10
  • 请求类型: get
  • 请求参数: 使用pageResult对象接收
参数名称参数说明备注
query用户查询的数据可以为null
pageNum分页查询的页数必须赋值不能为null
pageSize分页查询的条数必须赋值不能为null
  • 返回值结果:
参数名称参数说明备注
status状态信息200表示服务器请求成功 201表示服务器异常
msg服务器返回的提示信息可以为null
data服务器返回的业务数据商品分页对象

5.4.3 编辑ItemController

@RestController
@CrossOrigin
@RequestMapping("/item")
public class ItemController {@Autowiredprivate ItemService itemService;/*** 业务: 实现商品的分页查询* URL: /item/getItemList?query=&pageNum=1&pageSize=10* 参数: query=&pageNum=1&pageSize=10* 返回值: SysResult(PageResult)*/@GetMapping("/getItemList")public SysResult getItemList(PageResult pageResult){//3//3(用户查询的数据,分页查询的页数,分页查询的条数 )+2(总记录数,分页结果)pageResult  = itemService.getItemList(pageResult);return SysResult.success(pageResult);//5}
}

5.4.4 编辑ItemService

@Service
public class ItemServiceImpl implements ItemService{@Autowiredprivate ItemMapper itemMapper;/***  要求: 3(用户查询的数据,分页查询的页数,分页查询的条数 )+2(总记录数,分页结果)*  关于selectPage(参数说明)*   参数1: page MP提供的分页对象*   参数2: 条件构造器* @param pageResult* @return*/@Overridepublic PageResult getItemList(PageResult pageResult) {//1.构建分页对象  参数1: 第几页   参数2: 多少条Page<Item> page = new Page<>(pageResult.getPageNum(),pageResult.getPageSize());//2.准备条件构造器 构建模糊查询QueryWrapper queryWrapper = new QueryWrapper();String query = pageResult.getQuery();boolean flag = StringUtils.hasLength(query);queryWrapper.like(flag,"title",query);//3.根据MP查询 实现分页数据的自动封装page = itemMapper.selectPage(page,queryWrapper);//4.获取数据,返回分页对象long total = page.getTotal();//获取分页结果List<Item> rows = page.getRecords();return pageResult.setTotal(total).setRows(rows);}
}

5.4.5 编辑分页配置类

MyBatis-plus中对分页数据提供了相关插件功能,以助于实现分页功能,通过我们的配置文件,指定数据库的类型,方便其对具体版本的数据库进行操作,得到我们需要的结果:

我们这里仿照其官网的配置类进行我们案例配置类的完成:

@Configuration  //这是配置类
public class MybatisConfig {//需要通过配置文件 指定数据库类型.// 最新版@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MARIADB));return interceptor;}
}

5.4.6 页面效果展现

5.5 商品状态的修改

5.5.1 页面JS分析

		<template slot-scope="scope"><el-switch v-model="scope.row.status" active-color="#13ce66" inactive-color="#ff4949"@change="updateStatus(scope.row)"></el-switch></template>async updateStatus(item) {const { data: result} =await this.$http.put("/item/updateItemStatus", {id: item.id,status: item.status})if (result.status !== 200) return this.$message.error("更新状态失败")this.$message.success("更新状态成功")},

5.5.2 业务接口文档

  • 请求路径: /item/updateItemStatus
  • 请求类型: put
  • 请求参数: 使用对象接收
参数名称参数说明备注
id商品id不能为null
status状态信息不能为null
  • 返回值结果:
参数名称参数说明备注
status状态信息200表示服务器请求成功 201表示服务器异常
msg服务器返回的提示信息可以为null
data服务器返回的业务数据可以为null

5.5.3 编辑ItemController

 /*** 修改商品的状态信息* URL: /item/updateItemStatus* 参数: JSON串 {id:xx,status:xx}* 返回值: SysResult对象*/@PutMapping("/updateItemStatus")public SysResult updateItemStatus(@RequestBody Item item){itemService.updateItemStatus(item);return SysResult.success();}

5.5.4 编辑ItemService

 @Override@Transactional //控制事务public void updateItemStatus(Item item) {itemMapper.updateById(item);}

5.6 商品删除操作

5.6.1 页面分析

//根据id删除数据const {data: result} = await this.$http.delete("/item/deleteItemById", {params: {id: item.id}})if (result.status !== 200) return this.$message.error("商品删除失败")this.$message.success("商品删除成功")//重新获取商品列表信息this.getItemList()

5.6.2 业务接口文档

  • 请求路径: /item/deleteItemById
  • 请求类型: delete
  • 请求参数:
参数名称参数说明备注
ID商品ID号不能为null
  • 返回值结果:
参数名称参数说明备注
status状态信息200表示服务器请求成功 201表示服务器异常
msg服务器返回的提示信息可以为null
data服务器返回的业务数据可以为null

5.6.3 编辑ItemController

 /*** 业务需求: 根据Id 删除数据* URL: /item/deleteItemById* 参数: id* 返回值: SysResult对象*/@DeleteMapping("/deleteItemById")public SysResult deleteItemById(Integer id){itemService.deleteItemById(id);return SysResult.success();}

5.6.4 编辑ItemService

 	 @Override@Transactionalpublic void deleteItemById(Integer id) {itemMapper.deleteById(id);}

5.7 商品新增操作

5.7.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 ItemCat from '../components/items/ItemCat.vue'
import Item from '../components/items/Item.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, children:[{path: '/user',component: User},{path: '/itemCat', component: ItemCat},{path: '/item', component: Item},{path: '/item/addItem', component: AddItem}]},]

页面效果:

5.7.2 新增页面JS

表结构说明:

  1. 商品的基本信息 保存到item表
  2. 商品的详细信息 保存到item_desc表中.
 /* 添加商品按钮 */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 实现商品数据提交 用一个大对象 包裹2个小对象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")}

因为前端只能传递一个对象,所以我们将商品信息和商品详情信息的对象包裹到商品提交信息中进行传递。

5.7.3 业务接口说明

  • 请求路径: http://localhost:8091/item/saveItem
    
  • 请求类型: post
  • 前端传递参数分析
	{item: {images: "/2021/05/20/da0c1d4781c1499399f090da8b60f359.jpg,/2021/05/20/2ac1c34776a7465887eb019655354c3c.jpg"itemCatId: 560num: "100"price: 718800sellPoint: "【华为官方直供,至高12期免息0首付,原装正品】送华为原装无线充+运动蓝牙耳机+蓝牙音箱+三合一多功能数据线+钢化膜等!"title: "华为P40 Pro 5G手机【12期免息可选送豪礼】全网通智能手机"},itemDesc: {itemDesc: "<ul><li>品牌:&nbsp;<a href=.html".......      "}}
  • 请求参数: 使用ItemVO对象接收
参数名称参数说明备注
itemItem商品基本信息对象封装
itemDescItemDesc商品详情信息
itemParamItemParam商品参数信息
  • ItemVO参数详解:
  • Item对象
参数名称参数类型参数说明备注
titleString商品标题信息不能为null
sellPointString商品卖点信息
priceInteger商品价格信息不能为null 需要将数据扩大100倍
numInteger商品数量信息不能为null
imagesString商品图片地址信息不能为null
itemCatIdInteger商品父级分类ID不能为null
statusBoolean商品状态信息不能为null
  • itemDesc 对象

    为了降低商品提交代码的耦合性,将大字段信息详情,采用ItemDesc对象进行封装
    
参数名称参数类型参数说明备注
idInteger商品Id信息因为Item和ItemDesc是一对一关系 所以需要依赖Item对象的Id值
itemDescString商品详情信息内部包含了大量的html语句
  • 返回值结果:
参数名称参数说明备注
status状态信息200表示服务器请求成功 201表示服务器异常
msg服务器返回的提示信息可以为null
data服务器返回的业务数据可以为null

5.7.4 编辑ItemController

 /*** 完成商品新增操作* 1.URL地址  http://localhost:8091/item/saveItem* 2.参数     post   itemVO JSON串* 3.返回值   SysResult对象*/@PostMapping("/saveItem")public SysResult saveItem(@RequestBody ItemVO itemVO){itemService.saveItem(itemVO);return SysResult.success();}

5.7.5 编辑ItemService

 @Override@Transactionalpublic void saveItem(ItemVO itemVO) {Item item = itemVO.getItem();//设定状态item.setStatus(true);itemMapper.insert(item);}

6.商品文件上传

6.1 商品详情入库

6.1.1 富文本编辑器

说明: 富文本可以在页面中,实现 “所见即所得” 的效果

6.1.2 引入步骤

  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/* 将富文本编辑器注册为全局可用的组件 */
Vue.use(VueQuillEditor)
  1. 使用富文本编辑器
 <!-- 定义富文本编辑器--><quill-editor ref="myQuillEditor" v-model="itemDesc.itemDesc"></quill-editor>

6.1.3 关于ItemDesc 的说明

说明: 由于Item和ItemDesc 是典型的一对一. 所以要求 item.id = itemDesc.id

@Data
@Accessors(chain = true)
@TableName("item_desc")
public class ItemDesc extends BasePojo{@TableIdprivate Integer id;private String itemDesc;
}

6.1.4 编辑ItemService

 /*** 问题: id是主键自增. 入库之后才有主键所以*      应该让主键动态回显* 1.Mybatis 动态实现回显*      <insert id="xxxx" useGeneratedKeys="true" keyColumn="id" keyProperty="id">*         insertinto xxxx*     </insert>* 2.MP是mybatis的增强版本.所以可以实现自动的主键回显!!!* @param itemVO*/@Override@Transactionalpublic void saveItem(ItemVO itemVO) {Item item = itemVO.getItem();//设定状态item.setStatus(true);itemMapper.insert(item);//获取商品详情ItemDesc itemDesc = itemVO.getItemDesc();itemDesc.setId(item.getId());itemDescMapper.insert(itemDesc);}

6.1.5 数据库表字段类型设置

说明: 为了存储大字段.修改数据库类型

6.1.6 商品新增功能测试
我们可以直接复制其他网页的页面:

将其粘贴到商品详情中,点击添加后查看数据库中的数据,我们可以看到商品与商品详情中都新增了相关数据,并且商品详情中的数据是以html格式进行保存的:

6.2 实现商品图片上传

6.2.1 官网说明

//1. 官网图片JS说明<!-- 图片上传的JS1. action: 代表图片上传的地址url2. file-list: 图片列表数据的集合[{name:"xx",url:"xxx"},{}]3. 钩子函数: 满足某些条件时触发.4. on-preview 当点击已上传列表的信息时触发5. on-remove  当移除列表中的图片时触发--><el-uploadclass="upload-demo"action="/":on-preview="handlePreview":on-remove="handleRemove":file-list="fileList"list-type="picture"><el-button size="small" type="primary">点击上传</el-button><div slot="tip" class="el-upload__tip">只能上传jpg/png文件,且不超过500kb</div></el-upload>
2. 页面JS补充知识handlePreview(){console.log("触发查看函数!!!!")},handleRemove(){console.log("移除时触发!!!!")}

我们这里使用了element-ui官网中的upload标签用来实现图片的上传。

6.2.2 图片上传项目说明

<!--一.文件上传组件说明1.action: 上传图片地址 http://localhost:8091/xxx/xxx2.on-preview 点击已上传图片时触发3.on-remove  点击移除图片时触发4.on-success 图片上传成功时触发5.multiple   可以支持多张图片上传6.drag       是否允许拖拽二.请求类型:    一般上传字节信息时,首选post请求三.上传文件key  说明: 文件上传时的key=file.后端接收数据时采用file接收.--><el-upload class="upload-demo" :action="uploadUrl" :on-preview="handlePreview" :on-remove="handleRemove":on-success="handleSuccess" 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>//定义文件上传路径地址
uploadUrl: "http://localhost:8091/file/upload",

6.2.4 图片上传接口文档说明

  • 请求路径: http://localhost:8091/file/upload
  • 请求类型: post
  • 请求参数:
参数名称参数说明备注
file文件上传的参数名称file中携带的是二进制信息
  • 返回值结果:
参数名称参数说明备注
status状态信息200表示服务器请求成功 201表示服务器异常
msg服务器返回的提示信息可以为null
data服务器返回的业务数据返回ImageVO对象
  • ImageVO对象说明
参数名称参数类型参数说明备注
virtualPathString图片实际路径 不包含磁盘信息例如: 2021/11/11/a.jpg 不需要写磁盘地址
urlPathString图片url访问地址.jpg 需要指定域名地址
fileNameString文件上传后的文件名称UUID.type

6.2.5 编辑ImageVO

@Data
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
public class ImageVO {private String virtualPath; //图片虚拟路径 动态的路径private String urlPath;  //图片回显的URL地址private String fileName; //文件上传后的文件名称
}

6.2.6 编辑FileController

@RestController
@CrossOrigin
@RequestMapping("/file")
public class FileController {/*** 业务说明: 实现图片上传* URL: http://localhost:8091/file/upload* 类型: post* 参数: file 字节信息* 返回值: SysResult.success()* 扩展:*      一般情况下:*          一般前端向后端服务器发送字节信息.由外到内实现数据传输.*      采用输入流信息. InputStream file*          使用字节流的弊端: 1.必须手动关闭, 2.代码操作繁琐*          底层代码的实现.*       SpringMVC高级API  MultipartFile 专门处理IO流操作*  文件上传步骤:*        1.获取文件名称*        2.准备文件上传的目录*        3.判断目录是否存在  存在目录: 实现上传  没有目录:创建目录*        4.利用工具API方法,实现文件上传.*  注意事项: MultipartFile 默认支持1M的数据*/@PostMapping("/upload")public SysResult upload(MultipartFile file) throws IOException {//1.获取文件名称String fileName = file.getOriginalFilename();//2.准备磁盘地址String dirPath = "E:/project3/images/";//3.将这个文件目录 封装为File对象File dirFile = new File(dirPath);//4.判断对象是否存在if(!dirFile.exists()){//如果文件目录不存在,则创建目录dirFile.mkdirs(); //表示多级目录上传.}//5.封装文件全路径 E:xxx/xxx/a.jpgString path = dirPath + fileName;File allFile = new File(path);//6.实现文件上传 将IO流按照指定的对象格式进行输出.file.transferTo(allFile);return SysResult.success();}
}

上面只是我们的一个文件上传的demo,通过此demo先简单的实现文件的上传,下面,我们将更深入的了解文件上传的业务,将其更加完善。

6.3 正则表达式(复习)

6.3.1 正则表达式说明

正则表达式,又称规则表达式。(英语:Regular Expression,在代码中常简写为regex、regexp或RE),计算机科学的一个概念。正则表达式通常被用来检索、替换那些符合某个模式(规则)的文本。
总结: 正则表达式就是一种特殊格式的字符串.校验文本信息的.

6.3.2 匹配不确定次数

6.3.3 匹配固定次数

6.3.4 匹配取值区间

6.3.5 分组匹配

6.3.6 正则案例练习

  1. 要求匹配电话号码 11位 开头都是1
    正则表达式: 1[3-9][0-9]{9}

  2. 要求匹配邮箱 xxxx@qq
    正则表达式:

^[a-zA-Z0-9-_]+@[a-zA-Z0-9-_]+\.[a-zA-Z0-9-_]+$

上面的正则表达式中,允许匹配a到z,A到Z,0到9以及减号与下划线几个字符,后续使用加号继续拼接括号外的@字符,以及相同的字符串规则,最后拼接的.字符需要使用\字符进行转义后拼接后续接着跟相同的字符串规则,最后以$符结尾表明当前正则表达式结束。

6.4 文件上传实现

6.4.1 编辑FileController

@RestController
@CrossOrigin
@RequestMapping("/file")
public class FileController {@Autowiredprivate FileService fileService;/*** 业务说明: 实现图片上传* URL: http://localhost:8091/file/upload* 类型: post* 参数: MultipartFile file 字节信息* 返回值: SysResult.success()* 问题思考:*      1.完成图片类型校验 jpg|png|gif....*      2.防止恶意程序    a.exe.jpg*      3.将图片分目录存储*             3.1.按照类型分   理论可以但是得多分配几个*             3.2.按照时间划分. yyyy/MM/dd*      4.自定义文件名称. 利用UUID充当图片名称.*/@PostMapping("/upload")public SysResult upload(MultipartFile file) throws IOException {ImageVO imageVO = fileService.upload(file);if(imageVO == null){return SysResult.fail();}return SysResult.success(imageVO);}/*** 业务说明: 实现图片上传* URL: http://localhost:8091/file/upload* 类型: post* 参数:  file 字节信息* 返回值: SysResult.success()* 扩展:*      一般情况下:*          一般前端向后端服务器发送字节信息.由外到内实现数据传输.*      采用输入流信息. InputStream file*          使用字节流的弊端: 1.必须手动关闭, 2.代码操作繁琐*          底层代码的实现.*       SpringMVC高级API  MultipartFile 专门处理IO流操作*  文件上传步骤:*        1.获取文件名称*        2.准备文件上传的目录*        3.判断目录是否存在  存在目录: 实现上传  没有目录:创建目录*        4.利用工具API方法,实现文件上传.*  注意事项: MultipartFile 默认支持1M的数据*//*@PostMapping("/upload")public SysResult upload(MultipartFile file) throws IOException {//1.获取文件名称String fileName = file.getOriginalFilename();//2.准备磁盘地址String dirPath = "E:/project3/images/";//3.将这个文件目录 封装为File对象File dirFile = new File(dirPath);//4.判断对象是否存在if(!dirFile.exists()){//如果文件目录不存在,则创建目录dirFile.mkdirs(); //表示多级目录上传.}//5.封装文件全路径 E:xxx/xxx/a.jpgString path = dirPath + fileName;File allFile = new File(path);//6.实现文件上传 将IO流按照指定的对象格式进行输出.file.transferTo(allFile);return SysResult.success();}*/
}

6.4.2 编辑FileService

package com.jt.service;import com.jt.vo.ImageVO;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID;@Service
public class FileServiceImpl implements FileService{private String localDirPath = "D:/project3/images";//1.校验图片类型   xxx.jpg   校验后缀是否为jpg@Overridepublic ImageVO upload(MultipartFile file) {//1.1 获取文件名称  abc.jpgString fileName = file.getOriginalFilename();//1.2 全部转化为小写字母fileName = fileName.toLowerCase();//1.3正则校验是否为图片类型if(!fileName.matches("^.+\\.(jpg|png|gif)$")){//图片类型 不匹配  程序应该终止return null;}//2.校验是否为恶意程序 怎么判断就是一张图 高度和宽度//2.1 通过图片对象进行处理try {BufferedImage bufferedImage = ImageIO.read(file.getInputStream());int height = bufferedImage.getHeight();int width = bufferedImage.getWidth();if(height == 0 || width == 0){return null;}//3.将图片分目录存储 yyyy/MM/ddString dateDir = new SimpleDateFormat("/yyyy/MM/dd/").format(new Date());String dateDirPath = localDirPath + dateDir;File dirFile = new File(dateDirPath);if(!dirFile.exists()){dirFile.mkdirs();}//4.防止文件重名  动态生成UUID.类型//4.1 动态生成UUIDString uuid = UUID.randomUUID().toString().replace("-","");//4.2 获取图片类型        abc.jpg    .jpgString fileType = fileName.substring(fileName.lastIndexOf("."));// uuid.jpgString newFileName = uuid + fileType;//5.实现文件上传 1.准备全文件路径  2. 封装对象实现上传String path = dateDirPath + newFileName;file.transferTo(new File(path));//6.实现imageVO数据的返回//6.1准备虚拟路径String virtualPath = dateDir + newFileName;//6.2准备URL地址String url = ".jpg";return new ImageVO(virtualPath,url,newFileName);} catch (IOException e) {e.printStackTrace();return null;}}@Overridepublic void deleteFile(String virtualPath) {String filePath = localDirPath + virtualPath;File file = new File(filePath);if(file.exists()){ //如果文件存在,则删除数据file.delete();}}
}

这里我们通过正则表达式进行校验文件类型,摒弃复杂if-else结构来提高代码质量,在下方的代码中:

        //1.3正则校验是否为图片类型if(!fileName.matches("^.+\\.(jpg|png|gif)$")){//图片类型 不匹配  程序应该终止return null;}

我们首先使用一个.来表明任意字符,然后使用+号拼接后续的字符.,java的规则中,\转义字符也是一种特殊的字符,也需要转义,所以这里需要使用俩个\\进行字符.的转义,接着后续拼接文件名格式最后以$结束;

并且为了防止文件重名,我们使用UUID进行生成文件名对文件进行重命名;

为了保存文件,我们创建了全路径(磁盘地址信息+新文件的名称)最终实现文件上传的操作。

完成后点击文件上传即可在我们设置目录中查看到我们上传的图片。

6.5 文件删除操作

6.5.1 文件删除JS

6.5.2 文件删除业务接口

  • 请求路径: http://localhost:8091/file/deleteFile
  • 请求类型: delete
  • 请求参数:
参数名称参数说明备注
virtualPath文件上传的虚拟的路径删除时需要磁盘路径一起删除
  • 返回值结果:
参数名称参数说明备注
status状态信息200表示服务器请求成功 201表示服务器异常
msg服务器返回的提示信息可以为null
data服务器返回的业务数据可以为null

6.5.3 编辑FileController

 /*** 业务说明: 文件删除操作* URL地址:   http://localhost:8091/file/deleteFile* 请求类型:   delete* 参数:      virtualPath 虚拟路径* 返回值:    SysResult对象*/@DeleteMapping("/deleteFile")public SysResult deleteFile(String virtualPath){fileService.deleteFile(virtualPath);return SysResult.success();}

6.5.4 编辑FileService

@Overridepublic void deleteFile(String virtualPath) {String filePath = localDirPath + virtualPath;File file = new File(filePath);if(file.exists()){ //如果文件存在,则删除数据file.delete();}}

6.6 图片路径封装

6.6.1 路径分析

  1. 图片网络地址: .jpg
    协议://域名:80/虚拟地址
  2. 图片地址封装: :80/2021/11/11/uuid.jpg.

6.6.2 页面URL地址封装

package com.jt.service;import com.jt.vo.ImageVO;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID;@Service
public class FileServiceImpl implements FileService{private String localDirPath = "D:/project3/images";private String preUrl = "";//1.校验图片类型   xxx.jpg   校验后缀是否为jpg@Overridepublic ImageVO upload(MultipartFile file) {//1.1 获取文件名称  abc.jpgString fileName = file.getOriginalFilename();//1.2 全部转化为小写字母fileName = fileName.toLowerCase();//1.3正则校验是否为图片类型if(!fileName.matches("^.+\\.(jpg|png|gif)$")){//图片类型 不匹配  程序应该终止return null;}//2.校验是否为恶意程序 怎么判断就是一张图 高度和宽度//2.1 通过图片对象进行处理try {BufferedImage bufferedImage = ImageIO.read(file.getInputStream());int height = bufferedImage.getHeight();int width = bufferedImage.getWidth();if(height == 0 || width == 0){return null;}//3.将图片分目录存储 yyyy/MM/ddString dateDir = new SimpleDateFormat("/yyyy/MM/dd/").format(new Date());String dateDirPath = localDirPath + dateDir;File dirFile = new File(dateDirPath);if(!dirFile.exists()){dirFile.mkdirs();}//4.防止文件重名  动态生成UUID.类型//4.1 动态生成UUIDString uuid = UUID.randomUUID().toString().replace("-","");//4.2 获取图片类型        abc.jpg    .jpgString fileType = fileName.substring(fileName.lastIndexOf("."));// uuid.jpgString newFileName = uuid + fileType;//5.实现文件上传 1.准备全文件路径  2. 封装对象实现上传String path = dateDirPath + newFileName;file.transferTo(new File(path));//6.实现imageVO数据的返回//6.1准备虚拟路径String virtualPath = dateDir + newFileName;//6.2准备URL地址String url =  preUrl + virtualPath;System.out.println(url);return new ImageVO(virtualPath,url,newFileName);} catch (IOException e) {e.printStackTrace();return null;}}@Overridepublic void deleteFile(String virtualPath) {String filePath = localDirPath + virtualPath;File file = new File(filePath);if(file.exists()){ //如果文件存在,则删除数据file.delete();}}
}

因为我们这里并没有实际申请此链接地址,并且没有进行地址映射,所以实际并不可用;

所以我们在代码中将访问地址打印到了控制台:

我们将此地址复制到浏览器中,并将前缀改为我们本地的地址:

此时便能正常访问上传图片文件的预览。

6.7动态为属性赋值

6.7.1 业务需求

说明: 如果将属性写死到java类中,后期维护时 导致维护不方便.
优化: 可以通过@value注解动态赋值.

6.7.2 编辑properties配置文件

这里要注意,不要在类中添加空格等字符,一定要严格按照格式书写,此配置文件对文件格式要求较高。

6.7.3 属性动态赋值

这里涉及到一个问题,我们在点击图片链接进行预览时涉及到网络地址与物理地址进行映射的问题。我们能否直接实现通过网络地址访问到本地物理地址的内容呢?这就涉及到咱们下一节需要学习的知识Nginx。

7.拓展案例-实现商品列表中的修改功能

在item.vue中,我们定义了修改和删除的按钮:

但是对于其他内容我们并没有完成,例如点击修改后的跳转页面:

这里就需要我们自行完成前端及后端的代码来实现此功能,已完成的代码可以在码云中下载进行查看。

要实现点击修改后跳转到修改页面,我们首先需要实现弹窗功能,在element ui的官网,我们可以找到相关的组件,使用其提供的组件进行开发:

前端页面画好后,我们完成后端代码创建,进行案例的完成。

6.2.1 编辑页面

//1. 定义页面html
<!-- 定义商品修改的对话框 --><!-- 定义商品修改的对话框 --><el-dialog title="商品修改" :visible.sync="updateDialogVisible" width="60%"><!-- 准备修改的表单--><el-form :model="updateItem" :rules="rules" ref="ruleForm" label-width="100px" class="demo-ruleForm"><el-form-item label="标题信息" prop="title"><el-input v-model="updateItem.title"></el-input></el-form-item><el-form-item label="卖点信息" prop="sellPoint"><el-input v-model="updateItem.sellPoint"></el-input></el-form-item><el-form-item label="价格信息" prop="price"><el-input v-model="updateItem.price"></el-input></el-form-item><el-form-item label="数量信息" prop="num"><el-input v-model="updateItem.num"></el-input></el-form-item></el-form><span slot="footer" class="dialog-footer"><el-button @click="updateDialogVisible = false">取 消</el-button><el-button type="primary" @click="dialogVisible = false">确 定</el-button></span></el-dialog>//2. 定义页面JSupdateDialogVisible: false,updateItem: {},//准备一个校验规则rules: {title: [{ required: true, message: '请输入商品标题信息', trigger: 'blur' },{ min: 3, max: 50, message: '长度在 3 到 50 个字符', trigger: 'blur' }],sellPoint: [{ required: true, message: '请输入商品卖点信息', trigger: 'blur' },{ min: 3, max: 50, message: '长度在 3 到 50 个字符', trigger: 'blur' }],price: [{ required: true, message: '请输入商品价格信息', trigger: 'blur' },{ min: 3, max: 50, message: '长度在 3 到 50 个字符', trigger: 'blur' }],num: [{ required: true, message: '请输入商品数量信息', trigger: 'blur' },{ min: 3, max: 50, message: '长度在 3 到 50 个字符', trigger: 'blur' }],}//定义JS按钮updateItemBtn(item){console.log("扩展案例,自己实现 只需要修改 标题/卖点/价格/数量")this.updateDialogVisible = truethis.updateItem = itemthis.updateItem.price = (this.updateItem.price / 100).toFixed(2)}

更多推荐

2.9.7 商品模块功能实现

本文发布于:2024-02-27 01:57:05,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1704774.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:模块   功能   商品

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!