目录
一 全局异常处理/事务控制关系
二 优化商品分类列表三级嵌套菜单
1 商品分类查询优化
1.1 现有代码存在的问题
问题说明:如果经过2次for循环,连接数据库的次数将会很多,直接影响查询效率.
优化思路:尽可能降低查询次数,同时满足用户查询需求
1.2 优化策略
数据结构:Map<K,V>
key="父级ID",value =List<当前父级的子级>
用法:如果想获取任意父级的子级,map.get(父级ID)
误区:value只有父级的子级信息,没有嵌套结构,
Map<父级ID,List<ItemCat{id = xx,name = xx,children=null}>>
1.3 封装Map集合解析
原理解析:
1.4 封装Map实现
把业务层实现类重写的方法注释掉,重写该方法
1)第一步:封装一个map集合:
/**
* 1.封装Map集合 Map<Key=父级ID,value=List<ItemCat对象>>
* 2.说明: 将所有的数据库的父子关系,进行封装.(没有嵌套!!!!)
* 3.优势: 只查询一次数据库,就可以完成父子关系的封装.
策略:
* 1. key不存在, 准备一个新List集合,将自己当作第一个元素追加
* 2. key存在, 获取原有list集合,将自己追加.
*
*/
public Map<Integer,List<ItemCat>> initMap(){
//Map中包含了所有的父子级关系.
Map<Integer,List<ItemCat>> map = new HashMap<>();
//1.查询item_cat表中的所有的记录(1/2/3级菜单)
List<ItemCat> itemCatList = itemCatMapper.selectList(null);
//2.实现数据的封装
for(ItemCat itemCat : itemCatList){
int key = itemCat.getParentId();
if(map.containsKey(key)){ //存在
List<ItemCat> list = map.get(key);
//将自己追加到其中
list.add(itemCat);
}else{ //不存在: 准备List集合,将自己作为第一个元素封装
List<ItemCat> list = new ArrayList<>();
list.add(itemCat);
map.put(key,list);
}
}
//将封装的数据进行返回.
return map;
}
2)在重写商品分类的方法时,分别查询1-2二级商品信息和1-2-3三级商品信息,对他们的方法分别调用,代码如下:
/**
* 1.封装Map集合 Map<Key=父级ID,value=List<ItemCat对象>>
* 2.说明: 将所有的数据库的父子关系,进行封装.(没有嵌套!!!!)
* 3.优势: 只查询一次数据库,就可以完成父子关系的封装.
策略:
* 1. key不存在, 准备一个新List集合,将自己当作第一个元素追加
* 2. key存在, 获取原有list集合,将自己追加.
*
*/
public Map<Integer,List<ItemCat>> initMap(){
//Map中包含了所有的父子级关系.
Map<Integer,List<ItemCat>> map = new HashMap<>();
//1.查询item_cat表中的所有的记录(1/2/3级菜单)
List<ItemCat> itemCatList = itemCatMapper.selectList(null);
//2.实现数据的封装
for(ItemCat itemCat : itemCatList){
int key = itemCat.getParentId();
if(map.containsKey(key)){ //存在
List<ItemCat> list = map.get(key);
//将自己追加到其中
list.add(itemCat);
}else{ //不存在: 准备List集合,将自己作为第一个元素封装
List<ItemCat> list = new ArrayList<>();
list.add(itemCat);
map.put(key,list);
}
}
//将封装的数据进行返回.
return map;
}
/**
* level 1 只查询一级商品分类
* 2 查询一级/二级 嵌套封装
* 3 查询一级/二级/三级 嵌套封装
* @param level
* @return
*/
@Override
public List<ItemCat> findItemCatList(Integer level) {
long startTime = System.currentTimeMillis();
//Map集合里边封装的是所有的父子级关系.
Map<Integer,List<ItemCat>> map = initMap();
if(level == 1){ //只获取1级菜单. parent_id = 0
return map.get(0);
}
//用户查询1/2级商品分类信息
if(level == 2){
return getLevel2List(map);
}
//如果程序执行到这里,则说明用户查询的是1-2-3级菜单
List<ItemCat> list = getLevel3List(map);
long endTime = System.currentTimeMillis();
System.out.println("耗时:"+(endTime-startTime)+"毫秒");
return list;
}
public List<ItemCat> getLevel3List(Map<Integer, List<ItemCat>> map) {
//1.先查询1-2级
List<ItemCat> oneList = getLevel2List(map);
//2.遍历集合
for(ItemCat oneItemCat : oneList){
//获取二级集合信息
List<ItemCat> twoList = oneItemCat.getChildren();
if(twoList == null || twoList.size() ==0){
//当前一级菜单没有二级元素.结束本次循环,开始下一次!!!
continue;
}
//该元素有二级,应该查询三级.
for(ItemCat twoItemCat : twoList){
List<ItemCat> threeList = map.get(twoItemCat.getId());
twoItemCat.setChildren(threeList);
}
}
return oneList;
}
//查询一级和二级信息
public List<ItemCat> getLevel2List(Map<Integer, List<ItemCat>> map) {
//思路: 先查询一级,之后循环遍历,再次封装2级
//1.获取一级
List<ItemCat> oneList = map.get(0);
for(ItemCat oneItemCat : oneList){
//2.如何根据一级查询二级? 通过Map集合获取
List<ItemCat> twoList = map.get(oneItemCat.getId());
//3.实现了一级二级的封装
oneItemCat.setChildren(twoList);
}
return oneList;
}
三 商品分类状态的修改
1 页面JS分析--itemCat.vue
2 业务接口文档说明
- 请求路径: /itemCat/status/{id}/{status}
- 请求类型: put
- 请求参数:
参数名称 | 参数说明 | 备注 |
---|---|---|
id | 用户ID值 | 不能为null |
status | 用户的状态信息 | 不能为null |
- 返回值: SysResult对象
参数名称 | 参数说明 | 备注 |
---|---|---|
status | 状态信息 | 200表示服务器请求成功 201表示服务器异常 |
msg | 服务器返回的提示信息 | 可以为null |
data | 服务器返回的业务数据 | 可以为null |
3 编辑ItemCatController
/**
* 业务需求: 实现状态的修改
* 1.URL: /itemCat/status/{id}/{status}
* 2.请求类型: put
* 3.返回值: SysResult对象
*/
@PutMapping("/status/{id}/{status}")
public SysResult updateStatus(ItemCat itemCat){
itemCatService.updateStatus(itemCat);
return SysResult.success();
}
4 编辑ItemCatServiceImpl
@Override
public void updateStatus(ItemCat itemCat) {//id/status
itemCatMapper.updateById(itemCat);
}
四 商品分类的新增
1 业务说明
- 如果是一级菜单,则不需要勾选父级.
- 如果有父级,只能勾选1-2级.
2 页面JS说明
- 商品分类菜单信息 2级展现JS
async findParentItemCatList() { //动态获取商品分类信息 type=2表示获取2级商品分类信息 const { data: result } = await this.$http.get("/itemCat/findItemCatList/2") if (result.status !== 200) return this.$message.error("获取商品分类列表失败!!") this.parentItemCatList = result.data }, //2.发起Ajax请求,实现新增操作 async addItemCatForm() { //先将整个表单进行校验 this.$refs.itemCatFormRef.validate(async validate => { if (!validate) return const { data: result } = await this.$http.post("/itemCat/saveItemCat", this.itemCatForm) if (result.status !== 200) return this.$message.error("新增商品分类失败") this.$message.success("新增商品分类成功!!!") //新增成功,则刷新分类列表信息 this.findItemCatList(); this.addItemCatDialogVisible = false }) },
3 业务接口文档说明分析
- 请求路径: /itemCat/saveItemCat
- 请求类型: post
- 请求参数: 表单数据
参数名称 | 参数说明 | 备注 |
---|---|---|
name | 商品分类名称 | 不能为null |
parentId | 用户父级ID | 不能为null |
level | 分类级别 | 1 2 3 商品分类级别 |
- 返回值: SysResult对象
参数名称 | 参数说明 | 备注 |
---|---|---|
status | 状态信息 | 200表示服务器请求成功 201表示服务器异常 |
msg | 服务器返回的提示信息 | 可以为null |
data | 服务器返回的业务数据 | 可以为null |
4 编辑ItemCatController
/**
* 需求: 实现商品分类入库
* URL: /itemCat/saveItemCat
* 类型: post类型
* 参数: itemCat对象~~~JSON
* 返回值: SysResult
*/
@PostMapping("/saveItemCat")
public SysResult saveItemCat(@RequestBody ItemCat itemCat){
itemCatService.saveItemCat(itemCat);
return SysResult.success();
}
5 编辑ItemCatServiceImpl
@Override
@Transactional
public void saveItemCat(ItemCat itemCat) {
itemCat.setStatus(true);
itemCatMapper.insert(itemCat);
}
五 商品分类删除操作
1 业务说明
1.如果被删除的标签是3级标签,则可以直接删除.
2.如果被删除的标签是2级标签,则需要先删除3级,再删除2级.
3.如果被删除的标签是1级标签,则需要先删除3级,在删除2级.最后删除1级.
2 页面JS分析
3 业务接口文档说明
- 请求路径: /itemCat/deleteItemCat
- 请求类型: delete
- 业务描述: 当删除节点为父级时,应该删除自身和所有的子节点
- 请求参数:
参数名称 | 参数说明 | 备注 |
---|---|---|
id | 用户id号 | 不能为null |
level | 商品分类级别 一级,二级,三级 |
- 返回值结果 SysResult对象
参数名称 | 参数说明 | 备注 |
---|---|---|
status | 状态信息 | 200表示服务器请求成功 201表示服务器异常 |
msg | 服务器返回的提示信息 | 可以为null |
data | 服务器返回的业务数据 | 可以为null |
4 编辑ItemCatController
/**
* 商品分类删除操作
* URL: /itemCat/deleteItemCat?id=1&level=3
* 类型: delete
* 参数: id/level
* 返回值: SysResult对象
*/
@DeleteMapping("/deleteItemCat")
public SysResult deleteItemCat(Integer id,Integer level){
itemCatService.deleteItemCat(id,level);
return SysResult.success();
}
5 编辑ItemCatServiceImpl
/**
* 1.易用性,性能高
*
* @param id
* @param level
*/
@Override
@Transactional //添加事务的控制
public void deleteItemCat(Integer id, Integer level) {
if(level == 3){
itemCatMapper.deleteById(id);
}
if(level == 2){
//1.先删除3级菜单
QueryWrapper queryWrapper = new QueryWrapper();
queryWrapper.eq("parent_id",id);
itemCatMapper.delete(queryWrapper);
//2.再删除2级菜单
itemCatMapper.deleteById(id);
}
//如果程序执行到这一行 则需要先删除3级/再删除2级/最后删除1级
if(level == 1){
List<Integer> ids = new ArrayList<>();
//1.查询所有的二级菜单
QueryWrapper queryWrapper = new QueryWrapper();
queryWrapper.eq("parent_id",id);
List<ItemCat> twoList = itemCatMapper.selectList(queryWrapper);
for (ItemCat twoItemCat : twoList){
//删除3级菜单
queryWrapper.clear();
queryWrapper.eq("parent_id",twoItemCat.getId());
//delete from item_cat where parent_id = "二级ID"
itemCatMapper.delete(queryWrapper);
//获取需要删除的ID信息
ids.add(twoItemCat.getId());
}
//将所有的二级/一级ID封装到List集合中
ids.add(id);
itemCatMapper.deleteBatchIds(ids);
}
}
六 商品管理中商品列表的展现
1 表设计
item表设计 商品表(商品的基本信息)
2.item_desc 表设计 商品详情表 item_desc 专门保存大字段(html/url/图片网址…)
设计要求: item表与itemDesc表应该一对一. item.id = itemDesc.id
2 Item POJO对象设计
/**
* @author 刘昱江
* 时间 2021/4/7
*/
@TableName("item")
@Data
@Accessors(chain = true)
public class Item extends BasePojo{
@TableId(type = IdType.AUTO)
private Integer id; //商品Id号
private String title; //商品标题信息
private String sellPoint; //卖点信息
private Integer price; //商品价格 精度问题!!! 将小数扩大100倍 页面中将数据缩小100倍
private Integer num; //商品数量
private String images; //商品图片 1.jpg,2.jpg,3.jpg
private Integer itemCatId; //商品分类ID号
private Boolean status; //状态信息 0 下架 1 上架
}
3 商品列表展现
1 需求说明
要求: 当用户打开商品列表页面时,应该采用分页的方式 展现商品信息.
2 页面JS分析
3 业务接口文档实现
- 请求路径: /item/getItemList?query=&pageNum=1&pageSize=10
- 请求类型: get
- 请求参数: 使用pageResult对象接收
参数名称 | 参数说明 | 备注信息 |
---|---|---|
query | 用户查询的数据 | 可以为null |
pageNum | 分页查询的页数 | 必须赋值不能为null |
pageSize | 分页查询的条数 | 必须赋值不能为null |
- 返回值结果:
参数名称 | 参数说明 | 备注 |
---|---|---|
status | 状态信息 | 200表示服务器请求成功 201表示服务器异常 |
msg | 服务器返回的提示信息 | 可以为null |
data | 服务器返回的业务数据 | 商品分页对象 |
4 编辑ItemController
@RestController
@CrossOrigin
@RequestMapping("/item")
public class ItemController {
@Autowired
private ItemService itemService;
/**
* 业务逻辑: 查询商品分页
* URL: /item/getItemList?query=&pageNum=1&pageSize=10
* 类型: GET
* 接收参数: PageResult对象
* 返回值: SysResult
*/
@GetMapping("/getItemList")
public SysResult getItemList(PageResult pageResult){//3个参数
//返回5个参数,total/rows分页记录
pageResult = itemService.getItemList(pageResult);
return SysResult.success(pageResult);
}
}
5 编辑ItemServiceImpl
@Service
public class ItemServiceImpl implements ItemService{
@Autowired
private ItemMapper itemMapper;
/**
* 实现商品的分页操作
* @param pageResult
* @return
*/
@Override
public PageResult getItemList(PageResult pageResult) {
//参数1:页数 参数2: 条数
Page<Item> page = new Page<>(pageResult.getPageNum(),pageResult.getPageSize());
QueryWrapper queryWrapper = new QueryWrapper();
//如果参数有值true 如果query参数没有值 false
boolean flag = StringUtils.hasLength(pageResult.getQuery());
queryWrapper.like(flag,"title",pageResult.getQuery());
//利用MP的分页API 实现查询
page = itemMapper.selectPage(page,queryWrapper);
long total = page.getTotal();
List<Item> list = page.getRecords();
//封装成功之后原来3个参数 封装之后变为5个 之后返回
return pageResult.setTotal(total).setRows(list);
}
}