本文承接上一篇《从0到1搭建精品电商项目(用于毕设、简历等)—— 项目介绍与初步搭建》,上一次只是把项目的地基打好了,还需要继续完善各类功能。
老规矩,源码请联系公众号:
还是从service层写起。
创建操作轮播图的service接口:
package com.wjw.service;
import com.wjw.pojo.Carousel;
import java.util.List;
/**
* 2 * @Author: 小王同学
* 3 * @Date: 2020/12/24 15:08
* 4
*/
public interface CarouselService {
/**
* 查询所有轮播图列表
* @param isShow
* @return
*/
public List<Carousel> queryAll(Integer isShow);
}
创建接口实现类:
package com.wjw.service.impl;
import com.wjw.mapper.CarouselMapper;
import com.wjw.pojo.Carousel;
import com.wjw.service.CarouselService;
import org.springframework.beans.factory.annotation.Autowired;
import tk.mybatis.mapper.entity.Example;
import java.util.List;
/**
* 2 * @Author: 小王同学
* 3 * @Date: 2020/12/24 15:11
* 4
*/
public class CarouselServiceImpl implements CarouselService {
@Autowired
private CarouselMapper carouselMapper;
/**
* 查询所有轮播图列表
*
* @param isShow
* @return
*/
@Transactional(propagation = Propagation.SUPPORTS)
@Override
public List<Carousel> queryAll(Integer isShow) {
Example example = new Example(Carousel.class);
example.orderBy("sort").desc();
Example.Criteria criteria = example.createCriteria();
criteria.andEqualTo("isShow", isShow);
List<Carousel> result = carouselMapper.selectByExample(example);
return result;
}
}
创建表示“是否”的枚举(common模块):
package com.wjw.enums;
/**
* 2 * @Author: 小王同学
* 3 * @Date: 2020/12/21 21:04
* 4
*/
public enum YesOrNo {
NO(0, "否"),
YES(1, "是");
public final Integer type;
public final String value;
YesOrNo(Integer type, String value) {
this.type = type;
this.value = value;
}
}
创建控制器IndexController用于控制首页的展示:
package com.wjw.controller;
import com.wjw.enums.YesOrNo;
import com.wjw.pojo.Carousel;
import com.wjw.service.CarouselService;
import com.wjw.utils.WJWJSONResult;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import springfox.documentation.annotations.ApiIgnore;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.List;
/**
* 2 * @Author: 小王同学
* 3 * @Date: 2020/12/20 16:29
* 4
*/
@Api(value = "首页", tags = {
"首页展示的相关接口"})
@RestController
@RequestMapping("index")
public class IndexController {
@Autowired
private CarouselService carouselService;
@ApiOperation(value = "获取首页轮播图列表", notes = "获取首页轮播图列表", httpMethod = "GET")
@GetMapping("/carousel")
public WJWJSONResult carousel() {
List<Carousel> list = carouselService.queryAll(YesOrNo.YES.type);
return WJWJSONResult.ok(list);
}
}
分类之间存在递归关系,可以将所有的商品放在同一张表中,就可以无限地递归了。
slogan表示如下:
页面加载时,没有必要加载很多的数据,前期只用把大分类加载完毕就行了,当用户把鼠标移过来时进行懒加载,根据鼠标悬停的类别的id再去查询它的子类别。
基本思路:
创建商品分类服务接口:
package com.wjw.service;
import com.wjw.pojo.Carousel;
import com.wjw.pojo.Category;
import java.util.List;
/**
* 2 * @Author: 小王同学
* 3 * @Date: 2020/12/24 15:08
* 4
*/
public interface CategoryService {
/**
* 查询所有一级分类
* @return
*/
public List<Category> queryAllRootLevelCat();
}
创建相应的实现类:
package com.wjw.service.impl;
import com.wjw.mapper.CarouselMapper;
import com.wjw.mapper.CategoryMapper;
import com.wjw.pojo.Carousel;
import com.wjw.pojo.Category;
import com.wjw.service.CarouselService;
import com.wjw.service.CategoryService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import tk.mybatis.mapper.entity.Example;
import java.util.List;
/**
* 2 * @Author: 小王同学
* 3 * @Date: 2020/12/24 15:11
* 4
*/
@Service
public class CategoryServiceImpl implements CategoryService {
@Autowired
private CategoryMapper categoryMapper;
/**
* 查询所有一级分类
*
* @return
*/
@Transactional(propagation = Propagation.SUPPORTS)
@Override
public List<Category> queryAllRootLevelCat() {
Example example = new Example(Category.class);
Example.Criteria criteria = example.createCriteria();
criteria.andEqualTo("type", 1);
List<Category> result = categoryMapper.selectByExample(example);
return result;
}
}
在首页IndexController
中添加获取商品一级分类的方法:
@Autowired
private CategoryService categoryService;
@ApiOperation(value = "获取商品分类(一级分类)", notes = "获取商品分类(一级分类)", httpMethod = "GET")
@GetMapping("/cats")
public WJWJSONResult cats(){
List<Category> list = categoryService.queryAllRootLevelCat();
return WJWJSONResult.ok(list);
}
自连接相当于变种的多表查询,通用mapper做不到多表查询,所以要写自定义sql语句。
SELECT
f.id as id,
f.`name` as `name`,
f.type as type,
f.father_id as fatherId,
c.id as subId,
c.`name` as subName,
c.type as subType,
c.father_id as subFatherId
FROM
`category` f
LEFT JOIN
`category` c
ON
f.id = c.father_id
WHERE
f.father_id = 1;
左边的是二级分类,右边的是三级分类
将自动生成的CategoryMapper
拷贝一份命名为CategoryMapperCustom
package com.wjw.mapper;
import com.wjw.my.mapper.MyMapper;
import com.wjw.pojo.Category;
import com.wjw.pojo.vo.CategoryVO;
import java.util.List;
public interface CategoryMapperCustom {
public List<CategoryVO> getSubCatList(Integer rootCatId);
}
定义相应的vo:
vo和bo类似,是业务型的,从前端封装过后的数据传入到后端;从内部传给前端显示需要定义为vo。
分类表一般不纳入后续的分库分表的考虑,所以这里的id是int型。
三级分类的VO:
package com.wjw.pojo.vo;
/**
* 2 * @Author: 小王同学
* 3 * @Date: 2020/12/24 16:46
* 4 三级子分类的vo
*/
public class SubCategoryVO {
private Integer subId;
private String subName;
private String subType;
private Integer subFatherId;
......
}
二级分类的VO:
package com.wjw.pojo.vo;
import java.util.List;
/**
* 2 * @Author: 小王同学
* 3 * @Date: 2020/12/24 16:42
* 4 二级分类VO
*/
public class CategoryVO {
private Integer id;
private String name;
private String type;
private Integer fatherId;
/**
* 三级分类vo list
*/
private List<SubCategoryVO> subCatList;
}
定义相应的mapper.xml文件,同样先复制CategoryMapper.xml:
再将上面写好的sql语句转化为Mybatis的xml语法
DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.wjw.mapper.CategoryMapperCustom" >
<resultMap id="myCategoryVO" type="com.wjw.pojo.vo.CategoryVO">
<id column="id" property="id"/>
<result column="name" property="name"/>
<result column="type" property="type"/>
<result column="fatherId" property="fatherId"/>
<collection property="subCatList" ofType="com.wjw.pojo.vo.SubCategoryVO">
<id column="subId" property="subId"/>
<result column="subName" property="subName"/>
<result column="subType" property="subType"/>
<result column="subFatherId" property="subFatherId"/>
collection>
resultMap>
<select id="getSubCatList" resultMap="myCategoryVO" parameterType="int">
SELECT
f.id as id,
f.`name` as `name`,
f.type as type,
f.father_id as fatherId,
c.id as subId,
c.`name` as subName,
c.type as subType,
c.father_id as subFatherId
FROM
`category` f
LEFT JOIN
`category` c
ON
f.id = c.father_id
WHERE
f.father_id = #{rootCatId};
select>
mapper>
定义查询二、三级分类的service接口:
CategoryServiceImpl
中添加方法
@Autowired
private CategoryMapperCustom categoryMapperCustom;
/**
* 根据一级分类id查询子分类信息
*
* @param rootCatId
* @return
*/
@Transactional(propagation = Propagation.SUPPORTS)
@Override
public List<CategoryVO> getSubCatList(Integer rootCatId) {
return categoryMapperCustom.getSubCatList(rootCatId);
}
IndexController中添加鼠标移动上去后将要实现的查询二、三级分类的功能:
前端请求
@ApiOperation(value = "获取商品子分类", notes = "获取商品子分类", httpMethod = "GET")
@GetMapping("/subCat/{rootCatId}")
public WJWJSONResult subCat(
@ApiParam(name = "rootCatId", value = "一级分类id", required = true)
@PathVariable Integer rootCatId){
if (rootCatId == null) {
return WJWJSONResult.errorMsg("分类不存在");
}
List<CategoryVO> list = categoryService.getSubCatList(rootCatId);
return WJWJSONResult.ok(list);
}
前端依然使用的懒加载模式,前端会根据页面的滚动进行加载。
页面上每个种类下面包含了六个子类。
相应的数据库:
category 表:
cat_id是子分类的id,root_cat_id是一级分类id,商品推荐是按照一级分类去分的。
定义三表联合查询的sql:
SELECT
f.id as rootCatId,
f.`name` as rootCatName,
f.slogan as slogan,
f.cat_image as catImage,
f.bg_color as bgColor,
i.id as itemId,
i.item_name as itemName,
ii.url as itemUrl,
i.created_time as createdTime
FROM
category f
LEFT JOIN
items i
ON
f.id = i.root_cat_id
LEFT JOIN
items_img ii
ON
i.id = ii.item_id
WHERE
f.type = 1 # 固定不变
AND
i.root_cat_id = 7 # 根据具体的类别确定
AND
ii.is_main = 1 # 判断是否是主图,固定不变
ORDER BY
i.created_time DESC
LIMIT 0,6; # 每个大类查6个,固定不变
在CategoryService
中添加查询六个商品的懒加载接口:
/**
* 查询首页每个一级分类下的6条最新商品数据
* @param rootCatId
* @return
*/
public List getSixNewItemLazy(Integer rootCatId);
和之前获得子分类一样,修改mapper,在CategoryMapperCustom
中追加接口:
public List getSixNewItemLazy(@Param("paramsMap") Map<String, Object> map);
这里用map传参。
基于上面的sql语句,在CategoryMapperCustom.xml
中追加:
<resultMap id="myNewItemsVO" type="com.wjw.pojo.vo.NewItemsVO">
<id column="rootCatId" property="rootCatId"/>
<result column="rootCatName" property="rootCatName"/>
<result column="slogan" property="slogan"/>
<result column="catImage" property="catImage"/>
<result column="bgColor" property="bgColor"/>
<collection property="simpleItemList" ofType="com.wjw.pojo.vo.SimpleItemVO">
<id column="itemId" property="itemId"/>
<result column="itemName" property="itemName"/>
<result column="itemUrl" property="itemUrl"/>
collection>
resultMap>
<select id="getSixNewItemLazy" resultMap="myNewItemsVO" parameterType="Map">
SELECT
f.id as rootCatId,
f.`name` as rootCatName,
f.slogan as slogan,
f.cat_image as catImage,
f.bg_color as bgColor,
i.id as itemId,
i.item_name as itemName,
ii.url as itemUrl,
i.created_time as createdTime
FROM
category f
LEFT JOIN items i ON f.id = i.root_cat_id
LEFT JOIN items_img ii ON i.id = ii.item_id
WHERE
f.type = 1
AND
i.root_cat_id = #{paramsMap.rootCatId}
AND
ii.is_main = 1
ORDER BY
i.created_time DESC
LIMIT 0,6;
select>
其中涉及到NewItemsVO
:
package com.wjw.pojo.vo;
import java.util.List;
/**
* 2 * @Author: 小王同学
* 3 * @Date: 2020/12/24 16:42
* 4 最新商品推荐VO
*/
public class NewItemsVO {
private Integer rootCatId;
private String rootCatName;
private String slogan;
private String catImage;
private String bgColor;
private List<SimpleItemVO> simpleItemList;
......
}
simpleItemList
中保存了SQL语句中查询出来的
SimpleItemVO的定义为:
package com.wjw.pojo.vo;
/**
* 2 * @Author: 小王同学
* 3 * @Date: 2020/12/25 15:57
* 4 6个最新商品的简单数据类型
*/
public class SimpleItemVO {
private String itemId;
private String itemName;
private String itemUrl;
......
}
设置CategoryMapperCustom
和CategoryService
的返回值类型:
由于mapper中定义了我们传进去的参数是一个Map,所以service实现类中要声明一个map:
/**
* 查询首页每个一级分类下的6条最新商品数据
*
* @param rootCatId
* @return
*/
@Transactional(propagation = Propagation.SUPPORTS)
@Override
public List getSixNewItemLazy(Integer rootCatId) {
HashMap<String, Object> map = new HashMap<>();
map.put("rootCatId", rootCatId);
return categoryMapperCustom.getSixNewItemLazy(map);
}
IndexController中添加相应的接口:
@ApiOperation(value = "查询每个一级分类下的最新6条商品数据", notes = "查询每个一级分类下的最新6条商品数据", httpMethod = "GET")
@GetMapping("/sixNewItems/{rootCatId}")
public WJWJSONResult sixNewItems(
@ApiParam(name = "rootCatId", value = "一级分类id", required = true)
@PathVariable Integer rootCatId){
if (rootCatId == null) {
return WJWJSONResult.errorMsg("分类不存在");
}
List<NewItemsVO> list = categoryService.getSixNewItemLazy(rootCatId);
return WJWJSONResult.ok(list);
}
启动测试可以发现随着鼠标慢慢往下划,页面慢慢的被加载出来。
左边的图片是存放于items_img表中的:
详情中的信息存放于items表和items_spec表:
规格名称对应口味
宝贝详情存储在items_param表中
对于不同部分的加载,可以分开发送多个请求,用多个controller进行接收,将请求分散开来,速度稍微快一点。这里为了简便起见,还是用一次请求加载全部。
定义商品查询的接口:
package com.wjw.service;
import com.wjw.pojo.Items;
import com.wjw.pojo.ItemsImg;
import com.wjw.pojo.ItemsParam;
import com.wjw.pojo.ItemsSpec;
import java.util.List;
/**
* 2 * @Author: 小王同学
* 3 * @Date: 2020/12/25 16:47
* 4
*/
public interface ItemService {
/**
* 根据商品id查询详情
* @param itemId
* @return
*/
public Items queryItemById(String itemId);
/**
* 根据商品id查询商品图片列表
* @param itemId
* @return
*/
public List<ItemsImg> queryItemImgList(String itemId);
/**
* 根据商品id查询商品规格
* @param itemId
* @return
*/
public List<ItemsSpec> queryItemSpecList(String itemId);
/**
* 根据商品id查询商品参数
* @param itemId
* @return
*/
public ItemsParam queryItemParam(String itemId);
}
相应的具体实现(4个查询):
package com.wjw.service.impl;
import com.wjw.mapper.ItemsImgMapper;
import com.wjw.mapper.ItemsMapper;
import com.wjw.mapper.ItemsParamMapper;
import com.wjw.mapper.ItemsSpecMapper;
import com.wjw.pojo.Items;
import com.wjw.pojo.ItemsImg;
import com.wjw.pojo.ItemsParam;
import com.wjw.pojo.ItemsSpec;
import com.wjw.service.ItemService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import tk.mybatis.mapper.entity.Example;
import java.util.List;
/**
* 2 * @Author: 小王同学
* 3 * @Date: 2020/12/25 16:47
* 4
*/
@Service
public class ItemServiceImpl implements ItemService {
@Autowired
private ItemsMapper itemsMapper;
@Autowired
private ItemsImgMapper itemsImgMapper;
@Autowired
private ItemsSpecMapper itemsSpecMapper;
@Autowired
private ItemsParamMapper itemsParamMapper;
/**
* 根据商品id查询详情
*
* @param itemId
* @return
*/
@Transactional(propagation = Propagation.SUPPORTS)
@Override
public Items queryItemById(String itemId) {
return itemsMapper.selectByPrimaryKey(itemId);
}
/**
* 根据商品id查询商品图片列表
*
* @param itemId
* @return
*/
@Transactional(propagation = Propagation.SUPPORTS)
@Override
public List<ItemsImg> queryItemImgList(String itemId) {
Example itemsImgExp = new Example(ItemsImg.class);
Example.Criteria criteria = itemsImgExp.createCriteria();
criteria.andEqualTo("itemId", itemId);
return itemsImgMapper.selectByExample(itemsImgExp);
}
/**
* 根据商品id查询商品规格
*
* @param itemId
* @return
*/
@Transactional(propagation = Propagation.SUPPORTS)
@Override
public List<ItemsSpec> queryItemSpecList(String itemId) {
Example itemsSpecExp = new Example(ItemsSpec.class);
Example.Criteria criteria = itemsSpecExp.createCriteria();
criteria.andEqualTo("itemId", itemId);
return itemsSpecMapper.selectByExample(itemsSpecExp);
}
/**
* 根据商品id查询商品参数
*
* @param itemId
* @return
*/
@Transactional(propagation = Propagation.SUPPORTS)
@Override
public ItemsParam queryItemParam(String itemId) {
Example itemsParamExp = new Example(ItemsParam.class);
Example.Criteria criteria = itemsParamExp.createCriteria();
criteria.andEqualTo("itemId", itemId);
return itemsParamMapper.selectOneByExample(itemsParamExp);
}
}
前端请求url:
创建ItemsController:
由于这里一下查了四组参数要同时返回,所以还要借助VO
package com.wjw.pojo.vo;
import com.wjw.pojo.Items;
import com.wjw.pojo.ItemsImg;
import com.wjw.pojo.ItemsParam;
import com.wjw.pojo.ItemsSpec;
import java.util.List;
/**
* 2 * @Author: 小王同学
* 3 * @Date: 2020/12/25 18:49
* 4 商品详情VO
*/
public class ItemInfoVO {
private Items item;
private List<ItemsImg> itemImgList;
private List<ItemsSpec> itemSpecList ;
private ItemsParam itemParams;
......
}
controller:
package com.wjw.controller;
import com.wjw.pojo.Items;
import com.wjw.pojo.ItemsImg;
import com.wjw.pojo.ItemsParam;
import com.wjw.pojo.ItemsSpec;
import com.wjw.pojo.vo.ItemInfoVO;
import com.wjw.service.ItemService;
import com.wjw.utils.WJWJSONResult;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* 2 * @Author: 小王同学
* 3 * @Date: 2020/12/25 17:15
* 4
*/
@Api(value = "商品接口", tags = {
"商品信息展示的相关接口"})
@RestController
@RequestMapping("items")
public class ItemsController {
@Autowired
private ItemService itemService;
@ApiOperation(value = "查询商品详情", notes = "查询商品详情", httpMethod = "GET")
@GetMapping("/info/{itemId}")
public WJWJSONResult info(
@ApiParam(name = "itemId", value = "商品id", required = true)
@PathVariable String itemId){
if (StringUtils.isBlank(itemId)){
return WJWJSONResult.errorMsg(null);
}
Items item = itemService.queryItemById(itemId);
List<ItemsImg> itemImgList = itemService.queryItemImgList(itemId);
List<ItemsSpec> itemSpecList = itemService.queryItemSpecList(itemId);
ItemsParam itemParams = itemService.queryItemParam(itemId);
ItemInfoVO itemInfoVO = new ItemInfoVO();
itemInfoVO.setItem(item);
itemInfoVO.setItemImgList(itemImgList);
itemInfoVO.setItemSpecList(itemSpecList);
itemInfoVO.setItemParams(itemParams);
return WJWJSONResult.ok(itemInfoVO);
}
}
订单完成之后,在用户中心可以进行评价操作,展示评论时还要把用户信息进行一个隐私处理。还要涉及分页、分类操作。
表的设计要和用户表商品表关联,商品有不同规格所以要有规格id、名称
评价等级:1:好评 2:中评 3:差评
在ItemService中添加和评价有关的查询:
/**
* 根据商品id查询商品的评价等级数量
* @param itemId
*/
public CommentLevelCountsVO queryCommentCounts(String itemId);
将商品评价的展示封装成为VO:
package com.wjw.pojo.vo;
/**
* 2 * @Author: 小王同学
* 3 * @Date: 2020/12/25 20:33
* 4 用于展示商品评价数量的VO
*/
public class CommentLevelCountsVO {
/**
* 总评价数
*/
public Integer totalCounts;
/**
* 好评
*/
public Integer goodCounts;
/**
* 中评
*/
public Integer normalCounts;
/**
* 差评
*/
public Integer badCounts;
}
创建评价等级的枚举:
package com.wjw.enums;
/**
* 2 * @Author: 小王同学
* 3 * @Date: 2020/12/21 21:04
* 4 商品评价等级枚举
*/
public enum CommentLevel {
GOOD(1, "好评"),
NORMAL(2, "中评"),
BAD(3, "差评");
public final Integer type;
public final String value;
CommentLevel(Integer type, String value) {
this.type = type;
this.value = value;
}
}
service的具体实现:
@Autowired
private ItemsCommentsMapper itemsCommentsMapper;
/**
* 根据商品id查询商品的评价等级数量
*
* @param itemId
*/
@Transactional(propagation = Propagation.SUPPORTS)
@Override
public CommentLevelCountsVO queryCommentCounts(String itemId) {
Integer goodCounts = getCommentCounts(itemId, CommentLevel.GOOD.type);
Integer normalCounts = getCommentCounts(itemId, CommentLevel.NORMAL.type);
Integer badCounts = getCommentCounts(itemId, CommentLevel.BAD.type);
Integer totalCounts = goodCounts + normalCounts + badCounts;
CommentLevelCountsVO countsVO = new CommentLevelCountsVO();
countsVO.setTotalCounts(totalCounts);
countsVO.setGoodCounts(goodCounts);
countsVO.setNormalCounts(normalCounts);
countsVO.setBadCounts(badCounts);
return countsVO;
}
/**
* 查询某个评价等级的通用方法
* @param itemId
* @param level
* @return
*/
Integer getCommentCounts(String itemId, Integer level) {
ItemsComments condition = new ItemsComments();
condition.setItemId(itemId);
if (level != null){
condition.setCommentLevel(level);
}
return itemsCommentsMapper.selectCount(condition);
}
在IndexController中添加查询评价等级数量的接口:
注意这里路径用的是?
这种形式,不是前面的/
这种形式,所以对应的参数的注解为@RequestParam
@ApiOperation(value = "查询商品评价等级", notes = "查询商品评价等级", httpMethod = "GET")
@GetMapping("/commentLevel")
public WJWJSONResult commentLevel(
@ApiParam(name = "itemId", value = "商品id", required = true)
@RequestParam String itemId){
if (StringUtils.isBlank(itemId)){
return WJWJSONResult.errorMsg(null);
}
CommentLevelCountsVO countsVO = itemService.queryCommentCounts(itemId);
return WJWJSONResult.ok(countsVO);
}
SELECT
ic.comment_level as commentLevel,
ic.content as content,
ic.sepc_name as specName,
ic.created_time as createdTime,
u.face as userFace,
u.nickname as nickname
FROM
items_comments as ic
LEFT JOIN
users as u
ON
ic.user_id = u.id
WHERE
ic.item_id = 'cake-1001' # 指定查哪个商品评价
AND
ic.comment_level = 1; # 查好评
创建自定义ItemsMapperCustom.xml:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.wjw.mapper.ItemsMapperCustom" >
<select id="queryItemComments" parameterType="Map" resultType="">
SELECT
ic.comment_level as commentLevel,
ic.content as content,
ic.sepc_name as specName,
ic.created_time as createdTime,
u.face as userFace,
u.nickname as nickname
FROM
items_comments as ic
LEFT JOIN
users as u
ON
ic.user_id = u.id
WHERE
ic.item_id =
<if test="">
AND
ic.comment_level = 1
</if>
</select>
</mapper>
自定义mapper接口:
package com.wjw.mapper;
public interface ItemsMapperCustom {
}
上面两个文件未编写完毕,下面继续。
Sql语句的输出要返回给前端进行展示,所以要定义一个VO:
package com.wjw.pojo.vo;
import java.util.Date;
/**
* 2 * @Author: 小王同学
* 3 * @Date: 2020/12/26 10:31
* 4 用于展示商品评价的VO
*/
public class ItemCommentVO {
private Integer commentLevel;
private String content;
private String specName;
private Date createdTime;
private String userFace;
private String nickname;
......
}
在xml文件的namespace中添加相应的方法:
package com.wjw.mapper;
import com.wjw.pojo.vo.ItemCommentVO;
import org.apache.ibatis.annotations.Param;
import java.util.List;
import java.util.Map;
public interface ItemsMapperCustom {
public List<ItemCommentVO> queryItemComments(@Param("paramsMap") Map<String, Object> map);
}
在ItemService
中添加接口:
/**
* 根据商品id和评价等级查询评价(分页)
* @param itemId
* @param level
* @return
*/
public List<ItemCommentVO> queryPagedComments(String itemId, Integer level);
具体实现:
@Autowired
private ItemsMapperCustom itemsMapperCustom;
@Transactional(propagation = Propagation.SUPPORTS)
@Override
public List<ItemCommentVO> queryPagedComments(String itemId, Integer level) {
// 请求参数
HashMap<String, Object> map = new HashMap<>();
map.put("itemId", itemId);
map.put("level", level);
List<ItemCommentVO> list = itemsMapperCustom.queryItemComments(map);
return list;
}
1. 父工程引入分页插件依赖
<dependency>
<groupId>com.github.pagehelpergroupId>
<artifactId>pagehelper-spring-boot-starterartifactId>
<version>1.2.12version>
dependency>
2. foodie-dev-api配置yml
# 分页插件配置
pagehelper:
helperDialect: mysql
supportMethodsArguments: true
3. 使用分页插件,在查询前使用分页插件,原理:统一拦截sql,为其提供分页功能
/**
* page: 第几页
* pageSize: 每页显示条数
*/
PageHelper.startPage(page, pageSize);
其中page
和pageSize
应该是前端传进来的。既然有分页,所以要对queryPagedComments
再添加两个参数。
4.分页数据封装到PagedGridResult.java 传给前端
PageInfo<?> pageList = new PageInfo<>(list);
PagedGridResult grid = new PagedGridResult();
grid.setPage(page);
grid.setRows(list);
grid.setTotal(pageList.getPages());
grid.setRecords(pageList.getTotal());
其中PagedGridResult是自己封装的类。
package com.wjw.utils;
import java.util.List;
/**
*
* @Title: PagedGridResult.java
* @Package com.wjw.utils
* @Description: 用来返回分页Grid的数据格式
* Copyright: Copyright (c) 2019
*/
public class PagedGridResult {
private int page; // 当前页数
private int total; // 总页数
private long records; // 总记录数
private List<?> rows; // 每行显示的内容
......
}
同时service方法的返回值也要改变。
完整的queryPagedComments实现:
@Transactional(propagation = Propagation.SUPPORTS)
@Override
public PagedGridResult queryPagedComments(String itemId, Integer level,
Integer page, Integer pageSize) {
// 请求参数
HashMap<String, Object> map = new HashMap<>();
map.put("itemId", itemId);
map.put("level", level);
/**
* page: 第几页
* pageSize: 每页显示条数
*/
PageHelper.startPage(page, pageSize);
List<ItemCommentVO> list = itemsMapperCustom.queryItemComments(map);
return setterPagedGrid(list, page);
}
/**
* 抽取设置分页对象的方法
* @param list
* @param page
* @return
*/
private PagedGridResult setterPagedGrid(List<?> list, Integer page){
PageInfo<?> pageList = new PageInfo<>(list);
PagedGridResult grid = new PagedGridResult();
grid.setPage(page);
grid.setRows(list);
grid.setTotal(pageList.getPages());
grid.setRecords(pageList.getTotal());
return grid;
}
package com.wjw.controller;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import springfox.documentation.annotations.ApiIgnore;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
/**
* 2 * @Author: 小王同学
* 3 * @Date: 2020/12/20 16:29
* 4
*/
@Controller
public class BaseController {
public static final Integer COMMENT_PAGE_SIZE = 10;
}
ItemsController
中添加方法:
先继承BaseController
@ApiOperation(value = "查询商品评价", notes = "查询商品评价", httpMethod = "GET")
@GetMapping("/comments")
public WJWJSONResult comments(
@ApiParam(name = "itemId", value = "商品id", required = true)
@RequestParam String itemId,
@ApiParam(name = "level", value = "评价等级", required = false)
@RequestParam Integer level,
@ApiParam(name = "page", value = "查询下一页的第几页", required = false)
@RequestParam Integer page,
@ApiParam(name = "pageSize", value = "每一页显示的记录数", required = false)
@RequestParam Integer pageSize){
if (StringUtils.isBlank(itemId)){
return WJWJSONResult.errorMsg(null);
}
if (page == null){
page = 1;
}
if (pageSize == null){
pageSize = COMMENT_PAGE_SIZE;
}
PagedGridResult grid = itemService.queryPagedComments(itemId, level, page, pageSize);
return WJWJSONResult.ok(grid);
}
通用脱敏工具类:
package com.wjw.utils;
import sun.applet.Main;
/**
* 通用脱敏工具类
* 可用于:
* 用户名
* 手机号
* 邮箱
* 地址等
*/
public class DesensitizationUtil {
private static final int SIZE = 6;
private static final String SYMBOL = "*";
public static void main(String[] args) {
String name = commonDisplay("N小王日记");
String mobile = commonDisplay("13900000000");
String mail = commonDisplay("[email protected]");
String address = commonDisplay("南京北京东路888号");
System.out.println(name);
System.out.println(mobile);
System.out.println(mail);
System.out.println(address);
}
/**
* 通用脱敏方法
* @param value
* @return
*/
public static String commonDisplay(String value) {
if (null == value || "".equals(value)) {
return value;
}
int len = value.length();
int pamaone = len / 2;
int pamatwo = pamaone - 1;
int pamathree = len % 2;
StringBuilder stringBuilder = new StringBuilder();
if (len <= 2) {
if (pamathree == 1) {
return SYMBOL;
}
stringBuilder.append(SYMBOL);
stringBuilder.append(value.charAt(len - 1));
} else {
if (pamatwo <= 0) {
stringBuilder.append(value.substring(0, 1));
stringBuilder.append(SYMBOL);
stringBuilder.append(value.substring(len - 1, len));
} else if (pamatwo >= SIZE / 2 && SIZE + 1 != len) {
int pamafive = (len - SIZE) / 2;
stringBuilder.append(value.substring(0, pamafive));
for (int i = 0; i < SIZE; i++) {
stringBuilder.append(SYMBOL);
}
if ((pamathree == 0 && SIZE / 2 == 0) || (pamathree != 0 && SIZE % 2 != 0)) {
stringBuilder.append(value.substring(len - pamafive, len));
} else {
stringBuilder.append(value.substring(len - (pamafive + 1), len));
}
} else {
int pamafour = len - 2;
stringBuilder.append(value.substring(0, 1));
for (int i = 0; i < pamafour; i++) {
stringBuilder.append(SYMBOL);
}
stringBuilder.append(value.substring(len - 1, len));
}
}
return stringBuilder.toString();
}
}
搜索出来的结果要展示图片,价格,描述,销量,所以涉及到多表关联查询。
商品表关联商品规格表和商品图片表。
SELECT
i.id as itemId,
i.item_name as itemName,
i.sell_counts as sellConuts,
ii.url as imgUrl,
tempSpec.price_discount as price
FROM
items as i
LEFT JOIN
items_img ii
ON
i.id = ii.item_id
LEFT JOIN
(
SELECT
item_id, MIN(price_discount) as price_discount
FROM
items_spec
GROUP BY
item_id
) as tempSpec
ON
i.id = tempSpec.item_id
WHERE
ii.is_main = 1;
创建搜索结果展示VO:
后端涉及到的金额是以“分”为单位的int型数据,前端负责做除以100的展示
package com.wjw.pojo.vo;
/**
* 2 * @Author: 小王同学
* 3 * @Date: 2020/12/26 14:33
* 4 用于展示商品搜索列表的VO
*/
public class SearchItemsVO {
private String itemId;
private String itemName;
private int sellCounts;
private String imgUrl;
private int price;
......
}
实现mapper,在ItemsMapperCustom.xml中添加查询语句:
paramsMap.sort = k;
paramsMap.sort = c;
paramsMap.sort = p;
mybatis中单引号要转义 "
<select id="searchItems" parameterType="Map" resultType="com.wjw.pojo.vo.SearchItemsVO">
SELECT
i.id as itemId,
i.item_name as itemName,
i.sell_counts as sellCounts,
ii.url as imgUrl,
tempSpec.price_discount as price
FROM
items as i
LEFT JOIN
items_img ii
ON
i.id = ii.item_id
LEFT JOIN
( SELECT item_id, MIN(price_discount) as price_discount FROM items_spec GROUP BY item_id ) as tempSpec
ON
i.id = tempSpec.item_id
WHERE
ii.is_main = 1
<if test="paramsMap.keywords != null and paramsMap.keywords != ''">
AND i.item_name like '%${paramsMap.keywords}%'
if>
order by
<choose>
<when test="paramsMap.sort == "c" ">
i.sell_counts desc
when>
<when test="paramsMap.sort == "p" ">
tempSpec.price_discount asc
when>
<otherwise>
i.item_name asc
otherwise>
choose>
select>
在mapper的命名空间ItemsMapperCustom
中添加searchItems方法:
public List<SearchItemsVO> searchItems(@Param("paramsMap") Map<String, Object> map);
编写service层,ItemService
中添加:
/**
* 搜索商品列表
* @param keyword
* @param sort 排序类型:k,c,p
* @param page
* @param pageSize
* @return
*/
public PagedGridResult searchItems(String keyword, String sort, Integer page, Integer pageSize);
service层对应实现类:
@Transactional(propagation = Propagation.SUPPORTS)
@Override
public PagedGridResult searchItems(String keywords, String sort, Integer page, Integer pageSize) {
// 请求参数
HashMap<String, Object> map = new HashMap<>();
map.put("keywords", keywords);
map.put("sort", sort);
PageHelper.startPage(page, pageSize);
List<SearchItemsVO> list = itemsMapperCustom.searchItems(map);
return setterPagedGrid(list, page);
}
在ItemsController
中添加相应的接口:
前端请求地址:
@ApiOperation(value = "搜索商品列表", notes = "搜索商品列表", httpMethod = "GET")
@GetMapping("/search")
public WJWJSONResult search(
@ApiParam(name = "keywords", value = "关键字", required = true)
@RequestParam String keywords,
@ApiParam(name = "sort", value = "排序", required = false)
@RequestParam String sort,
@ApiParam(name = "page", value = "查询下一页的第几页", required = false)
@RequestParam Integer page,
@ApiParam(name = "pageSize", value = "每一页显示的记录数", required = false)
@RequestParam Integer pageSize){
if (StringUtils.isBlank(keywords)){
return WJWJSONResult.errorMsg(null);
}
if (page == null){
page = 1;
}
if (pageSize == null){
pageSize = PAGE_SIZE;
}
PagedGridResult grid = itemService.searchItems(keywords, sort, page, pageSize);
return WJWJSONResult.ok(grid);
}
点击按钮查找是按照其三级分类的id来查找该分类下的所有商品。所以前端就有两种方式跳转到搜索的结果页面。
在ItemsMapperCustom.xml中添加根据三级标签搜索的功能:
类似上面的根据关键词的搜索
<select id="searchItemsByThirdCat" parameterType="Map" resultType="com.wjw.pojo.vo.SearchItemsVO">
SELECT
i.id as itemId,
i.item_name as itemName,
i.sell_counts as sellCounts,
ii.url as imgUrl,
tempSpec.price_discount as price
FROM
items as i
LEFT JOIN
items_img ii
ON
i.id = ii.item_id
LEFT JOIN
( SELECT item_id, MIN(price_discount) as price_discount FROM items_spec GROUP BY item_id ) as tempSpec
ON
i.id = tempSpec.item_id
WHERE
ii.is_main = 1
AND
i.cat_id = #{paramsMap.catId}
order by
<choose>
<when test="paramsMap.sort == "c" ">
i.sell_counts desc
</when>
<when test="paramsMap.sort == "p" ">
tempSpec.price_discount asc
</when>
<otherwise>
i.item_name asc
</otherwise>
</choose>
</select>
在xml文件的namespace中定义searchItemsByThirdCat方法:
public List<SearchItemsVO> searchItemsByThirdCat(@Param("paramsMap") Map<String, Object> map);
service层实现,在ItemService
中追加接口:
/**
* 根据三级分类id搜索商品列表
* @param catId
* @param sort 排序类型:k,c,p
* @param page
* @param pageSize
* @return
*/
public PagedGridResult searchItems(Integer catId, String sort, Integer page, Integer pageSize);
对应的service层具体实现:
类似上面的根据关键词的搜索
@Transactional(propagation = Propagation.SUPPORTS)
@Override
public PagedGridResult searchItems(Integer catId, String sort, Integer page, Integer pageSize) {
// 请求参数
HashMap<String, Object> map = new HashMap<>();
map.put("catId", catId);
map.put("sort", sort);
PageHelper.startPage(page, pageSize);
List<SearchItemsVO> list = itemsMapperCustom.searchItemsByThirdCat(map);
return setterPagedGrid(list, page);
}
在ItemsController
中追加相应的接口:
@ApiOperation(value = "通过分类id搜索商品列表", notes = "通过分类id搜索商品列表", httpMethod = "GET")
@GetMapping("/catItems")
public WJWJSONResult catItems(
@ApiParam(name = "catId", value = "三级分类id", required = true)
@RequestParam Integer catId,
@ApiParam(name = "sort", value = "排序", required = false)
@RequestParam String sort,
@ApiParam(name = "page", value = "查询下一页的第几页", required = false)
@RequestParam Integer page,
@ApiParam(name = "pageSize", value = "每一页显示的记录数", required = false)
@RequestParam Integer pageSize){
if (catId == null){
return WJWJSONResult.errorMsg(null);
}
if (page == null){
page = 1;
}
if (pageSize == null){
pageSize = PAGE_SIZE;
}
PagedGridResult grid = itemService.searchItems(catId, sort, page, pageSize);
return WJWJSONResult.ok(grid);
}