Commit 0b7d42482f449112e33b74a9b511b0c31524bcb3

Authored by owen
1 parent f374e778

统计:增加商品统计定时任务

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<ProductBrowseHis @@ -42,8 +42,8 @@ public interface ProductBrowseHistoryMapper extends BaseMapperX<ProductBrowseHis
42 .eqIfPresent(ProductBrowseHistoryDO::getUserDeleted, userDeleted)); 42 .eqIfPresent(ProductBrowseHistoryDO::getUserDeleted, userDeleted));
43 } 43 }
44 44
45 - default Page<ProductBrowseHistoryDO> selectPageByUserIdOrderByCreateTimeAsc(Long userId) {  
46 - Page<ProductBrowseHistoryDO> page = Page.of(0, 1); 45 + default Page<ProductBrowseHistoryDO> selectPageByUserIdOrderByCreateTimeAsc(Long userId, Integer pageNo, Integer pageSize) {
  46 + Page<ProductBrowseHistoryDO> page = Page.of(pageNo, pageSize);
47 return selectPage(page, new LambdaQueryWrapperX<ProductBrowseHistoryDO>() 47 return selectPage(page, new LambdaQueryWrapperX<ProductBrowseHistoryDO>()
48 .eqIfPresent(ProductBrowseHistoryDO::getUserId, userId) 48 .eqIfPresent(ProductBrowseHistoryDO::getUserId, userId)
49 .orderByAsc(ProductBrowseHistoryDO::getCreateTime)); 49 .orderByAsc(ProductBrowseHistoryDO::getCreateTime));
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 @@ -32,8 +32,8 @@ public class ProductBrowseHistoryServiceImpl implements ProductBrowseHistoryServ
32 if (historyDO != null) { 32 if (historyDO != null) {
33 browseHistoryMapper.deleteById(historyDO); 33 browseHistoryMapper.deleteById(historyDO);
34 } else { 34 } else {
35 - // 情况二:限制每个用户的浏览记录的条数  
36 - Page<ProductBrowseHistoryDO> pageResult = browseHistoryMapper.selectPageByUserIdOrderByCreateTimeAsc(userId); 35 + // 情况二:限制每个用户的浏览记录的条数(只查一条最早地记录、记录总数)
  36 + Page<ProductBrowseHistoryDO> pageResult = browseHistoryMapper.selectPageByUserIdOrderByCreateTimeAsc(userId, 1, 1);
37 if (pageResult.getTotal() >= USER_STORE_MAXIMUM) { 37 if (pageResult.getTotal() >= USER_STORE_MAXIMUM) {
38 // 删除最早的一条 38 // 删除最早的一条
39 browseHistoryMapper.deleteById(CollUtil.getFirst(pageResult.getRecords())); 39 browseHistoryMapper.deleteById(CollUtil.getFirst(pageResult.getRecords()));
yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/dal/mysql/product/ProductSpuStatisticsDO.java deleted
1 -package cn.iocoder.yudao.module.statistics.dal.mysql.product;  
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 -import java.time.LocalDateTime;  
10 -  
11 -/**  
12 - * 商品 SPU 统计 DO  
13 - *  
14 - * 以天为维度,统计商品 SPU 的数据  
15 - *  
16 - * @author 芋道源码  
17 - */  
18 -@TableName("product_spu_statistics")  
19 -@KeySequence("product_spu_statistics_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。  
20 -@Data  
21 -@EqualsAndHashCode(callSuper = true)  
22 -@ToString(callSuper = true)  
23 -@Builder  
24 -@NoArgsConstructor  
25 -@AllArgsConstructor  
26 -public class ProductSpuStatisticsDO extends BaseDO {  
27 -  
28 - /**  
29 - * 编号,主键自增  
30 - */  
31 - @TableId  
32 - private Long id;  
33 -  
34 - /**  
35 - * 商品 SPU 编号  
36 - *  
37 - * 关联 ProductSpuDO 的 id 字段  
38 - */  
39 - private Long spuId;  
40 - /**  
41 - * 统计日期  
42 - */  
43 - private LocalDateTime time;  
44 -  
45 - /**  
46 - * 浏览量  
47 - */  
48 - private Integer browseCount;  
49 - /**  
50 - * 收藏量  
51 - */  
52 - private Integer favoriteCount;  
53 -  
54 - /**  
55 - * 添加购物车次数  
56 - *  
57 - * 以商品被添加到购物车的 createTime 计算,后续多次添加,不会增加该值。  
58 - * 直到该次被下单、或者被删除,后续再次被添加到购物车。  
59 - */  
60 - private Integer addCartCount;  
61 - /**  
62 - * 创建订单商品数  
63 - */  
64 - private Integer createOrderCount;  
65 - /**  
66 - * 支付订单商品数  
67 - */  
68 - private Integer payOrderCount;  
69 - /**  
70 - * 总支付金额,单位:分  
71 - */  
72 - private Integer payPrice;  
73 -  
74 -}  
yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/dal/mysql/product/ProductStatisticsDO.java deleted
1 -package cn.iocoder.yudao.module.statistics.dal.mysql.product;  
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 -import java.time.LocalDateTime;  
10 -  
11 -/**  
12 - * 商品统计 DO  
13 - *  
14 - * 以天为维度,统计全部的数据  
15 - *  
16 - * 和 {@link ProductSpuStatisticsDO} 的差异是,它是全局的统计  
17 - *  
18 - * @author 芋道源码  
19 - */  
20 -@TableName("product_spu_statistics")  
21 -@KeySequence("product_spu_statistics_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。  
22 -@Data  
23 -@EqualsAndHashCode(callSuper = true)  
24 -@ToString(callSuper = true)  
25 -@Builder  
26 -@NoArgsConstructor  
27 -@AllArgsConstructor  
28 -public class ProductStatisticsDO extends BaseDO {  
29 -  
30 - /**  
31 - * 编号,主键自增  
32 - */  
33 - @TableId  
34 - private Long id;  
35 -  
36 - /**  
37 - * 统计日期  
38 - */  
39 - private LocalDateTime time;  
40 -  
41 - /**  
42 - * 浏览量  
43 - */  
44 - private Integer browseCount;  
45 - /**  
46 - * 收藏量  
47 - */  
48 - private Integer favoriteCount;  
49 -  
50 - /**  
51 - * 添加购物车次数  
52 - *  
53 - * 以商品被添加到购物车的 createTime 计算,后续多次添加,不会增加该值。  
54 - * 直到该次被下单、或者被删除,后续再次被添加到购物车。  
55 - */  
56 - private Integer addCartCount;  
57 - /**  
58 - * 创建订单商品数  
59 - */  
60 - private Integer createOrderCount;  
61 - /**  
62 - * 支付订单商品数  
63 - */  
64 - private Integer payOrderCount;  
65 - /**  
66 - * 总支付金额,单位:分  
67 - */  
68 - private Integer payPrice;  
69 -  
70 -}  
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; @@ -3,12 +3,16 @@ package cn.iocoder.yudao.module.statistics.dal.mysql.product;
3 import cn.iocoder.yudao.framework.common.pojo.PageResult; 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 import cn.iocoder.yudao.framework.common.pojo.SortablePageParam; 4 import cn.iocoder.yudao.framework.common.pojo.SortablePageParam;
5 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; 5 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  6 +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
6 import cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX; 7 import cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX;
7 import cn.iocoder.yudao.module.statistics.controller.admin.product.vo.ProductStatisticsReqVO; 8 import cn.iocoder.yudao.module.statistics.controller.admin.product.vo.ProductStatisticsReqVO;
8 import cn.iocoder.yudao.module.statistics.controller.admin.product.vo.ProductStatisticsRespVO; 9 import cn.iocoder.yudao.module.statistics.controller.admin.product.vo.ProductStatisticsRespVO;
9 import cn.iocoder.yudao.module.statistics.dal.dataobject.product.ProductStatisticsDO; 10 import cn.iocoder.yudao.module.statistics.dal.dataobject.product.ProductStatisticsDO;
  11 +import com.baomidou.mybatisplus.core.metadata.IPage;
10 import org.apache.ibatis.annotations.Mapper; 12 import org.apache.ibatis.annotations.Mapper;
  13 +import org.apache.ibatis.annotations.Param;
11 14
  15 +import java.time.LocalDateTime;
12 import java.util.List; 16 import java.util.List;
13 17
14 /** 18 /**
@@ -57,4 +61,20 @@ public interface ProductStatisticsMapper extends BaseMapperX&lt;ProductStatisticsDO @@ -57,4 +61,20 @@ public interface ProductStatisticsMapper extends BaseMapperX&lt;ProductStatisticsDO
57 .selectAvg(ProductStatisticsDO::getBrowseConvertPercent); 61 .selectAvg(ProductStatisticsDO::getBrowseConvertPercent);
58 } 62 }
59 63
  64 + /**
  65 + * 根据时间范围统计商品信息
  66 + *
  67 + * @param page 分页参数
  68 + * @param beginTime 起始时间
  69 + * @param endTime 截止时间
  70 + * @return 统计
  71 + */
  72 + IPage<ProductStatisticsDO> selectStatisticsResultPageByTimeBetween(IPage<ProductStatisticsDO> page,
  73 + @Param("beginTime") LocalDateTime beginTime,
  74 + @Param("endTime") LocalDateTime endTime);
  75 +
  76 + default Long selectCountByTimeBetween(LocalDateTime beginTime, LocalDateTime endTime) {
  77 + return selectCount(new LambdaQueryWrapperX<ProductStatisticsDO>().between(ProductStatisticsDO::getTime, beginTime, endTime));
  78 + }
  79 +
60 } 80 }
61 \ No newline at end of file 81 \ No newline at end of file
yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/job/product/ProductStatisticsJob.java 0 → 100644
  1 +package cn.iocoder.yudao.module.statistics.job.product;
  2 +
  3 +import cn.hutool.core.convert.Convert;
  4 +import cn.hutool.core.util.NumberUtil;
  5 +import cn.hutool.core.util.ObjUtil;
  6 +import cn.hutool.core.util.StrUtil;
  7 +import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler;
  8 +import cn.iocoder.yudao.framework.tenant.core.job.TenantJob;
  9 +import cn.iocoder.yudao.module.statistics.service.product.ProductStatisticsService;
  10 +import jakarta.annotation.Resource;
  11 +import org.springframework.stereotype.Component;
  12 +
  13 +// TODO 芋艿:缺个 Job 的配置;等和 Product 一起配置
  14 +
  15 +/**
  16 + * 商品统计 Job
  17 + *
  18 + * @author owen
  19 + */
  20 +@Component
  21 +public class ProductStatisticsJob implements JobHandler {
  22 +
  23 + @Resource
  24 + private ProductStatisticsService productStatisticsService;
  25 +
  26 + /**
  27 + * 执行商品统计任务
  28 + *
  29 + * @param param 要统计的天数,只能是正整数,1 代表昨日数据
  30 + * @return 统计结果
  31 + */
  32 + @Override
  33 + @TenantJob
  34 + public String execute(String param) {
  35 + // 默认昨日
  36 + param = ObjUtil.defaultIfBlank(param, "1");
  37 + // 校验参数的合理性
  38 + if (!NumberUtil.isInteger(param)) {
  39 + throw new RuntimeException("商品统计任务的参数只能为是正整数");
  40 + }
  41 + Integer days = Convert.toInt(param, 0);
  42 + if (days < 1) {
  43 + throw new RuntimeException("商品统计任务的参数只能为是正整数");
  44 + }
  45 + String result = productStatisticsService.statisticsProduct(days);
  46 + return StrUtil.format("商品统计:\n{}", result);
  47 + }
  48 +}
yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/service/product/ProductStatisticsService.java
@@ -17,14 +17,6 @@ import java.util.List; @@ -17,14 +17,6 @@ import java.util.List;
17 public interface ProductStatisticsService { 17 public interface ProductStatisticsService {
18 18
19 /** 19 /**
20 - * 创建商品统计  
21 - *  
22 - * @param entity 创建信息  
23 - * @return 编号  
24 - */  
25 - Long createProductStatistics(ProductStatisticsDO entity);  
26 -  
27 - /**  
28 * 获得商品统计排行榜分页 20 * 获得商品统计排行榜分页
29 * 21 *
30 * @param reqVO 查询条件 22 * @param reqVO 查询条件
@@ -49,4 +41,11 @@ public interface ProductStatisticsService { @@ -49,4 +41,11 @@ public interface ProductStatisticsService {
49 */ 41 */
50 List<ProductStatisticsDO> getProductStatisticsList(ProductStatisticsReqVO reqVO); 42 List<ProductStatisticsDO> getProductStatisticsList(ProductStatisticsReqVO reqVO);
51 43
  44 + /**
  45 + * 统计指定天数的商品数据
  46 + *
  47 + * @return 统计结果
  48 + */
  49 + String statisticsProduct(Integer days);
  50 +
52 } 51 }
53 \ No newline at end of file 52 \ No newline at end of file
yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/service/product/ProductStatisticsServiceImpl.java
1 package cn.iocoder.yudao.module.statistics.service.product; 1 package cn.iocoder.yudao.module.statistics.service.product;
2 2
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.date.DatePattern;
  5 +import cn.hutool.core.date.LocalDateTimeUtil;
3 import cn.hutool.core.util.ArrayUtil; 6 import cn.hutool.core.util.ArrayUtil;
4 import cn.hutool.core.util.ObjUtil; 7 import cn.hutool.core.util.ObjUtil;
5 import cn.iocoder.yudao.framework.common.pojo.PageResult; 8 import cn.iocoder.yudao.framework.common.pojo.PageResult;
@@ -10,13 +13,18 @@ import cn.iocoder.yudao.module.statistics.controller.admin.product.vo.ProductSta @@ -10,13 +13,18 @@ import cn.iocoder.yudao.module.statistics.controller.admin.product.vo.ProductSta
10 import cn.iocoder.yudao.module.statistics.controller.admin.product.vo.ProductStatisticsRespVO; 13 import cn.iocoder.yudao.module.statistics.controller.admin.product.vo.ProductStatisticsRespVO;
11 import cn.iocoder.yudao.module.statistics.dal.dataobject.product.ProductStatisticsDO; 14 import cn.iocoder.yudao.module.statistics.dal.dataobject.product.ProductStatisticsDO;
12 import cn.iocoder.yudao.module.statistics.dal.mysql.product.ProductStatisticsMapper; 15 import cn.iocoder.yudao.module.statistics.dal.mysql.product.ProductStatisticsMapper;
  16 +import com.baomidou.mybatisplus.core.metadata.IPage;
  17 +import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
13 import jakarta.annotation.Resource; 18 import jakarta.annotation.Resource;
14 import org.springframework.stereotype.Service; 19 import org.springframework.stereotype.Service;
  20 +import org.springframework.util.StopWatch;
15 import org.springframework.validation.annotation.Validated; 21 import org.springframework.validation.annotation.Validated;
16 22
17 import java.time.Duration; 23 import java.time.Duration;
18 import java.time.LocalDateTime; 24 import java.time.LocalDateTime;
19 import java.util.List; 25 import java.util.List;
  26 +import java.util.stream.Collectors;
  27 +import java.util.stream.IntStream;
20 28
21 29
22 /** 30 /**
@@ -31,17 +39,6 @@ public class ProductStatisticsServiceImpl implements ProductStatisticsService { @@ -31,17 +39,6 @@ public class ProductStatisticsServiceImpl implements ProductStatisticsService {
31 @Resource 39 @Resource
32 private ProductStatisticsMapper productStatisticsMapper; 40 private ProductStatisticsMapper productStatisticsMapper;
33 41
34 - @Override  
35 - public Long createProductStatistics(ProductStatisticsDO entity) {  
36 - // 计算 访客支付转化率(百分比)  
37 - if (entity.getBrowseUserCount() != null && ObjUtil.notEqual(entity.getBrowseUserCount(), 0)) {  
38 - entity.setBrowseConvertPercent(100 * entity.getOrderPayCount() / entity.getBrowseUserCount());  
39 - }  
40 - // 插入  
41 - productStatisticsMapper.insert(entity);  
42 - // 返回  
43 - return entity.getId();  
44 - }  
45 42
46 @Override 43 @Override
47 public PageResult<ProductStatisticsDO> getProductStatisticsRankPage(ProductStatisticsReqVO reqVO, SortablePageParam pageParam) { 44 public PageResult<ProductStatisticsDO> getProductStatisticsRankPage(ProductStatisticsReqVO reqVO, SortablePageParam pageParam) {
@@ -69,4 +66,58 @@ public class ProductStatisticsServiceImpl implements ProductStatisticsService { @@ -69,4 +66,58 @@ public class ProductStatisticsServiceImpl implements ProductStatisticsService {
69 return productStatisticsMapper.selectListByTimeBetween(reqVO); 66 return productStatisticsMapper.selectListByTimeBetween(reqVO);
70 } 67 }
71 68
  69 + @Override
  70 + public String statisticsProduct(Integer days) {
  71 + LocalDateTime today = LocalDateTime.now();
  72 + return IntStream.rangeClosed(1, days)
  73 + .mapToObj(day -> statisticsProduct(today.minusDays(day)))
  74 + .sorted()
  75 + .collect(Collectors.joining("\n"));
  76 + }
  77 +
  78 + /**
  79 + * 统计商品数据
  80 + *
  81 + * @param date 需要统计的日期
  82 + * @return 统计结果
  83 + */
  84 + private String statisticsProduct(LocalDateTime date) {
  85 + // 1. 处理统计时间范围
  86 + LocalDateTime beginTime = LocalDateTimeUtil.beginOfDay(date);
  87 + LocalDateTime endTime = LocalDateTimeUtil.endOfDay(date);
  88 + String dateStr = DatePattern.NORM_DATE_FORMATTER.format(date);
  89 + // 2. 检查该日是否已经统计过
  90 + Long count = productStatisticsMapper.selectCountByTimeBetween(beginTime, endTime);
  91 + if (count != null && count > 0) {
  92 + return dateStr + " 数据已存在,如果需要重新统计,请先删除对应的数据";
  93 + }
  94 +
  95 + // 3. 统计数据
  96 + StopWatch stopWatch = new StopWatch(dateStr);
  97 + stopWatch.start();
  98 +
  99 + // 分页统计,避免商品表数据较多时,出现超时问题
  100 + final int pageSize = 100;
  101 + for (int pageNo = 1; ; pageNo ++) {
  102 + IPage<ProductStatisticsDO> page = productStatisticsMapper.selectStatisticsResultPageByTimeBetween(
  103 + Page.of(pageNo, pageSize, false), beginTime, endTime);
  104 + if (CollUtil.isEmpty(page.getRecords())) {
  105 + break;
  106 + }
  107 +
  108 + for (ProductStatisticsDO record : page.getRecords()) {
  109 + record.setTime(date.toLocalDate());
  110 + // 计算 访客支付转化率(百分比)
  111 + if (record.getBrowseUserCount() != null && ObjUtil.notEqual(record.getBrowseUserCount(), 0)) {
  112 + record.setBrowseConvertPercent(100 * record.getOrderPayCount() / record.getBrowseUserCount());
  113 + }
  114 + }
  115 +
  116 + // 4. 插入数据
  117 + productStatisticsMapper.insertBatch(page.getRecords());
  118 + }
  119 +
  120 + return stopWatch.prettyPrint();
  121 + }
  122 +
72 } 123 }
73 \ No newline at end of file 124 \ No newline at end of file
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 { @@ -99,7 +99,7 @@ public class TradeStatisticsServiceImpl implements TradeStatisticsService {
99 // 1. 处理统计时间范围 99 // 1. 处理统计时间范围
100 LocalDateTime beginTime = LocalDateTimeUtil.beginOfDay(date); 100 LocalDateTime beginTime = LocalDateTimeUtil.beginOfDay(date);
101 LocalDateTime endTime = LocalDateTimeUtil.endOfDay(date); 101 LocalDateTime endTime = LocalDateTimeUtil.endOfDay(date);
102 - String dateStr = DatePattern.NORM_DATE_FORMAT.format(date); 102 + String dateStr = DatePattern.NORM_DATE_FORMATTER.format(date);
103 // 2. 检查该日是否已经统计过 103 // 2. 检查该日是否已经统计过
104 TradeStatisticsDO entity = tradeStatisticsMapper.selectByTimeBetween(beginTime, endTime); 104 TradeStatisticsDO entity = tradeStatisticsMapper.selectByTimeBetween(beginTime, endTime);
105 if (entity != null) { 105 if (entity != null) {
yudao-module-mall/yudao-module-statistics-biz/src/main/resources/mapper/product/ProductStatisticsMapper.xml 0 → 100644
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  3 +<mapper namespace="cn.iocoder.yudao.module.statistics.dal.mysql.product.ProductStatisticsMapper">
  4 +
  5 + <select id="selectStatisticsResultPageByTimeBetween"
  6 + resultType="cn.iocoder.yudao.module.statistics.dal.dataobject.product.ProductStatisticsDO">
  7 + SELECT spu.id AS spuId
  8 + -- 浏览量:一个用户可以有多次
  9 + , (SELECT COUNT(1)
  10 + FROM product_browse_history
  11 + WHERE spu_id = spu.id
  12 + AND create_time BETWEEN #{beginTime} AND #{endTime}) AS browse_count
  13 + -- 访客量:按用户去重计数
  14 + , (SELECT COUNT(DISTINCT user_id)
  15 + FROM product_browse_history
  16 + WHERE spu_id = spu.id
  17 + AND create_time BETWEEN #{beginTime} AND #{endTime}) AS browse_user_count
  18 + -- 收藏数量:按用户去重计数
  19 + , (SELECT COUNT(DISTINCT user_id)
  20 + FROM product_favorite
  21 + WHERE spu_id = spu.id
  22 + AND create_time BETWEEN #{beginTime} AND #{endTime}) AS favorite_count
  23 + -- 加购数量:按用户去重计数
  24 + , (SELECT COUNT(DISTINCT user_id)
  25 + FROM trade_cart
  26 + WHERE spu_id = spu.id
  27 + AND create_time BETWEEN #{beginTime} AND #{endTime}) AS cart_count
  28 + -- 下单件数
  29 + , (SELECT IFNULL(SUM(count), 0)
  30 + FROM trade_order_item
  31 + WHERE spu_id = spu.id
  32 + AND create_time BETWEEN #{beginTime} AND #{endTime}) AS order_count
  33 + -- 支付件数
  34 + , (SELECT IFNULL(SUM(item.count), 0)
  35 + FROM trade_order_item item
  36 + JOIN trade_order o ON item.order_id = o.id
  37 + WHERE spu_id = spu.id
  38 + AND o.pay_status = TRUE
  39 + AND item.create_time BETWEEN #{beginTime} AND #{endTime}) AS order_pay_count
  40 + -- 支付金额
  41 + , (SELECT IFNULL(SUM(item.pay_price), 0)
  42 + FROM trade_order_item item
  43 + JOIN trade_order o ON item.order_id = o.id
  44 + WHERE spu_id = spu.id
  45 + AND o.pay_status = TRUE
  46 + AND item.create_time BETWEEN #{beginTime} AND #{endTime}) AS order_pay_price
  47 + -- 退款件数
  48 + , (SELECT IFNULL(SUM(count), 0)
  49 + FROM trade_after_sale
  50 + WHERE spu_id = spu.id
  51 + AND refund_time IS NOT NULL
  52 + AND create_time BETWEEN #{beginTime} AND #{endTime}) AS after_sale_count
  53 + -- 退款金额
  54 + , (SELECT IFNULL(SUM(refund_price), 0)
  55 + FROM trade_after_sale
  56 + WHERE spu_id = spu.id
  57 + AND refund_time IS NOT NULL
  58 + AND create_time BETWEEN #{beginTime} AND #{endTime}) AS after_sale_refund_price
  59 + FROM product_spu spu
  60 + WHERE spu.deleted = FALSE
  61 + ORDER BY spu.id
  62 + </select>
  63 +
  64 +</mapper>