first commit
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
# 统计中心模块 (ruoyi-mall-statistics)
|
||||
|
||||
## 模块简介
|
||||
|
||||
`ruoyi-mall-statistics` 是商城系统的统计中心模块,提供交易分析、会员分析等数据统计功能。
|
||||
|
||||
## 功能特性
|
||||
|
||||
### 1. 交易分析 ✅
|
||||
|
||||
- ✅ 订单统计(总订单数、已支付订单数)
|
||||
- ✅ 销售额统计(总销售额、已支付金额、退款金额)
|
||||
- ✅ 今日统计(今日订单数、今日销售额)
|
||||
- ✅ 商品销售排行(按销售金额排序)
|
||||
- ✅ 交易趋势分析(按日期统计)
|
||||
|
||||
### 2. 会员分析 ⏳
|
||||
|
||||
- ⏳ 会员增长统计(需会员模块支持)
|
||||
- ⏳ 会员消费统计(需会员模块支持)
|
||||
- ⏳ 会员活跃度分析(需会员模块支持)
|
||||
|
||||
### 3. 商品分析 ⏳
|
||||
|
||||
- ⏳ 商品销售统计(可通过交易统计的商品排行实现)
|
||||
- ⏳ 商品浏览统计(需商品模块支持浏览统计)
|
||||
- ⏳ 商品转化率分析(需商品模块支持)
|
||||
|
||||
### 4. 营销分析 ⏳
|
||||
|
||||
- ⏳ 活动效果统计(需营销模块支持)
|
||||
- ⏳ 优惠券使用统计(需优惠券模块支持)
|
||||
- ⏳ 营销ROI分析(需营销模块支持)
|
||||
|
||||
## 数据库表
|
||||
|
||||
统计模块主要基于现有业务表进行数据分析和统计,可根据需要创建统计汇总表。
|
||||
|
||||
## 模块结构
|
||||
|
||||
```
|
||||
org.dromara.mall.statistics
|
||||
├── controller
|
||||
│ ├── admin // 管理端API
|
||||
│ └── app // 用户端API(可选)
|
||||
├── domain
|
||||
│ ├── bo // 业务对象
|
||||
│ └── vo // 视图对象
|
||||
├── mapper // MyBatis Mapper
|
||||
├── service // 业务逻辑层
|
||||
│ └── impl
|
||||
├── enums // 枚举类
|
||||
└── config // 配置类
|
||||
```
|
||||
|
||||
## 依赖模块
|
||||
|
||||
- `ruoyi-mall-product` - 商品中心模块
|
||||
- `ruoyi-mall-trade` - 交易中心模块
|
||||
- `ruoyi-common-*` - 通用工具模块
|
||||
|
||||
## API接口
|
||||
|
||||
### 管理端接口
|
||||
|
||||
#### 统计数据 (`/mall/statistics`)
|
||||
|
||||
- `GET /mall/statistics/transaction` - 获取交易统计数据
|
||||
- 支持按日期范围查询(startDate, endDate)
|
||||
- 支持设置商品销售排行数量限制(limit,默认10)
|
||||
- 返回数据包括:总订单数、总销售额、已支付订单数、已支付金额、退款金额、今日统计、趋势数据、商品销售排行
|
||||
|
||||
- `GET /mall/statistics/member` - 获取会员统计数据
|
||||
- 支持按日期范围查询(startDate, endDate)
|
||||
- 返回数据包括:总会员数、今日新增会员数、活跃会员数、增长趋势、消费统计
|
||||
- ⚠️ 注意:会员统计功能需要会员模块支持,当前返回基础结构
|
||||
|
||||
## 待实现功能
|
||||
|
||||
- [x] 交易统计分析 ✅
|
||||
- [x] 订单统计 ✅
|
||||
- [x] 销售额统计 ✅
|
||||
- [x] 商品销售排行 ✅
|
||||
- [x] 交易趋势分析 ✅
|
||||
- [ ] 会员统计分析(需会员模块支持)
|
||||
- [ ] 商品统计分析(部分功能可通过交易统计实现)
|
||||
- [ ] 营销效果分析(需营销模块支持)
|
||||
- [ ] 数据报表导出
|
||||
|
||||
## 使用说明
|
||||
|
||||
### 交易统计
|
||||
|
||||
交易统计基于 `mall_order` 和 `mall_order_item` 表进行数据聚合分析:
|
||||
|
||||
1. **订单统计**:统计指定时间范围内的订单总数和总金额
|
||||
2. **已支付统计**:统计已支付(pay_status=1)的订单数和金额
|
||||
3. **退款统计**:统计已完成的退款金额(基于 `mall_after_sale` 表,status=5)
|
||||
4. **今日统计**:当未指定日期范围时,自动计算今日的订单和销售额
|
||||
5. **趋势数据**:按日期分组统计订单数和销售额
|
||||
6. **商品销售排行**:按销售金额排序,支持限制返回数量
|
||||
|
||||
### 查询参数
|
||||
|
||||
- `startDate`: 开始日期(格式:yyyy-MM-dd),可选
|
||||
- `endDate`: 结束日期(格式:yyyy-MM-dd),可选
|
||||
- `limit`: 商品销售排行数量限制,默认10,可选
|
||||
|
||||
**注意**:如果不指定日期范围,统计的是全部数据;如果指定了日期范围,则只统计该范围内的数据。
|
||||
@@ -0,0 +1,71 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-mall</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>ruoyi-mall-statistics</artifactId>
|
||||
|
||||
<description>
|
||||
ruoyi-mall-statistics 统计中心模块
|
||||
</description>
|
||||
|
||||
<dependencies>
|
||||
<!-- 通用工具-->
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-core</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-doc</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-mybatis</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-security</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-log</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-redis</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 商品中心模块 -->
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-mall-product</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 交易中心模块 -->
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-mall-trade</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
package org.dromara.mall.statistics.config;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* 统计中心自动配置类
|
||||
* 主要用于扫描统计中心模块的组件
|
||||
*
|
||||
* @author pengles
|
||||
*/
|
||||
@Slf4j
|
||||
@Configuration
|
||||
@ComponentScan(basePackages = "org.dromara.mall.statistics")
|
||||
@ConditionalOnProperty(prefix = "ruoyi.mall.statistics", name = "enabled", havingValue = "true", matchIfMissing = true)
|
||||
public class MallStatisticsAutoConfiguration {
|
||||
|
||||
public MallStatisticsAutoConfiguration() {
|
||||
log.info("统计中心模块已启用");
|
||||
}
|
||||
}
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
package org.dromara.mall.statistics.controller.admin;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.dromara.common.core.domain.R;
|
||||
import org.dromara.common.web.core.BaseController;
|
||||
import org.dromara.mall.statistics.domain.bo.StatisticsQueryBo;
|
||||
import org.dromara.mall.statistics.domain.vo.MemberStatisticsVo;
|
||||
import org.dromara.mall.statistics.domain.vo.TransactionStatisticsVo;
|
||||
import org.dromara.mall.statistics.service.MallStatisticsService;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
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.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 统计管理控制器
|
||||
*
|
||||
* @author pengles
|
||||
*/
|
||||
@Tag(name = "管理端-统计数据", description = "统计数据查询接口")
|
||||
@Validated
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("/mall/statistics")
|
||||
public class AdminMallStatisticsController extends BaseController {
|
||||
|
||||
private final MallStatisticsService statisticsService;
|
||||
|
||||
/**
|
||||
* 获取交易统计
|
||||
*/
|
||||
@Operation(summary = "获取交易统计", description = "获取交易统计数据,包括订单统计、销售额统计、商品销售排行等")
|
||||
@SaCheckPermission("mall:statistics:query")
|
||||
@GetMapping("/transaction")
|
||||
public R<TransactionStatisticsVo> getTransactionStatistics(
|
||||
@Parameter(description = "开始日期")
|
||||
@RequestParam(required = false)
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd") Date startDate,
|
||||
@Parameter(description = "结束日期")
|
||||
@RequestParam(required = false)
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd") Date endDate,
|
||||
@Parameter(description = "商品销售排行限制数量")
|
||||
@RequestParam(required = false, defaultValue = "10") Integer limit) {
|
||||
StatisticsQueryBo queryBo = new StatisticsQueryBo();
|
||||
queryBo.setStartDate(startDate);
|
||||
queryBo.setEndDate(endDate);
|
||||
queryBo.setLimit(limit);
|
||||
|
||||
TransactionStatisticsVo result = statisticsService.getTransactionStatistics(queryBo);
|
||||
return R.ok(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取会员统计
|
||||
*/
|
||||
@Operation(summary = "获取会员统计", description = "获取会员统计数据,包括会员增长、消费统计等")
|
||||
@SaCheckPermission("mall:statistics:query")
|
||||
@GetMapping("/member")
|
||||
public R<MemberStatisticsVo> getMemberStatistics(
|
||||
@Parameter(description = "开始日期")
|
||||
@RequestParam(required = false)
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd") Date startDate,
|
||||
@Parameter(description = "结束日期")
|
||||
@RequestParam(required = false)
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd") Date endDate) {
|
||||
StatisticsQueryBo queryBo = new StatisticsQueryBo();
|
||||
queryBo.setStartDate(startDate);
|
||||
queryBo.setEndDate(endDate);
|
||||
|
||||
MemberStatisticsVo result = statisticsService.getMemberStatistics(queryBo);
|
||||
return R.ok(result);
|
||||
}
|
||||
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
package org.dromara.mall.statistics.domain.bo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 统计查询业务对象
|
||||
*
|
||||
* @author pengles
|
||||
*/
|
||||
@Data
|
||||
public class StatisticsQueryBo implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 开始日期
|
||||
*/
|
||||
private Date startDate;
|
||||
|
||||
/**
|
||||
* 结束日期
|
||||
*/
|
||||
private Date endDate;
|
||||
|
||||
/**
|
||||
* 商品ID(可选)
|
||||
*/
|
||||
private Long productId;
|
||||
|
||||
/**
|
||||
* SKU ID(可选)
|
||||
*/
|
||||
private Long skuId;
|
||||
|
||||
/**
|
||||
* 用户ID(可选)
|
||||
*/
|
||||
private Long userId;
|
||||
|
||||
/**
|
||||
* 限制数量(用于排行)
|
||||
*/
|
||||
private Integer limit;
|
||||
|
||||
}
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
package org.dromara.mall.statistics.domain.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 会员统计视图对象
|
||||
*
|
||||
* @author pengles
|
||||
*/
|
||||
@Data
|
||||
public class MemberStatisticsVo implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 总会员数
|
||||
*/
|
||||
private Long totalMemberCount;
|
||||
|
||||
/**
|
||||
* 今日新增会员数
|
||||
*/
|
||||
private Long todayNewMemberCount;
|
||||
|
||||
/**
|
||||
* 活跃会员数(有订单的会员)
|
||||
*/
|
||||
private Long activeMemberCount;
|
||||
|
||||
/**
|
||||
* 会员增长趋势
|
||||
*/
|
||||
private List<DailyMemberGrowthVo> growthTrend;
|
||||
|
||||
/**
|
||||
* 会员消费统计
|
||||
*/
|
||||
private List<MemberConsumptionVo> consumptionStats;
|
||||
|
||||
/**
|
||||
* 每日会员增长
|
||||
*/
|
||||
@Data
|
||||
public static class DailyMemberGrowthVo implements Serializable {
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 日期
|
||||
*/
|
||||
private String date;
|
||||
|
||||
/**
|
||||
* 新增会员数
|
||||
*/
|
||||
private Long newMemberCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* 会员消费统计
|
||||
*/
|
||||
@Data
|
||||
public static class MemberConsumptionVo implements Serializable {
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
private Long userId;
|
||||
|
||||
/**
|
||||
* 订单数
|
||||
*/
|
||||
private Integer orderCount;
|
||||
|
||||
/**
|
||||
* 消费金额(分)
|
||||
*/
|
||||
private Long consumptionAmount;
|
||||
}
|
||||
|
||||
}
|
||||
+129
@@ -0,0 +1,129 @@
|
||||
package org.dromara.mall.statistics.domain.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 交易统计视图对象
|
||||
*
|
||||
* @author pengles
|
||||
*/
|
||||
@Data
|
||||
public class TransactionStatisticsVo implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 总订单数
|
||||
*/
|
||||
private Long totalOrderCount;
|
||||
|
||||
/**
|
||||
* 总销售额(分)
|
||||
*/
|
||||
private Long totalSalesAmount;
|
||||
|
||||
/**
|
||||
* 已支付订单数
|
||||
*/
|
||||
private Long paidOrderCount;
|
||||
|
||||
/**
|
||||
* 已支付金额(分)
|
||||
*/
|
||||
private Long paidAmount;
|
||||
|
||||
/**
|
||||
* 退款金额(分)
|
||||
*/
|
||||
private Long refundAmount;
|
||||
|
||||
/**
|
||||
* 今日订单数
|
||||
*/
|
||||
private Long todayOrderCount;
|
||||
|
||||
/**
|
||||
* 今日销售额(分)
|
||||
*/
|
||||
private Long todaySalesAmount;
|
||||
|
||||
/**
|
||||
* 趋势数据(按日期)
|
||||
*/
|
||||
private List<DailyStatisticsVo> trendData;
|
||||
|
||||
/**
|
||||
* 商品销售排行
|
||||
*/
|
||||
private List<ProductSalesRankVo> productSalesRank;
|
||||
|
||||
/**
|
||||
* 每日统计
|
||||
*/
|
||||
@Data
|
||||
public static class DailyStatisticsVo implements Serializable {
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 日期
|
||||
*/
|
||||
private String date;
|
||||
|
||||
/**
|
||||
* 订单数
|
||||
*/
|
||||
private Long orderCount;
|
||||
|
||||
/**
|
||||
* 销售额(分)
|
||||
*/
|
||||
private Long salesAmount;
|
||||
}
|
||||
|
||||
/**
|
||||
* 商品销售排行
|
||||
*/
|
||||
@Data
|
||||
public static class ProductSalesRankVo implements Serializable {
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 商品ID
|
||||
*/
|
||||
private Long productId;
|
||||
|
||||
/**
|
||||
* 商品名称
|
||||
*/
|
||||
private String productName;
|
||||
|
||||
/**
|
||||
* SKU ID
|
||||
*/
|
||||
private Long skuId;
|
||||
|
||||
/**
|
||||
* SKU名称
|
||||
*/
|
||||
private String skuName;
|
||||
|
||||
/**
|
||||
* 销售数量
|
||||
*/
|
||||
private Integer salesQuantity;
|
||||
|
||||
/**
|
||||
* 销售金额(分)
|
||||
*/
|
||||
private Long salesAmount;
|
||||
}
|
||||
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
package org.dromara.mall.statistics.mapper;
|
||||
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.dromara.mall.statistics.domain.vo.TransactionStatisticsVo;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 统计Mapper接口
|
||||
*
|
||||
* @author pengles
|
||||
*/
|
||||
@Mapper
|
||||
public interface MallStatisticsMapper {
|
||||
|
||||
/**
|
||||
* 统计订单总数和总金额
|
||||
*/
|
||||
Map<String, Object> countOrderStatistics(@Param("startDate") Date startDate,
|
||||
@Param("endDate") Date endDate);
|
||||
|
||||
/**
|
||||
* 统计已支付订单数和金额
|
||||
*/
|
||||
Map<String, Object> countPaidOrderStatistics(@Param("startDate") Date startDate,
|
||||
@Param("endDate") Date endDate);
|
||||
|
||||
/**
|
||||
* 统计退款金额
|
||||
*/
|
||||
Long sumRefundAmount(@Param("startDate") Date startDate,
|
||||
@Param("endDate") Date endDate);
|
||||
|
||||
/**
|
||||
* 统计每日订单数据
|
||||
*/
|
||||
List<TransactionStatisticsVo.DailyStatisticsVo> countDailyOrderStatistics(@Param("startDate") Date startDate,
|
||||
@Param("endDate") Date endDate);
|
||||
|
||||
/**
|
||||
* 统计商品销售排行
|
||||
*/
|
||||
List<TransactionStatisticsVo.ProductSalesRankVo> queryProductSalesRank(@Param("startDate") Date startDate,
|
||||
@Param("endDate") Date endDate,
|
||||
@Param("limit") Integer limit);
|
||||
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package org.dromara.mall.statistics.service;
|
||||
|
||||
import org.dromara.mall.statistics.domain.bo.StatisticsQueryBo;
|
||||
import org.dromara.mall.statistics.domain.vo.MemberStatisticsVo;
|
||||
import org.dromara.mall.statistics.domain.vo.TransactionStatisticsVo;
|
||||
|
||||
/**
|
||||
* 统计服务接口
|
||||
*
|
||||
* @author pengles
|
||||
*/
|
||||
public interface MallStatisticsService {
|
||||
|
||||
/**
|
||||
* 获取交易统计
|
||||
*
|
||||
* @param queryBo 查询条件
|
||||
* @return 交易统计结果
|
||||
*/
|
||||
TransactionStatisticsVo getTransactionStatistics(StatisticsQueryBo queryBo);
|
||||
|
||||
/**
|
||||
* 获取会员统计
|
||||
*
|
||||
* @param queryBo 查询条件
|
||||
* @return 会员统计结果
|
||||
*/
|
||||
MemberStatisticsVo getMemberStatistics(StatisticsQueryBo queryBo);
|
||||
|
||||
}
|
||||
+128
@@ -0,0 +1,128 @@
|
||||
package org.dromara.mall.statistics.service.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.mall.statistics.domain.bo.StatisticsQueryBo;
|
||||
import org.dromara.mall.statistics.domain.vo.MemberStatisticsVo;
|
||||
import org.dromara.mall.statistics.domain.vo.TransactionStatisticsVo;
|
||||
import org.dromara.mall.statistics.mapper.MallStatisticsMapper;
|
||||
import org.dromara.mall.statistics.service.MallStatisticsService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 统计服务实现类
|
||||
*
|
||||
* @author pengles
|
||||
*/
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
@Service
|
||||
public class MallStatisticsServiceImpl implements MallStatisticsService {
|
||||
|
||||
private final MallStatisticsMapper statisticsMapper;
|
||||
|
||||
/**
|
||||
* 获取交易统计
|
||||
*/
|
||||
@Override
|
||||
public TransactionStatisticsVo getTransactionStatistics(StatisticsQueryBo queryBo) {
|
||||
Date startDate = queryBo != null ? queryBo.getStartDate() : null;
|
||||
Date endDate = queryBo != null ? queryBo.getEndDate() : null;
|
||||
Integer limit = queryBo != null && queryBo.getLimit() != null ? queryBo.getLimit() : 10;
|
||||
|
||||
TransactionStatisticsVo vo = new TransactionStatisticsVo();
|
||||
|
||||
// 统计总订单数和总金额
|
||||
Map<String, Object> orderStats = statisticsMapper.countOrderStatistics(startDate, endDate);
|
||||
if (orderStats != null) {
|
||||
vo.setTotalOrderCount(getLongValue(orderStats, "totalOrderCount"));
|
||||
vo.setTotalSalesAmount(getLongValue(orderStats, "totalSalesAmount"));
|
||||
}
|
||||
|
||||
// 统计已支付订单数和金额
|
||||
Map<String, Object> paidStats = statisticsMapper.countPaidOrderStatistics(startDate, endDate);
|
||||
if (paidStats != null) {
|
||||
vo.setPaidOrderCount(getLongValue(paidStats, "paidOrderCount"));
|
||||
vo.setPaidAmount(getLongValue(paidStats, "paidAmount"));
|
||||
}
|
||||
|
||||
// 统计退款金额
|
||||
Long refundAmount = statisticsMapper.sumRefundAmount(startDate, endDate);
|
||||
vo.setRefundAmount(refundAmount != null ? refundAmount : 0L);
|
||||
|
||||
// 今日统计(如果没有指定日期范围,计算今日)
|
||||
if (startDate == null && endDate == null) {
|
||||
Date todayStart = DateUtil.beginOfDay(new Date());
|
||||
Date todayEnd = DateUtil.endOfDay(new Date());
|
||||
|
||||
Map<String, Object> todayOrderStats = statisticsMapper.countOrderStatistics(todayStart, todayEnd);
|
||||
if (todayOrderStats != null) {
|
||||
vo.setTodayOrderCount(getLongValue(todayOrderStats, "totalOrderCount"));
|
||||
vo.setTodaySalesAmount(getLongValue(todayOrderStats, "totalSalesAmount"));
|
||||
} else {
|
||||
vo.setTodayOrderCount(0L);
|
||||
vo.setTodaySalesAmount(0L);
|
||||
}
|
||||
} else {
|
||||
// 如果指定了日期范围,今日统计为0或null
|
||||
vo.setTodayOrderCount(0L);
|
||||
vo.setTodaySalesAmount(0L);
|
||||
}
|
||||
|
||||
// 趋势数据
|
||||
List<TransactionStatisticsVo.DailyStatisticsVo> trendData = statisticsMapper.countDailyOrderStatistics(startDate, endDate);
|
||||
vo.setTrendData(trendData != null ? trendData : CollUtil.newArrayList());
|
||||
|
||||
// 商品销售排行
|
||||
List<TransactionStatisticsVo.ProductSalesRankVo> productRank = statisticsMapper.queryProductSalesRank(startDate, endDate, limit);
|
||||
vo.setProductSalesRank(productRank != null ? productRank : CollUtil.newArrayList());
|
||||
|
||||
return vo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取会员统计
|
||||
*/
|
||||
@Override
|
||||
public MemberStatisticsVo getMemberStatistics(StatisticsQueryBo queryBo) {
|
||||
// TODO: 实现会员统计功能
|
||||
// 由于会员模块可能不在mall模块中,这里先返回基础结构
|
||||
MemberStatisticsVo vo = new MemberStatisticsVo();
|
||||
vo.setTotalMemberCount(0L);
|
||||
vo.setTodayNewMemberCount(0L);
|
||||
vo.setActiveMemberCount(0L);
|
||||
vo.setGrowthTrend(CollUtil.newArrayList());
|
||||
vo.setConsumptionStats(CollUtil.newArrayList());
|
||||
return vo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从Map中获取Long值
|
||||
*/
|
||||
private Long getLongValue(Map<String, Object> map, String key) {
|
||||
Object value = map.get(key);
|
||||
if (value == null) {
|
||||
return 0L;
|
||||
}
|
||||
if (value instanceof Long) {
|
||||
return (Long) value;
|
||||
}
|
||||
if (value instanceof Number) {
|
||||
return ((Number) value).longValue();
|
||||
}
|
||||
try {
|
||||
return Long.parseLong(value.toString());
|
||||
} catch (Exception e) {
|
||||
log.warn("无法转换值 {} 为Long类型: {}", value, e.getMessage());
|
||||
return 0L;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
# Auto Configure
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
org.dromara.mall.statistics.config.MallStatisticsAutoConfiguration
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
<?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="org.dromara.mall.statistics.mapper.MallStatisticsMapper">
|
||||
|
||||
<!-- 统计订单总数和总金额 -->
|
||||
<select id="countOrderStatistics" resultType="java.util.HashMap">
|
||||
SELECT
|
||||
COUNT(*) as totalOrderCount,
|
||||
IFNULL(SUM(total_amount), 0) as totalSalesAmount
|
||||
FROM mall_order
|
||||
<where>
|
||||
<if test="startDate != null">
|
||||
AND create_time >= #{startDate}
|
||||
</if>
|
||||
<if test="endDate != null">
|
||||
AND create_time <= #{endDate}
|
||||
</if>
|
||||
</where>
|
||||
</select>
|
||||
|
||||
<!-- 统计已支付订单数和金额 -->
|
||||
<select id="countPaidOrderStatistics" resultType="java.util.HashMap">
|
||||
SELECT
|
||||
COUNT(*) as paidOrderCount,
|
||||
IFNULL(SUM(pay_amount), 0) as paidAmount
|
||||
FROM mall_order
|
||||
<where>
|
||||
pay_status = 1
|
||||
<if test="startDate != null">
|
||||
AND create_time >= #{startDate}
|
||||
</if>
|
||||
<if test="endDate != null">
|
||||
AND create_time <= #{endDate}
|
||||
</if>
|
||||
</where>
|
||||
</select>
|
||||
|
||||
<!-- 统计退款金额 -->
|
||||
<select id="sumRefundAmount" resultType="java.lang.Long">
|
||||
SELECT IFNULL(SUM(refund_amount), 0)
|
||||
FROM mall_after_sale
|
||||
<where>
|
||||
status = 5
|
||||
<if test="startDate != null">
|
||||
AND refund_time >= #{startDate}
|
||||
</if>
|
||||
<if test="endDate != null">
|
||||
AND refund_time <= #{endDate}
|
||||
</if>
|
||||
</where>
|
||||
</select>
|
||||
|
||||
<!-- 统计每日订单数据 -->
|
||||
<select id="countDailyOrderStatistics" resultType="org.dromara.mall.statistics.domain.vo.TransactionStatisticsVo$DailyStatisticsVo">
|
||||
SELECT
|
||||
DATE_FORMAT(create_time, '%Y-%m-%d') as date,
|
||||
COUNT(*) as orderCount,
|
||||
IFNULL(SUM(total_amount), 0) as salesAmount
|
||||
FROM mall_order
|
||||
<where>
|
||||
<if test="startDate != null">
|
||||
AND create_time >= #{startDate}
|
||||
</if>
|
||||
<if test="endDate != null">
|
||||
AND create_time <= #{endDate}
|
||||
</if>
|
||||
</where>
|
||||
GROUP BY DATE_FORMAT(create_time, '%Y-%m-%d')
|
||||
ORDER BY date ASC
|
||||
</select>
|
||||
|
||||
<!-- 统计商品销售排行 -->
|
||||
<select id="queryProductSalesRank" resultType="org.dromara.mall.statistics.domain.vo.TransactionStatisticsVo$ProductSalesRankVo">
|
||||
SELECT
|
||||
oi.product_id as productId,
|
||||
oi.product_name as productName,
|
||||
oi.sku_id as skuId,
|
||||
oi.sku_name as skuName,
|
||||
SUM(oi.quantity) as salesQuantity,
|
||||
IFNULL(SUM(oi.total_amount), 0) as salesAmount
|
||||
FROM mall_order_item oi
|
||||
INNER JOIN mall_order o ON oi.order_id = o.id
|
||||
<where>
|
||||
o.pay_status = 1
|
||||
<if test="startDate != null">
|
||||
AND o.create_time >= #{startDate}
|
||||
</if>
|
||||
<if test="endDate != null">
|
||||
AND o.create_time <= #{endDate}
|
||||
</if>
|
||||
</where>
|
||||
GROUP BY oi.product_id, oi.product_name, oi.sku_id, oi.sku_name
|
||||
ORDER BY salesAmount DESC
|
||||
<if test="limit != null and limit > 0">
|
||||
LIMIT #{limit}
|
||||
</if>
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
Reference in New Issue
Block a user