package com.tcm.training.admin.statistics.service; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.tcm.training.admin.live.entity.HealthCourse; import com.tcm.training.admin.live.mapper.HealthCourseMapper; import com.tcm.training.admin.member.entity.AppUser; import com.tcm.training.admin.member.mapper.UserMapper; import com.tcm.training.admin.points.entity.PointsExchangeOrder; import com.tcm.training.admin.points.mapper.PointsExchangeOrderMapper; import com.tcm.training.admin.statistics.mapper.RevenueStatisticsMapper; import com.tcm.training.admin.statistics.mapper.UserStatisticsMapper; import com.tcm.training.admin.statistics.row.RevenueStatisticsDailyRow; import com.tcm.training.admin.statistics.row.UserStatisticsRegisterTrendRow; import com.tcm.training.admin.statistics.vo.HomeDashboardOverviewVO; import com.tcm.training.admin.statistics.vo.HomeDashboardRevenueTrendItemVO; import com.tcm.training.admin.statistics.vo.HomeDashboardUserTrendItemVO; import com.tcm.training.admin.statistics.vo.HomeDashboardVO; import com.tcm.training.admin.trade.service.PaymentFlowAppService; import org.springframework.stereotype.Service; import java.math.BigDecimal; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @Service public class HomeDashboardAppService { private static final int HOME_RECENT_DAYS = 7; private final UserMapper userMapper; private final HealthCourseMapper healthCourseMapper; private final PointsExchangeOrderMapper pointsExchangeOrderMapper; private final UserStatisticsMapper userStatisticsMapper; private final RevenueStatisticsMapper revenueStatisticsMapper; private final PaymentFlowAppService paymentFlowAppService; public HomeDashboardAppService(UserMapper userMapper, HealthCourseMapper healthCourseMapper, PointsExchangeOrderMapper pointsExchangeOrderMapper, UserStatisticsMapper userStatisticsMapper, RevenueStatisticsMapper revenueStatisticsMapper, PaymentFlowAppService paymentFlowAppService) { this.userMapper = userMapper; this.healthCourseMapper = healthCourseMapper; this.pointsExchangeOrderMapper = pointsExchangeOrderMapper; this.userStatisticsMapper = userStatisticsMapper; this.revenueStatisticsMapper = revenueStatisticsMapper; this.paymentFlowAppService = paymentFlowAppService; } public HomeDashboardVO dashboard() { LocalDate endDate = LocalDate.now(); LocalDate startDate = endDate.minusDays(HOME_RECENT_DAYS - 1L); LocalDateTime startTime = startDate.atStartOfDay(); LocalDateTime endTime = LocalDateTime.of(endDate, LocalTime.MAX); paymentFlowAppService.syncFlows(); HomeDashboardVO result = new HomeDashboardVO(); result.setOverview(buildOverview()); result.setUserRegisterTrend(buildUserRegisterTrend( userStatisticsMapper.selectRegisterTrend(null, startTime, endTime), startDate, endDate )); result.setRevenueTrend(buildRevenueTrend( revenueStatisticsMapper.selectDailyStatistics(null, startTime, endTime), startDate, endDate )); result.setPendingPointsExchangeShipmentCount(countPendingPointsExchangeShipment()); return result; } private HomeDashboardOverviewVO buildOverview() { HomeDashboardOverviewVO overview = new HomeDashboardOverviewVO(); overview.setRegisteredUserCount(userMapper.selectCount(new LambdaQueryWrapper() .eq(AppUser::getDeleted, 0))); overview.setRegisteredDoctorCount(userMapper.selectCount(new LambdaQueryWrapper() .eq(AppUser::getDeleted, 0) .eq(AppUser::getType, 1))); overview.setMemberUserCount(userMapper.selectCount(new LambdaQueryWrapper() .eq(AppUser::getDeleted, 0) .isNotNull(AppUser::getMemberExpireAt) .gt(AppUser::getMemberExpireAt, LocalDateTime.now()))); overview.setHealthCourseCount(healthCourseMapper.selectCount(new LambdaQueryWrapper() .eq(HealthCourse::getDeleted, 0))); return overview; } private List buildUserRegisterTrend(List rows, LocalDate startDate, LocalDate endDate) { Map rowMap = new HashMap(); for (UserStatisticsRegisterTrendRow row : rows) { rowMap.put(row.getStatDate(), row); } List result = new ArrayList(); LocalDate cursor = startDate; while (!cursor.isAfter(endDate)) { UserStatisticsRegisterTrendRow row = rowMap.get(cursor); HomeDashboardUserTrendItemVO item = new HomeDashboardUserTrendItemVO(); item.setDate(cursor.toString()); item.setRegisteredUserCount(nvl(row == null ? null : row.getRegisteredUserCount())); result.add(item); cursor = cursor.plusDays(1); } return result; } private List buildRevenueTrend(List rows, LocalDate startDate, LocalDate endDate) { Map rowMap = new HashMap(); for (RevenueStatisticsDailyRow row : rows) { rowMap.put(row.getStatDate(), row); } List result = new ArrayList(); LocalDate cursor = startDate; while (!cursor.isAfter(endDate)) { RevenueStatisticsDailyRow row = rowMap.get(cursor); HomeDashboardRevenueTrendItemVO item = new HomeDashboardRevenueTrendItemVO(); item.setDate(cursor.toString()); item.setHealthIncome(nvl(row == null ? null : row.getHealthIncome())); item.setMemberIncome(nvl(row == null ? null : row.getMemberIncome())); result.add(item); cursor = cursor.plusDays(1); } return result; } private Long countPendingPointsExchangeShipment() { return pointsExchangeOrderMapper.selectCount(new LambdaQueryWrapper() .eq(PointsExchangeOrder::getDeleted, 0) .eq(PointsExchangeOrder::getStatus, 0)); } private Long nvl(Long value) { return value == null ? 0L : value; } private BigDecimal nvl(BigDecimal value) { return value == null ? BigDecimal.ZERO : value; } }