package com.ruoyi.order.service.impl;
|
|
import com.alibaba.excel.EasyExcel;
|
import com.google.common.collect.Lists;
|
import com.ruoyi.common.core.enums.AuctionOrderTypeEnum;
|
import com.ruoyi.common.core.enums.OrderFromEnum;
|
import com.ruoyi.common.core.enums.OrderTypeEnum;
|
import com.ruoyi.common.core.enums.TimeTypeEnum;
|
import com.ruoyi.common.core.exception.ServiceException;
|
import com.ruoyi.common.core.utils.DateUtils;
|
import com.ruoyi.common.core.utils.page.CollUtils;
|
import com.ruoyi.order.controller.management.dto.MgtOrderStaticsQuery;
|
import com.ruoyi.order.controller.management.vo.MgtAmountChartVO;
|
import com.ruoyi.order.controller.management.vo.MgtCountChartVO;
|
import com.ruoyi.order.controller.management.vo.MgtOrderStaticsChartVO;
|
import com.ruoyi.order.controller.management.vo.MgtOrderStaticsVO;
|
import com.ruoyi.order.service.IOrderService;
|
import com.ruoyi.system.api.domain.Order;
|
import java.math.BigDecimal;
|
import java.net.URLEncoder;
|
import java.time.LocalDate;
|
import java.time.LocalDateTime;
|
import java.time.format.DateTimeFormatter;
|
import java.time.temporal.ChronoUnit;
|
import java.util.ArrayList;
|
import java.util.List;
|
import java.util.Map;
|
import java.util.Objects;
|
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.Executors;
|
import java.util.stream.Collectors;
|
import java.util.stream.Stream;
|
import javax.servlet.http.HttpServletResponse;
|
import lombok.RequiredArgsConstructor;
|
import lombok.extern.slf4j.Slf4j;
|
import org.jetbrains.annotations.NotNull;
|
import org.springframework.stereotype.Service;
|
|
/**
|
* @author mitao
|
* @date 2024/6/19
|
*/
|
@Slf4j
|
@Service
|
@RequiredArgsConstructor
|
public class MgtBusinessDataService {
|
|
private final IOrderService orderService;
|
|
/**
|
* 获取业务数据统计
|
*
|
* @param query 订单统计查询对象
|
* @return MgtOrderStaticsVO
|
*/
|
public MgtOrderStaticsVO getOverview(MgtOrderStaticsQuery query) {
|
|
List<LocalDateTime> timeByTimeType = getTimeByTimeType(query);
|
LocalDateTime startTime = timeByTimeType.get(0);
|
LocalDateTime endTime = timeByTimeType.get(1);
|
List<Order> orderList = orderService.getStaticsOrderList(startTime, endTime);
|
MgtOrderStaticsVO mgtOrderStaticsVO = new MgtOrderStaticsVO();
|
if (CollUtils.isNotEmpty(orderList)) {
|
/* ***************************************商城订单统计*************************************** */
|
// 商城订单统计-订单总数
|
long mallOrderTotalCount = orderList.stream()
|
.filter(order -> !order.getOrderFrom().equals(OrderFromEnum.AUCTION_ORDERS))
|
.count();
|
// 商城订单统计-商城订单
|
long mallOrderCount = orderList.stream()
|
.filter(order -> order.getOrderFrom().equals(OrderFromEnum.COMMODITY_ORDER))
|
.count();
|
// 商城订单统计-秒杀订单
|
long seckillOrderCount = orderList.stream()
|
.filter(order -> order.getOrderFrom().equals(OrderFromEnum.SNAP_ORDERS))
|
.count();
|
// 商城订单统计-团购订单
|
long groupPurchaseOrderCount = orderList.stream()
|
.filter(order -> order.getOrderFrom()
|
.equals(OrderFromEnum.GROUP_PURCHASE_ORDERS))
|
.count();
|
// 商城订单统计-订单总金额
|
BigDecimal mallOrderTotalAmount = orderList.stream()
|
.filter(order -> !order.getOrderFrom().equals(OrderFromEnum.AUCTION_ORDERS))
|
.map(Order::getTotalAmount).reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
|
// 商城订单统计 - 商城总金额
|
BigDecimal mallTotalAmount = orderList.stream()
|
.filter(order -> order.getOrderFrom().equals(OrderFromEnum.COMMODITY_ORDER))
|
.map(Order::getTotalAmount).reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
|
// 商城订单统计-秒杀总金额
|
BigDecimal seckillTotalAmount = orderList.stream()
|
.filter(order -> order.getOrderFrom().equals(OrderFromEnum.SNAP_ORDERS))
|
.map(Order::getTotalAmount).reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
|
|
// 商城订单统计-团购总金额
|
BigDecimal groupPurchaseTotalAmount = orderList.stream()
|
.filter(order -> order.getOrderFrom()
|
.equals(OrderFromEnum.GROUP_PURCHASE_ORDERS))
|
.map(Order::getTotalAmount).reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
|
|
mgtOrderStaticsVO.setMallOrderTotalCount(mallOrderTotalCount);
|
mgtOrderStaticsVO.setMallOrderCount(mallOrderCount);
|
mgtOrderStaticsVO.setSeckillOrderCount(seckillOrderCount);
|
mgtOrderStaticsVO.setGroupPurchaseOrderCount(groupPurchaseOrderCount);
|
|
mgtOrderStaticsVO.setMallOrderTotalAmount(mallOrderTotalAmount);
|
mgtOrderStaticsVO.setMallTotalAmount(mallTotalAmount);
|
mgtOrderStaticsVO.setSeckillTotalAmount(seckillTotalAmount);
|
mgtOrderStaticsVO.setGroupPurchaseTotalAmount(groupPurchaseTotalAmount);
|
|
/* ***************************************拍卖订单统计*************************************** */
|
// 拍卖订单统计-订单总数
|
long auctionOrderTotalCount = orderList.stream()
|
.filter(order -> order.getOrderFrom().equals(OrderFromEnum.AUCTION_ORDERS))
|
.count();
|
// 拍卖订单统计-拍卖商品订单
|
long auctionGoodsOrderCount = orderList.stream()
|
.filter(order -> order.getOrderFrom().equals(OrderFromEnum.AUCTION_ORDERS)
|
&& order.getAuctionOrderType()
|
.equals(AuctionOrderTypeEnum.REGULAR_ITEMS))
|
.count();
|
// 拍卖订单统计-拍卖场订单
|
long auctionSalesroomOrderCount = orderList.stream()
|
.filter(order -> order.getOrderFrom().equals(OrderFromEnum.AUCTION_ORDERS)
|
&& order.getAuctionOrderType()
|
.equals(AuctionOrderTypeEnum.AUCTION_ITEMS))
|
.count();
|
// 拍卖订单统计-订单总金额
|
BigDecimal auctionOrderTotalAmount = orderList.stream()
|
.filter(order -> order.getOrderFrom().equals(OrderFromEnum.AUCTION_ORDERS))
|
.map(Order::getTotalAmount)
|
.reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
|
// 拍卖订单统计-拍卖商品总金额
|
BigDecimal auctionGoodsTotalAmount = orderList.stream()
|
.filter(order -> order.getOrderFrom().equals(OrderFromEnum.AUCTION_ORDERS)
|
&& order.getAuctionOrderType()
|
.equals(AuctionOrderTypeEnum.REGULAR_ITEMS)
|
)
|
.map(Order::getTotalAmount).reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
|
// 拍卖订单统计-拍卖场总金额
|
BigDecimal auctionSalesroomTotalAmount = orderList.stream()
|
.filter(order -> order.getOrderFrom().equals(OrderFromEnum.AUCTION_ORDERS)
|
&& order.getAuctionOrderType()
|
.equals(AuctionOrderTypeEnum.AUCTION_ITEMS))
|
.map(Order::getTotalAmount).reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
|
|
mgtOrderStaticsVO.setAuctionOrderTotalCount(auctionOrderTotalCount);
|
mgtOrderStaticsVO.setAuctionGoodsOrderCount(auctionGoodsOrderCount);
|
mgtOrderStaticsVO.setAuctionSalesroomOrderCount(auctionSalesroomOrderCount);
|
|
mgtOrderStaticsVO.setAuctionOrderTotalAmount(auctionOrderTotalAmount);
|
mgtOrderStaticsVO.setAuctionGoodsTotalAmount(auctionGoodsTotalAmount);
|
mgtOrderStaticsVO.setAuctionSalesroomTotalAmount(auctionSalesroomTotalAmount);
|
}
|
return mgtOrderStaticsVO;
|
}
|
|
private List<LocalDateTime> getTimeByTimeType(MgtOrderStaticsQuery query) {
|
LocalDateTime startTime;
|
LocalDateTime endTime;
|
List<LocalDateTime> range = new ArrayList<>();
|
LocalDateTime now = LocalDateTime.now();
|
if (Objects.equals(TimeTypeEnum.TODAY.getCode(), query.getTimeType())) {
|
startTime = DateUtils.getDayStart(now);
|
endTime = DateUtils.getDayEnd(now);
|
} else if (Objects.equals(TimeTypeEnum.PAST_SEVEN_DAYS.getCode(), query.getTimeType())) {
|
startTime = DateUtils.getDayStart(now.minusDays(6)); // 过去七天 为过去六天加今天
|
endTime = DateUtils.getDayEnd(now);
|
} else if (Objects.equals(TimeTypeEnum.PAST_THIRTY_DAYS.getCode(), query.getTimeType())) {
|
startTime = DateUtils.getDayStart(now.minusDays(29)); // 过去30天 为过去29天加今天
|
endTime = DateUtils.getDayEnd(now);
|
} else if (Objects.equals(TimeTypeEnum.CUSTOM.getCode(), query.getTimeType())) {
|
if (Objects.isNull(query.getStartTime()) || Objects.isNull(query.getEndTime())) {
|
throw new ServiceException("自定义时间不能为空");
|
}
|
startTime = query.getStartTime();
|
endTime = query.getEndTime();
|
} else {
|
startTime = DateUtils.getDayStart(now);
|
endTime = DateUtils.getDayEnd(now);
|
}
|
range.add(startTime);
|
range.add(endTime);
|
return range;
|
}
|
|
/**
|
* 获取图表数据
|
*
|
* @return MgtOrderStaticsChartVO
|
*/
|
public MgtOrderStaticsChartVO getChart() {
|
LocalDateTime now = LocalDateTime.now();
|
LocalDateTime startTime = DateUtils.getDayStart(now.minusDays(29));
|
LocalDateTime endTime = DateUtils.getDayEnd(now);
|
List<LocalDate> datesBetween = getDatesBetween(startTime.toLocalDate(),
|
endTime.toLocalDate());
|
// 根据开始时间和结束时间,获取这个范围的每一天的日期
|
List<Order> orderList = orderService.getStaticsOrderList(startTime, endTime);
|
ExecutorService executorService = Executors.newFixedThreadPool(4);
|
// 商城订单总数
|
CompletableFuture<List<MgtCountChartVO>> mallOrderCountFuture = CompletableFuture.supplyAsync(
|
() -> getMgtCountChartVOS(datesBetween, orderList, OrderTypeEnum.MALL_ODER),
|
executorService);
|
// 商城订单总金额
|
CompletableFuture<List<MgtAmountChartVO>> mallOrderTotalAmountFuture = CompletableFuture.supplyAsync(
|
() -> getMgtAmountChartVOS(datesBetween, orderList, OrderTypeEnum.MALL_ODER),
|
executorService);
|
// 拍卖订单总数
|
CompletableFuture<List<MgtCountChartVO>> auctionOrderCountFuture = CompletableFuture.supplyAsync(
|
() -> getMgtCountChartVOS(datesBetween, orderList, OrderTypeEnum.AUCTION_ORDER),
|
executorService);
|
// 拍卖订单总金额
|
CompletableFuture<List<MgtAmountChartVO>> auctionOrderTotalAmountFuture = CompletableFuture.supplyAsync(
|
() -> getMgtAmountChartVOS(datesBetween, orderList, OrderTypeEnum.AUCTION_ORDER),
|
executorService);
|
// 使用 join 方法阻塞当前线程,直到所有任务完成
|
CompletableFuture.allOf(mallOrderCountFuture,
|
mallOrderTotalAmountFuture, auctionOrderCountFuture, auctionOrderTotalAmountFuture)
|
.join();
|
MgtOrderStaticsChartVO mgtOrderStaticsChartVO = new MgtOrderStaticsChartVO();
|
try {
|
// 设置结果
|
mgtOrderStaticsChartVO.setMallOrderCountList(mallOrderCountFuture.get());
|
mgtOrderStaticsChartVO.setMallOrderTotalAmountList(mallOrderTotalAmountFuture.get());
|
mgtOrderStaticsChartVO.setAuctionOrderCountList(auctionOrderCountFuture.get());
|
mgtOrderStaticsChartVO.setAuctionOrderTotalAmountList(
|
auctionOrderTotalAmountFuture.get());
|
} catch (InterruptedException | ExecutionException e) {
|
throw new RuntimeException(e);
|
} finally {
|
executorService.shutdown(); // 确保关闭线程池
|
}
|
return mgtOrderStaticsChartVO;
|
}
|
|
@NotNull
|
private static List<MgtAmountChartVO> getMgtAmountChartVOS(List<LocalDate> datesBetween,
|
List<Order> orderList, OrderTypeEnum type) {
|
Map<String, BigDecimal> orderTotalAmountMap;
|
if (type.equals(OrderTypeEnum.MALL_ODER)) {
|
orderTotalAmountMap = orderList.stream()
|
.filter(order -> !order.getOrderFrom().equals(OrderFromEnum.AUCTION_ORDERS))
|
.collect(Collectors.groupingBy(order -> order.getOrderTime().toLocalDate()
|
.format(DateTimeFormatter.ofPattern("MM-dd")),
|
Collectors.reducing(BigDecimal.ZERO, Order::getTotalAmount,
|
BigDecimal::add)));
|
} else if (type.equals(OrderTypeEnum.AUCTION_ORDER)) {
|
orderTotalAmountMap = orderList.stream()
|
.filter(order -> order.getOrderFrom().equals(OrderFromEnum.AUCTION_ORDERS))
|
.collect(Collectors.groupingBy(order -> order.getOrderTime().toLocalDate()
|
.format(DateTimeFormatter.ofPattern("MM-dd")),
|
Collectors.reducing(BigDecimal.ZERO, Order::getTotalAmount,
|
BigDecimal::add)));
|
} else {
|
orderTotalAmountMap = null;
|
}
|
if (orderTotalAmountMap != null) {
|
return datesBetween.stream().map(date -> {
|
MgtAmountChartVO mgtAmountChartVO = new MgtAmountChartVO();
|
mgtAmountChartVO.setDate(date.format(DateTimeFormatter.ofPattern("MM-dd")));
|
BigDecimal orderTotalAmount = orderTotalAmountMap.get(
|
date.format(DateTimeFormatter.ofPattern("MM-dd")));
|
mgtAmountChartVO.setValue(
|
orderTotalAmount == null ? BigDecimal.ZERO : orderTotalAmount);
|
return mgtAmountChartVO;
|
}).collect(Collectors.toList());
|
} else {
|
return CollUtils.emptyList();
|
}
|
}
|
|
@NotNull
|
private static List<MgtCountChartVO> getMgtCountChartVOS(List<LocalDate> datesBetween,
|
List<Order> orderList, OrderTypeEnum type) {
|
Map<String, Long> orderCountMap;
|
if (type.equals(OrderTypeEnum.MALL_ODER)) {
|
orderCountMap = orderList.stream()
|
.filter(order -> !order.getOrderFrom().equals(OrderFromEnum.AUCTION_ORDERS))
|
.collect(Collectors.groupingBy(order -> order.getOrderTime().toLocalDate()
|
.format(DateTimeFormatter.ofPattern("MM-dd")),
|
Collectors.counting()));
|
} else if (type.equals(OrderTypeEnum.AUCTION_ORDER)) {
|
orderCountMap = orderList.stream()
|
.filter(order -> order.getOrderFrom().equals(OrderFromEnum.AUCTION_ORDERS))
|
.collect(Collectors.groupingBy(order -> order.getOrderTime().toLocalDate()
|
.format(DateTimeFormatter.ofPattern("MM-dd")),
|
Collectors.counting()));
|
} else {
|
orderCountMap = null;
|
}
|
if (orderCountMap != null) {
|
return datesBetween.stream().map(date -> {
|
MgtCountChartVO mgtCountChartVO = new MgtCountChartVO();
|
mgtCountChartVO.setDate(date.format(DateTimeFormatter.ofPattern("MM-dd")));
|
Long orderCount = orderCountMap.get(
|
date.format(DateTimeFormatter.ofPattern("MM-dd")));
|
mgtCountChartVO.setValue(orderCount == null ? 0L : orderCount);
|
return mgtCountChartVO;
|
}).collect(Collectors.toList());
|
} else {
|
return CollUtils.emptyList();
|
}
|
}
|
|
public List<LocalDate> getDatesBetween(LocalDate startDate, LocalDate endDate) {
|
// 检查开始日期是否晚于结束日期
|
if (startDate.isAfter(endDate)) {
|
throw new IllegalArgumentException("开始日期必须早于结束日期");
|
}
|
|
// 使用ChronoUnit.DAYS.between计算日期差,并生成日期范围的流
|
long numOfDaysBetween = ChronoUnit.DAYS.between(startDate, endDate);
|
|
// 使用Stream.iterate生成日期序列,再用limit限制天数,最后collect收集结果
|
return Stream.iterate(startDate, date -> date.plusDays(1))
|
.limit(numOfDaysBetween + 1) // 加1是因为包含结束日期
|
.collect(Collectors.toList());
|
}
|
|
/**
|
* 业务数据导出
|
*
|
* @param query
|
* @param response
|
*/
|
public void exportBusinessData(MgtOrderStaticsQuery query, HttpServletResponse response) {
|
MgtOrderStaticsVO overview = getOverview(query);
|
// 这里注意 有同学反应使用swagger 会导致各种问题,请直接用浏览器或者用postman
|
response.setContentType(
|
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
|
response.setCharacterEncoding("utf-8");
|
// 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
|
try {
|
String fileName = URLEncoder.encode("业务数据", "UTF-8").replaceAll("\\+", "%20");
|
response.setHeader("Content-disposition",
|
"attachment;filename*=utf-8''" + fileName + ".xlsx");
|
EasyExcel.write(response.getOutputStream(), MgtOrderStaticsVO.class)
|
.sheet("业务数据")
|
.doWrite(Lists.newArrayList(overview));
|
} catch (Exception e) {
|
throw new RuntimeException(e);
|
}
|
}
|
}
|