基本架构 要先把基本框架搭建起来,才能够愉快的写代码
1. entity 先看下数据库表结构
要在eshop-business模块下新建src\main\java的文件夹,在该文件夹下创建com.eshop.entity的包,在该包下创建StoreProductRelation的实体类与之数据表一一对应,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 package com.eshop.domain;import com.baomidou.mybatisplus.annotation.IdType;import com.baomidou.mybatisplus.annotation.TableId;import lombok.AllArgsConstructor;import lombok.Builder;import lombok.Data;import lombok.NoArgsConstructor;@Data @AllArgsConstructor @NoArgsConstructor public class StoreProductRelation extends BaseDomain { private static final long serialVersionUID = 1L ; @TableId(value = "id", type = IdType.AUTO) private Long id; private Long uid; private Long productId; private String type; private String category; }
因为该表继承了BaseDomain,拥有父的属性
BaseDomain的部分代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 private static final long serialVersionUID = 1L ; @TableField( fill = FieldFill.INSERT ) @JsonFormat( pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8" ) private Date createTime; @TableField( fill = FieldFill.UPDATE ) @JsonFormat( pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8" ) private Date updateTime; @TableLogic @JsonIgnore @TableField( fill = FieldFill.INSERT ) private Integer isDel;
2. mapper 在mapper的包下新建ProductRelationMapper的类,代码如下:
1 2 3 4 5 6 7 8 9 package com.eshop.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;import com.eshop.domain.StoreProductRelation;import org.apache.ibatis.annotations.Mapper;@Mapper public interface ProductRelationMapper extends BaseMapper <StoreProductRelation> {}
3. service 在service的包下新建ProductRelationService的类,代码如下:
1 2 3 4 5 6 7 package com.eshop.service;import com.baomidou.mybatisplus.extension.service.IService;import com.eshop.domain.StoreProductRelation;public interface ProductRelationService extends IService <StoreProductRelation> {}
新建ProductRelationServiceImpl的实现类,实现ProductRelationService的接口,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 package com.eshop.service.impl;import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;import com.eshop.api.EshopException;import com.eshop.domain.StoreProductRelation;import com.eshop.mapper.ProductRelationMapper;import com.eshop.service.ProductRelationService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;@Service public class ProductRelationServiceImpl extends ServiceImpl <ProductRelationMapper, StoreProductRelation> implements ProductRelationService { @Autowired private ProductRelationMapper productRelationMapper; }
4. controller 在com.eshop下新建一个包,包名叫controller,新建一个类,类名叫ProductCollectController
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 package com.eshop.controller;import cn.hutool.core.util.NumberUtil;import com.eshop.api.ApiResult;import com.eshop.api.EshopException;import com.eshop.common.bean.LocalUser;import com.eshop.common.interceptor.AuthCheck;import com.eshop.modules.product.param.StoreProductRelationQueryParam;import com.eshop.service.ProductRelationService;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.validation.annotation.Validated;import org.springframework.web.bind.annotation.PostMapping;import org.springframework.web.bind.annotation.RequestBody;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;@Slf4j @RestController @RequestMapping("/collect") public class ProductCollectController { @Autowired private ProductRelationService productRelationService; }
功能编码 1. 商品添加收藏 前端发过来的请求(使用的是post方式):http://localhost:8008/api/collect/add
看报错Request method 'POST' not supported,不支持请求方法“POST”
开始编写controller层的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @PostMapping("/add") @AuthCheck public ApiResult<Boolean> addProductCollect (@Validated @RequestBody StoreProductRelationQueryParam param) { Long uid = LocalUser.getUser().getUid(); if (!NumberUtil.isNumber(param.getId())){ throw new EshopException ("参数非法" ); } productRelationService.addProductCollect(Long.valueOf(param.getId()), uid, param.getCategory()); return ApiResult.ok(); }
部分注解说明:
@NoRepeatSubmit:防止重复提交自定义注解 @AuthCheck:自定义注解实现用户行为认证 @Validated:参数验证注解 1.1 是否收藏 service接口:
1 2 3 4 5 6 7 Boolean isProductRelation (long productId, long uid) ;
实现该业务功能:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @Override public boolean isProductCollect (long productId, long uid) { LambdaQueryWrapper<StoreProductRelation> lqw = new LambdaQueryWrapper <>(); lqw.eq(StoreProductRelation::getProductId, productId) .eq(StoreProductRelation::getUid, uid) .eq(StoreProductRelation::getType, "collect" ); int count = productRelationMapper.selectCount(lqw); if (count > 0 ) { return true ; } return false ; }
1.2 添加收藏 service接口:
1 2 3 4 5 6 7 void addProductCollect (long productId, long uid, String category) ;
实现该业务功能:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @Override public void addProductCollect (long productId, long uid, String category) { if (isProductCollect(productId, uid)){ throw new EshopException ("已收藏" ); } StoreProductRelation storeProductRelation = new StoreProductRelation (); storeProductRelation.setProductId(productId); storeProductRelation.setUid(uid); storeProductRelation.setType(category); productRelationMapper.insert(storeProductRelation); }
2. 商品取消收藏 前端发过来的请求(使用的是post方式):http://localhost:8008/api/collect/del
controller层:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @NoRepeatSubmit @PostMapping("/del") @AuthCheck public ApiResult<Boolean> delProductCollect (@Validated @RequestBody StoreProductRelationQueryParam param) { Long uid = LocalUser.getUser().getUid(); if (!NumberUtil.isNumber(param.getId())){ throw new EshopException ("参数非法" ); } productRelationService.delProductCollect(Long.valueOf(param.getId()), uid, param.getCategory()); return ApiResult.ok(); }
service接口:
1 2 3 4 5 6 7 void delProductCollect (long productId, long uid, String category) ;
实现该业务功能:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @Override public void delProductCollect (long productId, long uid, String category) { LambdaQueryWrapper<StoreProductRelation> lqw = new LambdaQueryWrapper <>(); lqw.eq(StoreProductRelation::getProductId, productId) .eq(StoreProductRelation::getUid, uid) .eq(StoreProductRelation::getType, category); StoreProductRelation productRelation = productRelationMapper.selectOne(lqw); if (productRelation == null ) { throw new EshopException ("已取消" ); } this .removeById(productRelation.getId()); }
3. 批量删除收藏/足迹 前端发过来的请求(使用的是post方式):http://localhost:8008/api/collect/dels/{productIds}
controller层:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 @NoRepeatSubmit @PostMapping("/dels/{productIds}") @AuthCheck @Transactional public ApiResult<Boolean> delCollects (@PathVariable String productIds,@RequestBody StoreProductRelationQueryParam param) { Long uid = LocalUser.getUser().getUid(); String[] ids = productIds.split("," ); if (ids.length > 0 ){ for (String id : ids) { productRelationService.delProductCollect(Long.valueOf(id), uid, param.getCategory()); } }else { throw new EshopException ("参数非法" ); } return ApiResult.ok(); }
4. 获取收藏或足迹 前端发过来的请求(使用的是get方式):http://localhost:8008/api/collect/user?limit=10&page=1&type=foot
后端接受的请求:
1 2 3 4 5 6 7 8 @GetMapping("/user") @AuthCheck public ApiResult<List<StoreProductRelationQueryVo>> UserCollect (@RequestParam(value = "page", defaultValue = "1") int page, @RequestParam(value = "limit", defaultValue = "10") int limit, @RequestParam(value = "type") String type) { log.info("limit: {}" , limit); log.info("page: {}" , page); log.info("type: {}" , type); return null ; }
发现可以接收到前端参数后,开始补全controller层代码,添加下面的接口代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @GetMapping("/user") @AuthCheck public ApiResult<List<StoreProductRelationQueryVo>> UserCollect (@RequestParam(value = "page", defaultValue = "1") int page, @RequestParam(value = "limit", defaultValue = "10") int limit, @RequestParam(value = "type") String type) { Long uid = LocalUser.getUser().getUid(); List<StoreProductRelationQueryVo> storeProductRelationQueryVos = productRelationService.userProductCollect(page, limit, uid, type); return ApiResult.ok(storeProductRelationQueryVos); }
此时我们发现API文档要返回的数据并不能够满足我们的需求
通过分析返回的数据是来自store_product_relation和store_product两张表的字段,因此需要在vo包下造个StoreProductRelationQueryVo的类,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 package com.eshop.vo;import com.eshop.serializer.DoubleSerializer;import com.fasterxml.jackson.databind.annotation.JsonSerialize;import lombok.Data;import java.io.Serializable;import java.util.Date;@Data public class StoreProductRelationQueryVo implements Serializable { private static final Long serialVersion = 1L ; private Long id; private Long uid; private Long productId; private String type; private String category; private Date createTime; private Long pid; private String image; private String storeName; private Double price; @JsonSerialize(using = DoubleSerializer.class) private Double otPrice; private Integer sales; private Integer isShow; private Integer isIntegral; private Integer integer; }
service层:
1 2 3 4 5 6 7 8 9 List<StoreProductRelationQueryVo> userProductCollect (int page, int limit, Long uid, String type) ;
实现该业务功能:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Override public List<StoreProductRelationQueryVo> userProductCollect (int page, int limit, Long uid, String type) { Page<StoreProductRelation> pageModel = new Page <>(page, limit); List<StoreProductRelationQueryVo> list = productRelationMapper.selectRelationList(pageModel, uid, type); return list; }
注意:这里不能够用mybatis-plus的分页插件,控制台会报Handler dispatch failed; nested exception is java.lang.NoSuchMethodError的错误,我推测的sql语句的问题
因为该业务涉及到多表查询,mybatisplus并未给我们提供相关可以调用的接口,所以我们需要自己编写sql语句,去实现我们的需求。sql语句如下:
1 select B.id pid,A.type as category,B.store_name as storeName,B.price,B.is_integral as isIntegral, B.ot_price as otPrice,B.sales,B.image,B.is_show as isShow,B.integral as integral from store_product_relation A left join store_product B on A.product_id = B.id where A.type='foot' and A.uid=1 and A.is_del = 0 and B.is_del = 0 order by A.create_time desc
mapper层:
1 2 3 4 5 @Select("select B.id pid,A.type as category,B.store_name as storeName,B.price,B.is_integral as isIntegral," + "B.ot_price as otPrice,B.sales,B.image,B.is_show as isShow,B.integral as integral" + " from store_product_relation A left join store_product B " + "on A.product_id = B.id where A.type=#{type} and A.uid=#{uid} and A.is_del = 0 and B.is_del = 0 order by A.create_time desc") List<StoreProductRelationQueryVo> selectRelationList (Page page, @Param("uid") Long uid, @Param("type") String type) ;
至此功能编码完毕!!
程序排错 任务描述 接口地址/api/register输入相同的手机号注册新用户时,页面显示了SQL错误。正常情况应该显示该手机号已存在。
1 \r\n### Error updating database. Cause: java.sql.SQLIntegrityConstraintViolationException: Duplicate entry '13512345678' for key 'username'\r\n### The error may exist in com/eshop/modules/user/service/mapper/UserMapper.java (best guess)\r\n### The error may involve com.eshop.modules.user.service.mapper.UserMapper.insert-Inline\r\n### The error occurred while setting parameters\r\n### SQL: INSERT INTO eshop_user ( username, password, nickname, avatar, phone, add_ip, last_ip, user_type, create_time, is_del ) VALUES ( ?, ?, ?, ?, ?, ?, ?, ?, ?, ? )\r\n### Cause: java.sql.SQLIntegrityConstraintViolationException: Duplicate entry '13512345678' for key 'username'\n; Duplicate entry '13512345678' for key 'username'; nested exception is java.sql.SQLIntegrityConstraintViolationException: Duplicate entry '13512345678' for key 'username'
要求 修改完代码之后,相同手机号注册时应提示手机号已存在。
代码编写 对该接口代码进行分析,发现并未对获得shopUser的对象做判断是否为空对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @PostMapping("/register") @ApiOperation(value = "H5/APP注册新用户", notes = "H5/APP注册新用户") public ApiResult<String> register (@Validated @RequestBody RegParam param) { Object codeObj = redisUtil.get("code_" + param.getAccount()); if (codeObj == null ){ return ApiResult.fail("请先获取验证码" ); } String code = codeObj.toString(); if (!StrUtil.equals(code, param.getCaptcha())) { return ApiResult.fail("验证码错误" ); } ShopUser shopUser = userService.getOne(Wrappers.<ShopUser>lambdaQuery() .eq(ShopUser::getPhone,param.getAccount()),false ); authService.register(param); return ApiResult.ok("" ,"注册成功" ); }
因此编写以下代码对shopUser进行是否为空对象判断:
1 2 3 if (ObjectUtil.isNotNull(shopUser)){ return ApiResult.fail("用户已存在" ); }
经测试后成功!!