Step03day16学习笔记

目录

1. 商品管理实现

1.1 表设计

1.2.Item POJO对象设计

1.3 商品列表展现

1.3.1 需求说明

1. 3.2 页面JS分析

 1.3.3 业务接口文档实现

1.3.4 编辑ItemController

1.3.5 编辑ItemServiceImpl

1.3.6 页面效果展现

2. 商品管理业务

2.1 VUE 过滤器用法

2.2 商品新增操作

2.2.1 页面跳转

2.2.2 商品新增业务说明(一)

2.2.3 页面JS分析

2.2.4编辑ItemVO对象

2.2.5商品新增的业务接口

2.3 商品详情说明

2.3.1 引入富文本编辑器

2.3.2 Item和ItemDesc关系

2.3.4 编辑ItemController

2.3.5 编辑ItemServiceImpl

2.3.6 BUG说明

2.4文件上传操作

2.4.1 官网API

2.4.2 文件上传的业务接口文档

 2.4.3 封装ImageVO对象

2.4.4 文件上传入门案例

2.4.5 正则表达式

2.4.6 编辑FileController

2.4.7 编辑FileServiceImpl(部分)


前言:这是第三阶段第16天的学习笔记,主要内容为实现商品管理功能,包括商品列表展示,商品新增,商品详情查看和文件上传实现。

1. 商品管理实现

1.1 表设计

1.item表设计 商品表(商品的基本信息)

 2.item_desc 表设计 商品详情表 item_desc 专门保存大字段(html/url/图片网址…)

 设计要求: item表与itemDesc表应该一对一. item.id = itemDesc.id

1.2.Item POJO对象设计


@TableName("item")
@Data
@Accessors(chain = true)
public class Item extends BasePojo{
    @TableId(type = IdType.AUTO)
    private Integer id;         //商品Id号
    private String title;       //商品标题信息
    private String sellPoint;   //卖点信息
    private Integer price;      //商品价格 精度问题!!! 将小数扩大100倍 页面中将数据缩小100倍
    private Integer num;        //商品数量
    private String images;       //商品图片 1.jpg,2.jpg,3.jpg
    private Integer itemCatId;  //商品分类ID号
    private Boolean status;     //状态信息    0 下架 1 上架

}

1.3 商品列表展现

1.3.1 需求说明

要求: 当用户打开商品列表页面时,应该采用分页的方式 展现商品信息.

1. 3.2 页面JS分析

 1.3.3 业务接口文档实现

  • 请求路径: /item/getItemList?query=&pageNum=1&pageSize=10
  • 请求类型: get
  • 请求参数: 使用pageResult对象接收

1.3.4 编辑ItemController

@RestController
@CrossOrigin
@RequestMapping("/item")
public class ItemController {

    @Autowired
    private ItemService itemService;

    /**
     * 业务逻辑: 查询商品分页
     * URL: /item/getItemList?query=&pageNum=1&pageSize=10
     * 类型: GET
     * 接收参数: PageResult对象
     * 返回值: SysResult
     */
    @GetMapping("/getItemList")
    public SysResult getItemList(PageResult pageResult){//3个参数

        //返回5个参数,total/rows分页记录
        pageResult = itemService.getItemList(pageResult);
        return SysResult.success(pageResult);
    }

}

1.3.5 编辑ItemServiceImpl

@Service
public class ItemServiceImpl implements ItemService{

    @Autowired
    private ItemMapper itemMapper;

    /**
     * 实现商品的分页操作
     * @param pageResult
     * @return
     */
    @Override
    public PageResult getItemList(PageResult pageResult) {
        //参数1:页数  参数2: 条数
        Page<Item> page = new Page<>(pageResult.getPageNum(),pageResult.getPageSize());
        QueryWrapper queryWrapper = new QueryWrapper();
        //如果参数有值true 如果query参数没有值 false
        boolean flag = StringUtils.hasLength(pageResult.getQuery());
        queryWrapper.like(flag,"title",pageResult.getQuery());
        //利用MP的分页API 实现查询
        page = itemMapper.selectPage(page,queryWrapper);
        long total = page.getTotal();
        List<Item> list = page.getRecords();
        //封装成功之后原来3个参数   封装之后变为5个 之后返回
        return pageResult.setTotal(total).setRows(list);
    }
}

1.3.6 页面效果展现

2. 商品管理业务

2.1 VUE 过滤器用法

/* 定义过滤器 */
/* Vue.filter("定义过滤器名称",function(参数){
   过滤器需要添加return
}) */
Vue.filter("priceFormat",function(price){
    //console.log(price)
    return (price / 100).toFixed(2)
})


2.2 商品新增操作

2.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'

//使用路由机制  通过children实现路由嵌套, redirect重定向
Vue.use(VueRouter)
const routes = [
  {path: '/', redirect: '/login'},
  {path: '/login', component: Login},
  {path: '/elementUI', component: ElementUI},
  //children组件的跳转 在home组件内部进行填充
  {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}
  ]}
]

2.2.2 商品新增业务说明(一)

商品分为(item/itemDesc), 目前只完成商品基本信息的提交即可.

2.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 实现商品数据提交 商品(item基本信息/商品详情信息)
        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")
      }

2.2.4编辑ItemVO对象

@Data
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
public class ItemVO {  //该对象封装商品所有的参数信息
    private Item item;
    private ItemDesc itemDesc;
    private ItemParam itemParam;
}

2.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>品牌:&nbsp;<a href=https://list.jd.com/list.html".......      "
		}
	}
  • 请求参数: 使用ItemVO对象接收

  • ImageVO参数详解:
  • Item对象

  • itemDesc 对象
         
  • 为了降低商品提交代码的耦合性,将大字段信息详情,采用ItemDesc对象进行封装

2.3 商品详情说明

2.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. 页面展现
 <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>

2.3.2 Item和ItemDesc关系


Item表: 主要封装了商品的基本信息.
ItemDesc表: 主要封装商品详情信息(大字段—html代码片段)
原因: 如果用户频繁的查询大字段 则影响效率. 所以将商品信息分为item和itemDesc
关联关系: item.id = itemDesc.id ID的值一致的.


2.3.4 编辑ItemController

  /**
     * 完成商品新增操作
     * 1.URL地址:  http://localhost:8091/item/saveItem
     * 2.参数: {item,itemDesc} 使用ItemVO进行接收
     * 3.请求类型: post    JSON
     * 4.返回值: SysResult对象
     */
    @PostMapping("/saveItem")
    public SysResult saveItem(@RequestBody ItemVO itemVO){

        itemService.saveItem(itemVO);
        return SysResult.success();
    }

2.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();
        item.setStatus(true);
        //2.商品入库操作  主键自增,入库之后才能看到主键. ID回显
        itemMapper.insert(item);

        //3.获取商品详情
        ItemDesc itemDesc = itemVO.getItemDesc();
        //如何保证item和ItemDesc的ID一致?
        itemDesc.setId(item.getId());
        itemDescMapper.insert(itemDesc);
    }

2.3.6 BUG说明

如果出现主键重复,会报入库异常.则需要提前删除多余测试数据

2.4文件上传操作

2.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>

2.4.2 文件上传的业务接口文档

  • 请求路径: http://localhost:8091/file/upload
  • 请求类型: post
  • 请求参数:

 2.4.3 封装ImageVO对象

@Data
@Accessors(chain = true)
public class ImageVO {
    private String virtualPath; //虚拟路径
    private String urlPath;     //网络地址
    private String fileName;    //图片名称
}

2.4.4 文件上传入门案例

@RestController
@CrossOrigin
@RequestMapping("/file")
public class FileController {

    /**
     * 业务: 文件上传入门案例
     * 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 IOException {
        //1.动态获取文件名称
        String fileName = file.getOriginalFilename();
        //2.准备文件目录  Linux系统不能识别\
        String dirPath = "F:/images";
        File dirFile = new File(dirPath);
        if(!dirFile.exists()){
            //应该创建一个新目录 创建多级目录
            dirFile.mkdirs();
        }
        //3.拼接文件路径
        String filePath = "F:/images/" + fileName;
        //4.实现文件上传
        file.transferTo(new File(filePath));
        System.out.println("实现文件上传");
        return SysResult.success();
    }


}

2.4.5 正则表达式


正则表达式,又称规则表达式。(英语: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.4.6 编辑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.success(imageVO);
    }

2.4.7 编辑FileServiceImpl(部分)

@Service
public class FileServiceImpl implements FileService{

    /**
     * 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.图片类型的校验  正则表达式  aaa.jpg
        String fileName = file.getOriginalFilename();
        //字符大小写 干扰正则的判断  将所有的文件转化为小写字母
        fileName = fileName.toLowerCase();
        //程序不满足正则, 则用户上传的图片有问题
        if(!fileName.matches("^.+\\.(jpg|png|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 localDir = "F:/images";
            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.lastIndexOf(".");
            //截取文件类型
            String fileType =  fileName.substring(index);
            //拼接新文件路径
            String realFileName = uuid + fileType;

            //作业!!!!:  目录/文件名 实现文件上传  前端暂时不管


        } catch (IOException e) {
            e.printStackTrace();
            return null;    //如果程序执行报错,则返回null
        }

        return null;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值