Commit f374e778bb5d7c70d6ffc08ee2f02ad91f183093
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<ProductSpuDO> { | @@ -71,7 +71,7 @@ public interface ProductSpuMapper extends BaseMapperX<ProductSpuDO> { | ||
| 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<ProductSpuDO> { | @@ -141,8 +141,8 @@ public interface ProductSpuMapper extends BaseMapperX<ProductSpuDO> { | ||
| 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<ProductSpuDO> { | @@ -169,4 +169,17 @@ public interface ProductSpuMapper extends BaseMapperX<ProductSpuDO> { | ||
| 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 | // 校验存在 |