diff --git a/xdclass-common/pom.xml b/xdclass-common/pom.xml index 2f8bacf..cf6b753 100644 --- a/xdclass-common/pom.xml +++ b/xdclass-common/pom.xml @@ -108,6 +108,12 @@ redisson + + com.alibaba + fastjson + 1.2.75 + + diff --git a/xdclass-common/src/main/java/net/jieyuu/constant/CacheKey.java b/xdclass-common/src/main/java/net/jieyuu/constant/CacheKey.java index 697ba96..52c14cc 100644 --- a/xdclass-common/src/main/java/net/jieyuu/constant/CacheKey.java +++ b/xdclass-common/src/main/java/net/jieyuu/constant/CacheKey.java @@ -4,5 +4,10 @@ public class CacheKey { /** * 注册验证码,第一个是类型,第二个是接收号码 */ - public final static String CHECK_CODE_KEY="code:%s:%s"; + public final static String CHECK_CODE_KEY = "code:%s:%s"; + + /** + * 购物车 hash 结果 , key是用户唯一标识 + */ + public final static String CART_KEY = "cart:%s"; } diff --git a/xdclass-common/src/main/java/net/jieyuu/enums/BizCodeEnum.java b/xdclass-common/src/main/java/net/jieyuu/enums/BizCodeEnum.java index 9773ff7..8af4ac4 100644 --- a/xdclass-common/src/main/java/net/jieyuu/enums/BizCodeEnum.java +++ b/xdclass-common/src/main/java/net/jieyuu/enums/BizCodeEnum.java @@ -14,75 +14,109 @@ public enum BizCodeEnum { */ OPS_REPEAT(110001, "重复操作"), + /** + * 购物车 + */ + CART_FILE(220001, "添加购物车失败"), + /** * 验证码 */ CODE_TO_ERROR(240001, "接收号码不合规"), + CODE_LIMITED(240002, "验证码发送过快"), + CODE_ERROR(240003, "验证码错误"), + CODE_CAPTCHA(240101, "图形验证码错误"), /** * 账号 */ ACCOUNT_REPEAT(250001, "账号已经存在"), + ACCOUNT_UNREGISTER(250002, "账号不存在"), + ACCOUNT_PWD_ERROR(250003, "账号或者密码错误"), + ACCOUNT_UNLOGIN(250004, "账号未登录"), /** * 优惠券 */ - COUPON_CONDITION_ERROR(270001,"优惠券条件错误"), - COUPON_UNAVAILABLE(270002,"没有可用的优惠券"), - COUPON_NO_EXITS(270003,"优惠券不存在"), - COUPON_NO_STOCK(270005,"优惠券库存不足"), - COUPON_OUT_OF_LIMIT(270006,"优惠券领取超过限制次数"), - COUPON_OUT_OF_TIME(270407,"优惠券不在领取时间范围"), - COUPON_GET_FAIL(270407,"优惠券领取失败"), - COUPON_RECORD_LOCK_FAIL(270409,"优惠券锁定失败"), + COUPON_CONDITION_ERROR(270001, "优惠券条件错误"), + + COUPON_UNAVAILABLE(270002, "没有可用的优惠券"), + + COUPON_NO_EXITS(270003, "优惠券不存在"), + + COUPON_NO_STOCK(270005, "优惠券库存不足"), + + COUPON_OUT_OF_LIMIT(270006, "优惠券领取超过限制次数"), + + COUPON_OUT_OF_TIME(270407, "优惠券不在领取时间范围"), + + COUPON_GET_FAIL(270407, "优惠券领取失败"), + + COUPON_RECORD_LOCK_FAIL(270409, "优惠券锁定失败"), /** * 订单 */ - ORDER_CONFIRM_COUPON_FAIL(280001,"创建订单-优惠券使用失败,不满足价格条件"), - ORDER_CONFIRM_PRICE_FAIL(280002,"创建订单-验价失败"), - ORDER_CONFIRM_LOCK_PRODUCT_FAIL(280003,"创建订单-商品库存不足锁定失败"), - ORDER_CONFIRM_ADD_STOCK_TASK_FAIL(280004,"创建订单-新增商品库存锁定任务"), - ORDER_CONFIRM_TOKEN_NOT_EXIST(280008,"订单令牌缺少"), - ORDER_CONFIRM_TOKEN_EQUAL_FAIL(280009,"订单令牌不正确"), - ORDER_CONFIRM_NOT_EXIST(280010,"订单不存在"), - ORDER_CONFIRM_CART_ITEM_NOT_EXIST(280011,"购物车商品项不存在"), + ORDER_CONFIRM_COUPON_FAIL(280001, "创建订单-优惠券使用失败,不满足价格条件"), + + ORDER_CONFIRM_PRICE_FAIL(280002, "创建订单-验价失败"), + + ORDER_CONFIRM_LOCK_PRODUCT_FAIL(280003, "创建订单-商品库存不足锁定失败"), + + ORDER_CONFIRM_ADD_STOCK_TASK_FAIL(280004, "创建订单-新增商品库存锁定任务"), + + ORDER_CONFIRM_TOKEN_NOT_EXIST(280008, "订单令牌缺少"), + + ORDER_CONFIRM_TOKEN_EQUAL_FAIL(280009, "订单令牌不正确"), + + ORDER_CONFIRM_NOT_EXIST(280010, "订单不存在"), + + ORDER_CONFIRM_CART_ITEM_NOT_EXIST(280011, "购物车商品项不存在"), /** * 收货地址 */ - ADDRESS_ADD_FAIL(290001,"新增收货地址失败"), - ADDRESS_DEL_FAIL(290002,"删除收货地址失败"), - ADDRESS_NO_EXITS(290003,"地址不存在"), + ADDRESS_ADD_FAIL(290001, "新增收货地址失败"), + + ADDRESS_DEL_FAIL(290002, "删除收货地址失败"), + + ADDRESS_NO_EXITS(290003, "地址不存在"), /** * 支付 */ - PAY_ORDER_FAIL(300001,"创建支付订单失败"), - PAY_ORDER_CALLBACK_SIGN_FAIL(300002,"支付订单回调验证签失败"), - PAY_ORDER_CALLBACK_NOT_SUCCESS(300003,"创建支付订单失败"), - PAY_ORDER_NOT_EXIST(300005,"订单不存在"), - PAY_ORDER_STATE_ERROR(300006,"订单状态不正常"), - PAY_ORDER_PAY_TIMEOUT(300007,"订单支付超时"), + PAY_ORDER_FAIL(300001, "创建支付订单失败"), + + PAY_ORDER_CALLBACK_SIGN_FAIL(300002, "支付订单回调验证签失败"), + + PAY_ORDER_CALLBACK_NOT_SUCCESS(300003, "创建支付订单失败"), + + PAY_ORDER_NOT_EXIST(300005, "订单不存在"), + + PAY_ORDER_STATE_ERROR(300006, "订单状态不正常"), + + PAY_ORDER_PAY_TIMEOUT(300007, "订单支付超时"), /** * 流控操作 */ - CONTROL_FLOW(500101,"限流控制"), - CONTROL_DEGRADE(500201,"降级控制"), - CONTROL_AUTH(500301,"认证控制"), + CONTROL_FLOW(500101, "限流控制"), + + CONTROL_DEGRADE(500201, "降级控制"), + + CONTROL_AUTH(500301, "认证控制"), /** * 文件相关 */ - FILE_UPLOAD_USER_IMG_FAIL(600101,"用户头像文件上传失败"); + FILE_UPLOAD_USER_IMG_FAIL(600101, "用户头像文件上传失败"); @Getter private int code; diff --git a/xdclass-product-service/src/main/java/net/jieyuu/config/InterceptorConfig.java b/xdclass-product-service/src/main/java/net/jieyuu/config/InterceptorConfig.java index 0e5ffbe..ff0d6d9 100644 --- a/xdclass-product-service/src/main/java/net/jieyuu/config/InterceptorConfig.java +++ b/xdclass-product-service/src/main/java/net/jieyuu/config/InterceptorConfig.java @@ -16,7 +16,7 @@ public class InterceptorConfig implements WebMvcConfigurer { registry .addInterceptor(new LoginInterceptor()) //拦截的路径 - .addPathPatterns("") + .addPathPatterns("api/cart/*/**") //放行的路径 .excludePathPatterns("", ""); diff --git a/xdclass-product-service/src/main/java/net/jieyuu/controller/CartController.java b/xdclass-product-service/src/main/java/net/jieyuu/controller/CartController.java new file mode 100644 index 0000000..2ef371d --- /dev/null +++ b/xdclass-product-service/src/main/java/net/jieyuu/controller/CartController.java @@ -0,0 +1,45 @@ +package net.jieyuu.controller; + + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import net.jieyuu.request.CartItemRequest; +import net.jieyuu.service.CartService; +import net.jieyuu.service.ProductService; +import net.jieyuu.utils.JsonData; +import net.jieyuu.vo.ProductVO; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.Map; + + +/** + *

+ * 前端控制器 + *

+ * + * @author jieyuu + * @since 2024-08-07 + */ + +@Api("购物车") +@RestController +@RequestMapping("/api/cart/v1") +public class CartController { + + @Autowired + private CartService cartService; + + + @ApiOperation("添加到购物车") + @PostMapping("add") + public JsonData addToCart(@ApiParam("购物项") @RequestBody CartItemRequest cartItemRequest) { + cartService.addToCart(cartItemRequest); + return JsonData.buildSuccess(); + + } + + +} \ No newline at end of file diff --git a/xdclass-product-service/src/main/java/net/jieyuu/model/ProductDO.java b/xdclass-product-service/src/main/java/net/jieyuu/model/ProductDO.java index 98a9982..6f9a00c 100644 --- a/xdclass-product-service/src/main/java/net/jieyuu/model/ProductDO.java +++ b/xdclass-product-service/src/main/java/net/jieyuu/model/ProductDO.java @@ -45,12 +45,12 @@ public class ProductDO implements Serializable { /** * 老价格 */ - private BigDecimal oldPrice; + private BigDecimal oldAmount; /** * 新价格 */ - private BigDecimal price; + private BigDecimal amount; /** * 库存 diff --git a/xdclass-product-service/src/main/java/net/jieyuu/request/CartItemRequest.java b/xdclass-product-service/src/main/java/net/jieyuu/request/CartItemRequest.java new file mode 100644 index 0000000..7fd2e80 --- /dev/null +++ b/xdclass-product-service/src/main/java/net/jieyuu/request/CartItemRequest.java @@ -0,0 +1,21 @@ +package net.jieyuu.request; + +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + + +@Data +@ApiModel +public class CartItemRequest { + + + @ApiModelProperty(value = "商品id", example = "11") + @JsonProperty("product_id") + private long productId; + + @ApiModelProperty(value = "购买数量", example = "11") + @JsonProperty("buy_num") + private int buyNum; +} diff --git a/xdclass-product-service/src/main/java/net/jieyuu/service/CartService.java b/xdclass-product-service/src/main/java/net/jieyuu/service/CartService.java new file mode 100644 index 0000000..43e6c3e --- /dev/null +++ b/xdclass-product-service/src/main/java/net/jieyuu/service/CartService.java @@ -0,0 +1,11 @@ +package net.jieyuu.service; + +import net.jieyuu.request.CartItemRequest; + +public interface CartService { + /** + * 添加商品到购物车 + * @param cartItemRequest + */ + public void addToCart(CartItemRequest cartItemRequest); +} diff --git a/xdclass-product-service/src/main/java/net/jieyuu/service/impl/CartServiceImpl.java b/xdclass-product-service/src/main/java/net/jieyuu/service/impl/CartServiceImpl.java new file mode 100644 index 0000000..5e48991 --- /dev/null +++ b/xdclass-product-service/src/main/java/net/jieyuu/service/impl/CartServiceImpl.java @@ -0,0 +1,92 @@ +package net.jieyuu.service.impl; + + +import com.alibaba.fastjson.JSON; +import lombok.extern.slf4j.Slf4j; +import net.jieyuu.constant.CacheKey; +import net.jieyuu.enums.BizCodeEnum; +import net.jieyuu.exception.BizException; +import net.jieyuu.interceptor.LoginInterceptor; +import net.jieyuu.model.LoginUser; +import net.jieyuu.request.CartItemRequest; +import net.jieyuu.service.CartService; +import net.jieyuu.service.ProductService; +import net.jieyuu.vo.CartItemVO; +import net.jieyuu.vo.ProductVO; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.BoundHashOperations; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Service; + +@Service +@Slf4j +public class CartServiceImpl implements CartService { + + @Autowired + private ProductService productService; + @Autowired + private RedisTemplate redisTemplate; + + @Override + public void addToCart(CartItemRequest cartItemRequest) { + long productId = cartItemRequest.getProductId(); + int buyNum = cartItemRequest.getBuyNum(); + + //获取购物车 + BoundHashOperations myCart = getMyCartOps(); + + Object cacheObj = myCart.get(productId); + String result = ""; + + if (cacheObj != null) { + result = (String) cacheObj; + } + if (StringUtils.isBlank(result)) { + //不存在 + + CartItemVO cartItemVO = new CartItemVO(); + + ProductVO productVO = productService.findDetailById(productId); + + if (productVO == null) { + throw new BizException(BizCodeEnum.CART_FILE); + } + + cartItemVO.setAmount(productVO.getAmount()); + cartItemVO.setBuyNum(buyNum); + cartItemVO.setProductId(productId); + cartItemVO.setProductImg(productVO.getCoverImg()); + cartItemVO.setProductTitle(productVO.getTitle()); + + myCart.put(productId, JSON.toJSONString(cartItemVO)); + } else { + //存在商品,修改购物车 + CartItemVO cartItem = JSON.parseObject(result, CartItemVO.class); + cartItem.setBuyNum(cartItem.getBuyNum() + buyNum); + myCart.put(productId, JSON.toJSONString(cartItem)); + } + } + + /** + * 抽取购物车 通用方法 + * + * @return + */ + private BoundHashOperations getMyCartOps() { + String cartKey = getCartKey(); + return redisTemplate.boundHashOps(cartKey); + } + + /** + * 购物车 key + * + * @return + */ + private String getCartKey() { + LoginUser loginUser = LoginInterceptor.threadLocal.get(); + String cartKey = String.format(CacheKey.CART_KEY, loginUser.getId()); + + return cartKey; + } +} diff --git a/xdclass-product-service/src/main/java/net/jieyuu/vo/CartItemVO.java b/xdclass-product-service/src/main/java/net/jieyuu/vo/CartItemVO.java new file mode 100644 index 0000000..8322789 --- /dev/null +++ b/xdclass-product-service/src/main/java/net/jieyuu/vo/CartItemVO.java @@ -0,0 +1,91 @@ +package net.jieyuu.vo; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.math.BigDecimal; + +public class CartItemVO { + + /** + * 商品id + */ + @JsonProperty("product_id") + private Long productId; + + /** + * 购买数量 + */ + @JsonProperty("buy_num") + private Integer buyNum; + /** + * 商品标题 + */ + @JsonProperty("product_title") + private String productTitle; + /** + * 商品图片 + */ + @JsonProperty("product_img") + private String productImg; + + /** + * 商品单价 + */ + private BigDecimal amount; + /** + * 商品总价 + */ + @JsonProperty("total_amount") + private BigDecimal totalAmount; + + + public Long getProductId() { + return productId; + } + + public void setProductId(Long productId) { + this.productId = productId; + } + + + public Integer getBuyNum() { + return buyNum; + } + + public void setBuyNum(Integer buyNum) { + this.buyNum = buyNum; + } + + public String getProductTitle() { + return productTitle; + } + + public void setProductTitle(String productTitle) { + this.productTitle = productTitle; + } + + public String getProductImg() { + return productImg; + } + + public void setProductImg(String productImg) { + this.productImg = productImg; + } + + public BigDecimal getAmount() { + return amount; + } + + public void setAmount(BigDecimal amount) { + this.amount = amount; + } + + /** + * 商品总价 * 商品数量 + * @return + */ + public BigDecimal getTotalAmount() { + return this.amount.multiply(new BigDecimal(this.buyNum)); + } + +} diff --git a/xdclass-product-service/src/main/java/net/jieyuu/vo/CartVO.java b/xdclass-product-service/src/main/java/net/jieyuu/vo/CartVO.java new file mode 100644 index 0000000..6ad3158 --- /dev/null +++ b/xdclass-product-service/src/main/java/net/jieyuu/vo/CartVO.java @@ -0,0 +1,69 @@ +package net.jieyuu.vo; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.math.BigDecimal; +import java.util.List; + +public class CartVO { + @JsonProperty("cart_items") + private List cartItems; + + + @JsonProperty("total_num") + private Integer totalNum; + @JsonProperty("total_amount") + private BigDecimal totalAmount; + + @JsonProperty("real_pay_amount") + private BigDecimal realPayAmount; + + public void setCartItems(List cartItems) { + this.cartItems = cartItems; + } + + public List getCartItems() { + return cartItems; + } + + public Integer getTotalNum() { + if (this.cartItems != null) { + int total = cartItems.stream().mapToInt(CartItemVO::getBuyNum).sum(); + return total; + } + return 0; + } + + /** + * 总价格 + * + * @return + */ + public BigDecimal getTotalAmount() { + BigDecimal amount = new BigDecimal("0"); + + if (this.cartItems != null) { + for (CartItemVO cartItem : cartItems) { + BigDecimal itemTotalAmount = cartItem.getTotalAmount(); + amount = amount.add(itemTotalAmount); + } + } + return amount; + } + + /** + * 实际价格 + * + * @return + */ + public BigDecimal getRealPayAmount() { + BigDecimal amount = new BigDecimal("0"); + if (this.cartItems != null) { + for (CartItemVO cartItem : cartItems) { + BigDecimal itemTotalAmount = cartItem.getAmount(); + amount = amount.add(itemTotalAmount); + } + } + return amount; + } +} diff --git a/xdclass-product-service/src/main/java/net/jieyuu/vo/ProductVO.java b/xdclass-product-service/src/main/java/net/jieyuu/vo/ProductVO.java index 17d9364..db00e07 100644 --- a/xdclass-product-service/src/main/java/net/jieyuu/vo/ProductVO.java +++ b/xdclass-product-service/src/main/java/net/jieyuu/vo/ProductVO.java @@ -41,13 +41,13 @@ public class ProductVO { /** * 老价格 */ - @JsonProperty("old_price") - private BigDecimal oldPrice; + @JsonProperty("old_amount") + private BigDecimal oldAmount; /** * 新价格 */ - private BigDecimal price; + private BigDecimal amount; /** * 库存 diff --git a/xdclass-product-service/src/main/resources/mapper/ProductMapper.xml b/xdclass-product-service/src/main/resources/mapper/ProductMapper.xml index 10dc521..adc4657 100644 --- a/xdclass-product-service/src/main/resources/mapper/ProductMapper.xml +++ b/xdclass-product-service/src/main/resources/mapper/ProductMapper.xml @@ -8,8 +8,8 @@ - - + + @@ -17,7 +17,7 @@ - id, title, cover_img, detail, old_price, price, stock, create_time, lock_stock + id, title, cover_img, detail, old_amount, amount, stock, create_time, lock_stock