总结:
功能:根据三级分类查询group_name
查询tb_group表即可:根据cid查询name
tb_spec_group表:id name cid
http://api.leyou.com/api/item/spec/groups/76
返回:ResponseEntity
功能:根据关键字,分页,每页行数,上下架,查询结果,显示:spu_id, spu_titile, 商品分类,商品品牌
查询tb_spec_spu表,: 根据key, saleble, page, rows查询spu对象
tb_spu表: id, title, spu_titile, group_id, cid1, cid2, cid3, brandId, valid, create_time, last_updat_time
http://api.leyou.com/api/item/spu/page?key=&saleable=true&page=1&rows=5
返回:ResponseEntity<PageRessult> PageResult在List上增加了总条数,总页数,
注意点:
1.因为tb_spec_spu只有cid, brand_id字段,即对应的pojoSpu也只有Integer cid, Integer bid, 所以要包装Spu–SpuBo(添加了cname, 和bname字段)
根据:关键字,分页,每页行数,上下架查询得到List对象后:
2. 关键字查询: Example.Criteria添加模糊查询条件,riteria.andLike(“title”, “%” + key + “%”);
3. 上下架查询: Example.Criteria添加上下架字段查询,criteria.andEqualTo(“saleable”, saleable);
4. 页数,每页条数查询:PageHelper.startPage(page, rows);
根据Spu对象,显示
商品分类,商品品牌查询:
根据spu对象,得到spu对象的cid, 根据cid1, cid2, cid3查询tb_categry表,得到分类集合(三个级的分类集合),写入SpuBo类。
SelectByIdListMapper<Category, Long>
List names = this.categoryService.queryNamesByIds(Arrays.asList(spu.getCid1(), spu.getCid2(), spu.getCid3()));
spuBo.setCname(StringUtils.join(names, “/”));
根据spu对象,得到spu对象的bid, 根据bid查询tb_brand表,得到对应品牌名称,写入SpuBo对象
spu,_id, spu_titile显示
因为SpuBo继承了Spu,tb_spu表里有这些字段,所以也是有这些属性的。
在查询得到List对象后,要用forEach对每一个Spu,分别查询分类名称和品牌名称
表结构
前面实现了:查询全部分类,查询全部品牌
接触了三张表:tb_category,tb_brand,tb_brand_category
tb_category: id name parant_id, is_parent_id,sort
tb_brand: id name image
tb_brand_category: id_brand, id_category
易错:由于品牌和分类是多对多的关系,所以在tb_category, tb_brand 两张表中,只有自己的id,不是根据外键关联,关联是通过中间表关联。
五张表:
tb_spec_group: id cid name
tb_spec_params: id cid group_id name generic searching numeric segment
tb_spu: id cid1 cid2 cid3 title brand_id
tb_spu_detail : spu_id, description special_spec
tb_sku: id spu_id,indexes, own_spec
点击手机,查看手机的规格参数:
点击手机:
Request URL: http://api.leyou.com/api/item/spec/groups/76
-
请求方式:get
-
请求路径:/spec/groups/{cid} ,这里通过路径占位符传递商品分类的id
-
请求参数:商品分类id
-
返回结果:页面是直接把
resp.data
赋值给了groups:
那么我们返回的应该是规格组SpecGroup的集合
规格参数查询:根据group_id查询spec_params
后端代码
实体类
@Table(name = "tb_spec_group")
public class SpecGroup {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private Long cid;
private String name;
@Transient
private List<SpecParam> params;
// getter和setter省略
}
@Table(name = "tb_spec_param")
public class SpecParam {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private Long cid;
private Long groupId;
private String name;
@Column(name = "`numeric`")
private Boolean numeric;
private String unit;
private Boolean generic;
private Boolean searching;
private String segments;
// getter和setter ...
}
mapper
public interface SpecGroupMapper extends Mapper<SpecGroup> {
}
controller
Request URL: http://api.leyou.com/api/item/spec/groups/76
-
请求方式:get
-
请求路径:/spec/groups/{cid} ,这里通过路径占位符传递商品分类的id
-
请求参数:商品分类id
-
返回结果:页面是直接把
resp.data
赋值给了groups:
那么我们返回的应该是规格组SpecGroup的集合
@RestController
@RequestMapping("spec")
public class SpecificationController {
@Autowired
private SpecificationService specificationService;
/**
* 根据分类id查询分组
* @param cid
* @return
*/
@GetMapping("groups/{cid}")
public ResponseEntity<List<SpecGroup>> queryGroupsByCid(@PathVariable("cid")Long cid){
List<SpecGroup> groups = this.specificationService.queryGroupsByCid(cid);
if (CollectionUtils.isEmpty(groups)){
return ResponseEntity.notFound().build();
}
return ResponseEntity.ok(groups);
}
}
service
@Service
public class SpecificationService {
@Autowired
private SpecGroupMapper groupMapper;
/**
* 根据分类id查询分组
* @param cid
* @return
*/
public List<SpecGroup> queryGroupsByCid(Long cid) {
SpecGroup specGroup = new SpecGroup();
specGroup.setCid(cid);
return this.groupMapper.select(specGroup);
}
}
页面访问测试:
目前,我们数据库只为手机分类(76)提供了规格组:
我们访问:http://api.leyou.com/api/item/spec/groups/76
规格参数查询
查看页面控制台,发现请求已经发出:
报404,因为我们还没有实现后台逻辑,接下来就去实现。
后台实现
SpecificationController
分析:
- 请求方式:GET
- 请求路径:/spec/params
- 请求参数:gid,分组id
- 返回结果:该分组下的规格参数集合
List<SpecParam>
代码:
/**
* 根据条件查询规格参数
* @param gid
* @return
*/
@GetMapping("params")
public ResponseEntity<List<SpecParam>> queryParams(@RequestParam("gid")Long gid){
List<SpecParam> params = this.specificationService.queryParams(gid);
if (CollectionUtils.isEmpty(params)){
return ResponseEntity.notFound().build();
}
return ResponseEntity.ok(params);
}
SpecificationService
@Autowired
private SpecParamMapper paramMapper;
/**
* 根据条件查询规格参数
* @param gid
* @return
*/
public List<SpecParam> queryParams(Long gid) {
SpecParam param = new SpecParam();
param.setGroupId(gid);
return this.paramMapper.select(param);
}
SecParamMapper
public interface SpecParamMapper extends Mapper<SpecParam> {
}
商品查询
效果预览
可以看出整体是一个table,然后有新增按钮。是不是跟昨天写品牌管理很像?
因此接下来,我们编写接口即可。
后台提供分页查询SPU的功能
在leyou-item-interface工程中添加实体类:
SPU
@Table(name = "tb_spu")
public class Spu {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private Long brandId;
private Long cid1;// 1级类目
private Long cid2;// 2级类目
private Long cid3;// 3级类目
private String title;// 标题
private String subTitle;// 子标题
private Boolean saleable;// 是否上架
private Boolean valid;// 是否有效,逻辑删除用
private Date createTime;// 创建时间
private Date lastUpdateTime;// 最后修改时间
// 省略getter和setter
}
SPU详情
@Table(name="tb_spu_detail")
public class SpuDetail {
@Id
private Long spuId;// 对应的SPU的id
private String description;// 商品描述
private String specialSpec;// 商品特殊规格的名称及可选值模板
private String genericSpec;// 商品的全局规格属性
private String packingList;// 包装清单
private String afterService;// 售后服务
// 省略getter和setter
}
4.4.2.mapper
public interface SpuMapper extends Mapper<Spu> {
}
4.3.3.controller
先分析:
-
请求方式:GET
-
请求路径:/spu/page
-
请求参数:
- page:当前页
- rows:每页大小
- key:过滤条件
- saleable:上架或下架
Request URL: http://api.leyou.com/api/item/spu/page?key=&saleable=true&page=1&rows=5
-
返回结果:商品SPU的分页信息。
-
要注意,页面展示的是商品分类和品牌名称,而数据库中保存的是id,怎么办?
我们可以新建一个类,继承SPU,并且拓展cname和bname属性,写到
leyou-item-interface
public class SpuBo extends Spu { String cname;// 商品分类名称 String bname;// 品牌名称 // 略 。。 }
-
编写controller代码:
我们把与商品相关的一切业务接口都放到一起,起名为GoodsController,业务层也是这样
@Controller
public class GoodsController {
@Autowired
private GoodsService goodsService;
@GetMapping("spu/page")
public ResponseEntity<PageResult<SpuBo>> querySpuBoByPage(
@RequestParam(value = "key", required = false)String key,
@RequestParam(value = "saleable", required = false)Boolean saleable,
@RequestParam(value = "page", defaultValue = "1")Integer page,
@RequestP@aram(value = "rows", defaultValue = "5")Integer rows
){
PageResult<SpuBo> pageResult = this.goodsService.querySpuBoByPage(key, saleable, page, rows);
if(CollectionUtils.isEmpty(pageResult.getItems())){
return ResponseEntity.notFound().build();
}
return ResponseEntity.ok(pageResult);
}
}
4.4.4.service
所有商品相关的业务(包括SPU和SKU)放到一个业务下:GoodsService。
@Service
public class GoodsService {
@Autowired
private SpuMapper spuMapper;
@Autowired
private CategoryService categoryService;
@Autowired
private BrandMapper brandMapper;
public PageResult<SpuBo> querySpuBoByPage(String key, Boolean saleable, Integer page, Integer rows) {
Example example = new Example(Spu.class);
Example.Criteria criteria = example.createCriteria();
// 搜索条件
if (StringUtils.isNotBlank(key)) {
criteria.andLike("title", "%" + key + "%");
}
if (saleable != null) {
criteria.andEqualTo("saleable", saleable);
}
// 分页条件
PageHelper.startPage(page, rows);
// 执行查询
List<Spu> spus = this.spuMapper.selectByExample(example);
PageInfo<Spu> pageInfo = new PageInfo<>(spus);
List<SpuBo> spuBos = new ArrayList<>();
spus.forEach(spu->{
SpuBo spuBo = new SpuBo();
// copy共同属性的值到新的对象
BeanUtils.copyProperties(spu, spuBo);
// 查询分类名称
List<String> names = this.categoryService.queryNamesByIds(Arrays.asList(spu.getCid1(), spu.getCid2(), spu.getCid3()));
spuBo.setCname(StringUtils.join(names, "/"));
// 查询品牌的名称
spuBo.setBname(this.brandMapper.selectByPrimaryKey(spu.getBrandId()).getName());
spuBos.add(spuBo);
});
return new PageResult<>(pageInfo.getTotal(), spuBos);
}
}
4.4.5.Category中拓展查询名称的功能
页面需要商品的分类名称需要在这里查询,因此要额外提供查询分类名称的功能,
在CategoryService中添加功能:
public List<String> queryNamesByIds(List<Long> ids) {
List<Category> list = this.categoryMapper.selectByIdList(ids);
List<String> names = new ArrayList<>();
for (Category category : list) {
names.add(category.getName());
}
return names;
// return list.stream().map(category -> category.getName()).collect(Collectors.toList());
}
mapper的selectByIdList方法是来自于通用mapper。不过需要我们在mapper上继承一个通用mapper接口:
public interface CategoryMapper extends Mapper<Category>, SelectByIdListMapper<Category, Long> {
}
4.5.测试
刷新页面,查看效果:
http://manage.leyou.com/#/item/list
http://api.leyou.com/api/item/spu/page?key=&saleable=true&page=1&rows=5