mirror of
https://gitee.com/huangge1199_admin/vue-pro.git
synced 2024-11-22 15:21:53 +08:00
统计:增加商品统计定时任务
This commit is contained in:
parent
f374e778bb
commit
0b7d42482f
@ -42,8 +42,8 @@ public interface ProductBrowseHistoryMapper extends BaseMapperX<ProductBrowseHis
|
||||
.eqIfPresent(ProductBrowseHistoryDO::getUserDeleted, userDeleted));
|
||||
}
|
||||
|
||||
default Page<ProductBrowseHistoryDO> selectPageByUserIdOrderByCreateTimeAsc(Long userId) {
|
||||
Page<ProductBrowseHistoryDO> page = Page.of(0, 1);
|
||||
default Page<ProductBrowseHistoryDO> selectPageByUserIdOrderByCreateTimeAsc(Long userId, Integer pageNo, Integer pageSize) {
|
||||
Page<ProductBrowseHistoryDO> page = Page.of(pageNo, pageSize);
|
||||
return selectPage(page, new LambdaQueryWrapperX<ProductBrowseHistoryDO>()
|
||||
.eqIfPresent(ProductBrowseHistoryDO::getUserId, userId)
|
||||
.orderByAsc(ProductBrowseHistoryDO::getCreateTime));
|
||||
|
@ -32,8 +32,8 @@ public class ProductBrowseHistoryServiceImpl implements ProductBrowseHistoryServ
|
||||
if (historyDO != null) {
|
||||
browseHistoryMapper.deleteById(historyDO);
|
||||
} else {
|
||||
// 情况二:限制每个用户的浏览记录的条数
|
||||
Page<ProductBrowseHistoryDO> pageResult = browseHistoryMapper.selectPageByUserIdOrderByCreateTimeAsc(userId);
|
||||
// 情况二:限制每个用户的浏览记录的条数(只查一条最早地记录、记录总数)
|
||||
Page<ProductBrowseHistoryDO> pageResult = browseHistoryMapper.selectPageByUserIdOrderByCreateTimeAsc(userId, 1, 1);
|
||||
if (pageResult.getTotal() >= USER_STORE_MAXIMUM) {
|
||||
// 删除最早的一条
|
||||
browseHistoryMapper.deleteById(CollUtil.getFirst(pageResult.getRecords()));
|
||||
|
@ -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;
|
||||
|
||||
}
|
@ -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;
|
||||
|
||||
}
|
@ -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<ProductStatisticsDO
|
||||
.selectAvg(ProductStatisticsDO::getBrowseConvertPercent);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据时间范围统计商品信息
|
||||
*
|
||||
* @param page 分页参数
|
||||
* @param beginTime 起始时间
|
||||
* @param endTime 截止时间
|
||||
* @return 统计
|
||||
*/
|
||||
IPage<ProductStatisticsDO> selectStatisticsResultPageByTimeBetween(IPage<ProductStatisticsDO> page,
|
||||
@Param("beginTime") LocalDateTime beginTime,
|
||||
@Param("endTime") LocalDateTime endTime);
|
||||
|
||||
default Long selectCountByTimeBetween(LocalDateTime beginTime, LocalDateTime endTime) {
|
||||
return selectCount(new LambdaQueryWrapperX<ProductStatisticsDO>().between(ProductStatisticsDO::getTime, beginTime, endTime));
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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<ProductStatisticsDO> getProductStatisticsList(ProductStatisticsReqVO reqVO);
|
||||
|
||||
/**
|
||||
* 统计指定天数的商品数据
|
||||
*
|
||||
* @return 统计结果
|
||||
*/
|
||||
String statisticsProduct(Integer days);
|
||||
|
||||
}
|
@ -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<ProductStatisticsDO> 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<ProductStatisticsDO> 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();
|
||||
}
|
||||
|
||||
}
|
@ -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) {
|
||||
|
@ -0,0 +1,64 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="cn.iocoder.yudao.module.statistics.dal.mysql.product.ProductStatisticsMapper">
|
||||
|
||||
<select id="selectStatisticsResultPageByTimeBetween"
|
||||
resultType="cn.iocoder.yudao.module.statistics.dal.dataobject.product.ProductStatisticsDO">
|
||||
SELECT spu.id AS spuId
|
||||
-- 浏览量:一个用户可以有多次
|
||||
, (SELECT COUNT(1)
|
||||
FROM product_browse_history
|
||||
WHERE spu_id = spu.id
|
||||
AND create_time BETWEEN #{beginTime} AND #{endTime}) AS browse_count
|
||||
-- 访客量:按用户去重计数
|
||||
, (SELECT COUNT(DISTINCT user_id)
|
||||
FROM product_browse_history
|
||||
WHERE spu_id = spu.id
|
||||
AND create_time BETWEEN #{beginTime} AND #{endTime}) AS browse_user_count
|
||||
-- 收藏数量:按用户去重计数
|
||||
, (SELECT COUNT(DISTINCT user_id)
|
||||
FROM product_favorite
|
||||
WHERE spu_id = spu.id
|
||||
AND create_time BETWEEN #{beginTime} AND #{endTime}) AS favorite_count
|
||||
-- 加购数量:按用户去重计数
|
||||
, (SELECT COUNT(DISTINCT user_id)
|
||||
FROM trade_cart
|
||||
WHERE spu_id = spu.id
|
||||
AND create_time BETWEEN #{beginTime} AND #{endTime}) AS cart_count
|
||||
-- 下单件数
|
||||
, (SELECT IFNULL(SUM(count), 0)
|
||||
FROM trade_order_item
|
||||
WHERE spu_id = spu.id
|
||||
AND create_time BETWEEN #{beginTime} AND #{endTime}) AS order_count
|
||||
-- 支付件数
|
||||
, (SELECT IFNULL(SUM(item.count), 0)
|
||||
FROM trade_order_item item
|
||||
JOIN trade_order o ON item.order_id = o.id
|
||||
WHERE spu_id = spu.id
|
||||
AND o.pay_status = TRUE
|
||||
AND item.create_time BETWEEN #{beginTime} AND #{endTime}) AS order_pay_count
|
||||
-- 支付金额
|
||||
, (SELECT IFNULL(SUM(item.pay_price), 0)
|
||||
FROM trade_order_item item
|
||||
JOIN trade_order o ON item.order_id = o.id
|
||||
WHERE spu_id = spu.id
|
||||
AND o.pay_status = TRUE
|
||||
AND item.create_time BETWEEN #{beginTime} AND #{endTime}) AS order_pay_price
|
||||
-- 退款件数
|
||||
, (SELECT IFNULL(SUM(count), 0)
|
||||
FROM trade_after_sale
|
||||
WHERE spu_id = spu.id
|
||||
AND refund_time IS NOT NULL
|
||||
AND create_time BETWEEN #{beginTime} AND #{endTime}) AS after_sale_count
|
||||
-- 退款金额
|
||||
, (SELECT IFNULL(SUM(refund_price), 0)
|
||||
FROM trade_after_sale
|
||||
WHERE spu_id = spu.id
|
||||
AND refund_time IS NOT NULL
|
||||
AND create_time BETWEEN #{beginTime} AND #{endTime}) AS after_sale_refund_price
|
||||
FROM product_spu spu
|
||||
WHERE spu.deleted = FALSE
|
||||
ORDER BY spu.id
|
||||
</select>
|
||||
|
||||
</mapper>
|
Loading…
Reference in New Issue
Block a user