购物车相关代码

This commit is contained in:
litianxiang
2026-05-20 16:53:59 +08:00
parent 25a0ee1a3f
commit b6e4dc9673
12 changed files with 318 additions and 0 deletions

View File

@@ -0,0 +1,52 @@
package com.aida.buyer.module.cart.controller;
import com.aida.buyer.common.context.UserContext;
import com.aida.buyer.module.cart.dto.AddCartRequest;
import com.aida.buyer.module.cart.dto.CartItemDTO;
import com.aida.buyer.module.cart.service.ICartService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/buyer/cart")
@RequiredArgsConstructor
@Tag(name = "Shopping Cart", description = "Buyer shopping cart management")
public class CartController {
private final ICartService cartService;
@Operation(summary = "加购", description = "支持单个 listingId 或批量 listingIds")
@PostMapping("/add")
public void addItem(@Valid @RequestBody AddCartRequest request) {
Long buyerId = UserContext.getBuyerId();
cartService.addItem(buyerId, request);
}
@Operation(summary = "移除单个商品")
@DeleteMapping("/remove")
public void removeItem(
@Parameter(description = "商品ID") @RequestParam Long listingId) {
Long buyerId = UserContext.getBuyerId();
cartService.removeItem(buyerId, listingId);
}
@Operation(summary = "清空购物车")
@DeleteMapping("/clear")
public void clearCart() {
Long buyerId = UserContext.getBuyerId();
cartService.clearCart(buyerId);
}
@Operation(summary = "查询购物车列表", description = "返回含商品详情的购物车项列表")
@GetMapping("/list")
public List<CartItemDTO> getCartItems() {
Long buyerId = UserContext.getBuyerId();
return cartService.getCartItems(buyerId);
}
}

View File

@@ -0,0 +1,22 @@
package com.aida.buyer.module.cart.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
@Data
@Schema(description = "加购请求")
public class AddCartRequest implements Serializable {
private static final long serialVersionUID = 1L;
@Schema(description = "商品ID单个加购")
private Long listingId;
@Schema(description = "商品ID列表批量加购")
private List<Long> listingIds;
}

View File

@@ -0,0 +1,36 @@
package com.aida.buyer.module.cart.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Data
@Schema(description = "购物车项(含商品详情)")
public class CartItemDTO implements Serializable {
private static final long serialVersionUID = 1L;
@Schema(description = "购物车记录ID")
private Long cartId;
@Schema(description = "商品ID")
private Long listingId;
@Schema(description = "商品标题")
private String title;
@Schema(description = "商品封面图")
private String cover;
@Schema(description = "商品价格")
private BigDecimal price;
@Schema(description = "商品状态")
private Integer status;
@Schema(description = "加入购物车时间")
private LocalDateTime addTime;
}

View File

@@ -0,0 +1,32 @@
package com.aida.buyer.module.cart.entity;
import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDateTime;
@Data
@TableName(value = "buyer_cart", autoResultMap = true)
public class CartEntity implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(type = IdType.ASSIGN_ID)
@JsonSerialize(using = ToStringSerializer.class)
private Long id;
private Long buyerId;
private Long listingId;
private LocalDateTime addTime;
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
}

View File

@@ -0,0 +1,9 @@
package com.aida.buyer.module.cart.mapper;
import com.aida.buyer.module.cart.entity.CartEntity;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface CartMapper extends BaseMapper<CartEntity> {
}

View File

@@ -0,0 +1,17 @@
package com.aida.buyer.module.cart.service;
import com.aida.buyer.module.cart.dto.AddCartRequest;
import com.aida.buyer.module.cart.dto.CartItemDTO;
import java.util.List;
public interface ICartService {
void addItem(Long buyerId, AddCartRequest request);
void removeItem(Long buyerId, Long listingId);
void clearCart(Long buyerId);
List<CartItemDTO> getCartItems(Long buyerId);
}

View File

@@ -0,0 +1,116 @@
package com.aida.buyer.module.cart.service.impl;
import com.aida.buyer.common.result.Response;
import com.aida.buyer.module.cart.dto.AddCartRequest;
import com.aida.buyer.module.cart.dto.CartItemDTO;
import com.aida.buyer.module.cart.entity.CartEntity;
import com.aida.buyer.module.cart.mapper.CartMapper;
import com.aida.buyer.module.cart.service.ICartService;
import com.aida.buyer.module.listing.feign.ListingFeignClient;
import com.aida.buyer.module.listing.vo.ListingMallVO;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.time.LocalDateTime;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
@Service
@RequiredArgsConstructor
public class CartServiceImpl implements ICartService {
private final CartMapper cartMapper;
private final ListingFeignClient listingFeignClient;
@Override
public void addItem(Long buyerId, AddCartRequest request) {
List<Long> listingIds = resolveListingIds(request);
if (CollectionUtils.isEmpty(listingIds)) {
return;
}
for (Long listingId : listingIds) {
CartEntity existing = cartMapper.selectOne(
new LambdaQueryWrapper<CartEntity>()
.eq(CartEntity::getBuyerId, buyerId)
.eq(CartEntity::getListingId, listingId));
if (existing != null) {
existing.setAddTime(LocalDateTime.now());
cartMapper.updateById(existing);
} else {
CartEntity entity = new CartEntity();
entity.setBuyerId(buyerId);
entity.setListingId(listingId);
entity.setAddTime(LocalDateTime.now());
cartMapper.insert(entity);
}
}
}
@Override
public void removeItem(Long buyerId, Long listingId) {
cartMapper.delete(new LambdaQueryWrapper<CartEntity>()
.eq(CartEntity::getBuyerId, buyerId)
.eq(CartEntity::getListingId, listingId));
}
@Override
public void clearCart(Long buyerId) {
cartMapper.delete(new LambdaQueryWrapper<CartEntity>()
.eq(CartEntity::getBuyerId, buyerId));
}
@Override
public List<CartItemDTO> getCartItems(Long buyerId) {
List<CartEntity> cartItems = cartMapper.selectList(
new LambdaQueryWrapper<CartEntity>()
.eq(CartEntity::getBuyerId, buyerId)
.orderByDesc(CartEntity::getAddTime));
if (CollectionUtils.isEmpty(cartItems)) {
return List.of();
}
List<Long> listingIds = cartItems.stream()
.map(CartEntity::getListingId)
.toList();
Response<List<ListingMallVO>> response = listingFeignClient.getListingSummaries(listingIds);
List<ListingMallVO> listings = response != null && response.getData() != null
? response.getData()
: List.of();
Map<Long, ListingMallVO> listingMap = listings.stream()
.collect(Collectors.toMap(ListingMallVO::getId, Function.identity()));
return cartItems.stream()
.filter(item -> listingMap.containsKey(item.getListingId()))
.map(item -> {
ListingMallVO listing = listingMap.get(item.getListingId());
CartItemDTO dto = new CartItemDTO();
dto.setCartId(item.getId());
dto.setListingId(item.getListingId());
dto.setTitle(listing.getTitle());
dto.setCover(listing.getCover());
dto.setPrice(listing.getPrice());
dto.setStatus(listing.getStatus());
dto.setAddTime(item.getAddTime());
return dto;
})
.toList();
}
private List<Long> resolveListingIds(AddCartRequest request) {
List<Long> ids = new ArrayList<>();
if (request.getListingId() != null) {
ids.add(request.getListingId());
}
if (!CollectionUtils.isEmpty(request.getListingIds())) {
ids.addAll(request.getListingIds());
}
return ids;
}
}

View File

@@ -9,8 +9,11 @@ import com.aida.buyer.module.listing.vo.ListingPageVO;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.List;
/**
* 商品服务 Feign Client
*/
@@ -29,4 +32,7 @@ public interface ListingFeignClient {
@RequestParam String designFor,
@RequestParam(defaultValue = "1") int pageNum,
@RequestParam(defaultValue = "10") int pageSize);
@PostMapping("/mall/batch")
Response<List<ListingMallVO>> getListingSummaries(@RequestBody List<Long> listingIds);
}

View File

@@ -22,4 +22,6 @@ public class ListingMallVO implements Serializable {
private String title;
private BigDecimal price;
private Integer status;
}

View File

@@ -6,6 +6,8 @@ import com.aida.buyer.common.result.PageResponse;
import com.aida.buyer.common.result.Response;
import com.aida.buyer.module.account.entity.BuyerAccount;
import com.aida.buyer.module.account.mapper.BuyerAccountMapper;
import com.aida.buyer.module.cart.entity.CartEntity;
import com.aida.buyer.module.cart.mapper.CartMapper;
import com.aida.buyer.module.order.dto.AssetsDTO;
import com.aida.buyer.module.order.dto.BuyerOrdersDTO;
import com.aida.buyer.module.order.dto.CreateOrderDTO;
@@ -14,6 +16,7 @@ import com.aida.buyer.module.order.service.IBuyerOrderService;
import com.aida.buyer.module.order.vo.AssetsVO;
import com.aida.buyer.module.order.vo.BuyerOrderVO;
import com.aida.buyer.module.order.vo.CreateOrderResultVO;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
@@ -25,6 +28,7 @@ public class BuyerOrderServiceImpl implements IBuyerOrderService {
private final OrderFeignClient orderFeignClient;
private final BuyerAccountMapper buyerAccountMapper;
private final CartMapper cartMapper;
@Override
public Response<PageResponse<BuyerOrderVO>> getMyOrders(BuyerOrdersDTO dto) {
@@ -48,6 +52,13 @@ public class BuyerOrderServiceImpl implements IBuyerOrderService {
CreateOrderResultVO result = orderResp.getData();
// TODO:调用支付模块传入buyerIdresult.getOrderIds()result.getTotalAmount()
if (listingIds != null && !listingIds.isEmpty()) {
cartMapper.delete(new LambdaQueryWrapper<CartEntity>()
.eq(CartEntity::getBuyerId, buyerId)
.in(CartEntity::getListingId, listingIds));
}
return Response.success();
}

View File

@@ -18,4 +18,7 @@ public class CreateOrderResultVO implements Serializable {
@Schema(description = "所有订单总金额HK$")
private BigDecimal totalAmount;
@Schema(description = "本次成功下单的未购买商品ID列表")
private List<Long> unpurchasedListingIds;
}

View File

@@ -24,3 +24,15 @@ CREATE TABLE IF NOT EXISTS `buyer_account` (
UNIQUE KEY `uk_username` (`username`),
INDEX `idx_deleted` (`deleted`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='买家账号表';
-- 创建 buyer_cart 表
CREATE TABLE IF NOT EXISTS `buyer_cart` (
`id` BIGINT NOT NULL COMMENT '主键ID' PRIMARY KEY,
`buyer_id` BIGINT NOT NULL COMMENT '买家ID',
`listing_id` BIGINT NOT NULL COMMENT '商品ID',
`add_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '加入购物车时间',
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
UNIQUE KEY `uk_buyer_listing` (`buyer_id`, `listing_id`),
INDEX `idx_buyer_id` (`buyer_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='购物车表';