第4章 店铺注册功能模块

学习目标

  • 连接数据库
  • Mybatis数据表映射关系的配置
  • dao -> service -> controller层代码的编写,Junit的使用
  • Session,图片处理工具Thumbnailator的使用
  • suimobile前端设计与开发

Dao层

创建店铺

  • 创建ShopDao 接口
package com.imooc.o2o.dao;

import com.imooc.o2o.entity.Shop;

public interface ShopDao {
    // 新增店铺 返回1成功 返回-1失败
    int insertShop(Shop shop);
}
  • 创建ShopDao.xml映射文件



    
    
    
      INSERT INTO
      tb_shop(owner_id, area_id, shop_category_id,shop_name,
      shop_desc, shop_addr, phone, shop_img, priority, create_time,
      last_edit_time, enable_status, advice)
      VALUES
      (#{owner.userId}, #{area.areaId}, #{shopCategory.shopCategoryId},
      #{shopName}, #{shopDesc}, #{shopAddr}, #{phone},
      #{shopImg}, #{priority}, #{createTime},
       #{lastEditTime}, #{enableStatus}, #{advice})
    

  • 测试,新建ShopDaoTest
    测试前,由于要用到两个外键,所有先在数据表中先添加两条数据


    第4章 店铺注册功能模块_第1张图片
    image.png
package com.imooc.o2o.dao;

import com.imooc.o2o.BaseTest;
import com.imooc.o2o.entity.Area;
import com.imooc.o2o.entity.PersonInfo;
import com.imooc.o2o.entity.Shop;
import com.imooc.o2o.entity.ShopCategory;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.Date;

import static org.junit.Assert.assertEquals;

public class ShopDaoTest extends BaseTest {
    @Autowired
    private ShopDao shopDao;

    @Test
    public void testInsertShop() {
        Shop shop = new Shop();

        PersonInfo owner = new PersonInfo();
        Area area = new Area();
        ShopCategory shopCategory = new ShopCategory();

        owner.setUserId(1L);
        area.setAreaId(2);
        shopCategory.setShopCategoryId(1L);

        shop.setOwner(owner);
        shop.setArea(area);
        shop.setShopCategory(shopCategory);
        shop.setShopName("测试的店铺");
        shop.setShopDesc("test");
        shop.setPhone("test");
        shop.setShopImg("test");
        shop.setCreateTime(new Date());
        shop.setEnableStatus(1);
        shop.setAdvice("审核中");
        int effectedNum = shopDao.insertShop(shop);
        assertEquals(1, effectedNum);
    }
}
  • 测试结果


    第4章 店铺注册功能模块_第2张图片
    image.png

    image.png

更新店铺

  • 在ShopDao接口中,新增一个更新商铺的方法
    // 更新店铺信息
    int updateShop(Shop shop);
  • 在mapper/ShopDao.xml文件里添加相应的映射
    
    
    
        UPDATE tb_shop
        
            shop_name=#{shopName},
            shop_desc=#{shopDesc},
            shop_addr=#{shopAddr},
            phone=#{phone},
            shop_img=#{shopImg},
            priority=#{priority},
            last_edit_time=#{lastEditTime},
            enable_status=#{enableStatus},
            advice=#{advice},
            area_id=#{area.areaId},
            shop_category_id=#{shopCategory.shopCategoryId}
        
        WHERE shop_id = #{shopId}
    
  • 添加一个update的测试方法
    @Test
    public void testUpdateShop() {
        Shop shop = new Shop();
        shop.setShopId(1L);

        PersonInfo owner = new PersonInfo();
        Area area = new Area();
        ShopCategory shopCategory = new ShopCategory();

        owner.setUserId(5L);
        area.setAreaId(2);
        shopCategory.setShopCategoryId(1L);

        shop.setOwner(owner);
        shop.setArea(area);
        shop.setShopCategory(shopCategory);
        shop.setShopName("测试的店铺");

//        shop.setShopDesc("test");
//        shop.setPhone("test");
//        shop.setShopImg("test");

        shop.setShopDesc("测试");
        shop.setPhone("测试");
        shop.setShopImg("测试");

        shop.setCreateTime(new Date());
        shop.setEnableStatus(1);
        shop.setAdvice("审核中");
        int effectedNum = shopDao.updateShop(shop);
        assertEquals(1, effectedNum);
    }
  • 测试结果


    image.png

    image.png

Thumbnailator图片处理和封装Util

可以看看这个博客
https://blog.csdn.net/zmx729618/article/details/78729049

  • 在http://mvnrepository.com/中下载
  • 点击相应的版本


    第4章 店铺注册功能模块_第3张图片
    image.png
  • 添加到项目的pox.xml文件中
    
    
    
      net.coobird
      thumbnailator
      0.4.8
    

这样就成功引入底层的架包

  • 新建一个com.imooc.o2o.util包
  • 在utli包中新建imageUtil类
package com.imooc.o2o.util;

import net.coobird.thumbnailator.Thumbnails;
import org.springframework.web.multipart.commons.CommonsMultipartFile;

import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Random;

import static com.imooc.o2o.util.PathUtil.getImgBasePath;

public class ImageUtil {
    private static String basePath = Thread.currentThread().getContextClassLoader().getResource("").getPath();
    private static final SimpleDateFormat sDateFormate = new SimpleDateFormat("yyyyMMddHHmmss");
    private static final Random random = new Random();
    /**
     * 创建缩略图
     */
    public static String generateThumbnail(CommonsMultipartFile thumbnail, String targetAddr) {
        // 获取图片的随机图片名
        String realFileName = getRandomFileName();
        // 获取图片的拓展名
        String extension = getFileExtension(thumbnail);
        makeDirPath(targetAddr);
        String relativeAddr = targetAddr + realFileName + extension;
        File dest = new File(getImgBasePath() + relativeAddr);
        try {
            Thumbnails.of(thumbnail.getInputStream()).size(200, 200).outputQuality(0.25f).toFile(dest);
        } catch (IOException e) {
            throw new RuntimeException("创建缩略图失败:" + e.toString());
        }
        return relativeAddr;
    }

    /**
     * 创建路径  /home/work/cuzz/xx.jpg
     * 那么 home work xiangze 这三个文件都自动创建
     * @param targetAddr
     */
    private static void makeDirPath(String targetAddr) {
        String realFileParentPath = PathUtil.getImgBasePath() + targetAddr;
        File dirPath = new File(realFileParentPath);
        // 如果路径不存在就递归的创建
        if (!dirPath.exists()) {
            dirPath.mkdirs();
        }
    }

    /**
     * 获取输入文件流的拓展名
     * @param thumbnail
     * @return
     */
    private static String getFileExtension(CommonsMultipartFile cFile) {
        // 获取原来的文件名
        String originalFileName = cFile.getOriginalFilename();
        return originalFileName.substring(originalFileName.lastIndexOf("."));
    }

    /**
     * 生成随机文件名,当前年月日时分秒 + 5位随机数
     * @return
     */
    private static String getRandomFileName() {
        // 获取随机的5位数
        int rannum = random.nextInt(89999) + 10000;
        String nowTimestr = sDateFormate.format(new Date());
        return nowTimestr + rannum;
    }
}
  • 创建一个路径PathUtil类,获取存放图片的路径
package com.imooc.o2o.util;

public class PathUtil {
    // 获取系统分隔符
    private static String separator = System.getProperty("file.separator");

    public static String getImgBasePath() {
        // 根据不同的系统去选择根路径
        String os = System.getProperty("os.name");
        String basePath = "";
        if (os.toLowerCase().startsWith("win")) {
            basePath = "F/project/image";
        } else {
            basePath =  "home/cuzz/image";
        }
        basePath = basePath.replace("/", separator);
        return basePath;
    }

    public static String getShopImagePath(long shopId) {
        String imagePath = "/upload/item/shop" + shopId + "/";
        return imagePath.replace("/", separator);
    }
}

Dto之ShopExecution的实现

数据传输对象(DTO)(Data Transfer Object),是一种设计模式之间传输数据的软件应用系统。数据传输目标往往是数据访问对象从数据库中检索数据。数据传输对象与数据交互对象或数据访问对象之间的差异是一个以不具有任何行为除了存储和检索的数据(访问和存取器)

  • ShopExecution
package com.imooc.o2o.dto;

import com.imooc.o2o.enmus.ShopStateEnum;
import com.imooc.o2o.entity.Shop;

import java.util.List;

public class ShopExecution {
    // 结果状态
    private int state;

    // 状态标识
    private String stateInfo;
    
    // 店铺数量
    private int count;

    // 返回的shop列表
    private List shopList;

    // 操作的shop(增删改查的时候用到)
    private Shop shop;
    public int getState() {
        return state;
    }

    public void setState(int state) {
        this.state = state;
    }

    public String getStateInfo() {
        return stateInfo;
    }

    public void setStateInfo(String stateInfo) {
        this.stateInfo = stateInfo;
    }

    public int getCount() {
        return count;
    }

    public void setCount(int count) {
        this.count = count;
    }

    public List getShopList() {
        return shopList;
    }

    public void setShopList(List shopList) {
        this.shopList = shopList;
    }

    public Shop getShop() {
        return shop;
    }

    public void setShop(Shop shop) {
        this.shop = shop;
    }

    // 无参构造器
    public ShopExecution() {

    }

    // 操作失败有参构造器
    public ShopExecution(ShopStateEnum stateEnum) {
        this.state = stateEnum.getState();
        this.stateInfo = stateEnum.getStateInfo();
    }

    // 成功的有参构造器
    public ShopExecution(ShopStateEnum stateEnum, Shop shop) {
        this.state = stateEnum.getState();
        this.stateInfo = stateEnum.getStateInfo();
        this.shop = shop;
    }

    // 成功的有参构造器2
    public ShopExecution(ShopStateEnum stateEnum, List shopList) {
        this.state = stateEnum.getState();
        this.stateInfo = stateEnum.getStateInfo();
        this.shopList = shopList;
    }
}
  • 同时我们需要一个枚举类型
package com.imooc.o2o.enmus;

public enum ShopStateEnum {
    CHECK(0,"审核中"),
    OFFLINE(-1, "非法商铺"),
    SUCCESS(1, "操作成功"),
    PASS(2, "通过认证");

    private int state;
    private String stateInfo;

    ShopStateEnum(int state, String stateInfo) {
       this.state = state;
       this.stateInfo = stateInfo;
    }


    public int getState() {
        return state;
    }

    public String getStateInfo() {
        return stateInfo;
    }


    /**
     * 依据传入的state返回相应的enum值
     */

    public static ShopStateEnum stateOf(int state) {
        for (ShopStateEnum stateEnum : values()) {
            if (stateEnum.getState() == state) {
                return stateEnum;
            }
        }
        return null;
    }
}

店铺注册之service层

  • 新建shopService接口
package com.imooc.o2o.service;


import com.imooc.o2o.dto.ShopExecution;
import com.imooc.o2o.entity.Shop;
import org.springframework.web.multipart.commons.CommonsMultipartFile;


public interface ShopService {
    ShopExecution addShop(Shop shop, CommonsMultipartFile shopImg);
}
  • 先封装一些RuntimeExpection
    这样的好处是知道错误类型,出错的就知道是店铺相关的错误
package com.imooc.o2o.exceptiopns;

public class ShopOperationException extends RuntimeException {
    public ShopOperationException(String msg) {
        super(msg);
    }
}
  • 实现类
package com.imooc.o2o.service.impl;

import com.imooc.o2o.dao.ShopDao;
import com.imooc.o2o.dto.ShopExecution;
import com.imooc.o2o.enmus.ShopStateEnum;
import com.imooc.o2o.entity.Shop;
import com.imooc.o2o.exceptiopns.ShopOperationException;
import com.imooc.o2o.service.ShopService;
import com.imooc.o2o.util.ImageUtil;
import com.imooc.o2o.util.PathUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.commons.CommonsMultipartFile;

import java.util.Date;

@Service
public class ShopServiceImpl implements ShopService{
    @Autowired
    private ShopDao shopDao;

    @Override
    // 事务的支持
    @Transactional
    public ShopExecution addShop(Shop shop, CommonsMultipartFile shopImg) {
        // 空值判断
        if (shop == null) {
            return new ShopExecution(ShopStateEnum.NULL_SHOP);
        }

        try {
            // 给店铺信息赋值初始值
            shop.setEnableStatus(0);
            shop.setCreateTime(new Date());
            shop.setLastEditTime(new Date());
            // 添加店铺信息
            int effectedNum = shopDao.insertShop(shop);

            // 判断是否插入成功
            if (effectedNum <= 0) {
                throw new ShopOperationException("店铺创建失败");
            } else {
                if (shopImg != null) {
                    // 存储图片
                    try {
                        addShopImg(shop, shopImg);
                    } catch (Exception e) {
                        throw new ShopOperationException("addShopImg error" + e.getMessage());
                    }
                    // 更新图片信息
                    effectedNum = shopDao.updateShop(shop);
                    if (effectedNum <= 0) {
                        throw new ShopOperationException("更新图片地址失败");
                    }

                }
            }

        } catch (Exception e) {
            throw new ShopOperationException("addShop error" + e.getMessage());
        }


        return new ShopExecution(ShopStateEnum.CHECK, shop);
    }

    private void addShopImg(Shop shop, CommonsMultipartFile  shopImg) {
        // 获取shop图片目录的相对值路径
        String dest = PathUtil.getShopImagePath(shop.getShopId());
        String shopImgAddr = ImageUtil.generateThumbnail(shopImg, dest);
        shop.setShopImg(shopImgAddr);
    }
}

Controller层

  • 新建一个ShopAdminController类
package com.imooc.o2o.web.shopadmin;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
@RequestMapping(value = "shop", method = RequestMethod.GET)
public class ShopAdminController {
    @RequestMapping(value="/shopoperation")
    public String shopOperation() {
        return "shop/shopedit";
    }
}
  • 效果图


    第4章 店铺注册功能模块_第4张图片
    image.png
  • 添加shopoperation.js文件
$(function () {
   // 获取初始url
   var initUrl = "/project2/shop/getshopinitinfo";
   var registerShopUrl = "/project2/shop/registershop";
   alert(initUrl);
   getShopInitInfo();


   function getShopInitInfo(){
      // 获取初始值
      $.getJSON(initUrl, function (data) {
          alert("SSSSSSSS");
         if (data.success) {
            var tempHtml = "";
            var tempAreaHtml = "";
            data.shopCategoryList.map(function (item, index) {
               tempHtml += "";
            });
            
            data.areaList.map(function (item, index) {
                tempAreaHtml +=  "";
            });
            $("#shop-category").html(tempHtml);
            $("#area").html(tempAreaHtml);
         }
      });
   }
      // 提交
      $("#submit").click(function () {
         var shop = {};
         shop.shopName = $("#shop-name").val();
         shop.shopAddr = $("#shop-addr").val();
         shop.phone = $("#phone").val();
         shop.shopDesc = $("#shop-desc").val();
         shop.shopCategory = {
            shopCategoryId:$("#shop-category").find("option").not(function () {
                return !this.selected;
            }).data("id")
         };
         shop.area = {
            areaId: $("#area").find("option").not(function () {
               return !this.selected;
            }).data("id")
         };
         var shopImg = $("#shop-img")[0].file[0];
         var formData = new FormData();
         formData.append("shopImg", shopImg);
         formData.append("shopStr", JSON.stringify(shop));
         var verifyCodeActual = $("#j_captcha").val();
         // 判断是否为空
         if (!verifyCodeActual) {
             $.toast("请输入验证码!");
             return;
         }
         formData.append("verifyCodeActual", verifyCodeActual);
         // 提交到后台
          $.ajax({
              url: registerShopUrl,
              type: "POST",
              data: formData,
              contentType: false,
              processDate: false,
              cache: false,
              success: function (data) {
                 if (data.success){
                     $.toast("提交成功!");
                 } else {
                     $.toast("提交失败" + data.errMsg);
                 }
                 // 不管没有提交成功 都更换验证码
                 $("#captcha_img").click();
              }

          });
      });
});
  • 新建一个管理类,通过json数据
package com.imooc.o2o.web.shopadmin;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.imooc.o2o.dto.ShopExecution;
import com.imooc.o2o.enmus.ShopStateEnum;
import com.imooc.o2o.entity.Area;
import com.imooc.o2o.entity.PersonInfo;
import com.imooc.o2o.entity.Shop;
import com.imooc.o2o.entity.ShopCategory;
import com.imooc.o2o.service.AreaService;
import com.imooc.o2o.service.ShopCategoryService;
import com.imooc.o2o.service.ShopService;
import com.imooc.o2o.util.HttpServletRequestUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.commons.CommonsMultipartFile;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Controller
@RequestMapping("/shop")
public class ShopManagementController {
    @Autowired
    private ShopService shopService;

    @Autowired
    private ShopCategoryService shopCategoryService;

    @Autowired
    private AreaService areaService;

    @RequestMapping(value = "/getshopinitinfo", method = RequestMethod.GET)
    @ResponseBody
    private Map getShopInitInfo() {
        Map modelMap = new HashMap<>();
        List shopCategoryList = new ArrayList<>();
        List areaList = new ArrayList<>();
        try{
            shopCategoryList = shopCategoryService.getShopCategoryList(new ShopCategory());
            areaList = areaService.getAreaList();
            modelMap.put("shopCategoryList", shopCategoryList);
            modelMap.put("areaList", areaList);
            modelMap.put("success", true);
        } catch (Exception e) {
            modelMap.put("success", false);
            modelMap.put("errMsg", e.getMessage());
        }
        return modelMap;
    }

    @RequestMapping(value = "/registershop", method = RequestMethod.POST)
    @ResponseBody
    private Map registerShop(HttpServletRequest request) {
        Map modelMap = new HashMap<>();
        // 1. 接受并转化相应的参数, 包括店铺信息以及图片信息
        String shopstr = HttpServletRequestUtil.getString(request, "shopStr");
        ObjectMapper mapper = new ObjectMapper();
        Shop shop = null;
        try{
            shop = mapper.readValue(shopstr, Shop.class);
        } catch (Exception e) {
            modelMap.put("success", false);
            modelMap.put("errMsg", e.getMessage());
            return modelMap;
        }
        CommonsMultipartFile shopImg = null;
        CommonsMultipartResolver commonsMultipartResolver = new CommonsMultipartResolver(request.getSession().getServletContext());
        if (commonsMultipartResolver.isMultipart(request)) {
            MultipartHttpServletRequest multipartHttpServletRequest = (MultipartHttpServletRequest) request;
            shopImg = (CommonsMultipartFile) multipartHttpServletRequest.getFile("shopImg");
        } else {
            modelMap.put("success", false);
            modelMap.put("errMsh", "上传图片不能为空");
            return modelMap;
        }
        // 2. 注册店铺
        if (shop != null && shopImg != null) {
            PersonInfo owner = new PersonInfo();
            owner.setUserId(1L);
            shop.setOwner(owner);
            ShopExecution shopExecution = null;
            try {
                shopExecution = shopService.addShop(shop, shopImg.getInputStream(), shopImg.getOriginalFilename());
            } catch (IOException e) {
                e.printStackTrace();
            }
            if (shopExecution.getState() == ShopStateEnum.CHECK.getState()) {
                modelMap.put("success", true);
            } else {
                modelMap.put("success", false);
            }
            return modelMap;
        } else {
            modelMap.put("success", false);
            modelMap.put("errMsh", "请输入店铺信息");
            return modelMap;
        }
        // 3. 返回结果
    }
}
  • 获取到结果


    第4章 店铺注册功能模块_第5张图片
    image.png

验证码工具包

  • 拷贝依赖文件到pom.xml中
    http://mvnrepository.com/artifact/com.github.penggle/kaptcha/2.3.2
  • 在web.xml中,定义样式
    
        
        Kaptcha
        com.google.code.kaptcha.servlet.KaptchaServlet

        
        
            kaptcha.border
            no
        
        
        
            kaptcha.textproducer.font.color
            red
        
        
        
            kaptcha.image.width
            135
        
        
        
            kaptcha.textproducer.char.string
            ACDEFHKPRSTWX345679
        
        
        
            kaptcha.image.height
            50
        
        
        
            kaptcha.textproducer.font.size
            43
        
        
        
            kaptcha.noise.color
            black
        
        
        
            kaptcha.textproducer.char.length
            4
        
        
        
            kaptcha.textproducer.font.names
            Arial
        
    
    
    
        Kaptcha
        /Kaptcha
    
  • 点击更换
  • function changeVerifyCode(img) { img.src = "../Kaptcha?" + Math.floor(Math.random() * 100); }
    • 把验证码传入Json
             var verifyCodeActual = $("#j_captcha").val();
             // 判断是否为空
             if (!verifyCodeActual) {
                 $.toast("请输入验证码!");
                 return;
             }
             formData.append("verifyCodeActual", verifyCodeActual);
    
    • 就有了验证码


      第4章 店铺注册功能模块_第6张图片
      image.png
    • 验证码util
    package com.imooc.o2o.util;
    
    import javax.servlet.http.HttpServletRequest;
    
    public class CodeUtil {
        public static boolean checkVerifyCode(HttpServletRequest request) {
            String verifyCodeExpected = (String) request.getSession().getAttribute(
                    com.google.code.kaptcha.Constants.KAPTCHA_SESSION_KEY);
            String verifyCodeActual = HttpServletRequestUtil.getString(request,
                    "verifyCodeActual");
            if (verifyCodeActual == null
                    || !verifyCodeActual.equalsIgnoreCase(verifyCodeExpected)) {
                return false;
            }
            return true;
        }
    }
    
    • 测试


      第4章 店铺注册功能模块_第7张图片
      image.png

      image.png

      可以看出数据库中插入了数据

    你可能感兴趣的:(第4章 店铺注册功能模块)