项目:校园商铺系统开发日志(三)

店铺操作

Dao

思路:
自底向上编码,dao层先写ShopDao的接口,里面定义了若干的方法,包括店铺的增改查 ,其中查询包括按照id精确查询和分页批量查询。
注意:

  1. 增改方法中,正常来说是没有返回值的,但是因为搭配了mybatis,用一个int作为返回值,可以在操作失败时通过返回一个-1的值进行判断,从而作出相应的处理。
  2. mapper文件中的insert用到了,useGeneratedKeys=“true”,代表使用jdbc的getGeneratedKeys获取数据库的自增主键值,然后传入给实体类的id。这样的好处一是可以简化代码,二是在考虑到店铺的图片存储时,每个店铺都应该在各自独有的目录下存储图片,而目录的区分就使用店铺的主键进行区分。而且,在以后不需要图片功能时,可以将这个值设置为false,增加了系统的灵活性。
  3. mapper文件中的insert用到了,动态sql,好处是一条语句可以代替多条不同条件的update语句。用法是在<if
    test = “xxx != null” xxx(sql) = #{xxx}> 多个条件中间注意逗号连接。

shopDao接口:

package com.xh.o2o.dao;

import com.xh.o2o.entity.Shop;
import org.apache.ibatis.annotations.Param;

import java.util.List;

public interface ShopDao {
    /**
     * 新增店铺
     *
     * @param shop
     * @return 1代表成功
     */
    int insertShop(Shop shop);

    /**
     * 更改店铺信息
     *
     * @param shop
     * @return
     */
    int updateShop(Shop shop);

    /**
     * 查询店铺
     *
     * @param shopId
     * @return
     */
    Shop queryByShopId(long shopId);

    /**
     * 分页查询店铺,可输入的条件有:店铺名(模糊),店铺状态,店铺类别,区域Id,owner
     *
     * @param shopCondition 这里的param中的参数要与mapper中的sql中的字段对应上
     * @param rowIndex      从第几行开始读取数据
     * @param pageSize      读取多少行
     * @return
     */
    List<Shop> queryShopList(@Param("shopCondition") Shop shopCondition, @Param("rowIndex") int rowIndex, @Param("pageSize") int pageSize);

    /**
     * 返回queryShopList总数
     *
     * @param shopCondition
     * @return
     */
    int queryShopCount(@Param("shopCondition") Shop shopCondition);
}

shopDao.xml:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xh.o2o.dao.ShopDao">
    <insert id="insertShop" keyColumn="shop_id" keyProperty="shopId" useGeneratedKeys="true">
        INSERT
        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})
    </insert>

    <update id="updateShop" parameterType="com.xh.o2o.entity.Shop">
        update tb_shop
        <set>
            <if test="shopName != null">shop_name=#{shopName},</if>
            <if test="shopDesc != null">shop_desc=#{shopDesc},</if>
            <if test="shopAddr != null">shop_addr=#{shopAddr},</if>
            <if test="phone != null">phone=#{phone},</if>
            <if test="shopImg != null">shop_img=#{shopImg},</if>
            <if test="priority != null">priority=#{priority},</if>
            <if test="lastEditTime != null">last_edit_time=#{lastEditTime},</if>
            <if test="enableStatus != null">enable_status=#{enableStatus},</if>
            <if test="advice != null">advice=#{advice},</if>
            <if test="area != null">area_id=#{area.areaId},</if>
            <if test="shopCategory != null">shop_category_id=#{shopCategory.shopCategoryId}</if>
        </set>
        where shop_id=#{shopId}
    </update>

    <resultMap type="com.xh.o2o.entity.Shop" id="shopMap">
        <id column="shop_id" property="shopId"/>
        <result property="shopName" column="shop_name"/>
        <result property="shopDesc" column="shop_desc"/>
        <result property="shopAddr" column="shop_addr"/>
        <result property="phone" column="phone"/>
        <result property="shopImg" column="shop_img"/>
        <result property="priority" column="priority"/>
        <result property="createTime" column="create_time"/>
        <result property="lastEditTime" column="last_edit_time"/>
        <result property="enableStatus" column="enable_status"/>
        <result property="advice" column="advice"/>

        <association property="area" column="area_id" javaType="com.xh.o2o.entity.Area">
            <id column="area_id" property="areaId"/>
            <result column="area_name" property="areaName"/>
        </association>
        <association property="shopCategory" column="shop_category_id" javaType="com.xh.o2o.entity.ShopCategory">
            <id column="shop_category_id" property="shopCategoryId"/>
            <result column="shop_category_name" property="shopCategoryName"/>
        </association>
        <association property="owner" column="owner_id" javaType="com.xh.o2o.entity.PersonInfo">
            <id column="user_id" property="userId"/>
            <result column="name" property="name"/>
        </association>
    </resultMap>
    <select id="queryByShopId" resultMap="shopMap" parameterType="Long">
        select
        s.shop_id,per.user_id,s.shop_name,s.shop_desc,s.shop_addr,s.phone,s.shop_img,s.priority,s.create_time,s.last_edit_time,
        s.enable_status,s.advice,a.area_id,a.area_name,sc.shop_category_id,sc.shop_category_name,per.name
        from tb_shop s,tb_area a,tb_shop_category sc,tb_person_info per
        where s.area_id=a.area_id and s.shop_category_id=sc.shop_category_id and s.owner_id = per.user_id and
        s.shop_id=#{shopId}
    </select>

    <select id="queryShopList" resultMap="shopMap">
        select
        s.shop_id,s.shop_name,s.shop_desc,s.shop_addr,s.phone,s.shop_img,s.priority,s.create_time,s.last_edit_time,
        s.enable_status,s.advice,a.area_id,a.area_name,sc.shop_category_id,sc.shop_category_name
        from tb_shop s,tb_area a,tb_shop_category sc
        <where>
            <if test="shopCondition.shopCategory != null and shopCondition.shopCategory.shopCategoryId != null">
                and s.shop_category_id = #{shopCondition.shopCategory.shopCategoryId}
            </if>
            <if test="shopCondition.shopCategory != null and
                    shopCondition.shopCategory.parent != null and
                    shopCondition.shopCategory.parent.shopCategoryId != null">
                and s.shop_category_id in (select shop_category_id from tb_shop_category
                where parent_id = #{shopCondition.shopCategory.parent.shopCategoryId})
            </if>
            <if test="shopCondition.area != null and shopCondition.area.areaId != null">
                and s.area_id = #{shopCondition.area.areaId}
            </if>
            <if test="shopCondition.shopName != null">
                and s.shop_name like '%${shopCondition.shopName}%'
            </if>
            <if test="shopCondition.enableStatus !=null">
                and s.enable_status = #{shopCondition.enable_status}
            </if>
            <if test="shopCondition.owner != null and shopCondition.owner.userId != null">
                and s.owner_id = #{shopCondition.owner.userId}
            </if>
            and s.area_id=a.area_id and s.shop_category_id=sc.shop_category_id
        </where>
        order by s.priority desc
        limit #{rowIndex},#{pageSize}
    </select>

    <select id="queryShopCount" resultType="int">
        select count(1)
        from tb_shop s,tb_area a,tb_shop_category sc
        <where>
            <if test="shopCondition.shopCategory != null and shopCondition.shopCategory.shopCategoryId != null">
                and s.shop_category_id = #{shopCondition.shopCategory.shopCategoryId}
            </if>
            <if test="shopCondition.shopCategory != null and
                    shopCondition.shopCategory.parent != null and
                    shopCondition.shopCategory.parent.shopCategoryId != null">
                and s.shop_category_id in (select shop_category_id from tb_shop_category
                where parent_id = #{shopCondition.shopCategory.parent.shopCategoryId})
            </if>
            <if test="shopCondition.area != null and shopCondition.area.areaId != null">
                and s.area_id = #{shopCondition.area.areaId}
            </if>
            <if test="shopCondition.shopName != null">
                and s.shop_name like '%${shopCondition.shopName}%'
            </if>
            <if test="shopCondition.enableStatus !=null">
                and s.enable_status = #{shopCondition.enable_status}
            </if>
            <if test="shopCondition.owner != null and shopCondition.owner.userId != null">
                and s.owner_id = #{shopCondition.owner.userId}
            </if>
            and s.area_id=a.area_id and s.shop_category_id=sc.shop_category_id
        </where>
    </select>
</mapper>

Util

使用Thumbnailator对图片进行处理。
编写ImgUtil对Thumbnailator进行封装。Thumbnails中首先会读取目标图片的绝对路径,指定输出图片大小。然后可以给输出图片添加水印,其中需要传入参数:水印位置,水印图片的路径以及透明度,然后设置图片的压缩比,最后输出新图片的位置。
generateThumbnail方法中需要传入输入流(图片)和存储路径。

try {
         Thumbnails.of(thumbnail.getImage()).size(200, 200)
                    .watermark(Positions.BOTTOM_RIGHT, ImageIO.read(new File(basePath + "/waterwechat.jpg")), 0.05f)
                    .outputQuality(0.5f).toFile(dest);
        } catch (IOException e) {
            e.printStackTrace();
        }
private static String basePath = "D:/java program/PublicImgFile";

    private static final SimpleDateFormat sDateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
    private static final Random r = new Random();

    public static String generateThumbnail(ImageHolder thumbnail, String targetAddr) {
        //获取文件随即名,以保证唯一性
        String realFileName = getRandomFileName();
        //获取文件扩展名,如jpg
        String extension = getFileExtension(thumbnail.getImageName());
        //创建目录
        makeDirPath(targetAddr);
        //组合相对路径
        String relativeAddr = targetAddr + realFileName + extension;
        File dest = new File(PathUtil.getImgBasePath() + relativeAddr);
        try {
            Thumbnails.of(thumbnail.getImage()).size(200, 200)
                    .watermark(Positions.BOTTOM_RIGHT, ImageIO.read(new File(basePath + "/waterwechat.jpg")), 0.05f)
                    .outputQuality(0.5f).toFile(dest);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return relativeAddr;
    }

    public static String generateNormalImg(ImageHolder thumbnail, String targetAddr) {
        String realFileName = getRandomFileName();
        String extension = getFileExtension(thumbnail.getImageName());
        makeDirPath(targetAddr);
        String relativeAddr = targetAddr + realFileName + extension;
        File dest = new File(PathUtil.getImgBasePath() + relativeAddr);
        try {
            Thumbnails.of(thumbnail.getImage()).size(250, 550)
                    .watermark(Positions.BOTTOM_RIGHT, ImageIO.read(new File(basePath + "/waterwechat.jpg")), 0.15f)
                    .outputQuality(0.5f).toFile(dest);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return relativeAddr;
    }

    /**
     * 创建目标路径所涉及的目录
     *
     * @param targetAddr
     */
    private static void makeDirPath(String targetAddr) {
        String realFileParentPath = PathUtil.getImgBasePath() + targetAddr;
        File dirPath = new File(realFileParentPath);
        if (!dirPath.exists()) {
            dirPath.mkdirs();
        }
    }

    /**
     * 获取输入文件流的扩展名字
     *
     * @param fileName
     * @return
     */

    private static String getFileExtension(String fileName) {
        return fileName.substring(fileName.lastIndexOf("."));
    }

    /**
     * 生成随机文件名,当前时间+5位随机数
     *
     * @return
     */
    public static String getRandomFileName() {
        int ranNum = r.nextInt(89999) + 10000;
        String nowTimeStr = sDateFormat.format(new Date());
        return nowTimeStr + ranNum;
    }

    /**
     * 判断storePath是文件路径还是目录路径,
     * 如果是文件路径,则删除该文件,
     * 如果是目录路径,则删除该目录下所有文件
     *
     * @param storePath
     */
    public static void deleteFileOrPath(String storePath) {
        File fileOrPath = new File(PathUtil.getImgBasePath() + storePath);
        if (fileOrPath.exists()) {
            if (fileOrPath.isDirectory()) {
                File[] files = fileOrPath.listFiles();
                for (int i = 0; i < files.length; i++) {
                    files[i].delete();
                }
            }
            fileOrPath.delete();
        }

    }

上述过程中多次设计到对文件目录的处理,所以需要再定义一个类PathUtil对目录进行处理。这里分系统讨论。这是一个项目根路径以外的绝对文件路径(防止重新部署时被删除),以保存该项目中的所有图片。或者,可以将图片都保存在另外一台服务器上,通过URL引入,这样能提高系统的并发。因为不同系统目录的斜杠不同,所以也要在这里做统一替换,替换成对应系统的斜杠。
在图片总目录路径定义好之后,还需要根据id分别设置不同的子目录,以存储不同id下的图片。

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 = "D:/java program/PublicImgFile";
        } else {
            //其他操作系统路径
            //...
        }
        basePath = basePath.replace("/", separator);
        return basePath;
    }

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

Dto

引入dto的目的是在对店铺进行操作时,无论成功还是失败都会有一个状态,这些需要记录而且还要返回给控制层处理。同时还封装了List。里面包含了2个构造函数分别是操作成功和失败。两者需要传入一个共同的参数ShopStateEnum,这里主要是保存了每种状态码与状态信息的映射关系。

Service

Service层主要是处理业务逻辑,包含对dao层的方法调用,事务和日志的管理,对于异常的处理,根据操作状态返回相应的信息和一些属性的初始赋值。
addShop逻辑:
首先进行空值判断,第二步给店铺设置初始值(店铺创建时间和店铺状态),第三步调用dao层的inserShop方法,根据int返回值分别执行操作成功和失败。第四步存储图片,因为上一步中已经创建好店铺了,但是这个shop里面是没有图片的,所以要不传进来的输入流和shop(主要为了获取id好创建目录)作为参数去创建图片以及目录,即调用上面提到的generateThumbnail(),然后把图片的绝对路径作为参数赋值给shop属性。但注意,此时已经执行完insertShop操作,而且shop属性也已经赋值图片路径,但是数据库里图片的这一项还是空值,所以还要执行一次update(shop)操作,以更新数据库中的图片。最后用dto类型返回,包含shop和和此次操作的状态。

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

    @Override
    @Transactional
    public ShopExecution addShop(Shop shop, ImageHolder thumbnail) throws ShopOperationException {
        //空值判断
        if (shop == null) {
            return new ShopExecution(ShopStateEnum.NULL_SHOP);
        }
        try {
            //给店铺信息赋值初始值
            shop.setEnableStatus(1);
            shop.setCreateTime(new Date());
            shop.setLastEditTime(new Date());
            //添加店铺信息
            int effectedNum = shopDao.insertShop(shop);
            if (effectedNum <= 0) {
                throw new ShopOperationException("店铺创建失败");
            } else {
                if (thumbnail.getImage() != null) {
                    //存储图片
                    try {
                        addShopImg(shop, thumbnail);
                    } 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);
    }

    @Override
    @Transactional
    public ShopExecution modifyShop(Shop shop, ImageHolder thumbnail) throws ShopOperationException {
        //1.判断是否需要修改图片
        //2.更新店铺信息
        if (shop == null || shop.getShopId() == null) {
            return new ShopExecution(ShopStateEnum.NULL_SHOP);
        } else {
            shop.setLastEditTime(new Date());
            if (thumbnail.getImage() != null && thumbnail.getImageName() != null && "".equals(thumbnail.getImageName())) {
                Shop tempShop = shopDao.queryByShopId(shop.getShopId());
                if (tempShop.getShopImg() != null) {
                    ImageUtil.deleteFileOrPath(tempShop.getShopImg());
                }
                addShopImg(shop, thumbnail);
            }
            try {
                int effectedNum = shopDao.updateShop(shop);
                if (effectedNum <= 0) {
                    return new ShopExecution(ShopStateEnum.INNER_ERROR);
                } else {
                    shop = shopDao.queryByShopId(shop.getShopId());
                    return new ShopExecution(ShopStateEnum.SUCCESS, shop);
                }
            } catch (Exception e) {
                throw new ShopOperationException("modifyShop error: " + e.getMessage());
            }

        }
    }

    @Override
    public Shop getByShopId(long shopId) {
        return shopDao.queryByShopId(shopId);
    }

    @Override
    public ShopExecution getShopList(Shop shopCondition, int pageIndex, int pageSize) {
        int rowIndex = PageCalculator.calculateRowIndex(pageIndex, pageSize);
        List<Shop> shopList = shopDao.queryShopList(shopCondition, rowIndex, pageSize);
        int count = shopDao.queryShopCount(shopCondition);
        ShopExecution se = new ShopExecution();
        if (shopList != null) {
            se.setShopList(shopList);
            se.setCount(count);
        } else {
            se.setState(ShopStateEnum.INNER_ERROR.getState());
        }
        return se;
    }

    private void addShopImg(Shop shop, ImageHolder thumbnail) {
        //需要获取shop图片目录的相对路径
        String dest = PathUtil.getShopImgPath(shop.getShopId());
        String shopImgAddr = ImageUtil.generateThumbnail(thumbnail, dest);
        shop.setShopImg(shopImgAddr);
    }
}

Controller

在店铺注册的controller层主要思路如下:

  1. 接受并转换相应的参数,包括店铺信息和图片信息
  2. 注册店铺
  3. 返回结果

在接受这一步中,因为会将前端的请求参数传进方法中,然后通过需要获取request里的key,并将它转换为后台的实体类属性。这里需要再单独写一个类专门处理转换。request.getParameter(key)返回的是String类型,我们需要再处理成我们所需的类型。

public class HttpServletRequestUtil {
    public static int getInt(HttpServletRequest request, String key) {
        try {
            return Integer.decode(request.getParameter(key));
        } catch (Exception e) {
            return -1;
        }
    }

    public static String getString(HttpServletRequest request, String key) {
        try {
            String result = request.getParameter(key);
            if (result != null)
                result = result.trim();
            if ("".equals(result))
                result = null;
            return result;
        } catch (Exception e) {
            return null;
        }
    }
}

转换这一步需要json,引入jackson依赖。里面的关键语法就是ObjectMapper mapper = new ObjectMapper();它提供一些功能将转换成Java对象匹配JSON结构。通过mapper.readValue将前端传来的json数据源转换为我们实体类型。

		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;
        }

所以第一步的商店信息从前台表单传到后台的流程是:首先用户在前端页面填写表单信息之后,页面相应的js文件中会创建一个shop对象,并根据表单信息给shop对象赋上属性值。然后将这个shop转换成json格式,以ajax方式,根据url传给后台指定的controller。后台通过request.getgetParameter(key),这个key就是json的key,从而获取到前端的shop(是一个字符串),strShop是一个json格式的字符串,里面包含了shop的属性信息,比如{“shopName”:“���”,“shopAddr”:“���”,“phone”:“123123”,“shopDesc”:“���”,“shopCategory”:{“shopCategoryId”:11},“area”:{“areaId”:8}}。然后通过jackson的ObjectMapper 的方法,可以读取json字符串,并根据传入的类型参数,将这个json字符串转化为对应的Shop类型。至此,前端传shop到后端完成。

		var shop = {};
        shop.shopName = $('#shop-name').val();
        shop.shopAddr = $('#shop-addr').val();
        shop.phone = $('#shop-phone').val();
        shop.shopDesc = $('#shop-desc').val();
        var shopImg = $('#shop-img')[0].files[0];
        var formData = new FormData();
        formData.append('shopImg', shopImg);
        formData.append('shopStr', JSON.stringify(shop));
         $.ajax({
            url: (isEdit ? editShopUrl : registerShopUrl),
            type: 'POST',
            data: formData,
            contentType: false,
            processData: false,
            cache: false,
            success: function (data) {
                if (data.success) {
                    $.toast("提交成功!");
                } else {
                    $.toast("提交失败!" + data.errMsg);
                }
                $('#captcha_img').click()
            }
        });

难点:图片处理

shop信息获取到,但是还没有获取到图片信息,这里使用Spring自带的CommonsMultipartFile接受图片,CommonsMultipartResolver(文件上传解析器)去解析request里的文件信息。创建CommonsMultipartResolver里需要传入参数,也就是当前request的会话的上下文信息。CommonsMultipartResolver主要目的是判断图片是否上传?若上传图片之后,将request强制转换为MultipartHttpServletRequest表示要对文件进行处理,调用getFile方法即可获取前端的文件。至此,第一步全部完成。

		CommonsMultipartFile shopImg = null;
        CommonsMultipartResolver commonsMultipartResolver = new CommonsMultipartResolver(request.getSession().getServletContext());
        if (commonsMultipartResolver.isMultipart(request)) {
            MultipartHttpServletRequest multipartHttpServletRequest = (MultipartHttpServletRequest) request;
            shopImg = (CommonsMultipartFile) multipartHttpServletRequest.getFile("shopImg");

第二步注册店铺,店家肯定是先登陆再新增店铺。所以通过session获取店家id,在这个id在注册店铺。而且店铺信息里还有店家属性。给shop绑定personinfo之后,就可以调用service层的addShop方法。里面传入shop和图片。
这里需要注意:
因为之前是用multipartHttpServletRequest.getFile(“shopImg”)获取到图片,是一个File类型的(为啥用File,便于service层的单元测试),但是我们是使用Spring的CommonsMultipartFile接受这个File,CommonsMultipartFile并不能强制转换为File,所以在addShop参数里并不能直接把shopImg传进去,会报错。查看CommonsMultipartFile源码发现,里面有getInputStream(),里面返回的是InputStream,所以得到提示,将InputStream转换为File。

    private static void inputStreamToFile(InputStream ins, File file){
        FileOutputStream os = null;
        try{
            os = new FileOutputStream(file);
            int bytesRead = 0;
            byte[] buffer = new byte[1024];
            while ((bytesRead = ins.read(buffer))!=-1){
                os.write(buffer,0,bytesRead);
            }
        }catch (Exception e){
            throw new RuntimeException(e.getMessage());
        }finally {
            try{
                if(os != null){
                    os.close();
                }
                if(ins != null){
                    ins.close();
                }
            }catch (IOException e){
                throw new RuntimeException(e.getMessage());
            }
        }
    }

controller层注册店铺方法中调用:

	File shopImgFile = new File(PathUtil.getImgBasePath()+"随意名字");
    inputStreamToFile(shopImg.getInputStream(),shopImgFile);
    shopService.addShop(shop,shopImgFile);

进一步优化:
这样虽然解决了CommonsMultipartFile不能强转File的问题,但是每次在传图片的时候都要执行一次输入流转File的方法而且还要生成一个File文件,这样不仅会产生垃圾文件,也可能造成写入失败,系统不稳定。所以,当初因为方便测试编写了addShop方法里传入的File,需要进行修改。
修改ShopService以及ShopServiceImpl:

 ShopExecution addShop(Shop shop, InputStream shopImgInputStream, String fileName) throws ShopOperationException;

输入流是没办法获取文件名字的,所以也要传入文件名。为什么要获取文件名字?因为接下来要用到之前写的generateThumbnail方法,这里需要文件名来获取文件的扩展名(.jpg)。然后重新修改Service层的方法。
*在ShopServiceImpl中,在修改完参数之后,因为我们需要将图片存储本地指定路径中,所以调用了addShopImg(),这里面我们通过shopId获取了图片的相对路径,然后进一步调用了generateThumbnail()进行图片处理(图片大小,水印以及路径)。返回的是文件地址,然后作为属性赋值给shop对象。 所以,generateThumbnail()也要修改。Thumbnails.of()可以接收多种类型。

public static String generateThumbnail(InputSream inputSream, String fileName, String targetAddr) {
        //获取文件随即名,以保证唯一性
        String realFileName = getRandomFileName();
        //获取文件扩展名,如jpg
        String extension = getFileExtension(fileName);
        //创建目录
        makeDirPath(targetAddr);
        //组合相对路径
        String relativeAddr = targetAddr + realFileName + extension;
        File dest = new File(PathUtil.getImgBasePath() + relativeAddr);
        try {
            Thumbnails.of(inputSream).size(200, 200)
                    .watermark(Positions.BOTTOM_RIGHT, ImageIO.read(new File(basePath + "/waterwechat.jpg")), 0.05f)
                    .outputQuality(0.5f).toFile(dest);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return relativeAddr;
    }

controller层注册店铺方法中调用:

ShopExecution se = shopService.addShop(shop,shopImg.getInputStream(),shopImg.getOriginalFileName);

这里还可以优化:。。。

问题:浏览器缓存

在注册店铺时候,有两项信息是先用数据库查询的,区域和店铺类别。这两项都是先从数据库中查询出返回一个List,然后再传给前台。

后把结果以map的形式返回给前台,成功返回(success,true),失败返回(success,false),这样便于前台的判断。有时在修改代码后,启动tomcat发现店铺类别和区域还是为空。最后通过chorme浏览器检查前端代码发现访问的url地址出现问题。再回到项目中,发现js和html中的url地址没问题。最后才发现,修改了的js和css文件在浏览器中无法显示更新的,还是没更新之前的。因为浏览器有js缓存,需要清空浏览器的js缓存再重新读取就能成功了。
方法有两个:
解决方法一:清空浏览器中的js缓存或者直接CTRL+F5
解决方法二:直接在js的url中添加版本号,程序中添加以下代码

 <script type="text/javascript"> document.write("<link rel='stylesheet' type='text/css' href='/css/layout.css?v="+new Date().getTime()+"'>"); </script>

修改查询思路与上述类似,在map返回给前台时还要返回所需的数据内容。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值