商品分类架构
概述
- 商品分类是商品重要组成部分,商品参数和品牌信息都是通过分类与商品进行关联的
- Javashop电商系统只支持三级分类
- 分类由平台统一进行维护,商家在入驻时需要选择对应的经营类目
- 在新增商品时,必须要先选择分类
数据库设计
分类表
表名:es_category
字段名 | 类型与长度 | 备注 |
---|---|---|
category_id | bigint(20) | 主键ID |
name | varchar(100) | 分类名称 |
parent_id | bigint(20) | 父分类ID(一级分类该字段值为0) |
category_path | varchar(255) | 上下级分类ID关系结构(如:0|1|2|3|) |
goods_count | int(10) | 分类下的商品数量(预留字段,当前版本暂时未用到) |
category_order | int(10) | 分类排序值(数值越小,排序越靠前) |
image | varchar(255) | 分类图片 |
is_show | varchar(20) | 是否在客户端显示 YES:是,NO:否 |
adv_image | varchar(255) | 分类广告图片 |
adv_image_link | varchar(255) | 分类广告图片跳转链接 |
custom_sn | varchar(50) | 自定义编码(需唯一) |
结构图
API说明
核心API
实体类参数可参考下方的类图展示
管理端
请求方式 | API地址 | 参数说明 | 作用 |
---|---|---|---|
GET | https://{admin-api-domain}/admin/goods/categories/{parent_id}/children | Long parentId:父分类主键ID(如果获取一级分类,此值传0) | 查询某分类下的子分类数据集合 |
POST | https://{admin-api-domain}/admin/goods/categories | CategoryDO category:分类信息 | 新增分类信息 |
PUT | https://{admin-api-domain}/admin/goods/categories/{id} | CategoryDO category:分类信息,Long id:分类主键ID | 修改分类信息 |
GET | https://{admin-api-domain}/admin/goods/categories/{id} | Long id:分类主键ID | 根据主键ID获取分类信息 |
DELETE | https://{admin-api-domain}/admin/goods/categories/{id} | Long id:分类主键ID | 删除分类信息 |
商家端
请求方式 | API地址 | 参数说明 | 作用 |
---|---|---|---|
GET | https://{seller-api-domain}/seller/goods/category/{category_id}/children | Long categoryId:分类主键ID | 商品发布时,获取当前店铺选择的经营类目信息 |
GET | https://{seller-api-domain}/seller/goods/category/{parent_id}/all-children | Long parentId:父分类主键ID(如果获取一级分类,此值传0) | 查询某分类下的子分类数据集合 |
GET | https://{seller-api-domain}/seller/goods/category/seller/children | 无 | 获取商家所有经营类目数据 |
GET | https://{seller-api-domain}/seller/goods/export/categories | 无 | 导出店铺经营类目信息 |
用户端
请求方式 | API地址 | 参数说明 | 作用 |
---|---|---|---|
GET | https://{buyer-api-domain}/buyer/goods/categories/{parent_id}/children | Long parentId:父分类主键ID(如果获取一级分类,此值传0) | 查询某分类下的子分类数据集合 |
类图展示
API类图
实体类
缓存设计
由于商品分类数据较多,而且会频繁读取,不适合直接读库,因此我们把分类信息放入redis缓存中进行存储
缓存数据结构
缓存key值:{GOODS_CAT}_ALL
缓存value值:List<CategoryVO>
,格式如下:
只展示主要数据
[
{
"category_id":"1",
"name":"服装鞋靴",
"parent_id":"0",
"category_path":"0|1|",
"children":[
{
"category_id":"10",
"name":"男装",
"parent_id":"1",
"category_path":"0|1|10|",
"children":[
{
"category_id":"20",
"name":"衬衫",
"parent_id":"10",
"category_path":"0|1|10|20|",
"children":null
},
{
"category_id":"21",
"name":"西服",
"parent_id":"10",
"category_path":"0|1|11|21|",
"children":null
}
]
},
{
"category_id":"11",
"name":"女装",
"parent_id":"1",
"category_path":"0|1|11|",
"children":[
{
"category_id":"22",
"name":"连衣裙",
"parent_id":"11",
"category_path":"0|1|11|22|",
"children":null
},
{
"category_id":"23",
"name":"短裙",
"parent_id":"11",
"category_path":"0|1|11|23|",
"children":null
}
]
}
]
}
]
代码展示
com.enation.app.javashop.service.goods.impl.CategoryManagerImpl#getByParentId
@Override
public List<CategoryVO> getByParentId(Long parentId, Boolean showHidden) {
//如果不显示隐藏的分类,应为会员端调用 介于使用频率较高 则首先从缓存中取值 去不到的话再从数据库中取值
//显示隐藏的分类,应为后台管理端调用 则从数据库中取值
if (!showHidden) {
List<CategoryDO> categoryList = this.lambdaQuery().orderByAsc(CategoryDO::getCategoryOrder).list();
//获取分类树
return this.getMenuTree(categoryList, parentId);
}
//从缓存中取值
List<CategoryVO> categoryVOList = (List<CategoryVO>) this.cache.get(this.getCacheKey());
//如果缓存中为空则从数据库中取值
if (CollUtil.isEmpty(categoryVOList)) {
List<CategoryDO> categoryList = this.lambdaQuery()
.eq(CategoryDO::getIsShow, CommonStatusEnum.YES)
.orderByAsc(CategoryDO::getCategoryOrder)
.list();
categoryVOList = this.getMenuTree(categoryList, parentId);
//将分类信息放入缓存
this.cache.put(this.getCacheKey(), categoryVOList);
}
//获取分类树
return categoryVOList;
}
/**
* 获取分类缓存key
*
* @return String
*/
private String getCacheKey() {
return CachePrefix.GOODS_CAT.getPrefix() + "ALL";
}