美文网首页程序员
SSM框架学习日记(5)——商品模块

SSM框架学习日记(5)——商品模块

作者: 糯米团子_大芒果 | 来源:发表于2018-07-12 22:38 被阅读0次

    商品相关接口

    先在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 rowCount = productMapper.updateByPrimaryKey(product);
                    if(rowCount > 0){
                        return ServerResponse.createBySuccess("更新产品成功");
                    }
                    return ServerResponse.createBySuccess("更新产品失败");
                }else{
                    int rowCount = productMapper.insert(product);
                    if(rowCount > 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 and productName=productName” 是怎么也查不到的,所以,要用一个判断if,判断是否为空,所以这个查询的实现如下:

      <select id="selectByNameAndProductId" resultMap="BaseResultMap" parameterType="map">
        SELECT
        <include refid="Base_Column_List"/>
        FROM mmall_product
        <where>
          <if test="productName != null">
            AND NAME LIKE #{productName}
          </if>
          <if test="productId != null">
            and 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;
            }
        }
    

    相关文章

      网友评论

        本文标题:SSM框架学习日记(5)——商品模块

        本文链接:https://www.haomeiwen.com/subject/srocpftx.html