新蜂商城 项目笔记
1、启动项目,访问api在线文档
http://localhost:8080/swagger-ui.html
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BoaSZ1T1-1638114613547)(C:\Users\chen\AppData\Roaming\Typora\typora-user-images\image-20211128181427273.png)]
1. result类
作者为api文档的返回结果定义了一个result类作为响应结果的模板
public class Result<T> implements Serializable {
private static final long serialVersionUID = 1L;
//业务码,比如成功、失败、权限不足等 code,可自行定义
@ApiModelProperty("返回码")
private int resultCode;
//返回信息,后端在进行业务处理后返回给前端一个提示信息,可自行定义
@ApiModelProperty("返回信息")
private String message;
//数据结果,泛型,可以是列表、单个对象、数字、布尔值等
@ApiModelProperty("返回数据")
private T data;
public Result() {
}
public Result(int resultCode, String message) {
this.resultCode = resultCode;
this.message = message;
}
public int getResultCode() {
return resultCode;
}
public void setResultCode(int resultCode) {
this.resultCode = resultCode;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
@Override
public String toString() {
return "Result{" +
"resultCode=" + resultCode +
", message='" + message + '\'' +
", data=" + data +
'}';
}
}
每次responsebody 都包括了三个部分
- resultCode
- message
- data(其中data是泛型)
例子 :
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zjtZn5cF-1638114613550)(C:\Users\chen\AppData\Roaming\Typora\typora-user-images\image-20211128183745040.png)]
2.ResultGenerator
ResultGenerator是一个响应结果生成工具
- 成功: genSuccessResult()三种,参数为空,message,data;
- 失败: genFailResult()
- 错误:genErrorResult()
public class ResultGenerator {
private static final String DEFAULT_SUCCESS_MESSAGE = "SUCCESS";
private static final String DEFAULT_FAIL_MESSAGE = "FAIL";
private static final int RESULT_CODE_SUCCESS = 200;
private static final int RESULT_CODE_SERVER_ERROR = 500;
public static Result genSuccessResult() {
Result result = new Result();
result.setResultCode(RESULT_CODE_SUCCESS);
result.setMessage(DEFAULT_SUCCESS_MESSAGE);
return result;
}
public static Result genSuccessResult(String message) {
Result result = new Result();
result.setResultCode(RESULT_CODE_SUCCESS);
result.setMessage(message);
return result;
}
public static Result genSuccessResult(Object data) {
Result result = new Result();
result.setResultCode(RESULT_CODE_SUCCESS);
result.setMessage(DEFAULT_SUCCESS_MESSAGE);
result.setData(data);
return result;
}
public static Result genFailResult(String message) {
Result result = new Result();
result.setResultCode(RESULT_CODE_SERVER_ERROR);
if (StringUtils.isEmpty(message)) {
result.setMessage(DEFAULT_FAIL_MESSAGE);
} else {
result.setMessage(message);
}
return result;
}
public static Result genErrorResult(int code, String message) {
Result result = new Result();
result.setResultCode(code);
result.setMessage(message);
return result;
}
}
3. ServiceResultEnum
public enum ServiceResultEnum {
ERROR("error"),
SUCCESS("success"),
DATA_NOT_EXIST("未查询到记录!"),
PARAM_ERROR("参数错误!"),
SAME_CATEGORY_EXIST("已存在同级同名的分类!"),
SAME_LOGIN_NAME_EXIST("用户名已存在!"),
LOGIN_NAME_NULL("请输入登录名!"),
LOGIN_NAME_IS_NOT_PHONE("请输入正确的手机号!"),
LOGIN_PASSWORD_NULL("请输入密码!"),
LOGIN_VERIFY_CODE_NULL("请输入验证码!"),
LOGIN_VERIFY_CODE_ERROR("验证码错误!"),
SAME_INDEX_CONFIG_EXIST("已存在相同的首页配置项!"),
GOODS_CATEGORY_ERROR("分类数据异常!"),
SAME_GOODS_EXIST("已存在相同的商品信息!"),
GOODS_NOT_EXIST("商品不存在!"),
GOODS_PUT_DOWN("商品已下架!"),
SHOPPING_CART_ITEM_LIMIT_NUMBER_ERROR("超出单个商品的最大购买数量!"),
SHOPPING_CART_ITEM_NUMBER_ERROR("商品数量不能小于 1 !"),
SHOPPING_CART_ITEM_TOTAL_NUMBER_ERROR("超出购物车最大容量!"),
SHOPPING_CART_ITEM_EXIST_ERROR("已存在!无需重复添加!"),
LOGIN_ERROR("登录失败!"),
NOT_LOGIN_ERROR("未登录!"),
ADMIN_NOT_LOGIN_ERROR("管理员未登录!"),
TOKEN_EXPIRE_ERROR("无效认证!请重新登录!"),
ADMIN_TOKEN_EXPIRE_ERROR("管理员登录过期!请重新登录!"),
USER_NULL_ERROR("无效用户!请重新登录!"),
LOGIN_USER_LOCKED_ERROR("用户已被禁止登录!"),
ORDER_NOT_EXIST_ERROR("订单不存在!"),
NULL_ADDRESS_ERROR("地址不能为空!"),
ORDER_PRICE_ERROR("订单价格异常!"),
ORDER_ITEM_NULL_ERROR("订单项异常!"),
ORDER_GENERATE_ERROR("生成订单异常!"),
SHOPPING_ITEM_ERROR("购物车数据异常!"),
SHOPPING_ITEM_COUNT_ERROR("库存不足!"),
ORDER_STATUS_ERROR("订单状态异常!"),
OPERATE_ERROR("操作失败!"),
REQUEST_FORBIDEN_ERROR("禁止该操作!"),
NO_PERMISSION_ERROR("无权限!"),
DB_ERROR("database error");
private String result;
ServiceResultEnum(String result) {
this.result = result;
}
public String getResult() {
return result;
}
public void setResult(String result) {
this.result = result;
}
}
2、 接口分析
1. 新蜂商城首页接口 New Bee Mall Index API
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pQOlnUvp-1638114613552)(C:\Users\chen\AppData\Roaming\Typora\typora-user-images\image-20211128224011589.png)]
/api/v1/index-infos
文件: NewBeeMallIndexAPI.java
作用: 获取首页数据
代码分析:
通过@Resource(Spring按Name依赖注入,@AutoWired是按Type依赖注入)获取了两个service的对象
@Resource
private NewBeeMallCarouselService newBeeMallCarouselService;
@Resource
private NewBeeMallIndexConfigService newBeeMallIndexConfigService
IndexInfoVo类
作者定义了一个IndexInfoVO继承Serializable类
IndexInfoVO里有四个列表
- 轮播图(列表)
- 首页热销商品(列表)
- 首页新品推荐(列表)
- 首页推荐商品(列表)
@Data
public class IndexInfoVO implements Serializable {
@ApiModelProperty("轮播图(列表)")
private List<NewBeeMallIndexCarouselVO> carousels;
@ApiModelProperty("首页热销商品(列表)")
private List<NewBeeMallIndexConfigGoodsVO> hotGoodses;
@ApiModelProperty("首页新品推荐(列表)")
private List<NewBeeMallIndexConfigGoodsVO> newGoodses;
@ApiModelProperty("首页推荐商品(列表)")
private List<NewBeeMallIndexConfigGoodsVO> recommendGoodses;
}
轮播图vo
/**
* 首页配置商品VO
*/
@Data
public class NewBeeMallIndexCarouselVO implements Serializable {
@ApiModelProperty("轮播图图片地址")
private String carouselUrl;
@ApiModelProperty("轮播图点击后的跳转路径")
private String redirectUrl;
}
商品VO
/**
* 首页配置商品VO
*/
@Data
public class NewBeeMallIndexConfigGoodsVO implements Serializable {
@ApiModelProperty("商品id")
private Long goodsId;
@ApiModelProperty("商品名称")
private String goodsName;
@ApiModelProperty("商品简介")
private String goodsIntro;
@ApiModelProperty("商品图片地址")
private String goodsCoverImg;
@ApiModelProperty("商品价格")
private Integer sellingPrice;
@ApiModelProperty("商品标签")
private String tag;
}
获取四个列表过程
调用newBeeMallCarouselService和newBeeMallIndexConfigService里的方法获取四个列表,并把四个列表set给IndexInfoVO对象。
- newBeeMallCarouselService.getCarouselsForIndex
顺藤摸瓜找到NewBeeMallCarouselServiceImpl实现类的getCarouselsForIndex方法
NewBeeMallCarouselServiceImpl.java
@Override
public List<NewBeeMallIndexCarouselVO> getCarouselsForIndex(int number) {
List<NewBeeMallIndexCarouselVO> newBeeMallIndexCarouselVOS = new ArrayList<>(number);
List<Carousel> carousels = carouselMapper.findCarouselsByNum(number);
if (!CollectionUtils.isEmpty(carousels)) {
newBeeMallIndexCarouselVOS = BeanUtil.copyList(carousels, NewBeeMallIndexCarouselVO.class);
}
return newBeeMallIndexCarouselVOS;
}
findCarouselsByNum对应了CarouselMapper.xml
<select id="findCarouselList" parameterType="Map" resultMap="BaseResultMap">
select
<include refid="Base_Column_List"/>
from tb_newbee_mall_carousel
where is_deleted = 0
order by carousel_rank desc
<if test="start!=null and limit!=null">
limit #{start},#{limit}
</if>
</select>
(可能)其中BeanUtil.copyList的方法为了将一些具体list转成泛型list?
if (!CollectionUtils.isEmpty(carousels)) {
newBeeMallIndexCarouselVOS = BeanUtil.copyList(carousels, NewBeeMallIndexCarouselVO.class);
}
BeanUtil.java
public static <T> List<T> copyList(List sources, Class<T> clazz) {
return copyList(sources, clazz, null);
}
public static <T> List<T> copyList(List sources, Class<T> clazz, Callback<T> callback) {
List<T> targetList = new ArrayList<>();
if (sources != null) {
try {
for (Object source : sources) {
T target = clazz.newInstance();
copyProperties(source, target);
if (callback != null) {
callback.set(source, target);
}
targetList.add(target);
}
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
return targetList;
}
- newBeeMallIndexConfigService.getConfigGoodsesForIndex
顺藤摸瓜找到newBeeMallIndexConfigServiceImpl实现类的getConfigGoodsesForIndex方法
方法关键代码
List<NewBeeMallGoods> newBeeMallGoods = goodsMapper.selectByPrimaryKeys(goodsIds);
NewBeeMallGoodsMapper.xml
<select id="selectByPrimaryKey" parameterType="java.lang.Long" resultMap="ResultMapWithBLOBs">
select
<include refid="Base_Column_List"/>
,
<include refid="Blob_Column_List"/>
from tb_newbee_mall_goods_info
where goods_id = #{goodsId,jdbcType=BIGINT}
</select>
同样的转泛型处理
newBeeMallIndexConfigGoodsVOS = BeanUtil.copyList(newBeeMallGoods, NewBeeMallIndexConfigGoodsVO.class);
字符串过长导致文字超出的问题处理方法
for (NewBeeMallIndexConfigGoodsVO newBeeMallIndexConfigGoodsVO : newBeeMallIndexConfigGoodsVOS) {
String goodsName = newBeeMallIndexConfigGoodsVO.getGoodsName();
String goodsIntro = newBeeMallIndexConfigGoodsVO.getGoodsIntro();
// 字符串过长导致文字超出的问题
if (goodsName.length() > 30) {
goodsName = goodsName.substring(0, 30) + "...";
newBeeMallIndexConfigGoodsVO.setGoodsName(goodsName);
}
if (goodsIntro.length() > 22) {
goodsIntro = goodsIntro.substring(0, 22) + "...";
newBeeMallIndexConfigGoodsVO.setGoodsIntro(goodsIntro);
}
}
2. 新蜂商城用户操作相关接口 New Bee Mall Personal API
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dvY15n7L-1638114613555)(C:\Users\chen\AppData\Roaming\Typora\typora-user-images\image-20211128224037337.png)]
/api/v1/user
文件: NewBeeMallPersonalAPI.java
作用: 商城用户操作
代码分析:
a. NewBeeMallPersonalAPI
无论是登录接口 登出接口 用户注册 修改用户信息 主体都是同一套写法
- 调用NewBeeMallUserService中的方法(login,logout,register ,updateUserInfo)
- 调用Logger的info方法,添加相关信息
- 处理成功和失败的情况
例子: 登录接口
@PostMapping("/user/login")
@ApiOperation(value = "登录接口", notes = "返回token")
public Result<String> login(@RequestBody @Valid MallUserLoginParam mallUserLoginParam) {
if (!NumberUtil.isPhone(mallUserLoginParam.getLoginName())){
return ResultGenerator.genFailResult(ServiceResultEnum.LOGIN_NAME_IS_NOT_PHONE.getResult());
}
String loginResult = newBeeMallUserService.login(mallUserLoginParam.getLoginName(), mallUserLoginParam.getPasswordMd5());
logger.info("login api,loginName={},loginResult={}", mallUserLoginParam.getLoginName(), loginResult);
//登录成功
if (!StringUtils.isEmpty(loginResult) && loginResult.length() == Constants.TOKEN_LENGTH) {
Result result = ResultGenerator.genSuccessResult();
result.setData(loginResult);
return result;
}
//登录失败
return ResultGenerator.genFailResult(loginResult);
}
b. NewBeeMallUserServiceImpl
(个人想法:token其实是记录在线情况)
login
- selectByLoginNameAndPasswd方法在tb_newbee_mall_user表中找到对应的user,无则为null
MallUser user = mallUserMapper.selectByLoginNameAndPasswd(loginName, passwordMD5);
- 判断返回的user是不是null,null直接返回错误信息,不是null就继续下一步。
if (user != null)
-
登录后分三种情况,
第一种情况:新用户,没有登陆过,则为tb_newbee_mall_user_token添加一条新的token数据;
第二种情况:用户登陆过并退出了,token表没有他的信息,也为他在tb_newbee_mall_user_token添加一条新的token数据;
第三种情况:用户登陆过但没有退出,token表里有他的信息,则直接给tb_newbee_mall_user_token对应的用户修改token数据;
(作者太聪明了)
if (user.getLockedFlag() == 1) { return ServiceResultEnum.LOGIN_USER_LOCKED_ERROR.getResult(); } //登录后即执行修改token的操作 String token = getNewToken(System.currentTimeMillis() + "", user.getUserId()); MallUserToken mallUserToken = newBeeMallUserTokenMapper.selectByPrimaryKey(user.getUserId()); //当前时间 Date now = new Date(); //过期时间 Date expireTime = new Date(now.getTime() + 2 * 24 * 3600 * 1000);//过期时间 48 小时 if (mallUserToken == null) { mallUserToken = new MallUserToken(); mallUserToken.setUserId(user.getUserId()); mallUserToken.setToken(token); mallUserToken.setUpdateTime(now); mallUserToken.setExpireTime(expireTime); //新增一条token数据 if (newBeeMallUserTokenMapper.insertSelective(mallUserToken) > 0) { //新增成功后返回 return token; } } else { mallUserToken.setToken(token); mallUserToken.setUpdateTime(now); mallUserToken.setExpireTime(expireTime); //更新 if (newBeeMallUserTokenMapper.updateByPrimaryKeySelective(mallUserToken) > 0) { //修改成功后返回 return token; } }
logout
直接调用delete方法,根据登录的用户id删除对应tb_newbee_mall_user_token表的token数据
@Override
public Boolean logout(Long userId) {
return newBeeMallUserTokenMapper.deleteByPrimaryKey(userId) > 0;
}
register
insert方法
@Override
public String register(String loginName, String password) {
if (mallUserMapper.selectByLoginName(loginName) != null) {
return ServiceResultEnum.SAME_LOGIN_NAME_EXIST.getResult();
}
MallUser registerUser = new MallUser();
registerUser.setLoginName(loginName);
registerUser.setNickName(loginName);
registerUser.setIntroduceSign(Constants.USER_INTRO);
String passwordMD5 = MD5Util.MD5Encode(password, "UTF-8");
registerUser.setPasswordMd5(passwordMD5);
if (mallUserMapper.insertSelective(registerUser) > 0) {
return ServiceResultEnum.SUCCESS.getResult();
}
return ServiceResultEnum.DB_ERROR.getResult();
}
updateUserInfo
分开写,先seletct id找到对应的用户,存入user,修改user对象的相关信息,最后将user对象传给update方法
@Override
public Boolean updateUserInfo(MallUserUpdateParam mallUser, Long userId) {
MallUser user = mallUserMapper.selectByPrimaryKey(userId);
if (user == null) {
NewBeeMallException.fail(ServiceResultEnum.DATA_NOT_EXIST.getResult());
}
user.setNickName(mallUser.getNickName());
//user.setPasswordMd5(mallUser.getPasswordMd5());
//若密码为空字符,则表明用户不打算修改密码,使用原密码保存
if (!MD5Util.MD5Encode("", "UTF-8").equals(mallUser.getPasswordMd5())){
user.setPasswordMd5(mallUser.getPasswordMd5());
}
user.setIntroduceSign(mallUser.getIntroduceSign());
if (mallUserMapper.updateByPrimaryKeySelective(user) > 0) {
return true;
}
return false;
}
特殊 :获取用户信息
@GetMapping("/user/info")
@ApiOperation(value = "获取用户信息", notes = "")
public Result<NewBeeMallUserVO> getUserDetail(@TokenToMallUser MallUser loginMallUser) {
//已登录则直接返回
NewBeeMallUserVO mallUserVO = new NewBeeMallUserVO();
BeanUtil.copyProperties(loginMallUser, mallUserVO);
return ResultGenerator.genSuccessResult(mallUserVO);
}
用到的是一个BeanUtil类的copyProperties方法,(存疑:为什么不用select方法呢?)
3.新蜂商城分类页面接口 New Bee Mall Goods Category API
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VBRNetJW-1638114613557)(C:\Users\chen\AppData\Roaming\Typora\typora-user-images\image-20211128224054900.png)]
/api/v1/categories
文件: NewBeeMallGoodsCategoryAPI.java
作用: 获取分类数据
代码分析:
NewBeeMallGoodsCategoryAPI.java
创建了NewBeeMallCategoryService对象
@Resource
private NewBeeMallCategoryService newBeeMallCategoryService;
@GetMapping("/categories")
@ApiOperation(value = "获取分类数据", notes = "分类页面使用")
public Result<List<NewBeeMallIndexCategoryVO>> getCategories() {
List<NewBeeMallIndexCategoryVO> categories = newBeeMallCategoryService.getCategoriesForIndex();
if (CollectionUtils.isEmpty(categories)) {
NewBeeMallException.fail(ServiceResultEnum.DATA_NOT_EXIST.getResult());
}
return ResultGenerator.genSuccessResult(categories);
NewBeeMallCategoryServiceImpl 中的 getCategoriesForIndex()方法
1. 获取三个级的数据
首先看一下表里的数据部分属性
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0Qg5w7o9-1638114613558)(C:\Users\chen\AppData\Roaming\Typora\typora-user-images\image-20211128205531699.png)]
category_id :分类id
category_level : 分类等级 有三级
parent_id : 它对应的上一级的分类id
调用selectByLevelAndParentIdsAndNumber方法(GoodsCategoryMapper.xml)
根据三种category_level来获取三个分类等级的列表
//获取一级分类的固定数量的数据
List<GoodsCategory> firstLevelCategories = goodsCategoryMapper.selectByLevelAndParentIdsAndNumber(Collections.singletonList(0L), NewBeeMallCategoryLevelEnum.LEVEL_ONE.getLevel(), Constants.INDEX_CATEGORY_NUMBER);
if (!CollectionUtils.isEmpty(firstLevelCategories)) {
List<Long> firstLevelCategoryIds = firstLevelCategories.stream().map(GoodsCategory::getCategoryId).collect(Collectors.toList());
//获取二级分类的数据
List<GoodsCategory> secondLevelCategories = goodsCategoryMapper.selectByLevelAndParentIdsAndNumber(firstLevelCategoryIds, NewBeeMallCategoryLevelEnum.LEVEL_TWO.getLevel(), 0);
if (!CollectionUtils.isEmpty(secondLevelCategories)) {
List<Long> secondLevelCategoryIds = secondLevelCategories.stream().map(GoodsCategory::getCategoryId).collect(Collectors.toList());
//获取三级分类的数据
List<GoodsCategory> thirdLevelCategories = goodsCategoryMapper.selectByLevelAndParentIdsAndNumber(secondLevelCategoryIds, NewBeeMallCategoryLevelEnum.LEVEL_THREE.getLevel(), 0);
2.处理三个级的数据
我们看看部分处理结果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sxx63pPQ-1638114613559)(C:\Users\chen\AppData\Roaming\Typora\typora-user-images\image-20211128210511769.png)]
处理步骤
- 根据parentId给第三级分类保存到map中
- 每一组parentId对应第二级的categoryId,然后把1中对应的map添加到第二级的"thirdLevelCategoryVOS"属性
- 根据parentId给第二级分类保存到map中
- 每一组parentId对应第一级的categoryId,然后把3中对应的map添加到第一级的"secondLevelCategoryVOS"属性
代码:
if (!CollectionUtils.isEmpty(thirdLevelCategories)) {
//根据 parentId 将 thirdLevelCategories 分组
Map<Long, List<GoodsCategory>> thirdLevelCategoryMap = thirdLevelCategories.stream().collect(groupingBy(GoodsCategory::getParentId));
List<SecondLevelCategoryVO> secondLevelCategoryVOS = new ArrayList<>();
//处理二级分类
for (GoodsCategory secondLevelCategory : secondLevelCategories) {
SecondLevelCategoryVO secondLevelCategoryVO = new SecondLevelCategoryVO();
BeanUtil.copyProperties(secondLevelCategory, secondLevelCategoryVO);
//如果该二级分类下有数据则放入 secondLevelCategoryVOS 对象中
if (thirdLevelCategoryMap.containsKey(secondLevelCategory.getCategoryId())) {
//根据二级分类的id取出thirdLevelCategoryMap分组中的三级分类list
List<GoodsCategory> tempGoodsCategories = thirdLevelCategoryMap.get(secondLevelCategory.getCategoryId());
secondLevelCategoryVO.setThirdLevelCategoryVOS((BeanUtil.copyList(tempGoodsCategories, ThirdLevelCategoryVO.class)));
secondLevelCategoryVOS.add(secondLevelCategoryVO);
}
}
//处理一级分类
if (!CollectionUtils.isEmpty(secondLevelCategoryVOS)) {
//根据 parentId 将 thirdLevelCategories 分组
Map<Long, List<SecondLevelCategoryVO>> secondLevelCategoryVOMap = secondLevelCategoryVOS.stream().collect(groupingBy(SecondLevelCategoryVO::getParentId));
for (GoodsCategory firstCategory : firstLevelCategories) {
NewBeeMallIndexCategoryVO newBeeMallIndexCategoryVO = new NewBeeMallIndexCategoryVO();
BeanUtil.copyProperties(firstCategory, newBeeMallIndexCategoryVO);
//如果该一级分类下有数据则放入 newBeeMallIndexCategoryVOS 对象中
if (secondLevelCategoryVOMap.containsKey(firstCategory.getCategoryId())) {
//根据一级分类的id取出secondLevelCategoryVOMap分组中的二级级分类list
List<SecondLevelCategoryVO> tempGoodsCategories = secondLevelCategoryVOMap.get(firstCategory.getCategoryId());
newBeeMallIndexCategoryVO.setSecondLevelCategoryVOS(tempGoodsCategories);
newBeeMallIndexCategoryVOS.add(newBeeMallIndexCategoryVO);
}
}
}
}
心得:学会分类思想,在数据库的表时添加要用的属性,category_level ,caparent_id。
4.新蜂商城商品相关接口 New Bee Mall Goods API
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ua3wM5BT-1638114613560)(C:\Users\chen\AppData\Roaming\Typora\typora-user-images\image-20211128224108494.png)]
文件: NewBeeMallGoodsAPI.java
/api/v1/goods/detail/{goodsId}
作用: 商品详情接口
/api/v1/search
作用: 商品搜索接口
代码分析:
1. 搜索接口
1.NewBeeMallGoodsAPI.java
创建NewBeeMallGoodsService对象
@Resource
private NewBeeMallGoodsService newBeeMallGoodsService;
作者自己定义了一个PageQueryUtil,分页查询的参数(以后分页可以参考)
public class PageQueryUtil extends LinkedHashMap<String, Object> {
//当前页码
private int page;
//每页条数
private int limit;
public PageQueryUtil(Map<String, Object> params) {
this.putAll(params);
//分页参数
this.page = Integer.parseInt(params.get("page").toString());
this.limit = Integer.parseInt(params.get("limit").toString());
this.put("start", (page - 1) * limit);
this.put("page", page);
this.put("limit", limit);
}
public int getPage() {
return page;
}
public void setPage(int page) {
this.page = page;
}
public int getLimit() {
return limit;
}
public void setLimit(int limit) {
this.limit = limit;
}
@Override
public String toString() {
return "PageUtil{" +
"page=" + page +
", limit=" + limit +
'}';
}
}
- 将搜索接口传入的关键字 id order 页码等数据存入到Map中(对象名为params)
- 将Map封装到PageQueryUtil对象中
- 将PageQueryUtil对象交给newBeeMallGoodsService的searchNewBeeMallGoods方法
@GetMapping("/search")
@ApiOperation(value = "商品搜索接口", notes = "根据关键字和分类id进行搜索")
public Result<PageResult<List<NewBeeMallSearchGoodsVO>>> search(@RequestParam(required = false) @ApiParam(value = "搜索关键字") String keyword,
@RequestParam(required = false) @ApiParam(value = "分类id") Long goodsCategoryId,
@RequestParam(required = false) @ApiParam(value = "orderBy") String orderBy,
@RequestParam(required = false) @ApiParam(value = "页码") Integer pageNumber,
@TokenToMallUser MallUser loginMallUser) {
logger.info("goods search api,keyword={},goodsCategoryId={},orderBy={},pageNumber={},userId={}", keyword, goodsCategoryId, orderBy, pageNumber, loginMallUser.getUserId());
Map params = new HashMap(8);
//两个搜索参数都为空,直接返回异常
if (goodsCategoryId == null && StringUtils.isEmpty(keyword)) {
NewBeeMallException.fail("非法的搜索参数");
}
if (pageNumber == null || pageNumber < 1) {
pageNumber = 1;
}
params.put("goodsCategoryId", goodsCategoryId);
params.put("page", pageNumber);
params.put("limit", Constants.GOODS_SEARCH_PAGE_LIMIT);
//对keyword做过滤 去掉空格
if (!StringUtils.isEmpty(keyword)) {
params.put("keyword", keyword);
}
if (!StringUtils.isEmpty(orderBy)) {
params.put("orderBy", orderBy);
}
//搜索上架状态下的商品
params.put("goodsSellStatus", Constants.SELL_STATUS_UP);
//封装商品数据
PageQueryUtil pageUtil = new PageQueryUtil(params);
return ResultGenerator.genSuccessResult(newBeeMallGoodsService.searchNewBeeMallGoods(pageUtil));
}
2. NewBeeMallGoodsServiceImpl.java
- 根据传入的PageUtil找到goodsList商品列表
- 记录找到多少个商品
- 转为泛型列表
- 处理字符过长问题
- 拿PageResult保存最后结果(PageResult类是分页工具类)
@Override
public PageResult searchNewBeeMallGoods(PageQueryUtil pageUtil) {
List<NewBeeMallGoods> goodsList = goodsMapper.findNewBeeMallGoodsListBySearch(pageUtil);
int total = goodsMapper.getTotalNewBeeMallGoodsBySearch(pageUtil);
List<NewBeeMallSearchGoodsVO> newBeeMallSearchGoodsVOS = new ArrayList<>();
if (!CollectionUtils.isEmpty(goodsList)) {
newBeeMallSearchGoodsVOS = BeanUtil.copyList(goodsList, NewBeeMallSearchGoodsVO.class);
for (NewBeeMallSearchGoodsVO newBeeMallSearchGoodsVO : newBeeMallSearchGoodsVOS) {
String goodsName = newBeeMallSearchGoodsVO.getGoodsName();
String goodsIntro = newBeeMallSearchGoodsVO.getGoodsIntro();
// 字符串过长导致文字超出的问题
if (goodsName.length() > 28) {
goodsName = goodsName.substring(0, 28) + "...";
newBeeMallSearchGoodsVO.setGoodsName(goodsName);
}
if (goodsIntro.length() > 30) {
goodsIntro = goodsIntro.substring(0, 30) + "...";
newBeeMallSearchGoodsVO.setGoodsIntro(goodsIntro);
}
}
}
PageResult pageResult = new PageResult(newBeeMallSearchGoodsVOS, total, pageUtil.getLimit(), pageUtil.getPage());
return pageResult;
}
2.详情接口
传入商品id,获取对应商品所有信息
1.NewBeeMallGoodsAPI.java
@GetMapping("/goods/detail/{goodsId}")
@ApiOperation(value = "商品详情接口", notes = "传参为商品id")
public Result<NewBeeMallGoodsDetailVO> goodsDetail(@ApiParam(value = "商品id") @PathVariable("goodsId") Long goodsId, @TokenToMallUser MallUser loginMallUser) {
logger.info("goods detail api,goodsId={},userId={}", goodsId, loginMallUser.getUserId());
if (goodsId < 1) {
return ResultGenerator.genFailResult("参数异常");
}
NewBeeMallGoods goods = newBeeMallGoodsService.getNewBeeMallGoodsById(goodsId);
if (goods == null) {
return ResultGenerator.genFailResult("参数异常");
}
if (Constants.SELL_STATUS_UP != goods.getGoodsSellStatus()) {
NewBeeMallException.fail(ServiceResultEnum.GOODS_PUT_DOWN.getResult());
}
NewBeeMallGoodsDetailVO goodsDetailVO = new NewBeeMallGoodsDetailVO();
BeanUtil.copyProperties(goods, goodsDetailVO);
goodsDetailVO.setGoodsCarouselList(goods.getGoodsCarousel().split(","));
return ResultGenerator.genSuccessResult(goodsDetailVO);
}
2. NewBeeMallGoodsServiceImpl.java
@Override
public NewBeeMallGoods getNewBeeMallGoodsById(Long id) {
return goodsMapper.selectByPrimaryKey(id);
}
5.新蜂商城购物车相关接口 New Bee Mall Shopping Cart API
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MzWWTgHf-1638114613562)(C:\Users\chen\AppData\Roaming\Typora\typora-user-images\image-20211128222948995.png)]
1. NewBeeMallShoppingCartAPI.java
每一种方法,代码主体都基本一样,调用NewBeeMallShoppingCartService的方法,判断成功与否
@GetMapping("/shop-cart/page")
@ApiOperation(value = "购物车列表(每页默认5条)", notes = "传参为页码")
public Result<PageResult<List<NewBeeMallShoppingCartItemVO>>> cartItemPageList(Integer pageNumber, @TokenToMallUser MallUser loginMallUser) {
Map params = new HashMap(8);
if (pageNumber == null || pageNumber < 1) {
pageNumber = 1;
}
params.put("userId", loginMallUser.getUserId());
params.put("page", pageNumber);
params.put("limit", Constants.SHOPPING_CART_PAGE_LIMIT);
//封装分页请求参数
PageQueryUtil pageUtil = new PageQueryUtil(params);
return ResultGenerator.genSuccessResult(newBeeMallShoppingCartService.getMyShoppingCartItems(pageUtil));
}
2. NewBeeMallShoppingCartServiceImpl.java
get方法
三种get方法1,getNewBeeMallCartItemById,getMyShoppingCartItems,getCartItemsForSettle都是简单的select操作,
delete方法
需要考虑userId不同,不能删除
@Override
public Boolean deleteById(Long shoppingCartItemId, Long userId) {
NewBeeMallShoppingCartItem newBeeMallShoppingCartItem = newBeeMallShoppingCartItemMapper.selectByPrimaryKey(shoppingCartItemId);
if (newBeeMallShoppingCartItem == null) {
return false;
}
//userId不同不能删除
if (!userId.equals(newBeeMallShoppingCartItem.getUserId())) {
return false;
}
return newBeeMallShoppingCartItemMapper.deleteByPrimaryKey(shoppingCartItemId) > 0;
}
insert方法
添加商品要考虑的情况
- 商品已存在
- 商品为空
- 商品最大数量问题
@Override
public String saveNewBeeMallCartItem(SaveCartItemParam saveCartItemParam, Long userId) {
NewBeeMallShoppingCartItem temp = newBeeMallShoppingCartItemMapper.selectByUserIdAndGoodsId(userId, saveCartItemParam.getGoodsId());
if (temp != null) {
//已存在则修改该记录
NewBeeMallException.fail(ServiceResultEnum.SHOPPING_CART_ITEM_EXIST_ERROR.getResult());
}
NewBeeMallGoods newBeeMallGoods = newBeeMallGoodsMapper.selectByPrimaryKey(saveCartItemParam.getGoodsId());
//商品为空
if (newBeeMallGoods == null) {
return ServiceResultEnum.GOODS_NOT_EXIST.getResult();
}
int totalItem = newBeeMallShoppingCartItemMapper.selectCountByUserId(userId);
//超出单个商品的最大数量
if (saveCartItemParam.getGoodsCount() < 1) {
return ServiceResultEnum.SHOPPING_CART_ITEM_NUMBER_ERROR.getResult();
}
//超出单个商品的最大数量
if (saveCartItemParam.getGoodsCount() > Constants.SHOPPING_CART_ITEM_LIMIT_NUMBER) {
return ServiceResultEnum.SHOPPING_CART_ITEM_LIMIT_NUMBER_ERROR.getResult();
}
//超出最大数量
if (totalItem > Constants.SHOPPING_CART_ITEM_TOTAL_NUMBER) {
return ServiceResultEnum.SHOPPING_CART_ITEM_TOTAL_NUMBER_ERROR.getResult();
}
NewBeeMallShoppingCartItem newBeeMallShoppingCartItem = new NewBeeMallShoppingCartItem();
BeanUtil.copyProperties(saveCartItemParam, newBeeMallShoppingCartItem);
newBeeMallShoppingCartItem.setUserId(userId);
//保存记录
if (newBeeMallShoppingCartItemMapper.insertSelective(newBeeMallShoppingCartItem) > 0) {
return ServiceResultEnum.SUCCESS.getResult();
}
return ServiceResultEnum.DB_ERROR.getResult();
}
update方法
update考虑情况
- 商品最大数量
- userId不同
- 没有修改的情况,可以不用操作
//超出单个商品的最大数量
if (updateCartItemParam.getGoodsCount() > Constants.SHOPPING_CART_ITEM_LIMIT_NUMBER) {
return ServiceResultEnum.SHOPPING_CART_ITEM_LIMIT_NUMBER_ERROR.getResult();
}
//当前登录账号的userId与待修改的cartItem中userId不同,返回错误
if (!newBeeMallShoppingCartItemUpdate.getUserId().equals(userId)) {
return ServiceResultEnum.NO_PERMISSION_ERROR.getResult();
}
//数值相同,则不执行数据操作
if (updateCartItemParam.getGoodsCount().equals(newBeeMallShoppingCartItemUpdate.getGoodsCount())) {
return ServiceResultEnum.SUCCESS.getResult();
}
6.新蜂商城个人地址相关接口 New Bee Mall User Address API
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PiBj40HO-1638114613564)(C:\Users\chen\AppData\Roaming\Typora\typora-user-images\image-20211128223411451.png)]
调用NewBeeMallUserAddressService的方法,都是CRUD 的比较简单的操作 比一般多一个默认的收货地址,不多做分析
例子:修改地址
- 传入数据
- 进行CRUD操作
- 判断成功或失败
@PutMapping("/address")
@ApiOperation(value = "修改地址", notes = "")
public Result<Boolean> updateMallUserAddress(@RequestBody UpdateMallUserAddressParam updateMallUserAddressParam,
@TokenToMallUser MallUser loginMallUser) {
MallUserAddress mallUserAddressById = mallUserAddressService.getMallUserAddressById(updateMallUserAddressParam.getAddressId());
if (!loginMallUser.getUserId().equals(mallUserAddressById.getUserId())) {
return ResultGenerator.genFailResult(ServiceResultEnum.REQUEST_FORBIDEN_ERROR.getResult());
}
MallUserAddress userAddress = new MallUserAddress();
BeanUtil.copyProperties(updateMallUserAddressParam, userAddress);
userAddress.setUserId(loginMallUser.getUserId());
Boolean updateResult = mallUserAddressService.updateMallUserAddress(userAddress);
//修改成功
if (updateResult) {
return ResultGenerator.genSuccessResult();
}
//修改失败
return ResultGenerator.genFailResult("修改失败");
}
的收货地址,不多做分析
例子:修改地址
- 传入数据
- 进行CRUD操作
- 判断成功或失败
@PutMapping("/address")
@ApiOperation(value = "修改地址", notes = "")
public Result<Boolean> updateMallUserAddress(@RequestBody UpdateMallUserAddressParam updateMallUserAddressParam,
@TokenToMallUser MallUser loginMallUser) {
MallUserAddress mallUserAddressById = mallUserAddressService.getMallUserAddressById(updateMallUserAddressParam.getAddressId());
if (!loginMallUser.getUserId().equals(mallUserAddressById.getUserId())) {
return ResultGenerator.genFailResult(ServiceResultEnum.REQUEST_FORBIDEN_ERROR.getResult());
}
MallUserAddress userAddress = new MallUserAddress();
BeanUtil.copyProperties(updateMallUserAddressParam, userAddress);
userAddress.setUserId(loginMallUser.getUserId());
Boolean updateResult = mallUserAddressService.updateMallUserAddress(userAddress);
//修改成功
if (updateResult) {
return ResultGenerator.genSuccessResult();
}
//修改失败
return ResultGenerator.genFailResult("修改失败");
}