Commit f374e778bb5d7c70d6ffc08ee2f02ad91f183093

Authored by owen
1 parent 86a9c4bb

商品:增加商品浏览记录

Showing 16 changed files with 550 additions and 3 deletions
sql/mysql/optinal/product_browse_history.sql 0 → 100644
  1 +CREATE TABLE product_browse_history
  2 +(
  3 + id bigint AUTO_INCREMENT COMMENT '记录编号'
  4 + PRIMARY KEY,
  5 + user_id bigint NOT NULL COMMENT '用户编号',
  6 + spu_id bigint NOT NULL COMMENT '商品 SPU 编号',
  7 + user_deleted bit DEFAULT b'0' NOT NULL COMMENT '用户是否删除',
  8 + creator varchar(64) DEFAULT '' NULL COMMENT '创建者',
  9 + create_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL COMMENT '创建时间',
  10 + updater varchar(64) DEFAULT '' NULL COMMENT '更新者',
  11 + update_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  12 + deleted bit DEFAULT b'0' NOT NULL COMMENT '是否删除',
  13 + tenant_id bigint DEFAULT 0 NOT NULL COMMENT '租户编号'
  14 +)
  15 + COMMENT '商品浏览记录表';
  16 +
  17 +CREATE INDEX idx_spuId
  18 + ON product_browse_history (spu_id);
  19 +
  20 +CREATE INDEX idx_userId
  21 + ON product_browse_history (user_id);
  22 +
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/history/ProductBrowseHistoryController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.product.controller.admin.history;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  4 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  5 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  6 +import cn.iocoder.yudao.module.product.controller.admin.history.vo.ProductBrowseHistoryPageReqVO;
  7 +import cn.iocoder.yudao.module.product.controller.admin.history.vo.ProductBrowseHistoryRespVO;
  8 +import cn.iocoder.yudao.module.product.dal.dataobject.history.ProductBrowseHistoryDO;
  9 +import cn.iocoder.yudao.module.product.service.history.ProductBrowseHistoryService;
  10 +import io.swagger.v3.oas.annotations.Operation;
  11 +import io.swagger.v3.oas.annotations.tags.Tag;
  12 +import jakarta.annotation.Resource;
  13 +import jakarta.validation.Valid;
  14 +import org.springframework.security.access.prepost.PreAuthorize;
  15 +import org.springframework.validation.annotation.Validated;
  16 +import org.springframework.web.bind.annotation.GetMapping;
  17 +import org.springframework.web.bind.annotation.RequestMapping;
  18 +import org.springframework.web.bind.annotation.RestController;
  19 +
  20 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  21 +
  22 +@Tag(name = "管理后台 - 商品浏览记录")
  23 +@RestController
  24 +@RequestMapping("/product/browse-history")
  25 +@Validated
  26 +public class ProductBrowseHistoryController {
  27 +
  28 + @Resource
  29 + private ProductBrowseHistoryService browseHistoryService;
  30 +
  31 + @GetMapping("/page")
  32 + @Operation(summary = "获得商品浏览记录分页")
  33 + @PreAuthorize("@ss.hasPermission('product:browse-history:query')")
  34 + public CommonResult<PageResult<ProductBrowseHistoryRespVO>> getBrowseHistoryPage(@Valid ProductBrowseHistoryPageReqVO pageReqVO) {
  35 + PageResult<ProductBrowseHistoryDO> pageResult = browseHistoryService.getBrowseHistoryPage(pageReqVO);
  36 + return success(BeanUtils.toBean(pageResult, ProductBrowseHistoryRespVO.class));
  37 + }
  38 +
  39 +}
0 \ No newline at end of file 40 \ No newline at end of file
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/history/vo/ProductBrowseHistoryPageReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.product.controller.admin.history.vo;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.SortablePageParam;
  4 +import io.swagger.v3.oas.annotations.media.Schema;
  5 +import lombok.Data;
  6 +import lombok.EqualsAndHashCode;
  7 +import lombok.ToString;
  8 +import org.springframework.format.annotation.DateTimeFormat;
  9 +
  10 +import java.time.LocalDateTime;
  11 +
  12 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
  13 +
  14 +@Schema(description = "管理后台 - 商品浏览记录分页 Request VO")
  15 +@Data
  16 +@EqualsAndHashCode(callSuper = true)
  17 +@ToString(callSuper = true)
  18 +public class ProductBrowseHistoryPageReqVO extends SortablePageParam {
  19 +
  20 + @Schema(description = "用户编号", example = "4314")
  21 + private Long userId;
  22 +
  23 + @Schema(description = "用户是否删除", example = "false")
  24 + private Boolean userDeleted;
  25 +
  26 + @Schema(description = "商品 SPU 编号", example = "42")
  27 + private Long spuId;
  28 +
  29 + @Schema(description = "创建时间")
  30 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  31 + private LocalDateTime[] createTime;
  32 +
  33 +}
0 \ No newline at end of file 34 \ No newline at end of file
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/history/vo/ProductBrowseHistoryRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.product.controller.admin.history.vo;
  2 +
  3 +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
  4 +import com.alibaba.excel.annotation.ExcelProperty;
  5 +import io.swagger.v3.oas.annotations.media.Schema;
  6 +import lombok.Data;
  7 +
  8 +import java.time.LocalDateTime;
  9 +
  10 +@Schema(description = "管理后台 - 商品浏览记录 Response VO")
  11 +@Data
  12 +@ExcelIgnoreUnannotated
  13 +public class ProductBrowseHistoryRespVO {
  14 +
  15 + @Schema(description = "记录编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "26055")
  16 + @ExcelProperty("记录编号")
  17 + private Long id;
  18 +
  19 + @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "4314")
  20 + @ExcelProperty("用户编号")
  21 + private Long userId;
  22 +
  23 + @Schema(description = "用户是否删除", example = "false")
  24 + private Boolean userDeleted;
  25 +
  26 + @Schema(description = "商品 SPU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "42")
  27 + @ExcelProperty("商品 SPU 编号")
  28 + private Long spuId;
  29 +
  30 + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
  31 + @ExcelProperty("创建时间")
  32 + private LocalDateTime createTime;
  33 +
  34 +}
0 \ No newline at end of file 35 \ No newline at end of file
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/history/AppProductBrowseHistoryController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.product.controller.app.history;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  5 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  6 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  7 +import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated;
  8 +import cn.iocoder.yudao.module.product.controller.admin.history.vo.ProductBrowseHistoryPageReqVO;
  9 +import cn.iocoder.yudao.module.product.controller.app.history.vo.AppProductBrowseHistoryDeleteReqVO;
  10 +import cn.iocoder.yudao.module.product.controller.app.history.vo.AppProductBrowseHistoryPageReqVO;
  11 +import cn.iocoder.yudao.module.product.controller.app.history.vo.AppProductBrowseHistoryRespVO;
  12 +import cn.iocoder.yudao.module.product.dal.dataobject.history.ProductBrowseHistoryDO;
  13 +import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO;
  14 +import cn.iocoder.yudao.module.product.service.history.ProductBrowseHistoryService;
  15 +import cn.iocoder.yudao.module.product.service.spu.ProductSpuService;
  16 +import io.swagger.v3.oas.annotations.Operation;
  17 +import io.swagger.v3.oas.annotations.tags.Tag;
  18 +import jakarta.annotation.Resource;
  19 +import jakarta.validation.Valid;
  20 +import org.springframework.web.bind.annotation.*;
  21 +
  22 +import java.util.Map;
  23 +import java.util.Optional;
  24 +import java.util.Set;
  25 +
  26 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  27 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
  28 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
  29 +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
  30 +
  31 +@Tag(name = "用户 APP - 商品浏览记录")
  32 +@RestController
  33 +@RequestMapping("/product/browse-history")
  34 +public class AppProductBrowseHistoryController {
  35 +
  36 + @Resource
  37 + private ProductBrowseHistoryService productBrowseHistoryService;
  38 + @Resource
  39 + private ProductSpuService productSpuService;
  40 +
  41 + @DeleteMapping(value = "/delete")
  42 + @Operation(summary = "删除商品浏览记录")
  43 + @PreAuthenticated
  44 + public CommonResult<Boolean> deleteBrowseHistory(@RequestBody @Valid AppProductBrowseHistoryDeleteReqVO reqVO) {
  45 + productBrowseHistoryService.hideUserBrowseHistory(getLoginUserId(), reqVO.getSpuIds());
  46 + return success(Boolean.TRUE);
  47 + }
  48 +
  49 + @DeleteMapping(value = "/clean")
  50 + @Operation(summary = "清空商品浏览记录")
  51 + @PreAuthenticated
  52 + public CommonResult<Boolean> cleanBrowseHistory() {
  53 + productBrowseHistoryService.hideUserBrowseHistory(getLoginUserId(), null);
  54 + return success(Boolean.TRUE);
  55 + }
  56 +
  57 + @GetMapping(value = "/get-count")
  58 + @Operation(summary = "获得商品浏览记录数量")
  59 + @PreAuthenticated
  60 + public CommonResult<Long> getBrowseHistoryCount() {
  61 + return success(productBrowseHistoryService.getBrowseHistoryCount(getLoginUserId(), false));
  62 + }
  63 +
  64 + @GetMapping(value = "/page")
  65 + @Operation(summary = "获得商品浏览记录分页")
  66 + @PreAuthenticated
  67 + public CommonResult<PageResult<AppProductBrowseHistoryRespVO>> getBrowseHistoryPage(AppProductBrowseHistoryPageReqVO reqVO) {
  68 + ProductBrowseHistoryPageReqVO pageReqVO = BeanUtils.toBean(reqVO, ProductBrowseHistoryPageReqVO.class);
  69 + pageReqVO.setUserId(getLoginUserId());
  70 + // 排除用户已删除的(隐藏的)
  71 + pageReqVO.setUserDeleted(false);
  72 + PageResult<ProductBrowseHistoryDO> pageResult = productBrowseHistoryService.getBrowseHistoryPage(pageReqVO);
  73 + if (CollUtil.isEmpty(pageResult.getList())) {
  74 + return success(PageResult.empty());
  75 + }
  76 +
  77 + // 得到商品 spu 信息
  78 + Set<Long> spuIds = convertSet(pageResult.getList(), ProductBrowseHistoryDO::getSpuId);
  79 + Map<Long, ProductSpuDO> spuMap = convertMap(productSpuService.getSpuList(spuIds), ProductSpuDO::getId);
  80 +
  81 + // 转换 VO 结果
  82 + PageResult<AppProductBrowseHistoryRespVO> result = BeanUtils.toBean(pageResult, AppProductBrowseHistoryRespVO.class,
  83 + vo -> Optional.ofNullable(spuMap.get(vo.getSpuId())).ifPresent(spu -> {
  84 + vo.setSpuName(spu.getName());
  85 + vo.setPicUrl(spu.getPicUrl());
  86 + }));
  87 + return success(result);
  88 + }
  89 +
  90 +}
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/history/vo/AppProductBrowseHistoryDeleteReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.product.controller.app.history.vo;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import jakarta.validation.constraints.NotEmpty;
  5 +import lombok.Data;
  6 +
  7 +import java.util.List;
  8 +
  9 +import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED;
  10 +
  11 +@Schema(description = "用户 APP - 删除商品浏览记录的 Request VO")
  12 +@Data
  13 +public class AppProductBrowseHistoryDeleteReqVO {
  14 +
  15 + @Schema(description = "商品 SPU 编号数组", requiredMode = REQUIRED, example = "29502")
  16 + @NotEmpty(message = "商品 SPU 编号数组不能为空")
  17 + private List<Long> spuIds;
  18 +
  19 +}
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/history/vo/AppProductBrowseHistoryPageReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.product.controller.app.history.vo;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  4 +import io.swagger.v3.oas.annotations.media.Schema;
  5 +import lombok.Data;
  6 +import lombok.EqualsAndHashCode;
  7 +import lombok.ToString;
  8 +import org.springframework.format.annotation.DateTimeFormat;
  9 +
  10 +import java.time.LocalDateTime;
  11 +
  12 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
  13 +
  14 +@Schema(description = "用户 APP - 商品浏览记录分页 Request VO")
  15 +@Data
  16 +@EqualsAndHashCode(callSuper = true)
  17 +@ToString(callSuper = true)
  18 +public class AppProductBrowseHistoryPageReqVO extends PageParam {
  19 + @Schema(description = "创建时间")
  20 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  21 + private LocalDateTime[] createTime;
  22 +
  23 +}
0 \ No newline at end of file 24 \ No newline at end of file
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/history/vo/AppProductBrowseHistoryRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.product.controller.app.history.vo;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +
  6 +import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED;
  7 +
  8 +@Schema(description = "用户 App - 商品浏览记录 Response VO")
  9 +@Data
  10 +public class AppProductBrowseHistoryRespVO {
  11 +
  12 + @Schema(description = "编号", requiredMode = REQUIRED, example = "1")
  13 + private Long id;
  14 +
  15 + @Schema(description = "商品 SPU 编号", requiredMode = REQUIRED, example = "29502")
  16 + private Long spuId;
  17 +
  18 + // ========== 商品相关字段 ==========
  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 +}
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/AppProductSpuController.java
@@ -14,6 +14,7 @@ import cn.iocoder.yudao.module.product.convert.spu.ProductSpuConvert; @@ -14,6 +14,7 @@ import cn.iocoder.yudao.module.product.convert.spu.ProductSpuConvert;
14 import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO; 14 import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO;
15 import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO; 15 import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO;
16 import cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum; 16 import cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum;
  17 +import cn.iocoder.yudao.module.product.service.history.ProductBrowseHistoryService;
17 import cn.iocoder.yudao.module.product.service.sku.ProductSkuService; 18 import cn.iocoder.yudao.module.product.service.sku.ProductSkuService;
18 import cn.iocoder.yudao.module.product.service.spu.ProductSpuService; 19 import cn.iocoder.yudao.module.product.service.spu.ProductSpuService;
19 import io.swagger.v3.oas.annotations.Operation; 20 import io.swagger.v3.oas.annotations.Operation;
@@ -48,6 +49,8 @@ public class AppProductSpuController { @@ -48,6 +49,8 @@ public class AppProductSpuController {
48 private ProductSpuService productSpuService; 49 private ProductSpuService productSpuService;
49 @Resource 50 @Resource
50 private ProductSkuService productSkuService; 51 private ProductSkuService productSkuService;
  52 + @Resource
  53 + private ProductBrowseHistoryService productBrowseHistoryService;
51 54
52 @Resource 55 @Resource
53 private MemberLevelApi memberLevelApi; 56 private MemberLevelApi memberLevelApi;
@@ -122,6 +125,11 @@ public class AppProductSpuController { @@ -122,6 +125,11 @@ public class AppProductSpuController {
122 throw exception(SPU_NOT_ENABLE); 125 throw exception(SPU_NOT_ENABLE);
123 } 126 }
124 127
  128 + // 增加浏览量
  129 + productSpuService.updateBrowseCount(id, 1);
  130 + // 保存浏览记录
  131 + productBrowseHistoryService.createBrowseHistory(getLoginUserId(), id);
  132 +
125 // 拼接返回 133 // 拼接返回
126 List<ProductSkuDO> skus = productSkuService.getSkuListBySpuId(spu.getId()); 134 List<ProductSkuDO> skus = productSkuService.getSkuListBySpuId(spu.getId());
127 AppProductSpuDetailRespVO detailVO = ProductSpuConvert.INSTANCE.convertForGetSpuDetail(spu, skus); 135 AppProductSpuDetailRespVO detailVO = ProductSpuConvert.INSTANCE.convertForGetSpuDetail(spu, skus);
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/history/ProductBrowseHistoryDO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.product.dal.dataobject.history;
  2 +
  3 +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  4 +import com.baomidou.mybatisplus.annotation.KeySequence;
  5 +import com.baomidou.mybatisplus.annotation.TableId;
  6 +import com.baomidou.mybatisplus.annotation.TableName;
  7 +import lombok.*;
  8 +
  9 +/**
  10 + * 商品浏览记录 DO
  11 + *
  12 + * @author owen
  13 + */
  14 +@TableName("product_browse_history")
  15 +@KeySequence("product_browse_history_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
  16 +@Data
  17 +@EqualsAndHashCode(callSuper = true)
  18 +@ToString(callSuper = true)
  19 +@Builder
  20 +@NoArgsConstructor
  21 +@AllArgsConstructor
  22 +public class ProductBrowseHistoryDO extends BaseDO {
  23 +
  24 + /**
  25 + * 记录编号
  26 + */
  27 + @TableId
  28 + private Long id;
  29 + /**
  30 + * 商品 SPU 编号
  31 + */
  32 + private Long spuId;
  33 + /**
  34 + * 用户编号
  35 + */
  36 + private Long userId;
  37 + /**
  38 + * 用户是否删除
  39 + */
  40 + private Boolean userDeleted;
  41 +
  42 +}
0 \ No newline at end of file 43 \ No newline at end of file
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/history/ProductBrowseHistoryMapper.java 0 → 100644
  1 +package cn.iocoder.yudao.module.product.dal.mysql.history;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  5 +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  6 +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  7 +import cn.iocoder.yudao.module.product.controller.admin.history.vo.ProductBrowseHistoryPageReqVO;
  8 +import cn.iocoder.yudao.module.product.dal.dataobject.history.ProductBrowseHistoryDO;
  9 +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
  10 +import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
  11 +import org.apache.ibatis.annotations.Mapper;
  12 +
  13 +import java.util.Collection;
  14 +
  15 +/**
  16 + * 商品浏览记录 Mapper
  17 + *
  18 + * @author owen
  19 + */
  20 +@Mapper
  21 +public interface ProductBrowseHistoryMapper extends BaseMapperX<ProductBrowseHistoryDO> {
  22 +
  23 + default PageResult<ProductBrowseHistoryDO> selectPage(ProductBrowseHistoryPageReqVO reqVO) {
  24 + return selectPage(reqVO, new LambdaQueryWrapperX<ProductBrowseHistoryDO>()
  25 + .eqIfPresent(ProductBrowseHistoryDO::getUserId, reqVO.getUserId())
  26 + .eqIfPresent(ProductBrowseHistoryDO::getUserDeleted, reqVO.getUserDeleted())
  27 + .eqIfPresent(ProductBrowseHistoryDO::getSpuId, reqVO.getSpuId())
  28 + .betweenIfPresent(ProductBrowseHistoryDO::getCreateTime, reqVO.getCreateTime())
  29 + .orderByDesc(ProductBrowseHistoryDO::getId));
  30 + }
  31 +
  32 + default void updateUserDeletedByUserId(Long userId, Collection<Long> spuIds, Boolean userDeleted) {
  33 + update(new LambdaUpdateWrapper<ProductBrowseHistoryDO>()
  34 + .eq(ProductBrowseHistoryDO::getUserId, userId)
  35 + .in(CollUtil.isNotEmpty(spuIds), ProductBrowseHistoryDO::getSpuId, spuIds)
  36 + .set(ProductBrowseHistoryDO::getUserDeleted, userDeleted));
  37 + }
  38 +
  39 + default Long selectCountByUserIdAndUserDeleted(Long userId, Boolean userDeleted) {
  40 + return selectCount(new LambdaQueryWrapperX<ProductBrowseHistoryDO>()
  41 + .eq(ProductBrowseHistoryDO::getUserId, userId)
  42 + .eqIfPresent(ProductBrowseHistoryDO::getUserDeleted, userDeleted));
  43 + }
  44 +
  45 + default Page<ProductBrowseHistoryDO> selectPageByUserIdOrderByCreateTimeAsc(Long userId) {
  46 + Page<ProductBrowseHistoryDO> page = Page.of(0, 1);
  47 + return selectPage(page, new LambdaQueryWrapperX<ProductBrowseHistoryDO>()
  48 + .eqIfPresent(ProductBrowseHistoryDO::getUserId, userId)
  49 + .orderByAsc(ProductBrowseHistoryDO::getCreateTime));
  50 + }
  51 +
  52 +}
0 \ No newline at end of file 53 \ No newline at end of file
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/spu/ProductSpuMapper.java
@@ -71,7 +71,7 @@ public interface ProductSpuMapper extends BaseMapperX&lt;ProductSpuDO&gt; { @@ -71,7 +71,7 @@ public interface ProductSpuMapper extends BaseMapperX&lt;ProductSpuDO&gt; {
71 query.eq(ProductSpuDO::getRecommendBenefit, true); 71 query.eq(ProductSpuDO::getRecommendBenefit, true);
72 } else if (ObjUtil.equal(pageReqVO.getRecommendType(), AppProductSpuPageReqVO.RECOMMEND_TYPE_BEST)) { 72 } else if (ObjUtil.equal(pageReqVO.getRecommendType(), AppProductSpuPageReqVO.RECOMMEND_TYPE_BEST)) {
73 query.eq(ProductSpuDO::getRecommendBest, true); 73 query.eq(ProductSpuDO::getRecommendBest, true);
74 - } else if (ObjUtil.equal(pageReqVO.getRecommendType(), AppProductSpuPageReqVO.RECOMMEND_TYPE_NEW)) { 74 + } else if (ObjUtil.equal(pageReqVO.getRecommendType(), AppProductSpuPageReqVO.RECOMMEND_TYPE_NEW)) {
75 query.eq(ProductSpuDO::getRecommendNew, true); 75 query.eq(ProductSpuDO::getRecommendNew, true);
76 } else if (ObjUtil.equal(pageReqVO.getRecommendType(), AppProductSpuPageReqVO.RECOMMEND_TYPE_GOOD)) { 76 } else if (ObjUtil.equal(pageReqVO.getRecommendType(), AppProductSpuPageReqVO.RECOMMEND_TYPE_GOOD)) {
77 query.eq(ProductSpuDO::getRecommendGood, true); 77 query.eq(ProductSpuDO::getRecommendGood, true);
@@ -141,8 +141,8 @@ public interface ProductSpuMapper extends BaseMapperX&lt;ProductSpuDO&gt; { @@ -141,8 +141,8 @@ public interface ProductSpuMapper extends BaseMapperX&lt;ProductSpuDO&gt; {
141 /** 141 /**
142 * 添加后台 Tab 选项的查询条件 142 * 添加后台 Tab 选项的查询条件
143 * 143 *
144 - * @param tabType 标签类型  
145 - * @param query 查询条件 144 + * @param tabType 标签类型
  145 + * @param query 查询条件
146 */ 146 */
147 static void appendTabQuery(Integer tabType, LambdaQueryWrapperX<ProductSpuDO> query) { 147 static void appendTabQuery(Integer tabType, LambdaQueryWrapperX<ProductSpuDO> query) {
148 // 出售中商品 148 // 出售中商品
@@ -169,4 +169,17 @@ public interface ProductSpuMapper extends BaseMapperX&lt;ProductSpuDO&gt; { @@ -169,4 +169,17 @@ public interface ProductSpuMapper extends BaseMapperX&lt;ProductSpuDO&gt; {
169 } 169 }
170 } 170 }
171 171
  172 + /**
  173 + * 更新商品 SPU 浏览量
  174 + *
  175 + * @param id 商品 SPU 编号
  176 + * @param incrCount 增加的数量
  177 + */
  178 + default void updateBrowseCount(Long id, int incrCount) {
  179 + LambdaUpdateWrapper<ProductSpuDO> updateWrapper = new LambdaUpdateWrapper<ProductSpuDO>()
  180 + .setSql(" browse_count = browse_count +" + incrCount)
  181 + .eq(ProductSpuDO::getId, id);
  182 + update(null, updateWrapper);
  183 + }
  184 +
172 } 185 }
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/history/ProductBrowseHistoryService.java 0 → 100644
  1 +package cn.iocoder.yudao.module.product.service.history;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.module.product.controller.admin.history.vo.ProductBrowseHistoryPageReqVO;
  5 +import cn.iocoder.yudao.module.product.dal.dataobject.history.ProductBrowseHistoryDO;
  6 +
  7 +import java.util.Collection;
  8 +
  9 +/**
  10 + * 商品浏览记录 Service 接口
  11 + *
  12 + * @author owen
  13 + */
  14 +public interface ProductBrowseHistoryService {
  15 +
  16 + /**
  17 + * 创建商品浏览记录
  18 + *
  19 + * @param userId 用户编号
  20 + * @param spuId SPU 编号
  21 + * @return 编号
  22 + */
  23 + Long createBrowseHistory(Long userId, Long spuId);
  24 +
  25 + /**
  26 + * 隐藏用户商品浏览记录
  27 + *
  28 + * @param userId 用户编号
  29 + * @param spuId SPU 编号
  30 + */
  31 + void hideUserBrowseHistory(Long userId, Collection<Long> spuId);
  32 +
  33 + /**
  34 + * 获得商品浏览记录
  35 + *
  36 + * @param id 编号
  37 + * @return 商品浏览记录
  38 + */
  39 + ProductBrowseHistoryDO getBrowseHistory(Long id);
  40 +
  41 + /**
  42 + * 获取用户记录数量
  43 + *
  44 + * @param userId 用户编号
  45 + * @param userDeleted 用户是否删除
  46 + * @return 数量
  47 + */
  48 + Long getBrowseHistoryCount(Long userId, Boolean userDeleted);
  49 +
  50 + /**
  51 + * 获得商品浏览记录分页
  52 + *
  53 + * @param pageReqVO 分页查询
  54 + * @return 商品浏览记录分页
  55 + */
  56 + PageResult<ProductBrowseHistoryDO> getBrowseHistoryPage(ProductBrowseHistoryPageReqVO pageReqVO);
  57 +
  58 +}
0 \ No newline at end of file 59 \ No newline at end of file
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/history/ProductBrowseHistoryServiceImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.product.service.history;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  5 +import cn.iocoder.yudao.module.product.controller.admin.history.vo.ProductBrowseHistoryPageReqVO;
  6 +import cn.iocoder.yudao.module.product.dal.dataobject.history.ProductBrowseHistoryDO;
  7 +import cn.iocoder.yudao.module.product.dal.mysql.history.ProductBrowseHistoryMapper;
  8 +import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
  9 +import jakarta.annotation.Resource;
  10 +import org.springframework.stereotype.Service;
  11 +import org.springframework.validation.annotation.Validated;
  12 +
  13 +import java.util.Collection;
  14 +
  15 +/**
  16 + * 商品浏览记录 Service 实现类
  17 + *
  18 + * @author owen
  19 + */
  20 +@Service
  21 +@Validated
  22 +public class ProductBrowseHistoryServiceImpl implements ProductBrowseHistoryService {
  23 + private static final int USER_STORE_MAXIMUM = 100;
  24 +
  25 + @Resource
  26 + private ProductBrowseHistoryMapper browseHistoryMapper;
  27 +
  28 + @Override
  29 + public Long createBrowseHistory(Long userId, Long spuId) {
  30 + // 情况一:同一个商品,只保留最新的一条记录
  31 + ProductBrowseHistoryDO historyDO = browseHistoryMapper.selectOne(ProductBrowseHistoryDO::getUserId, userId, ProductBrowseHistoryDO::getSpuId, spuId);
  32 + if (historyDO != null) {
  33 + browseHistoryMapper.deleteById(historyDO);
  34 + } else {
  35 + // 情况二:限制每个用户的浏览记录的条数
  36 + Page<ProductBrowseHistoryDO> pageResult = browseHistoryMapper.selectPageByUserIdOrderByCreateTimeAsc(userId);
  37 + if (pageResult.getTotal() >= USER_STORE_MAXIMUM) {
  38 + // 删除最早的一条
  39 + browseHistoryMapper.deleteById(CollUtil.getFirst(pageResult.getRecords()));
  40 + }
  41 + }
  42 +
  43 + // 插入
  44 + ProductBrowseHistoryDO browseHistory = new ProductBrowseHistoryDO()
  45 + .setUserId(userId)
  46 + .setSpuId(spuId);
  47 + browseHistoryMapper.insert(browseHistory);
  48 + // 返回
  49 + return browseHistory.getId();
  50 + }
  51 +
  52 + @Override
  53 + public void hideUserBrowseHistory(Long userId, Collection<Long> spuIds) {
  54 + browseHistoryMapper.updateUserDeletedByUserId(userId, spuIds, true);
  55 + }
  56 +
  57 + @Override
  58 + public ProductBrowseHistoryDO getBrowseHistory(Long id) {
  59 + return browseHistoryMapper.selectById(id);
  60 + }
  61 +
  62 + @Override
  63 + public Long getBrowseHistoryCount(Long userId, Boolean userDeleted) {
  64 + return browseHistoryMapper.selectCountByUserIdAndUserDeleted(userId, userDeleted);
  65 + }
  66 +
  67 + @Override
  68 + public PageResult<ProductBrowseHistoryDO> getBrowseHistoryPage(ProductBrowseHistoryPageReqVO pageReqVO) {
  69 + return browseHistoryMapper.selectPage(pageReqVO);
  70 + }
  71 +
  72 +}
0 \ No newline at end of file 73 \ No newline at end of file
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuService.java
@@ -148,4 +148,12 @@ public interface ProductSpuService { @@ -148,4 +148,12 @@ public interface ProductSpuService {
148 */ 148 */
149 List<ProductSpuDO> validateSpuList(Collection<Long> ids); 149 List<ProductSpuDO> validateSpuList(Collection<Long> ids);
150 150
  151 + /**
  152 + * 更新商品 SPU 浏览量
  153 + *
  154 + * @param id 商品 SPU 编号
  155 + * @param incrCount 增加的数量
  156 + */
  157 + void updateBrowseCount(Long id, int incrCount);
  158 +
151 } 159 }
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuServiceImpl.java
@@ -158,6 +158,11 @@ public class ProductSpuServiceImpl implements ProductSpuService { @@ -158,6 +158,11 @@ public class ProductSpuServiceImpl implements ProductSpuService {
158 } 158 }
159 159
160 @Override 160 @Override
  161 + public void updateBrowseCount(Long id, int incrCount) {
  162 + productSpuMapper.updateBrowseCount(id , incrCount);
  163 + }
  164 +
  165 + @Override
161 @Transactional(rollbackFor = Exception.class) 166 @Transactional(rollbackFor = Exception.class)
162 public void deleteSpu(Long id) { 167 public void deleteSpu(Long id) {
163 // 校验存在 168 // 校验存在