eshop商城项目实训系列教程导航
要先把基本框架搭建起来,才能够愉快的写代码
先看下数据库表结构
要在eshop-business
模块下新建src\main\java
的文件夹,在该文件夹下创建com.eshop.entity
的包,在该包下创建StoreProductRelation
的实体类与之数据表一一对应,代码如下:
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
的部分代码如下:
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;
在mapper
的包下新建ProductRelationMapper
的类,代码如下:
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> {
}
在service
的包下新建ProductRelationService
的类,代码如下:
package com.eshop.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.eshop.domain.StoreProductRelation;
public interface ProductRelationService extends IService<StoreProductRelation> {
}
新建ProductRelationServiceImpl
的实现类,实现ProductRelationService
的接口,代码如下:
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;
}
在com.eshop
下新建一个包,包名叫controller
,新建一个类,类名叫ProductCollectController
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;
}
前端发过来的请求(使用的是post方式):http://localhost:8008/api/collect/add
看报错Request method 'POST' not supported
,不支持请求方法“POST”
开始编写controller层的代码
/**
* 添加收藏
* @param param body部分,需要查询的参数
* @return 是否成功:成功 true,失败 false
*/
@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();
}
部分注解说明:
service接口:
/**
* 是否收藏
* @param productId 商品ID
* @param uid 用户ID
* @return Boolean
*/
Boolean isProductRelation(long productId, long uid);
实现该业务功能:
/**
* 是否收藏
* @param productId 商品Id
* @param uid 用户Id
* @return
*/
@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;
}
service接口:
/**
* 添加收藏
* @param productId 商品Id
* @param uid 用户Id
* @param category
*/
void addProductCollect(long productId, long uid, String category);
实现该业务功能:
/**
* 添加收藏
* @param productId 商品Id
* @param uid 用户Id
* @param category
*/
@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);
}
前端发过来的请求(使用的是post方式):http://localhost:8008/api/collect/del
controller层:
/**
* 取消收藏
* @param param body部分,需要查询的参数
* @return 是否成功:成功 true,失败 false
*/
@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接口:
/**
* 取消收藏
* @param productId 商品Id
* @param uid 用户Id
* @param category
*/
void delProductCollect(long productId, long uid, String category);
实现该业务功能:
/**
* 取消收藏
*
* @param productId 商品Id
* @param uid 用户Id
* @param category
*/
@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());
}
前端发过来的请求(使用的是post方式):http://localhost:8008/api/collect/dels/{productIds}
controller层:
/**
* 批量删除收藏/足迹 collect收藏 foot 足迹
* @param productIds 商品Id
* @param param body部分,需要查询的参数
* @return 是否成功:成功 true,失败 false
*/
@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();
}
前端发过来的请求(使用的是get方式):http://localhost:8008/api/collect/user?limit=10&page=1&type=foot
后端接受的请求:
@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
层代码,添加下面的接口代码
/**
* 获取用户收藏列表
* @param page 页码,默认为 1
* @param limit 页大小,默认为 10
* @param type foot为足迹,collect为收藏
* @return
*/
@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
的类,代码如下:
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层:
/**
* 获取用户收藏列表
* @param limit 页大小
* @param page 页码
* @param type foot为足迹 collect为收藏
* @param uid 用户Id
* @return
*/
List<StoreProductRelationQueryVo> userProductCollect(int page, int limit, Long uid, String type);
实现该业务功能:
/**
* 获取用户收藏列表
* @param limit 页大小
* @param page 页码
* @param type foot为足迹 collect为收藏
* @param uid 用户Id
* @return
*/
@Override
public List<StoreProductRelationQueryVo> userProductCollect(int page, int limit, Long uid, String type) {
// IPage<StoreProductRelation> pageModel = new Page<>(page, limit);
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语句如下:
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层:
@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错误。正常情况应该显示该手机号已存在。
\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
的对象做判断是否为空对象
@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
进行是否为空对象判断:
if (ObjectUtil.isNotNull(shopUser)){
return ApiResult.fail("用户已存在");
}
经测试后成功!!