spingboot购物车模块+收获地址开发

前置准备:

Redis (高性能)+MongoDB(海量数据)+Elasticsearch/HBase(大数据数据库)

pom引入:

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.7.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.mook</groupId>
    <artifactId>shoppingmall</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>shoppingmall</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.0</version>
        </dependency>
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper-spring-boot-starter</artifactId>
            <version>1.2.13</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <dependency>
            <groupId>cn.springboot</groupId>
            <artifactId>best-pay-sdk</artifactId>
            <version>1.3.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-freemarker</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
        </dependency>
    </dependencies>

实体类:

表单数据:

//用于接收前端参数
@Data
public class CarAddForm {
    @NotNull
    private Integer productId;
    private Boolean selected = true;
}

pojo类:

//购物车需要参数,具体数量需要根据实际情况查询数据库,所以用不到
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Car {
    private Integer productId;
    private Integer quantity;
    private Boolean productSelected;
}

返回Vo:

//购物车类
@Data
public class CarVo {
    private List<CarProductVo> carProductVoList;
    private Boolean selectedAll;
    private BigDecimal carTotalPrice;
    private Integer carTotalQuantity;
}
//购物车商品类
@Data
public class CarProductVo {
    private Integer productId;
    private Integer quantity;
    private String productName;
    private String productSubtitle;
    private String productMainImage;
    private String detail;
    private String subImage;
    private BigDecimal productPrice;
    private BigDecimal productTotalPrice;
    private Integer productStock;
    private Integer productStatus;
    private Boolean productSelected;
}

Controller层:

@RestController
public class CarController {

    @Autowired
    private ICarService carService;

    /**
     * 获取购物车列表
     */
    @GetMapping("/carList")
    public ResponseVo<CarVo> list(HttpSession session){
        //注意这里参数校验异常已经在RuntimeException.notValidExceptionHandle中捕获,不用在做参数校验
        User user = (User) session.getAttribute(UserConst.CURRENT_USER);
        return carService.list(user.getId());
    }

    /**
     * 购物车添加
     */
    @PostMapping("/car")
    public ResponseVo<CarVo> add(@Valid @RequestBody CarAddForm carAddForm, HttpSession session){
        //注意这里参数校验异常已经在RuntimeException.notValidExceptionHandle中捕获,不用在做参数校验
        User user = (User) session.getAttribute(UserConst.CURRENT_USER);
        return carService.add(user.getId(),carAddForm);
    }

    @PutMapping("/car/{poductId}")
    public ResponseVo<CarVo> update(@PathVariable Integer productId, Integer quantity, Boolean selected, HttpSession session){
        User user = (User) session.getAttribute(UserConst.CURRENT_USER);
        return carService.update(user.getId(),productId,quantity,selected);
    }

    @Delete("/deleteCar/{poductId}")
    public ResponseVo<CarVo> delete(@PathVariable Integer productId, HttpSession session){
        User user = (User) session.getAttribute(UserConst.CURRENT_USER);
        return carService.delete(user.getId(),productId);
    }

    @PutMapping("/car/selectAll")
    public ResponseVo<CarVo> selectAll(HttpSession session){
        User user = (User) session.getAttribute(UserConst.CURRENT_USER);
        return carService.selectAll(user.getId());
    }

    @PutMapping("/car/unSelectAll")
    public ResponseVo<CarVo> unSelectAll(HttpSession session){
        User user = (User) session.getAttribute(UserConst.CURRENT_USER);
        return carService.unSelectAll(user.getId());
    }

    @GetMapping("/car/products/sum")
    public ResponseVo<Integer> getSum(HttpSession session){
        User user = (User) session.getAttribute(UserConst.CURRENT_USER);
        return carService.sum(user.getId());
    }
}

参数异常捕获:

@ControllerAdvice
public class RunTimeExceptionHandler {
    @ExceptionHandler(RuntimeException.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.FORBIDDEN)  //设置相应状态码
    public ResponseVo handle(RuntimeException e) {
        return ResponseVo.error(ResponseEnum.MASHINE_ERROR, e.getMessage());
    }

    //捕获自定义异常
    @ExceptionHandler(UserLoginException.class)
    @ResponseBody
    public ResponseVo userLoginHandle(){
        return ResponseVo.error(ResponseEnum.FAIL_NON_LOGIN);
    }

    //捕获参数不能为null异常
    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseBody
    public ResponseVo notValidExceptionHandle(MethodArgumentNotValidException e){
        String msg = e.getBindingResult().getFieldError().getField() + e.getBindingResult().getFieldError().getDefaultMessage();
        return ResponseVo.error(ResponseEnum.PARAMS_ERROR,msg);
    }
}

service层:

注意:查询时数量等商品变化信息要实时从数据库中获取最新值。

@Service
public class ICarService {

    private final static String CAR_REDIS_KEY_TEMPLATE = "car_%d";

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private IProductMapper productMapper;

    private Gson gson = new Gson();

    /**
     * 添加
     */
    public ResponseVo<CarVo> add(Integer uid, CarAddForm carAddForm) {
        Integer quantity = 1;
        ProductDetailVo product = productMapper.selectByPrimaryKey(carAddForm.getProductId());
        //商品是否存在
        if (product == null) {
            return ResponseVo.error(ResponseEnum.PRODUCT_NON);
        }
        //商品是否正常在售
        if (!product.getStatus().equals(ProductStatusEnum.ON_SALE.getI())) {
            return ResponseVo.error(ResponseEnum.PRODUCT_NON);
        }
        //商品库存是否充足
        if (product.getStock() <= 0) {
            return ResponseVo.error(ResponseEnum.PRODUCT_NON);
        }
        //写入到Redis , 使用gson进行序列化
        HashOperations<String, String, String> opsForHash = redisTemplate.opsForHash();
        //先要从redis中读取数量
        String redisKey = String.format(CAR_REDIS_KEY_TEMPLATE, uid);
        String value = opsForHash.get(redisKey, String.valueOf(product.getId()));
        Car car;
        if (StringUtils.isEmpty(value)) {
            //没有该商品,新增
            car = new Car(product.getId(), quantity, carAddForm.getSelected());
        } else {
            //有商品,数量+1
            car = gson.fromJson(value, Car.class);
            car.setQuantity(car.getQuantity() + quantity);

        }
        //list需要遍历,所以不用list,用hash进行存储
        //redisTemplate.opsForValue().set(String.format(CAR_REDIS_KEY_TEMPLATE,uid),gson.toJson(new Car(product.getId(),quantity,carAddForm.getSelected())));
        opsForHash.put(String.format(CAR_REDIS_KEY_TEMPLATE, uid), String.valueOf(product.getId()), gson.toJson(car));
        return null;
    }

    /**
     * 列表
     */
    public ResponseVo<CarVo> list(Integer uid) {
        HashOperations<String, String, String> opsForHash = redisTemplate.opsForHash();
        String redisKey = String.format(CAR_REDIS_KEY_TEMPLATE, uid);
        //获取所有数据进行遍历
        Map<String, String> entries = opsForHash.entries(redisKey);

        boolean selectAll = true;
        Integer carTotalQuantity = 0;
        BigDecimal carTotalPrice = BigDecimal.ZERO;
        CarVo carVo = new CarVo();
        List<CarProductVo> carProductVoList = new ArrayList<>();
        for (Map.Entry<String, String> entry : entries.entrySet()) {
            Integer productId = Integer.valueOf(entry.getKey());
            Car car = gson.fromJson(entry.getValue(), Car.class);
            //TODO: 需要优化,使用in
            ProductDetailVo productDetailVo = productMapper.selectByPrimaryKey(productId);
            if (productDetailVo != null) {
                CarProductVo carProductVo = new CarProductVo();
                carProductVo.setProductId(productId);
                carProductVo.setQuantity(car.getQuantity());
                carProductVo.setProductName(productDetailVo.getName());
                carProductVo.setProductSubtitle(productDetailVo.getSubtitle());
                carProductVo.setProductMainImage(productDetailVo.getMainImage());
                carProductVo.setProductPrice(productDetailVo.getPrice().multiply(BigDecimal.valueOf(car.getQuantity())));
                carProductVo.setProductStock(productDetailVo.getStock());
                carProductVo.setProductSelected(car.getProductSelected());

                carProductVoList.add(carProductVo);
                if (!car.getProductSelected()) {
                    selectAll = false;
                }
                //计算总价只计算选中的
                if (car.getProductSelected()) {
                    carTotalPrice = carTotalPrice.add(carProductVo.getProductTotalPrice());
                }
            }
            carTotalQuantity += car.getQuantity();
        }
        //有一个没有选中就不叫全选
        carVo.setSelectedAll(selectAll);
        carVo.setCarTotalQuantity(carTotalQuantity);
        //总价 = 购物车选中商品价格 * 数量
        carVo.setCarTotalPrice(carTotalPrice);
        carVo.setCarProductVoList(carProductVoList);
        return ResponseVo.successByCommonData(carVo);
    }

    /**
     * 更新
     */
    public ResponseVo<CarVo> update(Integer uid, Integer productId, Integer quantity, Boolean selected) {
        HashOperations<String, String, String> opsForHash = redisTemplate.opsForHash();
        String redisKey = String.format(CAR_REDIS_KEY_TEMPLATE, uid);

        String value = opsForHash.get(redisKey, String.valueOf(productId));
        if (StringUtils.isEmpty(value)) {
            //没有,报错
            return ResponseVo.error(ResponseEnum.PRODUCT_NON);
        } else {
            //有商品,数量+1
            Car car = gson.fromJson(value, Car.class);
            if (quantity != null && quantity >= 0) {
                car.setQuantity(car.getQuantity() + quantity);
            }
            if (selected != null) {
                car.setProductSelected(selected);
            }
            opsForHash.put(redisKey, String.valueOf(productId), gson.toJson(car));
        }
        return list(uid);
    }

    /**
     * 删除
     */
    public ResponseVo<CarVo> delete(Integer uid, Integer productId) {
        HashOperations<String, String, String> opsForHash = redisTemplate.opsForHash();
        String redisKey = String.format(CAR_REDIS_KEY_TEMPLATE, uid);

        String value = opsForHash.get(redisKey, String.valueOf(productId));
        if (StringUtils.isEmpty(value)) {
            //没有,报错
            return ResponseVo.error(ResponseEnum.PRODUCT_NON);
        }
        opsForHash.delete(redisKey, String.valueOf(productId));
        return list(uid);
    }

    /**
     * 全选
     */
    public ResponseVo<CarVo> selectAll(Integer uid){
        HashOperations<String, String, String> opsForHash = redisTemplate.opsForHash();
        String redisKey = String.format(CAR_REDIS_KEY_TEMPLATE, uid);

        for (Car car : listForCar(uid)) {
            car.setProductSelected(true);
            opsForHash.put(redisKey,String.valueOf(car.getProductId()),gson.toJson(car));
        }
        return list(uid);
    }

    /**
     * 全不选中
     */
    public ResponseVo<CarVo> unSelectAll(Integer uid){
        HashOperations<String, String, String> opsForHash = redisTemplate.opsForHash();
        String redisKey = String.format(CAR_REDIS_KEY_TEMPLATE, uid);

        for (Car car : listForCar(uid)) {
            car.setProductSelected(false);
            opsForHash.put(redisKey,String.valueOf(car.getProductId()),gson.toJson(car));
        }
        return list(uid);
    }

    /**
     * 总数
     */
    public ResponseVo<Integer> sum(Integer uid){
        Integer sum = listForCar(uid).stream().map(Car::getQuantity).reduce(0, Integer::sum);
        return ResponseVo.successByCommonData(sum);
    }

    /**
     * 遍历购物车
     */
    private List<Car> listForCar(Integer uid){
        HashOperations<String, String, String> opsForHash = redisTemplate.opsForHash();
        String redisKey = String.format(CAR_REDIS_KEY_TEMPLATE, uid);
        //获取所有数据进行遍历
        Map<String, String> entries = opsForHash.entries(redisKey);

        ArrayList<Car> carList = new ArrayList<>();
        for (Map.Entry<String, String> entry : entries.entrySet()) {
            carList.add(gson.fromJson(entry.getValue(),Car.class));
        }
        return carList;
    }
}

收获地址模块

收获地址Bean对象实体类

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Shipping {
    private Integer id;
    private Integer userId;
    private String receiverName;
    private String receiverPhone;
    private String receiverMobile;
    private String receiverProvince;
    private String receiverCity;
    private String receiverDistrict;
    private String receiverAddress;
    private String receiverZip;  //邮编
    private Date createTime;
    private Date updateTime;
}

 表单参数类

@Data
public class ShippingForm {
    @NotBlank
    private String receiverName;
    @NotBlank
    private String receiverPhone;
    @NotBlank
    private String receiverMobile;
    @NotBlank
    private String receiverProvince;
    @NotBlank
    private String receiverCity;
    @NotBlank
    private String receiverDistrict;
    @NotBlank
    private String receiverAddress;
    @NotBlank
    private String receiverZip;
}

Controller

@RestController
public class ShippingController {

    @Autowired
    private IShippingService shippingService;

    @PostMapping("/shippings")
    public ResponseVo add(@Valid @RequestBody ShippingForm form, HttpSession session) {
        User user = (User) session.getAttribute(UserConst.CURRENT_USER);
        return shippingService.add(user.getId(), form);
    }

    @DeleteMapping("/shippings/{shippingId}")
    public ResponseVo delete(@PathVariable Integer shippingId, HttpSession session) {
        User user = (User) session.getAttribute(UserConst.CURRENT_USER);
        return shippingService.delete(user.getId(), shippingId);
    }

    @DeleteMapping("/shippings/{shippingId}")
    public ResponseVo update(@PathVariable Integer shippingId, @Valid @RequestBody ShippingForm form, HttpSession session) {
        User user = (User) session.getAttribute(UserConst.CURRENT_USER);
        return shippingService.update(user.getId(), shippingId, form);
    }

    @GetMapping("/shippings/{shippingId}")
    public ResponseVo list(@RequestParam(required = false, defaultValue = "1") Integer pageNum,
                           @RequestParam(required = false, defaultValue = "10") Integer pageSize,
                           HttpSession session) {
        User user = (User) session.getAttribute(UserConst.CURRENT_USER);
        return shippingService.list(user.getId(), pageNum, pageSize);
    }
}

Service

@Service
public class IShippingService {

    @Autowired
    private IShippingMapper shippingMapper;

    /**
     * 添加地址
     */
    public ResponseVo<Map<String, Integer>> add(Integer uid, ShippingForm form) {
        Shipping shipping = new Shipping();
        BeanUtils.copyProperties(form, shipping);
        int row = shippingMapper.insertSelective(shipping);
        if (row == 0) {
            return ResponseVo.error(ResponseEnum.MASHINE_ERROR);
        }
        HashMap<String, Integer> map = new HashMap<>();

        //注意:这里需要插入后返回的自增id,需要在mapper文件中配置 useGeneratedKeys="true" keyProperty="id" 否则需要在查询一次
        map.put("shippingId", shipping.getId());
        return ResponseVo.successByCommonData(map);
    }

    /**
     * 删除地址
     */
    public ResponseVo delete(Integer uid, Integer shippingId) {
        int row = shippingMapper.deleteByIdAndUid(uid, shippingId);
        if (row == 0) {
            return ResponseVo.error(ResponseEnum.PARAMS_ERROR);
        }
        return ResponseVo.success();
    }

    /**
     * 更新地址
     */
    public ResponseVo update(Integer uid, Integer shippingId, ShippingForm form) {
        Shipping shipping = new Shipping();
        BeanUtils.copyProperties(form, shipping);
        shipping.setUserId(uid);
        shipping.setId(shippingId);
        int row = shippingMapper.updateByPrimaryKeySelective(shipping);
        if (row == 0) {
            return ResponseVo.error(ResponseEnum.PARAMS_ERROR);
        }
        return ResponseVo.success();
    }

    /**
     * 地址列表
     */
    public ResponseVo<PageInfo> list(Integer uid, Integer pageNum, Integer pageSize) {
        PageHelper.startPage(pageNum, pageSize);
        List<Shipping> shippings = shippingMapper.selectByUid(uid);
        PageInfo pageInfo = new PageInfo(shippings);
        return ResponseVo.successByCommonData(pageInfo);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值