Commit 08c76da3 by Jony.L

首页统计2.0 添加切换按月为维度统计功能

parent 82522963
......@@ -41,20 +41,20 @@ public class HomeIndexController {
@GetMapping("/getUsersData")
@Operation(summary = "获取用户数统计")
public CommonResult<List<HomeIndexUsersCountRespVO>> getHomeIndexUsersCount() {
return success(homeIndexService.getUsersData());
public CommonResult<List<HomeIndexUsersCountRespVO>> getHomeIndexUsersCount(String dateType) {
return success(homeIndexService.getUsersData(dateType));
}
@GetMapping("/getOrdersData")
@Operation(summary = "获取订单数据")
public CommonResult<List<HomeIndexOrdersCountRespVO>> getOrdersData() {
return success(homeIndexService.getOrdersData());
public CommonResult<List<HomeIndexOrdersCountRespVO>> getOrdersData(String dateType) {
return success(homeIndexService.getOrdersData(dateType));
}
@GetMapping("/getApiCallsData")
@Operation(summary = "获取api调用次数数据")
public CommonResult<List<HomeIndexApiCallsRespVO>> getApiCallsData(){
return success(homeIndexService.getApiCallsData());
public CommonResult<List<HomeIndexApiCallsRespVO>> getApiCallsData(String dateType){
return success(homeIndexService.getApiCallsData(dateType));
}
}
......@@ -14,9 +14,9 @@ import java.util.List;
public interface HomeIndexService {
List<HomeIndexUsersCountRespVO> getRegisterUsersCount();
List<HomeIndexUsersCountRespVO> getUsersData();
List<HomeIndexUsersCountRespVO> getUsersData(String dateType);
List<HomeIndexOrdersCountRespVO> getOrdersData();
List<HomeIndexOrdersCountRespVO> getOrdersData(String dateType);
List<HomeIndexApiCallsRespVO> getApiCallsData();
List<HomeIndexApiCallsRespVO> getApiCallsData(String dateType);
}
package com.luhu.computility.module.biz.service.home;
import cn.hutool.core.util.ObjectUtil;
import com.luhu.computility.framework.common.exception.ServiceException;
import com.luhu.computility.framework.common.pojo.PageResult;
import com.luhu.computility.module.apihub.controller.admin.apicalllog.vo.ApiCallLogPageReqVO;
import com.luhu.computility.module.apihub.controller.admin.apiorder.vo.ApiOrderPageReqVO;
......@@ -26,6 +27,8 @@ import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.YearMonth;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;
......@@ -76,97 +79,208 @@ public class HomeIndexServiceImpl implements HomeIndexService {
}
@Override
public List<HomeIndexUsersCountRespVO> getUsersData() {
public List<HomeIndexUsersCountRespVO> getUsersData(String dateType) {
MemberUserPageReqVO queryVO = new MemberUserPageReqVO();
LocalDate yesterday = LocalDate.now().minusDays(1);
LocalDate startDate = yesterday.minusDays(6); // 近7天:startDate 到 yesterday
// 统一查询截至"统计截止日"的所有用户(日/月统计共用)
LocalDate today = LocalDate.now();
LocalDateTime endTime = null; // 统计截止时间(所有用户注册时间<=该时间)
List<LocalDate> timeNodes = new ArrayList<>(); // 统计节点(7个:7天或7个月的结束日)
// 1. 先查询截至昨天的所有用户(用于计算累计值)
LocalDateTime[] allTimePeriod = new LocalDateTime[2];
allTimePeriod[0] = LocalDate.of(1970, 1, 1).atStartOfDay(); // 最早时间
allTimePeriod[1] = yesterday.atTime(23, 59, 59, 999_999_999); // 昨天结束
if ("d".equals(dateType) || ObjectUtil.isEmpty(dateType)) {
// 近7天(截止到昨天,避免今天未结束的数据不准)
LocalDate yesterday = today.minusDays(1);
LocalDate startDate = yesterday.minusDays(6);
endTime = yesterday.atTime(23, 59, 59, 999_999_999);
// 生成7个日期节点(每天)
for (int i = 0; i < 7; i++) {
timeNodes.add(startDate.plusDays(i));
}
} else if ("m".equals(dateType)) {
// 月统计:近7个月(截止到上月末,避免当月未结束的数据不准)
YearMonth lastMonth = YearMonth.from(today.minusMonths(1)); // 上月
endTime = lastMonth.atEndOfMonth().atTime(23, 59, 59, 999_999_999);
// 生成7个月份节点(每个月的最后一天)
for (int i = 6; i >= 0; i--) { // 从6个月前到上月
YearMonth currentMonth = lastMonth.minusMonths(i);
timeNodes.add(currentMonth.atEndOfMonth()); // 当月最后一天(如2025-09-30)
}
} else {
throw new ServiceException("未传入正确的统计时间类型!");
}
// 查询截止到endTime的所有用户(日/月统计共用此数据)
LocalDateTime[] allTimePeriod = {LocalDate.of(1970, 1, 1).atStartOfDay(), endTime};
queryVO.setCreateTime(allTimePeriod);
List<MemberUserDO> allUserList = memberUserService.getUserList(queryVO);
// 按日期分组:统计“截至当天”的总用户数(累计值)
Map<LocalDate, Long> dailyTotalMap = new HashMap<>();
// 遍历7天的每一天(从最早的一天开始)
for (int i = 0; i < 7; i++) {
LocalDate currentDate = startDate.plusDays(i);
// 计算截至currentDate的总用户数:统计所有注册日期<=currentDate的用户
long currentTotal = allUserList.stream()
.filter(user -> user.getCreateTime().toLocalDate().isBefore(currentDate.plusDays(1)))
// 按统计节点计算累计用户数(截至每个节点的总用户数)
Map<LocalDate, Long> totalMap = new HashMap<>();
for (LocalDate node : timeNodes) {
// 统计注册时间<=当前节点的用户数
long count = allUserList.stream()
.filter(user -> user.getCreateTime().toLocalDate().isBefore(node.plusDays(1)))
.count();
dailyTotalMap.put(currentDate, currentTotal);
totalMap.put(node, count);
}
//构建结果列表
// 构建结果列表(格式化日期显示)
List<HomeIndexUsersCountRespVO> resultList = new ArrayList<>();
for (int i = 0; i < 7; i++) {
LocalDate currentDate = startDate.plusDays(i);
for (LocalDate node : timeNodes) {
HomeIndexUsersCountRespVO respVO = new HomeIndexUsersCountRespVO();
respVO.setCountDate(currentDate.toString());
respVO.setUsersCount((int) (long) dailyTotalMap.get(currentDate)); // 累计用户数
// 日统计显示"yyyy-MM-dd",月统计显示"yyyy-MM"
respVO.setCountDate("d".equals(dateType)
? node.toString()
: node.format(DateTimeFormatter.ofPattern("yyyy-MM")));
respVO.setUsersCount(totalMap.get(node).intValue());
resultList.add(respVO);
}
//按日期排序
// 按时间升序排序(从最早到最近)
resultList.sort(Comparator.comparing(HomeIndexUsersCountRespVO::getCountDate));
return resultList;
}
@Override
public List<HomeIndexOrdersCountRespVO> getOrdersData() {
LocalDate yesterday = LocalDate.now().minusDays(1);
LocalDate startDate = yesterday.minusDays(6); // 近7天:startDate 到 yesterday
public List<HomeIndexApiCallsRespVO> getApiCallsData(String dateType) {
ApiCallLogPageReqVO queryVO = new ApiCallLogPageReqVO();
LocalDateTime[] allTimePeriod = new LocalDateTime[2];
allTimePeriod[0] = LocalDate.of(1970, 1, 1).atStartOfDay();
allTimePeriod[1] = yesterday.atTime(23, 59, 59, 999_999_999);
// 统一查询截至"统计截止日"的所有API调用日志(日/月统计共用)
LocalDate today = LocalDate.now();
LocalDateTime endTime = null; // 统计截止时间(所有调用时间<=该时间)
List<LocalDate> timeNodes = new ArrayList<>(); // 统计节点(7个:7天或7个月的结束日)
TradeOrderPageReqVO allComputeQueryVO = new TradeOrderPageReqVO();
allComputeQueryVO.setCreateTime(allTimePeriod);
allComputeQueryVO.setStatus(TradeOrderStatusEnum.COMPLETED.getStatus());//只统计已完成的订单
List<TradeOrderDO> allComputeOrderList = tradeOrderQueryService.getOrderList(allComputeQueryVO);
if ("d".equals(dateType) || ObjectUtil.isEmpty(dateType)) {
// 日统计:近7天(截止到昨天,避免今天未结束的数据不准)
LocalDate yesterday = today.minusDays(1);
LocalDate startDate = yesterday.minusDays(6);
endTime = yesterday.atTime(23, 59, 59, 999_999_999);
// 生成7个日期节点(每天)
for (int i = 0; i < 7; i++) {
timeNodes.add(startDate.plusDays(i));
}
} else if ("m".equals(dateType)) {
// 月统计:近7个月(截止到上月末,避免当月未结束的数据不准)
YearMonth lastMonth = YearMonth.from(today.minusMonths(1)); // 上月
endTime = lastMonth.atEndOfMonth().atTime(23, 59, 59, 999_999_999);
// 生成7个月份节点(每个月的最后一天)
for (int i = 6; i >= 0; i--) { // 从6个月前到上月
YearMonth currentMonth = lastMonth.minusMonths(i);
timeNodes.add(currentMonth.atEndOfMonth()); // 当月最后一天(如2025-09-30)
}
} else {
throw new ServiceException("未传入正确的统计时间类型!");
}
ApiOrderPageReqVO allApiQueryVO = new ApiOrderPageReqVO();
allApiQueryVO.setCreateTime(allTimePeriod);
allApiQueryVO.setStatus(ApiOrderStatus.PAID.getValue());//只统计已支付的订单(似乎api订单没有已完成的状态)
List<ApiOrderDO> allApiOrderList = apiOrderService.getOrderList(allApiQueryVO);
// 查询截止到endTime的所有API调用日志(日/月统计共用此数据)
LocalDateTime[] allTimePeriod = {LocalDate.of(1970, 1, 1).atStartOfDay(), endTime};
queryVO.setCreateTime(allTimePeriod);
List<ApiCallLogDO> apiCallLogList = apiCallLogService.getApiCallLogList(queryVO);
//根据日期分组订单数量
Map<LocalDate, Long> dailyComputeCountMap = groupOrderByDate(allComputeOrderList);
Map<LocalDate, Long> dailyApiCountMap = groupOrderByDate(allApiOrderList);
// 按统计节点计算API调用次数(每个节点对应的调用量)
Map<LocalDate, Long> callsMap = new HashMap<>();
for (LocalDate node : timeNodes) {
// 统计逻辑:
// - 日统计:调用时间的日期 == 当前节点日期
// - 月统计:调用时间的日期 <= 当前节点(当月最后一天)
long count = apiCallLogList.stream()
.filter(log -> log.getCreateTime().toLocalDate().isBefore(node.plusDays(1)))
.count();
callsMap.put(node, count);
}
//按日期分组统计订单金额
Map<LocalDate, Integer> dailyComputeAmountMap = groupComputeOrderByAmount(allComputeOrderList);
Map<LocalDate, Integer> dailyApiAmountMap = groupApiOrderByAmount(allApiOrderList);
// 构建结果列表(格式化日期显示)
List<HomeIndexApiCallsRespVO> resultList = new ArrayList<>();
for (LocalDate node : timeNodes) {
HomeIndexApiCallsRespVO respVO = new HomeIndexApiCallsRespVO();
// 日统计显示"yyyy-MM-dd",月统计显示"yyyy-MM"
respVO.setCountDate("d".equals(dateType)
? node.toString()
: node.format(DateTimeFormatter.ofPattern("yyyy-MM")));
respVO.setCallsCount(callsMap.get(node).intValue());
resultList.add(respVO);
}
//构建完整的7天数据列表
List<HomeIndexOrdersCountRespVO> resultList = new ArrayList<>();
// 按时间升序排序(从最早到最近)
resultList.sort(Comparator.comparing(HomeIndexApiCallsRespVO::getCountDate));
return resultList;
}
@Override
public List<HomeIndexOrdersCountRespVO> getOrdersData(String dateType) {
// 统一查询截至"统计截止日"的所有订单(日/月统计共用)
LocalDate today = LocalDate.now();
LocalDateTime endTime = null; // 统计截止时间(所有订单创建时间<=该时间)
List<LocalDate> timeNodes = new ArrayList<>(); // 统计节点(7个:7天或7个月的结束日)
// 根据dateType确定统计周期和节点
if ("d".equals(dateType) || ObjectUtil.isEmpty(dateType)) {
// 日统计:近7天(截止到昨天,避免今天未结束的数据)
LocalDate yesterday = today.minusDays(1);
LocalDate startDate = yesterday.minusDays(6);
endTime = yesterday.atTime(23, 59, 59, 999_999_999);
// 生成7个日期节点(每天)
for (int i = 0; i < 7; i++) {
LocalDate currentDate = startDate.plusDays(i);
//订单数量统计
long computeCount = dailyComputeCountMap.getOrDefault(currentDate, 0L); // 算力订单数
long apiCount = dailyApiCountMap.getOrDefault(currentDate, 0L); // API订单数
timeNodes.add(startDate.plusDays(i));
}
} else if ("m".equals(dateType)) {
// 月统计:近7个月(截止到上月末,避免当月未结束的数据)
YearMonth lastMonth = YearMonth.from(today.minusMonths(1)); // 上月
endTime = lastMonth.atEndOfMonth().atTime(23, 59, 59, 999_999_999);
// 生成7个月份节点(每个月的最后一天)
for (int i = 6; i >= 0; i--) { // 从6个月前到上月
YearMonth currentMonth = lastMonth.minusMonths(i);
timeNodes.add(currentMonth.atEndOfMonth()); // 当月最后一天(如2025-09-30)
}
} else {
throw new ServiceException("未传入正确的统计时间类型!");
}
// 2. 查询截止到endTime的所有订单(日/月统计共用)
LocalDateTime[] allTimePeriod = {LocalDate.of(1970, 1, 1).atStartOfDay(), endTime};
// 算力订单(已完成)
TradeOrderPageReqVO computeQueryVO = new TradeOrderPageReqVO();
computeQueryVO.setCreateTime(allTimePeriod);
computeQueryVO.setStatus(TradeOrderStatusEnum.COMPLETED.getStatus());
List<TradeOrderDO> computeOrderList = tradeOrderQueryService.getOrderList(computeQueryVO);
// API订单(已支付)
ApiOrderPageReqVO apiQueryVO = new ApiOrderPageReqVO();
apiQueryVO.setCreateTime(allTimePeriod);
apiQueryVO.setStatus(ApiOrderStatus.PAID.getValue());
List<ApiOrderDO> apiOrderList = apiOrderService.getOrderList(apiQueryVO);
// 3. 按统计节点分组统计:数量和金额
Map<LocalDate, Long> computeCountMap = groupOrderByNode(computeOrderList, timeNodes,dateType);
Map<LocalDate, Long> apiCountMap = groupOrderByNode(apiOrderList, timeNodes,dateType);
Map<LocalDate, Integer> computeAmountMap = groupComputeAmountByNode(computeOrderList, timeNodes,dateType);
Map<LocalDate, Integer> apiAmountMap = groupApiAmountByNode(apiOrderList, timeNodes,dateType);
// 4. 构建结果列表
List<HomeIndexOrdersCountRespVO> resultList = new ArrayList<>();
for (LocalDate node : timeNodes) {
// 统计数量
long computeCount = computeCountMap.getOrDefault(node, 0L);
long apiCount = apiCountMap.getOrDefault(node, 0L);
long totalCount = computeCount + apiCount;
//订单金额统计
int computeAmount = dailyComputeAmountMap.getOrDefault(currentDate, 0);
int apiAmount = dailyApiAmountMap.getOrDefault(currentDate, 0);
// 统计金额
int computeAmount = computeAmountMap.getOrDefault(node, 0);
int apiAmount = apiAmountMap.getOrDefault(node, 0);
int totalAmount = computeAmount + apiAmount;
//封装返回VO(每个日期对应一条统计数据)
// 封装VO
HomeIndexOrdersCountRespVO respVO = new HomeIndexOrdersCountRespVO();
respVO.setCountDate(currentDate.toString()); // 日期(格式:yyyy-MM-dd)
//订单数字段
respVO.setComputeOrdersCount((int) computeCount); // 算力订单数(long转int,订单数通常不超int范围)
respVO.setApiOrdersCount((int) apiCount); // API订单数
respVO.setTotalOrdersCount((int) totalCount); // 全部订单数
// 日期格式:日统计yyyy-MM-dd,月统计yyyy-MM
respVO.setCountDate("d".equals(dateType)
? node.toString()
: node.format(DateTimeFormatter.ofPattern("yyyy-MM")));
// 数量字段
respVO.setComputeOrdersCount((int) computeCount);
respVO.setApiOrdersCount((int) apiCount);
respVO.setTotalOrdersCount((int) totalCount);
// 金额字段
respVO.setComputeOrdersAmount(computeAmount);
respVO.setApiOrdersAmount(apiAmount);
......@@ -174,96 +288,94 @@ public class HomeIndexServiceImpl implements HomeIndexService {
resultList.add(respVO);
}
//按日期升序排序(确保返回数据从“最早一天”到“昨天”的顺序)
resultList.sort(Comparator.comparing(HomeIndexOrdersCountRespVO::getCountDate));
return resultList;
}
@Override
public List<HomeIndexApiCallsRespVO> getApiCallsData() {
LocalDate yesterday = LocalDate.now().minusDays(1);
LocalDate startDate = yesterday.minusDays(6); // 近7天:startDate 到 yesterday
LocalDateTime[] allTimePeriod = new LocalDateTime[2];
allTimePeriod[0] = LocalDate.of(1970, 1, 1).atStartOfDay();
allTimePeriod[1] = yesterday.atTime(23, 59, 59, 999_999_999);
ApiCallLogPageReqVO queryVO = new ApiCallLogPageReqVO();
queryVO.setCreateTime(allTimePeriod);
List<ApiCallLogDO> apiCallLogList = apiCallLogService.getApiCallLogList(queryVO);
Map<LocalDate, Long> dailyApiCallsMap =
apiCallLogList.stream()
.collect(Collectors.groupingBy(apiCallLog->
apiCallLog.getCreateTime().toLocalDate(), Collectors.counting()));
List<HomeIndexApiCallsRespVO> resultList = new ArrayList<>();
for (int i = 0; i < 7; i++) {
LocalDate currentDate = startDate.plusDays(i);
Long apiCallsCountCurrentDay = dailyApiCallsMap.getOrDefault(currentDate,0L);
HomeIndexApiCallsRespVO homeIndexApiCallsRespVO = new HomeIndexApiCallsRespVO();
homeIndexApiCallsRespVO.setCallsCount((int)(long)apiCallsCountCurrentDay);
homeIndexApiCallsRespVO.setCountDate(ObjectUtil.toString(currentDate));
resultList.add(homeIndexApiCallsRespVO);
}
resultList.sort(Comparator.comparing(HomeIndexApiCallsRespVO::getCountDate));
// 按时间升序排序
resultList.sort(Comparator.comparing(HomeIndexOrdersCountRespVO::getCountDate));
return resultList;
}
/**
* 通用工具方法:将订单列表按“创建日期”分组,统计每日订单数
*
* @param orderList 订单列表(支持TradeOrderDO、ApiOrderDO,只要有getCreateTime()方法)
* @return key=LocalDate(日期),value=当日订单数
*/
private <T> Map<LocalDate, Long> groupOrderByDate(List<T> orderList) {
// 若订单列表为空,直接返回空Map
private <T> Map<LocalDate, Long> groupOrderByNode(List<T> orderList, List<LocalDate> nodes, String dateType) {
if (CollectionUtils.isEmpty(orderList)) {
return new HashMap<>();
}
// 反射获取订单的“创建时间”
return orderList.stream()
.collect(Collectors.groupingBy(
order -> {
return nodes.stream()
.collect(Collectors.toMap(
node -> node, // 以统计节点为key
node -> orderList.stream()
.filter(order -> {
try {
// 调用订单对象的getCreateTime()方法,获取LocalDateTime
LocalDateTime createTime = (LocalDateTime) order.getClass()
.getMethod("getCreateTime")
.invoke(order);
return createTime.toLocalDate(); // 提取“日期”作为分组key
LocalDate orderDate = createTime.toLocalDate();
// 核心修改:按当天/当月统计(非累计)
if ("d".equals(dateType)) {
// 日统计:仅统计订单日期 == 节点日期(当天)
return orderDate.isEqual(node);
} else {
// 月统计:仅统计订单日期在节点所在月份(当月)
return YearMonth.from(orderDate).equals(YearMonth.from(node));
}
} catch (Exception e) {
throw new RuntimeException("订单日期提取失败", e);
}
},
Collectors.counting() // 统计每个日期的订单数量
})
.count()
));
}
/**
* 单独统计API订单金额
* 按统计节点分组统计算力订单金额(支持当天/当月统计)
* 新增dateType参数,区分日/月统计逻辑
*/
private Map<LocalDate, Integer> groupApiOrderByAmount(List<ApiOrderDO> orderList) {
private Map<LocalDate, Integer> groupComputeAmountByNode(List<TradeOrderDO> orderList, List<LocalDate> nodes, String dateType) {
if (CollectionUtils.isEmpty(orderList)) {
return new HashMap<>();
}
return orderList.stream()
.collect(Collectors.groupingBy(
order -> order.getCreateTime().toLocalDate(), // 提取日期
Collectors.summingInt(ApiOrderDO::getCostPrice) // 累加costPrice
return nodes.stream()
.collect(Collectors.toMap(
node -> node,
node -> orderList.stream()
.filter(order -> {
LocalDate orderDate = order.getCreateTime().toLocalDate();
if ("d".equals(dateType)) {
return orderDate.isEqual(node); // 当天
} else if("m".equals(dateType)) {
return YearMonth.from(orderDate).equals(YearMonth.from(node)); // 当月
}else{
throw new ServiceException("未传入正确的统计时间类型!");
}
})
.mapToInt(TradeOrderDO::getPayPrice)
.sum()
));
}
/**
* 单独统计算力订单金额(使用TradeOrderDO的payPrice字段)
* 按统计节点分组统计API订单金额(支持当天/当月统计)
* 新增dateType参数,区分日/月统计逻辑
*/
private Map<LocalDate, Integer> groupComputeOrderByAmount(List<TradeOrderDO> orderList) {
private Map<LocalDate, Integer> groupApiAmountByNode(List<ApiOrderDO> orderList, List<LocalDate> nodes, String dateType) {
if (CollectionUtils.isEmpty(orderList)) {
return new HashMap<>();
}
return orderList.stream()
.collect(Collectors.groupingBy(
order -> order.getCreateTime().toLocalDate(), // 提取日期
Collectors.summingInt(TradeOrderDO::getPayPrice) // 累加payPrice
return nodes.stream()
.collect(Collectors.toMap(
node -> node,
node -> orderList.stream()
.filter(order -> {
LocalDate orderDate = order.getCreateTime().toLocalDate();
if ("d".equals(dateType)) {
return orderDate.isEqual(node); // 当天
} else if("m".equals(dateType)) {
return YearMonth.from(orderDate).equals(YearMonth.from(node)); // 当月
}else{
throw new ServiceException("未传入正确的统计时间类型!");
}
})
.mapToInt(ApiOrderDO::getCostPrice)
.sum()
));
}
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment