reactor:移除 mall-statistics 的 api 包

This commit is contained in:
YunaiV
2025-05-17 10:48:53 +08:00
parent 5f6d0b3b19
commit 1519c03c49
81 changed files with 11 additions and 52 deletions

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,114 @@
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 jakarta.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;
@GetMapping("/summary")
@Operation(summary = "获得会员统计(实时统计)")
@PreAuthorize("@ss.hasPermission('statistics:member:query')")
public CommonResult<MemberSummaryRespVO> getMemberSummary() {
return success(memberStatisticsService.getMemberSummary());
}
@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));
}
@GetMapping("/area-statistics-list")
@Operation(summary = "按照省份,获得会员统计列表")
@PreAuthorize("@ss.hasPermission('statistics:member:query')")
public CommonResult<List<MemberAreaStatisticsRespVO>> getMemberAreaStatisticsList() {
return success(memberStatisticsService.getMemberAreaStatisticsList());
}
@GetMapping("/sex-statistics-list")
@Operation(summary = "按照性别,获得会员统计列表")
@PreAuthorize("@ss.hasPermission('statistics:member:query')")
public CommonResult<List<MemberSexStatisticsRespVO>> getMemberSexStatisticsList() {
return success(memberStatisticsService.getMemberSexStatisticsList());
}
@GetMapping("/terminal-statistics-list")
@Operation(summary = "按照终端,获得会员统计列表")
@PreAuthorize("@ss.hasPermission('statistics:member:query')")
public CommonResult<List<MemberTerminalStatisticsRespVO>> getMemberTerminalStatisticsList() {
return success(memberStatisticsService.getMemberTerminalStatisticsList());
}
// TODO @疯狂:要注意 date 的排序;
@GetMapping("/user-count-comparison")
@Operation(summary = "获得用户数量对照")
@PreAuthorize("@ss.hasPermission('statistics:member:query')")
public CommonResult<DataComparisonRespVO<MemberCountRespVO>> getUserCountComparison() {
return success(memberStatisticsService.getUserCountComparison());
}
@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 jakarta.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,87 @@
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.framework.common.pojo.SortablePageParam;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;
import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
import cn.iocoder.yudao.module.statistics.controller.admin.common.vo.DataComparisonRespVO;
import cn.iocoder.yudao.module.statistics.controller.admin.product.vo.ProductStatisticsReqVO;
import cn.iocoder.yudao.module.statistics.controller.admin.product.vo.ProductStatisticsRespVO;
import cn.iocoder.yudao.module.statistics.dal.dataobject.product.ProductStatisticsDO;
import cn.iocoder.yudao.module.statistics.service.product.ProductStatisticsService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
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 java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
@Tag(name = "管理后台 - 商品统计")
@RestController
@RequestMapping("/statistics/product")
@Validated
public class ProductStatisticsController {
@Resource
private ProductStatisticsService productStatisticsService;
@Resource
private ProductSpuApi productSpuApi;
@GetMapping("/analyse")
@Operation(summary = "获得商品统计分析")
@PreAuthorize("@ss.hasPermission('statistics:product:query')")
public CommonResult<DataComparisonRespVO<ProductStatisticsRespVO>> getProductStatisticsAnalyse(ProductStatisticsReqVO reqVO) {
return success(productStatisticsService.getProductStatisticsAnalyse(reqVO));
}
@GetMapping("/list")
@Operation(summary = "获得商品统计明细(日期维度)")
@PreAuthorize("@ss.hasPermission('statistics:product:query')")
public CommonResult<List<ProductStatisticsRespVO>> getProductStatisticsList(ProductStatisticsReqVO reqVO) {
List<ProductStatisticsDO> list = productStatisticsService.getProductStatisticsList(reqVO);
return success(BeanUtils.toBean(list, ProductStatisticsRespVO.class));
}
@GetMapping("/export-excel")
@Operation(summary = "导出获得商品统计明细 Excel日期维度")
@PreAuthorize("@ss.hasPermission('statistics:product:export')")
public void exportProductStatisticsExcel(ProductStatisticsReqVO reqVO, HttpServletResponse response) throws IOException {
List<ProductStatisticsDO> list = productStatisticsService.getProductStatisticsList(reqVO);
// 导出 Excel
List<ProductStatisticsRespVO> voList = BeanUtils.toBean(list, ProductStatisticsRespVO.class);
ExcelUtils.write(response, "商品状况.xls", "数据", ProductStatisticsRespVO.class, voList);
}
@GetMapping("/rank-page")
@Operation(summary = "获得商品统计排行榜分页(商品维度)")
@PreAuthorize("@ss.hasPermission('statistics:product:query')")
public CommonResult<PageResult<ProductStatisticsRespVO>> getProductStatisticsRankPage(@Valid ProductStatisticsReqVO reqVO,
@Valid SortablePageParam pageParam) {
PageResult<ProductStatisticsDO> pageResult = productStatisticsService.getProductStatisticsRankPage(reqVO, pageParam);
// 处理商品信息
Set<Long> spuIds = convertSet(pageResult.getList(), ProductStatisticsDO::getSpuId);
Map<Long, ProductSpuRespDTO> spuMap = convertMap(productSpuApi.getSpuList(spuIds), ProductSpuRespDTO::getId);
return success(BeanUtils.toBean(pageResult, ProductStatisticsRespVO.class,
item -> Optional.ofNullable(spuMap.get(item.getSpuId()))
.ifPresent(spu -> item.setName(spu.getName()).setPicUrl(spu.getPicUrl()))));
}
}

View File

@@ -0,0 +1,25 @@
package cn.iocoder.yudao.module.statistics.controller.admin.product.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
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
@ToString(callSuper = true)
@AllArgsConstructor
@NoArgsConstructor
public class ProductStatisticsReqVO {
@Schema(description = "统计时间范围", example = "[2022-07-01 00:00:00, 2022-07-01 23:59:59]")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] times;
}

View File

@@ -0,0 +1,81 @@
package cn.iocoder.yudao.module.statistics.controller.admin.product.vo;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
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
@ExcelIgnoreUnannotated
public class ProductStatisticsRespVO {
@Schema(description = "编号,主键自增", requiredMode = Schema.RequiredMode.REQUIRED, example = "12393")
private Long id;
@Schema(description = "统计日期", requiredMode = Schema.RequiredMode.REQUIRED, example = "2023-12-16")
@JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY)
@ExcelProperty("统计日期")
private LocalDate time;
@Schema(description = "商品SPU编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "15114")
@ExcelProperty("商品SPU编号")
private Long spuId;
// region 商品信息
@Schema(description = "商品名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "商品名称")
@ExcelProperty("商品名称")
private String name;
@Schema(description = "商品封面图", requiredMode = Schema.RequiredMode.REQUIRED, example = "15114")
@ExcelProperty("商品封面图")
private String picUrl;
// endregion
@Schema(description = "浏览量", requiredMode = Schema.RequiredMode.REQUIRED, example = "17505")
@ExcelProperty("浏览量")
private Integer browseCount;
@Schema(description = "访客量", requiredMode = Schema.RequiredMode.REQUIRED, example = "11814")
@ExcelProperty("访客量")
private Integer browseUserCount;
@Schema(description = "收藏数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "20950")
@ExcelProperty("收藏数量")
private Integer favoriteCount;
@Schema(description = "加购数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "28493")
@ExcelProperty("加购数量")
private Integer cartCount;
@Schema(description = "下单件数", requiredMode = Schema.RequiredMode.REQUIRED, example = "18966")
@ExcelProperty("下单件数")
private Integer orderCount;
@Schema(description = "支付件数", requiredMode = Schema.RequiredMode.REQUIRED, example = "15142")
@ExcelProperty("支付件数")
private Integer orderPayCount;
@Schema(description = "支付金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "11595")
@ExcelProperty("支付金额,单位:分")
private Integer orderPayPrice;
@Schema(description = "退款件数", requiredMode = Schema.RequiredMode.REQUIRED, example = "2591")
@ExcelProperty("退款件数")
private Integer afterSaleCount;
@Schema(description = "退款金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "21709")
@ExcelProperty("退款金额,单位:分")
private Integer afterSaleRefundPrice;
@Schema(description = "访客支付转化率(百分比)", requiredMode = Schema.RequiredMode.REQUIRED, example = "15")
private Integer browseConvertPercent;
}

View File

@@ -0,0 +1,130 @@
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 jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
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 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;
@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));
}
@GetMapping("/analyse")
@Operation(summary = "获得交易状况统计")
@PreAuthorize("@ss.hasPermission('statistics:trade:query')")
public CommonResult<DataComparisonRespVO<TradeTrendSummaryRespVO>> getTradeStatisticsAnalyse(TradeTrendReqVO reqVO) {
return success(tradeStatisticsService.getTradeStatisticsAnalyse(ArrayUtil.get(reqVO.getTimes(), 0),
ArrayUtil.get(reqVO.getTimes(), 1)));
}
@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));
}
@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);
}
@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));
}
@GetMapping("/order-comparison")
@Operation(summary = "获得交易订单数量")
@PreAuthorize("@ss.hasPermission('statistics:trade:query')")
public CommonResult<DataComparisonRespVO<TradeOrderSummaryRespVO>> getOrderComparison() {
return success(tradeOrderStatisticsService.getOrderComparison());
}
@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 jakarta.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 = "2023-12-16")
@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,4 @@
/**
* 占位 todo
*/
package cn.iocoder.yudao.module.statistics.dal.dataobject;

View File

@@ -0,0 +1,80 @@
package cn.iocoder.yudao.module.statistics.dal.dataobject.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.LocalDate;
/**
* 商品统计 DO
*
* @author owen
*/
@TableName("product_statistics")
@KeySequence("product_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 LocalDate time;
/**
* 商品 SPU 编号
*/
private Long spuId;
/**
* 浏览量
*/
private Integer browseCount;
/**
* 访客量
*/
private Integer browseUserCount;
/**
* 收藏数量
*/
private Integer favoriteCount;
/**
* 加购数量
*/
private Integer cartCount;
/**
* 下单件数
*/
private Integer orderCount;
/**
* 支付件数
*/
private Integer orderPayCount;
/**
* 支付金额,单位:分
*/
private Integer orderPayPrice;
/**
* 退款件数
*/
private Integer afterSaleCount;
/**
* 退款金额,单位:分
*/
private Integer afterSaleRefundPrice;
/**
* 访客支付转化率(百分比)
*/
private Integer browseConvertPercent;
}

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,27 @@
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 {
Integer selectIpCountByUserTypeAndCreateTimeBetween(@Param("userType") Integer userType,
@Param("beginTime") LocalDateTime beginTime,
@Param("endTime") LocalDateTime endTime);
Integer selectUserCountByUserTypeAndCreateTimeBetween(@Param("userType") Integer userType,
@Param("beginTime") LocalDateTime beginTime,
@Param("endTime") LocalDateTime endTime);
}

View File

@@ -0,0 +1,42 @@
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 {
List<MemberAreaStatisticsRespBO> selectSummaryListByAreaId();
List<MemberSexStatisticsRespVO> selectSummaryListBySex();
List<MemberTerminalStatisticsRespVO> selectSummaryListByRegisterTerminal();
Integer selectUserCount(@Param("beginTime") LocalDateTime beginTime,
@Param("endTime") LocalDateTime endTime);
/**
* 获得用户的每天注册数量列表
*
* @param beginTime 开始时间
* @param endTime 结束时间
* @return 每天注册数量列表
*/
List<MemberRegisterCountRespVO> selectListByCreateTimeBetween(@Param("beginTime") LocalDateTime beginTime,
@Param("endTime") LocalDateTime endTime);
}

View File

@@ -0,0 +1,38 @@
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 {
WalletSummaryRespBO selectRechargeSummaryByPayTimeBetween(@Param("beginTime") LocalDateTime beginTime,
@Param("endTime") LocalDateTime endTime,
@Param("payStatus") Boolean payStatus);
WalletSummaryRespBO selectRechargeSummaryByRefundTimeBetween(@Param("beginTime") LocalDateTime beginTime,
@Param("endTime") LocalDateTime endTime,
@Param("refundStatus") Integer refundStatus);
Integer selectPriceSummaryByBizTypeAndCreateTimeBetween(@Param("beginTime") LocalDateTime beginTime,
@Param("endTime") LocalDateTime endTime,
@Param("bizType") Integer bizType);
RechargeSummaryRespBO selectRechargeSummaryGroupByWalletId(@Param("beginTime") LocalDateTime beginTime,
@Param("endTime") LocalDateTime endTime,
@Param("payStatus") Boolean payStatus);
Integer selectRechargePriceSummary(@Param("payStatus") Boolean payStatus);
}

View File

@@ -0,0 +1,80 @@
package cn.iocoder.yudao.module.statistics.dal.mysql.product;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.SortablePageParam;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX;
import cn.iocoder.yudao.module.statistics.controller.admin.product.vo.ProductStatisticsReqVO;
import cn.iocoder.yudao.module.statistics.controller.admin.product.vo.ProductStatisticsRespVO;
import cn.iocoder.yudao.module.statistics.dal.dataobject.product.ProductStatisticsDO;
import com.baomidou.mybatisplus.core.metadata.IPage;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.time.LocalDateTime;
import java.util.List;
/**
* 商品统计 Mapper
*
* @author owen
*/
@Mapper
public interface ProductStatisticsMapper extends BaseMapperX<ProductStatisticsDO> {
default PageResult<ProductStatisticsDO> selectPageGroupBySpuId(ProductStatisticsReqVO reqVO, SortablePageParam pageParam) {
return selectPage(pageParam, buildWrapper(reqVO)
.groupBy(ProductStatisticsDO::getSpuId)
.select(ProductStatisticsDO::getSpuId)
);
}
default List<ProductStatisticsDO> selectListByTimeBetween(ProductStatisticsReqVO reqVO) {
return selectList(buildWrapper(reqVO)
.groupBy(ProductStatisticsDO::getTime)
.select(ProductStatisticsDO::getTime));
}
default ProductStatisticsRespVO selectVoByTimeBetween(ProductStatisticsReqVO reqVO) {
return selectJoinOne(ProductStatisticsRespVO.class, buildWrapper(reqVO));
}
/**
* 构建 LambdaWrapper
*
* @param reqVO 查询参数
* @return LambdaWrapper
*/
private static MPJLambdaWrapperX<ProductStatisticsDO> buildWrapper(ProductStatisticsReqVO reqVO) {
return new MPJLambdaWrapperX<ProductStatisticsDO>()
.betweenIfPresent(ProductStatisticsDO::getTime, reqVO.getTimes())
.selectSum(ProductStatisticsDO::getBrowseCount)
.selectSum(ProductStatisticsDO::getBrowseUserCount)
.selectSum(ProductStatisticsDO::getFavoriteCount)
.selectSum(ProductStatisticsDO::getCartCount)
.selectSum(ProductStatisticsDO::getOrderCount)
.selectSum(ProductStatisticsDO::getOrderPayCount)
.selectSum(ProductStatisticsDO::getOrderPayPrice)
.selectSum(ProductStatisticsDO::getAfterSaleCount)
.selectSum(ProductStatisticsDO::getAfterSaleRefundPrice)
.selectAvg(ProductStatisticsDO::getBrowseConvertPercent);
}
/**
* 根据时间范围统计商品信息
*
* @param page 分页参数
* @param beginTime 起始时间
* @param endTime 截止时间
* @return 统计
*/
IPage<ProductStatisticsDO> selectStatisticsResultPageByTimeBetween(IPage<ProductStatisticsDO> page,
@Param("beginTime") LocalDateTime beginTime,
@Param("endTime") LocalDateTime endTime);
default Long selectCountByTimeBetween(LocalDateTime beginTime, LocalDateTime endTime) {
return selectCount(new LambdaQueryWrapperX<ProductStatisticsDO>().between(ProductStatisticsDO::getTime, beginTime, endTime));
}
}

View File

@@ -0,0 +1,24 @@
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> {
AfterSaleSummaryRespBO selectSummaryByRefundTimeBetween(@Param("beginTime") LocalDateTime beginTime,
@Param("endTime") LocalDateTime endTime);
Long selectCountByStatus(@Param("status") Integer status);
}

View File

@@ -0,0 +1,25 @@
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> {
Integer selectSummaryPriceByStatusAndUnfreezeTimeBetween(@Param("bizType") Integer bizType,
@Param("status") Integer status,
@Param("beginTime") LocalDateTime beginTime,
@Param("endTime") LocalDateTime endTime);
Long selectWithdrawCountByStatus(@Param("status") Integer status);
}

View File

@@ -0,0 +1,65 @@
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> {
List<MemberAreaStatisticsRespBO> selectSummaryListByAreaId();
Integer selectCountByCreateTimeBetween(@Param("beginTime") LocalDateTime beginTime,
@Param("endTime") LocalDateTime endTime);
Integer selectCountByPayTimeBetween(@Param("beginTime") LocalDateTime beginTime,
@Param("endTime") LocalDateTime endTime);
Integer selectSummaryPriceByPayTimeBetween(@Param("beginTime") LocalDateTime beginTime,
@Param("endTime") LocalDateTime endTime);
Integer selectUserCountByCreateTimeBetween(@Param("beginTime") LocalDateTime beginTime,
@Param("endTime") LocalDateTime endTime);
Integer selectUserCountByPayTimeBetween(@Param("beginTime") LocalDateTime beginTime,
@Param("endTime") LocalDateTime endTime);
/**
* 按照支付时间统计订单(按天分组)
*
* @param beginTime 支付起始时间
* @param endTime 支付截止时间
* @return 订单统计列表
*/
List<TradeOrderTrendRespVO> selectListByPayTimeBetweenAndGroupByDay(@Param("beginTime") LocalDateTime beginTime,
@Param("endTime") LocalDateTime endTime);
/**
* 按照支付时间统计订单(按月分组)
*
* @param beginTime 支付起始时间
* @param endTime 支付截止时间
* @return 订单统计列表
*/
List<TradeOrderTrendRespVO> selectListByPayTimeBetweenAndGroupByMonth(@Param("beginTime") LocalDateTime beginTime,
@Param("endTime") LocalDateTime endTime);
Long selectCountByStatusAndDeliveryType(@Param("status") Integer status, @Param("deliveryType") Integer deliveryType);
TradeOrderSummaryRespVO selectPaySummaryByPayStatusAndPayTimeBetween(@Param("payStatus") Boolean payStatus,
@Param("beginTime") LocalDateTime beginTime,
@Param("endTime") LocalDateTime endTime);
}

View File

@@ -0,0 +1,41 @@
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);
default List<TradeStatisticsDO> selectListByTimeBetween(LocalDateTime beginTime, LocalDateTime endTime) {
return selectList(new LambdaQueryWrapperX<TradeStatisticsDO>()
.between(TradeStatisticsDO::getTime, beginTime, endTime));
}
Integer selectExpensePriceByTimeBetween(@Param("beginTime") LocalDateTime beginTime,
@Param("endTime") LocalDateTime endTime);
default TradeStatisticsDO selectByTimeBetween(LocalDateTime beginTime, LocalDateTime endTime) {
return selectOne(new LambdaQueryWrapperX<TradeStatisticsDO>()
.between(TradeStatisticsDO::getTime, beginTime, endTime));
}
}

View File

@@ -0,0 +1,48 @@
package cn.iocoder.yudao.module.statistics.enums;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
/**
* 时间范围类型的枚举
*
* @author owen
*/
@AllArgsConstructor
@Getter
public enum TimeRangeTypeEnum implements ArrayValuable<Integer> {
/**
* 天
*/
DAY(1),
/**
* 周
*/
WEEK(7),
/**
* 月
*/
MONTH(30),
/**
* 年
*/
YEAR(365),
;
public static final Integer[] ARRAYS = Arrays.stream(values()).map(TimeRangeTypeEnum::getType).toArray(Integer[]::new);
/**
* 类型
*/
private final Integer type;
@Override
public Integer[] array() {
return ARRAYS;
}
}

View File

@@ -0,0 +1,4 @@
/**
* TODO 芋艿,占坑,无特殊含义
*/
package cn.iocoder.yudao.module.statistics.job;

View File

@@ -0,0 +1,47 @@
package cn.iocoder.yudao.module.statistics.job.product;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler;
import cn.iocoder.yudao.framework.tenant.core.job.TenantJob;
import cn.iocoder.yudao.module.statistics.service.product.ProductStatisticsService;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Component;
/**
* 商品统计 Job
*
* @author owen
*/
@Component
public class ProductStatisticsJob implements JobHandler {
@Resource
private ProductStatisticsService productStatisticsService;
/**
* 执行商品统计任务
*
* @param param 要统计的天数只能是正整数1 代表昨日数据
* @return 统计结果
*/
@Override
@TenantJob
public String execute(String param) {
// 默认昨日
param = ObjUtil.defaultIfBlank(param, "1");
// 校验参数的合理性
if (!NumberUtil.isInteger(param)) {
throw new RuntimeException("商品统计任务的参数只能为是正整数");
}
Integer days = Convert.toInt(param, 0);
if (days < 1) {
throw new RuntimeException("商品统计任务的参数只能为是正整数");
}
String result = productStatisticsService.statisticsProduct(days);
return StrUtil.format("商品统计:\n{}", result);
}
}

View File

@@ -0,0 +1,48 @@
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.quartz.core.handler.JobHandler;
import cn.iocoder.yudao.framework.tenant.core.job.TenantJob;
import cn.iocoder.yudao.module.statistics.service.trade.TradeStatisticsService;
import org.springframework.stereotype.Component;
import jakarta.annotation.Resource;
/**
* 交易统计 Job
*
* @author owen
*/
@Component
public class TradeStatisticsJob implements JobHandler {
@Resource
private TradeStatisticsService tradeStatisticsService;
/**
* 执行交易统计任务
*
* @param param 要统计的天数只能是正整数1 代表昨日数据
* @return 统计结果
*/
@Override
@TenantJob
public String execute(String param) {
// 默认昨日
param = ObjUtil.defaultIfBlank(param, "1");
// 校验参数的合理性
if (!NumberUtil.isInteger(param)) {
throw new RuntimeException("交易统计任务的参数只能为是正整数");
}
Integer days = Convert.toInt(param, 0);
if (days < 1) {
throw new RuntimeException("交易统计任务的参数只能为是正整数");
}
String result = 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,32 @@
package cn.iocoder.yudao.module.statistics.service.infra;
import java.time.LocalDateTime;
/**
* API 访问日志的统计 Service 接口
*
* @author owen
*/
public interface ApiAccessLogStatisticsService {
/**
* 获取活跃用户数量
*
* @param userType 用户类型
* @param beginTime 起始时间
* @param endTime 截止时间
* @return 活跃用户数量
*/
Integer getUserCount(Integer userType, LocalDateTime beginTime, LocalDateTime endTime);
/**
* 获取访问用户数量
*
* @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 jakarta.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,70 @@
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 {
/**
* 获取会员统计(实时统计)
*
* @return 会员统计
*/
MemberSummaryRespVO getMemberSummary();
/**
* 获取会员分析对照数据
*
* @param beginTime 起始时间
* @param endTime 截止时间
* @return 会员分析对照数据
*/
DataComparisonRespVO<MemberAnalyseDataRespVO> getMemberAnalyseComparisonData(LocalDateTime beginTime,
LocalDateTime endTime);
/**
* 按照省份,获得会员统计列表
*
* @return 会员统计列表
*/
List<MemberAreaStatisticsRespVO> getMemberAreaStatisticsList();
/**
* 按照性别,获得会员统计列表
*
* @return 会员统计列表
*/
List<MemberSexStatisticsRespVO> getMemberSexStatisticsList();
/**
* 按照终端,获得会员统计列表
*
* @return 会员统计列表
*/
List<MemberTerminalStatisticsRespVO> getMemberTerminalStatisticsList();
/**
* 获取用户注册数量列表
*
* @param beginTime 起始时间
* @param endTime 截止时间
* @return 注册数量列表
*/
List<MemberRegisterCountRespVO> getMemberRegisterCountList(LocalDateTime beginTime, LocalDateTime endTime);
/**
* 获得用户数量量统计对照
*
* @return 用户数量量统计对照
*/
DataComparisonRespVO<MemberCountRespVO> getUserCountComparison();
}

View File

@@ -0,0 +1,140 @@
package cn.iocoder.yudao.module.statistics.service.member;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.ObjectUtil;
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 jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
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(ObjectUtil.defaultIfNull(a.getOrderCreateUserCount(), 0)
+ ObjectUtil.defaultIfNull(b.getOrderCreateUserCount(), 0))
.setOrderPayUserCount(ObjectUtil.defaultIfNull(a.getOrderPayUserCount(), 0)
+ ObjectUtil.defaultIfNull(b.getOrderPayUserCount(), 0))
.setOrderPayPrice(ObjectUtil.defaultIfNull(a.getOrderPayPrice(), 0)
+ ObjectUtil.defaultIfNull(b.getOrderPayPrice(), 0)));
// 拼接数据
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,40 @@
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 {
/**
* 获取钱包统计
*
* @param beginTime 起始时间
* @param endTime 截止时间
* @return 钱包统计
*/
WalletSummaryRespBO getWalletSummary(LocalDateTime beginTime, LocalDateTime endTime);
/**
* 获取钱包充值统计
*
* @param beginTime 起始时间
* @param endTime 截止时间
* @return 钱包充值统计
*/
RechargeSummaryRespBO getUserRechargeSummary(LocalDateTime beginTime, LocalDateTime endTime);
/**
* 获取充值金额合计
*
* @return 充值金额合计
*/
Integer getRechargePriceSummary();
}

View File

@@ -0,0 +1,52 @@
package cn.iocoder.yudao.module.statistics.service.pay;
import cn.iocoder.yudao.module.pay.enums.wallet.PayWalletBizTypeEnum;
import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum;
import cn.iocoder.yudao.module.pay.enums.refund.PayRefundStatusEnum;
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 jakarta.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(Boolean.TRUE);
}
}

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,51 @@
package cn.iocoder.yudao.module.statistics.service.product;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.SortablePageParam;
import cn.iocoder.yudao.module.statistics.controller.admin.common.vo.DataComparisonRespVO;
import cn.iocoder.yudao.module.statistics.controller.admin.product.vo.ProductStatisticsReqVO;
import cn.iocoder.yudao.module.statistics.controller.admin.product.vo.ProductStatisticsRespVO;
import cn.iocoder.yudao.module.statistics.dal.dataobject.product.ProductStatisticsDO;
import java.util.List;
/**
* 商品统计 Service 接口
*
* @author owen
*/
public interface ProductStatisticsService {
/**
* 获得商品统计排行榜分页
*
* @param reqVO 查询条件
* @param pageParam 分页排序查询
* @return 商品统计分页
*/
PageResult<ProductStatisticsDO> getProductStatisticsRankPage(ProductStatisticsReqVO reqVO, SortablePageParam pageParam);
/**
* 获得商品状况统计分析
*
* @param reqVO 查询条件
* @return 统计数据对照
*/
DataComparisonRespVO<ProductStatisticsRespVO> getProductStatisticsAnalyse(ProductStatisticsReqVO reqVO);
/**
* 获得商品状况明细
*
* @param reqVO 查询条件
* @return 统计数据对照
*/
List<ProductStatisticsDO> getProductStatisticsList(ProductStatisticsReqVO reqVO);
/**
* 统计指定天数的商品数据
*
* @return 统计结果
*/
String statisticsProduct(Integer days);
}

View File

@@ -0,0 +1,117 @@
package cn.iocoder.yudao.module.statistics.service.product;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.SortablePageParam;
import cn.iocoder.yudao.framework.common.util.object.PageUtils;
import cn.iocoder.yudao.module.statistics.controller.admin.common.vo.DataComparisonRespVO;
import cn.iocoder.yudao.module.statistics.controller.admin.product.vo.ProductStatisticsReqVO;
import cn.iocoder.yudao.module.statistics.controller.admin.product.vo.ProductStatisticsRespVO;
import cn.iocoder.yudao.module.statistics.dal.dataobject.product.ProductStatisticsDO;
import cn.iocoder.yudao.module.statistics.dal.mysql.product.ProductStatisticsMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.util.StopWatch;
import org.springframework.validation.annotation.Validated;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
/**
* 商品统计 Service 实现类
*
* @author owen
*/
@Service
@Validated
public class ProductStatisticsServiceImpl implements ProductStatisticsService {
@Resource
private ProductStatisticsMapper productStatisticsMapper;
@Override
public PageResult<ProductStatisticsDO> getProductStatisticsRankPage(ProductStatisticsReqVO reqVO, SortablePageParam pageParam) {
PageUtils.buildDefaultSortingField(pageParam, ProductStatisticsDO::getBrowseCount); // 默认浏览量倒序
return productStatisticsMapper.selectPageGroupBySpuId(reqVO, pageParam);
}
@Override
public DataComparisonRespVO<ProductStatisticsRespVO> getProductStatisticsAnalyse(ProductStatisticsReqVO reqVO) {
LocalDateTime beginTime = ArrayUtil.get(reqVO.getTimes(), 0);
LocalDateTime endTime = ArrayUtil.get(reqVO.getTimes(), 1);
// 统计数据
ProductStatisticsRespVO value = productStatisticsMapper.selectVoByTimeBetween(reqVO);
// 对照数据
LocalDateTime referenceBeginTime = beginTime.minus(Duration.between(beginTime, endTime));
ProductStatisticsReqVO referenceReqVO = new ProductStatisticsReqVO(new LocalDateTime[]{referenceBeginTime, beginTime});
ProductStatisticsRespVO reference = productStatisticsMapper.selectVoByTimeBetween(referenceReqVO);
return new DataComparisonRespVO<>(value, reference);
}
@Override
public List<ProductStatisticsDO> getProductStatisticsList(ProductStatisticsReqVO reqVO) {
return productStatisticsMapper.selectListByTimeBetween(reqVO);
}
@Override
public String statisticsProduct(Integer days) {
LocalDateTime today = LocalDateTime.now();
return IntStream.rangeClosed(1, days)
.mapToObj(day -> statisticsProduct(today.minusDays(day)))
.sorted()
.collect(Collectors.joining("\n"));
}
/**
* 统计商品数据
*
* @param date 需要统计的日期
* @return 统计结果
*/
private String statisticsProduct(LocalDateTime date) {
// 1. 处理统计时间范围
LocalDateTime beginTime = LocalDateTimeUtil.beginOfDay(date);
LocalDateTime endTime = LocalDateTimeUtil.endOfDay(date);
String dateStr = DatePattern.NORM_DATE_FORMATTER.format(date);
// 2. 检查该日是否已经统计过
Long count = productStatisticsMapper.selectCountByTimeBetween(beginTime, endTime);
if (count != null && count > 0) {
return dateStr + " 数据已存在,如果需要重新统计,请先删除对应的数据";
}
StopWatch stopWatch = new StopWatch(dateStr);
stopWatch.start();
// 4. 分页统计,避免商品表数据较多时,出现超时问题
final int pageSize = 100;
for (int pageNo = 1; ; pageNo++) {
IPage<ProductStatisticsDO> page = productStatisticsMapper.selectStatisticsResultPageByTimeBetween(
Page.of(pageNo, pageSize, false), beginTime, endTime);
if (CollUtil.isEmpty(page.getRecords())) {
break;
}
// 4.1 计算访客支付转化率(百分比)
for (ProductStatisticsDO record : page.getRecords()) {
record.setTime(date.toLocalDate());
if (record.getBrowseUserCount() != null && ObjUtil.notEqual(record.getBrowseUserCount(), 0)) {
record.setBrowseConvertPercent(100 * record.getOrderPayCount() / record.getBrowseUserCount());
}
}
// 4.2 插入数据
productStatisticsMapper.insertBatch(page.getRecords());
}
return stopWatch.prettyPrint();
}
}

View File

@@ -0,0 +1,32 @@
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 {
/**
* 获取售后单统计
*
* @param beginTime 起始时间
* @param endTime 截止时间
* @return 售后统计结果
*/
AfterSaleSummaryRespBO getAfterSaleSummary(LocalDateTime beginTime, LocalDateTime endTime);
/**
* 获取指定状态的售后订单数量
*
* @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 jakarta.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,31 @@
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 {
/**
* 获取已结算的佣金金额
*
* @param beginTime 起始时间
* @param endTime 截止时间
* @return 已结算的佣金金额
*/
Integer getBrokerageSettlementPriceSummary(LocalDateTime beginTime, LocalDateTime endTime);
/**
* 获取指定状态的提现记录数量
*
* @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 jakarta.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,83 @@
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);
/**
* 获取地区订单统计
*
* @return 订单统计结果
*/
List<MemberAreaStatisticsRespBO> getSummaryListByAreaId();
/**
* 获取下单用户数量
*
* @param beginTime 起始时间
* @param endTime 截止时间
* @return 下单用户数量
*/
Integer getOrderUserCount(LocalDateTime beginTime, LocalDateTime endTime);
/**
* 获取支付用户数量
*
* @param beginTime 起始时间
* @param endTime 截止时间
* @return 支付用户数量
*/
Integer getPayUserCount(LocalDateTime beginTime, LocalDateTime endTime);
/**
* 获取支付金额
*
* @param beginTime 起始时间
* @param endTime 截止时间
* @return 支付用户金额
*/
Integer getOrderPayPrice(LocalDateTime beginTime, LocalDateTime endTime);
/**
* 根据订单状态、物流类型,获得交易订单数量
*
* @return 订单数量
*/
Long getCountByStatusAndDeliveryType(Integer status, Integer deliveryType);
/**
* 交易订单销售额对照
*
* @return 销售额对照
*/
DataComparisonRespVO<TradeOrderSummaryRespVO> getOrderComparison();
/**
* 获得订单量趋势统计
*
* @param reqVO 统计参数
* @return 订单量趋势统计
*/
List<DataComparisonRespVO<TradeOrderTrendRespVO>> getOrderCountTrendComparison(TradeOrderTrendReqVO reqVO);
}

View File

@@ -0,0 +1,107 @@
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.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 jakarta.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.endOfDay(date);
return tradeOrderStatisticsMapper.selectPaySummaryByPayStatusAndPayTimeBetween(
Boolean.TRUE, 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,67 @@
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> getTradeStatisticsAnalyse(
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);
/**
* 统计指定天数的交易数据
*
* @return 统计结果
*/
String statisticsTrade(Integer days);
/**
* 统计指定日期的交易数据
*
* @param days 增加的天数
* @return 交易数据
*/
TradeSummaryRespBO getTradeSummaryByDays(int days);
/**
* 统计指定月份的交易数据
*
* @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 jakarta.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> getTradeStatisticsAnalyse(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_FORMATTER.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,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,64 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.iocoder.yudao.module.statistics.dal.mysql.product.ProductStatisticsMapper">
<select id="selectStatisticsResultPageByTimeBetween"
resultType="cn.iocoder.yudao.module.statistics.dal.dataobject.product.ProductStatisticsDO">
SELECT spu.id AS spuId
-- 浏览量:一个用户可以有多次
, (SELECT COUNT(1)
FROM product_browse_history
WHERE spu_id = spu.id
AND create_time BETWEEN #{beginTime} AND #{endTime}) AS browse_count
-- 访客量:按用户去重计数
, (SELECT COUNT(DISTINCT user_id)
FROM product_browse_history
WHERE spu_id = spu.id
AND create_time BETWEEN #{beginTime} AND #{endTime}) AS browse_user_count
-- 收藏数量:按用户去重计数
, (SELECT COUNT(DISTINCT user_id)
FROM product_favorite
WHERE spu_id = spu.id
AND create_time BETWEEN #{beginTime} AND #{endTime}) AS favorite_count
-- 加购数量:按用户去重计数
, (SELECT COUNT(DISTINCT user_id)
FROM trade_cart
WHERE spu_id = spu.id
AND create_time BETWEEN #{beginTime} AND #{endTime}) AS cart_count
-- 下单件数
, (SELECT IFNULL(SUM(count), 0)
FROM trade_order_item
WHERE spu_id = spu.id
AND create_time BETWEEN #{beginTime} AND #{endTime}) AS order_count
-- 支付件数
, (SELECT IFNULL(SUM(item.count), 0)
FROM trade_order_item item
JOIN trade_order o ON item.order_id = o.id
WHERE spu_id = spu.id
AND o.pay_status = TRUE
AND item.create_time BETWEEN #{beginTime} AND #{endTime}) AS order_pay_count
-- 支付金额
, (SELECT IFNULL(SUM(item.pay_price), 0)
FROM trade_order_item item
JOIN trade_order o ON item.order_id = o.id
WHERE spu_id = spu.id
AND o.pay_status = TRUE
AND item.create_time BETWEEN #{beginTime} AND #{endTime}) AS order_pay_price
-- 退款件数
, (SELECT IFNULL(SUM(count), 0)
FROM trade_after_sale
WHERE spu_id = spu.id
AND refund_time IS NOT NULL
AND create_time BETWEEN #{beginTime} AND #{endTime}) AS after_sale_count
-- 退款金额
, (SELECT IFNULL(SUM(refund_price), 0)
FROM trade_after_sale
WHERE spu_id = spu.id
AND refund_time IS NOT NULL
AND create_time BETWEEN #{beginTime} AND #{endTime}) AS after_sale_refund_price
FROM product_spu spu
WHERE spu.deleted = FALSE
ORDER BY spu.id
</select>
</mapper>

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="selectPaySummaryByPayStatusAndPayTimeBetween"
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 pay_status = #{payStatus}
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>