statistics:初始化

pull/58/head
YunaiV 2023-10-23 19:40:29 +08:00
parent e26b29c5eb
commit 97103a8846
85 changed files with 3873 additions and 3 deletions

View File

@ -7,12 +7,16 @@ import cn.hutool.core.text.csv.CsvUtil;
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
import cn.iocoder.yudao.framework.ip.core.Area;
import cn.iocoder.yudao.framework.ip.core.enums.AreaTypeEnum;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
/**
*
@ -116,4 +120,43 @@ public class AreaUtils {
return sb.toString();
}
/**
*
*
* @param type
* @param func
* @param <T>
* @return
*/
public static <T> List<T> getByType(AreaTypeEnum type, Function<Area, T> func) {
return convertList(areas.values(), func, area -> type.getType().equals(area.getType()));
}
/**
*
*
* @param id
* @param type
* @return
*/
public static Integer getParentIdByType(Integer id, @NonNull AreaTypeEnum type) {
for (int i = 0; i < Byte.MAX_VALUE; i++) {
Area area = AreaUtils.getArea(id);
if (area == null) {
return null;
}
// 情况一:匹配到,返回它
if (type.getType().equals(area.getType())) {
return area.getId();
}
// 情况二:找到根节点,返回空
if (area.getParent() == null || area.getParent().getId() == null) {
return null;
}
// 其它:继续向上查找
id = area.getParent().getId();
}
return null;
}
}

View File

@ -24,8 +24,8 @@
<module>yudao-module-product-biz</module>
<module>yudao-module-trade-api</module>
<module>yudao-module-trade-biz</module>
<!-- <module>yudao-module-statistics-api</module>-->
<!-- <module>yudao-module-statistics-biz</module>-->
<module>yudao-module-statistics-api</module>
<module>yudao-module-statistics-biz</module>
</modules>
</project>

View File

@ -0,0 +1,47 @@
<?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>cn.iocoder.cloud</groupId>
<artifactId>yudao-module-mall</artifactId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>yudao-module-statistics-api</artifactId>
<packaging>jar</packaging>
<name>${project.artifactId}</name>
<description>
statistics 模块 API暴露给其它模块调用
</description>
<dependencies>
<dependency>
<groupId>cn.iocoder.cloud</groupId>
<artifactId>yudao-common</artifactId>
</dependency>
<!-- Web 相关 -->
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<scope>provided</scope>
</dependency>
<!-- 参数校验 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
<optional>true</optional>
</dependency>
<!-- RPC 远程调用相关 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,4 @@
/**
* TODO
*/
package cn.iocoder.yudao.module.statistics.api;

View File

@ -0,0 +1,48 @@
package cn.iocoder.yudao.module.statistics.enums;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
/**
*
*
* @author owen
*/
@AllArgsConstructor
@Getter
public enum TimeRangeTypeEnum implements IntArrayValuable {
/**
*
*/
DAY(1),
/**
*
*/
WEEK(7),
/**
*
*/
MONTH(30),
/**
*
*/
YEAR(365),
;
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(TimeRangeTypeEnum::getType).toArray();
/**
*
*/
private final Integer type;
@Override
public int[] array() {
return ARRAYS;
}
}

View File

@ -0,0 +1,4 @@
/**
* TODO
*/
package cn.iocoder.yudao.module.statistics.enums;

View File

@ -0,0 +1,141 @@
<?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>cn.iocoder.cloud</groupId>
<artifactId>yudao-module-mall</artifactId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>yudao-module-statistics-biz</artifactId>
<packaging>jar</packaging>
<name>${project.artifactId}</name>
<description>
statistics 模块,主要实现统计相关功能
例如:统计商品、会员、交易等功能。
</description>
<dependencies>
<!-- Spring Cloud 基础 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
<dependency>
<groupId>cn.iocoder.cloud</groupId>
<artifactId>yudao-spring-boot-starter-env</artifactId>
</dependency>
<!-- 依赖服务 -->
<dependency>
<groupId>cn.iocoder.cloud</groupId>
<artifactId>yudao-module-statistics-api</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>cn.iocoder.cloud</groupId>
<artifactId>yudao-module-promotion-api</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>cn.iocoder.cloud</groupId>
<artifactId>yudao-module-product-api</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>cn.iocoder.cloud</groupId>
<artifactId>yudao-module-trade-api</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>cn.iocoder.cloud</groupId>
<artifactId>yudao-module-member-api</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>cn.iocoder.cloud</groupId>
<artifactId>yudao-module-pay-api</artifactId>
<version>${revision}</version>
</dependency>
<!-- 业务组件 -->
<dependency>
<groupId>cn.iocoder.cloud</groupId>
<artifactId>yudao-spring-boot-starter-biz-operatelog</artifactId>
</dependency>
<dependency>
<groupId>cn.iocoder.cloud</groupId>
<artifactId>yudao-spring-boot-starter-biz-tenant</artifactId>
</dependency>
<dependency>
<groupId>cn.iocoder.cloud</groupId>
<artifactId>yudao-spring-boot-starter-biz-ip</artifactId>
</dependency>
<!-- Web 相关 -->
<dependency>
<groupId>cn.iocoder.cloud</groupId>
<artifactId>yudao-spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>cn.iocoder.cloud</groupId>
<artifactId>yudao-spring-boot-starter-security</artifactId>
</dependency>
<!-- DB 相关 -->
<dependency>
<groupId>cn.iocoder.cloud</groupId>
<artifactId>yudao-spring-boot-starter-mybatis</artifactId>
</dependency>
<!-- RPC 远程调用相关 -->
<dependency>
<groupId>cn.iocoder.cloud</groupId>
<artifactId>yudao-spring-boot-starter-rpc</artifactId>
</dependency>
<!-- Registry 注册中心相关 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- Config 配置中心相关 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!-- Job 定时任务相关 -->
<dependency>
<groupId>cn.iocoder.cloud</groupId>
<artifactId>yudao-spring-boot-starter-job</artifactId>
</dependency>
<!-- Test 测试相关 -->
<dependency>
<groupId>cn.iocoder.cloud</groupId>
<artifactId>yudao-spring-boot-starter-test</artifactId>
</dependency>
<!-- 工具类相关 -->
<dependency>
<groupId>cn.iocoder.cloud</groupId>
<artifactId>yudao-spring-boot-starter-excel</artifactId>
</dependency>
<dependency>
<groupId>cn.iocoder.cloud</groupId>
<artifactId>yudao-spring-boot-starter-biz-dict</artifactId>
</dependency>
<!-- 监控相关 -->
<dependency>
<groupId>cn.iocoder.cloud</groupId>
<artifactId>yudao-spring-boot-starter-monitor</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,30 @@
package cn.iocoder.yudao.module.statistics;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
*
*
* https://cloud.iocoder.cn/quick-start/ 文章
* https://cloud.iocoder.cn/quick-start/ 文章
* https://cloud.iocoder.cn/quick-start/ 文章
*
* @author
*/
@SpringBootApplication
public class StatisticsServerApplication {
public static void main(String[] args) {
// 如果你碰到启动的问题,请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章
// 如果你碰到启动的问题,请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章
// 如果你碰到启动的问题,请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章
SpringApplication.run(StatisticsServerApplication.class, args);
// 如果你碰到启动的问题,请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章
// 如果你碰到启动的问题,请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章
// 如果你碰到启动的问题,请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章
}
}

View File

@ -0,0 +1,20 @@
package cn.iocoder.yudao.module.statistics.controller.admin.common.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Schema(description = "管理后台 - 数据对照 Response VO")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class DataComparisonRespVO<T> {
@Schema(description = "当前数据", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private T value;
@Schema(description = "参照数据", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private T reference;
}

View File

@ -0,0 +1,121 @@
package cn.iocoder.yudao.module.statistics.controller.admin.member;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.NumberUtil;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.module.statistics.controller.admin.common.vo.DataComparisonRespVO;
import cn.iocoder.yudao.module.statistics.controller.admin.member.vo.*;
import cn.iocoder.yudao.module.statistics.convert.member.MemberStatisticsConvert;
import cn.iocoder.yudao.module.statistics.service.infra.ApiAccessLogStatisticsService;
import cn.iocoder.yudao.module.statistics.service.member.MemberStatisticsService;
import cn.iocoder.yudao.module.statistics.service.trade.TradeOrderStatisticsService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.access.prepost.PreAuthorize;
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.RestController;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.List;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - 会员统计")
@RestController
@RequestMapping("/statistics/member")
@Validated
@Slf4j
public class MemberStatisticsController {
@Resource
private MemberStatisticsService memberStatisticsService;
@Resource
private TradeOrderStatisticsService tradeOrderStatisticsService;
@Resource
private ApiAccessLogStatisticsService apiAccessLogStatisticsService;
// TODO 芋艿:已经 review
@GetMapping("/summary")
@Operation(summary = "获得会员统计(实时统计)")
@PreAuthorize("@ss.hasPermission('statistics:member:query')")
public CommonResult<MemberSummaryRespVO> getMemberSummary() {
return success(memberStatisticsService.getMemberSummary());
}
// TODO 芋艿:已经 review
@GetMapping("/analyse")
@Operation(summary = "获得会员分析数据")
@PreAuthorize("@ss.hasPermission('statistics:member:query')")
public CommonResult<MemberAnalyseRespVO> getMemberAnalyse(MemberAnalyseReqVO reqVO) {
// 1. 查询数据
LocalDateTime beginTime = ArrayUtil.get(reqVO.getTimes(), 0);
LocalDateTime endTime = ArrayUtil.get(reqVO.getTimes(), 1);
// 1.1 查询分析对照数据
DataComparisonRespVO<MemberAnalyseDataRespVO> comparisonData = memberStatisticsService.getMemberAnalyseComparisonData(beginTime, endTime);
// TODO @疯狂:这个可能有点特殊,要按照 create_time 来查询;不然它的漏斗就不统一;因为是访问数量 > 今日下单人 > 今日支付人;是一个统一的维度;
// 1.2 查询成交用户数量
Integer payUserCount = tradeOrderStatisticsService.getPayUserCount(beginTime, endTime);
// 1.3 计算客单价
int atv = 0;
if (payUserCount != null && payUserCount > 0) {
// TODO @疯狂:类似上面的 payUserCount
Integer payPrice = tradeOrderStatisticsService.getOrderPayPrice(beginTime, endTime);
atv = NumberUtil.div(payPrice, payUserCount).intValue();
}
// 1.4 查询访客数量
Integer visitUserCount = apiAccessLogStatisticsService.getIpCount(UserTypeEnum.MEMBER.getValue(), beginTime, endTime);
// 1.5 下单用户数量
Integer orderUserCount = tradeOrderStatisticsService.getOrderUserCount(beginTime, endTime);
// 2. 拼接返回
return success(MemberStatisticsConvert.INSTANCE.convert(visitUserCount, orderUserCount, payUserCount, atv, comparisonData));
}
// TODO 芋艿:已经 review
@GetMapping("/area-statistics-list")
@Operation(summary = "按照省份,获得会员统计列表")
@PreAuthorize("@ss.hasPermission('statistics:member:query')")
public CommonResult<List<MemberAreaStatisticsRespVO>> getMemberAreaStatisticsList() {
return success(memberStatisticsService.getMemberAreaStatisticsList());
}
// TODO 芋艿:已经 review
@GetMapping("/sex-statistics-list")
@Operation(summary = "按照性别,获得会员统计列表")
@PreAuthorize("@ss.hasPermission('statistics:member:query')")
public CommonResult<List<MemberSexStatisticsRespVO>> getMemberSexStatisticsList() {
return success(memberStatisticsService.getMemberSexStatisticsList());
}
// TODO 芋艿:已经 review
@GetMapping("/terminal-statistics-list")
@Operation(summary = "按照终端,获得会员统计列表")
@PreAuthorize("@ss.hasPermission('statistics:member:query')")
public CommonResult<List<MemberTerminalStatisticsRespVO>> getMemberTerminalStatisticsList() {
return success(memberStatisticsService.getMemberTerminalStatisticsList());
}
// TODO 芋艿:已经 review
// TODO @疯狂:要注意 date 的排序;
@GetMapping("/user-count-comparison")
@Operation(summary = "获得用户数量对照")
@PreAuthorize("@ss.hasPermission('statistics:member:query')")
public CommonResult<DataComparisonRespVO<MemberCountRespVO>> getUserCountComparison() {
return success(memberStatisticsService.getUserCountComparison());
}
// TODO 芋艿:已经 review
@GetMapping("/register-count-list")
@Operation(summary = "获得会员注册数量列表")
@PreAuthorize("@ss.hasPermission('statistics:member:query')")
public CommonResult<List<MemberRegisterCountRespVO>> getMemberRegisterCountList(MemberAnalyseReqVO reqVO) {
return success(memberStatisticsService.getMemberRegisterCountList(
ArrayUtil.get(reqVO.getTimes(), 0), ArrayUtil.get(reqVO.getTimes(), 1)));
}
}

View File

@ -0,0 +1,19 @@
package cn.iocoder.yudao.module.statistics.controller.admin.member.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "管理后台 - 会员分析数据 Response VO")
@Data
public class MemberAnalyseDataRespVO {
@Schema(description = "会员数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Integer registerUserCount;
@Schema(description = "活跃用户数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Integer visitUserCount;
@Schema(description = "充值会员数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "221")
private Integer rechargeUserCount;
}

View File

@ -0,0 +1,19 @@
package cn.iocoder.yudao.module.statistics.controller.admin.member.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 会员分析 Request VO")
@Data
public class MemberAnalyseReqVO {
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
@Schema(description = "时间范围")
private LocalDateTime[] times;
}

View File

@ -0,0 +1,26 @@
package cn.iocoder.yudao.module.statistics.controller.admin.member.vo;
import cn.iocoder.yudao.module.statistics.controller.admin.common.vo.DataComparisonRespVO;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "管理后台 - 会员分析 Response VO")
@Data
public class MemberAnalyseRespVO {
@Schema(description = "访客数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Integer visitUserCount;
@Schema(description = "下单用户数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Integer orderUserCount;
@Schema(description = "成交用户数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Integer payUserCount;
@Schema(description = "客单价,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Integer atv;
@Schema(description = "对照数据", requiredMode = Schema.RequiredMode.REQUIRED)
private DataComparisonRespVO<MemberAnalyseDataRespVO> comparison;
}

View File

@ -0,0 +1,26 @@
package cn.iocoder.yudao.module.statistics.controller.admin.member.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "管理后台 - 会员地区统计 Response VO")
@Data
public class MemberAreaStatisticsRespVO {
@Schema(description = "省份编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer areaId;
@Schema(description = "省份名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "浙江省")
private String areaName;
@Schema(description = "会员数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Integer userCount;
@Schema(description = "下单的会员数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Integer orderCreateUserCount;
@Schema(description = "支付订单的会员数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "512")
private Integer orderPayUserCount;
@Schema(description = "订单支付金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "622")
private Integer orderPayPrice;
}

View File

@ -0,0 +1,16 @@
package cn.iocoder.yudao.module.statistics.controller.admin.member.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "管理后台 - 会员数量统计 Response VO")
@Data
public class MemberCountRespVO {
@Schema(description = "用户访问量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Integer visitUserCount;
@Schema(description = "注册用户数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Integer registerUserCount;
}

View File

@ -0,0 +1,23 @@
package cn.iocoder.yudao.module.statistics.controller.admin.member.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDate;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.TIME_ZONE_DEFAULT;
@Schema(description = "管理后台 - 会员注册数量 Response VO")
@Data
public class MemberRegisterCountRespVO {
@JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY, timezone = TIME_ZONE_DEFAULT)
@Schema(description = "日期", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private LocalDate date;
@Schema(description = "数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Integer count;
}

View File

@ -0,0 +1,17 @@
package cn.iocoder.yudao.module.statistics.controller.admin.member.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "管理后台 - 会员性别统计 Response VO")
@Data
public class MemberSexStatisticsRespVO {
@Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer sex;
// TODO @疯狂:要不还是其它字段,我们也补全,这样方便使用的用户,做定制化;就保持和 MemberAreaStatisticsRespVO 一致;
@Schema(description = "会员数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Integer userCount;
}

View File

@ -0,0 +1,23 @@
package cn.iocoder.yudao.module.statistics.controller.admin.member.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "管理后台 - 会员统计 Response VO")
@Data
public class MemberSummaryRespVO {
@Schema(description = "会员数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Integer userCount;
@Schema(description = "充值会员数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "221")
private Integer rechargeUserCount;
@Schema(description = "充值金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Integer rechargePrice;
// TODO @疯狂要不干脆这个字段改成orderPayPrice
@Schema(description = "支出金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Integer expensePrice; // 只计算 mall 交易订单的支付金额,不考虑退款
}

View File

@ -0,0 +1,17 @@
package cn.iocoder.yudao.module.statistics.controller.admin.member.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "管理后台 - 会员终端统计 Response VO")
@Data
public class MemberTerminalStatisticsRespVO {
@Schema(description = "终端", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer terminal;
// TODO @疯狂:要不 orderCreateUserCount 和 orderPayUserCount 貌似更统一一些;
@Schema(description = "会员数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Integer userCount;
}

View File

@ -0,0 +1,36 @@
package cn.iocoder.yudao.module.statistics.controller.admin.pay;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.module.statistics.controller.admin.pay.vo.PaySummaryRespVO;
import cn.iocoder.yudao.module.statistics.convert.pay.PayStatisticsConvert;
import cn.iocoder.yudao.module.statistics.service.pay.PayWalletStatisticsService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
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.RestController;
import javax.annotation.Resource;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - 支付统计")
@RestController
@RequestMapping("/statistics/pay")
@Validated
@Slf4j
public class PayStatisticsController {
@Resource
private PayWalletStatisticsService payWalletStatisticsService;
@GetMapping("/summary")
@Operation(summary = "获取充值金额")
public CommonResult<PaySummaryRespVO> getWalletRechargePrice() {
Integer rechargePrice = payWalletStatisticsService.getRechargePriceSummary();
return success(PayStatisticsConvert.INSTANCE.convert(rechargePrice));
}
}

View File

@ -0,0 +1,13 @@
package cn.iocoder.yudao.module.statistics.controller.admin.pay.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "管理后台 - 支付统计 Response VO")
@Data
public class PaySummaryRespVO {
@Schema(description = "充值金额,单位分", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Integer rechargePrice;
}

View File

@ -0,0 +1,41 @@
package cn.iocoder.yudao.module.statistics.controller.admin.product;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.statistics.dal.mysql.product.ProductSpuStatisticsDO;
import cn.iocoder.yudao.module.statistics.dal.mysql.product.ProductStatisticsDO;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.time.LocalDateTime;
import java.util.List;
@Tag(name = "管理后台 - 商品统计")
@RestController
@RequestMapping("/statistics/product")
@Validated
@Slf4j
public class ProductStatisticsController {
// TODO @麦子:返回 ProductStatisticsComparisonResp 里面有两个字段,一个是选择的时间范围的合计结果,一个是对比的时间范围的合计结果;
// 例如说,选择时间范围是 2023-10-01 ~ 2023-10-02那么对比就是 2023-09-30再倒推 2 天;
public CommonResult<Object> getProductStatisticsComparison() {
return null;
}
// TODO @麦子查询指定时间范围内的商品统计数据DO 到时需要改成 VO 哈
public CommonResult<List<ProductStatisticsDO>> getProductStatisticsList(
LocalDateTime[] times) {
return null;
}
// TODO @麦子:查询指定时间范围内的商品 SPU 统计数据DO 到时需要改成 VO 哈
// 入参是分页参数 + 时间范围 + 排序字段
public CommonResult<PageResult<ProductSpuStatisticsDO>> getProductSpuStatisticsPage() {
return null;
}
}

View File

@ -0,0 +1,138 @@
package cn.iocoder.yudao.module.statistics.controller.admin.trade;
import cn.hutool.core.util.ArrayUtil;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.module.statistics.controller.admin.common.vo.DataComparisonRespVO;
import cn.iocoder.yudao.module.statistics.controller.admin.trade.vo.*;
import cn.iocoder.yudao.module.statistics.convert.trade.TradeStatisticsConvert;
import cn.iocoder.yudao.module.statistics.dal.dataobject.trade.TradeStatisticsDO;
import cn.iocoder.yudao.module.statistics.service.trade.AfterSaleStatisticsService;
import cn.iocoder.yudao.module.statistics.service.trade.BrokerageStatisticsService;
import cn.iocoder.yudao.module.statistics.service.trade.TradeOrderStatisticsService;
import cn.iocoder.yudao.module.statistics.service.trade.TradeStatisticsService;
import cn.iocoder.yudao.module.statistics.service.trade.bo.TradeSummaryRespBO;
import cn.iocoder.yudao.module.trade.enums.aftersale.AfterSaleStatusEnum;
import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageWithdrawStatusEnum;
import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum;
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.access.prepost.PreAuthorize;
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.RestController;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.IOException;
import java.util.List;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - 交易统计")
@RestController
@RequestMapping("/statistics/trade")
@Validated
@Slf4j
public class TradeStatisticsController {
@Resource
private TradeStatisticsService tradeStatisticsService;
@Resource
private TradeOrderStatisticsService tradeOrderStatisticsService;
@Resource
private AfterSaleStatisticsService afterSaleStatisticsService;
@Resource
private BrokerageStatisticsService brokerageStatisticsService;
// TODO 芋艿:已经 review
@GetMapping("/summary")
@Operation(summary = "获得交易统计")
@PreAuthorize("@ss.hasPermission('statistics:trade:query')")
public CommonResult<DataComparisonRespVO<TradeSummaryRespVO>> getTradeSummaryComparison() {
// 1.1 昨天的数据
TradeSummaryRespBO yesterdayData = tradeStatisticsService.getTradeSummaryByDays(-1);
// 1.2 前天的数据(用于对照昨天的数据)
TradeSummaryRespBO beforeYesterdayData = tradeStatisticsService.getTradeSummaryByDays(-2);
// 2.1 本月数据
TradeSummaryRespBO monthData = tradeStatisticsService.getTradeSummaryByMonths(0);
// 2.2 上月数据(用于对照本月的数据)
TradeSummaryRespBO lastMonthData = tradeStatisticsService.getTradeSummaryByMonths(-1);
// 拼接数据
return success(TradeStatisticsConvert.INSTANCE.convert(yesterdayData, beforeYesterdayData, monthData, lastMonthData));
}
// TODO @疯狂:【晚点再改和讨论;等首页的接口出来】这个要不还是叫 analyse对比选中的时间段和上一个时间段类似 MemberStatisticsController 的 getMemberAnalyse
@GetMapping("/trend/summary")
@Operation(summary = "获得交易状况统计")
@PreAuthorize("@ss.hasPermission('statistics:trade:query')")
public CommonResult<DataComparisonRespVO<TradeTrendSummaryRespVO>> getTradeTrendSummaryComparison(
TradeTrendReqVO reqVO) {
return success(tradeStatisticsService.getTradeTrendSummaryComparison(ArrayUtil.get(reqVO.getTimes(), 0),
ArrayUtil.get(reqVO.getTimes(), 1)));
}
// TODO 芋艿:已经 review
@GetMapping("/list")
@Operation(summary = "获得交易状况明细")
@PreAuthorize("@ss.hasPermission('statistics:trade:query')")
public CommonResult<List<TradeTrendSummaryRespVO>> getTradeStatisticsList(TradeTrendReqVO reqVO) {
List<TradeStatisticsDO> list = tradeStatisticsService.getTradeStatisticsList(ArrayUtil.get(reqVO.getTimes(), 0),
ArrayUtil.get(reqVO.getTimes(), 1));
return success(TradeStatisticsConvert.INSTANCE.convertList(list));
}
// TODO 芋艿:已经 review
@GetMapping("/export-excel")
@Operation(summary = "导出获得交易状况明细 Excel")
@PreAuthorize("@ss.hasPermission('statistics:trade:export')")
public void exportTradeStatisticsExcel(TradeTrendReqVO reqVO, HttpServletResponse response) throws IOException {
List<TradeStatisticsDO> list = tradeStatisticsService.getTradeStatisticsList(ArrayUtil.get(reqVO.getTimes(), 0),
ArrayUtil.get(reqVO.getTimes(), 1));
// 导出 Excel
List<TradeTrendSummaryRespVO> voList = TradeStatisticsConvert.INSTANCE.convertList(list);
List<TradeTrendSummaryExcelVO> data = TradeStatisticsConvert.INSTANCE.convertList02(voList);
ExcelUtils.write(response, "交易状况.xls", "数据", TradeTrendSummaryExcelVO.class, data);
}
// TODO 芋艿:已经 review
@GetMapping("/order-count")
@Operation(summary = "获得交易订单数量")
@PreAuthorize("@ss.hasPermission('statistics:trade:query')")
public CommonResult<TradeOrderCountRespVO> getOrderCount() {
// 订单统计
Long undeliveredCount = tradeOrderStatisticsService.getCountByStatusAndDeliveryType(
TradeOrderStatusEnum.UNDELIVERED.getStatus(), DeliveryTypeEnum.EXPRESS.getType());
// TODO @疯狂:订单支付后,如果是门店自提的,需要 update 成 DELIVERED目前还没搞~~突然反应过来
Long pickUpCount = tradeOrderStatisticsService.getCountByStatusAndDeliveryType(
TradeOrderStatusEnum.DELIVERED.getStatus(), DeliveryTypeEnum.PICK_UP.getType());
// 售后统计
Long afterSaleApplyCount = afterSaleStatisticsService.getCountByStatus(AfterSaleStatusEnum.APPLY);
Long auditingWithdrawCount = brokerageStatisticsService.getWithdrawCountByStatus(BrokerageWithdrawStatusEnum.AUDITING);
// 拼接返回
return success(TradeStatisticsConvert.INSTANCE.convert(undeliveredCount, pickUpCount, afterSaleApplyCount, auditingWithdrawCount));
}
// TODO 芋艿:已经 review
@GetMapping("/order-comparison")
@Operation(summary = "获得交易订单数量")
@PreAuthorize("@ss.hasPermission('statistics:trade:query')")
public CommonResult<DataComparisonRespVO<TradeOrderSummaryRespVO>> getOrderComparison() {
return success(tradeOrderStatisticsService.getOrderComparison());
}
// TODO 芋艿:已经 review
@GetMapping("/order-count-trend")
@Operation(summary = "获得订单量趋势统计")
@PreAuthorize("@ss.hasPermission('statistics:trade:query')")
public CommonResult<List<DataComparisonRespVO<TradeOrderTrendRespVO>>> getOrderCountTrendComparison(@Valid TradeOrderTrendReqVO reqVO) {
// TODO @疯狂:要注意 date 的排序;
return success(tradeOrderStatisticsService.getOrderCountTrendComparison(reqVO));
}
}

View File

@ -0,0 +1,22 @@
package cn.iocoder.yudao.module.statistics.controller.admin.trade.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "管理后台 - 交易订单数量 Response VO")
@Data
public class TradeOrderCountRespVO {
@Schema(description = "待发货", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long undelivered;
@Schema(description = "待核销", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long pickUp;
@Schema(description = "退款中", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long afterSaleApply;
@Schema(description = "提现待审核", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long auditingWithdraw;
}

View File

@ -0,0 +1,16 @@
package cn.iocoder.yudao.module.statistics.controller.admin.trade.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "管理后台 - 交易订单统计 Response VO")
@Data
public class TradeOrderSummaryRespVO {
@Schema(description = "支付订单商品数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Integer orderPayCount;
@Schema(description = "总支付金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Integer orderPayPrice;
}

View File

@ -0,0 +1,31 @@
package cn.iocoder.yudao.module.statistics.controller.admin.trade.vo;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import cn.iocoder.yudao.module.statistics.enums.TimeRangeTypeEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import javax.validation.constraints.NotNull;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 交易订单量趋势统计 Request VO")
@Data
public class TradeOrderTrendReqVO {
@Schema(description = "日期范围类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "日期范围类型不能为空")
@InEnum(value = TimeRangeTypeEnum.class, message = "日期范围类型,必须是 {value}")
private Integer type;
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
@Schema(description = "起始时间")
private LocalDateTime beginTime;
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
@Schema(description = "截止时间")
private LocalDateTime endTime;
}

View File

@ -0,0 +1,19 @@
package cn.iocoder.yudao.module.statistics.controller.admin.trade.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "管理后台 - 订单量趋势统计 Response VO")
@Data
public class TradeOrderTrendRespVO {
@Schema(description = "日期", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private String date;
@Schema(description = "订单数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Integer orderPayCount;
@Schema(description = "订单支付金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Integer orderPayPrice;
}

View File

@ -0,0 +1,20 @@
package cn.iocoder.yudao.module.statistics.controller.admin.trade.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "管理后台 - 交易统计 Response VO")
@Data
public class TradeSummaryRespVO {
@Schema(description = "昨日订单数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Integer yesterdayOrderCount;
@Schema(description = "昨日支付金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Integer yesterdayPayPrice;
@Schema(description = "本月订单数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Integer monthOrderCount;
@Schema(description = "本月支付金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Integer monthPayPrice;
}

View File

@ -0,0 +1,18 @@
package cn.iocoder.yudao.module.statistics.controller.admin.trade.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 交易状况 Request VO")
@Data
public class TradeTrendReqVO {
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
@Schema(description = "时间范围")
private LocalDateTime[] times;
}

View File

@ -0,0 +1,44 @@
package cn.iocoder.yudao.module.statistics.controller.admin.trade.vo;
import cn.iocoder.yudao.framework.excel.core.convert.MoneyConvert;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.format.DateTimeFormat;
import lombok.Data;
import java.time.LocalDate;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY;
/**
* Excel VO
*
* @author owen
*/
@Data
public class TradeTrendSummaryExcelVO {
@ExcelProperty(value = "日期")
@DateTimeFormat(FORMAT_YEAR_MONTH_DAY)
private LocalDate date;
@ExcelProperty(value = "营业额", converter = MoneyConvert.class)
private Integer turnoverPrice;
@ExcelProperty(value = "商品支付金额", converter = MoneyConvert.class)
private Integer orderPayPrice;
@ExcelProperty(value = "充值金额", converter = MoneyConvert.class)
private Integer rechargePrice;
@ExcelProperty(value = "支出金额", converter = MoneyConvert.class)
private Integer expensePrice;
@ExcelProperty(value = "余额支付金额", converter = MoneyConvert.class)
private Integer walletPayPrice;
@ExcelProperty(value = "支付佣金金额", converter = MoneyConvert.class)
private Integer brokerageSettlementPrice;
@ExcelProperty(value = "商品退款金额", converter = MoneyConvert.class)
private Integer afterSaleRefundPrice;
}

View File

@ -0,0 +1,40 @@
package cn.iocoder.yudao.module.statistics.controller.admin.trade.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDate;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY;
@Schema(description = "管理后台 - 交易状况统计 Response VO")
@Data
public class TradeTrendSummaryRespVO {
@Schema(description = "日期", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY)
private LocalDate date;
@Schema(description = "营业额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Integer turnoverPrice; // 营业额 = 商品支付金额 + 充值金额
@Schema(description = "订单支付金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Integer orderPayPrice;
@Schema(description = "余额支付金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Integer walletPayPrice;
@Schema(description = "订单退款金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Integer afterSaleRefundPrice;
@Schema(description = "支付佣金金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Integer brokerageSettlementPrice;
@Schema(description = "充值金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Integer rechargePrice;
@Schema(description = "支出金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Integer expensePrice; // 余额支付金额 + 支付佣金金额 + 商品退款金额
}

View File

@ -0,0 +1,4 @@
/**
* TODO
*/
package cn.iocoder.yudao.module.statistics.controller.app;

View File

@ -0,0 +1,51 @@
package cn.iocoder.yudao.module.statistics.convert.member;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjUtil;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.ip.core.Area;
import cn.iocoder.yudao.module.statistics.controller.admin.common.vo.DataComparisonRespVO;
import cn.iocoder.yudao.module.statistics.controller.admin.member.vo.MemberAnalyseDataRespVO;
import cn.iocoder.yudao.module.statistics.controller.admin.member.vo.MemberAnalyseRespVO;
import cn.iocoder.yudao.module.statistics.controller.admin.member.vo.MemberAreaStatisticsRespVO;
import cn.iocoder.yudao.module.statistics.controller.admin.member.vo.MemberSummaryRespVO;
import cn.iocoder.yudao.module.statistics.service.member.bo.MemberAreaStatisticsRespBO;
import cn.iocoder.yudao.module.statistics.service.pay.bo.RechargeSummaryRespBO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
import java.util.List;
import java.util.Map;
import java.util.Optional;
/**
* Convert
*
* @author owen
*/
@Mapper
public interface MemberStatisticsConvert {
MemberStatisticsConvert INSTANCE = Mappers.getMapper(MemberStatisticsConvert.class);
default List<MemberAreaStatisticsRespVO> convertList(List<Area> areaList,
Map<Integer, Integer> userCountMap,
Map<Integer, MemberAreaStatisticsRespBO> orderMap) {
return CollectionUtils.convertList(areaList, area -> {
MemberAreaStatisticsRespBO orderVo = Optional.ofNullable(orderMap.get(area.getId()))
.orElseGet(MemberAreaStatisticsRespBO::new);
return new MemberAreaStatisticsRespVO()
.setAreaId(area.getId()).setAreaName(area.getName())
.setUserCount(MapUtil.getInt(userCountMap, area.getId(), 0))
.setOrderCreateUserCount(ObjUtil.defaultIfNull(orderVo.getOrderCreateUserCount(), 0))
.setOrderPayUserCount(ObjUtil.defaultIfNull(orderVo.getOrderPayUserCount(), 0))
.setOrderPayPrice(ObjUtil.defaultIfNull(orderVo.getOrderPayPrice(), 0));
});
}
MemberSummaryRespVO convert(RechargeSummaryRespBO rechargeSummary, Integer expensePrice, Integer userCount);
MemberAnalyseRespVO convert(Integer visitUserCount, Integer orderUserCount, Integer payUserCount, int atv,
DataComparisonRespVO<MemberAnalyseDataRespVO> comparison);
}

View File

@ -0,0 +1,19 @@
package cn.iocoder.yudao.module.statistics.convert.pay;
import cn.iocoder.yudao.module.statistics.controller.admin.pay.vo.PaySummaryRespVO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
/**
* Convert
*
* @author owen
*/
@Mapper
public interface PayStatisticsConvert {
PayStatisticsConvert INSTANCE = Mappers.getMapper(PayStatisticsConvert.class);
PaySummaryRespVO convert(Integer rechargePrice);
}

View File

@ -0,0 +1,74 @@
package cn.iocoder.yudao.module.statistics.convert.trade;
import cn.iocoder.yudao.module.statistics.controller.admin.common.vo.DataComparisonRespVO;
import cn.iocoder.yudao.module.statistics.controller.admin.trade.vo.TradeOrderCountRespVO;
import cn.iocoder.yudao.module.statistics.controller.admin.trade.vo.TradeSummaryRespVO;
import cn.iocoder.yudao.module.statistics.controller.admin.trade.vo.TradeTrendSummaryExcelVO;
import cn.iocoder.yudao.module.statistics.controller.admin.trade.vo.TradeTrendSummaryRespVO;
import cn.iocoder.yudao.module.statistics.dal.dataobject.trade.TradeStatisticsDO;
import cn.iocoder.yudao.module.statistics.service.trade.bo.AfterSaleSummaryRespBO;
import cn.iocoder.yudao.module.statistics.service.trade.bo.TradeOrderSummaryRespBO;
import cn.iocoder.yudao.module.statistics.service.trade.bo.TradeSummaryRespBO;
import cn.iocoder.yudao.module.statistics.service.trade.bo.WalletSummaryRespBO;
import org.mapstruct.IterableMapping;
import org.mapstruct.Mapper;
import org.mapstruct.Named;
import org.mapstruct.factory.Mappers;
import java.time.LocalDateTime;
import java.util.List;
/**
* Convert
*
* @author owen
*/
@Mapper
public interface TradeStatisticsConvert {
TradeStatisticsConvert INSTANCE = Mappers.getMapper(TradeStatisticsConvert.class);
default DataComparisonRespVO<TradeSummaryRespVO> convert(TradeSummaryRespBO yesterdayData,
TradeSummaryRespBO beforeYesterdayData,
TradeSummaryRespBO monthData,
TradeSummaryRespBO lastMonthData) {
return convert(convert(yesterdayData, monthData), convert(beforeYesterdayData, lastMonthData));
}
default TradeSummaryRespVO convert(TradeSummaryRespBO yesterdayData, TradeSummaryRespBO monthData) {
return new TradeSummaryRespVO()
.setYesterdayOrderCount(yesterdayData.getCount()).setYesterdayPayPrice(yesterdayData.getSummary())
.setMonthOrderCount(monthData.getCount()).setMonthPayPrice(monthData.getSummary());
}
DataComparisonRespVO<TradeSummaryRespVO> convert(TradeSummaryRespVO value, TradeSummaryRespVO reference);
DataComparisonRespVO<TradeTrendSummaryRespVO> convert(TradeTrendSummaryRespVO value,
TradeTrendSummaryRespVO reference);
List<TradeTrendSummaryExcelVO> convertList02(List<TradeTrendSummaryRespVO> list);
TradeStatisticsDO convert(LocalDateTime time, TradeOrderSummaryRespBO orderSummary,
AfterSaleSummaryRespBO afterSaleSummary, Integer brokerageSettlementPrice,
WalletSummaryRespBO walletSummary);
@IterableMapping(qualifiedByName = "convert")
List<TradeTrendSummaryRespVO> convertList(List<TradeStatisticsDO> list);
TradeTrendSummaryRespVO convertA(TradeStatisticsDO tradeStatistics);
@Named("convert")
default TradeTrendSummaryRespVO convert(TradeStatisticsDO tradeStatistics) {
TradeTrendSummaryRespVO vo = convertA(tradeStatistics);
return vo
.setDate(tradeStatistics.getTime().toLocalDate())
// 营业额 = 商品支付金额 + 充值金额
.setTurnoverPrice(tradeStatistics.getOrderPayPrice() + tradeStatistics.getRechargePayPrice())
// 支出金额 = 余额支付金额 + 支付佣金金额 + 商品退款金额
.setExpensePrice(tradeStatistics.getWalletPayPrice() + tradeStatistics.getBrokerageSettlementPrice() + tradeStatistics.getAfterSaleRefundPrice());
}
TradeOrderCountRespVO convert(Long undelivered, Long pickUp, Long afterSaleApply, Long auditingWithdraw);
}

View File

@ -0,0 +1,70 @@
package cn.iocoder.yudao.module.statistics.dal.dataobject.member;
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
* <p>
*
*
* @author
*/
@TableName("member_statistics")
@KeySequence("member_statistics_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class MemberStatisticsDO extends BaseDO {
/**
*
*/
@TableId
private Long id;
/**
*
*/
private LocalDateTime time;
/**
*
*/
private Integer userRegisterCount;
/**
* 访UV
*/
private Integer userVisitCount;
/**
* 访PV
*/
private Integer pageVisitCount;
/**
*
*/
private Integer rechargeUserCount;
/**
*
*/
private Integer orderCreateUserCount;
/**
*
*/
private Integer orderPayUserCount;
/**
*
*/
private Integer orderPayPrice;
}

View File

@ -0,0 +1,4 @@
/**
* todo
*/
package cn.iocoder.yudao.module.statistics.dal.dataobject;

View File

@ -0,0 +1,89 @@
package cn.iocoder.yudao.module.statistics.dal.dataobject.trade;
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
* <p>
*
*
* @author
*/
@TableName("trade_statistics")
@KeySequence("trade_statistics_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class TradeStatisticsDO extends BaseDO {
/**
*
*/
@TableId
private Long id;
/**
*
*/
private LocalDateTime time;
/**
*
*/
private Integer orderCreateCount;
/**
*
*/
private Integer orderPayCount;
/**
*
*/
private Integer orderPayPrice;
/**
* 退
*/
private Integer afterSaleCount;
/**
* 退
*/
private Integer afterSaleRefundPrice;
/**
*
*/
private Integer brokerageSettlementPrice;
/**
*
*/
private Integer walletPayPrice;
/**
*
* <p>
* PayWalletRechargeDO
*/
private Integer rechargePayCount;
/**
*
*/
private Integer rechargePayPrice;
/**
* 退
*/
private Integer rechargeRefundCount;
/**
* 退
*/
private Integer rechargeRefundPrice;
}

View File

@ -0,0 +1,29 @@
package cn.iocoder.yudao.module.statistics.dal.mysql.infra;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.time.LocalDateTime;
// TODO @芋艿api 访问日志,现在会清理,可能要单独有个偏业务的访问表;
/**
* API 访 Mapper
*
* @author owen
*/
@Mapper
@SuppressWarnings("rawtypes")
public interface ApiAccessLogStatisticsMapper extends BaseMapperX {
// TODO 芋艿:已经 review
Integer selectIpCountByUserTypeAndCreateTimeBetween(@Param("userType") Integer userType,
@Param("beginTime") LocalDateTime beginTime,
@Param("endTime") LocalDateTime endTime);
// TODO 芋艿:已经 review
Integer selectUserCountByUserTypeAndCreateTimeBetween(@Param("userType") Integer userType,
@Param("beginTime") LocalDateTime beginTime,
@Param("endTime") LocalDateTime endTime);
}

View File

@ -0,0 +1,47 @@
package cn.iocoder.yudao.module.statistics.dal.mysql.member;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.module.statistics.controller.admin.member.vo.MemberRegisterCountRespVO;
import cn.iocoder.yudao.module.statistics.controller.admin.member.vo.MemberSexStatisticsRespVO;
import cn.iocoder.yudao.module.statistics.controller.admin.member.vo.MemberTerminalStatisticsRespVO;
import cn.iocoder.yudao.module.statistics.service.member.bo.MemberAreaStatisticsRespBO;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.time.LocalDateTime;
import java.util.List;
/**
* Mapper
*
* @author owen
*/
@Mapper
@SuppressWarnings("rawtypes")
public interface MemberStatisticsMapper extends BaseMapperX {
// TODO @芋艿:已经 review
List<MemberAreaStatisticsRespBO> selectSummaryListByAreaId();
// TODO @芋艿:已经 review
List<MemberSexStatisticsRespVO> selectSummaryListBySex();
// TODO @芋艿:已经 review
List<MemberTerminalStatisticsRespVO> selectSummaryListByRegisterTerminal();
// TODO @芋艿:已经 review
Integer selectUserCount(@Param("beginTime") LocalDateTime beginTime,
@Param("endTime") LocalDateTime endTime);
// TODO @芋艿:已经 review
/**
*
*
* @param beginTime
* @param endTime
* @return
*/
List<MemberRegisterCountRespVO> selectListByCreateTimeBetween(@Param("beginTime") LocalDateTime beginTime,
@Param("endTime") LocalDateTime endTime);
}

View File

@ -0,0 +1,43 @@
package cn.iocoder.yudao.module.statistics.dal.mysql.pay;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.module.statistics.service.pay.bo.RechargeSummaryRespBO;
import cn.iocoder.yudao.module.statistics.service.trade.bo.WalletSummaryRespBO;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.time.LocalDateTime;
/**
* Mapper
*
* @author owen
*/
@Mapper
@SuppressWarnings("rawtypes")
public interface PayWalletStatisticsMapper extends BaseMapperX {
// TODO 芋艿:已经 review
WalletSummaryRespBO selectRechargeSummaryByPayTimeBetween(@Param("beginTime") LocalDateTime beginTime,
@Param("endTime") LocalDateTime endTime,
@Param("payStatus") Boolean payStatus);
// TODO 芋艿:已经 review
WalletSummaryRespBO selectRechargeSummaryByRefundTimeBetween(@Param("beginTime") LocalDateTime beginTime,
@Param("endTime") LocalDateTime endTime,
@Param("refundStatus") Integer refundStatus);
// TODO 芋艿:已经 review
Integer selectPriceSummaryByBizTypeAndCreateTimeBetween(@Param("beginTime") LocalDateTime beginTime,
@Param("endTime") LocalDateTime endTime,
@Param("bizType") Integer bizType);
// TODO 芋艿:已经 review
RechargeSummaryRespBO selectRechargeSummaryGroupByWalletId(@Param("beginTime") LocalDateTime beginTime,
@Param("endTime") LocalDateTime endTime,
@Param("payStatus") Boolean payStatus);
// TODO 芋艿:已经 review
Integer selectRechargePriceSummary(@Param("payStatus") Integer payStatus);
}

View File

@ -0,0 +1,74 @@
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;
}

View File

@ -0,0 +1,70 @@
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;
}

View File

@ -0,0 +1,26 @@
package cn.iocoder.yudao.module.statistics.dal.mysql.trade;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.module.statistics.dal.dataobject.trade.TradeStatisticsDO;
import cn.iocoder.yudao.module.statistics.service.trade.bo.AfterSaleSummaryRespBO;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.time.LocalDateTime;
/**
* Mapper
*
* @author owen
*/
@Mapper
public interface AfterSaleStatisticsMapper extends BaseMapperX<TradeStatisticsDO> {
// TODO 芋艿:已 review
AfterSaleSummaryRespBO selectSummaryByRefundTimeBetween(@Param("beginTime") LocalDateTime beginTime,
@Param("endTime") LocalDateTime endTime);
// TODO 芋艿:已经 review
Long selectCountByStatus(@Param("status") Integer status);
}

View File

@ -0,0 +1,27 @@
package cn.iocoder.yudao.module.statistics.dal.mysql.trade;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.module.statistics.dal.dataobject.trade.TradeStatisticsDO;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.time.LocalDateTime;
/**
* Mapper
*
* @author owen
*/
@Mapper
public interface BrokerageStatisticsMapper extends BaseMapperX<TradeStatisticsDO> {
// TODO 芋艿:已经 review
Integer selectSummaryPriceByStatusAndUnfreezeTimeBetween(@Param("bizType") Integer bizType,
@Param("status") Integer status,
@Param("beginTime") LocalDateTime beginTime,
@Param("endTime") LocalDateTime endTime);
// TODO 芋艿:已经 review
Long selectWithdrawCountByStatus(@Param("status") Integer status);
}

View File

@ -0,0 +1,75 @@
package cn.iocoder.yudao.module.statistics.dal.mysql.trade;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.module.statistics.controller.admin.trade.vo.TradeOrderSummaryRespVO;
import cn.iocoder.yudao.module.statistics.controller.admin.trade.vo.TradeOrderTrendRespVO;
import cn.iocoder.yudao.module.statistics.dal.dataobject.trade.TradeStatisticsDO;
import cn.iocoder.yudao.module.statistics.service.member.bo.MemberAreaStatisticsRespBO;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.time.LocalDateTime;
import java.util.List;
/**
* Mapper
*
* @author owen
*/
@Mapper
public interface TradeOrderStatisticsMapper extends BaseMapperX<TradeStatisticsDO> {
// TODO 芋艿:已经 review
List<MemberAreaStatisticsRespBO> selectSummaryListByAreaId();
// TODO 芋艿:已经 review
Integer selectCountByCreateTimeBetween(@Param("beginTime") LocalDateTime beginTime,
@Param("endTime") LocalDateTime endTime);
// TODO 芋艿:已经 review
Integer selectCountByPayTimeBetween(@Param("beginTime") LocalDateTime beginTime,
@Param("endTime") LocalDateTime endTime);
// TODO 芋艿:已经 review
Integer selectSummaryPriceByPayTimeBetween(@Param("beginTime") LocalDateTime beginTime,
@Param("endTime") LocalDateTime endTime);
// TODO 芋艿:已经 review
Integer selectUserCountByCreateTimeBetween(@Param("beginTime") LocalDateTime beginTime,
@Param("endTime") LocalDateTime endTime);
// TODO 芋艿:已经 review
Integer selectUserCountByPayTimeBetween(@Param("beginTime") LocalDateTime beginTime,
@Param("endTime") LocalDateTime endTime);
// TODO 芋艿:已经 review
/**
*
*
* @param beginTime
* @param endTime
* @return
*/
List<TradeOrderTrendRespVO> selectListByPayTimeBetweenAndGroupByDay(@Param("beginTime") LocalDateTime beginTime,
@Param("endTime") LocalDateTime endTime);
// TODO 芋艿:已经 review
/**
*
*
* @param beginTime
* @param endTime
* @return
*/
List<TradeOrderTrendRespVO> selectListByPayTimeBetweenAndGroupByMonth(@Param("beginTime") LocalDateTime beginTime,
@Param("endTime") LocalDateTime endTime);
// TODO @芋艿:已经 review
Long selectCountByStatusAndDeliveryType(@Param("status") Integer status, @Param("deliveryType") Integer deliveryType);
// TODO 芋艿:已经 review
TradeOrderSummaryRespVO selectPaySummaryByStatusAndPayTimeBetween(@Param("status") Integer status,
@Param("beginTime") LocalDateTime beginTime,
@Param("endTime") LocalDateTime endTime);
}

View File

@ -0,0 +1,44 @@
package cn.iocoder.yudao.module.statistics.dal.mysql.trade;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.statistics.controller.admin.trade.vo.TradeTrendSummaryRespVO;
import cn.iocoder.yudao.module.statistics.dal.dataobject.trade.TradeStatisticsDO;
import cn.iocoder.yudao.module.statistics.service.trade.bo.TradeSummaryRespBO;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.time.LocalDateTime;
import java.util.List;
/**
* Mapper
*
* @author owen
*/
@Mapper
public interface TradeStatisticsMapper extends BaseMapperX<TradeStatisticsDO> {
TradeSummaryRespBO selectOrderCreateCountSumAndOrderPayPriceSumByTimeBetween(@Param("beginTime") LocalDateTime beginTime,
@Param("endTime") LocalDateTime endTime);
TradeTrendSummaryRespVO selectVoByTimeBetween(@Param("beginTime") LocalDateTime beginTime,
@Param("endTime") LocalDateTime endTime);
// TODO @芋艿:已经 review
default List<TradeStatisticsDO> selectListByTimeBetween(LocalDateTime beginTime, LocalDateTime endTime) {
return selectList(new LambdaQueryWrapperX<TradeStatisticsDO>()
.between(TradeStatisticsDO::getTime, beginTime, endTime));
}
// TODO @芋艿:已经 review
Integer selectExpensePriceByTimeBetween(@Param("beginTime") LocalDateTime beginTime,
@Param("endTime") LocalDateTime endTime);
// TODO @芋艿:已经 review
default TradeStatisticsDO selectByTimeBetween(LocalDateTime beginTime, LocalDateTime endTime) {
return selectOne(new LambdaQueryWrapperX<TradeStatisticsDO>()
.between(TradeStatisticsDO::getTime, beginTime, endTime));
}
}

View File

@ -0,0 +1,4 @@
/**
* TODO
*/
package cn.iocoder.yudao.module.statistics.job;

View File

@ -0,0 +1,49 @@
package cn.iocoder.yudao.module.statistics.job.trade;
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.tenant.core.job.TenantJob;
import cn.iocoder.yudao.module.statistics.service.trade.TradeStatisticsService;
import com.xxl.job.core.handler.annotation.XxlJob;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
// TODO 芋艿:缺个 Job 的配置;等和 Product 一起配置
/**
* Job
*
* @author owen
*/
@Component
public class TradeStatisticsJob {
@Resource
private TradeStatisticsService tradeStatisticsService;
/**
*
*
* @param param 1
* @return
*/
@XxlJob("tradeStatisticsJob")
@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 = tradeStatisticsService.statisticsTrade(days);
return StrUtil.format("交易统计:\n{}", result);
}
}

View File

@ -0,0 +1,8 @@
/**
* statistics
*
*
* 1. Controller URL /statistics/ Module
* 2. DataObject statistics_ 便
*/
package cn.iocoder.yudao.module.statistics;

View File

@ -0,0 +1,34 @@
package cn.iocoder.yudao.module.statistics.service.infra;
import java.time.LocalDateTime;
/**
* API 访 Service
*
* @author owen
*/
public interface ApiAccessLogStatisticsService {
// TODO 芋艿:已经 review
/**
*
*
* @param userType
* @param beginTime
* @param endTime
* @return
*/
Integer getUserCount(Integer userType, LocalDateTime beginTime, LocalDateTime endTime);
// TODO 芋艿:已经 review
/**
* 访
*
* @param userType
* @param beginTime
* @param endTime
* @return 访
*/
Integer getIpCount(Integer userType, LocalDateTime beginTime, LocalDateTime endTime);
}

View File

@ -0,0 +1,32 @@
package cn.iocoder.yudao.module.statistics.service.infra;
import cn.iocoder.yudao.module.statistics.dal.mysql.infra.ApiAccessLogStatisticsMapper;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
import java.time.LocalDateTime;
/**
* API 访 Service
*
* @author owen
*/
@Service
@Validated
public class ApiAccessLogStatisticsServiceImpl implements ApiAccessLogStatisticsService {
@Resource
private ApiAccessLogStatisticsMapper apiAccessLogStatisticsMapper;
@Override
public Integer getUserCount(Integer userType, LocalDateTime beginTime, LocalDateTime endTime) {
return apiAccessLogStatisticsMapper.selectUserCountByUserTypeAndCreateTimeBetween(userType, beginTime, endTime);
}
@Override
public Integer getIpCount(Integer userType, LocalDateTime beginTime, LocalDateTime endTime) {
return apiAccessLogStatisticsMapper.selectIpCountByUserTypeAndCreateTimeBetween(userType, beginTime, endTime);
}
}

View File

@ -0,0 +1,76 @@
package cn.iocoder.yudao.module.statistics.service.member;
import cn.iocoder.yudao.module.statistics.controller.admin.common.vo.DataComparisonRespVO;
import cn.iocoder.yudao.module.statistics.controller.admin.member.vo.*;
import java.time.LocalDateTime;
import java.util.List;
/**
* Service
*
* @author owen
*/
public interface MemberStatisticsService {
// TODO 芋艿:已经 review
/**
*
*
* @return
*/
MemberSummaryRespVO getMemberSummary();
// TODO 芋艿:已经 review
/**
*
*
* @param beginTime
* @param endTime
* @return
*/
DataComparisonRespVO<MemberAnalyseDataRespVO> getMemberAnalyseComparisonData(LocalDateTime beginTime,
LocalDateTime endTime);
// TODO 芋艿:已经 review
/**
*
*
* @return
*/
List<MemberAreaStatisticsRespVO> getMemberAreaStatisticsList();
// TODO 芋艿:已经 review
/**
*
*
* @return
*/
List<MemberSexStatisticsRespVO> getMemberSexStatisticsList();
/**
*
*
* @return
*/
List<MemberTerminalStatisticsRespVO> getMemberTerminalStatisticsList();
// TODO 芋艿:已经 review
/**
*
*
* @param beginTime
* @param endTime
* @return
*/
List<MemberRegisterCountRespVO> getMemberRegisterCountList(LocalDateTime beginTime, LocalDateTime endTime);
// TODO 芋艿:已经 review
/**
*
*
* @return
*/
DataComparisonRespVO<MemberCountRespVO> getUserCountComparison();
}

View File

@ -0,0 +1,136 @@
package cn.iocoder.yudao.module.statistics.service.member;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.ip.core.Area;
import cn.iocoder.yudao.framework.ip.core.enums.AreaTypeEnum;
import cn.iocoder.yudao.framework.ip.core.utils.AreaUtils;
import cn.iocoder.yudao.module.statistics.controller.admin.common.vo.DataComparisonRespVO;
import cn.iocoder.yudao.module.statistics.controller.admin.member.vo.*;
import cn.iocoder.yudao.module.statistics.convert.member.MemberStatisticsConvert;
import cn.iocoder.yudao.module.statistics.dal.mysql.member.MemberStatisticsMapper;
import cn.iocoder.yudao.module.statistics.service.infra.ApiAccessLogStatisticsService;
import cn.iocoder.yudao.module.statistics.service.member.bo.MemberAreaStatisticsRespBO;
import cn.iocoder.yudao.module.statistics.service.pay.PayWalletStatisticsService;
import cn.iocoder.yudao.module.statistics.service.pay.bo.RechargeSummaryRespBO;
import cn.iocoder.yudao.module.statistics.service.trade.TradeOrderStatisticsService;
import cn.iocoder.yudao.module.statistics.service.trade.TradeStatisticsService;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
/**
* Service
*
* @author owen
*/
@Service
@Validated
public class MemberStatisticsServiceImpl implements MemberStatisticsService {
@Resource
private MemberStatisticsMapper memberStatisticsMapper;
@Resource
private PayWalletStatisticsService payWalletStatisticsService;
@Resource
private TradeStatisticsService tradeStatisticsService;
@Resource
private TradeOrderStatisticsService tradeOrderStatisticsService;
@Resource
private ApiAccessLogStatisticsService apiAccessLogStatisticsService;
@Override
public MemberSummaryRespVO getMemberSummary() {
RechargeSummaryRespBO rechargeSummary = payWalletStatisticsService.getUserRechargeSummary(null, null);
// TODO @疯狂1这里是实时统计不好走走 TradeStatistics 表2因为这个放在商城下所以只考虑订单数据即按照 trade_order 的 pay_price 并且已支付来计算;
Integer expensePrice = tradeStatisticsService.getExpensePrice(null, null);
Integer userCount = memberStatisticsMapper.selectUserCount(null, null);
return MemberStatisticsConvert.INSTANCE.convert(rechargeSummary, expensePrice, userCount);
}
@Override
public List<MemberAreaStatisticsRespVO> getMemberAreaStatisticsList() {
// 统计用户
// TODO @疯狂:可能得把每个省的用户,都查询出来,然后去 order 那边 in因为要按照这些人为基础来计算用户规模量大可能不太好但是暂时就先这样搞吧 = =
Map<Integer, Integer> userCountMap = convertMap(memberStatisticsMapper.selectSummaryListByAreaId(),
vo -> AreaUtils.getParentIdByType(vo.getAreaId(), AreaTypeEnum.PROVINCE),
MemberAreaStatisticsRespBO::getUserCount, Integer::sum);
// 统计订单
Map<Integer, MemberAreaStatisticsRespBO> orderMap = convertMap(tradeOrderStatisticsService.getSummaryListByAreaId(),
bo -> AreaUtils.getParentIdByType(bo.getAreaId(), AreaTypeEnum.PROVINCE),
bo -> bo,
(a, b) -> new MemberAreaStatisticsRespBO()
.setOrderCreateUserCount(a.getOrderCreateUserCount() + b.getOrderCreateUserCount())
.setOrderPayUserCount(a.getOrderPayUserCount() + b.getOrderPayUserCount())
.setOrderPayPrice(a.getOrderPayPrice() + b.getOrderPayPrice()));
// 拼接数据
List<Area> areaList = AreaUtils.getByType(AreaTypeEnum.PROVINCE, area -> area);
areaList.add(new Area().setId(null).setName("未知"));
return MemberStatisticsConvert.INSTANCE.convertList(areaList, userCountMap, orderMap);
}
@Override
public DataComparisonRespVO<MemberAnalyseDataRespVO> getMemberAnalyseComparisonData(LocalDateTime beginTime, LocalDateTime endTime) {
// 当前数据
MemberAnalyseDataRespVO vo = getMemberAnalyseData(beginTime, endTime);
// 对照数据
LocalDateTime referenceEndDate = beginTime.minusDays(1); // 减少1天防止出现时间重叠
LocalDateTime referenceBeginDate = referenceEndDate.minus(Duration.between(beginTime, endTime));
MemberAnalyseDataRespVO reference = getMemberAnalyseData(
LocalDateTimeUtil.beginOfDay(referenceBeginDate), LocalDateTimeUtil.endOfDay(referenceEndDate));
return new DataComparisonRespVO<>(vo, reference);
}
private MemberAnalyseDataRespVO getMemberAnalyseData(LocalDateTime beginTime, LocalDateTime endTime) {
Integer rechargeUserCount = Optional.ofNullable(payWalletStatisticsService.getUserRechargeSummary(beginTime, endTime))
.map(RechargeSummaryRespBO::getRechargeUserCount).orElse(0);
return new MemberAnalyseDataRespVO()
.setRegisterUserCount(memberStatisticsMapper.selectUserCount(beginTime, endTime))
.setVisitUserCount(apiAccessLogStatisticsService.getUserCount(UserTypeEnum.MEMBER.getValue(), beginTime, endTime))
.setRechargeUserCount(rechargeUserCount);
}
@Override
public List<MemberSexStatisticsRespVO> getMemberSexStatisticsList() {
return memberStatisticsMapper.selectSummaryListBySex();
}
@Override
public List<MemberTerminalStatisticsRespVO> getMemberTerminalStatisticsList() {
return memberStatisticsMapper.selectSummaryListByRegisterTerminal();
}
@Override
public List<MemberRegisterCountRespVO> getMemberRegisterCountList(LocalDateTime beginTime, LocalDateTime endTime) {
return memberStatisticsMapper.selectListByCreateTimeBetween(beginTime, endTime);
}
@Override
public DataComparisonRespVO<MemberCountRespVO> getUserCountComparison() {
// 今日时间范围
LocalDateTime beginOfToday = LocalDateTimeUtil.beginOfDay(LocalDateTime.now());
LocalDateTime endOfToday = LocalDateTimeUtil.endOfDay(beginOfToday);
// 昨日时间范围
LocalDateTime beginOfYesterday = LocalDateTimeUtil.beginOfDay(beginOfToday.minusDays(1));
LocalDateTime endOfYesterday = LocalDateTimeUtil.endOfDay(beginOfYesterday);
return new DataComparisonRespVO<MemberCountRespVO>()
.setValue(getUserCount(beginOfToday, endOfToday))
.setReference(getUserCount(beginOfYesterday, endOfYesterday));
}
private MemberCountRespVO getUserCount(LocalDateTime beginTime, LocalDateTime endTime) {
return new MemberCountRespVO()
.setRegisterUserCount(memberStatisticsMapper.selectUserCount(beginTime, endTime))
.setVisitUserCount(apiAccessLogStatisticsService.getIpCount(UserTypeEnum.MEMBER.getValue(), beginTime, endTime));
}
}

View File

@ -0,0 +1,38 @@
package cn.iocoder.yudao.module.statistics.service.member.bo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "管理后台 - 会员地区统计 Response BO")
@Data
public class MemberAreaStatisticsRespBO {
/**
*
*/
private Integer areaId;
/**
*
*/
private String areaName;
/**
*
*/
private Integer userCount;
/**
*
*/
private Integer orderCreateUserCount;
/**
*
*/
private Integer orderPayUserCount;
/**
*
*/
private Integer orderPayPrice;
}

View File

@ -0,0 +1,43 @@
package cn.iocoder.yudao.module.statistics.service.pay;
import cn.iocoder.yudao.module.statistics.service.pay.bo.RechargeSummaryRespBO;
import cn.iocoder.yudao.module.statistics.service.trade.bo.WalletSummaryRespBO;
import java.time.LocalDateTime;
/**
* Service
*
* @author owen
*/
public interface PayWalletStatisticsService {
// TODO 芋艿:已经 review
/**
*
*
* @param beginTime
* @param endTime
* @return
*/
WalletSummaryRespBO getWalletSummary(LocalDateTime beginTime, LocalDateTime endTime);
// TODO 芋艿:已经 review
/**
*
*
* @param beginTime
* @param endTime
* @return
*/
RechargeSummaryRespBO getUserRechargeSummary(LocalDateTime beginTime, LocalDateTime endTime);
// TODO 芋艿:已经 review
/**
*
*
* @return
*/
Integer getRechargePriceSummary();
}

View File

@ -0,0 +1,52 @@
package cn.iocoder.yudao.module.statistics.service.pay;
import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum;
import cn.iocoder.yudao.module.pay.enums.refund.PayRefundStatusEnum;
import cn.iocoder.yudao.module.pay.enums.wallet.PayWalletBizTypeEnum;
import cn.iocoder.yudao.module.statistics.dal.mysql.pay.PayWalletStatisticsMapper;
import cn.iocoder.yudao.module.statistics.service.pay.bo.RechargeSummaryRespBO;
import cn.iocoder.yudao.module.statistics.service.trade.bo.WalletSummaryRespBO;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
import java.time.LocalDateTime;
/**
* Service
*
* @author owen
*/
@Service
@Validated
public class PayWalletStatisticsServiceImpl implements PayWalletStatisticsService {
@Resource
private PayWalletStatisticsMapper payWalletStatisticsMapper;
@Override
public WalletSummaryRespBO getWalletSummary(LocalDateTime beginTime, LocalDateTime endTime) {
WalletSummaryRespBO paySummary = payWalletStatisticsMapper.selectRechargeSummaryByPayTimeBetween(
beginTime, endTime, true);
WalletSummaryRespBO refundSummary = payWalletStatisticsMapper.selectRechargeSummaryByRefundTimeBetween(
beginTime, endTime, PayRefundStatusEnum.SUCCESS.getStatus());
Integer walletPayPrice = payWalletStatisticsMapper.selectPriceSummaryByBizTypeAndCreateTimeBetween(
beginTime, endTime, PayWalletBizTypeEnum.PAYMENT.getType());
// 拼接
paySummary.setWalletPayPrice(walletPayPrice)
.setRechargeRefundCount(refundSummary.getRechargeRefundCount())
.setRechargeRefundPrice(refundSummary.getRechargeRefundPrice());
return paySummary;
}
@Override
public RechargeSummaryRespBO getUserRechargeSummary(LocalDateTime beginTime, LocalDateTime endTime) {
return payWalletStatisticsMapper.selectRechargeSummaryGroupByWalletId(beginTime, endTime, true);
}
@Override
public Integer getRechargePriceSummary() {
return payWalletStatisticsMapper.selectRechargePriceSummary(PayOrderStatusEnum.SUCCESS.getStatus());
}
}

View File

@ -0,0 +1,22 @@
package cn.iocoder.yudao.module.statistics.service.pay.bo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
* Response BO
*/
@Data
public class RechargeSummaryRespBO {
/**
*
*/
private Integer rechargeUserCount;
/**
*
*/
private Integer rechargePrice;
}

View File

@ -0,0 +1,34 @@
package cn.iocoder.yudao.module.statistics.service.trade;
import cn.iocoder.yudao.module.statistics.service.trade.bo.AfterSaleSummaryRespBO;
import cn.iocoder.yudao.module.trade.enums.aftersale.AfterSaleStatusEnum;
import java.time.LocalDateTime;
/**
* Service
*
* @author owen
*/
public interface AfterSaleStatisticsService {
// TODO 芋艿:已经 review
/**
*
*
* @param beginTime
* @param endTime
* @return
*/
AfterSaleSummaryRespBO getAfterSaleSummary(LocalDateTime beginTime, LocalDateTime endTime);
// TODO 芋艿:已经 review
/**
*
*
* @param status
* @return
*/
Long getCountByStatus(AfterSaleStatusEnum status);
}

View File

@ -0,0 +1,34 @@
package cn.iocoder.yudao.module.statistics.service.trade;
import cn.iocoder.yudao.module.statistics.dal.mysql.trade.AfterSaleStatisticsMapper;
import cn.iocoder.yudao.module.statistics.service.trade.bo.AfterSaleSummaryRespBO;
import cn.iocoder.yudao.module.trade.enums.aftersale.AfterSaleStatusEnum;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
import java.time.LocalDateTime;
/**
* Service
*
* @author owen
*/
@Service
@Validated
public class AfterSaleStatisticsServiceImpl implements AfterSaleStatisticsService {
@Resource
private AfterSaleStatisticsMapper afterSaleStatisticsMapper;
@Override
public AfterSaleSummaryRespBO getAfterSaleSummary(LocalDateTime beginTime, LocalDateTime endTime) {
return afterSaleStatisticsMapper.selectSummaryByRefundTimeBetween(beginTime, endTime);
}
@Override
public Long getCountByStatus(AfterSaleStatusEnum status) {
return afterSaleStatisticsMapper.selectCountByStatus(status.getStatus());
}
}

View File

@ -0,0 +1,33 @@
package cn.iocoder.yudao.module.statistics.service.trade;
import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageWithdrawStatusEnum;
import java.time.LocalDateTime;
/**
* Service
*
* @author owen
*/
public interface BrokerageStatisticsService {
// TODO 芋艿:已经 review
/**
*
*
* @param beginTime
* @param endTime
* @return
*/
Integer getBrokerageSettlementPriceSummary(LocalDateTime beginTime, LocalDateTime endTime);
// TODO 芋艿:已经 review
/**
*
*
* @param status
* @return
*/
Long getWithdrawCountByStatus(BrokerageWithdrawStatusEnum status);
}

View File

@ -0,0 +1,37 @@
package cn.iocoder.yudao.module.statistics.service.trade;
import cn.iocoder.yudao.module.statistics.dal.mysql.trade.BrokerageStatisticsMapper;
import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordBizTypeEnum;
import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordStatusEnum;
import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageWithdrawStatusEnum;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
import java.time.LocalDateTime;
/**
* Service
*
* @author owen
*/
@Service
@Validated
public class BrokerageStatisticsServiceImpl implements BrokerageStatisticsService {
@Resource
private BrokerageStatisticsMapper brokerageStatisticsMapper;
@Override
public Integer getBrokerageSettlementPriceSummary(LocalDateTime beginTime, LocalDateTime endTime) {
return brokerageStatisticsMapper.selectSummaryPriceByStatusAndUnfreezeTimeBetween(
BrokerageRecordBizTypeEnum.ORDER.getType(), BrokerageRecordStatusEnum.SETTLEMENT.getStatus(),
beginTime, endTime);
}
@Override
public Long getWithdrawCountByStatus(BrokerageWithdrawStatusEnum status) {
return brokerageStatisticsMapper.selectWithdrawCountByStatus(status.getStatus());
}
}

View File

@ -0,0 +1,89 @@
package cn.iocoder.yudao.module.statistics.service.trade;
import cn.iocoder.yudao.module.statistics.controller.admin.common.vo.DataComparisonRespVO;
import cn.iocoder.yudao.module.statistics.controller.admin.trade.vo.*;
import cn.iocoder.yudao.module.statistics.service.member.bo.MemberAreaStatisticsRespBO;
import cn.iocoder.yudao.module.statistics.service.trade.bo.TradeOrderSummaryRespBO;
import java.time.LocalDateTime;
import java.util.List;
/**
* Service
*
* @author owen
*/
public interface TradeOrderStatisticsService {
/**
*
*
* @param beginTime
* @param endTime
* @return
*/
TradeOrderSummaryRespBO getOrderSummary(LocalDateTime beginTime, LocalDateTime endTime);
// TODO 芋艿:已经 review
/**
*
*
* @return
*/
List<MemberAreaStatisticsRespBO> getSummaryListByAreaId();
// TODO 芋艿:已经 review
/**
*
*
* @param beginTime
* @param endTime
* @return
*/
Integer getOrderUserCount(LocalDateTime beginTime, LocalDateTime endTime);
// TODO 芋艿:已经 review
/**
*
*
* @param beginTime
* @param endTime
* @return
*/
Integer getPayUserCount(LocalDateTime beginTime, LocalDateTime endTime);
// TODO 芋艿:已经 review
/**
*
*
* @param beginTime
* @param endTime
* @return
*/
Integer getOrderPayPrice(LocalDateTime beginTime, LocalDateTime endTime);
/**
*
*
* @return
*/
Long getCountByStatusAndDeliveryType(Integer status, Integer deliveryType);
// TODO 芋艿:已经 review
/**
*
*
* @return
*/
DataComparisonRespVO<TradeOrderSummaryRespVO> getOrderComparison();
// TODO 芋艿:已经 review
/**
*
*
* @param reqVO
* @return
*/
List<DataComparisonRespVO<TradeOrderTrendRespVO>> getOrderCountTrendComparison(TradeOrderTrendReqVO reqVO);
}

View File

@ -0,0 +1,108 @@
package cn.iocoder.yudao.module.statistics.service.trade;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum;
import cn.iocoder.yudao.module.statistics.controller.admin.common.vo.DataComparisonRespVO;
import cn.iocoder.yudao.module.statistics.controller.admin.trade.vo.TradeOrderSummaryRespVO;
import cn.iocoder.yudao.module.statistics.controller.admin.trade.vo.TradeOrderTrendReqVO;
import cn.iocoder.yudao.module.statistics.controller.admin.trade.vo.TradeOrderTrendRespVO;
import cn.iocoder.yudao.module.statistics.dal.mysql.trade.TradeOrderStatisticsMapper;
import cn.iocoder.yudao.module.statistics.enums.TimeRangeTypeEnum;
import cn.iocoder.yudao.module.statistics.service.member.bo.MemberAreaStatisticsRespBO;
import cn.iocoder.yudao.module.statistics.service.trade.bo.TradeOrderSummaryRespBO;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
/**
* Service
*
* @author owen
*/
@Service
@Validated
public class TradeOrderStatisticsServiceImpl implements TradeOrderStatisticsService {
@Resource
private TradeOrderStatisticsMapper tradeOrderStatisticsMapper;
@Override
public TradeOrderSummaryRespBO getOrderSummary(LocalDateTime beginTime, LocalDateTime endTime) {
return new TradeOrderSummaryRespBO()
.setOrderCreateCount(tradeOrderStatisticsMapper.selectCountByCreateTimeBetween(beginTime, endTime))
.setOrderPayCount(tradeOrderStatisticsMapper.selectCountByPayTimeBetween(beginTime, endTime))
.setOrderPayPrice(tradeOrderStatisticsMapper.selectSummaryPriceByPayTimeBetween(beginTime, endTime));
}
@Override
public List<MemberAreaStatisticsRespBO> getSummaryListByAreaId() {
return tradeOrderStatisticsMapper.selectSummaryListByAreaId();
}
@Override
public Integer getOrderUserCount(LocalDateTime beginTime, LocalDateTime endTime) {
return tradeOrderStatisticsMapper.selectUserCountByCreateTimeBetween(beginTime, endTime);
}
@Override
public Integer getPayUserCount(LocalDateTime beginTime, LocalDateTime endTime) {
return tradeOrderStatisticsMapper.selectUserCountByPayTimeBetween(beginTime, endTime);
}
@Override
public Integer getOrderPayPrice(LocalDateTime beginTime, LocalDateTime endTime) {
return tradeOrderStatisticsMapper.selectSummaryPriceByPayTimeBetween(beginTime, endTime);
}
@Override
public Long getCountByStatusAndDeliveryType(Integer status, Integer deliveryType) {
return tradeOrderStatisticsMapper.selectCountByStatusAndDeliveryType(status, deliveryType);
}
@Override
public DataComparisonRespVO<TradeOrderSummaryRespVO> getOrderComparison() {
return new DataComparisonRespVO<TradeOrderSummaryRespVO>()
.setValue(getPayPriceSummary(LocalDateTime.now()))
.setReference(getPayPriceSummary(LocalDateTime.now().minusDays(1)));
}
private TradeOrderSummaryRespVO getPayPriceSummary(LocalDateTime date) {
LocalDateTime beginTime = LocalDateTimeUtil.beginOfDay(date);
LocalDateTime endTime = LocalDateTimeUtil.beginOfDay(date);
return tradeOrderStatisticsMapper.selectPaySummaryByStatusAndPayTimeBetween(
PayOrderStatusEnum.SUCCESS.getStatus(), beginTime, endTime);
}
@Override
public List<DataComparisonRespVO<TradeOrderTrendRespVO>> getOrderCountTrendComparison(TradeOrderTrendReqVO reqVO) {
// 查询当前数据
List<TradeOrderTrendRespVO> value = getOrderCountTrend(reqVO.getType(), reqVO.getBeginTime(), reqVO.getEndTime());
// 查询对照数据
LocalDateTime referenceEndTime = reqVO.getBeginTime().minusDays(1);
LocalDateTime referenceBeginTime = referenceEndTime.minus(Duration.between(reqVO.getBeginTime(), reqVO.getEndTime()));
List<TradeOrderTrendRespVO> reference = getOrderCountTrend(reqVO.getType(), referenceBeginTime, referenceEndTime);
// 顺序对比返回
return IntStream.range(0, value.size())
.mapToObj(index -> new DataComparisonRespVO<TradeOrderTrendRespVO>()
.setValue(CollUtil.get(value, index))
.setReference(CollUtil.get(reference, index)))
.collect(Collectors.toList());
}
private List<TradeOrderTrendRespVO> getOrderCountTrend(Integer timeRangeType, LocalDateTime beginTime, LocalDateTime endTime) {
// 情况一:按年统计时,以月份分组
if (TimeRangeTypeEnum.YEAR.getType().equals(timeRangeType)) {
return tradeOrderStatisticsMapper.selectListByPayTimeBetweenAndGroupByMonth(beginTime, endTime);
}
// 情况二:其它以天分组(天、周、月)
return tradeOrderStatisticsMapper.selectListByPayTimeBetweenAndGroupByDay(beginTime, endTime);
}
}

View File

@ -0,0 +1,70 @@
package cn.iocoder.yudao.module.statistics.service.trade;
import cn.iocoder.yudao.module.statistics.controller.admin.common.vo.DataComparisonRespVO;
import cn.iocoder.yudao.module.statistics.controller.admin.trade.vo.TradeTrendSummaryRespVO;
import cn.iocoder.yudao.module.statistics.dal.dataobject.trade.TradeStatisticsDO;
import cn.iocoder.yudao.module.statistics.service.trade.bo.TradeSummaryRespBO;
import java.time.LocalDateTime;
import java.util.List;
/**
* Service
*
* @author owen
*/
public interface TradeStatisticsService {
/**
*
*
* @return
*/
DataComparisonRespVO<TradeTrendSummaryRespVO> getTradeTrendSummaryComparison(
LocalDateTime beginTime, LocalDateTime endTime);
/**
*
*
* @param beginTime
* @param endTime
* @return
*/
Integer getExpensePrice(LocalDateTime beginTime, LocalDateTime endTime);
/**
*
*
* @param beginTime
* @param endTime
* @return
*/
List<TradeStatisticsDO> getTradeStatisticsList(LocalDateTime beginTime, LocalDateTime endTime);
// TODO 芋艿:已经 review
/**
*
*
* @return
*/
String statisticsTrade(Integer days);
// TODO 芋艿:已经 review
/**
*
*
* @param days
* @return
*/
TradeSummaryRespBO getTradeSummaryByDays(int days);
// TODO 芋艿:已经 review
/**
*
*
* @param months
* @return
*/
TradeSummaryRespBO getTradeSummaryByMonths(int months);
}

View File

@ -0,0 +1,135 @@
package cn.iocoder.yudao.module.statistics.service.trade;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
import cn.iocoder.yudao.module.statistics.controller.admin.common.vo.DataComparisonRespVO;
import cn.iocoder.yudao.module.statistics.controller.admin.trade.vo.TradeTrendSummaryRespVO;
import cn.iocoder.yudao.module.statistics.convert.trade.TradeStatisticsConvert;
import cn.iocoder.yudao.module.statistics.dal.dataobject.trade.TradeStatisticsDO;
import cn.iocoder.yudao.module.statistics.dal.mysql.trade.TradeStatisticsMapper;
import cn.iocoder.yudao.module.statistics.service.pay.PayWalletStatisticsService;
import cn.iocoder.yudao.module.statistics.service.trade.bo.AfterSaleSummaryRespBO;
import cn.iocoder.yudao.module.statistics.service.trade.bo.TradeOrderSummaryRespBO;
import cn.iocoder.yudao.module.statistics.service.trade.bo.TradeSummaryRespBO;
import cn.iocoder.yudao.module.statistics.service.trade.bo.WalletSummaryRespBO;
import org.springframework.stereotype.Service;
import org.springframework.util.StopWatch;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
/**
* Service
*
* @author owen
*/
@Service
@Validated
public class TradeStatisticsServiceImpl implements TradeStatisticsService {
@Resource
private TradeStatisticsMapper tradeStatisticsMapper;
@Resource
private TradeOrderStatisticsService tradeOrderStatisticsService;
@Resource
private AfterSaleStatisticsService afterSaleStatisticsService;
@Resource
private BrokerageStatisticsService brokerageStatisticsService;
@Resource
private PayWalletStatisticsService payWalletStatisticsService;
@Override
public TradeSummaryRespBO getTradeSummaryByDays(int days) {
LocalDateTime date = LocalDateTime.now().plusDays(days);
return tradeStatisticsMapper.selectOrderCreateCountSumAndOrderPayPriceSumByTimeBetween(
LocalDateTimeUtil.beginOfDay(date), LocalDateTimeUtil.endOfDay(date));
}
@Override
public TradeSummaryRespBO getTradeSummaryByMonths(int months) {
LocalDateTime monthDate = LocalDateTime.now().plusMonths(months);
return tradeStatisticsMapper.selectOrderCreateCountSumAndOrderPayPriceSumByTimeBetween(
LocalDateTimeUtils.beginOfMonth(monthDate), LocalDateTimeUtils.endOfMonth(monthDate));
}
@Override
public DataComparisonRespVO<TradeTrendSummaryRespVO> getTradeTrendSummaryComparison(LocalDateTime beginTime,
LocalDateTime endTime) {
// 统计数据
TradeTrendSummaryRespVO value = tradeStatisticsMapper.selectVoByTimeBetween(beginTime, endTime);
// 对照数据
LocalDateTime referenceBeginTime = beginTime.minus(Duration.between(beginTime, endTime));
TradeTrendSummaryRespVO reference = tradeStatisticsMapper.selectVoByTimeBetween(referenceBeginTime, beginTime);
return TradeStatisticsConvert.INSTANCE.convert(value, reference);
}
@Override
public Integer getExpensePrice(LocalDateTime beginTime, LocalDateTime endTime) {
return tradeStatisticsMapper.selectExpensePriceByTimeBetween(beginTime, endTime);
}
@Override
public List<TradeStatisticsDO> getTradeStatisticsList(LocalDateTime beginTime, LocalDateTime endTime) {
return tradeStatisticsMapper.selectListByTimeBetween(beginTime, endTime);
}
@Override
public String statisticsTrade(Integer days) {
LocalDateTime today = LocalDateTime.now();
return IntStream.rangeClosed(1, days)
.mapToObj(day -> statisticsTrade(today.minusDays(day)))
.sorted()
.collect(Collectors.joining("\n"));
}
/**
*
*
* @param date
* @return
*/
private String statisticsTrade(LocalDateTime date) {
// 1. 处理统计时间范围
LocalDateTime beginTime = LocalDateTimeUtil.beginOfDay(date);
LocalDateTime endTime = LocalDateTimeUtil.endOfDay(date);
String dateStr = DatePattern.NORM_DATE_FORMAT.format(date);
// 2. 检查该日是否已经统计过
TradeStatisticsDO entity = tradeStatisticsMapper.selectByTimeBetween(beginTime, endTime);
if (entity != null) {
return dateStr + " 数据已存在,如果需要重新统计,请先删除对应的数据";
}
// 3. 从各个数据表,统计对应数据
StopWatch stopWatch = new StopWatch(dateStr);
// 3.1 统计订单
stopWatch.start("统计订单");
TradeOrderSummaryRespBO orderSummary = tradeOrderStatisticsService.getOrderSummary(beginTime, endTime);
stopWatch.stop();
// 3.2 统计售后
stopWatch.start("统计售后");
AfterSaleSummaryRespBO afterSaleSummary = afterSaleStatisticsService.getAfterSaleSummary(beginTime, endTime);
stopWatch.stop();
// 3.3 统计佣金
stopWatch.start("统计佣金");
Integer brokerageSettlementPrice = brokerageStatisticsService.getBrokerageSettlementPriceSummary(beginTime, endTime);
stopWatch.stop();
// 3.4 统计充值
stopWatch.start("统计充值");
WalletSummaryRespBO walletSummary = payWalletStatisticsService.getWalletSummary(beginTime, endTime);
stopWatch.stop();
// 4. 插入数据
entity = TradeStatisticsConvert.INSTANCE.convert(date, orderSummary, afterSaleSummary, brokerageSettlementPrice,
walletSummary);
tradeStatisticsMapper.insert(entity);
return stopWatch.prettyPrint();
}
}

View File

@ -0,0 +1,22 @@
package cn.iocoder.yudao.module.statistics.service.trade.bo;
import lombok.Data;
/**
* Response DTO
*
* @author owen
*/
@Data
public class AfterSaleSummaryRespBO {
/**
* 退
*/
private Integer afterSaleCount;
/**
* 退
*/
private Integer afterSaleRefundPrice;
}

View File

@ -0,0 +1,41 @@
package cn.iocoder.yudao.module.statistics.service.trade.bo;
import lombok.Data;
/**
* Response BO
*
* @author owen
*/
@Data
public class MemberAreaStatisticsRespBO {
/**
*
*/
private Integer areaId;
/**
*
*/
private String areaName;
/**
*
*/
private Integer userCount;
/**
*
*/
private Integer orderCreateUserCount;
/**
*
*/
private Integer orderPayUserCount;
/**
*
*/
private Integer orderPayPrice;
}

View File

@ -0,0 +1,26 @@
package cn.iocoder.yudao.module.statistics.service.trade.bo;
import lombok.Data;
/**
* Response BO
*
* @author owen
*/
@Data
public class TradeOrderSummaryRespBO {
/**
*
*/
private Integer orderCreateCount;
/**
*
*/
private Integer orderPayCount;
/**
*
*/
private Integer orderPayPrice;
}

View File

@ -0,0 +1,23 @@
package cn.iocoder.yudao.module.statistics.service.trade.bo;
import lombok.Data;
/**
* Resp BO
*
* @author owen
*/
@Data
public class TradeSummaryRespBO {
/**
*
*/
private Integer count;
/**
*
*/
private Integer summary;
}

View File

@ -0,0 +1,35 @@
package cn.iocoder.yudao.module.statistics.service.trade.bo;
import lombok.Data;
/**
* Response DTO
*
* @author owen
*/
@Data
public class WalletSummaryRespBO {
/**
*
*/
private Integer walletPayPrice;
/**
*
*/
private Integer rechargePayCount;
/**
*
*/
private Integer rechargePayPrice;
/**
* 退
*/
private Integer rechargeRefundCount;
/**
* 退
*/
private Integer rechargeRefundPrice;
}

View File

@ -0,0 +1,113 @@
--- #################### 数据库相关配置 ####################
spring:
# 数据源配置项
autoconfigure:
exclude:
- com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure # 排除 Druid 的自动配置,使用 dynamic-datasource-spring-boot-starter 配置多数据源
datasource:
druid: # Druid 【监控】相关的全局配置
web-stat-filter:
enabled: true
stat-view-servlet:
enabled: true
allow: # 设置白名单,不填则允许所有访问
url-pattern: /druid/*
login-username: # 控制台管理用户名和密码
login-password:
filter:
stat:
enabled: true
log-slow-sql: true # 慢 SQL 记录
slow-sql-millis: 100
merge-sql: true
wall:
config:
multi-statement-allow: true
dynamic: # 多数据源配置
druid: # Druid 【连接池】相关的全局配置
initial-size: 5 # 初始连接数
min-idle: 10 # 最小连接池数量
max-active: 20 # 最大连接池数量
max-wait: 600000 # 配置获取连接等待超时的时间,单位:毫秒
time-between-eviction-runs-millis: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位:毫秒
min-evictable-idle-time-millis: 300000 # 配置一个连接在池中最小生存的时间,单位:毫秒
max-evictable-idle-time-millis: 900000 # 配置一个连接在池中最大生存的时间,单位:毫秒
validation-query: SELECT 1 FROM DUAL # 配置检测连接是否有效
test-while-idle: true
test-on-borrow: false
test-on-return: false
primary: master
datasource:
master:
name: ruoyi-vue-pro
url: jdbc:mysql://400-infra.server.iocoder.cn:3306/${spring.datasource.dynamic.datasource.master.name}?useSSL=false&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT&nullCatalogMeansCurrent=true
driver-class-name: com.mysql.jdbc.Driver
username: root
password: 3WLiVUBEwTbvAfsh
slave: # 模拟从库,可根据自己需要修改 # 模拟从库,可根据自己需要修改
name: ruoyi-vue-pro
url: jdbc:mysql://400-infra.server.iocoder.cn:3306/${spring.datasource.dynamic.datasource.slave.name}?useSSL=false&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT&nullCatalogMeansCurrent=true
driver-class-name: com.mysql.jdbc.Driver
username: root
password: 3WLiVUBEwTbvAfsh
# Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
redis:
host: 400-infra.server.iocoder.cn # 地址
port: 6379 # 端口
database: 1 # 数据库索引
# password: 123456 # 密码,建议生产环境开启
--- #################### MQ 消息队列相关配置 ####################
spring:
cloud:
stream:
rocketmq:
# RocketMQ Binder 配置项,对应 RocketMQBinderConfigurationProperties 类
binder:
name-server: 127.0.0.1:9876 # RocketMQ Namesrv 地址
--- #################### 定时任务相关配置 ####################
xxl:
job:
admin:
addresses: http://127.0.0.1:9090/xxl-job-admin # 调度中心部署跟地址
--- #################### 服务保障相关配置 ####################
# Lock4j 配置项
lock4j:
acquire-timeout: 3000 # 获取分布式锁超时时间,默认为 3000 毫秒
expire: 30000 # 分布式锁的超时时间,默认为 30 毫秒
--- #################### 监控相关配置 ####################
# Actuator 监控端点的配置项
management:
endpoints:
web:
base-path: /actuator # Actuator 提供的 API 接口的根目录。默认为 /actuator
exposure:
include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。
# Spring Boot Admin 配置项
spring:
boot:
admin:
# Spring Boot Admin Client 客户端的相关配置
client:
instance:
service-host-type: IP # 注册实例时,优先使用 IP [IP, HOST_NAME, CANONICAL_HOST_NAME]
# Spring Boot Admin Server 服务端的相关配置
context-path: /admin # 配置 Spring
--- #################### 芋道相关配置 ####################
# 芋道配置项,设置当前项目所有自定义的配置
yudao:
xss:
enable: false
web:
admin-ui:
url: http://dashboard.yudao.iocoder.cn # Admin 管理后台 UI 的地址
demo: true # 开启演示模式

View File

@ -0,0 +1,141 @@
--- #################### 数据库相关配置 ####################
spring:
# 数据源配置项
autoconfigure:
exclude:
- com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure # 排除 Druid 的自动配置,使用 dynamic-datasource-spring-boot-starter 配置多数据源
- de.codecentric.boot.admin.client.config.SpringBootAdminClientAutoConfiguration # 禁用 Spring Boot Admin 的 Client 的自动配置
datasource:
druid: # Druid 【监控】相关的全局配置
web-stat-filter:
enabled: true
stat-view-servlet:
enabled: true
allow: # 设置白名单,不填则允许所有访问
url-pattern: /druid/*
login-username: # 控制台管理用户名和密码
login-password:
filter:
stat:
enabled: true
log-slow-sql: true # 慢 SQL 记录
slow-sql-millis: 100
merge-sql: true
wall:
config:
multi-statement-allow: true
dynamic: # 多数据源配置
druid: # Druid 【连接池】相关的全局配置
initial-size: 1 # 初始连接数
min-idle: 1 # 最小连接池数量
max-active: 20 # 最大连接池数量
max-wait: 600000 # 配置获取连接等待超时的时间,单位:毫秒
time-between-eviction-runs-millis: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位:毫秒
min-evictable-idle-time-millis: 300000 # 配置一个连接在池中最小生存的时间,单位:毫秒
max-evictable-idle-time-millis: 900000 # 配置一个连接在池中最大生存的时间,单位:毫秒
validation-query: SELECT 1 FROM DUAL # 配置检测连接是否有效
test-while-idle: true
test-on-borrow: false
test-on-return: false
primary: master
datasource:
master:
name: ruoyi-vue-pro
url: jdbc:mysql://127.0.0.1:3306/${spring.datasource.dynamic.datasource.master.name}?allowMultiQueries=true&useUnicode=true&useSSL=false&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&autoReconnect=true&nullCatalogMeansCurrent=true # MySQL Connector/J 8.X 连接的示例
# url: jdbc:mysql://127.0.0.1:3306/${spring.datasource.dynamic.datasource.master.name}?useSSL=false&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT # MySQL Connector/J 5.X 连接的示例
# url: jdbc:postgresql://127.0.0.1:5432/${spring.datasource.dynamic.datasource.master.name} # PostgreSQL 连接的示例
# url: jdbc:oracle:thin:@127.0.0.1:1521:xe # Oracle 连接的示例
# url: jdbc:sqlserver://127.0.0.1:1433;DatabaseName=${spring.datasource.dynamic.datasource.master.name} # SQLServer 连接的示例
username: root
password: 123456
# username: sa
# password: JSm:g(*%lU4ZAkz06cd52KqT3)i1?H7W
slave: # 模拟从库,可根据自己需要修改
name: ruoyi-vue-pro
url: jdbc:mysql://127.0.0.1:3306/${spring.datasource.dynamic.datasource.slave.name}?allowMultiQueries=true&useUnicode=true&useSSL=false&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&autoReconnect=true&nullCatalogMeansCurrent=true # MySQL Connector/J 8.X 连接的示例
# url: jdbc:mysql://127.0.0.1:3306/${spring.datasource.dynamic.datasource.slave.name}?useSSL=false&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT # MySQL Connector/J 5.X 连接的示例
# url: jdbc:postgresql://127.0.0.1:5432/${spring.datasource.dynamic.datasource.slave.name} # PostgreSQL 连接的示例
# url: jdbc:oracle:thin:@127.0.0.1:1521:xe # Oracle 连接的示例
# url: jdbc:sqlserver://127.0.0.1:1433;DatabaseName=${spring.datasource.dynamic.datasource.slave.name} # SQLServer 连接的示例
username: root
password: 123456
# username: sa
# password: JSm:g(*%lU4ZAkz06cd52KqT3)i1?H7W
# Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
redis:
host: 127.0.0.1 # 地址
port: 6379 # 端口
database: 0 # 数据库索引
# password: 123456 # 密码,建议生产环境开启
--- #################### MQ 消息队列相关配置 ####################
spring:
cloud:
stream:
rocketmq:
# RocketMQ Binder 配置项,对应 RocketMQBinderConfigurationProperties 类
binder:
name-server: 127.0.0.1:9876 # RocketMQ Namesrv 地址
binding-retry-interval: 7200 # 消息绑定重试间隔时间,单位:秒,默认为 30 秒。考虑到本地可能不启动 RocketMQ 服务,设置为 2 小时
--- #################### 定时任务相关配置 ####################
xxl:
job:
enabled: false # 是否开启调度中心,默认为 true 开启
admin:
addresses: http://127.0.0.1:9090/xxl-job-admin # 调度中心部署跟地址
--- #################### 服务保障相关配置 ####################
# Lock4j 配置项
lock4j:
acquire-timeout: 3000 # 获取分布式锁超时时间,默认为 3000 毫秒
expire: 30000 # 分布式锁的超时时间,默认为 30 毫秒
--- #################### 监控相关配置 ####################
# Actuator 监控端点的配置项
management:
endpoints:
web:
base-path: /actuator # Actuator 提供的 API 接口的根目录。默认为 /actuator
exposure:
include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。
# Spring Boot Admin 配置项
spring:
boot:
admin:
# Spring Boot Admin Client 客户端的相关配置
client:
instance:
service-host-type: IP # 注册实例时,优先使用 IP [IP, HOST_NAME, CANONICAL_HOST_NAME]
# 日志文件配置
logging:
level:
# 配置自己写的 MyBatis Mapper 打印日志
cn.iocoder.yudao.module.system.dal.mysql: debug
cn.iocoder.yudao.module.system.dal.mysql.sensitiveword.SensitiveWordMapper: INFO # 配置 SensitiveWordMapper 的日志级别为 info
cn.iocoder.yudao.module.system.dal.mysql.sms.SmsChannelMapper: INFO # 配置 SmsChannelMapper 的日志级别为 info
--- #################### 芋道相关配置 ####################
# 芋道配置项,设置当前项目所有自定义的配置
yudao:
env: # 多环境的配置项
tag: ${HOSTNAME}
web:
admin-ui:
url: http://dashboard.yudao.iocoder.cn # Admin 管理后台 UI 的地址
security:
mock-enable: true
xss:
enable: false
access-log: # 访问日志的配置项
enable: false
error-code: # 错误码相关配置项
enable: false
demo: false # 关闭演示模式

View File

@ -0,0 +1,134 @@
spring:
main:
allow-circular-references: true # 允许循环依赖,因为项目是三层架构,无法避免这个情况。
allow-bean-definition-overriding: true # 允许 Bean 覆盖,例如说 Feign 等会存在重复定义的服务
# Servlet 配置
servlet:
# 文件上传相关配置项
multipart:
max-file-size: 16MB # 单个文件大小
max-request-size: 32MB # 设置总上传的文件大小
mvc:
pathmatch:
matching-strategy: ANT_PATH_MATCHER # 解决 SpringFox 与 SpringBoot 2.6.x 不兼容的问题,参见 SpringFoxHandlerProviderBeanPostProcessor 类
# Jackson 配置项
jackson:
serialization:
write-dates-as-timestamps: true # 设置 LocalDateTime 的格式,使用时间戳
write-date-timestamps-as-nanoseconds: false # 设置不使用 nanoseconds 的格式。例如说 1611460870.401,而是直接 1611460870401
write-durations-as-timestamps: true # 设置 Duration 的格式,使用时间戳
fail-on-empty-beans: false # 允许序列化无属性的 Bean
# Cache 配置项
cache:
type: REDIS
redis:
time-to-live: 1h # 设置过期时间为 1 小时
--- #################### 接口文档配置 ####################
springdoc:
api-docs:
enabled: true # 1. 是否开启 Swagger 接文档的元数据
path: /v3/api-docs
swagger-ui:
enabled: true # 2.1 是否开启 Swagger 文档的官方 UI 界面
path: /swagger-ui.html
default-flat-param-object: true # 参见 https://doc.xiaominfo.com/docs/faq/v4/knife4j-parameterobject-flat-param 文档
knife4j:
enable: true # 2.2 是否开启 Swagger 文档的 Knife4j UI 界面
setting:
language: zh_cn
# MyBatis Plus 的配置项
mybatis-plus:
configuration:
map-underscore-to-camel-case: true # 虽然默认为 true ,但是还是显示去指定下。
global-config:
db-config:
id-type: NONE # “智能”模式,基于 IdTypeEnvironmentPostProcessor + 数据源的类型,自动适配成 AUTO、INPUT 模式。
# id-type: AUTO # 自增 ID适合 MySQL 等直接自增的数据库
# id-type: INPUT # 用户输入 ID适合 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库
# id-type: ASSIGN_ID # 分配 ID默认使用雪花算法。注意Oracle、PostgreSQL、Kingbase、DB2、H2 数据库时,需要去除实体类上的 @KeySequence 注解
logic-delete-value: 1 # 逻辑已删除值(默认为 1)
logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
banner: false # 关闭控制台的 Banner 打印
type-aliases-package: ${yudao.info.base-package}.module.*.dal.dataobject
encryptor:
password: XDV71a+xqStEA3WH # 加解密的秘钥,可使用 https://www.imaegoo.com/2020/aes-key-generator/ 网站生成
mybatis-plus-join:
banner: false # 关闭控制台的 Banner 打印
# Spring Data Redis 配置
spring:
data:
redis:
repositories:
enabled: false # 项目未使用到 Spring Data Redis 的 Repository所以直接禁用保证启动速度
--- #################### RPC 远程调用相关配置 ####################
--- #################### MQ 消息队列相关配置 ####################
spring:
cloud:
# Spring Cloud Stream 配置项,对应 BindingServiceProperties 类
stream:
function:
definition: busConsumer;
# Binding 配置项,对应 BindingProperties Map
# Spring Cloud Stream RocketMQ 配置项
rocketmq:
# RocketMQ Binder 配置项,对应 RocketMQBinderConfigurationProperties 类
binder:
name-server: 127.0.0.1:9876 # RocketMQ Namesrv 地址
default: # 默认 bindings 全局配置
producer: # RocketMQ Producer 配置项,对应 RocketMQProducerProperties 类
group: statistics_producer_group # 生产者分组
send-type: SYNC # 发送模式SYNC 同步
bindings:
springCloudBusInput:
consumer:
message-model: BROADCASTING # 重要,解决 Spring Cloud Bus RocketMQ 默认不是 BROADCASTING 广播消费的问题
# Spring Cloud Bus 配置项,对应 BusProperties 类
bus:
enabled: true # 是否开启,默认为 true
id: ${spring.application.name}:${server.port} # 编号Spring Cloud Alibaba 建议使用“应用:端口”的格式
destination: springCloudBus # 目标消息队列,默认为 springCloudBus
--- #################### 定时任务相关配置 ####################
xxl:
job:
executor:
appname: ${spring.application.name} # 执行器 AppName
logpath: ${user.home}/logs/xxl-job/${spring.application.name} # 执行器运行日志文件存储磁盘路径
accessToken: default_token # 执行器通讯TOKEN
--- #################### 芋道相关配置 ####################
yudao:
info:
version: 1.0.0
base-package: cn.iocoder.yudao.module.statistics
swagger:
title: 管理后台
description: 提供管理员管理的所有功能
version: ${yudao.info.version}
base-package: ${yudao.info.base-package}
captcha:
enable: true # 验证码的开关,默认为 true
error-code: # 错误码相关配置项
constants-class-list:
- cn.iocoder.yudao.module.statistics.enums.ErrorCodeConstants
tenant: # 多租户相关配置项
enable: true
ignore-urls:
ignore-tables:
debug: false

View File

@ -0,0 +1,23 @@
--- #################### 注册中心相关配置 ####################
spring:
cloud:
nacos:
server-addr: 127.0.0.1:8848
discovery:
namespace: dev # 命名空间。这里使用 dev 开发环境
metadata:
version: 1.0.0 # 服务实例的版本号,可用于灰度发布
--- #################### 配置中心相关配置 ####################
spring:
cloud:
nacos:
# Nacos Config 配置项,对应 NacosConfigProperties 配置属性类
config:
server-addr: 127.0.0.1:8848 # Nacos 服务器地址
namespace: dev # 命名空间 dev 的ID不能直接使用 dev 名称。创建命名空间的时候需要指定ID为 dev这里使用 dev 开发环境
group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP
name: ${spring.application.name} # 使用的 Nacos 配置集的 dataId默认为 spring.application.name
file-extension: yaml # 使用的 Nacos 配置集的 dataId 的文件拓展名,同时也是 Nacos 配置集的配置格式,默认为 properties

View File

@ -0,0 +1,14 @@
spring:
application:
name: statistics-server
profiles:
active: local
server:
port: 48103
# 日志文件配置。注意,如果 logging.file.name 不放在 bootstrap.yaml 配置文件,而是放在 application.yaml 中,会导致出现 LOG_FILE_IS_UNDEFINED 文件
logging:
file:
name: ${user.home}/logs/${spring.application.name}.log # 日志文件名,全路径

View File

@ -0,0 +1,76 @@
<configuration>
<!-- 引用 Spring Boot 的 logback 基础配置 -->
<include resource="org/springframework/boot/logging/logback/defaults.xml" />
<!-- 变量 yudao.info.base-package基础业务包 -->
<springProperty scope="context" name="yudao.info.base-package" source="yudao.info.base-package"/>
<!-- 格式化输出:%d 表示日期,%X{tid} SkWalking 链路追踪编号,%thread 表示线程名,%-5level级别从左显示 5 个字符宽度,%msg日志消息%n是换行符 -->
<property name="PATTERN_DEFAULT" value="%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}} | %highlight(${LOG_LEVEL_PATTERN:-%5p} ${PID:- }) | %boldYellow(%thread [%tid]) %boldGreen(%-40.40logger{39}) | %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}"/>
<!-- 控制台 Appender -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">     
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">
<pattern>${PATTERN_DEFAULT}</pattern>
</layout>
</encoder>
</appender>
<!-- 文件 Appender -->
<!-- 参考 Spring Boot 的 file-appender.xml 编写 -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">
<pattern>${PATTERN_DEFAULT}</pattern>
</layout>
</encoder>
<!-- 日志文件名 -->
<file>${LOG_FILE}</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!-- 滚动后的日志文件名 -->
<fileNamePattern>${LOGBACK_ROLLINGPOLICY_FILE_NAME_PATTERN:-${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz}</fileNamePattern>
<!-- 启动服务时,是否清理历史日志,一般不建议清理 -->
<cleanHistoryOnStart>${LOGBACK_ROLLINGPOLICY_CLEAN_HISTORY_ON_START:-false}</cleanHistoryOnStart>
<!-- 日志文件,到达多少容量,进行滚动 -->
<maxFileSize>${LOGBACK_ROLLINGPOLICY_MAX_FILE_SIZE:-10MB}</maxFileSize>
<!-- 日志文件的总大小0 表示不限制 -->
<totalSizeCap>${LOGBACK_ROLLINGPOLICY_TOTAL_SIZE_CAP:-0}</totalSizeCap>
<!-- 日志文件的保留天数 -->
<maxHistory>${LOGBACK_ROLLINGPOLICY_MAX_HISTORY:-30}</maxHistory>
</rollingPolicy>
</appender>
<!-- 异步写入日志,提升性能 -->
<appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender">
<!-- 不丢失日志。默认的,如果队列的 80% 已满,则会丢弃 TRACT、DEBUG、INFO 级别的日志 -->
<discardingThreshold>0</discardingThreshold>
<!-- 更改默认的队列的深度,该值会影响性能。默认值为 256 -->
<queueSize>256</queueSize>
<appender-ref ref="FILE"/>
</appender>
<!-- SkyWalking GRPC 日志收集实现日志中心。注意SkyWalking 8.4.0 版本开始支持 -->
<appender name="GRPC" class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.log.GRPCLogClientAppender">
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">
<pattern>${PATTERN_DEFAULT}</pattern>
</layout>
</encoder>
</appender>
<!-- 本地环境 -->
<springProfile name="local">
<root level="INFO">
<appender-ref ref="STDOUT"/>
<appender-ref ref="GRPC"/> <!-- 本地环境下,如果不想接入 SkyWalking 日志服务,可以注释掉本行 -->
<appender-ref ref="ASYNC"/> <!-- 本地环境下,如果不想打印日志,可以注释掉本行 -->
</root>
</springProfile>
<!-- 其它环境 -->
<springProfile name="dev,test,stage,prod,default">
<root level="INFO">
<appender-ref ref="STDOUT"/>
<appender-ref ref="ASYNC"/>
<appender-ref ref="GRPC"/>
</root>
</springProfile>
</configuration>

View File

@ -0,0 +1,22 @@
<?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.infra.ApiAccessLogStatisticsMapper">
<select id="selectIpCountByUserTypeAndCreateTimeBetween" resultType="java.lang.Integer">
SELECT COUNT(DISTINCT user_ip)
FROM infra_api_access_log
WHERE user_type = #{userType}
AND create_time BETWEEN #{beginTime} AND #{endTime}
AND deleted = FALSE
</select>
<select id="selectUserCountByUserTypeAndCreateTimeBetween" resultType="java.lang.Integer">
SELECT COUNT(DISTINCT user_id)
FROM infra_api_access_log
WHERE user_id > 0
AND user_type = #{userType}
AND create_time BETWEEN #{beginTime} AND #{endTime}
AND deleted = FALSE
</select>
</mapper>

View File

@ -0,0 +1,51 @@
<?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.member.MemberStatisticsMapper">
<select id="selectSummaryListByAreaId"
resultType="cn.iocoder.yudao.module.statistics.service.member.bo.MemberAreaStatisticsRespBO">
SELECT area_id, COUNT(1) AS userCount
FROM member_user
WHERE deleted = FALSE
GROUP BY area_id
</select>
<select id="selectSummaryListBySex"
resultType="cn.iocoder.yudao.module.statistics.controller.admin.member.vo.MemberSexStatisticsRespVO">
SELECT sex, COUNT(1) AS userCount
FROM member_user
WHERE deleted = FALSE
GROUP BY sex
</select>
<select id="selectSummaryListByRegisterTerminal"
resultType="cn.iocoder.yudao.module.statistics.controller.admin.member.vo.MemberTerminalStatisticsRespVO">
SELECT register_terminal as terminal, COUNT(1) AS userCount
FROM member_user
WHERE deleted = FALSE
GROUP BY register_terminal
</select>
<select id="selectUserCount" resultType="java.lang.Integer">
SELECT COUNT(1)
FROM member_user
WHERE deleted = FALSE
<if test="beginTime != null">
AND create_time >= #{beginTime}
</if>
<if test="endTime != null">
AND create_time &lt;= #{endTime}
</if>
</select>
<select id="selectListByCreateTimeBetween"
resultType="cn.iocoder.yudao.module.statistics.controller.admin.member.vo.MemberRegisterCountRespVO">
SELECT DATE_FORMAT(create_time, '%Y-%m-%d') AS date,
count(1) AS count
FROM member_user
WHERE create_time BETWEEN #{beginTime} AND #{endTime}
AND deleted = FALSE
GROUP BY date
</select>
</mapper>

View File

@ -0,0 +1,55 @@
<?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.pay.PayWalletStatisticsMapper">
<select id="selectRechargeSummaryByPayTimeBetween"
resultType="cn.iocoder.yudao.module.statistics.service.trade.bo.WalletSummaryRespBO">
SELECT COUNT(1) AS rechargePayCount,
SUM(pay_price) AS rechargePayPrice
FROM pay_wallet_recharge
WHERE pay_status = #{payStatus}
AND pay_time BETWEEN #{beginTime} AND #{endTime}
AND deleted = FALSE
</select>
<select id="selectRechargeSummaryByRefundTimeBetween"
resultType="cn.iocoder.yudao.module.statistics.service.trade.bo.WalletSummaryRespBO">
SELECT COUNT(1) AS rechargeRefundCount,
SUM(pay_price) AS rechargeRefundPrice
FROM pay_wallet_recharge
WHERE refund_status = #{refundStatus}
AND refund_time BETWEEN #{beginTime} AND #{endTime}
AND deleted = FALSE
</select>
<select id="selectPriceSummaryByBizTypeAndCreateTimeBetween" resultType="java.lang.Integer">
SELECT SUM(price)
FROM pay_wallet_transaction
WHERE biz_type = #{bizType}
AND create_time BETWEEN #{beginTime} AND #{endTime}
AND deleted = FALSE
</select>
<select id="selectRechargeSummaryGroupByWalletId"
resultType="cn.iocoder.yudao.module.statistics.service.pay.bo.RechargeSummaryRespBO">
SELECT COUNT(DISTINCT wallet_id) AS rechargeUserCount,
SUM(pay_price) AS rechargePrice
FROM pay_wallet_recharge
WHERE pay_status = #{payStatus}
<if test="beginTime != null">
AND pay_time >= #{beginTime}
</if>
<if test="endTime != null">
AND pay_time &lt;= #{endTime}
</if>
AND deleted = FALSE
</select>
<select id="selectRechargePriceSummary" resultType="java.lang.Integer">
SELECT IFNULL(SUM(pay_price), 0)
FROM pay_wallet_recharge
WHERE pay_status = #{payStatus}
AND deleted = FALSE
</select>
</mapper>

View File

@ -0,0 +1,21 @@
<?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.trade.AfterSaleStatisticsMapper">
<select id="selectSummaryByRefundTimeBetween"
resultType="cn.iocoder.yudao.module.statistics.service.trade.bo.AfterSaleSummaryRespBO">
SELECT COUNT(1) AS afterSaleCount,
SUM(refund_price) AS afterSaleRefundPrice
FROM trade_after_sale
WHERE refund_time BETWEEN #{beginTime} AND #{endTime}
AND deleted = FALSE
</select>
<select id="selectCountByStatus" resultType="java.lang.Long">
SELECT COUNT(1)
FROM trade_after_sale
WHERE status = #{status}
AND deleted = FALSE
</select>
</mapper>

View File

@ -0,0 +1,21 @@
<?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.trade.BrokerageStatisticsMapper">
<select id="selectSummaryPriceByStatusAndUnfreezeTimeBetween" resultType="java.lang.Integer">
SELECT SUM(price)
FROM trade_brokerage_record
WHERE biz_type = #{bizType}
AND status = #{status}
AND unfreeze_time BETWEEN #{beginTime} AND #{endTime}
AND deleted = FALSE
</select>
<select id="selectWithdrawCountByStatus" resultType="java.lang.Long">
SELECT COUNT(1)
FROM trade_brokerage_withdraw
WHERE status = #{status}
AND deleted = FALSE
</select>
</mapper>

View File

@ -0,0 +1,106 @@
<?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.trade.TradeOrderStatisticsMapper">
<select id="selectSummaryListByAreaId"
resultType="cn.iocoder.yudao.module.statistics.service.member.bo.MemberAreaStatisticsRespBO">
SELECT receiver_area_id AS areaId,
(SELECT COUNT(DISTINCT s.user_id)
FROM trade_order AS s
WHERE s.receiver_area_id = m.receiver_area_id) AS orderCreateUserCount,
(SELECT COUNT(DISTINCT s.user_id)
FROM trade_order AS s
WHERE s.receiver_area_id = m.receiver_area_id
AND s.pay_status = TRUE
AND s.deleted = FALSE) AS orderPayUserCount,
(SELECT SUM(s.pay_price)
FROM trade_order AS s
WHERE s.receiver_area_id = m.receiver_area_id
AND s.pay_status = TRUE
AND s.deleted = FALSE) AS orderPayPrice
FROM trade_order m
WHERE deleted = FALSE
GROUP BY receiver_area_id
</select>
<select id="selectUserCountByCreateTimeBetween" resultType="java.lang.Integer">
SELECT COUNT(DISTINCT (user_id))
FROM trade_order
WHERE deleted = FALSE
AND create_time BETWEEN #{beginTime} AND #{endTime}
</select>
<select id="selectUserCountByPayTimeBetween" resultType="java.lang.Integer">
SELECT COUNT(DISTINCT (user_id))
FROM trade_order
WHERE pay_time BETWEEN #{beginTime} AND #{endTime}
AND pay_status = TRUE
AND deleted = FALSE
</select>
<select id="selectCountByCreateTimeBetween" resultType="java.lang.Integer">
SELECT COUNT(1)
FROM trade_order
WHERE create_time BETWEEN #{beginTime} AND #{endTime}
AND deleted = FALSE
</select>
<select id="selectCountByPayTimeBetween" resultType="java.lang.Integer">
SELECT COUNT(1)
FROM trade_order
WHERE pay_status = TRUE
AND create_time BETWEEN #{beginTime} AND #{endTime}
AND deleted = FALSE
</select>
<select id="selectSummaryPriceByPayTimeBetween" resultType="java.lang.Integer">
SELECT SUM(pay_price)
FROM trade_order AS s
WHERE s.pay_status = TRUE
AND deleted = FALSE
AND create_time BETWEEN #{beginTime} AND #{endTime}
</select>
<select id="selectListByPayTimeBetweenAndGroupByDay"
resultType="cn.iocoder.yudao.module.statistics.controller.admin.trade.vo.TradeOrderTrendRespVO">
SELECT DATE_FORMAT(pay_time, '%Y-%m-%d') AS date,
COUNT(1) AS orderPayCount,
SUM(pay_price) AS orderPayPrice
FROM trade_order
WHERE pay_status = TRUE
AND create_time BETWEEN #{beginTime} AND #{endTime}
AND deleted = FALSE
GROUP BY date
</select>
<select id="selectListByPayTimeBetweenAndGroupByMonth"
resultType="cn.iocoder.yudao.module.statistics.controller.admin.trade.vo.TradeOrderTrendRespVO">
SELECT DATE_FORMAT(pay_time, '%Y-%m') AS date,
COUNT(1) AS orderPayCount,
SUM(pay_price) AS orderPayPrice
FROM trade_order
WHERE pay_status = TRUE
AND create_time BETWEEN #{beginTime} AND #{endTime}
AND deleted = FALSE
GROUP BY date
</select>
<select id="selectCountByStatusAndDeliveryType" resultType="java.lang.Long">
SELECT COUNT(1)
FROM trade_order
WHERE status = #{status}
ANd delivery_type = #{deliveryType}
AND deleted = FALSE
</select>
<select id="selectPaySummaryByStatusAndPayTimeBetween"
resultType="cn.iocoder.yudao.module.statistics.controller.admin.trade.vo.TradeOrderSummaryRespVO">
SELECT IFNULL(SUM(pay_price), 0) AS orderPayPrice,
COUNT(1) AS orderPayCount
FROM trade_order
WHERE status = #{status}
AND pay_time BETWEEN #{beginTime} AND #{endTime}
AND deleted = FALSE
</select>
</mapper>

View File

@ -0,0 +1,44 @@
<?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.trade.TradeStatisticsMapper">
<select id="selectOrderCreateCountSumAndOrderPayPriceSumByTimeBetween"
resultType="cn.iocoder.yudao.module.statistics.service.trade.bo.TradeSummaryRespBO">
SELECT IFNULL(SUM(order_create_count), 0) AS count,
IFNULL(SUM(order_pay_price), 0) AS summary
FROM trade_statistics
WHERE time BETWEEN #{beginTime} AND #{endTime}
AND deleted = FALSE
</select>
<select id="selectVoByTimeBetween"
resultType="cn.iocoder.yudao.module.statistics.controller.admin.trade.vo.TradeTrendSummaryRespVO">
SELECT
-- 营业额 = 商品支付金额 + 充值金额
SUM(order_pay_price + recharge_pay_price) AS turnoverPrice,
SUM(order_pay_price) AS orderPayPrice,
SUM(recharge_pay_price) AS rechargePrice,
-- 支出金额 = 余额支付金额 + 支付佣金金额 + 商品退款金额
SUM(wallet_pay_price + brokerage_settlement_price + after_sale_refund_price) AS expensePrice,
SUM(wallet_pay_price) AS walletPayPrice,
SUM(brokerage_settlement_price) AS brokerageSettlementPrice,
SUM(after_sale_refund_price) AS afterSaleRefundPrice
FROM trade_statistics
WHERE time BETWEEN #{beginTime} AND #{endTime}
AND deleted = FALSE
</select>
<select id="selectExpensePriceByTimeBetween" resultType="java.lang.Integer">
SELECT -- 支出金额 = 余额支付金额 + 支付佣金金额 + 商品退款金额
SUM(wallet_pay_price + brokerage_settlement_price + after_sale_refund_price) AS expensePrice
FROM trade_statistics
WHERE deleted = FALSE
<if test="beginTime != null">
AND time >= #{beginTime}
</if>
<if test="endTime != null">
AND time &lt;= #{endTime}
</if>
</select>
</mapper>

View File

@ -0,0 +1,40 @@
package cn.iocoder.yudao.module.pay.enums.wallet;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
/**
*
*
* @author jason
*/
@AllArgsConstructor
@Getter
public enum PayWalletBizTypeEnum implements IntArrayValuable {
RECHARGE(1, "充值"),
RECHARGE_REFUND(2, "充值退款"),
PAYMENT(3, "支付"),
PAYMENT_REFUND(4, "支付退款");
// TODO 后续增加
/**
*
*/
private final Integer type;
/**
*
*/
private final String description;
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(PayWalletBizTypeEnum::getType).toArray();
@Override
public int[] array() {
return ARRAYS;
}
}