diff --git a/sql/mysql/optinal/product_browse_history.sql b/sql/mysql/optinal/product_browse_history.sql new file mode 100644 index 000000000..8925f7c9e --- /dev/null +++ b/sql/mysql/optinal/product_browse_history.sql @@ -0,0 +1,22 @@ +CREATE TABLE product_browse_history +( + id bigint AUTO_INCREMENT COMMENT '记录编号' + PRIMARY KEY, + user_id bigint NOT NULL COMMENT '用户编号', + spu_id bigint NOT NULL COMMENT '商品 SPU 编号', + user_deleted bit DEFAULT b'0' NOT NULL COMMENT '用户是否删除', + creator varchar(64) DEFAULT '' NULL COMMENT '创建者', + create_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL COMMENT '创建时间', + updater varchar(64) DEFAULT '' NULL COMMENT '更新者', + update_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + deleted bit DEFAULT b'0' NOT NULL COMMENT '是否删除', + tenant_id bigint DEFAULT 0 NOT NULL COMMENT '租户编号' +) + COMMENT '商品浏览记录表'; + +CREATE INDEX idx_spuId + ON product_browse_history (spu_id); + +CREATE INDEX idx_userId + ON product_browse_history (user_id); + diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/history/ProductBrowseHistoryController.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/history/ProductBrowseHistoryController.java new file mode 100644 index 000000000..b87e4ee44 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/history/ProductBrowseHistoryController.java @@ -0,0 +1,39 @@ +package cn.iocoder.yudao.module.product.controller.admin.history; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.product.controller.admin.history.vo.ProductBrowseHistoryPageReqVO; +import cn.iocoder.yudao.module.product.controller.admin.history.vo.ProductBrowseHistoryRespVO; +import cn.iocoder.yudao.module.product.dal.dataobject.history.ProductBrowseHistoryDO; +import cn.iocoder.yudao.module.product.service.history.ProductBrowseHistoryService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Tag(name = "管理后台 - 商品浏览记录") +@RestController +@RequestMapping("/product/browse-history") +@Validated +public class ProductBrowseHistoryController { + + @Resource + private ProductBrowseHistoryService browseHistoryService; + + @GetMapping("/page") + @Operation(summary = "获得商品浏览记录分页") + @PreAuthorize("@ss.hasPermission('product:browse-history:query')") + public CommonResult> getBrowseHistoryPage(@Valid ProductBrowseHistoryPageReqVO pageReqVO) { + PageResult pageResult = browseHistoryService.getBrowseHistoryPage(pageReqVO); + return success(BeanUtils.toBean(pageResult, ProductBrowseHistoryRespVO.class)); + } + +} \ No newline at end of file diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/history/vo/ProductBrowseHistoryPageReqVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/history/vo/ProductBrowseHistoryPageReqVO.java new file mode 100644 index 000000000..aa3010212 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/history/vo/ProductBrowseHistoryPageReqVO.java @@ -0,0 +1,33 @@ +package cn.iocoder.yudao.module.product.controller.admin.history.vo; + +import cn.iocoder.yudao.framework.common.pojo.SortablePageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - 商品浏览记录分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class ProductBrowseHistoryPageReqVO extends SortablePageParam { + + @Schema(description = "用户编号", example = "4314") + private Long userId; + + @Schema(description = "用户是否删除", example = "false") + private Boolean userDeleted; + + @Schema(description = "商品 SPU 编号", example = "42") + private Long spuId; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + +} \ No newline at end of file diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/history/vo/ProductBrowseHistoryRespVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/history/vo/ProductBrowseHistoryRespVO.java new file mode 100644 index 000000000..0e2e0cbed --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/history/vo/ProductBrowseHistoryRespVO.java @@ -0,0 +1,34 @@ +package cn.iocoder.yudao.module.product.controller.admin.history.vo; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - 商品浏览记录 Response VO") +@Data +@ExcelIgnoreUnannotated +public class ProductBrowseHistoryRespVO { + + @Schema(description = "记录编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "26055") + @ExcelProperty("记录编号") + private Long id; + + @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "4314") + @ExcelProperty("用户编号") + private Long userId; + + @Schema(description = "用户是否删除", example = "false") + private Boolean userDeleted; + + @Schema(description = "商品 SPU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "42") + @ExcelProperty("商品 SPU 编号") + private Long spuId; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("创建时间") + private LocalDateTime createTime; + +} \ No newline at end of file diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/history/AppProductBrowseHistoryController.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/history/AppProductBrowseHistoryController.java new file mode 100644 index 000000000..f15c149b1 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/history/AppProductBrowseHistoryController.java @@ -0,0 +1,90 @@ +package cn.iocoder.yudao.module.product.controller.app.history; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated; +import cn.iocoder.yudao.module.product.controller.admin.history.vo.ProductBrowseHistoryPageReqVO; +import cn.iocoder.yudao.module.product.controller.app.history.vo.AppProductBrowseHistoryDeleteReqVO; +import cn.iocoder.yudao.module.product.controller.app.history.vo.AppProductBrowseHistoryPageReqVO; +import cn.iocoder.yudao.module.product.controller.app.history.vo.AppProductBrowseHistoryRespVO; +import cn.iocoder.yudao.module.product.dal.dataobject.history.ProductBrowseHistoryDO; +import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO; +import cn.iocoder.yudao.module.product.service.history.ProductBrowseHistoryService; +import cn.iocoder.yudao.module.product.service.spu.ProductSpuService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import org.springframework.web.bind.annotation.*; + +import java.util.Map; +import java.util.Optional; +import java.util.Set; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; + +@Tag(name = "用户 APP - 商品浏览记录") +@RestController +@RequestMapping("/product/browse-history") +public class AppProductBrowseHistoryController { + + @Resource + private ProductBrowseHistoryService productBrowseHistoryService; + @Resource + private ProductSpuService productSpuService; + + @DeleteMapping(value = "/delete") + @Operation(summary = "删除商品浏览记录") + @PreAuthenticated + public CommonResult deleteBrowseHistory(@RequestBody @Valid AppProductBrowseHistoryDeleteReqVO reqVO) { + productBrowseHistoryService.hideUserBrowseHistory(getLoginUserId(), reqVO.getSpuIds()); + return success(Boolean.TRUE); + } + + @DeleteMapping(value = "/clean") + @Operation(summary = "清空商品浏览记录") + @PreAuthenticated + public CommonResult cleanBrowseHistory() { + productBrowseHistoryService.hideUserBrowseHistory(getLoginUserId(), null); + return success(Boolean.TRUE); + } + + @GetMapping(value = "/get-count") + @Operation(summary = "获得商品浏览记录数量") + @PreAuthenticated + public CommonResult getBrowseHistoryCount() { + return success(productBrowseHistoryService.getBrowseHistoryCount(getLoginUserId(), false)); + } + + @GetMapping(value = "/page") + @Operation(summary = "获得商品浏览记录分页") + @PreAuthenticated + public CommonResult> getBrowseHistoryPage(AppProductBrowseHistoryPageReqVO reqVO) { + ProductBrowseHistoryPageReqVO pageReqVO = BeanUtils.toBean(reqVO, ProductBrowseHistoryPageReqVO.class); + pageReqVO.setUserId(getLoginUserId()); + // 排除用户已删除的(隐藏的) + pageReqVO.setUserDeleted(false); + PageResult pageResult = productBrowseHistoryService.getBrowseHistoryPage(pageReqVO); + if (CollUtil.isEmpty(pageResult.getList())) { + return success(PageResult.empty()); + } + + // 得到商品 spu 信息 + Set spuIds = convertSet(pageResult.getList(), ProductBrowseHistoryDO::getSpuId); + Map spuMap = convertMap(productSpuService.getSpuList(spuIds), ProductSpuDO::getId); + + // 转换 VO 结果 + PageResult result = BeanUtils.toBean(pageResult, AppProductBrowseHistoryRespVO.class, + vo -> Optional.ofNullable(spuMap.get(vo.getSpuId())).ifPresent(spu -> { + vo.setSpuName(spu.getName()); + vo.setPicUrl(spu.getPicUrl()); + })); + return success(result); + } + +} diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/history/vo/AppProductBrowseHistoryDeleteReqVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/history/vo/AppProductBrowseHistoryDeleteReqVO.java new file mode 100644 index 000000000..5eb9d439a --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/history/vo/AppProductBrowseHistoryDeleteReqVO.java @@ -0,0 +1,19 @@ +package cn.iocoder.yudao.module.product.controller.app.history.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; + +import java.util.List; + +import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; + +@Schema(description = "用户 APP - 删除商品浏览记录的 Request VO") +@Data +public class AppProductBrowseHistoryDeleteReqVO { + + @Schema(description = "商品 SPU 编号数组", requiredMode = REQUIRED, example = "29502") + @NotEmpty(message = "商品 SPU 编号数组不能为空") + private List spuIds; + +} diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/history/vo/AppProductBrowseHistoryPageReqVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/history/vo/AppProductBrowseHistoryPageReqVO.java new file mode 100644 index 000000000..f2e0387fc --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/history/vo/AppProductBrowseHistoryPageReqVO.java @@ -0,0 +1,23 @@ +package cn.iocoder.yudao.module.product.controller.app.history.vo; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "用户 APP - 商品浏览记录分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class AppProductBrowseHistoryPageReqVO extends PageParam { + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + +} \ No newline at end of file diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/history/vo/AppProductBrowseHistoryRespVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/history/vo/AppProductBrowseHistoryRespVO.java new file mode 100644 index 000000000..05b528cdb --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/history/vo/AppProductBrowseHistoryRespVO.java @@ -0,0 +1,29 @@ +package cn.iocoder.yudao.module.product.controller.app.history.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; + +@Schema(description = "用户 App - 商品浏览记录 Response VO") +@Data +public class AppProductBrowseHistoryRespVO { + + @Schema(description = "编号", requiredMode = REQUIRED, example = "1") + private Long id; + + @Schema(description = "商品 SPU 编号", requiredMode = REQUIRED, example = "29502") + private Long spuId; + + // ========== 商品相关字段 ========== + + @Schema(description = "商品 SPU 名称", example = "赵六") + private String spuName; + + @Schema(description = "商品封面图", example = "https://domain/pic.png") + private String picUrl; + + @Schema(description = "商品单价", example = "100") + private Integer price; + +} diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/AppProductSpuController.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/AppProductSpuController.java index 87655523b..c12729665 100644 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/AppProductSpuController.java +++ b/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; import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO; import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO; import cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum; +import cn.iocoder.yudao.module.product.service.history.ProductBrowseHistoryService; import cn.iocoder.yudao.module.product.service.sku.ProductSkuService; import cn.iocoder.yudao.module.product.service.spu.ProductSpuService; import io.swagger.v3.oas.annotations.Operation; @@ -48,6 +49,8 @@ public class AppProductSpuController { private ProductSpuService productSpuService; @Resource private ProductSkuService productSkuService; + @Resource + private ProductBrowseHistoryService productBrowseHistoryService; @Resource private MemberLevelApi memberLevelApi; @@ -122,6 +125,11 @@ public class AppProductSpuController { throw exception(SPU_NOT_ENABLE); } + // 增加浏览量 + productSpuService.updateBrowseCount(id, 1); + // 保存浏览记录 + productBrowseHistoryService.createBrowseHistory(getLoginUserId(), id); + // 拼接返回 List skus = productSkuService.getSkuListBySpuId(spu.getId()); AppProductSpuDetailRespVO detailVO = ProductSpuConvert.INSTANCE.convertForGetSpuDetail(spu, skus); diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/history/ProductBrowseHistoryDO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/history/ProductBrowseHistoryDO.java new file mode 100644 index 000000000..472574bd9 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/history/ProductBrowseHistoryDO.java @@ -0,0 +1,42 @@ +package cn.iocoder.yudao.module.product.dal.dataobject.history; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +/** + * 商品浏览记录 DO + * + * @author owen + */ +@TableName("product_browse_history") +@KeySequence("product_browse_history_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ProductBrowseHistoryDO extends BaseDO { + + /** + * 记录编号 + */ + @TableId + private Long id; + /** + * 商品 SPU 编号 + */ + private Long spuId; + /** + * 用户编号 + */ + private Long userId; + /** + * 用户是否删除 + */ + private Boolean userDeleted; + +} \ No newline at end of file diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/history/ProductBrowseHistoryMapper.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/history/ProductBrowseHistoryMapper.java new file mode 100644 index 000000000..40eb68b2e --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/history/ProductBrowseHistoryMapper.java @@ -0,0 +1,52 @@ +package cn.iocoder.yudao.module.product.dal.mysql.history; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.product.controller.admin.history.vo.ProductBrowseHistoryPageReqVO; +import cn.iocoder.yudao.module.product.dal.dataobject.history.ProductBrowseHistoryDO; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import org.apache.ibatis.annotations.Mapper; + +import java.util.Collection; + +/** + * 商品浏览记录 Mapper + * + * @author owen + */ +@Mapper +public interface ProductBrowseHistoryMapper extends BaseMapperX { + + default PageResult selectPage(ProductBrowseHistoryPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .eqIfPresent(ProductBrowseHistoryDO::getUserId, reqVO.getUserId()) + .eqIfPresent(ProductBrowseHistoryDO::getUserDeleted, reqVO.getUserDeleted()) + .eqIfPresent(ProductBrowseHistoryDO::getSpuId, reqVO.getSpuId()) + .betweenIfPresent(ProductBrowseHistoryDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(ProductBrowseHistoryDO::getId)); + } + + default void updateUserDeletedByUserId(Long userId, Collection spuIds, Boolean userDeleted) { + update(new LambdaUpdateWrapper() + .eq(ProductBrowseHistoryDO::getUserId, userId) + .in(CollUtil.isNotEmpty(spuIds), ProductBrowseHistoryDO::getSpuId, spuIds) + .set(ProductBrowseHistoryDO::getUserDeleted, userDeleted)); + } + + default Long selectCountByUserIdAndUserDeleted(Long userId, Boolean userDeleted) { + return selectCount(new LambdaQueryWrapperX() + .eq(ProductBrowseHistoryDO::getUserId, userId) + .eqIfPresent(ProductBrowseHistoryDO::getUserDeleted, userDeleted)); + } + + default Page selectPageByUserIdOrderByCreateTimeAsc(Long userId) { + Page page = Page.of(0, 1); + return selectPage(page, new LambdaQueryWrapperX() + .eqIfPresent(ProductBrowseHistoryDO::getUserId, userId) + .orderByAsc(ProductBrowseHistoryDO::getCreateTime)); + } + +} \ No newline at end of file diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/spu/ProductSpuMapper.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/spu/ProductSpuMapper.java index aab2844fa..f354f72d5 100755 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/spu/ProductSpuMapper.java +++ b/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 { query.eq(ProductSpuDO::getRecommendBenefit, true); } else if (ObjUtil.equal(pageReqVO.getRecommendType(), AppProductSpuPageReqVO.RECOMMEND_TYPE_BEST)) { query.eq(ProductSpuDO::getRecommendBest, true); - } else if (ObjUtil.equal(pageReqVO.getRecommendType(), AppProductSpuPageReqVO.RECOMMEND_TYPE_NEW)) { + } else if (ObjUtil.equal(pageReqVO.getRecommendType(), AppProductSpuPageReqVO.RECOMMEND_TYPE_NEW)) { query.eq(ProductSpuDO::getRecommendNew, true); } else if (ObjUtil.equal(pageReqVO.getRecommendType(), AppProductSpuPageReqVO.RECOMMEND_TYPE_GOOD)) { query.eq(ProductSpuDO::getRecommendGood, true); @@ -141,8 +141,8 @@ public interface ProductSpuMapper extends BaseMapperX { /** * 添加后台 Tab 选项的查询条件 * - * @param tabType 标签类型 - * @param query 查询条件 + * @param tabType 标签类型 + * @param query 查询条件 */ static void appendTabQuery(Integer tabType, LambdaQueryWrapperX query) { // 出售中商品 @@ -169,4 +169,17 @@ public interface ProductSpuMapper extends BaseMapperX { } } + /** + * 更新商品 SPU 浏览量 + * + * @param id 商品 SPU 编号 + * @param incrCount 增加的数量 + */ + default void updateBrowseCount(Long id, int incrCount) { + LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper() + .setSql(" browse_count = browse_count +" + incrCount) + .eq(ProductSpuDO::getId, id); + update(null, updateWrapper); + } + } diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/history/ProductBrowseHistoryService.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/history/ProductBrowseHistoryService.java new file mode 100644 index 000000000..8a1721bd7 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/history/ProductBrowseHistoryService.java @@ -0,0 +1,58 @@ +package cn.iocoder.yudao.module.product.service.history; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.product.controller.admin.history.vo.ProductBrowseHistoryPageReqVO; +import cn.iocoder.yudao.module.product.dal.dataobject.history.ProductBrowseHistoryDO; + +import java.util.Collection; + +/** + * 商品浏览记录 Service 接口 + * + * @author owen + */ +public interface ProductBrowseHistoryService { + + /** + * 创建商品浏览记录 + * + * @param userId 用户编号 + * @param spuId SPU 编号 + * @return 编号 + */ + Long createBrowseHistory(Long userId, Long spuId); + + /** + * 隐藏用户商品浏览记录 + * + * @param userId 用户编号 + * @param spuId SPU 编号 + */ + void hideUserBrowseHistory(Long userId, Collection spuId); + + /** + * 获得商品浏览记录 + * + * @param id 编号 + * @return 商品浏览记录 + */ + ProductBrowseHistoryDO getBrowseHistory(Long id); + + /** + * 获取用户记录数量 + * + * @param userId 用户编号 + * @param userDeleted 用户是否删除 + * @return 数量 + */ + Long getBrowseHistoryCount(Long userId, Boolean userDeleted); + + /** + * 获得商品浏览记录分页 + * + * @param pageReqVO 分页查询 + * @return 商品浏览记录分页 + */ + PageResult getBrowseHistoryPage(ProductBrowseHistoryPageReqVO pageReqVO); + +} \ No newline at end of file diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/history/ProductBrowseHistoryServiceImpl.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/history/ProductBrowseHistoryServiceImpl.java new file mode 100644 index 000000000..de890b8a3 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/history/ProductBrowseHistoryServiceImpl.java @@ -0,0 +1,72 @@ +package cn.iocoder.yudao.module.product.service.history; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.product.controller.admin.history.vo.ProductBrowseHistoryPageReqVO; +import cn.iocoder.yudao.module.product.dal.dataobject.history.ProductBrowseHistoryDO; +import cn.iocoder.yudao.module.product.dal.mysql.history.ProductBrowseHistoryMapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import java.util.Collection; + +/** + * 商品浏览记录 Service 实现类 + * + * @author owen + */ +@Service +@Validated +public class ProductBrowseHistoryServiceImpl implements ProductBrowseHistoryService { + private static final int USER_STORE_MAXIMUM = 100; + + @Resource + private ProductBrowseHistoryMapper browseHistoryMapper; + + @Override + public Long createBrowseHistory(Long userId, Long spuId) { + // 情况一:同一个商品,只保留最新的一条记录 + ProductBrowseHistoryDO historyDO = browseHistoryMapper.selectOne(ProductBrowseHistoryDO::getUserId, userId, ProductBrowseHistoryDO::getSpuId, spuId); + if (historyDO != null) { + browseHistoryMapper.deleteById(historyDO); + } else { + // 情况二:限制每个用户的浏览记录的条数 + Page pageResult = browseHistoryMapper.selectPageByUserIdOrderByCreateTimeAsc(userId); + if (pageResult.getTotal() >= USER_STORE_MAXIMUM) { + // 删除最早的一条 + browseHistoryMapper.deleteById(CollUtil.getFirst(pageResult.getRecords())); + } + } + + // 插入 + ProductBrowseHistoryDO browseHistory = new ProductBrowseHistoryDO() + .setUserId(userId) + .setSpuId(spuId); + browseHistoryMapper.insert(browseHistory); + // 返回 + return browseHistory.getId(); + } + + @Override + public void hideUserBrowseHistory(Long userId, Collection spuIds) { + browseHistoryMapper.updateUserDeletedByUserId(userId, spuIds, true); + } + + @Override + public ProductBrowseHistoryDO getBrowseHistory(Long id) { + return browseHistoryMapper.selectById(id); + } + + @Override + public Long getBrowseHistoryCount(Long userId, Boolean userDeleted) { + return browseHistoryMapper.selectCountByUserIdAndUserDeleted(userId, userDeleted); + } + + @Override + public PageResult getBrowseHistoryPage(ProductBrowseHistoryPageReqVO pageReqVO) { + return browseHistoryMapper.selectPage(pageReqVO); + } + +} \ No newline at end of file diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuService.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuService.java index 513f9c36d..d8f83c68d 100755 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuService.java +++ b/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 { */ List validateSpuList(Collection ids); + /** + * 更新商品 SPU 浏览量 + * + * @param id 商品 SPU 编号 + * @param incrCount 增加的数量 + */ + void updateBrowseCount(Long id, int incrCount); + } diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuServiceImpl.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuServiceImpl.java index 1d4e1999e..48046b8a5 100755 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuServiceImpl.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuServiceImpl.java @@ -157,6 +157,11 @@ public class ProductSpuServiceImpl implements ProductSpuService { return list; } + @Override + public void updateBrowseCount(Long id, int incrCount) { + productSpuMapper.updateBrowseCount(id , incrCount); + } + @Override @Transactional(rollbackFor = Exception.class) public void deleteSpu(Long id) {