2018/7/12 23:00:13当前位置媒体热门新闻热点浏览文章

商品相关接口

先在controller,service下,公告和新建好ProductManageController和ProductService


项目结构

操作商品相关功可以都需要验证管理员权限,所以先注入UserService,需要使用到一个验证管理员身份方法,在UserService里增加

//校验能否是管理员    public ServerResponse checkAdminRole(User user){        if(user!=null && user.getRole()==Const.Role.ROLE_ADMIN){            return ServerResponse.createBySuccess();        }else {            return ServerResponse.createByError();        }   }

新添加和升级商品

在每一个操作商品的controller方法里,都是这样一个框架验证身份后执行service方法

        User user = (User)session.getAttribute(Const.CURRENT_USER);        if(user == null){            return ServerResponse.createByErrorCodeMessage(ResponseCode.NEED_LOGIN.getCode(),"使用户未登录,请登录管理员");        }        if(iUserService.checkAdminRole(user).isSuccess()){            //填充我们添加产品的业务逻辑        }else{            return ServerResponse.createByErrorMessage("无权限操作");        }

isSuccess判断成功后,直接return掉service层返回的结果
在service层里,把新添加和升级商品放在一个方法里,由于实际上这两个操作就一点不同而已,假如是升级商品那么我们传过来的product类里的id一定是非空的,而假如是新添加商品,是不会有id值的,所以以此为判断条件,来判断执行insert还是update

    public ServerResponse saveOrUpdateProduct(Product product){        if(product != null)        {            if(StringUtils.isNotBlank(product.getSubImages())){                String[] subImageArray = product.getSubImages().split(",");                if(subImageArray.length > 0){                    product.setMainImage(subImageArray[0]);                }            }            if(product.getId() != null){                int row= productMapper.updateByPrimaryKey(product);                if(row> 0){                    return ServerResponse.createBySuccess("升级产品成功");                }                return ServerResponse.createBySuccess("升级产品失败");            }else{                int row= productMapper.insert(product);                if(row> 0){                    return ServerResponse.createBySuccess("新添加产品成功");                }                return ServerResponse.createBySuccess("新添加产品失败");            }        }        return ServerResponse.createByErrorMessage("新添加或者升级产品参数不正确");    }

商品主图片采使用子图的第一张图片,所有使用字符串分割,把第一张图取出来放到MainImage

更改商品状态

商品上架或者者下架,只修改商品的状态码,逻辑较为简单,详见代码

商品细节

这里我们需要一个vo对象,value object,承载对象各个值的对象,来组装业务对象,由于pojo对象不肯定可以满足我们的业务需求。
在项目下新建vo包,在vo下新建ProductDetailVo


private Integer  id;private Integer categoryId;private String name;private String subtitle;private String mainImage;private String subImages;private String detail;private BigDecimal price;private Integer stock;private Integer status;private String createTime;private String updateTime;private String imageHost;private Integer parentCategoryId;//忽略get(),set()

能看到我们组装了两个属性,一个是图片服务器前缀,一个是父分类id
新添加一个方法组装productDetail

    private ProductDetailVo assembleProductDetailVo(Product product){        ProductDetailVo productDetailVo = new ProductDetailVo();        productDetailVo.setId(product.getId());        productDetailVo.setSubtitle(product.getSubtitle());        productDetailVo.setPrice(product.getPrice());        productDetailVo.setMainImage(product.getMainImage());        productDetailVo.setSubImages(product.getSubImages());        productDetailVo.setCategoryId(product.getCategoryId());        productDetailVo.setDetail(product.getDetail());        productDetailVo.setName(product.getName());        productDetailVo.setStatus(product.getStatus());        productDetailVo.setStock(product.getStock());        productDetailVo.setImageHost(PropertiesUtil.getProperty("ftp.server.http.prefix","http://image.kamisama.com/"));        Category category = categoryMapper.selectByPrimaryKey(product.getCategoryId());        if(category == null){            productDetailVo.setParentCategoryId(0);//默认根节点        }else{            productDetailVo.setParentCategoryId(category.getParentId());        }        productDetailVo.setCreateTime(DateTimeUtil.dateToStr(product.getCreateTime()));        productDetailVo.setUpdateTime(DateTimeUtil.dateToStr(product.getUpdateTime()));        return productDetailVo;    }

这里的ImageHost从配置文件里获取,所以需要一个PropertyUtils工具类,我们在tomcat启动的时候就要读取到里面的配置,所以用静态块处理这类事情,静态代码块先于普通代码块,普通代码块先于构造代码块,静态代码块在加载的时候被执行且,只执行一次,使用来初始化。

public class PropertiesUtil {    private static Logger logger = LoggerFactory.getLogger(PropertiesUtil.class);    private static Properties props;    static {        String fileName = "mmall.properties";        props = new Properties();        try {            props.load(new InputStreamReader(PropertiesUtil.class.getClassLoader().getResourceAsStream(fileName),"UTF-8"));        } catch (IOException e) {            logger.error("配置文件读取异常",e);        }    }    public static String getProperty(String key){        String value = props.getProperty(key.trim());        if(StringUtils.isBlank(value)){            return null;        }        return value.trim();    }    public static String getProperty(String key,String defaultValue){        String value = props.getProperty(key.trim());        if(StringUtils.isBlank(value)){            value = defaultValue;        }        return value.trim();    }}

trim()能避免配置文件里有多余的空格
从数据库拿出来的updateTime和createTime都是一个毫秒数不利于阅读,所以需要一个工具类转换一下,新建一个DateTimeUtils,字符串转date,date转字符串,这里使用了joda-time,比jdk自带的时间函数要好使用得多

public class DateTimeUtil {    //joda-time    public static final String STANDARD_FORMAT = "yyyy-MM-dd HH:mm:ss";    public static Date strToDate(String dateTimeStr,String formatStr){        DateTimeFormatter dateTimeFormatter = DateTimeFormat.forPattern(formatStr);        DateTime dateTime = dateTimeFormatter.parseDateTime(dateTimeStr);        return dateTime.toDate();    }    public static String dateToStr(Date date,String formatStr){        if (date==null){            return StringUtils.EMPTY;        }        DateTime dateTime = new DateTime(date);        return dateTime.toString(formatStr);    }    public static Date strToDate(String dateTimeStr){        DateTimeFormatter dateTimeFormatter = DateTimeFormat.forPattern(STANDARD_FORMAT);        DateTime dateTime = dateTimeFormatter.parseDateTime(dateTimeStr);        return dateTime.toDate();    }    public static String dateToStr(Date date){        if (date==null){            return StringUtils.EMPTY;        }        DateTime dateTime = new DateTime(date);        return dateTime.toString(STANDARD_FORMAT);    }}

商品List

controller里和上面基本一样,参数多了一点,由于要分页,所以有个pageNum和pageSize,使用@RequestParam注解加个默认值

    @RequestMapping("list.do")    @ResponseBody    public ServerResponse getList(HttpSession session, @RequestParam(value = "pageNum",defaultValue = "1") int pageNum, @RequestParam(value = "pageSize",defaultValue = "10") int pageSize){        User user = (User)session.getAttribute(Const.CURRENT_USER);        if(user == null){            return ServerResponse.createByErrorCodeMessage(ResponseCode.NEED_LOGIN.getCode(),"使用户未登录,请登录管理员");        }        if(iUserService.checkAdminRole(user).isSuccess()){            //填充业务            return iProductService.getProductList(pageNum,pageSize);        }else{            return ServerResponse.createByErrorMessage("无权限操作");        }    }

service层里使用mybatis分页插件进行分页,使用PageHelper.startPage(pageNum,pageSize)设置第几个页面开始和页面大小,而后sql查询出商品list,而后把查出来的商品list转成我们需要的vo,而后把它交给PageInfo

    public ServerResponse<PageInfo> getProductList(int pageNum, int pageSize){        //startPage--start        //填充自己的sql查询逻辑        //pageHelper-收尾        PageHelper.startPage(pageNum,pageSize);        List<Product> productList = productMapper.selectList();        List<ProductListVo> productListVoList = Lists.newArrayList();        for(Product productItem : productList){            ProductListVo productListVo = assembleProductListVo(productItem);            productListVoList.add(productListVo);        }        PageInfo pageResult = new PageInfo(productList);        pageResult.setList(productListVoList);        return ServerResponse.createBySuccess(pageResult);    }    private ProductListVo assembleProductListVo(Product product){        ProductListVo productListVo = new ProductListVo();        productListVo.setId(product.getId());        productListVo.setName(product.getName());        productListVo.setCategoryId(product.getCategoryId());        productListVo.setImageHost(PropertiesUtil.getProperty("ftp.server.http.prefix","http://image.kamisama.com/"));        productListVo.setMainImage(product.getMainImage());        productListVo.setPrice(product.getPrice());        productListVo.setSubtitle(product.getSubtitle());        productListVo.setStatus(product.getStatus());        return productListVo;    }

按名字查找商品

与上面一样,区别在于controller要再传一个productName和productId

    @RequestMapping("search.do")    @ResponseBody    public ServerResponse productSearch(              HttpSession session,String productName,Integer productId,               @RequestParam(value = "pageNum",defaultValue = "1") int pageNum,              @RequestParam(value = "pageSize",defaultValue = "10") int pageSize){        User user = (User)session.getAttribute(Const.CURRENT_USER);        if(user == null){            return ServerResponse.createByErrorCodeMessage(ResponseCode.NEED_LOGIN.getCode(),"使用户未登录,请登录管理员");        }        if(iUserService.checkAdminRole(user).isSuccess()){            //填充业务            return iProductService.searchProduct(productName,productId,pageNum,pageSize);        }else{            return ServerResponse.createByErrorMessage("无权限操作");        }    }
    public ServerResponse<PageInfo> searchProduct(String productName,Integer productId,int pageNum,int pageSize){        PageHelper.startPage(pageNum,pageSize);        if(StringUtils.isNotBlank(productName)){            productName = new StringBuilder().append("%").append(productName).append("%").toString();        }        List<Product> productList = productMapper.selectByNameAndProductId(productName,productId);        List<ProductListVo> productListVoList = Lists.newArrayList();        for(Product productItem : productList){            ProductListVo productListVo = assembleProductListVo(productItem);            productListVoList.add(productListVo);        }        PageInfo pageResult = new PageInfo(productList);        pageResult.setList(productListVoList);        return ServerResponse.createBySuccess(pageResult);    }

sql查询语句就有区别了,由于传了一个商品id和商品名称,所以不可以简单的匹配这两个字段,由于当商品id为null,“where id = id productName=productName” 是怎样也查不到的,所以,要使用一个判断if,判断能否为空,所以这个查询的实现如下:

  <id="selectByNameAndProductId" resultMap="BaseResultMap" parameterType="map">       <include refid="Base_Column_List"/>    FROM mmall_product    <where>      <if test="productName != null">        NAME LIKE #{productName}      </if>      <if test="productId != null">        id = #{productId}      </if>    </where>  </select>

文件上传

在SpringMVC的配置里要有文件上传的配置

<!-- 文件上传 --><bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">    <property name="maxUploadSize" value="10485760"/> <!-- 10m -->    <property name="maxInMemorySize" value="4096" />    <property name="defaultEncoding" value="UTF-8"></property></bean>

先创立一个FileService,写一个upload方法,返回值是新的文件名,参数传MultipartFile和路径Path,先拿到文件名再拿到扩展名,而后给原来的文件名生成一个随机的文件名,避免上传时会有重复文件名导致文件被覆盖,而后再连接上扩展名,上传之后把新文件名返回回去。上传时,假如路径不存在,那要先创立它,创立之前要先设置权限fileDir.setWritable(true),让它有写入的权限,而后fileDir.mkdirs()。而后即可以上传了

mkdir()和mkdirs()的区别在于,mkdirs能创立多个文件路径

@Service("iFileService")public class FileServiceImpl implements IFileService {    private Logger logger = LoggerFactory.getLogger(FileServiceImpl.class);    public String upload(MultipartFile file,String path){        String fileName = file.getOriginalFilename();        //扩展名        //abc.jpg        String fileExtensionName = fileName.substring(fileName.lastIndexOf(".")+1);        String uploadFileName = UUID.randomUUID().toString()+"."+fileExtensionName;        logger.info("开始上传文件,上传文件的文件名:{},上传的路径:{},新文件名:{}",fileName,path,uploadFileName);        File fileDir = new File(path);        if(!fileDir.exists()){            fileDir.setWritable(true);            fileDir.mkdirs();        }        File targetFile = new File(path,uploadFileName);        try {            file.transferTo(targetFile);            //文件已经上传成功了            //todo 文件上传            FTPUtil.uploadFile(Lists.newArrayList(targetFile));            //已经上传到ftp服务器上            targetFile.delete();        } catch (IOException e) {            logger.error("上传文件异常",e);            return null;        }        //A:abc.jpg        //B:abc.jpg        return targetFile.getName();    }}

而后在工具类下新建一个FTPUtils,内部封装了连接服务器方法和文件上传方法

public class FTPUtil {    private static  final Logger logger = LoggerFactory.getLogger(FTPUtil.class);    private static String ftpIp = PropertiesUtil.getProperty("ftp.server.ip");    private static String ftpUser = PropertiesUtil.getProperty("ftp.user");    private static String ftpPass = PropertiesUtil.getProperty("ftp.pass");    private String ip;    private int port;    private String user;    private String pwd;    private FTPClient ftpClient;   ...省略get和set方法    public FTPUtil(String ip,int port,String user,String pwd){        this.ip = ip;        this.port = port;        this.user = user;        this.pwd = pwd;    }    public static boolean uploadFile(List<File> fileList) throws IOException {        FTPUtil ftpUtil = new FTPUtil(ftpIp,21,ftpUser,ftpPass);        logger.info("开始连接ftp服务器");        boolean result = ftpUtil.uploadFile("img",fileList);        logger.info("开始连接ftp服务器,结束上传,上传结果:{}");        return result;    }    private boolean uploadFile(String remotePath,List<File> fileList) throws IOException {        boolean uploaded = true;        FileInputStream fis = null;        //连接FTP服务器        if(connectServer(this.ip,this.port,this.user,this.pwd)){            try {                ftpClient.changeWorkingDirectory(remotePath);                ftpClient.setBufferSize(1024);                ftpClient.setControlEncoding("UTF-8");                ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);                ftpClient.enterLocalPassiveMode();                for(File fileItem : fileList){                    fis = new FileInputStream(fileItem);                    ftpClient.storeFile(fileItem.getName(),fis);                }            } catch (IOException e) {                logger.error("上传文件异常",e);                uploaded = false;                e.printStackTrace();            } finally {                fis.close();                ftpClient.disconnect();            }        }        return uploaded;    }    private boolean connectServer(String ip,int port,String user,String pwd){        boolean isSuccess = false;        ftpClient = new FTPClient();        try {            ftpClient.connect(ip);            isSuccess = ftpClient.login(user,pwd);        } catch (IOException e) {            logger.error("连接FTP服务器异常",e);        }        return isSuccess;    }}

在controller里添加upload方法参数传session,request和一个MultipartFile(是SpringMVC的文件上传)

    @RequestMapping("upload.do")    @ResponseBody    public ServerResponse upload(    HttpSession session, @RequestParam(value = "upload_file",required = false) MultipartFile file, HttpServletRequest request){        User user = (User)session.getAttribute(Const.CURRENT_USER);        if(user == null){            return ServerResponse.createByErrorCodeMessage(ResponseCode.NEED_LOGIN.getCode(),"使用户未登录,请登录管理员");        }        if(iUserService.checkAdminRole(user).isSuccess()){            String path = request.getSession().getServletContext().getRealPath("upload");            String targetFileName = iFileService.upload(file,path);            String url = PropertiesUtil.getProperty("ftp.server.http.prefix")+targetFileName;            Map fileMap = Maps.newHashMap();            fileMap.put("uri",targetFileName);            fileMap.put("url",url);            return ServerResponse.createBySuccess(fileMap);        }else{            return ServerResponse.createByErrorMessage("无权限操作");        }    }

首先从request里拿到session而后拿到servlet的上下文,getRealPath()方法拿到路径,上传之后就是这里


上传路径

富文本上传

与普通的文件上传还是有一点点区别,富文本中对于返回值有自己的要求,我们用是simditor所以按照simditor的要求进行返回,很多前台插件对后台的返回值是有要求的,所以要适当更改。

    @RequestMapping("richtext_img_upload.do")    @ResponseBody    public Map richtextImgUpload(HttpSession session, @RequestParam(value = "upload_file",required = false) MultipartFile file, HttpServletRequest request, HttpServletResponse response){        Map resultMap = Maps.newHashMap();        User user = (User)session.getAttribute(Const.CURRENT_USER);        if(user == null){            resultMap.put("success",false);            resultMap.put("msg","请登录管理员");            return resultMap;        }        //富文本中对于返回值有自己的要求,我们用是simditor所以按照simditor的要求进行返回//        {//            "success": true/false,//                "msg": "error message", # optional//            "file_path": "[real file path]"//        }        if(iUserService.checkAdminRole(user).isSuccess()){            String path = request.getSession().getServletContext().getRealPath("upload");            String targetFileName = iFileService.upload(file,path);            if(StringUtils.isBlank(targetFileName)){                resultMap.put("success",false);                resultMap.put("msg","上传失败");                return resultMap;            }            String url = PropertiesUtil.getProperty("ftp.server.http.prefix")+targetFileName;            resultMap.put("success",true);            resultMap.put("msg","上传成功");            resultMap.put("file_path",url);            response.addHeader("Access-Control-Allow-Headers","X-File-Name");            return resultMap;        }else{            resultMap.put("success",false);            resultMap.put("msg","无权限操作");            return resultMap;        }    }
网友评论