mmal商城商品模块总结
学习目标
- FTP服务器的对接
- SpringMVC文件上传
- 流读取properties配置文件
- 抽象POJO、BO、VO对象之间的转换关系及解决思路
- joda-time快速入门
- 静态代码块
- mybatis-pageHelper
商品模块分为前后台操作,前台功能接口有:搜索,分页显示,商品详情;后台管理模块有保存商品,修改商品在线状态,获取商品详情,分页显示,按照名称或者商品id搜索,上传商品图片,富文本格式上传商品。
获取商品详情信息:
这个之前做的方法大差不差,都是通过商品id来获取需要在前端显示的信息,但是与之前不同的是这次引入了VO的概念;value object(值对象);它的作用就是当我们在dao层从数据库中获取的数据封装到pojo中,但是pojo中的数据还和前端显示的有所差别,这时我们再封装一个value object;由它返回到前端显示;总的来说value object是显示层的对象,通常是web向模板引擎渲染并传输的对象;
下面这段总结来源于阿里巴巴开发手册中对pojo,vo,bo,dto,的定义与区别:
阿里巴巴Java开发手册中的DO、DTO、BO、AO、VO、POJO定义
分层领域模型规约:
- DO( Data Object):与数据库表结构一一对应,通过DAO层向上传输数据源对象。
- DTO( Data Transfer Object):数据传输对象,Service或Manager向外传输的对象。
- BO( Business Object):业务对象。 由Service层输出的封装业务逻辑的对象。
- AO( Application Object):应用对象。 在Web层与Service层之间抽象的复用对象模型,极为贴近展示层,复用度不高。
- VO( View Object):显示层对象,通常是Web向模板渲染引擎层传输的对象。
- POJO( Plain Ordinary Java Object):在本手册中, POJO专指只有setter/getter/toString的简单类,包括DO/DTO/BO/VO等。
- Query:数据查询对象,各层接收上层的查询请求。 注意超过2个参数的查询封装,禁止使用Map类来传输。
领域模型命名规约:
- 数据对象:xxxDO,xxx即为数据表名。
- 数据传输对象:xxxDTO,xxx为业务领域相关的名称。
- 展示对象:xxxVO,xxx一般为网页名称。
- POJO是DO/DTO/BO/VO的统称,禁止命名成xxxPOJO。
具体实现逻辑就是:在id不为空的情况下,我们直接从数据库中查询这个商品的信息,封装到pojo的DO对象中,然后判断这个对象的在线状态是否是下架的,如果是我们就返回提示信息,说明该商品已经下架;否则我们就开始封装ProductDetailVo对象;
最后把这个对象返回到显示层;
public ServerResponse<ProductDetailVo> getProductDetail(Integer productId) {
if (productId == null) {
return ServerResponse.createByErrorCodeMessage(ResponseCode.ILLEGAL_ARGUMENT.getCode(), ResponseCode.ILLEGAL_ARGUMENT.getDesc());
}
Product product = productMapper.selectByPrimaryKey(productId);
//判断商品是否已经下架
if (product == null || product.getStatus() != Const.productStatus.ON_SEAL.getCode()) {
return ServerResponse.createByErrorMessage("此商品已下架或已删除");
}
//封装VO
ProductDetailVo productDetailVo = assembleProductDetailVo(product);
//返回到显示层
return ServerResponse.createBySuccessData(productDetailVo);
}
分页模块:
这里使用了MyBatis-Helper;分页插件;
这时候前端需要传的参数就多了,关键字,分类id,当前页,每页显示的商品数量,排序方式;
因为使用了分页插件所以到左后分页的事情我们就不需要管了,我们先来看一下SQL语句;
List<Product> selectByNameAndCategoryIds(@Param("productName")String productName,@Param("categoryIds") List<Integer> categoryIds);
这时Mapper接口我们需要注意的是,分类id是一个集合因为之前在分类模块中说过,一个父级别的分类下面还有许多子分类,所以传过来是一个集合;
xml中的SQL语句如下
<select id="selectByNameAndCategoryIds" resultMap="BaseResultMap" parameterType="map">
select
<include refid="Base_Column_List"/>
from mmall_product
where status = 1
<if test="productName != null">
and name like #{productName}
</if>
<if test="categoryIds != null">
and category_id in
<foreach collection="categoryIds" index="index" item="item" open="(" close=")" separator=",">
#{item}
</foreach>
</if>
</select>
<resultMap id="BaseResultMap" type="cn.edu.mmall.pojo.Product" >
<constructor >
<idArg column="id" jdbcType="INTEGER" javaType="java.lang.Integer" />
<arg column="category_id" jdbcType="INTEGER" javaType="java.lang.Integer" />
<arg column="name" jdbcType="VARCHAR" javaType="java.lang.String" />
<arg column="subtitle" jdbcType="VARCHAR" javaType="java.lang.String" />
<arg column="main_image" jdbcType="VARCHAR" javaType="java.lang.String" />
<arg column="sub_images" jdbcType="VARCHAR" javaType="java.lang.String" />
<arg column="detail" jdbcType="VARCHAR" javaType="java.lang.String" />
<arg column="price" jdbcType="DECIMAL" javaType="java.math.BigDecimal" />
<arg column="stock" jdbcType="INTEGER" javaType="java.lang.Integer" />
<arg column="status" jdbcType="INTEGER" javaType="java.lang.Integer" />
<arg column="create_time" jdbcType="TIMESTAMP" javaType="java.util.Date" />
<arg column="update_time" jdbcType="TIMESTAMP" javaType="java.util.Date" />
</constructor>
</resultMap>
<sql id="Base_Column_List" >
id, category_id, name, subtitle, main_image, sub_images, detail, price, stock, status,
create_time, update_time
</sql>
sql语句的拼写还是值得注意的,以及遍历集合时,使用的foreach循环语法,记得加上前缀,后缀,分隔符,遍历集合,以及单个元素的item;
这时我们再回来看处理分页的逻辑;首先在搜索关键字和分类id都没有的情况下也就是都是为null,我们就返回非法参数的提示;如果分类id不为空,但是我们根据id查不出来也即是查到的对象是null,我们不应返回错误,应该返回一个空集合,毕竟我们确实进行了数据库的查询操作;如果关键词不为空并且不是空字符串或者空格,我们就对他进行拼接处理,也就是前后都加上%,然后开始处理排序,在传过来的排序名称不为空的情况下,我们对他进行比较判断是不是按照价格进行的从大到小排序还是从小到大排序,最后在PageHelper中设置排序方式;最后调用我们刚才写的SQL接口方法返回list集合,再将集合中的product对象一个个封装成Vo对象。
最后具体代码如下
public ServerResponse<PageInfo> getProductByKeywordCategory(String keyword, int pageNum, int pageSize, Integer categoryId, String orderBy) {
if (StringUtils.isBlank(keyword) && categoryId == null) {
return ServerResponse.createByErrorCodeMessage(ResponseCode.ILLEGAL_ARGUMENT.getCode(), ResponseCode.ILLEGAL_ARGUMENT.getDesc());
}
List<Integer> categoryIdList = new ArrayList<>();
if (categoryId != null) {
Category category = categoryMapper.selectByPrimaryKey(categoryId);
if (category == null && StringUtils.isBlank(keyword)) {
//查不到分类,没有输入名称 ,返回空集合,不报错
PageHelper.startPage(pageNum, pageSize);
List<ProductListVo> productListVos = Lists.newArrayList();
PageInfo pageInfo = new PageInfo<>(productListVos);
return ServerResponse.createBySuccessData(pageInfo);
}
}
//处理关键字
if (StringUtils.isNoneBlank(keyword)) {
keyword = new StringBuffer().append("%").append(keyword).append("%").toString();
}
PageHelper.startPage(pageNum, pageSize);
//处理排序
if (StringUtils.isNoneBlank(orderBy)) {
if (Const.ProductListOrderBy.PRICE_ASC_DESC.contains(orderBy)) {
String[] s = orderBy.split("_");
PageHelper.orderBy(s[0] + " " + s[1]);
}
}
//categoryIdList先前已经new了出来所以不为空,因此需要判断一下如果里面没有元素就是空,keyword可能是空字符串或者空格那就赋值为空
List<Product> productList = productMapper.selectByNameAndCategoryIds(
StringUtils.isBlank(keyword) ? null : keyword,
categoryIdList.size() == 0 ? null : categoryIdList);
//将product对象一个个封装到vo中,返回list集合
List<ProductListVo> productListVoList = Lists.newArrayList();
for (Product product : productList) {
ProductListVo productListVo = assembleProductListVo(product);
productListVoList.add(productListVo);
}
//将分页信息对象返回到前端
PageInfo pageInfo = new PageInfo<>(productList);
pageInfo.setList(productListVoList);
return ServerResponse.createBySuccessData(pageInfo);
}
后台管理模块需要多一项判断,判断当前用户是不是管理员角色;如果不是的情况下是没有权限进行操作的。后台管理基本上也是增加商品,修改商品状态,获取商品详情,列表显示这些与前台代码相差无几,这里主要说一下文件上传的实现逻辑。
controller中我们可以从请求中获取路径
(项目在容器中的实际发布运行的根路径)
调用FileService的upload方法返回上传文件的文件名,最终将URL与文件名一起返回到前台。
FTPUtil(连接ftp服务器,上传文件)
public class FTPUtil {
private static 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;
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("结束上传,上传结果是:{}",result);
return result;
}
private boolean uploadFile(String remotePath, List<File> fileList) throws IOException {
boolean upload = true;
FileInputStream fis = null;
//连接ftp服务器
if (connectFTPServer(this.ip, this.port, this.user, this.pwd)) {
//如果连接成功,开始上传
//1. 首先改变工作目录
try {
ftpClient.changeWorkingDirectory(remotePath);
ftpClient.setBufferSize(1024);
ftpClient.setControlEncoding("UTF-8");
//设置文件类型是二进制文件
ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
ftpClient.enterLocalPassiveMode();
for (File fileItem : fileList) {
fis = new FileInputStream(fileItem);
//储存文件
ftpClient.storeUniqueFile(fileItem.getName(), fis);
}
} catch (IOException e) {
logger.error("上传文件异常");
upload = false;
e.printStackTrace();
}finally {
fis.close();
ftpClient.disconnect();
}
}
return upload;
}
private boolean connectFTPServer(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;
}
//省略getter/setter方法
public String upload(MultipartFile file, String path) {
//全路径加上文件名
String fileName = file.getName();
//获取文件扩展名
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 将target文件上传到FTP服务器上
FTPUtil.uploadFile(Lists.newArrayList(targetFile));
//todo 删除本地上传过的文件
targetFile.delete();
} catch (IOException e) {
logger.error("上传文件异常",e);
return null;
}
return targetFile.getName();
}
ftp配置文件
ftp.server.ip=192.168.245.1
ftp.user=mmallftp
ftp.pass=ftppassword
ftp.server.http.prefix=http://img.happymmall.com/
mmal商城商品模块总结的更多相关文章
- ssh整合问题总结--在添加商品模块实现图片(文件)的上传
今天在做毕设(基于SSH的网上商城项目)中碰到了一个文件上传的需求,就是在后台管理员的商品模块中,有一个添加商品,需要将磁盘上的图片上传到tomcat保存图片的指定目录中: 完成这个功能需要两个步,第 ...
- SSH网上商城---商品详情页的制作
在前面的博文中,小编分别简单的介绍了邮件的发送以及邮件的激活,逛淘宝的小伙伴都有这样的体会,比如在搜索框中输入连衣裙这个商品的时候,会出现多种多样各种款式的连衣裙,连衣裙的信息包括价格,多少人购买,商 ...
- Mvp快速搭建商城购物车模块
代码地址如下:http://www.demodashi.com/demo/12834.html 前言: 说到MVP的时候其实大家都不陌生,但是涉及到实际项目中使用,还是有些无从下手.因此这里小编带着大 ...
- Scrapy实战篇(八)之Scrapy对接selenium爬取京东商城商品数据
本篇目标:我们以爬取京东商城商品数据为例,展示Scrapy框架对接selenium爬取京东商城商品数据. 背景: 京东商城页面为js动态加载页面,直接使用request请求,无法得到我们想要的商品数据 ...
- ASP.NET MVC中商品模块小样
在前面的几篇文章中,已经在控制台和界面实现了属性值的笛卡尔乘积,这是商品模块中的一个难点.本篇就来实现在ASP.NET MVC4下商品模块的一个小样.与本篇相关的文章包括: 1.ASP.NET MVC ...
- 电子商务(电销)平台中商品模块(Product)数据库设计明细
以下是自己在电子商务系统设计中的数据库设计经验总结,而今发表出来一起分享,如有不当,欢迎跟帖讨论~ 商品表 (product)|-- 自动编号 (product_id)|-- 商品名称 (produc ...
- 电子商务(电销)平台中商品模块(Product)数据库设计明细(转载)
电子商务(电销)平台中商品模块(Product)数据库设计明细 以下是自己在电子商务系统设计中的数据库设计经验总结,而今发表出来一起分享,如有不当,欢迎跟帖讨论~ 商品表 (product)|-- 自 ...
- Python网络爬虫——京东商城商品列表
Python_网络爬虫--京东商城商品列表 最近在拓展自己知识面,想学习一下其他的编程语言,处于多方的考虑最终选择了Python,Python从发布之初就以庞大的用户集群占据了编程的一席之地,pyth ...
- MySQL-THINKPHP 商城系统一 商品模块的设计
在此之前,先了解下关于SPU及SKU的知识 SPU是商品信息聚合的最小单位,是一组可复用.易检索的标准化信息的集合,该集合描述了一个产品的特性.通俗点讲,属性值.特性相同的商品就可以称为一个SPU. ...
随机推荐
- 正交实验法之 Allpairs电商项目用例设计实战
一.正交实验法概述 正交实验法是研究多因素多水平的一种方法,它是通过正交表挑选部分有代表性的水平组合试验替代全面试验.这些有代表性的组合试验具备了"均匀分散,整齐可比"的特点.正交 ...
- SpringBoot集成基于tobato的fastdfs-client实现文件上传下载和删除
1. 简介 基于tobato的fastdfs-client是一个功能完善的FastDFS客户端工具,它是在FastDFS作者YuQing发布的客户端基础上进行了大量的重构,提供了上传.下载.删除. ...
- ASP .Net Core 中间件的使用(一):搭建静态文件服务器/访问指定文件
前言 随着Asp .Net Core的升级迭代,很多开发者都逐渐倾向于.net core开发. .net core是一个跨平台的应用程序,可以在windows.Linux.macOS系统上进行开发和部 ...
- Bug java 安全证书
在一个改修项目里,安全性方面总是出一个bug,错误信息如下: xception in thread "main" javax.crypto.BadPaddingException: ...
- 关于MySQL索引知识与小妙招 — get get get
一.索引基本知识 1.1 索引的优点 大大减少了服务器需要扫描的数据量,加快数据库的检索速度 帮助服务器避免排序和临时表 将随机io变成顺序io 1.2 索引的用处 速查找匹配WHERE子句的行 从c ...
- Go实现ssh执行远端命令及远程终端
什么是ssh? SSH是一种网络协议,用于计算机之间的加密登录. 如果一个用户从本地计算机,使用SSH协议登录另一台远程计算机,我们就可以认为,这种登录是安全的,即使被中途截获,密码也不会泄露. 互联 ...
- burpsuite进阶使用
.Burpsuite:爆破 个人建议选择pro破解版的,免费版的太鸡肋,爆破不能设置线程,速度超乎你想像 浏览器和burpsuite设置代理后,开启抓包,截获数据包后,右键选择发送到repeater修 ...
- svn add . 报错,不能add全部,因为有一些文件已经在版本库中了
svn add 目录名 --force SVN commit -m '' 目录名
- Django + FastDFS (分布式远程服务器存储文件)
之前随笔过一篇Docker来搭建分布式文件系统FastDfs就跳过了 https://www.cnblogs.com/xcsg/p/10901461.html FastDFS的Python (dja ...
- java基础:数组详解以及应用,评委打分案例实现,数组和随机数综合,附练习案列
1.数组 1.1 数组介绍 数组就是存储数据长度固定的容器,存储多个数据的数据类型要一致. 1.2 数组的定义格式 1.2.1 第一种格式 数据类型[] 数组名 示例: int[] arr; ...