猿实战是一个原创系列文章,通过实战的方式,采用前后端分离的技术结合SpringMVC Spring Mybatis,手把手教你撸一个完整的电商系统,变身猿人找到工作不是问题。想要一起实战吗?关注公号即可获取基础代码!
上一个章节,猿人君教会了你实现了后台类目的三级联动,今天我们继续来讲述,新增属性的实现。
功能概览
我们先看看新增属性的功能。
在页面选择类目后(目前暂时要求只新增1.2级类目属性),点击新增属性按钮,可以搜索属性库(要求同时检索属性以及属性组,同时支持模糊左匹配查询),通过勾选的方式新增属性为当前类目的属性。
属性展示之el-tree
我们可以很直观的看出,属性,是以分组的形式进行展示,当然,有的属性并没有从属于,某一个属性组,我们可以考虑一个叫“未分组”的属性组,用于归纳未分组的属性。
这样,属性组和属性之间,就形成了一种父子关系,对于这样的树形展示,element UI 提供了el-tree这样的组件,用于展现数据。
大家可以看一下官网上的内容。https://element.eleme.cn/#/zh-CN/component/tree
el-tree的属性和事件,相对来说比较丰富,就我们系统需要用到的而言,仅仅是展示,然后会关注数据的选择。
Data:用于展示的数据
Node-key:节点id的名称
default-expanded-keys:默认展开的节点的 key 的数组
props:定义数据节点的参数,详见上表
render-content:自定义,展示回调函数。
@check-change:节点选中状态发生变化时的回调。
属性检索后端实现
考虑到,属性和属性值的数据需要一起按照分组进行展示,我们需要定义一个专门用于显示的类来支撑,数据展示。像这样的类往往叫做VO。
由于是属性组内部再包装了,具体的属性,那么我们可以相对较快的定义这样的结构。
package com.pz.basic.mall.domain.product.property.vo;
import com.pz.basic.mall.domain.base.BaseDO;
import com.pz.basic.mall.domain.product.property.MallProperty;
import java.util.List;
public class MallGroupVo extends BaseDO {
private Long id;
private String label;
private List<MallProperty> children;
//省略getter setter
}
/**
* Copyright(c) 2004-2020 pangzi
* com.pz.basic.mall.domain.product.property.MallProperty.java
*/
package com.pz.basic.mall.domain.product.property;
import com.pz.basic.mall.domain.base.BaseDO;
import java.util.Date;
/**
*
* @author pangzi
* @date 2020-06-28 11:40:14
*
*/
public class MallProperty extends BaseDO {
/**属性库属性ID**/
private Long propertyId;
/**属性库属性名称**/
private String propertyName;
/**属性分组ID,来自property_group表**/
private Long groupId;
/**键值对,扩展字段**/
private String features;
/**排序**/
private Integer sortOrder;
/**状态
0=停用
1=启用
**/
private Integer status;
/**创建人**/
private String createUser;
/**修改人**/
private String modifyUser;
/**创建时间**/
private Date created;
/**最后修改时间**/
private Date modified;
}
接下来要做的事情,自然是要返回数据了。在MallCategoryPropertyController中定义方法,返回属性组俩表数据
/**
* 返回类目属性列表
* @param queryMallProperty
* @return
*/
@RequestMapping("/findForSelect")
public Result<List<MallGroupVo>> findForSelect(@RequestBody QueryMallProperty queryMallProperty){
return mallCategoryPropertyService.getMallPropertyGroup(queryMallProperty);
}
我们可以思考下服务层的实现,由于我们的属性表中,只有属性组ID,而没有属性组名称,而我们的检索条件是需要同时支持属性组名称和属性名称进行查询的,这该如何是好?
一般来讲,很常见而且有些偏传统管理软件的实现方式是直接将属性表与属性组表进行关联查询。但是互联网行业在做查询时,考虑到sql的后续优化,是需要控制表之间的关联连接查询的。通常情况下,需要做将这种join关系做些转化。比如将join转化为in的方式,就是最常见的一种方式。
比如我们可以先查询出符合条件的属性组的id,然后再将这些以in的方式在属性表中做查询就好了。
public Result<List<MallGroupVo>> getMallPropertyGroup(QueryMallProperty queryMallProperty) {
Result<List<MallGroupVo>> result = new Result<List<MallGroupVo>>();
QueryMallPropertyGroup groupQuery = new QueryMallPropertyGroup();
groupQuery.setGroupNameLike(queryMallProperty.getPropertyNameLike());
//根据分组名查询分组对象,用于后续展示分组名,以及对属性表支持对应的分组id 用于属性表的 or 关系查询
List<MallPropertyGroup> groupList =getGroupList(groupQuery);
if(!CollectionUtils.isEmpty(groupList)){
List<Long> idList = groupList.stream().map(e -> e.getGroupId()).collect(Collectors.toList());
queryMallProperty.setGroupIdList(idList);
}
List<MallProperty> propertyList=mallPropertyDao.selectMallPropertyByQuery(queryMallProperty);
List<Long> shieldIdList=getShieldPropertyIdList(queryMallProperty);
//删除需要屏蔽的对象
if(!CollectionUtils.isEmpty(shieldIdList)){
propertyList.removeIf(a -> {
return shieldIdList.stream().anyMatch(b -> {
if (b!= null && b.equals(a.getPropertyId())) {
return true;
} else {
return false;
}
});
});
}
List<MallGroupVo> dataList=getMallGroupVoList(propertyList,groupList);
result.addDefaultModel(dataList);
return result;
}
private List<MallGroupVo> getMallGroupVoList(List<MallProperty> propertyList,List<MallPropertyGroup> groupList ){
Map<Long,MallGroupVo> groupMap = new HashMap<Long,MallGroupVo>();
List<MallGroupVo> dataList= new ArrayList<MallGroupVo>();
MallGroupVo group =null;
MallPropertyGroup mallGroup=null;
if(!CollectionUtils.isEmpty(propertyList)){
for(MallProperty property:propertyList){
mallGroup=getMallPropertyGroup(groupList,property.getGroupId());
if(null==mallGroup){
group=groupMap.get(UnsignedGroupId);
}else{
group=groupMap.get(property.getGroupId());
}
if(null==group){
List<MallGroupVo> list = new ArrayList<MallGroupVo>();
List<MallProperty> dbList = new ArrayList<MallProperty>();
dbList.add(property);
group = new MallGroupVo();
list.add(group);
if(null==mallGroup){
group.setId(UnsignedGroupId);
group.setLabel(UnsignedGroupName);
}else{
group.setId(mallGroup.getGroupId());
group.setLabel(mallGroup.getGroupName());
}
group.setChildren(dbList);
groupMap.put(group.getId(),group);
}else{
group.getChildren().add(property);
}
}
}
if(!CollectionUtils.isEmpty(groupMap) ){
dataList.addAll(groupMap.values());
}
return dataList;
}
/**
* 获取需要屏蔽的对象
* @param queryMallProperty
* @return
*/
private List<Long> getShieldPropertyIdList(QueryMallProperty queryMallProperty){
List<Long> catIdList = new ArrayList<Long>();
if(null!=queryMallProperty.getCatOneId()){
catIdList.add(queryMallProperty.getCatOneId());
}
if(null!=queryMallProperty.getCatTwoId()){
catIdList.add(queryMallProperty.getCatTwoId());
}
if(null!=queryMallProperty.getCatThreeId()){
catIdList.add(queryMallProperty.getCatThreeId());
}
if(!CollectionUtils.isEmpty(catIdList)){
QueryMallCategoryProperty query = new QueryMallCategoryProperty();
query.setCategoryIdList(catIdList);
List<MallCategoryProperty> dataList=mallCategoryPropertyDao.selectMallCategoryPropertyByQuery(query);
List<Long> shieldIdList = dataList.stream().map(e -> e.getPropertyId()).collect(Collectors.toList());
return shieldIdList;
}
return null;
}
如何使用List的方面的in语句?这里可以给你看一下