From 0b7d42482f449112e33b74a9b511b0c31524bcb3 Mon Sep 17 00:00:00 2001 From: owen Date: Sun, 17 Dec 2023 19:30:50 +0800 Subject: [PATCH] =?UTF-8?q?=E7=BB=9F=E8=AE=A1=EF=BC=9A=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E5=95=86=E5=93=81=E7=BB=9F=E8=AE=A1=E5=AE=9A=E6=97=B6=E4=BB=BB?= =?UTF-8?q?=E5=8A=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../history/ProductBrowseHistoryMapper.java | 4 +- .../ProductBrowseHistoryServiceImpl.java | 4 +- .../mysql/product/ProductSpuStatisticsDO.java | 74 ------------------- .../mysql/product/ProductStatisticsDO.java | 70 ------------------ .../product/ProductStatisticsMapper.java | 20 +++++ .../job/product/ProductStatisticsJob.java | 48 ++++++++++++ .../product/ProductStatisticsService.java | 15 ++-- .../product/ProductStatisticsServiceImpl.java | 73 +++++++++++++++--- .../trade/TradeStatisticsServiceImpl.java | 2 +- .../product/ProductStatisticsMapper.xml | 64 ++++++++++++++++ 10 files changed, 206 insertions(+), 168 deletions(-) delete mode 100644 yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/dal/mysql/product/ProductSpuStatisticsDO.java delete mode 100644 yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/dal/mysql/product/ProductStatisticsDO.java create mode 100644 yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/job/product/ProductStatisticsJob.java create mode 100644 yudao-module-mall/yudao-module-statistics-biz/src/main/resources/mapper/product/ProductStatisticsMapper.xml 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 index 40eb68b2e..24ad124cc 100644 --- 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 @@ -42,8 +42,8 @@ public interface ProductBrowseHistoryMapper extends BaseMapperX selectPageByUserIdOrderByCreateTimeAsc(Long userId) { - Page page = Page.of(0, 1); + default Page selectPageByUserIdOrderByCreateTimeAsc(Long userId, Integer pageNo, Integer pageSize) { + Page page = Page.of(pageNo, pageSize); return selectPage(page, new LambdaQueryWrapperX() .eqIfPresent(ProductBrowseHistoryDO::getUserId, userId) .orderByAsc(ProductBrowseHistoryDO::getCreateTime)); 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 index de890b8a3..e1c80cf23 100644 --- 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 @@ -32,8 +32,8 @@ public class ProductBrowseHistoryServiceImpl implements ProductBrowseHistoryServ if (historyDO != null) { browseHistoryMapper.deleteById(historyDO); } else { - // 情况二:限制每个用户的浏览记录的条数 - Page pageResult = browseHistoryMapper.selectPageByUserIdOrderByCreateTimeAsc(userId); + // 情况二:限制每个用户的浏览记录的条数(只查一条最早地记录、记录总数) + Page pageResult = browseHistoryMapper.selectPageByUserIdOrderByCreateTimeAsc(userId, 1, 1); if (pageResult.getTotal() >= USER_STORE_MAXIMUM) { // 删除最早的一条 browseHistoryMapper.deleteById(CollUtil.getFirst(pageResult.getRecords())); diff --git a/yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/dal/mysql/product/ProductSpuStatisticsDO.java b/yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/dal/mysql/product/ProductSpuStatisticsDO.java deleted file mode 100644 index d29d4332b..000000000 --- a/yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/dal/mysql/product/ProductSpuStatisticsDO.java +++ /dev/null @@ -1,74 +0,0 @@ -package cn.iocoder.yudao.module.statistics.dal.mysql.product; - -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.*; - -import java.time.LocalDateTime; - -/** - * 商品 SPU 统计 DO - * - * 以天为维度,统计商品 SPU 的数据 - * - * @author 芋道源码 - */ -@TableName("product_spu_statistics") -@KeySequence("product_spu_statistics_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class ProductSpuStatisticsDO extends BaseDO { - - /** - * 编号,主键自增 - */ - @TableId - private Long id; - - /** - * 商品 SPU 编号 - * - * 关联 ProductSpuDO 的 id 字段 - */ - private Long spuId; - /** - * 统计日期 - */ - private LocalDateTime time; - - /** - * 浏览量 - */ - private Integer browseCount; - /** - * 收藏量 - */ - private Integer favoriteCount; - - /** - * 添加购物车次数 - * - * 以商品被添加到购物车的 createTime 计算,后续多次添加,不会增加该值。 - * 直到该次被下单、或者被删除,后续再次被添加到购物车。 - */ - private Integer addCartCount; - /** - * 创建订单商品数 - */ - private Integer createOrderCount; - /** - * 支付订单商品数 - */ - private Integer payOrderCount; - /** - * 总支付金额,单位:分 - */ - private Integer payPrice; - -} diff --git a/yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/dal/mysql/product/ProductStatisticsDO.java b/yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/dal/mysql/product/ProductStatisticsDO.java deleted file mode 100644 index 5937b41da..000000000 --- a/yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/dal/mysql/product/ProductStatisticsDO.java +++ /dev/null @@ -1,70 +0,0 @@ -package cn.iocoder.yudao.module.statistics.dal.mysql.product; - -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.*; - -import java.time.LocalDateTime; - -/** - * 商品统计 DO - * - * 以天为维度,统计全部的数据 - * - * 和 {@link ProductSpuStatisticsDO} 的差异是,它是全局的统计 - * - * @author 芋道源码 - */ -@TableName("product_spu_statistics") -@KeySequence("product_spu_statistics_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class ProductStatisticsDO extends BaseDO { - - /** - * 编号,主键自增 - */ - @TableId - private Long id; - - /** - * 统计日期 - */ - private LocalDateTime time; - - /** - * 浏览量 - */ - private Integer browseCount; - /** - * 收藏量 - */ - private Integer favoriteCount; - - /** - * 添加购物车次数 - * - * 以商品被添加到购物车的 createTime 计算,后续多次添加,不会增加该值。 - * 直到该次被下单、或者被删除,后续再次被添加到购物车。 - */ - private Integer addCartCount; - /** - * 创建订单商品数 - */ - private Integer createOrderCount; - /** - * 支付订单商品数 - */ - private Integer payOrderCount; - /** - * 总支付金额,单位:分 - */ - private Integer payPrice; - -} diff --git a/yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/dal/mysql/product/ProductStatisticsMapper.java b/yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/dal/mysql/product/ProductStatisticsMapper.java index f082bde90..4cf10f102 100644 --- a/yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/dal/mysql/product/ProductStatisticsMapper.java +++ b/yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/dal/mysql/product/ProductStatisticsMapper.java @@ -3,12 +3,16 @@ package cn.iocoder.yudao.module.statistics.dal.mysql.product; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.SortablePageParam; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; import cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX; import cn.iocoder.yudao.module.statistics.controller.admin.product.vo.ProductStatisticsReqVO; import cn.iocoder.yudao.module.statistics.controller.admin.product.vo.ProductStatisticsRespVO; import cn.iocoder.yudao.module.statistics.dal.dataobject.product.ProductStatisticsDO; +import com.baomidou.mybatisplus.core.metadata.IPage; import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import java.time.LocalDateTime; import java.util.List; /** @@ -57,4 +61,20 @@ public interface ProductStatisticsMapper extends BaseMapperX selectStatisticsResultPageByTimeBetween(IPage page, + @Param("beginTime") LocalDateTime beginTime, + @Param("endTime") LocalDateTime endTime); + + default Long selectCountByTimeBetween(LocalDateTime beginTime, LocalDateTime endTime) { + return selectCount(new LambdaQueryWrapperX().between(ProductStatisticsDO::getTime, beginTime, endTime)); + } + } \ No newline at end of file diff --git a/yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/job/product/ProductStatisticsJob.java b/yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/job/product/ProductStatisticsJob.java new file mode 100644 index 000000000..ab0a1fb71 --- /dev/null +++ b/yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/job/product/ProductStatisticsJob.java @@ -0,0 +1,48 @@ +package cn.iocoder.yudao.module.statistics.job.product; + +import cn.hutool.core.convert.Convert; +import cn.hutool.core.util.NumberUtil; +import cn.hutool.core.util.ObjUtil; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler; +import cn.iocoder.yudao.framework.tenant.core.job.TenantJob; +import cn.iocoder.yudao.module.statistics.service.product.ProductStatisticsService; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Component; + +// TODO 芋艿:缺个 Job 的配置;等和 Product 一起配置 + +/** + * 商品统计 Job + * + * @author owen + */ +@Component +public class ProductStatisticsJob implements JobHandler { + + @Resource + private ProductStatisticsService productStatisticsService; + + /** + * 执行商品统计任务 + * + * @param param 要统计的天数,只能是正整数,1 代表昨日数据 + * @return 统计结果 + */ + @Override + @TenantJob + public String execute(String param) { + // 默认昨日 + param = ObjUtil.defaultIfBlank(param, "1"); + // 校验参数的合理性 + if (!NumberUtil.isInteger(param)) { + throw new RuntimeException("商品统计任务的参数只能为是正整数"); + } + Integer days = Convert.toInt(param, 0); + if (days < 1) { + throw new RuntimeException("商品统计任务的参数只能为是正整数"); + } + String result = productStatisticsService.statisticsProduct(days); + return StrUtil.format("商品统计:\n{}", result); + } +} diff --git a/yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/service/product/ProductStatisticsService.java b/yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/service/product/ProductStatisticsService.java index dd99f85ae..09d84bdea 100644 --- a/yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/service/product/ProductStatisticsService.java +++ b/yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/service/product/ProductStatisticsService.java @@ -16,14 +16,6 @@ import java.util.List; */ public interface ProductStatisticsService { - /** - * 创建商品统计 - * - * @param entity 创建信息 - * @return 编号 - */ - Long createProductStatistics(ProductStatisticsDO entity); - /** * 获得商品统计排行榜分页 * @@ -49,4 +41,11 @@ public interface ProductStatisticsService { */ List getProductStatisticsList(ProductStatisticsReqVO reqVO); + /** + * 统计指定天数的商品数据 + * + * @return 统计结果 + */ + String statisticsProduct(Integer days); + } \ No newline at end of file diff --git a/yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/service/product/ProductStatisticsServiceImpl.java b/yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/service/product/ProductStatisticsServiceImpl.java index 1d1dd6cc9..1b1044f53 100644 --- a/yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/service/product/ProductStatisticsServiceImpl.java +++ b/yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/service/product/ProductStatisticsServiceImpl.java @@ -1,5 +1,8 @@ package cn.iocoder.yudao.module.statistics.service.product; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.date.DatePattern; +import cn.hutool.core.date.LocalDateTimeUtil; import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.ObjUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; @@ -10,13 +13,18 @@ import cn.iocoder.yudao.module.statistics.controller.admin.product.vo.ProductSta import cn.iocoder.yudao.module.statistics.controller.admin.product.vo.ProductStatisticsRespVO; import cn.iocoder.yudao.module.statistics.dal.dataobject.product.ProductStatisticsDO; import cn.iocoder.yudao.module.statistics.dal.mysql.product.ProductStatisticsMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import jakarta.annotation.Resource; import org.springframework.stereotype.Service; +import org.springframework.util.StopWatch; import org.springframework.validation.annotation.Validated; import java.time.Duration; import java.time.LocalDateTime; import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.IntStream; /** @@ -31,17 +39,6 @@ public class ProductStatisticsServiceImpl implements ProductStatisticsService { @Resource private ProductStatisticsMapper productStatisticsMapper; - @Override - public Long createProductStatistics(ProductStatisticsDO entity) { - // 计算 访客支付转化率(百分比) - if (entity.getBrowseUserCount() != null && ObjUtil.notEqual(entity.getBrowseUserCount(), 0)) { - entity.setBrowseConvertPercent(100 * entity.getOrderPayCount() / entity.getBrowseUserCount()); - } - // 插入 - productStatisticsMapper.insert(entity); - // 返回 - return entity.getId(); - } @Override public PageResult getProductStatisticsRankPage(ProductStatisticsReqVO reqVO, SortablePageParam pageParam) { @@ -69,4 +66,58 @@ public class ProductStatisticsServiceImpl implements ProductStatisticsService { return productStatisticsMapper.selectListByTimeBetween(reqVO); } + @Override + public String statisticsProduct(Integer days) { + LocalDateTime today = LocalDateTime.now(); + return IntStream.rangeClosed(1, days) + .mapToObj(day -> statisticsProduct(today.minusDays(day))) + .sorted() + .collect(Collectors.joining("\n")); + } + + /** + * 统计商品数据 + * + * @param date 需要统计的日期 + * @return 统计结果 + */ + private String statisticsProduct(LocalDateTime date) { + // 1. 处理统计时间范围 + LocalDateTime beginTime = LocalDateTimeUtil.beginOfDay(date); + LocalDateTime endTime = LocalDateTimeUtil.endOfDay(date); + String dateStr = DatePattern.NORM_DATE_FORMATTER.format(date); + // 2. 检查该日是否已经统计过 + Long count = productStatisticsMapper.selectCountByTimeBetween(beginTime, endTime); + if (count != null && count > 0) { + return dateStr + " 数据已存在,如果需要重新统计,请先删除对应的数据"; + } + + // 3. 统计数据 + StopWatch stopWatch = new StopWatch(dateStr); + stopWatch.start(); + + // 分页统计,避免商品表数据较多时,出现超时问题 + final int pageSize = 100; + for (int pageNo = 1; ; pageNo ++) { + IPage page = productStatisticsMapper.selectStatisticsResultPageByTimeBetween( + Page.of(pageNo, pageSize, false), beginTime, endTime); + if (CollUtil.isEmpty(page.getRecords())) { + break; + } + + for (ProductStatisticsDO record : page.getRecords()) { + record.setTime(date.toLocalDate()); + // 计算 访客支付转化率(百分比) + if (record.getBrowseUserCount() != null && ObjUtil.notEqual(record.getBrowseUserCount(), 0)) { + record.setBrowseConvertPercent(100 * record.getOrderPayCount() / record.getBrowseUserCount()); + } + } + + // 4. 插入数据 + productStatisticsMapper.insertBatch(page.getRecords()); + } + + return stopWatch.prettyPrint(); + } + } \ No newline at end of file diff --git a/yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/service/trade/TradeStatisticsServiceImpl.java b/yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/service/trade/TradeStatisticsServiceImpl.java index 465a1911d..88f3aa2b1 100644 --- a/yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/service/trade/TradeStatisticsServiceImpl.java +++ b/yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/service/trade/TradeStatisticsServiceImpl.java @@ -99,7 +99,7 @@ public class TradeStatisticsServiceImpl implements TradeStatisticsService { // 1. 处理统计时间范围 LocalDateTime beginTime = LocalDateTimeUtil.beginOfDay(date); LocalDateTime endTime = LocalDateTimeUtil.endOfDay(date); - String dateStr = DatePattern.NORM_DATE_FORMAT.format(date); + String dateStr = DatePattern.NORM_DATE_FORMATTER.format(date); // 2. 检查该日是否已经统计过 TradeStatisticsDO entity = tradeStatisticsMapper.selectByTimeBetween(beginTime, endTime); if (entity != null) { diff --git a/yudao-module-mall/yudao-module-statistics-biz/src/main/resources/mapper/product/ProductStatisticsMapper.xml b/yudao-module-mall/yudao-module-statistics-biz/src/main/resources/mapper/product/ProductStatisticsMapper.xml new file mode 100644 index 000000000..e640d1d83 --- /dev/null +++ b/yudao-module-mall/yudao-module-statistics-biz/src/main/resources/mapper/product/ProductStatisticsMapper.xml @@ -0,0 +1,64 @@ + + + + + + +