Commit 7345d80b53350da3d5b52afd0d31b0e974ea9e07

Authored by jason
1 parent 19ceea2c

新增商品收藏功能

Showing 13 changed files with 378 additions and 5 deletions
sql/mysql/optional/mall.sql
@@ -290,6 +290,25 @@ CREATE TABLE `product_spu` ( @@ -290,6 +290,25 @@ CREATE TABLE `product_spu` (
290 ) ENGINE=InnoDB COMMENT='商品spu'; 290 ) ENGINE=InnoDB COMMENT='商品spu';
291 291
292 -- ---------------------------- 292 -- ----------------------------
  293 +-- Table structure for product_favorite
  294 +-- ----------------------------
  295 +DROP TABLE IF EXISTS `product_favorite`;
  296 +CREATE TABLE `product_favorite` (
  297 + `id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号,自增',
  298 + `spu_id` bigint NOT NULL COMMENT '商品 SPU 编号',
  299 + `user_id` bigint NOT NULL COMMENT '用户id',
  300 + `type` int(10) NOT NULL DEFAULT 1 COMMENT '类型1:收藏 2:点赞',
  301 + `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',
  302 + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  303 + `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',
  304 + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  305 + `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
  306 + `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
  307 + PRIMARY KEY (`id`) USING BTREE
  308 +) ENGINE=InnoDB COMMENT='喜欢的商品表';
  309 +
  310 +
  311 +-- ----------------------------
293 -- Records of product_spu 312 -- Records of product_spu
294 -- ---------------------------- 313 -- ----------------------------
295 BEGIN; 314 BEGIN;
yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/enums/ErrorCodeConstants.java
@@ -48,4 +48,8 @@ public interface ErrorCodeConstants { @@ -48,4 +48,8 @@ public interface ErrorCodeConstants {
48 ErrorCode COMMENT_ERROR_OPT = new ErrorCode(1008007002, "商品评价非法操作"); 48 ErrorCode COMMENT_ERROR_OPT = new ErrorCode(1008007002, "商品评价非法操作");
49 ErrorCode COMMENT_ADDITIONAL_EXISTS = new ErrorCode(1008007003, "商品追加评价已存在"); 49 ErrorCode COMMENT_ADDITIONAL_EXISTS = new ErrorCode(1008007003, "商品追加评价已存在");
50 50
  51 + // ========== 喜爱商品 1008008000 ==========
  52 + ErrorCode COLLECTION_EXISTS = new ErrorCode(1008008000, "该商品已经被收藏");
  53 + ErrorCode COLLECTION_NOT_EXISTS = new ErrorCode(1008008001, "商品收藏不存在");
  54 +
51 } 55 }
yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/enums/favorite/ProductFavoriteTypeEnum.java 0 → 100644
  1 +package cn.iocoder.yudao.module.product.enums.favorite;
  2 +
  3 +import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
  4 +import lombok.AllArgsConstructor;
  5 +import lombok.Getter;
  6 +
  7 +import java.util.Arrays;
  8 +
  9 +/**
  10 + * 喜爱商品类型 枚举
  11 + *
  12 + * @author jason
  13 + */
  14 +@Getter
  15 +@AllArgsConstructor
  16 +public enum ProductFavoriteTypeEnum implements IntArrayValuable {
  17 + COLLECT(1,"收藏"),
  18 + THUMBS_UP(2, "点赞");
  19 +
  20 + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(ProductFavoriteTypeEnum::getType).toArray();
  21 + /**
  22 + * 类型
  23 + */
  24 + private final Integer type;
  25 + /**
  26 + * 描述
  27 + */
  28 + private final String desc;
  29 +
  30 + @Override
  31 + public int[] array() {
  32 + return ARRAYS;
  33 + }
  34 +}
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/favorite/AppFavoriteController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.product.controller.app.favorite;
  2 +
  3 +import cn.hutool.core.lang.Assert;
  4 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  5 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  6 +import cn.iocoder.yudao.module.product.controller.app.favorite.vo.AppFavoritePageReqVO;
  7 +import cn.iocoder.yudao.module.product.controller.app.favorite.vo.AppFavoriteReqVO;
  8 +import cn.iocoder.yudao.module.product.service.favorite.ProductFavoriteService;
  9 +import cn.iocoder.yudao.module.product.controller.app.favorite.vo.AppFavoriteRespVO;
  10 +import io.swagger.v3.oas.annotations.Operation;
  11 +import io.swagger.v3.oas.annotations.tags.Tag;
  12 +import org.springframework.web.bind.annotation.*;
  13 +
  14 +import javax.annotation.Resource;
  15 +import javax.validation.Valid;
  16 +import java.util.Objects;
  17 +
  18 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  19 +import static cn.iocoder.yudao.module.product.enums.favorite.ProductFavoriteTypeEnum.COLLECT;
  20 +
  21 +/**
  22 + * @author jason
  23 + */
  24 +@Tag(name = "用户 APP - 喜爱商品")
  25 +@RestController
  26 +@RequestMapping("/product/favorite")
  27 +public class AppFavoriteController {
  28 +
  29 + @Resource
  30 + private ProductFavoriteService productFavoriteService;
  31 +
  32 + @PostMapping(value = "/collect")
  33 + @Operation(summary = "商品收藏")
  34 + public CommonResult<Boolean> collect(@RequestBody @Valid AppFavoriteReqVO reqVO) {
  35 + Assert.isTrue(Objects.equals(COLLECT.getType(), reqVO.getType()), "参数type 不匹配");
  36 + return success(productFavoriteService.collect(reqVO));
  37 + }
  38 +
  39 + @PostMapping(value = "/cancelCollect")
  40 + @Operation(summary = "取消商品收藏(通过商品详情)")
  41 + public CommonResult<Boolean> cancelCollect(@RequestBody @Valid AppFavoriteReqVO reqVO) {
  42 + Assert.isTrue(Objects.equals(COLLECT.getType(), reqVO.getType()), "参数type 不匹配");
  43 + return success(productFavoriteService.cancelCollect(reqVO));
  44 + }
  45 +
  46 + @GetMapping(value = "/collectList")
  47 + @Operation(summary = "商品收藏列表")
  48 + public CommonResult<PageResult<AppFavoriteRespVO>> pageCollectList(AppFavoritePageReqVO reqVO) {
  49 + return success(productFavoriteService.pageCollectList(reqVO));
  50 + }
  51 +}
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/favorite/vo/AppFavoritePageReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.product.controller.app.favorite.vo;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  4 +import cn.iocoder.yudao.framework.common.validation.InEnum;
  5 +import cn.iocoder.yudao.module.product.enums.favorite.ProductFavoriteTypeEnum;
  6 +import io.swagger.v3.oas.annotations.media.Schema;
  7 +import lombok.Data;
  8 +
  9 +import javax.validation.constraints.NotNull;
  10 +
  11 +import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED;
  12 +
  13 +/**
  14 + * @author jason
  15 + */
  16 +@Schema(description = "用户APP - 喜爱商品分页查询 Request VO")
  17 +@Data
  18 +public class AppFavoritePageReqVO extends PageParam {
  19 +
  20 + @Schema(description = "类型 1:收藏 2:点赞", requiredMode = REQUIRED, example = "1")
  21 + @NotNull(message = "类型不能为空")
  22 + @InEnum(ProductFavoriteTypeEnum.class)
  23 + private Integer type;
  24 +}
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/favorite/vo/AppFavoriteReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.product.controller.app.favorite.vo;
  2 +
  3 +import cn.iocoder.yudao.framework.common.validation.InEnum;
  4 +import cn.iocoder.yudao.module.product.enums.favorite.ProductFavoriteTypeEnum;
  5 +import io.swagger.v3.oas.annotations.media.Schema;
  6 +import lombok.Data;
  7 +
  8 +import javax.validation.constraints.NotNull;
  9 +
  10 +import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED;
  11 +
  12 +
  13 +
  14 +/**
  15 + * @author jason
  16 + */
  17 +@Schema(description = "用户APP - 喜爱商品创建 Request VO")
  18 +@Data
  19 +public class AppFavoriteReqVO {
  20 +
  21 + @Schema(description = "商品SPU编号", requiredMode = REQUIRED, example = "29502")
  22 + @NotNull(message = "商品SPU编号不能为空")
  23 + private Long spuId;
  24 +
  25 + @Schema(description = "类型 1:收藏 2:点赞", requiredMode = REQUIRED, example = "1")
  26 + @NotNull(message = "类型不能为空")
  27 + @InEnum(ProductFavoriteTypeEnum.class)
  28 + private Integer type;
  29 +}
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/favorite/vo/AppFavoriteRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.product.controller.app.favorite.vo;
  2 +
  3 +import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO;
  4 +import io.swagger.v3.oas.annotations.media.Schema;
  5 +import lombok.Data;
  6 +
  7 +/**
  8 + * @author jason
  9 + */
  10 +@Schema(description = "用户APP - 喜爱商品 Response VO")
  11 +@Data
  12 +public class AppFavoriteRespVO {
  13 +
  14 + @Schema(description = "编号", example = "1")
  15 + private Long id;
  16 +
  17 + @Schema(description = "商品SPU编号", example = "29502")
  18 + private Long spuId;
  19 +
  20 + @Schema(description = "商品SPU名称", example = "赵六")
  21 + private String spuName;
  22 +
  23 + @Schema(description = "商品封面图", example = "https://domain/pic.png")
  24 + private String picUrl;
  25 +
  26 + @Schema(description = "商品单价", example = "100")
  27 + private Integer price;
  28 +
  29 + @Schema(description = "类型 1:收藏 2:点赞", example = "1")
  30 + private Integer type;
  31 +}
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/favorite/ProductFavoriteConvert.java 0 → 100644
  1 +package cn.iocoder.yudao.module.product.convert.favorite;
  2 +
  3 +import cn.iocoder.yudao.module.product.controller.app.favorite.vo.AppFavoriteReqVO;
  4 +import cn.iocoder.yudao.module.product.dal.dataobject.favorite.ProductFavoriteDO;
  5 +import org.mapstruct.Mapper;
  6 +import org.mapstruct.factory.Mappers;
  7 +
  8 +/**
  9 + * 喜爱商品 Convert
  10 + *
  11 + * @author jason
  12 + */
  13 +@Mapper
  14 +public interface ProductFavoriteConvert {
  15 +
  16 + ProductFavoriteConvert INSTANCE = Mappers.getMapper(ProductFavoriteConvert.class);
  17 +
  18 + ProductFavoriteDO convert(Long userId, AppFavoriteReqVO reqVO);
  19 +}
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/favorite/ProductFavoriteDO.java
1 package cn.iocoder.yudao.module.product.dal.dataobject.favorite; 1 package cn.iocoder.yudao.module.product.dal.dataobject.favorite;
2 2
3 -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; 3 +import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
4 import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO; 4 import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO;
5 import com.baomidou.mybatisplus.annotation.KeySequence; 5 import com.baomidou.mybatisplus.annotation.KeySequence;
6 import com.baomidou.mybatisplus.annotation.TableId; 6 import com.baomidou.mybatisplus.annotation.TableId;
@@ -8,7 +8,7 @@ import com.baomidou.mybatisplus.annotation.TableName; @@ -8,7 +8,7 @@ import com.baomidou.mybatisplus.annotation.TableName;
8 import lombok.*; 8 import lombok.*;
9 9
10 /** 10 /**
11 - * 商品收藏 DO 11 + * 喜爱商品 DO
12 * 12 *
13 * @author 芋道源码 13 * @author 芋道源码
14 */ 14 */
@@ -20,7 +20,7 @@ import lombok.*; @@ -20,7 +20,7 @@ import lombok.*;
20 @Builder 20 @Builder
21 @NoArgsConstructor 21 @NoArgsConstructor
22 @AllArgsConstructor 22 @AllArgsConstructor
23 -public class ProductFavoriteDO extends BaseDO { 23 +public class ProductFavoriteDO extends TenantBaseDO {
24 24
25 /** 25 /**
26 * 编号,主键自增 26 * 编号,主键自增
@@ -39,7 +39,9 @@ public class ProductFavoriteDO extends BaseDO { @@ -39,7 +39,9 @@ public class ProductFavoriteDO extends BaseDO {
39 * 关联 {@link ProductSpuDO#getId()} 39 * 关联 {@link ProductSpuDO#getId()}
40 */ 40 */
41 private Long spuId; 41 private Long spuId;
42 -  
43 - // TODO 芋艿:type 1 收藏;2 点赞 42 + /**
  43 + * 类型 1 收藏;2 点赞
  44 + */
  45 + private Integer type;
44 46
45 } 47 }
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/favorite/ProductFavoriteMapper.java 0 → 100644
  1 +package cn.iocoder.yudao.module.product.dal.mysql.favorite;
  2 +
  3 +import cn.hutool.core.lang.Assert;
  4 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  5 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  6 +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  7 +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  8 +import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;
  9 +import cn.iocoder.yudao.module.product.dal.dataobject.favorite.ProductFavoriteDO;
  10 +import cn.iocoder.yudao.module.product.controller.app.favorite.vo.AppFavoriteRespVO;
  11 +import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
  12 +import org.apache.ibatis.annotations.Mapper;
  13 +import org.apache.ibatis.annotations.Param;
  14 +
  15 +/**
  16 + * 喜爱商品 Mapper
  17 + *
  18 + * @author jason
  19 + */
  20 +@Mapper
  21 +public interface ProductFavoriteMapper extends BaseMapperX<ProductFavoriteDO> {
  22 +
  23 + default ProductFavoriteDO selectByUserAndSpuAndType(Long userId, Long spuId, Integer type){
  24 + Assert.notNull(userId, "the userId argument must not be null");
  25 + Assert.notNull(spuId, "the spuId argument must not be null");
  26 + Assert.notNull(type, "the type argument must not be null");
  27 + return selectOne(new LambdaQueryWrapperX<ProductFavoriteDO>()
  28 + .eq(ProductFavoriteDO::getUserId, userId)
  29 + .eq(ProductFavoriteDO::getSpuId, spuId)
  30 + .eq(ProductFavoriteDO::getType, type));
  31 + }
  32 +
  33 + default PageResult<AppFavoriteRespVO> selectPageByUserAndType(Long userId, Integer type, PageParam pageParam){
  34 + Page<AppFavoriteRespVO> page = MyBatisUtils.buildPage(pageParam);
  35 + page = selectFavoriteProductList(page, userId, type);
  36 + return new PageResult<>(page.getRecords(), page.getTotal());
  37 + }
  38 +
  39 + Page<AppFavoriteRespVO> selectFavoriteProductList(Page<AppFavoriteRespVO> page,
  40 + @Param("userId") Long userId,
  41 + @Param("type") Integer type);
  42 +}
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/favorite/ProductFavoriteService.java 0 → 100644
  1 +package cn.iocoder.yudao.module.product.service.favorite;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.module.product.controller.app.favorite.vo.AppFavoritePageReqVO;
  5 +import cn.iocoder.yudao.module.product.controller.app.favorite.vo.AppFavoriteReqVO;
  6 +import cn.iocoder.yudao.module.product.controller.app.favorite.vo.AppFavoriteRespVO;
  7 +
  8 +import javax.validation.Valid;
  9 +
  10 +/**
  11 + * 喜爱商品 Service 接口
  12 + *
  13 + * @author jason
  14 + */
  15 +public interface ProductFavoriteService {
  16 +
  17 + /**
  18 + * 商品收藏
  19 + * @param reqVO 请求vo
  20 + */
  21 + Boolean collect(@Valid AppFavoriteReqVO reqVO);
  22 +
  23 + /**
  24 + * 取消商品收藏 (通过商品详情页面)
  25 + * @param reqVO 请求vo
  26 + */
  27 + Boolean cancelCollect(@Valid AppFavoriteReqVO reqVO);
  28 +
  29 + /**
  30 + * 分页查询用户收藏列表
  31 + * @param reqVO 请求 vo
  32 + */
  33 + PageResult<AppFavoriteRespVO> pageCollectList(@Valid AppFavoritePageReqVO reqVO);
  34 +}
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/favorite/ProductFavoriteServiceImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.product.service.favorite;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.module.product.controller.app.favorite.vo.AppFavoritePageReqVO;
  5 +import cn.iocoder.yudao.module.product.controller.app.favorite.vo.AppFavoriteReqVO;
  6 +import cn.iocoder.yudao.module.product.convert.favorite.ProductFavoriteConvert;
  7 +import cn.iocoder.yudao.module.product.dal.dataobject.favorite.ProductFavoriteDO;
  8 +import cn.iocoder.yudao.module.product.dal.mysql.favorite.ProductFavoriteMapper;
  9 +import cn.iocoder.yudao.module.product.controller.app.favorite.vo.AppFavoriteRespVO;
  10 +import org.springframework.stereotype.Service;
  11 +import org.springframework.validation.annotation.Validated;
  12 +
  13 +import javax.annotation.Resource;
  14 +import javax.validation.Valid;
  15 +import java.util.Objects;
  16 +
  17 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  18 +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
  19 +import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.COLLECTION_EXISTS;
  20 +import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.COLLECTION_NOT_EXISTS;
  21 +
  22 +/**
  23 + * 喜爱商品 Service 实现类
  24 + *
  25 + * @author jason
  26 + */
  27 +@Service
  28 +@Validated
  29 +public class ProductFavoriteServiceImpl implements ProductFavoriteService {
  30 +
  31 + @Resource
  32 + private ProductFavoriteMapper mapper;
  33 +
  34 + @Override
  35 + public Boolean collect(@Valid AppFavoriteReqVO reqVO) {
  36 + Long userId = getLoginUserId();
  37 + ProductFavoriteDO favoriteDO = mapper.selectByUserAndSpuAndType(userId, reqVO.getSpuId(), reqVO.getType());
  38 + if (Objects.nonNull(favoriteDO)) {
  39 + throw exception(COLLECTION_EXISTS);
  40 + }
  41 + ProductFavoriteDO entity = ProductFavoriteConvert.INSTANCE.convert(userId, reqVO);
  42 + int count = mapper.insert(entity);
  43 + return count == 1;
  44 + }
  45 +
  46 + @Override
  47 + public Boolean cancelCollect(@Valid AppFavoriteReqVO reqVO) {
  48 + Long loginUserId = getLoginUserId();
  49 + ProductFavoriteDO favoriteDO = mapper.selectByUserAndSpuAndType(loginUserId, reqVO.getSpuId(), reqVO.getType());
  50 + if (Objects.isNull(favoriteDO)) {
  51 + throw exception(COLLECTION_NOT_EXISTS);
  52 + }
  53 + int count = mapper.deleteById(favoriteDO.getId());
  54 + return count == 1;
  55 + }
  56 +
  57 + @Override
  58 + public PageResult<AppFavoriteRespVO> pageCollectList(@Valid AppFavoritePageReqVO reqVO) {
  59 + Long userId = getLoginUserId();
  60 + return mapper.selectPageByUserAndType(userId, reqVO.getType(), reqVO);
  61 + }
  62 +}
yudao-module-mall/yudao-module-product-biz/src/main/resources/mapper/favorite/ProductFavoriteMapper.xml 0 → 100644
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  3 +<mapper namespace="cn.iocoder.yudao.module.product.dal.mysql.favorite.ProductFavoriteMapper">
  4 +
  5 + <!--
  6 + 一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。
  7 + 无法满足的场景,例如说多表关联查询,才使用 XML 编写 SQL。
  8 + 代码生成器暂时只生成 Mapper XML 文件本身,更多推荐 MybatisX 快速开发插件来生成查询。
  9 + 文档可见:https://www.iocoder.cn/MyBatis/x-plugins/
  10 + -->
  11 +
  12 + <select id="selectFavoriteProductList"
  13 + resultType="cn.iocoder.yudao.module.product.controller.app.favorite.vo.AppFavoriteRespVO">
  14 + SELECT f.id, f.type, f.spu_id as spuId , p.name as spuName, p.pic_url as picUrl , p.price
  15 + FROM product_favorite f
  16 + INNER JOIN product_spu p ON f.spu_id = p.id and f.deleted = 0
  17 + where f.type = #{type} and f.user_id = #{userId}
  18 + ORDER BY f.id DESC
  19 +
  20 + </select>
  21 +
  22 +</mapper>
0 \ No newline at end of file 23 \ No newline at end of file