上一篇: 微服务(七)—— 收货地址模块(backend-shipping).
一、创建项目
首先创建一个SpringBoot项目,具体创建过程和 微服务(三)—— 用户模块(backend-user).一样。
二、项目结构
1.目录结构
项目结构就是Controller、Service、Dao三层结构,由于订单和购物车耦合度比较高,所以我就将购物和订单业务放在一个模块了。
1.1购物车
(1)Controller层
主要是提供给前端的增删改查接口,完整的类如下:
package com.aiun.order.controller;
/**
* 购物车控制层
* @author lenovo
*/
@Api(tags = "购物车接口")
@RestController
@RequestMapping("/cart/")
public class CartController {
@Autowired
private ICartService iCartService;
@Autowired
private RedisTemplate<String, String> redisTemplate;
/**
* 添加购物车功能
* @param productId 产品id
* @param count 产品个数
* @return 封装好的购物车 VO
*/
@GetMapping("add")
@ApiOperation(value = "购物车添加商品功能")
public ServerResponse<CartVO> add(HttpServletRequest request, Integer productId, Integer count) {
ServerResponse hasLogin = loginHasExpired(request);
if (hasLogin.isSuccess()) {
User user = (User) hasLogin.getData();
return iCartService.add(user.getId(), productId, count);
}
return hasLogin;
}
/**
* 更新购物车商品功能
* @param productId 产品 id
* @param count 产品个数
* @return 封装好的购物车 VO
*/
@PostMapping("update")
@ApiOperation(value = "更新购物车商品功能")
public ServerResponse<CartVO> update(HttpServletRequest request, Integer productId, Integer count) {
ServerResponse hasLogin = loginHasExpired(request);
if (hasLogin.isSuccess()) {
User user = (User) hasLogin.getData();
return iCartService.update(user.getId(), productId, count);
}
return hasLogin;
}
/**
* 删除购物车商品
* @param productIds 多个产品的id
* @return 封装好的购物车 VO
*/
@DeleteMapping("delete_product")
@ApiOperation(value = "删除购物车商品")
public ServerResponse<CartVO> deleteProduct(HttpServletRequest request, String productIds) {
ServerResponse hasLogin = loginHasExpired(request);
if (hasLogin.isSuccess()) {
User user = (User) hasLogin.getData();
return iCartService.deleteProduct(user.getId(), productIds);
}
return hasLogin;
}
/**
* 查询购物车商品列表
* @return 封装好的购物车 VO
*/
@PostMapping("list")
@ApiOperation(value = "查询购物车商品列表")
public ServerResponse<CartVO> list(HttpServletRequest request) {
ServerResponse hasLogin = loginHasExpired(request);
if (hasLogin.isSuccess()) {
User user = (User) hasLogin.getData();
return iCartService.list(user.getId());
}
return hasLogin;
}
/**
* 全选
* @return 封装好的购物车 VO
*/
@PostMapping("select_all")
@ApiOperation(value = "购物车全选")
public ServerResponse<CartVO> selectAll(HttpServletRequest request) {
ServerResponse hasLogin = loginHasExpired(request);
if (hasLogin.isSuccess()) {
User user = (User) hasLogin.getData();
return iCartService.selectOrUnSelect(user.getId(), null, CartConst.Cart.CHECKED);
}
return hasLogin;
}
/**
* 反选
* @return 封装好的购物车 VO
*/
@PostMapping("un_select_all")
@ApiOperation(value = "反选")
public ServerResponse<CartVO> unSelectAll(HttpServletRequest request) {
ServerResponse hasLogin = loginHasExpired(request);
if (hasLogin.isSuccess()) {
User user = (User) hasLogin.getData();
return iCartService.selectOrUnSelect(user.getId(), null, CartConst.Cart.UN_CHECKED);
}
return hasLogin;
}
/**
* 单独选
* @return 封装好的购物车 VO
*/
@PostMapping("select")
@ApiOperation(value = "单独选")
public ServerResponse<CartVO> select(HttpServletRequest request, Integer productId) {
ServerResponse hasLogin = loginHasExpired(request);
if (hasLogin.isSuccess()) {
User user = (User) hasLogin.getData();
return iCartService.selectOrUnSelect(user.getId(), productId, CartConst.Cart.CHECKED);
}
return hasLogin;
}
/**
* 单独反选
* @return 封装好的购物车 VO
*/
@PostMapping("un_select")
@ResponseBody
@ApiOperation(value = "单独反选")
public ServerResponse<CartVO> unSelect(HttpServletRequest request, Integer productId) {
ServerResponse hasLogin = loginHasExpired(request);
if (hasLogin.isSuccess()) {
User user = (User) hasLogin.getData();
return iCartService.selectOrUnSelect(user.getId(), productId, CartConst.Cart.UN_CHECKED);
}
return hasLogin;
}
/**
* 查询当前用户购物车里产品的数量
* @return 返回产品的数量
*/
@PostMapping("get_cart_product_count")
@ResponseBody
@ApiOperation(value = "查询当前用户购物车里产品的数量")
public ServerResponse<Integer> getCartProductCount(HttpServletRequest request) {
ServerResponse hasLogin = loginHasExpired(request);
if (hasLogin.isSuccess()) {
User user = (User) hasLogin.getData();
return iCartService.getCartProductCount(user.getId());
}
return hasLogin;
}
/**
* 判断用户登录是否过期
*/
private ServerResponse<User> loginHasExpired(HttpServletRequest request) {
String key = request.getHeader(UserConst.AUTHORITY);
ValueOperations<String, String> valueOperations = redisTemplate.opsForValue();
String value = valueOperations.get(key);
if (StringUtils.isEmpty(value)) {
return ServerResponse.createByErrorMessage(ResponseCode.NEED_LOGIN.getCode(), ResponseCode.NEED_LOGIN.getDesc());
}
User user = JsonUtils.jsonStr2Object(value, User.class);
if (!key.equals(user.getUsername())) {
return ServerResponse.createByErrorMessage(ResponseCode.NEED_LOGIN.getCode(), ResponseCode.NEED_LOGIN.getDesc());
}
valueOperations.set(key, value, 1, TimeUnit.HOURS);
return ServerResponse.createBySuccess(user);
}
}
该类里面有一个私有的方法,是用于用户权限验证的,具体看:微服务(十)—— 单点登录.
(2)Service层
该层是业务的主要实现,下面看一下Service的实现类:
package com.aiun.order.service.impl;
/**
* @author lenovo
*/
@Service("iCartService")
public class CartServiceImpl implements ICartService {
@Autowired
private CartMapper cartMapper;
@Autowired
private ProductFeign productFeign;
@Value("mageHost")
private String mageHost;
@Override
public ServerResponse<CartVO> add(Integer userId, Integer productId, Integer count) {
if (productId == null || count == null) {
return ServerResponse.createByErrorMessage(ResponseCode.ILLEGAL_ARGUMENT.getCode(), ResponseCode.ILLEGAL_ARGUMENT.getDesc());
}
Cart cart = cartMapper.selectCartByUserIdProductId(userId, productId);
if (cart == null) {
//产品不在购物车里,需要新增一个这个产品的记录
Cart cartItem = new Cart();
cartItem.setQuantity(count)
.setChecked(CartConst.Cart.CHECKED)
.setProductId(productId)
.setUserId(userId);
int resultCount = cartMapper.insert(cartItem);
if (resultCount == 0){
return ServerResponse.createByErrorMessage("添加购物车失败");
}
} else {
//这个产品已经在购物车里了
//需要将数量相加
count = cart.getQuantity() + count;
cart.setQuantity(count);
cartMapper.updateByPrimaryKeySelective(cart);
}
return this.list(userId);
}
@Override
public ServerResponse<CartVO> update(Integer userId, Integer productId, Integer count) {
if (productId == null || count == null) {
return ServerResponse.createByErrorMessage(ResponseCode.ILLEGAL_ARGUMENT.getCode(), ResponseCode.ILLEGAL_ARGUMENT.getDesc());
}
Cart cart = cartMapper.selectCartByUserIdProductId(userId, productId);
if (cart != null) {
cart.setQuantity(count);
}
int resoultCount = cartMapper.updateByPrimaryKeySelective(cart);
if (resoultCount == 0) {
return ServerResponse.createByErrorMessage("更新购物车失败");
}
return this.list(userId);
}
@Override
public ServerResponse<CartVO> deleteProduct(Integer userId, String productIds) {
List<String> productList = new ArrayList<>();
String[] productIdStrings = productIds.split(",");
for(String id : productIdStrings) {
productList.add(id);
}
if (productList.isEmpty()) {
return ServerResponse.createByErrorMessage(ResponseCode.ILLEGAL_ARGUMENT.getCode(), ResponseCode.ILLEGAL_ARGUMENT.getDesc());
}
cartMapper.deleteByUserIdProductIds(userId, productList);
return this.list(userId);
}
@Override
public ServerResponse<CartVO> list(Integer userId) {
CartVO cartVo = this.getCartVoLimit(userId);
return ServerResponse.createBySuccess(cartVo);
}
@Override
public ServerResponse<CartVO> selectOrUnSelect(Integer userId, Integer productId, Integer checked) {
cartMapper.checkedOrUnCheckedProduct(userId, productId, checked);
return this.list(userId);
}
@Override
public ServerResponse<Integer> getCartProductCount(Integer userId) {
if (userId == null) {
return ServerResponse.createBySuccess(0);
}
return ServerResponse.createBySuccess(cartMapper.selectCartProductCount(userId));
}
/**
* 封装前端用到的购物车数据
* @param userId 用户 id
* @return 封装的购物车 VO
*/
private CartVO getCartVoLimit(Integer userId) {
CartVO cartVo = new CartVO();
List<Cart> cartList = cartMapper.selectCartByUserId(userId);
List<CartProductVO> cartProductVOList = Lists.newArrayList();
BigDecimal cartTotalPrice = new BigDecimal("0");
if (!cartList.isEmpty()) {
for (Cart cartItem : cartList) {
CartProductVO cartProductVo = new CartProductVO();
cartProductVo.setId(cartItem.getId())
.setProductId(cartItem.getProductId())
.setUserId(cartItem.getUserId());
Product product = productFeign.findById(cartItem.getProductId());
if (product != null) {
cartProductVo.setProductMainImage(product.getMainImage())
.setProductName(product.getName())
.setProductPrice(product.getPrice())
.setProductStatus(product.getStatus())
.setProductSubtitle(product.getSubtitle())
.setProductStock(product.getStock());
//判断库存
int buyLimitCount = 0;
if (product.getStock() >= cartItem.getQuantity()) {
buyLimitCount = cartItem.getQuantity();
cartProductVo.setLimitQuantity(CartConst.LIMIT_NUM_SUCCESS);
} else {
buyLimitCount = product.getStock();
cartProductVo.setLimitQuantity(CartConst.LIMIT_NUM_FAIL);
//购物车中更新有效库存
Cart cartForQuantity = new Cart();
cartForQuantity.setId(cartItem.getId())
.setQuantity(buyLimitCount);
cartMapper.updateByPrimaryKeySelective(cartForQuantity);
}
cartProductVo.setQuantity(buyLimitCount);
//计算总价
cartProductVo.setProductTotalPrice(BigDecimalUtils.mul(product.getPrice().doubleValue(), cartProductVo.getQuantity()))
.setProductChecked(cartItem.getChecked());
}
if (cartItem.getChecked() == CartConst.Cart.CHECKED) {
//如果已经勾选,增加到整个购物车的总价中
cartTotalPrice = BigDecimalUtils.add(cartTotalPrice.doubleValue(), cartProductVo.getProductTotalPrice().doubleValue());
}
cartProductVOList.add(cartProductVo);
}
}
cartVo.setCartTotalPrice(cartTotalPrice)
.setCartProductVOList(cartProductVOList)
.setAllChecked(this.getAllCheckedStatus(userId))
.setImageHost(mageHost);
return cartVo;
}
/**
* 查询是否是全选中状态
* @param userId 用户 id
* @return 产品是否是选中状态
*/
private boolean getAllCheckedStatus(Integer userId) {
if (userId == null) {
return false;
}
return cartMapper.selectCartProductCheckedStatusByUserId(userId) == 0;
}
}
这个类主要是订单的创建,以及查询商品的选中状态,然后计算价格。
计算价格的时候需要注意精度丢失问题,这里选用BigDecimal类,通过字符串的方式构造对象可以防止精度丢失。
(3)Dao层
首先是Mapper接口层
package com.aiun.order.mapper;
@Mapper
@Component
public interface CartMapper {
/**
* 通过主键删除购物车
* @param id 主键
* @return 影响的记录行
*/
int deleteByPrimaryKey(Integer id);
/**
* 插入购物车信息
* @param record 购物车信息
* @return 影响的记录行
*/
int insert(@Param("record") Cart record);
/**
* 有选择的插入购物车信息
* @param record 购物车信息
* @return 影响的记录行
*/
int insertSelective(@Param("record") Cart record);
/**
* 通过主键有选择的插入
* @param id 主键
* @return 购物车信息
*/
Cart selectByPrimaryKey(Integer id);
/**
* 通过主键有选择的更新购物车数据
* @param record 购物车
* @return 影响的记录行
*/
int updateByPrimaryKeySelective(@Param("record") Cart record);
/**
* 通过主键更新购物车数据
* @param record 购物车信息
* @return 影响的记录行
*/
int updateByPrimaryKey(@Param("record") Cart record);
/**
* 根据用户Id查询商品选中状态
* @param userId 用户 id
* @return 所有购物车信息
*/
List<Cart> selectCheckedCartByUserId(Integer userId);
/**
* 通过用户id和产品id查询购物车
* @param userId 用户 id
* @param productId 产品 id
* @return 购物车信息
*/
Cart selectCartByUserIdProductId(@Param("userId") Integer userId, @Param("productId") Integer productId);
/**
* 通过用户id查询购物车
* @param userId 用户 id
* @return 购物车信息
*/
List<Cart> selectCartByUserId(Integer userId);
/**
* 根据用户id和产品id删除
* @param userId 用户 id
* @param productIds 需要删除的产品 id
* @return 影响的记录行
*/
int deleteByUserIdProductIds(@Param("userId") Integer userId, @Param("productIds") List<String> productIds);
/**
* 更新购物车产品的选中状态
* @param userId 用户 id
* @param productId 需要选中的产品 id
* @param checked 选中状态
* @return 影响的记录行
*/
int checkedOrUnCheckedProduct(@Param("userId") Integer userId, @Param("productId") Integer productId, @Param("checked") Integer checked);
/**
* 获取购物车产品数量
* @param userId 用户 id
* @return 影响的记录行
*/<