电商平台搭建--商品管理功能模块开发(一)
Hi,大家好,我们又见面了。相信通过前面几篇博文的学习,大家已经对如何搭建一款属于自己的电商平台有了初步的了解,也大致懂了SSM框架的主要开发流程,那么在接下来的几篇博文中,我将带领大家完成商品管理功能模块的开发,还在等什么,直接进入正题吧!
一、商品管理功能模块-概要
先来看商品模块都需要实现哪些功能点
电商平台的商品管理模块,一般都分为前台和后台,所以在后端要写前台的商品管理,也要写后台的商品管理。按照正常顺序,先来完成前台商品管理的功能。
相对于后台来讲,前台的商品管理比较简单,主要功能一共就两个,获取商品详情、前台商品搜索。在这两个功能里面,获取商品详情比较简单,我们会在前台商品搜索中把分页逻辑写好,方便前台调用。
二、商品管理功能模块-前台-获取商品详情功能的实现
Service层
//前台-获取商品详细信息
public ServerResponse<ProductDetailVo> getProductDetail(Integer productId){
if(productId == null){
return ServerResponse.createByErrorCodeMessage(ResponseCode.ILLEGALARGUMENT.getCode(), ResponseCode.ILLEGALARGUMENT.getDesc());
}
Product product = productMapper.selectByPrimaryKey(productId);
if(product == null){
return ServerResponse.createByErrorMessage("产品已下架或已删除");
}
if(product.getStatus() != Const.ProductStatusEnum.ON_SALE.getCode()){
return ServerResponse.createByErrorMessage("产品已下架或已删除");
}
ProductDetailVo productDetailVo = assembleProductDetailVo(product);
return ServerResponse.createBySuccess(productDetailVo);
}
首先来看,在getProductDetail方法中,ServerResponse的泛型被指定为ProductDetailVo类型。这里的Vo是value-object也就是值对象的简称。什么是值对象呢?它的本质也是一个JavaBean,只不过是为了专门解决一种或多种需求独立出来的JavaBean。有了VO以后,对数据的处理就会更加灵活,因为我们可以单独的封装它们,这样即保证了数据的独立性,也不会对项目的整体数据造成影响,当我们的VO处理完毕以后,并入到项目中,也降低了各个功能之间的依赖性。就前台-获取商品详细信息来看,我封装了一个ProductDetailVo这么一个值对象,它里面存放了和商品所有有关的信息字段以及getter和setter方法。
先回到该方法,传递一个productId来在数据库中查询相关的商品信息。如果productId为空,则提示参数错误,否则就向数据库中查询,将查询结果返回给product,如果其为空表示产品已下架或已删除,还有一种情况是根据商品的状态,根据返回值来判断商品的状态,这个ProductStatusEnum会在下面补充。当商品的校验状态通过以后,就可以直接调用封装好的assembleProductDetailVo方法来处理商品的详细信息,最后直接返回productDetailVo即可,这个字段里面就包括了商品的详细信息。
public class 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;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getCategoryId() {
return categoryId;
}
public void setCategoryId(Integer categoryId) {
this.categoryId = categoryId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSubtitle() {
return subtitle;
}
public void setSubtitle(String subtitle) {
this.subtitle = subtitle;
}
public String getMainImage() {
return mainImage;
}
public void setMainImage(String mainImage) {
this.mainImage = mainImage;
}
public String getSubImages() {
return subImages;
}
public void setSubImages(String subImages) {
this.subImages = subImages;
}
public String getDetail() {
return detail;
}
public void setDetail(String detail) {
this.detail = detail;
}
public BigDecimal getPrice() {
return price;
}
public void setPrice(BigDecimal price) {
this.price = price;
}
public Integer getStock() {
return stock;
}
public void setStock(Integer stock) {
this.stock = stock;
}
public Integer getStatus() {
return status;
}
public void setStatus(Integer status) {
this.status = status;
}
public String getCreateTime() {
return createTime;
}
public void setCreateTime(String createTime) {
this.createTime = createTime;
}
public String getUpdateTime() {
return updateTime;
}
public void setUpdateTime(String updateTime) {
this.updateTime = updateTime;
}
public String getImageHost() {
return imageHost;
}
public void setImageHost(String imageHost) {
this.imageHost = imageHost;
}
public Integer getParentCategoryId() {
return parentCategoryId;
}
public void setParentCategoryId(Integer parentCategoryId) {
this.parentCategoryId = parentCategoryId;
}
}
为了使用这个VO,我们不能在其内部去写方法,然后在外部调用,这样是不符合情理的。对于这种情况,可以在对应的Service中封装一个处理VO的方法,这样一来,就可以实现“点对点”的功能服务。在该方法中,我写了一个处理商品详情的VO方法。
private ProductDetailVo assembleProductDetailVo(Product product){
ProductDetailVo productDetailVo = new ProductDetailVo();
productDetailVo.setId(product.getId());
productDetailVo.setSubtitle(product.getSubtitle());
productDetailVo.setPrice(product.getPrice());
productDetailVo.setName(product.getName());
productDetailVo.setSubImages(product.getSubImages());
productDetailVo.setMainImage(product.getMainImage());
productDetailVo.setDetail(product.getDetail());
productDetailVo.setStatus(product.getStatus());
productDetailVo.setStock(product.getStock());
productDetailVo.setCategoryId(product.getCategoryId());
productDetailVo.setImageHost(PropertiesUtil.getProperty("fet.server.http.prefix", "your-ftp-address"));
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;
}
像处理JavaBean一样,处理VO的方法类型一定要是当前VO类型的并传递给VO需要的Product数据,否则数据无法处理。首先在该方法中new出一个当前ProductDetailVo的实例,然后把需要处理的数据用setter方法设置好,然后有一个category判断。如果getCategoryId为空,就把它的父节点id也设置为空,否则就把当前的id值更新到父节点id上。最后再设置一下更新商品或者创建商品的创建时间和更新时间,返回productDetailVo,就处理完了ProductDetailVo值对象。
这里提一下,ImageHost字段为自己的图片服务器地址,因为所有的图片都是保存在图片服务器上面的,所以修改图片要通过图片服务器的方式进行修改,切记。
Service层就写好了,再来看Controller层
@RequestMapping(value = "detail.do")
@ResponseBody
public ServerResponse<ProductDetailVo> detail(Integer productId){
return iProductService.getProductDetail(productId);
}
因为和功能有关的逻辑在Service层中已经处理完善了,所以在Controller里直接返回处理结果即可,别忘了这里的泛型一定是ProductDetailVo类型。
三、商品管理功能模块-前台-商品搜索功能的实现
Service层
//前台商品搜索
public ServerResponse<PageInfo> getProductByKeywordCategory(String keyword, Integer categoryId, int pageNum, int pageSize, String orderBy){
if(StringUtils.isBlank(keyword) && categoryId == null){
return ServerResponse.createByErrorCodeMessage(ResponseCode.ILLEGALARGUMENT.getCode(), ResponseCode.ILLEGALARGUMENT.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> productListVoList = Lists.newArrayList();
PageInfo pageInfo = new PageInfo(productListVoList);
return ServerResponse.createBySuccess(pageInfo);
}
categoryIdList = iCategoryService.selectCategoryAndChildrenById(category.getId()).getData();
}
if(StringUtils.isNotBlank(keyword)){
keyword = new StringBuilder().append("%").append(keyword).append("%").toString();
}
// 排序处理
PageHelper.startPage(pageNum, pageSize);
if(StringUtils.isNotBlank(orderBy)){
if(Const.ProductListOrderBy.PRICE_ASC_DESC.contains(orderBy)){
String[] orderByArray = orderBy.split("_");
PageHelper.orderBy(orderByArray[0]+" "+orderByArray[1]);
}
}
List<Product> productList = productMapper.selectByNameAndCategoryIds(StringUtils.isBlank(keyword)?null:keyword,categoryIdList.size()==0?null:categoryIdList);
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.createBySuccess(pageInfo);
}
在前台商品搜索中,我们需要处理很多事情。首先是搜索方式,可以通过关键词进行搜索,也可以通过categoryId进行搜索(这个categoryId是后端为每一个商品单独添加的id,会直接存放在数据库中对应商品)。其次就是将搜索结果进行一个分页处理,如果没有分页的话,商品列表页会看着非常乱,会严重影响用户体验,这在企业中也是不允许的。
跟字段验证的方法相似,首先验证keyword和categoryId是否为空,如果为空,则提示参数错误,否则进行下一步操作。像上述方法一样,对于复杂数据的处理,应该封装一个VO,这里的是ProductListVo(下面有介绍)。回到方法中,分页的实现是之前提到的Mybatis三剑客的PageHelper,这是一个开源的GitHub项目(https://github.com/pagehelper/Mybatis-PageHelper)。使用PageHelper进行分页处理,需要三步实现。第一步,调用PageHelper的startPage方法,传入分页数量和每页显示的数量;第二步,填充分页数据;第三步,开始分页。注意,全部用List集合进行处理。对排序逻辑的处理,分为默认排序和价格升降序排序。其实默认排序就是把直接进行搜索的结果显示出来就行,不需要再处理,所以这里的代码是不用写的,只需处理价格的高低即可。因为每排一次序就是重新显示一个界面,所以在处理价格排序时要重新进行分页,逻辑和上述相同不再赘述。因为涉及到排序,所以getProductByKeywordCategory方法的泛型需要为PageInfo,否则无法进行排序。最后将排序处理后的结果返回,就完成了该方法的编写。
PageInfo pageInfo = new PageInfo(productListVoList);//第三步
List<ProductListVo> productListVoList = Lists.newArrayList();//第二步
public class ProductListVo {
private Integer id;
private Integer categoryId;
private String name;
private String subtitle;
private String mainImage;
private BigDecimal price;
private Integer status;
private String imageHost;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getCategoryId() {
return categoryId;
}
public void setCategoryId(Integer categoryId) {
this.categoryId = categoryId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSubtitle() {
return subtitle;
}
public void setSubtitle(String subtitle) {
this.subtitle = subtitle;
}
public String getMainImage() {
return mainImage;
}
public void setMainImage(String mainImage) {
this.mainImage = mainImage;
}
public BigDecimal getPrice() {
return price;
}
public void setPrice(BigDecimal price) {
this.price = price;
}
public Integer getStatus() {
return status;
}
public void setStatus(Integer status) {
this.status = status;
}
public String getImageHost() {
return imageHost;
}
public void setImageHost(String imageHost) {
this.imageHost = imageHost;
}
}
像之前处理VO方式一样,在对应的Service层中封装一个assemble方法来处里ProductListVo
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","your-ftp-address"));
productListVo.setMainImage(product.getMainImage());
productListVo.setPrice(product.getPrice());
productListVo.setSubtitle(product.getSubtitle());
productListVo.setStatus(product.getStatus());
return productListVo;
}
因为只是简单的获取到商品的列表信息,所以不用再封装处理数据的方法,只是对字段的一个处理。
Controller层
@RequestMapping(value = "list.do")
@ResponseBody
public ServerResponse<PageInfo> list(@RequestParam(value = "keyword", required = false) String keyword,
@RequestParam(value = "categoryId", required = false)Integer categoryId,
@RequestParam(value = "pageNum", defaultValue = "1") int pageNum,
@RequestParam(value = "pageSize", defaultValue = "10") int pageSize,
@RequestParam(value = "orderBy", defaultValue = "") String orderBy){
return iProductService.getProductByKeywordCategory(keyword, categoryId, pageNum, pageSize, orderBy);
}
因为和功能有关的逻辑在Service层中已经处理完善了,所以在Controller里直接返回处理结果即可。 这里又用到了RequestParam注解来为每个字段设置默认值。keyword和categoryId的默认值为false,表示如果前台不传递这两个字段给后台也是可以进行排序的,即这两个字段不是非必须的。pageNum默认值为1,表示默认只有一页,pageSize默认值为10,表示每页显示10条记录。orderBy默认值为空,表示默认使用默认排序,若使用价格排序,需要前台传入orderBy的值。
四、关于商品模块的一些补充
(1)、因为是商品模块,安全性较低,所以所有的接口请求都采用默认的GET请求;
(2)、整个项目的Mybatis层的所有Sql语句,会在后期进行更新;
(3)、笔者默认认为你已经有一定的JavaBean基础,所以在商品模块没有对JavaBean进行详细的解释;
(4)、值对象VO是依赖POJO的,所以在处理值对象VO的方法中传入的是对应的POJO类型,例如这里的Product;
写到这里,前台商品模块所有功能就实现完毕了,在本篇博文中,我们用了较长的篇幅来搭建商品模块的开发基础,希望大家能动手写写,体会一下前台商品模块的开发流程。如果有不懂的地方,欢迎关注,欢迎评论留言。我们下篇再见!!
上一篇: php实现的简单日志写入函数_php技巧
下一篇: win10传统启动改为uefi启动的方法