69-JT项目07-(商品/详情一对一操作//文件上传)

商品上架/下架

业务分析

当用户点击商品上架下架操作时,应该修改数据库中的状态信息status

分析:

  1. 下架操作 /item/instock status=2
  2. 上架操作 /item/reshelf status=1

restFul优化:

  1. 下架操作 /item/updateStatus/2
  2. 上架操作 /item/updateStatus/1

页面URL分析

1596590030625

页面JS

1596698691970

实现RestFul调用

重构页面url地址

1596698805250

编辑ItemController

//商品上下架操作,使用restful风格
@RequestMapping("/updateStatus/{status}")
public SysResult updateStatus(Long[] ids,@PathVariable("status") Integer status){
    itemService.updatStatus(ids,status);
    return  SysResult.success();
}

编辑ItemService

//利用手写sql实现状态修改
@Override
public void updatStatus(Long[] ids, Integer status) {
    itemMapper.updown(ids,status);

}

//利用MP实现状态修改(上下架操作)
//	@Override
//	public void updatStatus(Long[] ids, Integer status) {
//		List<Long> idsList = Arrays.asList(ids);//数组转化为集合
//		UpdateWrapper<Item> updateWrapper = new UpdateWrapper<>();
//
//		updateWrapper.in("id",idsList);
//		Item item = new Item();
//		item.setStatus(status);
//		itemMapper.update(item,updateWrapper);
//
//	}

编辑Mapper接口/xml映射文件

1596699019669

<update id="updown">
    update  tb_item set status =#{status},updated=now()
    where id in(
    <foreach collection="ids" item="id" separator=",">
        #{id}
    </foreach>
    )
</update>

富文本编辑器

富文本编辑器介绍

​ KindEditor是一套开源的HTML可视化编辑器,主要用于让用户在网站上获得所见即所得编辑效果,兼容IE、Firefox、Chrome、Safari、Opera等主流浏览器。

富文本编辑器入门案例

<%@ page language="java" contentType="text/html; charset=utf-8"
    pageEncoding="utf-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<link href="/js/kindeditor-4.1.10/themes/default/default.css" type="text/css" rel="stylesheet">
<script type="text/javascript" charset="utf-8" src="/js/kindeditor-4.1.10/kindeditor-all-min.js"></script>
<script type="text/javascript" charset="utf-8" src="/js/kindeditor-4.1.10/lang/zh_CN.js"></script>
<script type="text/javascript" charset="utf-8" src="/js/jquery-easyui-1.4.1/jquery.min.js"></script>

<script type="text/javascript">
	$(function(){
	    //在指定的为止初始化富文本.
		KindEditor.ready(function(){
			KindEditor.create("#editor")
		})
	})
</script>
</head>
<body>
<h1>富文本编辑器</h1>

<textarea style="width:700px;height:350px" id="editor"></textarea>
</body>
</html>

商品表/商品详情表关系

说明:由于用户查询商品时,首先查询的是商品的主要信息.如果用户对某个商品感兴趣,才会查询商品详情信息.所以采用2张表的形式 展现商品/商品详情.
思考: 商品信息由商品/详情2部分构成.所有CRUD操作应该同时操作商品详情表.应该实现关联的操作.

1596699239639

编辑ItemDesc POJO对象

package com.jt.pojo;
/**
 * 构建商品详情的POJO对象
 *
 */
@TableName("tb_item_desc")
@Data
@Accessors(chain = true)
public class ItemDesc extends BasePojo{

    @TableId                //只标识主键即可
    private Long itemId;    //要求与商品表Id保持一致.
    private String itemDesc;    //商品详情信息
}


编辑ItemDescMapper

package com.jt.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.jt.pojo.ItemDesc;
//继承接口并且要指定对应的表
public interface ItemDescMapper extends BaseMapper<ItemDesc> {//对象与表进行绑定
    //如果自己不写sql,则可以省略mapper映射文件的编写
}

重构后台商品CRUD操作

重构商品入库操作

页面参数提交

说明:在原有的商品新增的基础之上,完成商品详情新增操作.

1596699383588

编辑ItemController

动态接受参数信息 Item/ItemDesc.完成入库修改删除操作

//需要完成两张表的入库操作
@RequestMapping("/save")
public SysResult saveItem(Item item, ItemDesc itemDesc){
    itemService.saveItem(item,itemDesc);
    return  SysResult.success();
}

编辑ItemService

//保存商品 完成两张表的入库操作,保证商品的id与商品详情id一致
//注意事项: 完成数据库数据更新操作时,需要注意数据库事务问题
@Transactional
@Override
public void saveItem(Item item , ItemDesc itemDesc) {
    //保存信息时要记录更改时间
    //		item.setStatus(1).setCreated(new Date()).setUpdated(item.getCreated());
    item.setStatus(1);
    //典型一对一的插入
    itemMapper.insert(item);
    //分析问题: item表的 主键是自增数据库入库之后才会有 主键生成.
    //解决方案:让数据库完成入库之后自动的实现主键的回显。改操作由MP机制动态完成
    //<insert id="xxx" keyProperty="id" useGeneratedKeys="true"></insert>
    itemDesc.setItemId(item.getId());
    itemDescMapper.insert(itemDesc);

}

实现商品详情信息回显

页面url分析

说明:检查商品详情展现的url地址路径.

1596699780262

页面JS分析

1596699871372

编辑ItemController

说明:根据restFul风格实现商品详情查询.

/**
* 	根据商品id查询商品详情信息
http://localhost:8091/item/query/item/desc/1474391987
参数:  包含在url中,利用restFul方式动态获取
* 返回值:  SysResult对象
*
*/
@RequestMapping("/query/item/desc/{itemId}")
public SysResult findItemDescById(@PathVariable("itemId") Long itemId){
ItemDesc itemDesc= itemService.findItemDescById(itemId);
//将服务器数据返回页面
return  SysResult.success(itemDesc);
}

编辑ItemService

//根据指定的ID查询商品详情信息
@Override
public ItemDesc findItemDescById(Long itemId) {
    ItemDesc itemDesc = itemDescMapper.selectById(itemId);
    return itemDesc;
}

重构商品修改以及删除操作

编辑ItemController

//商品修改操作
/**
	 * 完成商品信息修改
	 * url:http://localhost:8091/item/update
	 * 参数: 整个商品表单
	 * 返回值: SysResult对象
	 */
@RequestMapping("/update")
public SysResult updateItem(Item item, ItemDesc itemDesc){
    itemService.updateItem(item,itemDesc);
    return   SysResult.success();
}
//删除商品信息 批量删除 (单个删除)
//springmvc知识点: 可以根据指定的类型动态的实现参数类型的转化 规则: 如果字符串使用","分割,则可以使用数组的形式传参
/**
	 * 业务需求: 完成商品删除操作
	 * url请求地址: /item/delete
	 * 参数: ids=  id1,id2 串
	 * 返回值结果:  SysResult对象
	 * SpringMVC知识点: 可以根据制定的类型动态的实现参数类型的转化.
	 * 					如果字符串使用","号分隔,则可以使用数组的方式接参.
	 */
@RequestMapping("/delete")
public SysResult deleteItems(Long[] ids){
    itemService.deleteItemsByIds(ids);
    return   SysResult.success();
}

编辑ItemService

//修改商品信息
@Transactional
@Override
public void updateItem(Item item , ItemDesc itemDesc) {
    item.setStatus(1);
    //		item.setStatus(1).setCreated(new Date()).setUpdated(item.getCreated());
    itemMapper.updateById(item);//设定主键
    itemDesc.setItemId(item.getId());
    //实现商品详情的更新
    itemDescMapper.updateById(itemDesc);
}
//删除商品信息
@Transactional
@Override
public void deleteItemsByIds(Long[] ids) {
    //将数组转化为集合
    //		List<Long> idsList = Arrays.asList(ids);
    //		itemMapper.deleteBatchIds(idsList);
    itemMapper.deleteItems(ids);
    //根据商品id删除商品的详情信息
    itemDescMapper.deleteItemDescByIds(ids);
}

编辑ItemDescMapper接口

package com.jt.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.jt.pojo.ItemDesc;
//继承接口并且要指定对应的表
public interface ItemDescMapper extends BaseMapper<ItemDesc> {//对象与表进行绑定
    //如果自己不写sql,则可以省略mapper映射文件的编写
    void deleteItemDescByIds(Long[] ids);

}

编辑ItemDescMapper.xml文件

<delete id="deleteItemDescByIds">
    delete  from tb_item_desc
    <where>
        item_id in
        <foreach collection="array" item="id" open="(" close=")" separator=",">
            #{id}
        </foreach>
    </where>
</delete>

商品详情展示

1596700454135

文件上传

文件上传入门案例

编辑HTML页面

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
	<h1>实现文件长传</h1>
	<!--enctype="开启多媒体标签"  -->
	<form action="http://localhost:8091/file" method="post" 
	enctype="multipart/form-data">
		<input name="fileImage" type="file" />
		<input type="submit" value="提交"/>
	</form>
</body>
</html>

编辑FileController

package com.jt.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;

@RestController
public class FileController {

    /**
     * 完成文件上传的入门案例
     * url地址: http://localhost:8091/file
     * 请求参数: fileImage
     * 返回值:   文件上传成功
     *
     * 利用SpringMVC中提供的工具API,实现文件上传的简化.
     * 记住类型:MultipartFile
     * 实现步骤:
     *      1.接收资源文件
     *      2.准备文件上传目录
     *      3.准备文件上传的全路径   目录/文件名称
     常识: 
	 * 		1.必须指定文件上传的路径信息   D:\JT-SOFT\images\文件名称.jpg
	 * 		2.将字节信息利用outPutStream进行输出操作
	 * 
	 * 说明:文件上传默认大小1M=1024*1024   
	 * 具体参见CommonsFileUploadSupport类
     */
    @RequestMapping("/file")
    public String  file(MultipartFile fileImage){

       //2.文件文件上传的目录
        String fileDirPath = "D:/JT-SOFT/images";
        File dirFile = new File(fileDirPath);
        //判断文件目录是否存在
        if(!dirFile.exists()){
            //如果文化间目录没有,则应该新建目录
            dirFile.mkdirs(); //创建多级目录
        }

        //3.准备文件上传的全路径.  路径+文件名称
        String fileName = fileImage.getOriginalFilename();  //文件名称.后缀   123.jgp
        File realFile = new File(fileDirPath+"/"+fileName);
        //将字节信息输出到文件中.
        try {
            fileImage.transferTo(realFile); //实现文件上传
            return "文件上传成功!!!";
        } catch (IOException e) {
            e.printStackTrace();
            return "文件上传失败!!!";
        }

    }
}

商品文件上传实现

富文本编辑器返回值说明

{“error”:0,“url”:“图片的保存路径”,“width”:图片的宽度,“height”:图片的高度}
属性1: error 如果在文件上传的过程中出现问题 则标识为1 ,如果没有错误 标识为0.
属性2: url 代表图片的虚拟访问地址. 磁盘地址
属性3: width/height 获取图片的宽高 可以省略.

封装文件上传的返回值VO-ImageVO

package com.jt.vo;

@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class ImageVO implements Serializable {

    private static final long serialVersionUID = -5302337766946721739L;
    private  Integer error;//确认图片是否有误 0 正常  1 错误
    private String url;  //图片访问的虚拟地址
    private  Integer width; //宽度
    private  Integer height; //高度

    //通过工具API
    public static ImageVO fail(){
        return  new ImageVO(1,null,null,null);
    }
    public static ImageVO success(String url){
        return  new ImageVO(0,url,null,null);
    }
    public static ImageVO success(String url,Integer width,Integer height){
        return  new ImageVO(0,url,width,height);
    }

}

文件上传的参数说明

请求的url分析

1596701033980

文件上传JS属性配置:

1596701054469

编辑FileController

说明: 文件上传时,需要注意富文本编辑器中传递的url参数.

	/**
	 * 业务分析:实现文件上传.
	 * 1.url地址: http://localhost:8091/pic/upload
	 * 2.参数:   uploadFile  文件上传对象
	 * 3.返回值:  ImageVO对象
	 */
	@RequestMapping("/pic/upload")
	public ImageVO uploadFile(MultipartFile uploadFile) {
		//直接交给业务层处理	
		return fileService.uploadFile(uploadFile);
	}

编辑FileService

package com.jt.service;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;

@Service
public class FileServiceImpl implements FileService {
    //创建根目录
    private String localDirPath ="D:/JT-SOFT/images";
    private  static Set<String> imageTypeSet = new HashSet<>();
    static {
        imageTypeSet.add(".jpg");
        imageTypeSet.add(".png");
        imageTypeSet.add(".gif");
        //.....可以动态的添加
    }

    /**
     *
     * 1.校验文件有效性. jpg/. png|. gif......
     * 2.校验文件是否为恶意程序 (木马. exe).jpg
     * 3.提高用户检索图片的效率  分目录存储.
     * 4.为了防止重名图片的提交自定义文件名称.
     * 5.实现图片的物理上传 本地磁盘中.
     * 6.准备一个访问图片的虚拟路径
     *  @param uploadFile
     * @return
     */

    @Override
    public ImageVO upload(MultipartFile uploadFile) {
        //1 校验图片类型 1) 利用正则表达式进行校验 2)指定一个集合进行校验
        //1.1获取文件名称
        String fileName= uploadFile.getOriginalFilename();
        fileName = fileName.toLowerCase();
        //1.2获取文件名称后缀,然后将获取的后缀转为小写
        String fileType = fileName.substring(fileName.lastIndexOf("."));

        //进行校验
        if (!imageTypeSet.contains(fileType)){
            //如果类型不匹配,应该告诉用户上传图片有误
            //
            return ImageVO.fail();
        }
        //.校验文件是否为恶意程序 (木马. exe).jpg 方法:通过图片的特有属性进行校验
        try {
           //2.1  将上传的图片利用图片API进行转化,如果不能成功转化则一定不是图片
            BufferedImage bufferedImage = ImageIO.read(uploadFile.getInputStream());
            //校验图片的特有属性 宽度和高度
            int width = bufferedImage.getWidth();
            int height = bufferedImage.getHeight();
            if (width==0 || height == 0){
                //如果获取的宽或者高为0 则不是图片
                return  ImageVO.fail();
            }

        } catch (IOException e) {
            e.printStackTrace();
            return  ImageVO.fail();
        }
        //实现分目录存储 方案1 利用hash 之后每隔2-3位进行
        //方案2 通过时间来划分目录
        //获取格式化时间 利用工具API
        String datePath = new SimpleDateFormat("/yyyy/MM/dd/").format(new Date());
        //创建文件目录 2部分 根目录+时间目录
        String localDir = localDirPath+datePath;
        File dirFile = new File(localDir);
        if (!dirFile.exists()){
            dirFile.mkdirs();
        }
        //4防止文件重名,需要自定义名称 UUID
        //4.1 生成uuid
        String uuid = UUID.randomUUID().toString().replace("-","");
        //4.2 动态生成文件名称
        String uuidName = uuid+fileType;
        //5 实现文件上传 准备文件全路径
        String realFilePath = localDir+uuidName;
        //5.1 封装文件真实对象
        File imageFile = new File(realFilePath);
        //5.2 实现文件上传
        try {
            uploadFile.transferTo(imageFile);

        } catch (IOException e) {
            e.printStackTrace();
            return  ImageVO.fail();
        }
        //6.暂时使用虚拟路径
        String url ="https://s1.ax1x.com/2020/08/02/atSA6U.png";
        return ImageVO.success(url);
    }
}


posted on 2020-08-06 16:35  liqiangbk  阅读(290)  评论(0编辑  收藏  举报

导航